Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 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_index_file.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/bind_helpers.h" | |
| 9 #include "base/file_util.h" | |
| 10 #include "base/hash.h" | |
| 11 #include "base/logging.h" | |
| 12 #include "base/message_loop.h" | |
| 13 #include "base/pickle.h" | |
| 14 #include "base/task_runner.h" | |
| 15 #include "net/disk_cache/simple/simple_disk_format.h" | |
| 16 #include "net/disk_cache/simple/simple_index_util.h" | |
| 17 #include "third_party/zlib/zlib.h" | |
| 18 | |
| 19 namespace { | |
| 20 | |
| 21 const uint64 kMaxEntiresInIndex = 100000000; | |
| 22 | |
| 23 } // namespace | |
| 24 | |
| 25 namespace disk_cache { | |
| 26 | |
| 27 SimpleIndexFile::IndexMetadata::IndexMetadata() : | |
| 28 initial_magic_number_(kSimpleIndexInitialMagicNumber), | |
| 29 version_(kSimpleVersion), | |
| 30 number_of_entries_(0), | |
| 31 cache_size_(0) {} | |
| 32 | |
| 33 SimpleIndexFile::IndexMetadata::IndexMetadata( | |
| 34 uint64 number_of_entries, uint64 cache_size) : | |
| 35 initial_magic_number_(kSimpleIndexInitialMagicNumber), | |
| 36 version_(kSimpleVersion), | |
| 37 number_of_entries_(number_of_entries), | |
| 38 cache_size_(cache_size) {} | |
| 39 | |
| 40 void SimpleIndexFile::IndexMetadata::Serialize(Pickle* pickle) const { | |
| 41 DCHECK(pickle); | |
| 42 pickle->WriteUInt64(initial_magic_number_); | |
| 43 pickle->WriteUInt32(version_); | |
| 44 pickle->WriteUInt64(number_of_entries_); | |
| 45 pickle->WriteUInt64(cache_size_); | |
| 46 } | |
| 47 | |
| 48 bool SimpleIndexFile::IndexMetadata::DeSerialize(PickleIterator* it, | |
| 49 IndexMetadata* out) { | |
| 50 DCHECK(it); | |
| 51 DCHECK(out); | |
| 52 return it->ReadUInt64(&(out->initial_magic_number_)) && | |
| 53 it->ReadUInt32(&(out->version_)) && | |
| 54 it->ReadUInt64(&(out->number_of_entries_))&& | |
| 55 it->ReadUInt64(&(out->cache_size_)); | |
| 56 } | |
| 57 | |
| 58 bool SimpleIndexFile::IndexMetadata::CheckIndexMetadata() { | |
| 59 return number_of_entries_ <= kMaxEntiresInIndex && | |
| 60 initial_magic_number_ == disk_cache::kSimpleIndexInitialMagicNumber && | |
| 61 version_ == disk_cache::kSimpleVersion; | |
| 62 } | |
| 63 | |
| 64 // static | |
| 65 void SimpleIndexFile::LoadFromDisk( | |
| 66 const base::FilePath& index_filename, | |
| 67 const scoped_refptr<base::TaskRunner>& callback_runner, | |
| 68 const IndexCompletionCallback& completion_callback) { | |
| 69 std::string contents; | |
| 70 if(!file_util::ReadFileToString(index_filename, | |
| 71 &contents)) { | |
| 72 LOG(WARNING) << "Could not read Simple Index file."; | |
| 73 return RestoreFromDisk( | |
| 74 index_filename, callback_runner, completion_callback); | |
| 75 } | |
| 76 | |
| 77 Pickle pickle (contents.data(), contents.size()); | |
| 78 PickleIterator pickle_it(pickle); | |
| 79 | |
| 80 SimpleIndexFile::PickleHeader* header_p = | |
| 81 pickle.headerT<SimpleIndexFile::PickleHeader>(); | |
| 82 const uint32 crc_read = header_p->crc; | |
| 83 const uint32 crc_calculated = CalculatePickleCRC(pickle); | |
| 84 | |
| 85 if (crc_read != crc_calculated) { | |
| 86 LOG(WARNING) << "Invalid CRC in Simple Index file."; | |
| 87 return RestoreFromDisk( | |
| 88 index_filename, callback_runner, completion_callback); | |
| 89 } | |
| 90 | |
| 91 SimpleIndexFile::IndexMetadata index_metadata; | |
| 92 if (!SimpleIndexFile::IndexMetadata::DeSerialize( | |
| 93 &pickle_it, &index_metadata)) { | |
| 94 LOG(ERROR) << "Invalid index_metadata on Simple Cache Index."; | |
| 95 return RestoreFromDisk( | |
| 96 index_filename, callback_runner, completion_callback); | |
| 97 } | |
| 98 | |
| 99 if (!index_metadata.CheckIndexMetadata()) { | |
| 100 LOG(ERROR) << "Invalid index_metadata on Simple Cache Index."; | |
| 101 return RestoreFromDisk( | |
| 102 index_filename, callback_runner, completion_callback); | |
| 103 } | |
| 104 | |
| 105 scoped_ptr<EntrySet> index_file_entries(new EntrySet()); | |
| 106 while (index_file_entries->size() < index_metadata.GetNumberOfEntries()) { | |
| 107 EntryMetadata entry_metadata; | |
| 108 if (!EntryMetadata::DeSerialize(&pickle_it, &entry_metadata)) { | |
| 109 LOG(WARNING) << "Invalid EntryMetadata in Simple Index file."; | |
| 110 return RestoreFromDisk( | |
| 111 index_filename, callback_runner, completion_callback); | |
| 112 } | |
| 113 InsertInEntrySet(index_file_entries.get(), entry_metadata); | |
| 114 } | |
| 115 callback_runner->PostTask(FROM_HERE, | |
| 116 base::Bind(completion_callback, | |
|
pasko-google - do not use
2013/04/15 14:23:55
can we please make pickling un-pickling independen
felipeg
2013/04/15 15:37:04
Done.
| |
| 117 base::Passed(&index_file_entries))); | |
| 118 } | |
| 119 | |
| 120 // static | |
| 121 void SimpleIndexFile::RestoreFromDisk( | |
| 122 const base::FilePath& index_filename, | |
| 123 const scoped_refptr<base::TaskRunner>& callback_runner, | |
| 124 const IndexCompletionCallback& completion_callback) { | |
| 125 using file_util::FileEnumerator; | |
| 126 LOG(INFO) << "Simple Cache Index is being restored from disk."; | |
| 127 | |
| 128 file_util::Delete(index_filename, /* recursive = */ false); | |
| 129 scoped_ptr<EntrySet> index_file_entries(new EntrySet()); | |
| 130 | |
| 131 // TODO(felipeg,gavinp): Fix this once we have a one-file per entry format. | |
| 132 COMPILE_ASSERT(kSimpleEntryFileCount == 3, | |
| 133 file_pattern_must_match_file_count); | |
| 134 const base::FilePath::StringType file_pattern = FILE_PATH_LITERAL("*_[0-2]"); | |
| 135 FileEnumerator enumerator(index_filename.DirName(), | |
| 136 false /* recursive */, | |
| 137 FileEnumerator::FILES, | |
| 138 file_pattern); | |
| 139 for (base::FilePath file_path = enumerator.Next(); !file_path.empty(); | |
| 140 file_path = enumerator.Next()) { | |
| 141 const base::FilePath::StringType base_name = file_path.BaseName().value(); | |
| 142 // Converting to std::string is OK since we never use UTF8 wide chars in our | |
| 143 // file names. | |
| 144 const std::string hash_name(base_name.begin(), base_name.end()); | |
| 145 const std::string hash_key_string = | |
| 146 hash_name.substr(0, kEntryHashKeyAsHexStringSize); | |
| 147 uint64 hash_key = 0; | |
| 148 if (!GetEntryHashKeyFromHexString(hash_key_string, &hash_key)) { | |
| 149 LOG(WARNING) << "Invalid Entry Hash Key filename while restoring " | |
| 150 << "Simple Index from disk: " << hash_name; | |
| 151 // TODO(felipeg): Should we delete the invalid file here ? | |
| 152 continue; | |
| 153 } | |
| 154 | |
| 155 FileEnumerator::FindInfo find_info = {}; | |
| 156 enumerator.GetFindInfo(&find_info); | |
| 157 base::Time last_used_time; | |
| 158 #if defined(OS_POSIX) | |
| 159 // For POSIX systems, a last access time is available. However, it's not | |
| 160 // guaranteed to be more accurate than mtime. It is no worse though. | |
| 161 last_used_time = base::Time::FromTimeT(find_info.stat.st_atime); | |
| 162 #endif | |
| 163 if (last_used_time.is_null()) | |
| 164 last_used_time = FileEnumerator::GetLastModifiedTime(find_info); | |
| 165 | |
| 166 int64 file_size = FileEnumerator::GetFilesize(find_info); | |
| 167 EntrySet::iterator it = index_file_entries->find(hash_key); | |
| 168 if (it == index_file_entries->end()) { | |
| 169 InsertInEntrySet(index_file_entries.get(), EntryMetadata( | |
| 170 hash_key, last_used_time, file_size)); | |
| 171 } else { | |
| 172 // Summing up the total size of the entry through all the *_[0-2] files | |
| 173 it->second.SetEntrySize(it->second.GetEntrySize() + file_size); | |
| 174 } | |
| 175 } | |
| 176 | |
| 177 callback_runner->PostTask(FROM_HERE, | |
| 178 base::Bind(completion_callback, | |
| 179 base::Passed(&index_file_entries))); | |
| 180 } | |
| 181 | |
| 182 // static | |
| 183 scoped_ptr<Pickle> SimpleIndexFile::Serialize( | |
| 184 const SimpleIndexFile::IndexMetadata& index_metadata, | |
| 185 const EntrySet& entries) { | |
| 186 scoped_ptr<Pickle> pickle(new Pickle(sizeof(SimpleIndexFile::PickleHeader))); | |
| 187 | |
| 188 // Serialize the |index_metadata|. | |
| 189 index_metadata.Serialize(pickle.get()); | |
| 190 // Then all the entries from |entries| set. | |
| 191 for (EntrySet::const_iterator it = entries.begin(); | |
| 192 it != entries.end(); ++it) { | |
| 193 it->second.Serialize(pickle.get()); | |
| 194 } | |
| 195 SimpleIndexFile::PickleHeader* header_p = | |
| 196 pickle->headerT<SimpleIndexFile::PickleHeader>(); | |
| 197 header_p->crc = CalculatePickleCRC(*pickle); | |
| 198 return pickle.Pass(); | |
| 199 } | |
| 200 | |
| 201 // static | |
| 202 void SimpleIndexFile::WriteToDisk(const base::FilePath& index_filename, | |
| 203 scoped_ptr<Pickle> pickle) { | |
| 204 const base::FilePath temp_filename = | |
| 205 index_filename.DirName().AppendASCII("index_temp"); | |
| 206 int bytes_written = file_util::WriteFile( | |
| 207 temp_filename, | |
| 208 reinterpret_cast<const char*>(pickle->data()), | |
| 209 pickle->size()); | |
| 210 DCHECK_EQ(bytes_written, implicit_cast<int>(pickle->size())); | |
| 211 if (bytes_written != static_cast<int>(pickle->size())) { | |
| 212 // TODO(felipeg): Add better error handling. | |
| 213 LOG(ERROR) << "Could not write Simple Cache index to temporary file: " | |
| 214 << temp_filename.value(); | |
| 215 file_util::Delete(temp_filename, /* recursive = */ false); | |
| 216 return; | |
| 217 } | |
| 218 // Swap temp and index_file. | |
| 219 bool result = file_util::ReplaceFile(temp_filename, index_filename); | |
| 220 DCHECK(result); | |
| 221 } | |
| 222 | |
| 223 // static | |
| 224 uint32 SimpleIndexFile::CalculatePickleCRC(const Pickle& pickle) { | |
| 225 return crc32(crc32(0, Z_NULL, 0), | |
| 226 reinterpret_cast<const Bytef*>(pickle.payload()), | |
| 227 implicit_cast<uInt>(pickle.payload_size())); | |
| 228 } | |
| 229 | |
| 230 } // namespace disk_cache | |
| OLD | NEW |