Add entry to histograms.xml for OverlyLargeOriginLength.
[chromium-blink-merge.git] / webkit / browser / fileapi / file_system_usage_cache.cc
blob56b1fb43566c4a8e9d40e3ed4f66fbf1eedaae27
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"
7 #include <utility>
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"
17 namespace fileapi {
19 namespace {
20 const int64 kCloseDelaySeconds = 5;
21 const size_t kMaxHandleCacheSize = 2;
22 } // namespace
24 FileSystemUsageCache::FileSystemUsageCache(
25 base::SequencedTaskRunner* task_runner)
26 : task_runner_(task_runner),
27 weak_factory_(this) {
30 FileSystemUsageCache::~FileSystemUsageCache() {
31 task_runner_ = NULL;
32 CloseCacheFiles();
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,
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;
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;
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;
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;
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;
101 if (dirty <= 0)
102 return false;
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;
111 uint32 dirty = 0;
112 int64 usage = 0;
113 if (!Read(usage_file_path, &is_valid, &dirty, &usage))
114 return false;
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;
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;
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);
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);
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());
158 CloseCacheFiles();
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();
171 timer_.reset();
174 bool FileSystemUsageCache::Read(const base::FilePath& usage_file_path,
175 bool* is_valid,
176 uint32* dirty_out,
177 int64* usage_out) {
178 TRACE_EVENT0("FileSystem", "UsageCache::Read");
179 DCHECK(CalledOnValidThread());
180 DCHECK(is_valid);
181 DCHECK(dirty_out);
182 DCHECK(usage_out);
183 char buffer[kUsageFileSize];
184 const char *header;
185 if (usage_file_path.empty() ||
186 !ReadBytes(usage_file_path, buffer, kUsageFileSize))
187 return false;
188 Pickle read_pickle(buffer, kUsageFileSize);
189 PickleIterator iter(read_pickle);
190 uint32 dirty = 0;
191 int64 usage = 0;
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))
197 return false;
199 if (header[0] != kUsageFileHeader[0] ||
200 header[1] != kUsageFileHeader[1] ||
201 header[2] != kUsageFileHeader[2] ||
202 header[3] != kUsageFileHeader[3])
203 return false;
205 *dirty_out = dirty;
206 *usage_out = usage;
207 return true;
210 bool FileSystemUsageCache::Write(const base::FilePath& usage_file_path,
211 bool is_valid,
212 int32 dirty,
213 int64 usage) {
214 TRACE_EVENT0("FileSystem", "UsageCache::Write");
215 DCHECK(CalledOnValidThread());
216 Pickle write_pickle;
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);
226 return false;
228 return true;
231 bool FileSystemUsageCache::GetPlatformFile(const base::FilePath& file_path,
232 base::PlatformFile* file) {
233 DCHECK(CalledOnValidThread());
234 if (cache_files_.size() >= kMaxHandleCacheSize)
235 CloseCacheFiles();
236 ScheduleCloseTimer();
238 std::pair<CacheFiles::iterator, bool> inserted =
239 cache_files_.insert(
240 std::make_pair(file_path, base::kInvalidPlatformFileValue));
241 if (!inserted.second) {
242 *file = inserted.first->second;
243 return true;
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,
252 NULL, &error);
253 if (error != base::PLATFORM_FILE_OK) {
254 cache_files_.erase(inserted.first);
255 return false;
258 inserted.first->second = platform_file;
259 *file = platform_file;
260 return true;
263 bool FileSystemUsageCache::ReadBytes(const base::FilePath& file_path,
264 char* buffer,
265 int64 buffer_size) {
266 DCHECK(CalledOnValidThread());
267 base::PlatformFile file;
268 if (!GetPlatformFile(file_path, &file))
269 return false;
270 return base::ReadPlatformFile(file, 0, buffer, buffer_size) == buffer_size;
273 bool FileSystemUsageCache::WriteBytes(const base::FilePath& file_path,
274 const char* buffer,
275 int64 buffer_size) {
276 DCHECK(CalledOnValidThread());
277 base::PlatformFile file;
278 if (!GetPlatformFile(file_path, &file))
279 return false;
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());
292 if (!timer_)
293 timer_.reset(new TimedTaskHelper(task_runner_.get()));
295 if (timer_->IsRunning()) {
296 timer_->Reset();
297 return;
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