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 "webkit/browser/fileapi/sandbox_context.h"
      6 
      7 #include "base/command_line.h"
      8 #include "base/file_util.h"
      9 #include "base/metrics/histogram.h"
     10 #include "base/stl_util.h"
     11 #include "base/task_runner_util.h"
     12 #include "net/base/net_util.h"
     13 #include "webkit/browser/fileapi/async_file_util_adapter.h"
     14 #include "webkit/browser/fileapi/file_system_context.h"
     15 #include "webkit/browser/fileapi/file_system_operation_context.h"
     16 #include "webkit/browser/fileapi/file_system_url.h"
     17 #include "webkit/browser/fileapi/file_system_usage_cache.h"
     18 #include "webkit/browser/fileapi/obfuscated_file_util.h"
     19 #include "webkit/browser/fileapi/sandbox_file_system_backend.h"
     20 #include "webkit/browser/fileapi/sandbox_quota_observer.h"
     21 #include "webkit/browser/quota/quota_manager.h"
     22 #include "webkit/common/fileapi/file_system_util.h"
     23 
     24 namespace fileapi {
     25 
     26 namespace {
     27 
     28 const char kOpenFileSystemLabel[] = "FileSystem.OpenFileSystem";
     29 const char kOpenFileSystemDetailLabel[] = "FileSystem.OpenFileSystemDetail";
     30 const char kOpenFileSystemDetailNonThrottledLabel[] =
     31     "FileSystem.OpenFileSystemDetailNonthrottled";
     32 int64 kMinimumStatsCollectionIntervalHours = 1;
     33 
     34 enum FileSystemError {
     35   kOK = 0,
     36   kIncognito,
     37   kInvalidSchemeError,
     38   kCreateDirectoryError,
     39   kNotFound,
     40   kUnknownError,
     41   kFileSystemErrorMax,
     42 };
     43 
     44 // Restricted names.
     45 // http://dev.w3.org/2009/dap/file-system/file-dir-sys.html#naming-restrictions
     46 const base::FilePath::CharType* const kRestrictedNames[] = {
     47   FILE_PATH_LITERAL("."), FILE_PATH_LITERAL(".."),
     48 };
     49 
     50 // Restricted chars.
     51 const base::FilePath::CharType kRestrictedChars[] = {
     52   FILE_PATH_LITERAL('/'), FILE_PATH_LITERAL('\\'),
     53 };
     54 
     55 class ObfuscatedOriginEnumerator
     56     : public SandboxContext::OriginEnumerator {
     57  public:
     58   explicit ObfuscatedOriginEnumerator(ObfuscatedFileUtil* file_util) {
     59     enum_.reset(file_util->CreateOriginEnumerator());
     60   }
     61   virtual ~ObfuscatedOriginEnumerator() {}
     62 
     63   virtual GURL Next() OVERRIDE {
     64     return enum_->Next();
     65   }
     66 
     67   virtual bool HasFileSystemType(fileapi::FileSystemType type) const OVERRIDE {
     68     return enum_->HasFileSystemType(type);
     69   }
     70 
     71  private:
     72   scoped_ptr<ObfuscatedFileUtil::AbstractOriginEnumerator> enum_;
     73 };
     74 
     75 void OpenFileSystemOnFileThread(
     76     ObfuscatedFileUtil* file_util,
     77     const GURL& origin_url,
     78     FileSystemType type,
     79     OpenFileSystemMode mode,
     80     base::PlatformFileError* error_ptr) {
     81   DCHECK(error_ptr);
     82   const bool create = (mode == OPEN_FILE_SYSTEM_CREATE_IF_NONEXISTENT);
     83   file_util->GetDirectoryForOriginAndType(origin_url, type, create, error_ptr);
     84   if (*error_ptr != base::PLATFORM_FILE_OK) {
     85     UMA_HISTOGRAM_ENUMERATION(kOpenFileSystemLabel,
     86                               kCreateDirectoryError,
     87                               kFileSystemErrorMax);
     88   } else {
     89     UMA_HISTOGRAM_ENUMERATION(kOpenFileSystemLabel, kOK, kFileSystemErrorMax);
     90   }
     91   // The reference of file_util will be derefed on the FILE thread
     92   // when the storage of this callback gets deleted regardless of whether
     93   // this method is called or not.
     94 }
     95 
     96 void DidOpenFileSystem(
     97     base::WeakPtr<SandboxContext> sandbox_context,
     98     const base::Callback<void(base::PlatformFileError error)>& callback,
     99     base::PlatformFileError* error) {
    100   if (sandbox_context.get())
    101     sandbox_context.get()->CollectOpenFileSystemMetrics(*error);
    102   callback.Run(*error);
    103 }
    104 
    105 }  // namespace
    106 
    107 const base::FilePath::CharType
    108 SandboxContext::kFileSystemDirectory[] = FILE_PATH_LITERAL("File System");
    109 
    110 SandboxContext::SandboxContext(
    111     quota::QuotaManagerProxy* quota_manager_proxy,
    112     base::SequencedTaskRunner* file_task_runner,
    113     const base::FilePath& profile_path,
    114     quota::SpecialStoragePolicy* special_storage_policy,
    115     const FileSystemOptions& file_system_options)
    116     : file_task_runner_(file_task_runner),
    117       sandbox_file_util_(new AsyncFileUtilAdapter(
    118           new ObfuscatedFileUtil(
    119               special_storage_policy,
    120               profile_path.Append(kFileSystemDirectory),
    121               file_task_runner))),
    122       file_system_usage_cache_(new FileSystemUsageCache(file_task_runner)),
    123       quota_observer_(new SandboxQuotaObserver(
    124           quota_manager_proxy,
    125           file_task_runner,
    126           sync_file_util(),
    127           usage_cache())),
    128       special_storage_policy_(special_storage_policy),
    129       file_system_options_(file_system_options),
    130       weak_factory_(this) {
    131 }
    132 
    133 SandboxContext::~SandboxContext() {
    134   if (!file_task_runner_->RunsTasksOnCurrentThread()) {
    135     AsyncFileUtilAdapter* sandbox_file_util = sandbox_file_util_.release();
    136     SandboxQuotaObserver* quota_observer = quota_observer_.release();
    137     FileSystemUsageCache* file_system_usage_cache =
    138         file_system_usage_cache_.release();
    139     if (!file_task_runner_->DeleteSoon(FROM_HERE, sandbox_file_util))
    140       delete sandbox_file_util;
    141     if (!file_task_runner_->DeleteSoon(FROM_HERE, quota_observer))
    142       delete quota_observer;
    143     if (!file_task_runner_->DeleteSoon(FROM_HERE, file_system_usage_cache))
    144       delete file_system_usage_cache;
    145   }
    146 }
    147 
    148 bool SandboxContext::IsAccessValid(const FileSystemURL& url) const {
    149   if (!IsAllowedScheme(url.origin()))
    150     return false;
    151 
    152   if (url.path().ReferencesParent())
    153     return false;
    154 
    155   // Return earlier if the path is '/', because VirtualPath::BaseName()
    156   // returns '/' for '/' and we fail the "basename != '/'" check below.
    157   // (We exclude '.' because it's disallowed by spec.)
    158   if (VirtualPath::IsRootPath(url.path()) &&
    159       url.path() != base::FilePath(base::FilePath::kCurrentDirectory))
    160     return true;
    161 
    162   // Restricted names specified in
    163   // http://dev.w3.org/2009/dap/file-system/file-dir-sys.html#naming-restrictions
    164   base::FilePath filename = VirtualPath::BaseName(url.path());
    165   // See if the name is allowed to create.
    166   for (size_t i = 0; i < arraysize(kRestrictedNames); ++i) {
    167     if (filename.value() == kRestrictedNames[i])
    168       return false;
    169   }
    170   for (size_t i = 0; i < arraysize(kRestrictedChars); ++i) {
    171     if (filename.value().find(kRestrictedChars[i]) !=
    172         base::FilePath::StringType::npos)
    173       return false;
    174   }
    175 
    176   return true;
    177 }
    178 
    179 bool SandboxContext::IsAllowedScheme(const GURL& url) const {
    180   // Basically we only accept http or https. We allow file:// URLs
    181   // only if --allow-file-access-from-files flag is given.
    182   if (url.SchemeIs("http") || url.SchemeIs("https"))
    183     return true;
    184   if (url.SchemeIsFileSystem())
    185     return url.inner_url() && IsAllowedScheme(*url.inner_url());
    186 
    187   for (size_t i = 0;
    188        i < file_system_options_.additional_allowed_schemes().size();
    189        ++i) {
    190     if (url.SchemeIs(
    191             file_system_options_.additional_allowed_schemes()[i].c_str()))
    192       return true;
    193   }
    194   return false;
    195 }
    196 
    197 SandboxContext::OriginEnumerator* SandboxContext::CreateOriginEnumerator() {
    198   return new ObfuscatedOriginEnumerator(sync_file_util());
    199 }
    200 
    201 base::FilePath SandboxContext::GetBaseDirectoryForOriginAndType(
    202     const GURL& origin_url, fileapi::FileSystemType type, bool create) {
    203   base::PlatformFileError error = base::PLATFORM_FILE_OK;
    204   base::FilePath path = sync_file_util()->GetDirectoryForOriginAndType(
    205       origin_url, type, create, &error);
    206   if (error != base::PLATFORM_FILE_OK)
    207     return base::FilePath();
    208   return path;
    209 }
    210 
    211 void SandboxContext::OpenFileSystem(
    212     const GURL& origin_url,
    213     fileapi::FileSystemType type,
    214     OpenFileSystemMode mode,
    215     const OpenFileSystemCallback& callback,
    216     const GURL& root_url) {
    217   if (!IsAllowedScheme(origin_url)) {
    218     callback.Run(GURL(), std::string(), base::PLATFORM_FILE_ERROR_SECURITY);
    219     return;
    220   }
    221 
    222   std::string name = GetFileSystemName(origin_url, type);
    223 
    224   base::PlatformFileError* error_ptr = new base::PlatformFileError;
    225   file_task_runner_->PostTaskAndReply(
    226       FROM_HERE,
    227       base::Bind(&OpenFileSystemOnFileThread,
    228                  sync_file_util(), origin_url, type, mode,
    229                  base::Unretained(error_ptr)),
    230       base::Bind(&DidOpenFileSystem,
    231                  weak_factory_.GetWeakPtr(),
    232                  base::Bind(callback, root_url, name),
    233                  base::Owned(error_ptr)));
    234 }
    235 
    236 base::PlatformFileError SandboxContext::DeleteOriginDataOnFileThread(
    237     FileSystemContext* file_system_context,
    238     quota::QuotaManagerProxy* proxy,
    239     const GURL& origin_url,
    240     fileapi::FileSystemType type) {
    241   int64 usage = GetOriginUsageOnFileThread(
    242       file_system_context, origin_url, type);
    243   usage_cache()->CloseCacheFiles();
    244   bool result = sync_file_util()->DeleteDirectoryForOriginAndType(
    245       origin_url, type);
    246   if (result && proxy) {
    247     proxy->NotifyStorageModified(
    248         quota::QuotaClient::kFileSystem,
    249         origin_url,
    250         FileSystemTypeToQuotaStorageType(type),
    251         -usage);
    252   }
    253 
    254   if (result)
    255     return base::PLATFORM_FILE_OK;
    256   return base::PLATFORM_FILE_ERROR_FAILED;
    257 }
    258 
    259 void SandboxContext::GetOriginsForTypeOnFileThread(
    260     fileapi::FileSystemType type, std::set<GURL>* origins) {
    261   DCHECK(origins);
    262   scoped_ptr<OriginEnumerator> enumerator(CreateOriginEnumerator());
    263   GURL origin;
    264   while (!(origin = enumerator->Next()).is_empty()) {
    265     if (enumerator->HasFileSystemType(type))
    266       origins->insert(origin);
    267   }
    268 }
    269 
    270 void SandboxContext::GetOriginsForHostOnFileThread(
    271     fileapi::FileSystemType type, const std::string& host,
    272     std::set<GURL>* origins) {
    273   DCHECK(origins);
    274   scoped_ptr<OriginEnumerator> enumerator(CreateOriginEnumerator());
    275   GURL origin;
    276   while (!(origin = enumerator->Next()).is_empty()) {
    277     if (host == net::GetHostOrSpecFromURL(origin) &&
    278         enumerator->HasFileSystemType(type))
    279       origins->insert(origin);
    280   }
    281 }
    282 
    283 int64 SandboxContext::GetOriginUsageOnFileThread(
    284     FileSystemContext* file_system_context,
    285     const GURL& origin_url,
    286     fileapi::FileSystemType type) {
    287   // Don't use usage cache and return recalculated usage for sticky invalidated
    288   // origins.
    289   if (ContainsKey(sticky_dirty_origins_, std::make_pair(origin_url, type)))
    290     return RecalculateUsage(file_system_context, origin_url, type);
    291 
    292   base::FilePath base_path =
    293       GetBaseDirectoryForOriginAndType(origin_url, type, false);
    294   if (base_path.empty() || !base::DirectoryExists(base_path))
    295     return 0;
    296   base::FilePath usage_file_path =
    297       base_path.Append(FileSystemUsageCache::kUsageFileName);
    298 
    299   bool is_valid = usage_cache()->IsValid(usage_file_path);
    300   uint32 dirty_status = 0;
    301   bool dirty_status_available =
    302       usage_cache()->GetDirty(usage_file_path, &dirty_status);
    303   bool visited = !visited_origins_.insert(origin_url).second;
    304   if (is_valid && (dirty_status == 0 || (dirty_status_available && visited))) {
    305     // The usage cache is clean (dirty == 0) or the origin is already
    306     // initialized and running.  Read the cache file to get the usage.
    307     int64 usage = 0;
    308     return usage_cache()->GetUsage(usage_file_path, &usage) ? usage : -1;
    309   }
    310   // The usage cache has not been initialized or the cache is dirty.
    311   // Get the directory size now and update the cache.
    312   usage_cache()->Delete(usage_file_path);
    313 
    314   int64 usage = RecalculateUsage(file_system_context, origin_url, type);
    315 
    316   // This clears the dirty flag too.
    317   usage_cache()->UpdateUsage(usage_file_path, usage);
    318   return usage;
    319 }
    320 
    321 void SandboxContext::InvalidateUsageCache(
    322     const GURL& origin,
    323     fileapi::FileSystemType type) {
    324   base::PlatformFileError error = base::PLATFORM_FILE_OK;
    325   base::FilePath usage_file_path = GetUsageCachePathForOriginAndType(
    326       sync_file_util(), origin, type, &error);
    327   if (error != base::PLATFORM_FILE_OK)
    328     return;
    329   usage_cache()->IncrementDirty(usage_file_path);
    330 }
    331 
    332 void SandboxContext::StickyInvalidateUsageCache(
    333     const GURL& origin,
    334     fileapi::FileSystemType type) {
    335   sticky_dirty_origins_.insert(std::make_pair(origin, type));
    336   quota_observer()->SetUsageCacheEnabled(origin, type, false);
    337   InvalidateUsageCache(origin, type);
    338 }
    339 
    340 base::FilePath SandboxContext::GetUsageCachePathForOriginAndType(
    341     const GURL& origin_url,
    342     FileSystemType type) {
    343   base::PlatformFileError error;
    344   base::FilePath path = GetUsageCachePathForOriginAndType(
    345       sync_file_util(), origin_url, type, &error);
    346   if (error != base::PLATFORM_FILE_OK)
    347     return base::FilePath();
    348   return path;
    349 }
    350 
    351 // static
    352 base::FilePath SandboxContext::GetUsageCachePathForOriginAndType(
    353     ObfuscatedFileUtil* sandbox_file_util,
    354     const GURL& origin_url,
    355     fileapi::FileSystemType type,
    356     base::PlatformFileError* error_out) {
    357   DCHECK(error_out);
    358   *error_out = base::PLATFORM_FILE_OK;
    359   base::FilePath base_path = sandbox_file_util->GetDirectoryForOriginAndType(
    360       origin_url, type, false /* create */, error_out);
    361   if (*error_out != base::PLATFORM_FILE_OK)
    362     return base::FilePath();
    363   return base_path.Append(FileSystemUsageCache::kUsageFileName);
    364 }
    365 
    366 int64 SandboxContext::RecalculateUsage(FileSystemContext* context,
    367                                        const GURL& origin,
    368                                        FileSystemType type) {
    369   FileSystemOperationContext operation_context(context);
    370   FileSystemURL url = context->CreateCrackedFileSystemURL(
    371       origin, type, base::FilePath());
    372   scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator> enumerator(
    373       sync_file_util()->CreateFileEnumerator(&operation_context, url, true));
    374 
    375   base::FilePath file_path_each;
    376   int64 usage = 0;
    377 
    378   while (!(file_path_each = enumerator->Next()).empty()) {
    379     usage += enumerator->Size();
    380     usage += ObfuscatedFileUtil::ComputeFilePathCost(file_path_each);
    381   }
    382 
    383   return usage;
    384 }
    385 
    386 void SandboxContext::CollectOpenFileSystemMetrics(
    387     base::PlatformFileError error_code) {
    388   base::Time now = base::Time::Now();
    389   bool throttled = now < next_release_time_for_open_filesystem_stat_;
    390   if (!throttled) {
    391     next_release_time_for_open_filesystem_stat_ =
    392         now + base::TimeDelta::FromHours(kMinimumStatsCollectionIntervalHours);
    393   }
    394 
    395 #define REPORT(report_value)                                            \
    396   UMA_HISTOGRAM_ENUMERATION(kOpenFileSystemDetailLabel,                 \
    397                             (report_value),                             \
    398                             kFileSystemErrorMax);                       \
    399   if (!throttled) {                                                     \
    400     UMA_HISTOGRAM_ENUMERATION(kOpenFileSystemDetailNonThrottledLabel,   \
    401                               (report_value),                           \
    402                               kFileSystemErrorMax);                     \
    403   }
    404 
    405   switch (error_code) {
    406     case base::PLATFORM_FILE_OK:
    407       REPORT(kOK);
    408       break;
    409     case base::PLATFORM_FILE_ERROR_INVALID_URL:
    410       REPORT(kInvalidSchemeError);
    411       break;
    412     case base::PLATFORM_FILE_ERROR_NOT_FOUND:
    413       REPORT(kNotFound);
    414       break;
    415     case base::PLATFORM_FILE_ERROR_FAILED:
    416     default:
    417       REPORT(kUnknownError);
    418       break;
    419   }
    420 #undef REPORT
    421 }
    422 
    423 ObfuscatedFileUtil* SandboxContext::sync_file_util() {
    424   return static_cast<ObfuscatedFileUtil*>(file_util()->sync_file_util());
    425 }
    426 
    427 }  // namespace fileapi
    428