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

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

Powered by Google App Engine
This is Rietveld 408576698