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
;
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
{
31 explicit ScopedSnapshot(leveldb::DB
* db
)
32 : db_(db
), snapshot_(db
->GetSnapshot()) {}
35 db_
->ReleaseSnapshot(snapshot_
);
38 const leveldb::Snapshot
* get() {
44 const leveldb::Snapshot
* snapshot_
;
46 DISALLOW_COPY_AND_ASSIGN(ScopedSnapshot
);
51 LeveldbValueStore::LeveldbValueStore(const std::string
& uma_client_name
,
52 const base::FilePath
& db_path
)
53 : db_path_(db_path
), open_histogram_(nullptr) {
54 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
56 // Used in lieu of UMA_HISTOGRAM_ENUMERATION because the histogram name is
58 open_histogram_
= base::LinearHistogram::FactoryGet(
59 "Extensions.Database.Open." + uma_client_name
, 1,
60 leveldb_env::LEVELDB_STATUS_MAX
, leveldb_env::LEVELDB_STATUS_MAX
+ 1,
61 base::Histogram::kUmaTargetedHistogramFlag
);
64 LeveldbValueStore::~LeveldbValueStore() {
65 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
67 // Delete the database from disk if it's empty (but only if we managed to
68 // open it!). This is safe on destruction, assuming that we have exclusive
69 // access to the database.
74 size_t LeveldbValueStore::GetBytesInUse(const std::string
& key
) {
75 // Let SettingsStorageQuotaEnforcer implement this.
76 NOTREACHED() << "Not implemented";
80 size_t LeveldbValueStore::GetBytesInUse(
81 const std::vector
<std::string
>& keys
) {
82 // Let SettingsStorageQuotaEnforcer implement this.
83 NOTREACHED() << "Not implemented";
87 size_t LeveldbValueStore::GetBytesInUse() {
88 // Let SettingsStorageQuotaEnforcer implement this.
89 NOTREACHED() << "Not implemented";
93 ValueStore::ReadResult
LeveldbValueStore::Get(const std::string
& key
) {
94 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
96 scoped_ptr
<Error
> open_error
= EnsureDbIsOpen();
98 return MakeReadResult(open_error
.Pass());
100 scoped_ptr
<base::Value
> setting
;
101 scoped_ptr
<Error
> error
= ReadFromDb(leveldb::ReadOptions(), key
, &setting
);
103 return MakeReadResult(error
.Pass());
105 base::DictionaryValue
* settings
= new base::DictionaryValue();
107 settings
->SetWithoutPathExpansion(key
, setting
.release());
108 return MakeReadResult(make_scoped_ptr(settings
));
111 ValueStore::ReadResult
LeveldbValueStore::Get(
112 const std::vector
<std::string
>& keys
) {
113 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
115 scoped_ptr
<Error
> open_error
= EnsureDbIsOpen();
117 return MakeReadResult(open_error
.Pass());
119 leveldb::ReadOptions options
;
120 scoped_ptr
<base::DictionaryValue
> settings(new base::DictionaryValue());
122 // All interaction with the db is done on the same thread, so snapshotting
123 // isn't strictly necessary. This is just defensive.
124 ScopedSnapshot
snapshot(db_
.get());
125 options
.snapshot
= snapshot
.get();
126 for (std::vector
<std::string
>::const_iterator it
= keys
.begin();
127 it
!= keys
.end(); ++it
) {
128 scoped_ptr
<base::Value
> setting
;
129 scoped_ptr
<Error
> error
= ReadFromDb(options
, *it
, &setting
);
131 return MakeReadResult(error
.Pass());
133 settings
->SetWithoutPathExpansion(*it
, setting
.release());
136 return MakeReadResult(settings
.Pass());
139 ValueStore::ReadResult
LeveldbValueStore::Get() {
140 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
142 scoped_ptr
<Error
> open_error
= EnsureDbIsOpen();
144 return MakeReadResult(open_error
.Pass());
146 base::JSONReader json_reader
;
147 leveldb::ReadOptions options
= leveldb::ReadOptions();
148 // All interaction with the db is done on the same thread, so snapshotting
149 // isn't strictly necessary. This is just defensive.
150 scoped_ptr
<base::DictionaryValue
> settings(new base::DictionaryValue());
152 ScopedSnapshot
snapshot(db_
.get());
153 options
.snapshot
= snapshot
.get();
154 scoped_ptr
<leveldb::Iterator
> it(db_
->NewIterator(options
));
155 for (it
->SeekToFirst(); it
->Valid(); it
->Next()) {
156 std::string key
= it
->key().ToString();
157 scoped_ptr
<base::Value
> value
=
158 json_reader
.ReadToValue(it
->value().ToString());
160 return MakeReadResult(
161 Error::Create(CORRUPTION
, kInvalidJson
, util::NewKey(key
)));
163 settings
->SetWithoutPathExpansion(key
, value
.Pass());
166 if (it
->status().IsNotFound()) {
167 NOTREACHED() << "IsNotFound() but iterating over all keys?!";
168 return MakeReadResult(settings
.Pass());
171 if (!it
->status().ok())
172 return MakeReadResult(ToValueStoreError(it
->status(), util::NoKey()));
174 return MakeReadResult(settings
.Pass());
177 ValueStore::WriteResult
LeveldbValueStore::Set(
178 WriteOptions options
, const std::string
& key
, const base::Value
& value
) {
179 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
181 scoped_ptr
<Error
> open_error
= EnsureDbIsOpen();
183 return MakeWriteResult(open_error
.Pass());
185 leveldb::WriteBatch batch
;
186 scoped_ptr
<ValueStoreChangeList
> changes(new ValueStoreChangeList());
187 scoped_ptr
<Error
> batch_error
=
188 AddToBatch(options
, key
, value
, &batch
, changes
.get());
190 return MakeWriteResult(batch_error
.Pass());
192 scoped_ptr
<Error
> write_error
= WriteToDb(&batch
);
193 return write_error
? MakeWriteResult(write_error
.Pass())
194 : MakeWriteResult(changes
.Pass());
197 ValueStore::WriteResult
LeveldbValueStore::Set(
198 WriteOptions options
, const base::DictionaryValue
& settings
) {
199 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
201 scoped_ptr
<Error
> open_error
= EnsureDbIsOpen();
203 return MakeWriteResult(open_error
.Pass());
205 leveldb::WriteBatch batch
;
206 scoped_ptr
<ValueStoreChangeList
> changes(new ValueStoreChangeList());
208 for (base::DictionaryValue::Iterator
it(settings
);
209 !it
.IsAtEnd(); it
.Advance()) {
210 scoped_ptr
<Error
> batch_error
=
211 AddToBatch(options
, it
.key(), it
.value(), &batch
, changes
.get());
213 return MakeWriteResult(batch_error
.Pass());
216 scoped_ptr
<Error
> write_error
= WriteToDb(&batch
);
217 return write_error
? MakeWriteResult(write_error
.Pass())
218 : MakeWriteResult(changes
.Pass());
221 ValueStore::WriteResult
LeveldbValueStore::Remove(const std::string
& key
) {
222 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
223 return Remove(std::vector
<std::string
>(1, key
));
226 ValueStore::WriteResult
LeveldbValueStore::Remove(
227 const std::vector
<std::string
>& keys
) {
228 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
230 scoped_ptr
<Error
> open_error
= EnsureDbIsOpen();
232 return MakeWriteResult(open_error
.Pass());
234 leveldb::WriteBatch batch
;
235 scoped_ptr
<ValueStoreChangeList
> changes(new ValueStoreChangeList());
237 for (std::vector
<std::string
>::const_iterator it
= keys
.begin();
238 it
!= keys
.end(); ++it
) {
239 scoped_ptr
<base::Value
> old_value
;
240 scoped_ptr
<Error
> read_error
=
241 ReadFromDb(leveldb::ReadOptions(), *it
, &old_value
);
243 return MakeWriteResult(read_error
.Pass());
246 changes
->push_back(ValueStoreChange(*it
, old_value
.release(), NULL
));
251 leveldb::Status status
= db_
->Write(leveldb::WriteOptions(), &batch
);
252 if (!status
.ok() && !status
.IsNotFound())
253 return MakeWriteResult(ToValueStoreError(status
, util::NoKey()));
254 return MakeWriteResult(changes
.Pass());
257 ValueStore::WriteResult
LeveldbValueStore::Clear() {
258 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
260 scoped_ptr
<ValueStoreChangeList
> changes(new ValueStoreChangeList());
262 ReadResult read_result
= Get();
263 if (read_result
->HasError())
264 return MakeWriteResult(read_result
->PassError());
266 base::DictionaryValue
& whole_db
= read_result
->settings();
267 while (!whole_db
.empty()) {
268 std::string next_key
= base::DictionaryValue::Iterator(whole_db
).key();
269 scoped_ptr
<base::Value
> next_value
;
270 whole_db
.RemoveWithoutPathExpansion(next_key
, &next_value
);
272 ValueStoreChange(next_key
, next_value
.release(), NULL
));
276 return MakeWriteResult(changes
.Pass());
279 bool LeveldbValueStore::Restore() {
280 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
282 ReadResult result
= Get();
283 std::string previous_key
;
284 while (result
->IsCorrupted()) {
285 // If we don't have a specific corrupted key, or we've tried and failed to
286 // clear this specific key, or we fail to restore the key, then wipe the
288 if (!result
->error().key
.get() || *result
->error().key
== previous_key
||
289 !RestoreKey(*result
->error().key
)) {
295 // Otherwise, re-Get() the database to check if there is still any
297 previous_key
= *result
->error().key
;
301 // If we still have an error, it means we've tried deleting the database file,
302 // and failed. There's nothing more we can do.
303 return !result
->IsCorrupted();
306 bool LeveldbValueStore::RestoreKey(const std::string
& key
) {
307 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
309 ReadResult result
= Get(key
);
310 if (result
->IsCorrupted()) {
311 leveldb::WriteBatch batch
;
313 scoped_ptr
<ValueStore::Error
> error
= WriteToDb(&batch
);
314 // If we can't delete the key, the restore failed.
320 // The restore succeeded if there is no corruption error.
321 return !result
->IsCorrupted();
324 bool LeveldbValueStore::WriteToDbForTest(leveldb::WriteBatch
* batch
) {
325 return !WriteToDb(batch
).get();
328 scoped_ptr
<ValueStore::Error
> LeveldbValueStore::EnsureDbIsOpen() {
329 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
332 return util::NoError();
334 leveldb::Options options
;
335 options
.max_open_files
= 0; // Use minimum.
336 options
.create_if_missing
= true;
337 options
.reuse_logs
= leveldb_env::kDefaultLogReuseOptionValue
;
339 leveldb::DB
* db
= NULL
;
340 leveldb::Status status
=
341 leveldb::DB::Open(options
, db_path_
.AsUTF8Unsafe(), &db
);
343 open_histogram_
->Add(leveldb_env::GetLevelDBStatusUMAValue(status
));
345 return ToValueStoreError(status
, util::NoKey());
349 return util::NoError();
352 scoped_ptr
<ValueStore::Error
> LeveldbValueStore::ReadFromDb(
353 leveldb::ReadOptions options
,
354 const std::string
& key
,
355 scoped_ptr
<base::Value
>* setting
) {
356 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
359 std::string value_as_json
;
360 leveldb::Status s
= db_
->Get(options
, key
, &value_as_json
);
362 if (s
.IsNotFound()) {
363 // Despite there being no value, it was still a success. Check this first
364 // because ok() is false on IsNotFound.
365 return util::NoError();
369 return ToValueStoreError(s
, util::NewKey(key
));
371 scoped_ptr
<base::Value
> value
= base::JSONReader().ReadToValue(value_as_json
);
373 return Error::Create(CORRUPTION
, kInvalidJson
, util::NewKey(key
));
375 *setting
= value
.Pass();
376 return util::NoError();
379 scoped_ptr
<ValueStore::Error
> LeveldbValueStore::AddToBatch(
380 ValueStore::WriteOptions options
,
381 const std::string
& key
,
382 const base::Value
& value
,
383 leveldb::WriteBatch
* batch
,
384 ValueStoreChangeList
* changes
) {
385 bool write_new_value
= true;
387 if (!(options
& NO_GENERATE_CHANGES
)) {
388 scoped_ptr
<base::Value
> old_value
;
389 scoped_ptr
<Error
> read_error
=
390 ReadFromDb(leveldb::ReadOptions(), key
, &old_value
);
392 return read_error
.Pass();
393 if (!old_value
|| !old_value
->Equals(&value
)) {
395 ValueStoreChange(key
, old_value
.release(), value
.DeepCopy()));
397 write_new_value
= false;
401 if (write_new_value
) {
402 std::string value_as_json
;
403 if (!base::JSONWriter::Write(value
, &value_as_json
))
404 return Error::Create(OTHER_ERROR
, kCannotSerialize
, util::NewKey(key
));
405 batch
->Put(key
, value_as_json
);
408 return util::NoError();
411 scoped_ptr
<ValueStore::Error
> LeveldbValueStore::WriteToDb(
412 leveldb::WriteBatch
* batch
) {
413 leveldb::Status status
= db_
->Write(leveldb::WriteOptions(), batch
);
414 return status
.ok() ? util::NoError()
415 : ToValueStoreError(status
, util::NoKey());
418 bool LeveldbValueStore::IsEmpty() {
419 DCHECK_CURRENTLY_ON(BrowserThread::FILE);
420 scoped_ptr
<leveldb::Iterator
> it(db_
->NewIterator(leveldb::ReadOptions()));
423 bool is_empty
= !it
->Valid();
424 if (!it
->status().ok()) {
425 LOG(ERROR
) << "Checking DB emptiness failed: " << it
->status().ToString();
431 void LeveldbValueStore::DeleteDbFile() {
432 db_
.reset(); // release any lock on the directory
433 if (!base::DeleteFile(db_path_
, true /* recursive */)) {
434 LOG(WARNING
) << "Failed to delete LeveldbValueStore database at " <<
439 scoped_ptr
<ValueStore::Error
> LeveldbValueStore::ToValueStoreError(
440 const leveldb::Status
& status
,
441 scoped_ptr
<std::string
> key
) {
443 CHECK(!status
.IsNotFound()); // not an error
445 std::string message
= status
.ToString();
446 // The message may contain |db_path_|, which may be considered sensitive
447 // data, and those strings are passed to the extension, so strip it out.
448 base::ReplaceSubstringsAfterOffset(
449 &message
, 0u, db_path_
.AsUTF8Unsafe(), "...");
451 return Error::Create(CORRUPTION
, message
, key
.Pass());