Skyler Grey | 40ae7a0 | 2024-06-06 21:22:25 +0000 | [diff] [blame] | 1 | # 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, ... }: |
| 8 | let |
| 9 | cfg = config.clicks.storage.impermanence; |
| 10 | in |
| 11 | { |
| 12 | options.clicks.storage.impermanence = { |
| 13 | enable = lib.mkEnableOption "Enable impermanent rootfs with btrfs subvolumes"; |
Skyler Grey | d337740 | 2024-06-06 22:01:26 +0000 | [diff] [blame] | 14 | 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 Grey | 40ae7a0 | 2024-06-06 21:22:25 +0000 | [diff] [blame] | 23 | }; |
| 24 | volumes = { |
| 25 | mount = lib.mkOption { |
| 26 | type = lib.types.str; |
Skyler Grey | d337740 | 2024-06-06 22:01:26 +0000 | [diff] [blame] | 27 | description = "Path on rootfs device to the mounting subvolume, everything on here will be deleted"; |
Skyler Grey | 40ae7a0 | 2024-06-06 21:22:25 +0000 | [diff] [blame] | 28 | default = "@"; |
| 29 | }; |
| 30 | old_roots = lib.mkOption { |
| 31 | type = lib.types.str; |
Skyler Grey | d337740 | 2024-06-06 22:01:26 +0000 | [diff] [blame] | 32 | description = "Path on rootfs device to store old roots on"; |
Skyler Grey | 40ae7a0 | 2024-06-06 21:22:25 +0000 | [diff] [blame] | 33 | default = "old_roots"; |
| 34 | }; |
Skyler Grey | d337740 | 2024-06-06 22:01:26 +0000 | [diff] [blame] | 35 | 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 Grey | 40ae7a0 | 2024-06-06 21:22:25 +0000 | [diff] [blame] | 40 | }; |
| 41 | delete_days = lib.mkOption { |
| 42 | type = lib.types.int; |
Skyler Grey | d337740 | 2024-06-06 22:01:26 +0000 | [diff] [blame] | 43 | description = "How many days to wait before deleting an old root from `cfg.volumes.old_roots`"; |
Skyler Grey | 40ae7a0 | 2024-06-06 21:22:25 +0000 | [diff] [blame] | 44 | default = 7; |
| 45 | }; |
Skyler Grey | d337740 | 2024-06-06 22:01:26 +0000 | [diff] [blame] | 46 | 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 Grey | 40ae7a0 | 2024-06-06 21:22:25 +0000 | [diff] [blame] | 58 | }; |
| 59 | |
| 60 | config = lib.mkIf cfg.enable { |
| 61 | boot.initrd.postDeviceCommands = lib.mkAfter '' |
| 62 | mkdir /impermanent_fs |
Skyler Grey | d337740 | 2024-06-06 22:01:26 +0000 | [diff] [blame] | 63 | mount ${cfg.devices.root} /impermanent_fs |
Skyler Grey | 40ae7a0 | 2024-06-06 21:22:25 +0000 | [diff] [blame] | 64 | |
| 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 Grey | d337740 | 2024-06-06 22:01:26 +0000 | [diff] [blame] | 88 | device = cfg.devices.root; |
Skyler Grey | 40ae7a0 | 2024-06-06 21:22:25 +0000 | [diff] [blame] | 89 | fsType = "btrfs"; |
| 90 | options = [ "subvol=${cfg.volumes.mount}" ]; |
| 91 | }; |
Skyler Grey | d337740 | 2024-06-06 22:01:26 +0000 | [diff] [blame] | 92 | |
| 93 | fileSystems."/persist" = { |
| 94 | device = cfg.devices.persist; |
| 95 | neededForBoot = true; |
| 96 | fsType = "btrfs"; |
| 97 | }; |
| 98 | |
| 99 | environment.persistence."/persist/${cfg.volumes.persistent_data}" = { |
Skyler Grey | 3f7fba8 | 2024-07-04 01:10:30 +0000 | [diff] [blame] | 100 | directories = [ |
| 101 | "/var/lib/nixos" # https://github.com/nix-community/impermanence/issues/178 |
| 102 | ] ++ cfg.persist.directories; |
Skyler Grey | d337740 | 2024-06-06 22:01:26 +0000 | [diff] [blame] | 103 | files = [ |
| 104 | "/etc/machine-id" |
| 105 | "/etc/ssh/ssh_host_ed25519_key" |
| 106 | "/etc/ssh/ssh_host_ed25519_key.pub" |
| 107 | "/etc/ssh/ssh_host_rsa_key" |
| 108 | "/etc/ssh/ssh_host_rsa_key.pub" |
| 109 | ] ++ cfg.persist.files; |
| 110 | }; |
Skyler Grey | 40ae7a0 | 2024-06-06 21:22:25 +0000 | [diff] [blame] | 111 | }; |
| 112 | } |