#include "net/disk_cache/simple/simple_version_upgrade.h"
#include <cstring>
#include "base/containers/span.h"
#include "base/files/file.h"
#include "base/files/file_enumerator.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/memory_mapped_file.h"
#include "base/logging.h"
#include "base/pickle.h"
#include "net/disk_cache/disk_cache.h"
#include "net/disk_cache/simple/simple_backend_version.h"
#include "net/disk_cache/simple/simple_entry_format.h"
#include "third_party/zlib/zlib.h"
namespace {
constexpr uint32_t kMinVersionAbleToUpgrade = 8;
constexpr char kFakeIndexFileName[] = "index";
constexpr char kIndexDirName[] = "index-dir";
constexpr char kIndexFileName[] = "the-real-index";
void LogMessageFailedUpgradeFromVersion(int version) {
LOG(ERROR) << "Failed to upgrade Simple Cache from version: " << version;
}
bool WriteFakeIndexFile(disk_cache::BackendFileOperations* file_operations,
const base::FilePath& file_name) {
base::File file = file_operations->OpenFile(
file_name, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
if (!file.IsValid())
return false;
disk_cache::FakeIndexData file_contents;
file_contents.initial_magic_number = disk_cache::kSimpleInitialMagicNumber;
file_contents.version = disk_cache::kSimpleVersion;
file_contents.zero = 0;
file_contents.zero2 = 0;
file_contents.encryption_status = file_operations->IsEncrypted() ? 1 : 0;
if (!file.WriteAndCheck(0, base::byte_span_from_ref(file_contents))) {
LOG(ERROR) << "Failed to write fake index file: "
<< file_name.LossyDisplayName();
return false;
}
return true;
}
}
namespace disk_cache {
FakeIndexData::FakeIndexData() {
static_assert(std::has_unique_object_representations_v<FakeIndexData>,
"FakeIndexData should have no implicit padding bytes");
}
SimpleCacheConsistencyResult UpgradeSimpleCacheOnDisk(
BackendFileOperations* file_operations,
const base::FilePath& path) {
const base::FilePath fake_index = path.AppendASCII(kFakeIndexFileName);
base::File fake_index_file = file_operations->OpenFile(
fake_index, base::File::FLAG_OPEN | base::File::FLAG_READ);
if (!fake_index_file.IsValid()) {
if (fake_index_file.error_details() == base::File::FILE_ERROR_NOT_FOUND) {
if (!WriteFakeIndexFile(file_operations, fake_index)) {
file_operations->DeleteFile(fake_index);
LOG(ERROR) << "Failed to write a new fake index.";
return SimpleCacheConsistencyResult::kWriteFakeIndexFileFailed;
}
return SimpleCacheConsistencyResult::kOK;
}
return SimpleCacheConsistencyResult::kBadFakeIndexFile;
}
FakeIndexData file_header;
if (!fake_index_file.ReadAndCheck(0, base::byte_span_from_ref(file_header))) {
LOG(ERROR) << "Disk cache backend fake index file has wrong size.";
return SimpleCacheConsistencyResult::kBadFakeIndexReadSize;
}
if (file_header.initial_magic_number != kSimpleInitialMagicNumber) {
LOG(ERROR) << "Disk cache backend fake index file has wrong magic number.";
return SimpleCacheConsistencyResult::kBadInitialMagicNumber;
}
fake_index_file.Close();
uint32_t version_from = file_header.version;
if (version_from < kMinVersionAbleToUpgrade) {
LOG(ERROR) << "Version " << version_from << " is too old.";
return SimpleCacheConsistencyResult::kVersionTooOld;
}
if (version_from > kSimpleVersion) {
LOG(ERROR) << "Version " << version_from << " is from the future.";
return SimpleCacheConsistencyResult::kVersionFromTheFuture;
}
if (file_header.zero != 0 && file_header.zero2 != 0) {
LOG(WARNING) << "Rebuilding cache due to experiment change";
return SimpleCacheConsistencyResult::kBadZeroCheck;
}
if (file_header.encryption_status != file_operations->IsEncrypted()) {
LOG(WARNING) << "Rebuilding cache due to encryption status change";
return SimpleCacheConsistencyResult::kEncryptionStatusMismatch;
}
bool new_fake_index_needed = (version_from != kSimpleVersion);
static_assert(kMinVersionAbleToUpgrade == 8, "upgrade routines don't match");
DCHECK_LE(8U, version_from);
if (version_from == 8) {
version_from++;
}
DCHECK_EQ(kSimpleIndexFileVersion, version_from);
if (!new_fake_index_needed)
return SimpleCacheConsistencyResult::kOK;
const base::FilePath temp_fake_index = path.AppendASCII("upgrade-index");
if (!WriteFakeIndexFile(file_operations, temp_fake_index)) {
file_operations->DeleteFile(temp_fake_index);
LOG(ERROR) << "Failed to write a new fake index.";
LogMessageFailedUpgradeFromVersion(file_header.version);
return SimpleCacheConsistencyResult::kWriteFakeIndexFileFailed;
}
if (!file_operations->ReplaceFile(temp_fake_index, fake_index, nullptr)) {
LOG(ERROR) << "Failed to replace the fake index.";
LogMessageFailedUpgradeFromVersion(file_header.version);
return SimpleCacheConsistencyResult::kReplaceFileFailed;
}
return SimpleCacheConsistencyResult::kOK;
}
bool DeleteIndexFilesIfCacheIsEmpty(const base::FilePath& path) {
const base::FilePath fake_index = path.AppendASCII(kFakeIndexFileName);
const base::FilePath index_dir = path.AppendASCII(kIndexDirName);
const base::FilePath legacy_index_file = path.AppendASCII(kIndexFileName);
base::FileEnumerator e(
path, false,
base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
for (base::FilePath name = e.Next(); !name.empty(); name = e.Next()) {
if (name == fake_index || name == index_dir || name == legacy_index_file)
continue;
return false;
}
bool deleted_fake_index = base::DeleteFile(fake_index);
bool deleted_index_dir = base::DeletePathRecursively(index_dir);
bool deleted_legacy_index_file = base::DeleteFile(legacy_index_file);
return deleted_fake_index || deleted_index_dir || deleted_legacy_index_file;
}
}