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_util.h"
11 #include "base/pickle.h"
12 #include "base/stl_util.h"
13 #include "base/trace_event/trace_event.h"
14 #include "storage/browser/fileapi/timed_task_helper.h"
19 const int64 kCloseDelaySeconds
= 5;
20 const size_t kMaxHandleCacheSize
= 2;
23 FileSystemUsageCache::FileSystemUsageCache(
24 base::SequencedTaskRunner
* task_runner
)
25 : task_runner_(task_runner
),
29 FileSystemUsageCache::~FileSystemUsageCache() {
34 const base::FilePath::CharType
FileSystemUsageCache::kUsageFileName
[] =
35 FILE_PATH_LITERAL(".usage");
36 const char FileSystemUsageCache::kUsageFileHeader
[] = "FSU5";
37 const int FileSystemUsageCache::kUsageFileHeaderSize
= 4;
39 // Pickle::{Read,Write}Bool treat bool as int
40 const int FileSystemUsageCache::kUsageFileSize
=
41 sizeof(base::Pickle::Header
) + FileSystemUsageCache::kUsageFileHeaderSize
+
42 sizeof(int) + sizeof(int32
) + sizeof(int64
); // NOLINT
44 bool FileSystemUsageCache::GetUsage(const base::FilePath
& usage_file_path
,
46 TRACE_EVENT0("FileSystem", "UsageCache::GetUsage");
47 DCHECK(CalledOnValidThread());
52 if (!Read(usage_file_path
, &is_valid
, &dirty
, &usage
))
58 bool FileSystemUsageCache::GetDirty(const base::FilePath
& usage_file_path
,
60 TRACE_EVENT0("FileSystem", "UsageCache::GetDirty");
61 DCHECK(CalledOnValidThread());
66 if (!Read(usage_file_path
, &is_valid
, &dirty
, &usage
))
72 bool FileSystemUsageCache::IncrementDirty(
73 const base::FilePath
& usage_file_path
) {
74 TRACE_EVENT0("FileSystem", "UsageCache::IncrementDirty");
75 DCHECK(CalledOnValidThread());
79 bool new_handle
= !HasCacheFileHandle(usage_file_path
);
80 if (!Read(usage_file_path
, &is_valid
, &dirty
, &usage
))
83 bool success
= Write(usage_file_path
, is_valid
, dirty
+ 1, usage
);
84 if (success
&& dirty
== 0 && new_handle
)
85 FlushFile(usage_file_path
);
89 bool FileSystemUsageCache::DecrementDirty(
90 const base::FilePath
& usage_file_path
) {
91 TRACE_EVENT0("FileSystem", "UsageCache::DecrementDirty");
92 DCHECK(CalledOnValidThread());
96 if (!Read(usage_file_path
, &is_valid
, &dirty
, &usage
) || dirty
== 0)
99 return Write(usage_file_path
, is_valid
, dirty
- 1, usage
);
102 bool FileSystemUsageCache::Invalidate(const base::FilePath
& usage_file_path
) {
103 TRACE_EVENT0("FileSystem", "UsageCache::Invalidate");
104 DCHECK(CalledOnValidThread());
105 bool is_valid
= true;
108 if (!Read(usage_file_path
, &is_valid
, &dirty
, &usage
))
111 return Write(usage_file_path
, false, dirty
, usage
);
114 bool FileSystemUsageCache::IsValid(const base::FilePath
& usage_file_path
) {
115 TRACE_EVENT0("FileSystem", "UsageCache::IsValid");
116 DCHECK(CalledOnValidThread());
117 bool is_valid
= true;
120 if (!Read(usage_file_path
, &is_valid
, &dirty
, &usage
))
125 bool FileSystemUsageCache::AtomicUpdateUsageByDelta(
126 const base::FilePath
& usage_file_path
, int64 delta
) {
127 TRACE_EVENT0("FileSystem", "UsageCache::AtomicUpdateUsageByDelta");
128 DCHECK(CalledOnValidThread());
129 bool is_valid
= true;
132 if (!Read(usage_file_path
, &is_valid
, &dirty
, &usage
))
134 return Write(usage_file_path
, is_valid
, dirty
, usage
+ delta
);
137 bool FileSystemUsageCache::UpdateUsage(const base::FilePath
& usage_file_path
,
139 TRACE_EVENT0("FileSystem", "UsageCache::UpdateUsage");
140 DCHECK(CalledOnValidThread());
141 return Write(usage_file_path
, true, 0, fs_usage
);
144 bool FileSystemUsageCache::Exists(const base::FilePath
& usage_file_path
) {
145 TRACE_EVENT0("FileSystem", "UsageCache::Exists");
146 DCHECK(CalledOnValidThread());
147 return base::PathExists(usage_file_path
);
150 bool FileSystemUsageCache::Delete(const base::FilePath
& usage_file_path
) {
151 TRACE_EVENT0("FileSystem", "UsageCache::Delete");
152 DCHECK(CalledOnValidThread());
154 return base::DeleteFile(usage_file_path
, true);
157 void FileSystemUsageCache::CloseCacheFiles() {
158 TRACE_EVENT0("FileSystem", "UsageCache::CloseCacheFiles");
159 DCHECK(CalledOnValidThread());
160 STLDeleteValues(&cache_files_
);
164 bool FileSystemUsageCache::Read(const base::FilePath
& usage_file_path
,
168 TRACE_EVENT0("FileSystem", "UsageCache::Read");
169 DCHECK(CalledOnValidThread());
173 char buffer
[kUsageFileSize
];
175 if (usage_file_path
.empty() ||
176 !ReadBytes(usage_file_path
, buffer
, kUsageFileSize
))
178 base::Pickle
read_pickle(buffer
, kUsageFileSize
);
179 base::PickleIterator
iter(read_pickle
);
183 if (!iter
.ReadBytes(&header
, kUsageFileHeaderSize
) ||
184 !iter
.ReadBool(is_valid
) ||
185 !iter
.ReadUInt32(&dirty
) ||
186 !iter
.ReadInt64(&usage
))
189 if (header
[0] != kUsageFileHeader
[0] ||
190 header
[1] != kUsageFileHeader
[1] ||
191 header
[2] != kUsageFileHeader
[2] ||
192 header
[3] != kUsageFileHeader
[3])
200 bool FileSystemUsageCache::Write(const base::FilePath
& usage_file_path
,
204 TRACE_EVENT0("FileSystem", "UsageCache::Write");
205 DCHECK(CalledOnValidThread());
206 base::Pickle write_pickle
;
207 write_pickle
.WriteBytes(kUsageFileHeader
, kUsageFileHeaderSize
);
208 write_pickle
.WriteBool(is_valid
);
209 write_pickle
.WriteUInt32(dirty
);
210 write_pickle
.WriteInt64(usage
);
212 if (!WriteBytes(usage_file_path
,
213 static_cast<const char*>(write_pickle
.data()),
214 write_pickle
.size())) {
215 Delete(usage_file_path
);
221 base::File
* FileSystemUsageCache::GetFile(const base::FilePath
& file_path
) {
222 DCHECK(CalledOnValidThread());
223 if (cache_files_
.size() >= kMaxHandleCacheSize
)
225 ScheduleCloseTimer();
227 base::File
* new_file
= NULL
;
228 std::pair
<CacheFiles::iterator
, bool> inserted
=
229 cache_files_
.insert(std::make_pair(file_path
, new_file
));
230 if (!inserted
.second
)
231 return inserted
.first
->second
;
233 new_file
= new base::File(file_path
,
234 base::File::FLAG_OPEN_ALWAYS
|
235 base::File::FLAG_READ
|
236 base::File::FLAG_WRITE
);
237 if (!new_file
->IsValid()) {
238 cache_files_
.erase(inserted
.first
);
243 inserted
.first
->second
= new_file
;
247 bool FileSystemUsageCache::ReadBytes(const base::FilePath
& file_path
,
250 DCHECK(CalledOnValidThread());
251 base::File
* file
= GetFile(file_path
);
254 return file
->Read(0, buffer
, buffer_size
) == buffer_size
;
257 bool FileSystemUsageCache::WriteBytes(const base::FilePath
& file_path
,
260 DCHECK(CalledOnValidThread());
261 base::File
* file
= GetFile(file_path
);
264 return file
->Write(0, buffer
, buffer_size
) == buffer_size
;
267 bool FileSystemUsageCache::FlushFile(const base::FilePath
& file_path
) {
268 TRACE_EVENT0("FileSystem", "UsageCache::FlushFile");
269 DCHECK(CalledOnValidThread());
270 base::File
* file
= GetFile(file_path
);
273 return file
->Flush();
276 void FileSystemUsageCache::ScheduleCloseTimer() {
277 DCHECK(CalledOnValidThread());
279 timer_
.reset(new TimedTaskHelper(task_runner_
.get()));
281 if (timer_
->IsRunning()) {
286 timer_
->Start(FROM_HERE
,
287 base::TimeDelta::FromSeconds(kCloseDelaySeconds
),
288 base::Bind(&FileSystemUsageCache::CloseCacheFiles
,
289 weak_factory_
.GetWeakPtr()));
292 bool FileSystemUsageCache::CalledOnValidThread() {
293 return !task_runner_
.get() || task_runner_
->RunsTasksOnCurrentThread();
296 bool FileSystemUsageCache::HasCacheFileHandle(const base::FilePath
& file_path
) {
297 DCHECK(CalledOnValidThread());
298 DCHECK_LE(cache_files_
.size(), kMaxHandleCacheSize
);
299 return ContainsKey(cache_files_
, file_path
);
302 } // namespace storage