main
 1{
 2  den,
 3  lib,
 4  ...
 5}: {
 6  den.aspects.core.impermanence.btrfs = {
 7    settings = {
 8      partId = lib.mkOption {
 9        type = lib.types.str;
10        default = "";
11        description = ''
12          Disk part id (e.g., "/dev/disk/by-partuuid/...").
13          If not set, auto-detect when aspects.disk.disko is included.
14        '';
15      };
16      preserveTime = lib.mkOption {
17        type = lib.types.ints.positive;
18        default = 30;
19        description = ''
20          Time old root/home subvolumes preserved.
21        '';
22      };
23    };
24
25    nixos = {host, ...}: let
26      cfg = host.settings.core.impermanence.btrfs;
27
28      partDevice =
29        if cfg.partId != ""
30        then
31          if lib.hasPrefix "/dev/" cfg.partId
32          then cfg.partId
33          else "/dev/disk/by-partuuid/" + cfg.partId
34        else if (host.hasAspect den.aspects.disk.disko)
35        then "/dev/disk/by-partlabel/disk-main-nixos"
36        else abort "Host not included disk.disko aspect, Please set settings.core.impermanence.btrfs.partId.";
37
38      partDeviceUnit =
39        lib.removePrefix "-" (
40          lib.replaceStrings ["-" "/"] ["\\x2d" "-"] partDevice
41        )
42        + ".device";
43
44      script = subvol: ''
45        mkdir -p /mnt
46
47        mount ${partDevice} /mnt
48
49        mkdir -p /mnt/${subvol}_old
50        timestamp=$(date "+%Y-%m-%-d_%H:%M:%S")
51        rm -rf "/mnt/${subvol}_old/$timestamp"
52        mv /mnt/${subvol} /mnt/${subvol}_old/$timestamp
53
54        delete_subvolume_recursively() {
55            IFS=$'\n'
56            for i in $(btrfs subvolume list -o "$1" | cut -f 9- -d ' '); do
57                delete_subvolume_recursively "/mnt/$i"
58            done
59            btrfs subvolume delete "$1"
60        }
61
62        for i in $(find /mnt/${subvol}_old -maxdepth 1 -mtime +${toString cfg.preserveTime}); do
63            delete_subvolume_recursively "$i"
64        done
65
66        btrfs subvolume create /mnt/${subvol}
67        umount /mnt
68      '';
69    in {
70      boot.initrd.systemd.services = lib.mkIf (host.hasAspect den.aspects.disk.btrfs) {
71        rollback-btrfs-root = {
72          description = "Rollback and clean old btrfs root subvolumes";
73          wantedBy = ["initrd.target"];
74          before = ["sysroot.mount"];
75          after = [partDeviceUnit];
76          requires = [partDeviceUnit];
77          unitConfig.DefaultDependencies = false;
78          serviceConfig.Type = "oneshot";
79          script = script "root";
80        };
81        rollback-btrfs-home = {
82          description = "Rollback and clean old btrfs home subvolumes";
83          wantedBy = ["initrd.target"];
84          before = ["home.mount"];
85          after = [partDeviceUnit];
86          requires = [partDeviceUnit];
87          unitConfig.DefaultDependencies = false;
88          serviceConfig.Type = "oneshot";
89          script = script "home";
90        };
91      };
92    };
93  };
94}