From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Andrew Udvare <audvare@gmail.com>
Date: Sat, 10 Oct 2026 05:51:45 -0400
Subject: [PATCH] Link host directories into the prefix at the same paths

The host filesystem is only visible under /Volumes/SystemRoot, so host
paths passed on the command line (for example ~/dev/file, expanded by
the host shell to /home/user/dev/file) did not resolve in the
container.

On every start, link /home, /media, /mnt and /srv into the prefix as
links to their /Volumes/SystemRoot counterparts. macOS does not use
these paths. A directory is only linked if it has content on the host,
since an empty one is a placeholder. An existing link, file or
non-empty directory in the prefix is left untouched; only an empty
placeholder directory is replaced.

Signed-off-by: Andrew Udvare <audvare@gmail.com>
---
 src/startup/darling.c | 56 +++++++++++++++++++++++++++++++++++++++++++
 src/startup/darling.h |  4 ++++
 2 files changed, 60 insertions(+)

diff --git a/src/startup/darling.c b/src/startup/darling.c
index 0452c685d..d5f0786e4 100644
--- a/src/startup/darling.c
+++ b/src/startup/darling.c
@@ -26,6 +26,7 @@ along with Darling.  If not, see <http://www.gnu.org/licenses/>.
 #include <errno.h>
 #include <fcntl.h>
 #include <sys/stat.h>
+#include <dirent.h>
 #include <signal.h>
 #include <stdbool.h>
 #include <sched.h>
@@ -196,6 +197,11 @@ int main(int argc, char ** argv)
 
 	seteuid(g_originalUid);
 
+#if USE_LINUX_4_11_HACK
+	// in the container's mount namespace, the prefix path is the merged view of the prefix
+	linkHostDirectories();
+#endif
+
 	if (strcmp(argv[1], "shell") == 0)
 	{
 		// Spawn the shell
@@ -1213,3 +1219,53 @@ void checkPrefixOwner()
 		exit(1);
 	}
 }
+
+// Returns true if path is a directory with at least one entry
+static bool isNonEmptyDirectory(const char* path)
+{
+	DIR* dir = opendir(path);
+	struct dirent* entry;
+	bool nonEmpty = false;
+
+	if (dir == NULL)
+		return false;
+
+	while ((entry = readdir(dir)) != NULL)
+	{
+		if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0)
+		{
+			nonEmpty = true;
+			break;
+		}
+	}
+
+	closedir(dir);
+	return nonEmpty;
+}
+
+void linkHostDirectories(void)
+{
+	static const char* const dirs[] = { "/home", "/media", "/mnt", "/srv" };
+	char path[4096];
+	char target[4096];
+	struct stat st;
+
+	for (size_t i = 0; i < sizeof(dirs) / sizeof(dirs[0]); i++)
+	{
+		// an empty directory on the host is only a placeholder
+		if (!isNonEmptyDirectory(dirs[i]))
+			continue;
+
+		snprintf(path, sizeof(path), "%s%s", prefix, dirs[i]);
+		if (lstat(path, &st) == 0)
+		{
+			// keep whatever the prefix already has there, except an empty placeholder directory
+			if (!S_ISDIR(st.st_mode) || isNonEmptyDirectory(path) || rmdir(path) != 0)
+				continue;
+		}
+
+		snprintf(target, sizeof(target), "%s%s", SYSTEM_ROOT, dirs[i]);
+		if (symlink(target, path) != 0)
+			fprintf(stderr, "Cannot link %s to %s: %s\n", path, target, strerror(errno));
+	}
+}
diff --git a/src/startup/darling.h b/src/startup/darling.h
index 1a49e2f50..f584abeba 100644
--- a/src/startup/darling.h
+++ b/src/startup/darling.h
@@ -40,6 +40,10 @@ int checkPrefixDir(void);
 // Creates the given directory, exit()ing if not possible
 void createDir(const char* path);
 
+// Links host directories that macOS does not use (e.g. /home) into the prefix at the same paths,
+// so host paths passed on the command line resolve
+void linkHostDirectories(void);
+
 int connectToShellspawn(void);
 void setupShellspawnEnv(int shellspawnFD);
 void setupWorkingDir(int shellspawnFD);
