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