From 18b7363a699c0b5a4bf59d2b320dfc2b84dc4e67 Mon Sep 17 00:00:00 2001 From: Eelco Dolstra Date: Mon, 31 Oct 2016 17:09:52 +0100 Subject: [PATCH] Support optional sandbox paths For example, you can now set build-sandbox-paths = /dev/nvidiactl? to specify that /dev/nvidiactl should only be mounted in the sandbox if it exists in the host filesystem. This is useful e.g. for EC2 images that should support both CUDA and non-CUDA instances. --- doc/manual/command-ref/conf-file.xml | 7 +++++- src/libstore/build.cc | 35 +++++++++++++++++++++------- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/doc/manual/command-ref/conf-file.xml b/doc/manual/command-ref/conf-file.xml index 69295fafc..d2c9145e0 100644 --- a/doc/manual/command-ref/conf-file.xml +++ b/doc/manual/command-ref/conf-file.xml @@ -268,7 +268,12 @@ flag, e.g. --option gc-keep-outputs false. to mount a path in a different location in the sandbox; for instance, /bin=/nix-bin will mount the path /nix-bin as /bin inside the - sandbox. + sandbox. If source is followed by + ?, then it is not an error if + source does not exist; for example, + /dev/nvidiactl? specifies that + /dev/nvidiactl will only be mounted in the + sandbox if it exists in the host filesystem. Depending on how Nix was built, the default value for this option may be empty or provide /bin/sh as a diff --git a/src/libstore/build.cc b/src/libstore/build.cc index efd6c0719..b682a8019 100644 --- a/src/libstore/build.cc +++ b/src/libstore/build.cc @@ -768,7 +768,14 @@ private: GoalState state; /* Stuff we need to pass to initChild(). */ - typedef map DirsInChroot; // maps target path to source path + struct ChrootPath { + Path source; + bool optional; + ChrootPath(Path source = "", bool optional = false) + : source(source), optional(optional) + { } + }; + typedef map DirsInChroot; // maps target path to source path DirsInChroot dirsInChroot; typedef map Environment; Environment env; @@ -1865,12 +1872,18 @@ void DerivationGoal::startBuilder() dirsInChroot.clear(); - for (auto & i : dirs) { + for (auto i : dirs) { + if (i.empty()) continue; + bool optional = false; + if (i[i.size() - 1] == '?') { + optional = true; + i.pop_back(); + } size_t p = i.find('='); if (p == string::npos) - dirsInChroot[i] = i; + dirsInChroot[i] = {i, optional}; else - dirsInChroot[string(i, 0, p)] = string(i, p + 1); + dirsInChroot[string(i, 0, p)] = {string(i, p + 1), optional}; } dirsInChroot[tmpDirInSandbox] = tmpDir; @@ -1878,8 +1891,8 @@ void DerivationGoal::startBuilder() PathSet closure; for (auto & i : dirsInChroot) try { - if (worker.store.isInStore(i.second)) - worker.store.computeFSClosure(worker.store.toStorePath(i.second), closure); + if (worker.store.isInStore(i.second.source)) + worker.store.computeFSClosure(worker.store.toStorePath(i.second.source), closure); } catch (Error & e) { throw Error(format("while processing ‘build-sandbox-paths’: %s") % e.what()); } @@ -2325,12 +2338,16 @@ void DerivationGoal::runChild() environment. */ for (auto & i : dirsInChroot) { struct stat st; - Path source = i.second; + Path source = i.second.source; 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); + if (stat(source.c_str(), &st) == -1) { + if (i.second.optional && errno == ENOENT) + continue; + else + throw SysError(format("getting attributes of path ‘%1%’") % source); + } if (S_ISDIR(st.st_mode)) createDirs(target); else {