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
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
43 #if defined(HAVE_GNUTLS) && defined(HAVE_GNUTLS_BASE64_DECODE2)
44 #include <gnutls/gnutls.h>
47 #define NBDKIT_API_VERSION 2
49 #include <nbdkit-plugin.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). */
75 sa
= alloc_sparse_array (data_debug_dir
);
82 /* On unload, free the sparse array. */
86 free_sparse_array (sa
);
89 /* Parse the base64 parameter. */
91 read_base64 (const char *value
)
93 #if defined(HAVE_GNUTLS) && defined(HAVE_GNUTLS_BASE64_DECODE2)
94 gnutls_datum_t in
, out
;
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
));
105 if (sparse_array_write (sa
, out
.data
, out
.size
, 0) == -1)
110 nbdkit_error ("base64 is not supported in this build of the plugin");
115 /* Store file at current offset in the sparse array, updating
119 store_file (const char *filename
, int64_t *offset
)
125 fp
= fopen (filename
, "r");
127 nbdkit_error ("%s: %m", filename
);
132 n
= fread (buf
, 1, BUFSIZ
, fp
);
134 if (sparse_array_write (sa
, buf
, n
, *offset
) == -1) {
140 nbdkit_error ("fread: %s: %m", filename
);
147 if (fclose (fp
) == EOF
) {
148 nbdkit_error ("fclose: %s: %m", filename
);
155 /* Parse the data parameter. */
157 read_data (const char *value
)
160 size_t i
, len
= strlen (value
);
162 for (i
= 0; i
< len
; ++i
) {
167 if (sscanf (&value
[i
], " @%" SCNi64
"%n", &j
, &n
) == 1) {
169 nbdkit_error ("data parameter @OFFSET must not be negative");
175 else if (sscanf (&value
[i
], " %" SCNi64
"*%" SCNi64
"%n",
177 if (j
< 0 || j
> 255) {
178 nbdkit_error ("data parameter BYTE must be in the range 0..255");
182 nbdkit_error ("data parameter *N must be >= 0");
189 if (sparse_array_write (sa
, &c
, 1, offset
) == -1)
194 if (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
;
206 /* The filename follows next in the string. */
207 flen
= strcspn (&value
[i
], " \t\n");
209 nbdkit_error ("data parameter <FILE not a filename");
212 filename
= strndup (&value
[i
], flen
);
213 if (filename
== NULL
) {
214 nbdkit_error ("strndup: %m");
219 if (store_file (filename
, &offset
) == -1)
222 if (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");
232 if (data_size
< offset
+1)
233 data_size
= offset
+1;
235 /* Store the byte. */
237 if (sparse_array_write (sa
, &c
, 1, offset
) == -1)
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) {
248 nbdkit_error ("data parameter: parsing error at offset %zu", i
);
257 data_config (const char *key
, const char *value
)
261 if (strcmp (key
, "size") == 0) {
262 r
= nbdkit_parse_size (value
);
267 else if (strcmp (key
, "raw") == 0 ||
268 strcmp (key
, "base64") == 0 ||
269 strcmp (key
, "data") == 0) {
271 nbdkit_error ("raw|base64|data parameter must be specified exactly once");
276 if (strcmp (key
, "raw") == 0) {
277 data_size
= strlen (value
);
278 if (sparse_array_write (sa
, value
, data_size
, 0) == -1)
281 else if (strcmp (key
, "base64") == 0) {
282 if (read_base64 (value
) == -1)
285 else if (strcmp (key
, "data") == 0) {
286 if (read_data (value
) == -1)
290 abort (); /* cannot happen */
293 nbdkit_error ("unknown parameter '%s'", key
);
300 /* Check the raw|base64|data was specified, and set the final size. */
302 data_config_complete (void)
305 nbdkit_error ("raw|base64|data parameter was not specified");
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.
316 nbdkit_debug ("final size: %" PRIi64
, size
);
317 sparse_array_set_size (sa
, size
);
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. */
328 data_dump_plugin (void)
330 #if defined(HAVE_GNUTLS) && defined(HAVE_GNUTLS_BASE64_DECODE2)
331 printf ("data_base64=yes\n");
335 #define THREAD_MODEL NBDKIT_THREAD_MODEL_PARALLEL
337 /* Create the per-connection handle. */
339 data_open (int readonly
)
341 return NBDKIT_HANDLE_NOT_NEEDED
;
344 /* Get the disk size. */
346 data_get_size (void *handle
)
351 /* Flush is a no-op, so advertise native FUA support */
353 data_can_fua (void *handle
)
355 return NBDKIT_FUA_NATIVE
;
358 /* Serves the same data over multiple connections. */
360 data_can_multi_conn (void *handle
)
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
;
377 data_can_fast_zero (void *handle
)
382 /* Does current client start with a sparse image. */
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. */
392 data_init_zero (void *handle
)
394 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock
);
395 return sparse_array_is_zero (sa
);
400 data_pread (void *handle
, void *buf
, uint32_t count
, uint64_t offset
,
404 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock
);
405 sparse_array_read (sa
, buf
, count
, offset
);
411 data_pwrite (void *handle
, const void *buf
, uint32_t count
, uint64_t offset
,
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
);
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
);
433 /* Trim (same as zero). */
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
);
444 /* Nothing is persistent, so flush is trivially supported */
446 data_flush (void *handle
, uint32_t flags
)
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
= {
462 .version
= PACKAGE_VERSION
,
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
,
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
,
478 .pwrite
= data_pwrite
,
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
)