From 5e4cbf39cf6c11556cc41d98e79fdee5ae84adc8 Mon Sep 17 00:00:00 2001 From: Raito Bezarius Date: Fri, 25 Jul 2025 21:28:05 +0200 Subject: [PATCH] libstore/build: endure dangling symlinks of system files at sandbox setup In certain scenarios, a path may exist but is a broken symlink. For instance, this happens frequently if you are rescuing an existing NixOS system via `nixos-enter` or a manual `chroot` invocation because the rescued system `/etc` may have broken links or the system prior to the root pivot may interfere. Nonetheless, these dangling symlinks are not always fatal for the builds so we can just warn and skip their setup. The warnings will provide a better diagnostics for system administrators also. Fixes #893. Change-Id: Ifa12be3a43f23c973d7b466e8b73bd776abf3e7b Signed-off-by: Raito Bezarius --- lix/libstore/build/local-derivation-goal.cc | 48 ++++++++++++++++++--- 1 file changed, 41 insertions(+), 7 deletions(-) diff --git a/lix/libstore/build/local-derivation-goal.cc b/lix/libstore/build/local-derivation-goal.cc index 80b57aeaf..af9542076 100644 --- a/lix/libstore/build/local-derivation-goal.cc +++ b/lix/libstore/build/local-derivation-goal.cc @@ -1147,7 +1147,7 @@ void LocalDerivationGoal::runChild() happens when testing Nix building fixed-output derivations within a pure derivation. */ for (auto & path : { "/etc/services", "/etc/hosts" }) - if (pathExists(path)) { + if (pathAccessible(path, true)) { // Copy the actual file, not the symlink, because we don't know where // the symlink is pointing, and we don't want to chase down the entire // chain. @@ -1165,18 +1165,52 @@ void LocalDerivationGoal::runChild() // I also just generally feel icky about modifying sandbox state under a build, // even though it really shouldn't be a big deal. -K900 copyFile(path, chrootRootDir + path, { .followSymlinks = true }); + } else if (pathExists(path)) { + // The path exist but we were not able to access it. This is not a fatal + // error, warn about this so the user can remediate. + warn( + "'%1%' exists but is inaccessible, it will not be copied in the " + "sandbox", + path + ); } - if (pathExists("/etc/resolv.conf")) { + if (pathAccessible("/etc/resolv.conf", true)) { const auto resolvConf = rewriteResolvConf(readFile("/etc/resolv.conf")); writeFile(chrootRootDir + "/etc/resolv.conf", resolvConf); + } else if (pathExists("/etc/resolv.conf")) { + // The path exist but we were not able to access it. This is not a fatal error, + // warn about this so the user can remediate. + warn( + "'/etc/resolv.conf' exists but is inaccessible, it will not be rewritten " + "inside the sandbox; DNS operations inside the sandbox may be " + "non-functional." + ); } - if (settings.caFile != "" && pathExists(settings.caFile)) { - // For the same reasons as above, copy the CA certificates file too. - // It should be even less likely to change during the build than resolv.conf. - createDirs(chrootRootDir + "/etc/ssl/certs"); - copyFile(settings.caFile, chrootRootDir + "/etc/ssl/certs/ca-certificates.crt", { .followSymlinks = true }); + if (settings.caFile != "") { + if (pathAccessible(settings.caFile, true)) { + // For the same reasons as above, copy the CA certificates file too. + // It should be even less likely to change during the build than + // resolv.conf. + createDirs(chrootRootDir + "/etc/ssl/certs"); + copyFile( + settings.caFile, + chrootRootDir + "/etc/ssl/certs/ca-certificates.crt", + {.followSymlinks = true} + ); + } else if (pathExists(settings.caFile)) { + // The path exist but we were not able to access it. This is not a fatal + // error, warn about this so the user can remediate. + warn( + "Configured certificate authority '%1' exists but is inaccessible, it " + "will " + "not be copied in the sandbox. TLS operations inside the sandbox may " + "be " + "non-functional.", + settings.caFile + ); + } } }