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/strings/stringprintf.h"
10#include "chrome/browser/chromeos/fileapi/file_access_permissions.h"
11#include "chrome/browser/chromeos/fileapi/file_system_backend_delegate.h"
12#include "chrome/browser/media_galleries/fileapi/media_file_system_backend.h"
13#include "chrome/common/url_constants.h"
14#include "chromeos/dbus/cros_disks_client.h"
15#include "storage/browser/blob/file_stream_reader.h"
16#include "storage/browser/fileapi/async_file_util.h"
17#include "storage/browser/fileapi/external_mount_points.h"
18#include "storage/browser/fileapi/file_stream_writer.h"
19#include "storage/browser/fileapi/file_system_context.h"
20#include "storage/browser/fileapi/file_system_operation.h"
21#include "storage/browser/fileapi/file_system_operation_context.h"
22#include "storage/browser/fileapi/file_system_url.h"
23
24namespace chromeos {
25
26// static
27bool FileSystemBackend::CanHandleURL(const storage::FileSystemURL& url) {
28  if (!url.is_valid())
29    return false;
30  return url.type() == storage::kFileSystemTypeNativeLocal ||
31         url.type() == storage::kFileSystemTypeRestrictedNativeLocal ||
32         url.type() == storage::kFileSystemTypeDrive ||
33         url.type() == storage::kFileSystemTypeProvided ||
34         url.type() == storage::kFileSystemTypeDeviceMediaAsFileStorage;
35}
36
37FileSystemBackend::FileSystemBackend(
38    FileSystemBackendDelegate* drive_delegate,
39    FileSystemBackendDelegate* file_system_provider_delegate,
40    FileSystemBackendDelegate* mtp_delegate,
41    scoped_refptr<storage::SpecialStoragePolicy> special_storage_policy,
42    scoped_refptr<storage::ExternalMountPoints> mount_points,
43    storage::ExternalMountPoints* system_mount_points)
44    : special_storage_policy_(special_storage_policy),
45      file_access_permissions_(new FileAccessPermissions()),
46      local_file_util_(storage::AsyncFileUtil::CreateForLocalFileSystem()),
47      drive_delegate_(drive_delegate),
48      file_system_provider_delegate_(file_system_provider_delegate),
49      mtp_delegate_(mtp_delegate),
50      mount_points_(mount_points),
51      system_mount_points_(system_mount_points) {
52}
53
54FileSystemBackend::~FileSystemBackend() {
55}
56
57void FileSystemBackend::AddSystemMountPoints() {
58  // RegisterFileSystem() is no-op if the mount point with the same name
59  // already exists, hence it's safe to call without checking if a mount
60  // point already exists or not.
61  system_mount_points_->RegisterFileSystem(
62      "archive",
63      storage::kFileSystemTypeNativeLocal,
64      storage::FileSystemMountOption(),
65      chromeos::CrosDisksClient::GetArchiveMountPoint());
66  system_mount_points_->RegisterFileSystem(
67      "removable",
68      storage::kFileSystemTypeNativeLocal,
69      storage::FileSystemMountOption(storage::COPY_SYNC_OPTION_SYNC),
70      chromeos::CrosDisksClient::GetRemovableDiskMountPoint());
71  system_mount_points_->RegisterFileSystem(
72      "oem",
73      storage::kFileSystemTypeRestrictedNativeLocal,
74      storage::FileSystemMountOption(),
75      base::FilePath(FILE_PATH_LITERAL("/usr/share/oem")));
76}
77
78bool FileSystemBackend::CanHandleType(storage::FileSystemType type) const {
79  switch (type) {
80    case storage::kFileSystemTypeExternal:
81    case storage::kFileSystemTypeDrive:
82    case storage::kFileSystemTypeRestrictedNativeLocal:
83    case storage::kFileSystemTypeNativeLocal:
84    case storage::kFileSystemTypeNativeForPlatformApp:
85    case storage::kFileSystemTypeDeviceMediaAsFileStorage:
86    case storage::kFileSystemTypeProvided:
87      return true;
88    default:
89      return false;
90  }
91}
92
93void FileSystemBackend::Initialize(storage::FileSystemContext* context) {
94}
95
96void FileSystemBackend::ResolveURL(const storage::FileSystemURL& url,
97                                   storage::OpenFileSystemMode mode,
98                                   const OpenFileSystemCallback& callback) {
99  std::string id;
100  storage::FileSystemType type;
101  std::string cracked_id;
102  base::FilePath path;
103  storage::FileSystemMountOption option;
104  if (!mount_points_->CrackVirtualPath(
105           url.virtual_path(), &id, &type, &cracked_id, &path, &option) &&
106      !system_mount_points_->CrackVirtualPath(
107           url.virtual_path(), &id, &type, &cracked_id, &path, &option)) {
108    // Not under a mount point, so return an error, since the root is not
109    // accessible.
110    GURL root_url = GURL(storage::GetExternalFileSystemRootURIString(
111        url.origin(), std::string()));
112    callback.Run(root_url, std::string(), base::File::FILE_ERROR_SECURITY);
113    return;
114  }
115
116  std::string name;
117  // Construct a URL restricted to the found mount point.
118  std::string root_url =
119      storage::GetExternalFileSystemRootURIString(url.origin(), id);
120
121  // For removable and archives, the file system root is the external mount
122  // point plus the inner mount point.
123  if (id == "archive" || id == "removable") {
124    std::vector<std::string> components;
125    url.virtual_path().GetComponents(&components);
126    DCHECK_EQ(id, components.at(0));
127    if (components.size() < 2) {
128      // Unable to access /archive and /removable directories directly. The
129      // inner mount name must be specified.
130      callback.Run(
131          GURL(root_url), std::string(), base::File::FILE_ERROR_SECURITY);
132      return;
133    }
134    std::string inner_mount_name = components[1];
135    root_url += inner_mount_name + "/";
136    name = inner_mount_name;
137  } else {
138    name = id;
139  }
140
141  callback.Run(GURL(root_url), name, base::File::FILE_OK);
142}
143
144storage::FileSystemQuotaUtil* FileSystemBackend::GetQuotaUtil() {
145  // No quota support.
146  return NULL;
147}
148
149const storage::UpdateObserverList* FileSystemBackend::GetUpdateObservers(
150    storage::FileSystemType type) const {
151  return NULL;
152}
153
154const storage::ChangeObserverList* FileSystemBackend::GetChangeObservers(
155    storage::FileSystemType type) const {
156  return NULL;
157}
158
159const storage::AccessObserverList* FileSystemBackend::GetAccessObservers(
160    storage::FileSystemType type) const {
161  return NULL;
162}
163
164bool FileSystemBackend::IsAccessAllowed(
165    const storage::FileSystemURL& url) const {
166  if (!url.is_valid())
167    return false;
168
169  // No extra check is needed for isolated file systems.
170  if (url.mount_type() == storage::kFileSystemTypeIsolated)
171    return true;
172
173  if (!CanHandleURL(url))
174    return false;
175
176  std::string extension_id = url.origin().host();
177  // TODO(mtomasz): Temporarily whitelist TimeScapes. Remove this in M-31.
178  // See: crbug.com/271946
179  if (extension_id == "mlbmkoenclnokonejhlfakkeabdlmpek" &&
180      url.type() == storage::kFileSystemTypeRestrictedNativeLocal) {
181    return true;
182  }
183
184  // Grant access for URL having "externalfile:" scheme. The URL
185  // filesystem:externalfile:/xxx cannot be parsed directly. The URL is created
186  // only by DriveURLRequestJob.
187  if (url.origin().scheme() == chrome::kExternalFileScheme)
188    return true;
189
190  // Check first to make sure this extension has fileBrowserHander permissions.
191  if (!special_storage_policy_.get() ||
192      !special_storage_policy_->IsFileHandler(extension_id))
193    return false;
194
195  return file_access_permissions_->HasAccessPermission(extension_id,
196                                                       url.virtual_path());
197}
198
199void FileSystemBackend::GrantFullAccessToExtension(
200    const std::string& extension_id) {
201  if (!special_storage_policy_.get())
202    return;
203  if (!special_storage_policy_->IsFileHandler(extension_id)) {
204    NOTREACHED();
205    return;
206  }
207  file_access_permissions_->GrantFullAccessPermission(extension_id);
208}
209
210void FileSystemBackend::GrantFileAccessToExtension(
211    const std::string& extension_id, const base::FilePath& virtual_path) {
212  if (!special_storage_policy_.get())
213    return;
214  // All we care about here is access from extensions for now.
215  if (!special_storage_policy_->IsFileHandler(extension_id)) {
216    NOTREACHED();
217    return;
218  }
219
220  std::string id;
221  storage::FileSystemType type;
222  std::string cracked_id;
223  base::FilePath path;
224  storage::FileSystemMountOption option;
225  if (!mount_points_->CrackVirtualPath(virtual_path, &id, &type, &cracked_id,
226                                       &path, &option) &&
227      !system_mount_points_->CrackVirtualPath(virtual_path, &id, &type,
228                                              &cracked_id, &path, &option)) {
229    return;
230  }
231
232  if (type == storage::kFileSystemTypeRestrictedNativeLocal) {
233    LOG(ERROR) << "Can't grant access for restricted mount point";
234    return;
235  }
236
237  file_access_permissions_->GrantAccessPermission(extension_id, virtual_path);
238}
239
240void FileSystemBackend::RevokeAccessForExtension(
241      const std::string& extension_id) {
242  file_access_permissions_->RevokePermissions(extension_id);
243}
244
245std::vector<base::FilePath> FileSystemBackend::GetRootDirectories() const {
246  std::vector<storage::MountPoints::MountPointInfo> mount_points;
247  mount_points_->AddMountPointInfosTo(&mount_points);
248  system_mount_points_->AddMountPointInfosTo(&mount_points);
249
250  std::vector<base::FilePath> root_dirs;
251  for (size_t i = 0; i < mount_points.size(); ++i)
252    root_dirs.push_back(mount_points[i].path);
253  return root_dirs;
254}
255
256storage::AsyncFileUtil* FileSystemBackend::GetAsyncFileUtil(
257    storage::FileSystemType type) {
258  switch (type) {
259    case storage::kFileSystemTypeDrive:
260      return drive_delegate_->GetAsyncFileUtil(type);
261    case storage::kFileSystemTypeProvided:
262      return file_system_provider_delegate_->GetAsyncFileUtil(type);
263    case storage::kFileSystemTypeNativeLocal:
264    case storage::kFileSystemTypeRestrictedNativeLocal:
265      return local_file_util_.get();
266    case storage::kFileSystemTypeDeviceMediaAsFileStorage:
267      return mtp_delegate_->GetAsyncFileUtil(type);
268    default:
269      NOTREACHED();
270  }
271  return NULL;
272}
273
274storage::WatcherManager* FileSystemBackend::GetWatcherManager(
275    storage::FileSystemType type) {
276  return NULL;
277}
278
279storage::CopyOrMoveFileValidatorFactory*
280FileSystemBackend::GetCopyOrMoveFileValidatorFactory(
281    storage::FileSystemType type,
282    base::File::Error* error_code) {
283  DCHECK(error_code);
284  *error_code = base::File::FILE_OK;
285  return NULL;
286}
287
288storage::FileSystemOperation* FileSystemBackend::CreateFileSystemOperation(
289    const storage::FileSystemURL& url,
290    storage::FileSystemContext* context,
291    base::File::Error* error_code) const {
292  DCHECK(url.is_valid());
293
294  if (!IsAccessAllowed(url)) {
295    *error_code = base::File::FILE_ERROR_SECURITY;
296    return NULL;
297  }
298
299  if (url.type() == storage::kFileSystemTypeDeviceMediaAsFileStorage) {
300    // MTP file operations run on MediaTaskRunner.
301    return storage::FileSystemOperation::Create(
302        url,
303        context,
304        make_scoped_ptr(new storage::FileSystemOperationContext(
305            context, MediaFileSystemBackend::MediaTaskRunner().get())));
306  }
307
308  DCHECK(url.type() == storage::kFileSystemTypeNativeLocal ||
309         url.type() == storage::kFileSystemTypeRestrictedNativeLocal ||
310         url.type() == storage::kFileSystemTypeDrive ||
311         url.type() == storage::kFileSystemTypeProvided);
312  return storage::FileSystemOperation::Create(
313      url,
314      context,
315      make_scoped_ptr(new storage::FileSystemOperationContext(context)));
316}
317
318bool FileSystemBackend::SupportsStreaming(
319    const storage::FileSystemURL& url) const {
320  return url.type() == storage::kFileSystemTypeDrive ||
321         url.type() == storage::kFileSystemTypeProvided ||
322         url.type() == storage::kFileSystemTypeDeviceMediaAsFileStorage;
323}
324
325bool FileSystemBackend::HasInplaceCopyImplementation(
326    storage::FileSystemType type) const {
327  switch (type) {
328    case storage::kFileSystemTypeDrive:
329    case storage::kFileSystemTypeProvided:
330    case storage::kFileSystemTypeDeviceMediaAsFileStorage:
331      return true;
332    case storage::kFileSystemTypeNativeLocal:
333    case storage::kFileSystemTypeRestrictedNativeLocal:
334      return false;
335    default:
336      NOTREACHED();
337  }
338  return true;
339}
340
341scoped_ptr<storage::FileStreamReader> FileSystemBackend::CreateFileStreamReader(
342    const storage::FileSystemURL& url,
343    int64 offset,
344    int64 max_bytes_to_read,
345    const base::Time& expected_modification_time,
346    storage::FileSystemContext* context) const {
347  DCHECK(url.is_valid());
348
349  if (!IsAccessAllowed(url))
350    return scoped_ptr<storage::FileStreamReader>();
351
352  switch (url.type()) {
353    case storage::kFileSystemTypeDrive:
354      return drive_delegate_->CreateFileStreamReader(
355          url, offset, max_bytes_to_read, expected_modification_time, context);
356    case storage::kFileSystemTypeProvided:
357      return file_system_provider_delegate_->CreateFileStreamReader(
358          url, offset, max_bytes_to_read, expected_modification_time, context);
359    case storage::kFileSystemTypeNativeLocal:
360    case storage::kFileSystemTypeRestrictedNativeLocal:
361      return scoped_ptr<storage::FileStreamReader>(
362          storage::FileStreamReader::CreateForFileSystemFile(
363              context, url, offset, expected_modification_time));
364    case storage::kFileSystemTypeDeviceMediaAsFileStorage:
365      return mtp_delegate_->CreateFileStreamReader(
366          url, offset, max_bytes_to_read, expected_modification_time, context);
367    default:
368      NOTREACHED();
369  }
370  return scoped_ptr<storage::FileStreamReader>();
371}
372
373scoped_ptr<storage::FileStreamWriter> FileSystemBackend::CreateFileStreamWriter(
374    const storage::FileSystemURL& url,
375    int64 offset,
376    storage::FileSystemContext* context) const {
377  DCHECK(url.is_valid());
378
379  if (!IsAccessAllowed(url))
380    return scoped_ptr<storage::FileStreamWriter>();
381
382  switch (url.type()) {
383    case storage::kFileSystemTypeDrive:
384      return drive_delegate_->CreateFileStreamWriter(url, offset, context);
385    case storage::kFileSystemTypeProvided:
386      return file_system_provider_delegate_->CreateFileStreamWriter(
387          url, offset, context);
388    case storage::kFileSystemTypeNativeLocal:
389      return scoped_ptr<storage::FileStreamWriter>(
390          storage::FileStreamWriter::CreateForLocalFile(
391              context->default_file_task_runner(),
392              url.path(),
393              offset,
394              storage::FileStreamWriter::OPEN_EXISTING_FILE));
395    case storage::kFileSystemTypeRestrictedNativeLocal:
396      // Restricted native local file system is read only.
397      return scoped_ptr<storage::FileStreamWriter>();
398    case storage::kFileSystemTypeDeviceMediaAsFileStorage:
399      return mtp_delegate_->CreateFileStreamWriter(url, offset, context);
400    default:
401      NOTREACHED();
402  }
403  return scoped_ptr<storage::FileStreamWriter>();
404}
405
406bool FileSystemBackend::GetVirtualPath(
407    const base::FilePath& filesystem_path,
408    base::FilePath* virtual_path) {
409  return mount_points_->GetVirtualPath(filesystem_path, virtual_path) ||
410         system_mount_points_->GetVirtualPath(filesystem_path, virtual_path);
411}
412
413void FileSystemBackend::GetRedirectURLForContents(
414    const storage::FileSystemURL& url,
415    const storage::URLCallback& callback) {
416  DCHECK(url.is_valid());
417
418  if (!IsAccessAllowed(url))
419    return callback.Run(GURL());
420
421  switch (url.type()) {
422    case storage::kFileSystemTypeDrive:
423      drive_delegate_->GetRedirectURLForContents(url, callback);
424      return;
425    case storage::kFileSystemTypeProvided:
426      file_system_provider_delegate_->GetRedirectURLForContents(url,
427                                                                  callback);
428      return;
429    case storage::kFileSystemTypeDeviceMediaAsFileStorage:
430      mtp_delegate_->GetRedirectURLForContents(url, callback);
431      return;
432    case storage::kFileSystemTypeNativeLocal:
433    case storage::kFileSystemTypeRestrictedNativeLocal:
434      callback.Run(GURL());
435      return;
436    default:
437      NOTREACHED();
438  }
439  callback.Run(GURL());
440}
441
442}  // namespace chromeos
443