1 /* qcow2raw.c
2  *
3  * Generates raw image data from an existing qcow image
4  *
5  * (c) 2006 Julian Chesterfield and Andrew Warfield
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License version 2
9  * as published by the Free Software Foundation; or, when distributed
10  * separately from the Linux kernel or incorporated into other
11  * software packages, subject to the following license:
12  *
13  * Permission is hereby granted, free of charge, to any person obtaining a copy
14  * of this source file (the "Software"), to deal in the Software without
15  * restriction, including without limitation the rights to use, copy, modify,
16  * merge, publish, distribute, sublicense, and/or sell copies of the Software,
17  * and to permit persons to whom the Software is furnished to do so, subject to
18  * the following conditions:
19  *
20  * The above copyright notice and this permission notice shall be included in
21  * all copies or substantial portions of the Software.
22  *
23  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
24  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
26  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
29  * IN THE SOFTWARE.
30  */
31 
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <inttypes.h>
37 #include <unistd.h>
38 #include <sys/statvfs.h>
39 #include <sys/stat.h>
40 #include <sys/ioctl.h>
41 #include <string.h>
42 
43 #include "bswap.h"
44 #include "aes.h"
45 #include "blk.h"
46 #include "tapdisk.h"
47 #include "tapdisk-server.h"
48 #include "tapdisk-driver.h"
49 #include "tapdisk-interface.h"
50 #include "tapdisk-disktype.h"
51 #include "qcow.h"
52 
53 #if 1
54 #define DFPRINTF(_f, _a...) fprintf ( stderr, _f , ## _a )
55 #else
56 #define DFPRINTF(_f, _a...) ((void)0)
57 #endif
58 
59 
60 /* *BSD has no O_LARGEFILE */
61 #ifndef O_LARGEFILE
62 #define O_LARGEFILE 0
63 #endif
64 
65 #define BLOCK_PROCESSSZ 4096
66 #define QCOW_VBD 0
67 #define AIO_VBD 1
68 #define WINDOW 32
69 #define PROGRESS_QUANT 2
70 
71 static int running = 1, complete = 0;
72 static int returned_read_events = 0, returned_write_events = 0;
73 static int submit_events = 0;
74 static uint32_t read_idx = 0;
75 td_driver_t *ddqcow, *ddaio;
76 td_vbd_t* qcow_vbd, *aio_vbd;
77 static uint64_t prev = 0, written = 0;
78 static char output[(100/PROGRESS_QUANT) + 5];
79 
80 extern tapdisk_server_t server;
81 
82 struct request_info {
83   void* buf;
84   uint64_t logical_sec;
85   int pending;
86 };
87 
print_bytes(void * ptr,int length)88 static void print_bytes(void *ptr, int length)
89 {
90   int i,k;
91   unsigned char *p = ptr;
92 
93     DFPRINTF("Buf dump, length %d:\n",length);
94     for (k = 0; k < length; k++) {
95         DFPRINTF("%x",*p);
96         *p++;
97 	if (k % 16 == 0) DFPRINTF("\n");
98         else if (k % 2 == 0) DFPRINTF(" ");
99     }
100     DFPRINTF("\n");
101     return;
102 }
103 
debug_output(uint64_t progress,uint64_t size)104 static void debug_output(uint64_t progress, uint64_t size)
105 {
106         //Output progress every PROGRESS_QUANT
107         uint64_t blocks = size/(100/PROGRESS_QUANT);
108 
109 	if (progress/blocks > prev) {
110 		memcpy(output+prev+1,"=>",2);
111 		prev++;
112 		DFPRINTF("\r%s     %"PRIu64"%%",
113 			output, (uint64_t)((prev-1)*PROGRESS_QUANT));
114 	}
115 	return;
116 }
117 
send_write_responses(td_request_t treq,int err)118 static void send_write_responses(td_request_t treq, int err)
119 {
120         struct request_info* req;
121 
122 	if (err < 0) {
123 		DFPRINTF("AIO FAILURE: res [%d]!\n",err);
124 		return;
125 	}
126 	returned_write_events+=treq.secs;
127         written += treq.secs;
128 
129         req= (struct request_info*)treq.cb_data;
130 
131         //Wait for whole request to complete.
132         req->pending-=treq.secs;
133         if(req->pending)
134           return;
135 
136         //Whole request has completed, we can free buffers.
137         free(req->buf);
138         free(req);
139 
140 	debug_output(written, ddaio->info.size);
141 
142 	return;
143 }
144 
send_read_responses(td_request_t treq,int err)145 static void send_read_responses(td_request_t treq, int err)
146 {
147 	int ret;
148         struct request_info* req;
149         td_vbd_request_t* vreq;
150 
151 	if (err < 0)  {
152 	  DFPRINTF("AIO FAILURE: res [%d]!\n",err);
153 	  return;
154 	}
155 	returned_read_events+=treq.secs;
156 
157         req= (struct request_info*)treq.cb_data;
158 
159         //do nothing until all fragments complete.
160         req->pending-=treq.secs;
161 
162         if(req->pending)
163           return;
164 
165         //This read is done.
166         tapdisk_vbd_complete_vbd_request(qcow_vbd, treq.private);
167 
168 
169         treq.op      = TD_OP_WRITE;
170         treq.buf     = req->buf;
171         treq.sec     = req->logical_sec;
172         treq.secs    = BLOCK_PROCESSSZ>>9;
173         treq.image   = tapdisk_vbd_first_image(aio_vbd);
174         treq.cb      = send_write_responses;
175         treq.id      = 0;
176         treq.sidx    = 0;
177 
178         req->pending = BLOCK_PROCESSSZ>>9;
179         treq.cb_data = req;
180 
181         vreq         = calloc(1, sizeof(td_vbd_request_t));
182         treq.private = vreq;
183 
184         //Put it in the VBD's queue, so we don't lose
185         //track of it.
186         vreq->submitting = 1;
187         INIT_LIST_HEAD(&vreq->next);
188         tapdisk_vbd_move_request(treq.private,
189                                  &aio_vbd->pending_requests);
190 
191         ddaio->ops->td_queue_write(ddaio,treq);
192         --vreq->submitting;
193 
194         tapdisk_submit_all_tiocbs(&server.aio_queue);
195 
196 	return;
197 }
198 
main(int argc,const char * argv[])199 int main(int argc, const char *argv[])
200 {
201 	int ret = -1, fd, len,input;
202 	uint64_t size;
203 	struct timeval timeout;
204 	uint64_t i;
205 	char *buf = NULL;
206 	struct stat finfo;
207 	td_request_t treq;
208 	td_vbd_request_t* vreq;
209         struct request_info* req;
210         int err;
211 
212 	if (argc != 3) {
213 		fprintf(stderr, "Qcow-utils: v1.0.0\n");
214 		fprintf(stderr, "usage: %s <Dest File descriptor> "
215 			"<Qcow SRC IMAGE>\n",
216 		       argv[0]);
217 		exit(-1);
218 	}
219 
220         err = tapdisk_server_initialize();
221         if( err ) {
222           DPRINTF("qcow2raw Couldn't initialize server instance.\n");
223           return err;
224         }
225 
226         err=tapdisk_vbd_initialize(QCOW_VBD);
227         if( err ) {
228           DPRINTF("qcow2raw Couldn't initialize qcow vbd.\n");
229           return err;
230         }
231 
232         qcow_vbd = tapdisk_server_get_vbd(QCOW_VBD);
233         if (!qcow_vbd) {
234           err = -ENODEV;
235           DPRINTF("qcow2raw Couldn't create qcow vbd.\n");
236           return err;
237         }
238 
239         err = tapdisk_vbd_open_vdi(qcow_vbd, argv[2], DISK_TYPE_QCOW,
240                                    TAPDISK_STORAGE_TYPE_DEFAULT,
241                                    TD_OPEN_RDONLY);
242         if( err ) {
243           DPRINTF("qcow2raw Couldn't open qcow file.\n");
244           return err;
245         }
246 
247         ddqcow=(tapdisk_vbd_first_image(qcow_vbd))->driver;
248 
249         /*Setup aio destination file*/
250 	ret = stat(argv[1],&finfo);
251 	if (ret == -1) {
252 		/*Check errno*/
253 		switch(errno) {
254 		case ENOENT:
255 			/*File doesn't exist, create*/
256 			fd = open(argv[1],
257 				  O_RDWR | O_LARGEFILE | O_CREAT, 0644);
258 			if (fd < 0) {
259 				DFPRINTF("ERROR creating file [%s] "
260 					 "(errno %d)\n",
261 				       argv[1], 0 - errno);
262 				exit(-1);
263 			}
264 			if (ftruncate(fd, (off_t)ddqcow->info.size<<9) < 0) {
265 				DFPRINTF("Unable to create file "
266 					"[%s] of size %"PRIu64" (errno %d). "
267 					 "Exiting...\n",
268 					argv[1],
269 					(uint64_t)ddqcow->info.size<<9,
270 					0 - errno);
271 				close(fd);
272 				exit(-1);
273 			}
274 			close(fd);
275 			break;
276 		case  ENXIO:
277 			DFPRINTF("ERROR Device [%s] does not exist\n",argv[1]);
278 			exit(-1);
279 		default:
280 			DFPRINTF("An error occurred opening Device [%s] "
281 				 "(errno %d)\n",
282 			       argv[1], 0 - errno);
283 			exit(-1);
284 		}
285 	} else {
286 		fprintf(stderr, "WARNING: All existing data in "
287 			"%s will be overwritten.\nDo you wish to continue? "
288 			"(y or n)  ",
289 			argv[1]);
290 		if (getchar() != 'y') {
291 			DFPRINTF("Exiting...\n");
292 			exit(-1);
293 		}
294 
295 		/*TODO - Test the existing file or device for adequate space*/
296 		fd = open(argv[1], O_RDWR | O_LARGEFILE);
297 		if (fd < 0) {
298 			DFPRINTF("ERROR: opening file [%s] (errno %d)\n",
299 			       argv[1], 0 - errno);
300 			exit(-1);
301 		}
302 
303 		if (S_ISBLK(finfo.st_mode)) {
304 			if (blk_getimagesize(fd, &size) != 0) {
305 				close(fd);
306 				return -1;
307 			}
308 
309 			if (size < ddqcow->info.size<<9) {
310 				DFPRINTF("ERROR: Not enough space on device "
311 					"%s (%"PRIu64" bytes available, "
312 					"%"PRIu64" bytes required\n",
313 					argv[1], size,
314 					(uint64_t)ddqcow->info.size<<9);
315 				close(fd);
316 				exit(-1);
317 			}
318 		} else {
319 			if (ftruncate(fd, (off_t)ddqcow->info.size<<9) < 0) {
320 				DFPRINTF("Unable to create file "
321 					"[%s] of size %"PRIu64" (errno %d). "
322 					 "Exiting...\n",
323 					argv[1],
324 					(uint64_t)ddqcow->info.size<<9,
325 					 0 - errno);
326 				close(fd);
327 				exit(-1);
328 			} else DFPRINTF("File [%s] truncated to length %"PRIu64" "
329 					"(%"PRIu64")\n",
330 				       argv[1],
331 				       (uint64_t)ddqcow->info.size<<9,
332 				       (uint64_t)ddqcow->info.size);
333 		}
334 		close(fd);
335 	}
336 
337         //Now the output file should be there, reopen it as an aio VBD
338         err=tapdisk_vbd_initialize(AIO_VBD);
339         if( err ) {
340           DPRINTF("qcow2raw Couldn't initialize aio vbd.\n");
341           return err;
342         }
343 
344         aio_vbd = tapdisk_server_get_vbd(AIO_VBD);
345         if (!aio_vbd) {
346           err = -ENODEV;
347           DPRINTF("qcow2raw Couldn't create aio vbd.\n");
348           return err;
349         }
350 
351         err = tapdisk_vbd_open_vdi(aio_vbd, argv[1], DISK_TYPE_AIO,
352                                    TAPDISK_STORAGE_TYPE_DEFAULT,
353                                    0);
354         if( err ) {
355           DPRINTF("qcow2raw Couldn't open aio file.\n");
356           return err;
357         }
358 
359         ddaio=(tapdisk_vbd_first_image(aio_vbd))->driver;
360 
361 	/*Initialise the output string*/
362 	memset(output,0x20,(100/PROGRESS_QUANT)+5);
363 	output[0] = '[';
364         output[(100/PROGRESS_QUANT)+2] = ']';
365         output[(100/PROGRESS_QUANT)+3] = '\0';
366 	DFPRINTF("%s",output);
367 
368 	i = 0;
369 	while (running) {
370 		timeout.tv_sec = 0;
371 
372 		if (!complete) {
373 			/*Read Pages from qcow image*/
374 			if ( (ret = posix_memalign((void **)&buf,
375 						   BLOCK_PROCESSSZ,
376 						   BLOCK_PROCESSSZ))
377 			     != 0) {
378 				DFPRINTF("Unable to alloc memory (%d)\n",ret);
379 				exit(-1);
380 			}
381 
382 			/*Attempt to read 4k sized blocks*/
383 			submit_events+=BLOCK_PROCESSSZ>>9;
384 
385 			//Set up the read request
386 			treq.op      = TD_OP_READ;
387 			treq.buf     = buf;
388 			treq.sec     = i;
389 			treq.secs    = BLOCK_PROCESSSZ>>9;
390 			treq.image   = tapdisk_vbd_first_image(qcow_vbd);
391 			treq.cb      = send_read_responses;
392 			treq.id      = 0;
393 			treq.sidx    = 0;
394 
395                         req = calloc(1, sizeof(struct request_info));
396                         req->buf = buf;
397                         req->logical_sec = i;
398                         req->pending = BLOCK_PROCESSSZ>>9;
399 			treq.cb_data = req;
400 
401                         vreq         = calloc(1, sizeof(td_vbd_request_t));
402                         treq.private = vreq;
403 
404                         //Put it in the VBD's queue, so we don't lose
405                         //track of it.
406                         vreq->submitting = 1;
407                         INIT_LIST_HEAD(&vreq->next);
408                         tapdisk_vbd_move_request(treq.private,
409                                                  &qcow_vbd->pending_requests);
410 
411 			ddqcow->ops->td_queue_read(ddqcow, treq);
412                         --vreq->submitting;
413 
414 			i += BLOCK_PROCESSSZ>>9;
415 
416 			if (i >= ddqcow->info.size)
417 			  complete = 1;
418 
419 
420 			tapdisk_submit_all_tiocbs(&server.aio_queue);
421 		}
422 
423 
424 		while(returned_write_events != submit_events) {
425 		  ret = scheduler_wait_for_events(&server.scheduler);
426 		  if (ret < 0) {
427 		    DFPRINTF("server wait returned %d\n", ret);
428 		    sleep(2);
429 		  }
430 		}
431 		if (complete && (returned_write_events == submit_events))
432 			running = 0;
433 	}
434 	memcpy(output+prev+1,"=",1);
435 	DFPRINTF("\r%s     100%%\nTRANSFER COMPLETE\n\n", output);
436 
437 	ddqcow->ops->td_close(ddqcow);
438 	ddaio->ops->td_close(ddaio);
439 	free(ddqcow->data);
440 	free(ddaio->data);
441 
442 	return 0;
443 }
444