1 /* img2qcow.c
2  *
3  * Generates a qcow format disk and fills it from an existing 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 <unistd.h>
37 #include <sys/statvfs.h>
38 #include <sys/stat.h>
39 #include <sys/ioctl.h>
40 #include <string.h>
41 #include <zlib.h>
42 #include <inttypes.h>
43 #include <libaio.h>
44 
45 #include "bswap.h"
46 #include "aes.h"
47 #include "tapdisk.h"
48 #include "tapdisk-server.h"
49 #include "tapdisk-driver.h"
50 #include "tapdisk-interface.h"
51 #include "tapdisk-disktype.h"
52 #include "qcow.h"
53 #include "blk.h"
54 
55 
56 #if 1
57 #define DFPRINTF(_f, _a...) fprintf ( stderr, _f , ## _a )
58 #else
59 #define DFPRINTF(_f, _a...) ((void)0)
60 #endif
61 
62 /* *BSD has no O_LARGEFILE */
63 #ifndef O_LARGEFILE
64 #define O_LARGEFILE	0
65 #endif
66 
67 #define BLOCK_PROCESSSZ 4096
68 #define QCOW_VBD 0
69 #define PROGRESS_QUANT 2
70 
71 static int running = 1, complete = 0;
72 static int returned_events = 0, submit_events = 0;
73 static uint32_t read_idx = 0;
74 td_driver_t *ddqcow;
75 td_vbd_t* qcow_vbd;
76 static uint64_t prev = 0, written = 0;
77 static char output[(100/PROGRESS_QUANT) + 5];
78 
79 extern tapdisk_server_t server;
80 
81 
print_bytes(void * ptr,int length)82 static void print_bytes(void *ptr, int length)
83 {
84   int i,k;
85   unsigned char *p = ptr;
86 
87     DFPRINTF("Buf dump, length %d:\n",length);
88     for (k = 0; k < length; k++) {
89         DFPRINTF("%x",*p);
90         *p++;
91 	if(k % 16 == 0) DFPRINTF("\n");
92         else if(k % 2 == 0) DFPRINTF(" ");
93     }
94     DFPRINTF("\n");
95     return;
96 }
97 
debug_output(uint64_t progress,uint64_t size)98 static void debug_output(uint64_t progress, uint64_t size)
99 {
100   //Output progress every PROGRESS_QUANT
101   uint64_t blocks = size/(100/PROGRESS_QUANT);
102 
103   if (progress/blocks > prev) {
104     memcpy(output+prev+1,"=>",2);
105     prev++;
106     DFPRINTF("\r%s     %"PRIi64"%%",
107              output, (int64_t)((prev-1)*PROGRESS_QUANT));
108   }
109   return;
110 }
111 
get_image_info(td_disk_info_t * driver,int fd)112 static int get_image_info(td_disk_info_t *driver, int fd)
113 {
114 	int ret;
115 	long size;
116 	unsigned long total_size;
117 	struct statvfs statBuf;
118 	struct stat stat;
119 	uint64_t sector_size=DEFAULT_SECTOR_SIZE;
120 
121 	ret = fstat(fd, &stat);
122 	if (ret != 0) {
123 		DFPRINTF("ERROR: fstat failed, Couldn't stat image");
124 		return -EINVAL;
125 	}
126 
127 	if (S_ISBLK(stat.st_mode)) {
128 		/*Accessing block device directly*/
129 		if (blk_getimagesize(fd, &driver->size) != 0)
130 			return -EINVAL;
131 
132 		DFPRINTF("Image size: \n\tpre sector_shift  [%"PRIu64"]\n\tpost "
133 			"sector_shift [%"PRIu64"]\n",
134 			(uint64_t)(driver->size << SECTOR_SHIFT),
135 			(uint64_t)driver->size);
136 
137 		/*Get the sector size*/
138 		if (!blk_getsectorsize(fd, &sector_size))
139 		  driver->sector_size = sector_size;
140 
141 	} else {
142 		/*Local file? try fstat instead*/
143 		driver->size = (stat.st_size >> SECTOR_SHIFT);
144 		driver->sector_size = DEFAULT_SECTOR_SIZE;
145 		DFPRINTF("Image size: [%"PRIu64"]\n",
146 			(uint64_t)driver->size);
147 	}
148 
149 	return 0;
150 }
151 
send_responses(td_request_t treq,int err)152 void send_responses(td_request_t treq, int err)
153 {
154   if (err < 0) {
155     DFPRINTF("AIO FAILURE: res [%d]!\n",err);
156     return;
157   }
158 
159   returned_events++;
160 
161   free(treq.buf);
162 }
163 
main(int argc,const char * argv[])164 int main(int argc, const char *argv[])
165 {
166         int ret = -1, fd, len, err;
167 	struct timeval timeout;
168 	uint64_t i;
169 	char *buf = NULL;
170 	td_request_t treq;
171         td_disk_info_t info;
172         td_vbd_request_t* vreq;
173 
174 	if (argc != 3) {
175 		fprintf(stderr, "Qcow-utils: v1.0.0\n");
176 		fprintf(stderr, "usage: %s <QCOW FILENAME> <SRC IMAGE>\n",
177 			argv[0]);
178 		exit(-1);
179 	}
180 
181 
182 	/*Open image*/
183 	fd = open(argv[2], O_RDONLY | O_LARGEFILE);
184 
185         if (fd == -1) {
186                 DFPRINTF("Unable to open [%s], (err %d)!\n",argv[2],0 - errno);
187                 exit(-1);
188         }
189 
190 	get_image_info(&info, fd);
191 
192 	/*Create qcow file*/
193 	ret = qcow_create(argv[1],info.size<<SECTOR_SHIFT,NULL,0);
194 
195 	if (ret < 0) {
196 		DFPRINTF("Unable to create QCOW file\n");
197 		exit(-1);
198 	} else DFPRINTF("Qcow file created: size %"PRIu64" sectors\n",
199 			(uint64_t)info.size);
200 
201         /* Open Qcow image*/
202         err = tapdisk_server_initialize();
203         if( err ) {
204           DPRINTF("qcow2raw Couldn't initialize server instance.\n");
205           return err;
206         }
207 
208         err=tapdisk_vbd_initialize(QCOW_VBD);
209         if( err ) {
210           DPRINTF("qcow2raw Couldn't initialize qcow vbd.\n");
211           return err;
212         }
213 
214         qcow_vbd = tapdisk_server_get_vbd(QCOW_VBD);
215         if (!qcow_vbd) {
216           err = -ENODEV;
217           DPRINTF("qcow2raw Couldn't create qcow vbd.\n");
218           return err;
219         }
220 
221         err = tapdisk_vbd_open_vdi(qcow_vbd, argv[1], DISK_TYPE_QCOW,
222                                    TAPDISK_STORAGE_TYPE_DEFAULT,
223                                    0);
224         if( err ) {
225           DPRINTF("qcow2raw Couldn't open qcow file.\n");
226           return err;
227         }
228 
229         ddqcow=(tapdisk_vbd_first_image(qcow_vbd))->driver;
230 
231         /*Initialise the output string*/
232         memset(output,0x20,(100/PROGRESS_QUANT)+5);
233         output[0] = '[';
234         output[(100/PROGRESS_QUANT)+2] = ']';
235         output[(100/PROGRESS_QUANT)+3] = '\0';
236         DFPRINTF("%s",output);
237 
238 	i = 0;
239 	while (running) {
240 
241 		if (!complete) {
242 			/*Read sector from image*/
243 			if (lseek(fd, i*512, SEEK_SET) == (off_t)-1) {
244 				DFPRINTF("Unable to access file offset %"PRIu64"\n",
245 				       (uint64_t)i*512);
246 				exit(-1);
247 			}
248 
249 			if( (ret = posix_memalign((void **)&buf,
250 						  BLOCK_PROCESSSZ,
251 						  BLOCK_PROCESSSZ)) != 0) {
252 				DFPRINTF("Unable to read memalign buf (%d)\n",ret);
253 				exit(-1);
254 			}
255 
256 			/*We attempt to read 4k sized blocks*/
257 			len = read(fd, buf, BLOCK_PROCESSSZ);
258 			if (len < 512) {
259 				DFPRINTF("Unable to read sector %"PRIu64"\n",
260 					 (uint64_t) (i));
261 				complete = 1;
262 				continue;
263 			}
264 
265 			len = (len >> 9);
266 
267 			treq.op = TD_OP_WRITE;
268 			treq.buf = buf;
269 			treq.sec = i;
270 			treq.secs = len;
271 			treq.image = 0;
272 			treq.cb = send_responses;
273 			treq.cb_data = buf;
274 			treq.id = 0;
275 			treq.sidx = 0;
276                         vreq = calloc(1, sizeof(td_vbd_request_t));
277 			treq.private = vreq;
278 
279 			vreq->submitting = 1;
280                         INIT_LIST_HEAD(&vreq->next);
281                         tapdisk_vbd_move_request(treq.private,
282                                                  &qcow_vbd->pending_requests);
283 
284                         ddqcow->ops->td_queue_write(ddqcow,treq);
285                         --vreq->submitting;
286 
287 			submit_events++;
288 
289 			i += len;
290 
291 			if (i == info.size)
292 			  complete = 1;
293 
294                         tapdisk_submit_all_tiocbs(&server.aio_queue);
295 			debug_output(i,info.size);
296                 }
297 
298 		while(returned_events != submit_events) {
299 		    ret = scheduler_wait_for_events(&server.scheduler);
300 		    if (ret < 0) {
301 		      DFPRINTF("server wait returned %d\n", ret);
302 		      sleep(2);
303 		    }
304 		}
305 
306 		if (complete && (returned_events == submit_events))
307 			running = 0;
308 	}
309 	memcpy(output+prev+1,"=",1);
310 	DFPRINTF("\r%s     100%%\nTRANSFER COMPLETE\n\n", output);
311 
312         ddqcow->ops->td_close(ddqcow);
313         free(ddqcow->data);
314 
315 	return 0;
316 }
317