X-Git-Url: https://repo.jachan.dev/qemu.git/blobdiff_plain/a3ce3668ccff7d350a4f795ad99a012a6d41caef..325b3ceff69c987e90acf9c8ef6f55e646b39767:/qemu-img.c diff --git a/qemu-img.c b/qemu-img.c index 54137a4e92..6a61ca8d06 100644 --- a/qemu-img.c +++ b/qemu-img.c @@ -66,7 +66,8 @@ static void help(void) " '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" + " options are: 'none', 'writeback' (default), 'writethrough', 'directsync'\n" + " 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" @@ -81,6 +82,8 @@ static void help(void) " 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" + " '-S' indicates the consecutive number of bytes that must contain only zeros\n" + " for qemu-img to create a sparse image during conversion\n" "\n" "Parameters to snapshot subcommand:\n" " 'snapshot' is the name of the snapshot to create, apply or delete\n" @@ -183,27 +186,6 @@ 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; @@ -350,8 +332,9 @@ static int img_create(int argc, char **argv) /* Get image size, if specified */ if (optind < argc) { int64_t sval; - sval = strtosz_suffix(argv[optind++], NULL, STRTOSZ_DEFSUFFIX_B); - if (sval < 0) { + char *end; + sval = strtosz_suffix(argv[optind++], &end, STRTOSZ_DEFSUFFIX_B); + if (sval < 0 || *end) { error_report("Invalid image size specified! You may use k, M, G or " "T suffixes for "); error_report("kilobytes, megabytes, gigabytes and terabytes."); @@ -445,6 +428,13 @@ static int img_check(int argc, char **argv) } } + if (result.bfi.total_clusters != 0 && result.bfi.allocated_clusters != 0) { + printf("%" PRId64 "/%" PRId64 "= %0.2f%% allocated, %0.2f%% fragmented\n", + result.bfi.allocated_clusters, result.bfi.total_clusters, + result.bfi.allocated_clusters * 100.0 / result.bfi.total_clusters, + result.bfi.fragmented_clusters * 100.0 / result.bfi.allocated_clusters); + } + bdrv_delete(bs); if (ret < 0 || result.check_errors) { @@ -495,7 +485,7 @@ static int img_commit(int argc, char **argv) filename = argv[optind++]; flags = BDRV_O_RDWR; - ret = set_cache_flag(cache, &flags); + ret = bdrv_parse_cache_flags(cache, &flags); if (ret < 0) { error_report("Invalid cache option: %s", cache); return -1; @@ -531,40 +521,6 @@ 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; - 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; -} - /* * Returns true iff the first sector pointed to by 'buf' contains at least * a non-NUL byte. @@ -574,20 +530,64 @@ static int is_not_zero(const uint8_t *sector, int len) */ static int is_allocated_sectors(const uint8_t *buf, int n, int *pnum) { - int v, i; + bool is_zero; + int i; if (n <= 0) { *pnum = 0; return 0; } - v = is_not_zero(buf, 512); + is_zero = buffer_is_zero(buf, 512); for(i = 1; i < n; i++) { buf += 512; - if (v != is_not_zero(buf, 512)) + if (is_zero != buffer_is_zero(buf, 512)) { break; + } } *pnum = i; - return v; + return !is_zero; +} + +/* + * Like is_allocated_sectors, but if the buffer starts with a used sector, + * up to 'min' consecutive sectors containing zeros are ignored. This avoids + * breaking up write requests for only small sparse areas. + */ +static int is_allocated_sectors_min(const uint8_t *buf, int n, int *pnum, + int min) +{ + int ret; + int num_checked, num_used; + + if (n < min) { + min = n; + } + + ret = is_allocated_sectors(buf, n, pnum); + if (!ret) { + return ret; + } + + num_used = *pnum; + buf += BDRV_SECTOR_SIZE * *pnum; + n -= *pnum; + num_checked = num_used; + + while (n > 0) { + ret = is_allocated_sectors(buf, n, pnum); + + buf += BDRV_SECTOR_SIZE * *pnum; + n -= *pnum; + num_checked += *pnum; + if (ret) { + num_used = num_checked; + } else if (*pnum >= min) { + break; + } + } + + *pnum = num_used; + return 1; } /* @@ -640,6 +640,7 @@ static int img_convert(int argc, char **argv) char *options = NULL; const char *snapshot_name = NULL; float local_progress; + int min_sparse = 8; /* Need at least 4k of zeros for sparse detection */ fmt = NULL; out_fmt = "raw"; @@ -647,7 +648,7 @@ static int img_convert(int argc, char **argv) out_baseimg = NULL; compress = 0; for(;;) { - c = getopt(argc, argv, "f:O:B:s:hce6o:pt:"); + c = getopt(argc, argv, "f:O:B:s:hce6o:pS:t:"); if (c == -1) { break; } @@ -682,6 +683,19 @@ static int img_convert(int argc, char **argv) case 's': snapshot_name = optarg; break; + case 'S': + { + int64_t sval; + char *end; + sval = strtosz_suffix(optarg, &end, STRTOSZ_DEFSUFFIX_B); + if (sval < 0 || *end) { + error_report("Invalid minimum zero buffer size for sparse output specified"); + return 1; + } + + min_sparse = sval / BDRV_SECTOR_SIZE; + break; + } case 'p': progress = 1; break; @@ -709,11 +723,11 @@ static int img_convert(int argc, char **argv) ret = -1; goto out; } - + qemu_progress_init(progress, 2.0); qemu_progress_print(0, 100); - bs = qemu_mallocz(bs_n * sizeof(BlockDriverState *)); + bs = g_malloc0(bs_n * sizeof(BlockDriverState *)); total_sectors = 0; for (bs_i = 0; bs_i < bs_n; bs_i++) { @@ -787,6 +801,8 @@ static int img_convert(int argc, char **argv) if (compress) { QEMUOptionParameter *encryption = get_option_parameter(param, BLOCK_OPT_ENCRYPT); + QEMUOptionParameter *preallocation = + get_option_parameter(param, BLOCK_OPT_PREALLOC); if (!drv->bdrv_write_compressed) { error_report("Compression not supported for this file format"); @@ -800,6 +816,15 @@ static int img_convert(int argc, char **argv) ret = -1; goto out; } + + if (preallocation && preallocation->value.s + && strcmp(preallocation->value.s, "off")) + { + error_report("Compression and preallocation not supported at " + "the same time"); + ret = -1; + goto out; + } } /* Create the new image */ @@ -819,7 +844,7 @@ static int img_convert(int argc, char **argv) } flags = BDRV_O_RDWR; - ret = set_cache_flag(cache, &flags); + ret = bdrv_parse_cache_flags(cache, &flags); if (ret < 0) { error_report("Invalid cache option: %s", cache); return -1; @@ -834,7 +859,7 @@ static int img_convert(int argc, char **argv) bs_i = 0; bs_offset = 0; bdrv_get_geometry(bs[0], &bs_sectors); - buf = qemu_malloc(IO_BUF_SIZE); + buf = qemu_blockalign(out_bs, IO_BUF_SIZE); if (compress) { ret = bdrv_get_info(out_bs, &bdi); @@ -890,7 +915,8 @@ static int img_convert(int argc, char **argv) ret = bdrv_read(bs[bs_i], bs_num, buf2, nlow); if (ret < 0) { - error_report("error while reading"); + error_report("error while reading sector %" PRId64 ": %s", + bs_num, strerror(-ret)); goto out; } @@ -904,12 +930,12 @@ static int img_convert(int argc, char **argv) if (n < cluster_sectors) { memset(buf + n * 512, 0, cluster_size - n * 512); } - if (is_not_zero(buf, cluster_size)) { + if (!buffer_is_zero(buf, cluster_size)) { ret = bdrv_write_compressed(out_bs, sector_num, buf, cluster_sectors); if (ret != 0) { - error_report("error while compressing sector %" PRId64, - sector_num); + error_report("error while compressing sector %" PRId64 + ": %s", sector_num, strerror(-ret)); goto out; } } @@ -972,7 +998,8 @@ static int img_convert(int argc, char **argv) ret = bdrv_read(bs[bs_i], sector_num - bs_offset, buf, n); if (ret < 0) { - error_report("error while reading"); + error_report("error while reading sector %" PRId64 ": %s", + sector_num - bs_offset, strerror(-ret)); goto out; } /* NOTE: at the same time we convert, we do not write zero @@ -988,10 +1015,11 @@ static int img_convert(int argc, char **argv) sectors that are entirely 0, since whatever data was already there is garbage, not 0s. */ if (!has_zero_init || out_baseimg || - is_allocated_sectors(buf1, n, &n1)) { + is_allocated_sectors_min(buf1, n, &n1, min_sparse)) { ret = bdrv_write(out_bs, sector_num, buf1, n1); if (ret < 0) { - error_report("error while writing"); + error_report("error while writing sector %" PRId64 + ": %s", sector_num, strerror(-ret)); goto out; } } @@ -1006,7 +1034,7 @@ out: qemu_progress_end(); free_option_parameters(create_options); free_option_parameters(param); - qemu_free(buf); + qemu_vfree(buf); if (out_bs) { bdrv_delete(out_bs); } @@ -1016,7 +1044,7 @@ out: bdrv_delete(bs[bs_i]); } } - qemu_free(bs); + g_free(bs); } if (ret) { return 1; @@ -1024,35 +1052,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) { @@ -1069,7 +1068,7 @@ static void dump_snapshots(BlockDriverState *bs) sn = &sn_tab[i]; printf("%s\n", bdrv_snapshot_dump(buf, sizeof(buf), sn)); } - qemu_free(sn_tab); + g_free(sn_tab); } static int img_info(int argc, char **argv) @@ -1112,7 +1111,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 { @@ -1133,6 +1132,9 @@ static int img_info(int argc, char **argv) if (bdi.cluster_size != 0) { printf("cluster_size: %d\n", bdi.cluster_size); } + if (bdi.is_dirty) { + printf("cleanly shut down: no\n"); + } } bdrv_get_backing_filename(bs, backing_filename, sizeof(backing_filename)); if (backing_filename[0] != '\0') { @@ -1320,7 +1322,7 @@ static int img_rebase(int argc, char **argv) qemu_progress_print(0, 100); flags = BDRV_O_RDWR | (unsafe ? BDRV_O_NO_BACKING : 0); - ret = set_cache_flag(cache, &flags); + ret = bdrv_parse_cache_flags(cache, &flags); if (ret < 0) { error_report("Invalid cache option: %s", cache); return -1; @@ -1396,16 +1398,20 @@ static int img_rebase(int argc, char **argv) */ if (!unsafe) { uint64_t num_sectors; + uint64_t old_backing_num_sectors; + uint64_t new_backing_num_sectors; uint64_t sector; 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); + buf_old = qemu_blockalign(bs, IO_BUF_SIZE); + buf_new = qemu_blockalign(bs, IO_BUF_SIZE); bdrv_get_geometry(bs, &num_sectors); + bdrv_get_geometry(bs_old_backing, &old_backing_num_sectors); + bdrv_get_geometry(bs_new_backing, &new_backing_num_sectors); local_progress = (float)100 / (num_sectors / MIN(num_sectors, IO_BUF_SIZE / 512)); @@ -1424,16 +1430,36 @@ static int img_rebase(int argc, char **argv) continue; } - /* Read old and new backing file */ - ret = bdrv_read(bs_old_backing, sector, buf_old, n); - if (ret < 0) { - error_report("error while reading from old backing file"); - goto out; + /* + * Read old and new backing file and take into consideration that + * backing files may be smaller than the COW image. + */ + if (sector >= old_backing_num_sectors) { + memset(buf_old, 0, n * BDRV_SECTOR_SIZE); + } else { + if (sector + n > old_backing_num_sectors) { + n = old_backing_num_sectors - sector; + } + + ret = bdrv_read(bs_old_backing, sector, buf_old, n); + if (ret < 0) { + error_report("error while reading from old backing file"); + goto out; + } } - ret = bdrv_read(bs_new_backing, sector, buf_new, n); - if (ret < 0) { - error_report("error while reading from new backing file"); - goto out; + + if (sector >= new_backing_num_sectors) { + memset(buf_new, 0, n * BDRV_SECTOR_SIZE); + } else { + if (sector + n > new_backing_num_sectors) { + n = new_backing_num_sectors - sector; + } + + ret = bdrv_read(bs_new_backing, sector, buf_new, n); + if (ret < 0) { + error_report("error while reading from new backing file"); + goto out; + } } /* If they differ, we need to write to the COW file */ @@ -1459,8 +1485,8 @@ static int img_rebase(int argc, char **argv) qemu_progress_print(local_progress, 100); } - qemu_free(buf_old); - qemu_free(buf_new); + qemu_vfree(buf_old); + qemu_vfree(buf_new); } /* @@ -1598,7 +1624,7 @@ static int img_resize(int argc, char **argv) printf("Image resized.\n"); break; case -ENOTSUP: - error_report("This image format does not support resize"); + error_report("This image does not support resize"); break; case -EACCES: error_report("Image is read-only"); @@ -1639,6 +1665,8 @@ int main(int argc, char **argv) cmdname = argv[1]; argc--; argv++; + qemu_init_main_loop(); + /* find the command */ for(cmd = img_cmds; cmd->name != NULL; cmd++) { if (!strcmp(cmdname, cmd->name)) {