retry: Also retry flush commands
[nbdkit/ericb.git] / plugins / data / data.c
blob9004a487c2bac73a7fe545c1f0308edf4f580f2b
1 /* nbdkit
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
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;
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 /* We need %1s for obscure reasons. sscanf " <%n" can return 0
176 * if nothing is matched, not only if the '<' is matched.
178 else if (sscanf (&value[i], " <%1s%n", cc, &n) == 1) {
179 CLEANUP_FREE char *filename = NULL;
180 size_t flen;
182 i += n-1;
184 /* The filename follows next in the string. */
185 flen = strcspn (&value[i], " \t\n");
186 if (flen == 0) {
187 nbdkit_error ("data parameter <FILE not a filename");
188 return -1;
190 filename = strndup (&value[i], flen);
191 if (filename == NULL) {
192 nbdkit_error ("strndup: %m");
193 return -1;
195 i += len;
197 if (store_file (filename, &offset) == -1)
198 return -1;
200 if (data_size < offset)
201 data_size = offset;
203 else if (sscanf (&value[i], " %" SCNi64 "%n", &j, &n) == 1) {
204 if (j < 0 || j > 255) {
205 nbdkit_error ("data parameter BYTE must be in the range 0..255");
206 return -1;
208 i += n;
210 if (data_size < offset+1)
211 data_size = offset+1;
213 /* Store the byte. */
214 c = j;
215 if (sparse_array_write (sa, &c, 1, offset) == -1)
216 return -1;
217 offset++;
219 /* We have to have a rule to skip just whitespace so that
220 * whitespace is permitted at the end of the string.
222 else if (sscanf (&value[i], " %n", &n) == 0) {
223 i += n;
225 else {
226 nbdkit_error ("data parameter: parsing error at offset %zu", i);
227 return -1;
231 return 0;
234 static int
235 data_config (const char *key, const char *value)
237 int64_t r;
239 if (strcmp (key, "size") == 0) {
240 r = nbdkit_parse_size (value);
241 if (r == -1)
242 return -1;
243 size = r;
245 else if (strcmp (key, "raw") == 0 ||
246 strcmp (key, "base64") == 0 ||
247 strcmp (key, "data") == 0) {
248 if (data_seen) {
249 nbdkit_error ("raw|base64|data parameter must be specified exactly once");
250 return -1;
252 data_seen = 1;
254 if (strcmp (key, "raw") == 0) {
255 data_size = strlen (value);
256 if (sparse_array_write (sa, value, data_size, 0) == -1)
257 return -1;
259 else if (strcmp (key, "base64") == 0) {
260 if (read_base64 (value) == -1)
261 return -1;
263 else if (strcmp (key, "data") == 0) {
264 if (read_data (value) == -1)
265 return -1;
267 else
268 abort (); /* cannot happen */
270 else {
271 nbdkit_error ("unknown parameter '%s'", key);
272 return -1;
275 return 0;
278 /* Check the raw|base64|data was specified, and set the final size. */
279 static int
280 data_config_complete (void)
282 if (!data_seen) {
283 nbdkit_error ("raw|base64|data parameter was not specified");
284 return -1;
287 nbdkit_debug ("implicit data size: %" PRIi64, data_size);
289 /* If size == -1 it means the size= parameter was not given so we
290 * must use the data size.
292 if (size == -1)
293 size = data_size;
294 nbdkit_debug ("final size: %" PRIi64, size);
296 return 0;
299 #define data_config_help \
300 "data|raw|base64=... Specify disk data on the command line\n" \
301 "size=<SIZE> (required) Size of the backing disk"
303 /* Provide a way to detect if the base64 feature is supported. */
304 static void
305 data_dump_plugin (void)
307 #if defined(HAVE_GNUTLS) && defined(HAVE_GNUTLS_BASE64_DECODE2)
308 printf ("data_base64=yes\n");
309 #endif
312 #define THREAD_MODEL NBDKIT_THREAD_MODEL_PARALLEL
314 /* Create the per-connection handle. */
315 static void *
316 data_open (int readonly)
318 return NBDKIT_HANDLE_NOT_NEEDED;
321 /* Get the disk size. */
322 static int64_t
323 data_get_size (void *handle)
325 return size;
328 /* Flush is a no-op, so advertise native FUA support */
329 static int
330 data_can_fua (void *handle)
332 return NBDKIT_FUA_NATIVE;
335 /* Serves the same data over multiple connections. */
336 static int
337 data_can_multi_conn (void *handle)
339 return 1;
342 /* Cache. */
343 static int
344 data_can_cache (void *handle)
346 /* Everything is already in memory, returning this without
347 * implementing .cache lets nbdkit do the correct no-op.
349 return NBDKIT_CACHE_NATIVE;
352 /* Fast zero. */
353 static int
354 data_can_fast_zero (void *handle)
356 return 1;
359 /* Read data. */
360 static int
361 data_pread (void *handle, void *buf, uint32_t count, uint64_t offset,
362 uint32_t flags)
364 assert (!flags);
365 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
366 sparse_array_read (sa, buf, count, offset);
367 return 0;
370 /* Write data. */
371 static int
372 data_pwrite (void *handle, const void *buf, uint32_t count, uint64_t offset,
373 uint32_t flags)
375 /* Flushing, and thus FUA flag, is a no-op */
376 assert ((flags & ~NBDKIT_FLAG_FUA) == 0);
377 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
378 return sparse_array_write (sa, buf, count, offset);
381 /* Zero. */
382 static int
383 data_zero (void *handle, uint32_t count, uint64_t offset, uint32_t flags)
385 /* Flushing, and thus FUA flag, is a no-op. Assume that
386 * sparse_array_zero generally beats writes, so FAST_ZERO is a no-op. */
387 assert ((flags & ~(NBDKIT_FLAG_FUA | NBDKIT_FLAG_MAY_TRIM |
388 NBDKIT_FLAG_FAST_ZERO)) == 0);
389 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
390 sparse_array_zero (sa, count, offset);
391 return 0;
394 /* Trim (same as zero). */
395 static int
396 data_trim (void *handle, uint32_t count, uint64_t offset, uint32_t flags)
398 /* Flushing, and thus FUA flag, is a no-op */
399 assert ((flags & ~NBDKIT_FLAG_FUA) == 0);
400 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
401 sparse_array_zero (sa, count, offset);
402 return 0;
405 /* Nothing is persistent, so flush is trivially supported */
406 static int
407 data_flush (void *handle, uint32_t flags)
409 return 0;
412 /* Extents. */
413 static int
414 data_extents (void *handle, uint32_t count, uint64_t offset,
415 uint32_t flags, struct nbdkit_extents *extents)
417 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock);
418 return sparse_array_extents (sa, count, offset, extents);
421 static struct nbdkit_plugin plugin = {
422 .name = "data",
423 .version = PACKAGE_VERSION,
424 .load = data_load,
425 .unload = data_unload,
426 .config = data_config,
427 .config_complete = data_config_complete,
428 .config_help = data_config_help,
429 .dump_plugin = data_dump_plugin,
430 .open = data_open,
431 .get_size = data_get_size,
432 .can_multi_conn = data_can_multi_conn,
433 .can_fua = data_can_fua,
434 .can_cache = data_can_cache,
435 .can_fast_zero = data_can_fast_zero,
436 .pread = data_pread,
437 .pwrite = data_pwrite,
438 .zero = data_zero,
439 .trim = data_trim,
440 .flush = data_flush,
441 .extents = data_extents,
442 /* In this plugin, errno is preserved properly along error return
443 * paths from failed system calls.
445 .errno_is_preserved = 1,
448 NBDKIT_REGISTER_PLUGIN(plugin)