[Sync] Fix autofill tests to be autofill profile entities.
[chromium-blink-merge.git] / extensions / browser / value_store / leveldb_value_store.cc
blobd29bc7477b1285e85abfc6cd506e60929a4f2a7c
1 // Copyright 2014 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 "extensions/browser/value_store/leveldb_value_store.h"
7 #include "base/files/file_util.h"
8 #include "base/json/json_reader.h"
9 #include "base/json/json_writer.h"
10 #include "base/logging.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/strings/sys_string_conversions.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "extensions/browser/value_store/value_store_util.h"
16 #include "third_party/leveldatabase/env_chromium.h"
17 #include "third_party/leveldatabase/src/include/leveldb/iterator.h"
18 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
20 namespace util = value_store_util;
21 using content::BrowserThread;
23 namespace {
25 const char kInvalidJson[] = "Invalid JSON";
26 const char kCannotSerialize[] = "Cannot serialize value to JSON";
28 // Scoped leveldb snapshot which releases the snapshot on destruction.
29 class ScopedSnapshot {
30 public:
31 explicit ScopedSnapshot(leveldb::DB* db)
32 : db_(db), snapshot_(db->GetSnapshot()) {}
34 ~ScopedSnapshot() {
35 db_->ReleaseSnapshot(snapshot_);
38 const leveldb::Snapshot* get() {
39 return snapshot_;
42 private:
43 leveldb::DB* db_;
44 const leveldb::Snapshot* snapshot_;
46 DISALLOW_COPY_AND_ASSIGN(ScopedSnapshot);
49 } // namespace
51 LeveldbValueStore::LeveldbValueStore(const base::FilePath& db_path)
52 : db_path_(db_path) {
53 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
56 LeveldbValueStore::~LeveldbValueStore() {
57 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
59 // Delete the database from disk if it's empty (but only if we managed to
60 // open it!). This is safe on destruction, assuming that we have exclusive
61 // access to the database.
62 if (db_ && IsEmpty())
63 DeleteDbFile();
66 size_t LeveldbValueStore::GetBytesInUse(const std::string& key) {
67 // Let SettingsStorageQuotaEnforcer implement this.
68 NOTREACHED() << "Not implemented";
69 return 0;
72 size_t LeveldbValueStore::GetBytesInUse(
73 const std::vector<std::string>& keys) {
74 // Let SettingsStorageQuotaEnforcer implement this.
75 NOTREACHED() << "Not implemented";
76 return 0;
79 size_t LeveldbValueStore::GetBytesInUse() {
80 // Let SettingsStorageQuotaEnforcer implement this.
81 NOTREACHED() << "Not implemented";
82 return 0;
85 ValueStore::ReadResult LeveldbValueStore::Get(const std::string& key) {
86 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
88 scoped_ptr<Error> open_error = EnsureDbIsOpen();
89 if (open_error)
90 return MakeReadResult(open_error.Pass());
92 scoped_ptr<base::Value> setting;
93 scoped_ptr<Error> error = ReadFromDb(leveldb::ReadOptions(), key, &setting);
94 if (error)
95 return MakeReadResult(error.Pass());
97 base::DictionaryValue* settings = new base::DictionaryValue();
98 if (setting)
99 settings->SetWithoutPathExpansion(key, setting.release());
100 return MakeReadResult(make_scoped_ptr(settings));
103 ValueStore::ReadResult LeveldbValueStore::Get(
104 const std::vector<std::string>& keys) {
105 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
107 scoped_ptr<Error> open_error = EnsureDbIsOpen();
108 if (open_error)
109 return MakeReadResult(open_error.Pass());
111 leveldb::ReadOptions options;
112 scoped_ptr<base::DictionaryValue> settings(new base::DictionaryValue());
114 // All interaction with the db is done on the same thread, so snapshotting
115 // isn't strictly necessary. This is just defensive.
116 ScopedSnapshot snapshot(db_.get());
117 options.snapshot = snapshot.get();
118 for (std::vector<std::string>::const_iterator it = keys.begin();
119 it != keys.end(); ++it) {
120 scoped_ptr<base::Value> setting;
121 scoped_ptr<Error> error = ReadFromDb(options, *it, &setting);
122 if (error)
123 return MakeReadResult(error.Pass());
124 if (setting)
125 settings->SetWithoutPathExpansion(*it, setting.release());
128 return MakeReadResult(settings.Pass());
131 ValueStore::ReadResult LeveldbValueStore::Get() {
132 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
134 scoped_ptr<Error> open_error = EnsureDbIsOpen();
135 if (open_error)
136 return MakeReadResult(open_error.Pass());
138 base::JSONReader json_reader;
139 leveldb::ReadOptions options = leveldb::ReadOptions();
140 // All interaction with the db is done on the same thread, so snapshotting
141 // isn't strictly necessary. This is just defensive.
142 scoped_ptr<base::DictionaryValue> settings(new base::DictionaryValue());
144 ScopedSnapshot snapshot(db_.get());
145 options.snapshot = snapshot.get();
146 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(options));
147 for (it->SeekToFirst(); it->Valid(); it->Next()) {
148 std::string key = it->key().ToString();
149 scoped_ptr<base::Value> value =
150 json_reader.ReadToValue(it->value().ToString());
151 if (!value) {
152 return MakeReadResult(
153 Error::Create(CORRUPTION, kInvalidJson, util::NewKey(key)));
155 settings->SetWithoutPathExpansion(key, value.Pass());
158 if (it->status().IsNotFound()) {
159 NOTREACHED() << "IsNotFound() but iterating over all keys?!";
160 return MakeReadResult(settings.Pass());
163 if (!it->status().ok())
164 return MakeReadResult(ToValueStoreError(it->status(), util::NoKey()));
166 return MakeReadResult(settings.Pass());
169 ValueStore::WriteResult LeveldbValueStore::Set(
170 WriteOptions options, const std::string& key, const base::Value& value) {
171 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
173 scoped_ptr<Error> open_error = EnsureDbIsOpen();
174 if (open_error)
175 return MakeWriteResult(open_error.Pass());
177 leveldb::WriteBatch batch;
178 scoped_ptr<ValueStoreChangeList> changes(new ValueStoreChangeList());
179 scoped_ptr<Error> batch_error =
180 AddToBatch(options, key, value, &batch, changes.get());
181 if (batch_error)
182 return MakeWriteResult(batch_error.Pass());
184 scoped_ptr<Error> write_error = WriteToDb(&batch);
185 return write_error ? MakeWriteResult(write_error.Pass())
186 : MakeWriteResult(changes.Pass());
189 ValueStore::WriteResult LeveldbValueStore::Set(
190 WriteOptions options, const base::DictionaryValue& settings) {
191 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
193 scoped_ptr<Error> open_error = EnsureDbIsOpen();
194 if (open_error)
195 return MakeWriteResult(open_error.Pass());
197 leveldb::WriteBatch batch;
198 scoped_ptr<ValueStoreChangeList> changes(new ValueStoreChangeList());
200 for (base::DictionaryValue::Iterator it(settings);
201 !it.IsAtEnd(); it.Advance()) {
202 scoped_ptr<Error> batch_error =
203 AddToBatch(options, it.key(), it.value(), &batch, changes.get());
204 if (batch_error)
205 return MakeWriteResult(batch_error.Pass());
208 scoped_ptr<Error> write_error = WriteToDb(&batch);
209 return write_error ? MakeWriteResult(write_error.Pass())
210 : MakeWriteResult(changes.Pass());
213 ValueStore::WriteResult LeveldbValueStore::Remove(const std::string& key) {
214 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
215 return Remove(std::vector<std::string>(1, key));
218 ValueStore::WriteResult LeveldbValueStore::Remove(
219 const std::vector<std::string>& keys) {
220 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
222 scoped_ptr<Error> open_error = EnsureDbIsOpen();
223 if (open_error)
224 return MakeWriteResult(open_error.Pass());
226 leveldb::WriteBatch batch;
227 scoped_ptr<ValueStoreChangeList> changes(new ValueStoreChangeList());
229 for (std::vector<std::string>::const_iterator it = keys.begin();
230 it != keys.end(); ++it) {
231 scoped_ptr<base::Value> old_value;
232 scoped_ptr<Error> read_error =
233 ReadFromDb(leveldb::ReadOptions(), *it, &old_value);
234 if (read_error)
235 return MakeWriteResult(read_error.Pass());
237 if (old_value) {
238 changes->push_back(ValueStoreChange(*it, old_value.release(), NULL));
239 batch.Delete(*it);
243 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch);
244 if (!status.ok() && !status.IsNotFound())
245 return MakeWriteResult(ToValueStoreError(status, util::NoKey()));
246 return MakeWriteResult(changes.Pass());
249 ValueStore::WriteResult LeveldbValueStore::Clear() {
250 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
252 scoped_ptr<ValueStoreChangeList> changes(new ValueStoreChangeList());
254 ReadResult read_result = Get();
255 if (read_result->HasError())
256 return MakeWriteResult(read_result->PassError());
258 base::DictionaryValue& whole_db = read_result->settings();
259 while (!whole_db.empty()) {
260 std::string next_key = base::DictionaryValue::Iterator(whole_db).key();
261 scoped_ptr<base::Value> next_value;
262 whole_db.RemoveWithoutPathExpansion(next_key, &next_value);
263 changes->push_back(
264 ValueStoreChange(next_key, next_value.release(), NULL));
267 DeleteDbFile();
268 return MakeWriteResult(changes.Pass());
271 bool LeveldbValueStore::Restore() {
272 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
274 ReadResult result = Get();
275 std::string previous_key;
276 while (result->IsCorrupted()) {
277 // If we don't have a specific corrupted key, or we've tried and failed to
278 // clear this specific key, or we fail to restore the key, then wipe the
279 // whole database.
280 if (!result->error().key.get() || *result->error().key == previous_key ||
281 !RestoreKey(*result->error().key)) {
282 DeleteDbFile();
283 result = Get();
284 break;
287 // Otherwise, re-Get() the database to check if there is still any
288 // corruption.
289 previous_key = *result->error().key;
290 result = Get();
293 // If we still have an error, it means we've tried deleting the database file,
294 // and failed. There's nothing more we can do.
295 return !result->IsCorrupted();
298 bool LeveldbValueStore::RestoreKey(const std::string& key) {
299 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
301 ReadResult result = Get(key);
302 if (result->IsCorrupted()) {
303 leveldb::WriteBatch batch;
304 batch.Delete(key);
305 scoped_ptr<ValueStore::Error> error = WriteToDb(&batch);
306 // If we can't delete the key, the restore failed.
307 if (error.get())
308 return false;
309 result = Get(key);
312 // The restore succeeded if there is no corruption error.
313 return !result->IsCorrupted();
316 bool LeveldbValueStore::WriteToDbForTest(leveldb::WriteBatch* batch) {
317 return !WriteToDb(batch).get();
320 scoped_ptr<ValueStore::Error> LeveldbValueStore::EnsureDbIsOpen() {
321 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
323 if (db_)
324 return util::NoError();
326 leveldb::Options options;
327 options.max_open_files = 0; // Use minimum.
328 options.create_if_missing = true;
329 options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
331 leveldb::DB* db = NULL;
332 leveldb::Status status =
333 leveldb::DB::Open(options, db_path_.AsUTF8Unsafe(), &db);
334 if (!status.ok())
335 return ToValueStoreError(status, util::NoKey());
337 CHECK(db);
338 db_.reset(db);
339 return util::NoError();
342 scoped_ptr<ValueStore::Error> LeveldbValueStore::ReadFromDb(
343 leveldb::ReadOptions options,
344 const std::string& key,
345 scoped_ptr<base::Value>* setting) {
346 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
347 DCHECK(setting);
349 std::string value_as_json;
350 leveldb::Status s = db_->Get(options, key, &value_as_json);
352 if (s.IsNotFound()) {
353 // Despite there being no value, it was still a success. Check this first
354 // because ok() is false on IsNotFound.
355 return util::NoError();
358 if (!s.ok())
359 return ToValueStoreError(s, util::NewKey(key));
361 scoped_ptr<base::Value> value = base::JSONReader().ReadToValue(value_as_json);
362 if (!value)
363 return Error::Create(CORRUPTION, kInvalidJson, util::NewKey(key));
365 *setting = value.Pass();
366 return util::NoError();
369 scoped_ptr<ValueStore::Error> LeveldbValueStore::AddToBatch(
370 ValueStore::WriteOptions options,
371 const std::string& key,
372 const base::Value& value,
373 leveldb::WriteBatch* batch,
374 ValueStoreChangeList* changes) {
375 bool write_new_value = true;
377 if (!(options & NO_GENERATE_CHANGES)) {
378 scoped_ptr<base::Value> old_value;
379 scoped_ptr<Error> read_error =
380 ReadFromDb(leveldb::ReadOptions(), key, &old_value);
381 if (read_error)
382 return read_error.Pass();
383 if (!old_value || !old_value->Equals(&value)) {
384 changes->push_back(
385 ValueStoreChange(key, old_value.release(), value.DeepCopy()));
386 } else {
387 write_new_value = false;
391 if (write_new_value) {
392 std::string value_as_json;
393 if (!base::JSONWriter::Write(value, &value_as_json))
394 return Error::Create(OTHER_ERROR, kCannotSerialize, util::NewKey(key));
395 batch->Put(key, value_as_json);
398 return util::NoError();
401 scoped_ptr<ValueStore::Error> LeveldbValueStore::WriteToDb(
402 leveldb::WriteBatch* batch) {
403 leveldb::Status status = db_->Write(leveldb::WriteOptions(), batch);
404 return status.ok() ? util::NoError()
405 : ToValueStoreError(status, util::NoKey());
408 bool LeveldbValueStore::IsEmpty() {
409 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
410 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions()));
412 it->SeekToFirst();
413 bool is_empty = !it->Valid();
414 if (!it->status().ok()) {
415 LOG(ERROR) << "Checking DB emptiness failed: " << it->status().ToString();
416 return false;
418 return is_empty;
421 void LeveldbValueStore::DeleteDbFile() {
422 db_.reset(); // release any lock on the directory
423 if (!base::DeleteFile(db_path_, true /* recursive */)) {
424 LOG(WARNING) << "Failed to delete LeveldbValueStore database at " <<
425 db_path_.value();
429 scoped_ptr<ValueStore::Error> LeveldbValueStore::ToValueStoreError(
430 const leveldb::Status& status,
431 scoped_ptr<std::string> key) {
432 CHECK(!status.ok());
433 CHECK(!status.IsNotFound()); // not an error
435 std::string message = status.ToString();
436 // The message may contain |db_path_|, which may be considered sensitive
437 // data, and those strings are passed to the extension, so strip it out.
438 ReplaceSubstringsAfterOffset(&message, 0u, db_path_.AsUTF8Unsafe(), "...");
440 return Error::Create(CORRUPTION, message, key.Pass());