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
;
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
{
30 explicit ScopedSnapshot(leveldb::DB
* db
)
31 : db_(db
), snapshot_(db
->GetSnapshot()) {}
34 db_
->ReleaseSnapshot(snapshot_
);
37 const leveldb::Snapshot
* get() {
43 const leveldb::Snapshot
* snapshot_
;
45 DISALLOW_COPY_AND_ASSIGN(ScopedSnapshot
);
50 LeveldbValueStore::LeveldbValueStore(const base::FilePath
& db_path
)
52 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
54 scoped_ptr
<Error
> open_error
= EnsureDbIsOpen();
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.
69 size_t LeveldbValueStore::GetBytesInUse(const std::string
& key
) {
70 // Let SettingsStorageQuotaEnforcer implement this.
71 NOTREACHED() << "Not implemented";
75 size_t LeveldbValueStore::GetBytesInUse(
76 const std::vector
<std::string
>& keys
) {
77 // Let SettingsStorageQuotaEnforcer implement this.
78 NOTREACHED() << "Not implemented";
82 size_t LeveldbValueStore::GetBytesInUse() {
83 // Let SettingsStorageQuotaEnforcer implement this.
84 NOTREACHED() << "Not implemented";
88 ValueStore::ReadResult
LeveldbValueStore::Get(const std::string
& key
) {
89 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
91 scoped_ptr
<Error
> open_error
= EnsureDbIsOpen();
93 return MakeReadResult(open_error
.Pass());
95 scoped_ptr
<base::Value
> setting
;
96 scoped_ptr
<Error
> error
= ReadFromDb(leveldb::ReadOptions(), key
, &setting
);
98 return MakeReadResult(error
.Pass());
100 base::DictionaryValue
* settings
= new base::DictionaryValue();
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();
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
);
126 return MakeReadResult(error
.Pass());
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();
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());
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();
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());
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();
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());
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();
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
);
237 return MakeWriteResult(read_error
.Pass());
240 changes
->push_back(ValueStoreChange(*it
, old_value
.release(), NULL
));
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
);
266 ValueStoreChange(next_key
, next_value
.release(), NULL
));
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
282 if (!result
->error().key
.get() || *result
->error().key
== previous_key
||
283 !RestoreKey(*result
->error().key
)) {
289 // Otherwise, re-Get() the database to check if there is still any
291 previous_key
= *result
->error().key
;
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
;
307 scoped_ptr
<ValueStore::Error
> error
= WriteToDb(&batch
);
308 // If we can't delete the key, the restore failed.
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);
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
);
336 return ToValueStoreError(status
, util::NoKey());
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);
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();
360 return ToValueStoreError(s
, util::NewKey(key
));
362 base::Value
* value
= base::JSONReader().ReadToValue(value_as_json
);
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
);
383 return read_error
.Pass();
384 if (!old_value
|| !old_value
->Equals(&value
)) {
386 ValueStoreChange(key
, old_value
.release(), value
.DeepCopy()));
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()));
414 bool is_empty
= !it
->Valid();
415 if (!it
->status().ok()) {
416 LOG(ERROR
) << "Checking DB emptiness failed: " << it
->status().ToString();
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 " <<
430 scoped_ptr
<ValueStore::Error
> LeveldbValueStore::ToValueStoreError(
431 const leveldb::Status
& status
,
432 scoped_ptr
<std::string
> key
) {
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());