upgrade.cc revision c407dc5cd9bdc5668497f21b26b09d988ab439de
1// Copyright (c) 2010 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 "base/file_path.h"
6#include "base/logging.h"
7#include "base/message_loop.h"
8#include "base/scoped_ptr.h"
9#include "base/string_util.h"
10#include "base/thread.h"
11#include "googleurl/src/gurl.h"
12#include "net/base/io_buffer.h"
13#include "net/base/test_completion_callback.h"
14#include "net/disk_cache/backend_impl.h"
15#include "net/disk_cache/entry_impl.h"
16#include "net/http/http_cache.h"
17#include "net/http/http_response_headers.h"
18#include "net/http/http_response_info.h"
19#include "net/tools/dump_cache/cache_dumper.h"
20
21namespace {
22
23const wchar_t kPipePrefix[] = L"\\\\.\\pipe\\dump_cache_";
24const int kChannelSize = 64 * 1024;
25const int kNumStreams = 4;
26
27// Simple macro to print out formatted debug messages. It is similar to a DLOG
28// except that it doesn't include a header.
29#ifdef NDEBUG
30#define DEBUGMSG(...) {}
31#else
32#define DEBUGMSG(...) { printf(__VA_ARGS__); }
33#endif
34
35HANDLE OpenServer(const std::wstring& pipe_number) {
36  std::wstring pipe_name(kPipePrefix);
37  pipe_name.append(pipe_number);
38  return CreateFile(pipe_name.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL,
39                    OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
40}
41
42// This is the basic message to use between the two processes. It is intended
43// to transmit a single action (like "get the key name for entry xx"), with up
44// to 5 32-bit arguments and 4 64-bit arguments. After this structure, the rest
45// of the message has |buffer_bytes| of length with the actual data.
46struct Message {
47  int32 command;
48  int32 result;
49  int32 buffer_bytes;
50  int32 arg1;
51  int32 arg2;
52  int32 arg3;
53  int32 arg4;
54  int32 arg5;
55  int64 long_arg1;
56  int64 long_arg2;
57  int64 long_arg3;
58  int64 long_arg4;
59  Message() {
60    memset(this, 0, sizeof(*this));
61  }
62  Message& operator= (const Message& other) {
63    memcpy(this, &other, sizeof(*this));
64    return *this;
65  }
66};
67
68const int kBufferSize = kChannelSize - sizeof(Message);
69struct IoBuffer {
70  Message msg;
71  char buffer[kBufferSize];
72};
73COMPILE_ASSERT(sizeof(IoBuffer) == kChannelSize, invalid_io_buffer);
74
75
76// The list of commands.
77// Currently, there is support for working ONLY with one entry at a time.
78enum {
79  // Get the entry from list |arg1| that follows |long_arg1|.
80  // The result is placed on |long_arg1| (closes the previous one).
81  GET_NEXT_ENTRY = 1,
82  // Get the entry from list |arg1| that precedes |long_arg1|.
83  // The result is placed on |long_arg1| (closes the previous one).
84  GET_PREV_ENTRY,
85  // Closes the entry |long_arg1|.
86  CLOSE_ENTRY,
87  // Get the key of the entry |long_arg1|.
88  GET_KEY,
89  // Get last used (long_arg2) and last modified (long_arg3) times for the
90  // entry at |long_arg1|.
91  GET_USE_TIMES,
92  // Returns on |arg2| the data size in bytes if the stream |arg1| of entry at
93  // |long_arg1|.
94  GET_DATA_SIZE,
95  // Returns |arg2| bytes of the stream |arg1| for the entry at |long_arg1|,
96  // starting at offset |arg3|.
97  READ_DATA,
98  // End processing requests.
99  QUIT
100};
101
102// The list of return codes.
103enum {
104  RESULT_OK = 0,
105  RESULT_UNKNOWN_COMMAND,
106  RESULT_INVALID_PARAMETER,
107  RESULT_NAME_OVERFLOW,
108  RESULT_PENDING  // This error code is NOT expected by the master process.
109};
110
111// -----------------------------------------------------------------------
112
113class BaseSM : public MessageLoopForIO::IOHandler {
114 public:
115  explicit BaseSM(HANDLE channel);
116  virtual ~BaseSM();
117
118 protected:
119  bool SendMsg(const Message& msg);
120  bool ReceiveMsg();
121  bool ConnectChannel();
122  bool IsPending();
123
124  MessageLoopForIO::IOContext in_context_;
125  MessageLoopForIO::IOContext out_context_;
126  disk_cache::EntryImpl* entry_;
127  HANDLE channel_;
128  int state_;
129  int pending_count_;
130  scoped_array<char> in_buffer_;
131  scoped_array<char> out_buffer_;
132  IoBuffer* input_;
133  IoBuffer* output_;
134  base::Thread cache_thread_;
135
136  DISALLOW_COPY_AND_ASSIGN(BaseSM);
137};
138
139BaseSM::BaseSM(HANDLE channel)
140      : entry_(NULL), channel_(channel), state_(0), pending_count_(0),
141        cache_thread_("cache") {
142  in_buffer_.reset(new char[kChannelSize]);
143  out_buffer_.reset(new char[kChannelSize]);
144  input_ = reinterpret_cast<IoBuffer*>(in_buffer_.get());
145  output_ = reinterpret_cast<IoBuffer*>(out_buffer_.get());
146
147  memset(&in_context_, 0, sizeof(in_context_));
148  memset(&out_context_, 0, sizeof(out_context_));
149  in_context_.handler = this;
150  out_context_.handler = this;
151  MessageLoopForIO::current()->RegisterIOHandler(channel_, this);
152  CHECK(cache_thread_.StartWithOptions(
153            base::Thread::Options(MessageLoop::TYPE_IO, 0)));
154}
155
156BaseSM::~BaseSM() {
157  if (entry_)
158    entry_->Close();
159}
160
161bool BaseSM::SendMsg(const Message& msg) {
162  // Only one command will be in-flight at a time. Let's start the Read IO here
163  // when we know that it will be pending.
164  if (!ReceiveMsg())
165    return false;
166
167  output_->msg = msg;
168  DWORD written;
169  if (!WriteFile(channel_, output_, sizeof(msg) + msg.buffer_bytes, &written,
170                 &out_context_.overlapped)) {
171    if (ERROR_IO_PENDING != GetLastError())
172      return false;
173  }
174  pending_count_++;
175  return true;
176}
177
178bool BaseSM::ReceiveMsg() {
179  DWORD read;
180  if (!ReadFile(channel_, input_, kChannelSize, &read,
181                &in_context_.overlapped)) {
182    if (ERROR_IO_PENDING != GetLastError())
183      return false;
184  }
185  pending_count_++;
186  return true;
187}
188
189bool BaseSM::ConnectChannel() {
190  if (!ConnectNamedPipe(channel_, &in_context_.overlapped)) {
191    DWORD error = GetLastError();
192    if (ERROR_PIPE_CONNECTED == error)
193      return true;
194    // By returning true in case of a generic error, we allow the operation to
195    // fail while sending the first message.
196    if (ERROR_IO_PENDING != error)
197      return true;
198  }
199  pending_count_++;
200  return false;
201}
202
203bool BaseSM::IsPending() {
204  return pending_count_ != 0;
205}
206
207// -----------------------------------------------------------------------
208
209class MasterSM : public BaseSM {
210 public:
211  MasterSM(const std::wstring& path, HANDLE channel, bool dump_to_disk)
212      : BaseSM(channel), path_(path), dump_to_disk_(dump_to_disk),
213        ALLOW_THIS_IN_INITIALIZER_LIST(
214            create_callback_(this, &MasterSM::DoCreateEntryComplete)),
215        ALLOW_THIS_IN_INITIALIZER_LIST(
216            write_callback_(this, &MasterSM::DoReadDataComplete)) {
217  }
218  virtual ~MasterSM() {
219    delete writer_;
220  }
221
222  bool DoInit();
223  virtual void OnIOCompleted(MessageLoopForIO::IOContext* context,
224                             DWORD bytes_transfered, DWORD error);
225
226 private:
227  enum {
228    MASTER_INITIAL = 0,
229    MASTER_CONNECT,
230    MASTER_GET_ENTRY,
231    MASTER_GET_NEXT_ENTRY,
232    MASTER_GET_KEY,
233    MASTER_GET_USE_TIMES,
234    MASTER_GET_DATA_SIZE,
235    MASTER_READ_DATA,
236    MASTER_END
237  };
238
239  void SendGetPrevEntry();
240  void DoGetEntry();
241  void DoGetKey(int bytes_read);
242  void DoCreateEntryComplete(int result);
243  void DoGetUseTimes();
244  void SendGetDataSize();
245  void DoGetDataSize();
246  void CloseEntry();
247  void SendReadData();
248  void DoReadData(int bytes_read);
249  void DoReadDataComplete(int ret);
250  void SendQuit();
251  void DoEnd();
252  void Fail();
253
254  base::Time last_used_;
255  base::Time last_modified_;
256  int64 remote_entry_;
257  int stream_;
258  int bytes_remaining_;
259  int offset_;
260  int copied_entries_;
261  int read_size_;
262  scoped_ptr<disk_cache::Backend> cache_;
263  CacheDumpWriter* writer_;
264  const std::wstring& path_;
265  bool dump_to_disk_;
266  net::CompletionCallbackImpl<MasterSM> create_callback_;
267  net::CompletionCallbackImpl<MasterSM> write_callback_;
268};
269
270void MasterSM::OnIOCompleted(MessageLoopForIO::IOContext* context,
271                             DWORD bytes_transfered, DWORD error) {
272  pending_count_--;
273  if (context == &out_context_) {
274    if (!error)
275      return;
276    return Fail();
277  }
278
279  int bytes_read = static_cast<int>(bytes_transfered);
280  if (bytes_read < sizeof(Message) && state_ != MASTER_END &&
281      state_ != MASTER_CONNECT) {
282    printf("Communication breakdown\n");
283    return Fail();
284  }
285
286  switch (state_) {
287    case MASTER_CONNECT:
288      SendGetPrevEntry();
289      break;
290    case MASTER_GET_ENTRY:
291      DoGetEntry();
292      break;
293    case MASTER_GET_KEY:
294      DoGetKey(bytes_read);
295      break;
296    case MASTER_GET_USE_TIMES:
297      DoGetUseTimes();
298      break;
299    case MASTER_GET_DATA_SIZE:
300      DoGetDataSize();
301      break;
302    case MASTER_READ_DATA:
303      DoReadData(bytes_read);
304      break;
305    case MASTER_END:
306      if (!IsPending())
307        DoEnd();
308      break;
309    default:
310      NOTREACHED();
311      break;
312  }
313}
314
315bool MasterSM::DoInit() {
316  DEBUGMSG("Master DoInit\n");
317  DCHECK(state_ == MASTER_INITIAL);
318
319  if (dump_to_disk_) {
320    writer_ = new DiskDumper(path_);
321  } else {
322    disk_cache::Backend* cache;
323    TestCompletionCallback cb;
324    int rv = disk_cache::CreateCacheBackend(net::DISK_CACHE,
325                                            FilePath::FromWStringHack(path_), 0,
326                                            false,
327                                            cache_thread_.message_loop_proxy(),
328                                            &cache, &cb);
329    if (cb.GetResult(rv) != net::OK) {
330      printf("Unable to initialize new files\n");
331      return false;
332    }
333    cache_.reset(cache);
334    writer_ = new CacheDumper(cache_.get());
335  }
336  if (!writer_)
337    return false;
338
339  copied_entries_ = 0;
340  remote_entry_ = 0;
341
342  if (ConnectChannel()) {
343    SendGetPrevEntry();
344    // If we don't have pending operations we couldn't connect.
345    return IsPending();
346  }
347
348  state_ = MASTER_CONNECT;
349  return true;
350}
351
352void MasterSM::SendGetPrevEntry() {
353  DEBUGMSG("Master SendGetPrevEntry\n");
354  state_ = MASTER_GET_ENTRY;
355  Message msg;
356  msg.command = GET_PREV_ENTRY;
357  msg.long_arg1 = remote_entry_;
358  SendMsg(msg);
359}
360
361void MasterSM::DoGetEntry() {
362  DEBUGMSG("Master DoGetEntry\n");
363  DCHECK(state_ == MASTER_GET_ENTRY);
364  DCHECK(input_->msg.command == GET_PREV_ENTRY);
365  if (input_->msg.result != RESULT_OK)
366    return Fail();
367
368  if (!input_->msg.long_arg1) {
369    printf("Done: %d entries copied over.\n", copied_entries_);
370    return SendQuit();
371  }
372  remote_entry_ = input_->msg.long_arg1;
373  state_ = MASTER_GET_KEY;
374  Message msg;
375  msg.command = GET_KEY;
376  msg.long_arg1 = remote_entry_;
377  SendMsg(msg);
378}
379
380void MasterSM::DoGetKey(int bytes_read) {
381  DEBUGMSG("Master DoGetKey\n");
382  DCHECK(state_ == MASTER_GET_KEY);
383  DCHECK(input_->msg.command == GET_KEY);
384  if (input_->msg.result == RESULT_NAME_OVERFLOW) {
385    // The key is too long. Just move on.
386    printf("Skipping entry (name too long)\n");
387    return SendGetPrevEntry();
388  }
389
390  if (input_->msg.result != RESULT_OK)
391    return Fail();
392
393  std::string key(input_->buffer);
394  DCHECK(key.size() == static_cast<size_t>(input_->msg.buffer_bytes - 1));
395
396  int rv = writer_->CreateEntry(key,
397                                reinterpret_cast<disk_cache::Entry**>(&entry_),
398                                &create_callback_);
399
400  if (rv != net::ERR_IO_PENDING)
401    DoCreateEntryComplete(rv);
402}
403
404void MasterSM::DoCreateEntryComplete(int result) {
405  std::string key(input_->buffer);
406  if (result != net::OK) {
407    printf("Skipping entry \"%s\": %d\n", key.c_str(), GetLastError());
408    return SendGetPrevEntry();
409  }
410
411  if (key.size() >= 64) {
412    key[60] = '.';
413    key[61] = '.';
414    key[62] = '.';
415    key[63] = '\0';
416  }
417  DEBUGMSG("Entry \"%s\" created\n", key.c_str());
418  state_ = MASTER_GET_USE_TIMES;
419  Message msg;
420  msg.command = GET_USE_TIMES;
421  msg.long_arg1 = remote_entry_;
422  SendMsg(msg);
423}
424
425void MasterSM::DoGetUseTimes() {
426  DEBUGMSG("Master DoGetUseTimes\n");
427  DCHECK(state_ == MASTER_GET_USE_TIMES);
428  DCHECK(input_->msg.command == GET_USE_TIMES);
429  if (input_->msg.result != RESULT_OK)
430    return Fail();
431
432  last_used_ = base::Time::FromInternalValue(input_->msg.long_arg2);
433  last_modified_ = base::Time::FromInternalValue(input_->msg.long_arg3);
434  stream_ = 0;
435  SendGetDataSize();
436}
437
438void MasterSM::SendGetDataSize() {
439  DEBUGMSG("Master SendGetDataSize (%d)\n", stream_);
440  state_ = MASTER_GET_DATA_SIZE;
441  Message msg;
442  msg.command = GET_DATA_SIZE;
443  msg.arg1 = stream_;
444  msg.long_arg1 = remote_entry_;
445  SendMsg(msg);
446}
447
448void MasterSM::DoGetDataSize() {
449  DEBUGMSG("Master DoGetDataSize: %d\n", input_->msg.arg2);
450  DCHECK(state_ == MASTER_GET_DATA_SIZE);
451  DCHECK(input_->msg.command == GET_DATA_SIZE);
452  if (input_->msg.result == RESULT_INVALID_PARAMETER)
453    // No more streams, move to the next entry.
454    return CloseEntry();
455
456  if (input_->msg.result != RESULT_OK)
457    return Fail();
458
459  bytes_remaining_ = input_->msg.arg2;
460  offset_ = 0;
461  SendReadData();
462}
463
464void MasterSM::CloseEntry() {
465  DEBUGMSG("Master CloseEntry\n");
466  printf("%c\r", copied_entries_ % 2 ? 'x' : '+');
467  writer_->CloseEntry(entry_, last_used_, last_modified_);
468  entry_ = NULL;
469  copied_entries_++;
470  SendGetPrevEntry();
471}
472
473void MasterSM::SendReadData() {
474  int read_size = std::min(bytes_remaining_, kBufferSize);
475  DEBUGMSG("Master SendReadData (%d): %d bytes at %d\n", stream_, read_size,
476           offset_);
477  if (bytes_remaining_ <= 0) {
478    stream_++;
479    if (stream_ >= kNumStreams)
480      return CloseEntry();
481    return SendGetDataSize();
482  }
483
484  state_ = MASTER_READ_DATA;
485  Message msg;
486  msg.command = READ_DATA;
487  msg.arg1 = stream_;
488  msg.arg2 = read_size;
489  msg.arg3 = offset_;
490  msg.long_arg1 = remote_entry_;
491  SendMsg(msg);
492}
493
494void MasterSM::DoReadData(int bytes_read) {
495  DEBUGMSG("Master DoReadData: %d bytes\n", input_->msg.buffer_bytes);
496  DCHECK(state_ == MASTER_READ_DATA);
497  DCHECK(input_->msg.command == READ_DATA);
498  if (input_->msg.result != RESULT_OK)
499    return Fail();
500
501  int read_size = input_->msg.buffer_bytes;
502  if (!read_size) {
503    printf("Read failed, entry \"%s\" truncated!\n", entry_->GetKey().c_str());
504    bytes_remaining_ = 0;
505    return SendReadData();
506  }
507
508  scoped_refptr<net::WrappedIOBuffer> buf =
509      new net::WrappedIOBuffer(input_->buffer);
510  int rv = writer_->WriteEntry(entry_, stream_, offset_, buf, read_size,
511                               &write_callback_);
512  if (rv == net::ERR_IO_PENDING) {
513    // We'll continue in DoReadDataComplete.
514    read_size_ = read_size;
515    return;
516  }
517
518  if (rv <= 0)
519    return Fail();
520
521  offset_ += read_size;
522  bytes_remaining_ -= read_size;
523  // Read some more.
524  SendReadData();
525}
526
527void MasterSM::DoReadDataComplete(int ret) {
528  if (ret != read_size_)
529    return Fail();
530
531  offset_ += ret;
532  bytes_remaining_ -= ret;
533  // Read some more.
534  SendReadData();
535}
536
537void MasterSM::SendQuit() {
538  DEBUGMSG("Master SendQuit\n");
539  state_ = MASTER_END;
540  Message msg;
541  msg.command = QUIT;
542  SendMsg(msg);
543  if (!IsPending())
544    DoEnd();
545}
546
547void MasterSM::DoEnd() {
548  DEBUGMSG("Master DoEnd\n");
549  MessageLoop::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
550}
551
552void MasterSM::Fail() {
553  DEBUGMSG("Master Fail\n");
554  printf("Unexpected failure\n");
555  SendQuit();
556}
557
558// -----------------------------------------------------------------------
559
560class SlaveSM : public BaseSM {
561 public:
562  SlaveSM(const std::wstring& path, HANDLE channel);
563  virtual ~SlaveSM();
564
565  bool DoInit();
566  virtual void OnIOCompleted(MessageLoopForIO::IOContext* context,
567                             DWORD bytes_transfered, DWORD error);
568
569 private:
570  enum {
571    SLAVE_INITIAL = 0,
572    SLAVE_WAITING,
573    SLAVE_END
574  };
575
576  void DoGetNextEntry();
577  void DoGetPrevEntry();
578  int32 GetEntryFromList();
579  void DoGetEntryComplete(int result);
580  void DoCloseEntry();
581  void DoGetKey();
582  void DoGetUseTimes();
583  void DoGetDataSize();
584  void DoReadData();
585  void DoReadDataComplete(int ret);
586  void DoEnd();
587  void Fail();
588
589  void* iterator_;
590  Message msg_;  // Used for DoReadDataComplete and DoGetEntryComplete.
591
592  net::CompletionCallbackImpl<SlaveSM> read_callback_;
593  net::CompletionCallbackImpl<SlaveSM> next_callback_;
594  scoped_ptr<disk_cache::BackendImpl> cache_;
595};
596
597SlaveSM::SlaveSM(const std::wstring& path, HANDLE channel)
598    : BaseSM(channel), iterator_(NULL),
599      ALLOW_THIS_IN_INITIALIZER_LIST(
600          read_callback_(this, &SlaveSM::DoReadDataComplete)),
601      ALLOW_THIS_IN_INITIALIZER_LIST(
602          next_callback_(this, &SlaveSM::DoGetEntryComplete)) {
603  disk_cache::Backend* cache;
604  TestCompletionCallback cb;
605  int rv = disk_cache::CreateCacheBackend(net::DISK_CACHE,
606                                          FilePath::FromWStringHack(path), 0,
607                                          false,
608                                          cache_thread_.message_loop_proxy(),
609                                          &cache, &cb);
610  if (cb.GetResult(rv) != net::OK) {
611    printf("Unable to open cache files\n");
612    return;
613  }
614  cache_.reset(reinterpret_cast<disk_cache::BackendImpl*>(cache));
615  cache_->SetUpgradeMode();
616}
617
618SlaveSM::~SlaveSM() {
619  if (iterator_)
620    cache_->EndEnumeration(&iterator_);
621}
622
623void SlaveSM::OnIOCompleted(MessageLoopForIO::IOContext* context,
624                            DWORD bytes_transfered, DWORD error) {
625  pending_count_--;
626  if (state_ == SLAVE_END) {
627    if (IsPending())
628      return;
629    return DoEnd();
630  }
631
632  if (context == &out_context_) {
633    if (!error)
634      return;
635    return Fail();
636  }
637
638  int bytes_read = static_cast<int>(bytes_transfered);
639  if (bytes_read < sizeof(Message)) {
640    printf("Communication breakdown\n");
641    return Fail();
642  }
643  DCHECK(state_ == SLAVE_WAITING);
644
645  switch (input_->msg.command) {
646    case GET_NEXT_ENTRY:
647      DoGetNextEntry();
648      break;
649    case GET_PREV_ENTRY:
650      DoGetPrevEntry();
651      break;
652    case CLOSE_ENTRY:
653      DoCloseEntry();
654      break;
655    case GET_KEY:
656      DoGetKey();
657      break;
658    case GET_USE_TIMES:
659      DoGetUseTimes();
660      break;
661    case GET_DATA_SIZE:
662      DoGetDataSize();
663      break;
664    case READ_DATA:
665      DoReadData();
666      break;
667    case QUIT:
668      DoEnd();
669      break;
670    default:
671      NOTREACHED();
672      break;
673  }
674}
675
676bool SlaveSM::DoInit() {
677  DEBUGMSG("\t\t\tSlave DoInit\n");
678  DCHECK(state_ == SLAVE_INITIAL);
679  state_ = SLAVE_WAITING;
680  if (!cache_.get())
681    return false;
682
683  return ReceiveMsg();
684}
685
686void SlaveSM::DoGetNextEntry() {
687  DEBUGMSG("\t\t\tSlave DoGetNextEntry\n");
688  Message msg;
689  msg.command = GET_NEXT_ENTRY;
690
691  if (input_->msg.arg1) {
692    // We only support one list.
693    msg.result = RESULT_UNKNOWN_COMMAND;
694  } else {
695    msg.result = GetEntryFromList();
696    msg.long_arg1 = reinterpret_cast<int64>(entry_);
697  }
698  SendMsg(msg);
699}
700
701void SlaveSM::DoGetPrevEntry() {
702  DEBUGMSG("\t\t\tSlave DoGetPrevEntry\n");
703  Message msg;
704  msg.command = GET_PREV_ENTRY;
705
706  if (input_->msg.arg1) {
707    // We only support one list.
708    msg.result = RESULT_UNKNOWN_COMMAND;
709  } else {
710    msg.result = GetEntryFromList();
711    if (msg.result == RESULT_PENDING) {
712      // We are not done yet.
713      msg_ = msg;
714      return;
715    }
716    msg.long_arg1 = reinterpret_cast<int64>(entry_);
717  }
718  SendMsg(msg);
719}
720
721// Move to the next or previous entry on the list.
722int32 SlaveSM::GetEntryFromList() {
723  DEBUGMSG("\t\t\tSlave GetEntryFromList\n");
724  if (input_->msg.long_arg1 != reinterpret_cast<int64>(entry_))
725    return RESULT_INVALID_PARAMETER;
726
727  // We know that the current iteration is valid.
728  if (entry_)
729    entry_->Close();
730
731  int rv;
732  if (input_->msg.command == GET_NEXT_ENTRY) {
733    rv = cache_->OpenNextEntry(&iterator_,
734                               reinterpret_cast<disk_cache::Entry**>(&entry_),
735                               &next_callback_);
736  } else {
737    DCHECK(input_->msg.command == GET_PREV_ENTRY);
738    rv = cache_->OpenPrevEntry(&iterator_,
739                               reinterpret_cast<disk_cache::Entry**>(&entry_),
740                               &next_callback_);
741  }
742  DCHECK_EQ(net::ERR_IO_PENDING, rv);
743  return RESULT_PENDING;
744}
745
746void SlaveSM::DoGetEntryComplete(int result) {
747  DEBUGMSG("\t\t\tSlave DoGetEntryComplete\n");
748  if (result != net::OK) {
749    entry_ = NULL;
750    DEBUGMSG("\t\t\tSlave end of list\n");
751  }
752
753  msg_.result = RESULT_OK;
754  msg_.long_arg1 = reinterpret_cast<int64>(entry_);
755  SendMsg(msg_);
756}
757
758void SlaveSM::DoCloseEntry() {
759  DEBUGMSG("\t\t\tSlave DoCloseEntry\n");
760  Message msg;
761  msg.command = GET_KEY;
762
763  if (!entry_ || input_->msg.long_arg1 != reinterpret_cast<int64>(entry_)) {
764    msg.result =  RESULT_INVALID_PARAMETER;
765  } else {
766    entry_->Close();
767    entry_ = NULL;
768    cache_->EndEnumeration(&iterator_);
769    msg.result = RESULT_OK;
770  }
771  SendMsg(msg);
772}
773
774void SlaveSM::DoGetKey() {
775  DEBUGMSG("\t\t\tSlave DoGetKey\n");
776  Message msg;
777  msg.command = GET_KEY;
778
779  if (!entry_ || input_->msg.long_arg1 != reinterpret_cast<int64>(entry_)) {
780    msg.result =  RESULT_INVALID_PARAMETER;
781  } else {
782    std::string key = entry_->GetKey();
783    msg.buffer_bytes = std::min(key.size() + 1,
784                                static_cast<size_t>(kBufferSize));
785    memcpy(output_->buffer, key.c_str(), msg.buffer_bytes);
786    if (msg.buffer_bytes != static_cast<int32>(key.size() + 1)) {
787      // We don't support moving this entry. Just tell the master.
788      msg.result = RESULT_NAME_OVERFLOW;
789    } else {
790      msg.result = RESULT_OK;
791    }
792  }
793  SendMsg(msg);
794}
795
796void SlaveSM::DoGetUseTimes() {
797  DEBUGMSG("\t\t\tSlave DoGetUseTimes\n");
798  Message msg;
799  msg.command = GET_USE_TIMES;
800
801  if (!entry_ || input_->msg.long_arg1 != reinterpret_cast<int64>(entry_)) {
802    msg.result =  RESULT_INVALID_PARAMETER;
803  } else {
804    msg.long_arg2 = entry_->GetLastUsed().ToInternalValue();
805    msg.long_arg3 = entry_->GetLastModified().ToInternalValue();
806    msg.result = RESULT_OK;
807  }
808  SendMsg(msg);
809}
810
811void SlaveSM::DoGetDataSize() {
812  DEBUGMSG("\t\t\tSlave DoGetDataSize\n");
813  Message msg;
814  msg.command = GET_DATA_SIZE;
815
816  int stream = input_->msg.arg1;
817  if (!entry_ || input_->msg.long_arg1 != reinterpret_cast<int64>(entry_) ||
818      stream < 0 || stream >= kNumStreams) {
819    msg.result =  RESULT_INVALID_PARAMETER;
820  } else {
821    msg.arg1 = stream;
822    msg.arg2 = entry_->GetDataSize(stream);
823    msg.result = RESULT_OK;
824  }
825  SendMsg(msg);
826}
827
828void SlaveSM::DoReadData() {
829  DEBUGMSG("\t\t\tSlave DoReadData\n");
830  Message msg;
831  msg.command = READ_DATA;
832
833  int stream = input_->msg.arg1;
834  int size = input_->msg.arg2;
835  if (!entry_ || input_->msg.long_arg1 != reinterpret_cast<int64>(entry_) ||
836      stream < 0 || stream > 1 || size > kBufferSize) {
837    msg.result =  RESULT_INVALID_PARAMETER;
838  } else {
839    scoped_refptr<net::WrappedIOBuffer> buf =
840        new net::WrappedIOBuffer(output_->buffer);
841    int ret = entry_->ReadData(stream, input_->msg.arg3, buf, size,
842                               &read_callback_);
843    if (ret == net::ERR_IO_PENDING) {
844      // Save the message so we can continue were we left.
845      msg_ = msg;
846      return;
847    }
848
849    msg.buffer_bytes = (ret < 0) ? 0 : ret;
850    msg.result = RESULT_OK;
851  }
852  SendMsg(msg);
853}
854
855void SlaveSM::DoReadDataComplete(int ret) {
856  DEBUGMSG("\t\t\tSlave DoReadDataComplete\n");
857  DCHECK_EQ(READ_DATA, msg_.command);
858  msg_.buffer_bytes = (ret < 0) ? 0 : ret;
859  msg_.result = RESULT_OK;
860  SendMsg(msg_);
861}
862
863void SlaveSM::DoEnd() {
864  DEBUGMSG("\t\t\tSlave DoEnd\n");
865  MessageLoop::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask());
866}
867
868void SlaveSM::Fail() {
869  DEBUGMSG("\t\t\tSlave Fail\n");
870  printf("Unexpected failure\n");
871  state_ = SLAVE_END;
872  if (IsPending()) {
873    CancelIo(channel_);
874  } else {
875    DoEnd();
876  }
877}
878
879}  // namespace.
880
881// -----------------------------------------------------------------------
882
883HANDLE CreateServer(std::wstring* pipe_number) {
884  std::wstring pipe_name(kPipePrefix);
885  srand(static_cast<int>(base::Time::Now().ToInternalValue()));
886  *pipe_number = IntToWString(rand());
887  pipe_name.append(*pipe_number);
888
889  DWORD mode = PIPE_ACCESS_DUPLEX | FILE_FLAG_FIRST_PIPE_INSTANCE |
890               FILE_FLAG_OVERLAPPED;
891
892  return CreateNamedPipe(pipe_name.c_str(), mode, 0, 1, kChannelSize,
893                         kChannelSize, 0, NULL);
894}
895
896// This is the controller process for an upgrade operation.
897int CopyCache(const std::wstring& output_path, HANDLE pipe, bool copy_to_text) {
898  MessageLoop loop(MessageLoop::TYPE_IO);
899
900  MasterSM master(output_path, pipe, copy_to_text);
901  if (!master.DoInit()) {
902    printf("Unable to talk with the helper\n");
903    return -1;
904  }
905
906  loop.Run();
907  return 0;
908}
909
910// This process will only execute commands from the controller.
911int RunSlave(const std::wstring& input_path, const std::wstring& pipe_number) {
912  MessageLoop loop(MessageLoop::TYPE_IO);
913
914  ScopedHandle pipe(OpenServer(pipe_number));
915  if (!pipe.IsValid()) {
916    printf("Unable to open the server pipe\n");
917    return -1;
918  }
919
920  SlaveSM slave(input_path, pipe);
921  if (!slave.DoInit()) {
922    printf("Unable to talk with the main process\n");
923    return -1;
924  }
925
926  loop.Run();
927  return 0;
928}
929