xref: /freebsd/sys/contrib/openzfs/contrib/initramfs/scripts/zfs (revision 1603881667360c015f6685131f2f25474fa67a72)
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#
8*16038816SMartin Matuska# $quiet, $root, $rpool, $bootfs come from the cmdline:
9*16038816SMartin 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"
99*16038816SMartin Matuska	POOL_IMPORTED=
100*16038816SMartin 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{
107*16038816SMartin Matuska	pools=$("$@" 2> /dev/null | \
108eda14cbcSMatt Macy		grep -E "pool:|^[a-zA-Z0-9]" | \
109eda14cbcSMatt Macy		sed 's@.*: @@' | \
110*16038816SMartin 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
204*16038816SMartin 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{
274*16038816SMartin Matuska	[ -n "$ROOTDELAY" ] && ZFS_INITRD_PRE_MOUNTROOT_SLEEP="$ROOTDELAY"
275*16038816SMartin 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)
330*16038816SMartin 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...
352*16038816SMartin Matuska		if [ "$mountpoint" = "legacy" ]; then
353*16038816SMartin 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'
393*16038816SMartin 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
407*16038816SMartin 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
412*16038816SMartin Matuska			elif /bin/plymouth --ping 2>/dev/null; then
413eda14cbcSMatt Macy				echo "plymouth" > /run/zfs_console_askpwd_cmd
414*16038816SMartin 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
422*16038816SMartin 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
563*16038816SMartin Matuska	# shellcheck disable=SC2046
564*16038816SMartin Matuska	IFS="
565*16038816SMartin Matuska" set -- $("${ZFS}" list -H -oname -tsnapshot -r "${fs}")
566eda14cbcSMatt Macy
567*16038816SMartin Matuska	i=1
568*16038816SMartin Matuska	for snap in "$@"; do
569*16038816SMartin Matuska		echo "  $i: $snap"
570*16038816SMartin Matuska		i=$((i + 1))
571*16038816SMartin Matuska	done > /dev/stderr
572*16038816SMartin Matuska
573*16038816SMartin Matuska	# expr instead of test here because [ a -lt 0 ] errors out,
574*16038816SMartin Matuska	# but expr falls back to lexicographical, which works out right
575*16038816SMartin Matuska	snapnr=0
576*16038816SMartin Matuska	while expr "$snapnr" "<" 1 > /dev/null ||
577*16038816SMartin Matuska	    expr "$snapnr" ">" "$#" > /dev/null
578*16038816SMartin Matuska	do
579*16038816SMartin Matuska		printf "%s" "Snap nr [1-$#]? " > /dev/stderr
580c40487d4SMatt Macy		read -r snapnr
581*16038816SMartin 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
589*16038816SMartin 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))
633eda14cbcSMatt Macy		else
634eda14cbcSMatt Macy			# Setup a destination filesystem name.
635eda14cbcSMatt Macy			# Ex: Called with 'rpool/ROOT/debian@snap2'
636eda14cbcSMatt Macy			#       rpool/ROOT/debian@snap2		=> rpool/ROOT/debian_snap2
637eda14cbcSMatt Macy			#       rpool/ROOT/debian/boot@snap2	=> rpool/ROOT/debian_snap2/boot
638eda14cbcSMatt Macy			#       rpool/ROOT/debian/usr@snap2	=> rpool/ROOT/debian_snap2/usr
639eda14cbcSMatt Macy			#       rpool/ROOT/debian/var@snap2	=> rpool/ROOT/debian_snap2/var
640eda14cbcSMatt Macy			subfs="${s##$rootfs}"
641eda14cbcSMatt Macy			subfs="${subfs%%@$snapname}"
642eda14cbcSMatt Macy
643eda14cbcSMatt Macy			destfs="${rootfs}_${snapname}" # base fs.
644eda14cbcSMatt Macy			[ -n "$subfs" ] && destfs="${destfs}$subfs" # + sub fs.
645eda14cbcSMatt Macy
646eda14cbcSMatt Macy			# Get the mountpoint of the filesystem, to be used
647eda14cbcSMatt Macy			# with clone_snap(). If legacy or none, then use
648eda14cbcSMatt Macy			# the sub fs value.
649eda14cbcSMatt Macy			mountpoint=$(get_fs_value "${s%%@*}" mountpoint)
650c40487d4SMatt Macy			if [ "$mountpoint" = "legacy" ] || \
651c40487d4SMatt Macy			   [ "$mountpoint" = "none" ]
652eda14cbcSMatt Macy			then
653eda14cbcSMatt Macy				if [ -n "${subfs}" ]; then
654eda14cbcSMatt Macy					mountpoint="${subfs}"
655eda14cbcSMatt Macy				else
656eda14cbcSMatt Macy					mountpoint="/"
657eda14cbcSMatt Macy				fi
658eda14cbcSMatt Macy			fi
659eda14cbcSMatt Macy
660eda14cbcSMatt Macy			# Clone the snapshot into its own
661eda14cbcSMatt Macy			# filesystem
662eda14cbcSMatt Macy			clone_snap "$s" "${destfs}" "${mountpoint}" || \
663eda14cbcSMatt Macy			    retval=$((retval + 1))
664eda14cbcSMatt Macy		fi
665eda14cbcSMatt Macy	done
666eda14cbcSMatt Macy
667eda14cbcSMatt Macy	# If we haven't return yet, we have a problem...
668eda14cbcSMatt Macy	return "${retval}"
669eda14cbcSMatt Macy}
670eda14cbcSMatt Macy
671eda14cbcSMatt Macy# ================================================================
672eda14cbcSMatt Macy
673eda14cbcSMatt Macy# This is the main function.
674eda14cbcSMatt Macymountroot()
675eda14cbcSMatt Macy{
676eda14cbcSMatt Macy	# ----------------------------------------------------------------
677eda14cbcSMatt Macy	# I N I T I A L   S E T U P
678eda14cbcSMatt Macy
679eda14cbcSMatt Macy	# ------------
680eda14cbcSMatt Macy	# Run the pre-mount scripts from /scripts/local-top.
681eda14cbcSMatt Macy	pre_mountroot
682eda14cbcSMatt Macy
683eda14cbcSMatt Macy	# ------------
684eda14cbcSMatt Macy	# Source the default setup variables.
685eda14cbcSMatt Macy	[ -r '/etc/default/zfs' ] && . /etc/default/zfs
686eda14cbcSMatt Macy
687eda14cbcSMatt Macy	# ------------
688eda14cbcSMatt Macy	# Support debug option
689eda14cbcSMatt Macy	if grep -qiE '(^|[^\\](\\\\)* )(zfs_debug|zfs\.debug|zfsdebug)=(on|yes|1)( |$)' /proc/cmdline
690eda14cbcSMatt Macy	then
691eda14cbcSMatt Macy		ZFS_DEBUG=1
692eda14cbcSMatt Macy		mkdir /var/log
693eda14cbcSMatt Macy		#exec 2> /var/log/boot.debug
694eda14cbcSMatt Macy		set -x
695eda14cbcSMatt Macy	fi
696eda14cbcSMatt Macy
697eda14cbcSMatt Macy	# ------------
698eda14cbcSMatt Macy	# Load ZFS module etc.
699eda14cbcSMatt Macy	if ! load_module_initrd; then
700eda14cbcSMatt Macy		disable_plymouth
701eda14cbcSMatt Macy		echo ""
702eda14cbcSMatt Macy		echo "Failed to load ZFS modules."
703eda14cbcSMatt Macy		echo "Manually load the modules and exit."
704eda14cbcSMatt Macy		shell
705eda14cbcSMatt Macy	fi
706eda14cbcSMatt Macy
707eda14cbcSMatt Macy	# ------------
708eda14cbcSMatt Macy	# Look for the cache file (if any).
709*16038816SMartin Matuska	[ -f "${ZPOOL_CACHE}" ] || unset ZPOOL_CACHE
710*16038816SMartin Matuska	[ -s "${ZPOOL_CACHE}" ] || unset ZPOOL_CACHE
711eda14cbcSMatt Macy
712eda14cbcSMatt Macy	# ------------
713eda14cbcSMatt Macy	# Compatibility: 'ROOT' is for Debian GNU/Linux (etc),
714eda14cbcSMatt Macy	#		 'root' is for Redhat/Fedora (etc),
715eda14cbcSMatt Macy	#		 'REAL_ROOT' is for Gentoo
716eda14cbcSMatt Macy	if [ -z "$ROOT" ]
717eda14cbcSMatt Macy	then
718eda14cbcSMatt Macy		[ -n "$root" ] && ROOT=${root}
719eda14cbcSMatt Macy
720eda14cbcSMatt Macy		[ -n "$REAL_ROOT" ] && ROOT=${REAL_ROOT}
721eda14cbcSMatt Macy	fi
722eda14cbcSMatt Macy
723eda14cbcSMatt Macy	# ------------
724eda14cbcSMatt Macy	# Where to mount the root fs in the initrd - set outside this script
725eda14cbcSMatt Macy	# Compatibility: 'rootmnt' is for Debian GNU/Linux (etc),
726eda14cbcSMatt Macy	#		 'NEWROOT' is for RedHat/Fedora (etc),
727eda14cbcSMatt Macy	#		 'NEW_ROOT' is for Gentoo
728eda14cbcSMatt Macy	if [ -z "$rootmnt" ]
729eda14cbcSMatt Macy	then
730eda14cbcSMatt Macy		[ -n "$NEWROOT" ] && rootmnt=${NEWROOT}
731eda14cbcSMatt Macy
732eda14cbcSMatt Macy		[ -n "$NEW_ROOT" ] && rootmnt=${NEW_ROOT}
733eda14cbcSMatt Macy	fi
734eda14cbcSMatt Macy
735eda14cbcSMatt Macy	# ------------
736eda14cbcSMatt Macy	# No longer set in the defaults file, but it could have been set in
737eda14cbcSMatt Macy	# get_pools() in some circumstances. If it's something, but not 'yes',
738eda14cbcSMatt Macy	# it's no good to us.
739c40487d4SMatt Macy	[ -n "$USE_DISK_BY_ID" ] && [ "$USE_DISK_BY_ID" != 'yes' ] && \
740eda14cbcSMatt Macy	    unset USE_DISK_BY_ID
741eda14cbcSMatt Macy
742eda14cbcSMatt Macy	# ----------------------------------------------------------------
743eda14cbcSMatt Macy	# P A R S E   C O M M A N D   L I N E   O P T I O N S
744eda14cbcSMatt Macy
745eda14cbcSMatt Macy	# This part is the really ugly part - there's so many options and permutations
746eda14cbcSMatt Macy	# 'out there', and if we should make this the 'primary' source for ZFS initrd
747eda14cbcSMatt Macy	# scripting, we need/should support them all.
748eda14cbcSMatt Macy	#
749eda14cbcSMatt Macy	# Supports the following kernel command line argument combinations
750eda14cbcSMatt Macy	# (in this order - first match win):
751eda14cbcSMatt Macy	#
752eda14cbcSMatt Macy	#	rpool=<pool>			(tries to finds bootfs automatically)
753eda14cbcSMatt Macy	#	bootfs=<pool>/<dataset>		(uses this for rpool - first part)
754eda14cbcSMatt Macy	#	rpool=<pool> bootfs=<pool>/<dataset>
755eda14cbcSMatt Macy	#	-B zfs-bootfs=<pool>/<fs>	(uses this for rpool - first part)
756eda14cbcSMatt Macy	#	rpool=rpool			(default if none of the above is used)
757eda14cbcSMatt Macy	#	root=<pool>/<dataset>		(uses this for rpool - first part)
758eda14cbcSMatt Macy	#	root=ZFS=<pool>/<dataset>	(uses this for rpool - first part, without 'ZFS=')
759eda14cbcSMatt Macy	#	root=zfs:AUTO			(tries to detect both pool and rootfs
760eda14cbcSMatt Macy	#	root=zfs:<pool>/<dataset>	(uses this for rpool - first part, without 'zfs:')
761eda14cbcSMatt Macy	#
762eda14cbcSMatt Macy	# Option <dataset> could also be <snapshot>
763eda14cbcSMatt Macy	# Option <pool> could also be <guid>
764eda14cbcSMatt Macy
765eda14cbcSMatt Macy	# ------------
766eda14cbcSMatt Macy	# Support force option
767eda14cbcSMatt Macy	# In addition, setting one of zfs_force, zfs.force or zfsforce to
768eda14cbcSMatt Macy	# 'yes', 'on' or '1' will make sure we force import the pool.
769eda14cbcSMatt Macy	# This should (almost) never be needed, but it's here for
770eda14cbcSMatt Macy	# completeness.
771eda14cbcSMatt Macy	ZPOOL_FORCE=""
772eda14cbcSMatt Macy	if grep -qiE '(^|[^\\](\\\\)* )(zfs_force|zfs\.force|zfsforce)=(on|yes|1)( |$)' /proc/cmdline
773eda14cbcSMatt Macy	then
774eda14cbcSMatt Macy		ZPOOL_FORCE="-f"
775eda14cbcSMatt Macy	fi
776eda14cbcSMatt Macy
777eda14cbcSMatt Macy	# ------------
778eda14cbcSMatt Macy	# Look for 'rpool' and 'bootfs' parameter
779eda14cbcSMatt Macy	[ -n "$rpool" ] && ZFS_RPOOL="${rpool#rpool=}"
780eda14cbcSMatt Macy	[ -n "$bootfs" ] && ZFS_BOOTFS="${bootfs#bootfs=}"
781eda14cbcSMatt Macy
782eda14cbcSMatt Macy	# ------------
783eda14cbcSMatt Macy	# If we have 'ROOT' (see above), but not 'ZFS_BOOTFS', then use
784eda14cbcSMatt Macy	# 'ROOT'
785c40487d4SMatt Macy	[ -n "$ROOT" ] && [ -z "${ZFS_BOOTFS}" ] && ZFS_BOOTFS="$ROOT"
786eda14cbcSMatt Macy
787eda14cbcSMatt Macy	# ------------
788eda14cbcSMatt Macy	# Check for the `-B zfs-bootfs=%s/%u,...` kind of parameter.
789eda14cbcSMatt Macy	# NOTE: Only use the pool name and dataset. The rest is not
790c40487d4SMatt Macy	#       supported by OpenZFS (whatever it's for).
791eda14cbcSMatt Macy	if [ -z "$ZFS_RPOOL" ]
792eda14cbcSMatt Macy	then
793eda14cbcSMatt Macy		# The ${zfs-bootfs} variable is set at the kernel command
794eda14cbcSMatt Macy		# line, usually by GRUB, but it cannot be referenced here
795eda14cbcSMatt Macy		# directly because bourne variable names cannot contain a
796eda14cbcSMatt Macy		# hyphen.
797eda14cbcSMatt Macy		#
798eda14cbcSMatt Macy		# Reassign the variable by dumping the environment and
799eda14cbcSMatt Macy		# stripping the zfs-bootfs= prefix.  Let the shell handle
800*16038816SMartin Matuska		# quoting through the eval command:
801*16038816SMartin Matuska		# shellcheck disable=SC2046
802eda14cbcSMatt Macy		eval ZFS_RPOOL=$(set | sed -n -e 's,^zfs-bootfs=,,p')
803eda14cbcSMatt Macy	fi
804eda14cbcSMatt Macy
805eda14cbcSMatt Macy	# ------------
806eda14cbcSMatt Macy	# No root fs or pool specified - do auto detect.
807c40487d4SMatt Macy	if [ -z "$ZFS_RPOOL" ] && [ -z "${ZFS_BOOTFS}" ]
808eda14cbcSMatt Macy	then
809eda14cbcSMatt Macy		# Do auto detect. Do this by 'cheating' - set 'root=zfs:AUTO'
810eda14cbcSMatt Macy		# which will be caught later
811c40487d4SMatt Macy		ROOT='zfs:AUTO'
812eda14cbcSMatt Macy	fi
813eda14cbcSMatt Macy
814eda14cbcSMatt Macy	# ----------------------------------------------------------------
815eda14cbcSMatt 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
816eda14cbcSMatt Macy
817eda14cbcSMatt Macy	# ------------
818eda14cbcSMatt Macy	if [ "$ROOT" = "zfs:AUTO" ]
819eda14cbcSMatt Macy	then
820eda14cbcSMatt Macy		# Try to detect both pool and root fs.
821eda14cbcSMatt Macy
822*16038816SMartin Matuska		# If we got here, that means we don't have a hint so as to
823*16038816SMartin Matuska		# the root dataset, but with root=zfs:AUTO on cmdline,
824*16038816SMartin Matuska		# this says "zfs:AUTO" here and interferes with checks later
825*16038816SMartin Matuska		ZFS_BOOTFS=
826*16038816SMartin Matuska
827eda14cbcSMatt Macy		[ "$quiet" != "y" ] && \
828eda14cbcSMatt Macy		    zfs_log_begin_msg "Attempting to import additional pools."
829eda14cbcSMatt Macy
830eda14cbcSMatt Macy		# Get a list of pools available for import
831eda14cbcSMatt Macy		if [ -n "$ZFS_RPOOL" ]
832eda14cbcSMatt Macy		then
833eda14cbcSMatt Macy			# We've specified a pool - check only that
834eda14cbcSMatt Macy			POOLS=$ZFS_RPOOL
835eda14cbcSMatt Macy		else
836eda14cbcSMatt Macy			POOLS=$(get_pools)
837eda14cbcSMatt Macy		fi
838eda14cbcSMatt Macy
839eda14cbcSMatt Macy		OLD_IFS="$IFS" ; IFS=";"
840eda14cbcSMatt Macy		for pool in $POOLS
841eda14cbcSMatt Macy		do
842eda14cbcSMatt Macy			[ -z "$pool" ] && continue
843eda14cbcSMatt Macy
844*16038816SMartin Matuska			IFS="$OLD_IFS" import_pool "$pool"
845*16038816SMartin Matuska			IFS="$OLD_IFS" find_rootfs "$pool" && break
846eda14cbcSMatt Macy		done
847eda14cbcSMatt Macy		IFS="$OLD_IFS"
848eda14cbcSMatt Macy
849eda14cbcSMatt Macy		[ "$quiet" != "y" ] && zfs_log_end_msg $ZFS_ERROR
850eda14cbcSMatt Macy	else
851eda14cbcSMatt Macy		# No auto - use value from the command line option.
852eda14cbcSMatt Macy
853eda14cbcSMatt Macy		# Strip 'zfs:' and 'ZFS='.
854eda14cbcSMatt Macy		ZFS_BOOTFS="${ROOT#*[:=]}"
855eda14cbcSMatt Macy
856eda14cbcSMatt Macy		# Strip everything after the first slash.
857eda14cbcSMatt Macy		ZFS_RPOOL="${ZFS_BOOTFS%%/*}"
858eda14cbcSMatt Macy	fi
859eda14cbcSMatt Macy
860eda14cbcSMatt Macy	# Import the pool (if not already done so in the AUTO check above).
861c40487d4SMatt Macy	if [ -n "$ZFS_RPOOL" ] && [ -z "${POOL_IMPORTED}" ]
862eda14cbcSMatt Macy	then
863eda14cbcSMatt Macy		[ "$quiet" != "y" ] && \
864eda14cbcSMatt Macy		    zfs_log_begin_msg "Importing ZFS root pool '$ZFS_RPOOL'"
865eda14cbcSMatt Macy
866eda14cbcSMatt Macy		import_pool "${ZFS_RPOOL}"
867eda14cbcSMatt Macy		find_rootfs "${ZFS_RPOOL}"
868eda14cbcSMatt Macy
869eda14cbcSMatt Macy		[ "$quiet" != "y" ] && zfs_log_end_msg
870eda14cbcSMatt Macy	fi
871eda14cbcSMatt Macy
872eda14cbcSMatt Macy	if [ -z "${POOL_IMPORTED}" ]
873eda14cbcSMatt Macy	then
874eda14cbcSMatt Macy		# No pool imported, this is serious!
875eda14cbcSMatt Macy		disable_plymouth
876eda14cbcSMatt Macy		echo ""
877eda14cbcSMatt Macy		echo "Command: $ZFS_CMD"
878eda14cbcSMatt Macy		echo "Message: $ZFS_STDERR"
879eda14cbcSMatt Macy		echo "Error: $ZFS_ERROR"
880eda14cbcSMatt Macy		echo ""
881eda14cbcSMatt Macy		echo "No pool imported. Manually import the root pool"
882eda14cbcSMatt Macy		echo "at the command prompt and then exit."
883*16038816SMartin Matuska		echo "Hint: Try:  zpool import -N ${ZFS_RPOOL}"
884eda14cbcSMatt Macy		shell
885eda14cbcSMatt Macy	fi
886eda14cbcSMatt Macy
887eda14cbcSMatt Macy	# In case the pool was specified as guid, resolve guid to name
888eda14cbcSMatt Macy	pool="$("${ZPOOL}" get name,guid -o name,value -H | \
889eda14cbcSMatt Macy	    awk -v pool="${ZFS_RPOOL}" '$2 == pool { print $1 }')"
890eda14cbcSMatt Macy	if [ -n "$pool" ]; then
891eda14cbcSMatt Macy		# If $ZFS_BOOTFS contains guid, replace the guid portion with $pool
892eda14cbcSMatt Macy		ZFS_BOOTFS=$(echo "$ZFS_BOOTFS" | \
893eda14cbcSMatt Macy			sed -e "s/$("${ZPOOL}" get guid -o value "$pool" -H)/$pool/g")
894eda14cbcSMatt Macy		ZFS_RPOOL="${pool}"
895eda14cbcSMatt Macy	fi
896eda14cbcSMatt Macy
897eda14cbcSMatt Macy
898eda14cbcSMatt Macy	# ----------------------------------------------------------------
899eda14cbcSMatt Macy	# P R E P A R E   R O O T   F I L E S Y S T E M
900eda14cbcSMatt Macy
901eda14cbcSMatt Macy	if [ -n "${ZFS_BOOTFS}" ]
902eda14cbcSMatt Macy	then
903eda14cbcSMatt Macy		# Booting from a snapshot?
904eda14cbcSMatt Macy		# Will overwrite the ZFS_BOOTFS variable like so:
905eda14cbcSMatt Macy		#   rpool/ROOT/debian@snap2 => rpool/ROOT/debian_snap2
906eda14cbcSMatt Macy		echo "${ZFS_BOOTFS}" | grep -q '@' && \
907eda14cbcSMatt Macy		    setup_snapshot_booting "${ZFS_BOOTFS}"
908eda14cbcSMatt Macy	fi
909eda14cbcSMatt Macy
910eda14cbcSMatt Macy	if [ -z "${ZFS_BOOTFS}" ]
911eda14cbcSMatt Macy	then
912eda14cbcSMatt Macy		# Still nothing! Let the user sort this out.
913eda14cbcSMatt Macy		disable_plymouth
914eda14cbcSMatt Macy		echo ""
915eda14cbcSMatt Macy		echo "Error: Unknown root filesystem - no 'bootfs' pool property and"
916eda14cbcSMatt Macy		echo "       not specified on the kernel command line."
917eda14cbcSMatt Macy		echo ""
918eda14cbcSMatt Macy		echo "Manually mount the root filesystem on $rootmnt and then exit."
919eda14cbcSMatt Macy		echo "Hint: Try:  mount -o zfsutil -t zfs ${ZFS_RPOOL-rpool}/ROOT/system $rootmnt"
920eda14cbcSMatt Macy		shell
921eda14cbcSMatt Macy	fi
922eda14cbcSMatt Macy
923eda14cbcSMatt Macy	# ----------------------------------------------------------------
924eda14cbcSMatt Macy	# M O U N T   F I L E S Y S T E M S
925eda14cbcSMatt Macy
926eda14cbcSMatt Macy	# * Ideally, the root filesystem would be mounted like this:
927eda14cbcSMatt Macy	#
928eda14cbcSMatt Macy	#     zpool import -R "$rootmnt" -N "$ZFS_RPOOL"
929eda14cbcSMatt Macy	#     zfs mount -o mountpoint=/ "${ZFS_BOOTFS}"
930eda14cbcSMatt Macy	#
931eda14cbcSMatt Macy	#   but the MOUNTPOINT prefix is preserved on descendent filesystem
932eda14cbcSMatt Macy	#   after the pivot into the regular root, which later breaks things
933eda14cbcSMatt Macy	#   like `zfs mount -a` and the /proc/self/mounts refresh.
934eda14cbcSMatt Macy	#
935eda14cbcSMatt Macy	# * Mount additional filesystems required
936eda14cbcSMatt Macy	#   Such as /usr, /var, /usr/local etc.
937eda14cbcSMatt Macy	#   NOTE: Mounted in the order specified in the
938eda14cbcSMatt Macy	#         ZFS_INITRD_ADDITIONAL_DATASETS variable so take care!
939eda14cbcSMatt Macy
940eda14cbcSMatt Macy	# Go through the complete list (recursively) of all filesystems below
941eda14cbcSMatt Macy	# the real root dataset
942*16038816SMartin Matuska	filesystems="$("${ZFS}" list -oname -tfilesystem -H -r "${ZFS_BOOTFS}")"
943*16038816SMartin Matuska	OLD_IFS="$IFS" ; IFS="
944*16038816SMartin Matuska"
945*16038816SMartin Matuska	for fs in $filesystems; do
946*16038816SMartin Matuska		IFS="$OLD_IFS" mount_fs "$fs"
947*16038816SMartin Matuska	done
948*16038816SMartin Matuska	IFS="$OLD_IFS"
949*16038816SMartin Matuska	for fs in $ZFS_INITRD_ADDITIONAL_DATASETS; do
950eda14cbcSMatt Macy		mount_fs "$fs"
951eda14cbcSMatt Macy	done
952eda14cbcSMatt Macy
953eda14cbcSMatt Macy	touch /run/zfs_unlock_complete
954eda14cbcSMatt Macy	if [ -e /run/zfs_unlock_complete_notify ]; then
955*16038816SMartin Matuska		read -r < /run/zfs_unlock_complete_notify
956eda14cbcSMatt Macy	fi
957eda14cbcSMatt Macy
958eda14cbcSMatt Macy	# ------------
959eda14cbcSMatt Macy	# Debugging information
960eda14cbcSMatt Macy	if [ -n "${ZFS_DEBUG}" ]
961eda14cbcSMatt Macy	then
962eda14cbcSMatt Macy		#exec 2>&1-
963eda14cbcSMatt Macy
964eda14cbcSMatt Macy		echo "DEBUG: imported pools:"
965eda14cbcSMatt Macy		"${ZPOOL}" list -H
966eda14cbcSMatt Macy		echo
967eda14cbcSMatt Macy
968eda14cbcSMatt Macy		echo "DEBUG: mounted ZFS filesystems:"
969eda14cbcSMatt Macy		mount | grep zfs
970eda14cbcSMatt Macy		echo
971eda14cbcSMatt Macy
972eda14cbcSMatt Macy		echo "=> waiting for ENTER before continuing because of 'zfsdebug=1'. "
973c40487d4SMatt Macy		printf "%s" "   'c' for shell, 'r' for reboot, 'ENTER' to continue. "
974c40487d4SMatt Macy		read -r b
975eda14cbcSMatt Macy
976eda14cbcSMatt Macy		[ "$b" = "c" ] && /bin/sh
977eda14cbcSMatt Macy		[ "$b" = "r" ] && reboot -f
978eda14cbcSMatt Macy
979eda14cbcSMatt Macy		set +x
980eda14cbcSMatt Macy	fi
981eda14cbcSMatt Macy
982eda14cbcSMatt Macy	# ------------
983eda14cbcSMatt Macy	# Run local bottom script
984c40487d4SMatt Macy	if command -v run_scripts > /dev/null 2>&1
985c40487d4SMatt Macy	then
986c40487d4SMatt Macy		if [ -f "/scripts/local-bottom" ] || [ -d "/scripts/local-bottom" ]
987eda14cbcSMatt Macy		then
988eda14cbcSMatt Macy			[ "$quiet" != "y" ] && \
989eda14cbcSMatt Macy			    zfs_log_begin_msg "Running /scripts/local-bottom"
990eda14cbcSMatt Macy			run_scripts /scripts/local-bottom
991eda14cbcSMatt Macy			[ "$quiet" != "y" ] && zfs_log_end_msg
992eda14cbcSMatt Macy		fi
993c40487d4SMatt Macy	fi
994eda14cbcSMatt Macy}
995