Roll src/third_party/WebKit 8b42d1d:744641d (svn 186770:186771)
[chromium-blink-merge.git] / chrome / browser / safe_browsing / safe_browsing_store_file_unittest.cc
blob765b4702a6daa1373ed62d7860215067b7668bce
1 // Copyright (c) 2012 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 "chrome/browser/safe_browsing/safe_browsing_store_file.h"
7 #include "base/bind.h"
8 #include "base/files/file_util.h"
9 #include "base/files/scoped_file.h"
10 #include "base/files/scoped_temp_dir.h"
11 #include "base/md5.h"
12 #include "base/path_service.h"
13 #include "chrome/common/chrome_paths.h"
14 #include "testing/gtest/include/gtest/gtest.h"
15 #include "testing/platform_test.h"
17 namespace {
19 const int kAddChunk1 = 1;
20 const int kAddChunk2 = 3;
21 const int kAddChunk3 = 5;
22 const int kAddChunk4 = 7;
23 // Disjoint chunk numbers for subs to flush out typos.
24 const int kSubChunk1 = 2;
25 const int kSubChunk2 = 4;
27 const SBFullHash kHash1 = SBFullHashForString("one");
28 const SBFullHash kHash2 = SBFullHashForString("two");
29 const SBFullHash kHash3 = SBFullHashForString("three");
30 const SBFullHash kHash4 = SBFullHashForString("four");
31 const SBFullHash kHash5 = SBFullHashForString("five");
32 const SBFullHash kHash6 = SBFullHashForString("six");
34 const SBPrefix kMinSBPrefix = 0u;
35 const SBPrefix kMaxSBPrefix = ~kMinSBPrefix;
37 } // namespace
39 namespace safe_browsing {
41 class SafeBrowsingStoreFileTest : public PlatformTest {
42 public:
43 void SetUp() override {
44 PlatformTest::SetUp();
46 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
48 filename_ = temp_dir_.path();
49 filename_ = filename_.AppendASCII("SafeBrowsingTestStore");
51 store_.reset(new SafeBrowsingStoreFile());
52 store_->Init(filename_,
53 base::Bind(&SafeBrowsingStoreFileTest::OnCorruptionDetected,
54 base::Unretained(this)));
55 corruption_detected_ = false;
57 void TearDown() override {
58 if (store_.get())
59 store_->Delete();
60 store_.reset();
62 PlatformTest::TearDown();
65 void OnCorruptionDetected() {
66 corruption_detected_ = true;
69 // Populate the store with some testing data.
70 void PopulateStore() {
71 ASSERT_TRUE(store_->BeginUpdate());
73 EXPECT_TRUE(store_->BeginChunk());
74 store_->SetAddChunk(kAddChunk1);
75 EXPECT_TRUE(store_->CheckAddChunk(kAddChunk1));
76 EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk1, kHash1.prefix));
77 EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk1, kHash2.prefix));
78 EXPECT_TRUE(store_->FinishChunk());
80 EXPECT_TRUE(store_->BeginChunk());
81 store_->SetSubChunk(kSubChunk1);
82 EXPECT_TRUE(store_->CheckSubChunk(kSubChunk1));
83 EXPECT_TRUE(store_->WriteSubPrefix(kSubChunk1, kAddChunk3, kHash3.prefix));
84 EXPECT_TRUE(store_->WriteSubHash(kSubChunk1, kAddChunk3, kHash3));
85 EXPECT_TRUE(store_->FinishChunk());
87 EXPECT_TRUE(store_->BeginChunk());
88 store_->SetAddChunk(kAddChunk2);
89 EXPECT_TRUE(store_->CheckAddChunk(kAddChunk2));
90 EXPECT_TRUE(store_->WriteAddHash(kAddChunk2, kHash4));
91 EXPECT_TRUE(store_->FinishChunk());
93 // Chunk numbers shouldn't leak over.
94 EXPECT_FALSE(store_->CheckAddChunk(kSubChunk1));
95 EXPECT_FALSE(store_->CheckAddChunk(kAddChunk3));
96 EXPECT_FALSE(store_->CheckSubChunk(kAddChunk1));
97 EXPECT_FALSE(store_->CheckSubChunk(kAddChunk2));
99 safe_browsing::PrefixSetBuilder builder;
100 std::vector<SBAddFullHash> add_full_hashes_result;
102 EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
105 // Manually read the shard stride info from the file.
106 uint32 ReadStride() {
107 base::ScopedFILE file(base::OpenFile(filename_, "rb"));
108 const long kOffset = 4 * sizeof(uint32);
109 EXPECT_EQ(fseek(file.get(), kOffset, SEEK_SET), 0);
110 uint32 shard_stride = 0;
111 EXPECT_EQ(fread(&shard_stride, sizeof(shard_stride), 1, file.get()), 1U);
112 return shard_stride;
115 base::ScopedTempDir temp_dir_;
116 base::FilePath filename_;
117 scoped_ptr<SafeBrowsingStoreFile> store_;
118 bool corruption_detected_;
121 // Test that the empty store looks empty.
122 TEST_F(SafeBrowsingStoreFileTest, Empty) {
123 ASSERT_TRUE(store_->BeginUpdate());
125 std::vector<int> chunks;
126 store_->GetAddChunks(&chunks);
127 EXPECT_TRUE(chunks.empty());
128 store_->GetSubChunks(&chunks);
129 EXPECT_TRUE(chunks.empty());
131 // Shouldn't see anything, but anything is a big set to test.
132 EXPECT_FALSE(store_->CheckAddChunk(0));
133 EXPECT_FALSE(store_->CheckAddChunk(1));
134 EXPECT_FALSE(store_->CheckAddChunk(-1));
136 EXPECT_FALSE(store_->CheckSubChunk(0));
137 EXPECT_FALSE(store_->CheckSubChunk(1));
138 EXPECT_FALSE(store_->CheckSubChunk(-1));
140 safe_browsing::PrefixSetBuilder builder;
141 std::vector<SBAddFullHash> add_full_hashes_result;
143 EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
144 EXPECT_TRUE(add_full_hashes_result.empty());
146 std::vector<SBPrefix> prefixes_result;
147 builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
148 EXPECT_TRUE(prefixes_result.empty());
151 // Write some prefix and hash data to the store, add more data in another
152 // transaction, then verify that the union of all the data is present.
153 TEST_F(SafeBrowsingStoreFileTest, BasicStore) {
154 PopulateStore();
156 ASSERT_TRUE(store_->BeginUpdate());
158 std::vector<int> chunks;
159 store_->GetAddChunks(&chunks);
160 ASSERT_EQ(2U, chunks.size());
161 EXPECT_EQ(kAddChunk1, chunks[0]);
162 EXPECT_EQ(kAddChunk2, chunks[1]);
164 store_->GetSubChunks(&chunks);
165 ASSERT_EQ(1U, chunks.size());
166 EXPECT_EQ(kSubChunk1, chunks[0]);
168 EXPECT_TRUE(store_->CheckAddChunk(kAddChunk1));
169 EXPECT_TRUE(store_->CheckSubChunk(kSubChunk1));
171 EXPECT_TRUE(store_->BeginChunk());
172 store_->SetAddChunk(kAddChunk3);
173 EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk3, kHash5.prefix));
174 EXPECT_TRUE(store_->FinishChunk());
176 // Still has the chunks expected in the next update.
178 safe_browsing::PrefixSetBuilder builder;
179 std::vector<SBAddFullHash> add_full_hashes_result;
180 EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
182 std::vector<SBPrefix> prefixes_result;
183 builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
184 ASSERT_EQ(3U, prefixes_result.size());
185 EXPECT_EQ(kHash1.prefix, prefixes_result[0]);
186 EXPECT_EQ(kHash5.prefix, prefixes_result[1]);
187 EXPECT_EQ(kHash2.prefix, prefixes_result[2]);
189 ASSERT_EQ(1U, add_full_hashes_result.size());
190 EXPECT_EQ(kAddChunk2, add_full_hashes_result[0].chunk_id);
191 EXPECT_TRUE(SBFullHashEqual(kHash4, add_full_hashes_result[0].full_hash));
195 // Verify that the min and max prefixes are stored and operated on.
196 TEST_F(SafeBrowsingStoreFileTest, PrefixMinMax) {
197 PopulateStore();
199 ASSERT_TRUE(store_->BeginUpdate());
201 EXPECT_TRUE(store_->BeginChunk());
202 store_->SetAddChunk(kAddChunk3);
203 EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk3, kMinSBPrefix));
204 EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk3, kMaxSBPrefix));
205 EXPECT_TRUE(store_->FinishChunk());
208 safe_browsing::PrefixSetBuilder builder;
209 std::vector<SBAddFullHash> add_full_hashes_result;
210 EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
212 std::vector<SBPrefix> prefixes_result;
213 builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
214 ASSERT_EQ(4U, prefixes_result.size());
215 EXPECT_EQ(kMinSBPrefix, prefixes_result[0]);
216 EXPECT_EQ(kHash1.prefix, prefixes_result[1]);
217 EXPECT_EQ(kHash2.prefix, prefixes_result[2]);
218 EXPECT_EQ(kMaxSBPrefix, prefixes_result[3]);
221 ASSERT_TRUE(store_->BeginUpdate());
223 EXPECT_TRUE(store_->BeginChunk());
224 store_->SetAddChunk(kSubChunk2);
225 EXPECT_TRUE(store_->WriteSubPrefix(kSubChunk2, kAddChunk3, kMinSBPrefix));
226 EXPECT_TRUE(store_->WriteSubPrefix(kSubChunk2, kAddChunk3, kMaxSBPrefix));
227 EXPECT_TRUE(store_->FinishChunk());
230 safe_browsing::PrefixSetBuilder builder;
231 std::vector<SBAddFullHash> add_full_hashes_result;
232 EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
234 std::vector<SBPrefix> prefixes_result;
235 builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
236 ASSERT_EQ(2U, prefixes_result.size());
237 EXPECT_EQ(kHash1.prefix, prefixes_result[0]);
238 EXPECT_EQ(kHash2.prefix, prefixes_result[1]);
242 // Test that subs knockout adds.
243 TEST_F(SafeBrowsingStoreFileTest, SubKnockout) {
244 ASSERT_TRUE(store_->BeginUpdate());
246 EXPECT_TRUE(store_->BeginChunk());
247 store_->SetAddChunk(kAddChunk1);
248 EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk1, kHash1.prefix));
249 EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk1, kHash2.prefix));
250 EXPECT_TRUE(store_->FinishChunk());
252 EXPECT_TRUE(store_->BeginChunk());
253 store_->SetAddChunk(kAddChunk2);
254 EXPECT_TRUE(store_->WriteAddHash(kAddChunk2, kHash4));
255 EXPECT_TRUE(store_->FinishChunk());
257 EXPECT_TRUE(store_->BeginChunk());
258 store_->SetSubChunk(kSubChunk1);
259 EXPECT_TRUE(store_->WriteSubPrefix(kSubChunk1, kAddChunk3, kHash3.prefix));
260 EXPECT_TRUE(store_->WriteSubPrefix(kSubChunk1, kAddChunk1, kHash2.prefix));
261 EXPECT_TRUE(store_->FinishChunk());
264 safe_browsing::PrefixSetBuilder builder;
265 std::vector<SBAddFullHash> add_full_hashes_result;
266 EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
268 // Knocked out the chunk expected.
269 std::vector<SBPrefix> prefixes_result;
270 builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
271 ASSERT_EQ(1U, prefixes_result.size());
272 EXPECT_EQ(kHash1.prefix, prefixes_result[0]);
274 ASSERT_EQ(1U, add_full_hashes_result.size());
275 EXPECT_EQ(kAddChunk2, add_full_hashes_result[0].chunk_id);
276 EXPECT_TRUE(SBFullHashEqual(kHash4, add_full_hashes_result[0].full_hash));
279 ASSERT_TRUE(store_->BeginUpdate());
281 // This add should be knocked out by an existing sub.
282 EXPECT_TRUE(store_->BeginChunk());
283 store_->SetAddChunk(kAddChunk3);
284 EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk3, kHash3.prefix));
285 EXPECT_TRUE(store_->FinishChunk());
288 safe_browsing::PrefixSetBuilder builder;
289 std::vector<SBAddFullHash> add_full_hashes_result;
290 EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
292 std::vector<SBPrefix> prefixes_result;
293 builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
294 ASSERT_EQ(1U, prefixes_result.size());
295 EXPECT_EQ(kHash1.prefix, prefixes_result[0]);
297 ASSERT_EQ(1U, add_full_hashes_result.size());
298 EXPECT_EQ(kAddChunk2, add_full_hashes_result[0].chunk_id);
299 EXPECT_TRUE(SBFullHashEqual(kHash4, add_full_hashes_result[0].full_hash));
302 ASSERT_TRUE(store_->BeginUpdate());
304 // But by here the sub should be gone, so it should stick this time.
305 EXPECT_TRUE(store_->BeginChunk());
306 store_->SetAddChunk(kAddChunk3);
307 EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk3, kHash3.prefix));
308 EXPECT_TRUE(store_->FinishChunk());
311 safe_browsing::PrefixSetBuilder builder;
312 std::vector<SBAddFullHash> add_full_hashes_result;
313 EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
315 std::vector<SBPrefix> prefixes_result;
316 builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
317 ASSERT_EQ(2U, prefixes_result.size());
318 EXPECT_EQ(kHash1.prefix, prefixes_result[0]);
319 EXPECT_EQ(kHash3.prefix, prefixes_result[1]);
321 ASSERT_EQ(1U, add_full_hashes_result.size());
322 EXPECT_EQ(kAddChunk2, add_full_hashes_result[0].chunk_id);
323 EXPECT_TRUE(SBFullHashEqual(kHash4, add_full_hashes_result[0].full_hash));
327 // Test that deletes delete the chunk's data.
328 TEST_F(SafeBrowsingStoreFileTest, DeleteChunks) {
329 ASSERT_TRUE(store_->BeginUpdate());
331 // A prefix chunk which will be deleted.
332 EXPECT_FALSE(store_->CheckAddChunk(kAddChunk1));
333 store_->SetAddChunk(kAddChunk1);
334 EXPECT_TRUE(store_->BeginChunk());
335 EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk1, kHash1.prefix));
336 EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk1, kHash2.prefix));
337 EXPECT_TRUE(store_->FinishChunk());
339 // A prefix chunk which won't be deleted.
340 EXPECT_FALSE(store_->CheckAddChunk(kAddChunk2));
341 store_->SetAddChunk(kAddChunk2);
342 EXPECT_TRUE(store_->BeginChunk());
343 EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk2, kHash3.prefix));
344 EXPECT_TRUE(store_->FinishChunk());
346 // A full-hash chunk which won't be deleted.
347 EXPECT_FALSE(store_->CheckAddChunk(kAddChunk3));
348 store_->SetAddChunk(kAddChunk3);
349 EXPECT_TRUE(store_->BeginChunk());
350 EXPECT_TRUE(store_->WriteAddHash(kAddChunk3, kHash6));
351 EXPECT_TRUE(store_->FinishChunk());
353 // A sub chunk to delete.
354 EXPECT_FALSE(store_->CheckSubChunk(kSubChunk1));
355 store_->SetSubChunk(kSubChunk1);
356 EXPECT_TRUE(store_->BeginChunk());
357 EXPECT_TRUE(store_->WriteSubHash(kSubChunk1, kAddChunk3, kHash4));
358 EXPECT_TRUE(store_->FinishChunk());
360 // A sub chunk to keep.
361 EXPECT_FALSE(store_->CheckSubChunk(kSubChunk2));
362 store_->SetSubChunk(kSubChunk2);
363 EXPECT_TRUE(store_->BeginChunk());
364 EXPECT_TRUE(store_->WriteSubHash(kSubChunk2, kAddChunk4, kHash5));
365 EXPECT_TRUE(store_->FinishChunk());
367 store_->DeleteAddChunk(kAddChunk1);
368 store_->DeleteSubChunk(kSubChunk1);
370 // Not actually deleted until finish.
371 EXPECT_TRUE(store_->CheckAddChunk(kAddChunk1));
372 EXPECT_TRUE(store_->CheckAddChunk(kAddChunk2));
373 EXPECT_TRUE(store_->CheckAddChunk(kAddChunk3));
374 EXPECT_TRUE(store_->CheckSubChunk(kSubChunk1));
375 EXPECT_TRUE(store_->CheckSubChunk(kSubChunk2));
378 safe_browsing::PrefixSetBuilder builder;
379 std::vector<SBAddFullHash> add_full_hashes_result;
380 EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
382 std::vector<SBPrefix> prefixes_result;
383 builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
384 ASSERT_EQ(1U, prefixes_result.size());
385 EXPECT_EQ(kHash3.prefix, prefixes_result[0]);
387 ASSERT_EQ(1U, add_full_hashes_result.size());
388 EXPECT_EQ(kAddChunk3, add_full_hashes_result[0].chunk_id);
389 EXPECT_TRUE(SBFullHashEqual(kHash6, add_full_hashes_result[0].full_hash));
392 // Expected chunks are there in another update.
393 ASSERT_TRUE(store_->BeginUpdate());
394 EXPECT_FALSE(store_->CheckAddChunk(kAddChunk1));
395 EXPECT_TRUE(store_->CheckAddChunk(kAddChunk2));
396 EXPECT_TRUE(store_->CheckAddChunk(kAddChunk3));
397 EXPECT_FALSE(store_->CheckSubChunk(kSubChunk1));
398 EXPECT_TRUE(store_->CheckSubChunk(kSubChunk2));
400 // Delete them, too.
401 store_->DeleteAddChunk(kAddChunk2);
402 store_->DeleteAddChunk(kAddChunk3);
403 store_->DeleteSubChunk(kSubChunk2);
406 safe_browsing::PrefixSetBuilder builder;
407 std::vector<SBAddFullHash> add_full_hashes_result;
408 EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
410 std::vector<SBPrefix> prefixes_result;
411 builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
412 EXPECT_TRUE(prefixes_result.empty());
413 EXPECT_TRUE(add_full_hashes_result.empty());
416 // Expect no more chunks.
417 ASSERT_TRUE(store_->BeginUpdate());
418 EXPECT_FALSE(store_->CheckAddChunk(kAddChunk1));
419 EXPECT_FALSE(store_->CheckAddChunk(kAddChunk2));
420 EXPECT_FALSE(store_->CheckAddChunk(kAddChunk3));
421 EXPECT_FALSE(store_->CheckSubChunk(kSubChunk1));
422 EXPECT_FALSE(store_->CheckSubChunk(kSubChunk2));
425 safe_browsing::PrefixSetBuilder builder;
426 std::vector<SBAddFullHash> add_full_hashes_result;
427 EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
429 std::vector<SBPrefix> prefixes_result;
430 builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
431 EXPECT_TRUE(prefixes_result.empty());
432 EXPECT_TRUE(add_full_hashes_result.empty());
436 // Test that deleting the store deletes the store.
437 TEST_F(SafeBrowsingStoreFileTest, Delete) {
438 // Delete should work if the file wasn't there in the first place.
439 EXPECT_FALSE(base::PathExists(filename_));
440 EXPECT_TRUE(store_->Delete());
442 // Create a store file.
443 PopulateStore();
445 EXPECT_TRUE(base::PathExists(filename_));
446 EXPECT_TRUE(store_->Delete());
447 EXPECT_FALSE(base::PathExists(filename_));
450 // Test that Delete() deletes the temporary store, if present.
451 TEST_F(SafeBrowsingStoreFileTest, DeleteTemp) {
452 const base::FilePath temp_file =
453 SafeBrowsingStoreFile::TemporaryFileForFilename(filename_);
455 EXPECT_FALSE(base::PathExists(filename_));
456 EXPECT_FALSE(base::PathExists(temp_file));
458 // Starting a transaction creates a temporary file.
459 ASSERT_TRUE(store_->BeginUpdate());
460 EXPECT_TRUE(base::PathExists(temp_file));
462 // Pull the rug out from under the existing store, simulating a
463 // crash.
464 store_.reset(new SafeBrowsingStoreFile());
465 store_->Init(filename_, base::Closure());
466 EXPECT_FALSE(base::PathExists(filename_));
467 EXPECT_TRUE(base::PathExists(temp_file));
469 // Make sure the temporary file is deleted.
470 EXPECT_TRUE(store_->Delete());
471 EXPECT_FALSE(base::PathExists(filename_));
472 EXPECT_FALSE(base::PathExists(temp_file));
475 // Test basic corruption-handling.
476 TEST_F(SafeBrowsingStoreFileTest, DetectsCorruption) {
477 // Load a store with some data.
478 PopulateStore();
480 // Can successfully open and read the store.
482 std::vector<SBPrefix> orig_prefixes;
483 std::vector<SBAddFullHash> orig_hashes;
484 safe_browsing::PrefixSetBuilder builder;
485 ASSERT_TRUE(store_->BeginUpdate());
486 EXPECT_TRUE(store_->FinishUpdate(&builder, &orig_hashes));
487 builder.GetPrefixSetNoHashes()->GetPrefixes(&orig_prefixes);
488 EXPECT_GT(orig_prefixes.size(), 0U);
489 EXPECT_GT(orig_hashes.size(), 0U);
490 EXPECT_FALSE(corruption_detected_);
493 // Corrupt the store.
494 base::ScopedFILE file(base::OpenFile(filename_, "rb+"));
495 const long kOffset = 60;
496 EXPECT_EQ(fseek(file.get(), kOffset, SEEK_SET), 0);
497 const uint32 kZero = 0;
498 uint32 previous = kZero;
499 EXPECT_EQ(fread(&previous, sizeof(previous), 1, file.get()), 1U);
500 EXPECT_NE(previous, kZero);
501 EXPECT_EQ(fseek(file.get(), kOffset, SEEK_SET), 0);
502 EXPECT_EQ(fwrite(&kZero, sizeof(kZero), 1, file.get()), 1U);
503 file.reset();
505 // Update fails and corruption callback is called.
506 std::vector<SBAddFullHash> add_hashes;
507 corruption_detected_ = false;
509 safe_browsing::PrefixSetBuilder builder;
510 ASSERT_TRUE(store_->BeginUpdate());
511 EXPECT_FALSE(store_->FinishUpdate(&builder, &add_hashes));
512 EXPECT_TRUE(corruption_detected_);
515 // Make it look like there is a lot of add-chunks-seen data.
516 const long kAddChunkCountOffset = 2 * sizeof(int32);
517 const int32 kLargeCount = 1000 * 1000 * 1000;
518 file.reset(base::OpenFile(filename_, "rb+"));
519 EXPECT_EQ(fseek(file.get(), kAddChunkCountOffset, SEEK_SET), 0);
520 EXPECT_EQ(fwrite(&kLargeCount, sizeof(kLargeCount), 1, file.get()), 1U);
521 file.reset();
523 // Detects corruption and fails to even begin the update.
524 corruption_detected_ = false;
525 EXPECT_FALSE(store_->BeginUpdate());
526 EXPECT_TRUE(corruption_detected_);
529 TEST_F(SafeBrowsingStoreFileTest, CheckValidity) {
530 // Empty store is valid.
531 EXPECT_FALSE(base::PathExists(filename_));
532 ASSERT_TRUE(store_->BeginUpdate());
533 EXPECT_FALSE(corruption_detected_);
534 EXPECT_TRUE(store_->CheckValidity());
535 EXPECT_FALSE(corruption_detected_);
536 EXPECT_TRUE(store_->CancelUpdate());
538 // A store with some data is valid.
539 EXPECT_FALSE(base::PathExists(filename_));
540 PopulateStore();
541 EXPECT_TRUE(base::PathExists(filename_));
542 ASSERT_TRUE(store_->BeginUpdate());
543 EXPECT_FALSE(corruption_detected_);
544 EXPECT_TRUE(store_->CheckValidity());
545 EXPECT_FALSE(corruption_detected_);
546 EXPECT_TRUE(store_->CancelUpdate());
549 // Corrupt the header.
550 TEST_F(SafeBrowsingStoreFileTest, CheckValidityHeader) {
551 PopulateStore();
552 EXPECT_TRUE(base::PathExists(filename_));
554 // 37 is the most random prime number. It's also past the initial header
555 // struct, somewhere in the chunk list.
556 const size_t kOffset = 37;
559 base::ScopedFILE file(base::OpenFile(filename_, "rb+"));
560 EXPECT_EQ(0, fseek(file.get(), kOffset, SEEK_SET));
561 EXPECT_GE(fputs("hello", file.get()), 0);
563 ASSERT_FALSE(store_->BeginUpdate());
564 EXPECT_TRUE(corruption_detected_);
567 // Corrupt the prefix payload.
568 TEST_F(SafeBrowsingStoreFileTest, CheckValidityPayload) {
569 PopulateStore();
570 EXPECT_TRUE(base::PathExists(filename_));
572 // 137 is the second most random prime number. It's also past the header and
573 // chunk-id area. Corrupting the header would fail BeginUpdate() in which
574 // case CheckValidity() cannot be called.
575 const size_t kOffset = 137;
578 base::ScopedFILE file(base::OpenFile(filename_, "rb+"));
579 EXPECT_EQ(0, fseek(file.get(), kOffset, SEEK_SET));
580 EXPECT_GE(fputs("hello", file.get()), 0);
582 ASSERT_TRUE(store_->BeginUpdate());
583 EXPECT_FALSE(corruption_detected_);
584 EXPECT_FALSE(store_->CheckValidity());
585 EXPECT_TRUE(corruption_detected_);
586 EXPECT_TRUE(store_->CancelUpdate());
589 // Corrupt the checksum.
590 TEST_F(SafeBrowsingStoreFileTest, CheckValidityChecksum) {
591 PopulateStore();
592 EXPECT_TRUE(base::PathExists(filename_));
594 // An offset from the end of the file which is in the checksum.
595 const int kOffset = -static_cast<int>(sizeof(base::MD5Digest));
598 base::ScopedFILE file(base::OpenFile(filename_, "rb+"));
599 EXPECT_EQ(0, fseek(file.get(), kOffset, SEEK_END));
600 EXPECT_GE(fputs("hello", file.get()), 0);
602 ASSERT_TRUE(store_->BeginUpdate());
603 EXPECT_FALSE(corruption_detected_);
604 EXPECT_FALSE(store_->CheckValidity());
605 EXPECT_TRUE(corruption_detected_);
606 EXPECT_TRUE(store_->CancelUpdate());
609 TEST_F(SafeBrowsingStoreFileTest, GetAddPrefixesAndHashes) {
610 ASSERT_TRUE(store_->BeginUpdate());
612 EXPECT_TRUE(store_->BeginChunk());
613 store_->SetAddChunk(kAddChunk1);
614 EXPECT_TRUE(store_->CheckAddChunk(kAddChunk1));
615 EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk1, kHash1.prefix));
616 EXPECT_TRUE(store_->WriteAddPrefix(kAddChunk1, kHash2.prefix));
617 EXPECT_TRUE(store_->FinishChunk());
619 EXPECT_TRUE(store_->BeginChunk());
620 store_->SetAddChunk(kAddChunk2);
621 EXPECT_TRUE(store_->CheckAddChunk(kAddChunk2));
622 EXPECT_TRUE(store_->WriteAddHash(kAddChunk2, kHash4));
623 EXPECT_TRUE(store_->FinishChunk());
625 store_->SetSubChunk(kSubChunk1);
626 EXPECT_TRUE(store_->CheckSubChunk(kSubChunk1));
627 EXPECT_TRUE(store_->WriteSubPrefix(kSubChunk1, kAddChunk3, kHash3.prefix));
628 EXPECT_TRUE(store_->WriteSubHash(kSubChunk1, kAddChunk3, kHash3));
629 EXPECT_TRUE(store_->FinishChunk());
631 // Chunk numbers shouldn't leak over.
632 EXPECT_FALSE(store_->CheckAddChunk(kSubChunk1));
633 EXPECT_FALSE(store_->CheckAddChunk(kAddChunk3));
634 EXPECT_FALSE(store_->CheckSubChunk(kAddChunk1));
636 std::vector<int> chunks;
637 store_->GetAddChunks(&chunks);
638 ASSERT_EQ(2U, chunks.size());
639 EXPECT_EQ(kAddChunk1, chunks[0]);
640 EXPECT_EQ(kAddChunk2, chunks[1]);
642 store_->GetSubChunks(&chunks);
643 ASSERT_EQ(1U, chunks.size());
644 EXPECT_EQ(kSubChunk1, chunks[0]);
646 safe_browsing::PrefixSetBuilder builder;
647 std::vector<SBAddFullHash> add_full_hashes_result;
648 EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
650 SBAddPrefixes add_prefixes;
651 EXPECT_TRUE(store_->GetAddPrefixes(&add_prefixes));
652 ASSERT_EQ(2U, add_prefixes.size());
653 EXPECT_EQ(kAddChunk1, add_prefixes[0].chunk_id);
654 EXPECT_EQ(kHash1.prefix, add_prefixes[0].prefix);
655 EXPECT_EQ(kAddChunk1, add_prefixes[1].chunk_id);
656 EXPECT_EQ(kHash2.prefix, add_prefixes[1].prefix);
658 std::vector<SBAddFullHash> add_hashes;
659 EXPECT_TRUE(store_->GetAddFullHashes(&add_hashes));
660 ASSERT_EQ(1U, add_hashes.size());
661 EXPECT_EQ(kAddChunk2, add_hashes[0].chunk_id);
662 EXPECT_TRUE(SBFullHashEqual(kHash4, add_hashes[0].full_hash));
665 // Test that the database handles resharding correctly, both when growing and
666 // which shrinking.
667 TEST_F(SafeBrowsingStoreFileTest, Resharding) {
668 // Loop through multiple stride boundaries (1<<32, 1<<31, 1<<30, 1<<29).
669 const uint32 kTargetStride = 1 << 29;
671 // Each chunk will require 8 bytes per prefix, plus 4 bytes for chunk
672 // information. It should be less than |kTargetFootprint| in the
673 // implementation, but high enough to keep the number of rewrites modest (to
674 // keep the test fast).
675 const size_t kPrefixesPerChunk = 10000;
677 uint32 shard_stride = 0;
678 int chunk_id = 1;
680 // Add a series of chunks, tracking that the stride size changes in a
681 // direction appropriate to increasing file size.
682 do {
683 ASSERT_TRUE(store_->BeginUpdate());
685 EXPECT_TRUE(store_->BeginChunk());
686 store_->SetAddChunk(chunk_id);
687 EXPECT_TRUE(store_->CheckAddChunk(chunk_id));
688 for (size_t i = 0; i < kPrefixesPerChunk; ++i) {
689 EXPECT_TRUE(store_->WriteAddPrefix(chunk_id, static_cast<SBPrefix>(i)));
691 EXPECT_TRUE(store_->FinishChunk());
693 safe_browsing::PrefixSetBuilder builder;
694 std::vector<SBAddFullHash> add_full_hashes_result;
695 EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
697 SBAddPrefixes add_prefixes;
698 EXPECT_TRUE(store_->GetAddPrefixes(&add_prefixes));
699 ASSERT_EQ(chunk_id * kPrefixesPerChunk, add_prefixes.size());
701 // New stride should be the same, or shifted one right.
702 const uint32 new_shard_stride = ReadStride();
703 EXPECT_TRUE((new_shard_stride == shard_stride) ||
704 ((new_shard_stride << 1) == shard_stride));
705 shard_stride = new_shard_stride;
706 ++chunk_id;
707 } while (!shard_stride || shard_stride > kTargetStride);
709 // Guard against writing too many chunks. If this gets too big, adjust
710 // |kPrefixesPerChunk|.
711 EXPECT_LT(chunk_id, 20);
713 // Remove each chunk and check that the stride goes back to 0.
714 while (--chunk_id) {
715 ASSERT_TRUE(store_->BeginUpdate());
716 EXPECT_TRUE(store_->CheckAddChunk(chunk_id));
717 EXPECT_FALSE(store_->CheckAddChunk(chunk_id + 1));
718 store_->DeleteAddChunk(chunk_id);
720 safe_browsing::PrefixSetBuilder builder;
721 std::vector<SBAddFullHash> add_full_hashes_result;
722 EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
724 // New stride should be the same, or shifted one left.
725 const uint32 new_shard_stride = ReadStride();
726 EXPECT_TRUE((new_shard_stride == shard_stride) ||
727 (new_shard_stride == (shard_stride << 1)));
728 shard_stride = new_shard_stride;
730 EXPECT_EQ(0u, shard_stride);
733 // Test that a golden v7 file can no longer be read. All platforms generating
734 // v7 files were little-endian, so there is no point to testing this transition
735 // if/when a big-endian port is added.
736 #if defined(ARCH_CPU_LITTLE_ENDIAN)
737 TEST_F(SafeBrowsingStoreFileTest, Version7) {
738 store_.reset();
740 // Copy the golden file into temporary storage. The golden file contains:
741 // - Add chunk kAddChunk1 containing kHash1.prefix and kHash2.
742 // - Sub chunk kSubChunk1 containing kHash3.
743 const char kBasename[] = "FileStoreVersion7";
744 base::FilePath golden_path;
745 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &golden_path));
746 golden_path = golden_path.AppendASCII("SafeBrowsing");
747 golden_path = golden_path.AppendASCII(kBasename);
748 ASSERT_TRUE(base::CopyFile(golden_path, filename_));
750 // Reset the store to make sure it re-reads the file.
751 store_.reset(new SafeBrowsingStoreFile());
752 store_->Init(filename_,
753 base::Bind(&SafeBrowsingStoreFileTest::OnCorruptionDetected,
754 base::Unretained(this)));
755 EXPECT_FALSE(corruption_detected_);
757 // The unknown version should be encountered on the first read.
758 EXPECT_FALSE(store_->BeginUpdate());
759 EXPECT_TRUE(corruption_detected_);
761 // No more to test because corrupt file stores are cleaned up by the database
762 // containing them.
764 #endif
766 // Test that a golden v8 file can be read by the current code. All platforms
767 // generating v8 files are little-endian, so there is no point to testing this
768 // transition if/when a big-endian port is added.
769 #if defined(ARCH_CPU_LITTLE_ENDIAN)
770 TEST_F(SafeBrowsingStoreFileTest, Version8) {
771 store_.reset();
773 // Copy the golden file into temporary storage. The golden file contains:
774 // - Add chunk kAddChunk1 containing kHash1.prefix and kHash2.
775 // - Sub chunk kSubChunk1 containing kHash3.
776 const char kBasename[] = "FileStoreVersion8";
777 base::FilePath golden_path;
778 ASSERT_TRUE(PathService::Get(chrome::DIR_TEST_DATA, &golden_path));
779 golden_path = golden_path.AppendASCII("SafeBrowsing");
780 golden_path = golden_path.AppendASCII(kBasename);
781 ASSERT_TRUE(base::CopyFile(golden_path, filename_));
783 // Reset the store to make sure it re-reads the file.
784 store_.reset(new SafeBrowsingStoreFile());
785 store_->Init(filename_,
786 base::Bind(&SafeBrowsingStoreFileTest::OnCorruptionDetected,
787 base::Unretained(this)));
789 // Check that the expected prefixes and hashes are in place.
790 SBAddPrefixes add_prefixes;
791 EXPECT_TRUE(store_->GetAddPrefixes(&add_prefixes));
792 ASSERT_EQ(2U, add_prefixes.size());
793 EXPECT_EQ(kAddChunk1, add_prefixes[0].chunk_id);
794 EXPECT_EQ(kHash1.prefix, add_prefixes[0].prefix);
795 EXPECT_EQ(kAddChunk1, add_prefixes[1].chunk_id);
796 EXPECT_EQ(kHash2.prefix, add_prefixes[1].prefix);
798 std::vector<SBAddFullHash> add_hashes;
799 EXPECT_TRUE(store_->GetAddFullHashes(&add_hashes));
800 ASSERT_EQ(1U, add_hashes.size());
801 EXPECT_EQ(kAddChunk1, add_hashes[0].chunk_id);
802 EXPECT_TRUE(SBFullHashEqual(kHash2, add_hashes[0].full_hash));
804 // Attempt an update to make sure things work end-to-end.
805 EXPECT_TRUE(store_->BeginUpdate());
807 // Still has the chunks expected in the next update.
808 std::vector<int> chunks;
809 store_->GetAddChunks(&chunks);
810 ASSERT_EQ(1U, chunks.size());
811 EXPECT_EQ(kAddChunk1, chunks[0]);
813 store_->GetSubChunks(&chunks);
814 ASSERT_EQ(1U, chunks.size());
815 EXPECT_EQ(kSubChunk1, chunks[0]);
817 EXPECT_TRUE(store_->CheckAddChunk(kAddChunk1));
818 EXPECT_TRUE(store_->CheckSubChunk(kSubChunk1));
820 // Sub chunk kAddChunk1 hash kHash2.
821 // NOTE(shess): Having full hashes and prefixes in the same chunk is no longer
822 // supported, though it was when this code was written.
823 store_->SetSubChunk(kSubChunk2);
824 EXPECT_TRUE(store_->CheckSubChunk(kSubChunk1));
825 EXPECT_TRUE(store_->WriteSubPrefix(kSubChunk1, kAddChunk1, kHash2.prefix));
826 EXPECT_TRUE(store_->WriteSubHash(kSubChunk1, kAddChunk1, kHash2));
827 EXPECT_TRUE(store_->FinishChunk());
830 safe_browsing::PrefixSetBuilder builder;
831 std::vector<SBAddFullHash> add_full_hashes_result;
832 EXPECT_TRUE(store_->FinishUpdate(&builder, &add_full_hashes_result));
834 // The sub'ed prefix and hash are gone.
835 std::vector<SBPrefix> prefixes_result;
836 builder.GetPrefixSetNoHashes()->GetPrefixes(&prefixes_result);
837 ASSERT_EQ(1U, prefixes_result.size());
838 EXPECT_EQ(kHash1.prefix, prefixes_result[0]);
839 EXPECT_TRUE(add_full_hashes_result.empty());
842 #endif
844 } // namespace safe_browsing