2 * Copyright (C) 2013-2018 Red Hat Inc.
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
11 * * Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
15 * * Neither the name of Red Hat nor the names of its contributors may be
16 * used to endorse or promote products derived from this software without
17 * specific prior written permission.
19 * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
21 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
22 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
23 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
27 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
29 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
44 #define NBDKIT_API_VERSION 2
46 #include <nbdkit-plugin.h>
49 #include "isaligned.h"
53 #include "vddk-structs.h"
55 /* Enable extra disk info debugging with: -D vddk.diskinfo=1 */
56 int vddk_debug_diskinfo
;
58 /* Enable debugging of extents code with: -D vddk.extents=1 */
59 int vddk_debug_extents
;
61 /* The VDDK APIs that we call. These globals are initialized when the
62 * plugin is loaded (by vddk_load).
64 static char *(*VixDiskLib_GetErrorText
) (VixError err
, const char *unused
);
65 static void (*VixDiskLib_FreeErrorText
) (char *text
);
66 static VixError (*VixDiskLib_InitEx
) (uint32_t major
, uint32_t minor
, VixDiskLibGenericLogFunc
*log_function
, VixDiskLibGenericLogFunc
*warn_function
, VixDiskLibGenericLogFunc
*panic_function
, const char *lib_dir
, const char *config_file
);
67 static void (*VixDiskLib_Exit
) (void);
68 static VixDiskLibConnectParams
*(*VixDiskLib_AllocateConnectParams
) (void);
69 static void (*VixDiskLib_FreeConnectParams
) (VixDiskLibConnectParams
*params
);
70 static VixError (*VixDiskLib_ConnectEx
) (const VixDiskLibConnectParams
*params
, char read_only
, const char *snapshot_ref
, const char *transport_modes
, VixDiskLibConnection
*connection
);
71 static VixError (*VixDiskLib_Open
) (const VixDiskLibConnection connection
, const char *path
, uint32_t flags
, VixDiskLibHandle
*handle
);
72 static const char *(*VixDiskLib_GetTransportMode
) (VixDiskLibHandle handle
);
73 static VixError (*VixDiskLib_Close
) (VixDiskLibHandle handle
);
74 static VixError (*VixDiskLib_Disconnect
) (VixDiskLibConnection connection
);
75 static VixError (*VixDiskLib_GetInfo
) (VixDiskLibHandle handle
, VixDiskLibInfo
**info
);
76 static void (*VixDiskLib_FreeInfo
) (VixDiskLibInfo
*info
);
77 static VixError (*VixDiskLib_Read
) (VixDiskLibHandle handle
, uint64_t start_sector
, uint64_t nr_sectors
, unsigned char *buf
);
78 static VixError (*VixDiskLib_Write
) (VixDiskLibHandle handle
, uint64_t start_sector
, uint64_t nr_sectors
, const unsigned char *buf
);
79 static VixError (*VixDiskLib_Flush
) (VixDiskLibHandle handle
);
80 static VixError (*VixDiskLib_QueryAllocatedBlocks
) (VixDiskLibHandle diskHandle
, uint64_t start_sector
, uint64_t nr_sectors
, uint64_t chunk_size
, VixDiskLibBlockList
**block_list
);
81 static VixError (*VixDiskLib_FreeBlockList
) (VixDiskLibBlockList
*block_list
);
83 /* Parameters passed to InitEx. */
87 static void *dl
= NULL
; /* dlopen handle */
88 static int init_called
= 0; /* was InitEx called */
90 static char *config
= NULL
; /* config */
91 static const char *cookie
= NULL
; /* cookie */
92 static const char *filename
= NULL
; /* file */
93 static char *libdir
= NULL
; /* libdir */
94 static int nfc_host_port
= 0; /* nfchostport */
95 static char *password
= NULL
; /* password */
96 static int port
= 0; /* port */
97 static const char *server_name
= NULL
; /* server */
98 static bool single_link
= false; /* single-link */
99 static const char *snapshot_moref
= NULL
; /* snapshot */
100 static const char *thumb_print
= NULL
; /* thumbprint */
101 static const char *transport_modes
= NULL
; /* transports */
102 static bool unbuffered
= false; /* unbuffered */
103 static const char *username
= NULL
; /* user */
104 static const char *vmx_spec
= NULL
; /* vm */
105 static bool is_remote
= false;
107 #define VDDK_ERROR(err, fs, ...) \
109 char *vddk_err_msg; \
110 vddk_err_msg = VixDiskLib_GetErrorText ((err), NULL); \
111 nbdkit_error (fs ": %s", ##__VA_ARGS__, vddk_err_msg); \
112 VixDiskLib_FreeErrorText (vddk_err_msg); \
115 #define DEBUG_CALL(fn, fs, ...) \
116 nbdkit_debug ("VDDK call: %s (" fs ")", fn, ##__VA_ARGS__)
121 size_t len
= strlen (str
);
123 if (len
> 0 && str
[len
-1] == '\n')
127 /* Turn log messages from the library into nbdkit_debug. */
129 debug_function (const char *fs
, va_list args
)
131 CLEANUP_FREE
char *str
= NULL
;
133 if (vasprintf (&str
, fs
, args
) == -1) {
134 nbdkit_debug ("lost debug message: %s", fs
);
140 nbdkit_debug ("%s", str
);
143 /* Turn error messages from the library into nbdkit_error. */
145 error_function (const char *fs
, va_list args
)
147 CLEANUP_FREE
char *str
= NULL
;
149 if (vasprintf (&str
, fs
, args
) == -1) {
150 nbdkit_error ("lost error message: %s", fs
);
156 nbdkit_error ("%s", str
);
159 /* Load and unload the plugin. */
163 static const char *sonames
[] = {
164 /* Prefer the newest library in case multiple exist. */
165 "libvixDiskLib.so.6",
166 "libvixDiskLib.so.5",
169 CLEANUP_FREE
char *orig_error
= NULL
;
171 /* Load the library. */
172 for (i
= 0; i
< sizeof sonames
/ sizeof sonames
[0]; ++i
) {
173 dl
= dlopen (sonames
[i
], RTLD_NOW
);
177 orig_error
= dlerror ();
179 orig_error
= strdup (orig_error
);
183 nbdkit_error ("%s\n\n"
184 "If '%s' is located on a non-standard path you may need to\n"
185 "set $LD_LIBRARY_PATH or edit /etc/ld.so.conf.\n\n"
186 "See the nbdkit-vddk-plugin(1) man page for details.",
187 orig_error
? : "(unknown error)", sonames
[0]);
191 /* Non-optional symbols. I have checked that all these exist in at
192 * least VDDK 5.1.1 (2011) which is the earliest version of VDDK
197 sym = dlsym (dl, #sym); \
199 nbdkit_error ("required VDDK symbol \"%s\" is missing: %s", \
201 exit (EXIT_FAILURE); \
204 LOAD (VixDiskLib_GetErrorText
);
205 LOAD (VixDiskLib_FreeErrorText
);
206 LOAD (VixDiskLib_InitEx
);
207 LOAD (VixDiskLib_Exit
);
208 LOAD (VixDiskLib_ConnectEx
);
209 LOAD (VixDiskLib_Open
);
210 LOAD (VixDiskLib_GetTransportMode
);
211 LOAD (VixDiskLib_Close
);
212 LOAD (VixDiskLib_Disconnect
);
213 LOAD (VixDiskLib_GetInfo
);
214 LOAD (VixDiskLib_FreeInfo
);
215 LOAD (VixDiskLib_Read
);
216 LOAD (VixDiskLib_Write
);
217 LOAD (VixDiskLib_FreeConnectParams
);
220 #define LOAD_OPTIONAL(sym) sym = dlsym (dl, #sym)
221 /* Added in VDDK 6.0, this will be NULL in earlier versions. */
222 LOAD_OPTIONAL (VixDiskLib_Flush
);
224 /* Added in VDDK 6.7, these will be NULL for earlier versions: */
225 LOAD_OPTIONAL (VixDiskLib_QueryAllocatedBlocks
);
226 LOAD_OPTIONAL (VixDiskLib_FreeBlockList
);
227 LOAD_OPTIONAL (VixDiskLib_AllocateConnectParams
);
235 DEBUG_CALL ("VixDiskLib_Exit", "");
247 vddk_config (const char *key
, const char *value
)
249 if (strcmp (key
, "config") == 0) {
250 /* See FILENAMES AND PATHS in nbdkit-plugin(3). */
252 config
= nbdkit_realpath (value
);
256 else if (strcmp (key
, "cookie") == 0) {
259 else if (strcmp (key
, "file") == 0) {
260 /* NB: Don't convert this to an absolute path, because in the
261 * remote case this can be a path located on the VMware server.
262 * For local paths the user must supply an absolute path.
266 else if (strcmp (key
, "libdir") == 0) {
267 /* See FILENAMES AND PATHS in nbdkit-plugin(3). */
269 libdir
= nbdkit_realpath (value
);
273 else if (strcmp (key
, "nfchostport") == 0) {
274 if (sscanf (value
, "%d", &nfc_host_port
) != 1) {
275 nbdkit_error ("cannot parse nfchostport: %s", value
);
279 else if (strcmp (key
, "password") == 0) {
281 if (nbdkit_read_password (value
, &password
) == -1)
284 else if (strcmp (key
, "port") == 0) {
285 if (sscanf (value
, "%d", &port
) != 1) {
286 nbdkit_error ("cannot parse port: %s", value
);
290 else if (strcmp (key
, "server") == 0) {
293 else if (strcmp (key
, "single-link") == 0) {
294 int r
= nbdkit_parse_bool (value
);
300 else if (strcmp (key
, "snapshot") == 0) {
301 snapshot_moref
= value
;
303 else if (strcmp (key
, "thumbprint") == 0) {
306 else if (strcmp (key
, "transports") == 0) {
307 transport_modes
= value
;
309 else if (strcmp (key
, "unbuffered") == 0) {
310 int r
= nbdkit_parse_bool (value
);
316 else if (strcmp (key
, "user") == 0) {
319 else if (strcmp (key
, "vimapiver") == 0) {
320 /* Ignored for backwards compatibility. */
322 else if (strcmp (key
, "vm") == 0) {
326 nbdkit_error ("unknown parameter '%s'", key
);
334 vddk_config_complete (void)
338 if (filename
== NULL
) {
339 nbdkit_error ("you must supply the file=<FILENAME> parameter "
340 "after the plugin name on the command line");
344 /* For remote connections, check all the parameters have been
345 * passed. Note that VDDK will segfault if parameters that it
346 * expects are NULL (and there's no real way to tell what parameters
347 * it is expecting). This implements the same test that the VDDK
348 * sample program does.
361 #define missing(test, param) \
363 nbdkit_error ("remote connection requested, missing parameter: %s", \
367 missing (!server_name
, "server");
368 missing (!username
, "user");
369 missing (!password
, "password");
370 missing (!vmx_spec
, "vm");
374 /* Initialize VDDK library. */
375 DEBUG_CALL ("VixDiskLib_InitEx",
376 "%d, %d, &debug_fn, &error_fn, &error_fn, %s, %s",
377 VDDK_MAJOR
, VDDK_MINOR
,
378 libdir
? : VDDK_LIBDIR
, config
? : "NULL");
379 err
= VixDiskLib_InitEx (VDDK_MAJOR
, VDDK_MINOR
,
380 &debug_function
, /* log function */
381 &error_function
, /* warn function */
382 &error_function
, /* panic function */
383 libdir
? : VDDK_LIBDIR
, config
);
385 VDDK_ERROR (err
, "VixDiskLib_InitEx");
393 #define vddk_config_help \
394 "file=<FILENAME> (required) The filename (eg. VMDK file) to serve.\n" \
395 "Many optional parameters are supported, see nbdkit-vddk-plugin(3)."
398 vddk_dump_plugin (void)
400 printf ("vddk_default_libdir=%s\n", VDDK_LIBDIR
);
401 printf ("vddk_has_nfchostport=1\n");
403 #if defined(HAVE_DLADDR)
404 /* It would be nice to print the version of VDDK from the shared
405 * library, but VDDK does not provide it. Instead we can get the
406 * path to the library using the glibc extension dladdr, and then
407 * resolve symlinks using realpath. The final pathname should
408 * contain the version number.
411 CLEANUP_FREE
char *p
= NULL
;
412 if (dladdr (VixDiskLib_InitEx
, &info
) != 0 &&
413 info
.dli_fname
!= NULL
&&
414 (p
= nbdkit_realpath (info
.dli_fname
)) != NULL
) {
415 printf ("vddk_dll=%s\n", p
);
420 /* XXX To really do threading correctly in accordance with the VDDK
421 * documentation, we must do all open/close calls from a single
422 * thread. This is a huge pain.
424 #define THREAD_MODEL NBDKIT_THREAD_MODEL_SERIALIZE_ALL_REQUESTS
426 /* The per-connection handle. */
428 VixDiskLibConnectParams
*params
; /* connection parameters */
429 VixDiskLibConnection connection
; /* connection */
430 VixDiskLibHandle handle
; /* disk handle */
433 static inline VixDiskLibConnectParams
*
434 allocate_connect_params (void)
436 VixDiskLibConnectParams
*ret
;
438 if (VixDiskLib_AllocateConnectParams
!= NULL
) {
439 DEBUG_CALL ("VixDiskLib_AllocateConnectParams", "");
440 ret
= VixDiskLib_AllocateConnectParams ();
443 ret
= calloc (1, sizeof (VixDiskLibConnectParams
));
449 free_connect_params (VixDiskLibConnectParams
*params
)
451 /* Only use FreeConnectParams if AllocateConnectParams was
452 * originally called. Otherwise use free.
454 if (VixDiskLib_AllocateConnectParams
!= NULL
) {
455 DEBUG_CALL ("VixDiskLib_FreeConnectParams", "params");
456 VixDiskLib_FreeConnectParams (params
);
462 /* Create the per-connection handle. */
464 vddk_open (int readonly
)
466 struct vddk_handle
*h
;
470 h
= malloc (sizeof *h
);
472 nbdkit_error ("malloc: %m");
476 h
->params
= allocate_connect_params ();
477 if (h
->params
== NULL
) {
478 nbdkit_error ("allocate VixDiskLibConnectParams: %m");
483 h
->params
->vmxSpec
= (char *) vmx_spec
;
484 h
->params
->serverName
= (char *) server_name
;
485 if (cookie
== NULL
) {
486 h
->params
->credType
= VIXDISKLIB_CRED_UID
;
487 h
->params
->creds
.uid
.userName
= (char *) username
;
488 h
->params
->creds
.uid
.password
= password
;
491 h
->params
->credType
= VIXDISKLIB_CRED_SESSIONID
;
492 h
->params
->creds
.sessionId
.cookie
= (char *) cookie
;
493 h
->params
->creds
.sessionId
.userName
= (char *) username
;
494 h
->params
->creds
.sessionId
.key
= password
;
496 h
->params
->thumbPrint
= (char *) thumb_print
;
497 h
->params
->port
= port
;
498 h
->params
->nfcHostPort
= nfc_host_port
;
501 /* XXX Some documentation suggests we should call
502 * VixDiskLib_PrepareForAccess here. It may be required for
503 * Advanced Transport modes, but I could not make it work with
504 * either ESXi or vCenter servers.
507 DEBUG_CALL ("VixDiskLib_ConnectEx",
508 "h->params, %d, %s, %s, &connection",
510 snapshot_moref
? : "NULL",
511 transport_modes
? : "NULL");
512 err
= VixDiskLib_ConnectEx (h
->params
,
518 VDDK_ERROR (err
, "VixDiskLib_ConnectEx");
524 flags
|= VIXDISKLIB_FLAG_OPEN_READ_ONLY
;
526 flags
|= VIXDISKLIB_FLAG_OPEN_SINGLE_LINK
;
528 flags
|= VIXDISKLIB_FLAG_OPEN_UNBUFFERED
;
530 DEBUG_CALL ("VixDiskLib_Open",
531 "connection, %s, %d, &handle", filename
, flags
);
532 err
= VixDiskLib_Open (h
->connection
, filename
, flags
, &h
->handle
);
534 VDDK_ERROR (err
, "VixDiskLib_Open: %s", filename
);
538 nbdkit_debug ("transport mode: %s",
539 VixDiskLib_GetTransportMode (h
->handle
));
544 DEBUG_CALL ("VixDiskLib_Disconnect", "connection");
545 VixDiskLib_Disconnect (h
->connection
);
547 free_connect_params (h
->params
);
553 /* Free up the per-connection handle. */
555 vddk_close (void *handle
)
557 struct vddk_handle
*h
= handle
;
559 free_connect_params (h
->params
);
560 DEBUG_CALL ("VixDiskLib_Close", "handle");
561 VixDiskLib_Close (h
->handle
);
562 DEBUG_CALL ("VixDiskLib_Disconnect", "connection");
563 VixDiskLib_Disconnect (h
->connection
);
567 /* Get the file size. */
569 vddk_get_size (void *handle
)
571 struct vddk_handle
*h
= handle
;
572 VixDiskLibInfo
*info
;
576 DEBUG_CALL ("VixDiskLib_GetInfo", "handle, &info");
577 err
= VixDiskLib_GetInfo (h
->handle
, &info
);
579 VDDK_ERROR (err
, "VixDiskLib_GetInfo");
583 size
= info
->capacity
* (uint64_t)VIXDISKLIB_SECTOR_SIZE
;
585 if (vddk_debug_diskinfo
) {
586 nbdkit_debug ("disk info: capacity: %" PRIu64
" (size: %" PRIi64
")",
587 info
->capacity
, size
);
588 nbdkit_debug ("disk info: biosGeo: C:%" PRIu32
" H:%" PRIu32
" S:%" PRIu32
,
589 info
->biosGeo
.cylinders
,
591 info
->biosGeo
.sectors
);
592 nbdkit_debug ("disk info: physGeo: C:%" PRIu32
" H:%" PRIu32
" S:%" PRIu32
,
593 info
->physGeo
.cylinders
,
595 info
->physGeo
.sectors
);
596 nbdkit_debug ("disk info: adapter type: %d",
597 (int) info
->adapterType
);
598 nbdkit_debug ("disk info: num links: %d", info
->numLinks
);
599 nbdkit_debug ("disk info: parent filename hint: %s",
600 info
->parentFileNameHint
? : "NULL");
601 nbdkit_debug ("disk info: uuid: %s",
602 info
->uuid
? : "NULL");
605 DEBUG_CALL ("VixDiskLib_FreeInfo", "info");
606 VixDiskLib_FreeInfo (info
);
608 return (int64_t) size
;
611 /* Read data from the file.
613 * Note that reads have to be aligned to sectors (XXX).
616 vddk_pread (void *handle
, void *buf
, uint32_t count
, uint64_t offset
,
619 struct vddk_handle
*h
= handle
;
622 /* Align to sectors. */
623 if (!IS_ALIGNED (offset
, VIXDISKLIB_SECTOR_SIZE
)) {
624 nbdkit_error ("read is not aligned to sectors");
627 if (!IS_ALIGNED (count
, VIXDISKLIB_SECTOR_SIZE
)) {
628 nbdkit_error ("read is not aligned to sectors");
631 offset
/= VIXDISKLIB_SECTOR_SIZE
;
632 count
/= VIXDISKLIB_SECTOR_SIZE
;
634 DEBUG_CALL ("VixDiskLib_Read",
635 "handle, %" PRIu64
" sectors, %" PRIu32
" sectors, buffer",
637 err
= VixDiskLib_Read (h
->handle
, offset
, count
, buf
);
639 VDDK_ERROR (err
, "VixDiskLib_Read");
646 static int vddk_flush (void *handle
, uint32_t flags
);
648 /* Write data to the file.
650 * Note that writes have to be aligned to sectors (XXX).
653 vddk_pwrite (void *handle
, const void *buf
, uint32_t count
, uint64_t offset
,
656 const bool fua
= flags
& NBDKIT_FLAG_FUA
;
657 struct vddk_handle
*h
= handle
;
660 /* Align to sectors. */
661 if (!IS_ALIGNED (offset
, VIXDISKLIB_SECTOR_SIZE
)) {
662 nbdkit_error ("read is not aligned to sectors");
665 if (!IS_ALIGNED (count
, VIXDISKLIB_SECTOR_SIZE
)) {
666 nbdkit_error ("read is not aligned to sectors");
669 offset
/= VIXDISKLIB_SECTOR_SIZE
;
670 count
/= VIXDISKLIB_SECTOR_SIZE
;
672 DEBUG_CALL ("VixDiskLib_Write",
673 "handle, %" PRIu64
" sectors, %" PRIu32
" sectors, buffer",
675 err
= VixDiskLib_Write (h
->handle
, offset
, count
, buf
);
677 VDDK_ERROR (err
, "VixDiskLib_Write");
681 if (fua
&& vddk_flush (handle
, 0) == -1)
687 /* Flush data to the file. */
689 vddk_flush (void *handle
, uint32_t flags
)
691 struct vddk_handle
*h
= handle
;
694 /* The Flush call was not available in VDDK < 6.0 so this is simply
695 * ignored on earlier versions.
697 if (VixDiskLib_Flush
== NULL
)
700 DEBUG_CALL ("VixDiskLib_Flush", "handle");
701 err
= VixDiskLib_Flush (h
->handle
);
703 VDDK_ERROR (err
, "VixDiskLib_Flush");
711 vddk_can_extents (void *handle
)
713 struct vddk_handle
*h
= handle
;
715 VixDiskLibBlockList
*block_list
;
717 /* This call was added in VDDK 6.7. In earlier versions the
718 * function pointer will be NULL and we cannot query extents.
720 if (VixDiskLib_QueryAllocatedBlocks
== NULL
) {
721 nbdkit_debug ("can_extents: VixDiskLib_QueryAllocatedBlocks == NULL, "
722 "probably this is VDDK < 6.7");
726 /* However even when the call is available it rarely works well so
727 * the best thing we can do here is to try the call and if it's
728 * non-functional return false.
730 DEBUG_CALL ("VixDiskLib_QueryAllocatedBlocks",
731 "handle, 0, %d sectors, %d sectors",
732 VIXDISKLIB_MIN_CHUNK_SIZE
, VIXDISKLIB_MIN_CHUNK_SIZE
);
733 err
= VixDiskLib_QueryAllocatedBlocks (h
->handle
,
734 0, VIXDISKLIB_MIN_CHUNK_SIZE
,
735 VIXDISKLIB_MIN_CHUNK_SIZE
,
738 DEBUG_CALL ("VixDiskLib_FreeBlockList", "block_list");
739 VixDiskLib_FreeBlockList (block_list
);
742 char *errmsg
= VixDiskLib_GetErrorText (err
, NULL
);
743 nbdkit_debug ("can_extents: VixDiskLib_QueryAllocatedBlocks test failed, "
744 "extents support will be disabled: "
745 "original error: %s",
747 VixDiskLib_FreeErrorText (errmsg
);
755 add_extent (struct nbdkit_extents
*extents
,
756 uint64_t *position
, uint64_t next_position
, bool is_hole
)
759 const uint64_t length
= next_position
- *position
;
762 type
= NBDKIT_EXTENT_HOLE
;
763 /* Images opened as single link might be backed by another file in the
764 chain, so the holes are not guaranteed to be zeroes. */
766 type
|= NBDKIT_EXTENT_ZERO
;
769 assert (*position
<= next_position
);
770 if (*position
== next_position
)
773 if (vddk_debug_extents
)
774 nbdkit_debug ("adding extent type %s at [%" PRIu64
"...%" PRIu64
"]",
775 is_hole
? "hole" : "allocated data",
776 *position
, next_position
-1);
777 if (nbdkit_add_extent (extents
, *position
, length
, type
) == -1)
780 *position
= next_position
;
785 vddk_extents (void *handle
, uint32_t count
, uint64_t offset
, uint32_t flags
,
786 struct nbdkit_extents
*extents
)
788 struct vddk_handle
*h
= handle
;
789 bool req_one
= flags
& NBDKIT_FLAG_REQ_ONE
;
790 uint64_t position
, end
, start_sector
;
793 end
= offset
+ count
;
795 /* We can only query whole chunks. Therefore start with the first
796 * chunk before offset.
799 ROUND_DOWN (offset
, VIXDISKLIB_MIN_CHUNK_SIZE
* VIXDISKLIB_SECTOR_SIZE
)
800 / VIXDISKLIB_SECTOR_SIZE
;
801 while (start_sector
* VIXDISKLIB_SECTOR_SIZE
< end
) {
804 uint64_t nr_chunks
, nr_sectors
;
805 VixDiskLibBlockList
*block_list
;
807 assert (IS_ALIGNED (start_sector
, VIXDISKLIB_MIN_CHUNK_SIZE
));
810 ROUND_UP (end
- start_sector
* VIXDISKLIB_SECTOR_SIZE
,
811 VIXDISKLIB_MIN_CHUNK_SIZE
* VIXDISKLIB_SECTOR_SIZE
)
812 / (VIXDISKLIB_MIN_CHUNK_SIZE
* VIXDISKLIB_SECTOR_SIZE
);
813 nr_chunks
= MIN (nr_chunks
, VIXDISKLIB_MAX_CHUNK_NUMBER
);
814 nr_sectors
= nr_chunks
* VIXDISKLIB_MIN_CHUNK_SIZE
;
816 DEBUG_CALL ("VixDiskLib_QueryAllocatedBlocks",
817 "handle, %" PRIu64
" sectors, %" PRIu64
" sectors, "
819 start_sector
, nr_sectors
, VIXDISKLIB_MIN_CHUNK_SIZE
);
820 err
= VixDiskLib_QueryAllocatedBlocks (h
->handle
,
821 start_sector
, nr_sectors
,
822 VIXDISKLIB_MIN_CHUNK_SIZE
,
825 VDDK_ERROR (err
, "VixDiskLib_QueryAllocatedBlocks");
829 for (i
= 0; i
< block_list
->numBlocks
; ++i
) {
830 uint64_t blk_offset
, blk_length
;
832 blk_offset
= block_list
->blocks
[i
].offset
* VIXDISKLIB_SECTOR_SIZE
;
833 blk_length
= block_list
->blocks
[i
].length
* VIXDISKLIB_SECTOR_SIZE
;
835 /* The query returns allocated blocks. We must insert holes
836 * between the blocks as necessary.
838 if ((position
< blk_offset
&&
839 add_extent (extents
, &position
, blk_offset
, true) == -1) ||
840 (add_extent (extents
,
841 &position
, blk_offset
+ blk_length
, false) == -1)) {
842 DEBUG_CALL ("VixDiskLib_FreeBlockList", "block_list");
843 VixDiskLib_FreeBlockList (block_list
);
847 DEBUG_CALL ("VixDiskLib_FreeBlockList", "block_list");
848 VixDiskLib_FreeBlockList (block_list
);
850 /* There's an implicit hole after the returned list of blocks, up
851 * to the end of the QueryAllocatedBlocks request.
853 if (add_extent (extents
,
855 (start_sector
+ nr_sectors
) * VIXDISKLIB_SECTOR_SIZE
,
859 start_sector
+= nr_sectors
;
861 /* If one extent was requested, as long as we've added an extent
862 * overlapping the original offset we're done.
864 if (req_one
&& position
> offset
)
871 static struct nbdkit_plugin plugin
= {
873 .longname
= "VMware VDDK plugin",
874 .version
= PACKAGE_VERSION
,
876 .unload
= vddk_unload
,
877 .config
= vddk_config
,
878 .config_complete
= vddk_config_complete
,
879 .config_help
= vddk_config_help
,
880 .dump_plugin
= vddk_dump_plugin
,
883 .get_size
= vddk_get_size
,
885 .pwrite
= vddk_pwrite
,
887 .can_extents
= vddk_can_extents
,
888 .extents
= vddk_extents
,
891 NBDKIT_REGISTER_PLUGIN(plugin
)