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