nbd: Add TLS client support
[nbdkit/ericb.git] / plugins / nbd / nbd.c
blobfa14ea1bb6ac8c4e05a1c1212e8d82b6d342bf01
1 /* nbdkit
2 * Copyright (C) 2017-2019 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 <stdlib.h>
36 #include <stddef.h>
37 #include <stdbool.h>
38 #include <stdio.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <errno.h>
42 #include <inttypes.h>
43 #include <limits.h>
44 #include <netdb.h>
45 #include <netinet/in.h>
46 #include <netinet/tcp.h>
47 #include <sys/socket.h>
48 #include <sys/un.h>
49 #include <assert.h>
50 #include <pthread.h>
51 #include <semaphore.h>
52 #include <poll.h>
54 #include <libnbd.h>
56 #define NBDKIT_API_VERSION 2
58 #include <nbdkit-plugin.h>
59 #include "byte-swapping.h"
60 #include "cleanup.h"
62 /* The per-transaction details */
63 struct transaction {
64 int64_t cookie;
65 sem_t sem;
66 uint32_t err;
67 struct transaction *next;
70 /* The per-connection handle */
71 struct handle {
72 /* These fields are read-only once initialized */
73 struct nbd_handle *nbd;
74 int fd; /* Cache of nbd_aio_get_fd */
75 int fds[2]; /* Pipe for kicking the reader thread */
76 bool readonly;
77 pthread_t reader;
79 pthread_mutex_t trans_lock; /* Covers access to trans list */
80 struct transaction *trans; /* List of pending transactions */
83 /* Connect to server via absolute name of Unix socket */
84 static char *sockname;
86 /* Connect to server via TCP socket */
87 static const char *hostname;
88 static const char *port;
90 /* Connect to server via URI */
91 static const char *uri;
93 /* Name of export on remote server, default '', ignored for oldstyle */
94 static const char *export;
96 /* Number of retries */
97 static unsigned long retry;
99 /* True to share single server connection among all clients */
100 static bool shared;
101 static struct handle *shared_handle;
103 /* Control TLS settings */
104 static int tls = -1;
105 static char *tls_certificates;
106 static int tls_verify = -1;
107 static const char *tls_username;
108 static char *tls_psk;
110 static struct handle *nbdplug_open_handle (int readonly);
111 static void nbdplug_close_handle (struct handle *h);
113 static void
114 nbdplug_unload (void)
116 if (shared)
117 nbdplug_close_handle (shared_handle);
118 free (sockname);
119 free (tls_certificates);
120 free (tls_psk);
123 /* Called for each key=value passed on the command line. This plugin
124 * accepts socket=<sockname>, hostname=<hostname>/port=<port>, or
125 * [uri=]<uri> (exactly one connection required), and optional
126 * parameters export=<name>, retry=<n>, shared=<bool> and various
127 * tls settings.
129 static int
130 nbdplug_config (const char *key, const char *value)
132 char *end;
133 int r;
135 if (strcmp (key, "socket") == 0) {
136 /* See FILENAMES AND PATHS in nbdkit-plugin(3) */
137 free (sockname);
138 sockname = nbdkit_absolute_path (value);
139 if (!sockname)
140 return -1;
142 else if (strcmp (key, "hostname") == 0)
143 hostname = value;
144 else if (strcmp (key, "port") == 0)
145 port = value;
146 else if (strcmp (key, "uri") == 0)
147 uri = value;
148 else if (strcmp (key, "export") == 0)
149 export = value;
150 else if (strcmp (key, "retry") == 0) {
151 errno = 0;
152 retry = strtoul (value, &end, 0);
153 if (value == end || errno) {
154 nbdkit_error ("could not parse retry as integer (%s)", value);
155 return -1;
158 else if (strcmp (key, "shared") == 0) {
159 r = nbdkit_parse_bool (value);
160 if (r == -1)
161 return -1;
162 shared = r;
164 else if (strcmp (key, "tls") == 0) {
165 if (strcasecmp (value, "require") == 0 ||
166 strcasecmp (value, "required") == 0 ||
167 strcasecmp (value, "force") == 0)
168 tls = 2;
169 else {
170 tls = nbdkit_parse_bool (value);
171 if (tls == -1)
172 exit (EXIT_FAILURE);
175 else if (strcmp (key, "tls-certificates") == 0) {
176 free (tls_certificates);
177 tls_certificates = nbdkit_absolute_path (value);
178 if (!tls_certificates)
179 return -1;
181 else if (strcmp (key, "tls-verify") == 0) {
182 r = nbdkit_parse_bool (value);
183 if (r == -1)
184 return -1;
185 tls_verify = r;
187 else if (strcmp (key, "tls-username") == 0)
188 tls_username = value;
189 else if (strcmp (key, "tls-psk") == 0) {
190 free (tls_psk);
191 tls_psk = nbdkit_absolute_path (value);
192 if (!tls_psk)
193 return -1;
195 else {
196 nbdkit_error ("unknown parameter '%s'", key);
197 return -1;
200 return 0;
203 /* Check the user passed exactly one socket description. */
204 static int
205 nbdplug_config_complete (void)
207 if (sockname) {
208 struct sockaddr_un sock;
210 if (hostname || port) {
211 nbdkit_error ("cannot mix Unix socket and TCP hostname/port parameters");
212 return -1;
214 else if (uri) {
215 nbdkit_error ("cannot mix Unix socket and URI parameters");
216 return -1;
218 if (strlen (sockname) > sizeof sock.sun_path) {
219 nbdkit_error ("socket file name too large");
220 return -1;
223 else if (hostname) {
224 if (uri) {
225 nbdkit_error ("cannot mix TCP hostname/port and URI parameters");
226 return -1;
228 if (!port)
229 port = "10809";
231 else if (uri) {
232 struct nbd_handle *nbd = nbd_create ();
234 if (!nbd) {
235 nbdkit_error ("unable to query libnbd details: %s", nbd_get_error ());
236 return -1;
238 if (!nbd_supports_uri (nbd)) {
239 nbdkit_error ("libnbd was compiled without uri support");
240 nbd_close (nbd);
241 return -1;
243 nbd_close (nbd);
244 } else {
245 nbdkit_error ("must supply socket=, hostname= or uri= of external NBD server");
246 return -1;
249 if (!export)
250 export = "";
252 if (tls == -1)
253 tls = tls_certificates || tls_verify >= 0 || tls_username || tls_psk;
254 if (tls > 0) {
255 struct nbd_handle *nbd = nbd_create ();
257 if (!nbd) {
258 nbdkit_error ("unable to query libnbd details: %s", nbd_get_error ());
259 return -1;
261 if (!nbd_supports_tls (nbd)) {
262 nbdkit_error ("libnbd was compiled without tls support");
263 nbd_close (nbd);
264 return -1;
266 nbd_close (nbd);
269 if (shared && (shared_handle = nbdplug_open_handle (false)) == NULL)
270 return -1;
271 return 0;
274 #define nbdplug_config_help \
275 "[uri=]<URI> URI of an NBD socket to connect to (if supported).\n" \
276 "socket=<SOCKNAME> The Unix socket to connect to.\n" \
277 "hostname=<HOST> The hostname for the TCP socket to connect to.\n" \
278 "port=<PORT> TCP port or service name to use (default 10809).\n" \
279 "export=<NAME> Export name to connect to (default \"\").\n" \
280 "retry=<N> Retry connection up to N seconds (default 0).\n" \
281 "shared=<BOOL> True to share one server connection among all clients,\n" \
282 " rather than a connection per client (default false).\n" \
283 "tls=<MODE> How to use TLS; one of 'off', 'on', or 'require'.\n" \
284 "tls-certificates=<DIR> Directory containing files for X.509 certificates.\n" \
285 "tls-verify=<BOOL> True (default for X.509) to validate server.\n" \
286 "tls-username=<NAME> Override username presented in X.509 TLS.\n" \
287 "tls-psk=<FILE> File containing Pre-Shared Key for TLS.\n" \
289 static void
290 nbdplug_dump_plugin (void)
292 struct nbd_handle *nbd = nbd_create ();
294 if (!nbd) {
295 nbdkit_error ("unable to query libnbd details: %s", nbd_get_error ());
296 exit (EXIT_FAILURE);
298 printf ("libnbd_version=%s\n", nbd_get_version (nbd));
299 printf ("libnbd_tls=%d\n", nbd_supports_tls (nbd));
300 printf ("libnbd_uri=%d\n", nbd_supports_uri (nbd));
301 nbd_close (nbd);
304 #define THREAD_MODEL NBDKIT_THREAD_MODEL_PARALLEL
306 /* Reader loop. */
307 void *
308 nbdplug_reader (void *handle)
310 struct handle *h = handle;
311 int r;
313 while (!nbd_aio_is_dead (h->nbd) && !nbd_aio_is_closed (h->nbd)) {
314 struct pollfd fds[2] = {
315 [0].fd = h->fd,
316 [1].fd = h->fds[0],
317 [1].events = POLLIN,
319 struct transaction *trans, **prev;
320 int dir;
321 char c;
323 dir = nbd_aio_get_direction (h->nbd);
324 nbdkit_debug ("polling, dir=%d", dir);
325 if (dir & LIBNBD_AIO_DIRECTION_READ)
326 fds[0].events |= POLLIN;
327 if (dir & LIBNBD_AIO_DIRECTION_WRITE)
328 fds[0].events |= POLLOUT;
329 if (poll (fds, 2, -1) == -1) {
330 nbdkit_error ("poll: %m");
331 break;
334 if (dir & LIBNBD_AIO_DIRECTION_READ && fds[0].revents & POLLIN)
335 nbd_aio_notify_read (h->nbd);
336 else if (dir & LIBNBD_AIO_DIRECTION_WRITE && fds[0].revents & POLLOUT)
337 nbd_aio_notify_write (h->nbd);
339 /* Check if we were kicked because a command was started */
340 if (fds[1].revents & POLLIN && read (h->fds[0], &c, 1) != 1) {
341 nbdkit_error ("failed to read pipe: %m");
342 break;
345 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&h->trans_lock);
346 trans = h->trans;
347 prev = &h->trans;
348 while (trans) {
349 r = nbd_aio_command_completed (h->nbd, trans->cookie);
350 if (r == -1) {
351 nbdkit_debug ("transaction %" PRId64 " failed: %s", trans->cookie,
352 nbd_get_error ());
353 trans->err = nbd_get_errno ();
354 if (!trans->err)
355 trans->err = EIO;
357 if (r) {
358 nbdkit_debug ("cookie %" PRId64 " completed state machine, status %d",
359 trans->cookie, trans->err);
360 *prev = trans->next;
361 if (sem_post (&trans->sem)) {
362 nbdkit_error ("failed to post semaphore: %m");
363 abort ();
366 else
367 prev = &trans->next;
368 trans = *prev;
372 /* Clean up any stranded in-flight requests */
373 nbdkit_debug ("state machine changed to %s", nbd_connection_state (h->nbd));
374 while (1) {
375 struct transaction *trans;
378 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&h->trans_lock);
379 trans = h->trans;
380 h->trans = trans ? trans->next : NULL;
382 if (!trans)
383 break;
384 r = nbd_aio_command_completed (h->nbd, trans->cookie);
385 if (r == -1) {
386 nbdkit_debug ("transaction %" PRId64 " failed: %s", trans->cookie,
387 nbd_get_error ());
388 trans->err = nbd_get_errno ();
389 if (!trans->err)
390 trans->err = ESHUTDOWN;
392 else if (!r)
393 trans->err = ESHUTDOWN;
394 if (sem_post (&trans->sem)) {
395 nbdkit_error ("failed to post semaphore: %m");
396 abort ();
399 nbdkit_debug ("exiting state machine thread");
400 return NULL;
403 /* Register a cookie and return a transaction. */
404 static struct transaction *
405 nbdplug_register (struct handle *h, int64_t cookie)
407 struct transaction *trans;
408 char c = 0;
410 if (cookie == -1) {
411 nbdkit_error ("command failed: %s", nbd_get_error ());
412 errno = nbd_get_errno ();
413 return NULL;
416 nbdkit_debug ("cookie %" PRId64 " started by state machine", cookie);
417 trans = calloc (1, sizeof *trans);
418 if (!trans) {
419 nbdkit_error ("unable to track transaction: %m");
420 return NULL;
423 /* While locked, kick the reader thread and add our transaction */
424 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&h->trans_lock);
425 if (write (h->fds[1], &c, 1) != 1) {
426 nbdkit_error ("write to pipe: %m");
427 free (trans);
428 return NULL;
430 if (sem_init (&trans->sem, 0, 0)) {
431 nbdkit_error ("unable to create semaphore: %m");
432 free (trans);
433 return NULL;
435 trans->cookie = cookie;
436 trans->next = h->trans;
437 h->trans = trans;
438 return trans;
441 /* Perform the reply half of a transaction. */
442 static int
443 nbdplug_reply (struct handle *h, struct transaction *trans)
445 int err;
447 if (!trans) {
448 assert (errno);
449 return -1;
452 while ((err = sem_wait (&trans->sem)) == -1 && errno == EINTR)
453 /* try again */;
454 if (err) {
455 nbdkit_debug ("failed to wait on semaphore: %m");
456 err = EIO;
458 else
459 err = trans->err;
460 if (sem_destroy (&trans->sem))
461 abort ();
462 free (trans);
463 errno = err;
464 return err ? -1 : 0;
467 /* Create the shared or per-connection handle. */
468 static struct handle *
469 nbdplug_open_handle (int readonly)
471 struct handle *h;
472 int r;
474 h = calloc (1, sizeof *h);
475 if (h == NULL) {
476 nbdkit_error ("malloc: %m");
477 return NULL;
479 if (pipe (h->fds)) {
480 nbdkit_error ("pipe: %m");
481 free (h);
482 return NULL;
485 retry:
486 h->fd = -1;
487 h->nbd = nbd_create ();
488 if (!h->nbd)
489 goto err;
490 if (nbd_set_export_name (h->nbd, export) == -1)
491 goto err;
492 if (nbd_add_meta_context (h->nbd, "base:allocation") == -1)
493 goto err;
494 if (nbd_set_tls (h->nbd, tls) == -1)
495 goto err;
496 if (tls_certificates &&
497 nbd_set_tls_certificates (h->nbd, tls_certificates) == -1)
498 goto err;
499 if (tls_verify >= 0 && nbd_set_tls_verify_peer (h->nbd, tls_verify) == -1)
500 goto err;
501 if (tls_username && nbd_set_tls_username (h->nbd, tls_username) == -1)
502 goto err;
503 if (tls_psk && nbd_set_tls_psk_file (h->nbd, tls_psk) == -1)
504 goto err;
505 if (uri)
506 r = nbd_connect_uri (h->nbd, uri);
507 else if (sockname)
508 r = nbd_connect_unix (h->nbd, sockname);
509 else
510 r = nbd_connect_tcp (h->nbd, hostname, port);
511 if (r == -1) {
512 if (retry--) {
513 nbdkit_debug ("connect failed; will try again: %s", nbd_get_error ());
514 nbd_close (h->nbd);
515 sleep (1);
516 goto retry;
518 goto err;
520 h->fd = nbd_aio_get_fd (h->nbd);
521 if (h->fd == -1)
522 goto err;
524 if (readonly)
525 h->readonly = true;
527 /* Spawn a dedicated reader thread */
528 if ((errno = pthread_mutex_init (&h->trans_lock, NULL))) {
529 nbdkit_error ("failed to initialize transaction mutex: %m");
530 goto err;
532 if ((errno = pthread_create (&h->reader, NULL, nbdplug_reader, h))) {
533 nbdkit_error ("failed to initialize reader thread: %m");
534 pthread_mutex_destroy (&h->trans_lock);
535 goto err;
538 return h;
540 err:
541 close (h->fds[0]);
542 close (h->fds[1]);
543 nbdkit_error ("failure while creating nbd handle: %s", nbd_get_error ());
544 if (h->nbd)
545 nbd_close (h->nbd);
546 free (h);
547 return NULL;
550 /* Create the per-connection handle. */
551 static void *
552 nbdplug_open (int readonly)
554 if (shared)
555 return shared_handle;
556 return nbdplug_open_handle (readonly);
559 /* Free up the shared or per-connection handle. */
560 static void
561 nbdplug_close_handle (struct handle *h)
563 if (nbd_shutdown (h->nbd) == -1)
564 nbdkit_debug ("failed to clean up handle: %s", nbd_get_error ());
565 if ((errno = pthread_join (h->reader, NULL)))
566 nbdkit_debug ("failed to join reader thread: %m");
567 close (h->fds[0]);
568 close (h->fds[1]);
569 nbd_close (h->nbd);
570 pthread_mutex_destroy (&h->trans_lock);
571 free (h);
574 /* Free up the per-connection handle. */
575 static void
576 nbdplug_close (void *handle)
578 struct handle *h = handle;
580 if (!shared)
581 nbdplug_close_handle (h);
586 /* Get the file size. */
587 static int64_t
588 nbdplug_get_size (void *handle)
590 struct handle *h = handle;
591 int64_t size = nbd_get_size (h->nbd);
593 if (size == -1) {
594 nbdkit_error ("failure to get size: %s", nbd_get_error ());
595 return -1;
597 return size;
600 static int
601 nbdplug_can_write (void *handle)
603 struct handle *h = handle;
604 int i = nbd_read_only (h->nbd);
606 if (i == -1) {
607 nbdkit_error ("failure to check readonly flag: %s", nbd_get_error ());
608 return -1;
610 return !(i || h->readonly);
613 static int
614 nbdplug_can_flush (void *handle)
616 struct handle *h = handle;
617 int i = nbd_can_flush (h->nbd);
619 if (i == -1) {
620 nbdkit_error ("failure to check flush flag: %s", nbd_get_error ());
621 return -1;
623 return i;
626 static int
627 nbdplug_is_rotational (void *handle)
629 struct handle *h = handle;
630 int i = nbd_is_rotational (h->nbd);
632 if (i == -1) {
633 nbdkit_error ("failure to check rotational flag: %s", nbd_get_error ());
634 return -1;
636 return i;
639 static int
640 nbdplug_can_trim (void *handle)
642 struct handle *h = handle;
643 int i = nbd_can_trim (h->nbd);
645 if (i == -1) {
646 nbdkit_error ("failure to check trim flag: %s", nbd_get_error ());
647 return -1;
649 return i;
652 static int
653 nbdplug_can_zero (void *handle)
655 struct handle *h = handle;
656 int i = nbd_can_zero (h->nbd);
658 if (i == -1) {
659 nbdkit_error ("failure to check zero flag: %s", nbd_get_error ());
660 return -1;
662 return i;
665 static int
666 nbdplug_can_fua (void *handle)
668 struct handle *h = handle;
669 int i = nbd_can_fua (h->nbd);
671 if (i == -1) {
672 nbdkit_error ("failure to check fua flag: %s", nbd_get_error ());
673 return -1;
675 return i ? NBDKIT_FUA_NATIVE : NBDKIT_FUA_NONE;
678 static int
679 nbdplug_can_multi_conn (void *handle)
681 struct handle *h = handle;
682 int i = nbd_can_multi_conn (h->nbd);
684 if (i == -1) {
685 nbdkit_error ("failure to check multi-conn flag: %s", nbd_get_error ());
686 return -1;
688 return i;
691 static int
692 nbdplug_can_cache (void *handle)
694 struct handle *h = handle;
695 int i = nbd_can_cache (h->nbd);
697 if (i == -1) {
698 nbdkit_error ("failure to check cache flag: %s", nbd_get_error ());
699 return -1;
701 return i ? NBDKIT_CACHE_NATIVE : NBDKIT_CACHE_NONE;
704 static int
705 nbdplug_can_extents (void *handle)
707 struct handle *h = handle;
708 int i = nbd_can_meta_context (h->nbd, "base:allocation");
710 if (i == -1) {
711 nbdkit_error ("failure to check extents ability: %s", nbd_get_error ());
712 return -1;
714 return i;
717 /* Read data from the file. */
718 static int
719 nbdplug_pread (void *handle, void *buf, uint32_t count, uint64_t offset,
720 uint32_t flags)
722 struct handle *h = handle;
723 struct transaction *s;
725 assert (!flags);
726 s = nbdplug_register (h, nbd_aio_pread (h->nbd, buf, count, offset, 0));
727 return nbdplug_reply (h, s);
730 /* Write data to the file. */
731 static int
732 nbdplug_pwrite (void *handle, const void *buf, uint32_t count, uint64_t offset,
733 uint32_t flags)
735 struct handle *h = handle;
736 struct transaction *s;
737 uint32_t f = flags & NBDKIT_FLAG_FUA ? LIBNBD_CMD_FLAG_FUA : 0;
739 assert (!(flags & ~NBDKIT_FLAG_FUA));
740 s = nbdplug_register (h, nbd_aio_pwrite (h->nbd, buf, count, offset, f));
741 return nbdplug_reply (h, s);
744 /* Write zeroes to the file. */
745 static int
746 nbdplug_zero (void *handle, uint32_t count, uint64_t offset, uint32_t flags)
748 struct handle *h = handle;
749 struct transaction *s;
750 uint32_t f = 0;
752 assert (!(flags & ~(NBDKIT_FLAG_FUA | NBDKIT_FLAG_MAY_TRIM)));
754 if (!(flags & NBDKIT_FLAG_MAY_TRIM))
755 f |= LIBNBD_CMD_FLAG_NO_HOLE;
756 if (flags & NBDKIT_FLAG_FUA)
757 f |= LIBNBD_CMD_FLAG_FUA;
758 s = nbdplug_register (h, nbd_aio_zero (h->nbd, count, offset, f));
759 return nbdplug_reply (h, s);
762 /* Trim a portion of the file. */
763 static int
764 nbdplug_trim (void *handle, uint32_t count, uint64_t offset, uint32_t flags)
766 struct handle *h = handle;
767 struct transaction *s;
768 uint32_t f = flags & NBDKIT_FLAG_FUA ? LIBNBD_CMD_FLAG_FUA : 0;
770 assert (!(flags & ~NBDKIT_FLAG_FUA));
771 s = nbdplug_register (h, nbd_aio_trim (h->nbd, count, offset, f));
772 return nbdplug_reply (h, s);
775 /* Flush the file to disk. */
776 static int
777 nbdplug_flush (void *handle, uint32_t flags)
779 struct handle *h = handle;
780 struct transaction *s;
782 assert (!flags);
783 s = nbdplug_register (h, nbd_aio_flush (h->nbd, 0));
784 return nbdplug_reply (h, s);
787 static int
788 nbdplug_extent (void *opaque, const char *metacontext, uint64_t offset,
789 uint32_t *entries, size_t nr_entries)
791 struct nbdkit_extents *extents = opaque;
793 assert (strcmp (metacontext, "base:allocation") == 0);
794 assert (nr_entries % 2 == 0);
795 while (nr_entries) {
796 /* We rely on the fact that NBDKIT_EXTENT_* match NBD_STATE_* */
797 if (nbdkit_add_extent (extents, offset, entries[0], entries[1]) == -1)
798 return -1;
799 offset += entries[0];
800 entries += 2;
801 nr_entries -= 2;
803 return 0;
806 /* Read extents of the file. */
807 static int
808 nbdplug_extents (void *handle, uint32_t count, uint64_t offset,
809 uint32_t flags, struct nbdkit_extents *extents)
811 struct handle *h = handle;
812 struct transaction *s;
813 uint32_t f = flags & NBDKIT_FLAG_REQ_ONE ? LIBNBD_CMD_FLAG_REQ_ONE : 0;
815 assert (!(flags & ~NBDKIT_FLAG_REQ_ONE));
816 s = nbdplug_register (h, nbd_aio_block_status (h->nbd, count, offset,
817 extents, nbdplug_extent, f));
818 return nbdplug_reply (h, s);
821 /* Cache a portion of the file. */
822 static int
823 nbdplug_cache (void *handle, uint32_t count, uint64_t offset, uint32_t flags)
825 struct handle *h = handle;
826 struct transaction *s;
828 assert (!flags);
829 s = nbdplug_register (h, nbd_aio_cache (h->nbd, count, offset, 0));
830 return nbdplug_reply (h, s);
833 static struct nbdkit_plugin plugin = {
834 .name = "nbd",
835 .longname = "nbdkit nbd plugin",
836 .version = PACKAGE_VERSION,
837 .unload = nbdplug_unload,
838 .config = nbdplug_config,
839 .config_complete = nbdplug_config_complete,
840 .config_help = nbdplug_config_help,
841 .magic_config_key = "uri",
842 .dump_plugin = nbdplug_dump_plugin,
843 .open = nbdplug_open,
844 .close = nbdplug_close,
845 .get_size = nbdplug_get_size,
846 .can_write = nbdplug_can_write,
847 .can_flush = nbdplug_can_flush,
848 .is_rotational = nbdplug_is_rotational,
849 .can_trim = nbdplug_can_trim,
850 .can_zero = nbdplug_can_zero,
851 .can_fua = nbdplug_can_fua,
852 .can_multi_conn = nbdplug_can_multi_conn,
853 .can_extents = nbdplug_can_extents,
854 .can_cache = nbdplug_can_cache,
855 .pread = nbdplug_pread,
856 .pwrite = nbdplug_pwrite,
857 .zero = nbdplug_zero,
858 .flush = nbdplug_flush,
859 .trim = nbdplug_trim,
860 .extents = nbdplug_extents,
861 .cache = nbdplug_cache,
862 .errno_is_preserved = 1,
865 NBDKIT_REGISTER_PLUGIN (plugin)