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. 4 5#include "chrome/browser/value_store/leveldb_value_store.h" 6 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" 18 19namespace util = value_store_util; 20using content::BrowserThread; 21 22namespace { 23 24const char kInvalidJson[] = "Invalid JSON"; 25 26// Scoped leveldb snapshot which releases the snapshot on destruction. 27class ScopedSnapshot { 28 public: 29 explicit ScopedSnapshot(leveldb::DB* db) 30 : db_(db), snapshot_(db->GetSnapshot()) {} 31 32 ~ScopedSnapshot() { 33 db_->ReleaseSnapshot(snapshot_); 34 } 35 36 const leveldb::Snapshot* get() { 37 return snapshot_; 38 } 39 40 private: 41 leveldb::DB* db_; 42 const leveldb::Snapshot* snapshot_; 43 44 DISALLOW_COPY_AND_ASSIGN(ScopedSnapshot); 45}; 46 47} // namespace 48 49LeveldbValueStore::LeveldbValueStore(const base::FilePath& db_path) 50 : db_path_(db_path) { 51 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 52 53 scoped_ptr<Error> open_error = EnsureDbIsOpen(); 54 if (open_error) 55 LOG(WARNING) << open_error->message; 56} 57 58LeveldbValueStore::~LeveldbValueStore() { 59 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 60 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. 64 if (db_ && IsEmpty()) 65 DeleteDbFile(); 66} 67 68size_t LeveldbValueStore::GetBytesInUse(const std::string& key) { 69 // Let SettingsStorageQuotaEnforcer implement this. 70 NOTREACHED() << "Not implemented"; 71 return 0; 72} 73 74size_t LeveldbValueStore::GetBytesInUse( 75 const std::vector<std::string>& keys) { 76 // Let SettingsStorageQuotaEnforcer implement this. 77 NOTREACHED() << "Not implemented"; 78 return 0; 79} 80 81size_t LeveldbValueStore::GetBytesInUse() { 82 // Let SettingsStorageQuotaEnforcer implement this. 83 NOTREACHED() << "Not implemented"; 84 return 0; 85} 86 87ValueStore::ReadResult LeveldbValueStore::Get(const std::string& key) { 88 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 89 90 scoped_ptr<Error> open_error = EnsureDbIsOpen(); 91 if (open_error) 92 return MakeReadResult(open_error.Pass()); 93 94 scoped_ptr<Value> setting; 95 scoped_ptr<Error> error = ReadFromDb(leveldb::ReadOptions(), key, &setting); 96 if (error) 97 return MakeReadResult(error.Pass()); 98 99 DictionaryValue* settings = new DictionaryValue(); 100 if (setting) 101 settings->SetWithoutPathExpansion(key, setting.release()); 102 return MakeReadResult(make_scoped_ptr(settings)); 103} 104 105ValueStore::ReadResult LeveldbValueStore::Get( 106 const std::vector<std::string>& keys) { 107 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 108 109 scoped_ptr<Error> open_error = EnsureDbIsOpen(); 110 if (open_error) 111 return MakeReadResult(open_error.Pass()); 112 113 leveldb::ReadOptions options; 114 scoped_ptr<DictionaryValue> settings(new DictionaryValue()); 115 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<Value> setting; 123 scoped_ptr<Error> error = ReadFromDb(options, *it, &setting); 124 if (error) 125 return MakeReadResult(error.Pass()); 126 if (setting) 127 settings->SetWithoutPathExpansion(*it, setting.release()); 128 } 129 130 return MakeReadResult(settings.Pass()); 131} 132 133ValueStore::ReadResult LeveldbValueStore::Get() { 134 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 135 136 scoped_ptr<Error> open_error = EnsureDbIsOpen(); 137 if (open_error) 138 return MakeReadResult(open_error.Pass()); 139 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<DictionaryValue> settings(new DictionaryValue()); 145 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 Value* value = json_reader.ReadToValue(it->value().ToString()); 152 if (!value) { 153 return MakeReadResult( 154 Error::Create(CORRUPTION, kInvalidJson, util::NewKey(key))); 155 } 156 settings->SetWithoutPathExpansion(key, value); 157 } 158 159 if (it->status().IsNotFound()) { 160 NOTREACHED() << "IsNotFound() but iterating over all keys?!"; 161 return MakeReadResult(settings.Pass()); 162 } 163 164 if (!it->status().ok()) 165 return MakeReadResult(ToValueStoreError(it->status(), util::NoKey())); 166 167 return MakeReadResult(settings.Pass()); 168} 169 170ValueStore::WriteResult LeveldbValueStore::Set( 171 WriteOptions options, const std::string& key, const Value& value) { 172 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 173 174 scoped_ptr<Error> open_error = EnsureDbIsOpen(); 175 if (open_error) 176 return MakeWriteResult(open_error.Pass()); 177 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()); 182 if (batch_error) 183 return MakeWriteResult(batch_error.Pass()); 184 185 scoped_ptr<Error> write_error = WriteToDb(&batch); 186 return write_error ? MakeWriteResult(write_error.Pass()) 187 : MakeWriteResult(changes.Pass()); 188} 189 190ValueStore::WriteResult LeveldbValueStore::Set( 191 WriteOptions options, const DictionaryValue& settings) { 192 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 193 194 scoped_ptr<Error> open_error = EnsureDbIsOpen(); 195 if (open_error) 196 return MakeWriteResult(open_error.Pass()); 197 198 leveldb::WriteBatch batch; 199 scoped_ptr<ValueStoreChangeList> changes(new ValueStoreChangeList()); 200 201 for (DictionaryValue::Iterator it(settings); !it.IsAtEnd(); it.Advance()) { 202 scoped_ptr<Error> batch_error = 203 AddToBatch(options, it.key(), it.value(), &batch, changes.get()); 204 if (batch_error) 205 return MakeWriteResult(batch_error.Pass()); 206 } 207 208 scoped_ptr<Error> write_error = WriteToDb(&batch); 209 return write_error ? MakeWriteResult(write_error.Pass()) 210 : MakeWriteResult(changes.Pass()); 211} 212 213ValueStore::WriteResult LeveldbValueStore::Remove(const std::string& key) { 214 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 215 return Remove(std::vector<std::string>(1, key)); 216} 217 218ValueStore::WriteResult LeveldbValueStore::Remove( 219 const std::vector<std::string>& keys) { 220 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 221 222 scoped_ptr<Error> open_error = EnsureDbIsOpen(); 223 if (open_error) 224 return MakeWriteResult(open_error.Pass()); 225 226 leveldb::WriteBatch batch; 227 scoped_ptr<ValueStoreChangeList> changes(new ValueStoreChangeList()); 228 229 for (std::vector<std::string>::const_iterator it = keys.begin(); 230 it != keys.end(); ++it) { 231 scoped_ptr<Value> old_value; 232 scoped_ptr<Error> read_error = 233 ReadFromDb(leveldb::ReadOptions(), *it, &old_value); 234 if (read_error) 235 return MakeWriteResult(read_error.Pass()); 236 237 if (old_value) { 238 changes->push_back(ValueStoreChange(*it, old_value.release(), NULL)); 239 batch.Delete(*it); 240 } 241 } 242 243 leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch); 244 if (!status.ok() && !status.IsNotFound()) 245 return MakeWriteResult(ToValueStoreError(status, util::NoKey())); 246 return MakeWriteResult(changes.Pass()); 247} 248 249ValueStore::WriteResult LeveldbValueStore::Clear() { 250 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 251 252 scoped_ptr<ValueStoreChangeList> changes(new ValueStoreChangeList()); 253 254 ReadResult read_result = Get(); 255 if (read_result->HasError()) 256 return MakeWriteResult(read_result->PassError()); 257 258 base::DictionaryValue& whole_db = read_result->settings(); 259 while (!whole_db.empty()) { 260 std::string next_key = DictionaryValue::Iterator(whole_db).key(); 261 scoped_ptr<base::Value> next_value; 262 whole_db.RemoveWithoutPathExpansion(next_key, &next_value); 263 changes->push_back( 264 ValueStoreChange(next_key, next_value.release(), NULL)); 265 } 266 267 DeleteDbFile(); 268 return MakeWriteResult(changes.Pass()); 269} 270 271scoped_ptr<ValueStore::Error> LeveldbValueStore::EnsureDbIsOpen() { 272 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 273 274 if (db_) 275 return util::NoError(); 276 277 leveldb::Options options; 278 options.max_open_files = 0; // Use minimum. 279 options.create_if_missing = true; 280 281 leveldb::DB* db = NULL; 282 leveldb::Status status = 283 leveldb::DB::Open(options, db_path_.AsUTF8Unsafe(), &db); 284 if (!status.ok()) 285 return ToValueStoreError(status, util::NoKey()); 286 287 CHECK(db); 288 db_.reset(db); 289 return util::NoError(); 290} 291 292scoped_ptr<ValueStore::Error> LeveldbValueStore::ReadFromDb( 293 leveldb::ReadOptions options, 294 const std::string& key, 295 scoped_ptr<Value>* setting) { 296 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 297 DCHECK(setting); 298 299 std::string value_as_json; 300 leveldb::Status s = db_->Get(options, key, &value_as_json); 301 302 if (s.IsNotFound()) { 303 // Despite there being no value, it was still a success. Check this first 304 // because ok() is false on IsNotFound. 305 return util::NoError(); 306 } 307 308 if (!s.ok()) 309 return ToValueStoreError(s, util::NewKey(key)); 310 311 Value* value = base::JSONReader().ReadToValue(value_as_json); 312 if (!value) 313 return Error::Create(CORRUPTION, kInvalidJson, util::NewKey(key)); 314 315 setting->reset(value); 316 return util::NoError(); 317} 318 319scoped_ptr<ValueStore::Error> LeveldbValueStore::AddToBatch( 320 ValueStore::WriteOptions options, 321 const std::string& key, 322 const base::Value& value, 323 leveldb::WriteBatch* batch, 324 ValueStoreChangeList* changes) { 325 bool write_new_value = true; 326 327 if (!(options & NO_GENERATE_CHANGES)) { 328 scoped_ptr<Value> old_value; 329 scoped_ptr<Error> read_error = 330 ReadFromDb(leveldb::ReadOptions(), key, &old_value); 331 if (read_error) 332 return read_error.Pass(); 333 if (!old_value || !old_value->Equals(&value)) { 334 changes->push_back( 335 ValueStoreChange(key, old_value.release(), value.DeepCopy())); 336 } else { 337 write_new_value = false; 338 } 339 } 340 341 if (write_new_value) { 342 std::string value_as_json; 343 base::JSONWriter::Write(&value, &value_as_json); 344 batch->Put(key, value_as_json); 345 } 346 347 return util::NoError(); 348} 349 350scoped_ptr<ValueStore::Error> LeveldbValueStore::WriteToDb( 351 leveldb::WriteBatch* batch) { 352 leveldb::Status status = db_->Write(leveldb::WriteOptions(), batch); 353 return status.ok() ? util::NoError() 354 : ToValueStoreError(status, util::NoKey()); 355} 356 357bool LeveldbValueStore::IsEmpty() { 358 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 359 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions())); 360 361 it->SeekToFirst(); 362 bool is_empty = !it->Valid(); 363 if (!it->status().ok()) { 364 LOG(ERROR) << "Checking DB emptiness failed: " << it->status().ToString(); 365 return false; 366 } 367 return is_empty; 368} 369 370void LeveldbValueStore::DeleteDbFile() { 371 db_.reset(); // release any lock on the directory 372 if (!base::DeleteFile(db_path_, true /* recursive */)) { 373 LOG(WARNING) << "Failed to delete LeveldbValueStore database at " << 374 db_path_.value(); 375 } 376} 377 378scoped_ptr<ValueStore::Error> LeveldbValueStore::ToValueStoreError( 379 const leveldb::Status& status, 380 scoped_ptr<std::string> key) { 381 CHECK(!status.ok()); 382 CHECK(!status.IsNotFound()); // not an error 383 384 std::string message = status.ToString(); 385 // The message may contain |db_path_|, which may be considered sensitive 386 // data, and those strings are passed to the extension, so strip it out. 387 ReplaceSubstringsAfterOffset(&message, 0u, db_path_.AsUTF8Unsafe(), "..."); 388 389 return Error::Create(CORRUPTION, message, key.Pass()); 390} 391