Home | History | Annotate | Download | only in value_store
      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