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