---
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