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, §or_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