- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / drive / file_system.cc
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/platform_file.h"
10 #include "base/prefs/pref_service.h"
11 #include "chrome/browser/browser_process.h"
12 #include "chrome/browser/chromeos/drive/change_list_loader.h"
13 #include "chrome/browser/chromeos/drive/change_list_processor.h"
14 #include "chrome/browser/chromeos/drive/drive.pb.h"
15 #include "chrome/browser/chromeos/drive/file_cache.h"
16 #include "chrome/browser/chromeos/drive/file_system/copy_operation.h"
17 #include "chrome/browser/chromeos/drive/file_system/create_directory_operation.h"
18 #include "chrome/browser/chromeos/drive/file_system/create_file_operation.h"
19 #include "chrome/browser/chromeos/drive/file_system/download_operation.h"
20 #include "chrome/browser/chromeos/drive/file_system/get_file_for_saving_operation.h"
21 #include "chrome/browser/chromeos/drive/file_system/move_operation.h"
22 #include "chrome/browser/chromeos/drive/file_system/open_file_operation.h"
23 #include "chrome/browser/chromeos/drive/file_system/remove_operation.h"
24 #include "chrome/browser/chromeos/drive/file_system/search_operation.h"
25 #include "chrome/browser/chromeos/drive/file_system/touch_operation.h"
26 #include "chrome/browser/chromeos/drive/file_system/truncate_operation.h"
27 #include "chrome/browser/chromeos/drive/file_system/update_operation.h"
28 #include "chrome/browser/chromeos/drive/file_system_observer.h"
29 #include "chrome/browser/chromeos/drive/file_system_util.h"
30 #include "chrome/browser/chromeos/drive/job_scheduler.h"
31 #include "chrome/browser/chromeos/drive/logging.h"
32 #include "chrome/browser/chromeos/drive/remove_stale_cache_files.h"
33 #include "chrome/browser/chromeos/drive/resource_entry_conversion.h"
34 #include "chrome/browser/chromeos/drive/search_metadata.h"
35 #include "chrome/browser/chromeos/drive/sync_client.h"
36 #include "chrome/browser/drive/drive_service_interface.h"
37 #include "chrome/browser/google_apis/drive_api_parser.h"
38 #include "chrome/common/pref_names.h"
39 #include "content/public/browser/browser_thread.h"
40
41 using content::BrowserThread;
42
43 namespace drive {
44 namespace {
45
46 // Gets a ResourceEntry from the metadata, and overwrites its file info when the
47 // cached file is dirty.
48 FileError GetLocallyStoredResourceEntry(
49     internal::ResourceMetadata* resource_metadata,
50     internal::FileCache* cache,
51     const base::FilePath& file_path,
52     ResourceEntry* entry) {
53   std::string local_id;
54   FileError error = resource_metadata->GetIdByPath(file_path, &local_id);
55   if (error != FILE_ERROR_OK)
56     return error;
57
58   error = resource_metadata->GetResourceEntryById(local_id, entry);
59   if (error != FILE_ERROR_OK)
60     return error;
61
62   // For entries that will never be cached, use the original resource entry
63   // as is.
64   if (!entry->has_file_specific_info() ||
65       entry->file_specific_info().is_hosted_document())
66     return FILE_ERROR_OK;
67
68   // When no dirty cache is found, use the original resource entry as is.
69   FileCacheEntry cache_entry;
70   if (!cache->GetCacheEntry(local_id, &cache_entry) || !cache_entry.is_dirty())
71     return FILE_ERROR_OK;
72
73   // If the cache is dirty, obtain the file info from the cache file itself.
74   base::FilePath local_cache_path;
75   error = cache->GetFile(local_id, &local_cache_path);
76   if (error != FILE_ERROR_OK)
77     return error;
78
79   base::PlatformFileInfo file_info;
80   if (!file_util::GetFileInfo(local_cache_path, &file_info))
81     return FILE_ERROR_NOT_FOUND;
82
83   SetPlatformFileInfoToResourceEntry(file_info, entry);
84   return FILE_ERROR_OK;
85 }
86
87 // Runs the callback with parameters.
88 void RunGetResourceEntryCallback(const GetResourceEntryCallback& callback,
89                                  scoped_ptr<ResourceEntry> entry,
90                                  FileError error) {
91   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
92   DCHECK(!callback.is_null());
93
94   if (error != FILE_ERROR_OK)
95     entry.reset();
96   callback.Run(error, entry.Pass());
97 }
98
99 // Used to implement Pin().
100 FileError PinInternal(internal::ResourceMetadata* resource_metadata,
101                       internal::FileCache* cache,
102                       const base::FilePath& file_path,
103                       std::string* local_id) {
104   FileError error = resource_metadata->GetIdByPath(file_path, local_id);
105   if (error != FILE_ERROR_OK)
106     return error;
107
108   ResourceEntry entry;
109   error = resource_metadata->GetResourceEntryById(*local_id, &entry);
110   if (error != FILE_ERROR_OK)
111     return error;
112
113   // TODO(hashimoto): Support pinning directories. crbug.com/127831
114   if (entry.file_info().is_directory())
115     return FILE_ERROR_NOT_A_FILE;
116
117   return cache->Pin(*local_id);
118 }
119
120 // Used to implement Unpin().
121 FileError UnpinInternal(internal::ResourceMetadata* resource_metadata,
122                         internal::FileCache* cache,
123                         const base::FilePath& file_path,
124                         std::string* local_id) {
125   FileError error = resource_metadata->GetIdByPath(file_path, local_id);
126   if (error != FILE_ERROR_OK)
127     return error;
128
129   return cache->Unpin(*local_id);
130 }
131
132 // Used to implement MarkCacheFileAsMounted().
133 FileError MarkCacheFileAsMountedInternal(
134     internal::ResourceMetadata* resource_metadata,
135     internal::FileCache* cache,
136     const base::FilePath& drive_file_path,
137     base::FilePath* cache_file_path) {
138   std::string local_id;
139   FileError error = resource_metadata->GetIdByPath(drive_file_path, &local_id);
140   if (error != FILE_ERROR_OK)
141     return error;
142
143   return cache->MarkAsMounted(local_id, cache_file_path);
144 }
145
146 // Runs the callback with arguments.
147 void RunMarkMountedCallback(const MarkMountedCallback& callback,
148                             base::FilePath* cache_file_path,
149                             FileError error) {
150   DCHECK(!callback.is_null());
151   callback.Run(error, *cache_file_path);
152 }
153
154 // Used to implement GetCacheEntryByPath.
155 bool GetCacheEntryByPathInternal(internal::ResourceMetadata* resource_metadata,
156                                  internal::FileCache* cache,
157                                  const base::FilePath& drive_file_path,
158                                  FileCacheEntry* cache_entry) {
159   std::string id;
160   if (resource_metadata->GetIdByPath(drive_file_path, &id) != FILE_ERROR_OK)
161     return false;
162
163   return cache->GetCacheEntry(id, cache_entry);
164 }
165
166 // Runs the callback with arguments.
167 void RunGetCacheEntryCallback(const GetCacheEntryCallback& callback,
168                               const FileCacheEntry* cache_entry,
169                               bool success) {
170   DCHECK(!callback.is_null());
171   callback.Run(success, *cache_entry);
172 }
173
174 // Callback for ResourceMetadata::GetLargestChangestamp.
175 // |callback| must not be null.
176 void OnGetLargestChangestamp(
177     FileSystemMetadata metadata,  // Will be modified.
178     const GetFilesystemMetadataCallback& callback,
179     int64 largest_changestamp) {
180   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
181   DCHECK(!callback.is_null());
182
183   metadata.largest_changestamp = largest_changestamp;
184   callback.Run(metadata);
185 }
186
187 // Thin adapter to map GetFileCallback to FileOperationCallback.
188 void GetFileCallbackToFileOperationCallbackAdapter(
189     const FileOperationCallback& callback,
190     FileError error,
191     const base::FilePath& unused_file_path,
192     scoped_ptr<ResourceEntry> unused_entry) {
193   callback.Run(error);
194 }
195
196 // Clears |resource_metadata| and |cache|.
197 FileError ResetOnBlockingPool(internal::ResourceMetadata* resource_metadata,
198                               internal::FileCache* cache) {
199   FileError error = resource_metadata->Reset();
200   if (error != FILE_ERROR_OK)
201     return error;
202  return cache->ClearAll() ? FILE_ERROR_OK : FILE_ERROR_FAILED;
203 }
204
205 }  // namespace
206
207 FileSystem::FileSystem(
208     PrefService* pref_service,
209     internal::FileCache* cache,
210     DriveServiceInterface* drive_service,
211     JobScheduler* scheduler,
212     internal::ResourceMetadata* resource_metadata,
213     base::SequencedTaskRunner* blocking_task_runner,
214     const base::FilePath& temporary_file_directory)
215     : pref_service_(pref_service),
216       cache_(cache),
217       drive_service_(drive_service),
218       scheduler_(scheduler),
219       resource_metadata_(resource_metadata),
220       last_update_check_error_(FILE_ERROR_OK),
221       blocking_task_runner_(blocking_task_runner),
222       temporary_file_directory_(temporary_file_directory),
223       weak_ptr_factory_(this) {
224   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
225
226   ResetComponents();
227 }
228
229 FileSystem::~FileSystem() {
230   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
231
232   change_list_loader_->RemoveObserver(this);
233 }
234
235 void FileSystem::Reload(const FileOperationCallback& callback) {
236   // Discard the current loader and operation objects and renew them. This is to
237   // avoid that changes initiated before the metadata reset is applied after the
238   // reset, which may cause an inconsistent state.
239   // TODO(kinaba): callbacks held in the subcomponents are discarded. We might
240   // want to have a way to abort and flush callbacks in in-flight operations.
241   ResetComponents();
242
243   base::PostTaskAndReplyWithResult(
244       blocking_task_runner_,
245       FROM_HERE,
246       base::Bind(&ResetOnBlockingPool, resource_metadata_, cache_),
247       base::Bind(&FileSystem::ReloadAfterReset,
248                  weak_ptr_factory_.GetWeakPtr(),
249                  callback));
250 }
251
252 void FileSystem::ReloadAfterReset(const FileOperationCallback& callback,
253                                   FileError error) {
254   if (error != FILE_ERROR_OK) {
255     LOG(ERROR) << "Failed to reload Drive file system: "
256                << FileErrorToString(error);
257     callback.Run(error);
258     return;
259   }
260
261   change_list_loader_->LoadIfNeeded(
262       internal::DirectoryFetchInfo(),
263       base::Bind(&FileSystem::OnUpdateChecked, weak_ptr_factory_.GetWeakPtr()));
264   callback.Run(error);
265 }
266
267 void FileSystem::ResetComponents() {
268   file_system::OperationObserver* observer = this;
269   copy_operation_.reset(
270       new file_system::CopyOperation(blocking_task_runner_.get(),
271                                      observer,
272                                      scheduler_,
273                                      resource_metadata_,
274                                      cache_,
275                                      drive_service_,
276                                      temporary_file_directory_));
277   create_directory_operation_.reset(new file_system::CreateDirectoryOperation(
278       blocking_task_runner_.get(), observer, scheduler_, resource_metadata_));
279   create_file_operation_.reset(
280       new file_system::CreateFileOperation(blocking_task_runner_.get(),
281                                            observer,
282                                            scheduler_,
283                                            resource_metadata_,
284                                            cache_));
285   move_operation_.reset(
286       new file_system::MoveOperation(blocking_task_runner_.get(),
287                                      observer,
288                                      scheduler_,
289                                      resource_metadata_));
290   open_file_operation_.reset(
291       new file_system::OpenFileOperation(blocking_task_runner_.get(),
292                                          observer,
293                                          scheduler_,
294                                          resource_metadata_,
295                                          cache_,
296                                          temporary_file_directory_));
297   remove_operation_.reset(
298       new file_system::RemoveOperation(blocking_task_runner_.get(),
299                                        observer,
300                                        scheduler_,
301                                        resource_metadata_,
302                                        cache_));
303   touch_operation_.reset(new file_system::TouchOperation(
304       blocking_task_runner_.get(), observer, scheduler_, resource_metadata_));
305   truncate_operation_.reset(
306       new file_system::TruncateOperation(blocking_task_runner_.get(),
307                                          observer,
308                                          scheduler_,
309                                          resource_metadata_,
310                                          cache_,
311                                          temporary_file_directory_));
312   download_operation_.reset(
313       new file_system::DownloadOperation(blocking_task_runner_.get(),
314                                          observer,
315                                          scheduler_,
316                                          resource_metadata_,
317                                          cache_,
318                                          temporary_file_directory_));
319   update_operation_.reset(
320       new file_system::UpdateOperation(blocking_task_runner_.get(),
321                                        observer,
322                                        scheduler_,
323                                        resource_metadata_,
324                                        cache_));
325   search_operation_.reset(new file_system::SearchOperation(
326       blocking_task_runner_.get(), scheduler_, resource_metadata_));
327   get_file_for_saving_operation_.reset(
328       new file_system::GetFileForSavingOperation(blocking_task_runner_.get(),
329                                                  observer,
330                                                  scheduler_,
331                                                  resource_metadata_,
332                                                  cache_,
333                                                  temporary_file_directory_));
334
335   sync_client_.reset(new internal::SyncClient(blocking_task_runner_.get(),
336                                               observer,
337                                               scheduler_,
338                                               resource_metadata_,
339                                               cache_,
340                                               temporary_file_directory_));
341
342   change_list_loader_.reset(new internal::ChangeListLoader(
343       blocking_task_runner_.get(),
344       resource_metadata_,
345       scheduler_,
346       drive_service_));
347   change_list_loader_->AddObserver(this);
348 }
349
350 void FileSystem::CheckForUpdates() {
351   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
352   DVLOG(1) << "CheckForUpdates";
353
354   change_list_loader_->CheckForUpdates(
355       base::Bind(&FileSystem::OnUpdateChecked, weak_ptr_factory_.GetWeakPtr()));
356 }
357
358 void FileSystem::OnUpdateChecked(FileError error) {
359   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
360   DVLOG(1) << "CheckForUpdates finished: " << FileErrorToString(error);
361   last_update_check_time_ = base::Time::Now();
362   last_update_check_error_ = error;
363 }
364
365 void FileSystem::AddObserver(FileSystemObserver* observer) {
366   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
367   observers_.AddObserver(observer);
368 }
369
370 void FileSystem::RemoveObserver(FileSystemObserver* observer) {
371   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
372   observers_.RemoveObserver(observer);
373 }
374
375 void FileSystem::TransferFileFromLocalToRemote(
376     const base::FilePath& local_src_file_path,
377     const base::FilePath& remote_dest_file_path,
378     const FileOperationCallback& callback) {
379   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
380   DCHECK(!callback.is_null());
381   copy_operation_->TransferFileFromLocalToRemote(local_src_file_path,
382                                                  remote_dest_file_path,
383                                                  callback);
384 }
385
386 void FileSystem::Copy(const base::FilePath& src_file_path,
387                       const base::FilePath& dest_file_path,
388                       bool preserve_last_modified,
389                       const FileOperationCallback& callback) {
390   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
391   DCHECK(!callback.is_null());
392   copy_operation_->Copy(
393       src_file_path, dest_file_path, preserve_last_modified, callback);
394 }
395
396 void FileSystem::Move(const base::FilePath& src_file_path,
397                       const base::FilePath& dest_file_path,
398                       bool preserve_last_modified,
399                       const FileOperationCallback& callback) {
400   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
401   DCHECK(!callback.is_null());
402   move_operation_->Move(
403       src_file_path, dest_file_path, preserve_last_modified, callback);
404 }
405
406 void FileSystem::Remove(const base::FilePath& file_path,
407                         bool is_recursive,
408                         const FileOperationCallback& callback) {
409   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
410   DCHECK(!callback.is_null());
411   remove_operation_->Remove(file_path, is_recursive, callback);
412 }
413
414 void FileSystem::CreateDirectory(
415     const base::FilePath& directory_path,
416     bool is_exclusive,
417     bool is_recursive,
418     const FileOperationCallback& callback) {
419   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
420   DCHECK(!callback.is_null());
421
422   // Ensure its parent directory is loaded to the local metadata.
423   LoadDirectoryIfNeeded(
424       directory_path.DirName(),
425       base::Bind(&FileSystem::CreateDirectoryAfterLoad,
426                  weak_ptr_factory_.GetWeakPtr(),
427                  directory_path, is_exclusive, is_recursive, callback));
428 }
429
430 void FileSystem::CreateDirectoryAfterLoad(
431     const base::FilePath& directory_path,
432     bool is_exclusive,
433     bool is_recursive,
434     const FileOperationCallback& callback,
435     FileError load_error) {
436   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
437   DCHECK(!callback.is_null());
438
439   if (load_error != FILE_ERROR_OK) {
440     callback.Run(load_error);
441     return;
442   }
443
444   create_directory_operation_->CreateDirectory(
445       directory_path, is_exclusive, is_recursive, callback);
446 }
447
448 void FileSystem::CreateFile(const base::FilePath& file_path,
449                             bool is_exclusive,
450                             const std::string& mime_type,
451                             const FileOperationCallback& callback) {
452   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
453   DCHECK(!callback.is_null());
454   create_file_operation_->CreateFile(
455       file_path, is_exclusive, mime_type, callback);
456 }
457
458 void FileSystem::TouchFile(const base::FilePath& file_path,
459                            const base::Time& last_access_time,
460                            const base::Time& last_modified_time,
461                            const FileOperationCallback& callback) {
462   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
463   DCHECK(!last_access_time.is_null());
464   DCHECK(!last_modified_time.is_null());
465   DCHECK(!callback.is_null());
466   touch_operation_->TouchFile(
467       file_path, last_access_time, last_modified_time, callback);
468 }
469
470 void FileSystem::TruncateFile(const base::FilePath& file_path,
471                               int64 length,
472                               const FileOperationCallback& callback) {
473   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
474   DCHECK(!callback.is_null());
475   truncate_operation_->Truncate(file_path, length, callback);
476 }
477
478 void FileSystem::Pin(const base::FilePath& file_path,
479                      const FileOperationCallback& callback) {
480   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
481   DCHECK(!callback.is_null());
482
483   std::string* local_id = new std::string;
484   base::PostTaskAndReplyWithResult(
485       blocking_task_runner_,
486       FROM_HERE,
487       base::Bind(&PinInternal, resource_metadata_, cache_, file_path, local_id),
488       base::Bind(&FileSystem::FinishPin,
489                  weak_ptr_factory_.GetWeakPtr(),
490                  callback,
491                  base::Owned(local_id)));
492 }
493
494 void FileSystem::FinishPin(const FileOperationCallback& callback,
495                            const std::string* local_id,
496                            FileError error) {
497   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
498   DCHECK(!callback.is_null());
499
500   if (error == FILE_ERROR_OK)
501     sync_client_->AddFetchTask(*local_id);
502   callback.Run(error);
503 }
504
505 void FileSystem::Unpin(const base::FilePath& file_path,
506                        const FileOperationCallback& callback) {
507   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
508   DCHECK(!callback.is_null());
509
510   std::string* local_id = new std::string;
511   base::PostTaskAndReplyWithResult(
512       blocking_task_runner_,
513       FROM_HERE,
514       base::Bind(&UnpinInternal,
515                  resource_metadata_,
516                  cache_,
517                  file_path,
518                  local_id),
519       base::Bind(&FileSystem::FinishUnpin,
520                  weak_ptr_factory_.GetWeakPtr(),
521                  callback,
522                  base::Owned(local_id)));
523 }
524
525 void FileSystem::FinishUnpin(const FileOperationCallback& callback,
526                              const std::string* local_id,
527                              FileError error) {
528   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
529   DCHECK(!callback.is_null());
530
531   if (error == FILE_ERROR_OK)
532     sync_client_->RemoveFetchTask(*local_id);
533   callback.Run(error);
534 }
535
536 void FileSystem::GetFileByPath(const base::FilePath& file_path,
537                                const GetFileCallback& callback) {
538   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
539   DCHECK(!callback.is_null());
540
541   download_operation_->EnsureFileDownloadedByPath(
542       file_path,
543       ClientContext(USER_INITIATED),
544       GetFileContentInitializedCallback(),
545       google_apis::GetContentCallback(),
546       callback);
547 }
548
549 void FileSystem::GetFileByPathForSaving(const base::FilePath& file_path,
550                                         const GetFileCallback& callback) {
551   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
552   DCHECK(!callback.is_null());
553
554   get_file_for_saving_operation_->GetFileForSaving(file_path, callback);
555 }
556
557 void FileSystem::GetFileContentByPath(
558     const base::FilePath& file_path,
559     const GetFileContentInitializedCallback& initialized_callback,
560     const google_apis::GetContentCallback& get_content_callback,
561     const FileOperationCallback& completion_callback) {
562   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
563   DCHECK(!initialized_callback.is_null());
564   DCHECK(!get_content_callback.is_null());
565   DCHECK(!completion_callback.is_null());
566
567   download_operation_->EnsureFileDownloadedByPath(
568       file_path,
569       ClientContext(USER_INITIATED),
570       initialized_callback,
571       get_content_callback,
572       base::Bind(&GetFileCallbackToFileOperationCallbackAdapter,
573                  completion_callback));
574 }
575
576 void FileSystem::GetResourceEntryByPath(
577     const base::FilePath& file_path,
578     const GetResourceEntryCallback& callback) {
579   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
580   DCHECK(!callback.is_null());
581
582   scoped_ptr<ResourceEntry> entry(new ResourceEntry);
583   ResourceEntry* entry_ptr = entry.get();
584   base::PostTaskAndReplyWithResult(
585       blocking_task_runner_,
586       FROM_HERE,
587       base::Bind(&GetLocallyStoredResourceEntry,
588                  resource_metadata_,
589                  cache_,
590                  file_path,
591                  entry_ptr),
592       base::Bind(&FileSystem::GetResourceEntryByPathAfterGetEntry,
593                  weak_ptr_factory_.GetWeakPtr(),
594                  file_path,
595                  callback,
596                  base::Passed(&entry)));
597 }
598
599 void FileSystem::GetResourceEntryByPathAfterGetEntry(
600     const base::FilePath& file_path,
601     const GetResourceEntryCallback& callback,
602     scoped_ptr<ResourceEntry> entry,
603     FileError error) {
604   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
605   DCHECK(!callback.is_null());
606
607   if (error == FILE_ERROR_NOT_FOUND) {
608     // If the information about the path is not in the local ResourceMetadata,
609     // try fetching information of the directory and retry.
610     //
611     // Note: this forms mutual recursion between GetResourceEntryByPath and
612     // LoadDirectoryIfNeeded, because directory loading requires the existence
613     // of directory entry itself. The recursion terminates because we always go
614     // up the hierarchy by .DirName() bounded under the Drive root path.
615     if (util::GetDriveGrandRootPath().IsParent(file_path)) {
616       LoadDirectoryIfNeeded(
617           file_path.DirName(),
618           base::Bind(&FileSystem::GetResourceEntryByPathAfterLoad,
619                      weak_ptr_factory_.GetWeakPtr(),
620                      file_path,
621                      callback));
622       return;
623     }
624   }
625
626   callback.Run(error, entry.Pass());
627 }
628
629 void FileSystem::GetResourceEntryByPathAfterLoad(
630     const base::FilePath& file_path,
631     const GetResourceEntryCallback& callback,
632     FileError error) {
633   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
634   DCHECK(!callback.is_null());
635
636   if (error != FILE_ERROR_OK) {
637     callback.Run(error, scoped_ptr<ResourceEntry>());
638     return;
639   }
640
641   scoped_ptr<ResourceEntry> entry(new ResourceEntry);
642   ResourceEntry* entry_ptr = entry.get();
643   base::PostTaskAndReplyWithResult(
644       blocking_task_runner_,
645       FROM_HERE,
646       base::Bind(&GetLocallyStoredResourceEntry,
647                  resource_metadata_,
648                  cache_,
649                  file_path,
650                  entry_ptr),
651       base::Bind(&RunGetResourceEntryCallback,
652                  callback,
653                  base::Passed(&entry)));
654 }
655
656 void FileSystem::ReadDirectoryByPath(
657     const base::FilePath& directory_path,
658     const ReadDirectoryCallback& callback) {
659   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
660   DCHECK(!callback.is_null());
661
662   LoadDirectoryIfNeeded(
663       directory_path,
664       base::Bind(&FileSystem::ReadDirectoryByPathAfterLoad,
665                  weak_ptr_factory_.GetWeakPtr(),
666                  directory_path,
667                  callback));
668 }
669
670 void FileSystem::LoadDirectoryIfNeeded(const base::FilePath& directory_path,
671                                        const FileOperationCallback& callback) {
672   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
673   DCHECK(!callback.is_null());
674
675   GetResourceEntryByPath(
676       directory_path,
677       base::Bind(&FileSystem::LoadDirectoryIfNeededAfterGetEntry,
678                  weak_ptr_factory_.GetWeakPtr(),
679                  directory_path,
680                  callback));
681 }
682
683 void FileSystem::LoadDirectoryIfNeededAfterGetEntry(
684     const base::FilePath& directory_path,
685     const FileOperationCallback& callback,
686     FileError error,
687     scoped_ptr<ResourceEntry> entry) {
688   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
689   DCHECK(!callback.is_null());
690
691   if (error != FILE_ERROR_OK) {
692     callback.Run(error);
693     return;
694   }
695
696   if (!entry->file_info().is_directory()) {
697     callback.Run(FILE_ERROR_NOT_A_DIRECTORY);
698     return;
699   }
700
701   // Pass the directory fetch info so we can fetch the contents of the
702   // directory before loading change lists.
703   internal::DirectoryFetchInfo directory_fetch_info(
704       entry->resource_id(),
705       entry->directory_specific_info().changestamp());
706   change_list_loader_->LoadIfNeeded(directory_fetch_info, callback);
707 }
708
709 void FileSystem::ReadDirectoryByPathAfterLoad(
710     const base::FilePath& directory_path,
711     const ReadDirectoryCallback& callback,
712     FileError error) {
713   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
714   DCHECK(!callback.is_null());
715
716   DLOG_IF(INFO, error != FILE_ERROR_OK) << "LoadIfNeeded failed. "
717                                         << FileErrorToString(error);
718
719   resource_metadata_->ReadDirectoryByPathOnUIThread(
720       directory_path,
721       base::Bind(&FileSystem::ReadDirectoryByPathAfterRead,
722                  weak_ptr_factory_.GetWeakPtr(),
723                  directory_path,
724                  callback));
725 }
726
727 void FileSystem::ReadDirectoryByPathAfterRead(
728     const base::FilePath& directory_path,
729     const ReadDirectoryCallback& callback,
730     FileError error,
731     scoped_ptr<ResourceEntryVector> entries) {
732   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
733   DCHECK(!callback.is_null());
734
735   if (error != FILE_ERROR_OK) {
736     callback.Run(error,
737                  scoped_ptr<ResourceEntryVector>());
738     return;
739   }
740   DCHECK(entries.get());  // This is valid for empty directories too.
741
742   // TODO(satorux): Stop handling hide_hosted_docs here. crbug.com/256520.
743   const bool hide_hosted_docs =
744       pref_service_->GetBoolean(prefs::kDisableDriveHostedFiles);
745   scoped_ptr<ResourceEntryVector> filtered(new ResourceEntryVector);
746   for (size_t i = 0; i < entries->size(); ++i) {
747     if (hide_hosted_docs &&
748         entries->at(i).file_specific_info().is_hosted_document()) {
749       continue;
750     }
751     filtered->push_back(entries->at(i));
752   }
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_link,
855                         const SearchCallback& callback) {
856   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
857   DCHECK(!callback.is_null());
858   search_operation_->Search(search_query, next_link, 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   // TODO(satorux): Stop handling hide_hosted_docs here. crbug.com/256520.
868   if (pref_service_->GetBoolean(prefs::kDisableDriveHostedFiles))
869     options |= SEARCH_METADATA_EXCLUDE_HOSTED_DOCUMENTS;
870
871   drive::internal::SearchMetadata(blocking_task_runner_,
872                                   resource_metadata_,
873                                   query,
874                                   options,
875                                   at_most_num_matches,
876                                   callback);
877 }
878
879 void FileSystem::OnDirectoryChangedByOperation(
880     const base::FilePath& directory_path) {
881   OnDirectoryChanged(directory_path);
882 }
883
884 void FileSystem::OnCacheFileUploadNeededByOperation(
885     const std::string& local_id) {
886   sync_client_->AddUploadTask(local_id);
887 }
888
889 void FileSystem::OnDirectoryChanged(const base::FilePath& directory_path) {
890   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
891
892   FOR_EACH_OBSERVER(FileSystemObserver, observers_,
893                     OnDirectoryChanged(directory_path));
894 }
895
896 void FileSystem::OnLoadFromServerComplete() {
897   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
898
899   sync_client_->StartCheckingExistingPinnedFiles();
900 }
901
902 void FileSystem::OnInitialLoadComplete() {
903   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
904
905   blocking_task_runner_->PostTask(FROM_HERE,
906                                   base::Bind(&internal::RemoveStaleCacheFiles,
907                                              cache_,
908                                              resource_metadata_));
909   sync_client_->StartProcessingBacklog();
910 }
911
912 void FileSystem::GetMetadata(
913     const GetFilesystemMetadataCallback& callback) {
914   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
915   DCHECK(!callback.is_null());
916
917   FileSystemMetadata metadata;
918   metadata.refreshing = change_list_loader_->IsRefreshing();
919
920   // Metadata related to delta update.
921   metadata.last_update_check_time = last_update_check_time_;
922   metadata.last_update_check_error = last_update_check_error_;
923
924   base::PostTaskAndReplyWithResult(
925       blocking_task_runner_,
926       FROM_HERE,
927       base::Bind(&internal::ResourceMetadata::GetLargestChangestamp,
928                  base::Unretained(resource_metadata_)),
929       base::Bind(&OnGetLargestChangestamp, metadata, callback));
930 }
931
932 void FileSystem::MarkCacheFileAsMounted(
933     const base::FilePath& drive_file_path,
934     const MarkMountedCallback& callback) {
935   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
936   DCHECK(!callback.is_null());
937
938   base::FilePath* cache_file_path = new base::FilePath;
939   base::PostTaskAndReplyWithResult(
940       blocking_task_runner_,
941       FROM_HERE,
942       base::Bind(&MarkCacheFileAsMountedInternal,
943                  resource_metadata_,
944                  cache_,
945                  drive_file_path,
946                  cache_file_path),
947       base::Bind(&RunMarkMountedCallback,
948                  callback,
949                  base::Owned(cache_file_path)));
950 }
951
952 void FileSystem::MarkCacheFileAsUnmounted(
953     const base::FilePath& cache_file_path,
954     const FileOperationCallback& callback) {
955   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
956   DCHECK(!callback.is_null());
957
958   if (!cache_->IsUnderFileCacheDirectory(cache_file_path)) {
959     callback.Run(FILE_ERROR_FAILED);
960     return;
961   }
962   cache_->MarkAsUnmountedOnUIThread(cache_file_path, callback);
963 }
964
965 void FileSystem::GetCacheEntryByPath(
966     const base::FilePath& drive_file_path,
967     const GetCacheEntryCallback& callback) {
968   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
969   DCHECK(!callback.is_null());
970
971   FileCacheEntry* cache_entry = new FileCacheEntry;
972   base::PostTaskAndReplyWithResult(
973       blocking_task_runner_,
974       FROM_HERE,
975       base::Bind(&GetCacheEntryByPathInternal,
976                  resource_metadata_,
977                  cache_,
978                  drive_file_path,
979                  cache_entry),
980       base::Bind(&RunGetCacheEntryCallback,
981                  callback,
982                  base::Owned(cache_entry)));
983 }
984
985 void FileSystem::OpenFile(const base::FilePath& file_path,
986                           OpenMode open_mode,
987                           const std::string& mime_type,
988                           const OpenFileCallback& callback) {
989   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
990   DCHECK(!callback.is_null());
991
992   open_file_operation_->OpenFile(file_path, open_mode, mime_type, callback);
993 }
994
995 }  // namespace drive