Home | History | Annotate | Download | only in simple
      1 // Copyright 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_version_upgrade.h"
      6 
      7 #include <cstring>
      8 
      9 #include "base/files/file.h"
     10 #include "base/files/file_path.h"
     11 #include "base/files/file_util.h"
     12 #include "base/files/memory_mapped_file.h"
     13 #include "base/logging.h"
     14 #include "base/pickle.h"
     15 #include "net/disk_cache/simple/simple_backend_version.h"
     16 #include "net/disk_cache/simple/simple_entry_format_history.h"
     17 #include "third_party/zlib/zlib.h"
     18 
     19 namespace {
     20 
     21 // It is not possible to upgrade cache structures on disk that are of version
     22 // below this, the entire cache should be dropped for them.
     23 const uint32 kMinVersionAbleToUpgrade = 5;
     24 
     25 const char kFakeIndexFileName[] = "index";
     26 const char kIndexFileName[] = "the-real-index";
     27 
     28 void LogMessageFailedUpgradeFromVersion(int version) {
     29   LOG(ERROR) << "Failed to upgrade Simple Cache from version: " << version;
     30 }
     31 
     32 bool WriteFakeIndexFile(const base::FilePath& file_name) {
     33   base::File file(file_name,  base::File::FLAG_CREATE | base::File::FLAG_WRITE);
     34   if (!file.IsValid())
     35     return false;
     36 
     37   disk_cache::FakeIndexData file_contents;
     38   file_contents.initial_magic_number =
     39       disk_cache::simplecache_v5::kSimpleInitialMagicNumber;
     40   file_contents.version = disk_cache::kSimpleVersion;
     41   int bytes_written = file.Write(0, reinterpret_cast<char*>(&file_contents),
     42                                  sizeof(file_contents));
     43   if (bytes_written != sizeof(file_contents)) {
     44     LOG(ERROR) << "Failed to write fake index file: "
     45                << file_name.LossyDisplayName();
     46     return false;
     47   }
     48   return true;
     49 }
     50 
     51 }  // namespace
     52 
     53 namespace disk_cache {
     54 
     55 FakeIndexData::FakeIndexData() {
     56   // Make hashing repeatable: leave no padding bytes untouched.
     57   std::memset(this, 0, sizeof(*this));
     58 }
     59 
     60 // Migrates the cache directory from version 4 to version 5.
     61 // Returns true iff it succeeds.
     62 //
     63 // The V5 and V6 caches differ in the name of the index file (it moved to a
     64 // subdirectory) and in the file format (directory last-modified time observed
     65 // by the index writer has gotten appended to the pickled format).
     66 //
     67 // To keep complexity small this specific upgrade code *deletes* the old index
     68 // file. The directory for the new index file has to be created lazily anyway,
     69 // so it is not done in the upgrader.
     70 //
     71 // Below is the detailed description of index file format differences. It is for
     72 // reference purposes. This documentation would be useful to move closer to the
     73 // next index upgrader when the latter gets introduced.
     74 //
     75 // Path:
     76 //   V5: $cachedir/the-real-index
     77 //   V6: $cachedir/index-dir/the-real-index
     78 //
     79 // Pickled file format:
     80 //   Both formats extend Pickle::Header by 32bit value of the CRC-32 of the
     81 //   pickled data.
     82 //   <v5-index> ::= <v5-index-metadata> <entry-info>*
     83 //   <v5-index-metadata> ::= UInt64(kSimpleIndexMagicNumber)
     84 //                           UInt32(4)
     85 //                           UInt64(<number-of-entries>)
     86 //                           UInt64(<cache-size-in-bytes>)
     87 //   <entry-info> ::= UInt64(<hash-of-the-key>)
     88 //                    Int64(<entry-last-used-time>)
     89 //                    UInt64(<entry-size-in-bytes>)
     90 //   <v6-index> ::= <v6-index-metadata>
     91 //                  <entry-info>*
     92 //                  Int64(<cache-dir-mtime>)
     93 //   <v6-index-metadata> ::= UInt64(kSimpleIndexMagicNumber)
     94 //                           UInt32(5)
     95 //                           UInt64(<number-of-entries>)
     96 //                           UInt64(<cache-size-in-bytes>)
     97 //   Where:
     98 //     <entry-size-in-bytes> is equal the sum of all file sizes of the entry.
     99 //     <cache-dir-mtime> is the last modification time with nanosecond precision
    100 //       of the directory, where all files for entries are stored.
    101 //     <hash-of-the-key> represent the first 64 bits of a SHA-1 of the key.
    102 bool UpgradeIndexV5V6(const base::FilePath& cache_directory) {
    103   const base::FilePath old_index_file =
    104       cache_directory.AppendASCII(kIndexFileName);
    105   if (!base::DeleteFile(old_index_file, /* recursive = */ false))
    106     return false;
    107   return true;
    108 }
    109 
    110 // Some points about the Upgrade process are still not clear:
    111 // 1. if the upgrade path requires dropping cache it would be faster to just
    112 //    return an initialization error here and proceed with asynchronous cache
    113 //    cleanup in CacheCreator. Should this hack be considered valid? Some smart
    114 //    tests may fail.
    115 // 2. Because Android process management allows for killing a process at any
    116 //    time, the upgrade process may need to deal with a partially completed
    117 //    previous upgrade. For example, while upgrading A -> A + 2 we are the
    118 //    process gets killed and some parts are remaining at version A + 1. There
    119 //    are currently no generic mechanisms to resolve this situation, co the
    120 //    upgrade codes need to ensure they can continue after being stopped in the
    121 //    middle. It also means that the "fake index" must be flushed in between the
    122 //    upgrade steps. Atomicity of this is an interesting research topic. The
    123 //    intermediate fake index flushing must be added as soon as we add more
    124 //    upgrade steps.
    125 bool UpgradeSimpleCacheOnDisk(const base::FilePath& path) {
    126   // There is a convention among disk cache backends: looking at the magic in
    127   // the file "index" it should be sufficient to determine if the cache belongs
    128   // to the currently running backend. The Simple Backend stores its index in
    129   // the file "the-real-index" (see simple_index_file.cc) and the file "index"
    130   // only signifies presence of the implementation's magic and version. There
    131   // are two reasons for that:
    132   // 1. Absence of the index is itself not a fatal error in the Simple Backend
    133   // 2. The Simple Backend has pickled file format for the index making it hacky
    134   //    to have the magic in the right place.
    135   const base::FilePath fake_index = path.AppendASCII(kFakeIndexFileName);
    136   base::File fake_index_file(fake_index,
    137                              base::File::FLAG_OPEN | base::File::FLAG_READ);
    138 
    139   if (!fake_index_file.IsValid()) {
    140     if (fake_index_file.error_details() == base::File::FILE_ERROR_NOT_FOUND) {
    141       return WriteFakeIndexFile(fake_index);
    142     }
    143     return false;
    144   }
    145 
    146   FakeIndexData file_header;
    147   int bytes_read = fake_index_file.Read(0,
    148                                         reinterpret_cast<char*>(&file_header),
    149                                         sizeof(file_header));
    150   if (bytes_read != sizeof(file_header) ||
    151       file_header.initial_magic_number !=
    152           disk_cache::simplecache_v5::kSimpleInitialMagicNumber) {
    153     LOG(ERROR) << "File structure does not match the disk cache backend.";
    154     return false;
    155   }
    156   fake_index_file.Close();
    157 
    158   uint32 version_from = file_header.version;
    159   if (version_from < kMinVersionAbleToUpgrade ||
    160       version_from > kSimpleVersion) {
    161     LOG(ERROR) << "Inconsistent cache version.";
    162     return false;
    163   }
    164   bool upgrade_needed = (version_from != kSimpleVersion);
    165   if (version_from == kMinVersionAbleToUpgrade) {
    166     // Upgrade only the index for V4 -> V5 move.
    167     if (!UpgradeIndexV5V6(path)) {
    168       LogMessageFailedUpgradeFromVersion(file_header.version);
    169       return false;
    170     }
    171     version_from++;
    172   }
    173   if (version_from == kSimpleVersion) {
    174     if (!upgrade_needed) {
    175       return true;
    176     } else {
    177       const base::FilePath temp_fake_index = path.AppendASCII("upgrade-index");
    178       if (!WriteFakeIndexFile(temp_fake_index)) {
    179         base::DeleteFile(temp_fake_index, /* recursive = */ false);
    180         LOG(ERROR) << "Failed to write a new fake index.";
    181         LogMessageFailedUpgradeFromVersion(file_header.version);
    182         return false;
    183       }
    184       if (!base::ReplaceFile(temp_fake_index, fake_index, NULL)) {
    185         LOG(ERROR) << "Failed to replace the fake index.";
    186         LogMessageFailedUpgradeFromVersion(file_header.version);
    187         return false;
    188       }
    189       return true;
    190     }
    191   }
    192   // Verify during the test stage that the upgraders are implemented for all
    193   // versions. The release build would cause backend initialization failure
    194   // which would then later lead to removing all files known to the backend.
    195   DCHECK_EQ(kSimpleVersion, version_from);
    196   return false;
    197 }
    198 
    199 }  // namespace disk_cache
    200