Skip to content

Instantly share code, notes, and snippets.

@AlexTMjugador
Last active November 17, 2022 14:43
Show Gist options
  • Save AlexTMjugador/04c9460ec7a531d8c458038d3e0fa0d1 to your computer and use it in GitHub Desktop.
Save AlexTMjugador/04c9460ec7a531d8c458038d3e0fa0d1 to your computer and use it in GitHub Desktop.
Script to download and reinstall all the files within a subtree provided by the Debian packages installed on the system. Unlike reinstalling all the packages, this script only extracts the files within the specified subtree, does not execute any package setup scripts, and stores its results in a configurable directory, making it a better fit for…
#!/bin/sh -e
# Script to download and reinstall all the files within a subtree
# provided by the Debian packages installed on the system. Unlike
# reinstalling all the packages, this script only extracts the
# files within the specified subtree, does not execute any package
# setup scripts, and stores its results in a configurable $OUTPUT_DIR,
# making it a better fit for advanced Linux installation repairs.
#
# When the filesystem to repair does not match the root filesystem
# (i.e., it is not mounted at /), the following commands can be used
# to run this script as if the filesystem to repair was the root
# filesystem. This should work as long as the basic binaries and shared
# libraries used by this script were not damaged:
# # cd <mountpoint>
# # mount -t proc proc proc
# # mount -t sysfs sysfs sys
# # mount -t devtmpfs devtmpfs dev
# # mount -t tmpfs tmpfs tmp
# # mount -t tmpfs tmpfs run
# <mount other filesystems as needed (/boot, etc.)>
# # chroot .
#
# Although an attempt is made to handle extended attributes properly,
# it may happen that some of them are not properly set after this script
# completes. It's a good idea to update the system packages or reinstall
# them afterwards. Related StackExchange question:
# https://askubuntu.com/questions/1099381/fix-setuid-and-setgid-bits-in-usr
if [ "$(id -u)" -ne 0 ]; then
echo "! This script should be run as root." >&2
exit 1
fi
readonly TO_REPAIR_DIR='/usr/bin'
readonly OUTPUT_DIR="$PWD/repaired"
WORKDIR=$(mktemp -d '/tmp/pkgs.XXX')
readonly WORKDIR
cleanup() {
rm -rf "$WORKDIR"
}
trap 'cleanup' EXIT INT QUIT TERM
# Uncomment the following to extract several packages in parallel.
# This may provide nice speedups, at the cost of garbled output.
# NOT TESTED.
#BATCH_SIZE=$(nproc)
BATCH_SIZE=1
readonly BATCH_SIZE
copyPackageSubtree() {
echo "> Copying package subtree for $1..."
apt-get download "$1"
# Ignore tar extraction errors, most likely caused by the package
# not containing any file in the subtree to be repaired
dpkg-deb --fsys-tarfile "${1%%:*}"*.deb | \
{ tar -vx -C "$OUTPUT_DIR" --keep-directory-symlink --overwrite --acls --xattrs \
--wildcards ."$TO_REPAIR_DIR"/'*' || true; }
}
getInstalledPackages() {
dpkg-query --show --showformat='${db:Status-Abbrev}\t${binary:Package}\n' '*' | \
while read -r package_data; do
package_status="${package_data%% *}"
package_name_and_version="${package_data#* }"
if [ "$package_status" = 'ii ' ]; then
echo "$package_name_and_version"
fi
done
}
cd "$WORKDIR"
mkdir -p "$OUTPUT_DIR"
remaining_batch_packages=$BATCH_SIZE
getInstalledPackages | while read -r package; do
if [ "$remaining_batch_packages" -ne 0 ]; then
# Extract several packages in parallel to improve performance
copyPackageSubtree "$package" &
remaining_batch_packages=$((remaining_batch_packages - 1))
else
wait
remaining_batch_packages=$BATCH_SIZE
fi
done
wait
echo "Done! Subtree stored at $OUTPUT_DIR"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment