Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / chromeos / extensions / file_manager / private_api_file_system.cc
1 // Copyright 2013 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/extensions/file_manager/private_api_file_system.h"
6
7 #include <sys/statvfs.h>
8
9 #include "base/posix/eintr_wrapper.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/stringprintf.h"
12 #include "base/task_runner_util.h"
13 #include "base/threading/sequenced_worker_pool.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/chromeos/drive/drive.pb.h"
16 #include "chrome/browser/chromeos/drive/file_system_interface.h"
17 #include "chrome/browser/chromeos/drive/file_system_util.h"
18 #include "chrome/browser/chromeos/extensions/file_manager/event_router.h"
19 #include "chrome/browser/chromeos/extensions/file_manager/file_browser_private_api.h"
20 #include "chrome/browser/chromeos/extensions/file_manager/private_api_util.h"
21 #include "chrome/browser/chromeos/file_manager/fileapi_util.h"
22 #include "chrome/browser/chromeos/file_manager/volume_manager.h"
23 #include "chrome/browser/chromeos/fileapi/file_system_backend.h"
24 #include "chrome/browser/profiles/profile.h"
25 #include "chrome/browser/profiles/profile_manager.h"
26 #include "chrome/common/extensions/api/file_browser_private.h"
27 #include "chromeos/disks/disk_mount_manager.h"
28 #include "content/public/browser/child_process_security_policy.h"
29 #include "content/public/browser/render_process_host.h"
30 #include "content/public/browser/render_view_host.h"
31 #include "webkit/browser/fileapi/file_system_context.h"
32 #include "webkit/browser/fileapi/file_system_file_util.h"
33 #include "webkit/browser/fileapi/file_system_operation_context.h"
34 #include "webkit/browser/fileapi/file_system_operation_runner.h"
35 #include "webkit/browser/fileapi/file_system_url.h"
36 #include "webkit/common/fileapi/file_system_info.h"
37 #include "webkit/common/fileapi/file_system_types.h"
38 #include "webkit/common/fileapi/file_system_util.h"
39
40 using chromeos::disks::DiskMountManager;
41 using content::BrowserThread;
42 using content::ChildProcessSecurityPolicy;
43 using fileapi::FileSystemURL;
44
45 namespace extensions {
46 namespace {
47
48 // Retrieves total and remaining available size on |mount_path|.
49 void GetSizeStatsOnBlockingPool(const std::string& mount_path,
50                                 uint64* total_size,
51                                 uint64* remaining_size) {
52   struct statvfs stat = {};  // Zero-clear
53   if (HANDLE_EINTR(statvfs(mount_path.c_str(), &stat)) == 0) {
54     *total_size = static_cast<uint64>(stat.f_blocks) * stat.f_frsize;
55     *remaining_size = static_cast<uint64>(stat.f_bavail) * stat.f_frsize;
56   }
57 }
58
59 // Retrieves the maximum file name length of the file system of |path|.
60 // Returns 0 if it could not be queried.
61 size_t GetFileNameMaxLengthOnBlockingPool(const std::string& path) {
62   struct statvfs stat = {};
63   if (HANDLE_EINTR(statvfs(path.c_str(), &stat)) != 0) {
64     // The filesystem seems not supporting statvfs(). Assume it to be a commonly
65     // used bound 255, and log the failure.
66     LOG(ERROR) << "Cannot statvfs() the name length limit for: " << path;
67     return 255;
68   }
69   return stat.f_namemax;
70 }
71
72 // Returns EventRouter for the |profile_id| if available.
73 file_manager::EventRouter* GetEventRouterByProfileId(void* profile_id) {
74   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
75
76   // |profile_id| needs to be checked with ProfileManager::IsValidProfile
77   // before using it.
78   Profile* profile = reinterpret_cast<Profile*>(profile_id);
79   if (!g_browser_process->profile_manager()->IsValidProfile(profile))
80     return NULL;
81
82   return file_manager::FileBrowserPrivateAPI::Get(profile)->event_router();
83 }
84
85 // Notifies the copy progress to extensions via event router.
86 void NotifyCopyProgress(
87     void* profile_id,
88     fileapi::FileSystemOperationRunner::OperationID operation_id,
89     fileapi::FileSystemOperation::CopyProgressType type,
90     const FileSystemURL& source_url,
91     const FileSystemURL& destination_url,
92     int64 size) {
93   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
94
95   file_manager::EventRouter* event_router =
96       GetEventRouterByProfileId(profile_id);
97   if (event_router) {
98     event_router->OnCopyProgress(
99         operation_id, type,
100         source_url.ToGURL(), destination_url.ToGURL(), size);
101   }
102 }
103
104 // Callback invoked periodically on progress update of Copy().
105 void OnCopyProgress(
106     void* profile_id,
107     fileapi::FileSystemOperationRunner::OperationID* operation_id,
108     fileapi::FileSystemOperation::CopyProgressType type,
109     const FileSystemURL& source_url,
110     const FileSystemURL& destination_url,
111     int64 size) {
112   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
113
114   BrowserThread::PostTask(
115       BrowserThread::UI, FROM_HERE,
116       base::Bind(&NotifyCopyProgress,
117                  profile_id, *operation_id, type,
118                  source_url, destination_url, size));
119 }
120
121 // Notifies the copy completion to extensions via event router.
122 void NotifyCopyCompletion(
123     void* profile_id,
124     fileapi::FileSystemOperationRunner::OperationID operation_id,
125     const FileSystemURL& source_url,
126     const FileSystemURL& destination_url,
127     base::File::Error error) {
128   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
129
130   file_manager::EventRouter* event_router =
131       GetEventRouterByProfileId(profile_id);
132   if (event_router)
133     event_router->OnCopyCompleted(
134         operation_id,
135         source_url.ToGURL(), destination_url.ToGURL(), error);
136 }
137
138 // Callback invoked upon completion of Copy() (regardless of succeeded or
139 // failed).
140 void OnCopyCompleted(
141     void* profile_id,
142     fileapi::FileSystemOperationRunner::OperationID* operation_id,
143     const FileSystemURL& source_url,
144     const FileSystemURL& destination_url,
145     base::File::Error error) {
146   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
147
148   BrowserThread::PostTask(
149       BrowserThread::UI, FROM_HERE,
150       base::Bind(&NotifyCopyCompletion,
151                  profile_id, *operation_id,
152                  source_url, destination_url, error));
153 }
154
155 // Starts the copy operation via FileSystemOperationRunner.
156 fileapi::FileSystemOperationRunner::OperationID StartCopyOnIOThread(
157     void* profile_id,
158     scoped_refptr<fileapi::FileSystemContext> file_system_context,
159     const FileSystemURL& source_url,
160     const FileSystemURL& destination_url) {
161   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
162
163   // Note: |operation_id| is owned by the callback for
164   // FileSystemOperationRunner::Copy(). It is always called in the next message
165   // loop or later, so at least during this invocation it should alive.
166   fileapi::FileSystemOperationRunner::OperationID* operation_id =
167       new fileapi::FileSystemOperationRunner::OperationID;
168   *operation_id = file_system_context->operation_runner()->Copy(
169       source_url, destination_url,
170       fileapi::FileSystemOperation::OPTION_PRESERVE_LAST_MODIFIED,
171       base::Bind(&OnCopyProgress,
172                  profile_id, base::Unretained(operation_id)),
173       base::Bind(&OnCopyCompleted,
174                  profile_id, base::Owned(operation_id),
175                  source_url, destination_url));
176   return *operation_id;
177 }
178
179 void OnCopyCancelled(base::File::Error error) {
180   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
181
182   // We just ignore the status if the copy is actually cancelled or not,
183   // because failing cancellation means the operation is not running now.
184   DLOG_IF(WARNING, error != base::File::FILE_OK)
185       << "Failed to cancel copy: " << error;
186 }
187
188 // Cancels the running copy operation identified by |operation_id|.
189 void CancelCopyOnIOThread(
190     scoped_refptr<fileapi::FileSystemContext> file_system_context,
191     fileapi::FileSystemOperationRunner::OperationID operation_id) {
192   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
193
194   file_system_context->operation_runner()->Cancel(
195       operation_id, base::Bind(&OnCopyCancelled));
196 }
197
198 }  // namespace
199
200 void FileBrowserPrivateRequestFileSystemFunction::DidFail(
201     base::File::Error error_code) {
202   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
203
204   SetError(base::StringPrintf("File error %d", static_cast<int>(error_code)));
205   SendResponse(false);
206 }
207
208 bool FileBrowserPrivateRequestFileSystemFunction::
209     SetupFileSystemAccessPermissions(
210         scoped_refptr<fileapi::FileSystemContext> file_system_context,
211         int child_id,
212         Profile* profile,
213         scoped_refptr<const extensions::Extension> extension) {
214   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
215
216   if (!extension.get())
217     return false;
218
219   // Make sure that only component extension can access the entire
220   // local file system.
221   if (extension_->location() != extensions::Manifest::COMPONENT) {
222     NOTREACHED() << "Private method access by non-component extension "
223                  << extension->id();
224     return false;
225   }
226
227   fileapi::ExternalFileSystemBackend* backend =
228       file_system_context->external_backend();
229   if (!backend)
230     return false;
231
232   // Grant full access to File API from this component extension.
233   backend->GrantFullAccessToExtension(extension_->id());
234
235   // Grant R/W file permissions to the renderer hosting component
236   // extension for all paths exposed by our local file system backend.
237   std::vector<base::FilePath> root_dirs = backend->GetRootDirectories();
238   for (size_t i = 0; i < root_dirs.size(); ++i) {
239     ChildProcessSecurityPolicy::GetInstance()->GrantCreateReadWriteFile(
240         child_id, root_dirs[i]);
241   }
242
243   // Grant R/W permissions to profile-specific directories (Drive, Downloads)
244   // from other profiles. Those directories may not be mounted at this moment
245   // yet, so we need to do this separately from the above loop over
246   // GetRootDirectories().
247   const std::vector<Profile*>& profiles =
248       g_browser_process->profile_manager()->GetLoadedProfiles();
249   for (size_t i = 0; i < profiles.size(); ++i) {
250     if (!profiles[i]->IsOffTheRecord()) {
251       file_manager::util::SetupProfileFileAccessPermissions(child_id,
252                                                             profiles[i]);
253     }
254   }
255
256   return true;
257 }
258
259 bool FileBrowserPrivateRequestFileSystemFunction::RunImpl() {
260   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
261   using extensions::api::file_browser_private::RequestFileSystem::Params;
262   const scoped_ptr<Params> params(Params::Create(*args_));
263   EXTENSION_FUNCTION_VALIDATE(params);
264
265   // TODO(satorux): Handle the file system ID. crbug.com/322305.
266   DCHECK_EQ("compatible", params->volume_id);
267
268   if (!dispatcher() || !render_view_host() || !render_view_host()->GetProcess())
269     return false;
270
271   set_log_on_completion(true);
272
273   scoped_refptr<fileapi::FileSystemContext> file_system_context =
274       file_manager::util::GetFileSystemContextForRenderViewHost(
275           GetProfile(), render_view_host());
276
277   // Set up file permission access.
278   const int child_id = render_view_host()->GetProcess()->GetID();
279   if (!SetupFileSystemAccessPermissions(file_system_context,
280                                         child_id,
281                                         GetProfile(),
282                                         GetExtension())) {
283     DidFail(base::File::FILE_ERROR_SECURITY);
284     return false;
285   }
286
287   fileapi::FileSystemInfo info =
288       fileapi::GetFileSystemInfoForChromeOS(source_url_.GetOrigin());
289
290   base::DictionaryValue* dict = new base::DictionaryValue();
291   SetResult(dict);
292   dict->SetString("name", info.name);
293   dict->SetString("root_url", info.root_url.spec());
294   dict->SetInteger("error", drive::FILE_ERROR_OK);
295   SendResponse(true);
296   return true;
297 }
298
299 void FileWatchFunctionBase::Respond(bool success) {
300   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
301
302   SetResult(base::Value::CreateBooleanValue(success));
303   SendResponse(success);
304 }
305
306 bool FileWatchFunctionBase::RunImpl() {
307   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
308
309   if (!render_view_host() || !render_view_host()->GetProcess())
310     return false;
311
312   // First param is url of a file to watch.
313   std::string url;
314   if (!args_->GetString(0, &url) || url.empty())
315     return false;
316
317   scoped_refptr<fileapi::FileSystemContext> file_system_context =
318       file_manager::util::GetFileSystemContextForRenderViewHost(
319           GetProfile(), render_view_host());
320
321   FileSystemURL file_watch_url = file_system_context->CrackURL(GURL(url));
322   base::FilePath local_path = file_watch_url.path();
323   base::FilePath virtual_path = file_watch_url.virtual_path();
324   if (local_path.empty()) {
325     Respond(false);
326     return true;
327   }
328   PerformFileWatchOperation(local_path, virtual_path, extension_id());
329
330   return true;
331 }
332
333 void FileBrowserPrivateAddFileWatchFunction::PerformFileWatchOperation(
334     const base::FilePath& local_path,
335     const base::FilePath& virtual_path,
336     const std::string& extension_id) {
337   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
338
339   file_manager::EventRouter* event_router =
340       file_manager::FileBrowserPrivateAPI::Get(GetProfile())->event_router();
341   event_router->AddFileWatch(
342       local_path,
343       virtual_path,
344       extension_id,
345       base::Bind(&FileBrowserPrivateAddFileWatchFunction::Respond, this));
346 }
347
348 void FileBrowserPrivateRemoveFileWatchFunction::PerformFileWatchOperation(
349     const base::FilePath& local_path,
350     const base::FilePath& unused,
351     const std::string& extension_id) {
352   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
353
354   file_manager::EventRouter* event_router =
355       file_manager::FileBrowserPrivateAPI::Get(GetProfile())->event_router();
356   event_router->RemoveFileWatch(local_path, extension_id);
357   Respond(true);
358 }
359
360 bool FileBrowserPrivateGetSizeStatsFunction::RunImpl() {
361   using extensions::api::file_browser_private::GetSizeStats::Params;
362   const scoped_ptr<Params> params(Params::Create(*args_));
363   EXTENSION_FUNCTION_VALIDATE(params);
364
365   using file_manager::VolumeManager;
366   using file_manager::VolumeInfo;
367   VolumeManager* volume_manager = VolumeManager::Get(GetProfile());
368   if (!volume_manager)
369     return false;
370
371   VolumeInfo volume_info;
372   if (!volume_manager->FindVolumeInfoById(params->volume_id, &volume_info))
373     return false;
374
375   if (volume_info.type == file_manager::VOLUME_TYPE_GOOGLE_DRIVE) {
376     drive::FileSystemInterface* file_system =
377         drive::util::GetFileSystemByProfile(GetProfile());
378     if (!file_system) {
379       // |file_system| is NULL if Drive is disabled.
380       // If stats couldn't be gotten for drive, result should be left
381       // undefined. See comments in GetDriveAvailableSpaceCallback().
382       SendResponse(true);
383       return true;
384     }
385
386     file_system->GetAvailableSpace(
387         base::Bind(&FileBrowserPrivateGetSizeStatsFunction::
388                        GetDriveAvailableSpaceCallback,
389                    this));
390   } else {
391     uint64* total_size = new uint64(0);
392     uint64* remaining_size = new uint64(0);
393     BrowserThread::PostBlockingPoolTaskAndReply(
394         FROM_HERE,
395         base::Bind(&GetSizeStatsOnBlockingPool,
396                    volume_info.mount_path.value(),
397                    total_size,
398                    remaining_size),
399         base::Bind(&FileBrowserPrivateGetSizeStatsFunction::
400                        GetSizeStatsCallback,
401                    this,
402                    base::Owned(total_size),
403                    base::Owned(remaining_size)));
404   }
405   return true;
406 }
407
408 void FileBrowserPrivateGetSizeStatsFunction::GetDriveAvailableSpaceCallback(
409     drive::FileError error,
410     int64 bytes_total,
411     int64 bytes_used) {
412   if (error == drive::FILE_ERROR_OK) {
413     const uint64 bytes_total_unsigned = bytes_total;
414     const uint64 bytes_remaining_unsigned = bytes_total - bytes_used;
415     GetSizeStatsCallback(&bytes_total_unsigned,
416                          &bytes_remaining_unsigned);
417   } else {
418     // If stats couldn't be gotten for drive, result should be left undefined.
419     SendResponse(true);
420   }
421 }
422
423 void FileBrowserPrivateGetSizeStatsFunction::GetSizeStatsCallback(
424     const uint64* total_size,
425     const uint64* remaining_size) {
426   base::DictionaryValue* sizes = new base::DictionaryValue();
427   SetResult(sizes);
428
429   sizes->SetDouble("totalSize", static_cast<double>(*total_size));
430   sizes->SetDouble("remainingSize", static_cast<double>(*remaining_size));
431
432   SendResponse(true);
433 }
434
435 bool FileBrowserPrivateValidatePathNameLengthFunction::RunImpl() {
436   using extensions::api::file_browser_private::ValidatePathNameLength::Params;
437   const scoped_ptr<Params> params(Params::Create(*args_));
438   EXTENSION_FUNCTION_VALIDATE(params);
439
440   scoped_refptr<fileapi::FileSystemContext> file_system_context =
441       file_manager::util::GetFileSystemContextForRenderViewHost(
442           GetProfile(), render_view_host());
443
444   fileapi::FileSystemURL filesystem_url(
445       file_system_context->CrackURL(GURL(params->parent_directory_url)));
446   if (!chromeos::FileSystemBackend::CanHandleURL(filesystem_url))
447     return false;
448
449   // No explicit limit on the length of Drive file names.
450   if (filesystem_url.type() == fileapi::kFileSystemTypeDrive) {
451     SetResult(new base::FundamentalValue(true));
452     SendResponse(true);
453     return true;
454   }
455
456   base::PostTaskAndReplyWithResult(
457       BrowserThread::GetBlockingPool(),
458       FROM_HERE,
459       base::Bind(&GetFileNameMaxLengthOnBlockingPool,
460                  filesystem_url.path().AsUTF8Unsafe()),
461       base::Bind(&FileBrowserPrivateValidatePathNameLengthFunction::
462                      OnFilePathLimitRetrieved,
463                  this, params->name.size()));
464   return true;
465 }
466
467 void FileBrowserPrivateValidatePathNameLengthFunction::OnFilePathLimitRetrieved(
468     size_t current_length,
469     size_t max_length) {
470   SetResult(new base::FundamentalValue(current_length <= max_length));
471   SendResponse(true);
472 }
473
474 bool FileBrowserPrivateFormatVolumeFunction::RunImpl() {
475   using extensions::api::file_browser_private::FormatVolume::Params;
476   const scoped_ptr<Params> params(Params::Create(*args_));
477   EXTENSION_FUNCTION_VALIDATE(params);
478
479   using file_manager::VolumeManager;
480   using file_manager::VolumeInfo;
481   VolumeManager* volume_manager = VolumeManager::Get(GetProfile());
482   if (!volume_manager)
483     return false;
484
485   VolumeInfo volume_info;
486   if (!volume_manager->FindVolumeInfoById(params->volume_id, &volume_info))
487     return false;
488
489   DiskMountManager::GetInstance()->FormatMountedDevice(
490       volume_info.mount_path.AsUTF8Unsafe());
491   SendResponse(true);
492   return true;
493 }
494
495 bool FileBrowserPrivateStartCopyFunction::RunImpl() {
496   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
497
498   using  extensions::api::file_browser_private::StartCopy::Params;
499   const scoped_ptr<Params> params(Params::Create(*args_));
500   EXTENSION_FUNCTION_VALIDATE(params);
501
502   if (params->source_url.empty() || params->parent.empty() ||
503       params->new_name.empty()) {
504     // Error code in format of DOMError.name.
505     SetError("EncodingError");
506     return false;
507   }
508
509   scoped_refptr<fileapi::FileSystemContext> file_system_context =
510       file_manager::util::GetFileSystemContextForRenderViewHost(
511           GetProfile(), render_view_host());
512
513   fileapi::FileSystemURL source_url(
514       file_system_context->CrackURL(GURL(params->source_url)));
515   fileapi::FileSystemURL destination_url(file_system_context->CrackURL(
516       GURL(params->parent + "/" + net::EscapePath(params->new_name))));
517
518   if (!source_url.is_valid() || !destination_url.is_valid()) {
519     // Error code in format of DOMError.name.
520     SetError("EncodingError");
521     return false;
522   }
523
524   return BrowserThread::PostTaskAndReplyWithResult(
525       BrowserThread::IO,
526       FROM_HERE,
527       base::Bind(&StartCopyOnIOThread,
528                  GetProfile(),
529                  file_system_context,
530                  source_url,
531                  destination_url),
532       base::Bind(&FileBrowserPrivateStartCopyFunction::RunAfterStartCopy,
533                  this));
534 }
535
536 void FileBrowserPrivateStartCopyFunction::RunAfterStartCopy(
537     int operation_id) {
538   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
539
540   SetResult(base::Value::CreateIntegerValue(operation_id));
541   SendResponse(true);
542 }
543
544 bool FileBrowserPrivateCancelCopyFunction::RunImpl() {
545   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
546
547   using  extensions::api::file_browser_private::CancelCopy::Params;
548   const scoped_ptr<Params> params(Params::Create(*args_));
549   EXTENSION_FUNCTION_VALIDATE(params);
550
551   scoped_refptr<fileapi::FileSystemContext> file_system_context =
552       file_manager::util::GetFileSystemContextForRenderViewHost(
553           GetProfile(), render_view_host());
554
555   // We don't much take care about the result of cancellation.
556   BrowserThread::PostTask(
557       BrowserThread::IO,
558       FROM_HERE,
559       base::Bind(&CancelCopyOnIOThread, file_system_context, params->copy_id));
560   SendResponse(true);
561   return true;
562 }
563
564 }  // namespace extensions