1 // Copyright (c) 2012 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 "content/browser/appcache/appcache_disk_cache.h"
6
7 #include <limits>
8 #include <utility>
9
10 #include "base/bind.h"
11 #include "base/callback_helpers.h"
12 #include "base/check.h"
13 #include "base/files/file_path.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/stl_util.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "net/base/cache_type.h"
19 #include "net/base/completion_repeating_callback.h"
20 #include "net/base/net_errors.h"
21
22 namespace content {
23
24 // A callback shim that provides storage for the 'backend_ptr' value
25 // and will delete a resulting ptr if completion occurs after its
26 // been canceled.
27 class AppCacheDiskCache::CreateBackendCallbackShim
28 : public base::RefCounted<CreateBackendCallbackShim> {
29 public:
CreateBackendCallbackShim(AppCacheDiskCache * object)30 explicit CreateBackendCallbackShim(AppCacheDiskCache* object)
31 : appcache_diskcache_(object) {
32 }
33
Cancel()34 void Cancel() { appcache_diskcache_ = nullptr; }
35
Callback(int return_value)36 void Callback(int return_value) {
37 if (appcache_diskcache_)
38 appcache_diskcache_->OnCreateBackendComplete(return_value);
39 }
40
41 std::unique_ptr<disk_cache::Backend> backend_ptr_; // Accessed directly.
42
43 private:
44 friend class base::RefCounted<CreateBackendCallbackShim>;
45
46 ~CreateBackendCallbackShim() = default;
47
48 AppCacheDiskCache* appcache_diskcache_; // Unowned pointer.
49 };
50
AppCacheDiskCacheEntry(disk_cache::Entry * disk_cache_entry,AppCacheDiskCache * cache)51 AppCacheDiskCacheEntry::AppCacheDiskCacheEntry(
52 disk_cache::Entry* disk_cache_entry,
53 AppCacheDiskCache* cache)
54 : disk_cache_entry_(disk_cache_entry), cache_(cache) {
55 DCHECK(disk_cache_entry);
56 DCHECK(cache);
57 cache_->AddOpenEntry(this);
58 }
59
~AppCacheDiskCacheEntry()60 AppCacheDiskCacheEntry::~AppCacheDiskCacheEntry() {
61 if (cache_)
62 cache_->RemoveOpenEntry(this);
63 }
64
Read(int index,int64_t offset,net::IOBuffer * buf,int buf_len,net::CompletionOnceCallback callback)65 int AppCacheDiskCacheEntry::Read(int index,
66 int64_t offset,
67 net::IOBuffer* buf,
68 int buf_len,
69 net::CompletionOnceCallback callback) {
70 if (offset < 0 || offset > std::numeric_limits<int32_t>::max())
71 return net::ERR_INVALID_ARGUMENT;
72 if (!disk_cache_entry_)
73 return net::ERR_ABORTED;
74 return disk_cache_entry_->ReadData(index, static_cast<int>(offset), buf,
75 buf_len, std::move(callback));
76 }
77
Write(int index,int64_t offset,net::IOBuffer * buf,int buf_len,net::CompletionOnceCallback callback)78 int AppCacheDiskCacheEntry::Write(int index,
79 int64_t offset,
80 net::IOBuffer* buf,
81 int buf_len,
82 net::CompletionOnceCallback callback) {
83 if (offset < 0 || offset > std::numeric_limits<int32_t>::max())
84 return net::ERR_INVALID_ARGUMENT;
85 if (!disk_cache_entry_)
86 return net::ERR_ABORTED;
87 const bool kTruncate = true;
88 return disk_cache_entry_->WriteData(index, static_cast<int>(offset), buf,
89 buf_len, std::move(callback), kTruncate);
90 }
91
GetSize(int index)92 int64_t AppCacheDiskCacheEntry::GetSize(int index) {
93 return disk_cache_entry_ ? disk_cache_entry_->GetDataSize(index) : 0L;
94 }
95
Close()96 void AppCacheDiskCacheEntry::Close() {
97 if (disk_cache_entry_)
98 disk_cache_entry_->Close();
99 delete this;
100 }
101
Abandon()102 void AppCacheDiskCacheEntry::Abandon() {
103 cache_ = nullptr;
104 disk_cache_entry_->Close();
105 disk_cache_entry_ = nullptr;
106 }
107
108 namespace {
109
110 // Separate object to hold state for each Create, Delete, or Doom call
111 // while the call is in-flight and to produce an EntryImpl upon completion.
112 class ActiveCall : public base::RefCounted<ActiveCall> {
113 public:
ActiveCall(const base::WeakPtr<AppCacheDiskCache> & owner,AppCacheDiskCacheEntry ** entry,net::CompletionOnceCallback callback)114 ActiveCall(const base::WeakPtr<AppCacheDiskCache>& owner,
115 AppCacheDiskCacheEntry** entry,
116 net::CompletionOnceCallback callback)
117 : owner_(owner), entry_(entry), callback_(std::move(callback)) {
118 DCHECK(owner_);
119 }
120
CreateEntry(const base::WeakPtr<AppCacheDiskCache> & owner,int64_t key,AppCacheDiskCacheEntry ** entry,net::CompletionOnceCallback callback)121 static net::Error CreateEntry(const base::WeakPtr<AppCacheDiskCache>& owner,
122 int64_t key,
123 AppCacheDiskCacheEntry** entry,
124 net::CompletionOnceCallback callback) {
125 scoped_refptr<ActiveCall> active_call =
126 base::MakeRefCounted<ActiveCall>(owner, entry, std::move(callback));
127 disk_cache::EntryResult result = owner->disk_cache()->CreateEntry(
128 base::NumberToString(key), net::HIGHEST,
129 base::BindOnce(&ActiveCall::OnAsyncCompletion, active_call));
130 return active_call->HandleImmediateReturnValue(std::move(result));
131 }
132
OpenEntry(const base::WeakPtr<AppCacheDiskCache> & owner,int64_t key,AppCacheDiskCacheEntry ** entry,net::CompletionOnceCallback callback)133 static net::Error OpenEntry(const base::WeakPtr<AppCacheDiskCache>& owner,
134 int64_t key,
135 AppCacheDiskCacheEntry** entry,
136 net::CompletionOnceCallback callback) {
137 scoped_refptr<ActiveCall> active_call =
138 base::MakeRefCounted<ActiveCall>(owner, entry, std::move(callback));
139 disk_cache::EntryResult result = owner->disk_cache()->OpenEntry(
140 base::NumberToString(key), net::HIGHEST,
141 base::BindOnce(&ActiveCall::OnAsyncCompletion, active_call));
142 return active_call->HandleImmediateReturnValue(std::move(result));
143 }
144
DoomEntry(const base::WeakPtr<AppCacheDiskCache> & owner,int64_t key,net::CompletionOnceCallback callback)145 static net::Error DoomEntry(const base::WeakPtr<AppCacheDiskCache>& owner,
146 int64_t key,
147 net::CompletionOnceCallback callback) {
148 return owner->disk_cache()->DoomEntry(base::NumberToString(key),
149 net::HIGHEST, std::move(callback));
150 }
151
152 private:
153 friend class base::RefCounted<ActiveCall>;
154
155 ~ActiveCall() = default;
156
HandleImmediateReturnValue(disk_cache::EntryResult result)157 net::Error HandleImmediateReturnValue(disk_cache::EntryResult result) {
158 net::Error rv = result.net_error();
159 if (rv == net::ERR_IO_PENDING) {
160 // OnAsyncCompletion will be called later.
161 return rv;
162 }
163
164 if (rv == net::OK) {
165 *entry_ = new AppCacheDiskCacheEntry(result.ReleaseEntry(), owner_.get());
166 }
167
168 return rv;
169 }
170
OnAsyncCompletion(disk_cache::EntryResult result)171 void OnAsyncCompletion(disk_cache::EntryResult result) {
172 int rv = result.net_error();
173 if (rv == net::OK) {
174 if (owner_) {
175 *entry_ =
176 new AppCacheDiskCacheEntry(result.ReleaseEntry(), owner_.get());
177 } else {
178 result.ReleaseEntry()->Close();
179 rv = net::ERR_ABORTED;
180 }
181 }
182 std::move(callback_).Run(rv);
183 }
184
185 base::WeakPtr<AppCacheDiskCache> owner_;
186 AppCacheDiskCacheEntry** entry_;
187 net::CompletionOnceCallback callback_;
188 };
189
190 } // namespace
191
AppCacheDiskCache()192 AppCacheDiskCache::AppCacheDiskCache()
193 #if defined(APPCACHE_USE_SIMPLE_CACHE)
194 : AppCacheDiskCache(true)
195 #else
196 : AppCacheDiskCache(false)
197 #endif
198 {
199 }
200
~AppCacheDiskCache()201 AppCacheDiskCache::~AppCacheDiskCache() {
202 Disable();
203 }
204
InitWithDiskBackend(const base::FilePath & disk_cache_directory,bool force,base::OnceClosure post_cleanup_callback,net::CompletionOnceCallback callback)205 net::Error AppCacheDiskCache::InitWithDiskBackend(
206 const base::FilePath& disk_cache_directory,
207 bool force,
208 base::OnceClosure post_cleanup_callback,
209 net::CompletionOnceCallback callback) {
210 return Init(net::APP_CACHE, disk_cache_directory,
211 std::numeric_limits<int64_t>::max(), force,
212 std::move(post_cleanup_callback), std::move(callback));
213 }
214
InitWithMemBackend(int64_t mem_cache_size,net::CompletionOnceCallback callback)215 net::Error AppCacheDiskCache::InitWithMemBackend(
216 int64_t mem_cache_size,
217 net::CompletionOnceCallback callback) {
218 return Init(net::MEMORY_CACHE, base::FilePath(), mem_cache_size, false,
219 base::OnceClosure(), std::move(callback));
220 }
221
Disable()222 void AppCacheDiskCache::Disable() {
223 if (is_disabled_)
224 return;
225
226 is_disabled_ = true;
227
228 if (create_backend_callback_.get()) {
229 create_backend_callback_->Cancel();
230 create_backend_callback_ = nullptr;
231 OnCreateBackendComplete(net::ERR_ABORTED);
232 }
233
234 // We need to close open file handles in order to reinitalize the
235 // appcache system on the fly. File handles held in both entries and in
236 // the main disk_cache::Backend class need to be released.
237 for (AppCacheDiskCacheEntry* entry : open_entries_) {
238 entry->Abandon();
239 }
240 open_entries_.clear();
241 disk_cache_.reset();
242 }
243
CreateEntry(int64_t key,AppCacheDiskCacheEntry ** entry,net::CompletionOnceCallback callback)244 net::Error AppCacheDiskCache::CreateEntry(
245 int64_t key,
246 AppCacheDiskCacheEntry** entry,
247 net::CompletionOnceCallback callback) {
248 DCHECK(entry);
249 DCHECK(!callback.is_null());
250 if (is_disabled_)
251 return net::ERR_ABORTED;
252
253 if (is_initializing_or_waiting_to_initialize()) {
254 pending_calls_.emplace_back(CREATE, key, entry, std::move(callback));
255 return net::ERR_IO_PENDING;
256 }
257
258 if (!disk_cache_)
259 return net::ERR_FAILED;
260
261 return ActiveCall::CreateEntry(weak_factory_.GetWeakPtr(), key, entry,
262 std::move(callback));
263 }
264
OpenEntry(int64_t key,AppCacheDiskCacheEntry ** entry,net::CompletionOnceCallback callback)265 net::Error AppCacheDiskCache::OpenEntry(int64_t key,
266 AppCacheDiskCacheEntry** entry,
267 net::CompletionOnceCallback callback) {
268 DCHECK(entry);
269 DCHECK(!callback.is_null());
270 if (is_disabled_)
271 return net::ERR_ABORTED;
272
273 if (is_initializing_or_waiting_to_initialize()) {
274 pending_calls_.emplace_back(OPEN, key, entry, std::move(callback));
275 return net::ERR_IO_PENDING;
276 }
277
278 if (!disk_cache_)
279 return net::ERR_FAILED;
280
281 return ActiveCall::OpenEntry(weak_factory_.GetWeakPtr(), key, entry,
282 std::move(callback));
283 }
284
DoomEntry(int64_t key,net::CompletionOnceCallback callback)285 net::Error AppCacheDiskCache::DoomEntry(int64_t key,
286 net::CompletionOnceCallback callback) {
287 DCHECK(!callback.is_null());
288 if (is_disabled_)
289 return net::ERR_ABORTED;
290
291 if (is_initializing_or_waiting_to_initialize()) {
292 pending_calls_.emplace_back(DOOM, key, nullptr, std::move(callback));
293 return net::ERR_IO_PENDING;
294 }
295
296 if (!disk_cache_)
297 return net::ERR_FAILED;
298
299 return ActiveCall::DoomEntry(weak_factory_.GetWeakPtr(), key,
300 std::move(callback));
301 }
302
GetWeakPtr()303 base::WeakPtr<AppCacheDiskCache> AppCacheDiskCache::GetWeakPtr() {
304 return weak_factory_.GetWeakPtr();
305 }
306
AppCacheDiskCache(bool use_simple_cache)307 AppCacheDiskCache::AppCacheDiskCache(bool use_simple_cache)
308 : use_simple_cache_(use_simple_cache),
309 is_disabled_(false),
310 is_waiting_to_initialize_(false) {}
311
PendingCall(PendingCallType call_type,int64_t key,AppCacheDiskCacheEntry ** entry,net::CompletionOnceCallback callback)312 AppCacheDiskCache::PendingCall::PendingCall(
313 PendingCallType call_type,
314 int64_t key,
315 AppCacheDiskCacheEntry** entry,
316 net::CompletionOnceCallback callback)
317 : call_type(call_type),
318 key(key),
319 entry(entry),
320 callback(std::move(callback)) {}
321
322 AppCacheDiskCache::PendingCall::PendingCall(PendingCall&&) = default;
323
324 AppCacheDiskCache::PendingCall::~PendingCall() = default;
325
Init(net::CacheType cache_type,const base::FilePath & cache_directory,int64_t cache_size,bool force,base::OnceClosure post_cleanup_callback,net::CompletionOnceCallback callback)326 net::Error AppCacheDiskCache::Init(net::CacheType cache_type,
327 const base::FilePath& cache_directory,
328 int64_t cache_size,
329 bool force,
330 base::OnceClosure post_cleanup_callback,
331 net::CompletionOnceCallback callback) {
332 DCHECK(!is_initializing_or_waiting_to_initialize() && !disk_cache_.get());
333 is_disabled_ = false;
334 create_backend_callback_ =
335 base::MakeRefCounted<CreateBackendCallbackShim>(this);
336 disk_cache::ResetHandling reset_handling =
337 force ? disk_cache::ResetHandling::kResetOnError
338 : disk_cache::ResetHandling::kNeverReset;
339
340 net::Error return_value = disk_cache::CreateCacheBackend(
341 cache_type,
342 use_simple_cache_ ? net::CACHE_BACKEND_SIMPLE
343 : net::CACHE_BACKEND_DEFAULT,
344 cache_directory, cache_size, reset_handling, nullptr,
345 &(create_backend_callback_->backend_ptr_),
346 std::move(post_cleanup_callback),
347 base::BindOnce(&CreateBackendCallbackShim::Callback,
348 create_backend_callback_));
349 if (return_value == net::ERR_IO_PENDING)
350 init_callback_ = std::move(callback);
351 else
352 OnCreateBackendComplete(return_value);
353 return return_value;
354 }
355
OnCreateBackendComplete(int return_value)356 void AppCacheDiskCache::OnCreateBackendComplete(int return_value) {
357 if (return_value == net::OK) {
358 disk_cache_ = std::move(create_backend_callback_->backend_ptr_);
359 }
360 create_backend_callback_ = nullptr;
361
362 // Invoke our clients callback function.
363 if (!init_callback_.is_null()) {
364 std::move(init_callback_).Run(return_value);
365 }
366
367 // Service pending calls that were queued up while we were initializing.
368 for (auto& call : pending_calls_) {
369 // This is safe, because the callback will only be called once.
370 net::CompletionRepeatingCallback copyable_callback =
371 base::AdaptCallbackForRepeating(std::move(call.callback));
372 return_value = net::ERR_FAILED;
373 switch (call.call_type) {
374 case CREATE:
375 return_value = CreateEntry(call.key, call.entry, copyable_callback);
376 break;
377 case OPEN:
378 return_value = OpenEntry(call.key, call.entry, copyable_callback);
379 break;
380 case DOOM:
381 return_value = DoomEntry(call.key, copyable_callback);
382 break;
383 }
384
385 // disk_cache::{Create,Open,Doom}Entry() call their callbacks iff they
386 // return net::ERR_IO_PENDING. In this case, the callback was not called.
387 // However, the corresponding ServiceWorkerDiskCache wrapper returned
388 // net::ERR_IO_PENDING as it queued up the pending call. To follow the
389 // disk_cache API contract, we need to call the callback ourselves here.
390 if (return_value != net::ERR_IO_PENDING)
391 copyable_callback.Run(return_value);
392 }
393 pending_calls_.clear();
394 }
395
396 } // namespace content
397