• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2013 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/disk_cache/simple/simple_version_upgrade.h"
6 
7 #include <cstring>
8 #include <zlib.h>
9 
10 #include "base/files/file.h"
11 #include "base/files/file_enumerator.h"
12 #include "base/files/file_path.h"
13 #include "base/files/file_util.h"
14 #include "base/files/memory_mapped_file.h"
15 #include "base/logging.h"
16 #include "base/pickle.h"
17 #include "net/disk_cache/disk_cache.h"
18 #include "net/disk_cache/simple/simple_backend_version.h"
19 #include "net/disk_cache/simple/simple_entry_format_history.h"
20 
21 namespace {
22 
23 // It is not possible to upgrade cache structures on disk that are of version
24 // below this, the entire cache should be dropped for them.
25 const uint32_t kMinVersionAbleToUpgrade = 5;
26 
27 const char kFakeIndexFileName[] = "index";
28 const char kIndexDirName[] = "index-dir";
29 const char kIndexFileName[] = "the-real-index";
30 
LogMessageFailedUpgradeFromVersion(int version)31 void LogMessageFailedUpgradeFromVersion(int version) {
32   LOG(ERROR) << "Failed to upgrade Simple Cache from version: " << version;
33 }
34 
WriteFakeIndexFile(disk_cache::BackendFileOperations * file_operations,const base::FilePath & file_name)35 bool WriteFakeIndexFile(disk_cache::BackendFileOperations* file_operations,
36                         const base::FilePath& file_name) {
37   base::File file = file_operations->OpenFile(
38       file_name, base::File::FLAG_CREATE | base::File::FLAG_WRITE);
39   if (!file.IsValid())
40     return false;
41 
42   disk_cache::FakeIndexData file_contents;
43   file_contents.initial_magic_number =
44       disk_cache::simplecache_v5::kSimpleInitialMagicNumber;
45   file_contents.version = disk_cache::kSimpleVersion;
46   file_contents.zero = 0;
47   file_contents.zero2 = 0;
48 
49   int bytes_written = file.Write(0, reinterpret_cast<char*>(&file_contents),
50                                  sizeof(file_contents));
51   if (bytes_written != sizeof(file_contents)) {
52     LOG(ERROR) << "Failed to write fake index file: "
53                << file_name.LossyDisplayName();
54     return false;
55   }
56   return true;
57 }
58 
59 }  // namespace
60 
61 namespace disk_cache {
62 
FakeIndexData()63 FakeIndexData::FakeIndexData() {
64   // Make hashing repeatable: leave no padding bytes untouched.
65   std::memset(this, 0, sizeof(*this));
66 }
67 
68 // Migrates the cache directory from version 4 to version 5.
69 // Returns true iff it succeeds.
70 //
71 // The V5 and V6 caches differ in the name of the index file (it moved to a
72 // subdirectory) and in the file format (directory last-modified time observed
73 // by the index writer has gotten appended to the pickled format).
74 //
75 // To keep complexity small this specific upgrade code *deletes* the old index
76 // file. The directory for the new index file has to be created lazily anyway,
77 // so it is not done in the upgrader.
78 //
79 // Below is the detailed description of index file format differences. It is for
80 // reference purposes. This documentation would be useful to move closer to the
81 // next index upgrader when the latter gets introduced.
82 //
83 // Path:
84 //   V5: $cachedir/the-real-index
85 //   V6: $cachedir/index-dir/the-real-index
86 //
87 // Pickled file format:
88 //   Both formats extend Pickle::Header by 32bit value of the CRC-32 of the
89 //   pickled data.
90 //   <v5-index> ::= <v5-index-metadata> <entry-info>*
91 //   <v5-index-metadata> ::= UInt64(kSimpleIndexMagicNumber)
92 //                           UInt32(4)
93 //                           UInt64(<number-of-entries>)
94 //                           UInt64(<cache-size-in-bytes>)
95 //   <entry-info> ::= UInt64(<hash-of-the-key>)
96 //                    Int64(<entry-last-used-time>)
97 //                    UInt64(<entry-size-in-bytes>)
98 //   <v6-index> ::= <v6-index-metadata>
99 //                  <entry-info>*
100 //                  Int64(<cache-dir-mtime>)
101 //   <v6-index-metadata> ::= UInt64(kSimpleIndexMagicNumber)
102 //                           UInt32(5)
103 //                           UInt64(<number-of-entries>)
104 //                           UInt64(<cache-size-in-bytes>)
105 //   Where:
106 //     <entry-size-in-bytes> is equal the sum of all file sizes of the entry.
107 //     <cache-dir-mtime> is the last modification time with nanosecond precision
108 //       of the directory, where all files for entries are stored.
109 //     <hash-of-the-key> represent the first 64 bits of a SHA-1 of the key.
UpgradeIndexV5V6(BackendFileOperations * file_operations,const base::FilePath & cache_directory)110 bool UpgradeIndexV5V6(BackendFileOperations* file_operations,
111                       const base::FilePath& cache_directory) {
112   const base::FilePath old_index_file =
113       cache_directory.AppendASCII(kIndexFileName);
114   return file_operations->DeleteFile(old_index_file);
115 }
116 
117 // Some points about the Upgrade process are still not clear:
118 // 1. if the upgrade path requires dropping cache it would be faster to just
119 //    return an initialization error here and proceed with asynchronous cache
120 //    cleanup in CacheCreator. Should this hack be considered valid? Some smart
121 //    tests may fail.
122 // 2. Because Android process management allows for killing a process at any
123 //    time, the upgrade process may need to deal with a partially completed
124 //    previous upgrade. For example, while upgrading A -> A + 2 we are the
125 //    process gets killed and some parts are remaining at version A + 1. There
126 //    are currently no generic mechanisms to resolve this situation, co the
127 //    upgrade codes need to ensure they can continue after being stopped in the
128 //    middle. It also means that the "fake index" must be flushed in between the
129 //    upgrade steps. Atomicity of this is an interesting research topic. The
130 //    intermediate fake index flushing must be added as soon as we add more
131 //    upgrade steps.
UpgradeSimpleCacheOnDisk(BackendFileOperations * file_operations,const base::FilePath & path)132 SimpleCacheConsistencyResult UpgradeSimpleCacheOnDisk(
133     BackendFileOperations* file_operations,
134     const base::FilePath& path) {
135   // There is a convention among disk cache backends: looking at the magic in
136   // the file "index" it should be sufficient to determine if the cache belongs
137   // to the currently running backend. The Simple Backend stores its index in
138   // the file "the-real-index" (see simple_index_file.cc) and the file "index"
139   // only signifies presence of the implementation's magic and version. There
140   // are two reasons for that:
141   // 1. Absence of the index is itself not a fatal error in the Simple Backend
142   // 2. The Simple Backend has pickled file format for the index making it hacky
143   //    to have the magic in the right place.
144   const base::FilePath fake_index = path.AppendASCII(kFakeIndexFileName);
145   base::File fake_index_file = file_operations->OpenFile(
146       fake_index, base::File::FLAG_OPEN | base::File::FLAG_READ);
147 
148   if (!fake_index_file.IsValid()) {
149     if (fake_index_file.error_details() == base::File::FILE_ERROR_NOT_FOUND) {
150       if (!WriteFakeIndexFile(file_operations, fake_index)) {
151         file_operations->DeleteFile(fake_index);
152         LOG(ERROR) << "Failed to write a new fake index.";
153         return SimpleCacheConsistencyResult::kWriteFakeIndexFileFailed;
154       }
155       return SimpleCacheConsistencyResult::kOK;
156     }
157     return SimpleCacheConsistencyResult::kBadFakeIndexFile;
158   }
159 
160   FakeIndexData file_header;
161   int bytes_read = fake_index_file.Read(0,
162                                         reinterpret_cast<char*>(&file_header),
163                                         sizeof(file_header));
164   if (bytes_read != sizeof(file_header)) {
165     LOG(ERROR) << "Disk cache backend fake index file has wrong size.";
166     return SimpleCacheConsistencyResult::kBadFakeIndexReadSize;
167   }
168   if (file_header.initial_magic_number !=
169       disk_cache::simplecache_v5::kSimpleInitialMagicNumber) {
170     LOG(ERROR) << "Disk cache backend fake index file has wrong magic number.";
171     return SimpleCacheConsistencyResult::kBadInitialMagicNumber;
172   }
173   fake_index_file.Close();
174 
175   uint32_t version_from = file_header.version;
176   if (version_from < kMinVersionAbleToUpgrade) {
177     LOG(ERROR) << "Version " << version_from << " is too old.";
178     return SimpleCacheConsistencyResult::kVersionTooOld;
179   }
180 
181   if (version_from > kSimpleVersion) {
182     LOG(ERROR) << "Version " << version_from << " is from the future.";
183     return SimpleCacheConsistencyResult::kVersionFromTheFuture;
184   }
185 
186   if (file_header.zero != 0 && file_header.zero2 != 0) {
187     LOG(WARNING) << "Rebuilding cache due to experiment change";
188     return SimpleCacheConsistencyResult::kBadZeroCheck;
189   }
190 
191   bool new_fake_index_needed = (version_from != kSimpleVersion);
192 
193   // There should be one upgrade routine here for each incremental upgrade
194   // starting at kMinVersionAbleToUpgrade.
195   static_assert(kMinVersionAbleToUpgrade == 5, "upgrade routines don't match");
196   DCHECK_LE(5U, version_from);
197   if (version_from == 5) {
198     // Upgrade only the index for V5 -> V6 move.
199     if (!UpgradeIndexV5V6(file_operations, path)) {
200       LogMessageFailedUpgradeFromVersion(file_header.version);
201       return SimpleCacheConsistencyResult::kUpgradeIndexV5V6Failed;
202     }
203     version_from++;
204   }
205   DCHECK_LE(6U, version_from);
206   if (version_from == 6) {
207     // No upgrade from V6 -> V7, because the entry format has not changed and
208     // the V7 index reader is backwards compatible.
209     version_from++;
210   }
211 
212   if (version_from == 7) {
213     // Likewise, V7 -> V8 is handled entirely by the index reader.
214     version_from++;
215   }
216 
217   if (version_from == 8) {
218     // Likewise, V8 -> V9 is handled entirely by the index reader.
219     version_from++;
220   }
221 
222   DCHECK_EQ(kSimpleVersion, version_from);
223 
224   if (!new_fake_index_needed)
225     return SimpleCacheConsistencyResult::kOK;
226 
227   const base::FilePath temp_fake_index = path.AppendASCII("upgrade-index");
228   if (!WriteFakeIndexFile(file_operations, temp_fake_index)) {
229     file_operations->DeleteFile(temp_fake_index);
230     LOG(ERROR) << "Failed to write a new fake index.";
231     LogMessageFailedUpgradeFromVersion(file_header.version);
232     return SimpleCacheConsistencyResult::kWriteFakeIndexFileFailed;
233   }
234   if (!file_operations->ReplaceFile(temp_fake_index, fake_index, nullptr)) {
235     LOG(ERROR) << "Failed to replace the fake index.";
236     LogMessageFailedUpgradeFromVersion(file_header.version);
237     return SimpleCacheConsistencyResult::kReplaceFileFailed;
238   }
239   return SimpleCacheConsistencyResult::kOK;
240 }
241 
DeleteIndexFilesIfCacheIsEmpty(const base::FilePath & path)242 bool DeleteIndexFilesIfCacheIsEmpty(const base::FilePath& path) {
243   const base::FilePath fake_index = path.AppendASCII(kFakeIndexFileName);
244   const base::FilePath index_dir = path.AppendASCII(kIndexDirName);
245   // The newer schema versions have the real index in the index directory.
246   // Older versions, however, had a real index file in the same directory.
247   const base::FilePath legacy_index_file = path.AppendASCII(kIndexFileName);
248   base::FileEnumerator e(
249       path, /* recursive = */ false,
250       base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
251   for (base::FilePath name = e.Next(); !name.empty(); name = e.Next()) {
252     if (name == fake_index || name == index_dir || name == legacy_index_file)
253       continue;
254     return false;
255   }
256   bool deleted_fake_index = base::DeleteFile(fake_index);
257   bool deleted_index_dir = base::DeletePathRecursively(index_dir);
258   bool deleted_legacy_index_file = base::DeleteFile(legacy_index_file);
259   return deleted_fake_index || deleted_index_dir || deleted_legacy_index_file;
260 }
261 
262 }  // namespace disk_cache
263