add a simple fork test
[qemu/linux-user.git] / qemu-img.c
blob964b28bcd42e9ba2113481e8044764ffdede3ad0
1 /*
2 * QEMU disk image utility
4 * Copyright (c) 2003-2008 Fabrice Bellard
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:
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
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.
24 #include "qemu-common.h"
25 #include "osdep.h"
26 #include "block_int.h"
27 #include <assert.h>
29 #ifdef _WIN32
30 #define WIN32_LEAN_AND_MEAN
31 #include <windows.h>
32 #endif
34 /* Default to cache=writeback as data integrity is not important for qemu-tcg. */
35 #define BRDV_O_FLAGS BDRV_O_CACHE_WB
37 static void __attribute__((noreturn)) error(const char *fmt, ...)
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);
48 static void format_print(void *opaque, const char *name)
50 printf(" %s", name);
53 static void help(void)
55 printf("qemu-img version " QEMU_VERSION ", Copyright (c) 2004-2008 Fabrice Bellard\n"
56 "usage: qemu-img command [command options]\n"
57 "QEMU disk image utility\n"
58 "\n"
59 "Command syntax:\n"
60 " create [-e] [-6] [-b base_image] [-f fmt] filename [size]\n"
61 " commit [-f fmt] filename\n"
62 " convert [-c] [-e] [-6] [-f fmt] [-O output_fmt] [-B output_base_image] filename [filename2 [...]] output_filename\n"
63 " info [-f fmt] filename\n"
64 " snapshot [-l|-a snapshot|-c snapshot|-d snapshot] filename\n"
65 "\n"
66 "Command parameters:\n"
67 " 'filename' is a disk image filename\n"
68 " 'base_image' is the read-only disk image which is used as base for a copy on\n"
69 " write image; the copy on write image only stores the modified data\n"
70 " 'output_base_image' forces the output image to be created as a copy on write\n"
71 " image of the specified base image; 'output_base_image' should have the same\n"
72 " content as the input's base image, however the path, image format, etc may\n"
73 " differ\n"
74 " 'fmt' is the disk image format. It is guessed automatically in most cases\n"
75 " 'size' is the disk image size in kilobytes. Optional suffixes 'M' (megabyte)\n"
76 " and 'G' (gigabyte) are supported\n"
77 " 'output_filename' is the destination disk image filename\n"
78 " 'output_fmt' is the destination format\n"
79 " '-c' indicates that target image must be compressed (qcow format only)\n"
80 " '-e' indicates that the target image must be encrypted (qcow format only)\n"
81 " '-6' indicates that the target image must use compatibility level 6 (vmdk format only)\n"
82 "\n"
83 " Parameters to snapshot subcommand:\n"
84 " 'snapshot' is the name of the snapshot to create, apply or delete\n"
85 " '-a' applies a snapshot (revert disk to saved state)\n"
86 " '-c' creates a snapshot\n"
87 " '-d' deletes a snapshot\n"
88 " '-l' lists all snapshots in the given image\n"
90 printf("\nSupported format:");
91 bdrv_iterate_format(format_print, NULL);
92 printf("\n");
93 exit(1);
96 #if defined(WIN32)
97 /* XXX: put correct support for win32 */
98 static int read_password(char *buf, int buf_size)
100 int c, i;
101 printf("Password: ");
102 fflush(stdout);
103 i = 0;
104 for(;;) {
105 c = getchar();
106 if (c == '\n')
107 break;
108 if (i < (buf_size - 1))
109 buf[i++] = c;
111 buf[i] = '\0';
112 return 0;
115 #else
117 #include <termios.h>
119 static struct termios oldtty;
121 static void term_exit(void)
123 tcsetattr (0, TCSANOW, &oldtty);
126 static void term_init(void)
128 struct termios tty;
130 tcgetattr (0, &tty);
131 oldtty = tty;
133 tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
134 |INLCR|IGNCR|ICRNL|IXON);
135 tty.c_oflag |= OPOST;
136 tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN);
137 tty.c_cflag &= ~(CSIZE|PARENB);
138 tty.c_cflag |= CS8;
139 tty.c_cc[VMIN] = 1;
140 tty.c_cc[VTIME] = 0;
142 tcsetattr (0, TCSANOW, &tty);
144 atexit(term_exit);
147 static int read_password(char *buf, int buf_size)
149 uint8_t ch;
150 int i, ret;
152 printf("password: ");
153 fflush(stdout);
154 term_init();
155 i = 0;
156 for(;;) {
157 ret = read(0, &ch, 1);
158 if (ret == -1) {
159 if (errno == EAGAIN || errno == EINTR) {
160 continue;
161 } else {
162 ret = -1;
163 break;
165 } else if (ret == 0) {
166 ret = -1;
167 break;
168 } else {
169 if (ch == '\r') {
170 ret = 0;
171 break;
173 if (i < (buf_size - 1))
174 buf[i++] = ch;
177 term_exit();
178 buf[i] = '\0';
179 printf("\n");
180 return ret;
182 #endif
184 static BlockDriverState *bdrv_new_open(const char *filename,
185 const char *fmt)
187 BlockDriverState *bs;
188 BlockDriver *drv;
189 char password[256];
191 bs = bdrv_new("");
192 if (!bs)
193 error("Not enough memory");
194 if (fmt) {
195 drv = bdrv_find_format(fmt);
196 if (!drv)
197 error("Unknown file format '%s'", fmt);
198 } else {
199 drv = NULL;
201 if (bdrv_open2(bs, filename, BRDV_O_FLAGS, drv) < 0) {
202 error("Could not open '%s'", filename);
204 if (bdrv_is_encrypted(bs)) {
205 printf("Disk image '%s' is encrypted.\n", filename);
206 if (read_password(password, sizeof(password)) < 0)
207 error("No password given");
208 if (bdrv_set_key(bs, password) < 0)
209 error("invalid password");
211 return bs;
214 static int img_create(int argc, char **argv)
216 int c, ret, flags;
217 const char *fmt = "raw";
218 const char *filename;
219 const char *base_filename = NULL;
220 uint64_t size;
221 const char *p;
222 BlockDriver *drv;
224 flags = 0;
225 for(;;) {
226 c = getopt(argc, argv, "b:f:he6");
227 if (c == -1)
228 break;
229 switch(c) {
230 case 'h':
231 help();
232 break;
233 case 'b':
234 base_filename = optarg;
235 break;
236 case 'f':
237 fmt = optarg;
238 break;
239 case 'e':
240 flags |= BLOCK_FLAG_ENCRYPT;
241 break;
242 case '6':
243 flags |= BLOCK_FLAG_COMPAT6;
244 break;
247 if (optind >= argc)
248 help();
249 filename = argv[optind++];
250 size = 0;
251 if (base_filename) {
252 BlockDriverState *bs;
253 bs = bdrv_new_open(base_filename, NULL);
254 bdrv_get_geometry(bs, &size);
255 size *= 512;
256 bdrv_delete(bs);
257 } else {
258 if (optind >= argc)
259 help();
260 p = argv[optind];
261 size = strtoul(p, (char **)&p, 0);
262 if (*p == 'M') {
263 size *= 1024 * 1024;
264 } else if (*p == 'G') {
265 size *= 1024 * 1024 * 1024;
266 } else if (*p == 'k' || *p == 'K' || *p == '\0') {
267 size *= 1024;
268 } else {
269 help();
272 drv = bdrv_find_format(fmt);
273 if (!drv)
274 error("Unknown file format '%s'", fmt);
275 printf("Formatting '%s', fmt=%s",
276 filename, fmt);
277 if (flags & BLOCK_FLAG_ENCRYPT)
278 printf(", encrypted");
279 if (flags & BLOCK_FLAG_COMPAT6)
280 printf(", compatibility level=6");
281 if (base_filename) {
282 printf(", backing_file=%s",
283 base_filename);
285 printf(", size=%" PRIu64 " kB\n", size / 1024);
286 ret = bdrv_create(drv, filename, size / 512, base_filename, flags);
287 if (ret < 0) {
288 if (ret == -ENOTSUP) {
289 error("Formatting or formatting option not supported for file format '%s'", fmt);
290 } else {
291 error("Error while formatting");
294 return 0;
297 static int img_commit(int argc, char **argv)
299 int c, ret;
300 const char *filename, *fmt;
301 BlockDriver *drv;
302 BlockDriverState *bs;
304 fmt = NULL;
305 for(;;) {
306 c = getopt(argc, argv, "f:h");
307 if (c == -1)
308 break;
309 switch(c) {
310 case 'h':
311 help();
312 break;
313 case 'f':
314 fmt = optarg;
315 break;
318 if (optind >= argc)
319 help();
320 filename = argv[optind++];
322 bs = bdrv_new("");
323 if (!bs)
324 error("Not enough memory");
325 if (fmt) {
326 drv = bdrv_find_format(fmt);
327 if (!drv)
328 error("Unknown file format '%s'", fmt);
329 } else {
330 drv = NULL;
332 if (bdrv_open2(bs, filename, BRDV_O_FLAGS, drv) < 0) {
333 error("Could not open '%s'", filename);
335 ret = bdrv_commit(bs);
336 switch(ret) {
337 case 0:
338 printf("Image committed.\n");
339 break;
340 case -ENOENT:
341 error("No disk inserted");
342 break;
343 case -EACCES:
344 error("Image is read-only");
345 break;
346 case -ENOTSUP:
347 error("Image is already committed");
348 break;
349 default:
350 error("Error while committing image");
351 break;
354 bdrv_delete(bs);
355 return 0;
358 static int is_not_zero(const uint8_t *sector, int len)
360 int i;
361 len >>= 2;
362 for(i = 0;i < len; i++) {
363 if (((uint32_t *)sector)[i] != 0)
364 return 1;
366 return 0;
370 * Returns true iff the first sector pointed to by 'buf' contains at least
371 * a non-NUL byte.
373 * 'pnum' is set to the number of sectors (including and immediately following
374 * the first one) that are known to be in the same allocated/unallocated state.
376 static int is_allocated_sectors(const uint8_t *buf, int n, int *pnum)
378 int v, i;
380 if (n <= 0) {
381 *pnum = 0;
382 return 0;
384 v = is_not_zero(buf, 512);
385 for(i = 1; i < n; i++) {
386 buf += 512;
387 if (v != is_not_zero(buf, 512))
388 break;
390 *pnum = i;
391 return v;
394 #define IO_BUF_SIZE 65536
396 static int img_convert(int argc, char **argv)
398 int c, ret, n, n1, bs_n, bs_i, flags, cluster_size, cluster_sectors;
399 const char *fmt, *out_fmt, *out_baseimg, *out_filename;
400 BlockDriver *drv;
401 BlockDriverState **bs, *out_bs;
402 int64_t total_sectors, nb_sectors, sector_num, bs_offset;
403 uint64_t bs_sectors;
404 uint8_t buf[IO_BUF_SIZE];
405 const uint8_t *buf1;
406 BlockDriverInfo bdi;
408 fmt = NULL;
409 out_fmt = "raw";
410 out_baseimg = NULL;
411 flags = 0;
412 for(;;) {
413 c = getopt(argc, argv, "f:O:B:hce6");
414 if (c == -1)
415 break;
416 switch(c) {
417 case 'h':
418 help();
419 break;
420 case 'f':
421 fmt = optarg;
422 break;
423 case 'O':
424 out_fmt = optarg;
425 break;
426 case 'B':
427 out_baseimg = optarg;
428 break;
429 case 'c':
430 flags |= BLOCK_FLAG_COMPRESS;
431 break;
432 case 'e':
433 flags |= BLOCK_FLAG_ENCRYPT;
434 break;
435 case '6':
436 flags |= BLOCK_FLAG_COMPAT6;
437 break;
441 bs_n = argc - optind - 1;
442 if (bs_n < 1) help();
444 out_filename = argv[argc - 1];
446 if (bs_n > 1 && out_baseimg)
447 error("-B makes no sense when concatenating multiple input images");
449 bs = calloc(bs_n, sizeof(BlockDriverState *));
450 if (!bs)
451 error("Out of memory");
453 total_sectors = 0;
454 for (bs_i = 0; bs_i < bs_n; bs_i++) {
455 bs[bs_i] = bdrv_new_open(argv[optind + bs_i], fmt);
456 if (!bs[bs_i])
457 error("Could not open '%s'", argv[optind + bs_i]);
458 bdrv_get_geometry(bs[bs_i], &bs_sectors);
459 total_sectors += bs_sectors;
462 drv = bdrv_find_format(out_fmt);
463 if (!drv)
464 error("Unknown file format '%s'", out_fmt);
465 if (flags & BLOCK_FLAG_COMPRESS && drv != &bdrv_qcow && drv != &bdrv_qcow2)
466 error("Compression not supported for this file format");
467 if (flags & BLOCK_FLAG_ENCRYPT && drv != &bdrv_qcow && drv != &bdrv_qcow2)
468 error("Encryption not supported for this file format");
469 if (flags & BLOCK_FLAG_COMPAT6 && drv != &bdrv_vmdk)
470 error("Alternative compatibility level not supported for this file format");
471 if (flags & BLOCK_FLAG_ENCRYPT && flags & BLOCK_FLAG_COMPRESS)
472 error("Compression and encryption not supported at the same time");
474 ret = bdrv_create(drv, out_filename, total_sectors, out_baseimg, flags);
475 if (ret < 0) {
476 if (ret == -ENOTSUP) {
477 error("Formatting not supported for file format '%s'", fmt);
478 } else {
479 error("Error while formatting '%s'", out_filename);
483 out_bs = bdrv_new_open(out_filename, out_fmt);
485 bs_i = 0;
486 bs_offset = 0;
487 bdrv_get_geometry(bs[0], &bs_sectors);
489 if (flags & BLOCK_FLAG_COMPRESS) {
490 if (bdrv_get_info(out_bs, &bdi) < 0)
491 error("could not get block driver info");
492 cluster_size = bdi.cluster_size;
493 if (cluster_size <= 0 || cluster_size > IO_BUF_SIZE)
494 error("invalid cluster size");
495 cluster_sectors = cluster_size >> 9;
496 sector_num = 0;
497 for(;;) {
498 int64_t bs_num;
499 int remainder;
500 uint8_t *buf2;
502 nb_sectors = total_sectors - sector_num;
503 if (nb_sectors <= 0)
504 break;
505 if (nb_sectors >= cluster_sectors)
506 n = cluster_sectors;
507 else
508 n = nb_sectors;
510 bs_num = sector_num - bs_offset;
511 assert (bs_num >= 0);
512 remainder = n;
513 buf2 = buf;
514 while (remainder > 0) {
515 int nlow;
516 while (bs_num == bs_sectors) {
517 bs_i++;
518 assert (bs_i < bs_n);
519 bs_offset += bs_sectors;
520 bdrv_get_geometry(bs[bs_i], &bs_sectors);
521 bs_num = 0;
522 /* printf("changing part: sector_num=%lld, "
523 "bs_i=%d, bs_offset=%lld, bs_sectors=%lld\n",
524 sector_num, bs_i, bs_offset, bs_sectors); */
526 assert (bs_num < bs_sectors);
528 nlow = (remainder > bs_sectors - bs_num) ? bs_sectors - bs_num : remainder;
530 if (bdrv_read(bs[bs_i], bs_num, buf2, nlow) < 0)
531 error("error while reading");
533 buf2 += nlow * 512;
534 bs_num += nlow;
536 remainder -= nlow;
538 assert (remainder == 0);
540 if (n < cluster_sectors)
541 memset(buf + n * 512, 0, cluster_size - n * 512);
542 if (is_not_zero(buf, cluster_size)) {
543 if (bdrv_write_compressed(out_bs, sector_num, buf,
544 cluster_sectors) != 0)
545 error("error while compressing sector %" PRId64,
546 sector_num);
548 sector_num += n;
550 /* signal EOF to align */
551 bdrv_write_compressed(out_bs, 0, NULL, 0);
552 } else {
553 sector_num = 0; // total number of sectors converted so far
554 for(;;) {
555 nb_sectors = total_sectors - sector_num;
556 if (nb_sectors <= 0)
557 break;
558 if (nb_sectors >= (IO_BUF_SIZE / 512))
559 n = (IO_BUF_SIZE / 512);
560 else
561 n = nb_sectors;
563 while (sector_num - bs_offset >= bs_sectors) {
564 bs_i ++;
565 assert (bs_i < bs_n);
566 bs_offset += bs_sectors;
567 bdrv_get_geometry(bs[bs_i], &bs_sectors);
568 /* printf("changing part: sector_num=%lld, bs_i=%d, "
569 "bs_offset=%lld, bs_sectors=%lld\n",
570 sector_num, bs_i, bs_offset, bs_sectors); */
573 if (n > bs_offset + bs_sectors - sector_num)
574 n = bs_offset + bs_sectors - sector_num;
576 /* If the output image is being created as a copy on write image,
577 assume that sectors which are unallocated in the input image
578 are present in both the output's and input's base images (no
579 need to copy them). */
580 if (out_baseimg) {
581 if (!bdrv_is_allocated(bs[bs_i], sector_num - bs_offset, n, &n1)) {
582 sector_num += n1;
583 continue;
585 /* The next 'n1' sectors are allocated in the input image. Copy
586 only those as they may be followed by unallocated sectors. */
587 n = n1;
590 if (bdrv_read(bs[bs_i], sector_num - bs_offset, buf, n) < 0)
591 error("error while reading");
592 /* NOTE: at the same time we convert, we do not write zero
593 sectors to have a chance to compress the image. Ideally, we
594 should add a specific call to have the info to go faster */
595 buf1 = buf;
596 while (n > 0) {
597 /* If the output image is being created as a copy on write image,
598 copy all sectors even the ones containing only NUL bytes,
599 because they may differ from the sectors in the base image. */
600 if (out_baseimg || is_allocated_sectors(buf1, n, &n1)) {
601 if (bdrv_write(out_bs, sector_num, buf1, n1) < 0)
602 error("error while writing");
604 sector_num += n1;
605 n -= n1;
606 buf1 += n1 * 512;
610 bdrv_delete(out_bs);
611 for (bs_i = 0; bs_i < bs_n; bs_i++)
612 bdrv_delete(bs[bs_i]);
613 free(bs);
614 return 0;
617 #ifdef _WIN32
618 static int64_t get_allocated_file_size(const char *filename)
620 typedef DWORD (WINAPI * get_compressed_t)(const char *filename, DWORD *high);
621 get_compressed_t get_compressed;
622 struct _stati64 st;
624 /* WinNT support GetCompressedFileSize to determine allocate size */
625 get_compressed = (get_compressed_t) GetProcAddress(GetModuleHandle("kernel32"), "GetCompressedFileSizeA");
626 if (get_compressed) {
627 DWORD high, low;
628 low = get_compressed(filename, &high);
629 if (low != 0xFFFFFFFFlu || GetLastError() == NO_ERROR)
630 return (((int64_t) high) << 32) + low;
633 if (_stati64(filename, &st) < 0)
634 return -1;
635 return st.st_size;
637 #else
638 static int64_t get_allocated_file_size(const char *filename)
640 struct stat st;
641 if (stat(filename, &st) < 0)
642 return -1;
643 return (int64_t)st.st_blocks * 512;
645 #endif
647 static void dump_snapshots(BlockDriverState *bs)
649 QEMUSnapshotInfo *sn_tab, *sn;
650 int nb_sns, i;
651 char buf[256];
653 nb_sns = bdrv_snapshot_list(bs, &sn_tab);
654 if (nb_sns <= 0)
655 return;
656 printf("Snapshot list:\n");
657 printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL));
658 for(i = 0; i < nb_sns; i++) {
659 sn = &sn_tab[i];
660 printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), sn));
662 qemu_free(sn_tab);
665 static int img_info(int argc, char **argv)
667 int c;
668 const char *filename, *fmt;
669 BlockDriver *drv;
670 BlockDriverState *bs;
671 char fmt_name[128], size_buf[128], dsize_buf[128];
672 uint64_t total_sectors;
673 int64_t allocated_size;
674 char backing_filename[1024];
675 char backing_filename2[1024];
676 BlockDriverInfo bdi;
678 fmt = NULL;
679 for(;;) {
680 c = getopt(argc, argv, "f:h");
681 if (c == -1)
682 break;
683 switch(c) {
684 case 'h':
685 help();
686 break;
687 case 'f':
688 fmt = optarg;
689 break;
692 if (optind >= argc)
693 help();
694 filename = argv[optind++];
696 bs = bdrv_new("");
697 if (!bs)
698 error("Not enough memory");
699 if (fmt) {
700 drv = bdrv_find_format(fmt);
701 if (!drv)
702 error("Unknown file format '%s'", fmt);
703 } else {
704 drv = NULL;
706 if (bdrv_open2(bs, filename, BRDV_O_FLAGS, drv) < 0) {
707 error("Could not open '%s'", filename);
709 bdrv_get_format(bs, fmt_name, sizeof(fmt_name));
710 bdrv_get_geometry(bs, &total_sectors);
711 get_human_readable_size(size_buf, sizeof(size_buf), total_sectors * 512);
712 allocated_size = get_allocated_file_size(filename);
713 if (allocated_size < 0)
714 snprintf(dsize_buf, sizeof(dsize_buf), "unavailable");
715 else
716 get_human_readable_size(dsize_buf, sizeof(dsize_buf),
717 allocated_size);
718 printf("image: %s\n"
719 "file format: %s\n"
720 "virtual size: %s (%" PRId64 " bytes)\n"
721 "disk size: %s\n",
722 filename, fmt_name, size_buf,
723 (total_sectors * 512),
724 dsize_buf);
725 if (bdrv_is_encrypted(bs))
726 printf("encrypted: yes\n");
727 if (bdrv_get_info(bs, &bdi) >= 0) {
728 if (bdi.cluster_size != 0)
729 printf("cluster_size: %d\n", bdi.cluster_size);
731 bdrv_get_backing_filename(bs, backing_filename, sizeof(backing_filename));
732 if (backing_filename[0] != '\0') {
733 path_combine(backing_filename2, sizeof(backing_filename2),
734 filename, backing_filename);
735 printf("backing file: %s (actual path: %s)\n",
736 backing_filename,
737 backing_filename2);
739 dump_snapshots(bs);
740 bdrv_delete(bs);
741 return 0;
744 #define SNAPSHOT_LIST 1
745 #define SNAPSHOT_CREATE 2
746 #define SNAPSHOT_APPLY 3
747 #define SNAPSHOT_DELETE 4
749 static void img_snapshot(int argc, char **argv)
751 BlockDriverState *bs;
752 QEMUSnapshotInfo sn;
753 char *filename, *snapshot_name = NULL;
754 char c;
755 int ret;
756 int action = 0;
757 qemu_timeval tv;
759 /* Parse commandline parameters */
760 for(;;) {
761 c = getopt(argc, argv, "la:c:d:h");
762 if (c == -1)
763 break;
764 switch(c) {
765 case 'h':
766 help();
767 return;
768 case 'l':
769 if (action) {
770 help();
771 return;
773 action = SNAPSHOT_LIST;
774 break;
775 case 'a':
776 if (action) {
777 help();
778 return;
780 action = SNAPSHOT_APPLY;
781 snapshot_name = optarg;
782 break;
783 case 'c':
784 if (action) {
785 help();
786 return;
788 action = SNAPSHOT_CREATE;
789 snapshot_name = optarg;
790 break;
791 case 'd':
792 if (action) {
793 help();
794 return;
796 action = SNAPSHOT_DELETE;
797 snapshot_name = optarg;
798 break;
802 if (optind >= argc)
803 help();
804 filename = argv[optind++];
806 /* Open the image */
807 bs = bdrv_new("");
808 if (!bs)
809 error("Not enough memory");
811 if (bdrv_open2(bs, filename, 0, NULL) < 0) {
812 error("Could not open '%s'", filename);
815 /* Perform the requested action */
816 switch(action) {
817 case SNAPSHOT_LIST:
818 dump_snapshots(bs);
819 break;
821 case SNAPSHOT_CREATE:
822 memset(&sn, 0, sizeof(sn));
823 pstrcpy(sn.name, sizeof(sn.name), snapshot_name);
825 qemu_gettimeofday(&tv);
826 sn.date_sec = tv.tv_sec;
827 sn.date_nsec = tv.tv_usec * 1000;
829 ret = bdrv_snapshot_create(bs, &sn);
830 if (ret)
831 error("Could not create snapshot '%s': %d (%s)",
832 snapshot_name, ret, strerror(-ret));
833 break;
835 case SNAPSHOT_APPLY:
836 ret = bdrv_snapshot_goto(bs, snapshot_name);
837 if (ret)
838 error("Could not apply snapshot '%s': %d (%s)",
839 snapshot_name, ret, strerror(-ret));
840 break;
842 case SNAPSHOT_DELETE:
843 ret = bdrv_snapshot_delete(bs, snapshot_name);
844 if (ret)
845 error("Could not delete snapshot '%s': %d (%s)",
846 snapshot_name, ret, strerror(-ret));
847 break;
850 /* Cleanup */
851 bdrv_delete(bs);
854 int main(int argc, char **argv)
856 const char *cmd;
858 bdrv_init();
859 if (argc < 2)
860 help();
861 cmd = argv[1];
862 optind++;
863 if (!strcmp(cmd, "create")) {
864 img_create(argc, argv);
865 } else if (!strcmp(cmd, "commit")) {
866 img_commit(argc, argv);
867 } else if (!strcmp(cmd, "convert")) {
868 img_convert(argc, argv);
869 } else if (!strcmp(cmd, "info")) {
870 img_info(argc, argv);
871 } else if (!strcmp(cmd, "snapshot")) {
872 img_snapshot(argc, argv);
873 } else {
874 help();
876 return 0;