patchShebangs: fix crash on read-only files (#421389)

K900 0ee6bf54 4a5ae767

Changed files
+116
pkgs
build-support
setup-hooks
test
+13
pkgs/build-support/setup-hooks/patch-shebangs.sh
···
_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
+103
pkgs/test/stdenv/patch-shebangs.nix
···
assertion = "grep '^#!/usr/bin/builtin' $out/bin/test > /dev/null";
};
};
+
+
read-only-script =
+
(derivation {
+
name = "read-only-script";
+
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
+
echo "#!/bin/bash" > $out/bin/test
+
echo "echo -n hello" >> $out/bin/test
+
chmod 555 $out/bin/test
+
patchShebangs $out/bin/test
+
''
+
];
+
assertion = "grep '^#!${stdenv.shell}' $out/bin/test > /dev/null";
+
})
+
// {
+
meta = { };
+
};
+
+
preserves-read-only =
+
(derivation {
+
name = "preserves-read-only";
+
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
+
echo "#!/bin/bash" > $out/bin/test
+
echo "echo -n hello" >> $out/bin/test
+
chmod 555 $out/bin/test
+
original_perms=$(stat -c %a $out/bin/test)
+
patchShebangs $out/bin/test
+
new_perms=$(stat -c %a $out/bin/test)
+
if ! [ "$original_perms" = "$new_perms" ]; then
+
echo "Permissions changed from $original_perms to $new_perms"
+
exit 1
+
fi
+
''
+
];
+
assertion = "grep '^#!${stdenv.shell}' $out/bin/test > /dev/null";
+
})
+
// {
+
meta = { };
+
};
+
+
# Preserve times, see: https://github.com/NixOS/nixpkgs/pull/33281
+
preserves-timestamp =
+
(derivation {
+
name = "preserves-timestamp";
+
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
+
echo "#!/bin/bash" > $out/bin/test
+
echo "echo -n hello" >> $out/bin/test
+
chmod +x $out/bin/test
+
# Set a specific timestamp (2000-01-01 00:00:00)
+
touch -t 200001010000 $out/bin/test
+
original_timestamp=$(stat -c %Y $out/bin/test)
+
patchShebangs $out/bin/test
+
new_timestamp=$(stat -c %Y $out/bin/test)
+
if ! [ "$original_timestamp" = "$new_timestamp" ]; then
+
echo "Timestamp changed from $original_timestamp to $new_timestamp"
+
exit 1
+
fi
+
''
+
];
+
assertion = "grep '^#!${stdenv.shell}' $out/bin/test > /dev/null";
+
})
+
// {
+
meta = { };
+
};
};
in
stdenv.mkDerivation {
···
updates-nix-store
split-string
without-trailing-newline
+
dont-patch-builtins
+
read-only-script
+
preserves-read-only
+
preserves-timestamp
;
};
buildCommand = ''