OSDN Git Service

Updated Opus encoder/decoder libraries to v1.1.x and Opus-Tools to v0.1.6 (2013-01...
[lamexp/LameXP.git] / src / LockedFile.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2012 LoRd_MuldeR <MuldeR2@GMX.de>
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 //
19 // http://www.gnu.org/licenses/gpl-2.0.txt
20 ///////////////////////////////////////////////////////////////////////////////
21
22 #include "LockedFile.h"
23 #include "Global.h"
24
25 #include <QResource>
26 #include <QFile>
27 #include <QFileInfo>
28 #include <QDir>
29 #include <QCryptographicHash>
30 #include <QKeccakHash>
31
32 #include <stdio.h>
33 #include <io.h>
34 #include <fcntl.h>
35
36 ///////////////////////////////////////////////////////////////////////////////
37
38 #define THROW(STR)                                      \
39 {                                                                       \
40         char error_msg[512];                    \
41         strcpy_s(error_msg, 512, STR);  \
42         throw error_msg;                                \
43 }
44
45 // WARNING: Passing file descriptors into Qt does NOT work with dynamically linked CRT!
46 #ifdef QT_NODLL
47         static const bool g_useFileDescr = 1;
48 #else
49         static const bool g_useFileDescr = 0;
50 #endif
51
52 ///////////////////////////////////////////////////////////////////////////////
53
54 static const char *g_blnk = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
55 static const char *g_seed = "c375d83b4388329408dfcbb4d9a065b6e06d28272f25ef299c70b506e26600af79fd2f866ae24602daf38f25c9d4b7e1";
56 static const char *g_salt = "ee9f7bdabc170763d2200a7e3030045aafe380011aefc1730e547e9244c62308aac42a976feeca224ba553de0c4bb883";
57
58 static QByteArray fileHash(QFile &file)
59 {
60         QByteArray hash = QByteArray::fromHex(g_blnk);
61
62         if(file.isOpen() && file.reset())
63         {
64                 QKeccakHash keccak;
65
66                 const QByteArray data = file.readAll();
67                 const QByteArray seed = QByteArray::fromHex(g_seed);
68                 const QByteArray salt = QByteArray::fromHex(g_salt);
69         
70                 if(keccak.init(QKeccakHash::hb384))
71                 {
72                         bool ok = true;
73                         ok = ok && keccak.addData(seed);
74                         ok = ok && keccak.addData(data);
75                         ok = ok && keccak.addData(salt);
76                         if(ok)
77                         {
78                                 const QByteArray digest = keccak.finalize();
79                                 if(!digest.isEmpty()) hash = digest.toHex();
80                         }
81                 }
82         }
83
84         return hash;
85 }
86
87 ///////////////////////////////////////////////////////////////////////////////
88
89 LockedFile::LockedFile(const QString &resourcePath, const QString &outPath, const QByteArray &expectedHash)
90 {
91         m_fileHandle = NULL;
92         QResource resource(resourcePath);
93         
94         //Make sure the resource is valid
95         if(!resource.isValid())
96         {
97                 THROW(QString("Resource '%1' is invalid!").arg(QFileInfo(resourcePath).absoluteFilePath().replace(QRegExp("^:/"), QString())).toUtf8().constData());
98         }
99
100         QFile outFile(outPath);
101         m_filePath = QFileInfo(outFile).absoluteFilePath();
102         
103         //Open output file
104         for(int i = 0; i < 64; i++)
105         {
106                 if(outFile.open(QIODevice::WriteOnly)) break;
107                 if(!i) qWarning("Failed to open file on first attemp, retrying...");
108                 Sleep(100);
109         }
110         
111         //Write data to file
112         if(outFile.isOpen() && outFile.isWritable())
113         {
114                 if(outFile.write(reinterpret_cast<const char*>(resource.data()), resource.size()) != resource.size())
115                 {
116                         QFile::remove(QFileInfo(outFile).canonicalFilePath());
117                         THROW(QString("File '%1' could not be written!").arg(QFileInfo(outFile).fileName()).toUtf8().constData());
118                 }
119                 outFile.close();
120         }
121         else
122         {
123                 THROW(QString("File '%1' could not be created!").arg(QFileInfo(outFile).fileName()).toUtf8().constData());
124         }
125
126         //Now lock the file!
127         for(int i = 0; i < 64; i++)
128         {
129                 m_fileHandle = CreateFileW(QWCHAR(QDir::toNativeSeparators(m_filePath)), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
130                 if((m_fileHandle != NULL) && (m_fileHandle != INVALID_HANDLE_VALUE)) break;
131                 if(!i) qWarning("Failed to lock file on first attemp, retrying...");
132                 Sleep(100);
133         }
134         
135         //Locked successfully?
136         if((m_fileHandle == NULL) || (m_fileHandle == INVALID_HANDLE_VALUE))
137         {
138                 QFile::remove(QFileInfo(outFile).canonicalFilePath());
139                 char error_msg[512];
140                 strcpy_s(error_msg, 512, QString("File '%1' could not be locked!").arg(QFileInfo(outFile).fileName()).toLatin1().constData());
141                 throw error_msg;
142         }
143
144         //Open file for reading
145         if(g_useFileDescr)
146         {
147                 int fd = _open_osfhandle(reinterpret_cast<intptr_t>(m_fileHandle), _O_RDONLY | _O_BINARY);
148                 if(fd >= 0) outFile.open(fd, QIODevice::ReadOnly);
149         }
150         else
151         {
152                 for(int i = 0; i < 64; i++)
153                 {
154                         if(outFile.open(QIODevice::ReadOnly)) break;
155                         if(!i) qWarning("Failed to re-open file on first attemp, retrying...");
156                         Sleep(100);
157                 }
158         }
159
160         //Verify file contents
161         QByteArray hash;
162         if(outFile.isOpen())
163         {
164                 hash = fileHash(outFile);
165                 outFile.close();
166         }
167         else
168         {
169                 QFile::remove(m_filePath);
170                 THROW(QString("File '%1' could not be read!").arg(QFileInfo(outFile).fileName()).toLatin1().constData());
171         }
172
173         //Compare hashes
174         if(hash.isNull() || _stricmp(hash.constData(), expectedHash.constData()))
175         {
176                 qWarning("\nFile checksum error:\n A = %s\n B = %s\n", expectedHash.constData(), hash.constData());
177                 LAMEXP_CLOSE(m_fileHandle);
178                 QFile::remove(m_filePath);
179                 THROW(QString("File '%1' is corruputed, take care!").arg(QFileInfo(resourcePath).absoluteFilePath().replace(QRegExp("^:/"), QString())).toLatin1().constData());
180         }
181 }
182
183 LockedFile::LockedFile(const QString &filePath)
184 {
185         m_fileHandle = NULL;
186         QFileInfo existingFile(filePath);
187         existingFile.setCaching(false);
188         
189         //Make sure the file exists, before we try to lock it
190         if(!existingFile.exists())
191         {
192                 char error_msg[256];
193                 strcpy_s(error_msg, 256, QString("File '%1' does not exist!").arg(existingFile.fileName()).toLatin1().constData());
194                 throw error_msg;
195         }
196         
197         //Remember file path
198         m_filePath = existingFile.canonicalFilePath();
199
200         //Now lock the file
201         for(int i = 0; i < 64; i++)
202         {
203                 m_fileHandle = CreateFileW(QWCHAR(QDir::toNativeSeparators(filePath)), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
204                 if((m_fileHandle != NULL) && (m_fileHandle != INVALID_HANDLE_VALUE)) break;
205                 if(!i) qWarning("Failed to lock file on first attemp, retrying...");
206                 Sleep(100);
207         }
208
209         //Locked successfully?
210         if((m_fileHandle == NULL) || (m_fileHandle == INVALID_HANDLE_VALUE))
211         {
212                 THROW(QString("File '%1' could not be locked!").arg(existingFile.fileName()).toLatin1().constData());
213         }
214 }
215
216 LockedFile::~LockedFile(void)
217 {
218         LAMEXP_CLOSE(m_fileHandle);
219 }
220
221 const QString &LockedFile::filePath()
222 {
223         return m_filePath;
224 }
225
226 void LockedFile::selfTest()
227 {
228         if(!QKeccakHash::selfTest())
229         {
230                 qFatal("QKeccakHash self-test has failed!");
231         }
232 }