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/obfuscated_file_util.h"
      6 
      7 #include <queue>
      8 #include <string>
      9 #include <vector>
     10 
     11 #include "base/file_util.h"
     12 #include "base/format_macros.h"
     13 #include "base/logging.h"
     14 #include "base/message_loop/message_loop.h"
     15 #include "base/metrics/histogram.h"
     16 #include "base/stl_util.h"
     17 #include "base/strings/string_number_conversions.h"
     18 #include "base/strings/stringprintf.h"
     19 #include "base/strings/sys_string_conversions.h"
     20 #include "base/strings/utf_string_conversions.h"
     21 #include "base/time/time.h"
     22 #include "url/gurl.h"
     23 #include "webkit/browser/fileapi/file_observers.h"
     24 #include "webkit/browser/fileapi/file_system_context.h"
     25 #include "webkit/browser/fileapi/file_system_operation_context.h"
     26 #include "webkit/browser/fileapi/file_system_url.h"
     27 #include "webkit/browser/fileapi/native_file_util.h"
     28 #include "webkit/browser/fileapi/sandbox_file_system_backend.h"
     29 #include "webkit/browser/fileapi/sandbox_isolated_origin_database.h"
     30 #include "webkit/browser/fileapi/sandbox_origin_database.h"
     31 #include "webkit/browser/fileapi/sandbox_prioritized_origin_database.h"
     32 #include "webkit/browser/fileapi/timed_task_helper.h"
     33 #include "webkit/browser/quota/quota_manager.h"
     34 #include "webkit/common/database/database_identifier.h"
     35 #include "webkit/common/fileapi/file_system_util.h"
     36 
     37 // Example of various paths:
     38 //   void ObfuscatedFileUtil::DoSomething(const FileSystemURL& url) {
     39 //     base::FilePath virtual_path = url.path();
     40 //     base::FilePath local_path = GetLocalFilePath(url);
     41 //
     42 //     NativeFileUtil::DoSomething(local_path);
     43 //     file_util::DoAnother(local_path);
     44 //  }
     45 
     46 namespace fileapi {
     47 
     48 namespace {
     49 
     50 typedef SandboxDirectoryDatabase::FileId FileId;
     51 typedef SandboxDirectoryDatabase::FileInfo FileInfo;
     52 
     53 void InitFileInfo(
     54     SandboxDirectoryDatabase::FileInfo* file_info,
     55     SandboxDirectoryDatabase::FileId parent_id,
     56     const base::FilePath::StringType& file_name) {
     57   DCHECK(file_info);
     58   file_info->parent_id = parent_id;
     59   file_info->name = file_name;
     60 }
     61 
     62 // Costs computed as per crbug.com/86114, based on the LevelDB implementation of
     63 // path storage under Linux.  It's not clear if that will differ on Windows, on
     64 // which base::FilePath uses wide chars [since they're converted to UTF-8 for
     65 // storage anyway], but as long as the cost is high enough that one can't cheat
     66 // on quota by storing data in paths, it doesn't need to be all that accurate.
     67 const int64 kPathCreationQuotaCost = 146;  // Bytes per inode, basically.
     68 const int64 kPathByteQuotaCost = 2;  // Bytes per byte of path length in UTF-8.
     69 
     70 int64 UsageForPath(size_t length) {
     71   return kPathCreationQuotaCost +
     72       static_cast<int64>(length) * kPathByteQuotaCost;
     73 }
     74 
     75 bool AllocateQuota(FileSystemOperationContext* context, int64 growth) {
     76   if (context->allowed_bytes_growth() == quota::QuotaManager::kNoLimit)
     77     return true;
     78 
     79   int64 new_quota = context->allowed_bytes_growth() - growth;
     80   if (growth > 0 && new_quota < 0)
     81     return false;
     82   context->set_allowed_bytes_growth(new_quota);
     83   return true;
     84 }
     85 
     86 void UpdateUsage(
     87     FileSystemOperationContext* context,
     88     const FileSystemURL& url,
     89     int64 growth) {
     90   context->update_observers()->Notify(
     91       &FileUpdateObserver::OnUpdate, MakeTuple(url, growth));
     92 }
     93 
     94 void TouchDirectory(SandboxDirectoryDatabase* db, FileId dir_id) {
     95   DCHECK(db);
     96   if (!db->UpdateModificationTime(dir_id, base::Time::Now()))
     97     NOTREACHED();
     98 }
     99 
    100 enum IsolatedOriginStatus {
    101   kIsolatedOriginMatch,
    102   kIsolatedOriginDontMatch,
    103   kIsolatedOriginStatusMax,
    104 };
    105 
    106 }  // namespace
    107 
    108 using base::PlatformFile;
    109 using base::PlatformFileError;
    110 
    111 class ObfuscatedFileEnumerator
    112     : public FileSystemFileUtil::AbstractFileEnumerator {
    113  public:
    114   ObfuscatedFileEnumerator(
    115       SandboxDirectoryDatabase* db,
    116       FileSystemOperationContext* context,
    117       ObfuscatedFileUtil* obfuscated_file_util,
    118       const FileSystemURL& root_url,
    119       bool recursive)
    120       : db_(db),
    121         context_(context),
    122         obfuscated_file_util_(obfuscated_file_util),
    123         root_url_(root_url),
    124         recursive_(recursive),
    125         current_file_id_(0) {
    126     base::FilePath root_virtual_path = root_url.path();
    127     FileId file_id;
    128 
    129     if (!db_->GetFileWithPath(root_virtual_path, &file_id))
    130       return;
    131 
    132     FileRecord record = { file_id, root_virtual_path };
    133     recurse_queue_.push(record);
    134   }
    135 
    136   virtual ~ObfuscatedFileEnumerator() {}
    137 
    138   virtual base::FilePath Next() OVERRIDE {
    139     ProcessRecurseQueue();
    140     if (display_stack_.empty())
    141       return base::FilePath();
    142 
    143     current_file_id_ = display_stack_.back();
    144     display_stack_.pop_back();
    145 
    146     FileInfo file_info;
    147     base::FilePath platform_file_path;
    148     base::PlatformFileError error =
    149         obfuscated_file_util_->GetFileInfoInternal(
    150             db_, context_, root_url_, current_file_id_,
    151             &file_info, &current_platform_file_info_, &platform_file_path);
    152     if (error != base::PLATFORM_FILE_OK)
    153       return Next();
    154 
    155     base::FilePath virtual_path =
    156         current_parent_virtual_path_.Append(file_info.name);
    157     if (recursive_ && file_info.is_directory()) {
    158       FileRecord record = { current_file_id_, virtual_path };
    159       recurse_queue_.push(record);
    160     }
    161     return virtual_path;
    162   }
    163 
    164   virtual int64 Size() OVERRIDE {
    165     return current_platform_file_info_.size;
    166   }
    167 
    168   virtual base::Time LastModifiedTime() OVERRIDE {
    169     return current_platform_file_info_.last_modified;
    170   }
    171 
    172   virtual bool IsDirectory() OVERRIDE {
    173     return current_platform_file_info_.is_directory;
    174   }
    175 
    176  private:
    177   typedef SandboxDirectoryDatabase::FileId FileId;
    178   typedef SandboxDirectoryDatabase::FileInfo FileInfo;
    179 
    180   struct FileRecord {
    181     FileId file_id;
    182     base::FilePath virtual_path;
    183   };
    184 
    185   void ProcessRecurseQueue() {
    186     while (display_stack_.empty() && !recurse_queue_.empty()) {
    187       FileRecord entry = recurse_queue_.front();
    188       recurse_queue_.pop();
    189       if (!db_->ListChildren(entry.file_id, &display_stack_)) {
    190         display_stack_.clear();
    191         return;
    192       }
    193       current_parent_virtual_path_ = entry.virtual_path;
    194     }
    195   }
    196 
    197   SandboxDirectoryDatabase* db_;
    198   FileSystemOperationContext* context_;
    199   ObfuscatedFileUtil* obfuscated_file_util_;
    200   FileSystemURL root_url_;
    201   bool recursive_;
    202 
    203   std::queue<FileRecord> recurse_queue_;
    204   std::vector<FileId> display_stack_;
    205   base::FilePath current_parent_virtual_path_;
    206 
    207   FileId current_file_id_;
    208   base::PlatformFileInfo current_platform_file_info_;
    209 };
    210 
    211 class ObfuscatedOriginEnumerator
    212     : public ObfuscatedFileUtil::AbstractOriginEnumerator {
    213  public:
    214   typedef SandboxOriginDatabase::OriginRecord OriginRecord;
    215   ObfuscatedOriginEnumerator(
    216       SandboxOriginDatabaseInterface* origin_database,
    217       const base::FilePath& base_file_path)
    218       : base_file_path_(base_file_path) {
    219     if (origin_database)
    220       origin_database->ListAllOrigins(&origins_);
    221   }
    222 
    223   virtual ~ObfuscatedOriginEnumerator() {}
    224 
    225   // Returns the next origin.  Returns empty if there are no more origins.
    226   virtual GURL Next() OVERRIDE {
    227     OriginRecord record;
    228     if (!origins_.empty()) {
    229       record = origins_.back();
    230       origins_.pop_back();
    231     }
    232     current_ = record;
    233     return webkit_database::GetOriginFromIdentifier(record.origin);
    234   }
    235 
    236   // Returns the current origin's information.
    237   virtual bool HasTypeDirectory(const std::string& type_string) const OVERRIDE {
    238     if (current_.path.empty())
    239       return false;
    240     if (type_string.empty()) {
    241       NOTREACHED();
    242       return false;
    243     }
    244     base::FilePath path =
    245         base_file_path_.Append(current_.path).AppendASCII(type_string);
    246     return base::DirectoryExists(path);
    247   }
    248 
    249  private:
    250   std::vector<OriginRecord> origins_;
    251   OriginRecord current_;
    252   base::FilePath base_file_path_;
    253 };
    254 
    255 ObfuscatedFileUtil::ObfuscatedFileUtil(
    256     quota::SpecialStoragePolicy* special_storage_policy,
    257     const base::FilePath& file_system_directory,
    258     base::SequencedTaskRunner* file_task_runner,
    259     const GetTypeStringForURLCallback& get_type_string_for_url,
    260     const std::set<std::string>& known_type_strings,
    261     SandboxFileSystemBackendDelegate* sandbox_delegate)
    262     : special_storage_policy_(special_storage_policy),
    263       file_system_directory_(file_system_directory),
    264       db_flush_delay_seconds_(10 * 60),  // 10 mins.
    265       file_task_runner_(file_task_runner),
    266       get_type_string_for_url_(get_type_string_for_url),
    267       known_type_strings_(known_type_strings),
    268       sandbox_delegate_(sandbox_delegate) {
    269 }
    270 
    271 ObfuscatedFileUtil::~ObfuscatedFileUtil() {
    272   DropDatabases();
    273 }
    274 
    275 PlatformFileError ObfuscatedFileUtil::CreateOrOpen(
    276     FileSystemOperationContext* context,
    277     const FileSystemURL& url, int file_flags,
    278     PlatformFile* file_handle, bool* created) {
    279   PlatformFileError error = CreateOrOpenInternal(context, url, file_flags,
    280                                                  file_handle, created);
    281   if (*file_handle != base::kInvalidPlatformFileValue &&
    282       file_flags & base::PLATFORM_FILE_WRITE &&
    283       context->quota_limit_type() == quota::kQuotaLimitTypeUnlimited &&
    284       sandbox_delegate_) {
    285     DCHECK_EQ(base::PLATFORM_FILE_OK, error);
    286     sandbox_delegate_->StickyInvalidateUsageCache(url.origin(), url.type());
    287   }
    288   return error;
    289 }
    290 
    291 PlatformFileError ObfuscatedFileUtil::Close(
    292     FileSystemOperationContext* context,
    293     base::PlatformFile file) {
    294   return NativeFileUtil::Close(file);
    295 }
    296 
    297 PlatformFileError ObfuscatedFileUtil::EnsureFileExists(
    298     FileSystemOperationContext* context,
    299     const FileSystemURL& url,
    300     bool* created) {
    301   SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, true);
    302   if (!db)
    303     return base::PLATFORM_FILE_ERROR_FAILED;
    304 
    305   FileId file_id;
    306   if (db->GetFileWithPath(url.path(), &file_id)) {
    307     FileInfo file_info;
    308     if (!db->GetFileInfo(file_id, &file_info)) {
    309       NOTREACHED();
    310       return base::PLATFORM_FILE_ERROR_FAILED;
    311     }
    312     if (file_info.is_directory())
    313       return base::PLATFORM_FILE_ERROR_NOT_A_FILE;
    314     if (created)
    315       *created = false;
    316     return base::PLATFORM_FILE_OK;
    317   }
    318   FileId parent_id;
    319   if (!db->GetFileWithPath(VirtualPath::DirName(url.path()), &parent_id))
    320     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
    321 
    322   FileInfo file_info;
    323   InitFileInfo(&file_info, parent_id,
    324                VirtualPath::BaseName(url.path()).value());
    325 
    326   int64 growth = UsageForPath(file_info.name.size());
    327   if (!AllocateQuota(context, growth))
    328     return base::PLATFORM_FILE_ERROR_NO_SPACE;
    329   PlatformFileError error = CreateFile(
    330       context, base::FilePath(), url, &file_info, 0, NULL);
    331   if (created && base::PLATFORM_FILE_OK == error) {
    332     *created = true;
    333     UpdateUsage(context, url, growth);
    334     context->change_observers()->Notify(
    335         &FileChangeObserver::OnCreateFile, MakeTuple(url));
    336   }
    337   return error;
    338 }
    339 
    340 PlatformFileError ObfuscatedFileUtil::CreateDirectory(
    341     FileSystemOperationContext* context,
    342     const FileSystemURL& url,
    343     bool exclusive,
    344     bool recursive) {
    345   SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, true);
    346   if (!db)
    347     return base::PLATFORM_FILE_ERROR_FAILED;
    348 
    349   FileId file_id;
    350   if (db->GetFileWithPath(url.path(), &file_id)) {
    351     FileInfo file_info;
    352     if (exclusive)
    353       return base::PLATFORM_FILE_ERROR_EXISTS;
    354     if (!db->GetFileInfo(file_id, &file_info)) {
    355       NOTREACHED();
    356       return base::PLATFORM_FILE_ERROR_FAILED;
    357     }
    358     if (!file_info.is_directory())
    359       return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
    360     return base::PLATFORM_FILE_OK;
    361   }
    362 
    363   std::vector<base::FilePath::StringType> components;
    364   VirtualPath::GetComponents(url.path(), &components);
    365   FileId parent_id = 0;
    366   size_t index;
    367   for (index = 0; index < components.size(); ++index) {
    368     base::FilePath::StringType name = components[index];
    369     if (name == FILE_PATH_LITERAL("/"))
    370       continue;
    371     if (!db->GetChildWithName(parent_id, name, &parent_id))
    372       break;
    373   }
    374   if (!db->IsDirectory(parent_id))
    375     return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
    376   if (!recursive && components.size() - index > 1)
    377     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
    378   bool first = true;
    379   for (; index < components.size(); ++index) {
    380     FileInfo file_info;
    381     file_info.name = components[index];
    382     if (file_info.name == FILE_PATH_LITERAL("/"))
    383       continue;
    384     file_info.modification_time = base::Time::Now();
    385     file_info.parent_id = parent_id;
    386     int64 growth = UsageForPath(file_info.name.size());
    387     if (!AllocateQuota(context, growth))
    388       return base::PLATFORM_FILE_ERROR_NO_SPACE;
    389     base::PlatformFileError error = db->AddFileInfo(file_info, &parent_id);
    390     if (error != base::PLATFORM_FILE_OK)
    391       return error;
    392     UpdateUsage(context, url, growth);
    393     context->change_observers()->Notify(
    394         &FileChangeObserver::OnCreateDirectory, MakeTuple(url));
    395     if (first) {
    396       first = false;
    397       TouchDirectory(db, file_info.parent_id);
    398     }
    399   }
    400   return base::PLATFORM_FILE_OK;
    401 }
    402 
    403 PlatformFileError ObfuscatedFileUtil::GetFileInfo(
    404     FileSystemOperationContext* context,
    405     const FileSystemURL& url,
    406     base::PlatformFileInfo* file_info,
    407     base::FilePath* platform_file_path) {
    408   SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, false);
    409   if (!db)
    410     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
    411   FileId file_id;
    412   if (!db->GetFileWithPath(url.path(), &file_id))
    413     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
    414   FileInfo local_info;
    415   return GetFileInfoInternal(db, context, url,
    416                              file_id, &local_info,
    417                              file_info, platform_file_path);
    418 }
    419 
    420 scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator>
    421     ObfuscatedFileUtil::CreateFileEnumerator(
    422     FileSystemOperationContext* context,
    423     const FileSystemURL& root_url) {
    424   return CreateFileEnumerator(context, root_url, false /* recursive */);
    425 }
    426 
    427 PlatformFileError ObfuscatedFileUtil::GetLocalFilePath(
    428     FileSystemOperationContext* context,
    429     const FileSystemURL& url,
    430     base::FilePath* local_path) {
    431   SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, false);
    432   if (!db)
    433     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
    434   FileId file_id;
    435   if (!db->GetFileWithPath(url.path(), &file_id))
    436     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
    437   FileInfo file_info;
    438   if (!db->GetFileInfo(file_id, &file_info) || file_info.is_directory()) {
    439     NOTREACHED();
    440     // Directories have no local file path.
    441     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
    442   }
    443   *local_path = DataPathToLocalPath(url, file_info.data_path);
    444 
    445   if (local_path->empty())
    446     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
    447   return base::PLATFORM_FILE_OK;
    448 }
    449 
    450 PlatformFileError ObfuscatedFileUtil::Touch(
    451     FileSystemOperationContext* context,
    452     const FileSystemURL& url,
    453     const base::Time& last_access_time,
    454     const base::Time& last_modified_time) {
    455   SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, false);
    456   if (!db)
    457     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
    458   FileId file_id;
    459   if (!db->GetFileWithPath(url.path(), &file_id))
    460     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
    461 
    462   FileInfo file_info;
    463   if (!db->GetFileInfo(file_id, &file_info)) {
    464     NOTREACHED();
    465     return base::PLATFORM_FILE_ERROR_FAILED;
    466   }
    467   if (file_info.is_directory()) {
    468     if (!db->UpdateModificationTime(file_id, last_modified_time))
    469       return base::PLATFORM_FILE_ERROR_FAILED;
    470     return base::PLATFORM_FILE_OK;
    471   }
    472   return NativeFileUtil::Touch(
    473       DataPathToLocalPath(url, file_info.data_path),
    474       last_access_time, last_modified_time);
    475 }
    476 
    477 PlatformFileError ObfuscatedFileUtil::Truncate(
    478     FileSystemOperationContext* context,
    479     const FileSystemURL& url,
    480     int64 length) {
    481   base::PlatformFileInfo file_info;
    482   base::FilePath local_path;
    483   base::PlatformFileError error =
    484       GetFileInfo(context, url, &file_info, &local_path);
    485   if (error != base::PLATFORM_FILE_OK)
    486     return error;
    487 
    488   int64 growth = length - file_info.size;
    489   if (!AllocateQuota(context, growth))
    490     return base::PLATFORM_FILE_ERROR_NO_SPACE;
    491   error = NativeFileUtil::Truncate(local_path, length);
    492   if (error == base::PLATFORM_FILE_OK) {
    493     UpdateUsage(context, url, growth);
    494     context->change_observers()->Notify(
    495         &FileChangeObserver::OnModifyFile, MakeTuple(url));
    496   }
    497   return error;
    498 }
    499 
    500 PlatformFileError ObfuscatedFileUtil::CopyOrMoveFile(
    501     FileSystemOperationContext* context,
    502     const FileSystemURL& src_url,
    503     const FileSystemURL& dest_url,
    504     CopyOrMoveOption option,
    505     bool copy) {
    506   // Cross-filesystem copies and moves should be handled via CopyInForeignFile.
    507   DCHECK(src_url.origin() == dest_url.origin());
    508   DCHECK(src_url.type() == dest_url.type());
    509 
    510   SandboxDirectoryDatabase* db = GetDirectoryDatabase(src_url, true);
    511   if (!db)
    512     return base::PLATFORM_FILE_ERROR_FAILED;
    513 
    514   FileId src_file_id;
    515   if (!db->GetFileWithPath(src_url.path(), &src_file_id))
    516     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
    517 
    518   FileId dest_file_id;
    519   bool overwrite = db->GetFileWithPath(dest_url.path(),
    520                                        &dest_file_id);
    521 
    522   FileInfo src_file_info;
    523   base::PlatformFileInfo src_platform_file_info;
    524   base::FilePath src_local_path;
    525   base::PlatformFileError error = GetFileInfoInternal(
    526       db, context, src_url, src_file_id,
    527       &src_file_info, &src_platform_file_info, &src_local_path);
    528   if (error != base::PLATFORM_FILE_OK)
    529     return error;
    530   if (src_file_info.is_directory())
    531     return base::PLATFORM_FILE_ERROR_NOT_A_FILE;
    532 
    533   FileInfo dest_file_info;
    534   base::PlatformFileInfo dest_platform_file_info;  // overwrite case only
    535   base::FilePath dest_local_path;  // overwrite case only
    536   if (overwrite) {
    537     base::PlatformFileError error = GetFileInfoInternal(
    538         db, context, dest_url, dest_file_id,
    539         &dest_file_info, &dest_platform_file_info, &dest_local_path);
    540     if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND)
    541       overwrite = false;  // fallback to non-overwrite case
    542     else if (error != base::PLATFORM_FILE_OK)
    543       return error;
    544     else if (dest_file_info.is_directory())
    545       return base::PLATFORM_FILE_ERROR_INVALID_OPERATION;
    546   }
    547   if (!overwrite) {
    548     FileId dest_parent_id;
    549     if (!db->GetFileWithPath(VirtualPath::DirName(dest_url.path()),
    550                              &dest_parent_id)) {
    551       return base::PLATFORM_FILE_ERROR_NOT_FOUND;
    552     }
    553 
    554     dest_file_info = src_file_info;
    555     dest_file_info.parent_id = dest_parent_id;
    556     dest_file_info.name =
    557         VirtualPath::BaseName(dest_url.path()).value();
    558   }
    559 
    560   int64 growth = 0;
    561   if (copy)
    562     growth += src_platform_file_info.size;
    563   else
    564     growth -= UsageForPath(src_file_info.name.size());
    565   if (overwrite)
    566     growth -= dest_platform_file_info.size;
    567   else
    568     growth += UsageForPath(dest_file_info.name.size());
    569   if (!AllocateQuota(context, growth))
    570     return base::PLATFORM_FILE_ERROR_NO_SPACE;
    571 
    572   /*
    573    * Copy-with-overwrite
    574    *  Just overwrite data file
    575    * Copy-without-overwrite
    576    *  Copy backing file
    577    *  Create new metadata pointing to new backing file.
    578    * Move-with-overwrite
    579    *  transaction:
    580    *    Remove source entry.
    581    *    Point target entry to source entry's backing file.
    582    *  Delete target entry's old backing file
    583    * Move-without-overwrite
    584    *  Just update metadata
    585    */
    586   error = base::PLATFORM_FILE_ERROR_FAILED;
    587   if (copy) {
    588     if (overwrite) {
    589       error = NativeFileUtil::CopyOrMoveFile(
    590           src_local_path,
    591           dest_local_path,
    592           option,
    593           fileapi::NativeFileUtil::CopyOrMoveModeForDestination(
    594               dest_url, true /* copy */));
    595     } else {  // non-overwrite
    596       error = CreateFile(context, src_local_path,
    597                          dest_url, &dest_file_info, 0, NULL);
    598     }
    599   } else {
    600     if (overwrite) {
    601       if (db->OverwritingMoveFile(src_file_id, dest_file_id)) {
    602         if (base::PLATFORM_FILE_OK !=
    603             NativeFileUtil::DeleteFile(dest_local_path))
    604           LOG(WARNING) << "Leaked a backing file.";
    605         error = base::PLATFORM_FILE_OK;
    606       } else {
    607         error = base::PLATFORM_FILE_ERROR_FAILED;
    608       }
    609     } else {  // non-overwrite
    610       if (db->UpdateFileInfo(src_file_id, dest_file_info))
    611         error = base::PLATFORM_FILE_OK;
    612       else
    613         error = base::PLATFORM_FILE_ERROR_FAILED;
    614     }
    615   }
    616 
    617   if (error != base::PLATFORM_FILE_OK)
    618     return error;
    619 
    620   if (overwrite) {
    621     context->change_observers()->Notify(
    622         &FileChangeObserver::OnModifyFile,
    623         MakeTuple(dest_url));
    624   } else {
    625     context->change_observers()->Notify(
    626         &FileChangeObserver::OnCreateFileFrom,
    627         MakeTuple(dest_url, src_url));
    628   }
    629 
    630   if (!copy) {
    631     context->change_observers()->Notify(
    632         &FileChangeObserver::OnRemoveFile, MakeTuple(src_url));
    633     TouchDirectory(db, src_file_info.parent_id);
    634   }
    635 
    636   TouchDirectory(db, dest_file_info.parent_id);
    637 
    638   UpdateUsage(context, dest_url, growth);
    639   return error;
    640 }
    641 
    642 PlatformFileError ObfuscatedFileUtil::CopyInForeignFile(
    643     FileSystemOperationContext* context,
    644     const base::FilePath& src_file_path,
    645     const FileSystemURL& dest_url) {
    646   SandboxDirectoryDatabase* db = GetDirectoryDatabase(dest_url, true);
    647   if (!db)
    648     return base::PLATFORM_FILE_ERROR_FAILED;
    649 
    650   base::PlatformFileInfo src_platform_file_info;
    651   if (!base::GetFileInfo(src_file_path, &src_platform_file_info))
    652     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
    653 
    654   FileId dest_file_id;
    655   bool overwrite = db->GetFileWithPath(dest_url.path(),
    656                                        &dest_file_id);
    657 
    658   FileInfo dest_file_info;
    659   base::PlatformFileInfo dest_platform_file_info;  // overwrite case only
    660   if (overwrite) {
    661     base::FilePath dest_local_path;
    662     base::PlatformFileError error = GetFileInfoInternal(
    663         db, context, dest_url, dest_file_id,
    664         &dest_file_info, &dest_platform_file_info, &dest_local_path);
    665     if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND)
    666       overwrite = false;  // fallback to non-overwrite case
    667     else if (error != base::PLATFORM_FILE_OK)
    668       return error;
    669     else if (dest_file_info.is_directory())
    670       return base::PLATFORM_FILE_ERROR_INVALID_OPERATION;
    671   }
    672   if (!overwrite) {
    673     FileId dest_parent_id;
    674     if (!db->GetFileWithPath(VirtualPath::DirName(dest_url.path()),
    675                              &dest_parent_id)) {
    676       return base::PLATFORM_FILE_ERROR_NOT_FOUND;
    677     }
    678     if (!dest_file_info.is_directory())
    679       return base::PLATFORM_FILE_ERROR_FAILED;
    680     InitFileInfo(&dest_file_info, dest_parent_id,
    681                  VirtualPath::BaseName(dest_url.path()).value());
    682   }
    683 
    684   int64 growth = src_platform_file_info.size;
    685   if (overwrite)
    686     growth -= dest_platform_file_info.size;
    687   else
    688     growth += UsageForPath(dest_file_info.name.size());
    689   if (!AllocateQuota(context, growth))
    690     return base::PLATFORM_FILE_ERROR_NO_SPACE;
    691 
    692   base::PlatformFileError error;
    693   if (overwrite) {
    694     base::FilePath dest_local_path =
    695         DataPathToLocalPath(dest_url, dest_file_info.data_path);
    696     error = NativeFileUtil::CopyOrMoveFile(
    697         src_file_path, dest_local_path,
    698         FileSystemOperation::OPTION_NONE,
    699         fileapi::NativeFileUtil::CopyOrMoveModeForDestination(dest_url,
    700                                                               true /* copy */));
    701   } else {
    702     error = CreateFile(context, src_file_path,
    703                        dest_url, &dest_file_info, 0, NULL);
    704   }
    705 
    706   if (error != base::PLATFORM_FILE_OK)
    707     return error;
    708 
    709   if (overwrite) {
    710     context->change_observers()->Notify(
    711         &FileChangeObserver::OnModifyFile, MakeTuple(dest_url));
    712   } else {
    713     context->change_observers()->Notify(
    714         &FileChangeObserver::OnCreateFile, MakeTuple(dest_url));
    715   }
    716 
    717   UpdateUsage(context, dest_url, growth);
    718   TouchDirectory(db, dest_file_info.parent_id);
    719   return base::PLATFORM_FILE_OK;
    720 }
    721 
    722 PlatformFileError ObfuscatedFileUtil::DeleteFile(
    723     FileSystemOperationContext* context,
    724     const FileSystemURL& url) {
    725   SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, true);
    726   if (!db)
    727     return base::PLATFORM_FILE_ERROR_FAILED;
    728   FileId file_id;
    729   if (!db->GetFileWithPath(url.path(), &file_id))
    730     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
    731 
    732   FileInfo file_info;
    733   base::PlatformFileInfo platform_file_info;
    734   base::FilePath local_path;
    735   base::PlatformFileError error = GetFileInfoInternal(
    736       db, context, url, file_id, &file_info, &platform_file_info, &local_path);
    737   if (error != base::PLATFORM_FILE_ERROR_NOT_FOUND &&
    738       error != base::PLATFORM_FILE_OK)
    739     return error;
    740 
    741   if (file_info.is_directory())
    742     return base::PLATFORM_FILE_ERROR_NOT_A_FILE;
    743 
    744   int64 growth = -UsageForPath(file_info.name.size()) - platform_file_info.size;
    745   AllocateQuota(context, growth);
    746   if (!db->RemoveFileInfo(file_id)) {
    747     NOTREACHED();
    748     return base::PLATFORM_FILE_ERROR_FAILED;
    749   }
    750   UpdateUsage(context, url, growth);
    751   TouchDirectory(db, file_info.parent_id);
    752 
    753   context->change_observers()->Notify(
    754       &FileChangeObserver::OnRemoveFile, MakeTuple(url));
    755 
    756   if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND)
    757     return base::PLATFORM_FILE_OK;
    758 
    759   error = NativeFileUtil::DeleteFile(local_path);
    760   if (base::PLATFORM_FILE_OK != error)
    761     LOG(WARNING) << "Leaked a backing file.";
    762   return base::PLATFORM_FILE_OK;
    763 }
    764 
    765 PlatformFileError ObfuscatedFileUtil::DeleteDirectory(
    766     FileSystemOperationContext* context,
    767     const FileSystemURL& url) {
    768   SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, true);
    769   if (!db)
    770     return base::PLATFORM_FILE_ERROR_FAILED;
    771 
    772   FileId file_id;
    773   if (!db->GetFileWithPath(url.path(), &file_id))
    774     return base::PLATFORM_FILE_ERROR_NOT_FOUND;
    775   FileInfo file_info;
    776   if (!db->GetFileInfo(file_id, &file_info)) {
    777     NOTREACHED();
    778     return base::PLATFORM_FILE_ERROR_FAILED;
    779   }
    780   if (!file_info.is_directory())
    781     return base::PLATFORM_FILE_ERROR_NOT_A_DIRECTORY;
    782   if (!db->RemoveFileInfo(file_id))
    783     return base::PLATFORM_FILE_ERROR_NOT_EMPTY;
    784   int64 growth = -UsageForPath(file_info.name.size());
    785   AllocateQuota(context, growth);
    786   UpdateUsage(context, url, growth);
    787   TouchDirectory(db, file_info.parent_id);
    788   context->change_observers()->Notify(
    789       &FileChangeObserver::OnRemoveDirectory, MakeTuple(url));
    790   return base::PLATFORM_FILE_OK;
    791 }
    792 
    793 webkit_blob::ScopedFile ObfuscatedFileUtil::CreateSnapshotFile(
    794     FileSystemOperationContext* context,
    795     const FileSystemURL& url,
    796     base::PlatformFileError* error,
    797     base::PlatformFileInfo* file_info,
    798     base::FilePath* platform_path) {
    799   // We're just returning the local file information.
    800   *error = GetFileInfo(context, url, file_info, platform_path);
    801   if (*error == base::PLATFORM_FILE_OK && file_info->is_directory) {
    802     *file_info = base::PlatformFileInfo();
    803     *error = base::PLATFORM_FILE_ERROR_NOT_A_FILE;
    804   }
    805   return webkit_blob::ScopedFile();
    806 }
    807 
    808 scoped_ptr<FileSystemFileUtil::AbstractFileEnumerator>
    809     ObfuscatedFileUtil::CreateFileEnumerator(
    810     FileSystemOperationContext* context,
    811     const FileSystemURL& root_url,
    812     bool recursive) {
    813   SandboxDirectoryDatabase* db = GetDirectoryDatabase(root_url, false);
    814   if (!db) {
    815     return scoped_ptr<AbstractFileEnumerator>(new EmptyFileEnumerator());
    816   }
    817   return scoped_ptr<AbstractFileEnumerator>(
    818       new ObfuscatedFileEnumerator(db, context, this, root_url, recursive));
    819 }
    820 
    821 bool ObfuscatedFileUtil::IsDirectoryEmpty(
    822     FileSystemOperationContext* context,
    823     const FileSystemURL& url) {
    824   SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, false);
    825   if (!db)
    826     return true;  // Not a great answer, but it's what others do.
    827   FileId file_id;
    828   if (!db->GetFileWithPath(url.path(), &file_id))
    829     return true;  // Ditto.
    830   FileInfo file_info;
    831   if (!db->GetFileInfo(file_id, &file_info)) {
    832     DCHECK(!file_id);
    833     // It's the root directory and the database hasn't been initialized yet.
    834     return true;
    835   }
    836   if (!file_info.is_directory())
    837     return true;
    838   std::vector<FileId> children;
    839   // TODO(ericu): This could easily be made faster with help from the database.
    840   if (!db->ListChildren(file_id, &children))
    841     return true;
    842   return children.empty();
    843 }
    844 
    845 base::FilePath ObfuscatedFileUtil::GetDirectoryForOriginAndType(
    846     const GURL& origin,
    847     const std::string& type_string,
    848     bool create,
    849     base::PlatformFileError* error_code) {
    850   base::FilePath origin_dir = GetDirectoryForOrigin(origin, create, error_code);
    851   if (origin_dir.empty())
    852     return base::FilePath();
    853   if (type_string.empty())
    854     return origin_dir;
    855   base::FilePath path = origin_dir.AppendASCII(type_string);
    856   base::PlatformFileError error = base::PLATFORM_FILE_OK;
    857   if (!base::DirectoryExists(path) &&
    858       (!create || !base::CreateDirectory(path))) {
    859     error = create ?
    860           base::PLATFORM_FILE_ERROR_FAILED :
    861           base::PLATFORM_FILE_ERROR_NOT_FOUND;
    862   }
    863 
    864   if (error_code)
    865     *error_code = error;
    866   return path;
    867 }
    868 
    869 bool ObfuscatedFileUtil::DeleteDirectoryForOriginAndType(
    870     const GURL& origin,
    871     const std::string& type_string) {
    872   base::PlatformFileError error = base::PLATFORM_FILE_OK;
    873   base::FilePath origin_type_path = GetDirectoryForOriginAndType(
    874       origin, type_string, false, &error);
    875   if (origin_type_path.empty())
    876     return true;
    877   if (error != base::PLATFORM_FILE_ERROR_NOT_FOUND) {
    878     // TODO(dmikurube): Consider the return value of DestroyDirectoryDatabase.
    879     // We ignore its error now since 1) it doesn't matter the final result, and
    880     // 2) it always returns false in Windows because of LevelDB's
    881     // implementation.
    882     // Information about failure would be useful for debugging.
    883     if (!type_string.empty())
    884       DestroyDirectoryDatabase(origin, type_string);
    885     if (!base::DeleteFile(origin_type_path, true /* recursive */))
    886       return false;
    887   }
    888 
    889   base::FilePath origin_path = VirtualPath::DirName(origin_type_path);
    890   DCHECK_EQ(origin_path.value(),
    891             GetDirectoryForOrigin(origin, false, NULL).value());
    892 
    893   if (!type_string.empty()) {
    894     // At this point we are sure we had successfully deleted the origin/type
    895     // directory (i.e. we're ready to just return true).
    896     // See if we have other directories in this origin directory.
    897     for (std::set<std::string>::iterator iter = known_type_strings_.begin();
    898          iter != known_type_strings_.end();
    899          ++iter) {
    900       if (*iter == type_string)
    901         continue;
    902       if (base::DirectoryExists(origin_path.AppendASCII(*iter))) {
    903         // Other type's directory exists; just return true here.
    904         return true;
    905       }
    906     }
    907   }
    908 
    909   // No other directories seem exist. Try deleting the entire origin directory.
    910   InitOriginDatabase(origin, false);
    911   if (origin_database_) {
    912     origin_database_->RemovePathForOrigin(
    913         webkit_database::GetIdentifierFromOrigin(origin));
    914   }
    915   if (!base::DeleteFile(origin_path, true /* recursive */))
    916     return false;
    917 
    918   return true;
    919 }
    920 
    921 ObfuscatedFileUtil::AbstractOriginEnumerator*
    922 ObfuscatedFileUtil::CreateOriginEnumerator() {
    923   std::vector<SandboxOriginDatabase::OriginRecord> origins;
    924 
    925   InitOriginDatabase(GURL(), false);
    926   return new ObfuscatedOriginEnumerator(
    927       origin_database_.get(), file_system_directory_);
    928 }
    929 
    930 bool ObfuscatedFileUtil::DestroyDirectoryDatabase(
    931     const GURL& origin,
    932     const std::string& type_string) {
    933   std::string key = GetDirectoryDatabaseKey(origin, type_string);
    934   if (key.empty())
    935     return true;
    936   DirectoryMap::iterator iter = directories_.find(key);
    937   if (iter != directories_.end()) {
    938     SandboxDirectoryDatabase* database = iter->second;
    939     directories_.erase(iter);
    940     delete database;
    941   }
    942 
    943   PlatformFileError error = base::PLATFORM_FILE_OK;
    944   base::FilePath path = GetDirectoryForOriginAndType(
    945       origin, type_string, false, &error);
    946   if (path.empty() || error == base::PLATFORM_FILE_ERROR_NOT_FOUND)
    947     return true;
    948   return SandboxDirectoryDatabase::DestroyDatabase(path);
    949 }
    950 
    951 // static
    952 int64 ObfuscatedFileUtil::ComputeFilePathCost(const base::FilePath& path) {
    953   return UsageForPath(VirtualPath::BaseName(path).value().size());
    954 }
    955 
    956 void ObfuscatedFileUtil::MaybePrepopulateDatabase(
    957     const std::vector<std::string>& type_strings_to_prepopulate) {
    958   SandboxPrioritizedOriginDatabase database(file_system_directory_);
    959   std::string origin_string = database.GetPrimaryOrigin();
    960   if (origin_string.empty() || !database.HasOriginPath(origin_string))
    961     return;
    962   const GURL origin = webkit_database::GetOriginFromIdentifier(origin_string);
    963 
    964   // Prepopulate the directory database(s) if and only if this instance
    965   // has primary origin and the directory database is already there.
    966   for (size_t i = 0; i < type_strings_to_prepopulate.size(); ++i) {
    967     const std::string type_string = type_strings_to_prepopulate[i];
    968     // Only handles known types.
    969     if (!ContainsKey(known_type_strings_, type_string))
    970       continue;
    971     PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
    972     base::FilePath path = GetDirectoryForOriginAndType(
    973         origin, type_string, false, &error);
    974     if (error != base::PLATFORM_FILE_OK)
    975       continue;
    976     scoped_ptr<SandboxDirectoryDatabase> db(new SandboxDirectoryDatabase(path));
    977     if (db->Init(SandboxDirectoryDatabase::FAIL_ON_CORRUPTION)) {
    978       directories_[GetDirectoryDatabaseKey(origin, type_string)] = db.release();
    979       MarkUsed();
    980       // Don't populate more than one database, as it may rather hurt
    981       // performance.
    982       break;
    983     }
    984   }
    985 }
    986 
    987 base::FilePath ObfuscatedFileUtil::GetDirectoryForURL(
    988     const FileSystemURL& url,
    989     bool create,
    990     base::PlatformFileError* error_code) {
    991   return GetDirectoryForOriginAndType(
    992       url.origin(), CallGetTypeStringForURL(url), create, error_code);
    993 }
    994 
    995 std::string ObfuscatedFileUtil::CallGetTypeStringForURL(
    996     const FileSystemURL& url) {
    997   DCHECK(!get_type_string_for_url_.is_null());
    998   return get_type_string_for_url_.Run(url);
    999 }
   1000 
   1001 PlatformFileError ObfuscatedFileUtil::GetFileInfoInternal(
   1002     SandboxDirectoryDatabase* db,
   1003     FileSystemOperationContext* context,
   1004     const FileSystemURL& url,
   1005     FileId file_id,
   1006     FileInfo* local_info,
   1007     base::PlatformFileInfo* file_info,
   1008     base::FilePath* platform_file_path) {
   1009   DCHECK(db);
   1010   DCHECK(context);
   1011   DCHECK(file_info);
   1012   DCHECK(platform_file_path);
   1013 
   1014   if (!db->GetFileInfo(file_id, local_info)) {
   1015     NOTREACHED();
   1016     return base::PLATFORM_FILE_ERROR_FAILED;
   1017   }
   1018 
   1019   if (local_info->is_directory()) {
   1020     file_info->size = 0;
   1021     file_info->is_directory = true;
   1022     file_info->is_symbolic_link = false;
   1023     file_info->last_modified = local_info->modification_time;
   1024     *platform_file_path = base::FilePath();
   1025     // We don't fill in ctime or atime.
   1026     return base::PLATFORM_FILE_OK;
   1027   }
   1028   if (local_info->data_path.empty())
   1029     return base::PLATFORM_FILE_ERROR_INVALID_OPERATION;
   1030   base::FilePath local_path = DataPathToLocalPath(url, local_info->data_path);
   1031   base::PlatformFileError error = NativeFileUtil::GetFileInfo(
   1032       local_path, file_info);
   1033   // We should not follow symbolic links in sandboxed file system.
   1034   if (base::IsLink(local_path)) {
   1035     LOG(WARNING) << "Found a symbolic file.";
   1036     error = base::PLATFORM_FILE_ERROR_NOT_FOUND;
   1037   }
   1038   if (error == base::PLATFORM_FILE_OK) {
   1039     *platform_file_path = local_path;
   1040   } else if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND) {
   1041     LOG(WARNING) << "Lost a backing file.";
   1042     InvalidateUsageCache(context, url.origin(), url.type());
   1043     if (!db->RemoveFileInfo(file_id))
   1044       return base::PLATFORM_FILE_ERROR_FAILED;
   1045   }
   1046   return error;
   1047 }
   1048 
   1049 PlatformFileError ObfuscatedFileUtil::CreateFile(
   1050     FileSystemOperationContext* context,
   1051     const base::FilePath& src_file_path,
   1052     const FileSystemURL& dest_url,
   1053     FileInfo* dest_file_info, int file_flags, PlatformFile* handle) {
   1054   if (handle)
   1055     *handle = base::kInvalidPlatformFileValue;
   1056   SandboxDirectoryDatabase* db = GetDirectoryDatabase(dest_url, true);
   1057 
   1058   PlatformFileError error = base::PLATFORM_FILE_OK;
   1059   base::FilePath root = GetDirectoryForURL(dest_url, false, &error);
   1060   if (error != base::PLATFORM_FILE_OK)
   1061     return error;
   1062 
   1063   base::FilePath dest_local_path;
   1064   error = GenerateNewLocalPath(db, context, dest_url, &dest_local_path);
   1065   if (error != base::PLATFORM_FILE_OK)
   1066     return error;
   1067 
   1068   bool created = false;
   1069   if (!src_file_path.empty()) {
   1070     DCHECK(!file_flags);
   1071     DCHECK(!handle);
   1072     error = NativeFileUtil::CopyOrMoveFile(
   1073         src_file_path, dest_local_path,
   1074         FileSystemOperation::OPTION_NONE,
   1075         fileapi::NativeFileUtil::CopyOrMoveModeForDestination(dest_url,
   1076                                                               true /* copy */));
   1077     created = true;
   1078   } else {
   1079     if (base::PathExists(dest_local_path)) {
   1080       if (!base::DeleteFile(dest_local_path, true /* recursive */)) {
   1081         NOTREACHED();
   1082         return base::PLATFORM_FILE_ERROR_FAILED;
   1083       }
   1084       LOG(WARNING) << "A stray file detected";
   1085       InvalidateUsageCache(context, dest_url.origin(), dest_url.type());
   1086     }
   1087 
   1088     if (handle) {
   1089       error = NativeFileUtil::CreateOrOpen(
   1090           dest_local_path, file_flags, handle, &created);
   1091       // If this succeeds, we must close handle on any subsequent error.
   1092     } else {
   1093       DCHECK(!file_flags);  // file_flags is only used by CreateOrOpen.
   1094       error = NativeFileUtil::EnsureFileExists(dest_local_path, &created);
   1095     }
   1096   }
   1097   if (error != base::PLATFORM_FILE_OK)
   1098     return error;
   1099 
   1100   if (!created) {
   1101     NOTREACHED();
   1102     if (handle) {
   1103       DCHECK_NE(base::kInvalidPlatformFileValue, *handle);
   1104       base::ClosePlatformFile(*handle);
   1105       base::DeleteFile(dest_local_path, false /* recursive */);
   1106       *handle = base::kInvalidPlatformFileValue;
   1107     }
   1108     return base::PLATFORM_FILE_ERROR_FAILED;
   1109   }
   1110 
   1111   // This removes the root, including the trailing slash, leaving a relative
   1112   // path.
   1113   dest_file_info->data_path = base::FilePath(
   1114       dest_local_path.value().substr(root.value().length() + 1));
   1115 
   1116   FileId file_id;
   1117   error = db->AddFileInfo(*dest_file_info, &file_id);
   1118   if (error != base::PLATFORM_FILE_OK) {
   1119     if (handle) {
   1120       DCHECK_NE(base::kInvalidPlatformFileValue, *handle);
   1121       base::ClosePlatformFile(*handle);
   1122       *handle = base::kInvalidPlatformFileValue;
   1123     }
   1124     base::DeleteFile(dest_local_path, false /* recursive */);
   1125     return error;
   1126   }
   1127   TouchDirectory(db, dest_file_info->parent_id);
   1128 
   1129   return base::PLATFORM_FILE_OK;
   1130 }
   1131 
   1132 base::FilePath ObfuscatedFileUtil::DataPathToLocalPath(
   1133     const FileSystemURL& url, const base::FilePath& data_path) {
   1134   PlatformFileError error = base::PLATFORM_FILE_OK;
   1135   base::FilePath root = GetDirectoryForURL(url, false, &error);
   1136   if (error != base::PLATFORM_FILE_OK)
   1137     return base::FilePath();
   1138   return root.Append(data_path);
   1139 }
   1140 
   1141 std::string ObfuscatedFileUtil::GetDirectoryDatabaseKey(
   1142     const GURL& origin, const std::string& type_string) {
   1143   if (type_string.empty()) {
   1144     LOG(WARNING) << "Unknown filesystem type requested:" << type_string;
   1145     return std::string();
   1146   }
   1147   // For isolated origin we just use a type string as a key.
   1148   return webkit_database::GetIdentifierFromOrigin(origin) +
   1149       type_string;
   1150 }
   1151 
   1152 // TODO(ericu): How to do the whole validation-without-creation thing?
   1153 // We may not have quota even to create the database.
   1154 // Ah, in that case don't even get here?
   1155 // Still doesn't answer the quota issue, though.
   1156 SandboxDirectoryDatabase* ObfuscatedFileUtil::GetDirectoryDatabase(
   1157     const FileSystemURL& url, bool create) {
   1158   std::string key = GetDirectoryDatabaseKey(
   1159       url.origin(), CallGetTypeStringForURL(url));
   1160   if (key.empty())
   1161     return NULL;
   1162 
   1163   DirectoryMap::iterator iter = directories_.find(key);
   1164   if (iter != directories_.end()) {
   1165     MarkUsed();
   1166     return iter->second;
   1167   }
   1168 
   1169   PlatformFileError error = base::PLATFORM_FILE_OK;
   1170   base::FilePath path = GetDirectoryForURL(url, create, &error);
   1171   if (error != base::PLATFORM_FILE_OK) {
   1172     LOG(WARNING) << "Failed to get origin+type directory: "
   1173                  << url.DebugString() << " error:" << error;
   1174     return NULL;
   1175   }
   1176   MarkUsed();
   1177   SandboxDirectoryDatabase* database = new SandboxDirectoryDatabase(path);
   1178   directories_[key] = database;
   1179   return database;
   1180 }
   1181 
   1182 base::FilePath ObfuscatedFileUtil::GetDirectoryForOrigin(
   1183     const GURL& origin, bool create, base::PlatformFileError* error_code) {
   1184   if (!InitOriginDatabase(origin, create)) {
   1185     if (error_code) {
   1186       *error_code = create ?
   1187           base::PLATFORM_FILE_ERROR_FAILED :
   1188           base::PLATFORM_FILE_ERROR_NOT_FOUND;
   1189     }
   1190     return base::FilePath();
   1191   }
   1192   base::FilePath directory_name;
   1193   std::string id = webkit_database::GetIdentifierFromOrigin(origin);
   1194 
   1195   bool exists_in_db = origin_database_->HasOriginPath(id);
   1196   if (!exists_in_db && !create) {
   1197     if (error_code)
   1198       *error_code = base::PLATFORM_FILE_ERROR_NOT_FOUND;
   1199     return base::FilePath();
   1200   }
   1201   if (!origin_database_->GetPathForOrigin(id, &directory_name)) {
   1202     if (error_code)
   1203       *error_code = base::PLATFORM_FILE_ERROR_FAILED;
   1204     return base::FilePath();
   1205   }
   1206 
   1207   base::FilePath path = file_system_directory_.Append(directory_name);
   1208   bool exists_in_fs = base::DirectoryExists(path);
   1209   if (!exists_in_db && exists_in_fs) {
   1210     if (!base::DeleteFile(path, true)) {
   1211       if (error_code)
   1212         *error_code = base::PLATFORM_FILE_ERROR_FAILED;
   1213       return base::FilePath();
   1214     }
   1215     exists_in_fs = false;
   1216   }
   1217 
   1218   if (!exists_in_fs) {
   1219     if (!create || !base::CreateDirectory(path)) {
   1220       if (error_code)
   1221         *error_code = create ?
   1222             base::PLATFORM_FILE_ERROR_FAILED :
   1223             base::PLATFORM_FILE_ERROR_NOT_FOUND;
   1224       return base::FilePath();
   1225     }
   1226   }
   1227 
   1228   if (error_code)
   1229     *error_code = base::PLATFORM_FILE_OK;
   1230 
   1231   return path;
   1232 }
   1233 
   1234 void ObfuscatedFileUtil::InvalidateUsageCache(
   1235     FileSystemOperationContext* context,
   1236     const GURL& origin,
   1237     FileSystemType type) {
   1238   if (sandbox_delegate_)
   1239     sandbox_delegate_->InvalidateUsageCache(origin, type);
   1240 }
   1241 
   1242 void ObfuscatedFileUtil::MarkUsed() {
   1243   if (!timer_)
   1244     timer_.reset(new TimedTaskHelper(file_task_runner_.get()));
   1245 
   1246   if (timer_->IsRunning()) {
   1247     timer_->Reset();
   1248   } else {
   1249     timer_->Start(FROM_HERE,
   1250                   base::TimeDelta::FromSeconds(db_flush_delay_seconds_),
   1251                   base::Bind(&ObfuscatedFileUtil::DropDatabases,
   1252                              base::Unretained(this)));
   1253   }
   1254 }
   1255 
   1256 void ObfuscatedFileUtil::DropDatabases() {
   1257   origin_database_.reset();
   1258   STLDeleteContainerPairSecondPointers(
   1259       directories_.begin(), directories_.end());
   1260   directories_.clear();
   1261   timer_.reset();
   1262 }
   1263 
   1264 bool ObfuscatedFileUtil::InitOriginDatabase(const GURL& origin_hint,
   1265                                             bool create) {
   1266   if (origin_database_)
   1267     return true;
   1268 
   1269   if (!create && !base::DirectoryExists(file_system_directory_))
   1270     return false;
   1271   if (!base::CreateDirectory(file_system_directory_)) {
   1272     LOG(WARNING) << "Failed to create FileSystem directory: " <<
   1273         file_system_directory_.value();
   1274     return false;
   1275   }
   1276 
   1277   SandboxPrioritizedOriginDatabase* prioritized_origin_database =
   1278       new SandboxPrioritizedOriginDatabase(file_system_directory_);
   1279   origin_database_.reset(prioritized_origin_database);
   1280 
   1281   if (origin_hint.is_empty() || !HasIsolatedStorage(origin_hint))
   1282     return true;
   1283 
   1284   const std::string isolated_origin_string =
   1285       webkit_database::GetIdentifierFromOrigin(origin_hint);
   1286 
   1287   // TODO(kinuko): Deprecate this after a few release cycles, e.g. around M33.
   1288   base::FilePath isolated_origin_dir = file_system_directory_.Append(
   1289       SandboxIsolatedOriginDatabase::kObsoleteOriginDirectory);
   1290   if (base::DirectoryExists(isolated_origin_dir) &&
   1291       prioritized_origin_database->GetSandboxOriginDatabase()) {
   1292     SandboxIsolatedOriginDatabase::MigrateBackFromObsoleteOriginDatabase(
   1293         isolated_origin_string,
   1294         file_system_directory_,
   1295         prioritized_origin_database->GetSandboxOriginDatabase());
   1296   }
   1297 
   1298   prioritized_origin_database->InitializePrimaryOrigin(
   1299       isolated_origin_string);
   1300 
   1301   return true;
   1302 }
   1303 
   1304 PlatformFileError ObfuscatedFileUtil::GenerateNewLocalPath(
   1305     SandboxDirectoryDatabase* db,
   1306     FileSystemOperationContext* context,
   1307     const FileSystemURL& url,
   1308     base::FilePath* local_path) {
   1309   DCHECK(local_path);
   1310   int64 number;
   1311   if (!db || !db->GetNextInteger(&number))
   1312     return base::PLATFORM_FILE_ERROR_FAILED;
   1313 
   1314   PlatformFileError error = base::PLATFORM_FILE_OK;
   1315   base::FilePath new_local_path = GetDirectoryForURL(url, false, &error);
   1316   if (error != base::PLATFORM_FILE_OK)
   1317     return base::PLATFORM_FILE_ERROR_FAILED;
   1318 
   1319   // We use the third- and fourth-to-last digits as the directory.
   1320   int64 directory_number = number % 10000 / 100;
   1321   new_local_path = new_local_path.AppendASCII(
   1322       base::StringPrintf("%02" PRId64, directory_number));
   1323 
   1324   error = NativeFileUtil::CreateDirectory(
   1325       new_local_path, false /* exclusive */, false /* recursive */);
   1326   if (error != base::PLATFORM_FILE_OK)
   1327     return error;
   1328 
   1329   *local_path =
   1330       new_local_path.AppendASCII(base::StringPrintf("%08" PRId64, number));
   1331   return base::PLATFORM_FILE_OK;
   1332 }
   1333 
   1334 PlatformFileError ObfuscatedFileUtil::CreateOrOpenInternal(
   1335     FileSystemOperationContext* context,
   1336     const FileSystemURL& url, int file_flags,
   1337     PlatformFile* file_handle, bool* created) {
   1338   DCHECK(!(file_flags & (base::PLATFORM_FILE_DELETE_ON_CLOSE |
   1339         base::PLATFORM_FILE_HIDDEN | base::PLATFORM_FILE_EXCLUSIVE_READ |
   1340         base::PLATFORM_FILE_EXCLUSIVE_WRITE)));
   1341   SandboxDirectoryDatabase* db = GetDirectoryDatabase(url, true);
   1342   if (!db)
   1343     return base::PLATFORM_FILE_ERROR_FAILED;
   1344   FileId file_id;
   1345   if (!db->GetFileWithPath(url.path(), &file_id)) {
   1346     // The file doesn't exist.
   1347     if (!(file_flags & (base::PLATFORM_FILE_CREATE |
   1348         base::PLATFORM_FILE_CREATE_ALWAYS | base::PLATFORM_FILE_OPEN_ALWAYS)))
   1349       return base::PLATFORM_FILE_ERROR_NOT_FOUND;
   1350     FileId parent_id;
   1351     if (!db->GetFileWithPath(VirtualPath::DirName(url.path()),
   1352                              &parent_id))
   1353       return base::PLATFORM_FILE_ERROR_NOT_FOUND;
   1354     FileInfo file_info;
   1355     InitFileInfo(&file_info, parent_id,
   1356                  VirtualPath::BaseName(url.path()).value());
   1357 
   1358     int64 growth = UsageForPath(file_info.name.size());
   1359     if (!AllocateQuota(context, growth))
   1360       return base::PLATFORM_FILE_ERROR_NO_SPACE;
   1361     PlatformFileError error = CreateFile(
   1362         context, base::FilePath(),
   1363         url, &file_info, file_flags, file_handle);
   1364     if (created && base::PLATFORM_FILE_OK == error) {
   1365       *created = true;
   1366       UpdateUsage(context, url, growth);
   1367       context->change_observers()->Notify(
   1368           &FileChangeObserver::OnCreateFile, MakeTuple(url));
   1369     }
   1370     return error;
   1371   }
   1372 
   1373   if (file_flags & base::PLATFORM_FILE_CREATE)
   1374     return base::PLATFORM_FILE_ERROR_EXISTS;
   1375 
   1376   base::PlatformFileInfo platform_file_info;
   1377   base::FilePath local_path;
   1378   FileInfo file_info;
   1379   base::PlatformFileError error = GetFileInfoInternal(
   1380       db, context, url, file_id, &file_info, &platform_file_info, &local_path);
   1381   if (error != base::PLATFORM_FILE_OK)
   1382     return error;
   1383   if (file_info.is_directory())
   1384     return base::PLATFORM_FILE_ERROR_NOT_A_FILE;
   1385 
   1386   int64 delta = 0;
   1387   if (file_flags & (base::PLATFORM_FILE_CREATE_ALWAYS |
   1388                     base::PLATFORM_FILE_OPEN_TRUNCATED)) {
   1389     // The file exists and we're truncating.
   1390     delta = -platform_file_info.size;
   1391     AllocateQuota(context, delta);
   1392   }
   1393 
   1394   error = NativeFileUtil::CreateOrOpen(
   1395       local_path, file_flags, file_handle, created);
   1396   if (error == base::PLATFORM_FILE_ERROR_NOT_FOUND) {
   1397     // TODO(tzik): Also invalidate on-memory usage cache in UsageTracker.
   1398     // TODO(tzik): Delete database entry after ensuring the file lost.
   1399     InvalidateUsageCache(context, url.origin(), url.type());
   1400     LOG(WARNING) << "Lost a backing file.";
   1401     error = base::PLATFORM_FILE_ERROR_FAILED;
   1402   }
   1403 
   1404   // If truncating we need to update the usage.
   1405   if (error == base::PLATFORM_FILE_OK && delta) {
   1406     UpdateUsage(context, url, delta);
   1407     context->change_observers()->Notify(
   1408         &FileChangeObserver::OnModifyFile, MakeTuple(url));
   1409   }
   1410   return error;
   1411 }
   1412 
   1413 bool ObfuscatedFileUtil::HasIsolatedStorage(const GURL& origin) {
   1414   return special_storage_policy_.get() &&
   1415       special_storage_policy_->HasIsolatedStorage(origin);
   1416 }
   1417 
   1418 }  // namespace fileapi
   1419