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 "storage/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/files/file_path.h"
     12 #include "base/files/file_util.h"
     13 #include "base/pickle.h"
     14 #include "base/stl_util.h"
     15 #include "storage/browser/fileapi/timed_task_helper.h"
     16 
     17 namespace storage {
     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   STLDeleteValues(&cache_files_);
    166   timer_.reset();
    167 }
    168 
    169 bool FileSystemUsageCache::Read(const base::FilePath& usage_file_path,
    170                                  bool* is_valid,
    171                                  uint32* dirty_out,
    172                                  int64* usage_out) {
    173   TRACE_EVENT0("FileSystem", "UsageCache::Read");
    174   DCHECK(CalledOnValidThread());
    175   DCHECK(is_valid);
    176   DCHECK(dirty_out);
    177   DCHECK(usage_out);
    178   char buffer[kUsageFileSize];
    179   const char *header;
    180   if (usage_file_path.empty() ||
    181       !ReadBytes(usage_file_path, buffer, kUsageFileSize))
    182     return false;
    183   Pickle read_pickle(buffer, kUsageFileSize);
    184   PickleIterator iter(read_pickle);
    185   uint32 dirty = 0;
    186   int64 usage = 0;
    187 
    188   if (!read_pickle.ReadBytes(&iter, &header, kUsageFileHeaderSize) ||
    189       !read_pickle.ReadBool(&iter, is_valid) ||
    190       !read_pickle.ReadUInt32(&iter, &dirty) ||
    191       !read_pickle.ReadInt64(&iter, &usage))
    192     return false;
    193 
    194   if (header[0] != kUsageFileHeader[0] ||
    195       header[1] != kUsageFileHeader[1] ||
    196       header[2] != kUsageFileHeader[2] ||
    197       header[3] != kUsageFileHeader[3])
    198     return false;
    199 
    200   *dirty_out = dirty;
    201   *usage_out = usage;
    202   return true;
    203 }
    204 
    205 bool FileSystemUsageCache::Write(const base::FilePath& usage_file_path,
    206                                  bool is_valid,
    207                                  int32 dirty,
    208                                  int64 usage) {
    209   TRACE_EVENT0("FileSystem", "UsageCache::Write");
    210   DCHECK(CalledOnValidThread());
    211   Pickle write_pickle;
    212   write_pickle.WriteBytes(kUsageFileHeader, kUsageFileHeaderSize);
    213   write_pickle.WriteBool(is_valid);
    214   write_pickle.WriteUInt32(dirty);
    215   write_pickle.WriteInt64(usage);
    216 
    217   if (!WriteBytes(usage_file_path,
    218                   static_cast<const char*>(write_pickle.data()),
    219                   write_pickle.size())) {
    220     Delete(usage_file_path);
    221     return false;
    222   }
    223   return true;
    224 }
    225 
    226 base::File* FileSystemUsageCache::GetFile(const base::FilePath& file_path) {
    227   DCHECK(CalledOnValidThread());
    228   if (cache_files_.size() >= kMaxHandleCacheSize)
    229     CloseCacheFiles();
    230   ScheduleCloseTimer();
    231 
    232   base::File* new_file = NULL;
    233   std::pair<CacheFiles::iterator, bool> inserted =
    234       cache_files_.insert(std::make_pair(file_path, new_file));
    235   if (!inserted.second)
    236     return inserted.first->second;
    237 
    238   new_file = new base::File(file_path,
    239                             base::File::FLAG_OPEN_ALWAYS |
    240                             base::File::FLAG_READ |
    241                             base::File::FLAG_WRITE);
    242   if (!new_file->IsValid()) {
    243     cache_files_.erase(inserted.first);
    244     delete new_file;
    245     return NULL;
    246   }
    247 
    248   inserted.first->second = new_file;
    249   return new_file;
    250 }
    251 
    252 bool FileSystemUsageCache::ReadBytes(const base::FilePath& file_path,
    253                                      char* buffer,
    254                                      int64 buffer_size) {
    255   DCHECK(CalledOnValidThread());
    256   base::File* file = GetFile(file_path);
    257   if (!file)
    258     return false;
    259   return file->Read(0, buffer, buffer_size) == buffer_size;
    260 }
    261 
    262 bool FileSystemUsageCache::WriteBytes(const base::FilePath& file_path,
    263                                       const char* buffer,
    264                                       int64 buffer_size) {
    265   DCHECK(CalledOnValidThread());
    266   base::File* file = GetFile(file_path);
    267   if (!file)
    268     return false;
    269   return file->Write(0, buffer, buffer_size) == buffer_size;
    270 }
    271 
    272 bool FileSystemUsageCache::FlushFile(const base::FilePath& file_path) {
    273   TRACE_EVENT0("FileSystem", "UsageCache::FlushFile");
    274   DCHECK(CalledOnValidThread());
    275   base::File* file = GetFile(file_path);
    276   if (!file)
    277     return false;
    278   return file->Flush();
    279 }
    280 
    281 void FileSystemUsageCache::ScheduleCloseTimer() {
    282   DCHECK(CalledOnValidThread());
    283   if (!timer_)
    284     timer_.reset(new TimedTaskHelper(task_runner_.get()));
    285 
    286   if (timer_->IsRunning()) {
    287     timer_->Reset();
    288     return;
    289   }
    290 
    291   timer_->Start(FROM_HERE,
    292                 base::TimeDelta::FromSeconds(kCloseDelaySeconds),
    293                 base::Bind(&FileSystemUsageCache::CloseCacheFiles,
    294                            weak_factory_.GetWeakPtr()));
    295 }
    296 
    297 bool FileSystemUsageCache::CalledOnValidThread() {
    298   return !task_runner_.get() || task_runner_->RunsTasksOnCurrentThread();
    299 }
    300 
    301 bool FileSystemUsageCache::HasCacheFileHandle(const base::FilePath& file_path) {
    302   DCHECK(CalledOnValidThread());
    303   DCHECK_LE(cache_files_.size(), kMaxHandleCacheSize);
    304   return ContainsKey(cache_files_, file_path);
    305 }
    306 
    307 }  // namespace storage
    308