nix-community / impermanence

Modules to help you handle persistent state on systems with ephemeral root storage [maintainer=@talyz]
MIT License
1.01k stars 76 forks source link

Handle bind mount of directory that already has content #169

Open willbush opened 4 months ago

willbush commented 4 months ago

I noticed that if I have a folder (e.g. test):

~
❯ mkdir test

~
❯ touch test/hi.txt

~
❯ ls test
hi.txt

And I want to persist test folder across reboots, then I add it to:

{
  environment.persistence."/nix/persist" = {
    # ...
    users.will = {
      directories = [
        "test"
      ];
      # ...
    };
  };
}
~
❯ sudo nixos-rebuild switch --flake '/home/will/code/system/#blazar'
[sudo: authenticate] Password:
warning: Git tree '/home/will/code/system' is dirty
building the system configuration...
warning: Git tree '/home/will/code/system' is dirty
warning: `--preserve-env` has not yet been implemented and will be ignored
warning: `--preserve-env` has not yet been implemented and will be ignored
warning: `--preserve-env` has not yet been implemented and will be ignored
activating the configuration...
Warning: Source directory '/nix/persist/home/will/test' does not exist; it will be created for you with the following permissions: owner: 'will:users', mode: '0755'.
setting up /etc...
reloading user units for will...
restarting sysinit-reactivation.target
the following new units were started: home-will-test.mount, libvirtd.service, sysinit-reactivation.target, systemd-tmpfiles-resetup.service

~
❯ ls test

~
❯ mount | rg test
/dev/mapper/crypted on /home/will/test type ext4 (rw,relatime,x-gvfs-hide)

It silently deletes the contents of the test folder. Shouldn't this be an error similar to when persisting files?

willbush commented 4 months ago

Looked into this a bit more and realized it's not deleting anything.

~/tester
❯ tree -a
.
├── a
│   └── test.txt
└── b

3 directories, 1 file

~/tester
❯ sudo mount -v -o bind ./b ./a
mount: /home/will/tester/b bound on /home/will/tester/a.

~/tester
❯ tree -a
.
├── a
└── b

3 directories, 0 files

~/tester
❯ sudo umount ./a

~/tester
❯ tree -a
.
├── a
│   └── test.txt
└── b

3 directories, 1 file

When using bind mount (mount -o bind) a directory (./b) to another directory (./a), the contents of ./a are "hidden", and instead, the contents of ./b become visible within ./a.

In my case, tmpfs root, the data is actually only lost when rebooting.

I still think there should be an error message though. Perhaps a check can be added to create-directories.bash?

Perhaps a more robust solution would be to move the data into /tmp and then back after mounting?

KFearsoff commented 3 months ago

Yes please. The current behavior of silently bind mounting is not convenient or obvious.

Apparently, there's a project that automates persisting: https://github.com/Geometer1729/persist-retro . I think this is not the best approach, and we should just throw an error if the directory is detected. The reason for that is there might be running processes that are working on the folder already. Unless we can figure out a heuristic that helps us not break running processes while moving the contents, we shouldn't do that.