| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 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/chromeos/gdata/gdata_directory_service.h" | |
| 6 | |
| 7 #include <leveldb/db.h> | |
| 8 #include <utility> | |
| 9 | |
| 10 #include "base/message_loop_proxy.h" | |
| 11 #include "base/string_number_conversions.h" | |
| 12 #include "base/sequenced_task_runner.h" | |
| 13 #include "base/tracked_objects.h" | |
| 14 #include "chrome/browser/chromeos/gdata/drive.pb.h" | |
| 15 #include "chrome/browser/chromeos/gdata/drive_files.h" | |
| 16 #include "chrome/browser/chromeos/gdata/gdata_util.h" | |
| 17 #include "chrome/browser/chromeos/gdata/gdata_wapi_parser.h" | |
| 18 #include "content/public/browser/browser_thread.h" | |
| 19 | |
| 20 using content::BrowserThread; | |
| 21 | |
| 22 namespace gdata { | |
| 23 namespace { | |
| 24 | |
| 25 // m: prefix for filesystem metadata db keys, version and largest_changestamp. | |
| 26 // r: prefix for resource id db keys. | |
| 27 const char kDBKeyLargestChangestamp[] = "m:largest_changestamp"; | |
| 28 const char kDBKeyVersion[] = "m:version"; | |
| 29 const char kDBKeyResourceIdPrefix[] = "r:"; | |
| 30 | |
| 31 } // namespace | |
| 32 | |
| 33 EntryInfoResult::EntryInfoResult() : error(GDATA_FILE_ERROR_FAILED) { | |
| 34 } | |
| 35 | |
| 36 EntryInfoResult::~EntryInfoResult() { | |
| 37 } | |
| 38 | |
| 39 EntryInfoPairResult::EntryInfoPairResult() { | |
| 40 } | |
| 41 | |
| 42 EntryInfoPairResult::~EntryInfoPairResult() { | |
| 43 } | |
| 44 | |
| 45 // ResourceMetadataDB implementation. | |
| 46 | |
| 47 // Params for GDatadirectoryServiceDB::Create. | |
| 48 struct CreateDBParams { | |
| 49 CreateDBParams(const FilePath& db_path, | |
| 50 base::SequencedTaskRunner* blocking_task_runner) | |
| 51 : db_path(db_path), | |
| 52 blocking_task_runner(blocking_task_runner) { | |
| 53 } | |
| 54 | |
| 55 FilePath db_path; | |
| 56 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner; | |
| 57 scoped_ptr<ResourceMetadataDB> db; | |
| 58 GDataDirectoryService::SerializedMap serialized_resources; | |
| 59 }; | |
| 60 | |
| 61 // Wrapper for level db. All methods must be called on blocking thread. | |
| 62 class ResourceMetadataDB { | |
| 63 public: | |
| 64 ResourceMetadataDB(const FilePath& db_path, | |
| 65 base::SequencedTaskRunner* blocking_task_runner); | |
| 66 | |
| 67 // Initializes the database. | |
| 68 void Init(); | |
| 69 | |
| 70 // Reads the database into |serialized_resources|. | |
| 71 void Read(GDataDirectoryService::SerializedMap* serialized_resources); | |
| 72 | |
| 73 // Saves |serialized_resources| to the database. | |
| 74 void Save(const GDataDirectoryService::SerializedMap& serialized_resources); | |
| 75 | |
| 76 private: | |
| 77 // Clears the database. | |
| 78 void Clear(); | |
| 79 | |
| 80 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner_; | |
| 81 scoped_ptr<leveldb::DB> level_db_; | |
| 82 FilePath db_path_; | |
| 83 }; | |
| 84 | |
| 85 ResourceMetadataDB::ResourceMetadataDB(const FilePath& db_path, | |
| 86 base::SequencedTaskRunner* blocking_task_runner) | |
| 87 : blocking_task_runner_(blocking_task_runner), | |
| 88 db_path_(db_path) { | |
| 89 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); | |
| 90 } | |
| 91 | |
| 92 // Creates, initializes and reads from the database. | |
| 93 // This must be defined after ResourceMetadataDB and CreateDBParams. | |
| 94 static void CreateResourceMetadataDBOnBlockingPool( | |
| 95 CreateDBParams* params) { | |
| 96 DCHECK(params->blocking_task_runner->RunsTasksOnCurrentThread()); | |
| 97 DCHECK(!params->db_path.empty()); | |
| 98 | |
| 99 params->db.reset(new ResourceMetadataDB(params->db_path, | |
| 100 params->blocking_task_runner)); | |
| 101 params->db->Init(); | |
| 102 params->db->Read(¶ms->serialized_resources); | |
| 103 } | |
| 104 | |
| 105 void ResourceMetadataDB::Init() { | |
| 106 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); | |
| 107 DCHECK(!db_path_.empty()); | |
| 108 | |
| 109 DVLOG(1) << "Init " << db_path_.value(); | |
| 110 | |
| 111 leveldb::DB* level_db = NULL; | |
| 112 leveldb::Options options; | |
| 113 options.create_if_missing = true; | |
| 114 leveldb::Status db_status = leveldb::DB::Open(options, db_path_.value(), | |
| 115 &level_db); | |
| 116 DCHECK(level_db); | |
| 117 DCHECK(db_status.ok()); | |
| 118 level_db_.reset(level_db); | |
| 119 } | |
| 120 | |
| 121 void ResourceMetadataDB::Read( | |
| 122 GDataDirectoryService::SerializedMap* serialized_resources) { | |
| 123 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); | |
| 124 DCHECK(serialized_resources); | |
| 125 DVLOG(1) << "Read " << db_path_.value(); | |
| 126 | |
| 127 scoped_ptr<leveldb::Iterator> iter(level_db_->NewIterator( | |
| 128 leveldb::ReadOptions())); | |
| 129 for (iter->SeekToFirst(); iter->Valid(); iter->Next()) { | |
| 130 DVLOG(1) << "Read, resource " << iter->key().ToString(); | |
| 131 serialized_resources->insert(std::make_pair(iter->key().ToString(), | |
| 132 iter->value().ToString())); | |
| 133 } | |
| 134 } | |
| 135 | |
| 136 void ResourceMetadataDB::Save( | |
| 137 const GDataDirectoryService::SerializedMap& serialized_resources) { | |
| 138 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); | |
| 139 | |
| 140 Clear(); | |
| 141 for (GDataDirectoryService::SerializedMap::const_iterator iter = | |
| 142 serialized_resources.begin(); | |
| 143 iter != serialized_resources.end(); ++iter) { | |
| 144 DVLOG(1) << "Saving resource " << iter->first << " to db"; | |
| 145 leveldb::Status status = level_db_->Put(leveldb::WriteOptions(), | |
| 146 leveldb::Slice(iter->first), | |
| 147 leveldb::Slice(iter->second)); | |
| 148 if (!status.ok()) { | |
| 149 LOG(ERROR) << "leveldb Put failed of " << iter->first | |
| 150 << ", with " << status.ToString(); | |
| 151 NOTREACHED(); | |
| 152 } | |
| 153 } | |
| 154 } | |
| 155 | |
| 156 void ResourceMetadataDB::Clear() { | |
| 157 level_db_.reset(); | |
| 158 leveldb::DestroyDB(db_path_.value(), leveldb::Options()); | |
| 159 Init(); | |
| 160 } | |
| 161 | |
| 162 // GDataDirectoryService class implementation. | |
| 163 | |
| 164 GDataDirectoryService::GDataDirectoryService() | |
| 165 : blocking_task_runner_(NULL), | |
| 166 serialized_size_(0), | |
| 167 largest_changestamp_(0), | |
| 168 origin_(UNINITIALIZED), | |
| 169 weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { | |
| 170 root_.reset(CreateDriveDirectory()); | |
| 171 if (!util::IsDriveV2ApiEnabled()) | |
| 172 InitializeRootEntry(kGDataRootDirectoryResourceId); | |
| 173 } | |
| 174 | |
| 175 GDataDirectoryService::~GDataDirectoryService() { | |
| 176 ClearRoot(); | |
| 177 | |
| 178 // Ensure db is closed on the blocking pool. | |
| 179 if (blocking_task_runner_ && directory_service_db_.get()) | |
| 180 blocking_task_runner_->DeleteSoon(FROM_HERE, | |
| 181 directory_service_db_.release()); | |
| 182 } | |
| 183 | |
| 184 DriveEntry* GDataDirectoryService::FromDocumentEntry(const DocumentEntry& doc) { | |
| 185 DriveEntry* entry = NULL; | |
| 186 if (doc.is_folder()) | |
| 187 entry = CreateDriveDirectory(); | |
| 188 else if (doc.is_hosted_document() || doc.is_file()) | |
| 189 entry = CreateDriveFile(); | |
| 190 | |
| 191 if (entry) | |
| 192 entry->InitFromDocumentEntry(doc); | |
| 193 return entry; | |
| 194 } | |
| 195 | |
| 196 DriveFile* GDataDirectoryService::CreateDriveFile() { | |
| 197 return new DriveFile(this); | |
| 198 } | |
| 199 | |
| 200 DriveDirectory* GDataDirectoryService::CreateDriveDirectory() { | |
| 201 return new DriveDirectory(this); | |
| 202 } | |
| 203 | |
| 204 void GDataDirectoryService::InitializeRootEntry(const std::string& root_id) { | |
| 205 root_.reset(CreateDriveDirectory()); | |
| 206 root_->set_title(kGDataRootDirectory); | |
| 207 root_->SetBaseNameFromTitle(); | |
| 208 root_->set_resource_id(root_id); | |
| 209 AddEntryToResourceMap(root_.get()); | |
| 210 } | |
| 211 | |
| 212 void GDataDirectoryService::ClearRoot() { | |
| 213 // Note that children have a reference to root_, | |
| 214 // so we need to delete them here. | |
| 215 root_->RemoveChildren(); | |
| 216 RemoveEntryFromResourceMap(root_->resource_id()); | |
| 217 DCHECK(resource_map_.empty()); | |
| 218 resource_map_.clear(); | |
| 219 root_.reset(); | |
| 220 } | |
| 221 | |
| 222 void GDataDirectoryService::AddEntryToDirectory( | |
| 223 DriveDirectory* directory, | |
| 224 DriveEntry* new_entry, | |
| 225 const FileMoveCallback& callback) { | |
| 226 DCHECK(directory); | |
| 227 DCHECK(new_entry); | |
| 228 DCHECK(!callback.is_null()); | |
| 229 | |
| 230 directory->AddEntry(new_entry); | |
| 231 DVLOG(1) << "AddEntryToDirectory " << new_entry->GetFilePath().value(); | |
| 232 base::MessageLoopProxy::current()->PostTask(FROM_HERE, | |
| 233 base::Bind(callback, GDATA_FILE_OK, new_entry->GetFilePath())); | |
| 234 } | |
| 235 | |
| 236 void GDataDirectoryService::MoveEntryToDirectory( | |
| 237 const FilePath& directory_path, | |
| 238 DriveEntry* entry, | |
| 239 const FileMoveCallback& callback) { | |
| 240 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 241 DCHECK(entry); | |
| 242 DCHECK(!callback.is_null()); | |
| 243 | |
| 244 if (entry->parent()) | |
| 245 entry->parent()->RemoveChild(entry); | |
| 246 | |
| 247 DriveEntry* destination = FindEntryByPathSync(directory_path); | |
| 248 FilePath moved_file_path; | |
| 249 GDataFileError error = GDATA_FILE_ERROR_FAILED; | |
| 250 if (!destination) { | |
| 251 error = GDATA_FILE_ERROR_NOT_FOUND; | |
| 252 } else if (!destination->AsDriveDirectory()) { | |
| 253 error = GDATA_FILE_ERROR_NOT_A_DIRECTORY; | |
| 254 } else { | |
| 255 destination->AsDriveDirectory()->AddEntry(entry); | |
| 256 moved_file_path = entry->GetFilePath(); | |
| 257 error = GDATA_FILE_OK; | |
| 258 } | |
| 259 DVLOG(1) << "MoveEntryToDirectory " << moved_file_path.value(); | |
| 260 base::MessageLoopProxy::current()->PostTask( | |
| 261 FROM_HERE, base::Bind(callback, error, moved_file_path)); | |
| 262 } | |
| 263 | |
| 264 void GDataDirectoryService::RemoveEntryFromParent( | |
| 265 DriveEntry* entry, | |
| 266 const FileMoveCallback& callback) { | |
| 267 DriveDirectory* parent = entry->parent(); | |
| 268 DCHECK(parent); | |
| 269 DCHECK(!callback.is_null()); | |
| 270 DVLOG(1) << "RemoveEntryFromParent " << entry->GetFilePath().value(); | |
| 271 | |
| 272 parent->RemoveEntry(entry); | |
| 273 base::MessageLoopProxy::current()->PostTask(FROM_HERE, | |
| 274 base::Bind(callback, GDATA_FILE_OK, parent->GetFilePath())); | |
| 275 } | |
| 276 | |
| 277 void GDataDirectoryService::AddEntryToResourceMap(DriveEntry* entry) { | |
| 278 DVLOG(1) << "AddEntryToResourceMap " << entry->resource_id(); | |
| 279 DCHECK(!entry->resource_id().empty()); | |
| 280 std::pair<ResourceMap::iterator, bool> ret = | |
| 281 resource_map_.insert(std::make_pair(entry->resource_id(), entry)); | |
| 282 DCHECK(ret.second); // resource_id did not previously exist in the map. | |
| 283 } | |
| 284 | |
| 285 void GDataDirectoryService::RemoveEntryFromResourceMap( | |
| 286 const std::string& resource_id) { | |
| 287 DVLOG(1) << "RemoveEntryFromResourceMap " << resource_id; | |
| 288 DCHECK(!resource_id.empty()); | |
| 289 size_t ret = resource_map_.erase(resource_id); | |
| 290 DCHECK_EQ(1u, ret); // resource_id was found in the map. | |
| 291 } | |
| 292 | |
| 293 DriveEntry* GDataDirectoryService::FindEntryByPathSync( | |
| 294 const FilePath& file_path) { | |
| 295 if (file_path == root_->GetFilePath()) | |
| 296 return root_.get(); | |
| 297 | |
| 298 std::vector<FilePath::StringType> components; | |
| 299 file_path.GetComponents(&components); | |
| 300 DriveDirectory* current_dir = root_.get(); | |
| 301 | |
| 302 for (size_t i = 1; i < components.size() && current_dir; ++i) { | |
| 303 std::string resource_id = current_dir->FindChild(components[i]); | |
| 304 if (resource_id.empty()) | |
| 305 return NULL; | |
| 306 | |
| 307 DriveEntry* entry = GetEntryByResourceId(resource_id); | |
| 308 DCHECK(entry); | |
| 309 | |
| 310 if (i == components.size() - 1) // Last component. | |
| 311 return entry; | |
| 312 else | |
| 313 current_dir = entry->AsDriveDirectory(); | |
| 314 } | |
| 315 return NULL; | |
| 316 } | |
| 317 | |
| 318 DriveEntry* GDataDirectoryService::GetEntryByResourceId( | |
| 319 const std::string& resource_id) { | |
| 320 DCHECK(!resource_id.empty()); | |
| 321 ResourceMap::const_iterator iter = resource_map_.find(resource_id); | |
| 322 return iter == resource_map_.end() ? NULL : iter->second; | |
| 323 } | |
| 324 | |
| 325 void GDataDirectoryService::GetEntryByResourceIdAsync( | |
| 326 const std::string& resource_id, | |
| 327 const GetEntryByResourceIdCallback& callback) { | |
| 328 DriveEntry* entry = GetEntryByResourceId(resource_id); | |
| 329 callback.Run(entry); | |
| 330 } | |
| 331 | |
| 332 void GDataDirectoryService::GetEntryInfoByResourceId( | |
| 333 const std::string& resource_id, | |
| 334 const GetEntryInfoWithFilePathCallback& callback) { | |
| 335 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 336 DCHECK(!callback.is_null()); | |
| 337 | |
| 338 scoped_ptr<DriveEntryProto> entry_proto; | |
| 339 GDataFileError error = GDATA_FILE_ERROR_FAILED; | |
| 340 FilePath drive_file_path; | |
| 341 | |
| 342 DriveEntry* entry = GetEntryByResourceId(resource_id); | |
| 343 if (entry) { | |
| 344 entry_proto.reset(new DriveEntryProto); | |
| 345 entry->ToProtoFull(entry_proto.get()); | |
| 346 error = GDATA_FILE_OK; | |
| 347 drive_file_path = entry->GetFilePath(); | |
| 348 } else { | |
| 349 error = GDATA_FILE_ERROR_NOT_FOUND; | |
| 350 } | |
| 351 | |
| 352 base::MessageLoopProxy::current()->PostTask( | |
| 353 FROM_HERE, | |
| 354 base::Bind(callback, | |
| 355 error, | |
| 356 drive_file_path, | |
| 357 base::Passed(&entry_proto))); | |
| 358 } | |
| 359 | |
| 360 void GDataDirectoryService::GetEntryInfoByPath( | |
| 361 const FilePath& path, | |
| 362 const GetEntryInfoCallback& callback) { | |
| 363 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 364 DCHECK(!callback.is_null()); | |
| 365 | |
| 366 scoped_ptr<DriveEntryProto> entry_proto; | |
| 367 GDataFileError error = GDATA_FILE_ERROR_FAILED; | |
| 368 | |
| 369 DriveEntry* entry = FindEntryByPathSync(path); | |
| 370 if (entry) { | |
| 371 entry_proto.reset(new DriveEntryProto); | |
| 372 entry->ToProtoFull(entry_proto.get()); | |
| 373 error = GDATA_FILE_OK; | |
| 374 } else { | |
| 375 error = GDATA_FILE_ERROR_NOT_FOUND; | |
| 376 } | |
| 377 | |
| 378 base::MessageLoopProxy::current()->PostTask( | |
| 379 FROM_HERE, | |
| 380 base::Bind(callback, error, base::Passed(&entry_proto))); | |
| 381 } | |
| 382 | |
| 383 void GDataDirectoryService::ReadDirectoryByPath( | |
| 384 const FilePath& path, | |
| 385 const ReadDirectoryCallback& callback) { | |
| 386 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 387 DCHECK(!callback.is_null()); | |
| 388 | |
| 389 scoped_ptr<DriveEntryProtoVector> entries; | |
| 390 GDataFileError error = GDATA_FILE_ERROR_FAILED; | |
| 391 | |
| 392 DriveEntry* entry = FindEntryByPathSync(path); | |
| 393 if (entry && entry->AsDriveDirectory()) { | |
| 394 entries = entry->AsDriveDirectory()->ToProtoVector(); | |
| 395 error = GDATA_FILE_OK; | |
| 396 } else if (entry && !entry->AsDriveDirectory()) { | |
| 397 error = GDATA_FILE_ERROR_NOT_A_DIRECTORY; | |
| 398 } else { | |
| 399 error = GDATA_FILE_ERROR_NOT_FOUND; | |
| 400 } | |
| 401 | |
| 402 base::MessageLoopProxy::current()->PostTask( | |
| 403 FROM_HERE, | |
| 404 base::Bind(callback, error, base::Passed(&entries))); | |
| 405 } | |
| 406 | |
| 407 void GDataDirectoryService::GetEntryInfoPairByPaths( | |
| 408 const FilePath& first_path, | |
| 409 const FilePath& second_path, | |
| 410 const GetEntryInfoPairCallback& callback) { | |
| 411 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 412 DCHECK(!callback.is_null()); | |
| 413 | |
| 414 // Get the first entry. | |
| 415 GetEntryInfoByPath( | |
| 416 first_path, | |
| 417 base::Bind(&GDataDirectoryService::GetEntryInfoPairByPathsAfterGetFirst, | |
| 418 weak_ptr_factory_.GetWeakPtr(), | |
| 419 first_path, | |
| 420 second_path, | |
| 421 callback)); | |
| 422 } | |
| 423 | |
| 424 void GDataDirectoryService::RefreshFile(scoped_ptr<DriveFile> fresh_file) { | |
| 425 DCHECK(fresh_file.get()); | |
| 426 | |
| 427 // Need to get a reference here because Passed() could get evaluated first. | |
| 428 const std::string& resource_id = fresh_file->resource_id(); | |
| 429 GetEntryByResourceIdAsync( | |
| 430 resource_id, | |
| 431 base::Bind(&GDataDirectoryService::RefreshFileInternal, | |
| 432 base::Passed(&fresh_file))); | |
| 433 } | |
| 434 | |
| 435 // static | |
| 436 void GDataDirectoryService::RefreshFileInternal( | |
| 437 scoped_ptr<DriveFile> fresh_file, | |
| 438 DriveEntry* old_entry) { | |
| 439 DriveDirectory* entry_parent = old_entry ? old_entry->parent() : NULL; | |
| 440 if (entry_parent) { | |
| 441 DCHECK_EQ(fresh_file->resource_id(), old_entry->resource_id()); | |
| 442 DCHECK(old_entry->AsDriveFile()); | |
| 443 | |
| 444 entry_parent->RemoveEntry(old_entry); | |
| 445 entry_parent->AddEntry(fresh_file.release()); | |
| 446 } | |
| 447 } | |
| 448 | |
| 449 void GDataDirectoryService::RefreshDirectory( | |
| 450 const std::string& directory_resource_id, | |
| 451 const ResourceMap& file_map, | |
| 452 const FileMoveCallback& callback) { | |
| 453 DCHECK(!callback.is_null()); | |
| 454 GetEntryByResourceIdAsync( | |
| 455 directory_resource_id, | |
| 456 base::Bind(&GDataDirectoryService::RefreshDirectoryInternal, | |
| 457 file_map, | |
| 458 callback)); | |
| 459 } | |
| 460 | |
| 461 // static | |
| 462 void GDataDirectoryService::RefreshDirectoryInternal( | |
| 463 const ResourceMap& file_map, | |
| 464 const FileMoveCallback& callback, | |
| 465 DriveEntry* directory_entry) { | |
| 466 DCHECK(!callback.is_null()); | |
| 467 | |
| 468 if (!directory_entry) { | |
| 469 callback.Run(GDATA_FILE_ERROR_NOT_FOUND, FilePath()); | |
| 470 return; | |
| 471 } | |
| 472 | |
| 473 DriveDirectory* directory = directory_entry->AsDriveDirectory(); | |
| 474 if (!directory) { | |
| 475 callback.Run(GDATA_FILE_ERROR_NOT_A_DIRECTORY, FilePath()); | |
| 476 return; | |
| 477 } | |
| 478 | |
| 479 DVLOG(1) << "RefreshDirectoryInternal"; | |
| 480 directory->RemoveChildFiles(); | |
| 481 // Add files from file_map. | |
| 482 for (ResourceMap::const_iterator it = file_map.begin(); | |
| 483 it != file_map.end(); ++it) { | |
| 484 scoped_ptr<DriveEntry> entry(it->second); | |
| 485 // Skip if it's not a file (i.e. directory). | |
| 486 if (!entry->AsDriveFile()) | |
| 487 continue; | |
| 488 directory->AddEntry(entry.release()); | |
| 489 } | |
| 490 | |
| 491 callback.Run(GDATA_FILE_OK, directory->GetFilePath()); | |
| 492 } | |
| 493 | |
| 494 void GDataDirectoryService::InitFromDB( | |
| 495 const FilePath& db_path, | |
| 496 base::SequencedTaskRunner* blocking_task_runner, | |
| 497 const FileOperationCallback& callback) { | |
| 498 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 499 DCHECK(!db_path.empty()); | |
| 500 DCHECK(blocking_task_runner); | |
| 501 | |
| 502 if (directory_service_db_.get()) { | |
| 503 if (!callback.is_null()) | |
| 504 callback.Run(GDATA_FILE_ERROR_FAILED); | |
| 505 return; | |
| 506 } | |
| 507 | |
| 508 blocking_task_runner_ = blocking_task_runner; | |
| 509 | |
| 510 DVLOG(1) << "InitFromDB " << db_path.value(); | |
| 511 | |
| 512 CreateDBParams* create_params = | |
| 513 new CreateDBParams(db_path, blocking_task_runner); | |
| 514 blocking_task_runner_->PostTaskAndReply( | |
| 515 FROM_HERE, | |
| 516 base::Bind(&CreateResourceMetadataDBOnBlockingPool, | |
| 517 create_params), | |
| 518 base::Bind(&GDataDirectoryService::InitResourceMap, | |
| 519 weak_ptr_factory_.GetWeakPtr(), | |
| 520 base::Owned(create_params), | |
| 521 callback)); | |
| 522 } | |
| 523 | |
| 524 void GDataDirectoryService::InitResourceMap( | |
| 525 CreateDBParams* create_params, | |
| 526 const FileOperationCallback& callback) { | |
| 527 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 528 DCHECK(create_params); | |
| 529 DCHECK(!directory_service_db_.get()); | |
| 530 | |
| 531 SerializedMap* serialized_resources = &create_params->serialized_resources; | |
| 532 directory_service_db_ = create_params->db.Pass(); | |
| 533 if (serialized_resources->empty()) { | |
| 534 origin_ = INITIALIZING; | |
| 535 if (!callback.is_null()) | |
| 536 callback.Run(GDATA_FILE_ERROR_NOT_FOUND); | |
| 537 return; | |
| 538 } | |
| 539 | |
| 540 ClearRoot(); | |
| 541 | |
| 542 // Version check. | |
| 543 int32 version = 0; | |
| 544 SerializedMap::iterator iter = serialized_resources->find(kDBKeyVersion); | |
| 545 if (iter == serialized_resources->end() || | |
| 546 !base::StringToInt(iter->second, &version) || | |
| 547 version != kProtoVersion) { | |
| 548 if (!callback.is_null()) | |
| 549 callback.Run(GDATA_FILE_ERROR_FAILED); | |
| 550 return; | |
| 551 } | |
| 552 serialized_resources->erase(iter); | |
| 553 | |
| 554 // Get the largest changestamp. | |
| 555 iter = serialized_resources->find(kDBKeyLargestChangestamp); | |
| 556 if (iter == serialized_resources->end() || | |
| 557 !base::StringToInt64(iter->second, &largest_changestamp_)) { | |
| 558 NOTREACHED() << "Could not find/parse largest_changestamp"; | |
| 559 if (!callback.is_null()) | |
| 560 callback.Run(GDATA_FILE_ERROR_FAILED); | |
| 561 return; | |
| 562 } else { | |
| 563 DVLOG(1) << "InitResourceMap largest_changestamp_" << largest_changestamp_; | |
| 564 serialized_resources->erase(iter); | |
| 565 } | |
| 566 | |
| 567 ResourceMap resource_map; | |
| 568 for (SerializedMap::const_iterator iter = serialized_resources->begin(); | |
| 569 iter != serialized_resources->end(); ++iter) { | |
| 570 if (iter->first.find(kDBKeyResourceIdPrefix) != 0) { | |
| 571 NOTREACHED() << "Incorrect prefix for db key " << iter->first; | |
| 572 continue; | |
| 573 } | |
| 574 | |
| 575 const std::string resource_id = | |
| 576 iter->first.substr(strlen(kDBKeyResourceIdPrefix)); | |
| 577 scoped_ptr<DriveEntry> entry = FromProtoString(iter->second); | |
| 578 if (entry.get()) { | |
| 579 DVLOG(1) << "Inserting resource " << resource_id | |
| 580 << " into resource_map"; | |
| 581 resource_map.insert(std::make_pair(resource_id, entry.release())); | |
| 582 } else { | |
| 583 NOTREACHED() << "Failed to parse DriveEntry for resource " << resource_id; | |
| 584 } | |
| 585 } | |
| 586 | |
| 587 // Fix up parent-child relations. | |
| 588 for (ResourceMap::iterator iter = resource_map.begin(); | |
| 589 iter != resource_map.end(); ++iter) { | |
| 590 DriveEntry* entry = iter->second; | |
| 591 ResourceMap::iterator parent_it = | |
| 592 resource_map.find(entry->parent_resource_id()); | |
| 593 if (parent_it != resource_map.end()) { | |
| 594 DriveDirectory* parent = parent_it->second->AsDriveDirectory(); | |
| 595 if (parent) { | |
| 596 DVLOG(1) << "Adding " << entry->resource_id() | |
| 597 << " as a child of " << parent->resource_id(); | |
| 598 parent->AddEntry(entry); | |
| 599 } else { | |
| 600 NOTREACHED() << "Parent is not a directory " << parent->resource_id(); | |
| 601 } | |
| 602 } else if (entry->resource_id() == kGDataRootDirectoryResourceId) { | |
| 603 root_.reset(entry->AsDriveDirectory()); | |
| 604 DCHECK(root_.get()); | |
| 605 AddEntryToResourceMap(root_.get()); | |
| 606 } else { | |
| 607 NOTREACHED() << "Missing parent id " << entry->parent_resource_id() | |
| 608 << " for resource " << entry->resource_id(); | |
| 609 } | |
| 610 } | |
| 611 | |
| 612 DCHECK(root_.get()); | |
| 613 DCHECK_EQ(resource_map.size(), resource_map_.size()); | |
| 614 DCHECK_EQ(resource_map.size(), serialized_resources->size()); | |
| 615 | |
| 616 origin_ = FROM_CACHE; | |
| 617 | |
| 618 if (!callback.is_null()) | |
| 619 callback.Run(GDATA_FILE_OK); | |
| 620 } | |
| 621 | |
| 622 void GDataDirectoryService::SaveToDB() { | |
| 623 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 624 | |
| 625 if (!blocking_task_runner_ || !directory_service_db_.get()) { | |
| 626 NOTREACHED(); | |
| 627 return; | |
| 628 } | |
| 629 | |
| 630 size_t serialized_size = 0; | |
| 631 SerializedMap serialized_resources; | |
| 632 for (ResourceMap::const_iterator iter = resource_map_.begin(); | |
| 633 iter != resource_map_.end(); ++iter) { | |
| 634 DriveEntryProto proto; | |
| 635 iter->second->ToProtoFull(&proto); | |
| 636 std::string serialized_string; | |
| 637 const bool ok = proto.SerializeToString(&serialized_string); | |
| 638 DCHECK(ok); | |
| 639 if (ok) { | |
| 640 serialized_resources.insert( | |
| 641 std::make_pair(std::string(kDBKeyResourceIdPrefix) + iter->first, | |
| 642 serialized_string)); | |
| 643 serialized_size += serialized_string.size(); | |
| 644 } | |
| 645 } | |
| 646 | |
| 647 serialized_resources.insert(std::make_pair(kDBKeyVersion, | |
| 648 base::IntToString(kProtoVersion))); | |
| 649 serialized_resources.insert(std::make_pair(kDBKeyLargestChangestamp, | |
| 650 base::IntToString(largest_changestamp_))); | |
| 651 set_last_serialized(base::Time::Now()); | |
| 652 set_serialized_size(serialized_size); | |
| 653 | |
| 654 blocking_task_runner_->PostTask( | |
| 655 FROM_HERE, | |
| 656 base::Bind(&ResourceMetadataDB::Save, | |
| 657 base::Unretained(directory_service_db_.get()), | |
| 658 serialized_resources)); | |
| 659 } | |
| 660 | |
| 661 void GDataDirectoryService::SerializeToString( | |
| 662 std::string* serialized_proto) const { | |
| 663 DriveRootDirectoryProto proto; | |
| 664 root_->ToProto(proto.mutable_gdata_directory()); | |
| 665 proto.set_largest_changestamp(largest_changestamp_); | |
| 666 proto.set_version(kProtoVersion); | |
| 667 | |
| 668 const bool ok = proto.SerializeToString(serialized_proto); | |
| 669 DCHECK(ok); | |
| 670 } | |
| 671 | |
| 672 bool GDataDirectoryService::ParseFromString( | |
| 673 const std::string& serialized_proto) { | |
| 674 DriveRootDirectoryProto proto; | |
| 675 if (!proto.ParseFromString(serialized_proto)) | |
| 676 return false; | |
| 677 | |
| 678 if (proto.version() != kProtoVersion) { | |
| 679 LOG(ERROR) << "Incompatible proto detected (incompatible version): " | |
| 680 << proto.version(); | |
| 681 return false; | |
| 682 } | |
| 683 | |
| 684 root_->FromProto(proto.gdata_directory()); | |
| 685 | |
| 686 origin_ = FROM_CACHE; | |
| 687 largest_changestamp_ = proto.largest_changestamp(); | |
| 688 | |
| 689 return true; | |
| 690 } | |
| 691 | |
| 692 scoped_ptr<DriveEntry> GDataDirectoryService::FromProtoString( | |
| 693 const std::string& serialized_proto) { | |
| 694 DriveEntryProto entry_proto; | |
| 695 if (!entry_proto.ParseFromString(serialized_proto)) | |
| 696 return scoped_ptr<DriveEntry>(); | |
| 697 | |
| 698 scoped_ptr<DriveEntry> entry; | |
| 699 if (entry_proto.file_info().is_directory()) { | |
| 700 entry.reset(CreateDriveDirectory()); | |
| 701 // Call DriveEntry::FromProto instead of DriveDirectory::FromProto because | |
| 702 // the proto does not include children. | |
| 703 entry->FromProto(entry_proto); | |
| 704 } else { | |
| 705 scoped_ptr<DriveFile> file(CreateDriveFile()); | |
| 706 // Call DriveFile::FromProto. | |
| 707 file->FromProto(entry_proto); | |
| 708 entry.reset(file.release()); | |
| 709 } | |
| 710 return entry.Pass(); | |
| 711 } | |
| 712 | |
| 713 void GDataDirectoryService::GetEntryInfoPairByPathsAfterGetFirst( | |
| 714 const FilePath& first_path, | |
| 715 const FilePath& second_path, | |
| 716 const GetEntryInfoPairCallback& callback, | |
| 717 GDataFileError error, | |
| 718 scoped_ptr<DriveEntryProto> entry_proto) { | |
| 719 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 720 DCHECK(!callback.is_null()); | |
| 721 | |
| 722 scoped_ptr<EntryInfoPairResult> result(new EntryInfoPairResult); | |
| 723 result->first.path = first_path; | |
| 724 result->first.error = error; | |
| 725 result->first.proto = entry_proto.Pass(); | |
| 726 | |
| 727 // If the first one is not found, don't continue. | |
| 728 if (error != GDATA_FILE_OK) { | |
| 729 callback.Run(result.Pass()); | |
| 730 return; | |
| 731 } | |
| 732 | |
| 733 // Get the second entry. | |
| 734 GetEntryInfoByPath( | |
| 735 second_path, | |
| 736 base::Bind(&GDataDirectoryService::GetEntryInfoPairByPathsAfterGetSecond, | |
| 737 weak_ptr_factory_.GetWeakPtr(), | |
| 738 second_path, | |
| 739 callback, | |
| 740 base::Passed(&result))); | |
| 741 } | |
| 742 | |
| 743 void GDataDirectoryService::GetEntryInfoPairByPathsAfterGetSecond( | |
| 744 const FilePath& second_path, | |
| 745 const GetEntryInfoPairCallback& callback, | |
| 746 scoped_ptr<EntryInfoPairResult> result, | |
| 747 GDataFileError error, | |
| 748 scoped_ptr<DriveEntryProto> entry_proto) { | |
| 749 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | |
| 750 DCHECK(!callback.is_null()); | |
| 751 DCHECK(result.get()); | |
| 752 | |
| 753 result->second.path = second_path; | |
| 754 result->second.error = error; | |
| 755 result->second.proto = entry_proto.Pass(); | |
| 756 | |
| 757 callback.Run(result.Pass()); | |
| 758 } | |
| 759 | |
| 760 } // namespace gdata | |
| OLD | NEW |