From 5ca168f6f2dfda3aa047386de6d8733fcddff1bd Mon Sep 17 00:00:00 2001 From: Raito Bezarius Date: Sat, 22 Mar 2025 20:26:04 +0100 Subject: [PATCH] chore: drop experimental feature `recursive-nix` This was discussed as part of https://git.lix.systems/lix-project/lix/issues/767 with a one month long comment period. This commit removes the recursive-nix feature from Lix. It has seen limited usage and is difficult to stabilize, especially on non-Linux systems where its behavior is underspecified. Maintaining this feature complicates core work on the store, as we must account for the potential presence of the daemon in the sandbox, adding unnecessary complexity. Additionally, its inclusion in the platform-independent local store creates risks for non-Linux platforms. For more details on this removal, refer to the release note entry or the issue entry. Change-Id: I9137202f563c0a317f9c5da79cd9fd07d801427a Signed-off-by: Raito Bezarius --- doc/manual/rl-next/drop-recursive-nix.md | 23 + lix/libstore/build/local-derivation-goal.cc | 496 +----------------- lix/libstore/build/local-derivation-goal.hh | 56 -- lix/libstore/daemon.cc | 20 +- lix/libstore/daemon.hh | 5 +- lix/libstore/platform/linux.cc | 12 - lix/libstore/store-api.cc | 3 - .../experimental-features/recursive-nix.md | 39 -- lix/libutil/meson.build | 1 - lix/nix/daemon.cc | 4 +- tests/functional/ca/recursive.sh | 9 - .../functional/dyn-drv/recursive-mod-json.nix | 33 -- .../functional/dyn-drv/recursive-mod-json.sh | 27 - tests/functional/meson.build | 3 - tests/functional/recursive.nix | 56 -- tests/functional/recursive.sh | 19 - 16 files changed, 36 insertions(+), 770 deletions(-) create mode 100644 doc/manual/rl-next/drop-recursive-nix.md delete mode 100644 lix/libutil/experimental-features/recursive-nix.md delete mode 100755 tests/functional/ca/recursive.sh delete mode 100644 tests/functional/dyn-drv/recursive-mod-json.nix delete mode 100644 tests/functional/dyn-drv/recursive-mod-json.sh delete mode 100644 tests/functional/recursive.nix delete mode 100644 tests/functional/recursive.sh diff --git a/doc/manual/rl-next/drop-recursive-nix.md b/doc/manual/rl-next/drop-recursive-nix.md new file mode 100644 index 000000000..a4b79f725 --- /dev/null +++ b/doc/manual/rl-next/drop-recursive-nix.md @@ -0,0 +1,23 @@ +--- +synopsis: "Removal of the `recursive-nix` experimental feature" +issues: [fj#767] +cls: [2872] +category: "Breaking Changes" +credits: ["raito"] +--- + +The `recursive-nix` experimental feature and all associated code have been removed. + +`recursive-nix` enabled running Nix operations (like evaluations and builds) *inside* a derivation builder. This worked by spawning a temporary Nix daemon socket within the build environment, allowing the derivation to emit outputs that appeared in the outer store. This was primarily used to prototype **dynamic derivations** (dyndrvs), where build plans are generated on-the-fly during a build. + +However, this approach introduced critical issues: + +- It entrenched the legacy Nix daemon protocol as part of the derivation ABI, which is a blocker for future stabilization. +- It imposed tight coupling between sandbox setup code and knowledge of Nix internals, complicating refactoring and long-term maintenance. +- It was never intended to be the final design for dynamic derivations. The original Nix implementation team, who are leading dyndrv development, have agreed it will be replaced (likely via `varlink` or similar) before any stabilization. +- There is currently no known usage of `recursive-nix` on `lix` or elsewhere **in production**. + +If you're using `recursive-nix` for something niche or experimental, we'd love to hear from you on the RFD issue. +You can still run `nix` inside a builder manually if needed — including with isolated user namespaces and fake stores — but the special daemon-handshake machinery is gone. + +This removal unblocks several important internal cleanups. diff --git a/lix/libstore/build/local-derivation-goal.cc b/lix/libstore/build/local-derivation-goal.cc index faac8084b..628948dab 100644 --- a/lix/libstore/build/local-derivation-goal.cc +++ b/lix/libstore/build/local-derivation-goal.cc @@ -110,7 +110,6 @@ LocalDerivationGoal::~LocalDerivationGoal() noexcept(false) /* Careful: we should never ever throw an exception from a destructor. */ try { killChild(); } catch (...) { ignoreExceptionInDestructor(); } - try { stopDaemon(); } catch (...) { ignoreExceptionInDestructor(); } try { deleteTmpDir(false, true); } catch (...) { ignoreExceptionInDestructor(); } } @@ -330,8 +329,6 @@ void LocalDerivationGoal::cleanupHookFinally() void LocalDerivationGoal::cleanupPreChildKill() { - sandboxMountNamespace.reset(); - sandboxUserNamespace.reset(); } @@ -343,9 +340,6 @@ void LocalDerivationGoal::cleanupPostChildKill() open and modifies them after they have been chown'ed to root. */ killSandbox(true); - - /* Terminate the recursive Nix daemon. */ - stopDaemon(); } @@ -734,11 +728,6 @@ try { } } - /* Fire up a Nix daemon to process recursive Nix calls from the - builder. */ - if (parsedDrv->getRequiredSystemFeatures().count("recursive-nix")) - startDaemon(); - /* Run the builder. */ printMsg(lvlChatty, "executing builder '%1%'", drv->builder); printMsg(lvlChatty, "using builder args '%1%'", concatStringsSep(" ", drv->args)); @@ -979,486 +968,6 @@ try { co_return result::current_exception(); } - -static StorePath pathPartOfReq(const SingleDerivedPath & req) -{ - return std::visit(overloaded { - [&](const SingleDerivedPath::Opaque & bo) { - return bo.path; - }, - [&](const SingleDerivedPath::Built & bfd) { - return pathPartOfReq(*bfd.drvPath); - }, - }, req.raw()); -} - - -static StorePath pathPartOfReq(const DerivedPath & req) -{ - return std::visit(overloaded { - [&](const DerivedPath::Opaque & bo) { - return bo.path; - }, - [&](const DerivedPath::Built & bfd) { - return pathPartOfReq(*bfd.drvPath); - }, - }, req.raw()); -} - - -bool LocalDerivationGoal::isAllowed(const DerivedPath & req) -{ - return this->isAllowed(pathPartOfReq(req)); -} - - -struct RestrictedStoreConfig final : LocalFSStoreConfig -{ - using LocalFSStoreConfig::LocalFSStoreConfig; - const std::string name() override { return "Restricted Store"; } -}; - -/* A wrapper around LocalStore that only allows building/querying of - paths that are in the input closures of the build or were added via - recursive Nix calls. */ -struct RestrictedStore : public virtual IndirectRootStore, public virtual GcStore -{ - RestrictedStoreConfig config_; - - RestrictedStoreConfig & config() override { return config_; } - const RestrictedStoreConfig & config() const override { return config_; } - - ref next; - - LocalDerivationGoal & goal; - - RestrictedStore(RestrictedStoreConfig config, ref next, LocalDerivationGoal & goal) - : Store(config) - , config_(std::move(config)) - , next(next), goal(goal) - { } - - Path getRealStoreDir() override - { return next->config().realStoreDir; } - - std::string getUri() override - { return next->getUri(); } - - kj::Promise> queryAllValidPaths() override - try { - StorePathSet paths; - for (auto & p : goal.inputPaths) paths.insert(p); - for (auto & p : goal.addedPaths) paths.insert(p); - co_return paths; - } catch (...) { - co_return result::current_exception(); - } - - kj::Promise>> - queryPathInfoUncached(const StorePath & path) override - try { - if (goal.isAllowed(path)) { - try { - /* Censor impure information. */ - auto info = std::make_shared(*TRY_AWAIT(next->queryPathInfo(path))); - info->deriver.reset(); - info->registrationTime = 0; - info->ultimate = false; - info->sigs.clear(); - co_return info; - } catch (InvalidPath &) { - co_return result::success(nullptr); - } - } else - co_return result::success(nullptr); - } catch (...) { - co_return result::current_exception(); - } - - kj::Promise> - queryReferrers(const StorePath & path, StorePathSet & referrers) override - { return {result::success()}; } - - kj::Promise>>> - queryPartialDerivationOutputMap(const StorePath & path, Store * evalStore = nullptr) override - try { - if (!goal.isAllowed(path)) - throw InvalidPath("cannot query output map for unknown path '%s' in recursive Nix", printStorePath(path)); - co_return TRY_AWAIT(next->queryPartialDerivationOutputMap(path, evalStore)); - } catch (...) { - co_return result::current_exception(); - } - - kj::Promise>> - queryPathFromHashPart(const std::string & hashPart) override - try { - throw Error("queryPathFromHashPart"); - } catch (...) { - return {result::current_exception()}; - } - - kj::Promise> addToStoreRecursive( - std::string_view name, - const PreparedDump & source, - HashType hashAlgo, - RepairFlag repair) override - try { throw Error("addToStoreRecursive"); } catch (...) { return {result::current_exception()}; } - - kj::Promise> addToStoreFlat( - std::string_view name, - const Path & srcPath, - HashType hashAlgo, - RepairFlag repair) override - try { throw Error("addToStoreFlat"); } catch (...) { return {result::current_exception()}; } - - kj::Promise> addToStore(const ValidPathInfo & info, AsyncInputStream & narSource, - RepairFlag repair = NoRepair, CheckSigsFlag checkSigs = CheckSigs) override - try { - TRY_AWAIT(next->addToStore(info, narSource, repair, checkSigs)); - goal.addDependency(info.path); - co_return result::success(); - } catch (...) { - co_return result::current_exception(); - } - - kj::Promise> addTextToStore( - std::string_view name, - std::string_view s, - const StorePathSet & references, - RepairFlag repair = NoRepair) override - try { - auto path = TRY_AWAIT(next->addTextToStore(name, s, references, repair)); - goal.addDependency(path); - co_return path; - } catch (...) { - co_return result::current_exception(); - } - - kj::Promise> addToStoreFromDump( - AsyncInputStream & dump, - std::string_view name, - FileIngestionMethod method, - HashType hashAlgo, - RepairFlag repair, - const StorePathSet & references) override - try { - auto path = TRY_AWAIT(next->addToStoreFromDump(dump, name, method, hashAlgo, repair, references)); - goal.addDependency(path); - co_return path; - } catch (...) { - co_return result::current_exception(); - } - - kj::Promise>> narFromPath(const StorePath & path) override - try { - if (!goal.isAllowed(path)) - throw InvalidPath("cannot dump unknown path '%s' in recursive Nix", printStorePath(path)); - co_return TRY_AWAIT(LocalFSStore::narFromPath(path)); - } catch (...) { - co_return result::current_exception(); - } - - kj::Promise> ensurePath(const StorePath & path) override - try { - if (!goal.isAllowed(path)) - throw InvalidPath("cannot substitute unknown path '%s' in recursive Nix", printStorePath(path)); - /* Nothing to be done; 'path' must already be valid. */ - return {result::success()}; - } catch (...) { - return {result::current_exception()}; - } - - kj::Promise> registerDrvOutput(const Realisation & info) override - // XXX: This should probably be allowed as a no-op if the realisation - // corresponds to an allowed derivation - try { throw Error("registerDrvOutput"); } catch (...) { return {result::current_exception()}; } - - kj::Promise>> - queryRealisationUncached(const DrvOutput & id) override - // XXX: This should probably be allowed if the realisation corresponds to - // an allowed derivation - try { - if (!goal.isAllowed(id)) - co_return result::success(nullptr); - co_return TRY_AWAIT(next->queryRealisation(id)); - } catch (...) { - co_return result::current_exception(); - } - - kj::Promise> buildPaths( - const std::vector & paths, - BuildMode buildMode, - std::shared_ptr evalStore - ) override - try { - for (auto & result : TRY_AWAIT(buildPathsWithResults(paths, buildMode, evalStore))) - if (!result.success()) - result.rethrow(); - co_return result::success(); - } catch (...) { - co_return result::current_exception(); - } - - kj::Promise>> buildPathsWithResults( - const std::vector & paths, - BuildMode buildMode = bmNormal, - std::shared_ptr evalStore = nullptr) override - try { - assert(!evalStore); - - if (buildMode != bmNormal) throw Error("unsupported build mode"); - - StorePathSet newPaths; - std::set newRealisations; - - for (auto & req : paths) { - if (!goal.isAllowed(req)) - throw InvalidPath("cannot build '%s' in recursive Nix because path is unknown", req.to_string(*next)); - } - - auto results = TRY_AWAIT(next->buildPathsWithResults(paths, buildMode)); - - for (auto & result : results) { - for (auto & [outputName, output] : result.builtOutputs) { - newPaths.insert(output.outPath); - newRealisations.insert(output); - } - } - - StorePathSet closure; - TRY_AWAIT(next->computeFSClosure(newPaths, closure)); - for (auto & path : closure) - goal.addDependency(path); - for (auto & real : TRY_AWAIT(Realisation::closure(*next, newRealisations))) - goal.addedDrvOutputs.insert(real.id); - - co_return results; - } catch (...) { - co_return result::current_exception(); - } - - kj::Promise> buildDerivation( - const StorePath & drvPath, const BasicDerivation & drv, BuildMode buildMode = bmNormal - ) override - try { unsupported("buildDerivation"); } catch (...) { return {result::current_exception()}; } - - kj::Promise> addTempRoot(const StorePath & path) override - { return {result::success()}; } - - kj::Promise> addIndirectRoot(const Path & path) override - { return {result::success()}; } - - kj::Promise> findRoots(bool censor) override - { return {Roots()}; } - - kj::Promise> - collectGarbage(const GCOptions & options, GCResults & results) override - { - return {result::success()}; - } - - kj::Promise> - addSignatures(const StorePath & storePath, const StringSet & sigs) override - try { unsupported("addSignatures"); } catch (...) { return {result::current_exception()}; } - - kj::Promise> queryMissing(const std::vector & targets, - StorePathSet & willBuild, StorePathSet & willSubstitute, StorePathSet & unknown, - uint64_t & downloadSize, uint64_t & narSize) override - try { - /* This is slightly impure since it leaks information to the - client about what paths will be built/substituted or are - already present. Probably not a big deal. */ - - std::vector allowed; - for (auto & req : targets) { - if (goal.isAllowed(req)) - allowed.emplace_back(req); - else - unknown.insert(pathPartOfReq(req)); - } - - TRY_AWAIT(next->queryMissing(allowed, willBuild, willSubstitute, - unknown, downloadSize, narSize)); - co_return result::success(); - } catch (...) { - co_return result::current_exception(); - } - - virtual kj::Promise>> getBuildLogExact(const StorePath & path) override - { - return {std::nullopt}; - } - - virtual kj::Promise> - addBuildLog(const StorePath & path, std::string_view log) override - try { - unsupported("addBuildLog"); - } catch (...) { - return {result::current_exception()}; - } - - kj::Promise>> isTrustedClient() override - { return {result::success(NotTrusted)}; } -}; - - -void LocalDerivationGoal::startDaemon() -{ - experimentalFeatureSettings.require(Xp::RecursiveNix); - - StoreConfig::Params params; - params["path-info-cache-size"] = "0"; - params["store"] = worker.store.config().storeDir; - if (auto & optRoot = getLocalStore().config().rootDir.get()) - params["root"] = *optRoot; - params["state"] = "/no-such-path"; - params["log"] = "/no-such-path"; - auto store = make_ref( - params, - ref::unsafeFromPtr( - std::dynamic_pointer_cast(worker.store.shared_from_this()) - ), - *this - ); - - addedPaths.clear(); - - auto socketName = ".nix-socket"; - Path socketPath = tmpDir + "/" + socketName; - env["NIX_REMOTE"] = "unix://" + tmpDirInSandbox + "/" + socketName; - - daemonSocket = createUnixDomainSocket(socketPath, 0600); - - chownToBuilder(socketPath); - - daemonThread = std::thread([this, store]() { - setCurrentThreadName("recursive nix daemon"); - - while (true) { - - /* Accept a connection. */ - struct sockaddr_un remoteAddr; - socklen_t remoteAddrLen = sizeof(remoteAddr); - - AutoCloseFD remote{accept(daemonSocket.get(), - reinterpret_cast(&remoteAddr), &remoteAddrLen)}; - if (!remote) { - if (errno == EINTR || errno == EAGAIN) continue; - if (errno == EINVAL || errno == ECONNABORTED) break; - throw SysError("accepting connection"); - } - - closeOnExec(remote.get()); - - debug("received daemon connection"); - - auto workerThread = std::thread([store, remote{std::move(remote)}]() { - setCurrentThreadName("recursive nix worker"); - FdSource from(remote.get()); - FdSink to(remote.get()); - try { - AsyncIoRoot aio; - daemon::processConnection(aio, store, from, to, - NotTrusted, daemon::Recursive); - debug("terminated daemon connection"); - } catch (SysError &) { - ignoreExceptionExceptInterrupt(); - } - }); - - daemonWorkerThreads.push_back(std::move(workerThread)); - } - - debug("daemon shutting down"); - }); -} - - -void LocalDerivationGoal::stopDaemon() -{ - if (daemonSocket && shutdown(daemonSocket.get(), SHUT_RDWR) == -1) { - // According to the POSIX standard, the 'shutdown' function should - // return an ENOTCONN error when attempting to shut down a socket that - // hasn't been connected yet. This situation occurs when the 'accept' - // function is called on a socket without any accepted connections, - // leaving the socket unconnected. While Linux doesn't seem to produce - // an error for sockets that have only been accepted, more - // POSIX-compliant operating systems like OpenBSD, macOS, and others do - // return the ENOTCONN error. Therefore, we handle this error here to - // avoid raising an exception for compliant behaviour. - if (errno == ENOTCONN) { - daemonSocket.close(); - } else { - throw SysError("shutting down daemon socket"); - } - } - - if (daemonThread.joinable()) - daemonThread.join(); - - // FIXME: should prune worker threads more quickly. - // FIXME: shutdown the client socket to speed up worker termination. - for (auto & thread : daemonWorkerThreads) - thread.join(); - daemonWorkerThreads.clear(); - - // release the socket. - daemonSocket.close(); -} - - -void LocalDerivationGoal::addDependency(const StorePath & path) -{ - if (isAllowed(path)) return; - - addedPaths.insert(path); - - /* If we're doing a sandbox build, then we have to make the path - appear in the sandbox. */ - if (useChroot) { - - debug("materialising '%s' in the sandbox", worker.store.printStorePath(path)); - - #if __linux__ - - Path source = worker.store.Store::toRealPath(path); - Path target = chrootRootDir + worker.store.printStorePath(path); - - if (pathExists(target)) { - // There is a similar debug message in bindPath, so only run it in this block to not have double messages. - debug("bind-mounting %s -> %s", target, source); - throw Error("store path '%s' already exists in the sandbox", worker.store.printStorePath(path)); - } - - /* Bind-mount the path into the sandbox. This requires - entering its mount namespace, which is not possible - in multithreaded programs. So we do this in a - child process.*/ - Pid child = startProcess([&]() { - - if (usingUserNamespace && (setns(sandboxUserNamespace.get(), 0) == -1)) - throw SysError("entering sandbox user namespace"); - - if (setns(sandboxMountNamespace.get(), 0) == -1) - throw SysError("entering sandbox mount namespace"); - - bindPath(source, target); - - _exit(0); - }); - - int status = child.wait(); - if (status != 0) - throw Error("could not add path '%s' to sandbox", worker.store.printStorePath(path)); - - #else - throw Error("don't know how to make path '%s' (produced by a recursive Nix call) appear in the sandbox", - worker.store.printStorePath(path)); - #endif - - } -} - void LocalDerivationGoal::chownToBuilder(const Path & path) { if (!buildUser) return; @@ -1692,6 +1201,10 @@ void LocalDerivationGoal::runChild() if (!parsedDrv->useUidRange()) chmodPath(chrootRootDir + "/etc", 0555); + /* The comment below is now outdated. Recursive Nix has been removed. + * So there's no need to make path appear in the sandbox. + * TODO(Raito): cleanup before a merge. + */ /* Unshare this mount namespace. This is necessary because pivot_root() below changes the root of the mount namespace. This means that the call to setns() in @@ -2010,7 +1523,6 @@ try { StorePathSet referenceablePaths; for (auto & p : inputPaths) referenceablePaths.insert(p); for (auto & i : scratchOutputs) referenceablePaths.insert(i.second); - for (auto & p : addedPaths) referenceablePaths.insert(p); /* FIXME `needsHashRewrite` should probably be removed and we get to the real reason why we aren't using the chroot dir */ diff --git a/lix/libstore/build/local-derivation-goal.hh b/lix/libstore/build/local-derivation-goal.hh index 9bfd28f35..239574673 100644 --- a/lix/libstore/build/local-derivation-goal.hh +++ b/lix/libstore/build/local-derivation-goal.hh @@ -47,13 +47,6 @@ struct LocalDerivationGoal : public DerivationGoal */ Pipe userNamespaceSync; - /** - * The mount namespace and user namespace of the builder, used to add additional - * paths to the sandbox as a result of recursive Nix calls. - */ - AutoCloseFD sandboxMountNamespace; - AutoCloseFD sandboxUserNamespace; - /** * On Linux, whether we're doing the build in its own user * namespace. @@ -134,50 +127,11 @@ struct LocalDerivationGoal : public DerivationGoal const static Path homeDir; - /** - * The recursive Nix daemon socket. - */ - AutoCloseFD daemonSocket; - - /** - * The daemon main thread. - */ - std::thread daemonThread; - /** * The daemon worker threads. */ std::vector daemonWorkerThreads; - /** - * Paths that were added via recursive Nix calls. - */ - StorePathSet addedPaths; - - /** - * Realisations that were added via recursive Nix calls. - */ - std::set addedDrvOutputs; - - /** - * Recursive Nix calls are only allowed to build or realize paths - * in the original input closure or added via a recursive Nix call - * (so e.g. you can't do 'nix-store -r /nix/store/' where - * /nix/store/ is some arbitrary path in a binary cache). - */ - bool isAllowed(const StorePath & path) - { - return inputPaths.count(path) || addedPaths.count(path); - } - bool isAllowed(const DrvOutput & id) - { - return addedDrvOutputs.count(id); - } - - bool isAllowed(const DerivedPath & req); - - friend struct RestrictedStore; - /** * Create a LocalDerivationGoal without an on-disk .drv file, * possibly a platform-specific subclass @@ -236,16 +190,6 @@ struct LocalDerivationGoal : public DerivationGoal */ kj::Promise> writeStructuredAttrs(); - void startDaemon(); - - void stopDaemon(); - - /** - * Add 'path' to the set of paths that may be referenced by the - * outputs, and make it appear in the sandbox. - */ - void addDependency(const StorePath & path); - /** * Make a file owned by the builder. */ diff --git a/lix/libstore/daemon.cc b/lix/libstore/daemon.cc index 15703e419..18f650770 100644 --- a/lix/libstore/daemon.cc +++ b/lix/libstore/daemon.cc @@ -264,7 +264,7 @@ struct ClientSettings }; static void performOp(AsyncIoRoot & aio, TunnelLogger * logger, ref store, - TrustedFlag trusted, RecursiveFlag recursive, WorkerProto::Version clientVersion, + TrustedFlag trusted, WorkerProto::Version clientVersion, Source & from, BufferedSink & to, WorkerProto::Op op) { WorkerProto::ReadConn rconn{from, clientVersion}; @@ -773,12 +773,7 @@ static void performOp(AsyncIoRoot & aio, TunnelLogger * logger, ref store } logger->startWork(); - - // FIXME: use some setting in recursive mode. Will need to use - // non-global variables. - if (!recursive) - clientSettings.apply(trusted); - + clientSettings.apply(trusted); logger->stopWork(); break; } @@ -1014,10 +1009,9 @@ void processConnection( ref store, FdSource & from, FdSink & to, - TrustedFlag trusted, - RecursiveFlag recursive) + TrustedFlag trusted) { - auto monitor = !recursive ? std::make_unique(from.fd) : nullptr; + auto monitor = std::make_unique(from.fd); /* Exchange the greeting. */ unsigned int magic = readInt(from); @@ -1031,9 +1025,7 @@ void processConnection( auto tunnelLogger = new TunnelLogger(to, clientVersion); auto prevLogger = nix::logger; - // FIXME - if (!recursive) - logger = tunnelLogger; + logger = tunnelLogger; unsigned int opCount = 0; @@ -1089,7 +1081,7 @@ void processConnection( debug("performing daemon worker op: %d", op); try { - performOp(aio, tunnelLogger, store, trusted, recursive, clientVersion, from, to, op); + performOp(aio, tunnelLogger, store, trusted, clientVersion, from, to, op); } catch (Error & e) { /* If we're not in a state where we can send replies, then something went wrong processing the input of the diff --git a/lix/libstore/daemon.hh b/lix/libstore/daemon.hh index fbc7180d2..6b8b8eb45 100644 --- a/lix/libstore/daemon.hh +++ b/lix/libstore/daemon.hh @@ -7,14 +7,11 @@ namespace nix::daemon { -enum RecursiveFlag : bool { NotRecursive = false, Recursive = true }; - void processConnection( AsyncIoRoot & aio, ref store, FdSource & from, FdSink & to, - TrustedFlag trusted, - RecursiveFlag recursive); + TrustedFlag trusted); } diff --git a/lix/libstore/platform/linux.cc b/lix/libstore/platform/linux.cc index d1217ccb4..02435e032 100644 --- a/lix/libstore/platform/linux.cc +++ b/lix/libstore/platform/linux.cc @@ -973,18 +973,6 @@ Pid LinuxLocalDerivationGoal::startChild(std::function openSlave) "nixbld:!:%1%:\n" "nogroup:x:65534:\n", sandboxGid())); - /* Save the mount- and user namespace of the child. We have to do this - *before* the child does a chroot. */ - sandboxMountNamespace = AutoCloseFD{open(fmt("/proc/%d/ns/mnt", pid.get()).c_str(), O_RDONLY)}; - if (sandboxMountNamespace.get() == -1) - throw SysError("getting sandbox mount namespace"); - - if (usingUserNamespace) { - sandboxUserNamespace = AutoCloseFD{open(fmt("/proc/%d/ns/user", pid.get()).c_str(), O_RDONLY)}; - if (sandboxUserNamespace.get() == -1) - throw SysError("getting sandbox user namespace"); - } - /* Move the child into its own cgroup. */ if (cgroup) writeFile(*cgroup + "/cgroup.procs", fmt("%d", pid.get())); diff --git a/lix/libstore/store-api.cc b/lix/libstore/store-api.cc index 059f5099c..1a6a9f93e 100644 --- a/lix/libstore/store-api.cc +++ b/lix/libstore/store-api.cc @@ -490,9 +490,6 @@ StringSet StoreConfig::getDefaultSystemFeatures() if (experimentalFeatureSettings.isEnabled(Xp::CaDerivations)) res.insert("ca-derivations"); - if (experimentalFeatureSettings.isEnabled(Xp::RecursiveNix)) - res.insert("recursive-nix"); - return res; } diff --git a/lix/libutil/experimental-features/recursive-nix.md b/lix/libutil/experimental-features/recursive-nix.md deleted file mode 100644 index fd67ed5aa..000000000 --- a/lix/libutil/experimental-features/recursive-nix.md +++ /dev/null @@ -1,39 +0,0 @@ ---- -name: recursive-nix -internalName: RecursiveNix ---- -Allow derivation builders to call Nix, and thus build derivations -recursively. - -Example: - -``` -with import {}; - -runCommand "foo" - { - buildInputs = [ nix jq ]; - NIX_PATH = "nixpkgs=${}"; - } - '' - hello=$(nix-build -E '(import {}).hello.overrideDerivation (args: { name = "recursive-hello"; })') - - mkdir -p $out/bin - ln -s $hello/bin/hello $out/bin/hello - '' -``` - -An important restriction on recursive builders is disallowing -arbitrary substitutions. For example, running - -``` -nix-store -r /nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10 -``` - -in the above `runCommand` script would be disallowed, as this could -lead to derivations with hidden dependencies or breaking -reproducibility by relying on the current state of the Nix store. An -exception would be if -`/nix/store/kmwd1hq55akdb9sc7l3finr175dajlby-hello-2.10` were -already in the build inputs or built by a previous recursive Nix -call. diff --git a/lix/libutil/meson.build b/lix/libutil/meson.build index 2b7214734..5d7a63dc5 100644 --- a/lix/libutil/meson.build +++ b/lix/libutil/meson.build @@ -154,7 +154,6 @@ experimental_feature_definitions = files( 'experimental-features/parse-toml-timestamps.md', 'experimental-features/pipe-operator.md', 'experimental-features/read-only-local-store.md', - 'experimental-features/recursive-nix.md', 'experimental-features/repl-automation.md', # keep-sorted end ) diff --git a/lix/nix/daemon.cc b/lix/nix/daemon.cc index d53ca88c4..a163e0c4b 100644 --- a/lix/nix/daemon.cc +++ b/lix/nix/daemon.cc @@ -368,7 +368,7 @@ static void daemonLoopImpl(std::optional forceTrustClientOpt) FdSource from(remote.get()); FdSink to(remote.get()); processConnection( - aio, aio.blockOn(openUncachedStore()), from, to, trusted, NotRecursive + aio, aio.blockOn(openUncachedStore()), from, to, trusted ); exit(0); @@ -451,7 +451,7 @@ processStdioConnection(AsyncIoRoot & aio, ref store, TrustedFlag trustCli { FdSource from(STDIN_FILENO); FdSink to(STDOUT_FILENO); - processConnection(aio, store, from, to, trustClient, NotRecursive); + processConnection(aio, store, from, to, trustClient); } /** diff --git a/tests/functional/ca/recursive.sh b/tests/functional/ca/recursive.sh deleted file mode 100755 index cd6736b24..000000000 --- a/tests/functional/ca/recursive.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env bash - -source common.sh - -requireDaemonNewerThan "2.4pre20210623" - -export NIX_TESTS_CA_BY_DEFAULT=1 -cd .. -source ./recursive.sh diff --git a/tests/functional/dyn-drv/recursive-mod-json.nix b/tests/functional/dyn-drv/recursive-mod-json.nix deleted file mode 100644 index c6a24ca4f..000000000 --- a/tests/functional/dyn-drv/recursive-mod-json.nix +++ /dev/null @@ -1,33 +0,0 @@ -with import ./config.nix; - -let innerName = "foo"; in - -mkDerivation rec { - name = "${innerName}.drv"; - SHELL = shell; - - requiredSystemFeatures = [ "recursive-nix" ]; - - drv = builtins.unsafeDiscardOutputDependency (import ./text-hashed-output.nix).hello.drvPath; - - buildCommand = '' - export NIX_CONFIG='experimental-features = nix-command ca-derivations' - - PATH=${builtins.getEnv "EXTRA_PATH"}:$PATH - - # JSON of pre-existing drv - nix derivation show $drv | jq .[] > drv0.json - - # Fix name - jq < drv0.json '.name = "${innerName}"' > drv1.json - - # Extend `buildCommand` - jq < drv1.json '.env.buildCommand += "echo \"I am alive!\" >> $out/hello\n"' > drv0.json - - # Used as our output - cp $(nix derivation add < drv0.json) $out - ''; - __contentAddressed = true; - outputHashMode = "text"; - outputHashAlgo = "sha256"; -} diff --git a/tests/functional/dyn-drv/recursive-mod-json.sh b/tests/functional/dyn-drv/recursive-mod-json.sh deleted file mode 100644 index 0698b81bd..000000000 --- a/tests/functional/dyn-drv/recursive-mod-json.sh +++ /dev/null @@ -1,27 +0,0 @@ -source common.sh - -# FIXME -if [[ $(uname) != Linux ]]; then skipTest "Not running Linux"; fi - -export NIX_TESTS_CA_BY_DEFAULT=1 - -enableFeatures 'recursive-nix' -restartDaemon - -clearStore - -rm -f $TEST_ROOT/result - -EXTRA_PATH=$(dirname $(type -p nix)):$(dirname $(type -p jq)) -export EXTRA_PATH - -# Will produce a drv -metaDrv=$(nix-instantiate ./recursive-mod-json.nix) - -# computed "dynamic" derivation -drv=$(nix-store -r $metaDrv) - -# build that dyn drv -res=$(nix-store -r $drv) - -grep 'I am alive!' $res/hello diff --git a/tests/functional/meson.build b/tests/functional/meson.build index e5036e1a4..2c3f843d3 100644 --- a/tests/functional/meson.build +++ b/tests/functional/meson.build @@ -49,14 +49,12 @@ functional_tests_scripts = [ 'ca/nix-run.sh', 'ca/nix-shell.sh', 'ca/post-hook.sh', - 'ca/recursive.sh', 'ca/repl.sh', 'ca/selfref-gc.sh', 'ca/signatures.sh', 'ca/substitute.sh', 'ca/why-depends.sh', 'dyn-drv/text-hashed-output.sh', - 'dyn-drv/recursive-mod-json.sh', 'dyn-drv/build-built-drv.sh', 'dyn-drv/eval-outputOf.sh', 'dyn-drv/dep-built-drv.sh', @@ -127,7 +125,6 @@ functional_tests_scripts = [ 'flakes/search-root.sh', 'readfile-context.sh', 'nix-channel.sh', - 'recursive.sh', 'dependencies.sh', 'check-reqs.sh', 'build-remote-content-addressed-fixed.sh', diff --git a/tests/functional/recursive.nix b/tests/functional/recursive.nix deleted file mode 100644 index fa8cc04db..000000000 --- a/tests/functional/recursive.nix +++ /dev/null @@ -1,56 +0,0 @@ -with import ./config.nix; - -mkDerivation rec { - name = "recursive"; - dummy = builtins.toFile "dummy" "bla bla"; - SHELL = shell; - - # Note: this is a string without context. - unreachable = builtins.getEnv "unreachable"; - - NIX_TESTS_CA_BY_DEFAULT = builtins.getEnv "NIX_TESTS_CA_BY_DEFAULT"; - - requiredSystemFeatures = [ "recursive-nix" ]; - - buildCommand = '' - mkdir $out - opts="--experimental-features nix-command ${if (NIX_TESTS_CA_BY_DEFAULT == "1") then "--extra-experimental-features ca-derivations" else ""}" - - PATH=${builtins.getEnv "NIX_BIN_DIR"}:$PATH - - # Check that we can query/build paths in our input closure. - nix $opts path-info $dummy - nix $opts build $dummy - - # Make sure we cannot query/build paths not in out input closure. - [[ -e $unreachable ]] - (! nix $opts path-info $unreachable) - (! nix $opts build $unreachable) - - # Add something to the store. - echo foobar > foobar - foobar=$(nix $opts store add-path ./foobar) - - nix $opts path-info $foobar - nix $opts build $foobar - - # Add it to our closure. - ln -s $foobar $out/foobar - - [[ $(nix $opts path-info --all | wc -l) -eq 4 ]] - - # Build a derivation. - nix $opts build -L --impure --expr ' - with import ${./config.nix}; - mkDerivation { - name = "inner1"; - buildCommand = "echo $fnord blaat > $out"; - fnord = builtins.toFile "fnord" "fnord"; - } - ' - - [[ $(nix $opts path-info --json ./result) =~ fnord ]] - - ln -s $(nix $opts path-info ./result) $out/inner1 - ''; -} diff --git a/tests/functional/recursive.sh b/tests/functional/recursive.sh deleted file mode 100644 index 0bf00f8fa..000000000 --- a/tests/functional/recursive.sh +++ /dev/null @@ -1,19 +0,0 @@ -source common.sh - -enableFeatures 'recursive-nix' -restartDaemon - -clearStore - -rm -f $TEST_ROOT/result - -export unreachable=$(nix store add-path ./recursive.sh) - -NIX_BIN_DIR=$(dirname $(type -p nix)) nix --extra-experimental-features 'nix-command recursive-nix' build -o $TEST_ROOT/result -L --impure --file ./recursive.nix - -[[ $(cat $TEST_ROOT/result/inner1) =~ blaat ]] - -# Make sure the recursively created paths are in the closure. -nix path-info -r $TEST_ROOT/result | grep foobar -nix path-info -r $TEST_ROOT/result | grep fnord -nix path-info -r $TEST_ROOT/result | grep inner1