X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=qemu-img.c;h=b205e98dd2513a7fd32bf2f99be197f73d011cc0;hb=4eb36d40da8062400a2e7e27f1038e1252df9ede;hp=0b871d842c03b711984f771b3e15e19f338e06e8;hpb=15654a6d7c3269e922b92f9596e48078c9bfcbfa;p=qemu.git diff --git a/qemu-img.c b/qemu-img.c index 0b871d842..b205e98dd 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -40,6 +40,7 @@ typedef struct img_cmd_t { /* Default to cache=writeback as data integrity is not important for qemu-tcg. */ #define BDRV_O_FLAGS BDRV_O_CACHE_WB +#define BDRV_DEFAULT_CACHE "writeback" static void format_print(void *opaque, const char *name) { @@ -64,6 +65,8 @@ static void help(void) "Command parameters:\n" " 'filename' is a disk image filename\n" " 'fmt' is the disk image format. It is guessed automatically in most cases\n" + " 'cache' is the cache mode used to write the output disk image, the valid\n" + " options are: 'none', 'writeback' (default), 'writethrough' and 'unsafe'\n" " 'size' is the disk image size in bytes. Optional suffixes\n" " 'k' or 'K' (kilobyte, 1024), 'M' (megabyte, 1024k), 'G' (gigabyte, 1024M)\n" " and T (terabyte, 1024G) are supported. 'b' is ignored.\n" @@ -77,6 +80,7 @@ static void help(void) " match exactly. The image doesn't need a working backing file before\n" " rebasing in this case (useful for renaming the backing file)\n" " '-h' with or without a command shows this help and lists the supported formats\n" + " '-p' show progress of command (only certain commands)\n" "\n" "Parameters to snapshot subcommand:\n" " 'snapshot' is the name of the snapshot to create, apply or delete\n" @@ -179,6 +183,27 @@ static int read_password(char *buf, int buf_size) } #endif +static int set_cache_flag(const char *mode, int *flags) +{ + *flags &= ~BDRV_O_CACHE_MASK; + + if (!strcmp(mode, "none") || !strcmp(mode, "off")) { + *flags |= BDRV_O_CACHE_WB; + *flags |= BDRV_O_NOCACHE; + } else if (!strcmp(mode, "writeback")) { + *flags |= BDRV_O_CACHE_WB; + } else if (!strcmp(mode, "unsafe")) { + *flags |= BDRV_O_CACHE_WB; + *flags |= BDRV_O_NO_FLUSH; + } else if (!strcmp(mode, "writethrough")) { + /* this is the default */ + } else { + return -1; + } + + return 0; +} + static int print_block_option_help(const char *filename, const char *fmt) { BlockDriver *drv, *proto_drv; @@ -213,12 +238,10 @@ static BlockDriverState *bdrv_new_open(const char *filename, BlockDriverState *bs; BlockDriver *drv; char password[256]; + int ret; + + bs = bdrv_new("image"); - bs = bdrv_new(""); - if (!bs) { - error_report("Not enough memory"); - goto fail; - } if (fmt) { drv = bdrv_find_format(fmt); if (!drv) { @@ -228,10 +251,13 @@ static BlockDriverState *bdrv_new_open(const char *filename, } else { drv = NULL; } - if (bdrv_open(bs, filename, flags, drv) < 0) { - error_report("Could not open '%s'", filename); + + ret = bdrv_open(bs, filename, flags, drv); + if (ret < 0) { + error_report("Could not open '%s': %s", filename, strerror(-ret)); goto fail; } + if (bdrv_is_encrypted(bs)) { printf("Disk image '%s' is encrypted.\n", filename); if (read_password(password, sizeof(password)) < 0) { @@ -302,11 +328,11 @@ static int img_create(int argc, char **argv) fmt = optarg; break; case 'e': - error_report("qemu-img: option -e is deprecated, please use \'-o " + error_report("option -e is deprecated, please use \'-o " "encryption\' instead!"); return 1; case '6': - error_report("qemu-img: option -6 is deprecated, please use \'-o " + error_report("option -6 is deprecated, please use \'-o " "compat6\' instead!"); return 1; case 'o': @@ -323,7 +349,7 @@ static int img_create(int argc, char **argv) /* Get image size, if specified */ if (optind < argc) { - ssize_t sval; + int64_t sval; sval = strtosz_suffix(argv[optind++], NULL, STRTOSZ_DEFSUFFIX_B); if (sval < 0) { error_report("Invalid image size specified! You may use k, M, G or " @@ -439,13 +465,14 @@ static int img_check(int argc, char **argv) static int img_commit(int argc, char **argv) { - int c, ret; - const char *filename, *fmt; + int c, ret, flags; + const char *filename, *fmt, *cache; BlockDriverState *bs; fmt = NULL; + cache = BDRV_DEFAULT_CACHE; for(;;) { - c = getopt(argc, argv, "f:h"); + c = getopt(argc, argv, "f:ht:"); if (c == -1) { break; } @@ -457,6 +484,9 @@ static int img_commit(int argc, char **argv) case 'f': fmt = optarg; break; + case 't': + cache = optarg; + break; } } if (optind >= argc) { @@ -464,7 +494,14 @@ static int img_commit(int argc, char **argv) } filename = argv[optind++]; - bs = bdrv_new_open(filename, fmt, BDRV_O_FLAGS | BDRV_O_RDWR); + flags = BDRV_O_RDWR; + ret = set_cache_flag(cache, &flags); + if (ret < 0) { + error_report("Invalid cache option: %s", cache); + return -1; + } + + bs = bdrv_new_open(filename, fmt, flags); if (!bs) { return 1; } @@ -494,14 +531,37 @@ static int img_commit(int argc, char **argv) return 0; } +/* + * Checks whether the sector is not a zero sector. + * + * Attention! The len must be a multiple of 4 * sizeof(long) due to + * restriction of optimizations in this function. + */ static int is_not_zero(const uint8_t *sector, int len) { + /* + * Use long as the biggest available internal data type that fits into the + * CPU register and unroll the loop to smooth out the effect of memory + * latency. + */ + int i; - len >>= 2; - for(i = 0;i < len; i++) { - if (((uint32_t *)sector)[i] != 0) + long d0, d1, d2, d3; + const long * const data = (const long *) sector; + + len /= sizeof(long); + + for(i = 0; i < len; i += 4) { + d0 = data[i + 0]; + d1 = data[i + 1]; + d2 = data[i + 2]; + d3 = data[i + 3]; + + if (d0 || d1 || d2 || d3) { return 1; + } } + return 0; } @@ -566,7 +626,8 @@ static int compare_sectors(const uint8_t *buf1, const uint8_t *buf2, int n, static int img_convert(int argc, char **argv) { int c, ret = 0, n, n1, bs_n, bs_i, compress, cluster_size, cluster_sectors; - const char *fmt, *out_fmt, *out_baseimg, *out_filename; + int progress = 0, flags; + const char *fmt, *out_fmt, *cache, *out_baseimg, *out_filename; BlockDriver *drv, *proto_drv; BlockDriverState **bs = NULL, *out_bs = NULL; int64_t total_sectors, nb_sectors, sector_num, bs_offset; @@ -578,13 +639,15 @@ static int img_convert(int argc, char **argv) QEMUOptionParameter *out_baseimg_param; char *options = NULL; const char *snapshot_name = NULL; + float local_progress; fmt = NULL; out_fmt = "raw"; + cache = "unsafe"; out_baseimg = NULL; compress = 0; for(;;) { - c = getopt(argc, argv, "f:O:B:s:hce6o:"); + c = getopt(argc, argv, "f:O:B:s:hce6o:pt:"); if (c == -1) { break; } @@ -606,11 +669,11 @@ static int img_convert(int argc, char **argv) compress = 1; break; case 'e': - error_report("qemu-img: option -e is deprecated, please use \'-o " + error_report("option -e is deprecated, please use \'-o " "encryption\' instead!"); return 1; case '6': - error_report("qemu-img: option -6 is deprecated, please use \'-o " + error_report("option -6 is deprecated, please use \'-o " "compat6\' instead!"); return 1; case 'o': @@ -619,6 +682,12 @@ static int img_convert(int argc, char **argv) case 's': snapshot_name = optarg; break; + case 'p': + progress = 1; + break; + case 't': + cache = optarg; + break; } } @@ -641,6 +710,9 @@ static int img_convert(int argc, char **argv) goto out; } + qemu_progress_init(progress, 2.0); + qemu_progress_print(0, 100); + bs = qemu_mallocz(bs_n * sizeof(BlockDriverState *)); total_sectors = 0; @@ -657,12 +729,12 @@ static int img_convert(int argc, char **argv) if (snapshot_name != NULL) { if (bs_n > 1) { - error_report("No support for concatenating multiple snapshot\n"); + error_report("No support for concatenating multiple snapshot"); ret = -1; goto out; } if (bdrv_snapshot_load_tmp(bs[0], snapshot_name) < 0) { - error_report("Failed to load snapshot\n"); + error_report("Failed to load snapshot"); ret = -1; goto out; } @@ -746,8 +818,14 @@ static int img_convert(int argc, char **argv) goto out; } - out_bs = bdrv_new_open(out_filename, out_fmt, - BDRV_O_FLAGS | BDRV_O_RDWR | BDRV_O_NO_FLUSH); + flags = BDRV_O_RDWR; + ret = set_cache_flag(cache, &flags); + if (ret < 0) { + error_report("Invalid cache option: %s", cache); + return -1; + } + + out_bs = bdrv_new_open(out_filename, out_fmt, flags); if (!out_bs) { ret = -1; goto out; @@ -772,6 +850,11 @@ static int img_convert(int argc, char **argv) } cluster_sectors = cluster_size >> 9; sector_num = 0; + + nb_sectors = total_sectors; + local_progress = (float)100 / + (nb_sectors / MIN(nb_sectors, cluster_sectors)); + for(;;) { int64_t bs_num; int remainder; @@ -831,6 +914,7 @@ static int img_convert(int argc, char **argv) } } sector_num += n; + qemu_progress_print(local_progress, 100); } /* signal EOF to align */ bdrv_write_compressed(out_bs, 0, NULL, 0); @@ -838,6 +922,10 @@ static int img_convert(int argc, char **argv) int has_zero_init = bdrv_has_zero_init(out_bs); sector_num = 0; // total number of sectors converted so far + nb_sectors = total_sectors - sector_num; + local_progress = (float)100 / + (nb_sectors / MIN(nb_sectors, IO_BUF_SIZE / 512)); + for(;;) { nb_sectors = total_sectors - sector_num; if (nb_sectors <= 0) { @@ -911,9 +999,11 @@ static int img_convert(int argc, char **argv) n -= n1; buf1 += n1 * 512; } + qemu_progress_print(local_progress, 100); } } out: + qemu_progress_end(); free_option_parameters(create_options); free_option_parameters(param); qemu_free(buf); @@ -934,35 +1024,6 @@ out: return 0; } -#ifdef _WIN32 -static int64_t get_allocated_file_size(const char *filename) -{ - typedef DWORD (WINAPI * get_compressed_t)(const char *filename, DWORD *high); - get_compressed_t get_compressed; - struct _stati64 st; - - /* WinNT support GetCompressedFileSize to determine allocate size */ - get_compressed = (get_compressed_t) GetProcAddress(GetModuleHandle("kernel32"), "GetCompressedFileSizeA"); - if (get_compressed) { - DWORD high, low; - low = get_compressed(filename, &high); - if (low != 0xFFFFFFFFlu || GetLastError() == NO_ERROR) - return (((int64_t) high) << 32) + low; - } - - if (_stati64(filename, &st) < 0) - return -1; - return st.st_size; -} -#else -static int64_t get_allocated_file_size(const char *filename) -{ - struct stat st; - if (stat(filename, &st) < 0) - return -1; - return (int64_t)st.st_blocks * 512; -} -#endif static void dump_snapshots(BlockDriverState *bs) { @@ -1022,7 +1083,7 @@ static int img_info(int argc, char **argv) bdrv_get_format(bs, fmt_name, sizeof(fmt_name)); bdrv_get_geometry(bs, &total_sectors); get_human_readable_size(size_buf, sizeof(size_buf), total_sectors * 512); - allocated_size = get_allocated_file_size(filename); + allocated_size = bdrv_get_allocated_file_size(bs); if (allocated_size < 0) { snprintf(dsize_buf, sizeof(dsize_buf), "unavailable"); } else { @@ -1071,7 +1132,7 @@ static int img_snapshot(int argc, char **argv) int action = 0; qemu_timeval tv; - bdrv_oflags = BDRV_O_RDWR; + bdrv_oflags = BDRV_O_FLAGS | BDRV_O_RDWR; /* Parse commandline parameters */ for(;;) { c = getopt(argc, argv, "la:c:d:h"); @@ -1180,17 +1241,18 @@ static int img_rebase(int argc, char **argv) BlockDriverState *bs, *bs_old_backing = NULL, *bs_new_backing = NULL; BlockDriver *old_backing_drv, *new_backing_drv; char *filename; - const char *fmt, *out_basefmt, *out_baseimg; + const char *fmt, *cache, *out_basefmt, *out_baseimg; int c, flags, ret; int unsafe = 0; + int progress = 0; /* Parse commandline parameters */ fmt = NULL; + cache = BDRV_DEFAULT_CACHE; out_baseimg = NULL; out_basefmt = NULL; - for(;;) { - c = getopt(argc, argv, "uhf:F:b:"); + c = getopt(argc, argv, "uhf:F:b:pt:"); if (c == -1) { break; } @@ -1211,21 +1273,36 @@ static int img_rebase(int argc, char **argv) case 'u': unsafe = 1; break; + case 'p': + progress = 1; + break; + case 't': + cache = optarg; + break; } } - if ((optind >= argc) || !out_baseimg) { + if ((optind >= argc) || (!unsafe && !out_baseimg)) { help(); } filename = argv[optind++]; + qemu_progress_init(progress, 2.0); + qemu_progress_print(0, 100); + + flags = BDRV_O_RDWR | (unsafe ? BDRV_O_NO_BACKING : 0); + ret = set_cache_flag(cache, &flags); + if (ret < 0) { + error_report("Invalid cache option: %s", cache); + return -1; + } + /* * Open the images. * * Ignore the old backing file for unsafe rebase in case we want to correct * the reference to a renamed or moved backing file. */ - flags = BDRV_O_FLAGS | BDRV_O_RDWR | (unsafe ? BDRV_O_NO_BACKING : 0); bs = bdrv_new_open(filename, fmt, flags); if (!bs) { return 1; @@ -1294,12 +1371,15 @@ static int img_rebase(int argc, char **argv) int n; uint8_t * buf_old; uint8_t * buf_new; + float local_progress; buf_old = qemu_malloc(IO_BUF_SIZE); buf_new = qemu_malloc(IO_BUF_SIZE); bdrv_get_geometry(bs, &num_sectors); + local_progress = (float)100 / + (num_sectors / MIN(num_sectors, IO_BUF_SIZE / 512)); for (sector = 0; sector < num_sectors; sector += n) { /* How many sectors can we handle with the next read? */ @@ -1347,6 +1427,7 @@ static int img_rebase(int argc, char **argv) written += pnum; } + qemu_progress_print(local_progress, 100); } qemu_free(buf_old); @@ -1367,6 +1448,7 @@ static int img_rebase(int argc, char **argv) out_baseimg, strerror(-ret)); } + qemu_progress_print(100, 0); /* * TODO At this point it is possible to check if any clusters that are * allocated in the COW file are the same in the backing file. If so, they @@ -1374,10 +1456,15 @@ static int img_rebase(int argc, char **argv) * backing file, in case of a crash this would lead to corruption. */ out: + qemu_progress_end(); /* Cleanup */ if (!unsafe) { - bdrv_delete(bs_old_backing); - bdrv_delete(bs_new_backing); + if (bs_old_backing != NULL) { + bdrv_delete(bs_old_backing); + } + if (bs_new_backing != NULL) { + bdrv_delete(bs_new_backing); + } } bdrv_delete(bs); @@ -1403,6 +1490,16 @@ static int img_resize(int argc, char **argv) { NULL } }; + /* Remove size from argv manually so that negative numbers are not treated + * as options by getopt. */ + if (argc < 3) { + help(); + return 1; + } + + size = argv[--argc]; + + /* Parse getopt arguments */ fmt = NULL; for(;;) { c = getopt(argc, argv, "f:h"); @@ -1419,11 +1516,10 @@ static int img_resize(int argc, char **argv) break; } } - if (optind + 1 >= argc) { + if (optind >= argc) { help(); } filename = argv[optind++]; - size = argv[optind++]; /* Choose grow, shrink, or absolute resize mode */ switch (size[0]) {