]> git.proxmox.com Git - qemu.git/blobdiff - qemu-img.c
qapi: Convert expire_password
[qemu.git] / qemu-img.c
index 2fee782217641f32c8cff954be74b284f81da46d..01cc0d35ad7d964196d0cbdc9a08ca0c13197cf0 100644 (file)
@@ -82,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"
@@ -330,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.");
@@ -570,6 +573,48 @@ static int is_allocated_sectors(const uint8_t *buf, int n, int *pnum)
     return v;
 }
 
+/*
+ * 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;
+}
+
 /*
  * Compares two buffers sector by sector. Returns 0 if the first sector of both
  * buffers matches, non-zero otherwise.
@@ -620,6 +665,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";
@@ -627,7 +673,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;
         }
@@ -662,6 +708,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;
@@ -767,6 +826,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");
@@ -780,6 +841,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 */
@@ -870,7 +940,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;
                 }
 
@@ -888,8 +959,8 @@ static int img_convert(int argc, char **argv)
                 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;
                 }
             }
@@ -952,7 +1023,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
@@ -968,10 +1040,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;
                     }
                 }
@@ -1347,6 +1420,8 @@ 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;
@@ -1357,6 +1432,8 @@ static int img_rebase(int argc, char **argv)
         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));
@@ -1375,16 +1452,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 */