text: // Copyright 2013 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 "storage/browser/fileapi/plugin_private_file_system_backend.h"
  
  #include <map>
  
  #include "base/stl_util.h"
  #include "base/synchronization/lock.h"
  #include "base/task_runner_util.h"
  #include "base/thread_task_runner_handle.h"
  #include "net/base/net_util.h"
  #include "storage/browser/fileapi/async_file_util_adapter.h"
  #include "storage/browser/fileapi/file_stream_reader.h"
  #include "storage/browser/fileapi/file_stream_writer.h"
  #include "storage/browser/fileapi/file_system_context.h"
  #include "storage/browser/fileapi/file_system_operation.h"
  #include "storage/browser/fileapi/file_system_operation_context.h"
  #include "storage/browser/fileapi/isolated_context.h"
  #include "storage/browser/fileapi/obfuscated_file_util.h"
  #include "storage/browser/fileapi/quota/quota_reservation.h"
  #include "storage/common/fileapi/file_system_util.h"
  
  namespace storage {
  
  class PluginPrivateFileSystemBackend::FileSystemIDToPluginMap {
   public:
    explicit FileSystemIDToPluginMap(base::SequencedTaskRunner* task_runner)
        : task_runner_(task_runner) {}
    ~FileSystemIDToPluginMap() {}
  
    std::string GetPluginIDForURL(const FileSystemURL& url) {
      DCHECK(task_runner_->RunsTasksOnCurrentThread());
      Map::iterator found = map_.find(url.filesystem_id());
      if (url.type() != kFileSystemTypePluginPrivate || found == map_.end()) {
        NOTREACHED() << "Unsupported url is given: " << url.DebugString();
        return std::string();
      }
      return found->second;
    }
  
    void RegisterFileSystem(const std::string& filesystem_id,
                            const std::string& plugin_id) {
      DCHECK(task_runner_->RunsTasksOnCurrentThread());
      DCHECK(!filesystem_id.empty());
      DCHECK(!ContainsKey(map_, filesystem_id)) << filesystem_id;
      map_[filesystem_id] = plugin_id;
    }
  
    void RemoveFileSystem(const std::string& filesystem_id) {
      DCHECK(task_runner_->RunsTasksOnCurrentThread());
      map_.erase(filesystem_id);
    }
  
   private:
    typedef std::map<std::string, std::string> Map;
    scoped_refptr<base::SequencedTaskRunner> task_runner_;
    Map map_;
  };
  
  namespace {
  
  const base::FilePath::CharType* kFileSystemDirectory =
      SandboxFileSystemBackendDelegate::kFileSystemDirectory;
  const base::FilePath::CharType* kPluginPrivateDirectory =
      FILE_PATH_LITERAL("Plugins");
  
  base::File::Error OpenFileSystemOnFileTaskRunner(
      ObfuscatedFileUtil* file_util,
      PluginPrivateFileSystemBackend::FileSystemIDToPluginMap* plugin_map,
      const GURL& origin_url,
      const std::string& filesystem_id,
      const std::string& plugin_id,
      OpenFileSystemMode mode) {
    base::File::Error error = base::File::FILE_ERROR_FAILED;
    const bool create = (mode == OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT);
    file_util->GetDirectoryForOriginAndType(
        origin_url, plugin_id, create, &error);
    if (error == base::File::FILE_OK)
      plugin_map->RegisterFileSystem(filesystem_id, plugin_id);
    return error;
  }
  
  }  // namespace
  
  PluginPrivateFileSystemBackend::PluginPrivateFileSystemBackend(
      base::SequencedTaskRunner* file_task_runner,
      const base::FilePath& profile_path,
      storage::SpecialStoragePolicy* special_storage_policy,
      const FileSystemOptions& file_system_options)
      : file_task_runner_(file_task_runner),
        file_system_options_(file_system_options),
        base_path_(profile_path.Append(kFileSystemDirectory)
                       .Append(kPluginPrivateDirectory)),
        plugin_map_(new FileSystemIDToPluginMap(file_task_runner)),
        weak_factory_(this) {
    file_util_.reset(
        new AsyncFileUtilAdapter(new ObfuscatedFileUtil(
            special_storage_policy,
            base_path_, file_system_options.env_override(),
            file_task_runner,
            base::Bind(&FileSystemIDToPluginMap::GetPluginIDForURL,
                       base::Owned(plugin_map_)),
            std::set<std::string>(),
            NULL)));
  }
  
  PluginPrivateFileSystemBackend::~PluginPrivateFileSystemBackend() {
    if (!file_task_runner_->RunsTasksOnCurrentThread()) {
      AsyncFileUtil* file_util = file_util_.release();
      if (!file_task_runner_->DeleteSoon(FROM_HERE, file_util))
        delete file_util;
    }
  }
  
  void PluginPrivateFileSystemBackend::OpenPrivateFileSystem(
      const GURL& origin_url,
      FileSystemType type,
      const std::string& filesystem_id,
      const std::string& plugin_id,
      OpenFileSystemMode mode,
      const StatusCallback& callback) {
    if (!CanHandleType(type) || file_system_options_.is_incognito()) {
      base::ThreadTaskRunnerHandle::Get()->PostTask(
          FROM_HERE, base::Bind(callback, base::File::FILE_ERROR_SECURITY));
      return;
    }
  
    PostTaskAndReplyWithResult(
        file_task_runner_.get(),
        FROM_HERE,
        base::Bind(&OpenFileSystemOnFileTaskRunner,
                   obfuscated_file_util(), plugin_map_,
                   origin_url, filesystem_id, plugin_id, mode),
        callback);
  }
  
  bool PluginPrivateFileSystemBackend::CanHandleType(FileSystemType type) const {
    return type == kFileSystemTypePluginPrivate;
  }
  
  void PluginPrivateFileSystemBackend::Initialize(FileSystemContext* context) {
  }
  
  void PluginPrivateFileSystemBackend::ResolveURL(
      const FileSystemURL& url,
      OpenFileSystemMode mode,
      const OpenFileSystemCallback& callback) {
    // We never allow opening a new plugin-private filesystem via usual
    // ResolveURL.
    base::ThreadTaskRunnerHandle::Get()->PostTask(
        FROM_HERE,
        base::Bind(callback, GURL(), std::string(),
                   base::File::FILE_ERROR_SECURITY));
  }
  
  AsyncFileUtil*
  PluginPrivateFileSystemBackend::GetAsyncFileUtil(FileSystemType type) {
    return file_util_.get();
  }
  
  WatcherManager* PluginPrivateFileSystemBackend::GetWatcherManager(
      FileSystemType type) {
    return NULL;
  }
  
  CopyOrMoveFileValidatorFactory*
  PluginPrivateFileSystemBackend::GetCopyOrMoveFileValidatorFactory(
      FileSystemType type,
      base::File::Error* error_code) {
    DCHECK(error_code);
    *error_code = base::File::FILE_OK;
    return NULL;
  }
  
  FileSystemOperation* PluginPrivateFileSystemBackend::CreateFileSystemOperation(
      const FileSystemURL& url,
      FileSystemContext* context,
      base::File::Error* error_code) const {
    scoped_ptr<FileSystemOperationContext> operation_context(
        new FileSystemOperationContext(context));
    return FileSystemOperation::Create(url, context, operation_context.Pass());
  }
  
  bool PluginPrivateFileSystemBackend::SupportsStreaming(
      const storage::FileSystemURL& url) const {
    return false;
  }
  
  bool PluginPrivateFileSystemBackend::HasInplaceCopyImplementation(
      storage::FileSystemType type) const {
    return false;
  }
  
  scoped_ptr<storage::FileStreamReader>
  PluginPrivateFileSystemBackend::CreateFileStreamReader(
      const FileSystemURL& url,
      int64 offset,
      int64 max_bytes_to_read,
      const base::Time& expected_modification_time,
      FileSystemContext* context) const {
    return scoped_ptr<storage::FileStreamReader>();
  }
  
  scoped_ptr<FileStreamWriter>
  PluginPrivateFileSystemBackend::CreateFileStreamWriter(
      const FileSystemURL& url,
      int64 offset,
      FileSystemContext* context) const {
    return scoped_ptr<FileStreamWriter>();
  }
  
  FileSystemQuotaUtil* PluginPrivateFileSystemBackend::GetQuotaUtil() {
    return this;
  }
  
  base::File::Error
  PluginPrivateFileSystemBackend::DeleteOriginDataOnFileTaskRunner(
      FileSystemContext* context,
      storage::QuotaManagerProxy* proxy,
      const GURL& origin_url,
      FileSystemType type) {
    if (!CanHandleType(type))
      return base::File::FILE_ERROR_SECURITY;
    bool result = obfuscated_file_util()->DeleteDirectoryForOriginAndType(
        origin_url, std::string());
    if (result)
      return base::File::FILE_OK;
    return base::File::FILE_ERROR_FAILED;
  }
  
  void PluginPrivateFileSystemBackend::GetOriginsForTypeOnFileTaskRunner(
      FileSystemType type,
      std::set<GURL>* origins) {
    if (!CanHandleType(type))
      return;
    scoped_ptr<ObfuscatedFileUtil::AbstractOriginEnumerator> enumerator(
        obfuscated_file_util()->CreateOriginEnumerator());
    GURL origin;
    while (!(origin = enumerator->Next()).is_empty())
      origins->insert(origin);
  }
  
  void PluginPrivateFileSystemBackend::GetOriginsForHostOnFileTaskRunner(
      FileSystemType type,
      const std::string& host,
      std::set<GURL>* origins) {
    if (!CanHandleType(type))
      return;
    scoped_ptr<ObfuscatedFileUtil::AbstractOriginEnumerator> enumerator(
        obfuscated_file_util()->CreateOriginEnumerator());
    GURL origin;
    while (!(origin = enumerator->Next()).is_empty()) {
      if (host == net::GetHostOrSpecFromURL(origin))
        origins->insert(origin);
    }
  }
  
  int64 PluginPrivateFileSystemBackend::GetOriginUsageOnFileTaskRunner(
      FileSystemContext* context,
      const GURL& origin_url,
      FileSystemType type) {
    // We don't track usage on this filesystem.
    return 0;
  }
  
  scoped_refptr<QuotaReservation>
  PluginPrivateFileSystemBackend::CreateQuotaReservationOnFileTaskRunner(
      const GURL& origin_url,
      FileSystemType type) {
    // We don't track usage on this filesystem.
    NOTREACHED();
    return scoped_refptr<QuotaReservation>();
  }
  
  const UpdateObserverList* PluginPrivateFileSystemBackend::GetUpdateObservers(
      FileSystemType type) const {
    return NULL;
  }
  
  const ChangeObserverList* PluginPrivateFileSystemBackend::GetChangeObservers(
      FileSystemType type) const {
    return NULL;
  }
  
  const AccessObserverList* PluginPrivateFileSystemBackend::GetAccessObservers(
      FileSystemType type) const {
    return NULL;
  }
  
  ObfuscatedFileUtil* PluginPrivateFileSystemBackend::obfuscated_file_util() {
    return static_cast<ObfuscatedFileUtil*>(
        static_cast<AsyncFileUtilAdapter*>(file_util_.get())->sync_file_util());
  }
  
  }  // namespace storage
  