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