OSDN Git Service

Make "LockedFile" class more robust against buggy a/v software + some refactoring.
[lamexp/LameXP.git] / src / LockedFile.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2011 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
31 #define THROW(STR)                                      \
32 {                                                                       \
33         char error_msg[512];                    \
34         strcpy_s(error_msg, 512, STR);  \
35         throw error_msg;                                \
36 }
37
38 LockedFile::LockedFile(const QString &resourcePath, const QString &outPath, const QByteArray &expectedHash)
39 {
40         m_fileHandle = NULL;
41         QResource resource(resourcePath);
42         
43         //Make sure the resource is valid
44         if(!resource.isValid())
45         {
46                 THROW(QString("Resource '%1' is invalid!").arg(QFileInfo(resourcePath).absoluteFilePath().replace(QRegExp("^:/"), QString())).toUtf8().constData());
47         }
48
49         QFile outFile(outPath);
50         m_filePath = QFileInfo(outFile).absoluteFilePath();
51         
52         //Open output file
53         for(int i = 0; i < 64; i++)
54         {
55                 if(outFile.open(QIODevice::WriteOnly)) break;
56                 if(!i) qWarning("Failed to open file on first attemp, retrying...");
57                 Sleep(100);
58         }
59         
60         //Write data to file
61         if(outFile.isOpen() && outFile.isWritable())
62         {
63                 if(outFile.write(reinterpret_cast<const char*>(resource.data()), resource.size()) != resource.size())
64                 {
65                         QFile::remove(QFileInfo(outFile).canonicalFilePath());
66                         THROW(QString("File '%1' could not be written!").arg(QFileInfo(outFile).fileName()).toUtf8().constData());
67                 }
68                 outFile.close();
69         }
70         else
71         {
72                 THROW(QString("File '%1' could not be created!").arg(QFileInfo(outFile).fileName()).toUtf8().constData());
73         }
74
75         //Now lock the file!
76         for(int i = 0; i < 64; i++)
77         {
78                 m_fileHandle = CreateFileW(QWCHAR(QDir::toNativeSeparators(m_filePath)), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
79                 if((m_fileHandle != NULL) && (m_fileHandle != INVALID_HANDLE_VALUE)) break;
80                 if(!i) qWarning("Failed to lock file on first attemp, retrying...");
81                 Sleep(100);
82         }
83         
84         //Locked successfully?
85         if((m_fileHandle == NULL) || (m_fileHandle == INVALID_HANDLE_VALUE))
86         {
87                 QFile::remove(QFileInfo(outFile).canonicalFilePath());
88                 char error_msg[512];
89                 strcpy_s(error_msg, 512, QString("File '%1' could not be locked!").arg(QFileInfo(outFile).fileName()).toLatin1().constData());
90                 throw error_msg;
91         }
92
93         //Verify file contents
94         outFile.open(QIODevice::ReadOnly);
95         QCryptographicHash fileHash(QCryptographicHash::Sha1);
96         if(outFile.isOpen() && outFile.isReadable())
97         {
98                 fileHash.addData(outFile.readAll());
99                 outFile.close();
100         }
101         else
102         {
103                 QFile::remove(QFileInfo(outFile).canonicalFilePath());
104                 THROW(QString("File '%1' could not be read!").arg(QFileInfo(outFile).fileName()).toLatin1().constData());
105         }
106
107         //Compare hashes
108         if(_stricmp(fileHash.result().toHex().constData(), expectedHash.constData()))
109         {
110                 qWarning("\nFile checksum error:\n Expected = %040s\n Detected = %040s\n", expectedHash.constData(), fileHash.result().toHex().constData());
111                 LAMEXP_CLOSE(m_fileHandle);
112                 QFile::remove(QFileInfo(outFile).canonicalFilePath());
113                 THROW(QString("File '%1' is corruputed, take care!").arg(QFileInfo(resourcePath).absoluteFilePath().replace(QRegExp("^:/"), QString())).toLatin1().constData());
114         }
115 }
116
117 LockedFile::LockedFile(const QString &filePath)
118 {
119         m_fileHandle = NULL;
120         QFileInfo existingFile(filePath);
121         existingFile.setCaching(false);
122         
123         //Make sure the file exists, before we try to lock it
124         if(!existingFile.exists())
125         {
126                 char error_msg[256];
127                 strcpy_s(error_msg, 256, QString("File '%1' does not exist!").arg(existingFile.fileName()).toLatin1().constData());
128                 throw error_msg;
129         }
130         
131         //Remember file path
132         m_filePath = existingFile.canonicalFilePath();
133
134         //Now lock the file
135         for(int i = 0; i < 64; i++)
136         {
137                 m_fileHandle = CreateFileW(QWCHAR(QDir::toNativeSeparators(filePath)), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, NULL, NULL);
138                 if((m_fileHandle != NULL) && (m_fileHandle != INVALID_HANDLE_VALUE)) break;
139                 if(!i) qWarning("Failed to lock file on first attemp, retrying...");
140                 Sleep(100);
141         }
142
143         //Locked successfully?
144         if((m_fileHandle == NULL) || (m_fileHandle == INVALID_HANDLE_VALUE))
145         {
146                 THROW(QString("File '%1' could not be locked!").arg(existingFile.fileName()).toLatin1().constData());
147         }
148 }
149
150 LockedFile::~LockedFile(void)
151 {
152         LAMEXP_CLOSE(m_fileHandle);
153 }
154
155 const QString &LockedFile::filePath()
156 {
157         return m_filePath;
158 }