file_system.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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/file_system.h" 6 7#include "base/bind.h" 8#include "base/file_util.h" 9#include "base/message_loop_proxy.h" 10#include "base/metrics/histogram.h" 11#include "base/platform_file.h" 12#include "base/prefs/pref_change_registrar.h" 13#include "base/prefs/pref_service.h" 14#include "base/stringprintf.h" 15#include "base/threading/sequenced_worker_pool.h" 16#include "chrome/browser/chromeos/drive/change_list_loader.h" 17#include "chrome/browser/chromeos/drive/change_list_processor.h" 18#include "chrome/browser/chromeos/drive/drive.pb.h" 19#include "chrome/browser/chromeos/drive/file_cache.h" 20#include "chrome/browser/chromeos/drive/file_system_observer.h" 21#include "chrome/browser/chromeos/drive/file_system_util.h" 22#include "chrome/browser/chromeos/drive/job_scheduler.h" 23#include "chrome/browser/chromeos/drive/resource_entry_conversion.h" 24#include "chrome/browser/chromeos/drive/search_metadata.h" 25#include "chrome/browser/google_apis/drive_api_parser.h" 26#include "chrome/browser/google_apis/drive_api_util.h" 27#include "chrome/browser/google_apis/drive_service_interface.h" 28#include "chrome/browser/profiles/profile.h" 29#include "chrome/common/chrome_notification_types.h" 30#include "chrome/common/pref_names.h" 31#include "content/public/browser/browser_thread.h" 32#include "content/public/browser/notification_details.h" 33 34using content::BrowserThread; 35 36namespace drive { 37namespace { 38 39const char kMimeTypeJson[] = "application/json"; 40 41//================================ Helper functions ============================ 42 43// Creates a temporary JSON file representing a document with |edit_url| 44// and |resource_id| under |document_dir| on blocking pool. 45FileError CreateDocumentJsonFileOnBlockingPool( 46 const base::FilePath& document_dir, 47 const GURL& edit_url, 48 const std::string& resource_id, 49 base::FilePath* temp_file_path) { 50 DCHECK(temp_file_path); 51 52 FileError error = FILE_ERROR_FAILED; 53 54 if (file_util::CreateTemporaryFileInDir(document_dir, temp_file_path)) { 55 std::string document_content = base::StringPrintf( 56 "{\"url\": \"%s\", \"resource_id\": \"%s\"}", 57 edit_url.spec().c_str(), resource_id.c_str()); 58 int document_size = static_cast<int>(document_content.size()); 59 if (file_util::WriteFile(*temp_file_path, document_content.data(), 60 document_size) == document_size) { 61 error = FILE_ERROR_OK; 62 } 63 } 64 65 if (error != FILE_ERROR_OK) 66 temp_file_path->clear(); 67 return error; 68} 69 70// Helper function for binding |path| to GetEntryInfoWithFilePathCallback and 71// create GetEntryInfoCallback. 72void RunGetEntryInfoWithFilePathCallback( 73 const GetEntryInfoWithFilePathCallback& callback, 74 const base::FilePath& path, 75 FileError error, 76 scoped_ptr<ResourceEntry> entry) { 77 DCHECK(!callback.is_null()); 78 callback.Run(error, path, entry.Pass()); 79} 80 81// Callback for ResourceMetadata::GetLargestChangestamp. 82// |callback| must not be null. 83void OnGetLargestChangestamp( 84 FileSystemMetadata metadata, // Will be modified. 85 const GetFilesystemMetadataCallback& callback, 86 int64 largest_changestamp) { 87 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 88 DCHECK(!callback.is_null()); 89 90 metadata.largest_changestamp = largest_changestamp; 91 callback.Run(metadata); 92} 93 94// Thin adapter to map GetFileCallback to FileOperationCallback. 95void GetFileCallbackToFileOperationCallbackAdapter( 96 const FileOperationCallback& callback, 97 FileError error, 98 const base::FilePath& unused_file_path, 99 const std::string& unused_mime_type, 100 DriveFileType unused_file_type) { 101 callback.Run(error); 102} 103 104// Wraps |callback| and always passes FILE_ERROR_OK when invoked. 105// This is used for adopting |callback| to wait for a non-fatal operation. 106void IgnoreError(const FileOperationCallback& callback, FileError error) { 107 callback.Run(FILE_ERROR_OK); 108} 109 110// Creates a file with unique name in |dir| and stores the path to |temp_file|. 111// Additionally, sets the permission of the file to allow read access from 112// others and group member users (i.e, "-rw-r--r--"). 113// We need this wrapper because Drive cache files may be read from other 114// processes (e.g., cros_disks for mounting zip files). 115// 116// Must be called on the blocking pool. 117bool CreateTemporaryReadableFileInDir(const base::FilePath& dir, 118 base::FilePath* temp_file) { 119 if (!file_util::CreateTemporaryFileInDir(dir, temp_file)) 120 return false; 121 return file_util::SetPosixFilePermissions( 122 *temp_file, 123 file_util::FILE_PERMISSION_READ_BY_USER | 124 file_util::FILE_PERMISSION_WRITE_BY_USER | 125 file_util::FILE_PERMISSION_READ_BY_GROUP | 126 file_util::FILE_PERMISSION_READ_BY_OTHERS); 127} 128 129} // namespace 130 131// FileSystem::GetFileCompleteForOpenParams struct implementation. 132struct FileSystem::GetFileCompleteForOpenParams { 133 GetFileCompleteForOpenParams(const OpenFileCallback& callback, 134 const std::string& resource_id, 135 const std::string& md5); 136 OpenFileCallback callback; 137 std::string resource_id; 138 std::string md5; 139}; 140 141FileSystem::GetFileCompleteForOpenParams::GetFileCompleteForOpenParams( 142 const OpenFileCallback& callback, 143 const std::string& resource_id, 144 const std::string& md5) 145 : callback(callback), 146 resource_id(resource_id), 147 md5(md5) { 148} 149 150// FileSystem::GetResolvedFileParams struct implementation. 151struct FileSystem::GetResolvedFileParams { 152 GetResolvedFileParams( 153 const base::FilePath& drive_file_path, 154 const DriveClientContext& context, 155 scoped_ptr<ResourceEntry> entry, 156 const GetFileContentInitializedCallback& initialized_callback, 157 const GetFileCallback& get_file_callback, 158 const google_apis::GetContentCallback& get_content_callback) 159 : drive_file_path(drive_file_path), 160 context(context), 161 entry(entry.Pass()), 162 initialized_callback(initialized_callback), 163 get_file_callback(get_file_callback), 164 get_content_callback(get_content_callback) { 165 DCHECK(!get_file_callback.is_null()); 166 DCHECK(this->entry); 167 } 168 169 void OnError(FileError error) { 170 get_file_callback.Run( 171 error, base::FilePath(), std::string(), REGULAR_FILE); 172 } 173 174 void OnCacheFileFound(const base::FilePath& local_file_path) { 175 if (initialized_callback.is_null()) { 176 return; 177 } 178 179 scoped_ptr<ResourceEntry> new_entry(new ResourceEntry(*entry)); 180 initialized_callback.Run(FILE_ERROR_OK, 181 new_entry.Pass(), 182 local_file_path, 183 base::Closure()); 184 } 185 186 void OnStartDownloading(const base::Closure& cancel_download_closure) { 187 if (initialized_callback.is_null()) { 188 return; 189 } 190 191 scoped_ptr<ResourceEntry> new_entry(new ResourceEntry(*entry)); 192 initialized_callback.Run(FILE_ERROR_OK, 193 new_entry.Pass(), 194 base::FilePath(), 195 cancel_download_closure); 196 } 197 198 void OnComplete(const base::FilePath& local_file_path) { 199 if (entry->file_specific_info().is_hosted_document()) { 200 get_file_callback.Run( 201 FILE_ERROR_OK, local_file_path, kMimeTypeJson, HOSTED_DOCUMENT); 202 } else { 203 get_file_callback.Run( 204 FILE_ERROR_OK, local_file_path, 205 entry->file_specific_info().content_mime_type(), REGULAR_FILE); 206 } 207 } 208 209 const base::FilePath drive_file_path; 210 const DriveClientContext context; 211 scoped_ptr<ResourceEntry> entry; 212 const GetFileContentInitializedCallback initialized_callback; 213 const GetFileCallback get_file_callback; 214 const google_apis::GetContentCallback get_content_callback; 215}; 216 217// FileSystem::AddUploadedFileParams implementation. 218struct FileSystem::AddUploadedFileParams { 219 AddUploadedFileParams(const base::FilePath& file_content_path, 220 const FileOperationCallback& callback, 221 const std::string& resource_id, 222 const std::string& md5) 223 : file_content_path(file_content_path), 224 callback(callback), 225 resource_id(resource_id), 226 md5(md5) { 227 } 228 229 base::FilePath file_content_path; 230 FileOperationCallback callback; 231 std::string resource_id; 232 std::string md5; 233}; 234 235 236// FileSystem class implementation. 237 238FileSystem::FileSystem( 239 Profile* profile, 240 FileCache* cache, 241 google_apis::DriveServiceInterface* drive_service, 242 JobScheduler* scheduler, 243 DriveWebAppsRegistry* webapps_registry, 244 internal::ResourceMetadata* resource_metadata, 245 base::SequencedTaskRunner* blocking_task_runner) 246 : profile_(profile), 247 cache_(cache), 248 drive_service_(drive_service), 249 scheduler_(scheduler), 250 webapps_registry_(webapps_registry), 251 resource_metadata_(resource_metadata), 252 last_update_check_error_(FILE_ERROR_OK), 253 hide_hosted_docs_(false), 254 blocking_task_runner_(blocking_task_runner), 255 weak_ptr_factory_(this) { 256 // Should be created from the file browser extension API on UI thread. 257 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 258} 259 260void FileSystem::Reload() { 261 resource_metadata_->Reset(base::Bind(&FileSystem::ReloadAfterReset, 262 weak_ptr_factory_.GetWeakPtr())); 263} 264 265void FileSystem::Initialize() { 266 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 267 268 SetupChangeListLoader(); 269 270 // Allocate the drive operation handlers. 271 drive_operations_.Init(scheduler_, 272 this, // FileSystemInterface 273 cache_, 274 resource_metadata_, 275 blocking_task_runner_, 276 this); // OperationObserver 277 278 PrefService* pref_service = profile_->GetPrefs(); 279 hide_hosted_docs_ = pref_service->GetBoolean(prefs::kDisableDriveHostedFiles); 280 281 InitializePreferenceObserver(); 282} 283 284void FileSystem::ReloadAfterReset(FileError error) { 285 if (error != FILE_ERROR_OK) { 286 LOG(ERROR) << "Failed to reset the resource metadata: " 287 << FileErrorToString(error); 288 return; 289 } 290 291 SetupChangeListLoader(); 292 293 change_list_loader_->LoadIfNeeded( 294 DirectoryFetchInfo(), 295 base::Bind(&FileSystem::OnUpdateChecked, 296 weak_ptr_factory_.GetWeakPtr())); 297} 298 299void FileSystem::SetupChangeListLoader() { 300 change_list_loader_.reset(new ChangeListLoader(resource_metadata_, 301 scheduler_, 302 webapps_registry_)); 303 change_list_loader_->AddObserver(this); 304} 305 306void FileSystem::CheckForUpdates() { 307 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 308 DVLOG(1) << "CheckForUpdates"; 309 310 if (change_list_loader_) { 311 change_list_loader_->CheckForUpdates( 312 base::Bind(&FileSystem::OnUpdateChecked, 313 weak_ptr_factory_.GetWeakPtr())); 314 } 315} 316 317void FileSystem::OnUpdateChecked(FileError error) { 318 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 319 DVLOG(1) << "CheckForUpdates finished: " << FileErrorToString(error); 320 last_update_check_time_ = base::Time::Now(); 321 last_update_check_error_ = error; 322} 323 324FileSystem::~FileSystem() { 325 // This should be called from UI thread, from DriveSystemService shutdown. 326 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 327 328 change_list_loader_->RemoveObserver(this); 329} 330 331void FileSystem::AddObserver(FileSystemObserver* observer) { 332 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 333 observers_.AddObserver(observer); 334} 335 336void FileSystem::RemoveObserver(FileSystemObserver* observer) { 337 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 338 observers_.RemoveObserver(observer); 339} 340 341void FileSystem::GetEntryInfoByResourceId( 342 const std::string& resource_id, 343 const GetEntryInfoWithFilePathCallback& callback) { 344 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 345 DCHECK(!resource_id.empty()); 346 DCHECK(!callback.is_null()); 347 348 resource_metadata_->GetEntryInfoByResourceId( 349 resource_id, 350 base::Bind(&FileSystem::GetEntryInfoByResourceIdAfterGetEntry, 351 weak_ptr_factory_.GetWeakPtr(), 352 callback)); 353} 354 355void FileSystem::GetEntryInfoByResourceIdAfterGetEntry( 356 const GetEntryInfoWithFilePathCallback& callback, 357 FileError error, 358 const base::FilePath& file_path, 359 scoped_ptr<ResourceEntry> entry) { 360 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 361 DCHECK(!callback.is_null()); 362 363 if (error != FILE_ERROR_OK) { 364 callback.Run(error, base::FilePath(), scoped_ptr<ResourceEntry>()); 365 return; 366 } 367 DCHECK(entry.get()); 368 369 CheckLocalModificationAndRun( 370 entry.Pass(), 371 base::Bind(&RunGetEntryInfoWithFilePathCallback, 372 callback, 373 file_path)); 374} 375 376void FileSystem::TransferFileFromRemoteToLocal( 377 const base::FilePath& remote_src_file_path, 378 const base::FilePath& local_dest_file_path, 379 const FileOperationCallback& callback) { 380 381 drive_operations_.TransferFileFromRemoteToLocal(remote_src_file_path, 382 local_dest_file_path, 383 callback); 384} 385 386void FileSystem::TransferFileFromLocalToRemote( 387 const base::FilePath& local_src_file_path, 388 const base::FilePath& remote_dest_file_path, 389 const FileOperationCallback& callback) { 390 391 drive_operations_.TransferFileFromLocalToRemote(local_src_file_path, 392 remote_dest_file_path, 393 callback); 394} 395 396void FileSystem::Copy(const base::FilePath& src_file_path, 397 const base::FilePath& dest_file_path, 398 const FileOperationCallback& callback) { 399 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 400 DCHECK(!callback.is_null()); 401 drive_operations_.Copy(src_file_path, dest_file_path, callback); 402} 403 404void FileSystem::Move(const base::FilePath& src_file_path, 405 const base::FilePath& dest_file_path, 406 const FileOperationCallback& callback) { 407 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 408 DCHECK(!callback.is_null()); 409 drive_operations_.Move(src_file_path, dest_file_path, callback); 410} 411 412void FileSystem::Remove(const base::FilePath& file_path, 413 bool is_recursive, 414 const FileOperationCallback& callback) { 415 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 416 DCHECK(!callback.is_null()); 417 drive_operations_.Remove(file_path, is_recursive, callback); 418} 419 420void FileSystem::CreateDirectory( 421 const base::FilePath& directory_path, 422 bool is_exclusive, 423 bool is_recursive, 424 const FileOperationCallback& callback) { 425 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 426 DCHECK(!callback.is_null()); 427 428 change_list_loader_->LoadIfNeeded( 429 DirectoryFetchInfo(), 430 base::Bind(&FileSystem::CreateDirectoryAfterLoad, 431 weak_ptr_factory_.GetWeakPtr(), 432 directory_path, is_exclusive, is_recursive, callback)); 433} 434 435void FileSystem::CreateDirectoryAfterLoad( 436 const base::FilePath& directory_path, 437 bool is_exclusive, 438 bool is_recursive, 439 const FileOperationCallback& callback, 440 FileError load_error) { 441 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 442 DCHECK(!callback.is_null()); 443 444 if (load_error != FILE_ERROR_OK) { 445 callback.Run(load_error); 446 return; 447 } 448 449 drive_operations_.CreateDirectory( 450 directory_path, is_exclusive, is_recursive, callback); 451} 452 453void FileSystem::CreateFile(const base::FilePath& file_path, 454 bool is_exclusive, 455 const FileOperationCallback& callback) { 456 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 457 DCHECK(!callback.is_null()); 458 459 drive_operations_.CreateFile(file_path, is_exclusive, callback); 460} 461 462void FileSystem::Pin(const base::FilePath& file_path, 463 const FileOperationCallback& callback) { 464 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 465 DCHECK(!callback.is_null()); 466 467 GetEntryInfoByPath(file_path, 468 base::Bind(&FileSystem::PinAfterGetEntryInfoByPath, 469 weak_ptr_factory_.GetWeakPtr(), 470 callback)); 471} 472 473void FileSystem::PinAfterGetEntryInfoByPath( 474 const FileOperationCallback& callback, 475 FileError error, 476 scoped_ptr<ResourceEntry> entry) { 477 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 478 DCHECK(!callback.is_null()); 479 480 // TODO(hashimoto): Support pinning directories. crbug.com/127831 481 if (entry && entry->file_info().is_directory()) 482 error = FILE_ERROR_NOT_A_FILE; 483 484 if (error != FILE_ERROR_OK) { 485 callback.Run(error); 486 return; 487 } 488 DCHECK(entry); 489 490 cache_->Pin(entry->resource_id(), entry->file_specific_info().file_md5(), 491 callback); 492} 493 494void FileSystem::Unpin(const base::FilePath& file_path, 495 const FileOperationCallback& callback) { 496 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 497 DCHECK(!callback.is_null()); 498 499 GetEntryInfoByPath(file_path, 500 base::Bind(&FileSystem::UnpinAfterGetEntryInfoByPath, 501 weak_ptr_factory_.GetWeakPtr(), 502 callback)); 503} 504 505void FileSystem::UnpinAfterGetEntryInfoByPath( 506 const FileOperationCallback& callback, 507 FileError error, 508 scoped_ptr<ResourceEntry> entry) { 509 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 510 DCHECK(!callback.is_null()); 511 512 // TODO(hashimoto): Support pinning directories. crbug.com/127831 513 if (entry && entry->file_info().is_directory()) 514 error = FILE_ERROR_NOT_A_FILE; 515 516 if (error != FILE_ERROR_OK) { 517 callback.Run(error); 518 return; 519 } 520 DCHECK(entry); 521 522 cache_->Unpin(entry->resource_id(), entry->file_specific_info().file_md5(), 523 callback); 524} 525 526void FileSystem::GetFileByPath(const base::FilePath& file_path, 527 const GetFileCallback& callback) { 528 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 529 DCHECK(!callback.is_null()); 530 531 resource_metadata_->GetEntryInfoByPath( 532 file_path, 533 base::Bind(&FileSystem::OnGetEntryInfoCompleteForGetFileByPath, 534 weak_ptr_factory_.GetWeakPtr(), 535 file_path, 536 callback)); 537} 538 539void FileSystem::OnGetEntryInfoCompleteForGetFileByPath( 540 const base::FilePath& file_path, 541 const GetFileCallback& callback, 542 FileError error, 543 scoped_ptr<ResourceEntry> entry) { 544 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 545 DCHECK(!callback.is_null()); 546 547 if (error != FILE_ERROR_OK) { 548 callback.Run(error, base::FilePath(), std::string(), REGULAR_FILE); 549 return; 550 } 551 DCHECK(entry); 552 553 GetResolvedFileByPath( 554 make_scoped_ptr(new GetResolvedFileParams( 555 file_path, 556 DriveClientContext(USER_INITIATED), 557 entry.Pass(), 558 GetFileContentInitializedCallback(), 559 callback, 560 google_apis::GetContentCallback()))); 561} 562 563void FileSystem::GetFileByResourceId( 564 const std::string& resource_id, 565 const DriveClientContext& context, 566 const GetFileCallback& get_file_callback, 567 const google_apis::GetContentCallback& get_content_callback) { 568 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 569 DCHECK(!resource_id.empty()); 570 DCHECK(!get_file_callback.is_null()); 571 572 resource_metadata_->GetEntryInfoByResourceId( 573 resource_id, 574 base::Bind(&FileSystem::GetFileByResourceIdAfterGetEntry, 575 weak_ptr_factory_.GetWeakPtr(), 576 context, 577 get_file_callback, 578 get_content_callback)); 579} 580 581void FileSystem::GetFileByResourceIdAfterGetEntry( 582 const DriveClientContext& context, 583 const GetFileCallback& get_file_callback, 584 const google_apis::GetContentCallback& get_content_callback, 585 FileError error, 586 const base::FilePath& file_path, 587 scoped_ptr<ResourceEntry> entry) { 588 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 589 DCHECK(!get_file_callback.is_null()); 590 591 if (error != FILE_ERROR_OK) { 592 get_file_callback.Run(FILE_ERROR_NOT_FOUND, 593 base::FilePath(), 594 std::string(), 595 REGULAR_FILE); 596 return; 597 } 598 599 GetResolvedFileByPath( 600 make_scoped_ptr(new GetResolvedFileParams( 601 file_path, 602 context, 603 entry.Pass(), 604 GetFileContentInitializedCallback(), 605 get_file_callback, 606 get_content_callback))); 607} 608 609void FileSystem::GetFileContentByPath( 610 const base::FilePath& file_path, 611 const GetFileContentInitializedCallback& initialized_callback, 612 const google_apis::GetContentCallback& get_content_callback, 613 const FileOperationCallback& completion_callback) { 614 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 615 DCHECK(!initialized_callback.is_null()); 616 DCHECK(!get_content_callback.is_null()); 617 DCHECK(!completion_callback.is_null()); 618 619 resource_metadata_->GetEntryInfoByPath( 620 file_path, 621 base::Bind(&FileSystem::GetFileContentByPathAfterGetEntry, 622 weak_ptr_factory_.GetWeakPtr(), 623 file_path, 624 initialized_callback, 625 get_content_callback, 626 completion_callback)); 627} 628 629void FileSystem::GetFileContentByPathAfterGetEntry( 630 const base::FilePath& file_path, 631 const GetFileContentInitializedCallback& initialized_callback, 632 const google_apis::GetContentCallback& get_content_callback, 633 const FileOperationCallback& completion_callback, 634 FileError error, 635 scoped_ptr<ResourceEntry> entry) { 636 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 637 DCHECK(!initialized_callback.is_null()); 638 DCHECK(!get_content_callback.is_null()); 639 DCHECK(!completion_callback.is_null()); 640 641 if (error != FILE_ERROR_OK) { 642 completion_callback.Run(error); 643 return; 644 } 645 646 DCHECK(entry); 647 GetResolvedFileByPath( 648 make_scoped_ptr(new GetResolvedFileParams( 649 file_path, 650 DriveClientContext(USER_INITIATED), 651 entry.Pass(), 652 initialized_callback, 653 base::Bind(&GetFileCallbackToFileOperationCallbackAdapter, 654 completion_callback), 655 get_content_callback))); 656} 657 658void FileSystem::GetEntryInfoByPath(const base::FilePath& file_path, 659 const GetEntryInfoCallback& callback) { 660 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 661 DCHECK(!callback.is_null()); 662 663 // ResourceMetadata may know about the entry even if the resource 664 // metadata is not yet fully loaded. For instance, ResourceMetadata() 665 // always knows about the root directory. For "fast fetch" 666 // (crbug.com/178348) to work, it's needed to delay the resource metadata 667 // loading until the first call to ReadDirectoryByPath(). 668 resource_metadata_->GetEntryInfoByPath( 669 file_path, 670 base::Bind(&FileSystem::GetEntryInfoByPathAfterGetEntry1, 671 weak_ptr_factory_.GetWeakPtr(), 672 file_path, 673 callback)); 674} 675 676void FileSystem::GetEntryInfoByPathAfterGetEntry1( 677 const base::FilePath& file_path, 678 const GetEntryInfoCallback& callback, 679 FileError error, 680 scoped_ptr<ResourceEntry> entry) { 681 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 682 DCHECK(!callback.is_null()); 683 684 if (error == FILE_ERROR_OK) { 685 CheckLocalModificationAndRun(entry.Pass(), callback); 686 return; 687 } 688 689 // Start loading if needed. Note that directory_fetch_info is empty here, 690 // as we don't need to fetch the contents of a directory when we just need 691 // to get an entry of the directory. 692 change_list_loader_->LoadIfNeeded( 693 DirectoryFetchInfo(), 694 base::Bind(&FileSystem::GetEntryInfoByPathAfterLoad, 695 weak_ptr_factory_.GetWeakPtr(), 696 file_path, 697 callback)); 698} 699 700void FileSystem::GetEntryInfoByPathAfterLoad( 701 const base::FilePath& file_path, 702 const GetEntryInfoCallback& callback, 703 FileError error) { 704 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 705 DCHECK(!callback.is_null()); 706 707 if (error != FILE_ERROR_OK) { 708 callback.Run(error, scoped_ptr<ResourceEntry>()); 709 return; 710 } 711 712 resource_metadata_->GetEntryInfoByPath( 713 file_path, 714 base::Bind(&FileSystem::GetEntryInfoByPathAfterGetEntry2, 715 weak_ptr_factory_.GetWeakPtr(), 716 callback)); 717} 718 719void FileSystem::GetEntryInfoByPathAfterGetEntry2( 720 const GetEntryInfoCallback& callback, 721 FileError error, 722 scoped_ptr<ResourceEntry> entry) { 723 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 724 DCHECK(!callback.is_null()); 725 726 if (error != FILE_ERROR_OK) { 727 callback.Run(error, scoped_ptr<ResourceEntry>()); 728 return; 729 } 730 DCHECK(entry.get()); 731 732 CheckLocalModificationAndRun(entry.Pass(), callback); 733} 734 735void FileSystem::ReadDirectoryByPath( 736 const base::FilePath& directory_path, 737 const ReadDirectoryWithSettingCallback& callback) { 738 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 739 DCHECK(!callback.is_null()); 740 741 // As described in GetEntryInfoByPath(), ResourceMetadata may know 742 // about the entry even if the file system is not yet fully loaded, hence we 743 // should just ask ResourceMetadata first. 744 resource_metadata_->GetEntryInfoByPath( 745 directory_path, 746 base::Bind(&FileSystem::ReadDirectoryByPathAfterGetEntry, 747 weak_ptr_factory_.GetWeakPtr(), 748 directory_path, 749 callback)); 750} 751 752void FileSystem::ReadDirectoryByPathAfterGetEntry( 753 const base::FilePath& directory_path, 754 const ReadDirectoryWithSettingCallback& callback, 755 FileError error, 756 scoped_ptr<ResourceEntry> entry) { 757 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 758 DCHECK(!callback.is_null()); 759 760 if (error != FILE_ERROR_OK) { 761 // If we don't know about the directory, start loading. 762 change_list_loader_->LoadIfNeeded( 763 DirectoryFetchInfo(), 764 base::Bind(&FileSystem::ReadDirectoryByPathAfterLoad, 765 weak_ptr_factory_.GetWeakPtr(), 766 directory_path, 767 callback)); 768 return; 769 } 770 771 if (!entry->file_info().is_directory()) { 772 callback.Run(FILE_ERROR_NOT_A_DIRECTORY, 773 hide_hosted_docs_, 774 scoped_ptr<ResourceEntryVector>()); 775 return; 776 } 777 778 // Pass the directory fetch info so we can fetch the contents of the 779 // directory before loading change lists. 780 DirectoryFetchInfo directory_fetch_info( 781 entry->resource_id(), 782 entry->directory_specific_info().changestamp()); 783 change_list_loader_->LoadIfNeeded( 784 directory_fetch_info, 785 base::Bind(&FileSystem::ReadDirectoryByPathAfterLoad, 786 weak_ptr_factory_.GetWeakPtr(), 787 directory_path, 788 callback)); 789} 790 791void FileSystem::ReadDirectoryByPathAfterLoad( 792 const base::FilePath& directory_path, 793 const ReadDirectoryWithSettingCallback& callback, 794 FileError error) { 795 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 796 DCHECK(!callback.is_null()); 797 798 if (error != FILE_ERROR_OK) { 799 callback.Run(error, 800 hide_hosted_docs_, 801 scoped_ptr<ResourceEntryVector>()); 802 return; 803 } 804 805 resource_metadata_->ReadDirectoryByPath( 806 directory_path, 807 base::Bind(&FileSystem::ReadDirectoryByPathAfterRead, 808 weak_ptr_factory_.GetWeakPtr(), 809 callback)); 810} 811 812void FileSystem::ReadDirectoryByPathAfterRead( 813 const ReadDirectoryWithSettingCallback& callback, 814 FileError error, 815 scoped_ptr<ResourceEntryVector> entries) { 816 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 817 DCHECK(!callback.is_null()); 818 819 if (error != FILE_ERROR_OK) { 820 callback.Run(error, 821 hide_hosted_docs_, 822 scoped_ptr<ResourceEntryVector>()); 823 return; 824 } 825 DCHECK(entries.get()); // This is valid for empty directories too. 826 827 callback.Run(FILE_ERROR_OK, hide_hosted_docs_, entries.Pass()); 828} 829 830void FileSystem::GetResolvedFileByPath( 831 scoped_ptr<GetResolvedFileParams> params) { 832 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 833 DCHECK(params); 834 835 if (params->entry->file_info().is_directory()) { 836 params->OnError(FILE_ERROR_NOT_A_FILE); 837 return; 838 } 839 840 // The file's entry should have its file specific info. 841 DCHECK(params->entry->has_file_specific_info()); 842 843 // For a hosted document, we create a special JSON file to represent the 844 // document instead of fetching the document content in one of the exported 845 // formats. The JSON file contains the edit URL and resource ID of the 846 // document. 847 if (params->entry->file_specific_info().is_hosted_document()) { 848 base::FilePath* temp_file_path = new base::FilePath; 849 ResourceEntry* entry_ptr = params->entry.get(); 850 base::PostTaskAndReplyWithResult( 851 blocking_task_runner_, 852 FROM_HERE, 853 base::Bind(&CreateDocumentJsonFileOnBlockingPool, 854 cache_->GetCacheDirectoryPath( 855 FileCache::CACHE_TYPE_TMP_DOCUMENTS), 856 GURL(entry_ptr->file_specific_info().alternate_url()), 857 entry_ptr->resource_id(), 858 temp_file_path), 859 base::Bind( 860 &FileSystem::GetResolvedFileByPathAfterCreateDocumentJsonFile, 861 weak_ptr_factory_.GetWeakPtr(), 862 base::Passed(¶ms), 863 base::Owned(temp_file_path))); 864 return; 865 } 866 867 // Returns absolute path of the file if it were cached or to be cached. 868 ResourceEntry* entry_ptr = params->entry.get(); 869 cache_->GetFile( 870 entry_ptr->resource_id(), 871 entry_ptr->file_specific_info().file_md5(), 872 base::Bind( 873 &FileSystem::GetResolvedFileByPathAfterGetFileFromCache, 874 weak_ptr_factory_.GetWeakPtr(), 875 base::Passed(¶ms))); 876} 877 878void FileSystem::GetResolvedFileByPathAfterCreateDocumentJsonFile( 879 scoped_ptr<GetResolvedFileParams> params, 880 const base::FilePath* file_path, 881 FileError error) { 882 DCHECK(params); 883 DCHECK(file_path); 884 885 if (error != FILE_ERROR_OK) { 886 params->OnError(error); 887 return; 888 } 889 890 params->OnCacheFileFound(*file_path); 891 params->OnComplete(*file_path); 892} 893 894void FileSystem::GetResolvedFileByPathAfterGetFileFromCache( 895 scoped_ptr<GetResolvedFileParams> params, 896 FileError error, 897 const base::FilePath& cache_file_path) { 898 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 899 DCHECK(params); 900 901 // Have we found the file in cache? If so, return it back to the caller. 902 if (error == FILE_ERROR_OK) { 903 params->OnCacheFileFound(cache_file_path); 904 params->OnComplete(cache_file_path); 905 return; 906 } 907 908 // If cache file is not found, try to download the file from the server 909 // instead. This logic is rather complicated but here's how this works: 910 // 911 // Retrieve fresh file metadata from server. We will extract file size and 912 // content url from there (we want to make sure used content url is not 913 // stale). 914 // 915 // Check if we have enough space, based on the expected file size. 916 // - if we don't have enough space, try to free up the disk space 917 // - if we still don't have enough space, return "no space" error 918 // - if we have enough space, start downloading the file from the server 919 GetResolvedFileParams* params_ptr = params.get(); 920 scheduler_->GetResourceEntry( 921 params_ptr->entry->resource_id(), 922 params_ptr->context, 923 base::Bind(&FileSystem::GetResolvedFileByPathAfterGetResourceEntry, 924 weak_ptr_factory_.GetWeakPtr(), 925 base::Passed(¶ms))); 926} 927 928void FileSystem::GetResolvedFileByPathAfterGetResourceEntry( 929 scoped_ptr<GetResolvedFileParams> params, 930 google_apis::GDataErrorCode status, 931 scoped_ptr<google_apis::ResourceEntry> entry) { 932 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 933 DCHECK(params); 934 935 const FileError error = util::GDataToFileError(status); 936 if (error != FILE_ERROR_OK) { 937 params->OnError(error); 938 return; 939 } 940 941 // The download URL is: 942 // 1) src attribute of content element, on GData WAPI. 943 // 2) the value of the key 'downloadUrl', on Drive API v2. 944 // In both cases, we can use ResourceEntry::download_url(). 945 const GURL& download_url = entry->download_url(); 946 947 // The content URL can be empty for non-downloadable files (such as files 948 // shared from others with "prevent downloading by viewers" flag set.) 949 if (download_url.is_empty()) { 950 params->OnError(FILE_ERROR_ACCESS_DENIED); 951 return; 952 } 953 954 DCHECK_EQ(params->entry->resource_id(), entry->resource_id()); 955 resource_metadata_->RefreshEntry( 956 ConvertToResourceEntry(*entry), 957 base::Bind(&FileSystem::GetResolvedFileByPathAfterRefreshEntry, 958 weak_ptr_factory_.GetWeakPtr(), 959 base::Passed(¶ms), 960 download_url)); 961} 962 963void FileSystem::GetResolvedFileByPathAfterRefreshEntry( 964 scoped_ptr<GetResolvedFileParams> params, 965 const GURL& download_url, 966 FileError error, 967 const base::FilePath& drive_file_path, 968 scoped_ptr<ResourceEntry> entry) { 969 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 970 DCHECK(params); 971 972 if (error != FILE_ERROR_OK) { 973 params->OnError(error); 974 return; 975 } 976 977 int64 file_size = entry->file_info().size(); 978 params->entry = entry.Pass(); // Update the entry in |params|. 979 cache_->FreeDiskSpaceIfNeededFor( 980 file_size, 981 base::Bind(&FileSystem::GetResolvedFileByPathAfterFreeDiskSpace, 982 weak_ptr_factory_.GetWeakPtr(), 983 base::Passed(¶ms), 984 download_url)); 985} 986 987void FileSystem::GetResolvedFileByPathAfterFreeDiskSpace( 988 scoped_ptr<GetResolvedFileParams> params, 989 const GURL& download_url, 990 bool has_enough_space) { 991 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 992 DCHECK(params); 993 994 if (!has_enough_space) { 995 // If no enough space, return FILE_ERROR_NO_SPACE. 996 params->OnError(FILE_ERROR_NO_SPACE); 997 return; 998 } 999 1000 // We have enough disk space. Create download destination file. 1001 const base::FilePath temp_download_directory = 1002 cache_->GetCacheDirectoryPath(FileCache::CACHE_TYPE_TMP_DOWNLOADS); 1003 base::FilePath* file_path = new base::FilePath; 1004 base::PostTaskAndReplyWithResult( 1005 blocking_task_runner_, 1006 FROM_HERE, 1007 base::Bind(&CreateTemporaryReadableFileInDir, 1008 temp_download_directory, 1009 file_path), 1010 base::Bind(&FileSystem::GetResolveFileByPathAfterCreateTemporaryFile, 1011 weak_ptr_factory_.GetWeakPtr(), 1012 base::Passed(¶ms), 1013 download_url, 1014 base::Owned(file_path))); 1015} 1016 1017void FileSystem::GetResolveFileByPathAfterCreateTemporaryFile( 1018 scoped_ptr<GetResolvedFileParams> params, 1019 const GURL& download_url, 1020 base::FilePath* temp_file, 1021 bool success) { 1022 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1023 DCHECK(params); 1024 1025 if (!success) { 1026 params->OnError(FILE_ERROR_FAILED); 1027 return; 1028 } 1029 1030 GetResolvedFileParams* params_ptr = params.get(); 1031 JobID id = scheduler_->DownloadFile( 1032 params_ptr->drive_file_path, 1033 *temp_file, 1034 download_url, 1035 params_ptr->context, 1036 base::Bind(&FileSystem::GetResolvedFileByPathAfterDownloadFile, 1037 weak_ptr_factory_.GetWeakPtr(), 1038 base::Passed(¶ms)), 1039 params_ptr->get_content_callback); 1040 params_ptr->OnStartDownloading( 1041 base::Bind(&FileSystem::CancelJobInScheduler, 1042 weak_ptr_factory_.GetWeakPtr(), 1043 id)); 1044} 1045 1046void FileSystem::GetResolvedFileByPathAfterDownloadFile( 1047 scoped_ptr<GetResolvedFileParams> params, 1048 google_apis::GDataErrorCode status, 1049 const base::FilePath& downloaded_file_path) { 1050 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1051 DCHECK(params); 1052 1053 // If user cancels download of a pinned-but-not-fetched file, mark file as 1054 // unpinned so that we do not sync the file again. 1055 if (status == google_apis::GDATA_CANCELLED) { 1056 cache_->GetCacheEntry( 1057 params->entry->resource_id(), 1058 params->entry->file_specific_info().file_md5(), 1059 base::Bind( 1060 &FileSystem::GetResolvedFileByPathAfterGetCacheEntryForCancel, 1061 weak_ptr_factory_.GetWeakPtr(), 1062 params->entry->resource_id(), 1063 params->entry->file_specific_info().file_md5())); 1064 } 1065 1066 FileError error = util::GDataToFileError(status); 1067 if (error != FILE_ERROR_OK) { 1068 params->OnError(error); 1069 return; 1070 } 1071 1072 ResourceEntry* entry = params->entry.get(); 1073 cache_->Store(entry->resource_id(), 1074 entry->file_specific_info().file_md5(), 1075 downloaded_file_path, 1076 FileCache::FILE_OPERATION_MOVE, 1077 base::Bind(&FileSystem::GetResolvedFileByPathAfterStore, 1078 weak_ptr_factory_.GetWeakPtr(), 1079 base::Passed(¶ms), 1080 downloaded_file_path)); 1081} 1082 1083void FileSystem::GetResolvedFileByPathAfterGetCacheEntryForCancel( 1084 const std::string& resource_id, 1085 const std::string& md5, 1086 bool success, 1087 const FileCacheEntry& cache_entry) { 1088 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1089 // TODO(hshi): http://crbug.com/127138 notify when file properties change. 1090 // This allows file manager to clear the "Available offline" checkbox. 1091 if (success && cache_entry.is_pinned()) { 1092 cache_->Unpin(resource_id, 1093 md5, 1094 base::Bind(&util::EmptyFileOperationCallback)); 1095 } 1096} 1097 1098void FileSystem::GetResolvedFileByPathAfterStore( 1099 scoped_ptr<GetResolvedFileParams> params, 1100 const base::FilePath& downloaded_file_path, 1101 FileError error) { 1102 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1103 DCHECK(params); 1104 1105 if (error != FILE_ERROR_OK) { 1106 blocking_task_runner_->PostTask( 1107 FROM_HERE, 1108 base::Bind(base::IgnoreResult(&file_util::Delete), 1109 downloaded_file_path, 1110 false /* recursive*/)); 1111 params->OnError(error); 1112 return; 1113 } 1114 // Storing to cache changes the "offline available" status, hence notify. 1115 OnDirectoryChanged(params->drive_file_path.DirName()); 1116 1117 ResourceEntry* entry = params->entry.get(); 1118 cache_->GetFile( 1119 entry->resource_id(), 1120 entry->file_specific_info().file_md5(), 1121 base::Bind(&FileSystem::GetResolvedFileByPathAfterGetFile, 1122 weak_ptr_factory_.GetWeakPtr(), 1123 base::Passed(¶ms))); 1124} 1125 1126void FileSystem::GetResolvedFileByPathAfterGetFile( 1127 scoped_ptr<GetResolvedFileParams> params, 1128 FileError error, 1129 const base::FilePath& cache_file) { 1130 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1131 DCHECK(params); 1132 1133 if (error != FILE_ERROR_OK) { 1134 params->OnError(error); 1135 return; 1136 } 1137 params->OnComplete(cache_file); 1138} 1139 1140void FileSystem::RefreshDirectory( 1141 const base::FilePath& directory_path, 1142 const FileOperationCallback& callback) { 1143 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1144 DCHECK(!callback.is_null()); 1145 1146 // Make sure the destination directory exists. 1147 resource_metadata_->GetEntryInfoByPath( 1148 directory_path, 1149 base::Bind(&FileSystem::RefreshDirectoryAfterGetEntryInfo, 1150 weak_ptr_factory_.GetWeakPtr(), 1151 directory_path, 1152 callback)); 1153} 1154 1155void FileSystem::RefreshDirectoryAfterGetEntryInfo( 1156 const base::FilePath& directory_path, 1157 const FileOperationCallback& callback, 1158 FileError error, 1159 scoped_ptr<ResourceEntry> entry) { 1160 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1161 DCHECK(!callback.is_null()); 1162 1163 if (error != FILE_ERROR_OK) { 1164 callback.Run(error); 1165 return; 1166 } 1167 if (!entry->file_info().is_directory()) { 1168 callback.Run(FILE_ERROR_NOT_A_DIRECTORY); 1169 return; 1170 } 1171 if (util::IsSpecialResourceId(entry->resource_id())) { 1172 // Do not load special directories. Just return. 1173 callback.Run(FILE_ERROR_OK); 1174 return; 1175 } 1176 1177 change_list_loader_->LoadDirectoryFromServer( 1178 entry->resource_id(), 1179 callback); 1180} 1181 1182void FileSystem::UpdateFileByResourceId( 1183 const std::string& resource_id, 1184 const DriveClientContext& context, 1185 const FileOperationCallback& callback) { 1186 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1187 DCHECK(!callback.is_null()); 1188 1189 drive_operations_.UpdateFileByResourceId(resource_id, context, callback); 1190} 1191 1192void FileSystem::GetAvailableSpace( 1193 const GetAvailableSpaceCallback& callback) { 1194 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1195 DCHECK(!callback.is_null()); 1196 1197 scheduler_->GetAboutResource( 1198 base::Bind(&FileSystem::OnGetAboutResource, 1199 weak_ptr_factory_.GetWeakPtr(), 1200 callback)); 1201} 1202 1203void FileSystem::OnGetAboutResource( 1204 const GetAvailableSpaceCallback& callback, 1205 google_apis::GDataErrorCode status, 1206 scoped_ptr<google_apis::AboutResource> about_resource) { 1207 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1208 DCHECK(!callback.is_null()); 1209 1210 FileError error = util::GDataToFileError(status); 1211 if (error != FILE_ERROR_OK) { 1212 callback.Run(error, -1, -1); 1213 return; 1214 } 1215 DCHECK(about_resource); 1216 1217 callback.Run(FILE_ERROR_OK, 1218 about_resource->quota_bytes_total(), 1219 about_resource->quota_bytes_used()); 1220} 1221 1222void FileSystem::OnSearch(const SearchCallback& search_callback, 1223 ScopedVector<ChangeList> change_lists, 1224 FileError error) { 1225 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1226 DCHECK(!search_callback.is_null()); 1227 1228 if (error != FILE_ERROR_OK) { 1229 search_callback.Run(error, 1230 GURL(), 1231 scoped_ptr<std::vector<SearchResultInfo> >()); 1232 return; 1233 } 1234 1235 // The search results will be returned using virtual directory. 1236 // The directory is not really part of the file system, so it has no parent or 1237 // root. 1238 std::vector<SearchResultInfo>* results(new std::vector<SearchResultInfo>()); 1239 scoped_ptr<std::vector<SearchResultInfo> > result_vec(results); 1240 1241 DCHECK_EQ(1u, change_lists.size()); 1242 const ChangeList* change_list = change_lists[0]; 1243 1244 // TODO(tbarzic): Limit total number of returned results for the query. 1245 const GURL& next_feed = change_list->next_url(); 1246 1247 const base::Closure callback = base::Bind( 1248 search_callback, FILE_ERROR_OK, next_feed, base::Passed(&result_vec)); 1249 1250 const std::vector<ResourceEntry>& entries = change_list->entries(); 1251 if (entries.empty()) { 1252 callback.Run(); 1253 return; 1254 } 1255 1256 DVLOG(1) << "OnSearch number of entries=" << entries.size(); 1257 // Go through all entries generated by the feed and add them to the search 1258 // result directory. 1259 for (size_t i = 0; i < entries.size(); ++i) { 1260 // Run the callback if this is the last iteration of the loop. 1261 const bool should_run_callback = (i + 1 == entries.size()); 1262 const ResourceEntry& entry = entries[i]; 1263 1264 const GetEntryInfoWithFilePathCallback entry_info_callback = 1265 base::Bind(&FileSystem::AddToSearchResults, 1266 weak_ptr_factory_.GetWeakPtr(), 1267 results, 1268 should_run_callback, 1269 callback); 1270 1271 resource_metadata_->RefreshEntry(entry, entry_info_callback); 1272 } 1273} 1274 1275void FileSystem::AddToSearchResults( 1276 std::vector<SearchResultInfo>* results, 1277 bool should_run_callback, 1278 const base::Closure& callback, 1279 FileError error, 1280 const base::FilePath& drive_file_path, 1281 scoped_ptr<ResourceEntry> entry) { 1282 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1283 1284 // If a result is not present in our local file system snapshot, call 1285 // CheckForUpdates to refresh the snapshot with a delta feed. This may happen 1286 // if the entry has recently been added to the drive (and we still haven't 1287 // received its delta feed). 1288 if (error == FILE_ERROR_OK) { 1289 DCHECK(entry.get()); 1290 results->push_back(SearchResultInfo(drive_file_path, *entry.get())); 1291 DVLOG(1) << "AddToSearchResults " << drive_file_path.value(); 1292 } else if (error == FILE_ERROR_NOT_FOUND) { 1293 CheckForUpdates(); 1294 } else { 1295 NOTREACHED(); 1296 } 1297 1298 if (should_run_callback) 1299 callback.Run(); 1300} 1301 1302void FileSystem::Search(const std::string& search_query, 1303 const GURL& next_feed, 1304 const SearchCallback& callback) { 1305 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1306 DCHECK(!callback.is_null()); 1307 1308 change_list_loader_->SearchFromServer( 1309 search_query, 1310 next_feed, 1311 base::Bind(&FileSystem::OnSearch, 1312 weak_ptr_factory_.GetWeakPtr(), 1313 callback)); 1314} 1315 1316void FileSystem::SearchMetadata(const std::string& query, 1317 int options, 1318 int at_most_num_matches, 1319 const SearchMetadataCallback& callback) { 1320 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1321 1322 if (hide_hosted_docs_) 1323 options |= SEARCH_METADATA_EXCLUDE_HOSTED_DOCUMENTS; 1324 1325 drive::SearchMetadata(resource_metadata_, 1326 query, 1327 options, 1328 at_most_num_matches, 1329 callback); 1330} 1331 1332void FileSystem::OnDirectoryChangedByOperation( 1333 const base::FilePath& directory_path) { 1334 OnDirectoryChanged(directory_path); 1335} 1336 1337void FileSystem::OnDirectoryChanged(const base::FilePath& directory_path) { 1338 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1339 1340 FOR_EACH_OBSERVER(FileSystemObserver, observers_, 1341 OnDirectoryChanged(directory_path)); 1342} 1343 1344void FileSystem::OnFeedFromServerLoaded() { 1345 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1346 1347 FOR_EACH_OBSERVER(FileSystemObserver, observers_, 1348 OnFeedFromServerLoaded()); 1349} 1350 1351void FileSystem::OnInitialFeedLoaded() { 1352 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1353 1354 FOR_EACH_OBSERVER(FileSystemObserver, 1355 observers_, 1356 OnInitialLoadFinished()); 1357} 1358 1359void FileSystem::AddUploadedFile( 1360 scoped_ptr<google_apis::ResourceEntry> entry, 1361 const base::FilePath& file_content_path, 1362 const FileOperationCallback& callback) { 1363 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1364 DCHECK(entry.get()); 1365 DCHECK(!entry->resource_id().empty()); 1366 DCHECK(!entry->file_md5().empty()); 1367 DCHECK(!callback.is_null()); 1368 1369 AddUploadedFileParams params(file_content_path, 1370 callback, 1371 entry->resource_id(), 1372 entry->file_md5()); 1373 1374 resource_metadata_->AddEntry( 1375 ConvertToResourceEntry(*entry), 1376 base::Bind(&FileSystem::AddUploadedFileToCache, 1377 weak_ptr_factory_.GetWeakPtr(), params)); 1378} 1379 1380void FileSystem::AddUploadedFileToCache( 1381 const AddUploadedFileParams& params, 1382 FileError error, 1383 const base::FilePath& file_path) { 1384 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1385 DCHECK(!params.resource_id.empty()); 1386 DCHECK(!params.md5.empty()); 1387 DCHECK(!params.callback.is_null()); 1388 1389 // Depending on timing, a metadata may have inserted via delta feed already. 1390 // So, FILE_ERROR_EXISTS is not an error. 1391 if (error != FILE_ERROR_OK && error != FILE_ERROR_EXISTS) { 1392 params.callback.Run(error); 1393 return; 1394 } 1395 1396 OnDirectoryChanged(file_path.DirName()); 1397 1398 // At this point, upload to the server is fully succeeded. Failure to store to 1399 // cache is not a fatal error, so we wrap the callback with IgnoreError, and 1400 // always return success to the caller. 1401 cache_->Store(params.resource_id, 1402 params.md5, 1403 params.file_content_path, 1404 FileCache::FILE_OPERATION_COPY, 1405 base::Bind(&IgnoreError, params.callback)); 1406} 1407 1408void FileSystem::GetMetadata( 1409 const GetFilesystemMetadataCallback& callback) { 1410 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1411 DCHECK(!callback.is_null()); 1412 1413 FileSystemMetadata metadata; 1414 metadata.refreshing = change_list_loader_->IsRefreshing(); 1415 1416 // Metadata related to delta update. 1417 metadata.last_update_check_time = last_update_check_time_; 1418 metadata.last_update_check_error = last_update_check_error_; 1419 1420 resource_metadata_->GetLargestChangestamp( 1421 base::Bind(&OnGetLargestChangestamp, metadata, callback)); 1422} 1423 1424void FileSystem::MarkCacheFileAsMounted( 1425 const base::FilePath& drive_file_path, 1426 const OpenFileCallback& callback) { 1427 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1428 DCHECK(!callback.is_null()); 1429 1430 GetEntryInfoByPath( 1431 drive_file_path, 1432 base::Bind(&FileSystem::MarkCacheFileAsMountedAfterGetEntryInfo, 1433 weak_ptr_factory_.GetWeakPtr(), callback)); 1434} 1435 1436void FileSystem::MarkCacheFileAsMountedAfterGetEntryInfo( 1437 const OpenFileCallback& callback, 1438 FileError error, 1439 scoped_ptr<ResourceEntry> entry) { 1440 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1441 DCHECK(!callback.is_null()); 1442 1443 if (error != FILE_ERROR_OK) { 1444 callback.Run(error, base::FilePath()); 1445 return; 1446 } 1447 1448 DCHECK(entry); 1449 cache_->MarkAsMounted(entry->resource_id(), 1450 entry->file_specific_info().file_md5(), 1451 callback); 1452} 1453 1454void FileSystem::MarkCacheFileAsUnmounted( 1455 const base::FilePath& cache_file_path, 1456 const FileOperationCallback& callback) { 1457 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1458 DCHECK(!callback.is_null()); 1459 1460 if (!cache_->IsUnderFileCacheDirectory(cache_file_path)) { 1461 callback.Run(FILE_ERROR_FAILED); 1462 return; 1463 } 1464 cache_->MarkAsUnmounted(cache_file_path, callback); 1465} 1466 1467void FileSystem::GetCacheEntryByResourceId( 1468 const std::string& resource_id, 1469 const std::string& md5, 1470 const GetCacheEntryCallback& callback) { 1471 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1472 DCHECK(!resource_id.empty()); 1473 DCHECK(!callback.is_null()); 1474 1475 cache_->GetCacheEntry(resource_id, md5, callback); 1476} 1477 1478void FileSystem::IterateCache( 1479 const CacheIterateCallback& iteration_callback, 1480 const base::Closure& completion_callback) { 1481 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1482 DCHECK(!iteration_callback.is_null()); 1483 DCHECK(!completion_callback.is_null()); 1484 1485 cache_->Iterate(iteration_callback, completion_callback); 1486} 1487 1488void FileSystem::OnDisableDriveHostedFilesChanged() { 1489 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1490 PrefService* pref_service = profile_->GetPrefs(); 1491 SetHideHostedDocuments( 1492 pref_service->GetBoolean(prefs::kDisableDriveHostedFiles)); 1493} 1494 1495void FileSystem::SetHideHostedDocuments(bool hide) { 1496 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1497 1498 if (hide == hide_hosted_docs_) 1499 return; 1500 1501 hide_hosted_docs_ = hide; 1502 1503 // Kick off directory refresh when this setting changes. 1504 FOR_EACH_OBSERVER(FileSystemObserver, observers_, 1505 OnDirectoryChanged(util::GetDriveGrandRootPath())); 1506} 1507 1508//============= FileSystem: internal helper functions ===================== 1509 1510void FileSystem::InitializePreferenceObserver() { 1511 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1512 1513 pref_registrar_.reset(new PrefChangeRegistrar()); 1514 pref_registrar_->Init(profile_->GetPrefs()); 1515 pref_registrar_->Add( 1516 prefs::kDisableDriveHostedFiles, 1517 base::Bind(&FileSystem::OnDisableDriveHostedFilesChanged, 1518 base::Unretained(this))); 1519} 1520 1521void FileSystem::OpenFile(const base::FilePath& file_path, 1522 const OpenFileCallback& callback) { 1523 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1524 DCHECK(!callback.is_null()); 1525 1526 // If the file is already opened, it cannot be opened again before closed. 1527 // This is for avoiding simultaneous modification to the file, and moreover 1528 // to avoid an inconsistent cache state (suppose an operation sequence like 1529 // Open->Open->modify->Close->modify->Close; the second modify may not be 1530 // synchronized to the server since it is already Closed on the cache). 1531 if (open_files_.find(file_path) != open_files_.end()) { 1532 base::MessageLoopProxy::current()->PostTask( 1533 FROM_HERE, 1534 base::Bind(callback, FILE_ERROR_IN_USE, base::FilePath())); 1535 return; 1536 } 1537 open_files_.insert(file_path); 1538 1539 resource_metadata_->GetEntryInfoByPath( 1540 file_path, 1541 base::Bind(&FileSystem::OnGetEntryInfoCompleteForOpenFile, 1542 weak_ptr_factory_.GetWeakPtr(), 1543 file_path, 1544 base::Bind(&FileSystem::OnOpenFileFinished, 1545 weak_ptr_factory_.GetWeakPtr(), 1546 file_path, 1547 callback))); 1548} 1549 1550void FileSystem::OnGetEntryInfoCompleteForOpenFile( 1551 const base::FilePath& file_path, 1552 const OpenFileCallback& callback, 1553 FileError error, 1554 scoped_ptr<ResourceEntry> entry) { 1555 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1556 DCHECK(!callback.is_null()); 1557 DCHECK(entry.get() || error != FILE_ERROR_OK); 1558 1559 if (entry.get() && !entry->has_file_specific_info()) 1560 error = FILE_ERROR_NOT_FOUND; 1561 1562 if (error == FILE_ERROR_OK) { 1563 if (entry->file_specific_info().file_md5().empty() || 1564 entry->file_specific_info().is_hosted_document()) { 1565 // No support for opening a directory or hosted document. 1566 error = FILE_ERROR_INVALID_OPERATION; 1567 } 1568 } 1569 1570 if (error != FILE_ERROR_OK) { 1571 callback.Run(error, base::FilePath()); 1572 return; 1573 } 1574 1575 DCHECK(!entry->resource_id().empty()); 1576 // Extract a pointer before we call Pass() so we can use it below. 1577 ResourceEntry* entry_ptr = entry.get(); 1578 GetResolvedFileByPath( 1579 make_scoped_ptr(new GetResolvedFileParams( 1580 file_path, 1581 DriveClientContext(USER_INITIATED), 1582 entry.Pass(), 1583 GetFileContentInitializedCallback(), 1584 base::Bind(&FileSystem::OnGetFileCompleteForOpenFile, 1585 weak_ptr_factory_.GetWeakPtr(), 1586 GetFileCompleteForOpenParams( 1587 callback, 1588 entry_ptr->resource_id(), 1589 entry_ptr->file_specific_info().file_md5())), 1590 google_apis::GetContentCallback()))); 1591} 1592 1593void FileSystem::OnGetFileCompleteForOpenFile( 1594 const GetFileCompleteForOpenParams& params, 1595 FileError error, 1596 const base::FilePath& file_path, 1597 const std::string& mime_type, 1598 DriveFileType file_type) { 1599 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1600 DCHECK(!params.callback.is_null()); 1601 1602 if (error != FILE_ERROR_OK) { 1603 params.callback.Run(error, base::FilePath()); 1604 return; 1605 } 1606 1607 // OpenFile ensures that the file is a regular file. 1608 DCHECK_EQ(REGULAR_FILE, file_type); 1609 1610 cache_->MarkDirty( 1611 params.resource_id, 1612 params.md5, 1613 base::Bind(&FileSystem::OnMarkDirtyInCacheCompleteForOpenFile, 1614 weak_ptr_factory_.GetWeakPtr(), 1615 params)); 1616} 1617 1618void FileSystem::OnMarkDirtyInCacheCompleteForOpenFile( 1619 const GetFileCompleteForOpenParams& params, 1620 FileError error) { 1621 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1622 DCHECK(!params.callback.is_null()); 1623 1624 if (error != FILE_ERROR_OK) { 1625 params.callback.Run(error, base::FilePath()); 1626 return; 1627 } 1628 1629 cache_->GetFile(params.resource_id, params.md5, params.callback); 1630} 1631 1632void FileSystem::OnOpenFileFinished( 1633 const base::FilePath& file_path, 1634 const OpenFileCallback& callback, 1635 FileError result, 1636 const base::FilePath& cache_file_path) { 1637 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1638 DCHECK(!callback.is_null()); 1639 1640 // All the invocation of |callback| from operations initiated from OpenFile 1641 // must go through here. Removes the |file_path| from the remembered set when 1642 // the file was not successfully opened. 1643 if (result != FILE_ERROR_OK) 1644 open_files_.erase(file_path); 1645 1646 callback.Run(result, cache_file_path); 1647} 1648 1649void FileSystem::CloseFile(const base::FilePath& file_path, 1650 const FileOperationCallback& callback) { 1651 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1652 DCHECK(!callback.is_null()); 1653 1654 if (open_files_.find(file_path) == open_files_.end()) { 1655 // The file is not being opened. 1656 base::MessageLoopProxy::current()->PostTask( 1657 FROM_HERE, 1658 base::Bind(callback, FILE_ERROR_NOT_FOUND)); 1659 return; 1660 } 1661 1662 // Step 1 of CloseFile: Get resource_id and md5 for |file_path|. 1663 resource_metadata_->GetEntryInfoByPath( 1664 file_path, 1665 base::Bind(&FileSystem::CloseFileAfterGetEntryInfo, 1666 weak_ptr_factory_.GetWeakPtr(), 1667 file_path, 1668 base::Bind(&FileSystem::CloseFileFinalize, 1669 weak_ptr_factory_.GetWeakPtr(), 1670 file_path, 1671 callback))); 1672} 1673 1674void FileSystem::CloseFileAfterGetEntryInfo( 1675 const base::FilePath& file_path, 1676 const FileOperationCallback& callback, 1677 FileError error, 1678 scoped_ptr<ResourceEntry> entry) { 1679 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1680 DCHECK(!callback.is_null()); 1681 1682 if (entry.get() && !entry->has_file_specific_info()) 1683 error = FILE_ERROR_NOT_FOUND; 1684 1685 if (error != FILE_ERROR_OK) { 1686 callback.Run(error); 1687 return; 1688 } 1689 1690 // Step 2 of CloseFile: Commit the modification in cache. This will trigger 1691 // background upload. 1692 // TODO(benchan,kinaba): Call ClearDirtyInCache instead of CommitDirtyInCache 1693 // if the file has not been modified. Come up with a way to detect the 1694 // intactness effectively, or provide a method for user to declare it when 1695 // calling CloseFile(). 1696 cache_->CommitDirty(entry->resource_id(), 1697 entry->file_specific_info().file_md5(), 1698 callback); 1699} 1700 1701void FileSystem::CloseFileFinalize(const base::FilePath& file_path, 1702 const FileOperationCallback& callback, 1703 FileError result) { 1704 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1705 DCHECK(!callback.is_null()); 1706 1707 // Step 3 of CloseFile. 1708 // All the invocation of |callback| from operations initiated from CloseFile 1709 // must go through here. Removes the |file_path| from the remembered set so 1710 // that subsequent operations can open the file again. 1711 open_files_.erase(file_path); 1712 1713 // Then invokes the user-supplied callback function. 1714 callback.Run(result); 1715} 1716 1717void FileSystem::CheckLocalModificationAndRun( 1718 scoped_ptr<ResourceEntry> entry, 1719 const GetEntryInfoCallback& callback) { 1720 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1721 DCHECK(entry.get()); 1722 DCHECK(!callback.is_null()); 1723 1724 // For entries that will never be cached, use the original entry info as is. 1725 if (!entry->has_file_specific_info() || 1726 entry->file_specific_info().is_hosted_document()) { 1727 callback.Run(FILE_ERROR_OK, entry.Pass()); 1728 return; 1729 } 1730 1731 // Checks if the file is cached and modified locally. 1732 const std::string resource_id = entry->resource_id(); 1733 const std::string md5 = entry->file_specific_info().file_md5(); 1734 cache_->GetCacheEntry( 1735 resource_id, 1736 md5, 1737 base::Bind( 1738 &FileSystem::CheckLocalModificationAndRunAfterGetCacheEntry, 1739 weak_ptr_factory_.GetWeakPtr(), 1740 base::Passed(&entry), 1741 callback)); 1742} 1743 1744void FileSystem::CheckLocalModificationAndRunAfterGetCacheEntry( 1745 scoped_ptr<ResourceEntry> entry, 1746 const GetEntryInfoCallback& callback, 1747 bool success, 1748 const FileCacheEntry& cache_entry) { 1749 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1750 DCHECK(!callback.is_null()); 1751 1752 // When no dirty cache is found, use the original entry info as is. 1753 if (!success || !cache_entry.is_dirty()) { 1754 callback.Run(FILE_ERROR_OK, entry.Pass()); 1755 return; 1756 } 1757 1758 // Gets the cache file path. 1759 const std::string& resource_id = entry->resource_id(); 1760 const std::string& md5 = entry->file_specific_info().file_md5(); 1761 cache_->GetFile( 1762 resource_id, 1763 md5, 1764 base::Bind( 1765 &FileSystem::CheckLocalModificationAndRunAfterGetCacheFile, 1766 weak_ptr_factory_.GetWeakPtr(), 1767 base::Passed(&entry), 1768 callback)); 1769} 1770 1771void FileSystem::CheckLocalModificationAndRunAfterGetCacheFile( 1772 scoped_ptr<ResourceEntry> entry, 1773 const GetEntryInfoCallback& callback, 1774 FileError error, 1775 const base::FilePath& local_cache_path) { 1776 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1777 DCHECK(!callback.is_null()); 1778 1779 // When no dirty cache is found, use the original entry info as is. 1780 if (error != FILE_ERROR_OK) { 1781 callback.Run(FILE_ERROR_OK, entry.Pass()); 1782 return; 1783 } 1784 1785 // If the cache is dirty, obtain the file info from the cache file itself. 1786 base::PlatformFileInfo* file_info = new base::PlatformFileInfo; 1787 base::PostTaskAndReplyWithResult( 1788 blocking_task_runner_, 1789 FROM_HERE, 1790 base::Bind(&file_util::GetFileInfo, 1791 local_cache_path, 1792 base::Unretained(file_info)), 1793 base::Bind(&FileSystem::CheckLocalModificationAndRunAfterGetFileInfo, 1794 weak_ptr_factory_.GetWeakPtr(), 1795 base::Passed(&entry), 1796 callback, 1797 base::Owned(file_info))); 1798} 1799 1800void FileSystem::CheckLocalModificationAndRunAfterGetFileInfo( 1801 scoped_ptr<ResourceEntry> entry, 1802 const GetEntryInfoCallback& callback, 1803 base::PlatformFileInfo* file_info, 1804 bool get_file_info_result) { 1805 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 1806 DCHECK(!callback.is_null()); 1807 1808 if (!get_file_info_result) { 1809 callback.Run(FILE_ERROR_NOT_FOUND, scoped_ptr<ResourceEntry>()); 1810 return; 1811 } 1812 1813 PlatformFileInfoProto entry_file_info; 1814 util::ConvertPlatformFileInfoToResourceEntry(*file_info, &entry_file_info); 1815 *entry->mutable_file_info() = entry_file_info; 1816 callback.Run(FILE_ERROR_OK, entry.Pass()); 1817} 1818 1819void FileSystem::CancelJobInScheduler(JobID id) { 1820 scheduler_->CancelJob(id); 1821} 1822 1823} // namespace drive 1824