nbdkit/SOURCES/0031-vddk-Implement-VMDK-cr...

539 lines
18 KiB
Diff

From cf58241f19ed179e48c53f4d6c71df47dd2f5931 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Tue, 18 Jan 2022 08:58:15 +0000
Subject: [PATCH] vddk: Implement VMDK creation
Add the create=(true|false) parameter. Setting this to true causes
the VMDK local file to be created. Currently this is done on first
connection, but we might change that in future. Various other
parameters can be used to control aspects of the VMDK file.
(cherry picked from commit a39d5773afc3ebab7e5768118a2bccb89a654585)
---
plugins/vddk/nbdkit-vddk-plugin.pod | 102 ++++++++++++++++++++++-
plugins/vddk/vddk-structs.h | 32 +++++++
plugins/vddk/vddk-stubs.h | 7 ++
plugins/vddk/vddk.c | 125 ++++++++++++++++++++++++++++
plugins/vddk/vddk.h | 5 ++
tests/Makefile.am | 6 +-
tests/dummy-vddk.c | 10 +++
tests/test-vddk-real-create.sh | 70 ++++++++++++++++
8 files changed, 354 insertions(+), 3 deletions(-)
create mode 100755 tests/test-vddk-real-create.sh
diff --git a/plugins/vddk/nbdkit-vddk-plugin.pod b/plugins/vddk/nbdkit-vddk-plugin.pod
index acec0bd2..b96192d0 100644
--- a/plugins/vddk/nbdkit-vddk-plugin.pod
+++ b/plugins/vddk/nbdkit-vddk-plugin.pod
@@ -6,7 +6,11 @@ nbdkit-vddk-plugin - nbdkit VMware VDDK plugin
nbdkit vddk [file=]FILENAME
[compression=none|zlib|fastlz|skipz]
- [config=FILENAME] [cookie=COOKIE] [libdir=LIBRARY]
+ [config=FILENAME] [cookie=COOKIE]
+ [create=true] [create-adapter-type=ide|scsi-buslogic|...]
+ [create-hwversion=workstation4|workstation5|...]
+ [create-size=...] [create-type=monolithic-sparse|...]
+ [libdir=LIBRARY]
[nfchostport=PORT] [single-link=true]
[password=PASSWORD | password=- | password=+FILENAME |
password=-FD]
@@ -26,7 +30,7 @@ yourself (see L</LIBRARY LOCATION> below).
=head1 EXAMPLES
-=head2 Open a local VMDK file
+=head2 Open an existing local VMDK file
nbdkit vddk /absolute/path/to/file.vmdk
@@ -38,6 +42,18 @@ I<-r> option):
nbdkit -r vddk /absolute/path/to/file.vmdk
+=head2 Create a new local VMDK file
+
+You can use VDDK to create a VMDK file and fill it with the contents
+of a disk image. Note the C<create-size> parameter is the virtual
+size of the final VMDK disk image and must be at least as large as the
+input disk:
+
+ nbdkit -U - vddk \
+ /absolute/path/to/output.vmdk \
+ create=1 create-size=100M \
+ --run 'qemu-img convert input.qcow2 $uri'
+
=head2 Open a file on a remote VMware ESXi hypervisor
Connect directly to a VMware ESXi hypervisor and export a particular
@@ -136,6 +152,88 @@ C<VIXDISKLIB_CRED_SESSIONID> which can improve performance. The
cookie can be found by connecting to a VCenter Server over HTTPS and
retrieving the C<vmware_soap_session> cookie.
+=item B<create=true>
+
+(nbdkit E<ge> 1.30)
+
+Create a new, local VMDK file. Instead of opening an existing VMDK
+file, a new VMDK file is created and opened. The filename is given by
+the C<file> parameter (see below). The file must not exist already.
+It is not possible to create a remote file using nbdkit.
+
+If this is used, the C<create-size> parameter is required to specify
+the virtual size of the disk. Other C<create-*> parameters (see
+below) can be used to control the VMDK sub-format.
+
+=item B<create-adapter-type=ide>
+
+=item B<create-adapter-type=scsi-buslogic>
+
+=item B<create-adapter-type=scsi-lsilogic>
+
+(nbdkit E<ge> 1.30)
+
+Specify the VMDK disk adapter type. The default is C<scsi-buslogic>.
+
+=item B<create-hwversion=workstation4>
+
+=item B<create-hwversion=workstation5>
+
+=item B<create-hwversion=workstation6>
+
+=item B<create-hwversion=esx30>
+
+=item B<create-hwversion=esx4x>
+
+=item B<create-hwversion=esx50>
+
+=item B<create-hwversion=esx51>
+
+=item B<create-hwversion=esx55>
+
+=item B<create-hwversion=esx60>
+
+=item B<create-hwversion=esx65>
+
+(nbdkit E<ge> 1.30)
+
+Specify the VMDK virtual hardware version. The default is
+C<workstation5>.
+
+=item B<create-size=>SIZE
+
+(nbdkit E<ge> 1.30)
+
+Specify the virtual size of the created disk. The C<SIZE> can use
+modifiers like C<100M> etc. It must be a multiple of 512 bytes
+because VMware only supports sector sizes.
+
+If you use C<create=true> then this parameter is required.
+
+=item B<create-type=monolithic-sparse>
+
+=item B<create-type=monolithic-flat>
+
+=item B<create-type=split-sparse>
+
+=item B<create-type=split-flat>
+
+=item B<create-type=vmfs-flat>
+
+=item B<create-type=stream-optimized>
+
+=item B<create-type=vmfs-thin>
+
+=item B<create-type=vmfs-sparse>
+
+(nbdkit E<ge> 1.30)
+
+Specify the VMDK sub-format. The default is C<monolithic-sparse>.
+
+Some VMDK sub-formats use multiple files, where the C<file> parameter
+specifies the "Disk Descriptor File" and the disk contents are stored
+in adjacent files.
+
=item [B<file=>]FILENAME
=item [B<file=>]B<[>datastoreB<] >vmname/vmnameB<.vmdk>
diff --git a/plugins/vddk/vddk-structs.h b/plugins/vddk/vddk-structs.h
index e97f017c..799c4aec 100644
--- a/plugins/vddk/vddk-structs.h
+++ b/plugins/vddk/vddk-structs.h
@@ -43,6 +43,7 @@
typedef uint64_t VixError;
#define VIX_OK 0
+#define VIX_E_NOT_SUPPORTED 6
#define VIX_ASYNC 25000
#define VIXDISKLIB_FLAG_OPEN_UNBUFFERED 1
@@ -54,6 +55,28 @@ typedef uint64_t VixError;
#define VIXDISKLIB_SECTOR_SIZE 512
+enum VixDiskLibDiskType {
+ VIXDISKLIB_DISK_MONOLITHIC_SPARSE = 1,
+ VIXDISKLIB_DISK_MONOLITHIC_FLAT = 2,
+ VIXDISKLIB_DISK_SPLIT_SPARSE = 3,
+ VIXDISKLIB_DISK_SPLIT_FLAT = 4,
+ VIXDISKLIB_DISK_VMFS_FLAT = 5,
+ VIXDISKLIB_DISK_STREAM_OPTIMIZED = 6,
+ VIXDISKLIB_DISK_VMFS_THIN = 7,
+ VIXDISKLIB_DISK_VMFS_SPARSE = 8
+};
+
+#define VIXDISKLIB_HWVERSION_WORKSTATION_4 3
+#define VIXDISKLIB_HWVERSION_WORKSTATION_5 4
+#define VIXDISKLIB_HWVERSION_WORKSTATION_6 6
+#define VIXDISKLIB_HWVERSION_ESX30 4
+#define VIXDISKLIB_HWVERSION_ESX4X 7
+#define VIXDISKLIB_HWVERSION_ESX50 8
+#define VIXDISKLIB_HWVERSION_ESX51 9
+#define VIXDISKLIB_HWVERSION_ESX55 10
+#define VIXDISKLIB_HWVERSION_ESX60 11
+#define VIXDISKLIB_HWVERSION_ESX65 13
+
#define VIXDISKLIB_MIN_CHUNK_SIZE 128
#define VIXDISKLIB_MAX_CHUNK_NUMBER (512*1024)
@@ -148,4 +171,13 @@ typedef struct {
VixDiskLibBlock blocks[1];
} VixDiskLibBlockList;
+typedef struct {
+ enum VixDiskLibDiskType diskType;
+ enum VixDiskLibAdapterType adapterType;
+ uint16_t hwVersion;
+ uint64_t capacity;
+ uint32_t logicalSectorSize;
+ uint32_t physicalSectorSize;
+} VixDiskLibCreateParams;
+
#endif /* NBDKIT_VDDK_STRUCTS_H */
diff --git a/plugins/vddk/vddk-stubs.h b/plugins/vddk/vddk-stubs.h
index 7d8644c3..d5affa10 100644
--- a/plugins/vddk/vddk-stubs.h
+++ b/plugins/vddk/vddk-stubs.h
@@ -99,6 +99,13 @@ STUB (VixDiskLib_Write,
(VixDiskLibHandle handle,
uint64_t start_sector, uint64_t nr_sectors,
const unsigned char *buf));
+STUB (VixDiskLib_Create,
+ VixError,
+ (const VixDiskLibConnection connection,
+ const char *path,
+ const VixDiskLibCreateParams *create_params,
+ void *progress_function_unused,
+ void *progress_data_unused));
/* Added in VDDK 6.0. */
STUB (VixDiskLib_Flush,
diff --git a/plugins/vddk/vddk.c b/plugins/vddk/vddk.c
index 31e5e23b..5ebf9a2c 100644
--- a/plugins/vddk/vddk.c
+++ b/plugins/vddk/vddk.c
@@ -81,6 +81,14 @@ bool is_remote; /* true if remote connection */
enum compression_type compression; /* compression */
char *config; /* config */
const char *cookie; /* cookie */
+bool create; /* create */
+enum VixDiskLibAdapterType create_adapter_type =
+ VIXDISKLIB_ADAPTER_SCSI_BUSLOGIC; /* create-adapter-type */
+uint16_t create_hwversion =
+ VIXDISKLIB_HWVERSION_WORKSTATION_5; /* create-hwversion */
+uint64_t create_size; /* create-size */
+enum VixDiskLibDiskType create_type =
+ VIXDISKLIB_DISK_MONOLITHIC_SPARSE; /* create-type */
const char *filename; /* file */
char *libdir; /* libdir */
uint16_t nfc_host_port; /* nfchostport */
@@ -119,6 +127,7 @@ static int
vddk_config (const char *key, const char *value)
{
int r;
+ int64_t r64;
if (strcmp (key, "compression") == 0) {
if (strcmp (value, "zlib") == 0)
@@ -144,6 +153,82 @@ vddk_config (const char *key, const char *value)
else if (strcmp (key, "cookie") == 0) {
cookie = value;
}
+ else if (strcmp (key, "create") == 0) {
+ r = nbdkit_parse_bool (value);
+ if (r == -1)
+ return -1;
+ create = r;
+ }
+ else if (strcmp (key, "create-adapter-type") == 0) {
+ if (strcmp (value, "ide") == 0)
+ create_adapter_type = VIXDISKLIB_ADAPTER_IDE;
+ else if (strcmp (value, "scsi-buslogic") == 0)
+ create_adapter_type = VIXDISKLIB_ADAPTER_SCSI_BUSLOGIC;
+ else if (strcmp (value, "scsi-lsilogic") == 0)
+ create_adapter_type = VIXDISKLIB_ADAPTER_SCSI_LSILOGIC;
+ else {
+ nbdkit_error ("unknown create-adapter-type: %s", value);
+ return -1;
+ }
+ }
+ else if (strcmp (key, "create-hwversion") == 0) {
+ if (strcmp (value, "workstation4") == 0)
+ create_hwversion = VIXDISKLIB_HWVERSION_WORKSTATION_4;
+ else if (strcmp (value, "workstation5") == 0)
+ create_hwversion = VIXDISKLIB_HWVERSION_WORKSTATION_5;
+ else if (strcmp (value, "workstation6") == 0)
+ create_hwversion = VIXDISKLIB_HWVERSION_WORKSTATION_6;
+ else if (strcmp (value, "esx30") == 0)
+ create_hwversion = VIXDISKLIB_HWVERSION_ESX30;
+ else if (strcmp (value, "esx4x") == 0)
+ create_hwversion = VIXDISKLIB_HWVERSION_ESX4X;
+ else if (strcmp (value, "esx50") == 0)
+ create_hwversion = VIXDISKLIB_HWVERSION_ESX50;
+ else if (strcmp (value, "esx51") == 0)
+ create_hwversion = VIXDISKLIB_HWVERSION_ESX51;
+ else if (strcmp (value, "esx55") == 0)
+ create_hwversion = VIXDISKLIB_HWVERSION_ESX55;
+ else if (strcmp (value, "esx60") == 0)
+ create_hwversion = VIXDISKLIB_HWVERSION_ESX60;
+ else if (strcmp (value, "esx65") == 0)
+ create_hwversion = VIXDISKLIB_HWVERSION_ESX65;
+ else {
+ nbdkit_error ("unknown create-hwversion: %s", value);
+ return -1;
+ }
+ }
+ else if (strcmp (key, "create-size") == 0) {
+ r64 = nbdkit_parse_size (value);
+ if (r64 == -1)
+ return -1;
+ if (r64 <= 0 || (r64 & 511) != 0) {
+ nbdkit_error ("create-size must be greater than zero and a multiple of 512");
+ return -1;
+ }
+ create_size = r64;
+ }
+ else if (strcmp (key, "create-type") == 0) {
+ if (strcmp (value, "monolithic-sparse") == 0)
+ create_type = VIXDISKLIB_DISK_MONOLITHIC_SPARSE;
+ else if (strcmp (value, "monolithic-flat") == 0)
+ create_type = VIXDISKLIB_DISK_MONOLITHIC_FLAT;
+ else if (strcmp (value, "split-sparse") == 0)
+ create_type = VIXDISKLIB_DISK_SPLIT_SPARSE;
+ else if (strcmp (value, "split-flat") == 0)
+ create_type = VIXDISKLIB_DISK_SPLIT_FLAT;
+ else if (strcmp (value, "vmfs-flat") == 0)
+ create_type = VIXDISKLIB_DISK_VMFS_FLAT;
+ else if (strcmp (value, "stream-optimized") == 0)
+ create_type = VIXDISKLIB_DISK_STREAM_OPTIMIZED;
+ else if (strcmp (value, "vmfs-thin") == 0)
+ create_type = VIXDISKLIB_DISK_VMFS_THIN;
+ else if (strcmp (value, "vmfs-sparse") == 0)
+ create_type = VIXDISKLIB_DISK_VMFS_SPARSE;
+ else {
+ nbdkit_error ("unknown create-type: %s", value);
+ return -1;
+ }
+ }
else if (strcmp (key, "file") == 0) {
/* NB: Don't convert this to an absolute path, because in the
* remote case this can be a path located on the VMware server.
@@ -266,6 +351,18 @@ vddk_config_complete (void)
#undef missing
}
+ if (create) {
+ if (is_remote) {
+ nbdkit_error ("create=true can only be used to create local VMDK files");
+ return -1;
+ }
+
+ if (create_size == 0) {
+ nbdkit_error ("if using create=true you must specify the size using the create-size parameter");
+ return -1;
+ }
+ }
+
/* Restore original LD_LIBRARY_PATH after reexec. */
if (restore_ld_library_path () == -1)
return -1;
@@ -618,6 +715,34 @@ vddk_open (int readonly)
goto err1;
}
+ /* Creating a disk? The first time the connection is opened we will
+ * create it here (we need h->connection). Then set create=false so
+ * we don't create it again. This is all serialized through
+ * open_close_lock so it is safe.
+ */
+ if (create) {
+ VixDiskLibCreateParams cparams = {
+ .diskType = create_type,
+ .adapterType = create_adapter_type,
+ .hwVersion = create_hwversion,
+ .capacity = create_size / VIXDISKLIB_SECTOR_SIZE,
+ .logicalSectorSize = 0,
+ .physicalSectorSize = 0
+ };
+
+ VDDK_CALL_START (VixDiskLib_Create,
+ "h->connection, %s, &cparams, NULL, NULL",
+ filename)
+ err = VixDiskLib_Create (h->connection, filename, &cparams, NULL, NULL);
+ VDDK_CALL_END (VixDiskLib_Create, 0);
+ if (err != VIX_OK) {
+ VDDK_ERROR (err, "VixDiskLib_Create: %s", filename);
+ goto err2;
+ }
+
+ create = false; /* Don't create it again. */
+ }
+
flags = 0;
if (readonly)
flags |= VIXDISKLIB_FLAG_OPEN_READ_ONLY;
diff --git a/plugins/vddk/vddk.h b/plugins/vddk/vddk.h
index d99b6f4b..3a808013 100644
--- a/plugins/vddk/vddk.h
+++ b/plugins/vddk/vddk.h
@@ -56,6 +56,11 @@ extern bool is_remote;
extern enum compression_type compression;
extern char *config;
extern const char *cookie;
+extern bool create;
+extern enum VixDiskLibAdapterType create_adapter_type;
+extern uint16_t create_hwversion;
+extern uint64_t create_size;
+extern enum VixDiskLibDiskType create_type;
extern const char *filename;
extern char *libdir;
extern uint16_t nfc_host_port;
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 43b60943..ad2d43b9 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -171,7 +171,9 @@ if HAVE_VDDK
#
# make check-vddk vddkdir=vmware-vix-disklib-distrib
check-vddk:
- $(MAKE) check TESTS="test-vddk-real.sh test-vddk-real-dump-plugin.sh"
+ $(MAKE) check TESTS="test-vddk-real.sh
+ test-vddk-real-dump-plugin.sh
+ test-vddk-real-create.sh"
endif HAVE_VDDK
#----------------------------------------------------------------------
@@ -1033,6 +1035,7 @@ TESTS += \
test-vddk-dump-plugin.sh \
test-vddk-password-fd.sh \
test-vddk-password-interactive.sh \
+ test-vddk-real-create.sh \
test-vddk-real-dump-plugin.sh \
test-vddk-real.sh \
test-vddk-reexec.sh \
@@ -1063,6 +1066,7 @@ EXTRA_DIST += \
test-vddk-dump-plugin.sh \
test-vddk-password-fd.sh \
test-vddk-password-interactive.sh \
+ test-vddk-real-create.sh \
test-vddk-real-dump-plugin.sh \
test-vddk-real.sh \
test-vddk-reexec.sh \
diff --git a/tests/dummy-vddk.c b/tests/dummy-vddk.c
index b6f12042..0c5e505f 100644
--- a/tests/dummy-vddk.c
+++ b/tests/dummy-vddk.c
@@ -236,3 +236,13 @@ VixDiskLib_Wait (VixDiskLibHandle handle)
{
return VIX_OK;
}
+
+NBDKIT_DLL_PUBLIC VixError
+VixDiskLib_Create (const VixDiskLibConnection connection,
+ const char *path,
+ const VixDiskLibCreateParams *create_params,
+ void *progress_function_unused,
+ void *progress_data_unused)
+{
+ return VIX_E_NOT_SUPPORTED;
+}
diff --git a/tests/test-vddk-real-create.sh b/tests/test-vddk-real-create.sh
new file mode 100755
index 00000000..8f39a4c9
--- /dev/null
+++ b/tests/test-vddk-real-create.sh
@@ -0,0 +1,70 @@
+#!/usr/bin/env bash
+# nbdkit
+# Copyright (C) 2018-2022 Red Hat Inc.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions are
+# met:
+#
+# * Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# * Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# * Neither the name of Red Hat nor the names of its contributors may be
+# used to endorse or promote products derived from this software without
+# specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+# USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+# OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+
+source ./functions.sh
+set -e
+set -x
+
+requires test "x$vddkdir" != "x"
+requires test -d "$vddkdir"
+requires test -f "$vddkdir/lib64/libvixDiskLib.so"
+requires test -f disk
+requires nbdcopy --version
+requires stat --version
+
+# Testing $LD_LIBRARY_PATH stuff breaks valgrind, so skip the rest of
+# this test if valgrinding.
+if [ "x$NBDKIT_VALGRIND" = "x1" ]; then
+ echo "$0: skipped LD_LIBRARY_PATH test when doing valgrind"
+ exit 77
+fi
+
+# VDDK > 5.1.1 only supports x86_64.
+if [ `uname -m` != "x86_64" ]; then
+ echo "$0: unsupported architecture"
+ exit 77
+fi
+
+vmdk=$PWD/test-vddk-real-create.vmdk ;# note must be an absolute path
+files="$vmdk"
+rm -f $files
+cleanup_fn rm -f $files
+
+size="$(stat -c %s disk)"
+
+nbdkit -fv -U - vddk libdir="$vddkdir" $vmdk \
+ create=true create-size=$size \
+ --run 'nbdcopy disk $uri'
+
+# Check the VMDK file was created and looks reasonable.
+test -f $vmdk
+file $vmdk | grep 'VMware'
--
2.31.1