Revert "ci: skip "lib/test-fork-safe-execvpe.sh" on Alpine Linux"
[libnbd.git] / interop / dirty-bitmap.c
blobfc32e3352eaf68d782c4ce3d6c064bd5c4046446
1 /* NBD client library in userspace
2 * Copyright Red Hat
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 /* Test reading qemu dirty bitmap. */
21 #include <config.h>
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <unistd.h>
27 #include <assert.h>
28 #include <stdbool.h>
29 #include <errno.h>
31 #include <libnbd.h>
33 static const char *bitmap;
35 struct data {
36 bool req_one; /* input: true if req_one was passed to request */
37 int count; /* input: count of expected remaining calls */
38 bool fail; /* input: true to return failure */
39 bool seen_base; /* output: true if base:allocation encountered */
40 bool seen_dirty; /* output: true if qemu:dirty-bitmap encountered */
43 static int
44 cb (void *opaque, const char *metacontext, uint64_t offset,
45 uint32_t *entries, size_t len, int *error)
47 struct data *data = opaque;
49 /* libnbd does not actually verify that a server is fully compliant
50 * to the spec; the asserts marked [qemu-nbd] are thus dependent on
51 * the fact that qemu-nbd is compliant. Furthermore, qemu 5.2 and
52 * 6.0 disagree on whether base:allocation includes the hole bit for
53 * the zeroes at 512k (both answers are compliant); but we care more
54 * that the zeroes show up in the dirty bitmap
56 assert (offset == 0);
57 assert (!*error || (data->fail && data->count == 1 && *error == EPROTO));
58 assert (data->count-- > 0); /* [qemu-nbd] */
60 if (strcmp (metacontext, LIBNBD_CONTEXT_BASE_ALLOCATION) == 0) {
61 assert (!data->seen_base); /* [qemu-nbd] */
62 data->seen_base = true;
63 if (data->req_one)
64 assert (len == 2); /* [qemu-nbd] */
65 else
66 assert ((len & 1) == 0 && len > 2); /* [qemu-nbd] */
68 /* Data block offset 0 size 128k */
69 assert (entries[0] == 131072); assert (entries[1] == 0);
70 if (!data->req_one) {
71 if (len == 4) {
72 /* hole|zero offset 128k size 896k */
73 assert (entries[2] == 917504);
74 assert (entries[3] == (LIBNBD_STATE_HOLE|
75 LIBNBD_STATE_ZERO));
77 else {
78 assert (len == 8);
79 /* hole|zero offset 128k size 384k */
80 assert (entries[2] == 393216);
81 assert (entries[3] == (LIBNBD_STATE_HOLE|
82 LIBNBD_STATE_ZERO));
83 /* allocated zero offset 512k size 64k */
84 assert (entries[4] == 65536);
85 assert (entries[5] == LIBNBD_STATE_ZERO);
86 /* hole|zero offset 576k size 448k */
87 assert (entries[6] == 458752);
88 assert (entries[7] == (LIBNBD_STATE_HOLE|
89 LIBNBD_STATE_ZERO));
93 else if (strcmp (metacontext, bitmap) == 0) {
94 assert (!data->seen_dirty); /* [qemu-nbd] */
95 data->seen_dirty = true;
96 assert (len == (data->req_one ? 2 : 10)); /* [qemu-nbd] */
98 assert (entries[0] == 65536); assert (entries[1] == 0);
99 if (!data->req_one) {
100 /* dirty block offset 64K size 64K */
101 assert (entries[2] == 65536); assert (entries[3] == 1);
102 assert (entries[4] == 393216); assert (entries[5] == 0);
103 /* dirty block offset 512K size 64K */
104 assert (entries[6] == 65536); assert (entries[7] == 1);
105 assert (entries[8] == 458752); assert (entries[9] == 0);
108 else {
109 fprintf (stderr, "unexpected context %s\n", metacontext);
110 exit (EXIT_FAILURE);
113 if (data->fail) {
114 /* Something NBD servers can't send */
115 *error = data->count == 1 ? EPROTO : ECONNREFUSED;
116 return -1;
118 return 0;
122 main (int argc, char *argv[])
124 struct nbd_handle *nbd;
125 int64_t exportsize;
126 struct data data;
127 char c;
129 if (argc < 3) {
130 fprintf (stderr, "%s bitmap qemu-nbd [args ...]\n", argv[0]);
131 exit (EXIT_FAILURE);
133 bitmap = argv[1];
135 nbd = nbd_create ();
136 if (nbd == NULL) {
137 fprintf (stderr, "%s\n", nbd_get_error ());
138 exit (EXIT_FAILURE);
141 nbd_add_meta_context (nbd, LIBNBD_CONTEXT_BASE_ALLOCATION);
142 nbd_add_meta_context (nbd, bitmap);
144 if (nbd_connect_systemd_socket_activation (nbd, &argv[2]) == -1) {
145 fprintf (stderr, "%s\n", nbd_get_error ());
146 exit (EXIT_FAILURE);
149 exportsize = nbd_get_size (nbd);
150 if (exportsize == -1) {
151 fprintf (stderr, "%s\n", nbd_get_error ());
152 exit (EXIT_FAILURE);
155 data = (struct data) { .count = 2, };
156 if (nbd_block_status (nbd, exportsize, 0,
157 (nbd_extent_callback) { .callback = cb, .user_data = &data },
158 0) == -1) {
159 fprintf (stderr, "%s\n", nbd_get_error ());
160 exit (EXIT_FAILURE);
162 assert (data.seen_base && data.seen_dirty);
164 data = (struct data) { .req_one = true, .count = 2, };
165 if (nbd_block_status (nbd, exportsize, 0,
166 (nbd_extent_callback) { .callback = cb, .user_data = &data },
167 LIBNBD_CMD_FLAG_REQ_ONE) == -1) {
168 fprintf (stderr, "%s\n", nbd_get_error ());
169 exit (EXIT_FAILURE);
171 assert (data.seen_base && data.seen_dirty);
173 /* Trigger a failed callback, to prove connection stays up. */
174 data = (struct data) { .count = 2, .fail = true, };
175 if (nbd_block_status (nbd, exportsize, 0,
176 (nbd_extent_callback) { .callback = cb, .user_data = &data },
177 0) != -1) {
178 fprintf (stderr, "unexpected block status success\n");
179 exit (EXIT_FAILURE);
181 assert (nbd_get_errno () == EPROTO && nbd_aio_is_ready (nbd));
182 assert (data.seen_base && data.seen_dirty);
184 if (nbd_pread (nbd, &c, 1, 0, 0) == -1) {
185 fprintf (stderr, "%s\n", nbd_get_error ());
186 exit (EXIT_FAILURE);
189 if (nbd_shutdown (nbd, 0) == -1) {
190 fprintf (stderr, "%s\n", nbd_get_error ());
191 exit (EXIT_FAILURE);
194 nbd_close (nbd);
196 exit (EXIT_SUCCESS);