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
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 /* 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
;
184 /* The filename follows next in the string. */
185 flen
= strcspn (&value
[i
], " \t\n");
187 nbdkit_error ("data parameter <FILE not a filename");
190 filename
= strndup (&value
[i
], flen
);
191 if (filename
== NULL
) {
192 nbdkit_error ("strndup: %m");
197 if (store_file (filename
, &offset
) == -1)
200 if (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");
210 if (data_size
< offset
+1)
211 data_size
= offset
+1;
213 /* Store the byte. */
215 if (sparse_array_write (sa
, &c
, 1, offset
) == -1)
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) {
226 nbdkit_error ("data parameter: parsing error at offset %zu", i
);
235 data_config (const char *key
, const char *value
)
239 if (strcmp (key
, "size") == 0) {
240 r
= nbdkit_parse_size (value
);
245 else if (strcmp (key
, "raw") == 0 ||
246 strcmp (key
, "base64") == 0 ||
247 strcmp (key
, "data") == 0) {
249 nbdkit_error ("raw|base64|data parameter must be specified exactly once");
254 if (strcmp (key
, "raw") == 0) {
255 data_size
= strlen (value
);
256 if (sparse_array_write (sa
, value
, data_size
, 0) == -1)
259 else if (strcmp (key
, "base64") == 0) {
260 if (read_base64 (value
) == -1)
263 else if (strcmp (key
, "data") == 0) {
264 if (read_data (value
) == -1)
268 abort (); /* cannot happen */
271 nbdkit_error ("unknown parameter '%s'", key
);
278 /* Check the raw|base64|data was specified, and set the final size. */
280 data_config_complete (void)
283 nbdkit_error ("raw|base64|data parameter was not specified");
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.
294 nbdkit_debug ("final size: %" PRIi64
, size
);
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. */
305 data_dump_plugin (void)
307 #if defined(HAVE_GNUTLS) && defined(HAVE_GNUTLS_BASE64_DECODE2)
308 printf ("data_base64=yes\n");
312 #define THREAD_MODEL NBDKIT_THREAD_MODEL_PARALLEL
314 /* Create the per-connection handle. */
316 data_open (int readonly
)
318 return NBDKIT_HANDLE_NOT_NEEDED
;
321 /* Get the disk size. */
323 data_get_size (void *handle
)
328 /* Flush is a no-op, so advertise native FUA support */
330 data_can_fua (void *handle
)
332 return NBDKIT_FUA_NATIVE
;
335 /* Serves the same data over multiple connections. */
337 data_can_multi_conn (void *handle
)
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
;
354 data_can_fast_zero (void *handle
)
361 data_pread (void *handle
, void *buf
, uint32_t count
, uint64_t offset
,
365 ACQUIRE_LOCK_FOR_CURRENT_SCOPE (&lock
);
366 sparse_array_read (sa
, buf
, count
, offset
);
372 data_pwrite (void *handle
, const void *buf
, uint32_t count
, uint64_t offset
,
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
);
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
);
394 /* Trim (same as zero). */
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
);
405 /* Nothing is persistent, so flush is trivially supported */
407 data_flush (void *handle
, uint32_t flags
)
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
= {
423 .version
= PACKAGE_VERSION
,
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
,
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
,
437 .pwrite
= data_pwrite
,
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
)