nbd: Check for libnbd
[nbdkit/ericb.git] / plugins / vddk / vddk.c
blob8ea05b15a4eef0c8c971cd9d75c1912fe52981da
1 /* nbdkit
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
6 * met:
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
30 * SUCH DAMAGE.
33 #include <config.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include <stdbool.h>
38 #include <stdint.h>
39 #include <inttypes.h>
40 #include <string.h>
41 #include <unistd.h>
42 #include <dlfcn.h>
44 #define NBDKIT_API_VERSION 2
46 #include <nbdkit-plugin.h>
48 #include "cleanup.h"
49 #include "isaligned.h"
50 #include "minmax.h"
51 #include "rounding.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. */
84 #define VDDK_MAJOR 5
85 #define VDDK_MINOR 1
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, ...) \
108 do { \
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); \
113 } while (0)
115 #define DEBUG_CALL(fn, fs, ...) \
116 nbdkit_debug ("VDDK call: %s (" fs ")", fn, ##__VA_ARGS__)
118 static void
119 trim (char *str)
121 size_t len = strlen (str);
123 if (len > 0 && str[len-1] == '\n')
124 str[len-1] = '\0';
127 /* Turn log messages from the library into nbdkit_debug. */
128 static void
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);
135 return;
138 trim (str);
140 nbdkit_debug ("%s", str);
143 /* Turn error messages from the library into nbdkit_error. */
144 static void
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);
151 return;
154 trim (str);
156 nbdkit_error ("%s", str);
159 /* Load and unload the plugin. */
160 static void
161 vddk_load (void)
163 static const char *sonames[] = {
164 /* Prefer the newest library in case multiple exist. */
165 "libvixDiskLib.so.6",
166 "libvixDiskLib.so.5",
168 size_t i;
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);
174 if (dl != NULL)
175 break;
176 if (i == 0) {
177 orig_error = dlerror ();
178 if (orig_error)
179 orig_error = strdup (orig_error);
182 if (dl == NULL) {
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]);
188 exit (EXIT_FAILURE);
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
193 * that we support.
195 #define LOAD(sym) \
196 do { \
197 sym = dlsym (dl, #sym); \
198 if (sym == NULL) { \
199 nbdkit_error ("required VDDK symbol \"%s\" is missing: %s", \
200 #sym, dlerror ()); \
201 exit (EXIT_FAILURE); \
203 } while (0)
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);
218 #undef LOAD
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);
228 #undef LOAD_OPTIONAL
231 static void
232 vddk_unload (void)
234 if (init_called) {
235 DEBUG_CALL ("VixDiskLib_Exit", "");
236 VixDiskLib_Exit ();
238 if (dl)
239 dlclose (dl);
240 free (config);
241 free (libdir);
242 free (password);
245 /* Configuration. */
246 static int
247 vddk_config (const char *key, const char *value)
249 if (strcmp (key, "config") == 0) {
250 /* See FILENAMES AND PATHS in nbdkit-plugin(3). */
251 free (config);
252 config = nbdkit_realpath (value);
253 if (!config)
254 return -1;
256 else if (strcmp (key, "cookie") == 0) {
257 cookie = value;
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.
264 filename = value;
266 else if (strcmp (key, "libdir") == 0) {
267 /* See FILENAMES AND PATHS in nbdkit-plugin(3). */
268 free (libdir);
269 libdir = nbdkit_realpath (value);
270 if (!libdir)
271 return -1;
273 else if (strcmp (key, "nfchostport") == 0) {
274 if (sscanf (value, "%d", &nfc_host_port) != 1) {
275 nbdkit_error ("cannot parse nfchostport: %s", value);
276 return -1;
279 else if (strcmp (key, "password") == 0) {
280 free (password);
281 if (nbdkit_read_password (value, &password) == -1)
282 return -1;
284 else if (strcmp (key, "port") == 0) {
285 if (sscanf (value, "%d", &port) != 1) {
286 nbdkit_error ("cannot parse port: %s", value);
287 return -1;
290 else if (strcmp (key, "server") == 0) {
291 server_name = value;
293 else if (strcmp (key, "single-link") == 0) {
294 int r = nbdkit_parse_bool (value);
296 if (r == -1)
297 return -1;
298 single_link = r;
300 else if (strcmp (key, "snapshot") == 0) {
301 snapshot_moref = value;
303 else if (strcmp (key, "thumbprint") == 0) {
304 thumb_print = value;
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);
312 if (r == -1)
313 return -1;
314 unbuffered = r;
316 else if (strcmp (key, "user") == 0) {
317 username = value;
319 else if (strcmp (key, "vimapiver") == 0) {
320 /* Ignored for backwards compatibility. */
322 else if (strcmp (key, "vm") == 0) {
323 vmx_spec = value;
325 else {
326 nbdkit_error ("unknown parameter '%s'", key);
327 return -1;
330 return 0;
333 static int
334 vddk_config_complete (void)
336 VixError err;
338 if (filename == NULL) {
339 nbdkit_error ("you must supply the file=<FILENAME> parameter "
340 "after the plugin name on the command line");
341 return -1;
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.
350 is_remote =
351 vmx_spec ||
352 server_name ||
353 username ||
354 password ||
355 cookie ||
356 thumb_print ||
357 port ||
358 nfc_host_port;
360 if (is_remote) {
361 #define missing(test, param) \
362 if (test) { \
363 nbdkit_error ("remote connection requested, missing parameter: %s", \
364 param); \
365 return -1; \
367 missing (!server_name, "server");
368 missing (!username, "user");
369 missing (!password, "password");
370 missing (!vmx_spec, "vm");
371 #undef missing
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);
384 if (err != VIX_OK) {
385 VDDK_ERROR (err, "VixDiskLib_InitEx");
386 exit (EXIT_FAILURE);
388 init_called = 1;
390 return 0;
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)."
397 static void
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.
410 Dl_info info;
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);
417 #endif
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. */
427 struct vddk_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 ();
442 else
443 ret = calloc (1, sizeof (VixDiskLibConnectParams));
445 return ret;
448 static inline void
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);
458 else
459 free (params);
462 /* Create the per-connection handle. */
463 static void *
464 vddk_open (int readonly)
466 struct vddk_handle *h;
467 VixError err;
468 uint32_t flags;
470 h = malloc (sizeof *h);
471 if (h == NULL) {
472 nbdkit_error ("malloc: %m");
473 return NULL;
476 h->params = allocate_connect_params ();
477 if (h->params == NULL) {
478 nbdkit_error ("allocate VixDiskLibConnectParams: %m");
479 goto err0;
482 if (is_remote) {
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;
490 else {
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",
509 readonly,
510 snapshot_moref ? : "NULL",
511 transport_modes ? : "NULL");
512 err = VixDiskLib_ConnectEx (h->params,
513 readonly,
514 snapshot_moref,
515 transport_modes,
516 &h->connection);
517 if (err != VIX_OK) {
518 VDDK_ERROR (err, "VixDiskLib_ConnectEx");
519 goto err1;
522 flags = 0;
523 if (readonly)
524 flags |= VIXDISKLIB_FLAG_OPEN_READ_ONLY;
525 if (single_link)
526 flags |= VIXDISKLIB_FLAG_OPEN_SINGLE_LINK;
527 if (unbuffered)
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);
533 if (err != VIX_OK) {
534 VDDK_ERROR (err, "VixDiskLib_Open: %s", filename);
535 goto err2;
538 nbdkit_debug ("transport mode: %s",
539 VixDiskLib_GetTransportMode (h->handle));
541 return h;
543 err2:
544 DEBUG_CALL ("VixDiskLib_Disconnect", "connection");
545 VixDiskLib_Disconnect (h->connection);
546 err1:
547 free_connect_params (h->params);
548 err0:
549 free (h);
550 return NULL;
553 /* Free up the per-connection handle. */
554 static void
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);
564 free (h);
567 /* Get the file size. */
568 static int64_t
569 vddk_get_size (void *handle)
571 struct vddk_handle *h = handle;
572 VixDiskLibInfo *info;
573 VixError err;
574 uint64_t size;
576 DEBUG_CALL ("VixDiskLib_GetInfo", "handle, &info");
577 err = VixDiskLib_GetInfo (h->handle, &info);
578 if (err != VIX_OK) {
579 VDDK_ERROR (err, "VixDiskLib_GetInfo");
580 return -1;
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,
590 info->biosGeo.heads,
591 info->biosGeo.sectors);
592 nbdkit_debug ("disk info: physGeo: C:%" PRIu32 " H:%" PRIu32 " S:%" PRIu32,
593 info->physGeo.cylinders,
594 info->physGeo.heads,
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).
615 static int
616 vddk_pread (void *handle, void *buf, uint32_t count, uint64_t offset,
617 uint32_t flags)
619 struct vddk_handle *h = handle;
620 VixError err;
622 /* Align to sectors. */
623 if (!IS_ALIGNED (offset, VIXDISKLIB_SECTOR_SIZE)) {
624 nbdkit_error ("read is not aligned to sectors");
625 return -1;
627 if (!IS_ALIGNED (count, VIXDISKLIB_SECTOR_SIZE)) {
628 nbdkit_error ("read is not aligned to sectors");
629 return -1;
631 offset /= VIXDISKLIB_SECTOR_SIZE;
632 count /= VIXDISKLIB_SECTOR_SIZE;
634 DEBUG_CALL ("VixDiskLib_Read",
635 "handle, %" PRIu64 " sectors, %" PRIu32 " sectors, buffer",
636 offset, count);
637 err = VixDiskLib_Read (h->handle, offset, count, buf);
638 if (err != VIX_OK) {
639 VDDK_ERROR (err, "VixDiskLib_Read");
640 return -1;
643 return 0;
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).
652 static int
653 vddk_pwrite (void *handle, const void *buf, uint32_t count, uint64_t offset,
654 uint32_t flags)
656 const bool fua = flags & NBDKIT_FLAG_FUA;
657 struct vddk_handle *h = handle;
658 VixError err;
660 /* Align to sectors. */
661 if (!IS_ALIGNED (offset, VIXDISKLIB_SECTOR_SIZE)) {
662 nbdkit_error ("read is not aligned to sectors");
663 return -1;
665 if (!IS_ALIGNED (count, VIXDISKLIB_SECTOR_SIZE)) {
666 nbdkit_error ("read is not aligned to sectors");
667 return -1;
669 offset /= VIXDISKLIB_SECTOR_SIZE;
670 count /= VIXDISKLIB_SECTOR_SIZE;
672 DEBUG_CALL ("VixDiskLib_Write",
673 "handle, %" PRIu64 " sectors, %" PRIu32 " sectors, buffer",
674 offset, count);
675 err = VixDiskLib_Write (h->handle, offset, count, buf);
676 if (err != VIX_OK) {
677 VDDK_ERROR (err, "VixDiskLib_Write");
678 return -1;
681 if (fua && vddk_flush (handle, 0) == -1)
682 return -1;
684 return 0;
687 /* Flush data to the file. */
688 static int
689 vddk_flush (void *handle, uint32_t flags)
691 struct vddk_handle *h = handle;
692 VixError err;
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)
698 return 0;
700 DEBUG_CALL ("VixDiskLib_Flush", "handle");
701 err = VixDiskLib_Flush (h->handle);
702 if (err != VIX_OK) {
703 VDDK_ERROR (err, "VixDiskLib_Flush");
704 return -1;
707 return 0;
710 static int
711 vddk_can_extents (void *handle)
713 struct vddk_handle *h = handle;
714 VixError err;
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");
723 return 0;
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,
736 &block_list);
737 if (err == VIX_OK) {
738 DEBUG_CALL ("VixDiskLib_FreeBlockList", "block_list");
739 VixDiskLib_FreeBlockList (block_list);
741 if (err != VIX_OK) {
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",
746 errmsg);
747 VixDiskLib_FreeErrorText (errmsg);
748 return 0;
751 return 1;
754 static int
755 add_extent (struct nbdkit_extents *extents,
756 uint64_t *position, uint64_t next_position, bool is_hole)
758 uint32_t type = 0;
759 const uint64_t length = next_position - *position;
761 if (is_hole) {
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. */
765 if (!single_link)
766 type |= NBDKIT_EXTENT_ZERO;
769 assert (*position <= next_position);
770 if (*position == next_position)
771 return 0;
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)
778 return -1;
780 *position = next_position;
781 return 0;
784 static int
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;
792 position = offset;
793 end = offset + count;
795 /* We can only query whole chunks. Therefore start with the first
796 * chunk before offset.
798 start_sector =
799 ROUND_DOWN (offset, VIXDISKLIB_MIN_CHUNK_SIZE * VIXDISKLIB_SECTOR_SIZE)
800 / VIXDISKLIB_SECTOR_SIZE;
801 while (start_sector * VIXDISKLIB_SECTOR_SIZE < end) {
802 VixError err;
803 uint32_t i;
804 uint64_t nr_chunks, nr_sectors;
805 VixDiskLibBlockList *block_list;
807 assert (IS_ALIGNED (start_sector, VIXDISKLIB_MIN_CHUNK_SIZE));
809 nr_chunks =
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, "
818 "%d 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,
823 &block_list);
824 if (err != VIX_OK) {
825 VDDK_ERROR (err, "VixDiskLib_QueryAllocatedBlocks");
826 return -1;
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);
844 return -1;
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,
854 &position,
855 (start_sector + nr_sectors) * VIXDISKLIB_SECTOR_SIZE,
856 true) == -1)
857 return -1;
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)
865 break;
868 return 0;
871 static struct nbdkit_plugin plugin = {
872 .name = "vddk",
873 .longname = "VMware VDDK plugin",
874 .version = PACKAGE_VERSION,
875 .load = vddk_load,
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,
881 .open = vddk_open,
882 .close = vddk_close,
883 .get_size = vddk_get_size,
884 .pread = vddk_pread,
885 .pwrite = vddk_pwrite,
886 .flush = vddk_flush,
887 .can_extents = vddk_can_extents,
888 .extents = vddk_extents,
891 NBDKIT_REGISTER_PLUGIN(plugin)