1eda14cbcSMatt Macy# ZFS boot stub for initramfs-tools. 2eda14cbcSMatt Macy# 3eda14cbcSMatt Macy# In the initramfs environment, the /init script sources this stub to 4eda14cbcSMatt Macy# override the default functions in the /scripts/local script. 5eda14cbcSMatt Macy# 6eda14cbcSMatt Macy# Enable this by passing boot=zfs on the kernel command line. 7eda14cbcSMatt Macy# 816038816SMartin Matuska# $quiet, $root, $rpool, $bootfs come from the cmdline: 916038816SMartin Matuska# shellcheck disable=SC2154 10eda14cbcSMatt Macy 11eda14cbcSMatt Macy# Source the common functions 12eda14cbcSMatt Macy. /etc/zfs/zfs-functions 13eda14cbcSMatt Macy 14eda14cbcSMatt Macy# Start interactive shell. 15eda14cbcSMatt Macy# Use debian's panic() if defined, because it allows to prevent shell access 16eda14cbcSMatt Macy# by setting panic in cmdline (e.g. panic=0 or panic=15). 17eda14cbcSMatt Macy# See "4.5 Disable root prompt on the initramfs" of Securing Debian Manual: 18eda14cbcSMatt Macy# https://www.debian.org/doc/manuals/securing-debian-howto/ch4.en.html 19eda14cbcSMatt Macyshell() { 20c40487d4SMatt Macy if command -v panic > /dev/null 2>&1; then 21c40487d4SMatt Macy panic 22eda14cbcSMatt Macy else 23eda14cbcSMatt Macy /bin/sh 24eda14cbcSMatt Macy fi 25eda14cbcSMatt Macy} 26eda14cbcSMatt Macy 27eda14cbcSMatt Macy# This runs any scripts that should run before we start importing 28eda14cbcSMatt Macy# pools and mounting any filesystems. 29eda14cbcSMatt Macypre_mountroot() 30eda14cbcSMatt Macy{ 31c40487d4SMatt Macy if command -v run_scripts > /dev/null 2>&1 32c40487d4SMatt Macy then 33c40487d4SMatt Macy if [ -f "/scripts/local-top" ] || [ -d "/scripts/local-top" ] 34eda14cbcSMatt Macy then 35eda14cbcSMatt Macy [ "$quiet" != "y" ] && \ 36eda14cbcSMatt Macy zfs_log_begin_msg "Running /scripts/local-top" 37eda14cbcSMatt Macy run_scripts /scripts/local-top 38eda14cbcSMatt Macy [ "$quiet" != "y" ] && zfs_log_end_msg 39eda14cbcSMatt Macy fi 40eda14cbcSMatt Macy 41c40487d4SMatt Macy if [ -f "/scripts/local-premount" ] || [ -d "/scripts/local-premount" ] 42eda14cbcSMatt Macy then 43eda14cbcSMatt Macy [ "$quiet" != "y" ] && \ 44eda14cbcSMatt Macy zfs_log_begin_msg "Running /scripts/local-premount" 45eda14cbcSMatt Macy run_scripts /scripts/local-premount 46eda14cbcSMatt Macy [ "$quiet" != "y" ] && zfs_log_end_msg 47eda14cbcSMatt Macy fi 48c40487d4SMatt Macy fi 49eda14cbcSMatt Macy} 50eda14cbcSMatt Macy 51eda14cbcSMatt Macy# If plymouth is available, hide the splash image. 52eda14cbcSMatt Macydisable_plymouth() 53eda14cbcSMatt Macy{ 54eda14cbcSMatt Macy if [ -x /bin/plymouth ] && /bin/plymouth --ping 55eda14cbcSMatt Macy then 56eda14cbcSMatt Macy /bin/plymouth hide-splash >/dev/null 2>&1 57eda14cbcSMatt Macy fi 58eda14cbcSMatt Macy} 59eda14cbcSMatt Macy 60eda14cbcSMatt Macy# Get a ZFS filesystem property value. 61eda14cbcSMatt Macyget_fs_value() 62eda14cbcSMatt Macy{ 63c40487d4SMatt Macy fs="$1" 64c40487d4SMatt Macy value=$2 65eda14cbcSMatt Macy 66c40487d4SMatt Macy "${ZFS}" get -H -ovalue "$value" "$fs" 2> /dev/null 67eda14cbcSMatt Macy} 68eda14cbcSMatt Macy 69eda14cbcSMatt Macy# Find the 'bootfs' property on pool $1. 70eda14cbcSMatt Macy# If the property does not contain '/', then ignore this 71eda14cbcSMatt Macy# pool by exporting it again. 72eda14cbcSMatt Macyfind_rootfs() 73eda14cbcSMatt Macy{ 74c40487d4SMatt Macy pool="$1" 75eda14cbcSMatt Macy 76eda14cbcSMatt Macy # If 'POOL_IMPORTED' isn't set, no pool imported and therefore 77eda14cbcSMatt Macy # we won't be able to find a root fs. 78eda14cbcSMatt Macy [ -z "${POOL_IMPORTED}" ] && return 1 79eda14cbcSMatt Macy 80eda14cbcSMatt Macy # If it's already specified, just keep it mounted and exit 81eda14cbcSMatt Macy # User (kernel command line) must be correct. 82dae17134SMartin Matuska if [ -n "${ZFS_BOOTFS}" ] && [ "${ZFS_BOOTFS}" != "zfs:AUTO" ]; then 83dae17134SMartin Matuska return 0 84dae17134SMartin Matuska fi 85eda14cbcSMatt Macy 86eda14cbcSMatt Macy # Not set, try to find it in the 'bootfs' property of the pool. 87eda14cbcSMatt Macy # NOTE: zpool does not support 'get -H -ovalue bootfs'... 88eda14cbcSMatt Macy ZFS_BOOTFS=$("${ZPOOL}" list -H -obootfs "$pool") 89eda14cbcSMatt Macy 90eda14cbcSMatt Macy # Make sure it's not '-' and that it starts with /. 91eda14cbcSMatt Macy if [ "${ZFS_BOOTFS}" != "-" ] && \ 92c40487d4SMatt Macy get_fs_value "${ZFS_BOOTFS}" mountpoint | grep -q '^/$' 93eda14cbcSMatt Macy then 94eda14cbcSMatt Macy # Keep it mounted 95eda14cbcSMatt Macy POOL_IMPORTED=1 96eda14cbcSMatt Macy return 0 97eda14cbcSMatt Macy fi 98eda14cbcSMatt Macy 99eda14cbcSMatt Macy # Not boot fs here, export it and later try again.. 100eda14cbcSMatt Macy "${ZPOOL}" export "$pool" 10116038816SMartin Matuska POOL_IMPORTED= 10216038816SMartin Matuska ZFS_BOOTFS= 103eda14cbcSMatt Macy return 1 104eda14cbcSMatt Macy} 105eda14cbcSMatt Macy 106eda14cbcSMatt Macy# Support function to get a list of all pools, separated with ';' 107eda14cbcSMatt Macyfind_pools() 108eda14cbcSMatt Macy{ 10916038816SMartin Matuska pools=$("$@" 2> /dev/null | \ 110dae17134SMartin Matuska sed -Ee '/pool:|^[a-zA-Z0-9]/!d' -e 's@.*: @@' | \ 11116038816SMartin Matuska tr '\n' ';') 112eda14cbcSMatt Macy 113eda14cbcSMatt Macy echo "${pools%%;}" # Return without the last ';'. 114eda14cbcSMatt Macy} 115eda14cbcSMatt Macy 116eda14cbcSMatt Macy# Get a list of all available pools 117eda14cbcSMatt Macyget_pools() 118eda14cbcSMatt Macy{ 119eda14cbcSMatt Macy if [ -n "${ZFS_POOL_IMPORT}" ]; then 120eda14cbcSMatt Macy echo "$ZFS_POOL_IMPORT" 121eda14cbcSMatt Macy return 0 122eda14cbcSMatt Macy fi 123eda14cbcSMatt Macy 124eda14cbcSMatt Macy # Get the base list of available pools. 125eda14cbcSMatt Macy available_pools=$(find_pools "$ZPOOL" import) 126eda14cbcSMatt Macy 127eda14cbcSMatt Macy # Just in case - seen it happen (that a pool isn't visible/found 128eda14cbcSMatt Macy # with a simple "zpool import" but only when using the "-d" 129eda14cbcSMatt Macy # option or setting ZPOOL_IMPORT_PATH). 130eda14cbcSMatt Macy if [ -d "/dev/disk/by-id" ] 131eda14cbcSMatt Macy then 132eda14cbcSMatt Macy npools=$(find_pools "$ZPOOL" import -d /dev/disk/by-id) 133eda14cbcSMatt Macy if [ -n "$npools" ] 134eda14cbcSMatt Macy then 135eda14cbcSMatt Macy # Because we have found extra pool(s) here, which wasn't 136eda14cbcSMatt Macy # found 'normally', we need to force USE_DISK_BY_ID to 137eda14cbcSMatt Macy # make sure we're able to actually import it/them later. 138eda14cbcSMatt Macy USE_DISK_BY_ID='yes' 139eda14cbcSMatt Macy 140eda14cbcSMatt Macy if [ -n "$available_pools" ] 141eda14cbcSMatt Macy then 142eda14cbcSMatt Macy # Filter out duplicates (pools found with the simple 143eda14cbcSMatt Macy # "zpool import" but which is also found with the 144eda14cbcSMatt Macy # "zpool import -d ..."). 145eda14cbcSMatt Macy npools=$(echo "$npools" | sed "s,$available_pools,,") 146eda14cbcSMatt Macy 147eda14cbcSMatt Macy # Add the list to the existing list of 148eda14cbcSMatt Macy # available pools 149eda14cbcSMatt Macy available_pools="$available_pools;$npools" 150eda14cbcSMatt Macy else 151eda14cbcSMatt Macy available_pools="$npools" 152eda14cbcSMatt Macy fi 153eda14cbcSMatt Macy fi 154eda14cbcSMatt Macy fi 155eda14cbcSMatt Macy 156eda14cbcSMatt Macy # Filter out any exceptions... 157eda14cbcSMatt Macy if [ -n "$ZFS_POOL_EXCEPTIONS" ] 158eda14cbcSMatt Macy then 159c40487d4SMatt Macy found="" 160c40487d4SMatt Macy apools="" 161eda14cbcSMatt Macy OLD_IFS="$IFS" ; IFS=";" 162eda14cbcSMatt Macy 163eda14cbcSMatt Macy for pool in $available_pools 164eda14cbcSMatt Macy do 165eda14cbcSMatt Macy for exception in $ZFS_POOL_EXCEPTIONS 166eda14cbcSMatt Macy do 167eda14cbcSMatt Macy [ "$pool" = "$exception" ] && continue 2 168eda14cbcSMatt Macy found="$pool" 169eda14cbcSMatt Macy done 170eda14cbcSMatt Macy 171eda14cbcSMatt Macy if [ -n "$found" ] 172eda14cbcSMatt Macy then 173eda14cbcSMatt Macy if [ -n "$apools" ] 174eda14cbcSMatt Macy then 175eda14cbcSMatt Macy apools="$apools;$pool" 176eda14cbcSMatt Macy else 177eda14cbcSMatt Macy apools="$pool" 178eda14cbcSMatt Macy fi 179eda14cbcSMatt Macy fi 180eda14cbcSMatt Macy done 181eda14cbcSMatt Macy 182eda14cbcSMatt Macy IFS="$OLD_IFS" 183eda14cbcSMatt Macy available_pools="$apools" 184eda14cbcSMatt Macy fi 185eda14cbcSMatt Macy 186eda14cbcSMatt Macy # Return list of available pools. 187eda14cbcSMatt Macy echo "$available_pools" 188eda14cbcSMatt Macy} 189eda14cbcSMatt Macy 190eda14cbcSMatt Macy# Import given pool $1 191eda14cbcSMatt Macyimport_pool() 192eda14cbcSMatt Macy{ 193c40487d4SMatt Macy pool="$1" 194eda14cbcSMatt Macy 195eda14cbcSMatt Macy # Verify that the pool isn't already imported 196eda14cbcSMatt Macy # Make as sure as we can to not require '-f' to import. 1972a58b312SMartin Matuska "${ZPOOL}" get -H -o value name,guid 2>/dev/null | grep -Fxq "$pool" && return 0 198eda14cbcSMatt Macy 199eda14cbcSMatt Macy # For backwards compatibility, make sure that ZPOOL_IMPORT_PATH is set 200eda14cbcSMatt Macy # to something we can use later with the real import(s). We want to 201eda14cbcSMatt Macy # make sure we find all by* dirs, BUT by-vdev should be first (if it 202eda14cbcSMatt Macy # exists). 203c40487d4SMatt Macy if [ -n "$USE_DISK_BY_ID" ] && [ -z "$ZPOOL_IMPORT_PATH" ] 204eda14cbcSMatt Macy then 20516038816SMartin Matuska dirs="$(for dir in /dev/disk/by-* 206eda14cbcSMatt Macy do 207eda14cbcSMatt Macy # Ignore by-vdev here - we want it first! 208eda14cbcSMatt Macy echo "$dir" | grep -q /by-vdev && continue 209eda14cbcSMatt Macy [ ! -d "$dir" ] && continue 210eda14cbcSMatt Macy 211c40487d4SMatt Macy printf "%s" "$dir:" 212eda14cbcSMatt Macy done | sed 's,:$,,g')" 213eda14cbcSMatt Macy 214eda14cbcSMatt Macy if [ -d "/dev/disk/by-vdev" ] 215eda14cbcSMatt Macy then 216eda14cbcSMatt Macy # Add by-vdev at the beginning. 217eda14cbcSMatt Macy ZPOOL_IMPORT_PATH="/dev/disk/by-vdev:" 218eda14cbcSMatt Macy fi 219eda14cbcSMatt Macy 220eda14cbcSMatt Macy # ... and /dev at the very end, just for good measure. 221eda14cbcSMatt Macy ZPOOL_IMPORT_PATH="$ZPOOL_IMPORT_PATH$dirs:/dev" 222eda14cbcSMatt Macy fi 223eda14cbcSMatt Macy 224eda14cbcSMatt Macy # Needs to be exported for "zpool" to catch it. 225eda14cbcSMatt Macy [ -n "$ZPOOL_IMPORT_PATH" ] && export ZPOOL_IMPORT_PATH 226eda14cbcSMatt Macy 227eda14cbcSMatt Macy 228eda14cbcSMatt Macy [ "$quiet" != "y" ] && zfs_log_begin_msg \ 229eda14cbcSMatt Macy "Importing pool '${pool}' using defaults" 230eda14cbcSMatt Macy 231eda14cbcSMatt Macy ZFS_CMD="${ZPOOL} import -N ${ZPOOL_FORCE} ${ZPOOL_IMPORT_OPTS}" 232eda14cbcSMatt Macy ZFS_STDERR="$($ZFS_CMD "$pool" 2>&1)" 233eda14cbcSMatt Macy ZFS_ERROR="$?" 234eda14cbcSMatt Macy if [ "${ZFS_ERROR}" != 0 ] 235eda14cbcSMatt Macy then 236eda14cbcSMatt Macy [ "$quiet" != "y" ] && zfs_log_failure_msg "${ZFS_ERROR}" 237eda14cbcSMatt Macy 238eda14cbcSMatt Macy if [ -f "${ZPOOL_CACHE}" ] 239eda14cbcSMatt Macy then 240eda14cbcSMatt Macy [ "$quiet" != "y" ] && zfs_log_begin_msg \ 241eda14cbcSMatt Macy "Importing pool '${pool}' using cachefile." 242eda14cbcSMatt Macy 243eda14cbcSMatt Macy ZFS_CMD="${ZPOOL} import -c ${ZPOOL_CACHE} -N ${ZPOOL_FORCE} ${ZPOOL_IMPORT_OPTS}" 244eda14cbcSMatt Macy ZFS_STDERR="$($ZFS_CMD "$pool" 2>&1)" 245eda14cbcSMatt Macy ZFS_ERROR="$?" 246eda14cbcSMatt Macy fi 247eda14cbcSMatt Macy 248eda14cbcSMatt Macy if [ "${ZFS_ERROR}" != 0 ] 249eda14cbcSMatt Macy then 250eda14cbcSMatt Macy [ "$quiet" != "y" ] && zfs_log_failure_msg "${ZFS_ERROR}" 251eda14cbcSMatt Macy 252eda14cbcSMatt Macy disable_plymouth 253eda14cbcSMatt Macy echo "" 254eda14cbcSMatt Macy echo "Command: ${ZFS_CMD} '$pool'" 255eda14cbcSMatt Macy echo "Message: $ZFS_STDERR" 256eda14cbcSMatt Macy echo "Error: $ZFS_ERROR" 257eda14cbcSMatt Macy echo "" 258eda14cbcSMatt Macy echo "Failed to import pool '$pool'." 259eda14cbcSMatt Macy echo "Manually import the pool and exit." 260eda14cbcSMatt Macy shell 261eda14cbcSMatt Macy fi 262eda14cbcSMatt Macy fi 263eda14cbcSMatt Macy 264eda14cbcSMatt Macy [ "$quiet" != "y" ] && zfs_log_end_msg 265eda14cbcSMatt Macy 266eda14cbcSMatt Macy POOL_IMPORTED=1 267eda14cbcSMatt Macy return 0 268eda14cbcSMatt Macy} 269eda14cbcSMatt Macy 270eda14cbcSMatt Macy# Load ZFS modules 271eda14cbcSMatt Macy# Loading a module in a initrd require a slightly different approach, 272eda14cbcSMatt Macy# with more logging etc. 273eda14cbcSMatt Macyload_module_initrd() 274eda14cbcSMatt Macy{ 275c9539b89SMartin Matuska ZFS_INITRD_PRE_MOUNTROOT_SLEEP=${ROOTDELAY:-0} 27616038816SMartin Matuska 277c9539b89SMartin Matuska if [ "$ZFS_INITRD_PRE_MOUNTROOT_SLEEP" -gt 0 ]; then 278c9539b89SMartin Matuska [ "$quiet" != "y" ] && zfs_log_begin_msg "Delaying for up to '${ZFS_INITRD_PRE_MOUNTROOT_SLEEP}' seconds." 279eda14cbcSMatt Macy fi 280c9539b89SMartin Matuska 281c9539b89SMartin Matuska START=$(/bin/date -u +%s) 282c9539b89SMartin Matuska END=$((START+ZFS_INITRD_PRE_MOUNTROOT_SLEEP)) 283c9539b89SMartin Matuska while true; do 284eda14cbcSMatt Macy 285eda14cbcSMatt Macy # Wait for all of the /dev/{hd,sd}[a-z] device nodes to appear. 286c40487d4SMatt Macy if command -v wait_for_udev > /dev/null 2>&1 ; then 287eda14cbcSMatt Macy wait_for_udev 10 288c40487d4SMatt Macy elif command -v wait_for_dev > /dev/null 2>&1 ; then 289eda14cbcSMatt Macy wait_for_dev 290eda14cbcSMatt Macy fi 291eda14cbcSMatt Macy 292c9539b89SMartin Matuska # 293c9539b89SMartin Matuska # zpool import refuse to import without a valid 294c9539b89SMartin Matuska # /proc/self/mounts 295c9539b89SMartin Matuska # 296eda14cbcSMatt Macy [ ! -f /proc/self/mounts ] && mount proc /proc 297eda14cbcSMatt Macy 298eda14cbcSMatt Macy # Load the module 299c9539b89SMartin Matuska if load_module "zfs"; then 300c9539b89SMartin Matuska ret=0 301c9539b89SMartin Matuska break 302c9539b89SMartin Matuska else 303c9539b89SMartin Matuska ret=1 304c9539b89SMartin Matuska fi 305c9539b89SMartin Matuska 306c9539b89SMartin Matuska [ "$(/bin/date -u +%s)" -gt "$END" ] && break 307c9539b89SMartin Matuska sleep 1 308c9539b89SMartin Matuska 309c9539b89SMartin Matuska done 310c9539b89SMartin Matuska if [ "$ZFS_INITRD_PRE_MOUNTROOT_SLEEP" -gt 0 ]; then 311c9539b89SMartin Matuska [ "$quiet" != "y" ] && zfs_log_end_msg 312c9539b89SMartin Matuska fi 313c9539b89SMartin Matuska 314c9539b89SMartin Matuska [ "$ret" -ne 0 ] && return 1 315eda14cbcSMatt Macy 316c40487d4SMatt Macy if [ "$ZFS_INITRD_POST_MODPROBE_SLEEP" -gt 0 ] 2>/dev/null 317eda14cbcSMatt Macy then 318eda14cbcSMatt Macy if [ "$quiet" != "y" ]; then 319eda14cbcSMatt Macy zfs_log_begin_msg "Sleeping for" \ 320eda14cbcSMatt Macy "$ZFS_INITRD_POST_MODPROBE_SLEEP seconds..." 321eda14cbcSMatt Macy fi 322eda14cbcSMatt Macy sleep "$ZFS_INITRD_POST_MODPROBE_SLEEP" 323eda14cbcSMatt Macy [ "$quiet" != "y" ] && zfs_log_end_msg 324eda14cbcSMatt Macy fi 325eda14cbcSMatt Macy 326eda14cbcSMatt Macy return 0 327eda14cbcSMatt Macy} 328eda14cbcSMatt Macy 329eda14cbcSMatt Macy# Mount a given filesystem 330eda14cbcSMatt Macymount_fs() 331eda14cbcSMatt Macy{ 332c40487d4SMatt Macy fs="$1" 333eda14cbcSMatt Macy 334eda14cbcSMatt Macy # Check that the filesystem exists 335c40487d4SMatt Macy "${ZFS}" list -oname -tfilesystem -H "${fs}" > /dev/null 2>&1 || return 1 336eda14cbcSMatt Macy 337eda14cbcSMatt Macy # Skip filesystems with canmount=off. The root fs should not have 338eda14cbcSMatt Macy # canmount=off, but ignore it for backwards compatibility just in case. 339eda14cbcSMatt Macy if [ "$fs" != "${ZFS_BOOTFS}" ] 340eda14cbcSMatt Macy then 341eda14cbcSMatt Macy canmount=$(get_fs_value "$fs" canmount) 342eda14cbcSMatt Macy [ "$canmount" = "off" ] && return 0 343eda14cbcSMatt Macy fi 344eda14cbcSMatt Macy 345eda14cbcSMatt Macy # Need the _original_ datasets mountpoint! 346eda14cbcSMatt Macy mountpoint=$(get_fs_value "$fs" mountpoint) 347716fd348SMartin Matuska ZFS_CMD="mount.zfs -o zfsutil" 348c40487d4SMatt Macy if [ "$mountpoint" = "legacy" ] || [ "$mountpoint" = "none" ]; then 349eda14cbcSMatt Macy # Can't use the mountpoint property. Might be one of our 350eda14cbcSMatt Macy # clones. Check the 'org.zol:mountpoint' property set in 351eda14cbcSMatt Macy # clone_snap() if that's usable. 35215f0b8c3SMartin Matuska mountpoint1=$(get_fs_value "$fs" org.zol:mountpoint) 35315f0b8c3SMartin Matuska if [ "$mountpoint1" = "legacy" ] || 35415f0b8c3SMartin Matuska [ "$mountpoint1" = "none" ] || 35515f0b8c3SMartin Matuska [ "$mountpoint1" = "-" ] 356eda14cbcSMatt Macy then 357eda14cbcSMatt Macy if [ "$fs" != "${ZFS_BOOTFS}" ]; then 358eda14cbcSMatt Macy # We don't have a proper mountpoint and this 359eda14cbcSMatt Macy # isn't the root fs. 360eda14cbcSMatt Macy return 0 36115f0b8c3SMartin Matuska fi 3622a58b312SMartin Matuska # Don't use mount.zfs -o zfsutils for legacy mountpoint 363c9539b89SMartin Matuska if [ "$mountpoint" = "legacy" ]; then 364c9539b89SMartin Matuska ZFS_CMD="mount.zfs" 365c9539b89SMartin Matuska fi 3662a58b312SMartin Matuska # Last hail-mary: Hope 'rootmnt' is set! 3672a58b312SMartin Matuska mountpoint="" 36815f0b8c3SMartin Matuska else 36915f0b8c3SMartin Matuska mountpoint="$mountpoint1" 370eda14cbcSMatt Macy fi 371eda14cbcSMatt Macy fi 372eda14cbcSMatt Macy 373eda14cbcSMatt Macy # Possibly decrypt a filesystem using native encryption. 374eda14cbcSMatt Macy decrypt_fs "$fs" 375eda14cbcSMatt Macy 376eda14cbcSMatt Macy [ "$quiet" != "y" ] && \ 377eda14cbcSMatt Macy zfs_log_begin_msg "Mounting '${fs}' on '${rootmnt}/${mountpoint}'" 378eda14cbcSMatt Macy [ -n "${ZFS_DEBUG}" ] && \ 379eda14cbcSMatt Macy zfs_log_begin_msg "CMD: '$ZFS_CMD ${fs} ${rootmnt}/${mountpoint}'" 380eda14cbcSMatt Macy 381eda14cbcSMatt Macy ZFS_STDERR=$(${ZFS_CMD} "${fs}" "${rootmnt}/${mountpoint}" 2>&1) 382eda14cbcSMatt Macy ZFS_ERROR=$? 383eda14cbcSMatt Macy if [ "${ZFS_ERROR}" != 0 ] 384eda14cbcSMatt Macy then 385eda14cbcSMatt Macy [ "$quiet" != "y" ] && zfs_log_failure_msg "${ZFS_ERROR}" 386eda14cbcSMatt Macy 387eda14cbcSMatt Macy disable_plymouth 388eda14cbcSMatt Macy echo "" 389eda14cbcSMatt Macy echo "Command: ${ZFS_CMD} ${fs} ${rootmnt}/${mountpoint}" 390eda14cbcSMatt Macy echo "Message: $ZFS_STDERR" 391eda14cbcSMatt Macy echo "Error: $ZFS_ERROR" 392eda14cbcSMatt Macy echo "" 393eda14cbcSMatt Macy echo "Failed to mount ${fs} on ${rootmnt}/${mountpoint}." 394eda14cbcSMatt Macy echo "Manually mount the filesystem and exit." 395eda14cbcSMatt Macy shell 396eda14cbcSMatt Macy else 397eda14cbcSMatt Macy [ "$quiet" != "y" ] && zfs_log_end_msg 398eda14cbcSMatt Macy fi 399eda14cbcSMatt Macy 400eda14cbcSMatt Macy return 0 401eda14cbcSMatt Macy} 402eda14cbcSMatt Macy 403eda14cbcSMatt Macy# Unlock a ZFS native encrypted filesystem. 404eda14cbcSMatt Macydecrypt_fs() 405eda14cbcSMatt Macy{ 406c40487d4SMatt Macy fs="$1" 407eda14cbcSMatt Macy 408eda14cbcSMatt Macy # If pool encryption is active and the zfs command understands '-o encryption' 40916038816SMartin Matuska if [ "$(zpool list -H -o feature@encryption "${fs%%/*}")" = 'active' ]; then 410eda14cbcSMatt Macy 411eda14cbcSMatt Macy # Determine dataset that holds key for root dataset 412eda14cbcSMatt Macy ENCRYPTIONROOT="$(get_fs_value "${fs}" encryptionroot)" 413eda14cbcSMatt Macy KEYLOCATION="$(get_fs_value "${ENCRYPTIONROOT}" keylocation)" 414eda14cbcSMatt Macy 415eda14cbcSMatt Macy echo "${ENCRYPTIONROOT}" > /run/zfs_fs_name 416eda14cbcSMatt Macy 417eda14cbcSMatt Macy # If root dataset is encrypted... 418eda14cbcSMatt Macy if ! [ "${ENCRYPTIONROOT}" = "-" ]; then 419eda14cbcSMatt Macy KEYSTATUS="$(get_fs_value "${ENCRYPTIONROOT}" keystatus)" 420eda14cbcSMatt Macy # Continue only if the key needs to be loaded 421eda14cbcSMatt Macy [ "$KEYSTATUS" = "unavailable" ] || return 0 422eda14cbcSMatt Macy 423*d411c1d6SMartin Matuska # Try extensions first 424*d411c1d6SMartin Matuska for f in "/etc/zfs/initramfs-tools-load-key" "/etc/zfs/initramfs-tools-load-key.d/"*; do 425*d411c1d6SMartin Matuska [ -r "$f" ] || continue 426*d411c1d6SMartin Matuska (. "$f") && { 427*d411c1d6SMartin Matuska # Successful return and actually-loaded key: we're done 428*d411c1d6SMartin Matuska KEYSTATUS="$(get_fs_value "${ENCRYPTIONROOT}" keystatus)" 429*d411c1d6SMartin Matuska [ "$KEYSTATUS" = "unavailable" ] || return 0 430*d411c1d6SMartin Matuska } 431*d411c1d6SMartin Matuska done 432*d411c1d6SMartin Matuska 43316038816SMartin Matuska # Do not prompt if key is stored noninteractively, 434eda14cbcSMatt Macy if ! [ "${KEYLOCATION}" = "prompt" ]; then 435eda14cbcSMatt Macy $ZFS load-key "${ENCRYPTIONROOT}" 436eda14cbcSMatt Macy 437eda14cbcSMatt Macy # Prompt with plymouth, if active 43816038816SMartin Matuska elif /bin/plymouth --ping 2>/dev/null; then 439eda14cbcSMatt Macy echo "plymouth" > /run/zfs_console_askpwd_cmd 44016038816SMartin Matuska for _ in 1 2 3; do 441eda14cbcSMatt Macy plymouth ask-for-password --prompt "Encrypted ZFS password for ${ENCRYPTIONROOT}" | \ 442eda14cbcSMatt Macy $ZFS load-key "${ENCRYPTIONROOT}" && break 443eda14cbcSMatt Macy done 444eda14cbcSMatt Macy 445eda14cbcSMatt Macy # Prompt with systemd, if active 446eda14cbcSMatt Macy elif [ -e /run/systemd/system ]; then 447eda14cbcSMatt Macy echo "systemd-ask-password" > /run/zfs_console_askpwd_cmd 44816038816SMartin Matuska for _ in 1 2 3; do 449e92ffd9bSMartin Matuska systemd-ask-password --no-tty "Encrypted ZFS password for ${ENCRYPTIONROOT}" | \ 450eda14cbcSMatt Macy $ZFS load-key "${ENCRYPTIONROOT}" && break 451eda14cbcSMatt Macy done 452eda14cbcSMatt Macy 453eda14cbcSMatt Macy # Prompt with ZFS tty, otherwise 454eda14cbcSMatt Macy else 455eda14cbcSMatt Macy # Temporarily setting "printk" to "7" allows the prompt to appear even when the "quiet" kernel option has been used 456eda14cbcSMatt Macy echo "load-key" > /run/zfs_console_askpwd_cmd 457dae17134SMartin Matuska read -r storeprintk _ < /proc/sys/kernel/printk 458eda14cbcSMatt Macy echo 7 > /proc/sys/kernel/printk 459eda14cbcSMatt Macy $ZFS load-key "${ENCRYPTIONROOT}" 460eda14cbcSMatt Macy echo "$storeprintk" > /proc/sys/kernel/printk 461eda14cbcSMatt Macy fi 462eda14cbcSMatt Macy fi 463eda14cbcSMatt Macy fi 464eda14cbcSMatt Macy 465eda14cbcSMatt Macy return 0 466eda14cbcSMatt Macy} 467eda14cbcSMatt Macy 468eda14cbcSMatt Macy# Destroy a given filesystem. 469eda14cbcSMatt Macydestroy_fs() 470eda14cbcSMatt Macy{ 471c40487d4SMatt Macy fs="$1" 472eda14cbcSMatt Macy 473eda14cbcSMatt Macy [ "$quiet" != "y" ] && \ 474eda14cbcSMatt Macy zfs_log_begin_msg "Destroying '$fs'" 475eda14cbcSMatt Macy 476eda14cbcSMatt Macy ZFS_CMD="${ZFS} destroy $fs" 477eda14cbcSMatt Macy ZFS_STDERR="$(${ZFS_CMD} 2>&1)" 478eda14cbcSMatt Macy ZFS_ERROR="$?" 479eda14cbcSMatt Macy if [ "${ZFS_ERROR}" != 0 ] 480eda14cbcSMatt Macy then 481eda14cbcSMatt Macy [ "$quiet" != "y" ] && zfs_log_failure_msg "${ZFS_ERROR}" 482eda14cbcSMatt Macy 483eda14cbcSMatt Macy disable_plymouth 484eda14cbcSMatt Macy echo "" 485eda14cbcSMatt Macy echo "Command: $ZFS_CMD" 486eda14cbcSMatt Macy echo "Message: $ZFS_STDERR" 487eda14cbcSMatt Macy echo "Error: $ZFS_ERROR" 488eda14cbcSMatt Macy echo "" 489eda14cbcSMatt Macy echo "Failed to destroy '$fs'. Please make sure that '$fs' is not available." 490eda14cbcSMatt Macy echo "Hint: Try: zfs destroy -Rfn $fs" 491eda14cbcSMatt Macy echo "If this dryrun looks good, then remove the 'n' from '-Rfn' and try again." 492eda14cbcSMatt Macy shell 493eda14cbcSMatt Macy else 494eda14cbcSMatt Macy [ "$quiet" != "y" ] && zfs_log_end_msg 495eda14cbcSMatt Macy fi 496eda14cbcSMatt Macy 497eda14cbcSMatt Macy return 0 498eda14cbcSMatt Macy} 499eda14cbcSMatt Macy 500eda14cbcSMatt Macy# Clone snapshot $1 to destination filesystem $2 501eda14cbcSMatt Macy# Set 'canmount=noauto' and 'mountpoint=none' so that we get to keep 502eda14cbcSMatt Macy# manual control over it's mounting (i.e., make sure it's not automatically 503eda14cbcSMatt Macy# mounted with a 'zfs mount -a' in the init/systemd scripts). 504eda14cbcSMatt Macyclone_snap() 505eda14cbcSMatt Macy{ 506c40487d4SMatt Macy snap="$1" 507c40487d4SMatt Macy destfs="$2" 508c40487d4SMatt Macy mountpoint="$3" 509eda14cbcSMatt Macy 510eda14cbcSMatt Macy [ "$quiet" != "y" ] && zfs_log_begin_msg "Cloning '$snap' to '$destfs'" 511eda14cbcSMatt Macy 512eda14cbcSMatt Macy # Clone the snapshot into a dataset we can boot from 513eda14cbcSMatt Macy # + We don't want this filesystem to be automatically mounted, we 514eda14cbcSMatt Macy # want control over this here and nowhere else. 515eda14cbcSMatt Macy # + We don't need any mountpoint set for the same reason. 516eda14cbcSMatt Macy # We use the 'org.zol:mountpoint' property to remember the mountpoint. 517eda14cbcSMatt Macy ZFS_CMD="${ZFS} clone -o canmount=noauto -o mountpoint=none" 518eda14cbcSMatt Macy ZFS_CMD="${ZFS_CMD} -o org.zol:mountpoint=${mountpoint}" 519eda14cbcSMatt Macy ZFS_CMD="${ZFS_CMD} $snap $destfs" 520eda14cbcSMatt Macy ZFS_STDERR="$(${ZFS_CMD} 2>&1)" 521eda14cbcSMatt Macy ZFS_ERROR="$?" 522eda14cbcSMatt Macy if [ "${ZFS_ERROR}" != 0 ] 523eda14cbcSMatt Macy then 524eda14cbcSMatt Macy [ "$quiet" != "y" ] && zfs_log_failure_msg "${ZFS_ERROR}" 525eda14cbcSMatt Macy 526eda14cbcSMatt Macy disable_plymouth 527eda14cbcSMatt Macy echo "" 528eda14cbcSMatt Macy echo "Command: $ZFS_CMD" 529eda14cbcSMatt Macy echo "Message: $ZFS_STDERR" 530eda14cbcSMatt Macy echo "Error: $ZFS_ERROR" 531eda14cbcSMatt Macy echo "" 532eda14cbcSMatt Macy echo "Failed to clone snapshot." 53315f0b8c3SMartin Matuska echo "Make sure that any problems are corrected and then make sure" 534eda14cbcSMatt Macy echo "that the dataset '$destfs' exists and is bootable." 535eda14cbcSMatt Macy shell 536eda14cbcSMatt Macy else 537eda14cbcSMatt Macy [ "$quiet" != "y" ] && zfs_log_end_msg 538eda14cbcSMatt Macy fi 539eda14cbcSMatt Macy 540eda14cbcSMatt Macy return 0 541eda14cbcSMatt Macy} 542eda14cbcSMatt Macy 543eda14cbcSMatt Macy# Rollback a given snapshot. 544eda14cbcSMatt Macyrollback_snap() 545eda14cbcSMatt Macy{ 546c40487d4SMatt Macy snap="$1" 547eda14cbcSMatt Macy 548eda14cbcSMatt Macy [ "$quiet" != "y" ] && zfs_log_begin_msg "Rollback $snap" 549eda14cbcSMatt Macy 550eda14cbcSMatt Macy ZFS_CMD="${ZFS} rollback -Rf $snap" 551eda14cbcSMatt Macy ZFS_STDERR="$(${ZFS_CMD} 2>&1)" 552eda14cbcSMatt Macy ZFS_ERROR="$?" 553eda14cbcSMatt Macy if [ "${ZFS_ERROR}" != 0 ] 554eda14cbcSMatt Macy then 555eda14cbcSMatt Macy [ "$quiet" != "y" ] && zfs_log_failure_msg "${ZFS_ERROR}" 556eda14cbcSMatt Macy 557eda14cbcSMatt Macy disable_plymouth 558eda14cbcSMatt Macy echo "" 559eda14cbcSMatt Macy echo "Command: $ZFS_CMD" 560eda14cbcSMatt Macy echo "Message: $ZFS_STDERR" 561eda14cbcSMatt Macy echo "Error: $ZFS_ERROR" 562eda14cbcSMatt Macy echo "" 563eda14cbcSMatt Macy echo "Failed to rollback snapshot." 564eda14cbcSMatt Macy shell 565eda14cbcSMatt Macy else 566eda14cbcSMatt Macy [ "$quiet" != "y" ] && zfs_log_end_msg 567eda14cbcSMatt Macy fi 568eda14cbcSMatt Macy 569eda14cbcSMatt Macy return 0 570eda14cbcSMatt Macy} 571eda14cbcSMatt Macy 572eda14cbcSMatt Macy# Get a list of snapshots, give them as a numbered list 573eda14cbcSMatt Macy# to the user to choose from. 574eda14cbcSMatt Macyask_user_snap() 575eda14cbcSMatt Macy{ 576c40487d4SMatt Macy fs="$1" 577eda14cbcSMatt Macy 578eda14cbcSMatt Macy # We need to temporarily disable debugging. Set 'debug' so we 579eda14cbcSMatt Macy # remember to enabled it again. 580eda14cbcSMatt Macy if [ -n "${ZFS_DEBUG}" ]; then 581eda14cbcSMatt Macy unset ZFS_DEBUG 582eda14cbcSMatt Macy set +x 583eda14cbcSMatt Macy debug=1 584eda14cbcSMatt Macy fi 585eda14cbcSMatt Macy 586eda14cbcSMatt Macy # Because we need the resulting snapshot, which is sent on 587eda14cbcSMatt Macy # stdout to the caller, we use stderr for our questions. 588eda14cbcSMatt Macy echo "What snapshot do you want to boot from?" > /dev/stderr 58916038816SMartin Matuska # shellcheck disable=SC2046 59016038816SMartin Matuska IFS=" 59116038816SMartin Matuska" set -- $("${ZFS}" list -H -oname -tsnapshot -r "${fs}") 592eda14cbcSMatt Macy 59316038816SMartin Matuska i=1 59416038816SMartin Matuska for snap in "$@"; do 59516038816SMartin Matuska echo " $i: $snap" 59616038816SMartin Matuska i=$((i + 1)) 59716038816SMartin Matuska done > /dev/stderr 59816038816SMartin Matuska 59916038816SMartin Matuska # expr instead of test here because [ a -lt 0 ] errors out, 60016038816SMartin Matuska # but expr falls back to lexicographical, which works out right 60116038816SMartin Matuska snapnr=0 60216038816SMartin Matuska while expr "$snapnr" "<" 1 > /dev/null || 60316038816SMartin Matuska expr "$snapnr" ">" "$#" > /dev/null 60416038816SMartin Matuska do 60516038816SMartin Matuska printf "%s" "Snap nr [1-$#]? " > /dev/stderr 606c40487d4SMatt Macy read -r snapnr 60716038816SMartin Matuska done 608eda14cbcSMatt Macy 609eda14cbcSMatt Macy # Re-enable debugging. 610eda14cbcSMatt Macy if [ -n "${debug}" ]; then 611eda14cbcSMatt Macy ZFS_DEBUG=1 612eda14cbcSMatt Macy set -x 613eda14cbcSMatt Macy fi 614eda14cbcSMatt Macy 61516038816SMartin Matuska eval echo '$'"$snapnr" 616eda14cbcSMatt Macy} 617eda14cbcSMatt Macy 618eda14cbcSMatt Macysetup_snapshot_booting() 619eda14cbcSMatt Macy{ 620c40487d4SMatt Macy snap="$1" 621c40487d4SMatt Macy retval=0 622eda14cbcSMatt Macy 623c40487d4SMatt Macy # Make sure that the snapshot specified actually exists. 624681ce946SMartin Matuska if [ -z "$(get_fs_value "${snap}" type)" ] 625eda14cbcSMatt Macy then 626eda14cbcSMatt Macy # Snapshot does not exist (...@<null> ?) 627eda14cbcSMatt Macy # ask the user for a snapshot to use. 628eda14cbcSMatt Macy snap="$(ask_user_snap "${snap%%@*}")" 629eda14cbcSMatt Macy fi 630eda14cbcSMatt Macy 631eda14cbcSMatt Macy # Separate the full snapshot ('$snap') into it's filesystem and 632eda14cbcSMatt Macy # snapshot names. Would have been nice with a split() function.. 633eda14cbcSMatt Macy rootfs="${snap%%@*}" 634eda14cbcSMatt Macy snapname="${snap##*@}" 635eda14cbcSMatt Macy ZFS_BOOTFS="${rootfs}_${snapname}" 636eda14cbcSMatt Macy 637eda14cbcSMatt Macy if ! grep -qiE '(^|[^\\](\\\\)* )(rollback)=(on|yes|1)( |$)' /proc/cmdline 638eda14cbcSMatt Macy then 639eda14cbcSMatt Macy # If the destination dataset for the clone 640eda14cbcSMatt Macy # already exists, destroy it. Recursively 641681ce946SMartin Matuska if [ -n "$(get_fs_value "${rootfs}_${snapname}" type)" ] 642681ce946SMartin Matuska then 643eda14cbcSMatt Macy filesystems=$("${ZFS}" list -oname -tfilesystem -H \ 644eda14cbcSMatt Macy -r -Sname "${ZFS_BOOTFS}") 645eda14cbcSMatt Macy for fs in $filesystems; do 646eda14cbcSMatt Macy destroy_fs "${fs}" 647eda14cbcSMatt Macy done 648eda14cbcSMatt Macy fi 649eda14cbcSMatt Macy fi 650eda14cbcSMatt Macy 651eda14cbcSMatt Macy # Get all snapshots, recursively (might need to clone /usr, /var etc 652eda14cbcSMatt Macy # as well). 653eda14cbcSMatt Macy for s in $("${ZFS}" list -H -oname -tsnapshot -r "${rootfs}" | \ 654eda14cbcSMatt Macy grep "${snapname}") 655eda14cbcSMatt Macy do 656eda14cbcSMatt Macy if grep -qiE '(^|[^\\](\\\\)* )(rollback)=(on|yes|1)( |$)' /proc/cmdline 657eda14cbcSMatt Macy then 658eda14cbcSMatt Macy # Rollback snapshot 659eda14cbcSMatt Macy rollback_snap "$s" || retval=$((retval + 1)) 6606ba2210eSMartin Matuska ZFS_BOOTFS="${rootfs}" 661eda14cbcSMatt Macy else 662eda14cbcSMatt Macy # Setup a destination filesystem name. 663eda14cbcSMatt Macy # Ex: Called with 'rpool/ROOT/debian@snap2' 664eda14cbcSMatt Macy # rpool/ROOT/debian@snap2 => rpool/ROOT/debian_snap2 665eda14cbcSMatt Macy # rpool/ROOT/debian/boot@snap2 => rpool/ROOT/debian_snap2/boot 666eda14cbcSMatt Macy # rpool/ROOT/debian/usr@snap2 => rpool/ROOT/debian_snap2/usr 667eda14cbcSMatt Macy # rpool/ROOT/debian/var@snap2 => rpool/ROOT/debian_snap2/var 668bb2d13b6SMartin Matuska subfs="${s##"$rootfs"}" 669bb2d13b6SMartin Matuska subfs="${subfs%%@"$snapname"}" 670eda14cbcSMatt Macy 671eda14cbcSMatt Macy destfs="${rootfs}_${snapname}" # base fs. 672eda14cbcSMatt Macy [ -n "$subfs" ] && destfs="${destfs}$subfs" # + sub fs. 673eda14cbcSMatt Macy 674eda14cbcSMatt Macy # Get the mountpoint of the filesystem, to be used 675eda14cbcSMatt Macy # with clone_snap(). If legacy or none, then use 676eda14cbcSMatt Macy # the sub fs value. 677eda14cbcSMatt Macy mountpoint=$(get_fs_value "${s%%@*}" mountpoint) 678c40487d4SMatt Macy if [ "$mountpoint" = "legacy" ] || \ 679c40487d4SMatt Macy [ "$mountpoint" = "none" ] 680eda14cbcSMatt Macy then 681eda14cbcSMatt Macy if [ -n "${subfs}" ]; then 682eda14cbcSMatt Macy mountpoint="${subfs}" 683eda14cbcSMatt Macy else 684eda14cbcSMatt Macy mountpoint="/" 685eda14cbcSMatt Macy fi 686eda14cbcSMatt Macy fi 687eda14cbcSMatt Macy 688eda14cbcSMatt Macy # Clone the snapshot into its own 689eda14cbcSMatt Macy # filesystem 690eda14cbcSMatt Macy clone_snap "$s" "${destfs}" "${mountpoint}" || \ 691eda14cbcSMatt Macy retval=$((retval + 1)) 692eda14cbcSMatt Macy fi 693eda14cbcSMatt Macy done 694eda14cbcSMatt Macy 695eda14cbcSMatt Macy # If we haven't return yet, we have a problem... 696eda14cbcSMatt Macy return "${retval}" 697eda14cbcSMatt Macy} 698eda14cbcSMatt Macy 699eda14cbcSMatt Macy# ================================================================ 700eda14cbcSMatt Macy 701eda14cbcSMatt Macy# This is the main function. 702eda14cbcSMatt Macymountroot() 703eda14cbcSMatt Macy{ 704eda14cbcSMatt Macy # ---------------------------------------------------------------- 705eda14cbcSMatt Macy # I N I T I A L S E T U P 706eda14cbcSMatt Macy 707eda14cbcSMatt Macy # ------------ 708eda14cbcSMatt Macy # Run the pre-mount scripts from /scripts/local-top. 709eda14cbcSMatt Macy pre_mountroot 710eda14cbcSMatt Macy 711eda14cbcSMatt Macy # ------------ 712eda14cbcSMatt Macy # Source the default setup variables. 713eda14cbcSMatt Macy [ -r '/etc/default/zfs' ] && . /etc/default/zfs 714eda14cbcSMatt Macy 715eda14cbcSMatt Macy # ------------ 716eda14cbcSMatt Macy # Support debug option 717eda14cbcSMatt Macy if grep -qiE '(^|[^\\](\\\\)* )(zfs_debug|zfs\.debug|zfsdebug)=(on|yes|1)( |$)' /proc/cmdline 718eda14cbcSMatt Macy then 719eda14cbcSMatt Macy ZFS_DEBUG=1 720eda14cbcSMatt Macy mkdir /var/log 721eda14cbcSMatt Macy #exec 2> /var/log/boot.debug 722eda14cbcSMatt Macy set -x 723eda14cbcSMatt Macy fi 724eda14cbcSMatt Macy 725eda14cbcSMatt Macy # ------------ 726eda14cbcSMatt Macy # Load ZFS module etc. 727eda14cbcSMatt Macy if ! load_module_initrd; then 728eda14cbcSMatt Macy disable_plymouth 729eda14cbcSMatt Macy echo "" 730eda14cbcSMatt Macy echo "Failed to load ZFS modules." 731eda14cbcSMatt Macy echo "Manually load the modules and exit." 732eda14cbcSMatt Macy shell 733eda14cbcSMatt Macy fi 734eda14cbcSMatt Macy 735eda14cbcSMatt Macy # ------------ 736eda14cbcSMatt Macy # Look for the cache file (if any). 73716038816SMartin Matuska [ -f "${ZPOOL_CACHE}" ] || unset ZPOOL_CACHE 73816038816SMartin Matuska [ -s "${ZPOOL_CACHE}" ] || unset ZPOOL_CACHE 739eda14cbcSMatt Macy 740eda14cbcSMatt Macy # ------------ 741eda14cbcSMatt Macy # Compatibility: 'ROOT' is for Debian GNU/Linux (etc), 742eda14cbcSMatt Macy # 'root' is for Redhat/Fedora (etc), 743eda14cbcSMatt Macy # 'REAL_ROOT' is for Gentoo 744eda14cbcSMatt Macy if [ -z "$ROOT" ] 745eda14cbcSMatt Macy then 746eda14cbcSMatt Macy [ -n "$root" ] && ROOT=${root} 747eda14cbcSMatt Macy 748eda14cbcSMatt Macy [ -n "$REAL_ROOT" ] && ROOT=${REAL_ROOT} 749eda14cbcSMatt Macy fi 750eda14cbcSMatt Macy 751eda14cbcSMatt Macy # ------------ 752eda14cbcSMatt Macy # Where to mount the root fs in the initrd - set outside this script 753eda14cbcSMatt Macy # Compatibility: 'rootmnt' is for Debian GNU/Linux (etc), 754eda14cbcSMatt Macy # 'NEWROOT' is for RedHat/Fedora (etc), 755eda14cbcSMatt Macy # 'NEW_ROOT' is for Gentoo 756eda14cbcSMatt Macy if [ -z "$rootmnt" ] 757eda14cbcSMatt Macy then 758eda14cbcSMatt Macy [ -n "$NEWROOT" ] && rootmnt=${NEWROOT} 759eda14cbcSMatt Macy 760eda14cbcSMatt Macy [ -n "$NEW_ROOT" ] && rootmnt=${NEW_ROOT} 761eda14cbcSMatt Macy fi 762eda14cbcSMatt Macy 763eda14cbcSMatt Macy # ------------ 764eda14cbcSMatt Macy # No longer set in the defaults file, but it could have been set in 765eda14cbcSMatt Macy # get_pools() in some circumstances. If it's something, but not 'yes', 766eda14cbcSMatt Macy # it's no good to us. 767c40487d4SMatt Macy [ -n "$USE_DISK_BY_ID" ] && [ "$USE_DISK_BY_ID" != 'yes' ] && \ 768eda14cbcSMatt Macy unset USE_DISK_BY_ID 769eda14cbcSMatt Macy 770eda14cbcSMatt Macy # ---------------------------------------------------------------- 771eda14cbcSMatt Macy # P A R S E C O M M A N D L I N E O P T I O N S 772eda14cbcSMatt Macy 773eda14cbcSMatt Macy # This part is the really ugly part - there's so many options and permutations 774eda14cbcSMatt Macy # 'out there', and if we should make this the 'primary' source for ZFS initrd 775eda14cbcSMatt Macy # scripting, we need/should support them all. 776eda14cbcSMatt Macy # 777eda14cbcSMatt Macy # Supports the following kernel command line argument combinations 778eda14cbcSMatt Macy # (in this order - first match win): 779eda14cbcSMatt Macy # 780eda14cbcSMatt Macy # rpool=<pool> (tries to finds bootfs automatically) 781eda14cbcSMatt Macy # bootfs=<pool>/<dataset> (uses this for rpool - first part) 782eda14cbcSMatt Macy # rpool=<pool> bootfs=<pool>/<dataset> 783eda14cbcSMatt Macy # -B zfs-bootfs=<pool>/<fs> (uses this for rpool - first part) 784eda14cbcSMatt Macy # rpool=rpool (default if none of the above is used) 785eda14cbcSMatt Macy # root=<pool>/<dataset> (uses this for rpool - first part) 786eda14cbcSMatt Macy # root=ZFS=<pool>/<dataset> (uses this for rpool - first part, without 'ZFS=') 787dae17134SMartin Matuska # root=zfs:AUTO (tries to detect both pool and rootfs) 788eda14cbcSMatt Macy # root=zfs:<pool>/<dataset> (uses this for rpool - first part, without 'zfs:') 789eda14cbcSMatt Macy # 790eda14cbcSMatt Macy # Option <dataset> could also be <snapshot> 791eda14cbcSMatt Macy # Option <pool> could also be <guid> 792eda14cbcSMatt Macy 793eda14cbcSMatt Macy # ------------ 794eda14cbcSMatt Macy # Support force option 795eda14cbcSMatt Macy # In addition, setting one of zfs_force, zfs.force or zfsforce to 796eda14cbcSMatt Macy # 'yes', 'on' or '1' will make sure we force import the pool. 797eda14cbcSMatt Macy # This should (almost) never be needed, but it's here for 798eda14cbcSMatt Macy # completeness. 799eda14cbcSMatt Macy ZPOOL_FORCE="" 800eda14cbcSMatt Macy if grep -qiE '(^|[^\\](\\\\)* )(zfs_force|zfs\.force|zfsforce)=(on|yes|1)( |$)' /proc/cmdline 801eda14cbcSMatt Macy then 802eda14cbcSMatt Macy ZPOOL_FORCE="-f" 803eda14cbcSMatt Macy fi 804eda14cbcSMatt Macy 805eda14cbcSMatt Macy # ------------ 806eda14cbcSMatt Macy # Look for 'rpool' and 'bootfs' parameter 807eda14cbcSMatt Macy [ -n "$rpool" ] && ZFS_RPOOL="${rpool#rpool=}" 808eda14cbcSMatt Macy [ -n "$bootfs" ] && ZFS_BOOTFS="${bootfs#bootfs=}" 809eda14cbcSMatt Macy 810eda14cbcSMatt Macy # ------------ 811eda14cbcSMatt Macy # If we have 'ROOT' (see above), but not 'ZFS_BOOTFS', then use 812eda14cbcSMatt Macy # 'ROOT' 813c40487d4SMatt Macy [ -n "$ROOT" ] && [ -z "${ZFS_BOOTFS}" ] && ZFS_BOOTFS="$ROOT" 814eda14cbcSMatt Macy 815eda14cbcSMatt Macy # ------------ 816eda14cbcSMatt Macy # Check for the `-B zfs-bootfs=%s/%u,...` kind of parameter. 817eda14cbcSMatt Macy # NOTE: Only use the pool name and dataset. The rest is not 818c40487d4SMatt Macy # supported by OpenZFS (whatever it's for). 819eda14cbcSMatt Macy if [ -z "$ZFS_RPOOL" ] 820eda14cbcSMatt Macy then 821eda14cbcSMatt Macy # The ${zfs-bootfs} variable is set at the kernel command 822eda14cbcSMatt Macy # line, usually by GRUB, but it cannot be referenced here 823eda14cbcSMatt Macy # directly because bourne variable names cannot contain a 824eda14cbcSMatt Macy # hyphen. 825eda14cbcSMatt Macy # 826eda14cbcSMatt Macy # Reassign the variable by dumping the environment and 827eda14cbcSMatt Macy # stripping the zfs-bootfs= prefix. Let the shell handle 82816038816SMartin Matuska # quoting through the eval command: 82916038816SMartin Matuska # shellcheck disable=SC2046 830eda14cbcSMatt Macy eval ZFS_RPOOL=$(set | sed -n -e 's,^zfs-bootfs=,,p') 831eda14cbcSMatt Macy fi 832eda14cbcSMatt Macy 833eda14cbcSMatt Macy # ------------ 834eda14cbcSMatt Macy # No root fs or pool specified - do auto detect. 835c40487d4SMatt Macy if [ -z "$ZFS_RPOOL" ] && [ -z "${ZFS_BOOTFS}" ] 836eda14cbcSMatt Macy then 837eda14cbcSMatt Macy # Do auto detect. Do this by 'cheating' - set 'root=zfs:AUTO' 838eda14cbcSMatt Macy # which will be caught later 839c40487d4SMatt Macy ROOT='zfs:AUTO' 840eda14cbcSMatt Macy fi 841eda14cbcSMatt Macy 842eda14cbcSMatt Macy # ---------------------------------------------------------------- 843eda14cbcSMatt Macy # F I N D A N D I M P O R T C O R R E C T P O O L 844eda14cbcSMatt Macy 845eda14cbcSMatt Macy # ------------ 846eda14cbcSMatt Macy if [ "$ROOT" = "zfs:AUTO" ] 847eda14cbcSMatt Macy then 848eda14cbcSMatt Macy # Try to detect both pool and root fs. 849eda14cbcSMatt Macy 85016038816SMartin Matuska # If we got here, that means we don't have a hint so as to 85116038816SMartin Matuska # the root dataset, but with root=zfs:AUTO on cmdline, 85216038816SMartin Matuska # this says "zfs:AUTO" here and interferes with checks later 85316038816SMartin Matuska ZFS_BOOTFS= 85416038816SMartin Matuska 855eda14cbcSMatt Macy [ "$quiet" != "y" ] && \ 856eda14cbcSMatt Macy zfs_log_begin_msg "Attempting to import additional pools." 857eda14cbcSMatt Macy 858eda14cbcSMatt Macy # Get a list of pools available for import 859eda14cbcSMatt Macy if [ -n "$ZFS_RPOOL" ] 860eda14cbcSMatt Macy then 861eda14cbcSMatt Macy # We've specified a pool - check only that 862eda14cbcSMatt Macy POOLS=$ZFS_RPOOL 863eda14cbcSMatt Macy else 864eda14cbcSMatt Macy POOLS=$(get_pools) 865eda14cbcSMatt Macy fi 866eda14cbcSMatt Macy 867eda14cbcSMatt Macy OLD_IFS="$IFS" ; IFS=";" 868eda14cbcSMatt Macy for pool in $POOLS 869eda14cbcSMatt Macy do 870eda14cbcSMatt Macy [ -z "$pool" ] && continue 871eda14cbcSMatt Macy 87216038816SMartin Matuska IFS="$OLD_IFS" import_pool "$pool" 87316038816SMartin Matuska IFS="$OLD_IFS" find_rootfs "$pool" && break 874eda14cbcSMatt Macy done 875eda14cbcSMatt Macy IFS="$OLD_IFS" 876eda14cbcSMatt Macy 877681ce946SMartin Matuska [ "$quiet" != "y" ] && zfs_log_end_msg "$ZFS_ERROR" 878eda14cbcSMatt Macy else 879eda14cbcSMatt Macy # No auto - use value from the command line option. 880eda14cbcSMatt Macy 881eda14cbcSMatt Macy # Strip 'zfs:' and 'ZFS='. 882eda14cbcSMatt Macy ZFS_BOOTFS="${ROOT#*[:=]}" 883eda14cbcSMatt Macy 884eda14cbcSMatt Macy # Strip everything after the first slash. 885eda14cbcSMatt Macy ZFS_RPOOL="${ZFS_BOOTFS%%/*}" 886eda14cbcSMatt Macy fi 887eda14cbcSMatt Macy 888eda14cbcSMatt Macy # Import the pool (if not already done so in the AUTO check above). 889c40487d4SMatt Macy if [ -n "$ZFS_RPOOL" ] && [ -z "${POOL_IMPORTED}" ] 890eda14cbcSMatt Macy then 891eda14cbcSMatt Macy [ "$quiet" != "y" ] && \ 892eda14cbcSMatt Macy zfs_log_begin_msg "Importing ZFS root pool '$ZFS_RPOOL'" 893eda14cbcSMatt Macy 894eda14cbcSMatt Macy import_pool "${ZFS_RPOOL}" 895eda14cbcSMatt Macy find_rootfs "${ZFS_RPOOL}" 896eda14cbcSMatt Macy 897eda14cbcSMatt Macy [ "$quiet" != "y" ] && zfs_log_end_msg 898eda14cbcSMatt Macy fi 899eda14cbcSMatt Macy 900eda14cbcSMatt Macy if [ -z "${POOL_IMPORTED}" ] 901eda14cbcSMatt Macy then 902eda14cbcSMatt Macy # No pool imported, this is serious! 903eda14cbcSMatt Macy disable_plymouth 904eda14cbcSMatt Macy echo "" 905eda14cbcSMatt Macy echo "Command: $ZFS_CMD" 906eda14cbcSMatt Macy echo "Message: $ZFS_STDERR" 907eda14cbcSMatt Macy echo "Error: $ZFS_ERROR" 908eda14cbcSMatt Macy echo "" 909eda14cbcSMatt Macy echo "No pool imported. Manually import the root pool" 910eda14cbcSMatt Macy echo "at the command prompt and then exit." 91116038816SMartin Matuska echo "Hint: Try: zpool import -N ${ZFS_RPOOL}" 912eda14cbcSMatt Macy shell 913eda14cbcSMatt Macy fi 914eda14cbcSMatt Macy 915eda14cbcSMatt Macy # In case the pool was specified as guid, resolve guid to name 9162a58b312SMartin Matuska pool="$("${ZPOOL}" get -H -o name,value name,guid | \ 917eda14cbcSMatt Macy awk -v pool="${ZFS_RPOOL}" '$2 == pool { print $1 }')" 918eda14cbcSMatt Macy if [ -n "$pool" ]; then 919eda14cbcSMatt Macy # If $ZFS_BOOTFS contains guid, replace the guid portion with $pool 920eda14cbcSMatt Macy ZFS_BOOTFS=$(echo "$ZFS_BOOTFS" | \ 9212a58b312SMartin Matuska sed -e "s/$("${ZPOOL}" get -H -o value guid "$pool")/$pool/g") 922eda14cbcSMatt Macy ZFS_RPOOL="${pool}" 923eda14cbcSMatt Macy fi 924eda14cbcSMatt Macy 925eda14cbcSMatt Macy 926eda14cbcSMatt Macy # ---------------------------------------------------------------- 927eda14cbcSMatt Macy # P R E P A R E R O O T F I L E S Y S T E M 928eda14cbcSMatt Macy 929eda14cbcSMatt Macy if [ -n "${ZFS_BOOTFS}" ] 930eda14cbcSMatt Macy then 931eda14cbcSMatt Macy # Booting from a snapshot? 932eda14cbcSMatt Macy # Will overwrite the ZFS_BOOTFS variable like so: 933eda14cbcSMatt Macy # rpool/ROOT/debian@snap2 => rpool/ROOT/debian_snap2 934eda14cbcSMatt Macy echo "${ZFS_BOOTFS}" | grep -q '@' && \ 935eda14cbcSMatt Macy setup_snapshot_booting "${ZFS_BOOTFS}" 936eda14cbcSMatt Macy fi 937eda14cbcSMatt Macy 938eda14cbcSMatt Macy if [ -z "${ZFS_BOOTFS}" ] 939eda14cbcSMatt Macy then 940eda14cbcSMatt Macy # Still nothing! Let the user sort this out. 941eda14cbcSMatt Macy disable_plymouth 942eda14cbcSMatt Macy echo "" 943eda14cbcSMatt Macy echo "Error: Unknown root filesystem - no 'bootfs' pool property and" 944eda14cbcSMatt Macy echo " not specified on the kernel command line." 945eda14cbcSMatt Macy echo "" 946eda14cbcSMatt Macy echo "Manually mount the root filesystem on $rootmnt and then exit." 947716fd348SMartin Matuska echo "Hint: Try: mount.zfs -o zfsutil ${ZFS_RPOOL-rpool}/ROOT/system $rootmnt" 948eda14cbcSMatt Macy shell 949eda14cbcSMatt Macy fi 950eda14cbcSMatt Macy 951eda14cbcSMatt Macy # ---------------------------------------------------------------- 952eda14cbcSMatt Macy # M O U N T F I L E S Y S T E M S 953eda14cbcSMatt Macy 954eda14cbcSMatt Macy # * Ideally, the root filesystem would be mounted like this: 955eda14cbcSMatt Macy # 956eda14cbcSMatt Macy # zpool import -R "$rootmnt" -N "$ZFS_RPOOL" 957eda14cbcSMatt Macy # zfs mount -o mountpoint=/ "${ZFS_BOOTFS}" 958eda14cbcSMatt Macy # 959eda14cbcSMatt Macy # but the MOUNTPOINT prefix is preserved on descendent filesystem 960eda14cbcSMatt Macy # after the pivot into the regular root, which later breaks things 961eda14cbcSMatt Macy # like `zfs mount -a` and the /proc/self/mounts refresh. 962eda14cbcSMatt Macy # 963eda14cbcSMatt Macy # * Mount additional filesystems required 964eda14cbcSMatt Macy # Such as /usr, /var, /usr/local etc. 965eda14cbcSMatt Macy # NOTE: Mounted in the order specified in the 966eda14cbcSMatt Macy # ZFS_INITRD_ADDITIONAL_DATASETS variable so take care! 967eda14cbcSMatt Macy 968eda14cbcSMatt Macy # Go through the complete list (recursively) of all filesystems below 969eda14cbcSMatt Macy # the real root dataset 97016038816SMartin Matuska filesystems="$("${ZFS}" list -oname -tfilesystem -H -r "${ZFS_BOOTFS}")" 97116038816SMartin Matuska OLD_IFS="$IFS" ; IFS=" 97216038816SMartin Matuska" 97316038816SMartin Matuska for fs in $filesystems; do 97416038816SMartin Matuska IFS="$OLD_IFS" mount_fs "$fs" 97516038816SMartin Matuska done 97616038816SMartin Matuska IFS="$OLD_IFS" 97716038816SMartin Matuska for fs in $ZFS_INITRD_ADDITIONAL_DATASETS; do 978eda14cbcSMatt Macy mount_fs "$fs" 979eda14cbcSMatt Macy done 980eda14cbcSMatt Macy 981eda14cbcSMatt Macy touch /run/zfs_unlock_complete 982eda14cbcSMatt Macy if [ -e /run/zfs_unlock_complete_notify ]; then 98316038816SMartin Matuska read -r < /run/zfs_unlock_complete_notify 984eda14cbcSMatt Macy fi 985eda14cbcSMatt Macy 986eda14cbcSMatt Macy # ------------ 987eda14cbcSMatt Macy # Debugging information 988eda14cbcSMatt Macy if [ -n "${ZFS_DEBUG}" ] 989eda14cbcSMatt Macy then 990eda14cbcSMatt Macy #exec 2>&1- 991eda14cbcSMatt Macy 992eda14cbcSMatt Macy echo "DEBUG: imported pools:" 993eda14cbcSMatt Macy "${ZPOOL}" list -H 994eda14cbcSMatt Macy echo 995eda14cbcSMatt Macy 996eda14cbcSMatt Macy echo "DEBUG: mounted ZFS filesystems:" 997eda14cbcSMatt Macy mount | grep zfs 998eda14cbcSMatt Macy echo 999eda14cbcSMatt Macy 1000eda14cbcSMatt Macy echo "=> waiting for ENTER before continuing because of 'zfsdebug=1'. " 1001c40487d4SMatt Macy printf "%s" " 'c' for shell, 'r' for reboot, 'ENTER' to continue. " 1002c40487d4SMatt Macy read -r b 1003eda14cbcSMatt Macy 1004eda14cbcSMatt Macy [ "$b" = "c" ] && /bin/sh 1005eda14cbcSMatt Macy [ "$b" = "r" ] && reboot -f 1006eda14cbcSMatt Macy 1007eda14cbcSMatt Macy set +x 1008eda14cbcSMatt Macy fi 1009eda14cbcSMatt Macy 1010eda14cbcSMatt Macy # ------------ 1011eda14cbcSMatt Macy # Run local bottom script 1012c40487d4SMatt Macy if command -v run_scripts > /dev/null 2>&1 1013c40487d4SMatt Macy then 1014c40487d4SMatt Macy if [ -f "/scripts/local-bottom" ] || [ -d "/scripts/local-bottom" ] 1015eda14cbcSMatt Macy then 1016eda14cbcSMatt Macy [ "$quiet" != "y" ] && \ 1017eda14cbcSMatt Macy zfs_log_begin_msg "Running /scripts/local-bottom" 1018eda14cbcSMatt Macy run_scripts /scripts/local-bottom 1019eda14cbcSMatt Macy [ "$quiet" != "y" ] && zfs_log_end_msg 1020eda14cbcSMatt Macy fi 1021c40487d4SMatt Macy fi 1022eda14cbcSMatt Macy} 1023