pnacl_translation_cache.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  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.
  4. #include "components/nacl/browser/pnacl_translation_cache.h"
  5. #include <string.h>
  6. #include <string>
  7. #include <utility>
  8. #include "base/bind.h"
  9. #include "base/callback.h"
  10. #include "base/files/file_path.h"
  11. #include "base/logging.h"
  12. #include "base/memory/raw_ptr.h"
  13. #include "base/strings/string_number_conversions.h"
  14. #include "base/threading/thread_checker.h"
  15. #include "components/nacl/common/pnacl_types.h"
  16. #include "content/public/browser/browser_task_traits.h"
  17. #include "content/public/browser/browser_thread.h"
  18. #include "net/base/io_buffer.h"
  19. #include "net/base/net_errors.h"
  20. #include "net/disk_cache/disk_cache.h"
  21. using base::NumberToString;
  22. namespace {
  23. void CloseDiskCacheEntry(disk_cache::Entry* entry) { entry->Close(); }
  24. } // namespace
  25. namespace pnacl {
  26. // This is in pnacl namespace instead of static so they can be used
  27. // by the unit test.
  28. const int kMaxMemCacheSize = 100 * 1024 * 1024;
  29. //////////////////////////////////////////////////////////////////////
  30. // Handle Reading/Writing to Cache.
  31. // PnaclTranslationCacheEntry is a shim that provides storage for the
  32. // 'key' and 'data' strings as the disk_cache is performing various async
  33. // operations. It also tracks the open disk_cache::Entry
  34. // and ensures that the entry is closed.
  35. class PnaclTranslationCacheEntry
  36. : public base::RefCountedThreadSafe<PnaclTranslationCacheEntry> {
  37. public:
  38. static PnaclTranslationCacheEntry* GetReadEntry(
  39. base::WeakPtr<PnaclTranslationCache> cache,
  40. const std::string& key,
  41. GetNexeCallback callback);
  42. static PnaclTranslationCacheEntry* GetWriteEntry(
  43. base::WeakPtr<PnaclTranslationCache> cache,
  44. const std::string& key,
  45. net::DrainableIOBuffer* write_nexe,
  46. CompletionOnceCallback callback);
  47. PnaclTranslationCacheEntry(const PnaclTranslationCacheEntry&) = delete;
  48. PnaclTranslationCacheEntry& operator=(const PnaclTranslationCacheEntry&) =
  49. delete;
  50. void Start();
  51. // Writes: ---
  52. // v |
  53. // Start -> Open Existing --------------> Write ---> Close
  54. // \ ^
  55. // \ /
  56. // --> Create --
  57. // Reads:
  58. // Start -> Open --------Read ----> Close
  59. // | ^
  60. // |__|
  61. enum CacheStep {
  62. UNINITIALIZED,
  63. OPEN_ENTRY,
  64. CREATE_ENTRY,
  65. TRANSFER_ENTRY,
  66. CLOSE_ENTRY,
  67. FINISHED
  68. };
  69. private:
  70. friend class base::RefCountedThreadSafe<PnaclTranslationCacheEntry>;
  71. PnaclTranslationCacheEntry(base::WeakPtr<PnaclTranslationCache> cache,
  72. const std::string& key,
  73. bool is_read);
  74. ~PnaclTranslationCacheEntry();
  75. // Try to open an existing entry in the backend
  76. void OpenEntry();
  77. // Create a new entry in the backend (for writes)
  78. void CreateEntry();
  79. // Write |len| bytes to the backend, starting at |offset|
  80. void WriteEntry(int offset, int len);
  81. // Read |len| bytes from the backend, starting at |offset|
  82. void ReadEntry(int offset, int len);
  83. // If there was an error, doom the entry. Then post a task to the IO
  84. // thread to close (and delete) it.
  85. void CloseEntry(int rv);
  86. // Call the user callback, and signal to the cache to delete this.
  87. void Finish(int rv);
  88. // Used as the callback for all operations to the backend except those that
  89. // first open/create entries. Handle state transitions, track bytes
  90. // transferred, and call the other helper methods.
  91. void DispatchNext(int rv);
  92. // Like above but for first opening or creating of |entry_|.
  93. void SaveEntryAndDispatchNext(disk_cache::EntryResult result);
  94. base::WeakPtr<PnaclTranslationCache> cache_;
  95. std::string key_;
  96. raw_ptr<disk_cache::Entry> entry_;
  97. CacheStep step_;
  98. bool is_read_;
  99. GetNexeCallback read_callback_;
  100. CompletionOnceCallback write_callback_;
  101. scoped_refptr<net::DrainableIOBuffer> io_buf_;
  102. base::ThreadChecker thread_checker_;
  103. };
  104. // static
  105. PnaclTranslationCacheEntry* PnaclTranslationCacheEntry::GetReadEntry(
  106. base::WeakPtr<PnaclTranslationCache> cache,
  107. const std::string& key,
  108. GetNexeCallback callback) {
  109. PnaclTranslationCacheEntry* entry(
  110. new PnaclTranslationCacheEntry(cache, key, true));
  111. entry->read_callback_ = std::move(callback);
  112. return entry;
  113. }
  114. // static
  115. PnaclTranslationCacheEntry* PnaclTranslationCacheEntry::GetWriteEntry(
  116. base::WeakPtr<PnaclTranslationCache> cache,
  117. const std::string& key,
  118. net::DrainableIOBuffer* write_nexe,
  119. CompletionOnceCallback callback) {
  120. PnaclTranslationCacheEntry* entry(
  121. new PnaclTranslationCacheEntry(cache, key, false));
  122. entry->io_buf_ = write_nexe;
  123. entry->write_callback_ = std::move(callback);
  124. return entry;
  125. }
  126. PnaclTranslationCacheEntry::PnaclTranslationCacheEntry(
  127. base::WeakPtr<PnaclTranslationCache> cache,
  128. const std::string& key,
  129. bool is_read)
  130. : cache_(cache),
  131. key_(key),
  132. entry_(nullptr),
  133. step_(UNINITIALIZED),
  134. is_read_(is_read) {}
  135. PnaclTranslationCacheEntry::~PnaclTranslationCacheEntry() {
  136. // Ensure we have called the user's callback
  137. if (step_ != FINISHED) {
  138. if (!read_callback_.is_null()) {
  139. content::GetUIThreadTaskRunner({})->PostTask(
  140. FROM_HERE, base::BindOnce(std::move(read_callback_), net::ERR_ABORTED,
  141. scoped_refptr<net::DrainableIOBuffer>()));
  142. }
  143. if (!write_callback_.is_null()) {
  144. content::GetUIThreadTaskRunner({})->PostTask(
  145. FROM_HERE,
  146. base::BindOnce(std::move(write_callback_), net::ERR_ABORTED));
  147. }
  148. }
  149. }
  150. void PnaclTranslationCacheEntry::Start() {
  151. DCHECK(thread_checker_.CalledOnValidThread());
  152. step_ = OPEN_ENTRY;
  153. OpenEntry();
  154. }
  155. // OpenEntry, CreateEntry, WriteEntry, ReadEntry and CloseEntry are only called
  156. // from DispatchNext, so they know that cache_ is still valid.
  157. void PnaclTranslationCacheEntry::OpenEntry() {
  158. disk_cache::EntryResult result = cache_->backend()->OpenEntry(
  159. key_, net::HIGHEST,
  160. base::BindOnce(&PnaclTranslationCacheEntry::SaveEntryAndDispatchNext,
  161. this));
  162. if (result.net_error() != net::ERR_IO_PENDING)
  163. SaveEntryAndDispatchNext(std::move(result));
  164. }
  165. void PnaclTranslationCacheEntry::CreateEntry() {
  166. disk_cache::EntryResult result = cache_->backend()->CreateEntry(
  167. key_, net::HIGHEST,
  168. base::BindOnce(&PnaclTranslationCacheEntry::SaveEntryAndDispatchNext,
  169. this));
  170. if (result.net_error() != net::ERR_IO_PENDING)
  171. SaveEntryAndDispatchNext(std::move(result));
  172. }
  173. void PnaclTranslationCacheEntry::WriteEntry(int offset, int len) {
  174. DCHECK(io_buf_->BytesRemaining() == len);
  175. int rv = entry_->WriteData(
  176. 1, offset, io_buf_.get(), len,
  177. base::BindOnce(&PnaclTranslationCacheEntry::DispatchNext, this), false);
  178. if (rv != net::ERR_IO_PENDING)
  179. DispatchNext(rv);
  180. }
  181. void PnaclTranslationCacheEntry::ReadEntry(int offset, int len) {
  182. int rv = entry_->ReadData(
  183. 1, offset, io_buf_.get(), len,
  184. base::BindOnce(&PnaclTranslationCacheEntry::DispatchNext, this));
  185. if (rv != net::ERR_IO_PENDING)
  186. DispatchNext(rv);
  187. }
  188. void PnaclTranslationCacheEntry::CloseEntry(int rv) {
  189. DCHECK(entry_);
  190. if (rv < 0) {
  191. LOG(ERROR) << "Failed to close entry: " << net::ErrorToString(rv);
  192. entry_->Doom();
  193. }
  194. content::GetUIThreadTaskRunner({})->PostTask(
  195. FROM_HERE, base::BindOnce(&CloseDiskCacheEntry, entry_));
  196. Finish(rv);
  197. }
  198. void PnaclTranslationCacheEntry::Finish(int rv) {
  199. step_ = FINISHED;
  200. if (is_read_) {
  201. if (!read_callback_.is_null()) {
  202. content::GetUIThreadTaskRunner({})->PostTask(
  203. FROM_HERE, base::BindOnce(std::move(read_callback_), rv, io_buf_));
  204. }
  205. } else {
  206. if (!write_callback_.is_null()) {
  207. content::GetUIThreadTaskRunner({})->PostTask(
  208. FROM_HERE, base::BindOnce(std::move(write_callback_), rv));
  209. }
  210. }
  211. cache_->OpComplete(this);
  212. }
  213. void PnaclTranslationCacheEntry::DispatchNext(int rv) {
  214. DCHECK(thread_checker_.CalledOnValidThread());
  215. if (!cache_)
  216. return;
  217. switch (step_) {
  218. case UNINITIALIZED:
  219. case FINISHED:
  220. LOG(ERROR) << "DispatchNext called uninitialized";
  221. break;
  222. case OPEN_ENTRY:
  223. if (rv == net::OK) {
  224. step_ = TRANSFER_ENTRY;
  225. if (is_read_) {
  226. int bytes_to_transfer = entry_->GetDataSize(1);
  227. io_buf_ = base::MakeRefCounted<net::DrainableIOBuffer>(
  228. base::MakeRefCounted<net::IOBuffer>(bytes_to_transfer),
  229. bytes_to_transfer);
  230. ReadEntry(0, bytes_to_transfer);
  231. } else {
  232. WriteEntry(0, io_buf_->size());
  233. }
  234. } else {
  235. if (rv != net::ERR_FAILED) {
  236. // ERROR_FAILED is what we expect if the entry doesn't exist.
  237. LOG(ERROR) << "OpenEntry failed: " << net::ErrorToString(rv);
  238. }
  239. if (is_read_) {
  240. // Just a cache miss, not necessarily an error.
  241. entry_ = nullptr;
  242. Finish(rv);
  243. } else {
  244. step_ = CREATE_ENTRY;
  245. CreateEntry();
  246. }
  247. }
  248. break;
  249. case CREATE_ENTRY:
  250. if (rv == net::OK) {
  251. step_ = TRANSFER_ENTRY;
  252. WriteEntry(io_buf_->BytesConsumed(), io_buf_->BytesRemaining());
  253. } else {
  254. LOG(ERROR) << "Failed to Create Entry: " << net::ErrorToString(rv);
  255. Finish(rv);
  256. }
  257. break;
  258. case TRANSFER_ENTRY:
  259. if (rv < 0) {
  260. // We do not call DispatchNext directly if WriteEntry/ReadEntry returns
  261. // ERR_IO_PENDING, and the callback should not return that value either.
  262. LOG(ERROR) << "Failed to complete write to entry: "
  263. << net::ErrorToString(rv);
  264. step_ = CLOSE_ENTRY;
  265. CloseEntry(rv);
  266. break;
  267. } else if (rv > 0) {
  268. io_buf_->DidConsume(rv);
  269. if (io_buf_->BytesRemaining() > 0) {
  270. is_read_
  271. ? ReadEntry(io_buf_->BytesConsumed(), io_buf_->BytesRemaining())
  272. : WriteEntry(io_buf_->BytesConsumed(), io_buf_->BytesRemaining());
  273. break;
  274. }
  275. }
  276. // rv == 0 or we fell through (i.e. we have transferred all the bytes)
  277. step_ = CLOSE_ENTRY;
  278. DCHECK(io_buf_->BytesConsumed() == io_buf_->size());
  279. if (is_read_)
  280. io_buf_->SetOffset(0);
  281. CloseEntry(0);
  282. break;
  283. case CLOSE_ENTRY:
  284. step_ = UNINITIALIZED;
  285. break;
  286. }
  287. }
  288. void PnaclTranslationCacheEntry::SaveEntryAndDispatchNext(
  289. disk_cache::EntryResult result) {
  290. int rv = result.net_error();
  291. entry_ = result.ReleaseEntry();
  292. DispatchNext(rv);
  293. }
  294. //////////////////////////////////////////////////////////////////////
  295. void PnaclTranslationCache::OpComplete(PnaclTranslationCacheEntry* entry) {
  296. open_entries_.erase(entry);
  297. }
  298. //////////////////////////////////////////////////////////////////////
  299. // Construction and cache backend initialization
  300. PnaclTranslationCache::PnaclTranslationCache() : in_memory_(false) {}
  301. PnaclTranslationCache::~PnaclTranslationCache() {}
  302. int PnaclTranslationCache::Init(net::CacheType cache_type,
  303. const base::FilePath& cache_dir,
  304. int cache_size,
  305. CompletionOnceCallback callback) {
  306. disk_cache::BackendResult result = disk_cache::CreateCacheBackend(
  307. cache_type, net::CACHE_BACKEND_DEFAULT, /*file_operations=*/nullptr,
  308. cache_dir, cache_size, disk_cache::ResetHandling::kResetOnError,
  309. nullptr, /* dummy net log */
  310. base::BindOnce(&PnaclTranslationCache::OnCreateBackendComplete,
  311. AsWeakPtr()));
  312. if (result.net_error == net::OK) {
  313. disk_cache_ = std::move(result.backend);
  314. } else if (result.net_error == net::ERR_IO_PENDING) {
  315. init_callback_ = std::move(callback);
  316. }
  317. return result.net_error;
  318. }
  319. void PnaclTranslationCache::OnCreateBackendComplete(
  320. disk_cache::BackendResult result) {
  321. if (result.net_error < 0) {
  322. LOG(ERROR) << "Backend init failed:"
  323. << net::ErrorToString(result.net_error);
  324. }
  325. disk_cache_ = std::move(result.backend);
  326. // Invoke our client's callback function.
  327. if (!init_callback_.is_null()) {
  328. content::GetUIThreadTaskRunner({})->PostTask(
  329. FROM_HERE, base::BindOnce(std::move(init_callback_), result.net_error));
  330. }
  331. }
  332. //////////////////////////////////////////////////////////////////////
  333. // High-level API
  334. void PnaclTranslationCache::StoreNexe(const std::string& key,
  335. net::DrainableIOBuffer* nexe_data,
  336. CompletionOnceCallback callback) {
  337. PnaclTranslationCacheEntry* entry = PnaclTranslationCacheEntry::GetWriteEntry(
  338. AsWeakPtr(), key, nexe_data, std::move(callback));
  339. open_entries_[entry] = entry;
  340. entry->Start();
  341. }
  342. void PnaclTranslationCache::GetNexe(const std::string& key,
  343. GetNexeCallback callback) {
  344. PnaclTranslationCacheEntry* entry = PnaclTranslationCacheEntry::GetReadEntry(
  345. AsWeakPtr(), key, std::move(callback));
  346. open_entries_[entry] = entry;
  347. entry->Start();
  348. }
  349. int PnaclTranslationCache::InitOnDisk(const base::FilePath& cache_directory,
  350. CompletionOnceCallback callback) {
  351. in_memory_ = false;
  352. return Init(net::PNACL_CACHE, cache_directory, 0 /* auto size */,
  353. std::move(callback));
  354. }
  355. int PnaclTranslationCache::InitInMemory(CompletionOnceCallback callback) {
  356. in_memory_ = true;
  357. return Init(net::MEMORY_CACHE, base::FilePath(), kMaxMemCacheSize,
  358. std::move(callback));
  359. }
  360. int PnaclTranslationCache::Size() {
  361. if (!disk_cache_)
  362. return -1;
  363. return disk_cache_->GetEntryCount();
  364. }
  365. // Beware that any changes to this function or to PnaclCacheInfo will
  366. // effectively invalidate existing translation cache entries.
  367. // static
  368. std::string PnaclTranslationCache::GetKey(const nacl::PnaclCacheInfo& info) {
  369. if (!info.pexe_url.is_valid() || info.abi_version < 0 || info.opt_level < 0 ||
  370. info.extra_flags.size() > 512)
  371. return std::string();
  372. std::string retval("ABI:");
  373. retval += NumberToString(info.abi_version) + ";" +
  374. "opt:" + NumberToString(info.opt_level) +
  375. (info.use_subzero ? "subzero;" : ";") + "URL:";
  376. // Filter the username, password, and ref components from the URL
  377. GURL::Replacements replacements;
  378. replacements.ClearUsername();
  379. replacements.ClearPassword();
  380. replacements.ClearRef();
  381. GURL key_url(info.pexe_url.ReplaceComponents(replacements));
  382. retval += key_url.spec() + ";";
  383. // You would think that there is already code to format base::Time values
  384. // somewhere, but I haven't found it yet. In any case, doing it ourselves
  385. // here means we can keep the format stable.
  386. base::Time::Exploded exploded;
  387. info.last_modified.UTCExplode(&exploded);
  388. if (info.last_modified.is_null() || !exploded.HasValidValues()) {
  389. memset(&exploded, 0, sizeof(exploded));
  390. }
  391. retval += "modified:" + NumberToString(exploded.year) + ":" +
  392. NumberToString(exploded.month) + ":" +
  393. NumberToString(exploded.day_of_month) + ":" +
  394. NumberToString(exploded.hour) + ":" +
  395. NumberToString(exploded.minute) + ":" +
  396. NumberToString(exploded.second) + ":" +
  397. NumberToString(exploded.millisecond) + ":UTC;";
  398. retval += "etag:" + info.etag + ";";
  399. retval += "sandbox:" + info.sandbox_isa + ";";
  400. retval += "extra_flags:" + info.extra_flags + ";";
  401. return retval;
  402. }
  403. int PnaclTranslationCache::DoomEntriesBetween(base::Time initial,
  404. base::Time end,
  405. CompletionOnceCallback callback) {
  406. return disk_cache_->DoomEntriesBetween(initial, end, std::move(callback));
  407. }
  408. } // namespace pnacl