From adcc11c474c35b7f981ef8015476e3e5a8cb459d Mon Sep 17 00:00:00 2001 From: John Preston Date: Sun, 26 Aug 2018 15:55:12 +0300 Subject: [PATCH] Ignore database actions after IO error. --- .../cache/storage_cache_database_object.cpp | 92 ++++++++++--------- .../cache/storage_cache_database_object.h | 2 + .../cache/storage_cache_database_tests.cpp | 6 +- .../storage/storage_encrypted_file.cpp | 7 ++ .../storage/storage_encrypted_file.h | 1 + 5 files changed, 63 insertions(+), 45 deletions(-) diff --git a/Telegram/SourceFiles/storage/cache/storage_cache_database_object.cpp b/Telegram/SourceFiles/storage/cache/storage_cache_database_object.cpp index 60b52c4ee0..0f8d6a64e7 100644 --- a/Telegram/SourceFiles/storage/cache/storage_cache_database_object.cpp +++ b/Telegram/SourceFiles/storage/cache/storage_cache_database_object.cpp @@ -106,37 +106,39 @@ Error DatabaseObject::ioError(const QString &path) const { } void DatabaseObject::open(EncryptionKey key, FnMut done) { + close(nullptr); + + const auto error = openSomeBinlog(key); + if (error.type != Error::Type::None) { + close(nullptr); + } + invokeCallback(done, error); +} + +Error DatabaseObject::openSomeBinlog(EncryptionKey &key) { const auto version = readVersion(); const auto result = openBinlog(version, File::Mode::ReadAppend, key); switch (result) { - case File::Result::Success: - invokeCallback(done, Error::NoError()); - break; + case File::Result::Success: return Error::NoError(); + case File::Result::Failed: return openNewBinlog(key); case File::Result::LockFailed: - invokeCallback( - done, - Error{ Error::Type::LockFailed, binlogPath(version) }); - break; + return Error{ Error::Type::LockFailed, binlogPath(version) }; case File::Result::WrongKey: - invokeCallback( - done, - Error{ Error::Type::WrongKey, binlogPath(version) }); - break; - case File::Result::Failed: { - const auto available = findAvailableVersion(); - if (writeVersion(available)) { - const auto open = openBinlog(available, File::Mode::Write, key); - if (open == File::Result::Success) { - invokeCallback(done, Error::NoError()); - } else { - invokeCallback(done, ioError(binlogPath(available))); - } - } else { - invokeCallback(done, ioError(versionPath())); - } - } break; - default: Unexpected("Result from DatabaseObject::openBinlog."); + return Error{ Error::Type::WrongKey, binlogPath(version) }; } + Unexpected("Result from DatabaseObject::openBinlog."); +} + +Error DatabaseObject::openNewBinlog(EncryptionKey &key) { + const auto available = findAvailableVersion(); + if (!writeVersion(available)) { + return ioError(versionPath()); + } + const auto open = openBinlog(available, File::Mode::Write, key); + if (open != File::Result::Success) { + return ioError(binlogPath(available)); + } + return Error::NoError(); } QString DatabaseObject::computePath(Version version) const { @@ -177,19 +179,20 @@ File::Result DatabaseObject::openBinlog( return File::Result::Failed; } const auto result = _binlog.open(path, mode, key); - if (result == File::Result::Success) { - const auto headerRequired = (mode == File::Mode::Read) - || (mode == File::Mode::ReadAppend && _binlog.size() > 0); - if (headerRequired ? readHeader() : writeHeader()) { - _path = computePath(version); - _key = std::move(key); - createCleaner(); - readBinlog(); - } else { - return File::Result::Failed; - } + if (result != File::Result::Success) { + return result; } - return result; + const auto headerRequired = (mode == File::Mode::Read) + || (mode == File::Mode::ReadAppend && _binlog.size() > 0); + const auto headerResult = headerRequired ? readHeader() : writeHeader(); + if (!headerResult) { + return File::Result::Failed; + } + _path = computePath(version); + _key = std::move(key); + createCleaner(); + readBinlog(); + return File::Result::Success; } bool DatabaseObject::readHeader() { @@ -575,13 +578,15 @@ void DatabaseObject::compactorDone( }); _binlog.close(); if (!File::Move(ready, binlog)) { - // megafail compactorFail(); return; } const auto result = _binlog.open(binlog, File::Mode::ReadAppend, _key); - if (result != File::Result::Success || !_binlog.seek(_binlog.size())) { - // megafail + if (result != File::Result::Success) { + compactorFail(); + return; + } else if (!_binlog.seek(_binlog.size())) { + _binlog.close(); compactorFail(); return; } @@ -600,7 +605,9 @@ void DatabaseObject::compactorFail() { } void DatabaseObject::close(FnMut done) { - writeBundles(); + if (_binlog.isOpen()) { + writeBundles(); + } _cleaner = CleanerWrap(); _compactor = CompactorWrap(); _binlog.close(); @@ -692,6 +699,7 @@ base::optional DatabaseObject::writeKeyPlaceGeneric( auto writeable = record; const auto success = _binlog.write(bytes::object_as_span(&writeable)); if (!success) { + _binlog.close(); return QString(); } _binlog.flush(); @@ -905,7 +913,7 @@ void DatabaseObject::checkCompactor() { && (_binlogExcessLength * _settings.compactAfterFullSize < _settings.compactAfterExcess * _binlog.size())) { return; - } else if (crl::time() < _compactor.nextAttempt) { + } else if (crl::time() < _compactor.nextAttempt || !_binlog.isOpen()) { return; } auto info = Compactor::Info(); diff --git a/Telegram/SourceFiles/storage/cache/storage_cache_database_object.h b/Telegram/SourceFiles/storage/cache/storage_cache_database_object.h index 23b61520a3..dd188f4827 100644 --- a/Telegram/SourceFiles/storage/cache/storage_cache_database_object.h +++ b/Telegram/SourceFiles/storage/cache/storage_cache_database_object.h @@ -88,6 +88,8 @@ private: QString binlogPath() const; QString compactReadyPath(Version version) const; QString compactReadyPath() const; + Error openSomeBinlog(EncryptionKey &key); + Error openNewBinlog(EncryptionKey &key); File::Result openBinlog( Version version, File::Mode mode, diff --git a/Telegram/SourceFiles/storage/cache/storage_cache_database_tests.cpp b/Telegram/SourceFiles/storage/cache/storage_cache_database_tests.cpp index 02a38c8a4e..86051ecc42 100644 --- a/Telegram/SourceFiles/storage/cache/storage_cache_database_tests.cpp +++ b/Telegram/SourceFiles/storage/cache/storage_cache_database_tests.cpp @@ -18,9 +18,9 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL using namespace Storage::Cache; -const auto DisableLimitsTests = true; -const auto DisableCompactTests = true; -const auto DisableLargeTest = false; +const auto DisableLimitsTests = false; +const auto DisableCompactTests = false; +const auto DisableLargeTest = true; const auto key = Storage::EncryptionKey(bytes::make_vector( bytes::make_span("\ diff --git a/Telegram/SourceFiles/storage/storage_encrypted_file.cpp b/Telegram/SourceFiles/storage/storage_encrypted_file.cpp index 75e8f4a20e..6f2d51071e 100644 --- a/Telegram/SourceFiles/storage/storage_encrypted_file.cpp +++ b/Telegram/SourceFiles/storage/storage_encrypted_file.cpp @@ -206,6 +206,9 @@ size_type File::read(bytes::span bytes) { bool File::write(bytes::span bytes) { Expects(bytes.size() % kBlockSize == 0); + if (!isOpen()) { + return false; + } encrypt(bytes); const auto count = writePlain(bytes); if (count == bytes.size()) { @@ -288,6 +291,10 @@ void File::close() { _state = base::none; } +bool File::isOpen() const { + return _data.isOpen(); +} + int64 File::size() const { return _dataSize; } diff --git a/Telegram/SourceFiles/storage/storage_encrypted_file.h b/Telegram/SourceFiles/storage/storage_encrypted_file.h index 31d8990021..7a03d38d99 100644 --- a/Telegram/SourceFiles/storage/storage_encrypted_file.h +++ b/Telegram/SourceFiles/storage/storage_encrypted_file.h @@ -37,6 +37,7 @@ public: bool flush(); + bool isOpen() const; int64 size() const; int64 offset() const; bool seek(int64 offset);