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

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

Powered by Google App Engine
This is Rietveld 408576698