xref: /freebsd/sys/contrib/openzfs/contrib/initramfs/scripts/zfs (revision dae1713419a669d4f6c7acddf81a21297c809741)
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.
82*dae17134SMartin Matuska	if [ -n "${ZFS_BOOTFS}" ] && [ "${ZFS_BOOTFS}" != "zfs:AUTO" ]; then
83*dae17134SMartin Matuska		return 0
84*dae17134SMartin 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 | \
110*dae17134SMartin 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.
197eda14cbcSMatt Macy	"${ZPOOL}" get name,guid -o value -H 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{
27516038816SMartin Matuska	[ -n "$ROOTDELAY" ] && ZFS_INITRD_PRE_MOUNTROOT_SLEEP="$ROOTDELAY"
27616038816SMartin Matuska
277c40487d4SMatt Macy	if [ "$ZFS_INITRD_PRE_MOUNTROOT_SLEEP" -gt 0 ] 2>/dev/null
278eda14cbcSMatt Macy	then
279eda14cbcSMatt Macy		if [ "$quiet" != "y" ]; then
280eda14cbcSMatt Macy			zfs_log_begin_msg "Sleeping for" \
281eda14cbcSMatt Macy				"$ZFS_INITRD_PRE_MOUNTROOT_SLEEP seconds..."
282eda14cbcSMatt Macy		fi
283eda14cbcSMatt Macy		sleep "$ZFS_INITRD_PRE_MOUNTROOT_SLEEP"
284eda14cbcSMatt Macy		[ "$quiet" != "y" ] && zfs_log_end_msg
285eda14cbcSMatt Macy	fi
286eda14cbcSMatt Macy
287eda14cbcSMatt Macy	# Wait for all of the /dev/{hd,sd}[a-z] device nodes to appear.
288c40487d4SMatt Macy	if command -v wait_for_udev > /dev/null 2>&1 ; then
289eda14cbcSMatt Macy		wait_for_udev 10
290c40487d4SMatt Macy	elif command -v wait_for_dev > /dev/null 2>&1 ; then
291eda14cbcSMatt Macy		wait_for_dev
292eda14cbcSMatt Macy	fi
293eda14cbcSMatt Macy
294eda14cbcSMatt Macy	# zpool import refuse to import without a valid /proc/self/mounts
295eda14cbcSMatt Macy	[ ! -f /proc/self/mounts ] && mount proc /proc
296eda14cbcSMatt Macy
297eda14cbcSMatt Macy	# Load the module
298eda14cbcSMatt Macy	load_module "zfs" || return 1
299eda14cbcSMatt Macy
300c40487d4SMatt Macy	if [ "$ZFS_INITRD_POST_MODPROBE_SLEEP" -gt 0 ] 2>/dev/null
301eda14cbcSMatt Macy	then
302eda14cbcSMatt Macy		if [ "$quiet" != "y" ]; then
303eda14cbcSMatt Macy			zfs_log_begin_msg "Sleeping for" \
304eda14cbcSMatt Macy				"$ZFS_INITRD_POST_MODPROBE_SLEEP seconds..."
305eda14cbcSMatt Macy		fi
306eda14cbcSMatt Macy		sleep "$ZFS_INITRD_POST_MODPROBE_SLEEP"
307eda14cbcSMatt Macy		[ "$quiet" != "y" ] && zfs_log_end_msg
308eda14cbcSMatt Macy	fi
309eda14cbcSMatt Macy
310eda14cbcSMatt Macy	return 0
311eda14cbcSMatt Macy}
312eda14cbcSMatt Macy
313eda14cbcSMatt Macy# Mount a given filesystem
314eda14cbcSMatt Macymount_fs()
315eda14cbcSMatt Macy{
316c40487d4SMatt Macy	fs="$1"
317eda14cbcSMatt Macy
318eda14cbcSMatt Macy	# Check that the filesystem exists
319c40487d4SMatt Macy	"${ZFS}" list -oname -tfilesystem -H "${fs}" > /dev/null 2>&1 ||  return 1
320eda14cbcSMatt Macy
321eda14cbcSMatt Macy	# Skip filesystems with canmount=off.  The root fs should not have
322eda14cbcSMatt Macy	# canmount=off, but ignore it for backwards compatibility just in case.
323eda14cbcSMatt Macy	if [ "$fs" != "${ZFS_BOOTFS}" ]
324eda14cbcSMatt Macy	then
325eda14cbcSMatt Macy		canmount=$(get_fs_value "$fs" canmount)
326eda14cbcSMatt Macy		[ "$canmount" = "off" ] && return 0
327eda14cbcSMatt Macy	fi
328eda14cbcSMatt Macy
329eda14cbcSMatt Macy	# Need the _original_ datasets mountpoint!
330eda14cbcSMatt Macy	mountpoint=$(get_fs_value "$fs" mountpoint)
33116038816SMartin Matuska	ZFS_CMD="mount -o zfsutil -t zfs"
332c40487d4SMatt Macy	if [ "$mountpoint" = "legacy" ] || [ "$mountpoint" = "none" ]; then
333eda14cbcSMatt Macy		# Can't use the mountpoint property. Might be one of our
334eda14cbcSMatt Macy		# clones. Check the 'org.zol:mountpoint' property set in
335eda14cbcSMatt Macy		# clone_snap() if that's usable.
336eda14cbcSMatt Macy		mountpoint=$(get_fs_value "$fs" org.zol:mountpoint)
337c40487d4SMatt Macy		if [ "$mountpoint" = "legacy" ] ||
338c40487d4SMatt Macy		   [ "$mountpoint" = "none" ] ||
339c40487d4SMatt Macy		   [ "$mountpoint" = "-" ]
340eda14cbcSMatt Macy		then
341eda14cbcSMatt Macy			if [ "$fs" != "${ZFS_BOOTFS}" ]; then
342eda14cbcSMatt Macy				# We don't have a proper mountpoint and this
343eda14cbcSMatt Macy				# isn't the root fs.
344eda14cbcSMatt Macy				return 0
345eda14cbcSMatt Macy			else
346eda14cbcSMatt Macy				# Last hail-mary: Hope 'rootmnt' is set!
347eda14cbcSMatt Macy				mountpoint=""
348eda14cbcSMatt Macy			fi
349eda14cbcSMatt Macy		fi
350eda14cbcSMatt Macy
351eda14cbcSMatt Macy		# If it's not a legacy filesystem, it can only be a
352eda14cbcSMatt Macy		# native one...
35316038816SMartin Matuska		if [ "$mountpoint" = "legacy" ]; then
35416038816SMartin Matuska			ZFS_CMD="mount -t zfs"
355eda14cbcSMatt Macy		fi
356eda14cbcSMatt Macy	fi
357eda14cbcSMatt Macy
358eda14cbcSMatt Macy	# Possibly decrypt a filesystem using native encryption.
359eda14cbcSMatt Macy	decrypt_fs "$fs"
360eda14cbcSMatt Macy
361eda14cbcSMatt Macy	[ "$quiet" != "y" ] && \
362eda14cbcSMatt Macy	    zfs_log_begin_msg "Mounting '${fs}' on '${rootmnt}/${mountpoint}'"
363eda14cbcSMatt Macy	[ -n "${ZFS_DEBUG}" ] && \
364eda14cbcSMatt Macy	    zfs_log_begin_msg "CMD: '$ZFS_CMD ${fs} ${rootmnt}/${mountpoint}'"
365eda14cbcSMatt Macy
366eda14cbcSMatt Macy	ZFS_STDERR=$(${ZFS_CMD} "${fs}" "${rootmnt}/${mountpoint}" 2>&1)
367eda14cbcSMatt Macy	ZFS_ERROR=$?
368eda14cbcSMatt Macy	if [ "${ZFS_ERROR}" != 0 ]
369eda14cbcSMatt Macy	then
370eda14cbcSMatt Macy		[ "$quiet" != "y" ] && zfs_log_failure_msg "${ZFS_ERROR}"
371eda14cbcSMatt Macy
372eda14cbcSMatt Macy		disable_plymouth
373eda14cbcSMatt Macy		echo ""
374eda14cbcSMatt Macy		echo "Command: ${ZFS_CMD} ${fs} ${rootmnt}/${mountpoint}"
375eda14cbcSMatt Macy		echo "Message: $ZFS_STDERR"
376eda14cbcSMatt Macy		echo "Error: $ZFS_ERROR"
377eda14cbcSMatt Macy		echo ""
378eda14cbcSMatt Macy		echo "Failed to mount ${fs} on ${rootmnt}/${mountpoint}."
379eda14cbcSMatt Macy		echo "Manually mount the filesystem and exit."
380eda14cbcSMatt Macy		shell
381eda14cbcSMatt Macy	else
382eda14cbcSMatt Macy		[ "$quiet" != "y" ] && zfs_log_end_msg
383eda14cbcSMatt Macy	fi
384eda14cbcSMatt Macy
385eda14cbcSMatt Macy	return 0
386eda14cbcSMatt Macy}
387eda14cbcSMatt Macy
388eda14cbcSMatt Macy# Unlock a ZFS native encrypted filesystem.
389eda14cbcSMatt Macydecrypt_fs()
390eda14cbcSMatt Macy{
391c40487d4SMatt Macy	fs="$1"
392eda14cbcSMatt Macy
393eda14cbcSMatt Macy	# If pool encryption is active and the zfs command understands '-o encryption'
39416038816SMartin Matuska	if [ "$(zpool list -H -o feature@encryption "${fs%%/*}")" = 'active' ]; then
395eda14cbcSMatt Macy
396eda14cbcSMatt Macy		# Determine dataset that holds key for root dataset
397eda14cbcSMatt Macy		ENCRYPTIONROOT="$(get_fs_value "${fs}" encryptionroot)"
398eda14cbcSMatt Macy		KEYLOCATION="$(get_fs_value "${ENCRYPTIONROOT}" keylocation)"
399eda14cbcSMatt Macy
400eda14cbcSMatt Macy		echo "${ENCRYPTIONROOT}" > /run/zfs_fs_name
401eda14cbcSMatt Macy
402eda14cbcSMatt Macy		# If root dataset is encrypted...
403eda14cbcSMatt Macy		if ! [ "${ENCRYPTIONROOT}" = "-" ]; then
404eda14cbcSMatt Macy			KEYSTATUS="$(get_fs_value "${ENCRYPTIONROOT}" keystatus)"
405eda14cbcSMatt Macy			# Continue only if the key needs to be loaded
406eda14cbcSMatt Macy			[ "$KEYSTATUS" = "unavailable" ] || return 0
407eda14cbcSMatt Macy
40816038816SMartin Matuska			# Do not prompt if key is stored noninteractively,
409eda14cbcSMatt Macy			if ! [ "${KEYLOCATION}" = "prompt" ]; then
410eda14cbcSMatt Macy				$ZFS load-key "${ENCRYPTIONROOT}"
411eda14cbcSMatt Macy
412eda14cbcSMatt Macy			# Prompt with plymouth, if active
41316038816SMartin Matuska			elif /bin/plymouth --ping 2>/dev/null; then
414eda14cbcSMatt Macy				echo "plymouth" > /run/zfs_console_askpwd_cmd
41516038816SMartin Matuska				for _ in 1 2 3; do
416eda14cbcSMatt Macy					plymouth ask-for-password --prompt "Encrypted ZFS password for ${ENCRYPTIONROOT}" | \
417eda14cbcSMatt Macy						$ZFS load-key "${ENCRYPTIONROOT}" && break
418eda14cbcSMatt Macy				done
419eda14cbcSMatt Macy
420eda14cbcSMatt Macy			# Prompt with systemd, if active
421eda14cbcSMatt Macy			elif [ -e /run/systemd/system ]; then
422eda14cbcSMatt Macy				echo "systemd-ask-password" > /run/zfs_console_askpwd_cmd
42316038816SMartin Matuska				for _ in 1 2 3; do
424eda14cbcSMatt Macy					systemd-ask-password "Encrypted ZFS password for ${ENCRYPTIONROOT}" --no-tty | \
425eda14cbcSMatt Macy						$ZFS load-key "${ENCRYPTIONROOT}" && break
426eda14cbcSMatt Macy				done
427eda14cbcSMatt Macy
428eda14cbcSMatt Macy			# Prompt with ZFS tty, otherwise
429eda14cbcSMatt Macy			else
430eda14cbcSMatt Macy				# Temporarily setting "printk" to "7" allows the prompt to appear even when the "quiet" kernel option has been used
431eda14cbcSMatt Macy				echo "load-key" > /run/zfs_console_askpwd_cmd
432*dae17134SMartin Matuska				read -r storeprintk _ < /proc/sys/kernel/printk
433eda14cbcSMatt Macy				echo 7 > /proc/sys/kernel/printk
434eda14cbcSMatt Macy				$ZFS load-key "${ENCRYPTIONROOT}"
435eda14cbcSMatt Macy				echo "$storeprintk" > /proc/sys/kernel/printk
436eda14cbcSMatt Macy			fi
437eda14cbcSMatt Macy		fi
438eda14cbcSMatt Macy	fi
439eda14cbcSMatt Macy
440eda14cbcSMatt Macy	return 0
441eda14cbcSMatt Macy}
442eda14cbcSMatt Macy
443eda14cbcSMatt Macy# Destroy a given filesystem.
444eda14cbcSMatt Macydestroy_fs()
445eda14cbcSMatt Macy{
446c40487d4SMatt Macy	fs="$1"
447eda14cbcSMatt Macy
448eda14cbcSMatt Macy	[ "$quiet" != "y" ] && \
449eda14cbcSMatt Macy	    zfs_log_begin_msg "Destroying '$fs'"
450eda14cbcSMatt Macy
451eda14cbcSMatt Macy	ZFS_CMD="${ZFS} destroy $fs"
452eda14cbcSMatt Macy	ZFS_STDERR="$(${ZFS_CMD} 2>&1)"
453eda14cbcSMatt Macy	ZFS_ERROR="$?"
454eda14cbcSMatt Macy	if [ "${ZFS_ERROR}" != 0 ]
455eda14cbcSMatt Macy	then
456eda14cbcSMatt Macy		[ "$quiet" != "y" ] && zfs_log_failure_msg "${ZFS_ERROR}"
457eda14cbcSMatt Macy
458eda14cbcSMatt Macy		disable_plymouth
459eda14cbcSMatt Macy		echo ""
460eda14cbcSMatt Macy		echo "Command: $ZFS_CMD"
461eda14cbcSMatt Macy		echo "Message: $ZFS_STDERR"
462eda14cbcSMatt Macy		echo "Error: $ZFS_ERROR"
463eda14cbcSMatt Macy		echo ""
464eda14cbcSMatt Macy		echo "Failed to destroy '$fs'. Please make sure that '$fs' is not available."
465eda14cbcSMatt Macy		echo "Hint: Try:  zfs destroy -Rfn $fs"
466eda14cbcSMatt Macy		echo "If this dryrun looks good, then remove the 'n' from '-Rfn' and try again."
467eda14cbcSMatt Macy		shell
468eda14cbcSMatt Macy	else
469eda14cbcSMatt Macy		[ "$quiet" != "y" ] && zfs_log_end_msg
470eda14cbcSMatt Macy	fi
471eda14cbcSMatt Macy
472eda14cbcSMatt Macy	return 0
473eda14cbcSMatt Macy}
474eda14cbcSMatt Macy
475eda14cbcSMatt Macy# Clone snapshot $1 to destination filesystem $2
476eda14cbcSMatt Macy# Set 'canmount=noauto' and 'mountpoint=none' so that we get to keep
477eda14cbcSMatt Macy# manual control over it's mounting (i.e., make sure it's not automatically
478eda14cbcSMatt Macy# mounted with a 'zfs mount -a' in the init/systemd scripts).
479eda14cbcSMatt Macyclone_snap()
480eda14cbcSMatt Macy{
481c40487d4SMatt Macy	snap="$1"
482c40487d4SMatt Macy	destfs="$2"
483c40487d4SMatt Macy	mountpoint="$3"
484eda14cbcSMatt Macy
485eda14cbcSMatt Macy	[ "$quiet" != "y" ] && zfs_log_begin_msg "Cloning '$snap' to '$destfs'"
486eda14cbcSMatt Macy
487eda14cbcSMatt Macy	# Clone the snapshot into a dataset we can boot from
488eda14cbcSMatt Macy	# + We don't want this filesystem to be automatically mounted, we
489eda14cbcSMatt Macy	#   want control over this here and nowhere else.
490eda14cbcSMatt Macy	# + We don't need any mountpoint set for the same reason.
491eda14cbcSMatt Macy	# We use the 'org.zol:mountpoint' property to remember the mountpoint.
492eda14cbcSMatt Macy	ZFS_CMD="${ZFS} clone -o canmount=noauto -o mountpoint=none"
493eda14cbcSMatt Macy	ZFS_CMD="${ZFS_CMD} -o org.zol:mountpoint=${mountpoint}"
494eda14cbcSMatt Macy	ZFS_CMD="${ZFS_CMD} $snap $destfs"
495eda14cbcSMatt Macy	ZFS_STDERR="$(${ZFS_CMD} 2>&1)"
496eda14cbcSMatt Macy	ZFS_ERROR="$?"
497eda14cbcSMatt Macy	if [ "${ZFS_ERROR}" != 0 ]
498eda14cbcSMatt Macy	then
499eda14cbcSMatt Macy		[ "$quiet" != "y" ] && zfs_log_failure_msg "${ZFS_ERROR}"
500eda14cbcSMatt Macy
501eda14cbcSMatt Macy		disable_plymouth
502eda14cbcSMatt Macy		echo ""
503eda14cbcSMatt Macy		echo "Command: $ZFS_CMD"
504eda14cbcSMatt Macy		echo "Message: $ZFS_STDERR"
505eda14cbcSMatt Macy		echo "Error: $ZFS_ERROR"
506eda14cbcSMatt Macy		echo ""
507eda14cbcSMatt Macy		echo "Failed to clone snapshot."
508eda14cbcSMatt Macy		echo "Make sure that the any problems are corrected and then make sure"
509eda14cbcSMatt Macy		echo "that the dataset '$destfs' exists and is bootable."
510eda14cbcSMatt Macy		shell
511eda14cbcSMatt Macy	else
512eda14cbcSMatt Macy		[ "$quiet" != "y" ] && zfs_log_end_msg
513eda14cbcSMatt Macy	fi
514eda14cbcSMatt Macy
515eda14cbcSMatt Macy	return 0
516eda14cbcSMatt Macy}
517eda14cbcSMatt Macy
518eda14cbcSMatt Macy# Rollback a given snapshot.
519eda14cbcSMatt Macyrollback_snap()
520eda14cbcSMatt Macy{
521c40487d4SMatt Macy	snap="$1"
522eda14cbcSMatt Macy
523eda14cbcSMatt Macy	[ "$quiet" != "y" ] && zfs_log_begin_msg "Rollback $snap"
524eda14cbcSMatt Macy
525eda14cbcSMatt Macy	ZFS_CMD="${ZFS} rollback -Rf $snap"
526eda14cbcSMatt Macy	ZFS_STDERR="$(${ZFS_CMD} 2>&1)"
527eda14cbcSMatt Macy	ZFS_ERROR="$?"
528eda14cbcSMatt Macy	if [ "${ZFS_ERROR}" != 0 ]
529eda14cbcSMatt Macy	then
530eda14cbcSMatt Macy		[ "$quiet" != "y" ] && zfs_log_failure_msg "${ZFS_ERROR}"
531eda14cbcSMatt Macy
532eda14cbcSMatt Macy		disable_plymouth
533eda14cbcSMatt Macy		echo ""
534eda14cbcSMatt Macy		echo "Command: $ZFS_CMD"
535eda14cbcSMatt Macy		echo "Message: $ZFS_STDERR"
536eda14cbcSMatt Macy		echo "Error: $ZFS_ERROR"
537eda14cbcSMatt Macy		echo ""
538eda14cbcSMatt Macy		echo "Failed to rollback snapshot."
539eda14cbcSMatt Macy		shell
540eda14cbcSMatt Macy	else
541eda14cbcSMatt Macy		[ "$quiet" != "y" ] && zfs_log_end_msg
542eda14cbcSMatt Macy	fi
543eda14cbcSMatt Macy
544eda14cbcSMatt Macy	return 0
545eda14cbcSMatt Macy}
546eda14cbcSMatt Macy
547eda14cbcSMatt Macy# Get a list of snapshots, give them as a numbered list
548eda14cbcSMatt Macy# to the user to choose from.
549eda14cbcSMatt Macyask_user_snap()
550eda14cbcSMatt Macy{
551c40487d4SMatt Macy	fs="$1"
552eda14cbcSMatt Macy
553eda14cbcSMatt Macy	# We need to temporarily disable debugging. Set 'debug' so we
554eda14cbcSMatt Macy	# remember to enabled it again.
555eda14cbcSMatt Macy	if [ -n "${ZFS_DEBUG}" ]; then
556eda14cbcSMatt Macy		unset ZFS_DEBUG
557eda14cbcSMatt Macy		set +x
558eda14cbcSMatt Macy		debug=1
559eda14cbcSMatt Macy	fi
560eda14cbcSMatt Macy
561eda14cbcSMatt Macy	# Because we need the resulting snapshot, which is sent on
562eda14cbcSMatt Macy	# stdout to the caller, we use stderr for our questions.
563eda14cbcSMatt Macy	echo "What snapshot do you want to boot from?" > /dev/stderr
56416038816SMartin Matuska	# shellcheck disable=SC2046
56516038816SMartin Matuska	IFS="
56616038816SMartin Matuska" set -- $("${ZFS}" list -H -oname -tsnapshot -r "${fs}")
567eda14cbcSMatt Macy
56816038816SMartin Matuska	i=1
56916038816SMartin Matuska	for snap in "$@"; do
57016038816SMartin Matuska		echo "  $i: $snap"
57116038816SMartin Matuska		i=$((i + 1))
57216038816SMartin Matuska	done > /dev/stderr
57316038816SMartin Matuska
57416038816SMartin Matuska	# expr instead of test here because [ a -lt 0 ] errors out,
57516038816SMartin Matuska	# but expr falls back to lexicographical, which works out right
57616038816SMartin Matuska	snapnr=0
57716038816SMartin Matuska	while expr "$snapnr" "<" 1 > /dev/null ||
57816038816SMartin Matuska	    expr "$snapnr" ">" "$#" > /dev/null
57916038816SMartin Matuska	do
58016038816SMartin Matuska		printf "%s" "Snap nr [1-$#]? " > /dev/stderr
581c40487d4SMatt Macy		read -r snapnr
58216038816SMartin Matuska	done
583eda14cbcSMatt Macy
584eda14cbcSMatt Macy	# Re-enable debugging.
585eda14cbcSMatt Macy	if [ -n "${debug}" ]; then
586eda14cbcSMatt Macy		ZFS_DEBUG=1
587eda14cbcSMatt Macy		set -x
588eda14cbcSMatt Macy	fi
589eda14cbcSMatt Macy
59016038816SMartin Matuska	eval echo '$'"$snapnr"
591eda14cbcSMatt Macy}
592eda14cbcSMatt Macy
593eda14cbcSMatt Macysetup_snapshot_booting()
594eda14cbcSMatt Macy{
595c40487d4SMatt Macy	snap="$1"
596c40487d4SMatt Macy	retval=0
597eda14cbcSMatt Macy
598c40487d4SMatt Macy	# Make sure that the snapshot specified actually exists.
599c40487d4SMatt Macy	if [ ! "$(get_fs_value "${snap}" type)" ]
600eda14cbcSMatt Macy	then
601eda14cbcSMatt Macy		# Snapshot does not exist (...@<null> ?)
602eda14cbcSMatt Macy		# ask the user for a snapshot to use.
603eda14cbcSMatt Macy		snap="$(ask_user_snap "${snap%%@*}")"
604eda14cbcSMatt Macy	fi
605eda14cbcSMatt Macy
606eda14cbcSMatt Macy	# Separate the full snapshot ('$snap') into it's filesystem and
607eda14cbcSMatt Macy	# snapshot names. Would have been nice with a split() function..
608eda14cbcSMatt Macy	rootfs="${snap%%@*}"
609eda14cbcSMatt Macy	snapname="${snap##*@}"
610eda14cbcSMatt Macy	ZFS_BOOTFS="${rootfs}_${snapname}"
611eda14cbcSMatt Macy
612eda14cbcSMatt Macy	if ! grep -qiE '(^|[^\\](\\\\)* )(rollback)=(on|yes|1)( |$)' /proc/cmdline
613eda14cbcSMatt Macy	then
614eda14cbcSMatt Macy		# If the destination dataset for the clone
615eda14cbcSMatt Macy		# already exists, destroy it. Recursively
616c40487d4SMatt Macy		if [ "$(get_fs_value "${rootfs}_${snapname}" type)" ]; then
617eda14cbcSMatt Macy			filesystems=$("${ZFS}" list -oname -tfilesystem -H \
618eda14cbcSMatt Macy			    -r -Sname "${ZFS_BOOTFS}")
619eda14cbcSMatt Macy			for fs in $filesystems; do
620eda14cbcSMatt Macy				destroy_fs "${fs}"
621eda14cbcSMatt Macy			done
622eda14cbcSMatt Macy		fi
623eda14cbcSMatt Macy	fi
624eda14cbcSMatt Macy
625eda14cbcSMatt Macy	# Get all snapshots, recursively (might need to clone /usr, /var etc
626eda14cbcSMatt Macy	# as well).
627eda14cbcSMatt Macy	for s in $("${ZFS}" list -H -oname -tsnapshot -r "${rootfs}" | \
628eda14cbcSMatt Macy	    grep "${snapname}")
629eda14cbcSMatt Macy	do
630eda14cbcSMatt Macy		if grep -qiE '(^|[^\\](\\\\)* )(rollback)=(on|yes|1)( |$)' /proc/cmdline
631eda14cbcSMatt Macy		then
632eda14cbcSMatt Macy			# Rollback snapshot
633eda14cbcSMatt Macy			rollback_snap "$s" || retval=$((retval + 1))
6346ba2210eSMartin Matuska			ZFS_BOOTFS="${rootfs}"
635eda14cbcSMatt Macy		else
636eda14cbcSMatt Macy			# Setup a destination filesystem name.
637eda14cbcSMatt Macy			# Ex: Called with 'rpool/ROOT/debian@snap2'
638eda14cbcSMatt Macy			#       rpool/ROOT/debian@snap2		=> rpool/ROOT/debian_snap2
639eda14cbcSMatt Macy			#       rpool/ROOT/debian/boot@snap2	=> rpool/ROOT/debian_snap2/boot
640eda14cbcSMatt Macy			#       rpool/ROOT/debian/usr@snap2	=> rpool/ROOT/debian_snap2/usr
641eda14cbcSMatt Macy			#       rpool/ROOT/debian/var@snap2	=> rpool/ROOT/debian_snap2/var
642eda14cbcSMatt Macy			subfs="${s##$rootfs}"
643eda14cbcSMatt Macy			subfs="${subfs%%@$snapname}"
644eda14cbcSMatt Macy
645eda14cbcSMatt Macy			destfs="${rootfs}_${snapname}" # base fs.
646eda14cbcSMatt Macy			[ -n "$subfs" ] && destfs="${destfs}$subfs" # + sub fs.
647eda14cbcSMatt Macy
648eda14cbcSMatt Macy			# Get the mountpoint of the filesystem, to be used
649eda14cbcSMatt Macy			# with clone_snap(). If legacy or none, then use
650eda14cbcSMatt Macy			# the sub fs value.
651eda14cbcSMatt Macy			mountpoint=$(get_fs_value "${s%%@*}" mountpoint)
652c40487d4SMatt Macy			if [ "$mountpoint" = "legacy" ] || \
653c40487d4SMatt Macy			   [ "$mountpoint" = "none" ]
654eda14cbcSMatt Macy			then
655eda14cbcSMatt Macy				if [ -n "${subfs}" ]; then
656eda14cbcSMatt Macy					mountpoint="${subfs}"
657eda14cbcSMatt Macy				else
658eda14cbcSMatt Macy					mountpoint="/"
659eda14cbcSMatt Macy				fi
660eda14cbcSMatt Macy			fi
661eda14cbcSMatt Macy
662eda14cbcSMatt Macy			# Clone the snapshot into its own
663eda14cbcSMatt Macy			# filesystem
664eda14cbcSMatt Macy			clone_snap "$s" "${destfs}" "${mountpoint}" || \
665eda14cbcSMatt Macy			    retval=$((retval + 1))
666eda14cbcSMatt Macy		fi
667eda14cbcSMatt Macy	done
668eda14cbcSMatt Macy
669eda14cbcSMatt Macy	# If we haven't return yet, we have a problem...
670eda14cbcSMatt Macy	return "${retval}"
671eda14cbcSMatt Macy}
672eda14cbcSMatt Macy
673eda14cbcSMatt Macy# ================================================================
674eda14cbcSMatt Macy
675eda14cbcSMatt Macy# This is the main function.
676eda14cbcSMatt Macymountroot()
677eda14cbcSMatt Macy{
678eda14cbcSMatt Macy	# ----------------------------------------------------------------
679eda14cbcSMatt Macy	# I N I T I A L   S E T U P
680eda14cbcSMatt Macy
681eda14cbcSMatt Macy	# ------------
682eda14cbcSMatt Macy	# Run the pre-mount scripts from /scripts/local-top.
683eda14cbcSMatt Macy	pre_mountroot
684eda14cbcSMatt Macy
685eda14cbcSMatt Macy	# ------------
686eda14cbcSMatt Macy	# Source the default setup variables.
687eda14cbcSMatt Macy	[ -r '/etc/default/zfs' ] && . /etc/default/zfs
688eda14cbcSMatt Macy
689eda14cbcSMatt Macy	# ------------
690eda14cbcSMatt Macy	# Support debug option
691eda14cbcSMatt Macy	if grep -qiE '(^|[^\\](\\\\)* )(zfs_debug|zfs\.debug|zfsdebug)=(on|yes|1)( |$)' /proc/cmdline
692eda14cbcSMatt Macy	then
693eda14cbcSMatt Macy		ZFS_DEBUG=1
694eda14cbcSMatt Macy		mkdir /var/log
695eda14cbcSMatt Macy		#exec 2> /var/log/boot.debug
696eda14cbcSMatt Macy		set -x
697eda14cbcSMatt Macy	fi
698eda14cbcSMatt Macy
699eda14cbcSMatt Macy	# ------------
700eda14cbcSMatt Macy	# Load ZFS module etc.
701eda14cbcSMatt Macy	if ! load_module_initrd; then
702eda14cbcSMatt Macy		disable_plymouth
703eda14cbcSMatt Macy		echo ""
704eda14cbcSMatt Macy		echo "Failed to load ZFS modules."
705eda14cbcSMatt Macy		echo "Manually load the modules and exit."
706eda14cbcSMatt Macy		shell
707eda14cbcSMatt Macy	fi
708eda14cbcSMatt Macy
709eda14cbcSMatt Macy	# ------------
710eda14cbcSMatt Macy	# Look for the cache file (if any).
71116038816SMartin Matuska	[ -f "${ZPOOL_CACHE}" ] || unset ZPOOL_CACHE
71216038816SMartin Matuska	[ -s "${ZPOOL_CACHE}" ] || unset ZPOOL_CACHE
713eda14cbcSMatt Macy
714eda14cbcSMatt Macy	# ------------
715eda14cbcSMatt Macy	# Compatibility: 'ROOT' is for Debian GNU/Linux (etc),
716eda14cbcSMatt Macy	#		 'root' is for Redhat/Fedora (etc),
717eda14cbcSMatt Macy	#		 'REAL_ROOT' is for Gentoo
718eda14cbcSMatt Macy	if [ -z "$ROOT" ]
719eda14cbcSMatt Macy	then
720eda14cbcSMatt Macy		[ -n "$root" ] && ROOT=${root}
721eda14cbcSMatt Macy
722eda14cbcSMatt Macy		[ -n "$REAL_ROOT" ] && ROOT=${REAL_ROOT}
723eda14cbcSMatt Macy	fi
724eda14cbcSMatt Macy
725eda14cbcSMatt Macy	# ------------
726eda14cbcSMatt Macy	# Where to mount the root fs in the initrd - set outside this script
727eda14cbcSMatt Macy	# Compatibility: 'rootmnt' is for Debian GNU/Linux (etc),
728eda14cbcSMatt Macy	#		 'NEWROOT' is for RedHat/Fedora (etc),
729eda14cbcSMatt Macy	#		 'NEW_ROOT' is for Gentoo
730eda14cbcSMatt Macy	if [ -z "$rootmnt" ]
731eda14cbcSMatt Macy	then
732eda14cbcSMatt Macy		[ -n "$NEWROOT" ] && rootmnt=${NEWROOT}
733eda14cbcSMatt Macy
734eda14cbcSMatt Macy		[ -n "$NEW_ROOT" ] && rootmnt=${NEW_ROOT}
735eda14cbcSMatt Macy	fi
736eda14cbcSMatt Macy
737eda14cbcSMatt Macy	# ------------
738eda14cbcSMatt Macy	# No longer set in the defaults file, but it could have been set in
739eda14cbcSMatt Macy	# get_pools() in some circumstances. If it's something, but not 'yes',
740eda14cbcSMatt Macy	# it's no good to us.
741c40487d4SMatt Macy	[ -n "$USE_DISK_BY_ID" ] && [ "$USE_DISK_BY_ID" != 'yes' ] && \
742eda14cbcSMatt Macy	    unset USE_DISK_BY_ID
743eda14cbcSMatt Macy
744eda14cbcSMatt Macy	# ----------------------------------------------------------------
745eda14cbcSMatt Macy	# P A R S E   C O M M A N D   L I N E   O P T I O N S
746eda14cbcSMatt Macy
747eda14cbcSMatt Macy	# This part is the really ugly part - there's so many options and permutations
748eda14cbcSMatt Macy	# 'out there', and if we should make this the 'primary' source for ZFS initrd
749eda14cbcSMatt Macy	# scripting, we need/should support them all.
750eda14cbcSMatt Macy	#
751eda14cbcSMatt Macy	# Supports the following kernel command line argument combinations
752eda14cbcSMatt Macy	# (in this order - first match win):
753eda14cbcSMatt Macy	#
754eda14cbcSMatt Macy	#	rpool=<pool>			(tries to finds bootfs automatically)
755eda14cbcSMatt Macy	#	bootfs=<pool>/<dataset>		(uses this for rpool - first part)
756eda14cbcSMatt Macy	#	rpool=<pool> bootfs=<pool>/<dataset>
757eda14cbcSMatt Macy	#	-B zfs-bootfs=<pool>/<fs>	(uses this for rpool - first part)
758eda14cbcSMatt Macy	#	rpool=rpool			(default if none of the above is used)
759eda14cbcSMatt Macy	#	root=<pool>/<dataset>		(uses this for rpool - first part)
760eda14cbcSMatt Macy	#	root=ZFS=<pool>/<dataset>	(uses this for rpool - first part, without 'ZFS=')
761*dae17134SMartin Matuska	#	root=zfs:AUTO			(tries to detect both pool and rootfs)
762eda14cbcSMatt Macy	#	root=zfs:<pool>/<dataset>	(uses this for rpool - first part, without 'zfs:')
763eda14cbcSMatt Macy	#
764eda14cbcSMatt Macy	# Option <dataset> could also be <snapshot>
765eda14cbcSMatt Macy	# Option <pool> could also be <guid>
766eda14cbcSMatt Macy
767eda14cbcSMatt Macy	# ------------
768eda14cbcSMatt Macy	# Support force option
769eda14cbcSMatt Macy	# In addition, setting one of zfs_force, zfs.force or zfsforce to
770eda14cbcSMatt Macy	# 'yes', 'on' or '1' will make sure we force import the pool.
771eda14cbcSMatt Macy	# This should (almost) never be needed, but it's here for
772eda14cbcSMatt Macy	# completeness.
773eda14cbcSMatt Macy	ZPOOL_FORCE=""
774eda14cbcSMatt Macy	if grep -qiE '(^|[^\\](\\\\)* )(zfs_force|zfs\.force|zfsforce)=(on|yes|1)( |$)' /proc/cmdline
775eda14cbcSMatt Macy	then
776eda14cbcSMatt Macy		ZPOOL_FORCE="-f"
777eda14cbcSMatt Macy	fi
778eda14cbcSMatt Macy
779eda14cbcSMatt Macy	# ------------
780eda14cbcSMatt Macy	# Look for 'rpool' and 'bootfs' parameter
781eda14cbcSMatt Macy	[ -n "$rpool" ] && ZFS_RPOOL="${rpool#rpool=}"
782eda14cbcSMatt Macy	[ -n "$bootfs" ] && ZFS_BOOTFS="${bootfs#bootfs=}"
783eda14cbcSMatt Macy
784eda14cbcSMatt Macy	# ------------
785eda14cbcSMatt Macy	# If we have 'ROOT' (see above), but not 'ZFS_BOOTFS', then use
786eda14cbcSMatt Macy	# 'ROOT'
787c40487d4SMatt Macy	[ -n "$ROOT" ] && [ -z "${ZFS_BOOTFS}" ] && ZFS_BOOTFS="$ROOT"
788eda14cbcSMatt Macy
789eda14cbcSMatt Macy	# ------------
790eda14cbcSMatt Macy	# Check for the `-B zfs-bootfs=%s/%u,...` kind of parameter.
791eda14cbcSMatt Macy	# NOTE: Only use the pool name and dataset. The rest is not
792c40487d4SMatt Macy	#       supported by OpenZFS (whatever it's for).
793eda14cbcSMatt Macy	if [ -z "$ZFS_RPOOL" ]
794eda14cbcSMatt Macy	then
795eda14cbcSMatt Macy		# The ${zfs-bootfs} variable is set at the kernel command
796eda14cbcSMatt Macy		# line, usually by GRUB, but it cannot be referenced here
797eda14cbcSMatt Macy		# directly because bourne variable names cannot contain a
798eda14cbcSMatt Macy		# hyphen.
799eda14cbcSMatt Macy		#
800eda14cbcSMatt Macy		# Reassign the variable by dumping the environment and
801eda14cbcSMatt Macy		# stripping the zfs-bootfs= prefix.  Let the shell handle
80216038816SMartin Matuska		# quoting through the eval command:
80316038816SMartin Matuska		# shellcheck disable=SC2046
804eda14cbcSMatt Macy		eval ZFS_RPOOL=$(set | sed -n -e 's,^zfs-bootfs=,,p')
805eda14cbcSMatt Macy	fi
806eda14cbcSMatt Macy
807eda14cbcSMatt Macy	# ------------
808eda14cbcSMatt Macy	# No root fs or pool specified - do auto detect.
809c40487d4SMatt Macy	if [ -z "$ZFS_RPOOL" ] && [ -z "${ZFS_BOOTFS}" ]
810eda14cbcSMatt Macy	then
811eda14cbcSMatt Macy		# Do auto detect. Do this by 'cheating' - set 'root=zfs:AUTO'
812eda14cbcSMatt Macy		# which will be caught later
813c40487d4SMatt Macy		ROOT='zfs:AUTO'
814eda14cbcSMatt Macy	fi
815eda14cbcSMatt Macy
816eda14cbcSMatt Macy	# ----------------------------------------------------------------
817eda14cbcSMatt 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
818eda14cbcSMatt Macy
819eda14cbcSMatt Macy	# ------------
820eda14cbcSMatt Macy	if [ "$ROOT" = "zfs:AUTO" ]
821eda14cbcSMatt Macy	then
822eda14cbcSMatt Macy		# Try to detect both pool and root fs.
823eda14cbcSMatt Macy
82416038816SMartin Matuska		# If we got here, that means we don't have a hint so as to
82516038816SMartin Matuska		# the root dataset, but with root=zfs:AUTO on cmdline,
82616038816SMartin Matuska		# this says "zfs:AUTO" here and interferes with checks later
82716038816SMartin Matuska		ZFS_BOOTFS=
82816038816SMartin Matuska
829eda14cbcSMatt Macy		[ "$quiet" != "y" ] && \
830eda14cbcSMatt Macy		    zfs_log_begin_msg "Attempting to import additional pools."
831eda14cbcSMatt Macy
832eda14cbcSMatt Macy		# Get a list of pools available for import
833eda14cbcSMatt Macy		if [ -n "$ZFS_RPOOL" ]
834eda14cbcSMatt Macy		then
835eda14cbcSMatt Macy			# We've specified a pool - check only that
836eda14cbcSMatt Macy			POOLS=$ZFS_RPOOL
837eda14cbcSMatt Macy		else
838eda14cbcSMatt Macy			POOLS=$(get_pools)
839eda14cbcSMatt Macy		fi
840eda14cbcSMatt Macy
841eda14cbcSMatt Macy		OLD_IFS="$IFS" ; IFS=";"
842eda14cbcSMatt Macy		for pool in $POOLS
843eda14cbcSMatt Macy		do
844eda14cbcSMatt Macy			[ -z "$pool" ] && continue
845eda14cbcSMatt Macy
84616038816SMartin Matuska			IFS="$OLD_IFS" import_pool "$pool"
84716038816SMartin Matuska			IFS="$OLD_IFS" find_rootfs "$pool" && break
848eda14cbcSMatt Macy		done
849eda14cbcSMatt Macy		IFS="$OLD_IFS"
850eda14cbcSMatt Macy
851eda14cbcSMatt Macy		[ "$quiet" != "y" ] && zfs_log_end_msg $ZFS_ERROR
852eda14cbcSMatt Macy	else
853eda14cbcSMatt Macy		# No auto - use value from the command line option.
854eda14cbcSMatt Macy
855eda14cbcSMatt Macy		# Strip 'zfs:' and 'ZFS='.
856eda14cbcSMatt Macy		ZFS_BOOTFS="${ROOT#*[:=]}"
857eda14cbcSMatt Macy
858eda14cbcSMatt Macy		# Strip everything after the first slash.
859eda14cbcSMatt Macy		ZFS_RPOOL="${ZFS_BOOTFS%%/*}"
860eda14cbcSMatt Macy	fi
861eda14cbcSMatt Macy
862eda14cbcSMatt Macy	# Import the pool (if not already done so in the AUTO check above).
863c40487d4SMatt Macy	if [ -n "$ZFS_RPOOL" ] && [ -z "${POOL_IMPORTED}" ]
864eda14cbcSMatt Macy	then
865eda14cbcSMatt Macy		[ "$quiet" != "y" ] && \
866eda14cbcSMatt Macy		    zfs_log_begin_msg "Importing ZFS root pool '$ZFS_RPOOL'"
867eda14cbcSMatt Macy
868eda14cbcSMatt Macy		import_pool "${ZFS_RPOOL}"
869eda14cbcSMatt Macy		find_rootfs "${ZFS_RPOOL}"
870eda14cbcSMatt Macy
871eda14cbcSMatt Macy		[ "$quiet" != "y" ] && zfs_log_end_msg
872eda14cbcSMatt Macy	fi
873eda14cbcSMatt Macy
874eda14cbcSMatt Macy	if [ -z "${POOL_IMPORTED}" ]
875eda14cbcSMatt Macy	then
876eda14cbcSMatt Macy		# No pool imported, this is serious!
877eda14cbcSMatt Macy		disable_plymouth
878eda14cbcSMatt Macy		echo ""
879eda14cbcSMatt Macy		echo "Command: $ZFS_CMD"
880eda14cbcSMatt Macy		echo "Message: $ZFS_STDERR"
881eda14cbcSMatt Macy		echo "Error: $ZFS_ERROR"
882eda14cbcSMatt Macy		echo ""
883eda14cbcSMatt Macy		echo "No pool imported. Manually import the root pool"
884eda14cbcSMatt Macy		echo "at the command prompt and then exit."
88516038816SMartin Matuska		echo "Hint: Try:  zpool import -N ${ZFS_RPOOL}"
886eda14cbcSMatt Macy		shell
887eda14cbcSMatt Macy	fi
888eda14cbcSMatt Macy
889eda14cbcSMatt Macy	# In case the pool was specified as guid, resolve guid to name
890eda14cbcSMatt Macy	pool="$("${ZPOOL}" get name,guid -o name,value -H | \
891eda14cbcSMatt Macy	    awk -v pool="${ZFS_RPOOL}" '$2 == pool { print $1 }')"
892eda14cbcSMatt Macy	if [ -n "$pool" ]; then
893eda14cbcSMatt Macy		# If $ZFS_BOOTFS contains guid, replace the guid portion with $pool
894eda14cbcSMatt Macy		ZFS_BOOTFS=$(echo "$ZFS_BOOTFS" | \
895eda14cbcSMatt Macy			sed -e "s/$("${ZPOOL}" get guid -o value "$pool" -H)/$pool/g")
896eda14cbcSMatt Macy		ZFS_RPOOL="${pool}"
897eda14cbcSMatt Macy	fi
898eda14cbcSMatt Macy
899eda14cbcSMatt Macy
900eda14cbcSMatt Macy	# ----------------------------------------------------------------
901eda14cbcSMatt Macy	# P R E P A R E   R O O T   F I L E S Y S T E M
902eda14cbcSMatt Macy
903eda14cbcSMatt Macy	if [ -n "${ZFS_BOOTFS}" ]
904eda14cbcSMatt Macy	then
905eda14cbcSMatt Macy		# Booting from a snapshot?
906eda14cbcSMatt Macy		# Will overwrite the ZFS_BOOTFS variable like so:
907eda14cbcSMatt Macy		#   rpool/ROOT/debian@snap2 => rpool/ROOT/debian_snap2
908eda14cbcSMatt Macy		echo "${ZFS_BOOTFS}" | grep -q '@' && \
909eda14cbcSMatt Macy		    setup_snapshot_booting "${ZFS_BOOTFS}"
910eda14cbcSMatt Macy	fi
911eda14cbcSMatt Macy
912eda14cbcSMatt Macy	if [ -z "${ZFS_BOOTFS}" ]
913eda14cbcSMatt Macy	then
914eda14cbcSMatt Macy		# Still nothing! Let the user sort this out.
915eda14cbcSMatt Macy		disable_plymouth
916eda14cbcSMatt Macy		echo ""
917eda14cbcSMatt Macy		echo "Error: Unknown root filesystem - no 'bootfs' pool property and"
918eda14cbcSMatt Macy		echo "       not specified on the kernel command line."
919eda14cbcSMatt Macy		echo ""
920eda14cbcSMatt Macy		echo "Manually mount the root filesystem on $rootmnt and then exit."
921eda14cbcSMatt Macy		echo "Hint: Try:  mount -o zfsutil -t zfs ${ZFS_RPOOL-rpool}/ROOT/system $rootmnt"
922eda14cbcSMatt Macy		shell
923eda14cbcSMatt Macy	fi
924eda14cbcSMatt Macy
925eda14cbcSMatt Macy	# ----------------------------------------------------------------
926eda14cbcSMatt Macy	# M O U N T   F I L E S Y S T E M S
927eda14cbcSMatt Macy
928eda14cbcSMatt Macy	# * Ideally, the root filesystem would be mounted like this:
929eda14cbcSMatt Macy	#
930eda14cbcSMatt Macy	#     zpool import -R "$rootmnt" -N "$ZFS_RPOOL"
931eda14cbcSMatt Macy	#     zfs mount -o mountpoint=/ "${ZFS_BOOTFS}"
932eda14cbcSMatt Macy	#
933eda14cbcSMatt Macy	#   but the MOUNTPOINT prefix is preserved on descendent filesystem
934eda14cbcSMatt Macy	#   after the pivot into the regular root, which later breaks things
935eda14cbcSMatt Macy	#   like `zfs mount -a` and the /proc/self/mounts refresh.
936eda14cbcSMatt Macy	#
937eda14cbcSMatt Macy	# * Mount additional filesystems required
938eda14cbcSMatt Macy	#   Such as /usr, /var, /usr/local etc.
939eda14cbcSMatt Macy	#   NOTE: Mounted in the order specified in the
940eda14cbcSMatt Macy	#         ZFS_INITRD_ADDITIONAL_DATASETS variable so take care!
941eda14cbcSMatt Macy
942eda14cbcSMatt Macy	# Go through the complete list (recursively) of all filesystems below
943eda14cbcSMatt Macy	# the real root dataset
94416038816SMartin Matuska	filesystems="$("${ZFS}" list -oname -tfilesystem -H -r "${ZFS_BOOTFS}")"
94516038816SMartin Matuska	OLD_IFS="$IFS" ; IFS="
94616038816SMartin Matuska"
94716038816SMartin Matuska	for fs in $filesystems; do
94816038816SMartin Matuska		IFS="$OLD_IFS" mount_fs "$fs"
94916038816SMartin Matuska	done
95016038816SMartin Matuska	IFS="$OLD_IFS"
95116038816SMartin Matuska	for fs in $ZFS_INITRD_ADDITIONAL_DATASETS; do
952eda14cbcSMatt Macy		mount_fs "$fs"
953eda14cbcSMatt Macy	done
954eda14cbcSMatt Macy
955eda14cbcSMatt Macy	touch /run/zfs_unlock_complete
956eda14cbcSMatt Macy	if [ -e /run/zfs_unlock_complete_notify ]; then
95716038816SMartin Matuska		read -r < /run/zfs_unlock_complete_notify
958eda14cbcSMatt Macy	fi
959eda14cbcSMatt Macy
960eda14cbcSMatt Macy	# ------------
961eda14cbcSMatt Macy	# Debugging information
962eda14cbcSMatt Macy	if [ -n "${ZFS_DEBUG}" ]
963eda14cbcSMatt Macy	then
964eda14cbcSMatt Macy		#exec 2>&1-
965eda14cbcSMatt Macy
966eda14cbcSMatt Macy		echo "DEBUG: imported pools:"
967eda14cbcSMatt Macy		"${ZPOOL}" list -H
968eda14cbcSMatt Macy		echo
969eda14cbcSMatt Macy
970eda14cbcSMatt Macy		echo "DEBUG: mounted ZFS filesystems:"
971eda14cbcSMatt Macy		mount | grep zfs
972eda14cbcSMatt Macy		echo
973eda14cbcSMatt Macy
974eda14cbcSMatt Macy		echo "=> waiting for ENTER before continuing because of 'zfsdebug=1'. "
975c40487d4SMatt Macy		printf "%s" "   'c' for shell, 'r' for reboot, 'ENTER' to continue. "
976c40487d4SMatt Macy		read -r b
977eda14cbcSMatt Macy
978eda14cbcSMatt Macy		[ "$b" = "c" ] && /bin/sh
979eda14cbcSMatt Macy		[ "$b" = "r" ] && reboot -f
980eda14cbcSMatt Macy
981eda14cbcSMatt Macy		set +x
982eda14cbcSMatt Macy	fi
983eda14cbcSMatt Macy
984eda14cbcSMatt Macy	# ------------
985eda14cbcSMatt Macy	# Run local bottom script
986c40487d4SMatt Macy	if command -v run_scripts > /dev/null 2>&1
987c40487d4SMatt Macy	then
988c40487d4SMatt Macy		if [ -f "/scripts/local-bottom" ] || [ -d "/scripts/local-bottom" ]
989eda14cbcSMatt Macy		then
990eda14cbcSMatt Macy			[ "$quiet" != "y" ] && \
991eda14cbcSMatt Macy			    zfs_log_begin_msg "Running /scripts/local-bottom"
992eda14cbcSMatt Macy			run_scripts /scripts/local-bottom
993eda14cbcSMatt Macy			[ "$quiet" != "y" ] && zfs_log_end_msg
994eda14cbcSMatt Macy		fi
995c40487d4SMatt Macy	fi
996eda14cbcSMatt Macy}
997