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