1 /*
2  * Copyright (c) 2008, XenSource Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the name of XenSource Inc. nor the names of its contributors
13  *       may be used to endorse or promote products derived from this software
14  *       without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
20  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28 #include <stdio.h>
29 #include <errno.h>
30 #include <fcntl.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <assert.h>
34 #include <libgen.h>	/* for basename(3) */
35 #include <unistd.h>
36 
37 #include "list.h"
38 #include "scheduler.h"
39 #include "tapdisk-vbd.h"
40 #include "tapdisk-server.h"
41 #include "tapdisk-disktype.h"
42 #include "tapdisk-utils.h"
43 #include "libvhd.h"
44 
45 #define POLL_READ                        0
46 #define POLL_WRITE                       1
47 
48 #define SPB_SHIFT (VHD_BLOCK_SHIFT - SECTOR_SHIFT)
49 
50 struct tapdisk_stream_poll {
51 	int                              pipe[2];
52 	int                              set;
53 };
54 
55 struct tapdisk_stream_request {
56 	uint64_t                         sec;
57 	uint32_t                         secs;
58 	uint64_t                         seqno;
59 	blkif_request_t                  blkif_req;
60 	struct list_head                 next;
61 };
62 
63 struct tapdisk_stream {
64 	td_vbd_t                        *vbd;
65 
66 	unsigned int                     id;
67 
68 	int                              err;
69 
70 	uint64_t                         cur;
71 	uint64_t                         start;
72 	uint64_t                         end;
73 
74 	uint64_t                         started;
75 	uint64_t                         completed;
76 
77 	struct tapdisk_stream_poll       poll;
78 	event_id_t                       enqueue_event_id;
79 
80 	struct list_head                 free_list;
81 	struct list_head                 pending_list;
82 	struct list_head                 completed_list;
83 
84 	struct tapdisk_stream_request    requests[MAX_REQUESTS];
85 };
86 
87 static unsigned int tapdisk_stream_count;
88 
89 static void tapdisk_stream_close_image(struct tapdisk_stream *);
90 
91 static char *program;
92 static struct tapdisk_stream stream1, stream2;
93 static vhd_context_t vhd1;
94 
95 static void
usage(FILE * stream)96 usage(FILE *stream)
97 {
98 	printf("usage: %s <-n type:/path/to/image> <-m type:/path/to/image>\n",
99 			program);
100 }
101 
102 static int
open_vhd(const char * path,vhd_context_t * vhd)103 open_vhd(const char *path, vhd_context_t *vhd)
104 {
105 	int err;
106 
107 	err = vhd_open(vhd, path, VHD_OPEN_RDONLY);
108 	if (err) {
109 		printf("error opening %s: %d\n", path, err);
110 		return err;
111 	}
112 
113 	err = vhd_get_bat(vhd);
114 	if (err)
115 	{
116 		printf("error reading BAT for %s: %d\n", path, err);
117 		vhd_close(vhd);
118 		return err;
119 	}
120 
121 	return 0;
122 }
123 
124 static inline void
tapdisk_stream_poll_initialize(struct tapdisk_stream_poll * p)125 tapdisk_stream_poll_initialize(struct tapdisk_stream_poll *p)
126 {
127 	p->set = 0;
128 	p->pipe[POLL_READ] = p->pipe[POLL_WRITE] = -1;
129 }
130 
131 static int
tapdisk_stream_poll_open(struct tapdisk_stream_poll * p)132 tapdisk_stream_poll_open(struct tapdisk_stream_poll *p)
133 {
134 	int err;
135 
136 	tapdisk_stream_poll_initialize(p);
137 
138 	err = pipe(p->pipe);
139 	if (err)
140 		return -errno;
141 
142 	err = fcntl(p->pipe[POLL_READ], F_SETFL, O_NONBLOCK);
143 	if (err)
144 		goto out;
145 
146 	err = fcntl(p->pipe[POLL_WRITE], F_SETFL, O_NONBLOCK);
147 	if (err)
148 		goto out;
149 
150 	return 0;
151 
152 out:
153 	close(p->pipe[POLL_READ]);
154 	close(p->pipe[POLL_WRITE]);
155 	tapdisk_stream_poll_initialize(p);
156 	return -errno;
157 }
158 
159 static void
tapdisk_stream_poll_close(struct tapdisk_stream_poll * p)160 tapdisk_stream_poll_close(struct tapdisk_stream_poll *p)
161 {
162 	if (p->pipe[POLL_READ] != -1)
163 		close(p->pipe[POLL_READ]);
164 	if (p->pipe[POLL_WRITE] != -1)
165 		close(p->pipe[POLL_WRITE]);
166 	tapdisk_stream_poll_initialize(p);
167 }
168 
169 static inline void
tapdisk_stream_poll_clear(struct tapdisk_stream_poll * p)170 tapdisk_stream_poll_clear(struct tapdisk_stream_poll *p)
171 {
172 	int dummy;
173 
174 	read_exact(p->pipe[POLL_READ], &dummy, sizeof(dummy));
175 	p->set = 0;
176 }
177 
178 static inline void
tapdisk_stream_poll_set(struct tapdisk_stream_poll * p)179 tapdisk_stream_poll_set(struct tapdisk_stream_poll *p)
180 {
181 	int dummy = 0;
182 
183 	if (!p->set) {
184 		write_exact(p->pipe[POLL_WRITE], &dummy, sizeof(dummy));
185 		p->set = 1;
186 	}
187 }
188 
189 static inline int
tapdisk_stream_stop(struct tapdisk_stream * s)190 tapdisk_stream_stop(struct tapdisk_stream *s)
191 {
192 	return ((s->cur == s->end || s->err) &&
193 			list_empty(&s->pending_list) &&
194 			list_empty(&s->completed_list));
195 }
196 
197 static inline void
tapdisk_stream_initialize_request(struct tapdisk_stream_request * req)198 tapdisk_stream_initialize_request(struct tapdisk_stream_request *req)
199 {
200 	memset(req, 0, sizeof(*req));
201 	INIT_LIST_HEAD(&req->next);
202 }
203 
204 static inline int
tapdisk_stream_request_idx(struct tapdisk_stream * s,struct tapdisk_stream_request * req)205 tapdisk_stream_request_idx(struct tapdisk_stream *s,
206 			   struct tapdisk_stream_request *req)
207 {
208 	return (req - s->requests);
209 }
210 
211 static inline struct tapdisk_stream_request *
tapdisk_stream_get_request(struct tapdisk_stream * s)212 tapdisk_stream_get_request(struct tapdisk_stream *s)
213 {
214 	struct tapdisk_stream_request *req;
215 
216 	if (list_empty(&s->free_list))
217 		return NULL;
218 
219 	req = list_entry(s->free_list.next,
220 			 struct tapdisk_stream_request, next);
221 
222 	list_del_init(&req->next);
223 	tapdisk_stream_initialize_request(req);
224 
225 	return req;
226 }
227 
228 static inline void
tapdisk_stream_queue_completed(struct tapdisk_stream * s,struct tapdisk_stream_request * sreq)229 tapdisk_stream_queue_completed(struct tapdisk_stream *s,
230 			       struct tapdisk_stream_request *sreq)
231 {
232 	struct tapdisk_stream_request *itr;
233 
234 	list_for_each_entry(itr, &s->completed_list, next)
235 		if (sreq->seqno < itr->seqno) {
236 			list_add_tail(&sreq->next, &itr->next);
237 			return;
238 		}
239 
240 	list_add_tail(&sreq->next, &s->completed_list);
241 }
242 
243 static int
tapdisk_result_compare(struct tapdisk_stream_request * sreq1,struct tapdisk_stream_request * sreq2)244 tapdisk_result_compare(struct tapdisk_stream_request *sreq1,
245 		struct tapdisk_stream_request  *sreq2)
246 {
247 	unsigned long idx1, idx2;
248 	char *buf1, *buf2;
249 	int result;
250 
251 	assert(sreq1->seqno == sreq2->seqno);
252 	assert(sreq1->secs == sreq2->secs);
253 	idx1 = (unsigned long)tapdisk_stream_request_idx(&stream1,
254 			sreq1);
255 	idx2 = (unsigned long)tapdisk_stream_request_idx(&stream2,
256 			sreq2);
257 	buf1 = (char *)MMAP_VADDR(stream1.vbd->ring.vstart, idx1, 0);
258 	buf2 = (char *)MMAP_VADDR(stream2.vbd->ring.vstart, idx2, 0);
259 
260 	result = memcmp(buf1, buf2, sreq1->secs << SECTOR_SHIFT);
261 	return result;
262 }
263 
264 static int
tapdisk_stream_process_data(void)265 tapdisk_stream_process_data(void)
266 {
267 	struct tapdisk_stream_request *sreq1, *sreq2, *tmp1, *tmp2;
268 	int advance_both;
269 	int result = 0;
270 
271 	sreq1 = list_entry(stream1.completed_list.next,
272 			struct tapdisk_stream_request, next);
273 	sreq2 = list_entry(stream2.completed_list.next,
274 			struct tapdisk_stream_request, next);
275 	tmp1 = list_entry(sreq1->next.next,
276 			struct tapdisk_stream_request, next);
277 	tmp2 = list_entry(sreq2->next.next,
278 			struct tapdisk_stream_request, next);
279 	while (result == 0 &&
280 			&sreq1->next != &stream1.completed_list &&
281 			&sreq2->next != &stream2.completed_list) {
282 		//printf("checking: %llu|%llu\n", sreq1->seqno, sreq2->seqno);
283 		advance_both = 1;
284 		if (sreq1->seqno < sreq2->seqno) {
285 			advance_both = 0;
286 			goto advance1;
287 		}
288 		if (sreq1->seqno > sreq2->seqno)
289 			goto advance2;
290 
291 		result = tapdisk_result_compare(sreq1, sreq2);
292 
293 		stream1.completed++;
294 		stream2.completed++;
295 
296 		list_del_init(&sreq1->next);
297 		list_add_tail(&sreq1->next, &stream1.free_list);
298 		list_del_init(&sreq2->next);
299 		list_add_tail(&sreq2->next, &stream2.free_list);
300 
301 advance1:
302 		sreq1 = tmp1;
303 		tmp1 = list_entry(tmp1->next.next,
304 				struct tapdisk_stream_request, next);
305 		if (!advance_both)
306 			continue;
307 advance2:
308 		sreq2 = tmp2;
309 		tmp2 = list_entry(tmp2->next.next,
310 				struct tapdisk_stream_request, next);
311 	}
312 
313 	return result;
314 }
315 
316 static void
tapdisk_stream_dequeue(void * arg,blkif_response_t * rsp)317 tapdisk_stream_dequeue(void *arg, blkif_response_t *rsp)
318 {
319 	struct tapdisk_stream *s = (struct tapdisk_stream *)arg;
320 	struct tapdisk_stream_request *sreq = s->requests + rsp->id;
321 
322 	list_del_init(&sreq->next);
323 
324 	if (rsp->status == BLKIF_RSP_OKAY)
325 		tapdisk_stream_queue_completed(s, sreq);
326 	else {
327 		s->err = EIO;
328 		list_add_tail(&sreq->next, &s->free_list);
329 		fprintf(stderr, "error reading sector 0x%"PRIx64"\n", sreq->sec);
330 	}
331 
332 	if (tapdisk_stream_process_data()) {
333 		fprintf(stderr, "mismatch at sector 0x%"PRIx64"\n",
334 				sreq->sec);
335 		stream1.err = EINVAL;
336 		stream2.err = EINVAL;
337 	}
338 
339 	tapdisk_stream_poll_set(&stream1.poll);
340 	tapdisk_stream_poll_set(&stream2.poll);
341 }
342 
343 static inline int
tapdisk_stream_enqueue_copy(struct tapdisk_stream * s,struct tapdisk_stream_request * r)344 tapdisk_stream_enqueue_copy(struct tapdisk_stream *s,
345 		struct tapdisk_stream_request *r)
346 {
347 	td_vbd_t *vbd;
348 	blkif_request_t *breq;
349 	td_vbd_request_t *vreq;
350 	struct tapdisk_stream_request *sreq;
351 	int idx;
352 
353 	vbd = stream2.vbd;
354 	sreq = tapdisk_stream_get_request(s);
355 	if (!sreq)
356 		return 1;
357 
358 	idx                 = tapdisk_stream_request_idx(s, sreq);
359 
360 	sreq->sec           = r->sec;
361 	sreq->secs          = r->secs;
362 	sreq->seqno         = r->seqno;
363 
364 	breq                = &sreq->blkif_req;
365 	breq->id            = idx;
366 	breq->nr_segments   = r->blkif_req.nr_segments;
367 	breq->sector_number = r->blkif_req.sector_number;
368 	breq->operation     = BLKIF_OP_READ;
369 
370 	for (int i = 0; i < r->blkif_req.nr_segments; i++) {
371 		struct blkif_request_segment *seg = breq->seg + i;
372 		seg->first_sect = r->blkif_req.seg[i].first_sect;
373 		seg->last_sect  = r->blkif_req.seg[i].last_sect;
374 	}
375 	s->cur += sreq->secs;
376 
377 	vreq = vbd->request_list + idx;
378 	assert(list_empty(&vreq->next));
379 	assert(vreq->secs_pending == 0);
380 
381 	memcpy(&vreq->req, breq, sizeof(*breq));
382 	vbd->received++;
383 	vreq->vbd = vbd;
384 
385 	tapdisk_vbd_move_request(vreq, &vbd->new_requests);
386 	list_add_tail(&sreq->next, &s->pending_list);
387 
388 	return 0;
389 }
390 
391 static void
tapdisk_stream_enqueue1(void)392 tapdisk_stream_enqueue1(void)
393 {
394 	td_vbd_t *vbd;
395 	int i, idx, psize, blk;
396 	struct tapdisk_stream *s = &stream1;
397 
398 	vbd = s->vbd;
399 	psize = getpagesize();
400 
401 	while (s->cur < s->end && !s->err) {
402 		blkif_request_t *breq;
403 		td_vbd_request_t *vreq;
404 		struct tapdisk_stream_request *sreq;
405 
406 		/* skip any blocks that are not present in this image */
407 		blk = s->cur >> SPB_SHIFT;
408 		while (s->cur < s->end && vhd1.bat.bat[blk] == DD_BLK_UNUSED) {
409 			//printf("skipping block %d\n", blk);
410 			blk++;
411 			s->cur = blk << SPB_SHIFT;
412 		}
413 
414 		if (s->cur >= s->end)
415 			break;
416 
417 		sreq = tapdisk_stream_get_request(s);
418 		if (!sreq)
419 			break;
420 
421 		idx                 = tapdisk_stream_request_idx(s, sreq);
422 
423 		sreq->sec           = s->cur;
424 		sreq->secs          = 0;
425 		sreq->seqno         = s->started++;
426 
427 		breq                = &sreq->blkif_req;
428 		breq->id            = idx;
429 		breq->nr_segments   = 0;
430 		breq->sector_number = sreq->sec;
431 		breq->operation     = BLKIF_OP_READ;
432 
433 		for (i = 0; i < BLKIF_MAX_SEGMENTS_PER_REQUEST; i++) {
434 			uint32_t secs;
435 			struct blkif_request_segment *seg = breq->seg + i;
436 
437 			secs = MIN(s->end - s->cur, psize >> SECTOR_SHIFT);
438 			secs = MIN(((blk + 1) << SPB_SHIFT) - s->cur, secs);
439 			if (!secs)
440 				break;
441 
442 			sreq->secs += secs;
443 			s->cur     += secs;
444 
445 			seg->first_sect = 0;
446 			seg->last_sect  = secs - 1;
447 			breq->nr_segments++;
448 		}
449 
450 		vreq = vbd->request_list + idx;
451 
452 		assert(list_empty(&vreq->next));
453 		assert(vreq->secs_pending == 0);
454 
455 		memcpy(&vreq->req, breq, sizeof(*breq));
456 		vbd->received++;
457 		vreq->vbd = vbd;
458 
459 		tapdisk_vbd_move_request(vreq, &vbd->new_requests);
460 		list_add_tail(&sreq->next, &s->pending_list);
461 	}
462 
463 	tapdisk_vbd_issue_requests(vbd);
464 }
465 
466 static void
tapdisk_stream_enqueue2(void)467 tapdisk_stream_enqueue2(void)
468 {
469 	td_vbd_t *vbd;
470 	int i, blk;
471 	struct tapdisk_stream_request *itr;
472 	struct tapdisk_stream *s = &stream2;
473 
474 	vbd = s->vbd;
475 
476 	/* issue the same requests that we issued on stream1 */
477 	list_for_each_entry(itr, &stream1.completed_list, next) {
478 		if (itr->sec < s->cur)
479 			continue;
480 		if (tapdisk_stream_enqueue_copy(s, itr))
481 			goto done;
482 	}
483 
484 	list_for_each_entry(itr, &stream1.pending_list, next) {
485 		if (itr->sec < s->cur)
486 			continue;
487 		if (tapdisk_stream_enqueue_copy(s, itr))
488 			goto done;
489 	}
490 
491 	stream2.cur = stream1.cur;
492 
493 done:
494 	tapdisk_vbd_issue_requests(vbd);
495 }
496 
497 static inline int
tapdisk_diff_done(void)498 tapdisk_diff_done(void)
499 {
500 	return (tapdisk_stream_stop(&stream1) && tapdisk_stream_stop(&stream2));
501 }
502 
503 static void
tapdisk_diff_stop(void)504 tapdisk_diff_stop(void)
505 {
506 	tapdisk_stream_close_image(&stream1);
507 	tapdisk_stream_close_image(&stream2);
508 }
509 
510 static void
tapdisk_stream_enqueue(event_id_t id,char mode,void * arg)511 tapdisk_stream_enqueue(event_id_t id, char mode, void *arg)
512 {
513 	struct tapdisk_stream *s = (struct tapdisk_stream *)arg;
514 
515 	tapdisk_stream_poll_clear(&s->poll);
516 
517 	if (tapdisk_diff_done()) {
518 		tapdisk_diff_stop();
519 		return;
520 	}
521 
522 	if (s == &stream1)
523 		tapdisk_stream_enqueue1();
524 	else if (s == &stream2)
525 		tapdisk_stream_enqueue2();
526 	else
527 		assert(0);
528 
529 	if (tapdisk_diff_done()) {
530 		// we have to check again for the case when stream1 had no
531 		// blocks at all
532 		tapdisk_diff_stop();
533 		return;
534 	}
535 }
536 
537 static int
tapdisk_stream_open_image(struct tapdisk_stream * s,const char * path,int type)538 tapdisk_stream_open_image(struct tapdisk_stream *s, const char *path, int type)
539 {
540 	int err;
541 	image_t image;
542 
543 	s->id = tapdisk_stream_count++;
544 
545 	err = tapdisk_vbd_initialize(s->id);
546 	if (err)
547 		goto out;
548 
549 	s->vbd = tapdisk_server_get_vbd(s->id);
550 	if (!s->vbd) {
551 		err = ENODEV;
552 		goto out;
553 	}
554 
555 	tapdisk_vbd_set_callback(s->vbd, tapdisk_stream_dequeue, s);
556 
557 	err = tapdisk_vbd_open_vdi(s->vbd, path, type,
558 				   TAPDISK_STORAGE_TYPE_DEFAULT,
559 				   TD_OPEN_RDONLY);
560 	if (err)
561 		goto out;
562 
563 	s->vbd->reopened = 1;
564 
565 	err = tapdisk_vbd_get_image_info(s->vbd, &image);
566 	if (err) {
567 		fprintf(stderr, "failed getting image size: %d\n", err);
568 		return err;
569 	}
570 
571 	s->start = 0;
572 	s->cur   = s->start;
573 	s->end   = image.size;
574 
575 	err = 0;
576 
577 out:
578 	if (err)
579 		fprintf(stderr, "failed to open image %s: %d\n", path, err);
580 	return err;
581 }
582 
583 static void
tapdisk_stream_close_image(struct tapdisk_stream * s)584 tapdisk_stream_close_image(struct tapdisk_stream *s)
585 {
586 	td_vbd_t *vbd;
587 
588 	vbd = tapdisk_server_get_vbd(s->id);
589 	if (vbd) {
590 		tapdisk_vbd_close_vdi(vbd);
591 		tapdisk_server_remove_vbd(vbd);
592 		free((void *)vbd->ring.vstart);
593 		free(vbd->name);
594 		free(vbd);
595 		s->vbd = NULL;
596 	}
597 }
598 
599 static int
tapdisk_stream_initialize_requests(struct tapdisk_stream * s)600 tapdisk_stream_initialize_requests(struct tapdisk_stream *s)
601 {
602 	size_t size;
603 	td_ring_t *ring;
604 	int err, i, psize;
605 
606 	ring  = &s->vbd->ring;
607 	psize = getpagesize();
608 	size  = psize * BLKTAP_MMAP_REGION_SIZE;
609 
610 	/* sneaky -- set up ring->vstart so tapdisk_vbd will use our buffers */
611 	err = posix_memalign((void **)&ring->vstart, psize, size);
612 	if (err) {
613 		fprintf(stderr, "failed to allocate buffers: %d\n", err);
614 		ring->vstart = 0;
615 		return err;
616 	}
617 
618 	for (i = 0; i < MAX_REQUESTS; i++) {
619 		struct tapdisk_stream_request *req = s->requests + i;
620 		tapdisk_stream_initialize_request(req);
621 		list_add_tail(&req->next, &s->free_list);
622 	}
623 
624 	return 0;
625 }
626 
627 static int
tapdisk_stream_register_enqueue_event(struct tapdisk_stream * s)628 tapdisk_stream_register_enqueue_event(struct tapdisk_stream *s)
629 {
630 	int err;
631 	struct tapdisk_stream_poll *p = &s->poll;
632 
633 	err = tapdisk_stream_poll_open(p);
634 	if (err)
635 		goto out;
636 
637 	err = tapdisk_server_register_event(SCHEDULER_POLL_READ_FD,
638 					    p->pipe[POLL_READ], 0,
639 					    tapdisk_stream_enqueue, s);
640 	if (err < 0)
641 		goto out;
642 
643 	s->enqueue_event_id = err;
644 	err = 0;
645 
646 out:
647 	if (err)
648 		fprintf(stderr, "failed to register event: %d\n", err);
649 	return err;
650 }
651 
652 static void
tapdisk_stream_unregister_enqueue_event(struct tapdisk_stream * s)653 tapdisk_stream_unregister_enqueue_event(struct tapdisk_stream *s)
654 {
655 	if (s->enqueue_event_id) {
656 		tapdisk_server_unregister_event(s->enqueue_event_id);
657 		s->enqueue_event_id = 0;
658 	}
659 	tapdisk_stream_poll_close(&s->poll);
660 }
661 
662 static inline void
tapdisk_stream_initialize(struct tapdisk_stream * s)663 tapdisk_stream_initialize(struct tapdisk_stream *s)
664 {
665 	memset(s, 0, sizeof(*s));
666 	INIT_LIST_HEAD(&s->free_list);
667 	INIT_LIST_HEAD(&s->pending_list);
668 	INIT_LIST_HEAD(&s->completed_list);
669 }
670 
671 static int
tapdisk_stream_open(struct tapdisk_stream * s,const char * arg)672 tapdisk_stream_open(struct tapdisk_stream *s, const char *arg)
673 {
674 	int err, type;
675 	const char *path;
676 
677 	type = tapdisk_disktype_parse_params(arg, &path);
678 	if (type < 0)
679 		return type;
680 
681 	tapdisk_stream_initialize(s);
682 
683 	err = tapdisk_stream_open_image(s, path, type);
684 	if (err)
685 		return err;
686 
687 	err = tapdisk_stream_initialize_requests(s);
688 	if (err)
689 		return err;
690 
691 	err = tapdisk_stream_register_enqueue_event(s);
692 	if (err)
693 		return err;
694 
695 	tapdisk_stream_enqueue(s->enqueue_event_id,
696 			       SCHEDULER_POLL_READ_FD, s);
697 
698 	return 0;
699 }
700 
701 static void
tapdisk_stream_release(struct tapdisk_stream * s)702 tapdisk_stream_release(struct tapdisk_stream *s)
703 {
704 	tapdisk_stream_close_image(s);
705 	tapdisk_stream_unregister_enqueue_event(s);
706 }
707 
708 static int
tapdisk_stream_run(struct tapdisk_stream * s)709 tapdisk_stream_run(struct tapdisk_stream *s)
710 {
711 	tapdisk_stream_enqueue(s->enqueue_event_id, SCHEDULER_POLL_READ_FD, s);
712 	tapdisk_server_run();
713 	return s->err;
714 }
715 
716 int
main(int argc,char * argv[])717 main(int argc, char *argv[])
718 {
719 	int c, err, type1;
720 	const char *arg1 = NULL, *arg2 = NULL;
721 	const disk_info_t *info;
722 	const char *path1;
723 
724 	err    = 0;
725 
726 	program = basename(argv[0]);
727 
728 	while ((c = getopt(argc, argv, "n:m:h")) != -1) {
729 		switch (c) {
730 		case 'n':
731 			arg1 = optarg;
732 			break;
733 		case 'm':
734 			arg2 = optarg;
735 			break;
736 		case 'h':
737 			usage(stdout);
738 			return 0;
739 		default:
740 			goto fail_usage;
741 		}
742 	}
743 
744 	if (!arg1 || !arg2)
745 		goto fail_usage;
746 
747 	type1 = tapdisk_disktype_parse_params(arg1, &path1);
748 	if (type1 < 0)
749 		return type1;
750 
751 	if (type1 != DISK_TYPE_VHD) {
752 		printf("error: first VDI is not VHD\n");
753 		return EINVAL;
754 	}
755 
756 	err = open_vhd(path1, &vhd1);
757 	if (err)
758 		return err;
759 
760 	tapdisk_start_logging("tapdisk-diff");
761 
762 	err = tapdisk_server_initialize();
763 	if (err)
764 		goto out;
765 
766 	err = tapdisk_stream_open(&stream1, arg1);
767 	if (err) {
768 		fprintf(stderr, "Failed to open %s: %s\n",
769 			arg1, strerror(-err));
770 		goto out;
771 	}
772 
773 	err = tapdisk_stream_open(&stream2, arg2);
774 	if (err) {
775 		fprintf(stderr, "Failed to open %s: %s\n",
776 			arg2, strerror(-err));
777 		goto out1;
778 	}
779 
780 	if (stream1.end != stream2.end) {
781 		fprintf(stderr, "Image sizes differ: %"PRIu64" != %"PRIu64"\n",
782 				stream1.end, stream2.end);
783 		err = EINVAL;
784 		goto out2;
785 	}
786 
787 	tapdisk_server_run();
788 
789 out2:
790 	tapdisk_stream_release(&stream2);
791 out1:
792 	tapdisk_stream_release(&stream1);
793 out:
794 	vhd_close(&vhd1);
795 	tapdisk_stop_logging();
796 
797 	return err ? : stream1.err;
798 
799 fail_usage:
800 	usage(stderr);
801 	return 1;
802 }
803