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