2 * Copyright (C) 2018-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
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
33 /* This test constructs a plugin and 3 layers of filters:
35 * NBD ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌────────┐
36 * client ───▶│ filter3 │───▶│ filter2 │───▶│ filter1 │───▶│ plugin │
37 * request └─────────┘ └─────────┘ └─────────┘ └────────┘
39 * We then run every possible request and ensure that each method in
40 * each filter and the plugin is called in the right order. This
41 * cannot be done with libguestfs or qemu-io, instead we must make NBD
42 * client requests over a socket directly.
55 #include <sys/types.h>
56 #include <sys/socket.h>
61 #include "byte-swapping.h"
63 #include "exit-with-parent.h"
64 #include "nbd-protocol.h"
66 /* Declare program_name. */
67 #if HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME == 1
69 #define program_name program_invocation_short_name
71 #define program_name "nbdkit"
74 static void *start_log_capture (void *);
75 static void log_verify_seen (const char *msg
);
76 static void log_verify_seen_in_order (const char *msg
, ...)
77 __attribute__((sentinel
));
78 static void log_free (void);
87 main (int argc
, char *argv
[])
95 struct nbd_new_handshake handshake
;
97 struct nbd_new_option option
;
98 struct nbd_export_name_option_reply handshake_finish
;
100 struct nbd_request request
;
101 struct nbd_simple_reply reply
;
104 #ifndef HAVE_EXIT_WITH_PARENT
105 printf ("%s: this test requires --exit-with-parent functionality\n",
110 /* Socket for communicating with nbdkit. The test doesn't care about
111 * fd leaks, so we don't bother with CLOEXEC.
113 if (socketpair (AF_LOCAL
, SOCK_STREAM
, 0, sfd
) == -1) {
114 perror ("socketpair");
120 if (pipe (pfd
) == -1) { /* Pipe for log messages. */
125 if (pid
== 0) { /* Child. */
131 execlp ("nbdkit", "nbdkit",
132 "--exit-with-parent",
134 /* Because of asynchronous shutdown with threads, finalize
135 * isn't reliably called unless we disable parallel.
138 "--filter", ".libs/test-layers-filter3.so",
139 "--filter", ".libs/test-layers-filter2.so",
140 "--filter", ".libs/test-layers-filter1.so",
141 ".libs/test-layers-plugin.so",
144 perror ("exec: nbdkit");
145 _exit (EXIT_FAILURE
);
152 fprintf (stderr
, "%s: nbdkit running\n", program_name
);
154 /* Start a thread which will just listen on the pipe and
155 * place the log messages in a memory buffer.
157 err
= pthread_create (&thread
, NULL
, start_log_capture
, &pfd
[0]);
160 perror ("pthread_create");
163 err
= pthread_detach (thread
);
166 perror ("pthread_detach");
170 /* Note for the purposes of this test we're not very careful about
171 * checking for errors (except for the bare minimum) or handling the
172 * full NBD protocol. This is because we can be certain about
173 * exactly which server we are connecting to and what it supports.
174 * Don't use this as example code for connecting to NBD servers.
176 * Expect to receive newstyle handshake.
178 if (recv (sock
, &handshake
, sizeof handshake
,
179 MSG_WAITALL
) != sizeof handshake
) {
180 perror ("recv: handshake");
183 if (be64toh (handshake
.nbdmagic
) != NBD_MAGIC
||
184 be64toh (handshake
.version
) != NBD_NEW_VERSION
) {
185 fprintf (stderr
, "%s: unexpected NBDMAGIC or version\n",
190 /* Send client flags. */
191 cflags
= htobe32 (be16toh (handshake
.gflags
));
192 if (send (sock
, &cflags
, sizeof cflags
, 0) != sizeof cflags
) {
193 perror ("send: flags");
197 /* Send NBD_OPT_EXPORT_NAME with no export name. */
198 option
.version
= htobe64 (NBD_NEW_VERSION
);
199 option
.option
= htobe32 (NBD_OPT_EXPORT_NAME
);
200 option
.optlen
= htobe32 (0);
201 if (send (sock
, &option
, sizeof option
, 0) != sizeof option
) {
202 perror ("send: option");
206 /* Receive handshake finish. */
207 if (recv (sock
, &handshake_finish
, sizeof handshake_finish
- 124,
208 MSG_WAITALL
) != sizeof handshake_finish
- 124) {
209 perror ("recv: handshake finish");
213 /* Verify export size (see tests/test-layers-plugin.c). */
214 if (be64toh (handshake_finish
.exportsize
) != 1024) {
215 fprintf (stderr
, "%s: unexpected export size %" PRIu64
" != 1024\n",
216 program_name
, be64toh (handshake_finish
.exportsize
));
220 /* Verify export flags. */
221 eflags
= be16toh (handshake_finish
.eflags
);
222 if ((eflags
& NBD_FLAG_READ_ONLY
) != 0) {
223 fprintf (stderr
, "%s: unexpected eflags: NBD_FLAG_READ_ONLY not clear\n",
227 if ((eflags
& NBD_FLAG_SEND_FLUSH
) == 0) {
228 fprintf (stderr
, "%s: unexpected eflags: NBD_FLAG_SEND_FLUSH not set\n",
232 if ((eflags
& NBD_FLAG_SEND_FUA
) == 0) {
233 fprintf (stderr
, "%s: unexpected eflags: NBD_FLAG_SEND_FUA not set\n",
237 if ((eflags
& NBD_FLAG_ROTATIONAL
) == 0) {
238 fprintf (stderr
, "%s: unexpected eflags: NBD_FLAG_ROTATIONAL not set\n",
242 if ((eflags
& NBD_FLAG_SEND_TRIM
) == 0) {
243 fprintf (stderr
, "%s: unexpected eflags: NBD_FLAG_SEND_TRIM not set\n",
247 if ((eflags
& NBD_FLAG_SEND_WRITE_ZEROES
) == 0) {
249 "%s: unexpected eflags: NBD_FLAG_SEND_WRITE_ZEROES not set\n",
254 /* Sleep briefly to allow the log to catch up. */
257 /* Verify expected log messages were seen during the handshake and
258 * option negotiation phases.
261 /* Plugin and 3 filters should run the load method in any order. */
262 log_verify_seen ("test_layers_plugin_load");
263 log_verify_seen ("filter1: test_layers_filter_load");
264 log_verify_seen ("filter2: test_layers_filter_load");
265 log_verify_seen ("filter3: test_layers_filter_load");
267 /* config methods called in order. */
268 log_verify_seen_in_order
269 ("testlayersfilter3: config key=foo, value=bar",
270 "filter3: test_layers_filter_config",
271 "testlayersfilter2: config key=foo, value=bar",
272 "filter2: test_layers_filter_config",
273 "testlayersfilter1: config key=foo, value=bar",
274 "filter1: test_layers_filter_config",
275 "testlayersplugin: config key=foo, value=bar",
276 "test_layers_plugin_config",
279 /* config_complete methods called in order. */
280 log_verify_seen_in_order
281 ("testlayersfilter3: config_complete",
282 "filter3: test_layers_filter_config_complete",
283 "testlayersfilter2: config_complete",
284 "filter2: test_layers_filter_config_complete",
285 "testlayersfilter1: config_complete",
286 "filter1: test_layers_filter_config_complete",
287 "testlayersplugin: config_complete",
288 "test_layers_plugin_config_complete",
291 /* open methods called in outer-to-inner order, but thanks to next
292 * pointer, complete in inner-to-outer order. */
293 log_verify_seen_in_order
294 ("testlayersfilter3: open readonly=0",
295 "testlayersfilter2: open readonly=0",
296 "testlayersfilter1: open readonly=0",
297 "testlayersplugin: open readonly=0",
298 "test_layers_plugin_open",
299 "filter1: test_layers_filter_open",
300 "filter2: test_layers_filter_open",
301 "filter3: test_layers_filter_open",
304 /* prepare methods called in inner-to-outer order.
306 * Note that prepare methods only exist for filters, and they must
307 * be called from inner to outer (but finalize methods below are
308 * called the other way around).
310 log_verify_seen_in_order
311 ("filter1: test_layers_filter_prepare",
312 "filter2: test_layers_filter_prepare",
313 "filter3: test_layers_filter_prepare",
316 /* get_size methods called in order. */
317 log_verify_seen_in_order
318 ("filter3: test_layers_filter_get_size",
319 "filter2: test_layers_filter_get_size",
320 "filter1: test_layers_filter_get_size",
321 "test_layers_plugin_get_size",
324 /* can_* / is_* methods called in order. */
325 log_verify_seen_in_order
326 ("filter3: test_layers_filter_can_write",
327 "filter2: test_layers_filter_can_write",
328 "filter1: test_layers_filter_can_write",
329 "test_layers_plugin_can_write",
331 log_verify_seen_in_order
332 ("filter3: test_layers_filter_can_zero",
333 "filter2: test_layers_filter_can_zero",
334 "filter1: test_layers_filter_can_zero",
335 "test_layers_plugin_can_zero",
337 log_verify_seen_in_order
338 ("filter3: test_layers_filter_can_trim",
339 "filter2: test_layers_filter_can_trim",
340 "filter1: test_layers_filter_can_trim",
341 "test_layers_plugin_can_trim",
343 log_verify_seen_in_order
344 ("filter3: test_layers_filter_can_fua",
345 "filter2: test_layers_filter_can_fua",
346 "filter1: test_layers_filter_can_fua",
347 "test_layers_plugin_can_fua",
349 log_verify_seen_in_order
350 ("filter3: test_layers_filter_can_flush",
351 "filter2: test_layers_filter_can_flush",
352 "filter1: test_layers_filter_can_flush",
353 "test_layers_plugin_can_flush",
355 log_verify_seen_in_order
356 ("filter3: test_layers_filter_is_rotational",
357 "filter2: test_layers_filter_is_rotational",
358 "filter1: test_layers_filter_is_rotational",
359 "test_layers_plugin_is_rotational",
361 log_verify_seen_in_order
362 ("filter3: test_layers_filter_can_multi_conn",
363 "filter2: test_layers_filter_can_multi_conn",
364 "filter1: test_layers_filter_can_multi_conn",
365 "test_layers_plugin_can_multi_conn",
367 log_verify_seen_in_order
368 ("filter3: test_layers_filter_can_extents",
369 "filter2: test_layers_filter_can_extents",
370 "filter1: test_layers_filter_can_extents",
371 "test_layers_plugin_can_extents",
373 log_verify_seen_in_order
374 ("filter3: test_layers_filter_can_cache",
375 "filter2: test_layers_filter_can_cache",
376 "filter1: test_layers_filter_can_cache",
377 "test_layers_plugin_can_cache",
380 fprintf (stderr
, "%s: protocol connected\n", program_name
);
382 /* Send one command of each type. */
383 request
.magic
= htobe32 (NBD_REQUEST_MAGIC
);
384 request
.handle
= htobe64 (0);
386 request
.type
= htobe16 (NBD_CMD_READ
);
387 request
.offset
= htobe64 (0);
388 request
.count
= htobe32 (512);
389 request
.flags
= htobe16 (0);
390 if (send (sock
, &request
, sizeof request
, 0) != sizeof request
) {
391 perror ("send: NBD_CMD_READ");
394 if (recv (sock
, &reply
, sizeof reply
, MSG_WAITALL
) != sizeof reply
) {
395 perror ("recv: NBD_CMD_READ reply");
398 if (reply
.error
!= NBD_SUCCESS
) {
399 fprintf (stderr
, "%s: NBD_CMD_READ failed with %d\n",
400 program_name
, reply
.error
);
403 if (recv (sock
, data
, sizeof data
, MSG_WAITALL
) != sizeof data
) {
404 perror ("recv: NBD_CMD_READ data");
409 log_verify_seen_in_order
410 ("testlayersfilter3: pread count=512 offset=0",
411 "filter3: test_layers_filter_pread",
412 "testlayersfilter2: pread count=512 offset=0",
413 "filter2: test_layers_filter_pread",
414 "testlayersfilter1: pread count=512 offset=0",
415 "filter1: test_layers_filter_pread",
416 "testlayersplugin: pread count=512 offset=0",
417 "test_layers_plugin_pread",
420 request
.type
= htobe16 (NBD_CMD_WRITE
);
421 request
.offset
= htobe64 (0);
422 request
.count
= htobe32 (512);
423 request
.flags
= htobe16 (0);
424 if (send (sock
, &request
, sizeof request
, 0) != sizeof request
) {
425 perror ("send: NBD_CMD_WRITE");
428 if (send (sock
, data
, sizeof data
, 0) != sizeof data
) {
429 perror ("send: NBD_CMD_WRITE data");
432 if (recv (sock
, &reply
, sizeof reply
, MSG_WAITALL
) != sizeof reply
) {
433 perror ("recv: NBD_CMD_WRITE");
436 if (reply
.error
!= NBD_SUCCESS
) {
437 fprintf (stderr
, "%s: NBD_CMD_WRITE failed with %d\n",
438 program_name
, reply
.error
);
443 log_verify_seen_in_order
444 ("testlayersfilter3: pwrite count=512 offset=0 fua=0",
445 "filter3: test_layers_filter_pwrite",
446 "testlayersfilter2: pwrite count=512 offset=0 fua=0",
447 "filter2: test_layers_filter_pwrite",
448 "testlayersfilter1: pwrite count=512 offset=0 fua=0",
449 "filter1: test_layers_filter_pwrite",
450 "testlayersplugin: pwrite count=512 offset=0 fua=0",
451 "test_layers_plugin_pwrite",
454 request
.type
= htobe16 (NBD_CMD_FLUSH
);
455 request
.offset
= htobe64 (0);
456 request
.count
= htobe32 (0);
457 request
.flags
= htobe16 (0);
458 if (send (sock
, &request
, sizeof request
, 0) != sizeof request
) {
459 perror ("send: NBD_CMD_FLUSH");
462 if (recv (sock
, &reply
, sizeof reply
, MSG_WAITALL
) != sizeof reply
) {
463 perror ("recv: NBD_CMD_FLUSH");
466 if (reply
.error
!= NBD_SUCCESS
) {
467 fprintf (stderr
, "%s: NBD_CMD_FLUSH failed with %d\n",
468 program_name
, reply
.error
);
473 log_verify_seen_in_order
474 ("testlayersfilter3: flush",
475 "filter3: test_layers_filter_flush",
476 "testlayersfilter2: flush",
477 "filter2: test_layers_filter_flush",
478 "testlayersfilter1: flush",
479 "filter1: test_layers_filter_flush",
480 "testlayersplugin: flush",
481 "test_layers_plugin_flush",
484 request
.type
= htobe16 (NBD_CMD_TRIM
);
485 request
.offset
= htobe64 (0);
486 request
.count
= htobe32 (512);
487 request
.flags
= htobe16 (0);
488 if (send (sock
, &request
, sizeof request
, 0) != sizeof request
) {
489 perror ("send: NBD_CMD_TRIM");
492 if (recv (sock
, &reply
, sizeof reply
, MSG_WAITALL
) != sizeof reply
) {
493 perror ("recv: NBD_CMD_TRIM");
496 if (reply
.error
!= NBD_SUCCESS
) {
497 fprintf (stderr
, "%s: NBD_CMD_TRIM failed with %d\n",
498 program_name
, reply
.error
);
503 log_verify_seen_in_order
504 ("testlayersfilter3: trim count=512 offset=0 fua=0",
505 "filter3: test_layers_filter_trim",
506 "testlayersfilter2: trim count=512 offset=0 fua=0",
507 "filter2: test_layers_filter_trim",
508 "testlayersfilter1: trim count=512 offset=0 fua=0",
509 "filter1: test_layers_filter_trim",
510 "testlayersplugin: trim count=512 offset=0 fua=0",
511 "test_layers_plugin_trim",
514 request
.type
= htobe16 (NBD_CMD_WRITE_ZEROES
);
515 request
.offset
= htobe64 (0);
516 request
.count
= htobe32 (512);
517 request
.flags
= htobe16 (0);
518 if (send (sock
, &request
, sizeof request
, 0) != sizeof request
) {
519 perror ("send: NBD_CMD_WRITE_ZEROES");
522 if (recv (sock
, &reply
, sizeof reply
, MSG_WAITALL
) != sizeof reply
) {
523 perror ("recv: NBD_CMD_WRITE_ZEROES");
526 if (reply
.error
!= NBD_SUCCESS
) {
527 fprintf (stderr
, "%s: NBD_CMD_WRITE_ZEROES failed with %d\n",
528 program_name
, reply
.error
);
533 log_verify_seen_in_order
534 ("testlayersfilter3: zero count=512 offset=0 may_trim=1 fua=0 fast=0",
535 "filter3: test_layers_filter_zero",
536 "testlayersfilter2: zero count=512 offset=0 may_trim=1 fua=0 fast=0",
537 "filter2: test_layers_filter_zero",
538 "testlayersfilter1: zero count=512 offset=0 may_trim=1 fua=0 fast=0",
539 "filter1: test_layers_filter_zero",
540 "testlayersplugin: zero count=512 offset=0 may_trim=1 fua=0 fast=0",
541 "test_layers_plugin_zero",
544 request
.type
= htobe16 (NBD_CMD_CACHE
);
545 request
.offset
= htobe64 (0);
546 request
.count
= htobe32 (512);
547 request
.flags
= htobe16 (0);
548 if (send (sock
, &request
, sizeof request
, 0) != sizeof request
) {
549 perror ("send: NBD_CMD_CACHE");
552 if (recv (sock
, &reply
, sizeof reply
, MSG_WAITALL
) != sizeof reply
) {
553 perror ("recv: NBD_CMD_CACHE");
556 if (reply
.error
!= NBD_SUCCESS
) {
557 fprintf (stderr
, "%s: NBD_CMD_CACHE failed with %d\n",
558 program_name
, reply
.error
);
563 log_verify_seen_in_order
564 ("testlayersfilter3: cache count=512 offset=0",
565 "filter3: test_layers_filter_cache",
566 "testlayersfilter2: cache count=512 offset=0",
567 "filter2: test_layers_filter_cache",
568 "testlayersfilter1: cache count=512 offset=0",
569 "filter1: test_layers_filter_cache",
570 "testlayersplugin: cache count=512 offset=0",
571 "test_layers_plugin_cache",
574 /* XXX We should test NBD_CMD_BLOCK_STATUS here. However it
575 * requires that we negotiate structured replies and base:allocation
576 * in the handshake, and the format of the reply is more complex
577 * than what we expect in this naive test.
580 /* Close the connection. */
581 fprintf (stderr
, "%s: closing the connection\n", program_name
);
582 request
.type
= htobe16 (NBD_CMD_DISC
);
583 request
.offset
= htobe64 (0);
584 request
.count
= htobe32 (0);
585 request
.flags
= htobe16 (0);
586 if (send (sock
, &request
, sizeof request
, 0) != sizeof request
) {
587 perror ("send: NBD_CMD_DISC");
590 /* (no reply from NBD_CMD_DISC) */
593 /* Clean up the child process. */
594 if (waitpid (pid
, NULL
, 0) == -1)
597 /* finalize methods called in reverse order of prepare */
599 log_verify_seen_in_order
600 ("filter3: test_layers_filter_finalize",
601 "filter2: test_layers_filter_finalize",
602 "filter1: test_layers_filter_finalize",
605 /* close methods called outer-to-inner, which is reverse of completion
607 log_verify_seen_in_order
608 ("filter3: test_layers_filter_close",
609 "filter2: test_layers_filter_close",
610 "filter1: test_layers_filter_close",
611 "test_layers_plugin_close",
614 /* unload methods should be run in any order. */
615 log_verify_seen ("test_layers_plugin_unload");
616 log_verify_seen ("filter1: test_layers_filter_unload");
617 log_verify_seen ("filter2: test_layers_filter_unload");
618 log_verify_seen ("filter3: test_layers_filter_unload");
625 /* The log from nbdkit is captured in a separate thread. */
626 static char *log_buf
= NULL
;
627 static size_t log_len
= 0;
628 static pthread_mutex_t log_lock
= PTHREAD_MUTEX_INITIALIZER
;
631 start_log_capture (void *arg
)
633 int fd
= *(int *)arg
;
634 size_t allocated
= 0;
639 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&log_lock
);
640 if (allocated
<= log_len
) {
642 log_buf
= realloc (log_buf
, allocated
);
643 if (log_buf
== NULL
) {
644 perror ("log: realloc");
650 r
= read (fd
, &log_buf
[log_len
], allocated
-log_len
);
652 perror ("log: read");
658 /* Dump the log as we receive it to stderr, for debugging. */
659 if (write (2, &log_buf
[log_len
], r
) == -1)
660 perror ("log: write");
662 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&log_lock
);
666 /* nbdkit closed the connection. */
670 /* These functions are called from the main thread to verify messages
671 * appeared as expected in the log.
673 * NB: The log buffer is NOT \0-terminated.
676 static void no_message_error (const char *msg
) __attribute__((noreturn
));
679 no_message_error (const char *msg
)
681 fprintf (stderr
, "%s: did not find expected message \"%s\"\n",
687 log_verify_seen (const char *msg
)
689 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&log_lock
);
690 if (memmem (log_buf
, log_len
, msg
, strlen (msg
)) == NULL
)
691 no_message_error (msg
);
694 static void messages_out_of_order (const char *msg1
, const char *msg2
)
695 __attribute__((noreturn
));
698 messages_out_of_order (const char *msg1
, const char *msg2
)
700 fprintf (stderr
, "%s: message \"%s\" expected before message \"%s\"\n",
701 program_name
, msg1
, msg2
);
706 log_verify_seen_in_order (const char *msg
, ...)
710 const char *prev_msg
, *curr_msg
;
712 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&log_lock
);
714 prev
= memmem (log_buf
, log_len
, msg
, strlen (msg
));
715 if (prev
== NULL
) no_message_error (msg
);
718 va_start (args
, msg
);
719 while ((curr_msg
= va_arg (args
, char *)) != NULL
) {
720 curr
= memmem (log_buf
, log_len
, curr_msg
, strlen (curr_msg
));
721 if (curr
== NULL
) no_message_error (curr_msg
);
722 if (prev
> curr
) messages_out_of_order (prev_msg
, curr_msg
);
732 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&log_lock
);