You are not logged in.

#1 2021-06-03 10:25:58

7thCore
Member
Registered: 2018-06-09
Posts: 75

Custom btrfs snapshot hook not working sometimes

So i wrote my own pacman hook and script. The intention was for it to make bootable btrfs snapshots before any update. That also brings up the problem of systemd-boot but I got around that by making the boot partition slightly larger (5GB) then mounting the boot partition to /mnt/backup/boot and then bind mount the correct folders for the live system and snapshots to boot.

Anyway the problem is that sometimes when copying initramfs files, those don't get copied. Only the ucode file gets copied. But this issue isn't a consistent one. Sometimes it works perfectly and other times it dosen't. It's like the files aren't there.

Here is the hook file that is located in /etc/pacman.d/hooks/system-snapshot.hook

[Trigger]
Operation = Upgrade
Type = Package
Target = *

[Action]
Description = Pre-upgrade system snapshot
When = PreTransaction
Exec = /bin/sh -c "/etc/pacman.d/hooks.bin/system-snapshot.sh"

And the script file wich is located in /etc/pacman.d/hooks.bin/system-snapshot.sh

#!/bin/sh

TIME=$(date +"%Y-%m-%d_%H-%M")
TIME_SEC=$(date +"%s")
TIME_MIN="3600"
MAX_SNAP="3"

ENCRYPTED="0"
CRYPTROOTUUID=""
UNCRYPTROOTUUID="8a7cb6cd-fe5f-479f-a76f-d1d92babb9e6"
CRYPTSWAPUUID=""
UNCRYPTSWAPUUID="89e057bd-bdf6-4899-9d92-d385eadd14d8"

#Create a new btrfs snapshot
create_btrfs_snapshot() {
	VOLUME="$1"
	echo ":: System snapshot: Creating new btrfs "$VOLUME" snapshot..."
	btrfs subvolume snapshot /mnt/backup-mnt/_active/"$VOLUME" /mnt/backup-mnt/_snapshots/"$VOLUME"_"$TIME"_"$TIME_SEC"
	sleep 1
	echo ":: System snapshot: Btrfs "$VOLUME" snapshot creation complete."
}

#Delete old btrfs snapshots
delete_old_btrfs_snapshots() {
	VOLUME="$1"
	echo ":: System snapshot: Deleting old btrfs "$VOLUME" snapshots..."

	#Get all snapshot timestamps and add them to an array.
	for i in /mnt/backup-mnt/_snapshots/"$VOLUME"_*; do
		TIME_SNAPSHOT_DELETE=$(echo "$i" | awk -F '_' '{print $NF}')
		DEL_VOL_ARRAY+=($TIME_SNAPSHOT_DELETE)
	done

	#Check array and delete old snapshots except the newest three.
	while [ "${#DEL_VOL_ARRAY[@]}" -gt "$MAX_SNAP" ]; do
		TIME_OLDEST_SNAPSHOT_DELETE=$(printf '%s\n' "${DEL_VOL_ARRAY[@]}" | awk '$1 < m || NR == 1 { m = $1 } END { print m }')
		btrfs subvolume delete /mnt/backup-mnt/_snapshots/"$VOLUME"_*"$TIME_OLDEST_SNAPSHOT_DELETE"
		sleep 1
		for i in "${!DEL_VOL_ARRAY[@]}"; do
			if [[ "${DEL_VOL_ARRAY[$i]}" = "$TIME_OLDEST_SNAPSHOT_DELETE" ]]; then
				unset DEL_VOL_ARRAY[$i]
			fi
		done
	done

	unset DEL_VOL_ARRAY
	echo ":: System snapshot: Old btrfs "$VOLUME" snapshot deletion complete."
}

#Create a new boot snapshot
create_boot_snapshot() {
	echo ":: System snapshot: Creating new boot snapshot..."

	#Create a new boot snapshot folder and copy the pre-upgraded boot files to it.
	mkdir /mnt/backup-boot/installs/snapshots/arch_${TIME}_${TIME_SEC}

	for boot_file in /mnt/backup-boot/installs/active/arch/*; do
		echo ":: System snapshot: Copying ${boot_file} to boot snapshot arch_${TIME}_${TIME_SEC}"
		cp $boot_file /mnt/backup-boot/installs/snapshots/arch_${TIME}_${TIME_SEC}/
	done

	#Create a systemd-boot loader entry for the snapshot.
	if [[ "$ENCRYPTED" = "0" ]]; then
		cat > /mnt/backup-boot/loader/entries/arch-snapshot_"$TIME"_"$TIME_SEC".conf <<- EOC
		title Arch Linux (Snapshot ${TIME})
		linux /installs/snapshots/arch_${TIME}_${TIME_SEC}/vmlinuz-linux
		initrd /installs/snapshots/arch_${TIME}_${TIME_SEC}/intel-ucode.img
		initrd /installs/snapshots/arch_${TIME}_${TIME_SEC}/initramfs-linux.img
		options root=UUID=${UNCRYPTROOTUUID} rootflags=subvol=_snapshots/root-arch_${TIME}_${TIME_SEC} resume=UUID=${UNCRYPTSWAPUUID} rw
		EOC
	else
		cat > /mnt/backup-boot/loader/entries/arch-snapshot_"$TIME"_"$TIME_SEC".conf <<- EOC
		title Arch Linux (Snapshot ${TIME})
		linux /installs/snapshots/arch_${TIME}_${TIME_SEC}/vmlinuz-linux
		initrd /installs/snapshots/arch_${TIME}_${TIME_SEC}/intel-ucode.img
		initrd /installs/snapshots/arch_${TIME}_${TIME_SEC}/initramfs-linux.img
		options luks.name=${CRYPTROOTUUID}=crypt_luks0 luks.name=${CRYPTSWAPUUID}=crypt_luks1 root=UUID=${UNCRYPTROOTUUID} rootflags=subvol=_snapshots/root-arch_${TIME}_${TIME_SEC} resume=UUID=$UNCRYPTSWAPUUID rw
		EOC
	fi
	
	echo ":: System snapshot: Created boot snapshot /mnt/backup-boot/installs/snapshots/arch_${TIME}_${TIME_SEC}"
	echo ":: System snapshot: Created systemd-boot entry /mnt/backup-boot/loader/entries/arch-snapshot_${TIME}_${TIME_SEC}.conf"
	echo ":: System snapshot: Boot snapshot creation complete."
}

#Delete old boot snapshots
delete_old_boot_snapshots() {
	echo ":: System snapshot: Deleting old boot snapshots..."
	#Get all boot snapshot timestamps and add them to an array.
	for i in /mnt/backup-boot/installs/snapshots/arch_*; do
		TIME_BOOT_SNAPSHOT_DELETE=$(echo "$i" | awk -F '_' '{print $NF}')
		DEL_BOOT_ARRAY+=($TIME_BOOT_SNAPSHOT_DELETE)
	done

	#Check array and delete old boot snapshots except the newest three.
	while [ "${#DEL_BOOT_ARRAY[@]}" -gt "$MAX_SNAP" ]; do
		TIME_OLDEST_BOOT_DELETE=$(printf '%s\n' "${DEL_BOOT_ARRAY[@]}" | awk '$1 < m || NR == 1 { m = $1 } END { print m }')
		DELETE_SNAP=$(find /mnt/backup-boot/installs/snapshots/ -maxdepth 1 -name "arch_*${TIME_OLDEST_BOOT_DELETE}")
		DELETE_SNAP_ENTRY=$(find /mnt/backup-boot/loader/entries/ -maxdepth 1 -name "arch-snapshot*${TIME_OLDEST_BOOT_DELETE}.conf")
		#rm -rf /mnt/backup-boot/installs/snapshots/arch_*"$TIME_OLDEST_BOOT_DELETE"
		#rm /mnt/backup-boot/loader/entries/arch-zen*"$TIME_OLDEST_BOOT_DELETE".conf
		rm -rf "$DELETE_SNAP"
		rm "$DELETE_SNAP_ENTRY"
		sleep 1
		for i in "${!DEL_BOOT_ARRAY[@]}"; do
			if [[ "${DEL_BOOT_ARRAY[$i]}" = "$TIME_OLDEST_BOOT_DELETE" ]]; then
				unset DEL_BOOT_ARRAY[$i]
			fi
		done
		if [ ! -z "$DELETE_SNAP" ]; then
			echo ":: System snapshot: Deleted boot snapshot ${DELETE_SNAP}"
		fi
		if [ ! -z "$DELETE_SNAP_ENTRY" ]; then
			echo ":: System snapshot: Deleted systemd-boot entry ${DELETE_SNAP_ENTRY}"
		fi
	done
	echo ":: System snapshot: Old boot snapshot deletion complete."
}

#Replace fstab entries for volumes
replace_fstab_entries() {
	VOLUME="$1"
	#Replace the btrfs subvolume names in the snapshot's /etc/fstab.
	echo ":: System snapshot: Changing snapshot fstab for volume ${VOLUME}"
	sed -i "s*subvol=_active/${VOLUME}*subvol=_snapshots/${VOLUME}_${TIME}_${TIME_SEC}*g" /mnt/backup-mnt/_snapshots/root-arch_"$TIME"_"$TIME_SEC"/etc/fstab
	echo ":: System snapshot: Changing of fstab for volume ${VOLUME} complete"
}

#Replace fstab entries for boot
replace_fstab_boot() {
	#Replace the boot and boot bind mount names in /etc/fstab.
	echo ":: System snapshot: Changing snapshot fstab for boot."
	sed -i "s*mnt/backup-boot/installs/active/arch*mnt/backup-boot/installs/snapshots/arch_${TIME}_${TIME_SEC}*g" /mnt/backup-mnt/_snapshots/root-arch_"$TIME"_"$TIME_SEC"/etc/fstab
	echo ":: System snapshot: Changing of fstab for boot complete."
}

echo ":: System snapshot: Beginning system snapshot generation."

if [[ ! -d /mnt/backup-mnt ]]; then
	mkdir -p /mnt/backup-mnt
fi

mount --uuid ${UNCRYPTROOTUUID} /mnt/backup-mnt

for ACTIVE_VOLUME in root-arch home-arch; do
	#Check the timestamp of the last snapshot generated. If it's withing the threshold, no new snapshots will be generated and the script will exit.
	echo ":: System snapshot: Checking time of last btrfs "$ACTIVE_VOLUME" snapshot generated..."
	if ls /mnt/backup-mnt/_snapshots/"$ACTIVE_VOLUME"_* &> /dev/null; then
		for i in /mnt/backup-mnt/_snapshots/"$ACTIVE_VOLUME"_*; do
			TIME_SNAPSHOT_CHECK=$(echo "$i" | awk -F '_' '{print $NF}')
			TIME_DIFFRENCE=$(($(date +"%s")-$TIME_SNAPSHOT_CHECK))
			TIME_VOL_ARRAY+=($TIME_DIFFRENCE)
		done

		TIME_OLDEST_SNAPSHOT=$(printf '%s\n' "${TIME_VOL_ARRAY[@]}" | awk '$1 < m || NR == 1 { m = $1 } END { print m }')

		if [ "$TIME_OLDEST_SNAPSHOT" -le "$TIME_MIN" ]; then
			echo ":: System snapshot: Pre-upgrade btrfs "$ACTIVE_VOLUME" snapshot canceled. Time threshold not met."
		else
			create_btrfs_snapshot $ACTIVE_VOLUME
			delete_old_btrfs_snapshots $ACTIVE_VOLUME
			replace_fstab_entries $ACTIVE_VOLUME
			if [[ "$ACTIVE_VOLUME" = "root-arch" ]]; then
				create_boot_snapshot
				delete_old_boot_snapshots
				replace_fstab_boot
			fi
		fi
		unset TIME_VOL_ARRAY
	else
		create_btrfs_snapshot $ACTIVE_VOLUME
		replace_fstab_entries $ACTIVE_VOLUME
		if [[ "$ACTIVE_VOLUME" = "root-arch" ]]; then
			create_boot_snapshot
			replace_fstab_boot
		fi
	fi
done

umount -R /mnt/backup-mnt

echo ":: System snapshot: Complete."

Snapshots, boot entries generation and old snapshot deletion works fine. Just the copying of boot files is giving me trouble.

Any help is appreciated.


The Linux philosophy is 'Laugh in the face of danger'. Oops. Wrong One. 'Do it yourself'. Yes, that's it.

Offline

#2 2021-06-03 11:15:56

Manix
Member
Registered: 2021-04-17
Posts: 81

Re: Custom btrfs snapshot hook not working sometimes

Hi

snap-pac does the same job !

https://github.com/wesbarnett/snap-pac

and for reading only :

btrfs property set /mnt/@snapshots/#/snapshot ro false

https://wiki.archlinux.org/title/Snapper


I don't love rosbeef

Offline

#3 2021-06-03 18:56:41

7thCore
Member
Registered: 2018-06-09
Posts: 75

Re: Custom btrfs snapshot hook not working sometimes

Yeah well I'm more of a DIY type of guy. I love to mess around and experiment with my system. Thanks for letting me know tho.

Last edited by 7thCore (2021-06-03 18:57:32)


The Linux philosophy is 'Laugh in the face of danger'. Oops. Wrong One. 'Do it yourself'. Yes, that's it.

Offline

#4 2021-06-04 07:06:20

Manix
Member
Registered: 2021-04-17
Posts: 81

Re: Custom btrfs snapshot hook not working sometimes

Yeah well I'm more of a DIY type of guy. I love to mess around and experiment with my system. Thanks for letting me know tho.

no worries man!
in this case I leave the hand to another, too complex for me smile
good luck!


I don't love rosbeef

Offline

Board footer

Powered by FluxBB