Home | History | Annotate | Download | only in fileapi
      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 "webkit/browser/fileapi/file_system_context.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/single_thread_task_runner.h"
      9 #include "base/stl_util.h"
     10 #include "base/task_runner_util.h"
     11 #include "net/url_request/url_request.h"
     12 #include "url/gurl.h"
     13 #include "webkit/browser/blob/file_stream_reader.h"
     14 #include "webkit/browser/fileapi/copy_or_move_file_validator.h"
     15 #include "webkit/browser/fileapi/external_mount_points.h"
     16 #include "webkit/browser/fileapi/file_permission_policy.h"
     17 #include "webkit/browser/fileapi/file_stream_writer.h"
     18 #include "webkit/browser/fileapi/file_system_file_util.h"
     19 #include "webkit/browser/fileapi/file_system_operation.h"
     20 #include "webkit/browser/fileapi/file_system_operation_runner.h"
     21 #include "webkit/browser/fileapi/file_system_options.h"
     22 #include "webkit/browser/fileapi/file_system_quota_client.h"
     23 #include "webkit/browser/fileapi/file_system_url.h"
     24 #include "webkit/browser/fileapi/isolated_context.h"
     25 #include "webkit/browser/fileapi/isolated_file_system_backend.h"
     26 #include "webkit/browser/fileapi/mount_points.h"
     27 #include "webkit/browser/fileapi/quota/quota_reservation.h"
     28 #include "webkit/browser/fileapi/sandbox_file_system_backend.h"
     29 #include "webkit/browser/quota/quota_manager_proxy.h"
     30 #include "webkit/browser/quota/special_storage_policy.h"
     31 #include "webkit/common/fileapi/file_system_info.h"
     32 #include "webkit/common/fileapi/file_system_util.h"
     33 
     34 using quota::QuotaClient;
     35 
     36 namespace fileapi {
     37 
     38 namespace {
     39 
     40 QuotaClient* CreateQuotaClient(
     41     FileSystemContext* context,
     42     bool is_incognito) {
     43   return new FileSystemQuotaClient(context, is_incognito);
     44 }
     45 
     46 
     47 void DidGetMetadataForResolveURL(
     48     const base::FilePath& path,
     49     const FileSystemContext::ResolveURLCallback& callback,
     50     const FileSystemInfo& info,
     51     base::File::Error error,
     52     const base::File::Info& file_info) {
     53   if (error != base::File::FILE_OK) {
     54     if (error == base::File::FILE_ERROR_NOT_FOUND) {
     55       callback.Run(base::File::FILE_OK, info, path,
     56                    FileSystemContext::RESOLVED_ENTRY_NOT_FOUND);
     57     } else {
     58       callback.Run(error, FileSystemInfo(), base::FilePath(),
     59                    FileSystemContext::RESOLVED_ENTRY_NOT_FOUND);
     60     }
     61     return;
     62   }
     63   callback.Run(error, info, path, file_info.is_directory ?
     64       FileSystemContext::RESOLVED_ENTRY_DIRECTORY :
     65       FileSystemContext::RESOLVED_ENTRY_FILE);
     66 }
     67 
     68 void RelayResolveURLCallback(
     69     scoped_refptr<base::MessageLoopProxy> message_loop,
     70     const FileSystemContext::ResolveURLCallback& callback,
     71     base::File::Error result,
     72     const FileSystemInfo& info,
     73     const base::FilePath& file_path,
     74     FileSystemContext::ResolvedEntryType type) {
     75   message_loop->PostTask(
     76       FROM_HERE, base::Bind(callback, result, info, file_path, type));
     77 }
     78 
     79 }  // namespace
     80 
     81 // static
     82 int FileSystemContext::GetPermissionPolicy(FileSystemType type) {
     83   switch (type) {
     84     case kFileSystemTypeTemporary:
     85     case kFileSystemTypePersistent:
     86     case kFileSystemTypeSyncable:
     87       return FILE_PERMISSION_SANDBOX;
     88 
     89     case kFileSystemTypeDrive:
     90     case kFileSystemTypeNativeForPlatformApp:
     91     case kFileSystemTypeNativeLocal:
     92     case kFileSystemTypeCloudDevice:
     93     case kFileSystemTypeProvided:
     94     case kFileSystemTypeDeviceMediaAsFileStorage:
     95       return FILE_PERMISSION_USE_FILE_PERMISSION;
     96 
     97     case kFileSystemTypeRestrictedNativeLocal:
     98       return FILE_PERMISSION_READ_ONLY |
     99              FILE_PERMISSION_USE_FILE_PERMISSION;
    100 
    101     case kFileSystemTypeDeviceMedia:
    102     case kFileSystemTypeIphoto:
    103     case kFileSystemTypeItunes:
    104     case kFileSystemTypeNativeMedia:
    105     case kFileSystemTypePicasa:
    106       return FILE_PERMISSION_USE_FILE_PERMISSION;
    107 
    108     // Following types are only accessed via IsolatedFileSystem, and
    109     // don't have their own permission policies.
    110     case kFileSystemTypeDragged:
    111     case kFileSystemTypeForTransientFile:
    112     case kFileSystemTypePluginPrivate:
    113       return FILE_PERMISSION_ALWAYS_DENY;
    114 
    115     // Following types only appear as mount_type, and will not be
    116     // queried for their permission policies.
    117     case kFileSystemTypeIsolated:
    118     case kFileSystemTypeExternal:
    119       return FILE_PERMISSION_ALWAYS_DENY;
    120 
    121     // Following types should not be used to access files by FileAPI clients.
    122     case kFileSystemTypeTest:
    123     case kFileSystemTypeSyncableForInternalSync:
    124     case kFileSystemInternalTypeEnumEnd:
    125     case kFileSystemInternalTypeEnumStart:
    126     case kFileSystemTypeUnknown:
    127       return FILE_PERMISSION_ALWAYS_DENY;
    128   }
    129   NOTREACHED();
    130   return FILE_PERMISSION_ALWAYS_DENY;
    131 }
    132 
    133 FileSystemContext::FileSystemContext(
    134     base::SingleThreadTaskRunner* io_task_runner,
    135     base::SequencedTaskRunner* file_task_runner,
    136     ExternalMountPoints* external_mount_points,
    137     quota::SpecialStoragePolicy* special_storage_policy,
    138     quota::QuotaManagerProxy* quota_manager_proxy,
    139     ScopedVector<FileSystemBackend> additional_backends,
    140     const std::vector<URLRequestAutoMountHandler>& auto_mount_handlers,
    141     const base::FilePath& partition_path,
    142     const FileSystemOptions& options)
    143     : io_task_runner_(io_task_runner),
    144       default_file_task_runner_(file_task_runner),
    145       quota_manager_proxy_(quota_manager_proxy),
    146       sandbox_delegate_(new SandboxFileSystemBackendDelegate(
    147           quota_manager_proxy,
    148           file_task_runner,
    149           partition_path,
    150           special_storage_policy,
    151           options)),
    152       sandbox_backend_(new SandboxFileSystemBackend(
    153           sandbox_delegate_.get())),
    154       isolated_backend_(new IsolatedFileSystemBackend()),
    155       plugin_private_backend_(new PluginPrivateFileSystemBackend(
    156           file_task_runner,
    157           partition_path,
    158           special_storage_policy,
    159           options)),
    160       additional_backends_(additional_backends.Pass()),
    161       auto_mount_handlers_(auto_mount_handlers),
    162       external_mount_points_(external_mount_points),
    163       partition_path_(partition_path),
    164       is_incognito_(options.is_incognito()),
    165       operation_runner_(new FileSystemOperationRunner(this)) {
    166   RegisterBackend(sandbox_backend_.get());
    167   RegisterBackend(isolated_backend_.get());
    168   RegisterBackend(plugin_private_backend_.get());
    169 
    170   for (ScopedVector<FileSystemBackend>::const_iterator iter =
    171           additional_backends_.begin();
    172        iter != additional_backends_.end(); ++iter) {
    173     RegisterBackend(*iter);
    174   }
    175 
    176   if (quota_manager_proxy) {
    177     // Quota client assumes all backends have registered.
    178     quota_manager_proxy->RegisterClient(CreateQuotaClient(
    179             this, options.is_incognito()));
    180   }
    181 
    182   sandbox_backend_->Initialize(this);
    183   isolated_backend_->Initialize(this);
    184   plugin_private_backend_->Initialize(this);
    185   for (ScopedVector<FileSystemBackend>::const_iterator iter =
    186           additional_backends_.begin();
    187        iter != additional_backends_.end(); ++iter) {
    188     (*iter)->Initialize(this);
    189   }
    190 
    191   // Additional mount points must be added before regular system-wide
    192   // mount points.
    193   if (external_mount_points)
    194     url_crackers_.push_back(external_mount_points);
    195   url_crackers_.push_back(ExternalMountPoints::GetSystemInstance());
    196   url_crackers_.push_back(IsolatedContext::GetInstance());
    197 }
    198 
    199 bool FileSystemContext::DeleteDataForOriginOnFileTaskRunner(
    200     const GURL& origin_url) {
    201   DCHECK(default_file_task_runner()->RunsTasksOnCurrentThread());
    202   DCHECK(origin_url == origin_url.GetOrigin());
    203 
    204   bool success = true;
    205   for (FileSystemBackendMap::iterator iter = backend_map_.begin();
    206        iter != backend_map_.end();
    207        ++iter) {
    208     FileSystemBackend* backend = iter->second;
    209     if (!backend->GetQuotaUtil())
    210       continue;
    211     if (backend->GetQuotaUtil()->DeleteOriginDataOnFileTaskRunner(
    212             this, quota_manager_proxy(), origin_url, iter->first)
    213             != base::File::FILE_OK) {
    214       // Continue the loop, but record the failure.
    215       success = false;
    216     }
    217   }
    218 
    219   return success;
    220 }
    221 
    222 scoped_refptr<QuotaReservation>
    223 FileSystemContext::CreateQuotaReservationOnFileTaskRunner(
    224     const GURL& origin_url,
    225     FileSystemType type) {
    226   DCHECK(default_file_task_runner()->RunsTasksOnCurrentThread());
    227   FileSystemBackend* backend = GetFileSystemBackend(type);
    228   if (!backend || !backend->GetQuotaUtil())
    229     return scoped_refptr<QuotaReservation>();
    230   return backend->GetQuotaUtil()->CreateQuotaReservationOnFileTaskRunner(
    231       origin_url, type);
    232 }
    233 
    234 void FileSystemContext::Shutdown() {
    235   if (!io_task_runner_->RunsTasksOnCurrentThread()) {
    236     io_task_runner_->PostTask(
    237         FROM_HERE, base::Bind(&FileSystemContext::Shutdown,
    238                               make_scoped_refptr(this)));
    239     return;
    240   }
    241   operation_runner_->Shutdown();
    242 }
    243 
    244 FileSystemQuotaUtil*
    245 FileSystemContext::GetQuotaUtil(FileSystemType type) const {
    246   FileSystemBackend* backend = GetFileSystemBackend(type);
    247   if (!backend)
    248     return NULL;
    249   return backend->GetQuotaUtil();
    250 }
    251 
    252 AsyncFileUtil* FileSystemContext::GetAsyncFileUtil(
    253     FileSystemType type) const {
    254   FileSystemBackend* backend = GetFileSystemBackend(type);
    255   if (!backend)
    256     return NULL;
    257   return backend->GetAsyncFileUtil(type);
    258 }
    259 
    260 CopyOrMoveFileValidatorFactory*
    261 FileSystemContext::GetCopyOrMoveFileValidatorFactory(
    262     FileSystemType type, base::File::Error* error_code) const {
    263   DCHECK(error_code);
    264   *error_code = base::File::FILE_OK;
    265   FileSystemBackend* backend = GetFileSystemBackend(type);
    266   if (!backend)
    267     return NULL;
    268   return backend->GetCopyOrMoveFileValidatorFactory(
    269       type, error_code);
    270 }
    271 
    272 FileSystemBackend* FileSystemContext::GetFileSystemBackend(
    273     FileSystemType type) const {
    274   FileSystemBackendMap::const_iterator found = backend_map_.find(type);
    275   if (found != backend_map_.end())
    276     return found->second;
    277   NOTREACHED() << "Unknown filesystem type: " << type;
    278   return NULL;
    279 }
    280 
    281 bool FileSystemContext::IsSandboxFileSystem(FileSystemType type) const {
    282   FileSystemBackendMap::const_iterator found = backend_map_.find(type);
    283   return found != backend_map_.end() && found->second->GetQuotaUtil();
    284 }
    285 
    286 const UpdateObserverList* FileSystemContext::GetUpdateObservers(
    287     FileSystemType type) const {
    288   FileSystemBackend* backend = GetFileSystemBackend(type);
    289   if (backend->GetQuotaUtil())
    290     return backend->GetQuotaUtil()->GetUpdateObservers(type);
    291   return NULL;
    292 }
    293 
    294 const AccessObserverList* FileSystemContext::GetAccessObservers(
    295     FileSystemType type) const {
    296   FileSystemBackend* backend = GetFileSystemBackend(type);
    297   if (backend->GetQuotaUtil())
    298     return backend->GetQuotaUtil()->GetAccessObservers(type);
    299   return NULL;
    300 }
    301 
    302 void FileSystemContext::GetFileSystemTypes(
    303     std::vector<FileSystemType>* types) const {
    304   types->clear();
    305   for (FileSystemBackendMap::const_iterator iter = backend_map_.begin();
    306        iter != backend_map_.end(); ++iter)
    307     types->push_back(iter->first);
    308 }
    309 
    310 ExternalFileSystemBackend*
    311 FileSystemContext::external_backend() const {
    312   return static_cast<ExternalFileSystemBackend*>(
    313       GetFileSystemBackend(kFileSystemTypeExternal));
    314 }
    315 
    316 void FileSystemContext::OpenFileSystem(
    317     const GURL& origin_url,
    318     FileSystemType type,
    319     OpenFileSystemMode mode,
    320     const OpenFileSystemCallback& callback) {
    321   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
    322   DCHECK(!callback.is_null());
    323 
    324   if (!FileSystemContext::IsSandboxFileSystem(type)) {
    325     // Disallow opening a non-sandboxed filesystem.
    326     callback.Run(GURL(), std::string(), base::File::FILE_ERROR_SECURITY);
    327     return;
    328   }
    329 
    330   FileSystemBackend* backend = GetFileSystemBackend(type);
    331   if (!backend) {
    332     callback.Run(GURL(), std::string(), base::File::FILE_ERROR_SECURITY);
    333     return;
    334   }
    335 
    336   backend->ResolveURL(
    337       CreateCrackedFileSystemURL(origin_url, type, base::FilePath()),
    338       mode,
    339       callback);
    340 }
    341 
    342 void FileSystemContext::ResolveURL(
    343     const FileSystemURL& url,
    344     const ResolveURLCallback& callback) {
    345   DCHECK(!callback.is_null());
    346 
    347   // If not on IO thread, forward before passing the task to the backend.
    348   if (!io_task_runner_->RunsTasksOnCurrentThread()) {
    349     ResolveURLCallback relay_callback =
    350         base::Bind(&RelayResolveURLCallback,
    351                    base::MessageLoopProxy::current(), callback);
    352     io_task_runner_->PostTask(
    353         FROM_HERE,
    354         base::Bind(&FileSystemContext::ResolveURL, this, url, relay_callback));
    355     return;
    356   }
    357 
    358   FileSystemBackend* backend = GetFileSystemBackend(url.type());
    359   if (!backend) {
    360     callback.Run(base::File::FILE_ERROR_SECURITY,
    361                  FileSystemInfo(), base::FilePath(),
    362                  FileSystemContext::RESOLVED_ENTRY_NOT_FOUND);
    363     return;
    364   }
    365 
    366   backend->ResolveURL(
    367       url,
    368       OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT,
    369       base::Bind(&FileSystemContext::DidOpenFileSystemForResolveURL,
    370                  this,
    371                  url,
    372                  callback));
    373 }
    374 
    375 void FileSystemContext::AttemptAutoMountForURLRequest(
    376     const net::URLRequest* url_request,
    377     const std::string& storage_domain,
    378     const StatusCallback& callback) {
    379   FileSystemURL filesystem_url(url_request->url());
    380   if (filesystem_url.type() == kFileSystemTypeExternal) {
    381     for (size_t i = 0; i < auto_mount_handlers_.size(); i++) {
    382       if (auto_mount_handlers_[i].Run(url_request, filesystem_url,
    383                                       storage_domain, callback)) {
    384         return;
    385       }
    386     }
    387   }
    388   callback.Run(base::File::FILE_ERROR_NOT_FOUND);
    389 }
    390 
    391 void FileSystemContext::DeleteFileSystem(
    392     const GURL& origin_url,
    393     FileSystemType type,
    394     const StatusCallback& callback) {
    395   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
    396   DCHECK(origin_url == origin_url.GetOrigin());
    397   DCHECK(!callback.is_null());
    398 
    399   FileSystemBackend* backend = GetFileSystemBackend(type);
    400   if (!backend) {
    401     callback.Run(base::File::FILE_ERROR_SECURITY);
    402     return;
    403   }
    404   if (!backend->GetQuotaUtil()) {
    405     callback.Run(base::File::FILE_ERROR_INVALID_OPERATION);
    406     return;
    407   }
    408 
    409   base::PostTaskAndReplyWithResult(
    410       default_file_task_runner(),
    411       FROM_HERE,
    412       // It is safe to pass Unretained(quota_util) since context owns it.
    413       base::Bind(&FileSystemQuotaUtil::DeleteOriginDataOnFileTaskRunner,
    414                  base::Unretained(backend->GetQuotaUtil()),
    415                  make_scoped_refptr(this),
    416                  base::Unretained(quota_manager_proxy()),
    417                  origin_url,
    418                  type),
    419       callback);
    420 }
    421 
    422 scoped_ptr<webkit_blob::FileStreamReader>
    423 FileSystemContext::CreateFileStreamReader(
    424     const FileSystemURL& url,
    425     int64 offset,
    426     const base::Time& expected_modification_time) {
    427   if (!url.is_valid())
    428     return scoped_ptr<webkit_blob::FileStreamReader>();
    429   FileSystemBackend* backend = GetFileSystemBackend(url.type());
    430   if (!backend)
    431     return scoped_ptr<webkit_blob::FileStreamReader>();
    432   return backend->CreateFileStreamReader(
    433       url, offset, expected_modification_time, this);
    434 }
    435 
    436 scoped_ptr<FileStreamWriter> FileSystemContext::CreateFileStreamWriter(
    437     const FileSystemURL& url,
    438     int64 offset) {
    439   if (!url.is_valid())
    440     return scoped_ptr<FileStreamWriter>();
    441   FileSystemBackend* backend = GetFileSystemBackend(url.type());
    442   if (!backend)
    443     return scoped_ptr<FileStreamWriter>();
    444   return backend->CreateFileStreamWriter(url, offset, this);
    445 }
    446 
    447 scoped_ptr<FileSystemOperationRunner>
    448 FileSystemContext::CreateFileSystemOperationRunner() {
    449   return make_scoped_ptr(new FileSystemOperationRunner(this));
    450 }
    451 
    452 FileSystemURL FileSystemContext::CrackURL(const GURL& url) const {
    453   return CrackFileSystemURL(FileSystemURL(url));
    454 }
    455 
    456 FileSystemURL FileSystemContext::CreateCrackedFileSystemURL(
    457     const GURL& origin,
    458     FileSystemType type,
    459     const base::FilePath& path) const {
    460   return CrackFileSystemURL(FileSystemURL(origin, type, path));
    461 }
    462 
    463 #if defined(OS_CHROMEOS)
    464 void FileSystemContext::EnableTemporaryFileSystemInIncognito() {
    465   sandbox_backend_->set_enable_temporary_file_system_in_incognito(true);
    466 }
    467 #endif
    468 
    469 bool FileSystemContext::CanServeURLRequest(const FileSystemURL& url) const {
    470   // We never support accessing files in isolated filesystems via an URL.
    471   if (url.mount_type() == kFileSystemTypeIsolated)
    472     return false;
    473 #if defined(OS_CHROMEOS)
    474   if (url.type() == kFileSystemTypeTemporary &&
    475       sandbox_backend_->enable_temporary_file_system_in_incognito()) {
    476     return true;
    477   }
    478 #endif
    479   return !is_incognito_ || !FileSystemContext::IsSandboxFileSystem(url.type());
    480 }
    481 
    482 bool FileSystemContext::ShouldFlushOnWriteCompletion(
    483     FileSystemType type) const {
    484   if (IsSandboxFileSystem(type)) {
    485     // Disable Flush() for each write operation on SandboxFileSystems since it
    486     // hurts the performance, assuming the FileSystems are stored in a local
    487     // disk, we don't need to keep calling fsync() for it.
    488     // On the other hand, other FileSystems that may stored on a removable media
    489     // should be Flush()ed as soon as a write operation is completed, so that
    490     // written data is saved over sudden media removal.
    491     return false;
    492   }
    493   return true;
    494 }
    495 
    496 void FileSystemContext::OpenPluginPrivateFileSystem(
    497     const GURL& origin_url,
    498     FileSystemType type,
    499     const std::string& filesystem_id,
    500     const std::string& plugin_id,
    501     OpenFileSystemMode mode,
    502     const StatusCallback& callback) {
    503   DCHECK(plugin_private_backend_);
    504   plugin_private_backend_->OpenPrivateFileSystem(
    505       origin_url, type, filesystem_id, plugin_id, mode, callback);
    506 }
    507 
    508 FileSystemContext::~FileSystemContext() {
    509 }
    510 
    511 void FileSystemContext::DeleteOnCorrectThread() const {
    512   if (!io_task_runner_->RunsTasksOnCurrentThread() &&
    513       io_task_runner_->DeleteSoon(FROM_HERE, this)) {
    514     return;
    515   }
    516   delete this;
    517 }
    518 
    519 FileSystemOperation* FileSystemContext::CreateFileSystemOperation(
    520     const FileSystemURL& url, base::File::Error* error_code) {
    521   if (!url.is_valid()) {
    522     if (error_code)
    523       *error_code = base::File::FILE_ERROR_INVALID_URL;
    524     return NULL;
    525   }
    526 
    527   FileSystemBackend* backend = GetFileSystemBackend(url.type());
    528   if (!backend) {
    529     if (error_code)
    530       *error_code = base::File::FILE_ERROR_FAILED;
    531     return NULL;
    532   }
    533 
    534   base::File::Error fs_error = base::File::FILE_OK;
    535   FileSystemOperation* operation =
    536       backend->CreateFileSystemOperation(url, this, &fs_error);
    537 
    538   if (error_code)
    539     *error_code = fs_error;
    540   return operation;
    541 }
    542 
    543 FileSystemURL FileSystemContext::CrackFileSystemURL(
    544     const FileSystemURL& url) const {
    545   if (!url.is_valid())
    546     return FileSystemURL();
    547 
    548   // The returned value in case there is no crackers which can crack the url.
    549   // This is valid situation for non isolated/external file systems.
    550   FileSystemURL current = url;
    551 
    552   // File system may be mounted multiple times (e.g., an isolated filesystem on
    553   // top of an external filesystem). Hence cracking needs to be iterated.
    554   for (;;) {
    555     FileSystemURL cracked = current;
    556     for (size_t i = 0; i < url_crackers_.size(); ++i) {
    557       if (!url_crackers_[i]->HandlesFileSystemMountType(current.type()))
    558         continue;
    559       cracked = url_crackers_[i]->CrackFileSystemURL(current);
    560       if (cracked.is_valid())
    561         break;
    562     }
    563     if (cracked == current)
    564       break;
    565     current = cracked;
    566   }
    567   return current;
    568 }
    569 
    570 void FileSystemContext::RegisterBackend(FileSystemBackend* backend) {
    571   const FileSystemType mount_types[] = {
    572     kFileSystemTypeTemporary,
    573     kFileSystemTypePersistent,
    574     kFileSystemTypeIsolated,
    575     kFileSystemTypeExternal,
    576   };
    577   // Register file system backends for public mount types.
    578   for (size_t j = 0; j < ARRAYSIZE_UNSAFE(mount_types); ++j) {
    579     if (backend->CanHandleType(mount_types[j])) {
    580       const bool inserted = backend_map_.insert(
    581           std::make_pair(mount_types[j], backend)).second;
    582       DCHECK(inserted);
    583     }
    584   }
    585   // Register file system backends for internal types.
    586   for (int t = kFileSystemInternalTypeEnumStart + 1;
    587        t < kFileSystemInternalTypeEnumEnd; ++t) {
    588     FileSystemType type = static_cast<FileSystemType>(t);
    589     if (backend->CanHandleType(type)) {
    590       const bool inserted = backend_map_.insert(
    591           std::make_pair(type, backend)).second;
    592       DCHECK(inserted);
    593     }
    594   }
    595 }
    596 
    597 void FileSystemContext::DidOpenFileSystemForResolveURL(
    598     const FileSystemURL& url,
    599     const FileSystemContext::ResolveURLCallback& callback,
    600     const GURL& filesystem_root,
    601     const std::string& filesystem_name,
    602     base::File::Error error) {
    603   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
    604 
    605   if (error != base::File::FILE_OK) {
    606     callback.Run(error, FileSystemInfo(), base::FilePath(),
    607                  FileSystemContext::RESOLVED_ENTRY_NOT_FOUND);
    608     return;
    609   }
    610 
    611   fileapi::FileSystemInfo info(
    612       filesystem_name, filesystem_root, url.mount_type());
    613 
    614   // Extract the virtual path not containing a filesystem type part from |url|.
    615   base::FilePath parent = CrackURL(filesystem_root).virtual_path();
    616   base::FilePath child = url.virtual_path();
    617   base::FilePath path;
    618 
    619   if (parent.empty()) {
    620     path = child;
    621   } else if (parent != child) {
    622     bool result = parent.AppendRelativePath(child, &path);
    623     DCHECK(result);
    624   }
    625 
    626   operation_runner()->GetMetadata(
    627       url, base::Bind(&DidGetMetadataForResolveURL, path, callback, info));
    628 }
    629 
    630 }  // namespace fileapi
    631