Home | History | Annotate | Download | only in fileapi
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "webkit/browser/fileapi/file_system_usage_cache.h"
      6 
      7 #include <utility>
      8 
      9 #include "base/bind.h"
     10 #include "base/debug/trace_event.h"
     11 #include "base/file_util.h"
     12 #include "base/files/file_path.h"
     13 #include "base/pickle.h"
     14 #include "base/stl_util.h"
     15 #include "webkit/browser/fileapi/timed_task_helper.h"
     16 
     17 namespace fileapi {
     18 
     19 namespace {
     20 const int64 kCloseDelaySeconds = 5;
     21 const size_t kMaxHandleCacheSize = 2;
     22 }  // namespace
     23 
     24 FileSystemUsageCache::FileSystemUsageCache(
     25     base::SequencedTaskRunner* task_runner)
     26     : task_runner_(task_runner),
     27       weak_factory_(this) {
     28 }
     29 
     30 FileSystemUsageCache::~FileSystemUsageCache() {
     31   task_runner_ = NULL;
     32   CloseCacheFiles();
     33 }
     34 
     35 const base::FilePath::CharType FileSystemUsageCache::kUsageFileName[] =
     36     FILE_PATH_LITERAL(".usage");
     37 const char FileSystemUsageCache::kUsageFileHeader[] = "FSU5";
     38 const int FileSystemUsageCache::kUsageFileHeaderSize = 4;
     39 
     40 // Pickle::{Read,Write}Bool treat bool as int
     41 const int FileSystemUsageCache::kUsageFileSize =
     42     sizeof(Pickle::Header) +
     43     FileSystemUsageCache::kUsageFileHeaderSize +
     44     sizeof(int) + sizeof(int32) + sizeof(int64);  // NOLINT
     45 
     46 bool FileSystemUsageCache::GetUsage(const base::FilePath& usage_file_path,
     47                                     int64* usage_out) {
     48   TRACE_EVENT0("FileSystem", "UsageCache::GetUsage");
     49   DCHECK(CalledOnValidThread());
     50   DCHECK(usage_out);
     51   bool is_valid = true;
     52   uint32 dirty = 0;
     53   int64 usage = 0;
     54   if (!Read(usage_file_path, &is_valid, &dirty, &usage))
     55     return false;
     56   *usage_out = usage;
     57   return true;
     58 }
     59 
     60 bool FileSystemUsageCache::GetDirty(const base::FilePath& usage_file_path,
     61                                     uint32* dirty_out) {
     62   TRACE_EVENT0("FileSystem", "UsageCache::GetDirty");
     63   DCHECK(CalledOnValidThread());
     64   DCHECK(dirty_out);
     65   bool is_valid = true;
     66   uint32 dirty = 0;
     67   int64 usage = 0;
     68   if (!Read(usage_file_path, &is_valid, &dirty, &usage))
     69     return false;
     70   *dirty_out = dirty;
     71   return true;
     72 }
     73 
     74 bool FileSystemUsageCache::IncrementDirty(
     75     const base::FilePath& usage_file_path) {
     76   TRACE_EVENT0("FileSystem", "UsageCache::IncrementDirty");
     77   DCHECK(CalledOnValidThread());
     78   bool is_valid = true;
     79   uint32 dirty = 0;
     80   int64 usage = 0;
     81   bool new_handle = !HasCacheFileHandle(usage_file_path);
     82   if (!Read(usage_file_path, &is_valid, &dirty, &usage))
     83     return false;
     84 
     85   bool success = Write(usage_file_path, is_valid, dirty + 1, usage);
     86   if (success && dirty == 0 && new_handle)
     87     FlushFile(usage_file_path);
     88   return success;
     89 }
     90 
     91 bool FileSystemUsageCache::DecrementDirty(
     92     const base::FilePath& usage_file_path) {
     93   TRACE_EVENT0("FileSystem", "UsageCache::DecrementDirty");
     94   DCHECK(CalledOnValidThread());
     95   bool is_valid = true;
     96   uint32 dirty = 0;
     97   int64 usage = 0;;
     98   if (!Read(usage_file_path, &is_valid, &dirty, &usage) || dirty <= 0)
     99     return false;
    100 
    101   if (dirty <= 0)
    102     return false;
    103 
    104   return Write(usage_file_path, is_valid, dirty - 1, usage);
    105 }
    106 
    107 bool FileSystemUsageCache::Invalidate(const base::FilePath& usage_file_path) {
    108   TRACE_EVENT0("FileSystem", "UsageCache::Invalidate");
    109   DCHECK(CalledOnValidThread());
    110   bool is_valid = true;
    111   uint32 dirty = 0;
    112   int64 usage = 0;
    113   if (!Read(usage_file_path, &is_valid, &dirty, &usage))
    114     return false;
    115 
    116   return Write(usage_file_path, false, dirty, usage);
    117 }
    118 
    119 bool FileSystemUsageCache::IsValid(const base::FilePath& usage_file_path) {
    120   TRACE_EVENT0("FileSystem", "UsageCache::IsValid");
    121   DCHECK(CalledOnValidThread());
    122   bool is_valid = true;
    123   uint32 dirty = 0;
    124   int64 usage = 0;
    125   if (!Read(usage_file_path, &is_valid, &dirty, &usage))
    126     return false;
    127   return is_valid;
    128 }
    129 
    130 bool FileSystemUsageCache::AtomicUpdateUsageByDelta(
    131     const base::FilePath& usage_file_path, int64 delta) {
    132   TRACE_EVENT0("FileSystem", "UsageCache::AtomicUpdateUsageByDelta");
    133   DCHECK(CalledOnValidThread());
    134   bool is_valid = true;
    135   uint32 dirty = 0;
    136   int64 usage = 0;;
    137   if (!Read(usage_file_path, &is_valid, &dirty, &usage))
    138     return false;
    139   return Write(usage_file_path, is_valid, dirty, usage + delta);
    140 }
    141 
    142 bool FileSystemUsageCache::UpdateUsage(const base::FilePath& usage_file_path,
    143                                        int64 fs_usage) {
    144   TRACE_EVENT0("FileSystem", "UsageCache::UpdateUsage");
    145   DCHECK(CalledOnValidThread());
    146   return Write(usage_file_path, true, 0, fs_usage);
    147 }
    148 
    149 bool FileSystemUsageCache::Exists(const base::FilePath& usage_file_path) {
    150   TRACE_EVENT0("FileSystem", "UsageCache::Exists");
    151   DCHECK(CalledOnValidThread());
    152   return base::PathExists(usage_file_path);
    153 }
    154 
    155 bool FileSystemUsageCache::Delete(const base::FilePath& usage_file_path) {
    156   TRACE_EVENT0("FileSystem", "UsageCache::Delete");
    157   DCHECK(CalledOnValidThread());
    158   CloseCacheFiles();
    159   return base::DeleteFile(usage_file_path, true);
    160 }
    161 
    162 void FileSystemUsageCache::CloseCacheFiles() {
    163   TRACE_EVENT0("FileSystem", "UsageCache::CloseCacheFiles");
    164   DCHECK(CalledOnValidThread());
    165   for (CacheFiles::iterator itr = cache_files_.begin();
    166        itr != cache_files_.end(); ++itr) {
    167     if (itr->second != base::kInvalidPlatformFileValue)
    168       base::ClosePlatformFile(itr->second);
    169   }
    170   cache_files_.clear();
    171   timer_.reset();
    172 }
    173 
    174 bool FileSystemUsageCache::Read(const base::FilePath& usage_file_path,
    175                                  bool* is_valid,
    176                                  uint32* dirty_out,
    177                                  int64* usage_out) {
    178   TRACE_EVENT0("FileSystem", "UsageCache::Read");
    179   DCHECK(CalledOnValidThread());
    180   DCHECK(is_valid);
    181   DCHECK(dirty_out);
    182   DCHECK(usage_out);
    183   char buffer[kUsageFileSize];
    184   const char *header;
    185   if (usage_file_path.empty() ||
    186       !ReadBytes(usage_file_path, buffer, kUsageFileSize))
    187     return false;
    188   Pickle read_pickle(buffer, kUsageFileSize);
    189   PickleIterator iter(read_pickle);
    190   uint32 dirty = 0;
    191   int64 usage = 0;
    192 
    193   if (!read_pickle.ReadBytes(&iter, &header, kUsageFileHeaderSize) ||
    194       !read_pickle.ReadBool(&iter, is_valid) ||
    195       !read_pickle.ReadUInt32(&iter, &dirty) ||
    196       !read_pickle.ReadInt64(&iter, &usage))
    197     return false;
    198 
    199   if (header[0] != kUsageFileHeader[0] ||
    200       header[1] != kUsageFileHeader[1] ||
    201       header[2] != kUsageFileHeader[2] ||
    202       header[3] != kUsageFileHeader[3])
    203     return false;
    204 
    205   *dirty_out = dirty;
    206   *usage_out = usage;
    207   return true;
    208 }
    209 
    210 bool FileSystemUsageCache::Write(const base::FilePath& usage_file_path,
    211                                  bool is_valid,
    212                                  int32 dirty,
    213                                  int64 usage) {
    214   TRACE_EVENT0("FileSystem", "UsageCache::Write");
    215   DCHECK(CalledOnValidThread());
    216   Pickle write_pickle;
    217   write_pickle.WriteBytes(kUsageFileHeader, kUsageFileHeaderSize);
    218   write_pickle.WriteBool(is_valid);
    219   write_pickle.WriteUInt32(dirty);
    220   write_pickle.WriteInt64(usage);
    221 
    222   if (!WriteBytes(usage_file_path,
    223                   static_cast<const char*>(write_pickle.data()),
    224                   write_pickle.size())) {
    225     Delete(usage_file_path);
    226     return false;
    227   }
    228   return true;
    229 }
    230 
    231 bool FileSystemUsageCache::GetPlatformFile(const base::FilePath& file_path,
    232                                            base::PlatformFile* file) {
    233   DCHECK(CalledOnValidThread());
    234   if (cache_files_.size() >= kMaxHandleCacheSize)
    235     CloseCacheFiles();
    236   ScheduleCloseTimer();
    237 
    238   std::pair<CacheFiles::iterator, bool> inserted =
    239       cache_files_.insert(
    240           std::make_pair(file_path, base::kInvalidPlatformFileValue));
    241   if (!inserted.second) {
    242     *file = inserted.first->second;
    243     return true;
    244   }
    245 
    246   base::PlatformFileError error = base::PLATFORM_FILE_ERROR_FAILED;
    247   base::PlatformFile platform_file =
    248       base::CreatePlatformFile(file_path,
    249                                base::PLATFORM_FILE_OPEN_ALWAYS |
    250                                base::PLATFORM_FILE_READ |
    251                                base::PLATFORM_FILE_WRITE,
    252                                NULL, &error);
    253   if (error != base::PLATFORM_FILE_OK) {
    254     cache_files_.erase(inserted.first);
    255     return false;
    256   }
    257 
    258   inserted.first->second = platform_file;
    259   *file = platform_file;
    260   return true;
    261 }
    262 
    263 bool FileSystemUsageCache::ReadBytes(const base::FilePath& file_path,
    264                                      char* buffer,
    265                                      int64 buffer_size) {
    266   DCHECK(CalledOnValidThread());
    267   base::PlatformFile file;
    268   if (!GetPlatformFile(file_path, &file))
    269     return false;
    270   return base::ReadPlatformFile(file, 0, buffer, buffer_size) == buffer_size;
    271 }
    272 
    273 bool FileSystemUsageCache::WriteBytes(const base::FilePath& file_path,
    274                                       const char* buffer,
    275                                       int64 buffer_size) {
    276   DCHECK(CalledOnValidThread());
    277   base::PlatformFile file;
    278   if (!GetPlatformFile(file_path, &file))
    279     return false;
    280   return base::WritePlatformFile(file, 0, buffer, buffer_size) == buffer_size;
    281 }
    282 
    283 bool FileSystemUsageCache::FlushFile(const base::FilePath& file_path) {
    284   TRACE_EVENT0("FileSystem", "UsageCache::FlushFile");
    285   DCHECK(CalledOnValidThread());
    286   base::PlatformFile file = base::kInvalidPlatformFileValue;
    287   return GetPlatformFile(file_path, &file) && base::FlushPlatformFile(file);
    288 }
    289 
    290 void FileSystemUsageCache::ScheduleCloseTimer() {
    291   DCHECK(CalledOnValidThread());
    292   if (!timer_)
    293     timer_.reset(new TimedTaskHelper(task_runner_.get()));
    294 
    295   if (timer_->IsRunning()) {
    296     timer_->Reset();
    297     return;
    298   }
    299 
    300   timer_->Start(FROM_HERE,
    301                 base::TimeDelta::FromSeconds(kCloseDelaySeconds),
    302                 base::Bind(&FileSystemUsageCache::CloseCacheFiles,
    303                            weak_factory_.GetWeakPtr()));
    304 }
    305 
    306 bool FileSystemUsageCache::CalledOnValidThread() {
    307   return !task_runner_.get() || task_runner_->RunsTasksOnCurrentThread();
    308 }
    309 
    310 bool FileSystemUsageCache::HasCacheFileHandle(const base::FilePath& file_path) {
    311   DCHECK(CalledOnValidThread());
    312   DCHECK_LE(cache_files_.size(), kMaxHandleCacheSize);
    313   return ContainsKey(cache_files_, file_path);
    314 }
    315 
    316 }  // namespace fileapi
    317