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