Annotation of qemu/qemu-img.c, revision 1.1.1.5
1.1 root 1: /*
1.1.1.3 root 2: * QEMU disk image utility
1.1.1.4 root 3: *
4: * Copyright (c) 2003-2008 Fabrice Bellard
5: *
1.1 root 6: * Permission is hereby granted, free of charge, to any person obtaining a copy
7: * of this software and associated documentation files (the "Software"), to deal
8: * in the Software without restriction, including without limitation the rights
9: * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10: * copies of the Software, and to permit persons to whom the Software is
11: * furnished to do so, subject to the following conditions:
12: *
13: * The above copyright notice and this permission notice shall be included in
14: * all copies or substantial portions of the Software.
15: *
16: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
19: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21: * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22: * THE SOFTWARE.
23: */
1.1.1.4 root 24: #include "qemu-common.h"
1.1.1.5 ! root 25: #include "osdep.h"
1.1.1.4 root 26: #include "block_int.h"
27: #include <assert.h>
1.1 root 28:
1.1.1.2 root 29: #ifdef _WIN32
1.1.1.4 root 30: #define WIN32_LEAN_AND_MEAN
1.1.1.2 root 31: #include <windows.h>
32: #endif
33:
1.1.1.5 ! root 34: /* Default to cache=writeback as data integrity is not important for qemu-tcg. */
! 35: #define BRDV_O_FLAGS BDRV_O_CACHE_WB
1.1 root 36:
1.1.1.5 ! root 37: static void QEMU_NORETURN error(const char *fmt, ...)
1.1 root 38: {
39: va_list ap;
40: va_start(ap, fmt);
41: fprintf(stderr, "qemu-img: ");
42: vfprintf(stderr, fmt, ap);
43: fprintf(stderr, "\n");
44: exit(1);
45: va_end(ap);
46: }
47:
48: static void format_print(void *opaque, const char *name)
49: {
50: printf(" %s", name);
51: }
52:
1.1.1.5 ! root 53: /* Please keep in synch with qemu-img.texi */
1.1.1.4 root 54: static void help(void)
1.1 root 55: {
1.1.1.4 root 56: printf("qemu-img version " QEMU_VERSION ", Copyright (c) 2004-2008 Fabrice Bellard\n"
1.1 root 57: "usage: qemu-img command [command options]\n"
58: "QEMU disk image utility\n"
59: "\n"
60: "Command syntax:\n"
1.1.1.4 root 61: " create [-e] [-6] [-b base_image] [-f fmt] filename [size]\n"
1.1 root 62: " commit [-f fmt] filename\n"
1.1.1.5 ! root 63: " convert [-c] [-e] [-6] [-f fmt] [-O output_fmt] [-B output_base_image] filename [filename2 [...]] output_filename\n"
1.1 root 64: " info [-f fmt] filename\n"
1.1.1.5 ! root 65: " snapshot [-l | -a snapshot | -c snapshot | -d snapshot] filename\n"
1.1 root 66: "\n"
67: "Command parameters:\n"
68: " 'filename' is a disk image filename\n"
69: " 'base_image' is the read-only disk image which is used as base for a copy on\n"
70: " write image; the copy on write image only stores the modified data\n"
1.1.1.5 ! root 71: " 'output_base_image' forces the output image to be created as a copy on write\n"
! 72: " image of the specified base image; 'output_base_image' should have the same\n"
! 73: " content as the input's base image, however the path, image format, etc may\n"
! 74: " differ\n"
1.1 root 75: " 'fmt' is the disk image format. It is guessed automatically in most cases\n"
1.1.1.5 ! root 76: " 'size' is the disk image size in kilobytes. Optional suffixes\n"
! 77: " 'M' (megabyte, 1024 * 1024) and 'G' (gigabyte, 1024 * 1024 * 1024) are"
! 78: " supported any @code{k} or @code{K} is ignored\n"
1.1 root 79: " 'output_filename' is the destination disk image filename\n"
80: " 'output_fmt' is the destination format\n"
81: " '-c' indicates that target image must be compressed (qcow format only)\n"
82: " '-e' indicates that the target image must be encrypted (qcow format only)\n"
1.1.1.4 root 83: " '-6' indicates that the target image must use compatibility level 6 (vmdk format only)\n"
1.1.1.5 ! root 84: " '-h' with or without a command shows this help and lists the supported formats\n"
! 85: "\n"
! 86: "Parameters to snapshot subcommand:\n"
! 87: " 'snapshot' is the name of the snapshot to create, apply or delete\n"
! 88: " '-a' applies a snapshot (revert disk to saved state)\n"
! 89: " '-c' creates a snapshot\n"
! 90: " '-d' deletes a snapshot\n"
! 91: " '-l' lists all snapshots in the given image\n"
1.1 root 92: );
1.1.1.5 ! root 93: printf("\nSupported formats:");
1.1 root 94: bdrv_iterate_format(format_print, NULL);
95: printf("\n");
96: exit(1);
97: }
98:
99: #if defined(WIN32)
100: /* XXX: put correct support for win32 */
101: static int read_password(char *buf, int buf_size)
102: {
103: int c, i;
104: printf("Password: ");
105: fflush(stdout);
106: i = 0;
107: for(;;) {
108: c = getchar();
109: if (c == '\n')
110: break;
111: if (i < (buf_size - 1))
112: buf[i++] = c;
113: }
114: buf[i] = '\0';
115: return 0;
116: }
117:
118: #else
119:
120: #include <termios.h>
121:
122: static struct termios oldtty;
123:
124: static void term_exit(void)
125: {
126: tcsetattr (0, TCSANOW, &oldtty);
127: }
128:
129: static void term_init(void)
130: {
131: struct termios tty;
132:
133: tcgetattr (0, &tty);
134: oldtty = tty;
135:
136: tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
137: |INLCR|IGNCR|ICRNL|IXON);
138: tty.c_oflag |= OPOST;
139: tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN);
140: tty.c_cflag &= ~(CSIZE|PARENB);
141: tty.c_cflag |= CS8;
142: tty.c_cc[VMIN] = 1;
143: tty.c_cc[VTIME] = 0;
1.1.1.4 root 144:
1.1 root 145: tcsetattr (0, TCSANOW, &tty);
146:
147: atexit(term_exit);
148: }
149:
1.1.1.4 root 150: static int read_password(char *buf, int buf_size)
1.1 root 151: {
152: uint8_t ch;
153: int i, ret;
154:
155: printf("password: ");
156: fflush(stdout);
157: term_init();
158: i = 0;
159: for(;;) {
160: ret = read(0, &ch, 1);
161: if (ret == -1) {
162: if (errno == EAGAIN || errno == EINTR) {
163: continue;
164: } else {
165: ret = -1;
166: break;
167: }
168: } else if (ret == 0) {
169: ret = -1;
170: break;
171: } else {
172: if (ch == '\r') {
173: ret = 0;
174: break;
175: }
176: if (i < (buf_size - 1))
177: buf[i++] = ch;
178: }
179: }
180: term_exit();
181: buf[i] = '\0';
182: printf("\n");
183: return ret;
184: }
185: #endif
186:
187: static BlockDriverState *bdrv_new_open(const char *filename,
188: const char *fmt)
189: {
190: BlockDriverState *bs;
191: BlockDriver *drv;
192: char password[256];
193:
194: bs = bdrv_new("");
195: if (!bs)
196: error("Not enough memory");
197: if (fmt) {
198: drv = bdrv_find_format(fmt);
199: if (!drv)
200: error("Unknown file format '%s'", fmt);
201: } else {
202: drv = NULL;
203: }
1.1.1.5 ! root 204: if (bdrv_open2(bs, filename, BRDV_O_FLAGS, drv) < 0) {
1.1 root 205: error("Could not open '%s'", filename);
206: }
207: if (bdrv_is_encrypted(bs)) {
208: printf("Disk image '%s' is encrypted.\n", filename);
209: if (read_password(password, sizeof(password)) < 0)
210: error("No password given");
211: if (bdrv_set_key(bs, password) < 0)
212: error("invalid password");
213: }
214: return bs;
215: }
216:
217: static int img_create(int argc, char **argv)
218: {
1.1.1.4 root 219: int c, ret, flags;
1.1 root 220: const char *fmt = "raw";
221: const char *filename;
222: const char *base_filename = NULL;
1.1.1.4 root 223: uint64_t size;
1.1 root 224: const char *p;
225: BlockDriver *drv;
1.1.1.4 root 226:
227: flags = 0;
1.1 root 228: for(;;) {
1.1.1.4 root 229: c = getopt(argc, argv, "b:f:he6");
1.1 root 230: if (c == -1)
231: break;
232: switch(c) {
233: case 'h':
234: help();
235: break;
236: case 'b':
237: base_filename = optarg;
238: break;
239: case 'f':
240: fmt = optarg;
241: break;
242: case 'e':
1.1.1.4 root 243: flags |= BLOCK_FLAG_ENCRYPT;
244: break;
245: case '6':
246: flags |= BLOCK_FLAG_COMPAT6;
1.1 root 247: break;
248: }
249: }
1.1.1.4 root 250: if (optind >= argc)
1.1 root 251: help();
252: filename = argv[optind++];
253: size = 0;
254: if (base_filename) {
255: BlockDriverState *bs;
256: bs = bdrv_new_open(base_filename, NULL);
257: bdrv_get_geometry(bs, &size);
258: size *= 512;
259: bdrv_delete(bs);
260: } else {
261: if (optind >= argc)
262: help();
263: p = argv[optind];
264: size = strtoul(p, (char **)&p, 0);
265: if (*p == 'M') {
266: size *= 1024 * 1024;
267: } else if (*p == 'G') {
268: size *= 1024 * 1024 * 1024;
269: } else if (*p == 'k' || *p == 'K' || *p == '\0') {
270: size *= 1024;
271: } else {
272: help();
273: }
274: }
275: drv = bdrv_find_format(fmt);
276: if (!drv)
277: error("Unknown file format '%s'", fmt);
1.1.1.4 root 278: printf("Formatting '%s', fmt=%s",
1.1 root 279: filename, fmt);
1.1.1.4 root 280: if (flags & BLOCK_FLAG_ENCRYPT)
1.1 root 281: printf(", encrypted");
1.1.1.4 root 282: if (flags & BLOCK_FLAG_COMPAT6)
283: printf(", compatibility level=6");
1.1 root 284: if (base_filename) {
285: printf(", backing_file=%s",
286: base_filename);
287: }
1.1.1.4 root 288: printf(", size=%" PRIu64 " kB\n", size / 1024);
289: ret = bdrv_create(drv, filename, size / 512, base_filename, flags);
1.1 root 290: if (ret < 0) {
291: if (ret == -ENOTSUP) {
292: error("Formatting or formatting option not supported for file format '%s'", fmt);
293: } else {
294: error("Error while formatting");
295: }
296: }
297: return 0;
298: }
299:
300: static int img_commit(int argc, char **argv)
301: {
302: int c, ret;
303: const char *filename, *fmt;
304: BlockDriver *drv;
305: BlockDriverState *bs;
306:
307: fmt = NULL;
308: for(;;) {
309: c = getopt(argc, argv, "f:h");
310: if (c == -1)
311: break;
312: switch(c) {
313: case 'h':
314: help();
315: break;
316: case 'f':
317: fmt = optarg;
318: break;
319: }
320: }
1.1.1.4 root 321: if (optind >= argc)
1.1 root 322: help();
323: filename = argv[optind++];
324:
325: bs = bdrv_new("");
326: if (!bs)
327: error("Not enough memory");
328: if (fmt) {
329: drv = bdrv_find_format(fmt);
330: if (!drv)
331: error("Unknown file format '%s'", fmt);
332: } else {
333: drv = NULL;
334: }
1.1.1.5 ! root 335: if (bdrv_open2(bs, filename, BRDV_O_FLAGS, drv) < 0) {
1.1 root 336: error("Could not open '%s'", filename);
337: }
338: ret = bdrv_commit(bs);
339: switch(ret) {
340: case 0:
341: printf("Image committed.\n");
342: break;
343: case -ENOENT:
344: error("No disk inserted");
345: break;
346: case -EACCES:
347: error("Image is read-only");
348: break;
349: case -ENOTSUP:
350: error("Image is already committed");
351: break;
352: default:
353: error("Error while committing image");
354: break;
355: }
356:
357: bdrv_delete(bs);
358: return 0;
359: }
360:
361: static int is_not_zero(const uint8_t *sector, int len)
362: {
363: int i;
364: len >>= 2;
365: for(i = 0;i < len; i++) {
366: if (((uint32_t *)sector)[i] != 0)
367: return 1;
368: }
369: return 0;
370: }
371:
1.1.1.5 ! root 372: /*
! 373: * Returns true iff the first sector pointed to by 'buf' contains at least
! 374: * a non-NUL byte.
! 375: *
! 376: * 'pnum' is set to the number of sectors (including and immediately following
! 377: * the first one) that are known to be in the same allocated/unallocated state.
! 378: */
1.1 root 379: static int is_allocated_sectors(const uint8_t *buf, int n, int *pnum)
380: {
381: int v, i;
382:
383: if (n <= 0) {
384: *pnum = 0;
385: return 0;
386: }
387: v = is_not_zero(buf, 512);
388: for(i = 1; i < n; i++) {
389: buf += 512;
390: if (v != is_not_zero(buf, 512))
391: break;
392: }
393: *pnum = i;
394: return v;
395: }
396:
397: #define IO_BUF_SIZE 65536
398:
399: static int img_convert(int argc, char **argv)
400: {
1.1.1.4 root 401: int c, ret, n, n1, bs_n, bs_i, flags, cluster_size, cluster_sectors;
1.1.1.5 ! root 402: const char *fmt, *out_fmt, *out_baseimg, *out_filename;
1.1 root 403: BlockDriver *drv;
1.1.1.4 root 404: BlockDriverState **bs, *out_bs;
405: int64_t total_sectors, nb_sectors, sector_num, bs_offset;
406: uint64_t bs_sectors;
1.1 root 407: uint8_t buf[IO_BUF_SIZE];
408: const uint8_t *buf1;
1.1.1.3 root 409: BlockDriverInfo bdi;
1.1 root 410:
411: fmt = NULL;
412: out_fmt = "raw";
1.1.1.5 ! root 413: out_baseimg = NULL;
1.1.1.4 root 414: flags = 0;
1.1 root 415: for(;;) {
1.1.1.5 ! root 416: c = getopt(argc, argv, "f:O:B:hce6");
1.1 root 417: if (c == -1)
418: break;
419: switch(c) {
420: case 'h':
421: help();
422: break;
423: case 'f':
424: fmt = optarg;
425: break;
426: case 'O':
427: out_fmt = optarg;
428: break;
1.1.1.5 ! root 429: case 'B':
! 430: out_baseimg = optarg;
! 431: break;
1.1 root 432: case 'c':
1.1.1.4 root 433: flags |= BLOCK_FLAG_COMPRESS;
1.1 root 434: break;
435: case 'e':
1.1.1.4 root 436: flags |= BLOCK_FLAG_ENCRYPT;
437: break;
438: case '6':
439: flags |= BLOCK_FLAG_COMPAT6;
1.1 root 440: break;
441: }
442: }
1.1.1.4 root 443:
444: bs_n = argc - optind - 1;
445: if (bs_n < 1) help();
446:
447: out_filename = argv[argc - 1];
1.1.1.5 ! root 448:
! 449: if (bs_n > 1 && out_baseimg)
! 450: error("-B makes no sense when concatenating multiple input images");
1.1.1.4 root 451:
452: bs = calloc(bs_n, sizeof(BlockDriverState *));
453: if (!bs)
454: error("Out of memory");
455:
456: total_sectors = 0;
457: for (bs_i = 0; bs_i < bs_n; bs_i++) {
458: bs[bs_i] = bdrv_new_open(argv[optind + bs_i], fmt);
459: if (!bs[bs_i])
460: error("Could not open '%s'", argv[optind + bs_i]);
461: bdrv_get_geometry(bs[bs_i], &bs_sectors);
462: total_sectors += bs_sectors;
463: }
1.1 root 464:
465: drv = bdrv_find_format(out_fmt);
466: if (!drv)
1.1.1.4 root 467: error("Unknown file format '%s'", out_fmt);
468: if (flags & BLOCK_FLAG_COMPRESS && drv != &bdrv_qcow && drv != &bdrv_qcow2)
1.1 root 469: error("Compression not supported for this file format");
1.1.1.4 root 470: if (flags & BLOCK_FLAG_ENCRYPT && drv != &bdrv_qcow && drv != &bdrv_qcow2)
1.1 root 471: error("Encryption not supported for this file format");
1.1.1.4 root 472: if (flags & BLOCK_FLAG_COMPAT6 && drv != &bdrv_vmdk)
473: error("Alternative compatibility level not supported for this file format");
474: if (flags & BLOCK_FLAG_ENCRYPT && flags & BLOCK_FLAG_COMPRESS)
1.1 root 475: error("Compression and encryption not supported at the same time");
1.1.1.4 root 476:
1.1.1.5 ! root 477: ret = bdrv_create(drv, out_filename, total_sectors, out_baseimg, flags);
1.1 root 478: if (ret < 0) {
479: if (ret == -ENOTSUP) {
480: error("Formatting not supported for file format '%s'", fmt);
481: } else {
482: error("Error while formatting '%s'", out_filename);
483: }
484: }
1.1.1.4 root 485:
1.1 root 486: out_bs = bdrv_new_open(out_filename, out_fmt);
487:
1.1.1.4 root 488: bs_i = 0;
489: bs_offset = 0;
490: bdrv_get_geometry(bs[0], &bs_sectors);
491:
492: if (flags & BLOCK_FLAG_COMPRESS) {
1.1.1.3 root 493: if (bdrv_get_info(out_bs, &bdi) < 0)
494: error("could not get block driver info");
495: cluster_size = bdi.cluster_size;
1.1 root 496: if (cluster_size <= 0 || cluster_size > IO_BUF_SIZE)
497: error("invalid cluster size");
498: cluster_sectors = cluster_size >> 9;
499: sector_num = 0;
500: for(;;) {
1.1.1.4 root 501: int64_t bs_num;
502: int remainder;
503: uint8_t *buf2;
504:
1.1 root 505: nb_sectors = total_sectors - sector_num;
506: if (nb_sectors <= 0)
507: break;
508: if (nb_sectors >= cluster_sectors)
509: n = cluster_sectors;
510: else
511: n = nb_sectors;
1.1.1.4 root 512:
513: bs_num = sector_num - bs_offset;
514: assert (bs_num >= 0);
515: remainder = n;
516: buf2 = buf;
517: while (remainder > 0) {
518: int nlow;
519: while (bs_num == bs_sectors) {
520: bs_i++;
521: assert (bs_i < bs_n);
522: bs_offset += bs_sectors;
523: bdrv_get_geometry(bs[bs_i], &bs_sectors);
524: bs_num = 0;
525: /* printf("changing part: sector_num=%lld, "
526: "bs_i=%d, bs_offset=%lld, bs_sectors=%lld\n",
527: sector_num, bs_i, bs_offset, bs_sectors); */
528: }
529: assert (bs_num < bs_sectors);
530:
531: nlow = (remainder > bs_sectors - bs_num) ? bs_sectors - bs_num : remainder;
532:
533: if (bdrv_read(bs[bs_i], bs_num, buf2, nlow) < 0)
534: error("error while reading");
535:
536: buf2 += nlow * 512;
537: bs_num += nlow;
538:
539: remainder -= nlow;
540: }
541: assert (remainder == 0);
542:
1.1 root 543: if (n < cluster_sectors)
544: memset(buf + n * 512, 0, cluster_size - n * 512);
545: if (is_not_zero(buf, cluster_size)) {
1.1.1.4 root 546: if (bdrv_write_compressed(out_bs, sector_num, buf,
1.1.1.3 root 547: cluster_sectors) != 0)
1.1.1.2 root 548: error("error while compressing sector %" PRId64,
549: sector_num);
1.1 root 550: }
551: sector_num += n;
552: }
1.1.1.3 root 553: /* signal EOF to align */
554: bdrv_write_compressed(out_bs, 0, NULL, 0);
1.1 root 555: } else {
1.1.1.5 ! root 556: sector_num = 0; // total number of sectors converted so far
1.1 root 557: for(;;) {
558: nb_sectors = total_sectors - sector_num;
559: if (nb_sectors <= 0)
560: break;
561: if (nb_sectors >= (IO_BUF_SIZE / 512))
562: n = (IO_BUF_SIZE / 512);
563: else
564: n = nb_sectors;
1.1.1.4 root 565:
566: while (sector_num - bs_offset >= bs_sectors) {
567: bs_i ++;
568: assert (bs_i < bs_n);
569: bs_offset += bs_sectors;
570: bdrv_get_geometry(bs[bs_i], &bs_sectors);
571: /* printf("changing part: sector_num=%lld, bs_i=%d, "
572: "bs_offset=%lld, bs_sectors=%lld\n",
573: sector_num, bs_i, bs_offset, bs_sectors); */
574: }
575:
576: if (n > bs_offset + bs_sectors - sector_num)
577: n = bs_offset + bs_sectors - sector_num;
578:
1.1.1.5 ! root 579: /* If the output image is being created as a copy on write image,
! 580: assume that sectors which are unallocated in the input image
! 581: are present in both the output's and input's base images (no
! 582: need to copy them). */
! 583: if (out_baseimg) {
! 584: if (!bdrv_is_allocated(bs[bs_i], sector_num - bs_offset, n, &n1)) {
! 585: sector_num += n1;
! 586: continue;
! 587: }
! 588: /* The next 'n1' sectors are allocated in the input image. Copy
! 589: only those as they may be followed by unallocated sectors. */
! 590: n = n1;
! 591: }
! 592:
1.1.1.4 root 593: if (bdrv_read(bs[bs_i], sector_num - bs_offset, buf, n) < 0)
1.1 root 594: error("error while reading");
595: /* NOTE: at the same time we convert, we do not write zero
596: sectors to have a chance to compress the image. Ideally, we
597: should add a specific call to have the info to go faster */
598: buf1 = buf;
599: while (n > 0) {
1.1.1.5 ! root 600: /* If the output image is being created as a copy on write image,
! 601: copy all sectors even the ones containing only NUL bytes,
! 602: because they may differ from the sectors in the base image. */
! 603: if (out_baseimg || is_allocated_sectors(buf1, n, &n1)) {
1.1.1.4 root 604: if (bdrv_write(out_bs, sector_num, buf1, n1) < 0)
1.1 root 605: error("error while writing");
606: }
607: sector_num += n1;
608: n -= n1;
609: buf1 += n1 * 512;
610: }
611: }
612: }
613: bdrv_delete(out_bs);
1.1.1.4 root 614: for (bs_i = 0; bs_i < bs_n; bs_i++)
615: bdrv_delete(bs[bs_i]);
616: free(bs);
1.1 root 617: return 0;
618: }
619:
620: #ifdef _WIN32
621: static int64_t get_allocated_file_size(const char *filename)
622: {
1.1.1.2 root 623: typedef DWORD (WINAPI * get_compressed_t)(const char *filename, DWORD *high);
624: get_compressed_t get_compressed;
1.1 root 625: struct _stati64 st;
1.1.1.2 root 626:
627: /* WinNT support GetCompressedFileSize to determine allocate size */
628: get_compressed = (get_compressed_t) GetProcAddress(GetModuleHandle("kernel32"), "GetCompressedFileSizeA");
629: if (get_compressed) {
630: DWORD high, low;
631: low = get_compressed(filename, &high);
632: if (low != 0xFFFFFFFFlu || GetLastError() == NO_ERROR)
633: return (((int64_t) high) << 32) + low;
634: }
635:
1.1.1.4 root 636: if (_stati64(filename, &st) < 0)
1.1 root 637: return -1;
638: return st.st_size;
639: }
640: #else
641: static int64_t get_allocated_file_size(const char *filename)
642: {
643: struct stat st;
1.1.1.4 root 644: if (stat(filename, &st) < 0)
1.1 root 645: return -1;
646: return (int64_t)st.st_blocks * 512;
647: }
648: #endif
649:
1.1.1.3 root 650: static void dump_snapshots(BlockDriverState *bs)
651: {
652: QEMUSnapshotInfo *sn_tab, *sn;
653: int nb_sns, i;
654: char buf[256];
655:
656: nb_sns = bdrv_snapshot_list(bs, &sn_tab);
657: if (nb_sns <= 0)
658: return;
659: printf("Snapshot list:\n");
660: printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL));
661: for(i = 0; i < nb_sns; i++) {
662: sn = &sn_tab[i];
663: printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), sn));
664: }
665: qemu_free(sn_tab);
666: }
667:
1.1 root 668: static int img_info(int argc, char **argv)
669: {
670: int c;
671: const char *filename, *fmt;
672: BlockDriver *drv;
673: BlockDriverState *bs;
674: char fmt_name[128], size_buf[128], dsize_buf[128];
1.1.1.4 root 675: uint64_t total_sectors;
676: int64_t allocated_size;
1.1.1.3 root 677: char backing_filename[1024];
678: char backing_filename2[1024];
679: BlockDriverInfo bdi;
1.1 root 680:
681: fmt = NULL;
682: for(;;) {
683: c = getopt(argc, argv, "f:h");
684: if (c == -1)
685: break;
686: switch(c) {
687: case 'h':
688: help();
689: break;
690: case 'f':
691: fmt = optarg;
692: break;
693: }
694: }
1.1.1.4 root 695: if (optind >= argc)
1.1 root 696: help();
697: filename = argv[optind++];
698:
699: bs = bdrv_new("");
700: if (!bs)
701: error("Not enough memory");
702: if (fmt) {
703: drv = bdrv_find_format(fmt);
704: if (!drv)
705: error("Unknown file format '%s'", fmt);
706: } else {
707: drv = NULL;
708: }
1.1.1.5 ! root 709: if (bdrv_open2(bs, filename, BRDV_O_FLAGS, drv) < 0) {
1.1 root 710: error("Could not open '%s'", filename);
711: }
712: bdrv_get_format(bs, fmt_name, sizeof(fmt_name));
713: bdrv_get_geometry(bs, &total_sectors);
714: get_human_readable_size(size_buf, sizeof(size_buf), total_sectors * 512);
715: allocated_size = get_allocated_file_size(filename);
716: if (allocated_size < 0)
1.1.1.5 ! root 717: snprintf(dsize_buf, sizeof(dsize_buf), "unavailable");
1.1 root 718: else
1.1.1.4 root 719: get_human_readable_size(dsize_buf, sizeof(dsize_buf),
1.1 root 720: allocated_size);
721: printf("image: %s\n"
722: "file format: %s\n"
1.1.1.2 root 723: "virtual size: %s (%" PRId64 " bytes)\n"
1.1 root 724: "disk size: %s\n",
1.1.1.4 root 725: filename, fmt_name, size_buf,
1.1.1.2 root 726: (total_sectors * 512),
1.1 root 727: dsize_buf);
728: if (bdrv_is_encrypted(bs))
729: printf("encrypted: yes\n");
1.1.1.3 root 730: if (bdrv_get_info(bs, &bdi) >= 0) {
1.1.1.4 root 731: if (bdi.cluster_size != 0)
1.1.1.3 root 732: printf("cluster_size: %d\n", bdi.cluster_size);
1.1.1.5 ! root 733: if (bdi.highest_alloc)
! 734: printf("highest_alloc: %" PRId64 "\n", bdi.highest_alloc);
! 735: if (bdi.num_free_bytes)
! 736: printf("num_free_bytes: %" PRId64 "\n", bdi.num_free_bytes);
1.1.1.3 root 737: }
738: bdrv_get_backing_filename(bs, backing_filename, sizeof(backing_filename));
739: if (backing_filename[0] != '\0') {
740: path_combine(backing_filename2, sizeof(backing_filename2),
741: filename, backing_filename);
1.1.1.4 root 742: printf("backing file: %s (actual path: %s)\n",
1.1.1.3 root 743: backing_filename,
744: backing_filename2);
745: }
746: dump_snapshots(bs);
1.1 root 747: bdrv_delete(bs);
748: return 0;
749: }
750:
1.1.1.5 ! root 751: #define SNAPSHOT_LIST 1
! 752: #define SNAPSHOT_CREATE 2
! 753: #define SNAPSHOT_APPLY 3
! 754: #define SNAPSHOT_DELETE 4
! 755:
! 756: static void img_snapshot(int argc, char **argv)
! 757: {
! 758: BlockDriverState *bs;
! 759: QEMUSnapshotInfo sn;
! 760: char *filename, *snapshot_name = NULL;
! 761: int c, ret;
! 762: int action = 0;
! 763: qemu_timeval tv;
! 764:
! 765: /* Parse commandline parameters */
! 766: for(;;) {
! 767: c = getopt(argc, argv, "la:c:d:h");
! 768: if (c == -1)
! 769: break;
! 770: switch(c) {
! 771: case 'h':
! 772: help();
! 773: return;
! 774: case 'l':
! 775: if (action) {
! 776: help();
! 777: return;
! 778: }
! 779: action = SNAPSHOT_LIST;
! 780: break;
! 781: case 'a':
! 782: if (action) {
! 783: help();
! 784: return;
! 785: }
! 786: action = SNAPSHOT_APPLY;
! 787: snapshot_name = optarg;
! 788: break;
! 789: case 'c':
! 790: if (action) {
! 791: help();
! 792: return;
! 793: }
! 794: action = SNAPSHOT_CREATE;
! 795: snapshot_name = optarg;
! 796: break;
! 797: case 'd':
! 798: if (action) {
! 799: help();
! 800: return;
! 801: }
! 802: action = SNAPSHOT_DELETE;
! 803: snapshot_name = optarg;
! 804: break;
! 805: }
! 806: }
! 807:
! 808: if (optind >= argc)
! 809: help();
! 810: filename = argv[optind++];
! 811:
! 812: /* Open the image */
! 813: bs = bdrv_new("");
! 814: if (!bs)
! 815: error("Not enough memory");
! 816:
! 817: if (bdrv_open2(bs, filename, 0, NULL) < 0) {
! 818: error("Could not open '%s'", filename);
! 819: }
! 820:
! 821: /* Perform the requested action */
! 822: switch(action) {
! 823: case SNAPSHOT_LIST:
! 824: dump_snapshots(bs);
! 825: break;
! 826:
! 827: case SNAPSHOT_CREATE:
! 828: memset(&sn, 0, sizeof(sn));
! 829: pstrcpy(sn.name, sizeof(sn.name), snapshot_name);
! 830:
! 831: qemu_gettimeofday(&tv);
! 832: sn.date_sec = tv.tv_sec;
! 833: sn.date_nsec = tv.tv_usec * 1000;
! 834:
! 835: ret = bdrv_snapshot_create(bs, &sn);
! 836: if (ret)
! 837: error("Could not create snapshot '%s': %d (%s)",
! 838: snapshot_name, ret, strerror(-ret));
! 839: break;
! 840:
! 841: case SNAPSHOT_APPLY:
! 842: ret = bdrv_snapshot_goto(bs, snapshot_name);
! 843: if (ret)
! 844: error("Could not apply snapshot '%s': %d (%s)",
! 845: snapshot_name, ret, strerror(-ret));
! 846: break;
! 847:
! 848: case SNAPSHOT_DELETE:
! 849: ret = bdrv_snapshot_delete(bs, snapshot_name);
! 850: if (ret)
! 851: error("Could not delete snapshot '%s': %d (%s)",
! 852: snapshot_name, ret, strerror(-ret));
! 853: break;
! 854: }
! 855:
! 856: /* Cleanup */
! 857: bdrv_delete(bs);
! 858: }
! 859:
1.1 root 860: int main(int argc, char **argv)
861: {
862: const char *cmd;
863:
864: bdrv_init();
865: if (argc < 2)
866: help();
867: cmd = argv[1];
1.1.1.5 ! root 868: argc--; argv++;
1.1 root 869: if (!strcmp(cmd, "create")) {
870: img_create(argc, argv);
871: } else if (!strcmp(cmd, "commit")) {
872: img_commit(argc, argv);
873: } else if (!strcmp(cmd, "convert")) {
874: img_convert(argc, argv);
875: } else if (!strcmp(cmd, "info")) {
876: img_info(argc, argv);
1.1.1.5 ! root 877: } else if (!strcmp(cmd, "snapshot")) {
! 878: img_snapshot(argc, argv);
1.1 root 879: } else {
880: help();
881: }
882: return 0;
883: }
unix.superglobalmegacorp.com