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/fileapi/test_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_util.h"
31
32using quota::QuotaClient;
33
34namespace fileapi {
35
36namespace {
37
38QuotaClient* CreateQuotaClient(
39    FileSystemContext* context,
40    bool is_incognito) {
41  return new FileSystemQuotaClient(context, is_incognito);
42}
43
44void DidOpenFileSystem(
45    const FileSystemContext::OpenFileSystemCallback& callback,
46    const GURL& filesystem_root,
47    const std::string& filesystem_name,
48    base::PlatformFileError error) {
49  callback.Run(error, filesystem_name, filesystem_root);
50}
51
52}  // namespace
53
54// static
55int FileSystemContext::GetPermissionPolicy(FileSystemType type) {
56  switch (type) {
57    case kFileSystemTypeTemporary:
58    case kFileSystemTypePersistent:
59    case kFileSystemTypeSyncable:
60      return FILE_PERMISSION_SANDBOX;
61
62    case kFileSystemTypeDrive:
63    case kFileSystemTypeNativeForPlatformApp:
64    case kFileSystemTypeNativeLocal:
65      return FILE_PERMISSION_USE_FILE_PERMISSION;
66
67    case kFileSystemTypeRestrictedNativeLocal:
68      return FILE_PERMISSION_READ_ONLY |
69             FILE_PERMISSION_USE_FILE_PERMISSION;
70
71    // Following types are only accessed via IsolatedFileSystem, and
72    // don't have their own permission policies.
73    case kFileSystemTypeDeviceMedia:
74    case kFileSystemTypeDragged:
75    case kFileSystemTypeForTransientFile:
76    case kFileSystemTypeItunes:
77    case kFileSystemTypeNativeMedia:
78    case kFileSystemTypePicasa:
79      return FILE_PERMISSION_ALWAYS_DENY;
80
81    // Following types only appear as mount_type, and will not be
82    // queried for their permission policies.
83    case kFileSystemTypeIsolated:
84    case kFileSystemTypeExternal:
85      return FILE_PERMISSION_ALWAYS_DENY;
86
87    // Following types should not be used to access files by FileAPI clients.
88    case kFileSystemTypeTest:
89    case kFileSystemTypeSyncableForInternalSync:
90    case kFileSystemInternalTypeEnumEnd:
91    case kFileSystemInternalTypeEnumStart:
92    case kFileSystemTypeUnknown:
93      return FILE_PERMISSION_ALWAYS_DENY;
94  }
95  NOTREACHED();
96  return FILE_PERMISSION_ALWAYS_DENY;
97}
98
99FileSystemContext::FileSystemContext(
100    base::SingleThreadTaskRunner* io_task_runner,
101    base::SequencedTaskRunner* file_task_runner,
102    ExternalMountPoints* external_mount_points,
103    quota::SpecialStoragePolicy* special_storage_policy,
104    quota::QuotaManagerProxy* quota_manager_proxy,
105    ScopedVector<FileSystemBackend> additional_backends,
106    const base::FilePath& partition_path,
107    const FileSystemOptions& options)
108    : io_task_runner_(io_task_runner),
109      default_file_task_runner_(file_task_runner),
110      quota_manager_proxy_(quota_manager_proxy),
111      sandbox_context_(new SandboxContext(
112          quota_manager_proxy,
113          file_task_runner,
114          partition_path,
115          special_storage_policy,
116          options)),
117      sandbox_backend_(new SandboxFileSystemBackend(
118          sandbox_context_.get())),
119      isolated_backend_(new IsolatedFileSystemBackend()),
120      additional_backends_(additional_backends.Pass()),
121      external_mount_points_(external_mount_points),
122      partition_path_(partition_path),
123      operation_runner_(new FileSystemOperationRunner(this)) {
124  if (quota_manager_proxy) {
125    quota_manager_proxy->RegisterClient(CreateQuotaClient(
126            this, options.is_incognito()));
127  }
128
129  RegisterBackend(sandbox_backend_.get());
130  RegisterBackend(isolated_backend_.get());
131
132  for (ScopedVector<FileSystemBackend>::const_iterator iter =
133          additional_backends_.begin();
134       iter != additional_backends_.end(); ++iter) {
135    RegisterBackend(*iter);
136  }
137
138  sandbox_backend_->Initialize(this);
139  isolated_backend_->Initialize(this);
140  for (ScopedVector<FileSystemBackend>::const_iterator iter =
141          additional_backends_.begin();
142       iter != additional_backends_.end(); ++iter) {
143    (*iter)->Initialize(this);
144  }
145
146  // Additional mount points must be added before regular system-wide
147  // mount points.
148  if (external_mount_points)
149    url_crackers_.push_back(external_mount_points);
150  url_crackers_.push_back(ExternalMountPoints::GetSystemInstance());
151  url_crackers_.push_back(IsolatedContext::GetInstance());
152}
153
154bool FileSystemContext::DeleteDataForOriginOnFileThread(
155    const GURL& origin_url) {
156  DCHECK(default_file_task_runner()->RunsTasksOnCurrentThread());
157  DCHECK(origin_url == origin_url.GetOrigin());
158
159  bool success = true;
160  for (FileSystemBackendMap::iterator iter = backend_map_.begin();
161       iter != backend_map_.end();
162       ++iter) {
163    FileSystemBackend* backend = iter->second;
164    if (!backend->GetQuotaUtil())
165      continue;
166    if (backend->GetQuotaUtil()->DeleteOriginDataOnFileThread(
167            this, quota_manager_proxy(), origin_url, iter->first)
168            != base::PLATFORM_FILE_OK) {
169      // Continue the loop, but record the failure.
170      success = false;
171    }
172  }
173
174  return success;
175}
176
177void FileSystemContext::Shutdown() {
178  if (!io_task_runner_->RunsTasksOnCurrentThread()) {
179    io_task_runner_->PostTask(
180        FROM_HERE, base::Bind(&FileSystemContext::Shutdown,
181                              make_scoped_refptr(this)));
182    return;
183  }
184  operation_runner_->Shutdown();
185}
186
187FileSystemQuotaUtil*
188FileSystemContext::GetQuotaUtil(FileSystemType type) const {
189  FileSystemBackend* backend = GetFileSystemBackend(type);
190  if (!backend)
191    return NULL;
192  return backend->GetQuotaUtil();
193}
194
195AsyncFileUtil* FileSystemContext::GetAsyncFileUtil(
196    FileSystemType type) const {
197  FileSystemBackend* backend = GetFileSystemBackend(type);
198  if (!backend)
199    return NULL;
200  return backend->GetAsyncFileUtil(type);
201}
202
203FileSystemFileUtil* FileSystemContext::GetFileUtil(
204    FileSystemType type) const {
205  FileSystemBackend* backend = GetFileSystemBackend(type);
206  if (!backend)
207    return NULL;
208  return backend->GetFileUtil(type);
209}
210
211CopyOrMoveFileValidatorFactory*
212FileSystemContext::GetCopyOrMoveFileValidatorFactory(
213    FileSystemType type, base::PlatformFileError* error_code) const {
214  DCHECK(error_code);
215  *error_code = base::PLATFORM_FILE_OK;
216  FileSystemBackend* backend = GetFileSystemBackend(type);
217  if (!backend)
218    return NULL;
219  return backend->GetCopyOrMoveFileValidatorFactory(
220      type, error_code);
221}
222
223FileSystemBackend* FileSystemContext::GetFileSystemBackend(
224    FileSystemType type) const {
225  FileSystemBackendMap::const_iterator found = backend_map_.find(type);
226  if (found != backend_map_.end())
227    return found->second;
228  NOTREACHED() << "Unknown filesystem type: " << type;
229  return NULL;
230}
231
232bool FileSystemContext::IsSandboxFileSystem(FileSystemType type) const {
233  return GetQuotaUtil(type) != NULL;
234}
235
236const UpdateObserverList* FileSystemContext::GetUpdateObservers(
237    FileSystemType type) const {
238  FileSystemBackend* backend = GetFileSystemBackend(type);
239  if (backend->GetQuotaUtil())
240    return backend->GetQuotaUtil()->GetUpdateObservers(type);
241  return NULL;
242}
243
244const AccessObserverList* FileSystemContext::GetAccessObservers(
245    FileSystemType type) const {
246  FileSystemBackend* backend = GetFileSystemBackend(type);
247  if (backend->GetQuotaUtil())
248    return backend->GetQuotaUtil()->GetAccessObservers(type);
249  return NULL;
250}
251
252void FileSystemContext::GetFileSystemTypes(
253    std::vector<FileSystemType>* types) const {
254  types->clear();
255  for (FileSystemBackendMap::const_iterator iter = backend_map_.begin();
256       iter != backend_map_.end(); ++iter)
257    types->push_back(iter->first);
258}
259
260ExternalFileSystemBackend*
261FileSystemContext::external_backend() const {
262  return static_cast<ExternalFileSystemBackend*>(
263      GetFileSystemBackend(kFileSystemTypeExternal));
264}
265
266void FileSystemContext::OpenFileSystem(
267    const GURL& origin_url,
268    FileSystemType type,
269    OpenFileSystemMode mode,
270    const OpenFileSystemCallback& callback) {
271  DCHECK(!callback.is_null());
272
273  FileSystemBackend* backend = GetFileSystemBackend(type);
274  if (!backend) {
275    callback.Run(base::PLATFORM_FILE_ERROR_SECURITY, std::string(), GURL());
276    return;
277  }
278
279  backend->OpenFileSystem(origin_url, type, mode,
280                          base::Bind(&DidOpenFileSystem, callback));
281}
282
283void FileSystemContext::DeleteFileSystem(
284    const GURL& origin_url,
285    FileSystemType type,
286    const DeleteFileSystemCallback& callback) {
287  DCHECK(origin_url == origin_url.GetOrigin());
288  FileSystemBackend* backend = GetFileSystemBackend(type);
289  if (!backend) {
290    callback.Run(base::PLATFORM_FILE_ERROR_SECURITY);
291    return;
292  }
293  if (!backend->GetQuotaUtil()) {
294    callback.Run(base::PLATFORM_FILE_ERROR_INVALID_OPERATION);
295    return;
296  }
297
298  base::PostTaskAndReplyWithResult(
299      default_file_task_runner(),
300      FROM_HERE,
301      // It is safe to pass Unretained(quota_util) since context owns it.
302      base::Bind(&FileSystemQuotaUtil::DeleteOriginDataOnFileThread,
303                 base::Unretained(backend->GetQuotaUtil()),
304                 make_scoped_refptr(this),
305                 base::Unretained(quota_manager_proxy()),
306                 origin_url,
307                 type),
308      callback);
309}
310
311scoped_ptr<webkit_blob::FileStreamReader>
312FileSystemContext::CreateFileStreamReader(
313    const FileSystemURL& url,
314    int64 offset,
315    const base::Time& expected_modification_time) {
316  if (!url.is_valid())
317    return scoped_ptr<webkit_blob::FileStreamReader>();
318  FileSystemBackend* backend = GetFileSystemBackend(url.type());
319  if (!backend)
320    return scoped_ptr<webkit_blob::FileStreamReader>();
321  return backend->CreateFileStreamReader(
322      url, offset, expected_modification_time, this);
323}
324
325scoped_ptr<FileStreamWriter> FileSystemContext::CreateFileStreamWriter(
326    const FileSystemURL& url,
327    int64 offset) {
328  if (!url.is_valid())
329    return scoped_ptr<FileStreamWriter>();
330  FileSystemBackend* backend = GetFileSystemBackend(url.type());
331  if (!backend)
332    return scoped_ptr<FileStreamWriter>();
333  return backend->CreateFileStreamWriter(url, offset, this);
334}
335
336scoped_ptr<FileSystemOperationRunner>
337FileSystemContext::CreateFileSystemOperationRunner() {
338  return make_scoped_ptr(new FileSystemOperationRunner(this));
339}
340
341FileSystemURL FileSystemContext::CrackURL(const GURL& url) const {
342  return CrackFileSystemURL(FileSystemURL(url));
343}
344
345FileSystemURL FileSystemContext::CreateCrackedFileSystemURL(
346    const GURL& origin,
347    FileSystemType type,
348    const base::FilePath& path) const {
349  return CrackFileSystemURL(FileSystemURL(origin, type, path));
350}
351
352#if defined(OS_CHROMEOS) && defined(GOOGLE_CHROME_BUILD)
353void FileSystemContext::EnableTemporaryFileSystemInIncognito() {
354  sandbox_backend_->set_enable_temporary_file_system_in_incognito(true);
355}
356#endif
357
358FileSystemContext::~FileSystemContext() {
359}
360
361void FileSystemContext::DeleteOnCorrectThread() const {
362  if (!io_task_runner_->RunsTasksOnCurrentThread() &&
363      io_task_runner_->DeleteSoon(FROM_HERE, this)) {
364    return;
365  }
366  delete this;
367}
368
369FileSystemOperation* FileSystemContext::CreateFileSystemOperation(
370    const FileSystemURL& url, base::PlatformFileError* error_code) {
371  if (!url.is_valid()) {
372    if (error_code)
373      *error_code = base::PLATFORM_FILE_ERROR_INVALID_URL;
374    return NULL;
375  }
376
377  FileSystemBackend* backend = GetFileSystemBackend(url.type());
378  if (!backend) {
379    if (error_code)
380      *error_code = base::PLATFORM_FILE_ERROR_FAILED;
381    return NULL;
382  }
383
384  base::PlatformFileError fs_error = base::PLATFORM_FILE_OK;
385  FileSystemOperation* operation =
386      backend->CreateFileSystemOperation(url, this, &fs_error);
387
388  if (error_code)
389    *error_code = fs_error;
390  return operation;
391}
392
393FileSystemURL FileSystemContext::CrackFileSystemURL(
394    const FileSystemURL& url) const {
395  if (!url.is_valid())
396    return FileSystemURL();
397
398  // The returned value in case there is no crackers which can crack the url.
399  // This is valid situation for non isolated/external file systems.
400  FileSystemURL current = url;
401
402  // File system may be mounted multiple times (e.g., an isolated filesystem on
403  // top of an external filesystem). Hence cracking needs to be iterated.
404  for (;;) {
405    FileSystemURL cracked = current;
406    for (size_t i = 0; i < url_crackers_.size(); ++i) {
407      if (!url_crackers_[i]->HandlesFileSystemMountType(current.type()))
408        continue;
409      cracked = url_crackers_[i]->CrackFileSystemURL(current);
410      if (cracked.is_valid())
411        break;
412    }
413    if (cracked == current)
414      break;
415    current = cracked;
416  }
417  return current;
418}
419
420void FileSystemContext::RegisterBackend(
421    FileSystemBackend* backend) {
422  const FileSystemType mount_types[] = {
423    kFileSystemTypeTemporary,
424    kFileSystemTypePersistent,
425    kFileSystemTypeIsolated,
426    kFileSystemTypeExternal,
427  };
428  // Register file system backends for public mount types.
429  for (size_t j = 0; j < ARRAYSIZE_UNSAFE(mount_types); ++j) {
430    if (backend->CanHandleType(mount_types[j])) {
431      const bool inserted = backend_map_.insert(
432          std::make_pair(mount_types[j], backend)).second;
433      DCHECK(inserted);
434    }
435  }
436  // Register file system backends for internal types.
437  for (int t = kFileSystemInternalTypeEnumStart + 1;
438       t < kFileSystemInternalTypeEnumEnd; ++t) {
439    FileSystemType type = static_cast<FileSystemType>(t);
440    if (backend->CanHandleType(type)) {
441      const bool inserted = backend_map_.insert(
442          std::make_pair(type, backend)).second;
443      DCHECK(inserted);
444    }
445  }
446}
447
448}  // namespace fileapi
449