aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/libcmd/command.cc10
-rw-r--r--src/libcmd/command.hh12
-rw-r--r--src/libcmd/installables.cc58
-rw-r--r--src/libcmd/installables.hh12
-rw-r--r--src/libcmd/nix-cmd.pc.in2
-rw-r--r--src/libexpr/flake/lockfile.cc2
-rw-r--r--src/libexpr/nix-expr.pc.in2
-rw-r--r--src/libexpr/primops/fetchTree.cc11
-rw-r--r--src/libexpr/tests/error_traces.cc2
-rw-r--r--src/libexpr/tests/json.cc2
-rw-r--r--src/libexpr/tests/libexpr.hh (renamed from src/libexpr/tests/libexprtests.hh)8
-rw-r--r--src/libexpr/tests/local.mk4
-rw-r--r--src/libexpr/tests/primops.cc2
-rw-r--r--src/libexpr/tests/trivial.cc2
-rw-r--r--src/libexpr/tests/value/context.cc59
-rw-r--r--src/libexpr/tests/value/context.hh30
-rw-r--r--src/libexpr/value/context.hh17
-rw-r--r--src/libfetchers/git.cc41
-rw-r--r--src/libmain/nix-main.pc.in2
-rw-r--r--src/libstore/binary-cache-store.cc2
-rw-r--r--src/libstore/build/local-derivation-goal.cc85
-rw-r--r--src/libstore/build/worker.cc2
-rw-r--r--src/libstore/daemon.cc24
-rw-r--r--src/libstore/daemon.hh7
-rw-r--r--src/libstore/derived-path.hh11
-rw-r--r--src/libstore/filetransfer.cc1
-rw-r--r--src/libstore/globals.cc6
-rw-r--r--src/libstore/globals.hh34
-rw-r--r--src/libstore/legacy-ssh-store.cc1
-rw-r--r--src/libstore/nix-store.pc.in2
-rw-r--r--src/libstore/path-info.hh6
-rw-r--r--src/libstore/path.hh5
-rw-r--r--src/libstore/realisation.hh3
-rw-r--r--src/libstore/remote-store.cc1
-rw-r--r--src/libstore/store-api.cc2
-rw-r--r--src/libstore/store-api.hh2
-rw-r--r--src/libstore/tests/derived-path.cc62
-rw-r--r--src/libstore/tests/derived-path.hh28
-rw-r--r--src/libstore/tests/libstore.hh (renamed from src/libstore/tests/libstoretests.hh)0
-rw-r--r--src/libstore/tests/local.mk20
-rw-r--r--src/libstore/tests/outputs-spec.cc32
-rw-r--r--src/libstore/tests/outputs-spec.hh17
-rw-r--r--src/libstore/tests/path.cc31
-rw-r--r--src/libstore/tests/path.hh28
-rw-r--r--src/libutil/args.cc2
-rw-r--r--src/libutil/config.cc8
-rw-r--r--src/libutil/config.hh12
-rw-r--r--src/libutil/namespaces.cc100
-rw-r--r--src/libutil/namespaces.hh15
-rw-r--r--src/libutil/tests/hash.cc22
-rw-r--r--src/libutil/tests/hash.hh15
-rw-r--r--src/libutil/tests/local.mk18
-rw-r--r--src/nix-build/nix-build.cc4
-rw-r--r--src/nix/daemon.cc52
-rw-r--r--src/nix/diff-closures.cc2
-rw-r--r--src/nix/flake.cc62
-rw-r--r--src/nix/ping-store.cc24
-rw-r--r--src/nix/search.cc4
-rw-r--r--src/nix/why-depends.cc2
59 files changed, 772 insertions, 260 deletions
diff --git a/src/libcmd/command.cc b/src/libcmd/command.cc
index 0740ea960..517cdf617 100644
--- a/src/libcmd/command.cc
+++ b/src/libcmd/command.cc
@@ -127,6 +127,16 @@ ref<EvalState> EvalCommand::getEvalState()
return ref<EvalState>(evalState);
}
+MixOperateOnOptions::MixOperateOnOptions()
+{
+ addFlag({
+ .longName = "derivation",
+ .description = "Operate on the [store derivation](../../glossary.md#gloss-store-derivation) rather than its outputs.",
+ .category = installablesCategory,
+ .handler = {&operateOn, OperateOn::Derivation},
+ });
+}
+
BuiltPathsCommand::BuiltPathsCommand(bool recursive)
: recursive(recursive)
{
diff --git a/src/libcmd/command.hh b/src/libcmd/command.hh
index 3b4b40981..d16bdbc4b 100644
--- a/src/libcmd/command.hh
+++ b/src/libcmd/command.hh
@@ -96,9 +96,6 @@ struct SourceExprCommand : virtual Args, MixFlakeOptions
std::optional<std::string> expr;
bool readOnlyMode = false;
- // FIXME: move this; not all commands (e.g. 'nix run') use it.
- OperateOn operateOn = OperateOn::Output;
-
SourceExprCommand(bool supportReadOnlyMode = false);
std::vector<std::shared_ptr<Installable>> parseInstallables(
@@ -153,8 +150,15 @@ private:
std::string _installable{"."};
};
+struct MixOperateOnOptions : virtual Args
+{
+ OperateOn operateOn = OperateOn::Output;
+
+ MixOperateOnOptions();
+};
+
/* A command that operates on zero or more store paths. */
-struct BuiltPathsCommand : public InstallablesCommand
+struct BuiltPathsCommand : InstallablesCommand, virtual MixOperateOnOptions
{
private:
diff --git a/src/libcmd/installables.cc b/src/libcmd/installables.cc
index 5090ea6d2..ff8261b09 100644
--- a/src/libcmd/installables.cc
+++ b/src/libcmd/installables.cc
@@ -167,13 +167,6 @@ SourceExprCommand::SourceExprCommand(bool supportReadOnlyMode)
.handler = {&expr}
});
- addFlag({
- .longName = "derivation",
- .description = "Operate on the [store derivation](../../glossary.md#gloss-store-derivation) rather than its outputs.",
- .category = installablesCategory,
- .handler = {&operateOn, OperateOn::Derivation},
- });
-
if (supportReadOnlyMode) {
addFlag({
.longName = "read-only",
@@ -379,10 +372,9 @@ Installable::getCursors(EvalState & state)
ref<eval_cache::AttrCursor>
Installable::getCursor(EvalState & state)
{
- auto cursors = getCursors(state);
- if (cursors.empty())
- throw Error("cannot find flake attribute '%s'", what());
- return cursors[0];
+ /* Although getCursors should return at least one element, in case it doesn't,
+ bound check to avoid an undefined behavior for vector[0] */
+ return getCursors(state).at(0);
}
static StorePath getDeriver(
@@ -696,46 +688,28 @@ InstallableFlake::getCursors(EvalState & state)
std::vector<ref<eval_cache::AttrCursor>> res;
- for (auto & attrPath : getActualAttrPaths()) {
- auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath));
- if (attr) res.push_back(ref(*attr));
- }
-
- return res;
-}
-
-ref<eval_cache::AttrCursor> InstallableFlake::getCursor(EvalState & state)
-{
- auto lockedFlake = getLockedFlake();
-
- auto cache = openEvalCache(state, lockedFlake);
- auto root = cache->getRoot();
-
Suggestions suggestions;
-
auto attrPaths = getActualAttrPaths();
for (auto & attrPath : attrPaths) {
debug("trying flake output attribute '%s'", attrPath);
- auto attrOrSuggestions = root->findAlongAttrPath(
- parseAttrPath(state, attrPath),
- true
- );
-
- if (!attrOrSuggestions) {
- suggestions += attrOrSuggestions.getSuggestions();
- continue;
+ auto attr = root->findAlongAttrPath(parseAttrPath(state, attrPath));
+ if (attr) {
+ res.push_back(ref(*attr));
+ } else {
+ suggestions += attr.getSuggestions();
}
-
- return *attrOrSuggestions;
}
- throw Error(
- suggestions,
- "flake '%s' does not provide attribute %s",
- flakeRef,
- showAttrPaths(attrPaths));
+ if (res.size() == 0)
+ throw Error(
+ suggestions,
+ "flake '%s' does not provide attribute %s",
+ flakeRef,
+ showAttrPaths(attrPaths));
+
+ return res;
}
std::shared_ptr<flake::LockedFlake> InstallableFlake::getLockedFlake() const
diff --git a/src/libcmd/installables.hh b/src/libcmd/installables.hh
index 3d12639b0..da6a3addd 100644
--- a/src/libcmd/installables.hh
+++ b/src/libcmd/installables.hh
@@ -103,9 +103,13 @@ struct Installable
return {};
}
+ /* Get a cursor to each value this Installable could refer to. However
+ if none exists, throw exception instead of returning empty vector. */
virtual std::vector<ref<eval_cache::AttrCursor>>
getCursors(EvalState & state);
+ /* Get the first and most preferred cursor this Installable could refer
+ to, or throw an exception if none exists. */
virtual ref<eval_cache::AttrCursor>
getCursor(EvalState & state);
@@ -193,15 +197,11 @@ struct InstallableFlake : InstallableValue
std::pair<Value *, PosIdx> toValue(EvalState & state) override;
- /* Get a cursor to every attrpath in getActualAttrPaths() that
- exists. */
+ /* Get a cursor to every attrpath in getActualAttrPaths()
+ that exists. However if none exists, throw an exception. */
std::vector<ref<eval_cache::AttrCursor>>
getCursors(EvalState & state) override;
- /* Get a cursor to the first attrpath in getActualAttrPaths() that
- exists, or throw an exception with suggestions if none exists. */
- ref<eval_cache::AttrCursor> getCursor(EvalState & state) override;
-
std::shared_ptr<flake::LockedFlake> getLockedFlake() const;
FlakeRef nixpkgsFlakeRef() const override;
diff --git a/src/libcmd/nix-cmd.pc.in b/src/libcmd/nix-cmd.pc.in
index 1761a9f41..a21d93f1d 100644
--- a/src/libcmd/nix-cmd.pc.in
+++ b/src/libcmd/nix-cmd.pc.in
@@ -6,4 +6,4 @@ Name: Nix
Description: Nix Package Manager
Version: @PACKAGE_VERSION@
Libs: -L${libdir} -lnixcmd
-Cflags: -I${includedir}/nix -std=c++17
+Cflags: -I${includedir}/nix -std=c++20
diff --git a/src/libexpr/flake/lockfile.cc b/src/libexpr/flake/lockfile.cc
index a3ed90e1f..a74e68c9c 100644
--- a/src/libexpr/flake/lockfile.cc
+++ b/src/libexpr/flake/lockfile.cc
@@ -219,7 +219,7 @@ std::optional<FlakeRef> LockFile::isUnlocked() const
visit(root);
for (auto & i : nodes) {
- if (i == root) continue;
+ if (i == ref<const Node>(root)) continue;
auto node = i.dynamic_pointer_cast<const LockedNode>();
if (node && !node->lockedRef.input.isLocked())
return node->lockedRef;
diff --git a/src/libexpr/nix-expr.pc.in b/src/libexpr/nix-expr.pc.in
index 80f7a492b..95d452ca8 100644
--- a/src/libexpr/nix-expr.pc.in
+++ b/src/libexpr/nix-expr.pc.in
@@ -7,4 +7,4 @@ Description: Nix Package Manager
Version: @PACKAGE_VERSION@
Requires: nix-store bdw-gc
Libs: -L${libdir} -lnixexpr
-Cflags: -I${includedir}/nix -std=c++17
+Cflags: -I${includedir}/nix -std=c++20
diff --git a/src/libexpr/primops/fetchTree.cc b/src/libexpr/primops/fetchTree.cc
index 83d93b75c..2e924c302 100644
--- a/src/libexpr/primops/fetchTree.cc
+++ b/src/libexpr/primops/fetchTree.cc
@@ -456,6 +456,17 @@ static RegisterPrimOp primop_fetchGit({
> **Note**
>
> This behavior is disabled in *Pure evaluation mode*.
+
+ - To fetch the content of a checked-out work directory:
+
+ ```nix
+ builtins.fetchGit ./work-dir
+ ```
+
+ If the URL points to a local directory, and no `ref` or `rev` is
+ given, `fetchGit` will use the current content of the checked-out
+ files, even if they are not committed or added to Git's index. It will
+ only consider files added to the Git repository, as listed by `git ls-files`.
)",
.fun = prim_fetchGit,
});
diff --git a/src/libexpr/tests/error_traces.cc b/src/libexpr/tests/error_traces.cc
index 5e2213f69..24e95ac39 100644
--- a/src/libexpr/tests/error_traces.cc
+++ b/src/libexpr/tests/error_traces.cc
@@ -1,7 +1,7 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
-#include "libexprtests.hh"
+#include "tests/libexpr.hh"
namespace nix {
diff --git a/src/libexpr/tests/json.cc b/src/libexpr/tests/json.cc
index f1ea1b197..411bc0ac3 100644
--- a/src/libexpr/tests/json.cc
+++ b/src/libexpr/tests/json.cc
@@ -1,4 +1,4 @@
-#include "libexprtests.hh"
+#include "tests/libexpr.hh"
#include "value-to-json.hh"
namespace nix {
diff --git a/src/libexpr/tests/libexprtests.hh b/src/libexpr/tests/libexpr.hh
index 03e468fbb..8534d9567 100644
--- a/src/libexpr/tests/libexprtests.hh
+++ b/src/libexpr/tests/libexpr.hh
@@ -7,18 +7,19 @@
#include "eval-inline.hh"
#include "store-api.hh"
+#include "tests/libstore.hh"
namespace nix {
- class LibExprTest : public ::testing::Test {
+ class LibExprTest : public LibStoreTest {
public:
static void SetUpTestSuite() {
- initLibStore();
+ LibStoreTest::SetUpTestSuite();
initGC();
}
protected:
LibExprTest()
- : store(openStore("dummy://"))
+ : LibStoreTest()
, state({}, store)
{
}
@@ -36,7 +37,6 @@ namespace nix {
return state.symbols.create(value);
}
- ref<Store> store;
EvalState state;
};
diff --git a/src/libexpr/tests/local.mk b/src/libexpr/tests/local.mk
index e483575a4..3e5504f71 100644
--- a/src/libexpr/tests/local.mk
+++ b/src/libexpr/tests/local.mk
@@ -2,6 +2,8 @@ check: libexpr-tests_RUN
programs += libexpr-tests
+libexpr-tests_NAME := libnixexpr-tests
+
libexpr-tests_DIR := $(d)
libexpr-tests_INSTALL_DIR :=
@@ -12,6 +14,6 @@ libexpr-tests_SOURCES := \
libexpr-tests_CXXFLAGS += -I src/libexpr -I src/libutil -I src/libstore -I src/libexpr/tests
-libexpr-tests_LIBS = libexpr libutil libstore libfetchers
+libexpr-tests_LIBS = libstore-tests libutils-tests libexpr libutil libstore libfetchers
libexpr-tests_LDFLAGS := $(GTEST_LIBS) -lgmock
diff --git a/src/libexpr/tests/primops.cc b/src/libexpr/tests/primops.cc
index 9cdcf64a1..e1d3ac503 100644
--- a/src/libexpr/tests/primops.cc
+++ b/src/libexpr/tests/primops.cc
@@ -1,7 +1,7 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>
-#include "libexprtests.hh"
+#include "tests/libexpr.hh"
namespace nix {
class CaptureLogger : public Logger
diff --git a/src/libexpr/tests/trivial.cc b/src/libexpr/tests/trivial.cc
index 8ce276e52..171727ac7 100644
--- a/src/libexpr/tests/trivial.cc
+++ b/src/libexpr/tests/trivial.cc
@@ -1,4 +1,4 @@
-#include "libexprtests.hh"
+#include "tests/libexpr.hh"
namespace nix {
// Testing of trivial expressions
diff --git a/src/libexpr/tests/value/context.cc b/src/libexpr/tests/value/context.cc
index d5c9d3bce..083359b7a 100644
--- a/src/libexpr/tests/value/context.cc
+++ b/src/libexpr/tests/value/context.cc
@@ -1,6 +1,10 @@
-#include "value/context.hh"
+#include <nlohmann/json.hpp>
+#include <gtest/gtest.h>
+#include <rapidcheck/gtest.h>
-#include "libexprtests.hh"
+#include "tests/path.hh"
+#include "tests/libexpr.hh"
+#include "tests/value/context.hh"
namespace nix {
@@ -70,3 +74,54 @@ TEST_F(NixStringContextElemTest, built) {
}
}
+
+namespace rc {
+using namespace nix;
+
+Gen<NixStringContextElem::Opaque> Arbitrary<NixStringContextElem::Opaque>::arbitrary()
+{
+ return gen::just(NixStringContextElem::Opaque {
+ .path = *gen::arbitrary<StorePath>(),
+ });
+}
+
+Gen<NixStringContextElem::DrvDeep> Arbitrary<NixStringContextElem::DrvDeep>::arbitrary()
+{
+ return gen::just(NixStringContextElem::DrvDeep {
+ .drvPath = *gen::arbitrary<StorePath>(),
+ });
+}
+
+Gen<NixStringContextElem::Built> Arbitrary<NixStringContextElem::Built>::arbitrary()
+{
+ return gen::just(NixStringContextElem::Built {
+ .drvPath = *gen::arbitrary<StorePath>(),
+ .output = (*gen::arbitrary<StorePathName>()).name,
+ });
+}
+
+Gen<NixStringContextElem> Arbitrary<NixStringContextElem>::arbitrary()
+{
+ switch (*gen::inRange<uint8_t>(0, 2)) {
+ case 0:
+ return gen::just<NixStringContextElem>(*gen::arbitrary<NixStringContextElem::Opaque>());
+ case 1:
+ return gen::just<NixStringContextElem>(*gen::arbitrary<NixStringContextElem::DrvDeep>());
+ default:
+ return gen::just<NixStringContextElem>(*gen::arbitrary<NixStringContextElem::Built>());
+ }
+}
+
+}
+
+namespace nix {
+
+RC_GTEST_FIXTURE_PROP(
+ NixStringContextElemTest,
+ prop_round_rip,
+ (const NixStringContextElem & o))
+{
+ RC_ASSERT(o == NixStringContextElem::parse(store(), o.to_string(store())));
+}
+
+}
diff --git a/src/libexpr/tests/value/context.hh b/src/libexpr/tests/value/context.hh
new file mode 100644
index 000000000..54d21760e
--- /dev/null
+++ b/src/libexpr/tests/value/context.hh
@@ -0,0 +1,30 @@
+#pragma once
+
+#include <rapidcheck/gen/Arbitrary.h>
+
+#include <value/context.hh>
+
+namespace rc {
+using namespace nix;
+
+template<>
+struct Arbitrary<NixStringContextElem::Opaque> {
+ static Gen<NixStringContextElem::Opaque> arbitrary();
+};
+
+template<>
+struct Arbitrary<NixStringContextElem::Built> {
+ static Gen<NixStringContextElem::Built> arbitrary();
+};
+
+template<>
+struct Arbitrary<NixStringContextElem::DrvDeep> {
+ static Gen<NixStringContextElem::DrvDeep> arbitrary();
+};
+
+template<>
+struct Arbitrary<NixStringContextElem> {
+ static Gen<NixStringContextElem> arbitrary();
+};
+
+}
diff --git a/src/libexpr/value/context.hh b/src/libexpr/value/context.hh
index d8008c436..721563cba 100644
--- a/src/libexpr/value/context.hh
+++ b/src/libexpr/value/context.hh
@@ -1,9 +1,10 @@
#pragma once
#include "util.hh"
+#include "comparator.hh"
#include "path.hh"
-#include <optional>
+#include <variant>
#include <nlohmann/json_fwd.hpp>
@@ -31,7 +32,9 @@ class Store;
Encoded as just the path: ‘<path>’.
*/
struct NixStringContextElem_Opaque {
- StorePath path;
+ StorePath path;
+
+ GENERATE_CMP(NixStringContextElem_Opaque, me->path);
};
/* Path to a derivation and its entire build closure.
@@ -43,7 +46,9 @@ struct NixStringContextElem_Opaque {
Encoded in the form ‘=<drvPath>’.
*/
struct NixStringContextElem_DrvDeep {
- StorePath drvPath;
+ StorePath drvPath;
+
+ GENERATE_CMP(NixStringContextElem_DrvDeep, me->drvPath);
};
/* Derivation output.
@@ -51,8 +56,10 @@ struct NixStringContextElem_DrvDeep {
Encoded in the form ‘!<output>!<drvPath>’.
*/
struct NixStringContextElem_Built {
- StorePath drvPath;
- std::string output;
+ StorePath drvPath;
+ std::string output;
+
+ GENERATE_CMP(NixStringContextElem_Built, me->drvPath, me->output);
};
using _NixStringContextElem_Raw = std::variant<
diff --git a/src/libfetchers/git.cc b/src/libfetchers/git.cc
index 1f7d7c07d..309a143f5 100644
--- a/src/libfetchers/git.cc
+++ b/src/libfetchers/git.cc
@@ -615,15 +615,42 @@ struct GitInputScheme : InputScheme
AutoDelete delTmpGitDir(tmpGitDir, true);
runProgram("git", true, { "-c", "init.defaultBranch=" + gitInitialBranch, "init", tmpDir, "--separate-git-dir", tmpGitDir });
- // TODO: repoDir might lack the ref (it only checks if rev
- // exists, see FIXME above) so use a big hammer and fetch
- // everything to ensure we get the rev.
- runProgram("git", true, { "-C", tmpDir, "fetch", "--quiet", "--force",
- "--update-head-ok", "--", repoDir, "refs/*:refs/*" });
+
+ {
+ // TODO: repoDir might lack the ref (it only checks if rev
+ // exists, see FIXME above) so use a big hammer and fetch
+ // everything to ensure we get the rev.
+ Activity act(*logger, lvlTalkative, actUnknown, fmt("making temporary clone of '%s'", repoDir));
+ runProgram("git", true, { "-C", tmpDir, "fetch", "--quiet", "--force",
+ "--update-head-ok", "--", repoDir, "refs/*:refs/*" });
+ }
runProgram("git", true, { "-C", tmpDir, "checkout", "--quiet", input.getRev()->gitRev() });
- runProgram("git", true, { "-C", tmpDir, "remote", "add", "origin", actualUrl });
- runProgram("git", true, { "-C", tmpDir, "submodule", "--quiet", "update", "--init", "--recursive" });
+
+ /* Ensure that we use the correct origin for fetching
+ submodules. This matters for submodules with relative
+ URLs. */
+ if (isLocal) {
+ writeFile(tmpGitDir + "/config", readFile(repoDir + "/" + gitDir + "/config"));
+
+ /* Restore the config.bare setting we may have just
+ copied erroneously from the user's repo. */
+ runProgram("git", true, { "-C", tmpDir, "config", "core.bare", "false" });
+ } else
+ runProgram("git", true, { "-C", tmpDir, "config", "remote.origin.url", actualUrl });
+
+ /* As an optimisation, copy the modules directory of the
+ source repo if it exists. */
+ auto modulesPath = repoDir + "/" + gitDir + "/modules";
+ if (pathExists(modulesPath)) {
+ Activity act(*logger, lvlTalkative, actUnknown, fmt("copying submodules of '%s'", actualUrl));
+ runProgram("cp", true, { "-R", "--", modulesPath, tmpGitDir + "/modules" });
+ }
+
+ {
+ Activity act(*logger, lvlTalkative, actUnknown, fmt("fetching submodules of '%s'", actualUrl));
+ runProgram("git", true, { "-C", tmpDir, "submodule", "--quiet", "update", "--init", "--recursive" });
+ }
filter = isNotDotGitDirectory;
} else {
diff --git a/src/libmain/nix-main.pc.in b/src/libmain/nix-main.pc.in
index 37b03dcd4..b46ce1990 100644
--- a/src/libmain/nix-main.pc.in
+++ b/src/libmain/nix-main.pc.in
@@ -6,4 +6,4 @@ Name: Nix
Description: Nix Package Manager
Version: @PACKAGE_VERSION@
Libs: -L${libdir} -lnixmain
-Cflags: -I${includedir}/nix -std=c++17
+Cflags: -I${includedir}/nix -std=c++20
diff --git a/src/libstore/binary-cache-store.cc b/src/libstore/binary-cache-store.cc
index 05bf0501d..751cf8c30 100644
--- a/src/libstore/binary-cache-store.cc
+++ b/src/libstore/binary-cache-store.cc
@@ -370,7 +370,7 @@ void BinaryCacheStore::queryPathInfoUncached(const StorePath & storePath,
auto callbackPtr = std::make_shared<decltype(callback)>(std::move(callback));
getFile(narInfoFile,
- {[=](std::future<std::optional<std::string>> fut) {
+ {[=,this](std::future<std::optional<std::string>> fut) {
try {
auto data = fut.get();
diff --git a/src/libstore/build/local-derivation-goal.cc b/src/libstore/build/local-derivation-goal.cc
index 572f71045..e1cc504f8 100644
--- a/src/libstore/build/local-derivation-goal.cc
+++ b/src/libstore/build/local-derivation-goal.cc
@@ -16,6 +16,7 @@
#include "json-utils.hh"
#include "cgroup.hh"
#include "personality.hh"
+#include "namespaces.hh"
#include <regex>
#include <queue>
@@ -167,7 +168,8 @@ void LocalDerivationGoal::killSandbox(bool getStats)
}
-void LocalDerivationGoal::tryLocalBuild() {
+void LocalDerivationGoal::tryLocalBuild()
+{
unsigned int curBuilds = worker.getNrLocalBuilds();
if (curBuilds >= settings.maxBuildJobs) {
state = &DerivationGoal::tryToBuild;
@@ -205,6 +207,17 @@ void LocalDerivationGoal::tryLocalBuild() {
#endif
}
+ #if __linux__
+ if (useChroot) {
+ if (!mountNamespacesSupported() || !pidNamespacesSupported()) {
+ if (!settings.sandboxFallback)
+ throw Error("this system does not support the kernel namespaces that are required for sandboxing; use '--no-sandbox' to disable sandboxing");
+ debug("auto-disabling sandboxing because the prerequisite namespaces are not available");
+ useChroot = false;
+ }
+ }
+ #endif
+
if (useBuildUsers()) {
if (!buildUser)
buildUser = acquireUserLock(parsedDrv->useUidRange() ? 65536 : 1, useChroot);
@@ -888,12 +901,7 @@ void LocalDerivationGoal::startBuilder()
userNamespaceSync.create();
- Path maxUserNamespaces = "/proc/sys/user/max_user_namespaces";
- static bool userNamespacesEnabled =
- pathExists(maxUserNamespaces)
- && trim(readFile(maxUserNamespaces)) != "0";
-
- usingUserNamespace = userNamespacesEnabled;
+ usingUserNamespace = userNamespacesSupported();
Pid helper = startProcess([&]() {
@@ -920,64 +928,15 @@ void LocalDerivationGoal::startBuilder()
flags |= CLONE_NEWUSER;
pid_t child = clone(childEntry, stack + stackSize, flags, this);
- if (child == -1 && errno == EINVAL) {
- /* Fallback for Linux < 2.13 where CLONE_NEWPID and
- CLONE_PARENT are not allowed together. */
- flags &= ~CLONE_NEWPID;
- child = clone(childEntry, stack + stackSize, flags, this);
- }
- if (usingUserNamespace && child == -1 && (errno == EPERM || errno == EINVAL)) {
- /* Some distros patch Linux to not allow unprivileged
- * user namespaces. If we get EPERM or EINVAL, try
- * without CLONE_NEWUSER and see if that works.
- * Details: https://salsa.debian.org/kernel-team/linux/-/commit/d98e00eda6bea437e39b9e80444eee84a32438a6
- */
- usingUserNamespace = false;
- flags &= ~CLONE_NEWUSER;
- child = clone(childEntry, stack + stackSize, flags, this);
- }
- if (child == -1) {
- switch(errno) {
- case EPERM:
- case EINVAL: {
- int errno_ = errno;
- if (!userNamespacesEnabled && errno==EPERM)
- notice("user namespaces appear to be disabled; they are required for sandboxing; check /proc/sys/user/max_user_namespaces");
- if (userNamespacesEnabled) {
- Path procSysKernelUnprivilegedUsernsClone = "/proc/sys/kernel/unprivileged_userns_clone";
- if (pathExists(procSysKernelUnprivilegedUsernsClone)
- && trim(readFile(procSysKernelUnprivilegedUsernsClone)) == "0") {
- notice("user namespaces appear to be disabled; they are required for sandboxing; check /proc/sys/kernel/unprivileged_userns_clone");
- }
- }
- Path procSelfNsUser = "/proc/self/ns/user";
- if (!pathExists(procSelfNsUser))
- notice("/proc/self/ns/user does not exist; your kernel was likely built without CONFIG_USER_NS=y, which is required for sandboxing");
- /* Otherwise exit with EPERM so we can handle this in the
- parent. This is only done when sandbox-fallback is set
- to true (the default). */
- if (settings.sandboxFallback)
- _exit(1);
- /* Mention sandbox-fallback in the error message so the user
- knows that having it disabled contributed to the
- unrecoverability of this failure */
- throw SysError(errno_, "creating sandboxed builder process using clone(), without sandbox-fallback");
- }
- default:
- throw SysError("creating sandboxed builder process using clone()");
- }
- }
+
+ if (child == -1)
+ throw SysError("creating sandboxed builder process using clone()");
writeFull(builderOut.writeSide.get(),
fmt("%d %d\n", usingUserNamespace, child));
_exit(0);
});
- int res = helper.wait();
- if (res != 0 && settings.sandboxFallback) {
- useChroot = false;
- initTmpDir();
- goto fallback;
- } else if (res != 0)
+ if (helper.wait() != 0)
throw Error("unable to start build process");
userNamespaceSync.readSide = -1;
@@ -1045,9 +1004,6 @@ void LocalDerivationGoal::startBuilder()
} else
#endif
{
-#if __linux__
- fallback:
-#endif
pid = startProcess([&]() {
runChild();
});
@@ -1516,8 +1472,7 @@ void LocalDerivationGoal::startDaemon()
FdSink to(remote.get());
try {
daemon::processConnection(store, from, to,
- daemon::NotTrusted, daemon::Recursive,
- [&](Store & store) {});
+ daemon::NotTrusted, daemon::Recursive);
debug("terminated daemon connection");
} catch (SysError &) {
ignoreException();
diff --git a/src/libstore/build/worker.cc b/src/libstore/build/worker.cc
index b94fb8416..f775f8486 100644
--- a/src/libstore/build/worker.cc
+++ b/src/libstore/build/worker.cc
@@ -276,7 +276,7 @@ void Worker::run(const Goals & _topGoals)
if (!children.empty() || !waitingForAWhile.empty())
waitForInput();
else {
- if (awake.empty() && 0 == settings.maxBuildJobs)
+ if (awake.empty() && 0U == settings.maxBuildJobs)
{
if (getMachines().empty())
throw Error("unable to start any build; either increase '--max-jobs' "
diff --git a/src/libstore/daemon.cc b/src/libstore/daemon.cc
index e2a7dab35..5e6fd011f 100644
--- a/src/libstore/daemon.cc
+++ b/src/libstore/daemon.cc
@@ -236,6 +236,10 @@ struct ClientSettings
// the daemon, as that could cause some pretty weird stuff
if (parseFeatures(tokenizeString<StringSet>(value)) != settings.experimentalFeatures.get())
debug("Ignoring the client-specified experimental features");
+ } else if (name == settings.pluginFiles.name) {
+ if (tokenizeString<Paths>(value) != settings.pluginFiles.get())
+ warn("Ignoring the client-specified plugin-files.\n"
+ "The client specifying plugins to the daemon never made sense, and was removed in Nix >=2.14.");
}
else if (trusted
|| name == settings.buildTimeout.name
@@ -525,7 +529,14 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
mode = (BuildMode) readInt(from);
/* Repairing is not atomic, so disallowed for "untrusted"
- clients. */
+ clients.
+
+ FIXME: layer violation in this message: the daemon code (i.e.
+ this file) knows whether a client/connection is trusted, but it
+ does not how how the client was authenticated. The mechanism
+ need not be getting the UID of the other end of a Unix Domain
+ Socket.
+ */
if (mode == bmRepair && !trusted)
throw Error("repairing is not allowed because you are not in 'trusted-users'");
}
@@ -542,7 +553,9 @@ static void performOp(TunnelLogger * logger, ref<Store> store,
mode = (BuildMode) readInt(from);
/* Repairing is not atomic, so disallowed for "untrusted"
- clients. */
+ clients.
+
+ FIXME: layer violation; see above. */
if (mode == bmRepair && !trusted)
throw Error("repairing is not allowed because you are not in 'trusted-users'");
@@ -981,8 +994,7 @@ void processConnection(
FdSource & from,
FdSink & to,
TrustedFlag trusted,
- RecursiveFlag recursive,
- std::function<void(Store &)> authHook)
+ RecursiveFlag recursive)
{
auto monitor = !recursive ? std::make_unique<MonitorFdHup>(from.fd) : nullptr;
@@ -1025,10 +1037,6 @@ void processConnection(
try {
- /* If we can't accept clientVersion, then throw an error
- *here* (not above). */
- authHook(*store);
-
tunnelLogger->stopWork();
to.flush();
diff --git a/src/libstore/daemon.hh b/src/libstore/daemon.hh
index 67755d54e..8c765615c 100644
--- a/src/libstore/daemon.hh
+++ b/src/libstore/daemon.hh
@@ -13,11 +13,6 @@ void processConnection(
FdSource & from,
FdSink & to,
TrustedFlag trusted,
- RecursiveFlag recursive,
- /* Arbitrary hook to check authorization / initialize user data / whatever
- after the protocol has been negotiated. The idea is that this function
- and everything it calls doesn't know about this stuff, and the
- `nix-daemon` handles that instead. */
- std::function<void(Store &)> authHook);
+ RecursiveFlag recursive);
}
diff --git a/src/libstore/derived-path.hh b/src/libstore/derived-path.hh
index 4edff7467..9e0cce377 100644
--- a/src/libstore/derived-path.hh
+++ b/src/libstore/derived-path.hh
@@ -4,8 +4,9 @@
#include "path.hh"
#include "realisation.hh"
#include "outputs-spec.hh"
+#include "comparator.hh"
-#include <optional>
+#include <variant>
#include <nlohmann/json_fwd.hpp>
@@ -27,8 +28,7 @@ struct DerivedPathOpaque {
std::string to_string(const Store & store) const;
static DerivedPathOpaque parse(const Store & store, std::string_view);
- bool operator < (const DerivedPathOpaque & b) const
- { return path < b.path; }
+ GENERATE_CMP(DerivedPathOpaque, me->path);
};
/**
@@ -51,8 +51,7 @@ struct DerivedPathBuilt {
static DerivedPathBuilt parse(const Store & store, std::string_view, std::string_view);
nlohmann::json toJSON(ref<Store> store) const;
- bool operator < (const DerivedPathBuilt & b) const
- { return std::make_pair(drvPath, outputs) < std::make_pair(b.drvPath, b.outputs); }
+ GENERATE_CMP(DerivedPathBuilt, me->drvPath, me->outputs);
};
using _DerivedPathRaw = std::variant<
@@ -96,6 +95,8 @@ struct BuiltPathBuilt {
nlohmann::json toJSON(ref<Store> store) const;
static BuiltPathBuilt parse(const Store & store, std::string_view);
+
+ GENERATE_CMP(BuiltPathBuilt, me->drvPath, me->outputs);
};
using _BuiltPathRaw = std::variant<
diff --git a/src/libstore/filetransfer.cc b/src/libstore/filetransfer.cc
index 756bd4423..b25089ec3 100644
--- a/src/libstore/filetransfer.cc
+++ b/src/libstore/filetransfer.cc
@@ -101,6 +101,7 @@ struct curlFileTransfer : public FileTransfer
this->result.data.append(data);
})
{
+ requestHeaders = curl_slist_append(requestHeaders, "Accept-Encoding: zstd, br, gzip, deflate, bzip2, xz");
if (!request.expectedETag.empty())
requestHeaders = curl_slist_append(requestHeaders, ("If-None-Match: " + request.expectedETag).c_str());
if (!request.mimeType.empty())
diff --git a/src/libstore/globals.cc b/src/libstore/globals.cc
index 130c5b670..8e33a3dec 100644
--- a/src/libstore/globals.cc
+++ b/src/libstore/globals.cc
@@ -222,19 +222,19 @@ template<> void BaseSetting<SandboxMode>::convertToArg(Args & args, const std::s
.longName = name,
.description = "Enable sandboxing.",
.category = category,
- .handler = {[=]() { override(smEnabled); }}
+ .handler = {[this]() { override(smEnabled); }}
});
args.addFlag({
.longName = "no-" + name,
.description = "Disable sandboxing.",
.category = category,
- .handler = {[=]() { override(smDisabled); }}
+ .handler = {[this]() { override(smDisabled); }}
});
args.addFlag({
.longName = "relaxed-" + name,
.description = "Enable sandboxing, but allow builds to disable it.",
.category = category,
- .handler = {[=]() { override(smRelaxed); }}
+ .handler = {[this]() { override(smRelaxed); }}
});
}
diff --git a/src/libstore/globals.hh b/src/libstore/globals.hh
index 40f10fde3..0a4912f67 100644
--- a/src/libstore/globals.hh
+++ b/src/libstore/globals.hh
@@ -279,8 +279,8 @@ public:
If the build users group is empty, builds will be performed under
the uid of the Nix process (that is, the uid of the caller if
`NIX_REMOTE` is empty, the uid under which the Nix daemon runs if
- `NIX_REMOTE` is `daemon`). Obviously, this should not be used in
- multi-user settings with untrusted users.
+ `NIX_REMOTE` is `daemon`). Obviously, this should not be used
+ with a nix daemon accessible to untrusted clients.
Defaults to `nixbld` when running as root, *empty* otherwise.
)",
@@ -696,24 +696,6 @@ public:
)",
{"trusted-binary-caches"}};
- Setting<Strings> trustedUsers{
- this, {"root"}, "trusted-users",
- R"(
- A list of names of users (separated by whitespace) that have
- additional rights when connecting to the Nix daemon, such as the
- ability to specify additional binary caches, or to import unsigned
- NARs. You can also specify groups by prefixing them with `@`; for
- instance, `@wheel` means all users in the `wheel` group. The default
- is `root`.
-
- > **Warning**
- >
- > Adding a user to `trusted-users` is essentially equivalent to
- > giving that user root access to the system. For example, the user
- > can set `sandbox-paths` and thereby obtain read access to
- > directories that are otherwise inacessible to them.
- )"};
-
Setting<unsigned int> ttlNegativeNarInfoCache{
this, 3600, "narinfo-cache-negative-ttl",
R"(
@@ -736,18 +718,6 @@ public:
mismatch if the build isn't reproducible.
)"};
- /* ?Who we trust to use the daemon in safe ways */
- Setting<Strings> allowedUsers{
- this, {"*"}, "allowed-users",
- R"(
- A list of names of users (separated by whitespace) that are allowed
- to connect to the Nix daemon. As with the `trusted-users` option,
- you can specify groups by prefixing them with `@`. Also, you can
- allow all users by specifying `*`. The default is `*`.
-
- Note that trusted users are always allowed to connect.
- )"};
-
Setting<bool> printMissing{this, true, "print-missing",
"Whether to print what paths need to be built or downloaded."};
diff --git a/src/libstore/legacy-ssh-store.cc b/src/libstore/legacy-ssh-store.cc
index e1a4e13a3..2c9dd2680 100644
--- a/src/libstore/legacy-ssh-store.cc
+++ b/src/libstore/legacy-ssh-store.cc
@@ -134,7 +134,6 @@ struct LegacySSHStore : public virtual LegacySSHStoreConfig, public virtual Stor
/* Hash will be set below. FIXME construct ValidPathInfo at end. */
auto info = std::make_shared<ValidPathInfo>(path, Hash::dummy);
- PathSet references;
auto deriver = readString(conn->from);
if (deriver != "")
info->deriver = parseStorePath(deriver);
diff --git a/src/libstore/nix-store.pc.in b/src/libstore/nix-store.pc.in
index 6d67b1e03..385169a13 100644
--- a/src/libstore/nix-store.pc.in
+++ b/src/libstore/nix-store.pc.in
@@ -6,4 +6,4 @@ Name: Nix
Description: Nix Package Manager
Version: @PACKAGE_VERSION@
Libs: -L${libdir} -lnixstore -lnixutil
-Cflags: -I${includedir}/nix -std=c++17
+Cflags: -I${includedir}/nix -std=c++20
diff --git a/src/libstore/path-info.hh b/src/libstore/path-info.hh
index b4b54e593..a7fcbd232 100644
--- a/src/libstore/path-info.hh
+++ b/src/libstore/path-info.hh
@@ -81,12 +81,6 @@ struct ValidPathInfo
/* Return true iff the path is verifiably content-addressed. */
bool isContentAddressed(const Store & store) const;
- /* Functions to view references + hasSelfReference as one set, mainly for
- compatibility's sake. */
- StorePathSet referencesPossiblyToSelf() const;
- void insertReferencePossiblyToSelf(StorePath && ref);
- void setReferencesPossiblyToSelf(StorePathSet && refs);
-
static const size_t maxSigs = std::numeric_limits<size_t>::max();
/* Return the number of signatures on this .narinfo that were
diff --git a/src/libstore/path.hh b/src/libstore/path.hh
index 6a8f027f9..1e5579b90 100644
--- a/src/libstore/path.hh
+++ b/src/libstore/path.hh
@@ -1,6 +1,7 @@
#pragma once
-#include "content-address.hh"
+#include <string_view>
+
#include "types.hh"
namespace nix {
@@ -66,8 +67,6 @@ public:
typedef std::set<StorePath> StorePathSet;
typedef std::vector<StorePath> StorePaths;
-typedef std::map<StorePath, std::optional<ContentAddress>> StorePathCAMap;
-
/* Extension of derivations in the Nix store. */
const std::string drvExtension = ".drv";
diff --git a/src/libstore/realisation.hh b/src/libstore/realisation.hh
index 62561fce3..48d0283de 100644
--- a/src/libstore/realisation.hh
+++ b/src/libstore/realisation.hh
@@ -1,5 +1,8 @@
#pragma once
+#include <variant>
+
+#include "hash.hh"
#include "path.hh"
#include <nlohmann/json_fwd.hpp>
#include "comparator.hh"
diff --git a/src/libstore/remote-store.cc b/src/libstore/remote-store.cc
index ff57a77ca..d1296627a 100644
--- a/src/libstore/remote-store.cc
+++ b/src/libstore/remote-store.cc
@@ -266,6 +266,7 @@ void RemoteStore::setOptions(Connection & conn)
overrides.erase(settings.useSubstitutes.name);
overrides.erase(loggerSettings.showTrace.name);
overrides.erase(settings.experimentalFeatures.name);
+ overrides.erase(settings.pluginFiles.name);
conn.to << overrides.size();
for (auto & i : overrides)
conn.to << i.first << i.second.value;
diff --git a/src/libstore/store-api.cc b/src/libstore/store-api.cc
index 31bd9318f..601efa1cc 100644
--- a/src/libstore/store-api.cc
+++ b/src/libstore/store-api.cc
@@ -744,7 +744,7 @@ StorePathSet Store::queryValidPaths(const StorePathSet & paths, SubstituteFlag m
auto doQuery = [&](const StorePath & path) {
checkInterrupt();
- queryPathInfo(path, {[path, this, &state_, &wakeup](std::future<ref<const ValidPathInfo>> fut) {
+ queryPathInfo(path, {[path, &state_, &wakeup](std::future<ref<const ValidPathInfo>> fut) {
auto state(state_.lock());
try {
auto info = fut.get();
diff --git a/src/libstore/store-api.hh b/src/libstore/store-api.hh
index 5807392a7..4d8db3596 100644
--- a/src/libstore/store-api.hh
+++ b/src/libstore/store-api.hh
@@ -87,6 +87,8 @@ enum BuildMode { bmNormal, bmRepair, bmCheck };
struct BuildResult;
+typedef std::map<StorePath, std::optional<ContentAddress>> StorePathCAMap;
+
struct StoreConfig : public Config
{
using Config::Config;
diff --git a/src/libstore/tests/derived-path.cc b/src/libstore/tests/derived-path.cc
new file mode 100644
index 000000000..d1ac2c5e7
--- /dev/null
+++ b/src/libstore/tests/derived-path.cc
@@ -0,0 +1,62 @@
+#include <regex>
+
+#include <nlohmann/json.hpp>
+#include <gtest/gtest.h>
+#include <rapidcheck/gtest.h>
+
+#include "tests/derived-path.hh"
+#include "tests/libstore.hh"
+
+namespace rc {
+using namespace nix;
+
+Gen<DerivedPath::Opaque> Arbitrary<DerivedPath::Opaque>::arbitrary()
+{
+ return gen::just(DerivedPath::Opaque {
+ .path = *gen::arbitrary<StorePath>(),
+ });
+}
+
+Gen<DerivedPath::Built> Arbitrary<DerivedPath::Built>::arbitrary()
+{
+ return gen::just(DerivedPath::Built {
+ .drvPath = *gen::arbitrary<StorePath>(),
+ .outputs = *gen::arbitrary<OutputsSpec>(),
+ });
+}
+
+Gen<DerivedPath> Arbitrary<DerivedPath>::arbitrary()
+{
+ switch (*gen::inRange<uint8_t>(0, 1)) {
+ case 0:
+ return gen::just<DerivedPath>(*gen::arbitrary<DerivedPath::Opaque>());
+ default:
+ return gen::just<DerivedPath>(*gen::arbitrary<DerivedPath::Built>());
+ }
+}
+
+}
+
+namespace nix {
+
+class DerivedPathTest : public LibStoreTest
+{
+};
+
+// FIXME: `RC_GTEST_FIXTURE_PROP` isn't calling `SetUpTestSuite` because it is
+// no a real fixture.
+//
+// See https://github.com/emil-e/rapidcheck/blob/master/doc/gtest.md#rc_gtest_fixture_propfixture-name-args
+TEST_F(DerivedPathTest, force_init)
+{
+}
+
+RC_GTEST_FIXTURE_PROP(
+ DerivedPathTest,
+ prop_round_rip,
+ (const DerivedPath & o))
+{
+ RC_ASSERT(o == DerivedPath::parse(*store, o.to_string(*store)));
+}
+
+}
diff --git a/src/libstore/tests/derived-path.hh b/src/libstore/tests/derived-path.hh
new file mode 100644
index 000000000..3bc812440
--- /dev/null
+++ b/src/libstore/tests/derived-path.hh
@@ -0,0 +1,28 @@
+#pragma once
+
+#include <rapidcheck/gen/Arbitrary.h>
+
+#include <derived-path.hh>
+
+#include "tests/path.hh"
+#include "tests/outputs-spec.hh"
+
+namespace rc {
+using namespace nix;
+
+template<>
+struct Arbitrary<DerivedPath::Opaque> {
+ static Gen<DerivedPath::Opaque> arbitrary();
+};
+
+template<>
+struct Arbitrary<DerivedPath::Built> {
+ static Gen<DerivedPath::Built> arbitrary();
+};
+
+template<>
+struct Arbitrary<DerivedPath> {
+ static Gen<DerivedPath> arbitrary();
+};
+
+}
diff --git a/src/libstore/tests/libstoretests.hh b/src/libstore/tests/libstore.hh
index 05397659b..05397659b 100644
--- a/src/libstore/tests/libstoretests.hh
+++ b/src/libstore/tests/libstore.hh
diff --git a/src/libstore/tests/local.mk b/src/libstore/tests/local.mk
index a2cf8a0cf..03becc7d1 100644
--- a/src/libstore/tests/local.mk
+++ b/src/libstore/tests/local.mk
@@ -1,6 +1,20 @@
-check: libstore-tests_RUN
+check: libstore-tests-exe_RUN
-programs += libstore-tests
+programs += libstore-tests-exe
+
+libstore-tests-exe_NAME = libnixstore-tests
+
+libstore-tests-exe_DIR := $(d)
+
+libstore-tests-exe_INSTALL_DIR :=
+
+libstore-tests-exe_LIBS = libstore-tests
+
+libstore-tests-exe_LDFLAGS := $(GTEST_LIBS)
+
+libraries += libstore-tests
+
+libstore-tests_NAME = libnixstore-tests
libstore-tests_DIR := $(d)
@@ -10,6 +24,6 @@ libstore-tests_SOURCES := $(wildcard $(d)/*.cc)
libstore-tests_CXXFLAGS += -I src/libstore -I src/libutil
-libstore-tests_LIBS = libstore libutil
+libstore-tests_LIBS = libutil-tests libstore libutil
libstore-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS)
diff --git a/src/libstore/tests/outputs-spec.cc b/src/libstore/tests/outputs-spec.cc
index 06e4cabbd..984d1d963 100644
--- a/src/libstore/tests/outputs-spec.cc
+++ b/src/libstore/tests/outputs-spec.cc
@@ -2,6 +2,7 @@
#include <nlohmann/json.hpp>
#include <gtest/gtest.h>
+#include <rapidcheck/gtest.h>
namespace nix {
@@ -199,3 +200,34 @@ TEST_JSON(ExtendedOutputsSpec, names, R"(["a","b"])", (ExtendedOutputsSpec::Expl
#undef TEST_JSON
}
+
+namespace rc {
+using namespace nix;
+
+Gen<OutputsSpec> Arbitrary<OutputsSpec>::arbitrary()
+{
+ switch (*gen::inRange<uint8_t>(0, 1)) {
+ case 0:
+ return gen::just((OutputsSpec) OutputsSpec::All { });
+ default:
+ return gen::just((OutputsSpec) OutputsSpec::Names {
+ *gen::nonEmpty(gen::container<StringSet>(gen::map(
+ gen::arbitrary<StorePathName>(),
+ [](StorePathName n) { return n.name; }))),
+ });
+ }
+}
+
+}
+
+namespace nix {
+
+RC_GTEST_PROP(
+ OutputsSpec,
+ prop_round_rip,
+ (const OutputsSpec & o))
+{
+ RC_ASSERT(o == OutputsSpec::parse(o.to_string()));
+}
+
+}
diff --git a/src/libstore/tests/outputs-spec.hh b/src/libstore/tests/outputs-spec.hh
new file mode 100644
index 000000000..2d455c817
--- /dev/null
+++ b/src/libstore/tests/outputs-spec.hh
@@ -0,0 +1,17 @@
+#pragma once
+
+#include <rapidcheck/gen/Arbitrary.h>
+
+#include <outputs-spec.hh>
+
+#include <tests/path.hh>
+
+namespace rc {
+using namespace nix;
+
+template<>
+struct Arbitrary<OutputsSpec> {
+ static Gen<OutputsSpec> arbitrary();
+};
+
+}
diff --git a/src/libstore/tests/path.cc b/src/libstore/tests/path.cc
index 8ea252c92..430aa0099 100644
--- a/src/libstore/tests/path.cc
+++ b/src/libstore/tests/path.cc
@@ -7,7 +7,9 @@
#include "path-regex.hh"
#include "store-api.hh"
-#include "libstoretests.hh"
+#include "tests/hash.hh"
+#include "tests/libstore.hh"
+#include "tests/path.hh"
namespace nix {
@@ -73,17 +75,14 @@ void showValue(const StorePath & p, std::ostream & os) {
namespace rc {
using namespace nix;
-template<>
-struct Arbitrary<StorePath> {
- static Gen<StorePath> arbitrary();
-};
-
-Gen<StorePath> Arbitrary<StorePath>::arbitrary()
+Gen<StorePathName> Arbitrary<StorePathName>::arbitrary()
{
- auto len = *gen::inRange<size_t>(1, StorePath::MaxPathLen);
+ auto len = *gen::inRange<size_t>(
+ 1,
+ StorePath::MaxPathLen - std::string_view { HASH_PART }.size());
- std::string pre { HASH_PART "-" };
- pre.reserve(pre.size() + len);
+ std::string pre;
+ pre.reserve(len);
for (size_t c = 0; c < len; ++c) {
switch (auto i = *gen::inRange<uint8_t>(0, 10 + 2 * 26 + 6)) {
@@ -118,7 +117,17 @@ Gen<StorePath> Arbitrary<StorePath>::arbitrary()
}
}
- return gen::just(StorePath { pre });
+ return gen::just(StorePathName {
+ .name = std::move(pre),
+ });
+}
+
+Gen<StorePath> Arbitrary<StorePath>::arbitrary()
+{
+ return gen::just(StorePath {
+ *gen::arbitrary<Hash>(),
+ (*gen::arbitrary<StorePathName>()).name,
+ });
}
} // namespace rc
diff --git a/src/libstore/tests/path.hh b/src/libstore/tests/path.hh
new file mode 100644
index 000000000..d7f1a8988
--- /dev/null
+++ b/src/libstore/tests/path.hh
@@ -0,0 +1,28 @@
+#pragma once
+
+#include <rapidcheck/gen/Arbitrary.h>
+
+#include <path.hh>
+
+namespace nix {
+
+struct StorePathName {
+ std::string name;
+};
+
+}
+
+namespace rc {
+using namespace nix;
+
+template<>
+struct Arbitrary<StorePathName> {
+ static Gen<StorePathName> arbitrary();
+};
+
+template<>
+struct Arbitrary<StorePath> {
+ static Gen<StorePath> arbitrary();
+};
+
+}
diff --git a/src/libutil/args.cc b/src/libutil/args.cc
index 753980fd4..2930913d6 100644
--- a/src/libutil/args.cc
+++ b/src/libutil/args.cc
@@ -324,7 +324,7 @@ MultiCommand::MultiCommand(const Commands & commands_)
expectArgs({
.label = "subcommand",
.optional = true,
- .handler = {[=](std::string s) {
+ .handler = {[=,this](std::string s) {
assert(!command);
auto i = commands.find(s);
if (i == commands.end()) {
diff --git a/src/libutil/config.cc b/src/libutil/config.cc
index 9bb412b4f..b349f2d80 100644
--- a/src/libutil/config.cc
+++ b/src/libutil/config.cc
@@ -209,7 +209,7 @@ void BaseSetting<T>::convertToArg(Args & args, const std::string & category)
.description = fmt("Set the `%s` setting.", name),
.category = category,
.labels = {"value"},
- .handler = {[=](std::string s) { overridden = true; set(s); }},
+ .handler = {[this](std::string s) { overridden = true; set(s); }},
});
if (isAppendable())
@@ -218,7 +218,7 @@ void BaseSetting<T>::convertToArg(Args & args, const std::string & category)
.description = fmt("Append to the `%s` setting.", name),
.category = category,
.labels = {"value"},
- .handler = {[=](std::string s) { overridden = true; set(s, true); }},
+ .handler = {[this](std::string s) { overridden = true; set(s, true); }},
});
}
@@ -270,13 +270,13 @@ template<> void BaseSetting<bool>::convertToArg(Args & args, const std::string &
.longName = name,
.description = fmt("Enable the `%s` setting.", name),
.category = category,
- .handler = {[=]() { override(true); }}
+ .handler = {[this]() { override(true); }}
});
args.addFlag({
.longName = "no-" + name,
.description = fmt("Disable the `%s` setting.", name),
.category = category,
- .handler = {[=]() { override(false); }}
+ .handler = {[this]() { override(false); }}
});
}
diff --git a/src/libutil/config.hh b/src/libutil/config.hh
index 79ec0f9cf..7ac43c854 100644
--- a/src/libutil/config.hh
+++ b/src/libutil/config.hh
@@ -250,11 +250,15 @@ public:
operator const T &() const { return value; }
operator T &() { return value; }
const T & get() const { return value; }
- bool operator ==(const T & v2) const { return value == v2; }
- bool operator !=(const T & v2) const { return value != v2; }
- void operator =(const T & v) { assign(v); }
+ template<typename U>
+ bool operator ==(const U & v2) const { return value == v2; }
+ template<typename U>
+ bool operator !=(const U & v2) const { return value != v2; }
+ template<typename U>
+ void operator =(const U & v) { assign(v); }
virtual void assign(const T & v) { value = v; }
- void setDefault(const T & v) { if (!overridden) value = v; }
+ template<typename U>
+ void setDefault(const U & v) { if (!overridden) value = v; }
void set(const std::string & str, bool append = false) override;
diff --git a/src/libutil/namespaces.cc b/src/libutil/namespaces.cc
new file mode 100644
index 000000000..fdd52d92b
--- /dev/null
+++ b/src/libutil/namespaces.cc
@@ -0,0 +1,100 @@
+#if __linux__
+
+#include "namespaces.hh"
+#include "util.hh"
+#include "finally.hh"
+
+#include <mntent.h>
+
+namespace nix {
+
+bool userNamespacesSupported()
+{
+ static auto res = [&]() -> bool
+ {
+ if (!pathExists("/proc/self/ns/user")) {
+ debug("'/proc/self/ns/user' does not exist; your kernel was likely built without CONFIG_USER_NS=y");
+ return false;
+ }
+
+ Path maxUserNamespaces = "/proc/sys/user/max_user_namespaces";
+ if (!pathExists(maxUserNamespaces) ||
+ trim(readFile(maxUserNamespaces)) == "0")
+ {
+ debug("user namespaces appear to be disabled; check '/proc/sys/user/max_user_namespaces'");
+ return false;
+ }
+
+ Path procSysKernelUnprivilegedUsernsClone = "/proc/sys/kernel/unprivileged_userns_clone";
+ if (pathExists(procSysKernelUnprivilegedUsernsClone)
+ && trim(readFile(procSysKernelUnprivilegedUsernsClone)) == "0")
+ {
+ debug("user namespaces appear to be disabled; check '/proc/sys/kernel/unprivileged_userns_clone'");
+ return false;
+ }
+
+ Pid pid = startProcess([&]()
+ {
+ auto res = unshare(CLONE_NEWUSER);
+ _exit(res ? 1 : 0);
+ });
+
+ bool supported = pid.wait() == 0;
+
+ if (!supported)
+ debug("user namespaces do not work on this system");
+
+ return supported;
+ }();
+ return res;
+}
+
+bool mountNamespacesSupported()
+{
+ static auto res = [&]() -> bool
+ {
+ bool useUserNamespace = userNamespacesSupported();
+
+ Pid pid = startProcess([&]()
+ {
+ auto res = unshare(CLONE_NEWNS | (useUserNamespace ? CLONE_NEWUSER : 0));
+ _exit(res ? 1 : 0);
+ });
+
+ bool supported = pid.wait() == 0;
+
+ if (!supported)
+ debug("mount namespaces do not work on this system");
+
+ return supported;
+ }();
+ return res;
+}
+
+bool pidNamespacesSupported()
+{
+ static auto res = [&]() -> bool
+ {
+ /* Check whether /proc is fully visible, i.e. there are no
+ filesystems mounted on top of files inside /proc. If this
+ is not the case, then we cannot mount a new /proc inside
+ the sandbox that matches the sandbox's PID namespace.
+ See https://lore.kernel.org/lkml/87tvsrjai0.fsf@xmission.com/T/. */
+ auto fp = fopen("/proc/mounts", "r");
+ if (!fp) return false;
+ Finally delFP = [&]() { fclose(fp); };
+
+ while (auto ent = getmntent(fp))
+ if (hasPrefix(std::string_view(ent->mnt_dir), "/proc/")) {
+ debug("PID namespaces do not work because /proc is not fully visible; disabling sandboxing");
+ return false;
+ }
+
+ return true;
+ }();
+ return res;
+}
+
+}
+
+#endif
diff --git a/src/libutil/namespaces.hh b/src/libutil/namespaces.hh
new file mode 100644
index 000000000..34e54d5ad
--- /dev/null
+++ b/src/libutil/namespaces.hh
@@ -0,0 +1,15 @@
+#pragma once
+
+namespace nix {
+
+#if __linux__
+
+bool userNamespacesSupported();
+
+bool mountNamespacesSupported();
+
+bool pidNamespacesSupported();
+
+#endif
+
+}
diff --git a/src/libutil/tests/hash.cc b/src/libutil/tests/hash.cc
index 412c03030..e4e928b3b 100644
--- a/src/libutil/tests/hash.cc
+++ b/src/libutil/tests/hash.cc
@@ -1,5 +1,12 @@
-#include "hash.hh"
+#include <regex>
+
+#include <nlohmann/json.hpp>
#include <gtest/gtest.h>
+#include <rapidcheck/gtest.h>
+
+#include <hash.hh>
+
+#include "tests/hash.hh"
namespace nix {
@@ -73,3 +80,16 @@ namespace nix {
"c7d329eeb6dd26545e96e55b874be909");
}
}
+
+namespace rc {
+using namespace nix;
+
+Gen<Hash> Arbitrary<Hash>::arbitrary()
+{
+ Hash hash(htSHA1);
+ for (size_t i = 0; i < hash.hashSize; ++i)
+ hash.hash[i] = *gen::arbitrary<uint8_t>();
+ return gen::just(hash);
+}
+
+}
diff --git a/src/libutil/tests/hash.hh b/src/libutil/tests/hash.hh
new file mode 100644
index 000000000..9e9650e6e
--- /dev/null
+++ b/src/libutil/tests/hash.hh
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <rapidcheck/gen/Arbitrary.h>
+
+#include <hash.hh>
+
+namespace rc {
+using namespace nix;
+
+template<>
+struct Arbitrary<Hash> {
+ static Gen<Hash> arbitrary();
+};
+
+}
diff --git a/src/libutil/tests/local.mk b/src/libutil/tests/local.mk
index 815e18560..167915439 100644
--- a/src/libutil/tests/local.mk
+++ b/src/libutil/tests/local.mk
@@ -2,14 +2,28 @@ check: libutil-tests_RUN
programs += libutil-tests
+libutil-tests-exe_NAME = libnixutil-tests
+
+libutil-tests-exe_DIR := $(d)
+
+libutil-tests-exe_INSTALL_DIR :=
+
+libutil-tests-exe_LIBS = libutil-tests
+
+libutil-tests-exe_LDFLAGS := $(GTEST_LIBS)
+
+libraries += libutil-tests
+
+libutil-tests_NAME = libnixutil-tests
+
libutil-tests_DIR := $(d)
libutil-tests_INSTALL_DIR :=
libutil-tests_SOURCES := $(wildcard $(d)/*.cc)
-libutil-tests_CXXFLAGS += -I src/libutil -I src/libexpr
+libutil-tests_CXXFLAGS += -I src/libutil
libutil-tests_LIBS = libutil
-libutil-tests_LDFLAGS := $(GTEST_LIBS)
+libutil-tests_LDFLAGS := -lrapidcheck $(GTEST_LIBS)
diff --git a/src/nix-build/nix-build.cc b/src/nix-build/nix-build.cc
index 049838bb1..da76c2ace 100644
--- a/src/nix-build/nix-build.cc
+++ b/src/nix-build/nix-build.cc
@@ -536,7 +536,9 @@ static void main_nix_build(int argc, char * * argv)
"SHELL=%5%; "
"BASH=%5%; "
"set +e; "
- R"s([ -n "$PS1" -a -z "$NIX_SHELL_PRESERVE_PROMPT" ] && PS1='\n\[\033[1;32m\][nix-shell:\w]\$\[\033[0m\] '; )s"
+ R"s([ -n "$PS1" -a -z "$NIX_SHELL_PRESERVE_PROMPT" ] && )s" +
+ (getuid() == 0 ? R"s(PS1='\n\[\033[1;31m\][nix-shell:\w]\$\[\033[0m\] '; )s"
+ : R"s(PS1='\n\[\033[1;32m\][nix-shell:\w]\$\[\033[0m\] '; )s") +
"if [ \"$(type -t runHook)\" = function ]; then runHook shellHook; fi; "
"unset NIX_ENFORCE_PURITY; "
"shopt -u nullglob; "
diff --git a/src/nix/daemon.cc b/src/nix/daemon.cc
index 19fbbf155..a22bccba1 100644
--- a/src/nix/daemon.cc
+++ b/src/nix/daemon.cc
@@ -34,6 +34,43 @@
using namespace nix;
using namespace nix::daemon;
+struct AuthorizationSettings : Config {
+
+ Setting<Strings> trustedUsers{
+ this, {"root"}, "trusted-users",
+ R"(
+ A list of names of users (separated by whitespace) that have
+ additional rights when connecting to the Nix daemon, such as the
+ ability to specify additional binary caches, or to import unsigned
+ NARs. You can also specify groups by prefixing them with `@`; for
+ instance, `@wheel` means all users in the `wheel` group. The default
+ is `root`.
+
+ > **Warning**
+ >
+ > Adding a user to `trusted-users` is essentially equivalent to
+ > giving that user root access to the system. For example, the user
+ > can set `sandbox-paths` and thereby obtain read access to
+ > directories that are otherwise inacessible to them.
+ )"};
+
+ /* ?Who we trust to use the daemon in safe ways */
+ Setting<Strings> allowedUsers{
+ this, {"*"}, "allowed-users",
+ R"(
+ A list of names of users (separated by whitespace) that are allowed
+ to connect to the Nix daemon. As with the `trusted-users` option,
+ you can specify groups by prefixing them with `@`. Also, you can
+ allow all users by specifying `*`. The default is `*`.
+
+ Note that trusted users are always allowed to connect.
+ )"};
+};
+
+AuthorizationSettings authorizationSettings;
+
+static GlobalConfig::Register rSettings(&authorizationSettings);
+
#ifndef __linux__
#define SPLICE_F_MOVE 0
static ssize_t splice(int fd_in, void *off_in, int fd_out, void *off_out, size_t len, unsigned int flags)
@@ -203,8 +240,8 @@ static void daemonLoop()
struct group * gr = peer.gidKnown ? getgrgid(peer.gid) : 0;
std::string group = gr ? gr->gr_name : std::to_string(peer.gid);
- Strings trustedUsers = settings.trustedUsers;
- Strings allowedUsers = settings.allowedUsers;
+ Strings trustedUsers = authorizationSettings.trustedUsers;
+ Strings allowedUsers = authorizationSettings.allowedUsers;
if (matchUser(user, group, trustedUsers))
trusted = Trusted;
@@ -241,14 +278,7 @@ static void daemonLoop()
// Handle the connection.
FdSource from(remote.get());
FdSink to(remote.get());
- processConnection(openUncachedStore(), from, to, trusted, NotRecursive, [&](Store & store) {
-#if 0
- /* Prevent users from doing something very dangerous. */
- if (geteuid() == 0 &&
- querySetting("build-users-group", "") == "")
- throw Error("if you run 'nix-daemon' as root, then you MUST set 'build-users-group'!");
-#endif
- });
+ processConnection(openUncachedStore(), from, to, trusted, NotRecursive);
exit(0);
}, options);
@@ -301,7 +331,7 @@ static void runDaemon(bool stdio)
/* Auth hook is empty because in this mode we blindly trust the
standard streams. Limiting access to those is explicitly
not `nix-daemon`'s responsibility. */
- processConnection(openUncachedStore(), from, to, Trusted, NotRecursive, [&](Store & _){});
+ processConnection(openUncachedStore(), from, to, Trusted, NotRecursive);
}
} else
daemonLoop();
diff --git a/src/nix/diff-closures.cc b/src/nix/diff-closures.cc
index 0621d662c..3489cc132 100644
--- a/src/nix/diff-closures.cc
+++ b/src/nix/diff-closures.cc
@@ -106,7 +106,7 @@ void printClosureDiff(
using namespace nix;
-struct CmdDiffClosures : SourceExprCommand
+struct CmdDiffClosures : SourceExprCommand, MixOperateOnOptions
{
std::string _before, _after;
diff --git a/src/nix/flake.cc b/src/nix/flake.cc
index d2f68c37c..c025bc7a6 100644
--- a/src/nix/flake.cc
+++ b/src/nix/flake.cc
@@ -1002,6 +1002,61 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
auto flake = std::make_shared<LockedFlake>(lockFlake());
auto localSystem = std::string(settings.thisSystem.get());
+ std::function<bool(
+ eval_cache::AttrCursor & visitor,
+ const std::vector<Symbol> &attrPath,
+ const Symbol &attr)> hasContent;
+
+ // For frameworks it's important that structures are as lazy as possible
+ // to prevent infinite recursions, performance issues and errors that
+ // aren't related to the thing to evaluate. As a consequence, they have
+ // to emit more attributes than strictly (sic) necessary.
+ // However, these attributes with empty values are not useful to the user
+ // so we omit them.
+ hasContent = [&](
+ eval_cache::AttrCursor & visitor,
+ const std::vector<Symbol> &attrPath,
+ const Symbol &attr) -> bool
+ {
+ auto attrPath2(attrPath);
+ attrPath2.push_back(attr);
+ auto attrPathS = state->symbols.resolve(attrPath2);
+ const auto & attrName = state->symbols[attr];
+
+ auto visitor2 = visitor.getAttr(attrName);
+
+ if ((attrPathS[0] == "apps"
+ || attrPathS[0] == "checks"
+ || attrPathS[0] == "devShells"
+ || attrPathS[0] == "legacyPackages"
+ || attrPathS[0] == "packages")
+ && (attrPathS.size() == 1 || attrPathS.size() == 2)) {
+ for (const auto &subAttr : visitor2->getAttrs()) {
+ if (hasContent(*visitor2, attrPath2, subAttr)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ if ((attrPathS.size() == 1)
+ && (attrPathS[0] == "formatter"
+ || attrPathS[0] == "nixosConfigurations"
+ || attrPathS[0] == "nixosModules"
+ || attrPathS[0] == "overlays"
+ )) {
+ for (const auto &subAttr : visitor2->getAttrs()) {
+ if (hasContent(*visitor2, attrPath2, subAttr)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // If we don't recognize it, it's probably content
+ return true;
+ };
+
std::function<nlohmann::json(
eval_cache::AttrCursor & visitor,
const std::vector<Symbol> & attrPath,
@@ -1027,7 +1082,12 @@ struct CmdFlakeShow : FlakeCommand, MixJSON
{
if (!json)
logger->cout("%s", headerPrefix);
- auto attrs = visitor.getAttrs();
+ std::vector<Symbol> attrs;
+ for (const auto &attr : visitor.getAttrs()) {
+ if (hasContent(visitor, attrPath, attr))
+ attrs.push_back(attr);
+ }
+
for (const auto & [i, attr] : enumerate(attrs)) {
const auto & attrName = state->symbols[attr];
bool last = i + 1 == attrs.size();
diff --git a/src/nix/ping-store.cc b/src/nix/ping-store.cc
index 3c3b7bb45..5c44510ab 100644
--- a/src/nix/ping-store.cc
+++ b/src/nix/ping-store.cc
@@ -1,10 +1,13 @@
#include "command.hh"
#include "shared.hh"
#include "store-api.hh"
+#include "finally.hh"
+
+#include <nlohmann/json.hpp>
using namespace nix;
-struct CmdPingStore : StoreCommand
+struct CmdPingStore : StoreCommand, MixJSON
{
std::string description() override
{
@@ -20,10 +23,21 @@ struct CmdPingStore : StoreCommand
void run(ref<Store> store) override
{
- notice("Store URL: %s", store->getUri());
- store->connect();
- if (auto version = store->getVersion())
- notice("Version: %s", *version);
+ if (!json) {
+ notice("Store URL: %s", store->getUri());
+ store->connect();
+ if (auto version = store->getVersion())
+ notice("Version: %s", *version);
+ } else {
+ nlohmann::json res;
+ Finally printRes([&]() {
+ logger->cout("%s", res);
+ });
+ res["url"] = store->getUri();
+ store->connect();
+ if (auto version = store->getVersion())
+ res["version"] = *version;
+ }
}
};
diff --git a/src/nix/search.cc b/src/nix/search.cc
index d2a31607d..4fa1e7837 100644
--- a/src/nix/search.cc
+++ b/src/nix/search.cc
@@ -56,8 +56,8 @@ struct CmdSearch : InstallableCommand, MixJSON
Strings getDefaultFlakeAttrPaths() override
{
return {
- "packages." + settings.thisSystem.get() + ".",
- "legacyPackages." + settings.thisSystem.get() + "."
+ "packages." + settings.thisSystem.get(),
+ "legacyPackages." + settings.thisSystem.get()
};
}
diff --git a/src/nix/why-depends.cc b/src/nix/why-depends.cc
index 76125e5e4..a3a9dc698 100644
--- a/src/nix/why-depends.cc
+++ b/src/nix/why-depends.cc
@@ -27,7 +27,7 @@ static std::string filterPrintable(const std::string & s)
return res;
}
-struct CmdWhyDepends : SourceExprCommand
+struct CmdWhyDepends : SourceExprCommand, MixOperateOnOptions
{
std::string _package, _dependency;
bool all = false;