OLD | NEW |
| (Empty) |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | |
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 | |
9 #include "base/files/file.h" | |
10 #include "base/files/file_path.h" | |
11 #include "base/files/file_util.h" | |
12 #include "base/files/memory_mapped_file.h" | |
13 #include "base/logging.h" | |
14 #include "base/pickle.h" | |
15 #include "net/disk_cache/simple/simple_backend_version.h" | |
16 #include "net/disk_cache/simple/simple_entry_format_history.h" | |
17 #include "third_party/zlib/zlib.h" | |
18 | |
19 namespace { | |
20 | |
21 // It is not possible to upgrade cache structures on disk that are of version | |
22 // below this, the entire cache should be dropped for them. | |
23 const uint32 kMinVersionAbleToUpgrade = 5; | |
24 | |
25 const char kFakeIndexFileName[] = "index"; | |
26 const char kIndexFileName[] = "the-real-index"; | |
27 | |
28 void LogMessageFailedUpgradeFromVersion(int version) { | |
29 LOG(ERROR) << "Failed to upgrade Simple Cache from version: " << version; | |
30 } | |
31 | |
32 bool WriteFakeIndexFile(const base::FilePath& file_name) { | |
33 base::File file(file_name, base::File::FLAG_CREATE | base::File::FLAG_WRITE); | |
34 if (!file.IsValid()) | |
35 return false; | |
36 | |
37 disk_cache::FakeIndexData file_contents; | |
38 file_contents.initial_magic_number = | |
39 disk_cache::simplecache_v5::kSimpleInitialMagicNumber; | |
40 file_contents.version = disk_cache::kSimpleVersion; | |
41 int bytes_written = file.Write(0, reinterpret_cast<char*>(&file_contents), | |
42 sizeof(file_contents)); | |
43 if (bytes_written != sizeof(file_contents)) { | |
44 LOG(ERROR) << "Failed to write fake index file: " | |
45 << file_name.LossyDisplayName(); | |
46 return false; | |
47 } | |
48 return true; | |
49 } | |
50 | |
51 } // namespace | |
52 | |
53 namespace disk_cache { | |
54 | |
55 FakeIndexData::FakeIndexData() { | |
56 // Make hashing repeatable: leave no padding bytes untouched. | |
57 std::memset(this, 0, sizeof(*this)); | |
58 } | |
59 | |
60 // Migrates the cache directory from version 4 to version 5. | |
61 // Returns true iff it succeeds. | |
62 // | |
63 // The V5 and V6 caches differ in the name of the index file (it moved to a | |
64 // subdirectory) and in the file format (directory last-modified time observed | |
65 // by the index writer has gotten appended to the pickled format). | |
66 // | |
67 // To keep complexity small this specific upgrade code *deletes* the old index | |
68 // file. The directory for the new index file has to be created lazily anyway, | |
69 // so it is not done in the upgrader. | |
70 // | |
71 // Below is the detailed description of index file format differences. It is for | |
72 // reference purposes. This documentation would be useful to move closer to the | |
73 // next index upgrader when the latter gets introduced. | |
74 // | |
75 // Path: | |
76 // V5: $cachedir/the-real-index | |
77 // V6: $cachedir/index-dir/the-real-index | |
78 // | |
79 // Pickled file format: | |
80 // Both formats extend Pickle::Header by 32bit value of the CRC-32 of the | |
81 // pickled data. | |
82 // <v5-index> ::= <v5-index-metadata> <entry-info>* | |
83 // <v5-index-metadata> ::= UInt64(kSimpleIndexMagicNumber) | |
84 // UInt32(4) | |
85 // UInt64(<number-of-entries>) | |
86 // UInt64(<cache-size-in-bytes>) | |
87 // <entry-info> ::= UInt64(<hash-of-the-key>) | |
88 // Int64(<entry-last-used-time>) | |
89 // UInt64(<entry-size-in-bytes>) | |
90 // <v6-index> ::= <v6-index-metadata> | |
91 // <entry-info>* | |
92 // Int64(<cache-dir-mtime>) | |
93 // <v6-index-metadata> ::= UInt64(kSimpleIndexMagicNumber) | |
94 // UInt32(5) | |
95 // UInt64(<number-of-entries>) | |
96 // UInt64(<cache-size-in-bytes>) | |
97 // Where: | |
98 // <entry-size-in-bytes> is equal the sum of all file sizes of the entry. | |
99 // <cache-dir-mtime> is the last modification time with nanosecond precision | |
100 // of the directory, where all files for entries are stored. | |
101 // <hash-of-the-key> represent the first 64 bits of a SHA-1 of the key. | |
102 bool UpgradeIndexV5V6(const base::FilePath& cache_directory) { | |
103 const base::FilePath old_index_file = | |
104 cache_directory.AppendASCII(kIndexFileName); | |
105 if (!base::DeleteFile(old_index_file, /* recursive = */ false)) | |
106 return false; | |
107 return true; | |
108 } | |
109 | |
110 // Some points about the Upgrade process are still not clear: | |
111 // 1. if the upgrade path requires dropping cache it would be faster to just | |
112 // return an initialization error here and proceed with asynchronous cache | |
113 // cleanup in CacheCreator. Should this hack be considered valid? Some smart | |
114 // tests may fail. | |
115 // 2. Because Android process management allows for killing a process at any | |
116 // time, the upgrade process may need to deal with a partially completed | |
117 // previous upgrade. For example, while upgrading A -> A + 2 we are the | |
118 // process gets killed and some parts are remaining at version A + 1. There | |
119 // are currently no generic mechanisms to resolve this situation, co the | |
120 // upgrade codes need to ensure they can continue after being stopped in the | |
121 // middle. It also means that the "fake index" must be flushed in between the | |
122 // upgrade steps. Atomicity of this is an interesting research topic. The | |
123 // intermediate fake index flushing must be added as soon as we add more | |
124 // upgrade steps. | |
125 bool UpgradeSimpleCacheOnDisk(const base::FilePath& path) { | |
126 // There is a convention among disk cache backends: looking at the magic in | |
127 // the file "index" it should be sufficient to determine if the cache belongs | |
128 // to the currently running backend. The Simple Backend stores its index in | |
129 // the file "the-real-index" (see simple_index_file.cc) and the file "index" | |
130 // only signifies presence of the implementation's magic and version. There | |
131 // are two reasons for that: | |
132 // 1. Absence of the index is itself not a fatal error in the Simple Backend | |
133 // 2. The Simple Backend has pickled file format for the index making it hacky | |
134 // to have the magic in the right place. | |
135 const base::FilePath fake_index = path.AppendASCII(kFakeIndexFileName); | |
136 base::File fake_index_file(fake_index, | |
137 base::File::FLAG_OPEN | base::File::FLAG_READ); | |
138 | |
139 if (!fake_index_file.IsValid()) { | |
140 if (fake_index_file.error_details() == base::File::FILE_ERROR_NOT_FOUND) { | |
141 return WriteFakeIndexFile(fake_index); | |
142 } | |
143 return false; | |
144 } | |
145 | |
146 FakeIndexData file_header; | |
147 int bytes_read = fake_index_file.Read(0, | |
148 reinterpret_cast<char*>(&file_header), | |
149 sizeof(file_header)); | |
150 if (bytes_read != sizeof(file_header) || | |
151 file_header.initial_magic_number != | |
152 disk_cache::simplecache_v5::kSimpleInitialMagicNumber) { | |
153 LOG(ERROR) << "File structure does not match the disk cache backend."; | |
154 return false; | |
155 } | |
156 fake_index_file.Close(); | |
157 | |
158 uint32 version_from = file_header.version; | |
159 if (version_from < kMinVersionAbleToUpgrade || | |
160 version_from > kSimpleVersion) { | |
161 LOG(ERROR) << "Inconsistent cache version."; | |
162 return false; | |
163 } | |
164 bool upgrade_needed = (version_from != kSimpleVersion); | |
165 if (version_from == kMinVersionAbleToUpgrade) { | |
166 // Upgrade only the index for V4 -> V5 move. | |
167 if (!UpgradeIndexV5V6(path)) { | |
168 LogMessageFailedUpgradeFromVersion(file_header.version); | |
169 return false; | |
170 } | |
171 version_from++; | |
172 } | |
173 if (version_from == kSimpleVersion) { | |
174 if (!upgrade_needed) { | |
175 return true; | |
176 } else { | |
177 const base::FilePath temp_fake_index = path.AppendASCII("upgrade-index"); | |
178 if (!WriteFakeIndexFile(temp_fake_index)) { | |
179 base::DeleteFile(temp_fake_index, /* recursive = */ false); | |
180 LOG(ERROR) << "Failed to write a new fake index."; | |
181 LogMessageFailedUpgradeFromVersion(file_header.version); | |
182 return false; | |
183 } | |
184 if (!base::ReplaceFile(temp_fake_index, fake_index, NULL)) { | |
185 LOG(ERROR) << "Failed to replace the fake index."; | |
186 LogMessageFailedUpgradeFromVersion(file_header.version); | |
187 return false; | |
188 } | |
189 return true; | |
190 } | |
191 } | |
192 // Verify during the test stage that the upgraders are implemented for all | |
193 // versions. The release build would cause backend initialization failure | |
194 // which would then later lead to removing all files known to the backend. | |
195 DCHECK_EQ(kSimpleVersion, version_from); | |
196 return false; | |
197 } | |
198 | |
199 } // namespace disk_cache | |
OLD | NEW |