1 // Copyright (c) 2012 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 "chrome/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 "chrome/browser/value_store/value_store_util.h"
15 #include "content/public/browser/browser_thread.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";
26 // Scoped leveldb snapshot which releases the snapshot on destruction.
27 class ScopedSnapshot
{
29 explicit ScopedSnapshot(leveldb::DB
* db
)
30 : db_(db
), snapshot_(db
->GetSnapshot()) {}
33 db_
->ReleaseSnapshot(snapshot_
);
36 const leveldb::Snapshot
* get() {
42 const leveldb::Snapshot
* snapshot_
;
44 DISALLOW_COPY_AND_ASSIGN(ScopedSnapshot
);
49 LeveldbValueStore::LeveldbValueStore(const base::FilePath
& db_path
)
51 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
53 scoped_ptr
<Error
> open_error
= EnsureDbIsOpen();
55 LOG(WARNING
) << open_error
->message
;
58 LeveldbValueStore::~LeveldbValueStore() {
59 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
61 // Delete the database from disk if it's empty (but only if we managed to
62 // open it!). This is safe on destruction, assuming that we have exclusive
63 // access to the database.
68 size_t LeveldbValueStore::GetBytesInUse(const std::string
& key
) {
69 // Let SettingsStorageQuotaEnforcer implement this.
70 NOTREACHED() << "Not implemented";
74 size_t LeveldbValueStore::GetBytesInUse(
75 const std::vector
<std::string
>& keys
) {
76 // Let SettingsStorageQuotaEnforcer implement this.
77 NOTREACHED() << "Not implemented";
81 size_t LeveldbValueStore::GetBytesInUse() {
82 // Let SettingsStorageQuotaEnforcer implement this.
83 NOTREACHED() << "Not implemented";
87 ValueStore::ReadResult
LeveldbValueStore::Get(const std::string
& key
) {
88 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
90 scoped_ptr
<Error
> open_error
= EnsureDbIsOpen();
92 return MakeReadResult(open_error
.Pass());
94 scoped_ptr
<base::Value
> setting
;
95 scoped_ptr
<Error
> error
= ReadFromDb(leveldb::ReadOptions(), key
, &setting
);
97 return MakeReadResult(error
.Pass());
99 base::DictionaryValue
* settings
= new base::DictionaryValue();
101 settings
->SetWithoutPathExpansion(key
, setting
.release());
102 return MakeReadResult(make_scoped_ptr(settings
));
105 ValueStore::ReadResult
LeveldbValueStore::Get(
106 const std::vector
<std::string
>& keys
) {
107 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
109 scoped_ptr
<Error
> open_error
= EnsureDbIsOpen();
111 return MakeReadResult(open_error
.Pass());
113 leveldb::ReadOptions options
;
114 scoped_ptr
<base::DictionaryValue
> settings(new base::DictionaryValue());
116 // All interaction with the db is done on the same thread, so snapshotting
117 // isn't strictly necessary. This is just defensive.
118 ScopedSnapshot
snapshot(db_
.get());
119 options
.snapshot
= snapshot
.get();
120 for (std::vector
<std::string
>::const_iterator it
= keys
.begin();
121 it
!= keys
.end(); ++it
) {
122 scoped_ptr
<base::Value
> setting
;
123 scoped_ptr
<Error
> error
= ReadFromDb(options
, *it
, &setting
);
125 return MakeReadResult(error
.Pass());
127 settings
->SetWithoutPathExpansion(*it
, setting
.release());
130 return MakeReadResult(settings
.Pass());
133 ValueStore::ReadResult
LeveldbValueStore::Get() {
134 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
136 scoped_ptr
<Error
> open_error
= EnsureDbIsOpen();
138 return MakeReadResult(open_error
.Pass());
140 base::JSONReader json_reader
;
141 leveldb::ReadOptions options
= leveldb::ReadOptions();
142 // All interaction with the db is done on the same thread, so snapshotting
143 // isn't strictly necessary. This is just defensive.
144 scoped_ptr
<base::DictionaryValue
> settings(new base::DictionaryValue());
146 ScopedSnapshot
snapshot(db_
.get());
147 options
.snapshot
= snapshot
.get();
148 scoped_ptr
<leveldb::Iterator
> it(db_
->NewIterator(options
));
149 for (it
->SeekToFirst(); it
->Valid(); it
->Next()) {
150 std::string key
= it
->key().ToString();
151 base::Value
* value
= json_reader
.ReadToValue(it
->value().ToString());
153 return MakeReadResult(
154 Error::Create(CORRUPTION
, kInvalidJson
, util::NewKey(key
)));
156 settings
->SetWithoutPathExpansion(key
, value
);
159 if (it
->status().IsNotFound()) {
160 NOTREACHED() << "IsNotFound() but iterating over all keys?!";
161 return MakeReadResult(settings
.Pass());
164 if (!it
->status().ok())
165 return MakeReadResult(ToValueStoreError(it
->status(), util::NoKey()));
167 return MakeReadResult(settings
.Pass());
170 ValueStore::WriteResult
LeveldbValueStore::Set(
171 WriteOptions options
, const std::string
& key
, const base::Value
& value
) {
172 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
174 scoped_ptr
<Error
> open_error
= EnsureDbIsOpen();
176 return MakeWriteResult(open_error
.Pass());
178 leveldb::WriteBatch batch
;
179 scoped_ptr
<ValueStoreChangeList
> changes(new ValueStoreChangeList());
180 scoped_ptr
<Error
> batch_error
=
181 AddToBatch(options
, key
, value
, &batch
, changes
.get());
183 return MakeWriteResult(batch_error
.Pass());
185 scoped_ptr
<Error
> write_error
= WriteToDb(&batch
);
186 return write_error
? MakeWriteResult(write_error
.Pass())
187 : MakeWriteResult(changes
.Pass());
190 ValueStore::WriteResult
LeveldbValueStore::Set(
191 WriteOptions options
, const base::DictionaryValue
& settings
) {
192 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
194 scoped_ptr
<Error
> open_error
= EnsureDbIsOpen();
196 return MakeWriteResult(open_error
.Pass());
198 leveldb::WriteBatch batch
;
199 scoped_ptr
<ValueStoreChangeList
> changes(new ValueStoreChangeList());
201 for (base::DictionaryValue::Iterator
it(settings
);
202 !it
.IsAtEnd(); it
.Advance()) {
203 scoped_ptr
<Error
> batch_error
=
204 AddToBatch(options
, it
.key(), it
.value(), &batch
, changes
.get());
206 return MakeWriteResult(batch_error
.Pass());
209 scoped_ptr
<Error
> write_error
= WriteToDb(&batch
);
210 return write_error
? MakeWriteResult(write_error
.Pass())
211 : MakeWriteResult(changes
.Pass());
214 ValueStore::WriteResult
LeveldbValueStore::Remove(const std::string
& key
) {
215 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
216 return Remove(std::vector
<std::string
>(1, key
));
219 ValueStore::WriteResult
LeveldbValueStore::Remove(
220 const std::vector
<std::string
>& keys
) {
221 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
223 scoped_ptr
<Error
> open_error
= EnsureDbIsOpen();
225 return MakeWriteResult(open_error
.Pass());
227 leveldb::WriteBatch batch
;
228 scoped_ptr
<ValueStoreChangeList
> changes(new ValueStoreChangeList());
230 for (std::vector
<std::string
>::const_iterator it
= keys
.begin();
231 it
!= keys
.end(); ++it
) {
232 scoped_ptr
<base::Value
> old_value
;
233 scoped_ptr
<Error
> read_error
=
234 ReadFromDb(leveldb::ReadOptions(), *it
, &old_value
);
236 return MakeWriteResult(read_error
.Pass());
239 changes
->push_back(ValueStoreChange(*it
, old_value
.release(), NULL
));
244 leveldb::Status status
= db_
->Write(leveldb::WriteOptions(), &batch
);
245 if (!status
.ok() && !status
.IsNotFound())
246 return MakeWriteResult(ToValueStoreError(status
, util::NoKey()));
247 return MakeWriteResult(changes
.Pass());
250 ValueStore::WriteResult
LeveldbValueStore::Clear() {
251 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
253 scoped_ptr
<ValueStoreChangeList
> changes(new ValueStoreChangeList());
255 ReadResult read_result
= Get();
256 if (read_result
->HasError())
257 return MakeWriteResult(read_result
->PassError());
259 base::DictionaryValue
& whole_db
= read_result
->settings();
260 while (!whole_db
.empty()) {
261 std::string next_key
= base::DictionaryValue::Iterator(whole_db
).key();
262 scoped_ptr
<base::Value
> next_value
;
263 whole_db
.RemoveWithoutPathExpansion(next_key
, &next_value
);
265 ValueStoreChange(next_key
, next_value
.release(), NULL
));
269 return MakeWriteResult(changes
.Pass());
272 scoped_ptr
<ValueStore::Error
> LeveldbValueStore::EnsureDbIsOpen() {
273 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
276 return util::NoError();
278 leveldb::Options options
;
279 options
.max_open_files
= 0; // Use minimum.
280 options
.create_if_missing
= true;
282 leveldb::DB
* db
= NULL
;
283 leveldb::Status status
=
284 leveldb::DB::Open(options
, db_path_
.AsUTF8Unsafe(), &db
);
286 return ToValueStoreError(status
, util::NoKey());
290 return util::NoError();
293 scoped_ptr
<ValueStore::Error
> LeveldbValueStore::ReadFromDb(
294 leveldb::ReadOptions options
,
295 const std::string
& key
,
296 scoped_ptr
<base::Value
>* setting
) {
297 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
300 std::string value_as_json
;
301 leveldb::Status s
= db_
->Get(options
, key
, &value_as_json
);
303 if (s
.IsNotFound()) {
304 // Despite there being no value, it was still a success. Check this first
305 // because ok() is false on IsNotFound.
306 return util::NoError();
310 return ToValueStoreError(s
, util::NewKey(key
));
312 base::Value
* value
= base::JSONReader().ReadToValue(value_as_json
);
314 return Error::Create(CORRUPTION
, kInvalidJson
, util::NewKey(key
));
316 setting
->reset(value
);
317 return util::NoError();
320 scoped_ptr
<ValueStore::Error
> LeveldbValueStore::AddToBatch(
321 ValueStore::WriteOptions options
,
322 const std::string
& key
,
323 const base::Value
& value
,
324 leveldb::WriteBatch
* batch
,
325 ValueStoreChangeList
* changes
) {
326 bool write_new_value
= true;
328 if (!(options
& NO_GENERATE_CHANGES
)) {
329 scoped_ptr
<base::Value
> old_value
;
330 scoped_ptr
<Error
> read_error
=
331 ReadFromDb(leveldb::ReadOptions(), key
, &old_value
);
333 return read_error
.Pass();
334 if (!old_value
|| !old_value
->Equals(&value
)) {
336 ValueStoreChange(key
, old_value
.release(), value
.DeepCopy()));
338 write_new_value
= false;
342 if (write_new_value
) {
343 std::string value_as_json
;
344 base::JSONWriter::Write(&value
, &value_as_json
);
345 batch
->Put(key
, value_as_json
);
348 return util::NoError();
351 scoped_ptr
<ValueStore::Error
> LeveldbValueStore::WriteToDb(
352 leveldb::WriteBatch
* batch
) {
353 leveldb::Status status
= db_
->Write(leveldb::WriteOptions(), batch
);
354 return status
.ok() ? util::NoError()
355 : ToValueStoreError(status
, util::NoKey());
358 bool LeveldbValueStore::IsEmpty() {
359 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
360 scoped_ptr
<leveldb::Iterator
> it(db_
->NewIterator(leveldb::ReadOptions()));
363 bool is_empty
= !it
->Valid();
364 if (!it
->status().ok()) {
365 LOG(ERROR
) << "Checking DB emptiness failed: " << it
->status().ToString();
371 void LeveldbValueStore::DeleteDbFile() {
372 db_
.reset(); // release any lock on the directory
373 if (!base::DeleteFile(db_path_
, true /* recursive */)) {
374 LOG(WARNING
) << "Failed to delete LeveldbValueStore database at " <<
379 scoped_ptr
<ValueStore::Error
> LeveldbValueStore::ToValueStoreError(
380 const leveldb::Status
& status
,
381 scoped_ptr
<std::string
> key
) {
383 CHECK(!status
.IsNotFound()); // not an error
385 std::string message
= status
.ToString();
386 // The message may contain |db_path_|, which may be considered sensitive
387 // data, and those strings are passed to the extension, so strip it out.
388 ReplaceSubstringsAfterOffset(&message
, 0u, db_path_
.AsUTF8Unsafe(), "...");
390 return Error::Create(CORRUPTION
, message
, key
.Pass());