Statistics
| Branch: | Revision:

root / qemu-img.c @ 42388c4b

History | View | Annotate | Download (19.9 kB)

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 "block_int.h"
26
#include <assert.h>
27

    
28
#ifdef _WIN32
29
#define WIN32_LEAN_AND_MEAN
30
#include <windows.h>
31
#endif
32

    
33
static void __attribute__((noreturn)) error(const char *fmt, ...)
34
{
35
    va_list ap;
36
    va_start(ap, fmt);
37
    fprintf(stderr, "qemu-img: ");
38
    vfprintf(stderr, fmt, ap);
39
    fprintf(stderr, "\n");
40
    exit(1);
41
    va_end(ap);
42
}
43

    
44
static void format_print(void *opaque, const char *name)
45
{
46
    printf(" %s", name);
47
}
48

    
49
static void help(void)
50
{
51
    printf("qemu-img version " QEMU_VERSION ", Copyright (c) 2004-2008 Fabrice Bellard\n"
52
           "usage: qemu-img command [command options]\n"
53
           "QEMU disk image utility\n"
54
           "\n"
55
           "Command syntax:\n"
56
           "  create [-e] [-6] [-b base_image] [-f fmt] filename [size]\n"
57
           "  commit [-f fmt] filename\n"
58
           "  convert [-c] [-e] [-6] [-f fmt] [-O output_fmt] filename [filename2 [...]] output_filename\n"
59
           "  info [-f fmt] filename\n"
60
           "\n"
61
           "Command parameters:\n"
62
           "  'filename' is a disk image filename\n"
63
           "  'base_image' is the read-only disk image which is used as base for a copy on\n"
64
           "    write image; the copy on write image only stores the modified data\n"
65
           "  'fmt' is the disk image format. It is guessed automatically in most cases\n"
66
           "  'size' is the disk image size in kilobytes. Optional suffixes 'M' (megabyte)\n"
67
           "    and 'G' (gigabyte) are supported\n"
68
           "  'output_filename' is the destination disk image filename\n"
69
           "  'output_fmt' is the destination format\n"
70
           "  '-c' indicates that target image must be compressed (qcow format only)\n"
71
           "  '-e' indicates that the target image must be encrypted (qcow format only)\n"
72
           "  '-6' indicates that the target image must use compatibility level 6 (vmdk format only)\n"
73
           );
74
    printf("\nSupported format:");
75
    bdrv_iterate_format(format_print, NULL);
76
    printf("\n");
77
    exit(1);
78
}
79

    
80
#if defined(WIN32)
81
/* XXX: put correct support for win32 */
82
static int read_password(char *buf, int buf_size)
83
{
84
    int c, i;
85
    printf("Password: ");
86
    fflush(stdout);
87
    i = 0;
88
    for(;;) {
89
        c = getchar();
90
        if (c == '\n')
91
            break;
92
        if (i < (buf_size - 1))
93
            buf[i++] = c;
94
    }
95
    buf[i] = '\0';
96
    return 0;
97
}
98

    
99
#else
100

    
101
#include <termios.h>
102

    
103
static struct termios oldtty;
104

    
105
static void term_exit(void)
106
{
107
    tcsetattr (0, TCSANOW, &oldtty);
108
}
109

    
110
static void term_init(void)
111
{
112
    struct termios tty;
113

    
114
    tcgetattr (0, &tty);
115
    oldtty = tty;
116

    
117
    tty.c_iflag &= ~(IGNBRK|BRKINT|PARMRK|ISTRIP
118
                          |INLCR|IGNCR|ICRNL|IXON);
119
    tty.c_oflag |= OPOST;
120
    tty.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN);
121
    tty.c_cflag &= ~(CSIZE|PARENB);
122
    tty.c_cflag |= CS8;
123
    tty.c_cc[VMIN] = 1;
124
    tty.c_cc[VTIME] = 0;
125

    
126
    tcsetattr (0, TCSANOW, &tty);
127

    
128
    atexit(term_exit);
129
}
130

    
131
static int read_password(char *buf, int buf_size)
132
{
133
    uint8_t ch;
134
    int i, ret;
135

    
136
    printf("password: ");
137
    fflush(stdout);
138
    term_init();
139
    i = 0;
140
    for(;;) {
141
        ret = read(0, &ch, 1);
142
        if (ret == -1) {
143
            if (errno == EAGAIN || errno == EINTR) {
144
                continue;
145
            } else {
146
                ret = -1;
147
                break;
148
            }
149
        } else if (ret == 0) {
150
            ret = -1;
151
            break;
152
        } else {
153
            if (ch == '\r') {
154
                ret = 0;
155
                break;
156
            }
157
            if (i < (buf_size - 1))
158
                buf[i++] = ch;
159
        }
160
    }
161
    term_exit();
162
    buf[i] = '\0';
163
    printf("\n");
164
    return ret;
165
}
166
#endif
167

    
168
static BlockDriverState *bdrv_new_open(const char *filename,
169
                                       const char *fmt)
170
{
171
    BlockDriverState *bs;
172
    BlockDriver *drv;
173
    char password[256];
174

    
175
    bs = bdrv_new("");
176
    if (!bs)
177
        error("Not enough memory");
178
    if (fmt) {
179
        drv = bdrv_find_format(fmt);
180
        if (!drv)
181
            error("Unknown file format '%s'", fmt);
182
    } else {
183
        drv = NULL;
184
    }
185
    if (bdrv_open2(bs, filename, 0, drv) < 0) {
186
        error("Could not open '%s'", filename);
187
    }
188
    if (bdrv_is_encrypted(bs)) {
189
        printf("Disk image '%s' is encrypted.\n", filename);
190
        if (read_password(password, sizeof(password)) < 0)
191
            error("No password given");
192
        if (bdrv_set_key(bs, password) < 0)
193
            error("invalid password");
194
    }
195
    return bs;
196
}
197

    
198
static int img_create(int argc, char **argv)
199
{
200
    int c, ret, flags;
201
    const char *fmt = "raw";
202
    const char *filename;
203
    const char *base_filename = NULL;
204
    uint64_t size;
205
    const char *p;
206
    BlockDriver *drv;
207

    
208
    flags = 0;
209
    for(;;) {
210
        c = getopt(argc, argv, "b:f:he6");
211
        if (c == -1)
212
            break;
213
        switch(c) {
214
        case 'h':
215
            help();
216
            break;
217
        case 'b':
218
            base_filename = optarg;
219
            break;
220
        case 'f':
221
            fmt = optarg;
222
            break;
223
        case 'e':
224
            flags |= BLOCK_FLAG_ENCRYPT;
225
            break;
226
        case '6':
227
            flags |= BLOCK_FLAG_COMPAT6;
228
            break;
229
        }
230
    }
231
    if (optind >= argc)
232
        help();
233
    filename = argv[optind++];
234
    size = 0;
235
    if (base_filename) {
236
        BlockDriverState *bs;
237
        bs = bdrv_new_open(base_filename, NULL);
238
        bdrv_get_geometry(bs, &size);
239
        size *= 512;
240
        bdrv_delete(bs);
241
    } else {
242
        if (optind >= argc)
243
            help();
244
        p = argv[optind];
245
        size = strtoul(p, (char **)&p, 0);
246
        if (*p == 'M') {
247
            size *= 1024 * 1024;
248
        } else if (*p == 'G') {
249
            size *= 1024 * 1024 * 1024;
250
        } else if (*p == 'k' || *p == 'K' || *p == '\0') {
251
            size *= 1024;
252
        } else {
253
            help();
254
        }
255
    }
256
    drv = bdrv_find_format(fmt);
257
    if (!drv)
258
        error("Unknown file format '%s'", fmt);
259
    printf("Formatting '%s', fmt=%s",
260
           filename, fmt);
261
    if (flags & BLOCK_FLAG_ENCRYPT)
262
        printf(", encrypted");
263
    if (flags & BLOCK_FLAG_COMPAT6)
264
        printf(", compatibility level=6");
265
    if (base_filename) {
266
        printf(", backing_file=%s",
267
               base_filename);
268
    }
269
    printf(", size=%" PRIu64 " kB\n", size / 1024);
270
    ret = bdrv_create(drv, filename, size / 512, base_filename, flags);
271
    if (ret < 0) {
272
        if (ret == -ENOTSUP) {
273
            error("Formatting or formatting option not supported for file format '%s'", fmt);
274
        } else {
275
            error("Error while formatting");
276
        }
277
    }
278
    return 0;
279
}
280

    
281
static int img_commit(int argc, char **argv)
282
{
283
    int c, ret;
284
    const char *filename, *fmt;
285
    BlockDriver *drv;
286
    BlockDriverState *bs;
287

    
288
    fmt = NULL;
289
    for(;;) {
290
        c = getopt(argc, argv, "f:h");
291
        if (c == -1)
292
            break;
293
        switch(c) {
294
        case 'h':
295
            help();
296
            break;
297
        case 'f':
298
            fmt = optarg;
299
            break;
300
        }
301
    }
302
    if (optind >= argc)
303
        help();
304
    filename = argv[optind++];
305

    
306
    bs = bdrv_new("");
307
    if (!bs)
308
        error("Not enough memory");
309
    if (fmt) {
310
        drv = bdrv_find_format(fmt);
311
        if (!drv)
312
            error("Unknown file format '%s'", fmt);
313
    } else {
314
        drv = NULL;
315
    }
316
    if (bdrv_open2(bs, filename, 0, drv) < 0) {
317
        error("Could not open '%s'", filename);
318
    }
319
    ret = bdrv_commit(bs);
320
    switch(ret) {
321
    case 0:
322
        printf("Image committed.\n");
323
        break;
324
    case -ENOENT:
325
        error("No disk inserted");
326
        break;
327
    case -EACCES:
328
        error("Image is read-only");
329
        break;
330
    case -ENOTSUP:
331
        error("Image is already committed");
332
        break;
333
    default:
334
        error("Error while committing image");
335
        break;
336
    }
337

    
338
    bdrv_delete(bs);
339
    return 0;
340
}
341

    
342
static int is_not_zero(const uint8_t *sector, int len)
343
{
344
    int i;
345
    len >>= 2;
346
    for(i = 0;i < len; i++) {
347
        if (((uint32_t *)sector)[i] != 0)
348
            return 1;
349
    }
350
    return 0;
351
}
352

    
353
static int is_allocated_sectors(const uint8_t *buf, int n, int *pnum)
354
{
355
    int v, i;
356

    
357
    if (n <= 0) {
358
        *pnum = 0;
359
        return 0;
360
    }
361
    v = is_not_zero(buf, 512);
362
    for(i = 1; i < n; i++) {
363
        buf += 512;
364
        if (v != is_not_zero(buf, 512))
365
            break;
366
    }
367
    *pnum = i;
368
    return v;
369
}
370

    
371
#define IO_BUF_SIZE 65536
372

    
373
static int img_convert(int argc, char **argv)
374
{
375
    int c, ret, n, n1, bs_n, bs_i, flags, cluster_size, cluster_sectors;
376
    const char *fmt, *out_fmt, *out_filename;
377
    BlockDriver *drv;
378
    BlockDriverState **bs, *out_bs;
379
    int64_t total_sectors, nb_sectors, sector_num, bs_offset;
380
    uint64_t bs_sectors;
381
    uint8_t buf[IO_BUF_SIZE];
382
    const uint8_t *buf1;
383
    BlockDriverInfo bdi;
384

    
385
    fmt = NULL;
386
    out_fmt = "raw";
387
    flags = 0;
388
    for(;;) {
389
        c = getopt(argc, argv, "f:O:hce6");
390
        if (c == -1)
391
            break;
392
        switch(c) {
393
        case 'h':
394
            help();
395
            break;
396
        case 'f':
397
            fmt = optarg;
398
            break;
399
        case 'O':
400
            out_fmt = optarg;
401
            break;
402
        case 'c':
403
            flags |= BLOCK_FLAG_COMPRESS;
404
            break;
405
        case 'e':
406
            flags |= BLOCK_FLAG_ENCRYPT;
407
            break;
408
        case '6':
409
            flags |= BLOCK_FLAG_COMPAT6;
410
            break;
411
        }
412
    }
413

    
414
    bs_n = argc - optind - 1;
415
    if (bs_n < 1) help();
416

    
417
    out_filename = argv[argc - 1];
418
        
419
    bs = calloc(bs_n, sizeof(BlockDriverState *));
420
    if (!bs)
421
        error("Out of memory");
422

    
423
    total_sectors = 0;
424
    for (bs_i = 0; bs_i < bs_n; bs_i++) {
425
        bs[bs_i] = bdrv_new_open(argv[optind + bs_i], fmt);
426
        if (!bs[bs_i])
427
            error("Could not open '%s'", argv[optind + bs_i]);
428
        bdrv_get_geometry(bs[bs_i], &bs_sectors);
429
        total_sectors += bs_sectors;
430
    }
431

    
432
    drv = bdrv_find_format(out_fmt);
433
    if (!drv)
434
        error("Unknown file format '%s'", out_fmt);
435
    if (flags & BLOCK_FLAG_COMPRESS && drv != &bdrv_qcow && drv != &bdrv_qcow2)
436
        error("Compression not supported for this file format");
437
    if (flags & BLOCK_FLAG_ENCRYPT && drv != &bdrv_qcow && drv != &bdrv_qcow2)
438
        error("Encryption not supported for this file format");
439
    if (flags & BLOCK_FLAG_COMPAT6 && drv != &bdrv_vmdk)
440
        error("Alternative compatibility level not supported for this file format");
441
    if (flags & BLOCK_FLAG_ENCRYPT && flags & BLOCK_FLAG_COMPRESS)
442
        error("Compression and encryption not supported at the same time");
443

    
444
    ret = bdrv_create(drv, out_filename, total_sectors, NULL, flags);
445
    if (ret < 0) {
446
        if (ret == -ENOTSUP) {
447
            error("Formatting not supported for file format '%s'", fmt);
448
        } else {
449
            error("Error while formatting '%s'", out_filename);
450
        }
451
    }
452

    
453
    out_bs = bdrv_new_open(out_filename, out_fmt);
454

    
455
    bs_i = 0;
456
    bs_offset = 0;
457
    bdrv_get_geometry(bs[0], &bs_sectors);
458

    
459
    if (flags & BLOCK_FLAG_COMPRESS) {
460
        if (bdrv_get_info(out_bs, &bdi) < 0)
461
            error("could not get block driver info");
462
        cluster_size = bdi.cluster_size;
463
        if (cluster_size <= 0 || cluster_size > IO_BUF_SIZE)
464
            error("invalid cluster size");
465
        cluster_sectors = cluster_size >> 9;
466
        sector_num = 0;
467
        for(;;) {
468
            int64_t bs_num;
469
            int remainder;
470
            uint8_t *buf2;
471

    
472
            nb_sectors = total_sectors - sector_num;
473
            if (nb_sectors <= 0)
474
                break;
475
            if (nb_sectors >= cluster_sectors)
476
                n = cluster_sectors;
477
            else
478
                n = nb_sectors;
479

    
480
            bs_num = sector_num - bs_offset;
481
            assert (bs_num >= 0);
482
            remainder = n;
483
            buf2 = buf;
484
            while (remainder > 0) {
485
                int nlow;
486
                while (bs_num == bs_sectors) {
487
                    bs_i++;
488
                    assert (bs_i < bs_n);
489
                    bs_offset += bs_sectors;
490
                    bdrv_get_geometry(bs[bs_i], &bs_sectors);
491
                    bs_num = 0;
492
                    /* printf("changing part: sector_num=%lld, "
493
                       "bs_i=%d, bs_offset=%lld, bs_sectors=%lld\n",
494
                       sector_num, bs_i, bs_offset, bs_sectors); */
495
                }
496
                assert (bs_num < bs_sectors);
497

    
498
                nlow = (remainder > bs_sectors - bs_num) ? bs_sectors - bs_num : remainder;
499

    
500
                if (bdrv_read(bs[bs_i], bs_num, buf2, nlow) < 0) 
501
                    error("error while reading");
502

    
503
                buf2 += nlow * 512;
504
                bs_num += nlow;
505

    
506
                remainder -= nlow;
507
            }
508
            assert (remainder == 0);
509

    
510
            if (n < cluster_sectors)
511
                memset(buf + n * 512, 0, cluster_size - n * 512);
512
            if (is_not_zero(buf, cluster_size)) {
513
                if (bdrv_write_compressed(out_bs, sector_num, buf,
514
                                          cluster_sectors) != 0)
515
                    error("error while compressing sector %" PRId64,
516
                          sector_num);
517
            }
518
            sector_num += n;
519
        }
520
        /* signal EOF to align */
521
        bdrv_write_compressed(out_bs, 0, NULL, 0);
522
    } else {
523
        sector_num = 0;
524
        for(;;) {
525
            nb_sectors = total_sectors - sector_num;
526
            if (nb_sectors <= 0)
527
                break;
528
            if (nb_sectors >= (IO_BUF_SIZE / 512))
529
                n = (IO_BUF_SIZE / 512);
530
            else
531
                n = nb_sectors;
532

    
533
            while (sector_num - bs_offset >= bs_sectors) {
534
                bs_i ++;
535
                assert (bs_i < bs_n);
536
                bs_offset += bs_sectors;
537
                bdrv_get_geometry(bs[bs_i], &bs_sectors);
538
                /* printf("changing part: sector_num=%lld, bs_i=%d, "
539
                  "bs_offset=%lld, bs_sectors=%lld\n",
540
                   sector_num, bs_i, bs_offset, bs_sectors); */
541
            }
542

    
543
            if (n > bs_offset + bs_sectors - sector_num)
544
                n = bs_offset + bs_sectors - sector_num;
545

    
546
            if (bdrv_read(bs[bs_i], sector_num - bs_offset, buf, n) < 0) 
547
                error("error while reading");
548
            /* NOTE: at the same time we convert, we do not write zero
549
               sectors to have a chance to compress the image. Ideally, we
550
               should add a specific call to have the info to go faster */
551
            buf1 = buf;
552
            while (n > 0) {
553
                if (is_allocated_sectors(buf1, n, &n1)) {
554
                    if (bdrv_write(out_bs, sector_num, buf1, n1) < 0)
555
                        error("error while writing");
556
                }
557
                sector_num += n1;
558
                n -= n1;
559
                buf1 += n1 * 512;
560
            }
561
        }
562
    }
563
    bdrv_delete(out_bs);
564
    for (bs_i = 0; bs_i < bs_n; bs_i++)
565
        bdrv_delete(bs[bs_i]);
566
    free(bs);
567
    return 0;
568
}
569

    
570
#ifdef _WIN32
571
static int64_t get_allocated_file_size(const char *filename)
572
{
573
    typedef DWORD (WINAPI * get_compressed_t)(const char *filename, DWORD *high);
574
    get_compressed_t get_compressed;
575
    struct _stati64 st;
576

    
577
    /* WinNT support GetCompressedFileSize to determine allocate size */
578
    get_compressed = (get_compressed_t) GetProcAddress(GetModuleHandle("kernel32"), "GetCompressedFileSizeA");
579
    if (get_compressed) {
580
            DWORD high, low;
581
            low = get_compressed(filename, &high);
582
            if (low != 0xFFFFFFFFlu || GetLastError() == NO_ERROR)
583
            return (((int64_t) high) << 32) + low;
584
    }
585

    
586
    if (_stati64(filename, &st) < 0)
587
        return -1;
588
    return st.st_size;
589
}
590
#else
591
static int64_t get_allocated_file_size(const char *filename)
592
{
593
    struct stat st;
594
    if (stat(filename, &st) < 0)
595
        return -1;
596
    return (int64_t)st.st_blocks * 512;
597
}
598
#endif
599

    
600
static void dump_snapshots(BlockDriverState *bs)
601
{
602
    QEMUSnapshotInfo *sn_tab, *sn;
603
    int nb_sns, i;
604
    char buf[256];
605

    
606
    nb_sns = bdrv_snapshot_list(bs, &sn_tab);
607
    if (nb_sns <= 0)
608
        return;
609
    printf("Snapshot list:\n");
610
    printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), NULL));
611
    for(i = 0; i < nb_sns; i++) {
612
        sn = &sn_tab[i];
613
        printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), sn));
614
    }
615
    qemu_free(sn_tab);
616
}
617

    
618
static int img_info(int argc, char **argv)
619
{
620
    int c;
621
    const char *filename, *fmt;
622
    BlockDriver *drv;
623
    BlockDriverState *bs;
624
    char fmt_name[128], size_buf[128], dsize_buf[128];
625
    uint64_t total_sectors;
626
    int64_t allocated_size;
627
    char backing_filename[1024];
628
    char backing_filename2[1024];
629
    BlockDriverInfo bdi;
630

    
631
    fmt = NULL;
632
    for(;;) {
633
        c = getopt(argc, argv, "f:h");
634
        if (c == -1)
635
            break;
636
        switch(c) {
637
        case 'h':
638
            help();
639
            break;
640
        case 'f':
641
            fmt = optarg;
642
            break;
643
        }
644
    }
645
    if (optind >= argc)
646
        help();
647
    filename = argv[optind++];
648

    
649
    bs = bdrv_new("");
650
    if (!bs)
651
        error("Not enough memory");
652
    if (fmt) {
653
        drv = bdrv_find_format(fmt);
654
        if (!drv)
655
            error("Unknown file format '%s'", fmt);
656
    } else {
657
        drv = NULL;
658
    }
659
    if (bdrv_open2(bs, filename, 0, drv) < 0) {
660
        error("Could not open '%s'", filename);
661
    }
662
    bdrv_get_format(bs, fmt_name, sizeof(fmt_name));
663
    bdrv_get_geometry(bs, &total_sectors);
664
    get_human_readable_size(size_buf, sizeof(size_buf), total_sectors * 512);
665
    allocated_size = get_allocated_file_size(filename);
666
    if (allocated_size < 0)
667
        sprintf(dsize_buf, "unavailable");
668
    else
669
        get_human_readable_size(dsize_buf, sizeof(dsize_buf),
670
                                allocated_size);
671
    printf("image: %s\n"
672
           "file format: %s\n"
673
           "virtual size: %s (%" PRId64 " bytes)\n"
674
           "disk size: %s\n",
675
           filename, fmt_name, size_buf,
676
           (total_sectors * 512),
677
           dsize_buf);
678
    if (bdrv_is_encrypted(bs))
679
        printf("encrypted: yes\n");
680
    if (bdrv_get_info(bs, &bdi) >= 0) {
681
        if (bdi.cluster_size != 0)
682
            printf("cluster_size: %d\n", bdi.cluster_size);
683
    }
684
    bdrv_get_backing_filename(bs, backing_filename, sizeof(backing_filename));
685
    if (backing_filename[0] != '\0') {
686
        path_combine(backing_filename2, sizeof(backing_filename2),
687
                     filename, backing_filename);
688
        printf("backing file: %s (actual path: %s)\n",
689
               backing_filename,
690
               backing_filename2);
691
    }
692
    dump_snapshots(bs);
693
    bdrv_delete(bs);
694
    return 0;
695
}
696

    
697
int main(int argc, char **argv)
698
{
699
    const char *cmd;
700

    
701
    bdrv_init();
702
    if (argc < 2)
703
        help();
704
    cmd = argv[1];
705
    optind++;
706
    if (!strcmp(cmd, "create")) {
707
        img_create(argc, argv);
708
    } else if (!strcmp(cmd, "commit")) {
709
        img_commit(argc, argv);
710
    } else if (!strcmp(cmd, "convert")) {
711
        img_convert(argc, argv);
712
    } else if (!strcmp(cmd, "info")) {
713
        img_info(argc, argv);
714
    } else {
715
        help();
716
    }
717
    return 0;
718
}