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