Separate Simple Backend creation from initialization.
[chromium-blink-merge.git] / net / disk_cache / simple / simple_synchronous_entry.cc
blobd3091d1be6446b6e85996438dee2076e893d9607
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.
5 #include "net/disk_cache/simple/simple_synchronous_entry.h"
7 #include <algorithm>
8 #include <cstring>
9 #include <limits>
11 #include "base/basictypes.h"
12 #include "base/compiler_specific.h"
13 #include "base/file_util.h"
14 #include "base/hash.h"
15 #include "base/location.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/message_loop_proxy.h"
18 #include "base/sha1.h"
19 #include "base/stringprintf.h"
20 #include "base/task_runner.h"
21 #include "net/base/io_buffer.h"
22 #include "net/base/net_errors.h"
24 using base::ClosePlatformFile;
25 using base::FilePath;
26 using base::GetPlatformFileInfo;
27 using base::PlatformFileError;
28 using base::PlatformFileInfo;
29 using base::PLATFORM_FILE_CREATE;
30 using base::PLATFORM_FILE_OK;
31 using base::PLATFORM_FILE_OPEN;
32 using base::PLATFORM_FILE_READ;
33 using base::PLATFORM_FILE_WRITE;
34 using base::ReadPlatformFile;
35 using base::TaskRunner;
36 using base::Time;
37 using base::TruncatePlatformFile;
38 using base::WritePlatformFile;
40 namespace {
42 std::string GetFilenameForKeyAndIndex(const std::string& key, int index) {
43 return disk_cache::GetEntryHashForKey(key) +
44 base::StringPrintf("_%1d", index);
47 int32 DataSizeFromKeyAndFileSize(size_t key_size, int64 file_size) {
48 int64 data_size = file_size - key_size - sizeof(disk_cache::SimpleFileHeader);
49 DCHECK_GE(implicit_cast<int64>(std::numeric_limits<int32>::max()), data_size);
50 return data_size;
53 int64 FileOffsetFromDataOffset(size_t key_size, int data_offset) {
54 const int64 headers_size = sizeof(disk_cache::SimpleFileHeader) + key_size;
55 return headers_size + data_offset;
58 } // namespace
60 namespace disk_cache {
62 // static
63 void SimpleSynchronousEntry::OpenEntry(
64 const FilePath& path,
65 const std::string& key,
66 const scoped_refptr<TaskRunner>& callback_runner,
67 const SynchronousCreationCallback& callback) {
68 SimpleSynchronousEntry* sync_entry =
69 new SimpleSynchronousEntry(callback_runner, path, key);
71 if (!sync_entry->InitializeForOpen()) {
72 delete sync_entry;
73 sync_entry = NULL;
75 callback_runner->PostTask(FROM_HERE, base::Bind(callback, sync_entry));
78 // static
79 void SimpleSynchronousEntry::CreateEntry(
80 const FilePath& path,
81 const std::string& key,
82 const scoped_refptr<TaskRunner>& callback_runner,
83 const SynchronousCreationCallback& callback) {
84 SimpleSynchronousEntry* sync_entry =
85 new SimpleSynchronousEntry(callback_runner, path, key);
86 if (!sync_entry->InitializeForCreate()) {
87 delete sync_entry;
88 sync_entry = NULL;
90 callback_runner->PostTask(FROM_HERE, base::Bind(callback, sync_entry));
93 // static
94 void SimpleSynchronousEntry::DoomEntry(
95 const FilePath& path,
96 const std::string& key,
97 scoped_refptr<TaskRunner> callback_runner,
98 const net::CompletionCallback& callback) {
99 for (int i = 0; i < kSimpleEntryFileCount; ++i) {
100 FilePath to_delete = path.AppendASCII(GetFilenameForKeyAndIndex(key, i));
101 bool ALLOW_UNUSED result = file_util::Delete(to_delete, false);
102 DLOG_IF(ERROR, !result) << "Could not delete " << to_delete.MaybeAsASCII();
104 if (!callback.is_null())
105 callback_runner->PostTask(FROM_HERE, base::Bind(callback, net::OK));
108 void SimpleSynchronousEntry::Close() {
109 for (int i = 0; i < kSimpleEntryFileCount; ++i) {
110 bool ALLOW_UNUSED result = ClosePlatformFile(files_[i]);
111 DLOG_IF(INFO, !result) << "Could not Close() file.";
113 delete this;
116 void SimpleSynchronousEntry::ReadData(
117 int index,
118 int offset,
119 net::IOBuffer* buf,
120 int buf_len,
121 const SynchronousOperationCallback& callback) {
122 DCHECK(initialized_);
124 int64 file_offset = FileOffsetFromDataOffset(key_.size(), offset);
125 int bytes_read = ReadPlatformFile(files_[index], file_offset,
126 buf->data(), buf_len);
127 if (bytes_read > 0)
128 last_used_ = Time::Now();
129 int result = (bytes_read >= 0) ? bytes_read : net::ERR_FAILED;
130 callback_runner_->PostTask(FROM_HERE, base::Bind(callback, result));
133 void SimpleSynchronousEntry::WriteData(
134 int index,
135 int offset,
136 net::IOBuffer* buf,
137 int buf_len,
138 const SynchronousOperationCallback& callback,
139 bool truncate) {
140 DCHECK(initialized_);
142 int64 file_offset = FileOffsetFromDataOffset(key_.size(), offset);
143 if (buf_len > 0) {
144 if (WritePlatformFile(files_[index], file_offset, buf->data(), buf_len) !=
145 buf_len) {
146 callback_runner_->PostTask(FROM_HERE,
147 base::Bind(callback, net::ERR_FAILED));
148 return;
150 data_size_[index] = std::max(data_size_[index], offset + buf_len);
152 if (truncate) {
153 data_size_[index] = offset + buf_len;
154 if (!TruncatePlatformFile(files_[index], file_offset + buf_len)) {
155 callback_runner_->PostTask(FROM_HERE,
156 base::Bind(callback, net::ERR_FAILED));
157 return;
160 last_modified_ = Time::Now();
161 callback_runner_->PostTask(FROM_HERE, base::Bind(callback, buf_len));
164 SimpleSynchronousEntry::SimpleSynchronousEntry(
165 const scoped_refptr<TaskRunner>& callback_runner,
166 const FilePath& path,
167 const std::string& key)
168 : callback_runner_(callback_runner),
169 path_(path),
170 key_(key),
171 initialized_(false) {
174 SimpleSynchronousEntry::~SimpleSynchronousEntry() {
177 bool SimpleSynchronousEntry::OpenOrCreateFiles(bool create) {
178 for (int i = 0; i < kSimpleEntryFileCount; ++i) {
179 FilePath filename = path_.AppendASCII(GetFilenameForKeyAndIndex(key_, i));
180 int flags = PLATFORM_FILE_READ | PLATFORM_FILE_WRITE;
181 if (create)
182 flags |= PLATFORM_FILE_CREATE;
183 else
184 flags |= PLATFORM_FILE_OPEN;
185 PlatformFileError error;
186 files_[i] = CreatePlatformFile(filename, flags, NULL, &error);
187 if (error != PLATFORM_FILE_OK) {
188 DVLOG(8) << "CreatePlatformFile error " << error << " while "
189 << (create ? "creating " : "opening ")
190 << filename.MaybeAsASCII();
191 while (--i >= 0) {
192 bool ALLOW_UNUSED did_close = ClosePlatformFile(files_[i]);
193 DLOG_IF(INFO, !did_close) << "Could not close file "
194 << filename.MaybeAsASCII();
196 return false;
200 for (int i = 0; i < kSimpleEntryFileCount; ++i) {
201 PlatformFileInfo file_info;
202 bool success = GetPlatformFileInfo(files_[i], &file_info);
203 if (!success) {
204 DLOG(WARNING) << "Could not get platform file info.";
205 continue;
207 last_used_ = std::max(last_used_, file_info.last_accessed);
208 last_modified_ = std::max(last_modified_, file_info.last_modified);
209 data_size_[i] = DataSizeFromKeyAndFileSize(key_.size(), file_info.size);
212 return true;
215 bool SimpleSynchronousEntry::InitializeForOpen() {
216 DCHECK(!initialized_);
217 if (!OpenOrCreateFiles(false))
218 return false;
220 for (int i = 0; i < kSimpleEntryFileCount; ++i) {
221 SimpleFileHeader header;
222 int header_read_result =
223 ReadPlatformFile(files_[i], 0, reinterpret_cast<char*>(&header),
224 sizeof(header));
225 if (header_read_result != sizeof(header)) {
226 DLOG(WARNING) << "Cannot read header from entry.";
227 return false;
230 if (header.initial_magic_number != kSimpleInitialMagicNumber) {
231 // TODO(gavinp): This seems very bad; for now we log at WARNING, but we
232 // should give consideration to not saturating the log with these if that
233 // becomes a problem.
234 DLOG(WARNING) << "Magic number did not match.";
235 return false;
238 if (header.version != kSimpleVersion) {
239 DLOG(WARNING) << "Unreadable version.";
240 return false;
243 scoped_ptr<char[]> key(new char[header.key_length]);
244 int key_read_result = ReadPlatformFile(files_[i], sizeof(header),
245 key.get(), header.key_length);
246 if (key_read_result != implicit_cast<int>(header.key_length)) {
247 DLOG(WARNING) << "Cannot read key from entry.";
248 return false;
250 if (header.key_length != key_.size() ||
251 std::memcmp(key_.data(), key.get(), key_.size()) != 0) {
252 // TODO(gavinp): Since the way we use Entry SHA to name entries means this
253 // is expected to occur at some frequency, add unit_tests that this does
254 // is handled gracefully at higher levels.
255 DLOG(WARNING) << "Key mismatch on open.";
256 return false;
259 if (base::Hash(key.get(), header.key_length) != header.key_hash) {
260 DLOG(WARNING) << "Hash mismatch on key.";
261 return false;
265 initialized_ = true;
266 return true;
269 bool SimpleSynchronousEntry::InitializeForCreate() {
270 DCHECK(!initialized_);
271 if (!OpenOrCreateFiles(true)) {
272 DLOG(WARNING) << "Could not create platform files.";
273 return false;
275 for (int i = 0; i < kSimpleEntryFileCount; ++i) {
276 SimpleFileHeader header;
277 header.initial_magic_number = kSimpleInitialMagicNumber;
278 header.version = kSimpleVersion;
280 header.key_length = key_.size();
281 header.key_hash = base::Hash(key_);
283 if (WritePlatformFile(files_[i], 0, reinterpret_cast<char*>(&header),
284 sizeof(header)) != sizeof(header)) {
285 // TODO(gavinp): Clean up created files.
286 DLOG(WARNING) << "Could not write headers to new cache entry.";
287 return false;
290 if (WritePlatformFile(files_[i], sizeof(header), key_.data(),
291 key_.size()) != implicit_cast<int>(key_.size())) {
292 // TODO(gavinp): Clean up created files.
293 DLOG(WARNING) << "Could not write keys to new cache entry.";
294 return false;
297 initialized_ = true;
298 return true;
301 } // namespace disk_cache