Home | History | Annotate | Download | only in disk_cache
      1 // Copyright (c) 2012 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/block_files.h"
      6 
      7 #include "base/atomicops.h"
      8 #include "base/files/file_path.h"
      9 #include "base/metrics/histogram.h"
     10 #include "base/strings/string_util.h"
     11 #include "base/strings/stringprintf.h"
     12 #include "base/threading/thread_checker.h"
     13 #include "base/time/time.h"
     14 #include "net/disk_cache/cache_util.h"
     15 #include "net/disk_cache/file_lock.h"
     16 #include "net/disk_cache/trace.h"
     17 
     18 using base::TimeTicks;
     19 
     20 namespace {
     21 
     22 const char* kBlockName = "data_";
     23 
     24 // This array is used to perform a fast lookup of the nibble bit pattern to the
     25 // type of entry that can be stored there (number of consecutive blocks).
     26 const char s_types[16] = {4, 3, 2, 2, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0};
     27 
     28 // Returns the type of block (number of consecutive blocks that can be stored)
     29 // for a given nibble of the bitmap.
     30 inline int GetMapBlockType(uint8 value) {
     31   value &= 0xf;
     32   return s_types[value];
     33 }
     34 
     35 }  // namespace
     36 
     37 namespace disk_cache {
     38 
     39 BlockHeader::BlockHeader() : header_(NULL) {
     40 }
     41 
     42 BlockHeader::BlockHeader(BlockFileHeader* header) : header_(header) {
     43 }
     44 
     45 BlockHeader::BlockHeader(MappedFile* file)
     46     : header_(reinterpret_cast<BlockFileHeader*>(file->buffer())) {
     47 }
     48 
     49 BlockHeader::BlockHeader(const BlockHeader& other) : header_(other.header_) {
     50 }
     51 
     52 BlockHeader::~BlockHeader() {
     53 }
     54 
     55 bool BlockHeader::CreateMapBlock(int target, int size, int* index) {
     56   if (target <= 0 || target > kMaxNumBlocks ||
     57       size <= 0 || size > kMaxNumBlocks) {
     58     NOTREACHED();
     59     return false;
     60   }
     61 
     62   TimeTicks start = TimeTicks::Now();
     63   // We are going to process the map on 32-block chunks (32 bits), and on every
     64   // chunk, iterate through the 8 nibbles where the new block can be located.
     65   int current = header_->hints[target - 1];
     66   for (int i = 0; i < header_->max_entries / 32; i++, current++) {
     67     if (current == header_->max_entries / 32)
     68       current = 0;
     69     uint32 map_block = header_->allocation_map[current];
     70 
     71     for (int j = 0; j < 8; j++, map_block >>= 4) {
     72       if (GetMapBlockType(map_block) != target)
     73         continue;
     74 
     75       disk_cache::FileLock lock(header_);
     76       int index_offset = j * 4 + 4 - target;
     77       *index = current * 32 + index_offset;
     78       DCHECK_EQ(*index / 4, (*index + size - 1) / 4);
     79       uint32 to_add = ((1 << size) - 1) << index_offset;
     80       header_->num_entries++;
     81 
     82       // Note that there is no race in the normal sense here, but if we enforce
     83       // the order of memory accesses between num_entries and allocation_map, we
     84       // can assert that even if we crash here, num_entries will never be less
     85       // than the actual number of used blocks.
     86       base::subtle::MemoryBarrier();
     87       header_->allocation_map[current] |= to_add;
     88 
     89       header_->hints[target - 1] = current;
     90       header_->empty[target - 1]--;
     91       DCHECK_GE(header_->empty[target - 1], 0);
     92       if (target != size) {
     93         header_->empty[target - size - 1]++;
     94       }
     95       HISTOGRAM_TIMES("DiskCache.CreateBlock", TimeTicks::Now() - start);
     96       return true;
     97     }
     98   }
     99 
    100   // It is possible to have an undetected corruption (for example when the OS
    101   // crashes), fix it here.
    102   LOG(ERROR) << "Failing CreateMapBlock";
    103   FixAllocationCounters();
    104   return false;
    105 }
    106 
    107 void BlockHeader::DeleteMapBlock(int index, int size) {
    108   if (size < 0 || size > kMaxNumBlocks) {
    109     NOTREACHED();
    110     return;
    111   }
    112   TimeTicks start = TimeTicks::Now();
    113   int byte_index = index / 8;
    114   uint8* byte_map = reinterpret_cast<uint8*>(header_->allocation_map);
    115   uint8 map_block = byte_map[byte_index];
    116 
    117   if (index % 8 >= 4)
    118     map_block >>= 4;
    119 
    120   // See what type of block will be available after we delete this one.
    121   int bits_at_end = 4 - size - index % 4;
    122   uint8 end_mask = (0xf << (4 - bits_at_end)) & 0xf;
    123   bool update_counters = (map_block & end_mask) == 0;
    124   uint8 new_value = map_block & ~(((1 << size) - 1) << (index % 4));
    125   int new_type = GetMapBlockType(new_value);
    126 
    127   disk_cache::FileLock lock(header_);
    128   DCHECK((((1 << size) - 1) << (index % 8)) < 0x100);
    129   uint8  to_clear = ((1 << size) - 1) << (index % 8);
    130   DCHECK((byte_map[byte_index] & to_clear) == to_clear);
    131   byte_map[byte_index] &= ~to_clear;
    132 
    133   if (update_counters) {
    134     if (bits_at_end)
    135       header_->empty[bits_at_end - 1]--;
    136     header_->empty[new_type - 1]++;
    137     DCHECK_GE(header_->empty[bits_at_end - 1], 0);
    138   }
    139   base::subtle::MemoryBarrier();
    140   header_->num_entries--;
    141   DCHECK_GE(header_->num_entries, 0);
    142   HISTOGRAM_TIMES("DiskCache.DeleteBlock", TimeTicks::Now() - start);
    143 }
    144 
    145 // Note that this is a simplified version of DeleteMapBlock().
    146 bool BlockHeader::UsedMapBlock(int index, int size) {
    147   if (size < 0 || size > kMaxNumBlocks) {
    148     NOTREACHED();
    149     return false;
    150   }
    151   int byte_index = index / 8;
    152   uint8* byte_map = reinterpret_cast<uint8*>(header_->allocation_map);
    153   uint8 map_block = byte_map[byte_index];
    154 
    155   if (index % 8 >= 4)
    156     map_block >>= 4;
    157 
    158   DCHECK((((1 << size) - 1) << (index % 8)) < 0x100);
    159   uint8  to_clear = ((1 << size) - 1) << (index % 8);
    160   return ((byte_map[byte_index] & to_clear) == to_clear);
    161 }
    162 
    163 void BlockHeader::FixAllocationCounters() {
    164   for (int i = 0; i < kMaxNumBlocks; i++) {
    165     header_->hints[i] = 0;
    166     header_->empty[i] = 0;
    167   }
    168 
    169   for (int i = 0; i < header_->max_entries / 32; i++) {
    170     uint32 map_block = header_->allocation_map[i];
    171 
    172     for (int j = 0; j < 8; j++, map_block >>= 4) {
    173       int type = GetMapBlockType(map_block);
    174       if (type)
    175         header_->empty[type -1]++;
    176     }
    177   }
    178 }
    179 
    180 bool BlockHeader::NeedToGrowBlockFile(int block_count) {
    181   bool have_space = false;
    182   int empty_blocks = 0;
    183   for (int i = 0; i < kMaxNumBlocks; i++) {
    184     empty_blocks += header_->empty[i] * (i + 1);
    185     if (i >= block_count - 1 && header_->empty[i])
    186       have_space = true;
    187   }
    188 
    189   if (header_->next_file && (empty_blocks < kMaxBlocks / 10)) {
    190     // This file is almost full but we already created another one, don't use
    191     // this file yet so that it is easier to find empty blocks when we start
    192     // using this file again.
    193     return true;
    194   }
    195   return !have_space;
    196 }
    197 
    198 int BlockHeader::EmptyBlocks() const {
    199   int empty_blocks = 0;
    200   for (int i = 0; i < disk_cache::kMaxNumBlocks; i++) {
    201     empty_blocks += header_->empty[i] * (i + 1);
    202     if (header_->empty[i] < 0)
    203       return 0;
    204   }
    205   return empty_blocks;
    206 }
    207 
    208 bool BlockHeader::ValidateCounters() const {
    209   if (header_->max_entries < 0 || header_->max_entries > kMaxBlocks ||
    210       header_->num_entries < 0)
    211     return false;
    212 
    213   int empty_blocks = EmptyBlocks();
    214   if (empty_blocks + header_->num_entries > header_->max_entries)
    215     return false;
    216 
    217   return true;
    218 }
    219 
    220 int BlockHeader::Size() const {
    221   return static_cast<int>(sizeof(*header_));
    222 }
    223 
    224 // ------------------------------------------------------------------------
    225 
    226 BlockFiles::BlockFiles(const base::FilePath& path)
    227     : init_(false), zero_buffer_(NULL), path_(path) {
    228 }
    229 
    230 BlockFiles::~BlockFiles() {
    231   if (zero_buffer_)
    232     delete[] zero_buffer_;
    233   CloseFiles();
    234 }
    235 
    236 bool BlockFiles::Init(bool create_files) {
    237   DCHECK(!init_);
    238   if (init_)
    239     return false;
    240 
    241   thread_checker_.reset(new base::ThreadChecker);
    242 
    243   block_files_.resize(kFirstAdditionalBlockFile);
    244   for (int i = 0; i < kFirstAdditionalBlockFile; i++) {
    245     if (create_files)
    246       if (!CreateBlockFile(i, static_cast<FileType>(i + 1), true))
    247         return false;
    248 
    249     if (!OpenBlockFile(i))
    250       return false;
    251 
    252     // Walk this chain of files removing empty ones.
    253     if (!RemoveEmptyFile(static_cast<FileType>(i + 1)))
    254       return false;
    255   }
    256 
    257   init_ = true;
    258   return true;
    259 }
    260 
    261 MappedFile* BlockFiles::GetFile(Addr address) {
    262   DCHECK(thread_checker_->CalledOnValidThread());
    263   DCHECK(block_files_.size() >= 4);
    264   DCHECK(address.is_block_file() || !address.is_initialized());
    265   if (!address.is_initialized())
    266     return NULL;
    267 
    268   int file_index = address.FileNumber();
    269   if (static_cast<unsigned int>(file_index) >= block_files_.size() ||
    270       !block_files_[file_index]) {
    271     // We need to open the file
    272     if (!OpenBlockFile(file_index))
    273       return NULL;
    274   }
    275   DCHECK(block_files_.size() >= static_cast<unsigned int>(file_index));
    276   return block_files_[file_index];
    277 }
    278 
    279 bool BlockFiles::CreateBlock(FileType block_type, int block_count,
    280                              Addr* block_address) {
    281   DCHECK(thread_checker_->CalledOnValidThread());
    282   if (block_type < RANKINGS || block_type > BLOCK_4K ||
    283       block_count < 1 || block_count > 4)
    284     return false;
    285   if (!init_)
    286     return false;
    287 
    288   MappedFile* file = FileForNewBlock(block_type, block_count);
    289   if (!file)
    290     return false;
    291 
    292   ScopedFlush flush(file);
    293   BlockHeader header(file);
    294 
    295   int target_size = 0;
    296   for (int i = block_count; i <= 4; i++) {
    297     if (header->empty[i - 1]) {
    298       target_size = i;
    299       break;
    300     }
    301   }
    302 
    303   DCHECK(target_size);
    304   int index;
    305   if (!header.CreateMapBlock(target_size, block_count, &index))
    306     return false;
    307 
    308   Addr address(block_type, block_count, header->this_file, index);
    309   block_address->set_value(address.value());
    310   Trace("CreateBlock 0x%x", address.value());
    311   return true;
    312 }
    313 
    314 void BlockFiles::DeleteBlock(Addr address, bool deep) {
    315   DCHECK(thread_checker_->CalledOnValidThread());
    316   if (!address.is_initialized() || address.is_separate_file())
    317     return;
    318 
    319   if (!zero_buffer_) {
    320     zero_buffer_ = new char[Addr::BlockSizeForFileType(BLOCK_4K) * 4];
    321     memset(zero_buffer_, 0, Addr::BlockSizeForFileType(BLOCK_4K) * 4);
    322   }
    323   MappedFile* file = GetFile(address);
    324   if (!file)
    325     return;
    326 
    327   Trace("DeleteBlock 0x%x", address.value());
    328 
    329   size_t size = address.BlockSize() * address.num_blocks();
    330   size_t offset = address.start_block() * address.BlockSize() +
    331                   kBlockHeaderSize;
    332   if (deep)
    333     file->Write(zero_buffer_, size, offset);
    334 
    335   BlockHeader header(file);
    336   header.DeleteMapBlock(address.start_block(), address.num_blocks());
    337   file->Flush();
    338 
    339   if (!header->num_entries) {
    340     // This file is now empty. Let's try to delete it.
    341     FileType type = Addr::RequiredFileType(header->entry_size);
    342     if (Addr::BlockSizeForFileType(RANKINGS) == header->entry_size)
    343       type = RANKINGS;
    344     RemoveEmptyFile(type);  // Ignore failures.
    345   }
    346 }
    347 
    348 void BlockFiles::CloseFiles() {
    349   if (init_) {
    350     DCHECK(thread_checker_->CalledOnValidThread());
    351   }
    352   init_ = false;
    353   for (unsigned int i = 0; i < block_files_.size(); i++) {
    354     if (block_files_[i]) {
    355       block_files_[i]->Release();
    356       block_files_[i] = NULL;
    357     }
    358   }
    359   block_files_.clear();
    360 }
    361 
    362 void BlockFiles::ReportStats() {
    363   DCHECK(thread_checker_->CalledOnValidThread());
    364   int used_blocks[kFirstAdditionalBlockFile];
    365   int load[kFirstAdditionalBlockFile];
    366   for (int i = 0; i < kFirstAdditionalBlockFile; i++) {
    367     GetFileStats(i, &used_blocks[i], &load[i]);
    368   }
    369   UMA_HISTOGRAM_COUNTS("DiskCache.Blocks_0", used_blocks[0]);
    370   UMA_HISTOGRAM_COUNTS("DiskCache.Blocks_1", used_blocks[1]);
    371   UMA_HISTOGRAM_COUNTS("DiskCache.Blocks_2", used_blocks[2]);
    372   UMA_HISTOGRAM_COUNTS("DiskCache.Blocks_3", used_blocks[3]);
    373 
    374   UMA_HISTOGRAM_ENUMERATION("DiskCache.BlockLoad_0", load[0], 101);
    375   UMA_HISTOGRAM_ENUMERATION("DiskCache.BlockLoad_1", load[1], 101);
    376   UMA_HISTOGRAM_ENUMERATION("DiskCache.BlockLoad_2", load[2], 101);
    377   UMA_HISTOGRAM_ENUMERATION("DiskCache.BlockLoad_3", load[3], 101);
    378 }
    379 
    380 bool BlockFiles::IsValid(Addr address) {
    381 #ifdef NDEBUG
    382   return true;
    383 #else
    384   if (!address.is_initialized() || address.is_separate_file())
    385     return false;
    386 
    387   MappedFile* file = GetFile(address);
    388   if (!file)
    389     return false;
    390 
    391   BlockHeader header(file);
    392   bool rv = header.UsedMapBlock(address.start_block(), address.num_blocks());
    393   DCHECK(rv);
    394 
    395   static bool read_contents = false;
    396   if (read_contents) {
    397     scoped_ptr<char[]> buffer;
    398     buffer.reset(new char[Addr::BlockSizeForFileType(BLOCK_4K) * 4]);
    399     size_t size = address.BlockSize() * address.num_blocks();
    400     size_t offset = address.start_block() * address.BlockSize() +
    401                     kBlockHeaderSize;
    402     bool ok = file->Read(buffer.get(), size, offset);
    403     DCHECK(ok);
    404   }
    405 
    406   return rv;
    407 #endif
    408 }
    409 
    410 bool BlockFiles::CreateBlockFile(int index, FileType file_type, bool force) {
    411   base::FilePath name = Name(index);
    412   int flags =
    413       force ? base::PLATFORM_FILE_CREATE_ALWAYS : base::PLATFORM_FILE_CREATE;
    414   flags |= base::PLATFORM_FILE_WRITE | base::PLATFORM_FILE_EXCLUSIVE_WRITE;
    415 
    416   scoped_refptr<File> file(new File(
    417       base::CreatePlatformFile(name, flags, NULL, NULL)));
    418   if (!file->IsValid())
    419     return false;
    420 
    421   BlockFileHeader header;
    422   memset(&header, 0, sizeof(header));
    423   header.magic = kBlockMagic;
    424   header.version = kBlockVersion2;
    425   header.entry_size = Addr::BlockSizeForFileType(file_type);
    426   header.this_file = static_cast<int16>(index);
    427   DCHECK(index <= kint16max && index >= 0);
    428 
    429   return file->Write(&header, sizeof(header), 0);
    430 }
    431 
    432 bool BlockFiles::OpenBlockFile(int index) {
    433   if (block_files_.size() - 1 < static_cast<unsigned int>(index)) {
    434     DCHECK(index > 0);
    435     int to_add = index - static_cast<int>(block_files_.size()) + 1;
    436     block_files_.resize(block_files_.size() + to_add);
    437   }
    438 
    439   base::FilePath name = Name(index);
    440   scoped_refptr<MappedFile> file(new MappedFile());
    441 
    442   if (!file->Init(name, kBlockHeaderSize)) {
    443     LOG(ERROR) << "Failed to open " << name.value();
    444     return false;
    445   }
    446 
    447   size_t file_len = file->GetLength();
    448   if (file_len < static_cast<size_t>(kBlockHeaderSize)) {
    449     LOG(ERROR) << "File too small " << name.value();
    450     return false;
    451   }
    452 
    453   BlockHeader header(file.get());
    454   if (kBlockMagic != header->magic || kBlockVersion2 != header->version) {
    455     LOG(ERROR) << "Invalid file version or magic " << name.value();
    456     return false;
    457   }
    458 
    459   if (header->updating || !header.ValidateCounters()) {
    460     // Last instance was not properly shutdown, or counters are out of sync.
    461     if (!FixBlockFileHeader(file.get())) {
    462       LOG(ERROR) << "Unable to fix block file " << name.value();
    463       return false;
    464     }
    465   }
    466 
    467   if (static_cast<int>(file_len) <
    468       header->max_entries * header->entry_size + kBlockHeaderSize) {
    469     LOG(ERROR) << "File too small " << name.value();
    470     return false;
    471   }
    472 
    473   if (index == 0) {
    474     // Load the links file into memory with a single read.
    475     scoped_ptr<char[]> buf(new char[file_len]);
    476     if (!file->Read(buf.get(), file_len, 0))
    477       return false;
    478   }
    479 
    480   ScopedFlush flush(file.get());
    481   DCHECK(!block_files_[index]);
    482   file.swap(&block_files_[index]);
    483   return true;
    484 }
    485 
    486 bool BlockFiles::GrowBlockFile(MappedFile* file, BlockFileHeader* header) {
    487   if (kMaxBlocks == header->max_entries)
    488     return false;
    489 
    490   ScopedFlush flush(file);
    491   DCHECK(!header->empty[3]);
    492   int new_size = header->max_entries + 1024;
    493   if (new_size > kMaxBlocks)
    494     new_size = kMaxBlocks;
    495 
    496   int new_size_bytes = new_size * header->entry_size + sizeof(*header);
    497 
    498   if (!file->SetLength(new_size_bytes)) {
    499     // Most likely we are trying to truncate the file, so the header is wrong.
    500     if (header->updating < 10 && !FixBlockFileHeader(file)) {
    501       // If we can't fix the file increase the lock guard so we'll pick it on
    502       // the next start and replace it.
    503       header->updating = 100;
    504       return false;
    505     }
    506     return (header->max_entries >= new_size);
    507   }
    508 
    509   FileLock lock(header);
    510   header->empty[3] = (new_size - header->max_entries) / 4;  // 4 blocks entries
    511   header->max_entries = new_size;
    512 
    513   return true;
    514 }
    515 
    516 MappedFile* BlockFiles::FileForNewBlock(FileType block_type, int block_count) {
    517   COMPILE_ASSERT(RANKINGS == 1, invalid_file_type);
    518   MappedFile* file = block_files_[block_type - 1];
    519   BlockHeader header(file);
    520 
    521   TimeTicks start = TimeTicks::Now();
    522   while (header.NeedToGrowBlockFile(block_count)) {
    523     if (kMaxBlocks == header->max_entries) {
    524       file = NextFile(file);
    525       if (!file)
    526         return NULL;
    527       header = BlockHeader(file);
    528       continue;
    529     }
    530 
    531     if (!GrowBlockFile(file, header.Get()))
    532       return NULL;
    533     break;
    534   }
    535   HISTOGRAM_TIMES("DiskCache.GetFileForNewBlock", TimeTicks::Now() - start);
    536   return file;
    537 }
    538 
    539 MappedFile* BlockFiles::NextFile(MappedFile* file) {
    540   ScopedFlush flush(file);
    541   BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer());
    542   int new_file = header->next_file;
    543   if (!new_file) {
    544     // RANKINGS is not reported as a type for small entries, but we may be
    545     // extending the rankings block file.
    546     FileType type = Addr::RequiredFileType(header->entry_size);
    547     if (header->entry_size == Addr::BlockSizeForFileType(RANKINGS))
    548       type = RANKINGS;
    549 
    550     new_file = CreateNextBlockFile(type);
    551     if (!new_file)
    552       return NULL;
    553 
    554     FileLock lock(header);
    555     header->next_file = new_file;
    556   }
    557 
    558   // Only the block_file argument is relevant for what we want.
    559   Addr address(BLOCK_256, 1, new_file, 0);
    560   return GetFile(address);
    561 }
    562 
    563 int BlockFiles::CreateNextBlockFile(FileType block_type) {
    564   for (int i = kFirstAdditionalBlockFile; i <= kMaxBlockFile; i++) {
    565     if (CreateBlockFile(i, block_type, false))
    566       return i;
    567   }
    568   return 0;
    569 }
    570 
    571 // We walk the list of files for this particular block type, deleting the ones
    572 // that are empty.
    573 bool BlockFiles::RemoveEmptyFile(FileType block_type) {
    574   MappedFile* file = block_files_[block_type - 1];
    575   BlockFileHeader* header = reinterpret_cast<BlockFileHeader*>(file->buffer());
    576 
    577   while (header->next_file) {
    578     // Only the block_file argument is relevant for what we want.
    579     Addr address(BLOCK_256, 1, header->next_file, 0);
    580     MappedFile* next_file = GetFile(address);
    581     if (!next_file)
    582       return false;
    583 
    584     BlockFileHeader* next_header =
    585         reinterpret_cast<BlockFileHeader*>(next_file->buffer());
    586     if (!next_header->num_entries) {
    587       DCHECK_EQ(next_header->entry_size, header->entry_size);
    588       // Delete next_file and remove it from the chain.
    589       int file_index = header->next_file;
    590       header->next_file = next_header->next_file;
    591       DCHECK(block_files_.size() >= static_cast<unsigned int>(file_index));
    592       file->Flush();
    593 
    594       // We get a new handle to the file and release the old one so that the
    595       // file gets unmmaped... so we can delete it.
    596       base::FilePath name = Name(file_index);
    597       scoped_refptr<File> this_file(new File(false));
    598       this_file->Init(name);
    599       block_files_[file_index]->Release();
    600       block_files_[file_index] = NULL;
    601 
    602       int failure = DeleteCacheFile(name) ? 0 : 1;
    603       UMA_HISTOGRAM_COUNTS("DiskCache.DeleteFailed2", failure);
    604       if (failure)
    605         LOG(ERROR) << "Failed to delete " << name.value() << " from the cache.";
    606       continue;
    607     }
    608 
    609     header = next_header;
    610     file = next_file;
    611   }
    612   return true;
    613 }
    614 
    615 // Note that we expect to be called outside of a FileLock... however, we cannot
    616 // DCHECK on header->updating because we may be fixing a crash.
    617 bool BlockFiles::FixBlockFileHeader(MappedFile* file) {
    618   ScopedFlush flush(file);
    619   BlockHeader header(file);
    620   int file_size = static_cast<int>(file->GetLength());
    621   if (file_size < header.Size())
    622     return false;  // file_size > 2GB is also an error.
    623 
    624   const int kMinBlockSize = 36;
    625   const int kMaxBlockSize = 4096;
    626   if (header->entry_size < kMinBlockSize ||
    627       header->entry_size > kMaxBlockSize || header->num_entries < 0)
    628     return false;
    629 
    630   // Make sure that we survive crashes.
    631   header->updating = 1;
    632   int expected = header->entry_size * header->max_entries + header.Size();
    633   if (file_size != expected) {
    634     int max_expected = header->entry_size * kMaxBlocks + header.Size();
    635     if (file_size < expected || header->empty[3] || file_size > max_expected) {
    636       NOTREACHED();
    637       LOG(ERROR) << "Unexpected file size";
    638       return false;
    639     }
    640     // We were in the middle of growing the file.
    641     int num_entries = (file_size - header.Size()) / header->entry_size;
    642     header->max_entries = num_entries;
    643   }
    644 
    645   header.FixAllocationCounters();
    646   int empty_blocks = header.EmptyBlocks();
    647   if (empty_blocks + header->num_entries > header->max_entries)
    648     header->num_entries = header->max_entries - empty_blocks;
    649 
    650   if (!header.ValidateCounters())
    651     return false;
    652 
    653   header->updating = 0;
    654   return true;
    655 }
    656 
    657 // We are interested in the total number of blocks used by this file type, and
    658 // the max number of blocks that we can store (reported as the percentage of
    659 // used blocks). In order to find out the number of used blocks, we have to
    660 // substract the empty blocks from the total blocks for each file in the chain.
    661 void BlockFiles::GetFileStats(int index, int* used_count, int* load) {
    662   int max_blocks = 0;
    663   *used_count = 0;
    664   *load = 0;
    665   for (;;) {
    666     if (!block_files_[index] && !OpenBlockFile(index))
    667       return;
    668 
    669     BlockFileHeader* header =
    670         reinterpret_cast<BlockFileHeader*>(block_files_[index]->buffer());
    671 
    672     max_blocks += header->max_entries;
    673     int used = header->max_entries;
    674     for (int i = 0; i < 4; i++) {
    675       used -= header->empty[i] * (i + 1);
    676       DCHECK_GE(used, 0);
    677     }
    678     *used_count += used;
    679 
    680     if (!header->next_file)
    681       break;
    682     index = header->next_file;
    683   }
    684   if (max_blocks)
    685     *load = *used_count * 100 / max_blocks;
    686 }
    687 
    688 base::FilePath BlockFiles::Name(int index) {
    689   // The file format allows for 256 files.
    690   DCHECK(index < 256 || index >= 0);
    691   std::string tmp = base::StringPrintf("%s%d", kBlockName, index);
    692   return path_.AppendASCII(tmp);
    693 }
    694 
    695 }  // namespace disk_cache
    696