127 lines
4.2 KiB
Bash
127 lines
4.2 KiB
Bash
#!/bin/bash
|
|
|
|
rpm_safe_symlink_target()
|
|
{
|
|
local root=$1 relative_path=$2 link_target=$3
|
|
local canonical_root link_directory resolved_target
|
|
|
|
canonical_root=$(realpath -m -- "$root") || return 1
|
|
link_directory=$(realpath -m -- "$canonical_root/$(dirname -- "$relative_path")") || return 1
|
|
|
|
if [[ $link_target == /* ]]; then
|
|
resolved_target=$(realpath -m -- "$canonical_root/${link_target#/}") || return 1
|
|
else
|
|
resolved_target=$(realpath -m -- "$link_directory/$link_target") || return 1
|
|
fi
|
|
|
|
[[ $resolved_target == "$canonical_root" || $resolved_target == "$canonical_root"/* ]] || return 1
|
|
|
|
if [[ $link_target == /* ]]; then
|
|
realpath -m --relative-to="$link_directory" -- "$resolved_target"
|
|
else
|
|
printf '%s\n' "$link_target"
|
|
fi
|
|
}
|
|
|
|
rpm_cpio_literal_pattern()
|
|
{
|
|
local input=$1 output= character index
|
|
|
|
[[ $input != *$'\n'* && $input != *$'\r'* ]] || return 1
|
|
for ((index = 0; index < ${#input}; ++index)); do
|
|
character=${input:index:1}
|
|
case $character in
|
|
'[') output+='[[]' ;;
|
|
']') output+='[]]' ;;
|
|
'*') output+='[*]' ;;
|
|
'?') output+='[?]' ;;
|
|
'\\') output+='[\\]' ;;
|
|
*) output+=$character ;;
|
|
esac
|
|
done
|
|
printf '%s\n' "$output"
|
|
}
|
|
|
|
rpm_split_file_record()
|
|
{
|
|
local record=$1 separator=$'\034'
|
|
|
|
RPM_FILENAME=
|
|
RPM_LINK_TARGET=
|
|
[[ $record == *"$separator"* ]] || return 1
|
|
RPM_FILENAME=${record%%"$separator"*}
|
|
RPM_LINK_TARGET=${record#*"$separator"}
|
|
[[ $RPM_LINK_TARGET != *"$separator"* ]] || return 1
|
|
[[ $RPM_FILENAME != *$'\t'* && $RPM_FILENAME != *$'\n'* && $RPM_FILENAME != *$'\r'* ]] || return 1
|
|
[[ $RPM_LINK_TARGET != *$'\t'* && $RPM_LINK_TARGET != *$'\n'* && $RPM_LINK_TARGET != *$'\r'* ]] || return 1
|
|
}
|
|
|
|
rpm_install_symlink_manifest()
|
|
{
|
|
local root=$1 manifest=$2 log=$3 separator=$'\034'
|
|
local canonical_root sorted row relative link_target safe_target destination parent existing
|
|
|
|
canonical_root=$(realpath -m -- "$root") || return 1
|
|
|
|
sorted=$(mktemp "${manifest}.sorted.XXXXXX") || return 1
|
|
if ! awk -F '\t' -v separator="$separator" '
|
|
NF >= 2 {
|
|
path=$1
|
|
depth=gsub(/\//, "/", path)
|
|
printf "%08d%s%s\n", depth, separator, $0
|
|
}
|
|
' "$manifest" | LC_ALL=C sort -t "$separator" -k1,1n -k2,2 >"$sorted"; then
|
|
rm -f -- "$sorted"
|
|
return 1
|
|
fi
|
|
|
|
while IFS=$separator read -r _depth row; do
|
|
IFS=$'\t' read -r relative link_target <<<"$row"
|
|
[[ -n $relative && -n $link_target ]] || continue
|
|
destination=$canonical_root/$relative
|
|
parent=$(realpath -m -- "$(dirname -- "$destination")") || {
|
|
rm -f -- "$sorted"
|
|
return 1
|
|
}
|
|
[[ $parent == "$canonical_root" || $parent == "$canonical_root"/* ]] || {
|
|
printf 'escaping RPM symlink parent rejected: %q\n' "/$relative" >>"$log"
|
|
rm -f -- "$sorted"
|
|
return 1
|
|
}
|
|
if ! safe_target=$(rpm_safe_symlink_target "$canonical_root" "$relative" "$link_target"); then
|
|
printf 'escaping RPM symlink rejected: %q -> %q\n' "/$relative" "$link_target" >>"$log"
|
|
rm -f -- "$sorted"
|
|
return 1
|
|
fi
|
|
mkdir -p -- "$parent" || {
|
|
rm -f -- "$sorted"
|
|
return 1
|
|
}
|
|
|
|
if [[ -L $destination ]]; then
|
|
existing=$(readlink -- "$destination") || {
|
|
rm -f -- "$sorted"
|
|
return 1
|
|
}
|
|
if [[ $existing == "$safe_target" ]]; then
|
|
continue
|
|
fi
|
|
printf 'conflicting RPM symlinks: %q -> %q and %q\n' \
|
|
"/$relative" "$existing" "$safe_target" >>"$log"
|
|
rm -f -- "$sorted"
|
|
return 1
|
|
elif [[ -e $destination ]]; then
|
|
printf 'RPM symlink conflicts with existing path: %q\n' "/$relative" >>"$log"
|
|
rm -f -- "$sorted"
|
|
return 1
|
|
fi
|
|
if ! ln -s -- "$safe_target" "$destination"; then
|
|
printf 'unable to create RPM symlink: %q -> %q\n' "/$relative" "$safe_target" >>"$log"
|
|
rm -f -- "$sorted"
|
|
return 1
|
|
fi
|
|
done <"$sorted"
|
|
|
|
rm -f -- "$sorted"
|
|
}
|