#!/bin/sh -e
# SPDX-License-Identifier: GPL-2.0-or-later
# SPDX-FileCopyrightText: Copyright SUSE LLC

make_sure_snapshot_is_writable() {
  isRO="$(btrfs property get "/.snapshots/${snapshot}/snapshot/" ro)"
  if [ "${isRO}" = "ro=true" ]; then
    btrfs property set "/.snapshots/${snapshot}/snapshot/" ro false
  fi
}

reset_snapshot_writability() {
  if [ "${isRO}" = "ro=true" ]; then
    btrfs property set "/.snapshots/${snapshot}/snapshot/" ro true
  fi
}

create_reference_etc() {
  if [ -d "/.snapshots/${snapshot}/snapshot/etc/etc.syncpoint" ]; then
    rmdir "/.snapshots/${snapshot}/snapshot/etc/etc.syncpoint"
  fi
  # Syncing is only necessary when the snapshot is based on the currently running system.
  # "Current" means the snapshot number of the currently running /usr
  current="$(findmnt --target /usr --raw --noheadings --output FSROOT | tail -n 1 | cut -d '/' -f 4)"

  # If the parent snapshot was created from the running system, perform a sync now and use the current running /etc as syncpoint
  if [ -e "/.snapshots/${parent}/snapshot/etc/etc.syncpoint/transactional-update.comparewith" ] \
     && [ "$(cat "/.snapshots/${parent}/snapshot/etc/etc.syncpoint/transactional-update.comparewith")" = "${current}" ]; then
    # Snapshot first, then sync to avoid racing with changes to /etc in between.
    btrfs subvolume snapshot "/etc" "/.snapshots/${snapshot}/snapshot/etc/etc.syncpoint"
    /usr/libexec/transactional-update-sync-etc-state --keep-syncpoint "/.snapshots/${snapshot}/snapshot/etc/etc.syncpoint" "/.snapshots/${snapshot}/snapshot/etc" "/.snapshots/${parent}/snapshot/etc/etc.syncpoint"
    echo "${current}" > "/.snapshots/${snapshot}/snapshot/etc/etc.syncpoint/transactional-update.comparewith"
  # If it's the first descendant of the current system store a reference copy of the state before changes will be applied ...
  elif [ "${parent}" = "${current}" ]; then
    btrfs subvolume snapshot "/.snapshots/${snapshot}/snapshot/etc" "/.snapshots/${snapshot}/snapshot/etc/etc.syncpoint"
    echo "${parent}" > "/.snapshots/${snapshot}/snapshot/etc/etc.syncpoint/transactional-update.comparewith"
  # ... or if it's a consecutive snapshot, copy the already existing syncpoint
  elif [ -e "/.snapshots/${parent}/snapshot/etc/etc.syncpoint" ]; then
    btrfs subvolume snapshot "/.snapshots/${parent}/snapshot/etc/etc.syncpoint" "/.snapshots/${snapshot}/snapshot/etc/etc.syncpoint"
  else
    : # consecutive snapshot without syncpoint, skip syncing altogether
  fi
}

create_snapshot_post() {
  snapshot="${1}"

  # "Parent UUID" means which parent this snapshot was created from
  parentuuid="$(btrfs subvolume list -q / | grep -E " path @/.snapshots/${snapshot}/snapshot"$ | cut -f 9 -d ' ')"
  # "Parent" is the number of the parent snapshot (as shown by snapper)
  parent="$(btrfs subvolume list -u / | grep " uuid ${parentuuid} " | cut -f 9- -d ' ' | cut -f 3 -d '/')"
  # "Parent ID" means the parent subvolume in the file system tree
  parentid="$(btrfs subvolume list / | grep -E " path @/.snapshots/${parent}/snapshot"$ | cut -f 2 -d ' ')"

  make_sure_snapshot_is_writable

  rm -rf "/.snapshots/${snapshot}/snapshot/etc"
  if btrfs subvolume list / | grep -Eq " top level ${parentid} path (@/.snapshots/${parent}/snapshot/)?etc"; then
    btrfs subvolume snapshot "/.snapshots/${parent}/snapshot/etc" "/.snapshots/${snapshot}/snapshot/etc"
    create_reference_etc
  else
    # Migration from old system
    echo "Migrating from /etc overlays to /etc subvolume..."
    btrfs subvolume create "/.snapshots/${snapshot}/snapshot/etc"

    # Syncing old /etc state
    oldetcopts="$(findmnt --output OPTIONS --noheadings --fstab --tab-file "/.snapshots/${parent}/snapshot/etc/fstab" /etc | sed 's;/sysroot/;/;g')"
    mkdir "/var/lib/overlay/${parent}/rsync"
    trap 'rmdir "/var/lib/overlay/${parent}/rsync"' EXIT
    mount -t overlay overlay -o"${oldetcopts}" "/var/lib/overlay/${parent}/rsync"
    trap 'umount "/var/lib/overlay/${parent}/rsync" && rmdir "/var/lib/overlay/${parent}/rsync"' EXIT
    rsync --quiet --archive --xattrs --acls "/var/lib/overlay/${parent}/rsync/" "/.snapshots/${snapshot}/snapshot/etc"

    create_reference_etc

    # Add entry for /etc
    sed -i '/^overlay[[:space:]]\+\/etc[[:space:]]/d' "/.snapshots/${snapshot}/snapshot/etc/fstab"
    echo "/etc /etc none bind,x-initrd.mount 0 0" >> "/.snapshots/${snapshot}/snapshot/etc/fstab"

    # Clean up potential conflicting overlay cruft
    rm -rf "/var/lib/overlay/${snapshot}"
  fi

  # Bugfix: Older versions didn't include the x-initrd.mount flag
  awk -i inplace '$2 == "/etc" && $4 !~ /x-initrd.mount/ { $4 = $4 ",x-initrd.mount" } { print }' /etc/fstab

  reset_snapshot_writability
}

delete_snapshot_pre() {
  snapshot="${1}"

  # Only operate on snapshots with /etc as subvolume
  if btrfs subvolume list / | grep -q " @/.snapshots/${snapshot}/snapshot/etc"; then
    make_sure_snapshot_is_writable
    if btrfs subvolume list / | grep -q " @/.snapshots/${snapshot}/snapshot/etc/etc.syncpoint"; then
      btrfs subvolume delete "/.snapshots/${snapshot}/snapshot/etc/etc.syncpoint"
    fi
    btrfs subvolume delete "/.snapshots/${snapshot}/snapshot/etc"
  fi
}

if [ "$2" != "/" ]; then
  exit 0
fi
if [ -e "/.snapshots/${4}/snapshot/etc/fstab" ] && ! findmnt --mountpoint /etc --noheadings --output TARGET --tab-file "/.snapshots/${4}/snapshot/etc/fstab"; then
  echo "/etc is not a mount point - skipping..."
  exit 0
fi
if [ "$3" != "btrfs" ]; then
  echo "Unsupported file system $3!"
  exit 1
fi

if [ "$1" = "create-snapshot-post" ]; then
  create_snapshot_post "${4}"
elif [ "$1" = "delete-snapshot-pre" ]; then
  delete_snapshot_pre "${4}"
fi

