summaryrefslogtreecommitdiffstats
path: root/modules.d/95nfs
diff options
context:
space:
mode:
Diffstat (limited to 'modules.d/95nfs')
-rwxr-xr-xmodules.d/95nfs/module-setup.sh138
-rwxr-xr-xmodules.d/95nfs/nfs-lib.sh157
-rwxr-xr-xmodules.d/95nfs/nfs-start-rpc.sh23
-rwxr-xr-xmodules.d/95nfs/nfsroot-cleanup.sh29
-rwxr-xr-xmodules.d/95nfs/nfsroot.sh30
-rwxr-xr-xmodules.d/95nfs/parse-nfsroot.sh128
6 files changed, 505 insertions, 0 deletions
diff --git a/modules.d/95nfs/module-setup.sh b/modules.d/95nfs/module-setup.sh
new file mode 100755
index 0000000..16bafe3
--- /dev/null
+++ b/modules.d/95nfs/module-setup.sh
@@ -0,0 +1,138 @@
+#!/bin/bash
+
+# return value:
+# 'nfs4': Only nfs4 founded
+# 'nfs': nfs with version < 4 founded
+# '': No nfs founded
+get_nfs_type() {
+ local _nfs _nfs4
+
+ for fs in "${host_fs_types[@]}"; do
+ [[ $fs == "nfs" ]] && _nfs=1
+ [[ $fs == "nfs3" ]] && _nfs=1
+ [[ $fs == "nfs4" ]] && _nfs4=1
+ done
+
+ [[ "$_nfs" ]] && echo "nfs" && return
+ [[ "$_nfs4" ]] && echo "nfs4" && return
+}
+
+# called by dracut
+check() {
+ # If our prerequisites are not met, fail anyways.
+ require_any_binary rpcbind portmap || return 1
+ require_binaries rpc.statd mount.nfs mount.nfs4 umount sed chmod chown || return 1
+
+ [[ $hostonly ]] || [[ $mount_needs ]] && {
+ [[ "$(get_nfs_type)" ]] && return 0
+ return 255
+ }
+ return 0
+}
+
+# called by dracut
+depends() {
+ # We depend on network modules being loaded
+ echo network
+}
+
+# called by dracut
+installkernel() {
+ hostonly=$(optional_hostonly) instmods '=net/sunrpc' '=fs/nfs' ipv6 nfs_acl nfs_layout_nfsv41_files
+}
+
+cmdline() {
+ local nfs_device
+ local nfs_options
+ local nfs_root
+ local nfs_address
+ local lookup
+
+ ### nfsroot= ###
+ nfs_device=$(findmnt -t nfs4 -n -o SOURCE /)
+ if [ -n "$nfs_device" ]; then
+ nfs_root="root=nfs4:$nfs_device"
+ else
+ nfs_device=$(findmnt -t nfs -n -o SOURCE /)
+ [ -z "$nfs_device" ] && return
+ nfs_root="root=nfs:$nfs_device"
+ fi
+ nfs_options=$(findmnt -t nfs4,nfs -n -o OPTIONS /)
+ [ -n "$nfs_options" ] && nfs_root="$nfs_root:$nfs_options"
+ echo "$nfs_root"
+
+ ### ip= ###
+ if [[ $nfs_device =~ [0-9]*\.[0-9]*\.[0-9]*.[0-9]* ]] || [[ $nfs_device =~ \[[^]]*\] ]]; then
+ nfs_address="${nfs_device%%:*}"
+ else
+ lookup=$(host "${nfs_device%%:*}" | grep " address " | head -n1)
+ nfs_address=${lookup##* }
+ fi
+
+ [[ $nfs_address ]] || return
+ ip_params_for_remote_addr "$nfs_address"
+}
+
+# called by dracut
+install() {
+ local _nsslibs
+ inst_multiple -o rpc.idmapd mount.nfs mount.nfs4 umount sed /etc/netconfig chmod chown "$tmpfilesdir/rpcbind.conf"
+ inst_multiple -o /etc/idmapd.conf
+ inst_multiple -o /etc/services /etc/nsswitch.conf /etc/rpc /etc/protocols
+ inst_multiple -o /usr/etc/services /usr/etc/nsswitch.conf /usr/etc/rpc /usr/etc/protocols
+
+ if [[ $hostonly_cmdline == "yes" ]]; then
+ local _netconf
+ _netconf="$(cmdline)"
+ [[ $_netconf ]] && printf "%s\n" "$_netconf" >> "${initdir}/etc/cmdline.d/95nfs.conf"
+ fi
+
+ if [[ -f $dracutsysrootdir/lib/modprobe.d/nfs.conf ]]; then
+ inst_multiple /lib/modprobe.d/nfs.conf
+ else
+ [[ -d $initdir/etc/modprobe.d ]] || mkdir -p "$initdir"/etc/modprobe.d
+ echo "alias nfs4 nfs" > "$initdir"/etc/modprobe.d/nfs.conf
+ fi
+
+ inst_libdir_file 'libnfsidmap_nsswitch.so*' 'libnfsidmap/*.so' 'libnfsidmap*.so*'
+
+ _nsslibs=$(
+ cat "$dracutsysrootdir"/{,usr/}etc/nsswitch.conf 2> /dev/null \
+ | sed -e '/^#/d' -e 's/^.*://' -e 's/\[NOTFOUND=return\]//' \
+ | tr -s '[:space:]' '\n' | sort -u | tr -s '[:space:]' '|'
+ )
+ _nsslibs=${_nsslibs#|}
+ _nsslibs=${_nsslibs%|}
+
+ inst_libdir_file -n "$_nsslibs" 'libnss_*.so*'
+
+ inst_hook cmdline 90 "$moddir/parse-nfsroot.sh"
+ inst_hook pre-udev 99 "$moddir/nfs-start-rpc.sh"
+ inst_hook cleanup 99 "$moddir/nfsroot-cleanup.sh"
+ inst "$moddir/nfsroot.sh" "/sbin/nfsroot"
+
+ # For strict hostonly, only install rpcbind for NFS < 4
+ if [[ $hostonly_mode != "strict" ]] || [[ "$(get_nfs_type)" != "nfs4" ]]; then
+ inst_multiple -o portmap rpcbind rpc.statd
+ fi
+
+ inst "$moddir/nfs-lib.sh" "/lib/nfs-lib.sh"
+ mkdir -m 0755 -p "$initdir/var/lib/nfs"
+ mkdir -m 0755 -p "$initdir/var/lib/nfs/rpc_pipefs"
+ mkdir -m 0770 -p "$initdir/var/lib/rpcbind"
+ [ -d "/var/lib/nfs/statd/sm" ] && mkdir -m 0755 -p "$initdir/var/lib/nfs/statd/sm"
+ [ -d "/var/lib/nfs/sm" ] && mkdir -m 0755 -p "$initdir/var/lib/nfs/sm"
+
+ # Rather than copy the passwd file in, just set a user for rpcbind
+ # We'll save the state and restart the daemon from the root anyway
+ grep -E '^nfsnobody:|^rpc:|^rpcuser:' "$dracutsysrootdir"/etc/passwd >> "$initdir/etc/passwd"
+ grep -E '^nogroup:|^rpc:|^nobody:' "$dracutsysrootdir"/etc/group >> "$initdir/etc/group"
+
+ # rpc user needs to be able to write to this directory to save the warmstart
+ # file
+ chmod 770 "$initdir/var/lib/rpcbind"
+ grep -q '^rpc:' "$dracutsysrootdir"/etc/passwd \
+ && grep -q '^rpc:' "$dracutsysrootdir"/etc/group
+
+ dracut_need_initqueue
+}
diff --git a/modules.d/95nfs/nfs-lib.sh b/modules.d/95nfs/nfs-lib.sh
new file mode 100755
index 0000000..f000671
--- /dev/null
+++ b/modules.d/95nfs/nfs-lib.sh
@@ -0,0 +1,157 @@
+#!/bin/sh
+
+type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh
+. /lib/net-lib.sh
+
+# TODO: make these things not pollute the calling namespace
+
+# nfs_to_var NFSROOT [NETIF]
+# use NFSROOT to set $nfs, $server, $path, and $options.
+# NFSROOT is something like: nfs[4]:<server>:/<path>[:<options>|,<options>]
+# NETIF is used to get information from DHCP options, if needed.
+nfs_to_var() {
+ # Unfortunately, there's multiple styles of nfs "URL" in use, so we need
+ # extra functions to parse them into $nfs, $server, $path, and $options.
+ # FIXME: local netif=${2:-$netif}?
+ case "$1" in
+ nfs://*) rfc2224_nfs_to_var "$1" ;;
+ nfs:*[*) anaconda_nfsv6_to_var "$1" ;;
+ nfs:*:*:/*) anaconda_nfs_to_var "$1" ;;
+ *) nfsroot_to_var "$1" ;;
+ esac
+ # if anything's missing, try to fill it in from DHCP options
+ if [ -z "$server" ] || [ -z "$path" ]; then nfsroot_from_dhcp "$2"; fi
+ # if there's a "%s" in the path, replace it with the hostname/IP
+ if strstr "$path" "%s"; then
+ local node=""
+ read -r node < /proc/sys/kernel/hostname
+ [ "$node" = "(none)" ] && node=$(get_ip "$2")
+ path=${path%%%s*}$node${path#*%s} # replace only the first %s
+ fi
+}
+
+# root=nfs:[<server-ip>:]<root-dir>[:<nfs-options>]
+# root=nfs4:[<server-ip>:]<root-dir>[:<nfs-options>]
+nfsroot_to_var() {
+ # strip nfs[4]:
+ local arg="$*:"
+ nfs="${arg%%:*}"
+ arg="${arg##"$nfs":}"
+
+ # check if we have a server
+ if strstr "$arg" ':/'; then
+ server="${arg%%:/*}"
+ arg="/${arg##*:/}"
+ fi
+
+ path="${arg%%:*}"
+
+ # rest are options
+ options="${arg##"$path"}"
+ # strip leading ":"
+ options="${options##:}"
+ # strip ":"
+ options="${options%%:}"
+
+ # Does it really start with '/'?
+ [ -n "${path%%/*}" ] && path="error"
+
+ #Fix kernel legacy style separating path and options with ','
+ if [ "$path" != "${path#*,}" ]; then
+ options=${path#*,}
+ path=${path%%,*}
+ fi
+}
+
+# RFC2224: nfs://<server>[:<port>]/<path>
+rfc2224_nfs_to_var() {
+ nfs="nfs"
+ server="${1#nfs://}"
+ path="/${server#*/}"
+ server="${server%%/*}"
+ server="${server%%:}" # anaconda compat (nfs://<server>:/<path>)
+ local port="${server##*:}"
+ [ "$port" != "$server" ] && options="port=$port"
+}
+
+# Anaconda-style path with options: nfs:<options>:<server>:/<path>
+# (without mount options, anaconda is the same as dracut)
+anaconda_nfs_to_var() {
+ nfs="nfs"
+ options="${1#nfs:}"
+ server="${options#*:}"
+ server="${server%:/*}"
+ options="${options%%:*}"
+ path="/${1##*:/}"
+}
+
+# IPv6 nfs path will be treated separately
+anaconda_nfsv6_to_var() {
+ nfs="nfs"
+ path="$1"
+ options="${path#*:/}"
+ path="/${options%%:*}"
+ server="${1#*nfs:}"
+ if str_starts "$server" '['; then
+ server="${server%:/*}"
+ options="${options#*:*}"
+ else
+ server="${server%:/*}"
+ options="${server%%:*}"
+ server="${server#*:}"
+ fi
+}
+
+# nfsroot_from_dhcp NETIF
+# fill in missing server/path from DHCP options.
+nfsroot_from_dhcp() {
+ local f
+ for f in /tmp/net.$1.override /tmp/dhclient.$1.dhcpopts; do
+ # shellcheck disable=SC1090
+ [ -f "$f" ] && . "$f"
+ done
+ [ -n "$new_root_path" ] && nfsroot_to_var "$nfs:$new_root_path"
+ [ -z "$path" ] && [ "$(getarg root=)" = "/dev/nfs" ] && path=/tftpboot/%s
+ [ -z "$server" ] && server=$srv
+ [ -z "$server" ] && server=$new_next_server
+ [ -z "$server" ] && server=$new_dhcp_server_identifier
+ [ -z "$server" ] && server=${new_root_path%%:*}
+}
+
+# Look through $options, fix "rw"/"ro", move "lock"/"nolock" to $nfslock
+munge_nfs_options() {
+ local f="" flags="" nfsrw="ro" OLDIFS="$IFS"
+ IFS=,
+ for f in $options; do
+ case $f in
+ ro | rw) nfsrw=$f ;;
+ lock | nolock) nfslock=$f ;;
+ *) flags=${flags:+$flags,}$f ;;
+ esac
+ done
+ IFS="$OLDIFS"
+
+ # Override rw/ro if set on cmdline
+ getarg ro > /dev/null && nfsrw=ro
+ getarg rw > /dev/null && nfsrw=rw
+
+ options=$nfsrw${flags:+,$flags}
+}
+
+# mount_nfs NFSROOT MNTDIR [NETIF]
+mount_nfs() {
+ local nfsroot="$1" mntdir="$2" netif="$3"
+ local nfs="" server="" path="" options=""
+ nfs_to_var "$nfsroot" "$netif"
+ munge_nfs_options
+ if [ "$nfs" = "nfs4" ]; then
+ options=$options${nfslock:+,$nfslock}
+ else
+ # NFSv{2,3} doesn't support using locks as it requires a helper to
+ # transfer the rpcbind state to the new root
+ [ "$nfslock" = "lock" ] \
+ && warn "Locks unsupported on NFSv{2,3}, using nolock" 1>&2
+ options=$options,nolock
+ fi
+ mount -t "$nfs" -o"$options" "$server:$path" "$mntdir"
+}
diff --git a/modules.d/95nfs/nfs-start-rpc.sh b/modules.d/95nfs/nfs-start-rpc.sh
new file mode 100755
index 0000000..52f6a4d
--- /dev/null
+++ b/modules.d/95nfs/nfs-start-rpc.sh
@@ -0,0 +1,23 @@
+#!/bin/sh
+
+if load_fstype sunrpc rpc_pipefs; then
+ [ ! -d /var/lib/nfs/rpc_pipefs/nfs ] \
+ && mount -t rpc_pipefs rpc_pipefs /var/lib/nfs/rpc_pipefs
+
+ # Start rpcbind or rpcbind
+ # FIXME occasionally saw 'rpcbind: fork failed: No such device' -- why?
+ command -v portmap > /dev/null && [ -z "$(pidof portmap)" ] && portmap
+ if command -v rpcbind > /dev/null && [ -z "$(pidof rpcbind)" ]; then
+ mkdir -p /run/rpcbind
+ chown rpc:rpc /run/rpcbind
+ rpcbind
+ fi
+
+ # Start rpc.statd as mount won't let us use locks on a NFSv4
+ # filesystem without talking to it. NFSv4 does locks internally,
+ # rpc.lockd isn't needed
+ command -v rpc.statd > /dev/null && [ -z "$(pidof rpc.statd)" ] && rpc.statd
+ command -v rpc.idmapd > /dev/null && [ -z "$(pidof rpc.idmapd)" ] && rpc.idmapd
+else
+ warn 'Kernel module "sunrpc" not in the initramfs, or support for filesystem "rpc_pipefs" missing!'
+fi
diff --git a/modules.d/95nfs/nfsroot-cleanup.sh b/modules.d/95nfs/nfsroot-cleanup.sh
new file mode 100755
index 0000000..d99519b
--- /dev/null
+++ b/modules.d/95nfs/nfsroot-cleanup.sh
@@ -0,0 +1,29 @@
+#!/bin/sh
+
+type incol2 > /dev/null 2>&1 || . /lib/dracut-lib.sh
+
+[ -f /tmp/nfs.rpc_pipefs_path ] && read -r rpcpipefspath < /tmp/nfs.rpc_pipefs_path
+[ -z "$rpcpipefspath" ] && rpcpipefspath=var/lib/nfs/rpc_pipefs
+
+pid=$(pidof rpc.statd)
+[ -n "$pid" ] && kill "$pid"
+
+pid=$(pidof rpc.idmapd)
+[ -n "$pid" ] && kill "$pid"
+
+pid=$(pidof rpcbind)
+[ -n "$pid" ] && kill "$pid"
+
+if incol2 /proc/mounts /var/lib/nfs/rpc_pipefs; then
+ # try to create the destination directory
+ [ -d "$NEWROOT"/$rpcpipefspath ] \
+ || mkdir -m 0755 -p "$NEWROOT"/$rpcpipefspath 2> /dev/null
+
+ if [ -d "$NEWROOT"/$rpcpipefspath ]; then
+ # mount --move does not seem to work???
+ mount --bind /var/lib/nfs/rpc_pipefs "$NEWROOT"/$rpcpipefspath
+ umount /var/lib/nfs/rpc_pipefs 2> /dev/null
+ else
+ umount /var/lib/nfs/rpc_pipefs 2> /dev/null
+ fi
+fi
diff --git a/modules.d/95nfs/nfsroot.sh b/modules.d/95nfs/nfsroot.sh
new file mode 100755
index 0000000..794e0d8
--- /dev/null
+++ b/modules.d/95nfs/nfsroot.sh
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh
+. /lib/nfs-lib.sh
+
+[ "$#" = 3 ] || exit 1
+
+# root is in the form root=nfs[4]:[server:]path[:options], either from
+# cmdline or dhcp root-path
+netif="$1"
+root="$2"
+NEWROOT="$3"
+
+nfs_to_var "$root" "$netif"
+[ -z "$server" ] && die "Required parameter 'server' is missing"
+
+mount_nfs "$root" "$NEWROOT" "$netif" && {
+ [ -e /dev/root ] || ln -s null /dev/root
+ [ -e /dev/nfs ] || ln -s null /dev/nfs
+}
+
+[ -f "$NEWROOT"/etc/fstab ] && cat "$NEWROOT"/etc/fstab > /dev/null
+
+# inject new exit_if_exists
+# shellcheck disable=SC2016
+echo 'settle_exit_if_exists="--exit-if-exists=/dev/root"; rm -- "$job"' > "$hookdir"/initqueue/nfs.sh
+# force udevsettle to break
+: > "$hookdir"/initqueue/work
+
+need_shutdown
diff --git a/modules.d/95nfs/parse-nfsroot.sh b/modules.d/95nfs/parse-nfsroot.sh
new file mode 100755
index 0000000..0c8dbbb
--- /dev/null
+++ b/modules.d/95nfs/parse-nfsroot.sh
@@ -0,0 +1,128 @@
+#!/bin/sh
+#
+# Preferred format:
+# root=nfs[4]:[server:]path[:options]
+#
+# This syntax can come from DHCP root-path as well.
+#
+# Legacy format:
+# root=/dev/nfs nfsroot=[server:]path[,options]
+#
+# In Legacy root=/dev/nfs mode, if the 'nfsroot' parameter is not given
+# on the command line or is empty, the dhcp root-path is used as
+# [server:]path[:options] or the default "/tftpboot/%s" will be used.
+#
+# If server is unspecified it will be pulled from one of the following
+# sources, in order:
+# static ip= option on kernel command line
+# DHCP next-server option
+# DHCP server-id option
+# DHCP root-path option
+#
+# NFSv4 is only used if explicitly requested with nfs4: prefix, otherwise
+# NFSv3 is used.
+#
+
+type getarg > /dev/null 2>&1 || . /lib/dracut-lib.sh
+. /lib/nfs-lib.sh
+
+# This script is sourced, so root should be set. But let's be paranoid
+[ -z "$root" ] && root=$(getarg root=)
+[ -z "$nfsroot" ] && nfsroot=$(getarg nfsroot=)
+
+[ -n "$netroot" ] && oldnetroot="$netroot"
+
+# netroot= cmdline argument must be ignored, but must be used if
+# we're inside netroot to parse dhcp root-path
+if [ -n "$netroot" ]; then
+ for n in $(getargs netroot=); do
+ [ "$n" = "$netroot" ] && break
+ done
+ if [ "$n" = "$netroot" ]; then
+ #warn "Ignoring netroot argument for NFS"
+ netroot=$root
+ fi
+else
+ netroot=$root
+fi
+
+# LEGACY: nfsroot= is valid only if root=/dev/nfs
+if [ -n "$nfsroot" ]; then
+ # @deprecated
+ warn "Argument nfsroot is deprecated and might be removed in a future release. See 'man dracut.kernel' for more information."
+ if [ "$(getarg root=)" != "/dev/nfs" ]; then
+ die "Argument nfsroot only accepted for legacy root=/dev/nfs"
+ fi
+ netroot=nfs:$nfsroot
+fi
+
+case "$netroot" in
+ /dev/nfs) netroot=nfs ;;
+ /dev/*)
+ if [ -n "$oldnetroot" ]; then
+ netroot="$oldnetroot"
+ else
+ unset netroot
+ fi
+ return
+ ;;
+ # LEGACY: root=<server-ip>:/<path
+ [0-9]*:/* | [0-9]*\.[0-9]*\.[0-9]*[!:] | /*)
+ netroot=nfs:$netroot
+ ;;
+esac
+
+# Continue if nfs
+case "${netroot%%:*}" in
+ nfs | nfs4 | /dev/nfs) ;;
+ *)
+ if [ -n "$oldnetroot" ]; then
+ netroot="$oldnetroot"
+ else
+ unset netroot
+ fi
+ return
+ ;;
+esac
+
+# Check required arguments
+
+if nfsdomain=$(getarg rd.nfs.domain -d rd_NFS_DOMAIN); then
+ if [ -f /etc/idmapd.conf ]; then
+ sed -i -e \
+ "s/^[[:space:]#]*Domain[[:space:]]*=.*/Domain = $nfsdomain/g" \
+ /etc/idmapd.conf
+ fi
+ # and even again after the sed, in case it was not yet specified
+ echo "Domain = $nfsdomain" >> /etc/idmapd.conf
+fi
+
+nfsroot_to_var "$netroot"
+[ "$path" = "error" ] && die "Argument nfsroot must contain a valid path!"
+
+# Set fstype, might help somewhere
+fstype=${nfs#/dev/}
+
+# Rewrite root so we don't have to parse this ugliness later on again
+netroot="$fstype:$server:$path:$options"
+
+# If we don't have a server, we need dhcp
+if [ -z "$server" ]; then
+ # shellcheck disable=SC2034
+ DHCPORSERVER="1"
+fi
+
+# Done, all good!
+# shellcheck disable=SC2034
+rootok=1
+
+# Shut up init error check or make sure that block parser won't get
+# confused by having /dev/nfs[4]
+root="$fstype"
+
+# shellcheck disable=SC2016
+echo '[ -e $NEWROOT/proc ]' > "$hookdir"/initqueue/finished/nfsroot.sh
+
+mkdir -p /var/lib/rpcbind
+chown rpc:rpc /var/lib/rpcbind
+chmod 770 /var/lib/rpcbind