summary refs log tree commit diff
path: root/pkgs/build-support/build-fhs-userenv
diff options
context:
space:
mode:
authorNikolay Amiantov <ab@fmap.me>2019-05-12 09:35:12 +0300
committerNikolay Amiantov <ab@fmap.me>2019-05-30 15:34:01 +0300
commit06f27dc2e932778cf1d2ce6b0bf30b32575dd16a (patch)
treeaf911880a15d1572a752012afe72a3fcc343832b /pkgs/build-support/build-fhs-userenv
parent7664ffbbafbe1275529fcca7936a321cb1c9e90c (diff)
downloadnixpkgs-06f27dc2e932778cf1d2ce6b0bf30b32575dd16a.tar
nixpkgs-06f27dc2e932778cf1d2ce6b0bf30b32575dd16a.tar.gz
nixpkgs-06f27dc2e932778cf1d2ce6b0bf30b32575dd16a.tar.bz2
nixpkgs-06f27dc2e932778cf1d2ce6b0bf30b32575dd16a.tar.lz
nixpkgs-06f27dc2e932778cf1d2ce6b0bf30b32575dd16a.tar.xz
nixpkgs-06f27dc2e932778cf1d2ce6b0bf30b32575dd16a.tar.zst
nixpkgs-06f27dc2e932778cf1d2ce6b0bf30b32575dd16a.zip
chrootenv: propagate nested chrootenv /host
To avoid symlink loops to /host in nested chrootenvs we need to remove
one level of indirection. This is also what's generally expected of
/host contents.
Diffstat (limited to 'pkgs/build-support/build-fhs-userenv')
-rw-r--r--pkgs/build-support/build-fhs-userenv/chrootenv/chrootenv.c29
1 files changed, 23 insertions, 6 deletions
diff --git a/pkgs/build-support/build-fhs-userenv/chrootenv/chrootenv.c b/pkgs/build-support/build-fhs-userenv/chrootenv/chrootenv.c
index 9a7c76b1c07..dcb2e97aa93 100644
--- a/pkgs/build-support/build-fhs-userenv/chrootenv/chrootenv.c
+++ b/pkgs/build-support/build-fhs-userenv/chrootenv/chrootenv.c
@@ -18,7 +18,7 @@
   if (expr)                                                                    \
     fail(#expr, errno);
 
-const gchar *bind_blacklist[] = {"bin", "etc", "host", "usr", "lib", "lib64", "lib32", "sbin", NULL};
+const gchar *bind_blacklist[] = {"bin", "etc", "host", "real-host", "usr", "lib", "lib64", "lib32", "sbin", NULL};
 
 int pivot_root(const char *new_root, const char *put_old) {
   return syscall(SYS_pivot_root, new_root, put_old);
@@ -56,9 +56,12 @@ void bind_mount_item(const gchar *host, const gchar *guest, const gchar *name) {
 
 void bind(const gchar *host, const gchar *guest) {
   mount_tmpfs(guest);
+  pivot_host(guest);
+
+  g_autofree gchar *host_dir = g_build_filename("/host", host, NULL);
 
   g_autoptr(GError) err = NULL;
-  g_autoptr(GDir) dir = g_dir_open(host, 0, &err);
+  g_autoptr(GDir) dir = g_dir_open(host_dir, 0, &err);
 
   if (err != NULL)
     fail("g_dir_open", errno);
@@ -67,9 +70,7 @@ void bind(const gchar *host, const gchar *guest) {
 
   while ((item = g_dir_read_name(dir)))
     if (!g_strv_contains(bind_blacklist, item))
-      bind_mount_item(host, guest, item);
-
-  pivot_host(guest);
+      bind_mount_item(host_dir, "/", item);
 }
 
 void spit(const char *path, char *fmt, ...) {
@@ -119,7 +120,23 @@ int main(gint argc, gchar **argv) {
     spit("/proc/self/uid_map", "%d %d 1", uid, uid);
     spit("/proc/self/gid_map", "%d %d 1", gid, gid);
 
-    bind("/", prefix);
+    // If there is a /host directory, assume this is nested chrootenv and use it as host instead.
+    gboolean nested_host = g_file_test("/host", G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR);
+    g_autofree const gchar *host = nested_host ? "/host" : "/";
+
+    bind(host, prefix);
+
+    // Replace /host by an actual (inner) /host.
+    if (nested_host) {
+      fail_if(g_mkdir("/real-host", 0755));
+      fail_if(mount("/host/host", "/real-host", NULL, MS_BIND | MS_REC, NULL));
+      // For some reason umount("/host") returns EBUSY even immediately after
+      // pivot_root. We detach it at least to keep `/proc/mounts` from blowing
+      // up in nested cases.
+      fail_if(umount2("/host", MNT_DETACH));
+      fail_if(mount("/real-host", "/host", NULL, MS_MOVE, NULL));
+      fail_if(rmdir("/real-host"));
+    }
 
     fail_if(chdir("/"));
     fail_if(execvp(*argv, argv));