|
|
@@ -15,12 +15,12 @@ Get started like so:
|
|
|
|
1. Symlink to files, for example
|
|
|
|
1. Symlink to files, for example
|
|
|
|
```
|
|
|
|
```
|
|
|
|
sudo ln -s <repo>/pacman-zfs-snapshot.sh /usr/local/bin/pacman-zfs-snapshot
|
|
|
|
sudo ln -s <repo>/pacman-zfs-snapshot.sh /usr/local/bin/pacman-zfs-snapshot
|
|
|
|
sudo ln -s <repo>/pacman-zfs-snapshot-install.hook /usr/share/libalpm/hooks/pacman-zfs-snapshot-install.hook
|
|
|
|
sudo ln -s <repo>/pacman-zfs-snapshot-install.hook /usr/share/libalpm/hooks/00-pacman-zfs-snapshot-install.hook
|
|
|
|
sudo ln -s <repo>/pacman-zfs-snapshot-remove.hook /usr/share/libalpm/hooks/pacman-zfs-snapshot-remove.hook
|
|
|
|
sudo ln -s <repo>/pacman-zfs-snapshot-remove.hook /usr/share/libalpm/hooks/00-pacman-zfs-snapshot-remove.hook
|
|
|
|
sudo ln -s <repo>/pacman-zfs-snapshot-upgrade.hook /usr/share/libalpm/hooks/pacman-zfs-snapshot-upgrade.hook
|
|
|
|
sudo ln -s <repo>/pacman-zfs-snapshot-upgrade.hook /usr/share/libalpm/hooks/00-pacman-zfs-snapshot-upgrade.hook
|
|
|
|
sudo ln -s <repo>/pacman-zfs-snapshot.conf /etc/pacman-zfs-snapshot.conf
|
|
|
|
sudo ln -s <repo>/pacman-zfs-snapshot.conf /etc/pacman-zfs-snapshot.conf
|
|
|
|
```
|
|
|
|
```
|
|
|
|
Note that while you may choose arbitrary locations for symlinks the `pacman-zfs-snapshot-*.hook` files reference `/usr/local/bin/pacman-zfs-snapshot`. Change that accordingly if you need to.
|
|
|
|
Note that while you may choose arbitrary locations for symlinks the `00-pacman-zfs-snapshot-*.hook` files reference `/usr/local/bin/pacman-zfs-snapshot`. Change that accordingly if you need to.
|
|
|
|
1. For datasets you want auto-snapshotted add property `space.quico:auto-snapshot=true`
|
|
|
|
1. For datasets you want auto-snapshotted add property `space.quico:auto-snapshot=true`
|
|
|
|
```
|
|
|
|
```
|
|
|
|
zfs set space.quico:auto-snapshot=true zpool/root/archlinux
|
|
|
|
zfs set space.quico:auto-snapshot=true zpool/root/archlinux
|
|
|
@@ -30,11 +30,30 @@ Get started like so:
|
|
|
|
|
|
|
|
|
|
|
|
# What's it do?
|
|
|
|
# What's it do?
|
|
|
|
|
|
|
|
|
|
|
|
In `pacman` on every `PreTransaction`, meaning right before any actual operation on a package begins, we trigger a ZFS snapshot. By default we identify the active system dataset by doing `findmnt / --noheadings --output source`. If exactly one source returns that is the exact name of a ZFS dataset in an imported zpool we create a snapshot on it. If no source returns we silently exit. If more than one source returns we raise an error and halt the `pacman` transaction.
|
|
|
|
In `pacman` on every `PreTransaction`, meaning right before any actual operation on a package begins, we trigger a ZFS snapshot. This happens via a so-called hook which is a plain text config file. Hook files make use of the Arch Linux Package Management (ALPM) library, also known as `libalpm` for which `pacman` is a frontend. By default hooks are stored in `/usr/share/libalpm/hooks`. Additionally `/etc/pacman.conf` has a directory configured as:
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
#HookDir = /etc/pacman.d/hooks/
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
Hook files from both directories are collectively parsed and executed in lexicographical order. Hook names from _this_ repo begin with `00-*` so on a default Arch Linux they are the first to be executed during `pacman` transactions.
|
|
|
|
|
|
|
|
|
|
|
|
We retain two different snapshot chains, one for `pacman` transactions that only affect what we are calling _trivial_ packages and a separate chain for _important_ packages. By default only the exact regular expression package name match `^(linux|systemd|zfs-(dkms|utils))$` is considered important. Whenever an important package is affected by a transaction a snapshot goes into the corresponding chain. In all other cases - when an important package is not affected - snapshots go into the trivial chain.
|
|
|
|
For ZFS snapshots intended to save your bacon the `00-*` naming convention is particularly critical. In `/usr/share/libalpm/hooks` you can see for example that when a kernel upgrade happens `60-mkinitcpio-remove.hook` is executed (deleting your existing `vmlinuz-*` kernel image for example at `/boot/vmlinuz-linux`). After that if you're using the `zfs-dkms` package which itself requires `dkms` which in turn installs `71-dkms-remove.hook` this hook removes your ZFS kernel module files. Both the `60-*` and optionally the `71-*` hook (for `zfs-dkms` users) run early due to their naming. If we don't create a snapshot before them we end up creating a snapshot that has not kernel image and no ZFS kernel module files. Out `00-*` hook files are executed first ensuring that a snapshot can safely return you to a working system.
|
|
|
|
|
|
|
|
|
|
|
|
The _trivial_ snapshot chain by default keeps 15 snapshots, the _important_ chain keeps 5. The thought process here is that you will likely not futz around with a kernel every day whereas you may very well install arbitrary packages multiple times a day. Snapshots should keep you safe for a couple of days hence the defaults of 5 and 15 snapshots, respectively.
|
|
|
|
By default we identify the active system dataset by doing `findmnt / --noheadings --output source` which for example returns:
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
zpool/root/archlinux
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
If exactly one source returns that is the exact name of a ZFS dataset in an imported zpool we create a snapshot on it. If no source returns we silently exit. If more than one source returns we raise an error and halt the `pacman` transaction.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
We retain two different snapshot chains, one for `pacman` transactions that only affect what we are calling _trivial_ packages and a separate chain for _important_ packages. By default only the exact regular expression package name match `^(linux|systemd|zfs-(dkms|utils))$` is considered important so in plain English any one of:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- `linux`
|
|
|
|
|
|
|
|
- `systemd`
|
|
|
|
|
|
|
|
- `zfs-dkms`
|
|
|
|
|
|
|
|
- `zfs-utils`
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Whenever an important package is affected by a transaction a snapshot goes into the corresponding chain. In all other cases - when an important package is not affected - snapshots go into the trivial chain.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The _trivial_ snapshot chain by default keeps 25 snapshots, the _important_ chain keeps 5. The thought process here is that you will likely not futz around with a kernel every day whereas you may very well install arbitrary packages multiple times a day. Snapshots should keep you safe for a couple of days hence the defaults of 5 and 15 snapshots, respectively.
|
|
|
|
|
|
|
|
|
|
|
|
Snapshots may look like so:
|
|
|
|
Snapshots may look like so:
|
|
|
|
```
|
|
|
|
```
|
|
|
@@ -55,6 +74,27 @@ Severity based on affected packages, here trivial ───────┘
|
|
|
|
|
|
|
|
|
|
|
|
Have a look at `pacman-zfs-snapshot.conf` as well, its comments should be clear enough to get you going.
|
|
|
|
Have a look at `pacman-zfs-snapshot.conf` as well, its comments should be clear enough to get you going.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
# Rollback
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
After a rollback for example via the excellent [ZFSBootMenu](https://docs.zfsbootmenu.org/) `pacman` and all AUR helpers you may be using will consider the `pacman` database to be locked. No `pacman` transactions can start, you will for example see:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- In `pacman`
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
# pacman -Syu
|
|
|
|
|
|
|
|
:: Synchronizing package databases...
|
|
|
|
|
|
|
|
error: failed to synchronize all databases (unable to lock database)
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
- In `paru`
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
$ paru
|
|
|
|
|
|
|
|
:: Pacman is currently in use, please wait...
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
The moment a snapshot was created `pacman` was already in a transaction so it had already written its lock file to `/var/lib/pacman/db.lck`. After a clean finish `pacman` would have deleted that lock itself but since you rolled back to a point mid-transaction it's still there. Just delete the file and you're good to go:
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
sudo rm /var/lib/pacman/db.lck
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
# Development
|
|
|
|
# Development
|
|
|
|
|
|
|
|
|
|
|
|
## Conventional commits
|
|
|
|
## Conventional commits
|
|
|
|