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 <functional> | |
10 #include <limits> | |
11 | |
12 #include "base/basictypes.h" | |
13 #include "base/compiler_specific.h" | |
14 #include "base/files/file_util.h" | |
15 #include "base/hash.h" | |
16 #include "base/location.h" | |
17 #include "base/numerics/safe_conversions.h" | |
18 #include "base/sha1.h" | |
19 #include "base/strings/stringprintf.h" | |
20 #include "net/base/io_buffer.h" | |
21 #include "net/base/net_errors.h" | |
22 #include "net/disk_cache/simple/simple_backend_version.h" | |
23 #include "net/disk_cache/simple/simple_histogram_macros.h" | |
24 #include "net/disk_cache/simple/simple_util.h" | |
25 #include "third_party/zlib/zlib.h" | |
26 | |
27 using base::File; | |
28 using base::FilePath; | |
29 using base::Time; | |
30 | |
31 namespace { | |
32 | |
33 // Used in histograms, please only add entries at the end. | |
34 enum OpenEntryResult { | |
35 OPEN_ENTRY_SUCCESS = 0, | |
36 OPEN_ENTRY_PLATFORM_FILE_ERROR = 1, | |
37 OPEN_ENTRY_CANT_READ_HEADER = 2, | |
38 OPEN_ENTRY_BAD_MAGIC_NUMBER = 3, | |
39 OPEN_ENTRY_BAD_VERSION = 4, | |
40 OPEN_ENTRY_CANT_READ_KEY = 5, | |
41 // OPEN_ENTRY_KEY_MISMATCH = 6, Deprecated. | |
42 OPEN_ENTRY_KEY_HASH_MISMATCH = 7, | |
43 OPEN_ENTRY_SPARSE_OPEN_FAILED = 8, | |
44 OPEN_ENTRY_MAX = 9, | |
45 }; | |
46 | |
47 // Used in histograms, please only add entries at the end. | |
48 enum WriteResult { | |
49 WRITE_RESULT_SUCCESS = 0, | |
50 WRITE_RESULT_PRETRUNCATE_FAILURE, | |
51 WRITE_RESULT_WRITE_FAILURE, | |
52 WRITE_RESULT_TRUNCATE_FAILURE, | |
53 WRITE_RESULT_LAZY_STREAM_ENTRY_DOOMED, | |
54 WRITE_RESULT_LAZY_CREATE_FAILURE, | |
55 WRITE_RESULT_LAZY_INITIALIZE_FAILURE, | |
56 WRITE_RESULT_MAX, | |
57 }; | |
58 | |
59 // Used in histograms, please only add entries at the end. | |
60 enum CheckEOFResult { | |
61 CHECK_EOF_RESULT_SUCCESS, | |
62 CHECK_EOF_RESULT_READ_FAILURE, | |
63 CHECK_EOF_RESULT_MAGIC_NUMBER_MISMATCH, | |
64 CHECK_EOF_RESULT_CRC_MISMATCH, | |
65 CHECK_EOF_RESULT_MAX, | |
66 }; | |
67 | |
68 // Used in histograms, please only add entries at the end. | |
69 enum CloseResult { | |
70 CLOSE_RESULT_SUCCESS, | |
71 CLOSE_RESULT_WRITE_FAILURE, | |
72 }; | |
73 | |
74 void RecordSyncOpenResult(net::CacheType cache_type, | |
75 OpenEntryResult result, | |
76 bool had_index) { | |
77 DCHECK_LT(result, OPEN_ENTRY_MAX); | |
78 SIMPLE_CACHE_UMA(ENUMERATION, | |
79 "SyncOpenResult", cache_type, result, OPEN_ENTRY_MAX); | |
80 if (had_index) { | |
81 SIMPLE_CACHE_UMA(ENUMERATION, | |
82 "SyncOpenResult_WithIndex", cache_type, | |
83 result, OPEN_ENTRY_MAX); | |
84 } else { | |
85 SIMPLE_CACHE_UMA(ENUMERATION, | |
86 "SyncOpenResult_WithoutIndex", cache_type, | |
87 result, OPEN_ENTRY_MAX); | |
88 } | |
89 } | |
90 | |
91 void RecordWriteResult(net::CacheType cache_type, WriteResult result) { | |
92 SIMPLE_CACHE_UMA(ENUMERATION, | |
93 "SyncWriteResult", cache_type, result, WRITE_RESULT_MAX); | |
94 } | |
95 | |
96 void RecordCheckEOFResult(net::CacheType cache_type, CheckEOFResult result) { | |
97 SIMPLE_CACHE_UMA(ENUMERATION, | |
98 "SyncCheckEOFResult", cache_type, | |
99 result, CHECK_EOF_RESULT_MAX); | |
100 } | |
101 | |
102 void RecordCloseResult(net::CacheType cache_type, CloseResult result) { | |
103 SIMPLE_CACHE_UMA(ENUMERATION, | |
104 "SyncCloseResult", cache_type, result, WRITE_RESULT_MAX); | |
105 } | |
106 | |
107 bool CanOmitEmptyFile(int file_index) { | |
108 DCHECK_GE(file_index, 0); | |
109 DCHECK_LT(file_index, disk_cache::kSimpleEntryFileCount); | |
110 return file_index == disk_cache::simple_util::GetFileIndexFromStreamIndex(2); | |
111 } | |
112 | |
113 } // namespace | |
114 | |
115 namespace disk_cache { | |
116 | |
117 using simple_util::GetEntryHashKey; | |
118 using simple_util::GetFilenameFromEntryHashAndFileIndex; | |
119 using simple_util::GetSparseFilenameFromEntryHash; | |
120 using simple_util::GetDataSizeFromKeyAndFileSize; | |
121 using simple_util::GetFileSizeFromKeyAndDataSize; | |
122 using simple_util::GetFileIndexFromStreamIndex; | |
123 | |
124 SimpleEntryStat::SimpleEntryStat(base::Time last_used, | |
125 base::Time last_modified, | |
126 const int32 data_size[], | |
127 const int32 sparse_data_size) | |
128 : last_used_(last_used), | |
129 last_modified_(last_modified), | |
130 sparse_data_size_(sparse_data_size) { | |
131 memcpy(data_size_, data_size, sizeof(data_size_)); | |
132 } | |
133 | |
134 int SimpleEntryStat::GetOffsetInFile(const std::string& key, | |
135 int offset, | |
136 int stream_index) const { | |
137 const size_t headers_size = sizeof(SimpleFileHeader) + key.size(); | |
138 const size_t additional_offset = | |
139 stream_index == 0 ? data_size_[1] + sizeof(SimpleFileEOF) : 0; | |
140 return headers_size + offset + additional_offset; | |
141 } | |
142 | |
143 int SimpleEntryStat::GetEOFOffsetInFile(const std::string& key, | |
144 int stream_index) const { | |
145 return GetOffsetInFile(key, data_size_[stream_index], stream_index); | |
146 } | |
147 | |
148 int SimpleEntryStat::GetLastEOFOffsetInFile(const std::string& key, | |
149 int stream_index) const { | |
150 const int file_index = GetFileIndexFromStreamIndex(stream_index); | |
151 const int eof_data_offset = | |
152 file_index == 0 ? data_size_[0] + data_size_[1] + sizeof(SimpleFileEOF) | |
153 : data_size_[2]; | |
154 return GetOffsetInFile(key, eof_data_offset, stream_index); | |
155 } | |
156 | |
157 int64 SimpleEntryStat::GetFileSize(const std::string& key, | |
158 int file_index) const { | |
159 const int32 total_data_size = | |
160 file_index == 0 ? data_size_[0] + data_size_[1] + sizeof(SimpleFileEOF) | |
161 : data_size_[2]; | |
162 return GetFileSizeFromKeyAndDataSize(key, total_data_size); | |
163 } | |
164 | |
165 SimpleEntryCreationResults::SimpleEntryCreationResults( | |
166 SimpleEntryStat entry_stat) | |
167 : sync_entry(NULL), | |
168 entry_stat(entry_stat), | |
169 stream_0_crc32(crc32(0, Z_NULL, 0)), | |
170 result(net::OK) { | |
171 } | |
172 | |
173 SimpleEntryCreationResults::~SimpleEntryCreationResults() { | |
174 } | |
175 | |
176 SimpleSynchronousEntry::CRCRecord::CRCRecord() : index(-1), | |
177 has_crc32(false), | |
178 data_crc32(0) { | |
179 } | |
180 | |
181 SimpleSynchronousEntry::CRCRecord::CRCRecord(int index_p, | |
182 bool has_crc32_p, | |
183 uint32 data_crc32_p) | |
184 : index(index_p), | |
185 has_crc32(has_crc32_p), | |
186 data_crc32(data_crc32_p) {} | |
187 | |
188 SimpleSynchronousEntry::EntryOperationData::EntryOperationData(int index_p, | |
189 int offset_p, | |
190 int buf_len_p) | |
191 : index(index_p), | |
192 offset(offset_p), | |
193 buf_len(buf_len_p) {} | |
194 | |
195 SimpleSynchronousEntry::EntryOperationData::EntryOperationData(int index_p, | |
196 int offset_p, | |
197 int buf_len_p, | |
198 bool truncate_p, | |
199 bool doomed_p) | |
200 : index(index_p), | |
201 offset(offset_p), | |
202 buf_len(buf_len_p), | |
203 truncate(truncate_p), | |
204 doomed(doomed_p) {} | |
205 | |
206 SimpleSynchronousEntry::EntryOperationData::EntryOperationData( | |
207 int64 sparse_offset_p, | |
208 int buf_len_p) | |
209 : sparse_offset(sparse_offset_p), | |
210 buf_len(buf_len_p) {} | |
211 | |
212 // static | |
213 void SimpleSynchronousEntry::OpenEntry( | |
214 net::CacheType cache_type, | |
215 const FilePath& path, | |
216 const uint64 entry_hash, | |
217 bool had_index, | |
218 SimpleEntryCreationResults *out_results) { | |
219 SimpleSynchronousEntry* sync_entry = | |
220 new SimpleSynchronousEntry(cache_type, path, "", entry_hash); | |
221 out_results->result = | |
222 sync_entry->InitializeForOpen(had_index, | |
223 &out_results->entry_stat, | |
224 &out_results->stream_0_data, | |
225 &out_results->stream_0_crc32); | |
226 if (out_results->result != net::OK) { | |
227 sync_entry->Doom(); | |
228 delete sync_entry; | |
229 out_results->sync_entry = NULL; | |
230 out_results->stream_0_data = NULL; | |
231 return; | |
232 } | |
233 out_results->sync_entry = sync_entry; | |
234 } | |
235 | |
236 // static | |
237 void SimpleSynchronousEntry::CreateEntry( | |
238 net::CacheType cache_type, | |
239 const FilePath& path, | |
240 const std::string& key, | |
241 const uint64 entry_hash, | |
242 bool had_index, | |
243 SimpleEntryCreationResults *out_results) { | |
244 DCHECK_EQ(entry_hash, GetEntryHashKey(key)); | |
245 SimpleSynchronousEntry* sync_entry = | |
246 new SimpleSynchronousEntry(cache_type, path, key, entry_hash); | |
247 out_results->result = sync_entry->InitializeForCreate( | |
248 had_index, &out_results->entry_stat); | |
249 if (out_results->result != net::OK) { | |
250 if (out_results->result != net::ERR_FILE_EXISTS) | |
251 sync_entry->Doom(); | |
252 delete sync_entry; | |
253 out_results->sync_entry = NULL; | |
254 return; | |
255 } | |
256 out_results->sync_entry = sync_entry; | |
257 } | |
258 | |
259 // static | |
260 int SimpleSynchronousEntry::DoomEntry( | |
261 const FilePath& path, | |
262 uint64 entry_hash) { | |
263 const bool deleted_well = DeleteFilesForEntryHash(path, entry_hash); | |
264 return deleted_well ? net::OK : net::ERR_FAILED; | |
265 } | |
266 | |
267 // static | |
268 int SimpleSynchronousEntry::DoomEntrySet( | |
269 const std::vector<uint64>* key_hashes, | |
270 const FilePath& path) { | |
271 const size_t did_delete_count = std::count_if( | |
272 key_hashes->begin(), key_hashes->end(), std::bind1st( | |
273 std::ptr_fun(SimpleSynchronousEntry::DeleteFilesForEntryHash), path)); | |
274 return (did_delete_count == key_hashes->size()) ? net::OK : net::ERR_FAILED; | |
275 } | |
276 | |
277 void SimpleSynchronousEntry::ReadData(const EntryOperationData& in_entry_op, | |
278 net::IOBuffer* out_buf, | |
279 uint32* out_crc32, | |
280 SimpleEntryStat* entry_stat, | |
281 int* out_result) const { | |
282 DCHECK(initialized_); | |
283 DCHECK_NE(0, in_entry_op.index); | |
284 const int64 file_offset = | |
285 entry_stat->GetOffsetInFile(key_, in_entry_op.offset, in_entry_op.index); | |
286 int file_index = GetFileIndexFromStreamIndex(in_entry_op.index); | |
287 // Zero-length reads and reads to the empty streams of omitted files should | |
288 // be handled in the SimpleEntryImpl. | |
289 DCHECK_GT(in_entry_op.buf_len, 0); | |
290 DCHECK(!empty_file_omitted_[file_index]); | |
291 File* file = const_cast<File*>(&files_[file_index]); | |
292 int bytes_read = | |
293 file->Read(file_offset, out_buf->data(), in_entry_op.buf_len); | |
294 if (bytes_read > 0) { | |
295 entry_stat->set_last_used(Time::Now()); | |
296 *out_crc32 = crc32(crc32(0L, Z_NULL, 0), | |
297 reinterpret_cast<const Bytef*>(out_buf->data()), | |
298 bytes_read); | |
299 } | |
300 if (bytes_read >= 0) { | |
301 *out_result = bytes_read; | |
302 } else { | |
303 *out_result = net::ERR_CACHE_READ_FAILURE; | |
304 Doom(); | |
305 } | |
306 } | |
307 | |
308 void SimpleSynchronousEntry::WriteData(const EntryOperationData& in_entry_op, | |
309 net::IOBuffer* in_buf, | |
310 SimpleEntryStat* out_entry_stat, | |
311 int* out_result) { | |
312 DCHECK(initialized_); | |
313 DCHECK_NE(0, in_entry_op.index); | |
314 int index = in_entry_op.index; | |
315 int file_index = GetFileIndexFromStreamIndex(index); | |
316 int offset = in_entry_op.offset; | |
317 int buf_len = in_entry_op.buf_len; | |
318 bool truncate = in_entry_op.truncate; | |
319 bool doomed = in_entry_op.doomed; | |
320 const int64 file_offset = out_entry_stat->GetOffsetInFile( | |
321 key_, in_entry_op.offset, in_entry_op.index); | |
322 bool extending_by_write = offset + buf_len > out_entry_stat->data_size(index); | |
323 | |
324 if (empty_file_omitted_[file_index]) { | |
325 // Don't create a new file if the entry has been doomed, to avoid it being | |
326 // mixed up with a newly-created entry with the same key. | |
327 if (doomed) { | |
328 DLOG(WARNING) << "Rejecting write to lazily omitted stream " | |
329 << in_entry_op.index << " of doomed cache entry."; | |
330 RecordWriteResult(cache_type_, WRITE_RESULT_LAZY_STREAM_ENTRY_DOOMED); | |
331 *out_result = net::ERR_CACHE_WRITE_FAILURE; | |
332 return; | |
333 } | |
334 File::Error error; | |
335 if (!MaybeCreateFile(file_index, FILE_REQUIRED, &error)) { | |
336 RecordWriteResult(cache_type_, WRITE_RESULT_LAZY_CREATE_FAILURE); | |
337 Doom(); | |
338 *out_result = net::ERR_CACHE_WRITE_FAILURE; | |
339 return; | |
340 } | |
341 CreateEntryResult result; | |
342 if (!InitializeCreatedFile(file_index, &result)) { | |
343 RecordWriteResult(cache_type_, WRITE_RESULT_LAZY_INITIALIZE_FAILURE); | |
344 Doom(); | |
345 *out_result = net::ERR_CACHE_WRITE_FAILURE; | |
346 return; | |
347 } | |
348 } | |
349 DCHECK(!empty_file_omitted_[file_index]); | |
350 | |
351 if (extending_by_write) { | |
352 // The EOF record and the eventual stream afterward need to be zeroed out. | |
353 const int64 file_eof_offset = | |
354 out_entry_stat->GetEOFOffsetInFile(key_, index); | |
355 if (!files_[file_index].SetLength(file_eof_offset)) { | |
356 RecordWriteResult(cache_type_, WRITE_RESULT_PRETRUNCATE_FAILURE); | |
357 Doom(); | |
358 *out_result = net::ERR_CACHE_WRITE_FAILURE; | |
359 return; | |
360 } | |
361 } | |
362 if (buf_len > 0) { | |
363 if (files_[file_index].Write(file_offset, in_buf->data(), buf_len) != | |
364 buf_len) { | |
365 RecordWriteResult(cache_type_, WRITE_RESULT_WRITE_FAILURE); | |
366 Doom(); | |
367 *out_result = net::ERR_CACHE_WRITE_FAILURE; | |
368 return; | |
369 } | |
370 } | |
371 if (!truncate && (buf_len > 0 || !extending_by_write)) { | |
372 out_entry_stat->set_data_size( | |
373 index, std::max(out_entry_stat->data_size(index), offset + buf_len)); | |
374 } else { | |
375 out_entry_stat->set_data_size(index, offset + buf_len); | |
376 int file_eof_offset = out_entry_stat->GetLastEOFOffsetInFile(key_, index); | |
377 if (!files_[file_index].SetLength(file_eof_offset)) { | |
378 RecordWriteResult(cache_type_, WRITE_RESULT_TRUNCATE_FAILURE); | |
379 Doom(); | |
380 *out_result = net::ERR_CACHE_WRITE_FAILURE; | |
381 return; | |
382 } | |
383 } | |
384 | |
385 RecordWriteResult(cache_type_, WRITE_RESULT_SUCCESS); | |
386 base::Time modification_time = Time::Now(); | |
387 out_entry_stat->set_last_used(modification_time); | |
388 out_entry_stat->set_last_modified(modification_time); | |
389 *out_result = buf_len; | |
390 } | |
391 | |
392 void SimpleSynchronousEntry::ReadSparseData( | |
393 const EntryOperationData& in_entry_op, | |
394 net::IOBuffer* out_buf, | |
395 base::Time* out_last_used, | |
396 int* out_result) { | |
397 DCHECK(initialized_); | |
398 int64 offset = in_entry_op.sparse_offset; | |
399 int buf_len = in_entry_op.buf_len; | |
400 | |
401 char* buf = out_buf->data(); | |
402 int read_so_far = 0; | |
403 | |
404 // Find the first sparse range at or after the requested offset. | |
405 SparseRangeIterator it = sparse_ranges_.lower_bound(offset); | |
406 | |
407 if (it != sparse_ranges_.begin()) { | |
408 // Hop back one range and read the one overlapping with the start. | |
409 --it; | |
410 SparseRange* found_range = &it->second; | |
411 DCHECK_EQ(it->first, found_range->offset); | |
412 if (found_range->offset + found_range->length > offset) { | |
413 DCHECK_GE(found_range->length, 0); | |
414 DCHECK_LE(found_range->length, kint32max); | |
415 DCHECK_GE(offset - found_range->offset, 0); | |
416 DCHECK_LE(offset - found_range->offset, kint32max); | |
417 int net_offset = static_cast<int>(offset - found_range->offset); | |
418 int range_len_after_offset = | |
419 static_cast<int>(found_range->length - net_offset); | |
420 DCHECK_GE(range_len_after_offset, 0); | |
421 | |
422 int len_to_read = std::min(buf_len, range_len_after_offset); | |
423 if (!ReadSparseRange(found_range, net_offset, len_to_read, buf)) { | |
424 *out_result = net::ERR_CACHE_READ_FAILURE; | |
425 return; | |
426 } | |
427 read_so_far += len_to_read; | |
428 } | |
429 ++it; | |
430 } | |
431 | |
432 // Keep reading until the buffer is full or there is not another contiguous | |
433 // range. | |
434 while (read_so_far < buf_len && | |
435 it != sparse_ranges_.end() && | |
436 it->second.offset == offset + read_so_far) { | |
437 SparseRange* found_range = &it->second; | |
438 DCHECK_EQ(it->first, found_range->offset); | |
439 int range_len = base::saturated_cast<int>(found_range->length); | |
440 int len_to_read = std::min(buf_len - read_so_far, range_len); | |
441 if (!ReadSparseRange(found_range, 0, len_to_read, buf + read_so_far)) { | |
442 *out_result = net::ERR_CACHE_READ_FAILURE; | |
443 return; | |
444 } | |
445 read_so_far += len_to_read; | |
446 ++it; | |
447 } | |
448 | |
449 *out_result = read_so_far; | |
450 } | |
451 | |
452 void SimpleSynchronousEntry::WriteSparseData( | |
453 const EntryOperationData& in_entry_op, | |
454 net::IOBuffer* in_buf, | |
455 uint64 max_sparse_data_size, | |
456 SimpleEntryStat* out_entry_stat, | |
457 int* out_result) { | |
458 DCHECK(initialized_); | |
459 int64 offset = in_entry_op.sparse_offset; | |
460 int buf_len = in_entry_op.buf_len; | |
461 | |
462 const char* buf = in_buf->data(); | |
463 int written_so_far = 0; | |
464 int appended_so_far = 0; | |
465 | |
466 if (!sparse_file_open() && !CreateSparseFile()) { | |
467 *out_result = net::ERR_CACHE_WRITE_FAILURE; | |
468 return; | |
469 } | |
470 | |
471 uint64 sparse_data_size = out_entry_stat->sparse_data_size(); | |
472 // This is a pessimistic estimate; it assumes the entire buffer is going to | |
473 // be appended as a new range, not written over existing ranges. | |
474 if (sparse_data_size + buf_len > max_sparse_data_size) { | |
475 DVLOG(1) << "Truncating sparse data file (" << sparse_data_size << " + " | |
476 << buf_len << " > " << max_sparse_data_size << ")"; | |
477 TruncateSparseFile(); | |
478 } | |
479 | |
480 SparseRangeIterator it = sparse_ranges_.lower_bound(offset); | |
481 | |
482 if (it != sparse_ranges_.begin()) { | |
483 --it; | |
484 SparseRange* found_range = &it->second; | |
485 if (found_range->offset + found_range->length > offset) { | |
486 DCHECK_GE(found_range->length, 0); | |
487 DCHECK_LE(found_range->length, kint32max); | |
488 DCHECK_GE(offset - found_range->offset, 0); | |
489 DCHECK_LE(offset - found_range->offset, kint32max); | |
490 int net_offset = static_cast<int>(offset - found_range->offset); | |
491 int range_len_after_offset = | |
492 static_cast<int>(found_range->length - net_offset); | |
493 DCHECK_GE(range_len_after_offset, 0); | |
494 | |
495 int len_to_write = std::min(buf_len, range_len_after_offset); | |
496 if (!WriteSparseRange(found_range, net_offset, len_to_write, buf)) { | |
497 *out_result = net::ERR_CACHE_WRITE_FAILURE; | |
498 return; | |
499 } | |
500 written_so_far += len_to_write; | |
501 } | |
502 ++it; | |
503 } | |
504 | |
505 while (written_so_far < buf_len && | |
506 it != sparse_ranges_.end() && | |
507 it->second.offset < offset + buf_len) { | |
508 SparseRange* found_range = &it->second; | |
509 if (offset + written_so_far < found_range->offset) { | |
510 int len_to_append = | |
511 static_cast<int>(found_range->offset - (offset + written_so_far)); | |
512 if (!AppendSparseRange(offset + written_so_far, | |
513 len_to_append, | |
514 buf + written_so_far)) { | |
515 *out_result = net::ERR_CACHE_WRITE_FAILURE; | |
516 return; | |
517 } | |
518 written_so_far += len_to_append; | |
519 appended_so_far += len_to_append; | |
520 } | |
521 int range_len = base::saturated_cast<int>(found_range->length); | |
522 int len_to_write = std::min(buf_len - written_so_far, range_len); | |
523 if (!WriteSparseRange(found_range, | |
524 0, | |
525 len_to_write, | |
526 buf + written_so_far)) { | |
527 *out_result = net::ERR_CACHE_WRITE_FAILURE; | |
528 return; | |
529 } | |
530 written_so_far += len_to_write; | |
531 ++it; | |
532 } | |
533 | |
534 if (written_so_far < buf_len) { | |
535 int len_to_append = buf_len - written_so_far; | |
536 if (!AppendSparseRange(offset + written_so_far, | |
537 len_to_append, | |
538 buf + written_so_far)) { | |
539 *out_result = net::ERR_CACHE_WRITE_FAILURE; | |
540 return; | |
541 } | |
542 written_so_far += len_to_append; | |
543 appended_so_far += len_to_append; | |
544 } | |
545 | |
546 DCHECK_EQ(buf_len, written_so_far); | |
547 | |
548 base::Time modification_time = Time::Now(); | |
549 out_entry_stat->set_last_used(modification_time); | |
550 out_entry_stat->set_last_modified(modification_time); | |
551 int32 old_sparse_data_size = out_entry_stat->sparse_data_size(); | |
552 out_entry_stat->set_sparse_data_size(old_sparse_data_size + appended_so_far); | |
553 *out_result = written_so_far; | |
554 } | |
555 | |
556 void SimpleSynchronousEntry::GetAvailableRange( | |
557 const EntryOperationData& in_entry_op, | |
558 int64* out_start, | |
559 int* out_result) { | |
560 DCHECK(initialized_); | |
561 int64 offset = in_entry_op.sparse_offset; | |
562 int len = in_entry_op.buf_len; | |
563 | |
564 SparseRangeIterator it = sparse_ranges_.lower_bound(offset); | |
565 | |
566 int64 start = offset; | |
567 int64 avail_so_far = 0; | |
568 | |
569 if (it != sparse_ranges_.end() && it->second.offset < offset + len) | |
570 start = it->second.offset; | |
571 | |
572 if ((it == sparse_ranges_.end() || it->second.offset > offset) && | |
573 it != sparse_ranges_.begin()) { | |
574 --it; | |
575 if (it->second.offset + it->second.length > offset) { | |
576 start = offset; | |
577 avail_so_far = (it->second.offset + it->second.length) - offset; | |
578 } | |
579 ++it; | |
580 } | |
581 | |
582 while (start + avail_so_far < offset + len && | |
583 it != sparse_ranges_.end() && | |
584 it->second.offset == start + avail_so_far) { | |
585 avail_so_far += it->second.length; | |
586 ++it; | |
587 } | |
588 | |
589 int64 len_from_start = len - (start - offset); | |
590 *out_start = start; | |
591 *out_result = static_cast<int>(std::min(avail_so_far, len_from_start)); | |
592 } | |
593 | |
594 void SimpleSynchronousEntry::CheckEOFRecord(int index, | |
595 const SimpleEntryStat& entry_stat, | |
596 uint32 expected_crc32, | |
597 int* out_result) const { | |
598 DCHECK(initialized_); | |
599 uint32 crc32; | |
600 bool has_crc32; | |
601 int stream_size; | |
602 *out_result = | |
603 GetEOFRecordData(index, entry_stat, &has_crc32, &crc32, &stream_size); | |
604 if (*out_result != net::OK) { | |
605 Doom(); | |
606 return; | |
607 } | |
608 if (has_crc32 && crc32 != expected_crc32) { | |
609 DVLOG(1) << "EOF record had bad crc."; | |
610 *out_result = net::ERR_CACHE_CHECKSUM_MISMATCH; | |
611 RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_CRC_MISMATCH); | |
612 Doom(); | |
613 return; | |
614 } | |
615 RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_SUCCESS); | |
616 } | |
617 | |
618 void SimpleSynchronousEntry::Close( | |
619 const SimpleEntryStat& entry_stat, | |
620 scoped_ptr<std::vector<CRCRecord> > crc32s_to_write, | |
621 net::GrowableIOBuffer* stream_0_data) { | |
622 DCHECK(stream_0_data); | |
623 // Write stream 0 data. | |
624 int stream_0_offset = entry_stat.GetOffsetInFile(key_, 0, 0); | |
625 if (files_[0].Write(stream_0_offset, stream_0_data->data(), | |
626 entry_stat.data_size(0)) != | |
627 entry_stat.data_size(0)) { | |
628 RecordCloseResult(cache_type_, CLOSE_RESULT_WRITE_FAILURE); | |
629 DVLOG(1) << "Could not write stream 0 data."; | |
630 Doom(); | |
631 } | |
632 | |
633 for (std::vector<CRCRecord>::const_iterator it = crc32s_to_write->begin(); | |
634 it != crc32s_to_write->end(); ++it) { | |
635 const int stream_index = it->index; | |
636 const int file_index = GetFileIndexFromStreamIndex(stream_index); | |
637 if (empty_file_omitted_[file_index]) | |
638 continue; | |
639 | |
640 SimpleFileEOF eof_record; | |
641 eof_record.stream_size = entry_stat.data_size(stream_index); | |
642 eof_record.final_magic_number = kSimpleFinalMagicNumber; | |
643 eof_record.flags = 0; | |
644 if (it->has_crc32) | |
645 eof_record.flags |= SimpleFileEOF::FLAG_HAS_CRC32; | |
646 eof_record.data_crc32 = it->data_crc32; | |
647 int eof_offset = entry_stat.GetEOFOffsetInFile(key_, stream_index); | |
648 // If stream 0 changed size, the file needs to be resized, otherwise the | |
649 // next open will yield wrong stream sizes. On stream 1 and stream 2 proper | |
650 // resizing of the file is handled in SimpleSynchronousEntry::WriteData(). | |
651 if (stream_index == 0 && | |
652 !files_[file_index].SetLength(eof_offset)) { | |
653 RecordCloseResult(cache_type_, CLOSE_RESULT_WRITE_FAILURE); | |
654 DVLOG(1) << "Could not truncate stream 0 file."; | |
655 Doom(); | |
656 break; | |
657 } | |
658 if (files_[file_index].Write(eof_offset, | |
659 reinterpret_cast<const char*>(&eof_record), | |
660 sizeof(eof_record)) != | |
661 sizeof(eof_record)) { | |
662 RecordCloseResult(cache_type_, CLOSE_RESULT_WRITE_FAILURE); | |
663 DVLOG(1) << "Could not write eof record."; | |
664 Doom(); | |
665 break; | |
666 } | |
667 } | |
668 for (int i = 0; i < kSimpleEntryFileCount; ++i) { | |
669 if (empty_file_omitted_[i]) | |
670 continue; | |
671 | |
672 files_[i].Close(); | |
673 const int64 file_size = entry_stat.GetFileSize(key_, i); | |
674 SIMPLE_CACHE_UMA(CUSTOM_COUNTS, | |
675 "LastClusterSize", cache_type_, | |
676 file_size % 4096, 0, 4097, 50); | |
677 const int64 cluster_loss = file_size % 4096 ? 4096 - file_size % 4096 : 0; | |
678 SIMPLE_CACHE_UMA(PERCENTAGE, | |
679 "LastClusterLossPercent", cache_type_, | |
680 static_cast<base::HistogramBase::Sample>( | |
681 cluster_loss * 100 / (cluster_loss + file_size))); | |
682 } | |
683 | |
684 if (sparse_file_open()) | |
685 sparse_file_.Close(); | |
686 | |
687 if (files_created_) { | |
688 const int stream2_file_index = GetFileIndexFromStreamIndex(2); | |
689 SIMPLE_CACHE_UMA(BOOLEAN, "EntryCreatedAndStream2Omitted", cache_type_, | |
690 empty_file_omitted_[stream2_file_index]); | |
691 } | |
692 RecordCloseResult(cache_type_, CLOSE_RESULT_SUCCESS); | |
693 have_open_files_ = false; | |
694 delete this; | |
695 } | |
696 | |
697 SimpleSynchronousEntry::SimpleSynchronousEntry(net::CacheType cache_type, | |
698 const FilePath& path, | |
699 const std::string& key, | |
700 const uint64 entry_hash) | |
701 : cache_type_(cache_type), | |
702 path_(path), | |
703 entry_hash_(entry_hash), | |
704 key_(key), | |
705 have_open_files_(false), | |
706 initialized_(false) { | |
707 for (int i = 0; i < kSimpleEntryFileCount; ++i) | |
708 empty_file_omitted_[i] = false; | |
709 } | |
710 | |
711 SimpleSynchronousEntry::~SimpleSynchronousEntry() { | |
712 DCHECK(!(have_open_files_ && initialized_)); | |
713 if (have_open_files_) | |
714 CloseFiles(); | |
715 } | |
716 | |
717 bool SimpleSynchronousEntry::MaybeOpenFile( | |
718 int file_index, | |
719 File::Error* out_error) { | |
720 DCHECK(out_error); | |
721 | |
722 FilePath filename = GetFilenameFromFileIndex(file_index); | |
723 int flags = File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE | | |
724 File::FLAG_SHARE_DELETE; | |
725 files_[file_index].Initialize(filename, flags); | |
726 *out_error = files_[file_index].error_details(); | |
727 | |
728 if (CanOmitEmptyFile(file_index) && !files_[file_index].IsValid() && | |
729 *out_error == File::FILE_ERROR_NOT_FOUND) { | |
730 empty_file_omitted_[file_index] = true; | |
731 return true; | |
732 } | |
733 | |
734 return files_[file_index].IsValid(); | |
735 } | |
736 | |
737 bool SimpleSynchronousEntry::MaybeCreateFile( | |
738 int file_index, | |
739 FileRequired file_required, | |
740 File::Error* out_error) { | |
741 DCHECK(out_error); | |
742 | |
743 if (CanOmitEmptyFile(file_index) && file_required == FILE_NOT_REQUIRED) { | |
744 empty_file_omitted_[file_index] = true; | |
745 return true; | |
746 } | |
747 | |
748 FilePath filename = GetFilenameFromFileIndex(file_index); | |
749 int flags = File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE | | |
750 File::FLAG_SHARE_DELETE; | |
751 files_[file_index].Initialize(filename, flags); | |
752 *out_error = files_[file_index].error_details(); | |
753 | |
754 empty_file_omitted_[file_index] = false; | |
755 | |
756 return files_[file_index].IsValid(); | |
757 } | |
758 | |
759 bool SimpleSynchronousEntry::OpenFiles( | |
760 bool had_index, | |
761 SimpleEntryStat* out_entry_stat) { | |
762 for (int i = 0; i < kSimpleEntryFileCount; ++i) { | |
763 File::Error error; | |
764 if (!MaybeOpenFile(i, &error)) { | |
765 // TODO(ttuttle,gavinp): Remove one each of these triplets of histograms. | |
766 // We can calculate the third as the sum or difference of the other two. | |
767 RecordSyncOpenResult( | |
768 cache_type_, OPEN_ENTRY_PLATFORM_FILE_ERROR, had_index); | |
769 SIMPLE_CACHE_UMA(ENUMERATION, | |
770 "SyncOpenPlatformFileError", cache_type_, | |
771 -error, -base::File::FILE_ERROR_MAX); | |
772 if (had_index) { | |
773 SIMPLE_CACHE_UMA(ENUMERATION, | |
774 "SyncOpenPlatformFileError_WithIndex", cache_type_, | |
775 -error, -base::File::FILE_ERROR_MAX); | |
776 } else { | |
777 SIMPLE_CACHE_UMA(ENUMERATION, | |
778 "SyncOpenPlatformFileError_WithoutIndex", | |
779 cache_type_, | |
780 -error, -base::File::FILE_ERROR_MAX); | |
781 } | |
782 while (--i >= 0) | |
783 CloseFile(i); | |
784 return false; | |
785 } | |
786 } | |
787 | |
788 have_open_files_ = true; | |
789 | |
790 base::TimeDelta entry_age = base::Time::Now() - base::Time::UnixEpoch(); | |
791 for (int i = 0; i < kSimpleEntryFileCount; ++i) { | |
792 if (empty_file_omitted_[i]) { | |
793 out_entry_stat->set_data_size(i + 1, 0); | |
794 continue; | |
795 } | |
796 | |
797 File::Info file_info; | |
798 bool success = files_[i].GetInfo(&file_info); | |
799 base::Time file_last_modified; | |
800 if (!success) { | |
801 DLOG(WARNING) << "Could not get platform file info."; | |
802 continue; | |
803 } | |
804 out_entry_stat->set_last_used(file_info.last_accessed); | |
805 if (simple_util::GetMTime(path_, &file_last_modified)) | |
806 out_entry_stat->set_last_modified(file_last_modified); | |
807 else | |
808 out_entry_stat->set_last_modified(file_info.last_modified); | |
809 | |
810 base::TimeDelta stream_age = | |
811 base::Time::Now() - out_entry_stat->last_modified(); | |
812 if (stream_age < entry_age) | |
813 entry_age = stream_age; | |
814 | |
815 // Two things prevent from knowing the right values for |data_size|: | |
816 // 1) The key is not known, hence its length is unknown. | |
817 // 2) Stream 0 and stream 1 are in the same file, and the exact size for | |
818 // each will only be known when reading the EOF record for stream 0. | |
819 // | |
820 // The size for file 0 and 1 is temporarily kept in | |
821 // |data_size(1)| and |data_size(2)| respectively. Reading the key in | |
822 // InitializeForOpen yields the data size for each file. In the case of | |
823 // file hash_1, this is the total size of stream 2, and is assigned to | |
824 // data_size(2). In the case of file 0, it is the combined size of stream | |
825 // 0, stream 1 and one EOF record. The exact distribution of sizes between | |
826 // stream 1 and stream 0 is only determined after reading the EOF record | |
827 // for stream 0 in ReadAndValidateStream0. | |
828 out_entry_stat->set_data_size(i + 1, static_cast<int>(file_info.size)); | |
829 } | |
830 SIMPLE_CACHE_UMA(CUSTOM_COUNTS, | |
831 "SyncOpenEntryAge", cache_type_, | |
832 entry_age.InHours(), 1, 1000, 50); | |
833 | |
834 files_created_ = false; | |
835 | |
836 return true; | |
837 } | |
838 | |
839 bool SimpleSynchronousEntry::CreateFiles( | |
840 bool had_index, | |
841 SimpleEntryStat* out_entry_stat) { | |
842 for (int i = 0; i < kSimpleEntryFileCount; ++i) { | |
843 File::Error error; | |
844 if (!MaybeCreateFile(i, FILE_NOT_REQUIRED, &error)) { | |
845 // TODO(ttuttle,gavinp): Remove one each of these triplets of histograms. | |
846 // We can calculate the third as the sum or difference of the other two. | |
847 RecordSyncCreateResult(CREATE_ENTRY_PLATFORM_FILE_ERROR, had_index); | |
848 SIMPLE_CACHE_UMA(ENUMERATION, | |
849 "SyncCreatePlatformFileError", cache_type_, | |
850 -error, -base::File::FILE_ERROR_MAX); | |
851 if (had_index) { | |
852 SIMPLE_CACHE_UMA(ENUMERATION, | |
853 "SyncCreatePlatformFileError_WithIndex", cache_type_, | |
854 -error, -base::File::FILE_ERROR_MAX); | |
855 } else { | |
856 SIMPLE_CACHE_UMA(ENUMERATION, | |
857 "SyncCreatePlatformFileError_WithoutIndex", | |
858 cache_type_, | |
859 -error, -base::File::FILE_ERROR_MAX); | |
860 } | |
861 while (--i >= 0) | |
862 CloseFile(i); | |
863 return false; | |
864 } | |
865 } | |
866 | |
867 have_open_files_ = true; | |
868 | |
869 base::Time creation_time = Time::Now(); | |
870 out_entry_stat->set_last_modified(creation_time); | |
871 out_entry_stat->set_last_used(creation_time); | |
872 for (int i = 0; i < kSimpleEntryStreamCount; ++i) | |
873 out_entry_stat->set_data_size(i, 0); | |
874 | |
875 files_created_ = true; | |
876 | |
877 return true; | |
878 } | |
879 | |
880 void SimpleSynchronousEntry::CloseFile(int index) { | |
881 if (empty_file_omitted_[index]) { | |
882 empty_file_omitted_[index] = false; | |
883 } else { | |
884 DCHECK(files_[index].IsValid()); | |
885 files_[index].Close(); | |
886 } | |
887 | |
888 if (sparse_file_open()) | |
889 CloseSparseFile(); | |
890 } | |
891 | |
892 void SimpleSynchronousEntry::CloseFiles() { | |
893 for (int i = 0; i < kSimpleEntryFileCount; ++i) | |
894 CloseFile(i); | |
895 } | |
896 | |
897 int SimpleSynchronousEntry::InitializeForOpen( | |
898 bool had_index, | |
899 SimpleEntryStat* out_entry_stat, | |
900 scoped_refptr<net::GrowableIOBuffer>* stream_0_data, | |
901 uint32* out_stream_0_crc32) { | |
902 DCHECK(!initialized_); | |
903 if (!OpenFiles(had_index, out_entry_stat)) { | |
904 DLOG(WARNING) << "Could not open platform files for entry."; | |
905 return net::ERR_FAILED; | |
906 } | |
907 for (int i = 0; i < kSimpleEntryFileCount; ++i) { | |
908 if (empty_file_omitted_[i]) | |
909 continue; | |
910 | |
911 SimpleFileHeader header; | |
912 int header_read_result = | |
913 files_[i].Read(0, reinterpret_cast<char*>(&header), sizeof(header)); | |
914 if (header_read_result != sizeof(header)) { | |
915 DLOG(WARNING) << "Cannot read header from entry."; | |
916 RecordSyncOpenResult(cache_type_, OPEN_ENTRY_CANT_READ_HEADER, had_index); | |
917 return net::ERR_FAILED; | |
918 } | |
919 | |
920 if (header.initial_magic_number != kSimpleInitialMagicNumber) { | |
921 // TODO(gavinp): This seems very bad; for now we log at WARNING, but we | |
922 // should give consideration to not saturating the log with these if that | |
923 // becomes a problem. | |
924 DLOG(WARNING) << "Magic number did not match."; | |
925 RecordSyncOpenResult(cache_type_, OPEN_ENTRY_BAD_MAGIC_NUMBER, had_index); | |
926 return net::ERR_FAILED; | |
927 } | |
928 | |
929 if (header.version != kSimpleEntryVersionOnDisk) { | |
930 DLOG(WARNING) << "Unreadable version."; | |
931 RecordSyncOpenResult(cache_type_, OPEN_ENTRY_BAD_VERSION, had_index); | |
932 return net::ERR_FAILED; | |
933 } | |
934 | |
935 scoped_ptr<char[]> key(new char[header.key_length]); | |
936 int key_read_result = files_[i].Read(sizeof(header), key.get(), | |
937 header.key_length); | |
938 if (key_read_result != implicit_cast<int>(header.key_length)) { | |
939 DLOG(WARNING) << "Cannot read key from entry."; | |
940 RecordSyncOpenResult(cache_type_, OPEN_ENTRY_CANT_READ_KEY, had_index); | |
941 return net::ERR_FAILED; | |
942 } | |
943 | |
944 key_ = std::string(key.get(), header.key_length); | |
945 if (i == 0) { | |
946 // File size for stream 0 has been stored temporarily in data_size[1]. | |
947 int total_data_size = | |
948 GetDataSizeFromKeyAndFileSize(key_, out_entry_stat->data_size(1)); | |
949 int ret_value_stream_0 = ReadAndValidateStream0( | |
950 total_data_size, out_entry_stat, stream_0_data, out_stream_0_crc32); | |
951 if (ret_value_stream_0 != net::OK) | |
952 return ret_value_stream_0; | |
953 } else { | |
954 out_entry_stat->set_data_size( | |
955 2, GetDataSizeFromKeyAndFileSize(key_, out_entry_stat->data_size(2))); | |
956 if (out_entry_stat->data_size(2) < 0) { | |
957 DLOG(WARNING) << "Stream 2 file is too small."; | |
958 return net::ERR_FAILED; | |
959 } | |
960 } | |
961 | |
962 if (base::Hash(key.get(), header.key_length) != header.key_hash) { | |
963 DLOG(WARNING) << "Hash mismatch on key."; | |
964 RecordSyncOpenResult( | |
965 cache_type_, OPEN_ENTRY_KEY_HASH_MISMATCH, had_index); | |
966 return net::ERR_FAILED; | |
967 } | |
968 } | |
969 | |
970 int32 sparse_data_size = 0; | |
971 if (!OpenSparseFileIfExists(&sparse_data_size)) { | |
972 RecordSyncOpenResult( | |
973 cache_type_, OPEN_ENTRY_SPARSE_OPEN_FAILED, had_index); | |
974 return net::ERR_FAILED; | |
975 } | |
976 out_entry_stat->set_sparse_data_size(sparse_data_size); | |
977 | |
978 bool removed_stream2 = false; | |
979 const int stream2_file_index = GetFileIndexFromStreamIndex(2); | |
980 DCHECK(CanOmitEmptyFile(stream2_file_index)); | |
981 if (!empty_file_omitted_[stream2_file_index] && | |
982 out_entry_stat->data_size(2) == 0) { | |
983 DVLOG(1) << "Removing empty stream 2 file."; | |
984 CloseFile(stream2_file_index); | |
985 DeleteFileForEntryHash(path_, entry_hash_, stream2_file_index); | |
986 empty_file_omitted_[stream2_file_index] = true; | |
987 removed_stream2 = true; | |
988 } | |
989 | |
990 SIMPLE_CACHE_UMA(BOOLEAN, "EntryOpenedAndStream2Removed", cache_type_, | |
991 removed_stream2); | |
992 | |
993 RecordSyncOpenResult(cache_type_, OPEN_ENTRY_SUCCESS, had_index); | |
994 initialized_ = true; | |
995 return net::OK; | |
996 } | |
997 | |
998 bool SimpleSynchronousEntry::InitializeCreatedFile( | |
999 int file_index, | |
1000 CreateEntryResult* out_result) { | |
1001 SimpleFileHeader header; | |
1002 header.initial_magic_number = kSimpleInitialMagicNumber; | |
1003 header.version = kSimpleEntryVersionOnDisk; | |
1004 | |
1005 header.key_length = key_.size(); | |
1006 header.key_hash = base::Hash(key_); | |
1007 | |
1008 int bytes_written = files_[file_index].Write( | |
1009 0, reinterpret_cast<char*>(&header), sizeof(header)); | |
1010 if (bytes_written != sizeof(header)) { | |
1011 *out_result = CREATE_ENTRY_CANT_WRITE_HEADER; | |
1012 return false; | |
1013 } | |
1014 | |
1015 bytes_written = files_[file_index].Write(sizeof(header), key_.data(), | |
1016 key_.size()); | |
1017 if (bytes_written != implicit_cast<int>(key_.size())) { | |
1018 *out_result = CREATE_ENTRY_CANT_WRITE_KEY; | |
1019 return false; | |
1020 } | |
1021 | |
1022 return true; | |
1023 } | |
1024 | |
1025 int SimpleSynchronousEntry::InitializeForCreate( | |
1026 bool had_index, | |
1027 SimpleEntryStat* out_entry_stat) { | |
1028 DCHECK(!initialized_); | |
1029 if (!CreateFiles(had_index, out_entry_stat)) { | |
1030 DLOG(WARNING) << "Could not create platform files."; | |
1031 return net::ERR_FILE_EXISTS; | |
1032 } | |
1033 for (int i = 0; i < kSimpleEntryFileCount; ++i) { | |
1034 if (empty_file_omitted_[i]) | |
1035 continue; | |
1036 | |
1037 CreateEntryResult result; | |
1038 if (!InitializeCreatedFile(i, &result)) { | |
1039 RecordSyncCreateResult(result, had_index); | |
1040 return net::ERR_FAILED; | |
1041 } | |
1042 } | |
1043 RecordSyncCreateResult(CREATE_ENTRY_SUCCESS, had_index); | |
1044 initialized_ = true; | |
1045 return net::OK; | |
1046 } | |
1047 | |
1048 int SimpleSynchronousEntry::ReadAndValidateStream0( | |
1049 int total_data_size, | |
1050 SimpleEntryStat* out_entry_stat, | |
1051 scoped_refptr<net::GrowableIOBuffer>* stream_0_data, | |
1052 uint32* out_stream_0_crc32) const { | |
1053 // Temporarily assign all the data size to stream 1 in order to read the | |
1054 // EOF record for stream 0, which contains the size of stream 0. | |
1055 out_entry_stat->set_data_size(0, 0); | |
1056 out_entry_stat->set_data_size(1, total_data_size - sizeof(SimpleFileEOF)); | |
1057 | |
1058 bool has_crc32; | |
1059 uint32 read_crc32; | |
1060 int stream_0_size; | |
1061 int ret_value_crc32 = GetEOFRecordData( | |
1062 0, *out_entry_stat, &has_crc32, &read_crc32, &stream_0_size); | |
1063 if (ret_value_crc32 != net::OK) | |
1064 return ret_value_crc32; | |
1065 | |
1066 if (stream_0_size > out_entry_stat->data_size(1)) | |
1067 return net::ERR_FAILED; | |
1068 | |
1069 // These are the real values of data size. | |
1070 out_entry_stat->set_data_size(0, stream_0_size); | |
1071 out_entry_stat->set_data_size( | |
1072 1, out_entry_stat->data_size(1) - stream_0_size); | |
1073 | |
1074 // Put stream 0 data in memory. | |
1075 *stream_0_data = new net::GrowableIOBuffer(); | |
1076 (*stream_0_data)->SetCapacity(stream_0_size); | |
1077 int file_offset = out_entry_stat->GetOffsetInFile(key_, 0, 0); | |
1078 File* file = const_cast<File*>(&files_[0]); | |
1079 int bytes_read = | |
1080 file->Read(file_offset, (*stream_0_data)->data(), stream_0_size); | |
1081 if (bytes_read != stream_0_size) | |
1082 return net::ERR_FAILED; | |
1083 | |
1084 // Check the CRC32. | |
1085 uint32 expected_crc32 = | |
1086 stream_0_size == 0 | |
1087 ? crc32(0, Z_NULL, 0) | |
1088 : crc32(crc32(0, Z_NULL, 0), | |
1089 reinterpret_cast<const Bytef*>((*stream_0_data)->data()), | |
1090 stream_0_size); | |
1091 if (has_crc32 && read_crc32 != expected_crc32) { | |
1092 DVLOG(1) << "EOF record had bad crc."; | |
1093 RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_CRC_MISMATCH); | |
1094 return net::ERR_FAILED; | |
1095 } | |
1096 *out_stream_0_crc32 = expected_crc32; | |
1097 RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_SUCCESS); | |
1098 return net::OK; | |
1099 } | |
1100 | |
1101 int SimpleSynchronousEntry::GetEOFRecordData(int index, | |
1102 const SimpleEntryStat& entry_stat, | |
1103 bool* out_has_crc32, | |
1104 uint32* out_crc32, | |
1105 int* out_data_size) const { | |
1106 SimpleFileEOF eof_record; | |
1107 int file_offset = entry_stat.GetEOFOffsetInFile(key_, index); | |
1108 int file_index = GetFileIndexFromStreamIndex(index); | |
1109 File* file = const_cast<File*>(&files_[file_index]); | |
1110 if (file->Read(file_offset, reinterpret_cast<char*>(&eof_record), | |
1111 sizeof(eof_record)) != | |
1112 sizeof(eof_record)) { | |
1113 RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_READ_FAILURE); | |
1114 return net::ERR_CACHE_CHECKSUM_READ_FAILURE; | |
1115 } | |
1116 | |
1117 if (eof_record.final_magic_number != kSimpleFinalMagicNumber) { | |
1118 RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_MAGIC_NUMBER_MISMATCH); | |
1119 DVLOG(1) << "EOF record had bad magic number."; | |
1120 return net::ERR_CACHE_CHECKSUM_READ_FAILURE; | |
1121 } | |
1122 | |
1123 *out_has_crc32 = (eof_record.flags & SimpleFileEOF::FLAG_HAS_CRC32) == | |
1124 SimpleFileEOF::FLAG_HAS_CRC32; | |
1125 *out_crc32 = eof_record.data_crc32; | |
1126 *out_data_size = eof_record.stream_size; | |
1127 SIMPLE_CACHE_UMA(BOOLEAN, "SyncCheckEOFHasCrc", cache_type_, *out_has_crc32); | |
1128 return net::OK; | |
1129 } | |
1130 | |
1131 void SimpleSynchronousEntry::Doom() const { | |
1132 DeleteFilesForEntryHash(path_, entry_hash_); | |
1133 } | |
1134 | |
1135 // static | |
1136 bool SimpleSynchronousEntry::DeleteFileForEntryHash( | |
1137 const FilePath& path, | |
1138 const uint64 entry_hash, | |
1139 const int file_index) { | |
1140 FilePath to_delete = path.AppendASCII( | |
1141 GetFilenameFromEntryHashAndFileIndex(entry_hash, file_index)); | |
1142 return simple_util::SimpleCacheDeleteFile(to_delete); | |
1143 } | |
1144 | |
1145 // static | |
1146 bool SimpleSynchronousEntry::DeleteFilesForEntryHash( | |
1147 const FilePath& path, | |
1148 const uint64 entry_hash) { | |
1149 bool result = true; | |
1150 for (int i = 0; i < kSimpleEntryFileCount; ++i) { | |
1151 if (!DeleteFileForEntryHash(path, entry_hash, i) && !CanOmitEmptyFile(i)) | |
1152 result = false; | |
1153 } | |
1154 FilePath to_delete = path.AppendASCII( | |
1155 GetSparseFilenameFromEntryHash(entry_hash)); | |
1156 simple_util::SimpleCacheDeleteFile(to_delete); | |
1157 return result; | |
1158 } | |
1159 | |
1160 void SimpleSynchronousEntry::RecordSyncCreateResult(CreateEntryResult result, | |
1161 bool had_index) { | |
1162 DCHECK_LT(result, CREATE_ENTRY_MAX); | |
1163 SIMPLE_CACHE_UMA(ENUMERATION, | |
1164 "SyncCreateResult", cache_type_, result, CREATE_ENTRY_MAX); | |
1165 if (had_index) { | |
1166 SIMPLE_CACHE_UMA(ENUMERATION, | |
1167 "SyncCreateResult_WithIndex", cache_type_, | |
1168 result, CREATE_ENTRY_MAX); | |
1169 } else { | |
1170 SIMPLE_CACHE_UMA(ENUMERATION, | |
1171 "SyncCreateResult_WithoutIndex", cache_type_, | |
1172 result, CREATE_ENTRY_MAX); | |
1173 } | |
1174 } | |
1175 | |
1176 FilePath SimpleSynchronousEntry::GetFilenameFromFileIndex(int file_index) { | |
1177 return path_.AppendASCII( | |
1178 GetFilenameFromEntryHashAndFileIndex(entry_hash_, file_index)); | |
1179 } | |
1180 | |
1181 bool SimpleSynchronousEntry::OpenSparseFileIfExists( | |
1182 int32* out_sparse_data_size) { | |
1183 DCHECK(!sparse_file_open()); | |
1184 | |
1185 FilePath filename = path_.AppendASCII( | |
1186 GetSparseFilenameFromEntryHash(entry_hash_)); | |
1187 int flags = File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE | | |
1188 File::FLAG_SHARE_DELETE; | |
1189 sparse_file_.Initialize(filename, flags); | |
1190 if (sparse_file_.IsValid()) | |
1191 return ScanSparseFile(out_sparse_data_size); | |
1192 | |
1193 return sparse_file_.error_details() == File::FILE_ERROR_NOT_FOUND; | |
1194 } | |
1195 | |
1196 bool SimpleSynchronousEntry::CreateSparseFile() { | |
1197 DCHECK(!sparse_file_open()); | |
1198 | |
1199 FilePath filename = path_.AppendASCII( | |
1200 GetSparseFilenameFromEntryHash(entry_hash_)); | |
1201 int flags = File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE | | |
1202 File::FLAG_SHARE_DELETE; | |
1203 sparse_file_.Initialize(filename, flags); | |
1204 if (!sparse_file_.IsValid()) | |
1205 return false; | |
1206 | |
1207 return InitializeSparseFile(); | |
1208 } | |
1209 | |
1210 void SimpleSynchronousEntry::CloseSparseFile() { | |
1211 DCHECK(sparse_file_open()); | |
1212 sparse_file_.Close(); | |
1213 } | |
1214 | |
1215 bool SimpleSynchronousEntry::TruncateSparseFile() { | |
1216 DCHECK(sparse_file_open()); | |
1217 | |
1218 int64 header_and_key_length = sizeof(SimpleFileHeader) + key_.size(); | |
1219 if (!sparse_file_.SetLength(header_and_key_length)) { | |
1220 DLOG(WARNING) << "Could not truncate sparse file"; | |
1221 return false; | |
1222 } | |
1223 | |
1224 sparse_ranges_.clear(); | |
1225 | |
1226 return true; | |
1227 } | |
1228 | |
1229 bool SimpleSynchronousEntry::InitializeSparseFile() { | |
1230 DCHECK(sparse_file_open()); | |
1231 | |
1232 SimpleFileHeader header; | |
1233 header.initial_magic_number = kSimpleInitialMagicNumber; | |
1234 header.version = kSimpleVersion; | |
1235 header.key_length = key_.size(); | |
1236 header.key_hash = base::Hash(key_); | |
1237 | |
1238 int header_write_result = | |
1239 sparse_file_.Write(0, reinterpret_cast<char*>(&header), sizeof(header)); | |
1240 if (header_write_result != sizeof(header)) { | |
1241 DLOG(WARNING) << "Could not write sparse file header"; | |
1242 return false; | |
1243 } | |
1244 | |
1245 int key_write_result = sparse_file_.Write(sizeof(header), key_.data(), | |
1246 key_.size()); | |
1247 if (key_write_result != implicit_cast<int>(key_.size())) { | |
1248 DLOG(WARNING) << "Could not write sparse file key"; | |
1249 return false; | |
1250 } | |
1251 | |
1252 sparse_ranges_.clear(); | |
1253 sparse_tail_offset_ = sizeof(header) + key_.size(); | |
1254 | |
1255 return true; | |
1256 } | |
1257 | |
1258 bool SimpleSynchronousEntry::ScanSparseFile(int32* out_sparse_data_size) { | |
1259 DCHECK(sparse_file_open()); | |
1260 | |
1261 int64 sparse_data_size = 0; | |
1262 | |
1263 SimpleFileHeader header; | |
1264 int header_read_result = | |
1265 sparse_file_.Read(0, reinterpret_cast<char*>(&header), sizeof(header)); | |
1266 if (header_read_result != sizeof(header)) { | |
1267 DLOG(WARNING) << "Could not read header from sparse file."; | |
1268 return false; | |
1269 } | |
1270 | |
1271 if (header.initial_magic_number != kSimpleInitialMagicNumber) { | |
1272 DLOG(WARNING) << "Sparse file magic number did not match."; | |
1273 return false; | |
1274 } | |
1275 | |
1276 if (header.version != kSimpleVersion) { | |
1277 DLOG(WARNING) << "Sparse file unreadable version."; | |
1278 return false; | |
1279 } | |
1280 | |
1281 sparse_ranges_.clear(); | |
1282 | |
1283 int64 range_header_offset = sizeof(header) + key_.size(); | |
1284 while (1) { | |
1285 SimpleFileSparseRangeHeader range_header; | |
1286 int range_header_read_result = | |
1287 sparse_file_.Read(range_header_offset, | |
1288 reinterpret_cast<char*>(&range_header), | |
1289 sizeof(range_header)); | |
1290 if (range_header_read_result == 0) | |
1291 break; | |
1292 if (range_header_read_result != sizeof(range_header)) { | |
1293 DLOG(WARNING) << "Could not read sparse range header."; | |
1294 return false; | |
1295 } | |
1296 | |
1297 if (range_header.sparse_range_magic_number != | |
1298 kSimpleSparseRangeMagicNumber) { | |
1299 DLOG(WARNING) << "Invalid sparse range header magic number."; | |
1300 return false; | |
1301 } | |
1302 | |
1303 SparseRange range; | |
1304 range.offset = range_header.offset; | |
1305 range.length = range_header.length; | |
1306 range.data_crc32 = range_header.data_crc32; | |
1307 range.file_offset = range_header_offset + sizeof(range_header); | |
1308 sparse_ranges_.insert(std::make_pair(range.offset, range)); | |
1309 | |
1310 range_header_offset += sizeof(range_header) + range.length; | |
1311 | |
1312 DCHECK_GE(sparse_data_size + range.length, sparse_data_size); | |
1313 sparse_data_size += range.length; | |
1314 } | |
1315 | |
1316 *out_sparse_data_size = static_cast<int32>(sparse_data_size); | |
1317 sparse_tail_offset_ = range_header_offset; | |
1318 | |
1319 return true; | |
1320 } | |
1321 | |
1322 bool SimpleSynchronousEntry::ReadSparseRange(const SparseRange* range, | |
1323 int offset, int len, char* buf) { | |
1324 DCHECK(range); | |
1325 DCHECK(buf); | |
1326 DCHECK_LE(offset, range->length); | |
1327 DCHECK_LE(offset + len, range->length); | |
1328 | |
1329 int bytes_read = sparse_file_.Read(range->file_offset + offset, buf, len); | |
1330 if (bytes_read < len) { | |
1331 DLOG(WARNING) << "Could not read sparse range."; | |
1332 return false; | |
1333 } | |
1334 | |
1335 // If we read the whole range and we have a crc32, check it. | |
1336 if (offset == 0 && len == range->length && range->data_crc32 != 0) { | |
1337 uint32 actual_crc32 = crc32(crc32(0L, Z_NULL, 0), | |
1338 reinterpret_cast<const Bytef*>(buf), | |
1339 len); | |
1340 if (actual_crc32 != range->data_crc32) { | |
1341 DLOG(WARNING) << "Sparse range crc32 mismatch."; | |
1342 return false; | |
1343 } | |
1344 } | |
1345 // TODO(ttuttle): Incremental crc32 calculation? | |
1346 | |
1347 return true; | |
1348 } | |
1349 | |
1350 bool SimpleSynchronousEntry::WriteSparseRange(SparseRange* range, | |
1351 int offset, int len, | |
1352 const char* buf) { | |
1353 DCHECK(range); | |
1354 DCHECK(buf); | |
1355 DCHECK_LE(offset, range->length); | |
1356 DCHECK_LE(offset + len, range->length); | |
1357 | |
1358 uint32 new_crc32 = 0; | |
1359 if (offset == 0 && len == range->length) { | |
1360 new_crc32 = crc32(crc32(0L, Z_NULL, 0), | |
1361 reinterpret_cast<const Bytef*>(buf), | |
1362 len); | |
1363 } | |
1364 | |
1365 if (new_crc32 != range->data_crc32) { | |
1366 range->data_crc32 = new_crc32; | |
1367 | |
1368 SimpleFileSparseRangeHeader header; | |
1369 header.sparse_range_magic_number = kSimpleSparseRangeMagicNumber; | |
1370 header.offset = range->offset; | |
1371 header.length = range->length; | |
1372 header.data_crc32 = range->data_crc32; | |
1373 | |
1374 int bytes_written = sparse_file_.Write(range->file_offset - sizeof(header), | |
1375 reinterpret_cast<char*>(&header), | |
1376 sizeof(header)); | |
1377 if (bytes_written != implicit_cast<int>(sizeof(header))) { | |
1378 DLOG(WARNING) << "Could not rewrite sparse range header."; | |
1379 return false; | |
1380 } | |
1381 } | |
1382 | |
1383 int bytes_written = sparse_file_.Write(range->file_offset + offset, buf, len); | |
1384 if (bytes_written < len) { | |
1385 DLOG(WARNING) << "Could not write sparse range."; | |
1386 return false; | |
1387 } | |
1388 | |
1389 return true; | |
1390 } | |
1391 | |
1392 bool SimpleSynchronousEntry::AppendSparseRange(int64 offset, | |
1393 int len, | |
1394 const char* buf) { | |
1395 DCHECK_GE(offset, 0); | |
1396 DCHECK_GT(len, 0); | |
1397 DCHECK(buf); | |
1398 | |
1399 uint32 data_crc32 = crc32(crc32(0L, Z_NULL, 0), | |
1400 reinterpret_cast<const Bytef*>(buf), | |
1401 len); | |
1402 | |
1403 SimpleFileSparseRangeHeader header; | |
1404 header.sparse_range_magic_number = kSimpleSparseRangeMagicNumber; | |
1405 header.offset = offset; | |
1406 header.length = len; | |
1407 header.data_crc32 = data_crc32; | |
1408 | |
1409 int bytes_written = sparse_file_.Write(sparse_tail_offset_, | |
1410 reinterpret_cast<char*>(&header), | |
1411 sizeof(header)); | |
1412 if (bytes_written != implicit_cast<int>(sizeof(header))) { | |
1413 DLOG(WARNING) << "Could not append sparse range header."; | |
1414 return false; | |
1415 } | |
1416 sparse_tail_offset_ += bytes_written; | |
1417 | |
1418 bytes_written = sparse_file_.Write(sparse_tail_offset_, buf, len); | |
1419 if (bytes_written < len) { | |
1420 DLOG(WARNING) << "Could not append sparse range data."; | |
1421 return false; | |
1422 } | |
1423 int64 data_file_offset = sparse_tail_offset_; | |
1424 sparse_tail_offset_ += bytes_written; | |
1425 | |
1426 SparseRange range; | |
1427 range.offset = offset; | |
1428 range.length = len; | |
1429 range.data_crc32 = data_crc32; | |
1430 range.file_offset = data_file_offset; | |
1431 sparse_ranges_.insert(std::make_pair(offset, range)); | |
1432 | |
1433 return true; | |
1434 } | |
1435 | |
1436 } // namespace disk_cache | |
OLD | NEW |