Home | History | Annotate | Download | only in prefs
      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 "chrome/browser/prefs/leveldb_pref_store.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/callback.h"
      9 #include "base/file_util.h"
     10 #include "base/json/json_string_value_serializer.h"
     11 #include "base/location.h"
     12 #include "base/metrics/sparse_histogram.h"
     13 #include "base/sequenced_task_runner.h"
     14 #include "base/task_runner_util.h"
     15 #include "base/threading/thread_restrictions.h"
     16 #include "base/time/time.h"
     17 #include "base/values.h"
     18 #include "third_party/leveldatabase/env_chromium.h"
     19 #include "third_party/leveldatabase/src/include/leveldb/db.h"
     20 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
     21 
     22 namespace {
     23 
     24 enum ErrorMasks {
     25   OPENED = 1 << 0,
     26   DESTROYED = 1 << 1,
     27   REPAIRED = 1 << 2,
     28   DESTROY_FAILED = 1 << 3,
     29   REPAIR_FAILED = 1 << 4,
     30   IO_ERROR = 1 << 5,
     31   DATA_LOST = 1 << 6,
     32   ITER_NOT_OK = 1 << 7,
     33   FILE_NOT_SPECIFIED = 1 << 8,
     34 };
     35 
     36 PersistentPrefStore::PrefReadError IntToPrefReadError(int error) {
     37   DCHECK(error);
     38   if (error == FILE_NOT_SPECIFIED)
     39     return PersistentPrefStore::PREF_READ_ERROR_FILE_NOT_SPECIFIED;
     40   if (error == OPENED)
     41     return PersistentPrefStore::PREF_READ_ERROR_NONE;
     42   if (error & IO_ERROR)
     43     return PersistentPrefStore::PREF_READ_ERROR_LEVELDB_IO;
     44   if (error & OPENED)
     45     return PersistentPrefStore::PREF_READ_ERROR_LEVELDB_CORRUPTION;
     46   return PersistentPrefStore::PREF_READ_ERROR_LEVELDB_CORRUPTION_READ_ONLY;
     47 }
     48 
     49 } // namespace
     50 
     51 struct LevelDBPrefStore::ReadingResults {
     52   ReadingResults() : no_dir(true), error(0) {}
     53   bool no_dir;
     54   scoped_ptr<leveldb::DB> db;
     55   scoped_ptr<PrefValueMap> value_map;
     56   int error;
     57 };
     58 
     59 // An instance of this class is created on the UI thread but is used
     60 // exclusively on the FILE thread.
     61 class LevelDBPrefStore::FileThreadSerializer {
     62  public:
     63   explicit FileThreadSerializer(scoped_ptr<leveldb::DB> db) : db_(db.Pass()) {}
     64   void WriteToDatabase(
     65       std::map<std::string, std::string>* keys_to_set,
     66       std::set<std::string>* keys_to_delete) {
     67     DCHECK(keys_to_set->size() > 0 || keys_to_delete->size() > 0);
     68     leveldb::WriteBatch batch;
     69     for (std::map<std::string, std::string>::iterator iter =
     70              keys_to_set->begin();
     71          iter != keys_to_set->end();
     72          iter++) {
     73       batch.Put(iter->first, iter->second);
     74     }
     75 
     76     for (std::set<std::string>::iterator iter = keys_to_delete->begin();
     77          iter != keys_to_delete->end();
     78          iter++) {
     79       batch.Delete(*iter);
     80     }
     81 
     82     leveldb::Status status = db_->Write(leveldb::WriteOptions(), &batch);
     83 
     84     // DCHECK is fine; the corresponding error is ignored in JsonPrefStore.
     85     // There's also no API available to surface the error back up to the caller.
     86     // TODO(dgrogan): UMA?
     87     DCHECK(status.ok()) << status.ToString();
     88   }
     89 
     90  private:
     91   scoped_ptr<leveldb::DB> db_;
     92   DISALLOW_COPY_AND_ASSIGN(FileThreadSerializer);
     93 };
     94 
     95 bool MoveDirectoryAside(const base::FilePath& path) {
     96   base::FilePath bad_path = path.AppendASCII(".bad");
     97   if (!base::Move(path, bad_path)) {
     98     base::DeleteFile(bad_path, true);
     99     return false;
    100   }
    101   return true;
    102 }
    103 
    104 /* static */
    105 void LevelDBPrefStore::OpenDB(const base::FilePath& path,
    106                               ReadingResults* reading_results) {
    107   DCHECK_EQ(0, reading_results->error);
    108   leveldb::Options options;
    109   options.create_if_missing = true;
    110   leveldb::DB* db;
    111   while (1) {
    112     leveldb::Status status =
    113         leveldb::DB::Open(options, path.AsUTF8Unsafe(), &db);
    114     if (status.ok()) {
    115       reading_results->db.reset(db);
    116       reading_results->error |= OPENED;
    117       break;
    118     }
    119     if (leveldb_env::IsIOError(status)) {
    120       reading_results->error |= IO_ERROR;
    121       break;
    122     }
    123     if (reading_results->error & DESTROYED)
    124       break;
    125 
    126     DCHECK(!(reading_results->error & REPAIR_FAILED));
    127     if (!(reading_results->error & REPAIRED)) {
    128       status = leveldb::RepairDB(path.AsUTF8Unsafe(), options);
    129       if (status.ok()) {
    130         reading_results->error |= REPAIRED;
    131         continue;
    132       }
    133       reading_results->error |= REPAIR_FAILED;
    134     }
    135     if (!MoveDirectoryAside(path)) {
    136       status = leveldb::DestroyDB(path.AsUTF8Unsafe(), options);
    137       if (!status.ok()) {
    138         reading_results->error |= DESTROY_FAILED;
    139         break;
    140       }
    141     }
    142     reading_results->error |= DESTROYED;
    143   }
    144   DCHECK(reading_results->error);
    145   DCHECK(!((reading_results->error & OPENED) &&
    146            (reading_results->error & DESTROY_FAILED)));
    147   DCHECK(reading_results->error != (REPAIR_FAILED | OPENED));
    148 }
    149 
    150 /* static */
    151 scoped_ptr<LevelDBPrefStore::ReadingResults> LevelDBPrefStore::DoReading(
    152     const base::FilePath& path) {
    153   base::ThreadRestrictions::AssertIOAllowed();
    154 
    155   scoped_ptr<ReadingResults> reading_results(new ReadingResults);
    156 
    157   reading_results->no_dir = !base::PathExists(path.DirName());
    158   OpenDB(path, reading_results.get());
    159   if (!reading_results->db) {
    160     DCHECK(!(reading_results->error & OPENED));
    161     return reading_results.Pass();
    162   }
    163 
    164   DCHECK(reading_results->error & OPENED);
    165   reading_results->value_map.reset(new PrefValueMap);
    166   scoped_ptr<leveldb::Iterator> it(
    167       reading_results->db->NewIterator(leveldb::ReadOptions()));
    168   // TODO(dgrogan): Is it really necessary to check it->status() each iteration?
    169   for (it->SeekToFirst(); it->Valid() && it->status().ok(); it->Next()) {
    170     const std::string value_string = it->value().ToString();
    171     JSONStringValueSerializer deserializer(value_string);
    172     std::string error_message;
    173     int error_code;
    174     base::Value* json_value =
    175         deserializer.Deserialize(&error_code, &error_message);
    176     if (json_value) {
    177       reading_results->value_map->SetValue(it->key().ToString(), json_value);
    178     } else {
    179       DLOG(ERROR) << "Invalid json for key " << it->key().ToString()
    180                   << ": " << error_message;
    181       reading_results->error |= DATA_LOST;
    182     }
    183   }
    184 
    185   if (!it->status().ok())
    186     reading_results->error |= ITER_NOT_OK;
    187 
    188   return reading_results.Pass();
    189 }
    190 
    191 LevelDBPrefStore::LevelDBPrefStore(
    192     const base::FilePath& filename,
    193     base::SequencedTaskRunner* sequenced_task_runner)
    194     : path_(filename),
    195       sequenced_task_runner_(sequenced_task_runner),
    196       original_task_runner_(base::MessageLoopProxy::current()),
    197       read_only_(false),
    198       initialized_(false),
    199       read_error_(PREF_READ_ERROR_NONE),
    200       weak_ptr_factory_(this) {}
    201 
    202 LevelDBPrefStore::~LevelDBPrefStore() {
    203   CommitPendingWrite();
    204   sequenced_task_runner_->DeleteSoon(FROM_HERE, serializer_.release());
    205 }
    206 
    207 bool LevelDBPrefStore::GetValue(const std::string& key,
    208                                 const base::Value** result) const {
    209   DCHECK(initialized_);
    210   const base::Value* tmp = NULL;
    211   if (!prefs_.GetValue(key, &tmp)) {
    212     return false;
    213   }
    214 
    215   if (result)
    216     *result = tmp;
    217   return true;
    218 }
    219 
    220 // Callers of GetMutableValue have to also call ReportValueChanged.
    221 bool LevelDBPrefStore::GetMutableValue(const std::string& key,
    222                                        base::Value** result) {
    223   DCHECK(initialized_);
    224   return prefs_.GetValue(key, result);
    225 }
    226 
    227 void LevelDBPrefStore::AddObserver(PrefStore::Observer* observer) {
    228   observers_.AddObserver(observer);
    229 }
    230 
    231 void LevelDBPrefStore::RemoveObserver(PrefStore::Observer* observer) {
    232   observers_.RemoveObserver(observer);
    233 }
    234 
    235 bool LevelDBPrefStore::HasObservers() const {
    236   return observers_.might_have_observers();
    237 }
    238 
    239 bool LevelDBPrefStore::IsInitializationComplete() const { return initialized_; }
    240 
    241 void LevelDBPrefStore::PersistFromUIThread() {
    242   if (read_only_)
    243     return;
    244   DCHECK(serializer_);
    245 
    246   scoped_ptr<std::set<std::string> > keys_to_delete(new std::set<std::string>);
    247   keys_to_delete->swap(keys_to_delete_);
    248 
    249   scoped_ptr<std::map<std::string, std::string> > keys_to_set(
    250       new std::map<std::string, std::string>);
    251   keys_to_set->swap(keys_to_set_);
    252 
    253   sequenced_task_runner_->PostTask(
    254       FROM_HERE,
    255       base::Bind(&LevelDBPrefStore::FileThreadSerializer::WriteToDatabase,
    256                  base::Unretained(serializer_.get()),
    257                  base::Owned(keys_to_set.release()),
    258                  base::Owned(keys_to_delete.release())));
    259 }
    260 
    261 void LevelDBPrefStore::ScheduleWrite() {
    262   if (!timer_.IsRunning()) {
    263     timer_.Start(FROM_HERE,
    264                  base::TimeDelta::FromSeconds(10),
    265                  this,
    266                  &LevelDBPrefStore::PersistFromUIThread);
    267   }
    268 }
    269 
    270 void LevelDBPrefStore::SetValue(const std::string& key, base::Value* value) {
    271   SetValueInternal(key, value, true /*notify*/);
    272 }
    273 
    274 void LevelDBPrefStore::SetValueSilently(const std::string& key,
    275                                         base::Value* value) {
    276   SetValueInternal(key, value, false /*notify*/);
    277 }
    278 
    279 static std::string Serialize(base::Value* value) {
    280   std::string value_string;
    281   JSONStringValueSerializer serializer(&value_string);
    282   bool serialized_ok = serializer.Serialize(*value);
    283   DCHECK(serialized_ok);
    284   return value_string;
    285 }
    286 
    287 void LevelDBPrefStore::SetValueInternal(const std::string& key,
    288                                         base::Value* value,
    289                                         bool notify) {
    290   DCHECK(initialized_);
    291   DCHECK(value);
    292   scoped_ptr<base::Value> new_value(value);
    293   base::Value* old_value = NULL;
    294   prefs_.GetValue(key, &old_value);
    295   if (!old_value || !value->Equals(old_value)) {
    296     std::string value_string = Serialize(value);
    297     prefs_.SetValue(key, new_value.release());
    298     MarkForInsertion(key, value_string);
    299     if (notify)
    300       NotifyObservers(key);
    301   }
    302 }
    303 
    304 void LevelDBPrefStore::RemoveValue(const std::string& key) {
    305   DCHECK(initialized_);
    306   if (prefs_.RemoveValue(key)) {
    307     MarkForDeletion(key);
    308     NotifyObservers(key);
    309   }
    310 }
    311 
    312 bool LevelDBPrefStore::ReadOnly() const { return read_only_; }
    313 
    314 PersistentPrefStore::PrefReadError LevelDBPrefStore::GetReadError() const {
    315   return read_error_;
    316 }
    317 
    318 PersistentPrefStore::PrefReadError LevelDBPrefStore::ReadPrefs() {
    319   DCHECK(!initialized_);
    320   scoped_ptr<ReadingResults> reading_results;
    321   if (path_.empty()) {
    322     reading_results.reset(new ReadingResults);
    323     reading_results->error = FILE_NOT_SPECIFIED;
    324   } else {
    325     reading_results = DoReading(path_);
    326   }
    327 
    328   PrefReadError error = IntToPrefReadError(reading_results->error);
    329   OnStorageRead(reading_results.Pass());
    330   return error;
    331 }
    332 
    333 void LevelDBPrefStore::ReadPrefsAsync(ReadErrorDelegate* error_delegate) {
    334   DCHECK_EQ(false, initialized_);
    335   error_delegate_.reset(error_delegate);
    336   if (path_.empty()) {
    337     scoped_ptr<ReadingResults> reading_results(new ReadingResults);
    338     reading_results->error = FILE_NOT_SPECIFIED;
    339     OnStorageRead(reading_results.Pass());
    340     return;
    341   }
    342   PostTaskAndReplyWithResult(sequenced_task_runner_,
    343                              FROM_HERE,
    344                              base::Bind(&LevelDBPrefStore::DoReading, path_),
    345                              base::Bind(&LevelDBPrefStore::OnStorageRead,
    346                                         weak_ptr_factory_.GetWeakPtr()));
    347 }
    348 
    349 void LevelDBPrefStore::CommitPendingWrite() {
    350   if (timer_.IsRunning()) {
    351     timer_.Stop();
    352     PersistFromUIThread();
    353   }
    354 }
    355 
    356 void LevelDBPrefStore::MarkForDeletion(const std::string& key) {
    357   if (read_only_)
    358     return;
    359   keys_to_delete_.insert(key);
    360   // Need to erase in case there's a set operation in the same batch that would
    361   // clobber this delete.
    362   keys_to_set_.erase(key);
    363   ScheduleWrite();
    364 }
    365 
    366 void LevelDBPrefStore::MarkForInsertion(const std::string& key,
    367                                         const std::string& value) {
    368   if (read_only_)
    369     return;
    370   keys_to_set_[key] = value;
    371   // Need to erase in case there's a delete operation in the same batch that
    372   // would clobber this set.
    373   keys_to_delete_.erase(key);
    374   ScheduleWrite();
    375 }
    376 
    377 void LevelDBPrefStore::ReportValueChanged(const std::string& key) {
    378   base::Value* new_value = NULL;
    379   bool contains_value = prefs_.GetValue(key, &new_value);
    380   DCHECK(contains_value);
    381   std::string value_string = Serialize(new_value);
    382   MarkForInsertion(key, value_string);
    383   NotifyObservers(key);
    384 }
    385 
    386 void LevelDBPrefStore::NotifyObservers(const std::string& key) {
    387   FOR_EACH_OBSERVER(PrefStore::Observer, observers_, OnPrefValueChanged(key));
    388 }
    389 
    390 void LevelDBPrefStore::OnStorageRead(
    391     scoped_ptr<LevelDBPrefStore::ReadingResults> reading_results) {
    392   UMA_HISTOGRAM_SPARSE_SLOWLY("LevelDBPrefStore.ReadErrors",
    393                               reading_results->error);
    394   read_error_ = IntToPrefReadError(reading_results->error);
    395 
    396   if (reading_results->no_dir) {
    397     FOR_EACH_OBSERVER(
    398         PrefStore::Observer, observers_, OnInitializationCompleted(false));
    399     return;
    400   }
    401 
    402   initialized_ = true;
    403 
    404   if (reading_results->db) {
    405     DCHECK(reading_results->value_map);
    406     serializer_.reset(new FileThreadSerializer(reading_results->db.Pass()));
    407     prefs_.Swap(reading_results->value_map.get());
    408   } else {
    409     read_only_ = true;
    410   }
    411 
    412   // TODO(dgrogan): Call pref_filter_->FilterOnLoad
    413 
    414   if (error_delegate_.get() && read_error_ != PREF_READ_ERROR_NONE)
    415     error_delegate_->OnError(read_error_);
    416 
    417   FOR_EACH_OBSERVER(
    418       PrefStore::Observer, observers_, OnInitializationCompleted(true));
    419 }
    420