Home | History | Annotate | Download | only in fileapi
      1 // Copyright (c) 2012 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 "webkit/browser/fileapi/file_system_context.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/single_thread_task_runner.h"
      9 #include "base/stl_util.h"
     10 #include "base/task_runner_util.h"
     11 #include "url/gurl.h"
     12 #include "webkit/browser/blob/file_stream_reader.h"
     13 #include "webkit/browser/fileapi/copy_or_move_file_validator.h"
     14 #include "webkit/browser/fileapi/external_mount_points.h"
     15 #include "webkit/browser/fileapi/file_permission_policy.h"
     16 #include "webkit/browser/fileapi/file_stream_writer.h"
     17 #include "webkit/browser/fileapi/file_system_file_util.h"
     18 #include "webkit/browser/fileapi/file_system_operation.h"
     19 #include "webkit/browser/fileapi/file_system_operation_runner.h"
     20 #include "webkit/browser/fileapi/file_system_options.h"
     21 #include "webkit/browser/fileapi/file_system_quota_client.h"
     22 #include "webkit/browser/fileapi/file_system_url.h"
     23 #include "webkit/browser/fileapi/isolated_context.h"
     24 #include "webkit/browser/fileapi/isolated_file_system_backend.h"
     25 #include "webkit/browser/fileapi/mount_points.h"
     26 #include "webkit/browser/fileapi/quota/quota_reservation.h"
     27 #include "webkit/browser/fileapi/sandbox_file_system_backend.h"
     28 #include "webkit/browser/quota/quota_manager.h"
     29 #include "webkit/browser/quota/special_storage_policy.h"
     30 #include "webkit/common/fileapi/file_system_info.h"
     31 #include "webkit/common/fileapi/file_system_util.h"
     32 
     33 using quota::QuotaClient;
     34 
     35 namespace fileapi {
     36 
     37 namespace {
     38 
     39 QuotaClient* CreateQuotaClient(
     40     FileSystemContext* context,
     41     bool is_incognito) {
     42   return new FileSystemQuotaClient(context, is_incognito);
     43 }
     44 
     45 
     46 void DidGetMetadataForResolveURL(
     47     const base::FilePath& path,
     48     const FileSystemContext::ResolveURLCallback& callback,
     49     const FileSystemInfo& info,
     50     base::PlatformFileError error,
     51     const base::PlatformFileInfo& file_info) {
     52   if (error != base::PLATFORM_FILE_OK) {
     53     callback.Run(error, FileSystemInfo(), base::FilePath(), false);
     54     return;
     55   }
     56   callback.Run(error, info, path, file_info.is_directory);
     57 }
     58 
     59 }  // namespace
     60 
     61 // static
     62 int FileSystemContext::GetPermissionPolicy(FileSystemType type) {
     63   switch (type) {
     64     case kFileSystemTypeTemporary:
     65     case kFileSystemTypePersistent:
     66     case kFileSystemTypeSyncable:
     67       return FILE_PERMISSION_SANDBOX;
     68 
     69     case kFileSystemTypeDrive:
     70     case kFileSystemTypeNativeForPlatformApp:
     71     case kFileSystemTypeNativeLocal:
     72       return FILE_PERMISSION_USE_FILE_PERMISSION;
     73 
     74     case kFileSystemTypeRestrictedNativeLocal:
     75       return FILE_PERMISSION_READ_ONLY |
     76              FILE_PERMISSION_USE_FILE_PERMISSION;
     77 
     78     // Following types are only accessed via IsolatedFileSystem, and
     79     // don't have their own permission policies.
     80     case kFileSystemTypeDeviceMedia:
     81     case kFileSystemTypeDragged:
     82     case kFileSystemTypeForTransientFile:
     83     case kFileSystemTypeIphoto:
     84     case kFileSystemTypeItunes:
     85     case kFileSystemTypeNativeMedia:
     86     case kFileSystemTypePicasa:
     87     case kFileSystemTypePluginPrivate:
     88       return FILE_PERMISSION_ALWAYS_DENY;
     89 
     90     // Following types only appear as mount_type, and will not be
     91     // queried for their permission policies.
     92     case kFileSystemTypeIsolated:
     93     case kFileSystemTypeExternal:
     94       return FILE_PERMISSION_ALWAYS_DENY;
     95 
     96     // Following types should not be used to access files by FileAPI clients.
     97     case kFileSystemTypeTest:
     98     case kFileSystemTypeSyncableForInternalSync:
     99     case kFileSystemInternalTypeEnumEnd:
    100     case kFileSystemInternalTypeEnumStart:
    101     case kFileSystemTypeUnknown:
    102       return FILE_PERMISSION_ALWAYS_DENY;
    103   }
    104   NOTREACHED();
    105   return FILE_PERMISSION_ALWAYS_DENY;
    106 }
    107 
    108 FileSystemContext::FileSystemContext(
    109     base::SingleThreadTaskRunner* io_task_runner,
    110     base::SequencedTaskRunner* file_task_runner,
    111     ExternalMountPoints* external_mount_points,
    112     quota::SpecialStoragePolicy* special_storage_policy,
    113     quota::QuotaManagerProxy* quota_manager_proxy,
    114     ScopedVector<FileSystemBackend> additional_backends,
    115     const base::FilePath& partition_path,
    116     const FileSystemOptions& options)
    117     : io_task_runner_(io_task_runner),
    118       default_file_task_runner_(file_task_runner),
    119       quota_manager_proxy_(quota_manager_proxy),
    120       sandbox_delegate_(new SandboxFileSystemBackendDelegate(
    121           quota_manager_proxy,
    122           file_task_runner,
    123           partition_path,
    124           special_storage_policy,
    125           options)),
    126       sandbox_backend_(new SandboxFileSystemBackend(
    127           sandbox_delegate_.get())),
    128       isolated_backend_(new IsolatedFileSystemBackend()),
    129       plugin_private_backend_(new PluginPrivateFileSystemBackend(
    130           file_task_runner,
    131           partition_path,
    132           special_storage_policy,
    133           options)),
    134       additional_backends_(additional_backends.Pass()),
    135       external_mount_points_(external_mount_points),
    136       partition_path_(partition_path),
    137       is_incognito_(options.is_incognito()),
    138       operation_runner_(new FileSystemOperationRunner(this)) {
    139   RegisterBackend(sandbox_backend_.get());
    140   RegisterBackend(isolated_backend_.get());
    141   RegisterBackend(plugin_private_backend_.get());
    142 
    143   for (ScopedVector<FileSystemBackend>::const_iterator iter =
    144           additional_backends_.begin();
    145        iter != additional_backends_.end(); ++iter) {
    146     RegisterBackend(*iter);
    147   }
    148 
    149   if (quota_manager_proxy) {
    150     // Quota client assumes all backends have registered.
    151     quota_manager_proxy->RegisterClient(CreateQuotaClient(
    152             this, options.is_incognito()));
    153   }
    154 
    155   sandbox_backend_->Initialize(this);
    156   isolated_backend_->Initialize(this);
    157   plugin_private_backend_->Initialize(this);
    158   for (ScopedVector<FileSystemBackend>::const_iterator iter =
    159           additional_backends_.begin();
    160        iter != additional_backends_.end(); ++iter) {
    161     (*iter)->Initialize(this);
    162   }
    163 
    164   // Additional mount points must be added before regular system-wide
    165   // mount points.
    166   if (external_mount_points)
    167     url_crackers_.push_back(external_mount_points);
    168   url_crackers_.push_back(ExternalMountPoints::GetSystemInstance());
    169   url_crackers_.push_back(IsolatedContext::GetInstance());
    170 }
    171 
    172 bool FileSystemContext::DeleteDataForOriginOnFileThread(
    173     const GURL& origin_url) {
    174   DCHECK(default_file_task_runner()->RunsTasksOnCurrentThread());
    175   DCHECK(origin_url == origin_url.GetOrigin());
    176 
    177   bool success = true;
    178   for (FileSystemBackendMap::iterator iter = backend_map_.begin();
    179        iter != backend_map_.end();
    180        ++iter) {
    181     FileSystemBackend* backend = iter->second;
    182     if (!backend->GetQuotaUtil())
    183       continue;
    184     if (backend->GetQuotaUtil()->DeleteOriginDataOnFileThread(
    185             this, quota_manager_proxy(), origin_url, iter->first)
    186             != base::PLATFORM_FILE_OK) {
    187       // Continue the loop, but record the failure.
    188       success = false;
    189     }
    190   }
    191 
    192   return success;
    193 }
    194 
    195 scoped_refptr<QuotaReservation>
    196 FileSystemContext::CreateQuotaReservationOnFileTaskRunner(
    197     const GURL& origin_url,
    198     FileSystemType type) {
    199   DCHECK(default_file_task_runner()->RunsTasksOnCurrentThread());
    200   FileSystemBackend* backend = GetFileSystemBackend(type);
    201   if (!backend || !backend->GetQuotaUtil())
    202     return scoped_refptr<QuotaReservation>();
    203   return backend->GetQuotaUtil()->CreateQuotaReservationOnFileTaskRunner(
    204       origin_url, type);
    205 }
    206 
    207 void FileSystemContext::Shutdown() {
    208   if (!io_task_runner_->RunsTasksOnCurrentThread()) {
    209     io_task_runner_->PostTask(
    210         FROM_HERE, base::Bind(&FileSystemContext::Shutdown,
    211                               make_scoped_refptr(this)));
    212     return;
    213   }
    214   operation_runner_->Shutdown();
    215 }
    216 
    217 FileSystemQuotaUtil*
    218 FileSystemContext::GetQuotaUtil(FileSystemType type) const {
    219   FileSystemBackend* backend = GetFileSystemBackend(type);
    220   if (!backend)
    221     return NULL;
    222   return backend->GetQuotaUtil();
    223 }
    224 
    225 AsyncFileUtil* FileSystemContext::GetAsyncFileUtil(
    226     FileSystemType type) const {
    227   FileSystemBackend* backend = GetFileSystemBackend(type);
    228   if (!backend)
    229     return NULL;
    230   return backend->GetAsyncFileUtil(type);
    231 }
    232 
    233 CopyOrMoveFileValidatorFactory*
    234 FileSystemContext::GetCopyOrMoveFileValidatorFactory(
    235     FileSystemType type, base::PlatformFileError* error_code) const {
    236   DCHECK(error_code);
    237   *error_code = base::PLATFORM_FILE_OK;
    238   FileSystemBackend* backend = GetFileSystemBackend(type);
    239   if (!backend)
    240     return NULL;
    241   return backend->GetCopyOrMoveFileValidatorFactory(
    242       type, error_code);
    243 }
    244 
    245 FileSystemBackend* FileSystemContext::GetFileSystemBackend(
    246     FileSystemType type) const {
    247   FileSystemBackendMap::const_iterator found = backend_map_.find(type);
    248   if (found != backend_map_.end())
    249     return found->second;
    250   NOTREACHED() << "Unknown filesystem type: " << type;
    251   return NULL;
    252 }
    253 
    254 bool FileSystemContext::IsSandboxFileSystem(FileSystemType type) const {
    255   FileSystemBackendMap::const_iterator found = backend_map_.find(type);
    256   return found != backend_map_.end() && found->second->GetQuotaUtil();
    257 }
    258 
    259 const UpdateObserverList* FileSystemContext::GetUpdateObservers(
    260     FileSystemType type) const {
    261   FileSystemBackend* backend = GetFileSystemBackend(type);
    262   if (backend->GetQuotaUtil())
    263     return backend->GetQuotaUtil()->GetUpdateObservers(type);
    264   return NULL;
    265 }
    266 
    267 const AccessObserverList* FileSystemContext::GetAccessObservers(
    268     FileSystemType type) const {
    269   FileSystemBackend* backend = GetFileSystemBackend(type);
    270   if (backend->GetQuotaUtil())
    271     return backend->GetQuotaUtil()->GetAccessObservers(type);
    272   return NULL;
    273 }
    274 
    275 void FileSystemContext::GetFileSystemTypes(
    276     std::vector<FileSystemType>* types) const {
    277   types->clear();
    278   for (FileSystemBackendMap::const_iterator iter = backend_map_.begin();
    279        iter != backend_map_.end(); ++iter)
    280     types->push_back(iter->first);
    281 }
    282 
    283 ExternalFileSystemBackend*
    284 FileSystemContext::external_backend() const {
    285   return static_cast<ExternalFileSystemBackend*>(
    286       GetFileSystemBackend(kFileSystemTypeExternal));
    287 }
    288 
    289 void FileSystemContext::OpenFileSystem(
    290     const GURL& origin_url,
    291     FileSystemType type,
    292     OpenFileSystemMode mode,
    293     const OpenFileSystemCallback& callback) {
    294   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
    295   DCHECK(!callback.is_null());
    296 
    297   if (!FileSystemContext::IsSandboxFileSystem(type)) {
    298     // Disallow opening a non-sandboxed filesystem.
    299     callback.Run(GURL(), std::string(), base::PLATFORM_FILE_ERROR_SECURITY);
    300     return;
    301   }
    302 
    303   FileSystemBackend* backend = GetFileSystemBackend(type);
    304   if (!backend) {
    305     callback.Run(GURL(), std::string(), base::PLATFORM_FILE_ERROR_SECURITY);
    306     return;
    307   }
    308 
    309   backend->OpenFileSystem(origin_url, type, mode, callback);
    310 }
    311 
    312 void FileSystemContext::ResolveURL(
    313     const FileSystemURL& url,
    314     const ResolveURLCallback& callback) {
    315   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
    316   DCHECK(!callback.is_null());
    317 
    318   if (!FileSystemContext::IsSandboxFileSystem(url.type())) {
    319 #ifdef OS_CHROMEOS
    320     // Do not have to open a non-sandboxed filesystem.
    321     // TODO(nhiroki): For now we assume this path is called only on ChromeOS,
    322     // but this assumption may be broken in the future and we should handle
    323     // more generally. http://crbug.com/304062.
    324     FileSystemInfo info = GetFileSystemInfoForChromeOS(url.origin());
    325     DidOpenFileSystemForResolveURL(
    326         url, callback, info.root_url, info.name, base::PLATFORM_FILE_OK);
    327     return;
    328 #endif
    329     callback.Run(base::PLATFORM_FILE_ERROR_SECURITY,
    330                  FileSystemInfo(), base::FilePath(), false);
    331     return;
    332   }
    333 
    334   FileSystemBackend* backend = GetFileSystemBackend(url.type());
    335   if (!backend) {
    336     callback.Run(base::PLATFORM_FILE_ERROR_SECURITY,
    337                  FileSystemInfo(), base::FilePath(), false);
    338     return;
    339   }
    340 
    341   backend->OpenFileSystem(
    342       url.origin(), url.type(),
    343       OPEN_FILE_SYSTEM_FAIL_IF_NONEXISTENT,
    344       base::Bind(&FileSystemContext::DidOpenFileSystemForResolveURL,
    345                  this, url, callback));
    346 }
    347 
    348 void FileSystemContext::DeleteFileSystem(
    349     const GURL& origin_url,
    350     FileSystemType type,
    351     const StatusCallback& callback) {
    352   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
    353   DCHECK(origin_url == origin_url.GetOrigin());
    354   DCHECK(!callback.is_null());
    355 
    356   FileSystemBackend* backend = GetFileSystemBackend(type);
    357   if (!backend) {
    358     callback.Run(base::PLATFORM_FILE_ERROR_SECURITY);
    359     return;
    360   }
    361   if (!backend->GetQuotaUtil()) {
    362     callback.Run(base::PLATFORM_FILE_ERROR_INVALID_OPERATION);
    363     return;
    364   }
    365 
    366   base::PostTaskAndReplyWithResult(
    367       default_file_task_runner(),
    368       FROM_HERE,
    369       // It is safe to pass Unretained(quota_util) since context owns it.
    370       base::Bind(&FileSystemQuotaUtil::DeleteOriginDataOnFileThread,
    371                  base::Unretained(backend->GetQuotaUtil()),
    372                  make_scoped_refptr(this),
    373                  base::Unretained(quota_manager_proxy()),
    374                  origin_url,
    375                  type),
    376       callback);
    377 }
    378 
    379 scoped_ptr<webkit_blob::FileStreamReader>
    380 FileSystemContext::CreateFileStreamReader(
    381     const FileSystemURL& url,
    382     int64 offset,
    383     const base::Time& expected_modification_time) {
    384   if (!url.is_valid())
    385     return scoped_ptr<webkit_blob::FileStreamReader>();
    386   FileSystemBackend* backend = GetFileSystemBackend(url.type());
    387   if (!backend)
    388     return scoped_ptr<webkit_blob::FileStreamReader>();
    389   return backend->CreateFileStreamReader(
    390       url, offset, expected_modification_time, this);
    391 }
    392 
    393 scoped_ptr<FileStreamWriter> FileSystemContext::CreateFileStreamWriter(
    394     const FileSystemURL& url,
    395     int64 offset) {
    396   if (!url.is_valid())
    397     return scoped_ptr<FileStreamWriter>();
    398   FileSystemBackend* backend = GetFileSystemBackend(url.type());
    399   if (!backend)
    400     return scoped_ptr<FileStreamWriter>();
    401   return backend->CreateFileStreamWriter(url, offset, this);
    402 }
    403 
    404 scoped_ptr<FileSystemOperationRunner>
    405 FileSystemContext::CreateFileSystemOperationRunner() {
    406   return make_scoped_ptr(new FileSystemOperationRunner(this));
    407 }
    408 
    409 FileSystemURL FileSystemContext::CrackURL(const GURL& url) const {
    410   return CrackFileSystemURL(FileSystemURL(url));
    411 }
    412 
    413 FileSystemURL FileSystemContext::CreateCrackedFileSystemURL(
    414     const GURL& origin,
    415     FileSystemType type,
    416     const base::FilePath& path) const {
    417   return CrackFileSystemURL(FileSystemURL(origin, type, path));
    418 }
    419 
    420 #if defined(OS_CHROMEOS)
    421 void FileSystemContext::EnableTemporaryFileSystemInIncognito() {
    422   sandbox_backend_->set_enable_temporary_file_system_in_incognito(true);
    423 }
    424 #endif
    425 
    426 bool FileSystemContext::CanServeURLRequest(const FileSystemURL& url) const {
    427   // We never support accessing files in isolated filesystems via an URL.
    428   if (url.mount_type() == kFileSystemTypeIsolated)
    429     return false;
    430 #if defined(OS_CHROMEOS)
    431   if (url.type() == kFileSystemTypeTemporary &&
    432       sandbox_backend_->enable_temporary_file_system_in_incognito()) {
    433     return true;
    434   }
    435 #endif
    436   return !is_incognito_ || !FileSystemContext::IsSandboxFileSystem(url.type());
    437 }
    438 
    439 void FileSystemContext::OpenPluginPrivateFileSystem(
    440     const GURL& origin_url,
    441     FileSystemType type,
    442     const std::string& filesystem_id,
    443     const std::string& plugin_id,
    444     OpenFileSystemMode mode,
    445     const StatusCallback& callback) {
    446   DCHECK(plugin_private_backend_);
    447   plugin_private_backend_->OpenPrivateFileSystem(
    448       origin_url, type, filesystem_id, plugin_id, mode, callback);
    449 }
    450 
    451 FileSystemContext::~FileSystemContext() {
    452 }
    453 
    454 void FileSystemContext::DeleteOnCorrectThread() const {
    455   if (!io_task_runner_->RunsTasksOnCurrentThread() &&
    456       io_task_runner_->DeleteSoon(FROM_HERE, this)) {
    457     return;
    458   }
    459   delete this;
    460 }
    461 
    462 FileSystemOperation* FileSystemContext::CreateFileSystemOperation(
    463     const FileSystemURL& url, base::PlatformFileError* error_code) {
    464   if (!url.is_valid()) {
    465     if (error_code)
    466       *error_code = base::PLATFORM_FILE_ERROR_INVALID_URL;
    467     return NULL;
    468   }
    469 
    470   FileSystemBackend* backend = GetFileSystemBackend(url.type());
    471   if (!backend) {
    472     if (error_code)
    473       *error_code = base::PLATFORM_FILE_ERROR_FAILED;
    474     return NULL;
    475   }
    476 
    477   base::PlatformFileError fs_error = base::PLATFORM_FILE_OK;
    478   FileSystemOperation* operation =
    479       backend->CreateFileSystemOperation(url, this, &fs_error);
    480 
    481   if (error_code)
    482     *error_code = fs_error;
    483   return operation;
    484 }
    485 
    486 FileSystemURL FileSystemContext::CrackFileSystemURL(
    487     const FileSystemURL& url) const {
    488   if (!url.is_valid())
    489     return FileSystemURL();
    490 
    491   // The returned value in case there is no crackers which can crack the url.
    492   // This is valid situation for non isolated/external file systems.
    493   FileSystemURL current = url;
    494 
    495   // File system may be mounted multiple times (e.g., an isolated filesystem on
    496   // top of an external filesystem). Hence cracking needs to be iterated.
    497   for (;;) {
    498     FileSystemURL cracked = current;
    499     for (size_t i = 0; i < url_crackers_.size(); ++i) {
    500       if (!url_crackers_[i]->HandlesFileSystemMountType(current.type()))
    501         continue;
    502       cracked = url_crackers_[i]->CrackFileSystemURL(current);
    503       if (cracked.is_valid())
    504         break;
    505     }
    506     if (cracked == current)
    507       break;
    508     current = cracked;
    509   }
    510   return current;
    511 }
    512 
    513 void FileSystemContext::RegisterBackend(FileSystemBackend* backend) {
    514   const FileSystemType mount_types[] = {
    515     kFileSystemTypeTemporary,
    516     kFileSystemTypePersistent,
    517     kFileSystemTypeIsolated,
    518     kFileSystemTypeExternal,
    519   };
    520   // Register file system backends for public mount types.
    521   for (size_t j = 0; j < ARRAYSIZE_UNSAFE(mount_types); ++j) {
    522     if (backend->CanHandleType(mount_types[j])) {
    523       const bool inserted = backend_map_.insert(
    524           std::make_pair(mount_types[j], backend)).second;
    525       DCHECK(inserted);
    526     }
    527   }
    528   // Register file system backends for internal types.
    529   for (int t = kFileSystemInternalTypeEnumStart + 1;
    530        t < kFileSystemInternalTypeEnumEnd; ++t) {
    531     FileSystemType type = static_cast<FileSystemType>(t);
    532     if (backend->CanHandleType(type)) {
    533       const bool inserted = backend_map_.insert(
    534           std::make_pair(type, backend)).second;
    535       DCHECK(inserted);
    536     }
    537   }
    538 }
    539 
    540 void FileSystemContext::DidOpenFileSystemForResolveURL(
    541     const FileSystemURL& url,
    542     const FileSystemContext::ResolveURLCallback& callback,
    543     const GURL& filesystem_root,
    544     const std::string& filesystem_name,
    545     base::PlatformFileError error) {
    546   DCHECK(io_task_runner_->RunsTasksOnCurrentThread());
    547 
    548   if (error != base::PLATFORM_FILE_OK) {
    549     callback.Run(error, FileSystemInfo(), base::FilePath(), false);
    550     return;
    551   }
    552 
    553   fileapi::FileSystemInfo info(
    554       filesystem_name, filesystem_root, url.mount_type());
    555 
    556   // Extract the virtual path not containing a filesystem type part from |url|.
    557   base::FilePath parent = CrackURL(filesystem_root).virtual_path();
    558   base::FilePath child = url.virtual_path();
    559   base::FilePath path;
    560 
    561   if (parent.empty()) {
    562     path = child;
    563   } else if (parent != child) {
    564     bool result = parent.AppendRelativePath(child, &path);
    565     DCHECK(result);
    566   }
    567 
    568   operation_runner()->GetMetadata(
    569       url, base::Bind(&DidGetMetadataForResolveURL, path, callback, info));
    570 }
    571 
    572 }  // namespace fileapi
    573