Add fallback codepath for QSaveFile::open failure.

This commit is contained in:
John Preston 2020-03-30 13:46:06 +04:00
parent 0d58f1c9fa
commit 49111814e4
2 changed files with 152 additions and 94 deletions

View File

@ -15,6 +15,8 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "data/data_drafts.h" #include "data/data_drafts.h"
#include "data/data_user.h" #include "data/data_user.h"
#include "boxes/send_files_box.h" #include "boxes/send_files_box.h"
#include "base/flags.h"
#include "base/platform/base_platform_file_utilities.h"
#include "base/platform/base_platform_info.h" #include "base/platform/base_platform_info.h"
#include "ui/widgets/input_fields.h" #include "ui/widgets/input_fields.h"
#include "ui/emoji_config.h" #include "ui/emoji_config.h"
@ -35,7 +37,6 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "main/main_session.h" #include "main/main_session.h"
#include "window/themes/window_theme.h" #include "window/themes/window_theme.h"
#include "window/window_session_controller.h" #include "window/window_session_controller.h"
#include "base/flags.h"
#include "data/data_session.h" #include "data/data_session.h"
#include "history/history.h" #include "history/history.h"
#include "facades.h" #include "facades.h"
@ -251,52 +252,36 @@ struct EncryptedDescriptor {
}; };
struct FileWriteDescriptor { struct FileWriteDescriptor {
FileWriteDescriptor(const FileKey &key, FileOptions options = FileOption::User | FileOption::Safe) FileWriteDescriptor(
: file((options & FileOption::Safe) ? (QFileDevice&)saveFile : plainFile) { const FileKey &key,
init(toFilePart(key), options); FileOptions options = FileOption::User | FileOption::Safe);
} FileWriteDescriptor(
FileWriteDescriptor(const QString &name, FileOptions options = FileOption::User | FileOption::Safe) const QString &name,
: file((options & FileOption::Safe) ? (QFileDevice&)saveFile : plainFile) { FileOptions options = FileOption::User | FileOption::Safe);
init(name, options); ~FileWriteDescriptor();
}
void init(const QString &name, FileOptions options) {
if (options & FileOption::User) {
if (!_userWorking()) return;
} else {
if (!_working()) return;
}
const auto base = ((options & FileOption::User) ? _userBasePath : _basePath) + name; void init(const QString &name, FileOptions options);
if (options & FileOption::Safe) { bool writeData(const QByteArray &data);
toDelete = base; bool writeEncrypted(
saveFile.setFileName(base + 's'); EncryptedDescriptor &data,
} else { const MTP::AuthKeyPtr &key = LocalKey);
plainFile.setFileName(base + '0'); void finish();
}
if (file.open(QIODevice::WriteOnly)) {
file.write(tdfMagic, tdfMagicLen);
qint32 version = AppVersion;
file.write((const char*)&version, sizeof(version));
stream.setDevice(&file); QFile plainFile;
stream.setVersion(QDataStream::Qt_5_1); QSaveFile saveFile;
} not_null<QFileDevice*> file;
} QDataStream stream;
bool writeData(const QByteArray &data) {
if (!file.isOpen()) return false;
stream << data; QString toDelete;
quint32 len = data.isNull() ? 0xffffffff : data.size();
if (QSysInfo::ByteOrder != QSysInfo::BigEndian) {
len = qbswap(len);
}
md5.feed(&len, sizeof(len));
md5.feed(data.constData(), data.size());
dataSize += sizeof(len) + data.size();
return true; HashMd5 md5;
} int32 dataSize = 0;
static QByteArray prepareEncrypted(EncryptedDescriptor &data, const MTP::AuthKeyPtr &key = LocalKey) {
};
[[nodiscard]] QByteArray PrepareEncrypted(
EncryptedDescriptor &data,
const MTP::AuthKeyPtr &key = LocalKey) {
data.finish(); data.finish();
QByteArray &toEncrypt(data.data); QByteArray &toEncrypt(data.data);
@ -314,11 +299,85 @@ struct FileWriteDescriptor {
return encrypted; return encrypted;
} }
bool writeEncrypted(EncryptedDescriptor &data, const MTP::AuthKeyPtr &key = LocalKey) {
return writeData(prepareEncrypted(data, key)); FileWriteDescriptor::FileWriteDescriptor(
const FileKey &key,
FileOptions options)
: file((options & FileOption::Safe) ? (QFileDevice*)&saveFile : &plainFile) {
init(toFilePart(key), options);
}
FileWriteDescriptor::FileWriteDescriptor(
const QString &name,
FileOptions options)
: file((options & FileOption::Safe) ? (QFileDevice*)&saveFile : &plainFile) {
init(name, options);
}
FileWriteDescriptor::~FileWriteDescriptor() {
finish();
}
void FileWriteDescriptor::init(const QString &name, FileOptions options) {
if (options & FileOption::User) {
if (!_userWorking()) return;
} else {
if (!_working()) return;
}
const auto base = ((options & FileOption::User) ? _userBasePath : _basePath) + name;
saveFile.setFileName(base + 's');
plainFile.setFileName(base + '0');
if (options & FileOption::Safe) {
toDelete = base;
}
if (!file->open(QIODevice::WriteOnly)) {
LOG(("Storage Error: Could not open '%1' for writing.").arg(file->fileName()));
if (!(options & FileOption::Safe)) {
return;
}
file = &plainFile;
LOG(("Storage Info: Trying to fallback to '%1'.").arg(file->fileName()));
if (!file->open(QIODevice::WriteOnly)) {
LOG(("Storage Error: Could not open '%1' for safe writing.").arg(file->fileName()));
return;
}
}
file->write(tdfMagic, tdfMagicLen);
qint32 version = AppVersion;
file->write((const char*)&version, sizeof(version));
stream.setDevice(file);
stream.setVersion(QDataStream::Qt_5_1);
}
bool FileWriteDescriptor::writeData(const QByteArray &data) {
if (!file->isOpen()) {
return false;
}
stream << data;
quint32 len = data.isNull() ? 0xffffffff : data.size();
if (QSysInfo::ByteOrder != QSysInfo::BigEndian) {
len = qbswap(len);
}
md5.feed(&len, sizeof(len));
md5.feed(data.constData(), data.size());
dataSize += sizeof(len) + data.size();
return true;
}
bool FileWriteDescriptor::writeEncrypted(
EncryptedDescriptor &data,
const MTP::AuthKeyPtr &key) {
return writeData(PrepareEncrypted(data, key));
}
void FileWriteDescriptor::finish() {
if (!file->isOpen()) {
return;
} }
void finish() {
if (!file.isOpen()) return;
stream.setDevice(nullptr); stream.setDevice(nullptr);
@ -326,12 +385,25 @@ struct FileWriteDescriptor {
qint32 version = AppVersion; qint32 version = AppVersion;
md5.feed(&version, sizeof(version)); md5.feed(&version, sizeof(version));
md5.feed(tdfMagic, tdfMagicLen); md5.feed(tdfMagic, tdfMagicLen);
file.write((const char*)md5.result(), 0x10); file->write((const char*)md5.result(), 0x10);
if (saveFile.isOpen()) { if (saveFile.isOpen()) {
saveFile.commit(); if (!saveFile.commit()) {
LOG(("Storage Error: Could not commit safe writing in '%1'."
).arg(saveFile.fileName()));
}
} else { } else {
plainFile.close(); plainFile.close();
if (!toDelete.isEmpty()) {
QFile::remove(toDelete + '1');
if (!base::Platform::RenameWithOverwrite(
plainFile.fileName(),
saveFile.fileName())) {
LOG(("Storage Error: Could not rename '%1' to '%2'"
).arg(plainFile.fileName()
).arg(saveFile.fileName()));
}
}
} }
if (!toDelete.isEmpty()) { if (!toDelete.isEmpty()) {
@ -339,20 +411,6 @@ struct FileWriteDescriptor {
QFile::remove(toDelete + '1'); QFile::remove(toDelete + '1');
} }
} }
QFile plainFile;
QSaveFile saveFile;
QFileDevice &file;
QDataStream stream;
QString toDelete;
HashMd5 md5;
int32 dataSize = 0;
~FileWriteDescriptor() {
finish();
}
};
bool readFile(FileReadDescriptor &result, const QString &name, FileOptions options = FileOption::User | FileOption::Safe) { bool readFile(FileReadDescriptor &result, const QString &name, FileOptions options = FileOption::User | FileOption::Safe) {
if (options & FileOption::User) { if (options & FileOption::User) {
@ -2482,7 +2540,7 @@ void _writeMap(WriteMapWhen when) {
EncryptedDescriptor passKeyData(kLocalKeySize); EncryptedDescriptor passKeyData(kLocalKeySize);
LocalKey->write(passKeyData.stream); LocalKey->write(passKeyData.stream);
_passKeyEncrypted = FileWriteDescriptor::prepareEncrypted(passKeyData, PassKey); _passKeyEncrypted = PrepareEncrypted(passKeyData, PassKey);
} }
map.writeData(_passKeySalt); map.writeData(_passKeySalt);
map.writeData(_passKeyEncrypted); map.writeData(_passKeyEncrypted);
@ -2868,7 +2926,7 @@ void setPasscode(const QByteArray &passcode) {
EncryptedDescriptor passKeyData(kLocalKeySize); EncryptedDescriptor passKeyData(kLocalKeySize);
LocalKey->write(passKeyData.stream); LocalKey->write(passKeyData.stream);
_passKeyEncrypted = FileWriteDescriptor::prepareEncrypted(passKeyData, PassKey); _passKeyEncrypted = PrepareEncrypted(passKeyData, PassKey);
_mapChanged = true; _mapChanged = true;
_writeMap(WriteMapWhen::Now); _writeMap(WriteMapWhen::Now);

@ -1 +1 @@
Subproject commit 7de25679dd68f3fb7d2faee77ff5311f4d9587d6 Subproject commit 05b4e4e2a813b2888858acde80f67c4eafffdbce