LameXP/src/LockedFile.cpp

288 lines
7.4 KiB
C++
Raw Normal View History

2010-11-06 23:04:47 +01:00
///////////////////////////////////////////////////////////////////////////////
// LameXP - Audio Encoder Front-End
2015-01-01 18:06:21 +01:00
// Copyright (C) 2004-2015 LoRd_MuldeR <MuldeR2@GMX.de>
2010-11-06 23:04:47 +01:00
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
2013-10-23 20:56:57 +02:00
// (at your option) any later version, but always including the *additional*
// restrictions defined in the "License.txt" file.
2010-11-06 23:04:47 +01:00
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// http://www.gnu.org/licenses/gpl-2.0.txt
///////////////////////////////////////////////////////////////////////////////
#include "LockedFile.h"
//Internal
2010-11-06 23:04:47 +01:00
#include "Global.h"
#include "FileHash.h"
2010-11-06 23:04:47 +01:00
//MUtils
#include <MUtils/OSSupport.h>
#include <MUtils/Exception.h>
//Qt
2010-11-06 23:04:47 +01:00
#include <QResource>
#include <QFile>
#include <QFileInfo>
#include <QDir>
#include <QCryptographicHash>
//CRT
#include <stdio.h>
#include <io.h>
#include <fcntl.h>
#include <stdexcept>
//Windows includes
#define NOMINMAX
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
///////////////////////////////////////////////////////////////////////////////
// WARNING: Passing file descriptors into Qt does NOT work with dynamically linked CRT!
#ifdef QT_NODLL
static const bool g_useFileDescrForQFile = true;
#else
static const bool g_useFileDescrForQFile = false;
#endif
2015-04-01 21:11:09 +02:00
#define VALID_HANDLE(H) (((H) != NULL) && ((H) != INVALID_HANDLE_VALUE))
static void CLOSE_HANDLE(HANDLE &h)
{
2015-04-01 21:11:09 +02:00
if(VALID_HANDLE(h))
{
CloseHandle(h);
h = NULL;
}
}
///////////////////////////////////////////////////////////////////////////////
static __forceinline void doWriteOutput(QFile &outFile, const QResource *const resource)
{
for(int i = 0; i < 64; i++)
{
if(outFile.open(QIODevice::WriteOnly))
{
break;
}
if(i == 0)
{
qWarning("Failed to open file on first attemp, retrying...");
}
Sleep(25);
}
2010-11-06 23:04:47 +01:00
//Write data to file
if(outFile.isOpen() && outFile.isWritable())
2010-11-06 23:04:47 +01:00
{
if(outFile.write(reinterpret_cast<const char*>(resource->data()), resource->size()) != resource->size())
2010-11-06 23:04:47 +01:00
{
QFile::remove(QFileInfo(outFile).canonicalFilePath());
MUTILS_THROW_FMT("File '%s' could not be written!", MUTILS_UTF8(QFileInfo(outFile).fileName()));
2010-11-06 23:04:47 +01:00
}
}
else
{
MUTILS_THROW_FMT("File '%s' could not be created!", MUTILS_UTF8(QFileInfo(outFile).fileName()));
2010-11-06 23:04:47 +01:00
}
//Close file after it has been written
outFile.close();
}
static __forceinline void doValidateFileExists(const QString &filePath)
{
QFileInfo existingFileInfo(filePath);
existingFileInfo.setCaching(false);
//Make sure the file exists, before we try to lock it
if((!existingFileInfo.exists()) || (!existingFileInfo.isFile()) || filePath.isEmpty())
{
MUTILS_THROW_FMT("File '%s' does not exist!", MUTILS_UTF8(filePath));
}
}
static __forceinline void doLockFile(HANDLE &fileHandle, const QString &filePath, QFile *const outFile)
{
for(int i = 0; i < 64; i++)
2011-06-21 14:35:46 +02:00
{
fileHandle = CreateFileW(MUTILS_WCHR(QDir::toNativeSeparators(filePath)), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
if(VALID_HANDLE(fileHandle))
{
break;
}
if(i == 0)
{
qWarning("Failed to lock file on first attemp, retrying...");
}
Sleep(25);
2011-06-21 14:35:46 +02:00
}
//Locked successfully?
2015-04-01 21:11:09 +02:00
if(!VALID_HANDLE(fileHandle))
2010-11-06 23:04:47 +01:00
{
if(outFile)
{
QFile::remove(QFileInfo(*outFile).canonicalFilePath());
}
MUTILS_THROW_FMT("File '%s' could not be locked!", MUTILS_UTF8(QFileInfo(filePath).fileName()));
2010-11-06 23:04:47 +01:00
}
}
2010-11-06 23:04:47 +01:00
static __forceinline void doInitFileDescriptor(const HANDLE &fileHandle, int &fileDescriptor)
{
fileDescriptor = _open_osfhandle(reinterpret_cast<intptr_t>(fileHandle), _O_RDONLY | _O_BINARY);
if(fileDescriptor < 0)
{
MUTILS_THROW_FMT("Failed to obtain C Runtime file descriptor!");
}
}
static __forceinline void doValidateHash(HANDLE &fileHandle, const int &fileDescriptor, const QByteArray &expectedHash, const QString &filePath)
{
QFile checkFile;
//Now re-open the file for reading
if(g_useFileDescrForQFile)
{
checkFile.open(fileDescriptor, QIODevice::ReadOnly);
}
else
{
checkFile.setFileName(filePath);
for(int i = 0; i < 64; i++)
{
if(checkFile.open(QIODevice::ReadOnly)) break;
if(!i) qWarning("Failed to re-open file on first attemp, retrying...");
Sleep(100);
}
}
//Opened successfully
if(!checkFile.isOpen())
2011-06-21 14:35:46 +02:00
{
QFile::remove(filePath);
MUTILS_THROW_FMT("File '%s' could not be read!", MUTILS_UTF8(QFileInfo(filePath).fileName()));
2011-06-21 14:35:46 +02:00
}
2010-11-06 23:04:47 +01:00
//Verify file contents
const QByteArray hash = FileHash::computeHash(checkFile);
checkFile.close();
//Compare hashes
if(hash.isNull() || _stricmp(hash.constData(), expectedHash.constData()))
2010-11-06 23:04:47 +01:00
{
qWarning("\nFile checksum error:\n A = %s\n B = %s\n", expectedHash.constData(), hash.constData());
CLOSE_HANDLE(fileHandle);
QFile::remove(filePath);
MUTILS_THROW_FMT("File '%s' is corruputed, take care!", MUTILS_UTF8(QFileInfo(filePath).fileName()));
}
}
///////////////////////////////////////////////////////////////////////////////
LockedFile::LockedFile(QResource *const resource, const QString &outPath, const QByteArray &expectedHash, const bool bOwnsFile)
:
m_bOwnsFile(bOwnsFile),
m_filePath(QFileInfo(outPath).absoluteFilePath())
{
m_fileDescriptor = -1;
HANDLE fileHandle = NULL;
//Make sure the resource is valid
if(!(resource->isValid() && resource->data()))
{
MUTILS_THROW_FMT("The resource at %p is invalid!", resource);
}
//Write data to output file
QFile outFile(m_filePath);
doWriteOutput(outFile, resource);
//Now lock the file!
doLockFile(fileHandle, m_filePath, &outFile);
//Get file descriptor
doInitFileDescriptor(fileHandle, m_fileDescriptor);
//Validate file hash
doValidateHash(fileHandle, m_fileDescriptor, expectedHash, m_filePath);
}
LockedFile::LockedFile(const QString &filePath, const QByteArray &expectedHash, const bool bOwnsFile)
:
m_bOwnsFile(bOwnsFile),
m_filePath(QFileInfo(filePath).absoluteFilePath())
{
m_fileDescriptor = -1;
HANDLE fileHandle = NULL;
//Make sure the file exists, before we try to lock it
doValidateFileExists(m_filePath);
//Now lock the file!
doLockFile(fileHandle, m_filePath, NULL);
//Get file descriptor
doInitFileDescriptor(fileHandle, m_fileDescriptor);
//Validate file hash
doValidateHash(fileHandle, m_fileDescriptor, expectedHash, m_filePath);
}
LockedFile::LockedFile(const QString &filePath, const bool bOwnsFile)
:
m_bOwnsFile(bOwnsFile),
m_filePath(QFileInfo(filePath).canonicalFilePath())
{
m_fileDescriptor = -1;
HANDLE fileHandle = NULL;
//Make sure the file exists, before we try to lock it
doValidateFileExists(m_filePath);
//Now lock the file!
doLockFile(fileHandle, m_filePath, NULL);
//Get file descriptor
doInitFileDescriptor(fileHandle, m_fileDescriptor);
2010-11-06 23:04:47 +01:00
}
LockedFile::~LockedFile(void)
{
if(m_fileDescriptor >= 0)
{
_close(m_fileDescriptor);
m_fileDescriptor = -1;
}
if(m_bOwnsFile)
{
if(QFileInfo(m_filePath).exists())
{
for(int i = 0; i < 64; i++)
{
if(QFile::remove(m_filePath)) break;
MUtils::OS::sleep_ms(1);
}
}
}
2010-11-06 23:04:47 +01:00
}
const QString &LockedFile::filePath()
{
return m_filePath;
}