1 /* NBD client library in userspace
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. */
33 static const char *bitmap
;
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 */
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
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;
64 assert (len
== 2); /* [qemu-nbd] */
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);
72 /* hole|zero offset 128k size 896k */
73 assert (entries
[2] == 917504);
74 assert (entries
[3] == (LIBNBD_STATE_HOLE
|
79 /* hole|zero offset 128k size 384k */
80 assert (entries
[2] == 393216);
81 assert (entries
[3] == (LIBNBD_STATE_HOLE
|
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
|
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);
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);
109 fprintf (stderr
, "unexpected context %s\n", metacontext
);
114 /* Something NBD servers can't send */
115 *error
= data
->count
== 1 ? EPROTO
: ECONNREFUSED
;
122 main (int argc
, char *argv
[])
124 struct nbd_handle
*nbd
;
130 fprintf (stderr
, "%s bitmap qemu-nbd [args ...]\n", argv
[0]);
137 fprintf (stderr
, "%s\n", nbd_get_error ());
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 ());
149 exportsize
= nbd_get_size (nbd
);
150 if (exportsize
== -1) {
151 fprintf (stderr
, "%s\n", nbd_get_error ());
155 data
= (struct data
) { .count
= 2, };
156 if (nbd_block_status (nbd
, exportsize
, 0,
157 (nbd_extent_callback
) { .callback
= cb
, .user_data
= &data
},
159 fprintf (stderr
, "%s\n", nbd_get_error ());
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 ());
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
},
178 fprintf (stderr
, "unexpected block status success\n");
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 ());
189 if (nbd_shutdown (nbd
, 0) == -1) {
190 fprintf (stderr
, "%s\n", nbd_get_error ());