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