blob: eb8a72e121b81492bb63b5bcccc88b986b64e7ce [file] [log] [blame]
Skyler Grey40ae7a02024-06-06 21:22:25 +00001# SPDX-FileCopyrightText: 2024 Clicks Codes
2# SPDX-FileCopyrightText: 2020 Nix Community Projects
3#
4# SPDX-License-Identifier: MIT
5# postDeviceCommands based of code from https://github.com/nix-community/impermanence/tree/d5f1ed7141fa407880ff5956ded2c88a307ca940?tab=readme-ov-file#btrfs-subvolumes
6
7{ lib, config, ... }:
8let
9 cfg = config.clicks.storage.impermanence;
10in
11{
12 options.clicks.storage.impermanence = {
13 enable = lib.mkEnableOption "Enable impermanent rootfs with btrfs subvolumes";
Skyler Greyd3377402024-06-06 22:01:26 +000014 devices = {
15 root = lib.mkOption {
16 type = lib.types.str;
17 description = "Rootfs device path";
18 };
19 persist = lib.mkOption {
20 type = lib.types.str;
21 description = "Persistent data device path";
22 };
Skyler Grey40ae7a02024-06-06 21:22:25 +000023 };
24 volumes = {
25 mount = lib.mkOption {
26 type = lib.types.str;
Skyler Greyd3377402024-06-06 22:01:26 +000027 description = "Path on rootfs device to the mounting subvolume, everything on here will be deleted";
Skyler Grey40ae7a02024-06-06 21:22:25 +000028 default = "@";
29 };
30 old_roots = lib.mkOption {
31 type = lib.types.str;
Skyler Greyd3377402024-06-06 22:01:26 +000032 description = "Path on rootfs device to store old roots on";
Skyler Grey40ae7a02024-06-06 21:22:25 +000033 default = "old_roots";
34 };
Skyler Greyd3377402024-06-06 22:01:26 +000035 persistent_data = lib.mkOption {
36 type = lib.types.str;
37 description = "Path on persist device to store persistent data on";
38 default = "data";
39 };
Skyler Grey40ae7a02024-06-06 21:22:25 +000040 };
41 delete_days = lib.mkOption {
42 type = lib.types.int;
Skyler Greyd3377402024-06-06 22:01:26 +000043 description = "How many days to wait before deleting an old root from `cfg.volumes.old_roots`";
Skyler Grey40ae7a02024-06-06 21:22:25 +000044 default = 7;
45 };
Skyler Greyd3377402024-06-06 22:01:26 +000046 persist = {
47 directories = lib.mkOption {
48 type = lib.types.listOf lib.types.str;
49 description = "List of directories to store between boots";
50 default = [ ];
51 };
52 files = lib.mkOption {
53 type = lib.types.listOf lib.types.str;
54 description = "List of files to store between boots";
55 default = [ ];
56 };
57 };
Skyler Grey40ae7a02024-06-06 21:22:25 +000058 };
59
60 config = lib.mkIf cfg.enable {
61 boot.initrd.postDeviceCommands = lib.mkAfter ''
62 mkdir /impermanent_fs
Skyler Greyd3377402024-06-06 22:01:26 +000063 mount ${cfg.devices.root} /impermanent_fs
Skyler Grey40ae7a02024-06-06 21:22:25 +000064
65 if [[ -e /impermanent_fs/${cfg.volumes.mount} ]]; then
66 mkdir -p /impermanent_fs/${cfg.volumes.old_roots}
67 timestamp=$(date --date="@$(stat -c %Y /impermanent_fs/${cfg.volumes.mount})" "+%Y-%m-%-d_%H:%M:%S")
68 mv /impermanent_fs/${cfg.volumes.mount} "/impermanent_fs/${cfg.volumes.old_roots}/$timestamp"
69 fi
70
71 delete_subvolume_recursively() {
72 IFS=$'\n'
73 for i in $(btrfs subvolume list -o "$1" | cut -f 9- -d ' '); do
74 delete_subvolume_recursively "/impermanent_fs/$i"
75 done
76 btrfs subvolume delete "$1"
77 }
78
79 for i in $(find /impermanent_fs/${cfg.volumes.old_roots}/ -maxdepth 1 -mtime +${builtins.toString cfg.delete_days}); do
80 delete_subvolume_recursively "$i"
81 done
82
83 btrfs subvolume create /impermanent_fs/${cfg.volumes.mount}
84 umount /impermanent_fs
85 '';
86
87 fileSystems."/" = {
Skyler Greyd3377402024-06-06 22:01:26 +000088 device = cfg.devices.root;
Skyler Grey40ae7a02024-06-06 21:22:25 +000089 fsType = "btrfs";
90 options = [ "subvol=${cfg.volumes.mount}" ];
91 };
Skyler Greyd3377402024-06-06 22:01:26 +000092
93 fileSystems."/persist" = {
94 device = cfg.devices.persist;
95 neededForBoot = true;
96 fsType = "btrfs";
97 };
98
99 environment.persistence."/persist/${cfg.volumes.persistent_data}" = {
100 directories = [ ] ++ cfg.persist.directories;
101 files = [
102 "/etc/machine-id"
103 "/etc/ssh/ssh_host_ed25519_key"
104 "/etc/ssh/ssh_host_ed25519_key.pub"
105 "/etc/ssh/ssh_host_rsa_key"
106 "/etc/ssh/ssh_host_rsa_key.pub"
107 ] ++ cfg.persist.files;
108 };
Skyler Grey40ae7a02024-06-06 21:22:25 +0000109 };
110}