Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / extensions / browser / value_store / leveldb_value_store.cc
blob6d7fdc9a34f12fa8eaca6add8d29b5c47c3483bf
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/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/src/include/leveldb/iterator.h"
17 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
19 namespace util = value_store_util;
20 using content::BrowserThread;
22 namespace {
24 const char kInvalidJson[] = "Invalid JSON";
25 const char kCannotSerialize[] = "Cannot serialize value to JSON";
27 // Scoped leveldb snapshot which releases the snapshot on destruction.
28 class ScopedSnapshot {
29 public:
30 explicit ScopedSnapshot(leveldb::DB* db)
31 : db_(db), snapshot_(db->GetSnapshot()) {}
33 ~ScopedSnapshot() {
34 db_->ReleaseSnapshot(snapshot_);
37 const leveldb::Snapshot* get() {
38 return snapshot_;
41 private:
42 leveldb::DB* db_;
43 const leveldb::Snapshot* snapshot_;
45 DISALLOW_COPY_AND_ASSIGN(ScopedSnapshot);
48 } // namespace
50 LeveldbValueStore::LeveldbValueStore(const base::FilePath& db_path)
51 : db_path_(db_path) {
52 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
54 scoped_ptr<Error> open_error = EnsureDbIsOpen();
55 if (open_error)
56 LOG(WARNING) << open_error->message;
59 LeveldbValueStore::~LeveldbValueStore() {
60 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
62 // Delete the database from disk if it's empty (but only if we managed to
63 // open it!). This is safe on destruction, assuming that we have exclusive
64 // access to the database.
65 if (db_ && IsEmpty())
66 DeleteDbFile();
69 size_t LeveldbValueStore::GetBytesInUse(const std::string& key) {
70 // Let SettingsStorageQuotaEnforcer implement this.
71 NOTREACHED() << "Not implemented";
72 return 0;
75 size_t LeveldbValueStore::GetBytesInUse(
76 const std::vector<std::string>& keys) {
77 // Let SettingsStorageQuotaEnforcer implement this.
78 NOTREACHED() << "Not implemented";
79 return 0;
82 size_t LeveldbValueStore::GetBytesInUse() {
83 // Let SettingsStorageQuotaEnforcer implement this.
84 NOTREACHED() << "Not implemented";
85 return 0;
88 ValueStore::ReadResult LeveldbValueStore::Get(const std::string& key) {
89 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
91 scoped_ptr<Error> open_error = EnsureDbIsOpen();
92 if (open_error)
93 return MakeReadResult(open_error.Pass());
95 scoped_ptr<base::Value> setting;
96 scoped_ptr<Error> error = ReadFromDb(leveldb::ReadOptions(), key, &setting);
97 if (error)
98 return MakeReadResult(error.Pass());
100 base::DictionaryValue* settings = new base::DictionaryValue();
101 if (setting)
102 settings->SetWithoutPathExpansion(key, setting.release());
103 return MakeReadResult(make_scoped_ptr(settings));
106 ValueStore::ReadResult LeveldbValueStore::Get(
107 const std::vector<std::string>& keys) {
108 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
110 scoped_ptr<Error> open_error = EnsureDbIsOpen();
111 if (open_error)
112 return MakeReadResult(open_error.Pass());
114 leveldb::ReadOptions options;
115 scoped_ptr<base::DictionaryValue> settings(new base::DictionaryValue());
117 // All interaction with the db is done on the same thread, so snapshotting
118 // isn't strictly necessary. This is just defensive.
119 ScopedSnapshot snapshot(db_.get());
120 options.snapshot = snapshot.get();
121 for (std::vector<std::string>::const_iterator it = keys.begin();
122 it != keys.end(); ++it) {
123 scoped_ptr<base::Value> setting;
124 scoped_ptr<Error> error = ReadFromDb(options, *it, &setting);
125 if (error)
126 return MakeReadResult(error.Pass());
127 if (setting)
128 settings->SetWithoutPathExpansion(*it, setting.release());
131 return MakeReadResult(settings.Pass());
134 ValueStore::ReadResult LeveldbValueStore::Get() {
135 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
137 scoped_ptr<Error> open_error = EnsureDbIsOpen();
138 if (open_error)
139 return MakeReadResult(open_error.Pass());
141 base::JSONReader json_reader;
142 leveldb::ReadOptions options = leveldb::ReadOptions();
143 // All interaction with the db is done on the same thread, so snapshotting
144 // isn't strictly necessary. This is just defensive.
145 scoped_ptr<base::DictionaryValue> settings(new base::DictionaryValue());
147 ScopedSnapshot snapshot(db_.get());
148 options.snapshot = snapshot.get();
149 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(options));
150 for (it->SeekToFirst(); it->Valid(); it->Next()) {
151 std::string key = it->key().ToString();
152 base::Value* value = json_reader.ReadToValue(it->value().ToString());
153 if (!value) {
154 return MakeReadResult(
155 Error::Create(CORRUPTION, kInvalidJson, util::NewKey(key)));
157 settings->SetWithoutPathExpansion(key, value);
160 if (it->status().IsNotFound()) {
161 NOTREACHED() << "IsNotFound() but iterating over all keys?!";
162 return MakeReadResult(settings.Pass());
165 if (!it->status().ok())
166 return MakeReadResult(ToValueStoreError(it->status(), util::NoKey()));
168 return MakeReadResult(settings.Pass());
171 ValueStore::WriteResult LeveldbValueStore::Set(
172 WriteOptions options, const std::string& key, const base::Value& value) {
173 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
175 scoped_ptr<Error> open_error = EnsureDbIsOpen();
176 if (open_error)
177 return MakeWriteResult(open_error.Pass());
179 leveldb::WriteBatch batch;
180 scoped_ptr<ValueStoreChangeList> changes(new ValueStoreChangeList());
181 scoped_ptr<Error> batch_error =
182 AddToBatch(options, key, value, &batch, changes.get());
183 if (batch_error)
184 return MakeWriteResult(batch_error.Pass());
186 scoped_ptr<Error> write_error = WriteToDb(&batch);
187 return write_error ? MakeWriteResult(write_error.Pass())
188 : MakeWriteResult(changes.Pass());
191 ValueStore::WriteResult LeveldbValueStore::Set(
192 WriteOptions options, const base::DictionaryValue& settings) {
193 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
195 scoped_ptr<Error> open_error = EnsureDbIsOpen();
196 if (open_error)
197 return MakeWriteResult(open_error.Pass());
199 leveldb::WriteBatch batch;
200 scoped_ptr<ValueStoreChangeList> changes(new ValueStoreChangeList());
202 for (base::DictionaryValue::Iterator it(settings);
203 !it.IsAtEnd(); it.Advance()) {
204 scoped_ptr<Error> batch_error =
205 AddToBatch(options, it.key(), it.value(), &batch, changes.get());
206 if (batch_error)
207 return MakeWriteResult(batch_error.Pass());
210 scoped_ptr<Error> write_error = WriteToDb(&batch);
211 return write_error ? MakeWriteResult(write_error.Pass())
212 : MakeWriteResult(changes.Pass());
215 ValueStore::WriteResult LeveldbValueStore::Remove(const std::string& key) {
216 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
217 return Remove(std::vector<std::string>(1, key));
220 ValueStore::WriteResult LeveldbValueStore::Remove(
221 const std::vector<std::string>& keys) {
222 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
224 scoped_ptr<Error> open_error = EnsureDbIsOpen();
225 if (open_error)
226 return MakeWriteResult(open_error.Pass());
228 leveldb::WriteBatch batch;
229 scoped_ptr<ValueStoreChangeList> changes(new ValueStoreChangeList());
231 for (std::vector<std::string>::const_iterator it = keys.begin();
232 it != keys.end(); ++it) {
233 scoped_ptr<base::Value> old_value;
234 scoped_ptr<Error> read_error =
235 ReadFromDb(leveldb::ReadOptions(), *it, &old_value);
236 if (read_error)
237 return MakeWriteResult(read_error.Pass());
239 if (old_value) {
240 changes->push_back(ValueStoreChange(*it, old_value.release(), NULL));
241 batch.Delete(*it);
245 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch);
246 if (!status.ok() && !status.IsNotFound())
247 return MakeWriteResult(ToValueStoreError(status, util::NoKey()));
248 return MakeWriteResult(changes.Pass());
251 ValueStore::WriteResult LeveldbValueStore::Clear() {
252 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
254 scoped_ptr<ValueStoreChangeList> changes(new ValueStoreChangeList());
256 ReadResult read_result = Get();
257 if (read_result->HasError())
258 return MakeWriteResult(read_result->PassError());
260 base::DictionaryValue& whole_db = read_result->settings();
261 while (!whole_db.empty()) {
262 std::string next_key = base::DictionaryValue::Iterator(whole_db).key();
263 scoped_ptr<base::Value> next_value;
264 whole_db.RemoveWithoutPathExpansion(next_key, &next_value);
265 changes->push_back(
266 ValueStoreChange(next_key, next_value.release(), NULL));
269 DeleteDbFile();
270 return MakeWriteResult(changes.Pass());
273 bool LeveldbValueStore::Restore() {
274 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
276 ReadResult result = Get();
277 std::string previous_key;
278 while (result->IsCorrupted()) {
279 // If we don't have a specific corrupted key, or we've tried and failed to
280 // clear this specific key, or we fail to restore the key, then wipe the
281 // whole database.
282 if (!result->error().key.get() || *result->error().key == previous_key ||
283 !RestoreKey(*result->error().key)) {
284 DeleteDbFile();
285 result = Get();
286 break;
289 // Otherwise, re-Get() the database to check if there is still any
290 // corruption.
291 previous_key = *result->error().key;
292 result = Get();
295 // If we still have an error, it means we've tried deleting the database file,
296 // and failed. There's nothing more we can do.
297 return !result->IsCorrupted();
300 bool LeveldbValueStore::RestoreKey(const std::string& key) {
301 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
303 ReadResult result = Get(key);
304 if (result->IsCorrupted()) {
305 leveldb::WriteBatch batch;
306 batch.Delete(key);
307 scoped_ptr<ValueStore::Error> error = WriteToDb(&batch);
308 // If we can't delete the key, the restore failed.
309 if (error.get())
310 return false;
311 result = Get(key);
314 // The restore succeeded if there is no corruption error.
315 return !result->IsCorrupted();
318 bool LeveldbValueStore::WriteToDbForTest(leveldb::WriteBatch* batch) {
319 return !WriteToDb(batch).get();
322 scoped_ptr<ValueStore::Error> LeveldbValueStore::EnsureDbIsOpen() {
323 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
325 if (db_)
326 return util::NoError();
328 leveldb::Options options;
329 options.max_open_files = 0; // Use minimum.
330 options.create_if_missing = true;
332 leveldb::DB* db = NULL;
333 leveldb::Status status =
334 leveldb::DB::Open(options, db_path_.AsUTF8Unsafe(), &db);
335 if (!status.ok())
336 return ToValueStoreError(status, util::NoKey());
338 CHECK(db);
339 db_.reset(db);
340 return util::NoError();
343 scoped_ptr<ValueStore::Error> LeveldbValueStore::ReadFromDb(
344 leveldb::ReadOptions options,
345 const std::string& key,
346 scoped_ptr<base::Value>* setting) {
347 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
348 DCHECK(setting);
350 std::string value_as_json;
351 leveldb::Status s = db_->Get(options, key, &value_as_json);
353 if (s.IsNotFound()) {
354 // Despite there being no value, it was still a success. Check this first
355 // because ok() is false on IsNotFound.
356 return util::NoError();
359 if (!s.ok())
360 return ToValueStoreError(s, util::NewKey(key));
362 base::Value* value = base::JSONReader().ReadToValue(value_as_json);
363 if (!value)
364 return Error::Create(CORRUPTION, kInvalidJson, util::NewKey(key));
366 setting->reset(value);
367 return util::NoError();
370 scoped_ptr<ValueStore::Error> LeveldbValueStore::AddToBatch(
371 ValueStore::WriteOptions options,
372 const std::string& key,
373 const base::Value& value,
374 leveldb::WriteBatch* batch,
375 ValueStoreChangeList* changes) {
376 bool write_new_value = true;
378 if (!(options & NO_GENERATE_CHANGES)) {
379 scoped_ptr<base::Value> old_value;
380 scoped_ptr<Error> read_error =
381 ReadFromDb(leveldb::ReadOptions(), key, &old_value);
382 if (read_error)
383 return read_error.Pass();
384 if (!old_value || !old_value->Equals(&value)) {
385 changes->push_back(
386 ValueStoreChange(key, old_value.release(), value.DeepCopy()));
387 } else {
388 write_new_value = false;
392 if (write_new_value) {
393 std::string value_as_json;
394 if (!base::JSONWriter::Write(&value, &value_as_json))
395 return Error::Create(OTHER_ERROR, kCannotSerialize, util::NewKey(key));
396 batch->Put(key, value_as_json);
399 return util::NoError();
402 scoped_ptr<ValueStore::Error> LeveldbValueStore::WriteToDb(
403 leveldb::WriteBatch* batch) {
404 leveldb::Status status = db_->Write(leveldb::WriteOptions(), batch);
405 return status.ok() ? util::NoError()
406 : ToValueStoreError(status, util::NoKey());
409 bool LeveldbValueStore::IsEmpty() {
410 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
411 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions()));
413 it->SeekToFirst();
414 bool is_empty = !it->Valid();
415 if (!it->status().ok()) {
416 LOG(ERROR) << "Checking DB emptiness failed: " << it->status().ToString();
417 return false;
419 return is_empty;
422 void LeveldbValueStore::DeleteDbFile() {
423 db_.reset(); // release any lock on the directory
424 if (!base::DeleteFile(db_path_, true /* recursive */)) {
425 LOG(WARNING) << "Failed to delete LeveldbValueStore database at " <<
426 db_path_.value();
430 scoped_ptr<ValueStore::Error> LeveldbValueStore::ToValueStoreError(
431 const leveldb::Status& status,
432 scoped_ptr<std::string> key) {
433 CHECK(!status.ok());
434 CHECK(!status.IsNotFound()); // not an error
436 std::string message = status.ToString();
437 // The message may contain |db_path_|, which may be considered sensitive
438 // data, and those strings are passed to the extension, so strip it out.
439 ReplaceSubstringsAfterOffset(&message, 0u, db_path_.AsUTF8Unsafe(), "...");
441 return Error::Create(CORRUPTION, message, key.Pass());