impermanence
impermanence copied to clipboard
FS corruption after hibernation
The BTRFS script provided in the README.org has some problems that could result in filesytem corruption or data loss.
-
Filesystem corruption. This script runs both during the normal boot and when resuming from hibernation. Mounting BTRFS right before resuming is equivalent to mounting the same filesystem twice at the same time (without proper safeguards). I had my filesystem corrupted and turned into an unmountable/unbootable state because of this.
The solution: instead of
postDeviceCommands, usepostResumeCommands. Seestage-1-init.shsource for the difference.Also, see "How to fix the FS corruption after hibernation" below.
-
Wiping the wrong subvolumes. The function
delete_subvolume_recursivelymight delete all the subvolumes on a disk (not just a particular subdirectory of/old_roots). This can occur ifold_rootsand its children are plain directories rather than subvolumes, which can happen after restoring disk contents from a backup.The reason is that the
-ooption inbtrfs subvolume listprints the subvolumes of the given parent subvolume, not the subvolumes of the given directory. To illustrate:$ ls nix old_roots persistent root $ sudo btrfs subvolume list -o . # note no subvolumes in `old_roots` ID 259 gen 2575 top level 5 path root ID 260 gen 2334 top level 5 path nix ID 261 gen 2575 top level 5 path persistent $ sudo btrfs subvolume list -o ./root # root is a subvolume, the output is expected ID 262 gen 1862 top level 259 path root/srv ID 263 gen 1865 top level 259 path root/var/lib/portables ID 264 gen 1865 top level 259 path root/var/lib/machines ID 265 gen 2511 top level 259 path root/tmp ID 266 gen 2511 top level 259 path root/var/tmp $ sudo btrfs subvolume list -o ./old_roots/2024-11-20_05:35:10 # (!) not a subvolume, the output lists unrelated subvolumes ID 259 gen 2575 top level 5 path root ID 260 gen 2334 top level 5 path nix ID 261 gen 2575 top level 5 path persistentIn this case,
delete_subvolume_recursively /old_roots/2024-11-20_05:35:10will happily delete/root,/nix, and/persistent.Possible solutions:
- Delete subvolumes using the
--recursiveflag instead of awkwardly unreliably bash function. Tho this flag is added very recently (v6.12, 2024-11-29) and not available in NixOS 24.11 which has v6.11. - Use the plain
rm -rf. Starting from the kernel v4.17,rmcould be used to delete subvolumes (see BTRFS changelog). Might be slower thanbtrfs subvolume delete. - Don't rely on the
-oflag and filter subvolumes on our side usingawkor bash string manipulation.
Combining the first two solutions:
btrfs subvolume delete --recursive -- "$i" || rm -rf -- "$i" - Delete subvolumes using the
-
find -mtime +30filters based on a boot time, not the "last used" time. Therefore, if you reboot after an uptime of more than one month, you'll end up with an empty/old_roots. Also, the same if your system clock got reset (e.g., CMOS battery is dead). Solutions:- Keep at least one old root regardless of its age.
- Delete old roots based on their date compared to the date of the latest root, not the current date.
-
(not a corruption/data loss, but a slight inconvenience) I'm not sure why the date format is
+%Y-%m-%-d_%H:%M:%Swith%-dand not with%d. This makes lexically sorting unusable, e.g.:2025-01-1,2025-01-20,2025-01-3.
How to fix the FS corruption after hibernation
In my case, the situation was similar to this SO thread, with these errors in dmesg (citing from SO):
parent transid verify failed on 109973766144 wanted 1823 found 1821
What did help:
- Mount BTRFS in
romode, copy the most important data to another disk and then recreate the filesystem from scratch. If you are lucky, it might work until you stumble upon a corrupted file. If not, see the next step. btrfs restoreto copy the data, then recreate FS from scratch.
What did not help: much everything else.
After copying the data back, make sure that /old_roots contains only subvolumes, otherwise the script will wipe your drive (see "Wiping the wrong subvolumes" above).
- I see. Personally, I don't use hibernation, so this did not occur to me. I'm also using a systemd intitrd version of this, which I now realize was never added to the README. Anyway, I've changed the README to use
postResumeCommandsnow. - While the error condition you list is a case of user error, it would still be nice to resolve. The first solution is the obvious choice, since I only wrote the function because there was no native recursive subvolume delete. We'll have to wait for it to be widely available, but I guess that shouldn't be too long. I tried using using
rm -rfirst, but it's not reliable enough, sadly. - (and 4) Yeah, while in theory this seemed nice, it simply doesn't work well enough in practice. I think the better solution is to label them serially and just keep a set number of roots instead.
First issue is actually exactly why I added postResumeCommands to nixpkgs; nice to see it put in use.
FYI, to prevent the same problem in systemd initrd, you need your service to have after = [ "local-fs-pre.target" ];
FYI, to prevent the same problem in systemd initrd, you need your service to have
after = [ "local-fs-pre.target" ];
@ElvishJerricco FYI the folllowing seems to work to bypass running on resume entirely.
(only relevant under systemd initrd)