File: simple_version_upgrade.cc

package info (click to toggle)
chromium 139.0.7258.127-2
  • links: PTS, VCS
  • area: main
  • in suites: forky
  • size: 6,122,156 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (190 lines) | stat: -rw-r--r-- 8,168 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
// Copyright 2013 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#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 {

// It is not possible to upgrade cache structures on disk that are of version
// below this, the entire cache should be dropped for them.
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;

  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

namespace disk_cache {

FakeIndexData::FakeIndexData() {
  // We don't want unset holes in types stored to disk.
  static_assert(std::has_unique_object_representations_v<FakeIndexData>,
                "FakeIndexData should have no implicit padding bytes");
}

// Some points about the Upgrade process are still not clear:
// 1. if the upgrade path requires dropping cache it would be faster to just
//    return an initialization error here and proceed with asynchronous cache
//    cleanup in CacheCreator. Should this hack be considered valid? Some smart
//    tests may fail.
// 2. Because Android process management allows for killing a process at any
//    time, the upgrade process may need to deal with a partially completed
//    previous upgrade. For example, while upgrading A -> A + 2 we are the
//    process gets killed and some parts are remaining at version A + 1. There
//    are currently no generic mechanisms to resolve this situation, co the
//    upgrade codes need to ensure they can continue after being stopped in the
//    middle. It also means that the "fake index" must be flushed in between the
//    upgrade steps. Atomicity of this is an interesting research topic. The
//    intermediate fake index flushing must be added as soon as we add more
//    upgrade steps.
// 3. This upgrade logic only upgrades the fake index file and not other files
//    (entry cache file nor sparse entry file) on Version 9.
SimpleCacheConsistencyResult UpgradeSimpleCacheOnDisk(
    BackendFileOperations* file_operations,
    const base::FilePath& path) {
  // There is a convention among disk cache backends: looking at the magic in
  // the file "index" it should be sufficient to determine if the cache belongs
  // to the currently running backend. The Simple Backend stores its index in
  // the file "the-real-index" (see simple_index_file.cc) and the file "index"
  // only signifies presence of the implementation's magic and version. There
  // are two reasons for that:
  // 1. Absence of the index is itself not a fatal error in the Simple Backend
  // 2. The Simple Backend has pickled file format for the index making it hacky
  //    to have the magic in the right place.
  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;
  }

  bool new_fake_index_needed = (version_from != kSimpleVersion);

  // There should be one upgrade routine here for each incremental upgrade
  // starting at kMinVersionAbleToUpgrade.
  static_assert(kMinVersionAbleToUpgrade == 8, "upgrade routines don't match");
  DCHECK_LE(8U, version_from);
  if (version_from == 8) {
    // Likewise, V8 -> V9 is handled entirely by the index reader.
    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);
  // The newer schema versions have the real index in the index directory.
  // Older versions, however, had a real index file in the same directory.
  const base::FilePath legacy_index_file = path.AppendASCII(kIndexFileName);
  base::FileEnumerator e(
      path, /* recursive = */ 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;
}

}  // namespace disk_cache