OSDN Git Service

Now using Keccak/SHA-3 to verify the built-in files.
[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                 QByteArray data = file.readAll();
67                 QByteArray seed = QByteArray::fromHex(g_seed);
68                 QByteArray salt = QByteArray::fromHex(g_salt);
69         
70                 if(keccak.startBatch(QKeccakHash::hb384))
71                 {
72                         bool ok[3];
73                         ok[0] = keccak.putBatch(seed);
74                         ok[1] = keccak.putBatch(data);
75                         ok[2] = keccak.putBatch(salt);
76                         if(ok[0] && ok[1] && ok[2])
77                         {
78                                 if(keccak.stopBatch())
79                                 {
80                                         hash = keccak.toHex();
81                                 }
82                         }
83                 }
84         }
85
86         return hash;
87 }
88
89 ///////////////////////////////////////////////////////////////////////////////
90
91 LockedFile::LockedFile(const QString &resourcePath, const QString &outPath, const QByteArray &expectedHash)
92 {
93         m_fileHandle = NULL;
94         QResource resource(resourcePath);
95         
96         //Make sure the resource is valid
97         if(!resource.isValid())
98         {
99                 THROW(QString("Resource '%1' is invalid!").arg(QFileInfo(resourcePath).absoluteFilePath().replace(QRegExp("^:/"), QString())).toUtf8().constData());
100         }
101
102         QFile outFile(outPath);
103         m_filePath = QFileInfo(outFile).absoluteFilePath();
104         
105         //Open output file
106         for(int i = 0; i < 64; i++)
107         {
108                 if(outFile.open(QIODevice::WriteOnly)) break;
109                 if(!i) qWarning("Failed to open file on first attemp, retrying...");
110                 Sleep(100);
111         }
112         
113         //Write data to file
114         if(outFile.isOpen() && outFile.isWritable())
115         {
116                 if(outFile.write(reinterpret_cast<const char*>(resource.data()), resource.size()) != resource.size())
117                 {
118                         QFile::remove(QFileInfo(outFile).canonicalFilePath());
119                         THROW(QString("File '%1' could not be written!").arg(QFileInfo(outFile).fileName()).toUtf8().constData());
120                 }
121                 outFile.close();
122         }
123         else
124         {
125                 THROW(QString("File '%1' could not be created!").arg(QFileInfo(outFile).fileName()).toUtf8().constData());
126         }
127
128         //Now lock the file!
129         for(int i = 0; i < 64; i++)
130         {
131                 m_fileHandle = CreateFileW(QWCHAR(QDir::toNativeSeparators(m_filePath)), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
132                 if((m_fileHandle != NULL) && (m_fileHandle != INVALID_HANDLE_VALUE)) break;
133                 if(!i) qWarning("Failed to lock file on first attemp, retrying...");
134                 Sleep(100);
135         }
136         
137         //Locked successfully?
138         if((m_fileHandle == NULL) || (m_fileHandle == INVALID_HANDLE_VALUE))
139         {
140                 QFile::remove(QFileInfo(outFile).canonicalFilePath());
141                 char error_msg[512];
142                 strcpy_s(error_msg, 512, QString("File '%1' could not be locked!").arg(QFileInfo(outFile).fileName()).toLatin1().constData());
143                 throw error_msg;
144         }
145
146         //Open file for reading
147         if(g_useFileDescr)
148         {
149                 int fd = _open_osfhandle(reinterpret_cast<intptr_t>(m_fileHandle), _O_RDONLY | _O_BINARY);
150                 if(fd >= 0) outFile.open(fd, QIODevice::ReadOnly);
151         }
152         else
153         {
154                 for(int i = 0; i < 64; i++)
155                 {
156                         if(outFile.open(QIODevice::ReadOnly)) break;
157                         if(!i) qWarning("Failed to re-open file on first attemp, retrying...");
158                         Sleep(100);
159                 }
160         }
161
162         //Verify file contents
163         QByteArray hash;
164         if(outFile.isOpen())
165         {
166                 hash = fileHash(outFile);
167                 outFile.close();
168         }
169         else
170         {
171                 QFile::remove(m_filePath);
172                 THROW(QString("File '%1' could not be read!").arg(QFileInfo(outFile).fileName()).toLatin1().constData());
173         }
174
175         //Compare hashes
176         if(hash.isNull() || _stricmp(hash.constData(), expectedHash.constData()))
177         {
178                 qWarning("\nFile checksum error:\n A = %s\n B = %s\n", expectedHash.constData(), hash.constData());
179                 LAMEXP_CLOSE(m_fileHandle);
180                 QFile::remove(m_filePath);
181                 THROW(QString("File '%1' is corruputed, take care!").arg(QFileInfo(resourcePath).absoluteFilePath().replace(QRegExp("^:/"), QString())).toLatin1().constData());
182         }
183 }
184
185 LockedFile::LockedFile(const QString &filePath)
186 {
187         m_fileHandle = NULL;
188         QFileInfo existingFile(filePath);
189         existingFile.setCaching(false);
190         
191         //Make sure the file exists, before we try to lock it
192         if(!existingFile.exists())
193         {
194                 char error_msg[256];
195                 strcpy_s(error_msg, 256, QString("File '%1' does not exist!").arg(existingFile.fileName()).toLatin1().constData());
196                 throw error_msg;
197         }
198         
199         //Remember file path
200         m_filePath = existingFile.canonicalFilePath();
201
202         //Now lock the file
203         for(int i = 0; i < 64; i++)
204         {
205                 m_fileHandle = CreateFileW(QWCHAR(QDir::toNativeSeparators(filePath)), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
206                 if((m_fileHandle != NULL) && (m_fileHandle != INVALID_HANDLE_VALUE)) break;
207                 if(!i) qWarning("Failed to lock file on first attemp, retrying...");
208                 Sleep(100);
209         }
210
211         //Locked successfully?
212         if((m_fileHandle == NULL) || (m_fileHandle == INVALID_HANDLE_VALUE))
213         {
214                 THROW(QString("File '%1' could not be locked!").arg(existingFile.fileName()).toLatin1().constData());
215         }
216 }
217
218 LockedFile::~LockedFile(void)
219 {
220         LAMEXP_CLOSE(m_fileHandle);
221 }
222
223 const QString &LockedFile::filePath()
224 {
225         return m_filePath;
226 }