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/drive/resource_metadata.h" 6 7 #include "base/strings/string_number_conversions.h" 8 #include "base/strings/stringprintf.h" 9 #include "base/sys_info.h" 10 #include "chrome/browser/chromeos/drive/drive.pb.h" 11 #include "chrome/browser/chromeos/drive/file_system_util.h" 12 #include "chrome/browser/chromeos/drive/resource_metadata_storage.h" 13 #include "content/public/browser/browser_thread.h" 14 15 using content::BrowserThread; 16 17 namespace drive { 18 namespace { 19 20 // Sets entry's base name from its title and other attributes. 21 void SetBaseNameFromTitle(ResourceEntry* entry) { 22 std::string base_name = entry->title(); 23 if (entry->has_file_specific_info() && 24 entry->file_specific_info().is_hosted_document()) { 25 base_name += entry->file_specific_info().document_extension(); 26 } 27 entry->set_base_name(util::NormalizeFileName(base_name)); 28 } 29 30 // Creates an entry by copying |source|, and setting the base name properly. 31 ResourceEntry CreateEntryWithProperBaseName(const ResourceEntry& source) { 32 ResourceEntry entry(source); 33 SetBaseNameFromTitle(&entry); 34 return entry; 35 } 36 37 // Returns true if enough disk space is available for DB operation. 38 // TODO(hashimoto): Merge this with FileCache's FreeDiskSpaceGetterInterface. 39 bool EnoughDiskSpaceIsAvailableForDBOperation(const base::FilePath& path) { 40 const int64 kRequiredDiskSpaceInMB = 128; // 128 MB seems to be large enough. 41 return base::SysInfo::AmountOfFreeDiskSpace(path) >= 42 kRequiredDiskSpaceInMB * (1 << 20); 43 } 44 45 // Runs |callback| with arguments. 46 void RunGetResourceEntryCallback(const GetResourceEntryCallback& callback, 47 scoped_ptr<ResourceEntry> entry, 48 FileError error) { 49 DCHECK(!callback.is_null()); 50 51 if (error != FILE_ERROR_OK) 52 entry.reset(); 53 callback.Run(error, entry.Pass()); 54 } 55 56 // Runs |callback| with arguments. 57 void RunReadDirectoryCallback(const ReadDirectoryCallback& callback, 58 scoped_ptr<ResourceEntryVector> entries, 59 FileError error) { 60 DCHECK(!callback.is_null()); 61 62 if (error != FILE_ERROR_OK) 63 entries.reset(); 64 callback.Run(error, entries.Pass()); 65 } 66 67 // Runs |callback| with arguments. 68 void RunFileMoveCallback(const FileMoveCallback& callback, 69 base::FilePath* path, 70 FileError error) { 71 DCHECK(!callback.is_null()); 72 DCHECK(path); 73 74 callback.Run(error, *path); 75 } 76 77 // Helper function to run tasks with FileMoveCallback. 78 void PostFileMoveTask( 79 base::TaskRunner* task_runner, 80 const base::Callback<FileError(base::FilePath* out_file_path)>& task, 81 const FileMoveCallback& callback) { 82 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 83 DCHECK(!task.is_null()); 84 DCHECK(!callback.is_null()); 85 86 base::FilePath* file_path = new base::FilePath; 87 88 base::PostTaskAndReplyWithResult( 89 task_runner, 90 FROM_HERE, 91 base::Bind(task, file_path), 92 base::Bind(&RunFileMoveCallback, callback, base::Owned(file_path))); 93 } 94 95 FileError AddEntryWithFilePath(internal::ResourceMetadata* metadata, 96 const ResourceEntry& entry, 97 base::FilePath* out_file_path) { 98 DCHECK(metadata); 99 DCHECK(out_file_path); 100 FileError error = metadata->AddEntry(entry); 101 if (error == FILE_ERROR_OK) 102 *out_file_path = metadata->GetFilePath(entry.resource_id()); 103 return error; 104 } 105 106 } // namespace 107 108 std::string DirectoryFetchInfo::ToString() const { 109 return ("resource_id: " + resource_id_ + 110 ", changestamp: " + base::Int64ToString(changestamp_)); 111 } 112 113 EntryInfoResult::EntryInfoResult() : error(FILE_ERROR_FAILED) { 114 } 115 116 EntryInfoResult::~EntryInfoResult() { 117 } 118 119 EntryInfoPairResult::EntryInfoPairResult() { 120 } 121 122 EntryInfoPairResult::~EntryInfoPairResult() { 123 } 124 125 namespace internal { 126 127 ResourceMetadata::ResourceMetadata( 128 ResourceMetadataStorage* storage, 129 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner) 130 : blocking_task_runner_(blocking_task_runner), 131 storage_(storage), 132 weak_ptr_factory_(this) { 133 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 134 } 135 136 FileError ResourceMetadata::Initialize() { 137 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); 138 139 if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path())) 140 return FILE_ERROR_NO_LOCAL_SPACE; 141 142 if (!SetUpDefaultEntries()) 143 return FILE_ERROR_FAILED; 144 145 return FILE_ERROR_OK; 146 } 147 148 void ResourceMetadata::Destroy() { 149 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 150 151 weak_ptr_factory_.InvalidateWeakPtrs(); 152 blocking_task_runner_->PostTask( 153 FROM_HERE, 154 base::Bind(&ResourceMetadata::DestroyOnBlockingPool, 155 base::Unretained(this))); 156 } 157 158 void ResourceMetadata::ResetOnUIThread(const FileOperationCallback& callback) { 159 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 160 DCHECK(!callback.is_null()); 161 162 base::PostTaskAndReplyWithResult( 163 blocking_task_runner_.get(), 164 FROM_HERE, 165 base::Bind(&ResourceMetadata::Reset, base::Unretained(this)), 166 callback); 167 } 168 169 FileError ResourceMetadata::Reset() { 170 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); 171 172 if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path())) 173 return FILE_ERROR_NO_LOCAL_SPACE; 174 175 if (!storage_->SetLargestChangestamp(0) || 176 !RemoveEntryRecursively(util::kDriveGrandRootSpecialResourceId) || 177 !SetUpDefaultEntries()) 178 return FILE_ERROR_FAILED; 179 180 return FILE_ERROR_OK; 181 } 182 183 ResourceMetadata::~ResourceMetadata() { 184 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); 185 } 186 187 bool ResourceMetadata::SetUpDefaultEntries() { 188 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); 189 190 // Initialize the grand root and "other" entries. "/drive" and "/drive/other". 191 // As an intermediate change, "/drive/root" is also added here. 192 ResourceEntry entry; 193 if (!storage_->GetEntry(util::kDriveGrandRootSpecialResourceId, &entry)) { 194 ResourceEntry root; 195 root.mutable_file_info()->set_is_directory(true); 196 root.set_resource_id(util::kDriveGrandRootSpecialResourceId); 197 root.set_title(util::kDriveGrandRootDirName); 198 if (!storage_->PutEntry(CreateEntryWithProperBaseName(root))) 199 return false; 200 } 201 if (!storage_->GetEntry(util::kDriveOtherDirSpecialResourceId, &entry)) { 202 if (!PutEntryUnderDirectory(util::CreateOtherDirEntry())) 203 return false; 204 } 205 return true; 206 } 207 208 void ResourceMetadata::DestroyOnBlockingPool() { 209 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); 210 delete this; 211 } 212 213 void ResourceMetadata::GetLargestChangestampOnUIThread( 214 const GetChangestampCallback& callback) { 215 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 216 DCHECK(!callback.is_null()); 217 base::PostTaskAndReplyWithResult( 218 blocking_task_runner_.get(), 219 FROM_HERE, 220 base::Bind(&ResourceMetadata::GetLargestChangestamp, 221 base::Unretained(this)), 222 callback); 223 } 224 225 void ResourceMetadata::SetLargestChangestampOnUIThread( 226 int64 value, 227 const FileOperationCallback& callback) { 228 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 229 DCHECK(!callback.is_null()); 230 base::PostTaskAndReplyWithResult( 231 blocking_task_runner_.get(), 232 FROM_HERE, 233 base::Bind(&ResourceMetadata::SetLargestChangestamp, 234 base::Unretained(this), 235 value), 236 callback); 237 } 238 239 int64 ResourceMetadata::GetLargestChangestamp() { 240 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); 241 return storage_->GetLargestChangestamp(); 242 } 243 244 FileError ResourceMetadata::SetLargestChangestamp(int64 value) { 245 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); 246 247 if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path())) 248 return FILE_ERROR_NO_LOCAL_SPACE; 249 250 storage_->SetLargestChangestamp(value); 251 return FILE_ERROR_OK; 252 } 253 254 void ResourceMetadata::AddEntryOnUIThread(const ResourceEntry& entry, 255 const FileMoveCallback& callback) { 256 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 257 DCHECK(!callback.is_null()); 258 259 PostFileMoveTask( 260 blocking_task_runner_.get(), 261 base::Bind(&AddEntryWithFilePath, base::Unretained(this), entry), 262 callback); 263 } 264 265 FileError ResourceMetadata::AddEntry(const ResourceEntry& entry) { 266 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); 267 268 if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path())) 269 return FILE_ERROR_NO_LOCAL_SPACE; 270 271 ResourceEntry existing_entry; 272 if (storage_->GetEntry(entry.resource_id(), &existing_entry)) 273 return FILE_ERROR_EXISTS; 274 275 ResourceEntry parent; 276 if (!storage_->GetEntry(entry.parent_resource_id(), &parent) || 277 !parent.file_info().is_directory()) 278 return FILE_ERROR_NOT_FOUND; 279 280 if (!PutEntryUnderDirectory(entry)) 281 return FILE_ERROR_FAILED; 282 283 return FILE_ERROR_OK; 284 } 285 286 void ResourceMetadata::MoveEntryToDirectoryOnUIThread( 287 const base::FilePath& file_path, 288 const base::FilePath& directory_path, 289 const FileMoveCallback& callback) { 290 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 291 DCHECK(!callback.is_null()); 292 293 PostFileMoveTask(blocking_task_runner_.get(), 294 base::Bind(&ResourceMetadata::MoveEntryToDirectory, 295 base::Unretained(this), 296 file_path, 297 directory_path), 298 callback); 299 } 300 301 void ResourceMetadata::RenameEntryOnUIThread(const base::FilePath& file_path, 302 const std::string& new_name, 303 const FileMoveCallback& callback) { 304 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 305 DCHECK(!callback.is_null()); 306 307 PostFileMoveTask(blocking_task_runner_.get(), 308 base::Bind(&ResourceMetadata::RenameEntry, 309 base::Unretained(this), 310 file_path, 311 new_name), 312 callback); 313 } 314 315 FileError ResourceMetadata::RemoveEntry(const std::string& resource_id) { 316 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); 317 318 if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path())) 319 return FILE_ERROR_NO_LOCAL_SPACE; 320 321 // Disallow deletion of special entries "/drive" and "/drive/other". 322 if (util::IsSpecialResourceId(resource_id)) 323 return FILE_ERROR_ACCESS_DENIED; 324 325 ResourceEntry entry; 326 if (!storage_->GetEntry(resource_id, &entry)) 327 return FILE_ERROR_NOT_FOUND; 328 329 if (!RemoveEntryRecursively(entry.resource_id())) 330 return FILE_ERROR_FAILED; 331 return FILE_ERROR_OK; 332 } 333 334 void ResourceMetadata::GetResourceEntryByIdOnUIThread( 335 const std::string& resource_id, 336 const GetResourceEntryCallback& callback) { 337 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 338 DCHECK(!callback.is_null()); 339 340 scoped_ptr<ResourceEntry> entry(new ResourceEntry); 341 ResourceEntry* entry_ptr = entry.get(); 342 base::PostTaskAndReplyWithResult( 343 blocking_task_runner_.get(), 344 FROM_HERE, 345 base::Bind(&ResourceMetadata::GetResourceEntryById, 346 base::Unretained(this), 347 resource_id, 348 entry_ptr), 349 base::Bind(&RunGetResourceEntryCallback, callback, base::Passed(&entry))); 350 } 351 352 FileError ResourceMetadata::GetResourceEntryById( 353 const std::string& resource_id, 354 ResourceEntry* out_entry) { 355 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); 356 DCHECK(!resource_id.empty()); 357 DCHECK(out_entry); 358 359 return storage_->GetEntry(resource_id, out_entry) ? 360 FILE_ERROR_OK : FILE_ERROR_NOT_FOUND; 361 } 362 363 void ResourceMetadata::GetResourceEntryByPathOnUIThread( 364 const base::FilePath& file_path, 365 const GetResourceEntryCallback& callback) { 366 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 367 DCHECK(!callback.is_null()); 368 369 scoped_ptr<ResourceEntry> entry(new ResourceEntry); 370 ResourceEntry* entry_ptr = entry.get(); 371 base::PostTaskAndReplyWithResult( 372 blocking_task_runner_.get(), 373 FROM_HERE, 374 base::Bind(&ResourceMetadata::GetResourceEntryByPath, 375 base::Unretained(this), 376 file_path, 377 entry_ptr), 378 base::Bind(&RunGetResourceEntryCallback, callback, base::Passed(&entry))); 379 } 380 381 FileError ResourceMetadata::GetResourceEntryByPath(const base::FilePath& path, 382 ResourceEntry* out_entry) { 383 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); 384 DCHECK(out_entry); 385 386 return FindEntryByPathSync(path, out_entry) ? 387 FILE_ERROR_OK : FILE_ERROR_NOT_FOUND; 388 } 389 390 void ResourceMetadata::ReadDirectoryByPathOnUIThread( 391 const base::FilePath& file_path, 392 const ReadDirectoryCallback& callback) { 393 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 394 DCHECK(!callback.is_null()); 395 396 scoped_ptr<ResourceEntryVector> entries(new ResourceEntryVector); 397 ResourceEntryVector* entries_ptr = entries.get(); 398 base::PostTaskAndReplyWithResult( 399 blocking_task_runner_.get(), 400 FROM_HERE, 401 base::Bind(&ResourceMetadata::ReadDirectoryByPath, 402 base::Unretained(this), 403 file_path, 404 entries_ptr), 405 base::Bind(&RunReadDirectoryCallback, callback, base::Passed(&entries))); 406 } 407 408 FileError ResourceMetadata::ReadDirectoryByPath( 409 const base::FilePath& path, 410 ResourceEntryVector* out_entries) { 411 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); 412 DCHECK(out_entries); 413 414 ResourceEntry entry; 415 if (!FindEntryByPathSync(path, &entry)) 416 return FILE_ERROR_NOT_FOUND; 417 418 if (!entry.file_info().is_directory()) 419 return FILE_ERROR_NOT_A_DIRECTORY; 420 421 std::vector<std::string> children; 422 storage_->GetChildren(entry.resource_id(), &children); 423 424 ResourceEntryVector entries(children.size()); 425 for (size_t i = 0; i < children.size(); ++i) { 426 if (!storage_->GetEntry(children[i], &entries[i])) 427 return FILE_ERROR_FAILED; 428 } 429 out_entries->swap(entries); 430 return FILE_ERROR_OK; 431 } 432 433 FileError ResourceMetadata::RefreshEntry(const ResourceEntry& entry) { 434 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); 435 436 if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path())) 437 return FILE_ERROR_NO_LOCAL_SPACE; 438 439 ResourceEntry old_entry; 440 if (!storage_->GetEntry(entry.resource_id(), &old_entry)) 441 return FILE_ERROR_NOT_FOUND; 442 443 if (old_entry.parent_resource_id().empty() || // Reject root. 444 old_entry.file_info().is_directory() != // Reject incompatible input. 445 entry.file_info().is_directory()) 446 return FILE_ERROR_INVALID_OPERATION; 447 448 // Update data. 449 ResourceEntry new_parent; 450 if (!storage_->GetEntry(entry.parent_resource_id(), &new_parent) || 451 !new_parent.file_info().is_directory()) 452 return FILE_ERROR_NOT_FOUND; 453 454 // Remove from the old parent and add it to the new parent with the new data. 455 if (!PutEntryUnderDirectory(CreateEntryWithProperBaseName(entry))) 456 return FILE_ERROR_FAILED; 457 return FILE_ERROR_OK; 458 } 459 460 void ResourceMetadata::RefreshDirectoryOnUIThread( 461 const DirectoryFetchInfo& directory_fetch_info, 462 const ResourceEntryMap& entry_map, 463 const FileMoveCallback& callback) { 464 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 465 DCHECK(!callback.is_null()); 466 467 PostFileMoveTask(blocking_task_runner_.get(), 468 base::Bind(&ResourceMetadata::RefreshDirectory, 469 base::Unretained(this), 470 directory_fetch_info, 471 entry_map), 472 callback); 473 } 474 475 void ResourceMetadata::GetChildDirectories( 476 const std::string& resource_id, 477 std::set<base::FilePath>* child_directories) { 478 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); 479 480 std::vector<std::string> children; 481 storage_->GetChildren(resource_id, &children); 482 for (size_t i = 0; i < children.size(); ++i) { 483 ResourceEntry entry; 484 if (storage_->GetEntry(children[i], &entry) && 485 entry.file_info().is_directory()) { 486 child_directories->insert(GetFilePath(entry.resource_id())); 487 GetChildDirectories(entry.resource_id(), child_directories); 488 } 489 } 490 } 491 492 std::string ResourceMetadata::GetChildResourceId( 493 const std::string& parent_resource_id, const std::string& base_name) { 494 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); 495 return storage_->GetChild(parent_resource_id, base_name); 496 } 497 498 scoped_ptr<ResourceMetadata::Iterator> ResourceMetadata::GetIterator() { 499 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); 500 501 return storage_->GetIterator(); 502 } 503 504 base::FilePath ResourceMetadata::GetFilePath( 505 const std::string& resource_id) { 506 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); 507 508 base::FilePath path; 509 ResourceEntry entry; 510 if (storage_->GetEntry(resource_id, &entry)) { 511 if (!entry.parent_resource_id().empty()) 512 path = GetFilePath(entry.parent_resource_id()); 513 path = path.Append(base::FilePath::FromUTF8Unsafe(entry.base_name())); 514 } 515 return path; 516 } 517 518 FileError ResourceMetadata::MoveEntryToDirectory( 519 const base::FilePath& file_path, 520 const base::FilePath& directory_path, 521 base::FilePath* out_file_path) { 522 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); 523 DCHECK(!directory_path.empty()); 524 DCHECK(!file_path.empty()); 525 DCHECK(out_file_path); 526 527 if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path())) 528 return FILE_ERROR_NO_LOCAL_SPACE; 529 530 ResourceEntry entry, destination; 531 if (!FindEntryByPathSync(file_path, &entry) || 532 !FindEntryByPathSync(directory_path, &destination)) 533 return FILE_ERROR_NOT_FOUND; 534 if (!destination.file_info().is_directory()) 535 return FILE_ERROR_NOT_A_DIRECTORY; 536 537 entry.set_parent_resource_id(destination.resource_id()); 538 539 FileError error = RefreshEntry(entry); 540 if (error == FILE_ERROR_OK) 541 *out_file_path = GetFilePath(entry.resource_id()); 542 return error; 543 } 544 545 FileError ResourceMetadata::RenameEntry( 546 const base::FilePath& file_path, 547 const std::string& new_title, 548 base::FilePath* out_file_path) { 549 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); 550 DCHECK(!file_path.empty()); 551 DCHECK(!new_title.empty()); 552 DCHECK(out_file_path); 553 554 DVLOG(1) << "RenameEntry " << file_path.value() << " to " << new_title; 555 556 if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path())) 557 return FILE_ERROR_NO_LOCAL_SPACE; 558 559 ResourceEntry entry; 560 if (!FindEntryByPathSync(file_path, &entry)) 561 return FILE_ERROR_NOT_FOUND; 562 563 if (base::FilePath::FromUTF8Unsafe(new_title) == file_path.BaseName()) 564 return FILE_ERROR_EXISTS; 565 566 entry.set_title(new_title); 567 568 FileError error = RefreshEntry(entry); 569 if (error == FILE_ERROR_OK) 570 *out_file_path = GetFilePath(entry.resource_id()); 571 return error; 572 } 573 574 bool ResourceMetadata::FindEntryByPathSync(const base::FilePath& file_path, 575 ResourceEntry* out_entry) { 576 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); 577 578 // Start from the root. 579 ResourceEntry entry; 580 if (!storage_->GetEntry(util::kDriveGrandRootSpecialResourceId, &entry)) 581 return false; 582 DCHECK(entry.parent_resource_id().empty()); 583 584 // Check the first component. 585 std::vector<base::FilePath::StringType> components; 586 file_path.GetComponents(&components); 587 if (components.empty() || 588 base::FilePath(components[0]).AsUTF8Unsafe() != entry.base_name()) 589 return scoped_ptr<ResourceEntry>(); 590 591 // Iterate over the remaining components. 592 for (size_t i = 1; i < components.size(); ++i) { 593 const std::string component = base::FilePath(components[i]).AsUTF8Unsafe(); 594 const std::string resource_id = storage_->GetChild(entry.resource_id(), 595 component); 596 if (resource_id.empty() || !storage_->GetEntry(resource_id, &entry)) 597 return false; 598 DCHECK_EQ(entry.base_name(), component); 599 } 600 out_entry->Swap(&entry); 601 return true; 602 } 603 604 void ResourceMetadata::GetResourceEntryPairByPathsOnUIThread( 605 const base::FilePath& first_path, 606 const base::FilePath& second_path, 607 const GetResourceEntryPairCallback& callback) { 608 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 609 DCHECK(!callback.is_null()); 610 611 // Get the first entry. 612 GetResourceEntryByPathOnUIThread( 613 first_path, 614 base::Bind( 615 &ResourceMetadata::GetResourceEntryPairByPathsOnUIThreadAfterGetFirst, 616 weak_ptr_factory_.GetWeakPtr(), 617 first_path, 618 second_path, 619 callback)); 620 } 621 622 FileError ResourceMetadata::RefreshDirectory( 623 const DirectoryFetchInfo& directory_fetch_info, 624 const ResourceEntryMap& entry_map, 625 base::FilePath* out_file_path) { 626 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); 627 DCHECK(!directory_fetch_info.empty()); 628 629 if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path())) 630 return FILE_ERROR_NO_LOCAL_SPACE; 631 632 ResourceEntry directory; 633 if (!storage_->GetEntry(directory_fetch_info.resource_id(), &directory)) 634 return FILE_ERROR_NOT_FOUND; 635 636 if (!directory.file_info().is_directory()) 637 return FILE_ERROR_NOT_A_DIRECTORY; 638 639 directory.mutable_directory_specific_info()->set_changestamp( 640 directory_fetch_info.changestamp()); 641 storage_->PutEntry(directory); 642 643 // Go through the entry map. Handle existing entries and new entries. 644 for (ResourceEntryMap::const_iterator it = entry_map.begin(); 645 it != entry_map.end(); ++it) { 646 if (!EnoughDiskSpaceIsAvailableForDBOperation(storage_->directory_path())) 647 return FILE_ERROR_NO_LOCAL_SPACE; 648 649 const ResourceEntry& entry = it->second; 650 // Skip if the parent resource ID does not match. This is needed to 651 // handle entries with multiple parents. For such entries, the first 652 // parent is picked and other parents are ignored, hence some entries may 653 // have a parent resource ID which does not match the target directory's. 654 // 655 // TODO(satorux): Move the filtering logic to somewhere more appropriate. 656 // crbug.com/193525. 657 if (entry.parent_resource_id() != 658 directory_fetch_info.resource_id()) { 659 DVLOG(1) << "Wrong-parent entry rejected: " << entry.resource_id(); 660 continue; 661 } 662 663 if (!PutEntryUnderDirectory(CreateEntryWithProperBaseName(entry))) 664 return FILE_ERROR_FAILED; 665 } 666 667 if (out_file_path) 668 *out_file_path = GetFilePath(directory.resource_id()); 669 670 return FILE_ERROR_OK; 671 } 672 673 void ResourceMetadata::GetResourceEntryPairByPathsOnUIThreadAfterGetFirst( 674 const base::FilePath& first_path, 675 const base::FilePath& second_path, 676 const GetResourceEntryPairCallback& callback, 677 FileError error, 678 scoped_ptr<ResourceEntry> entry) { 679 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 680 DCHECK(!callback.is_null()); 681 682 scoped_ptr<EntryInfoPairResult> result(new EntryInfoPairResult); 683 result->first.path = first_path; 684 result->first.error = error; 685 result->first.entry = entry.Pass(); 686 687 // If the first one is not found, don't continue. 688 if (error != FILE_ERROR_OK) { 689 callback.Run(result.Pass()); 690 return; 691 } 692 693 // Get the second entry. 694 GetResourceEntryByPathOnUIThread( 695 second_path, 696 base::Bind( 697 &ResourceMetadata:: 698 GetResourceEntryPairByPathsOnUIThreadAfterGetSecond, 699 weak_ptr_factory_.GetWeakPtr(), 700 second_path, 701 callback, 702 base::Passed(&result))); 703 } 704 705 void ResourceMetadata::GetResourceEntryPairByPathsOnUIThreadAfterGetSecond( 706 const base::FilePath& second_path, 707 const GetResourceEntryPairCallback& callback, 708 scoped_ptr<EntryInfoPairResult> result, 709 FileError error, 710 scoped_ptr<ResourceEntry> entry) { 711 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 712 DCHECK(!callback.is_null()); 713 DCHECK(result.get()); 714 715 result->second.path = second_path; 716 result->second.error = error; 717 result->second.entry = entry.Pass(); 718 719 callback.Run(result.Pass()); 720 } 721 722 bool ResourceMetadata::PutEntryUnderDirectory( 723 const ResourceEntry& entry) { 724 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); 725 726 ResourceEntry updated_entry(entry); 727 728 // The entry name may have been changed due to prior name de-duplication. 729 // We need to first restore the file name based on the title before going 730 // through name de-duplication again when it is added to another directory. 731 SetBaseNameFromTitle(&updated_entry); 732 733 // Do file name de-duplication - Keep changing |entry|'s name until there is 734 // no other entry with the same name under the parent. 735 int modifier = 0; 736 std::string new_base_name = updated_entry.base_name(); 737 while (true) { 738 const std::string existing_entry_id = 739 storage_->GetChild(entry.parent_resource_id(), new_base_name); 740 if (existing_entry_id.empty() || existing_entry_id == entry.resource_id()) 741 break; 742 743 base::FilePath new_path = 744 base::FilePath::FromUTF8Unsafe(updated_entry.base_name()); 745 new_path = 746 new_path.InsertBeforeExtension(base::StringPrintf(" (%d)", ++modifier)); 747 // The new filename must be different from the previous one. 748 DCHECK_NE(new_base_name, new_path.AsUTF8Unsafe()); 749 new_base_name = new_path.AsUTF8Unsafe(); 750 } 751 updated_entry.set_base_name(new_base_name); 752 753 // Add the entry to resource map. 754 return storage_->PutEntry(updated_entry); 755 } 756 757 bool ResourceMetadata::RemoveEntryRecursively( 758 const std::string& resource_id) { 759 DCHECK(blocking_task_runner_->RunsTasksOnCurrentThread()); 760 761 ResourceEntry entry; 762 if (!storage_->GetEntry(resource_id, &entry)) 763 return false; 764 765 if (entry.file_info().is_directory()) { 766 std::vector<std::string> children; 767 storage_->GetChildren(resource_id, &children); 768 for (size_t i = 0; i < children.size(); ++i) { 769 if (!RemoveEntryRecursively(children[i])) 770 return false; 771 } 772 } 773 return storage_->RemoveEntry(resource_id); 774 } 775 776 } // namespace internal 777 } // namespace drive 778