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