Browse Source

nfsroot: mount NFS root from udev rules

Work-in-progress to mount NFS root from udev, using the DHCP options and
allowing an override from the kernel command line. Supports NFSv4 w/locking
and NFSv2/v3 with nolock.

This is just an RFC patch, you'll note all the XXX markings where I've been
thinking about how to best do things. In addition, we really want to retry
DHCP and NFS mounting in a loop, in case the servers are down or overloaded
like when booting a full diskless cluster.
master
David Dillow 16 years ago committed by Harald Hoyer
parent
commit
f6f74096f6
  1. 5
      dracut
  2. 1
      modules.d/40nfsroot/60-nfsroot.rules
  3. 5
      modules.d/40nfsroot/check
  4. 27
      modules.d/40nfsroot/install
  5. 125
      modules.d/40nfsroot/nfsroot
  6. 9
      modules.d/40nfsroot/nfsroot-cleanup.sh

5
dracut

@ -111,10 +111,13 @@ hookdirs="pre-udev pre-mount pre-pivot mount emergency" @@ -111,10 +111,13 @@ hookdirs="pre-udev pre-mount pre-pivot mount emergency"
readonly initdir=$(mktemp -d -t initramfs.XXXXXX)
trap 'rm -rf "$initdir"' 0 # clean up after ourselves no matter how we die.

# Need to be able to have non-root users read stuff (rpcbind etc)
chmod 755 "$initdir"

export initdir hookdirs dsrc dracutmodules modules debug beverbose

# Create some directory structure first
for d in bin sbin usr/bin usr/sbin usr/lib etc proc sys sysroot dev/pts; do
for d in bin sbin usr/bin usr/sbin usr/lib etc proc sys sysroot dev/pts var/run; do
mkdir -p "$initdir/$d";
done


1
modules.d/40nfsroot/60-nfsroot.rules

@ -0,0 +1 @@ @@ -0,0 +1 @@
ACTION=="online", SUBSYSTEM=="net", RUN+="/sbin/nfsroot $env{INTERFACE}"

5
modules.d/40nfsroot/check

@ -0,0 +1,5 @@ @@ -0,0 +1,5 @@
#!/bin/sh
#
# This is not a module that should be selected when we try to
# autoconfigure the initrd
exit 1

27
modules.d/40nfsroot/install

@ -0,0 +1,27 @@ @@ -0,0 +1,27 @@
#!/bin/sh
dracut_install rpcbind rpc.statd mount.nfs mount.nfs4
dracut_install /etc/netconfig /etc/passwd /etc/services

# XXX debug stuff
dracut_install rpcinfo ping strace dmesg nc free df

dracut_install rpc.idmapd /etc/idmapd.conf

instmods nfs
inst_rules "$moddir/60-nfsroot.rules"
inst_hook pre-pivot 70 "$moddir/nfsroot-cleanup.sh"
inst "$moddir/nfsroot" "/sbin/nfsroot"
mkdir -p "$initdir/var/lib/nfs/rpc_pipefs"
mkdir -p "$initdir/var/lib/rpcbind"
mkdir -p "$initdir/var/lib/nfs/statd/sm"

# XXX debug
mkdir -p "$initdir/mnt"

# 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
#echo "rpc:x:32:32:Rpcbind:/var/lib/rpcbind:/bin/false" >> "$initdir/etc/passwd"

# rpc user needs to be able to write to this directory to save the warmstart
# file
chmod 777 "$initdir/var/lib/rpcbind"

125
modules.d/40nfsroot/nfsroot

@ -0,0 +1,125 @@ @@ -0,0 +1,125 @@
#!/bin/sh

. /lib/dracut-lib

PATH=$PATH:/sbin:/usr/sbin

# XXX needs error handling like ifup/dhclient-script

# XXX need to lock our attempts if we're doing the mount here

getarg netdebug && {
exec > /nfsroot.$1.$$.out
exec 2>> /nfsroot.$1.$$.out
set -x
}

[ "$NFS_LOCKED" ] || {
NFS_LOCKED=true
export NFS_LOCKED
exec flock -xo /nfs.lock -c "$0 $*"
exit 1
}

[ -e /nfsdone ] && exit 0

nfs_done() {
>/nfsdone
exit 0
}

root=$(getarg root)
case $root in
nfs|/dev/nfs) type=nfs ;;
nfs4|/dev/nfs4) type=nfs4 ;;
auto|'') type=auto ;;
esac

rootfstype=$(getarg rootfstype)
case $rootfstype in
nfs|nfs4|auto) type=$rootfstype ;;
esac

# If we're not doing NFS at all, don't keep banging our head
[ -n "$type" ] || nfs_done

[ -e /net.$1.dhcpopts ] && . /net.$1.dhcpopts

nfsroot=$(getarg nfsroot)
[ -n "$nfsroot" ] || nfsroot="$new_root_path"
[ -n "$nfsroot" ] || nfs_done

# check for IP address at front, if there is none, use
# new_dhcp_server_identifier
#
# XXX kernel nfsroot uses , to separate NFS options at end
#
nfsserver=${nfsroot%%:*}; nfsroot=${nfsroot#*:}
nfspath=${nfsroot%%:*}
flags=${nfsroot#*:}
[ "$nfsserver" = "$nfspath" ] && nfsserver=$new_dhcp_server_identifier
[ "$nfspath" = "$flags" ] && unset flags

[ -n "$nfsserver" ] || no_nfs

# look through the flags and see if any are overridden by the command line
while [ -n "$flags" ]; do
f=${flags%%,*}; flags=${flags#*,}
[ "$f" = "nfs" -o "$f" = "nfs4" ] && {
[ "$type" = "auto" ] && type=$f
continue
}
[ "$f" = "ro" -o "$f" = "rw" ] && {
nfsrw=$f
continue
}
[ "$f" = "lock" -o "$f" = "nolock" ] && {
nfslock=$f
continue
}
nfsflags=${nfsflags+$nfsflags,}$f
done

getarg ro && nfsrw=ro
getarg rw && nfsrw=rw
nfsflags=${nfsflags+$nfsflags,}${nfsrw}

# load our modules explicitly, so we can fail fast in the future
modprobe nfs || nfs_done

# XXX don't forget to move /var/lib/nfs/rpc_pipefs to new /
# XXX need host name set before now?

# Start rpcbind and rpc.statd as mount won't let us use locks on a NFSv4
# filesystem without talking to them, even though they are unneeded
# XXX occasionally saw 'rpcbind: fork failed: No such device' -- why?
[ -n "$(pidof rpcbind)" ] || rpcbind
[ -n "$(pidof rpc.statd)" ] || rpc.statd

# XXX should I do rpc.idmapd here, or wait and start in the new root
# XXX waiting assumes root can read everything it needs right up until
# XXX we start it...

# XXX really, want to retry in a loop I think, but not here...

[ "$type" = "nfs4" -o "$type" = "auto" ] && {
# XXX really needed? Do we need non-root users before we start it in
# XXX the real root image?
[ -n "$(pidof rpc.idmapd)" ] || rpc.idmapd

# NFSv4 does locks internally
mount -t nfs4 -o${nfsflags}${nfslock+,$nfslock} \
$nfsserver:$nfspath /sysroot && nfs_done

# If we're specified to be NFSv4, then stop when we fail
# Don't mark us done, as this may be transient
[ "$type" = "nfs4" ] && exit 0
}

# we're NFSv{2,3} or auto and NFSv4 failed. We don't support using locks
# on NFSv{2,3} because that requires a helper to transfer the rpcbind state
# rpcbind to the new root
[ -z "$nfslock" -o "$nfslock" = "lock" ] &&
echo "Locks unsupported on NFSv{2,3}, using nolock" 1>&2
mount -t nfs -onolock,$nfsflags $nfsserver:$nfspath /sysroot && nfs_done
exit 0

9
modules.d/40nfsroot/nfsroot-cleanup.sh

@ -0,0 +1,9 @@ @@ -0,0 +1,9 @@
#!/bin/sh

pid=$(pidof rpc.statd)
[ -n "$pid" ] && kill $pid

pid=$(pidof rpcbind)
[ -n "$pid" ] && kill $pid

mount --move /var/lib/nfs/rpc_pipefs $NEWROOT/var/lib/nfs/rpc_pipefs
Loading…
Cancel
Save