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
DBContentssync_file_system::DBContents57 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
RemovePrefix(const std::string & str,const std::string & prefix)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
OriginAndPathToMetadataKey(const GURL & origin,const base::FilePath & path)86 std::string OriginAndPathToMetadataKey(const GURL& origin,
87 const base::FilePath& path) {
88 return kDriveMetadataKeyPrefix + origin.spec() +
89 kMetadataKeySeparator + path.AsUTF8Unsafe();
90 }
91
FileSystemURLToMetadataKey(const FileSystemURL & url)92 std::string FileSystemURLToMetadataKey(const FileSystemURL& url) {
93 return OriginAndPathToMetadataKey(url.origin(), url.path());
94 }
95
MetadataKeyToOriginAndPath(const std::string & metadata_key,GURL * origin,base::FilePath * path)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
UpdateResourceIdMap(ResourceIdByOrigin * map,OriginByResourceId * reverse_map,const GURL & origin,const std::string & resource_id)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
IsDBEmpty(leveldb::DB * db)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
OpenDatabase(const base::FilePath & path,SyncStatusCode * status,bool * created)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
WriteInitialData(leveldb::DB * db)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
MigrateDatabaseIfNeeded(leveldb::DB * db)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
ReadContents(DBContents * contents)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
LoadDBContents(const base::FilePath & db_path)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".
CreateKeyForOriginRoot(const GURL & origin,OriginSyncType sync_type)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
AddOriginsToVector(std::vector<GURL> * all_origins,const ResourceIdByOrigin & resource_map)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
InsertReverseMap(const ResourceIdByOrigin & forward_map,OriginByResourceId * backward_map)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
EraseIfExists(ResourceIdByOrigin * map,const GURL & origin,std::string * resource_id)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
AppendMetadataDeletionToBatch(const MetadataMap & metadata_map,const GURL & origin,leveldb::WriteBatch * batch)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
DriveTypeToString(DriveMetadata_ResourceType drive_type)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
DriveMetadataStore(const base::FilePath & base_dir,base::SequencedTaskRunner * file_task_runner)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
~DriveMetadataStore()369 DriveMetadataStore::~DriveMetadataStore() {
370 DCHECK(CalledOnValidThread());
371 file_task_runner_->DeleteSoon(FROM_HERE, db_.release());
372 }
373
Initialize(const InitializationCallback & callback)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
DidInitialize(const InitializationCallback & callback,scoped_ptr<DBContents> contents)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
SetLargestChangeStamp(int64 largest_changestamp,const SyncStatusCallback & callback)408 void DriveMetadataStore::SetLargestChangeStamp(
409 int64 largest_changestamp,
410 const SyncStatusCallback& callback) {
411 DCHECK(CalledOnValidThread());
412 DCHECK_EQ(SYNC_STATUS_OK, db_status_);
413 largest_changestamp_ = largest_changestamp;
414
415 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
416 batch->Put(kChangeStampKey, base::Int64ToString(largest_changestamp));
417 return WriteToDB(batch.Pass(), callback);
418 }
419
GetLargestChangeStamp() const420 int64 DriveMetadataStore::GetLargestChangeStamp() const {
421 DCHECK(CalledOnValidThread());
422 DCHECK_EQ(SYNC_STATUS_OK, db_status_);
423 return largest_changestamp_;
424 }
425
UpdateEntry(const FileSystemURL & url,const DriveMetadata & metadata,const SyncStatusCallback & callback)426 void DriveMetadataStore::UpdateEntry(
427 const FileSystemURL& url,
428 const DriveMetadata& metadata,
429 const SyncStatusCallback& callback) {
430 DCHECK(CalledOnValidThread());
431 DCHECK_EQ(SYNC_STATUS_OK, db_status_);
432 DCHECK(!metadata.conflicted() || !metadata.to_be_fetched());
433
434 std::pair<PathToMetadata::iterator, bool> result =
435 metadata_map_[url.origin()].insert(std::make_pair(url.path(), metadata));
436 if (!result.second)
437 result.first->second = metadata;
438
439 std::string value;
440 if (IsDriveAPIDisabled()) {
441 DriveMetadata metadata_in_db(metadata);
442 metadata_in_db.set_resource_id(
443 drive_backend::RemoveWapiIdPrefix(metadata.resource_id()));
444 bool success = metadata_in_db.SerializeToString(&value);
445 DCHECK(success);
446 } else {
447 bool success = metadata.SerializeToString(&value);
448 DCHECK(success);
449 }
450
451 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
452 batch->Put(FileSystemURLToMetadataKey(url), value);
453 WriteToDB(batch.Pass(), callback);
454 }
455
DeleteEntry(const FileSystemURL & url,const SyncStatusCallback & callback)456 void DriveMetadataStore::DeleteEntry(
457 const FileSystemURL& url,
458 const SyncStatusCallback& callback) {
459 DCHECK(CalledOnValidThread());
460 MetadataMap::iterator found = metadata_map_.find(url.origin());
461 if (found == metadata_map_.end()) {
462 RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND));
463 return;
464 }
465
466 if (found->second.erase(url.path()) == 1) {
467 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
468 batch->Delete(FileSystemURLToMetadataKey(url));
469 WriteToDB(batch.Pass(), callback);
470 return;
471 }
472
473 RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND));
474 }
475
ReadEntry(const FileSystemURL & url,DriveMetadata * metadata) const476 SyncStatusCode DriveMetadataStore::ReadEntry(const FileSystemURL& url,
477 DriveMetadata* metadata) const {
478 DCHECK(CalledOnValidThread());
479 DCHECK(metadata);
480
481 MetadataMap::const_iterator found_origin = metadata_map_.find(url.origin());
482 if (found_origin == metadata_map_.end())
483 return SYNC_DATABASE_ERROR_NOT_FOUND;
484
485 PathToMetadata::const_iterator found = found_origin->second.find(url.path());
486 if (found == found_origin->second.end())
487 return SYNC_DATABASE_ERROR_NOT_FOUND;
488
489 *metadata = found->second;
490 return SYNC_STATUS_OK;
491 }
492
AddIncrementalSyncOrigin(const GURL & origin,const std::string & resource_id)493 void DriveMetadataStore::AddIncrementalSyncOrigin(
494 const GURL& origin,
495 const std::string& resource_id) {
496 DCHECK(CalledOnValidThread());
497 DCHECK(!IsIncrementalSyncOrigin(origin));
498 DCHECK(!IsOriginDisabled(origin));
499 DCHECK_EQ(SYNC_STATUS_OK, db_status_);
500
501 incremental_sync_origins_.insert(std::make_pair(origin, resource_id));
502 origin_by_resource_id_.insert(std::make_pair(resource_id, origin));
503
504 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
505 batch->Delete(CreateKeyForOriginRoot(origin, DISABLED_ORIGIN));
506 batch->Put(CreateKeyForOriginRoot(origin, INCREMENTAL_SYNC_ORIGIN),
507 drive_backend::RemoveWapiIdPrefix(resource_id));
508 WriteToDB(batch.Pass(),
509 base::Bind(&DriveMetadataStore::UpdateDBStatus, AsWeakPtr()));
510 }
511
SetSyncRootDirectory(const std::string & resource_id)512 void DriveMetadataStore::SetSyncRootDirectory(const std::string& resource_id) {
513 DCHECK(CalledOnValidThread());
514
515 sync_root_directory_resource_id_ = resource_id;
516
517 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
518 batch->Put(kSyncRootDirectoryKey,
519 drive_backend::RemoveWapiIdPrefix(resource_id));
520 return WriteToDB(batch.Pass(),
521 base::Bind(&DriveMetadataStore::UpdateDBStatus,
522 AsWeakPtr()));
523 }
524
SetOriginRootDirectory(const GURL & origin,const std::string & resource_id)525 void DriveMetadataStore::SetOriginRootDirectory(
526 const GURL& origin,
527 const std::string& resource_id) {
528 DCHECK(CalledOnValidThread());
529 DCHECK(IsKnownOrigin(origin));
530
531 OriginSyncType sync_type;
532 if (UpdateResourceIdMap(
533 &incremental_sync_origins_, &origin_by_resource_id_,
534 origin, resource_id)) {
535 sync_type = INCREMENTAL_SYNC_ORIGIN;
536 } else if (UpdateResourceIdMap(&disabled_origins_, &origin_by_resource_id_,
537 origin, resource_id)) {
538 sync_type = DISABLED_ORIGIN;
539 } else {
540 return;
541 }
542
543 std::string key = CreateKeyForOriginRoot(origin, sync_type);
544 DCHECK(!key.empty());
545
546 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
547 batch->Put(key, drive_backend::RemoveWapiIdPrefix(resource_id));
548 WriteToDB(batch.Pass(),
549 base::Bind(&DriveMetadataStore::UpdateDBStatus, AsWeakPtr()));
550 }
551
IsKnownOrigin(const GURL & origin) const552 bool DriveMetadataStore::IsKnownOrigin(const GURL& origin) const {
553 DCHECK(CalledOnValidThread());
554 return IsIncrementalSyncOrigin(origin) || IsOriginDisabled(origin);
555 }
556
IsIncrementalSyncOrigin(const GURL & origin) const557 bool DriveMetadataStore::IsIncrementalSyncOrigin(const GURL& origin) const {
558 DCHECK(CalledOnValidThread());
559 return ContainsKey(incremental_sync_origins_, origin);
560 }
561
IsOriginDisabled(const GURL & origin) const562 bool DriveMetadataStore::IsOriginDisabled(const GURL& origin) const {
563 DCHECK(CalledOnValidThread());
564 return ContainsKey(disabled_origins_, origin);
565 }
566
EnableOrigin(const GURL & origin,const SyncStatusCallback & callback)567 void DriveMetadataStore::EnableOrigin(
568 const GURL& origin,
569 const SyncStatusCallback& callback) {
570 DCHECK(CalledOnValidThread());
571
572 std::map<GURL, std::string>::iterator found = disabled_origins_.find(origin);
573 if (found == disabled_origins_.end()) {
574 RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND));
575 // |origin| has not been registered yet.
576 return;
577 }
578 disabled_origins_.erase(found);
579
580 // |origin| goes back to DriveFileSyncService::pending_batch_sync_origins_
581 // only and is not stored in drive_metadata_store.
582 found = incremental_sync_origins_.find(origin);
583 if (found != incremental_sync_origins_.end())
584 incremental_sync_origins_.erase(found);
585
586 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
587 batch->Delete(CreateKeyForOriginRoot(origin, INCREMENTAL_SYNC_ORIGIN));
588 batch->Delete(CreateKeyForOriginRoot(origin, DISABLED_ORIGIN));
589 WriteToDB(batch.Pass(), callback);
590 }
591
DisableOrigin(const GURL & origin,const SyncStatusCallback & callback)592 void DriveMetadataStore::DisableOrigin(
593 const GURL& origin,
594 const SyncStatusCallback& callback) {
595 DCHECK(CalledOnValidThread());
596
597 std::string resource_id;
598 if (!EraseIfExists(&incremental_sync_origins_, origin, &resource_id)) {
599 RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND));
600 return;
601 }
602 disabled_origins_[origin] = resource_id;
603
604 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
605 batch->Delete(CreateKeyForOriginRoot(origin, INCREMENTAL_SYNC_ORIGIN));
606 batch->Put(CreateKeyForOriginRoot(origin, DISABLED_ORIGIN),
607 drive_backend::RemoveWapiIdPrefix(resource_id));
608 AppendMetadataDeletionToBatch(metadata_map_, origin, batch.get());
609 metadata_map_.erase(origin);
610
611 WriteToDB(batch.Pass(), callback);
612 }
613
RemoveOrigin(const GURL & origin,const SyncStatusCallback & callback)614 void DriveMetadataStore::RemoveOrigin(
615 const GURL& origin,
616 const SyncStatusCallback& callback) {
617 DCHECK(CalledOnValidThread());
618
619 std::string resource_id;
620 if (!EraseIfExists(&incremental_sync_origins_, origin, &resource_id) &&
621 !EraseIfExists(&disabled_origins_, origin, &resource_id)) {
622 RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_NOT_FOUND));
623 return;
624 }
625 origin_by_resource_id_.erase(resource_id);
626
627 scoped_ptr<leveldb::WriteBatch> batch(new leveldb::WriteBatch);
628 batch->Delete(CreateKeyForOriginRoot(origin, INCREMENTAL_SYNC_ORIGIN));
629 batch->Delete(CreateKeyForOriginRoot(origin, DISABLED_ORIGIN));
630 AppendMetadataDeletionToBatch(metadata_map_, origin, batch.get());
631 metadata_map_.erase(origin);
632
633 WriteToDB(batch.Pass(), callback);
634 }
635
WriteToDB(scoped_ptr<leveldb::WriteBatch> batch,const SyncStatusCallback & callback)636 void DriveMetadataStore::WriteToDB(scoped_ptr<leveldb::WriteBatch> batch,
637 const SyncStatusCallback& callback) {
638 DCHECK(CalledOnValidThread());
639 if (db_status_ != SYNC_STATUS_OK &&
640 db_status_ != SYNC_DATABASE_ERROR_NOT_FOUND) {
641 RunSoon(FROM_HERE, base::Bind(callback, SYNC_DATABASE_ERROR_FAILED));
642 return;
643 }
644
645 DCHECK(db_);
646 base::PostTaskAndReplyWithResult(
647 file_task_runner_.get(),
648 FROM_HERE,
649 base::Bind(&leveldb::DB::Write,
650 base::Unretained(db_.get()),
651 leveldb::WriteOptions(),
652 base::Owned(batch.release())),
653 base::Bind(&DriveMetadataStore::UpdateDBStatusAndInvokeCallback,
654 AsWeakPtr(),
655 callback));
656 }
657
UpdateDBStatus(SyncStatusCode status)658 void DriveMetadataStore::UpdateDBStatus(SyncStatusCode status) {
659 DCHECK(CalledOnValidThread());
660 if (db_status_ != SYNC_STATUS_OK &&
661 db_status_ != SYNC_DATABASE_ERROR_NOT_FOUND) {
662 // TODO(tzik): Handle database corruption. http://crbug.com/153709
663 db_status_ = status;
664 util::Log(logging::LOG_WARNING,
665 FROM_HERE,
666 "DriveMetadataStore turned to wrong state: %s",
667 SyncStatusCodeToString(status));
668 return;
669 }
670 db_status_ = SYNC_STATUS_OK;
671 }
672
UpdateDBStatusAndInvokeCallback(const SyncStatusCallback & callback,const leveldb::Status & leveldb_status)673 void DriveMetadataStore::UpdateDBStatusAndInvokeCallback(
674 const SyncStatusCallback& callback,
675 const leveldb::Status& leveldb_status) {
676 SyncStatusCode status = LevelDBStatusToSyncStatusCode(leveldb_status);
677 UpdateDBStatus(status);
678 callback.Run(status);
679 }
680
GetConflictURLs(fileapi::FileSystemURLSet * urls) const681 SyncStatusCode DriveMetadataStore::GetConflictURLs(
682 fileapi::FileSystemURLSet* urls) const {
683 DCHECK(CalledOnValidThread());
684 DCHECK_EQ(SYNC_STATUS_OK, db_status_);
685
686 urls->clear();
687 for (MetadataMap::const_iterator origin_itr = metadata_map_.begin();
688 origin_itr != metadata_map_.end();
689 ++origin_itr) {
690 for (PathToMetadata::const_iterator itr = origin_itr->second.begin();
691 itr != origin_itr->second.end();
692 ++itr) {
693 if (itr->second.conflicted()) {
694 urls->insert(CreateSyncableFileSystemURL(
695 origin_itr->first, itr->first));
696 }
697 }
698 }
699 return SYNC_STATUS_OK;
700 }
701
GetToBeFetchedFiles(URLAndDriveMetadataList * list) const702 SyncStatusCode DriveMetadataStore::GetToBeFetchedFiles(
703 URLAndDriveMetadataList* list) const {
704 DCHECK(CalledOnValidThread());
705 DCHECK_EQ(SYNC_STATUS_OK, db_status_);
706
707 list->clear();
708 for (MetadataMap::const_iterator origin_itr = metadata_map_.begin();
709 origin_itr != metadata_map_.end();
710 ++origin_itr) {
711 for (PathToMetadata::const_iterator itr = origin_itr->second.begin();
712 itr != origin_itr->second.end();
713 ++itr) {
714 if (itr->second.to_be_fetched()) {
715 FileSystemURL url = CreateSyncableFileSystemURL(
716 origin_itr->first, itr->first);
717 list->push_back(std::make_pair(url, itr->second));
718 }
719 }
720 }
721 return SYNC_STATUS_OK;
722 }
723
GetResourceIdForOrigin(const GURL & origin) const724 std::string DriveMetadataStore::GetResourceIdForOrigin(
725 const GURL& origin) const {
726 DCHECK(CalledOnValidThread());
727
728 // If we don't have valid root directory (this could be reset even after
729 // initialized) just return empty string, as the origin directories
730 // in the root directory must have become invalid now too.
731 if (sync_root_directory().empty())
732 return std::string();
733
734 ResourceIdByOrigin::const_iterator found =
735 incremental_sync_origins_.find(origin);
736 if (found != incremental_sync_origins_.end())
737 return found->second;
738
739 found = disabled_origins_.find(origin);
740 if (found != disabled_origins_.end())
741 return found->second;
742
743 return std::string();
744 }
745
GetAllOrigins(std::vector<GURL> * origins)746 void DriveMetadataStore::GetAllOrigins(std::vector<GURL>* origins) {
747 DCHECK(CalledOnValidThread());
748 DCHECK(origins);
749 origins->clear();
750 origins->reserve(incremental_sync_origins_.size() +
751 disabled_origins_.size());
752 AddOriginsToVector(origins, incremental_sync_origins_);
753 AddOriginsToVector(origins, disabled_origins_);
754 }
755
GetOriginByOriginRootDirectoryId(const std::string & resource_id,GURL * origin)756 bool DriveMetadataStore::GetOriginByOriginRootDirectoryId(
757 const std::string& resource_id,
758 GURL* origin) {
759 DCHECK(CalledOnValidThread());
760 DCHECK(origin);
761
762 OriginByResourceId::iterator found = origin_by_resource_id_.find(resource_id);
763 if (found == origin_by_resource_id_.end())
764 return false;
765 *origin = found->second;
766 return true;
767 }
768
DumpFiles(const GURL & origin)769 scoped_ptr<base::ListValue> DriveMetadataStore::DumpFiles(const GURL& origin) {
770 DCHECK(CalledOnValidThread());
771
772 scoped_ptr<base::ListValue> files(new base::ListValue);
773
774 MetadataMap::const_iterator found = metadata_map_.find(origin);
775 if (found == metadata_map_.end())
776 return make_scoped_ptr(new base::ListValue);
777
778 for (PathToMetadata::const_iterator itr = found->second.begin();
779 itr != found->second.end();
780 ++itr) {
781 // Convert Drive specific metadata to Common File metadata object.
782 const DriveMetadata& metadata = itr->second;
783
784 base::DictionaryValue* file = new base::DictionaryValue;
785 file->SetString("path", itr->first.AsUTF8Unsafe());
786 file->SetString("title", itr->first.BaseName().AsUTF8Unsafe());
787 file->SetString("type", DriveTypeToString(metadata.type()));
788
789 base::DictionaryValue* details = new base::DictionaryValue;
790 details->SetString("resource_id", metadata.resource_id());
791 details->SetString("md5", metadata.md5_checksum());
792 details->SetString("dirty", metadata.to_be_fetched() ? "true" : "false");
793
794 file->Set("details", details);
795 files->Append(file);
796 }
797
798 return files.Pass();
799 }
800
801 } // namespace sync_file_system
802