Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2013 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 "net/disk_cache/simple/simple_synchronous_entry.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <cstring> | |
| 9 #include <limits> | |
| 10 | |
| 11 #include "base/basictypes.h" | |
| 12 #include "base/file_util.h" | |
| 13 #include "base/hash.h" | |
| 14 #include "base/location.h" | |
| 15 #include "base/memory/scoped_ptr.h" | |
| 16 #include "base/message_loop_proxy.h" | |
| 17 #include "base/sha1.h" | |
| 18 #include "base/stringprintf.h" | |
| 19 #include "base/task_runner.h" | |
| 20 #include "net/base/io_buffer.h" | |
| 21 #include "net/base/net_errors.h" | |
| 22 #include "net/disk_cache/simple/simple_disk_format.h" | |
| 23 | |
| 24 using base::ClosePlatformFile; | |
| 25 using base::GetPlatformFileInfo; | |
| 26 using base::PlatformFileError; | |
| 27 using base::PlatformFileInfo; | |
| 28 using base::PLATFORM_FILE_CREATE; | |
| 29 using base::PLATFORM_FILE_OK; | |
| 30 using base::PLATFORM_FILE_OPEN; | |
| 31 using base::PLATFORM_FILE_READ; | |
| 32 using base::PLATFORM_FILE_WRITE; | |
| 33 using base::ReadPlatformFile; | |
| 34 using base::TaskRunner; | |
| 35 using base::Time; | |
| 36 using base::TruncatePlatformFile; | |
| 37 using base::WritePlatformFile; | |
| 38 | |
| 39 namespace { | |
| 40 | |
| 41 std::string GetFilenameForKeyAndIndex(const std::string& key, int index) { | |
| 42 const std::string sha_hash = base::SHA1HashString(key); | |
| 43 return StringPrintf("%02x%02x%02x%02x%02x_%1d", | |
| 44 implicit_cast<unsigned char>(sha_hash[0]), | |
| 45 implicit_cast<unsigned char>(sha_hash[1]), | |
| 46 implicit_cast<unsigned char>(sha_hash[2]), | |
| 47 implicit_cast<unsigned char>(sha_hash[3]), | |
| 48 implicit_cast<unsigned char>(sha_hash[4]), index); | |
| 49 } | |
| 50 | |
| 51 int32 DataSizeFromKeyAndFileSize(size_t key_size, int64 file_size) { | |
| 52 int64 data_size = file_size - key_size - sizeof(disk_cache::SimpleFileHeader); | |
| 53 DCHECK_GE(implicit_cast<int64>(std::numeric_limits<int32>::max()), data_size); | |
| 54 return data_size; | |
| 55 } | |
| 56 | |
| 57 int64 FileOffsetFromDataOffset(size_t key_size, int data_offset) { | |
| 58 const int64 headers_size = sizeof(disk_cache::SimpleFileHeader) + | |
| 59 key_size; | |
| 60 return headers_size + data_offset; | |
| 61 } | |
| 62 | |
| 63 } // namespace | |
| 64 | |
| 65 namespace disk_cache { | |
| 66 | |
| 67 // static | |
| 68 void SimpleSynchronousEntry::OpenEntry( | |
| 69 const FilePath& path, | |
| 70 const std::string& key, | |
| 71 const scoped_refptr<TaskRunner>& callback_runner, | |
| 72 const SynchronousCreationCallback& callback) { | |
| 73 SimpleSynchronousEntry* sync_entry = | |
| 74 new SimpleSynchronousEntry(callback_runner, path, key); | |
| 75 | |
| 76 if (!sync_entry->InitializeForOpen()) { | |
| 77 delete sync_entry; | |
| 78 sync_entry = NULL; | |
| 79 } | |
| 80 callback_runner->PostTask(FROM_HERE, base::Bind(callback, sync_entry)); | |
| 81 } | |
| 82 | |
| 83 // static | |
| 84 void SimpleSynchronousEntry::CreateEntry( | |
| 85 const FilePath& path, | |
| 86 const std::string& key, | |
| 87 const scoped_refptr<TaskRunner>& callback_runner, | |
| 88 const SynchronousCreationCallback& callback) { | |
| 89 SimpleSynchronousEntry* sync_entry = | |
| 90 new SimpleSynchronousEntry(callback_runner, path, key); | |
| 91 | |
| 92 if (!sync_entry->InitializeForCreate()) { | |
| 93 delete sync_entry; | |
| 94 sync_entry = NULL; | |
| 95 } | |
| 96 callback_runner->PostTask(FROM_HERE, base::Bind(callback, sync_entry)); | |
| 97 } | |
| 98 | |
| 99 // static | |
| 100 void SimpleSynchronousEntry::DoomEntry( | |
| 101 const FilePath& path, | |
| 102 const std::string& key, | |
| 103 scoped_refptr<TaskRunner> callback_runner, | |
| 104 const net::CompletionCallback& callback) { | |
| 105 for (int i = 0; i < kIndexCount; ++i) { | |
| 106 FilePath to_delete = path.AppendASCII(GetFilenameForKeyAndIndex(key, i)); | |
| 107 bool result = file_util::Delete(to_delete, false); | |
| 108 DLOG_IF(ERROR, !result) << "Could not delete " << to_delete.MaybeAsASCII(); | |
| 109 } | |
| 110 if (!callback.is_null()) | |
| 111 callback_runner->PostTask(FROM_HERE, base::Bind(callback, net::OK)); | |
| 112 } | |
| 113 | |
| 114 void SimpleSynchronousEntry::DoomAndClose() { | |
| 115 scoped_refptr<TaskRunner> callback_runner = callback_runner_; | |
| 116 FilePath path = path_; | |
| 117 std::string key = key_; | |
| 118 | |
| 119 Close(); | |
| 120 // |this| is now deleted. | |
| 121 | |
| 122 DoomEntry(path, key, callback_runner, net::CompletionCallback()); | |
| 123 } | |
| 124 | |
| 125 void SimpleSynchronousEntry::Close() { | |
| 126 for (int i = 0; i < kIndexCount; ++i) { | |
| 127 bool result = ClosePlatformFile(files_[i]); | |
| 128 DLOG_IF(INFO, !result) << "Could not Close() file."; | |
| 129 } | |
| 130 delete this; | |
| 131 } | |
| 132 | |
| 133 void SimpleSynchronousEntry::ReadData( | |
| 134 int index, | |
| 135 int offset, | |
| 136 net::IOBuffer* buf, | |
| 137 int buf_len, | |
| 138 const SynchronousOperationCallback& callback) { | |
| 139 DCHECK(initialized_); | |
| 140 | |
| 141 int64 file_offset = FileOffsetFromDataOffset(key_.size(), offset); | |
| 142 int bytes_read = ReadPlatformFile(files_[index], file_offset, | |
| 143 buf->data(), buf_len); | |
| 144 if (bytes_read > 0) | |
| 145 last_used_ = Time::Now(); | |
| 146 int result = (bytes_read >= 0) ? bytes_read : net::ERR_FAILED; | |
| 147 callback_runner_->PostTask(FROM_HERE, base::Bind(callback, result)); | |
| 148 } | |
| 149 | |
| 150 void SimpleSynchronousEntry::WriteData( | |
| 151 int index, | |
| 152 int offset, | |
| 153 net::IOBuffer* buf, | |
| 154 int buf_len, | |
| 155 const SynchronousOperationCallback& callback, | |
| 156 bool truncate) { | |
| 157 DCHECK(initialized_); | |
| 158 | |
| 159 int64 file_offset = FileOffsetFromDataOffset(key_.size(), offset); | |
| 160 if (buf_len > 0) { | |
| 161 if (WritePlatformFile(files_[index], file_offset, buf->data(), buf_len) != | |
| 162 buf_len) { | |
| 163 callback_runner_->PostTask(FROM_HERE, | |
| 164 base::Bind(callback, net::ERR_FAILED)); | |
| 165 return; | |
| 166 } | |
| 167 data_size_[index] = std::max(data_size_[index], offset + buf_len); | |
| 168 } | |
| 169 if (truncate) { | |
| 170 data_size_[index] = offset + buf_len; | |
| 171 if (!TruncatePlatformFile(files_[index], file_offset + buf_len)) { | |
| 172 callback_runner_->PostTask(FROM_HERE, | |
| 173 base::Bind(callback, net::ERR_FAILED)); | |
| 174 return; | |
| 175 } | |
| 176 } | |
| 177 last_modified_ = Time::Now(); | |
| 178 callback_runner_->PostTask(FROM_HERE, base::Bind(callback, buf_len)); | |
| 179 } | |
| 180 | |
| 181 SimpleSynchronousEntry::SimpleSynchronousEntry( | |
| 182 const scoped_refptr<TaskRunner>& callback_runner, | |
| 183 const FilePath& path, | |
| 184 const std::string& key) | |
| 185 : callback_runner_(callback_runner), | |
| 186 path_(path), | |
| 187 key_(key), | |
| 188 initialized_(false) { | |
| 189 } | |
| 190 | |
| 191 SimpleSynchronousEntry::~SimpleSynchronousEntry() { | |
| 192 } | |
| 193 | |
| 194 bool SimpleSynchronousEntry::OpenOrCreateFiles(bool create) { | |
| 195 for (int i = 0; i < kIndexCount; ++i) { | |
| 196 FilePath filename = path_.AppendASCII(GetFilenameForKeyAndIndex(key_, i)); | |
| 197 int flags = PLATFORM_FILE_READ | PLATFORM_FILE_WRITE; | |
| 198 if (create) | |
| 199 flags |= PLATFORM_FILE_CREATE; | |
| 200 else | |
| 201 flags |= PLATFORM_FILE_OPEN; | |
| 202 PlatformFileError error; | |
| 203 files_[i] = CreatePlatformFile(filename, flags, NULL, &error); | |
| 204 if (error != PLATFORM_FILE_OK) { | |
| 205 DLOG(INFO) << "CreatePlatformFile error " << error << " while " | |
|
pasko-google - do not use
2013/02/15 16:42:01
this fires every time there is a cache miss, it is
gavinp
2013/02/15 17:19:54
Done.
| |
| 206 << (create ? "creating " : "opening ") | |
| 207 << filename.MaybeAsASCII(); | |
| 208 while (--i >= 0) { | |
| 209 bool did_close = ClosePlatformFile(files_[i]); | |
| 210 DLOG_IF(INFO, did_close) << "Could not close file " | |
|
pasko-google - do not use
2013/02/15 16:42:01
!did_close
gavinp
2013/02/15 17:19:54
Done.
| |
| 211 << filename.MaybeAsASCII(); | |
| 212 } | |
| 213 return false; | |
| 214 } | |
| 215 } | |
| 216 | |
| 217 for (int i = 0; i < kIndexCount; ++i) { | |
| 218 PlatformFileInfo file_info; | |
| 219 bool success = GetPlatformFileInfo(files_[i], &file_info); | |
| 220 if (!success) { | |
| 221 DLOG(ERROR) << "Could not get platform file info."; | |
|
rvargas (doing something else)
2013/02/14 21:09:31
nit: same level as other failures of this code (in
gavinp
2013/02/15 16:04:01
Done.
| |
| 222 continue; | |
| 223 } | |
| 224 last_used_ = std::max(last_used_, file_info.last_accessed); | |
| 225 last_modified_ = std::max(last_modified_, file_info.last_modified); | |
| 226 data_size_[i] = DataSizeFromKeyAndFileSize(key_.size(), file_info.size); | |
| 227 } | |
| 228 | |
| 229 return true; | |
| 230 } | |
| 231 | |
| 232 bool SimpleSynchronousEntry::InitializeForOpen() { | |
| 233 DCHECK(!initialized_); | |
| 234 if (!OpenOrCreateFiles(false)) | |
| 235 return false; | |
| 236 | |
| 237 for (int i = 0; i < kIndexCount; ++i) { | |
| 238 SimpleFileHeader header; | |
| 239 int header_read_result = | |
| 240 ReadPlatformFile(files_[i], 0, reinterpret_cast<char*>(&header), | |
| 241 sizeof(header)); | |
| 242 if (header_read_result != sizeof(header)) { | |
| 243 DLOG(WARNING) << "Cannot read header from entry."; | |
| 244 return false; | |
| 245 } | |
| 246 | |
| 247 if (header.initial_magic_number != kSimpleInitialMagicNumber) { | |
| 248 // TODO(gavinp): This seems very bad; for now we log at WARNING, but we | |
| 249 // should give consideration to not saturating the log with these if that | |
| 250 // becomes a problem. | |
| 251 DLOG(WARNING) << "Magic number did not match."; | |
| 252 return false; | |
| 253 } | |
| 254 | |
| 255 if (header.version != kSimpleVersion) { | |
| 256 DLOG(WARNING) << "Unreadable version."; | |
| 257 return false; | |
| 258 } | |
| 259 | |
| 260 scoped_ptr<char[]> key(new char[header.key_length]); | |
| 261 int key_read_result = ReadPlatformFile(files_[i], sizeof(header), | |
| 262 key.get(), header.key_length); | |
| 263 if (key_read_result != implicit_cast<int>(header.key_length)) { | |
| 264 DLOG(WARNING) << "Cannot read key from entry."; | |
| 265 return false; | |
| 266 } | |
| 267 if (header.key_length != key_.size() || | |
| 268 std::memcmp(key_.data(), key.get(), key_.size()) != 0) { | |
| 269 // TODO(gavinp): Since the way we use Entry SHA to name entries means this | |
| 270 // is expected to occur at some frequency, add unit_tests that this does | |
| 271 // is handled gracefully at higher levels. | |
| 272 DLOG(WARNING) << "Key mismatch on open."; | |
| 273 return false; | |
| 274 } | |
| 275 | |
| 276 if (base::Hash(key.get(), header.key_length) != header.key_hash) { | |
| 277 DLOG(WARNING) << "Hash mismatch on key."; | |
| 278 return false; | |
| 279 } | |
| 280 } | |
| 281 | |
| 282 initialized_ = true; | |
| 283 return true; | |
| 284 } | |
| 285 | |
| 286 bool SimpleSynchronousEntry::InitializeForCreate() { | |
| 287 DCHECK(!initialized_); | |
| 288 if (!OpenOrCreateFiles(true)) { | |
| 289 DLOG(WARNING) << "Could not create platform files."; | |
| 290 return false; | |
| 291 } | |
| 292 | |
| 293 for (int i = 0; i < kIndexCount; ++i) { | |
| 294 SimpleFileHeader header; | |
| 295 header.initial_magic_number = kSimpleInitialMagicNumber; | |
| 296 header.version = kSimpleVersion; | |
| 297 | |
| 298 header.key_length = key_.size(); | |
| 299 header.key_hash = base::Hash(key_); | |
| 300 | |
| 301 if (WritePlatformFile(files_[i], 0, reinterpret_cast<char*>(&header), | |
| 302 sizeof(header)) != sizeof(header)) { | |
| 303 // TODO(gavinp): Clean up created files. | |
| 304 DLOG(WARNING) << "Could not write headers to new cache entry."; | |
| 305 return false; | |
| 306 } | |
| 307 | |
| 308 if (WritePlatformFile(files_[i], sizeof(header), key_.data(), | |
| 309 key_.size()) != implicit_cast<int>(key_.size())) { | |
| 310 // TODO(gavinp): Clean up created files. | |
| 311 DLOG(WARNING) << "Could not write keys to new cache entry."; | |
| 312 return false; | |
| 313 } | |
| 314 } | |
| 315 | |
| 316 initialized_ = true; | |
| 317 return true; | |
| 318 } | |
| 319 | |
| 320 } // namespace disk_cache | |
| OLD | NEW |