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
This commit is contained in:
DavHau 2025-07-07 14:57:21 +07:00
parent 1cf013f7d7
commit a1154d6348
2 changed files with 59 additions and 28 deletions

View File

@ -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 () {

View File

@ -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 = ''