From nobody Sat Sep 26 20:03:23 2026 Delivered-To: importer@patchew.org Authentication-Results: mx.zohomail.com; spf=pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) smtp.mailfrom=qemu-devel-bounces+importer=patchew.org@nongnu.org Return-Path: Received: from lists1p.gnu.org (lists1p.gnu.org [209.51.188.17]) by mx.zohomail.com with SMTPS id 17900816806941008.0301140500562; Tue, 22 Sep 2026 05:54:40 -0700 (PDT) Received: from localhost ([::1] helo=lists1p.gnu.org) by lists1p.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1x900t-0000lw-4T; Tue, 22 Sep 2026 08:54:23 -0400 Received: from eggs.gnu.org ([2001:470:142:3::10]) by lists1p.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1x900q-0000lf-TO; Tue, 22 Sep 2026 08:54:21 -0400 Received: from proxmox-new.maurer-it.com ([94.136.29.106]) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1x900o-0003AP-Jt; Tue, 22 Sep 2026 08:54:20 -0400 Received: from proxmox-new.maurer-it.com (localhost.localdomain [127.0.0.1]) by proxmox-new.maurer-it.com (Proxmox) with ESMTP id B0775421F0; Tue, 22 Sep 2026 14:54:07 +0200 (CEST) From: "Max R. Carrara" To: qemu-devel@nongnu.org Cc: Fiona Ebner , Fam Zheng , Kevin Wolf , Hanna Reitz , qemu-block@nongnu.org Subject: [PATCH] block: vmdk: support raw device mappings in vmdk format Date: Tue, 22 Sep 2026 14:54:03 +0200 Message-ID: <20260922-2026-09-support-rdm-import-from-esxi-v1-v1-1-2c8c8644d233@proxmox.com> X-Mailer: git-send-email 2.47.3 MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" X-Change-ID: 20260922-2026-09-support-rdm-import-from-esxi-v1-7f601c111381 X-Mailer: b4 0.14.2 Content-Transfer-Encoding: quoted-printable X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1790081646394 Received-SPF: pass (zohomail.com: domain of gnu.org designates 209.51.188.17 as permitted sender) client-ip=209.51.188.17; envelope-from=qemu-devel-bounces+importer=patchew.org@nongnu.org; helo=lists1p.gnu.org; Received-SPF: pass client-ip=94.136.29.106; envelope-from=m.carrara@proxmox.com; helo=proxmox-new.maurer-it.com X-Spam_score_int: -41 X-Spam_score: -4.2 X-Spam_bar: ---- X-Spam_report: (-4.2 / 5.0 requ) BAYES_00=-1.9, RCVD_IN_DNSWL_MED=-2.3, SPF_HELO_NONE=0.001, SPF_PASS=-0.001 autolearn=ham autolearn_force=no X-Spam_action: no action X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: qemu development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+importer=patchew.org@nongnu.org Sender: qemu-devel-bounces+importer=patchew.org@nongnu.org X-ZM-MESSAGEID: 1790081683096158500 Implement support for raw device mappings (RDMs) in QEMU's VMDK block driver. Handle both virtual and physical compatibility modes. This allows us to import RDM disks from ESXi through our FUSE layer without any additional changes inside Proxmox VE. According to the vSphere docs, an RDM provides a mechanism for a VM to have direct access to a LUN on physical storage [rdm]. What it really does is simply pass through a block device to a VM. The VMDK for an RDM will reference a so-called "pointer file" in its extent description, with the type `VMFSRDM`, which is created alongide the VMDK file. This pointer file then points to the block device, kind of like a symlink. - Physical compatibility mode In the "Disk DescriptorFile" section: `createType=3D"vmfsPassthroughRawDeviceMap"` This is the simplest of the two modes; the block device is simply passed through to the VM. RDMs in this compat mode are excluded from snapshots. On ESXi, such RDMs can be created with: vmkfstools --createrdmpassthru \ /vmfs/devices/disks/... [PATH TO NEW VMDK] - Virtual compatibility mode In the "Disk DescriptorFile" section: `createType=3D"vmfsRawDeviceMap"` This mode allows RDMs to be included in snapshots. When making a snapshot, the new delta is written to a SESPARSE file on the datastore instead, and the data on the passthrough disk will remain unmodified from that point onwards. On ESXi, such RDMs can be created with: vmkfstools --createrdm \ /vmfs/devices/disks/... [PATH TO NEW VMDK] In order to support both of these modes, make the VMDK block driver aware of the `vmfsPassthroughRawDeviceMap` and `vmfsRawDeviceMap` createTypes. Then, handle the `VMFSRDM` extent type in the extents parser. The `VMFSRDM` extent type works similar to the regular `VMFS` and `FLAT` types. The entire extent's description consists of four fields; the referenced file points to a raw block device and thus stores no metadata. Therefore, this file can be treated just like a `FLAT` extent and read directly. [rdm]: https://techdocs.broadcom.com/us/en/vmware-cis/vsphere/vsphere/7-0/v= sphere-storage/raw-device-mapping-in-vsphere.html Signed-off-by: Max R. Carrara --- Hello! Some context: Our FUSE layer in Proxmox VE allows us to mount the VMware filesystem on ESXi directly on our side, which makes migrating VMs from ESXi to PVE possible using our import tools. However, we currently cannot import RDM disks, since QEMU's VMDK driver does not support them. I was advised to upstream this here directly instead of patching our downstream version. So, I'd appreciate any feedback! :) How I tested this: 1. Create a physical and virtual RDM each on ESXi 2. Assign the RDMs to a plain Debian VM 3. Boot up the VM and write over the disks with /dev/urandom from inside the VM 4. Run a `sha256sum` for each disk inside the VM and note down the checksums 5. Configure the ESXi host on Proxmox VE 6. Import the Debian VM with the two RDM disks 7. Boot up the VM once the import has been completed (if doing a non-live import) and run the checksums again Alternatively, it is also possible to use our [tooling] directly; I can provide the steps for that, if desired. That makes it possible to convert images directly, run `qemu-img compare` w/ the mounted disks, etc. FWIW, I did that as well and got matching images. Thanks a lot! :) Best regards, Max R. Carrara [tooling]: https://git.proxmox.com/?p=3Dpve-esxi-import-tools.git;a=3Dtree;= h=3Drefs/heads/master;hb=3Drefs/heads/master block/vmdk.c | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/block/vmdk.c b/block/vmdk.c index cd8b4ec7c88187188e79801bd71dd9da42223db0..22f67e351bee2582023653709fa= 8c4859081b4c4 100644 --- a/block/vmdk.c +++ b/block/vmdk.c @@ -1174,6 +1174,7 @@ vmdk_parse_extents(const char *desc, BlockDriverState= *bs, QDict *options, * RW [size in sectors] VMFS "file-name.vmdk" * RW [size in sectors] VMFSSPARSE "file-name.vmdk" * RW [size in sectors] SESPARSE "file-name.vmdk" + * RW [size in sectors] VMFSRDM "file-name.vmdk" */ flat_offset =3D -1; matches =3D sscanf(p, "%10s %" SCNd64 " %10s \"%511[^\n\r\"]\" %" = SCNd64, @@ -1184,7 +1185,7 @@ vmdk_parse_extents(const char *desc, BlockDriverState= *bs, QDict *options, if (matches !=3D 5 || flat_offset < 0) { goto invalid; } - } else if (!strcmp(type, "VMFS")) { + } else if (!strcmp(type, "VMFS") || !strcmp(type, "VMFSRDM")) { if (matches =3D=3D 4) { flat_offset =3D 0; } else { @@ -1197,7 +1198,7 @@ vmdk_parse_extents(const char *desc, BlockDriverState= *bs, QDict *options, if (sectors <=3D 0 || (strcmp(type, "FLAT") && strcmp(type, "SPARSE") && strcmp(type, "VMFS") && strcmp(type, "VMFSSPARSE") && - strcmp(type, "SESPARSE")) || + strcmp(type, "SESPARSE") && strcmp(type, "VMFSRDM")) || (strcmp(access, "RW"))) { continue; } @@ -1224,7 +1225,7 @@ vmdk_parse_extents(const char *desc, BlockDriverState= *bs, QDict *options, assert(ret < 32); extent_role =3D BDRV_CHILD_DATA; - if (strcmp(type, "FLAT") !=3D 0 && strcmp(type, "VMFS") !=3D 0) { + if (strcmp(type, "FLAT") !=3D 0 && strcmp(type, "VMFS") !=3D 0 && = strcmp(type, "VMFSRDM")) { /* non-flat extents have metadata */ extent_role |=3D BDRV_CHILD_METADATA; } @@ -1242,7 +1243,7 @@ vmdk_parse_extents(const char *desc, BlockDriverState= *bs, QDict *options, } /* save to extents array */ - if (!strcmp(type, "FLAT") || !strcmp(type, "VMFS")) { + if (!strcmp(type, "FLAT") || !strcmp(type, "VMFS") || !strcmp(type= , "VMFSRDM")) { /* FLAT extent */ ret =3D vmdk_add_extent(bs, extent_file, true, sectors, @@ -1334,7 +1335,9 @@ vmdk_open_desc_file(BlockDriverState *bs, int flags, = char *buf, QDict *options, strcmp(ct, "vmfsSparse") && strcmp(ct, "seSparse") && strcmp(ct, "twoGbMaxExtentSparse") && - strcmp(ct, "twoGbMaxExtentFlat")) { + strcmp(ct, "twoGbMaxExtentFlat") && + strcmp(ct, "vmfsRawDeviceMap") && + strcmp(ct, "vmfsPassthroughRawDeviceMap")) { error_setg(errp, "Unsupported image type '%s'", ct); ret =3D -ENOTSUP; goto exit; --- base-commit: c3d48b7d1e89604920e5b81b91140c2ad39a1943 change-id: 20260922-2026-09-support-rdm-import-from-esxi-v1-7f601c111381 --