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/event_router_factory.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_manager_private.h"
27#include "chrome/common/extensions/api/file_manager_private_internal.h"
28#include "chromeos/disks/disk_mount_manager.h"
29#include "content/public/browser/child_process_security_policy.h"
30#include "content/public/browser/render_process_host.h"
31#include "content/public/browser/render_view_host.h"
32#include "net/base/escape.h"
33#include "storage/browser/fileapi/file_system_context.h"
34#include "storage/browser/fileapi/file_system_file_util.h"
35#include "storage/browser/fileapi/file_system_operation_context.h"
36#include "storage/browser/fileapi/file_system_operation_runner.h"
37#include "storage/browser/fileapi/file_system_url.h"
38#include "storage/common/fileapi/file_system_info.h"
39#include "storage/common/fileapi/file_system_types.h"
40#include "storage/common/fileapi/file_system_util.h"
41
42using chromeos::disks::DiskMountManager;
43using content::BrowserThread;
44using content::ChildProcessSecurityPolicy;
45using file_manager::util::EntryDefinition;
46using file_manager::util::FileDefinition;
47using storage::FileSystemURL;
48
49namespace extensions {
50namespace {
51
52// Retrieves total and remaining available size on |mount_path|.
53void GetSizeStatsOnBlockingPool(const std::string& mount_path,
54                                uint64* total_size,
55                                uint64* remaining_size) {
56  struct statvfs stat = {};  // Zero-clear
57  if (HANDLE_EINTR(statvfs(mount_path.c_str(), &stat)) == 0) {
58    *total_size = static_cast<uint64>(stat.f_blocks) * stat.f_frsize;
59    *remaining_size = static_cast<uint64>(stat.f_bavail) * stat.f_frsize;
60  }
61}
62
63// Retrieves the maximum file name length of the file system of |path|.
64// Returns 0 if it could not be queried.
65size_t GetFileNameMaxLengthOnBlockingPool(const std::string& path) {
66  struct statvfs stat = {};
67  if (HANDLE_EINTR(statvfs(path.c_str(), &stat)) != 0) {
68    // The filesystem seems not supporting statvfs(). Assume it to be a commonly
69    // used bound 255, and log the failure.
70    LOG(ERROR) << "Cannot statvfs() the name length limit for: " << path;
71    return 255;
72  }
73  return stat.f_namemax;
74}
75
76// Returns EventRouter for the |profile_id| if available.
77file_manager::EventRouter* GetEventRouterByProfileId(void* profile_id) {
78  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
79
80  // |profile_id| needs to be checked with ProfileManager::IsValidProfile
81  // before using it.
82  Profile* profile = reinterpret_cast<Profile*>(profile_id);
83  if (!g_browser_process->profile_manager()->IsValidProfile(profile))
84    return NULL;
85
86  return file_manager::EventRouterFactory::GetForProfile(profile);
87}
88
89// Notifies the copy progress to extensions via event router.
90void NotifyCopyProgress(
91    void* profile_id,
92    storage::FileSystemOperationRunner::OperationID operation_id,
93    storage::FileSystemOperation::CopyProgressType type,
94    const FileSystemURL& source_url,
95    const FileSystemURL& destination_url,
96    int64 size) {
97  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
98
99  file_manager::EventRouter* event_router =
100      GetEventRouterByProfileId(profile_id);
101  if (event_router) {
102    event_router->OnCopyProgress(
103        operation_id, type,
104        source_url.ToGURL(), destination_url.ToGURL(), size);
105  }
106}
107
108// Callback invoked periodically on progress update of Copy().
109void OnCopyProgress(
110    void* profile_id,
111    storage::FileSystemOperationRunner::OperationID* operation_id,
112    storage::FileSystemOperation::CopyProgressType type,
113    const FileSystemURL& source_url,
114    const FileSystemURL& destination_url,
115    int64 size) {
116  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
117
118  BrowserThread::PostTask(
119      BrowserThread::UI, FROM_HERE,
120      base::Bind(&NotifyCopyProgress,
121                 profile_id, *operation_id, type,
122                 source_url, destination_url, size));
123}
124
125// Notifies the copy completion to extensions via event router.
126void NotifyCopyCompletion(
127    void* profile_id,
128    storage::FileSystemOperationRunner::OperationID operation_id,
129    const FileSystemURL& source_url,
130    const FileSystemURL& destination_url,
131    base::File::Error error) {
132  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
133
134  file_manager::EventRouter* event_router =
135      GetEventRouterByProfileId(profile_id);
136  if (event_router)
137    event_router->OnCopyCompleted(
138        operation_id,
139        source_url.ToGURL(), destination_url.ToGURL(), error);
140}
141
142// Callback invoked upon completion of Copy() (regardless of succeeded or
143// failed).
144void OnCopyCompleted(
145    void* profile_id,
146    storage::FileSystemOperationRunner::OperationID* operation_id,
147    const FileSystemURL& source_url,
148    const FileSystemURL& destination_url,
149    base::File::Error error) {
150  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
151
152  BrowserThread::PostTask(
153      BrowserThread::UI, FROM_HERE,
154      base::Bind(&NotifyCopyCompletion,
155                 profile_id, *operation_id,
156                 source_url, destination_url, error));
157}
158
159// Starts the copy operation via FileSystemOperationRunner.
160storage::FileSystemOperationRunner::OperationID StartCopyOnIOThread(
161    void* profile_id,
162    scoped_refptr<storage::FileSystemContext> file_system_context,
163    const FileSystemURL& source_url,
164    const FileSystemURL& destination_url) {
165  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
166
167  // Note: |operation_id| is owned by the callback for
168  // FileSystemOperationRunner::Copy(). It is always called in the next message
169  // loop or later, so at least during this invocation it should alive.
170  storage::FileSystemOperationRunner::OperationID* operation_id =
171      new storage::FileSystemOperationRunner::OperationID;
172  *operation_id = file_system_context->operation_runner()->Copy(
173      source_url,
174      destination_url,
175      storage::FileSystemOperation::OPTION_PRESERVE_LAST_MODIFIED,
176      base::Bind(&OnCopyProgress, profile_id, base::Unretained(operation_id)),
177      base::Bind(&OnCopyCompleted,
178                 profile_id,
179                 base::Owned(operation_id),
180                 source_url,
181                 destination_url));
182  return *operation_id;
183}
184
185void OnCopyCancelled(base::File::Error error) {
186  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
187
188  // We just ignore the status if the copy is actually cancelled or not,
189  // because failing cancellation means the operation is not running now.
190  DLOG_IF(WARNING, error != base::File::FILE_OK)
191      << "Failed to cancel copy: " << error;
192}
193
194// Cancels the running copy operation identified by |operation_id|.
195void CancelCopyOnIOThread(
196    scoped_refptr<storage::FileSystemContext> file_system_context,
197    storage::FileSystemOperationRunner::OperationID operation_id) {
198  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
199
200  file_system_context->operation_runner()->Cancel(
201      operation_id, base::Bind(&OnCopyCancelled));
202}
203
204}  // namespace
205
206void FileManagerPrivateRequestFileSystemFunction::DidFail(
207    base::File::Error error_code) {
208  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
209
210  SetError(base::StringPrintf("File error %d", static_cast<int>(error_code)));
211  SendResponse(false);
212}
213
214bool
215FileManagerPrivateRequestFileSystemFunction::SetupFileSystemAccessPermissions(
216    scoped_refptr<storage::FileSystemContext> file_system_context,
217    int child_id,
218    Profile* profile,
219    scoped_refptr<const extensions::Extension> extension) {
220  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
221
222  if (!extension.get())
223    return false;
224
225  // Make sure that only component extension can access the entire
226  // local file system.
227  if (extension_->location() != extensions::Manifest::COMPONENT) {
228    NOTREACHED() << "Private method access by non-component extension "
229                 << extension->id();
230    return false;
231  }
232
233  storage::ExternalFileSystemBackend* backend =
234      file_system_context->external_backend();
235  if (!backend)
236    return false;
237
238  // Grant full access to File API from this component extension.
239  backend->GrantFullAccessToExtension(extension_->id());
240
241  // Grant R/W file permissions to the renderer hosting component
242  // extension for all paths exposed by our local file system backend.
243  std::vector<base::FilePath> root_dirs = backend->GetRootDirectories();
244  for (size_t i = 0; i < root_dirs.size(); ++i) {
245    ChildProcessSecurityPolicy::GetInstance()->GrantCreateReadWriteFile(
246        child_id, root_dirs[i]);
247  }
248
249  // Grant R/W permissions to profile-specific directories (Drive, Downloads)
250  // from other profiles. Those directories may not be mounted at this moment
251  // yet, so we need to do this separately from the above loop over
252  // GetRootDirectories().
253  const std::vector<Profile*>& profiles =
254      g_browser_process->profile_manager()->GetLoadedProfiles();
255  for (size_t i = 0; i < profiles.size(); ++i) {
256    if (!profiles[i]->IsOffTheRecord()) {
257      file_manager::util::SetupProfileFileAccessPermissions(child_id,
258                                                            profiles[i]);
259    }
260  }
261
262  return true;
263}
264
265bool FileManagerPrivateRequestFileSystemFunction::RunAsync() {
266  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
267  using extensions::api::file_manager_private::RequestFileSystem::Params;
268  const scoped_ptr<Params> params(Params::Create(*args_));
269  EXTENSION_FUNCTION_VALIDATE(params);
270
271  if (!dispatcher() || !render_view_host() || !render_view_host()->GetProcess())
272    return false;
273
274  set_log_on_completion(true);
275
276  using file_manager::VolumeManager;
277  using file_manager::VolumeInfo;
278  VolumeManager* volume_manager = VolumeManager::Get(GetProfile());
279  if (!volume_manager)
280    return false;
281
282  VolumeInfo volume_info;
283  if (!volume_manager->FindVolumeInfoById(params->volume_id, &volume_info)) {
284    DidFail(base::File::FILE_ERROR_NOT_FOUND);
285    return false;
286  }
287
288  scoped_refptr<storage::FileSystemContext> file_system_context =
289      file_manager::util::GetFileSystemContextForRenderViewHost(
290          GetProfile(), render_view_host());
291
292  // Set up file permission access.
293  const int child_id = render_view_host()->GetProcess()->GetID();
294  if (!SetupFileSystemAccessPermissions(
295          file_system_context, child_id, GetProfile(), extension())) {
296    DidFail(base::File::FILE_ERROR_SECURITY);
297    return false;
298  }
299
300  FileDefinition file_definition;
301  if (!file_manager::util::ConvertAbsoluteFilePathToRelativeFileSystemPath(
302           GetProfile(),
303           extension_id(),
304           volume_info.mount_path,
305           &file_definition.virtual_path)) {
306    DidFail(base::File::FILE_ERROR_INVALID_OPERATION);
307    return false;
308  }
309  file_definition.is_directory = true;
310
311  file_manager::util::ConvertFileDefinitionToEntryDefinition(
312      GetProfile(),
313      extension_id(),
314      file_definition,
315      base::Bind(
316          &FileManagerPrivateRequestFileSystemFunction::OnEntryDefinition,
317          this));
318  return true;
319}
320
321void FileManagerPrivateRequestFileSystemFunction::OnEntryDefinition(
322    const EntryDefinition& entry_definition) {
323  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
324
325  if (entry_definition.error != base::File::FILE_OK) {
326    DidFail(entry_definition.error);
327    return;
328  }
329
330  if (!entry_definition.is_directory) {
331    DidFail(base::File::FILE_ERROR_NOT_A_DIRECTORY);
332    return;
333  }
334
335  base::DictionaryValue* dict = new base::DictionaryValue();
336  SetResult(dict);
337  dict->SetString("name", entry_definition.file_system_name);
338  dict->SetString("root_url", entry_definition.file_system_root_url);
339  dict->SetInteger("error", drive::FILE_ERROR_OK);
340  SendResponse(true);
341}
342
343void FileWatchFunctionBase::Respond(bool success) {
344  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
345
346  SetResult(new base::FundamentalValue(success));
347  SendResponse(success);
348}
349
350bool FileWatchFunctionBase::RunAsync() {
351  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
352
353  if (!render_view_host() || !render_view_host()->GetProcess())
354    return false;
355
356  // First param is url of a file to watch.
357  std::string url;
358  if (!args_->GetString(0, &url) || url.empty())
359    return false;
360
361  scoped_refptr<storage::FileSystemContext> file_system_context =
362      file_manager::util::GetFileSystemContextForRenderViewHost(
363          GetProfile(), render_view_host());
364
365  FileSystemURL file_watch_url = file_system_context->CrackURL(GURL(url));
366  base::FilePath local_path = file_watch_url.path();
367  base::FilePath virtual_path = file_watch_url.virtual_path();
368  if (local_path.empty()) {
369    Respond(false);
370    return true;
371  }
372  PerformFileWatchOperation(local_path, virtual_path, extension_id());
373
374  return true;
375}
376
377void FileManagerPrivateAddFileWatchFunction::PerformFileWatchOperation(
378    const base::FilePath& local_path,
379    const base::FilePath& virtual_path,
380    const std::string& extension_id) {
381  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
382
383  file_manager::EventRouter* event_router =
384      file_manager::EventRouterFactory::GetForProfile(GetProfile());
385  event_router->AddFileWatch(
386      local_path,
387      virtual_path,
388      extension_id,
389      base::Bind(&FileManagerPrivateAddFileWatchFunction::Respond, this));
390}
391
392void FileManagerPrivateRemoveFileWatchFunction::PerformFileWatchOperation(
393    const base::FilePath& local_path,
394    const base::FilePath& unused,
395    const std::string& extension_id) {
396  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
397
398  file_manager::EventRouter* event_router =
399      file_manager::EventRouterFactory::GetForProfile(GetProfile());
400  event_router->RemoveFileWatch(local_path, extension_id);
401  Respond(true);
402}
403
404bool FileManagerPrivateGetSizeStatsFunction::RunAsync() {
405  using extensions::api::file_manager_private::GetSizeStats::Params;
406  const scoped_ptr<Params> params(Params::Create(*args_));
407  EXTENSION_FUNCTION_VALIDATE(params);
408
409  using file_manager::VolumeManager;
410  using file_manager::VolumeInfo;
411  VolumeManager* volume_manager = VolumeManager::Get(GetProfile());
412  if (!volume_manager)
413    return false;
414
415  VolumeInfo volume_info;
416  if (!volume_manager->FindVolumeInfoById(params->volume_id, &volume_info))
417    return false;
418
419  if (volume_info.type == file_manager::VOLUME_TYPE_GOOGLE_DRIVE) {
420    drive::FileSystemInterface* file_system =
421        drive::util::GetFileSystemByProfile(GetProfile());
422    if (!file_system) {
423      // |file_system| is NULL if Drive is disabled.
424      // If stats couldn't be gotten for drive, result should be left
425      // undefined. See comments in GetDriveAvailableSpaceCallback().
426      SendResponse(true);
427      return true;
428    }
429
430    file_system->GetAvailableSpace(
431        base::Bind(&FileManagerPrivateGetSizeStatsFunction::
432                       GetDriveAvailableSpaceCallback,
433                   this));
434  } else {
435    uint64* total_size = new uint64(0);
436    uint64* remaining_size = new uint64(0);
437    BrowserThread::PostBlockingPoolTaskAndReply(
438        FROM_HERE,
439        base::Bind(&GetSizeStatsOnBlockingPool,
440                   volume_info.mount_path.value(),
441                   total_size,
442                   remaining_size),
443        base::Bind(&FileManagerPrivateGetSizeStatsFunction::
444                       GetSizeStatsCallback,
445                   this,
446                   base::Owned(total_size),
447                   base::Owned(remaining_size)));
448  }
449  return true;
450}
451
452void FileManagerPrivateGetSizeStatsFunction::GetDriveAvailableSpaceCallback(
453    drive::FileError error,
454    int64 bytes_total,
455    int64 bytes_used) {
456  if (error == drive::FILE_ERROR_OK) {
457    const uint64 bytes_total_unsigned = bytes_total;
458    const uint64 bytes_remaining_unsigned = bytes_total - bytes_used;
459    GetSizeStatsCallback(&bytes_total_unsigned,
460                         &bytes_remaining_unsigned);
461  } else {
462    // If stats couldn't be gotten for drive, result should be left undefined.
463    SendResponse(true);
464  }
465}
466
467void FileManagerPrivateGetSizeStatsFunction::GetSizeStatsCallback(
468    const uint64* total_size,
469    const uint64* remaining_size) {
470  base::DictionaryValue* sizes = new base::DictionaryValue();
471  SetResult(sizes);
472
473  sizes->SetDouble("totalSize", static_cast<double>(*total_size));
474  sizes->SetDouble("remainingSize", static_cast<double>(*remaining_size));
475
476  SendResponse(true);
477}
478
479bool FileManagerPrivateValidatePathNameLengthFunction::RunAsync() {
480  using extensions::api::file_manager_private::ValidatePathNameLength::Params;
481  const scoped_ptr<Params> params(Params::Create(*args_));
482  EXTENSION_FUNCTION_VALIDATE(params);
483
484  scoped_refptr<storage::FileSystemContext> file_system_context =
485      file_manager::util::GetFileSystemContextForRenderViewHost(
486          GetProfile(), render_view_host());
487
488  storage::FileSystemURL filesystem_url(
489      file_system_context->CrackURL(GURL(params->parent_directory_url)));
490  if (!chromeos::FileSystemBackend::CanHandleURL(filesystem_url))
491    return false;
492
493  // No explicit limit on the length of Drive file names.
494  if (filesystem_url.type() == storage::kFileSystemTypeDrive) {
495    SetResult(new base::FundamentalValue(true));
496    SendResponse(true);
497    return true;
498  }
499
500  base::PostTaskAndReplyWithResult(
501      BrowserThread::GetBlockingPool(),
502      FROM_HERE,
503      base::Bind(&GetFileNameMaxLengthOnBlockingPool,
504                 filesystem_url.path().AsUTF8Unsafe()),
505      base::Bind(&FileManagerPrivateValidatePathNameLengthFunction::
506                     OnFilePathLimitRetrieved,
507                 this, params->name.size()));
508  return true;
509}
510
511void FileManagerPrivateValidatePathNameLengthFunction::OnFilePathLimitRetrieved(
512    size_t current_length,
513    size_t max_length) {
514  SetResult(new base::FundamentalValue(current_length <= max_length));
515  SendResponse(true);
516}
517
518bool FileManagerPrivateFormatVolumeFunction::RunAsync() {
519  using extensions::api::file_manager_private::FormatVolume::Params;
520  const scoped_ptr<Params> params(Params::Create(*args_));
521  EXTENSION_FUNCTION_VALIDATE(params);
522
523  using file_manager::VolumeManager;
524  using file_manager::VolumeInfo;
525  VolumeManager* volume_manager = VolumeManager::Get(GetProfile());
526  if (!volume_manager)
527    return false;
528
529  VolumeInfo volume_info;
530  if (!volume_manager->FindVolumeInfoById(params->volume_id, &volume_info))
531    return false;
532
533  DiskMountManager::GetInstance()->FormatMountedDevice(
534      volume_info.mount_path.AsUTF8Unsafe());
535  SendResponse(true);
536  return true;
537}
538
539bool FileManagerPrivateStartCopyFunction::RunAsync() {
540  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
541
542  using  extensions::api::file_manager_private::StartCopy::Params;
543  const scoped_ptr<Params> params(Params::Create(*args_));
544  EXTENSION_FUNCTION_VALIDATE(params);
545
546  if (params->source_url.empty() || params->parent.empty() ||
547      params->new_name.empty()) {
548    // Error code in format of DOMError.name.
549    SetError("EncodingError");
550    return false;
551  }
552
553  scoped_refptr<storage::FileSystemContext> file_system_context =
554      file_manager::util::GetFileSystemContextForRenderViewHost(
555          GetProfile(), render_view_host());
556
557  // |parent| may have a trailing slash if it is a root directory.
558  std::string destination_url_string = params->parent;
559  if (destination_url_string[destination_url_string.size() - 1] != '/')
560    destination_url_string += '/';
561  destination_url_string += net::EscapePath(params->new_name);
562
563  storage::FileSystemURL source_url(
564      file_system_context->CrackURL(GURL(params->source_url)));
565  storage::FileSystemURL destination_url(
566      file_system_context->CrackURL(GURL(destination_url_string)));
567
568  if (!source_url.is_valid() || !destination_url.is_valid()) {
569    // Error code in format of DOMError.name.
570    SetError("EncodingError");
571    return false;
572  }
573
574  return BrowserThread::PostTaskAndReplyWithResult(
575      BrowserThread::IO,
576      FROM_HERE,
577      base::Bind(&StartCopyOnIOThread,
578                 GetProfile(),
579                 file_system_context,
580                 source_url,
581                 destination_url),
582      base::Bind(&FileManagerPrivateStartCopyFunction::RunAfterStartCopy,
583                 this));
584}
585
586void FileManagerPrivateStartCopyFunction::RunAfterStartCopy(
587    int operation_id) {
588  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
589
590  SetResult(new base::FundamentalValue(operation_id));
591  SendResponse(true);
592}
593
594bool FileManagerPrivateCancelCopyFunction::RunAsync() {
595  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
596
597  using extensions::api::file_manager_private::CancelCopy::Params;
598  const scoped_ptr<Params> params(Params::Create(*args_));
599  EXTENSION_FUNCTION_VALIDATE(params);
600
601  scoped_refptr<storage::FileSystemContext> file_system_context =
602      file_manager::util::GetFileSystemContextForRenderViewHost(
603          GetProfile(), render_view_host());
604
605  // We don't much take care about the result of cancellation.
606  BrowserThread::PostTask(
607      BrowserThread::IO,
608      FROM_HERE,
609      base::Bind(&CancelCopyOnIOThread, file_system_context, params->copy_id));
610  SendResponse(true);
611  return true;
612}
613
614bool FileManagerPrivateInternalResolveIsolatedEntriesFunction::RunAsync() {
615  using extensions::api::file_manager_private_internal::ResolveIsolatedEntries::
616      Params;
617  const scoped_ptr<Params> params(Params::Create(*args_));
618  EXTENSION_FUNCTION_VALIDATE(params);
619
620  scoped_refptr<storage::FileSystemContext> file_system_context =
621      file_manager::util::GetFileSystemContextForRenderViewHost(
622          GetProfile(), render_view_host());
623  DCHECK(file_system_context.get());
624
625  const storage::ExternalFileSystemBackend* external_backend =
626      file_system_context->external_backend();
627  DCHECK(external_backend);
628
629  file_manager::util::FileDefinitionList file_definition_list;
630  for (size_t i = 0; i < params->urls.size(); ++i) {
631    FileSystemURL fileSystemUrl =
632        file_system_context->CrackURL(GURL(params->urls[i]));
633    DCHECK(external_backend->CanHandleType(fileSystemUrl.type()));
634
635    FileDefinition file_definition;
636    const bool result =
637        file_manager::util::ConvertAbsoluteFilePathToRelativeFileSystemPath(
638            GetProfile(),
639            extension_->id(),
640            fileSystemUrl.path(),
641            &file_definition.virtual_path);
642    if (!result)
643      continue;
644    // The API only supports isolated files.
645    file_definition.is_directory = false;
646    file_definition_list.push_back(file_definition);
647  }
648
649  file_manager::util::ConvertFileDefinitionListToEntryDefinitionList(
650      GetProfile(),
651      extension_->id(),
652      file_definition_list,  // Safe, since copied internally.
653      base::Bind(
654          &FileManagerPrivateInternalResolveIsolatedEntriesFunction::
655              RunAsyncAfterConvertFileDefinitionListToEntryDefinitionList,
656          this));
657  return true;
658}
659
660void FileManagerPrivateInternalResolveIsolatedEntriesFunction::
661    RunAsyncAfterConvertFileDefinitionListToEntryDefinitionList(scoped_ptr<
662        file_manager::util::EntryDefinitionList> entry_definition_list) {
663  using extensions::api::file_manager_private_internal::EntryDescription;
664  std::vector<linked_ptr<EntryDescription> > entries;
665
666  for (size_t i = 0; i < entry_definition_list->size(); ++i) {
667    if (entry_definition_list->at(i).error != base::File::FILE_OK)
668      continue;
669    linked_ptr<EntryDescription> entry(new EntryDescription);
670    entry->file_system_name = entry_definition_list->at(i).file_system_name;
671    entry->file_system_root = entry_definition_list->at(i).file_system_root_url;
672    entry->file_full_path =
673        "/" + entry_definition_list->at(i).full_path.AsUTF8Unsafe();
674    entry->file_is_directory = entry_definition_list->at(i).is_directory;
675    entries.push_back(entry);
676  }
677
678  results_ = extensions::api::file_manager_private_internal::
679      ResolveIsolatedEntries::Results::Create(entries);
680  SendResponse(true);
681}
682}  // namespace extensions
683