• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 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 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
7 #pragma allow_unsafe_buffers
8 #endif
9 
10 #include "base/files/file_enumerator.h"
11 #include "base/files/file_util.h"
12 #include "build/chromeos_buildflags.h"
13 #include "net/disk_cache/blockfile/block_files.h"
14 #include "net/disk_cache/disk_cache.h"
15 #include "net/disk_cache/disk_cache_test_base.h"
16 #include "net/disk_cache/disk_cache_test_util.h"
17 #include "testing/gtest/include/gtest/gtest.h"
18 
19 using base::Time;
20 
21 namespace {
22 
23 // Returns the number of files in this folder.
NumberOfFiles(const base::FilePath & path)24 int NumberOfFiles(const base::FilePath& path) {
25   base::FileEnumerator iter(path, false, base::FileEnumerator::FILES);
26   int count = 0;
27   for (base::FilePath file = iter.Next(); !file.value().empty();
28        file = iter.Next()) {
29     count++;
30   }
31   return count;
32 }
33 
34 }  // namespace
35 
36 namespace disk_cache {
37 
38 #if BUILDFLAG(IS_CHROMEOS_ASH)
39 // Flaky on ChromeOS: https://crbug.com/1156795
40 #define MAYBE_BlockFiles_Grow DISABLED_BlockFiles_Grow
41 #else
42 #define MAYBE_BlockFiles_Grow BlockFiles_Grow
43 #endif
TEST_F(DiskCacheTest,MAYBE_BlockFiles_Grow)44 TEST_F(DiskCacheTest, MAYBE_BlockFiles_Grow) {
45   ASSERT_TRUE(CleanupCacheDir());
46   ASSERT_TRUE(base::CreateDirectory(cache_path_));
47 
48   BlockFiles files(cache_path_);
49   ASSERT_TRUE(files.Init(true));
50 
51 #if BUILDFLAG(IS_FUCHSIA)
52   // Too slow on Fuchsia: https://crbug.com/1354793
53   const int kMaxSize = 3500;
54   const int kNumberOfFiles = 4;
55 #else
56   const int kMaxSize = 35000;
57   const int kNumberOfFiles = 6;
58 #endif
59   Addr address[kMaxSize];
60 
61   // Fill up the 32-byte block file (use three files).
62   for (auto& addr : address) {
63     EXPECT_TRUE(files.CreateBlock(RANKINGS, 4, &addr));
64   }
65   EXPECT_EQ(kNumberOfFiles, NumberOfFiles(cache_path_));
66 
67   // Make sure we don't keep adding files.
68   for (int i = 0; i < kMaxSize * 4; i += 2) {
69     int target = i % kMaxSize;
70     files.DeleteBlock(address[target], false);
71     EXPECT_TRUE(files.CreateBlock(RANKINGS, 4, &address[target]));
72   }
73   EXPECT_EQ(kNumberOfFiles, NumberOfFiles(cache_path_));
74 }
75 
76 // We should be able to delete empty block files.
TEST_F(DiskCacheTest,BlockFiles_Shrink)77 TEST_F(DiskCacheTest, BlockFiles_Shrink) {
78   ASSERT_TRUE(CleanupCacheDir());
79   ASSERT_TRUE(base::CreateDirectory(cache_path_));
80 
81   BlockFiles files(cache_path_);
82   ASSERT_TRUE(files.Init(true));
83 
84   const int kMaxSize = 35000;
85   Addr address[kMaxSize];
86 
87   // Fill up the 32-byte block file (use three files).
88   for (auto& addr : address) {
89     EXPECT_TRUE(files.CreateBlock(RANKINGS, 4, &addr));
90   }
91 
92   // Now delete all the blocks, so that we can delete the two extra files.
93   for (const auto& addr : address) {
94     files.DeleteBlock(addr, false);
95   }
96   EXPECT_EQ(4, NumberOfFiles(cache_path_));
97 }
98 
99 // Handling of block files not properly closed.
TEST_F(DiskCacheTest,BlockFiles_Recover)100 TEST_F(DiskCacheTest, BlockFiles_Recover) {
101   ASSERT_TRUE(CleanupCacheDir());
102   ASSERT_TRUE(base::CreateDirectory(cache_path_));
103 
104   BlockFiles files(cache_path_);
105   ASSERT_TRUE(files.Init(true));
106 
107   const int kNumEntries = 2000;
108   CacheAddr entries[kNumEntries];
109 
110   int seed = static_cast<int>(Time::Now().ToInternalValue());
111   srand(seed);
112   for (auto& entry : entries) {
113     Addr address(0);
114     int size = (rand() % 4) + 1;
115     EXPECT_TRUE(files.CreateBlock(RANKINGS, size, &address));
116     entry = address.value();
117   }
118 
119   for (int i = 0; i < kNumEntries; i++) {
120     int source1 = rand() % kNumEntries;
121     int source2 = rand() % kNumEntries;
122     CacheAddr temp = entries[source1];
123     entries[source1] = entries[source2];
124     entries[source2] = temp;
125   }
126 
127   for (int i = 0; i < kNumEntries / 2; i++) {
128     Addr address(entries[i]);
129     files.DeleteBlock(address, false);
130   }
131 
132   // At this point, there are kNumEntries / 2 entries on the file, randomly
133   // distributed both on location and size.
134 
135   Addr address(entries[kNumEntries / 2]);
136   MappedFile* file = files.GetFile(address);
137   ASSERT_TRUE(nullptr != file);
138 
139   BlockFileHeader* header =
140       reinterpret_cast<BlockFileHeader*>(file->buffer());
141   ASSERT_TRUE(nullptr != header);
142 
143   ASSERT_EQ(0, header->updating);
144 
145   int max_entries = header->max_entries;
146   int empty_1 = header->empty[0];
147   int empty_2 = header->empty[1];
148   int empty_3 = header->empty[2];
149   int empty_4 = header->empty[3];
150 
151   // Corrupt the file.
152   header->max_entries = header->empty[0] = 0;
153   header->empty[1] = header->empty[2] = header->empty[3] = 0;
154   header->updating = -1;
155 
156   files.CloseFiles();
157 
158   ASSERT_TRUE(files.Init(false));
159 
160   // The file must have been fixed.
161   file = files.GetFile(address);
162   ASSERT_TRUE(nullptr != file);
163 
164   header = reinterpret_cast<BlockFileHeader*>(file->buffer());
165   ASSERT_TRUE(nullptr != header);
166 
167   ASSERT_EQ(0, header->updating);
168 
169   EXPECT_EQ(max_entries, header->max_entries);
170   EXPECT_EQ(empty_1, header->empty[0]);
171   EXPECT_EQ(empty_2, header->empty[1]);
172   EXPECT_EQ(empty_3, header->empty[2]);
173   EXPECT_EQ(empty_4, header->empty[3]);
174 }
175 
176 // Handling of truncated files.
TEST_F(DiskCacheTest,BlockFiles_ZeroSizeFile)177 TEST_F(DiskCacheTest, BlockFiles_ZeroSizeFile) {
178   ASSERT_TRUE(CleanupCacheDir());
179   ASSERT_TRUE(base::CreateDirectory(cache_path_));
180 
181   BlockFiles files(cache_path_);
182   ASSERT_TRUE(files.Init(true));
183 
184   base::FilePath filename = files.Name(0);
185   files.CloseFiles();
186   // Truncate one of the files.
187   {
188     auto file = base::MakeRefCounted<File>();
189     ASSERT_TRUE(file->Init(filename));
190     EXPECT_TRUE(file->SetLength(0));
191   }
192 
193   // Initializing should fail, not crash.
194   ASSERT_FALSE(files.Init(false));
195 }
196 
197 // Handling of truncated files (non empty).
TEST_F(DiskCacheTest,BlockFiles_TruncatedFile)198 TEST_F(DiskCacheTest, BlockFiles_TruncatedFile) {
199   ASSERT_TRUE(CleanupCacheDir());
200   ASSERT_TRUE(base::CreateDirectory(cache_path_));
201 
202   BlockFiles files(cache_path_);
203   ASSERT_TRUE(files.Init(true));
204   Addr address;
205   EXPECT_TRUE(files.CreateBlock(RANKINGS, 2, &address));
206 
207   base::FilePath filename = files.Name(0);
208   files.CloseFiles();
209   // Truncate one of the files.
210   {
211     auto file = base::MakeRefCounted<File>();
212     ASSERT_TRUE(file->Init(filename));
213     EXPECT_TRUE(file->SetLength(15000));
214   }
215 
216   // Initializing should fail, not crash.
217   ASSERT_FALSE(files.Init(false));
218 }
219 
220 // Tests detection of out of sync counters.
TEST_F(DiskCacheTest,BlockFiles_Counters)221 TEST_F(DiskCacheTest, BlockFiles_Counters) {
222   ASSERT_TRUE(CleanupCacheDir());
223   ASSERT_TRUE(base::CreateDirectory(cache_path_));
224 
225   BlockFiles files(cache_path_);
226   ASSERT_TRUE(files.Init(true));
227 
228   // Create a block of size 2.
229   Addr address(0);
230   EXPECT_TRUE(files.CreateBlock(RANKINGS, 2, &address));
231 
232   MappedFile* file = files.GetFile(address);
233   ASSERT_TRUE(nullptr != file);
234 
235   BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer());
236   ASSERT_TRUE(nullptr != header);
237   ASSERT_EQ(0, header->updating);
238 
239   // Alter the counters so that the free space doesn't add up.
240   header->empty[2] = 50;  // 50 free blocks of size 3.
241   files.CloseFiles();
242 
243   ASSERT_TRUE(files.Init(false));
244   file = files.GetFile(address);
245   ASSERT_TRUE(nullptr != file);
246   header = reinterpret_cast<BlockFileHeader*>(file->buffer());
247   ASSERT_TRUE(nullptr != header);
248 
249   // The file must have been fixed.
250   ASSERT_EQ(0, header->empty[2]);
251 
252   // Change the number of entries.
253   header->num_entries = 3;
254   header->updating = 1;
255   files.CloseFiles();
256 
257   ASSERT_TRUE(files.Init(false));
258   file = files.GetFile(address);
259   ASSERT_TRUE(nullptr != file);
260   header = reinterpret_cast<BlockFileHeader*>(file->buffer());
261   ASSERT_TRUE(nullptr != header);
262 
263   // The file must have been "fixed".
264   ASSERT_EQ(2, header->num_entries);
265 
266   // Change the number of entries.
267   header->num_entries = -1;
268   header->updating = 1;
269   files.CloseFiles();
270 
271   // Detect the error.
272   ASSERT_FALSE(files.Init(false));
273 }
274 
275 // An invalid file can be detected after init.
TEST_F(DiskCacheTest,BlockFiles_InvalidFile)276 TEST_F(DiskCacheTest, BlockFiles_InvalidFile) {
277   ASSERT_TRUE(CleanupCacheDir());
278   ASSERT_TRUE(base::CreateDirectory(cache_path_));
279 
280   BlockFiles files(cache_path_);
281   ASSERT_TRUE(files.Init(true));
282 
283   // Let's access block 10 of file 5. (There is no file).
284   Addr addr(BLOCK_256, 1, 5, 10);
285   EXPECT_TRUE(nullptr == files.GetFile(addr));
286 
287   // Let's create an invalid file.
288   base::FilePath filename(files.Name(5));
289   char header[kBlockHeaderSize];
290   memset(header, 'a', kBlockHeaderSize);
291   EXPECT_TRUE(base::WriteFile(filename, {header, kBlockHeaderSize}));
292 
293   EXPECT_TRUE(nullptr == files.GetFile(addr));
294 
295   // The file should not have been changed (it is still invalid).
296   EXPECT_TRUE(nullptr == files.GetFile(addr));
297 }
298 
299 // Tests that we add and remove blocks correctly.
TEST_F(DiskCacheTest,AllocationMap)300 TEST_F(DiskCacheTest, AllocationMap) {
301   ASSERT_TRUE(CleanupCacheDir());
302   ASSERT_TRUE(base::CreateDirectory(cache_path_));
303 
304   BlockFiles files(cache_path_);
305   ASSERT_TRUE(files.Init(true));
306 
307   // Create a bunch of entries.
308   const int kSize = 100;
309   Addr address[kSize];
310   for (int i = 0; i < kSize; i++) {
311     SCOPED_TRACE(i);
312     int block_size = i % 4 + 1;
313     EXPECT_TRUE(files.CreateBlock(BLOCK_1K, block_size, &address[i]));
314     EXPECT_EQ(BLOCK_1K, address[i].file_type());
315     EXPECT_EQ(block_size, address[i].num_blocks());
316     int start = address[i].start_block();
317     EXPECT_EQ(start / 4, (start + block_size - 1) / 4);
318   }
319 
320   for (int i = 0; i < kSize; i++) {
321     SCOPED_TRACE(i);
322     EXPECT_TRUE(files.IsValid(address[i]));
323   }
324 
325   // The first part of the allocation map should be completely filled. We used
326   // 10 bits per each four entries, so 250 bits total.
327   BlockFileHeader* header =
328       reinterpret_cast<BlockFileHeader*>(files.GetFile(address[0])->buffer());
329   uint8_t* buffer = reinterpret_cast<uint8_t*>(&header->allocation_map);
330   for (int i =0; i < 29; i++) {
331     SCOPED_TRACE(i);
332     EXPECT_EQ(0xff, buffer[i]);
333   }
334 
335   for (int i = 0; i < kSize; i++) {
336     SCOPED_TRACE(i);
337     files.DeleteBlock(address[i], false);
338   }
339 
340   // The allocation map should be empty.
341   for (int i =0; i < 50; i++) {
342     SCOPED_TRACE(i);
343     EXPECT_EQ(0, buffer[i]);
344   }
345 }
346 
347 }  // namespace disk_cache
348