On Fri, Oct 30, 2026 at 06:10:29PM +0100, Niklas Cassel wrote:
> @@ -432,6 +518,96 @@ qcow2_read_extensions(BlockDriverState *bs, uint64_t
> start_offset,
> break;
> }
>
> + case QCOW2_EXT_MAGIC_ZONED_FORMAT:
> + {
> + if (ext.len != sizeof(zoned_ext)) {
> + error_setg(errp, "zoned_ext: unexpected len=%" PRIu32 " "
> + "(expected %zu)", ext.len, sizeof(zoned_ext));
> + return -EINVAL;
> + }
> + ret = bdrv_pread(bs->file, offset, ext.len, &zoned_ext, 0);
> + if (ret < 0) {
> + error_setg_errno(errp, -ret, "zoned_ext: "
> + "Could not read ext header");
> + return ret;
> + }
> +
> + zoned_ext.zone_size = be64_to_cpu(zoned_ext.zone_size);
> + zoned_ext.zone_capacity = be64_to_cpu(zoned_ext.zone_capacity);
> + zoned_ext.conventional_zones =
> + be32_to_cpu(zoned_ext.conventional_zones);
> + zoned_ext.nr_zones = be32_to_cpu(zoned_ext.nr_zones);
> + zoned_ext.max_open_zones = be32_to_cpu(zoned_ext.max_open_zones);
> + zoned_ext.max_active_zones =
> + be32_to_cpu(zoned_ext.max_active_zones);
> + /*
> + * A zoned image has the zoned incompatible feature bit set,
> + * which a version 2 image cannot have.
> + */
> + if (zoned_ext.zoned == QCOW2_Z_HM &&
> + !(s->incompatible_features & QCOW2_INCOMPAT_ZONED_FORMAT)) {
> + error_setg(errp, "Zoned extension header without the zoned "
> + "incompatible feature bit");
> + return -EINVAL;
> + }
> +
> + /*
> + * Validate the header before it becomes the driver state.
> + * qcow2_check_zone_options() also clamps the zone resource
> + * limits, so the driver has to pick up the checked copy.
> + */
> + if (!qcow2_check_zone_options(&zoned_ext, s->cluster_size)) {
> + error_setg(errp, "Invalid zoned extension header");
Please pass errp into qcow2_check_zone_options() and use error_setg()
inside the function instead of warn_report() for consistent error
reporting.
> @@ -237,6 +242,62 @@ typedef struct Qcow2CryptoHeaderExtension {
> uint64_t length;
> } QEMU_PACKED Qcow2CryptoHeaderExtension;
>
> +/* Maximum number of zones a zoned image can have. */
> +#define QCOW2_MAX_NR_ZONES (1U << 20)
> +
> +typedef struct Qcow2ZonedHeaderExtension {
> + /* Zoned device attributes */
> + uint8_t zoned;
> + uint8_t reserved[3];
> + uint32_t nr_zones;
> + uint64_t zone_size;
> + uint64_t zone_capacity;
> + uint64_t zonedmeta_offset;
> + uint32_t conventional_zones;
> + uint32_t max_active_zones;
> + uint32_t max_open_zones;
> +} QEMU_PACKED Qcow2ZonedHeaderExtension;
> +
> +typedef struct Qcow2ZoneListEntry {
> + QTAILQ_ENTRY(Qcow2ZoneListEntry) exp_open_zone_entry;
> + QTAILQ_ENTRY(Qcow2ZoneListEntry) imp_open_zone_entry;
> + QTAILQ_ENTRY(Qcow2ZoneListEntry) closed_zone_entry;
> +} Qcow2ZoneListEntry;
This and the other struct definitions and fields that are not yet used
should be moved to the patches that introduce code that needs them. It
is not possible to meaningfully review them here before they are used.
> diff --git a/tests/qemu-iotests/031.out b/tests/qemu-iotests/031.out
> index 0054c2ed97..02a68b34f7 100644
> --- a/tests/qemu-iotests/031.out
> +++ b/tests/qemu-iotests/031.out
> @@ -117,7 +117,7 @@ header_length 112
>
> Header extension:
> magic 0x6803f857 (Feature table)
> -length 384
> +length 432
> data <binary>
This looks suspicious but it's because qcow2_update_header() eagerly
advertises all features it knows about, even if the image file does not
use them. This means newly created non-zoned files will now have the
name for QCOW2_INCOMPAT_ZONED_FORMAT_BITNR even though they will never
use it.
There is no harm in this change. In the future maybe
qcow2_update_header() should only include feature bits that are actually
used, but you don't need to implement this.
Feel free to add by R-b to the next revision:
Reviewed-by: Stefan Hajnoczi <[email protected]>