metadata_database.cc revision 7dbb3d5cf0c15f500944d211057644d6a2f37371
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/drive_backend/metadata_database.h"
6
7#include <stack>
8
9#include "base/bind.h"
10#include "base/callback.h"
11#include "base/files/file_path.h"
12#include "base/location.h"
13#include "base/memory/scoped_vector.h"
14#include "base/message_loop/message_loop_proxy.h"
15#include "base/sequenced_task_runner.h"
16#include "base/stl_util.h"
17#include "base/strings/string_number_conversions.h"
18#include "base/strings/string_util.h"
19#include "base/strings/stringprintf.h"
20#include "base/task_runner_util.h"
21#include "base/threading/thread_restrictions.h"
22#include "chrome/browser/google_apis/drive_api_parser.h"
23#include "chrome/browser/sync_file_system/drive_backend/metadata_database.pb.h"
24#include "chrome/browser/sync_file_system/drive_backend/metadata_db_migration_util.h"
25#include "chrome/browser/sync_file_system/logger.h"
26#include "third_party/leveldatabase/src/include/leveldb/db.h"
27#include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
28#include "webkit/browser/fileapi/syncable/syncable_file_system_util.h"
29#include "webkit/common/fileapi/file_system_util.h"
30
31namespace sync_file_system {
32namespace drive_backend {
33
34typedef MetadataDatabase::FileByAppID FileByAppID;
35typedef MetadataDatabase::FileByFileID FileByFileID;
36typedef MetadataDatabase::FileByParentAndTitle FileByParentAndTitle;
37typedef MetadataDatabase::FileSet FileSet;
38typedef MetadataDatabase::FilesByParent FilesByParent;
39
40const char kDatabaseVersionKey[] = "VERSION";
41const int64 kCurrentDatabaseVersion = 3;
42const char kServiceMetadataKey[] = "SERVICE";
43const char kFileMetadataKeyPrefix[] = "FILE: ";
44
45struct DatabaseContents {
46  scoped_ptr<ServiceMetadata> service_metadata;
47  ScopedVector<DriveFileMetadata> file_metadata;
48};
49
50namespace {
51
52std::string RemovePrefix(const std::string& str, const std::string& prefix) {
53  if (StartsWithASCII(str, prefix, true))
54    return str.substr(prefix.size());
55  return str;
56}
57
58void AdaptLevelDBStatusToSyncStatusCode(const SyncStatusCallback& callback,
59                                        const leveldb::Status& status) {
60  callback.Run(LevelDBStatusToSyncStatusCode(status));
61}
62
63void PutFileToBatch(const DriveFileMetadata& file, leveldb::WriteBatch* batch) {
64  std::string value;
65  bool success = file.SerializeToString(&value);
66  DCHECK(success);
67  batch->Put(kFileMetadataKeyPrefix + file.file_id(), value);
68}
69
70void PushChildrenToStack(const FilesByParent& files_by_parent,
71                         const std::string& folder_id,
72                         std::stack<std::string>* stack) {
73  FilesByParent::const_iterator found = files_by_parent.find(folder_id);
74  if (found == files_by_parent.end())
75    return;
76  const FileSet& children = found->second;
77  for (FileSet::const_iterator itr = children.begin();
78       itr != children.end(); ++itr)
79    stack->push((*itr)->file_id());
80}
81
82// Returns true if |db| has no content.
83bool IsDatabaseEmpty(leveldb::DB* db) {
84  DCHECK(db);
85  scoped_ptr<leveldb::Iterator> itr(db->NewIterator(leveldb::ReadOptions()));
86  itr->SeekToFirst();
87  return !itr->Valid();
88}
89
90SyncStatusCode OpenDatabase(const base::FilePath& path,
91                            scoped_ptr<leveldb::DB>* db_out,
92                            bool* created) {
93  base::ThreadRestrictions::AssertIOAllowed();
94  DCHECK(db_out);
95  DCHECK(created);
96
97  leveldb::Options options;
98  options.max_open_files = 0;  // Use minimum.
99  options.create_if_missing = true;
100  leveldb::DB* db = NULL;
101  leveldb::Status db_status =
102      leveldb::DB::Open(options, path.AsUTF8Unsafe(), &db);
103  SyncStatusCode status = LevelDBStatusToSyncStatusCode(db_status);
104  if (status != SYNC_STATUS_OK) {
105    delete db;
106    return status;
107  }
108
109  *created = IsDatabaseEmpty(db);
110  db_out->reset(db);
111  return status;
112}
113
114SyncStatusCode MigrateDatabaseIfNeeded(leveldb::DB* db) {
115  base::ThreadRestrictions::AssertIOAllowed();
116  DCHECK(db);
117  std::string value;
118  leveldb::Status status =
119      db->Get(leveldb::ReadOptions(), kDatabaseVersionKey, &value);
120  int64 version = 0;
121  if (status.ok()) {
122    if (!base::StringToInt64(value, &version))
123      return SYNC_DATABASE_ERROR_FAILED;
124  } else {
125    if (!status.IsNotFound())
126      return SYNC_DATABASE_ERROR_FAILED;
127  }
128
129  switch (version) {
130    case 0:
131      drive_backend::MigrateDatabaseFromV0ToV1(db);
132      // fall-through
133    case 1:
134      drive_backend::MigrateDatabaseFromV1ToV2(db);
135      // fall-through
136    case 2:
137      // TODO(tzik): Migrate database from version 2 to 3.
138      //   * Add sync-root folder as active, dirty and needs_folder_listing
139      //     folder.
140      //   * Add app-root folders for each origins.  Each app-root folder for
141      //     an enabled origin should be a active, dirty and
142      //     needs_folder_listing folder.  And Each app-root folder for a
143      //     disabled origin should be an inactive, dirty and
144      //     non-needs_folder_listing folder.
145      //   * Add a file metadata for each file in previous version.
146      NOTIMPLEMENTED();
147      return SYNC_DATABASE_ERROR_FAILED;
148      // fall-through
149    case 3:
150      DCHECK_EQ(3, kCurrentDatabaseVersion);
151      return SYNC_STATUS_OK;
152    default:
153      return SYNC_DATABASE_ERROR_FAILED;
154  }
155}
156
157SyncStatusCode WriteVersionInfo(leveldb::DB* db) {
158  base::ThreadRestrictions::AssertIOAllowed();
159  DCHECK(db);
160  return LevelDBStatusToSyncStatusCode(
161      db->Put(leveldb::WriteOptions(),
162              kDatabaseVersionKey,
163              base::Int64ToString(kCurrentDatabaseVersion)));
164}
165
166SyncStatusCode ReadDatabaseContents(leveldb::DB* db,
167                                    DatabaseContents* contents) {
168  base::ThreadRestrictions::AssertIOAllowed();
169  DCHECK(db);
170  DCHECK(contents);
171
172  scoped_ptr<leveldb::Iterator> itr(db->NewIterator(leveldb::ReadOptions()));
173  for (itr->SeekToFirst(); itr->Valid(); itr->Next()) {
174    std::string key = itr->key().ToString();
175    std::string value = itr->value().ToString();
176    if (key == kServiceMetadataKey) {
177      scoped_ptr<ServiceMetadata> service_metadata(new ServiceMetadata);
178      if (!service_metadata->ParseFromString(value)) {
179        util::Log(logging::LOG_WARNING, FROM_HERE,
180                  "Failed to parse SyncServiceMetadata");
181        continue;
182      }
183
184      contents->service_metadata = service_metadata.Pass();
185      continue;
186    }
187
188    if (StartsWithASCII(key, kFileMetadataKeyPrefix, true)) {
189      std::string file_id = RemovePrefix(key, kFileMetadataKeyPrefix);
190
191      scoped_ptr<DriveFileMetadata> metadata(new DriveFileMetadata);
192      if (!metadata->ParseFromString(itr->value().ToString())) {
193        util::Log(logging::LOG_WARNING, FROM_HERE,
194                  "Failed to parse a Metadata");
195        continue;
196      }
197
198      contents->file_metadata.push_back(metadata.release());
199      continue;
200    }
201  }
202
203  return SYNC_STATUS_OK;
204}
205
206SyncStatusCode InitializeServiceMetadata(DatabaseContents* contents,
207                                         leveldb::WriteBatch* batch) {
208
209  if (!contents->service_metadata) {
210    contents->service_metadata.reset(new ServiceMetadata);
211
212    std::string value;
213    contents->service_metadata->SerializeToString(&value);
214    batch->Put(kServiceMetadataKey, value);
215  }
216  return SYNC_STATUS_OK;
217}
218
219SyncStatusCode RemoveUnreachableFiles(DatabaseContents* contents,
220                                      leveldb::WriteBatch* batch) {
221  FileByFileID unvisited_files;
222  FilesByParent files_by_parent;
223
224  for (ScopedVector<DriveFileMetadata>::iterator itr =
225           contents->file_metadata.begin();
226       itr != contents->file_metadata.end();
227       ++itr) {
228    DriveFileMetadata* metadata = *itr;
229    DCHECK(!ContainsKey(unvisited_files, metadata->file_id()));
230    unvisited_files[metadata->file_id()] = metadata;
231    files_by_parent[metadata->parent_folder_id()].insert(metadata);
232  }
233
234  // Traverse synced metadata tree. Take only active items and their children.
235  // Drop unreachable items.
236  ScopedVector<DriveFileMetadata> reachable_files;
237  std::stack<std::string> pending;
238  if (!contents->service_metadata->sync_root_folder_id().empty())
239    pending.push(contents->service_metadata->sync_root_folder_id());
240
241  while (!pending.empty()) {
242    std::string file_id = pending.top();
243    pending.pop();
244
245    {
246      FileByFileID::iterator found = unvisited_files.find(file_id);
247      if (found == unvisited_files.end())
248        continue;
249
250      DriveFileMetadata* metadata = found->second;
251      unvisited_files.erase(found);
252      reachable_files.push_back(metadata);
253
254      if (!metadata->active())
255        continue;
256    }
257
258    FilesByParent::iterator found = files_by_parent.find(file_id);
259    if (found == files_by_parent.end())
260      continue;
261
262    for (FileSet::const_iterator itr = found->second.begin();
263         itr != found->second.end();
264         ++itr)
265      pending.push((*itr)->file_id());
266  }
267
268  for (FileByFileID::iterator itr = unvisited_files.begin();
269       itr != unvisited_files.end();
270       ++itr) {
271    DriveFileMetadata* metadata = itr->second;
272    batch->Delete(metadata->file_id());
273    delete metadata;
274  }
275  unvisited_files.clear();
276
277  // |reachable_files| contains all files/folders reachable from sync-root
278  // folder via active folders.
279  contents->file_metadata.weak_clear();
280  contents->file_metadata.swap(reachable_files);
281
282  return SYNC_STATUS_OK;
283}
284
285template <typename Container, typename Key, typename Value>
286bool FindItem(const Container& container, const Key& key, Value* value) {
287  typename Container::const_iterator found = container.find(key);
288  if (found == container.end())
289    return false;
290  if (value)
291    *value = *found->second;
292  return true;
293}
294
295void RunSoon(const tracked_objects::Location& from_here,
296             const base::Closure& closure) {
297  base::MessageLoopProxy::current()->PostTask(from_here, closure);
298}
299
300}  // namespace
301
302bool MetadataDatabase::FileIDComparator::operator()(
303    DriveFileMetadata* left,
304    DriveFileMetadata* right) const {
305  return left->file_id() < right->file_id();
306}
307
308// static
309void MetadataDatabase::Create(base::SequencedTaskRunner* task_runner,
310                              const base::FilePath& database_path,
311                              const CreateCallback& callback) {
312  task_runner->PostTask(FROM_HERE, base::Bind(
313      &CreateOnTaskRunner,
314      base::MessageLoopProxy::current(),
315      make_scoped_refptr(task_runner),
316      database_path, callback));
317}
318
319MetadataDatabase::~MetadataDatabase() {
320  task_runner_->DeleteSoon(FROM_HERE, db_.release());
321  STLDeleteContainerPairSecondPointers(
322      file_by_file_id_.begin(), file_by_file_id_.end());
323}
324
325int64 MetadataDatabase::GetLargestChangeID() const {
326  return service_metadata_->largest_change_id();
327}
328
329void MetadataDatabase::RegisterApp(const std::string& app_id,
330                                   const std::string& folder_id,
331                                   const SyncStatusCallback& callback) {
332  if (FindAppRootFolder(app_id, NULL)) {
333    RunSoon(FROM_HERE, base::Bind(callback, SYNC_STATUS_OK));
334    return;
335  }
336
337  DriveFileMetadata folder;
338  if (!FindFileByFileID(folder_id, &folder)) {
339    RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND));
340    return;
341  }
342
343  DCHECK(!folder.active());
344  scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
345  RegisterFolderAsAppRoot(app_id, folder.file_id(), batch.get());
346  WriteToDatabase(batch.Pass(), callback);
347}
348
349void MetadataDatabase::DisableApp(const std::string& app_id,
350                                  const SyncStatusCallback& callback) {
351  DriveFileMetadata folder;
352  if (!FindAppRootFolder(app_id, &folder)) {
353    RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND));
354    return;
355  }
356
357  if (!folder.active()) {
358    RunSoon(FROM_HERE, base::Bind(callback, SYNC_STATUS_OK));
359    return;
360  }
361
362  scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
363  MakeFileInactive(folder.file_id(), batch.get());
364  WriteToDatabase(batch.Pass(), callback);
365}
366
367void MetadataDatabase::EnableApp(const std::string& app_id,
368                                 const SyncStatusCallback& callback) {
369  DriveFileMetadata folder;
370  if (!FindAppRootFolder(app_id, &folder)) {
371    RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND));
372    return;
373  }
374
375  if (folder.active()) {
376    RunSoon(FROM_HERE, base::Bind(callback, SYNC_STATUS_OK));
377    return;
378  }
379
380  scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
381  MakeFileActive(folder.file_id(), batch.get());
382  WriteToDatabase(batch.Pass(), callback);
383}
384
385void MetadataDatabase::UnregisterApp(const std::string& app_id,
386                                     const SyncStatusCallback& callback) {
387  DriveFileMetadata folder;
388  if (!FindAppRootFolder(app_id, &folder)) {
389    RunSoon(FROM_HERE, base::Bind(callback, SYNC_STATUS_OK));
390    return;
391  }
392
393  scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
394  UnregisterFolderAsAppRoot(app_id, batch.get());
395  WriteToDatabase(batch.Pass(), callback);
396}
397
398bool MetadataDatabase::FindAppRootFolder(const std::string& app_id,
399                                         DriveFileMetadata* folder) const {
400  return FindItem(app_root_by_app_id_, app_id, folder);
401}
402
403bool MetadataDatabase::FindFileByFileID(const std::string& file_id,
404                                        DriveFileMetadata* metadata) const {
405  return FindItem(file_by_file_id_, file_id, metadata);
406}
407
408size_t MetadataDatabase::FindFilesByParentAndTitle(
409    const std::string& file_id,
410    const std::string& title,
411    ScopedVector<DriveFileMetadata>* files) const {
412  NOTIMPLEMENTED();
413  return 0;
414}
415
416bool MetadataDatabase::FindActiveFileByParentAndTitle(
417    const std::string& folder_id,
418    const std::string& title,
419    DriveFileMetadata* file) const {
420  return FindItem(active_file_by_parent_and_title_,
421                  std::make_pair(folder_id, title),
422                  file);
423}
424
425bool MetadataDatabase::FindActiveFileByPath(const std::string& app_id,
426                                            const base::FilePath& path,
427                                            DriveFileMetadata* file) const {
428  DriveFileMetadata current;
429  if (!FindAppRootFolder(app_id, &current))
430    return false;
431
432  std::vector<base::FilePath::StringType> components;
433  path.GetComponents(&components);
434
435  std::string parent_folder_id = current.file_id();
436  for (std::vector<base::FilePath::StringType>::iterator itr =
437           components.begin();
438       itr != components.end();
439       ++itr) {
440    std::string current_folder_id = current.file_id();
441    if (!FindActiveFileByParentAndTitle(
442            current_folder_id, base::FilePath(*itr).AsUTF8Unsafe(), &current))
443      return false;
444  }
445  if (file)
446    *file = current;
447  return true;
448}
449
450bool MetadataDatabase::BuildPathForFile(const std::string& file_id,
451                                        base::FilePath* path) const {
452  NOTIMPLEMENTED();
453  return false;
454}
455
456void MetadataDatabase::UpdateByChangeList(
457    ScopedVector<google_apis::ChangeResource> changes,
458    const SyncStatusCallback& callback) {
459  NOTIMPLEMENTED();
460}
461
462void MetadataDatabase::PopulateFolder(
463    const std::string& folder_id,
464    ScopedVector<google_apis::ResourceEntry> children,
465    const SyncStatusCallback& callback) {
466  NOTIMPLEMENTED();
467}
468
469MetadataDatabase::MetadataDatabase(base::SequencedTaskRunner* task_runner)
470    : task_runner_(task_runner), weak_ptr_factory_(this) {
471  DCHECK(task_runner);
472}
473
474// static
475void MetadataDatabase::CreateOnTaskRunner(
476    base::SingleThreadTaskRunner* callback_runner,
477    base::SequencedTaskRunner* task_runner,
478    const base::FilePath& database_path,
479    const CreateCallback& callback) {
480  scoped_ptr<MetadataDatabase> metadata_database(
481      new MetadataDatabase(task_runner));
482  SyncStatusCode status =
483      metadata_database->InitializeOnTaskRunner(database_path);
484  if (status != SYNC_STATUS_OK)
485    metadata_database.reset();
486
487  callback_runner->PostTask(FROM_HERE, base::Bind(
488      callback, status, base::Passed(&metadata_database)));
489}
490
491// static
492SyncStatusCode MetadataDatabase::CreateForTesting(
493    scoped_ptr<leveldb::DB> db,
494    scoped_ptr<MetadataDatabase>* metadata_database_out) {
495  scoped_ptr<MetadataDatabase> metadata_database(
496      new MetadataDatabase(base::MessageLoopProxy::current()));
497  metadata_database->db_ = db.Pass();
498  SyncStatusCode status =
499      metadata_database->InitializeOnTaskRunner(base::FilePath());
500  if (status == SYNC_STATUS_OK)
501    *metadata_database_out = metadata_database.Pass();
502  return status;
503}
504
505SyncStatusCode MetadataDatabase::InitializeOnTaskRunner(
506    const base::FilePath& database_path) {
507  base::ThreadRestrictions::AssertIOAllowed();
508  DCHECK(task_runner_->RunsTasksOnCurrentThread());
509
510  SyncStatusCode status = SYNC_STATUS_UNKNOWN;
511  bool created = false;
512  // Open database unless |db_| is overridden for testing.
513  if (!db_) {
514    status = OpenDatabase(database_path, &db_, &created);
515    if (status != SYNC_STATUS_OK)
516      return status;
517  }
518
519  if (created) {
520    status = WriteVersionInfo(db_.get());
521    if (status != SYNC_STATUS_OK)
522      return status;
523  } else {
524    status = MigrateDatabaseIfNeeded(db_.get());
525    if (status != SYNC_STATUS_OK)
526      return status;
527  }
528
529  DatabaseContents contents;
530  status = ReadDatabaseContents(db_.get(), &contents);
531  if (status != SYNC_STATUS_OK)
532    return status;
533
534  leveldb::WriteBatch batch;
535  status = InitializeServiceMetadata(&contents, &batch);
536  if (status != SYNC_STATUS_OK)
537    return status;
538
539  status = RemoveUnreachableFiles(&contents, &batch);
540  if (status != SYNC_STATUS_OK)
541    return status;
542
543  status = LevelDBStatusToSyncStatusCode(
544      db_->Write(leveldb::WriteOptions(), &batch));
545  if (status != SYNC_STATUS_OK)
546    return status;
547
548  BuildIndexes(&contents);
549  return status;
550}
551
552void MetadataDatabase::BuildIndexes(DatabaseContents* contents) {
553  for (ScopedVector<DriveFileMetadata>::iterator itr =
554           contents->file_metadata.begin();
555       itr != contents->file_metadata.end();
556       ++itr) {
557    DriveFileMetadata* file = *itr;
558    file_by_file_id_[file->file_id()] = file;
559
560    if (file->is_app_root())
561      app_root_by_app_id_[file->app_id()] = file;
562
563    if (file->active() && file->has_synced_details()) {
564      FileByParentAndTitle::key_type key(
565          file->parent_folder_id(), file->synced_details().title());
566      active_file_by_parent_and_title_[key] = file;
567    }
568
569    if (!file->parent_folder_id().empty())
570      files_by_parent_[file->parent_folder_id()].insert(file);
571
572    if (file->dirty())
573      dirty_files_.insert(file);
574  }
575
576  contents->file_metadata.weak_clear();
577}
578
579void MetadataDatabase::RegisterFolderAsAppRoot(
580    const std::string& app_id,
581    const std::string& folder_id,
582    leveldb::WriteBatch* batch) {
583  DriveFileMetadata* folder = file_by_file_id_[folder_id];
584  if (!folder || folder->active() || folder->is_app_root() ||
585      !folder->app_id().empty()) {
586    NOTREACHED();
587    return;
588  }
589
590  folder->set_is_app_root(true);
591  folder->set_app_id(app_id);
592  folder->set_active(true);
593  folder->set_dirty(true);
594  folder->set_needs_folder_listing(true);
595  PutFileToBatch(*folder, batch);
596
597  app_root_by_app_id_[app_id] = folder;
598  if (folder->has_synced_details()) {
599    FileByParentAndTitle::key_type key(
600        folder->parent_folder_id(), folder->synced_details().title());
601    DCHECK(!ContainsKey(active_file_by_parent_and_title_, key));
602    active_file_by_parent_and_title_[key] = folder;
603  }
604  dirty_files_.insert(folder);
605}
606
607void MetadataDatabase::UnregisterFolderAsAppRoot(
608    const std::string& app_id,
609    leveldb::WriteBatch* batch) {
610  DriveFileMetadata* folder = app_root_by_app_id_[app_id];
611  if (!folder || !folder->active() ||
612      folder->app_id() != app_id || !folder->is_app_root()) {
613    NOTREACHED();
614    return;
615  }
616
617  folder->set_active(false);
618  folder->set_is_app_root(false);
619  folder->set_app_id(std::string());
620  PutFileToBatch(*folder, batch);
621
622  // Remove child entries recursively.
623  std::stack<std::string> pending_files;
624  PushChildrenToStack(files_by_parent_, folder->file_id(), &pending_files);
625  while (!pending_files.empty()) {
626    std::string file_id = pending_files.top();
627    pending_files.pop();
628    PushChildrenToStack(files_by_parent_, file_id, &pending_files);
629    RemoveFile(file_id, batch);
630  }
631
632  app_root_by_app_id_.erase(app_id);
633  if (folder->has_synced_details()) {
634    FileByParentAndTitle::key_type key(
635        folder->parent_folder_id(), folder->synced_details().title());
636    active_file_by_parent_and_title_.erase(key);
637  }
638}
639
640void MetadataDatabase::MakeFileActive(const std::string& file_id,
641                                      leveldb::WriteBatch* batch) {
642  DriveFileMetadata* file = file_by_file_id_[file_id];
643  if (!file || file->active()) {
644    NOTREACHED();
645    return;
646  }
647
648  file->set_active(true);
649  if (file->has_synced_details() &&
650      file->synced_details().kind() == KIND_FOLDER)
651    file->set_needs_folder_listing(true);
652  PutFileToBatch(*file, batch);
653
654  if (file->has_synced_details()) {
655    FileByParentAndTitle::key_type key(
656        file->parent_folder_id(), file->synced_details().title());
657    DCHECK(!ContainsKey(active_file_by_parent_and_title_, key));
658    active_file_by_parent_and_title_[key] = file;
659  }
660}
661
662void MetadataDatabase::MakeFileInactive(const std::string& file_id,
663                                        leveldb::WriteBatch* batch) {
664  DriveFileMetadata* file = file_by_file_id_[file_id];
665  if (!file || !file->active()) {
666    NOTREACHED();
667    return;
668  }
669
670  file->set_active(false);
671  PutFileToBatch(*file, batch);
672
673  if (file->has_synced_details()) {
674    FileByParentAndTitle::key_type key(
675        file->parent_folder_id(), file->synced_details().title());
676    DCHECK(ContainsKey(active_file_by_parent_and_title_, key));
677    active_file_by_parent_and_title_.erase(key);
678  }
679}
680
681void MetadataDatabase::RemoveFile(const std::string& file_id,
682                                  leveldb::WriteBatch* batch) {
683  scoped_ptr<DriveFileMetadata> file(file_by_file_id_[file_id]);
684  file_by_file_id_.erase(file_id);
685
686  batch->Delete(file->file_id());
687
688  files_by_parent_[file->parent_folder_id()].erase(file.get());
689  if (file->is_app_root())
690    app_root_by_app_id_.erase(file->app_id());
691  if (file->active() && file->has_synced_details()) {
692    FileByParentAndTitle::key_type key(
693        file->parent_folder_id(), file->synced_details().title());
694    active_file_by_parent_and_title_.erase(key);
695  }
696  dirty_files_.erase(file.get());
697}
698
699void MetadataDatabase::WriteToDatabase(scoped_ptr<leveldb::WriteBatch> batch,
700                                       const SyncStatusCallback& callback) {
701  base::PostTaskAndReplyWithResult(
702      task_runner_.get(),
703      FROM_HERE,
704      base::Bind(&leveldb::DB::Write,
705                 base::Unretained(db_.get()),
706                 leveldb::WriteOptions(),
707                 base::Owned(batch.release())),
708      base::Bind(&AdaptLevelDBStatusToSyncStatusCode, callback));
709}
710
711}  // namespace drive_backend
712}  // namespace sync_file_system
713