diff options
Diffstat (limited to 'net/disk_cache/simple')
-rw-r--r-- | net/disk_cache/simple/simple_backend_impl.cc | 154 | ||||
-rw-r--r-- | net/disk_cache/simple/simple_backend_impl.h | 87 | ||||
-rw-r--r-- | net/disk_cache/simple/simple_disk_format.h | 28 | ||||
-rw-r--r-- | net/disk_cache/simple/simple_entry_impl.cc | 266 | ||||
-rw-r--r-- | net/disk_cache/simple/simple_entry_impl.h | 112 | ||||
-rw-r--r-- | net/disk_cache/simple/simple_synchronous_entry.cc | 322 | ||||
-rw-r--r-- | net/disk_cache/simple/simple_synchronous_entry.h | 111 |
7 files changed, 1080 insertions, 0 deletions
diff --git a/net/disk_cache/simple/simple_backend_impl.cc b/net/disk_cache/simple/simple_backend_impl.cc new file mode 100644 index 0000000..a9da4ad --- /dev/null +++ b/net/disk_cache/simple/simple_backend_impl.cc @@ -0,0 +1,154 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/disk_cache/simple/simple_backend_impl.h" + +#include "base/bind.h" +#include "base/callback.h" +#include "base/file_util.h" +#include "base/location.h" +#include "base/message_loop_proxy.h" +#include "base/threading/worker_pool.h" +#include "net/base/net_errors.h" +#include "net/disk_cache/simple/simple_entry_impl.h" + +using base::FilePath; +using base::MessageLoopProxy; +using base::Time; +using base::WorkerPool; +using file_util::DirectoryExists; +using file_util::CreateDirectory; + +namespace { + +const char* kSimpleBackendSubdirectory = "Simple"; + +} // namespace + +namespace disk_cache { + +// static +int SimpleBackendImpl::CreateBackend( + const FilePath& full_path, + bool force, + int max_bytes, + net::CacheType type, + uint32 flags, + scoped_refptr<base::TaskRunner> task_runner, + net::NetLog* net_log, + Backend** backend, + const CompletionCallback& callback) { + // TODO(gavinp): Use the |net_log|. + DCHECK_EQ(net::DISK_CACHE, type); + FilePath simple_cache_path = + full_path.AppendASCII(kSimpleBackendSubdirectory); + WorkerPool::PostTask(FROM_HERE, + base::Bind(&SimpleBackendImpl::EnsureCachePathExists, + simple_cache_path, + MessageLoopProxy::current(), callback, + backend), + true); + return net::ERR_IO_PENDING; +} + +SimpleBackendImpl::~SimpleBackendImpl() { +} + +net::CacheType SimpleBackendImpl::GetCacheType() const { + return net::DISK_CACHE; +} + +int32 SimpleBackendImpl::GetEntryCount() const { + NOTIMPLEMENTED(); + return 0; +} + +int SimpleBackendImpl::OpenEntry(const std::string& key, + Entry** entry, + const CompletionCallback& callback) { + return SimpleEntryImpl::OpenEntry(path_, key, entry, callback); +} + +int SimpleBackendImpl::CreateEntry(const std::string& key, + Entry** entry, + const CompletionCallback& callback) { + return SimpleEntryImpl::CreateEntry(path_, key, entry, callback); +} + +int SimpleBackendImpl::DoomEntry(const std::string& key, + const net::CompletionCallback& callback) { + return SimpleEntryImpl::DoomEntry(path_, key, callback); +} + +int SimpleBackendImpl::DoomAllEntries(const CompletionCallback& callback) { + NOTIMPLEMENTED(); + return net::ERR_FAILED; +} + +int SimpleBackendImpl::DoomEntriesBetween( + const Time initial_time, + const Time end_time, + const CompletionCallback& callback) { + NOTIMPLEMENTED(); + return net::ERR_FAILED; +} + +int SimpleBackendImpl::DoomEntriesSince( + const Time initial_time, + const CompletionCallback& callback) { + NOTIMPLEMENTED(); + return net::ERR_FAILED; +} + +int SimpleBackendImpl::OpenNextEntry(void** iter, + Entry** next_entry, + const CompletionCallback& callback) { + NOTIMPLEMENTED(); + return net::ERR_FAILED; +} + +void SimpleBackendImpl::EndEnumeration(void** iter) { + NOTIMPLEMENTED(); +} + +void SimpleBackendImpl::GetStats( + std::vector<std::pair<std::string, std::string> >* stats) { + NOTIMPLEMENTED(); +} + +void SimpleBackendImpl::OnExternalCacheHit(const std::string& key) { + NOTIMPLEMENTED(); +} + +SimpleBackendImpl::SimpleBackendImpl( + const FilePath& path) : path_(path) { +} + +// static +void SimpleBackendImpl::EnsureCachePathExists( + const FilePath& path, + const scoped_refptr<base::TaskRunner>& callback_runner, + const CompletionCallback& callback, + Backend** backend) { + int result = net::OK; + if (!DirectoryExists(path) && !CreateDirectory(path)) + result = net::ERR_FAILED; + callback_runner->PostTask(FROM_HERE, + base::Bind(&SimpleBackendImpl::OnCachePathCreated, + result, path, callback, backend)); +} + +// static +void SimpleBackendImpl::OnCachePathCreated(int result, + const FilePath& path, + const CompletionCallback& callback, + Backend** backend) { + if (result == net::OK) + *backend = new SimpleBackendImpl(path); + else + *backend = NULL; + callback.Run(result); +} + +} // namespace disk_cache diff --git a/net/disk_cache/simple/simple_backend_impl.h b/net/disk_cache/simple/simple_backend_impl.h new file mode 100644 index 0000000..9e56915 --- /dev/null +++ b/net/disk_cache/simple/simple_backend_impl.h @@ -0,0 +1,87 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_DISK_CACHE_SIMPLE_SIMPLE_BACKEND_IMPL_H_ +#define NET_DISK_CACHE_SIMPLE_SIMPLE_BACKEND_IMPL_H_ + +#include <string> +#include <utility> +#include <vector> + +#include "base/compiler_specific.h" +#include "base/files/file_path.h" +#include "base/task_runner.h" +#include "net/base/cache_type.h" +#include "net/disk_cache/disk_cache.h" + +namespace disk_cache { + +// SimpleBackendImpl is a new cache backend that stores entries in individual +// files. + +// It is currently a work in progress, missing many features of a real cache, +// such as eviction. + +// See http://www.chromium.org/developers/design-documents/network-stack/disk-cache/very-simple-backend + +class SimpleBackendImpl : public Backend { + public: + virtual ~SimpleBackendImpl(); + + static int CreateBackend(const base::FilePath& full_path, + bool force, + int max_bytes, + net::CacheType type, + uint32 flags, + scoped_refptr<base::TaskRunner> thread, + net::NetLog* net_log, + Backend** backend, + const CompletionCallback& callback); + + // From Backend: + virtual net::CacheType GetCacheType() const OVERRIDE; + virtual int32 GetEntryCount() const OVERRIDE; + virtual int OpenEntry(const std::string& key, Entry** entry, + const CompletionCallback& callback) OVERRIDE; + virtual int CreateEntry(const std::string& key, Entry** entry, + const CompletionCallback& callback) OVERRIDE; + virtual int DoomEntry(const std::string& key, + const CompletionCallback& callback) OVERRIDE; + virtual int DoomAllEntries(const CompletionCallback& callback) OVERRIDE; + virtual int DoomEntriesBetween(base::Time initial_time, + base::Time end_time, + const CompletionCallback& callback) OVERRIDE; + virtual int DoomEntriesSince(base::Time initial_time, + const CompletionCallback& callback) OVERRIDE; + virtual int OpenNextEntry(void** iter, Entry** next_entry, + const CompletionCallback& callback) OVERRIDE; + virtual void EndEnumeration(void** iter) OVERRIDE; + virtual void GetStats( + std::vector<std::pair<std::string, std::string> >* stats) OVERRIDE; + virtual void OnExternalCacheHit(const std::string& key) OVERRIDE; + + private: + explicit SimpleBackendImpl(const base::FilePath& path); + + // Creates the Cache directory if needed. Performs blocking IO, so it cannot + // be called on IO thread. + static void EnsureCachePathExists( + const base::FilePath& path, + const scoped_refptr<base::TaskRunner>& callback_runner, + const CompletionCallback& callback, + Backend** backend); + + // IO thread completion of cache creation, called from EnsureCachePath exists + // to complete initialization of the cache on the IO thread. + static void OnCachePathCreated(int result, + const base::FilePath& path, + const CompletionCallback& callback, + Backend** backend); + + const base::FilePath path_; +}; + +} // namespace disk_cache + +#endif // NET_DISK_CACHE_SIMPLE_SIMPLE_BACKEND_IMPL_H_ diff --git a/net/disk_cache/simple/simple_disk_format.h b/net/disk_cache/simple/simple_disk_format.h new file mode 100644 index 0000000..052adf2 --- /dev/null +++ b/net/disk_cache/simple/simple_disk_format.h @@ -0,0 +1,28 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_DISK_CACHE_SIMPLE_SIMPLE_DISK_FORMAT_H_ +#define NET_DISK_CACHE_SIMPLE_SIMPLE_DISK_FORMAT_H_ + +#include "base/port.h" + +namespace disk_cache { + +const uint64 kSimpleInitialMagicNumber = GG_UINT64_C(0xfcfb6d1ba7725c30); + +// A file in the Simple cache consists of a SimpleFileHeader followed +// by data. + +const uint32 kSimpleVersion = 1; + +struct SimpleFileHeader { + uint64 initial_magic_number; + uint32 version; + uint32 key_length; + uint32 key_hash; +}; + +} // namespace disk_cache + +#endif // NET_DISK_CACHE_SIMPLE_SIMPLE_DISK_FORMAT_H_ diff --git a/net/disk_cache/simple/simple_entry_impl.cc b/net/disk_cache/simple/simple_entry_impl.cc new file mode 100644 index 0000000..ba19844 --- /dev/null +++ b/net/disk_cache/simple/simple_entry_impl.cc @@ -0,0 +1,266 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/disk_cache/simple/simple_entry_impl.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/callback.h" +#include "base/location.h" +#include "base/message_loop_proxy.h" +#include "base/threading/worker_pool.h" +#include "net/base/io_buffer.h" +#include "net/base/net_errors.h" +#include "net/disk_cache/simple/simple_synchronous_entry.h" + +namespace { + +typedef disk_cache::Entry::CompletionCallback CompletionCallback; +typedef disk_cache::SimpleSynchronousEntry::SynchronousCreationCallback + SynchronousCreationCallback; +typedef disk_cache::SimpleSynchronousEntry::SynchronousOperationCallback + SynchronousOperationCallback; + +} // namespace + +namespace disk_cache { + +using base::FilePath; +using base::MessageLoopProxy; +using base::Time; +using base::WeakPtr; +using base::WorkerPool; + +// static +int SimpleEntryImpl::OpenEntry(const FilePath& path, + const std::string& key, + Entry** entry, + const CompletionCallback& callback) { + SynchronousCreationCallback sync_creation_callback = + base::Bind(&SimpleEntryImpl::CreationOperationComplete, callback, entry); + WorkerPool::PostTask(FROM_HERE, + base::Bind(&SimpleSynchronousEntry::OpenEntry, path, key, + MessageLoopProxy::current(), + sync_creation_callback), + true); + return net::ERR_IO_PENDING; +} + +// static +int SimpleEntryImpl::CreateEntry(const FilePath& path, + const std::string& key, + Entry** entry, + const CompletionCallback& callback) { + SynchronousCreationCallback sync_creation_callback = + base::Bind(&SimpleEntryImpl::CreationOperationComplete, callback, entry); + WorkerPool::PostTask(FROM_HERE, + base::Bind(&SimpleSynchronousEntry::CreateEntry, path, + key, MessageLoopProxy::current(), + sync_creation_callback), + true); + return net::ERR_IO_PENDING; +} + +// static +int SimpleEntryImpl::DoomEntry(const FilePath& path, + const std::string& key, + const CompletionCallback& callback) { + WorkerPool::PostTask(FROM_HERE, + base::Bind(&SimpleSynchronousEntry::DoomEntry, path, key, + MessageLoopProxy::current(), callback), + true); + return net::ERR_IO_PENDING; +} + +void SimpleEntryImpl::Doom() { + if (synchronous_entry_in_use_by_worker_) { + NOTIMPLEMENTED(); + return; + } + WorkerPool::PostTask(FROM_HERE, + base::Bind(&SimpleSynchronousEntry::DoomAndClose, + base::Unretained(synchronous_entry_)), + true); + synchronous_entry_ = NULL; + has_been_doomed_ = true; +} + +void SimpleEntryImpl::Close() { + if (synchronous_entry_in_use_by_worker_) { + NOTIMPLEMENTED(); + delete this; + return; + } + DCHECK(synchronous_entry_ || has_been_doomed_); + if (!has_been_doomed_) { + WorkerPool::PostTask(FROM_HERE, + base::Bind(&SimpleSynchronousEntry::Close, + base::Unretained(synchronous_entry_)), + true); + synchronous_entry_ = NULL; + } + // Entry::Close() is expected to release this entry. See disk_cache.h for + // details. + delete this; +} + +std::string SimpleEntryImpl::GetKey() const { + return key_; +} + +Time SimpleEntryImpl::GetLastUsed() const { + if (synchronous_entry_in_use_by_worker_) { + NOTIMPLEMENTED(); + CHECK(false); + } + return synchronous_entry_->last_used(); +} + +Time SimpleEntryImpl::GetLastModified() const { + if (synchronous_entry_in_use_by_worker_) { + NOTIMPLEMENTED(); + CHECK(false); + } + return synchronous_entry_->last_modified(); +} + +int32 SimpleEntryImpl::GetDataSize(int index) const { + if (synchronous_entry_in_use_by_worker_) { + NOTIMPLEMENTED(); + CHECK(false); + } + return synchronous_entry_->data_size(index); +} + +int SimpleEntryImpl::ReadData(int index, + int offset, + net::IOBuffer* buf, + int buf_len, + const CompletionCallback& callback) { + // TODO(gavinp): Add support for overlapping reads. The net::HttpCache does + // make overlapping read requests when multiple transactions access the same + // entry as read only. + if (synchronous_entry_in_use_by_worker_) { + NOTIMPLEMENTED(); + CHECK(false); + } + synchronous_entry_in_use_by_worker_ = true; + SynchronousOperationCallback sync_operation_callback = + base::Bind(&SimpleEntryImpl::EntryOperationComplete, + callback, weak_ptr_factory_.GetWeakPtr()); + WorkerPool::PostTask(FROM_HERE, + base::Bind(&SimpleSynchronousEntry::ReadData, + base::Unretained(synchronous_entry_), + index, offset, make_scoped_refptr(buf), + buf_len, sync_operation_callback), + true); + return net::ERR_IO_PENDING; +} + +int SimpleEntryImpl::WriteData(int index, + int offset, + net::IOBuffer* buf, + int buf_len, + const CompletionCallback& callback, + bool truncate) { + if (synchronous_entry_in_use_by_worker_) { + NOTIMPLEMENTED(); + CHECK(false); + } + synchronous_entry_in_use_by_worker_ = true; + SynchronousOperationCallback sync_operation_callback = + base::Bind(&SimpleEntryImpl::EntryOperationComplete, + callback, weak_ptr_factory_.GetWeakPtr()); + WorkerPool::PostTask(FROM_HERE, + base::Bind(&SimpleSynchronousEntry::WriteData, + base::Unretained(synchronous_entry_), + index, offset, make_scoped_refptr(buf), + buf_len, sync_operation_callback, truncate), + true); + return net::ERR_IO_PENDING; +} + +int SimpleEntryImpl::ReadSparseData(int64 offset, + net::IOBuffer* buf, + int buf_len, + const CompletionCallback& callback) { + // TODO(gavinp): Determine if the simple backend should support sparse data. + NOTIMPLEMENTED(); + return net::ERR_FAILED; +} + +int SimpleEntryImpl::WriteSparseData(int64 offset, + net::IOBuffer* buf, + int buf_len, + const CompletionCallback& callback) { + // TODO(gavinp): Determine if the simple backend should support sparse data. + NOTIMPLEMENTED(); + return net::ERR_FAILED; +} + +int SimpleEntryImpl::GetAvailableRange(int64 offset, + int len, + int64* start, + const CompletionCallback& callback) { + // TODO(gavinp): Determine if the simple backend should support sparse data. + NOTIMPLEMENTED(); + return net::ERR_FAILED; +} + +bool SimpleEntryImpl::CouldBeSparse() const { + // TODO(gavinp): Determine if the simple backend should support sparse data. + return false; +} + +void SimpleEntryImpl::CancelSparseIO() { + // TODO(gavinp): Determine if the simple backend should support sparse data. + NOTIMPLEMENTED(); +} + +int SimpleEntryImpl::ReadyForSparseIO(const CompletionCallback& callback) { + // TODO(gavinp): Determine if the simple backend should support sparse data. + NOTIMPLEMENTED(); + return net::ERR_FAILED; +} + +SimpleEntryImpl::SimpleEntryImpl( + SimpleSynchronousEntry* synchronous_entry) + : ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)), + key_(synchronous_entry->key()), + synchronous_entry_(synchronous_entry), + synchronous_entry_in_use_by_worker_(false), + has_been_doomed_(false) { + DCHECK(synchronous_entry); +} + +SimpleEntryImpl::~SimpleEntryImpl() { + DCHECK(!synchronous_entry_); +} + +// static +void SimpleEntryImpl::CreationOperationComplete( + const CompletionCallback& completion_callback, + Entry** out_entry, + SimpleSynchronousEntry* sync_entry) { + if (!sync_entry) { + completion_callback.Run(net::ERR_FAILED); + return; + } + *out_entry = new SimpleEntryImpl(sync_entry); + completion_callback.Run(net::OK); +} + +// static +void SimpleEntryImpl::EntryOperationComplete( + const CompletionCallback& completion_callback, + base::WeakPtr<SimpleEntryImpl> entry, + int result) { + if (entry) { + DCHECK(entry->synchronous_entry_in_use_by_worker_); + entry->synchronous_entry_in_use_by_worker_ = false; + } + completion_callback.Run(result); +} + +} // namespace disk_cache diff --git a/net/disk_cache/simple/simple_entry_impl.h b/net/disk_cache/simple/simple_entry_impl.h new file mode 100644 index 0000000..26f9d4d --- /dev/null +++ b/net/disk_cache/simple/simple_entry_impl.h @@ -0,0 +1,112 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_DISK_CACHE_SIMPLE_SIMPLE_ENTRY_IMPL_H_ +#define NET_DISK_CACHE_SIMPLE_SIMPLE_ENTRY_IMPL_H_ + +#include <string> + +#include "base/memory/weak_ptr.h" +#include "net/disk_cache/disk_cache.h" + +namespace net { +class IOBuffer; +} + +namespace disk_cache { + +class SimpleSynchronousEntry; + +// SimpleEntryImpl is the IO thread interface to an entry in the very simple +// disk cache. It proxies for the SimpleSynchronousEntry, which performs IO +// on the worker thread. +class SimpleEntryImpl : public Entry { + public: + static int OpenEntry(const base::FilePath& path, + const std::string& key, + Entry** entry, + const CompletionCallback& callback); + + static int CreateEntry(const base::FilePath& path, + const std::string& key, + Entry** entry, + const CompletionCallback& callback); + + static int DoomEntry(const base::FilePath& path, + const std::string& key, + const CompletionCallback& callback); + + // From Entry: + virtual void Doom() OVERRIDE; + virtual void Close() OVERRIDE; + virtual std::string GetKey() const OVERRIDE; + virtual base::Time GetLastUsed() const OVERRIDE; + virtual base::Time GetLastModified() const OVERRIDE; + virtual int32 GetDataSize(int index) const OVERRIDE; + virtual int ReadData(int index, + int offset, + net::IOBuffer* buf, + int buf_len, + const CompletionCallback& callback) OVERRIDE; + virtual int WriteData(int index, + int offset, + net::IOBuffer* buf, + int buf_len, + const CompletionCallback& callback, + bool truncate) OVERRIDE; + virtual int ReadSparseData(int64 offset, + net::IOBuffer* buf, + int buf_len, + const CompletionCallback& callback) OVERRIDE; + virtual int WriteSparseData(int64 offset, + net::IOBuffer* buf, + int buf_len, + const CompletionCallback& callback) OVERRIDE; + virtual int GetAvailableRange(int64 offset, + int len, + int64* start, + const CompletionCallback& callback) OVERRIDE; + virtual bool CouldBeSparse() const OVERRIDE; + virtual void CancelSparseIO() OVERRIDE; + virtual int ReadyForSparseIO(const CompletionCallback& callback) OVERRIDE; + + private: + explicit SimpleEntryImpl(SimpleSynchronousEntry* synchronous_entry); + + virtual ~SimpleEntryImpl(); + + // Called after a SimpleSynchronousEntry has completed CreateEntry() or + // OpenEntry(). Constructs the new SimpleEntryImpl (if |result| is net::OK) + // and passes it back to the caller via |out_entry|. Also runs + // |completion_callback|. + static void CreationOperationComplete( + const CompletionCallback& completion_callback, + Entry** out_entry, + SimpleSynchronousEntry* sync_entry); + + // Called after a SimpleSynchronousEntry has completed an asynchronous IO + // operation, such as ReadData() or WriteData(). Calls |completion_callback|. + static void EntryOperationComplete( + const CompletionCallback& completion_callback, + base::WeakPtr<SimpleEntryImpl> entry, + int result); + + base::WeakPtrFactory<SimpleEntryImpl> weak_ptr_factory_; + std::string key_; + + // The |synchronous_entry_| is the worker thread object that performs IO on + // entries. + SimpleSynchronousEntry* synchronous_entry_; + + // Set to true when a worker operation is posted on the |synchronous_entry_|, + // and false after. Used to insure thread safety by not allowing multiple + // threads to access the |synchronous_entry_| simultaneously. + bool synchronous_entry_in_use_by_worker_; + + bool has_been_doomed_; +}; + +} // namespace disk_cache + +#endif // NET_DISK_CACHE_SIMPLE_SIMPLE_ENTRY_IMPL_H_ diff --git a/net/disk_cache/simple/simple_synchronous_entry.cc b/net/disk_cache/simple/simple_synchronous_entry.cc new file mode 100644 index 0000000..6a8b7f5 --- /dev/null +++ b/net/disk_cache/simple/simple_synchronous_entry.cc @@ -0,0 +1,322 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/disk_cache/simple/simple_synchronous_entry.h" + +#include <algorithm> +#include <cstring> +#include <limits> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/file_util.h" +#include "base/hash.h" +#include "base/location.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop_proxy.h" +#include "base/sha1.h" +#include "base/stringprintf.h" +#include "base/task_runner.h" +#include "net/base/io_buffer.h" +#include "net/base/net_errors.h" +#include "net/disk_cache/simple/simple_disk_format.h" + +using base::ClosePlatformFile; +using base::FilePath; +using base::GetPlatformFileInfo; +using base::PlatformFileError; +using base::PlatformFileInfo; +using base::PLATFORM_FILE_CREATE; +using base::PLATFORM_FILE_OK; +using base::PLATFORM_FILE_OPEN; +using base::PLATFORM_FILE_READ; +using base::PLATFORM_FILE_WRITE; +using base::ReadPlatformFile; +using base::TaskRunner; +using base::Time; +using base::TruncatePlatformFile; +using base::WritePlatformFile; + +namespace { + +std::string GetFilenameForKeyAndIndex(const std::string& key, int index) { + const std::string sha_hash = base::SHA1HashString(key); + return StringPrintf("%02x%02x%02x%02x%02x_%1d", + implicit_cast<unsigned char>(sha_hash[0]), + implicit_cast<unsigned char>(sha_hash[1]), + implicit_cast<unsigned char>(sha_hash[2]), + implicit_cast<unsigned char>(sha_hash[3]), + implicit_cast<unsigned char>(sha_hash[4]), index); +} + +int32 DataSizeFromKeyAndFileSize(size_t key_size, int64 file_size) { + int64 data_size = file_size - key_size - sizeof(disk_cache::SimpleFileHeader); + DCHECK_GE(implicit_cast<int64>(std::numeric_limits<int32>::max()), data_size); + return data_size; +} + +int64 FileOffsetFromDataOffset(size_t key_size, int data_offset) { + const int64 headers_size = sizeof(disk_cache::SimpleFileHeader) + + key_size; + return headers_size + data_offset; +} + +} // namespace + +namespace disk_cache { + +// static +void SimpleSynchronousEntry::OpenEntry( + const FilePath& path, + const std::string& key, + const scoped_refptr<TaskRunner>& callback_runner, + const SynchronousCreationCallback& callback) { + SimpleSynchronousEntry* sync_entry = + new SimpleSynchronousEntry(callback_runner, path, key); + + if (!sync_entry->InitializeForOpen()) { + delete sync_entry; + sync_entry = NULL; + } + callback_runner->PostTask(FROM_HERE, base::Bind(callback, sync_entry)); +} + +// static +void SimpleSynchronousEntry::CreateEntry( + const FilePath& path, + const std::string& key, + const scoped_refptr<TaskRunner>& callback_runner, + const SynchronousCreationCallback& callback) { + SimpleSynchronousEntry* sync_entry = + new SimpleSynchronousEntry(callback_runner, path, key); + + if (!sync_entry->InitializeForCreate()) { + delete sync_entry; + sync_entry = NULL; + } + callback_runner->PostTask(FROM_HERE, base::Bind(callback, sync_entry)); +} + +// static +void SimpleSynchronousEntry::DoomEntry( + const FilePath& path, + const std::string& key, + scoped_refptr<TaskRunner> callback_runner, + const net::CompletionCallback& callback) { + for (int i = 0; i < kIndexCount; ++i) { + FilePath to_delete = path.AppendASCII(GetFilenameForKeyAndIndex(key, i)); + bool ALLOW_UNUSED result = file_util::Delete(to_delete, false); + DLOG_IF(ERROR, !result) << "Could not delete " << to_delete.MaybeAsASCII(); + } + if (!callback.is_null()) + callback_runner->PostTask(FROM_HERE, base::Bind(callback, net::OK)); +} + +void SimpleSynchronousEntry::DoomAndClose() { + scoped_refptr<TaskRunner> callback_runner = callback_runner_; + FilePath path = path_; + std::string key = key_; + + Close(); + // |this| is now deleted. + + DoomEntry(path, key, callback_runner, net::CompletionCallback()); +} + +void SimpleSynchronousEntry::Close() { + for (int i = 0; i < kIndexCount; ++i) { + bool ALLOW_UNUSED result = ClosePlatformFile(files_[i]); + DLOG_IF(INFO, !result) << "Could not Close() file."; + } + delete this; +} + +void SimpleSynchronousEntry::ReadData( + int index, + int offset, + net::IOBuffer* buf, + int buf_len, + const SynchronousOperationCallback& callback) { + DCHECK(initialized_); + + int64 file_offset = FileOffsetFromDataOffset(key_.size(), offset); + int bytes_read = ReadPlatformFile(files_[index], file_offset, + buf->data(), buf_len); + if (bytes_read > 0) + last_used_ = Time::Now(); + int result = (bytes_read >= 0) ? bytes_read : net::ERR_FAILED; + callback_runner_->PostTask(FROM_HERE, base::Bind(callback, result)); +} + +void SimpleSynchronousEntry::WriteData( + int index, + int offset, + net::IOBuffer* buf, + int buf_len, + const SynchronousOperationCallback& callback, + bool truncate) { + DCHECK(initialized_); + + int64 file_offset = FileOffsetFromDataOffset(key_.size(), offset); + if (buf_len > 0) { + if (WritePlatformFile(files_[index], file_offset, buf->data(), buf_len) != + buf_len) { + callback_runner_->PostTask(FROM_HERE, + base::Bind(callback, net::ERR_FAILED)); + return; + } + data_size_[index] = std::max(data_size_[index], offset + buf_len); + } + if (truncate) { + data_size_[index] = offset + buf_len; + if (!TruncatePlatformFile(files_[index], file_offset + buf_len)) { + callback_runner_->PostTask(FROM_HERE, + base::Bind(callback, net::ERR_FAILED)); + return; + } + } + last_modified_ = Time::Now(); + callback_runner_->PostTask(FROM_HERE, base::Bind(callback, buf_len)); +} + +SimpleSynchronousEntry::SimpleSynchronousEntry( + const scoped_refptr<TaskRunner>& callback_runner, + const FilePath& path, + const std::string& key) + : callback_runner_(callback_runner), + path_(path), + key_(key), + initialized_(false) { +} + +SimpleSynchronousEntry::~SimpleSynchronousEntry() { +} + +bool SimpleSynchronousEntry::OpenOrCreateFiles(bool create) { + for (int i = 0; i < kIndexCount; ++i) { + FilePath filename = path_.AppendASCII(GetFilenameForKeyAndIndex(key_, i)); + int flags = PLATFORM_FILE_READ | PLATFORM_FILE_WRITE; + if (create) + flags |= PLATFORM_FILE_CREATE; + else + flags |= PLATFORM_FILE_OPEN; + PlatformFileError error; + files_[i] = CreatePlatformFile(filename, flags, NULL, &error); + if (error != PLATFORM_FILE_OK) { + DVLOG(8) << "CreatePlatformFile error " << error << " while " + << (create ? "creating " : "opening ") + << filename.MaybeAsASCII(); + while (--i >= 0) { + bool ALLOW_UNUSED did_close = ClosePlatformFile(files_[i]); + DLOG_IF(INFO, !did_close) << "Could not close file " + << filename.MaybeAsASCII(); + } + return false; + } + } + + for (int i = 0; i < kIndexCount; ++i) { + PlatformFileInfo file_info; + bool success = GetPlatformFileInfo(files_[i], &file_info); + if (!success) { + DLOG(WARNING) << "Could not get platform file info."; + continue; + } + last_used_ = std::max(last_used_, file_info.last_accessed); + last_modified_ = std::max(last_modified_, file_info.last_modified); + data_size_[i] = DataSizeFromKeyAndFileSize(key_.size(), file_info.size); + } + + return true; +} + +bool SimpleSynchronousEntry::InitializeForOpen() { + DCHECK(!initialized_); + if (!OpenOrCreateFiles(false)) + return false; + + for (int i = 0; i < kIndexCount; ++i) { + SimpleFileHeader header; + int header_read_result = + ReadPlatformFile(files_[i], 0, reinterpret_cast<char*>(&header), + sizeof(header)); + if (header_read_result != sizeof(header)) { + DLOG(WARNING) << "Cannot read header from entry."; + return false; + } + + if (header.initial_magic_number != kSimpleInitialMagicNumber) { + // TODO(gavinp): This seems very bad; for now we log at WARNING, but we + // should give consideration to not saturating the log with these if that + // becomes a problem. + DLOG(WARNING) << "Magic number did not match."; + return false; + } + + if (header.version != kSimpleVersion) { + DLOG(WARNING) << "Unreadable version."; + return false; + } + + scoped_ptr<char[]> key(new char[header.key_length]); + int key_read_result = ReadPlatformFile(files_[i], sizeof(header), + key.get(), header.key_length); + if (key_read_result != implicit_cast<int>(header.key_length)) { + DLOG(WARNING) << "Cannot read key from entry."; + return false; + } + if (header.key_length != key_.size() || + std::memcmp(key_.data(), key.get(), key_.size()) != 0) { + // TODO(gavinp): Since the way we use Entry SHA to name entries means this + // is expected to occur at some frequency, add unit_tests that this does + // is handled gracefully at higher levels. + DLOG(WARNING) << "Key mismatch on open."; + return false; + } + + if (base::Hash(key.get(), header.key_length) != header.key_hash) { + DLOG(WARNING) << "Hash mismatch on key."; + return false; + } + } + + initialized_ = true; + return true; +} + +bool SimpleSynchronousEntry::InitializeForCreate() { + DCHECK(!initialized_); + if (!OpenOrCreateFiles(true)) { + DLOG(WARNING) << "Could not create platform files."; + return false; + } + + for (int i = 0; i < kIndexCount; ++i) { + SimpleFileHeader header; + header.initial_magic_number = kSimpleInitialMagicNumber; + header.version = kSimpleVersion; + + header.key_length = key_.size(); + header.key_hash = base::Hash(key_); + + if (WritePlatformFile(files_[i], 0, reinterpret_cast<char*>(&header), + sizeof(header)) != sizeof(header)) { + // TODO(gavinp): Clean up created files. + DLOG(WARNING) << "Could not write headers to new cache entry."; + return false; + } + + if (WritePlatformFile(files_[i], sizeof(header), key_.data(), + key_.size()) != implicit_cast<int>(key_.size())) { + // TODO(gavinp): Clean up created files. + DLOG(WARNING) << "Could not write keys to new cache entry."; + return false; + } + } + + initialized_ = true; + return true; +} + +} // namespace disk_cache diff --git a/net/disk_cache/simple/simple_synchronous_entry.h b/net/disk_cache/simple/simple_synchronous_entry.h new file mode 100644 index 0000000..1bf6a54 --- /dev/null +++ b/net/disk_cache/simple/simple_synchronous_entry.h @@ -0,0 +1,111 @@ +// Copyright (c) 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_DISK_CACHE_SIMPLE_SIMPLE_SYNCHRONOUS_ENTRY_H_ +#define NET_DISK_CACHE_SIMPLE_SIMPLE_SYNCHRONOUS_ENTRY_H_ + +#include <string> + +#include "base/callback_forward.h" +#include "base/files/file_path.h" +#include "base/memory/ref_counted.h" +#include "base/platform_file.h" +#include "base/task_runner.h" +#include "base/time.h" +#include "net/base/completion_callback.h" + +namespace base { +class SingleThreadTaskRunner; +} + +namespace net { +class IOBuffer; +} + +namespace disk_cache { + +// Worker thread interface to the very simple cache. This interface is not +// thread safe, and callers must insure that it is only ever accessed from +// a single thread between synchronization points. +class SimpleSynchronousEntry { + public: + typedef base::Callback<void(SimpleSynchronousEntry*)> + SynchronousCreationCallback; + + typedef base::Callback<void(int)> + SynchronousOperationCallback; + + static void OpenEntry( + const base::FilePath& path, + const std::string& key, + const scoped_refptr<base::TaskRunner>& callback_runner, + const SynchronousCreationCallback& callback); + + static void CreateEntry( + const base::FilePath& path, + const std::string& key, + const scoped_refptr<base::TaskRunner>& callback_runner, + const SynchronousCreationCallback& callback); + + // Deletes an entry without first Opening it. Does not check if there is + // already an Entry object in memory holding the open files. Be careful! This + // is meant to be used by the Backend::DoomEntry() call. |callback| will be + // run by |callback_runner|. + static void DoomEntry(const base::FilePath& path, + const std::string& key, + scoped_refptr<base::TaskRunner> callback_runner, + const net::CompletionCallback& callback); + + // N.B. DoomAndClose(), Close(), ReadData() and WriteData() may block on IO. + void DoomAndClose(); + void Close(); + void ReadData(int index, + int offset, + net::IOBuffer* buf, + int buf_len, + const SynchronousOperationCallback& callback); + void WriteData(int index, + int offset, + net::IOBuffer* buf, + int buf_len, + const SynchronousOperationCallback& callback, + bool truncate); + + std::string key() const { return key_; } + base::Time last_used() const { return last_used_; } + base::Time last_modified() const { return last_modified_; } + int32 data_size(int index) const { return data_size_[index]; } + + private: + static const int kIndexCount = 3; + + SimpleSynchronousEntry( + const scoped_refptr<base::TaskRunner>& callback_runner, + const base::FilePath& path, + const std::string& key); + + // Like Entry, the SimpleSynchronousEntry self releases when Close() is + // called. + ~SimpleSynchronousEntry(); + + bool OpenOrCreateFiles(bool create); + bool InitializeForOpen(); + bool InitializeForCreate(); + + scoped_refptr<base::TaskRunner> callback_runner_; + const base::FilePath path_; + const std::string key_; + + bool initialized_; + + base::Time last_used_; + base::Time last_modified_; + int32 data_size_[kIndexCount]; + + base::PlatformFile files_[kIndexCount]; +}; + +} // namespace disk_cache + +#endif // NET_DISK_CACHE_SIMPLE_SIMPLE_SYNCHRONOUS_ENTRY_H_ |