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