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"
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
;
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
;
37 using base::TruncatePlatformFile
;
38 using base::WritePlatformFile
;
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
);
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
;
60 namespace disk_cache
{
63 void SimpleSynchronousEntry::OpenEntry(
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()) {
75 callback_runner
->PostTask(FROM_HERE
, base::Bind(callback
, sync_entry
));
79 void SimpleSynchronousEntry::CreateEntry(
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()) {
90 callback_runner
->PostTask(FROM_HERE
, base::Bind(callback
, sync_entry
));
94 void SimpleSynchronousEntry::DoomEntry(
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.";
116 void SimpleSynchronousEntry::ReadData(
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
);
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(
138 const SynchronousOperationCallback
& callback
,
140 DCHECK(initialized_
);
142 int64 file_offset
= FileOffsetFromDataOffset(key_
.size(), offset
);
144 if (WritePlatformFile(files_
[index
], file_offset
, buf
->data(), buf_len
) !=
146 callback_runner_
->PostTask(FROM_HERE
,
147 base::Bind(callback
, net::ERR_FAILED
));
150 data_size_
[index
] = std::max(data_size_
[index
], offset
+ buf_len
);
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
));
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
),
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
;
182 flags
|= PLATFORM_FILE_CREATE
;
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();
192 bool ALLOW_UNUSED did_close
= ClosePlatformFile(files_
[i
]);
193 DLOG_IF(INFO
, !did_close
) << "Could not close file "
194 << filename
.MaybeAsASCII();
200 for (int i
= 0; i
< kSimpleEntryFileCount
; ++i
) {
201 PlatformFileInfo file_info
;
202 bool success
= GetPlatformFileInfo(files_
[i
], &file_info
);
204 DLOG(WARNING
) << "Could not get platform file info.";
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
);
215 bool SimpleSynchronousEntry::InitializeForOpen() {
216 DCHECK(!initialized_
);
217 if (!OpenOrCreateFiles(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
),
225 if (header_read_result
!= sizeof(header
)) {
226 DLOG(WARNING
) << "Cannot read header from entry.";
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.";
238 if (header
.version
!= kSimpleVersion
) {
239 DLOG(WARNING
) << "Unreadable version.";
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.";
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.";
259 if (base::Hash(key
.get(), header
.key_length
) != header
.key_hash
) {
260 DLOG(WARNING
) << "Hash mismatch on key.";
269 bool SimpleSynchronousEntry::InitializeForCreate() {
270 DCHECK(!initialized_
);
271 if (!OpenOrCreateFiles(true)) {
272 DLOG(WARNING
) << "Could not create platform files.";
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.";
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.";
301 } // namespace disk_cache