From a1154d6348511153fe41da3656c7ec4a35be9a83 Mon Sep 17 00:00:00 2001 From: DavHau Date: Mon, 7 Jul 2025 14:57:21 +0700 Subject: [PATCH] patch-shebangs: fix binary data corrupt after patching This removes the recently introduced shell based implementation of `sponge` which wasn't capable of managing binary input. Now, a tmpFile under $TMPDIR is created manually and later deleted see: https://github.com/NixOS/nixpkgs/pull/414448#issuecomment-3041238623 --- .../setup-hooks/patch-shebangs.sh | 48 ++++++++----------- pkgs/test/stdenv/patch-shebangs.nix | 39 +++++++++++++++ 2 files changed, 59 insertions(+), 28 deletions(-) diff --git a/pkgs/build-support/setup-hooks/patch-shebangs.sh b/pkgs/build-support/setup-hooks/patch-shebangs.sh index 7fb6dc86c87a0..5bd1f5078ad00 100644 --- a/pkgs/build-support/setup-hooks/patch-shebangs.sh +++ b/pkgs/build-support/setup-hooks/patch-shebangs.sh @@ -68,31 +68,6 @@ patchShebangs() { return 0 fi - # like sponge from moreutils but in pure bash - _sponge() { - local content - local target - local restoreReadOnly - content="" - target="$1" - - # Make file writable if it is read-only - if [[ ! -w "$target" ]]; then - chmod +w "$target" - restoreReadOnly=true - fi - - while IFS= read -r line || [[ -n "$line" ]]; do - content+="$line"$'\n' - done - printf '%s' "$content" > "$target" - - # Restore read-only if it was read-only before - if [[ -n "${restoreReadOnly:-}" ]]; then - chmod -w "$target" - fi - } - local f while IFS= read -r -d $'\0' f; do isScript "$f" || continue @@ -151,14 +126,31 @@ patchShebangs() { # Preserve times, see: https://github.com/NixOS/nixpkgs/pull/33281 timestamp=$(stat --printf "%y" "$f") - sed -e "1 s|.*|#\!$escapedInterpreterLine|" "$f" | _sponge "$f" + + # Manually create temporary file instead of using sed -i + # (sed -i on $out/x creates tmpfile /nix/store/x which fails on macos + sandbox) + tmpFile=$(mktemp -t patchShebangs.XXXXXXXXXX) + sed -e "1 s|.*|#\!$escapedInterpreterLine|" "$f" > "$tmpFile" + + # Make original file writable if it is read-only + local restoreReadOnly + if [[ ! -w "$f" ]]; then + chmod +w "$f" + restoreReadOnly=true + fi + + # Replace the original file's content with the patched content + # (preserving permissions) + cat "$tmpFile" > "$f" + rm "$tmpFile" + if [[ -n "${restoreReadOnly:-}" ]]; then + chmod -w "$f" + fi touch --date "$timestamp" "$f" fi fi done < <(find "$@" -type f -perm -0100 -print0) - - unset -f _sponge } patchShebangsAuto () { diff --git a/pkgs/test/stdenv/patch-shebangs.nix b/pkgs/test/stdenv/patch-shebangs.nix index 392f9630a1d1a..653058dca130b 100644 --- a/pkgs/test/stdenv/patch-shebangs.nix +++ b/pkgs/test/stdenv/patch-shebangs.nix @@ -204,6 +204,44 @@ let // { meta = { }; }; + + preserves-binary-data = + (derivation { + name = "preserves-binary-data"; + system = stdenv.buildPlatform.system; + builder = "${stdenv.__bootPackages.stdenv.__bootPackages.bashNonInteractive}/bin/bash"; + initialPath = [ + stdenv.__bootPackages.stdenv.__bootPackages.coreutils + ]; + strictDeps = false; + args = [ + "-c" + '' + set -euo pipefail + . ${../../stdenv/generic/setup.sh} + . ${../../build-support/setup-hooks/patch-shebangs.sh} + mkdir -p $out/bin + # Create a script with binary data after the shebang + echo "#!/bin/bash" > $out/bin/test + echo "echo 'script start'" >> $out/bin/test + # Add some binary data (null bytes and other non-printable chars) + printf '\x00\x01\x02\xff\xfe' >> $out/bin/test + echo >> $out/bin/test + echo "echo 'script end'" >> $out/bin/test + chmod +x $out/bin/test + patchShebangs $out/bin/test + # Verify binary data is still present by checking file size and content + if ! printf '\x00\x01\x02\xff\xfe' | cmp -s - <(sed -n '3p' $out/bin/test | tr -d '\n'); then + echo "Binary data corrupted during patching" + exit 1 + fi + '' + ]; + assertion = "grep '^#!${stdenv.shell}' $out/bin/test > /dev/null"; + }) + // { + meta = { }; + }; }; in stdenv.mkDerivation { @@ -219,6 +257,7 @@ stdenv.mkDerivation { read-only-script preserves-read-only preserves-timestamp + preserves-binary-data ; }; buildCommand = ''