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. 4 5 #include "extensions/browser/value_store/leveldb_value_store.h" 6 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/src/include/leveldb/iterator.h" 17 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h" 18 19 namespace util = value_store_util; 20 using content::BrowserThread; 21 22 namespace { 23 24 const char kInvalidJson[] = "Invalid JSON"; 25 const char kCannotSerialize[] = "Cannot serialize value to JSON"; 26 27 // Scoped leveldb snapshot which releases the snapshot on destruction. 28 class ScopedSnapshot { 29 public: 30 explicit ScopedSnapshot(leveldb::DB* db) 31 : db_(db), snapshot_(db->GetSnapshot()) {} 32 33 ~ScopedSnapshot() { 34 db_->ReleaseSnapshot(snapshot_); 35 } 36 37 const leveldb::Snapshot* get() { 38 return snapshot_; 39 } 40 41 private: 42 leveldb::DB* db_; 43 const leveldb::Snapshot* snapshot_; 44 45 DISALLOW_COPY_AND_ASSIGN(ScopedSnapshot); 46 }; 47 48 } // namespace 49 50 LeveldbValueStore::LeveldbValueStore(const base::FilePath& db_path) 51 : db_path_(db_path) { 52 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 53 54 scoped_ptr<Error> open_error = EnsureDbIsOpen(); 55 if (open_error) 56 LOG(WARNING) << open_error->message; 57 } 58 59 LeveldbValueStore::~LeveldbValueStore() { 60 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 61 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. 65 if (db_ && IsEmpty()) 66 DeleteDbFile(); 67 } 68 69 size_t LeveldbValueStore::GetBytesInUse(const std::string& key) { 70 // Let SettingsStorageQuotaEnforcer implement this. 71 NOTREACHED() << "Not implemented"; 72 return 0; 73 } 74 75 size_t LeveldbValueStore::GetBytesInUse( 76 const std::vector<std::string>& keys) { 77 // Let SettingsStorageQuotaEnforcer implement this. 78 NOTREACHED() << "Not implemented"; 79 return 0; 80 } 81 82 size_t LeveldbValueStore::GetBytesInUse() { 83 // Let SettingsStorageQuotaEnforcer implement this. 84 NOTREACHED() << "Not implemented"; 85 return 0; 86 } 87 88 ValueStore::ReadResult LeveldbValueStore::Get(const std::string& key) { 89 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 90 91 scoped_ptr<Error> open_error = EnsureDbIsOpen(); 92 if (open_error) 93 return MakeReadResult(open_error.Pass()); 94 95 scoped_ptr<base::Value> setting; 96 scoped_ptr<Error> error = ReadFromDb(leveldb::ReadOptions(), key, &setting); 97 if (error) 98 return MakeReadResult(error.Pass()); 99 100 base::DictionaryValue* settings = new base::DictionaryValue(); 101 if (setting) 102 settings->SetWithoutPathExpansion(key, setting.release()); 103 return MakeReadResult(make_scoped_ptr(settings)); 104 } 105 106 ValueStore::ReadResult LeveldbValueStore::Get( 107 const std::vector<std::string>& keys) { 108 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 109 110 scoped_ptr<Error> open_error = EnsureDbIsOpen(); 111 if (open_error) 112 return MakeReadResult(open_error.Pass()); 113 114 leveldb::ReadOptions options; 115 scoped_ptr<base::DictionaryValue> settings(new base::DictionaryValue()); 116 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); 125 if (error) 126 return MakeReadResult(error.Pass()); 127 if (setting) 128 settings->SetWithoutPathExpansion(*it, setting.release()); 129 } 130 131 return MakeReadResult(settings.Pass()); 132 } 133 134 ValueStore::ReadResult LeveldbValueStore::Get() { 135 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 136 137 scoped_ptr<Error> open_error = EnsureDbIsOpen(); 138 if (open_error) 139 return MakeReadResult(open_error.Pass()); 140 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()); 146 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()); 153 if (!value) { 154 return MakeReadResult( 155 Error::Create(CORRUPTION, kInvalidJson, util::NewKey(key))); 156 } 157 settings->SetWithoutPathExpansion(key, value); 158 } 159 160 if (it->status().IsNotFound()) { 161 NOTREACHED() << "IsNotFound() but iterating over all keys?!"; 162 return MakeReadResult(settings.Pass()); 163 } 164 165 if (!it->status().ok()) 166 return MakeReadResult(ToValueStoreError(it->status(), util::NoKey())); 167 168 return MakeReadResult(settings.Pass()); 169 } 170 171 ValueStore::WriteResult LeveldbValueStore::Set( 172 WriteOptions options, const std::string& key, const base::Value& value) { 173 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 174 175 scoped_ptr<Error> open_error = EnsureDbIsOpen(); 176 if (open_error) 177 return MakeWriteResult(open_error.Pass()); 178 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()); 183 if (batch_error) 184 return MakeWriteResult(batch_error.Pass()); 185 186 scoped_ptr<Error> write_error = WriteToDb(&batch); 187 return write_error ? MakeWriteResult(write_error.Pass()) 188 : MakeWriteResult(changes.Pass()); 189 } 190 191 ValueStore::WriteResult LeveldbValueStore::Set( 192 WriteOptions options, const base::DictionaryValue& settings) { 193 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 194 195 scoped_ptr<Error> open_error = EnsureDbIsOpen(); 196 if (open_error) 197 return MakeWriteResult(open_error.Pass()); 198 199 leveldb::WriteBatch batch; 200 scoped_ptr<ValueStoreChangeList> changes(new ValueStoreChangeList()); 201 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()); 206 if (batch_error) 207 return MakeWriteResult(batch_error.Pass()); 208 } 209 210 scoped_ptr<Error> write_error = WriteToDb(&batch); 211 return write_error ? MakeWriteResult(write_error.Pass()) 212 : MakeWriteResult(changes.Pass()); 213 } 214 215 ValueStore::WriteResult LeveldbValueStore::Remove(const std::string& key) { 216 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 217 return Remove(std::vector<std::string>(1, key)); 218 } 219 220 ValueStore::WriteResult LeveldbValueStore::Remove( 221 const std::vector<std::string>& keys) { 222 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 223 224 scoped_ptr<Error> open_error = EnsureDbIsOpen(); 225 if (open_error) 226 return MakeWriteResult(open_error.Pass()); 227 228 leveldb::WriteBatch batch; 229 scoped_ptr<ValueStoreChangeList> changes(new ValueStoreChangeList()); 230 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); 236 if (read_error) 237 return MakeWriteResult(read_error.Pass()); 238 239 if (old_value) { 240 changes->push_back(ValueStoreChange(*it, old_value.release(), NULL)); 241 batch.Delete(*it); 242 } 243 } 244 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()); 249 } 250 251 ValueStore::WriteResult LeveldbValueStore::Clear() { 252 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 253 254 scoped_ptr<ValueStoreChangeList> changes(new ValueStoreChangeList()); 255 256 ReadResult read_result = Get(); 257 if (read_result->HasError()) 258 return MakeWriteResult(read_result->PassError()); 259 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); 265 changes->push_back( 266 ValueStoreChange(next_key, next_value.release(), NULL)); 267 } 268 269 DeleteDbFile(); 270 return MakeWriteResult(changes.Pass()); 271 } 272 273 bool LeveldbValueStore::Restore() { 274 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 275 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 281 // whole database. 282 if (!result->error().key.get() || *result->error().key == previous_key || 283 !RestoreKey(*result->error().key)) { 284 DeleteDbFile(); 285 result = Get(); 286 break; 287 } 288 289 // Otherwise, re-Get() the database to check if there is still any 290 // corruption. 291 previous_key = *result->error().key; 292 result = Get(); 293 } 294 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(); 298 } 299 300 bool LeveldbValueStore::RestoreKey(const std::string& key) { 301 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 302 303 ReadResult result = Get(key); 304 if (result->IsCorrupted()) { 305 leveldb::WriteBatch batch; 306 batch.Delete(key); 307 scoped_ptr<ValueStore::Error> error = WriteToDb(&batch); 308 // If we can't delete the key, the restore failed. 309 if (error.get()) 310 return false; 311 result = Get(key); 312 } 313 314 // The restore succeeded if there is no corruption error. 315 return !result->IsCorrupted(); 316 } 317 318 bool LeveldbValueStore::WriteToDbForTest(leveldb::WriteBatch* batch) { 319 return !WriteToDb(batch).get(); 320 } 321 322 scoped_ptr<ValueStore::Error> LeveldbValueStore::EnsureDbIsOpen() { 323 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 324 325 if (db_) 326 return util::NoError(); 327 328 leveldb::Options options; 329 options.max_open_files = 0; // Use minimum. 330 options.create_if_missing = true; 331 332 leveldb::DB* db = NULL; 333 leveldb::Status status = 334 leveldb::DB::Open(options, db_path_.AsUTF8Unsafe(), &db); 335 if (!status.ok()) 336 return ToValueStoreError(status, util::NoKey()); 337 338 CHECK(db); 339 db_.reset(db); 340 return util::NoError(); 341 } 342 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); 348 DCHECK(setting); 349 350 std::string value_as_json; 351 leveldb::Status s = db_->Get(options, key, &value_as_json); 352 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(); 357 } 358 359 if (!s.ok()) 360 return ToValueStoreError(s, util::NewKey(key)); 361 362 base::Value* value = base::JSONReader().ReadToValue(value_as_json); 363 if (!value) 364 return Error::Create(CORRUPTION, kInvalidJson, util::NewKey(key)); 365 366 setting->reset(value); 367 return util::NoError(); 368 } 369 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; 377 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); 382 if (read_error) 383 return read_error.Pass(); 384 if (!old_value || !old_value->Equals(&value)) { 385 changes->push_back( 386 ValueStoreChange(key, old_value.release(), value.DeepCopy())); 387 } else { 388 write_new_value = false; 389 } 390 } 391 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); 397 } 398 399 return util::NoError(); 400 } 401 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()); 407 } 408 409 bool LeveldbValueStore::IsEmpty() { 410 DCHECK_CURRENTLY_ON(BrowserThread::FILE); 411 scoped_ptr<leveldb::Iterator> it(db_->NewIterator(leveldb::ReadOptions())); 412 413 it->SeekToFirst(); 414 bool is_empty = !it->Valid(); 415 if (!it->status().ok()) { 416 LOG(ERROR) << "Checking DB emptiness failed: " << it->status().ToString(); 417 return false; 418 } 419 return is_empty; 420 } 421 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 " << 426 db_path_.value(); 427 } 428 } 429 430 scoped_ptr<ValueStore::Error> LeveldbValueStore::ToValueStoreError( 431 const leveldb::Status& status, 432 scoped_ptr<std::string> key) { 433 CHECK(!status.ok()); 434 CHECK(!status.IsNotFound()); // not an error 435 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(), "..."); 440 441 return Error::Create(CORRUPTION, message, key.Pass()); 442 } 443