Fixed MSVC 11.0 (VS2012) project file.
[LameXP.git] / src / LockedFile.cpp
blob5601c519990e6908bde532bd3d1b68e044b132d8
1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2014 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, but always including the *additional*
9 // restrictions defined in the "License.txt" file.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License along
17 // with this program; if not, write to the Free Software Foundation, Inc.,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 // http://www.gnu.org/licenses/gpl-2.0.txt
21 ///////////////////////////////////////////////////////////////////////////////
23 #include "LockedFile.h"
24 #include "Global.h"
26 #include <QResource>
27 #include <QFile>
28 #include <QFileInfo>
29 #include <QDir>
30 #include <QCryptographicHash>
31 #include <QKeccakHash>
33 #include <stdio.h>
34 #include <io.h>
35 #include <fcntl.h>
36 #include <stdexcept>
38 //Windows includes
39 #define NOMINMAX
40 #define WIN32_LEAN_AND_MEAN
41 #include <Windows.h>
43 ///////////////////////////////////////////////////////////////////////////////
45 // WARNING: Passing file descriptors into Qt does NOT work with dynamically linked CRT!
46 #ifdef QT_NODLL
47 static const bool g_useFileDescrForQFile = true;
48 #else
49 static const bool g_useFileDescrForQFile = false;
50 #endif
52 ///////////////////////////////////////////////////////////////////////////////
54 static const char *g_blnk = "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef";
55 static const char *g_seed = "c375d83b4388329408dfcbb4d9a065b6e06d28272f25ef299c70b506e26600af79fd2f866ae24602daf38f25c9d4b7e1";
56 static const char *g_salt = "ee9f7bdabc170763d2200a7e3030045aafe380011aefc1730e547e9244c62308aac42a976feeca224ba553de0c4bb883";
58 QByteArray LockedFile::fileHash(QFile &file)
60 QByteArray hash = QByteArray::fromHex(g_blnk);
62 if(file.isOpen() && file.reset())
64 QKeccakHash keccak;
66 const QByteArray data = file.readAll();
67 const QByteArray seed = QByteArray::fromHex(g_seed);
68 const QByteArray salt = QByteArray::fromHex(g_salt);
70 if(keccak.init(QKeccakHash::hb384))
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)
78 const QByteArray digest = keccak.finalize();
79 if(!digest.isEmpty()) hash = digest.toHex();
84 return hash;
87 ///////////////////////////////////////////////////////////////////////////////
89 LockedFile::LockedFile(QResource *const resource, const QString &outPath, const QByteArray &expectedHash, const bool bOwnsFile)
91 m_bOwnsFile(bOwnsFile),
92 m_filePath(QFileInfo(outPath).absoluteFilePath())
94 m_fileDescriptor = -1;
95 HANDLE fileHandle = NULL;
97 //Make sure the resource is valid
98 if(!(resource->isValid() && resource->data()))
100 THROW_FMT("The resource at %p is invalid!", resource);
103 QFile outFile(m_filePath);
105 //Open output file
106 for(int i = 0; i < 64; i++)
108 if(outFile.open(QIODevice::WriteOnly)) break;
109 if(!i) qWarning("Failed to open file on first attemp, retrying...");
110 Sleep(100);
113 //Write data to file
114 if(outFile.isOpen() && outFile.isWritable())
116 if(outFile.write(reinterpret_cast<const char*>(resource->data()), resource->size()) != resource->size())
118 QFile::remove(QFileInfo(outFile).canonicalFilePath());
119 THROW_FMT("File '%s' could not be written!", QUTF8(QFileInfo(outFile).fileName()));
122 else
124 THROW_FMT("File '%s' could not be created!", QUTF8(QFileInfo(outFile).fileName()));
127 //Close file after it has been written
128 outFile.close();
130 //Now lock the file!
131 for(int i = 0; i < 64; i++)
133 fileHandle = CreateFileW(QWCHAR(QDir::toNativeSeparators(m_filePath)), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
134 if((fileHandle != NULL) && (fileHandle != INVALID_HANDLE_VALUE)) break;
135 if(!i) qWarning("Failed to lock file on first attemp, retrying...");
136 Sleep(100);
139 //Locked successfully?
140 if((fileHandle == NULL) || (fileHandle == INVALID_HANDLE_VALUE))
142 QFile::remove(QFileInfo(outFile).canonicalFilePath());
143 THROW_FMT("File '%s' could not be locked!", QUTF8(QFileInfo(m_filePath).fileName()));
146 //Get file descriptor
147 m_fileDescriptor = _open_osfhandle(reinterpret_cast<intptr_t>(fileHandle), _O_RDONLY | _O_BINARY);
148 if(m_fileDescriptor < 0)
150 THROW_FMT("Failed to obtain C Runtime file descriptor!");
153 QFile checkFile;
155 //Now re-open the file for reading
156 if(g_useFileDescrForQFile)
158 checkFile.open(m_fileDescriptor, QIODevice::ReadOnly);
160 else
162 checkFile.setFileName(m_filePath);
163 for(int i = 0; i < 64; i++)
165 if(checkFile.open(QIODevice::ReadOnly)) break;
166 if(!i) qWarning("Failed to re-open file on first attemp, retrying...");
167 Sleep(100);
171 //Opened successfully
172 if(!checkFile.isOpen())
174 QFile::remove(m_filePath);
175 THROW_FMT("File '%s' could not be read!", QUTF8(QFileInfo(m_filePath).fileName()));
178 //Verify file contents
179 const QByteArray hash = fileHash(checkFile);
180 checkFile.close();
182 //Compare hashes
183 if(hash.isNull() || _stricmp(hash.constData(), expectedHash.constData()))
185 qWarning("\nFile checksum error:\n A = %s\n B = %s\n", expectedHash.constData(), hash.constData());
186 LAMEXP_CLOSE(fileHandle);
187 QFile::remove(m_filePath);
188 THROW_FMT("File '%s' is corruputed, take care!", QUTF8(QFileInfo(m_filePath).fileName()));
192 LockedFile::LockedFile(const QString &filePath, const bool bOwnsFile)
194 m_bOwnsFile(bOwnsFile),
195 m_filePath(QFileInfo(filePath).canonicalFilePath())
197 m_fileDescriptor = -1;
198 HANDLE fileHandle = NULL;
200 QFileInfo existingFileInfo(filePath);
201 existingFileInfo.setCaching(false);
203 //Make sure the file exists, before we try to lock it
204 if(!(existingFileInfo.exists() && existingFileInfo.isFile()))
206 THROW_FMT("File '%s' does not exist!", QUTF8(m_filePath));
209 //Now lock the file
210 for(int i = 0; i < 64; i++)
212 fileHandle = CreateFileW(QWCHAR(QDir::toNativeSeparators(m_filePath)), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
213 if((fileHandle != NULL) && (fileHandle != INVALID_HANDLE_VALUE)) break;
214 if(!i) qWarning("Failed to lock file on first attemp, retrying...");
215 Sleep(100);
218 //Locked successfully?
219 if((fileHandle == NULL) || (fileHandle == INVALID_HANDLE_VALUE))
221 THROW_FMT("File '%s' could not be locked!", QUTF8(QFileInfo(m_filePath).fileName()));
224 //Get file descriptor
225 m_fileDescriptor = _open_osfhandle(reinterpret_cast<intptr_t>(fileHandle), _O_RDONLY | _O_BINARY);
226 if(m_fileDescriptor < 0)
228 THROW_FMT("Failed to obtain C Runtime file descriptor!");
232 LockedFile::~LockedFile(void)
234 if(m_fileDescriptor >= 0)
236 _close(m_fileDescriptor);
237 m_fileDescriptor = -1;
239 if(m_bOwnsFile)
241 if(QFileInfo(m_filePath).exists())
243 for(int i = 0; i < 64; i++)
245 if(QFile::remove(m_filePath)) break;
246 lamexp_sleep(1);
252 const QString &LockedFile::filePath()
254 return m_filePath;
257 void LockedFile::selfTest()
259 if(!QKeccakHash::selfTest())
261 THROW("QKeccakHash self-test has failed!");