A zone append cannot cross a zone boundary, so a request larger than a zone can never be carried out.
Nothing rejects one. The block layer does not look at the length at all, and raw_co_zone_append() writes qiov->size bytes at the write pointer once it has validated the offset, so the write runs past the end of the zone: on a disk with 8 MiB zones, an append of 8 MiB plus one block fills the zone and advances the write pointer of the zone after it. virtio-blk does compare the length, but against BlockLimits.max_append_sectors, which is how much the backend takes in one command and not how large a zone is. For a Linux block device that value is never larger than a zone, so virtio-blk is protected by what the backend happens to report rather than by the check it makes. Check the length in bdrv_co_zone_append(), next to the check that an append is a multiple of the sector size. Both are conditions of the operation rather than of a medium, so the block layer is where they hold for every caller and where no driver has to repeat them. The exact bound is tighter: a request also has to fit in the part of the zone that is still writable, which depends on the write pointer and so stays with the drivers. Signed-off-by: Niklas Cassel <[email protected]> --- block/io.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/block/io.c b/block/io.c index b9d0b3fd7b..d7c403bc08 100644 --- a/block/io.c +++ b/block/io.c @@ -3365,6 +3365,18 @@ int coroutine_fn bdrv_co_zone_append(BlockDriverState *bs, int64_t *offset, return -EINVAL; } + /* + * An append cannot cross a zone boundary, so one that is larger than a + * zone can never be carried out, wherever the write pointer of the zone + * is. The exact bound is the part of the zone that is still writable, + * which only a driver can check, as only it holds the write pointer. + * zone_size is zero when the device is not zoned, which is reported as + * unsupported below. + */ + if (bs->bl.zone_size && qiov->size > bs->bl.zone_size) { + return -EINVAL; + } + bdrv_inc_in_flight(bs); if (!drv || !drv->bdrv_co_zone_append || bs->bl.zoned == BLK_Z_NONE) { co.ret = -ENOTSUP; -- 2.55.0
