Commit 7621ed71b6 for qemu.org
commit 7621ed71b626aaaf3a539ca197e57b7b3250bd58
Author: Sanne Raymaekers <sraymaek@redhat.com>
Date: Fri Oct 16 14:37:49 2026 +0200
block/vmdk: skip RGD for stream optimized disks
For stream optimized disks the spec notes: "The rgdOffset should be
ignored because bit 1 of the flags field is not set." Where bit 1 of the
flags field indicates a redundant grain directory (RGD).[^1]
In short, stream optimized disks should have the RGD offset set to 0,
and only write the main grain directory.
This patch also corrects the comments around the code writing the grain
directories, and it makes the error thrown when it fails to write the
backup grain directory more explicit.
[^1] Virtual Disk Format 5.0 Technical Note.
Signed-off-by: Sanne Raymaekers <sraymaek@redhat.com>
Message-ID: <20261016123750.94769-3-sraymaek@redhat.com>
Reviewed-by: Kevin Wolf <kwolf@redhat.com>
Signed-off-by: Kevin Wolf <kwolf@redhat.com>
diff --git a/block/vmdk.c b/block/vmdk.c
index 7ca030c5fe..e090e122f1 100644
--- a/block/vmdk.c
+++ b/block/vmdk.c
@@ -2246,8 +2246,9 @@ vmdk_init_extent(BlockBackend *blk, int64_t filesize, bool flat, bool compress,
} else {
header.version = 1;
}
- header.flags = VMDK4_FLAG_RGD | VMDK4_FLAG_NL_DETECT
- | (compress ? VMDK4_FLAG_COMPRESS | VMDK4_FLAG_MARKER : 0)
+ header.flags = VMDK4_FLAG_NL_DETECT
+ | (compress ?
+ VMDK4_FLAG_COMPRESS | VMDK4_FLAG_MARKER : VMDK4_FLAG_RGD)
| (zeroed_grain ? VMDK4_FLAG_ZERO_GRAIN : 0);
header.compressAlgorithm = compress ? VMDK4_COMPRESSION_DEFLATE : 0;
header.capacity = filesize / BDRV_SECTOR_SIZE;
@@ -2262,8 +2263,16 @@ vmdk_init_extent(BlockBackend *blk, int64_t filesize, bool flat, bool compress,
header.desc_offset = 1;
header.desc_size = 20;
- header.rgd_offset = header.desc_offset + header.desc_size;
- header.gd_offset = header.rgd_offset + gd_sectors + (gt_size * gt_count);
+
+ if (compress) {
+ header.rgd_offset = 0;
+ header.gd_offset = header.desc_offset + header.desc_size;
+ } else {
+ header.rgd_offset = header.desc_offset + header.desc_size;
+ header.gd_offset =
+ header.rgd_offset + gd_sectors + (gt_size * gt_count);
+ }
+
header.grain_offset =
ROUND_UP(header.gd_offset + gd_sectors + (gt_size * gt_count),
header.granularity);
@@ -2303,21 +2312,26 @@ vmdk_init_extent(BlockBackend *blk, int64_t filesize, bool flat, bool compress,
goto exit;
}
- /* write grain directory */
+ /* write backup grain directory */
gd_buf_size = gd_sectors * BDRV_SECTOR_SIZE;
gd_buf = g_malloc0(gd_buf_size);
- for (i = 0, tmp = le64_to_cpu(header.rgd_offset) + gd_sectors;
- i < gt_count; i++, tmp += gt_size) {
- gd_buf[i] = cpu_to_le32(tmp);
- }
- ret = blk_co_pwrite(blk, le64_to_cpu(header.rgd_offset) * BDRV_SECTOR_SIZE,
- gd_buf_size, gd_buf, 0);
- if (ret < 0) {
- error_setg_errno(errp, -ret, "failed to write VMDK grain directory");
- goto exit;
+
+ if (!compress) {
+ for (i = 0, tmp = le64_to_cpu(header.rgd_offset) + gd_sectors;
+ i < gt_count; i++, tmp += gt_size) {
+ gd_buf[i] = cpu_to_le32(tmp);
+ }
+ ret = blk_co_pwrite(blk,
+ le64_to_cpu(header.rgd_offset) * BDRV_SECTOR_SIZE,
+ gd_buf_size, gd_buf, 0);
+ if (ret < 0) {
+ error_setg_errno(errp, -ret,
+ "failed to write VMDK backup grain directory");
+ goto exit;
+ }
}
- /* write backup grain directory */
+ /* write grain directory */
for (i = 0, tmp = le64_to_cpu(header.gd_offset) + gd_sectors;
i < gt_count; i++, tmp += gt_size) {
gd_buf[i] = cpu_to_le32(tmp);
@@ -2326,7 +2340,7 @@ vmdk_init_extent(BlockBackend *blk, int64_t filesize, bool flat, bool compress,
gd_buf_size, gd_buf, 0);
if (ret < 0) {
error_setg_errno(errp, -ret,
- "failed to write VMDK backup grain directory");
+ "failed to write VMDK grain directory");
}
ret = 0;