1 // Copyright 2011 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_index_file.h"
6
7 #include <memory>
8
9 #include "base/check.h"
10 #include "base/files/file.h"
11 #include "base/files/file_util.h"
12 #include "base/files/scoped_temp_dir.h"
13 #include "base/functional/callback.h"
14 #include "base/hash/hash.h"
15 #include "base/location.h"
16 #include "base/pickle.h"
17 #include "base/run_loop.h"
18 #include "base/task/single_thread_task_runner.h"
19 #include "base/threading/thread.h"
20 #include "base/time/time.h"
21 #include "net/base/cache_type.h"
22 #include "net/base/test_completion_callback.h"
23 #include "net/disk_cache/backend_cleanup_tracker.h"
24 #include "net/disk_cache/disk_cache.h"
25 #include "net/disk_cache/disk_cache_test_util.h"
26 #include "net/disk_cache/simple/simple_backend_impl.h"
27 #include "net/disk_cache/simple/simple_backend_version.h"
28 #include "net/disk_cache/simple/simple_entry_format.h"
29 #include "net/disk_cache/simple/simple_index.h"
30 #include "net/disk_cache/simple/simple_util.h"
31 #include "net/disk_cache/simple/simple_version_upgrade.h"
32 #include "net/test/gtest_util.h"
33 #include "net/test/test_with_task_environment.h"
34 #include "testing/gmock/include/gmock/gmock.h"
35 #include "testing/gtest/include/gtest/gtest.h"
36
37 using net::test::IsOk;
38
39 using base::Time;
40 using disk_cache::SimpleIndexFile;
41 using disk_cache::SimpleIndex;
42
43 namespace disk_cache {
44
45 namespace {
46
RoundSize(uint32_t in)47 uint32_t RoundSize(uint32_t in) {
48 return (in + 0xFFu) & 0xFFFFFF00u;
49 }
50
51 } // namespace
52
TEST(IndexMetadataTest,Basics)53 TEST(IndexMetadataTest, Basics) {
54 SimpleIndexFile::IndexMetadata index_metadata;
55
56 EXPECT_EQ(disk_cache::kSimpleIndexMagicNumber, index_metadata.magic_number_);
57 EXPECT_EQ(disk_cache::kSimpleVersion, index_metadata.version_);
58 EXPECT_EQ(0U, index_metadata.entry_count());
59 EXPECT_EQ(0U, index_metadata.cache_size_);
60
61 // Without setting a |reason_|, the index metadata isn't valid.
62 index_metadata.reason_ = SimpleIndex::INDEX_WRITE_REASON_SHUTDOWN;
63
64 EXPECT_TRUE(index_metadata.CheckIndexMetadata());
65 }
66
TEST(IndexMetadataTest,Serialize)67 TEST(IndexMetadataTest, Serialize) {
68 SimpleIndexFile::IndexMetadata index_metadata(
69 SimpleIndex::INDEX_WRITE_REASON_SHUTDOWN, 123, 456);
70 base::Pickle pickle;
71 index_metadata.Serialize(&pickle);
72 base::PickleIterator it(pickle);
73 SimpleIndexFile::IndexMetadata new_index_metadata;
74 new_index_metadata.Deserialize(&it);
75
76 EXPECT_EQ(new_index_metadata.magic_number_, index_metadata.magic_number_);
77 EXPECT_EQ(new_index_metadata.version_, index_metadata.version_);
78 EXPECT_EQ(new_index_metadata.reason_, index_metadata.reason_);
79 EXPECT_EQ(new_index_metadata.entry_count(), index_metadata.entry_count());
80 EXPECT_EQ(new_index_metadata.cache_size_, index_metadata.cache_size_);
81
82 EXPECT_TRUE(new_index_metadata.CheckIndexMetadata());
83 }
84
85 // This derived index metadata class allows us to serialize the older V6 format
86 // of the index metadata, thus allowing us to test deserializing the old format.
87 class V6IndexMetadataForTest : public SimpleIndexFile::IndexMetadata {
88 public:
89 // Do not default to |SimpleIndex::INDEX_WRITE_REASON_MAX|, because we want to
90 // ensure we don't serialize that value and then deserialize it and have a
91 // false positive result.
V6IndexMetadataForTest(uint64_t entry_count,uint64_t cache_size)92 V6IndexMetadataForTest(uint64_t entry_count, uint64_t cache_size)
93 : SimpleIndexFile::IndexMetadata(SimpleIndex::INDEX_WRITE_REASON_SHUTDOWN,
94 entry_count,
95 cache_size) {
96 version_ = 6;
97 }
98
99 // Copied and pasted from the V6 implementation of
100 // |SimpleIndexFile::IndexMetadata()| (removing DCHECKs).
Serialize(base::Pickle * pickle) const101 void Serialize(base::Pickle* pickle) const override {
102 pickle->WriteUInt64(magic_number_);
103 pickle->WriteUInt32(version_);
104 pickle->WriteUInt64(entry_count_);
105 pickle->WriteUInt64(cache_size_);
106 }
107 };
108
TEST(IndexMetadataTest,ReadV6Format)109 TEST(IndexMetadataTest, ReadV6Format) {
110 V6IndexMetadataForTest v6_index_metadata(123, 456);
111 EXPECT_EQ(6U, v6_index_metadata.version_);
112 base::Pickle pickle;
113 v6_index_metadata.Serialize(&pickle);
114 base::PickleIterator it(pickle);
115 SimpleIndexFile::IndexMetadata new_index_metadata;
116 new_index_metadata.Deserialize(&it);
117
118 EXPECT_EQ(new_index_metadata.magic_number_, v6_index_metadata.magic_number_);
119 EXPECT_EQ(new_index_metadata.version_, v6_index_metadata.version_);
120
121 EXPECT_EQ(new_index_metadata.reason_, SimpleIndex::INDEX_WRITE_REASON_MAX);
122 EXPECT_EQ(new_index_metadata.entry_count(), v6_index_metadata.entry_count());
123 EXPECT_EQ(new_index_metadata.cache_size_, v6_index_metadata.cache_size_);
124
125 EXPECT_TRUE(new_index_metadata.CheckIndexMetadata());
126 }
127
128 // This derived index metadata class allows us to serialize the older V7 format
129 // of the index metadata, thus allowing us to test deserializing the old format.
130 class V7IndexMetadataForTest : public SimpleIndexFile::IndexMetadata {
131 public:
V7IndexMetadataForTest(uint64_t entry_count,uint64_t cache_size)132 V7IndexMetadataForTest(uint64_t entry_count, uint64_t cache_size)
133 : SimpleIndexFile::IndexMetadata(SimpleIndex::INDEX_WRITE_REASON_SHUTDOWN,
134 entry_count,
135 cache_size) {
136 version_ = 7;
137 }
138 };
139
140 class V8IndexMetadataForTest : public SimpleIndexFile::IndexMetadata {
141 public:
V8IndexMetadataForTest(uint64_t entry_count,uint64_t cache_size)142 V8IndexMetadataForTest(uint64_t entry_count, uint64_t cache_size)
143 : SimpleIndexFile::IndexMetadata(SimpleIndex::INDEX_WRITE_REASON_SHUTDOWN,
144 entry_count,
145 cache_size) {
146 version_ = 8;
147 }
148 };
149
150 // This friend derived class is able to reexport its ancestors private methods
151 // as public, for use in tests.
152 class WrappedSimpleIndexFile : public SimpleIndexFile {
153 public:
154 using SimpleIndexFile::Deserialize;
155 using SimpleIndexFile::Serialize;
156 using SimpleIndexFile::SerializeFinalData;
157
WrappedSimpleIndexFile(const base::FilePath & index_file_directory)158 explicit WrappedSimpleIndexFile(const base::FilePath& index_file_directory)
159 : SimpleIndexFile(base::SingleThreadTaskRunner::GetCurrentDefault(),
160 base::MakeRefCounted<TrivialFileOperationsFactory>(),
161 net::DISK_CACHE,
162 index_file_directory) {}
163 ~WrappedSimpleIndexFile() override = default;
164
GetIndexFilePath() const165 const base::FilePath& GetIndexFilePath() const {
166 return index_file_;
167 }
168
GetTempIndexFilePath() const169 const base::FilePath& GetTempIndexFilePath() const {
170 return temp_index_file_;
171 }
172
CreateIndexFileDirectory() const173 bool CreateIndexFileDirectory() const {
174 return base::CreateDirectory(index_file_.DirName());
175 }
176
LegacyIsIndexFileStale(base::Time cache_last_modified,const base::FilePath & index_file_path)177 static bool LegacyIsIndexFileStale(base::Time cache_last_modified,
178 const base::FilePath& index_file_path) {
179 TrivialFileOperations ops;
180 return SimpleIndexFile::LegacyIsIndexFileStale(&ops, cache_last_modified,
181 index_file_path);
182 }
183 };
184
185 class SimpleIndexFileTest : public net::TestWithTaskEnvironment {
186 public:
CompareTwoEntryMetadata(const EntryMetadata & a,const EntryMetadata & b)187 bool CompareTwoEntryMetadata(const EntryMetadata& a, const EntryMetadata& b) {
188 return a.last_used_time_seconds_since_epoch_ ==
189 b.last_used_time_seconds_since_epoch_ &&
190 a.entry_size_256b_chunks_ == b.entry_size_256b_chunks_ &&
191 a.in_memory_data_ == b.in_memory_data_;
192 }
193
CompareTwoAppCacheEntryMetadata(const EntryMetadata & a,const EntryMetadata & b)194 bool CompareTwoAppCacheEntryMetadata(const EntryMetadata& a,
195 const EntryMetadata& b) {
196 return a.trailer_prefetch_size_ == b.trailer_prefetch_size_ &&
197 a.entry_size_256b_chunks_ == b.entry_size_256b_chunks_ &&
198 a.in_memory_data_ == b.in_memory_data_;
199 }
200 };
201
TEST_F(SimpleIndexFileTest,Serialize)202 TEST_F(SimpleIndexFileTest, Serialize) {
203 SimpleIndex::EntrySet entries;
204 static const size_t kNumHashes = 3u;
205 const std::array<uint64_t, kNumHashes> kHashes = {11, 22, 33};
206 std::array<EntryMetadata, kNumHashes> metadata_entries;
207
208 SimpleIndexFile::IndexMetadata index_metadata(
209 SimpleIndex::INDEX_WRITE_REASON_SHUTDOWN,
210 static_cast<uint64_t>(kNumHashes), 456);
211 for (size_t i = 0; i < kNumHashes; ++i) {
212 uint64_t hash = kHashes[i];
213 // TODO(eroman): Should restructure the test so no casting here (and same
214 // elsewhere where a hash is cast to an entry size).
215 metadata_entries[i] = EntryMetadata(Time(), static_cast<uint32_t>(hash));
216 metadata_entries[i].SetInMemoryData(static_cast<uint8_t>(i));
217 SimpleIndex::InsertInEntrySet(hash, metadata_entries[i], &entries);
218 }
219
220 std::unique_ptr<base::Pickle> pickle = WrappedSimpleIndexFile::Serialize(
221 net::DISK_CACHE, index_metadata, entries);
222 EXPECT_TRUE(pickle.get() != nullptr);
223 base::Time now = base::Time::Now();
224 WrappedSimpleIndexFile::SerializeFinalData(now, pickle.get());
225 base::Time when_index_last_saw_cache;
226 SimpleIndexLoadResult deserialize_result;
227 WrappedSimpleIndexFile::Deserialize(net::DISK_CACHE, *pickle,
228 &when_index_last_saw_cache,
229 &deserialize_result);
230 EXPECT_TRUE(deserialize_result.did_load);
231 EXPECT_EQ(now, when_index_last_saw_cache);
232 const SimpleIndex::EntrySet& new_entries = deserialize_result.entries;
233 EXPECT_EQ(entries.size(), new_entries.size());
234
235 for (size_t i = 0; i < kNumHashes; ++i) {
236 auto it = new_entries.find(kHashes[i]);
237 EXPECT_TRUE(new_entries.end() != it);
238 EXPECT_TRUE(CompareTwoEntryMetadata(it->second, metadata_entries[i]));
239 }
240 }
241
TEST_F(SimpleIndexFileTest,SerializeAppCache)242 TEST_F(SimpleIndexFileTest, SerializeAppCache) {
243 SimpleIndex::EntrySet entries;
244
245 static const size_t kNumHashes = 3u;
246 const std::array<uint64_t, kNumHashes> kHashes = {11, 22, 33};
247 const std::array<int32_t, kNumHashes> kTrailerPrefetches = {123, -1, 987};
248 std::array<EntryMetadata, kNumHashes> metadata_entries;
249
250 SimpleIndexFile::IndexMetadata index_metadata(
251 SimpleIndex::INDEX_WRITE_REASON_SHUTDOWN,
252 static_cast<uint64_t>(kNumHashes), 456);
253 for (size_t i = 0; i < kNumHashes; ++i) {
254 uint64_t hash = kHashes[i];
255 metadata_entries[i] =
256 EntryMetadata(kTrailerPrefetches[i], static_cast<uint32_t>(hash));
257 metadata_entries[i].SetInMemoryData(static_cast<uint8_t>(i));
258 SimpleIndex::InsertInEntrySet(hash, metadata_entries[i], &entries);
259 }
260
261 std::unique_ptr<base::Pickle> pickle = WrappedSimpleIndexFile::Serialize(
262 net::APP_CACHE, index_metadata, entries);
263 EXPECT_TRUE(pickle.get() != nullptr);
264 base::Time now = base::Time::Now();
265 WrappedSimpleIndexFile::SerializeFinalData(now, pickle.get());
266 base::Time when_index_last_saw_cache;
267 SimpleIndexLoadResult deserialize_result;
268 WrappedSimpleIndexFile::Deserialize(
269 net::APP_CACHE, *pickle, &when_index_last_saw_cache, &deserialize_result);
270 EXPECT_TRUE(deserialize_result.did_load);
271 EXPECT_EQ(now, when_index_last_saw_cache);
272 const SimpleIndex::EntrySet& new_entries = deserialize_result.entries;
273 EXPECT_EQ(entries.size(), new_entries.size());
274
275 for (size_t i = 0; i < kNumHashes; ++i) {
276 auto it = new_entries.find(kHashes[i]);
277 EXPECT_TRUE(new_entries.end() != it);
278 EXPECT_TRUE(
279 CompareTwoAppCacheEntryMetadata(it->second, metadata_entries[i]));
280 }
281 }
282
TEST_F(SimpleIndexFileTest,ReadV7Format)283 TEST_F(SimpleIndexFileTest, ReadV7Format) {
284 static const size_t kNumHashes = 3u;
285 const std::array<uint64_t, kNumHashes> kHashes = {11, 22, 33};
286 const std::array<uint32_t, kNumHashes> kSizes = {394, 594, 495940};
287
288 V7IndexMetadataForTest v7_metadata(kNumHashes, 100 * 1024 * 1024);
289
290 // We don't have a convenient way of serializing the actual entries in the
291 // V7 format, but we can cheat a bit by using the implementation details: if
292 // we set the 8 lower bits of size as the memory data, and upper bits
293 // as the size, the new serialization will produce what we want.
294 SimpleIndex::EntrySet entries;
295 for (size_t i = 0; i < kNumHashes; ++i) {
296 EntryMetadata entry(Time(), kSizes[i] & 0xFFFFFF00u);
297 entry.SetInMemoryData(static_cast<uint8_t>(kSizes[i] & 0xFFu));
298 SimpleIndex::InsertInEntrySet(kHashes[i], entry, &entries);
299 }
300 std::unique_ptr<base::Pickle> pickle =
301 WrappedSimpleIndexFile::Serialize(net::DISK_CACHE, v7_metadata, entries);
302 ASSERT_TRUE(pickle.get() != nullptr);
303 base::Time now = base::Time::Now();
304 WrappedSimpleIndexFile::SerializeFinalData(now, pickle.get());
305
306 // Now read it back. We should get the sizes rounded, and 0 for mem entries.
307 base::Time when_index_last_saw_cache;
308 SimpleIndexLoadResult deserialize_result;
309 WrappedSimpleIndexFile::Deserialize(net::DISK_CACHE, *pickle,
310 &when_index_last_saw_cache,
311 &deserialize_result);
312 EXPECT_TRUE(deserialize_result.did_load);
313 EXPECT_EQ(now, when_index_last_saw_cache);
314 const SimpleIndex::EntrySet& new_entries = deserialize_result.entries;
315 ASSERT_EQ(entries.size(), new_entries.size());
316 for (size_t i = 0; i < kNumHashes; ++i) {
317 auto it = new_entries.find(kHashes[i]);
318 ASSERT_TRUE(new_entries.end() != it);
319 EXPECT_EQ(RoundSize(kSizes[i]), it->second.GetEntrySize());
320 EXPECT_EQ(0u, it->second.GetInMemoryData());
321 }
322 }
323
TEST_F(SimpleIndexFileTest,ReadV8Format)324 TEST_F(SimpleIndexFileTest, ReadV8Format) {
325 static const size_t kNumHashes = 3u;
326 const std::array<uint64_t, kNumHashes> kHashes = {11, 22, 33};
327 std::array<EntryMetadata, kNumHashes> metadata_entries;
328
329 // V8 to V9 should not make any modifications for non-APP_CACHE modes.
330 // Verify that the data is preserved through the migration.
331 V8IndexMetadataForTest v8_metadata(kNumHashes, 100 * 1024 * 1024);
332
333 SimpleIndex::EntrySet entries;
334 for (size_t i = 0; i < kNumHashes; ++i) {
335 metadata_entries[i] =
336 EntryMetadata(base::Time::Now(), static_cast<uint32_t>(kHashes[i]));
337 metadata_entries[i].SetInMemoryData(static_cast<uint8_t>(i));
338 SimpleIndex::InsertInEntrySet(kHashes[i], metadata_entries[i], &entries);
339 }
340 std::unique_ptr<base::Pickle> pickle =
341 WrappedSimpleIndexFile::Serialize(net::DISK_CACHE, v8_metadata, entries);
342 ASSERT_TRUE(pickle.get() != nullptr);
343 base::Time now = base::Time::Now();
344 WrappedSimpleIndexFile::SerializeFinalData(now, pickle.get());
345
346 base::Time when_index_last_saw_cache;
347 SimpleIndexLoadResult deserialize_result;
348 WrappedSimpleIndexFile::Deserialize(net::DISK_CACHE, *pickle,
349 &when_index_last_saw_cache,
350 &deserialize_result);
351 EXPECT_TRUE(deserialize_result.did_load);
352 EXPECT_EQ(now, when_index_last_saw_cache);
353 const SimpleIndex::EntrySet& new_entries = deserialize_result.entries;
354 ASSERT_EQ(entries.size(), new_entries.size());
355 for (size_t i = 0; i < kNumHashes; ++i) {
356 auto it = new_entries.find(kHashes[i]);
357 ASSERT_TRUE(new_entries.end() != it);
358 EXPECT_TRUE(CompareTwoEntryMetadata(it->second, metadata_entries[i]));
359 }
360 }
361
TEST_F(SimpleIndexFileTest,ReadV8FormatAppCache)362 TEST_F(SimpleIndexFileTest, ReadV8FormatAppCache) {
363 static const size_t kNumHashes = 3u;
364 const std::array<uint64_t, kNumHashes> kHashes = {11, 22, 33};
365 std::array<EntryMetadata, kNumHashes> metadata_entries;
366
367 // To simulate an upgrade from v8 to v9 write out the v8 schema
368 // using DISK_CACHE mode. The read it back in in APP_CACHE mode.
369 // The entry access time data should be zeroed to reset it as the
370 // new trailer prefetch size.
371 V8IndexMetadataForTest v8_metadata(kNumHashes, 100 * 1024 * 1024);
372
373 SimpleIndex::EntrySet entries;
374 for (size_t i = 0; i < kNumHashes; ++i) {
375 metadata_entries[i] =
376 EntryMetadata(base::Time::Now(), static_cast<uint32_t>(kHashes[i]));
377 metadata_entries[i].SetInMemoryData(static_cast<uint8_t>(i));
378 SimpleIndex::InsertInEntrySet(kHashes[i], metadata_entries[i], &entries);
379 }
380 std::unique_ptr<base::Pickle> pickle =
381 WrappedSimpleIndexFile::Serialize(net::DISK_CACHE, v8_metadata, entries);
382 ASSERT_TRUE(pickle.get() != nullptr);
383 base::Time now = base::Time::Now();
384 WrappedSimpleIndexFile::SerializeFinalData(now, pickle.get());
385
386 // Deserialize using APP_CACHE mode. This should zero out the
387 // trailer_prefetch_size_ instead of using the time bits written
388 // out previously.
389 base::Time when_index_last_saw_cache;
390 SimpleIndexLoadResult deserialize_result;
391 WrappedSimpleIndexFile::Deserialize(
392 net::APP_CACHE, *pickle, &when_index_last_saw_cache, &deserialize_result);
393 EXPECT_TRUE(deserialize_result.did_load);
394 EXPECT_EQ(now, when_index_last_saw_cache);
395 const SimpleIndex::EntrySet& new_entries = deserialize_result.entries;
396 ASSERT_EQ(entries.size(), new_entries.size());
397 for (size_t i = 0; i < kNumHashes; ++i) {
398 auto it = new_entries.find(kHashes[i]);
399 ASSERT_TRUE(new_entries.end() != it);
400 // The trailer prefetch size should be zeroed.
401 EXPECT_NE(metadata_entries[i].trailer_prefetch_size_,
402 it->second.trailer_prefetch_size_);
403 EXPECT_EQ(0, it->second.trailer_prefetch_size_);
404 // Other data should be unaffected.
405 EXPECT_EQ(metadata_entries[i].entry_size_256b_chunks_,
406 it->second.entry_size_256b_chunks_);
407 EXPECT_EQ(metadata_entries[i].in_memory_data_, it->second.in_memory_data_);
408 }
409 }
410
TEST_F(SimpleIndexFileTest,LegacyIsIndexFileStale)411 TEST_F(SimpleIndexFileTest, LegacyIsIndexFileStale) {
412 base::ScopedTempDir cache_dir;
413 ASSERT_TRUE(cache_dir.CreateUniqueTempDir());
414 base::File::Info file_info;
415 base::Time cache_mtime;
416 const base::FilePath cache_path = cache_dir.GetPath();
417
418 ASSERT_TRUE(base::GetFileInfo(cache_path, &file_info));
419 cache_mtime = file_info.last_modified;
420 WrappedSimpleIndexFile simple_index_file(cache_path);
421 ASSERT_TRUE(simple_index_file.CreateIndexFileDirectory());
422 const base::FilePath& index_path = simple_index_file.GetIndexFilePath();
423 EXPECT_TRUE(
424 WrappedSimpleIndexFile::LegacyIsIndexFileStale(cache_mtime, index_path));
425 const std::string kDummyData = "nothing to be seen here";
426 EXPECT_TRUE(base::WriteFile(index_path, kDummyData));
427 ASSERT_TRUE(base::GetFileInfo(cache_path, &file_info));
428 cache_mtime = file_info.last_modified;
429 EXPECT_FALSE(
430 WrappedSimpleIndexFile::LegacyIsIndexFileStale(cache_mtime, index_path));
431
432 const base::Time past_time = base::Time::Now() - base::Seconds(10);
433 EXPECT_TRUE(base::TouchFile(index_path, past_time, past_time));
434 EXPECT_TRUE(base::TouchFile(cache_path, past_time, past_time));
435 ASSERT_TRUE(base::GetFileInfo(cache_path, &file_info));
436 cache_mtime = file_info.last_modified;
437 EXPECT_FALSE(
438 WrappedSimpleIndexFile::LegacyIsIndexFileStale(cache_mtime, index_path));
439 const base::Time even_older = past_time - base::Seconds(10);
440 EXPECT_TRUE(base::TouchFile(index_path, even_older, even_older));
441 EXPECT_TRUE(
442 WrappedSimpleIndexFile::LegacyIsIndexFileStale(cache_mtime, index_path));
443 }
444
TEST_F(SimpleIndexFileTest,WriteThenLoadIndex)445 TEST_F(SimpleIndexFileTest, WriteThenLoadIndex) {
446 base::ScopedTempDir cache_dir;
447 ASSERT_TRUE(cache_dir.CreateUniqueTempDir());
448
449 SimpleIndex::EntrySet entries;
450
451 static const size_t kNumHashes = 3u;
452 const std::array<uint64_t, kNumHashes> kHashes = {11, 22, 33};
453 std::array<EntryMetadata, kNumHashes> metadata_entries;
454 for (size_t i = 0; i < kNumHashes; ++i) {
455 uint64_t hash = kHashes[i];
456 metadata_entries[i] = EntryMetadata(Time(), static_cast<uint32_t>(hash));
457 SimpleIndex::InsertInEntrySet(hash, metadata_entries[i], &entries);
458 }
459
460 const uint64_t kCacheSize = 456U;
461 net::TestClosure closure;
462 {
463 WrappedSimpleIndexFile simple_index_file(cache_dir.GetPath());
464 simple_index_file.WriteToDisk(net::DISK_CACHE,
465 SimpleIndex::INDEX_WRITE_REASON_SHUTDOWN,
466 entries, kCacheSize, closure.closure());
467 closure.WaitForResult();
468 EXPECT_TRUE(base::PathExists(simple_index_file.GetIndexFilePath()));
469 }
470
471 WrappedSimpleIndexFile simple_index_file(cache_dir.GetPath());
472 base::File::Info file_info;
473 ASSERT_TRUE(base::GetFileInfo(cache_dir.GetPath(), &file_info));
474 base::Time fake_cache_mtime = file_info.last_modified;
475 SimpleIndexLoadResult load_index_result;
476 simple_index_file.LoadIndexEntries(fake_cache_mtime, closure.closure(),
477 &load_index_result);
478 closure.WaitForResult();
479
480 EXPECT_TRUE(base::PathExists(simple_index_file.GetIndexFilePath()));
481 EXPECT_TRUE(load_index_result.did_load);
482 EXPECT_FALSE(load_index_result.flush_required);
483
484 EXPECT_EQ(kNumHashes, load_index_result.entries.size());
485 for (uint64_t hash : kHashes)
486 EXPECT_EQ(1U, load_index_result.entries.count(hash));
487 }
488
TEST_F(SimpleIndexFileTest,LoadCorruptIndex)489 TEST_F(SimpleIndexFileTest, LoadCorruptIndex) {
490 base::ScopedTempDir cache_dir;
491 ASSERT_TRUE(cache_dir.CreateUniqueTempDir());
492
493 WrappedSimpleIndexFile simple_index_file(cache_dir.GetPath());
494 ASSERT_TRUE(simple_index_file.CreateIndexFileDirectory());
495 const base::FilePath& index_path = simple_index_file.GetIndexFilePath();
496 const std::string kDummyData = "nothing to be seen here";
497 EXPECT_TRUE(base::WriteFile(index_path, kDummyData));
498 base::File::Info file_info;
499 ASSERT_TRUE(
500 base::GetFileInfo(simple_index_file.GetIndexFilePath(), &file_info));
501 base::Time fake_cache_mtime = file_info.last_modified;
502 EXPECT_FALSE(WrappedSimpleIndexFile::LegacyIsIndexFileStale(fake_cache_mtime,
503 index_path));
504 SimpleIndexLoadResult load_index_result;
505 net::TestClosure closure;
506 simple_index_file.LoadIndexEntries(fake_cache_mtime, closure.closure(),
507 &load_index_result);
508 closure.WaitForResult();
509
510 EXPECT_FALSE(base::PathExists(index_path));
511 EXPECT_TRUE(load_index_result.did_load);
512 EXPECT_TRUE(load_index_result.flush_required);
513 }
514
TEST_F(SimpleIndexFileTest,LoadCorruptIndex2)515 TEST_F(SimpleIndexFileTest, LoadCorruptIndex2) {
516 // Variant where the index looks like a pickle, but not one with right
517 // header size --- that used to hit a DCHECK on debug builds.
518 base::ScopedTempDir cache_dir;
519 ASSERT_TRUE(cache_dir.CreateUniqueTempDir());
520
521 WrappedSimpleIndexFile simple_index_file(cache_dir.GetPath());
522 ASSERT_TRUE(simple_index_file.CreateIndexFileDirectory());
523 const base::FilePath& index_path = simple_index_file.GetIndexFilePath();
524 base::Pickle bad_payload;
525 bad_payload.WriteString("nothing to be seen here");
526
527 EXPECT_TRUE(base::WriteFile(index_path, bad_payload));
528 base::File::Info file_info;
529 ASSERT_TRUE(
530 base::GetFileInfo(simple_index_file.GetIndexFilePath(), &file_info));
531 base::Time fake_cache_mtime = file_info.last_modified;
532 EXPECT_FALSE(WrappedSimpleIndexFile::LegacyIsIndexFileStale(fake_cache_mtime,
533 index_path));
534 SimpleIndexLoadResult load_index_result;
535 net::TestClosure closure;
536 simple_index_file.LoadIndexEntries(fake_cache_mtime, closure.closure(),
537 &load_index_result);
538 closure.WaitForResult();
539
540 EXPECT_FALSE(base::PathExists(index_path));
541 EXPECT_TRUE(load_index_result.did_load);
542 EXPECT_TRUE(load_index_result.flush_required);
543 }
544
545 // Tests that after an upgrade the backend has the index file put in place.
TEST_F(SimpleIndexFileTest,SimpleCacheUpgrade)546 TEST_F(SimpleIndexFileTest, SimpleCacheUpgrade) {
547 base::ScopedTempDir cache_dir;
548 ASSERT_TRUE(cache_dir.CreateUniqueTempDir());
549 const base::FilePath cache_path = cache_dir.GetPath();
550
551 // Write an old fake index file.
552 base::File file(cache_path.AppendASCII("index"),
553 base::File::FLAG_CREATE | base::File::FLAG_WRITE);
554 ASSERT_TRUE(file.IsValid());
555 disk_cache::FakeIndexData file_contents;
556 file_contents.initial_magic_number = disk_cache::kSimpleInitialMagicNumber;
557 file_contents.version = 5;
558 auto bytes_written = file.Write(0, base::byte_span_from_ref(file_contents));
559 ASSERT_EQ(sizeof(file_contents), bytes_written);
560 file.Close();
561
562 // Write the index file. The format is incorrect, but for transitioning from
563 // v5 it does not matter.
564 const std::string index_file_contents("incorrectly serialized data");
565 const base::FilePath old_index_file =
566 cache_path.AppendASCII("the-real-index");
567 ASSERT_TRUE(base::WriteFile(old_index_file, index_file_contents));
568
569 TrivialFileOperations file_operations;
570 // Upgrade the cache.
571 ASSERT_EQ(disk_cache::UpgradeSimpleCacheOnDisk(&file_operations, cache_path),
572 SimpleCacheConsistencyResult::kOK);
573
574 // Create the backend and initiate index flush by destroying the backend.
575 scoped_refptr<disk_cache::BackendCleanupTracker> cleanup_tracker =
576 disk_cache::BackendCleanupTracker::TryCreate(cache_path,
577 base::OnceClosure());
578 ASSERT_TRUE(cleanup_tracker != nullptr);
579
580 net::TestClosure post_cleanup;
581 cleanup_tracker->AddPostCleanupCallback(post_cleanup.closure());
582
583 auto simple_cache = std::make_unique<disk_cache::SimpleBackendImpl>(
584 /*file_operations_factory=*/nullptr, cache_path, cleanup_tracker,
585 /*file_tracker=*/nullptr, 0, net::DISK_CACHE,
586 /*net_log=*/nullptr);
587 net::TestCompletionCallback cb;
588 simple_cache->Init(cb.callback());
589 EXPECT_THAT(cb.WaitForResult(), IsOk());
590 simple_cache->index()->ExecuteWhenReady(cb.callback());
591 int rv = cb.WaitForResult();
592 EXPECT_THAT(rv, IsOk());
593 simple_cache.reset();
594 cleanup_tracker = nullptr;
595
596 // The backend flushes the index on destruction; it will run the post-cleanup
597 // callback set on the cleanup_tracker once that finishes.
598 post_cleanup.WaitForResult();
599
600 // Verify that the index file exists.
601 const base::FilePath& index_file_path =
602 cache_path.AppendASCII("index-dir").AppendASCII("the-real-index");
603 EXPECT_TRUE(base::PathExists(index_file_path));
604
605 // Verify that the version of the index file is correct.
606 std::string contents;
607 EXPECT_TRUE(base::ReadFileToString(index_file_path, &contents));
608 base::Time when_index_last_saw_cache;
609 SimpleIndexLoadResult deserialize_result;
610 WrappedSimpleIndexFile::Deserialize(
611 net::DISK_CACHE, base::as_byte_span(contents), &when_index_last_saw_cache,
612 &deserialize_result);
613 EXPECT_TRUE(deserialize_result.did_load);
614 }
615
TEST_F(SimpleIndexFileTest,OverwritesStaleTempFile)616 TEST_F(SimpleIndexFileTest, OverwritesStaleTempFile) {
617 base::ScopedTempDir cache_dir;
618 ASSERT_TRUE(cache_dir.CreateUniqueTempDir());
619 const base::FilePath cache_path = cache_dir.GetPath();
620 WrappedSimpleIndexFile simple_index_file(cache_path);
621 ASSERT_TRUE(simple_index_file.CreateIndexFileDirectory());
622
623 // Create an temporary index file.
624 const base::FilePath& temp_index_path =
625 simple_index_file.GetTempIndexFilePath();
626 const std::string kDummyData = "nothing to be seen here";
627 EXPECT_TRUE(base::WriteFile(temp_index_path, kDummyData));
628 ASSERT_TRUE(base::PathExists(simple_index_file.GetTempIndexFilePath()));
629
630 // Write the index file.
631 SimpleIndex::EntrySet entries;
632 SimpleIndex::InsertInEntrySet(11, EntryMetadata(Time(), 11u), &entries);
633 net::TestClosure closure;
634 simple_index_file.WriteToDisk(net::DISK_CACHE,
635 SimpleIndex::INDEX_WRITE_REASON_SHUTDOWN,
636 entries, 120U, closure.closure());
637 closure.WaitForResult();
638
639 // Check that the temporary file was deleted and the index file was created.
640 EXPECT_FALSE(base::PathExists(simple_index_file.GetTempIndexFilePath()));
641 EXPECT_TRUE(base::PathExists(simple_index_file.GetIndexFilePath()));
642 }
643
644 } // namespace disk_cache
645