Home | History | Annotate | Download | only in fileapi
      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 
     24 namespace chromeos {
     25 
     26 // static
     27 bool 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 
     37 FileSystemBackend::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 
     54 FileSystemBackend::~FileSystemBackend() {
     55 }
     56 
     57 void 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 
     78 bool 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 
     93 void FileSystemBackend::Initialize(storage::FileSystemContext* context) {
     94 }
     95 
     96 void 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 
    144 storage::FileSystemQuotaUtil* FileSystemBackend::GetQuotaUtil() {
    145   // No quota support.
    146   return NULL;
    147 }
    148 
    149 const storage::UpdateObserverList* FileSystemBackend::GetUpdateObservers(
    150     storage::FileSystemType type) const {
    151   return NULL;
    152 }
    153 
    154 const storage::ChangeObserverList* FileSystemBackend::GetChangeObservers(
    155     storage::FileSystemType type) const {
    156   return NULL;
    157 }
    158 
    159 const storage::AccessObserverList* FileSystemBackend::GetAccessObservers(
    160     storage::FileSystemType type) const {
    161   return NULL;
    162 }
    163 
    164 bool 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 
    199 void 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 
    210 void 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 
    240 void FileSystemBackend::RevokeAccessForExtension(
    241       const std::string& extension_id) {
    242   file_access_permissions_->RevokePermissions(extension_id);
    243 }
    244 
    245 std::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 
    256 storage::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 
    274 storage::WatcherManager* FileSystemBackend::GetWatcherManager(
    275     storage::FileSystemType type) {
    276   return NULL;
    277 }
    278 
    279 storage::CopyOrMoveFileValidatorFactory*
    280 FileSystemBackend::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 
    288 storage::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 
    318 bool 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 
    325 bool 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 
    341 scoped_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 
    373 scoped_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 
    406 bool 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 
    413 void 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