{ config, lib, pkgs, ... }: with lib; let cfg = config.sydnix.impermanence.rollback; in { options = { sydnix.impermanence.rollback = { enable = mkEnableOption "rollback of the root filesystem"; device = mkOption { description = "Device to be wiped"; type = types.str; }; subvolume = mkOption { description = '' The subvolume to be rolled back. Usually, this should be where your root filesystem resides. ''; type = types.str; }; }; }; config = mkIf cfg.enable { boot.initrd.systemd = { services.impermance-btrfs-rolling-root = { description = "Archiving existing BTRFS root subvolume and creating a fresh one"; unitConfig.DefaultDependencies = false; serviceConfig = { # The script needs to run to completion before this service # is done Type = "oneshot"; # NOTE: to be able to see errors in your script do this StandardOutput = "journal+console"; StandardError = "journal+console"; }; # This service is required for boot to succeed requiredBy = ["initrd.target"]; # Should complete before any file systems are mounted before = ["sysroot.mount"]; # Wait until the root device is available # If you're altering a different device, specify its device unit explicitly. # see: systemd-escape(1) requires = ["initrd-root-device.target"]; after = [ "initrd-root-device.target" # Allow hibernation to resume before trying to alter any data "local-fs-pre.target" ]; script = '' # Mount the btrfs filesystem. mkdir -p /btrfs-tmp mount -t btrfs "${cfg.device}" /btrfs-tmp # If the moribund subvolume exists, send it do 'death row' (old-roots), # where live for about three days before its eventual deletion. if [[ -e "/btrfs-tmp/${cfg.subvolume}" ]]; then mkdir -p /btrfs-tmp/old-roots timestamp=$(date --date="@$(stat -c %Y "/btrfs-tmp/${cfg.subvolume}")" "+%Y-%m-%-d_%H:%M:%S") mv "/btrfs-tmp/${cfg.subvolume}" "/btrfs-tmp/old-roots/$timestamp" fi delete_subvolume_recursively() { IFS=$'\n' for i in $(btrfs subvolume list -o "$1" | cut -f 9- -d ' '); do delete_subvolume_recursively "/btrfs-tmp/$i" done btrfs subvolume delete "$1" } # Delete previous roots older than three days. # TODO: I would prefer archiving the last N previous roots, rather than # time. for i in $(find /btrfs-tmp/old-roots/ -maxdepth 1 -mtime +3); do delete_subvolume_recursively "$i" done btrfs subvolume create "/btrfs-tmp/${cfg.subvolume}" umount /btrfs-tmp ''; }; # NOTE: path = [...]; doesnt work for initrd, use full paths in # your script or extraBin extraBin = { "mkdir" = "${pkgs.coreutils}/bin/mkdir"; "date" = "${pkgs.coreutils}/bin/date"; "stat" = "${pkgs.coreutils}/bin/stat"; "mv" = "${pkgs.coreutils}/bin/mv"; "find" = "${pkgs.findutils}/bin/find"; "btrfs" = "${pkgs.btrfs-progs}/bin/btrfs"; }; }; }; }