kiss: unique temporary files

All temporary files now have unique names and reuse of the same
file for multiple purposes has been removed.

This prevents issues where a failed write to a log file used
in multiple places can cause prior data to be read elsewhere
in the package manager.
This commit is contained in:
Dylan Araps 2021-07-16 09:58:14 +03:00
parent e853e1accc
commit 9895a14e83
No known key found for this signature in database
GPG Key ID: 13295DAC2CF13B5C
1 changed files with 71 additions and 34 deletions

105
kiss
View File

@ -44,6 +44,29 @@ contains() {
case " $1 " in *" $2 "*) return 0; esac; return 1
}
tmp_file() {
# Create a uniquely named temporary file and store its absolute path
# in a variable (_tmp_file).
#
# To prevent subshell usage and to handle cases where multiple files
# are needed, this saves the last two temporary files to variables
# for access by the caller (allowing 3 files at once).
_tmp_file_pre_pre=$_tmp_file_pre
_tmp_file_pre=$_tmp_file
_tmp_file=$mak_dir/.$pid-$1-$2
: > "$_tmp_file" ||
die "$1" "Failed to create temporary file"
}
tmp_file_copy() {
# Create a uniquely named temporary file and make a duplicate of
# the file in '$3' if it exists.
tmp_file "$1" "$2"
! [ -f "$3" ] || cp -f "$3" "$_tmp_file"
}
prompt() {
[ "$1" ] && log "$1"
@ -925,8 +948,10 @@ pkg_verify() {
set -- "$@" "$chk"
done 2>/dev/null < "$repo_dir/checksums"
tmp_file "$1" verify
# Generate a new set of checksums to compare against.
pkg_checksums "$1" > "$mak_dir/v"
pkg_checksums "$1" > "$_tmp_file"
# Check that the first column (separated by whitespace) match in both
# checksum files. If any part of either file differs, mismatch. Abort.
@ -938,13 +963,16 @@ pkg_verify() {
*) die "${repo_dir##*/}" "Checksum mismatch"
esac
done < "$mak_dir/v"
done < "$_tmp_file"
}
pkg_conflicts() {
# Check to see if a package conflicts with another.
log "$1" "Checking for package conflicts"
tmp_file "$1" manifest-files
tmp_file "$1" found-conflicts
# Filter the tarball's manifest and select only files. Resolve all
# symlinks in file paths as well.
while read -r file; do
@ -961,7 +989,7 @@ pkg_conflicts() {
# Print the file with all symlinks in its path
# resolved to their real locations.
printf '%s\n' "${PWD#"$KISS_ROOT"}/${file##*/}"
done < "$tar_dir/$1/$pkg_db/$1/manifest" > "$mak_dir/cf_m"
done < "$tar_dir/$1/$pkg_db/$1/manifest" > "$_tmp_file_pre"
cd "$tar_dir/$1"
@ -989,16 +1017,16 @@ pkg_conflicts() {
# Store the list of found conflicts in a file as we'll be using the
# information multiple times. Storing things in the cache dir allows
# us to be lazy as they'll be automatically removed on script end.
grep -Fxf "$mak_dir/cf_m" -- "$@" 2>/dev/null > "$mak_dir/cf" || :
grep -Fxf "$_tmp_file_pre" -- "$@" 2>/dev/null > "$_tmp_file" || :
# Enable alternatives automatically if it is safe to do so.
# This checks to see that the package that is about to be installed
# doesn't overwrite anything it shouldn't in '/var/db/kiss/installed'.
grep -q ":/var/db/kiss/installed/" "$mak_dir/cf" || choice_auto=1
grep -q ":/var/db/kiss/installed/" "$_tmp_file" || choice_auto=1
if [ "$KISS_CHOICE" != 0 ] &&
[ "$choice_auto" = 1 ] &&
[ -s "$mak_dir/cf" ]; then
[ -s "$_tmp_file" ]; then
# This is a novel way of offering an "alternatives" system.
# It is entirely dynamic and all "choices" are created and
# destroyed on the fly.
@ -1042,7 +1070,7 @@ pkg_conflicts() {
log "this must be fixed in $p_name. Contact the maintainer"
die "by finding their details via 'kiss-maintainer'" "" "!>"
}
done < "$mak_dir/cf"
done < "$_tmp_file"
log "$p_name" "Converted all conflicts to choices (kiss a)"
@ -1050,7 +1078,7 @@ pkg_conflicts() {
# to its new spot (and name) in the choices directory.
pkg_manifest "$p_name" "$tar_dir" 2>/dev/null
elif [ -s "$mak_dir/cf" ]; then
elif [ -s "$_tmp_file" ]; then
log "Package '$p_name' conflicts with another package" "" "!>"
log "Run 'KISS_CHOICE=1 kiss i $p_name' to add conflicts" "" "!>"
die "as alternatives." "" "!>"
@ -1082,6 +1110,7 @@ pkg_swap() {
[ "$pkg_owns" ] || die "File '$2' exists on filesystem but isn't owned"
log "Swapping '$2' from '$pkg_owns' to '$1'"
tmp_file "$pkg_owns" from-alt
# Convert the current owner to an alternative and rewrite its manifest
# file to reflect this.
@ -1096,15 +1125,17 @@ pkg_swap() {
"$2") printf '%s\n' "${PWD#"$KISS_ROOT"}/$pkg_owns>${alt#*>}" ;;
*) printf '%s\n' "$line" ;;
esac
done < "../installed/$pkg_owns/manifest" | sort -r > "$mak_dir/.$1"
done < "../installed/$pkg_owns/manifest" | sort -r > "$_tmp_file"
mv -f "$mak_dir/.$1" "../installed/$pkg_owns/manifest"
mv -f "$_tmp_file" "../installed/$pkg_owns/manifest"
fi
# Convert the desired alternative to a real file and rewrite the manifest
# file to reflect this. The reverse of above.
mv -f "$alt" "$KISS_ROOT/$2"
tmp_file "$pkg_owns" to-alt
# Replace the matching line in the manifest with the desired replacement.
# This used to be a 'sed' call which turned out to be a little error-prone
# in some cases. This new method is a tad slower but ensures we never wipe
@ -1114,9 +1145,9 @@ pkg_swap() {
"${PWD#"$KISS_ROOT"}/$alt") printf '%s\n' "$2" ;;
*) printf '%s\n' "$line" ;;
esac
done < "../installed/$1/manifest" | sort -r > "$mak_dir/.$1"
done < "../installed/$1/manifest" | sort -r > "$_tmp_file"
mv -f "$mak_dir/.$1" "../installed/$1/manifest"
mv -f "$_tmp_file" "../installed/$1/manifest"
}
file_rwx() {
@ -1196,7 +1227,7 @@ pkg_remove_files() {
while read -r file; do
case $file in /etc/?*[!/])
sh256 "$KISS_ROOT/$file" >/dev/null
sum_old=$(grep -F "${hash:-null}" "$mak_dir/c")
sum_old=$(grep -F "${hash:-null}" "$1")
[ "${hash:-null}" = "$sum_old" ] || {
printf 'Skipping %s (modified)\n' "$file"
@ -1222,6 +1253,9 @@ pkg_remove_files() {
fi
done
# First argument needs to be dropped.
shift
# Remove all broken directory symlinks.
for sym do
[ -e "$sym" ] || rm -f "$sym"
@ -1229,9 +1263,9 @@ pkg_remove_files() {
}
pkg_etc() (
[ -d "$tar_dir/$pkg_name/etc" ] || return 0
[ -d "$tar_dir/$1/etc" ] || return 0
cd "$tar_dir/$pkg_name"
cd "$tar_dir/$1"
# Create all directories beforehand.
find etc -type d | while read -r dir; do
@ -1244,9 +1278,9 @@ pkg_etc() (
{ sh256 "$file"; sum_new=$hash
sh256 "$KISS_ROOT/$file"; sum_sys=$hash
sum_old=$(awk "NR == $i" "$mak_dir/c"); } >/dev/null 2>&1 || :
sum_old=$(awk "NR == $i" "$2"); } >/dev/null 2>&1 || :
log "$pkg_name" "Doing 3-way handshake for $file"
log "$1" "Doing 3-way handshake for $file"
printf '%s\n' "Previous: ${sum_old:-null}"
printf '%s\n' "System: ${sum_sys:-null}"
printf '%s\n' "New: ${sum_new:-null}"
@ -1272,7 +1306,7 @@ pkg_etc() (
# All other cases.
*)
war "$pkg_name" "saving /$file as /$file.new"
war "$1" "saving /$file as /$file.new"
new=.new
;;
esac
@ -1317,11 +1351,11 @@ pkg_remove() {
run_hook_pkg pre-remove "$1"
run_hook pre-remove "$1" "$sys_db/$1"
# Make a backup of the etcsums file (if it exists).
cp -f "$sys_db/$1/etcsums" "$mak_dir/c" 2>/dev/null || : > "$mak_dir/c"
# Make a backup of any etcsums if they exist.
tmp_file_copy "$pkg_name" etcsums-copy "$sys_db/$pkg_name/etcsums"
log "$1" "Removing package"
pkg_remove_files < "$sys_db/$1/manifest"
pkg_remove_files "$_tmp_file" < "$sys_db/$1/manifest"
# Reset 'trap' to its original value. Removal is done so
# we no longer need to block 'Ctrl+C'.
@ -1431,23 +1465,26 @@ pkg_install() {
log "$pkg_name" "Installing package (${tar_file##*/})"
# Make a backup of any etcsums if they exist.
tmp_file_copy "$pkg_name" etcsums-copy "$sys_db/$pkg_name/etcsums"
_etc_sums=$_tmp_file
# If the package is already installed (and this is an upgrade) make a
# backup of the manifest and etcsums files.
cp -f "$sys_db/$pkg_name/manifest" "$mak_dir/m" 2>/dev/null ||
: > "$mak_dir/m"
cp -f "$sys_db/$pkg_name/etcsums" "$mak_dir/c" 2>/dev/null ||
: > "$mak_dir/c"
tmp_file_copy "$pkg_name" manifest-copy "$sys_db/$pkg_name/manifest"
tmp_file "$pkg_name" manifest-diff
tar_man=$tar_dir/$pkg_name/$pkg_db/$pkg_name/manifest
# Generate a list of files which exist in the currently installed manifest
# but not in the newer (to be installed) manifest.
grep -vFxf "$tar_man" "$_tmp_file_pre" > "$_tmp_file" 2>/dev/null ||:
# Reverse the manifest file so that we start shallow and go deeper as we
# iterate over each item. This is needed so that directories are created
# going down the tree.
sort "$tar_man" > "$mak_dir/if"
# Generate a list of files which exist in the currently installed manifest
# but not in the newer (to be installed) manifest.
grep -vFxf "$tar_man" "$mak_dir/m" > "$mak_dir/rm" 2>/dev/null ||:
tmp_file "$pkg_name" manifest-reverse
sort "$tar_man" > "$_tmp_file"
# Block being able to abort the script with Ctrl+C during installation.
# Removes all risk of the user aborting a package installation leaving
@ -1456,7 +1493,7 @@ pkg_install() {
if
# Install the package's files by iterating over its manifest.
pkg_install_files -z "$tar_dir/$pkg_name" < "$mak_dir/if" &&
pkg_install_files -z "$tar_dir/$pkg_name" < "$_tmp_file" &&
# Handle /etc/ files in a special way (via a 3-way checksum) to
# determine how these files should be installed. Do we overwrite the
@ -1465,16 +1502,16 @@ pkg_install() {
#
# This is more or less similar to Arch Linux's Pacman with the user
# manually handling the .new files when and if they appear.
pkg_etc &&
pkg_etc "$pkg_name" "$_etc_sums" &&
# This is the aforementioned step removing any files from the old
# version of the package if the installation is an update. Each file
# type has to be specially handled to ensure no system breakage occurs.
pkg_remove_files < "$mak_dir/rm" &&
pkg_remove_files "$_etc_sums" < "$_tmp_file_pre" &&
# Install the package's files a second time to fix any mess caused by
# the above removal of the previous version of the package.
pkg_install_files -e "$tar_dir/$pkg_name" < "$mak_dir/if"
pkg_install_files -e "$tar_dir/$pkg_name" < "$_tmp_file"
then
# Reset 'trap' to its original value. Installation is done so we no longer