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 typedef struct img_cmd_t
{
36 int (*handler
)(int argc
, char **argv
);
39 /* Default to cache=writeback as data integrity is not important for qemu-tcg. */
40 #define BRDV_O_FLAGS BDRV_O_CACHE_WB
42 static void QEMU_NORETURN
error(const char *fmt
, ...)
46 fprintf(stderr
, "qemu-img: ");
47 vfprintf(stderr
, fmt
, ap
);
48 fprintf(stderr
, "\n");
53 static void format_print(void *opaque
, const char *name
)
58 /* Please keep in synch with qemu-img.texi */
59 static void help(void)
61 printf("qemu-img version " QEMU_VERSION
", Copyright (c) 2004-2008 Fabrice Bellard\n"
62 "usage: qemu-img command [command options]\n"
63 "QEMU disk image utility\n"
66 #define DEF(option, callback, arg_string) \
68 #include "qemu-img-cmds.h"
72 "Command parameters:\n"
73 " 'filename' is a disk image filename\n"
74 " 'base_image' is the read-only disk image which is used as base for a copy on\n"
75 " write image; the copy on write image only stores the modified data\n"
76 " 'output_base_image' forces the output image to be created as a copy on write\n"
77 " image of the specified base image; 'output_base_image' should have the same\n"
78 " content as the input's base image, however the path, image format, etc may\n"
80 " 'fmt' is the disk image format. It is guessed automatically in most cases\n"
81 " 'size' is the disk image size in kilobytes. Optional suffixes\n"
82 " 'M' (megabyte, 1024 * 1024) and 'G' (gigabyte, 1024 * 1024 * 1024) are\n"
83 " supported any 'k' or 'K' is ignored\n"
84 " 'output_filename' is the destination disk image filename\n"
85 " 'output_fmt' is the destination format\n"
86 " 'options' is a comma separated list of format specific options in a\n"
87 " name=value format. Use -o ? for an overview of the options supported by the\n"
89 " '-c' indicates that target image must be compressed (qcow format only)\n"
90 " '-h' with or without a command shows this help and lists the supported formats\n"
92 "Parameters to snapshot subcommand:\n"
93 " 'snapshot' is the name of the snapshot to create, apply or delete\n"
94 " '-a' applies a snapshot (revert disk to saved state)\n"
95 " '-c' creates a snapshot\n"
96 " '-d' deletes a snapshot\n"
97 " '-l' lists all snapshots in the given image\n"
99 printf("\nSupported formats:");
100 bdrv_iterate_format(format_print
, NULL
);
106 /* XXX: put correct support for win32 */
107 static int read_password(char *buf
, int buf_size
)
110 printf("Password: ");
117 if (i
< (buf_size
- 1))
128 static struct termios oldtty
;
130 static void term_exit(void)
132 tcsetattr (0, TCSANOW
, &oldtty
);
135 static void term_init(void)
142 tty
.c_iflag
&= ~(IGNBRK
|BRKINT
|PARMRK
|ISTRIP
143 |INLCR
|IGNCR
|ICRNL
|IXON
);
144 tty
.c_oflag
|= OPOST
;
145 tty
.c_lflag
&= ~(ECHO
|ECHONL
|ICANON
|IEXTEN
);
146 tty
.c_cflag
&= ~(CSIZE
|PARENB
);
151 tcsetattr (0, TCSANOW
, &tty
);
156 static int read_password(char *buf
, int buf_size
)
161 printf("password: ");
166 ret
= read(0, &ch
, 1);
168 if (errno
== EAGAIN
|| errno
== EINTR
) {
174 } else if (ret
== 0) {
182 if (i
< (buf_size
- 1))
193 static BlockDriverState
*bdrv_new_open(const char *filename
,
196 BlockDriverState
*bs
;
202 error("Not enough memory");
204 drv
= bdrv_find_format(fmt
);
206 error("Unknown file format '%s'", fmt
);
210 if (bdrv_open2(bs
, filename
, BRDV_O_FLAGS
, drv
) < 0) {
211 error("Could not open '%s'", filename
);
213 if (bdrv_is_encrypted(bs
)) {
214 printf("Disk image '%s' is encrypted.\n", filename
);
215 if (read_password(password
, sizeof(password
)) < 0)
216 error("No password given");
217 if (bdrv_set_key(bs
, password
) < 0)
218 error("invalid password");
223 static void add_old_style_options(const char *fmt
, QEMUOptionParameter
*list
,
224 int flags
, const char *base_filename
, const char *base_fmt
)
226 if (flags
& BLOCK_FLAG_ENCRYPT
) {
227 if (set_option_parameter(list
, BLOCK_OPT_ENCRYPT
, "on")) {
228 error("Encryption not supported for file format '%s'", fmt
);
231 if (flags
& BLOCK_FLAG_COMPAT6
) {
232 if (set_option_parameter(list
, BLOCK_OPT_COMPAT6
, "on")) {
233 error("VMDK version 6 not supported for file format '%s'", fmt
);
238 if (set_option_parameter(list
, BLOCK_OPT_BACKING_FILE
, base_filename
)) {
239 error("Backing file not supported for file format '%s'", fmt
);
243 if (set_option_parameter(list
, BLOCK_OPT_BACKING_FMT
, base_fmt
)) {
244 error("Backing file format not supported for file format '%s'", fmt
);
249 static int img_create(int argc
, char **argv
)
252 const char *fmt
= "raw";
253 const char *base_fmt
= NULL
;
254 const char *filename
;
255 const char *base_filename
= NULL
;
257 QEMUOptionParameter
*param
= NULL
;
258 char *options
= NULL
;
262 c
= getopt(argc
, argv
, "F:b:f:he6o:");
273 base_filename
= optarg
;
279 flags
|= BLOCK_FLAG_ENCRYPT
;
282 flags
|= BLOCK_FLAG_COMPAT6
;
290 /* Find driver and parse its options */
291 drv
= bdrv_find_format(fmt
);
293 error("Unknown file format '%s'", fmt
);
295 if (options
&& !strcmp(options
, "?")) {
296 print_option_help(drv
->create_options
);
300 /* Create parameter list with default values */
301 param
= parse_option_parameters("", drv
->create_options
, param
);
302 set_option_parameter_int(param
, BLOCK_OPT_SIZE
, -1);
304 /* Parse -o options */
306 param
= parse_option_parameters(options
, drv
->create_options
, param
);
308 error("Invalid options for file format '%s'.", fmt
);
312 /* Get the filename */
315 filename
= argv
[optind
++];
317 /* Add size to parameters */
319 set_option_parameter(param
, BLOCK_OPT_SIZE
, argv
[optind
++]);
322 /* Add old-style options to parameters */
323 add_old_style_options(fmt
, param
, flags
, base_filename
, base_fmt
);
325 // The size for the image must always be specified, with one exception:
326 // If we are using a backing file, we can obtain the size from there
327 if (get_option_parameter(param
, BLOCK_OPT_SIZE
)->value
.n
== -1) {
329 QEMUOptionParameter
*backing_file
=
330 get_option_parameter(param
, BLOCK_OPT_BACKING_FILE
);
331 QEMUOptionParameter
*backing_fmt
=
332 get_option_parameter(param
, BLOCK_OPT_BACKING_FMT
);
334 if (backing_file
&& backing_file
->value
.s
) {
335 BlockDriverState
*bs
;
337 const char *fmt
= NULL
;
340 if (backing_fmt
&& backing_fmt
->value
.s
) {
341 if (bdrv_find_format(backing_fmt
->value
.s
)) {
342 fmt
= backing_fmt
->value
.s
;
344 error("Unknown backing file format '%s'",
345 backing_fmt
->value
.s
);
349 bs
= bdrv_new_open(backing_file
->value
.s
, fmt
);
350 bdrv_get_geometry(bs
, &size
);
354 snprintf(buf
, sizeof(buf
), "%" PRId64
, size
);
355 set_option_parameter(param
, BLOCK_OPT_SIZE
, buf
);
357 error("Image creation needs a size parameter");
361 printf("Formatting '%s', fmt=%s ", filename
, fmt
);
362 print_option_parameters(param
);
365 ret
= bdrv_create(drv
, filename
, param
);
366 free_option_parameters(param
);
369 if (ret
== -ENOTSUP
) {
370 error("Formatting or formatting option not supported for file format '%s'", fmt
);
371 } else if (ret
== -EFBIG
) {
372 error("The image size is too large for file format '%s'", fmt
);
374 error("Error while formatting");
380 static int img_check(int argc
, char **argv
)
383 const char *filename
, *fmt
;
385 BlockDriverState
*bs
;
389 c
= getopt(argc
, argv
, "f:h");
403 filename
= argv
[optind
++];
407 error("Not enough memory");
409 drv
= bdrv_find_format(fmt
);
411 error("Unknown file format '%s'", fmt
);
415 if (bdrv_open2(bs
, filename
, BRDV_O_FLAGS
, drv
) < 0) {
416 error("Could not open '%s'", filename
);
418 ret
= bdrv_check(bs
);
421 printf("No errors were found on the image.\n");
424 error("This image format does not support checks");
428 error("An error occurred during the check");
430 printf("%d errors were found on the image.\n", ret
);
439 static int img_commit(int argc
, char **argv
)
442 const char *filename
, *fmt
;
444 BlockDriverState
*bs
;
448 c
= getopt(argc
, argv
, "f:h");
462 filename
= argv
[optind
++];
466 error("Not enough memory");
468 drv
= bdrv_find_format(fmt
);
470 error("Unknown file format '%s'", fmt
);
474 if (bdrv_open2(bs
, filename
, BRDV_O_FLAGS
, drv
) < 0) {
475 error("Could not open '%s'", filename
);
477 ret
= bdrv_commit(bs
);
480 printf("Image committed.\n");
483 error("No disk inserted");
486 error("Image is read-only");
489 error("Image is already committed");
492 error("Error while committing image");
500 static int is_not_zero(const uint8_t *sector
, int len
)
504 for(i
= 0;i
< len
; i
++) {
505 if (((uint32_t *)sector
)[i
] != 0)
512 * Returns true iff the first sector pointed to by 'buf' contains at least
515 * 'pnum' is set to the number of sectors (including and immediately following
516 * the first one) that are known to be in the same allocated/unallocated state.
518 static int is_allocated_sectors(const uint8_t *buf
, int n
, int *pnum
)
526 v
= is_not_zero(buf
, 512);
527 for(i
= 1; i
< n
; i
++) {
529 if (v
!= is_not_zero(buf
, 512))
536 #define IO_BUF_SIZE (2 * 1024 * 1024)
538 static int img_convert(int argc
, char **argv
)
540 int c
, ret
, n
, n1
, bs_n
, bs_i
, flags
, cluster_size
, cluster_sectors
;
541 const char *fmt
, *out_fmt
, *out_baseimg
, *out_filename
;
543 BlockDriverState
**bs
, *out_bs
;
544 int64_t total_sectors
, nb_sectors
, sector_num
, bs_offset
;
546 uint8_t buf
[IO_BUF_SIZE
];
549 QEMUOptionParameter
*param
= NULL
;
550 char *options
= NULL
;
557 c
= getopt(argc
, argv
, "f:O:B:hce6o:");
571 out_baseimg
= optarg
;
574 flags
|= BLOCK_FLAG_COMPRESS
;
577 flags
|= BLOCK_FLAG_ENCRYPT
;
580 flags
|= BLOCK_FLAG_COMPAT6
;
588 bs_n
= argc
- optind
- 1;
589 if (bs_n
< 1) help();
591 out_filename
= argv
[argc
- 1];
593 if (bs_n
> 1 && out_baseimg
)
594 error("-B makes no sense when concatenating multiple input images");
596 bs
= calloc(bs_n
, sizeof(BlockDriverState
*));
598 error("Out of memory");
601 for (bs_i
= 0; bs_i
< bs_n
; bs_i
++) {
602 bs
[bs_i
] = bdrv_new_open(argv
[optind
+ bs_i
], fmt
);
604 error("Could not open '%s'", argv
[optind
+ bs_i
]);
605 bdrv_get_geometry(bs
[bs_i
], &bs_sectors
);
606 total_sectors
+= bs_sectors
;
609 /* Find driver and parse its options */
610 drv
= bdrv_find_format(out_fmt
);
612 error("Unknown file format '%s'", out_fmt
);
614 if (options
&& !strcmp(options
, "?")) {
615 print_option_help(drv
->create_options
);
620 param
= parse_option_parameters(options
, drv
->create_options
, param
);
622 error("Invalid options for file format '%s'.", out_fmt
);
625 param
= parse_option_parameters("", drv
->create_options
, param
);
628 set_option_parameter_int(param
, BLOCK_OPT_SIZE
, total_sectors
* 512);
629 add_old_style_options(out_fmt
, param
, flags
, out_baseimg
, NULL
);
631 /* Check if compression is supported */
632 if (flags
& BLOCK_FLAG_COMPRESS
) {
633 QEMUOptionParameter
*encryption
=
634 get_option_parameter(param
, BLOCK_OPT_ENCRYPT
);
636 if (!drv
->bdrv_write_compressed
) {
637 error("Compression not supported for this file format");
640 if (encryption
&& encryption
->value
.n
) {
641 error("Compression and encryption not supported at the same time");
645 /* Create the new image */
646 ret
= bdrv_create(drv
, out_filename
, param
);
647 free_option_parameters(param
);
650 if (ret
== -ENOTSUP
) {
651 error("Formatting not supported for file format '%s'", out_fmt
);
652 } else if (ret
== -EFBIG
) {
653 error("The image size is too large for file format '%s'", out_fmt
);
655 error("Error while formatting '%s'", out_filename
);
659 out_bs
= bdrv_new_open(out_filename
, out_fmt
);
663 bdrv_get_geometry(bs
[0], &bs_sectors
);
665 if (flags
& BLOCK_FLAG_COMPRESS
) {
666 if (bdrv_get_info(out_bs
, &bdi
) < 0)
667 error("could not get block driver info");
668 cluster_size
= bdi
.cluster_size
;
669 if (cluster_size
<= 0 || cluster_size
> IO_BUF_SIZE
)
670 error("invalid cluster size");
671 cluster_sectors
= cluster_size
>> 9;
678 nb_sectors
= total_sectors
- sector_num
;
681 if (nb_sectors
>= cluster_sectors
)
686 bs_num
= sector_num
- bs_offset
;
687 assert (bs_num
>= 0);
690 while (remainder
> 0) {
692 while (bs_num
== bs_sectors
) {
694 assert (bs_i
< bs_n
);
695 bs_offset
+= bs_sectors
;
696 bdrv_get_geometry(bs
[bs_i
], &bs_sectors
);
698 /* printf("changing part: sector_num=%lld, "
699 "bs_i=%d, bs_offset=%lld, bs_sectors=%lld\n",
700 sector_num, bs_i, bs_offset, bs_sectors); */
702 assert (bs_num
< bs_sectors
);
704 nlow
= (remainder
> bs_sectors
- bs_num
) ? bs_sectors
- bs_num
: remainder
;
706 if (bdrv_read(bs
[bs_i
], bs_num
, buf2
, nlow
) < 0)
707 error("error while reading");
714 assert (remainder
== 0);
716 if (n
< cluster_sectors
)
717 memset(buf
+ n
* 512, 0, cluster_size
- n
* 512);
718 if (is_not_zero(buf
, cluster_size
)) {
719 if (bdrv_write_compressed(out_bs
, sector_num
, buf
,
720 cluster_sectors
) != 0)
721 error("error while compressing sector %" PRId64
,
726 /* signal EOF to align */
727 bdrv_write_compressed(out_bs
, 0, NULL
, 0);
729 sector_num
= 0; // total number of sectors converted so far
731 nb_sectors
= total_sectors
- sector_num
;
734 if (nb_sectors
>= (IO_BUF_SIZE
/ 512))
735 n
= (IO_BUF_SIZE
/ 512);
739 while (sector_num
- bs_offset
>= bs_sectors
) {
741 assert (bs_i
< bs_n
);
742 bs_offset
+= bs_sectors
;
743 bdrv_get_geometry(bs
[bs_i
], &bs_sectors
);
744 /* printf("changing part: sector_num=%lld, bs_i=%d, "
745 "bs_offset=%lld, bs_sectors=%lld\n",
746 sector_num, bs_i, bs_offset, bs_sectors); */
749 if (n
> bs_offset
+ bs_sectors
- sector_num
)
750 n
= bs_offset
+ bs_sectors
- sector_num
;
752 if (strcmp(drv
->format_name
, "host_device")) {
753 /* If the output image is being created as a copy on write image,
754 assume that sectors which are unallocated in the input image
755 are present in both the output's and input's base images (no
756 need to copy them). */
758 if (!bdrv_is_allocated(bs
[bs_i
], sector_num
- bs_offset
,
763 /* The next 'n1' sectors are allocated in the input image. Copy
764 only those as they may be followed by unallocated sectors. */
771 if (bdrv_read(bs
[bs_i
], sector_num
- bs_offset
, buf
, n
) < 0)
772 error("error while reading");
773 /* NOTE: at the same time we convert, we do not write zero
774 sectors to have a chance to compress the image. Ideally, we
775 should add a specific call to have the info to go faster */
778 /* If the output image is being created as a copy on write image,
779 copy all sectors even the ones containing only NUL bytes,
780 because they may differ from the sectors in the base image.
782 If the output is to a host device, we also write out
783 sectors that are entirely 0, since whatever data was
784 already there is garbage, not 0s. */
785 if (strcmp(drv
->format_name
, "host_device") == 0 || out_baseimg
||
786 is_allocated_sectors(buf1
, n
, &n1
)) {
787 if (bdrv_write(out_bs
, sector_num
, buf1
, n1
) < 0)
788 error("error while writing");
797 for (bs_i
= 0; bs_i
< bs_n
; bs_i
++)
798 bdrv_delete(bs
[bs_i
]);
804 static int64_t get_allocated_file_size(const char *filename
)
806 typedef DWORD (WINAPI
* get_compressed_t
)(const char *filename
, DWORD
*high
);
807 get_compressed_t get_compressed
;
810 /* WinNT support GetCompressedFileSize to determine allocate size */
811 get_compressed
= (get_compressed_t
) GetProcAddress(GetModuleHandle("kernel32"), "GetCompressedFileSizeA");
812 if (get_compressed
) {
814 low
= get_compressed(filename
, &high
);
815 if (low
!= 0xFFFFFFFFlu
|| GetLastError() == NO_ERROR
)
816 return (((int64_t) high
) << 32) + low
;
819 if (_stati64(filename
, &st
) < 0)
824 static int64_t get_allocated_file_size(const char *filename
)
827 if (stat(filename
, &st
) < 0)
829 return (int64_t)st
.st_blocks
* 512;
833 static void dump_snapshots(BlockDriverState
*bs
)
835 QEMUSnapshotInfo
*sn_tab
, *sn
;
839 nb_sns
= bdrv_snapshot_list(bs
, &sn_tab
);
842 printf("Snapshot list:\n");
843 printf("%s\n", bdrv_snapshot_dump(buf
, sizeof(buf
), NULL
));
844 for(i
= 0; i
< nb_sns
; i
++) {
846 printf("%s\n", bdrv_snapshot_dump(buf
, sizeof(buf
), sn
));
851 static int img_info(int argc
, char **argv
)
854 const char *filename
, *fmt
;
856 BlockDriverState
*bs
;
857 char fmt_name
[128], size_buf
[128], dsize_buf
[128];
858 uint64_t total_sectors
;
859 int64_t allocated_size
;
860 char backing_filename
[1024];
861 char backing_filename2
[1024];
866 c
= getopt(argc
, argv
, "f:h");
880 filename
= argv
[optind
++];
884 error("Not enough memory");
886 drv
= bdrv_find_format(fmt
);
888 error("Unknown file format '%s'", fmt
);
892 if (bdrv_open2(bs
, filename
, BRDV_O_FLAGS
, drv
) < 0) {
893 error("Could not open '%s'", filename
);
895 bdrv_get_format(bs
, fmt_name
, sizeof(fmt_name
));
896 bdrv_get_geometry(bs
, &total_sectors
);
897 get_human_readable_size(size_buf
, sizeof(size_buf
), total_sectors
* 512);
898 allocated_size
= get_allocated_file_size(filename
);
899 if (allocated_size
< 0)
900 snprintf(dsize_buf
, sizeof(dsize_buf
), "unavailable");
902 get_human_readable_size(dsize_buf
, sizeof(dsize_buf
),
906 "virtual size: %s (%" PRId64
" bytes)\n"
908 filename
, fmt_name
, size_buf
,
909 (total_sectors
* 512),
911 if (bdrv_is_encrypted(bs
))
912 printf("encrypted: yes\n");
913 if (bdrv_get_info(bs
, &bdi
) >= 0) {
914 if (bdi
.cluster_size
!= 0)
915 printf("cluster_size: %d\n", bdi
.cluster_size
);
917 bdrv_get_backing_filename(bs
, backing_filename
, sizeof(backing_filename
));
918 if (backing_filename
[0] != '\0') {
919 path_combine(backing_filename2
, sizeof(backing_filename2
),
920 filename
, backing_filename
);
921 printf("backing file: %s (actual path: %s)\n",
930 #define SNAPSHOT_LIST 1
931 #define SNAPSHOT_CREATE 2
932 #define SNAPSHOT_APPLY 3
933 #define SNAPSHOT_DELETE 4
935 static int img_snapshot(int argc
, char **argv
)
937 BlockDriverState
*bs
;
939 char *filename
, *snapshot_name
= NULL
;
944 /* Parse commandline parameters */
946 c
= getopt(argc
, argv
, "la:c:d:h");
958 action
= SNAPSHOT_LIST
;
965 action
= SNAPSHOT_APPLY
;
966 snapshot_name
= optarg
;
973 action
= SNAPSHOT_CREATE
;
974 snapshot_name
= optarg
;
981 action
= SNAPSHOT_DELETE
;
982 snapshot_name
= optarg
;
989 filename
= argv
[optind
++];
994 error("Not enough memory");
996 if (bdrv_open2(bs
, filename
, 0, NULL
) < 0) {
997 error("Could not open '%s'", filename
);
1000 /* Perform the requested action */
1006 case SNAPSHOT_CREATE
:
1007 memset(&sn
, 0, sizeof(sn
));
1008 pstrcpy(sn
.name
, sizeof(sn
.name
), snapshot_name
);
1010 qemu_gettimeofday(&tv
);
1011 sn
.date_sec
= tv
.tv_sec
;
1012 sn
.date_nsec
= tv
.tv_usec
* 1000;
1014 ret
= bdrv_snapshot_create(bs
, &sn
);
1016 error("Could not create snapshot '%s': %d (%s)",
1017 snapshot_name
, ret
, strerror(-ret
));
1020 case SNAPSHOT_APPLY
:
1021 ret
= bdrv_snapshot_goto(bs
, snapshot_name
);
1023 error("Could not apply snapshot '%s': %d (%s)",
1024 snapshot_name
, ret
, strerror(-ret
));
1027 case SNAPSHOT_DELETE
:
1028 ret
= bdrv_snapshot_delete(bs
, snapshot_name
);
1030 error("Could not delete snapshot '%s': %d (%s)",
1031 snapshot_name
, ret
, strerror(-ret
));
1041 static const img_cmd_t img_cmds
[] = {
1042 #define DEF(option, callback, arg_string) \
1043 { option, callback },
1044 #include "qemu-img-cmds.h"
1050 int main(int argc
, char **argv
)
1052 const img_cmd_t
*cmd
;
1053 const char *cmdname
;
1061 /* find the command */
1062 for(cmd
= img_cmds
; cmd
->name
!= NULL
; cmd
++) {
1063 if (!strcmp(cmdname
, cmd
->name
)) {
1064 return cmd
->handler(argc
, argv
);