OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "webkit/browser/fileapi/file_system_usage_cache.h" | |
6 | |
7 #include <utility> | |
8 | |
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" | |
16 | |
17 namespace storage { | |
18 | |
19 namespace { | |
20 const int64 kCloseDelaySeconds = 5; | |
21 const size_t kMaxHandleCacheSize = 2; | |
22 } // namespace | |
23 | |
24 FileSystemUsageCache::FileSystemUsageCache( | |
25 base::SequencedTaskRunner* task_runner) | |
26 : task_runner_(task_runner), | |
27 weak_factory_(this) { | |
28 } | |
29 | |
30 FileSystemUsageCache::~FileSystemUsageCache() { | |
31 task_runner_ = NULL; | |
32 CloseCacheFiles(); | |
33 } | |
34 | |
35 const base::FilePath::CharType FileSystemUsageCache::kUsageFileName[] = | |
36 FILE_PATH_LITERAL(".usage"); | |
37 const char FileSystemUsageCache::kUsageFileHeader[] = "FSU5"; | |
38 const int FileSystemUsageCache::kUsageFileHeaderSize = 4; | |
39 | |
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 | |
45 | |
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; | |
58 } | |
59 | |
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; | |
72 } | |
73 | |
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; | |
84 | |
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; | |
89 } | |
90 | |
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; | |
100 | |
101 if (dirty <= 0) | |
102 return false; | |
103 | |
104 return Write(usage_file_path, is_valid, dirty - 1, usage); | |
105 } | |
106 | |
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; | |
115 | |
116 return Write(usage_file_path, false, dirty, usage); | |
117 } | |
118 | |
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; | |
128 } | |
129 | |
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); | |
140 } | |
141 | |
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); | |
147 } | |
148 | |
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); | |
153 } | |
154 | |
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); | |
160 } | |
161 | |
162 void FileSystemUsageCache::CloseCacheFiles() { | |
163 TRACE_EVENT0("FileSystem", "UsageCache::CloseCacheFiles"); | |
164 DCHECK(CalledOnValidThread()); | |
165 STLDeleteValues(&cache_files_); | |
166 timer_.reset(); | |
167 } | |
168 | |
169 bool FileSystemUsageCache::Read(const base::FilePath& usage_file_path, | |
170 bool* is_valid, | |
171 uint32* dirty_out, | |
172 int64* usage_out) { | |
173 TRACE_EVENT0("FileSystem", "UsageCache::Read"); | |
174 DCHECK(CalledOnValidThread()); | |
175 DCHECK(is_valid); | |
176 DCHECK(dirty_out); | |
177 DCHECK(usage_out); | |
178 char buffer[kUsageFileSize]; | |
179 const char *header; | |
180 if (usage_file_path.empty() || | |
181 !ReadBytes(usage_file_path, buffer, kUsageFileSize)) | |
182 return false; | |
183 Pickle read_pickle(buffer, kUsageFileSize); | |
184 PickleIterator iter(read_pickle); | |
185 uint32 dirty = 0; | |
186 int64 usage = 0; | |
187 | |
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)) | |
192 return false; | |
193 | |
194 if (header[0] != kUsageFileHeader[0] || | |
195 header[1] != kUsageFileHeader[1] || | |
196 header[2] != kUsageFileHeader[2] || | |
197 header[3] != kUsageFileHeader[3]) | |
198 return false; | |
199 | |
200 *dirty_out = dirty; | |
201 *usage_out = usage; | |
202 return true; | |
203 } | |
204 | |
205 bool FileSystemUsageCache::Write(const base::FilePath& usage_file_path, | |
206 bool is_valid, | |
207 int32 dirty, | |
208 int64 usage) { | |
209 TRACE_EVENT0("FileSystem", "UsageCache::Write"); | |
210 DCHECK(CalledOnValidThread()); | |
211 Pickle write_pickle; | |
212 write_pickle.WriteBytes(kUsageFileHeader, kUsageFileHeaderSize); | |
213 write_pickle.WriteBool(is_valid); | |
214 write_pickle.WriteUInt32(dirty); | |
215 write_pickle.WriteInt64(usage); | |
216 | |
217 if (!WriteBytes(usage_file_path, | |
218 static_cast<const char*>(write_pickle.data()), | |
219 write_pickle.size())) { | |
220 Delete(usage_file_path); | |
221 return false; | |
222 } | |
223 return true; | |
224 } | |
225 | |
226 base::File* FileSystemUsageCache::GetFile(const base::FilePath& file_path) { | |
227 DCHECK(CalledOnValidThread()); | |
228 if (cache_files_.size() >= kMaxHandleCacheSize) | |
229 CloseCacheFiles(); | |
230 ScheduleCloseTimer(); | |
231 | |
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; | |
237 | |
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); | |
244 delete new_file; | |
245 return NULL; | |
246 } | |
247 | |
248 inserted.first->second = new_file; | |
249 return new_file; | |
250 } | |
251 | |
252 bool FileSystemUsageCache::ReadBytes(const base::FilePath& file_path, | |
253 char* buffer, | |
254 int64 buffer_size) { | |
255 DCHECK(CalledOnValidThread()); | |
256 base::File* file = GetFile(file_path); | |
257 if (!file) | |
258 return false; | |
259 return file->Read(0, buffer, buffer_size) == buffer_size; | |
260 } | |
261 | |
262 bool FileSystemUsageCache::WriteBytes(const base::FilePath& file_path, | |
263 const char* buffer, | |
264 int64 buffer_size) { | |
265 DCHECK(CalledOnValidThread()); | |
266 base::File* file = GetFile(file_path); | |
267 if (!file) | |
268 return false; | |
269 return file->Write(0, buffer, buffer_size) == buffer_size; | |
270 } | |
271 | |
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); | |
276 if (!file) | |
277 return false; | |
278 return file->Flush(); | |
279 } | |
280 | |
281 void FileSystemUsageCache::ScheduleCloseTimer() { | |
282 DCHECK(CalledOnValidThread()); | |
283 if (!timer_) | |
284 timer_.reset(new TimedTaskHelper(task_runner_.get())); | |
285 | |
286 if (timer_->IsRunning()) { | |
287 timer_->Reset(); | |
288 return; | |
289 } | |
290 | |
291 timer_->Start(FROM_HERE, | |
292 base::TimeDelta::FromSeconds(kCloseDelaySeconds), | |
293 base::Bind(&FileSystemUsageCache::CloseCacheFiles, | |
294 weak_factory_.GetWeakPtr())); | |
295 } | |
296 | |
297 bool FileSystemUsageCache::CalledOnValidThread() { | |
298 return !task_runner_.get() || task_runner_->RunsTasksOnCurrentThread(); | |
299 } | |
300 | |
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); | |
305 } | |
306 | |
307 } // namespace storage | |
OLD | NEW |