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 17namespace fileapi { 18 19namespace { 20const int64 kCloseDelaySeconds = 5; 21const size_t kMaxHandleCacheSize = 2; 22} // namespace 23 24FileSystemUsageCache::FileSystemUsageCache( 25 base::SequencedTaskRunner* task_runner) 26 : task_runner_(task_runner), 27 weak_factory_(this) { 28} 29 30FileSystemUsageCache::~FileSystemUsageCache() { 31 task_runner_ = NULL; 32 CloseCacheFiles(); 33} 34 35const base::FilePath::CharType FileSystemUsageCache::kUsageFileName[] = 36 FILE_PATH_LITERAL(".usage"); 37const char FileSystemUsageCache::kUsageFileHeader[] = "FSU5"; 38const int FileSystemUsageCache::kUsageFileHeaderSize = 4; 39 40// Pickle::{Read,Write}Bool treat bool as int 41const int FileSystemUsageCache::kUsageFileSize = 42 sizeof(Pickle::Header) + 43 FileSystemUsageCache::kUsageFileHeaderSize + 44 sizeof(int) + sizeof(int32) + sizeof(int64); // NOLINT 45 46bool 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 60bool 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 74bool 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 91bool 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 107bool 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 119bool 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 130bool 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 142bool 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 149bool 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 155bool 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 162void FileSystemUsageCache::CloseCacheFiles() { 163 TRACE_EVENT0("FileSystem", "UsageCache::CloseCacheFiles"); 164 DCHECK(CalledOnValidThread()); 165 STLDeleteValues(&cache_files_); 166 timer_.reset(); 167} 168 169bool 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 205bool 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 226base::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 252bool 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 262bool 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 272bool 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 281void 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 297bool FileSystemUsageCache::CalledOnValidThread() { 298 return !task_runner_.get() || task_runner_->RunsTasksOnCurrentThread(); 299} 300 301bool 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 fileapi 308