Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / sync_file_system / drive_backend / metadata_database_index_on_disk.cc
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/sync_file_system/drive_backend/metadata_database_index_on_disk.h"
6
7 #include "base/format_macros.h"
8 #include "base/logging.h"
9 #include "base/strings/string_number_conversions.h"
10 #include "base/strings/string_util.h"
11 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_constants.h"
12 #include "chrome/browser/sync_file_system/drive_backend/drive_backend_util.h"
13 #include "chrome/browser/sync_file_system/drive_backend/leveldb_wrapper.h"
14 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h"
15 #include "chrome/browser/sync_file_system/logger.h"
16 #include "third_party/leveldatabase/src/include/leveldb/status.h"
17
18 // LevelDB database schema
19 // =======================
20 //
21 // NOTE
22 // - Entries are sorted by keys.
23 // - int64 value is serialized as a string by base::Int64ToString().
24 // - ServiceMetadata, FileMetadata, and FileTracker values are serialized
25 //   as a string by SerializeToString() of protocol buffers.
26 //
27 // Version 4:
28 //   # Version of this schema
29 //   key: "VERSION"
30 //   value: "4"
31 //
32 //   # Metadata of the SyncFS service (compatible with version 3)
33 //   key: "SERVICE"
34 //   value: <ServiceMetadata 'service_metadata'>
35 //
36 //   # Metadata of remote files (compatible with version 3)
37 //   key: "FILE: " + <string 'file_id'>
38 //   value: <FileMetadata 'metadata'>
39 //
40 //   # Trackers of remote file updates (compatible with version 3)
41 //   key: "TRACKER: " + <int64 'tracker_id'>
42 //   value: <FileTracker 'tracker'>
43 //
44 //   # Index from App ID to the tracker ID
45 //   key: "APP_ROOT: " + <string 'app_id'>
46 //   value: <int64 'app_root_tracker_id'>
47 //
48 //   # Index from file ID to the active tracker ID
49 //   key: "ACTIVE_FILE: " + <string 'file_id'>
50 //   value: <int64 'active_tracker_id'>
51 //
52 //   # Index from file ID to a tracker ID
53 //   key: "TRACKER_FILE: " + <string 'file_id'> + '\x00' + <int64 'tracker_id'>
54 //   value: <empty>
55 //
56 //   # Tracker IDs; a file metadata linked to multiple tracker IDs.
57 //   key: "MULTI_FILE: " + <int64 'tracker_id'>
58 //   value: <empty>
59 //
60 //   # Index from the parent tracker ID and the title to the active tracker ID
61 //   key: "ACTIVE_PATH: " + <int64 'parent_tracker_id'> +
62 //        '\x00' + <string 'title'>
63 //   value: <int64 'active_tracker_id'>
64 //
65 //   # Index from the parent tracker ID and the title to a tracker ID
66 //   key: "TRACKER_PATH: " + <int64 'parent_tracker_id'> +
67 //        '\x00' + <string 'title'> + '\x00' + <int64 'tracker_id'>
68 //   value: <empty>
69 //
70 //   # Tracker IDs; a parent tracker ID and a title figure multiple tracker IDs
71 //   key: "MULTI_PATH: " + <int64 'tracker_id'>
72 //   value: <empty>
73 //
74 //   # Dirty tracker IDs
75 //   key: "DIRTY: " + <int64 'dirty_tracker_id'>
76 //   value: <empty>
77 //
78 //   # Demoted dirty tracker IDs
79 //   key: "DEMOTED_DIRTY: " + <int64 'demoted_dirty_tracker_id'>
80 //   value: <empty>
81 //
82 //   # Timestamp when the last validation ran
83 //   key: "LAST_VALID"
84 //   value: <time_t 'last_valid_time'>
85
86 namespace sync_file_system {
87 namespace drive_backend {
88
89 namespace {
90
91 std::string GenerateAppRootIDByAppIDKey(const std::string& app_id) {
92   return kAppRootIDByAppIDKeyPrefix + app_id;
93 }
94
95 std::string GenerateActiveTrackerIDByFileIDKey(const std::string& file_id) {
96   return kActiveTrackerIDByFileIDKeyPrefix + file_id;
97 }
98
99 std::string GenerateTrackerIDByFileIDKeyPrefix(const std::string& file_id) {
100   std::ostringstream oss;
101   oss << kTrackerIDByFileIDKeyPrefix << file_id << '\0';
102   return oss.str();
103 }
104
105 std::string GenerateMultiTrackerKey(const std::string& file_id) {
106   return kMultiTrackerByFileIDKeyPrefix + file_id;
107 }
108
109 std::string GenerateActiveTrackerIDByParentAndTitleKey(
110     int64 parent_id, const std::string& title) {
111   std::ostringstream oss;
112   oss << kActiveTrackerIDByParentAndTitleKeyPrefix << parent_id
113       << '\0' << title;
114   return oss.str();
115 }
116
117 std::string GenerateTrackerIDByParentAndTitleKeyPrefix(
118     int64 parent_id, const std::string& title) {
119   std::ostringstream oss;
120   oss << kTrackerIDByParentAndTitleKeyPrefix << parent_id << '\0'
121       << title << '\0';
122   return oss.str();
123 }
124
125 std::string GenerateTrackerIDsByParentIDKeyPrefix(int64 parent_id) {
126   std::ostringstream oss;
127   oss << kTrackerIDByParentAndTitleKeyPrefix << parent_id << '\0';
128   return oss.str();
129 }
130
131 std::string GenerateMultiBackingParentAndTitleKey(
132     int64 parent_id, const std::string& title) {
133   std::ostringstream oss;
134   oss << kMultiBackingParentAndTitleKeyPrefix << parent_id << '\0'
135       << title;
136   return oss.str();
137 }
138
139 std::string GenerateDirtyIDKey(int64 tracker_id) {
140   return kDirtyIDKeyPrefix + base::Int64ToString(tracker_id);
141 }
142
143 std::string GenerateDemotedDirtyIDKey(int64 tracker_id) {
144   return kDemotedDirtyIDKeyPrefix + base::Int64ToString(tracker_id);
145 }
146
147 void RemoveUnreachableItems(LevelDBWrapper* db, int64 sync_root_tracker_id) {
148   DCHECK(db);
149
150   typedef std::map<int64, std::set<int64> > ChildTrackersByParent;
151   ChildTrackersByParent trackers_by_parent;
152   {
153     // Set up links from parent tracker to child trackers.
154     std::set<int64> inactive_trackers;
155     scoped_ptr<LevelDBWrapper::Iterator> itr = db->NewIterator();
156     for (itr->Seek(kFileTrackerKeyPrefix); itr->Valid(); itr->Next()) {
157       if (!RemovePrefix(itr->key().ToString(), kFileTrackerKeyPrefix, nullptr))
158         break;
159
160       scoped_ptr<FileTracker> tracker(new FileTracker);
161       if (!tracker->ParseFromString(itr->value().ToString())) {
162         util::Log(logging::LOG_WARNING, FROM_HERE,
163                   "Failed to parse a Tracker");
164         continue;
165       }
166
167       int64 parent_tracker_id = tracker->parent_tracker_id();
168       int64 tracker_id = tracker->tracker_id();
169       trackers_by_parent[parent_tracker_id].insert(tracker_id);
170       if (!tracker->active())
171         inactive_trackers.insert(tracker_id);
172     }
173
174     // Drop links from inactive trackers.
175     for (std::set<int64>::iterator iter = inactive_trackers.begin();
176          iter != inactive_trackers.end(); ++iter) {
177       trackers_by_parent.erase(*iter);
178     }
179   }
180
181   // Traverse tracker tree from sync-root.
182   std::set<int64> visited_trackers;
183   {
184     std::vector<int64> pending;
185     if (sync_root_tracker_id != kInvalidTrackerID)
186       pending.push_back(sync_root_tracker_id);
187
188     while (!pending.empty()) {
189       int64 tracker_id = pending.back();
190       DCHECK_NE(kInvalidTrackerID, tracker_id);
191       pending.pop_back();
192
193       if (!visited_trackers.insert(tracker_id).second) {
194         NOTREACHED();
195         continue;
196       }
197
198       AppendContents(
199           LookUpMap(trackers_by_parent, tracker_id, std::set<int64>()),
200           &pending);
201     }
202   }
203
204   // Delete all unreachable trackers, and list all |file_id| referred by
205   // remained trackers.
206   base::hash_set<std::string> referred_file_ids;
207   {
208     scoped_ptr<LevelDBWrapper::Iterator> itr = db->NewIterator();
209     for (itr->Seek(kFileTrackerKeyPrefix); itr->Valid(); itr->Next()) {
210       if (!RemovePrefix(itr->key().ToString(), kFileTrackerKeyPrefix, nullptr))
211         break;
212
213       scoped_ptr<FileTracker> tracker(new FileTracker);
214       if (!tracker->ParseFromString(itr->value().ToString())) {
215         util::Log(logging::LOG_WARNING, FROM_HERE,
216                   "Failed to parse a Tracker");
217         continue;
218       }
219
220       if (ContainsKey(visited_trackers, tracker->tracker_id())) {
221         referred_file_ids.insert(tracker->file_id());
222       } else {
223         PutFileTrackerDeletionToDB(tracker->tracker_id(), db);
224       }
225     }
226   }
227
228   // Delete all unreferred metadata.
229   {
230     scoped_ptr<LevelDBWrapper::Iterator> itr = db->NewIterator();
231     for (itr->Seek(kFileMetadataKeyPrefix); itr->Valid(); itr->Next()) {
232       if (!RemovePrefix(itr->key().ToString(), kFileMetadataKeyPrefix, nullptr))
233         break;
234
235       scoped_ptr<FileMetadata> metadata(new FileMetadata);
236       if (!metadata->ParseFromString(itr->value().ToString())) {
237         util::Log(logging::LOG_WARNING, FROM_HERE,
238                   "Failed to parse a Tracker");
239         continue;
240       }
241
242       if (!ContainsKey(referred_file_ids, metadata->file_id()))
243         PutFileMetadataDeletionToDB(metadata->file_id(), db);
244     }
245   }
246 }
247
248 }  // namespace
249
250 // static
251 scoped_ptr<MetadataDatabaseIndexOnDisk>
252 MetadataDatabaseIndexOnDisk::Create(LevelDBWrapper* db) {
253   DCHECK(db);
254
255   scoped_ptr<ServiceMetadata> service_metadata = InitializeServiceMetadata(db);
256   if (!service_metadata)
257     return nullptr;
258
259   PutVersionToDB(kDatabaseOnDiskVersion, db);
260   RemoveUnreachableItems(db, service_metadata->sync_root_tracker_id());
261   scoped_ptr<MetadataDatabaseIndexOnDisk>
262       index(new MetadataDatabaseIndexOnDisk(db));
263
264   return index.Pass();
265 }
266
267 MetadataDatabaseIndexOnDisk::~MetadataDatabaseIndexOnDisk() {}
268
269 bool MetadataDatabaseIndexOnDisk::GetFileMetadata(
270     const std::string& file_id, FileMetadata* metadata) const {
271   const std::string key = kFileMetadataKeyPrefix + file_id;
272   std::string value;
273   leveldb::Status status = db_->Get(key, &value);
274
275   if (status.IsNotFound())
276     return false;
277
278   if (!status.ok()) {
279     util::Log(logging::LOG_WARNING, FROM_HERE,
280               "LevelDB error (%s) in getting FileMetadata for ID: %s",
281               status.ToString().c_str(),
282               file_id.c_str());
283     return false;
284   }
285
286   FileMetadata tmp_metadata;
287   if (!tmp_metadata.ParseFromString(value)) {
288     util::Log(logging::LOG_WARNING, FROM_HERE,
289               "Failed to parse a FileMetadata for ID: %s",
290               file_id.c_str());
291     return false;
292   }
293   if (metadata)
294     metadata->CopyFrom(tmp_metadata);
295
296   return true;
297 }
298
299 bool MetadataDatabaseIndexOnDisk::GetFileTracker(
300     int64 tracker_id, FileTracker* tracker) const {
301   const std::string key =
302       kFileTrackerKeyPrefix + base::Int64ToString(tracker_id);
303   std::string value;
304   leveldb::Status status = db_->Get(key, &value);
305
306   if (status.IsNotFound())
307     return false;
308
309   if (!status.ok()) {
310     util::Log(logging::LOG_WARNING, FROM_HERE,
311               "LevelDB error (%s) in getting FileTracker for ID: %" PRId64,
312               status.ToString().c_str(),
313               tracker_id);
314     return false;
315   }
316
317   FileTracker tmp_tracker;
318   if (!tmp_tracker.ParseFromString(value)) {
319     util::Log(logging::LOG_WARNING, FROM_HERE,
320               "Failed to parse a Tracker for ID: %" PRId64,
321               tracker_id);
322     return false;
323   }
324   if (tracker)
325     tracker->CopyFrom(tmp_tracker);
326
327   return true;
328 }
329
330 void MetadataDatabaseIndexOnDisk::StoreFileMetadata(
331     scoped_ptr<FileMetadata> metadata) {
332   DCHECK(metadata);
333   PutFileMetadataToDB(*metadata, db_);
334 }
335
336 void MetadataDatabaseIndexOnDisk::StoreFileTracker(
337     scoped_ptr<FileTracker> tracker) {
338   DCHECK(tracker);
339
340   int64 tracker_id = tracker->tracker_id();
341   FileTracker old_tracker;
342   if (!GetFileTracker(tracker_id, &old_tracker)) {
343     DVLOG(3) << "Adding new tracker: " << tracker->tracker_id()
344              << " " << GetTrackerTitle(*tracker);
345     AddToAppIDIndex(*tracker);
346     AddToFileIDIndexes(*tracker);
347     AddToPathIndexes(*tracker);
348     AddToDirtyTrackerIndexes(*tracker);
349   } else {
350     DVLOG(3) << "Updating tracker: " << tracker->tracker_id()
351              << " " << GetTrackerTitle(*tracker);
352     UpdateInAppIDIndex(old_tracker, *tracker);
353     UpdateInFileIDIndexes(old_tracker, *tracker);
354     UpdateInPathIndexes(old_tracker, *tracker);
355     UpdateInDirtyTrackerIndexes(old_tracker, *tracker);
356   }
357
358   PutFileTrackerToDB(*tracker, db_);
359 }
360
361 void MetadataDatabaseIndexOnDisk::RemoveFileMetadata(
362     const std::string& file_id) {
363   PutFileMetadataDeletionToDB(file_id, db_);
364 }
365
366 void MetadataDatabaseIndexOnDisk::RemoveFileTracker(int64 tracker_id) {
367   FileTracker tracker;
368   if (!GetFileTracker(tracker_id, &tracker)) {
369     NOTREACHED();
370     return;
371   }
372
373   DVLOG(1) << "Removing tracker: "
374            << tracker.tracker_id() << " " << GetTrackerTitle(tracker);
375   RemoveFromAppIDIndex(tracker);
376   RemoveFromFileIDIndexes(tracker);
377   RemoveFromPathIndexes(tracker);
378   RemoveFromDirtyTrackerIndexes(tracker);
379
380   PutFileTrackerDeletionToDB(tracker_id, db_);
381 }
382
383 TrackerIDSet MetadataDatabaseIndexOnDisk::GetFileTrackerIDsByFileID(
384     const std::string& file_id) const {
385   return GetTrackerIDSetByPrefix(
386       GenerateActiveTrackerIDByFileIDKey(file_id),
387       GenerateTrackerIDByFileIDKeyPrefix(file_id));
388 }
389
390 int64 MetadataDatabaseIndexOnDisk::GetAppRootTracker(
391     const std::string& app_id) const {
392   const std::string key = GenerateAppRootIDByAppIDKey(app_id);
393   std::string value;
394   leveldb::Status status = db_->Get(key, &value);
395
396   if (status.IsNotFound())
397     return kInvalidTrackerID;
398
399   if (!status.ok()) {
400     util::Log(logging::LOG_WARNING, FROM_HERE,
401               "LevelDB error (%s) in getting AppRoot for AppID: %s",
402               status.ToString().c_str(),
403               app_id.c_str());
404     return kInvalidTrackerID;
405   }
406
407   int64 root_id;
408   if (!base::StringToInt64(value, &root_id)) {
409     util::Log(logging::LOG_WARNING, FROM_HERE,
410               "Failed to parse a root ID (%s) for an App ID: %s",
411               value.c_str(),
412               app_id.c_str());
413     return kInvalidTrackerID;
414   }
415
416   return root_id;
417 }
418
419 TrackerIDSet MetadataDatabaseIndexOnDisk::GetFileTrackerIDsByParentAndTitle(
420     int64 parent_tracker_id, const std::string& title) const {
421   return GetTrackerIDSetByPrefix(
422       GenerateActiveTrackerIDByParentAndTitleKey(parent_tracker_id, title),
423       GenerateTrackerIDByParentAndTitleKeyPrefix(parent_tracker_id, title));
424 }
425
426 std::vector<int64> MetadataDatabaseIndexOnDisk::GetFileTrackerIDsByParent(
427     int64 parent_id) const {
428   std::vector<int64> result;
429
430   const std::string prefix = GenerateTrackerIDsByParentIDKeyPrefix(parent_id);
431   scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
432   for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
433     const std::string& key(itr->key().ToString());
434     std::string title_and_id;
435     if (!RemovePrefix(key, prefix, &title_and_id))
436       break;
437
438     size_t pos = title_and_id.rfind('\0');
439     DCHECK(pos != std::string::npos);
440
441     int64 tracker_id;
442     if (!base::StringToInt64(title_and_id.substr(pos + 1), &tracker_id))
443       continue;
444     result.push_back(tracker_id);
445   }
446   return result;
447 }
448
449 std::string MetadataDatabaseIndexOnDisk::PickMultiTrackerFileID() const {
450   scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
451   itr->Seek(kMultiTrackerByFileIDKeyPrefix);
452   if (!itr->Valid())
453     return std::string();
454
455   std::string file_id;
456   if (!RemovePrefix(itr->key().ToString(),
457                     kMultiTrackerByFileIDKeyPrefix, &file_id))
458     return std::string();
459
460   return file_id;
461 }
462
463 ParentIDAndTitle MetadataDatabaseIndexOnDisk::PickMultiBackingFilePath() const {
464   scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
465   itr->Seek(kMultiBackingParentAndTitleKeyPrefix);
466   if (!itr->Valid())
467     return ParentIDAndTitle();
468
469   std::string value;
470   if (!RemovePrefix(itr->key().ToString(),
471                     kMultiBackingParentAndTitleKeyPrefix, &value))
472     return ParentIDAndTitle();
473
474   size_t pos = value.find('\0');  // '\0' is a separator.
475   if (pos == std::string::npos)
476     return ParentIDAndTitle();
477
478   int64 parent_id;
479   return base::StringToInt64(value.substr(0, pos), &parent_id) ?
480       ParentIDAndTitle(parent_id, value.substr(pos + 1)) : ParentIDAndTitle();
481 }
482
483 int64 MetadataDatabaseIndexOnDisk::PickDirtyTracker() const {
484   scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
485   itr->Seek(kDirtyIDKeyPrefix);
486   if (!itr->Valid())
487     return kInvalidTrackerID;
488
489   std::string id_str;
490   if (!RemovePrefix(itr->key().ToString(), kDirtyIDKeyPrefix, &id_str))
491     return kInvalidTrackerID;
492
493   int64 tracker_id;
494   if (!base::StringToInt64(id_str, &tracker_id))
495     return kInvalidTrackerID;
496
497   return tracker_id;
498 }
499
500 void MetadataDatabaseIndexOnDisk::DemoteDirtyTracker(int64 tracker_id) {
501   const std::string key = GenerateDirtyIDKey(tracker_id);
502
503   std::string value;
504   leveldb::Status status = db_->Get(key, &value);
505   if (status.IsNotFound())
506     return;
507   if (!status.ok()) {
508     util::Log(logging::LOG_WARNING, FROM_HERE,
509               "LevelDB error (%s) in getting a dirty tracker for ID: %" PRId64,
510               status.ToString().c_str(),
511               tracker_id);
512     return;
513   }
514
515   db_->Delete(key);
516   db_->Put(GenerateDemotedDirtyIDKey(tracker_id), std::string());
517   --num_dirty_trackers_;
518 }
519
520 bool MetadataDatabaseIndexOnDisk::HasDemotedDirtyTracker() const {
521   scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
522   itr->Seek(kDemotedDirtyIDKeyPrefix);
523   if (!itr->Valid())
524     return false;
525   return StartsWithASCII(itr->key().ToString(), kDemotedDirtyIDKeyPrefix, true);
526 }
527
528 bool MetadataDatabaseIndexOnDisk::IsDemotedDirtyTracker(
529     int64 tracker_id) const {
530   return DBHasKey(GenerateDemotedDirtyIDKey(tracker_id));
531 }
532
533 void MetadataDatabaseIndexOnDisk::PromoteDemotedDirtyTracker(int64 tracker_id) {
534   std::string demoted_key = GenerateDemotedDirtyIDKey(tracker_id);
535
536   std::string empty;
537   if (db_->Get(demoted_key, &empty).ok()) {
538     db_->Delete(demoted_key);
539     db_->Put(GenerateDirtyIDKey(tracker_id), std::string());
540     ++num_dirty_trackers_;
541   }
542 }
543
544 bool MetadataDatabaseIndexOnDisk::PromoteDemotedDirtyTrackers() {
545   bool promoted = false;
546   scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
547   for (itr->Seek(kDemotedDirtyIDKeyPrefix); itr->Valid(); itr->Next()) {
548     std::string id_str;
549     if (!RemovePrefix(itr->key().ToString(), kDemotedDirtyIDKeyPrefix, &id_str))
550       break;
551
552     int64 tracker_id;
553     if (!base::StringToInt64(id_str, &tracker_id))
554       continue;
555
556     db_->Delete(itr->key().ToString());
557     db_->Put(GenerateDirtyIDKey(tracker_id), std::string());
558     ++num_dirty_trackers_;
559     promoted = true;
560   }
561   return promoted;
562 }
563
564 size_t MetadataDatabaseIndexOnDisk::CountDirtyTracker() const {
565   return num_dirty_trackers_;
566 }
567
568 size_t MetadataDatabaseIndexOnDisk::CountFileMetadata() const {
569   // TODO(peria): Cache the number of FileMetadata in the DB.
570   size_t count = 0;
571   scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
572   for (itr->Seek(kFileMetadataKeyPrefix); itr->Valid(); itr->Next()) {
573     if (!StartsWithASCII(itr->key().ToString(), kFileMetadataKeyPrefix, true))
574       break;
575     ++count;
576   }
577   return count;
578 }
579
580 size_t MetadataDatabaseIndexOnDisk::CountFileTracker() const {
581   // TODO(peria): Cache the number of FileTracker in the DB.
582   size_t count = 0;
583   scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
584   for (itr->Seek(kFileTrackerKeyPrefix); itr->Valid(); itr->Next()) {
585     if (!StartsWithASCII(itr->key().ToString(), kFileTrackerKeyPrefix, true))
586       break;
587     ++count;
588   }
589   return count;
590 }
591
592 void MetadataDatabaseIndexOnDisk::SetSyncRootTrackerID(
593     int64 sync_root_id) const {
594   service_metadata_->set_sync_root_tracker_id(sync_root_id);
595   PutServiceMetadataToDB(*service_metadata_, db_);
596 }
597
598 void MetadataDatabaseIndexOnDisk::SetLargestChangeID(
599     int64 largest_change_id) const {
600   service_metadata_->set_largest_change_id(largest_change_id);
601   PutServiceMetadataToDB(*service_metadata_, db_);
602 }
603
604 void MetadataDatabaseIndexOnDisk::SetNextTrackerID(
605     int64 next_tracker_id) const {
606   service_metadata_->set_next_tracker_id(next_tracker_id);
607   PutServiceMetadataToDB(*service_metadata_, db_);
608 }
609
610 int64 MetadataDatabaseIndexOnDisk::GetSyncRootTrackerID() const {
611   if (!service_metadata_->has_sync_root_tracker_id())
612     return kInvalidTrackerID;
613   return service_metadata_->sync_root_tracker_id();
614 }
615
616 int64 MetadataDatabaseIndexOnDisk::GetLargestChangeID() const {
617   if (!service_metadata_->has_largest_change_id())
618     return kInvalidTrackerID;
619   return service_metadata_->largest_change_id();
620 }
621
622 int64 MetadataDatabaseIndexOnDisk::GetNextTrackerID() const {
623   if (!service_metadata_->has_next_tracker_id())
624     return kInvalidTrackerID;
625   return service_metadata_->next_tracker_id();
626 }
627
628 std::vector<std::string>
629 MetadataDatabaseIndexOnDisk::GetRegisteredAppIDs() const {
630   std::vector<std::string> result;
631   scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
632   for (itr->Seek(kAppRootIDByAppIDKeyPrefix); itr->Valid(); itr->Next()) {
633     std::string id;
634     if (!RemovePrefix(itr->key().ToString(), kAppRootIDByAppIDKeyPrefix, &id))
635       break;
636     result.push_back(id);
637   }
638   return result;
639 }
640
641 std::vector<int64> MetadataDatabaseIndexOnDisk::GetAllTrackerIDs() const {
642   std::vector<int64> tracker_ids;
643   scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
644   for (itr->Seek(kFileTrackerKeyPrefix); itr->Valid(); itr->Next()) {
645     std::string id_str;
646     if (!RemovePrefix(itr->key().ToString(), kFileTrackerKeyPrefix, &id_str))
647       break;
648
649     int64 tracker_id;
650     if (!base::StringToInt64(id_str, &tracker_id))
651       continue;
652     tracker_ids.push_back(tracker_id);
653   }
654   return tracker_ids;
655 }
656
657 std::vector<std::string>
658 MetadataDatabaseIndexOnDisk::GetAllMetadataIDs() const {
659   std::vector<std::string> file_ids;
660   scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
661   for (itr->Seek(kFileMetadataKeyPrefix); itr->Valid(); itr->Next()) {
662     std::string file_id;
663     if (!RemovePrefix(itr->key().ToString(), kFileMetadataKeyPrefix, &file_id))
664       break;
665     file_ids.push_back(file_id);
666   }
667   return file_ids;
668 }
669
670 int64 MetadataDatabaseIndexOnDisk::BuildTrackerIndexes() {
671   int64 num_puts_before = db_->num_puts();
672
673   scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
674   for (itr->Seek(kFileTrackerKeyPrefix); itr->Valid(); itr->Next()) {
675     if (!RemovePrefix(itr->key().ToString(), kFileTrackerKeyPrefix, nullptr))
676       break;
677
678     FileTracker tracker;
679     if (!tracker.ParseFromString(itr->value().ToString())) {
680       util::Log(logging::LOG_WARNING, FROM_HERE,
681                 "Failed to parse a Tracker");
682       continue;
683     }
684
685     AddToAppIDIndex(tracker);
686     AddToFileIDIndexes(tracker);
687     AddToPathIndexes(tracker);
688     AddToDirtyTrackerIndexes(tracker);
689   }
690
691   return db_->num_puts() - num_puts_before;
692 }
693
694 int64 MetadataDatabaseIndexOnDisk::DeleteTrackerIndexes() {
695   const char* kIndexPrefixes[] = {
696     kAppRootIDByAppIDKeyPrefix, kActiveTrackerIDByFileIDKeyPrefix,
697     kTrackerIDByFileIDKeyPrefix, kMultiTrackerByFileIDKeyPrefix,
698     kActiveTrackerIDByParentAndTitleKeyPrefix,
699     kTrackerIDByParentAndTitleKeyPrefix, kMultiBackingParentAndTitleKeyPrefix,
700     kDirtyIDKeyPrefix, kDemotedDirtyIDKeyPrefix
701   };
702
703   int64 num_deletes_before = db_->num_deletes();
704   for (size_t i = 0; i < arraysize(kIndexPrefixes); ++i)
705     DeleteKeyStartsWith(kIndexPrefixes[i]);
706   num_dirty_trackers_ = 0;
707   return db_->num_deletes() - num_deletes_before;
708 }
709
710 LevelDBWrapper* MetadataDatabaseIndexOnDisk::GetDBForTesting() {
711   return db_;
712 }
713
714 MetadataDatabaseIndexOnDisk::MetadataDatabaseIndexOnDisk(LevelDBWrapper* db)
715     : db_(db),
716       num_dirty_trackers_(0) {
717   // TODO(peria): Add UMA to measure the number of FileMetadata, FileTracker,
718   //    and AppRootId.
719   service_metadata_ = InitializeServiceMetadata(db_);
720
721   // Check if index is valid, if no validations run in 7 days.
722   const int64 kThresholdToValidateInDays = 7;
723
724   int64 last_check_time = 0;
725   std::string value;
726   if (db_->Get(kLastValidationTimeKey, &value).ok())
727     base::StringToInt64(value, &last_check_time);
728   base::TimeDelta since_last_check =
729       base::Time::Now() - base::Time::FromInternalValue(last_check_time);
730   int64 since_last_check_in_days = since_last_check.InDays();
731   if (since_last_check_in_days >= kThresholdToValidateInDays ||
732       since_last_check_in_days < 0) {
733     // TODO(peria): Add UMA to check if the number of deleted entries and the
734     // number of built entries are different or not.
735     DeleteTrackerIndexes();
736     BuildTrackerIndexes();
737     db_->Put(kLastValidationTimeKey,
738              base::Int64ToString(base::Time::Now().ToInternalValue()));
739   } else {
740     num_dirty_trackers_ = CountDirtyTrackerInternal();
741   }
742 }
743
744 void MetadataDatabaseIndexOnDisk::AddToAppIDIndex(const FileTracker& tracker) {
745   if (!IsAppRoot(tracker)) {
746     DVLOG(3) << "  Tracker for " << tracker.file_id() << " is not an App root.";
747     return;
748   }
749
750   DVLOG(1) << "  Add to App root by App ID: " << tracker.app_id();
751
752   const std::string db_key = GenerateAppRootIDByAppIDKey(tracker.app_id());
753   DCHECK(tracker.active());
754   DCHECK(!DBHasKey(db_key));
755   db_->Put(db_key, base::Int64ToString(tracker.tracker_id()));
756 }
757
758 void MetadataDatabaseIndexOnDisk::UpdateInAppIDIndex(
759     const FileTracker& old_tracker,
760     const FileTracker& new_tracker) {
761   DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id());
762
763   if (IsAppRoot(old_tracker) && !IsAppRoot(new_tracker)) {
764     DCHECK(old_tracker.active());
765     DCHECK(!new_tracker.active());
766     const std::string key = GenerateAppRootIDByAppIDKey(old_tracker.app_id());
767     DCHECK(DBHasKey(key));
768
769     DVLOG(1) << "  Remove from App root by App ID: " << old_tracker.app_id();
770     db_->Delete(key);
771   } else if (!IsAppRoot(old_tracker) && IsAppRoot(new_tracker)) {
772     DCHECK(!old_tracker.active());
773     DCHECK(new_tracker.active());
774     const std::string key = GenerateAppRootIDByAppIDKey(new_tracker.app_id());
775     DCHECK(!DBHasKey(key));
776
777     DVLOG(1) << "  Add to App root by App ID: " << new_tracker.app_id();
778     db_->Put(key, base::Int64ToString(new_tracker.tracker_id()));
779   }
780 }
781
782 void MetadataDatabaseIndexOnDisk::RemoveFromAppIDIndex(
783     const FileTracker& tracker) {
784   if (!IsAppRoot(tracker)) {
785     DVLOG(3) << "  Tracker for " << tracker.file_id() << " is not an App root.";
786     return;
787   }
788
789   DCHECK(tracker.active());
790   const std::string key = GenerateAppRootIDByAppIDKey(tracker.app_id());
791   DCHECK(DBHasKey(key));
792
793   DVLOG(1) << "  Remove from App root by App ID: " << tracker.app_id();
794   db_->Delete(key);
795 }
796
797 void MetadataDatabaseIndexOnDisk::AddToFileIDIndexes(
798     const FileTracker& new_tracker) {
799   const std::string& file_id = new_tracker.file_id();
800
801   DVLOG(1) << "  Add to trackers by file ID: " << file_id;
802   const std::string prefix = GenerateTrackerIDByFileIDKeyPrefix(file_id);
803   AddToTrackerIDSetWithPrefix(
804       GenerateActiveTrackerIDByFileIDKey(file_id), prefix, new_tracker);
805
806   const std::string multi_tracker_key = GenerateMultiTrackerKey(file_id);
807   if (!DBHasKey(multi_tracker_key) &&
808       CountWithPrefix(prefix, new_tracker.tracker_id()) != NONE) {
809     DVLOG(1) << "  Add to multi-tracker file IDs: " << file_id;
810     db_->Put(multi_tracker_key, std::string());
811   }
812 }
813
814 void MetadataDatabaseIndexOnDisk::UpdateInFileIDIndexes(
815     const FileTracker& old_tracker,
816     const FileTracker& new_tracker) {
817   DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id());
818   DCHECK_EQ(old_tracker.file_id(), new_tracker.file_id());
819
820   const std::string& file_id = new_tracker.file_id();
821   const std::string prefix = GenerateTrackerIDByFileIDKeyPrefix(file_id);
822   DCHECK(DBHasKey(prefix + base::Int64ToString(new_tracker.tracker_id())));
823
824   if (old_tracker.active() && !new_tracker.active()) {
825     DeactivateInTrackerIDSetWithPrefix(
826         GenerateActiveTrackerIDByFileIDKey(file_id), prefix,
827         new_tracker.tracker_id());
828   } else if (!old_tracker.active() && new_tracker.active()) {
829     ActivateInTrackerIDSetWithPrefix(
830         GenerateActiveTrackerIDByFileIDKey(file_id), prefix,
831         new_tracker.tracker_id());
832   }
833 }
834
835 void MetadataDatabaseIndexOnDisk::RemoveFromFileIDIndexes(
836     const FileTracker& tracker) {
837   const std::string& file_id = tracker.file_id();
838   const std::string prefix =
839       GenerateTrackerIDByFileIDKeyPrefix(file_id);
840
841   if (!EraseInTrackerIDSetWithPrefix(
842           GenerateActiveTrackerIDByFileIDKey(file_id),
843           prefix, tracker.tracker_id()))
844     return;
845
846   DVLOG(1) << "  Remove from trackers by file ID: " << tracker.tracker_id();
847
848   const std::string multi_key = GenerateMultiTrackerKey(file_id);
849   if (DBHasKey(multi_key) &&
850       CountWithPrefix(prefix, tracker.tracker_id()) != MULTIPLE) {
851     DVLOG(1) << "  Remove from multi-tracker file IDs: " << file_id;
852     db_->Delete(multi_key);
853   }
854 }
855
856 void MetadataDatabaseIndexOnDisk::AddToPathIndexes(
857     const FileTracker& new_tracker) {
858   int64 parent_id = new_tracker.parent_tracker_id();
859   std::string title = GetTrackerTitle(new_tracker);
860
861   DVLOG(1) << "  Add to trackers by parent and title: "
862            << parent_id << " " << title;
863
864   const std::string prefix =
865       GenerateTrackerIDByParentAndTitleKeyPrefix(parent_id, title);
866   if (!title.empty()) {
867     scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
868     for (itr->Seek(prefix); itr->Valid(); itr->Next()) {
869       std::string id_str;
870       if (!RemovePrefix(itr->key().ToString(), prefix, &id_str))
871         break;
872
873       int64 tracker_id;
874       if (!base::StringToInt64(id_str, &tracker_id))
875         continue;
876       if (tracker_id == new_tracker.tracker_id()) {
877         NOTREACHED();
878         continue;
879       }
880
881       const std::string multi_key =
882           GenerateMultiBackingParentAndTitleKey(parent_id, title);
883       DVLOG_IF(1, !DBHasKey(multi_key))
884           << "  Add to multi backing file paths: " << parent_id << " " << title;
885       db_->Put(multi_key, std::string());
886       break;
887     }
888   }
889
890   AddToTrackerIDSetWithPrefix(
891       GenerateActiveTrackerIDByParentAndTitleKey(parent_id, title),
892       prefix, new_tracker);
893 }
894
895 void MetadataDatabaseIndexOnDisk::UpdateInPathIndexes(
896     const FileTracker& old_tracker,
897     const FileTracker& new_tracker) {
898   DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id());
899   DCHECK_EQ(old_tracker.parent_tracker_id(), new_tracker.parent_tracker_id());
900   DCHECK(GetTrackerTitle(old_tracker) == GetTrackerTitle(new_tracker) ||
901          !old_tracker.has_synced_details());
902
903   int64 tracker_id = new_tracker.tracker_id();
904   int64 parent_id = new_tracker.parent_tracker_id();
905   const std::string old_title = GetTrackerTitle(old_tracker);
906   const std::string title = GetTrackerTitle(new_tracker);
907
908   if (old_title != title) {
909     const std::string old_prefix =
910         GenerateTrackerIDByParentAndTitleKeyPrefix(parent_id, old_title);
911     EraseInTrackerIDSetWithPrefix(
912         GenerateActiveTrackerIDByParentAndTitleKey(parent_id, old_title),
913         old_prefix, tracker_id);
914
915     if (!old_title.empty() &&
916         CountWithPrefix(old_prefix, tracker_id) != MULTIPLE) {
917       const std::string old_multi_backing_key =
918           GenerateMultiBackingParentAndTitleKey(parent_id, old_title);
919       DVLOG_IF(1, DBHasKey(old_multi_backing_key))
920           << "  Remove from multi backing file paths: "
921           << parent_id << " " << old_title;
922       db_->Delete(old_multi_backing_key);
923     }
924
925     DVLOG(1) << "  Add to trackers by parent and title: "
926              << parent_id << " " << title;
927
928     const std::string prefix =
929         GenerateTrackerIDByParentAndTitleKeyPrefix(parent_id, title);
930     AddToTrackerIDSetWithPrefix(
931         GenerateActiveTrackerIDByParentAndTitleKey(parent_id, title),
932         prefix, new_tracker);
933
934     if (!title.empty() && CountWithPrefix(prefix, tracker_id) != NONE) {
935       const std::string multi_backing_key =
936           GenerateMultiBackingParentAndTitleKey(parent_id, title);
937       DVLOG_IF(1, !DBHasKey(multi_backing_key))
938           << "  Add to multi backing file_paths: "
939           << parent_id << " " << title;
940       db_->Put(multi_backing_key, std::string());
941     }
942
943     return;
944   }
945
946   const std::string active_tracker_key =
947       GenerateActiveTrackerIDByParentAndTitleKey(parent_id, title);
948   const std::string prefix =
949       GenerateTrackerIDByParentAndTitleKeyPrefix(parent_id, title);
950   if (old_tracker.active() && !new_tracker.active()) {
951     DeactivateInTrackerIDSetWithPrefix(
952         active_tracker_key, prefix, tracker_id);
953   } else if (!old_tracker.active() && new_tracker.active()) {
954     ActivateInTrackerIDSetWithPrefix(
955         active_tracker_key, prefix, tracker_id);
956   }
957 }
958
959 void MetadataDatabaseIndexOnDisk::RemoveFromPathIndexes(
960     const FileTracker& tracker) {
961   int64 tracker_id = tracker.tracker_id();
962   int64 parent_id = tracker.parent_tracker_id();
963   std::string title = GetTrackerTitle(tracker);
964
965   DVLOG(1) << "  Remove from trackers by parent and title: "
966            << parent_id << " " << title;
967
968   const std::string active_tracker_key =
969       GenerateActiveTrackerIDByParentAndTitleKey(parent_id, title);
970   const std::string key_prefix =
971       GenerateTrackerIDByParentAndTitleKeyPrefix(parent_id, title);
972   if (!EraseInTrackerIDSetWithPrefix(
973           active_tracker_key, key_prefix, tracker_id))
974     return;
975
976   const std::string multi_key =
977       GenerateMultiBackingParentAndTitleKey(parent_id, title);
978   if (!title.empty() && DBHasKey(multi_key) &&
979       CountWithPrefix(key_prefix, tracker_id) != MULTIPLE) {
980     DVLOG(1) << "  Remove from multi backing file paths: "
981              << parent_id << " " << title;
982     db_->Delete(multi_key);
983   }
984 }
985
986 void MetadataDatabaseIndexOnDisk::AddToDirtyTrackerIndexes(
987     const FileTracker& new_tracker) {
988   const std::string dirty_key = GenerateDirtyIDKey(new_tracker.tracker_id());
989   DCHECK(!DBHasKey(dirty_key));
990   DCHECK(!DBHasKey(GenerateDemotedDirtyIDKey(new_tracker.tracker_id())));
991
992   if (new_tracker.dirty()) {
993     DVLOG(1) << "  Add to dirty tracker IDs: " << new_tracker.tracker_id();
994     db_->Put(dirty_key, std::string());
995     ++num_dirty_trackers_;
996   }
997 }
998
999 void MetadataDatabaseIndexOnDisk::UpdateInDirtyTrackerIndexes(
1000     const FileTracker& old_tracker,
1001     const FileTracker& new_tracker) {
1002   DCHECK_EQ(old_tracker.tracker_id(), new_tracker.tracker_id());
1003
1004   int64 tracker_id = new_tracker.tracker_id();
1005   const std::string dirty_key = GenerateDirtyIDKey(tracker_id);
1006   const std::string demoted_key = GenerateDemotedDirtyIDKey(tracker_id);
1007   if (old_tracker.dirty() && !new_tracker.dirty()) {
1008     DCHECK(DBHasKey(dirty_key) || DBHasKey(demoted_key));
1009
1010     DVLOG(1) << "  Remove from dirty trackers IDs: " << tracker_id;
1011
1012     if (DBHasKey(dirty_key))
1013       --num_dirty_trackers_;
1014     db_->Delete(dirty_key);
1015     db_->Delete(demoted_key);
1016   } else if (!old_tracker.dirty() && new_tracker.dirty()) {
1017     DCHECK(!DBHasKey(dirty_key));
1018     DCHECK(!DBHasKey(demoted_key));
1019
1020     DVLOG(1) << "  Add to dirty tracker IDs: " << tracker_id;
1021
1022     db_->Put(dirty_key, std::string());
1023     ++num_dirty_trackers_;
1024   }
1025 }
1026
1027 void MetadataDatabaseIndexOnDisk::RemoveFromDirtyTrackerIndexes(
1028     const FileTracker& tracker) {
1029   if (tracker.dirty()) {
1030     int64 tracker_id = tracker.tracker_id();
1031     const std::string dirty_key = GenerateDirtyIDKey(tracker_id);
1032     const std::string demoted_key = GenerateDemotedDirtyIDKey(tracker_id);
1033     DCHECK(DBHasKey(dirty_key) || DBHasKey(demoted_key));
1034
1035     DVLOG(1) << "  Remove from dirty tracker IDs: " << tracker_id;
1036     if (DBHasKey(dirty_key))
1037       --num_dirty_trackers_;
1038     db_->Delete(dirty_key);
1039     db_->Delete(demoted_key);
1040   }
1041 }
1042
1043 TrackerIDSet MetadataDatabaseIndexOnDisk::GetTrackerIDSetByPrefix(
1044     const std::string& active_tracker_key,
1045     const std::string& ids_prefix) const {
1046   TrackerIDSet trackers;
1047
1048   // Seek IDs.
1049   scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
1050   for (itr->Seek(ids_prefix); itr->Valid(); itr->Next()) {
1051     const std::string& key(itr->key().ToString());
1052     std::string id_str;
1053     if (!RemovePrefix(key, ids_prefix, &id_str))
1054       break;
1055
1056     int64 tracker_id;
1057     if (!base::StringToInt64(id_str, &tracker_id))
1058       continue;
1059     trackers.InsertInactiveTracker(tracker_id);
1060   }
1061
1062   // Set an active tracker ID, if available.
1063   std::string value;
1064   leveldb::Status status = db_->Get(active_tracker_key, &value);
1065   int64 active_tracker;
1066   if (status.ok() && base::StringToInt64(value, &active_tracker)) {
1067     DCHECK_NE(kInvalidTrackerID, active_tracker);
1068     trackers.Activate(active_tracker);
1069   }
1070
1071   return trackers;
1072 }
1073
1074 void MetadataDatabaseIndexOnDisk::AddToTrackerIDSetWithPrefix(
1075     const std::string& active_tracker_key, const std::string& key_prefix,
1076     const FileTracker& tracker) {
1077   DCHECK(tracker.tracker_id());
1078
1079   const std::string id_str = base::Int64ToString(tracker.tracker_id());
1080   db_->Put(key_prefix + id_str, std::string());
1081   if (tracker.active())
1082     db_->Put(active_tracker_key, id_str);
1083 }
1084
1085 bool MetadataDatabaseIndexOnDisk::EraseInTrackerIDSetWithPrefix(
1086     const std::string& active_tracker_key, const std::string& key_prefix,
1087     int64 tracker_id) {
1088   std::string value;
1089   const std::string del_key = key_prefix + base::Int64ToString(tracker_id);
1090   leveldb::Status status = db_->Get(del_key, &value);
1091   if (status.IsNotFound())
1092     return false;
1093
1094   db_->Delete(del_key);
1095
1096   status = db_->Get(active_tracker_key, &value);
1097   int64 active_tracker_id;
1098   if (status.ok() && base::StringToInt64(value, &active_tracker_id) &&
1099       active_tracker_id == tracker_id) {
1100     db_->Delete(active_tracker_key);
1101   }
1102
1103   return true;
1104 }
1105
1106 void MetadataDatabaseIndexOnDisk::ActivateInTrackerIDSetWithPrefix(
1107     const std::string& active_tracker_key, const std::string& key_prefix,
1108     int64 tracker_id) {
1109   DCHECK(DBHasKey(key_prefix + base::Int64ToString(tracker_id)));
1110
1111   std::string value;
1112   leveldb::Status status = db_->Get(active_tracker_key, &value);
1113   int64 active_tracker_id = kInvalidTrackerID;
1114   if (status.IsNotFound() ||
1115       (status.ok() && base::StringToInt64(value, &active_tracker_id))) {
1116     DCHECK(active_tracker_id != tracker_id);
1117     db_->Put(active_tracker_key, base::Int64ToString(tracker_id));
1118   }
1119 }
1120
1121 void MetadataDatabaseIndexOnDisk::DeactivateInTrackerIDSetWithPrefix(
1122     const std::string& active_tracker_key, const std::string& key_prefix,
1123     int64 tracker_id) {
1124   DCHECK(DBHasKey(key_prefix + base::Int64ToString(tracker_id)));
1125
1126   std::string value;
1127   leveldb::Status status = db_->Get(active_tracker_key, &value);
1128   int64 active_tracker_id;
1129   if (status.ok() && base::StringToInt64(value, &active_tracker_id)) {
1130     DCHECK(active_tracker_id == tracker_id);
1131     db_->Delete(active_tracker_key);
1132   }
1133 }
1134
1135 bool MetadataDatabaseIndexOnDisk::DBHasKey(const std::string& key) const {
1136   scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
1137   itr->Seek(key);
1138   return itr->Valid() && (itr->key() == key);
1139 }
1140
1141 size_t MetadataDatabaseIndexOnDisk::CountDirtyTrackerInternal() const {
1142   size_t num_dirty_trackers = 0;
1143
1144   scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
1145   for (itr->Seek(kDirtyIDKeyPrefix); itr->Valid(); itr->Next()) {
1146     if (!StartsWithASCII(itr->key().ToString(), kDirtyIDKeyPrefix, true))
1147       break;
1148     ++num_dirty_trackers;
1149   }
1150
1151   return num_dirty_trackers;
1152 }
1153
1154 MetadataDatabaseIndexOnDisk::NumEntries
1155 MetadataDatabaseIndexOnDisk::CountWithPrefix(
1156     const std::string& prefix, int64 ignored_id) {
1157   const std::string ignored = base::Int64ToString(ignored_id);
1158
1159   size_t count = 0;
1160   scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
1161   for (itr->Seek(prefix); itr->Valid() && count <= 1; itr->Next()) {
1162     std::string value;
1163     if (!RemovePrefix(itr->key().ToString(), prefix, &value))
1164       break;
1165     if (value == ignored)
1166       continue;
1167
1168     ++count;
1169   }
1170
1171   if (count >= 2)
1172     return MULTIPLE;
1173   return count == 0 ? NONE : SINGLE;
1174 }
1175
1176 void MetadataDatabaseIndexOnDisk::DeleteKeyStartsWith(
1177     const std::string& prefix) {
1178   scoped_ptr<LevelDBWrapper::Iterator> itr(db_->NewIterator());
1179   for (itr->Seek(prefix); itr->Valid();) {
1180     const std::string key = itr->key().ToString();
1181     if (!StartsWithASCII(key, prefix, true))
1182       break;
1183     itr->Delete();
1184   }
1185 }
1186
1187 }  // namespace drive_backend
1188 }  // namespace sync_file_system