1// Copyright (c) 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/disk_cache/simple/simple_synchronous_entry.h"
6
7#include <algorithm>
8#include <cstring>
9#include <functional>
10#include <limits>
11
12#include "base/basictypes.h"
13#include "base/compiler_specific.h"
14#include "base/files/file_util.h"
15#include "base/hash.h"
16#include "base/location.h"
17#include "base/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
26using base::File;
27using base::FilePath;
28using base::Time;
29
30namespace {
31
32// Used in histograms, please only add entries at the end.
33enum 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.
47enum 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.
59enum 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.
68enum CloseResult {
69  CLOSE_RESULT_SUCCESS,
70  CLOSE_RESULT_WRITE_FAILURE,
71};
72
73void 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
90void RecordWriteResult(net::CacheType cache_type, WriteResult result) {
91  SIMPLE_CACHE_UMA(ENUMERATION,
92                   "SyncWriteResult", cache_type, result, WRITE_RESULT_MAX);
93}
94
95void RecordCheckEOFResult(net::CacheType cache_type, CheckEOFResult result) {
96  SIMPLE_CACHE_UMA(ENUMERATION,
97                   "SyncCheckEOFResult", cache_type,
98                   result, CHECK_EOF_RESULT_MAX);
99}
100
101void RecordCloseResult(net::CacheType cache_type, CloseResult result) {
102  SIMPLE_CACHE_UMA(ENUMERATION,
103                   "SyncCloseResult", cache_type, result, WRITE_RESULT_MAX);
104}
105
106bool 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
114namespace disk_cache {
115
116using simple_util::GetEntryHashKey;
117using simple_util::GetFilenameFromEntryHashAndFileIndex;
118using simple_util::GetSparseFilenameFromEntryHash;
119using simple_util::GetDataSizeFromKeyAndFileSize;
120using simple_util::GetFileSizeFromKeyAndDataSize;
121using simple_util::GetFileIndexFromStreamIndex;
122
123SimpleEntryStat::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
133int 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
142int SimpleEntryStat::GetEOFOffsetInFile(const std::string& key,
143                                        int stream_index) const {
144  return GetOffsetInFile(key, data_size_[stream_index], stream_index);
145}
146
147int 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
156int 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
163SimpleEntryCreationResults::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
171SimpleEntryCreationResults::~SimpleEntryCreationResults() {
172}
173
174SimpleSynchronousEntry::CRCRecord::CRCRecord() : index(-1),
175                                                 has_crc32(false),
176                                                 data_crc32(0) {
177}
178
179SimpleSynchronousEntry::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
186SimpleSynchronousEntry::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
193SimpleSynchronousEntry::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
204SimpleSynchronousEntry::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
211void 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
235void 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
258int 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
266int 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
275void 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
306void 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
390void 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
453void 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
559void 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
597void 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
621void 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
699SimpleSynchronousEntry::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
713SimpleSynchronousEntry::~SimpleSynchronousEntry() {
714  DCHECK(!(have_open_files_ && initialized_));
715  if (have_open_files_)
716    CloseFiles();
717}
718
719bool 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
738bool 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
759bool 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
839bool 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
880void 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
892void SimpleSynchronousEntry::CloseFiles() {
893  for (int i = 0; i < kSimpleEntryFileCount; ++i)
894    CloseFile(i);
895}
896
897int 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
998bool 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
1025int 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
1048int 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
1101int 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
1131void SimpleSynchronousEntry::Doom() const {
1132  DeleteFilesForEntryHash(path_, entry_hash_);
1133}
1134
1135// static
1136bool 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
1146bool 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
1160void 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
1176FilePath SimpleSynchronousEntry::GetFilenameFromFileIndex(int file_index) {
1177  return path_.AppendASCII(
1178      GetFilenameFromEntryHashAndFileIndex(entry_hash_, file_index));
1179}
1180
1181bool 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
1195bool 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
1208void SimpleSynchronousEntry::CloseSparseFile() {
1209  DCHECK(sparse_file_open());
1210  sparse_file_.Close();
1211}
1212
1213bool 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
1227bool 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
1256bool 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
1320bool 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
1348bool 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
1390bool 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