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