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