mirror_zfs/contrib/initramfs
Umer Saleem 27e8f56102
Fix inconsistent mount options for ZFS root
While mounting ZFS root during boot on Linux distributions from initrd,
mount from busybox is effectively used which executes mount system call
directly. This skips the ZFS helper mount.zfs, which checks and enables
the mount options as specified in dataset properties. As a result,
datasets mounted during boot from initrd do not have correct mount
options as specified in ZFS dataset properties.

There has been an attempt to use mount.zfs in zfs initrd script,
responsible for mounting the ZFS root filesystem (PR#13305). This was
later reverted (PR#14908) after discovering that using mount.zfs breaks
mounting of snapshots on root (/) and other child datasets of root have
the same issue (Issue#9461).

This happens because switching from busybox mount to mount.zfs correctly
parses the mount options but also adds 'mntpoint=/root' to the mount
options, which is then prepended to the snapshot mountpoint in
'.zfs/snapshot'. '/root' is the directory on Debian with initramfs-tools
where root filesystem is mounted before pivot_root. When Linux runtime
is reached, trying to access the snapshots on root results in
automounting the snapshot on '/root/.zfs/*', which fails.

This commit attempts to fix the automounting of snapshots on root, while
using mount.zfs in initrd script. Since the mountpoint of dataset is
stored in vfs_mntpoint field, we can check if current mountpoint of
dataset and vfs_mntpoint are same or not. If they are not same, reset
the vfs_mntpoint field with current mountpoint. This fixes the
mountpoints of root dataset and children in respective vfs_mntpoint
fields when we try to access the snapshots of root dataset or its
children. With correct mountpoint for root dataset and children stored
in vfs_mntpoint, all snapshots of root dataset are mounted correctly
and become accessible.

This fix will come into play only if current process, that is trying to
access the snapshots is not in chroot context. The Linux kernel API
that is used to convert struct path into char format (d_path), returns
the complete path for given struct path. It works in chroot environment
as well and returns the correct path from original filesystem root.

However d_path fails to return the complete path if any directory from
original root filesystem is mounted using --bind flag or --rbind flag
in chroot environment. In this case, if we try to access the snapshot
from outside the chroot environment, d_path returns the path correctly,
i.e. it returns the correct path to the directory that is mounted with
--bind flag. However inside the chroot environment, it only returns the
path inside chroot.

For now, there is not a better way in my understanding that gives the
complete path in char format and handles the case where directories from
root filesystem are mounted with --bind or --rbind on another path which
user will later chroot into. So this fix gets enabled if current
process trying to access the snapshot is not in chroot context.

With the snapshots issue fixed for root filesystem, using mount.zfs in
ZFS initrd script, mounts the datasets with correct mount options.

Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov>
Reviewed-by: Alexander Motin <mav@FreeBSD.org>
Reviewed-by: Ameer Hamza <ahamza@ixsystems.com>
Signed-off-by: Umer Saleem <usaleem@ixsystems.com>
Closes #16646
2024-10-17 09:09:39 -04:00
..
conf-hooks.d autoconf: use include directives instead of recursing down contrib 2022-05-10 10:19:44 -07:00
conf.d autoconf: use include directives instead of recursing down contrib 2022-05-10 10:19:44 -07:00
hooks initramfs: source user scripts from /e/z/initramfs-tools-load-key{,.d/*} 2023-04-12 10:08:49 -07:00
scripts Fix inconsistent mount options for ZFS root 2024-10-17 09:09:39 -04:00
Makefile.am Replace EXTRA_DIST with dist_noinst_DATA 2022-05-26 09:24:50 -07:00
README.md initramfs: source user scripts from /e/z/initramfs-tools-load-key{,.d/*} 2023-04-12 10:08:49 -07:00
zfsunlock Silence 'make checkbashisms' 2020-08-20 13:45:47 -07:00

Description

These scripts are intended to be used with initramfs-tools, which is a similar software product to dracut (which is used in Red Hat based distributions), and is mainly used by Debian GNU/Linux and derivatives.

These scripts share some common functionality with the SysV init scripts, primarily the /etc/zfs/zfs-functions script.

Configuration

Root pool/filesystem

Different distributions have their own standard on what to specify on the kernel command line to boot off a ZFS filesystem.

This script supports the following kernel command line argument combinations (in this order - first match wins):

  • rpool=<pool>
  • bootfs=<pool>/<dataset>
  • rpool=<pool> bootfs=<pool>/<dataset>
  • -B zfs-bootfs=<pool>/<fs>
  • root=<pool>/<dataset>
  • root=ZFS=<pool>/<dataset>
  • root=zfs:AUTO
  • root=zfs:<pool>/<dataset>
  • rpool=rpool

If a pool is specified, it will be used. Otherwise, in AUTO mode, all pools will be searched. Pools may be excluded from the search by listing them in ZFS_POOL_EXCEPTIONS in /etc/default/zfs.

Pools will be imported as follows:

  • Try /dev/disk/by-vdev if it exists; see /etc/zfs/vdev_id.conf.
  • Try /dev/disk/by-id and any other /dev/disk/by-* directories.
  • Try /dev.
  • Use the cache file if nothing else worked.

This order may be modified by setting ZPOOL_IMPORT_PATH in /etc/default/zfs.

If a dataset is specified, it will be used as the root filesystem. Otherwise, this script will attempt to find a root filesystem automatically (in the specified pool or all pools, as described above).

Filesystems below the root filesystem will be automatically mounted with no additional configuration necessary. For example, if the root filesystem is rpool/ROOT/rootfs, rpool/root/rootfs/var, rpool/root/rootfs/usr, etc. will be mounted (if they exist).

Snapshots

The <dataset> can be a snapshot. In this case, the snapshot will be cloned and the clone used as the root filesystem. Note:

  • If the snapshot does not exist, the base dataset (the part before @) is used as the boot filesystem instead.
  • If the resulting clone dataset already exists, it is destroyed.
  • The clone is created with mountpoint=none and canmount=noauto. The root filesystem is mounted manually by the initramfs script.
  • If no snapshot is specified on the root= kernel command line, but there is an @, the user will be prompted to choose a snapshot to use.

Extra options

The following kernel command line arguments are supported:

  • zfsdebug=(on,yes,1): Show extra debugging information
  • zfsforce=(on,yes,1): Force import the pool
  • rollback=(on,yes,1): Rollback to (instead of clone) the snapshot

Unlocking a ZFS encrypted root over SSH

To use this feature:

  1. Install the dropbear-initramfs package. You may wish to uninstall the cryptsetup-initramfs package to avoid warnings.
  2. Add your SSH key(s) to /etc/dropbear-initramfs/authorized_keys. Note that Dropbear does not support ed25519 keys before version 2020.79; in that case, use RSA (2048-bit or more) instead.
  3. Rebuild the initramfs with your keys: update-initramfs -u
  4. During the system boot, login via SSH and run: zfsunlock

Unlocking a ZFS encrypted root via alternate means

If present, a shell program at /etc/zfs/initramfs-tools-load-key and files matching /etc/zfs/initramfs-tools-load-key.d/* will be copied to the initramfs during generation and sourced to load the key, if required.

The $ENCRYPTIONROOT to load the key for and $KEYLOCATION variables are set, and all initramfs-tools functions are available; use unquoted $ZPOOL and $ZFS to run zpool and zfs.

A successful return (and loaded key) stops the search. A failure return is non-fatal, and loading keys proceeds as normal if no hook succeeds.

A trivial example of a key-loading drop-in that uses the BLAKE2 checksum of the file at the keylocation as the key follows.

key="$(b2sum "${KEYLOCATION#file://}")" || return
printf '%s\n' "${key%% *}" | $ZFS load-key -L prompt "$ENCRYPTIONROOT"