root/webkit/browser/fileapi/file_system_context.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. CreateQuotaClient
  2. DidGetMetadataForResolveURL
  3. GetPermissionPolicy
  4. operation_runner_
  5. DeleteDataForOriginOnFileTaskRunner
  6. CreateQuotaReservationOnFileTaskRunner
  7. Shutdown
  8. GetQuotaUtil
  9. GetAsyncFileUtil
  10. GetCopyOrMoveFileValidatorFactory
  11. GetFileSystemBackend
  12. IsSandboxFileSystem
  13. GetUpdateObservers
  14. GetAccessObservers
  15. GetFileSystemTypes
  16. external_backend
  17. OpenFileSystem
  18. ResolveURL
  19. AttemptAutoMountForURLRequest
  20. DeleteFileSystem
  21. CreateFileStreamReader
  22. CreateFileStreamWriter
  23. CreateFileSystemOperationRunner
  24. CrackURL
  25. CreateCrackedFileSystemURL
  26. EnableTemporaryFileSystemInIncognito
  27. CanServeURLRequest
  28. OpenPluginPrivateFileSystem
  29. DeleteOnCorrectThread
  30. CreateFileSystemOperation
  31. CrackFileSystemURL
  32. RegisterBackend
  33. DidOpenFileSystemForResolveURL

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "webkit/browser/fileapi/file_system_context.h"

#include "base/bind.h"
#include "base/single_thread_task_runner.h"
#include "base/stl_util.h"
#include "base/task_runner_util.h"
#include "net/url_request/url_request.h"
#include "url/gurl.h"
#include "webkit/browser/blob/file_stream_reader.h"
#include "webkit/browser/fileapi/copy_or_move_file_validator.h"
#include "webkit/browser/fileapi/external_mount_points.h"
#include "webkit/browser/fileapi/file_permission_policy.h"
#include "webkit/browser/fileapi/file_stream_writer.h"
#include "webkit/browser/fileapi/file_system_file_util.h"
#include "webkit/browser/fileapi/file_system_operation.h"
#include "webkit/browser/fileapi/file_system_operation_runner.h"
#include "webkit/browser/fileapi/file_system_options.h"
#include "webkit/browser/fileapi/file_system_quota_client.h"
#include "webkit/browser/fileapi/file_system_url.h"
#include "webkit/browser/fileapi/isolated_context.h"
#include "webkit/browser/fileapi/isolated_file_system_backend.h"
#include "webkit/browser/fileapi/mount_points.h"
#include "webkit/browser/fileapi/quota/quota_reservation.h"
#include "webkit/browser/fileapi/sandbox_file_system_backend.h"
#include "webkit/browser/quota/quota_manager_proxy.h"
#include "webkit/browser/quota/special_storage_policy.h"
#include "webkit/common/fileapi/file_system_info.h"
#include "webkit/common/fileapi/file_system_util.h"

using quota::QuotaClient;

namespace fileapi {

namespace {

QuotaClient* CreateQuotaClient(
    FileSystemContext* context,
    bool is_incognito) {
  return new FileSystemQuotaClient(context, is_incognito);
}


void DidGetMetadataForResolveURL(
    const base::FilePath& path,
    const FileSystemContext::ResolveURLCallback& callback,
    const FileSystemInfo& info,
    base::File::Error error,
    const base::File::Info& file_info) {
  if (error != base::File::FILE_OK) {
    callback.Run(error, FileSystemInfo(), base::FilePath(), false);
    return;
  }
  callback.Run(error, info, path, file_info.is_directory);
}

}  // namespace

// static
int FileSystemContext::GetPermissionPolicy(FileSystemType type) {
  switch (type) {
    case kFileSystemTypeTemporary:
    case kFileSystemTypePersistent:
    case kFileSystemTypeSyncable:
      return FILE_PERMISSION_SANDBOX;

    case kFileSystemTypeDrive:
    case kFileSystemTypeNativeForPlatformApp:
    case kFileSystemTypeNativeLocal:
    case kFileSystemTypeCloudDevice:
    case kFileSystemTypeProvided:
      return FILE_PERMISSION_USE_FILE_PERMISSION;

    case kFileSystemTypeRestrictedNativeLocal:
      return FILE_PERMISSION_READ_ONLY |
             FILE_PERMISSION_USE_FILE_PERMISSION;

    case kFileSystemTypeDeviceMedia:
    case kFileSystemTypeIphoto:
    case kFileSystemTypeItunes:
    case kFileSystemTypeNativeMedia:
    case kFileSystemTypePicasa:
      return FILE_PERMISSION_USE_FILE_PERMISSION;

    // Following types are only accessed via IsolatedFileSystem, and
    // don't have their own permission policies.
    case kFileSystemTypeDragged:
    case kFileSystemTypeForTransientFile:
    case kFileSystemTypePluginPrivate:
      return FILE_PERMISSION_ALWAYS_DENY;

    // Following types only appear as mount_type, and will not be
    // queried for their permission policies.
    case kFileSystemTypeIsolated:
    case kFileSystemTypeExternal:
      return FILE_PERMISSION_ALWAYS_DENY;

    // Following types should not be used to access files by FileAPI clients.
    case kFileSystemTypeTest:
    case kFileSystemTypeSyncableForInternalSync:
    case kFileSystemInternalTypeEnumEnd:
    case kFileSystemInternalTypeEnumStart:
    case kFileSystemTypeUnknown:
      return FILE_PERMISSION_ALWAYS_DENY;
  }
  NOTREACHED();
  return FILE_PERMISSION_ALWAYS_DENY;
}

FileSystemContext::FileSystemContext(
    base::SingleThreadTaskRunner* io_task_runner,
    base::SequencedTaskRunner* file_task_runner,
    ExternalMountPoints* external_mount_points,
    quota::SpecialStoragePolicy* special_storage_policy,
    quota::QuotaManagerProxy* quota_manager_proxy,
    ScopedVector<FileSystemBackend> additional_backends,
    const std::vector<URLRequestAutoMountHandler>& auto_mount_handlers,
    const base::FilePath& partition_path,
    const FileSystemOptions& options)
    : io_task_runner_(io_task_runner),
      default_file_task_runner_(file_task_runner),
      quota_manager_proxy_(quota_manager_proxy),
      sandbox_delegate_(new SandboxFileSystemBackendDelegate(
          quota_manager_proxy,
          file_task_runner,
          partition_path,
          special_storage_policy,
          options)),
      sandbox_backend_(new SandboxFileSystemBackend(
          sandbox_delegate_.get())),
      isolated_backend_(new IsolatedFileSystemBackend()),
      plugin_private_backend_(new PluginPrivateFileSystemBackend(
          file_task_runner,
          partition_path,
          special_storage_policy,
          options)),
      additional_backends_(additional_backends.Pass()),
      auto_mount_handlers_(auto_mount_handlers),
      external_mount_points_(external_mount_points),
      partition_path_(partition_path),
      is_incognito_(options.is_incognito()),
      operation_runner_(new FileSystemOperationRunner(this)) {
  RegisterBackend(sandbox_backend_.get());
  RegisterBackend(isolated_backend_.get());
  RegisterBackend(plugin_private_backend_.get());

  for (ScopedVector<FileSystemBackend>::const_iterator iter =
          additional_backends_.begin();
       iter != additional_backends_.end(); ++iter) {
    RegisterBackend(*iter);
  }

  if (quota_manager_proxy) {
    // Quota client assumes all backends have registered.
    quota_manager_proxy->RegisterClient(CreateQuotaClient(
            this, options.is_incognito()));
  }

  sandbox_backend_->Initialize(this);
  isolated_backend_->Initialize(this);
  plugin_private_backend_->Initialize(this);
  for (ScopedVector<FileSystemBackend>::const_iterator iter =
          additional_backends_.begin();
       iter != additional_backends_.end(); ++iter) {
    (*iter)->Initialize(this);
  }

  // Additional mount points must be added before regular system-wide
  // mount points.
  if (external_mount_points)
    url_crackers_.push_back(external_mount_points);
  url_crackers_.push_back(ExternalMountPoints::GetSystemInstance());
  url_crackers_.push_back(IsolatedContext::GetInstance());
}

bool FileSystemContext::DeleteDataForOriginOnFileTaskRunner(
    const GURL& origin_url) {
  DCHECK(default_file_task_runner()->RunsTasksOnCurrentThread());
  DCHECK(origin_url == origin_url.GetOrigin());

  bool success = true;
  for (FileSystemBackendMap::iterator iter = backend_map_.begin();
       iter != backend_map_.end();
       ++iter) {
    FileSystemBackend* backend = iter->second;
    if (!backend->GetQuotaUtil())
      continue;
    if (backend->GetQuotaUtil()->DeleteOriginDataOnFileTaskRunner(
            this, quota_manager_proxy(), origin_url, iter->first)
            != base::File::FILE_OK) {
      // Continue the loop, but record the failure.
      success = false;
    }
  }

  return success;
}

scoped_refptr<QuotaReservation>
FileSystemContext::CreateQuotaReservationOnFileTaskRunner(
    const GURL& origin_url,
    FileSystemType type) {
  DCHECK(default_file_task_runner()->RunsTasksOnCurrentThread());
  FileSystemBackend* backend = GetFileSystemBackend(type);
  if (!backend || !backend->GetQuotaUtil())
    return scoped_refptr<QuotaReservation>();
  return backend->GetQuotaUtil()->CreateQuotaReservationOnFileTaskRunner(
      origin_url, type);
}

void FileSystemContext::Shutdown() {
  if (!io_task_runner_->RunsTasksOnCurrentThread()) {
    io_task_runner_->PostTask(
        FROM_HERE, base::Bind(&FileSystemContext::Shutdown,
                              make_scoped_refptr(this)));
    return;
  }
  operation_runner_->Shutdown();
}

FileSystemQuotaUtil*
FileSystemContext::GetQuotaUtil(FileSystemType type) const {
  FileSystemBackend* backend = GetFileSystemBackend(type);
  if (!backend)
    return NULL;
  return backend->GetQuotaUtil();
}

AsyncFileUtil* FileSystemContext::GetAsyncFileUtil(
    FileSystemType type) const {
  FileSystemBackend* backend = GetFileSystemBackend(type);
  if (!backend)
    return NULL;
  return backend->GetAsyncFileUtil(type);
}

CopyOrMoveFileValidatorFactory*
FileSystemContext::GetCopyOrMoveFileValidatorFactory(
    FileSystemType type, base::File::Error* error_code) const {
  DCHECK(error_code);
  *error_code = base::File::FILE_OK;
  FileSystemBackend* backend = GetFileSystemBackend(type);
  if (!backend)
    return NULL;
  return backend->GetCopyOrMoveFileValidatorFactory(
      type, error_code);
}

FileSystemBackend* FileSystemContext::GetFileSystemBackend(
    FileSystemType type) const {
  FileSystemBackendMap::const_iterator found = backend_map_.find(type);
  if (found != backend_map_.end())
    return found->second;
  NOTREACHED() << "Unknown filesystem type: " << type;
  return NULL;
}

bool FileSystemContext::IsSandboxFileSystem(FileSystemType type) const {
  FileSystemBackendMap::const_iterator found = backend_map_.find(type);
  return found != backend_map_.end() && found->second->GetQuotaUtil();
}

const UpdateObserverList* FileSystemContext::GetUpdateObservers(
    FileSystemType type) const {
  FileSystemBackend* backend = GetFileSystemBackend(type);
  if (backend->GetQuotaUtil())
    return backend->GetQuotaUtil()->GetUpdateObservers(type);
  return NULL;
}

const AccessObserverList* FileSystemContext::GetAccessObservers(
    FileSystemType type) const {
  FileSystemBackend* backend = GetFileSystemBackend(type);
  if (backend->GetQuotaUtil())
    return backend->GetQuotaUtil()->GetAccessObservers(type);
  return NULL;
}

void FileSystemContext::GetFileSystemTypes(
    std::vector<FileSystemType>* types) const {
  types->clear();
  for (FileSystemBackendMap::const_iterator iter = backend_map_.begin();
       iter != backend_map_.end(); ++iter)
    types->push_back(iter->first);
}

ExternalFileSystemBackend*
FileSystemContext::external_backend() const {
  return static_cast<ExternalFileSystemBackend*>(
      GetFileSystemBackend(kFileSystemTypeExternal));
}

void FileSystemContext::OpenFileSystem(
    const GURL& origin_url,
    FileSystemType type,
    OpenFileSystemMode mode,
    const OpenFileSystemCallback& callback) {
  DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
  DCHECK(!callback.is_null());

  if (!FileSystemContext::IsSandboxFileSystem(type)) {
    // Disallow opening a non-sandboxed filesystem.
    callback.Run(GURL(), std::string(), base::File::FILE_ERROR_SECURITY);
    return;
  }

  FileSystemBackend* backend = GetFileSystemBackend(type);
  if (!backend) {
    callback.Run(GURL(), std::string(), base::File::FILE_ERROR_SECURITY);
    return;
  }

  backend->ResolveURL(
      CreateCrackedFileSystemURL(origin_url, type, base::FilePath()),
      mode,
      callback);
}

void FileSystemContext::ResolveURL(
    const FileSystemURL& url,
    const ResolveURLCallback& callback) {
  // TODO(nhiroki, kinuko): Remove this thread restriction, so it can be called
  // on either UI or IO thread.
  DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
  DCHECK(!callback.is_null());

  FileSystemBackend* backend = GetFileSystemBackend(url.type());
  if (!backend) {
    callback.Run(base::File::FILE_ERROR_SECURITY,
                 FileSystemInfo(), base::FilePath(), false);
    return;
  }

  backend->ResolveURL(
      url,
      OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT,
      base::Bind(&FileSystemContext::DidOpenFileSystemForResolveURL,
                 this,
                 url,
                 callback));
}

void FileSystemContext::AttemptAutoMountForURLRequest(
    const net::URLRequest* url_request,
    const std::string& storage_domain,
    const StatusCallback& callback) {
  FileSystemURL filesystem_url(url_request->url());
  if (filesystem_url.type() == kFileSystemTypeExternal) {
    for (size_t i = 0; i < auto_mount_handlers_.size(); i++) {
      if (auto_mount_handlers_[i].Run(url_request, filesystem_url,
                                      storage_domain, callback)) {
        return;
      }
    }
  }
  callback.Run(base::File::FILE_ERROR_NOT_FOUND);
}

void FileSystemContext::DeleteFileSystem(
    const GURL& origin_url,
    FileSystemType type,
    const StatusCallback& callback) {
  DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
  DCHECK(origin_url == origin_url.GetOrigin());
  DCHECK(!callback.is_null());

  FileSystemBackend* backend = GetFileSystemBackend(type);
  if (!backend) {
    callback.Run(base::File::FILE_ERROR_SECURITY);
    return;
  }
  if (!backend->GetQuotaUtil()) {
    callback.Run(base::File::FILE_ERROR_INVALID_OPERATION);
    return;
  }

  base::PostTaskAndReplyWithResult(
      default_file_task_runner(),
      FROM_HERE,
      // It is safe to pass Unretained(quota_util) since context owns it.
      base::Bind(&FileSystemQuotaUtil::DeleteOriginDataOnFileTaskRunner,
                 base::Unretained(backend->GetQuotaUtil()),
                 make_scoped_refptr(this),
                 base::Unretained(quota_manager_proxy()),
                 origin_url,
                 type),
      callback);
}

scoped_ptr<webkit_blob::FileStreamReader>
FileSystemContext::CreateFileStreamReader(
    const FileSystemURL& url,
    int64 offset,
    const base::Time& expected_modification_time) {
  if (!url.is_valid())
    return scoped_ptr<webkit_blob::FileStreamReader>();
  FileSystemBackend* backend = GetFileSystemBackend(url.type());
  if (!backend)
    return scoped_ptr<webkit_blob::FileStreamReader>();
  return backend->CreateFileStreamReader(
      url, offset, expected_modification_time, this);
}

scoped_ptr<FileStreamWriter> FileSystemContext::CreateFileStreamWriter(
    const FileSystemURL& url,
    int64 offset) {
  if (!url.is_valid())
    return scoped_ptr<FileStreamWriter>();
  FileSystemBackend* backend = GetFileSystemBackend(url.type());
  if (!backend)
    return scoped_ptr<FileStreamWriter>();
  return backend->CreateFileStreamWriter(url, offset, this);
}

scoped_ptr<FileSystemOperationRunner>
FileSystemContext::CreateFileSystemOperationRunner() {
  return make_scoped_ptr(new FileSystemOperationRunner(this));
}

FileSystemURL FileSystemContext::CrackURL(const GURL& url) const {
  return CrackFileSystemURL(FileSystemURL(url));
}

FileSystemURL FileSystemContext::CreateCrackedFileSystemURL(
    const GURL& origin,
    FileSystemType type,
    const base::FilePath& path) const {
  return CrackFileSystemURL(FileSystemURL(origin, type, path));
}

#if defined(OS_CHROMEOS)
void FileSystemContext::EnableTemporaryFileSystemInIncognito() {
  sandbox_backend_->set_enable_temporary_file_system_in_incognito(true);
}
#endif

bool FileSystemContext::CanServeURLRequest(const FileSystemURL& url) const {
  // We never support accessing files in isolated filesystems via an URL.
  if (url.mount_type() == kFileSystemTypeIsolated)
    return false;
#if defined(OS_CHROMEOS)
  if (url.type() == kFileSystemTypeTemporary &&
      sandbox_backend_->enable_temporary_file_system_in_incognito()) {
    return true;
  }
#endif
  return !is_incognito_ || !FileSystemContext::IsSandboxFileSystem(url.type());
}

void FileSystemContext::OpenPluginPrivateFileSystem(
    const GURL& origin_url,
    FileSystemType type,
    const std::string& filesystem_id,
    const std::string& plugin_id,
    OpenFileSystemMode mode,
    const StatusCallback& callback) {
  DCHECK(plugin_private_backend_);
  plugin_private_backend_->OpenPrivateFileSystem(
      origin_url, type, filesystem_id, plugin_id, mode, callback);
}

FileSystemContext::~FileSystemContext() {
}

void FileSystemContext::DeleteOnCorrectThread() const {
  if (!io_task_runner_->RunsTasksOnCurrentThread() &&
      io_task_runner_->DeleteSoon(FROM_HERE, this)) {
    return;
  }
  delete this;
}

FileSystemOperation* FileSystemContext::CreateFileSystemOperation(
    const FileSystemURL& url, base::File::Error* error_code) {
  if (!url.is_valid()) {
    if (error_code)
      *error_code = base::File::FILE_ERROR_INVALID_URL;
    return NULL;
  }

  FileSystemBackend* backend = GetFileSystemBackend(url.type());
  if (!backend) {
    if (error_code)
      *error_code = base::File::FILE_ERROR_FAILED;
    return NULL;
  }

  base::File::Error fs_error = base::File::FILE_OK;
  FileSystemOperation* operation =
      backend->CreateFileSystemOperation(url, this, &fs_error);

  if (error_code)
    *error_code = fs_error;
  return operation;
}

FileSystemURL FileSystemContext::CrackFileSystemURL(
    const FileSystemURL& url) const {
  if (!url.is_valid())
    return FileSystemURL();

  // The returned value in case there is no crackers which can crack the url.
  // This is valid situation for non isolated/external file systems.
  FileSystemURL current = url;

  // File system may be mounted multiple times (e.g., an isolated filesystem on
  // top of an external filesystem). Hence cracking needs to be iterated.
  for (;;) {
    FileSystemURL cracked = current;
    for (size_t i = 0; i < url_crackers_.size(); ++i) {
      if (!url_crackers_[i]->HandlesFileSystemMountType(current.type()))
        continue;
      cracked = url_crackers_[i]->CrackFileSystemURL(current);
      if (cracked.is_valid())
        break;
    }
    if (cracked == current)
      break;
    current = cracked;
  }
  return current;
}

void FileSystemContext::RegisterBackend(FileSystemBackend* backend) {
  const FileSystemType mount_types[] = {
    kFileSystemTypeTemporary,
    kFileSystemTypePersistent,
    kFileSystemTypeIsolated,
    kFileSystemTypeExternal,
  };
  // Register file system backends for public mount types.
  for (size_t j = 0; j < ARRAYSIZE_UNSAFE(mount_types); ++j) {
    if (backend->CanHandleType(mount_types[j])) {
      const bool inserted = backend_map_.insert(
          std::make_pair(mount_types[j], backend)).second;
      DCHECK(inserted);
    }
  }
  // Register file system backends for internal types.
  for (int t = kFileSystemInternalTypeEnumStart + 1;
       t < kFileSystemInternalTypeEnumEnd; ++t) {
    FileSystemType type = static_cast<FileSystemType>(t);
    if (backend->CanHandleType(type)) {
      const bool inserted = backend_map_.insert(
          std::make_pair(type, backend)).second;
      DCHECK(inserted);
    }
  }
}

void FileSystemContext::DidOpenFileSystemForResolveURL(
    const FileSystemURL& url,
    const FileSystemContext::ResolveURLCallback& callback,
    const GURL& filesystem_root,
    const std::string& filesystem_name,
    base::File::Error error) {
  DCHECK(io_task_runner_->RunsTasksOnCurrentThread());

  if (error != base::File::FILE_OK) {
    callback.Run(error, FileSystemInfo(), base::FilePath(), false);
    return;
  }

  fileapi::FileSystemInfo info(
      filesystem_name, filesystem_root, url.mount_type());

  // Extract the virtual path not containing a filesystem type part from |url|.
  base::FilePath parent = CrackURL(filesystem_root).virtual_path();
  base::FilePath child = url.virtual_path();
  base::FilePath path;

  if (parent.empty()) {
    path = child;
  } else if (parent != child) {
    bool result = parent.AppendRelativePath(child, &path);
    DCHECK(result);
  }

  operation_runner()->GetMetadata(
      url, base::Bind(&DidGetMetadataForResolveURL, path, callback, info));
}

}  // namespace fileapi

/* [<][>][^][v][top][bottom][index][help] */