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
24 #include "qemu-common.h"
25 #include "qemu-option.h"
27 #include "block_int.h"
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 QEMU_NORETURN
error(const char *fmt
, ...)
41 fprintf(stderr
, "qemu-img: ");
42 vfprintf(stderr
, fmt
, ap
);
43 fprintf(stderr
, "\n");
48 static void format_print(void *opaque
, const char *name
)
53 /* Please keep in synch with qemu-img.texi */
54 static void help(void)
56 printf("qemu-img version " QEMU_VERSION
", Copyright (c) 2004-2008 Fabrice Bellard\n"
57 "usage: qemu-img command [command options]\n"
58 "QEMU disk image utility\n"
61 " check [-f fmt] filename\n"
62 " create [-e] [-6] [-F fmt] [-b base_image] [-f fmt] filename [size]\n"
63 " commit [-f fmt] filename\n"
64 " convert [-c] [-e] [-6] [-f fmt] [-O output_fmt] [-B output_base_image] filename [filename2 [...]] output_filename\n"
65 " info [-f fmt] filename\n"
66 " snapshot [-l | -a snapshot | -c snapshot | -d snapshot] filename\n"
68 "Command parameters:\n"
69 " 'filename' is a disk image filename\n"
70 " 'base_image' is the read-only disk image which is used as base for a copy on\n"
71 " write image; the copy on write image only stores the modified data\n"
72 " 'output_base_image' forces the output image to be created as a copy on write\n"
73 " image of the specified base image; 'output_base_image' should have the same\n"
74 " content as the input's base image, however the path, image format, etc may\n"
76 " 'fmt' is the disk image format. It is guessed automatically in most cases\n"
77 " 'size' is the disk image size in kilobytes. Optional suffixes\n"
78 " 'M' (megabyte, 1024 * 1024) and 'G' (gigabyte, 1024 * 1024 * 1024) are\n"
79 " supported any 'k' or 'K' is ignored\n"
80 " 'output_filename' is the destination disk image filename\n"
81 " 'output_fmt' is the destination format\n"
82 " '-c' indicates that target image must be compressed (qcow format only)\n"
83 " '-e' indicates that the target image must be encrypted (qcow format only)\n"
84 " '-6' indicates that the target image must use compatibility level 6 (vmdk format only)\n"
85 " '-h' with or without a command shows this help and lists the supported formats\n"
87 "Parameters to snapshot subcommand:\n"
88 " 'snapshot' is the name of the snapshot to create, apply or delete\n"
89 " '-a' applies a snapshot (revert disk to saved state)\n"
90 " '-c' creates a snapshot\n"
91 " '-d' deletes a snapshot\n"
92 " '-l' lists all snapshots in the given image\n"
94 printf("\nSupported formats:");
95 bdrv_iterate_format(format_print
, NULL
);
101 /* XXX: put correct support for win32 */
102 static int read_password(char *buf
, int buf_size
)
105 printf("Password: ");
112 if (i
< (buf_size
- 1))
123 static struct termios oldtty
;
125 static void term_exit(void)
127 tcsetattr (0, TCSANOW
, &oldtty
);
130 static void term_init(void)
137 tty
.c_iflag
&= ~(IGNBRK
|BRKINT
|PARMRK
|ISTRIP
138 |INLCR
|IGNCR
|ICRNL
|IXON
);
139 tty
.c_oflag
|= OPOST
;
140 tty
.c_lflag
&= ~(ECHO
|ECHONL
|ICANON
|IEXTEN
);
141 tty
.c_cflag
&= ~(CSIZE
|PARENB
);
146 tcsetattr (0, TCSANOW
, &tty
);
151 static int read_password(char *buf
, int buf_size
)
156 printf("password: ");
161 ret
= read(0, &ch
, 1);
163 if (errno
== EAGAIN
|| errno
== EINTR
) {
169 } else if (ret
== 0) {
177 if (i
< (buf_size
- 1))
188 static BlockDriverState
*bdrv_new_open(const char *filename
,
191 BlockDriverState
*bs
;
197 error("Not enough memory");
199 drv
= bdrv_find_format(fmt
);
201 error("Unknown file format '%s'", fmt
);
205 if (bdrv_open2(bs
, filename
, BRDV_O_FLAGS
, drv
) < 0) {
206 error("Could not open '%s'", filename
);
208 if (bdrv_is_encrypted(bs
)) {
209 printf("Disk image '%s' is encrypted.\n", filename
);
210 if (read_password(password
, sizeof(password
)) < 0)
211 error("No password given");
212 if (bdrv_set_key(bs
, password
) < 0)
213 error("invalid password");
218 static void add_old_style_options(const char *fmt
, QEMUOptionParameter
*list
,
219 int flags
, const char *base_filename
, const char *base_fmt
)
221 if (flags
& BLOCK_FLAG_ENCRYPT
) {
222 if (set_option_parameter(list
, BLOCK_OPT_ENCRYPT
, "on")) {
223 error("Encryption not supported for file format '%s'", fmt
);
226 if (flags
& BLOCK_FLAG_COMPAT6
) {
227 if (set_option_parameter(list
, BLOCK_OPT_COMPAT6
, "on")) {
228 error("VMDK version 6 not supported for file format '%s'", fmt
);
233 if (set_option_parameter(list
, BLOCK_OPT_BACKING_FILE
, base_filename
)) {
234 error("Backing file not supported for file format '%s'", fmt
);
238 if (set_option_parameter(list
, BLOCK_OPT_BACKING_FMT
, base_fmt
)) {
239 error("Backing file format not supported for file format '%s'", fmt
);
244 static int img_create(int argc
, char **argv
)
247 const char *fmt
= "raw";
248 const char *base_fmt
= NULL
;
249 const char *filename
;
250 const char *base_filename
= NULL
;
252 QEMUOptionParameter
*param
= NULL
;
253 char *options
= NULL
;
257 c
= getopt(argc
, argv
, "F:b:f:he6o:");
268 base_filename
= optarg
;
274 flags
|= BLOCK_FLAG_ENCRYPT
;
277 flags
|= BLOCK_FLAG_COMPAT6
;
285 /* Find driver and parse its options */
286 drv
= bdrv_find_format(fmt
);
288 error("Unknown file format '%s'", fmt
);
290 if (options
&& !strcmp(options
, "?")) {
291 print_option_help(drv
->create_options
);
296 param
= parse_option_parameters(options
, drv
->create_options
, param
);
298 error("Invalid options for file format '%s'.", fmt
);
301 param
= parse_option_parameters("", drv
->create_options
, param
);
304 /* Get the filename */
307 filename
= argv
[optind
++];
309 /* Add size to parameters */
311 set_option_parameter(param
, BLOCK_OPT_SIZE
, argv
[optind
++]);
314 /* Add old-style options to parameters */
315 add_old_style_options(fmt
, param
, flags
, base_filename
, base_fmt
);
317 // The size for the image must always be specified, with one exception:
318 // If we are using a backing file, we can obtain the size from there
319 if (get_option_parameter(param
, BLOCK_OPT_SIZE
)->value
.n
== 0) {
321 QEMUOptionParameter
*backing_file
=
322 get_option_parameter(param
, BLOCK_OPT_BACKING_FILE
);
323 QEMUOptionParameter
*backing_fmt
=
324 get_option_parameter(param
, BLOCK_OPT_BACKING_FMT
);
326 if (backing_file
&& backing_file
->value
.s
) {
327 BlockDriverState
*bs
;
329 const char *fmt
= NULL
;
332 if (backing_fmt
&& backing_fmt
->value
.s
) {
333 if (bdrv_find_format(backing_fmt
->value
.s
)) {
334 fmt
= backing_fmt
->value
.s
;
336 error("Unknown backing file format '%s'",
337 backing_fmt
->value
.s
);
341 bs
= bdrv_new_open(backing_file
->value
.s
, fmt
);
342 bdrv_get_geometry(bs
, &size
);
346 snprintf(buf
, sizeof(buf
), "%" PRId64
, size
);
347 set_option_parameter(param
, BLOCK_OPT_SIZE
, buf
);
349 error("Image creation needs a size parameter");
353 printf("Formatting '%s', fmt=%s ", filename
, fmt
);
354 print_option_parameters(param
);
357 ret
= bdrv_create(drv
, filename
, param
);
358 free_option_parameters(param
);
361 if (ret
== -ENOTSUP
) {
362 error("Formatting or formatting option not supported for file format '%s'", fmt
);
363 } else if (ret
== -EFBIG
) {
364 error("The image size is too large for file format '%s'", fmt
);
366 error("Error while formatting");
372 static int img_check(int argc
, char **argv
)
375 const char *filename
, *fmt
;
377 BlockDriverState
*bs
;
381 c
= getopt(argc
, argv
, "f:h");
395 filename
= argv
[optind
++];
399 error("Not enough memory");
401 drv
= bdrv_find_format(fmt
);
403 error("Unknown file format '%s'", fmt
);
407 if (bdrv_open2(bs
, filename
, BRDV_O_FLAGS
, drv
) < 0) {
408 error("Could not open '%s'", filename
);
410 ret
= bdrv_check(bs
);
413 printf("No errors were found on the image.\n");
416 error("This image format does not support checks");
420 error("An error occurred during the check");
422 printf("%d errors were found on the image.\n", ret
);
431 static int img_commit(int argc
, char **argv
)
434 const char *filename
, *fmt
;
436 BlockDriverState
*bs
;
440 c
= getopt(argc
, argv
, "f:h");
454 filename
= argv
[optind
++];
458 error("Not enough memory");
460 drv
= bdrv_find_format(fmt
);
462 error("Unknown file format '%s'", fmt
);
466 if (bdrv_open2(bs
, filename
, BRDV_O_FLAGS
, drv
) < 0) {
467 error("Could not open '%s'", filename
);
469 ret
= bdrv_commit(bs
);
472 printf("Image committed.\n");
475 error("No disk inserted");
478 error("Image is read-only");
481 error("Image is already committed");
484 error("Error while committing image");
492 static int is_not_zero(const uint8_t *sector
, int len
)
496 for(i
= 0;i
< len
; i
++) {
497 if (((uint32_t *)sector
)[i
] != 0)
504 * Returns true iff the first sector pointed to by 'buf' contains at least
507 * 'pnum' is set to the number of sectors (including and immediately following
508 * the first one) that are known to be in the same allocated/unallocated state.
510 static int is_allocated_sectors(const uint8_t *buf
, int n
, int *pnum
)
518 v
= is_not_zero(buf
, 512);
519 for(i
= 1; i
< n
; i
++) {
521 if (v
!= is_not_zero(buf
, 512))
528 #define IO_BUF_SIZE 65536
530 static int img_convert(int argc
, char **argv
)
532 int c
, ret
, n
, n1
, bs_n
, bs_i
, flags
, cluster_size
, cluster_sectors
;
533 const char *fmt
, *out_fmt
, *out_baseimg
, *out_filename
;
535 BlockDriverState
**bs
, *out_bs
;
536 int64_t total_sectors
, nb_sectors
, sector_num
, bs_offset
;
538 uint8_t buf
[IO_BUF_SIZE
];
541 QEMUOptionParameter
*param
= NULL
;
542 char *options
= NULL
;
549 c
= getopt(argc
, argv
, "f:O:B:hce6o:");
563 out_baseimg
= optarg
;
566 flags
|= BLOCK_FLAG_COMPRESS
;
569 flags
|= BLOCK_FLAG_ENCRYPT
;
572 flags
|= BLOCK_FLAG_COMPAT6
;
580 bs_n
= argc
- optind
- 1;
581 if (bs_n
< 1) help();
583 out_filename
= argv
[argc
- 1];
585 if (bs_n
> 1 && out_baseimg
)
586 error("-B makes no sense when concatenating multiple input images");
588 bs
= calloc(bs_n
, sizeof(BlockDriverState
*));
590 error("Out of memory");
593 for (bs_i
= 0; bs_i
< bs_n
; bs_i
++) {
594 bs
[bs_i
] = bdrv_new_open(argv
[optind
+ bs_i
], fmt
);
596 error("Could not open '%s'", argv
[optind
+ bs_i
]);
597 bdrv_get_geometry(bs
[bs_i
], &bs_sectors
);
598 total_sectors
+= bs_sectors
;
601 /* Find driver and parse its options */
602 drv
= bdrv_find_format(out_fmt
);
604 error("Unknown file format '%s'", out_fmt
);
606 if (options
&& !strcmp(options
, "?")) {
607 print_option_help(drv
->create_options
);
612 param
= parse_option_parameters(options
, drv
->create_options
, param
);
614 error("Invalid options for file format '%s'.", out_fmt
);
617 param
= parse_option_parameters("", drv
->create_options
, param
);
620 set_option_parameter_int(param
, BLOCK_OPT_SIZE
, total_sectors
* 512);
621 add_old_style_options(out_fmt
, param
, flags
, out_baseimg
, NULL
);
623 /* Check if compression is supported */
624 if (flags
& BLOCK_FLAG_COMPRESS
) {
625 QEMUOptionParameter
*encryption
=
626 get_option_parameter(param
, BLOCK_OPT_ENCRYPT
);
628 if (!drv
->bdrv_write_compressed
) {
629 error("Compression not supported for this file format");
632 if (encryption
&& encryption
->value
.n
) {
633 error("Compression and encryption not supported at the same time");
637 /* Create the new image */
638 ret
= bdrv_create(drv
, out_filename
, param
);
639 free_option_parameters(param
);
642 if (ret
== -ENOTSUP
) {
643 error("Formatting not supported for file format '%s'", out_fmt
);
644 } else if (ret
== -EFBIG
) {
645 error("The image size is too large for file format '%s'", out_fmt
);
647 error("Error while formatting '%s'", out_filename
);
651 out_bs
= bdrv_new_open(out_filename
, out_fmt
);
655 bdrv_get_geometry(bs
[0], &bs_sectors
);
657 if (flags
& BLOCK_FLAG_COMPRESS
) {
658 if (bdrv_get_info(out_bs
, &bdi
) < 0)
659 error("could not get block driver info");
660 cluster_size
= bdi
.cluster_size
;
661 if (cluster_size
<= 0 || cluster_size
> IO_BUF_SIZE
)
662 error("invalid cluster size");
663 cluster_sectors
= cluster_size
>> 9;
670 nb_sectors
= total_sectors
- sector_num
;
673 if (nb_sectors
>= cluster_sectors
)
678 bs_num
= sector_num
- bs_offset
;
679 assert (bs_num
>= 0);
682 while (remainder
> 0) {
684 while (bs_num
== bs_sectors
) {
686 assert (bs_i
< bs_n
);
687 bs_offset
+= bs_sectors
;
688 bdrv_get_geometry(bs
[bs_i
], &bs_sectors
);
690 /* printf("changing part: sector_num=%lld, "
691 "bs_i=%d, bs_offset=%lld, bs_sectors=%lld\n",
692 sector_num, bs_i, bs_offset, bs_sectors); */
694 assert (bs_num
< bs_sectors
);
696 nlow
= (remainder
> bs_sectors
- bs_num
) ? bs_sectors
- bs_num
: remainder
;
698 if (bdrv_read(bs
[bs_i
], bs_num
, buf2
, nlow
) < 0)
699 error("error while reading");
706 assert (remainder
== 0);
708 if (n
< cluster_sectors
)
709 memset(buf
+ n
* 512, 0, cluster_size
- n
* 512);
710 if (is_not_zero(buf
, cluster_size
)) {
711 if (bdrv_write_compressed(out_bs
, sector_num
, buf
,
712 cluster_sectors
) != 0)
713 error("error while compressing sector %" PRId64
,
718 /* signal EOF to align */
719 bdrv_write_compressed(out_bs
, 0, NULL
, 0);
721 sector_num
= 0; // total number of sectors converted so far
723 nb_sectors
= total_sectors
- sector_num
;
726 if (nb_sectors
>= (IO_BUF_SIZE
/ 512))
727 n
= (IO_BUF_SIZE
/ 512);
731 while (sector_num
- bs_offset
>= bs_sectors
) {
733 assert (bs_i
< bs_n
);
734 bs_offset
+= bs_sectors
;
735 bdrv_get_geometry(bs
[bs_i
], &bs_sectors
);
736 /* printf("changing part: sector_num=%lld, bs_i=%d, "
737 "bs_offset=%lld, bs_sectors=%lld\n",
738 sector_num, bs_i, bs_offset, bs_sectors); */
741 if (n
> bs_offset
+ bs_sectors
- sector_num
)
742 n
= bs_offset
+ bs_sectors
- sector_num
;
744 if (strcmp(drv
->format_name
, "host_device")) {
745 if (!bdrv_is_allocated(bs
[bs_i
], sector_num
- bs_offset
,
750 /* The next 'n1' sectors are allocated in the input image. Copy
751 only those as they may be followed by unallocated sectors. */
757 if (bdrv_read(bs
[bs_i
], sector_num
- bs_offset
, buf
, n
) < 0)
758 error("error while reading");
759 /* NOTE: at the same time we convert, we do not write zero
760 sectors to have a chance to compress the image. Ideally, we
761 should add a specific call to have the info to go faster */
764 /* If the output image is being created as a copy on write image,
765 copy all sectors even the ones containing only NUL bytes,
766 because they may differ from the sectors in the base image.
768 If the output is to a host device, we also write out
769 sectors that are entirely 0, since whatever data was
770 already there is garbage, not 0s. */
771 if (strcmp(drv
->format_name
, "host_device") == 0 || out_baseimg
||
772 is_allocated_sectors(buf1
, n
, &n1
)) {
773 if (bdrv_write(out_bs
, sector_num
, buf1
, n1
) < 0)
774 error("error while writing");
783 for (bs_i
= 0; bs_i
< bs_n
; bs_i
++)
784 bdrv_delete(bs
[bs_i
]);
790 static int64_t get_allocated_file_size(const char *filename
)
792 typedef DWORD (WINAPI
* get_compressed_t
)(const char *filename
, DWORD
*high
);
793 get_compressed_t get_compressed
;
796 /* WinNT support GetCompressedFileSize to determine allocate size */
797 get_compressed
= (get_compressed_t
) GetProcAddress(GetModuleHandle("kernel32"), "GetCompressedFileSizeA");
798 if (get_compressed
) {
800 low
= get_compressed(filename
, &high
);
801 if (low
!= 0xFFFFFFFFlu
|| GetLastError() == NO_ERROR
)
802 return (((int64_t) high
) << 32) + low
;
805 if (_stati64(filename
, &st
) < 0)
810 static int64_t get_allocated_file_size(const char *filename
)
813 if (stat(filename
, &st
) < 0)
815 return (int64_t)st
.st_blocks
* 512;
819 static void dump_snapshots(BlockDriverState
*bs
)
821 QEMUSnapshotInfo
*sn_tab
, *sn
;
825 nb_sns
= bdrv_snapshot_list(bs
, &sn_tab
);
828 printf("Snapshot list:\n");
829 printf("%s\n", bdrv_snapshot_dump(buf
, sizeof(buf
), NULL
));
830 for(i
= 0; i
< nb_sns
; i
++) {
832 printf("%s\n", bdrv_snapshot_dump(buf
, sizeof(buf
), sn
));
837 static int img_info(int argc
, char **argv
)
840 const char *filename
, *fmt
;
842 BlockDriverState
*bs
;
843 char fmt_name
[128], size_buf
[128], dsize_buf
[128];
844 uint64_t total_sectors
;
845 int64_t allocated_size
;
846 char backing_filename
[1024];
847 char backing_filename2
[1024];
852 c
= getopt(argc
, argv
, "f:h");
866 filename
= argv
[optind
++];
870 error("Not enough memory");
872 drv
= bdrv_find_format(fmt
);
874 error("Unknown file format '%s'", fmt
);
878 if (bdrv_open2(bs
, filename
, BRDV_O_FLAGS
, drv
) < 0) {
879 error("Could not open '%s'", filename
);
881 bdrv_get_format(bs
, fmt_name
, sizeof(fmt_name
));
882 bdrv_get_geometry(bs
, &total_sectors
);
883 get_human_readable_size(size_buf
, sizeof(size_buf
), total_sectors
* 512);
884 allocated_size
= get_allocated_file_size(filename
);
885 if (allocated_size
< 0)
886 snprintf(dsize_buf
, sizeof(dsize_buf
), "unavailable");
888 get_human_readable_size(dsize_buf
, sizeof(dsize_buf
),
892 "virtual size: %s (%" PRId64
" bytes)\n"
894 filename
, fmt_name
, size_buf
,
895 (total_sectors
* 512),
897 if (bdrv_is_encrypted(bs
))
898 printf("encrypted: yes\n");
899 if (bdrv_get_info(bs
, &bdi
) >= 0) {
900 if (bdi
.cluster_size
!= 0)
901 printf("cluster_size: %d\n", bdi
.cluster_size
);
903 bdrv_get_backing_filename(bs
, backing_filename
, sizeof(backing_filename
));
904 if (backing_filename
[0] != '\0') {
905 path_combine(backing_filename2
, sizeof(backing_filename2
),
906 filename
, backing_filename
);
907 printf("backing file: %s (actual path: %s)\n",
916 #define SNAPSHOT_LIST 1
917 #define SNAPSHOT_CREATE 2
918 #define SNAPSHOT_APPLY 3
919 #define SNAPSHOT_DELETE 4
921 static void img_snapshot(int argc
, char **argv
)
923 BlockDriverState
*bs
;
925 char *filename
, *snapshot_name
= NULL
;
930 /* Parse commandline parameters */
932 c
= getopt(argc
, argv
, "la:c:d:h");
944 action
= SNAPSHOT_LIST
;
951 action
= SNAPSHOT_APPLY
;
952 snapshot_name
= optarg
;
959 action
= SNAPSHOT_CREATE
;
960 snapshot_name
= optarg
;
967 action
= SNAPSHOT_DELETE
;
968 snapshot_name
= optarg
;
975 filename
= argv
[optind
++];
980 error("Not enough memory");
982 if (bdrv_open2(bs
, filename
, 0, NULL
) < 0) {
983 error("Could not open '%s'", filename
);
986 /* Perform the requested action */
992 case SNAPSHOT_CREATE
:
993 memset(&sn
, 0, sizeof(sn
));
994 pstrcpy(sn
.name
, sizeof(sn
.name
), snapshot_name
);
996 qemu_gettimeofday(&tv
);
997 sn
.date_sec
= tv
.tv_sec
;
998 sn
.date_nsec
= tv
.tv_usec
* 1000;
1000 ret
= bdrv_snapshot_create(bs
, &sn
);
1002 error("Could not create snapshot '%s': %d (%s)",
1003 snapshot_name
, ret
, strerror(-ret
));
1006 case SNAPSHOT_APPLY
:
1007 ret
= bdrv_snapshot_goto(bs
, snapshot_name
);
1009 error("Could not apply snapshot '%s': %d (%s)",
1010 snapshot_name
, ret
, strerror(-ret
));
1013 case SNAPSHOT_DELETE
:
1014 ret
= bdrv_snapshot_delete(bs
, snapshot_name
);
1016 error("Could not delete snapshot '%s': %d (%s)",
1017 snapshot_name
, ret
, strerror(-ret
));
1025 int main(int argc
, char **argv
)
1034 if (!strcmp(cmd
, "create")) {
1035 img_create(argc
, argv
);
1036 } else if (!strcmp(cmd
, "check")) {
1037 img_check(argc
, argv
);
1038 } else if (!strcmp(cmd
, "commit")) {
1039 img_commit(argc
, argv
);
1040 } else if (!strcmp(cmd
, "convert")) {
1041 img_convert(argc
, argv
);
1042 } else if (!strcmp(cmd
, "info")) {
1043 img_info(argc
, argv
);
1044 } else if (!strcmp(cmd
, "snapshot")) {
1045 img_snapshot(argc
, argv
);