plugins: Wire up nbd plugin support for NBD_INFO_INIT_STATE
[nbdkit/ericb.git] / plugins / data / data.c
blobff8b272489d3cc96a95cb99c2453646d082b7149
1 /* nbdkit
2 * Copyright (C) 2018-2020 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 <stdint.h>
38 #include <inttypes.h>
39 #include <string.h>
41 #include <pthread.h>
43 #if defined(HAVE_GNUTLS) && defined(HAVE_GNUTLS_BASE64_DECODE2)
44 #include <gnutls/gnutls.h>
45 #endif
47 #define NBDKIT_API_VERSION 2
49 #include <nbdkit-plugin.h>
51 #include "cleanup.h"
52 #include "sparse.h"
54 /* If raw|base64|data parameter seen. */
55 static int data_seen = 0;
57 /* size= parameter on the command line, -1 if not set. */
58 static int64_t size = -1;
60 /* Size of data specified on the command line. */
61 static int64_t data_size = -1;
63 /* Sparse array - the lock must be held when accessing this from
64 * connected callbacks.
66 static struct sparse_array *sa;
67 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
69 /* Debug directory operations (-D data.dir=1). */
70 int data_debug_dir;
72 static void
73 data_load (void)
75 sa = alloc_sparse_array (data_debug_dir);
76 if (sa == NULL) {
77 perror ("malloc");
78 exit (EXIT_FAILURE);
82 /* On unload, free the sparse array. */
83 static void
84 data_unload (void)
86 free_sparse_array (sa);
89 /* Parse the base64 parameter. */
90 static int
91 read_base64 (const char *value)
93 #if defined(HAVE_GNUTLS) && defined(HAVE_GNUTLS_BASE64_DECODE2)
94 gnutls_datum_t in, out;
95 int err;
97 in.data = (unsigned char *) value;
98 in.size = strlen (value);
99 err = gnutls_base64_decode2 (&in, &out);
100 if (err != GNUTLS_E_SUCCESS) {
101 nbdkit_error ("base64: %s", gnutls_strerror (err));
102 return -1;
105 if (sparse_array_write (sa, out.data, out.size, 0) == -1)
106 return -1;
107 free (out.data);
108 return 0;
109 #else
110 nbdkit_error ("base64 is not supported in this build of the plugin");
111 return -1;
112 #endif
115 /* Store file at current offset in the sparse array, updating
116 * the offset.
118 static int
119 store_file (const char *filename, int64_t *offset)
121 FILE *fp;
122 char buf[BUFSIZ];
123 size_t n;
125 fp = fopen (filename, "r");
126 if (fp == NULL) {
127 nbdkit_error ("%s: %m", filename);
128 return -1;
131 while (!feof (fp)) {
132 n = fread (buf, 1, BUFSIZ, fp);
133 if (n > 0) {
134 if (sparse_array_write (sa, buf, n, *offset) == -1) {
135 fclose (fp);
136 return -1;
139 if (ferror (fp)) {
140 nbdkit_error ("fread: %s: %m", filename);
141 fclose (fp);
142 return -1;
144 (*offset) += n;
147 if (fclose (fp) == EOF) {
148 nbdkit_error ("fclose: %s: %m", filename);
149 return -1;
152 return 0;
155 /* Parse the data parameter. */
156 static int
157 read_data (const char *value)
159 int64_t offset = 0;
160 size_t i, len = strlen (value);
162 for (i = 0; i < len; ++i) {
163 int64_t j, k;
164 int n;
165 char c, cc[2];
167 if (sscanf (&value[i], " @%" SCNi64 "%n", &j, &n) == 1) {
168 if (j < 0) {
169 nbdkit_error ("data parameter @OFFSET must not be negative");
170 return -1;
172 i += n;
173 offset = j;
175 else if (sscanf (&value[i], " %" SCNi64 "*%" SCNi64 "%n",
176 &j, &k, &n) == 2) {
177 if (j < 0 || j > 255) {
178 nbdkit_error ("data parameter BYTE must be in the range 0..255");
179 return -1;
181 if (k < 0) {
182 nbdkit_error ("data parameter *N must be >= 0");
183 return -1;
185 i += n;
187 c = j;
188 while (k > 0) {
189 if (sparse_array_write (sa, &c, 1, offset) == -1)
190 return -1;
191 offset++;
192 k--;
194 if (data_size < offset)
195 data_size = offset;
197 /* We need %1s for obscure reasons. sscanf " <%n" can return 0
198 * if nothing is matched, not only if the '<' is matched.
200 else if (sscanf (&value[i], " <%1s%n", cc, &n) == 1) {
201 CLEANUP_FREE char *filename = NULL;
202 size_t flen;
204 i += n-1;
206 /* The filename follows next in the string. */
207 flen = strcspn (&value[i], " \t\n");
208 if (flen == 0) {
209 nbdkit_error ("data parameter <FILE not a filename");
210 return -1;
212 filename = strndup (&value[i], flen);
213 if (filename == NULL) {
214 nbdkit_error ("strndup: %m");
215 return -1;
217 i += len;
219 if (store_file (filename, &offset) == -1)
220 return -1;
222 if (data_size < offset)
223 data_size = offset;
225 else if (sscanf (&value[i], " %" SCNi64 "%n", &j, &n) == 1) {
226 if (j < 0 || j > 255) {
227 nbdkit_error ("data parameter BYTE must be in the range 0..255");
228 return -1;
230 i += n;
232 if (data_size < offset+1)
233 data_size = offset+1;
235 /* Store the byte. */
236 c = j;
237 if (sparse_array_write (sa, &c, 1, offset) == -1)
238 return -1;
239 offset++;
241 /* We have to have a rule to skip just whitespace so that
242 * whitespace is permitted at the end of the string.
244 else if (sscanf (&value[i], " %n", &n) == 0) {
245 i += n;
247 else {
248 nbdkit_error ("data parameter: parsing error at offset %zu", i);
249 return -1;
253 return 0;
256 static int
257 data_config (const char *key, const char *value)
259 int64_t r;
261 if (strcmp (key, "size") == 0) {
262 r = nbdkit_parse_size (value);
263 if (r == -1)
264 return -1;
265 size = r;
267 else if (strcmp (key, "raw") == 0 ||
268 strcmp (key, "base64") == 0 ||
269 strcmp (key, "data") == 0) {
270 if (data_seen) {
271 nbdkit_error ("raw|base64|data parameter must be specified exactly once");
272 return -1;
274 data_seen = 1;
276 if (strcmp (key, "raw") == 0) {
277 data_size = strlen (value);
278 if (sparse_array_write (sa, value, data_size, 0) == -1)
279 return -1;
281 else if (strcmp (key, "base64") == 0) {
282 if (read_base64 (value) == -1)
283 return -1;
285 else if (strcmp (key, "data") == 0) {
286 if (read_data (value) == -1)
287 return -1;
289 else
290 abort (); /* cannot happen */
292 else {
293 nbdkit_error ("unknown parameter '%s'", key);
294 return -1;
297 return 0;
300 /* Check the raw|base64|data was specified, and set the final size. */
301 static int
302 data_config_complete (void)
304 if (!data_seen) {
305 nbdkit_error ("raw|base64|data parameter was not specified");
306 return -1;
309 nbdkit_debug ("implicit data size: %" PRIi64, data_size);
311 /* If size == -1 it means the size= parameter was not given so we
312 * must use the data size.
314 if (size == -1)
315 size = data_size;
316 nbdkit_debug ("final size: %" PRIi64, size);
317 sparse_array_set_size (sa, size);
319 return 0;
322 #define data_config_help \
323 "data|raw|base64=... Specify disk data on the command line\n" \
324 "size=<SIZE> (required) Size of the backing disk"
326 /* Provide a way to detect if the base64 feature is supported. */
327 static void
328 data_dump_plugin (void)
330 #if defined(HAVE_GNUTLS) && defined(HAVE_GNUTLS_BASE64_DECODE2)
331 printf ("data_base64=yes\n");
332 #endif
335 #define THREAD_MODEL NBDKIT_THREAD_MODEL_PARALLEL
337 /* Create the per-connection handle. */
338 static void *
339 data_open (int readonly)
341 return NBDKIT_HANDLE_NOT_NEEDED;
344 /* Get the disk size. */
345 static int64_t
346 data_get_size (void *handle)
348 return size;
351 /* Flush is a no-op, so advertise native FUA support */
352 static int
353 data_can_fua (void *handle)
355 return NBDKIT_FUA_NATIVE;
358 /* Serves the same data over multiple connections. */
359 static int
360 data_can_multi_conn (void *handle)
362 return 1;
365 /* Cache. */
366 static int
367 data_can_cache (void *handle)
369 /* Everything is already in memory, returning this without
370 * implementing .cache lets nbdkit do the correct no-op.
372 return NBDKIT_CACHE_NATIVE;
375 /* Fast zero. */
376 static int
377 data_can_fast_zero (void *handle)
379 return 1;
382 /* Does current client start with a sparse image. */
383 static int
384 data_init_sparse (void *handle)
386 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
387 return sparse_array_is_sparse (sa);
390 /* Does current client start with all zeroes. */
391 static int
392 data_init_zero (void *handle)
394 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
395 return sparse_array_is_zero (sa);
398 /* Read data. */
399 static int
400 data_pread (void *handle, void *buf, uint32_t count, uint64_t offset,
401 uint32_t flags)
403 assert (!flags);
404 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
405 sparse_array_read (sa, buf, count, offset);
406 return 0;
409 /* Write data. */
410 static int
411 data_pwrite (void *handle, const void *buf, uint32_t count, uint64_t offset,
412 uint32_t flags)
414 /* Flushing, and thus FUA flag, is a no-op */
415 assert ((flags & ~NBDKIT_FLAG_FUA) == 0);
416 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
417 return sparse_array_write (sa, buf, count, offset);
420 /* Zero. */
421 static int
422 data_zero (void *handle, uint32_t count, uint64_t offset, uint32_t flags)
424 /* Flushing, and thus FUA flag, is a no-op. Assume that
425 * sparse_array_zero generally beats writes, so FAST_ZERO is a no-op. */
426 assert ((flags & ~(NBDKIT_FLAG_FUA | NBDKIT_FLAG_MAY_TRIM |
427 NBDKIT_FLAG_FAST_ZERO)) == 0);
428 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
429 sparse_array_zero (sa, count, offset);
430 return 0;
433 /* Trim (same as zero). */
434 static int
435 data_trim (void *handle, uint32_t count, uint64_t offset, uint32_t flags)
437 /* Flushing, and thus FUA flag, is a no-op */
438 assert ((flags & ~NBDKIT_FLAG_FUA) == 0);
439 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
440 sparse_array_zero (sa, count, offset);
441 return 0;
444 /* Nothing is persistent, so flush is trivially supported */
445 static int
446 data_flush (void *handle, uint32_t flags)
448 return 0;
451 /* Extents. */
452 static int
453 data_extents (void *handle, uint32_t count, uint64_t offset,
454 uint32_t flags, struct nbdkit_extents *extents)
456 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
457 return sparse_array_extents (sa, count, offset, extents);
460 static struct nbdkit_plugin plugin = {
461 .name = "data",
462 .version = PACKAGE_VERSION,
463 .load = data_load,
464 .unload = data_unload,
465 .config = data_config,
466 .config_complete = data_config_complete,
467 .config_help = data_config_help,
468 .dump_plugin = data_dump_plugin,
469 .open = data_open,
470 .get_size = data_get_size,
471 .can_multi_conn = data_can_multi_conn,
472 .can_fua = data_can_fua,
473 .can_cache = data_can_cache,
474 .can_fast_zero = data_can_fast_zero,
475 .init_sparse = data_init_sparse,
476 .init_zero = data_init_zero,
477 .pread = data_pread,
478 .pwrite = data_pwrite,
479 .zero = data_zero,
480 .trim = data_trim,
481 .flush = data_flush,
482 .extents = data_extents,
483 /* In this plugin, errno is preserved properly along error return
484 * paths from failed system calls.
486 .errno_is_preserved = 1,
489 NBDKIT_REGISTER_PLUGIN(plugin)