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 fileapi { 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 for (CacheFiles::iterator itr = cache_files_.begin(); 166 itr != cache_files_.end(); ++itr) { 167 if (itr->second != base::kInvalidPlatformFileValue) 168 base::ClosePlatformFile(itr->second); 169 } 170 cache_files_.clear(); 171 timer_.reset(); 172 } 173 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; 192 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; 198 199 if (header[0] != kUsageFileHeader[0] || 200 header[1] != kUsageFileHeader[1] || 201 header[2] != kUsageFileHeader[2] || 202 header[3] != kUsageFileHeader[3]) 203 return false; 204 205 *dirty_out = dirty; 206 *usage_out = usage; 207 return true; 208 } 209 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); 221 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; 227 } 228 return true; 229 } 230 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(); 237 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; 244 } 245 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; 256 } 257 258 inserted.first->second = platform_file; 259 *file = platform_file; 260 return true; 261 } 262 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; 271 } 272 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; 281 } 282 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); 288 } 289 290 void FileSystemUsageCache::ScheduleCloseTimer() { 291 DCHECK(CalledOnValidThread()); 292 if (!timer_) 293 timer_.reset(new TimedTaskHelper(task_runner_.get())); 294 295 if (timer_->IsRunning()) { 296 timer_->Reset(); 297 return; 298 } 299 300 timer_->Start(FROM_HERE, 301 base::TimeDelta::FromSeconds(kCloseDelaySeconds), 302 base::Bind(&FileSystemUsageCache::CloseCacheFiles, 303 weak_factory_.GetWeakPtr())); 304 } 305 306 bool FileSystemUsageCache::CalledOnValidThread() { 307 return !task_runner_.get() || task_runner_->RunsTasksOnCurrentThread(); 308 } 309 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); 314 } 315 316 } // namespace fileapi 317