This uses a new API VixDiskLib_QueryAllocatedBlocks provided in
VDDK >= 6.7.
Thanks: Martin Kletzander.
---
plugins/vddk/vddk-structs.h | 15 +++-
plugins/vddk/vddk.c | 141 ++++++++++++++++++++++++++++++++++++
2 files changed, 155 insertions(+), 1 deletion(-)
diff --git a/plugins/vddk/vddk-structs.h b/plugins/vddk/vddk-structs.h
index dbed94a..df88322 100644
--- a/plugins/vddk/vddk-structs.h
+++ b/plugins/vddk/vddk-structs.h
@@ -1,5 +1,5 @@
/* nbdkit
- * Copyright (C) 2013-2018 Red Hat Inc.
+ * Copyright (C) 2013-2019 Red Hat Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
@@ -47,6 +47,9 @@ typedef uint64_t VixError;
#define VIXDISKLIB_FLAG_OPEN_READ_ONLY 4
#define VIXDISKLIB_SECTOR_SIZE 512
+#define VIXDISKLIB_MIN_CHUNK_SIZE 128
+#define VIXDISKLIB_MAX_CHUNK_NUMBER (512*1024)
+
typedef void *VixDiskLibConnection;
typedef void *VixDiskLibHandle;
@@ -124,4 +127,14 @@ typedef struct VixDiskLibInfo {
char *uuid;
} VixDiskLibInfo;
+typedef struct {
+ uint64_t offset;
+ uint64_t length;
+} VixDiskLibBlock;
+
+typedef struct {
+ uint32_t numBlocks;
+ VixDiskLibBlock blocks[1];
+} VixDiskLibBlockList;
+
#endif /* NBDKIT_VDDK_STRUCTS_H */
diff --git a/plugins/vddk/vddk.c b/plugins/vddk/vddk.c
index 56871cc..436aa4d 100644
--- a/plugins/vddk/vddk.c
+++ b/plugins/vddk/vddk.c
@@ -45,6 +45,8 @@
#include <nbdkit-plugin.h>
#include "isaligned.h"
+#include "minmax.h"
+#include "rounding.h"
#include "vddk-structs.h"
@@ -67,6 +69,8 @@ static VixError (*VixDiskLib_GetInfo) (VixDiskLibHandle handle,
VixDiskLibInfo *
static void (*VixDiskLib_FreeInfo) (VixDiskLibInfo *info);
static VixError (*VixDiskLib_Read) (VixDiskLibHandle handle, uint64_t start_sector,
uint64_t nr_sectors, unsigned char *buf);
static VixError (*VixDiskLib_Write) (VixDiskLibHandle handle, uint64_t start_sector,
uint64_t nr_sectors, const unsigned char *buf);
+static VixError (*VixDiskLib_QueryAllocatedBlocks) (VixDiskLibHandle diskHandle, uint64_t
start_sector, uint64_t nr_sectors, uint64_t chunk_size, VixDiskLibBlockList
**block_list);
+static VixError (*VixDiskLib_FreeBlockList) (VixDiskLibBlockList *block_list);
/* Parameters passed to InitEx. */
#define VDDK_MAJOR 5
@@ -174,6 +178,11 @@ vddk_load (void)
VixDiskLib_FreeInfo = dlsym (dl, "VixDiskLib_FreeInfo");
VixDiskLib_Read = dlsym (dl, "VixDiskLib_Read");
VixDiskLib_Write = dlsym (dl, "VixDiskLib_Write");
+
+ /* Added in VDDK 6.7, these will be NULL for earlier versions: */
+ VixDiskLib_QueryAllocatedBlocks =
+ dlsym (dl, "VixDiskLib_QueryAllocatedBlocks");
+ VixDiskLib_FreeBlockList = dlsym (dl, "VixDiskLib_FreeBlockList");
}
static void
@@ -570,6 +579,136 @@ vddk_pwrite (void *handle, const void *buf, uint32_t count, uint64_t
offset)
return 0;
}
+static int
+vddk_can_extents (void *handle)
+{
+ struct vddk_handle *h = handle;
+ VixError err;
+ VixDiskLibBlockList *block_list;
+
+ /* This call was added in VDDK 6.7. In earlier versions the
+ * function pointer will be NULL and we cannot query extents.
+ */
+ if (VixDiskLib_QueryAllocatedBlocks == NULL) {
+ nbdkit_debug ("can_extents: VixDiskLib_QueryAllocatedBlocks == NULL, "
+ "probably this is VDDK < 6.7");
+ return 0;
+ }
+
+ /* However even when the call is available it rarely works well so
+ * the best thing we can do here is to try the call and if it's
+ * non-functional return false.
+ */
+ DEBUG_CALL ("VixDiskLib_QueryAllocatedBlocks",
+ "handle, 0, %d sectors, %d sectors",
+ VIXDISKLIB_MIN_CHUNK_SIZE, VIXDISKLIB_MIN_CHUNK_SIZE);
+ err = VixDiskLib_QueryAllocatedBlocks (h->handle,
+ 0, VIXDISKLIB_MIN_CHUNK_SIZE,
+ VIXDISKLIB_MIN_CHUNK_SIZE,
+ &block_list);
+ if (err == VIX_OK) {
+ DEBUG_CALL ("VixDiskLib_FreeBlockList", "block_list");
+ VixDiskLib_FreeBlockList (block_list);
+ }
+ if (err != VIX_OK) {
+ char *errmsg = VixDiskLib_GetErrorText (err, NULL);
+ nbdkit_debug ("can_extents: VixDiskLib_QueryAllocatedBlocks test failed, "
+ "extents support will be disabled: "
+ "original error: %s",
+ errmsg);
+ VixDiskLib_FreeErrorText (errmsg);
+ return 0;
+ }
+
+ return 1;
+}
+
+static int
+vddk_extents (void *handle, uint32_t count, uint64_t offset, uint32_t flags,
+ struct nbdkit_extents *extents)
+{
+ struct vddk_handle *h = handle;
+ bool req_one = flags & NBDKIT_FLAG_REQ_ONE;
+ uint64_t position, end, start_sector;
+
+ position = offset;
+
+ /* We can only query whole chunks. Therefore start with the first
+ * chunk before offset.
+ */
+ end = offset + count;
+ start_sector =
+ ROUND_DOWN (offset, VIXDISKLIB_MIN_CHUNK_SIZE * VIXDISKLIB_SECTOR_SIZE)
+ / VIXDISKLIB_SECTOR_SIZE;
+ while (start_sector * VIXDISKLIB_SECTOR_SIZE < end) {
+ VixError err;
+ uint32_t i;
+ uint64_t nr_chunks, nr_sectors;
+ VixDiskLibBlockList *block_list;
+
+ assert (IS_ALIGNED (start_sector, VIXDISKLIB_MIN_CHUNK_SIZE));
+
+ nr_chunks =
+ ROUND_UP (end - start_sector * VIXDISKLIB_SECTOR_SIZE,
+ VIXDISKLIB_MIN_CHUNK_SIZE * VIXDISKLIB_SECTOR_SIZE)
+ / (VIXDISKLIB_MIN_CHUNK_SIZE * VIXDISKLIB_SECTOR_SIZE);
+ nr_chunks = MIN (nr_chunks, VIXDISKLIB_MAX_CHUNK_NUMBER);
+ nr_sectors = nr_chunks * VIXDISKLIB_MIN_CHUNK_SIZE;
+
+ DEBUG_CALL ("VixDiskLib_QueryAllocatedBlocks",
+ "handle, %" PRIu64 " sectors, %" PRIu64 "
sectors, "
+ "%d sectors",
+ start_sector, nr_sectors, VIXDISKLIB_MIN_CHUNK_SIZE);
+ err = VixDiskLib_QueryAllocatedBlocks (h->handle,
+ start_sector, nr_sectors,
+ VIXDISKLIB_MIN_CHUNK_SIZE,
+ &block_list);
+ if (err != VIX_OK) {
+ VDDK_ERROR (err, "VixDiskLib_QueryAllocatedBlocks");
+ return -1;
+ }
+
+ for (i = 0; i < block_list->numBlocks; ++i) {
+ uint64_t offset, length;
+
+ offset = block_list->blocks[i].offset * VIXDISKLIB_SECTOR_SIZE;
+ length = block_list->blocks[i].length * VIXDISKLIB_SECTOR_SIZE;
+
+ /* The query returns blocks. We must insert holes between the
+ * blocks as necessary.
+ */
+ if (position < offset) {
+ if (nbdkit_add_extent (extents,
+ offset, length,
+ NBDKIT_EXTENT_HOLE | NBDKIT_EXTENT_ZERO) == -1)
+ goto error_in_add;
+ }
+
+ if (nbdkit_add_extent (extents,
+ offset, length, 0 /* allocated data */) == -1) {
+ error_in_add:
+ DEBUG_CALL ("VixDiskLib_FreeBlockList", "block_list");
+ VixDiskLib_FreeBlockList (block_list);
+ return -1;
+ }
+
+ position = offset + length;
+
+ if (req_one)
+ break;
+ }
+ DEBUG_CALL ("VixDiskLib_FreeBlockList", "block_list");
+ VixDiskLib_FreeBlockList (block_list);
+
+ start_sector += nr_sectors;
+
+ if (req_one)
+ break;
+ }
+
+ return 0;
+}
+
static struct nbdkit_plugin plugin = {
.name = "vddk",
.longname = "VMware VDDK plugin",
@@ -585,6 +724,8 @@ static struct nbdkit_plugin plugin = {
.get_size = vddk_get_size,
.pread = vddk_pread,
.pwrite = vddk_pwrite,
+ .can_extents = vddk_can_extents,
+ .extents = vddk_extents,
};
NBDKIT_REGISTER_PLUGIN(plugin)
--
2.20.1