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/files/file_util.h"
9#include "base/prefs/pref_service.h"
10#include "chrome/browser/chromeos/drive/change_list_loader.h"
11#include "chrome/browser/chromeos/drive/directory_loader.h"
12#include "chrome/browser/chromeos/drive/drive.pb.h"
13#include "chrome/browser/chromeos/drive/file_cache.h"
14#include "chrome/browser/chromeos/drive/file_change.h"
15#include "chrome/browser/chromeos/drive/file_system/copy_operation.h"
16#include "chrome/browser/chromeos/drive/file_system/create_directory_operation.h"
17#include "chrome/browser/chromeos/drive/file_system/create_file_operation.h"
18#include "chrome/browser/chromeos/drive/file_system/download_operation.h"
19#include "chrome/browser/chromeos/drive/file_system/get_file_for_saving_operation.h"
20#include "chrome/browser/chromeos/drive/file_system/move_operation.h"
21#include "chrome/browser/chromeos/drive/file_system/open_file_operation.h"
22#include "chrome/browser/chromeos/drive/file_system/remove_operation.h"
23#include "chrome/browser/chromeos/drive/file_system/search_operation.h"
24#include "chrome/browser/chromeos/drive/file_system/touch_operation.h"
25#include "chrome/browser/chromeos/drive/file_system/truncate_operation.h"
26#include "chrome/browser/chromeos/drive/file_system_observer.h"
27#include "chrome/browser/chromeos/drive/file_system_util.h"
28#include "chrome/browser/chromeos/drive/job_scheduler.h"
29#include "chrome/browser/chromeos/drive/remove_stale_cache_files.h"
30#include "chrome/browser/chromeos/drive/resource_entry_conversion.h"
31#include "chrome/browser/chromeos/drive/search_metadata.h"
32#include "chrome/browser/chromeos/drive/sync_client.h"
33#include "chrome/browser/drive/drive_service_interface.h"
34#include "chrome/common/pref_names.h"
35#include "content/public/browser/browser_thread.h"
36#include "google_apis/drive/drive_api_parser.h"
37
38using content::BrowserThread;
39
40namespace drive {
41namespace {
42
43// Gets a ResourceEntry from the metadata, and overwrites its file info when the
44// cached file is dirty.
45FileError GetLocallyStoredResourceEntry(
46    internal::ResourceMetadata* resource_metadata,
47    internal::FileCache* cache,
48    const base::FilePath& file_path,
49    ResourceEntry* entry) {
50  std::string local_id;
51  FileError error = resource_metadata->GetIdByPath(file_path, &local_id);
52  if (error != FILE_ERROR_OK)
53    return error;
54
55  error = resource_metadata->GetResourceEntryById(local_id, entry);
56  if (error != FILE_ERROR_OK)
57    return error;
58
59  // For entries that will never be cached, use the original resource entry
60  // as is.
61  if (!entry->has_file_specific_info() ||
62      entry->file_specific_info().is_hosted_document())
63    return FILE_ERROR_OK;
64
65  // When cache is not found, use the original resource entry as is.
66  if (!entry->file_specific_info().has_cache_state())
67    return FILE_ERROR_OK;
68
69  // When cache is non-dirty and obsolete (old hash), use the original entry.
70  if (!entry->file_specific_info().cache_state().is_dirty() &&
71      entry->file_specific_info().md5() !=
72      entry->file_specific_info().cache_state().md5())
73    return FILE_ERROR_OK;
74
75  // If there's a valid cache, obtain the file info from the cache file itself.
76  base::FilePath local_cache_path;
77  error = cache->GetFile(local_id, &local_cache_path);
78  if (error != FILE_ERROR_OK)
79    return error;
80
81  base::File::Info file_info;
82  if (!base::GetFileInfo(local_cache_path, &file_info))
83    return FILE_ERROR_NOT_FOUND;
84
85  entry->mutable_file_info()->set_size(file_info.size);
86  return FILE_ERROR_OK;
87}
88
89// Runs the callback with parameters.
90void RunGetResourceEntryCallback(const GetResourceEntryCallback& callback,
91                                 scoped_ptr<ResourceEntry> entry,
92                                 FileError error) {
93  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
94  DCHECK(!callback.is_null());
95
96  if (error != FILE_ERROR_OK)
97    entry.reset();
98  callback.Run(error, entry.Pass());
99}
100
101// Used to implement Pin().
102FileError PinInternal(internal::ResourceMetadata* resource_metadata,
103                      internal::FileCache* cache,
104                      const base::FilePath& file_path,
105                      std::string* local_id) {
106  FileError error = resource_metadata->GetIdByPath(file_path, local_id);
107  if (error != FILE_ERROR_OK)
108    return error;
109
110  ResourceEntry entry;
111  error = resource_metadata->GetResourceEntryById(*local_id, &entry);
112  if (error != FILE_ERROR_OK)
113    return error;
114
115  // TODO(hashimoto): Support pinning directories. crbug.com/127831
116  if (entry.file_info().is_directory())
117    return FILE_ERROR_NOT_A_FILE;
118
119  return cache->Pin(*local_id);
120}
121
122// Used to implement Unpin().
123FileError UnpinInternal(internal::ResourceMetadata* resource_metadata,
124                        internal::FileCache* cache,
125                        const base::FilePath& file_path,
126                        std::string* local_id) {
127  FileError error = resource_metadata->GetIdByPath(file_path, local_id);
128  if (error != FILE_ERROR_OK)
129    return error;
130
131  return cache->Unpin(*local_id);
132}
133
134// Used to implement MarkCacheFileAsMounted().
135FileError MarkCacheFileAsMountedInternal(
136    internal::ResourceMetadata* resource_metadata,
137    internal::FileCache* cache,
138    const base::FilePath& drive_file_path,
139    base::FilePath* cache_file_path) {
140  std::string local_id;
141  FileError error = resource_metadata->GetIdByPath(drive_file_path, &local_id);
142  if (error != FILE_ERROR_OK)
143    return error;
144
145  return cache->MarkAsMounted(local_id, cache_file_path);
146}
147
148// Runs the callback with arguments.
149void RunMarkMountedCallback(const MarkMountedCallback& callback,
150                            base::FilePath* cache_file_path,
151                            FileError error) {
152  DCHECK(!callback.is_null());
153  callback.Run(error, *cache_file_path);
154}
155
156// Callback for ResourceMetadata::GetLargestChangestamp.
157// |callback| must not be null.
158void OnGetLargestChangestamp(
159    FileSystemMetadata metadata,  // Will be modified.
160    const GetFilesystemMetadataCallback& callback,
161    const int64* largest_changestamp,
162    FileError error) {
163  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
164  DCHECK(!callback.is_null());
165
166  metadata.largest_changestamp = *largest_changestamp;
167  callback.Run(metadata);
168}
169
170// Thin adapter to map GetFileCallback to FileOperationCallback.
171void GetFileCallbackToFileOperationCallbackAdapter(
172    const FileOperationCallback& callback,
173    FileError error,
174    const base::FilePath& unused_file_path,
175    scoped_ptr<ResourceEntry> unused_entry) {
176  callback.Run(error);
177}
178
179// Clears |resource_metadata| and |cache|.
180FileError ResetOnBlockingPool(internal::ResourceMetadata* resource_metadata,
181                              internal::FileCache* cache) {
182  FileError error = resource_metadata->Reset();
183  if (error != FILE_ERROR_OK)
184    return error;
185 return cache->ClearAll() ? FILE_ERROR_OK : FILE_ERROR_FAILED;
186}
187
188// Part of GetPathFromResourceId().
189// Obtains |file_path| from |resource_id|. The function should be run on the
190// blocking pool.
191FileError GetPathFromResourceIdOnBlockingPool(
192    internal::ResourceMetadata* resource_metadata,
193    const std::string& resource_id,
194    base::FilePath* file_path) {
195  std::string local_id;
196  const FileError error =
197      resource_metadata->GetIdByResourceId(resource_id, &local_id);
198  if (error != FILE_ERROR_OK)
199    return error;
200  return resource_metadata->GetFilePath(local_id, file_path);
201}
202
203// Part of GetPathFromResourceId().
204// Called when GetPathFromResourceIdInBlockingPool is complete.
205void GetPathFromResourceIdAfterGetPath(base::FilePath* file_path,
206                                       const GetFilePathCallback& callback,
207                                       FileError error) {
208  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
209  callback.Run(error, *file_path);
210}
211
212// Excludes hosted documents from the given entries.
213// Used to implement ReadDirectory().
214void FilterHostedDocuments(const ReadDirectoryEntriesCallback& callback,
215                           scoped_ptr<ResourceEntryVector> entries) {
216  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
217  DCHECK(!callback.is_null());
218
219  if (entries) {
220    // TODO(kinaba): Stop handling hide_hosted_docs here. crbug.com/256520.
221    scoped_ptr<ResourceEntryVector> filtered(new ResourceEntryVector);
222    for (size_t i = 0; i < entries->size(); ++i) {
223      if (entries->at(i).file_specific_info().is_hosted_document()) {
224        continue;
225      }
226      filtered->push_back(entries->at(i));
227    }
228    entries.swap(filtered);
229  }
230  callback.Run(entries.Pass());
231}
232
233// Adapter for using FileOperationCallback as google_apis::EntryActionCallback.
234void RunFileOperationCallbackAsEntryActionCallback(
235    const FileOperationCallback& callback,
236    google_apis::GDataErrorCode error) {
237  callback.Run(GDataToFileError(error));
238}
239
240}  // namespace
241
242struct FileSystem::CreateDirectoryParams {
243  base::FilePath directory_path;
244  bool is_exclusive;
245  bool is_recursive;
246  FileOperationCallback callback;
247};
248
249FileSystem::FileSystem(
250    PrefService* pref_service,
251    EventLogger* logger,
252    internal::FileCache* cache,
253    DriveServiceInterface* drive_service,
254    JobScheduler* scheduler,
255    internal::ResourceMetadata* resource_metadata,
256    base::SequencedTaskRunner* blocking_task_runner,
257    const base::FilePath& temporary_file_directory)
258    : pref_service_(pref_service),
259      logger_(logger),
260      cache_(cache),
261      drive_service_(drive_service),
262      scheduler_(scheduler),
263      resource_metadata_(resource_metadata),
264      last_update_check_error_(FILE_ERROR_OK),
265      blocking_task_runner_(blocking_task_runner),
266      temporary_file_directory_(temporary_file_directory),
267      weak_ptr_factory_(this) {
268  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
269
270  ResetComponents();
271}
272
273FileSystem::~FileSystem() {
274  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
275
276  directory_loader_->RemoveObserver(this);
277  change_list_loader_->RemoveObserver(this);
278}
279
280void FileSystem::Reset(const FileOperationCallback& callback) {
281  // Discard the current loader and operation objects and renew them. This is to
282  // avoid that changes initiated before the metadata reset is applied after the
283  // reset, which may cause an inconsistent state.
284  // TODO(kinaba): callbacks held in the subcomponents are discarded. We might
285  // want to have a way to abort and flush callbacks in in-flight operations.
286  ResetComponents();
287
288  base::PostTaskAndReplyWithResult(
289      blocking_task_runner_.get(),
290      FROM_HERE,
291      base::Bind(&ResetOnBlockingPool, resource_metadata_, cache_),
292      callback);
293}
294
295void FileSystem::ResetComponents() {
296  file_system::OperationDelegate* delegate = this;
297
298  about_resource_loader_.reset(new internal::AboutResourceLoader(scheduler_));
299  loader_controller_.reset(new internal::LoaderController);
300  change_list_loader_.reset(new internal::ChangeListLoader(
301      logger_,
302      blocking_task_runner_.get(),
303      resource_metadata_,
304      scheduler_,
305      about_resource_loader_.get(),
306      loader_controller_.get()));
307  change_list_loader_->AddObserver(this);
308  directory_loader_.reset(new internal::DirectoryLoader(
309      logger_,
310      blocking_task_runner_.get(),
311      resource_metadata_,
312      scheduler_,
313      about_resource_loader_.get(),
314      loader_controller_.get()));
315  directory_loader_->AddObserver(this);
316
317  sync_client_.reset(new internal::SyncClient(blocking_task_runner_.get(),
318                                              delegate,
319                                              scheduler_,
320                                              resource_metadata_,
321                                              cache_,
322                                              loader_controller_.get(),
323                                              temporary_file_directory_));
324
325  copy_operation_.reset(
326      new file_system::CopyOperation(blocking_task_runner_.get(),
327                                     delegate,
328                                     scheduler_,
329                                     resource_metadata_,
330                                     cache_));
331  create_directory_operation_.reset(new file_system::CreateDirectoryOperation(
332      blocking_task_runner_.get(), delegate, resource_metadata_));
333  create_file_operation_.reset(
334      new file_system::CreateFileOperation(blocking_task_runner_.get(),
335                                           delegate,
336                                           resource_metadata_));
337  move_operation_.reset(
338      new file_system::MoveOperation(blocking_task_runner_.get(),
339                                     delegate,
340                                     resource_metadata_));
341  open_file_operation_.reset(
342      new file_system::OpenFileOperation(blocking_task_runner_.get(),
343                                         delegate,
344                                         scheduler_,
345                                         resource_metadata_,
346                                         cache_,
347                                         temporary_file_directory_));
348  remove_operation_.reset(
349      new file_system::RemoveOperation(blocking_task_runner_.get(),
350                                       delegate,
351                                       resource_metadata_,
352                                       cache_));
353  touch_operation_.reset(new file_system::TouchOperation(
354      blocking_task_runner_.get(), delegate, resource_metadata_));
355  truncate_operation_.reset(
356      new file_system::TruncateOperation(blocking_task_runner_.get(),
357                                         delegate,
358                                         scheduler_,
359                                         resource_metadata_,
360                                         cache_,
361                                         temporary_file_directory_));
362  download_operation_.reset(
363      new file_system::DownloadOperation(blocking_task_runner_.get(),
364                                         delegate,
365                                         scheduler_,
366                                         resource_metadata_,
367                                         cache_,
368                                         temporary_file_directory_));
369  search_operation_.reset(new file_system::SearchOperation(
370      blocking_task_runner_.get(), scheduler_, resource_metadata_,
371      loader_controller_.get()));
372  get_file_for_saving_operation_.reset(
373      new file_system::GetFileForSavingOperation(logger_,
374                                                 blocking_task_runner_.get(),
375                                                 delegate,
376                                                 scheduler_,
377                                                 resource_metadata_,
378                                                 cache_,
379                                                 temporary_file_directory_));
380}
381
382void FileSystem::CheckForUpdates() {
383  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
384  DVLOG(1) << "CheckForUpdates";
385
386  change_list_loader_->CheckForUpdates(
387      base::Bind(&FileSystem::OnUpdateChecked, weak_ptr_factory_.GetWeakPtr()));
388}
389
390void FileSystem::OnUpdateChecked(FileError error) {
391  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
392  DVLOG(1) << "CheckForUpdates finished: " << FileErrorToString(error);
393  last_update_check_time_ = base::Time::Now();
394  last_update_check_error_ = error;
395}
396
397void FileSystem::AddObserver(FileSystemObserver* observer) {
398  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
399  observers_.AddObserver(observer);
400}
401
402void FileSystem::RemoveObserver(FileSystemObserver* observer) {
403  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
404  observers_.RemoveObserver(observer);
405}
406
407void FileSystem::TransferFileFromLocalToRemote(
408    const base::FilePath& local_src_file_path,
409    const base::FilePath& remote_dest_file_path,
410    const FileOperationCallback& callback) {
411  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
412  DCHECK(!callback.is_null());
413  copy_operation_->TransferFileFromLocalToRemote(local_src_file_path,
414                                                 remote_dest_file_path,
415                                                 callback);
416}
417
418void FileSystem::Copy(const base::FilePath& src_file_path,
419                      const base::FilePath& dest_file_path,
420                      bool preserve_last_modified,
421                      const FileOperationCallback& callback) {
422  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
423  DCHECK(!callback.is_null());
424  copy_operation_->Copy(
425      src_file_path, dest_file_path, preserve_last_modified, callback);
426}
427
428void FileSystem::Move(const base::FilePath& src_file_path,
429                      const base::FilePath& dest_file_path,
430                      const FileOperationCallback& callback) {
431  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
432  DCHECK(!callback.is_null());
433  move_operation_->Move(src_file_path, dest_file_path, callback);
434}
435
436void FileSystem::Remove(const base::FilePath& file_path,
437                        bool is_recursive,
438                        const FileOperationCallback& callback) {
439  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
440  DCHECK(!callback.is_null());
441  remove_operation_->Remove(file_path, is_recursive, callback);
442}
443
444void FileSystem::CreateDirectory(
445    const base::FilePath& directory_path,
446    bool is_exclusive,
447    bool is_recursive,
448    const FileOperationCallback& callback) {
449  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
450  DCHECK(!callback.is_null());
451
452  CreateDirectoryParams params;
453  params.directory_path = directory_path;
454  params.is_exclusive = is_exclusive;
455  params.is_recursive = is_recursive;
456  params.callback = callback;
457
458  // Ensure its parent directory is loaded to the local metadata.
459  ReadDirectory(directory_path.DirName(),
460                ReadDirectoryEntriesCallback(),
461                base::Bind(&FileSystem::CreateDirectoryAfterRead,
462                           weak_ptr_factory_.GetWeakPtr(), params));
463}
464
465void FileSystem::CreateDirectoryAfterRead(const CreateDirectoryParams& params,
466                                          FileError error) {
467  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
468  DCHECK(!params.callback.is_null());
469
470  DVLOG_IF(1, error != FILE_ERROR_OK) << "ReadDirectory failed. "
471                                      << FileErrorToString(error);
472
473  create_directory_operation_->CreateDirectory(
474      params.directory_path, params.is_exclusive, params.is_recursive,
475      params.callback);
476}
477
478void FileSystem::CreateFile(const base::FilePath& file_path,
479                            bool is_exclusive,
480                            const std::string& mime_type,
481                            const FileOperationCallback& callback) {
482  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
483  DCHECK(!callback.is_null());
484  create_file_operation_->CreateFile(
485      file_path, is_exclusive, mime_type, callback);
486}
487
488void FileSystem::TouchFile(const base::FilePath& file_path,
489                           const base::Time& last_access_time,
490                           const base::Time& last_modified_time,
491                           const FileOperationCallback& callback) {
492  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
493  DCHECK(!callback.is_null());
494  touch_operation_->TouchFile(
495      file_path, last_access_time, last_modified_time, callback);
496}
497
498void FileSystem::TruncateFile(const base::FilePath& file_path,
499                              int64 length,
500                              const FileOperationCallback& callback) {
501  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
502  DCHECK(!callback.is_null());
503  truncate_operation_->Truncate(file_path, length, callback);
504}
505
506void FileSystem::Pin(const base::FilePath& file_path,
507                     const FileOperationCallback& callback) {
508  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
509  DCHECK(!callback.is_null());
510
511  std::string* local_id = new std::string;
512  base::PostTaskAndReplyWithResult(
513      blocking_task_runner_.get(),
514      FROM_HERE,
515      base::Bind(&PinInternal, resource_metadata_, cache_, file_path, local_id),
516      base::Bind(&FileSystem::FinishPin,
517                 weak_ptr_factory_.GetWeakPtr(),
518                 callback,
519                 base::Owned(local_id)));
520}
521
522void FileSystem::FinishPin(const FileOperationCallback& callback,
523                           const std::string* local_id,
524                           FileError error) {
525  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
526  DCHECK(!callback.is_null());
527
528  if (error == FILE_ERROR_OK)
529    sync_client_->AddFetchTask(*local_id);
530  callback.Run(error);
531}
532
533void FileSystem::Unpin(const base::FilePath& file_path,
534                       const FileOperationCallback& callback) {
535  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
536  DCHECK(!callback.is_null());
537
538  std::string* local_id = new std::string;
539  base::PostTaskAndReplyWithResult(
540      blocking_task_runner_.get(),
541      FROM_HERE,
542      base::Bind(
543          &UnpinInternal, resource_metadata_, cache_, file_path, local_id),
544      base::Bind(&FileSystem::FinishUnpin,
545                 weak_ptr_factory_.GetWeakPtr(),
546                 callback,
547                 base::Owned(local_id)));
548}
549
550void FileSystem::FinishUnpin(const FileOperationCallback& callback,
551                             const std::string* local_id,
552                             FileError error) {
553  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
554  DCHECK(!callback.is_null());
555
556  if (error == FILE_ERROR_OK)
557    sync_client_->RemoveFetchTask(*local_id);
558  callback.Run(error);
559}
560
561void FileSystem::GetFile(const base::FilePath& file_path,
562                         const GetFileCallback& callback) {
563  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
564  DCHECK(!callback.is_null());
565
566  download_operation_->EnsureFileDownloadedByPath(
567      file_path,
568      ClientContext(USER_INITIATED),
569      GetFileContentInitializedCallback(),
570      google_apis::GetContentCallback(),
571      callback);
572}
573
574void FileSystem::GetFileForSaving(const base::FilePath& file_path,
575                                  const GetFileCallback& callback) {
576  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
577  DCHECK(!callback.is_null());
578
579  get_file_for_saving_operation_->GetFileForSaving(file_path, callback);
580}
581
582base::Closure FileSystem::GetFileContent(
583    const base::FilePath& file_path,
584    const GetFileContentInitializedCallback& initialized_callback,
585    const google_apis::GetContentCallback& get_content_callback,
586    const FileOperationCallback& completion_callback) {
587  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
588  DCHECK(!initialized_callback.is_null());
589  DCHECK(!get_content_callback.is_null());
590  DCHECK(!completion_callback.is_null());
591
592  return download_operation_->EnsureFileDownloadedByPath(
593      file_path,
594      ClientContext(USER_INITIATED),
595      initialized_callback,
596      get_content_callback,
597      base::Bind(&GetFileCallbackToFileOperationCallbackAdapter,
598                 completion_callback));
599}
600
601void FileSystem::GetResourceEntry(
602    const base::FilePath& file_path,
603    const GetResourceEntryCallback& callback) {
604  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
605  DCHECK(!callback.is_null());
606
607  ReadDirectory(file_path.DirName(),
608                ReadDirectoryEntriesCallback(),
609                base::Bind(&FileSystem::GetResourceEntryAfterRead,
610                           weak_ptr_factory_.GetWeakPtr(),
611                           file_path,
612                           callback));
613}
614
615void FileSystem::GetResourceEntryAfterRead(
616    const base::FilePath& file_path,
617    const GetResourceEntryCallback& callback,
618    FileError error) {
619  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
620  DCHECK(!callback.is_null());
621
622  DVLOG_IF(1, error != FILE_ERROR_OK) << "ReadDirectory failed. "
623                                      << FileErrorToString(error);
624
625  scoped_ptr<ResourceEntry> entry(new ResourceEntry);
626  ResourceEntry* entry_ptr = entry.get();
627  base::PostTaskAndReplyWithResult(
628      blocking_task_runner_.get(),
629      FROM_HERE,
630      base::Bind(&GetLocallyStoredResourceEntry,
631                 resource_metadata_,
632                 cache_,
633                 file_path,
634                 entry_ptr),
635      base::Bind(&RunGetResourceEntryCallback, callback, base::Passed(&entry)));
636}
637
638void FileSystem::ReadDirectory(
639    const base::FilePath& directory_path,
640    const ReadDirectoryEntriesCallback& entries_callback_in,
641    const FileOperationCallback& completion_callback) {
642  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
643  DCHECK(!completion_callback.is_null());
644
645  const bool hide_hosted_docs =
646      pref_service_->GetBoolean(prefs::kDisableDriveHostedFiles);
647  ReadDirectoryEntriesCallback entries_callback = entries_callback_in;
648  if (!entries_callback.is_null() && hide_hosted_docs)
649    entries_callback = base::Bind(&FilterHostedDocuments, entries_callback);
650
651  directory_loader_->ReadDirectory(
652      directory_path, entries_callback, completion_callback);
653
654  // Also start loading all of the user's contents.
655  change_list_loader_->LoadIfNeeded(
656      base::Bind(&util::EmptyFileOperationCallback));
657}
658
659void FileSystem::GetAvailableSpace(
660    const GetAvailableSpaceCallback& callback) {
661  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
662  DCHECK(!callback.is_null());
663
664  about_resource_loader_->GetAboutResource(
665      base::Bind(&FileSystem::OnGetAboutResource,
666                 weak_ptr_factory_.GetWeakPtr(),
667                 callback));
668}
669
670void FileSystem::OnGetAboutResource(
671    const GetAvailableSpaceCallback& callback,
672    google_apis::GDataErrorCode status,
673    scoped_ptr<google_apis::AboutResource> about_resource) {
674  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
675  DCHECK(!callback.is_null());
676
677  FileError error = GDataToFileError(status);
678  if (error != FILE_ERROR_OK) {
679    callback.Run(error, -1, -1);
680    return;
681  }
682  DCHECK(about_resource);
683
684  callback.Run(FILE_ERROR_OK,
685               about_resource->quota_bytes_total(),
686               about_resource->quota_bytes_used());
687}
688
689void FileSystem::GetShareUrl(const base::FilePath& file_path,
690                             const GURL& embed_origin,
691                             const GetShareUrlCallback& callback) {
692  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
693  DCHECK(!callback.is_null());
694
695  // Resolve the resource id.
696  ResourceEntry* entry = new ResourceEntry;
697  base::PostTaskAndReplyWithResult(
698      blocking_task_runner_.get(),
699      FROM_HERE,
700      base::Bind(&internal::ResourceMetadata::GetResourceEntryByPath,
701                 base::Unretained(resource_metadata_),
702                 file_path,
703                 entry),
704      base::Bind(&FileSystem::GetShareUrlAfterGetResourceEntry,
705                 weak_ptr_factory_.GetWeakPtr(),
706                 file_path,
707                 embed_origin,
708                 callback,
709                 base::Owned(entry)));
710}
711
712void FileSystem::GetShareUrlAfterGetResourceEntry(
713    const base::FilePath& file_path,
714    const GURL& embed_origin,
715    const GetShareUrlCallback& callback,
716    ResourceEntry* entry,
717    FileError error) {
718  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
719  DCHECK(!callback.is_null());
720
721  if (error != FILE_ERROR_OK) {
722    callback.Run(error, GURL());
723    return;
724  }
725  if (entry->resource_id().empty()) {
726    // This entry does not exist on the server. Just return.
727    callback.Run(FILE_ERROR_FAILED, GURL());
728    return;
729  }
730
731  scheduler_->GetShareUrl(
732      entry->resource_id(),
733      embed_origin,
734      ClientContext(USER_INITIATED),
735      base::Bind(&FileSystem::OnGetResourceEntryForGetShareUrl,
736                 weak_ptr_factory_.GetWeakPtr(),
737                 callback));
738}
739
740void FileSystem::OnGetResourceEntryForGetShareUrl(
741    const GetShareUrlCallback& callback,
742    google_apis::GDataErrorCode status,
743    const GURL& share_url) {
744  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
745  DCHECK(!callback.is_null());
746
747  FileError error = GDataToFileError(status);
748  if (error != FILE_ERROR_OK) {
749    callback.Run(error, GURL());
750    return;
751  }
752
753  if (share_url.is_empty()) {
754    callback.Run(FILE_ERROR_FAILED, GURL());
755    return;
756  }
757
758  callback.Run(FILE_ERROR_OK, share_url);
759}
760
761void FileSystem::Search(const std::string& search_query,
762                        const GURL& next_link,
763                        const SearchCallback& callback) {
764  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
765  DCHECK(!callback.is_null());
766  search_operation_->Search(search_query, next_link, callback);
767}
768
769void FileSystem::SearchMetadata(const std::string& query,
770                                int options,
771                                int at_most_num_matches,
772                                const SearchMetadataCallback& callback) {
773  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
774
775  // TODO(satorux): Stop handling hide_hosted_docs here. crbug.com/256520.
776  if (pref_service_->GetBoolean(prefs::kDisableDriveHostedFiles))
777    options |= SEARCH_METADATA_EXCLUDE_HOSTED_DOCUMENTS;
778
779  drive::internal::SearchMetadata(blocking_task_runner_,
780                                  resource_metadata_,
781                                  query,
782                                  options,
783                                  at_most_num_matches,
784                                  callback);
785}
786
787void FileSystem::OnFileChangedByOperation(const FileChange& changed_files) {
788  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
789
790  FOR_EACH_OBSERVER(
791      FileSystemObserver, observers_, OnFileChanged(changed_files));
792}
793
794void FileSystem::OnEntryUpdatedByOperation(const std::string& local_id) {
795  sync_client_->AddUpdateTask(ClientContext(USER_INITIATED), local_id);
796}
797
798void FileSystem::OnDriveSyncError(file_system::DriveSyncErrorType type,
799                                  const std::string& local_id) {
800  base::FilePath* file_path = new base::FilePath;
801  base::PostTaskAndReplyWithResult(
802      blocking_task_runner_.get(),
803      FROM_HERE,
804      base::Bind(&internal::ResourceMetadata::GetFilePath,
805                 base::Unretained(resource_metadata_),
806                 local_id,
807                 file_path),
808      base::Bind(&FileSystem::OnDriveSyncErrorAfterGetFilePath,
809                 weak_ptr_factory_.GetWeakPtr(),
810                 type,
811                 base::Owned(file_path)));
812}
813
814void FileSystem::OnDriveSyncErrorAfterGetFilePath(
815    file_system::DriveSyncErrorType type,
816    const base::FilePath* file_path,
817    FileError error) {
818  if (error != FILE_ERROR_OK)
819    return;
820  FOR_EACH_OBSERVER(FileSystemObserver,
821                    observers_,
822                    OnDriveSyncError(type, *file_path));
823}
824
825bool FileSystem::WaitForSyncComplete(const std::string& local_id,
826                                     const FileOperationCallback& callback) {
827  return sync_client_->WaitForUpdateTaskToComplete(local_id, callback);
828}
829
830void FileSystem::OnDirectoryReloaded(const base::FilePath& directory_path) {
831  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
832
833  FOR_EACH_OBSERVER(
834      FileSystemObserver, observers_, OnDirectoryChanged(directory_path));
835}
836
837void FileSystem::OnFileChanged(const FileChange& changed_files) {
838  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
839
840  FOR_EACH_OBSERVER(
841      FileSystemObserver, observers_, OnFileChanged(changed_files));
842}
843
844void FileSystem::OnLoadFromServerComplete() {
845  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
846
847  sync_client_->StartCheckingExistingPinnedFiles();
848}
849
850void FileSystem::OnInitialLoadComplete() {
851  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
852
853  blocking_task_runner_->PostTask(FROM_HERE,
854                                  base::Bind(&internal::RemoveStaleCacheFiles,
855                                             cache_,
856                                             resource_metadata_));
857  sync_client_->StartProcessingBacklog();
858}
859
860void FileSystem::GetMetadata(
861    const GetFilesystemMetadataCallback& callback) {
862  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
863  DCHECK(!callback.is_null());
864
865  FileSystemMetadata metadata;
866  metadata.refreshing = change_list_loader_->IsRefreshing();
867
868  // Metadata related to delta update.
869  metadata.last_update_check_time = last_update_check_time_;
870  metadata.last_update_check_error = last_update_check_error_;
871
872  int64* largest_changestamp = new int64(0);
873  base::PostTaskAndReplyWithResult(
874      blocking_task_runner_.get(),
875      FROM_HERE,
876      base::Bind(&internal::ResourceMetadata::GetLargestChangestamp,
877                 base::Unretained(resource_metadata_),
878                 largest_changestamp),
879      base::Bind(&OnGetLargestChangestamp,
880                 metadata,
881                 callback,
882                 base::Owned(largest_changestamp)));
883}
884
885void FileSystem::MarkCacheFileAsMounted(
886    const base::FilePath& drive_file_path,
887    const MarkMountedCallback& callback) {
888  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
889  DCHECK(!callback.is_null());
890
891  base::FilePath* cache_file_path = new base::FilePath;
892  base::PostTaskAndReplyWithResult(
893      blocking_task_runner_.get(),
894      FROM_HERE,
895      base::Bind(&MarkCacheFileAsMountedInternal,
896                 resource_metadata_,
897                 cache_,
898                 drive_file_path,
899                 cache_file_path),
900      base::Bind(
901          &RunMarkMountedCallback, callback, base::Owned(cache_file_path)));
902}
903
904void FileSystem::MarkCacheFileAsUnmounted(
905    const base::FilePath& cache_file_path,
906    const FileOperationCallback& callback) {
907  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
908  DCHECK(!callback.is_null());
909
910  if (!cache_->IsUnderFileCacheDirectory(cache_file_path)) {
911    callback.Run(FILE_ERROR_FAILED);
912    return;
913  }
914
915  base::PostTaskAndReplyWithResult(
916      blocking_task_runner_.get(),
917      FROM_HERE,
918      base::Bind(&internal::FileCache::MarkAsUnmounted,
919                 base::Unretained(cache_),
920                 cache_file_path),
921      callback);
922}
923
924void FileSystem::AddPermission(const base::FilePath& drive_file_path,
925                               const std::string& email,
926                               google_apis::drive::PermissionRole role,
927                               const FileOperationCallback& callback) {
928  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
929  DCHECK(!callback.is_null());
930
931  // Resolve the resource id.
932  ResourceEntry* const entry = new ResourceEntry;
933  base::PostTaskAndReplyWithResult(
934      blocking_task_runner_.get(),
935      FROM_HERE,
936      base::Bind(&internal::ResourceMetadata::GetResourceEntryByPath,
937                 base::Unretained(resource_metadata_),
938                 drive_file_path,
939                 entry),
940      base::Bind(&FileSystem::AddPermissionAfterGetResourceEntry,
941                 weak_ptr_factory_.GetWeakPtr(),
942                 email,
943                 role,
944                 callback,
945                 base::Owned(entry)));
946}
947
948void FileSystem::AddPermissionAfterGetResourceEntry(
949    const std::string& email,
950    google_apis::drive::PermissionRole role,
951    const FileOperationCallback& callback,
952    ResourceEntry* entry,
953    FileError error) {
954  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
955
956  if (error != FILE_ERROR_OK) {
957    callback.Run(error);
958    return;
959  }
960
961  scheduler_->AddPermission(
962      entry->resource_id(),
963      email,
964      role,
965      base::Bind(&RunFileOperationCallbackAsEntryActionCallback, callback));
966}
967
968void FileSystem::OpenFile(const base::FilePath& file_path,
969                          OpenMode open_mode,
970                          const std::string& mime_type,
971                          const OpenFileCallback& callback) {
972  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
973  DCHECK(!callback.is_null());
974
975  open_file_operation_->OpenFile(file_path, open_mode, mime_type, callback);
976}
977
978void FileSystem::GetPathFromResourceId(const std::string& resource_id,
979                                       const GetFilePathCallback& callback) {
980  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
981  DCHECK(!callback.is_null());
982
983  base::FilePath* const file_path = new base::FilePath();
984  base::PostTaskAndReplyWithResult(
985      blocking_task_runner_.get(),
986      FROM_HERE,
987      base::Bind(&GetPathFromResourceIdOnBlockingPool,
988                 resource_metadata_,
989                 resource_id,
990                 file_path),
991      base::Bind(&GetPathFromResourceIdAfterGetPath,
992                 base::Owned(file_path),
993                 callback));
994}
995}  // namespace drive
996