]> git.proxmox.com Git - qemu.git/blob - qemu-img.c
964b28bcd42e9ba2113481e8044764ffdede3ad0
[qemu.git] / qemu-img.c
1 /*
2 * QEMU disk image utility
3 *
4 * Copyright (c) 2003-2008 Fabrice Bellard
5 *
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 */
24 #include "qemu-common.h"
25 #include "osdep.h"
26 #include "block_int.h"
27 #include <assert.h>
28
29 #ifdef _WIN32
30 #define WIN32_LEAN_AND_MEAN
31 #include <windows.h>
32 #endif
33
34 /* Default to cache=writeback as data integrity is not important for qemu-tcg. */
35 #define BRDV_O_FLAGS BDRV_O_CACHE_WB
36
37 static void __attribute__((noreturn)) error(const char *fmt, ...)
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
53 static void help(void)
54 {
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"
89 );
90 printf("\nSupported format:");
91 bdrv_iterate_format(format_print, NULL);
92 printf("\n");
93 exit(1);
94 }
95
96 #if defined(WIN32)
97 /* XXX: put correct support for win32 */
98 static int read_password(char *buf, int buf_size)
99 {
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;
110 }
111 buf[i] = '\0';
112 return 0;
113 }
114
115 #else
116
117 #include <termios.h>
118
119 static struct termios oldtty;
120
121 static void term_exit(void)
122 {
123 tcsetattr (0, TCSANOW, &oldtty);
124 }
125
126 static void term_init(void)
127 {
128 struct termios tty;
129
130 tcgetattr (0, &tty);
131 oldtty = tty;
132
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;
141
142 tcsetattr (0, TCSANOW, &tty);
143
144 atexit(term_exit);
145 }
146
147 static int read_password(char *buf, int buf_size)
148 {
149 uint8_t ch;
150 int i, ret;
151
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;
164 }
165 } else if (ret == 0) {
166 ret = -1;
167 break;
168 } else {
169 if (ch == '\r') {
170 ret = 0;
171 break;
172 }
173 if (i < (buf_size - 1))
174 buf[i++] = ch;
175 }
176 }
177 term_exit();
178 buf[i] = '\0';
179 printf("\n");
180 return ret;
181 }
182 #endif
183
184 static BlockDriverState *bdrv_new_open(const char *filename,
185 const char *fmt)
186 {
187 BlockDriverState *bs;
188 BlockDriver *drv;
189 char password[256];
190
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;
200 }
201 if (bdrv_open2(bs, filename, BRDV_O_FLAGS, drv) < 0) {
202 error("Could not open '%s'", filename);
203 }
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");
210 }
211 return bs;
212 }
213
214 static int img_create(int argc, char **argv)
215 {
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;
223
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;
245 }
246 }
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();
270 }
271 }
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);
284 }
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");
292 }
293 }
294 return 0;
295 }
296
297 static int img_commit(int argc, char **argv)
298 {
299 int c, ret;
300 const char *filename, *fmt;
301 BlockDriver *drv;
302 BlockDriverState *bs;
303
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;
316 }
317 }
318 if (optind >= argc)
319 help();
320 filename = argv[optind++];
321
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;
331 }
332 if (bdrv_open2(bs, filename, BRDV_O_FLAGS, drv) < 0) {
333 error("Could not open '%s'", filename);
334 }
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;
352 }
353
354 bdrv_delete(bs);
355 return 0;
356 }
357
358 static int is_not_zero(const uint8_t *sector, int len)
359 {
360 int i;
361 len >>= 2;
362 for(i = 0;i < len; i++) {
363 if (((uint32_t *)sector)[i] != 0)
364 return 1;
365 }
366 return 0;
367 }
368
369 /*
370 * Returns true iff the first sector pointed to by 'buf' contains at least
371 * a non-NUL byte.
372 *
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.
375 */
376 static int is_allocated_sectors(const uint8_t *buf, int n, int *pnum)
377 {
378 int v, i;
379
380 if (n <= 0) {
381 *pnum = 0;
382 return 0;
383 }
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;
389 }
390 *pnum = i;
391 return v;
392 }
393
394 #define IO_BUF_SIZE 65536
395
396 static int img_convert(int argc, char **argv)
397 {
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;
407
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;
438 }
439 }
440
441 bs_n = argc - optind - 1;
442 if (bs_n < 1) help();
443
444 out_filename = argv[argc - 1];
445
446 if (bs_n > 1 && out_baseimg)
447 error("-B makes no sense when concatenating multiple input images");
448
449 bs = calloc(bs_n, sizeof(BlockDriverState *));
450 if (!bs)
451 error("Out of memory");
452
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;
460 }
461
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");
473
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);
480 }
481 }
482
483 out_bs = bdrv_new_open(out_filename, out_fmt);
484
485 bs_i = 0;
486 bs_offset = 0;
487 bdrv_get_geometry(bs[0], &bs_sectors);
488
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;
501
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;
509
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); */
525 }
526 assert (bs_num < bs_sectors);
527
528 nlow = (remainder > bs_sectors - bs_num) ? bs_sectors - bs_num : remainder;
529
530 if (bdrv_read(bs[bs_i], bs_num, buf2, nlow) < 0)
531 error("error while reading");
532
533 buf2 += nlow * 512;
534 bs_num += nlow;
535
536 remainder -= nlow;
537 }
538 assert (remainder == 0);
539
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);
547 }
548 sector_num += n;
549 }
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;
562
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); */
571 }
572
573 if (n > bs_offset + bs_sectors - sector_num)
574 n = bs_offset + bs_sectors - sector_num;
575
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;
584 }
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;
588 }
589
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");
603 }
604 sector_num += n1;
605 n -= n1;
606 buf1 += n1 * 512;
607 }
608 }
609 }
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;
615 }
616
617 #ifdef _WIN32
618 static int64_t get_allocated_file_size(const char *filename)
619 {
620 typedef DWORD (WINAPI * get_compressed_t)(const char *filename, DWORD *high);
621 get_compressed_t get_compressed;
622 struct _stati64 st;
623
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;
631 }
632
633 if (_stati64(filename, &st) < 0)
634 return -1;
635 return st.st_size;
636 }
637 #else
638 static int64_t get_allocated_file_size(const char *filename)
639 {
640 struct stat st;
641 if (stat(filename, &st) < 0)
642 return -1;
643 return (int64_t)st.st_blocks * 512;
644 }
645 #endif
646
647 static void dump_snapshots(BlockDriverState *bs)
648 {
649 QEMUSnapshotInfo *sn_tab, *sn;
650 int nb_sns, i;
651 char buf[256];
652
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));
661 }
662 qemu_free(sn_tab);
663 }
664
665 static int img_info(int argc, char **argv)
666 {
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;
677
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;
690 }
691 }
692 if (optind >= argc)
693 help();
694 filename = argv[optind++];
695
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;
705 }
706 if (bdrv_open2(bs, filename, BRDV_O_FLAGS, drv) < 0) {
707 error("Could not open '%s'", filename);
708 }
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);
730 }
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);
738 }
739 dump_snapshots(bs);
740 bdrv_delete(bs);
741 return 0;
742 }
743
744 #define SNAPSHOT_LIST 1
745 #define SNAPSHOT_CREATE 2
746 #define SNAPSHOT_APPLY 3
747 #define SNAPSHOT_DELETE 4
748
749 static void img_snapshot(int argc, char **argv)
750 {
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;
758
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;
772 }
773 action = SNAPSHOT_LIST;
774 break;
775 case 'a':
776 if (action) {
777 help();
778 return;
779 }
780 action = SNAPSHOT_APPLY;
781 snapshot_name = optarg;
782 break;
783 case 'c':
784 if (action) {
785 help();
786 return;
787 }
788 action = SNAPSHOT_CREATE;
789 snapshot_name = optarg;
790 break;
791 case 'd':
792 if (action) {
793 help();
794 return;
795 }
796 action = SNAPSHOT_DELETE;
797 snapshot_name = optarg;
798 break;
799 }
800 }
801
802 if (optind >= argc)
803 help();
804 filename = argv[optind++];
805
806 /* Open the image */
807 bs = bdrv_new("");
808 if (!bs)
809 error("Not enough memory");
810
811 if (bdrv_open2(bs, filename, 0, NULL) < 0) {
812 error("Could not open '%s'", filename);
813 }
814
815 /* Perform the requested action */
816 switch(action) {
817 case SNAPSHOT_LIST:
818 dump_snapshots(bs);
819 break;
820
821 case SNAPSHOT_CREATE:
822 memset(&sn, 0, sizeof(sn));
823 pstrcpy(sn.name, sizeof(sn.name), snapshot_name);
824
825 qemu_gettimeofday(&tv);
826 sn.date_sec = tv.tv_sec;
827 sn.date_nsec = tv.tv_usec * 1000;
828
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;
834
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;
841
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;
848 }
849
850 /* Cleanup */
851 bdrv_delete(bs);
852 }
853
854 int main(int argc, char **argv)
855 {
856 const char *cmd;
857
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();
875 }
876 return 0;
877 }