From 753a99e15e7d714883aabefc4ac195b3be521834 Mon Sep 17 00:00:00 2001 From: "rch@chromium.org" Date: Wed, 28 Nov 2012 19:48:49 +0000 Subject: Adding a simple class for dumping the contents of a cache. Use this new class in dump_cache. Reverted: 169769 Initially landed: 169704 Review URL: https://codereview.chromium.org/11316204 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@170024 0039d316-1c4b-4281-b951-d872f2087c98 --- net/net.gyp | 49 +- net/tools/dump_cache/cache_dumper.cc | 22 +- net/tools/dump_cache/cache_dumper.h | 19 +- net/tools/dump_cache/dump_cache.cc | 59 +- net/tools/dump_cache/dump_files.cc | 25 +- net/tools/dump_cache/dump_files.h | 23 + net/tools/dump_cache/simple_cache_dumper.cc | 268 ++++++++ net/tools/dump_cache/simple_cache_dumper.h | 94 +++ net/tools/dump_cache/upgrade.cc | 922 --------------------------- net/tools/dump_cache/upgrade_win.cc | 948 ++++++++++++++++++++++++++++ net/tools/dump_cache/upgrade_win.h | 22 + 11 files changed, 1445 insertions(+), 1006 deletions(-) create mode 100644 net/tools/dump_cache/dump_files.h create mode 100644 net/tools/dump_cache/simple_cache_dumper.cc create mode 100644 net/tools/dump_cache/simple_cache_dumper.h delete mode 100644 net/tools/dump_cache/upgrade.cc create mode 100644 net/tools/dump_cache/upgrade_win.cc create mode 100644 net/tools/dump_cache/upgrade_win.h (limited to 'net') diff --git a/net/net.gyp b/net/net.gyp index 795f627..57253726 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -1942,6 +1942,30 @@ 'server/web_socket.h', ], }, + { + 'target_name': 'dump_cache', + 'type': 'executable', + 'dependencies': [ + '../base/base.gyp:base', + 'net', + 'net_test_support', + ], + 'sources': [ + 'tools/dump_cache/cache_dumper.cc', + 'tools/dump_cache/cache_dumper.h', + 'tools/dump_cache/dump_cache.cc', + 'tools/dump_cache/dump_files.cc', + 'tools/dump_cache/dump_files.h', + 'tools/dump_cache/simple_cache_dumper.cc', + 'tools/dump_cache/simple_cache_dumper.h', + 'tools/dump_cache/upgrade_win.cc', + 'tools/dump_cache/upgrade_win.h', + 'tools/dump_cache/url_to_filename_encoder.cc', + 'tools/dump_cache/url_to_filename_encoder.h', + 'tools/dump_cache/url_utilities.h', + 'tools/dump_cache/url_utilities.cc', + ], + }, ], 'conditions': [ ['use_v8_in_net == 1', { @@ -2306,31 +2330,6 @@ }, ], }], - ['OS=="win"', { - 'targets': [ - { - # TODO(port): dump_cache is still Windows-specific. - 'target_name': 'dump_cache', - 'type': 'executable', - 'dependencies': [ - '../base/base.gyp:base', - 'net', - 'net_test_support', - ], - 'sources': [ - 'tools/dump_cache/cache_dumper.cc', - 'tools/dump_cache/cache_dumper.h', - 'tools/dump_cache/dump_cache.cc', - 'tools/dump_cache/dump_files.cc', - 'tools/dump_cache/upgrade.cc', - 'tools/dump_cache/url_to_filename_encoder.cc', - 'tools/dump_cache/url_to_filename_encoder.h', - 'tools/dump_cache/url_utilities.h', - 'tools/dump_cache/url_utilities.cc', - ], - }, - ], - }], ['test_isolation_mode != "noop"', { 'targets': [ { diff --git a/net/tools/dump_cache/cache_dumper.cc b/net/tools/dump_cache/cache_dumper.cc index dbe8273..7f2292b 100644 --- a/net/tools/dump_cache/cache_dumper.cc +++ b/net/tools/dump_cache/cache_dumper.cc @@ -13,6 +13,10 @@ #include "net/http/http_response_info.h" #include "net/tools/dump_cache/url_to_filename_encoder.h" +CacheDumper::CacheDumper(disk_cache::Backend* cache) + : cache_(cache) { +} + int CacheDumper::CreateEntry(const std::string& key, disk_cache::Entry** entry, const net::CompletionCallback& callback) { @@ -52,7 +56,7 @@ bool SafeCreateDirectory(const FilePath& path) { // Create the subdirectories individually while ((pos = path.value().find(backslash, pos)) != std::wstring::npos) { - std::wstring subdir = path.value().substr(0, pos); + FilePath::StringType subdir = path.value().substr(0, pos); CreateDirectoryW(subdir.c_str(), NULL); // we keep going even if directory creation failed. pos++; @@ -64,16 +68,21 @@ bool SafeCreateDirectory(const FilePath& path) { #endif } +DiskDumper::DiskDumper(const FilePath& path) + : path_(path), entry_(NULL) { + file_util::CreateDirectory(path); +} + int DiskDumper::CreateEntry(const std::string& key, disk_cache::Entry** entry, const net::CompletionCallback& callback) { // The URL may not start with a valid protocol; search for it. int urlpos = key.find("http"); std::string url = urlpos > 0 ? key.substr(urlpos) : key; - std::string base_path = WideToASCII(path_.value()); + std::string base_path = path_.MaybeAsASCII(); std::string new_path = net::UrlToFilenameEncoder::Encode(url, base_path, false); - entry_path_ = FilePath(ASCIIToWide(new_path)); + entry_path_ = FilePath::FromUTF8Unsafe(new_path); #ifdef WIN32_LARGE_FILENAME_SUPPORT // In order for long filenames to work, we'll need to prepend @@ -90,7 +99,7 @@ int DiskDumper::CreateEntry(const std::string& key, SafeCreateDirectory(entry_path_.DirName()); - std::wstring file = entry_path_.value(); + FilePath::StringType file = entry_path_.value(); #ifdef WIN32_LARGE_FILENAME_SUPPORT entry_ = CreateFileW(file.c_str(), GENERIC_WRITE|GENERIC_READ, 0, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0); @@ -185,10 +194,13 @@ int DiskDumper::WriteEntry(disk_cache::Entry* entry, int index, int offset, data = headers.c_str(); len = headers.size(); - } else if (index == 1) { // Stream 1 is the data. + } else if (index == 1) { data = buf->data(); len = buf_len; + } else { + return 0; } + #ifdef WIN32_LARGE_FILENAME_SUPPORT DWORD bytes; if (!WriteFile(entry_, data, len, &bytes, 0)) diff --git a/net/tools/dump_cache/cache_dumper.h b/net/tools/dump_cache/cache_dumper.h index 1c0c7c2..daca8fe 100644 --- a/net/tools/dump_cache/cache_dumper.h +++ b/net/tools/dump_cache/cache_dumper.h @@ -46,15 +46,15 @@ class CacheDumpWriter { // Writes data to a cache. class CacheDumper : public CacheDumpWriter { public: - explicit CacheDumper(disk_cache::Backend* cache) : cache_(cache) {} + explicit CacheDumper(disk_cache::Backend* cache); virtual int CreateEntry(const std::string& key, disk_cache::Entry** entry, - const net::CompletionCallback& callback); + const net::CompletionCallback& callback) OVERRIDE; virtual int WriteEntry(disk_cache::Entry* entry, int stream, int offset, net::IOBuffer* buf, int buf_len, - const net::CompletionCallback& callback); + const net::CompletionCallback& callback) OVERRIDE; virtual void CloseEntry(disk_cache::Entry* entry, base::Time last_used, - base::Time last_modified); + base::Time last_modified) OVERRIDE; private: disk_cache::Backend* cache_; @@ -63,16 +63,15 @@ class CacheDumper : public CacheDumpWriter { // Writes data to a disk. class DiskDumper : public CacheDumpWriter { public: - explicit DiskDumper(const FilePath& path) : path_(path), entry_(NULL) { - file_util::CreateDirectory(path); - } + explicit DiskDumper(const FilePath& path); + virtual int CreateEntry(const std::string& key, disk_cache::Entry** entry, - const net::CompletionCallback& callback); + const net::CompletionCallback& callback) OVERRIDE; virtual int WriteEntry(disk_cache::Entry* entry, int stream, int offset, net::IOBuffer* buf, int buf_len, - const net::CompletionCallback& callback); + const net::CompletionCallback& callback) OVERRIDE; virtual void CloseEntry(disk_cache::Entry* entry, base::Time last_used, - base::Time last_modified); + base::Time last_modified) OVERRIDE; private: FilePath path_; diff --git a/net/tools/dump_cache/dump_cache.cc b/net/tools/dump_cache/dump_cache.cc index 1b65118..adff056 100644 --- a/net/tools/dump_cache/dump_cache.cc +++ b/net/tools/dump_cache/dump_cache.cc @@ -13,8 +13,14 @@ #include "base/process_util.h" #include "base/string_util.h" #include "base/stringprintf.h" -#include "base/win/scoped_handle.h" #include "net/disk_cache/disk_format.h" +#include "net/tools/dump_cache/dump_files.h" +#include "net/tools/dump_cache/simple_cache_dumper.h" + +#if defined(OS_WIN) +#include "base/win/scoped_handle.h" +#include "net/tools/dump_cache/upgrade_win.h" +#endif enum Errors { GENERIC = -1, @@ -25,13 +31,6 @@ enum Errors { TOOL_NOT_FOUND, }; -int GetMajorVersion(const FilePath& input_path); -int DumpContents(const FilePath& input_path); -int DumpHeaders(const FilePath& input_path); -int RunSlave(const FilePath& input_path, const std::wstring& pipe_number); -int CopyCache(const FilePath& output_path, HANDLE pipe, bool copy_to_text); -HANDLE CreateServer(std::wstring* pipe_number); - const char kUpgradeHelp[] = "\nIn order to use the upgrade function, a version of this tool that\n" "understands the file format of the files to upgrade is needed. For\n" @@ -70,31 +69,6 @@ int Help() { return INVALID_ARGUMENT; } -// Starts a new process, to generate the files. -int LaunchSlave(CommandLine command_line, - const std::wstring& pipe_number, - int version) { - bool do_upgrade = command_line.HasSwitch(kUpgrade); - bool do_convert_to_text = command_line.HasSwitch(kDumpToFiles); - - if (do_upgrade) { - FilePath program(base::StringPrintf(L"%ls%d", L"dump_cache", version)); - command_line.SetProgram(program); - } - - if (do_upgrade || do_convert_to_text) - command_line.AppendSwitch(kSlave); - - command_line.AppendSwitchNative(kPipe, pipe_number); - if (!base::LaunchProcess(command_line, base::LaunchOptions(), NULL)) { - printf("Unable to launch the needed version of this tool: %ls\n", - command_line.GetProgram().value().c_str()); - printf(kUpgradeHelp); - return TOOL_NOT_FOUND; - } - return ALL_GOOD; -} - // ----------------------------------------------------------------------- int main(int argc, const char* argv[]) { @@ -109,16 +83,24 @@ int main(int argc, const char* argv[]) { return Help(); bool upgrade = false; - bool slave_required = false; bool copy_to_text = false; FilePath output_path = command_line.GetSwitchValuePath(kOutputPath); if (command_line.HasSwitch(kUpgrade)) upgrade = true; + if (command_line.HasSwitch(kDumpToFiles)) copy_to_text = true; - if (upgrade || copy_to_text) { + if (copy_to_text) { + net::SimpleCacheDumper dumper(input_path, output_path); + dumper.Run(); + return 0; + } + +#if defined(OS_WIN) + bool slave_required = false; + if (upgrade) { if (output_path.empty()) return Help(); slave_required = true; @@ -153,8 +135,10 @@ int main(int argc, const char* argv[]) { return ret; } - if (upgrade || copy_to_text) - return CopyCache(output_path, server, copy_to_text); + // TODO(rch): Remove the logic from CopyCache that is redundant with + // SimpleCacheDumper. + if (upgrade) + return CopyCache(output_path, server, false); if (slave_required) { // Wait until the slave starts dumping data before we quit. Lazy "fix" for a @@ -167,5 +151,6 @@ int main(int argc, const char* argv[]) { return DumpContents(input_path); if (command_line.HasSwitch(kDumpHeaders)) return DumpHeaders(input_path); +#endif return Help(); } diff --git a/net/tools/dump_cache/dump_files.cc b/net/tools/dump_cache/dump_files.cc index 26a11c3..3ce890a 100644 --- a/net/tools/dump_cache/dump_files.cc +++ b/net/tools/dump_cache/dump_files.cc @@ -6,6 +6,8 @@ // to the actual files (they still may change if an error is detected on the // files). +#include "net/tools/dump_cache/dump_files.h" + #include #include @@ -22,20 +24,20 @@ namespace { -const wchar_t kIndexName[] = L"index"; +const FilePath::CharType kIndexName[] = FILE_PATH_LITERAL("index"); // Reads the |header_size| bytes from the beginning of file |name|. bool ReadHeader(const FilePath& name, char* header, int header_size) { net::FileStream file(NULL); file.OpenSync(name, base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ); if (!file.IsOpen()) { - printf("Unable to open file %ls\n", name.value().c_str()); + printf("Unable to open file %s\n", name.MaybeAsASCII().c_str()); return false; } int read = file.ReadSync(header, header_size); if (read != header_size) { - printf("Unable to read file %ls\n", name.value().c_str()); + printf("Unable to read file %s\n", name.MaybeAsASCII().c_str()); return false; } return true; @@ -82,7 +84,7 @@ void DumpBlockHeader(const FilePath& name) { if (!ReadHeader(name, reinterpret_cast(&header), sizeof(header))) return; - printf("Block file: %ls\n", name.BaseName().value().c_str()); + printf("Block file: %s\n", name.BaseName().MaybeAsASCII().c_str()); printf("magic: %x\n", header.magic); printf("version: %d.%d\n", header.version >> 16, header.version & 0xffff); printf("file id: %d\n", header.this_file); @@ -268,11 +270,19 @@ int GetMajorVersion(const FilePath& input_path) { if (!version) return 0; - FilePath data_name(input_path.Append(L"data_0")); + FilePath data_name(input_path.Append(FILE_PATH_LITERAL("data_0"))); + if (version != GetMajorVersionFromFile(data_name)) + return 0; + + data_name = input_path.Append(FILE_PATH_LITERAL("data_1")); + if (version != GetMajorVersionFromFile(data_name)) + return 0; + + data_name = input_path.Append(FILE_PATH_LITERAL("data_2")); if (version != GetMajorVersionFromFile(data_name)) return 0; - data_name = input_path.Append(L"data_1"); + data_name = input_path.Append(FILE_PATH_LITERAL("data_3")); if (version != GetMajorVersionFromFile(data_name)) return 0; @@ -285,7 +295,8 @@ int DumpHeaders(const FilePath& input_path) { DumpIndexHeader(index_name); file_util::FileEnumerator iter(input_path, false, - file_util::FileEnumerator::FILES, L"data_*"); + file_util::FileEnumerator::FILES, + FILE_PATH_LITERAL("data_*")); for (FilePath file = iter.Next(); !file.empty(); file = iter.Next()) DumpBlockHeader(file); return 0; diff --git a/net/tools/dump_cache/dump_files.h b/net/tools/dump_cache/dump_files.h new file mode 100644 index 0000000..abc8b5b --- /dev/null +++ b/net/tools/dump_cache/dump_files.h @@ -0,0 +1,23 @@ +// Copyright (c) 2012 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_TOOLS_DUMP_CACHE_DUMP_FILES_H_ +#define NET_TOOLS_DUMP_CACHE_DUMP_FILE_H_ + +// Performs basic inspection of the disk cache files with minimal disruption +// to the actual files (they still may change if an error is detected on the +// files). + +#include "base/file_path.h" + +// Returns the major version of the specified cache. +int GetMajorVersion(const FilePath& input_path); + +// Dumps all entries from the cache. +int DumpContents(const FilePath& input_path); + +// Dumps the headers of all files. +int DumpHeaders(const FilePath& input_path); + +#endif // NET_TOOLS_DUMP_CACHE_DUMP_FILES_H_ diff --git a/net/tools/dump_cache/simple_cache_dumper.cc b/net/tools/dump_cache/simple_cache_dumper.cc new file mode 100644 index 0000000..73c3cd0 --- /dev/null +++ b/net/tools/dump_cache/simple_cache_dumper.cc @@ -0,0 +1,268 @@ +// Copyright (c) 2012 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/tools/dump_cache/simple_cache_dumper.h" + +#include "base/at_exit.h" +#include "base/command_line.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "base/message_loop_proxy.h" +#include "base/threading/thread.h" +#include "net/base/io_buffer.h" +#include "net/base/net_errors.h" +#include "net/disk_cache/disk_cache.h" +#include "net/tools/dump_cache/cache_dumper.h" + +namespace net { + +SimpleCacheDumper::SimpleCacheDumper(FilePath input_path, FilePath output_path) + : state_(STATE_NONE), + input_path_(input_path), + output_path_(output_path), + cache_(NULL), + writer_(new DiskDumper(output_path)), + cache_thread_(new base::Thread("CacheThead")), + iter_(NULL), + src_entry_(NULL), + dst_entry_(NULL), + io_callback_(base::Bind(&SimpleCacheDumper::OnIOComplete, + base::Unretained(this))), + rv_(0) { +} + +SimpleCacheDumper::~SimpleCacheDumper() { + delete(cache_); +} + +int SimpleCacheDumper::Run() { + MessageLoopForIO main_message_loop; + + LOG(INFO) << "Reading cache from: " << input_path_.value(); + LOG(INFO) << "Writing cache to: " << output_path_.value(); + + if (!cache_thread_->StartWithOptions( + base::Thread::Options(MessageLoop::TYPE_IO, 0))) { + LOG(ERROR) << "Unable to start thread"; + return ERR_UNEXPECTED; + } + state_ = STATE_CREATE_CACHE; + int rv = DoLoop(OK); + if (rv == ERR_IO_PENDING) { + main_message_loop.Run(); + return rv_; + } + return rv; +} + +int SimpleCacheDumper::DoLoop(int rv) { + do { + State state = state_; + state_ = STATE_NONE; + switch (state) { + case STATE_CREATE_CACHE: + CHECK_EQ(OK, rv); + rv = DoCreateCache(); + break; + case STATE_CREATE_CACHE_COMPLETE: + rv = DoCreateCacheComplete(rv); + break; + case STATE_OPEN_ENTRY: + CHECK_EQ(OK, rv); + rv = DoOpenEntry(); + break; + case STATE_OPEN_ENTRY_COMPLETE: + rv = DoOpenEntryComplete(rv); + break; + case STATE_CREATE_ENTRY: + CHECK_EQ(OK, rv); + rv = DoCreateEntry(); + break; + case STATE_CREATE_ENTRY_COMPLETE: + rv = DoCreateEntryComplete(rv); + break; + case STATE_READ_HEADERS: + CHECK_EQ(OK, rv); + rv = DoReadHeaders(); + break; + case STATE_READ_HEADERS_COMPLETE: + rv = DoReadHeadersComplete(rv); + break; + case STATE_WRITE_HEADERS: + CHECK_EQ(OK, rv); + rv = DoWriteHeaders(); + break; + case STATE_WRITE_HEADERS_COMPLETE: + rv = DoWriteHeadersComplete(rv); + break; + case STATE_READ_BODY: + CHECK_EQ(OK, rv); + rv = DoReadBody(); + break; + case STATE_READ_BODY_COMPLETE: + rv = DoReadBodyComplete(rv); + break; + case STATE_WRITE_BODY: + CHECK_EQ(OK, rv); + rv = DoWriteBody(); + break; + case STATE_WRITE_BODY_COMPLETE: + rv = DoWriteBodyComplete(rv); + break; + default: + NOTREACHED() << "state_: " << state_; + break; + } + } while (state_ != STATE_NONE && rv != ERR_IO_PENDING); + return rv; +} + +int SimpleCacheDumper::DoCreateCache() { + DCHECK(!cache_); + state_ = STATE_CREATE_CACHE_COMPLETE; + return disk_cache::CreateCacheBackend( + DISK_CACHE, input_path_, 0, false, + cache_thread_->message_loop_proxy(), + NULL, &cache_, io_callback_); +} + +int SimpleCacheDumper::DoCreateCacheComplete(int rv) { + if (rv < 0) + return rv; + + reinterpret_cast(cache_)->SetUpgradeMode(); + reinterpret_cast(cache_)->SetFlags( + disk_cache::kNoRandom); + + state_ = STATE_OPEN_ENTRY; + return OK; +} + +int SimpleCacheDumper::DoOpenEntry() { + DCHECK(!dst_entry_); + DCHECK(!src_entry_); + state_ = STATE_OPEN_ENTRY_COMPLETE; + return cache_->OpenNextEntry(&iter_, &src_entry_, io_callback_); +} + +int SimpleCacheDumper::DoOpenEntryComplete(int rv) { + // ERR_FAILED indicates iteration finished. + if (rv == ERR_FAILED) { + cache_->EndEnumeration(&iter_); + return OK; + } + + if (rv < 0) + return rv; + + state_ = STATE_CREATE_ENTRY; + return OK; +} + +int SimpleCacheDumper::DoCreateEntry() { + DCHECK(!dst_entry_); + state_ = STATE_CREATE_ENTRY_COMPLETE; + + return writer_->CreateEntry(src_entry_->GetKey(), &dst_entry_, + io_callback_); +} + +int SimpleCacheDumper::DoCreateEntryComplete(int rv) { + if (rv < 0) + return rv; + + state_ = STATE_READ_HEADERS; + return OK; +} + +int SimpleCacheDumper::DoReadHeaders() { + state_ = STATE_READ_HEADERS_COMPLETE; + int32 size = src_entry_->GetDataSize(0); + buf_ = new IOBufferWithSize(size); + return src_entry_->ReadData(0, 0, buf_, size, io_callback_); +} + +int SimpleCacheDumper::DoReadHeadersComplete(int rv) { + if (rv < 0) + return rv; + + state_ = STATE_WRITE_HEADERS; + return OK; +} + +int SimpleCacheDumper::DoWriteHeaders() { + int rv = writer_->WriteEntry(dst_entry_, 0, 0, buf_, buf_->size(), + io_callback_); + if (rv == 0) + return ERR_FAILED; + + state_ = STATE_WRITE_HEADERS_COMPLETE; + return OK; +} + +int SimpleCacheDumper::DoWriteHeadersComplete(int rv) { + if (rv < 0) + return rv; + + state_ = STATE_READ_BODY; + return OK; +} + +int SimpleCacheDumper::DoReadBody() { + state_ = STATE_READ_BODY_COMPLETE; + int32 size = src_entry_->GetDataSize(1); + // If the body is empty, we can neither read nor write it, so + // just move to the next. + if (size <= 0) { + state_ = STATE_WRITE_BODY_COMPLETE; + return OK; + } + buf_ = new IOBufferWithSize(size); + return src_entry_->ReadData(1, 0, buf_, size, io_callback_); +} + +int SimpleCacheDumper::DoReadBodyComplete(int rv) { + if (rv < 0) + return rv; + + state_ = STATE_WRITE_BODY; + return OK; +} + +int SimpleCacheDumper::DoWriteBody() { + int rv = writer_->WriteEntry(dst_entry_, 1, 0, buf_, buf_->size(), + io_callback_); + if (rv == 0) + return ERR_FAILED; + + state_ = STATE_WRITE_BODY_COMPLETE; + return OK; +} + +int SimpleCacheDumper::DoWriteBodyComplete(int rv) { + if (rv < 0) + return rv; + + src_entry_->Close(); + writer_->CloseEntry(dst_entry_, base::Time::Now(), base::Time::Now()); + src_entry_ = NULL; + dst_entry_ = NULL; + + state_ = STATE_OPEN_ENTRY; + return OK; +} + +void SimpleCacheDumper::OnIOComplete(int rv) { + rv = DoLoop(rv); + + if (rv != ERR_IO_PENDING) { + rv_ = rv; + delete cache_; + cache_ = NULL; + MessageLoop::current()->Quit(); + } +} + +} // namespace net diff --git a/net/tools/dump_cache/simple_cache_dumper.h b/net/tools/dump_cache/simple_cache_dumper.h new file mode 100644 index 0000000..4a76529 --- /dev/null +++ b/net/tools/dump_cache/simple_cache_dumper.h @@ -0,0 +1,94 @@ +// Copyright (c) 2012 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_TOOLS_DUMP_CACHE_SIMPLE_CACHE_DUMPER_H_ +#define NET_TOOLS_DUMP_CACHE_SIMPLE_CACHE_DUMPER_H_ + +#include "base/file_path.h" +#include "base/memory/scoped_ptr.h" +#include "base/threading/thread.h" +#include "net/base/completion_callback.h" + +class DiskDumper; + +namespace disk_cache { +class Backend; +class Entry; +} // namespace disk_cache + +namespace net { + +class IOBufferWithSize; + +// A class for dumping the contents of a disk cache to a series of text +// files. The files will contain the response headers, followed by the +// response body, as if the HTTP response were written directly to disk. +class SimpleCacheDumper { + public: + SimpleCacheDumper(FilePath input_path, FilePath output_path); + ~SimpleCacheDumper(); + + // Dumps the cache to disk. Returns OK if the operation was successful, + // and a net error code otherwise. + int Run(); + + private: + enum State { + STATE_NONE, + STATE_CREATE_CACHE, + STATE_CREATE_CACHE_COMPLETE, + STATE_OPEN_ENTRY, + STATE_OPEN_ENTRY_COMPLETE, + STATE_CREATE_ENTRY, + STATE_CREATE_ENTRY_COMPLETE, + STATE_READ_HEADERS, + STATE_READ_HEADERS_COMPLETE, + STATE_WRITE_HEADERS, + STATE_WRITE_HEADERS_COMPLETE, + STATE_READ_BODY, + STATE_READ_BODY_COMPLETE, + STATE_WRITE_BODY, + STATE_WRITE_BODY_COMPLETE, + STATE_DONE, + }; + + int DoLoop(int rv); + + int DoCreateCache(); + int DoCreateCacheComplete(int rv); + int DoOpenEntry(); + int DoOpenEntryComplete(int rv); + int DoCreateEntry(); + int DoCreateEntryComplete(int rv); + int DoReadHeaders(); + int DoReadHeadersComplete(int rv); + int DoWriteHeaders(); + int DoWriteHeadersComplete(int rv); + int DoReadBody(); + int DoReadBodyComplete(int rv); + int DoWriteBody(); + int DoWriteBodyComplete(int rv); + int DoDone(); + + void OnIOComplete(int rv); + + State state_; + FilePath input_path_; + FilePath output_path_; + disk_cache::Backend* cache_; + scoped_ptr writer_; + base::Thread* cache_thread_; + void* iter_; + disk_cache::Entry* src_entry_; + disk_cache::Entry* dst_entry_; + CompletionCallback io_callback_; + scoped_refptr buf_; + int rv_; + + DISALLOW_COPY_AND_ASSIGN(SimpleCacheDumper); +}; + +} // namespace net + +#endif // NET_TOOLS_DUMP_CACHE_SIMPLE_CACHE_DUMPER_H_ diff --git a/net/tools/dump_cache/upgrade.cc b/net/tools/dump_cache/upgrade.cc deleted file mode 100644 index 34bc174..0000000 --- a/net/tools/dump_cache/upgrade.cc +++ /dev/null @@ -1,922 +0,0 @@ -// Copyright (c) 2012 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 "base/bind.h" -#include "base/bind_helpers.h" -#include "base/file_path.h" -#include "base/logging.h" -#include "base/memory/scoped_ptr.h" -#include "base/message_loop.h" -#include "base/string_number_conversions.h" -#include "base/string_util.h" -#include "base/threading/thread.h" -#include "base/win/scoped_handle.h" -#include "googleurl/src/gurl.h" -#include "net/base/io_buffer.h" -#include "net/base/net_errors.h" -#include "net/base/test_completion_callback.h" -#include "net/disk_cache/backend_impl.h" -#include "net/disk_cache/entry_impl.h" -#include "net/http/http_cache.h" -#include "net/http/http_response_headers.h" -#include "net/http/http_response_info.h" -#include "net/tools/dump_cache/cache_dumper.h" - -namespace { - -const wchar_t kPipePrefix[] = L"\\\\.\\pipe\\dump_cache_"; -const int kChannelSize = 64 * 1024; -const int kNumStreams = 4; - -// Simple macro to print out formatted debug messages. It is similar to a DLOG -// except that it doesn't include a header. -#ifdef NDEBUG -#define DEBUGMSG(...) {} -#else -#define DEBUGMSG(...) { printf(__VA_ARGS__); } -#endif - -HANDLE OpenServer(const std::wstring& pipe_number) { - std::wstring pipe_name(kPipePrefix); - pipe_name.append(pipe_number); - return CreateFile(pipe_name.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, - OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); -} - -// This is the basic message to use between the two processes. It is intended -// to transmit a single action (like "get the key name for entry xx"), with up -// to 5 32-bit arguments and 4 64-bit arguments. After this structure, the rest -// of the message has |buffer_bytes| of length with the actual data. -struct Message { - int32 command; - int32 result; - int32 buffer_bytes; - int32 arg1; - int32 arg2; - int32 arg3; - int32 arg4; - int32 arg5; - int64 long_arg1; - int64 long_arg2; - int64 long_arg3; - int64 long_arg4; - Message() { - memset(this, 0, sizeof(*this)); - } - Message& operator= (const Message& other) { - memcpy(this, &other, sizeof(*this)); - return *this; - } -}; - -const int kBufferSize = kChannelSize - sizeof(Message); -struct IoBuffer { - Message msg; - char buffer[kBufferSize]; -}; -COMPILE_ASSERT(sizeof(IoBuffer) == kChannelSize, invalid_io_buffer); - - -// The list of commands. -// Currently, there is support for working ONLY with one entry at a time. -enum { - // Get the entry from list |arg1| that follows |long_arg1|. - // The result is placed on |long_arg1| (closes the previous one). - GET_NEXT_ENTRY = 1, - // Get the entry from list |arg1| that precedes |long_arg1|. - // The result is placed on |long_arg1| (closes the previous one). - GET_PREV_ENTRY, - // Closes the entry |long_arg1|. - CLOSE_ENTRY, - // Get the key of the entry |long_arg1|. - GET_KEY, - // Get last used (long_arg2) and last modified (long_arg3) times for the - // entry at |long_arg1|. - GET_USE_TIMES, - // Returns on |arg2| the data size in bytes if the stream |arg1| of entry at - // |long_arg1|. - GET_DATA_SIZE, - // Returns |arg2| bytes of the stream |arg1| for the entry at |long_arg1|, - // starting at offset |arg3|. - READ_DATA, - // End processing requests. - QUIT -}; - -// The list of return codes. -enum { - RESULT_OK = 0, - RESULT_UNKNOWN_COMMAND, - RESULT_INVALID_PARAMETER, - RESULT_NAME_OVERFLOW, - RESULT_PENDING // This error code is NOT expected by the master process. -}; - -// ----------------------------------------------------------------------- - -class BaseSM : public MessageLoopForIO::IOHandler { - public: - explicit BaseSM(HANDLE channel); - virtual ~BaseSM(); - - protected: - bool SendMsg(const Message& msg); - bool ReceiveMsg(); - bool ConnectChannel(); - bool IsPending(); - - MessageLoopForIO::IOContext in_context_; - MessageLoopForIO::IOContext out_context_; - disk_cache::EntryImpl* entry_; - HANDLE channel_; - int state_; - int pending_count_; - scoped_array in_buffer_; - scoped_array out_buffer_; - IoBuffer* input_; - IoBuffer* output_; - base::Thread cache_thread_; - - DISALLOW_COPY_AND_ASSIGN(BaseSM); -}; - -BaseSM::BaseSM(HANDLE channel) - : entry_(NULL), channel_(channel), state_(0), pending_count_(0), - cache_thread_("cache") { - in_buffer_.reset(new char[kChannelSize]); - out_buffer_.reset(new char[kChannelSize]); - input_ = reinterpret_cast(in_buffer_.get()); - output_ = reinterpret_cast(out_buffer_.get()); - - memset(&in_context_, 0, sizeof(in_context_)); - memset(&out_context_, 0, sizeof(out_context_)); - in_context_.handler = this; - out_context_.handler = this; - MessageLoopForIO::current()->RegisterIOHandler(channel_, this); - CHECK(cache_thread_.StartWithOptions( - base::Thread::Options(MessageLoop::TYPE_IO, 0))); -} - -BaseSM::~BaseSM() { - if (entry_) - entry_->Close(); -} - -bool BaseSM::SendMsg(const Message& msg) { - // Only one command will be in-flight at a time. Let's start the Read IO here - // when we know that it will be pending. - if (!ReceiveMsg()) - return false; - - output_->msg = msg; - DWORD written; - if (!WriteFile(channel_, output_, sizeof(msg) + msg.buffer_bytes, &written, - &out_context_.overlapped)) { - if (ERROR_IO_PENDING != GetLastError()) - return false; - } - pending_count_++; - return true; -} - -bool BaseSM::ReceiveMsg() { - DWORD read; - if (!ReadFile(channel_, input_, kChannelSize, &read, - &in_context_.overlapped)) { - if (ERROR_IO_PENDING != GetLastError()) - return false; - } - pending_count_++; - return true; -} - -bool BaseSM::ConnectChannel() { - if (!ConnectNamedPipe(channel_, &in_context_.overlapped)) { - DWORD error = GetLastError(); - if (ERROR_PIPE_CONNECTED == error) - return true; - // By returning true in case of a generic error, we allow the operation to - // fail while sending the first message. - if (ERROR_IO_PENDING != error) - return true; - } - pending_count_++; - return false; -} - -bool BaseSM::IsPending() { - return pending_count_ != 0; -} - -// ----------------------------------------------------------------------- - -class MasterSM : public BaseSM { - public: - MasterSM(const FilePath& path, HANDLE channel, bool dump_to_disk) - : BaseSM(channel), - path_(path), - dump_to_disk_(dump_to_disk) { - } - virtual ~MasterSM() { - delete writer_; - } - - bool DoInit(); - virtual void OnIOCompleted(MessageLoopForIO::IOContext* context, - DWORD bytes_transfered, DWORD error); - - private: - enum { - MASTER_INITIAL = 0, - MASTER_CONNECT, - MASTER_GET_ENTRY, - MASTER_GET_NEXT_ENTRY, - MASTER_GET_KEY, - MASTER_GET_USE_TIMES, - MASTER_GET_DATA_SIZE, - MASTER_READ_DATA, - MASTER_END - }; - - void SendGetPrevEntry(); - void DoGetEntry(); - void DoGetKey(int bytes_read); - void DoCreateEntryComplete(int result); - void DoGetUseTimes(); - void SendGetDataSize(); - void DoGetDataSize(); - void CloseEntry(); - void SendReadData(); - void DoReadData(int bytes_read); - void DoReadDataComplete(int ret); - void SendQuit(); - void DoEnd(); - void Fail(); - - base::Time last_used_; - base::Time last_modified_; - int64 remote_entry_; - int stream_; - int bytes_remaining_; - int offset_; - int copied_entries_; - int read_size_; - scoped_ptr cache_; - CacheDumpWriter* writer_; - const FilePath path_; - bool dump_to_disk_; -}; - -void MasterSM::OnIOCompleted(MessageLoopForIO::IOContext* context, - DWORD bytes_transfered, DWORD error) { - pending_count_--; - if (context == &out_context_) { - if (!error) - return; - return Fail(); - } - - int bytes_read = static_cast(bytes_transfered); - if (bytes_read < sizeof(Message) && state_ != MASTER_END && - state_ != MASTER_CONNECT) { - printf("Communication breakdown\n"); - return Fail(); - } - - switch (state_) { - case MASTER_CONNECT: - SendGetPrevEntry(); - break; - case MASTER_GET_ENTRY: - DoGetEntry(); - break; - case MASTER_GET_KEY: - DoGetKey(bytes_read); - break; - case MASTER_GET_USE_TIMES: - DoGetUseTimes(); - break; - case MASTER_GET_DATA_SIZE: - DoGetDataSize(); - break; - case MASTER_READ_DATA: - DoReadData(bytes_read); - break; - case MASTER_END: - if (!IsPending()) - DoEnd(); - break; - default: - NOTREACHED(); - break; - } -} - -bool MasterSM::DoInit() { - DEBUGMSG("Master DoInit\n"); - DCHECK(state_ == MASTER_INITIAL); - - if (dump_to_disk_) { - writer_ = new DiskDumper(path_); - } else { - disk_cache::Backend* cache; - net::TestCompletionCallback cb; - int rv = disk_cache::CreateCacheBackend(net::DISK_CACHE, path_, 0, false, - cache_thread_.message_loop_proxy(), - NULL, &cache, cb.callback()); - if (cb.GetResult(rv) != net::OK) { - printf("Unable to initialize new files\n"); - return false; - } - cache_.reset(cache); - writer_ = new CacheDumper(cache_.get()); - } - if (!writer_) - return false; - - copied_entries_ = 0; - remote_entry_ = 0; - - if (ConnectChannel()) { - SendGetPrevEntry(); - // If we don't have pending operations we couldn't connect. - return IsPending(); - } - - state_ = MASTER_CONNECT; - return true; -} - -void MasterSM::SendGetPrevEntry() { - DEBUGMSG("Master SendGetPrevEntry\n"); - state_ = MASTER_GET_ENTRY; - Message msg; - msg.command = GET_PREV_ENTRY; - msg.long_arg1 = remote_entry_; - SendMsg(msg); -} - -void MasterSM::DoGetEntry() { - DEBUGMSG("Master DoGetEntry\n"); - DCHECK(state_ == MASTER_GET_ENTRY); - DCHECK(input_->msg.command == GET_PREV_ENTRY); - if (input_->msg.result != RESULT_OK) - return Fail(); - - if (!input_->msg.long_arg1) { - printf("Done: %d entries copied over.\n", copied_entries_); - return SendQuit(); - } - remote_entry_ = input_->msg.long_arg1; - state_ = MASTER_GET_KEY; - Message msg; - msg.command = GET_KEY; - msg.long_arg1 = remote_entry_; - SendMsg(msg); -} - -void MasterSM::DoGetKey(int bytes_read) { - DEBUGMSG("Master DoGetKey\n"); - DCHECK(state_ == MASTER_GET_KEY); - DCHECK(input_->msg.command == GET_KEY); - if (input_->msg.result == RESULT_NAME_OVERFLOW) { - // The key is too long. Just move on. - printf("Skipping entry (name too long)\n"); - return SendGetPrevEntry(); - } - - if (input_->msg.result != RESULT_OK) - return Fail(); - - std::string key(input_->buffer); - DCHECK(key.size() == static_cast(input_->msg.buffer_bytes - 1)); - - int rv = writer_->CreateEntry( - key, reinterpret_cast(&entry_), - base::Bind(&MasterSM::DoCreateEntryComplete, base::Unretained(this))); - - if (rv != net::ERR_IO_PENDING) - DoCreateEntryComplete(rv); -} - -void MasterSM::DoCreateEntryComplete(int result) { - std::string key(input_->buffer); - if (result != net::OK) { - printf("Skipping entry \"%s\": %d\n", key.c_str(), GetLastError()); - return SendGetPrevEntry(); - } - - if (key.size() >= 64) { - key[60] = '.'; - key[61] = '.'; - key[62] = '.'; - key[63] = '\0'; - } - DEBUGMSG("Entry \"%s\" created\n", key.c_str()); - state_ = MASTER_GET_USE_TIMES; - Message msg; - msg.command = GET_USE_TIMES; - msg.long_arg1 = remote_entry_; - SendMsg(msg); -} - -void MasterSM::DoGetUseTimes() { - DEBUGMSG("Master DoGetUseTimes\n"); - DCHECK(state_ == MASTER_GET_USE_TIMES); - DCHECK(input_->msg.command == GET_USE_TIMES); - if (input_->msg.result != RESULT_OK) - return Fail(); - - last_used_ = base::Time::FromInternalValue(input_->msg.long_arg2); - last_modified_ = base::Time::FromInternalValue(input_->msg.long_arg3); - stream_ = 0; - SendGetDataSize(); -} - -void MasterSM::SendGetDataSize() { - DEBUGMSG("Master SendGetDataSize (%d)\n", stream_); - state_ = MASTER_GET_DATA_SIZE; - Message msg; - msg.command = GET_DATA_SIZE; - msg.arg1 = stream_; - msg.long_arg1 = remote_entry_; - SendMsg(msg); -} - -void MasterSM::DoGetDataSize() { - DEBUGMSG("Master DoGetDataSize: %d\n", input_->msg.arg2); - DCHECK(state_ == MASTER_GET_DATA_SIZE); - DCHECK(input_->msg.command == GET_DATA_SIZE); - if (input_->msg.result == RESULT_INVALID_PARAMETER) - // No more streams, move to the next entry. - return CloseEntry(); - - if (input_->msg.result != RESULT_OK) - return Fail(); - - bytes_remaining_ = input_->msg.arg2; - offset_ = 0; - SendReadData(); -} - -void MasterSM::CloseEntry() { - DEBUGMSG("Master CloseEntry\n"); - printf("%c\r", copied_entries_ % 2 ? 'x' : '+'); - writer_->CloseEntry(entry_, last_used_, last_modified_); - entry_ = NULL; - copied_entries_++; - SendGetPrevEntry(); -} - -void MasterSM::SendReadData() { - int read_size = std::min(bytes_remaining_, kBufferSize); - DEBUGMSG("Master SendReadData (%d): %d bytes at %d\n", stream_, read_size, - offset_); - if (bytes_remaining_ <= 0) { - stream_++; - if (stream_ >= kNumStreams) - return CloseEntry(); - return SendGetDataSize(); - } - - state_ = MASTER_READ_DATA; - Message msg; - msg.command = READ_DATA; - msg.arg1 = stream_; - msg.arg2 = read_size; - msg.arg3 = offset_; - msg.long_arg1 = remote_entry_; - SendMsg(msg); -} - -void MasterSM::DoReadData(int bytes_read) { - DEBUGMSG("Master DoReadData: %d bytes\n", input_->msg.buffer_bytes); - DCHECK(state_ == MASTER_READ_DATA); - DCHECK(input_->msg.command == READ_DATA); - if (input_->msg.result != RESULT_OK) - return Fail(); - - int read_size = input_->msg.buffer_bytes; - if (!read_size) { - printf("Read failed, entry \"%s\" truncated!\n", entry_->GetKey().c_str()); - bytes_remaining_ = 0; - return SendReadData(); - } - - scoped_refptr buf = - new net::WrappedIOBuffer(input_->buffer); - int rv = writer_->WriteEntry( - entry_, stream_, offset_, buf, read_size, - base::Bind(&MasterSM::DoReadDataComplete, base::Unretained(this))); - if (rv == net::ERR_IO_PENDING) { - // We'll continue in DoReadDataComplete. - read_size_ = read_size; - return; - } - - if (rv <= 0) - return Fail(); - - offset_ += read_size; - bytes_remaining_ -= read_size; - // Read some more. - SendReadData(); -} - -void MasterSM::DoReadDataComplete(int ret) { - if (ret != read_size_) - return Fail(); - - offset_ += ret; - bytes_remaining_ -= ret; - // Read some more. - SendReadData(); -} - -void MasterSM::SendQuit() { - DEBUGMSG("Master SendQuit\n"); - state_ = MASTER_END; - Message msg; - msg.command = QUIT; - SendMsg(msg); - if (!IsPending()) - DoEnd(); -} - -void MasterSM::DoEnd() { - DEBUGMSG("Master DoEnd\n"); - MessageLoop::current()->PostTask(FROM_HERE, MessageLoop::QuitClosure()); -} - -void MasterSM::Fail() { - DEBUGMSG("Master Fail\n"); - printf("Unexpected failure\n"); - SendQuit(); -} - -// ----------------------------------------------------------------------- - -class SlaveSM : public BaseSM { - public: - SlaveSM(const FilePath& path, HANDLE channel); - virtual ~SlaveSM(); - - bool DoInit(); - virtual void OnIOCompleted(MessageLoopForIO::IOContext* context, - DWORD bytes_transfered, DWORD error); - - private: - enum { - SLAVE_INITIAL = 0, - SLAVE_WAITING, - SLAVE_END - }; - - void DoGetNextEntry(); - void DoGetPrevEntry(); - int32 GetEntryFromList(); - void DoGetEntryComplete(int result); - void DoCloseEntry(); - void DoGetKey(); - void DoGetUseTimes(); - void DoGetDataSize(); - void DoReadData(); - void DoReadDataComplete(int ret); - void DoEnd(); - void Fail(); - - void* iterator_; - Message msg_; // Used for DoReadDataComplete and DoGetEntryComplete. - - scoped_ptr cache_; -}; - -SlaveSM::SlaveSM(const FilePath& path, HANDLE channel) - : BaseSM(channel), iterator_(NULL) { - disk_cache::Backend* cache; - net::TestCompletionCallback cb; - int rv = disk_cache::CreateCacheBackend(net::DISK_CACHE, path, 0, false, - cache_thread_.message_loop_proxy(), - NULL, &cache, cb.callback()); - if (cb.GetResult(rv) != net::OK) { - printf("Unable to open cache files\n"); - return; - } - cache_.reset(reinterpret_cast(cache)); - cache_->SetUpgradeMode(); -} - -SlaveSM::~SlaveSM() { - if (iterator_) - cache_->EndEnumeration(&iterator_); -} - -void SlaveSM::OnIOCompleted(MessageLoopForIO::IOContext* context, - DWORD bytes_transfered, DWORD error) { - pending_count_--; - if (state_ == SLAVE_END) { - if (IsPending()) - return; - return DoEnd(); - } - - if (context == &out_context_) { - if (!error) - return; - return Fail(); - } - - int bytes_read = static_cast(bytes_transfered); - if (bytes_read < sizeof(Message)) { - printf("Communication breakdown\n"); - return Fail(); - } - DCHECK(state_ == SLAVE_WAITING); - - switch (input_->msg.command) { - case GET_NEXT_ENTRY: - DoGetNextEntry(); - break; - case GET_PREV_ENTRY: - DoGetPrevEntry(); - break; - case CLOSE_ENTRY: - DoCloseEntry(); - break; - case GET_KEY: - DoGetKey(); - break; - case GET_USE_TIMES: - DoGetUseTimes(); - break; - case GET_DATA_SIZE: - DoGetDataSize(); - break; - case READ_DATA: - DoReadData(); - break; - case QUIT: - DoEnd(); - break; - default: - NOTREACHED(); - break; - } -} - -bool SlaveSM::DoInit() { - DEBUGMSG("\t\t\tSlave DoInit\n"); - DCHECK(state_ == SLAVE_INITIAL); - state_ = SLAVE_WAITING; - if (!cache_.get()) - return false; - - return ReceiveMsg(); -} - -void SlaveSM::DoGetNextEntry() { - DEBUGMSG("\t\t\tSlave DoGetNextEntry\n"); - Message msg; - msg.command = GET_NEXT_ENTRY; - - if (input_->msg.arg1) { - // We only support one list. - msg.result = RESULT_UNKNOWN_COMMAND; - } else { - msg.result = GetEntryFromList(); - msg.long_arg1 = reinterpret_cast(entry_); - } - SendMsg(msg); -} - -void SlaveSM::DoGetPrevEntry() { - DEBUGMSG("\t\t\tSlave DoGetPrevEntry\n"); - Message msg; - msg.command = GET_PREV_ENTRY; - - if (input_->msg.arg1) { - // We only support one list. - msg.result = RESULT_UNKNOWN_COMMAND; - } else { - msg.result = GetEntryFromList(); - if (msg.result == RESULT_PENDING) { - // We are not done yet. - msg_ = msg; - return; - } - msg.long_arg1 = reinterpret_cast(entry_); - } - SendMsg(msg); -} - -// Move to the next or previous entry on the list. -int32 SlaveSM::GetEntryFromList() { - DEBUGMSG("\t\t\tSlave GetEntryFromList\n"); - if (input_->msg.long_arg1 != reinterpret_cast(entry_)) - return RESULT_INVALID_PARAMETER; - - // We know that the current iteration is valid. - if (entry_) - entry_->Close(); - - int rv; - if (input_->msg.command == GET_NEXT_ENTRY) { - rv = cache_->OpenNextEntry( - &iterator_, reinterpret_cast(&entry_), - base::Bind(&SlaveSM::DoGetEntryComplete, base::Unretained(this))); - } else { - DCHECK(input_->msg.command == GET_PREV_ENTRY); - rv = cache_->OpenPrevEntry(&iterator_, - reinterpret_cast(&entry_), - base::Bind(&SlaveSM::DoGetEntryComplete, - base::Unretained(this))); - } - DCHECK_EQ(net::ERR_IO_PENDING, rv); - return RESULT_PENDING; -} - -void SlaveSM::DoGetEntryComplete(int result) { - DEBUGMSG("\t\t\tSlave DoGetEntryComplete\n"); - if (result != net::OK) { - entry_ = NULL; - DEBUGMSG("\t\t\tSlave end of list\n"); - } - - msg_.result = RESULT_OK; - msg_.long_arg1 = reinterpret_cast(entry_); - SendMsg(msg_); -} - -void SlaveSM::DoCloseEntry() { - DEBUGMSG("\t\t\tSlave DoCloseEntry\n"); - Message msg; - msg.command = GET_KEY; - - if (!entry_ || input_->msg.long_arg1 != reinterpret_cast(entry_)) { - msg.result = RESULT_INVALID_PARAMETER; - } else { - entry_->Close(); - entry_ = NULL; - cache_->EndEnumeration(&iterator_); - msg.result = RESULT_OK; - } - SendMsg(msg); -} - -void SlaveSM::DoGetKey() { - DEBUGMSG("\t\t\tSlave DoGetKey\n"); - Message msg; - msg.command = GET_KEY; - - if (!entry_ || input_->msg.long_arg1 != reinterpret_cast(entry_)) { - msg.result = RESULT_INVALID_PARAMETER; - } else { - std::string key = entry_->GetKey(); - msg.buffer_bytes = std::min(key.size() + 1, - static_cast(kBufferSize)); - memcpy(output_->buffer, key.c_str(), msg.buffer_bytes); - if (msg.buffer_bytes != static_cast(key.size() + 1)) { - // We don't support moving this entry. Just tell the master. - msg.result = RESULT_NAME_OVERFLOW; - } else { - msg.result = RESULT_OK; - } - } - SendMsg(msg); -} - -void SlaveSM::DoGetUseTimes() { - DEBUGMSG("\t\t\tSlave DoGetUseTimes\n"); - Message msg; - msg.command = GET_USE_TIMES; - - if (!entry_ || input_->msg.long_arg1 != reinterpret_cast(entry_)) { - msg.result = RESULT_INVALID_PARAMETER; - } else { - msg.long_arg2 = entry_->GetLastUsed().ToInternalValue(); - msg.long_arg3 = entry_->GetLastModified().ToInternalValue(); - msg.result = RESULT_OK; - } - SendMsg(msg); -} - -void SlaveSM::DoGetDataSize() { - DEBUGMSG("\t\t\tSlave DoGetDataSize\n"); - Message msg; - msg.command = GET_DATA_SIZE; - - int stream = input_->msg.arg1; - if (!entry_ || input_->msg.long_arg1 != reinterpret_cast(entry_) || - stream < 0 || stream >= kNumStreams) { - msg.result = RESULT_INVALID_PARAMETER; - } else { - msg.arg1 = stream; - msg.arg2 = entry_->GetDataSize(stream); - msg.result = RESULT_OK; - } - SendMsg(msg); -} - -void SlaveSM::DoReadData() { - DEBUGMSG("\t\t\tSlave DoReadData\n"); - Message msg; - msg.command = READ_DATA; - - int stream = input_->msg.arg1; - int size = input_->msg.arg2; - if (!entry_ || input_->msg.long_arg1 != reinterpret_cast(entry_) || - stream < 0 || stream > 1 || size > kBufferSize) { - msg.result = RESULT_INVALID_PARAMETER; - } else { - scoped_refptr buf = - new net::WrappedIOBuffer(output_->buffer); - int ret = entry_->ReadData(stream, input_->msg.arg3, buf, size, - base::Bind(&SlaveSM::DoReadDataComplete, - base::Unretained(this))); - if (ret == net::ERR_IO_PENDING) { - // Save the message so we can continue were we left. - msg_ = msg; - return; - } - - msg.buffer_bytes = (ret < 0) ? 0 : ret; - msg.result = RESULT_OK; - } - SendMsg(msg); -} - -void SlaveSM::DoReadDataComplete(int ret) { - DEBUGMSG("\t\t\tSlave DoReadDataComplete\n"); - DCHECK_EQ(READ_DATA, msg_.command); - msg_.buffer_bytes = (ret < 0) ? 0 : ret; - msg_.result = RESULT_OK; - SendMsg(msg_); -} - -void SlaveSM::DoEnd() { - DEBUGMSG("\t\t\tSlave DoEnd\n"); - MessageLoop::current()->PostTask(FROM_HERE, MessageLoop::QuitClosure()); -} - -void SlaveSM::Fail() { - DEBUGMSG("\t\t\tSlave Fail\n"); - printf("Unexpected failure\n"); - state_ = SLAVE_END; - if (IsPending()) { - CancelIo(channel_); - } else { - DoEnd(); - } -} - -} // namespace. - -// ----------------------------------------------------------------------- - -HANDLE CreateServer(std::wstring* pipe_number) { - std::wstring pipe_name(kPipePrefix); - srand(static_cast(base::Time::Now().ToInternalValue())); - *pipe_number = base::IntToString16(rand()); - pipe_name.append(*pipe_number); - - DWORD mode = PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE | - FILE_FLAG_OVERLAPPED; - - return CreateNamedPipe(pipe_name.c_str(), mode, 0, 1, kChannelSize, - kChannelSize, 0, NULL); -} - -// This is the controller process for an upgrade operation. -int CopyCache(const FilePath& output_path, HANDLE pipe, bool copy_to_text) { - MessageLoop loop(MessageLoop::TYPE_IO); - - MasterSM master(output_path, pipe, copy_to_text); - if (!master.DoInit()) { - printf("Unable to talk with the helper\n"); - return -1; - } - - loop.Run(); - return 0; -} - -// This process will only execute commands from the controller. -int RunSlave(const FilePath& input_path, const std::wstring& pipe_number) { - MessageLoop loop(MessageLoop::TYPE_IO); - - base::win::ScopedHandle pipe(OpenServer(pipe_number)); - if (!pipe.IsValid()) { - printf("Unable to open the server pipe\n"); - return -1; - } - - SlaveSM slave(input_path, pipe); - if (!slave.DoInit()) { - printf("Unable to talk with the main process\n"); - return -1; - } - - loop.Run(); - return 0; -} diff --git a/net/tools/dump_cache/upgrade_win.cc b/net/tools/dump_cache/upgrade_win.cc new file mode 100644 index 0000000..df5107c --- /dev/null +++ b/net/tools/dump_cache/upgrade_win.cc @@ -0,0 +1,948 @@ +// Copyright (c) 2012 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/tools/dump_cache/upgrade_win.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "base/string_number_conversions.h" +#include "base/string_util.h" +#include "base/threading/thread.h" +#include "base/win/scoped_handle.h" +#include "googleurl/src/gurl.h" +#include "net/base/io_buffer.h" +#include "net/base/net_errors.h" +#include "net/base/test_completion_callback.h" +#include "net/disk_cache/backend_impl.h" +#include "net/disk_cache/entry_impl.h" +#include "net/http/http_cache.h" +#include "net/http/http_response_headers.h" +#include "net/http/http_response_info.h" +#include "net/tools/dump_cache/cache_dumper.h" + +namespace { + +const wchar_t kPipePrefix[] = L"\\\\.\\pipe\\dump_cache_"; +const int kChannelSize = 64 * 1024; +const int kNumStreams = 4; + +// Simple macro to print out formatted debug messages. It is similar to a DLOG +// except that it doesn't include a header. +#ifdef NDEBUG +#define DEBUGMSG(...) {} +#else +#define DEBUGMSG(...) { printf(__VA_ARGS__); } +#endif + +HANDLE OpenServer(const std::wstring& pipe_number) { + std::wstring pipe_name(kPipePrefix); + pipe_name.append(pipe_number); + return CreateFile(pipe_name.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); +} + +// This is the basic message to use between the two processes. It is intended +// to transmit a single action (like "get the key name for entry xx"), with up +// to 5 32-bit arguments and 4 64-bit arguments. After this structure, the rest +// of the message has |buffer_bytes| of length with the actual data. +struct Message { + int32 command; + int32 result; + int32 buffer_bytes; + int32 arg1; + int32 arg2; + int32 arg3; + int32 arg4; + int32 arg5; + int64 long_arg1; + int64 long_arg2; + int64 long_arg3; + int64 long_arg4; + Message() { + memset(this, 0, sizeof(*this)); + } + Message& operator= (const Message& other) { + memcpy(this, &other, sizeof(*this)); + return *this; + } +}; + +const int kBufferSize = kChannelSize - sizeof(Message); +struct IoBuffer { + Message msg; + char buffer[kBufferSize]; +}; +COMPILE_ASSERT(sizeof(IoBuffer) == kChannelSize, invalid_io_buffer); + + +// The list of commands. +// Currently, there is support for working ONLY with one entry at a time. +enum { + // Get the entry from list |arg1| that follows |long_arg1|. + // The result is placed on |long_arg1| (closes the previous one). + GET_NEXT_ENTRY = 1, + // Get the entry from list |arg1| that precedes |long_arg1|. + // The result is placed on |long_arg1| (closes the previous one). + GET_PREV_ENTRY, + // Closes the entry |long_arg1|. + CLOSE_ENTRY, + // Get the key of the entry |long_arg1|. + GET_KEY, + // Get last used (long_arg2) and last modified (long_arg3) times for the + // entry at |long_arg1|. + GET_USE_TIMES, + // Returns on |arg2| the data size in bytes if the stream |arg1| of entry at + // |long_arg1|. + GET_DATA_SIZE, + // Returns |arg2| bytes of the stream |arg1| for the entry at |long_arg1|, + // starting at offset |arg3|. + READ_DATA, + // End processing requests. + QUIT +}; + +// The list of return codes. +enum { + RESULT_OK = 0, + RESULT_UNKNOWN_COMMAND, + RESULT_INVALID_PARAMETER, + RESULT_NAME_OVERFLOW, + RESULT_PENDING // This error code is NOT expected by the master process. +}; + +// ----------------------------------------------------------------------- + +class BaseSM : public MessageLoopForIO::IOHandler { + public: + explicit BaseSM(HANDLE channel); + virtual ~BaseSM(); + + protected: + bool SendMsg(const Message& msg); + bool ReceiveMsg(); + bool ConnectChannel(); + bool IsPending(); + + MessageLoopForIO::IOContext in_context_; + MessageLoopForIO::IOContext out_context_; + disk_cache::EntryImpl* entry_; + HANDLE channel_; + int state_; + int pending_count_; + scoped_array in_buffer_; + scoped_array out_buffer_; + IoBuffer* input_; + IoBuffer* output_; + base::Thread cache_thread_; + + DISALLOW_COPY_AND_ASSIGN(BaseSM); +}; + +BaseSM::BaseSM(HANDLE channel) + : entry_(NULL), channel_(channel), state_(0), pending_count_(0), + cache_thread_("cache") { + in_buffer_.reset(new char[kChannelSize]); + out_buffer_.reset(new char[kChannelSize]); + input_ = reinterpret_cast(in_buffer_.get()); + output_ = reinterpret_cast(out_buffer_.get()); + + memset(&in_context_, 0, sizeof(in_context_)); + memset(&out_context_, 0, sizeof(out_context_)); + in_context_.handler = this; + out_context_.handler = this; + MessageLoopForIO::current()->RegisterIOHandler(channel_, this); + CHECK(cache_thread_.StartWithOptions( + base::Thread::Options(MessageLoop::TYPE_IO, 0))); +} + +BaseSM::~BaseSM() { + if (entry_) + entry_->Close(); +} + +bool BaseSM::SendMsg(const Message& msg) { + // Only one command will be in-flight at a time. Let's start the Read IO here + // when we know that it will be pending. + if (!ReceiveMsg()) + return false; + + output_->msg = msg; + DWORD written; + if (!WriteFile(channel_, output_, sizeof(msg) + msg.buffer_bytes, &written, + &out_context_.overlapped)) { + if (ERROR_IO_PENDING != GetLastError()) + return false; + } + pending_count_++; + return true; +} + +bool BaseSM::ReceiveMsg() { + DWORD read; + if (!ReadFile(channel_, input_, kChannelSize, &read, + &in_context_.overlapped)) { + if (ERROR_IO_PENDING != GetLastError()) + return false; + } + pending_count_++; + return true; +} + +bool BaseSM::ConnectChannel() { + if (!ConnectNamedPipe(channel_, &in_context_.overlapped)) { + DWORD error = GetLastError(); + if (ERROR_PIPE_CONNECTED == error) + return true; + // By returning true in case of a generic error, we allow the operation to + // fail while sending the first message. + if (ERROR_IO_PENDING != error) + return true; + } + pending_count_++; + return false; +} + +bool BaseSM::IsPending() { + return pending_count_ != 0; +} + +// ----------------------------------------------------------------------- + +class MasterSM : public BaseSM { + public: + MasterSM(const FilePath& path, HANDLE channel, bool dump_to_disk) + : BaseSM(channel), + path_(path), + dump_to_disk_(dump_to_disk) { + } + virtual ~MasterSM() { + delete writer_; + } + + bool DoInit(); + virtual void OnIOCompleted(MessageLoopForIO::IOContext* context, + DWORD bytes_transfered, DWORD error); + + private: + enum { + MASTER_INITIAL = 0, + MASTER_CONNECT, + MASTER_GET_ENTRY, + MASTER_GET_NEXT_ENTRY, + MASTER_GET_KEY, + MASTER_GET_USE_TIMES, + MASTER_GET_DATA_SIZE, + MASTER_READ_DATA, + MASTER_END + }; + + void SendGetPrevEntry(); + void DoGetEntry(); + void DoGetKey(int bytes_read); + void DoCreateEntryComplete(int result); + void DoGetUseTimes(); + void SendGetDataSize(); + void DoGetDataSize(); + void CloseEntry(); + void SendReadData(); + void DoReadData(int bytes_read); + void DoReadDataComplete(int ret); + void SendQuit(); + void DoEnd(); + void Fail(); + + base::Time last_used_; + base::Time last_modified_; + int64 remote_entry_; + int stream_; + int bytes_remaining_; + int offset_; + int copied_entries_; + int read_size_; + scoped_ptr cache_; + CacheDumpWriter* writer_; + const FilePath path_; + bool dump_to_disk_; +}; + +void MasterSM::OnIOCompleted(MessageLoopForIO::IOContext* context, + DWORD bytes_transfered, DWORD error) { + pending_count_--; + if (context == &out_context_) { + if (!error) + return; + return Fail(); + } + + int bytes_read = static_cast(bytes_transfered); + if (bytes_read < sizeof(Message) && state_ != MASTER_END && + state_ != MASTER_CONNECT) { + printf("Communication breakdown\n"); + return Fail(); + } + + switch (state_) { + case MASTER_CONNECT: + SendGetPrevEntry(); + break; + case MASTER_GET_ENTRY: + DoGetEntry(); + break; + case MASTER_GET_KEY: + DoGetKey(bytes_read); + break; + case MASTER_GET_USE_TIMES: + DoGetUseTimes(); + break; + case MASTER_GET_DATA_SIZE: + DoGetDataSize(); + break; + case MASTER_READ_DATA: + DoReadData(bytes_read); + break; + case MASTER_END: + if (!IsPending()) + DoEnd(); + break; + default: + NOTREACHED(); + break; + } +} + +bool MasterSM::DoInit() { + DEBUGMSG("Master DoInit\n"); + DCHECK(state_ == MASTER_INITIAL); + + if (dump_to_disk_) { + writer_ = new DiskDumper(path_); + } else { + disk_cache::Backend* cache; + net::TestCompletionCallback cb; + int rv = disk_cache::CreateCacheBackend(net::DISK_CACHE, path_, 0, false, + cache_thread_.message_loop_proxy(), + NULL, &cache, cb.callback()); + if (cb.GetResult(rv) != net::OK) { + printf("Unable to initialize new files\n"); + return false; + } + cache_.reset(cache); + writer_ = new CacheDumper(cache_.get()); + } + if (!writer_) + return false; + + copied_entries_ = 0; + remote_entry_ = 0; + + if (ConnectChannel()) { + SendGetPrevEntry(); + // If we don't have pending operations we couldn't connect. + return IsPending(); + } + + state_ = MASTER_CONNECT; + return true; +} + +void MasterSM::SendGetPrevEntry() { + DEBUGMSG("Master SendGetPrevEntry\n"); + state_ = MASTER_GET_ENTRY; + Message msg; + msg.command = GET_PREV_ENTRY; + msg.long_arg1 = remote_entry_; + SendMsg(msg); +} + +void MasterSM::DoGetEntry() { + DEBUGMSG("Master DoGetEntry\n"); + DCHECK(state_ == MASTER_GET_ENTRY); + DCHECK(input_->msg.command == GET_PREV_ENTRY); + if (input_->msg.result != RESULT_OK) + return Fail(); + + if (!input_->msg.long_arg1) { + printf("Done: %d entries copied over.\n", copied_entries_); + return SendQuit(); + } + remote_entry_ = input_->msg.long_arg1; + state_ = MASTER_GET_KEY; + Message msg; + msg.command = GET_KEY; + msg.long_arg1 = remote_entry_; + SendMsg(msg); +} + +void MasterSM::DoGetKey(int bytes_read) { + DEBUGMSG("Master DoGetKey\n"); + DCHECK(state_ == MASTER_GET_KEY); + DCHECK(input_->msg.command == GET_KEY); + if (input_->msg.result == RESULT_NAME_OVERFLOW) { + // The key is too long. Just move on. + printf("Skipping entry (name too long)\n"); + return SendGetPrevEntry(); + } + + if (input_->msg.result != RESULT_OK) + return Fail(); + + std::string key(input_->buffer); + DCHECK(key.size() == static_cast(input_->msg.buffer_bytes - 1)); + + int rv = writer_->CreateEntry( + key, reinterpret_cast(&entry_), + base::Bind(&MasterSM::DoCreateEntryComplete, base::Unretained(this))); + + if (rv != net::ERR_IO_PENDING) + DoCreateEntryComplete(rv); +} + +void MasterSM::DoCreateEntryComplete(int result) { + std::string key(input_->buffer); + if (result != net::OK) { + printf("Skipping entry \"%s\": %d\n", key.c_str(), GetLastError()); + return SendGetPrevEntry(); + } + + if (key.size() >= 64) { + key[60] = '.'; + key[61] = '.'; + key[62] = '.'; + key[63] = '\0'; + } + DEBUGMSG("Entry \"%s\" created\n", key.c_str()); + state_ = MASTER_GET_USE_TIMES; + Message msg; + msg.command = GET_USE_TIMES; + msg.long_arg1 = remote_entry_; + SendMsg(msg); +} + +void MasterSM::DoGetUseTimes() { + DEBUGMSG("Master DoGetUseTimes\n"); + DCHECK(state_ == MASTER_GET_USE_TIMES); + DCHECK(input_->msg.command == GET_USE_TIMES); + if (input_->msg.result != RESULT_OK) + return Fail(); + + last_used_ = base::Time::FromInternalValue(input_->msg.long_arg2); + last_modified_ = base::Time::FromInternalValue(input_->msg.long_arg3); + stream_ = 0; + SendGetDataSize(); +} + +void MasterSM::SendGetDataSize() { + DEBUGMSG("Master SendGetDataSize (%d)\n", stream_); + state_ = MASTER_GET_DATA_SIZE; + Message msg; + msg.command = GET_DATA_SIZE; + msg.arg1 = stream_; + msg.long_arg1 = remote_entry_; + SendMsg(msg); +} + +void MasterSM::DoGetDataSize() { + DEBUGMSG("Master DoGetDataSize: %d\n", input_->msg.arg2); + DCHECK(state_ == MASTER_GET_DATA_SIZE); + DCHECK(input_->msg.command == GET_DATA_SIZE); + if (input_->msg.result == RESULT_INVALID_PARAMETER) + // No more streams, move to the next entry. + return CloseEntry(); + + if (input_->msg.result != RESULT_OK) + return Fail(); + + bytes_remaining_ = input_->msg.arg2; + offset_ = 0; + SendReadData(); +} + +void MasterSM::CloseEntry() { + DEBUGMSG("Master CloseEntry\n"); + printf("%c\r", copied_entries_ % 2 ? 'x' : '+'); + writer_->CloseEntry(entry_, last_used_, last_modified_); + entry_ = NULL; + copied_entries_++; + SendGetPrevEntry(); +} + +void MasterSM::SendReadData() { + int read_size = std::min(bytes_remaining_, kBufferSize); + DEBUGMSG("Master SendReadData (%d): %d bytes at %d\n", stream_, read_size, + offset_); + if (bytes_remaining_ <= 0) { + stream_++; + if (stream_ >= kNumStreams) + return CloseEntry(); + return SendGetDataSize(); + } + + state_ = MASTER_READ_DATA; + Message msg; + msg.command = READ_DATA; + msg.arg1 = stream_; + msg.arg2 = read_size; + msg.arg3 = offset_; + msg.long_arg1 = remote_entry_; + SendMsg(msg); +} + +void MasterSM::DoReadData(int bytes_read) { + DEBUGMSG("Master DoReadData: %d bytes\n", input_->msg.buffer_bytes); + DCHECK(state_ == MASTER_READ_DATA); + DCHECK(input_->msg.command == READ_DATA); + if (input_->msg.result != RESULT_OK) + return Fail(); + + int read_size = input_->msg.buffer_bytes; + if (!read_size) { + printf("Read failed, entry \"%s\" truncated!\n", entry_->GetKey().c_str()); + bytes_remaining_ = 0; + return SendReadData(); + } + + scoped_refptr buf = + new net::WrappedIOBuffer(input_->buffer); + int rv = writer_->WriteEntry( + entry_, stream_, offset_, buf, read_size, + base::Bind(&MasterSM::DoReadDataComplete, base::Unretained(this))); + if (rv == net::ERR_IO_PENDING) { + // We'll continue in DoReadDataComplete. + read_size_ = read_size; + return; + } + + if (rv <= 0) + return Fail(); + + offset_ += read_size; + bytes_remaining_ -= read_size; + // Read some more. + SendReadData(); +} + +void MasterSM::DoReadDataComplete(int ret) { + if (ret != read_size_) + return Fail(); + + offset_ += ret; + bytes_remaining_ -= ret; + // Read some more. + SendReadData(); +} + +void MasterSM::SendQuit() { + DEBUGMSG("Master SendQuit\n"); + state_ = MASTER_END; + Message msg; + msg.command = QUIT; + SendMsg(msg); + if (!IsPending()) + DoEnd(); +} + +void MasterSM::DoEnd() { + DEBUGMSG("Master DoEnd\n"); + MessageLoop::current()->PostTask(FROM_HERE, MessageLoop::QuitClosure()); +} + +void MasterSM::Fail() { + DEBUGMSG("Master Fail\n"); + printf("Unexpected failure\n"); + SendQuit(); +} + +// ----------------------------------------------------------------------- + +class SlaveSM : public BaseSM { + public: + SlaveSM(const FilePath& path, HANDLE channel); + virtual ~SlaveSM(); + + bool DoInit(); + virtual void OnIOCompleted(MessageLoopForIO::IOContext* context, + DWORD bytes_transfered, DWORD error); + + private: + enum { + SLAVE_INITIAL = 0, + SLAVE_WAITING, + SLAVE_END + }; + + void DoGetNextEntry(); + void DoGetPrevEntry(); + int32 GetEntryFromList(); + void DoGetEntryComplete(int result); + void DoCloseEntry(); + void DoGetKey(); + void DoGetUseTimes(); + void DoGetDataSize(); + void DoReadData(); + void DoReadDataComplete(int ret); + void DoEnd(); + void Fail(); + + void* iterator_; + Message msg_; // Used for DoReadDataComplete and DoGetEntryComplete. + + scoped_ptr cache_; +}; + +SlaveSM::SlaveSM(const FilePath& path, HANDLE channel) + : BaseSM(channel), iterator_(NULL) { + disk_cache::Backend* cache; + net::TestCompletionCallback cb; + int rv = disk_cache::CreateCacheBackend(net::DISK_CACHE, path, 0, false, + cache_thread_.message_loop_proxy(), + NULL, &cache, cb.callback()); + if (cb.GetResult(rv) != net::OK) { + printf("Unable to open cache files\n"); + return; + } + cache_.reset(reinterpret_cast(cache)); + cache_->SetUpgradeMode(); +} + +SlaveSM::~SlaveSM() { + if (iterator_) + cache_->EndEnumeration(&iterator_); +} + +void SlaveSM::OnIOCompleted(MessageLoopForIO::IOContext* context, + DWORD bytes_transfered, DWORD error) { + pending_count_--; + if (state_ == SLAVE_END) { + if (IsPending()) + return; + return DoEnd(); + } + + if (context == &out_context_) { + if (!error) + return; + return Fail(); + } + + int bytes_read = static_cast(bytes_transfered); + if (bytes_read < sizeof(Message)) { + printf("Communication breakdown\n"); + return Fail(); + } + DCHECK(state_ == SLAVE_WAITING); + + switch (input_->msg.command) { + case GET_NEXT_ENTRY: + DoGetNextEntry(); + break; + case GET_PREV_ENTRY: + DoGetPrevEntry(); + break; + case CLOSE_ENTRY: + DoCloseEntry(); + break; + case GET_KEY: + DoGetKey(); + break; + case GET_USE_TIMES: + DoGetUseTimes(); + break; + case GET_DATA_SIZE: + DoGetDataSize(); + break; + case READ_DATA: + DoReadData(); + break; + case QUIT: + DoEnd(); + break; + default: + NOTREACHED(); + break; + } +} + +bool SlaveSM::DoInit() { + DEBUGMSG("\t\t\tSlave DoInit\n"); + DCHECK(state_ == SLAVE_INITIAL); + state_ = SLAVE_WAITING; + if (!cache_.get()) + return false; + + return ReceiveMsg(); +} + +void SlaveSM::DoGetNextEntry() { + DEBUGMSG("\t\t\tSlave DoGetNextEntry\n"); + Message msg; + msg.command = GET_NEXT_ENTRY; + + if (input_->msg.arg1) { + // We only support one list. + msg.result = RESULT_UNKNOWN_COMMAND; + } else { + msg.result = GetEntryFromList(); + msg.long_arg1 = reinterpret_cast(entry_); + } + SendMsg(msg); +} + +void SlaveSM::DoGetPrevEntry() { + DEBUGMSG("\t\t\tSlave DoGetPrevEntry\n"); + Message msg; + msg.command = GET_PREV_ENTRY; + + if (input_->msg.arg1) { + // We only support one list. + msg.result = RESULT_UNKNOWN_COMMAND; + } else { + msg.result = GetEntryFromList(); + if (msg.result == RESULT_PENDING) { + // We are not done yet. + msg_ = msg; + return; + } + msg.long_arg1 = reinterpret_cast(entry_); + } + SendMsg(msg); +} + +// Move to the next or previous entry on the list. +int32 SlaveSM::GetEntryFromList() { + DEBUGMSG("\t\t\tSlave GetEntryFromList\n"); + if (input_->msg.long_arg1 != reinterpret_cast(entry_)) + return RESULT_INVALID_PARAMETER; + + // We know that the current iteration is valid. + if (entry_) + entry_->Close(); + + int rv; + if (input_->msg.command == GET_NEXT_ENTRY) { + rv = cache_->OpenNextEntry( + &iterator_, reinterpret_cast(&entry_), + base::Bind(&SlaveSM::DoGetEntryComplete, base::Unretained(this))); + } else { + DCHECK(input_->msg.command == GET_PREV_ENTRY); + rv = cache_->OpenPrevEntry(&iterator_, + reinterpret_cast(&entry_), + base::Bind(&SlaveSM::DoGetEntryComplete, + base::Unretained(this))); + } + DCHECK_EQ(net::ERR_IO_PENDING, rv); + return RESULT_PENDING; +} + +void SlaveSM::DoGetEntryComplete(int result) { + DEBUGMSG("\t\t\tSlave DoGetEntryComplete\n"); + if (result != net::OK) { + entry_ = NULL; + DEBUGMSG("\t\t\tSlave end of list\n"); + } + + msg_.result = RESULT_OK; + msg_.long_arg1 = reinterpret_cast(entry_); + SendMsg(msg_); +} + +void SlaveSM::DoCloseEntry() { + DEBUGMSG("\t\t\tSlave DoCloseEntry\n"); + Message msg; + msg.command = GET_KEY; + + if (!entry_ || input_->msg.long_arg1 != reinterpret_cast(entry_)) { + msg.result = RESULT_INVALID_PARAMETER; + } else { + entry_->Close(); + entry_ = NULL; + cache_->EndEnumeration(&iterator_); + msg.result = RESULT_OK; + } + SendMsg(msg); +} + +void SlaveSM::DoGetKey() { + DEBUGMSG("\t\t\tSlave DoGetKey\n"); + Message msg; + msg.command = GET_KEY; + + if (!entry_ || input_->msg.long_arg1 != reinterpret_cast(entry_)) { + msg.result = RESULT_INVALID_PARAMETER; + } else { + std::string key = entry_->GetKey(); + msg.buffer_bytes = std::min(key.size() + 1, + static_cast(kBufferSize)); + memcpy(output_->buffer, key.c_str(), msg.buffer_bytes); + if (msg.buffer_bytes != static_cast(key.size() + 1)) { + // We don't support moving this entry. Just tell the master. + msg.result = RESULT_NAME_OVERFLOW; + } else { + msg.result = RESULT_OK; + } + } + SendMsg(msg); +} + +void SlaveSM::DoGetUseTimes() { + DEBUGMSG("\t\t\tSlave DoGetUseTimes\n"); + Message msg; + msg.command = GET_USE_TIMES; + + if (!entry_ || input_->msg.long_arg1 != reinterpret_cast(entry_)) { + msg.result = RESULT_INVALID_PARAMETER; + } else { + msg.long_arg2 = entry_->GetLastUsed().ToInternalValue(); + msg.long_arg3 = entry_->GetLastModified().ToInternalValue(); + msg.result = RESULT_OK; + } + SendMsg(msg); +} + +void SlaveSM::DoGetDataSize() { + DEBUGMSG("\t\t\tSlave DoGetDataSize\n"); + Message msg; + msg.command = GET_DATA_SIZE; + + int stream = input_->msg.arg1; + if (!entry_ || input_->msg.long_arg1 != reinterpret_cast(entry_) || + stream < 0 || stream >= kNumStreams) { + msg.result = RESULT_INVALID_PARAMETER; + } else { + msg.arg1 = stream; + msg.arg2 = entry_->GetDataSize(stream); + msg.result = RESULT_OK; + } + SendMsg(msg); +} + +void SlaveSM::DoReadData() { + DEBUGMSG("\t\t\tSlave DoReadData\n"); + Message msg; + msg.command = READ_DATA; + + int stream = input_->msg.arg1; + int size = input_->msg.arg2; + if (!entry_ || input_->msg.long_arg1 != reinterpret_cast(entry_) || + stream < 0 || stream > 1 || size > kBufferSize) { + msg.result = RESULT_INVALID_PARAMETER; + } else { + scoped_refptr buf = + new net::WrappedIOBuffer(output_->buffer); + int ret = entry_->ReadData(stream, input_->msg.arg3, buf, size, + base::Bind(&SlaveSM::DoReadDataComplete, + base::Unretained(this))); + if (ret == net::ERR_IO_PENDING) { + // Save the message so we can continue were we left. + msg_ = msg; + return; + } + + msg.buffer_bytes = (ret < 0) ? 0 : ret; + msg.result = RESULT_OK; + } + SendMsg(msg); +} + +void SlaveSM::DoReadDataComplete(int ret) { + DEBUGMSG("\t\t\tSlave DoReadDataComplete\n"); + DCHECK_EQ(READ_DATA, msg_.command); + msg_.buffer_bytes = (ret < 0) ? 0 : ret; + msg_.result = RESULT_OK; + SendMsg(msg_); +} + +void SlaveSM::DoEnd() { + DEBUGMSG("\t\t\tSlave DoEnd\n"); + MessageLoop::current()->PostTask(FROM_HERE, MessageLoop::QuitClosure()); +} + +void SlaveSM::Fail() { + DEBUGMSG("\t\t\tSlave Fail\n"); + printf("Unexpected failure\n"); + state_ = SLAVE_END; + if (IsPending()) { + CancelIo(channel_); + } else { + DoEnd(); + } +} + +} // namespace. + +// ----------------------------------------------------------------------- + +HANDLE CreateServer(std::wstring* pipe_number) { + std::wstring pipe_name(kPipePrefix); + srand(static_cast(base::Time::Now().ToInternalValue())); + *pipe_number = base::IntToString16(rand()); + pipe_name.append(*pipe_number); + + DWORD mode = PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE | + FILE_FLAG_OVERLAPPED; + + return CreateNamedPipe(pipe_name.c_str(), mode, 0, 1, kChannelSize, + kChannelSize, 0, NULL); +} + +// This is the controller process for an upgrade operation. +int CopyCache(const FilePath& output_path, HANDLE pipe, bool copy_to_text) { + MessageLoop loop(MessageLoop::TYPE_IO); + + MasterSM master(output_path, pipe, copy_to_text); + if (!master.DoInit()) { + printf("Unable to talk with the helper\n"); + return -1; + } + + loop.Run(); + return 0; +} + +// This process will only execute commands from the controller. +int RunSlave(const FilePath& input_path, const std::wstring& pipe_number) { + MessageLoop loop(MessageLoop::TYPE_IO); + + base::win::ScopedHandle pipe(OpenServer(pipe_number)); + if (!pipe.IsValid()) { + printf("Unable to open the server pipe\n"); + return -1; + } + + SlaveSM slave(input_path, pipe); + if (!slave.DoInit()) { + printf("Unable to talk with the main process\n"); + return -1; + } + + loop.Run(); + return 0; +} + +// Starts a new process, to generate the files. +int LaunchSlave(CommandLine command_line, + const std::wstring& pipe_number, + int version) { + bool do_upgrade = command_line.HasSwitch(kUpgrade); + bool do_convert_to_text = command_line.HasSwitch(kDumpToFiles); + + if (do_upgrade) { + FilePath program(base::StringPrintf(L"%ls%d", L"dump_cache", version)); + command_line.SetProgram(program); + } + + if (do_upgrade || do_convert_to_text) + command_line.AppendSwitch(kSlave); + + command_line.AppendSwitchNative(kPipe, pipe_number); + if (!base::LaunchProcess(command_line, base::LaunchOptions(), NULL)) { + printf("Unable to launch the needed version of this tool: %ls\n", + command_line.GetProgram().value().c_str()); + printf(kUpgradeHelp); + return TOOL_NOT_FOUND; + } + return ALL_GOOD; +} diff --git a/net/tools/dump_cache/upgrade_win.h b/net/tools/dump_cache/upgrade_win.h new file mode 100644 index 0000000..3384707 --- /dev/null +++ b/net/tools/dump_cache/upgrade_win.h @@ -0,0 +1,22 @@ +// Copyright (c) 2012 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 + +#include "base/command_line.h" +#include "base/file_path.h" + +// Creates a new server, and returns a new named pipe to communicate with it. +HANDLE CreateServer(std::wstring* pipe_number); + +// This is the controller process for an upgrade operation. +int CopyCache(const FilePath& output_path, HANDLE pipe, bool copy_to_text); + +// This process will only execute commands from the controller. +int RunSlave(const FilePath& input_path, const std::wstring& pipe_number); + +// Starts a new process, to generate the files. +int LaunchSlave(CommandLine command_line, + const std::wstring& pipe_number, + int version); -- cgit v1.1