From 1887d0dee0031df0de117b3a6339495504b4b489 Mon Sep 17 00:00:00 2001
* nix/libutil/util.cc (firmlink): New functions.
(_deletePath) [__GNU__]: Check whether a translator is set on PATH.
Call 'fsys_goaway' if this is the case.
* nix/libutil/util.hh (firmlink): New declaration.
* nix/libstore/build.cc (CHROOT_ENABLED): Define to 1. Error out when
both __GNU__ and __linux__ are undefined.
(DerivationGoal::runChild): Remove special treatment of /proc. Use
'firmlink' instead of 'mount' with MS_BIND.
Wrap /proc, /dev/shm, /dev/pts, and /proc/self handling in #ifdef
__linux__. Same for 'pivot_root' call.
* config-daemon.ac: Set and substitute 'HURD_LIBS'.
* nix/local.mk (guix_daemon_LDADD): Add $(HURD_LIBS).
config-daemon.ac | 13 ++++
nix/libstore/build.cc | 43 +++++++++----
nix/libutil/util.cc | 139 ++++++++++++++++++++++++++++++++++++++++++
nix/libutil/util.hh | 3 +
5 files changed, 189 insertions(+), 12 deletions(-)
Toggle diff (337 lines)
diff --git a/config-daemon.ac b/config-daemon.ac
index 50ead355a8..bdaee82fb8 100644
@@ -38,6 +38,19 @@ if test "x$guix_build_daemon" = "xyes"; then
AC_DEFINE_UNQUOTED([SYSTEM], ["$guix_system"],
[Guix host system type--i.e., platform and OS kernel tuple.])
+ dnl On GNU/Hurd guix-daemon depends on libfshelp.
+ AC_CHECK_LIB([fshelp], [fshelp_start_translator])
+ if test "x$ac_cv_lib_fshelp_fshelp_start_translator" != "xyes"; then
+ AC_MSG_ERROR([libfshelp (GNU Hurd) could not be found])
case "$LIBGCRYPT_PREFIX" in
diff --git a/nix/libstore/build.cc b/nix/libstore/build.cc
index ccec513d8d..7151932403 100644
--- a/nix/libstore/build.cc
+++ b/nix/libstore/build.cc
-#define CHROOT_ENABLED HAVE_CHROOT && HAVE_SYS_MOUNT_H && defined(MS_BIND) && defined(MS_PRIVATE)
+/* Chroot builds are supported both on GNU/Linux and on GNU/Hurd. */
+#if defined __linux__ || defined __GNU__
+# define CHROOT_ENABLED 1
+# error unsupported operating system
#define CLONE_ENABLED defined(CLONE_NEWNS)
#if defined(SYS_pivot_root)
@@ -1991,6 +1997,7 @@ void DerivationGoal::runChild()
if (setdomainname(domainname, sizeof(domainname)) == -1)
throw SysError("cannot set domain name");
/* Make all filesystems private. This is necessary
because subtrees may have been mounted as "shared"
(MS_SHARED). (Systemd does this, for instance.) Even
@@ -2007,27 +2014,30 @@ void DerivationGoal::runChild()
different filesystem from /, as needed for pivot_root. */
if (mount(chrootRootDir.c_str(), chrootRootDir.c_str(), 0, MS_BIND, 0) == -1)
throw SysError(format("unable to bind mount ‘%1%’") % chrootRootDir);
/* Set up a nearly empty /dev, unless the user asked to
bind-mount the host /dev. */
if (dirsInChroot.find("/dev") == dirsInChroot.end()) {
createDirs(chrootRootDir + "/dev/shm");
createDirs(chrootRootDir + "/dev/pts");
- ss.push_back("/dev/full");
+ createSymlink("/proc/self/fd", chrootRootDir + "/dev/fd");
+ createSymlink("/proc/self/fd/0", chrootRootDir + "/dev/stdin");
+ createSymlink("/proc/self/fd/1", chrootRootDir + "/dev/stdout");
+ createSymlink("/proc/self/fd/2", chrootRootDir + "/dev/stderr");
+ ss.push_back("/dev/tty");
if (pathExists("/dev/kvm"))
ss.push_back("/dev/kvm");
+ ss.push_back("/servers");
+ ss.push_back("/dev/full");
ss.push_back("/dev/null");
ss.push_back("/dev/random");
- ss.push_back("/dev/tty");
ss.push_back("/dev/urandom");
ss.push_back("/dev/zero");
- createSymlink("/proc/self/fd", chrootRootDir + "/dev/fd");
- createSymlink("/proc/self/fd/0", chrootRootDir + "/dev/stdin");
- createSymlink("/proc/self/fd/1", chrootRootDir + "/dev/stdout");
- createSymlink("/proc/self/fd/2", chrootRootDir + "/dev/stderr");
/* Fixed-output derivations typically need to access the
@@ -2049,7 +2059,7 @@ void DerivationGoal::runChild()
Path target = chrootRootDir + i->first;
- if (source == "/proc") continue; // backwards compatibility
debug(format("bind mounting `%1%' to `%2%'") % source % target);
if (stat(source.c_str(), &st) == -1)
throw SysError(format("getting attributes of path `%1%'") % source);
@@ -2059,10 +2069,11 @@ void DerivationGoal::runChild()
createDirs(dirOf(target));
- if (mount(source.c_str(), target.c_str(), "", MS_BIND, 0) == -1)
+ if (firmlink(source, target) == -1)
throw SysError(format("bind mount from `%1%' to `%2%' failed") % source % target);
/* Bind a new instance of procfs on /proc to reflect our
private PID namespace. */
createDirs(chrootRootDir + "/proc");
@@ -2090,11 +2101,16 @@ void DerivationGoal::runChild()
Linux versions, it is created with permissions 0. */
chmod_(chrootRootDir + "/dev/pts/ptmx", 0666);
+ /* Do not mount things that are implemented in user land: /proc,
+ /dev/shm, /dev/pts, etc. */
if (chdir(chrootRootDir.c_str()) == -1)
throw SysError(format("cannot change directory to '%1%'") % chrootRootDir);
if (mkdir("real-root", 0) == -1)
throw SysError("cannot create real-root directory");
@@ -2109,8 +2125,13 @@ void DerivationGoal::runChild()
if (rmdir("real-root") == -1)
throw SysError("cannot remove real-root directory");
+ throw SysError(format("cannot change root directory to '%1%'") % chrootRootDir);
+#endif // CHROOT_ENABLED
if (chdir(tmpDirInSandbox.c_str()) == -1)
throw SysError(format("changing into `%1%'") % tmpDir);
diff --git a/nix/libutil/util.cc b/nix/libutil/util.cc
index 59a2981359..b49a17a6eb 100644
--- a/nix/libutil/util.cc
+++ b/nix/libutil/util.cc
+/* XXX: <idvec.h> uses 'new' as a parameter name. Work around it. */
+# include <hurd/paths.h>
+/* XXX: <fshelp.h> is not C++-compatible. Copy these declarations to work
+typedef error_t (*fshelp_open_fn_t) (int flags,
+ mach_msg_type_name_t *node_type,
+fshelp_start_translator (fshelp_open_fn_t underlying_open_fn, void *cookie,
+ char *name, char *argz, int argz_len,
+ int timeout, fsys_t *control);
+# define _HURD_FIRMLINK _HURD "firmlink"
@@ -214,6 +249,89 @@ bool isLink(const Path & path)
return S_ISLNK(st.st_mode);
+int firmlink(const Path &source, const Path &target)
+ return mount(source.c_str(), target.c_str(), "", MS_BIND, 0);
+static error_t return_node (int flags,
+ mach_port_t *underlying,
+ mach_msg_type_name_t *underlying_type,
+ task_t task, void *node)
+ *underlying = * (mach_port_t *) node;
+ *underlying_type = MACH_MSG_TYPE_COPY_SEND;
+int firmlink(const Path &source, const Path &target)
+ static char firmlink[] = _HURD_FIRMLINK;
+ char *arg_vec[] = { firmlink, (char *) source.c_str (), NULL };
+ file_t target_file = MACH_PORT_NULL;
+ printMsg (lvlChatty, format("creating firmlink from '%1%' to '%2%'")
+ target_file = file_name_lookup (target.c_str (), O_NOTRANS, 0);
+ if (! MACH_PORT_VALID (target_file)) {
+ printMsg (lvlChatty, format("firmlink target '%s' unavailable: %s")
+ % target % strerror (errno));
+ err = argz_create (arg_vec, &args, &args_len);
+ if (err != 0) goto fail;
+ err = fshelp_start_translator (return_node, &target_file,
+ firmlink, args, args_len,
+ printMsg (lvlChatty, format("failed to start '%s' translator: %s") %
+ firmlink % strerror(errno));
+ err = (error_t) file_set_translator (target_file, 0, FS_TRANS_SET, 0,
+ control, MACH_MSG_TYPE_COPY_SEND);
+ mach_port_deallocate (mach_task_self (), control);
+ mach_port_deallocate (mach_task_self (), target_file);
+ printMsg (lvlChatty, format("failed to set '%s' translator on node '%s': %s") %
+ firmlink % target % strerror(errno));
+ int saved_errno = errno;
+ if (MACH_PORT_VALID (target_file))
+ mach_port_deallocate (mach_task_self (), target_file);
+# error unsupported operating system
DirEntries readDirectory(const Path & path)
@@ -311,6 +429,27 @@ static void _deletePath(const Path & path, unsigned long long & bytesFreed, size
printMsg(lvlVomit, format("%1%") % path);
+ /* Check whether there's an active translator on PATH--typically
+ /hurd/firmlink. If there is one, let it go away. */
+ file_t file = file_name_lookup (path.c_str (), O_NOTRANS, 0);
+ if (MACH_PORT_VALID (file)) {
+ int err = file_get_translator_cntl (file, &fsys);
+ mach_port_deallocate (mach_task_self (), file);
+ /* There's a translator, tell it to leave. */
+ err = fsys_goaway (fsys, FSYS_GOAWAY_FORCE | FSYS_GOAWAY_RECURSE);
+ mach_port_deallocate (mach_task_self (), fsys);
+ throw SysError(format("removing translator from '%1%'") % path);
# define st_mode stx_mode
# define st_size stx_size
diff --git a/nix/libutil/util.hh b/nix/libutil/util.hh
index 13cff44316..353c758895 100644
--- a/nix/libutil/util.hh
+++ b/nix/libutil/util.hh
@@ -62,6 +62,9 @@ Path readLink(const Path & path);
bool isLink(const Path & path);
+/* Make TARGET a firmlink (aka. "bind mount") to SOURCE. */
+int firmlink(const Path & source, const Path & target);
/* Read the contents of a directory. The entries `.' and `..' are
diff --git a/nix/local.mk b/nix/local.mk
index 2bb01041b9..782e2c85cc 100644
@@ -124,7 +124,8 @@ guix_daemon_CPPFLAGS = \
libstore.a libutil.a libformat.a -lz \
- $(SQLITE3_LIBS) $(LIBGCRYPT_LIBS)
+ $(SQLITE3_LIBS) $(LIBGCRYPT_LIBS) \