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/files/file_path.h"
11 #include "base/files/file_util.h"
12 #include "base/pickle.h"
13 #include "base/stl_util.h"
14 #include "base/trace_event/trace_event.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)
101 return Write(usage_file_path
, is_valid
, dirty
- 1, usage
);
104 bool FileSystemUsageCache::Invalidate(const base::FilePath
& usage_file_path
) {
105 TRACE_EVENT0("FileSystem", "UsageCache::Invalidate");
106 DCHECK(CalledOnValidThread());
107 bool is_valid
= true;
110 if (!Read(usage_file_path
, &is_valid
, &dirty
, &usage
))
113 return Write(usage_file_path
, false, dirty
, usage
);
116 bool FileSystemUsageCache::IsValid(const base::FilePath
& usage_file_path
) {
117 TRACE_EVENT0("FileSystem", "UsageCache::IsValid");
118 DCHECK(CalledOnValidThread());
119 bool is_valid
= true;
122 if (!Read(usage_file_path
, &is_valid
, &dirty
, &usage
))
127 bool FileSystemUsageCache::AtomicUpdateUsageByDelta(
128 const base::FilePath
& usage_file_path
, int64 delta
) {
129 TRACE_EVENT0("FileSystem", "UsageCache::AtomicUpdateUsageByDelta");
130 DCHECK(CalledOnValidThread());
131 bool is_valid
= true;
134 if (!Read(usage_file_path
, &is_valid
, &dirty
, &usage
))
136 return Write(usage_file_path
, is_valid
, dirty
, usage
+ delta
);
139 bool FileSystemUsageCache::UpdateUsage(const base::FilePath
& usage_file_path
,
141 TRACE_EVENT0("FileSystem", "UsageCache::UpdateUsage");
142 DCHECK(CalledOnValidThread());
143 return Write(usage_file_path
, true, 0, fs_usage
);
146 bool FileSystemUsageCache::Exists(const base::FilePath
& usage_file_path
) {
147 TRACE_EVENT0("FileSystem", "UsageCache::Exists");
148 DCHECK(CalledOnValidThread());
149 return base::PathExists(usage_file_path
);
152 bool FileSystemUsageCache::Delete(const base::FilePath
& usage_file_path
) {
153 TRACE_EVENT0("FileSystem", "UsageCache::Delete");
154 DCHECK(CalledOnValidThread());
156 return base::DeleteFile(usage_file_path
, true);
159 void FileSystemUsageCache::CloseCacheFiles() {
160 TRACE_EVENT0("FileSystem", "UsageCache::CloseCacheFiles");
161 DCHECK(CalledOnValidThread());
162 STLDeleteValues(&cache_files_
);
166 bool FileSystemUsageCache::Read(const base::FilePath
& usage_file_path
,
170 TRACE_EVENT0("FileSystem", "UsageCache::Read");
171 DCHECK(CalledOnValidThread());
175 char buffer
[kUsageFileSize
];
177 if (usage_file_path
.empty() ||
178 !ReadBytes(usage_file_path
, buffer
, kUsageFileSize
))
180 Pickle
read_pickle(buffer
, kUsageFileSize
);
181 PickleIterator
iter(read_pickle
);
185 if (!iter
.ReadBytes(&header
, kUsageFileHeaderSize
) ||
186 !iter
.ReadBool(is_valid
) ||
187 !iter
.ReadUInt32(&dirty
) ||
188 !iter
.ReadInt64(&usage
))
191 if (header
[0] != kUsageFileHeader
[0] ||
192 header
[1] != kUsageFileHeader
[1] ||
193 header
[2] != kUsageFileHeader
[2] ||
194 header
[3] != kUsageFileHeader
[3])
202 bool FileSystemUsageCache::Write(const base::FilePath
& usage_file_path
,
206 TRACE_EVENT0("FileSystem", "UsageCache::Write");
207 DCHECK(CalledOnValidThread());
209 write_pickle
.WriteBytes(kUsageFileHeader
, kUsageFileHeaderSize
);
210 write_pickle
.WriteBool(is_valid
);
211 write_pickle
.WriteUInt32(dirty
);
212 write_pickle
.WriteInt64(usage
);
214 if (!WriteBytes(usage_file_path
,
215 static_cast<const char*>(write_pickle
.data()),
216 write_pickle
.size())) {
217 Delete(usage_file_path
);
223 base::File
* FileSystemUsageCache::GetFile(const base::FilePath
& file_path
) {
224 DCHECK(CalledOnValidThread());
225 if (cache_files_
.size() >= kMaxHandleCacheSize
)
227 ScheduleCloseTimer();
229 base::File
* new_file
= NULL
;
230 std::pair
<CacheFiles::iterator
, bool> inserted
=
231 cache_files_
.insert(std::make_pair(file_path
, new_file
));
232 if (!inserted
.second
)
233 return inserted
.first
->second
;
235 new_file
= new base::File(file_path
,
236 base::File::FLAG_OPEN_ALWAYS
|
237 base::File::FLAG_READ
|
238 base::File::FLAG_WRITE
);
239 if (!new_file
->IsValid()) {
240 cache_files_
.erase(inserted
.first
);
245 inserted
.first
->second
= new_file
;
249 bool FileSystemUsageCache::ReadBytes(const base::FilePath
& file_path
,
252 DCHECK(CalledOnValidThread());
253 base::File
* file
= GetFile(file_path
);
256 return file
->Read(0, buffer
, buffer_size
) == buffer_size
;
259 bool FileSystemUsageCache::WriteBytes(const base::FilePath
& file_path
,
262 DCHECK(CalledOnValidThread());
263 base::File
* file
= GetFile(file_path
);
266 return file
->Write(0, buffer
, buffer_size
) == buffer_size
;
269 bool FileSystemUsageCache::FlushFile(const base::FilePath
& file_path
) {
270 TRACE_EVENT0("FileSystem", "UsageCache::FlushFile");
271 DCHECK(CalledOnValidThread());
272 base::File
* file
= GetFile(file_path
);
275 return file
->Flush();
278 void FileSystemUsageCache::ScheduleCloseTimer() {
279 DCHECK(CalledOnValidThread());
281 timer_
.reset(new TimedTaskHelper(task_runner_
.get()));
283 if (timer_
->IsRunning()) {
288 timer_
->Start(FROM_HERE
,
289 base::TimeDelta::FromSeconds(kCloseDelaySeconds
),
290 base::Bind(&FileSystemUsageCache::CloseCacheFiles
,
291 weak_factory_
.GetWeakPtr()));
294 bool FileSystemUsageCache::CalledOnValidThread() {
295 return !task_runner_
.get() || task_runner_
->RunsTasksOnCurrentThread();
298 bool FileSystemUsageCache::HasCacheFileHandle(const base::FilePath
& file_path
) {
299 DCHECK(CalledOnValidThread());
300 DCHECK_LE(cache_files_
.size(), kMaxHandleCacheSize
);
301 return ContainsKey(cache_files_
, file_path
);
304 } // namespace storage