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/fileapi/file_system_backend.h"
6 
7 #include <stddef.h>
8 
9 #include <memory>
10 #include <utility>
11 
12 #include "base/check_op.h"
13 #include "base/command_line.h"
14 #include "base/notreached.h"
15 #include "base/stl_util.h"
16 #include "base/task/post_task.h"
17 #include "base/task/task_traits.h"
18 #include "base/task/thread_pool.h"
19 #include "chrome/browser/chromeos/arc/fileapi/arc_documents_provider_util.h"
20 #include "chrome/browser/chromeos/fileapi/file_access_permissions.h"
21 #include "chrome/browser/chromeos/fileapi/file_system_backend_delegate.h"
22 #include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h"
23 #include "chrome/common/url_constants.h"
24 #include "chromeos/constants/chromeos_switches.h"
25 #include "chromeos/dbus/cros_disks_client.h"
26 #include "net/base/escape.h"
27 #include "storage/browser/file_system/async_file_util.h"
28 #include "storage/browser/file_system/external_mount_points.h"
29 #include "storage/browser/file_system/file_stream_reader.h"
30 #include "storage/browser/file_system/file_stream_writer.h"
31 #include "storage/browser/file_system/file_system_context.h"
32 #include "storage/browser/file_system/file_system_operation.h"
33 #include "storage/browser/file_system/file_system_operation_context.h"
34 #include "storage/browser/file_system/file_system_url.h"
35 #include "storage/common/file_system/file_system_mount_option.h"
36 #include "storage/common/file_system/file_system_types.h"
37 #include "storage/common/file_system/file_system_util.h"
38 #include "url/origin.h"
39 
40 namespace chromeos {
41 namespace {
42 
43 // TODO(mtomasz): Remove this hacky whitelist.
44 // See: crbug.com/271946
45 const char* kOemAccessibleExtensions[] = {
46     "mlbmkoenclnokonejhlfakkeabdlmpek",  // TimeScapes,
47     "nhpmmldpbfjofkipjaieeomhnmcgihfm",  // Retail Demo (public session),
48     "klimoghijjogocdbaikffefjfcfheiel",  // Retail Demo (OOBE),
49 };
50 
51 }  // namespace
52 
53 // static
CanHandleURL(const storage::FileSystemURL & url)54 bool FileSystemBackend::CanHandleURL(const storage::FileSystemURL& url) {
55   if (!url.is_valid())
56     return false;
57   return url.type() == storage::kFileSystemTypeNativeLocal ||
58          url.type() == storage::kFileSystemTypeRestrictedNativeLocal ||
59          url.type() == storage::kFileSystemTypeProvided ||
60          url.type() == storage::kFileSystemTypeDeviceMediaAsFileStorage ||
61          url.type() == storage::kFileSystemTypeArcContent ||
62          url.type() == storage::kFileSystemTypeArcDocumentsProvider ||
63          url.type() == storage::kFileSystemTypeDriveFs ||
64          url.type() == storage::kFileSystemTypeSmbFs;
65 }
66 
FileSystemBackend(std::unique_ptr<FileSystemBackendDelegate> file_system_provider_delegate,std::unique_ptr<FileSystemBackendDelegate> mtp_delegate,std::unique_ptr<FileSystemBackendDelegate> arc_content_delegate,std::unique_ptr<FileSystemBackendDelegate> arc_documents_provider_delegate,std::unique_ptr<FileSystemBackendDelegate> drivefs_delegate,std::unique_ptr<FileSystemBackendDelegate> smbfs_delegate,scoped_refptr<storage::ExternalMountPoints> mount_points,storage::ExternalMountPoints * system_mount_points)67 FileSystemBackend::FileSystemBackend(
68     std::unique_ptr<FileSystemBackendDelegate> file_system_provider_delegate,
69     std::unique_ptr<FileSystemBackendDelegate> mtp_delegate,
70     std::unique_ptr<FileSystemBackendDelegate> arc_content_delegate,
71     std::unique_ptr<FileSystemBackendDelegate> arc_documents_provider_delegate,
72     std::unique_ptr<FileSystemBackendDelegate> drivefs_delegate,
73     std::unique_ptr<FileSystemBackendDelegate> smbfs_delegate,
74     scoped_refptr<storage::ExternalMountPoints> mount_points,
75     storage::ExternalMountPoints* system_mount_points)
76     : file_access_permissions_(new FileAccessPermissions()),
77       local_file_util_(storage::AsyncFileUtil::CreateForLocalFileSystem()),
78       file_system_provider_delegate_(std::move(file_system_provider_delegate)),
79       mtp_delegate_(std::move(mtp_delegate)),
80       arc_content_delegate_(std::move(arc_content_delegate)),
81       arc_documents_provider_delegate_(
82           std::move(arc_documents_provider_delegate)),
83       drivefs_delegate_(std::move(drivefs_delegate)),
84       smbfs_delegate_(std::move(smbfs_delegate)),
85       mount_points_(mount_points),
86       system_mount_points_(system_mount_points) {}
87 
~FileSystemBackend()88 FileSystemBackend::~FileSystemBackend() {
89 }
90 
AddSystemMountPoints()91 void FileSystemBackend::AddSystemMountPoints() {
92   // RegisterFileSystem() is no-op if the mount point with the same name
93   // already exists, hence it's safe to call without checking if a mount
94   // point already exists or not.
95   system_mount_points_->RegisterFileSystem(
96       kSystemMountNameArchive, storage::kFileSystemTypeNativeLocal,
97       storage::FileSystemMountOption(),
98       chromeos::CrosDisksClient::GetArchiveMountPoint());
99   system_mount_points_->RegisterFileSystem(
100       kSystemMountNameRemovable, storage::kFileSystemTypeNativeLocal,
101       storage::FileSystemMountOption(storage::FlushPolicy::FLUSH_ON_COMPLETION),
102       chromeos::CrosDisksClient::GetRemovableDiskMountPoint());
103   system_mount_points_->RegisterFileSystem(
104       kSystemMountNameOem, storage::kFileSystemTypeRestrictedNativeLocal,
105       storage::FileSystemMountOption(),
106       base::FilePath(FILE_PATH_LITERAL("/usr/share/oem")));
107 }
108 
CanHandleType(storage::FileSystemType type) const109 bool FileSystemBackend::CanHandleType(storage::FileSystemType type) const {
110   switch (type) {
111     case storage::kFileSystemTypeExternal:
112     case storage::kFileSystemTypeRestrictedNativeLocal:
113     case storage::kFileSystemTypeNativeLocal:
114     case storage::kFileSystemTypeNativeForPlatformApp:
115     case storage::kFileSystemTypeDeviceMediaAsFileStorage:
116     case storage::kFileSystemTypeProvided:
117     case storage::kFileSystemTypeArcContent:
118     case storage::kFileSystemTypeArcDocumentsProvider:
119     case storage::kFileSystemTypeDriveFs:
120     case storage::kFileSystemTypeSmbFs:
121       return true;
122     default:
123       return false;
124   }
125 }
126 
Initialize(storage::FileSystemContext * context)127 void FileSystemBackend::Initialize(storage::FileSystemContext* context) {
128 }
129 
ResolveURL(const storage::FileSystemURL & url,storage::OpenFileSystemMode mode,OpenFileSystemCallback callback)130 void FileSystemBackend::ResolveURL(const storage::FileSystemURL& url,
131                                    storage::OpenFileSystemMode mode,
132                                    OpenFileSystemCallback callback) {
133   std::string id;
134   storage::FileSystemType type;
135   std::string cracked_id;
136   base::FilePath path;
137   storage::FileSystemMountOption option;
138   if (!mount_points_->CrackVirtualPath(
139            url.virtual_path(), &id, &type, &cracked_id, &path, &option) &&
140       !system_mount_points_->CrackVirtualPath(
141            url.virtual_path(), &id, &type, &cracked_id, &path, &option)) {
142     // Not under a mount point, so return an error, since the root is not
143     // accessible.
144     GURL root_url = GURL(storage::GetExternalFileSystemRootURIString(
145         url.origin().GetURL(), std::string()));
146     std::move(callback).Run(root_url, std::string(),
147                             base::File::FILE_ERROR_SECURITY);
148     return;
149   }
150 
151   std::string name;
152   // Construct a URL restricted to the found mount point.
153   std::string root_url =
154       storage::GetExternalFileSystemRootURIString(url.origin().GetURL(), id);
155 
156   // For removable and archives, the file system root is the external mount
157   // point plus the inner mount point.
158   if (id == "archive" || id == "removable") {
159     std::vector<std::string> components;
160     url.virtual_path().GetComponents(&components);
161     DCHECK_EQ(id, components.at(0));
162     if (components.size() < 2) {
163       // Unable to access /archive and /removable directories directly. The
164       // inner mount name must be specified.
165       std::move(callback).Run(GURL(root_url), std::string(),
166                               base::File::FILE_ERROR_SECURITY);
167       return;
168     }
169     std::string inner_mount_name = components[1];
170     root_url += inner_mount_name + "/";
171     name = inner_mount_name;
172   } else if (id == arc::kDocumentsProviderMountPointName) {
173     // For ARC documents provider file system, volumes are mounted per document
174     // provider root, so we need to fix up |root_url| to point to an individual
175     // root.
176     std::string authority;
177     std::string root_document_id;
178     base::FilePath unused_path;
179     if (!arc::ParseDocumentsProviderUrl(url, &authority, &root_document_id,
180                                         &unused_path)) {
181       std::move(callback).Run(GURL(root_url), std::string(),
182                               base::File::FILE_ERROR_SECURITY);
183       return;
184     }
185     base::FilePath mount_path =
186         arc::GetDocumentsProviderMountPath(authority, root_document_id);
187     base::FilePath relative_mount_path;
188     base::FilePath(arc::kDocumentsProviderMountPointPath)
189         .AppendRelativePath(mount_path, &relative_mount_path);
190     root_url +=
191         net::EscapePath(storage::FilePathToString(relative_mount_path)) + "/";
192     name = authority + ":" + root_document_id;
193   } else {
194     name = id;
195   }
196 
197   std::move(callback).Run(GURL(root_url), name, base::File::FILE_OK);
198 }
199 
GetQuotaUtil()200 storage::FileSystemQuotaUtil* FileSystemBackend::GetQuotaUtil() {
201   // No quota support.
202   return NULL;
203 }
204 
GetUpdateObservers(storage::FileSystemType type) const205 const storage::UpdateObserverList* FileSystemBackend::GetUpdateObservers(
206     storage::FileSystemType type) const {
207   return NULL;
208 }
209 
GetChangeObservers(storage::FileSystemType type) const210 const storage::ChangeObserverList* FileSystemBackend::GetChangeObservers(
211     storage::FileSystemType type) const {
212   return NULL;
213 }
214 
GetAccessObservers(storage::FileSystemType type) const215 const storage::AccessObserverList* FileSystemBackend::GetAccessObservers(
216     storage::FileSystemType type) const {
217   return NULL;
218 }
219 
IsAccessAllowed(const storage::FileSystemURL & url) const220 bool FileSystemBackend::IsAccessAllowed(
221     const storage::FileSystemURL& url) const {
222   if (!url.is_valid())
223     return false;
224 
225   // No extra check is needed for isolated file systems.
226   if (url.mount_type() == storage::kFileSystemTypeIsolated)
227     return true;
228 
229   if (!CanHandleURL(url))
230     return false;
231 
232   // If there is no origin set, then it's an internal access.
233   if (url.origin().opaque())
234     return true;
235 
236   const std::string& extension_id = url.origin().host();
237   if (url.type() == storage::kFileSystemTypeRestrictedNativeLocal) {
238     for (size_t i = 0; i < base::size(kOemAccessibleExtensions); ++i) {
239       if (extension_id == kOemAccessibleExtensions[i])
240         return true;
241     }
242   }
243 
244   return file_access_permissions_->HasAccessPermission(extension_id,
245                                                        url.virtual_path());
246 }
247 
GrantFileAccessToExtension(const std::string & extension_id,const base::FilePath & virtual_path)248 void FileSystemBackend::GrantFileAccessToExtension(
249     const std::string& extension_id, const base::FilePath& virtual_path) {
250   std::string id;
251   storage::FileSystemType type;
252   std::string cracked_id;
253   base::FilePath path;
254   storage::FileSystemMountOption option;
255   if (!mount_points_->CrackVirtualPath(virtual_path, &id, &type, &cracked_id,
256                                        &path, &option) &&
257       !system_mount_points_->CrackVirtualPath(virtual_path, &id, &type,
258                                               &cracked_id, &path, &option)) {
259     return;
260   }
261 
262   file_access_permissions_->GrantAccessPermission(extension_id, virtual_path);
263 }
264 
RevokeAccessForExtension(const std::string & extension_id)265 void FileSystemBackend::RevokeAccessForExtension(
266       const std::string& extension_id) {
267   file_access_permissions_->RevokePermissions(extension_id);
268 }
269 
GetRootDirectories() const270 std::vector<base::FilePath> FileSystemBackend::GetRootDirectories() const {
271   std::vector<storage::MountPoints::MountPointInfo> mount_points;
272   mount_points_->AddMountPointInfosTo(&mount_points);
273   system_mount_points_->AddMountPointInfosTo(&mount_points);
274 
275   std::vector<base::FilePath> root_dirs;
276   for (size_t i = 0; i < mount_points.size(); ++i)
277     root_dirs.push_back(mount_points[i].path);
278   return root_dirs;
279 }
280 
GetAsyncFileUtil(storage::FileSystemType type)281 storage::AsyncFileUtil* FileSystemBackend::GetAsyncFileUtil(
282     storage::FileSystemType type) {
283   switch (type) {
284     case storage::kFileSystemTypeProvided:
285       return file_system_provider_delegate_->GetAsyncFileUtil(type);
286     case storage::kFileSystemTypeNativeLocal:
287     case storage::kFileSystemTypeRestrictedNativeLocal:
288       return local_file_util_.get();
289     case storage::kFileSystemTypeDeviceMediaAsFileStorage:
290       return mtp_delegate_->GetAsyncFileUtil(type);
291     case storage::kFileSystemTypeArcContent:
292       return arc_content_delegate_->GetAsyncFileUtil(type);
293     case storage::kFileSystemTypeArcDocumentsProvider:
294       return arc_documents_provider_delegate_->GetAsyncFileUtil(type);
295     case storage::kFileSystemTypeDriveFs:
296       return drivefs_delegate_->GetAsyncFileUtil(type);
297     case storage::kFileSystemTypeSmbFs:
298       return smbfs_delegate_->GetAsyncFileUtil(type);
299     default:
300       NOTREACHED();
301   }
302   return NULL;
303 }
304 
GetWatcherManager(storage::FileSystemType type)305 storage::WatcherManager* FileSystemBackend::GetWatcherManager(
306     storage::FileSystemType type) {
307   if (type == storage::kFileSystemTypeProvided)
308     return file_system_provider_delegate_->GetWatcherManager(type);
309 
310   if (type == storage::kFileSystemTypeDeviceMediaAsFileStorage) {
311     return mtp_delegate_->GetWatcherManager(type);
312   }
313 
314   if (type == storage::kFileSystemTypeArcDocumentsProvider)
315     return arc_documents_provider_delegate_->GetWatcherManager(type);
316 
317   // TODO(mtomasz): Add support for other backends.
318   return NULL;
319 }
320 
321 storage::CopyOrMoveFileValidatorFactory*
GetCopyOrMoveFileValidatorFactory(storage::FileSystemType type,base::File::Error * error_code)322 FileSystemBackend::GetCopyOrMoveFileValidatorFactory(
323     storage::FileSystemType type,
324     base::File::Error* error_code) {
325   DCHECK(error_code);
326   *error_code = base::File::FILE_OK;
327   return NULL;
328 }
329 
CreateFileSystemOperation(const storage::FileSystemURL & url,storage::FileSystemContext * context,base::File::Error * error_code) const330 storage::FileSystemOperation* FileSystemBackend::CreateFileSystemOperation(
331     const storage::FileSystemURL& url,
332     storage::FileSystemContext* context,
333     base::File::Error* error_code) const {
334   DCHECK(url.is_valid());
335 
336   if (!IsAccessAllowed(url)) {
337     *error_code = base::File::FILE_ERROR_SECURITY;
338     return NULL;
339   }
340 
341   if (url.type() == storage::kFileSystemTypeDeviceMediaAsFileStorage) {
342     // MTP file operations run on MediaTaskRunner.
343     return storage::FileSystemOperation::Create(
344         url, context,
345         std::make_unique<storage::FileSystemOperationContext>(
346             context, MediaFileSystemBackend::MediaTaskRunner().get()));
347   }
348   if (url.type() == storage::kFileSystemTypeNativeLocal ||
349       url.type() == storage::kFileSystemTypeRestrictedNativeLocal ||
350       url.type() == storage::kFileSystemTypeDriveFs ||
351       url.type() == storage::kFileSystemTypeSmbFs) {
352     return storage::FileSystemOperation::Create(
353         url, context,
354         std::make_unique<storage::FileSystemOperationContext>(
355             context, base::ThreadPool::CreateSequencedTaskRunner(
356                          {base::MayBlock(), base::TaskPriority::USER_VISIBLE})
357                          .get()));
358   }
359 
360   DCHECK(url.type() == storage::kFileSystemTypeProvided ||
361          url.type() == storage::kFileSystemTypeArcContent ||
362          url.type() == storage::kFileSystemTypeArcDocumentsProvider);
363   return storage::FileSystemOperation::Create(
364       url, context,
365       std::make_unique<storage::FileSystemOperationContext>(context));
366 }
367 
SupportsStreaming(const storage::FileSystemURL & url) const368 bool FileSystemBackend::SupportsStreaming(
369     const storage::FileSystemURL& url) const {
370   return url.type() == storage::kFileSystemTypeProvided ||
371          url.type() == storage::kFileSystemTypeDeviceMediaAsFileStorage ||
372          url.type() == storage::kFileSystemTypeArcContent ||
373          url.type() == storage::kFileSystemTypeArcDocumentsProvider;
374 }
375 
HasInplaceCopyImplementation(storage::FileSystemType type) const376 bool FileSystemBackend::HasInplaceCopyImplementation(
377     storage::FileSystemType type) const {
378   switch (type) {
379     case storage::kFileSystemTypeProvided:
380     case storage::kFileSystemTypeDeviceMediaAsFileStorage:
381     case storage::kFileSystemTypeDriveFs:
382       return true;
383     // TODO(fukino): Support in-place copy for DocumentsProvider.
384     // crbug.com/953603.
385     case storage::kFileSystemTypeArcDocumentsProvider:
386     case storage::kFileSystemTypeNativeLocal:
387     case storage::kFileSystemTypeRestrictedNativeLocal:
388     case storage::kFileSystemTypeArcContent:
389     // TODO(crbug.com/939235): Implement in-place copy in SmbFs.
390     case storage::kFileSystemTypeSmbFs:
391       return false;
392     default:
393       NOTREACHED();
394   }
395   return true;
396 }
397 
398 std::unique_ptr<storage::FileStreamReader>
CreateFileStreamReader(const storage::FileSystemURL & url,int64_t offset,int64_t max_bytes_to_read,const base::Time & expected_modification_time,storage::FileSystemContext * context) const399 FileSystemBackend::CreateFileStreamReader(
400     const storage::FileSystemURL& url,
401     int64_t offset,
402     int64_t max_bytes_to_read,
403     const base::Time& expected_modification_time,
404     storage::FileSystemContext* context) const {
405   DCHECK(url.is_valid());
406 
407   if (!IsAccessAllowed(url))
408     return std::unique_ptr<storage::FileStreamReader>();
409 
410   switch (url.type()) {
411     case storage::kFileSystemTypeProvided:
412       return file_system_provider_delegate_->CreateFileStreamReader(
413           url, offset, max_bytes_to_read, expected_modification_time, context);
414     case storage::kFileSystemTypeNativeLocal:
415     case storage::kFileSystemTypeRestrictedNativeLocal:
416     case storage::kFileSystemTypeDriveFs:
417     case storage::kFileSystemTypeSmbFs:
418       return std::unique_ptr<storage::FileStreamReader>(
419           storage::FileStreamReader::CreateForLocalFile(
420               base::ThreadPool::CreateTaskRunner(
421                   {base::MayBlock(), base::TaskPriority::USER_VISIBLE})
422                   .get(),
423               url.path(), offset, expected_modification_time));
424     case storage::kFileSystemTypeDeviceMediaAsFileStorage:
425       return mtp_delegate_->CreateFileStreamReader(
426           url, offset, max_bytes_to_read, expected_modification_time, context);
427     case storage::kFileSystemTypeArcContent:
428       return arc_content_delegate_->CreateFileStreamReader(
429           url, offset, max_bytes_to_read, expected_modification_time, context);
430     case storage::kFileSystemTypeArcDocumentsProvider:
431       return arc_documents_provider_delegate_->CreateFileStreamReader(
432           url, offset, max_bytes_to_read, expected_modification_time, context);
433     default:
434       NOTREACHED();
435   }
436   return std::unique_ptr<storage::FileStreamReader>();
437 }
438 
439 std::unique_ptr<storage::FileStreamWriter>
CreateFileStreamWriter(const storage::FileSystemURL & url,int64_t offset,storage::FileSystemContext * context) const440 FileSystemBackend::CreateFileStreamWriter(
441     const storage::FileSystemURL& url,
442     int64_t offset,
443     storage::FileSystemContext* context) const {
444   DCHECK(url.is_valid());
445 
446   if (!IsAccessAllowed(url))
447     return std::unique_ptr<storage::FileStreamWriter>();
448 
449   switch (url.type()) {
450     case storage::kFileSystemTypeProvided:
451       return file_system_provider_delegate_->CreateFileStreamWriter(
452           url, offset, context);
453     case storage::kFileSystemTypeNativeLocal:
454     case storage::kFileSystemTypeDriveFs:
455     case storage::kFileSystemTypeSmbFs:
456       return storage::FileStreamWriter::CreateForLocalFile(
457           base::ThreadPool::CreateTaskRunner(
458               {base::MayBlock(), base::TaskPriority::USER_VISIBLE})
459               .get(),
460           url.path(), offset, storage::FileStreamWriter::OPEN_EXISTING_FILE);
461     case storage::kFileSystemTypeDeviceMediaAsFileStorage:
462       return mtp_delegate_->CreateFileStreamWriter(url, offset, context);
463     case storage::kFileSystemTypeArcDocumentsProvider:
464       return arc_documents_provider_delegate_->CreateFileStreamWriter(
465           url, offset, context);
466     // Read only file systems.
467     case storage::kFileSystemTypeRestrictedNativeLocal:
468     case storage::kFileSystemTypeArcContent:
469       return std::unique_ptr<storage::FileStreamWriter>();
470     default:
471       NOTREACHED();
472   }
473   return std::unique_ptr<storage::FileStreamWriter>();
474 }
475 
GetVirtualPath(const base::FilePath & filesystem_path,base::FilePath * virtual_path) const476 bool FileSystemBackend::GetVirtualPath(const base::FilePath& filesystem_path,
477                                        base::FilePath* virtual_path) const {
478   return mount_points_->GetVirtualPath(filesystem_path, virtual_path) ||
479          system_mount_points_->GetVirtualPath(filesystem_path, virtual_path);
480 }
481 
GetRedirectURLForContents(const storage::FileSystemURL & url,storage::URLCallback callback) const482 void FileSystemBackend::GetRedirectURLForContents(
483     const storage::FileSystemURL& url,
484     storage::URLCallback callback) const {
485   DCHECK(url.is_valid());
486 
487   if (!IsAccessAllowed(url)) {
488     std::move(callback).Run(GURL());
489     return;
490   }
491 
492   switch (url.type()) {
493     case storage::kFileSystemTypeProvided:
494       file_system_provider_delegate_->GetRedirectURLForContents(
495           url, std::move(callback));
496       return;
497     case storage::kFileSystemTypeDeviceMediaAsFileStorage:
498       mtp_delegate_->GetRedirectURLForContents(url, std::move(callback));
499       return;
500     case storage::kFileSystemTypeNativeLocal:
501     case storage::kFileSystemTypeRestrictedNativeLocal:
502     case storage::kFileSystemTypeArcContent:
503     case storage::kFileSystemTypeArcDocumentsProvider:
504     case storage::kFileSystemTypeDriveFs:
505     case storage::kFileSystemTypeSmbFs:
506       std::move(callback).Run(GURL());
507       return;
508     default:
509       NOTREACHED();
510   }
511   std::move(callback).Run(GURL());
512 }
513 
CreateInternalURL(storage::FileSystemContext * context,const base::FilePath & entry_path) const514 storage::FileSystemURL FileSystemBackend::CreateInternalURL(
515     storage::FileSystemContext* context,
516     const base::FilePath& entry_path) const {
517   base::FilePath virtual_path;
518   if (!GetVirtualPath(entry_path, &virtual_path))
519     return storage::FileSystemURL();
520 
521   return context->CreateCrackedFileSystemURL(
522       url::Origin(), storage::kFileSystemTypeExternal, virtual_path);
523 }
524 
525 }  // namespace chromeos
526