• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
FileSystemUsageCache(base::SequencedTaskRunner * task_runner)24 FileSystemUsageCache::FileSystemUsageCache(
25     base::SequencedTaskRunner* task_runner)
26     : task_runner_(task_runner),
27       weak_factory_(this) {
28 }
29 
~FileSystemUsageCache()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 
GetUsage(const base::FilePath & usage_file_path,int64 * usage_out)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 
GetDirty(const base::FilePath & usage_file_path,uint32 * dirty_out)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 
IncrementDirty(const base::FilePath & usage_file_path)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 
DecrementDirty(const base::FilePath & usage_file_path)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 
Invalidate(const base::FilePath & usage_file_path)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 
IsValid(const base::FilePath & usage_file_path)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 
AtomicUpdateUsageByDelta(const base::FilePath & usage_file_path,int64 delta)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 
UpdateUsage(const base::FilePath & usage_file_path,int64 fs_usage)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 
Exists(const base::FilePath & usage_file_path)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 
Delete(const base::FilePath & usage_file_path)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 
CloseCacheFiles()162 void FileSystemUsageCache::CloseCacheFiles() {
163   TRACE_EVENT0("FileSystem", "UsageCache::CloseCacheFiles");
164   DCHECK(CalledOnValidThread());
165   STLDeleteValues(&cache_files_);
166   timer_.reset();
167 }
168 
Read(const base::FilePath & usage_file_path,bool * is_valid,uint32 * dirty_out,int64 * usage_out)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 
Write(const base::FilePath & usage_file_path,bool is_valid,int32 dirty,int64 usage)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 
GetFile(const base::FilePath & file_path)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 
ReadBytes(const base::FilePath & file_path,char * buffer,int64 buffer_size)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 
WriteBytes(const base::FilePath & file_path,const char * buffer,int64 buffer_size)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 
FlushFile(const base::FilePath & file_path)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 
ScheduleCloseTimer()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 
CalledOnValidThread()297 bool FileSystemUsageCache::CalledOnValidThread() {
298   return !task_runner_.get() || task_runner_->RunsTasksOnCurrentThread();
299 }
300 
HasCacheFileHandle(const base::FilePath & file_path)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 fileapi
308