diff --git a/block.c b/block.c
index d132662f3103..fac0813140aa 100644
--- a/block.c
+++ b/block.c
@@ -5066,38 +5066,20 @@ int bdrv_get_flags(BlockDriverState *bs)
     return bs->open_flags;
 }

-int bdrv_has_zero_init_1(BlockDriverState *bs)
+int bdrv_known_zeroes_create(BlockDriverState *bs)
 {
-    return 1;
+    return BDRV_ZERO_CREATE;
 }

-int bdrv_has_zero_init(BlockDriverState *bs)
+int bdrv_known_zeroes_truncate(BlockDriverState *bs)
 {
-    if (!bs->drv) {
-        return 0;
-    }
-
-    /*
-     * If BS is a copy on write image, it is initialized to the
-     * contents of the base image, which may not be zeroes.  Likewise,
-     * encrypted images do not read as zero.
-     */
-    if (bs->backing || bs->encrypted) {
-        return 0;
-    }
-    if (bs->drv->bdrv_has_zero_init) {
-        return bs->drv->bdrv_has_zero_init(bs);
-    }
-    if (bs->file && bs->drv->is_filter) {
-        return bdrv_has_zero_init(bs->file->bs);
-    }
-
-    /* safe default */
-    return 0;
+    return BDRV_ZERO_CREATE | BDRV_ZERO_TRUNCATE;
 }

-int bdrv_has_zero_init_truncate(BlockDriverState *bs)
+int bdrv_known_zeroes(BlockDriverState *bs)
 {
+    int mask = BDRV_ZERO_CREATE | BDRV_ZERO_TRUNCATE;
+
     if (!bs->drv) {
         return 0;
     }
@@ -5113,9 +5095,12 @@ int bdrv_has_zero_init_truncate(BlockDriverState *bs)
     }

     /*
-     * If the current layer is smaller than the backing layer,
-     * truncation may expose backing data; treat failure to query size
-     * in the same manner. Otherwise, we can trust the driver.
+     * If BS is a copy on write image, it is initialized to the
+     * contents of the base image, which may not be zeroes, so
+     * ZERO_CREATE is not viable.  If the current layer is smaller
+     * than the backing layer, truncation may expose backing data,
+     * restricting ZERO_TRUNCATE; treat failure to query size in the
+     * same manner.  Otherwise, we can trust the driver.
      */

     if (bs->backing) {
@@ -5125,12 +5110,14 @@ int bdrv_has_zero_init_truncate(BlockDriverState *bs)
         if (back < 0 || curr < back) {
             return 0;
         }
+        mask = BDRV_ZERO_TRUNCATE;
     }
-    if (bs->drv->bdrv_has_zero_init_truncate) {
-        return bs->drv->bdrv_has_zero_init_truncate(bs);
+
+    if (bs->drv->bdrv_known_zeroes) {
+        return bs->drv->bdrv_known_zeroes(bs) & mask;
     }
     if (bs->file && bs->drv->is_filter) {
-        return bdrv_has_zero_init_truncate(bs->file->bs);
+        return bdrv_known_zeroes(bs->file->bs) & mask;
     }

     /* safe default */
diff --git a/block/file-posix.c b/block/file-posix.c
index ab82ee1a6718..ff9e39ab882f 100644
--- a/block/file-posix.c
+++ b/block/file-posix.c
@@ -3071,8 +3071,7 @@ BlockDriver bdrv_file = {
     .bdrv_close = raw_close,
     .bdrv_co_create = raw_co_create,
     .bdrv_co_create_opts = raw_co_create_opts,
-    .bdrv_has_zero_init = bdrv_has_zero_init_1,
-    .bdrv_has_zero_init_truncate = bdrv_has_zero_init_1,
+    .bdrv_known_zeroes = bdrv_known_zeroes_truncate,
     .bdrv_co_block_status = raw_co_block_status,
     .bdrv_co_invalidate_cache = raw_co_invalidate_cache,
     .bdrv_co_pwrite_zeroes = raw_co_pwrite_zeroes,
diff --git a/block/file-win32.c b/block/file-win32.c
index 77e8ff7b68ae..e9b8f3b2370b 100644
--- a/block/file-win32.c
+++ b/block/file-win32.c
@@ -635,8 +635,7 @@ BlockDriver bdrv_file = {
     .bdrv_refresh_limits = raw_probe_alignment,
     .bdrv_close         = raw_close,
     .bdrv_co_create_opts = raw_co_create_opts,
-    .bdrv_has_zero_init = bdrv_has_zero_init_1,
-    .bdrv_has_zero_init_truncate = bdrv_has_zero_init_1,
+    .bdrv_known_zeroes  = bdrv_known_zeroes_truncate,

     .bdrv_aio_preadv    = raw_aio_preadv,
     .bdrv_aio_pwritev   = raw_aio_pwritev,
diff --git a/block/nfs.c b/block/nfs.c
index 9a6311e27066..34ebe91d5b39 100644
--- a/block/nfs.c
+++ b/block/nfs.c
@@ -702,10 +702,10 @@ out:
     return ret;
 }

-static int nfs_has_zero_init(BlockDriverState *bs)
+static int nfs_known_zeroes(BlockDriverState *bs)
 {
     NFSClient *client = bs->opaque;
-    return client->has_zero_init;
+    return client->has_zero_init ? BDRV_ZERO_CREATE | BDRV_ZERO_TRUNCATE : 0;
 }

 /* Called (via nfs_service) with QemuMutex held.  */
@@ -869,8 +869,7 @@ static BlockDriver bdrv_nfs = {
     .bdrv_parse_filename            = nfs_parse_filename,
     .create_opts                    = &nfs_create_opts,

-    .bdrv_has_zero_init             = nfs_has_zero_init,
-    .bdrv_has_zero_init_truncate    = nfs_has_zero_init,
+    .bdrv_known_zeroes              = nfs_known_zeroes,
     .bdrv_get_allocated_file_size   = nfs_get_allocated_file_size,
     .bdrv_co_truncate               = nfs_file_co_truncate,

diff --git a/block/parallels.c b/block/parallels.c
index 7a01997659b0..dad6389c8481 100644
--- a/block/parallels.c
+++ b/block/parallels.c
@@ -835,7 +835,7 @@ static int parallels_open(BlockDriverState *bs, QDict *options, int flags,
         goto fail_options;
     }

-    if (!bdrv_has_zero_init_truncate(bs->file->bs)) {
+    if (!(bdrv_known_zeroes(bs->file->bs) & BDRV_ZERO_TRUNCATE)) {
         s->prealloc_mode = PRL_PREALLOC_MODE_FALLOCATE;
     }

@@ -906,7 +906,7 @@ static BlockDriver bdrv_parallels = {
     .bdrv_close		= parallels_close,
     .bdrv_child_perm          = bdrv_format_default_perms,
     .bdrv_co_block_status     = parallels_co_block_status,
-    .bdrv_has_zero_init       = bdrv_has_zero_init_1,
+    .bdrv_known_zeroes        = bdrv_known_zeroes_create,
     .bdrv_co_flush_to_os      = parallels_co_flush_to_os,
     .bdrv_co_readv  = parallels_co_readv,
     .bdrv_co_writev = parallels_co_writev,
diff --git a/block/qcow.c b/block/qcow.c
index fce89898681f..b0c9e212fdb1 100644
--- a/block/qcow.c
+++ b/block/qcow.c
@@ -1183,7 +1183,7 @@ static BlockDriver bdrv_qcow = {
     .bdrv_reopen_prepare    = qcow_reopen_prepare,
     .bdrv_co_create         = qcow_co_create,
     .bdrv_co_create_opts    = qcow_co_create_opts,
-    .bdrv_has_zero_init     = bdrv_has_zero_init_1,
+    .bdrv_known_zeroes      = bdrv_known_zeroes_create,
     .supports_backing       = true,
     .bdrv_refresh_limits    = qcow_refresh_limits,

diff --git a/block/qcow2.c b/block/qcow2.c
index 40aa751d1de7..9f2371925737 100644
--- a/block/qcow2.c
+++ b/block/qcow2.c
@@ -4914,10 +4914,11 @@ static ImageInfoSpecific *qcow2_get_specific_info(BlockDriverState *bs,
     return spec_info;
 }

-static int qcow2_has_zero_init(BlockDriverState *bs)
+static int qcow2_known_zeroes(BlockDriverState *bs)
 {
     BDRVQcow2State *s = bs->opaque;
     bool preallocated;
+    int r = BDRV_ZERO_TRUNCATE;

     if (qemu_in_coroutine()) {
         qemu_co_mutex_lock(&s->lock);
@@ -4933,9 +4934,9 @@ static int qcow2_has_zero_init(BlockDriverState *bs)
     }

     if (!preallocated) {
-        return 1;
+        return r | BDRV_ZERO_CREATE;
     } else {
-        return bdrv_has_zero_init(s->data_file->bs);
+        return r | bdrv_known_zeroes(s->data_file->bs);
     }
 }

@@ -5559,8 +5560,7 @@ BlockDriver bdrv_qcow2 = {
     .bdrv_child_perm      = bdrv_format_default_perms,
     .bdrv_co_create_opts  = qcow2_co_create_opts,
     .bdrv_co_create       = qcow2_co_create,
-    .bdrv_has_zero_init   = qcow2_has_zero_init,
-    .bdrv_has_zero_init_truncate = bdrv_has_zero_init_1,
+    .bdrv_known_zeroes    = qcow2_known_zeroes,
     .bdrv_co_block_status = qcow2_co_block_status,

     .bdrv_co_preadv_part    = qcow2_co_preadv_part,
diff --git a/block/qed.c b/block/qed.c
index d8c4e5fb1e85..b00cef2035b3 100644
--- a/block/qed.c
+++ b/block/qed.c
@@ -1672,8 +1672,7 @@ static BlockDriver bdrv_qed = {
     .bdrv_child_perm          = bdrv_format_default_perms,
     .bdrv_co_create           = bdrv_qed_co_create,
     .bdrv_co_create_opts      = bdrv_qed_co_create_opts,
-    .bdrv_has_zero_init       = bdrv_has_zero_init_1,
-    .bdrv_has_zero_init_truncate = bdrv_has_zero_init_1,
+    .bdrv_known_zeroes        = bdrv_known_zeroes_truncate,
     .bdrv_co_block_status     = bdrv_qed_co_block_status,
     .bdrv_co_readv            = bdrv_qed_co_readv,
     .bdrv_co_writev           = bdrv_qed_co_writev,
diff --git a/block/raw-format.c b/block/raw-format.c
index 3a76ec7dd21b..1334a7a2c224 100644
--- a/block/raw-format.c
+++ b/block/raw-format.c
@@ -409,14 +409,9 @@ static int raw_co_ioctl(BlockDriverState *bs, unsigned long int req, void *buf)
     return bdrv_co_ioctl(bs->file->bs, req, buf);
 }

-static int raw_has_zero_init(BlockDriverState *bs)
+static int raw_known_zeroes(BlockDriverState *bs)
 {
-    return bdrv_has_zero_init(bs->file->bs);
-}
-
-static int raw_has_zero_init_truncate(BlockDriverState *bs)
-{
-    return bdrv_has_zero_init_truncate(bs->file->bs);
+    return bdrv_known_zeroes(bs->file->bs);
 }

 static int coroutine_fn raw_co_create_opts(const char *filename, QemuOpts *opts,
@@ -577,8 +572,7 @@ BlockDriver bdrv_raw = {
     .bdrv_lock_medium     = &raw_lock_medium,
     .bdrv_co_ioctl        = &raw_co_ioctl,
     .create_opts          = &raw_create_opts,
-    .bdrv_has_zero_init   = &raw_has_zero_init,
-    .bdrv_has_zero_init_truncate = &raw_has_zero_init_truncate,
+    .bdrv_known_zeroes    = &raw_known_zeroes,
     .strong_runtime_opts  = raw_strong_runtime_opts,
     .mutable_opts         = mutable_opts,
 };
diff --git a/block/rbd.c b/block/rbd.c
index 027cbcc69520..6cd8e86bccec 100644
--- a/block/rbd.c
+++ b/block/rbd.c
@@ -1289,8 +1289,7 @@ static BlockDriver bdrv_rbd = {
     .bdrv_reopen_prepare    = qemu_rbd_reopen_prepare,
     .bdrv_co_create         = qemu_rbd_co_create,
     .bdrv_co_create_opts    = qemu_rbd_co_create_opts,
-    .bdrv_has_zero_init     = bdrv_has_zero_init_1,
-    .bdrv_has_zero_init_truncate = bdrv_has_zero_init_1,
+    .bdrv_known_zeroes      = bdrv_known_zeroes_truncate,
     .bdrv_get_info          = qemu_rbd_getinfo,
     .create_opts            = &qemu_rbd_create_opts,
     .bdrv_getlength         = qemu_rbd_getlength,
diff --git a/block/sheepdog.c b/block/sheepdog.c
index 522c16a93676..916e64abdd74 100644
--- a/block/sheepdog.c
+++ b/block/sheepdog.c
@@ -3229,8 +3229,7 @@ static BlockDriver bdrv_sheepdog = {
     .bdrv_close                   = sd_close,
     .bdrv_co_create               = sd_co_create,
     .bdrv_co_create_opts          = sd_co_create_opts,
-    .bdrv_has_zero_init           = bdrv_has_zero_init_1,
-    .bdrv_has_zero_init_truncate  = bdrv_has_zero_init_1,
+    .bdrv_known_zeroes            = bdrv_known_zeroes_truncate,
     .bdrv_getlength               = sd_getlength,
     .bdrv_get_allocated_file_size = sd_get_allocated_file_size,
     .bdrv_co_truncate             = sd_co_truncate,
@@ -3268,8 +3267,7 @@ static BlockDriver bdrv_sheepdog_tcp = {
     .bdrv_close                   = sd_close,
     .bdrv_co_create               = sd_co_create,
     .bdrv_co_create_opts          = sd_co_create_opts,
-    .bdrv_has_zero_init           = bdrv_has_zero_init_1,
-    .bdrv_has_zero_init_truncate  = bdrv_has_zero_init_1,
+    .bdrv_known_zeroes            = bdrv_known_zeroes_truncate,
     .bdrv_getlength               = sd_getlength,
     .bdrv_get_allocated_file_size = sd_get_allocated_file_size,
     .bdrv_co_truncate             = sd_co_truncate,
@@ -3307,8 +3305,7 @@ static BlockDriver bdrv_sheepdog_unix = {
     .bdrv_close                   = sd_close,
     .bdrv_co_create               = sd_co_create,
     .bdrv_co_create_opts          = sd_co_create_opts,
-    .bdrv_has_zero_init           = bdrv_has_zero_init_1,
-    .bdrv_has_zero_init_truncate  = bdrv_has_zero_init_1,
+    .bdrv_known_zeroes            = bdrv_known_zeroes_truncate,
     .bdrv_getlength               = sd_getlength,
     .bdrv_get_allocated_file_size = sd_get_allocated_file_size,
     .bdrv_co_truncate             = sd_co_truncate,
diff --git a/block/ssh.c b/block/ssh.c
index b4375cf7d2e5..e89dae39800c 100644
--- a/block/ssh.c
+++ b/block/ssh.c
@@ -1007,14 +1007,14 @@ static void ssh_close(BlockDriverState *bs)
     ssh_state_free(s);
 }

-static int ssh_has_zero_init(BlockDriverState *bs)
+static int ssh_known_zeroes(BlockDriverState *bs)
 {
     BDRVSSHState *s = bs->opaque;
     /* Assume false, unless we can positively prove it's true. */
     int has_zero_init = 0;

     if (s->attrs->type == SSH_FILEXFER_TYPE_REGULAR) {
-        has_zero_init = 1;
+        has_zero_init = BDRV_ZERO_CREATE | BDRV_ZERO_TRUNCATE;
     }

     return has_zero_init;
@@ -1390,8 +1390,7 @@ static BlockDriver bdrv_ssh = {
     .bdrv_co_create               = ssh_co_create,
     .bdrv_co_create_opts          = ssh_co_create_opts,
     .bdrv_close                   = ssh_close,
-    .bdrv_has_zero_init           = ssh_has_zero_init,
-    .bdrv_has_zero_init_truncate  = ssh_has_zero_init,
+    .bdrv_known_zeroes            = ssh_known_zeroes,
     .bdrv_co_readv                = ssh_co_readv,
     .bdrv_co_writev               = ssh_co_writev,
     .bdrv_getlength               = ssh_getlength,
diff --git a/block/vdi.c b/block/vdi.c
index 0142da723315..df8f62624ccf 100644
--- a/block/vdi.c
+++ b/block/vdi.c
@@ -989,14 +989,14 @@ static void vdi_close(BlockDriverState *bs)
     error_free(s->migration_blocker);
 }

-static int vdi_has_zero_init(BlockDriverState *bs)
+static int vdi_known_zeroes(BlockDriverState *bs)
 {
     BDRVVdiState *s = bs->opaque;

     if (s->header.image_type == VDI_TYPE_STATIC) {
-        return bdrv_has_zero_init(bs->file->bs);
+        return bdrv_known_zeroes(bs->file->bs) & BDRV_ZERO_CREATE;
     } else {
-        return 1;
+        return BDRV_ZERO_CREATE;
     }
 }

@@ -1040,7 +1040,7 @@ static BlockDriver bdrv_vdi = {
     .bdrv_child_perm          = bdrv_format_default_perms,
     .bdrv_co_create      = vdi_co_create,
     .bdrv_co_create_opts = vdi_co_create_opts,
-    .bdrv_has_zero_init  = vdi_has_zero_init,
+    .bdrv_known_zeroes   = vdi_known_zeroes,
     .bdrv_co_block_status = vdi_co_block_status,
     .bdrv_make_empty = vdi_make_empty,

diff --git a/block/vhdx.c b/block/vhdx.c
index f02d2611bef8..4e8320c1b855 100644
--- a/block/vhdx.c
+++ b/block/vhdx.c
@@ -1365,7 +1365,7 @@ static coroutine_fn int vhdx_co_writev(BlockDriverState *bs, int64_t sector_num,
                 /* Queue another write of zero buffers if the underlying file
                  * does not zero-fill on file extension */

-                if (bdrv_has_zero_init_truncate(bs->file->bs) == 0) {
+                if (!(bdrv_known_zeroes(bs->file->bs) & BDRV_ZERO_TRUNCATE)) {
                     use_zero_buffers = true;

                     /* zero fill the front, if any */
@@ -1720,8 +1720,8 @@ static int vhdx_create_bat(BlockBackend *blk, BDRVVHDXState *s,
     }

     if (type == VHDX_TYPE_FIXED ||
-                use_zero_blocks ||
-                bdrv_has_zero_init(blk_bs(blk)) == 0) {
+        use_zero_blocks ||
+        !(bdrv_known_zeroes(blk_bs(blk)) & BDRV_ZERO_CREATE)) {
         /* for a fixed file, the default BAT entry is not zero */
         s->bat = g_try_malloc0(length);
         if (length && s->bat == NULL) {
@@ -2162,7 +2162,7 @@ static int coroutine_fn vhdx_co_check(BlockDriverState *bs,
     return 0;
 }

-static int vhdx_has_zero_init(BlockDriverState *bs)
+static int vhdx_known_zeroes(BlockDriverState *bs)
 {
     BDRVVHDXState *s = bs->opaque;
     int state;
@@ -2173,17 +2173,17 @@ static int vhdx_has_zero_init(BlockDriverState *bs)
      * therefore enough to check the first BAT entry.
      */
     if (!s->bat_entries) {
-        return 1;
+        return BDRV_ZERO_CREATE;
     }

     state = s->bat[0] & VHDX_BAT_STATE_BIT_MASK;
     if (state == PAYLOAD_BLOCK_FULLY_PRESENT) {
         /* Fixed subformat */
-        return bdrv_has_zero_init(bs->file->bs);
+        return bdrv_known_zeroes(bs->file->bs) & BDRV_ZERO_CREATE;
     }

     /* Dynamic subformat */
-    return 1;
+    return BDRV_ZERO_CREATE;
 }

 static QemuOptsList vhdx_create_opts = {
@@ -2239,7 +2239,7 @@ static BlockDriver bdrv_vhdx = {
     .bdrv_co_create_opts    = vhdx_co_create_opts,
     .bdrv_get_info          = vhdx_get_info,
     .bdrv_co_check          = vhdx_co_check,
-    .bdrv_has_zero_init     = vhdx_has_zero_init,
+    .bdrv_known_zeroes      = vhdx_known_zeroes,

     .create_opts            = &vhdx_create_opts,
 };
diff --git a/block/vmdk.c b/block/vmdk.c
index 20e909d99794..ca59f50413d2 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -2815,7 +2815,7 @@ static int64_t vmdk_get_allocated_file_size(BlockDriverState *bs)
     return ret;
 }

-static int vmdk_has_zero_init(BlockDriverState *bs)
+static int vmdk_known_zeroes(BlockDriverState *bs)
 {
     int i;
     BDRVVmdkState *s = bs->opaque;
@@ -2824,12 +2824,13 @@ static int vmdk_has_zero_init(BlockDriverState *bs)
      * return 0. */
     for (i = 0; i < s->num_extents; i++) {
         if (s->extents[i].flat) {
-            if (!bdrv_has_zero_init(s->extents[i].file->bs)) {
+            if (!(bdrv_known_zeroes(s->extents[i].file->bs) &
+                  BDRV_ZERO_CREATE)) {
                 return 0;
             }
         }
     }
-    return 1;
+    return BDRV_ZERO_CREATE;
 }

 static ImageInfo *vmdk_get_extent_info(VmdkExtent *extent)
@@ -3052,7 +3053,7 @@ static BlockDriver bdrv_vmdk = {
     .bdrv_co_flush_to_disk        = vmdk_co_flush,
     .bdrv_co_block_status         = vmdk_co_block_status,
     .bdrv_get_allocated_file_size = vmdk_get_allocated_file_size,
-    .bdrv_has_zero_init           = vmdk_has_zero_init,
+    .bdrv_known_zeroes            = vmdk_known_zeroes,
     .bdrv_get_specific_info       = vmdk_get_specific_info,
     .bdrv_refresh_limits          = vmdk_refresh_limits,
     .bdrv_get_info                = vmdk_get_info,
diff --git a/block/vpc.c b/block/vpc.c
index a65550298e19..f4741e07bfb2 100644
--- a/block/vpc.c
+++ b/block/vpc.c
@@ -1173,15 +1173,15 @@ fail:
 }


-static int vpc_has_zero_init(BlockDriverState *bs)
+static int vpc_known_zeroes(BlockDriverState *bs)
 {
     BDRVVPCState *s = bs->opaque;
     VHDFooter *footer =  (VHDFooter *) s->footer_buf;

     if (be32_to_cpu(footer->type) == VHD_FIXED) {
-        return bdrv_has_zero_init(bs->file->bs);
+        return bdrv_known_zeroes(bs->file->bs) & BDRV_ZERO_CREATE;
     } else {
-        return 1;
+        return BDRV_ZERO_CREATE;
     }
 }

@@ -1249,7 +1249,7 @@ static BlockDriver bdrv_vpc = {
     .bdrv_get_info          = vpc_get_info,

     .create_opts            = &vpc_create_opts,
-    .bdrv_has_zero_init     = vpc_has_zero_init,
+    .bdrv_known_zeroes      = vpc_known_zeroes,
     .strong_runtime_opts    = vpc_strong_runtime_opts,
 };

diff --git a/blockdev.c b/blockdev.c
index c6a727cca99d..90a17e7f7bce 100644
--- a/blockdev.c
+++ b/blockdev.c
@@ -4001,7 +4001,7 @@ void qmp_drive_mirror(DriveMirror *arg, Error **errp)

     zero_target = (arg->sync == MIRROR_SYNC_MODE_FULL &&
                    (arg->mode == NEW_IMAGE_MODE_EXISTING ||
-                    !bdrv_has_zero_init(target_bs)));
+                    !(bdrv_known_zeroes(target_bs) & BDRV_ZERO_CREATE)));


     /* Honor bdrv_try_set_aio_context() context acquisition requirements. */
diff --git a/include/block/block.h b/include/block/block.h
index 6cd566324d95..a6a227f50678 100644
--- a/include/block/block.h
+++ b/include/block/block.h
@@ -85,6 +85,28 @@ typedef enum {
     BDRV_REQ_MASK               = 0x3ff,
 } BdrvRequestFlags;

+typedef enum {
+    /*
+     * bdrv_known_zeroes() should include this bit if the contents of
+     * a freshly-created image with no backing file reads as all
+     * zeroes without any additional effort.  If .bdrv_co_truncate is
+     * set, then this must be clear if BDRV_ZERO_TRUNCATE is clear.
+     * Since this bit is only reliable at image creation, a driver may
+     * return this bit even for existing images that do not currently
+     * read as zero.
+     */
+    BDRV_ZERO_CREATE        = 0x1,
+
+    /*
+     * bdrv_known_zeroes() should include this bit if growing an image
+     * with PREALLOC_MODE_OFF (either with no backing file, or beyond
+     * the size of the backing file) will read the new data as all
+     * zeroes without any additional effort.  This bit only matters
+     * for drivers that set .bdrv_co_truncate.
+     */
+    BDRV_ZERO_TRUNCATE      = 0x2,
+} BdrvZeroFlags;
+
 typedef struct BlockSizes {
     uint32_t phys;
     uint32_t log;
@@ -430,9 +452,9 @@ void bdrv_drain_all(void);

 int bdrv_pdiscard(BdrvChild *child, int64_t offset, int64_t bytes);
 int bdrv_co_pdiscard(BdrvChild *child, int64_t offset, int64_t bytes);
-int bdrv_has_zero_init_1(BlockDriverState *bs);
-int bdrv_has_zero_init(BlockDriverState *bs);
-int bdrv_has_zero_init_truncate(BlockDriverState *bs);
+int bdrv_known_zeroes_create(BlockDriverState *bs);
+int bdrv_known_zeroes_truncate(BlockDriverState *bs);
+int bdrv_known_zeroes(BlockDriverState *bs);
 bool bdrv_unallocated_blocks_are_zero(BlockDriverState *bs);
 bool bdrv_can_write_zeroes_with_unmap(BlockDriverState *bs);
 int bdrv_block_status(BlockDriverState *bs, int64_t offset,
diff --git a/include/block/block_int.h b/include/block/block_int.h
index 77ab45dc87cf..47b34860bf95 100644
--- a/include/block/block_int.h
+++ b/include/block/block_int.h
@@ -441,19 +441,8 @@ struct BlockDriver {

     void (*bdrv_refresh_limits)(BlockDriverState *bs, Error **errp);

-    /*
-     * Returns 1 if newly created images are guaranteed to contain only
-     * zeros, 0 otherwise.
-     * Must return 0 if .bdrv_co_truncate is set and
-     * .bdrv_has_zero_init_truncate() returns 0.
-     */
-    int (*bdrv_has_zero_init)(BlockDriverState *bs);
-
-    /*
-     * Returns 1 if new areas added by growing the image with
-     * PREALLOC_MODE_OFF contain only zeros, 0 otherwise.
-     */
-    int (*bdrv_has_zero_init_truncate)(BlockDriverState *bs);
+    /* Returns bitwise-OR of BdrvZeroFlags. */
+    int (*bdrv_known_zeroes)(BlockDriverState *bs);

     /* Remove fd handlers, timers, and other event loop callbacks so the event
      * loop is no longer in use.  Called with no in-flight requests and in
diff --git a/qemu-img.c b/qemu-img.c
index e0bfc33ef4f6..e60217e6c382 100644
--- a/qemu-img.c
+++ b/qemu-img.c
@@ -1987,7 +1987,8 @@ static int convert_do_copy(ImgConvertState *s)
     /* Check whether we have zero initialisation or can get it efficiently */
     if (!s->has_zero_init && s->target_is_new && s->min_sparse &&
         !s->target_has_backing) {
-        s->has_zero_init = bdrv_has_zero_init(blk_bs(s->target));
+        s->has_zero_init = !!(bdrv_known_zeroes(blk_bs(s->target)) &
+                              BDRV_ZERO_CREATE);
     }

     if (!s->has_zero_init && !s->target_has_backing &&
diff --git a/tests/qemu-iotests/122 b/tests/qemu-iotests/122
index dfa350936fe6..7cb09309948f 100755
--- a/tests/qemu-iotests/122
+++ b/tests/qemu-iotests/122
@@ -267,7 +267,7 @@ echo
 # Keep source zero
 _make_test_img 64M

-# Output is not zero, but has bdrv_has_zero_init() == 1
+# Output is not zero, but has bdrv_known_zeroes() including BDRV_ZERO_CREATE
 TEST_IMG="$TEST_IMG".orig _make_test_img 64M
 $QEMU_IO -c "write -P 42 0 64k" "$TEST_IMG".orig | _filter_qemu_io

diff --git a/tests/qemu-iotests/188 b/tests/qemu-iotests/188
index afca44df5427..9656969fef4a 100755
--- a/tests/qemu-iotests/188
+++ b/tests/qemu-iotests/188
@@ -71,7 +71,7 @@ $QEMU_IO --object $SECRETALT -c "read -P 0xa 0 $size" --image-opts $IMGSPEC | _f
 _cleanup_test_img

 echo
-echo "== verify that has_zero_init returns false when preallocating =="
+echo "== verify that known_zeroes returns 0 when preallocating =="

 # Empty source file
 if [ -n "$TEST_IMG_FILE" ]; then
diff --git a/tests/qemu-iotests/188.out b/tests/qemu-iotests/188.out
index c568ef370145..f7da30440c65 100644
--- a/tests/qemu-iotests/188.out
+++ b/tests/qemu-iotests/188.out
@@ -16,7 +16,7 @@ read 16777216/16777216 bytes at offset 0
 == verify open failure with wrong password ==
 qemu-io: can't open: Invalid password, cannot unlock any keyslot

-== verify that has_zero_init returns false when preallocating ==
+== verify that known_zeroes returns 0 when preallocating ==
 Formatting 'TEST_DIR/t.IMGFMT.orig', fmt=IMGFMT size=16777216
 Images are identical.
 *** done
