/* 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)
{
"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"
" 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"
}
#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;
BlockDriverState *bs;
BlockDriver *drv;
char password[256];
+ int ret;
- bs = bdrv_new("");
+ bs = bdrv_new("image");
if (fmt) {
drv = bdrv_find_format(fmt);
} 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) {
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':
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;
}
case 'f':
fmt = optarg;
break;
+ case 't':
+ cache = optarg;
+ break;
}
}
if (optind >= argc) {
}
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;
}
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;
}
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;
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;
}
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':
case 's':
snapshot_name = optarg;
break;
+ case 'p':
+ progress = 1;
+ break;
+ case 't':
+ cache = optarg;
+ break;
}
}
goto out;
}
+ qemu_progress_init(progress, 2.0);
+ qemu_progress_print(0, 100);
+
bs = qemu_mallocz(bs_n * sizeof(BlockDriverState *));
total_sectors = 0;
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;
}
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;
}
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;
}
}
sector_num += n;
+ qemu_progress_print(local_progress, 100);
}
/* signal EOF to align */
bdrv_write_compressed(out_bs, 0, NULL, 0);
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) {
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);
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)
{
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 {
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;
}
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;
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? */
written += pnum;
}
+ qemu_progress_print(local_progress, 100);
}
qemu_free(buf_old);
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
* 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);
{ 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");
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]) {