netroot: Introduce detailed cmdline parsers

This introduces detailed cmdline parsing, warning or aborting if the
cmdline does not contain arguments according to the spec.

Makeing sure the parsers don't just call getarg for netroot et al,
allows their reuse inside netroot to analyse dhcp root-path as well.
Hence we can get rid of the current netroot hooks. The hook itself stays
in order to add further modules which should run before netroot handlers
are called.

This has one drawback: nfsroot needs some more logic to handle nfs
specific data inside dhcp root-path.

The parsers have been writting according to current discussions about
cmdline arguments. This lead to the "discovery" that some test-cases
violate the spec. These tests have not been removed, but change to
"must fail".
This commit is contained in:
Philippe Seewer 2009-06-16 18:25:16 +02:00
parent 9f10836d63
commit 50e7ff76de
16 changed files with 474 additions and 317 deletions

View File

@ -1,16 +1,23 @@
# We should go last, and default the root if needed
if [ -z "$root" -a -z "$netroot" ]; then
rootok=1
root=dhcp
netroot=dhcp
fi
if [ "$root" = "dhcp" -a -z "$netroot" ]; then
rootok=1
netroot=dhcp
unset root
fi
# Cleanup any coversions from root->netroot if they are the same
if [ "$netroot" = "$root" ]; then
unset root
if [ "$netroot" = "dhcp" -a -z "$root" ]; then
rootok=1
root=dhcp
fi
if [ -n "$NEEDDHCP" ] ; then
rootok=1
root=dhcp
#Don't overwrite netroot here, as it might contain something useful
fi

View File

@ -32,19 +32,39 @@ fi
netif=$1
[ -e /tmp/dhclient.$netif.dhcpopts ] && . /tmp/dhclient.$netif.dhcpopts
# Figure out the handler for root=dhcp by recalling all netroot cmdline
# handlers
if [ "$netroot" = "dhcp" ] ; then
# Unset root so we can check later
unset root
# Now, let the installed network root handlers figure this out
#
source_all netroot
# Load dhcp options
[ -e /tmp/dhclient.$netif.dhcpopts ] && . /tmp/dhclient.$netif.dhcpopts
# If we didn't get a handler set, then we're done
#
if [ -z "$handler" ]; then
# XXX informative error message?
exit 0
# Set netroot to new_root_path, so cmdline parsers don't call
netroot=$new_root_path
for f in ./cmdline/90*.sh; do
[ -f "$f" ] && . "$f";
done
else
rootok="1"
fi
# Check: do we really know how to handle (net)root?
[ -z "$root" ] && die "No or empty root= argument"
[ -z "$rootok" ] && die "Don't know how to handle 'root=$root'"
handler=${netroot%%:*}
handler=${handler%%4}
handler="/sbin/${handler}root"
if [ -z "$netroot" ] || [ ! -e "$handler" ] ; then
die "No handler for netroot type '$netroot'"
fi
# Source netroot hooks before we start the handler
source_all netroot
# Run the handler; don't store the root, it may change from device to device
# XXX other variables to export?
if $handler $netif $netroot $NEWROOT; then

View File

@ -4,6 +4,5 @@ inst iscsistart
inst hostname
inst iscsi-iname
inst_hook cmdline 90 "$moddir/parse-iscsiroot.sh"
inst_hook netroot 90 "$moddir/iscsi-netroot.sh"
inst "$moddir/iscsiroot" "/sbin/iscsiroot"
instmods iscsi_tcp crc32c

View File

@ -1,13 +0,0 @@
if [ "$root" = "dhcp" ]; then
if [ -n "$new_root_path" -a -z "${new_root_path%%iscsi:*}" ]; then
root="$new_root_path"
fi
fi
if [ -z "${root%iscsi:*}" ]; then
handler=/sbin/iscsiroot
fi
if getarg iscsiroot >/dev/null; then
handler=/sbin/iscsiroot
fi

View File

@ -10,8 +10,6 @@
PATH=$PATH:/sbin:/usr/sbin
# XXX needs error handling like ifup/dhclient-script
if getarg rdnetdebug; then
exec > /tmp/iscsiroot.$1.$$.out
exec 2>> /tmp/iscsiroot.$1.$$.out
@ -37,7 +35,11 @@ for conf in conf/conf.d/*; do
[ -f ${conf} ] && . ${conf}
done
modprobe iscsi_tcp
# If it's not iscsi we don't continue
[ "${root%%:*}" = "iscsi" ] || exit 1
# XXX modprobe crc32c should go in the cmdline parser, but I haven't yet
# figured out a way how to check whether this is built-in or not
modprobe crc32c
if getarg iscsi_firmware ; then

View File

@ -1,32 +1,75 @@
# XXX actually we could, if we move to root=XXX and netroot=XXX, then
# you could do root=LABEL=/ iscsiroot=XXX, or netroot=iscsi:XXX
#
#!/bin/sh
#
# Preferred format:
# root=iscsi:[<servername>]:[<protocol>]:[<port>]:[<LUN>]:<targetname>
# root=iscsi:[<servername>]:[<protocol>]:[<port>]:[<LUN>]:<targetname>
# [root=*] netroot=iscsi:[<servername>]:[<protocol>]:[<port>]:[<LUN>]:<targetname>
#
# Legacy formats:
# iscsiroot=[<servername>]:[<protocol>]:[<port>]:[<LUN>]:<targetname>
# root=dhcp iscsiroot=[<servername>]:[<protocol>]:[<port>]:[<LUN>]:<targetname>
# root=iscsi iscsiroot=[<servername>]:[<protocol>]:[<port>]:[<LUN>]:<targetname>
# root=??? iscsi_initiator= iscsi_target_name= iscsi_target_ip= iscsi_target_port= iscsi_target_group= iscsi_username= iscsi_password= iscsi_in_username= iscsi_in_password=
# root=??? iscsi_firmware
# [net]root=[iscsi] iscsiroot=[<servername>]:[<protocol>]:[<port>]:[<LUN>]:<targetname>
# [net]root=[iscsi] iscsi_firmware
#
# root= takes precedence over netroot= if root=iscsi[...]
#
case "$root" in
iscsi|dhcp|'')
if getarg iscsiroot= > /dev/null; then
root=iscsi:$(getarg iscsiroot=)
fi
;;
esac
# Don't continue if root is ok
[ -n "$rootok" ] && return
if [ "${root%%:*}" = "iscsi" ]; then
# XXX validate options here?
# XXX generate udev rules?
rootok=1
netroot=iscsi
# This script is sourced, so root should be set. But let's be paranoid
[ -z "$root" ] && root=$(getarg root=)
[ -z "$netroot" ] && netroot=$(getarg netroot=)
[ -z "$iscsiroot" ] && iscsiroot=$(getarg iscsiroot=)
[ -z "$iscsi_firmware" ] && getarg iscsi_firmware && iscsi_firmware="1"
[ -n "$iscsiroot" ] && [ -n "$iscsi_firmware" ] && die "Mixing iscsiroot and iscsi_firmware is dangerous"
# Root takes precedence over netroot
if [ "${root%%:*}" = "iscsi" ] ; then
if [ -n "$netroot" ] ; then
echo "Warning: root takes precedence over netroot. Ignoring netroot"
fi
netroot=$root
fi
if getarg iscsiroot; then
netroot=iscsi
# If it's not empty or iscsi we don't continue
[ -z "$netroot" ] || [ "${netroot%%:*}" = "iscsi" ] || return
if [ -n "$iscsiroot" ] ; then
[ -z "$netroot" ] && netroot=$root
# @deprecated
echo "Warning: Argument isciroot is deprecated and might be removed in a future"
echo "release. See http://apps.sourceforge.net/trac/dracut/wiki/commandline for"
echo "more information."
# Accept iscsiroot argument?
[ -z "$netroot" ] || [ "$netroot" = "iscsi" ] || \
die "Argument iscsiroot only accepted for empty root= or [net]root=iscsi"
# Override root with iscsiroot content?
[ -z "$netroot" ] || [ "$netroot" = "iscsi" ] && netroot=iscsi:$iscsiroot
fi
# iscsi_firmware does not need argument checking
if [ -n "$iscsi_firmware" ] ; then
netroot=${netroot:-iscsi}
fi
# If it's not iscsi we don't continue
[ "${netroot%%:*}" = "iscsi" ] || return
# Check required arguments. there's only one, but it's at the end
if [ -z "$iscsi_firmware" ] ; then
case "${netroot##iscsi:*:*:*:*:}" in
$netroot|'') die "Argument targetname for iscsiroot is missing";;
esac
fi
# ISCSI actually supported?
[ -e /sys/devices/virtual/iscsi_transport ] || modprobe iscsi_tcp || die "iscsiroot requested but kernel/initrd does not support iscsi"
# Done, all good!
rootok=1
# Shut up init error check
[ -z "$root" ] && root="iscsi"

View File

@ -2,6 +2,5 @@
inst nbd-client
inst_hook cmdline 90 "$moddir/parse-nbdroot.sh"
inst_hook netroot 90 "$moddir/nbd-netroot.sh"
inst "$moddir/nbdroot" "/sbin/nbdroot"
instmods nbd

View File

@ -1,9 +0,0 @@
if [ "$netroot" = "dhcp" ]; then
if [ -n "$new_root_path" -a -z "${new_root_path%%nbd:*}" ]; then
netroot="$new_root_path"
fi
fi
if [ -z "${netroot%nbd:*}" ]; then
handler=/sbin/nbdroot
fi

View File

@ -4,8 +4,6 @@
PATH=$PATH:/sbin:/usr/sbin
# XXX needs error handling like ifup/dhclient-script
if getarg rdnetdebug; then
exec > /tmp/nbdroot.$1.$$.out
exec 2>> /tmp/nbdroot.$1.$$.out
@ -21,11 +19,14 @@ fi
# Huh? Empty $3?
[ -z "$3" ] && exit 1
# root is in the form root=nbd:server:port:fstype:fsopts:nbdopts
# root is in the form root=nbd:srv:port[:fstype[:rootflags[:nbdopts]]]
netif="$1"
root="$2"
NEWROOT="$3"
# If it's not nbd we don't continue
[ "${root%%:*}" = "nbd" ] || return
root=${root#nbd:}
nbdserver=${root%%:*}; root=${root#*:}
nbdport=${root%%:*}; root=${root#*:}
@ -81,8 +82,6 @@ getarg ro && nbdrw=ro
getarg rw && nbdrw=rw
fsopts=${fsopts+$fsopts,}${nbdrw}
incol2 /proc/devices nbd || modprobe nbd || exit 1
# XXX better way to wait for the device to be made?
i=0
while [ ! -b /dev/nbd0 ]; do

View File

@ -1,51 +1,81 @@
# It'd be nice if this could share rules with 99-block.sh, but since
# the kernel side adds nbd{1..16} when the module is loaded -- before
# they are associated with a server -- we cannot use the udev add rule
# to find it
#
# XXX actually we could, if we move to root=XXX and netroot=XXX, then
# you could do root=LABEL=/ nbdroot=XXX, or netroot=nbd:XXX
#
# However, we need to be 90-nbd.sh to catch root=/dev/nbd*
#!/bin/sh
#
# Preferred format:
# root=nbd:srv:port[:fstype[:rootflags[:nbdopts]]]
# [root=*] netroot=nbd:srv:port[:fstype[:rootflags[:nbdopts]]]
#
# Legacy formats:
# [net]root=[nbd] nbdroot=srv,port
# [net]root=[nbd] nbdroot=srv:port[:fstype[:rootflags[:nbdopts]]]
#
# nbdopts is a comma seperated list of options to give to nbd-client
#
#
# Legacy formats:
# nbdroot=srv,port
# nbdroot=srv:port[:fstype[:rootflags[:nbdopts]]]
# root=dhcp nbdroot=srv:port[:fstype[:rootflags[:nbdopts]]]
# root=nbd nbdroot=srv:port[:fstype[:rootflags[:nbdopts]]]
# root= takes precedence over netroot= if root=nbd[...]
#
case "$root" in
nbd|dhcp|'')
if getarg nbdroot= > /dev/null; then
root=nbd:$(getarg nbdroot=)
fi
;;
esac
# Sadly there's no easy way to split ':' separated lines into variables
netroot_to_var() {
local v=${1}:
set --
while [ -n "$v" ]; do
set -- "$@" "${v%%:*}"
v=${v#*:}
done
# Convert the Debian style to our syntax, but avoid matches on fs arguments
case "$root" in
nbd:*,*)
if check_occurances "$root" ',' 1 && check_occurances "$root" ':' 1;
then
root=${root%,*}:${root#*,}
fi
;;
esac
unset server port
server=$2; port=$3;
}
if [ -z "$netroot" -a -n "$root" -a -z "${root%%nbd:*}" ]; then
netroot="$root"
unset root
# Don't continue if root is ok
[ -n "$rootok" ] && return
# This script is sourced, so root should be set. But let's be paranoid
[ -z "$root" ] && root=$(getarg root=)
[ -z "$netroot" ] && netroot=$(getarg netroot=)
[ -z "$nbdroot" ] && nbdroot=$(getarg nbdroot=)
# Root takes precedence over netroot
if [ "${root%%:*}" = "nbd" ] ; then
if [ -n "$netroot" ] ; then
warn "root takes precedence over netroot. Ignoring netroot"
fi
netroot=$root
fi
if [ "${netroot%%:*}" = "nbd" ]; then
# XXX validate options here?
# XXX generate udev rules?
rootok=1
# If it's not empty or nbd we don't continue
[ -z "$netroot" ] || [ "${netroot%%:*}" = "nbd" ] || return
if [ -n "$nbdroot" ] ; then
[ -z "$netroot" ] && netroot=$root
# Debian legacy style contains no ':' Converting is easy
[ "$nbdroot" = "${nbdroot##*:}" ] && nbdroot=${nbdroot%,*}:${nbdroot#*,}
# @deprecated
warn "Argument nbdroot is deprecated and might be removed in a future release. See http://apps.sourceforge.net/trac/dracut/wiki/commandline for more information."
# Accept nbdroot argument?
[ -z "$netroot" ] || [ "$netroot" = "nbd" ] || \
die "Argument nbdroot only accepted for empty root= or [net]root=nbd"
# Override netroot with nbdroot content?
[ -z "$netroot" ] || [ "$netroot" = "nbd" ] && netroot=nbd:$nbdroot
fi
# If it's not nbd we don't continue
[ "${netroot%%:*}" = "nbd" ] || return
# Check required arguments
netroot_to_var $netroot
[ -z "$server" ] && die "Argument server for nbdroot is missing"
[ -z "$port" ] && die "Argument port for nbdroot is missing"
# NBD actually supported?
incol2 /proc/devices nbd || modprobe nbd || die "nbdroot requested but kernel/initrd does not support nbd"
# Done, all good!
rootok=1
# Shut up init error check
[ -z "$root" ] && root="nbd"

View File

@ -18,7 +18,6 @@ dracut_install $(ls {/usr,}$LIBDIR/libnss*.so 2>/dev/null)
instmods nfs sunrpc ipv6
inst_hook cmdline 90 "$moddir/parse-nfsroot.sh"
inst_hook netroot 90 "$moddir/nfs-netroot.sh"
inst_hook pre-pivot 70 "$moddir/nfsroot-cleanup.sh"
inst "$moddir/nfsroot" "/sbin/nfsroot"
mkdir -p "$initdir/var/lib/nfs/rpc_pipefs"

View File

@ -1,67 +0,0 @@
# If we're auto-detecting our root type from DHCP, see if this looks like
# an NFS root option. As the variety of root-path formats is large, validate
# that the number of colons match what we expect, and our glob didn't
# inadvertently match a different handler's.
#
if [ "$netroot" = "dhcp" -o "$netroot" = "nfs" -o "$netroot" = "nfs4" ]; then
nfsver=nfs
if [ "$netroot" = "nfs4" ]; then
nfsver=nfs4
fi
case "$new_root_path" in
nfs:*|nfs4:*) netroot="$new_root_path" ;;
*:/*:*)
if check_occurances "$new_root_path" ':' 2; then
netroot="$nfsver:$new_root_path"
fi ;;
*:/*,*)
if check_occurances "$new_root_path" ':' 1; then
netroot="$nfsver:$new_root_path"
fi ;;
*:/*)
if check_occurances "$new_root_path" ':' 1; then
netroot="$nfsver:$new_root_path:"
fi ;;
/*:*)
if check_occurances "$new_root_path" ':' 1; then
netroot="$nfsver::$new_root_path"
fi ;;
/*,*)
if check_occurances "$new_root_path" ':' 0; then
netroot="$nfsver::$new_root_path"
fi ;;
/*)
if check_occurances "$new_root_path" ':' 0; then
netroot="$nfsver::$new_root_path:"
fi ;;
'') netroot="$nfsver:::" ;;
esac
fi
if [ -z "${netroot%%nfs:*}" -o -z "${netroot%%nfs4:*}" ]; then
# Fill in missing information from DHCP
nfsver=${netroot%%:*}; netroot=${netroot#*:}
nfsserver=${netroot%%:*}; netroot=${netroot#*:}
nfspath=${netroot%%:*}
nfsflags=${netroot#*:}
# XXX where does dhclient stash the next-server option? Do we care?
if [ -z "$nfsserver" -o "$nfsserver" = "$nfspath" ]; then
nfsserver=$new_dhcp_server_identifier
fi
# Handle alternate syntax of path,options
if [ "$nfsflags" = "$nfspath" -a "${netroot#*,}" != "$netroot" ]; then
nfspath=${netroot%%,*}
nfsflags=${netroot#*,}
fi
# Catch the case when no additional flags are set
if [ "$nfspath" = "$nfsflags" ]; then
unset nfsflags
fi
# XXX validate we have all the required info?
netroot="$nfsver:$nfsserver:$nfspath:$nfsflags"
handler=/sbin/nfsroot
fi

View File

@ -1,11 +1,47 @@
#!/bin/sh
# Copy from parse-nfsroot.sh
root_to_var() {
local v=${1}:
set --
while [ -n "$v" ]; do
set -- "$@" "${v%%:*}"
v=${v#*:}
done
unset nfs server path options
# Ugly: Can't -z test $path after the case, since it might be allowed
# to be empty for root=nfs
nfs=$1
case $# in
0|1);;
2) path=$2;;
3)
# This is ultra ugly. But we can't decide in which position path
# sits without checking if the string starts with '/'
case $2 in
/*) path=$2; options=$3;;
*) server=$2; path=$3;;
esac
;;
*) server=$2; path=$3; options=$4;
esac
# 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
}
. /lib/dracut-lib
PATH=$PATH:/sbin:/usr/sbin
# XXX needs error handling like ifup/dhclient-script
if getarg rdnetdebug ; then
exec > /tmp/nfsroot.$1.$$.out
exec 2>> /tmp/nfsroot.$1.$$.out
@ -21,65 +57,89 @@ fi
# Huh? Empty $3?
[ -z "$3" ] && exit 1
# root is in the form root=nfs[4]:server:path:[options]
# root is in the form root=nfs[4]:[server:]path[:options], either from
# cmdline or dhcp root-path
netif="$1"
root="$2"
NEWROOT="$3"
nfsver=${root%%:*}; root=${root#*:}
nfsserver=${root%%:*}; root=${root#*:}
nfspath=${root%%:*}
flags=${root#*:}
# If it's not nfs we don't continue
case "${root%%:*}" in
nfs|nfs4);;
*) return;;
esac
[ -z "$nfspath" ] && nfspath=/tftpboot/%s
# Ugly: root=nfs[4] requires dhcp root-path
if [ "$root" = "nfs" ] || [ "$root" = "nfs4" ] ; then
# No need to check if the file exists. ip cmdline parser takes care of this
. /tmp/dhclient.$netif.dhcpopts
[ -z "$new_root_path" ] && die "Required dhcp option root-path not available"
root=$root:$new_root_path
fi
root_to_var $root
#Load other data that might provide info
[ -f /tmp/net.$netif.override ] && . /tmp/net.$netif.override
[ -f /tmp/dhclient.$netif.dhcpopts ] && . /tmp/dhclient.$netif.dhcpopts
#Empty path means try dhcp root-path, this is ok here since parse-nfsroot.sh
#already takes care of nfs:... formatted root-path
[ -z "$path" ] && root_to_var $nfs:$new_root_path
#Empty path defaults to "/tftpboot/%s"
[ -z "$path" ] && path="/tftpboot/%s"
if [ -z "$server" ] ; then
# XXX new_dhcp_next_server is unconfirmed this is an assumption
for var in $srv $new_dhcp_server_identifier $new_dhcp_next_server $new_root_path '' ; do
[ -n "$var" ] && server=$var && break;
done
# XXX This blindly assumes that if new_root_path has to used that
# XXX it really can be used as server
server=${server%%:*}
fi
[ -z "$server" ] && die "Required parameter 'server' is missing"
# Kernel replaces first %s with host name, and falls back to the ip address
# if it isn't set. Only the first %s is substituted.
#
if [ "${nfspath#*%s}" != "$nfspath" ]; then
if [ "${path#*%s}" != "$path" ]; then
ip=$(ip -o -f inet addr show $netif)
ip=${ip%%/*}
ip=${ip##* }
read node < /proc/sys/kernel/hostname
[ "$node" = "(none)" ] && node=$ip
nfspath=${nfspath%%%s*}$node${nfspath#*%s}
path=${path%%%s*}$node${path#*%s}
fi
# Look through the flags and see if any are overridden by the command line
# Append a , so we know we terminate
flags=${flags},
while [ -n "$flags" ]; do
f=${flags%%,*}
flags=${flags#*,}
if [ -z "$f" ]; then
break
fi
if [ "$f" = "ro" -o "$f" = "rw" ]; then
nfsrw=$f
continue
fi
if [ "$f" = "lock" -o "$f" = "nolock" ]; then
nfslock=$f
continue
fi
nfsflags=${nfsflags+$nfsflags,}$f
# Look through the options and remove rw/locking options
OLDIFS=$IFS
IFS=,
for f in $options ; do
[ "$f" = "ro" -o "$f" = "rw" ] && nfsrw=$f && continue
[ "$f" = "lock" -o "$f" = "nolock" ] && nfslock=$f && continue
flags=${flags:+$flags,}$f
done
IFS=$OLDIFS
options=$flags
# Override rw/ro if set on cmdline
getarg ro && nfsrw=ro
getarg rw && nfsrw=rw
nfsflags=${nfsflags+$nfsflags,}${nfsrw}
# Load the modules so the filesystem type is there
incol2 /proc/filesystems nfs || modprobe nfs || exit 1
incol2 /proc/filesystems nfs4 || modprobe nfs || exit 1
# Default to ro if unset
[ -z "$nfsrw" ] && nfsrw=ro
options=${options:+$options,}$nfsrw
# Start rpcbind or rpcbind
# FIXME occasionally saw 'rpcbind: fork failed: No such device' -- why?
[ -x /sbin/portmap ] && [ -z "$(pidof portmap)" ] && portmap
[ -x /sbin/rpcbind ] && [ -z "$(pidof rpcbind)" ] && rpcbind
if [ "$nfsver" = "nfs4" ]; then
if [ "$nfs" = "nfs4" ]; then
# 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
@ -87,19 +147,17 @@ if [ "$nfsver" = "nfs4" ]; then
# XXX really needed? Do we need non-root users before we start it in
# XXX the real root image?
if [ -z "$(pidof rpc.idmapd)" ]; then
rpc.idmapd
fi
[ -z "$(pidof rpc.idmapd)" ] && rpc.idmapd
# XXX Should we loop here?
exec mount -t nfs4 -o${nfsflags}${nfslock+,$nfslock} \
$nfsserver:$nfspath $NEWROOT
exec mount -t nfs4 -o$options${nfslock+,$nfslock} \
$server:$path $NEWROOT
fi
# NFSv{2,3} doesn't support using locks as it requires a helper to transfer
# the rpcbind state to the new root
[ -z "$nfslock" -o "$nfslock" = "lock" ] &&
echo "Warning: Locks unsupported on NFSv{2,3}, using nolock" 1>&2
[ "$nfslock" = "lock" ] && \
warn "Locks unsupported on NFSv{2,3}, using nolock" 1>&2
# XXX Should we loop here?
exec mount -t nfs -onolock,$nfsflags $nfsserver:$nfspath $NEWROOT
exec mount -t nfs -o$options${options:+,}nolock $server:$path $NEWROOT

View File

@ -1,55 +1,136 @@
# We're 90-nfs.sh to catch root=/dev/nfs
#!/bin/sh
#
# Preferred format:
# root=nfs[4]:[server:]path[:options]
# netroot=nfs[4]:[server:]path[:options]
# [root=*] netroot=nfs[4]:[server:]path[:options]
#
# Legacy formats:
# [net]root=[[/dev/]nfs[4]] nfsroot=[server:]path[,options]
# [net]root=[[/dev/]nfs[4]] nfsroot=[server:]path[:options]
#
# 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
#
# Legacy formats:
# root=nfs[4]
# root=/dev/nfs[4] nfsroot=[server:]path[,options]
# NFSv4 is only used if explicitly requested; default is NFSv2 or NFSv3
# depending on kernel configuration
#
# Plain "root=nfs" interprets DHCP root-path option as [ip:]path[:options]
#
# NFSv4 is only used if explicitly listed; default is NFSv3
# root= takes precedence over netroot= if root=nfs[...]
#
case "$root" in
nfs|dhcp|'')
if getarg nfsroot= > /dev/null; then
root=nfs:$(getarg nfsroot=)
fi
;;
nfs4)
if getarg nfsroot= > /dev/null; then
root=nfs4:$(getarg nfsroot=)
fi
;;
/dev/nfs|/dev/nfs4)
if getarg nfsroot= > /dev/null; then
root=${root#/dev/}:$(getarg nfsroot=)
else
root=${root#/dev/}
fi
;;
esac
# Sadly there's no easy way to split ':' separated lines into variables
netroot_to_var() {
local v=${1}:
set --
while [ -n "$v" ]; do
set -- "$@" "${v%%:*}"
v=${v#*:}
done
if [ -z "$netroot" -a -n "$root" -a -z "${root%%nfs*}" ]; then
netroot="$root"
unset root
fi
unset nfs server path options
case "$netroot" in
nfs|nfs4|nfs:*|nfs4:*)
rootok=1
if [ -n "$root" -a "$netroot" != "$root" ]; then
echo "WARNING: root= and netroot= do not match, ignoring root="
fi
unset root
nfs=$1
# Ugly: Can't -z test #path after the case, since it might be allowed
# to be empty for root=nfs
case $# in
0|1);;
2) path=${2:-error};;
3)
# This is ultra ugly. But we can't decide in which position path
# sits without checking if the string starts with '/'
case $2 in
/*) path=$2; options=$3;;
*) server=$2; path=${3:-error};;
esac
;;
*) server=$2; path=${3:-error}; options=$4;
esac
# 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
}
#Don't continue if root is ok
[ -n "$rootok" ] && return
# This script is sourced, so root should be set. But let's be paranoid
[ -z "$root" ] && root=$(getarg root=)
[ -z "$netroot" ] && netroot=$(getarg netroot=)
[ -z "$nfsroot" ] && nfsroot=$(getarg nfsroot=)
# Root takes precedence over netroot
case "${root%%:*}" in
nfs|nfs4|/dev/nfs|/dev/nfs4)
if [ -n "$netroot" ] ; then
warn "root takes precedence over netroot. Ignoring netroot"
fi
netroot=$root
;;
esac
# If it's not empty or nfs we don't continue
case "${netroot%%:*}" in
''|nfs|nfs4|/dev/nfs|/dev/nfs4);;
*) return;;
esac
if [ -n "$nfsroot" ] ; then
[ -z "$netroot" ] && netroot=$root
# @deprecated
warn "Argument nfsroot is deprecated and might be removed in a future release. See http://apps.sourceforge.net/trac/dracut/wiki/commandline for more information."
case "$netroot" in
''|nfs|nfs4|/dev/nfs|/dev/nfs4) netroot=${netroot:-nfs}:$nfsroot;;
*) die "Argument nfsroot only accepted for empty root= or root=[/dev/]nfs[4]"
esac
fi
# If it's not nfs we don't continue
case "${netroot%%:*}" in
nfs|nfs4|/dev/nfs|/dev/nfs4);;
*) return;;
esac
# Check required arguments
netroot_to_var $netroot
[ "$path" = "error" ] && die "Argument nfsroot must contain a valid path!"
# Set fstype, might help somewhere
fstype=${nfs#/dev/}
# NFS actually supported? Some more uglyness here: nfs3 or nfs4 might not
# be in the module...
if ! incol2 /proc/filesystems $fstype ; then
modprobe nfs
incol2 /proc/filesystems $fstype || die "nfsroot type $fstype requested but kernel/initrd does not support nfs"
fi
# Rewrite root so we don't have to parse this uglyness later on again
netroot="$fstype:$server:$path:$options"
# If we don't have a server, we need dhcp
if [ -z "$server" ] ; then
NEEDDHCP="1"
fi;
# Done, all good!
rootok=1
# Shut up init error check or make sure that block parser wont get
# confused by having /dev/nfs[4]
root="$fstype"

View File

@ -43,11 +43,11 @@ client_test() {
fi
$testdir/run-qemu -hda client.img -m 512M -nographic \
-net nic,macaddr=$mac,model=e1000 \
-net socket,mcast=230.0.0.1:1234 \
-kernel /boot/vmlinuz-$KVERSION \
-append "$cmdline $DEBUGFAIL ro quiet console=ttyS0,115200n81" \
-initrd initramfs.testing
-net nic,macaddr=$mac,model=e1000 \
-net socket,mcast=230.0.0.1:1234 \
-kernel /boot/vmlinuz-$KVERSION \
-append "$cmdline $DEBUGFAIL ro quiet console=ttyS0,115200n81" \
-initrd initramfs.testing
if [[ $? -ne 0 ]] || ! grep -m 1 -q nfs-OK client.img; then
echo "CLIENT TEST END: $test_name [FAILED - BAD EXIT]"
@ -101,45 +101,49 @@ test_nfsv3() {
# NFSv3: last octect starts at 0x00 and works up
# NFSv4: last octect starts at 0x80 and works up
# This test must fail: dhcp root-path must have proto:...
client_test "NFSv3 netroot=dhcp DHCP path only" 52:54:00:12:34:00 \
"netroot=dhcp" 192.168.50.1 -wsize=4096 || return 1
"netroot=dhcp" 192.168.50.1 -wsize=4096 && return 1
# This test must fail: dhcp root-path must have proto:...
client_test "NFSv3 root=dhcp DHCP path only" 52:54:00:12:34:00 \
"root=dhcp" 192.168.50.1 -wsize=4096 || return 1
"root=dhcp" 192.168.50.1 -wsize=4096 && return 1
client_test "NFSv3 root=nfs DHCP path only" 52:54:00:12:34:00 \
"root=nfs" 192.168.50.1 -wsize=4096 || return 1
"root=nfs" 192.168.50.1 -wsize=4096 || return 1
client_test "NFSv3 root=/dev/nfs DHCP path only" 52:54:00:12:34:00 \
"root=/dev/nfs" 192.168.50.1 -wsize=4096 || return 1
"root=/dev/nfs" 192.168.50.1 -wsize=4096 || return 1
# This test must fail: dhcp root-path must have proto:...
client_test "NFSv3 netroot=dhcp DHCP IP:path" 52:54:00:12:34:01 \
"netroot=dhcp" 192.168.50.2 -wsize=4096 || return 1
"netroot=dhcp" 192.168.50.2 -wsize=4096 && return 1
# This test must fail: dhcp root-path must have proto:...
client_test "NFSv3 root=dhcp DHCP IP:path" 52:54:00:12:34:01 \
"root=dhcp" 192.168.50.2 -wsize=4096 || return 1
"root=dhcp" 192.168.50.2 -wsize=4096 && return 1
client_test "NFSv3 root=nfs DHCP IP:path" 52:54:00:12:34:01 \
"root=nfs" 192.168.50.2 -wsize=4096 || return 1
"root=nfs" 192.168.50.2 -wsize=4096 || return 1
client_test "NFSv3 root=/dev/nfs DHCP IP:path" 52:54:00:12:34:01 \
"root=/dev/nfs" 192.168.50.2 -wsize=4096 || return 1
"root=/dev/nfs" 192.168.50.2 -wsize=4096 || return 1
client_test "NFSv3 root=dhcp DHCP proto:IP:path" 52:54:00:12:34:02 \
"root=dhcp" 192.168.50.3 -wsize=4096 || return 1
"root=dhcp" 192.168.50.3 -wsize=4096 || return 1
client_test "NFSv3 netroot=dhcp DHCP proto:IP:path:options" \
52:54:00:12:34:03 "netroot=dhcp" 192.168.50.3 wsize=4096 || return 1
52:54:00:12:34:03 "netroot=dhcp" 192.168.50.3 wsize=4096 || return 1
client_test "NFSv3 root=dhcp DHCP proto:IP:path:options" 52:54:00:12:34:03 \
"root=dhcp" 192.168.50.3 wsize=4096 || return 1
"root=dhcp" 192.168.50.3 wsize=4096 || return 1
client_test "NFSv3 netroot=nfs:..." 52:54:00:12:34:04 \
"netroot=nfs:192.168.50.1:/nfs/client" 192.168.50.1 \
-wsize=4096 || return 1
"netroot=nfs:192.168.50.1:/nfs/client" 192.168.50.1 \
-wsize=4096 || return 1
client_test "NFSv3 root=nfs:..." 52:54:00:12:34:04 \
"root=nfs:192.168.50.1:/nfs/client" 192.168.50.1 -wsize=4096 || return 1
"root=nfs:192.168.50.1:/nfs/client" 192.168.50.1 -wsize=4096 || return 1
client_test "NFSv3 nfsroot=/nfs/client" 52:54:00:12:34:04 \
"nfsroot=/nfs/client" 192.168.50.1 -wsize=4096 || return 1
@ -147,8 +151,9 @@ test_nfsv3() {
client_test "NFSv3 nfsroot=192.168.50.2:/nfs/client" 52:54:00:12:34:04 \
"nfsroot=192.168.50.2:/nfs/client" 192.168.50.2 -wsize=4096 || return 1
# This test must fail: root=dhcp must ignore other arguments
client_test "NFSv3 root=dhcp nfsroot=/nfs/client" 52:54:00:12:34:04 \
"root=dhcp nfsroot=/nfs/client" 192.168.50.1 -wsize=4096 || return 1
"root=dhcp nfsroot=/nfs/client" 192.168.50.1 -wsize=4096 && return 1
client_test "NFSv3 root=nfs nfsroot=/nfs/client" 52:54:00:12:34:04 \
"root=nfs nfsroot=/nfs/client" 192.168.50.1 -wsize=4096 || return 1
@ -166,11 +171,13 @@ test_nfsv3() {
52:54:00:12:34:04 "root=nfs nfsroot=/nfs/client,wsize=4096" \
192.168.50.1 wsize=4096 || return 1
client_test "NFSv3 root=nfs DHCP path,options" \
52:54:00:12:34:05 "root=dhcp" 192.168.50.1 wsize=4096 || return 1
# This test must fail: dhcp root-path must have proto:...
client_test "NFSv3 root=dhcp DHCP path,options" \
52:54:00:12:34:05 "root=dhcp" 192.168.50.1 wsize=4096 && return 1
# This test must fail: dhcp root-path must have proto:...
client_test "NFSv3 root=dhcp DHCP IP:path,options" \
52:54:00:12:34:06 "root=dhcp" 192.168.50.2 wsize=4096 || return 1
52:54:00:12:34:06 "root=dhcp" 192.168.50.2 wsize=4096 && return 1
client_test "NFSv3 root=dhcp DHCP proto:IP:path,options" \
52:54:00:12:34:07 "root=dhcp" 192.168.50.3 wsize=4096 || return 1
@ -182,34 +189,34 @@ test_nfsv4() {
# switch_root
client_test "NFSv4 netroot=nfs4 DHCP path only" 52:54:00:12:34:80 \
"netroot=nfs4" 192.168.50.1 -wsize=4096 || return 1
"netroot=nfs4" 192.168.50.1 -wsize=4096 || return 1
client_test "NFSv4 root=nfs4 DHCP path only" 52:54:00:12:34:80 \
"root=nfs4" 192.168.50.1 -wsize=4096 || return 1
"root=nfs4" 192.168.50.1 -wsize=4096 || return 1
client_test "NFSv4 root=/dev/nfs4 DHCP path only" 52:54:00:12:34:80 \
"root=/dev/nfs4" 192.168.50.1 -wsize=4096 || return 1
"root=/dev/nfs4" 192.168.50.1 -wsize=4096 || return 1
client_test "NFSv4 netroot=nfs4 DHCP IP:path" 52:54:00:12:34:81 \
"netroot=nfs4" 192.168.50.2 -wsize=4096 || return 1
"netroot=nfs4" 192.168.50.2 -wsize=4096 || return 1
client_test "NFSv4 root=nfs4 DHCP IP:path" 52:54:00:12:34:81 \
"root=nfs4" 192.168.50.2 -wsize=4096 || return 1
"root=nfs4" 192.168.50.2 -wsize=4096 || return 1
client_test "NFSv4 root=/dev/nfs4 DHCP IP:path" 52:54:00:12:34:81 \
"root=/dev/nfs4" 192.168.50.2 -wsize=4096 || return 1
"root=/dev/nfs4" 192.168.50.2 -wsize=4096 || return 1
client_test "NFSv4 netroot=dhcp DHCP proto:IP:path" 52:54:00:12:34:82 \
"netroot=dhcp" 192.168.50.3 -wsize=4096 || return 1
"netroot=dhcp" 192.168.50.3 -wsize=4096 || return 1
client_test "NFSv4 root=dhcp DHCP proto:IP:path" 52:54:00:12:34:82 \
"root=dhcp" 192.168.50.3 -wsize=4096 || return 1
"root=dhcp" 192.168.50.3 -wsize=4096 || return 1
client_test "NFSv4 netroot=dhcp DHCP proto:IP:path:options" \
52:54:00:12:34:83 "netroot=dhcp" 192.168.50.3 wsize=4096 || return 1
52:54:00:12:34:83 "netroot=dhcp" 192.168.50.3 wsize=4096 || return 1
client_test "NFSv4 root=dhcp DHCP proto:IP:path:options" 52:54:00:12:34:83 \
"root=dhcp" 192.168.50.3 wsize=4096 || return 1
"root=dhcp" 192.168.50.3 wsize=4096 || return 1
client_test "NFSv4 netroot=nfs4:..." 52:54:00:12:34:84 \
"netroot=nfs4:192.168.50.1:/client" 192.168.50.1 \

View File

@ -92,47 +92,49 @@ test_run() {
# The default is ext3,errors=continue so use that to determine
# if our options were parsed and used
client_test "NBD root=nbd:IP:port" 52:54:00:12:34:00 \
"root=nbd:192.168.50.1:2000" || return 1
client_test "NBD root=nbd:IP:port" 52:54:00:12:34:00 \
"root=nbd:192.168.50.1:2000" || return 1
client_test "NBD root=nbd:IP:port:fstype" 52:54:00:12:34:00 \
"root=nbd:192.168.50.1:2000:ext2" ext2 || return 1
client_test "NBD root=nbd:IP:port:fstype" 52:54:00:12:34:00 \
"root=nbd:192.168.50.1:2000:ext2" ext2 || return 1
client_test "NBD root=nbd:IP:port::fsopts" 52:54:00:12:34:00 \
"root=nbd:192.168.50.1:2000::errors=panic" \
ext3 errors=panic || return 1
client_test "NBD root=nbd:IP:port::fsopts" 52:54:00:12:34:00 \
"root=nbd:192.168.50.1:2000::errors=panic" \
ext3 errors=panic || return 1
client_test "NBD root=nbd:IP:port:fstype:fsopts" 52:54:00:12:34:00 \
"root=nbd:192.168.50.1:2000:ext2:errors=panic" \
ext2 errors=panic || return 1
client_test "NBD root=nbd:IP:port:fstype:fsopts" 52:54:00:12:34:00 \
"root=nbd:192.168.50.1:2000:ext2:errors=panic" \
ext2 errors=panic || return 1
# There doesn't seem to be a good way to validate the NBD options, so
# just check that we don't screw up the other options
# There doesn't seem to be a good way to validate the NBD options, so
# just check that we don't screw up the other options
client_test "NBD root=nbd:IP:port:::NBD opts" 52:54:00:12:34:00 \
"root=nbd:192.168.50.1:2000:::bs=2048" || return 1
client_test "NBD root=nbd:IP:port:::NBD opts" 52:54:00:12:34:00 \
"root=nbd:192.168.50.1:2000:::bs=2048" || return 1
client_test "NBD root=nbd:IP:port:fstype::NBD opts" 52:54:00:12:34:00 \
"root=nbd:192.168.50.1:2000:ext2::bs=2048" ext2 || return 1
client_test "NBD root=nbd:IP:port:fstype::NBD opts" 52:54:00:12:34:00 \
"root=nbd:192.168.50.1:2000:ext2::bs=2048" ext2 || return 1
client_test "NBD root=nbd:IP:port:fstype:fsopts:NBD opts" \
52:54:00:12:34:00 \
"root=nbd:192.168.50.1:2000:ext2:errors=panic:bs=2048" \
ext2 errors=panic || return 1
client_test "NBD root=nbd:IP:port:fstype:fsopts:NBD opts" \
52:54:00:12:34:00 \
"root=nbd:192.168.50.1:2000:ext2:errors=panic:bs=2048" \
ext2 errors=panic || return 1
# Check legacy parsing
# Check legacy parsing
client_test "NBD root=nbd nbdroot=srv:port" 52:54:00:12:34:00 \
"root=nbd nbdroot=192.168.50.1:2000" || return 1
client_test "NBD root=nbd nbdroot=srv:port" 52:54:00:12:34:00 \
"root=nbd nbdroot=192.168.50.1:2000" || return 1
client_test "NBD root=dhcp nbdroot=srv:port" 52:54:00:12:34:00 \
"root=dhcp nbdroot=192.168.50.1:2000" || return 1
# This test must fail: root=dhcp ignores nbdroot
client_test "NBD root=dhcp nbdroot=srv:port" 52:54:00:12:34:00 \
"root=dhcp nbdroot=192.168.50.1:2000" && return 1
client_test "NBD root=nbd nbdroot=srv,port" 52:54:00:12:34:00 \
"root=nbd nbdroot=192.168.50.1,2000" || return 1
client_test "NBD root=nbd nbdroot=srv,port" 52:54:00:12:34:00 \
"root=nbd nbdroot=192.168.50.1,2000" || return 1
client_test "NBD root=dhcp nbdroot=srv,port" 52:54:00:12:34:00 \
"root=dhcp nbdroot=192.168.50.1,2000" || return 1
# This test must fail: root=dhcp ignores nbdroot
client_test "NBD root=dhcp nbdroot=srv,port" 52:54:00:12:34:00 \
"root=dhcp nbdroot=192.168.50.1,2000" && return 1
# DHCP root-path parsing