feat: add CONFFILES to record /etc files

This commit is contained in:
2025-12-29 01:40:45 -05:00
parent db903ca802
commit d0600c4b07

144
leaf
View File

@@ -440,9 +440,81 @@ leaf_trace_package() {
find . -type l | sort > "${_trace_dir}"/LINKS
sed -i "s/.//" "${_trace_dir}"/{DIRS,FILES,LINKS}
install -vm644 "${PKGBUILD_DIR}/${PKG_PREFIX}/${PKG_NAME}.PKGBUILD" "${_trace_dir}/PKGBUILD"
leaf_trace_conffiles "${_trace_dir}"
popd > /dev/null 2>&1
}
leaf_trace_conffiles() {
# usage: leaf_trace_conffiles <trace_dir>
# called with CWD = pkgdir (same as leaf_trace_package)
local _trace_dir="$1"
local _out="${_trace_dir}/CONFFILES"
local _p _type _val _mode _uid _gid
[ -d ./etc ] || return 0
: > "${_out}"
# regular files
while IFS= read -r -d '' _p; do
_mode=$(stat -c %a -- "$_p")
_uid=$(stat -c %u -- "$_p")
_gid=$(stat -c %g -- "$_p")
_val=$(sha256sum -- "$_p" | awk '{print $1}')
printf '%s\tf\t%s\t%s\t%s\t%s\n' "/${_p#./}" "${_val}" "${_mode}" "${_uid}" "${_gid}" >> "${_out}"
done < <(find ./etc -type f -print0)
# symlinks
while IFS= read -r -d '' _p; do
_mode=$(stat -c %a -- "$_p")
_uid=$(stat -c %u -- "$_p")
_gid=$(stat -c %g -- "$_p")
_val=$(readlink -- "$_p")
printf '%s\tl\t%s\t%s\t%s\t%s\n' "/${_p#./}" "${_val}" "${_mode}" "${_uid}" "${_gid}" >> "${_out}"
done < <(find ./etc -type l -print0)
sort -o "${_out}" "${_out}" 2>/dev/null || true
}
leaf_conffile_is_unmodified() {
# usage: leaf_conffile_is_unmodified <abs_path> <trace_dir>
# return 0 if unmodified (safe to remove), 1 otherwise
local path="$1" tdir="$2"
local cf="${tdir}/CONFFILES"
local line type val mode uid gid
local cur_val cur_mode cur_uid cur_gid
[ -r "${cf}" ] || return 1
[ -e "${path}" ] || return 1
line="$(grep -F -m1 -- "${path}"$'\t' "${cf}" 2>/dev/null)" || return 1
IFS=$'\t' read -r _p type val mode uid gid <<< "${line}"
cur_mode="$(stat -c %a -- "${path}" 2>/dev/null)" || return 1
cur_uid="$(stat -c %u -- "${path}" 2>/dev/null)" || return 1
cur_gid="$(stat -c %g -- "${path}" 2>/dev/null)" || return 1
# metadata changed -> treat as modified
if [ "${cur_mode}" != "${mode}" ] || [ "${cur_uid}" != "${uid}" ] || [ "${cur_gid}" != "${gid}" ]; then
return 1
fi
case "${type}" in
f)
cur_val="$(sha256sum -- "${path}" 2>/dev/null | awk '{print $1}')" || return 1
[ "${cur_val}" = "${val}" ] && return 0 || return 1
;;
l)
cur_val="$(readlink -- "${path}" 2>/dev/null)" || return 1
[ "${cur_val}" = "${val}" ] && return 0 || return 1
;;
*)
return 1
;;
esac
}
leaf_update_package_database() {
local _item="$(echo "$2" | awk 'BEGIN{FS="/";OFS="\\/"}{print $1,$2}')"
case $1 in
@@ -645,33 +717,73 @@ leaf_remove_package() {
[ -n "$(grep "${PKG_PREFIX}/${PKG_NAME}" ${INSTALLED_PACKAGES})" ] || {
leaf_error "Package ${PKG_PREFIX}/${PKG_NAME} is NOT installed"
}
leaf_phase_must "Preremove" src_preremove "Preremove failed."
local _file _link _directory _etc_backup_path _relative_path _backup_etc=false
_etc_backup_path="/etc/.leaf_backup/${PKGNAME}_$(date +%Y%m%d%H%M%S)"
cat "${TRACE_DIR}/${PKG_PREFIX}/${PKG_NAME}/FILES" | while read -r _file; do
if [[ "${_file}" == /etc/* && -f "${_file}" ]]; then
_backup_etc=true
_relative_path="${_file#/etc/}"
mkdir -pv "${_etc_backup_path}/$(dirname "${_relative_path}")"
mv -v "${_file}" "${_etc_backup_path}/${_relative_path}"
local _trace_dir="${TRACE_DIR}/${PKG_PREFIX}/${PKG_NAME}"
local _has_conffiles=0
[ -r "${_trace_dir}/CONFFILES" ] && _has_conffiles=1
_etc_backup_path="/etc/.leaf_backup/${PKG_NAME}_$(date +%Y%m%d%H%M%S)"
# FILES
while read -r _file; do
if [[ "${_file}" == /etc/* && -e "${_file}" ]]; then
if [ "${_has_conffiles}" -eq 1 ]; then
if leaf_conffile_is_unmodified "${_file}" "${_trace_dir}"; then
rm -fv -- "${_file}"
else
leaf_record_message "Preserved modified config file: ${_file}"
fi
continue
fi
# fallback for old packages without CONFFILES (keep old behavior)
if [[ -f "${_file}" ]]; then
_backup_etc=true
_relative_path="${_file#/etc/}"
mkdir -pv "${_etc_backup_path}/$(dirname "${_relative_path}")"
mv -v -- "${_file}" "${_etc_backup_path}/${_relative_path}"
else
rm -fv -- "${_file}"
fi
else
rm -fv "${_file}"
rm -fv -- "${_file}"
fi
done
done < "${_trace_dir}/FILES"
if [[ "${_backup_etc}" == true ]]; then
leaf_record_message "Found files in /etc, backuped to ${_etc_backup_path}."
fi
cat "${TRACE_DIR}/${PKG_PREFIX}/${PKG_NAME}/LINKS" | while read -r _link; do
[ ! -L "${_link}" ] || rm -fv "${_link}"
done
cat "${TRACE_DIR}/${PKG_PREFIX}/${PKG_NAME}/DIRS" | while read -r _directory; do
if [[ -d ${_directory} ]]; then
# LINKS
while read -r _link; do
if [[ "${_link}" == /etc/* && -e "${_link}" ]]; then
if [ "${_has_conffiles}" -eq 1 ]; then
if leaf_conffile_is_unmodified "${_link}" "${_trace_dir}"; then
rm -fv -- "${_link}"
else
leaf_record_message "Preserved modified config link: ${_link}"
fi
continue
fi
else
[ ! -L "${_link}" ] || rm -fv -- "${_link}"
fi
done < "${_trace_dir}/LINKS"
# DIRS
while read -r _directory; do
if [[ -d "${_directory}" ]]; then
rmdir --ignore-fail-on-non-empty "${_directory}"
fi
done
done < "${_trace_dir}/DIRS"
leaf_phase_must "Postremove" src_postremove "Postremove failed."
leaf_invoke_hooks remove
rm -rf "${TRACE_DIR}/${PKG_PREFIX}/${PKG_NAME}"
rm -rf "${_trace_dir}"
leaf_update_package_database delete "${PKG_PREFIX}/${PKG_NAME}"
leaf_update_environment
}