• 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::File;
27 using base::FilePath;
28 using base::Time;
29 
30 namespace {
31 
32 // Used in histograms, please only add entries at the end.
33 enum OpenEntryResult {
34   OPEN_ENTRY_SUCCESS = 0,
35   OPEN_ENTRY_PLATFORM_FILE_ERROR = 1,
36   OPEN_ENTRY_CANT_READ_HEADER = 2,
37   OPEN_ENTRY_BAD_MAGIC_NUMBER = 3,
38   OPEN_ENTRY_BAD_VERSION = 4,
39   OPEN_ENTRY_CANT_READ_KEY = 5,
40   // OPEN_ENTRY_KEY_MISMATCH = 6, Deprecated.
41   OPEN_ENTRY_KEY_HASH_MISMATCH = 7,
42   OPEN_ENTRY_SPARSE_OPEN_FAILED = 8,
43   OPEN_ENTRY_MAX = 9,
44 };
45 
46 // Used in histograms, please only add entries at the end.
47 enum WriteResult {
48   WRITE_RESULT_SUCCESS = 0,
49   WRITE_RESULT_PRETRUNCATE_FAILURE,
50   WRITE_RESULT_WRITE_FAILURE,
51   WRITE_RESULT_TRUNCATE_FAILURE,
52   WRITE_RESULT_LAZY_STREAM_ENTRY_DOOMED,
53   WRITE_RESULT_LAZY_CREATE_FAILURE,
54   WRITE_RESULT_LAZY_INITIALIZE_FAILURE,
55   WRITE_RESULT_MAX,
56 };
57 
58 // Used in histograms, please only add entries at the end.
59 enum CheckEOFResult {
60   CHECK_EOF_RESULT_SUCCESS,
61   CHECK_EOF_RESULT_READ_FAILURE,
62   CHECK_EOF_RESULT_MAGIC_NUMBER_MISMATCH,
63   CHECK_EOF_RESULT_CRC_MISMATCH,
64   CHECK_EOF_RESULT_MAX,
65 };
66 
67 // Used in histograms, please only add entries at the end.
68 enum CloseResult {
69   CLOSE_RESULT_SUCCESS,
70   CLOSE_RESULT_WRITE_FAILURE,
71 };
72 
RecordSyncOpenResult(net::CacheType cache_type,OpenEntryResult result,bool had_index)73 void RecordSyncOpenResult(net::CacheType cache_type,
74                           OpenEntryResult result,
75                           bool had_index) {
76   DCHECK_GT(OPEN_ENTRY_MAX, result);
77   SIMPLE_CACHE_UMA(ENUMERATION,
78                    "SyncOpenResult", cache_type, result, OPEN_ENTRY_MAX);
79   if (had_index) {
80     SIMPLE_CACHE_UMA(ENUMERATION,
81                      "SyncOpenResult_WithIndex", cache_type,
82                      result, OPEN_ENTRY_MAX);
83   } else {
84     SIMPLE_CACHE_UMA(ENUMERATION,
85                      "SyncOpenResult_WithoutIndex", cache_type,
86                      result, OPEN_ENTRY_MAX);
87   }
88 }
89 
RecordWriteResult(net::CacheType cache_type,WriteResult result)90 void RecordWriteResult(net::CacheType cache_type, WriteResult result) {
91   SIMPLE_CACHE_UMA(ENUMERATION,
92                    "SyncWriteResult", cache_type, result, WRITE_RESULT_MAX);
93 }
94 
RecordCheckEOFResult(net::CacheType cache_type,CheckEOFResult result)95 void RecordCheckEOFResult(net::CacheType cache_type, CheckEOFResult result) {
96   SIMPLE_CACHE_UMA(ENUMERATION,
97                    "SyncCheckEOFResult", cache_type,
98                    result, CHECK_EOF_RESULT_MAX);
99 }
100 
RecordCloseResult(net::CacheType cache_type,CloseResult result)101 void RecordCloseResult(net::CacheType cache_type, CloseResult result) {
102   SIMPLE_CACHE_UMA(ENUMERATION,
103                    "SyncCloseResult", cache_type, result, WRITE_RESULT_MAX);
104 }
105 
CanOmitEmptyFile(int file_index)106 bool CanOmitEmptyFile(int file_index) {
107   DCHECK_LE(0, file_index);
108   DCHECK_GT(disk_cache::kSimpleEntryFileCount, file_index);
109   return file_index == disk_cache::simple_util::GetFileIndexFromStreamIndex(2);
110 }
111 
112 }  // namespace
113 
114 namespace disk_cache {
115 
116 using simple_util::GetEntryHashKey;
117 using simple_util::GetFilenameFromEntryHashAndFileIndex;
118 using simple_util::GetSparseFilenameFromEntryHash;
119 using simple_util::GetDataSizeFromKeyAndFileSize;
120 using simple_util::GetFileSizeFromKeyAndDataSize;
121 using simple_util::GetFileIndexFromStreamIndex;
122 
SimpleEntryStat(base::Time last_used,base::Time last_modified,const int32 data_size[],const int32 sparse_data_size)123 SimpleEntryStat::SimpleEntryStat(base::Time last_used,
124                                  base::Time last_modified,
125                                  const int32 data_size[],
126                                  const int32 sparse_data_size)
127     : last_used_(last_used),
128       last_modified_(last_modified),
129       sparse_data_size_(sparse_data_size) {
130   memcpy(data_size_, data_size, sizeof(data_size_));
131 }
132 
GetOffsetInFile(const std::string & key,int offset,int stream_index) const133 int SimpleEntryStat::GetOffsetInFile(const std::string& key,
134                                      int offset,
135                                      int stream_index) const {
136   const int64 headers_size = sizeof(SimpleFileHeader) + key.size();
137   const int64 additional_offset =
138       stream_index == 0 ? data_size_[1] + sizeof(SimpleFileEOF) : 0;
139   return headers_size + offset + additional_offset;
140 }
141 
GetEOFOffsetInFile(const std::string & key,int stream_index) const142 int SimpleEntryStat::GetEOFOffsetInFile(const std::string& key,
143                                         int stream_index) const {
144   return GetOffsetInFile(key, data_size_[stream_index], stream_index);
145 }
146 
GetLastEOFOffsetInFile(const std::string & key,int stream_index) const147 int SimpleEntryStat::GetLastEOFOffsetInFile(const std::string& key,
148                                             int stream_index) const {
149   const int file_index = GetFileIndexFromStreamIndex(stream_index);
150   const int eof_data_offset =
151       file_index == 0 ? data_size_[0] + data_size_[1] + sizeof(SimpleFileEOF)
152                       : data_size_[2];
153   return GetOffsetInFile(key, eof_data_offset, stream_index);
154 }
155 
GetFileSize(const std::string & key,int file_index) const156 int SimpleEntryStat::GetFileSize(const std::string& key, int file_index) const {
157   const int total_data_size =
158       file_index == 0 ? data_size_[0] + data_size_[1] + sizeof(SimpleFileEOF)
159                       : data_size_[2];
160   return GetFileSizeFromKeyAndDataSize(key, total_data_size);
161 }
162 
SimpleEntryCreationResults(SimpleEntryStat entry_stat)163 SimpleEntryCreationResults::SimpleEntryCreationResults(
164     SimpleEntryStat entry_stat)
165     : sync_entry(NULL),
166       entry_stat(entry_stat),
167       stream_0_crc32(crc32(0, Z_NULL, 0)),
168       result(net::OK) {
169 }
170 
~SimpleEntryCreationResults()171 SimpleEntryCreationResults::~SimpleEntryCreationResults() {
172 }
173 
CRCRecord()174 SimpleSynchronousEntry::CRCRecord::CRCRecord() : index(-1),
175                                                  has_crc32(false),
176                                                  data_crc32(0) {
177 }
178 
CRCRecord(int index_p,bool has_crc32_p,uint32 data_crc32_p)179 SimpleSynchronousEntry::CRCRecord::CRCRecord(int index_p,
180                                              bool has_crc32_p,
181                                              uint32 data_crc32_p)
182     : index(index_p),
183       has_crc32(has_crc32_p),
184       data_crc32(data_crc32_p) {}
185 
EntryOperationData(int index_p,int offset_p,int buf_len_p)186 SimpleSynchronousEntry::EntryOperationData::EntryOperationData(int index_p,
187                                                                int offset_p,
188                                                                int buf_len_p)
189     : index(index_p),
190       offset(offset_p),
191       buf_len(buf_len_p) {}
192 
EntryOperationData(int index_p,int offset_p,int buf_len_p,bool truncate_p,bool doomed_p)193 SimpleSynchronousEntry::EntryOperationData::EntryOperationData(int index_p,
194                                                                int offset_p,
195                                                                int buf_len_p,
196                                                                bool truncate_p,
197                                                                bool doomed_p)
198     : index(index_p),
199       offset(offset_p),
200       buf_len(buf_len_p),
201       truncate(truncate_p),
202       doomed(doomed_p) {}
203 
EntryOperationData(int64 sparse_offset_p,int buf_len_p)204 SimpleSynchronousEntry::EntryOperationData::EntryOperationData(
205     int64 sparse_offset_p,
206     int buf_len_p)
207     : sparse_offset(sparse_offset_p),
208       buf_len(buf_len_p) {}
209 
210 // static
OpenEntry(net::CacheType cache_type,const FilePath & path,const uint64 entry_hash,bool had_index,SimpleEntryCreationResults * out_results)211 void SimpleSynchronousEntry::OpenEntry(
212     net::CacheType cache_type,
213     const FilePath& path,
214     const uint64 entry_hash,
215     bool had_index,
216     SimpleEntryCreationResults *out_results) {
217   SimpleSynchronousEntry* sync_entry =
218       new SimpleSynchronousEntry(cache_type, path, "", entry_hash);
219   out_results->result =
220       sync_entry->InitializeForOpen(had_index,
221                                     &out_results->entry_stat,
222                                     &out_results->stream_0_data,
223                                     &out_results->stream_0_crc32);
224   if (out_results->result != net::OK) {
225     sync_entry->Doom();
226     delete sync_entry;
227     out_results->sync_entry = NULL;
228     out_results->stream_0_data = NULL;
229     return;
230   }
231   out_results->sync_entry = sync_entry;
232 }
233 
234 // static
CreateEntry(net::CacheType cache_type,const FilePath & path,const std::string & key,const uint64 entry_hash,bool had_index,SimpleEntryCreationResults * out_results)235 void SimpleSynchronousEntry::CreateEntry(
236     net::CacheType cache_type,
237     const FilePath& path,
238     const std::string& key,
239     const uint64 entry_hash,
240     bool had_index,
241     SimpleEntryCreationResults *out_results) {
242   DCHECK_EQ(entry_hash, GetEntryHashKey(key));
243   SimpleSynchronousEntry* sync_entry =
244       new SimpleSynchronousEntry(cache_type, path, key, entry_hash);
245   out_results->result = sync_entry->InitializeForCreate(
246       had_index, &out_results->entry_stat);
247   if (out_results->result != net::OK) {
248     if (out_results->result != net::ERR_FILE_EXISTS)
249       sync_entry->Doom();
250     delete sync_entry;
251     out_results->sync_entry = NULL;
252     return;
253   }
254   out_results->sync_entry = sync_entry;
255 }
256 
257 // static
DoomEntry(const FilePath & path,uint64 entry_hash)258 int SimpleSynchronousEntry::DoomEntry(
259     const FilePath& path,
260     uint64 entry_hash) {
261   const bool deleted_well = DeleteFilesForEntryHash(path, entry_hash);
262   return deleted_well ? net::OK : net::ERR_FAILED;
263 }
264 
265 // static
DoomEntrySet(const std::vector<uint64> * key_hashes,const FilePath & path)266 int SimpleSynchronousEntry::DoomEntrySet(
267     const std::vector<uint64>* key_hashes,
268     const FilePath& path) {
269   const size_t did_delete_count = std::count_if(
270       key_hashes->begin(), key_hashes->end(), std::bind1st(
271           std::ptr_fun(SimpleSynchronousEntry::DeleteFilesForEntryHash), path));
272   return (did_delete_count == key_hashes->size()) ? net::OK : net::ERR_FAILED;
273 }
274 
ReadData(const EntryOperationData & in_entry_op,net::IOBuffer * out_buf,uint32 * out_crc32,SimpleEntryStat * entry_stat,int * out_result) const275 void SimpleSynchronousEntry::ReadData(const EntryOperationData& in_entry_op,
276                                       net::IOBuffer* out_buf,
277                                       uint32* out_crc32,
278                                       SimpleEntryStat* entry_stat,
279                                       int* out_result) const {
280   DCHECK(initialized_);
281   DCHECK_NE(0, in_entry_op.index);
282   const int64 file_offset =
283       entry_stat->GetOffsetInFile(key_, in_entry_op.offset, in_entry_op.index);
284   int file_index = GetFileIndexFromStreamIndex(in_entry_op.index);
285   // Zero-length reads and reads to the empty streams of omitted files should
286   // be handled in the SimpleEntryImpl.
287   DCHECK_LT(0, in_entry_op.buf_len);
288   DCHECK(!empty_file_omitted_[file_index]);
289   File* file = const_cast<File*>(&files_[file_index]);
290   int bytes_read =
291       file->Read(file_offset, out_buf->data(), in_entry_op.buf_len);
292   if (bytes_read > 0) {
293     entry_stat->set_last_used(Time::Now());
294     *out_crc32 = crc32(crc32(0L, Z_NULL, 0),
295                        reinterpret_cast<const Bytef*>(out_buf->data()),
296                        bytes_read);
297   }
298   if (bytes_read >= 0) {
299     *out_result = bytes_read;
300   } else {
301     *out_result = net::ERR_CACHE_READ_FAILURE;
302     Doom();
303   }
304 }
305 
WriteData(const EntryOperationData & in_entry_op,net::IOBuffer * in_buf,SimpleEntryStat * out_entry_stat,int * out_result)306 void SimpleSynchronousEntry::WriteData(const EntryOperationData& in_entry_op,
307                                        net::IOBuffer* in_buf,
308                                        SimpleEntryStat* out_entry_stat,
309                                        int* out_result) {
310   DCHECK(initialized_);
311   DCHECK_NE(0, in_entry_op.index);
312   int index = in_entry_op.index;
313   int file_index = GetFileIndexFromStreamIndex(index);
314   int offset = in_entry_op.offset;
315   int buf_len = in_entry_op.buf_len;
316   bool truncate = in_entry_op.truncate;
317   bool doomed = in_entry_op.doomed;
318   const int64 file_offset = out_entry_stat->GetOffsetInFile(
319       key_, in_entry_op.offset, in_entry_op.index);
320   bool extending_by_write = offset + buf_len > out_entry_stat->data_size(index);
321 
322   if (empty_file_omitted_[file_index]) {
323     // Don't create a new file if the entry has been doomed, to avoid it being
324     // mixed up with a newly-created entry with the same key.
325     if (doomed) {
326       DLOG(WARNING) << "Rejecting write to lazily omitted stream "
327                     << in_entry_op.index << " of doomed cache entry.";
328       RecordWriteResult(cache_type_, WRITE_RESULT_LAZY_STREAM_ENTRY_DOOMED);
329       *out_result = net::ERR_CACHE_WRITE_FAILURE;
330       return;
331     }
332     File::Error error;
333     if (!MaybeCreateFile(file_index, FILE_REQUIRED, &error)) {
334       RecordWriteResult(cache_type_, WRITE_RESULT_LAZY_CREATE_FAILURE);
335       Doom();
336       *out_result = net::ERR_CACHE_WRITE_FAILURE;
337       return;
338     }
339     CreateEntryResult result;
340     if (!InitializeCreatedFile(file_index, &result)) {
341       RecordWriteResult(cache_type_, WRITE_RESULT_LAZY_INITIALIZE_FAILURE);
342       Doom();
343       *out_result = net::ERR_CACHE_WRITE_FAILURE;
344       return;
345     }
346   }
347   DCHECK(!empty_file_omitted_[file_index]);
348 
349   if (extending_by_write) {
350     // The EOF record and the eventual stream afterward need to be zeroed out.
351     const int64 file_eof_offset =
352         out_entry_stat->GetEOFOffsetInFile(key_, index);
353     if (!files_[file_index].SetLength(file_eof_offset)) {
354       RecordWriteResult(cache_type_, WRITE_RESULT_PRETRUNCATE_FAILURE);
355       Doom();
356       *out_result = net::ERR_CACHE_WRITE_FAILURE;
357       return;
358     }
359   }
360   if (buf_len > 0) {
361     if (files_[file_index].Write(file_offset, in_buf->data(), buf_len) !=
362         buf_len) {
363       RecordWriteResult(cache_type_, WRITE_RESULT_WRITE_FAILURE);
364       Doom();
365       *out_result = net::ERR_CACHE_WRITE_FAILURE;
366       return;
367     }
368   }
369   if (!truncate && (buf_len > 0 || !extending_by_write)) {
370     out_entry_stat->set_data_size(
371         index, std::max(out_entry_stat->data_size(index), offset + buf_len));
372   } else {
373     out_entry_stat->set_data_size(index, offset + buf_len);
374     int file_eof_offset = out_entry_stat->GetLastEOFOffsetInFile(key_, index);
375     if (!files_[file_index].SetLength(file_eof_offset)) {
376       RecordWriteResult(cache_type_, WRITE_RESULT_TRUNCATE_FAILURE);
377       Doom();
378       *out_result = net::ERR_CACHE_WRITE_FAILURE;
379       return;
380     }
381   }
382 
383   RecordWriteResult(cache_type_, WRITE_RESULT_SUCCESS);
384   base::Time modification_time = Time::Now();
385   out_entry_stat->set_last_used(modification_time);
386   out_entry_stat->set_last_modified(modification_time);
387   *out_result = buf_len;
388 }
389 
ReadSparseData(const EntryOperationData & in_entry_op,net::IOBuffer * out_buf,base::Time * out_last_used,int * out_result)390 void SimpleSynchronousEntry::ReadSparseData(
391     const EntryOperationData& in_entry_op,
392     net::IOBuffer* out_buf,
393     base::Time* out_last_used,
394     int* out_result) {
395   DCHECK(initialized_);
396   int64 offset = in_entry_op.sparse_offset;
397   int buf_len = in_entry_op.buf_len;
398 
399   char* buf = out_buf->data();
400   int read_so_far = 0;
401 
402   // Find the first sparse range at or after the requested offset.
403   SparseRangeIterator it = sparse_ranges_.lower_bound(offset);
404 
405   if (it != sparse_ranges_.begin()) {
406     // Hop back one range and read the one overlapping with the start.
407     --it;
408     SparseRange* found_range = &it->second;
409     DCHECK_EQ(it->first, found_range->offset);
410     if (found_range->offset + found_range->length > offset) {
411       DCHECK_LE(0, found_range->length);
412       DCHECK_GE(kint32max, found_range->length);
413       DCHECK_LE(0, offset - found_range->offset);
414       DCHECK_GE(kint32max, offset - found_range->offset);
415       int range_len_after_offset = found_range->length -
416                                    (offset - found_range->offset);
417       DCHECK_LE(0, range_len_after_offset);
418 
419       int len_to_read = std::min(buf_len, range_len_after_offset);
420       if (!ReadSparseRange(found_range,
421                            offset - found_range->offset,
422                            len_to_read,
423                            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 = (found_range->length > kint32max) ?
440                     kint32max : found_range->length;
441     int len_to_read = std::min(buf_len - read_so_far, range_len);
442     if (!ReadSparseRange(found_range, 0, len_to_read, buf + read_so_far)) {
443       *out_result = net::ERR_CACHE_READ_FAILURE;
444       return;
445     }
446     read_so_far += len_to_read;
447     ++it;
448   }
449 
450   *out_result = read_so_far;
451 }
452 
WriteSparseData(const EntryOperationData & in_entry_op,net::IOBuffer * in_buf,int64 max_sparse_data_size,SimpleEntryStat * out_entry_stat,int * out_result)453 void SimpleSynchronousEntry::WriteSparseData(
454     const EntryOperationData& in_entry_op,
455     net::IOBuffer* in_buf,
456     int64 max_sparse_data_size,
457     SimpleEntryStat* out_entry_stat,
458     int* out_result) {
459   DCHECK(initialized_);
460   int64 offset = in_entry_op.sparse_offset;
461   int buf_len = in_entry_op.buf_len;
462 
463   const char* buf = in_buf->data();
464   int written_so_far = 0;
465   int appended_so_far = 0;
466 
467   if (!sparse_file_open() && !CreateSparseFile()) {
468     *out_result = net::ERR_CACHE_WRITE_FAILURE;
469     return;
470   }
471 
472   int64 sparse_data_size = out_entry_stat->sparse_data_size();
473   // This is a pessimistic estimate; it assumes the entire buffer is going to
474   // be appended as a new range, not written over existing ranges.
475   if (sparse_data_size + buf_len > max_sparse_data_size) {
476     DVLOG(1) << "Truncating sparse data file (" << sparse_data_size << " + "
477              << buf_len << " > " << max_sparse_data_size << ")";
478     TruncateSparseFile();
479   }
480 
481   SparseRangeIterator it = sparse_ranges_.lower_bound(offset);
482 
483   if (it != sparse_ranges_.begin()) {
484     --it;
485     SparseRange* found_range = &it->second;
486     if (found_range->offset + found_range->length > offset) {
487       DCHECK_LE(0, found_range->length);
488       DCHECK_GE(kint32max, found_range->length);
489       DCHECK_LE(0, offset - found_range->offset);
490       DCHECK_GE(kint32max, offset - found_range->offset);
491       int range_len_after_offset = found_range->length -
492                                    (offset - found_range->offset);
493       DCHECK_LE(0, range_len_after_offset);
494 
495       int len_to_write = std::min(buf_len, range_len_after_offset);
496       if (!WriteSparseRange(found_range,
497                             offset - found_range->offset,
498                             len_to_write,
499                             buf)) {
500         *out_result = net::ERR_CACHE_WRITE_FAILURE;
501         return;
502       }
503       written_so_far += len_to_write;
504     }
505     ++it;
506   }
507 
508   while (written_so_far < buf_len &&
509          it != sparse_ranges_.end() &&
510          it->second.offset < offset + buf_len) {
511     SparseRange* found_range = &it->second;
512     if (offset + written_so_far < found_range->offset) {
513       int len_to_append = found_range->offset - (offset + written_so_far);
514       if (!AppendSparseRange(offset + written_so_far,
515                              len_to_append,
516                              buf + written_so_far)) {
517         *out_result = net::ERR_CACHE_WRITE_FAILURE;
518         return;
519       }
520       written_so_far += len_to_append;
521       appended_so_far += len_to_append;
522     }
523     int range_len = (found_range->length > kint32max) ?
524                     kint32max : found_range->length;
525     int len_to_write = std::min(buf_len - written_so_far, range_len);
526     if (!WriteSparseRange(found_range,
527                           0,
528                           len_to_write,
529                           buf + written_so_far)) {
530       *out_result = net::ERR_CACHE_WRITE_FAILURE;
531       return;
532     }
533     written_so_far += len_to_write;
534     ++it;
535   }
536 
537   if (written_so_far < buf_len) {
538     int len_to_append = buf_len - written_so_far;
539     if (!AppendSparseRange(offset + written_so_far,
540                            len_to_append,
541                            buf + written_so_far)) {
542       *out_result = net::ERR_CACHE_WRITE_FAILURE;
543       return;
544     }
545     written_so_far += len_to_append;
546     appended_so_far += len_to_append;
547   }
548 
549   DCHECK_EQ(buf_len, written_so_far);
550 
551   base::Time modification_time = Time::Now();
552   out_entry_stat->set_last_used(modification_time);
553   out_entry_stat->set_last_modified(modification_time);
554   int32 old_sparse_data_size = out_entry_stat->sparse_data_size();
555   out_entry_stat->set_sparse_data_size(old_sparse_data_size + appended_so_far);
556   *out_result = written_so_far;
557 }
558 
GetAvailableRange(const EntryOperationData & in_entry_op,int64 * out_start,int * out_result)559 void SimpleSynchronousEntry::GetAvailableRange(
560     const EntryOperationData& in_entry_op,
561     int64* out_start,
562     int* out_result) {
563   DCHECK(initialized_);
564   int64 offset = in_entry_op.sparse_offset;
565   int len = in_entry_op.buf_len;
566 
567   SparseRangeIterator it = sparse_ranges_.lower_bound(offset);
568 
569   int64 start = offset;
570   int avail_so_far = 0;
571 
572   if (it != sparse_ranges_.end() && it->second.offset < offset + len)
573     start = it->second.offset;
574 
575   if ((it == sparse_ranges_.end() || it->second.offset > offset) &&
576       it != sparse_ranges_.begin()) {
577     --it;
578     if (it->second.offset + it->second.length > offset) {
579       start = offset;
580       avail_so_far = (it->second.offset + it->second.length) - offset;
581     }
582     ++it;
583   }
584 
585   while (start + avail_so_far < offset + len &&
586          it != sparse_ranges_.end() &&
587          it->second.offset == start + avail_so_far) {
588     avail_so_far += it->second.length;
589     ++it;
590   }
591 
592   int len_from_start = len - (start - offset);
593   *out_start = start;
594   *out_result = std::min(avail_so_far, len_from_start);
595 }
596 
CheckEOFRecord(int index,const SimpleEntryStat & entry_stat,uint32 expected_crc32,int * out_result) const597 void SimpleSynchronousEntry::CheckEOFRecord(int index,
598                                             const SimpleEntryStat& entry_stat,
599                                             uint32 expected_crc32,
600                                             int* out_result) const {
601   DCHECK(initialized_);
602   uint32 crc32;
603   bool has_crc32;
604   int stream_size;
605   *out_result =
606       GetEOFRecordData(index, entry_stat, &has_crc32, &crc32, &stream_size);
607   if (*out_result != net::OK) {
608     Doom();
609     return;
610   }
611   if (has_crc32 && crc32 != expected_crc32) {
612     DVLOG(1) << "EOF record had bad crc.";
613     *out_result = net::ERR_CACHE_CHECKSUM_MISMATCH;
614     RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_CRC_MISMATCH);
615     Doom();
616     return;
617   }
618   RecordCheckEOFResult(cache_type_, CHECK_EOF_RESULT_SUCCESS);
619 }
620 
Close(const SimpleEntryStat & entry_stat,scoped_ptr<std::vector<CRCRecord>> crc32s_to_write,net::GrowableIOBuffer * stream_0_data)621 void SimpleSynchronousEntry::Close(
622     const SimpleEntryStat& entry_stat,
623     scoped_ptr<std::vector<CRCRecord> > crc32s_to_write,
624     net::GrowableIOBuffer* stream_0_data) {
625   DCHECK(stream_0_data);
626   // Write stream 0 data.
627   int stream_0_offset = entry_stat.GetOffsetInFile(key_, 0, 0);
628   if (files_[0].Write(stream_0_offset, stream_0_data->data(),
629                       entry_stat.data_size(0)) !=
630       entry_stat.data_size(0)) {
631     RecordCloseResult(cache_type_, CLOSE_RESULT_WRITE_FAILURE);
632     DVLOG(1) << "Could not write stream 0 data.";
633     Doom();
634   }
635 
636   for (std::vector<CRCRecord>::const_iterator it = crc32s_to_write->begin();
637        it != crc32s_to_write->end(); ++it) {
638     const int stream_index = it->index;
639     const int file_index = GetFileIndexFromStreamIndex(stream_index);
640     if (empty_file_omitted_[file_index])
641       continue;
642 
643     SimpleFileEOF eof_record;
644     eof_record.stream_size = entry_stat.data_size(stream_index);
645     eof_record.final_magic_number = kSimpleFinalMagicNumber;
646     eof_record.flags = 0;
647     if (it->has_crc32)
648       eof_record.flags |= SimpleFileEOF::FLAG_HAS_CRC32;
649     eof_record.data_crc32 = it->data_crc32;
650     int eof_offset = entry_stat.GetEOFOffsetInFile(key_, stream_index);
651     // If stream 0 changed size, the file needs to be resized, otherwise the
652     // next open will yield wrong stream sizes. On stream 1 and stream 2 proper
653     // resizing of the file is handled in SimpleSynchronousEntry::WriteData().
654     if (stream_index == 0 &&
655         !files_[file_index].SetLength(eof_offset)) {
656       RecordCloseResult(cache_type_, CLOSE_RESULT_WRITE_FAILURE);
657       DVLOG(1) << "Could not truncate stream 0 file.";
658       Doom();
659       break;
660     }
661     if (files_[file_index].Write(eof_offset,
662                                  reinterpret_cast<const char*>(&eof_record),
663                                  sizeof(eof_record)) !=
664         sizeof(eof_record)) {
665       RecordCloseResult(cache_type_, CLOSE_RESULT_WRITE_FAILURE);
666       DVLOG(1) << "Could not write eof record.";
667       Doom();
668       break;
669     }
670   }
671   for (int i = 0; i < kSimpleEntryFileCount; ++i) {
672     if (empty_file_omitted_[i])
673       continue;
674 
675     files_[i].Close();
676     const int64 file_size = entry_stat.GetFileSize(key_, i);
677     SIMPLE_CACHE_UMA(CUSTOM_COUNTS,
678                      "LastClusterSize", cache_type_,
679                      file_size % 4096, 0, 4097, 50);
680     const int64 cluster_loss = file_size % 4096 ? 4096 - file_size % 4096 : 0;
681     SIMPLE_CACHE_UMA(PERCENTAGE,
682                      "LastClusterLossPercent", cache_type_,
683                      cluster_loss * 100 / (cluster_loss + file_size));
684   }
685 
686   if (sparse_file_open())
687     sparse_file_.Close();
688 
689   if (files_created_) {
690     const int stream2_file_index = GetFileIndexFromStreamIndex(2);
691     SIMPLE_CACHE_UMA(BOOLEAN, "EntryCreatedAndStream2Omitted", cache_type_,
692                      empty_file_omitted_[stream2_file_index]);
693   }
694   RecordCloseResult(cache_type_, CLOSE_RESULT_SUCCESS);
695   have_open_files_ = false;
696   delete this;
697 }
698 
SimpleSynchronousEntry(net::CacheType cache_type,const FilePath & path,const std::string & key,const uint64 entry_hash)699 SimpleSynchronousEntry::SimpleSynchronousEntry(net::CacheType cache_type,
700                                                const FilePath& path,
701                                                const std::string& key,
702                                                const uint64 entry_hash)
703     : cache_type_(cache_type),
704       path_(path),
705       entry_hash_(entry_hash),
706       key_(key),
707       have_open_files_(false),
708       initialized_(false) {
709   for (int i = 0; i < kSimpleEntryFileCount; ++i)
710     empty_file_omitted_[i] = false;
711 }
712 
~SimpleSynchronousEntry()713 SimpleSynchronousEntry::~SimpleSynchronousEntry() {
714   DCHECK(!(have_open_files_ && initialized_));
715   if (have_open_files_)
716     CloseFiles();
717 }
718 
MaybeOpenFile(int file_index,File::Error * out_error)719 bool SimpleSynchronousEntry::MaybeOpenFile(
720     int file_index,
721     File::Error* out_error) {
722   DCHECK(out_error);
723 
724   FilePath filename = GetFilenameFromFileIndex(file_index);
725   int flags = File::FLAG_OPEN | File::FLAG_READ | File::FLAG_WRITE;
726   files_[file_index].Initialize(filename, flags);
727   *out_error = files_[file_index].error_details();
728 
729   if (CanOmitEmptyFile(file_index) && !files_[file_index].IsValid() &&
730       *out_error == File::FILE_ERROR_NOT_FOUND) {
731     empty_file_omitted_[file_index] = true;
732     return true;
733   }
734 
735   return files_[file_index].IsValid();
736 }
737 
MaybeCreateFile(int file_index,FileRequired file_required,File::Error * out_error)738 bool SimpleSynchronousEntry::MaybeCreateFile(
739     int file_index,
740     FileRequired file_required,
741     File::Error* out_error) {
742   DCHECK(out_error);
743 
744   if (CanOmitEmptyFile(file_index) && file_required == FILE_NOT_REQUIRED) {
745     empty_file_omitted_[file_index] = true;
746     return true;
747   }
748 
749   FilePath filename = GetFilenameFromFileIndex(file_index);
750   int flags = File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE;
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 
OpenFiles(bool had_index,SimpleEntryStat * out_entry_stat)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, 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 
CreateFiles(bool had_index,SimpleEntryStat * out_entry_stat)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 
CloseFile(int index)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 
CloseFiles()892 void SimpleSynchronousEntry::CloseFiles() {
893   for (int i = 0; i < kSimpleEntryFileCount; ++i)
894     CloseFile(i);
895 }
896 
InitializeForOpen(bool had_index,SimpleEntryStat * out_entry_stat,scoped_refptr<net::GrowableIOBuffer> * stream_0_data,uint32 * out_stream_0_crc32)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 
InitializeCreatedFile(int file_index,CreateEntryResult * out_result)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 
InitializeForCreate(bool had_index,SimpleEntryStat * out_entry_stat)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 
ReadAndValidateStream0(int total_data_size,SimpleEntryStat * out_entry_stat,scoped_refptr<net::GrowableIOBuffer> * stream_0_data,uint32 * out_stream_0_crc32) const1048 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 
GetEOFRecordData(int index,const SimpleEntryStat & entry_stat,bool * out_has_crc32,uint32 * out_crc32,int * out_data_size) const1101 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 
Doom() const1131 void SimpleSynchronousEntry::Doom() const {
1132   DeleteFilesForEntryHash(path_, entry_hash_);
1133 }
1134 
1135 // static
DeleteFileForEntryHash(const FilePath & path,const uint64 entry_hash,const int file_index)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 base::DeleteFile(to_delete, false);
1143 }
1144 
1145 // static
DeleteFilesForEntryHash(const FilePath & path,const uint64 entry_hash)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   base::DeleteFile(to_delete, false);
1157   return result;
1158 }
1159 
RecordSyncCreateResult(CreateEntryResult result,bool had_index)1160 void SimpleSynchronousEntry::RecordSyncCreateResult(CreateEntryResult result,
1161                                                     bool had_index) {
1162   DCHECK_GT(CREATE_ENTRY_MAX, result);
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 
GetFilenameFromFileIndex(int file_index)1176 FilePath SimpleSynchronousEntry::GetFilenameFromFileIndex(int file_index) {
1177   return path_.AppendASCII(
1178       GetFilenameFromEntryHashAndFileIndex(entry_hash_, file_index));
1179 }
1180 
OpenSparseFileIfExists(int32 * out_sparse_data_size)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   sparse_file_.Initialize(filename, flags);
1189   if (sparse_file_.IsValid())
1190     return ScanSparseFile(out_sparse_data_size);
1191 
1192   return sparse_file_.error_details() == File::FILE_ERROR_NOT_FOUND;
1193 }
1194 
CreateSparseFile()1195 bool SimpleSynchronousEntry::CreateSparseFile() {
1196   DCHECK(!sparse_file_open());
1197 
1198   FilePath filename = path_.AppendASCII(
1199       GetSparseFilenameFromEntryHash(entry_hash_));
1200   int flags = File::FLAG_CREATE | File::FLAG_READ | File::FLAG_WRITE;
1201   sparse_file_.Initialize(filename, flags);
1202   if (!sparse_file_.IsValid())
1203     return false;
1204 
1205   return InitializeSparseFile();
1206 }
1207 
CloseSparseFile()1208 void SimpleSynchronousEntry::CloseSparseFile() {
1209   DCHECK(sparse_file_open());
1210   sparse_file_.Close();
1211 }
1212 
TruncateSparseFile()1213 bool SimpleSynchronousEntry::TruncateSparseFile() {
1214   DCHECK(sparse_file_open());
1215 
1216   int64 header_and_key_length = sizeof(SimpleFileHeader) + key_.size();
1217   if (!sparse_file_.SetLength(header_and_key_length)) {
1218     DLOG(WARNING) << "Could not truncate sparse file";
1219     return false;
1220   }
1221 
1222   sparse_ranges_.clear();
1223 
1224   return true;
1225 }
1226 
InitializeSparseFile()1227 bool SimpleSynchronousEntry::InitializeSparseFile() {
1228   DCHECK(sparse_file_open());
1229 
1230   SimpleFileHeader header;
1231   header.initial_magic_number = kSimpleInitialMagicNumber;
1232   header.version = kSimpleVersion;
1233   header.key_length = key_.size();
1234   header.key_hash = base::Hash(key_);
1235 
1236   int header_write_result =
1237       sparse_file_.Write(0, reinterpret_cast<char*>(&header), sizeof(header));
1238   if (header_write_result != sizeof(header)) {
1239     DLOG(WARNING) << "Could not write sparse file header";
1240     return false;
1241   }
1242 
1243   int key_write_result = sparse_file_.Write(sizeof(header), key_.data(),
1244                                             key_.size());
1245   if (key_write_result != implicit_cast<int>(key_.size())) {
1246     DLOG(WARNING) << "Could not write sparse file key";
1247     return false;
1248   }
1249 
1250   sparse_ranges_.clear();
1251   sparse_tail_offset_ = sizeof(header) + key_.size();
1252 
1253   return true;
1254 }
1255 
ScanSparseFile(int32 * out_sparse_data_size)1256 bool SimpleSynchronousEntry::ScanSparseFile(int32* out_sparse_data_size) {
1257   DCHECK(sparse_file_open());
1258 
1259   int32 sparse_data_size = 0;
1260 
1261   SimpleFileHeader header;
1262   int header_read_result =
1263       sparse_file_.Read(0, reinterpret_cast<char*>(&header), sizeof(header));
1264   if (header_read_result != sizeof(header)) {
1265     DLOG(WARNING) << "Could not read header from sparse file.";
1266     return false;
1267   }
1268 
1269   if (header.initial_magic_number != kSimpleInitialMagicNumber) {
1270     DLOG(WARNING) << "Sparse file magic number did not match.";
1271     return false;
1272   }
1273 
1274   if (header.version != kSimpleVersion) {
1275     DLOG(WARNING) << "Sparse file unreadable version.";
1276     return false;
1277   }
1278 
1279   sparse_ranges_.clear();
1280 
1281   int64 range_header_offset = sizeof(header) + key_.size();
1282   while (1) {
1283     SimpleFileSparseRangeHeader range_header;
1284     int range_header_read_result =
1285         sparse_file_.Read(range_header_offset,
1286                           reinterpret_cast<char*>(&range_header),
1287                           sizeof(range_header));
1288     if (range_header_read_result == 0)
1289       break;
1290     if (range_header_read_result != sizeof(range_header)) {
1291       DLOG(WARNING) << "Could not read sparse range header.";
1292       return false;
1293     }
1294 
1295     if (range_header.sparse_range_magic_number !=
1296         kSimpleSparseRangeMagicNumber) {
1297       DLOG(WARNING) << "Invalid sparse range header magic number.";
1298       return false;
1299     }
1300 
1301     SparseRange range;
1302     range.offset = range_header.offset;
1303     range.length = range_header.length;
1304     range.data_crc32 = range_header.data_crc32;
1305     range.file_offset = range_header_offset + sizeof(range_header);
1306     sparse_ranges_.insert(std::make_pair(range.offset, range));
1307 
1308     range_header_offset += sizeof(range_header) + range.length;
1309 
1310     DCHECK_LE(sparse_data_size, sparse_data_size + range.length);
1311     sparse_data_size += range.length;
1312   }
1313 
1314   *out_sparse_data_size = sparse_data_size;
1315   sparse_tail_offset_ = range_header_offset;
1316 
1317   return true;
1318 }
1319 
ReadSparseRange(const SparseRange * range,int offset,int len,char * buf)1320 bool SimpleSynchronousEntry::ReadSparseRange(const SparseRange* range,
1321                                              int offset, int len, char* buf) {
1322   DCHECK(range);
1323   DCHECK(buf);
1324   DCHECK_GE(range->length, offset);
1325   DCHECK_GE(range->length, offset + len);
1326 
1327   int bytes_read = sparse_file_.Read(range->file_offset + offset, buf, len);
1328   if (bytes_read < len) {
1329     DLOG(WARNING) << "Could not read sparse range.";
1330     return false;
1331   }
1332 
1333   // If we read the whole range and we have a crc32, check it.
1334   if (offset == 0 && len == range->length && range->data_crc32 != 0) {
1335     uint32 actual_crc32 = crc32(crc32(0L, Z_NULL, 0),
1336                                 reinterpret_cast<const Bytef*>(buf),
1337                                 len);
1338     if (actual_crc32 != range->data_crc32) {
1339       DLOG(WARNING) << "Sparse range crc32 mismatch.";
1340       return false;
1341     }
1342   }
1343   // TODO(ttuttle): Incremental crc32 calculation?
1344 
1345   return true;
1346 }
1347 
WriteSparseRange(SparseRange * range,int offset,int len,const char * buf)1348 bool SimpleSynchronousEntry::WriteSparseRange(SparseRange* range,
1349                                               int offset, int len,
1350                                               const char* buf) {
1351   DCHECK(range);
1352   DCHECK(buf);
1353   DCHECK_GE(range->length, offset);
1354   DCHECK_GE(range->length, offset + len);
1355 
1356   uint32 new_crc32 = 0;
1357   if (offset == 0 && len == range->length) {
1358     new_crc32 = crc32(crc32(0L, Z_NULL, 0),
1359                       reinterpret_cast<const Bytef*>(buf),
1360                       len);
1361   }
1362 
1363   if (new_crc32 != range->data_crc32) {
1364     range->data_crc32 = new_crc32;
1365 
1366     SimpleFileSparseRangeHeader header;
1367     header.sparse_range_magic_number = kSimpleSparseRangeMagicNumber;
1368     header.offset = range->offset;
1369     header.length = range->length;
1370     header.data_crc32 = range->data_crc32;
1371 
1372     int bytes_written = sparse_file_.Write(range->file_offset - sizeof(header),
1373                                            reinterpret_cast<char*>(&header),
1374                                            sizeof(header));
1375     if (bytes_written != implicit_cast<int>(sizeof(header))) {
1376       DLOG(WARNING) << "Could not rewrite sparse range header.";
1377       return false;
1378     }
1379   }
1380 
1381   int bytes_written = sparse_file_.Write(range->file_offset + offset, buf, len);
1382   if (bytes_written < len) {
1383     DLOG(WARNING) << "Could not write sparse range.";
1384     return false;
1385   }
1386 
1387   return true;
1388 }
1389 
AppendSparseRange(int64 offset,int len,const char * buf)1390 bool SimpleSynchronousEntry::AppendSparseRange(int64 offset,
1391                                                int len,
1392                                                const char* buf) {
1393   DCHECK_LE(0, offset);
1394   DCHECK_LT(0, len);
1395   DCHECK(buf);
1396 
1397   uint32 data_crc32 = crc32(crc32(0L, Z_NULL, 0),
1398                             reinterpret_cast<const Bytef*>(buf),
1399                             len);
1400 
1401   SimpleFileSparseRangeHeader header;
1402   header.sparse_range_magic_number = kSimpleSparseRangeMagicNumber;
1403   header.offset = offset;
1404   header.length = len;
1405   header.data_crc32 = data_crc32;
1406 
1407   int bytes_written = sparse_file_.Write(sparse_tail_offset_,
1408                                          reinterpret_cast<char*>(&header),
1409                                          sizeof(header));
1410   if (bytes_written != implicit_cast<int>(sizeof(header))) {
1411     DLOG(WARNING) << "Could not append sparse range header.";
1412     return false;
1413   }
1414   sparse_tail_offset_ += bytes_written;
1415 
1416   bytes_written = sparse_file_.Write(sparse_tail_offset_, buf, len);
1417   if (bytes_written < len) {
1418     DLOG(WARNING) << "Could not append sparse range data.";
1419     return false;
1420   }
1421   int64 data_file_offset = sparse_tail_offset_;
1422   sparse_tail_offset_ += bytes_written;
1423 
1424   SparseRange range;
1425   range.offset = offset;
1426   range.length = len;
1427   range.data_crc32 = data_crc32;
1428   range.file_offset = data_file_offset;
1429   sparse_ranges_.insert(std::make_pair(offset, range));
1430 
1431   return true;
1432 }
1433 
1434 }  // namespace disk_cache
1435