tier1/fix:re-sync file owner post upgrade

Resync file owner if there are any discrepancies after the system is
upgraded from a different build system.

The script is to iterate all the files except /sysroot in parallel to
check for a change in the uid/gid post-upgrade. If there is
any it will trigger chown/chgrp to sync them according to the new
uid/gid. The script caches the unchanged uid/gid to skip files with
unchanged pairs and optimize the execution time. The scripts run via a
systemd unit which in turn requires ostree-finalized-stage.service
to ensure that the sync script runs only when a system is upgraded.
This commit is contained in:
saypaul 2024-11-27 19:35:26 +05:30
parent 2ed185f6d1
commit 082e9ca10b
4 changed files with 111 additions and 0 deletions

83
tier-1/bootc-file-ownership Executable file
View File

@ -0,0 +1,83 @@
#!/bin/bash
# Log file for tracking changes
LOGFILE="/var/log/fix-ownership.log"
echo "Starting ownership change process on $(date)" > "$LOGFILE"
# Cache to track unchanged users and groups
UNCHANGED_USERS=()
UNCHANGED_GROUPS=()
USER_EXISTS=false
GROUP_EXISTS=false
# function to check if a user is unchanged
is_user_unchanged() {
local user="$1"
[[ " ${UNCHANGED_USERS[@]} " =~ " ${user} " ]]
}
# function to check if a group is unchanged
is_group_unchanged() {
local group="$1"
[[ " ${UNCHANGED_GROUPS[@]} " =~ " ${group} " ]]
}
# function to process a single file
process_file() {
local filepath="$1"
# gather username, UID, group name, and GID of the file/folder
FILE_UID=$(stat -c "%u" "$filepath")
FILE_GID=$(stat -c "%g" "$filepath")
FILE_USER=$(stat -c "%U" "$filepath")
FILE_GROUP=$(stat -c "%G" "$filepath")
# skip files owned by unchanged users and groups
if is_user_unchanged "$FILE_USER" && is_group_unchanged "$FILE_GROUP"; then
return
fi
# check if the user exists in /lib/passwd
if grep -q "^${FILE_USER}:" /lib/passwd; then
EXPECTED_UID=$(getent passwd "$FILE_USER" | cut -d: -f3)
USER_EXISTS=true
fi
# check if the group exists in /lib/group
if grep -q "^${FILE_GROUP}:" /lib/group; then
EXPECTED_GID=$(getent group "$FILE_GROUP" | cut -d: -f3)
GROUP_EXISTS=true
fi
# compare UID
if $USER_EXISTS then
if [[ "$FILE_UID" != "$EXPECTED_UID" ]]; then
echo "Fixing UID for $filepath: $FILE_UID -> $EXPECTED_UID" >> "$LOGFILE"
chown "$EXPECTED_UID" "$filepath"
else
# mark user as unchanged
UNCHANGED_USERS+=("$FILE_USER")
fi
fi
# compare GID
if $GROUP_EXISTS then
if [[ "$FILE_GID" != "$EXPECTED_GID" ]]; then
echo "Fixing GID for $filepath: $FILE_GID -> $EXPECTED_GID" >> "$LOGFILE"
chgrp "$EXPECTED_GID" "$filepath"
else
# mark group as unchanged
UNCHANGED_GROUPS+=("$FILE_GROUP")
fi
fi
}
export -f process_file
export LOGFILE
export UNCHANGED_USERS
export UNCHANGED_GROUPS
# Parallelized processing with find and xargs
find / -path /sysroot -prune -o -print 2>/dev/null | xargs -P "$(nproc)" -I {} bash -c 'process_file "$@"' _ {}
echo "ownership chnage process completed on $(date)" >> "$LOGFILE"

View File

@ -0,0 +1,15 @@
[Unit]
Description=Bootc system migration file ownership sync
# This helps verify that we're running in a bootc/ostree based target.
ConditionPathIsMountPoint=/sysroot
RequiresMountsFor=/boot
After=network.target
[Service]
ExecStart=/usr/libexec/bootc-file-ownership
# So we can temporarily remount the sysroot writable
MountFlags=slave
# Just to auto-cleanup our temporary files
PrivateTmp=yes

View File

@ -13,6 +13,7 @@ include:
- persistent-journal.yaml
- initramfs-full.yaml
- generic-growfs.yaml
- ownership-sync.yaml
packages:
# Include and set the default editor

View File

@ -0,0 +1,12 @@
add-files:
- - bootc-file-ownership
- /usr/libexec/bootc-file-ownership
- - bootc-file-ownership.service
- /usr/lib/systemd/system/bootc-file-ownership.service
postprocess:
- |
#!/bin/bash
set -euo pipefail
mkdir -p /usr/lib/systemd/system/ostree-finalized-stage.service.requires
ln -s ../bootc-file-ownership.service /usr/lib/systemd/system/ostree-finalized-stage.service.requires/bootc-file-ownership.service