From 264d0a25ffcac6272484b934049007204e9c5844 Mon Sep 17 00:00:00 2001 From: Ross Smyth <18294397+RossSmyth@users.noreply.github.com> Date: Thu, 21 May 2026 12:28:20 -0400 Subject: [PATCH 1/2] buildFHSEnvChroot: Remove Deprecation period has been a few years. Let's chuck it. --- .../build-fhsenv-chroot/chrootenv/default.nix | 27 -- .../chrootenv/src/chrootenv.c | 169 ---------- .../chrootenv/src/meson.build | 5 - .../build-fhsenv-chroot/default.nix | 87 ------ .../build-support/build-fhsenv-chroot/env.nix | 290 ------------------ pkgs/top-level/aliases.nix | 1 + pkgs/top-level/all-packages.nix | 1 - 7 files changed, 1 insertion(+), 579 deletions(-) delete mode 100644 pkgs/build-support/build-fhsenv-chroot/chrootenv/default.nix delete mode 100644 pkgs/build-support/build-fhsenv-chroot/chrootenv/src/chrootenv.c delete mode 100644 pkgs/build-support/build-fhsenv-chroot/chrootenv/src/meson.build delete mode 100644 pkgs/build-support/build-fhsenv-chroot/default.nix delete mode 100644 pkgs/build-support/build-fhsenv-chroot/env.nix diff --git a/pkgs/build-support/build-fhsenv-chroot/chrootenv/default.nix b/pkgs/build-support/build-fhsenv-chroot/chrootenv/default.nix deleted file mode 100644 index 194e77a67a43..000000000000 --- a/pkgs/build-support/build-fhsenv-chroot/chrootenv/default.nix +++ /dev/null @@ -1,27 +0,0 @@ -{ - lib, - stdenv, - meson, - ninja, - pkg-config, - glib, -}: - -stdenv.mkDerivation { - name = "chrootenv"; - src = ./src; - - nativeBuildInputs = [ - meson - ninja - pkg-config - ]; - buildInputs = [ glib ]; - - meta = { - description = "Setup mount/user namespace for FHS emulation"; - license = lib.licenses.mit; - maintainers = [ ]; - platforms = lib.platforms.linux; - }; -} diff --git a/pkgs/build-support/build-fhsenv-chroot/chrootenv/src/chrootenv.c b/pkgs/build-support/build-fhsenv-chroot/chrootenv/src/chrootenv.c deleted file mode 100644 index c109d7297e17..000000000000 --- a/pkgs/build-support/build-fhsenv-chroot/chrootenv/src/chrootenv.c +++ /dev/null @@ -1,169 +0,0 @@ -#define _GNU_SOURCE - -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include - -#define fail(s, err) g_error("%s: %s: %s", __func__, s, g_strerror(err)) -#define fail_if(expr) \ - if (expr) \ - fail(#expr, errno); - -const gchar *bind_blacklist[] = {"bin", "etc", "host", "real-host", "usr", "lib", "lib64", "lib32", "sbin", "opt", NULL}; - -int pivot_root(const char *new_root, const char *put_old) { - return syscall(SYS_pivot_root, new_root, put_old); -} - -void mount_tmpfs(const gchar *target) { - fail_if(mount("none", target, "tmpfs", 0, NULL)); -} - -void bind_mount(const gchar *source, const gchar *target) { - fail_if(g_mkdir(target, 0755)); - fail_if(mount(source, target, NULL, MS_BIND | MS_REC, NULL)); -} - -const gchar *create_tmpdir() { - gchar *prefix = - g_build_filename(g_get_tmp_dir(), "chrootenvXXXXXX", NULL); - fail_if(!g_mkdtemp_full(prefix, 0755)); - return prefix; -} - -void pivot_host(const gchar *guest) { - g_autofree gchar *point = g_build_filename(guest, "host", NULL); - fail_if(g_mkdir(point, 0755)); - fail_if(pivot_root(guest, point)); -} - -void bind_mount_item(const gchar *host, const gchar *guest, const gchar *name) { - g_autofree gchar *source = g_build_filename(host, name, NULL); - g_autofree gchar *target = g_build_filename(guest, name, NULL); - - if (G_LIKELY(g_file_test(source, G_FILE_TEST_IS_DIR))) - bind_mount(source, target); -} - -void bind(const gchar *host, const gchar *guest) { - mount_tmpfs(guest); - - pivot_host(guest); - - g_autofree gchar *host_dir = g_build_filename("/host", host, NULL); - - g_autoptr(GError) err = NULL; - g_autoptr(GDir) dir = g_dir_open(host_dir, 0, &err); - - if (err != NULL) - fail("g_dir_open", errno); - - const gchar *item; - - while ((item = g_dir_read_name(dir))) - if (!g_strv_contains(bind_blacklist, item)) - bind_mount_item(host_dir, "/", item); -} - -void spit(const char *path, char *fmt, ...) { - va_list args; - va_start(args, fmt); - - FILE *f = g_fopen(path, "w"); - - if (f == NULL) - fail("g_fopen", errno); - - g_vfprintf(f, fmt, args); - fclose(f); -} - -int main(gint argc, gchar **argv) { - const gchar *self = *argv++; - - if (argc < 2) { - g_message("%s command [arguments...]", self); - return 1; - } - - g_autofree const gchar *prefix = create_tmpdir(); - - pid_t cpid = fork(); - - if (cpid < 0) - fail("fork", errno); - - else if (cpid == 0) { - uid_t uid = getuid(); - gid_t gid = getgid(); - - int namespaces = CLONE_NEWNS; - if (uid != 0) { - namespaces |= CLONE_NEWUSER; - } - if (unshare(namespaces) < 0) { - int unshare_errno = errno; - - g_message("Requires Linux version >= 3.19 built with CONFIG_USER_NS"); - if (g_file_test("/proc/sys/kernel/unprivileged_userns_clone", - G_FILE_TEST_EXISTS)) - g_message("Run: sudo sysctl -w kernel.unprivileged_userns_clone=1"); - - fail("unshare", unshare_errno); - } - - // hide all mounts we do from the parent - fail_if(mount(0, "/", 0, MS_SLAVE | MS_REC, 0)); - - if (uid != 0) { - spit("/proc/self/setgroups", "deny"); - spit("/proc/self/uid_map", "%d %d 1", uid, uid); - spit("/proc/self/gid_map", "%d %d 1", gid, gid); - } - - // If there is a /host directory, assume this is nested chrootenv and use it as host instead. - gboolean nested_host = g_file_test("/host", G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR); - g_autofree const gchar *host = nested_host ? "/host" : "/"; - - bind(host, prefix); - - // Replace /host by an actual (inner) /host. - if (nested_host) { - fail_if(g_mkdir("/real-host", 0755)); - fail_if(mount("/host/host", "/real-host", NULL, MS_BIND | MS_REC, NULL)); - // For some reason umount("/host") returns EBUSY even immediately after - // pivot_root. We detach it at least to keep `/proc/mounts` from blowing - // up in nested cases. - fail_if(umount2("/host", MNT_DETACH)); - fail_if(mount("/real-host", "/host", NULL, MS_MOVE, NULL)); - fail_if(rmdir("/real-host")); - } - - fail_if(chdir("/")); - fail_if(execvp(*argv, argv)); - } - - else { - int status; - - fail_if(waitpid(cpid, &status, 0) != cpid); - fail_if(rmdir(prefix)); - - if (WIFEXITED(status)) - return WEXITSTATUS(status); - - else if (WIFSIGNALED(status)) - kill(getpid(), WTERMSIG(status)); - - return 1; - } -} diff --git a/pkgs/build-support/build-fhsenv-chroot/chrootenv/src/meson.build b/pkgs/build-support/build-fhsenv-chroot/chrootenv/src/meson.build deleted file mode 100644 index 6d0770a0dc4a..000000000000 --- a/pkgs/build-support/build-fhsenv-chroot/chrootenv/src/meson.build +++ /dev/null @@ -1,5 +0,0 @@ -project('chrootenv', 'c') - -glib = dependency('glib-2.0') - -executable('chrootenv', 'chrootenv.c', dependencies: [glib], install: true) diff --git a/pkgs/build-support/build-fhsenv-chroot/default.nix b/pkgs/build-support/build-fhsenv-chroot/default.nix deleted file mode 100644 index 10d2af55a7b0..000000000000 --- a/pkgs/build-support/build-fhsenv-chroot/default.nix +++ /dev/null @@ -1,87 +0,0 @@ -{ - lib, - callPackage, - runCommandLocal, - writeScript, - stdenv, - coreutils, -}: - -let - buildFHSEnv = callPackage ./env.nix { }; -in - -args@{ - name, - version ? null, - runScript ? "bash", - nativeBuildInputs ? [ ], - extraInstallCommands ? "", - meta ? { }, - passthru ? { }, - ... -}: - -let - env = buildFHSEnv ( - removeAttrs args [ - "version" - "runScript" - "extraInstallCommands" - "meta" - "passthru" - ] - ); - - chrootenv = callPackage ./chrootenv { }; - - init = - run: - writeScript "${name}-init" '' - #! ${stdenv.shell} - for i in ${env}/* /host/*; do - path="/''${i##*/}" - [ -e "$path" ] || ${coreutils}/bin/ln -s "$i" "$path" - done - - [ -d "$1" ] && [ -r "$1" ] && cd "$1" - shift - - source /etc/profile - exec ${run} "$@" - ''; - - versionStr = lib.optionalString (version != null) ("-" + version); - - nameAndVersion = name + versionStr; - -in -runCommandLocal nameAndVersion - { - inherit nativeBuildInputs meta; - - passthru = passthru // { - env = - runCommandLocal "${name}-shell-env" - { - shellHook = '' - exec ${chrootenv}/bin/chrootenv ${init runScript} "$(pwd)" - ''; - } - '' - echo >&2 "" - echo >&2 "*** User chroot 'env' attributes are intended for interactive nix-shell sessions, not for building! ***" - echo >&2 "" - exit 1 - ''; - }; - } - '' - mkdir -p $out/bin - cat <$out/bin/${name} - #! ${stdenv.shell} - exec ${chrootenv}/bin/chrootenv ${init runScript} "\$(pwd)" "\$@" - EOF - chmod +x $out/bin/${name} - ${extraInstallCommands} - '' diff --git a/pkgs/build-support/build-fhsenv-chroot/env.nix b/pkgs/build-support/build-fhsenv-chroot/env.nix deleted file mode 100644 index 1f774da847a7..000000000000 --- a/pkgs/build-support/build-fhsenv-chroot/env.nix +++ /dev/null @@ -1,290 +0,0 @@ -{ - stdenv, - lib, - buildEnv, - writeText, - pkgs, - pkgsi686Linux, -}: - -{ - name, - profile ? "", - targetPkgs ? pkgs: [ ], - multiPkgs ? pkgs: [ ], - nativeBuildInputs ? [ ], - extraBuildCommands ? "", - extraBuildCommandsMulti ? "", - extraOutputsToInstall ? [ ], -}: - -# HOWTO: -# All packages (most likely programs) returned from targetPkgs will only be -# installed once--matching the host's architecture (64bit on x86_64 and 32bit on -# x86). -# -# Packages (most likely libraries) returned from multiPkgs are installed -# once on x86 systems and twice on x86_64 systems. -# On x86 they are merged with packages from targetPkgs. -# On x86_64 they are added to targetPkgs and in addition their 32bit -# versions are also installed. The final directory structure looks as -# follows: -# /lib32 will include 32bit libraries from multiPkgs -# /lib64 will include 64bit libraries from multiPkgs and targetPkgs -# /lib will link to /lib32 - -let - is64Bit = stdenv.hostPlatform.parsed.cpu.bits == 64; - # multi-lib glibc is only supported on x86_64 - isMultiBuild = multiPkgs != null && stdenv.hostPlatform.system == "x86_64-linux"; - isTargetBuild = !isMultiBuild; - - # list of packages (usually programs) which are only be installed for the - # host's architecture - targetPaths = targetPkgs pkgs ++ (if multiPkgs == null then [ ] else multiPkgs pkgs); - - # list of packages which are installed for both x86 and x86_64 on x86_64 - # systems - multiPaths = multiPkgs pkgsi686Linux; - - # base packages of the chroot - # these match the host's architecture, glibc_multi is used for multilib - # builds. glibcLocales must be before glibc or glibc_multi as otherwiese - # the wrong LOCALE_ARCHIVE will be used where only C.UTF-8 is available. - basePkgs = with pkgs; [ - glibcLocales - (if isMultiBuild then glibc_multi else glibc) - (toString gcc.cc.lib) - bashInteractiveFHS - coreutils - less - shadow - su - gawk - diffutils - findutils - gnused - gnugrep - gnutar - gzip - bzip2 - xz - ]; - baseMultiPkgs = with pkgsi686Linux; [ - (toString gcc.cc.lib) - ]; - - etcProfile = writeText "profile" '' - export PS1='${name}-chrootenv:\u@\h:\w\$ ' - export LOCALE_ARCHIVE='/usr/lib/locale/locale-archive' - export LD_LIBRARY_PATH="/run/opengl-driver/lib:/run/opengl-driver-32/lib:/usr/lib:/usr/lib32''${LD_LIBRARY_PATH:+:}$LD_LIBRARY_PATH" - export PATH="/run/wrappers/bin:/usr/bin:/usr/sbin:$PATH" - export TZDIR='/etc/zoneinfo' - - # XDG_DATA_DIRS is used by pressure-vessel (steam proton) and vulkan loaders to find the corresponding icd - export XDG_DATA_DIRS=$XDG_DATA_DIRS''${XDG_DATA_DIRS:+:}/run/opengl-driver/share:/run/opengl-driver-32/share - - # Following XDG spec [1], XDG_DATA_DIRS should default to "/usr/local/share:/usr/share". - # In nix, it is commonly set without containing these values, so we add them as fallback. - # - # [1] - case ":$XDG_DATA_DIRS:" in - *:/usr/local/share:*) ;; - *) export XDG_DATA_DIRS="$XDG_DATA_DIRS''${XDG_DATA_DIRS:+:}/usr/local/share" ;; - esac - case ":$XDG_DATA_DIRS:" in - *:/usr/share:*) ;; - *) export XDG_DATA_DIRS="$XDG_DATA_DIRS''${XDG_DATA_DIRS:+:}/usr/share" ;; - esac - - # Force compilers and other tools to look in default search paths - unset NIX_ENFORCE_PURITY - export NIX_BINTOOLS_WRAPPER_TARGET_HOST_${stdenv.cc.suffixSalt}=1 - export NIX_CC_WRAPPER_TARGET_HOST_${stdenv.cc.suffixSalt}=1 - export NIX_CFLAGS_COMPILE='-idirafter /usr/include' - export NIX_CFLAGS_LINK='-L/usr/lib -L/usr/lib32' - export NIX_LDFLAGS='-L/usr/lib -L/usr/lib32' - export PKG_CONFIG_PATH=/usr/lib/pkgconfig - export ACLOCAL_PATH=/usr/share/aclocal - - ${profile} - ''; - - # Compose /etc for the chroot environment - etcPkg = stdenv.mkDerivation { - name = "${name}-chrootenv-etc"; - buildCommand = '' - mkdir -p $out/etc - cd $out/etc - - # environment variables - ln -s ${etcProfile} profile - - # compatibility with NixOS - ln -s /host/etc/static static - - # symlink nix config - ln -s /host/etc/nix nix - - # symlink some NSS stuff - ln -s /host/etc/passwd passwd - ln -s /host/etc/group group - ln -s /host/etc/shadow shadow - ln -s /host/etc/hosts hosts - ln -s /host/etc/resolv.conf resolv.conf - ln -s /host/etc/nsswitch.conf nsswitch.conf - - # symlink user profiles - ln -s /host/etc/profiles profiles - - # symlink sudo and su stuff - ln -s /host/etc/login.defs login.defs - ln -s /host/etc/sudoers sudoers - ln -s /host/etc/sudoers.d sudoers.d - - # symlink other core stuff - ln -s /host/etc/localtime localtime - ln -s /host/etc/zoneinfo zoneinfo - ln -s /host/etc/machine-id machine-id - ln -s /host/etc/os-release os-release - - # symlink PAM stuff - ln -s /host/etc/pam.d pam.d - - # symlink fonts stuff - ln -s /host/etc/fonts fonts - - # symlink ALSA stuff - ln -s /host/etc/asound.conf asound.conf - ln -s /host/etc/alsa alsa - - # symlink SSL certs - mkdir -p ssl - ln -s /host/etc/ssl/certs ssl/certs - - # symlink /etc/mtab -> /proc/mounts (compat for old userspace progs) - ln -s /proc/mounts mtab - ''; - }; - - # Composes a /usr-like directory structure - staticUsrProfileTarget = buildEnv { - name = "${name}-usr-target"; - paths = [ etcPkg ] ++ basePkgs ++ targetPaths; - extraOutputsToInstall = [ - "out" - "lib" - "bin" - ] - ++ extraOutputsToInstall; - ignoreCollisions = true; - postBuild = '' - if [[ -d $out/share/gsettings-schemas/ ]]; then - # Recreate the standard schemas directory if its a symlink to make it writable - if [[ -L $out/share/glib-2.0 ]]; then - target=$(readlink $out/share/glib-2.0) - rm $out/share/glib-2.0 - mkdir $out/share/glib-2.0 - ln -fs $target/* $out/share/glib-2.0 - fi - - if [[ -L $out/share/glib-2.0/schemas ]]; then - target=$(readlink $out/share/glib-2.0/schemas) - rm $out/share/glib-2.0/schemas - mkdir $out/share/glib-2.0/schemas - ln -fs $target/* $out/share/glib-2.0/schemas - fi - - mkdir -p $out/share/glib-2.0/schemas - - for d in $out/share/gsettings-schemas/*; do - # Force symlink, in case there are duplicates - ln -fs $d/glib-2.0/schemas/*.xml $out/share/glib-2.0/schemas - ln -fs $d/glib-2.0/schemas/*.gschema.override $out/share/glib-2.0/schemas - done - - # and compile them - ${pkgs.glib.dev}/bin/glib-compile-schemas $out/share/glib-2.0/schemas - fi - ''; - }; - - staticUsrProfileMulti = buildEnv { - name = "${name}-usr-multi"; - paths = baseMultiPkgs ++ multiPaths; - extraOutputsToInstall = [ - "out" - "lib" - ] - ++ extraOutputsToInstall; - ignoreCollisions = true; - }; - - # setup library paths only for the targeted architecture - setupLibDirs_target = '' - # link content of targetPaths - cp -rsHf ${staticUsrProfileTarget}/lib lib - ln -s lib lib${if is64Bit then "64" else "32"} - ''; - - # setup /lib, /lib32 and /lib64 - setupLibDirs_multi = '' - mkdir -m0755 lib32 - mkdir -m0755 lib64 - ln -s lib64 lib - - # copy glibc stuff - cp -rsHf ${staticUsrProfileTarget}/lib/32/* lib32/ && chmod u+w -R lib32/ - - # copy content of multiPaths (32bit libs) - [ -d ${staticUsrProfileMulti}/lib ] && cp -rsHf ${staticUsrProfileMulti}/lib/* lib32/ && chmod u+w -R lib32/ - - # copy content of targetPaths (64bit libs) - cp -rsHf ${staticUsrProfileTarget}/lib/* lib64/ && chmod u+w -R lib64/ - - # symlink 32-bit ld-linux.so - ln -Ls ${staticUsrProfileTarget}/lib/32/ld-linux.so.2 lib/ - ''; - - setupLibDirs = if isTargetBuild then setupLibDirs_target else setupLibDirs_multi; - - # the target profile is the actual profile that will be used for the chroot - setupTargetProfile = '' - mkdir -m0755 usr - cd usr - ${setupLibDirs} - for i in bin sbin share include; do - if [ -d "${staticUsrProfileTarget}/$i" ]; then - cp -rsHf "${staticUsrProfileTarget}/$i" "$i" - fi - done - cd .. - - for i in var etc opt; do - if [ -d "${staticUsrProfileTarget}/$i" ]; then - cp -rsHf "${staticUsrProfileTarget}/$i" "$i" - fi - done - for i in usr/{bin,sbin,lib,lib32,lib64}; do - if [ -d "$i" ]; then - ln -s "$i" - fi - done - ''; - -in -stdenv.mkDerivation { - name = "${name}-fhs"; - inherit nativeBuildInputs; - buildCommand = '' - mkdir -p $out - cd $out - ${setupTargetProfile} - cd $out - ${extraBuildCommands} - cd $out - ${lib.optionalString isMultiBuild extraBuildCommandsMulti} - ''; - preferLocalBuild = true; - allowSubstitutes = false; -} diff --git a/pkgs/top-level/aliases.nix b/pkgs/top-level/aliases.nix index 749962b7e66e..ee365bf29d64 100644 --- a/pkgs/top-level/aliases.nix +++ b/pkgs/top-level/aliases.nix @@ -420,6 +420,7 @@ mapAliases { buck = throw "'buck' has been removed has it was deprecated and archived upstream. Consider moving to buck2"; # Added 2026-01-16 budgie-screensaver = throw "'budgie-screensaver' has been removed, no longer used by budgie-desktop."; # Added 2025-11-19 buildBowerComponents = throw "buildBowerComponents has been removed as bower was removed. It is recommended to migrate to yarn."; # Added 2025-09-17 + buildFHSEnvChroot = throw "'buildFHSEnvChroot' is deprecated, please use 'buildFHSEnv'"; # Added 2026-05-21 buildGo123Module = throw "Go 1.23 is end-of-life, and 'buildGo123Module' has been removed. Please use a newer builder version."; # Added 2025-08-13 buildPlatform = warnAlias "'buildPlatform' has been renamed to/replaced by 'stdenv.buildPlatform'" stdenv.buildPlatform; # Converted to warning 2025-10-28 buildXenPackage = throw "'buildXenPackage' has been removed as a custom Xen build can now be achieved by simply overriding 'xen'."; # Added 2025-05-12 diff --git a/pkgs/top-level/all-packages.nix b/pkgs/top-level/all-packages.nix index aeabb3905b47..c2898a4676bc 100644 --- a/pkgs/top-level/all-packages.nix +++ b/pkgs/top-level/all-packages.nix @@ -364,7 +364,6 @@ with pkgs; buildEnv = callPackage ../build-support/buildenv { }; # not actually a package buildFHSEnv = buildFHSEnvBubblewrap; - buildFHSEnvChroot = callPackage ../build-support/build-fhsenv-chroot { }; # Deprecated; use buildFHSEnv/buildFHSEnvBubblewrap buildFHSEnvBubblewrap = callPackage ../build-support/build-fhsenv-bubblewrap { }; buildLakePackage = callPackage ../build-support/lake { }; From c0bd204fa0ee5c8271b1ec427625c88377966a85 Mon Sep 17 00:00:00 2001 From: Ross Smyth <18294397+RossSmyth@users.noreply.github.com> Date: Tue, 16 Jun 2026 11:34:56 -0400 Subject: [PATCH 2/2] doc: add buildFHSEnvChroot removal to release-notes --- doc/release-notes/rl-2611.section.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/release-notes/rl-2611.section.md b/doc/release-notes/rl-2611.section.md index c760306c0608..7db914d4b6a1 100644 --- a/doc/release-notes/rl-2611.section.md +++ b/doc/release-notes/rl-2611.section.md @@ -42,6 +42,8 @@ Users who must continue using ingress-nginx will now need to manually provide the rke2-images-ingress-nginx tarball. - Future Removal: The ingress-nginx chart will not receive any additional updates and will be completely removed in v1.37 for community users. +- `buildFHSEnvChroot` has been removed after deprecation in 23.05. + - `requireFile` now sets `meta.license = lib.licenses.unfree` by default. Users of `requireFile`-based derivations that preserve this default will need to explicitly allow their evaluation as described in [](#sec-allow-unfree). - `librest` providing 0.7 ABI was removed. `librest_1_0` providing 1.0 ABI was renamed to `librest` and `librest_1_0` was kept as an alias.