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