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.
5 #include "storage/browser/fileapi/file_system_usage_cache.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"
20 const int64 kCloseDelaySeconds = 5;
21 const size_t kMaxHandleCacheSize = 2;
24 FileSystemUsageCache::FileSystemUsageCache(
25 base::SequencedTaskRunner* task_runner)
26 : task_runner_(task_runner),
30 FileSystemUsageCache::~FileSystemUsageCache() {
35 const base::FilePath::CharType FileSystemUsageCache::kUsageFileName[] =
36 FILE_PATH_LITERAL(".usage");
37 const char FileSystemUsageCache::kUsageFileHeader[] = "FSU5";
38 const int FileSystemUsageCache::kUsageFileHeaderSize = 4;
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
46 bool FileSystemUsageCache::GetUsage(const base::FilePath& usage_file_path,
48 TRACE_EVENT0("FileSystem", "UsageCache::GetUsage");
49 DCHECK(CalledOnValidThread());
54 if (!Read(usage_file_path, &is_valid, &dirty, &usage))
60 bool FileSystemUsageCache::GetDirty(const base::FilePath& usage_file_path,
62 TRACE_EVENT0("FileSystem", "UsageCache::GetDirty");
63 DCHECK(CalledOnValidThread());
68 if (!Read(usage_file_path, &is_valid, &dirty, &usage))
74 bool FileSystemUsageCache::IncrementDirty(
75 const base::FilePath& usage_file_path) {
76 TRACE_EVENT0("FileSystem", "UsageCache::IncrementDirty");
77 DCHECK(CalledOnValidThread());
81 bool new_handle = !HasCacheFileHandle(usage_file_path);
82 if (!Read(usage_file_path, &is_valid, &dirty, &usage))
85 bool success = Write(usage_file_path, is_valid, dirty + 1, usage);
86 if (success && dirty == 0 && new_handle)
87 FlushFile(usage_file_path);
91 bool FileSystemUsageCache::DecrementDirty(
92 const base::FilePath& usage_file_path) {
93 TRACE_EVENT0("FileSystem", "UsageCache::DecrementDirty");
94 DCHECK(CalledOnValidThread());
98 if (!Read(usage_file_path, &is_valid, &dirty, &usage) || dirty <= 0)
104 return Write(usage_file_path, is_valid, dirty - 1, usage);
107 bool FileSystemUsageCache::Invalidate(const base::FilePath& usage_file_path) {
108 TRACE_EVENT0("FileSystem", "UsageCache::Invalidate");
109 DCHECK(CalledOnValidThread());
110 bool is_valid = true;
113 if (!Read(usage_file_path, &is_valid, &dirty, &usage))
116 return Write(usage_file_path, false, dirty, usage);
119 bool FileSystemUsageCache::IsValid(const base::FilePath& usage_file_path) {
120 TRACE_EVENT0("FileSystem", "UsageCache::IsValid");
121 DCHECK(CalledOnValidThread());
122 bool is_valid = true;
125 if (!Read(usage_file_path, &is_valid, &dirty, &usage))
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;
137 if (!Read(usage_file_path, &is_valid, &dirty, &usage))
139 return Write(usage_file_path, is_valid, dirty, usage + delta);
142 bool FileSystemUsageCache::UpdateUsage(const base::FilePath& usage_file_path,
144 TRACE_EVENT0("FileSystem", "UsageCache::UpdateUsage");
145 DCHECK(CalledOnValidThread());
146 return Write(usage_file_path, true, 0, fs_usage);
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);
155 bool FileSystemUsageCache::Delete(const base::FilePath& usage_file_path) {
156 TRACE_EVENT0("FileSystem", "UsageCache::Delete");
157 DCHECK(CalledOnValidThread());
159 return base::DeleteFile(usage_file_path, true);
162 void FileSystemUsageCache::CloseCacheFiles() {
163 TRACE_EVENT0("FileSystem", "UsageCache::CloseCacheFiles");
164 DCHECK(CalledOnValidThread());
165 STLDeleteValues(&cache_files_);
169 bool FileSystemUsageCache::Read(const base::FilePath& usage_file_path,
173 TRACE_EVENT0("FileSystem", "UsageCache::Read");
174 DCHECK(CalledOnValidThread());
178 char buffer[kUsageFileSize];
180 if (usage_file_path.empty() ||
181 !ReadBytes(usage_file_path, buffer, kUsageFileSize))
183 Pickle read_pickle(buffer, kUsageFileSize);
184 PickleIterator iter(read_pickle);
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))
194 if (header[0] != kUsageFileHeader[0] ||
195 header[1] != kUsageFileHeader[1] ||
196 header[2] != kUsageFileHeader[2] ||
197 header[3] != kUsageFileHeader[3])
205 bool FileSystemUsageCache::Write(const base::FilePath& usage_file_path,
209 TRACE_EVENT0("FileSystem", "UsageCache::Write");
210 DCHECK(CalledOnValidThread());
212 write_pickle.WriteBytes(kUsageFileHeader, kUsageFileHeaderSize);
213 write_pickle.WriteBool(is_valid);
214 write_pickle.WriteUInt32(dirty);
215 write_pickle.WriteInt64(usage);
217 if (!WriteBytes(usage_file_path,
218 static_cast<const char*>(write_pickle.data()),
219 write_pickle.size())) {
220 Delete(usage_file_path);
226 base::File* FileSystemUsageCache::GetFile(const base::FilePath& file_path) {
227 DCHECK(CalledOnValidThread());
228 if (cache_files_.size() >= kMaxHandleCacheSize)
230 ScheduleCloseTimer();
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;
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);
248 inserted.first->second = new_file;
252 bool FileSystemUsageCache::ReadBytes(const base::FilePath& file_path,
255 DCHECK(CalledOnValidThread());
256 base::File* file = GetFile(file_path);
259 return file->Read(0, buffer, buffer_size) == buffer_size;
262 bool FileSystemUsageCache::WriteBytes(const base::FilePath& file_path,
265 DCHECK(CalledOnValidThread());
266 base::File* file = GetFile(file_path);
269 return file->Write(0, buffer, buffer_size) == buffer_size;
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);
278 return file->Flush();
281 void FileSystemUsageCache::ScheduleCloseTimer() {
282 DCHECK(CalledOnValidThread());
284 timer_.reset(new TimedTaskHelper(task_runner_.get()));
286 if (timer_->IsRunning()) {
291 timer_->Start(FROM_HERE,
292 base::TimeDelta::FromSeconds(kCloseDelaySeconds),
293 base::Bind(&FileSystemUsageCache::CloseCacheFiles,
294 weak_factory_.GetWeakPtr()));
297 bool FileSystemUsageCache::CalledOnValidThread() {
298 return !task_runner_.get() || task_runner_->RunsTasksOnCurrentThread();
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);
307 } // namespace storage