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 "webkit/browser/fileapi/file_system_usage_cache.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"
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 for (CacheFiles::iterator itr
= cache_files_
.begin();
166 itr
!= cache_files_
.end(); ++itr
) {
167 if (itr
->second
!= base::kInvalidPlatformFileValue
)
168 base::ClosePlatformFile(itr
->second
);
170 cache_files_
.clear();
174 bool FileSystemUsageCache::Read(const base::FilePath
& usage_file_path
,
178 TRACE_EVENT0("FileSystem", "UsageCache::Read");
179 DCHECK(CalledOnValidThread());
183 char buffer
[kUsageFileSize
];
185 if (usage_file_path
.empty() ||
186 !ReadBytes(usage_file_path
, buffer
, kUsageFileSize
))
188 Pickle
read_pickle(buffer
, kUsageFileSize
);
189 PickleIterator
iter(read_pickle
);
193 if (!read_pickle
.ReadBytes(&iter
, &header
, kUsageFileHeaderSize
) ||
194 !read_pickle
.ReadBool(&iter
, is_valid
) ||
195 !read_pickle
.ReadUInt32(&iter
, &dirty
) ||
196 !read_pickle
.ReadInt64(&iter
, &usage
))
199 if (header
[0] != kUsageFileHeader
[0] ||
200 header
[1] != kUsageFileHeader
[1] ||
201 header
[2] != kUsageFileHeader
[2] ||
202 header
[3] != kUsageFileHeader
[3])
210 bool FileSystemUsageCache::Write(const base::FilePath
& usage_file_path
,
214 TRACE_EVENT0("FileSystem", "UsageCache::Write");
215 DCHECK(CalledOnValidThread());
217 write_pickle
.WriteBytes(kUsageFileHeader
, kUsageFileHeaderSize
);
218 write_pickle
.WriteBool(is_valid
);
219 write_pickle
.WriteUInt32(dirty
);
220 write_pickle
.WriteInt64(usage
);
222 if (!WriteBytes(usage_file_path
,
223 static_cast<const char*>(write_pickle
.data()),
224 write_pickle
.size())) {
225 Delete(usage_file_path
);
231 bool FileSystemUsageCache::GetPlatformFile(const base::FilePath
& file_path
,
232 base::PlatformFile
* file
) {
233 DCHECK(CalledOnValidThread());
234 if (cache_files_
.size() >= kMaxHandleCacheSize
)
236 ScheduleCloseTimer();
238 std::pair
<CacheFiles::iterator
, bool> inserted
=
240 std::make_pair(file_path
, base::kInvalidPlatformFileValue
));
241 if (!inserted
.second
) {
242 *file
= inserted
.first
->second
;
246 base::PlatformFileError error
= base::PLATFORM_FILE_ERROR_FAILED
;
247 base::PlatformFile platform_file
=
248 base::CreatePlatformFile(file_path
,
249 base::PLATFORM_FILE_OPEN_ALWAYS
|
250 base::PLATFORM_FILE_READ
|
251 base::PLATFORM_FILE_WRITE
,
253 if (error
!= base::PLATFORM_FILE_OK
) {
254 cache_files_
.erase(inserted
.first
);
258 inserted
.first
->second
= platform_file
;
259 *file
= platform_file
;
263 bool FileSystemUsageCache::ReadBytes(const base::FilePath
& file_path
,
266 DCHECK(CalledOnValidThread());
267 base::PlatformFile file
;
268 if (!GetPlatformFile(file_path
, &file
))
270 return base::ReadPlatformFile(file
, 0, buffer
, buffer_size
) == buffer_size
;
273 bool FileSystemUsageCache::WriteBytes(const base::FilePath
& file_path
,
276 DCHECK(CalledOnValidThread());
277 base::PlatformFile file
;
278 if (!GetPlatformFile(file_path
, &file
))
280 return base::WritePlatformFile(file
, 0, buffer
, buffer_size
) == buffer_size
;
283 bool FileSystemUsageCache::FlushFile(const base::FilePath
& file_path
) {
284 TRACE_EVENT0("FileSystem", "UsageCache::FlushFile");
285 DCHECK(CalledOnValidThread());
286 base::PlatformFile file
= base::kInvalidPlatformFileValue
;
287 return GetPlatformFile(file_path
, &file
) && base::FlushPlatformFile(file
);
290 void FileSystemUsageCache::ScheduleCloseTimer() {
291 DCHECK(CalledOnValidThread());
293 timer_
.reset(new TimedTaskHelper(task_runner_
.get()));
295 if (timer_
->IsRunning()) {
300 timer_
->Start(FROM_HERE
,
301 base::TimeDelta::FromSeconds(kCloseDelaySeconds
),
302 base::Bind(&FileSystemUsageCache::CloseCacheFiles
,
303 weak_factory_
.GetWeakPtr()));
306 bool FileSystemUsageCache::CalledOnValidThread() {
307 return !task_runner_
.get() || task_runner_
->RunsTasksOnCurrentThread();
310 bool FileSystemUsageCache::HasCacheFileHandle(const base::FilePath
& file_path
) {
311 DCHECK(CalledOnValidThread());
312 DCHECK_LE(cache_files_
.size(), kMaxHandleCacheSize
);
313 return ContainsKey(cache_files_
, file_path
);
316 } // namespace fileapi