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.
5 #include "net/disk_cache/simple/simple_version_upgrade.h"
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"
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;
25 const char kFakeIndexFileName
[] = "index";
26 const char kIndexFileName
[] = "the-real-index";
28 void LogMessageFailedUpgradeFromVersion(int version
) {
29 LOG(ERROR
) << "Failed to upgrade Simple Cache from version: " << version
;
32 bool WriteFakeIndexFile(const base::FilePath
& file_name
) {
33 base::File
file(file_name
, base::File::FLAG_CREATE
| base::File::FLAG_WRITE
);
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();
53 namespace disk_cache
{
55 FakeIndexData::FakeIndexData() {
56 // Make hashing repeatable: leave no padding bytes untouched.
57 std::memset(this, 0, sizeof(*this));
60 // Migrates the cache directory from version 4 to version 5.
61 // Returns true iff it succeeds.
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).
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.
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.
76 // V5: $cachedir/the-real-index
77 // V6: $cachedir/index-dir/the-real-index
79 // Pickled file format:
80 // Both formats extend Pickle::Header by 32bit value of the CRC-32 of the
82 // <v5-index> ::= <v5-index-metadata> <entry-info>*
83 // <v5-index-metadata> ::= UInt64(kSimpleIndexMagicNumber)
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>
92 // Int64(<cache-dir-mtime>)
93 // <v6-index-metadata> ::= UInt64(kSimpleIndexMagicNumber)
95 // UInt64(<number-of-entries>)
96 // UInt64(<cache-size-in-bytes>)
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))
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
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
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
);
139 if (!fake_index_file
.IsValid()) {
140 if (fake_index_file
.error_details() == base::File::FILE_ERROR_NOT_FOUND
) {
141 return WriteFakeIndexFile(fake_index
);
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.";
156 fake_index_file
.Close();
158 uint32 version_from
= file_header
.version
;
159 if (version_from
< kMinVersionAbleToUpgrade
||
160 version_from
> kSimpleVersion
) {
161 LOG(ERROR
) << "Inconsistent cache version.";
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
);
173 if (version_from
== kSimpleVersion
) {
174 if (!upgrade_needed
) {
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
);
184 if (!base::ReplaceFile(temp_fake_index
, fake_index
, NULL
)) {
185 LOG(ERROR
) << "Failed to replace the fake index.";
186 LogMessageFailedUpgradeFromVersion(file_header
.version
);
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
);
199 } // namespace disk_cache