1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "net/tools/dump_cache/simple_cache_dumper.h"
6
7#include "base/at_exit.h"
8#include "base/command_line.h"
9#include "base/logging.h"
10#include "base/memory/scoped_ptr.h"
11#include "base/message_loop/message_loop.h"
12#include "base/message_loop/message_loop_proxy.h"
13#include "base/threading/thread.h"
14#include "net/base/cache_type.h"
15#include "net/base/io_buffer.h"
16#include "net/base/net_errors.h"
17#include "net/disk_cache/disk_cache.h"
18#include "net/tools/dump_cache/cache_dumper.h"
19
20namespace net {
21
22SimpleCacheDumper::SimpleCacheDumper(base::FilePath input_path,
23                                     base::FilePath output_path)
24    : state_(STATE_NONE),
25      input_path_(input_path),
26      output_path_(output_path),
27      writer_(new DiskDumper(output_path)),
28      cache_thread_(new base::Thread("CacheThead")),
29      src_entry_(NULL),
30      dst_entry_(NULL),
31      io_callback_(base::Bind(&SimpleCacheDumper::OnIOComplete,
32                              base::Unretained(this))),
33      rv_(0) {
34}
35
36SimpleCacheDumper::~SimpleCacheDumper() {
37}
38
39int SimpleCacheDumper::Run() {
40  base::MessageLoopForIO main_message_loop;
41
42  LOG(INFO) << "Reading cache from: " << input_path_.value();
43  LOG(INFO) << "Writing cache to: " << output_path_.value();
44
45  if (!cache_thread_->StartWithOptions(
46          base::Thread::Options(base::MessageLoop::TYPE_IO, 0))) {
47    LOG(ERROR) << "Unable to start thread";
48    return ERR_UNEXPECTED;
49  }
50  state_ = STATE_CREATE_CACHE;
51  int rv = DoLoop(OK);
52  if (rv == ERR_IO_PENDING) {
53    main_message_loop.Run();
54    return rv_;
55  }
56  return rv;
57}
58
59int SimpleCacheDumper::DoLoop(int rv) {
60  do {
61    State state = state_;
62    state_ = STATE_NONE;
63    switch (state) {
64      case STATE_CREATE_CACHE:
65        CHECK_EQ(OK, rv);
66        rv = DoCreateCache();
67        break;
68      case STATE_CREATE_CACHE_COMPLETE:
69        rv = DoCreateCacheComplete(rv);
70        break;
71      case STATE_OPEN_ENTRY:
72        CHECK_EQ(OK, rv);
73        rv = DoOpenEntry();
74        break;
75      case STATE_OPEN_ENTRY_COMPLETE:
76        rv = DoOpenEntryComplete(rv);
77        break;
78      case STATE_CREATE_ENTRY:
79        CHECK_EQ(OK, rv);
80        rv = DoCreateEntry();
81        break;
82      case STATE_CREATE_ENTRY_COMPLETE:
83        rv = DoCreateEntryComplete(rv);
84        break;
85      case STATE_READ_HEADERS:
86        CHECK_EQ(OK, rv);
87        rv = DoReadHeaders();
88        break;
89      case STATE_READ_HEADERS_COMPLETE:
90        rv = DoReadHeadersComplete(rv);
91        break;
92      case STATE_WRITE_HEADERS:
93        CHECK_EQ(OK, rv);
94        rv = DoWriteHeaders();
95        break;
96      case STATE_WRITE_HEADERS_COMPLETE:
97        rv = DoWriteHeadersComplete(rv);
98        break;
99      case STATE_READ_BODY:
100        CHECK_EQ(OK, rv);
101        rv = DoReadBody();
102        break;
103      case STATE_READ_BODY_COMPLETE:
104        rv = DoReadBodyComplete(rv);
105        break;
106      case STATE_WRITE_BODY:
107        CHECK_EQ(OK, rv);
108        rv = DoWriteBody();
109        break;
110      case STATE_WRITE_BODY_COMPLETE:
111        rv = DoWriteBodyComplete(rv);
112        break;
113      default:
114        NOTREACHED() << "state_: " << state_;
115        break;
116    }
117  } while (state_ != STATE_NONE && rv != ERR_IO_PENDING);
118  return rv;
119}
120
121int SimpleCacheDumper::DoCreateCache() {
122  DCHECK(!cache_);
123  state_ = STATE_CREATE_CACHE_COMPLETE;
124  return disk_cache::CreateCacheBackend(
125      DISK_CACHE,
126      CACHE_BACKEND_DEFAULT,
127      input_path_,
128      0,
129      false,
130      cache_thread_->message_loop_proxy().get(),
131      NULL,
132      &cache_,
133      io_callback_);
134}
135
136int SimpleCacheDumper::DoCreateCacheComplete(int rv) {
137  if (rv < 0)
138    return rv;
139
140  reinterpret_cast<disk_cache::BackendImpl*>(cache_.get())->SetUpgradeMode();
141  reinterpret_cast<disk_cache::BackendImpl*>(cache_.get())->SetFlags(
142      disk_cache::kNoRandom);
143
144  state_ = STATE_OPEN_ENTRY;
145  return OK;
146}
147
148int SimpleCacheDumper::DoOpenEntry() {
149  DCHECK(!dst_entry_);
150  DCHECK(!src_entry_);
151  state_ = STATE_OPEN_ENTRY_COMPLETE;
152  if (!iter_)
153    iter_ = cache_->CreateIterator();
154  return iter_->OpenNextEntry(&src_entry_, io_callback_);
155}
156
157int SimpleCacheDumper::DoOpenEntryComplete(int rv) {
158  // ERR_FAILED indicates iteration finished.
159  if (rv == ERR_FAILED)
160    return OK;
161
162  if (rv < 0)
163    return rv;
164
165  state_ = STATE_CREATE_ENTRY;
166  return OK;
167}
168
169int SimpleCacheDumper::DoCreateEntry() {
170  DCHECK(!dst_entry_);
171  state_ = STATE_CREATE_ENTRY_COMPLETE;
172
173  return writer_->CreateEntry(src_entry_->GetKey(), &dst_entry_,
174                              io_callback_);
175}
176
177int SimpleCacheDumper::DoCreateEntryComplete(int rv) {
178  if (rv < 0)
179    return rv;
180
181  state_ = STATE_READ_HEADERS;
182  return OK;
183}
184
185int SimpleCacheDumper::DoReadHeaders() {
186  state_ = STATE_READ_HEADERS_COMPLETE;
187  int32 size = src_entry_->GetDataSize(0);
188  buf_ = new IOBufferWithSize(size);
189  return src_entry_->ReadData(0, 0, buf_.get(), size, io_callback_);
190}
191
192int SimpleCacheDumper::DoReadHeadersComplete(int rv) {
193  if (rv < 0)
194    return rv;
195
196  state_ = STATE_WRITE_HEADERS;
197  return OK;
198}
199
200int SimpleCacheDumper::DoWriteHeaders() {
201  int rv = writer_->WriteEntry(
202      dst_entry_, 0, 0, buf_.get(), buf_->size(), io_callback_);
203  if (rv == 0)
204    return ERR_FAILED;
205
206  state_ = STATE_WRITE_HEADERS_COMPLETE;
207  return OK;
208}
209
210int SimpleCacheDumper::DoWriteHeadersComplete(int rv) {
211  if (rv < 0)
212    return rv;
213
214  state_ = STATE_READ_BODY;
215  return OK;
216}
217
218int SimpleCacheDumper::DoReadBody() {
219  state_ = STATE_READ_BODY_COMPLETE;
220  int32 size = src_entry_->GetDataSize(1);
221  // If the body is empty, we can neither read nor write it, so
222  // just move to the next.
223  if (size <= 0) {
224    state_ = STATE_WRITE_BODY_COMPLETE;
225    return OK;
226  }
227  buf_ = new IOBufferWithSize(size);
228  return src_entry_->ReadData(1, 0, buf_.get(), size, io_callback_);
229}
230
231int SimpleCacheDumper::DoReadBodyComplete(int rv) {
232  if (rv < 0)
233    return rv;
234
235  state_ = STATE_WRITE_BODY;
236  return OK;
237}
238
239int SimpleCacheDumper::DoWriteBody() {
240  int rv = writer_->WriteEntry(
241      dst_entry_, 1, 0, buf_.get(), buf_->size(), io_callback_);
242  if (rv == 0)
243    return ERR_FAILED;
244
245  state_ = STATE_WRITE_BODY_COMPLETE;
246  return OK;
247}
248
249int SimpleCacheDumper::DoWriteBodyComplete(int rv) {
250  if (rv < 0)
251    return rv;
252
253  src_entry_->Close();
254  writer_->CloseEntry(dst_entry_, base::Time::Now(), base::Time::Now());
255  src_entry_ = NULL;
256  dst_entry_ = NULL;
257
258  state_ = STATE_OPEN_ENTRY;
259  return OK;
260}
261
262void SimpleCacheDumper::OnIOComplete(int rv) {
263  rv = DoLoop(rv);
264
265  if (rv != ERR_IO_PENDING) {
266    rv_ = rv;
267    cache_.reset();
268    base::MessageLoop::current()->Quit();
269  }
270}
271
272}  // namespace net
273