Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / sync_file_system / drive_backend_v1 / drive_metadata_store.cc
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_v1/drive_metadata_store.h"
6
7 #include <utility>
8 #include <vector>
9
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/files/file_path.h"
13 #include "base/location.h"
14 #include "base/sequenced_task_runner.h"
15 #include "base/stl_util.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_util.h"
18 #include "base/strings/stringprintf.h"
19 #include "base/task_runner_util.h"
20 #include "base/values.h"
21 #include "chrome/browser/sync_file_system/drive_backend/metadata_db_migration_util.h"
22 #include "chrome/browser/sync_file_system/drive_backend_v1/drive_file_sync_service.h"
23 #include "chrome/browser/sync_file_system/drive_backend_v1/drive_file_sync_util.h"
24 #include "chrome/browser/sync_file_system/logger.h"
25 #include "chrome/browser/sync_file_system/sync_file_system.pb.h"
26 #include "chrome/browser/sync_file_system/syncable_file_system_util.h"
27 #include "third_party/leveldatabase/src/include/leveldb/db.h"
28 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
29 #include "url/gurl.h"
30 #include "webkit/browser/fileapi/file_system_url.h"
31 #include "webkit/common/fileapi/file_system_util.h"
32
33 using fileapi::FileSystemURL;
34
35 namespace sync_file_system {
36
37 typedef DriveMetadataStore::MetadataMap MetadataMap;
38 typedef DriveMetadataStore::OriginByResourceId OriginByResourceId;
39 typedef DriveMetadataStore::PathToMetadata PathToMetadata;
40 typedef DriveMetadataStore::ResourceIdByOrigin ResourceIdByOrigin;
41
42 const base::FilePath::CharType DriveMetadataStore::kDatabaseName[] =
43     FILE_PATH_LITERAL("DriveMetadata");
44
45 struct DBContents {
46   SyncStatusCode status;
47   scoped_ptr<leveldb::DB> db;
48   bool created;
49
50   int64 largest_changestamp;
51   DriveMetadataStore::MetadataMap metadata_map;
52   std::string sync_root_directory_resource_id;
53   ResourceIdByOrigin incremental_sync_origins;
54   ResourceIdByOrigin disabled_origins;
55
56   DBContents()
57       : status(SYNC_STATUS_UNKNOWN),
58         created(false),
59         largest_changestamp(0) {
60   }
61 };
62
63 namespace {
64
65 const char kDatabaseVersionKey[] = "VERSION";
66 const int64 kCurrentDatabaseVersion = 2;
67 const char kChangeStampKey[] = "CHANGE_STAMP";
68 const char kSyncRootDirectoryKey[] = "SYNC_ROOT_DIR";
69 const char kDriveMetadataKeyPrefix[] = "METADATA: ";
70 const char kMetadataKeySeparator = ' ';
71 const char kDriveIncrementalSyncOriginKeyPrefix[] = "ISYNC_ORIGIN: ";
72 const char kDriveDisabledOriginKeyPrefix[] = "DISABLED_ORIGIN: ";
73
74 enum OriginSyncType {
75   INCREMENTAL_SYNC_ORIGIN,
76   DISABLED_ORIGIN
77 };
78
79 std::string RemovePrefix(const std::string& str, const std::string& prefix) {
80   if (StartsWithASCII(str, prefix, true))
81     return str.substr(prefix.size());
82   return str;
83 }
84
85 std::string OriginAndPathToMetadataKey(const GURL& origin,
86                                        const base::FilePath& path) {
87   return kDriveMetadataKeyPrefix + origin.spec() +
88       kMetadataKeySeparator + path.AsUTF8Unsafe();
89 }
90
91 std::string FileSystemURLToMetadataKey(const FileSystemURL& url) {
92   return OriginAndPathToMetadataKey(url.origin(), url.path());
93 }
94
95 void MetadataKeyToOriginAndPath(const std::string& metadata_key,
96                                 GURL* origin,
97                                 base::FilePath* path) {
98   std::string key_body(RemovePrefix(metadata_key, kDriveMetadataKeyPrefix));
99   size_t separator_position = key_body.find(kMetadataKeySeparator);
100   *origin = GURL(key_body.substr(0, separator_position));
101   *path = base::FilePath::FromUTF8Unsafe(
102       key_body.substr(separator_position + 1));
103 }
104
105 bool UpdateResourceIdMap(ResourceIdByOrigin* map,
106                          OriginByResourceId* reverse_map,
107                          const GURL& origin,
108                          const std::string& resource_id) {
109   ResourceIdByOrigin::iterator found = map->find(origin);
110   if (found == map->end())
111     return false;
112   reverse_map->erase(found->second);
113   reverse_map->insert(std::make_pair(resource_id, origin));
114
115   found->second = resource_id;
116   return true;
117 }
118
119 ////////////////////////////////////////////////////////////////////////////////
120
121 bool IsDBEmpty(leveldb::DB* db) {
122   DCHECK(db);
123   scoped_ptr<leveldb::Iterator> itr(db->NewIterator(leveldb::ReadOptions()));
124   itr->SeekToFirst();
125   return !itr->Valid();
126 }
127
128 scoped_ptr<leveldb::DB> OpenDatabase(const base::FilePath& path,
129                                      SyncStatusCode* status,
130                                      bool* created) {
131   DCHECK(status);
132   DCHECK(created);
133
134   leveldb::Options options;
135   options.max_open_files = 0;  // Use minimum.
136   options.create_if_missing = true;
137   leveldb::DB* db = NULL;
138   leveldb::Status db_status = leveldb::DB::Open(
139       options, path.AsUTF8Unsafe(), &db);
140   if (db_status.ok()) {
141     *created = IsDBEmpty(db);
142   } else {
143     delete db;
144     db = NULL;
145   }
146   *status = LevelDBStatusToSyncStatusCode(db_status);
147
148   return make_scoped_ptr(db);
149 }
150
151 SyncStatusCode WriteInitialData(leveldb::DB* db) {
152   DCHECK(db);
153   return LevelDBStatusToSyncStatusCode(db->Put(
154       leveldb::WriteOptions(),
155       kDatabaseVersionKey,
156       base::Int64ToString(kCurrentDatabaseVersion)));
157 }
158
159 SyncStatusCode MigrateDatabaseIfNeeded(leveldb::DB* db) {
160   DCHECK(db);
161   std::string value;
162   leveldb::Status status = db->Get(leveldb::ReadOptions(),
163                                    kDatabaseVersionKey, &value);
164   int64 version = 0;
165   if (status.ok()) {
166     if (!base::StringToInt64(value, &version))
167       return SYNC_DATABASE_ERROR_FAILED;
168   } else {
169     if (!status.IsNotFound())
170       return SYNC_DATABASE_ERROR_FAILED;
171   }
172
173   switch (version) {
174     case 0:
175       drive_backend::MigrateDatabaseFromV0ToV1(db);
176       // fall-through
177     case 1:
178       drive_backend::MigrateDatabaseFromV1ToV2(db);
179       // fall-through
180     case 2:
181       DCHECK_EQ(2, kCurrentDatabaseVersion);
182       return SYNC_STATUS_OK;
183     default:
184       return SYNC_DATABASE_ERROR_FAILED;
185   }
186 }
187
188 SyncStatusCode ReadContents(DBContents* contents) {
189   DCHECK(contents);
190   DCHECK(contents->db);
191
192   contents->largest_changestamp = 0;
193   contents->metadata_map.clear();
194   contents->incremental_sync_origins.clear();
195
196   scoped_ptr<leveldb::Iterator> itr(
197       contents->db->NewIterator(leveldb::ReadOptions()));
198   for (itr->SeekToFirst(); itr->Valid(); itr->Next()) {
199     std::string key = itr->key().ToString();
200     if (key == kChangeStampKey) {
201       bool success = base::StringToInt64(itr->value().ToString(),
202                                          &contents->largest_changestamp);
203       DCHECK(success);
204       continue;
205     }
206
207     if (key == kSyncRootDirectoryKey) {
208       std::string resource_id = itr->value().ToString();
209       if (IsDriveAPIDisabled())
210         resource_id = drive_backend::AddWapiFolderPrefix(resource_id);
211       contents->sync_root_directory_resource_id = resource_id;
212       continue;
213     }
214
215     if (StartsWithASCII(key, kDriveMetadataKeyPrefix, true)) {
216       GURL origin;
217       base::FilePath path;
218       MetadataKeyToOriginAndPath(key, &origin, &path);
219
220       DriveMetadata metadata;
221       bool success = metadata.ParseFromString(itr->value().ToString());
222       DCHECK(success);
223
224       if (IsDriveAPIDisabled()) {
225         metadata.set_resource_id(drive_backend::AddWapiIdPrefix(
226             metadata.resource_id(), metadata.type()));
227       }
228
229       success = contents->metadata_map[origin].insert(
230           std::make_pair(path, metadata)).second;
231       DCHECK(success);
232       continue;
233     }
234
235     if (StartsWithASCII(key, kDriveIncrementalSyncOriginKeyPrefix, true)) {
236       GURL origin(RemovePrefix(key, kDriveIncrementalSyncOriginKeyPrefix));
237       DCHECK(origin.is_valid());
238
239       std::string origin_resource_id = IsDriveAPIDisabled()
240           ? drive_backend::AddWapiFolderPrefix(itr->value().ToString())
241           : itr->value().ToString();
242
243       DCHECK(!ContainsKey(contents->incremental_sync_origins, origin));
244       contents->incremental_sync_origins[origin] = origin_resource_id;
245       continue;
246     }
247
248     if (StartsWithASCII(key, kDriveDisabledOriginKeyPrefix, true)) {
249       GURL origin(RemovePrefix(key, kDriveDisabledOriginKeyPrefix));
250       DCHECK(origin.is_valid());
251
252       std::string origin_resource_id = IsDriveAPIDisabled()
253           ? drive_backend::AddWapiFolderPrefix(itr->value().ToString())
254           : itr->value().ToString();
255
256       DCHECK(!ContainsKey(contents->disabled_origins, origin));
257       contents->disabled_origins[origin] = origin_resource_id;
258       continue;
259     }
260   }
261
262   return SYNC_STATUS_OK;
263 }
264
265 scoped_ptr<DBContents> LoadDBContents(const base::FilePath& db_path) {
266   scoped_ptr<DBContents> contents(new DBContents);
267   contents->db = OpenDatabase(db_path,
268                               &contents->status,
269                               &contents->created);
270   if (contents->status != SYNC_STATUS_OK)
271     return contents.Pass();
272
273   if (contents->created) {
274     contents->status = WriteInitialData(contents->db.get());
275     if (contents->status != SYNC_STATUS_OK)
276       return contents.Pass();
277   } else {
278     contents->status = MigrateDatabaseIfNeeded(contents->db.get());
279     if (contents->status != SYNC_STATUS_OK)
280       return contents.Pass();
281   }
282
283   contents->status = ReadContents(contents.get());
284   return contents.Pass();
285 }
286
287 ////////////////////////////////////////////////////////////////////////////////
288
289 // Returns a key string for the given origin.
290 // For example, when |origin| is "http://www.example.com" and |sync_type| is
291 // BATCH_SYNC_ORIGIN, returns "BSYNC_ORIGIN: http://www.example.com".
292 std::string CreateKeyForOriginRoot(const GURL& origin,
293                                    OriginSyncType sync_type) {
294   DCHECK(origin.is_valid());
295   switch (sync_type) {
296     case INCREMENTAL_SYNC_ORIGIN:
297       return kDriveIncrementalSyncOriginKeyPrefix + origin.spec();
298     case DISABLED_ORIGIN:
299       return kDriveDisabledOriginKeyPrefix + origin.spec();
300   }
301   NOTREACHED();
302   return std::string();
303 }
304
305 void AddOriginsToVector(std::vector<GURL>* all_origins,
306                         const ResourceIdByOrigin& resource_map) {
307   for (ResourceIdByOrigin::const_iterator itr = resource_map.begin();
308        itr != resource_map.end();
309        ++itr) {
310     all_origins->push_back(itr->first);
311   }
312 }
313
314 void InsertReverseMap(const ResourceIdByOrigin& forward_map,
315                       OriginByResourceId* backward_map) {
316   for (ResourceIdByOrigin::const_iterator itr = forward_map.begin();
317        itr != forward_map.end(); ++itr)
318     backward_map->insert(std::make_pair(itr->second, itr->first));
319 }
320
321 bool EraseIfExists(ResourceIdByOrigin* map,
322                    const GURL& origin,
323                    std::string* resource_id) {
324   ResourceIdByOrigin::iterator found = map->find(origin);
325   if (found == map->end())
326     return false;
327   *resource_id = found->second;
328   map->erase(found);
329   return true;
330 }
331
332 void AppendMetadataDeletionToBatch(const MetadataMap& metadata_map,
333                                    const GURL& origin,
334                                    leveldb::WriteBatch* batch) {
335   MetadataMap::const_iterator found = metadata_map.find(origin);
336   if (found == metadata_map.end())
337     return;
338
339   for (PathToMetadata::const_iterator itr = found->second.begin();
340        itr != found->second.end(); ++itr)
341     batch->Delete(OriginAndPathToMetadataKey(origin, itr->first));
342 }
343
344 std::string DriveTypeToString(DriveMetadata_ResourceType drive_type) {
345   switch (drive_type) {
346     case DriveMetadata_ResourceType_RESOURCE_TYPE_FILE:
347       return "file";
348     case DriveMetadata_ResourceType_RESOURCE_TYPE_FOLDER:
349       return "folder";
350   }
351
352   NOTREACHED();
353   return "unknown";
354 }
355
356 }  // namespace
357
358 DriveMetadataStore::DriveMetadataStore(
359     const base::FilePath& base_dir,
360     base::SequencedTaskRunner* file_task_runner)
361     : file_task_runner_(file_task_runner),
362       base_dir_(base_dir),
363       db_status_(SYNC_STATUS_UNKNOWN),
364       largest_changestamp_(0) {
365   DCHECK(file_task_runner);
366 }
367
368 DriveMetadataStore::~DriveMetadataStore() {
369   DCHECK(CalledOnValidThread());
370   file_task_runner_->DeleteSoon(FROM_HERE, db_.release());
371 }
372
373 void DriveMetadataStore::Initialize(const InitializationCallback& callback) {
374   DCHECK(CalledOnValidThread());
375   base::PostTaskAndReplyWithResult(
376       file_task_runner_.get(), FROM_HERE,
377       base::Bind(&LoadDBContents, base_dir_.Append(kDatabaseName)),
378       base::Bind(&DriveMetadataStore::DidInitialize, AsWeakPtr(), callback));
379 }
380
381 void DriveMetadataStore::DidInitialize(const InitializationCallback& callback,
382                                        scoped_ptr<DBContents> contents) {
383   DCHECK(CalledOnValidThread());
384   DCHECK(contents);
385
386   db_status_ = contents->status;
387   if (db_status_ != SYNC_STATUS_OK) {
388     callback.Run(db_status_, false);
389     file_task_runner_->DeleteSoon(FROM_HERE, contents.release());
390     return;
391   }
392
393   db_ = contents->db.Pass();
394   largest_changestamp_ = contents->largest_changestamp;
395   metadata_map_.swap(contents->metadata_map);
396   sync_root_directory_resource_id_ = contents->sync_root_directory_resource_id;
397   incremental_sync_origins_.swap(contents->incremental_sync_origins);
398   disabled_origins_.swap(contents->disabled_origins);
399
400   origin_by_resource_id_.clear();
401   InsertReverseMap(incremental_sync_origins_, &origin_by_resource_id_);
402   InsertReverseMap(disabled_origins_, &origin_by_resource_id_);
403
404   callback.Run(db_status_, contents->created);
405 }
406
407 void DriveMetadataStore::SetLargestChangeStamp(
408     int64 largest_changestamp,
409     const SyncStatusCallback& callback) {
410   DCHECK(CalledOnValidThread());
411   DCHECK_EQ(SYNC_STATUS_OK, db_status_);
412   largest_changestamp_ = largest_changestamp;
413
414   scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
415   batch->Put(kChangeStampKey, base::Int64ToString(largest_changestamp));
416   return WriteToDB(batch.Pass(), callback);
417 }
418
419 int64 DriveMetadataStore::GetLargestChangeStamp() const {
420   DCHECK(CalledOnValidThread());
421   DCHECK_EQ(SYNC_STATUS_OK, db_status_);
422   return largest_changestamp_;
423 }
424
425 void DriveMetadataStore::UpdateEntry(
426     const FileSystemURL& url,
427     const DriveMetadata& metadata,
428     const SyncStatusCallback& callback) {
429   DCHECK(CalledOnValidThread());
430   DCHECK_EQ(SYNC_STATUS_OK, db_status_);
431   DCHECK(!metadata.conflicted() || !metadata.to_be_fetched());
432
433   std::pair<PathToMetadata::iterator, bool> result =
434       metadata_map_[url.origin()].insert(std::make_pair(url.path(), metadata));
435   if (!result.second)
436     result.first->second = metadata;
437
438   std::string value;
439   if (IsDriveAPIDisabled()) {
440     DriveMetadata metadata_in_db(metadata);
441     metadata_in_db.set_resource_id(
442         drive_backend::RemoveWapiIdPrefix(metadata.resource_id()));
443     bool success = metadata_in_db.SerializeToString(&value);
444     DCHECK(success);
445   } else {
446     bool success = metadata.SerializeToString(&value);
447     DCHECK(success);
448   }
449
450   scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
451   batch->Put(FileSystemURLToMetadataKey(url), value);
452   WriteToDB(batch.Pass(), callback);
453 }
454
455 void DriveMetadataStore::DeleteEntry(
456     const FileSystemURL& url,
457     const SyncStatusCallback& callback) {
458   DCHECK(CalledOnValidThread());
459   MetadataMap::iterator found = metadata_map_.find(url.origin());
460   if (found == metadata_map_.end()) {
461     RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND));
462     return;
463   }
464
465   if (found->second.erase(url.path()) == 1) {
466     scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
467     batch->Delete(FileSystemURLToMetadataKey(url));
468     WriteToDB(batch.Pass(), callback);
469     return;
470   }
471
472   RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND));
473 }
474
475 SyncStatusCode DriveMetadataStore::ReadEntry(const FileSystemURL& url,
476                                              DriveMetadata* metadata) const {
477   DCHECK(CalledOnValidThread());
478   DCHECK(metadata);
479
480   MetadataMap::const_iterator found_origin = metadata_map_.find(url.origin());
481   if (found_origin == metadata_map_.end())
482     return SYNC_DATABASE_ERROR_NOT_FOUND;
483
484   PathToMetadata::const_iterator found = found_origin->second.find(url.path());
485   if (found == found_origin->second.end())
486     return SYNC_DATABASE_ERROR_NOT_FOUND;
487
488   *metadata = found->second;
489   return SYNC_STATUS_OK;
490 }
491
492 void DriveMetadataStore::AddIncrementalSyncOrigin(
493     const GURL& origin,
494     const std::string& resource_id) {
495   DCHECK(CalledOnValidThread());
496   DCHECK(!IsIncrementalSyncOrigin(origin));
497   DCHECK(!IsOriginDisabled(origin));
498   DCHECK_EQ(SYNC_STATUS_OK, db_status_);
499
500   incremental_sync_origins_.insert(std::make_pair(origin, resource_id));
501   origin_by_resource_id_.insert(std::make_pair(resource_id, origin));
502
503   scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
504   batch->Delete(CreateKeyForOriginRoot(origin, DISABLED_ORIGIN));
505   batch->Put(CreateKeyForOriginRoot(origin, INCREMENTAL_SYNC_ORIGIN),
506              drive_backend::RemoveWapiIdPrefix(resource_id));
507   WriteToDB(batch.Pass(),
508             base::Bind(&DriveMetadataStore::UpdateDBStatus, AsWeakPtr()));
509 }
510
511 void DriveMetadataStore::SetSyncRootDirectory(const std::string& resource_id) {
512   DCHECK(CalledOnValidThread());
513
514   sync_root_directory_resource_id_ = resource_id;
515
516   scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
517   batch->Put(kSyncRootDirectoryKey,
518              drive_backend::RemoveWapiIdPrefix(resource_id));
519   return WriteToDB(batch.Pass(),
520                    base::Bind(&DriveMetadataStore::UpdateDBStatus,
521                               AsWeakPtr()));
522 }
523
524 void DriveMetadataStore::SetOriginRootDirectory(
525     const GURL& origin,
526     const std::string& resource_id) {
527   DCHECK(CalledOnValidThread());
528   DCHECK(IsKnownOrigin(origin));
529
530   OriginSyncType sync_type;
531   if (UpdateResourceIdMap(
532       &incremental_sync_origins_, &origin_by_resource_id_,
533       origin, resource_id)) {
534     sync_type = INCREMENTAL_SYNC_ORIGIN;
535   } else if (UpdateResourceIdMap(&disabled_origins_, &origin_by_resource_id_,
536                                  origin, resource_id)) {
537     sync_type = DISABLED_ORIGIN;
538   } else {
539     return;
540   }
541
542   std::string key = CreateKeyForOriginRoot(origin, sync_type);
543   DCHECK(!key.empty());
544
545   scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
546   batch->Put(key, drive_backend::RemoveWapiIdPrefix(resource_id));
547   WriteToDB(batch.Pass(),
548             base::Bind(&DriveMetadataStore::UpdateDBStatus, AsWeakPtr()));
549 }
550
551 bool DriveMetadataStore::IsKnownOrigin(const GURL& origin) const {
552   DCHECK(CalledOnValidThread());
553   return IsIncrementalSyncOrigin(origin) || IsOriginDisabled(origin);
554 }
555
556 bool DriveMetadataStore::IsIncrementalSyncOrigin(const GURL& origin) const {
557   DCHECK(CalledOnValidThread());
558   return ContainsKey(incremental_sync_origins_, origin);
559 }
560
561 bool DriveMetadataStore::IsOriginDisabled(const GURL& origin) const {
562   DCHECK(CalledOnValidThread());
563   return ContainsKey(disabled_origins_, origin);
564 }
565
566 void DriveMetadataStore::EnableOrigin(
567     const GURL& origin,
568     const SyncStatusCallback& callback) {
569   DCHECK(CalledOnValidThread());
570
571   std::map<GURL, std::string>::iterator found = disabled_origins_.find(origin);
572   if (found == disabled_origins_.end()) {
573     RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND));
574     // |origin| has not been registered yet.
575     return;
576   }
577   disabled_origins_.erase(found);
578
579   // |origin| goes back to DriveFileSyncService::pending_batch_sync_origins_
580   // only and is not stored in drive_metadata_store.
581   found = incremental_sync_origins_.find(origin);
582   if (found != incremental_sync_origins_.end())
583     incremental_sync_origins_.erase(found);
584
585   scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
586   batch->Delete(CreateKeyForOriginRoot(origin, INCREMENTAL_SYNC_ORIGIN));
587   batch->Delete(CreateKeyForOriginRoot(origin, DISABLED_ORIGIN));
588   WriteToDB(batch.Pass(), callback);
589 }
590
591 void DriveMetadataStore::DisableOrigin(
592     const GURL& origin,
593     const SyncStatusCallback& callback) {
594   DCHECK(CalledOnValidThread());
595
596   std::string resource_id;
597   if (!EraseIfExists(&incremental_sync_origins_, origin, &resource_id)) {
598     RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND));
599     return;
600   }
601   disabled_origins_[origin] = resource_id;
602
603   scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
604   batch->Delete(CreateKeyForOriginRoot(origin, INCREMENTAL_SYNC_ORIGIN));
605   batch->Put(CreateKeyForOriginRoot(origin, DISABLED_ORIGIN),
606              drive_backend::RemoveWapiIdPrefix(resource_id));
607   AppendMetadataDeletionToBatch(metadata_map_, origin, batch.get());
608   metadata_map_.erase(origin);
609
610   WriteToDB(batch.Pass(), callback);
611 }
612
613 void DriveMetadataStore::RemoveOrigin(
614     const GURL& origin,
615     const SyncStatusCallback& callback) {
616   DCHECK(CalledOnValidThread());
617
618   std::string resource_id;
619   if (!EraseIfExists(&incremental_sync_origins_, origin, &resource_id) &&
620       !EraseIfExists(&disabled_origins_, origin, &resource_id)) {
621     RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND));
622     return;
623   }
624   origin_by_resource_id_.erase(resource_id);
625
626   scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
627   batch->Delete(CreateKeyForOriginRoot(origin, INCREMENTAL_SYNC_ORIGIN));
628   batch->Delete(CreateKeyForOriginRoot(origin, DISABLED_ORIGIN));
629   AppendMetadataDeletionToBatch(metadata_map_, origin, batch.get());
630   metadata_map_.erase(origin);
631
632   WriteToDB(batch.Pass(), callback);
633 }
634
635 void DriveMetadataStore::WriteToDB(scoped_ptr<leveldb::WriteBatch> batch,
636                                    const SyncStatusCallback& callback) {
637   DCHECK(CalledOnValidThread());
638   if (db_status_ != SYNC_STATUS_OK &&
639       db_status_ != SYNC_DATABASE_ERROR_NOT_FOUND) {
640     RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_FAILED));
641     return;
642   }
643
644   DCHECK(db_);
645   base::PostTaskAndReplyWithResult(
646       file_task_runner_.get(),
647       FROM_HERE,
648       base::Bind(&leveldb::DB::Write,
649                  base::Unretained(db_.get()),
650                  leveldb::WriteOptions(),
651                  base::Owned(batch.release())),
652       base::Bind(&DriveMetadataStore::UpdateDBStatusAndInvokeCallback,
653                  AsWeakPtr(),
654                  callback));
655 }
656
657 void DriveMetadataStore::UpdateDBStatus(SyncStatusCode status) {
658   DCHECK(CalledOnValidThread());
659   if (db_status_ != SYNC_STATUS_OK &&
660       db_status_ != SYNC_DATABASE_ERROR_NOT_FOUND) {
661     // TODO(tzik): Handle database corruption. http://crbug.com/153709
662     db_status_ = status;
663     util::Log(logging::LOG_WARNING,
664               FROM_HERE,
665               "DriveMetadataStore turned to wrong state: %s",
666               SyncStatusCodeToString(status));
667     return;
668   }
669   db_status_ = SYNC_STATUS_OK;
670 }
671
672 void DriveMetadataStore::UpdateDBStatusAndInvokeCallback(
673     const SyncStatusCallback& callback,
674     const leveldb::Status& leveldb_status) {
675   SyncStatusCode status = LevelDBStatusToSyncStatusCode(leveldb_status);
676   UpdateDBStatus(status);
677   callback.Run(status);
678 }
679
680 SyncStatusCode DriveMetadataStore::GetConflictURLs(
681     fileapi::FileSystemURLSet* urls) const {
682   DCHECK(CalledOnValidThread());
683   DCHECK_EQ(SYNC_STATUS_OK, db_status_);
684
685   urls->clear();
686   for (MetadataMap::const_iterator origin_itr = metadata_map_.begin();
687        origin_itr != metadata_map_.end();
688        ++origin_itr) {
689     for (PathToMetadata::const_iterator itr = origin_itr->second.begin();
690          itr != origin_itr->second.end();
691          ++itr) {
692       if (itr->second.conflicted()) {
693         urls->insert(CreateSyncableFileSystemURL(
694             origin_itr->first, itr->first));
695       }
696     }
697   }
698   return SYNC_STATUS_OK;
699 }
700
701 SyncStatusCode DriveMetadataStore::GetToBeFetchedFiles(
702     URLAndDriveMetadataList* list) const {
703   DCHECK(CalledOnValidThread());
704   DCHECK_EQ(SYNC_STATUS_OK, db_status_);
705
706   list->clear();
707   for (MetadataMap::const_iterator origin_itr = metadata_map_.begin();
708        origin_itr != metadata_map_.end();
709        ++origin_itr) {
710     for (PathToMetadata::const_iterator itr = origin_itr->second.begin();
711          itr != origin_itr->second.end();
712          ++itr) {
713       if (itr->second.to_be_fetched()) {
714         FileSystemURL url = CreateSyncableFileSystemURL(
715             origin_itr->first, itr->first);
716         list->push_back(std::make_pair(url, itr->second));
717       }
718     }
719   }
720   return SYNC_STATUS_OK;
721 }
722
723 std::string DriveMetadataStore::GetResourceIdForOrigin(
724     const GURL& origin) const {
725   DCHECK(CalledOnValidThread());
726
727   // If we don't have valid root directory (this could be reset even after
728   // initialized) just return empty string, as the origin directories
729   // in the root directory must have become invalid now too.
730   if (sync_root_directory().empty())
731     return std::string();
732
733   ResourceIdByOrigin::const_iterator found =
734       incremental_sync_origins_.find(origin);
735   if (found != incremental_sync_origins_.end())
736     return found->second;
737
738   found = disabled_origins_.find(origin);
739   if (found != disabled_origins_.end())
740     return found->second;
741
742   return std::string();
743 }
744
745 void DriveMetadataStore::GetAllOrigins(std::vector<GURL>* origins) {
746   DCHECK(CalledOnValidThread());
747   DCHECK(origins);
748   origins->clear();
749   origins->reserve(incremental_sync_origins_.size() +
750                    disabled_origins_.size());
751   AddOriginsToVector(origins, incremental_sync_origins_);
752   AddOriginsToVector(origins, disabled_origins_);
753 }
754
755 bool DriveMetadataStore::GetOriginByOriginRootDirectoryId(
756     const std::string& resource_id,
757     GURL* origin) {
758   DCHECK(CalledOnValidThread());
759   DCHECK(origin);
760
761   OriginByResourceId::iterator found = origin_by_resource_id_.find(resource_id);
762   if (found == origin_by_resource_id_.end())
763     return false;
764   *origin = found->second;
765   return true;
766 }
767
768 scoped_ptr<base::ListValue> DriveMetadataStore::DumpFiles(const GURL& origin) {
769   DCHECK(CalledOnValidThread());
770
771   scoped_ptr<base::ListValue> files(new base::ListValue);
772
773   MetadataMap::const_iterator found = metadata_map_.find(origin);
774   if (found == metadata_map_.end())
775     return make_scoped_ptr(new base::ListValue);
776
777   for (PathToMetadata::const_iterator itr = found->second.begin();
778        itr != found->second.end();
779        ++itr) {
780     // Convert Drive specific metadata to Common File metadata object.
781     const DriveMetadata& metadata = itr->second;
782
783     base::DictionaryValue* file = new base::DictionaryValue;
784     file->SetString("path", itr->first.AsUTF8Unsafe());
785     file->SetString("title", itr->first.BaseName().AsUTF8Unsafe());
786     file->SetString("type", DriveTypeToString(metadata.type()));
787
788     base::DictionaryValue* details = new base::DictionaryValue;
789     details->SetString("resource_id", metadata.resource_id());
790     details->SetString("md5", metadata.md5_checksum());
791     details->SetString("dirty", metadata.to_be_fetched() ? "true" : "false");
792
793     file->Set("details", details);
794     files->Append(file);
795   }
796
797   return files.Pass();
798 }
799
800 }  // namespace sync_file_system