Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(600)

Side by Side Diff: chrome/browser/sync_file_system/drive_backend/metadata_database.cc

Issue 18591004: [SyncFS] Implement MetadataDatabase initialization (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.h" 5 #include "chrome/browser/sync_file_system/drive_backend/metadata_database.h"
6 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"
7 #include "chrome/browser/google_apis/drive_api_parser.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_ut il.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"
8 30
9 namespace sync_file_system { 31 namespace sync_file_system {
10 namespace drive_backend { 32 namespace drive_backend {
11 33
12 MetadataDatabase::MetadataDatabase(base::SequencedTaskRunner* task_runner) { 34 typedef MetadataDatabase::FileByAppID FileByAppID;
13 NOTIMPLEMENTED(); 35 typedef MetadataDatabase::FileByFileID FileByFileID;
14 } 36 typedef MetadataDatabase::FileByParentAndTitle FileByParentAndTitle;
15 37 typedef MetadataDatabase::FileSet FileSet;
16 MetadataDatabase::~MetadataDatabase() { 38 typedef MetadataDatabase::FilesByParent FilesByParent;
17 } 39
18 40 const char kDatabaseVersionKey[] = "VERSION";
19 void MetadataDatabase::Initialize(const base::FilePath& database_dir, 41 const int64 kCurrentDatabaseVersion = 3;
20 const SyncStatusCallback& callback) { 42 const char kServiceMetadataKey[] = "SERVICE";
21 NOTIMPLEMENTED(); 43 const char kFileMetadataKeyPrefix[] = "FILE: ";
44
45 struct DatabaseContents {
46 scoped_ptr<ServiceMetadata> service_metadata;
47 ScopedVector<DriveFileMetadata> file_metadata;
48 };
49
50 namespace {
51
52 std::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
58 void AdaptLevelDBStatusToSyncStatusCode(const SyncStatusCallback& callback,
59 const leveldb::Status& status) {
60 callback.Run(LevelDBStatusToSyncStatusCode(status));
61 }
62
63 // Returns true if |db| has no content.
64 bool IsDatabaseEmpty(leveldb::DB* db) {
65 DCHECK(db);
66 scoped_ptr<leveldb::Iterator> itr(db->NewIterator(leveldb::ReadOptions()));
67 itr->SeekToFirst();
68 return !itr->Valid();
69 }
70
71 SyncStatusCode OpenDatabase(const base::FilePath& path,
72 scoped_ptr<leveldb::DB>* db_out,
73 bool* created) {
74 base::ThreadRestrictions::AssertIOAllowed();
75 DCHECK(db_out);
76 DCHECK(created);
77
78 leveldb::Options options;
79 options.create_if_missing = true;
80 leveldb::DB* db = NULL;
81 leveldb::Status db_status =
82 leveldb::DB::Open(options, path.AsUTF8Unsafe(), &db);
83 SyncStatusCode status = LevelDBStatusToSyncStatusCode(db_status);
84 if (status != SYNC_STATUS_OK) {
85 delete db;
86 return status;
87 }
88
89 *created = IsDatabaseEmpty(db);
90 db_out->reset(db);
91 return status;
92 }
93
94 SyncStatusCode MigrateDatabaseIfNeeded(leveldb::DB* db) {
95 base::ThreadRestrictions::AssertIOAllowed();
96 DCHECK(db);
97 std::string value;
98 leveldb::Status status =
99 db->Get(leveldb::ReadOptions(), kDatabaseVersionKey, &value);
100 int64 version = 0;
101 if (status.ok()) {
102 if (!base::StringToInt64(value, &version))
103 return SYNC_DATABASE_ERROR_FAILED;
104 } else {
105 if (!status.IsNotFound())
106 return SYNC_DATABASE_ERROR_FAILED;
107 }
108
109 switch (version) {
110 case 0:
111 drive_backend::MigrateDatabaseFromV0ToV1(db);
112 // fall-through
113 case 1:
114 drive_backend::MigrateDatabaseFromV1ToV2(db);
115 // fall-through
116 case 2:
117 // TODO(tzik): Migrate database from version 2 to 3.
118 // * Add sync-root folder as active, dirty and needs_folder_listing
119 // folder.
120 // * Add app-root folders for each origins. Each app-root folder for
121 // an enabled origin should be a active, dirty and
122 // needs_folder_listing folder. And Each app-root folder for a
123 // disabled origin should be an inactive, dirty and
124 // non-needs_folder_listing folder.
125 // * Add a file metadata for each file in previous version.
126 NOTIMPLEMENTED();
127 return SYNC_DATABASE_ERROR_FAILED;
128 // fall-through
129 case 3:
130 DCHECK_EQ(3, kCurrentDatabaseVersion);
131 return SYNC_STATUS_OK;
132 default:
133 return SYNC_DATABASE_ERROR_FAILED;
134 }
135 }
136
137 SyncStatusCode WriteVersionInfo(leveldb::DB* db) {
138 base::ThreadRestrictions::AssertIOAllowed();
139 DCHECK(db);
140 return LevelDBStatusToSyncStatusCode(
141 db->Put(leveldb::WriteOptions(),
142 kDatabaseVersionKey,
143 base::Int64ToString(kCurrentDatabaseVersion)));
144 }
145
146 SyncStatusCode ReadDatabaseContents(leveldb::DB* db,
147 DatabaseContents* contents) {
148 base::ThreadRestrictions::AssertIOAllowed();
149 DCHECK(db);
150 DCHECK(contents);
151
152 scoped_ptr<leveldb::Iterator> itr(db->NewIterator(leveldb::ReadOptions()));
153 for (itr->SeekToFirst(); itr->Valid(); itr->Next()) {
154 std::string key = itr->key().ToString();
155 std::string value = itr->value().ToString();
156 if (key == kServiceMetadataKey) {
157 scoped_ptr<ServiceMetadata> service_metadata(new ServiceMetadata);
158 if (!service_metadata->ParseFromString(value)) {
159 util::Log(logging::LOG_WARNING, FROM_HERE,
160 "Failed to parse SyncServiceMetadata");
161 continue;
162 }
163
164 contents->service_metadata = service_metadata.Pass();
165 continue;
166 }
167
168 if (StartsWithASCII(key, kFileMetadataKeyPrefix, true)) {
169 std::string file_id = RemovePrefix(key, kFileMetadataKeyPrefix);
170
171 scoped_ptr<DriveFileMetadata> metadata(new DriveFileMetadata);
172 if (!metadata->ParseFromString(itr->value().ToString())) {
173 util::Log(logging::LOG_WARNING, FROM_HERE,
174 "Failed to parse a Metadata");
175 continue;
176 }
177
178 contents->file_metadata.push_back(metadata.release());
179 continue;
180 }
181 }
182
183 return SYNC_STATUS_OK;
184 }
185
186 SyncStatusCode InitializeServiceMetadata(DatabaseContents* contents,
187 leveldb::WriteBatch* batch) {
188
189 if (!contents->service_metadata) {
190 contents->service_metadata.reset(new ServiceMetadata);
191
192 std::string value;
193 contents->service_metadata->SerializeToString(&value);
194 batch->Put(kServiceMetadataKey, value);
195 }
196 return SYNC_STATUS_OK;
197 }
198
199 SyncStatusCode RemoveUnreachableFiles(DatabaseContents* contents,
200 leveldb::WriteBatch* batch) {
201 FileByFileID unvisited_files;
202 FilesByParent files_by_parent;
203
204 for (ScopedVector<DriveFileMetadata>::iterator itr =
205 contents->file_metadata.begin();
206 itr != contents->file_metadata.end();
207 ++itr) {
208 DriveFileMetadata* metadata = *itr;
209 DCHECK(!ContainsKey(unvisited_files, metadata->file_id()));
210 unvisited_files[metadata->file_id()] = metadata;
211 files_by_parent[metadata->parent_folder_id()].insert(metadata);
212 }
213
214 // Traverse synced metadata tree. Take only active items and their children.
215 // Drop unreachable items.
216 ScopedVector<DriveFileMetadata> reachable_files;
217 std::stack<std::string> pending;
218 if (!contents->service_metadata->sync_root_folder_id().empty())
219 pending.push(contents->service_metadata->sync_root_folder_id());
220
221 while (!pending.empty()) {
222 std::string file_id = pending.top();
223 pending.pop();
224
225 {
226 FileByFileID::iterator found = unvisited_files.find(file_id);
227 if (found == unvisited_files.end())
228 continue;
229
230 DriveFileMetadata* metadata = found->second;
231 unvisited_files.erase(found);
232 reachable_files.push_back(metadata);
233
234 if (!metadata->active())
235 continue;
236 }
237
238 FilesByParent::iterator found = files_by_parent.find(file_id);
239 if (found == files_by_parent.end())
240 continue;
241
242 for (FileSet::const_iterator itr = found->second.begin();
243 itr != found->second.end();
244 ++itr)
245 pending.push((*itr)->file_id());
246 }
247
248 for (FileByFileID::iterator itr = unvisited_files.begin();
249 itr != unvisited_files.end();
250 ++itr) {
251 DriveFileMetadata* metadata = itr->second;
252 batch->Delete(metadata->file_id());
253 delete metadata;
254 }
255 unvisited_files.clear();
256
257 // |reachable_files| contains all files/folders reachable from sync-root
258 // folder via active folders.
259 contents->file_metadata.weak_clear();
260 contents->file_metadata.swap(reachable_files);
261
262 return SYNC_STATUS_OK;
263 }
264
265 template <typename Container, typename Key, typename Value>
266 bool FindItem(const Container& container, const Key& key, Value* value) {
267 typename Container::const_iterator found = container.find(key);
268 if (found == container.end())
269 return false;
270 if (value)
271 *value = *found->second;
272 return true;
273 }
274
275 } // namespace
276
277 bool MetadataDatabase::FileIDComparator::operator()(DriveFileMetadata* left,
278 DriveFileMetadata* right) {
279 return left->file_id() < right->file_id();
280 }
281
282 MetadataDatabase::MetadataDatabase(base::SequencedTaskRunner* task_runner)
283 : task_runner_(task_runner), weak_ptr_factory_(this) {
284 DCHECK(task_runner);
285 }
286
287 // static
288 void MetadataDatabase::Create(base::SequencedTaskRunner* task_runner,
289 const base::FilePath& database_path,
290 const CreateCallback& callback) {
291 task_runner->PostTask(FROM_HERE, base::Bind(
292 &CreateOnTaskRunner,
293 base::MessageLoopProxy::current(),
294 make_scoped_refptr(task_runner),
295 database_path, callback));
22 } 296 }
23 297
24 int64 MetadataDatabase::GetLargestChangeID() const { 298 int64 MetadataDatabase::GetLargestChangeID() const {
25 NOTIMPLEMENTED(); 299 return service_metadata_->largest_change_id();
26 return 0;
27 } 300 }
28 301
29 void MetadataDatabase::RegisterApp(const std::string& app_id, 302 void MetadataDatabase::RegisterApp(const std::string& app_id,
30 const std::string& folder_id, 303 const std::string& folder_id,
31 const SyncStatusCallback& callback) { 304 const SyncStatusCallback& callback) {
32 NOTIMPLEMENTED(); 305 NOTIMPLEMENTED();
33 } 306 }
34 307
35 void MetadataDatabase::DisableApp(const std::string& app_id, 308 void MetadataDatabase::DisableApp(const std::string& app_id,
36 const SyncStatusCallback& callback) { 309 const SyncStatusCallback& callback) {
37 NOTIMPLEMENTED(); 310 NOTIMPLEMENTED();
38 } 311 }
39 312
40 void MetadataDatabase::EnableApp(const std::string& app_id, 313 void MetadataDatabase::EnableApp(const std::string& app_id,
41 const SyncStatusCallback& callback) { 314 const SyncStatusCallback& callback) {
42 NOTIMPLEMENTED(); 315 NOTIMPLEMENTED();
43 } 316 }
44 317
45 void MetadataDatabase::UnregisterApp(const std::string& app_id, 318 void MetadataDatabase::UnregisterApp(const std::string& app_id,
46 const SyncStatusCallback& callback) { 319 const SyncStatusCallback& callback) {
47 NOTIMPLEMENTED(); 320 NOTIMPLEMENTED();
48 } 321 }
49 322
50 bool MetadataDatabase::FindAppRootFolder(const std::string& app_id, 323 bool MetadataDatabase::FindAppRootFolder(const std::string& app_id,
51 DriveFileMetadata* folder) const { 324 DriveFileMetadata* folder) const {
52 NOTIMPLEMENTED(); 325 return FindItem(app_root_by_app_id_, app_id, folder);
53 return false;
54 } 326 }
55 327
56 bool MetadataDatabase::FindFileByFileID(const std::string& file_id, 328 bool MetadataDatabase::FindFileByFileID(const std::string& file_id,
57 DriveFileMetadata* metadata) const { 329 DriveFileMetadata* metadata) const {
58 NOTIMPLEMENTED(); 330 return FindItem(file_by_file_id_, file_id, metadata);
59 return false;
60 } 331 }
61 332
62 size_t MetadataDatabase::FindFilesByParentAndTitle( 333 size_t MetadataDatabase::FindFilesByParentAndTitle(
63 const std::string& file_id, 334 const std::string& file_id,
64 const std::string& title, 335 const std::string& title,
65 ScopedVector<DriveFileMetadata>* files) const { 336 ScopedVector<DriveFileMetadata>* files) const {
66 NOTIMPLEMENTED(); 337 NOTIMPLEMENTED();
67 return 0; 338 return 0;
68 } 339 }
69 340
70 bool MetadataDatabase::FindActiveFileByParentAndTitle( 341 bool MetadataDatabase::FindActiveFileByParentAndTitle(
71 const std::string& folder_id, 342 const std::string& folder_id,
72 const std::string& title, 343 const std::string& title,
73 DriveFileMetadata* file) const { 344 DriveFileMetadata* file) const {
74 NOTIMPLEMENTED(); 345 return FindItem(active_file_by_parent_and_title_,
75 return false; 346 std::make_pair(folder_id, title),
347 file);
76 } 348 }
77 349
78 bool MetadataDatabase::FindActiveFileByPath(const std::string& app_id, 350 bool MetadataDatabase::FindActiveFileByPath(const std::string& app_id,
79 const base::FilePath& path, 351 const base::FilePath& path,
80 DriveFileMetadata* file) const { 352 DriveFileMetadata* file) const {
353 DriveFileMetadata current;
354 if (!FindAppRootFolder(app_id, &current))
355 return false;
356
357 std::vector<base::FilePath::StringType> components;
358 path.GetComponents(&components);
359
360 std::string parent_folder_id = current.file_id();
361 for (std::vector<base::FilePath::StringType>::iterator itr =
362 components.begin();
363 itr != components.end();
364 ++itr) {
365 std::string current_folder_id = current.file_id();
366 if (!FindActiveFileByParentAndTitle(
367 current_folder_id, base::FilePath(*itr).AsUTF8Unsafe(), &current))
368 return false;
369 }
370 if (file)
371 *file = current;
372 return true;
373 }
374
375 bool MetadataDatabase::BuildPathForFile(const std::string& file_id,
376 base::FilePath* path) const {
81 NOTIMPLEMENTED(); 377 NOTIMPLEMENTED();
82 return false; 378 return false;
83 } 379 }
84
85 bool MetadataDatabase::ConstructPathForFile(const std::string& file_id,
86 base::FilePath* path) const {
87 NOTIMPLEMENTED();
88 return false;
89 }
90 380
91 void MetadataDatabase::UpdateByChangeList( 381 void MetadataDatabase::UpdateByChangeList(
92 ScopedVector<google_apis::ChangeResource> changes, 382 ScopedVector<google_apis::ChangeResource> changes,
93 const SyncStatusCallback& callback) { 383 const SyncStatusCallback& callback) {
94 NOTIMPLEMENTED(); 384 NOTIMPLEMENTED();
95 } 385 }
96 386
97 void MetadataDatabase::PopulateFolder( 387 void MetadataDatabase::PopulateFolder(
98 const std::string& folder_id, 388 const std::string& folder_id,
99 ScopedVector<google_apis::ResourceEntry> children, 389 ScopedVector<google_apis::ResourceEntry> children,
100 const SyncStatusCallback& callback) { 390 const SyncStatusCallback& callback) {
101 NOTIMPLEMENTED(); 391 NOTIMPLEMENTED();
102 } 392 }
103 393
394 void MetadataDatabase::BuildIndexes(DatabaseContents* contents) {
395 for (ScopedVector<DriveFileMetadata>::iterator itr =
396 contents->file_metadata.begin();
397 itr != contents->file_metadata.end();
398 ++itr) {
399 DriveFileMetadata* file = *itr;
400 file_by_file_id_[file->file_id()] = file;
401
402 if (file->is_app_root())
403 app_root_by_app_id_[file->app_id()] = file;
404
405 if (file->active() && file->has_synced_details()) {
406 FileByParentAndTitle::key_type key =
407 std::make_pair(file->parent_folder_id(),
408 file->synced_details().title());
409 active_file_by_parent_and_title_[key] = file;
410 }
411
412 if (!file->parent_folder_id().empty())
413 files_by_parent_[file->parent_folder_id()].insert(file);
414 }
415
416 contents->file_metadata.weak_clear();
417 }
418
419 void MetadataDatabase::CreateOnTaskRunner(
420 base::SingleThreadTaskRunner* callback_runner,
421 base::SequencedTaskRunner* task_runner,
422 const base::FilePath& database_path,
423 const CreateCallback& callback) {
424 scoped_ptr<MetadataDatabase> metadata_database(
425 new MetadataDatabase(task_runner));
426 SyncStatusCode status =
427 metadata_database->InitializeOnTaskRunner(database_path);
428 if (status != SYNC_STATUS_OK)
429 metadata_database.reset();
430
431 callback_runner->PostTask(FROM_HERE, base::Bind(
432 callback, status, base::Passed(&metadata_database)));
433 }
434
435 SyncStatusCode MetadataDatabase::InitializeOnTaskRunner(
436 const base::FilePath& database_path) {
437 base::ThreadRestrictions::AssertIOAllowed();
438 DCHECK(task_runner_->RunsTasksOnCurrentThread());
439
440 SyncStatusCode status = SYNC_STATUS_UNKNOWN;
441 bool created = false;
442 status = OpenDatabase(database_path, &db_, &created);
443 if (status != SYNC_STATUS_OK)
444 return status;
445
446 if (created) {
447 status = WriteVersionInfo(db_.get());
448 if (status != SYNC_STATUS_OK)
449 return status;
450 } else {
451 status = MigrateDatabaseIfNeeded(db_.get());
452 if (status != SYNC_STATUS_OK)
453 return status;
454 }
455
456 DatabaseContents contents;
457 status = ReadDatabaseContents(db_.get(), &contents);
458 if (status != SYNC_STATUS_OK)
459 return status;
460
461 leveldb::WriteBatch batch;
462 status = InitializeServiceMetadata(&contents, &batch);
463 if (status != SYNC_STATUS_OK)
464 return status;
465
466 status = RemoveUnreachableFiles(&contents, &batch);
467 if (status != SYNC_STATUS_OK)
468 return status;
469
470 status = LevelDBStatusToSyncStatusCode(
471 db_->Write(leveldb::WriteOptions(), &batch));
472 if (status != SYNC_STATUS_OK)
473 return status;
474
475 BuildIndexes(&contents);
476 return status;
477 }
478
479 MetadataDatabase::~MetadataDatabase() {
480 task_runner_->DeleteSoon(FROM_HERE, db_.release());
481 STLDeleteContainerPairSecondPointers(
482 file_by_file_id_.begin(), file_by_file_id_.end());
483 }
484
485 void MetadataDatabase::WriteToDatabase(scoped_ptr<leveldb::WriteBatch> batch,
486 const SyncStatusCallback& callback) {
487 base::PostTaskAndReplyWithResult(
488 task_runner_.get(),
489 FROM_HERE,
490 base::Bind(&leveldb::DB::Write,
491 base::Unretained(db_.get()),
492 leveldb::WriteOptions(),
493 base::Owned(batch.release())),
494 base::Bind(&AdaptLevelDBStatusToSyncStatusCode, callback));
495 }
496
104 } // namespace drive_backend 497 } // namespace drive_backend
105 } // namespace sync_file_system 498 } // namespace sync_file_system
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698