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