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