From afd40adc90a9efd101814977009505440ccc2e3a Mon Sep 17 00:00:00 2001 From: Amaan Qureshi Date: Tue, 27 Jan 2026 12:55:10 -0500 Subject: [PATCH] libstore: split out local build and store related settings under `LocalSettings` The global `Settings` struct contained many settings that only apply to local builds or the local store (sandbox configuration, GC settings, build user groups, etc.). This commit extracts these into a dedicated `LocalSettings` struct in its own header, along with `GCSettings` and `AutoAllocateUidSettings`. This improves code organization and prepares for eventually making these per-store settings in the future. Settings are accessed via `getLocalSettings()` from the global settings object or through `LocalStoreConfig::getLocalSettings()` for store-specific access. --- src/libstore-test-support/test-main.cc | 4 +- .../build/derivation-building-goal.cc | 3 +- src/libstore/build/worker.cc | 2 +- src/libstore/gc.cc | 4 +- src/libstore/include/nix/store/globals.hh | 634 +---------------- .../include/nix/store/local-settings.hh | 665 ++++++++++++++++++ src/libstore/include/nix/store/local-store.hh | 14 +- src/libstore/include/nix/store/meson.build | 1 + src/libstore/linux/personality.cc | 3 +- src/libstore/local-store.cc | 56 +- src/libstore/optimise-store.cc | 2 +- .../unix/build/chroot-derivation-builder.cc | 6 +- .../unix/build/darwin-derivation-builder.cc | 2 +- src/libstore/unix/build/derivation-builder.cc | 36 +- .../unix/build/linux-derivation-builder.cc | 23 +- .../unix/include/nix/store/user-lock.hh | 6 +- src/libstore/unix/user-lock.cc | 14 +- src/nix/nix-store/nix-store.cc | 7 +- src/nix/run.cc | 4 +- src/nix/unix/daemon.cc | 4 +- 20 files changed, 774 insertions(+), 716 deletions(-) create mode 100644 src/libstore/include/nix/store/local-settings.hh diff --git a/src/libstore-test-support/test-main.cc b/src/libstore-test-support/test-main.cc index 0b9072dc08f..1c17dd3e206 100644 --- a/src/libstore-test-support/test-main.cc +++ b/src/libstore-test-support/test-main.cc @@ -31,13 +31,13 @@ int testMainForBuidingPre(int argc, char ** argv) // sandboxBuildDir = /build // However, we have a rule that the store dir must not be inside the storeDir, so we need to pick a different // sandboxBuildDir. - settings.sandboxBuildDir = "/test-build-dir-instead-of-usual-build-dir"; + settings.getLocalSettings().sandboxBuildDir = "/test-build-dir-instead-of-usual-build-dir"; #endif #ifdef __APPLE__ // Avoid this error, when already running in a sandbox: // sandbox-exec: sandbox_apply: Operation not permitted - settings.sandboxMode = smDisabled; + settings.getLocalSettings().sandboxMode = smDisabled; setEnv("_NIX_TEST_NO_SANDBOX", "1"); #endif diff --git a/src/libstore/build/derivation-building-goal.cc b/src/libstore/build/derivation-building-goal.cc index 2bf6cb674aa..6a3f8b2d924 100644 --- a/src/libstore/build/derivation-building-goal.cc +++ b/src/libstore/build/derivation-building-goal.cc @@ -707,7 +707,8 @@ Goal::Co DerivationBuildingGoal::buildLocally( } }; - decltype(DerivationBuilderParams::defaultPathsInChroot) defaultPathsInChroot = settings.sandboxPaths.get(); + decltype(DerivationBuilderParams::defaultPathsInChroot) defaultPathsInChroot = + localStore.config->getLocalSettings().sandboxPaths.get(); DesugaredEnv desugaredEnv; /* Add the closure of store paths to the chroot. */ diff --git a/src/libstore/build/worker.cc b/src/libstore/build/worker.cc index 460ac61d799..5aff13055cc 100644 --- a/src/libstore/build/worker.cc +++ b/src/libstore/build/worker.cc @@ -406,7 +406,7 @@ void Worker::waitForInput() auto nearest = steady_time_point::max(); // nearest deadline auto localStore = dynamic_cast(&store); - if (localStore && localStore->config->getGCSettings().minFree.get() != 0) + if (localStore && localStore->config->getLocalSettings().getGCSettings().minFree.get() != 0) // If we have a local store (and thus are capable of automatically collecting garbage) and configured to do so, // periodically wake up to see if we need to run the garbage collector. (See the `autoGC` call site above in // this file, also gated on having a local store. when we wake up, we intended to reach that call site.) diff --git a/src/libstore/gc.cc b/src/libstore/gc.cc index 6d40313d509..e41e5bac4cd 100644 --- a/src/libstore/gc.cc +++ b/src/libstore/gc.cc @@ -463,7 +463,7 @@ struct GCLimitReached void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) { - const auto & gcSettings = config->getGCSettings(); + const auto & gcSettings = config->getLocalSettings().getGCSettings(); bool shouldDelete = options.action == GCOptions::gcDeleteDead || options.action == GCOptions::gcDeleteSpecific; bool keepOutputs = gcSettings.keepOutputs; @@ -918,7 +918,7 @@ void LocalStore::collectGarbage(const GCOptions & options, GCResults & results) void LocalStore::autoGC(bool sync) { #if HAVE_STATVFS - const auto & gcSettings = config->getGCSettings(); + const auto & gcSettings = config->getLocalSettings().getGCSettings(); static auto fakeFreeSpaceFile = getEnv("_NIX_TEST_FREE_SPACE_FILE"); diff --git a/src/libstore/include/nix/store/globals.hh b/src/libstore/include/nix/store/globals.hh index f4014e7b8fd..0dbc3958171 100644 --- a/src/libstore/include/nix/store/globals.hh +++ b/src/libstore/include/nix/store/globals.hh @@ -1,43 +1,18 @@ #pragma once ///@file -#include -#include - #include #include "nix/util/types.hh" #include "nix/util/configuration.hh" #include "nix/util/environment-variables.hh" -#include "nix/util/experimental-features.hh" -#include "nix/util/users.hh" #include "nix/store/build/derivation-builder.hh" +#include "nix/store/local-settings.hh" #include "nix/store/config.hh" namespace nix { -typedef enum { smEnabled, smRelaxed, smDisabled } SandboxMode; - -template<> -SandboxMode BaseSetting::parse(const std::string & str) const; -template<> -std::string BaseSetting::to_string() const; - -template<> -PathsInChroot BaseSetting::parse(const std::string & str) const; -template<> -std::string BaseSetting::to_string() const; - -template<> -struct BaseSetting::trait -{ - static constexpr bool appendable = true; -}; - -template<> -void BaseSetting::appendOrSet(PathsInChroot newValue, bool append); - struct MaxBuildJobsSetting : public BaseSetting { MaxBuildJobsSetting( @@ -54,95 +29,6 @@ struct MaxBuildJobsSetting : public BaseSetting unsigned int parse(const std::string & str) const override; }; -const uint32_t maxIdsPerBuild = -#ifdef __linux__ - 1 << 16 -#else - 1 -#endif - ; - -struct GCSettings : public virtual Config -{ - Setting reservedSize{ - this, - 8 * 1024 * 1024, - "gc-reserved-space", - "Amount of reserved disk space for the garbage collector.", - }; - - Setting keepOutputs{ - this, - false, - "keep-outputs", - R"( - If `true`, the garbage collector keeps the outputs of - non-garbage derivations. If `false` (default), outputs are - deleted unless they are GC roots themselves (or reachable from other - roots). - - In general, outputs must be registered as roots separately. However, - even if the output of a derivation is registered as a root, the - collector still deletes store paths that are used only at build - time (e.g., the C compiler, or source tarballs downloaded from the - network). To prevent it from doing so, set this option to `true`. - )", - {"gc-keep-outputs"}, - }; - - Setting keepDerivations{ - this, - true, - "keep-derivations", - R"( - If `true` (default), the garbage collector keeps the derivations - from which non-garbage store paths were built. If `false`, they are - deleted unless explicitly registered as a root (or reachable from - other roots). - - Keeping derivation around is useful for querying and traceability - (e.g., it allows you to ask with what dependencies or options a - store path was built), so by default this option is on. Turn it off - to save a bit of disk space (or a lot if `keep-outputs` is also - turned on). - )", - {"gc-keep-derivations"}, - }; - - Setting minFree{ - this, - 0, - "min-free", - R"( - When free disk space in `/nix/store` drops below `min-free` during a - build, Nix performs a garbage-collection until `max-free` bytes are - available or there is no more garbage. A value of `0` (the default) - disables this feature. - )", - }; - - // n.b. this is deliberately int64 max rather than uint64 max because - // this goes through the Nix language JSON parser and thus needs to be - // representable in Nix language integers. - Setting maxFree{ - this, - std::numeric_limits::max(), - "max-free", - R"( - When a garbage collection is triggered by the `min-free` option, it - stops as soon as `max-free` bytes are available. The default is - infinity (i.e. delete all garbage). - )", - }; - - Setting minFreeCheckInterval{ - this, - 5, - "min-free-check-interval", - "Number of seconds between checking free disk space.", - }; -}; - struct LogFileSettings : public virtual Config { /** @@ -178,30 +64,7 @@ public: {"build-compress-log"}}; }; -struct AutoAllocateUidSettings : public virtual Config -{ - Setting startId{ - this, -#ifdef __linux__ - 0x34000000, -#else - 56930, -#endif - "start-id", - "The first UID and GID to use for dynamic ID allocation."}; - - Setting uidCount{ - this, -#ifdef __linux__ - maxIdsPerBuild * 128, -#else - 128, -#endif - "id-count", - "The number of UIDs/GIDs to use for dynamic ID allocation."}; -}; - -class Settings : public virtual Config, private AutoAllocateUidSettings, private GCSettings, private LogFileSettings +class Settings : public virtual Config, private LocalSettings, private LogFileSettings { StringSet getDefaultSystemFeatures(); @@ -215,15 +78,17 @@ public: Settings(); + using ExternalBuilders = std::vector; + /** - * Get the GC settings. + * Get the local store settings. */ - GCSettings & getGCSettings() + LocalSettings & getLocalSettings() { return *this; } - const GCSettings & getGCSettings() const + const LocalSettings & getLocalSettings() const { return *this; } @@ -241,15 +106,6 @@ public: return *this; } - /** - * Get AutoAllocateUidSettings if auto-allocate-uids is enabled. - * @return Pointer to settings if enabled, nullptr otherwise. - */ - const AutoAllocateUidSettings * getAutoAllocateUidSettings() const - { - return autoAllocateUids ? this : nullptr; - } - static unsigned int getDefaultCores(); /** @@ -289,6 +145,8 @@ public: section of the manual for supported store types and settings. )"}; + Setting useSQLiteWAL{this, !isWSL1(), "use-sqlite-wal", "Whether SQLite should use WAL mode."}; + Setting keepFailed{this, false, "keep-failed", "Whether to keep temporary directories of failed builds."}; Setting keepGoing{ @@ -595,35 +453,6 @@ public: This can drastically reduce build times if the network connection between the local machine and the remote build host is slow. )"}; - Setting fsyncMetadata{ - this, - true, - "fsync-metadata", - R"( - If set to `true`, changes to the Nix store metadata (in - `/nix/var/nix/db`) are synchronously flushed to disk. This improves - robustness in case of system crashes, but reduces performance. The - default is `true`. - )"}; - - Setting fsyncStorePaths{ - this, - false, - "fsync-store-paths", - R"( - Whether to call `fsync()` on store paths before registering them, to - flush them to disk. This improves robustness in case of system crashes, - but reduces performance. The default is `false`. - )"}; - - Setting useSQLiteWAL{this, !isWSL1(), "use-sqlite-wal", "Whether SQLite should use WAL mode."}; - -#ifndef _WIN32 - // FIXME: remove this option, `fsync-store-paths` is faster. - Setting syncBeforeRegistering{ - this, false, "sync-before-registering", "Whether to call `sync()` before registering a path as valid."}; -#endif - Setting useSubstitutes{ this, true, @@ -635,81 +464,6 @@ public: )", {"build-use-substitutes"}}; - Setting buildUsersGroup{ - this, - "", - "build-users-group", - R"( - This options specifies the Unix group containing the Nix build user - accounts. In multi-user Nix installations, builds should not be - performed by the Nix account since that would allow users to - arbitrarily modify the Nix store and database by supplying specially - crafted builders; and they cannot be performed by the calling user - since that would allow him/her to influence the build result. - - Therefore, if this option is non-empty and specifies a valid group, - builds are performed under the user accounts that are a member - of the group specified here (as listed in `/etc/group`). Those user - accounts should not be used for any other purpose\! - - Nix never runs two builds under the same user account at the - same time. This is to prevent an obvious security hole: a malicious - user writing a Nix expression that modifies the build result of a - legitimate Nix expression being built by another user. Therefore it - is good to have as many Nix build user accounts as you can spare. - (Remember: uids are cheap.) - - The build users should have permission to create files in the Nix - store, but not delete them. Therefore, `/nix/store` should be owned - by the Nix account, its group should be the group specified here, - and its mode should be `1775`. - - If the build users group is empty, builds are 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 - with a nix daemon accessible to untrusted clients. - - Defaults to `nixbld` when running as root, *empty* otherwise. - )", - {}, - false}; - - Setting autoAllocateUids{ - this, - false, - "auto-allocate-uids", - R"( - Whether to select UIDs for builds automatically, instead of using the - users in `build-users-group`. - - UIDs are allocated starting at 872415232 (0x34000000) on Linux and 56930 on macOS. - )", - {}, - true, - Xp::AutoAllocateUids}; - -#ifdef __linux__ - Setting useCgroups{ - this, - false, - "use-cgroups", - R"( - Whether to execute builds inside cgroups. - This is only supported on Linux. - - Cgroups are required and enabled automatically for derivations - that require the `uid-range` system feature. - )"}; -#endif - - Setting impersonateLinux26{ - this, - false, - "impersonate-linux-26", - "Whether to impersonate a Linux 2.6 machine on newer kernels.", - {"build-impersonate-linux-26"}}; - Setting maxLogSize{ this, 0, @@ -723,210 +477,6 @@ public: Setting pollInterval{this, 5, "build-poll-interval", "How often (in seconds) to poll for locks."}; - Setting autoOptimiseStore{ - this, - false, - "auto-optimise-store", - R"( - If set to `true`, Nix automatically detects files in the store - that have identical contents, and replaces them with hard links to - a single copy. This saves disk space. If set to `false` (the - default), you can still run `nix-store --optimise` to get rid of - duplicate files. - )"}; - - Setting sandboxMode{ - this, -#ifdef __linux__ - smEnabled -#else - smDisabled -#endif - , - "sandbox", - R"( - If set to `true`, builds are performed in a *sandboxed - environment*, i.e., they’re isolated from the normal file system - hierarchy and only see their dependencies in the Nix store, - the temporary build directory, private versions of `/proc`, - `/dev`, `/dev/shm` and `/dev/pts` (on Linux), and the paths - configured with the `sandbox-paths` option. This is useful to - prevent undeclared dependencies on files in directories such as - `/usr/bin`. In addition, on Linux, builds run in private PID, - mount, network, IPC and UTS namespaces to isolate them from other - processes in the system (except that fixed-output derivations do - not run in private network namespace to ensure they can access the - network). - - Currently, sandboxing only work on Linux and macOS. The use of a - sandbox requires that Nix is run as root (so you should use the - “build users” feature to perform the actual builds under different - users than root). - - If this option is set to `relaxed`, then fixed-output derivations - and derivations that have the `__noChroot` attribute set to `true` - do not run in sandboxes. - - The default is `true` on Linux and `false` on all other platforms. - )", - {"build-use-chroot", "build-use-sandbox"}}; - - Setting sandboxPaths{ - this, - {}, - "sandbox-paths", - R"( - A list of paths bind-mounted into Nix sandbox environments. You can - use the syntax `target=source` to mount a path in a different - location in the sandbox; for instance, `/bin=/nix-bin` mounts - the path `/nix-bin` as `/bin` inside the 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` - only be mounted in the sandbox if it exists in the host filesystem. - - If the source is in the Nix store, then its closure is added to - the sandbox as well. - - Depending on how Nix was built, the default value for this option - may be empty or provide `/bin/sh` as a bind-mount of `bash`. - )", - {"build-chroot-dirs", "build-sandbox-paths"}}; - - Setting sandboxFallback{ - this, true, "sandbox-fallback", "Whether to disable sandboxing when the kernel doesn't allow it."}; - -#ifndef _WIN32 - Setting requireDropSupplementaryGroups{ - this, - isRootUser(), - "require-drop-supplementary-groups", - R"( - Following the principle of least privilege, - Nix attempts to drop supplementary groups when building with sandboxing. - - However this can fail under some circumstances. - For example, if the user lacks the `CAP_SETGID` capability. - Search `setgroups(2)` for `EPERM` to find more detailed information on this. - - If you encounter such a failure, setting this option to `false` enables you to ignore it and continue. - But before doing so, you should consider the security implications carefully. - Not dropping supplementary groups means the build sandbox is less restricted than intended. - - This option defaults to `true` when the user is root - (since `root` usually has permissions to call setgroups) - and `false` otherwise. - )"}; -#endif - -#ifdef __linux__ - Setting sandboxShmSize{ - this, - "50%", - "sandbox-dev-shm-size", - R"( - *Linux only* - - This option determines the maximum size of the `tmpfs` filesystem - mounted on `/dev/shm` in Linux sandboxes. For the format, see the - description of the `size` option of `tmpfs` in mount(8). The default - is `50%`. - )"}; -#endif - -#if defined(__linux__) || defined(__FreeBSD__) - Setting sandboxBuildDir{ - this, - "/build", - "sandbox-build-dir", - R"( - *Linux only* - - The build directory inside the sandbox. - - This directory is backed by [`build-dir`](#conf-build-dir) on the host. - )"}; -#endif - - Setting> buildDir{ - this, - std::nullopt, - "build-dir", - R"( - Override the `build-dir` store setting for all stores that have this setting. - - See also the per-store [`build-dir`](@docroot@/store/types/local-store.md#store-local-store-build-dir) setting. - )"}; - - Setting allowedImpureHostPrefixes{ - this, - {}, - "allowed-impure-host-deps", - "Which prefixes to allow derivations to ask for access to (primarily for Darwin)."}; - -#ifdef __APPLE__ - Setting darwinLogSandboxViolations{ - this, - false, - "darwin-log-sandbox-violations", - "Whether to log Darwin sandbox access violations to the system log."}; -#endif - - Setting runDiffHook{ - this, - false, - "run-diff-hook", - R"( - If true, enable the execution of the `diff-hook` program. - - When using the Nix daemon, `run-diff-hook` must be set in the - `nix.conf` configuration file, and cannot be passed at the command - line. - )"}; - -private: - - OptionalPathSetting diffHook{ - this, - std::nullopt, - "diff-hook", - R"( - Absolute path to an executable capable of diffing build - results. The hook is executed if `run-diff-hook` is true, and the - output of a build is known to not be the same. This program is not - executed to determine if two results are the same. - - The diff hook is executed by the same user and group who ran the - build. However, the diff hook does not have write access to the - store path just built. - - The diff hook program receives three parameters: - - 1. A path to the previous build's results - - 2. A path to the current build's results - - 3. The path to the build's derivation - - 4. The path to the build's scratch directory. This directory - exists only if the build was run with `--keep-failed`. - - The stderr and stdout output from the diff hook isn't displayed - to the user. Instead, it prints to the nix-daemon's log. - - When using the Nix daemon, `diff-hook` must be set in the `nix.conf` - configuration file, and cannot be passed at the command line. - )"}; - -public: - - const Path * getDiffHook() const - { - if (!runDiffHook.get()) { - return nullptr; - } - return get(diffHook.get()); - } - Setting trustedPublicKeys{ this, {"cache.nixos.org-1:6NCHdD59X431o0gWypbMrAURkbJ16ZPMQFGspcDShjY="}, @@ -1135,27 +685,6 @@ public: Setting printMissing{ this, true, "print-missing", "Whether to print what paths need to be built or downloaded."}; - Setting preBuildHook{ - this, - "", - "pre-build-hook", - R"( - If set, the path to a program that can set extra derivation-specific - settings for this system. This is used for settings that can't be - captured by the derivation model itself and are too variable between - different versions of the same system to be hard-coded into nix. - - The hook is passed the derivation path and, if sandboxes are - enabled, the sandbox directory. It can then modify the sandbox and - send a series of commands to modify various settings to stdout. The - currently recognized commands are: - - - `extra-sandbox-paths`\ - Pass a list of files and directories to be included in the - sandbox for this build. One entry per line, terminated by an - empty line. Entries have the same format as `sandbox-paths`. - )"}; - Setting postBuildHook{ this, "", @@ -1261,48 +790,6 @@ public: // Don't document the machine-specific default value false}; -#ifdef __linux__ - Setting filterSyscalls{ - this, - true, - "filter-syscalls", - R"( - Whether to prevent certain dangerous system calls, such as - creation of setuid/setgid files or adding ACLs or extended - attributes. Only disable this if you're aware of the - security implications. - )"}; - - Setting allowNewPrivileges{ - this, - false, - "allow-new-privileges", - R"( - (Linux-specific.) By default, builders on Linux cannot acquire new - privileges by calling setuid/setgid programs or programs that have - file capabilities. For example, programs such as `sudo` or `ping` - should fail. (Note that in sandbox builds, no such programs are - available unless you bind-mount them into the sandbox via the - `sandbox-paths` option.) You can allow the use of such programs by - enabling this option. This is impure and usually undesirable, but - may be useful in certain scenarios (e.g. to spin up containers or - set up userspace network interfaces in tests). - )"}; -#endif - -#if NIX_SUPPORT_ACL - Setting ignoredAcls{ - this, - {"security.selinux", "system.nfs4_acl", "security.csm"}, - "ignored-acls", - R"( - A list of ACLs that should be ignored, normally Nix attempts to - remove all ACLs from files and directories in the Nix store, but - some ACLs like `security.selinux` or `system.nfs4_acl` can't be - removed even by root. Therefore it's best to just ignore them. - )"}; -#endif - Setting hashedMirrors{ this, {}, @@ -1327,24 +814,6 @@ public: first. If it is not available there, it tries the original URI. )"}; - Setting narBufferSize{ - this, 32 * 1024 * 1024, "nar-buffer-size", "Maximum size of NARs before spilling them to disk."}; - - Setting allowSymlinkedStore{ - this, - false, - "allow-symlinked-store", - R"( - If set to `true`, Nix stops complaining if the store directory - (typically `/nix/store`) contains symlink components. - - This risks making some builds "impure" because builders sometimes - "canonicalise" paths by resolving all symlink components. Problems - occur if those builds are then deployed to machines where /nix/store - resolves to a different location from that of the build machine. You - can enable this setting if you are sure you're not going to do that. - )"}; - Setting useXDGBaseDirectories{ this, false, @@ -1379,27 +848,6 @@ public: ``` )"}; - Setting impureEnv{ - this, - {}, - "impure-env", - R"( - A list of items, each in the format of: - - - `name=value`: Set environment variable `name` to `value`. - - If the user is trusted (see `trusted-users` option), when building - a fixed-output derivation, environment variables set in this option - is passed to the builder if they are listed in [`impureEnvVars`](@docroot@/language/advanced-attributes.md#adv-attr-impureEnvVars). - - This option is useful for, e.g., setting `https_proxy` for - fixed-output derivations and in a multi-user Nix installation, or - setting private access tokens when fetching a private repository. - )", - {}, // aliases - true, // document default - Xp::ConfigurableImpureEnv}; - Setting warnLargePathThreshold{ this, 0, @@ -1411,70 +859,6 @@ public: Set it to 1 to warn on all paths. )"}; - using ExternalBuilders = std::vector; - - Setting externalBuilders{ - this, - {}, - "external-builders", - R"( - Helper programs that execute derivations. - - The program is passed a JSON document that describes the build environment as the final argument. - The JSON document looks like this: - - { - "args": [ - "-e", - "/nix/store/vj1c3wf9…-source-stdenv.sh", - "/nix/store/shkw4qm9…-default-builder.sh" - ], - "builder": "/nix/store/s1qkj0ph…-bash-5.2p37/bin/bash", - "env": { - "HOME": "/homeless-shelter", - "builder": "/nix/store/s1qkj0ph…-bash-5.2p37/bin/bash", - "nativeBuildInputs": "/nix/store/l31j72f1…-version-check-hook", - "out": "/nix/store/2yx2prgx…-hello-2.12.2" - … - }, - "inputPaths": [ - "/nix/store/14dciax3…-glibc-2.32-54-dev", - "/nix/store/1azs5s8z…-gettext-0.21", - … - ], - "outputs": { - "out": "/nix/store/2yx2prgx…-hello-2.12.2" - }, - "realStoreDir": "/nix/store", - "storeDir": "/nix/store", - "system": "aarch64-linux", - "tmpDir": "/private/tmp/nix-build-hello-2.12.2.drv-0/build", - "tmpDirInSandbox": "/build", - "topTmpDir": "/private/tmp/nix-build-hello-2.12.2.drv-0", - "version": 1 - } - )", - {}, // aliases - true, // document default - // NOTE(cole-h): even though we can make the experimental feature required here, the errors - // are not as good (it just becomes a warning if you try to use this setting without the - // experimental feature) - // - // With this commented out: - // - // error: experimental Nix feature 'external-builders' is disabled; add '--extra-experimental-features - // external-builders' to enable it - // - // With this uncommented: - // - // warning: Ignoring setting 'external-builders' because experimental feature 'external-builders' is not enabled - // error: Cannot build '/nix/store/vwsp4qd8…-opentofu-1.10.2.drv'. - // Reason: required system or feature not available - // Required system: 'aarch64-linux' with features {} - // Current system: 'aarch64-darwin' with features {apple-virt, benchmark, big-parallel, nixos-test} - // Xp::ExternalBuilders - }; - /** * Finds the first external derivation builder that supports this * derivation, or else returns a null pointer. diff --git a/src/libstore/include/nix/store/local-settings.hh b/src/libstore/include/nix/store/local-settings.hh new file mode 100644 index 00000000000..9e29fc51074 --- /dev/null +++ b/src/libstore/include/nix/store/local-settings.hh @@ -0,0 +1,665 @@ +#pragma once +///@file + +#include "nix/util/types.hh" +#include "nix/util/configuration.hh" +#include "nix/util/experimental-features.hh" +#include "nix/util/users.hh" +#include "nix/store/build/derivation-builder.hh" + +#include "nix/store/config.hh" + +#include +#include +#include +#include + +namespace nix { + +typedef enum { smEnabled, smRelaxed, smDisabled } SandboxMode; + +template<> +SandboxMode BaseSetting::parse(const std::string & str) const; +template<> +std::string BaseSetting::to_string() const; + +template<> +PathsInChroot BaseSetting::parse(const std::string & str) const; +template<> +std::string BaseSetting::to_string() const; + +template<> +struct BaseSetting::trait +{ + static constexpr bool appendable = true; +}; + +template<> +void BaseSetting::appendOrSet(PathsInChroot newValue, bool append); + +struct GCSettings : public virtual Config +{ + Setting reservedSize{ + this, + 8 * 1024 * 1024, + "gc-reserved-space", + "Amount of reserved disk space for the garbage collector.", + }; + + Setting keepOutputs{ + this, + false, + "keep-outputs", + R"( + If `true`, the garbage collector keeps the outputs of + non-garbage derivations. If `false` (default), outputs are + deleted unless they are GC roots themselves (or reachable from other + roots). + + In general, outputs must be registered as roots separately. However, + even if the output of a derivation is registered as a root, the + collector still deletes store paths that are used only at build + time (e.g., the C compiler, or source tarballs downloaded from the + network). To prevent it from doing so, set this option to `true`. + )", + {"gc-keep-outputs"}, + }; + + Setting keepDerivations{ + this, + true, + "keep-derivations", + R"( + If `true` (default), the garbage collector keeps the derivations + from which non-garbage store paths were built. If `false`, they are + deleted unless explicitly registered as a root (or reachable from + other roots). + + Keeping derivation around is useful for querying and traceability + (e.g., it allows you to ask with what dependencies or options a + store path was built), so by default this option is on. Turn it off + to save a bit of disk space (or a lot if `keep-outputs` is also + turned on). + )", + {"gc-keep-derivations"}, + }; + + Setting minFree{ + this, + 0, + "min-free", + R"( + When free disk space in `/nix/store` drops below `min-free` during a + build, Nix performs a garbage-collection until `max-free` bytes are + available or there is no more garbage. A value of `0` (the default) + disables this feature. + )", + }; + + // n.b. this is deliberately int64 max rather than uint64 max because + // this goes through the Nix language JSON parser and thus needs to be + // representable in Nix language integers. + Setting maxFree{ + this, + std::numeric_limits::max(), + "max-free", + R"( + When a garbage collection is triggered by the `min-free` option, it + stops as soon as `max-free` bytes are available. The default is + infinity (i.e. delete all garbage). + )", + }; + + Setting minFreeCheckInterval{ + this, + 5, + "min-free-check-interval", + "Number of seconds between checking free disk space.", + }; +}; + +const uint32_t maxIdsPerBuild = +#ifdef __linux__ + 1 << 16 +#else + 1 +#endif + ; + +struct AutoAllocateUidSettings : public virtual Config +{ + Setting startId{ + this, +#ifdef __linux__ + 0x34000000, +#else + 56930, +#endif + "start-id", + "The first UID and GID to use for dynamic ID allocation."}; + + Setting uidCount{ + this, +#ifdef __linux__ + maxIdsPerBuild * 128, +#else + 128, +#endif + "id-count", + "The number of UIDs/GIDs to use for dynamic ID allocation."}; +}; + +/** + * Either about local store or local building + * + * These are things that should not be part of the global settings, but + * should be per-local-store at a minimum. We expose them from + * `settings` with `settings.getLocalSettings()` for now, but we also + * have `localStore.config->getLocalSettings()` as a way to get them + * too. Even though both ways will actually draw from the same global + * variable, we would much prefer if you use the second one, because + * this will prepare the code base to making these *actual*, rather than + * pretend, per-store settings. + */ +struct LocalSettings : public virtual Config, public GCSettings, public AutoAllocateUidSettings +{ + /** + * Get the GC settings. + */ + GCSettings & getGCSettings() + { + return *this; + } + + const GCSettings & getGCSettings() const + { + return *this; + } + + /** + * Get AutoAllocateUidSettings if auto-allocate-uids is enabled. + * @return Pointer to settings if enabled, nullptr otherwise. + */ + const AutoAllocateUidSettings * getAutoAllocateUidSettings() const + { + return autoAllocateUids ? this : nullptr; + } + + Setting fsyncMetadata{ + this, + true, + "fsync-metadata", + R"( + If set to `true`, changes to the Nix store metadata (in + `/nix/var/nix/db`) are synchronously flushed to disk. This improves + robustness in case of system crashes, but reduces performance. The + default is `true`. + )"}; + + Setting fsyncStorePaths{ + this, + false, + "fsync-store-paths", + R"( + Whether to call `fsync()` on store paths before registering them, to + flush them to disk. This improves robustness in case of system crashes, + but reduces performance. The default is `false`. + )"}; + +#ifndef _WIN32 + // FIXME: remove this option, `fsync-store-paths` is faster. + Setting syncBeforeRegistering{ + this, false, "sync-before-registering", "Whether to call `sync()` before registering a path as valid."}; +#endif + + Setting autoOptimiseStore{ + this, + false, + "auto-optimise-store", + R"( + If set to `true`, Nix automatically detects files in the store + that have identical contents, and replaces them with hard links to + a single copy. This saves disk space. If set to `false` (the + default), you can still run `nix-store --optimise` to get rid of + duplicate files. + )"}; + + Setting narBufferSize{ + this, 32 * 1024 * 1024, "nar-buffer-size", "Maximum size of NARs before spilling them to disk."}; + + Setting allowSymlinkedStore{ + this, + false, + "allow-symlinked-store", + R"( + If set to `true`, Nix stops complaining if the store directory + (typically `/nix/store`) contains symlink components. + + This risks making some builds "impure" because builders sometimes + "canonicalise" paths by resolving all symlink components. Problems + occur if those builds are then deployed to machines where /nix/store + resolves to a different location from that of the build machine. You + can enable this setting if you are sure you're not going to do that. + )"}; + + Setting buildUsersGroup{ + this, + "", + "build-users-group", + R"( + This options specifies the Unix group containing the Nix build user + accounts. In multi-user Nix installations, builds should not be + performed by the Nix account since that would allow users to + arbitrarily modify the Nix store and database by supplying specially + crafted builders; and they cannot be performed by the calling user + since that would allow him/her to influence the build result. + + Therefore, if this option is non-empty and specifies a valid group, + builds are performed under the user accounts that are a member + of the group specified here (as listed in `/etc/group`). Those user + accounts should not be used for any other purpose\! + + Nix never runs two builds under the same user account at the + same time. This is to prevent an obvious security hole: a malicious + user writing a Nix expression that modifies the build result of a + legitimate Nix expression being built by another user. Therefore it + is good to have as many Nix build user accounts as you can spare. + (Remember: uids are cheap.) + + The build users should have permission to create files in the Nix + store, but not delete them. Therefore, `/nix/store` should be owned + by the Nix account, its group should be the group specified here, + and its mode should be `1775`. + + If the build users group is empty, builds are 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 + with a nix daemon accessible to untrusted clients. + + Defaults to `nixbld` when running as root, *empty* otherwise. + )", + {}, + false}; + + Setting autoAllocateUids{ + this, + false, + "auto-allocate-uids", + R"( + Whether to select UIDs for builds automatically, instead of using the + users in `build-users-group`. + + UIDs are allocated starting at 872415232 (0x34000000) on Linux and 56930 on macOS. + )", + {}, + true, + Xp::AutoAllocateUids}; + +#ifdef __linux__ + Setting useCgroups{ + this, + false, + "use-cgroups", + R"( + Whether to execute builds inside cgroups. + This is only supported on Linux. + + Cgroups are required and enabled automatically for derivations + that require the `uid-range` system feature. + )"}; +#endif + + Setting impersonateLinux26{ + this, + false, + "impersonate-linux-26", + "Whether to impersonate a Linux 2.6 machine on newer kernels.", + {"build-impersonate-linux-26"}}; + + Setting sandboxMode{ + this, +#ifdef __linux__ + smEnabled +#else + smDisabled +#endif + , + "sandbox", + R"( + If set to `true`, builds are performed in a *sandboxed + environment*, i.e., they're isolated from the normal file system + hierarchy and only see their dependencies in the Nix store, + the temporary build directory, private versions of `/proc`, + `/dev`, `/dev/shm` and `/dev/pts` (on Linux), and the paths + configured with the `sandbox-paths` option. This is useful to + prevent undeclared dependencies on files in directories such as + `/usr/bin`. In addition, on Linux, builds run in private PID, + mount, network, IPC and UTS namespaces to isolate them from other + processes in the system (except that fixed-output derivations do + not run in private network namespace to ensure they can access the + network). + + Currently, sandboxing only work on Linux and macOS. The use of a + sandbox requires that Nix is run as root (so you should use the + "build users" feature to perform the actual builds under different + users than root). + + If this option is set to `relaxed`, then fixed-output derivations + and derivations that have the `__noChroot` attribute set to `true` + do not run in sandboxes. + + The default is `true` on Linux and `false` on all other platforms. + )", + {"build-use-chroot", "build-use-sandbox"}}; + + Setting sandboxPaths{ + this, + {}, + "sandbox-paths", + R"( + A list of paths bind-mounted into Nix sandbox environments. You can + use the syntax `target=source` to mount a path in a different + location in the sandbox; for instance, `/bin=/nix-bin` mounts + the path `/nix-bin` as `/bin` inside the 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` + only be mounted in the sandbox if it exists in the host filesystem. + + If the source is in the Nix store, then its closure is added to + the sandbox as well. + + Depending on how Nix was built, the default value for this option + may be empty or provide `/bin/sh` as a bind-mount of `bash`. + )", + {"build-chroot-dirs", "build-sandbox-paths"}}; + + Setting sandboxFallback{ + this, true, "sandbox-fallback", "Whether to disable sandboxing when the kernel doesn't allow it."}; + +#ifndef _WIN32 + Setting requireDropSupplementaryGroups{ + this, + isRootUser(), + "require-drop-supplementary-groups", + R"( + Following the principle of least privilege, + Nix attempts to drop supplementary groups when building with sandboxing. + + However this can fail under some circumstances. + For example, if the user lacks the `CAP_SETGID` capability. + Search `setgroups(2)` for `EPERM` to find more detailed information on this. + + If you encounter such a failure, setting this option to `false` enables you to ignore it and continue. + But before doing so, you should consider the security implications carefully. + Not dropping supplementary groups means the build sandbox is less restricted than intended. + + This option defaults to `true` when the user is root + (since `root` usually has permissions to call setgroups) + and `false` otherwise. + )"}; +#endif + +#ifdef __linux__ + Setting sandboxShmSize{ + this, + "50%", + "sandbox-dev-shm-size", + R"( + *Linux only* + + This option determines the maximum size of the `tmpfs` filesystem + mounted on `/dev/shm` in Linux sandboxes. For the format, see the + description of the `size` option of `tmpfs` in mount(8). The default + is `50%`. + )"}; +#endif + +#if defined(__linux__) || defined(__FreeBSD__) + Setting sandboxBuildDir{ + this, + "/build", + "sandbox-build-dir", + R"( + *Linux only* + + The build directory inside the sandbox. + + This directory is backed by [`build-dir`](#conf-build-dir) on the host. + )"}; +#endif + + Setting> buildDir{ + this, + std::nullopt, + "build-dir", + R"( + Override the `build-dir` store setting for all stores that have this setting. + + See also the per-store [`build-dir`](@docroot@/store/types/local-store.md#store-local-store-build-dir) setting. + )"}; + + Setting allowedImpureHostPrefixes{ + this, + {}, + "allowed-impure-host-deps", + "Which prefixes to allow derivations to ask for access to (primarily for Darwin)."}; + +#ifdef __APPLE__ + Setting darwinLogSandboxViolations{ + this, + false, + "darwin-log-sandbox-violations", + "Whether to log Darwin sandbox access violations to the system log."}; +#endif + + Setting runDiffHook{ + this, + false, + "run-diff-hook", + R"( + If true, enable the execution of the `diff-hook` program. + + When using the Nix daemon, `run-diff-hook` must be set in the + `nix.conf` configuration file, and cannot be passed at the command + line. + )"}; + +private: + + OptionalPathSetting diffHook{ + this, + std::nullopt, + "diff-hook", + R"( + Absolute path to an executable capable of diffing build + results. The hook is executed if `run-diff-hook` is true, and the + output of a build is known to not be the same. This program is not + executed to determine if two results are the same. + + The diff hook is executed by the same user and group who ran the + build. However, the diff hook does not have write access to the + store path just built. + + The diff hook program receives three parameters: + + 1. A path to the previous build's results + + 2. A path to the current build's results + + 3. The path to the build's derivation + + 4. The path to the build's scratch directory. This directory + exists only if the build was run with `--keep-failed`. + + The stderr and stdout output from the diff hook isn't displayed + to the user. Instead, it prints to the nix-daemon's log. + + When using the Nix daemon, `diff-hook` must be set in the `nix.conf` + configuration file, and cannot be passed at the command line. + )"}; + +public: + + /** + * Get the diff hook path if run-diff-hook is enabled. + * @return Pointer to path if enabled, nullptr otherwise. + */ + const Path * getDiffHook() const + { + if (!runDiffHook.get()) { + return nullptr; + } + return get(diffHook.get()); + } + + Setting preBuildHook{ + this, + "", + "pre-build-hook", + R"( + If set, the path to a program that can set extra derivation-specific + settings for this system. This is used for settings that can't be + captured by the derivation model itself and are too variable between + different versions of the same system to be hard-coded into nix. + + The hook is passed the derivation path and, if sandboxes are + enabled, the sandbox directory. It can then modify the sandbox and + send a series of commands to modify various settings to stdout. The + currently recognized commands are: + + - `extra-sandbox-paths`\ + Pass a list of files and directories to be included in the + sandbox for this build. One entry per line, terminated by an + empty line. Entries have the same format as `sandbox-paths`. + )"}; + +#ifdef __linux__ + Setting filterSyscalls{ + this, + true, + "filter-syscalls", + R"( + Whether to prevent certain dangerous system calls, such as + creation of setuid/setgid files or adding ACLs or extended + attributes. Only disable this if you're aware of the + security implications. + )"}; + + Setting allowNewPrivileges{ + this, + false, + "allow-new-privileges", + R"( + (Linux-specific.) By default, builders on Linux cannot acquire new + privileges by calling setuid/setgid programs or programs that have + file capabilities. For example, programs such as `sudo` or `ping` + should fail. (Note that in sandbox builds, no such programs are + available unless you bind-mount them into the sandbox via the + `sandbox-paths` option.) You can allow the use of such programs by + enabling this option. This is impure and usually undesirable, but + may be useful in certain scenarios (e.g. to spin up containers or + set up userspace network interfaces in tests). + )"}; +#endif + +#if NIX_SUPPORT_ACL + Setting ignoredAcls{ + this, + {"security.selinux", "system.nfs4_acl", "security.csm"}, + "ignored-acls", + R"( + A list of ACLs that should be ignored, normally Nix attempts to + remove all ACLs from files and directories in the Nix store, but + some ACLs like `security.selinux` or `system.nfs4_acl` can't be + removed even by root. Therefore it's best to just ignore them. + )"}; +#endif + + Setting impureEnv{ + this, + {}, + "impure-env", + R"( + A list of items, each in the format of: + + - `name=value`: Set environment variable `name` to `value`. + + If the user is trusted (see `trusted-users` option), when building + a fixed-output derivation, environment variables set in this option + is passed to the builder if they are listed in [`impureEnvVars`](@docroot@/language/advanced-attributes.md#adv-attr-impureEnvVars). + + This option is useful for, e.g., setting `https_proxy` for + fixed-output derivations and in a multi-user Nix installation, or + setting private access tokens when fetching a private repository. + )", + {}, // aliases + true, // document default + Xp::ConfigurableImpureEnv}; + + using ExternalBuilders = std::vector; + + Setting externalBuilders{ + this, + {}, + "external-builders", + R"( + Helper programs that execute derivations. + + The program is passed a JSON document that describes the build environment as the final argument. + The JSON document looks like this: + + { + "args": [ + "-e", + "/nix/store/vj1c3wf9…-source-stdenv.sh", + "/nix/store/shkw4qm9…-default-builder.sh" + ], + "builder": "/nix/store/s1qkj0ph…-bash-5.2p37/bin/bash", + "env": { + "HOME": "/homeless-shelter", + "builder": "/nix/store/s1qkj0ph…-bash-5.2p37/bin/bash", + "nativeBuildInputs": "/nix/store/l31j72f1…-version-check-hook", + "out": "/nix/store/2yx2prgx…-hello-2.12.2" + … + }, + "inputPaths": [ + "/nix/store/14dciax3…-glibc-2.32-54-dev", + "/nix/store/1azs5s8z…-gettext-0.21", + … + ], + "outputs": { + "out": "/nix/store/2yx2prgx…-hello-2.12.2" + }, + "realStoreDir": "/nix/store", + "storeDir": "/nix/store", + "system": "aarch64-linux", + "tmpDir": "/private/tmp/nix-build-hello-2.12.2.drv-0/build", + "tmpDirInSandbox": "/build", + "topTmpDir": "/private/tmp/nix-build-hello-2.12.2.drv-0", + "version": 1 + } + )", + {}, // aliases + true, // document default + // NOTE(cole-h): even though we can make the experimental feature required here, the errors + // are not as good (it just becomes a warning if you try to use this setting without the + // experimental feature) + // + // With this commented out: + // + // error: experimental Nix feature 'external-builders' is disabled; add '--extra-experimental-features + // external-builders' to enable it + // + // With this uncommented: + // + // warning: Ignoring setting 'external-builders' because experimental feature 'external-builders' is not enabled + // error: Cannot build '/nix/store/vwsp4qd8…-opentofu-1.10.2.drv'. + // Reason: required system or feature not available + // Required system: 'aarch64-linux' with features {} + // Current system: 'aarch64-darwin' with features {apple-virt, benchmark, big-parallel, nixos-test} + // Xp::ExternalBuilders + }; +}; + +} // namespace nix diff --git a/src/libstore/include/nix/store/local-store.hh b/src/libstore/include/nix/store/local-store.hh index ef24fe05925..5e99ec20931 100644 --- a/src/libstore/include/nix/store/local-store.hh +++ b/src/libstore/include/nix/store/local-store.hh @@ -31,7 +31,7 @@ struct OptimiseStats uint64_t bytesFreed = 0; }; -struct GCSettings; +struct LocalSettings; struct LocalBuildStoreConfig : virtual LocalFSStoreConfig { @@ -67,6 +67,12 @@ private: See also the global [`build-dir`](@docroot@/command-ref/conf-file.md#conf-build-dir) setting. )"}; public: + /** + * For now, this just grabs the global local settings, but by having this method we get ready for these being + * per-store settings instead. + */ + const LocalSettings & getLocalSettings() const; + Path getBuildDir() const; }; @@ -87,12 +93,6 @@ private: bool getDefaultRequireSigs(); public: - /** - * For now, this just grabs the global GC settings, but by having this method we get ready for these being per-store - * settings instead. - */ - const GCSettings & getGCSettings() const; - Setting requireSigs{ this, getDefaultRequireSigs(), diff --git a/src/libstore/include/nix/store/meson.build b/src/libstore/include/nix/store/meson.build index c7026818ea7..e3dafbc1227 100644 --- a/src/libstore/include/nix/store/meson.build +++ b/src/libstore/include/nix/store/meson.build @@ -51,6 +51,7 @@ headers = [ config_pub_h ] + files( 'local-binary-cache-store.hh', 'local-fs-store.hh', 'local-overlay-store.hh', + 'local-settings.hh', 'local-store.hh', 'log-store.hh', 'machines.hh', diff --git a/src/libstore/linux/personality.cc b/src/libstore/linux/personality.cc index 7b0b39007af..f669c9fa127 100644 --- a/src/libstore/linux/personality.cc +++ b/src/libstore/linux/personality.cc @@ -1,5 +1,6 @@ #include "nix/store/personality.hh" -#include "nix/store/globals.hh" +#include "nix/store/config.hh" +#include "nix/util/error.hh" #include #include diff --git a/src/libstore/local-store.cc b/src/libstore/local-store.cc index 91c3867e5b5..7263d73ec47 100644 --- a/src/libstore/local-store.cc +++ b/src/libstore/local-store.cc @@ -6,7 +6,6 @@ #include "nix/store/worker-protocol.hh" #include "nix/store/derivations.hh" #include "nix/store/realisation.hh" -#include "nix/store/nar-info.hh" #include "nix/store/references.hh" #include "nix/util/callback.hh" #include "nix/util/topo-sort.hh" @@ -16,12 +15,9 @@ #include "nix/store/posix-fs-canonicalise.hh" #include "nix/util/posix-source-accessor.hh" #include "nix/store/keys.hh" -#include "nix/util/url.hh" #include "nix/util/users.hh" -#include "nix/store/store-open.hh" #include "nix/store/store-registration.hh" -#include #include #include @@ -33,7 +29,6 @@ #include #include #include -#include #include #include @@ -74,11 +69,16 @@ std::string LocalStoreConfig::doc() ; } +const LocalSettings & LocalBuildStoreConfig::getLocalSettings() const + +{ + return settings.getLocalSettings(); +} + Path LocalBuildStoreConfig::getBuildDir() const { - return settings.buildDir.get().has_value() ? *settings.buildDir.get() - : buildDir.get().has_value() ? *buildDir.get() - : stateDir.get() + "/builds"; + auto & bd = getLocalSettings().buildDir.get(); + return bd.has_value() ? *bd : buildDir.get().has_value() ? *buildDir.get() : stateDir.get() + "/builds"; } ref LocalStore::Config::openStore() const @@ -140,7 +140,8 @@ LocalStore::LocalStore(ref config) createDirs(tempRootsDir); createDirs(dbDir); Path gcRootsDir = config->stateDir + "/gcroots"; - const auto & gcSettings = settings.getGCSettings(); + const auto & localSettings = config->getLocalSettings(); + const auto & gcSettings = localSettings.getGCSettings(); if (!pathExists(gcRootsDir)) { createDirs(gcRootsDir); replaceSymlink(profilesDir, gcRootsDir + "/profiles"); @@ -159,13 +160,14 @@ LocalStore::LocalStore(ref config) #ifndef _WIN32 /* Optionally, create directories and set permissions for a multi-user install. */ - if (isRootUser() && settings.buildUsersGroup != "") { + if (isRootUser() && localSettings.buildUsersGroup != "") { mode_t perm = 01775; - struct group * gr = getgrnam(settings.buildUsersGroup.get().c_str()); + struct group * gr = getgrnam(localSettings.buildUsersGroup.get().c_str()); if (!gr) printError( - "warning: the group '%1%' specified in 'build-users-group' does not exist", settings.buildUsersGroup); + "warning: the group '%1%' specified in 'build-users-group' does not exist", + localSettings.buildUsersGroup); else if (!config->readOnly) { auto st = stat(config->realStoreDir.get()); @@ -179,7 +181,7 @@ LocalStore::LocalStore(ref config) #endif /* Ensure that the store and its parents are not symlinks. */ - if (!settings.allowSymlinkedStore) { + if (!localSettings.allowSymlinkedStore) { std::filesystem::path path = config->realStoreDir.get(); std::filesystem::path root = path.root_path(); while (path != root) { @@ -453,11 +455,6 @@ LocalStore::~LocalStore() } } -const GCSettings & LocalStoreConfig::getGCSettings() const -{ - return settings.getGCSettings(); -} - StoreReference LocalStoreConfig::getReference() const { auto params = getQueryParams(); @@ -520,7 +517,7 @@ void LocalStore::openDB(State & state, bool create) should be safe enough. If the user asks for it, don't sync at all. This can cause database corruption if the system crashes. */ - std::string syncMode = settings.fsyncMetadata ? "normal" : "off"; + std::string syncMode = config->getLocalSettings().fsyncMetadata ? "normal" : "off"; db.exec("pragma synchronous = " + syncMode); /* Set the SQLite journal mode. WAL mode is fastest, so it's the @@ -902,7 +899,7 @@ void LocalStore::registerValidPaths(const ValidPathInfos & infos) be fsync-ed. So some may want to fsync them before registering the validity, at the expense of some speed of the path registering operation. */ - if (settings.syncBeforeRegistering) + if (config->getLocalSettings().syncBeforeRegistering) sync(); #endif @@ -1027,7 +1024,7 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source, RepairF TeeSource wrapperSource{source, hashSink}; narRead = true; - restorePath(realPath, wrapperSource, settings.fsyncStorePaths); + restorePath(realPath, wrapperSource, config->getLocalSettings().fsyncStorePaths); auto hashResult = hashSink.finish(); @@ -1083,11 +1080,11 @@ void LocalStore::addToStore(const ValidPathInfo & info, Source & source, RepairF autoGC(); - canonicalisePathMetaData(realPath, {NIX_WHEN_SUPPORT_ACLS(settings.ignoredAcls)}); + canonicalisePathMetaData(realPath, {NIX_WHEN_SUPPORT_ACLS(config->getLocalSettings().ignoredAcls)}); optimisePath(realPath, repair); // FIXME: combine with hashPath() - if (settings.fsyncStorePaths) { + if (config->getLocalSettings().fsyncStorePaths) { recursiveSync(realPath); syncParent(realPath); } @@ -1115,6 +1112,7 @@ StorePath LocalStore::addToStoreFromDump( /* For computing the store path. */ auto hashSink = std::make_unique(hashAlgo); TeeSource source{source0, *hashSink}; + const LocalSettings & localSettings = config->getLocalSettings(); /* Read the source path into memory, but only if it's up to narBufferSize bytes. If it's larger, write it to a temporary @@ -1138,10 +1136,10 @@ StorePath LocalStore::addToStoreFromDump( /* Fill out buffer, and decide whether we are working strictly in memory based on whether we break out because the buffer is full or the original source is empty */ - while (dump.size() < settings.narBufferSize) { + while (dump.size() < localSettings.narBufferSize) { auto oldSize = dump.size(); constexpr size_t chunkSize = 65536; - auto want = std::min(chunkSize, settings.narBufferSize - oldSize); + auto want = std::min(chunkSize, localSettings.narBufferSize - oldSize); if (auto tmp = realloc(dumpBuffer.get(), oldSize + want)) { dumpBuffer.release(); dumpBuffer.reset((char *) tmp); @@ -1178,7 +1176,7 @@ StorePath LocalStore::addToStoreFromDump( delTempDir = std::make_unique(tempDir); tempPath = tempDir / "x"; - restorePath(tempPath.string(), bothSource, dumpMethod, settings.fsyncStorePaths); + restorePath(tempPath.string(), bothSource, dumpMethod, localSettings.fsyncStorePaths); dumpBuffer.reset(); dump = {}; @@ -1222,7 +1220,7 @@ StorePath LocalStore::addToStoreFromDump( switch (fim) { case FileIngestionMethod::Flat: case FileIngestionMethod::NixArchive: - restorePath(realPath, dumpSource, (FileSerialisationMethod) fim, settings.fsyncStorePaths); + restorePath(realPath, dumpSource, (FileSerialisationMethod) fim, localSettings.fsyncStorePaths); break; case FileIngestionMethod::Git: // doesn't correspond to serialization method, so @@ -1244,11 +1242,11 @@ StorePath LocalStore::addToStoreFromDump( } canonicalisePathMetaData( - realPath, {NIX_WHEN_SUPPORT_ACLS(settings.ignoredAcls)}); // FIXME: merge into restorePath + realPath, {NIX_WHEN_SUPPORT_ACLS(localSettings.ignoredAcls)}); // FIXME: merge into restorePath optimisePath(realPath, repair); - if (settings.fsyncStorePaths) { + if (localSettings.fsyncStorePaths) { recursiveSync(realPath); syncParent(realPath); } diff --git a/src/libstore/optimise-store.cc b/src/libstore/optimise-store.cc index 0a10899459c..efdf748826f 100644 --- a/src/libstore/optimise-store.cc +++ b/src/libstore/optimise-store.cc @@ -326,7 +326,7 @@ void LocalStore::optimisePath(const Path & path, RepairFlag repair) OptimiseStats stats; InodeHash inodeHash; - if (settings.autoOptimiseStore) + if (config->getLocalSettings().autoOptimiseStore) optimisePath_(nullptr, stats, path, inodeHash, repair); } diff --git a/src/libstore/unix/build/chroot-derivation-builder.cc b/src/libstore/unix/build/chroot-derivation-builder.cc index 7e9e13d4a73..f48d7fdce4b 100644 --- a/src/libstore/unix/build/chroot-derivation-builder.cc +++ b/src/libstore/unix/build/chroot-derivation-builder.cc @@ -44,7 +44,7 @@ struct ChrootDerivationBuilder : virtual DerivationBuilderImpl { /* In a sandbox, for determinism, always use the same temporary directory. */ - return settings.sandboxBuildDir.get(); + return store.config->getLocalSettings().sandboxBuildDir.get(); } virtual gid_t sandboxGid() @@ -95,7 +95,9 @@ struct ChrootDerivationBuilder : virtual DerivationBuilderImpl chownToBuilder(chrootRootDir / "etc"); if (drvOptions.useUidRange(drv) && (!buildUser || buildUser->getUIDCount() < 65536)) - throw Error("feature 'uid-range' requires the setting '%s' to be enabled", settings.autoAllocateUids.name); + throw Error( + "feature 'uid-range' requires the setting '%s' to be enabled", + store.config->getLocalSettings().autoAllocateUids.name); /* Declare the build user's group so that programs get a consistent view of the system (e.g., "id -gn"). */ diff --git a/src/libstore/unix/build/darwin-derivation-builder.cc b/src/libstore/unix/build/darwin-derivation-builder.cc index 36de96e9414..336e402d0b5 100644 --- a/src/libstore/unix/build/darwin-derivation-builder.cc +++ b/src/libstore/unix/build/darwin-derivation-builder.cc @@ -96,7 +96,7 @@ struct DarwinDerivationBuilder : DerivationBuilderImpl /* Violations will go to the syslog if you set this. Unfortunately the destination does not appear to be * configurable */ - if (settings.darwinLogSandboxViolations) { + if (store.config->getLocalSettings().darwinLogSandboxViolations) { sandboxProfile += "(deny default)\n"; } else { sandboxProfile += "(deny default (with no-log))\n"; diff --git a/src/libstore/unix/build/derivation-builder.cc b/src/libstore/unix/build/derivation-builder.cc index eeba6a8834f..f85c79e63ff 100644 --- a/src/libstore/unix/build/derivation-builder.cc +++ b/src/libstore/unix/build/derivation-builder.cc @@ -4,7 +4,6 @@ #include "nix/util/processes.hh" #include "nix/store/builtins.hh" #include "nix/store/path-references.hh" -#include "nix/util/finally.hh" #include "nix/util/util.hh" #include "nix/util/archive.hh" #include "nix/util/git.hh" @@ -20,8 +19,6 @@ #include "nix/store/build/derivation-env-desugar.hh" #include "nix/util/terminal.hh" -#include - #include #include #include @@ -86,6 +83,8 @@ class DerivationBuilderImpl : public DerivationBuilder, public DerivationBuilder LocalStore & store; + const LocalSettings & localSettings = store.config->getLocalSettings(); + std::unique_ptr miscMethods; public: @@ -236,7 +235,7 @@ class DerivationBuilderImpl : public DerivationBuilder, public DerivationBuilder */ virtual std::unique_ptr getBuildUser() { - return acquireUserLock(settings.buildUsersGroup, 1, false); + return acquireUserLock(localSettings, 1, false); } /** @@ -695,7 +694,7 @@ static void checkNotWorldWritable(std::filesystem::path path) std::optional DerivationBuilderImpl::startBuild() { - if (useBuildUsers()) { + if (useBuildUsers(localSettings)) { if (!buildUser) buildUser = getBuildUser(); @@ -854,7 +853,7 @@ PathsInChroot DerivationBuilderImpl::getPathsInSandbox() } pathsInChroot[tmpDirInSandbox()] = {.source = tmpDir}; - PathSet allowedPaths = settings.allowedImpureHostPrefixes; + PathSet allowedPaths = localSettings.allowedImpureHostPrefixes; /* This works like the above, except on a per-derivation level */ auto impurePaths = drvOptions.impureHostDeps; @@ -884,13 +883,13 @@ PathsInChroot DerivationBuilderImpl::getPathsInSandbox() pathsInChroot[i] = {i, true}; } - if (settings.preBuildHook != "") { - printMsg(lvlChatty, "executing pre-build hook '%1%'", settings.preBuildHook); + if (localSettings.preBuildHook != "") { + printMsg(lvlChatty, "executing pre-build hook '%1%'", localSettings.preBuildHook); enum BuildHookState { stBegin, stExtraChrootDirs }; auto state = stBegin; - auto lines = runProgram(settings.preBuildHook, false, getPreBuildHookArgs()); + auto lines = runProgram(localSettings.preBuildHook, false, getPreBuildHookArgs()); auto lastPos = std::string::size_type{0}; for (auto nlPos = lines.find('\n'); nlPos != std::string::npos; nlPos = lines.find('\n', lastPos)) { auto line = lines.substr(lastPos, nlPos - lastPos); @@ -1085,7 +1084,7 @@ void DerivationBuilderImpl::initEnv() fixed-output derivations is by definition pure (since we already know the cryptographic hash of the output). */ if (!derivationType.isSandboxed()) { - auto & impureEnv = settings.impureEnv.get(); + auto & impureEnv = localSettings.impureEnv.get(); if (!impureEnv.empty()) experimentalFeatureSettings.require(Xp::ConfigurableImpureEnv); @@ -1464,7 +1463,7 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs() #ifndef _WIN32 .uidRange = buildUser ? std::optional(buildUser->getUIDRange()) : std::nullopt, #endif - NIX_WHEN_SUPPORT_ACLS(settings.ignoredAcls)}, + NIX_WHEN_SUPPORT_ACLS(localSettings.ignoredAcls)}, inodesSeen); bool discardReferences = false; @@ -1594,7 +1593,7 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs() // builder UIDs are already dealt with .uidRange = std::nullopt, #endif - NIX_WHEN_SUPPORT_ACLS(settings.ignoredAcls)}, + NIX_WHEN_SUPPORT_ACLS(localSettings.ignoredAcls)}, inodesSeen); } }; @@ -1759,7 +1758,7 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs() // builder UIDs are already dealt with .uidRange = std::nullopt, #endif - NIX_WHEN_SUPPORT_ACLS(settings.ignoredAcls)}, + NIX_WHEN_SUPPORT_ACLS(localSettings.ignoredAcls)}, inodesSeen); /* Calculate where we'll move the output files. In the checking case we @@ -1805,7 +1804,7 @@ SingleDrvOutputs DerivationBuilderImpl::registerOutputs() if (store.isValidPath(newInfo.path)) { ValidPathInfo oldInfo(*store.queryPathInfo(newInfo.path)); if (newInfo.narHash != oldInfo.narHash) { - auto * diffHook = settings.getDiffHook(); + auto * diffHook = localSettings.getDiffHook(); if (diffHook || settings.keepFailed) { auto dst = store.toRealPath(finalDestPath + ".check"); deletePath(dst); @@ -2011,10 +2010,11 @@ std::unique_ptr makeDerivationBuild LocalStore & store, std::unique_ptr miscMethods, DerivationBuilderParams params) { bool useSandbox = false; + const LocalSettings & localSettings = store.config->getLocalSettings(); /* Are we doing a sandboxed build? */ { - if (settings.sandboxMode == smEnabled) { + if (localSettings.sandboxMode == smEnabled) { if (params.drvOptions.noChroot) throw Error( "derivation '%s' has '__noChroot' set, " @@ -2028,9 +2028,9 @@ std::unique_ptr makeDerivationBuild store.printStorePath(params.drvPath)); #endif useSandbox = true; - } else if (settings.sandboxMode == smDisabled) + } else if (localSettings.sandboxMode == smDisabled) useSandbox = false; - else if (settings.sandboxMode == smRelaxed) + else if (localSettings.sandboxMode == smRelaxed) // FIXME: cache derivationType useSandbox = params.drv.type().isSandboxed() && !params.drvOptions.noChroot; } @@ -2045,7 +2045,7 @@ std::unique_ptr makeDerivationBuild #ifdef __linux__ if (useSandbox && !mountAndPidNamespacesSupported()) { - if (!settings.sandboxFallback) + if (!localSettings.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"); diff --git a/src/libstore/unix/build/linux-derivation-builder.cc b/src/libstore/unix/build/linux-derivation-builder.cc index 286ead922c7..1ad4ac9a3f9 100644 --- a/src/libstore/unix/build/linux-derivation-builder.cc +++ b/src/libstore/unix/build/linux-derivation-builder.cc @@ -1,5 +1,6 @@ #ifdef __linux__ +# include "nix/store/globals.hh" # include "nix/store/personality.hh" # include "nix/util/cgroup.hh" # include "nix/util/linux-namespaces.hh" @@ -24,9 +25,9 @@ namespace nix { -static void setupSeccomp() +static void setupSeccomp(const LocalSettings & localSettings) { - if (!settings.filterSyscalls) + if (!localSettings.filterSyscalls) return; # if HAVE_SECCOMP @@ -111,7 +112,7 @@ static void setupSeccomp() || seccomp_rule_add(ctx, SCMP_ACT_ERRNO(ENOTSUP), SCMP_SYS(fsetxattr), 0) != 0) throw SysError("unable to add seccomp rule"); - if (seccomp_attr_set(ctx, SCMP_FLTATR_CTL_NNP, settings.allowNewPrivileges ? 0 : 1) != 0) + if (seccomp_attr_set(ctx, SCMP_FLTATR_CTL_NNP, localSettings.allowNewPrivileges ? 0 : 1) != 0) throw SysError("unable to set 'no new privileges' seccomp attribute"); if (seccomp_load(ctx) != 0) @@ -161,11 +162,13 @@ struct LinuxDerivationBuilder : virtual DerivationBuilderImpl void enterChroot() override { - setupSeccomp(); + auto & localSettings = store.config->getLocalSettings(); + + setupSeccomp(localSettings); linux::setPersonality({ .system = drv.platform, - .impersonateLinux26 = settings.impersonateLinux26, + .impersonateLinux26 = localSettings.impersonateLinux26, }); } }; @@ -218,12 +221,12 @@ struct ChrootLinuxDerivationBuilder : ChrootDerivationBuilder, LinuxDerivationBu std::unique_ptr getBuildUser() override { - return acquireUserLock(settings.buildUsersGroup, drvOptions.useUidRange(drv) ? 65536 : 1, true); + return acquireUserLock(store.config->getLocalSettings(), drvOptions.useUidRange(drv) ? 65536 : 1, true); } void prepareUser() override { - if ((buildUser && buildUser->getUIDCount() != 1) || settings.useCgroups) { + if ((buildUser && buildUser->getUIDCount() != 1) || store.config->getLocalSettings().useCgroups) { experimentalFeatureSettings.require(Xp::Cgroups); /* If we're running from the daemon, then this will return the @@ -343,7 +346,7 @@ struct ChrootLinuxDerivationBuilder : ChrootDerivationBuilder, LinuxDerivationBu if (setgroups(0, 0) == -1) { if (errno != EPERM) throw SysError("setgroups failed"); - if (settings.requireDropSupplementaryGroups) + if (store.config->getLocalSettings().requireDropSupplementaryGroups) throw Error( "setgroups failed. Set the require-drop-supplementary-groups option to false to skip this step."); } @@ -427,7 +430,7 @@ struct ChrootLinuxDerivationBuilder : ChrootDerivationBuilder, LinuxDerivationBu "nobody:x:65534:65534:Nobody:/:/noshell\n", sandboxUid(), sandboxGid(), - settings.sandboxBuildDir)); + store.config->getLocalSettings().sandboxBuildDir)); /* Save the mount- and user namespace of the child. We have to do this *before* the child does a chroot. */ @@ -617,7 +620,7 @@ struct ChrootLinuxDerivationBuilder : ChrootDerivationBuilder, LinuxDerivationBu (chrootRootDir / "dev" / "shm").c_str(), "tmpfs", 0, - fmt("size=%s", settings.sandboxShmSize).c_str()) + fmt("size=%s", store.config->getLocalSettings().sandboxShmSize).c_str()) == -1) throw SysError("mounting /dev/shm"); diff --git a/src/libstore/unix/include/nix/store/user-lock.hh b/src/libstore/unix/include/nix/store/user-lock.hh index 1f7e2af31a4..707c7c38a9b 100644 --- a/src/libstore/unix/include/nix/store/user-lock.hh +++ b/src/libstore/unix/include/nix/store/user-lock.hh @@ -7,6 +7,8 @@ namespace nix { +struct LocalSettings; + struct UserLock { virtual ~UserLock() {} @@ -36,8 +38,8 @@ struct UserLock * Acquire a user lock for a UID range of size `nrIds`. Note that this * may return nullptr if no user is available. */ -std::unique_ptr acquireUserLock(const std::string & userGroup, uid_t nrIds, bool useUserNamespace); +std::unique_ptr acquireUserLock(const LocalSettings & localSettings, uid_t nrIds, bool useUserNamespace); -bool useBuildUsers(); +bool useBuildUsers(const LocalSettings &); } // namespace nix diff --git a/src/libstore/unix/user-lock.cc b/src/libstore/unix/user-lock.cc index c324addac46..7f4881ffc2f 100644 --- a/src/libstore/unix/user-lock.cc +++ b/src/libstore/unix/user-lock.cc @@ -214,26 +214,26 @@ struct AutoUserLock : UserLock } }; -std::unique_ptr acquireUserLock(const std::string & userGroup, uid_t nrIds, bool useUserNamespace) +std::unique_ptr acquireUserLock(const LocalSettings & localSettings, uid_t nrIds, bool useUserNamespace) { - if (auto * uidSettings = settings.getAutoAllocateUidSettings()) { + if (auto * uidSettings = localSettings.getAutoAllocateUidSettings()) { auto userPoolDir = std::filesystem::path{settings.nixStateDir} / "userpool2"; createDirs(userPoolDir); - return AutoUserLock::acquire(userPoolDir, userGroup, nrIds, useUserNamespace, *uidSettings); + return AutoUserLock::acquire(userPoolDir, localSettings.buildUsersGroup, nrIds, useUserNamespace, *uidSettings); } else { auto userPoolDir = std::filesystem::path{settings.nixStateDir} / "userpool"; createDirs(userPoolDir); - return SimpleUserLock::acquire(userPoolDir, userGroup); + return SimpleUserLock::acquire(userPoolDir, localSettings.buildUsersGroup); } } -bool useBuildUsers() +bool useBuildUsers(const LocalSettings & localSettings) { #ifdef __linux__ - static bool b = (settings.buildUsersGroup != "" || settings.autoAllocateUids) && isRootUser(); + static bool b = (localSettings.buildUsersGroup != "" || localSettings.autoAllocateUids) && isRootUser(); return b; #elif defined(__APPLE__) || defined(__FreeBSD__) - static bool b = settings.buildUsersGroup != "" && isRootUser(); + static bool b = localSettings.buildUsersGroup != "" && isRootUser(); return b; #else return false; diff --git a/src/nix/nix-store/nix-store.cc b/src/nix/nix-store/nix-store.cc index e0c5c6b7a78..663325a93ee 100644 --- a/src/nix/nix-store/nix-store.cc +++ b/src/nix/nix-store/nix-store.cc @@ -502,7 +502,7 @@ static void opQuery(Strings opFlags, Strings opArgs) args.insert(p); StorePathSet referrers; - auto & gcSettings = settings.getGCSettings(); + auto & gcSettings = settings.getLocalSettings().getGCSettings(); store->computeFSClosure(args, referrers, true, gcSettings.keepOutputs, gcSettings.keepDerivations); auto & gcStore = require(*store); @@ -589,7 +589,8 @@ static void registerValidity(bool reregister, bool hashGiven, bool canonicalise) /* !!! races */ if (canonicalise) canonicalisePathMetaData( - store->printStorePath(info->path), {NIX_WHEN_SUPPORT_ACLS(settings.ignoredAcls)}); + store->printStorePath(info->path), + {NIX_WHEN_SUPPORT_ACLS(settings.getLocalSettings().ignoredAcls)}); if (!hashGiven) { HashResult hash = hashPath( {store->requireStoreObjectAccessor(info->path, /*requireValidPath=*/false)}, @@ -921,7 +922,7 @@ static void opServe(Strings opFlags, Strings opArgs) // checked that `nrRepeats` in fact is 0, so we can safely // ignore this without doing something other than what the // client asked for. - settings.runDiffHook = true; + settings.getLocalSettings().runDiffHook = true; } if (GET_PROTOCOL_MINOR(clientVersion) >= 7) { settings.keepFailed = options.keepFailed; diff --git a/src/nix/run.cc b/src/nix/run.cc index 3f09caf6412..c8636f7585d 100644 --- a/src/nix/run.cc +++ b/src/nix/run.cc @@ -98,7 +98,7 @@ void execProgramInStore( if (system) linux::setPersonality({ .system = *system, - .impersonateLinux26 = settings.impersonateLinux26, + .impersonateLinux26 = settings.getLocalSettings().impersonateLinux26, }); #endif @@ -256,7 +256,7 @@ void chrootHelper(int argc, char ** argv) if (system != "") linux::setPersonality({ .system = system, - .impersonateLinux26 = settings.impersonateLinux26, + .impersonateLinux26 = settings.getLocalSettings().impersonateLinux26, }); # endif diff --git a/src/nix/unix/daemon.cc b/src/nix/unix/daemon.cc index f45079e0405..8012f3118b3 100644 --- a/src/nix/unix/daemon.cc +++ b/src/nix/unix/daemon.cc @@ -287,7 +287,7 @@ static std::pair> authPeer(const PeerInf if (matchUser(user, group, trustedUsers)) trusted = Trusted; - if ((!trusted && !matchUser(user, group, allowedUsers)) || group == settings.buildUsersGroup) + if ((!trusted && !matchUser(user, group, allowedUsers)) || group == settings.getLocalSettings().buildUsersGroup) throw Error("user '%1%' is not allowed to connect to the Nix daemon", user.value_or("")); return {trusted, std::move(user)}; @@ -336,7 +336,7 @@ static void daemonLoop(std::optional forceTrustClientOpt) setSigChldAction(true); #ifdef __linux__ - if (settings.useCgroups) { + if (settings.getLocalSettings().useCgroups) { experimentalFeatureSettings.require(Xp::Cgroups); // This also sets the root cgroup to the current one.