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