[Libguestfs] [PATCH EXPERIMENTAL nbdkit 2/2] Port to Windows using mingw.

Richard W.M. Jones rjones at redhat.com
Sat Aug 15 21:16:41 UTC 2020


---
 include/nbdkit-common.h       |   6 ++
 configure.ac                  |  41 +++++++--
 common/utils/Makefile.am      |   2 +
 server/internal.h             |   6 +-
 common/utils/windows-compat.h |  99 ++++++++++++++++++++
 server/background.c           |  20 +++++
 server/captive.c              |  20 ++++-
 server/connections.c          |   9 +-
 server/crypto.c               |   7 ++
 server/debug.c                |   8 ++
 server/log-stderr.c           |   4 +
 server/log-syslog.c           |  19 ++++
 server/main.c                 |  28 +++++-
 server/plugins.c              |   3 +
 server/protocol.c             |   4 +
 server/public.c               |  48 +++++++++-
 server/quit.c                 |   4 +
 server/signals.c              |   2 +
 server/socket-activation.c    |  12 +++
 server/sockets.c              |  32 ++++++-
 server/usergroup.c            |  25 +++++-
 common/utils/utils.c          |  44 ++++++++-
 common/utils/windows-compat.c | 165 ++++++++++++++++++++++++++++++++++
 plugins/cdi/cdi.c             |   7 --
 README                        |  50 +++++++++++
 25 files changed, 637 insertions(+), 28 deletions(-)

diff --git a/include/nbdkit-common.h b/include/nbdkit-common.h
index d38b37d2..c7d91512 100644
--- a/include/nbdkit-common.h
+++ b/include/nbdkit-common.h
@@ -40,7 +40,13 @@
 #include <stdarg.h>
 #include <stdint.h>
 #include <errno.h>
+
+#if !defined(_WIN32) && !defined(__MINGW32__) && \
+    !defined(__CYGWIN__) && !defined(_MSC_VER)
 #include <sys/socket.h>
+#else
+#include <ws2tcpip.h>
+#endif
 
 #include <nbdkit-version.h>
 
diff --git a/configure.ac b/configure.ac
index a4d7fa88..df42594e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -306,11 +306,22 @@ AC_CHECK_HEADERS([\
 	alloca.h \
 	byteswap.h \
 	endian.h \
+	grp.h \
+	poll.h \
+	netdb.h \
+	netinet/in.h \
+	netinet/tcp.h \
+	pwd.h \
+	termios.h \
 	stdatomic.h \
+	syslog.h \
 	sys/endian.h \
 	sys/mman.h \
 	sys/prctl.h \
-	sys/procctl.h])
+	sys/procctl.h \
+	sys/socket.h \
+	sys/un.h \
+	sys/wait.h])
 
 AC_CHECK_HEADERS([linux/vm_sockets.h], [], [], [#include <sys/socket.h>])
 
@@ -318,13 +329,21 @@ dnl Check for functions in libc, all optional.
 AC_CHECK_FUNCS([\
 	accept4 \
 	fdatasync \
+	flockfile \
+	funlockfile \
 	get_current_dir_name \
+	getpeername \
 	mkostemp \
 	mlockall \
 	openlog \
+	pipe \
 	pipe2 \
+	poll \
 	ppoll \
-	posix_fadvise])
+	posix_fadvise \
+	realpath \
+	sigaction \
+	strndup])
 
 dnl Check whether printf("%m") works
 AC_CACHE_CHECK([whether the printf family supports %m],
@@ -440,11 +459,21 @@ LIBS="$DL_LIBS $LIBS"
 AC_CHECK_FUNCS([dladdr])
 LIBS="$old_LIBS"
 
-dnl On mingw we should enable the -no-undefined flag.
+dnl Is the platform Windows?
+AC_MSG_CHECKING([if we are on the Windows platform])
+AS_CASE([$host_os],
+    [mingw*|msys*|cygwin*], [
+        is_windows=yes
+        AC_DEFINE([IS_WINDOWS],[1],[this is the Windows platform.])
+        LIBS="$LIBS -lwsock32 -lws2_32"
+    ],
+    [is_windows=no]
+)
+AC_MSG_RESULT([$is_windows])
+
 AC_MSG_CHECKING([SHARED_LDFLAGS])
-AS_CASE([$host_os],
-    [mingw*|msys*|cygwin*], [SHARED_LDFLAGS="$SHARED_LDFLAGS -no-undefined"],
-    []
+AS_IF([test "x$is_windows" = "xyes"],
+    [SHARED_LDFLAGS="$SHARED_LDFLAGS -no-undefined"]
 )
 AC_MSG_RESULT([$SHARED_LDFLAGS])
 AC_SUBST([SHARED_LDFLAGS])
diff --git a/common/utils/Makefile.am b/common/utils/Makefile.am
index a621790a..46d9de98 100644
--- a/common/utils/Makefile.am
+++ b/common/utils/Makefile.am
@@ -43,6 +43,8 @@ libutils_la_SOURCES = \
 	utils.h \
 	vector.c \
 	vector.h \
+	windows-compat.c \
+	windows-compat.h \
 	$(NULL)
 libutils_la_CPPFLAGS = \
 	-I$(top_srcdir)/include \
diff --git a/server/internal.h b/server/internal.h
index d043225a..3b52ad00 100644
--- a/server/internal.h
+++ b/server/internal.h
@@ -36,9 +36,12 @@
 #include <stdbool.h>
 #include <stddef.h>
 #include <stdarg.h>
-#include <sys/socket.h>
 #include <pthread.h>
 
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
 #define NBDKIT_API_VERSION 2
 #define NBDKIT_INTERNAL
 #include "nbdkit-plugin.h"
@@ -47,6 +50,7 @@
 #include "nbd-protocol.h"
 #include "unix-path-max.h"
 #include "vector.h"
+#include "windows-compat.h"
 
 /* Define unlikely macro, but only for GCC.  These are used to move
  * debug and error handling code out of hot paths.
diff --git a/common/utils/windows-compat.h b/common/utils/windows-compat.h
new file mode 100644
index 00000000..1f9a8d85
--- /dev/null
+++ b/common/utils/windows-compat.h
@@ -0,0 +1,99 @@
+/* nbdkit
+ * Copyright (C) 2020 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.
+ */
+
+#ifndef NBDKIT_WINDOWS_COMPAT_H
+#define NBDKIT_WINDOWS_COMPAT_H
+
+#include <config.h>
+
+#include <ws2tcpip.h>
+#include <windows.h>
+
+/* Windows doesn't have O_CLOEXEC, but it also doesn't have file
+ * descriptors that can be inherited across exec.  Similarly for
+ * O_NOCTTY.
+ */
+#ifndef O_CLOEXEC
+#define O_CLOEXEC 0
+#endif
+#ifndef O_NOCTTY
+#define O_NOCTTY 0
+#endif
+
+/* AI_ADDRCONFIG is not available on Windows.  It enables a rather
+ * obscure feature of getaddrinfo to do with IPv6.
+ */
+#ifndef AI_ADDRCONFIG
+#define AI_ADDRCONFIG 0
+#endif
+
+/* Unfortunately quite commonly used at the moment.  Make it a common
+ * macro so we can easily find places which need porting.
+ *
+ * Note: Don't use this for things which can never work on Windows
+ * (eg. Unix socket support).  Those should just give regular errors.
+ */
+#define NOT_IMPLEMENTED_ON_WINDOWS(feature)                             \
+  do {                                                                  \
+    fprintf (stderr, "nbdkit: %s is not implemented for Windows.\n", feature); \
+    fprintf (stderr, "You can help by contributing to the Windows port, see\n"); \
+    fprintf (stderr, "nbdkit README in the source for how to contribute.\n"); \
+    exit (EXIT_FAILURE);                                                \
+  } while (0)
+
+/* On Windows we can replace certain functions if they appear to be
+ * missing.  Implementations of these are present in windows-compat.c.
+ * We don't bother using the LIBOBJS mechanism.
+ */
+#ifdef IS_WINDOWS
+#ifndef HAVE_POLL
+struct pollfd {
+  int fd;
+  short events;
+  short revents;
+};
+#define POLLIN    0x0001
+#define POLLOUT   0x0002
+#define POLLERR   0x0008
+#define POLLHUP   0x0010
+#define POLLRDHUP 0x2000
+extern int poll (struct pollfd *fds, int n, int timeout);
+#endif
+#ifndef HAVE_REALPATH
+extern char *realpath (const char *path, char *resolved_path);
+#endif
+#ifndef HAVE_STRNDUP
+extern char *strndup (const char *s, size_t n);
+#endif
+#endif
+
+#endif /* NBDKIT_WINDOWS_COMPAT_H */
diff --git a/server/background.c b/server/background.c
index 72ab1ef6..371ee4ce 100644
--- a/server/background.c
+++ b/server/background.c
@@ -44,6 +44,8 @@
 /* True if we forked into the background (used to control log messages). */
 bool forked_into_background;
 
+#ifndef IS_WINDOWS
+
 /* Run as a background process.  If foreground is set (ie. -f or
  * equivalent) then this does nothing.  Otherwise it forks into the
  * background and sets forked_into_background.
@@ -79,3 +81,21 @@ fork_into_background (void)
   forked_into_background = true;
   debug ("forked into background (new pid = %d)", getpid ());
 }
+
+#else /* IS_WINDOWS */
+
+/* Note if you implement this, you must also implement (or change)
+ * server/log-syslog.c
+ */
+
+void
+fork_into_background (void)
+{
+  if (foreground)
+    return;
+
+  fprintf (stderr, "nbdkit: You must use the -f option on Windows.\n");
+  NOT_IMPLEMENTED_ON_WINDOWS ("daemonizing");
+}
+
+#endif /* IS_WINDOWS */
diff --git a/server/captive.c b/server/captive.c
index a8947d7c..06aea714 100644
--- a/server/captive.c
+++ b/server/captive.c
@@ -38,14 +38,19 @@
 #include <string.h>
 #include <unistd.h>
 #include <sys/types.h>
-#include <sys/wait.h>
 #include <signal.h>
 #include <assert.h>
 
+#ifdef HAVE_SYS_WAIT_H
+#include <sys/wait.h>
+#endif
+
 #include "utils.h"
 
 #include "internal.h"
 
+#ifndef IS_WINDOWS
+
 /* Handle the --run option.  If run is NULL, does nothing.  If run is
  * not NULL then run nbdkit as a captive subprocess of the command.
  */
@@ -208,3 +213,16 @@ run_command (void)
 
   debug ("forked into background (new pid = %d)", getpid ());
 }
+
+#else /* IS_WINDOWS */
+
+void
+run_command (void)
+{
+  if (!run)
+    return;
+
+  NOT_IMPLEMENTED_ON_WINDOWS ("--run");
+}
+
+#endif /* IS_WINDOWS */
diff --git a/server/connections.c b/server/connections.c
index a3dd4ca7..318186ef 100644
--- a/server/connections.c
+++ b/server/connections.c
@@ -38,10 +38,13 @@
 #include <inttypes.h>
 #include <string.h>
 #include <unistd.h>
-#include <sys/socket.h>
 #include <fcntl.h>
 #include <assert.h>
 
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
 #include "internal.h"
 #include "utils.h"
 
@@ -267,7 +270,7 @@ new_connection (int sockin, int sockout, int nworkers)
       perror ("pipe2");
       goto error2;
     }
-#else
+#elifdef HAVE_PIPE
     /* If we were fully parallel, then this function could be
      * accepting connections in one thread while another thread could
      * be in a plugin trying to fork.  But plugins.c forced
@@ -296,6 +299,8 @@ new_connection (int sockin, int sockout, int nworkers)
       goto error2;
     }
     unlock_request ();
+#else
+    /* Windows has neither pipe2 nor pipe. XXX */
 #endif
   }
 
diff --git a/server/crypto.c b/server/crypto.c
index d291f4e7..ba8b558d 100644
--- a/server/crypto.c
+++ b/server/crypto.c
@@ -156,6 +156,7 @@ load_certificates (const char *path)
 static int
 start_certificates (void)
 {
+#ifndef IS_WINDOWS
   /* Try to locate the certificates directory and load them. */
   if (tls_certificates_dir == NULL) {
     const char *home;
@@ -190,6 +191,12 @@ start_certificates (void)
   }
   return -1;
 
+#else /* IS_WINDOWS */
+  if (load_certificates (root_tls_certificates_dir))
+    goto found_certificates;
+  return -1;
+#endif /* IS_WINDOWS */
+
  found_certificates:
 #ifdef HAVE_GNUTLS_CERTIFICATE_SET_KNOWN_DH_PARAMS
   gnutls_certificate_set_known_dh_params (x509_creds, GNUTLS_SEC_PARAM_MEDIUM);
diff --git a/server/debug.c b/server/debug.c
index 96c59798..956e61ad 100644
--- a/server/debug.c
+++ b/server/debug.c
@@ -67,13 +67,17 @@ nbdkit_vdebug (const char *fs, va_list args)
 
   if (!verbose)
     return;
+#ifdef HAVE_FLOCKFILE
   flockfile (stderr);
+#endif
   prologue ();
 
   vfprintf (stderr, fs, args);
 
   fprintf (stderr, "\n");
+#ifdef HAVE_FUNLOCKFILE
   funlockfile (stderr);
+#endif
 
   errno = err;
 }
@@ -88,7 +92,9 @@ nbdkit_debug (const char *fs, ...)
   if (!verbose)
     return;
 
+#ifdef HAVE_FLOCKFILE
   flockfile (stderr);
+#endif
   prologue ();
 
   va_start (args, fs);
@@ -96,7 +102,9 @@ nbdkit_debug (const char *fs, ...)
   va_end (args);
 
   fprintf (stderr, "\n");
+#ifdef HAVE_FUNLOCKFILE
   funlockfile (stderr);
+#endif
 
   errno = err;
 }
diff --git a/server/log-stderr.c b/server/log-stderr.c
index 4a8cec00..7a33a043 100644
--- a/server/log-stderr.c
+++ b/server/log-stderr.c
@@ -51,7 +51,9 @@ log_stderr_verror (const char *fs, va_list args)
   size_t instance_num = threadlocal_get_instance_num ();
   int tty;
 
+#ifdef HAVE_FLOCKFILE
   flockfile (stderr);
+#endif
   tty = isatty (fileno (stderr));
   if (tty) fputs ("\033[1;31m", stderr);
 
@@ -71,7 +73,9 @@ log_stderr_verror (const char *fs, va_list args)
 
   if (tty) fputs ("\033[0m", stderr);
 
+#ifdef HAVE_FUNLOCKFILE
   funlockfile (stderr);
+#endif
 
   errno = err;                  /* must be last line of function */
 }
diff --git a/server/log-syslog.c b/server/log-syslog.c
index 7f4a6cbb..6297e377 100644
--- a/server/log-syslog.c
+++ b/server/log-syslog.c
@@ -37,10 +37,15 @@
 #include <stdarg.h>
 #include <string.h>
 #include <errno.h>
+
+#ifdef HAVE_SYSLOG_H
 #include <syslog.h>
+#endif
 
 #include "internal.h"
 
+#ifndef IS_WINDOWS
+
 /* Tempted to use LOG_FTP instead of LOG_DAEMON! */
 static const int PRIORITY = LOG_DAEMON|LOG_ERR;
 
@@ -79,3 +84,17 @@ log_syslog_verror (const char *fs, va_list args)
  out:
   errno = err;
 }
+
+#else /* IS_WINDOWS */
+
+/* Note that because daemonization is not implemented, this should
+ * never be called unless the user explicitly added --log=syslog.
+ */
+
+void
+log_syslog_verror (const char *fs, va_list args)
+{
+  NOT_IMPLEMENTED_ON_WINDOWS ("--log=syslog");
+}
+
+#endif
diff --git a/server/main.c b/server/main.c
index 1f806824..9451bc72 100644
--- a/server/main.c
+++ b/server/main.c
@@ -42,15 +42,21 @@
 #include <limits.h>
 #include <errno.h>
 #include <assert.h>
-#include <syslog.h>
 #include <sys/types.h>
 #include <sys/stat.h>
-#include <sys/socket.h>
+
+#ifdef HAVE_SYSLOG_H
+#include <syslog.h>
+#endif
 
 #ifdef HAVE_SYS_MMAN_H
 #include <sys/mman.h>
 #endif
 
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
 #ifdef HAVE_LINUX_VM_SOCKETS_H
 #include <linux/vm_sockets.h>
 #endif
@@ -729,6 +735,8 @@ nbdkit_main (int argc, char *argv[])
   return EXIT_SUCCESS;
 }
 
+#ifndef IS_WINDOWS
+
 /* Implementation of '-U -' */
 static char *
 make_random_fifo (void)
@@ -761,6 +769,16 @@ make_random_fifo (void)
   return sock;
 }
 
+#else /* IS_WINDOWS */
+
+static char *
+make_random_fifo (void)
+{
+  NOT_IMPLEMENTED_ON_WINDOWS ("-U -");
+}
+
+#endif /* IS_WINDOWS */
+
 static struct backend *
 open_plugin_so (size_t i, const char *name, int short_name)
 {
@@ -1002,6 +1020,7 @@ is_config_key (const char *key, size_t len)
 static void
 error_if_stdio_closed (void)
 {
+#ifdef F_GETFL
   if (fcntl (STDERR_FILENO, F_GETFL) == -1) {
     /* Nowhere we can report the error. Oh well. */
     exit (EXIT_FAILURE);
@@ -1011,6 +1030,7 @@ error_if_stdio_closed (void)
     perror ("expecting stdin/stdout to be opened");
     exit (EXIT_FAILURE);
   }
+#endif
 }
 
 /* Sanitize stdin/stdout to /dev/null, after saving the originals
@@ -1025,6 +1045,7 @@ error_if_stdio_closed (void)
 static void
 switch_stdio (void)
 {
+#if defined(F_DUPFD_CLOEXEC) || defined(F_DUPFD)
   fflush (stdin);
   fflush (NULL);
   if (listen_stdin || run) {
@@ -1042,6 +1063,8 @@ switch_stdio (void)
       exit (EXIT_FAILURE);
     }
   }
+#endif
+#ifndef IS_WINDOWS
   close (STDIN_FILENO);
   close (STDOUT_FILENO);
   if (open ("/dev/null", O_RDONLY) != STDIN_FILENO ||
@@ -1049,4 +1072,5 @@ switch_stdio (void)
     perror ("open");
     exit (EXIT_FAILURE);
   }
+#endif
 }
diff --git a/server/plugins.c b/server/plugins.c
index 218764da..736154b8 100644
--- a/server/plugins.c
+++ b/server/plugins.c
@@ -39,7 +39,10 @@
 #include <inttypes.h>
 #include <assert.h>
 #include <errno.h>
+
+#ifdef HAVE_SYS_SOCKET_H
 #include <sys/socket.h>
+#endif
 
 #include "internal.h"
 #include "minmax.h"
diff --git a/server/protocol.c b/server/protocol.c
index e7ba4bf1..7f995baa 100644
--- a/server/protocol.c
+++ b/server/protocol.c
@@ -706,7 +706,11 @@ protocol_recv_request_send_reply (void)
 
   /* Perform the request.  Only this part happens inside the request lock. */
   if (quit || !connection_get_status ()) {
+#ifdef ESHUTDOWN
     error = ESHUTDOWN;
+#else
+    error = EIO;
+#endif
   }
   else {
     lock_request ();
diff --git a/server/public.c b/server/public.c
index f682d732..fbc27f5b 100644
--- a/server/public.c
+++ b/server/public.c
@@ -45,11 +45,20 @@
 #include <string.h>
 #include <unistd.h>
 #include <limits.h>
-#include <termios.h>
 #include <errno.h>
-#include <poll.h>
 #include <signal.h>
+
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#endif
+
+#ifdef HAVE_TERMIOS_H
+#include <termios.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
 #include <sys/socket.h>
+#endif
 
 #include "ascii-ctype.h"
 #include "ascii-string.h"
@@ -466,6 +475,8 @@ nbdkit_read_password (const char *value, char **password)
   return 0;
 }
 
+#ifndef IS_WINDOWS
+
 static int
 read_password_interactive (char **password)
 {
@@ -528,6 +539,18 @@ read_password_interactive (char **password)
   return 0;
 }
 
+#else /* IS_WINDOWS */
+
+static int
+read_password_interactive (char **password)
+{
+  NOT_IMPLEMENTED_ON_WINDOWS ("password=-");
+}
+
+#endif
+
+#ifndef IS_WINDOWS
+
 static int
 read_password_from_fd (const char *what, int fd, char **password)
 {
@@ -575,6 +598,21 @@ read_password_from_fd (const char *what, int fd, char **password)
   return 0;
 }
 
+#else /* IS_WINDOWS */
+
+/* As far as I know this will never be possible on Windows, so it's a
+ * simple error.
+ */
+static int
+read_password_from_fd (const char *what, int fd, char **password)
+{
+  nbdkit_error ("not possible to read passwords from file descriptors "
+                "under Windows");
+  return -1;
+}
+
+#endif /* IS_WINDOWS */
+
 int
 nbdkit_nanosleep (unsigned sec, unsigned nsec)
 {
@@ -703,10 +741,14 @@ nbdkit_peer_name (struct sockaddr *addr, socklen_t *addrlen)
     return -1;
   }
 
+#ifdef HAVE_GETPEERNAME
   if (getpeername (s, addr, addrlen) == -1) {
     nbdkit_error ("peername: %m");
     return -1;
   }
-
   return 0;
+#else
+  nbdkit_error ("getpeername not available on this platform");
+  return -1;
+#endif
 }
diff --git a/server/quit.c b/server/quit.c
index 13fef437..9d189a90 100644
--- a/server/quit.c
+++ b/server/quit.c
@@ -54,6 +54,7 @@ static int write_quit_fd;
 void
 set_up_quit_pipe (void)
 {
+#ifndef IS_WINDOWS
   int fds[2];
 
 #ifdef HAVE_PIPE2
@@ -78,13 +79,16 @@ set_up_quit_pipe (void)
 #endif
   quit_fd = fds[0];
   write_quit_fd = fds[1];
+#endif
 }
 
 void
 close_quit_pipe (void)
 {
+#ifndef IS_WINDOWS
   close (quit_fd);
   close (write_quit_fd);
+#endif
 }
 
 static void
diff --git a/server/signals.c b/server/signals.c
index d7dc17d0..6354b630 100644
--- a/server/signals.c
+++ b/server/signals.c
@@ -44,6 +44,7 @@
 void
 set_up_signals (void)
 {
+#ifdef HAVE_SIGACTION
   struct sigaction sa;
 
   memset (&sa, 0, sizeof sa);
@@ -58,4 +59,5 @@ set_up_signals (void)
   sa.sa_flags = SA_RESTART;
   sa.sa_handler = SIG_IGN;
   sigaction (SIGPIPE, &sa, NULL);
+#endif /* HAVE_SIGACTION */
 }
diff --git a/server/socket-activation.c b/server/socket-activation.c
index f273f8cc..609c8efc 100644
--- a/server/socket-activation.c
+++ b/server/socket-activation.c
@@ -42,6 +42,8 @@
 
 #include "internal.h"
 
+#ifndef IS_WINDOWS
+
 /* Handle socket activation.  This is controlled through special
  * environment variables inherited by nbdkit.  Returns 0 if no socket
  * activation.  Otherwise returns the number of FDs.  See also
@@ -105,3 +107,13 @@ get_socket_activation (void)
 
   return nr_fds;
 }
+
+#else /* IS_WINDOWS */
+
+unsigned int
+get_socket_activation (void)
+{
+  return 0;
+}
+
+#endif /* IS_WINDOWS */
diff --git a/server/sockets.c b/server/sockets.c
index f6c9643a..1af88f37 100644
--- a/server/sockets.c
+++ b/server/sockets.c
@@ -38,15 +38,33 @@
 #include <inttypes.h>
 #include <string.h>
 #include <unistd.h>
-#include <poll.h>
 #include <errno.h>
 #include <assert.h>
 #include <sys/types.h>
+
+#ifdef HAVE_POLL_H
+#include <poll.h>
+#endif
+
+#ifdef HAVE_SYS_SOCKET_H
 #include <sys/socket.h>
+#endif
+
+#ifdef HAVE_SYS_UN_H
 #include <sys/un.h>
+#endif
+
+#ifdef HAVE_NETINET_IN_H
 #include <netinet/in.h>
+#endif
+
+#ifdef HAVE_NETINET_TCP_H
 #include <netinet/tcp.h>
+#endif
+
+#ifdef HAVE_NETDB_H
 #include <netdb.h>
+#endif
 
 #ifdef HAVE_LINUX_VM_SOCKETS_H
 #include <linux/vm_sockets.h>
@@ -94,6 +112,8 @@ clear_selinux_label (void)
 #endif
 }
 
+#ifndef IS_WINDOWS
+
 void
 bind_unix_socket (sockets *socks)
 {
@@ -149,6 +169,16 @@ bind_unix_socket (sockets *socks)
   debug ("bound to unix socket %s", unixsocket);
 }
 
+#else /* IS_WINDOWS */
+
+void
+bind_unix_socket (sockets *socks)
+{
+  NOT_IMPLEMENTED_ON_WINDOWS ("-U");
+}
+
+#endif /* IS_WINDOWS */
+
 void
 bind_tcpip_socket (sockets *socks)
 {
diff --git a/server/usergroup.c b/server/usergroup.c
index 11bafceb..0875e660 100644
--- a/server/usergroup.c
+++ b/server/usergroup.c
@@ -37,13 +37,21 @@
 #include <stdarg.h>
 #include <string.h>
 #include <unistd.h>
-#include <pwd.h>
-#include <grp.h>
 #include <errno.h>
 #include <sys/types.h>
 
+#ifdef HAVE_PWD_H
+#include <pwd.h>
+#endif
+
+#ifdef HAVE_GRP_H
+#include <grp.h>
+#endif
+
 #include "internal.h"
 
+#ifndef IS_WINDOWS
+
 static uid_t parseuser (const char *);
 static gid_t parsegroup (const char *);
 
@@ -138,3 +146,16 @@ parsegroup (const char *id)
 
   return grp->gr_gid;
 }
+
+#else /* IS_WINDOWS */
+
+void
+change_user (void)
+{
+  if (!user && !group)
+    return;
+
+  NOT_IMPLEMENTED_ON_WINDOWS ("--user/--group");
+}
+
+#endif /* IS_WINDOWS */
diff --git a/common/utils/utils.c b/common/utils/utils.c
index 275835e0..4c0a0fc3 100644
--- a/common/utils/utils.c
+++ b/common/utils/utils.c
@@ -36,12 +36,22 @@
 #include <stdlib.h>
 #include <fcntl.h>
 #include <unistd.h>
-#include <sys/socket.h>
 #include <sys/types.h>
+
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+
+#ifdef HAVE_SYS_WAIT_H
 #include <sys/wait.h>
+#endif
 
 #include <nbdkit-plugin.h>
 
+#include "windows-compat.h"
+
+#ifndef IS_WINDOWS
+
 /* Convert exit status to nbd_error.  If the exit status was nonzero
  * or another failure then -1 is returned.
  */
@@ -67,6 +77,10 @@ exit_status_to_nbd_error (int status, const char *cmd)
   return 0;
 }
 
+#endif /* !IS_WINDOWS */
+
+#ifndef IS_WINDOWS
+
 /* Set the FD_CLOEXEC flag on the given fd, if it is non-negative.
  * On failure, close fd and return -1; on success, return fd.
  *
@@ -76,7 +90,8 @@ exit_status_to_nbd_error (int status, const char *cmd)
  * prevent fd leaks to plugins that want to fork().
  */
 int
-set_cloexec (int fd) {
+set_cloexec (int fd)
+{
 #if (defined SOCK_CLOEXEC && defined HAVE_MKOSTEMP && defined HAVE_PIPE2 && \
      defined HAVE_ACCEPT4)
   nbdkit_error ("prefer creating fds with CLOEXEC atomically set");
@@ -106,11 +121,24 @@ set_cloexec (int fd) {
 #endif
 }
 
+#else /* IS_WINDOWS */
+
+int
+set_cloexec (int fd)
+{
+  return fd;
+}
+
+#endif /* IS_WINDOWS */
+
+#ifndef IS_WINDOWS
+
 /* Set the O_NONBLOCK flag on the given fd, if it is non-negative.
  * On failure, close fd and return -1; on success, return fd.
  */
 int
-set_nonblock (int fd) {
+set_nonblock (int fd)
+{
   int f;
   int err;
 
@@ -127,3 +155,13 @@ set_nonblock (int fd) {
   }
   return fd;
 }
+
+#else /* IS_WINDOWS */
+
+int
+set_nonblock (int fd)
+{
+  return fd;
+}
+
+#endif /* IS_WINDOWS */
diff --git a/common/utils/windows-compat.c b/common/utils/windows-compat.c
new file mode 100644
index 00000000..6722c110
--- /dev/null
+++ b/common/utils/windows-compat.c
@@ -0,0 +1,165 @@
+/* nbdkit
+ * Copyright (C) 2020 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 <nbdkit-plugin.h>
+
+#include "windows-compat.h"
+
+#ifdef IS_WINDOWS
+
+/* Replacement functions. */
+
+#ifndef HAVE_POLL
+/* Windows doesn't have poll.  It has something called WSAPoll in
+ * Winsock, but even MSFT admit it is broken.  Gnulib contains an
+ * elaborate emulation of poll written by Paolo, but it's distributed
+ * under an incompatible license.  However Winsock has select so we
+ * can write a simple (but slow) emulation of poll using select.
+ */
+int
+poll (struct pollfd *fds, int n, int timeout)
+{
+  int i, nfds = 0, r;
+  fd_set readfds, writefds;
+  struct timeval tv;
+
+  FD_ZERO (&readfds);
+  FD_ZERO (&writefds);
+
+  for (i = 0; i < n; ++i) {
+    if (fds[i].events & POLLIN)
+      FD_SET (fds[i].fd, &readfds);
+    if (fds[i].events & POLLOUT)
+      FD_SET (fds[i].fd, &writefds);
+    if (fds[i].fd > nfds)
+      nfds = fds[i].fd;
+    fds[i].revents = 0;
+  }
+  nfds++;
+
+  tv.tv_sec = timeout / 1000;
+  tv.tv_usec = timeout % 1000;
+
+  r = select (nfds, &readfds, &writefds, NULL, &tv);
+  if (r == -1)
+    return -1;
+
+  r = 0;
+  for (i = 0; i < n; ++i) {
+    if (FD_ISSET (fds[i].fd, &readfds))
+      fds[i].revents |= POLLIN;
+    if (FD_ISSET (fds[i].fd, &writefds))
+      fds[i].revents |= POLLOUT;
+    if (fds[i].revents != 0)
+      r++;
+  }
+
+  return r;
+}
+#endif /* HAVE_POLL */
+
+#ifndef HAVE_REALPATH
+char *
+realpath (const char *path, char *resolved_path)
+{
+  DWORD retval = 0;
+  char c, *r;
+
+  /* Note that GetFullPathNameA is not thread-safe on Windows(!)  so
+   * this will probably fail if called from a multi-threaded part of
+   * nbdkit.  Also this function doesn't check that the file exists,
+   * so it's more like nbdkit_absolute_path rather than
+   * nbdkit_realpath.  To see how we might do this properly (it's
+   * complicated) see https://github.com/JuliaLang/julia/issues/30588
+   * XXX
+   */
+
+  /* Discover the length of the path. */
+  retval = GetFullPathNameA (path, sizeof c, &c, NULL);
+  if (retval == 0) {
+  error:
+    nbdkit_error ("GetFullPathNameA: %s: %d", path, (int) GetLastError ());
+    return NULL;
+  }
+  r = malloc (retval);
+  if (r == NULL) {
+    nbdkit_error ("malloc: %d", (int) GetLastError ());
+    return NULL;
+  }
+
+  /* Return the path. */
+  retval = GetFullPathNameA (path, retval, r, NULL);
+  if (retval == 0) goto error;
+  return r;                     /* caller frees */
+}
+#endif /* HAVE_REALPATH */
+
+#ifndef HAVE_STRNDUP
+/* From
+ * https://github.com/freebsd/freebsd/blob/master/lib/libc/string/strnlen.c
+ */
+size_t
+strnlen (const char *s, size_t maxlen)
+{
+  size_t len;
+
+  for (len = 0; len < maxlen; len++, s++) {
+    if (!*s)
+      break;
+  }
+  return len;
+}
+
+/* From
+ * https://github.com/freebsd/freebsd/blob/master/lib/libc/string/strndup.c
+ */
+char *
+strndup (const char *str, size_t maxlen)
+{
+  char *copy;
+  size_t len = strnlen (str, maxlen);
+
+  copy = malloc (len+1);
+  if (!copy)
+    return NULL;
+  memcpy (copy, str, len);
+  copy[len] = '\0';
+  return copy;
+}
+#endif /* HAVE_STRNDUP */
+
+#endif /* IS_WINDOWS */
diff --git a/plugins/cdi/cdi.c b/plugins/cdi/cdi.c
index 70738da6..371e7f9e 100644
--- a/plugins/cdi/cdi.c
+++ b/plugins/cdi/cdi.c
@@ -45,13 +45,6 @@
 #include "cleanup.h"
 #include "utils.h"
 
-/* This plugin doesn't do anything tricky with threads so we don't
- * need O_CLOEXEC on platforms where it is not defined.
- */
-#ifndef O_CLOEXEC
-#define O_CLOEXEC 0
-#endif
-
 /* Parameters. */
 static const char *name;    /* Name or URI of container image. */
 static int layer = 0;       /* Layer (may be negative to count from end). */
diff --git a/README b/README
index 9054d12f..03b735bf 100644
--- a/README
+++ b/README
@@ -300,3 +300,53 @@ Test coverage
 Open your browser and examine the coverage/ directory.  At the time of
 writing (2020-04) test coverage of the server is reasonable, but
 things are much worse for certain plugins and filters.
+
+WINDOWS
+=======
+
+Experimentally, the server can be compiled on Windows or
+cross-compiled from Linux using mingw-w64.  Currently many features
+are DISABLED, including:
+
+* Daemonization.  This is not really applicable for Windows where you
+  would instead want to run nbdkit as a service using something like
+  SRVANY.  Anyway at the moment you must use the -f option or one of
+  the other options that implies -f.
+
+* These options are all unimplemented:
+  --group, --log=syslog, --pidfile, --run, --selinux-label, --single
+  --swap, --unix, --user, --vsock
+
+* Plugins cannot read passwords interactively.
+
+* The file plugin.  The current file plugin is essentially POSIX-only.
+  We would like to eventually write an alternate file plugin which
+  uses Windows APIs.
+
+* Many other plugins and filters.
+
+* Most tests will fail because of the missing features above.
+
+For the rest of this section we talk about cross-compiling for Windows
+using Linux and mingw-w64.  At a minimum you will need:
+
+    mingw-w64 GCC
+    mingw-w64 dlfcn
+    mingw-w64 winpthreads
+    mingw-w64 gnutls  (optional, but highly recommended)
+    mingw-w64 libxml2 (optional, but highly recommended)
+
+Other mingw-w64 libraries may be installed which will add
+functionality (see full list of requirements above), but you may end
+up hitting areas we have not compiled or tested before.
+
+To cross compile do:
+
+    mingw64-configure
+    make
+
+The server will be compiled as server/nbdkit.exe.  The plugins which
+are available will be compiled to .dll files.  You can run them
+without installing using for example:
+
+    server/nbdkit.exe plugins/memory/.libs/nbdkit-memory-plugin.dll 1G
-- 
2.27.0




More information about the Libguestfs mailing list