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 | |
10 #include "base/basictypes.h" | |
11 #include "base/file_util.h" | |
12 #include "base/hash.h" | |
13 #include "base/location.h" | |
14 #include "base/message_loop_proxy.h" | |
15 #include "base/sha1.h" | |
16 #include "base/stringprintf.h" | |
17 #include "base/task_runner.h" | |
18 #include "net/base/io_buffer.h" | |
19 #include "net/base/net_errors.h" | |
20 #include "net/disk_cache/simple/simple_disk_format.h" | |
21 | |
22 using base::ClosePlatformFile; | |
23 using base::GetPlatformFileInfo; | |
24 using base::PlatformFileError; | |
25 using base::PlatformFileInfo; | |
26 using base::PLATFORM_FILE_CREATE_ALWAYS; | |
27 using base::PLATFORM_FILE_OK; | |
28 using base::PLATFORM_FILE_OPEN; | |
29 using base::PLATFORM_FILE_READ; | |
30 using base::PLATFORM_FILE_WRITE; | |
31 using base::ReadPlatformFile; | |
32 using base::TaskRunner; | |
33 using base::Time; | |
34 using base::TruncatePlatformFile; | |
35 using base::WritePlatformFile; | |
36 | |
37 namespace { | |
38 | |
39 std::string GetFilenameForKeyAndIndex(const std::string& key, int index) { | |
40 const std::string sha_hash = base::SHA1HashString(key); | |
41 return StringPrintf("%02x%02x%02x%02x%02x_%1d", | |
42 implicit_cast<unsigned char>(sha_hash[0]), | |
43 implicit_cast<unsigned char>(sha_hash[1]), | |
44 implicit_cast<unsigned char>(sha_hash[2]), | |
45 implicit_cast<unsigned char>(sha_hash[3]), | |
46 implicit_cast<unsigned char>(sha_hash[4]), index); | |
47 } | |
48 | |
49 int32 DataSizeFromKeyAndFileSize(size_t key_size, int64 file_size) { | |
50 return file_size - key_size - sizeof(disk_cache::SimpleFileHeader); | |
rvargas (doing something else)
2013/02/13 01:48:46
There's a size mismatch here that should be handle
gavinp
2013/02/14 15:29:55
Done. I used std::numeric_limits<> to guard the ca
| |
51 } | |
52 | |
53 int64 FileOffsetFromDataOffset(size_t key_size, int offset) { | |
rvargas (doing something else)
2013/02/13 01:48:46
Do you expect to have large files?
gavinp
2013/02/14 15:29:55
No.
rvargas (doing something else)
2013/02/14 21:09:30
My point is that then you can return the size that
| |
54 const int64 headers_size = sizeof(disk_cache::SimpleFileHeader) + | |
55 key_size; | |
56 return headers_size + offset; | |
57 } | |
58 | |
59 } // namespace | |
60 | |
61 namespace disk_cache { | |
62 | |
63 // static | |
64 void SimpleSynchronousEntry::OpenEntry( | |
65 const FilePath& path, | |
66 const std::string& key, | |
67 const scoped_refptr<TaskRunner>& callback_runner, | |
68 const SynchronousEntryCallback& callback) { | |
69 SimpleSynchronousEntry* sync_entry = | |
70 new SimpleSynchronousEntry(callback_runner, path, key); | |
71 | |
72 if (!sync_entry->InitializeForOpen()) { | |
73 delete sync_entry; | |
74 sync_entry = NULL; | |
75 } | |
76 callback_runner->PostTask(FROM_HERE, | |
77 base::Bind(callback, sync_entry, | |
78 sync_entry ? net::OK : net::ERR_FAILED)); | |
rvargas (doing something else)
2013/02/13 01:48:46
Sounds simpler (on both sides) to remove the last
gavinp
2013/02/14 15:29:55
Done.
| |
79 } | |
80 | |
81 // static | |
82 void SimpleSynchronousEntry::CreateEntry( | |
83 const FilePath& path, | |
84 const std::string& key, | |
85 const scoped_refptr<TaskRunner>& callback_runner, | |
86 const SynchronousEntryCallback& callback) { | |
87 SimpleSynchronousEntry* sync_entry = | |
88 new SimpleSynchronousEntry(callback_runner, path, key); | |
89 | |
90 if (!sync_entry->InitializeForCreate()) { | |
91 delete sync_entry; | |
92 sync_entry = NULL; | |
93 } | |
94 callback_runner->PostTask(FROM_HERE, | |
95 base::Bind(callback, sync_entry, | |
96 sync_entry ? net::OK : net::ERR_FAILED)); | |
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 bool delete_result = | |
107 file_util::Delete(path.AppendASCII(GetFilenameForKeyAndIndex(key, i)), | |
108 false); | |
109 DCHECK(delete_result); | |
rvargas (doing something else)
2013/02/13 01:48:46
should not dcheck something coming from the OS
gavinp
2013/02/14 15:29:55
Done.
| |
110 } | |
111 if (!callback.is_null()) | |
112 callback_runner->PostTask(FROM_HERE, base::Bind(callback, net::OK)); | |
113 } | |
114 | |
115 void SimpleSynchronousEntry::DoomAndClose() { | |
116 scoped_refptr<TaskRunner> callback_runner = callback_runner_; | |
117 FilePath path = path_; | |
118 std::string key = key_; | |
119 | |
120 Close(); | |
121 // |this| is now deleted. | |
122 | |
123 DoomEntry(path, key, callback_runner, net::CompletionCallback()); | |
124 } | |
125 | |
126 void SimpleSynchronousEntry::Close() { | |
127 for (int i = 0; i < kIndexCount; ++i) { | |
128 bool result = ClosePlatformFile(files_[i]); | |
129 DCHECK(result); | |
130 } | |
131 delete this; | |
132 } | |
133 | |
134 void SimpleSynchronousEntry::ReadData( | |
135 int index, | |
136 int offset, | |
137 net::IOBuffer* buf, | |
138 int buf_len, | |
139 const SynchronousEntryCallback& callback) { | |
140 DCHECK(initialized_); | |
141 if (status_[index].mode != EntryStatus::ENTRY_READER) | |
rvargas (doing something else)
2013/02/13 01:48:46
What's the purpose of this?
gavinp
2013/02/14 15:29:55
Removed.
| |
142 status_[index].data_offset = 0; | |
143 DCHECK_EQ(status_[index].data_offset, offset); | |
144 status_[index].mode = EntryStatus::ENTRY_READER; | |
145 | |
146 int64 file_offset = FileOffsetFromDataOffset(key_.size(), offset); | |
147 int bytes_read = ReadPlatformFile(files_[index], file_offset, | |
148 buf->data(), buf_len); | |
149 if (bytes_read > 0) { | |
150 last_used_ = Time::Now(); | |
151 status_[index].data_offset += bytes_read; | |
152 } | |
153 int result = (bytes_read >= 0) ? bytes_read : net::ERR_FAILED; | |
154 callback_runner_->PostTask(FROM_HERE, base::Bind(callback, this, result)); | |
155 } | |
156 | |
157 void SimpleSynchronousEntry::WriteData( | |
158 int index, | |
159 int offset, | |
160 net::IOBuffer* buf, | |
161 int buf_len, | |
162 const SynchronousEntryCallback& callback, | |
163 bool truncate) { | |
164 DCHECK(initialized_); | |
165 if (status_[index].mode != EntryStatus::ENTRY_WRITER || | |
166 (truncate && offset == 0)) | |
167 status_[index].data_offset = 0; | |
168 DCHECK_EQ(status_[index].data_offset, offset); | |
169 status_[index].mode = EntryStatus::ENTRY_WRITER; | |
170 | |
171 int64 file_offset = FileOffsetFromDataOffset(key_.size(), offset); | |
172 if (buf_len > 0) { | |
173 if (WritePlatformFile(files_[index], file_offset, buf->data(), buf_len) != | |
174 buf_len) { | |
175 callback_runner_->PostTask(FROM_HERE, | |
176 base::Bind(callback, this, net::ERR_FAILED)); | |
177 return; | |
178 } | |
179 data_size_[index] = std::max(data_size_[index], offset + buf_len); | |
180 status_[index].data_offset += buf_len; | |
181 } | |
182 if (truncate) { | |
183 data_size_[index] = offset + buf_len; | |
184 if (!TruncatePlatformFile(files_[index], file_offset + buf_len)) { | |
185 callback_runner_->PostTask(FROM_HERE, | |
186 base::Bind(callback, this, net::ERR_FAILED)); | |
187 return; | |
188 } | |
189 } | |
190 last_modified_ = Time::Now(); | |
191 callback_runner_->PostTask(FROM_HERE, base::Bind(callback, this, buf_len)); | |
192 } | |
193 | |
194 SimpleSynchronousEntry::EntryStatus::EntryStatus() | |
195 : mode(ENTRY_UNINITIALIZED), | |
196 data_offset(0) { | |
197 } | |
198 | |
199 SimpleSynchronousEntry::SimpleSynchronousEntry( | |
200 const scoped_refptr<TaskRunner>& callback_runner, | |
201 const FilePath& path, | |
202 const std::string& key) : callback_runner_(callback_runner), | |
rvargas (doing something else)
2013/02/13 01:48:46
nit: initialization list on the next line
gavinp
2013/02/14 15:29:55
Done.
| |
203 path_(path), | |
204 key_(key), | |
205 initialized_(false) { | |
206 } | |
207 | |
208 SimpleSynchronousEntry::~SimpleSynchronousEntry() { | |
209 } | |
210 | |
211 bool SimpleSynchronousEntry::OpenOrCreateFiles(bool create) { | |
212 for (int i = 0; i < kIndexCount; ++i) { | |
213 FilePath filename = path_.AppendASCII(GetFilenameForKeyAndIndex(key_, i)); | |
214 int flags = PLATFORM_FILE_READ | PLATFORM_FILE_WRITE; | |
215 if (create) | |
216 flags |= PLATFORM_FILE_CREATE_ALWAYS; | |
rvargas (doing something else)
2013/02/13 01:48:46
overwrite would be an error
gavinp
2013/02/14 15:29:55
Done.
| |
217 else | |
218 flags |= PLATFORM_FILE_OPEN; | |
219 PlatformFileError error; | |
220 files_[i] = CreatePlatformFile(filename, flags, NULL, &error); | |
221 if (error != PLATFORM_FILE_OK) { | |
pasko-google - do not use
2013/02/13 11:36:24
please log the error here
gavinp
2013/02/14 15:29:55
Done.
| |
222 while (--i >= 0) { | |
223 bool did_close = ClosePlatformFile(files_[i]); | |
224 DCHECK(did_close); | |
225 } | |
226 return false; | |
227 } | |
228 } | |
229 | |
230 for (int i = 0; i < kIndexCount; ++i) { | |
231 PlatformFileInfo file_info; | |
232 CHECK(GetPlatformFileInfo(files_[i], &file_info)); | |
233 last_used_ = std::max(last_used_, file_info.last_accessed); | |
234 last_modified_ = std::max(last_modified_, file_info.last_modified); | |
235 data_size_[i] = DataSizeFromKeyAndFileSize(key_.size(), file_info.size); | |
236 } | |
237 | |
238 return true; | |
239 } | |
240 | |
241 bool SimpleSynchronousEntry::InitializeForOpen() { | |
242 DCHECK(!initialized_); | |
243 if (!OpenOrCreateFiles(false)) | |
244 return false; | |
245 | |
246 for (int i = 0; i < kIndexCount; ++i) { | |
247 SimpleFileHeader header; | |
248 if (ReadPlatformFile(files_[i], 0, reinterpret_cast<char*>(&header), | |
249 sizeof(header)) != sizeof(header)) { | |
250 return false; | |
251 } | |
252 | |
253 if (header.initial_magic_number != kSimpleInitialMagicNumber) { | |
254 // TODO(gavinp): This seems very bad; for now we log at WARNING, but we | |
255 // should give consideration to not saturating the log with these if that | |
256 // becomes a problem. | |
257 LOG(WARNING) << "Magic number did not match, saw " | |
258 << header.initial_magic_number << " expecting " | |
rvargas (doing something else)
2013/02/13 01:48:46
nit: consider removing the saw vs expected parts.
gavinp
2013/02/14 15:29:55
Done.
| |
259 << kSimpleInitialMagicNumber; | |
260 } | |
261 | |
262 if (header.version != kSimpleVersion) { | |
263 LOG(INFO) << "Unreadable version. Got " << header.version | |
rvargas (doing something else)
2013/02/13 01:48:46
nit: remove the variable parts?.
Shouldn't this be
gavinp
2013/02/14 15:29:55
Done.
| |
264 << " but can only read " << kSimpleVersion; | |
265 return false; | |
266 } | |
267 | |
268 char key[4096]; | |
269 DCHECK_LE(header.key_length, sizeof(key)); | |
rvargas (doing something else)
2013/02/13 01:48:46
this doesn't work (in general).
gavinp
2013/02/14 15:29:55
Now replaced with the thing that works in general.
| |
270 if (ReadPlatformFile(files_[i], sizeof(header), key, header.key_length) != | |
271 implicit_cast<int>(header.key_length)) { | |
rvargas (doing something else)
2013/02/13 01:48:46
why is key_length not an int?
gavinp
2013/02/14 15:29:55
Because there are no negative length keys.
rvargas (doing something else)
2013/02/14 21:09:30
That's not the principle that determines signed vs
gavinp
2013/02/15 16:04:01
Good point; I"d been assuming file formats were "d
| |
272 LOG(ERROR) << "Cannot read key from entry."; | |
rvargas (doing something else)
2013/02/13 01:48:46
nit: I would expect all failures from this method
gavinp
2013/02/14 15:29:55
Done.
| |
273 return false; | |
274 } | |
275 if (header.key_length != key_.size() || | |
276 std::memcmp(static_cast<const void*>(key_.data()), | |
277 static_cast<const void*>(key), | |
rvargas (doing something else)
2013/02/13 01:48:46
shouldn't need the casts here
gavinp
2013/02/14 15:29:55
Done.
| |
278 key_.size()) != 0) { | |
279 // TODO(gavinp): Since the way we use Entry SHA to name entries means this | |
280 // is expected to occur at some frequency, add unit_tests that this does | |
281 // is handled gracefully at higher levels. | |
282 LOG(INFO) << "Key mismatch on open, expecting " << key_ | |
283 << " but file stored " << std::string(key, header.key_length); | |
284 return false; | |
285 } | |
286 | |
287 if (base::Hash(key, header.key_length) != header.key_hash) { | |
288 LOG(ERROR) << "Hash mismatch on key."; | |
289 return false; | |
290 } | |
291 } | |
292 | |
293 initialized_ = true; | |
294 return true; | |
295 } | |
296 | |
297 bool SimpleSynchronousEntry::InitializeForCreate() { | |
298 DCHECK(!initialized_); | |
299 if (!OpenOrCreateFiles(true)) { | |
300 LOG(ERROR) << "Could not create platform files for key " << key_; | |
301 return false; | |
302 } | |
303 | |
304 for (int i = 0; i < kIndexCount; ++i) { | |
305 SimpleFileHeader header; | |
306 header.initial_magic_number = kSimpleInitialMagicNumber; | |
307 header.version = kSimpleVersion; | |
308 | |
309 header.key_length = key_.size(); | |
310 header.key_hash = base::Hash(key_); | |
311 | |
312 if (WritePlatformFile(files_[i], 0, reinterpret_cast<char*>(&header), | |
313 sizeof(header)) != sizeof(header)) { | |
314 // TODO(gavinp): Clean up created files. | |
315 LOG(WARNING) << "Could not write headers to new cache entry."; | |
316 return false; | |
317 } | |
318 | |
319 if (WritePlatformFile(files_[i], sizeof(header), key_.data(), | |
320 key_.size()) != implicit_cast<int>(key_.size())) { | |
321 // TODO(gavinp): Clean up created files. | |
322 LOG(WARNING) << "Could not write keys to new cache entry."; | |
323 return false; | |
324 } | |
325 } | |
326 | |
327 initialized_ = true; | |
328 return true; | |
329 } | |
330 | |
331 } // namespace disk_cache | |
OLD | NEW |