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.
5 #include "webkit/browser/fileapi/file_system_context.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"
13 #include "webkit/browser/blob/file_stream_reader.h"
14 #include "webkit/browser/fileapi/copy_or_move_file_validator.h"
15 #include "webkit/browser/fileapi/external_mount_points.h"
16 #include "webkit/browser/fileapi/file_permission_policy.h"
17 #include "webkit/browser/fileapi/file_stream_writer.h"
18 #include "webkit/browser/fileapi/file_system_file_util.h"
19 #include "webkit/browser/fileapi/file_system_operation.h"
20 #include "webkit/browser/fileapi/file_system_operation_runner.h"
21 #include "webkit/browser/fileapi/file_system_options.h"
22 #include "webkit/browser/fileapi/file_system_quota_client.h"
23 #include "webkit/browser/fileapi/file_system_url.h"
24 #include "webkit/browser/fileapi/isolated_context.h"
25 #include "webkit/browser/fileapi/isolated_file_system_backend.h"
26 #include "webkit/browser/fileapi/mount_points.h"
27 #include "webkit/browser/fileapi/quota/quota_reservation.h"
28 #include "webkit/browser/fileapi/sandbox_file_system_backend.h"
29 #include "webkit/browser/quota/quota_manager_proxy.h"
30 #include "webkit/browser/quota/special_storage_policy.h"
31 #include "webkit/common/fileapi/file_system_info.h"
32 #include "webkit/common/fileapi/file_system_util.h"
34 using quota::QuotaClient
;
40 QuotaClient
* CreateQuotaClient(
41 FileSystemContext
* context
,
43 return new FileSystemQuotaClient(context
, is_incognito
);
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
);
58 callback
.Run(error
, FileSystemInfo(), base::FilePath(),
59 FileSystemContext::RESOLVED_ENTRY_NOT_FOUND
);
63 callback
.Run(error
, info
, path
, file_info
.is_directory
?
64 FileSystemContext::RESOLVED_ENTRY_DIRECTORY
:
65 FileSystemContext::RESOLVED_ENTRY_FILE
);
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
));
82 int FileSystemContext::GetPermissionPolicy(FileSystemType type
) {
84 case kFileSystemTypeTemporary
:
85 case kFileSystemTypePersistent
:
86 case kFileSystemTypeSyncable
:
87 return FILE_PERMISSION_SANDBOX
;
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
;
97 case kFileSystemTypeRestrictedNativeLocal
:
98 return FILE_PERMISSION_READ_ONLY
|
99 FILE_PERMISSION_USE_FILE_PERMISSION
;
101 case kFileSystemTypeDeviceMedia
:
102 case kFileSystemTypeIphoto
:
103 case kFileSystemTypeItunes
:
104 case kFileSystemTypeNativeMedia
:
105 case kFileSystemTypePicasa
:
106 return FILE_PERMISSION_USE_FILE_PERMISSION
;
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
;
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
;
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
;
130 return FILE_PERMISSION_ALWAYS_DENY
;
133 FileSystemContext::FileSystemContext(
134 base::SingleThreadTaskRunner
* io_task_runner
,
135 base::SequencedTaskRunner
* file_task_runner
,
136 ExternalMountPoints
* external_mount_points
,
137 quota::SpecialStoragePolicy
* special_storage_policy
,
138 quota::QuotaManagerProxy
* quota_manager_proxy
,
139 ScopedVector
<FileSystemBackend
> additional_backends
,
140 const std::vector
<URLRequestAutoMountHandler
>& auto_mount_handlers
,
141 const base::FilePath
& partition_path
,
142 const FileSystemOptions
& options
)
143 : io_task_runner_(io_task_runner
),
144 default_file_task_runner_(file_task_runner
),
145 quota_manager_proxy_(quota_manager_proxy
),
146 sandbox_delegate_(new SandboxFileSystemBackendDelegate(
150 special_storage_policy
,
152 sandbox_backend_(new SandboxFileSystemBackend(
153 sandbox_delegate_
.get())),
154 isolated_backend_(new IsolatedFileSystemBackend()),
155 plugin_private_backend_(new PluginPrivateFileSystemBackend(
158 special_storage_policy
,
160 additional_backends_(additional_backends
.Pass()),
161 auto_mount_handlers_(auto_mount_handlers
),
162 external_mount_points_(external_mount_points
),
163 partition_path_(partition_path
),
164 is_incognito_(options
.is_incognito()),
165 operation_runner_(new FileSystemOperationRunner(this)) {
166 RegisterBackend(sandbox_backend_
.get());
167 RegisterBackend(isolated_backend_
.get());
168 RegisterBackend(plugin_private_backend_
.get());
170 for (ScopedVector
<FileSystemBackend
>::const_iterator iter
=
171 additional_backends_
.begin();
172 iter
!= additional_backends_
.end(); ++iter
) {
173 RegisterBackend(*iter
);
176 if (quota_manager_proxy
) {
177 // Quota client assumes all backends have registered.
178 quota_manager_proxy
->RegisterClient(CreateQuotaClient(
179 this, options
.is_incognito()));
182 sandbox_backend_
->Initialize(this);
183 isolated_backend_
->Initialize(this);
184 plugin_private_backend_
->Initialize(this);
185 for (ScopedVector
<FileSystemBackend
>::const_iterator iter
=
186 additional_backends_
.begin();
187 iter
!= additional_backends_
.end(); ++iter
) {
188 (*iter
)->Initialize(this);
191 // Additional mount points must be added before regular system-wide
193 if (external_mount_points
)
194 url_crackers_
.push_back(external_mount_points
);
195 url_crackers_
.push_back(ExternalMountPoints::GetSystemInstance());
196 url_crackers_
.push_back(IsolatedContext::GetInstance());
199 bool FileSystemContext::DeleteDataForOriginOnFileTaskRunner(
200 const GURL
& origin_url
) {
201 DCHECK(default_file_task_runner()->RunsTasksOnCurrentThread());
202 DCHECK(origin_url
== origin_url
.GetOrigin());
205 for (FileSystemBackendMap::iterator iter
= backend_map_
.begin();
206 iter
!= backend_map_
.end();
208 FileSystemBackend
* backend
= iter
->second
;
209 if (!backend
->GetQuotaUtil())
211 if (backend
->GetQuotaUtil()->DeleteOriginDataOnFileTaskRunner(
212 this, quota_manager_proxy(), origin_url
, iter
->first
)
213 != base::File::FILE_OK
) {
214 // Continue the loop, but record the failure.
222 scoped_refptr
<QuotaReservation
>
223 FileSystemContext::CreateQuotaReservationOnFileTaskRunner(
224 const GURL
& origin_url
,
225 FileSystemType type
) {
226 DCHECK(default_file_task_runner()->RunsTasksOnCurrentThread());
227 FileSystemBackend
* backend
= GetFileSystemBackend(type
);
228 if (!backend
|| !backend
->GetQuotaUtil())
229 return scoped_refptr
<QuotaReservation
>();
230 return backend
->GetQuotaUtil()->CreateQuotaReservationOnFileTaskRunner(
234 void FileSystemContext::Shutdown() {
235 if (!io_task_runner_
->RunsTasksOnCurrentThread()) {
236 io_task_runner_
->PostTask(
237 FROM_HERE
, base::Bind(&FileSystemContext::Shutdown
,
238 make_scoped_refptr(this)));
241 operation_runner_
->Shutdown();
245 FileSystemContext::GetQuotaUtil(FileSystemType type
) const {
246 FileSystemBackend
* backend
= GetFileSystemBackend(type
);
249 return backend
->GetQuotaUtil();
252 AsyncFileUtil
* FileSystemContext::GetAsyncFileUtil(
253 FileSystemType type
) const {
254 FileSystemBackend
* backend
= GetFileSystemBackend(type
);
257 return backend
->GetAsyncFileUtil(type
);
260 CopyOrMoveFileValidatorFactory
*
261 FileSystemContext::GetCopyOrMoveFileValidatorFactory(
262 FileSystemType type
, base::File::Error
* error_code
) const {
264 *error_code
= base::File::FILE_OK
;
265 FileSystemBackend
* backend
= GetFileSystemBackend(type
);
268 return backend
->GetCopyOrMoveFileValidatorFactory(
272 FileSystemBackend
* FileSystemContext::GetFileSystemBackend(
273 FileSystemType type
) const {
274 FileSystemBackendMap::const_iterator found
= backend_map_
.find(type
);
275 if (found
!= backend_map_
.end())
276 return found
->second
;
277 NOTREACHED() << "Unknown filesystem type: " << type
;
281 bool FileSystemContext::IsSandboxFileSystem(FileSystemType type
) const {
282 FileSystemBackendMap::const_iterator found
= backend_map_
.find(type
);
283 return found
!= backend_map_
.end() && found
->second
->GetQuotaUtil();
286 const UpdateObserverList
* FileSystemContext::GetUpdateObservers(
287 FileSystemType type
) const {
288 FileSystemBackend
* backend
= GetFileSystemBackend(type
);
289 if (backend
->GetQuotaUtil())
290 return backend
->GetQuotaUtil()->GetUpdateObservers(type
);
294 const AccessObserverList
* FileSystemContext::GetAccessObservers(
295 FileSystemType type
) const {
296 FileSystemBackend
* backend
= GetFileSystemBackend(type
);
297 if (backend
->GetQuotaUtil())
298 return backend
->GetQuotaUtil()->GetAccessObservers(type
);
302 void FileSystemContext::GetFileSystemTypes(
303 std::vector
<FileSystemType
>* types
) const {
305 for (FileSystemBackendMap::const_iterator iter
= backend_map_
.begin();
306 iter
!= backend_map_
.end(); ++iter
)
307 types
->push_back(iter
->first
);
310 ExternalFileSystemBackend
*
311 FileSystemContext::external_backend() const {
312 return static_cast<ExternalFileSystemBackend
*>(
313 GetFileSystemBackend(kFileSystemTypeExternal
));
316 void FileSystemContext::OpenFileSystem(
317 const GURL
& origin_url
,
319 OpenFileSystemMode mode
,
320 const OpenFileSystemCallback
& callback
) {
321 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
322 DCHECK(!callback
.is_null());
324 if (!FileSystemContext::IsSandboxFileSystem(type
)) {
325 // Disallow opening a non-sandboxed filesystem.
326 callback
.Run(GURL(), std::string(), base::File::FILE_ERROR_SECURITY
);
330 FileSystemBackend
* backend
= GetFileSystemBackend(type
);
332 callback
.Run(GURL(), std::string(), base::File::FILE_ERROR_SECURITY
);
337 CreateCrackedFileSystemURL(origin_url
, type
, base::FilePath()),
342 void FileSystemContext::ResolveURL(
343 const FileSystemURL
& url
,
344 const ResolveURLCallback
& callback
) {
345 DCHECK(!callback
.is_null());
347 // If not on IO thread, forward before passing the task to the backend.
348 if (!io_task_runner_
->RunsTasksOnCurrentThread()) {
349 ResolveURLCallback relay_callback
=
350 base::Bind(&RelayResolveURLCallback
,
351 base::MessageLoopProxy::current(), callback
);
352 io_task_runner_
->PostTask(
354 base::Bind(&FileSystemContext::ResolveURL
, this, url
, relay_callback
));
358 FileSystemBackend
* backend
= GetFileSystemBackend(url
.type());
360 callback
.Run(base::File::FILE_ERROR_SECURITY
,
361 FileSystemInfo(), base::FilePath(),
362 FileSystemContext::RESOLVED_ENTRY_NOT_FOUND
);
368 OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT
,
369 base::Bind(&FileSystemContext::DidOpenFileSystemForResolveURL
,
375 void FileSystemContext::AttemptAutoMountForURLRequest(
376 const net::URLRequest
* url_request
,
377 const std::string
& storage_domain
,
378 const StatusCallback
& callback
) {
379 FileSystemURL
filesystem_url(url_request
->url());
380 if (filesystem_url
.type() == kFileSystemTypeExternal
) {
381 for (size_t i
= 0; i
< auto_mount_handlers_
.size(); i
++) {
382 if (auto_mount_handlers_
[i
].Run(url_request
, filesystem_url
,
383 storage_domain
, callback
)) {
388 callback
.Run(base::File::FILE_ERROR_NOT_FOUND
);
391 void FileSystemContext::DeleteFileSystem(
392 const GURL
& origin_url
,
394 const StatusCallback
& callback
) {
395 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
396 DCHECK(origin_url
== origin_url
.GetOrigin());
397 DCHECK(!callback
.is_null());
399 FileSystemBackend
* backend
= GetFileSystemBackend(type
);
401 callback
.Run(base::File::FILE_ERROR_SECURITY
);
404 if (!backend
->GetQuotaUtil()) {
405 callback
.Run(base::File::FILE_ERROR_INVALID_OPERATION
);
409 base::PostTaskAndReplyWithResult(
410 default_file_task_runner(),
412 // It is safe to pass Unretained(quota_util) since context owns it.
413 base::Bind(&FileSystemQuotaUtil::DeleteOriginDataOnFileTaskRunner
,
414 base::Unretained(backend
->GetQuotaUtil()),
415 make_scoped_refptr(this),
416 base::Unretained(quota_manager_proxy()),
422 scoped_ptr
<webkit_blob::FileStreamReader
>
423 FileSystemContext::CreateFileStreamReader(
424 const FileSystemURL
& url
,
426 const base::Time
& expected_modification_time
) {
428 return scoped_ptr
<webkit_blob::FileStreamReader
>();
429 FileSystemBackend
* backend
= GetFileSystemBackend(url
.type());
431 return scoped_ptr
<webkit_blob::FileStreamReader
>();
432 return backend
->CreateFileStreamReader(
433 url
, offset
, expected_modification_time
, this);
436 scoped_ptr
<FileStreamWriter
> FileSystemContext::CreateFileStreamWriter(
437 const FileSystemURL
& url
,
440 return scoped_ptr
<FileStreamWriter
>();
441 FileSystemBackend
* backend
= GetFileSystemBackend(url
.type());
443 return scoped_ptr
<FileStreamWriter
>();
444 return backend
->CreateFileStreamWriter(url
, offset
, this);
447 scoped_ptr
<FileSystemOperationRunner
>
448 FileSystemContext::CreateFileSystemOperationRunner() {
449 return make_scoped_ptr(new FileSystemOperationRunner(this));
452 FileSystemURL
FileSystemContext::CrackURL(const GURL
& url
) const {
453 return CrackFileSystemURL(FileSystemURL(url
));
456 FileSystemURL
FileSystemContext::CreateCrackedFileSystemURL(
459 const base::FilePath
& path
) const {
460 return CrackFileSystemURL(FileSystemURL(origin
, type
, path
));
463 #if defined(OS_CHROMEOS)
464 void FileSystemContext::EnableTemporaryFileSystemInIncognito() {
465 sandbox_backend_
->set_enable_temporary_file_system_in_incognito(true);
469 bool FileSystemContext::CanServeURLRequest(const FileSystemURL
& url
) const {
470 // We never support accessing files in isolated filesystems via an URL.
471 if (url
.mount_type() == kFileSystemTypeIsolated
)
473 #if defined(OS_CHROMEOS)
474 if (url
.type() == kFileSystemTypeTemporary
&&
475 sandbox_backend_
->enable_temporary_file_system_in_incognito()) {
479 return !is_incognito_
|| !FileSystemContext::IsSandboxFileSystem(url
.type());
482 bool FileSystemContext::ShouldFlushOnWriteCompletion(
483 FileSystemType type
) const {
484 if (IsSandboxFileSystem(type
)) {
485 // Disable Flush() for each write operation on SandboxFileSystems since it
486 // hurts the performance, assuming the FileSystems are stored in a local
487 // disk, we don't need to keep calling fsync() for it.
488 // On the other hand, other FileSystems that may stored on a removable media
489 // should be Flush()ed as soon as a write operation is completed, so that
490 // written data is saved over sudden media removal.
496 void FileSystemContext::OpenPluginPrivateFileSystem(
497 const GURL
& origin_url
,
499 const std::string
& filesystem_id
,
500 const std::string
& plugin_id
,
501 OpenFileSystemMode mode
,
502 const StatusCallback
& callback
) {
503 DCHECK(plugin_private_backend_
);
504 plugin_private_backend_
->OpenPrivateFileSystem(
505 origin_url
, type
, filesystem_id
, plugin_id
, mode
, callback
);
508 FileSystemContext::~FileSystemContext() {
511 void FileSystemContext::DeleteOnCorrectThread() const {
512 if (!io_task_runner_
->RunsTasksOnCurrentThread() &&
513 io_task_runner_
->DeleteSoon(FROM_HERE
, this)) {
519 FileSystemOperation
* FileSystemContext::CreateFileSystemOperation(
520 const FileSystemURL
& url
, base::File::Error
* error_code
) {
521 if (!url
.is_valid()) {
523 *error_code
= base::File::FILE_ERROR_INVALID_URL
;
527 FileSystemBackend
* backend
= GetFileSystemBackend(url
.type());
530 *error_code
= base::File::FILE_ERROR_FAILED
;
534 base::File::Error fs_error
= base::File::FILE_OK
;
535 FileSystemOperation
* operation
=
536 backend
->CreateFileSystemOperation(url
, this, &fs_error
);
539 *error_code
= fs_error
;
543 FileSystemURL
FileSystemContext::CrackFileSystemURL(
544 const FileSystemURL
& url
) const {
546 return FileSystemURL();
548 // The returned value in case there is no crackers which can crack the url.
549 // This is valid situation for non isolated/external file systems.
550 FileSystemURL current
= url
;
552 // File system may be mounted multiple times (e.g., an isolated filesystem on
553 // top of an external filesystem). Hence cracking needs to be iterated.
555 FileSystemURL cracked
= current
;
556 for (size_t i
= 0; i
< url_crackers_
.size(); ++i
) {
557 if (!url_crackers_
[i
]->HandlesFileSystemMountType(current
.type()))
559 cracked
= url_crackers_
[i
]->CrackFileSystemURL(current
);
560 if (cracked
.is_valid())
563 if (cracked
== current
)
570 void FileSystemContext::RegisterBackend(FileSystemBackend
* backend
) {
571 const FileSystemType mount_types
[] = {
572 kFileSystemTypeTemporary
,
573 kFileSystemTypePersistent
,
574 kFileSystemTypeIsolated
,
575 kFileSystemTypeExternal
,
577 // Register file system backends for public mount types.
578 for (size_t j
= 0; j
< ARRAYSIZE_UNSAFE(mount_types
); ++j
) {
579 if (backend
->CanHandleType(mount_types
[j
])) {
580 const bool inserted
= backend_map_
.insert(
581 std::make_pair(mount_types
[j
], backend
)).second
;
585 // Register file system backends for internal types.
586 for (int t
= kFileSystemInternalTypeEnumStart
+ 1;
587 t
< kFileSystemInternalTypeEnumEnd
; ++t
) {
588 FileSystemType type
= static_cast<FileSystemType
>(t
);
589 if (backend
->CanHandleType(type
)) {
590 const bool inserted
= backend_map_
.insert(
591 std::make_pair(type
, backend
)).second
;
597 void FileSystemContext::DidOpenFileSystemForResolveURL(
598 const FileSystemURL
& url
,
599 const FileSystemContext::ResolveURLCallback
& callback
,
600 const GURL
& filesystem_root
,
601 const std::string
& filesystem_name
,
602 base::File::Error error
) {
603 DCHECK(io_task_runner_
->RunsTasksOnCurrentThread());
605 if (error
!= base::File::FILE_OK
) {
606 callback
.Run(error
, FileSystemInfo(), base::FilePath(),
607 FileSystemContext::RESOLVED_ENTRY_NOT_FOUND
);
611 fileapi::FileSystemInfo
info(
612 filesystem_name
, filesystem_root
, url
.mount_type());
614 // Extract the virtual path not containing a filesystem type part from |url|.
615 base::FilePath parent
= CrackURL(filesystem_root
).virtual_path();
616 base::FilePath child
= url
.virtual_path();
619 if (parent
.empty()) {
621 } else if (parent
!= child
) {
622 bool result
= parent
.AppendRelativePath(child
, &path
);
626 operation_runner()->GetMetadata(
627 url
, base::Bind(&DidGetMetadataForResolveURL
, path
, callback
, info
));
630 } // namespace fileapi