Home | History | Annotate | Download | only in drive
      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/message_loop_proxy.h"
     10 #include "base/platform_file.h"
     11 #include "base/prefs/pref_change_registrar.h"
     12 #include "base/prefs/pref_service.h"
     13 #include "base/strings/stringprintf.h"
     14 #include "base/threading/sequenced_worker_pool.h"
     15 #include "chrome/browser/browser_process.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/copy_operation.h"
     21 #include "chrome/browser/chromeos/drive/file_system/create_directory_operation.h"
     22 #include "chrome/browser/chromeos/drive/file_system/create_file_operation.h"
     23 #include "chrome/browser/chromeos/drive/file_system/download_operation.h"
     24 #include "chrome/browser/chromeos/drive/file_system/get_file_for_saving_operation.h"
     25 #include "chrome/browser/chromeos/drive/file_system/move_operation.h"
     26 #include "chrome/browser/chromeos/drive/file_system/open_file_operation.h"
     27 #include "chrome/browser/chromeos/drive/file_system/remove_operation.h"
     28 #include "chrome/browser/chromeos/drive/file_system/search_operation.h"
     29 #include "chrome/browser/chromeos/drive/file_system/touch_operation.h"
     30 #include "chrome/browser/chromeos/drive/file_system/truncate_operation.h"
     31 #include "chrome/browser/chromeos/drive/file_system/update_operation.h"
     32 #include "chrome/browser/chromeos/drive/file_system_observer.h"
     33 #include "chrome/browser/chromeos/drive/file_system_util.h"
     34 #include "chrome/browser/chromeos/drive/job_scheduler.h"
     35 #include "chrome/browser/chromeos/drive/logging.h"
     36 #include "chrome/browser/chromeos/drive/remove_stale_cache_files.h"
     37 #include "chrome/browser/chromeos/drive/resource_entry_conversion.h"
     38 #include "chrome/browser/chromeos/drive/search_metadata.h"
     39 #include "chrome/browser/chromeos/drive/sync_client.h"
     40 #include "chrome/browser/drive/drive_api_util.h"
     41 #include "chrome/browser/drive/drive_service_interface.h"
     42 #include "chrome/browser/google_apis/drive_api_parser.h"
     43 #include "chrome/common/pref_names.h"
     44 #include "content/public/browser/browser_thread.h"
     45 #include "net/http/http_status_code.h"
     46 
     47 using content::BrowserThread;
     48 
     49 namespace drive {
     50 namespace {
     51 
     52 // Gets a ResourceEntry from the metadata, and overwrites its file info when the
     53 // cached file is dirty.
     54 FileError GetLocallyStoredResourceEntry(
     55     internal::ResourceMetadata* resource_metadata,
     56     internal::FileCache* cache,
     57     const base::FilePath& file_path,
     58     ResourceEntry* entry) {
     59   FileError error =
     60       resource_metadata->GetResourceEntryByPath(file_path, entry);
     61   if (error != FILE_ERROR_OK)
     62     return error;
     63 
     64   // For entries that will never be cached, use the original resource entry
     65   // as is.
     66   if (!entry->has_file_specific_info() ||
     67       entry->file_specific_info().is_hosted_document())
     68     return FILE_ERROR_OK;
     69 
     70   // When no dirty cache is found, use the original resource entry as is.
     71   FileCacheEntry cache_entry;
     72   if (!cache->GetCacheEntry(entry->resource_id(), &cache_entry) ||
     73       !cache_entry.is_dirty())
     74     return FILE_ERROR_OK;
     75 
     76   // If the cache is dirty, obtain the file info from the cache file itself.
     77   base::FilePath local_cache_path;
     78   error = cache->GetFile(entry->resource_id(), &local_cache_path);
     79   if (error != FILE_ERROR_OK)
     80     return error;
     81 
     82   base::PlatformFileInfo file_info;
     83   if (!file_util::GetFileInfo(local_cache_path, &file_info))
     84     return FILE_ERROR_NOT_FOUND;
     85 
     86   SetPlatformFileInfoToResourceEntry(file_info, entry);
     87   return FILE_ERROR_OK;
     88 }
     89 
     90 // Runs the callback with parameters.
     91 void RunGetResourceEntryCallback(const GetResourceEntryCallback& callback,
     92                                  scoped_ptr<ResourceEntry> entry,
     93                                  FileError error) {
     94   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     95   DCHECK(!callback.is_null());
     96 
     97   if (error != FILE_ERROR_OK)
     98     entry.reset();
     99   callback.Run(error, entry.Pass());
    100 }
    101 
    102 // Callback for ResourceMetadata::GetLargestChangestamp.
    103 // |callback| must not be null.
    104 void OnGetLargestChangestamp(
    105     FileSystemMetadata metadata,  // Will be modified.
    106     const GetFilesystemMetadataCallback& callback,
    107     int64 largest_changestamp) {
    108   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    109   DCHECK(!callback.is_null());
    110 
    111   metadata.largest_changestamp = largest_changestamp;
    112   callback.Run(metadata);
    113 }
    114 
    115 // Thin adapter to map GetFileCallback to FileOperationCallback.
    116 void GetFileCallbackToFileOperationCallbackAdapter(
    117     const FileOperationCallback& callback,
    118     FileError error,
    119     const base::FilePath& unused_file_path,
    120     scoped_ptr<ResourceEntry> unused_entry) {
    121   callback.Run(error);
    122 }
    123 
    124 // Checks whether the |url| passed to the constructor is accessible. If it is
    125 // not, invokes |on_stale_closure|.
    126 class StaleURLChecker : public net::URLFetcherDelegate {
    127  public:
    128   StaleURLChecker(const GURL& url, const base::Closure& on_stale_closure)
    129       : on_stale_closure_(on_stale_closure) {
    130     fetcher_.reset(net::URLFetcher::Create(url, net::URLFetcher::HEAD, this));
    131     fetcher_->SetRequestContext(g_browser_process->system_request_context());
    132     fetcher_->Start();
    133   }
    134 
    135   virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE {
    136     int code = source->GetResponseCode();
    137     if (code == net::HTTP_FORBIDDEN)
    138       on_stale_closure_.Run();
    139     delete this;
    140   }
    141 
    142  private:
    143   scoped_ptr<net::URLFetcher> fetcher_;
    144   const base::Closure on_stale_closure_;
    145 };
    146 
    147 // Checks the first thumbnail URL in |entries| whether it is still available
    148 // by sending a HEAD request. If it's stale, invokes |on_stale_closure|.
    149 void CheckStaleThumbnailURL(ResourceEntryVector* entries,
    150                             const base::Closure& on_stale_closure) {
    151   const char kImageThumbnailDomain[] = "googleusercontent.com";
    152   for (size_t i = 0; i < entries->size(); ++i) {
    153     const std::string& url =
    154         entries->at(i).file_specific_info().thumbnail_url();
    155     if (url.find(kImageThumbnailDomain) != std::string::npos) {
    156       // The stale URL checker deletes itself.
    157       new StaleURLChecker(GURL(url), on_stale_closure);
    158       break;
    159     }
    160   }
    161 }
    162 
    163 }  // namespace
    164 
    165 FileSystem::FileSystem(
    166     PrefService* pref_service,
    167     internal::FileCache* cache,
    168     DriveServiceInterface* drive_service,
    169     JobScheduler* scheduler,
    170     internal::ResourceMetadata* resource_metadata,
    171     base::SequencedTaskRunner* blocking_task_runner,
    172     const base::FilePath& temporary_file_directory)
    173     : pref_service_(pref_service),
    174       cache_(cache),
    175       drive_service_(drive_service),
    176       scheduler_(scheduler),
    177       resource_metadata_(resource_metadata),
    178       last_update_check_error_(FILE_ERROR_OK),
    179       hide_hosted_docs_(false),
    180       blocking_task_runner_(blocking_task_runner),
    181       temporary_file_directory_(temporary_file_directory),
    182       weak_ptr_factory_(this) {
    183   // Should be created from the file browser extension API on UI thread.
    184   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    185 }
    186 
    187 void FileSystem::Reload() {
    188   resource_metadata_->ResetOnUIThread(base::Bind(
    189       &FileSystem::ReloadAfterReset,
    190       weak_ptr_factory_.GetWeakPtr()));
    191 }
    192 
    193 void FileSystem::Initialize() {
    194   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    195 
    196   SetupChangeListLoader();
    197 
    198   file_system::OperationObserver* observer = this;
    199   copy_operation_.reset(
    200       new file_system::CopyOperation(blocking_task_runner_.get(),
    201                                      observer,
    202                                      scheduler_,
    203                                      resource_metadata_,
    204                                      cache_,
    205                                      drive_service_,
    206                                      temporary_file_directory_));
    207   create_directory_operation_.reset(new file_system::CreateDirectoryOperation(
    208       blocking_task_runner_.get(), observer, scheduler_, resource_metadata_));
    209   create_file_operation_.reset(
    210       new file_system::CreateFileOperation(blocking_task_runner_.get(),
    211                                            observer,
    212                                            scheduler_,
    213                                            resource_metadata_,
    214                                            cache_));
    215   move_operation_.reset(
    216       new file_system::MoveOperation(observer, scheduler_, resource_metadata_));
    217   open_file_operation_.reset(
    218       new file_system::OpenFileOperation(blocking_task_runner_.get(),
    219                                          observer,
    220                                          scheduler_,
    221                                          resource_metadata_,
    222                                          cache_,
    223                                          temporary_file_directory_));
    224   remove_operation_.reset(
    225       new file_system::RemoveOperation(blocking_task_runner_.get(),
    226                                        observer,
    227                                        scheduler_,
    228                                        resource_metadata_,
    229                                        cache_));
    230   touch_operation_.reset(new file_system::TouchOperation(
    231       blocking_task_runner_.get(), observer, scheduler_, resource_metadata_));
    232   truncate_operation_.reset(
    233       new file_system::TruncateOperation(blocking_task_runner_.get(),
    234                                          observer,
    235                                          scheduler_,
    236                                          resource_metadata_,
    237                                          cache_,
    238                                          temporary_file_directory_));
    239   download_operation_.reset(
    240       new file_system::DownloadOperation(blocking_task_runner_.get(),
    241                                          observer,
    242                                          scheduler_,
    243                                          resource_metadata_,
    244                                          cache_,
    245                                          temporary_file_directory_));
    246   update_operation_.reset(
    247       new file_system::UpdateOperation(blocking_task_runner_.get(),
    248                                        observer,
    249                                        scheduler_,
    250                                        resource_metadata_,
    251                                        cache_));
    252   search_operation_.reset(new file_system::SearchOperation(
    253       blocking_task_runner_.get(), scheduler_, resource_metadata_));
    254   get_file_for_saving_operation_.reset(
    255       new file_system::GetFileForSavingOperation(blocking_task_runner_.get(),
    256                                                  observer,
    257                                                  scheduler_,
    258                                                  resource_metadata_,
    259                                                  cache_,
    260                                                  temporary_file_directory_));
    261 
    262   sync_client_.reset(new internal::SyncClient(blocking_task_runner_.get(),
    263                                               observer,
    264                                               scheduler_,
    265                                               resource_metadata_,
    266                                               cache_,
    267                                               temporary_file_directory_));
    268   hide_hosted_docs_ =
    269       pref_service_->GetBoolean(prefs::kDisableDriveHostedFiles);
    270 
    271   InitializePreferenceObserver();
    272 }
    273 
    274 void FileSystem::ReloadAfterReset(FileError error) {
    275   if (error != FILE_ERROR_OK) {
    276     LOG(ERROR) << "Failed to reset the resource metadata: "
    277                << FileErrorToString(error);
    278     return;
    279   }
    280 
    281   SetupChangeListLoader();
    282 
    283   change_list_loader_->LoadIfNeeded(
    284       DirectoryFetchInfo(),
    285       base::Bind(&FileSystem::OnUpdateChecked,
    286                  weak_ptr_factory_.GetWeakPtr()));
    287 }
    288 
    289 void FileSystem::SetupChangeListLoader() {
    290   change_list_loader_.reset(new internal::ChangeListLoader(
    291       blocking_task_runner_.get(), resource_metadata_, scheduler_));
    292   change_list_loader_->AddObserver(this);
    293 }
    294 
    295 void FileSystem::CheckForUpdates() {
    296   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    297   DVLOG(1) << "CheckForUpdates";
    298 
    299   if (change_list_loader_) {
    300     change_list_loader_->CheckForUpdates(
    301         base::Bind(&FileSystem::OnUpdateChecked,
    302                    weak_ptr_factory_.GetWeakPtr()));
    303   }
    304 }
    305 
    306 void FileSystem::OnUpdateChecked(FileError error) {
    307   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    308   DVLOG(1) << "CheckForUpdates finished: " << FileErrorToString(error);
    309   last_update_check_time_ = base::Time::Now();
    310   last_update_check_error_ = error;
    311 }
    312 
    313 FileSystem::~FileSystem() {
    314   // This should be called from UI thread, from DriveIntegrationService
    315   // shutdown.
    316   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    317 
    318   change_list_loader_->RemoveObserver(this);
    319 }
    320 
    321 void FileSystem::AddObserver(FileSystemObserver* observer) {
    322   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    323   observers_.AddObserver(observer);
    324 }
    325 
    326 void FileSystem::RemoveObserver(FileSystemObserver* observer) {
    327   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    328   observers_.RemoveObserver(observer);
    329 }
    330 
    331 void FileSystem::TransferFileFromRemoteToLocal(
    332     const base::FilePath& remote_src_file_path,
    333     const base::FilePath& local_dest_file_path,
    334     const FileOperationCallback& callback) {
    335   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    336   DCHECK(!callback.is_null());
    337   copy_operation_->TransferFileFromRemoteToLocal(remote_src_file_path,
    338                                                  local_dest_file_path,
    339                                                  callback);
    340 }
    341 
    342 void FileSystem::TransferFileFromLocalToRemote(
    343     const base::FilePath& local_src_file_path,
    344     const base::FilePath& remote_dest_file_path,
    345     const FileOperationCallback& callback) {
    346   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    347   DCHECK(!callback.is_null());
    348   copy_operation_->TransferFileFromLocalToRemote(local_src_file_path,
    349                                                  remote_dest_file_path,
    350                                                  callback);
    351 }
    352 
    353 void FileSystem::Copy(const base::FilePath& src_file_path,
    354                       const base::FilePath& dest_file_path,
    355                       const FileOperationCallback& callback) {
    356   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    357   DCHECK(!callback.is_null());
    358   copy_operation_->Copy(src_file_path, dest_file_path, callback);
    359 }
    360 
    361 void FileSystem::Move(const base::FilePath& src_file_path,
    362                       const base::FilePath& dest_file_path,
    363                       const FileOperationCallback& callback) {
    364   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    365   DCHECK(!callback.is_null());
    366   move_operation_->Move(src_file_path, dest_file_path, callback);
    367 }
    368 
    369 void FileSystem::Remove(const base::FilePath& file_path,
    370                         bool is_recursive,
    371                         const FileOperationCallback& callback) {
    372   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    373   DCHECK(!callback.is_null());
    374   remove_operation_->Remove(file_path, is_recursive, callback);
    375 }
    376 
    377 void FileSystem::CreateDirectory(
    378     const base::FilePath& directory_path,
    379     bool is_exclusive,
    380     bool is_recursive,
    381     const FileOperationCallback& callback) {
    382   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    383   DCHECK(!callback.is_null());
    384 
    385   change_list_loader_->LoadIfNeeded(
    386       DirectoryFetchInfo(),
    387       base::Bind(&FileSystem::CreateDirectoryAfterLoad,
    388                  weak_ptr_factory_.GetWeakPtr(),
    389                  directory_path, is_exclusive, is_recursive, callback));
    390 }
    391 
    392 void FileSystem::CreateDirectoryAfterLoad(
    393     const base::FilePath& directory_path,
    394     bool is_exclusive,
    395     bool is_recursive,
    396     const FileOperationCallback& callback,
    397     FileError load_error) {
    398   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    399   DCHECK(!callback.is_null());
    400 
    401   if (load_error != FILE_ERROR_OK) {
    402     callback.Run(load_error);
    403     return;
    404   }
    405 
    406   create_directory_operation_->CreateDirectory(
    407       directory_path, is_exclusive, is_recursive, callback);
    408 }
    409 
    410 void FileSystem::CreateFile(const base::FilePath& file_path,
    411                             bool is_exclusive,
    412                             const FileOperationCallback& callback) {
    413   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    414   DCHECK(!callback.is_null());
    415   create_file_operation_->CreateFile(file_path, is_exclusive, callback);
    416 }
    417 
    418 void FileSystem::TouchFile(const base::FilePath& file_path,
    419                            const base::Time& last_access_time,
    420                            const base::Time& last_modified_time,
    421                            const FileOperationCallback& callback) {
    422   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    423   DCHECK(!last_access_time.is_null());
    424   DCHECK(!last_modified_time.is_null());
    425   DCHECK(!callback.is_null());
    426   touch_operation_->TouchFile(
    427       file_path, last_access_time, last_modified_time, callback);
    428 }
    429 
    430 void FileSystem::TruncateFile(const base::FilePath& file_path,
    431                               int64 length,
    432                               const FileOperationCallback& callback) {
    433   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    434   DCHECK(!callback.is_null());
    435   truncate_operation_->Truncate(file_path, length, callback);
    436 }
    437 
    438 void FileSystem::Pin(const base::FilePath& file_path,
    439                      const FileOperationCallback& callback) {
    440   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    441   DCHECK(!callback.is_null());
    442 
    443   GetResourceEntryByPath(file_path,
    444                          base::Bind(&FileSystem::PinAfterGetResourceEntryByPath,
    445                                     weak_ptr_factory_.GetWeakPtr(),
    446                                     callback));
    447 }
    448 
    449 void FileSystem::PinAfterGetResourceEntryByPath(
    450     const FileOperationCallback& callback,
    451     FileError error,
    452     scoped_ptr<ResourceEntry> entry) {
    453   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    454   DCHECK(!callback.is_null());
    455 
    456   // TODO(hashimoto): Support pinning directories. crbug.com/127831
    457   if (entry && entry->file_info().is_directory())
    458     error = FILE_ERROR_NOT_A_FILE;
    459 
    460   if (error != FILE_ERROR_OK) {
    461     callback.Run(error);
    462     return;
    463   }
    464   DCHECK(entry);
    465 
    466   cache_->PinOnUIThread(entry->resource_id(),
    467                         base::Bind(&FileSystem::FinishPin,
    468                                    weak_ptr_factory_.GetWeakPtr(),
    469                                    callback,
    470                                    entry->resource_id()));
    471 }
    472 
    473 void FileSystem::FinishPin(const FileOperationCallback& callback,
    474                            const std::string& resource_id,
    475                            FileError error) {
    476   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    477   DCHECK(!callback.is_null());
    478 
    479   if (error == FILE_ERROR_OK)
    480     sync_client_->AddFetchTask(resource_id);
    481   callback.Run(error);
    482 }
    483 
    484 void FileSystem::Unpin(const base::FilePath& file_path,
    485                        const FileOperationCallback& callback) {
    486   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    487   DCHECK(!callback.is_null());
    488 
    489   GetResourceEntryByPath(
    490       file_path,
    491       base::Bind(&FileSystem::UnpinAfterGetResourceEntryByPath,
    492                  weak_ptr_factory_.GetWeakPtr(),
    493                  callback));
    494 }
    495 
    496 void FileSystem::UnpinAfterGetResourceEntryByPath(
    497     const FileOperationCallback& callback,
    498     FileError error,
    499     scoped_ptr<ResourceEntry> entry) {
    500   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    501   DCHECK(!callback.is_null());
    502 
    503   // TODO(hashimoto): Support pinning directories. crbug.com/127831
    504   if (entry && entry->file_info().is_directory())
    505     error = FILE_ERROR_NOT_A_FILE;
    506 
    507   if (error != FILE_ERROR_OK) {
    508     callback.Run(error);
    509     return;
    510   }
    511   DCHECK(entry);
    512 
    513   cache_->UnpinOnUIThread(entry->resource_id(),
    514                           base::Bind(&FileSystem::FinishUnpin,
    515                                      weak_ptr_factory_.GetWeakPtr(),
    516                                      callback,
    517                                      entry->resource_id()));
    518 }
    519 
    520 void FileSystem::FinishUnpin(const FileOperationCallback& callback,
    521                              const std::string& resource_id,
    522                              FileError error) {
    523   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    524   DCHECK(!callback.is_null());
    525 
    526   if (error == FILE_ERROR_OK)
    527     sync_client_->RemoveFetchTask(resource_id);
    528   callback.Run(error);
    529 }
    530 
    531 void FileSystem::GetFileByPath(const base::FilePath& file_path,
    532                                const GetFileCallback& callback) {
    533   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    534   DCHECK(!callback.is_null());
    535 
    536   download_operation_->EnsureFileDownloadedByPath(
    537       file_path,
    538       ClientContext(USER_INITIATED),
    539       GetFileContentInitializedCallback(),
    540       google_apis::GetContentCallback(),
    541       callback);
    542 }
    543 
    544 void FileSystem::GetFileByPathForSaving(const base::FilePath& file_path,
    545                                         const GetFileCallback& callback) {
    546   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    547   DCHECK(!callback.is_null());
    548 
    549   get_file_for_saving_operation_->GetFileForSaving(file_path, callback);
    550 }
    551 
    552 void FileSystem::GetFileContentByPath(
    553     const base::FilePath& file_path,
    554     const GetFileContentInitializedCallback& initialized_callback,
    555     const google_apis::GetContentCallback& get_content_callback,
    556     const FileOperationCallback& completion_callback) {
    557   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    558   DCHECK(!initialized_callback.is_null());
    559   DCHECK(!get_content_callback.is_null());
    560   DCHECK(!completion_callback.is_null());
    561 
    562   download_operation_->EnsureFileDownloadedByPath(
    563       file_path,
    564       ClientContext(USER_INITIATED),
    565       initialized_callback,
    566       get_content_callback,
    567       base::Bind(&GetFileCallbackToFileOperationCallbackAdapter,
    568                  completion_callback));
    569 }
    570 
    571 void FileSystem::GetResourceEntryByPath(
    572     const base::FilePath& file_path,
    573     const GetResourceEntryCallback& callback) {
    574   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    575   DCHECK(!callback.is_null());
    576 
    577   scoped_ptr<ResourceEntry> entry(new ResourceEntry);
    578   ResourceEntry* entry_ptr = entry.get();
    579   base::PostTaskAndReplyWithResult(
    580       blocking_task_runner_,
    581       FROM_HERE,
    582       base::Bind(&GetLocallyStoredResourceEntry,
    583                  resource_metadata_,
    584                  cache_,
    585                  file_path,
    586                  entry_ptr),
    587       base::Bind(&FileSystem::GetResourceEntryByPathAfterGetEntry,
    588                  weak_ptr_factory_.GetWeakPtr(),
    589                  file_path,
    590                  callback,
    591                  base::Passed(&entry)));
    592 }
    593 
    594 void FileSystem::GetResourceEntryByPathAfterGetEntry(
    595     const base::FilePath& file_path,
    596     const GetResourceEntryCallback& callback,
    597     scoped_ptr<ResourceEntry> entry,
    598     FileError error) {
    599   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    600   DCHECK(!callback.is_null());
    601 
    602   if (error == FILE_ERROR_OK) {
    603     callback.Run(error, entry.Pass());
    604     return;
    605   }
    606 
    607   // If the information about the path is not in the local ResourceMetadata,
    608   // try fetching information of the directory and retry.
    609   LoadDirectoryIfNeeded(
    610       file_path.DirName(),
    611       base::Bind(&FileSystem::GetResourceEntryByPathAfterLoad,
    612                  weak_ptr_factory_.GetWeakPtr(),
    613                  file_path,
    614                  callback));
    615 }
    616 
    617 void FileSystem::GetResourceEntryByPathAfterLoad(
    618     const base::FilePath& file_path,
    619     const GetResourceEntryCallback& callback,
    620     FileError error) {
    621   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    622   DCHECK(!callback.is_null());
    623 
    624   if (error != FILE_ERROR_OK) {
    625     callback.Run(error, scoped_ptr<ResourceEntry>());
    626     return;
    627   }
    628 
    629   scoped_ptr<ResourceEntry> entry(new ResourceEntry);
    630   ResourceEntry* entry_ptr = entry.get();
    631   base::PostTaskAndReplyWithResult(
    632       blocking_task_runner_,
    633       FROM_HERE,
    634       base::Bind(&GetLocallyStoredResourceEntry,
    635                  resource_metadata_,
    636                  cache_,
    637                  file_path,
    638                  entry_ptr),
    639       base::Bind(&RunGetResourceEntryCallback,
    640                  callback,
    641                  base::Passed(&entry)));
    642 }
    643 
    644 void FileSystem::ReadDirectoryByPath(
    645     const base::FilePath& directory_path,
    646     const ReadDirectoryCallback& callback) {
    647   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    648   DCHECK(!callback.is_null());
    649 
    650   LoadDirectoryIfNeeded(
    651       directory_path,
    652       base::Bind(&FileSystem::ReadDirectoryByPathAfterLoad,
    653                  weak_ptr_factory_.GetWeakPtr(),
    654                  directory_path,
    655                  callback));
    656 }
    657 
    658 void FileSystem::LoadDirectoryIfNeeded(const base::FilePath& directory_path,
    659                                        const FileOperationCallback& 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.
    665   resource_metadata_->GetResourceEntryByPathOnUIThread(
    666       directory_path,
    667       base::Bind(&FileSystem::LoadDirectoryIfNeededAfterGetEntry,
    668                  weak_ptr_factory_.GetWeakPtr(),
    669                  directory_path,
    670                  callback));
    671 }
    672 
    673 void FileSystem::LoadDirectoryIfNeededAfterGetEntry(
    674     const base::FilePath& directory_path,
    675     const FileOperationCallback& callback,
    676     FileError error,
    677     scoped_ptr<ResourceEntry> entry) {
    678   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    679   DCHECK(!callback.is_null());
    680 
    681   if (error != FILE_ERROR_OK ||
    682       entry->resource_id() == util::kDriveOtherDirSpecialResourceId) {
    683     // If we don't know about the directory, or it is the "drive/other"
    684     // directory that has to gather all orphan entries, start loading full
    685     // resource list.
    686     change_list_loader_->LoadIfNeeded(DirectoryFetchInfo(), callback);
    687     return;
    688   }
    689 
    690   if (!entry->file_info().is_directory()) {
    691     callback.Run(FILE_ERROR_NOT_A_DIRECTORY);
    692     return;
    693   }
    694 
    695   // Pass the directory fetch info so we can fetch the contents of the
    696   // directory before loading change lists.
    697   DirectoryFetchInfo directory_fetch_info(
    698       entry->resource_id(),
    699       entry->directory_specific_info().changestamp());
    700   change_list_loader_->LoadIfNeeded(directory_fetch_info, callback);
    701 }
    702 
    703 void FileSystem::ReadDirectoryByPathAfterLoad(
    704     const base::FilePath& directory_path,
    705     const ReadDirectoryCallback& callback,
    706     FileError error) {
    707   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    708   DCHECK(!callback.is_null());
    709 
    710   DLOG_IF(INFO, error != FILE_ERROR_OK) << "LoadIfNeeded failed. "
    711                                         << FileErrorToString(error);
    712 
    713   resource_metadata_->ReadDirectoryByPathOnUIThread(
    714       directory_path,
    715       base::Bind(&FileSystem::ReadDirectoryByPathAfterRead,
    716                  weak_ptr_factory_.GetWeakPtr(),
    717                  directory_path,
    718                  callback));
    719 }
    720 
    721 void FileSystem::ReadDirectoryByPathAfterRead(
    722     const base::FilePath& directory_path,
    723     const ReadDirectoryCallback& callback,
    724     FileError error,
    725     scoped_ptr<ResourceEntryVector> entries) {
    726   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    727   DCHECK(!callback.is_null());
    728 
    729   if (error != FILE_ERROR_OK) {
    730     callback.Run(error,
    731                  scoped_ptr<ResourceEntryVector>());
    732     return;
    733   }
    734   DCHECK(entries.get());  // This is valid for empty directories too.
    735 
    736   // TODO(satorux): Stop handling hide_hosted_docs_ here. crbug.com/256520.
    737   scoped_ptr<ResourceEntryVector> filtered(new ResourceEntryVector);
    738   for (size_t i = 0; i < entries->size(); ++i) {
    739     if (hide_hosted_docs_ &&
    740         entries->at(i).file_specific_info().is_hosted_document()) {
    741       continue;
    742     }
    743     filtered->push_back(entries->at(i));
    744   }
    745 
    746   // Thumbnail URLs are short-lived. We check the validness of the URL in
    747   // background, and refresh the metadata for the directory if necessary.
    748   // TODO(kinaba): Remove this hack by using persistent URLs crbug.com/254025.
    749   CheckStaleThumbnailURL(filtered.get(),
    750                          base::Bind(&FileSystem::RefreshDirectory,
    751                                     weak_ptr_factory_.GetWeakPtr(),
    752                                     directory_path));
    753 
    754   callback.Run(FILE_ERROR_OK, filtered.Pass());
    755 }
    756 
    757 void FileSystem::GetAvailableSpace(
    758     const GetAvailableSpaceCallback& callback) {
    759   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    760   DCHECK(!callback.is_null());
    761 
    762   scheduler_->GetAboutResource(
    763       base::Bind(&FileSystem::OnGetAboutResource,
    764                  weak_ptr_factory_.GetWeakPtr(),
    765                  callback));
    766 }
    767 
    768 void FileSystem::OnGetAboutResource(
    769     const GetAvailableSpaceCallback& callback,
    770     google_apis::GDataErrorCode status,
    771     scoped_ptr<google_apis::AboutResource> about_resource) {
    772   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    773   DCHECK(!callback.is_null());
    774 
    775   FileError error = GDataToFileError(status);
    776   if (error != FILE_ERROR_OK) {
    777     callback.Run(error, -1, -1);
    778     return;
    779   }
    780   DCHECK(about_resource);
    781 
    782   callback.Run(FILE_ERROR_OK,
    783                about_resource->quota_bytes_total(),
    784                about_resource->quota_bytes_used());
    785 }
    786 
    787 void FileSystem::GetShareUrl(
    788     const base::FilePath& file_path,
    789     const GURL& embed_origin,
    790     const GetShareUrlCallback& callback) {
    791   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    792   DCHECK(!callback.is_null());
    793 
    794   // Resolve the resource id.
    795   resource_metadata_->GetResourceEntryByPathOnUIThread(
    796       file_path,
    797       base::Bind(&FileSystem::GetShareUrlAfterGetResourceEntry,
    798                  weak_ptr_factory_.GetWeakPtr(),
    799                  file_path,
    800                  embed_origin,
    801                  callback));
    802 }
    803 
    804 void FileSystem::GetShareUrlAfterGetResourceEntry(
    805     const base::FilePath& file_path,
    806     const GURL& embed_origin,
    807     const GetShareUrlCallback& callback,
    808     FileError error,
    809     scoped_ptr<ResourceEntry> entry) {
    810   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    811   DCHECK(!callback.is_null());
    812 
    813   if (error != FILE_ERROR_OK) {
    814     callback.Run(error, GURL());
    815     return;
    816   }
    817   if (util::IsSpecialResourceId(entry->resource_id())) {
    818     // Do not load special directories. Just return.
    819     callback.Run(FILE_ERROR_FAILED, GURL());
    820     return;
    821   }
    822 
    823   scheduler_->GetShareUrl(
    824       entry->resource_id(),
    825       embed_origin,
    826       ClientContext(USER_INITIATED),
    827       base::Bind(&FileSystem::OnGetResourceEntryForGetShareUrl,
    828                  weak_ptr_factory_.GetWeakPtr(),
    829                  callback));
    830 }
    831 
    832 void FileSystem::OnGetResourceEntryForGetShareUrl(
    833     const GetShareUrlCallback& callback,
    834     google_apis::GDataErrorCode status,
    835     const GURL& share_url) {
    836   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    837   DCHECK(!callback.is_null());
    838 
    839   FileError error = GDataToFileError(status);
    840   if (error != FILE_ERROR_OK) {
    841     callback.Run(error, GURL());
    842     return;
    843   }
    844 
    845   if (share_url.is_empty()) {
    846     callback.Run(FILE_ERROR_FAILED, GURL());
    847     return;
    848   }
    849 
    850   callback.Run(FILE_ERROR_OK, share_url);
    851 }
    852 
    853 void FileSystem::Search(const std::string& search_query,
    854                         const GURL& next_url,
    855                         const SearchCallback& callback) {
    856   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    857   DCHECK(!callback.is_null());
    858   search_operation_->Search(search_query, next_url, callback);
    859 }
    860 
    861 void FileSystem::SearchMetadata(const std::string& query,
    862                                 int options,
    863                                 int at_most_num_matches,
    864                                 const SearchMetadataCallback& callback) {
    865   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    866 
    867   if (hide_hosted_docs_)
    868     options |= SEARCH_METADATA_EXCLUDE_HOSTED_DOCUMENTS;
    869 
    870   drive::internal::SearchMetadata(blocking_task_runner_,
    871                                   resource_metadata_,
    872                                   query,
    873                                   options,
    874                                   at_most_num_matches,
    875                                   callback);
    876 }
    877 
    878 void FileSystem::OnDirectoryChangedByOperation(
    879     const base::FilePath& directory_path) {
    880   OnDirectoryChanged(directory_path);
    881 }
    882 
    883 void FileSystem::OnCacheFileUploadNeededByOperation(
    884     const std::string& resource_id) {
    885   sync_client_->AddUploadTask(resource_id);
    886 }
    887 
    888 void FileSystem::OnDirectoryChanged(const base::FilePath& directory_path) {
    889   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    890 
    891   FOR_EACH_OBSERVER(FileSystemObserver, observers_,
    892                     OnDirectoryChanged(directory_path));
    893 }
    894 
    895 void FileSystem::OnLoadFromServerComplete() {
    896   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    897 
    898   sync_client_->StartCheckingExistingPinnedFiles();
    899 }
    900 
    901 void FileSystem::OnInitialLoadComplete() {
    902   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    903 
    904   blocking_task_runner_->PostTask(FROM_HERE,
    905                                   base::Bind(&internal::RemoveStaleCacheFiles,
    906                                              cache_,
    907                                              resource_metadata_));
    908   sync_client_->StartProcessingBacklog();
    909 }
    910 
    911 void FileSystem::GetMetadata(
    912     const GetFilesystemMetadataCallback& callback) {
    913   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    914   DCHECK(!callback.is_null());
    915 
    916   FileSystemMetadata metadata;
    917   metadata.refreshing = change_list_loader_->IsRefreshing();
    918 
    919   // Metadata related to delta update.
    920   metadata.last_update_check_time = last_update_check_time_;
    921   metadata.last_update_check_error = last_update_check_error_;
    922 
    923   resource_metadata_->GetLargestChangestampOnUIThread(
    924       base::Bind(&OnGetLargestChangestamp, metadata, callback));
    925 }
    926 
    927 void FileSystem::MarkCacheFileAsMounted(
    928     const base::FilePath& drive_file_path,
    929     const MarkMountedCallback& callback) {
    930   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    931   DCHECK(!callback.is_null());
    932 
    933   GetResourceEntryByPath(
    934       drive_file_path,
    935       base::Bind(&FileSystem::MarkCacheFileAsMountedAfterGetResourceEntry,
    936                  weak_ptr_factory_.GetWeakPtr(), callback));
    937 }
    938 
    939 void FileSystem::MarkCacheFileAsMountedAfterGetResourceEntry(
    940     const MarkMountedCallback& callback,
    941     FileError error,
    942     scoped_ptr<ResourceEntry> entry) {
    943   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    944   DCHECK(!callback.is_null());
    945 
    946   if (error != FILE_ERROR_OK) {
    947     callback.Run(error, base::FilePath());
    948     return;
    949   }
    950 
    951   DCHECK(entry);
    952   cache_->MarkAsMountedOnUIThread(entry->resource_id(), callback);
    953 }
    954 
    955 void FileSystem::MarkCacheFileAsUnmounted(
    956     const base::FilePath& cache_file_path,
    957     const FileOperationCallback& callback) {
    958   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    959   DCHECK(!callback.is_null());
    960 
    961   if (!cache_->IsUnderFileCacheDirectory(cache_file_path)) {
    962     callback.Run(FILE_ERROR_FAILED);
    963     return;
    964   }
    965   cache_->MarkAsUnmountedOnUIThread(cache_file_path, callback);
    966 }
    967 
    968 void FileSystem::GetCacheEntryByResourceId(
    969     const std::string& resource_id,
    970     const GetCacheEntryCallback& callback) {
    971   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    972   DCHECK(!resource_id.empty());
    973   DCHECK(!callback.is_null());
    974 
    975   cache_->GetCacheEntryOnUIThread(resource_id, callback);
    976 }
    977 
    978 void FileSystem::OnDisableDriveHostedFilesChanged() {
    979   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    980   SetHideHostedDocuments(
    981       pref_service_->GetBoolean(prefs::kDisableDriveHostedFiles));
    982 }
    983 
    984 void FileSystem::SetHideHostedDocuments(bool hide) {
    985   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    986 
    987   if (hide == hide_hosted_docs_)
    988     return;
    989 
    990   hide_hosted_docs_ = hide;
    991 
    992   // Kick off directory refresh when this setting changes.
    993   FOR_EACH_OBSERVER(FileSystemObserver, observers_,
    994                     OnDirectoryChanged(util::GetDriveGrandRootPath()));
    995 }
    996 
    997 void FileSystem::InitializePreferenceObserver() {
    998   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    999 
   1000   pref_registrar_.reset(new PrefChangeRegistrar());
   1001   pref_registrar_->Init(pref_service_);
   1002   pref_registrar_->Add(
   1003       prefs::kDisableDriveHostedFiles,
   1004       base::Bind(&FileSystem::OnDisableDriveHostedFilesChanged,
   1005                  base::Unretained(this)));
   1006 }
   1007 
   1008 void FileSystem::OpenFile(const base::FilePath& file_path,
   1009                           OpenMode open_mode,
   1010                           const OpenFileCallback& callback) {
   1011   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   1012   DCHECK(!callback.is_null());
   1013 
   1014   open_file_operation_->OpenFile(file_path, open_mode, callback);
   1015 }
   1016 
   1017 void FileSystem::RefreshDirectory(const base::FilePath& directory_path) {
   1018   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   1019 
   1020   resource_metadata_->GetResourceEntryByPathOnUIThread(
   1021       directory_path,
   1022       base::Bind(&FileSystem::RefreshDirectoryAfterGetResourceEntry,
   1023                  weak_ptr_factory_.GetWeakPtr(),
   1024                  directory_path));
   1025 }
   1026 
   1027 void FileSystem::RefreshDirectoryAfterGetResourceEntry(
   1028     const base::FilePath& directory_path,
   1029     FileError error,
   1030     scoped_ptr<ResourceEntry> entry) {
   1031   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
   1032 
   1033   if (error != FILE_ERROR_OK || !entry->file_info().is_directory())
   1034     return;
   1035 
   1036   // Do not load special directories. Just return.
   1037   const std::string& id = entry->resource_id();
   1038   if (util::IsSpecialResourceId(id))
   1039     return;
   1040 
   1041   util::Log(logging::LOG_INFO,
   1042             "Thumbnail refresh for %s", directory_path.AsUTF8Unsafe().c_str());
   1043   change_list_loader_->LoadDirectoryFromServer(
   1044       id, base::Bind(&util::EmptyFileOperationCallback));
   1045 }
   1046 
   1047 }  // namespace drive
   1048