file_system_backend.cc revision 68043e1e95eeb07d5cae7aca370b26518b0867d6
1// Copyright 2013 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 "chrome/browser/chromeos/fileapi/file_system_backend.h"
6
7#include "base/logging.h"
8#include "base/memory/scoped_ptr.h"
9#include "base/message_loop/message_loop.h"
10#include "base/path_service.h"
11#include "base/strings/stringprintf.h"
12#include "base/strings/utf_string_conversions.h"
13#include "base/synchronization/lock.h"
14#include "chrome/browser/chromeos/fileapi/file_access_permissions.h"
15#include "chrome/browser/chromeos/fileapi/file_system_backend_delegate.h"
16#include "chromeos/dbus/cros_disks_client.h"
17#include "webkit/browser/blob/file_stream_reader.h"
18#include "webkit/browser/fileapi/async_file_util.h"
19#include "webkit/browser/fileapi/external_mount_points.h"
20#include "webkit/browser/fileapi/file_stream_writer.h"
21#include "webkit/browser/fileapi/file_system_context.h"
22#include "webkit/browser/fileapi/file_system_operation.h"
23#include "webkit/browser/fileapi/file_system_operation_context.h"
24#include "webkit/browser/fileapi/file_system_url.h"
25#include "webkit/browser/fileapi/isolated_context.h"
26
27namespace {
28
29const char kChromeUIScheme[] = "chrome";
30
31}  // namespace
32
33namespace chromeos {
34
35// static
36bool FileSystemBackend::CanHandleURL(const fileapi::FileSystemURL& url) {
37  if (!url.is_valid())
38    return false;
39  return url.type() == fileapi::kFileSystemTypeNativeLocal ||
40         url.type() == fileapi::kFileSystemTypeRestrictedNativeLocal ||
41         url.type() == fileapi::kFileSystemTypeDrive;
42}
43
44FileSystemBackend::FileSystemBackend(
45    FileSystemBackendDelegate* drive_delegate,
46    scoped_refptr<quota::SpecialStoragePolicy> special_storage_policy,
47    scoped_refptr<fileapi::ExternalMountPoints> mount_points,
48    fileapi::ExternalMountPoints* system_mount_points)
49    : special_storage_policy_(special_storage_policy),
50      file_access_permissions_(new FileAccessPermissions()),
51      local_file_util_(fileapi::AsyncFileUtil::CreateForLocalFileSystem()),
52      drive_delegate_(drive_delegate),
53      mount_points_(mount_points),
54      system_mount_points_(system_mount_points) {
55}
56
57FileSystemBackend::~FileSystemBackend() {
58}
59
60void FileSystemBackend::AddSystemMountPoints() {
61  // RegisterFileSystem() is no-op if the mount point with the same name
62  // already exists, hence it's safe to call without checking if a mount
63  // point already exists or not.
64
65  // TODO(satorux): "Downloads" directory should probably be per-profile. For
66  // this to be per-profile, a unique directory path should be chosen per
67  // profile, and the mount point should be added to
68  // mount_points_. crbug.com/247236
69  base::FilePath home_path;
70  if (PathService::Get(base::DIR_HOME, &home_path)) {
71    system_mount_points_->RegisterFileSystem(
72        "Downloads",
73        fileapi::kFileSystemTypeNativeLocal,
74        home_path.AppendASCII("Downloads"));
75  }
76
77  system_mount_points_->RegisterFileSystem(
78      "archive",
79      fileapi::kFileSystemTypeNativeLocal,
80      chromeos::CrosDisksClient::GetArchiveMountPoint());
81  system_mount_points_->RegisterFileSystem(
82      "removable",
83      fileapi::kFileSystemTypeNativeLocal,
84      chromeos::CrosDisksClient::GetRemovableDiskMountPoint());
85  system_mount_points_->RegisterFileSystem(
86      "oem",
87      fileapi::kFileSystemTypeRestrictedNativeLocal,
88      base::FilePath(FILE_PATH_LITERAL("/usr/share/oem")));
89}
90
91bool FileSystemBackend::CanHandleType(fileapi::FileSystemType type) const {
92  switch (type) {
93    case fileapi::kFileSystemTypeExternal:
94    case fileapi::kFileSystemTypeDrive:
95    case fileapi::kFileSystemTypeRestrictedNativeLocal:
96    case fileapi::kFileSystemTypeNativeLocal:
97    case fileapi::kFileSystemTypeNativeForPlatformApp:
98      return true;
99    default:
100      return false;
101  }
102}
103
104void FileSystemBackend::Initialize(fileapi::FileSystemContext* context) {
105}
106
107void FileSystemBackend::OpenFileSystem(
108    const GURL& origin_url,
109    fileapi::FileSystemType type,
110    fileapi::OpenFileSystemMode mode,
111    const OpenFileSystemCallback& callback) {
112  DCHECK(CanHandleType(type));
113  // TODO(nhiroki): Avoid this hard-coded mount type to support multiple
114  // filesystems (http://crbug.com/297412).
115  fileapi::FileSystemType mount_type = fileapi::kFileSystemTypeExternal;
116  // Nothing to validate for external filesystem.
117  callback.Run(GetFileSystemRootURI(origin_url, mount_type),
118               GetFileSystemName(origin_url, mount_type),
119               base::PLATFORM_FILE_OK);
120}
121
122fileapi::FileSystemQuotaUtil* FileSystemBackend::GetQuotaUtil() {
123  // No quota support.
124  return NULL;
125}
126
127bool FileSystemBackend::IsAccessAllowed(
128    const fileapi::FileSystemURL& url) const {
129  if (!url.is_valid())
130    return false;
131
132  // Permit access to mount points from internal WebUI.
133  const GURL& origin_url = url.origin();
134  if (origin_url.SchemeIs(kChromeUIScheme))
135    return true;
136
137  // No extra check is needed for isolated file systems.
138  if (url.mount_type() == fileapi::kFileSystemTypeIsolated)
139    return true;
140
141  if (!CanHandleURL(url))
142    return false;
143
144  std::string extension_id = origin_url.host();
145  // TODO(mtomasz): Temporarily whitelist TimeScapes. Remove this in M-31.
146  // See: crbug.com/271946
147  if (extension_id == "mlbmkoenclnokonejhlfakkeabdlmpek" &&
148      url.type() == fileapi::kFileSystemTypeRestrictedNativeLocal) {
149    return true;
150  }
151
152  // Check first to make sure this extension has fileBrowserHander permissions.
153  if (!special_storage_policy_->IsFileHandler(extension_id))
154    return false;
155
156  return file_access_permissions_->HasAccessPermission(extension_id,
157                                                       url.virtual_path());
158}
159
160void FileSystemBackend::GrantFullAccessToExtension(
161    const std::string& extension_id) {
162  DCHECK(special_storage_policy_->IsFileHandler(extension_id));
163  if (!special_storage_policy_->IsFileHandler(extension_id))
164    return;
165
166  std::vector<fileapi::MountPoints::MountPointInfo> files;
167  mount_points_->AddMountPointInfosTo(&files);
168  system_mount_points_->AddMountPointInfosTo(&files);
169
170  for (size_t i = 0; i < files.size(); ++i) {
171    file_access_permissions_->GrantAccessPermission(
172        extension_id,
173        base::FilePath::FromUTF8Unsafe(files[i].name));
174  }
175}
176
177void FileSystemBackend::GrantFileAccessToExtension(
178    const std::string& extension_id, const base::FilePath& virtual_path) {
179  // All we care about here is access from extensions for now.
180  DCHECK(special_storage_policy_->IsFileHandler(extension_id));
181  if (!special_storage_policy_->IsFileHandler(extension_id))
182    return;
183
184  std::string id;
185  fileapi::FileSystemType type;
186  base::FilePath path;
187  if (!mount_points_->CrackVirtualPath(virtual_path, &id, &type, &path) &&
188      !system_mount_points_->CrackVirtualPath(virtual_path,
189                                              &id, &type, &path)) {
190    return;
191  }
192
193  if (type == fileapi::kFileSystemTypeRestrictedNativeLocal) {
194    LOG(ERROR) << "Can't grant access for restricted mount point";
195    return;
196  }
197
198  file_access_permissions_->GrantAccessPermission(extension_id, virtual_path);
199}
200
201void FileSystemBackend::RevokeAccessForExtension(
202      const std::string& extension_id) {
203  file_access_permissions_->RevokePermissions(extension_id);
204}
205
206std::vector<base::FilePath> FileSystemBackend::GetRootDirectories() const {
207  std::vector<fileapi::MountPoints::MountPointInfo> mount_points;
208  mount_points_->AddMountPointInfosTo(&mount_points);
209  system_mount_points_->AddMountPointInfosTo(&mount_points);
210
211  std::vector<base::FilePath> root_dirs;
212  for (size_t i = 0; i < mount_points.size(); ++i)
213    root_dirs.push_back(mount_points[i].path);
214  return root_dirs;
215}
216
217fileapi::AsyncFileUtil* FileSystemBackend::GetAsyncFileUtil(
218    fileapi::FileSystemType type) {
219  if (type == fileapi::kFileSystemTypeDrive)
220    return drive_delegate_->GetAsyncFileUtil(type);
221
222  DCHECK(type == fileapi::kFileSystemTypeNativeLocal ||
223         type == fileapi::kFileSystemTypeRestrictedNativeLocal);
224  return local_file_util_.get();
225}
226
227fileapi::CopyOrMoveFileValidatorFactory*
228FileSystemBackend::GetCopyOrMoveFileValidatorFactory(
229    fileapi::FileSystemType type, base::PlatformFileError* error_code) {
230  DCHECK(error_code);
231  *error_code = base::PLATFORM_FILE_OK;
232  return NULL;
233}
234
235fileapi::FileSystemOperation* FileSystemBackend::CreateFileSystemOperation(
236    const fileapi::FileSystemURL& url,
237    fileapi::FileSystemContext* context,
238    base::PlatformFileError* error_code) const {
239  DCHECK(url.is_valid());
240
241  if (!IsAccessAllowed(url)) {
242    *error_code = base::PLATFORM_FILE_ERROR_SECURITY;
243    return NULL;
244  }
245
246  DCHECK(url.type() == fileapi::kFileSystemTypeNativeLocal ||
247         url.type() == fileapi::kFileSystemTypeRestrictedNativeLocal ||
248         url.type() == fileapi::kFileSystemTypeDrive);
249  return fileapi::FileSystemOperation::Create(
250      url, context,
251      make_scoped_ptr(new fileapi::FileSystemOperationContext(context)));
252}
253
254scoped_ptr<webkit_blob::FileStreamReader>
255FileSystemBackend::CreateFileStreamReader(
256    const fileapi::FileSystemURL& url,
257    int64 offset,
258    const base::Time& expected_modification_time,
259    fileapi::FileSystemContext* context) const {
260  DCHECK(url.is_valid());
261
262  if (!IsAccessAllowed(url))
263    return scoped_ptr<webkit_blob::FileStreamReader>();
264
265  if (url.type() == fileapi::kFileSystemTypeDrive) {
266    return drive_delegate_->CreateFileStreamReader(
267        url, offset, expected_modification_time, context);
268  }
269
270  return scoped_ptr<webkit_blob::FileStreamReader>(
271      webkit_blob::FileStreamReader::CreateForFileSystemFile(
272          context, url, offset, expected_modification_time));
273}
274
275scoped_ptr<fileapi::FileStreamWriter>
276FileSystemBackend::CreateFileStreamWriter(
277    const fileapi::FileSystemURL& url,
278    int64 offset,
279    fileapi::FileSystemContext* context) const {
280  DCHECK(url.is_valid());
281
282  if (!IsAccessAllowed(url))
283    return scoped_ptr<fileapi::FileStreamWriter>();
284
285  if (url.type() == fileapi::kFileSystemTypeDrive)
286    return drive_delegate_->CreateFileStreamWriter(url, offset, context);
287
288  if (url.type() == fileapi::kFileSystemTypeRestrictedNativeLocal)
289    return scoped_ptr<fileapi::FileStreamWriter>();
290
291  DCHECK(url.type() == fileapi::kFileSystemTypeNativeLocal);
292  return scoped_ptr<fileapi::FileStreamWriter>(
293      fileapi::FileStreamWriter::CreateForLocalFile(
294          context->default_file_task_runner(), url.path(), offset));
295}
296
297bool FileSystemBackend::GetVirtualPath(
298    const base::FilePath& filesystem_path,
299    base::FilePath* virtual_path) {
300  return mount_points_->GetVirtualPath(filesystem_path, virtual_path) ||
301         system_mount_points_->GetVirtualPath(filesystem_path, virtual_path);
302}
303
304}  // namespace chromeos
305