#!/bin/bash status_output=$(bootc status) # extract the rollback checksum rollback_checksum=$(echo "$status_output" | awk '/rollback:/{flag=1} /checksum:/{if(flag){print $2; exit}}') # retrun if rollback_checksum is empty if [ -z "$rollback_checksum" ]; then echo "Rollback deployment not found no need to sync " exit 0 fi # source /etc/os-release to get the ID source /etc/os-release # extract the ID and remove quotes os_id="${ID//\"}" prefix="/ostree/deploy/" # Adjust the regex as needed to match specific patterns or wildcards prefix=$(find "$prefix" -type d -regex ".*${os_id}.*" -print -quit) prefix="${prefix}/deploy" # Regular expression to find folders matching the checksums rollback_folder=$(find "$prefix" -type d -regex ".*${rollback_checksum}.*" -print -quit) # Define the suffixes suffix_group="usr/lib/group" suffix_passwd="usr/lib/passwd" # Construct the paths for process_* function BOOTED_GROUP_PATH="/${suffix_group}" BOOTED_PASSWD_PATH="/${suffix_passwd}" ROLLBACK_GROUP_PATH="${rollback_folder}/${suffix_group}" ROLLBACK_PASSWD_PATH="${rollback_folder}/${suffix_passwd}" # Array to store booted username and UID declare -A passwd_booted=() # Array to store rollback username and UID declare -A passwd_rollback=() declare -A passwd_gid_rollback=() # Create a key-value list for UID mismatches declare -A uid_mismatch=() declare -A uid_missing=() # Array to store rollback groupname and gid declare -A group_rollback=() # Array to map groups and subgroups if present in rollback_group declare -A subgroup_mapping=() declare -A group_booted=() # Create a key-value list for gid mismatches declare -A gid_mismatch=() declare -A missing_groups=() get_username_by_uid() { local uid=$1 local mode=$2 # "booted" or "rollback" local username="" if [[ $mode == "booted" ]]; then for user in "${!passwd_booted[@]}"; do if [[ "${passwd_booted[$user]}" == "$uid" ]]; then username="$user" break fi done elif [[ $mode == "rollback" ]]; then for user in "${!passwd_rollback[@]}"; do if [[ "${passwd_rollback[$user]}" == "$uid" ]]; then username="$user" break fi done fi echo "$username" } get_groupname_by_gid() { local gid=$1 local mode=$2 # "booted" or "rollback" local groupname="" if [[ $mode == "booted" ]]; then for group in "${!group_booted[@]}"; do if [[ "${group_booted[$group]}" == "$gid" ]]; then groupname="$group" break fi done elif [[ $mode == "rollback" ]]; then for group in "${!group_rollback[@]}"; do if [[ "${group_rollback[$group]}" == "$gid" ]]; then groupname="$group" break fi done fi echo "$groupname" } # Read booted_passwd file and store username and UID in an associative array while IFS=: read -r username password uid gid userinfo homedir command; do passwd_booted["$username"]="$uid" done < $BOOTED_PASSWD_PATH # Read rollback_passwd file and store username and UID in an associative array while IFS=: read -r username password uid gid userinfo homedir command; do passwd_rollback["$username"]="$uid" passwd_gid_rollback["$username"]="$gid" done < $ROLLBACK_PASSWD_PATH # Check for users in rollback_passwd but not in booted_passwd for username in "${!passwd_rollback[@]}"; do if [[ -z "${passwd_booted[$username]}" ]]; then # Add user with details from rollback_passwd uid_missing["${passwd_rollback[$username]}"]="$username" elif [[ "${passwd_rollback[$username]}" != "${passwd_booted[$username]}" ]]; then # UID mismatch - store in key-value list uid_mismatch["${passwd_rollback[$username]}"]="${passwd_booted[$username]}" fi done # Display missing users if [[ ${#uid_missing[@]} -gt 0 ]]; then echo "Missing Users" for uid in "${!uid_missing[@]}"; do echo "${uid_missing[$uid]} (UID: $uid) (GID: ${passwd_gid_rollback[${uid_missing[$uid]}]})" done else echo "No missing user found" fi if [[ ${#uid_mismatch[@]} -gt 0 ]]; then echo "UID mismatches found:" for rollback_uid in "${!uid_mismatch[@]}"; do booted_uid="${uid_mismatch[$rollback_uid]}" booted_user=$(get_username_by_uid "$booted_uid" "booted") echo "$booted_user : (Rollback UID: $rollback_uid) (Booted UID: $booted_uid)" done else echo "No UID mismatches found." fi # Read rollback_group file and store groupname, gid, and subgroup in arrays while IFS=: read -r groupname password gid subgroup; do group_rollback["$groupname"]="$gid" if [[ -n "$subgroup" ]]; then subgroup_mapping["$groupname"]="$subgroup" fi done < $ROLLBACK_GROUP_PATH # Array to store booted groupname and GID # Read booted_group file and store groupname and gid in an associative array while IFS=: read -r groupname password gid subgroup; do group_booted["$groupname"]="$gid" if [[ ${subgroup_mapping["$groupname"]} == $subgroup ]]; then unset "subgroup_mapping[$groupname]" fi done < $BOOTED_GROUP_PATH # Check for users in rollback_group but not in booted_group for groupname in "${!group_rollback[@]}"; do if [[ -z "${group_booted[$groupname]}" ]]; then # Add group with details from rollback_group missing_groups["${group_rollback[$groupname]}"]="$groupname" elif [[ "${group_rollback[$groupname]}" != "${group_booted[$groupname]}" ]]; then # GID mismatch - store groupname and mismatched GIDs gid_mismatch["${group_rollback[$groupname]}"]="${group_booted[$groupname]}" fi done # Display missing groups if [[ ${#missing_groups[@]} -gt 0 ]]; then echo "Missing groups:" for gid in "${!missing_groups[@]}"; do echo "${missing_groups[$gid]} (GID: ${gid})" done else echo "No missing groups found." fi # Display GID mismatches if [[ ${#gid_mismatch[@]} -gt 0 ]]; then echo "GID mismatches found:" for rollback_gid in "${!gid_mismatch[@]}"; do booted_gid="${gid_mismatch[$rollback_gid]}" rollback_group=$(get_groupname_by_gid "$rollback_gid" "rollback") echo "$rollback_group : (Rollback GID: $rollback_gid) (Booted GID: $booted_gid)" done else echo "No GID mismatches found." fi # Display remaining groups with subgroups if [[ ${#subgroup_mapping[@]} -gt 0 ]]; then echo "Groups with subgroups:" for groupname in "${!subgroup_mapping[@]}"; do echo "Group: $groupname, Subgroup: ${subgroup_mapping[$groupname]}" done else echo "No groups with subgroups found." fi # Output of the above script when ran on system switched form rpm-ostree to bootc: # Missing Users # core (UID: 1000) (GID: 1000) # dnsmasq (UID: 994) (GID: 991) # clevis (UID: 998) (GID: 996) # UID mismatches found: # nobody : (Rollback UID: 65534) (Booted UID: 99) # chrony : (Rollback UID: 995) (Booted UID: 994) # polkitd : (Rollback UID: 997) (Booted UID: 999) # sssd : (Rollback UID: 996) (Booted UID: 995) # systemd-coredump : (Rollback UID: 999) (Booted UID: 987) # Missing groups: # core (GID: 1000) # dnsmasq (GID: 991) # clevis (GID: 996) # GID mismatches found: # ssh_keys : (Rollback GID: 101) (Booted GID: 999) # nobody : (Rollback GID: 65534) (Booted GID: 99) # polkitd : (Rollback GID: 995) (Booted GID: 998) # printadmin : (Rollback GID: 994) (Booted GID: 983) # systemd-coredump : (Rollback GID: 997) (Booted GID: 984) # input : (Rollback GID: 999) (Booted GID: 104) # render : (Rollback GID: 998) (Booted GID: 985) # Groups with subgroups: # Group: tss, Subgroup: clevis # Group: wheel, Subgroup: core # END # Narrow down the search to UIDs and GIDs missing/mismatch found above instead of the whole file system. # Use find with -uid and -gid flag, and prarllel processing to speed up the process. # Some initial checks to prevent the need for processing to save execution time. # Need to process the files based the following 3 cases. # 1. uid or gid = UNKNOWN as seen below which is due to the shift in ids and the old ID does not exist # [root@vm-1 ~]# stat /etc/ssh/ssh_host_ecdsa_key # File: /etc/ssh/ssh_host_ecdsa_key # Size: 492 Blocks: 8 IO Block: 4096 regular file # Device: fc03h/64515d Inode: 33879037 Links: 1 # Access: (0640/-rw-r-----) Uid: ( 0/ root) Gid: ( 101/ UNKNOWN) # Context: system_u:object_r:sshd_key_t:s0 # Access: 2024-11-29 11:52:32.042510648 +0000 # Modify: 2024-11-28 14:16:01.651000000 +0000 # Change: 2024-11-29 11:52:07.833734902 +0000 # Birth: 2024-11-29 11:52:07.833734902 +0000 # 2.Some files like below have different user probaly due to the shift in gid # example: dsnmasq which has gid in 991 in rollback_passwd is now missing # and the new bootc system has systemd-timesync assigned to 991 # [root@localhost ~]# stat /var/lib/dnsmasq # File: /var/lib/dnsmasq # Size: 6 Blocks: 0 IO Block: 4096 dir # ectory # Device: fc03h/64515d Inode: 33735167 Links: 2 # Access: (0755/drwxr-xr-x) Uid: ( 0/ root) Gid: ( 991/ # systemd-timesync) # Context: system_u:object_r:dnsmasq_lease_t:s0 # Access: 2024-12-01 18:39:07.618331912 +0000 # Modify: 2024-11-29 20:38:37.992367043 +0000 # Change: 2024-11-29 23:03:57.297643273 +0000 # Birth: 2024-11-29 20:38:37.992367043 +0000 # 3.Missing user(core) when upgrading from rpm-ostree system in bootc,its # kind of same behavior as rpm-ostree but do we want to change it, restore # the user. # [root@localhost etc]# stat /home/core/ # File: /home/core/ # Size: 53 Blocks: 0 IO Block: 4096 dir # ectory # Device: fc03h/64515d Inode: 33735133 Links: 3 # Access: (0700/drwx------) Uid: ( 1000/ UNKNOWN) Gid: ( 1000/ # UNKNOWN) # Context: unconfined_u:object_r:user_home_dir_t:s0 # Access: 2024-12-01 18:01:09.363246718 +0000 # Modify: 2024-11-30 08:42:18.323172433 +0000 # Change: 2024-11-30 08:42:18.323172433 +0000 # Birth: 2024-11-29 20:34:36.496949664 +0000