From 9895a14e833535061677073c263226ee12af8a25 Mon Sep 17 00:00:00 2001 From: Dylan Araps Date: Fri, 16 Jul 2021 09:58:14 +0300 Subject: [PATCH] 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. --- kiss | 105 ++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 71 insertions(+), 34 deletions(-) diff --git a/kiss b/kiss index bbf9d69..70a6cf1 100755 --- a/kiss +++ b/kiss @@ -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