--- lib/Makefile.am | 27 ++ server/Makefile.am | 24 -- server/nbdkit.syms | 28 +-- server/public.c | 459 +--------------------------------- {server => lib}/extents.c | 4 +- lib/parse.c | 341 +++++++++++++++++++++++++ lib/password.c | 152 +++++++++++ lib/path.c | 97 +++++++ {server => lib}/test-public.c | 3 +- .gitignore | 2 +- lib/libnbdkit.syms | 20 ++ 11 files changed, 645 insertions(+), 512 deletions(-) diff --git a/lib/Makefile.am b/lib/Makefile.am index 4896dc95..268282af 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -40,8 +40,12 @@ EXTRA_DIST = libnbdkit.syms lib_LTLIBRARIES = libnbdkit.la libnbdkit_la_SOURCES = \ + extents.c \ init.c \ lib.h \ + parse.c \ + password.c \ + path.c \ $(NULL) libnbdkit_la_CPPFLAGS = \ @@ -69,3 +73,26 @@ if !ENABLE_LIBFUZZER libnbdkit_la_LDFLAGS += -Wl,--version-script=$(srcdir)/libnbdkit.syms endif endif + +# Unit testing + +TESTS = test-public + +check_PROGRAMS = test-public + +test_public_SOURCES = \ + test-public.c \ + extents.c \ + parse.c \ + password.c \ + $(NULL) +test_public_CPPFLAGS = \ + -I$(top_srcdir)/include \ + -I$(top_srcdir)/common/include \ + -I$(top_srcdir)/common/protocol \ + -I$(top_srcdir)/common/utils \ + $(NULL) +test_public_CFLAGS = $(WARNINGS_CFLAGS) $(VALGRIND_CFLAGS) +test_public_LDADD = \ + $(top_builddir)/common/utils/libutils.la \ + $(NULL) diff --git a/server/Makefile.am b/server/Makefile.am index ad0de9b1..8448bc10 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -43,7 +43,6 @@ nbdkit_SOURCES = \ crypto.c \ debug.c \ debug-flags.c \ - extents.c \ filters.c \ internal.h \ locks.c \ @@ -131,26 +130,3 @@ synopsis.c: $(top_srcdir)/docs/synopsis.txt pkgconfigdir = $(libdir)/pkgconfig pkgconfig_DATA = nbdkit.pc - -# Unit testing - -TESTS = test-public - -check_PROGRAMS = test-public - -test_public_SOURCES = \ - test-public.c \ - public.c \ - extents.c \ - $(NULL) -test_public_CPPFLAGS = \ - -I$(top_srcdir)/lib \ - -I$(top_srcdir)/include \ - -I$(top_srcdir)/common/include \ - -I$(top_srcdir)/common/protocol \ - -I$(top_srcdir)/common/utils \ - $(NULL) -test_public_CFLAGS = $(WARNINGS_CFLAGS) $(VALGRIND_CFLAGS) -test_public_LDADD = \ - $(top_builddir)/common/utils/libutils.la \ - $(NULL) diff --git a/server/nbdkit.syms b/server/nbdkit.syms index 111223f2..56e5008e 100644 --- a/server/nbdkit.syms +++ b/server/nbdkit.syms @@ -30,44 +30,20 @@ # SUCH DAMAGE. # This linker script controls the visibility of symbols in the final -# nbdkit binary. We want to export some symbols to plugins, but at -# the same time we don't want plugins to be able to call arbitrary -# functions from nbdkit, so this script lists only the symbols we want -# to export. +# nbdkit binary. { - # The functions we want plugins and filters to call. global: - nbdkit_absolute_path; - nbdkit_add_extent; nbdkit_debug; nbdkit_error; nbdkit_export_name; - nbdkit_extents_count; - nbdkit_extents_free; - nbdkit_extents_new; - nbdkit_get_extent; nbdkit_nanosleep; - nbdkit_parse_bool; - nbdkit_parse_int8_t; - nbdkit_parse_int16_t; - nbdkit_parse_int32_t; - nbdkit_parse_int64_t; - nbdkit_parse_int; - nbdkit_parse_size; - nbdkit_parse_uint8_t; - nbdkit_parse_uint16_t; - nbdkit_parse_uint32_t; - nbdkit_parse_uint64_t; - nbdkit_parse_unsigned; nbdkit_peer_name; - nbdkit_read_password; - nbdkit_realpath; nbdkit_set_error; nbdkit_shutdown; nbdkit_vdebug; nbdkit_verror; - + # -D server.* flags must be visible to nbdkit itself. nbdkit_debug_*; # Everything else is hidden. diff --git a/server/public.c b/server/public.c index 3fd11253..33d40688 100644 --- a/server/public.c +++ b/server/public.c @@ -36,7 +36,6 @@ #include <config.h> -#include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <stdbool.h> @@ -44,6 +43,7 @@ #include <inttypes.h> #include <string.h> #include <unistd.h> +#include <fcntl.h> #include <limits.h> #include <ctype.h> #include <termios.h> @@ -56,463 +56,6 @@ #include "internal.h" -char * -nbdkit_absolute_path (const char *path) -{ - CLEANUP_FREE char *pwd = NULL; - char *ret; - - if (path == NULL || *path == '\0') { - nbdkit_error ("cannot convert null or empty path to an absolute path"); - return NULL; - } - - if (*path == '/') { - ret = strdup (path); - if (!ret) { - nbdkit_error ("strdup: %m"); - return NULL; - } - return ret; - } - - pwd = get_current_dir_name (); - if (pwd == NULL) { - nbdkit_error ("get_current_dir_name: %m"); - return NULL; - } - - if (asprintf (&ret, "%s/%s", pwd, path) == -1) { - nbdkit_error ("asprintf: %m"); - return NULL; - } - - return ret; -} - -char * -nbdkit_realpath (const char *path) -{ - char *ret; - - if (path == NULL || *path == '\0') { - nbdkit_error ("cannot resolve a null or empty path"); - return NULL; - } - - ret = realpath (path, NULL); - if (ret == NULL) { - nbdkit_error ("realpath: %s: %m", path); - return NULL; - } - - return ret; -} - -/* Common code for parsing integers. */ -#define PARSE_COMMON_TAIL \ - if (errno != 0) { \ - nbdkit_error ("%s: could not parse number: \"%s\": %m", \ - what, str); \ - return -1; \ - } \ - if (end == str) { \ - nbdkit_error ("%s: empty string where we expected a number", \ - what); \ - return -1; \ - } \ - if (*end) { \ - nbdkit_error ("%s: could not parse number: \"%s\": trailing garbage", \ - what, str); \ - return -1; \ - } \ - \ - if (rp) \ - *rp = r; \ - return 0 - -/* Functions for parsing signed integers. */ -int -nbdkit_parse_int (const char *what, const char *str, int *rp) -{ - long r; - char *end; - - errno = 0; - r = strtol (str, &end, 0); -#if INT_MAX != LONG_MAX - if (r < INT_MIN || r > INT_MAX) - errno = ERANGE; -#endif - PARSE_COMMON_TAIL; -} - -int -nbdkit_parse_int8_t (const char *what, const char *str, int8_t *rp) -{ - long r; - char *end; - - errno = 0; - r = strtol (str, &end, 0); - if (r < INT8_MIN || r > INT8_MAX) - errno = ERANGE; - PARSE_COMMON_TAIL; -} - -int -nbdkit_parse_int16_t (const char *what, const char *str, int16_t *rp) -{ - long r; - char *end; - - errno = 0; - r = strtol (str, &end, 0); - if (r < INT16_MIN || r > INT16_MAX) - errno = ERANGE; - PARSE_COMMON_TAIL; -} - -int -nbdkit_parse_int32_t (const char *what, const char *str, int32_t *rp) -{ - long r; - char *end; - - errno = 0; - r = strtol (str, &end, 0); -#if INT32_MAX != LONG_MAX - if (r < INT32_MIN || r > INT32_MAX) - errno = ERANGE; -#endif - PARSE_COMMON_TAIL; -} - -int -nbdkit_parse_int64_t (const char *what, const char *str, int64_t *rp) -{ - long long r; - char *end; - - errno = 0; - r = strtoll (str, &end, 0); -#if INT64_MAX != LONGLONG_MAX - if (r < INT64_MIN || r > INT64_MAX) - errno = ERANGE; -#endif - PARSE_COMMON_TAIL; -} - -/* Functions for parsing unsigned integers. */ - -/* strtou* functions have surprising behaviour if the first character - * (after whitespace) is '-', so reject this early. - */ -#define PARSE_ERROR_IF_NEGATIVE \ - do { \ - while (isspace (*str)) \ - str++; \ - if (*str == '-') { \ - nbdkit_error ("%s: negative numbers are not allowed", what); \ - return -1; \ - } \ - } while (0) - -int -nbdkit_parse_unsigned (const char *what, const char *str, unsigned *rp) -{ - unsigned long r; - char *end; - - PARSE_ERROR_IF_NEGATIVE; - errno = 0; - r = strtoul (str, &end, 0); -#if UINT_MAX != ULONG_MAX - if (r > UINT_MAX) - errno = ERANGE; -#endif - PARSE_COMMON_TAIL; -} - -int -nbdkit_parse_uint8_t (const char *what, const char *str, uint8_t *rp) -{ - unsigned long r; - char *end; - - PARSE_ERROR_IF_NEGATIVE; - errno = 0; - r = strtoul (str, &end, 0); - if (r > UINT8_MAX) - errno = ERANGE; - PARSE_COMMON_TAIL; -} - -int -nbdkit_parse_uint16_t (const char *what, const char *str, uint16_t *rp) -{ - unsigned long r; - char *end; - - PARSE_ERROR_IF_NEGATIVE; - errno = 0; - r = strtoul (str, &end, 0); - if (r > UINT16_MAX) - errno = ERANGE; - PARSE_COMMON_TAIL; -} - -int -nbdkit_parse_uint32_t (const char *what, const char *str, uint32_t *rp) -{ - unsigned long r; - char *end; - - PARSE_ERROR_IF_NEGATIVE; - errno = 0; - r = strtoul (str, &end, 0); -#if UINT32_MAX != ULONG_MAX - if (r > UINT32_MAX) - errno = ERANGE; -#endif - PARSE_COMMON_TAIL; -} - -int -nbdkit_parse_uint64_t (const char *what, const char *str, uint64_t *rp) -{ - unsigned long long r; - char *end; - - PARSE_ERROR_IF_NEGATIVE; - errno = 0; - r = strtoull (str, &end, 0); -#if UINT64_MAX != ULONGLONG_MAX - if (r > UINT64_MAX) - errno = ERANGE; -#endif - PARSE_COMMON_TAIL; -} - -/* Parse a string as a size with possible scaling suffix, or return -1 - * after reporting the error. - */ -int64_t -nbdkit_parse_size (const char *str) -{ - int64_t size; - char *end; - uint64_t scale = 1; - - /* Disk sizes cannot usefully exceed off_t (which is signed) and - * cannot be negative. */ - /* XXX Should we also parse things like '1.5M'? */ - /* XXX Should we allow hex? If so, hex cannot use scaling suffixes, - * because some of them are valid hex digits */ - errno = 0; - size = strtoimax (str, &end, 10); - if (str == end) { - nbdkit_error ("could not parse size string (%s)", str); - return -1; - } - if (size < 0) { - nbdkit_error ("size cannot be negative (%s)", str); - return -1; - } - if (errno) { - nbdkit_error ("size (%s) exceeds maximum value", str); - return -1; - } - - switch (*end) { - /* No suffix */ - case '\0': - end--; /* Safe, since we already filtered out empty string */ - break; - - /* Powers of 1024 */ - case 'e': case 'E': - scale *= 1024; - /* fallthru */ - case 'p': case 'P': - scale *= 1024; - /* fallthru */ - case 't': case 'T': - scale *= 1024; - /* fallthru */ - case 'g': case 'G': - scale *= 1024; - /* fallthru */ - case 'm': case 'M': - scale *= 1024; - /* fallthru */ - case 'k': case 'K': - scale *= 1024; - /* fallthru */ - case 'b': case 'B': - break; - - /* "sectors", ie. units of 512 bytes, even if that's not the real - * sector size */ - case 's': case 'S': - scale = 512; - break; - - default: - nbdkit_error ("could not parse size: unknown suffix '%s'", end); - return -1; - } - - /* XXX Maybe we should support 'MiB' as a synonym for 'M'; and 'MB' - * for powers of 1000, for similarity to GNU tools. But for now, - * anything beyond 'M' is dropped. */ - if (end[1]) { - nbdkit_error ("could not parse size: unknown suffix '%s'", end); - return -1; - } - - if (INT64_MAX / scale < size) { - nbdkit_error ("overflow computing size (%s)", str); - return -1; - } - - return size * scale; -} - -/* Parse a string as a boolean, or return -1 after reporting the error. - */ -int -nbdkit_parse_bool (const char *str) -{ - if (!strcmp (str, "1") || - !strcasecmp (str, "true") || - !strcasecmp (str, "t") || - !strcasecmp (str, "yes") || - !strcasecmp (str, "y") || - !strcasecmp (str, "on")) - return 1; - - if (!strcmp (str, "0") || - !strcasecmp (str, "false") || - !strcasecmp (str, "f") || - !strcasecmp (str, "no") || - !strcasecmp (str, "n") || - !strcasecmp (str, "off")) - return 0; - - nbdkit_error ("could not decipher boolean (%s)", str); - return -1; -} - -/* Read a password from configuration value. */ -static int read_password_from_fd (const char *what, int fd, char **password); - -int -nbdkit_read_password (const char *value, char **password) -{ - int tty, err; - struct termios orig, temp; - ssize_t r; - size_t n; - - *password = NULL; - - /* Read from stdin. */ - if (strcmp (value, "-") == 0) { - printf ("password: "); - - /* Set no echo. */ - tty = isatty (0); - if (tty) { - tcgetattr (0, &orig); - temp = orig; - temp.c_lflag &= ~ECHO; - tcsetattr (0, TCSAFLUSH, &temp); - } - - r = getline (password, &n, stdin); - err = errno; - - /* Restore echo. */ - if (tty) - tcsetattr (0, TCSAFLUSH, &orig); - - /* Complete the printf above. */ - printf ("\n"); - - if (r == -1) { - errno = err; - nbdkit_error ("could not read password from stdin: %m"); - return -1; - } - if (*password && r > 0 && (*password)[r-1] == '\n') - (*password)[r-1] = '\0'; - } - - /* Read from numbered file descriptor. */ - else if (value[0] == '-') { - int fd; - - if (nbdkit_parse_int ("password file descriptor", &value[1], &fd) == -1) - return -1; - if (read_password_from_fd (&value[1], fd, password) == -1) - return -1; - } - - /* Read password from a file. */ - else if (value[0] == '+') { - int fd; - - fd = open (&value[1], O_CLOEXEC | O_RDONLY); - if (fd == -1) { - nbdkit_error ("open %s: %m", &value[1]); - return -1; - } - if (read_password_from_fd (&value[1], fd, password) == -1) - return -1; - } - - /* Parameter is the password. */ - else { - *password = strdup (value); - if (*password == NULL) { - nbdkit_error ("strdup: %m"); - return -1; - } - } - - return 0; -} - -static int -read_password_from_fd (const char *what, int fd, char **password) -{ - FILE *fp; - size_t n; - ssize_t r; - int err; - - fp = fdopen (fd, "r"); - if (fp == NULL) { - nbdkit_error ("fdopen %s: %m", what); - close (fd); - return -1; - } - r = getline (password, &n, fp); - err = errno; - fclose (fp); - if (r == -1) { - errno = err; - nbdkit_error ("could not read password from %s: %m", what); - return -1; - } - - if (*password && r > 0 && (*password)[r-1] == '\n') - (*password)[r-1] = '\0'; - - return 0; -} - int nbdkit_nanosleep (unsigned sec, unsigned nsec) { diff --git a/server/extents.c b/lib/extents.c similarity index 99% rename from server/extents.c rename to lib/extents.c index 2d609652..8e3c8632 100644 --- a/server/extents.c +++ b/lib/extents.c @@ -41,10 +41,10 @@ #include <errno.h> #include <assert.h> +#include "nbdkit-filter.h" + #include "minmax.h" -#include "internal.h" - /* Cap nr_extents to avoid sending over-large replies to the client, * and to avoid a plugin with frequent alternations consuming too much * memory. diff --git a/lib/parse.c b/lib/parse.c new file mode 100644 index 00000000..cd3c88ac --- /dev/null +++ b/lib/parse.c @@ -0,0 +1,341 @@ +/* nbdkit + * Copyright (C) 2013-2019 Red Hat Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Red Hat nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <stdbool.h> +#include <stdint.h> +#include <inttypes.h> +#include <limits.h> +#include <string.h> +#include <ctype.h> + +#include "nbdkit-plugin.h" + +#include "cleanup.h" + +/* Common code for parsing integers. */ +#define PARSE_COMMON_TAIL \ + if (errno != 0) { \ + nbdkit_error ("%s: could not parse number: \"%s\": %m", \ + what, str); \ + return -1; \ + } \ + if (end == str) { \ + nbdkit_error ("%s: empty string where we expected a number", \ + what); \ + return -1; \ + } \ + if (*end) { \ + nbdkit_error ("%s: could not parse number: \"%s\": trailing garbage", \ + what, str); \ + return -1; \ + } \ + \ + if (rp) \ + *rp = r; \ + return 0 + +/* Functions for parsing signed integers. */ +int +nbdkit_parse_int (const char *what, const char *str, int *rp) +{ + long r; + char *end; + + errno = 0; + r = strtol (str, &end, 0); +#if INT_MAX != LONG_MAX + if (r < INT_MIN || r > INT_MAX) + errno = ERANGE; +#endif + PARSE_COMMON_TAIL; +} + +int +nbdkit_parse_int8_t (const char *what, const char *str, int8_t *rp) +{ + long r; + char *end; + + errno = 0; + r = strtol (str, &end, 0); + if (r < INT8_MIN || r > INT8_MAX) + errno = ERANGE; + PARSE_COMMON_TAIL; +} + +int +nbdkit_parse_int16_t (const char *what, const char *str, int16_t *rp) +{ + long r; + char *end; + + errno = 0; + r = strtol (str, &end, 0); + if (r < INT16_MIN || r > INT16_MAX) + errno = ERANGE; + PARSE_COMMON_TAIL; +} + +int +nbdkit_parse_int32_t (const char *what, const char *str, int32_t *rp) +{ + long r; + char *end; + + errno = 0; + r = strtol (str, &end, 0); +#if INT32_MAX != LONG_MAX + if (r < INT32_MIN || r > INT32_MAX) + errno = ERANGE; +#endif + PARSE_COMMON_TAIL; +} + +int +nbdkit_parse_int64_t (const char *what, const char *str, int64_t *rp) +{ + long long r; + char *end; + + errno = 0; + r = strtoll (str, &end, 0); +#if INT64_MAX != LONGLONG_MAX + if (r < INT64_MIN || r > INT64_MAX) + errno = ERANGE; +#endif + PARSE_COMMON_TAIL; +} + +/* Functions for parsing unsigned integers. */ + +/* strtou* functions have surprising behaviour if the first character + * (after whitespace) is '-', so reject this early. + */ +#define PARSE_ERROR_IF_NEGATIVE \ + do { \ + while (isspace (*str)) \ + str++; \ + if (*str == '-') { \ + nbdkit_error ("%s: negative numbers are not allowed", what); \ + return -1; \ + } \ + } while (0) + +int +nbdkit_parse_unsigned (const char *what, const char *str, unsigned *rp) +{ + unsigned long r; + char *end; + + PARSE_ERROR_IF_NEGATIVE; + errno = 0; + r = strtoul (str, &end, 0); +#if UINT_MAX != ULONG_MAX + if (r > UINT_MAX) + errno = ERANGE; +#endif + PARSE_COMMON_TAIL; +} + +int +nbdkit_parse_uint8_t (const char *what, const char *str, uint8_t *rp) +{ + unsigned long r; + char *end; + + PARSE_ERROR_IF_NEGATIVE; + errno = 0; + r = strtoul (str, &end, 0); + if (r > UINT8_MAX) + errno = ERANGE; + PARSE_COMMON_TAIL; +} + +int +nbdkit_parse_uint16_t (const char *what, const char *str, uint16_t *rp) +{ + unsigned long r; + char *end; + + PARSE_ERROR_IF_NEGATIVE; + errno = 0; + r = strtoul (str, &end, 0); + if (r > UINT16_MAX) + errno = ERANGE; + PARSE_COMMON_TAIL; +} + +int +nbdkit_parse_uint32_t (const char *what, const char *str, uint32_t *rp) +{ + unsigned long r; + char *end; + + PARSE_ERROR_IF_NEGATIVE; + errno = 0; + r = strtoul (str, &end, 0); +#if UINT32_MAX != ULONG_MAX + if (r > UINT32_MAX) + errno = ERANGE; +#endif + PARSE_COMMON_TAIL; +} + +int +nbdkit_parse_uint64_t (const char *what, const char *str, uint64_t *rp) +{ + unsigned long long r; + char *end; + + PARSE_ERROR_IF_NEGATIVE; + errno = 0; + r = strtoull (str, &end, 0); +#if UINT64_MAX != ULONGLONG_MAX + if (r > UINT64_MAX) + errno = ERANGE; +#endif + PARSE_COMMON_TAIL; +} + +/* Parse a string as a size with possible scaling suffix, or return -1 + * after reporting the error. + */ +int64_t +nbdkit_parse_size (const char *str) +{ + int64_t size; + char *end; + uint64_t scale = 1; + + /* Disk sizes cannot usefully exceed off_t (which is signed) and + * cannot be negative. */ + /* XXX Should we also parse things like '1.5M'? */ + /* XXX Should we allow hex? If so, hex cannot use scaling suffixes, + * because some of them are valid hex digits */ + errno = 0; + size = strtoimax (str, &end, 10); + if (str == end) { + nbdkit_error ("could not parse size string (%s)", str); + return -1; + } + if (size < 0) { + nbdkit_error ("size cannot be negative (%s)", str); + return -1; + } + if (errno) { + nbdkit_error ("size (%s) exceeds maximum value", str); + return -1; + } + + switch (*end) { + /* No suffix */ + case '\0': + end--; /* Safe, since we already filtered out empty string */ + break; + + /* Powers of 1024 */ + case 'e': case 'E': + scale *= 1024; + /* fallthru */ + case 'p': case 'P': + scale *= 1024; + /* fallthru */ + case 't': case 'T': + scale *= 1024; + /* fallthru */ + case 'g': case 'G': + scale *= 1024; + /* fallthru */ + case 'm': case 'M': + scale *= 1024; + /* fallthru */ + case 'k': case 'K': + scale *= 1024; + /* fallthru */ + case 'b': case 'B': + break; + + /* "sectors", ie. units of 512 bytes, even if that's not the real + * sector size */ + case 's': case 'S': + scale = 512; + break; + + default: + nbdkit_error ("could not parse size: unknown suffix '%s'", end); + return -1; + } + + /* XXX Maybe we should support 'MiB' as a synonym for 'M'; and 'MB' + * for powers of 1000, for similarity to GNU tools. But for now, + * anything beyond 'M' is dropped. */ + if (end[1]) { + nbdkit_error ("could not parse size: unknown suffix '%s'", end); + return -1; + } + + if (INT64_MAX / scale < size) { + nbdkit_error ("overflow computing size (%s)", str); + return -1; + } + + return size * scale; +} + +/* Parse a string as a boolean, or return -1 after reporting the error. + */ +int +nbdkit_parse_bool (const char *str) +{ + if (!strcmp (str, "1") || + !strcasecmp (str, "true") || + !strcasecmp (str, "t") || + !strcasecmp (str, "yes") || + !strcasecmp (str, "y") || + !strcasecmp (str, "on")) + return 1; + + if (!strcmp (str, "0") || + !strcasecmp (str, "false") || + !strcasecmp (str, "f") || + !strcasecmp (str, "no") || + !strcasecmp (str, "n") || + !strcasecmp (str, "off")) + return 0; + + nbdkit_error ("could not decipher boolean (%s)", str); + return -1; +} diff --git a/lib/password.c b/lib/password.c new file mode 100644 index 00000000..8452c153 --- /dev/null +++ b/lib/password.c @@ -0,0 +1,152 @@ +/* nbdkit + * Copyright (C) 2013-2019 Red Hat Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Red Hat nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <termios.h> + +#include "nbdkit-plugin.h" + +/* Read a password from configuration value. */ + +static int read_password_from_fd (const char *what, int fd, char **password); + +int +nbdkit_read_password (const char *value, char **password) +{ + int tty, err; + struct termios orig, temp; + ssize_t r; + size_t n; + + *password = NULL; + + /* Read from stdin. */ + if (strcmp (value, "-") == 0) { + printf ("password: "); + + /* Set no echo. */ + tty = isatty (0); + if (tty) { + tcgetattr (0, &orig); + temp = orig; + temp.c_lflag &= ~ECHO; + tcsetattr (0, TCSAFLUSH, &temp); + } + + r = getline (password, &n, stdin); + err = errno; + + /* Restore echo. */ + if (tty) + tcsetattr (0, TCSAFLUSH, &orig); + + /* Complete the printf above. */ + printf ("\n"); + + if (r == -1) { + errno = err; + nbdkit_error ("could not read password from stdin: %m"); + return -1; + } + if (*password && r > 0 && (*password)[r-1] == '\n') + (*password)[r-1] = '\0'; + } + + /* Read from numbered file descriptor. */ + else if (value[0] == '-') { + int fd; + + if (nbdkit_parse_int ("password file descriptor", &value[1], &fd) == -1) + return -1; + if (read_password_from_fd (&value[1], fd, password) == -1) + return -1; + } + + /* Read password from a file. */ + else if (value[0] == '+') { + int fd; + + fd = open (&value[1], O_CLOEXEC | O_RDONLY); + if (fd == -1) { + nbdkit_error ("open %s: %m", &value[1]); + return -1; + } + if (read_password_from_fd (&value[1], fd, password) == -1) + return -1; + } + + /* Parameter is the password. */ + else { + *password = strdup (value); + if (*password == NULL) { + nbdkit_error ("strdup: %m"); + return -1; + } + } + + return 0; +} + +static int +read_password_from_fd (const char *what, int fd, char **password) +{ + FILE *fp; + size_t n; + ssize_t r; + int err; + + fp = fdopen (fd, "r"); + if (fp == NULL) { + nbdkit_error ("fdopen %s: %m", what); + close (fd); + return -1; + } + r = getline (password, &n, fp); + err = errno; + fclose (fp); + if (r == -1) { + errno = err; + nbdkit_error ("could not read password from %s: %m", what); + return -1; + } + + if (*password && r > 0 && (*password)[r-1] == '\n') + (*password)[r-1] = '\0'; + + return 0; +} diff --git a/lib/path.c b/lib/path.c new file mode 100644 index 00000000..244c2b0a --- /dev/null +++ b/lib/path.c @@ -0,0 +1,97 @@ +/* nbdkit + * Copyright (C) 2013-2019 Red Hat Inc. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * * Neither the name of Red Hat nor the names of its contributors may be + * used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, + * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT + * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <config.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <limits.h> + +#include "nbdkit-plugin.h" + +#include "cleanup.h" +#include "get-current-dir-name.h" + +char * +nbdkit_absolute_path (const char *path) +{ + CLEANUP_FREE char *pwd = NULL; + char *ret; + + if (path == NULL || *path == '\0') { + nbdkit_error ("cannot convert null or empty path to an absolute path"); + return NULL; + } + + if (*path == '/') { + ret = strdup (path); + if (!ret) { + nbdkit_error ("strdup: %m"); + return NULL; + } + return ret; + } + + pwd = get_current_dir_name (); + if (pwd == NULL) { + nbdkit_error ("get_current_dir_name: %m"); + return NULL; + } + + if (asprintf (&ret, "%s/%s", pwd, path) == -1) { + nbdkit_error ("asprintf: %m"); + return NULL; + } + + return ret; +} + +char * +nbdkit_realpath (const char *path) +{ + char *ret; + + if (path == NULL || *path == '\0') { + nbdkit_error ("cannot resolve a null or empty path"); + return NULL; + } + + ret = realpath (path, NULL); + if (ret == NULL) { + nbdkit_error ("realpath: %s: %m", path); + return NULL; + } + + return ret; +} diff --git a/server/test-public.c b/lib/test-public.c similarity index 99% rename from server/test-public.c rename to lib/test-public.c index fe347d44..d680e7ad 100644 --- a/server/test-public.c +++ b/lib/test-public.c @@ -41,7 +41,8 @@ #include <string.h> #include <unistd.h> -#include "internal.h" +#include "nbdkit-plugin.h" +#include "nbdkit-filter.h" static bool error_flagged; diff --git a/.gitignore b/.gitignore index 4bb035e1..2f9d45a9 100644 --- a/.gitignore +++ b/.gitignore @@ -64,6 +64,7 @@ plugins/*/*.3 /include/nbdkit-version.h /INSTALL /install-sh +/lib/test-public /libtool /ltmain.sh /missing @@ -80,7 +81,6 @@ plugins/*/*.3 /server/nbdkit /server/nbdkit.pc /server/synopsis.c -/server/test-public /stamp-h1 /tests/__pycache__/ /tests/disk diff --git a/lib/libnbdkit.syms b/lib/libnbdkit.syms index a70b35fc..f4e80e42 100644 --- a/lib/libnbdkit.syms +++ b/lib/libnbdkit.syms @@ -38,6 +38,26 @@ { # The functions we want plugins and filters to call. global: + nbdkit_absolute_path; + nbdkit_add_extent; + nbdkit_extents_count; + nbdkit_extents_free; + nbdkit_extents_new; + nbdkit_get_extent; + nbdkit_parse_bool; + nbdkit_parse_int16_t; + nbdkit_parse_int32_t; + nbdkit_parse_int64_t; + nbdkit_parse_int8_t; + nbdkit_parse_int; + nbdkit_parse_size; + nbdkit_parse_uint16_t; + nbdkit_parse_uint32_t; + nbdkit_parse_uint64_t; + nbdkit_parse_uint8_t; + nbdkit_parse_unsigned; + nbdkit_read_password; + nbdkit_realpath; # Private function that must only be called by the server. libnbdkit_private_init; -- 2.25.0