1// Copyright (c) 2010 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 "chrome/browser/safe_browsing/safe_browsing_store_file.h"
6
7#include "base/callback.h"
8#include "chrome/browser/safe_browsing/safe_browsing_store_unittest_helper.h"
9#include "chrome/test/file_test_utils.h"
10#include "testing/gtest/include/gtest/gtest.h"
11#include "testing/platform_test.h"
12
13namespace {
14
15const FilePath::CharType kFolderPrefix[] =
16    FILE_PATH_LITERAL("SafeBrowsingTestStoreFile");
17
18class SafeBrowsingStoreFileTest : public PlatformTest {
19 public:
20  virtual void SetUp() {
21    PlatformTest::SetUp();
22
23    FilePath temp_dir;
24    ASSERT_TRUE(file_util::CreateNewTempDirectory(kFolderPrefix, &temp_dir));
25
26    file_deleter_.reset(new FileAutoDeleter(temp_dir));
27
28    filename_ = temp_dir;
29    filename_ = filename_.AppendASCII("SafeBrowsingTestStore");
30    file_util::Delete(filename_, false);
31
32    // Make sure an old temporary file isn't hanging around.
33    const FilePath temp_file =
34        SafeBrowsingStoreFile::TemporaryFileForFilename(filename_);
35    file_util::Delete(temp_file, false);
36
37    store_.reset(new SafeBrowsingStoreFile());
38    store_->Init(filename_, NULL);
39  }
40  virtual void TearDown() {
41    if (store_.get())
42      store_->Delete();
43    store_.reset();
44    file_deleter_.reset();
45
46    PlatformTest::TearDown();
47  }
48
49  void OnCorruptionDetected() {
50    corruption_detected_ = true;
51  }
52
53  scoped_ptr<FileAutoDeleter> file_deleter_;
54  FilePath filename_;
55  scoped_ptr<SafeBrowsingStoreFile> store_;
56  bool corruption_detected_;
57};
58
59TEST_STORE(SafeBrowsingStoreFileTest, store_.get(), filename_);
60
61// Test that Delete() deletes the temporary store, if present.
62TEST_F(SafeBrowsingStoreFileTest, DeleteTemp) {
63    const FilePath temp_file =
64        SafeBrowsingStoreFile::TemporaryFileForFilename(filename_);
65
66  EXPECT_FALSE(file_util::PathExists(filename_));
67  EXPECT_FALSE(file_util::PathExists(temp_file));
68
69  // Starting a transaction creates a temporary file.
70  EXPECT_TRUE(store_->BeginUpdate());
71  EXPECT_TRUE(file_util::PathExists(temp_file));
72
73  // Pull the rug out from under the existing store, simulating a
74  // crash.
75  store_.reset(new SafeBrowsingStoreFile());
76  store_->Init(filename_, NULL);
77  EXPECT_FALSE(file_util::PathExists(filename_));
78  EXPECT_TRUE(file_util::PathExists(temp_file));
79
80  // Make sure the temporary file is deleted.
81  EXPECT_TRUE(store_->Delete());
82  EXPECT_FALSE(file_util::PathExists(filename_));
83  EXPECT_FALSE(file_util::PathExists(temp_file));
84}
85
86// Test basic corruption-handling.
87TEST_F(SafeBrowsingStoreFileTest, DetectsCorruption) {
88  // Load a store with some data.
89  SafeBrowsingStoreTestStorePrefix(store_.get());
90
91  SafeBrowsingStoreFile test_store;
92  test_store.Init(
93      filename_,
94      NewCallback(static_cast<SafeBrowsingStoreFileTest*>(this),
95                  &SafeBrowsingStoreFileTest::OnCorruptionDetected));
96
97  corruption_detected_ = false;
98
99  // Can successfully open and read the store.
100  std::vector<SBAddFullHash> pending_adds;
101  std::set<SBPrefix> prefix_misses;
102  std::vector<SBAddPrefix> orig_prefixes;
103  std::vector<SBAddFullHash> orig_hashes;
104  EXPECT_TRUE(test_store.BeginUpdate());
105  EXPECT_TRUE(test_store.FinishUpdate(pending_adds, prefix_misses,
106                                      &orig_prefixes, &orig_hashes));
107  EXPECT_GT(orig_prefixes.size(), 0U);
108  EXPECT_GT(orig_hashes.size(), 0U);
109  EXPECT_FALSE(corruption_detected_);
110
111  // Corrupt the store.
112  file_util::ScopedFILE file(file_util::OpenFile(filename_, "rb+"));
113  const long kOffset = 60;
114  EXPECT_EQ(fseek(file.get(), kOffset, SEEK_SET), 0);
115  const int32 kZero = 0;
116  int32 previous = kZero;
117  EXPECT_EQ(fread(&previous, sizeof(previous), 1, file.get()), 1U);
118  EXPECT_NE(previous, kZero);
119  EXPECT_EQ(fseek(file.get(), kOffset, SEEK_SET), 0);
120  EXPECT_EQ(fwrite(&kZero, sizeof(kZero), 1, file.get()), 1U);
121  file.reset();
122
123  // Update fails and corruption callback is called.
124  std::vector<SBAddPrefix> add_prefixes;
125  std::vector<SBAddFullHash> add_hashes;
126  corruption_detected_ = false;
127  EXPECT_TRUE(test_store.BeginUpdate());
128  EXPECT_FALSE(test_store.FinishUpdate(pending_adds, prefix_misses,
129                                       &add_prefixes, &add_hashes));
130  EXPECT_TRUE(corruption_detected_);
131  EXPECT_EQ(add_prefixes.size(), 0U);
132  EXPECT_EQ(add_hashes.size(), 0U);
133
134  // Make it look like there is a lot of add-chunks-seen data.
135  const long kAddChunkCountOffset = 2 * sizeof(int32);
136  const int32 kLargeCount = 1000 * 1000 * 1000;
137  file.reset(file_util::OpenFile(filename_, "rb+"));
138  EXPECT_EQ(fseek(file.get(), kAddChunkCountOffset, SEEK_SET), 0);
139  EXPECT_EQ(fwrite(&kLargeCount, sizeof(kLargeCount), 1, file.get()), 1U);
140  file.reset();
141
142  // Detects corruption and fails to even begin the update.
143  corruption_detected_ = false;
144  EXPECT_FALSE(test_store.BeginUpdate());
145  EXPECT_TRUE(corruption_detected_);
146}
147
148}  // namespace
149