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/very_simple/very_simple_synchronous_entry.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 #include <cstring> | |
| 9 | |
| 10 #include "base/basictypes.h" | |
| 11 #include "base/file_util.h" | |
| 12 #include "base/location.h" | |
| 13 #include "base/message_loop_proxy.h" | |
| 14 #include "base/sha1.h" | |
| 15 #include "base/stringprintf.h" | |
| 16 #include "base/task_runner.h" | |
| 17 #include "net/base/io_buffer.h" | |
| 18 #include "net/base/net_errors.h" | |
| 19 #include "net/disk_cache/very_simple/very_simple_disk_format.h" | |
| 20 #include "third_party/smhasher/src/MurmurHash3.h" | |
| 21 | |
| 22 using base::Bind; | |
| 23 using base::ClosePlatformFile; | |
| 24 using base::GetPlatformFileInfo; | |
| 25 using base::PlatformFileError; | |
| 26 using base::PlatformFileInfo; | |
| 27 using base::PLATFORM_FILE_CREATE_ALWAYS; | |
| 28 using base::PLATFORM_FILE_OK; | |
| 29 using base::PLATFORM_FILE_OPEN; | |
| 30 using base::PLATFORM_FILE_READ; | |
| 31 using base::PLATFORM_FILE_WRITE; | |
| 32 using base::ReadPlatformFile; | |
| 33 using base::TaskRunner; | |
| 34 using base::Time; | |
| 35 using base::TruncatePlatformFile; | |
| 36 using base::WritePlatformFile; | |
| 37 | |
| 38 namespace { | |
| 39 | |
| 40 std::string GetFilenameForKeyAndIndex(const std::string& key, int index) { | |
| 41 const std::string sha_hash = base::SHA1HashString(key); | |
| 42 return StringPrintf("%02x%02x%02x%02x%02x_%1d", | |
| 43 implicit_cast<unsigned char>(sha_hash[0]), | |
| 44 implicit_cast<unsigned char>(sha_hash[1]), | |
| 45 implicit_cast<unsigned char>(sha_hash[2]), | |
| 46 implicit_cast<unsigned char>(sha_hash[3]), | |
| 47 implicit_cast<unsigned char>(sha_hash[4]), index); | |
| 48 } | |
| 49 | |
| 50 int32 DataSizeFromKeyAndFileSize(size_t key_size, int64 file_size) { | |
| 51 return file_size - key_size - sizeof(disk_cache::VerySimpleFileHeader); | |
| 52 } | |
| 53 | |
| 54 int64 FileOffsetFromDataOffset(size_t key_size, int offset) { | |
| 55 const int64 headers_size = sizeof(disk_cache::VerySimpleFileHeader) + | |
| 56 key_size; | |
| 57 return headers_size + offset; | |
| 58 } | |
| 59 | |
| 60 } // namespace | |
| 61 | |
| 62 namespace disk_cache { | |
| 63 | |
| 64 // static | |
| 65 void VerySimpleSynchronousEntry::OpenEntry( | |
| 66 const FilePath& path, | |
| 67 const std::string& key, | |
| 68 const scoped_refptr<TaskRunner>& callback_runner, | |
| 69 const SynchronousEntryCallback& callback) { | |
| 70 VerySimpleSynchronousEntry* sync_entry = | |
| 71 new VerySimpleSynchronousEntry(callback_runner, path, key); | |
| 72 | |
| 73 if (!sync_entry->InitializeForOpen()) { | |
| 74 delete sync_entry; | |
| 75 callback_runner->PostTask(FROM_HERE, | |
| 76 Bind(callback, | |
| 77 static_cast<VerySimpleSynchronousEntry*>( | |
| 78 NULL), | |
| 79 net::ERR_FAILED)); | |
| 80 return; | |
| 81 } | |
| 82 callback_runner->PostTask(FROM_HERE, Bind(callback, sync_entry, net::OK)); | |
|
rvargas (doing something else)
2013/02/06 03:28:40
nit: merge the two PostTasks?
gavinp
2013/02/08 23:17:51
Done.
| |
| 83 } | |
| 84 | |
| 85 // static | |
| 86 void VerySimpleSynchronousEntry::CreateEntry( | |
| 87 const FilePath& path, | |
| 88 const std::string& key, | |
| 89 const scoped_refptr<TaskRunner>& callback_runner, | |
| 90 const SynchronousEntryCallback& callback) { | |
| 91 VerySimpleSynchronousEntry* sync_entry = | |
| 92 new VerySimpleSynchronousEntry(callback_runner, path, key); | |
| 93 | |
| 94 if (!sync_entry->InitializeForCreate()) { | |
| 95 delete sync_entry; | |
| 96 callback_runner->PostTask(FROM_HERE, | |
| 97 Bind(callback, | |
| 98 static_cast<VerySimpleSynchronousEntry*>( | |
| 99 NULL), | |
| 100 net::ERR_FAILED)); | |
| 101 return; | |
| 102 } | |
| 103 callback_runner->PostTask(FROM_HERE, Bind(callback, sync_entry, net::OK)); | |
| 104 } | |
| 105 | |
| 106 // static | |
| 107 void VerySimpleSynchronousEntry::DoomEntry( | |
| 108 const FilePath& path, | |
| 109 const std::string& key, | |
| 110 scoped_refptr<TaskRunner> callback_runner, | |
| 111 const net::CompletionCallback& callback) { | |
| 112 for (int i = 0; i < kIndexCount; ++i) { | |
| 113 bool delete_result = | |
| 114 file_util::Delete(path.AppendASCII(GetFilenameForKeyAndIndex(key, i)), | |
|
rvargas (doing something else)
2013/02/06 03:28:40
How is Read and Write expected to work after this?
gavinp
2013/02/08 23:17:51
They are not.
pasko-google - do not use
2013/02/11 13:59:25
I think Ricardo refers to when we have pending Rea
gavinp
2013/02/11 17:55:50
Aha, yes. And we should support that, and we don't
| |
| 115 false); | |
| 116 DCHECK(delete_result); | |
| 117 } | |
| 118 if (!callback.is_null()) | |
| 119 callback_runner->PostTask(FROM_HERE, Bind(callback, net::OK)); | |
| 120 } | |
| 121 | |
| 122 void VerySimpleSynchronousEntry::DoomAndClose() { | |
| 123 scoped_refptr<TaskRunner> callback_runner = callback_runner_; | |
| 124 FilePath path = path_; | |
| 125 std::string key = key_; | |
| 126 | |
| 127 Close(); | |
| 128 // |this| is now deleted. | |
| 129 | |
| 130 DoomEntry(path, key, callback_runner, net::CompletionCallback()); | |
| 131 } | |
| 132 | |
| 133 void VerySimpleSynchronousEntry::Close() { | |
| 134 for (int i = 0; i < kIndexCount; ++i) { | |
| 135 bool result = ClosePlatformFile(files_[i]); | |
| 136 DCHECK(result); | |
| 137 } | |
| 138 delete this; | |
| 139 } | |
| 140 | |
| 141 void VerySimpleSynchronousEntry::ReadData( | |
| 142 int index, | |
| 143 int offset, | |
| 144 net::IOBuffer* buf, | |
| 145 int buf_len, | |
| 146 const SynchronousEntryCallback& callback) { | |
| 147 DCHECK(initialized_); | |
| 148 if (status_[index].mode != EntryStatus::ENTRY_READER) | |
| 149 status_[index].data_offset = 0; | |
| 150 DCHECK_EQ(status_[index].data_offset, offset); | |
| 151 status_[index].mode = EntryStatus::ENTRY_READER; | |
| 152 | |
| 153 int64 file_offset = FileOffsetFromDataOffset(key_.size(), offset); | |
| 154 int bytes_read = ReadPlatformFile(files_[index], file_offset, | |
| 155 buf->data(), buf_len); | |
| 156 if (bytes_read > 0) { | |
| 157 last_used_ = Time::Now(); | |
| 158 status_[index].data_offset += bytes_read; | |
| 159 } | |
| 160 int result = (bytes_read >= 0) ? bytes_read : net::ERR_FAILED; | |
| 161 callback_runner_->PostTask(FROM_HERE, Bind(callback, this, result)); | |
| 162 } | |
| 163 | |
| 164 void VerySimpleSynchronousEntry::WriteData( | |
| 165 int index, | |
| 166 int offset, | |
| 167 net::IOBuffer* buf, | |
| 168 int buf_len, | |
| 169 const SynchronousEntryCallback& callback, | |
| 170 bool truncate) { | |
| 171 DCHECK(initialized_); | |
| 172 if (status_[index].mode != EntryStatus::ENTRY_WRITER) | |
| 173 status_[index].data_offset = 0; | |
| 174 DCHECK_EQ(status_[index].data_offset, offset); | |
| 175 status_[index].mode = EntryStatus::ENTRY_WRITER; | |
| 176 | |
| 177 int64 file_offset = FileOffsetFromDataOffset(key_.size(), offset); | |
| 178 if (buf_len > 0) { | |
| 179 if (WritePlatformFile(files_[index], file_offset, buf->data(), buf_len) != | |
| 180 buf_len) { | |
| 181 callback_runner_->PostTask(FROM_HERE, | |
| 182 Bind(callback, this, net::ERR_FAILED)); | |
| 183 return; | |
| 184 } | |
| 185 data_size_[index] = std::max(data_size_[index], offset + buf_len); | |
| 186 status_[index].data_offset += buf_len; | |
| 187 } | |
| 188 if (truncate) { | |
| 189 data_size_[index] = offset + buf_len; | |
| 190 if (!TruncatePlatformFile(files_[index], file_offset + buf_len)) { | |
| 191 callback_runner_->PostTask(FROM_HERE, | |
| 192 Bind(callback, this, net::ERR_FAILED)); | |
| 193 return; | |
| 194 } | |
| 195 } | |
| 196 last_modified_ = Time::Now(); | |
| 197 callback_runner_->PostTask(FROM_HERE, Bind(callback, this, buf_len)); | |
| 198 } | |
| 199 | |
| 200 VerySimpleSynchronousEntry::EntryStatus::EntryStatus() | |
| 201 : mode(ENTRY_UNINITIALIZED), | |
| 202 data_offset(0) { | |
| 203 } | |
| 204 | |
| 205 VerySimpleSynchronousEntry::VerySimpleSynchronousEntry( | |
| 206 const scoped_refptr<TaskRunner>& callback_runner, | |
| 207 const FilePath& path, | |
| 208 const std::string& key) : callback_runner_(callback_runner), | |
| 209 path_(path), | |
| 210 key_(key), | |
| 211 initialized_(false) { | |
| 212 } | |
| 213 | |
| 214 VerySimpleSynchronousEntry::~VerySimpleSynchronousEntry() { | |
| 215 } | |
| 216 | |
| 217 bool VerySimpleSynchronousEntry::OpenOrCreateFiles(bool create) { | |
| 218 for (int i = 0; i < kIndexCount; ++i) { | |
| 219 FilePath filename = path_.AppendASCII(GetFilenameForKeyAndIndex(key_, i)); | |
| 220 int flags = PLATFORM_FILE_READ | PLATFORM_FILE_WRITE; | |
| 221 if (create) | |
| 222 flags = flags | PLATFORM_FILE_CREATE_ALWAYS; | |
| 223 else | |
| 224 flags = flags | PLATFORM_FILE_OPEN; | |
| 225 PlatformFileError error; | |
| 226 files_[i] = CreatePlatformFile(filename, flags, NULL, &error); | |
| 227 if (error != PLATFORM_FILE_OK) { | |
| 228 while (--i >= 0) { | |
| 229 bool did_close = ClosePlatformFile(files_[i]); | |
| 230 DCHECK(did_close); | |
| 231 } | |
| 232 return false; | |
| 233 } | |
| 234 } | |
| 235 | |
| 236 for (int i = 0; i < kIndexCount; ++i) { | |
| 237 PlatformFileInfo file_info; | |
| 238 if (!GetPlatformFileInfo(files_[i], &file_info)) | |
| 239 NOTREACHED(); | |
| 240 last_used_ = std::max(last_used_, file_info.last_accessed); | |
| 241 last_modified_ = std::max(last_modified_, file_info.last_modified); | |
| 242 data_size_[i] = DataSizeFromKeyAndFileSize(key_.size(), file_info.size); | |
| 243 } | |
| 244 | |
| 245 return true; | |
| 246 } | |
| 247 | |
| 248 bool VerySimpleSynchronousEntry::InitializeForOpen() { | |
| 249 DCHECK(!initialized_); | |
| 250 if (!OpenOrCreateFiles(false)) | |
| 251 return false; | |
| 252 | |
| 253 VLOG(8) << "VerySimpleSynchronousEntry::InitializeForOpen"; | |
|
rvargas (doing something else)
2013/02/06 03:28:40
nit: general tracing is bad
gavinp
2013/02/08 23:17:51
Done.
| |
| 254 for (int i = 0; i < kIndexCount; ++i) { | |
| 255 VerySimpleFileHeader header; | |
| 256 if (ReadPlatformFile(files_[i], 0, reinterpret_cast<char*>(&header), | |
| 257 sizeof(header)) != sizeof(header)) { | |
| 258 VLOG(9) << " no header"; | |
|
rvargas (doing something else)
2013/02/06 03:28:40
... and logging on errors is good.
gavinp
2013/02/08 23:17:51
Done.
| |
| 259 return false; | |
| 260 } | |
| 261 if (header.initial_magic_number != kVerySimpleInitialMagicNumber || | |
| 262 header.version != kVerySimpleVersion) { | |
| 263 VLOG(9) << " no magic number"; | |
| 264 return false; | |
| 265 } | |
| 266 | |
| 267 char key[1024]; | |
| 268 DCHECK_LE(header.key_length, sizeof(key)); | |
| 269 if (ReadPlatformFile(files_[i], sizeof(header), key, header.key_length) != | |
| 270 implicit_cast<int>(header.key_length)) { | |
| 271 VLOG(9) << " no key"; | |
| 272 return false; | |
| 273 } | |
| 274 if (std::memcmp(static_cast<const void*>(key_.data()), | |
| 275 static_cast<const void*>(key), | |
| 276 key_.size()) != 0) { | |
| 277 VLOG(9) << " bad key"; | |
| 278 return false; | |
| 279 } | |
| 280 | |
| 281 uint32 check_murmur3; | |
| 282 MurmurHash3_x86_32(static_cast<void*>(key), header.key_length, 0, | |
| 283 &check_murmur3); | |
| 284 if (check_murmur3 != header.key_murmur3) { | |
| 285 VLOG(9) << " bad key hash"; | |
| 286 return false; | |
| 287 } | |
| 288 } | |
| 289 | |
| 290 VLOG(9) << "success we openned it!"; | |
| 291 initialized_ = true; | |
| 292 return true; | |
| 293 } | |
| 294 | |
| 295 bool VerySimpleSynchronousEntry::InitializeForCreate() { | |
| 296 DCHECK(!initialized_); | |
| 297 if (!OpenOrCreateFiles(true)) | |
| 298 return false; | |
| 299 | |
| 300 for (int i = 0; i < kIndexCount; ++i) { | |
| 301 VerySimpleFileHeader header; | |
| 302 header.initial_magic_number = kVerySimpleInitialMagicNumber; | |
| 303 header.version = kVerySimpleVersion; | |
| 304 | |
| 305 header.key_length = key_.size(); | |
| 306 MurmurHash3_x86_32(static_cast<const void*>(key_.data()), key_.size(), 0, | |
| 307 static_cast<void*>(&header.key_murmur3)); | |
| 308 | |
| 309 if (WritePlatformFile(files_[i], 0, reinterpret_cast<char*>(&header), | |
| 310 sizeof(header)) != sizeof(header)) { | |
| 311 return false; | |
| 312 } | |
| 313 | |
| 314 if (WritePlatformFile(files_[i], sizeof(header), key_.data(), | |
| 315 key_.size()) != implicit_cast<int>(key_.size())) { | |
| 316 return false; | |
| 317 } | |
| 318 } | |
| 319 | |
| 320 initialized_ = true; | |
| 321 return true; | |
| 322 } | |
| 323 | |
| 324 } // namespace disk_cache | |
| OLD | NEW |