xref: /freebsd/sys/contrib/openzfs/contrib/initramfs/scripts/zfs (revision 6ba2210ee039f2f12878c217bcf058e9c8b26b29)
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.
82eda14cbcSMatt Macy	[ -n "${ZFS_BOOTFS}" ] && return 0
83eda14cbcSMatt Macy
84eda14cbcSMatt Macy	# Not set, try to find it in the 'bootfs' property of the pool.
85eda14cbcSMatt Macy	# NOTE: zpool does not support 'get -H -ovalue bootfs'...
86eda14cbcSMatt Macy	ZFS_BOOTFS=$("${ZPOOL}" list -H -obootfs "$pool")
87eda14cbcSMatt Macy
88eda14cbcSMatt Macy	# Make sure it's not '-' and that it starts with /.
89eda14cbcSMatt Macy	if [ "${ZFS_BOOTFS}" != "-" ] && \
90c40487d4SMatt Macy		get_fs_value "${ZFS_BOOTFS}" mountpoint | grep -q '^/$'
91eda14cbcSMatt Macy	then
92eda14cbcSMatt Macy		# Keep it mounted
93eda14cbcSMatt Macy		POOL_IMPORTED=1
94eda14cbcSMatt Macy		return 0
95eda14cbcSMatt Macy	fi
96eda14cbcSMatt Macy
97eda14cbcSMatt Macy	# Not boot fs here, export it and later try again..
98eda14cbcSMatt Macy	"${ZPOOL}" export "$pool"
9916038816SMartin Matuska	POOL_IMPORTED=
10016038816SMartin Matuska	ZFS_BOOTFS=
101eda14cbcSMatt Macy	return 1
102eda14cbcSMatt Macy}
103eda14cbcSMatt Macy
104eda14cbcSMatt Macy# Support function to get a list of all pools, separated with ';'
105eda14cbcSMatt Macyfind_pools()
106eda14cbcSMatt Macy{
10716038816SMartin Matuska	pools=$("$@" 2> /dev/null | \
108eda14cbcSMatt Macy		grep -E "pool:|^[a-zA-Z0-9]" | \
109eda14cbcSMatt Macy		sed 's@.*: @@' | \
11016038816SMartin Matuska		tr '\n' ';')
111eda14cbcSMatt Macy
112eda14cbcSMatt Macy	echo "${pools%%;}" # Return without the last ';'.
113eda14cbcSMatt Macy}
114eda14cbcSMatt Macy
115eda14cbcSMatt Macy# Get a list of all available pools
116eda14cbcSMatt Macyget_pools()
117eda14cbcSMatt Macy{
118eda14cbcSMatt Macy	if [ -n "${ZFS_POOL_IMPORT}" ]; then
119eda14cbcSMatt Macy		echo "$ZFS_POOL_IMPORT"
120eda14cbcSMatt Macy		return 0
121eda14cbcSMatt Macy	fi
122eda14cbcSMatt Macy
123eda14cbcSMatt Macy	# Get the base list of available pools.
124eda14cbcSMatt Macy	available_pools=$(find_pools "$ZPOOL" import)
125eda14cbcSMatt Macy
126eda14cbcSMatt Macy	# Just in case - seen it happen (that a pool isn't visible/found
127eda14cbcSMatt Macy	# with a simple "zpool import" but only when using the "-d"
128eda14cbcSMatt Macy	# option or setting ZPOOL_IMPORT_PATH).
129eda14cbcSMatt Macy	if [ -d "/dev/disk/by-id" ]
130eda14cbcSMatt Macy	then
131eda14cbcSMatt Macy		npools=$(find_pools "$ZPOOL" import -d /dev/disk/by-id)
132eda14cbcSMatt Macy		if [ -n "$npools" ]
133eda14cbcSMatt Macy		then
134eda14cbcSMatt Macy			# Because we have found extra pool(s) here, which wasn't
135eda14cbcSMatt Macy			# found 'normally', we need to force USE_DISK_BY_ID to
136eda14cbcSMatt Macy			# make sure we're able to actually import it/them later.
137eda14cbcSMatt Macy			USE_DISK_BY_ID='yes'
138eda14cbcSMatt Macy
139eda14cbcSMatt Macy			if [ -n "$available_pools" ]
140eda14cbcSMatt Macy			then
141eda14cbcSMatt Macy				# Filter out duplicates (pools found with the simple
142eda14cbcSMatt Macy				# "zpool import" but which is also found with the
143eda14cbcSMatt Macy				# "zpool import -d ...").
144eda14cbcSMatt Macy				npools=$(echo "$npools" | sed "s,$available_pools,,")
145eda14cbcSMatt Macy
146eda14cbcSMatt Macy				# Add the list to the existing list of
147eda14cbcSMatt Macy				# available pools
148eda14cbcSMatt Macy				available_pools="$available_pools;$npools"
149eda14cbcSMatt Macy			else
150eda14cbcSMatt Macy				available_pools="$npools"
151eda14cbcSMatt Macy			fi
152eda14cbcSMatt Macy		fi
153eda14cbcSMatt Macy	fi
154eda14cbcSMatt Macy
155eda14cbcSMatt Macy	# Filter out any exceptions...
156eda14cbcSMatt Macy	if [ -n "$ZFS_POOL_EXCEPTIONS" ]
157eda14cbcSMatt Macy	then
158c40487d4SMatt Macy		found=""
159c40487d4SMatt Macy		apools=""
160eda14cbcSMatt Macy		OLD_IFS="$IFS" ; IFS=";"
161eda14cbcSMatt Macy
162eda14cbcSMatt Macy		for pool in $available_pools
163eda14cbcSMatt Macy		do
164eda14cbcSMatt Macy			for exception in $ZFS_POOL_EXCEPTIONS
165eda14cbcSMatt Macy			do
166eda14cbcSMatt Macy				[ "$pool" = "$exception" ] && continue 2
167eda14cbcSMatt Macy				found="$pool"
168eda14cbcSMatt Macy			done
169eda14cbcSMatt Macy
170eda14cbcSMatt Macy			if [ -n "$found" ]
171eda14cbcSMatt Macy			then
172eda14cbcSMatt Macy				if [ -n "$apools" ]
173eda14cbcSMatt Macy				then
174eda14cbcSMatt Macy					apools="$apools;$pool"
175eda14cbcSMatt Macy				else
176eda14cbcSMatt Macy					apools="$pool"
177eda14cbcSMatt Macy				fi
178eda14cbcSMatt Macy			fi
179eda14cbcSMatt Macy		done
180eda14cbcSMatt Macy
181eda14cbcSMatt Macy		IFS="$OLD_IFS"
182eda14cbcSMatt Macy		available_pools="$apools"
183eda14cbcSMatt Macy	fi
184eda14cbcSMatt Macy
185eda14cbcSMatt Macy	# Return list of available pools.
186eda14cbcSMatt Macy	echo "$available_pools"
187eda14cbcSMatt Macy}
188eda14cbcSMatt Macy
189eda14cbcSMatt Macy# Import given pool $1
190eda14cbcSMatt Macyimport_pool()
191eda14cbcSMatt Macy{
192c40487d4SMatt Macy	pool="$1"
193eda14cbcSMatt Macy
194eda14cbcSMatt Macy	# Verify that the pool isn't already imported
195eda14cbcSMatt Macy	# Make as sure as we can to not require '-f' to import.
196eda14cbcSMatt Macy	"${ZPOOL}" get name,guid -o value -H 2>/dev/null | grep -Fxq "$pool" && return 0
197eda14cbcSMatt Macy
198eda14cbcSMatt Macy	# For backwards compatibility, make sure that ZPOOL_IMPORT_PATH is set
199eda14cbcSMatt Macy	# to something we can use later with the real import(s). We want to
200eda14cbcSMatt Macy	# make sure we find all by* dirs, BUT by-vdev should be first (if it
201eda14cbcSMatt Macy	# exists).
202c40487d4SMatt Macy	if [ -n "$USE_DISK_BY_ID" ] && [ -z "$ZPOOL_IMPORT_PATH" ]
203eda14cbcSMatt Macy	then
20416038816SMartin Matuska		dirs="$(for dir in /dev/disk/by-*
205eda14cbcSMatt Macy		do
206eda14cbcSMatt Macy			# Ignore by-vdev here - we want it first!
207eda14cbcSMatt Macy			echo "$dir" | grep -q /by-vdev && continue
208eda14cbcSMatt Macy			[ ! -d "$dir" ] && continue
209eda14cbcSMatt Macy
210c40487d4SMatt Macy			printf "%s" "$dir:"
211eda14cbcSMatt Macy		done | sed 's,:$,,g')"
212eda14cbcSMatt Macy
213eda14cbcSMatt Macy		if [ -d "/dev/disk/by-vdev" ]
214eda14cbcSMatt Macy		then
215eda14cbcSMatt Macy			# Add by-vdev at the beginning.
216eda14cbcSMatt Macy			ZPOOL_IMPORT_PATH="/dev/disk/by-vdev:"
217eda14cbcSMatt Macy		fi
218eda14cbcSMatt Macy
219eda14cbcSMatt Macy		# ... and /dev at the very end, just for good measure.
220eda14cbcSMatt Macy		ZPOOL_IMPORT_PATH="$ZPOOL_IMPORT_PATH$dirs:/dev"
221eda14cbcSMatt Macy	fi
222eda14cbcSMatt Macy
223eda14cbcSMatt Macy	# Needs to be exported for "zpool" to catch it.
224eda14cbcSMatt Macy	[ -n "$ZPOOL_IMPORT_PATH" ] && export ZPOOL_IMPORT_PATH
225eda14cbcSMatt Macy
226eda14cbcSMatt Macy
227eda14cbcSMatt Macy	[ "$quiet" != "y" ] && zfs_log_begin_msg \
228eda14cbcSMatt Macy		"Importing pool '${pool}' using defaults"
229eda14cbcSMatt Macy
230eda14cbcSMatt Macy	ZFS_CMD="${ZPOOL} import -N ${ZPOOL_FORCE} ${ZPOOL_IMPORT_OPTS}"
231eda14cbcSMatt Macy	ZFS_STDERR="$($ZFS_CMD "$pool" 2>&1)"
232eda14cbcSMatt Macy	ZFS_ERROR="$?"
233eda14cbcSMatt Macy	if [ "${ZFS_ERROR}" != 0 ]
234eda14cbcSMatt Macy	then
235eda14cbcSMatt Macy		[ "$quiet" != "y" ] && zfs_log_failure_msg "${ZFS_ERROR}"
236eda14cbcSMatt Macy
237eda14cbcSMatt Macy		if [ -f "${ZPOOL_CACHE}" ]
238eda14cbcSMatt Macy		then
239eda14cbcSMatt Macy			[ "$quiet" != "y" ] && zfs_log_begin_msg \
240eda14cbcSMatt Macy				"Importing pool '${pool}' using cachefile."
241eda14cbcSMatt Macy
242eda14cbcSMatt Macy			ZFS_CMD="${ZPOOL} import -c ${ZPOOL_CACHE} -N ${ZPOOL_FORCE} ${ZPOOL_IMPORT_OPTS}"
243eda14cbcSMatt Macy			ZFS_STDERR="$($ZFS_CMD "$pool" 2>&1)"
244eda14cbcSMatt Macy			ZFS_ERROR="$?"
245eda14cbcSMatt Macy		fi
246eda14cbcSMatt Macy
247eda14cbcSMatt Macy		if [ "${ZFS_ERROR}" != 0 ]
248eda14cbcSMatt Macy		then
249eda14cbcSMatt Macy			[ "$quiet" != "y" ] && zfs_log_failure_msg "${ZFS_ERROR}"
250eda14cbcSMatt Macy
251eda14cbcSMatt Macy			disable_plymouth
252eda14cbcSMatt Macy			echo ""
253eda14cbcSMatt Macy			echo "Command: ${ZFS_CMD} '$pool'"
254eda14cbcSMatt Macy			echo "Message: $ZFS_STDERR"
255eda14cbcSMatt Macy			echo "Error: $ZFS_ERROR"
256eda14cbcSMatt Macy			echo ""
257eda14cbcSMatt Macy			echo "Failed to import pool '$pool'."
258eda14cbcSMatt Macy			echo "Manually import the pool and exit."
259eda14cbcSMatt Macy			shell
260eda14cbcSMatt Macy		fi
261eda14cbcSMatt Macy	fi
262eda14cbcSMatt Macy
263eda14cbcSMatt Macy	[ "$quiet" != "y" ] && zfs_log_end_msg
264eda14cbcSMatt Macy
265eda14cbcSMatt Macy	POOL_IMPORTED=1
266eda14cbcSMatt Macy	return 0
267eda14cbcSMatt Macy}
268eda14cbcSMatt Macy
269eda14cbcSMatt Macy# Load ZFS modules
270eda14cbcSMatt Macy# Loading a module in a initrd require a slightly different approach,
271eda14cbcSMatt Macy# with more logging etc.
272eda14cbcSMatt Macyload_module_initrd()
273eda14cbcSMatt Macy{
27416038816SMartin Matuska	[ -n "$ROOTDELAY" ] && ZFS_INITRD_PRE_MOUNTROOT_SLEEP="$ROOTDELAY"
27516038816SMartin Matuska
276c40487d4SMatt Macy	if [ "$ZFS_INITRD_PRE_MOUNTROOT_SLEEP" -gt 0 ] 2>/dev/null
277eda14cbcSMatt Macy	then
278eda14cbcSMatt Macy		if [ "$quiet" != "y" ]; then
279eda14cbcSMatt Macy			zfs_log_begin_msg "Sleeping for" \
280eda14cbcSMatt Macy				"$ZFS_INITRD_PRE_MOUNTROOT_SLEEP seconds..."
281eda14cbcSMatt Macy		fi
282eda14cbcSMatt Macy		sleep "$ZFS_INITRD_PRE_MOUNTROOT_SLEEP"
283eda14cbcSMatt Macy		[ "$quiet" != "y" ] && zfs_log_end_msg
284eda14cbcSMatt Macy	fi
285eda14cbcSMatt Macy
286eda14cbcSMatt Macy	# Wait for all of the /dev/{hd,sd}[a-z] device nodes to appear.
287c40487d4SMatt Macy	if command -v wait_for_udev > /dev/null 2>&1 ; then
288eda14cbcSMatt Macy		wait_for_udev 10
289c40487d4SMatt Macy	elif command -v wait_for_dev > /dev/null 2>&1 ; then
290eda14cbcSMatt Macy		wait_for_dev
291eda14cbcSMatt Macy	fi
292eda14cbcSMatt Macy
293eda14cbcSMatt Macy	# zpool import refuse to import without a valid /proc/self/mounts
294eda14cbcSMatt Macy	[ ! -f /proc/self/mounts ] && mount proc /proc
295eda14cbcSMatt Macy
296eda14cbcSMatt Macy	# Load the module
297eda14cbcSMatt Macy	load_module "zfs" || return 1
298eda14cbcSMatt Macy
299c40487d4SMatt Macy	if [ "$ZFS_INITRD_POST_MODPROBE_SLEEP" -gt 0 ] 2>/dev/null
300eda14cbcSMatt Macy	then
301eda14cbcSMatt Macy		if [ "$quiet" != "y" ]; then
302eda14cbcSMatt Macy			zfs_log_begin_msg "Sleeping for" \
303eda14cbcSMatt Macy				"$ZFS_INITRD_POST_MODPROBE_SLEEP seconds..."
304eda14cbcSMatt Macy		fi
305eda14cbcSMatt Macy		sleep "$ZFS_INITRD_POST_MODPROBE_SLEEP"
306eda14cbcSMatt Macy		[ "$quiet" != "y" ] && zfs_log_end_msg
307eda14cbcSMatt Macy	fi
308eda14cbcSMatt Macy
309eda14cbcSMatt Macy	return 0
310eda14cbcSMatt Macy}
311eda14cbcSMatt Macy
312eda14cbcSMatt Macy# Mount a given filesystem
313eda14cbcSMatt Macymount_fs()
314eda14cbcSMatt Macy{
315c40487d4SMatt Macy	fs="$1"
316eda14cbcSMatt Macy
317eda14cbcSMatt Macy	# Check that the filesystem exists
318c40487d4SMatt Macy	"${ZFS}" list -oname -tfilesystem -H "${fs}" > /dev/null 2>&1 ||  return 1
319eda14cbcSMatt Macy
320eda14cbcSMatt Macy	# Skip filesystems with canmount=off.  The root fs should not have
321eda14cbcSMatt Macy	# canmount=off, but ignore it for backwards compatibility just in case.
322eda14cbcSMatt Macy	if [ "$fs" != "${ZFS_BOOTFS}" ]
323eda14cbcSMatt Macy	then
324eda14cbcSMatt Macy		canmount=$(get_fs_value "$fs" canmount)
325eda14cbcSMatt Macy		[ "$canmount" = "off" ] && return 0
326eda14cbcSMatt Macy	fi
327eda14cbcSMatt Macy
328eda14cbcSMatt Macy	# Need the _original_ datasets mountpoint!
329eda14cbcSMatt Macy	mountpoint=$(get_fs_value "$fs" mountpoint)
33016038816SMartin Matuska	ZFS_CMD="mount -o zfsutil -t zfs"
331c40487d4SMatt Macy	if [ "$mountpoint" = "legacy" ] || [ "$mountpoint" = "none" ]; then
332eda14cbcSMatt Macy		# Can't use the mountpoint property. Might be one of our
333eda14cbcSMatt Macy		# clones. Check the 'org.zol:mountpoint' property set in
334eda14cbcSMatt Macy		# clone_snap() if that's usable.
335eda14cbcSMatt Macy		mountpoint=$(get_fs_value "$fs" org.zol:mountpoint)
336c40487d4SMatt Macy		if [ "$mountpoint" = "legacy" ] ||
337c40487d4SMatt Macy		   [ "$mountpoint" = "none" ] ||
338c40487d4SMatt Macy		   [ "$mountpoint" = "-" ]
339eda14cbcSMatt Macy		then
340eda14cbcSMatt Macy			if [ "$fs" != "${ZFS_BOOTFS}" ]; then
341eda14cbcSMatt Macy				# We don't have a proper mountpoint and this
342eda14cbcSMatt Macy				# isn't the root fs.
343eda14cbcSMatt Macy				return 0
344eda14cbcSMatt Macy			else
345eda14cbcSMatt Macy				# Last hail-mary: Hope 'rootmnt' is set!
346eda14cbcSMatt Macy				mountpoint=""
347eda14cbcSMatt Macy			fi
348eda14cbcSMatt Macy		fi
349eda14cbcSMatt Macy
350eda14cbcSMatt Macy		# If it's not a legacy filesystem, it can only be a
351eda14cbcSMatt Macy		# native one...
35216038816SMartin Matuska		if [ "$mountpoint" = "legacy" ]; then
35316038816SMartin Matuska			ZFS_CMD="mount -t zfs"
354eda14cbcSMatt Macy		fi
355eda14cbcSMatt Macy	fi
356eda14cbcSMatt Macy
357eda14cbcSMatt Macy	# Possibly decrypt a filesystem using native encryption.
358eda14cbcSMatt Macy	decrypt_fs "$fs"
359eda14cbcSMatt Macy
360eda14cbcSMatt Macy	[ "$quiet" != "y" ] && \
361eda14cbcSMatt Macy	    zfs_log_begin_msg "Mounting '${fs}' on '${rootmnt}/${mountpoint}'"
362eda14cbcSMatt Macy	[ -n "${ZFS_DEBUG}" ] && \
363eda14cbcSMatt Macy	    zfs_log_begin_msg "CMD: '$ZFS_CMD ${fs} ${rootmnt}/${mountpoint}'"
364eda14cbcSMatt Macy
365eda14cbcSMatt Macy	ZFS_STDERR=$(${ZFS_CMD} "${fs}" "${rootmnt}/${mountpoint}" 2>&1)
366eda14cbcSMatt Macy	ZFS_ERROR=$?
367eda14cbcSMatt Macy	if [ "${ZFS_ERROR}" != 0 ]
368eda14cbcSMatt Macy	then
369eda14cbcSMatt Macy		[ "$quiet" != "y" ] && zfs_log_failure_msg "${ZFS_ERROR}"
370eda14cbcSMatt Macy
371eda14cbcSMatt Macy		disable_plymouth
372eda14cbcSMatt Macy		echo ""
373eda14cbcSMatt Macy		echo "Command: ${ZFS_CMD} ${fs} ${rootmnt}/${mountpoint}"
374eda14cbcSMatt Macy		echo "Message: $ZFS_STDERR"
375eda14cbcSMatt Macy		echo "Error: $ZFS_ERROR"
376eda14cbcSMatt Macy		echo ""
377eda14cbcSMatt Macy		echo "Failed to mount ${fs} on ${rootmnt}/${mountpoint}."
378eda14cbcSMatt Macy		echo "Manually mount the filesystem and exit."
379eda14cbcSMatt Macy		shell
380eda14cbcSMatt Macy	else
381eda14cbcSMatt Macy		[ "$quiet" != "y" ] && zfs_log_end_msg
382eda14cbcSMatt Macy	fi
383eda14cbcSMatt Macy
384eda14cbcSMatt Macy	return 0
385eda14cbcSMatt Macy}
386eda14cbcSMatt Macy
387eda14cbcSMatt Macy# Unlock a ZFS native encrypted filesystem.
388eda14cbcSMatt Macydecrypt_fs()
389eda14cbcSMatt Macy{
390c40487d4SMatt Macy	fs="$1"
391eda14cbcSMatt Macy
392eda14cbcSMatt Macy	# If pool encryption is active and the zfs command understands '-o encryption'
39316038816SMartin Matuska	if [ "$(zpool list -H -o feature@encryption "${fs%%/*}")" = 'active' ]; then
394eda14cbcSMatt Macy
395eda14cbcSMatt Macy		# Determine dataset that holds key for root dataset
396eda14cbcSMatt Macy		ENCRYPTIONROOT="$(get_fs_value "${fs}" encryptionroot)"
397eda14cbcSMatt Macy		KEYLOCATION="$(get_fs_value "${ENCRYPTIONROOT}" keylocation)"
398eda14cbcSMatt Macy
399eda14cbcSMatt Macy		echo "${ENCRYPTIONROOT}" > /run/zfs_fs_name
400eda14cbcSMatt Macy
401eda14cbcSMatt Macy		# If root dataset is encrypted...
402eda14cbcSMatt Macy		if ! [ "${ENCRYPTIONROOT}" = "-" ]; then
403eda14cbcSMatt Macy			KEYSTATUS="$(get_fs_value "${ENCRYPTIONROOT}" keystatus)"
404eda14cbcSMatt Macy			# Continue only if the key needs to be loaded
405eda14cbcSMatt Macy			[ "$KEYSTATUS" = "unavailable" ] || return 0
406eda14cbcSMatt Macy
40716038816SMartin Matuska			# Do not prompt if key is stored noninteractively,
408eda14cbcSMatt Macy			if ! [ "${KEYLOCATION}" = "prompt" ]; then
409eda14cbcSMatt Macy				$ZFS load-key "${ENCRYPTIONROOT}"
410eda14cbcSMatt Macy
411eda14cbcSMatt Macy			# Prompt with plymouth, if active
41216038816SMartin Matuska			elif /bin/plymouth --ping 2>/dev/null; then
413eda14cbcSMatt Macy				echo "plymouth" > /run/zfs_console_askpwd_cmd
41416038816SMartin Matuska				for _ in 1 2 3; do
415eda14cbcSMatt Macy					plymouth ask-for-password --prompt "Encrypted ZFS password for ${ENCRYPTIONROOT}" | \
416eda14cbcSMatt Macy						$ZFS load-key "${ENCRYPTIONROOT}" && break
417eda14cbcSMatt Macy				done
418eda14cbcSMatt Macy
419eda14cbcSMatt Macy			# Prompt with systemd, if active
420eda14cbcSMatt Macy			elif [ -e /run/systemd/system ]; then
421eda14cbcSMatt Macy				echo "systemd-ask-password" > /run/zfs_console_askpwd_cmd
42216038816SMartin Matuska				for _ in 1 2 3; do
423eda14cbcSMatt Macy					systemd-ask-password "Encrypted ZFS password for ${ENCRYPTIONROOT}" --no-tty | \
424eda14cbcSMatt Macy						$ZFS load-key "${ENCRYPTIONROOT}" && break
425eda14cbcSMatt Macy				done
426eda14cbcSMatt Macy
427eda14cbcSMatt Macy			# Prompt with ZFS tty, otherwise
428eda14cbcSMatt Macy			else
429eda14cbcSMatt Macy				# Temporarily setting "printk" to "7" allows the prompt to appear even when the "quiet" kernel option has been used
430eda14cbcSMatt Macy				echo "load-key" > /run/zfs_console_askpwd_cmd
431eda14cbcSMatt Macy				storeprintk="$(awk '{print $1}' /proc/sys/kernel/printk)"
432eda14cbcSMatt Macy				echo 7 > /proc/sys/kernel/printk
433eda14cbcSMatt Macy				$ZFS load-key "${ENCRYPTIONROOT}"
434eda14cbcSMatt Macy				echo "$storeprintk" > /proc/sys/kernel/printk
435eda14cbcSMatt Macy			fi
436eda14cbcSMatt Macy		fi
437eda14cbcSMatt Macy	fi
438eda14cbcSMatt Macy
439eda14cbcSMatt Macy	return 0
440eda14cbcSMatt Macy}
441eda14cbcSMatt Macy
442eda14cbcSMatt Macy# Destroy a given filesystem.
443eda14cbcSMatt Macydestroy_fs()
444eda14cbcSMatt Macy{
445c40487d4SMatt Macy	fs="$1"
446eda14cbcSMatt Macy
447eda14cbcSMatt Macy	[ "$quiet" != "y" ] && \
448eda14cbcSMatt Macy	    zfs_log_begin_msg "Destroying '$fs'"
449eda14cbcSMatt Macy
450eda14cbcSMatt Macy	ZFS_CMD="${ZFS} destroy $fs"
451eda14cbcSMatt Macy	ZFS_STDERR="$(${ZFS_CMD} 2>&1)"
452eda14cbcSMatt Macy	ZFS_ERROR="$?"
453eda14cbcSMatt Macy	if [ "${ZFS_ERROR}" != 0 ]
454eda14cbcSMatt Macy	then
455eda14cbcSMatt Macy		[ "$quiet" != "y" ] && zfs_log_failure_msg "${ZFS_ERROR}"
456eda14cbcSMatt Macy
457eda14cbcSMatt Macy		disable_plymouth
458eda14cbcSMatt Macy		echo ""
459eda14cbcSMatt Macy		echo "Command: $ZFS_CMD"
460eda14cbcSMatt Macy		echo "Message: $ZFS_STDERR"
461eda14cbcSMatt Macy		echo "Error: $ZFS_ERROR"
462eda14cbcSMatt Macy		echo ""
463eda14cbcSMatt Macy		echo "Failed to destroy '$fs'. Please make sure that '$fs' is not available."
464eda14cbcSMatt Macy		echo "Hint: Try:  zfs destroy -Rfn $fs"
465eda14cbcSMatt Macy		echo "If this dryrun looks good, then remove the 'n' from '-Rfn' and try again."
466eda14cbcSMatt Macy		shell
467eda14cbcSMatt Macy	else
468eda14cbcSMatt Macy		[ "$quiet" != "y" ] && zfs_log_end_msg
469eda14cbcSMatt Macy	fi
470eda14cbcSMatt Macy
471eda14cbcSMatt Macy	return 0
472eda14cbcSMatt Macy}
473eda14cbcSMatt Macy
474eda14cbcSMatt Macy# Clone snapshot $1 to destination filesystem $2
475eda14cbcSMatt Macy# Set 'canmount=noauto' and 'mountpoint=none' so that we get to keep
476eda14cbcSMatt Macy# manual control over it's mounting (i.e., make sure it's not automatically
477eda14cbcSMatt Macy# mounted with a 'zfs mount -a' in the init/systemd scripts).
478eda14cbcSMatt Macyclone_snap()
479eda14cbcSMatt Macy{
480c40487d4SMatt Macy	snap="$1"
481c40487d4SMatt Macy	destfs="$2"
482c40487d4SMatt Macy	mountpoint="$3"
483eda14cbcSMatt Macy
484eda14cbcSMatt Macy	[ "$quiet" != "y" ] && zfs_log_begin_msg "Cloning '$snap' to '$destfs'"
485eda14cbcSMatt Macy
486eda14cbcSMatt Macy	# Clone the snapshot into a dataset we can boot from
487eda14cbcSMatt Macy	# + We don't want this filesystem to be automatically mounted, we
488eda14cbcSMatt Macy	#   want control over this here and nowhere else.
489eda14cbcSMatt Macy	# + We don't need any mountpoint set for the same reason.
490eda14cbcSMatt Macy	# We use the 'org.zol:mountpoint' property to remember the mountpoint.
491eda14cbcSMatt Macy	ZFS_CMD="${ZFS} clone -o canmount=noauto -o mountpoint=none"
492eda14cbcSMatt Macy	ZFS_CMD="${ZFS_CMD} -o org.zol:mountpoint=${mountpoint}"
493eda14cbcSMatt Macy	ZFS_CMD="${ZFS_CMD} $snap $destfs"
494eda14cbcSMatt Macy	ZFS_STDERR="$(${ZFS_CMD} 2>&1)"
495eda14cbcSMatt Macy	ZFS_ERROR="$?"
496eda14cbcSMatt Macy	if [ "${ZFS_ERROR}" != 0 ]
497eda14cbcSMatt Macy	then
498eda14cbcSMatt Macy		[ "$quiet" != "y" ] && zfs_log_failure_msg "${ZFS_ERROR}"
499eda14cbcSMatt Macy
500eda14cbcSMatt Macy		disable_plymouth
501eda14cbcSMatt Macy		echo ""
502eda14cbcSMatt Macy		echo "Command: $ZFS_CMD"
503eda14cbcSMatt Macy		echo "Message: $ZFS_STDERR"
504eda14cbcSMatt Macy		echo "Error: $ZFS_ERROR"
505eda14cbcSMatt Macy		echo ""
506eda14cbcSMatt Macy		echo "Failed to clone snapshot."
507eda14cbcSMatt Macy		echo "Make sure that the any problems are corrected and then make sure"
508eda14cbcSMatt Macy		echo "that the dataset '$destfs' exists and is bootable."
509eda14cbcSMatt Macy		shell
510eda14cbcSMatt Macy	else
511eda14cbcSMatt Macy		[ "$quiet" != "y" ] && zfs_log_end_msg
512eda14cbcSMatt Macy	fi
513eda14cbcSMatt Macy
514eda14cbcSMatt Macy	return 0
515eda14cbcSMatt Macy}
516eda14cbcSMatt Macy
517eda14cbcSMatt Macy# Rollback a given snapshot.
518eda14cbcSMatt Macyrollback_snap()
519eda14cbcSMatt Macy{
520c40487d4SMatt Macy	snap="$1"
521eda14cbcSMatt Macy
522eda14cbcSMatt Macy	[ "$quiet" != "y" ] && zfs_log_begin_msg "Rollback $snap"
523eda14cbcSMatt Macy
524eda14cbcSMatt Macy	ZFS_CMD="${ZFS} rollback -Rf $snap"
525eda14cbcSMatt Macy	ZFS_STDERR="$(${ZFS_CMD} 2>&1)"
526eda14cbcSMatt Macy	ZFS_ERROR="$?"
527eda14cbcSMatt Macy	if [ "${ZFS_ERROR}" != 0 ]
528eda14cbcSMatt Macy	then
529eda14cbcSMatt Macy		[ "$quiet" != "y" ] && zfs_log_failure_msg "${ZFS_ERROR}"
530eda14cbcSMatt Macy
531eda14cbcSMatt Macy		disable_plymouth
532eda14cbcSMatt Macy		echo ""
533eda14cbcSMatt Macy		echo "Command: $ZFS_CMD"
534eda14cbcSMatt Macy		echo "Message: $ZFS_STDERR"
535eda14cbcSMatt Macy		echo "Error: $ZFS_ERROR"
536eda14cbcSMatt Macy		echo ""
537eda14cbcSMatt Macy		echo "Failed to rollback snapshot."
538eda14cbcSMatt Macy		shell
539eda14cbcSMatt Macy	else
540eda14cbcSMatt Macy		[ "$quiet" != "y" ] && zfs_log_end_msg
541eda14cbcSMatt Macy	fi
542eda14cbcSMatt Macy
543eda14cbcSMatt Macy	return 0
544eda14cbcSMatt Macy}
545eda14cbcSMatt Macy
546eda14cbcSMatt Macy# Get a list of snapshots, give them as a numbered list
547eda14cbcSMatt Macy# to the user to choose from.
548eda14cbcSMatt Macyask_user_snap()
549eda14cbcSMatt Macy{
550c40487d4SMatt Macy	fs="$1"
551eda14cbcSMatt Macy
552eda14cbcSMatt Macy	# We need to temporarily disable debugging. Set 'debug' so we
553eda14cbcSMatt Macy	# remember to enabled it again.
554eda14cbcSMatt Macy	if [ -n "${ZFS_DEBUG}" ]; then
555eda14cbcSMatt Macy		unset ZFS_DEBUG
556eda14cbcSMatt Macy		set +x
557eda14cbcSMatt Macy		debug=1
558eda14cbcSMatt Macy	fi
559eda14cbcSMatt Macy
560eda14cbcSMatt Macy	# Because we need the resulting snapshot, which is sent on
561eda14cbcSMatt Macy	# stdout to the caller, we use stderr for our questions.
562eda14cbcSMatt Macy	echo "What snapshot do you want to boot from?" > /dev/stderr
56316038816SMartin Matuska	# shellcheck disable=SC2046
56416038816SMartin Matuska	IFS="
56516038816SMartin Matuska" set -- $("${ZFS}" list -H -oname -tsnapshot -r "${fs}")
566eda14cbcSMatt Macy
56716038816SMartin Matuska	i=1
56816038816SMartin Matuska	for snap in "$@"; do
56916038816SMartin Matuska		echo "  $i: $snap"
57016038816SMartin Matuska		i=$((i + 1))
57116038816SMartin Matuska	done > /dev/stderr
57216038816SMartin Matuska
57316038816SMartin Matuska	# expr instead of test here because [ a -lt 0 ] errors out,
57416038816SMartin Matuska	# but expr falls back to lexicographical, which works out right
57516038816SMartin Matuska	snapnr=0
57616038816SMartin Matuska	while expr "$snapnr" "<" 1 > /dev/null ||
57716038816SMartin Matuska	    expr "$snapnr" ">" "$#" > /dev/null
57816038816SMartin Matuska	do
57916038816SMartin Matuska		printf "%s" "Snap nr [1-$#]? " > /dev/stderr
580c40487d4SMatt Macy		read -r snapnr
58116038816SMartin Matuska	done
582eda14cbcSMatt Macy
583eda14cbcSMatt Macy	# Re-enable debugging.
584eda14cbcSMatt Macy	if [ -n "${debug}" ]; then
585eda14cbcSMatt Macy		ZFS_DEBUG=1
586eda14cbcSMatt Macy		set -x
587eda14cbcSMatt Macy	fi
588eda14cbcSMatt Macy
58916038816SMartin Matuska	eval echo '$'"$snapnr"
590eda14cbcSMatt Macy}
591eda14cbcSMatt Macy
592eda14cbcSMatt Macysetup_snapshot_booting()
593eda14cbcSMatt Macy{
594c40487d4SMatt Macy	snap="$1"
595c40487d4SMatt Macy	retval=0
596eda14cbcSMatt Macy
597c40487d4SMatt Macy	# Make sure that the snapshot specified actually exists.
598c40487d4SMatt Macy	if [ ! "$(get_fs_value "${snap}" type)" ]
599eda14cbcSMatt Macy	then
600eda14cbcSMatt Macy		# Snapshot does not exist (...@<null> ?)
601eda14cbcSMatt Macy		# ask the user for a snapshot to use.
602eda14cbcSMatt Macy		snap="$(ask_user_snap "${snap%%@*}")"
603eda14cbcSMatt Macy	fi
604eda14cbcSMatt Macy
605eda14cbcSMatt Macy	# Separate the full snapshot ('$snap') into it's filesystem and
606eda14cbcSMatt Macy	# snapshot names. Would have been nice with a split() function..
607eda14cbcSMatt Macy	rootfs="${snap%%@*}"
608eda14cbcSMatt Macy	snapname="${snap##*@}"
609eda14cbcSMatt Macy	ZFS_BOOTFS="${rootfs}_${snapname}"
610eda14cbcSMatt Macy
611eda14cbcSMatt Macy	if ! grep -qiE '(^|[^\\](\\\\)* )(rollback)=(on|yes|1)( |$)' /proc/cmdline
612eda14cbcSMatt Macy	then
613eda14cbcSMatt Macy		# If the destination dataset for the clone
614eda14cbcSMatt Macy		# already exists, destroy it. Recursively
615c40487d4SMatt Macy		if [ "$(get_fs_value "${rootfs}_${snapname}" type)" ]; then
616eda14cbcSMatt Macy			filesystems=$("${ZFS}" list -oname -tfilesystem -H \
617eda14cbcSMatt Macy			    -r -Sname "${ZFS_BOOTFS}")
618eda14cbcSMatt Macy			for fs in $filesystems; do
619eda14cbcSMatt Macy				destroy_fs "${fs}"
620eda14cbcSMatt Macy			done
621eda14cbcSMatt Macy		fi
622eda14cbcSMatt Macy	fi
623eda14cbcSMatt Macy
624eda14cbcSMatt Macy	# Get all snapshots, recursively (might need to clone /usr, /var etc
625eda14cbcSMatt Macy	# as well).
626eda14cbcSMatt Macy	for s in $("${ZFS}" list -H -oname -tsnapshot -r "${rootfs}" | \
627eda14cbcSMatt Macy	    grep "${snapname}")
628eda14cbcSMatt Macy	do
629eda14cbcSMatt Macy		if grep -qiE '(^|[^\\](\\\\)* )(rollback)=(on|yes|1)( |$)' /proc/cmdline
630eda14cbcSMatt Macy		then
631eda14cbcSMatt Macy			# Rollback snapshot
632eda14cbcSMatt Macy			rollback_snap "$s" || retval=$((retval + 1))
633*6ba2210eSMartin Matuska			ZFS_BOOTFS="${rootfs}"
634eda14cbcSMatt Macy		else
635eda14cbcSMatt Macy			# Setup a destination filesystem name.
636eda14cbcSMatt Macy			# Ex: Called with 'rpool/ROOT/debian@snap2'
637eda14cbcSMatt Macy			#       rpool/ROOT/debian@snap2		=> rpool/ROOT/debian_snap2
638eda14cbcSMatt Macy			#       rpool/ROOT/debian/boot@snap2	=> rpool/ROOT/debian_snap2/boot
639eda14cbcSMatt Macy			#       rpool/ROOT/debian/usr@snap2	=> rpool/ROOT/debian_snap2/usr
640eda14cbcSMatt Macy			#       rpool/ROOT/debian/var@snap2	=> rpool/ROOT/debian_snap2/var
641eda14cbcSMatt Macy			subfs="${s##$rootfs}"
642eda14cbcSMatt Macy			subfs="${subfs%%@$snapname}"
643eda14cbcSMatt Macy
644eda14cbcSMatt Macy			destfs="${rootfs}_${snapname}" # base fs.
645eda14cbcSMatt Macy			[ -n "$subfs" ] && destfs="${destfs}$subfs" # + sub fs.
646eda14cbcSMatt Macy
647eda14cbcSMatt Macy			# Get the mountpoint of the filesystem, to be used
648eda14cbcSMatt Macy			# with clone_snap(). If legacy or none, then use
649eda14cbcSMatt Macy			# the sub fs value.
650eda14cbcSMatt Macy			mountpoint=$(get_fs_value "${s%%@*}" mountpoint)
651c40487d4SMatt Macy			if [ "$mountpoint" = "legacy" ] || \
652c40487d4SMatt Macy			   [ "$mountpoint" = "none" ]
653eda14cbcSMatt Macy			then
654eda14cbcSMatt Macy				if [ -n "${subfs}" ]; then
655eda14cbcSMatt Macy					mountpoint="${subfs}"
656eda14cbcSMatt Macy				else
657eda14cbcSMatt Macy					mountpoint="/"
658eda14cbcSMatt Macy				fi
659eda14cbcSMatt Macy			fi
660eda14cbcSMatt Macy
661eda14cbcSMatt Macy			# Clone the snapshot into its own
662eda14cbcSMatt Macy			# filesystem
663eda14cbcSMatt Macy			clone_snap "$s" "${destfs}" "${mountpoint}" || \
664eda14cbcSMatt Macy			    retval=$((retval + 1))
665eda14cbcSMatt Macy		fi
666eda14cbcSMatt Macy	done
667eda14cbcSMatt Macy
668eda14cbcSMatt Macy	# If we haven't return yet, we have a problem...
669eda14cbcSMatt Macy	return "${retval}"
670eda14cbcSMatt Macy}
671eda14cbcSMatt Macy
672eda14cbcSMatt Macy# ================================================================
673eda14cbcSMatt Macy
674eda14cbcSMatt Macy# This is the main function.
675eda14cbcSMatt Macymountroot()
676eda14cbcSMatt Macy{
677eda14cbcSMatt Macy	# ----------------------------------------------------------------
678eda14cbcSMatt Macy	# I N I T I A L   S E T U P
679eda14cbcSMatt Macy
680eda14cbcSMatt Macy	# ------------
681eda14cbcSMatt Macy	# Run the pre-mount scripts from /scripts/local-top.
682eda14cbcSMatt Macy	pre_mountroot
683eda14cbcSMatt Macy
684eda14cbcSMatt Macy	# ------------
685eda14cbcSMatt Macy	# Source the default setup variables.
686eda14cbcSMatt Macy	[ -r '/etc/default/zfs' ] && . /etc/default/zfs
687eda14cbcSMatt Macy
688eda14cbcSMatt Macy	# ------------
689eda14cbcSMatt Macy	# Support debug option
690eda14cbcSMatt Macy	if grep -qiE '(^|[^\\](\\\\)* )(zfs_debug|zfs\.debug|zfsdebug)=(on|yes|1)( |$)' /proc/cmdline
691eda14cbcSMatt Macy	then
692eda14cbcSMatt Macy		ZFS_DEBUG=1
693eda14cbcSMatt Macy		mkdir /var/log
694eda14cbcSMatt Macy		#exec 2> /var/log/boot.debug
695eda14cbcSMatt Macy		set -x
696eda14cbcSMatt Macy	fi
697eda14cbcSMatt Macy
698eda14cbcSMatt Macy	# ------------
699eda14cbcSMatt Macy	# Load ZFS module etc.
700eda14cbcSMatt Macy	if ! load_module_initrd; then
701eda14cbcSMatt Macy		disable_plymouth
702eda14cbcSMatt Macy		echo ""
703eda14cbcSMatt Macy		echo "Failed to load ZFS modules."
704eda14cbcSMatt Macy		echo "Manually load the modules and exit."
705eda14cbcSMatt Macy		shell
706eda14cbcSMatt Macy	fi
707eda14cbcSMatt Macy
708eda14cbcSMatt Macy	# ------------
709eda14cbcSMatt Macy	# Look for the cache file (if any).
71016038816SMartin Matuska	[ -f "${ZPOOL_CACHE}" ] || unset ZPOOL_CACHE
71116038816SMartin Matuska	[ -s "${ZPOOL_CACHE}" ] || unset ZPOOL_CACHE
712eda14cbcSMatt Macy
713eda14cbcSMatt Macy	# ------------
714eda14cbcSMatt Macy	# Compatibility: 'ROOT' is for Debian GNU/Linux (etc),
715eda14cbcSMatt Macy	#		 'root' is for Redhat/Fedora (etc),
716eda14cbcSMatt Macy	#		 'REAL_ROOT' is for Gentoo
717eda14cbcSMatt Macy	if [ -z "$ROOT" ]
718eda14cbcSMatt Macy	then
719eda14cbcSMatt Macy		[ -n "$root" ] && ROOT=${root}
720eda14cbcSMatt Macy
721eda14cbcSMatt Macy		[ -n "$REAL_ROOT" ] && ROOT=${REAL_ROOT}
722eda14cbcSMatt Macy	fi
723eda14cbcSMatt Macy
724eda14cbcSMatt Macy	# ------------
725eda14cbcSMatt Macy	# Where to mount the root fs in the initrd - set outside this script
726eda14cbcSMatt Macy	# Compatibility: 'rootmnt' is for Debian GNU/Linux (etc),
727eda14cbcSMatt Macy	#		 'NEWROOT' is for RedHat/Fedora (etc),
728eda14cbcSMatt Macy	#		 'NEW_ROOT' is for Gentoo
729eda14cbcSMatt Macy	if [ -z "$rootmnt" ]
730eda14cbcSMatt Macy	then
731eda14cbcSMatt Macy		[ -n "$NEWROOT" ] && rootmnt=${NEWROOT}
732eda14cbcSMatt Macy
733eda14cbcSMatt Macy		[ -n "$NEW_ROOT" ] && rootmnt=${NEW_ROOT}
734eda14cbcSMatt Macy	fi
735eda14cbcSMatt Macy
736eda14cbcSMatt Macy	# ------------
737eda14cbcSMatt Macy	# No longer set in the defaults file, but it could have been set in
738eda14cbcSMatt Macy	# get_pools() in some circumstances. If it's something, but not 'yes',
739eda14cbcSMatt Macy	# it's no good to us.
740c40487d4SMatt Macy	[ -n "$USE_DISK_BY_ID" ] && [ "$USE_DISK_BY_ID" != 'yes' ] && \
741eda14cbcSMatt Macy	    unset USE_DISK_BY_ID
742eda14cbcSMatt Macy
743eda14cbcSMatt Macy	# ----------------------------------------------------------------
744eda14cbcSMatt Macy	# P A R S E   C O M M A N D   L I N E   O P T I O N S
745eda14cbcSMatt Macy
746eda14cbcSMatt Macy	# This part is the really ugly part - there's so many options and permutations
747eda14cbcSMatt Macy	# 'out there', and if we should make this the 'primary' source for ZFS initrd
748eda14cbcSMatt Macy	# scripting, we need/should support them all.
749eda14cbcSMatt Macy	#
750eda14cbcSMatt Macy	# Supports the following kernel command line argument combinations
751eda14cbcSMatt Macy	# (in this order - first match win):
752eda14cbcSMatt Macy	#
753eda14cbcSMatt Macy	#	rpool=<pool>			(tries to finds bootfs automatically)
754eda14cbcSMatt Macy	#	bootfs=<pool>/<dataset>		(uses this for rpool - first part)
755eda14cbcSMatt Macy	#	rpool=<pool> bootfs=<pool>/<dataset>
756eda14cbcSMatt Macy	#	-B zfs-bootfs=<pool>/<fs>	(uses this for rpool - first part)
757eda14cbcSMatt Macy	#	rpool=rpool			(default if none of the above is used)
758eda14cbcSMatt Macy	#	root=<pool>/<dataset>		(uses this for rpool - first part)
759eda14cbcSMatt Macy	#	root=ZFS=<pool>/<dataset>	(uses this for rpool - first part, without 'ZFS=')
760eda14cbcSMatt Macy	#	root=zfs:AUTO			(tries to detect both pool and rootfs
761eda14cbcSMatt Macy	#	root=zfs:<pool>/<dataset>	(uses this for rpool - first part, without 'zfs:')
762eda14cbcSMatt Macy	#
763eda14cbcSMatt Macy	# Option <dataset> could also be <snapshot>
764eda14cbcSMatt Macy	# Option <pool> could also be <guid>
765eda14cbcSMatt Macy
766eda14cbcSMatt Macy	# ------------
767eda14cbcSMatt Macy	# Support force option
768eda14cbcSMatt Macy	# In addition, setting one of zfs_force, zfs.force or zfsforce to
769eda14cbcSMatt Macy	# 'yes', 'on' or '1' will make sure we force import the pool.
770eda14cbcSMatt Macy	# This should (almost) never be needed, but it's here for
771eda14cbcSMatt Macy	# completeness.
772eda14cbcSMatt Macy	ZPOOL_FORCE=""
773eda14cbcSMatt Macy	if grep -qiE '(^|[^\\](\\\\)* )(zfs_force|zfs\.force|zfsforce)=(on|yes|1)( |$)' /proc/cmdline
774eda14cbcSMatt Macy	then
775eda14cbcSMatt Macy		ZPOOL_FORCE="-f"
776eda14cbcSMatt Macy	fi
777eda14cbcSMatt Macy
778eda14cbcSMatt Macy	# ------------
779eda14cbcSMatt Macy	# Look for 'rpool' and 'bootfs' parameter
780eda14cbcSMatt Macy	[ -n "$rpool" ] && ZFS_RPOOL="${rpool#rpool=}"
781eda14cbcSMatt Macy	[ -n "$bootfs" ] && ZFS_BOOTFS="${bootfs#bootfs=}"
782eda14cbcSMatt Macy
783eda14cbcSMatt Macy	# ------------
784eda14cbcSMatt Macy	# If we have 'ROOT' (see above), but not 'ZFS_BOOTFS', then use
785eda14cbcSMatt Macy	# 'ROOT'
786c40487d4SMatt Macy	[ -n "$ROOT" ] && [ -z "${ZFS_BOOTFS}" ] && ZFS_BOOTFS="$ROOT"
787eda14cbcSMatt Macy
788eda14cbcSMatt Macy	# ------------
789eda14cbcSMatt Macy	# Check for the `-B zfs-bootfs=%s/%u,...` kind of parameter.
790eda14cbcSMatt Macy	# NOTE: Only use the pool name and dataset. The rest is not
791c40487d4SMatt Macy	#       supported by OpenZFS (whatever it's for).
792eda14cbcSMatt Macy	if [ -z "$ZFS_RPOOL" ]
793eda14cbcSMatt Macy	then
794eda14cbcSMatt Macy		# The ${zfs-bootfs} variable is set at the kernel command
795eda14cbcSMatt Macy		# line, usually by GRUB, but it cannot be referenced here
796eda14cbcSMatt Macy		# directly because bourne variable names cannot contain a
797eda14cbcSMatt Macy		# hyphen.
798eda14cbcSMatt Macy		#
799eda14cbcSMatt Macy		# Reassign the variable by dumping the environment and
800eda14cbcSMatt Macy		# stripping the zfs-bootfs= prefix.  Let the shell handle
80116038816SMartin Matuska		# quoting through the eval command:
80216038816SMartin Matuska		# shellcheck disable=SC2046
803eda14cbcSMatt Macy		eval ZFS_RPOOL=$(set | sed -n -e 's,^zfs-bootfs=,,p')
804eda14cbcSMatt Macy	fi
805eda14cbcSMatt Macy
806eda14cbcSMatt Macy	# ------------
807eda14cbcSMatt Macy	# No root fs or pool specified - do auto detect.
808c40487d4SMatt Macy	if [ -z "$ZFS_RPOOL" ] && [ -z "${ZFS_BOOTFS}" ]
809eda14cbcSMatt Macy	then
810eda14cbcSMatt Macy		# Do auto detect. Do this by 'cheating' - set 'root=zfs:AUTO'
811eda14cbcSMatt Macy		# which will be caught later
812c40487d4SMatt Macy		ROOT='zfs:AUTO'
813eda14cbcSMatt Macy	fi
814eda14cbcSMatt Macy
815eda14cbcSMatt Macy	# ----------------------------------------------------------------
816eda14cbcSMatt 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
817eda14cbcSMatt Macy
818eda14cbcSMatt Macy	# ------------
819eda14cbcSMatt Macy	if [ "$ROOT" = "zfs:AUTO" ]
820eda14cbcSMatt Macy	then
821eda14cbcSMatt Macy		# Try to detect both pool and root fs.
822eda14cbcSMatt Macy
82316038816SMartin Matuska		# If we got here, that means we don't have a hint so as to
82416038816SMartin Matuska		# the root dataset, but with root=zfs:AUTO on cmdline,
82516038816SMartin Matuska		# this says "zfs:AUTO" here and interferes with checks later
82616038816SMartin Matuska		ZFS_BOOTFS=
82716038816SMartin Matuska
828eda14cbcSMatt Macy		[ "$quiet" != "y" ] && \
829eda14cbcSMatt Macy		    zfs_log_begin_msg "Attempting to import additional pools."
830eda14cbcSMatt Macy
831eda14cbcSMatt Macy		# Get a list of pools available for import
832eda14cbcSMatt Macy		if [ -n "$ZFS_RPOOL" ]
833eda14cbcSMatt Macy		then
834eda14cbcSMatt Macy			# We've specified a pool - check only that
835eda14cbcSMatt Macy			POOLS=$ZFS_RPOOL
836eda14cbcSMatt Macy		else
837eda14cbcSMatt Macy			POOLS=$(get_pools)
838eda14cbcSMatt Macy		fi
839eda14cbcSMatt Macy
840eda14cbcSMatt Macy		OLD_IFS="$IFS" ; IFS=";"
841eda14cbcSMatt Macy		for pool in $POOLS
842eda14cbcSMatt Macy		do
843eda14cbcSMatt Macy			[ -z "$pool" ] && continue
844eda14cbcSMatt Macy
84516038816SMartin Matuska			IFS="$OLD_IFS" import_pool "$pool"
84616038816SMartin Matuska			IFS="$OLD_IFS" find_rootfs "$pool" && break
847eda14cbcSMatt Macy		done
848eda14cbcSMatt Macy		IFS="$OLD_IFS"
849eda14cbcSMatt Macy
850eda14cbcSMatt Macy		[ "$quiet" != "y" ] && zfs_log_end_msg $ZFS_ERROR
851eda14cbcSMatt Macy	else
852eda14cbcSMatt Macy		# No auto - use value from the command line option.
853eda14cbcSMatt Macy
854eda14cbcSMatt Macy		# Strip 'zfs:' and 'ZFS='.
855eda14cbcSMatt Macy		ZFS_BOOTFS="${ROOT#*[:=]}"
856eda14cbcSMatt Macy
857eda14cbcSMatt Macy		# Strip everything after the first slash.
858eda14cbcSMatt Macy		ZFS_RPOOL="${ZFS_BOOTFS%%/*}"
859eda14cbcSMatt Macy	fi
860eda14cbcSMatt Macy
861eda14cbcSMatt Macy	# Import the pool (if not already done so in the AUTO check above).
862c40487d4SMatt Macy	if [ -n "$ZFS_RPOOL" ] && [ -z "${POOL_IMPORTED}" ]
863eda14cbcSMatt Macy	then
864eda14cbcSMatt Macy		[ "$quiet" != "y" ] && \
865eda14cbcSMatt Macy		    zfs_log_begin_msg "Importing ZFS root pool '$ZFS_RPOOL'"
866eda14cbcSMatt Macy
867eda14cbcSMatt Macy		import_pool "${ZFS_RPOOL}"
868eda14cbcSMatt Macy		find_rootfs "${ZFS_RPOOL}"
869eda14cbcSMatt Macy
870eda14cbcSMatt Macy		[ "$quiet" != "y" ] && zfs_log_end_msg
871eda14cbcSMatt Macy	fi
872eda14cbcSMatt Macy
873eda14cbcSMatt Macy	if [ -z "${POOL_IMPORTED}" ]
874eda14cbcSMatt Macy	then
875eda14cbcSMatt Macy		# No pool imported, this is serious!
876eda14cbcSMatt Macy		disable_plymouth
877eda14cbcSMatt Macy		echo ""
878eda14cbcSMatt Macy		echo "Command: $ZFS_CMD"
879eda14cbcSMatt Macy		echo "Message: $ZFS_STDERR"
880eda14cbcSMatt Macy		echo "Error: $ZFS_ERROR"
881eda14cbcSMatt Macy		echo ""
882eda14cbcSMatt Macy		echo "No pool imported. Manually import the root pool"
883eda14cbcSMatt Macy		echo "at the command prompt and then exit."
88416038816SMartin Matuska		echo "Hint: Try:  zpool import -N ${ZFS_RPOOL}"
885eda14cbcSMatt Macy		shell
886eda14cbcSMatt Macy	fi
887eda14cbcSMatt Macy
888eda14cbcSMatt Macy	# In case the pool was specified as guid, resolve guid to name
889eda14cbcSMatt Macy	pool="$("${ZPOOL}" get name,guid -o name,value -H | \
890eda14cbcSMatt Macy	    awk -v pool="${ZFS_RPOOL}" '$2 == pool { print $1 }')"
891eda14cbcSMatt Macy	if [ -n "$pool" ]; then
892eda14cbcSMatt Macy		# If $ZFS_BOOTFS contains guid, replace the guid portion with $pool
893eda14cbcSMatt Macy		ZFS_BOOTFS=$(echo "$ZFS_BOOTFS" | \
894eda14cbcSMatt Macy			sed -e "s/$("${ZPOOL}" get guid -o value "$pool" -H)/$pool/g")
895eda14cbcSMatt Macy		ZFS_RPOOL="${pool}"
896eda14cbcSMatt Macy	fi
897eda14cbcSMatt Macy
898eda14cbcSMatt Macy
899eda14cbcSMatt Macy	# ----------------------------------------------------------------
900eda14cbcSMatt Macy	# P R E P A R E   R O O T   F I L E S Y S T E M
901eda14cbcSMatt Macy
902eda14cbcSMatt Macy	if [ -n "${ZFS_BOOTFS}" ]
903eda14cbcSMatt Macy	then
904eda14cbcSMatt Macy		# Booting from a snapshot?
905eda14cbcSMatt Macy		# Will overwrite the ZFS_BOOTFS variable like so:
906eda14cbcSMatt Macy		#   rpool/ROOT/debian@snap2 => rpool/ROOT/debian_snap2
907eda14cbcSMatt Macy		echo "${ZFS_BOOTFS}" | grep -q '@' && \
908eda14cbcSMatt Macy		    setup_snapshot_booting "${ZFS_BOOTFS}"
909eda14cbcSMatt Macy	fi
910eda14cbcSMatt Macy
911eda14cbcSMatt Macy	if [ -z "${ZFS_BOOTFS}" ]
912eda14cbcSMatt Macy	then
913eda14cbcSMatt Macy		# Still nothing! Let the user sort this out.
914eda14cbcSMatt Macy		disable_plymouth
915eda14cbcSMatt Macy		echo ""
916eda14cbcSMatt Macy		echo "Error: Unknown root filesystem - no 'bootfs' pool property and"
917eda14cbcSMatt Macy		echo "       not specified on the kernel command line."
918eda14cbcSMatt Macy		echo ""
919eda14cbcSMatt Macy		echo "Manually mount the root filesystem on $rootmnt and then exit."
920eda14cbcSMatt Macy		echo "Hint: Try:  mount -o zfsutil -t zfs ${ZFS_RPOOL-rpool}/ROOT/system $rootmnt"
921eda14cbcSMatt Macy		shell
922eda14cbcSMatt Macy	fi
923eda14cbcSMatt Macy
924eda14cbcSMatt Macy	# ----------------------------------------------------------------
925eda14cbcSMatt Macy	# M O U N T   F I L E S Y S T E M S
926eda14cbcSMatt Macy
927eda14cbcSMatt Macy	# * Ideally, the root filesystem would be mounted like this:
928eda14cbcSMatt Macy	#
929eda14cbcSMatt Macy	#     zpool import -R "$rootmnt" -N "$ZFS_RPOOL"
930eda14cbcSMatt Macy	#     zfs mount -o mountpoint=/ "${ZFS_BOOTFS}"
931eda14cbcSMatt Macy	#
932eda14cbcSMatt Macy	#   but the MOUNTPOINT prefix is preserved on descendent filesystem
933eda14cbcSMatt Macy	#   after the pivot into the regular root, which later breaks things
934eda14cbcSMatt Macy	#   like `zfs mount -a` and the /proc/self/mounts refresh.
935eda14cbcSMatt Macy	#
936eda14cbcSMatt Macy	# * Mount additional filesystems required
937eda14cbcSMatt Macy	#   Such as /usr, /var, /usr/local etc.
938eda14cbcSMatt Macy	#   NOTE: Mounted in the order specified in the
939eda14cbcSMatt Macy	#         ZFS_INITRD_ADDITIONAL_DATASETS variable so take care!
940eda14cbcSMatt Macy
941eda14cbcSMatt Macy	# Go through the complete list (recursively) of all filesystems below
942eda14cbcSMatt Macy	# the real root dataset
94316038816SMartin Matuska	filesystems="$("${ZFS}" list -oname -tfilesystem -H -r "${ZFS_BOOTFS}")"
94416038816SMartin Matuska	OLD_IFS="$IFS" ; IFS="
94516038816SMartin Matuska"
94616038816SMartin Matuska	for fs in $filesystems; do
94716038816SMartin Matuska		IFS="$OLD_IFS" mount_fs "$fs"
94816038816SMartin Matuska	done
94916038816SMartin Matuska	IFS="$OLD_IFS"
95016038816SMartin Matuska	for fs in $ZFS_INITRD_ADDITIONAL_DATASETS; do
951eda14cbcSMatt Macy		mount_fs "$fs"
952eda14cbcSMatt Macy	done
953eda14cbcSMatt Macy
954eda14cbcSMatt Macy	touch /run/zfs_unlock_complete
955eda14cbcSMatt Macy	if [ -e /run/zfs_unlock_complete_notify ]; then
95616038816SMartin Matuska		read -r < /run/zfs_unlock_complete_notify
957eda14cbcSMatt Macy	fi
958eda14cbcSMatt Macy
959eda14cbcSMatt Macy	# ------------
960eda14cbcSMatt Macy	# Debugging information
961eda14cbcSMatt Macy	if [ -n "${ZFS_DEBUG}" ]
962eda14cbcSMatt Macy	then
963eda14cbcSMatt Macy		#exec 2>&1-
964eda14cbcSMatt Macy
965eda14cbcSMatt Macy		echo "DEBUG: imported pools:"
966eda14cbcSMatt Macy		"${ZPOOL}" list -H
967eda14cbcSMatt Macy		echo
968eda14cbcSMatt Macy
969eda14cbcSMatt Macy		echo "DEBUG: mounted ZFS filesystems:"
970eda14cbcSMatt Macy		mount | grep zfs
971eda14cbcSMatt Macy		echo
972eda14cbcSMatt Macy
973eda14cbcSMatt Macy		echo "=> waiting for ENTER before continuing because of 'zfsdebug=1'. "
974c40487d4SMatt Macy		printf "%s" "   'c' for shell, 'r' for reboot, 'ENTER' to continue. "
975c40487d4SMatt Macy		read -r b
976eda14cbcSMatt Macy
977eda14cbcSMatt Macy		[ "$b" = "c" ] && /bin/sh
978eda14cbcSMatt Macy		[ "$b" = "r" ] && reboot -f
979eda14cbcSMatt Macy
980eda14cbcSMatt Macy		set +x
981eda14cbcSMatt Macy	fi
982eda14cbcSMatt Macy
983eda14cbcSMatt Macy	# ------------
984eda14cbcSMatt Macy	# Run local bottom script
985c40487d4SMatt Macy	if command -v run_scripts > /dev/null 2>&1
986c40487d4SMatt Macy	then
987c40487d4SMatt Macy		if [ -f "/scripts/local-bottom" ] || [ -d "/scripts/local-bottom" ]
988eda14cbcSMatt Macy		then
989eda14cbcSMatt Macy			[ "$quiet" != "y" ] && \
990eda14cbcSMatt Macy			    zfs_log_begin_msg "Running /scripts/local-bottom"
991eda14cbcSMatt Macy			run_scripts /scripts/local-bottom
992eda14cbcSMatt Macy			[ "$quiet" != "y" ] && zfs_log_end_msg
993eda14cbcSMatt Macy		fi
994c40487d4SMatt Macy	fi
995eda14cbcSMatt Macy}
996