1 // Copyright 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 "components/nacl/browser/pnacl_translation_cache.h"
9 #include "base/callback.h"
10 #include "base/files/file_path.h"
11 #include "base/logging.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/threading/thread_checker.h"
14 #include "components/nacl/common/pnacl_types.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "net/base/io_buffer.h"
17 #include "net/base/net_errors.h"
18 #include "net/disk_cache/disk_cache.h"
20 using base::IntToString;
21 using content::BrowserThread;
25 void CloseDiskCacheEntry(disk_cache::Entry* entry) { entry->Close(); }
30 // This is in pnacl namespace instead of static so they can be used
32 const int kMaxMemCacheSize = 100 * 1024 * 1024;
34 //////////////////////////////////////////////////////////////////////
35 // Handle Reading/Writing to Cache.
37 // PnaclTranslationCacheEntry is a shim that provides storage for the
38 // 'key' and 'data' strings as the disk_cache is performing various async
39 // operations. It also tracks the open disk_cache::Entry
40 // and ensures that the entry is closed.
41 class PnaclTranslationCacheEntry
42 : public base::RefCounted<PnaclTranslationCacheEntry> {
44 static PnaclTranslationCacheEntry* GetReadEntry(
45 base::WeakPtr<PnaclTranslationCache> cache,
46 const std::string& key,
47 const GetNexeCallback& callback);
48 static PnaclTranslationCacheEntry* GetWriteEntry(
49 base::WeakPtr<PnaclTranslationCache> cache,
50 const std::string& key,
51 net::DrainableIOBuffer* write_nexe,
52 const CompletionCallback& callback);
58 // Start -> Open Existing --------------> Write ---> Close
63 // Start -> Open --------Read ----> Close
76 friend class base::RefCounted<PnaclTranslationCacheEntry>;
77 PnaclTranslationCacheEntry(base::WeakPtr<PnaclTranslationCache> cache,
78 const std::string& key,
80 ~PnaclTranslationCacheEntry();
82 // Try to open an existing entry in the backend
84 // Create a new entry in the backend (for writes)
86 // Write |len| bytes to the backend, starting at |offset|
87 void WriteEntry(int offset, int len);
88 // Read |len| bytes from the backend, starting at |offset|
89 void ReadEntry(int offset, int len);
90 // If there was an error, doom the entry. Then post a task to the IO
91 // thread to close (and delete) it.
92 void CloseEntry(int rv);
93 // Call the user callback, and signal to the cache to delete this.
95 // Used as the callback for all operations to the backend. Handle state
96 // transitions, track bytes transferred, and call the other helper methods.
97 void DispatchNext(int rv);
99 base::WeakPtr<PnaclTranslationCache> cache_;
101 disk_cache::Entry* entry_;
104 GetNexeCallback read_callback_;
105 CompletionCallback write_callback_;
106 scoped_refptr<net::DrainableIOBuffer> io_buf_;
107 base::ThreadChecker thread_checker_;
108 DISALLOW_COPY_AND_ASSIGN(PnaclTranslationCacheEntry);
112 PnaclTranslationCacheEntry* PnaclTranslationCacheEntry::GetReadEntry(
113 base::WeakPtr<PnaclTranslationCache> cache,
114 const std::string& key,
115 const GetNexeCallback& callback) {
116 PnaclTranslationCacheEntry* entry(
117 new PnaclTranslationCacheEntry(cache, key, true));
118 entry->read_callback_ = callback;
123 PnaclTranslationCacheEntry* PnaclTranslationCacheEntry::GetWriteEntry(
124 base::WeakPtr<PnaclTranslationCache> cache,
125 const std::string& key,
126 net::DrainableIOBuffer* write_nexe,
127 const CompletionCallback& callback) {
128 PnaclTranslationCacheEntry* entry(
129 new PnaclTranslationCacheEntry(cache, key, false));
130 entry->io_buf_ = write_nexe;
131 entry->write_callback_ = callback;
135 PnaclTranslationCacheEntry::PnaclTranslationCacheEntry(
136 base::WeakPtr<PnaclTranslationCache> cache,
137 const std::string& key,
142 step_(UNINITIALIZED),
145 PnaclTranslationCacheEntry::~PnaclTranslationCacheEntry() {
146 // Ensure we have called the user's callback
147 if (step_ != FINISHED) {
148 if (!read_callback_.is_null()) {
149 BrowserThread::PostTask(
152 base::Bind(read_callback_,
154 scoped_refptr<net::DrainableIOBuffer>()));
156 if (!write_callback_.is_null()) {
157 BrowserThread::PostTask(BrowserThread::IO,
159 base::Bind(write_callback_, net::ERR_ABORTED));
164 void PnaclTranslationCacheEntry::Start() {
165 DCHECK(thread_checker_.CalledOnValidThread());
170 // OpenEntry, CreateEntry, WriteEntry, ReadEntry and CloseEntry are only called
171 // from DispatchNext, so they know that cache_ is still valid.
172 void PnaclTranslationCacheEntry::OpenEntry() {
173 int rv = cache_->backend()->OpenEntry(
176 base::Bind(&PnaclTranslationCacheEntry::DispatchNext, this));
177 if (rv != net::ERR_IO_PENDING)
181 void PnaclTranslationCacheEntry::CreateEntry() {
182 int rv = cache_->backend()->CreateEntry(
185 base::Bind(&PnaclTranslationCacheEntry::DispatchNext, this));
186 if (rv != net::ERR_IO_PENDING)
190 void PnaclTranslationCacheEntry::WriteEntry(int offset, int len) {
191 DCHECK(io_buf_->BytesRemaining() == len);
192 int rv = entry_->WriteData(
197 base::Bind(&PnaclTranslationCacheEntry::DispatchNext, this),
199 if (rv != net::ERR_IO_PENDING)
203 void PnaclTranslationCacheEntry::ReadEntry(int offset, int len) {
204 int rv = entry_->ReadData(
209 base::Bind(&PnaclTranslationCacheEntry::DispatchNext, this));
210 if (rv != net::ERR_IO_PENDING)
214 void PnaclTranslationCacheEntry::CloseEntry(int rv) {
217 LOG(ERROR) << "Failed to close entry: " << net::ErrorToString(rv);
220 BrowserThread::PostTask(
221 BrowserThread::IO, FROM_HERE, base::Bind(&CloseDiskCacheEntry, entry_));
225 void PnaclTranslationCacheEntry::Finish(int rv) {
228 if (!read_callback_.is_null()) {
229 BrowserThread::PostTask(BrowserThread::IO,
231 base::Bind(read_callback_, rv, io_buf_));
234 if (!write_callback_.is_null()) {
235 BrowserThread::PostTask(
236 BrowserThread::IO, FROM_HERE, base::Bind(write_callback_, rv));
239 cache_->OpComplete(this);
242 void PnaclTranslationCacheEntry::DispatchNext(int rv) {
243 DCHECK(thread_checker_.CalledOnValidThread());
250 LOG(ERROR) << "DispatchNext called uninitialized";
255 step_ = TRANSFER_ENTRY;
257 int bytes_to_transfer = entry_->GetDataSize(1);
258 io_buf_ = new net::DrainableIOBuffer(
259 new net::IOBuffer(bytes_to_transfer), bytes_to_transfer);
260 ReadEntry(0, bytes_to_transfer);
262 WriteEntry(0, io_buf_->size());
265 if (rv != net::ERR_FAILED) {
266 // ERROR_FAILED is what we expect if the entry doesn't exist.
267 LOG(ERROR) << "OpenEntry failed: " << net::ErrorToString(rv);
270 // Just a cache miss, not necessarily an error.
274 step_ = CREATE_ENTRY;
282 step_ = TRANSFER_ENTRY;
283 WriteEntry(io_buf_->BytesConsumed(), io_buf_->BytesRemaining());
285 LOG(ERROR) << "Failed to Create Entry: " << net::ErrorToString(rv);
292 // We do not call DispatchNext directly if WriteEntry/ReadEntry returns
293 // ERR_IO_PENDING, and the callback should not return that value either.
294 LOG(ERROR) << "Failed to complete write to entry: "
295 << net::ErrorToString(rv);
300 io_buf_->DidConsume(rv);
301 if (io_buf_->BytesRemaining() > 0) {
303 ? ReadEntry(io_buf_->BytesConsumed(), io_buf_->BytesRemaining())
304 : WriteEntry(io_buf_->BytesConsumed(), io_buf_->BytesRemaining());
308 // rv == 0 or we fell through (i.e. we have transferred all the bytes)
310 DCHECK(io_buf_->BytesConsumed() == io_buf_->size());
312 io_buf_->SetOffset(0);
317 step_ = UNINITIALIZED;
322 //////////////////////////////////////////////////////////////////////
323 void PnaclTranslationCache::OpComplete(PnaclTranslationCacheEntry* entry) {
324 open_entries_.erase(entry);
327 //////////////////////////////////////////////////////////////////////
328 // Construction and cache backend initialization
329 PnaclTranslationCache::PnaclTranslationCache() : in_memory_(false) {}
331 PnaclTranslationCache::~PnaclTranslationCache() {}
333 int PnaclTranslationCache::Init(net::CacheType cache_type,
334 const base::FilePath& cache_dir,
336 const CompletionCallback& callback) {
337 int rv = disk_cache::CreateCacheBackend(
339 net::CACHE_BACKEND_DEFAULT,
342 true /* force_initialize */,
343 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE).get(),
344 NULL, /* dummy net log */
346 base::Bind(&PnaclTranslationCache::OnCreateBackendComplete, AsWeakPtr()));
347 if (rv == net::ERR_IO_PENDING) {
348 init_callback_ = callback;
353 void PnaclTranslationCache::OnCreateBackendComplete(int rv) {
355 LOG(ERROR) << "Backend init failed:" << net::ErrorToString(rv);
357 // Invoke our client's callback function.
358 if (!init_callback_.is_null()) {
359 BrowserThread::PostTask(
360 BrowserThread::IO, FROM_HERE, base::Bind(init_callback_, rv));
364 //////////////////////////////////////////////////////////////////////
367 void PnaclTranslationCache::StoreNexe(const std::string& key,
368 net::DrainableIOBuffer* nexe_data,
369 const CompletionCallback& callback) {
370 PnaclTranslationCacheEntry* entry = PnaclTranslationCacheEntry::GetWriteEntry(
371 AsWeakPtr(), key, nexe_data, callback);
372 open_entries_[entry] = entry;
376 void PnaclTranslationCache::GetNexe(const std::string& key,
377 const GetNexeCallback& callback) {
378 PnaclTranslationCacheEntry* entry =
379 PnaclTranslationCacheEntry::GetReadEntry(AsWeakPtr(), key, callback);
380 open_entries_[entry] = entry;
384 int PnaclTranslationCache::InitOnDisk(const base::FilePath& cache_directory,
385 const CompletionCallback& callback) {
387 return Init(net::PNACL_CACHE, cache_directory, 0 /* auto size */, callback);
390 int PnaclTranslationCache::InitInMemory(const CompletionCallback& callback) {
392 return Init(net::MEMORY_CACHE, base::FilePath(), kMaxMemCacheSize, callback);
395 int PnaclTranslationCache::Size() {
398 return disk_cache_->GetEntryCount();
401 // Beware that any changes to this function or to PnaclCacheInfo will
402 // effectively invalidate existing translation cache entries.
405 std::string PnaclTranslationCache::GetKey(const nacl::PnaclCacheInfo& info) {
406 if (!info.pexe_url.is_valid() || info.abi_version < 0 || info.opt_level < 0 ||
407 info.extra_flags.size() > 512)
408 return std::string();
409 std::string retval("ABI:");
410 retval += IntToString(info.abi_version) + ";" + "opt:" +
411 IntToString(info.opt_level) + ";" + "URL:";
412 // Filter the username, password, and ref components from the URL
413 GURL::Replacements replacements;
414 replacements.ClearUsername();
415 replacements.ClearPassword();
416 replacements.ClearRef();
417 GURL key_url(info.pexe_url.ReplaceComponents(replacements));
418 retval += key_url.spec() + ";";
419 // You would think that there is already code to format base::Time values
420 // somewhere, but I haven't found it yet. In any case, doing it ourselves
421 // here means we can keep the format stable.
422 base::Time::Exploded exploded;
423 info.last_modified.UTCExplode(&exploded);
424 if (info.last_modified.is_null() || !exploded.HasValidValues()) {
425 memset(&exploded, 0, sizeof(exploded));
427 retval += "modified:" + IntToString(exploded.year) + ":" +
428 IntToString(exploded.month) + ":" +
429 IntToString(exploded.day_of_month) + ":" +
430 IntToString(exploded.hour) + ":" + IntToString(exploded.minute) +
431 ":" + IntToString(exploded.second) + ":" +
432 IntToString(exploded.millisecond) + ":UTC;";
433 retval += "etag:" + info.etag + ";";
434 retval += "sandbox:" + info.sandbox_isa + ";";
435 retval += "extra_flags:" + info.extra_flags + ";";
439 int PnaclTranslationCache::DoomEntriesBetween(
442 const CompletionCallback& callback) {
443 return disk_cache_->DoomEntriesBetween(initial, end, callback);