Remove unused parameter.
[chromium-blink-merge.git] / extensions / browser / value_store / leveldb_value_store.cc
blob56fd2650bb711e615c36ac1262e670d1e1404892
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);
55 scoped_ptr<Error> open_error = EnsureDbIsOpen();
56 if (open_error)
57 LOG(WARNING) << open_error->message;
60 LeveldbValueStore::~LeveldbValueStore() {
61 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
63 // Delete the database from disk if it's empty (but only if we managed to
64 // open it!). This is safe on destruction, assuming that we have exclusive
65 // access to the database.
66 if (db_ && IsEmpty())
67 DeleteDbFile();
70 size_t LeveldbValueStore::GetBytesInUse(const std::string& key) {
71 // Let SettingsStorageQuotaEnforcer implement this.
72 NOTREACHED() << "Not implemented";
73 return 0;
76 size_t LeveldbValueStore::GetBytesInUse(
77 const std::vector<std::string>& keys) {
78 // Let SettingsStorageQuotaEnforcer implement this.
79 NOTREACHED() << "Not implemented";
80 return 0;
83 size_t LeveldbValueStore::GetBytesInUse() {
84 // Let SettingsStorageQuotaEnforcer implement this.
85 NOTREACHED() << "Not implemented";
86 return 0;
89 ValueStore::ReadResult LeveldbValueStore::Get(const std::string& key) {
90 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
92 scoped_ptr<Error> open_error = EnsureDbIsOpen();
93 if (open_error)
94 return MakeReadResult(open_error.Pass());
96 scoped_ptr<base::Value> setting;
97 scoped_ptr<Error> error = ReadFromDb(leveldb::ReadOptions(), key, &setting);
98 if (error)
99 return MakeReadResult(error.Pass());
101 base::DictionaryValue* settings = new base::DictionaryValue();
102 if (setting)
103 settings->SetWithoutPathExpansion(key, setting.release());
104 return MakeReadResult(make_scoped_ptr(settings));
107 ValueStore::ReadResult LeveldbValueStore::Get(
108 const std::vector<std::string>& keys) {
109 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
111 scoped_ptr<Error> open_error = EnsureDbIsOpen();
112 if (open_error)
113 return MakeReadResult(open_error.Pass());
115 leveldb::ReadOptions options;
116 scoped_ptr<base::DictionaryValue> settings(new base::DictionaryValue());
118 // All interaction with the db is done on the same thread, so snapshotting
119 // isn't strictly necessary. This is just defensive.
120 ScopedSnapshot snapshot(db_.get());
121 options.snapshot = snapshot.get();
122 for (std::vector<std::string>::const_iterator it = keys.begin();
123 it != keys.end(); ++it) {
124 scoped_ptr<base::Value> setting;
125 scoped_ptr<Error> error = ReadFromDb(options, *it, &setting);
126 if (error)
127 return MakeReadResult(error.Pass());
128 if (setting)
129 settings->SetWithoutPathExpansion(*it, setting.release());
132 return MakeReadResult(settings.Pass());
135 ValueStore::ReadResult LeveldbValueStore::Get() {
136 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
138 scoped_ptr<Error> open_error = EnsureDbIsOpen();
139 if (open_error)
140 return MakeReadResult(open_error.Pass());
142 base::JSONReader json_reader;
143 leveldb::ReadOptions options = leveldb::ReadOptions();
144 // All interaction with the db is done on the same thread, so snapshotting
145 // isn't strictly necessary. This is just defensive.
146 scoped_ptr<base::DictionaryValue> settings(new base::DictionaryValue());
148 ScopedSnapshot snapshot(db_.get());
149 options.snapshot = snapshot.get();
150 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(options));
151 for (it->SeekToFirst(); it->Valid(); it->Next()) {
152 std::string key = it->key().ToString();
153 base::Value* value = json_reader.ReadToValue(it->value().ToString());
154 if (!value) {
155 return MakeReadResult(
156 Error::Create(CORRUPTION, kInvalidJson, util::NewKey(key)));
158 settings->SetWithoutPathExpansion(key, value);
161 if (it->status().IsNotFound()) {
162 NOTREACHED() << "IsNotFound() but iterating over all keys?!";
163 return MakeReadResult(settings.Pass());
166 if (!it->status().ok())
167 return MakeReadResult(ToValueStoreError(it->status(), util::NoKey()));
169 return MakeReadResult(settings.Pass());
172 ValueStore::WriteResult LeveldbValueStore::Set(
173 WriteOptions options, const std::string& key, const base::Value& value) {
174 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
176 scoped_ptr<Error> open_error = EnsureDbIsOpen();
177 if (open_error)
178 return MakeWriteResult(open_error.Pass());
180 leveldb::WriteBatch batch;
181 scoped_ptr<ValueStoreChangeList> changes(new ValueStoreChangeList());
182 scoped_ptr<Error> batch_error =
183 AddToBatch(options, key, value, &batch, changes.get());
184 if (batch_error)
185 return MakeWriteResult(batch_error.Pass());
187 scoped_ptr<Error> write_error = WriteToDb(&batch);
188 return write_error ? MakeWriteResult(write_error.Pass())
189 : MakeWriteResult(changes.Pass());
192 ValueStore::WriteResult LeveldbValueStore::Set(
193 WriteOptions options, const base::DictionaryValue& settings) {
194 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
196 scoped_ptr<Error> open_error = EnsureDbIsOpen();
197 if (open_error)
198 return MakeWriteResult(open_error.Pass());
200 leveldb::WriteBatch batch;
201 scoped_ptr<ValueStoreChangeList> changes(new ValueStoreChangeList());
203 for (base::DictionaryValue::Iterator it(settings);
204 !it.IsAtEnd(); it.Advance()) {
205 scoped_ptr<Error> batch_error =
206 AddToBatch(options, it.key(), it.value(), &batch, changes.get());
207 if (batch_error)
208 return MakeWriteResult(batch_error.Pass());
211 scoped_ptr<Error> write_error = WriteToDb(&batch);
212 return write_error ? MakeWriteResult(write_error.Pass())
213 : MakeWriteResult(changes.Pass());
216 ValueStore::WriteResult LeveldbValueStore::Remove(const std::string& key) {
217 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
218 return Remove(std::vector<std::string>(1, key));
221 ValueStore::WriteResult LeveldbValueStore::Remove(
222 const std::vector<std::string>& keys) {
223 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
225 scoped_ptr<Error> open_error = EnsureDbIsOpen();
226 if (open_error)
227 return MakeWriteResult(open_error.Pass());
229 leveldb::WriteBatch batch;
230 scoped_ptr<ValueStoreChangeList> changes(new ValueStoreChangeList());
232 for (std::vector<std::string>::const_iterator it = keys.begin();
233 it != keys.end(); ++it) {
234 scoped_ptr<base::Value> old_value;
235 scoped_ptr<Error> read_error =
236 ReadFromDb(leveldb::ReadOptions(), *it, &old_value);
237 if (read_error)
238 return MakeWriteResult(read_error.Pass());
240 if (old_value) {
241 changes->push_back(ValueStoreChange(*it, old_value.release(), NULL));
242 batch.Delete(*it);
246 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch);
247 if (!status.ok() && !status.IsNotFound())
248 return MakeWriteResult(ToValueStoreError(status, util::NoKey()));
249 return MakeWriteResult(changes.Pass());
252 ValueStore::WriteResult LeveldbValueStore::Clear() {
253 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
255 scoped_ptr<ValueStoreChangeList> changes(new ValueStoreChangeList());
257 ReadResult read_result = Get();
258 if (read_result->HasError())
259 return MakeWriteResult(read_result->PassError());
261 base::DictionaryValue& whole_db = read_result->settings();
262 while (!whole_db.empty()) {
263 std::string next_key = base::DictionaryValue::Iterator(whole_db).key();
264 scoped_ptr<base::Value> next_value;
265 whole_db.RemoveWithoutPathExpansion(next_key, &next_value);
266 changes->push_back(
267 ValueStoreChange(next_key, next_value.release(), NULL));
270 DeleteDbFile();
271 return MakeWriteResult(changes.Pass());
274 bool LeveldbValueStore::Restore() {
275 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
277 ReadResult result = Get();
278 std::string previous_key;
279 while (result->IsCorrupted()) {
280 // If we don't have a specific corrupted key, or we've tried and failed to
281 // clear this specific key, or we fail to restore the key, then wipe the
282 // whole database.
283 if (!result->error().key.get() || *result->error().key == previous_key ||
284 !RestoreKey(*result->error().key)) {
285 DeleteDbFile();
286 result = Get();
287 break;
290 // Otherwise, re-Get() the database to check if there is still any
291 // corruption.
292 previous_key = *result->error().key;
293 result = Get();
296 // If we still have an error, it means we've tried deleting the database file,
297 // and failed. There's nothing more we can do.
298 return !result->IsCorrupted();
301 bool LeveldbValueStore::RestoreKey(const std::string& key) {
302 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
304 ReadResult result = Get(key);
305 if (result->IsCorrupted()) {
306 leveldb::WriteBatch batch;
307 batch.Delete(key);
308 scoped_ptr<ValueStore::Error> error = WriteToDb(&batch);
309 // If we can't delete the key, the restore failed.
310 if (error.get())
311 return false;
312 result = Get(key);
315 // The restore succeeded if there is no corruption error.
316 return !result->IsCorrupted();
319 bool LeveldbValueStore::WriteToDbForTest(leveldb::WriteBatch* batch) {
320 return !WriteToDb(batch).get();
323 scoped_ptr<ValueStore::Error> LeveldbValueStore::EnsureDbIsOpen() {
324 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
326 if (db_)
327 return util::NoError();
329 leveldb::Options options;
330 options.max_open_files = 0; // Use minimum.
331 options.create_if_missing = true;
332 options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
334 leveldb::DB* db = NULL;
335 leveldb::Status status =
336 leveldb::DB::Open(options, db_path_.AsUTF8Unsafe(), &db);
337 if (!status.ok())
338 return ToValueStoreError(status, util::NoKey());
340 CHECK(db);
341 db_.reset(db);
342 return util::NoError();
345 scoped_ptr<ValueStore::Error> LeveldbValueStore::ReadFromDb(
346 leveldb::ReadOptions options,
347 const std::string& key,
348 scoped_ptr<base::Value>* setting) {
349 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
350 DCHECK(setting);
352 std::string value_as_json;
353 leveldb::Status s = db_->Get(options, key, &value_as_json);
355 if (s.IsNotFound()) {
356 // Despite there being no value, it was still a success. Check this first
357 // because ok() is false on IsNotFound.
358 return util::NoError();
361 if (!s.ok())
362 return ToValueStoreError(s, util::NewKey(key));
364 base::Value* value = base::JSONReader().ReadToValue(value_as_json);
365 if (!value)
366 return Error::Create(CORRUPTION, kInvalidJson, util::NewKey(key));
368 setting->reset(value);
369 return util::NoError();
372 scoped_ptr<ValueStore::Error> LeveldbValueStore::AddToBatch(
373 ValueStore::WriteOptions options,
374 const std::string& key,
375 const base::Value& value,
376 leveldb::WriteBatch* batch,
377 ValueStoreChangeList* changes) {
378 bool write_new_value = true;
380 if (!(options & NO_GENERATE_CHANGES)) {
381 scoped_ptr<base::Value> old_value;
382 scoped_ptr<Error> read_error =
383 ReadFromDb(leveldb::ReadOptions(), key, &old_value);
384 if (read_error)
385 return read_error.Pass();
386 if (!old_value || !old_value->Equals(&value)) {
387 changes->push_back(
388 ValueStoreChange(key, old_value.release(), value.DeepCopy()));
389 } else {
390 write_new_value = false;
394 if (write_new_value) {
395 std::string value_as_json;
396 if (!base::JSONWriter::Write(&value, &value_as_json))
397 return Error::Create(OTHER_ERROR, kCannotSerialize, util::NewKey(key));
398 batch->Put(key, value_as_json);
401 return util::NoError();
404 scoped_ptr<ValueStore::Error> LeveldbValueStore::WriteToDb(
405 leveldb::WriteBatch* batch) {
406 leveldb::Status status = db_->Write(leveldb::WriteOptions(), batch);
407 return status.ok() ? util::NoError()
408 : ToValueStoreError(status, util::NoKey());
411 bool LeveldbValueStore::IsEmpty() {
412 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
413 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions()));
415 it->SeekToFirst();
416 bool is_empty = !it->Valid();
417 if (!it->status().ok()) {
418 LOG(ERROR) << "Checking DB emptiness failed: " << it->status().ToString();
419 return false;
421 return is_empty;
424 void LeveldbValueStore::DeleteDbFile() {
425 db_.reset(); // release any lock on the directory
426 if (!base::DeleteFile(db_path_, true /* recursive */)) {
427 LOG(WARNING) << "Failed to delete LeveldbValueStore database at " <<
428 db_path_.value();
432 scoped_ptr<ValueStore::Error> LeveldbValueStore::ToValueStoreError(
433 const leveldb::Status& status,
434 scoped_ptr<std::string> key) {
435 CHECK(!status.ok());
436 CHECK(!status.IsNotFound()); // not an error
438 std::string message = status.ToString();
439 // The message may contain |db_path_|, which may be considered sensitive
440 // data, and those strings are passed to the extension, so strip it out.
441 ReplaceSubstringsAfterOffset(&message, 0u, db_path_.AsUTF8Unsafe(), "...");
443 return Error::Create(CORRUPTION, message, key.Pass());