Home | History | Annotate | Download | only in local
      1 // Copyright 2013 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/sync_file_system/local/local_file_change_tracker.h"
      6 
      7 #include <queue>
      8 
      9 #include "base/location.h"
     10 #include "base/logging.h"
     11 #include "base/sequenced_task_runner.h"
     12 #include "base/stl_util.h"
     13 #include "chrome/browser/sync_file_system/local/local_file_sync_status.h"
     14 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
     15 #include "storage/browser/fileapi/file_system_context.h"
     16 #include "storage/browser/fileapi/file_system_file_util.h"
     17 #include "storage/browser/fileapi/file_system_operation_context.h"
     18 #include "storage/common/fileapi/file_system_util.h"
     19 #include "third_party/leveldatabase/src/helpers/memenv/memenv.h"
     20 #include "third_party/leveldatabase/src/include/leveldb/db.h"
     21 #include "third_party/leveldatabase/src/include/leveldb/env.h"
     22 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
     23 
     24 using storage::FileSystemContext;
     25 using storage::FileSystemFileUtil;
     26 using storage::FileSystemOperationContext;
     27 using storage::FileSystemURL;
     28 using storage::FileSystemURLSet;
     29 
     30 namespace sync_file_system {
     31 
     32 namespace {
     33 const base::FilePath::CharType kDatabaseName[] =
     34     FILE_PATH_LITERAL("LocalFileChangeTracker");
     35 const char kMark[] = "d";
     36 }  // namespace
     37 
     38 // A database class that stores local file changes in a local database. This
     39 // object must be destructed on file_task_runner.
     40 class LocalFileChangeTracker::TrackerDB {
     41  public:
     42   TrackerDB(const base::FilePath& base_path,
     43             leveldb::Env* env_override);
     44 
     45   SyncStatusCode MarkDirty(const std::string& url);
     46   SyncStatusCode ClearDirty(const std::string& url);
     47   SyncStatusCode GetDirtyEntries(
     48       std::queue<FileSystemURL>* dirty_files);
     49   SyncStatusCode WriteBatch(scoped_ptr<leveldb::WriteBatch> batch);
     50 
     51  private:
     52   enum RecoveryOption {
     53     REPAIR_ON_CORRUPTION,
     54     FAIL_ON_CORRUPTION,
     55   };
     56 
     57   SyncStatusCode Init(RecoveryOption recovery_option);
     58   SyncStatusCode Repair(const std::string& db_path);
     59   void HandleError(const tracked_objects::Location& from_here,
     60                    const leveldb::Status& status);
     61 
     62   const base::FilePath base_path_;
     63   leveldb::Env* env_override_;
     64   scoped_ptr<leveldb::DB> db_;
     65   SyncStatusCode db_status_;
     66 
     67   DISALLOW_COPY_AND_ASSIGN(TrackerDB);
     68 };
     69 
     70 LocalFileChangeTracker::ChangeInfo::ChangeInfo() : change_seq(-1) {}
     71 LocalFileChangeTracker::ChangeInfo::~ChangeInfo() {}
     72 
     73 // LocalFileChangeTracker ------------------------------------------------------
     74 
     75 LocalFileChangeTracker::LocalFileChangeTracker(
     76     const base::FilePath& base_path,
     77     leveldb::Env* env_override,
     78     base::SequencedTaskRunner* file_task_runner)
     79     : initialized_(false),
     80       file_task_runner_(file_task_runner),
     81       tracker_db_(new TrackerDB(base_path, env_override)),
     82       current_change_seq_number_(0),
     83       num_changes_(0) {
     84 }
     85 
     86 LocalFileChangeTracker::~LocalFileChangeTracker() {
     87   DCHECK(file_task_runner_->RunsTasksOnCurrentThread());
     88   tracker_db_.reset();
     89 }
     90 
     91 void LocalFileChangeTracker::OnStartUpdate(const FileSystemURL& url) {
     92   DCHECK(file_task_runner_->RunsTasksOnCurrentThread());
     93   if (ContainsKey(changes_, url) || ContainsKey(demoted_changes_, url))
     94     return;
     95   // TODO(nhiroki): propagate the error code (see http://crbug.com/152127).
     96   MarkDirtyOnDatabase(url);
     97 }
     98 
     99 void LocalFileChangeTracker::OnEndUpdate(const FileSystemURL& url) {}
    100 
    101 void LocalFileChangeTracker::OnCreateFile(const FileSystemURL& url) {
    102   RecordChange(url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    103                                SYNC_FILE_TYPE_FILE));
    104 }
    105 
    106 void LocalFileChangeTracker::OnCreateFileFrom(const FileSystemURL& url,
    107                                               const FileSystemURL& src) {
    108   RecordChange(url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    109                                SYNC_FILE_TYPE_FILE));
    110 }
    111 
    112 void LocalFileChangeTracker::OnRemoveFile(const FileSystemURL& url) {
    113   RecordChange(url, FileChange(FileChange::FILE_CHANGE_DELETE,
    114                                SYNC_FILE_TYPE_FILE));
    115 }
    116 
    117 void LocalFileChangeTracker::OnModifyFile(const FileSystemURL& url) {
    118   RecordChange(url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    119                                SYNC_FILE_TYPE_FILE));
    120 }
    121 
    122 void LocalFileChangeTracker::OnCreateDirectory(const FileSystemURL& url) {
    123   RecordChange(url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    124                                SYNC_FILE_TYPE_DIRECTORY));
    125 }
    126 
    127 void LocalFileChangeTracker::OnRemoveDirectory(const FileSystemURL& url) {
    128   RecordChange(url, FileChange(FileChange::FILE_CHANGE_DELETE,
    129                                SYNC_FILE_TYPE_DIRECTORY));
    130 }
    131 
    132 void LocalFileChangeTracker::GetNextChangedURLs(
    133     std::deque<FileSystemURL>* urls, int max_urls) {
    134   DCHECK(file_task_runner_->RunsTasksOnCurrentThread());
    135   DCHECK(urls);
    136   urls->clear();
    137   // Mildly prioritizes the URLs that older changes and have not been updated
    138   // for a while.
    139   for (ChangeSeqMap::iterator iter = change_seqs_.begin();
    140        iter != change_seqs_.end() &&
    141        (max_urls == 0 || urls->size() < static_cast<size_t>(max_urls));
    142        ++iter) {
    143     urls->push_back(iter->second);
    144   }
    145 }
    146 
    147 void LocalFileChangeTracker::GetChangesForURL(
    148     const FileSystemURL& url, FileChangeList* changes) {
    149   DCHECK(file_task_runner_->RunsTasksOnCurrentThread());
    150   DCHECK(changes);
    151   changes->clear();
    152   FileChangeMap::iterator found = changes_.find(url);
    153   if (found == changes_.end()) {
    154     found = demoted_changes_.find(url);
    155     if (found == demoted_changes_.end())
    156       return;
    157   }
    158   *changes = found->second.change_list;
    159 }
    160 
    161 void LocalFileChangeTracker::ClearChangesForURL(const FileSystemURL& url) {
    162   DCHECK(file_task_runner_->RunsTasksOnCurrentThread());
    163   ClearDirtyOnDatabase(url);
    164   mirror_changes_.erase(url);
    165   demoted_changes_.erase(url);
    166   FileChangeMap::iterator found = changes_.find(url);
    167   if (found == changes_.end())
    168     return;
    169   change_seqs_.erase(found->second.change_seq);
    170   changes_.erase(found);
    171   UpdateNumChanges();
    172 }
    173 
    174 void LocalFileChangeTracker::CreateFreshMirrorForURL(
    175     const storage::FileSystemURL& url) {
    176   DCHECK(file_task_runner_->RunsTasksOnCurrentThread());
    177   DCHECK(!ContainsKey(mirror_changes_, url));
    178   mirror_changes_[url] = ChangeInfo();
    179 }
    180 
    181 void LocalFileChangeTracker::RemoveMirrorAndCommitChangesForURL(
    182     const storage::FileSystemURL& url) {
    183   DCHECK(file_task_runner_->RunsTasksOnCurrentThread());
    184   FileChangeMap::iterator found = mirror_changes_.find(url);
    185   if (found == mirror_changes_.end())
    186     return;
    187   mirror_changes_.erase(found);
    188 
    189   if (ContainsKey(changes_, url) || ContainsKey(demoted_changes_, url))
    190     MarkDirtyOnDatabase(url);
    191   else
    192     ClearDirtyOnDatabase(url);
    193   UpdateNumChanges();
    194 }
    195 
    196 void LocalFileChangeTracker::ResetToMirrorAndCommitChangesForURL(
    197     const storage::FileSystemURL& url) {
    198   DCHECK(file_task_runner_->RunsTasksOnCurrentThread());
    199   FileChangeMap::iterator found = mirror_changes_.find(url);
    200   if (found == mirror_changes_.end() || found->second.change_list.empty()) {
    201     ClearChangesForURL(url);
    202     return;
    203   }
    204   const ChangeInfo& info = found->second;
    205   if (ContainsKey(demoted_changes_, url)) {
    206     DCHECK(!ContainsKey(changes_, url));
    207     demoted_changes_[url] = info;
    208   } else {
    209     DCHECK(!ContainsKey(demoted_changes_, url));
    210     change_seqs_[info.change_seq] = url;
    211     changes_[url] = info;
    212   }
    213   RemoveMirrorAndCommitChangesForURL(url);
    214 }
    215 
    216 void LocalFileChangeTracker::DemoteChangesForURL(
    217     const storage::FileSystemURL& url) {
    218   DCHECK(file_task_runner_->RunsTasksOnCurrentThread());
    219 
    220   FileChangeMap::iterator found = changes_.find(url);
    221   if (found == changes_.end())
    222     return;
    223   DCHECK(!ContainsKey(demoted_changes_, url));
    224   change_seqs_.erase(found->second.change_seq);
    225   demoted_changes_.insert(*found);
    226   changes_.erase(found);
    227   UpdateNumChanges();
    228 }
    229 
    230 void LocalFileChangeTracker::PromoteDemotedChangesForURL(
    231     const storage::FileSystemURL& url) {
    232   DCHECK(file_task_runner_->RunsTasksOnCurrentThread());
    233 
    234   FileChangeMap::iterator iter = demoted_changes_.find(url);
    235   if (iter == demoted_changes_.end())
    236     return;
    237 
    238   FileChangeList::List change_list = iter->second.change_list.list();
    239   // Make sure that this URL is in no queues.
    240   DCHECK(!ContainsKey(change_seqs_, iter->second.change_seq));
    241   DCHECK(!ContainsKey(changes_, url));
    242 
    243   change_seqs_[iter->second.change_seq] = url;
    244   changes_.insert(*iter);
    245   demoted_changes_.erase(iter);
    246   UpdateNumChanges();
    247 }
    248 
    249 bool LocalFileChangeTracker::PromoteDemotedChanges() {
    250   DCHECK(file_task_runner_->RunsTasksOnCurrentThread());
    251   if (demoted_changes_.empty())
    252     return false;
    253   while (!demoted_changes_.empty()) {
    254     storage::FileSystemURL url = demoted_changes_.begin()->first;
    255     PromoteDemotedChangesForURL(url);
    256   }
    257   UpdateNumChanges();
    258   return true;
    259 }
    260 
    261 SyncStatusCode LocalFileChangeTracker::Initialize(
    262     FileSystemContext* file_system_context) {
    263   DCHECK(file_task_runner_->RunsTasksOnCurrentThread());
    264   DCHECK(!initialized_);
    265   DCHECK(file_system_context);
    266 
    267   SyncStatusCode status = CollectLastDirtyChanges(file_system_context);
    268   if (status == SYNC_STATUS_OK)
    269     initialized_ = true;
    270   return status;
    271 }
    272 
    273 void LocalFileChangeTracker::ResetForFileSystem(const GURL& origin,
    274                                                 storage::FileSystemType type) {
    275   DCHECK(file_task_runner_->RunsTasksOnCurrentThread());
    276   scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
    277   for (FileChangeMap::iterator iter = changes_.begin();
    278        iter != changes_.end();) {
    279     storage::FileSystemURL url = iter->first;
    280     int change_seq = iter->second.change_seq;
    281     // Advance |iter| before calling ResetForURL to avoid the iterator
    282     // invalidation in it.
    283     ++iter;
    284     if (url.origin() == origin && url.type() == type)
    285       ResetForURL(url, change_seq, batch.get());
    286   }
    287 
    288   for (FileChangeMap::iterator iter = demoted_changes_.begin();
    289        iter != demoted_changes_.end();) {
    290     storage::FileSystemURL url = iter->first;
    291     int change_seq = iter->second.change_seq;
    292     // Advance |iter| before calling ResetForURL to avoid the iterator
    293     // invalidation in it.
    294     ++iter;
    295     if (url.origin() == origin && url.type() == type)
    296       ResetForURL(url, change_seq, batch.get());
    297   }
    298 
    299   // Fail to apply batch to database wouldn't have critical effect, they'll be
    300   // just marked deleted on next relaunch.
    301   tracker_db_->WriteBatch(batch.Pass());
    302   UpdateNumChanges();
    303 }
    304 
    305 void LocalFileChangeTracker::UpdateNumChanges() {
    306   base::AutoLock lock(num_changes_lock_);
    307   num_changes_ = static_cast<int64>(change_seqs_.size());
    308 }
    309 
    310 void LocalFileChangeTracker::GetAllChangedURLs(FileSystemURLSet* urls) {
    311   DCHECK(file_task_runner_->RunsTasksOnCurrentThread());
    312   std::deque<FileSystemURL> url_deque;
    313   GetNextChangedURLs(&url_deque, 0);
    314   urls->clear();
    315   urls->insert(url_deque.begin(), url_deque.end());
    316 }
    317 
    318 void LocalFileChangeTracker::DropAllChanges() {
    319   DCHECK(file_task_runner_->RunsTasksOnCurrentThread());
    320   changes_.clear();
    321   change_seqs_.clear();
    322   mirror_changes_.clear();
    323   UpdateNumChanges();
    324 }
    325 
    326 SyncStatusCode LocalFileChangeTracker::MarkDirtyOnDatabase(
    327     const FileSystemURL& url) {
    328   std::string serialized_url;
    329   if (!SerializeSyncableFileSystemURL(url, &serialized_url))
    330     return SYNC_FILE_ERROR_INVALID_URL;
    331 
    332   return tracker_db_->MarkDirty(serialized_url);
    333 }
    334 
    335 SyncStatusCode LocalFileChangeTracker::ClearDirtyOnDatabase(
    336     const FileSystemURL& url) {
    337   DCHECK(file_task_runner_->RunsTasksOnCurrentThread());
    338   std::string serialized_url;
    339   if (!SerializeSyncableFileSystemURL(url, &serialized_url))
    340     return SYNC_FILE_ERROR_INVALID_URL;
    341 
    342   return tracker_db_->ClearDirty(serialized_url);
    343 }
    344 
    345 SyncStatusCode LocalFileChangeTracker::CollectLastDirtyChanges(
    346     FileSystemContext* file_system_context) {
    347   DCHECK(file_task_runner_->RunsTasksOnCurrentThread());
    348 
    349   std::queue<FileSystemURL> dirty_files;
    350   const SyncStatusCode status = tracker_db_->GetDirtyEntries(&dirty_files);
    351   if (status != SYNC_STATUS_OK)
    352     return status;
    353 
    354   FileSystemFileUtil* file_util =
    355       file_system_context->sandbox_delegate()->sync_file_util();
    356   DCHECK(file_util);
    357   scoped_ptr<FileSystemOperationContext> context(
    358       new FileSystemOperationContext(file_system_context));
    359 
    360   base::File::Info file_info;
    361   base::FilePath platform_path;
    362 
    363   while (!dirty_files.empty()) {
    364     const FileSystemURL url = dirty_files.front();
    365     dirty_files.pop();
    366     DCHECK_EQ(url.type(), storage::kFileSystemTypeSyncable);
    367 
    368     switch (file_util->GetFileInfo(context.get(), url,
    369                                    &file_info, &platform_path)) {
    370       case base::File::FILE_OK: {
    371         if (!file_info.is_directory) {
    372           RecordChange(url, FileChange(FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    373                                        SYNC_FILE_TYPE_FILE));
    374           break;
    375         }
    376 
    377         RecordChange(url, FileChange(
    378             FileChange::FILE_CHANGE_ADD_OR_UPDATE,
    379             SYNC_FILE_TYPE_DIRECTORY));
    380 
    381         // Push files and directories in this directory into |dirty_files|.
    382         scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator> enumerator(
    383             file_util->CreateFileEnumerator(context.get(), url));
    384         base::FilePath path_each;
    385         while (!(path_each = enumerator->Next()).empty()) {
    386           dirty_files.push(CreateSyncableFileSystemURL(
    387                   url.origin(), path_each));
    388         }
    389         break;
    390       }
    391       case base::File::FILE_ERROR_NOT_FOUND: {
    392         // File represented by |url| has already been deleted. Since we cannot
    393         // figure out if this file was directory or not from the URL, file
    394         // type is treated as SYNC_FILE_TYPE_UNKNOWN.
    395         //
    396         // NOTE: Directory to have been reverted (that is, ADD -> DELETE) is
    397         // also treated as FILE_CHANGE_DELETE.
    398         RecordChange(url, FileChange(FileChange::FILE_CHANGE_DELETE,
    399                                      SYNC_FILE_TYPE_UNKNOWN));
    400         break;
    401       }
    402       case base::File::FILE_ERROR_FAILED:
    403       default:
    404         // TODO(nhiroki): handle file access error (http://crbug.com/155251).
    405         LOG(WARNING) << "Failed to access local file.";
    406         break;
    407     }
    408   }
    409   return SYNC_STATUS_OK;
    410 }
    411 
    412 void LocalFileChangeTracker::RecordChange(
    413     const FileSystemURL& url, const FileChange& change) {
    414   DCHECK(file_task_runner_->RunsTasksOnCurrentThread());
    415   int change_seq = current_change_seq_number_++;
    416   if (ContainsKey(demoted_changes_, url)) {
    417     RecordChangeToChangeMaps(url, change, change_seq,
    418                              &demoted_changes_, NULL);
    419   } else {
    420     RecordChangeToChangeMaps(url, change, change_seq, &changes_, &change_seqs_);
    421   }
    422   if (ContainsKey(mirror_changes_, url))
    423     RecordChangeToChangeMaps(url, change, change_seq, &mirror_changes_, NULL);
    424   UpdateNumChanges();
    425 }
    426 
    427 // static
    428 void LocalFileChangeTracker::RecordChangeToChangeMaps(
    429     const FileSystemURL& url,
    430     const FileChange& change,
    431     int new_change_seq,
    432     FileChangeMap* changes,
    433     ChangeSeqMap* change_seqs) {
    434   ChangeInfo& info = (*changes)[url];
    435   if (info.change_seq >= 0 && change_seqs)
    436     change_seqs->erase(info.change_seq);
    437   info.change_list.Update(change);
    438   if (info.change_list.empty()) {
    439     changes->erase(url);
    440     return;
    441   }
    442   info.change_seq = new_change_seq;
    443   if (change_seqs)
    444     (*change_seqs)[info.change_seq] = url;
    445 }
    446 
    447 void LocalFileChangeTracker::ResetForURL(const storage::FileSystemURL& url,
    448                                          int change_seq,
    449                                          leveldb::WriteBatch* batch) {
    450   mirror_changes_.erase(url);
    451   demoted_changes_.erase(url);
    452   change_seqs_.erase(change_seq);
    453   changes_.erase(url);
    454 
    455   std::string serialized_url;
    456   if (!SerializeSyncableFileSystemURL(url, &serialized_url)) {
    457     NOTREACHED() << "Failed to serialize: " << url.DebugString();
    458     return;
    459   }
    460   batch->Delete(serialized_url);
    461 }
    462 
    463 // TrackerDB -------------------------------------------------------------------
    464 
    465 LocalFileChangeTracker::TrackerDB::TrackerDB(const base::FilePath& base_path,
    466                                              leveldb::Env* env_override)
    467   : base_path_(base_path),
    468     env_override_(env_override),
    469     db_status_(SYNC_STATUS_OK) {}
    470 
    471 SyncStatusCode LocalFileChangeTracker::TrackerDB::Init(
    472     RecoveryOption recovery_option) {
    473   if (db_.get() && db_status_ == SYNC_STATUS_OK)
    474     return SYNC_STATUS_OK;
    475 
    476   std::string path =
    477       storage::FilePathToString(base_path_.Append(kDatabaseName));
    478   leveldb::Options options;
    479   options.max_open_files = 0;  // Use minimum.
    480   options.create_if_missing = true;
    481   if (env_override_)
    482     options.env = env_override_;
    483   leveldb::DB* db;
    484   leveldb::Status status = leveldb::DB::Open(options, path, &db);
    485   if (status.ok()) {
    486     db_.reset(db);
    487     return SYNC_STATUS_OK;
    488   }
    489 
    490   HandleError(FROM_HERE, status);
    491   if (!status.IsCorruption())
    492     return LevelDBStatusToSyncStatusCode(status);
    493 
    494   // Try to repair the corrupted DB.
    495   switch (recovery_option) {
    496     case FAIL_ON_CORRUPTION:
    497       return SYNC_DATABASE_ERROR_CORRUPTION;
    498     case REPAIR_ON_CORRUPTION:
    499       return Repair(path);
    500   }
    501   NOTREACHED();
    502   return SYNC_DATABASE_ERROR_FAILED;
    503 }
    504 
    505 SyncStatusCode LocalFileChangeTracker::TrackerDB::Repair(
    506     const std::string& db_path) {
    507   DCHECK(!db_.get());
    508   LOG(WARNING) << "Attempting to repair TrackerDB.";
    509 
    510   leveldb::Options options;
    511   options.max_open_files = 0;  // Use minimum.
    512   if (leveldb::RepairDB(db_path, options).ok() &&
    513       Init(FAIL_ON_CORRUPTION) == SYNC_STATUS_OK) {
    514     // TODO(nhiroki): perform some consistency checks between TrackerDB and
    515     // syncable file system.
    516     LOG(WARNING) << "Repairing TrackerDB completed.";
    517     return SYNC_STATUS_OK;
    518   }
    519 
    520   LOG(WARNING) << "Failed to repair TrackerDB.";
    521   return SYNC_DATABASE_ERROR_CORRUPTION;
    522 }
    523 
    524 // TODO(nhiroki): factor out the common methods into somewhere else.
    525 void LocalFileChangeTracker::TrackerDB::HandleError(
    526     const tracked_objects::Location& from_here,
    527     const leveldb::Status& status) {
    528   LOG(ERROR) << "LocalFileChangeTracker::TrackerDB failed at: "
    529              << from_here.ToString() << " with error: " << status.ToString();
    530 }
    531 
    532 SyncStatusCode LocalFileChangeTracker::TrackerDB::MarkDirty(
    533     const std::string& url) {
    534   if (db_status_ != SYNC_STATUS_OK)
    535     return db_status_;
    536 
    537   db_status_ = Init(REPAIR_ON_CORRUPTION);
    538   if (db_status_ != SYNC_STATUS_OK) {
    539     db_.reset();
    540     return db_status_;
    541   }
    542 
    543   leveldb::Status status = db_->Put(leveldb::WriteOptions(), url, kMark);
    544   if (!status.ok()) {
    545     HandleError(FROM_HERE, status);
    546     db_status_ = LevelDBStatusToSyncStatusCode(status);
    547     db_.reset();
    548     return db_status_;
    549   }
    550   return SYNC_STATUS_OK;
    551 }
    552 
    553 SyncStatusCode LocalFileChangeTracker::TrackerDB::ClearDirty(
    554     const std::string& url) {
    555   if (db_status_ != SYNC_STATUS_OK)
    556     return db_status_;
    557 
    558   // Should not reach here before initializing the database. The database should
    559   // be cleared after read, and should be initialized during read if
    560   // uninitialized.
    561   DCHECK(db_.get());
    562 
    563   leveldb::Status status = db_->Delete(leveldb::WriteOptions(), url);
    564   if (!status.ok() && !status.IsNotFound()) {
    565     HandleError(FROM_HERE, status);
    566     db_status_ = LevelDBStatusToSyncStatusCode(status);
    567     db_.reset();
    568     return db_status_;
    569   }
    570   return SYNC_STATUS_OK;
    571 }
    572 
    573 SyncStatusCode LocalFileChangeTracker::TrackerDB::GetDirtyEntries(
    574     std::queue<FileSystemURL>* dirty_files) {
    575   if (db_status_ != SYNC_STATUS_OK)
    576     return db_status_;
    577 
    578   db_status_ = Init(REPAIR_ON_CORRUPTION);
    579   if (db_status_ != SYNC_STATUS_OK) {
    580     db_.reset();
    581     return db_status_;
    582   }
    583 
    584   scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(leveldb::ReadOptions()));
    585   iter->SeekToFirst();
    586   FileSystemURL url;
    587   while (iter->Valid()) {
    588     if (!DeserializeSyncableFileSystemURL(iter->key().ToString(), &url)) {
    589       LOG(WARNING) << "Failed to deserialize an URL. "
    590                    << "TrackerDB might be corrupted.";
    591       db_status_ = SYNC_DATABASE_ERROR_CORRUPTION;
    592       db_.reset();
    593       return db_status_;
    594     }
    595     dirty_files->push(url);
    596     iter->Next();
    597   }
    598   return SYNC_STATUS_OK;
    599 }
    600 
    601 SyncStatusCode LocalFileChangeTracker::TrackerDB::WriteBatch(
    602     scoped_ptr<leveldb::WriteBatch> batch) {
    603   if (db_status_ != SYNC_STATUS_OK)
    604     return db_status_;
    605 
    606   leveldb::Status status = db_->Write(leveldb::WriteOptions(), batch.get());
    607   if (!status.ok() && !status.IsNotFound()) {
    608     HandleError(FROM_HERE, status);
    609     db_status_ = LevelDBStatusToSyncStatusCode(status);
    610     db_.reset();
    611     return db_status_;
    612   }
    613   return SYNC_STATUS_OK;
    614 }
    615 
    616 }  // namespace sync_file_system
    617