summaryrefslogtreecommitdiffstats
path: root/utils/mount
diff options
context:
space:
mode:
Diffstat (limited to 'utils/mount')
-rw-r--r--utils/mount/Makefile.am82
-rw-r--r--utils/mount/Makefile.in976
-rw-r--r--utils/mount/configfile.c347
-rw-r--r--utils/mount/error.c360
-rw-r--r--utils/mount/error.h35
-rw-r--r--utils/mount/fstab.c653
-rw-r--r--utils/mount/fstab.h32
-rw-r--r--utils/mount/mount.c558
-rw-r--r--utils/mount/mount.nfs.man93
-rw-r--r--utils/mount/mount_config.h55
-rw-r--r--utils/mount/mount_constants.h71
-rw-r--r--utils/mount/mount_libmount.c461
-rw-r--r--utils/mount/network.c1801
-rw-r--r--utils/mount/network.h96
-rw-r--r--utils/mount/nfs.man1968
-rw-r--r--utils/mount/nfs4_mount.h73
-rw-r--r--utils/mount/nfs4mount.c481
-rw-r--r--utils/mount/nfs_mount.h83
-rw-r--r--utils/mount/nfsmount.c873
-rw-r--r--utils/mount/nfsmount.conf144
-rw-r--r--utils/mount/nfsmount.conf.man131
-rw-r--r--utils/mount/nfsumount.c351
-rw-r--r--utils/mount/parse_dev.c233
-rw-r--r--utils/mount/parse_dev.h28
-rw-r--r--utils/mount/parse_opt.c610
-rw-r--r--utils/mount/parse_opt.h60
-rw-r--r--utils/mount/stropts.c1310
-rw-r--r--utils/mount/stropts.h30
-rw-r--r--utils/mount/token.c157
-rw-r--r--utils/mount/token.h34
-rw-r--r--utils/mount/umount.nfs.man70
-rw-r--r--utils/mount/utils.c175
-rw-r--r--utils/mount/utils.h36
-rw-r--r--utils/mount/version.h53
34 files changed, 12520 insertions, 0 deletions
diff --git a/utils/mount/Makefile.am b/utils/mount/Makefile.am
new file mode 100644
index 0000000..5ff1148
--- /dev/null
+++ b/utils/mount/Makefile.am
@@ -0,0 +1,82 @@
+## Process this file with automake to produce Makefile.in
+
+# These binaries go in /sbin (not /usr/sbin), unless CONFIG_SBIN_OVERRIDE
+# is disabled as may be appropriate when /sbin is a symlink.
+# Note that we don't use "if CONFIG_SBIN_OVERRIDE" as that
+# causes autotools to notice the override and disable it.
+@CONFIG_SBIN_OVERRIDE_TRUE@sbindir = /sbin
+
+man8_MANS = mount.nfs.man umount.nfs.man
+man5_MANS = nfs.man
+
+sbin_PROGRAMS = mount.nfs
+EXTRA_DIST = nfsmount.conf $(man8_MANS) $(man5_MANS)
+mount_common = error.c network.c token.c \
+ parse_opt.c parse_dev.c \
+ nfsmount.c nfs4mount.c stropts.c\
+ mount_constants.h error.h network.h token.h \
+ parse_opt.h parse_dev.h \
+ nfs4_mount.h stropts.h version.h \
+ mount_config.h utils.c utils.h \
+ nfs_mount.h
+
+if MOUNT_CONFIG
+mount_common += configfile.c
+man5_MANS += nfsmount.conf.man
+else
+EXTRA_DIST += nfsmount.conf.man
+endif
+
+mount_nfs_LDADD = ../../support/nfs/libnfs.la \
+ ../../support/export/libexport.a \
+ ../../support/reexport/libreexport.a \
+ ../../support/misc/libmisc.a \
+ $(LIBTIRPC) $(LIBPTHREAD)
+
+mount_nfs_SOURCES = $(mount_common)
+
+if CONFIG_LIBMOUNT
+mount_nfs_SOURCES += mount_libmount.c
+mount_nfs_LDADD += $(LIBMOUNT)
+else
+mount_nfs_SOURCES += mount.c fstab.c nfsumount.c fstab.h
+
+endif
+
+MAINTAINERCLEANFILES = Makefile.in
+
+install-exec-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ ln -sf mount.nfs mount.nfs4 && \
+ ln -sf mount.nfs umount.nfs && \
+ ln -sf mount.nfs umount.nfs4 && \
+ chmod 4511 mount.nfs )
+uninstall-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ rm -f mount.nfs4 umount.nfs umount.nfs4)
+
+
+install-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $$inst ; \
+ done)
+ (cd $(DESTDIR)$(man5dir) && \
+ for m in $(man5_MANS) $(dist_man5_MANS) $(nodist_man5_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/5/'`; \
+ rm -f $$inst ; \
+ done)
+
+uninstall-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $$inst ; \
+ done)
+ (cd $(DESTDIR)$(man5dir) && \
+ for m in $(man5_MANS) $(dist_man5_MANS) $(nodist_man5_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/5/'`; \
+ rm -f $$inst ; \
+ done)
+
diff --git a/utils/mount/Makefile.in b/utils/mount/Makefile.in
new file mode 100644
index 0000000..5bbbb5d
--- /dev/null
+++ b/utils/mount/Makefile.in
@@ -0,0 +1,976 @@
+# Makefile.in generated by automake 1.16.5 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994-2021 Free Software Foundation, Inc.
+
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+VPATH = @srcdir@
+am__is_gnu_make = { \
+ if test -z '$(MAKELEVEL)'; then \
+ false; \
+ elif test -n '$(MAKE_HOST)'; then \
+ true; \
+ elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
+ true; \
+ else \
+ false; \
+ fi; \
+}
+am__make_running_with_option = \
+ case $${target_option-} in \
+ ?) ;; \
+ *) echo "am__make_running_with_option: internal error: invalid" \
+ "target option '$${target_option-}' specified" >&2; \
+ exit 1;; \
+ esac; \
+ has_opt=no; \
+ sane_makeflags=$$MAKEFLAGS; \
+ if $(am__is_gnu_make); then \
+ sane_makeflags=$$MFLAGS; \
+ else \
+ case $$MAKEFLAGS in \
+ *\\[\ \ ]*) \
+ bs=\\; \
+ sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
+ | sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
+ esac; \
+ fi; \
+ skip_next=no; \
+ strip_trailopt () \
+ { \
+ flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
+ }; \
+ for flg in $$sane_makeflags; do \
+ test $$skip_next = yes && { skip_next=no; continue; }; \
+ case $$flg in \
+ *=*|--*) continue;; \
+ -*I) strip_trailopt 'I'; skip_next=yes;; \
+ -*I?*) strip_trailopt 'I';; \
+ -*O) strip_trailopt 'O'; skip_next=yes;; \
+ -*O?*) strip_trailopt 'O';; \
+ -*l) strip_trailopt 'l'; skip_next=yes;; \
+ -*l?*) strip_trailopt 'l';; \
+ -[dEDm]) skip_next=yes;; \
+ -[JT]) skip_next=yes;; \
+ esac; \
+ case $$flg in \
+ *$$target_option*) has_opt=yes; break;; \
+ esac; \
+ done; \
+ test $$has_opt = yes
+am__make_dryrun = (target_option=n; $(am__make_running_with_option))
+am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
+pkgdatadir = $(datadir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkglibexecdir = $(libexecdir)/@PACKAGE@
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+sbin_PROGRAMS = mount.nfs$(EXEEXT)
+@MOUNT_CONFIG_TRUE@am__append_1 = configfile.c
+@MOUNT_CONFIG_TRUE@am__append_2 = nfsmount.conf.man
+@MOUNT_CONFIG_FALSE@am__append_3 = nfsmount.conf.man
+@CONFIG_LIBMOUNT_TRUE@am__append_4 = mount_libmount.c
+@CONFIG_LIBMOUNT_TRUE@am__append_5 = $(LIBMOUNT)
+@CONFIG_LIBMOUNT_FALSE@am__append_6 = mount.c fstab.c nfsumount.c fstab.h
+subdir = utils/mount
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/aclocal/ax_gcc_func_attribute.m4 \
+ $(top_srcdir)/aclocal/bsdsignals.m4 \
+ $(top_srcdir)/aclocal/getrandom.m4 \
+ $(top_srcdir)/aclocal/ipv6.m4 \
+ $(top_srcdir)/aclocal/kerberos5.m4 \
+ $(top_srcdir)/aclocal/keyutils.m4 \
+ $(top_srcdir)/aclocal/libblkid.m4 \
+ $(top_srcdir)/aclocal/libcap.m4 \
+ $(top_srcdir)/aclocal/libevent.m4 \
+ $(top_srcdir)/aclocal/libpthread.m4 \
+ $(top_srcdir)/aclocal/libsqlite3.m4 \
+ $(top_srcdir)/aclocal/libtirpc.m4 \
+ $(top_srcdir)/aclocal/libxml2.m4 \
+ $(top_srcdir)/aclocal/nfs-utils.m4 \
+ $(top_srcdir)/aclocal/rpcsec_vers.m4 \
+ $(top_srcdir)/aclocal/tcp-wrappers.m4 \
+ $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON)
+mkinstalldirs = $(install_sh) -d
+CONFIG_HEADER = $(top_builddir)/support/include/config.h
+CONFIG_CLEAN_FILES =
+CONFIG_CLEAN_VPATH_FILES =
+am__installdirs = "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man5dir)" \
+ "$(DESTDIR)$(man8dir)"
+PROGRAMS = $(sbin_PROGRAMS)
+am__mount_nfs_SOURCES_DIST = error.c network.c token.c parse_opt.c \
+ parse_dev.c nfsmount.c nfs4mount.c stropts.c mount_constants.h \
+ error.h network.h token.h parse_opt.h parse_dev.h nfs4_mount.h \
+ stropts.h version.h mount_config.h utils.c utils.h nfs_mount.h \
+ configfile.c mount_libmount.c mount.c fstab.c nfsumount.c \
+ fstab.h
+@MOUNT_CONFIG_TRUE@am__objects_1 = configfile.$(OBJEXT)
+am__objects_2 = error.$(OBJEXT) network.$(OBJEXT) token.$(OBJEXT) \
+ parse_opt.$(OBJEXT) parse_dev.$(OBJEXT) nfsmount.$(OBJEXT) \
+ nfs4mount.$(OBJEXT) stropts.$(OBJEXT) utils.$(OBJEXT) \
+ $(am__objects_1)
+@CONFIG_LIBMOUNT_TRUE@am__objects_3 = mount_libmount.$(OBJEXT)
+@CONFIG_LIBMOUNT_FALSE@am__objects_4 = mount.$(OBJEXT) fstab.$(OBJEXT) \
+@CONFIG_LIBMOUNT_FALSE@ nfsumount.$(OBJEXT)
+am_mount_nfs_OBJECTS = $(am__objects_2) $(am__objects_3) \
+ $(am__objects_4)
+mount_nfs_OBJECTS = $(am_mount_nfs_OBJECTS)
+am__DEPENDENCIES_1 =
+@CONFIG_LIBMOUNT_TRUE@am__DEPENDENCIES_2 = $(am__DEPENDENCIES_1)
+mount_nfs_DEPENDENCIES = ../../support/nfs/libnfs.la \
+ ../../support/export/libexport.a \
+ ../../support/reexport/libreexport.a \
+ ../../support/misc/libmisc.a $(am__DEPENDENCIES_1) \
+ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_2)
+AM_V_lt = $(am__v_lt_@AM_V@)
+am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
+am__v_lt_0 = --silent
+am__v_lt_1 =
+AM_V_P = $(am__v_P_@AM_V@)
+am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
+am__v_P_0 = false
+am__v_P_1 = :
+AM_V_GEN = $(am__v_GEN_@AM_V@)
+am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
+am__v_GEN_0 = @echo " GEN " $@;
+am__v_GEN_1 =
+AM_V_at = $(am__v_at_@AM_V@)
+am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
+am__v_at_0 = @
+am__v_at_1 =
+DEFAULT_INCLUDES = -I.@am__isrc@ -I$(top_builddir)/support/include
+depcomp = $(SHELL) $(top_srcdir)/depcomp
+am__maybe_remake_depfiles = depfiles
+am__depfiles_remade = ./$(DEPDIR)/configfile.Po ./$(DEPDIR)/error.Po \
+ ./$(DEPDIR)/fstab.Po ./$(DEPDIR)/mount.Po \
+ ./$(DEPDIR)/mount_libmount.Po ./$(DEPDIR)/network.Po \
+ ./$(DEPDIR)/nfs4mount.Po ./$(DEPDIR)/nfsmount.Po \
+ ./$(DEPDIR)/nfsumount.Po ./$(DEPDIR)/parse_dev.Po \
+ ./$(DEPDIR)/parse_opt.Po ./$(DEPDIR)/stropts.Po \
+ ./$(DEPDIR)/token.Po ./$(DEPDIR)/utils.Po
+am__mv = mv -f
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+LTCOMPILE = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) \
+ $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) \
+ $(AM_CFLAGS) $(CFLAGS)
+AM_V_CC = $(am__v_CC_@AM_V@)
+am__v_CC_ = $(am__v_CC_@AM_DEFAULT_V@)
+am__v_CC_0 = @echo " CC " $@;
+am__v_CC_1 =
+CCLD = $(CC)
+LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \
+ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \
+ $(AM_LDFLAGS) $(LDFLAGS) -o $@
+AM_V_CCLD = $(am__v_CCLD_@AM_V@)
+am__v_CCLD_ = $(am__v_CCLD_@AM_DEFAULT_V@)
+am__v_CCLD_0 = @echo " CCLD " $@;
+am__v_CCLD_1 =
+SOURCES = $(mount_nfs_SOURCES)
+DIST_SOURCES = $(am__mount_nfs_SOURCES_DIST)
+am__can_run_installinfo = \
+ case $$AM_UPDATE_INFO_DIR in \
+ n|no|NO) false;; \
+ *) (install-info --version) >/dev/null 2>&1;; \
+ esac
+am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
+am__vpath_adj = case $$p in \
+ $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
+ *) f=$$p;; \
+ esac;
+am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
+am__install_max = 40
+am__nobase_strip_setup = \
+ srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
+am__nobase_strip = \
+ for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
+am__nobase_list = $(am__nobase_strip_setup); \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
+ $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
+ if (++n[$$2] == $(am__install_max)) \
+ { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
+ END { for (dir in files) print dir, files[dir] }'
+am__base_list = \
+ sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
+ sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
+am__uninstall_files_from_dir = { \
+ test -z "$$files" \
+ || { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
+ || { echo " ( cd '$$dir' && rm -f" $$files ")"; \
+ $(am__cd) "$$dir" && rm -f $$files; }; \
+ }
+man5dir = $(mandir)/man5
+man8dir = $(mandir)/man8
+NROFF = nroff
+MANS = $(man5_MANS) $(man8_MANS)
+am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
+# Read a list of newline-separated strings from the standard input,
+# and print each of them once, without duplicates. Input order is
+# *not* preserved.
+am__uniquify_input = $(AWK) '\
+ BEGIN { nonempty = 0; } \
+ { items[$$0] = 1; nonempty = 1; } \
+ END { if (nonempty) { for (i in items) print i; }; } \
+'
+# Make sure the list of sources is unique. This is necessary because,
+# e.g., the same source file might be shared among _SOURCES variables
+# for different programs/libraries.
+am__define_uniq_tagged_files = \
+ list='$(am__tagged_files)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | $(am__uniquify_input)`
+am__DIST_COMMON = $(srcdir)/Makefile.in $(top_srcdir)/depcomp
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@
+ALLOCA = @ALLOCA@
+AMTAR = @AMTAR@
+AM_CFLAGS = @AM_CFLAGS@
+AM_CPPFLAGS = @AM_CPPFLAGS@
+AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+CC = @CC@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CFLAGS = @CFLAGS@
+CFLAGS_FOR_BUILD = @CFLAGS_FOR_BUILD@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CPPFLAGS_FOR_BUILD = @CPPFLAGS_FOR_BUILD@
+CSCOPE = @CSCOPE@
+CTAGS = @CTAGS@
+CXX = @CXX@
+CXXCPP = @CXXCPP@
+CXXDEPMODE = @CXXDEPMODE@
+CXXFLAGS = @CXXFLAGS@
+CXXFLAGS_FOR_BUILD = @CXXFLAGS_FOR_BUILD@
+CYGPATH_W = @CYGPATH_W@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLTOOL = @DLLTOOL@
+DSYMUTIL = @DSYMUTIL@
+DUMPBIN = @DUMPBIN@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ETAGS = @ETAGS@
+EXEEXT = @EXEEXT@
+FGREP = @FGREP@
+FILECMD = @FILECMD@
+GREP = @GREP@
+GSSAPI_CFLAGS = @GSSAPI_CFLAGS@
+GSSAPI_LIBS = @GSSAPI_LIBS@
+GSSD = @GSSD@
+GSSGLUE_CFLAGS = @GSSGLUE_CFLAGS@
+GSSGLUE_LIBS = @GSSGLUE_LIBS@
+GSSKRB_CFLAGS = @GSSKRB_CFLAGS@
+GSSKRB_LIBS = @GSSKRB_LIBS@
+HAVE_GETRANDOM = @HAVE_GETRANDOM@
+HAVE_LIBWRAP = @HAVE_LIBWRAP@
+HAVE_TCP_WRAPPER = @HAVE_TCP_WRAPPER@
+IDMAPD = @IDMAPD@
+INSTALL = @INSTALL@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+K5VERS = @K5VERS@
+KRBCFLAGS = @KRBCFLAGS@
+KRBDIR = @KRBDIR@
+KRBLDFLAGS = @KRBLDFLAGS@
+KRBLIBS = @KRBLIBS@
+LD = @LD@
+LDFLAGS = @LDFLAGS@
+LDFLAGS_FOR_BUILD = @LDFLAGS_FOR_BUILD@
+LIBBLKID = @LIBBLKID@
+LIBBSD = @LIBBSD@
+LIBCAP = @LIBCAP@
+LIBCRYPT = @LIBCRYPT@
+LIBEVENT = @LIBEVENT@
+LIBKEYUTILS = @LIBKEYUTILS@
+LIBMOUNT = @LIBMOUNT@
+LIBMOUNT_CFLAGS = @LIBMOUNT_CFLAGS@
+LIBMOUNT_LIBS = @LIBMOUNT_LIBS@
+LIBNSL = @LIBNSL@
+LIBOBJS = @LIBOBJS@
+LIBPTHREAD = @LIBPTHREAD@
+LIBS = @LIBS@
+LIBSOCKET = @LIBSOCKET@
+LIBSQLITE = @LIBSQLITE@
+LIBTIRPC = @LIBTIRPC@
+LIBTOOL = @LIBTOOL@
+LIBWRAP = @LIBWRAP@
+LIBXML2 = @LIBXML2@
+LIPO = @LIPO@
+LN_S = @LN_S@
+LTLIBOBJS = @LTLIBOBJS@
+LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
+MAINT = @MAINT@
+MAKEINFO = @MAKEINFO@
+MANIFEST_TOOL = @MANIFEST_TOOL@
+MKDIR_P = @MKDIR_P@
+NM = @NM@
+NMEDIT = @NMEDIT@
+OBJDUMP = @OBJDUMP@
+OBJEXT = @OBJEXT@
+OTOOL = @OTOOL@
+OTOOL64 = @OTOOL64@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_URL = @PACKAGE_URL@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_PLUGINS = @PATH_PLUGINS@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PKG_CONFIG = @PKG_CONFIG@
+PKG_CONFIG_LIBDIR = @PKG_CONFIG_LIBDIR@
+PKG_CONFIG_PATH = @PKG_CONFIG_PATH@
+RANLIB = @RANLIB@
+RELEASE = @RELEASE@
+RPCGEN_PATH = @RPCGEN_PATH@
+RPCSECGSS_CFLAGS = @RPCSECGSS_CFLAGS@
+RPCSECGSS_LIBS = @RPCSECGSS_LIBS@
+SED = @SED@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+SVCGSSD = @SVCGSSD@
+TIRPC_CFLAGS = @TIRPC_CFLAGS@
+TIRPC_LIBS = @TIRPC_LIBS@
+VERSION = @VERSION@
+XML2_CFLAGS = @XML2_CFLAGS@
+XML2_LIBS = @XML2_LIBS@
+_rpc_pipefsmount = @_rpc_pipefsmount@
+_statedir = @_statedir@
+_sysconfdir = @_sysconfdir@
+abs_builddir = @abs_builddir@
+abs_srcdir = @abs_srcdir@
+abs_top_builddir = @abs_top_builddir@
+abs_top_srcdir = @abs_top_srcdir@
+ac_ct_AR = @ac_ct_AR@
+ac_ct_CC = @ac_ct_CC@
+ac_ct_CXX = @ac_ct_CXX@
+ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+builddir = @builddir@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+enable_gss = @enable_gss@
+enable_ipv6 = @enable_ipv6@
+enable_mountconfig = @enable_mountconfig@
+enable_nfsv4 = @enable_nfsv4@
+enable_nfsv41 = @enable_nfsv41@
+enable_svcgss = @enable_svcgss@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+kprefix = @kprefix@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+mountfile = @mountfile@
+nfsconfig = @nfsconfig@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+rpc_pipefsmount = @rpc_pipefsmount@
+runstatedir = @runstatedir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+srcdir = @srcdir@
+startstatd = @startstatd@
+statdpath = @statdpath@
+statduser = @statduser@
+statedir = @statedir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+top_build_prefix = @top_build_prefix@
+top_builddir = @top_builddir@
+top_srcdir = @top_srcdir@
+unitdir = @unitdir@
+
+# These binaries go in /sbin (not /usr/sbin), unless CONFIG_SBIN_OVERRIDE
+# is disabled as may be appropriate when /sbin is a symlink.
+# Note that we don't use "if CONFIG_SBIN_OVERRIDE" as that
+# causes autotools to notice the override and disable it.
+@CONFIG_SBIN_OVERRIDE_TRUE@sbindir = /sbin
+man8_MANS = mount.nfs.man umount.nfs.man
+man5_MANS = nfs.man $(am__append_2)
+EXTRA_DIST = nfsmount.conf $(man8_MANS) $(man5_MANS) $(am__append_3)
+mount_common = error.c network.c token.c parse_opt.c parse_dev.c \
+ nfsmount.c nfs4mount.c stropts.c mount_constants.h error.h \
+ network.h token.h parse_opt.h parse_dev.h nfs4_mount.h \
+ stropts.h version.h mount_config.h utils.c utils.h nfs_mount.h \
+ $(am__append_1)
+mount_nfs_LDADD = ../../support/nfs/libnfs.la \
+ ../../support/export/libexport.a \
+ ../../support/reexport/libreexport.a \
+ ../../support/misc/libmisc.a $(LIBTIRPC) $(LIBPTHREAD) \
+ $(am__append_5)
+mount_nfs_SOURCES = $(mount_common) $(am__append_4) $(am__append_6)
+MAINTAINERCLEANFILES = Makefile.in
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .lo .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
+ && { if test -f $@; then exit 0; else break; fi; }; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu utils/mount/Makefile'; \
+ $(am__cd) $(top_srcdir) && \
+ $(AUTOMAKE) --gnu utils/mount/Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__maybe_remake_depfiles);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(am__aclocal_m4_deps):
+install-sbinPROGRAMS: $(sbin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ if test -n "$$list"; then \
+ echo " $(MKDIR_P) '$(DESTDIR)$(sbindir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(sbindir)" || exit 1; \
+ fi; \
+ for p in $$list; do echo "$$p $$p"; done | \
+ sed 's/$(EXEEXT)$$//' | \
+ while read p p1; do if test -f $$p \
+ || test -f $$p1 \
+ ; then echo "$$p"; echo "$$p"; else :; fi; \
+ done | \
+ sed -e 'p;s,.*/,,;n;h' \
+ -e 's|.*|.|' \
+ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \
+ sed 'N;N;N;s,\n, ,g' | \
+ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \
+ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \
+ if ($$2 == $$4) files[d] = files[d] " " $$1; \
+ else { print "f", $$3 "/" $$4, $$1; } } \
+ END { for (d in files) print "f", d, files[d] }' | \
+ while read type dir files; do \
+ if test "$$dir" = .; then dir=; else dir=/$$dir; fi; \
+ test -z "$$files" || { \
+ echo " $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files '$(DESTDIR)$(sbindir)$$dir'"; \
+ $(INSTALL_PROGRAM_ENV) $(LIBTOOL) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=install $(INSTALL_PROGRAM) $$files "$(DESTDIR)$(sbindir)$$dir" || exit $$?; \
+ } \
+ ; done
+
+uninstall-sbinPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(sbin_PROGRAMS)'; test -n "$(sbindir)" || list=; \
+ files=`for p in $$list; do echo "$$p"; done | \
+ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \
+ -e 's/$$/$(EXEEXT)/' \
+ `; \
+ test -n "$$list" || exit 0; \
+ echo " ( cd '$(DESTDIR)$(sbindir)' && rm -f" $$files ")"; \
+ cd "$(DESTDIR)$(sbindir)" && rm -f $$files
+
+clean-sbinPROGRAMS:
+ @list='$(sbin_PROGRAMS)'; test -n "$$list" || exit 0; \
+ echo " rm -f" $$list; \
+ rm -f $$list || exit $$?; \
+ test -n "$(EXEEXT)" || exit 0; \
+ list=`for p in $$list; do echo "$$p"; done | sed 's/$(EXEEXT)$$//'`; \
+ echo " rm -f" $$list; \
+ rm -f $$list
+
+mount.nfs$(EXEEXT): $(mount_nfs_OBJECTS) $(mount_nfs_DEPENDENCIES) $(EXTRA_mount_nfs_DEPENDENCIES)
+ @rm -f mount.nfs$(EXEEXT)
+ $(AM_V_CCLD)$(LINK) $(mount_nfs_OBJECTS) $(mount_nfs_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/configfile.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/error.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fstab.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mount.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mount_libmount.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/network.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nfs4mount.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nfsmount.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nfsumount.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parse_dev.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parse_opt.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/stropts.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/token.Po@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/utils.Po@am__quote@ # am--include-marker
+
+$(am__depfiles_remade):
+ @$(MKDIR_P) $(@D)
+ @echo '# dummy' >$@-t && $(am__mv) $@-t $@
+
+am--depfiles: $(am__depfiles_remade)
+
+.c.o:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(COMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(COMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
+
+.c.lo:
+@am__fastdepCC_TRUE@ $(AM_V_CC)$(LTCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
+@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Plo
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(LTCOMPILE) -c -o $@ $<
+
+mostlyclean-libtool:
+ -rm -f *.lo
+
+clean-libtool:
+ -rm -rf .libs _libs
+install-man5: $(man5_MANS)
+ @$(NORMAL_INSTALL)
+ @list1='$(man5_MANS)'; \
+ list2=''; \
+ test -n "$(man5dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man5dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man5dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.5[a-z]*$$/p'; \
+ fi; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man5dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man5dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man5dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man5dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man5:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man5_MANS)'; test -n "$(man5dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^5][0-9a-z]*$$,5,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man5dir)'; $(am__uninstall_files_from_dir)
+install-man8: $(man8_MANS)
+ @$(NORMAL_INSTALL)
+ @list1='$(man8_MANS)'; \
+ list2=''; \
+ test -n "$(man8dir)" \
+ && test -n "`echo $$list1$$list2`" \
+ || exit 0; \
+ echo " $(MKDIR_P) '$(DESTDIR)$(man8dir)'"; \
+ $(MKDIR_P) "$(DESTDIR)$(man8dir)" || exit 1; \
+ { for i in $$list1; do echo "$$i"; done; \
+ if test -n "$$list2"; then \
+ for i in $$list2; do echo "$$i"; done \
+ | sed -n '/\.8[a-z]*$$/p'; \
+ fi; \
+ } | while read p; do \
+ if test -f $$p; then d=; else d="$(srcdir)/"; fi; \
+ echo "$$d$$p"; echo "$$p"; \
+ done | \
+ sed -e 'n;s,.*/,,;p;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,' | \
+ sed 'N;N;s,\n, ,g' | { \
+ list=; while read file base inst; do \
+ if test "$$base" = "$$inst"; then list="$$list $$file"; else \
+ echo " $(INSTALL_DATA) '$$file' '$(DESTDIR)$(man8dir)/$$inst'"; \
+ $(INSTALL_DATA) "$$file" "$(DESTDIR)$(man8dir)/$$inst" || exit $$?; \
+ fi; \
+ done; \
+ for i in $$list; do echo "$$i"; done | $(am__base_list) | \
+ while read files; do \
+ test -z "$$files" || { \
+ echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(man8dir)'"; \
+ $(INSTALL_DATA) $$files "$(DESTDIR)$(man8dir)" || exit $$?; }; \
+ done; }
+
+uninstall-man8:
+ @$(NORMAL_UNINSTALL)
+ @list='$(man8_MANS)'; test -n "$(man8dir)" || exit 0; \
+ files=`{ for i in $$list; do echo "$$i"; done; \
+ } | sed -e 's,.*/,,;h;s,.*\.,,;s,^[^8][0-9a-z]*$$,8,;x' \
+ -e 's,\.[0-9a-z]*$$,,;$(transform);G;s,\n,.,'`; \
+ dir='$(DESTDIR)$(man8dir)'; $(am__uninstall_files_from_dir)
+
+ID: $(am__tagged_files)
+ $(am__define_uniq_tagged_files); mkid -fID $$unique
+tags: tags-am
+TAGS: tags
+
+tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ set x; \
+ here=`pwd`; \
+ $(am__define_uniq_tagged_files); \
+ shift; \
+ if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ if test $$# -gt 0; then \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ "$$@" $$unique; \
+ else \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$unique; \
+ fi; \
+ fi
+ctags: ctags-am
+
+CTAGS: ctags
+ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
+ $(am__define_uniq_tagged_files); \
+ test -z "$(CTAGS_ARGS)$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && $(am__cd) $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) "$$here"
+cscopelist: cscopelist-am
+
+cscopelist-am: $(am__tagged_files)
+ list='$(am__tagged_files)'; \
+ case "$(srcdir)" in \
+ [\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
+ *) sdir=$(subdir)/$(srcdir) ;; \
+ esac; \
+ for i in $$list; do \
+ if test -f "$$i"; then \
+ echo "$(subdir)/$$i"; \
+ else \
+ echo "$$sdir/$$i"; \
+ fi; \
+ done >> $(top_builddir)/cscope.files
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+distdir: $(BUILT_SOURCES)
+ $(MAKE) $(AM_MAKEFLAGS) distdir-am
+
+distdir-am: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
+ list='$(DISTFILES)'; \
+ dist_files=`for file in $$list; do echo $$file; done | \
+ sed -e "s|^$$srcdirstrip/||;t" \
+ -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
+ case $$dist_files in \
+ */*) $(MKDIR_P) `echo "$$dist_files" | \
+ sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
+ sort -u` ;; \
+ esac; \
+ for file in $$dist_files; do \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ if test -d $$d/$$file; then \
+ dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test -d "$(distdir)/$$file"; then \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
+ find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
+ fi; \
+ cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
+ else \
+ test -f "$(distdir)/$$file" \
+ || cp -p $$d/$$file "$(distdir)/$$file" \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS) $(MANS)
+installdirs:
+ for dir in "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(man8dir)"; do \
+ test -z "$$dir" || $(MKDIR_P) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ if test -z '$(STRIP)'; then \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ install; \
+ else \
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
+ fi
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+ -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+ -test -z "$(MAINTAINERCLEANFILES)" || rm -f $(MAINTAINERCLEANFILES)
+clean: clean-am
+
+clean-am: clean-generic clean-libtool clean-sbinPROGRAMS \
+ mostlyclean-am
+
+distclean: distclean-am
+ -rm -f ./$(DEPDIR)/configfile.Po
+ -rm -f ./$(DEPDIR)/error.Po
+ -rm -f ./$(DEPDIR)/fstab.Po
+ -rm -f ./$(DEPDIR)/mount.Po
+ -rm -f ./$(DEPDIR)/mount_libmount.Po
+ -rm -f ./$(DEPDIR)/network.Po
+ -rm -f ./$(DEPDIR)/nfs4mount.Po
+ -rm -f ./$(DEPDIR)/nfsmount.Po
+ -rm -f ./$(DEPDIR)/nfsumount.Po
+ -rm -f ./$(DEPDIR)/parse_dev.Po
+ -rm -f ./$(DEPDIR)/parse_opt.Po
+ -rm -f ./$(DEPDIR)/stropts.Po
+ -rm -f ./$(DEPDIR)/token.Po
+ -rm -f ./$(DEPDIR)/utils.Po
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+html-am:
+
+info: info-am
+
+info-am:
+
+install-data-am: install-man
+
+install-dvi: install-dvi-am
+
+install-dvi-am:
+
+install-exec-am: install-sbinPROGRAMS
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) install-exec-hook
+install-html: install-html-am
+
+install-html-am:
+
+install-info: install-info-am
+
+install-info-am:
+
+install-man: install-man5 install-man8
+
+install-pdf: install-pdf-am
+
+install-pdf-am:
+
+install-ps: install-ps-am
+
+install-ps-am:
+
+installcheck-am:
+
+maintainer-clean: maintainer-clean-am
+ -rm -f ./$(DEPDIR)/configfile.Po
+ -rm -f ./$(DEPDIR)/error.Po
+ -rm -f ./$(DEPDIR)/fstab.Po
+ -rm -f ./$(DEPDIR)/mount.Po
+ -rm -f ./$(DEPDIR)/mount_libmount.Po
+ -rm -f ./$(DEPDIR)/network.Po
+ -rm -f ./$(DEPDIR)/nfs4mount.Po
+ -rm -f ./$(DEPDIR)/nfsmount.Po
+ -rm -f ./$(DEPDIR)/nfsumount.Po
+ -rm -f ./$(DEPDIR)/parse_dev.Po
+ -rm -f ./$(DEPDIR)/parse_opt.Po
+ -rm -f ./$(DEPDIR)/stropts.Po
+ -rm -f ./$(DEPDIR)/token.Po
+ -rm -f ./$(DEPDIR)/utils.Po
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic \
+ mostlyclean-libtool
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-man uninstall-sbinPROGRAMS
+ @$(NORMAL_INSTALL)
+ $(MAKE) $(AM_MAKEFLAGS) uninstall-hook
+uninstall-man: uninstall-man5 uninstall-man8
+
+.MAKE: install-am install-exec-am install-strip uninstall-am
+
+.PHONY: CTAGS GTAGS TAGS all all-am am--depfiles check check-am clean \
+ clean-generic clean-libtool clean-sbinPROGRAMS cscopelist-am \
+ ctags ctags-am distclean distclean-compile distclean-generic \
+ distclean-libtool distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-data \
+ install-data-am install-dvi install-dvi-am install-exec \
+ install-exec-am install-exec-hook install-html install-html-am \
+ install-info install-info-am install-man install-man5 \
+ install-man8 install-pdf install-pdf-am install-ps \
+ install-ps-am install-sbinPROGRAMS install-strip installcheck \
+ installcheck-am installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \
+ tags tags-am uninstall uninstall-am uninstall-hook \
+ uninstall-man uninstall-man5 uninstall-man8 \
+ uninstall-sbinPROGRAMS
+
+.PRECIOUS: Makefile
+
+
+install-exec-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ ln -sf mount.nfs mount.nfs4 && \
+ ln -sf mount.nfs umount.nfs && \
+ ln -sf mount.nfs umount.nfs4 && \
+ chmod 4511 mount.nfs )
+uninstall-hook:
+ (cd $(DESTDIR)$(sbindir) && \
+ rm -f mount.nfs4 umount.nfs umount.nfs4)
+
+install-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $$inst ; \
+ done)
+ (cd $(DESTDIR)$(man5dir) && \
+ for m in $(man5_MANS) $(dist_man5_MANS) $(nodist_man5_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/5/'`; \
+ rm -f $$inst ; \
+ done)
+
+uninstall-man-links:
+ (cd $(DESTDIR)$(man8dir) && \
+ for m in $(man8_MANS) $(dist_man8_MANS) $(nodist_man8_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/8/'`; \
+ rm -f $$inst ; \
+ done)
+ (cd $(DESTDIR)$(man5dir) && \
+ for m in $(man5_MANS) $(dist_man5_MANS) $(nodist_man5_MANS); do \
+ inst=`echo $$m | sed -e 's/man$$/5/'`; \
+ rm -f $$inst ; \
+ done)
+
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/utils/mount/configfile.c b/utils/mount/configfile.c
new file mode 100644
index 0000000..1d88cbf
--- /dev/null
+++ b/utils/mount/configfile.c
@@ -0,0 +1,347 @@
+/*
+ * configfile.c -- mount configuration file manipulation
+ * Copyright (C) 2008 Red Hat, Inc <nfs@redhat.com>
+ *
+ * - Routines use to create mount options from the mount
+ * configuration file.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "xlog.h"
+#include "mount.h"
+#include "parse_opt.h"
+#include "network.h"
+#include "conffile.h"
+#include "mount_config.h"
+
+#define KBYTES(x) ((x) * (1024))
+#define MEGABYTES(x) ((x) * (1048576))
+#define GIGABYTES(x) ((x) * (1073741824))
+
+#ifndef NFSMOUNT_GLOBAL_OPTS
+#define NFSMOUNT_GLOBAL_OPTS "NFSMount_Global_Options"
+#endif
+
+#ifndef NFSMOUNT_MOUNTPOINT
+#define NFSMOUNT_MOUNTPOINT "MountPoint"
+#endif
+
+#ifndef NFSMOUNT_SERVER
+#define NFSMOUNT_SERVER "Server"
+#endif
+
+enum {
+ MNT_NOARG=0,
+ MNT_INTARG,
+ MNT_STRARG,
+ MNT_SPEC,
+ MNT_UNSET
+};
+struct mnt_alias {
+ char *alias;
+ char *opt;
+ int argtype;
+} mnt_alias_tab[] = {
+ {"background", "bg", MNT_NOARG},
+ {"foreground", "fg", MNT_NOARG},
+ {"sloppy", "sloppy", MNT_NOARG},
+};
+int mnt_alias_sz = (sizeof(mnt_alias_tab)/sizeof(mnt_alias_tab[0]));
+
+static const char *version_keys[] = {
+ "v3", "v4", "vers", "nfsvers", "minorversion", NULL
+};
+
+static int strict;
+
+static int is_version(const char *field)
+{
+ int i;
+ for (i = 0; version_keys[i] ; i++)
+ if (strcmp(version_keys[i], field) == 0)
+ return 1;
+ if (strncmp(field, "v4.", 3) == 0)
+ return 1;
+ return 0;
+}
+
+/*
+ * See if the option is an alias, if so return the
+ * real mount option along with the argument type.
+ */
+inline static
+char *mountopts_alias(char *opt, int *argtype)
+{
+ int i;
+
+ *argtype = MNT_UNSET;
+ for (i=0; i < mnt_alias_sz; i++) {
+ if (strcasecmp(opt, mnt_alias_tab[i].alias) != 0)
+ continue;
+ *argtype = mnt_alias_tab[i].argtype;
+ return mnt_alias_tab[i].opt;
+ }
+ /* Make option names case-insensitive */
+ upper2lower(opt);
+
+ return opt;
+}
+/*
+ * Convert numeric strings that end with 'k', 'm' or 'g'
+ * into numeric strings with the real value.
+ * Meaning '8k' becomes '8094'.
+ */
+static char *mountopts_convert(char *value)
+{
+ unsigned long long factor, num;
+ static char buf[64];
+ char *ch;
+
+ ch = &value[strlen(value)-1];
+ switch (tolower(*ch)) {
+ case 'k':
+ factor = KBYTES(1);
+ break;
+ case 'm':
+ factor = MEGABYTES(1);
+ break;
+ case 'g':
+ factor = GIGABYTES(1);
+ break;
+ default:
+ return value;
+ }
+ *ch = '\0';
+ if (strncmp(value, "0x", 2) == 0) {
+ num = strtol(value, (char **)NULL, 16);
+ } else if (strncmp(value, "0", 1) == 0) {
+ num = strtol(value, (char **)NULL, 8);
+ } else {
+ num = strtol(value, (char **)NULL, 10);
+ }
+ num *= factor;
+ snprintf(buf, 64, "%lld", num);
+
+ return buf;
+}
+
+struct nfs_version config_default_vers;
+unsigned long config_default_proto;
+extern sa_family_t config_default_family;
+
+/*
+ * Check to see if a default value is being set.
+ * If so, set the appropriate global value which will
+ * be used as the initial value in the server negation.
+ */
+static int
+default_value(char *mopt)
+{
+ struct mount_options *options = NULL;
+ int dftlen = strlen("default");
+ char *field;
+
+ if (strncasecmp(mopt, "default", dftlen) != 0)
+ return 0;
+
+ field = mopt + dftlen;
+ if (strncasecmp(field, "proto", strlen("proto")) == 0) {
+ if ((options = po_split(field)) != NULL) {
+ if (!nfs_nfs_protocol(options, &config_default_proto)) {
+ xlog_warn("Unable to set default protocol : %s",
+ strerror(errno));
+ }
+ if (!nfs_nfs_proto_family(options, &config_default_family)) {
+ xlog_warn("Unable to set default family : %s",
+ strerror(errno));
+ }
+ } else {
+ xlog_warn("Unable to alloc memory for default protocol");
+ }
+ } else if (strncasecmp(field, "vers", strlen("vers")) == 0) {
+ if ((options = po_split(field)) != NULL) {
+ if (!nfs_nfs_version("nfs", options, &config_default_vers)) {
+ xlog_warn("Unable to set default version: %s",
+ strerror(errno));
+ }
+ } else {
+ xlog_warn("Unable to alloc memory for default version");
+ }
+ } else
+ xlog_warn("Invalid default setting: '%s'", mopt);
+
+ if (options)
+ po_destroy(options);
+
+ return 1;
+}
+/*
+ * Parse the given section of the configuration
+ * file to if there are any mount options set.
+ * If so, added them to link list.
+ */
+static void
+conf_parse_mntopts(char *section, char *arg, struct mount_options *options)
+{
+ struct conf_list *list;
+ struct conf_list_node *node;
+ char buf[BUFSIZ], *value, *field;
+ char *nvalue, *ptr;
+ int argtype;
+ int have_version = 0;
+
+ if (po_rightmost(options, version_keys) >= 0 ||
+ po_contains_prefix(options, "v4.", NULL, 0) == PO_FOUND)
+ have_version = 1;
+
+ list = conf_get_tag_list(section, arg);
+ TAILQ_FOREACH(node, &list->fields, link) {
+ /*
+ * Do not overwrite options if already exists
+ */
+ field = mountopts_alias(node->field, &argtype);
+ if (po_contains(options, field) == PO_FOUND)
+ continue;
+ /* Some options can be inverted by a "no" prefix.
+ * Check for these.
+ * "no" prefixes are unlikely in the config file as
+ * "option=false" is preferred, but still possible.
+ */
+ if (strncmp(field, "no", 2) == 0 &&
+ po_contains(options, field+2) == PO_FOUND)
+ continue;
+ if (strlen(field) < BUFSIZ-3) {
+ strcat(strcpy(buf, "no"), field);
+ if (po_contains(options, buf) == PO_FOUND)
+ continue;
+ }
+
+ /* If fg or bg already present, ignore bg or fg */
+ if (strcmp(field, "fg") == 0 &&
+ po_contains(options, "bg") == PO_FOUND)
+ continue;
+ if (strcmp(field, "bg") == 0 &&
+ po_contains(options, "fg") == PO_FOUND)
+ continue;
+
+ if (is_version(field)) {
+ if (have_version)
+ continue;
+ have_version = 1;
+ }
+
+ buf[0] = '\0';
+ value = conf_get_section(section, arg, node->field);
+ if (value == NULL)
+ continue;
+ if (strcasecmp(value, "false") == 0) {
+ if (argtype != MNT_NOARG)
+ snprintf(buf, BUFSIZ, "no%s", field);
+ else if (strcasecmp(field, "bg") == 0)
+ snprintf(buf, BUFSIZ, "fg");
+ else if (strcasecmp(field, "fg") == 0)
+ snprintf(buf, BUFSIZ, "bg");
+ else if (strcasecmp(field, "sloppy") == 0)
+ strict = 1;
+ } else if (strcasecmp(value, "true") == 0) {
+ if ((strcasecmp(field, "sloppy") == 0) && strict)
+ continue;
+ snprintf(buf, BUFSIZ, "%s", field);
+ } else {
+ nvalue = strdup(value);
+ ptr = mountopts_convert(nvalue);
+ snprintf(buf, BUFSIZ, "%s=%s", field, ptr);
+ free(nvalue);
+ }
+ if (buf[0] == '\0')
+ continue;
+
+ po_append(options, buf);
+ default_value(buf);
+ }
+ conf_free_list(list);
+}
+
+/*
+ * Concatenate options from the configuration file with the
+ * given options by building a link list of options from the
+ * different sections in the conf file. Options that exists
+ * in the either the given options or link list are not
+ * overwritten so it matter which when each section is
+ * parsed.
+ */
+char *conf_get_mntopts(char *spec, char *mount_point,
+ char *mount_opts)
+{
+ struct mount_options *options;
+ char *ptr, *server;
+
+ strict = 0;
+ options = po_split(mount_opts);
+ if (!options) {
+ xlog_warn("conf_get_mountops: Unable calloc memory for options");
+ return mount_opts;
+ }
+ /*
+ * First see if there are any mount options relative
+ * to the mount point.
+ */
+ conf_parse_mntopts(NFSMOUNT_MOUNTPOINT, mount_point, options);
+
+ /*
+ * Next, see if there are any mount options relative
+ * to the server
+ */
+ server = strdup(spec);
+ if (server == NULL) {
+ xlog_warn("conf_get_mountops: Unable calloc memory for server");
+ po_destroy(options);
+ return mount_opts;
+ }
+ if ((ptr = strchr(server, ':')) != NULL)
+ *ptr='\0';
+ conf_parse_mntopts(NFSMOUNT_SERVER, server, options);
+ free(server);
+
+ /*
+ * Finally process all the global mount options.
+ */
+ conf_parse_mntopts(NFSMOUNT_GLOBAL_OPTS, NULL, options);
+
+ /*
+ * Strip out defaults, which have already been handled,
+ * then join the rest and return.
+ */
+ while (po_contains_prefix(options, "default", &ptr, 0) == PO_FOUND) {
+ ptr = strdup(ptr);
+ po_remove_all(options, ptr);
+ free(ptr);
+ }
+
+ po_join(options, &mount_opts);
+ po_destroy(options);
+
+ return mount_opts;
+}
diff --git a/utils/mount/error.c b/utils/mount/error.c
new file mode 100644
index 0000000..9ddbcc0
--- /dev/null
+++ b/utils/mount/error.c
@@ -0,0 +1,360 @@
+/*
+ * error.c -- Common error handling functions
+ *
+ * Copyright (C) 2007 Oracle. All rights reserved.
+ * Copyright (C) 2007 Chuck Lever <chuck.lever@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA
+ *
+ * To Do:
+ * + Proper support for internationalization
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <rpc/rpc.h>
+
+#include "xcommon.h"
+#include "nls.h"
+#include "mount.h"
+#include "error.h"
+
+#ifdef HAVE_RPCSVC_NFS_PROT_H
+#include <rpcsvc/nfs_prot.h>
+#else
+#include <linux/nfs.h>
+#define nfsstat nfs_stat
+#endif
+
+extern char *progname;
+
+static char errbuf[PATH_MAX];
+static char *erreob = &errbuf[PATH_MAX];
+
+/* Convert RPC errors into strings */
+static int rpc_strerror(int spos)
+{
+ int cf_stat = rpc_createerr.cf_stat;
+ int pos = 0, cf_errno = rpc_createerr.cf_error.re_errno;
+ char *ptr, *estr = clnt_sperrno(cf_stat);
+ char *tmp;
+
+ if (estr) {
+ if ((ptr = strchr(estr, ':')))
+ estr = ++ptr;
+
+ tmp = &errbuf[spos];
+ if (cf_stat == RPC_SYSTEMERROR)
+ pos = snprintf(tmp, (erreob - tmp),
+ _("System Error: %s"),
+ strerror(cf_errno));
+ else {
+ if (cf_errno)
+ pos = snprintf(tmp, (erreob - tmp),
+ _("RPC Error:%s; errno = %s"),
+ estr, strerror(cf_errno));
+ else
+ pos = snprintf(tmp, (erreob - tmp),
+ _("RPC Error:%s"), estr);
+ }
+ }
+ return pos;
+}
+
+/**
+ * rpc_mount_errors - log an RPC error that occurred during a user-space mount
+ * @server: C string containing name of server we are attempting to mount
+ * @will_retry: one indicates mount will retry at some later point
+ * @bg: one indicates this is a background mount
+ *
+ * Extracts the error code from the user-space RPC library, and reports it
+ * on stderr (fg mount) or in the system log (bg mount).
+ */
+void rpc_mount_errors(char *server, int will_retry, int bg)
+{
+ int pos = 0;
+ char *tmp;
+ static int onlyonce = 0;
+
+ tmp = &errbuf[pos];
+ if (bg)
+ pos = snprintf(tmp, (erreob - tmp),
+ _("mount to NFS server '%s' failed: "),
+ server);
+ else
+ pos = snprintf(tmp, (erreob - tmp),
+ _("%s: mount to NFS server '%s' failed: "),
+ progname, server);
+
+ tmp = &errbuf[pos];
+ if (rpc_createerr.cf_stat == RPC_TIMEDOUT) {
+ if (will_retry)
+ pos = snprintf(tmp, (erreob - tmp),
+ _("timed out, retrying"));
+ else
+ pos = snprintf(tmp, (erreob - tmp),
+ _("timed out, giving up"));
+ } else {
+ pos += rpc_strerror(pos);
+ tmp = &errbuf[pos];
+ if (bg) {
+ if (will_retry)
+ pos = snprintf(tmp, (erreob - tmp),
+ _(", retrying"));
+ else
+ pos = snprintf(tmp, (erreob - tmp),
+ _(", giving up"));
+ }
+ }
+
+ if (bg) {
+ if (onlyonce++ < 1)
+ openlog("mount", LOG_CONS|LOG_PID, LOG_AUTH);
+ syslog(LOG_ERR, "%s", errbuf);
+ } else
+ fprintf(stderr, "%s\n", errbuf);
+}
+
+/**
+ * sys_mount_errors - log an error that occurred during a mount system call
+ * @server: C string containing name of server we are attempting to mount
+ * @error: errno value to report
+ * @will_retry: one indicates mount will retry at some later point
+ * @bg: one indicates this is a background mount
+ *
+ * Passed an errno value generated by a mount system call, and reports it
+ * on stderr (fg mount) or in the system log (bg mount).
+ */
+void sys_mount_errors(char *server, int error, int will_retry, int bg)
+{
+ int pos = 0;
+ char *tmp;
+ static int onlyonce = 0;
+
+ tmp = &errbuf[pos];
+ if (bg)
+ pos = snprintf(tmp, (erreob - tmp),
+ _("mount to NFS server '%s' failed: "),
+ server);
+ else
+ pos = snprintf(tmp, (erreob - tmp),
+ _("%s: mount to NFS server '%s' failed: "),
+ progname, server);
+
+ tmp = &errbuf[pos];
+ if (error == ETIMEDOUT) {
+ if (will_retry)
+ pos = snprintf(tmp, (erreob - tmp),
+ _("timed out, retrying"));
+ else
+ pos = snprintf(tmp, (erreob - tmp),
+ _("timed out, giving up"));
+ } else {
+ if (bg) {
+ if (will_retry)
+ pos = snprintf(tmp, (erreob - tmp),
+ _("%s, retrying"),
+ strerror(error));
+ else
+ pos = snprintf(tmp, (erreob - tmp),
+ _("%s, giving up"),
+ strerror(error));
+ }
+ }
+
+ if (bg) {
+ if (onlyonce++ < 1)
+ openlog("mount", LOG_CONS|LOG_PID, LOG_AUTH);
+ syslog(LOG_ERR, "%s", errbuf);
+ } else
+ fprintf(stderr, "%s\n", errbuf);
+}
+
+/**
+ * mount_error - report a foreground mount error
+ * @spec: C string containing the device name being mounted
+ * @mount_point: C string containing the pathname of the local mounted on dir
+ * @error: errno value to report
+ *
+ */
+void mount_error(const char *spec, const char *mount_point, int error)
+{
+ switch(error) {
+ case EACCES:
+ nfs_error(_("%s: access denied by server while mounting %s"),
+ progname, spec);
+ break;
+ case EINVAL:
+ nfs_error(_("%s: an incorrect mount option was specified for %s"),
+ progname, mount_point);
+ break;
+ case EOPNOTSUPP:
+ nfs_error(_("%s: requested NFS version or transport protocol is not supported for %s"),
+ progname, mount_point);
+ break;
+ case ENOTDIR:
+ if (spec)
+ nfs_error(_("%s: mount spec %s or point %s is not a directory"),
+ progname, spec, mount_point);
+ else
+ nfs_error(_("%s: mount point %s is not a directory"),
+ progname, mount_point);
+ break;
+ case EBUSY:
+ nfs_error(_("%s: %s is busy or already mounted or sharecache fail"),
+ progname, mount_point);
+ break;
+ case ENOENT:
+ if (spec)
+ nfs_error(_("%s: mounting %s failed, reason given by server: %s"),
+ progname, spec, strerror(error));
+ else
+ nfs_error(_("%s: mount point %s does not exist"),
+ progname, mount_point);
+ break;
+ case ESPIPE:
+ rpc_mount_errors((char *)spec, 0, 0);
+ break;
+ case EIO:
+ nfs_error(_("%s: mount system call failed for %s"),
+ progname, mount_point);
+ break;
+ case EFAULT:
+ nfs_error(_("%s: encountered unexpected error condition for %s."),
+ progname, mount_point);
+ nfs_error(_("%s: please report the error to" PACKAGE_BUGREPORT),
+ progname);
+ break;
+ case EALREADY:
+ /* Error message has already been provided */
+ break;
+ default:
+ nfs_error(_("%s: %s for %s on %s"),
+ progname, strerror(error), spec, mount_point);
+ }
+}
+
+/*
+ * umount_error - report a failed umount request
+ * @err: errno value to report
+ * @dev: C string containing the pathname of the local mounted on dir
+ *
+ */
+void umount_error(int err, const char *dev)
+{
+ switch (err) {
+ case ENXIO:
+ nfs_error(_("%s: %s: invalid block device"),
+ progname, dev);
+ break;
+ case EINVAL:
+ nfs_error(_("%s: %s: not mounted"),
+ progname, dev);
+ break;
+ case EIO:
+ nfs_error(_("%s: %s: can't write superblock"),
+ progname, dev);
+ break;
+ case EBUSY:
+ nfs_error(_("%s: %s: device is busy"),
+ progname, dev);
+ break;
+ case ENOENT:
+ nfs_error(_("%s: %s: not found"),
+ progname, dev);
+ break;
+ case EPERM:
+ nfs_error(_("%s: %s: must be superuser to umount"),
+ progname, dev);
+ break;
+ case EACCES:
+ nfs_error(_("%s: %s: block devices not permitted on fs"),
+ progname, dev);
+ break;
+ default:
+ nfs_error(_("%s: %s: %s"),
+ progname, dev, strerror(err));
+ break;
+ }
+}
+
+/*
+ * We need to translate between nfs status return values and
+ * the local errno values which may not be the same.
+ *
+ * Andreas Schwab <schwab@LS5.informatik.uni-dortmund.de>: change errno:
+ * "after #include <errno.h> the symbol errno is reserved for any use,
+ * it cannot even be used as a struct tag or field name".
+ */
+
+#ifndef EDQUOT
+#define EDQUOT ENOSPC
+#endif
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+static struct {
+ enum nfsstat stat;
+ int errnum;
+} nfs_errtbl[] = {
+ { NFS_OK, 0 },
+ { NFSERR_PERM, EPERM },
+ { NFSERR_NOENT, ENOENT },
+ { NFSERR_IO, EIO },
+ { NFSERR_NXIO, ENXIO },
+ { NFSERR_ACCES, EACCES },
+ { NFSERR_EXIST, EEXIST },
+ { NFSERR_NODEV, ENODEV },
+ { NFSERR_NOTDIR, ENOTDIR },
+ { NFSERR_ISDIR, EISDIR },
+#ifdef NFSERR_INVAL
+ { NFSERR_INVAL, EINVAL }, /* that Sun forgot */
+#endif
+ { NFSERR_FBIG, EFBIG },
+ { NFSERR_NOSPC, ENOSPC },
+ { NFSERR_ROFS, EROFS },
+ { NFSERR_NAMETOOLONG, ENAMETOOLONG },
+ { NFSERR_NOTEMPTY, ENOTEMPTY },
+ { NFSERR_DQUOT, EDQUOT },
+ { NFSERR_STALE, ESTALE },
+#ifdef EWFLUSH
+ { NFSERR_WFLUSH, EWFLUSH },
+#endif
+ /* Throw in some NFSv3 values for even more fun (HP returns these) */
+ { 71, EREMOTE },
+};
+
+char *nfs_strerror(unsigned int stat)
+{
+ unsigned int i;
+ static char buf[256];
+
+ for (i = 0; i < ARRAY_SIZE(nfs_errtbl); i++) {
+ if (nfs_errtbl[i].stat == stat)
+ return strerror(nfs_errtbl[i].errnum);
+ }
+ sprintf(buf, _("unknown nfs status return value: %u"), stat);
+ return buf;
+}
diff --git a/utils/mount/error.h b/utils/mount/error.h
new file mode 100644
index 0000000..ef80fd0
--- /dev/null
+++ b/utils/mount/error.h
@@ -0,0 +1,35 @@
+/*
+ * error.h: Common error handling functions
+ *
+ * Copyright (C) 2007 Oracle. All rights reserved.
+ * Copyright (C) 2007 Chuck Lever <chuck.lever@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA
+ *
+ */
+
+#ifndef _NFS_UTILS_MOUNT_ERROR_H
+#define _NFS_UTILS_MOUNT_ERROR_H
+
+char *nfs_strerror(unsigned int);
+
+void mount_error(const char *, const char *, int);
+void rpc_mount_errors(char *, int, int);
+void sys_mount_errors(char *, int, int, int);
+
+void umount_error(int, const char *);
+
+#endif /* _NFS_UTILS_MOUNT_ERROR_H */
diff --git a/utils/mount/fstab.c b/utils/mount/fstab.c
new file mode 100644
index 0000000..146d8f4
--- /dev/null
+++ b/utils/mount/fstab.c
@@ -0,0 +1,653 @@
+/* 1999-02-22 Arkadiusz Miskiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ * Sun Mar 21 1999 - Arnaldo Carvalho de Melo <acme@conectiva.com.br>
+ * - fixed strerr(errno) in gettext calls
+ *
+ * 2006-06-08 Amit Gud <agud@redhat.com>
+ * - Moved code to nfs-utils/support/nfs from util-linux/mount.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <mntent.h>
+
+#include "fstab.h"
+#include "xcommon.h"
+#include "nfs_mntent.h"
+#include "nfs_paths.h"
+#include "nls.h"
+
+#define LOCK_TIMEOUT 10
+#define streq(s, t) (strcmp ((s), (t)) == 0)
+#define PROC_MOUNTS "/proc/mounts"
+
+extern char *progname;
+extern int verbose;
+
+/* Information about mtab. ------------------------------------*/
+static int have_mtab_info = 0;
+static int var_mtab_does_not_exist = 0;
+static int var_mtab_is_a_symlink = 0;
+
+static void
+get_mtab_info(void) {
+ struct stat mtab_stat;
+
+ if (!have_mtab_info) {
+ if (lstat(MOUNTED, &mtab_stat))
+ var_mtab_does_not_exist = 1;
+ else if (S_ISLNK(mtab_stat.st_mode))
+ var_mtab_is_a_symlink = 1;
+ have_mtab_info = 1;
+ }
+}
+
+void
+reset_mtab_info(void) {
+ have_mtab_info = 0;
+}
+
+int
+mtab_does_not_exist(void) {
+ get_mtab_info();
+ return var_mtab_does_not_exist;
+}
+
+int
+mtab_is_a_symlink(void) {
+ get_mtab_info();
+ return var_mtab_is_a_symlink;
+}
+
+int
+mtab_is_writable() {
+ int fd;
+
+ /* Should we write to /etc/mtab upon an update?
+ Probably not if it is a symlink to /proc/mounts, since that
+ would create a file /proc/mounts in case the proc filesystem
+ is not mounted. */
+ if (mtab_is_a_symlink())
+ return 0;
+
+ fd = open(MOUNTED, O_RDWR | O_CREAT, 0644);
+ if (fd >= 0) {
+ close(fd);
+ return 1;
+ } else
+ return 0;
+}
+
+/* Contents of mtab and fstab ---------------------------------*/
+
+struct mntentchn mounttable;
+static int got_mtab = 0;
+struct mntentchn procmounts;
+static int got_procmounts = 0;
+struct mntentchn fstab;
+static int got_fstab = 0;
+
+static void read_mounttable(void);
+static void read_procmounts(void);
+static void read_fstab(void);
+
+static struct mntentchn *
+mtab_head(void)
+{
+ if (!got_mtab)
+ read_mounttable();
+ return &mounttable;
+}
+
+static struct mntentchn *
+procmounts_head(void)
+{
+ if (!got_procmounts)
+ read_procmounts();
+ return &procmounts;
+}
+
+static struct mntentchn *
+fstab_head(void)
+{
+ if (!got_fstab)
+ read_fstab();
+ return &fstab;
+}
+
+#if 0
+static void
+my_free(const void *s) {
+ if (s)
+ free((void *) s);
+}
+
+static void
+discard_mntentchn(struct mntentchn *mc0) {
+ struct mntentchn *mc, *mc1;
+
+ for (mc = mc0->nxt; mc && mc != mc0; mc = mc1) {
+ mc1 = mc->nxt;
+ my_free(mc->m.mnt_fsname);
+ my_free(mc->m.mnt_dir);
+ my_free(mc->m.mnt_type);
+ my_free(mc->m.mnt_opts);
+ free(mc);
+ }
+}
+#endif
+
+static void
+read_mntentchn(mntFILE *mfp, const char *fnam, struct mntentchn *mc0) {
+ struct mntentchn *mc = mc0;
+ struct mntent *mnt;
+
+ while ((mnt = nfs_getmntent(mfp)) != NULL) {
+ if (!streq(mnt->mnt_type, MNTTYPE_IGNORE)) {
+ mc->nxt = (struct mntentchn *) xmalloc(sizeof(*mc));
+ mc->nxt->prev = mc;
+ mc = mc->nxt;
+ mc->m = *mnt;
+ mc->nxt = mc0;
+ }
+ }
+ mc0->prev = mc;
+ if (ferror(mfp->mntent_fp)) {
+ int errsv = errno;
+ nfs_error(_("warning: error reading %s: %s"),
+ fnam, strerror (errsv));
+ mc0->nxt = mc0->prev = NULL;
+ }
+ nfs_endmntent(mfp);
+}
+
+/*
+ * Read /etc/mtab. If that fails, try /proc/mounts.
+ * This produces a linked list. The list head mounttable is a dummy.
+ * Return 0 on success.
+ */
+static void
+read_mounttable() {
+ mntFILE *mfp;
+ const char *fnam;
+ struct mntentchn *mc = &mounttable;
+
+ got_mtab = 1;
+ mc->nxt = mc->prev = NULL;
+
+ fnam = MOUNTED;
+ mfp = nfs_setmntent (fnam, "r");
+ if (mfp == NULL || mfp->mntent_fp == NULL) {
+ int errsv = errno;
+ fnam = PROC_MOUNTS;
+ mfp = nfs_setmntent (fnam, "r");
+ if (mfp == NULL || mfp->mntent_fp == NULL) {
+ nfs_error(_("warning: can't open %s: %s"),
+ MOUNTED, strerror (errsv));
+ return;
+ }
+ if (verbose)
+ printf(_("%s: could not open %s; using %s instead\n"),
+ progname, MOUNTED, PROC_MOUNTS);
+ }
+ read_mntentchn(mfp, fnam, mc);
+}
+
+/*
+ * Read /proc/mounts.
+ * This produces a linked list. The list head procmounts is a dummy.
+ * Return 0 on success.
+ */
+static void
+read_procmounts() {
+ mntFILE *mfp;
+ const char *fnam;
+ struct mntentchn *mc = &procmounts;
+
+ got_procmounts = 1;
+ mc->nxt = mc->prev = NULL;
+
+ fnam = PROC_MOUNTS;
+ mfp = nfs_setmntent(fnam, "r");
+ if (mfp == NULL || mfp->mntent_fp == NULL) {
+ nfs_error(_("warning: can't open %s: %s"),
+ PROC_MOUNTS, strerror (errno));
+ return;
+ }
+ read_mntentchn(mfp, fnam, mc);
+}
+
+static void
+read_fstab()
+{
+ mntFILE *mfp = NULL;
+ const char *fnam;
+ struct mntentchn *mc = &fstab;
+
+ got_fstab = 1;
+ mc->nxt = mc->prev = NULL;
+
+ fnam = _PATH_FSTAB;
+ mfp = nfs_setmntent (fnam, "r");
+ if (mfp == NULL || mfp->mntent_fp == NULL) {
+ int errsv = errno;
+ nfs_error(_("warning: can't open %s: %s"),
+ _PATH_FSTAB, strerror (errsv));
+ return;
+ }
+ read_mntentchn(mfp, fnam, mc);
+}
+
+/*
+ * Given the directory name NAME, and the place MCPREV we found it last time,
+ * try to find more occurrences.
+ */
+struct mntentchn *
+getmntdirbackward (const char *name, struct mntentchn *mcprev) {
+ struct mntentchn *mc, *mc0;
+
+ mc0 = mtab_head();
+ if (!mcprev)
+ mcprev = mc0;
+ for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev)
+ if (streq(mc->m.mnt_dir, name))
+ return mc;
+ return NULL;
+}
+
+/*
+ * Given the directory name NAME, and the place MCPREV we found it last time,
+ * try to find more occurrences.
+ */
+struct mntentchn *
+getprocmntdirbackward (const char *name, struct mntentchn *mcprev) {
+ struct mntentchn *mc, *mc0;
+
+ mc0 = procmounts_head();
+ if (!mcprev)
+ mcprev = mc0;
+ for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev)
+ if (streq(mc->m.mnt_dir, name))
+ return mc;
+ return NULL;
+}
+
+/*
+ * Given the device name NAME, and the place MCPREV we found it last time,
+ * try to find more occurrences.
+ */
+struct mntentchn *
+getmntdevbackward (const char *name, struct mntentchn *mcprev) {
+ struct mntentchn *mc, *mc0;
+
+ mc0 = mtab_head();
+ if (!mcprev)
+ mcprev = mc0;
+ for (mc = mcprev->prev; mc && mc != mc0; mc = mc->prev)
+ if (streq(mc->m.mnt_fsname, name))
+ return mc;
+ return NULL;
+}
+
+/* Find the dir FILE in fstab. */
+struct mntentchn *
+getfsfile (const char *file)
+{
+ struct mntentchn *mc, *mc0;
+
+ mc0 = fstab_head();
+ for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
+ if (streq(mc->m.mnt_dir, file))
+ return mc;
+ return NULL;
+}
+
+/* Find the device SPEC in fstab. */
+struct mntentchn *
+getfsspec (const char *spec)
+{
+ struct mntentchn *mc, *mc0;
+
+ mc0 = fstab_head();
+ for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt)
+ if (streq(mc->m.mnt_fsname, spec))
+ return mc;
+ return NULL;
+}
+
+/* Updating mtab ----------------------------------------------*/
+
+/* Flag for already existing lock file. */
+static int we_created_lockfile = 0;
+static int lockfile_fd = -1;
+
+/* Flag to indicate that signals have been set up. */
+static int signals_have_been_setup = 0;
+
+/* Ensure that the lock is released if we are interrupted. */
+extern char *strsignal(int sig); /* not always in <string.h> */
+
+static void
+handler (int sig) {
+ die(EX_USER, "%s", strsignal(sig));
+}
+
+static void
+setlkw_timeout (__attribute__((unused)) int sig) {
+ /* nothing, fcntl will fail anyway */
+}
+
+/* Remove lock file. */
+void
+unlock_mtab (void) {
+ if (we_created_lockfile) {
+ close(lockfile_fd);
+ lockfile_fd = -1;
+ unlink (MOUNTED_LOCK);
+ we_created_lockfile = 0;
+ }
+}
+
+/* Create the lock file.
+ The lock file will be removed if we catch a signal or when we exit. */
+/* The old code here used flock on a lock file /etc/mtab~ and deleted
+ this lock file afterwards. However, as rgooch remarks, that has a
+ race: a second mount may be waiting on the lock and proceed as
+ soon as the lock file is deleted by the first mount, and immediately
+ afterwards a third mount comes, creates a new /etc/mtab~, applies
+ flock to that, and also proceeds, so that the second and third mount
+ now both are scribbling in /etc/mtab.
+ The new code uses a link() instead of a creat(), where we proceed
+ only if it was us that created the lock, and hence we always have
+ to delete the lock afterwards. Now the use of flock() is in principle
+ superfluous, but avoids an arbitrary sleep(). */
+
+/* Where does the link point to? Obvious choices are mtab and mtab~~.
+ HJLu points out that the latter leads to races. Right now we use
+ mtab~.<pid> instead. Use 20 as upper bound for the length of %d. */
+#define MOUNTLOCK_LINKTARGET MOUNTED_LOCK "%d"
+#define MOUNTLOCK_LINKTARGET_LTH (sizeof(MOUNTED_LOCK)+20)
+
+void
+lock_mtab (void) {
+ int tries = 100000, i;
+ char linktargetfile[MOUNTLOCK_LINKTARGET_LTH];
+
+ at_die = unlock_mtab;
+
+ if (!signals_have_been_setup) {
+ int sig = 0;
+ struct sigaction sa;
+
+ sa.sa_flags = 0;
+ sigfillset (&sa.sa_mask);
+
+ while (sigismember (&sa.sa_mask, ++sig) != -1) {
+ switch(sig) {
+ case SIGCHLD:
+ case SIGKILL:
+ case SIGCONT:
+ case SIGSTOP:
+ /* The cannot be caught, or should not,
+ * so don't even try.
+ */
+ continue;
+ case SIGALRM:
+ sa.sa_handler = setlkw_timeout;
+ break;
+ case SIGHUP:
+ case SIGINT:
+ case SIGQUIT:
+ case SIGWINCH:
+ case SIGTSTP:
+ case SIGTTIN:
+ case SIGTTOU:
+ case SIGPIPE:
+ case SIGXFSZ:
+ case SIGXCPU:
+ /* non-priv user can cause these to be
+ * generated, so ignore them.
+ */
+ sa.sa_handler = SIG_IGN;
+ break;
+ default:
+ /* The rest should not be possible, so just
+ * print a message and unlock mtab.
+ */
+ sa.sa_handler = handler;
+ }
+ sigaction (sig, &sa, (struct sigaction *) 0);
+ }
+ signals_have_been_setup = 1;
+ }
+
+ sprintf(linktargetfile, MOUNTLOCK_LINKTARGET, getpid ());
+
+ i = open (linktargetfile, O_WRONLY|O_CREAT, 0);
+ if (i < 0) {
+ int errsv = errno;
+ /* linktargetfile does not exist (as a file)
+ and we cannot create it. Read-only filesystem?
+ Too many files open in the system?
+ Filesystem full? */
+ die (EX_FILEIO, _("can't create lock file %s: %s "
+ "(use -n flag to override)"),
+ linktargetfile, strerror (errsv));
+ }
+ close(i);
+
+ /* Repeat until it was us who made the link */
+ while (!we_created_lockfile) {
+ struct flock flock;
+ int j;
+
+ j = link(linktargetfile, MOUNTED_LOCK);
+
+ {
+ int errsv = errno;
+
+ if (j == 0)
+ we_created_lockfile = 1;
+
+ if (j < 0 && errsv != EEXIST) {
+ (void) unlink(linktargetfile);
+ die (EX_FILEIO, _("can't link lock file %s: %s "
+ "(use -n flag to override)"),
+ MOUNTED_LOCK, strerror (errsv));
+ }
+ }
+
+ lockfile_fd = open (MOUNTED_LOCK, O_WRONLY);
+
+ if (lockfile_fd < 0) {
+ int errsv = errno;
+ /* Strange... Maybe the file was just deleted? */
+ if (errno == ENOENT && tries-- > 0) {
+ if (tries % 200 == 0)
+ usleep(30);
+ continue;
+ }
+ (void) unlink(linktargetfile);
+ die (EX_FILEIO, _("can't open lock file %s: %s "
+ "(use -n flag to override)"),
+ MOUNTED_LOCK, strerror (errsv));
+ }
+
+ flock.l_type = F_WRLCK;
+ flock.l_whence = SEEK_SET;
+ flock.l_start = 0;
+ flock.l_len = 0;
+
+ if (j == 0) {
+ /* We made the link. Now claim the lock. */
+ if (fcntl (lockfile_fd, F_SETLK, &flock) == -1) {
+ if (verbose) {
+ int errsv = errno;
+ nfs_error(_("%s: Can't lock lock file "
+ "%s: %s"), progname,
+ MOUNTED_LOCK,
+ strerror (errsv));
+ }
+ /* proceed anyway */
+ }
+ (void) unlink(linktargetfile);
+ } else {
+ static int retries = 0;
+
+ /* Someone else made the link. Wait. */
+ alarm(LOCK_TIMEOUT);
+ if (fcntl (lockfile_fd, F_SETLKW, &flock) == -1) {
+ int errsv = errno;
+ (void) unlink(linktargetfile);
+ die (EX_FILEIO, _("can't lock lock file %s: %s"),
+ MOUNTED_LOCK, (errno == EINTR) ?
+ _("timed out") : strerror (errsv));
+ }
+ alarm(0);
+ /* Limit the number of iterations - maybe there
+ still is some old /etc/mtab~ */
+ ++retries;
+ if (retries % 200 == 0)
+ usleep(30);
+ if (retries > 100000) {
+ (void) unlink(linktargetfile);
+ close(lockfile_fd);
+ die (EX_FILEIO, _("Cannot create link %s\n"
+ "Perhaps there is a stale lock file?\n"),
+ MOUNTED_LOCK);
+ }
+ close(lockfile_fd);
+ }
+ }
+}
+
+/*
+ * Update the mtab.
+ * Used by umount with null INSTEAD: remove the last DIR entry.
+ * Used by mount upon a remount: update option part,
+ * and complain if a wrong device or type was given.
+ * [Note that often a remount will be a rw remount of /
+ * where there was no entry before, and we'll have to believe
+ * the values given in INSTEAD.]
+ */
+
+void
+update_mtab (const char *dir, struct mntent *instead)
+{
+ mntFILE *mfp, *mftmp;
+ const char *fnam = MOUNTED;
+ struct mntentchn mtabhead; /* dummy */
+ struct mntentchn *mc, *mc0, *absent = NULL;
+
+ if (mtab_does_not_exist() || !mtab_is_writable())
+ return;
+
+ lock_mtab();
+
+ /* having locked mtab, read it again */
+ mc0 = mc = &mtabhead;
+ mc->nxt = mc->prev = NULL;
+
+ mfp = nfs_setmntent(fnam, "r");
+ if (mfp == NULL || mfp->mntent_fp == NULL) {
+ int errsv = errno;
+ nfs_error (_("cannot open %s (%s) - mtab not updated"),
+ fnam, strerror (errsv));
+ goto leave;
+ }
+
+ read_mntentchn(mfp, fnam, mc);
+
+ /* find last occurrence of dir */
+ for (mc = mc0->prev; mc && mc != mc0; mc = mc->prev)
+ if (streq(mc->m.mnt_dir, dir))
+ break;
+ if (mc && mc != mc0) {
+ if (instead == NULL) {
+ /* An umount - remove entry */
+ if (mc && mc != mc0) {
+ mc->prev->nxt = mc->nxt;
+ mc->nxt->prev = mc->prev;
+ free(mc);
+ }
+ } else {
+ /* A remount */
+ mc->m.mnt_opts = instead->mnt_opts;
+ }
+ } else if (instead) {
+ /* not found, add a new entry */
+ absent = xmalloc(sizeof(*absent));
+ absent->m = *instead;
+ absent->nxt = mc0;
+ absent->prev = mc0->prev;
+ mc0->prev = absent;
+ if (mc0->nxt == NULL)
+ mc0->nxt = absent;
+ }
+
+ /* write chain to mtemp */
+ mftmp = nfs_setmntent (MOUNTED_TEMP, "w");
+ if (mftmp == NULL || mftmp->mntent_fp == NULL) {
+ int errsv = errno;
+ nfs_error (_("cannot open %s (%s) - mtab not updated"),
+ MOUNTED_TEMP, strerror (errsv));
+ goto leave;
+ }
+
+ for (mc = mc0->nxt; mc && mc != mc0; mc = mc->nxt) {
+ if (nfs_addmntent(mftmp, &(mc->m)) == 1) {
+ int errsv = errno;
+ die (EX_FILEIO, _("error writing %s: %s"),
+ MOUNTED_TEMP, strerror (errsv));
+ }
+ }
+
+#if 0
+ /* the chain might have strings copied from 'instead',
+ * so we cannot safely free it.
+ * And there is no need anyway because we are going to exit
+ * shortly. So just don't call discard_mntentchn....
+ */
+ discard_mntentchn(mc0);
+#endif
+ if (fchmod (fileno (mftmp->mntent_fp),
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH) < 0) {
+ int errsv = errno;
+ nfs_error(_("%s: error changing mode of %s: %s"),
+ progname, MOUNTED_TEMP, strerror (errsv));
+ }
+ nfs_endmntent (mftmp);
+
+ { /*
+ * If mount is setuid and some non-root user mounts sth,
+ * then mtab.tmp might get the group of this user. Copy uid/gid
+ * from the present mtab before renaming.
+ */
+ struct stat sbuf;
+ if (stat (MOUNTED, &sbuf) == 0) {
+ if (chown (MOUNTED_TEMP, sbuf.st_uid, sbuf.st_gid) < 0) {
+ nfs_error(_("%s: error changing owner of %s: %s"),
+ progname, MOUNTED_TEMP, strerror (errno));
+ }
+ }
+ }
+
+ /* rename mtemp to mtab */
+ if (rename (MOUNTED_TEMP, MOUNTED) < 0) {
+ int errsv = errno;
+ nfs_error(_("%s: can't rename %s to %s: %s\n"),
+ progname, MOUNTED_TEMP, MOUNTED,
+ strerror(errsv));
+ }
+
+ leave:
+ unlock_mtab();
+}
diff --git a/utils/mount/fstab.h b/utils/mount/fstab.h
new file mode 100644
index 0000000..8676c8c
--- /dev/null
+++ b/utils/mount/fstab.h
@@ -0,0 +1,32 @@
+#ifndef _NFS_UTILS_MOUNT_FSTAB_H
+#define _NFS_UTILS_MOUNT_FSTAB_H
+
+#include "nfs_mntent.h"
+
+#ifndef _PATH_FSTAB
+#define _PATH_FSTAB "/etc/fstab"
+#endif
+
+int mtab_is_a_symlink(void);
+int mtab_is_writable(void);
+int mtab_does_not_exist(void);
+void reset_mtab_info(void);
+
+struct mntentchn {
+ struct mntentchn *nxt, *prev;
+ struct mntent m;
+};
+
+struct mntentchn *getmntoptfile (const char *file);
+struct mntentchn *getmntdirbackward (const char *dir, struct mntentchn *mc);
+struct mntentchn *getprocmntdirbackward (const char *name, struct mntentchn *mc);
+struct mntentchn *getmntdevbackward (const char *dev, struct mntentchn *mc);
+
+struct mntentchn *getfsfile (const char *file);
+struct mntentchn *getfsspec (const char *spec);
+
+void lock_mtab (void);
+void unlock_mtab (void);
+void update_mtab (const char *special, struct mntent *with);
+
+#endif /* _NFS_UTILS_MOUNT_FSTAB_H */
diff --git a/utils/mount/mount.c b/utils/mount/mount.c
new file mode 100644
index 0000000..b98f9e0
--- /dev/null
+++ b/utils/mount/mount.c
@@ -0,0 +1,558 @@
+/*
+ * mount.c -- Linux NFS mount
+ *
+ * Copyright (C) 2006 Amit Gud <agud@redhat.com>
+ *
+ * - Basic code and wrapper around mount and umount code of NFS.
+ * Based on util-linux/mount/mount.c.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/mount.h>
+#include <getopt.h>
+#include <mntent.h>
+#include <pwd.h>
+#include <libgen.h>
+
+#include "fstab.h"
+#include "xcommon.h"
+#include "nls.h"
+#include "mount_constants.h"
+#include "mount_config.h"
+#include "nfs_paths.h"
+#include "nfs_mntent.h"
+
+#include "nfs_mount.h"
+#include "nfs4_mount.h"
+#include "mount.h"
+#include "error.h"
+#include "stropts.h"
+#include "utils.h"
+
+char *progname;
+int nfs_mount_data_version;
+int nomtab;
+int verbose;
+int sloppy;
+int string;
+
+#define FOREGROUND (0)
+#define BACKGROUND (1)
+
+static struct option longopts[] = {
+ { "fake", 0, 0, 'f' },
+ { "help", 0, 0, 'h' },
+ { "no-mtab", 0, 0, 'n' },
+ { "read-only", 0, 0, 'r' },
+ { "ro", 0, 0, 'r' },
+ { "verbose", 0, 0, 'v' },
+ { "version", 0, 0, 'V' },
+ { "read-write", 0, 0, 'w' },
+ { "rw", 0, 0, 'w' },
+ { "options", 1, 0, 'o' },
+ { NULL, 0, 0, 0 }
+};
+
+/*
+ * Map from -o and fstab option strings to the flag argument to mount(2).
+ */
+struct opt_map {
+ const char *opt; /* option name */
+ int skip; /* skip in mtab option string */
+ int inv; /* true if flag value should be inverted */
+ int mask; /* flag mask value */
+};
+
+static const struct opt_map opt_map[] = {
+ { "defaults", 0, 0, 0 }, /* default options */
+ { "ro", 1, 0, MS_RDONLY }, /* read-only */
+ { "rw", 1, 1, MS_RDONLY }, /* read-write */
+ { "exec", 0, 1, MS_NOEXEC }, /* permit execution of binaries */
+ { "noexec", 0, 0, MS_NOEXEC }, /* don't execute binaries */
+ { "suid", 0, 1, MS_NOSUID }, /* honor suid executables */
+ { "nosuid", 0, 0, MS_NOSUID }, /* don't honor suid executables */
+ { "dev", 0, 1, MS_NODEV }, /* interpret device files */
+ { "nodev", 0, 0, MS_NODEV }, /* don't interpret devices */
+ { "sync", 0, 0, MS_SYNCHRONOUS}, /* synchronous I/O */
+ { "async", 0, 1, MS_SYNCHRONOUS}, /* asynchronous I/O */
+ { "dirsync", 0, 0, MS_DIRSYNC}, /* synchronous directory modifications */
+ { "remount", 0, 0, MS_REMOUNT}, /* Alter flags of mounted FS */
+ { "bind", 0, 0, MS_BIND }, /* Remount part of tree elsewhere */
+ { "rbind", 0, 0, MS_BIND|MS_REC }, /* Idem, plus mounted subtrees */
+ { "auto", 0, 0, MS_DUMMY }, /* Can be mounted using -a */
+ { "noauto", 0, 0, MS_DUMMY }, /* Can only be mounted explicitly */
+ { "users", 1, 0, MS_USERS }, /* Allow ordinary user to mount */
+ { "nousers", 0, 1, MS_DUMMY }, /* Forbid ordinary user to mount */
+ { "user", 1, 0, MS_USER }, /* Allow ordinary user to mount */
+ { "nouser", 0, 1, MS_DUMMY }, /* Forbid ordinary user to mount */
+ { "owner", 0, 0, MS_DUMMY }, /* Let the owner of the device mount */
+ { "noowner", 0, 0, MS_DUMMY }, /* Device owner has no special privs */
+ { "group", 0, 0, MS_DUMMY }, /* Let the group of the device mount */
+ { "nogroup", 0, 0, MS_DUMMY }, /* Device group has no special privs */
+ { "_netdev", 0, 0, MS_DUMMY}, /* Device requires network */
+ { "comment", 0, 0, MS_DUMMY}, /* fstab comment only (kudzu,_netdev)*/
+
+ /* add new options here */
+#ifdef MS_NOSUB
+ { "sub", 0, 1, MS_NOSUB }, /* allow submounts */
+ { "nosub", 0, 0, MS_NOSUB }, /* don't allow submounts */
+#endif
+#ifdef MS_SILENT
+ { "quiet", 0, 0, MS_SILENT }, /* be quiet */
+ { "loud", 0, 1, MS_SILENT }, /* print out messages. */
+#endif
+#ifdef MS_MANDLOCK
+ { "mand", 0, 0, MS_MANDLOCK }, /* Allow mandatory locks on this FS */
+ { "nomand", 0, 1, MS_MANDLOCK }, /* Forbid mandatory locks on this FS */
+#endif
+ { "loop", 1, 0, MS_DUMMY }, /* use a loop device */
+#ifdef MS_NOATIME
+ { "atime", 0, 1, MS_NOATIME }, /* Update access time */
+ { "noatime", 0, 0, MS_NOATIME }, /* Do not update access time */
+#endif
+#ifdef MS_NODIRATIME
+ { "diratime", 0, 1, MS_NODIRATIME }, /* Update dir access times */
+ { "nodiratime", 0, 0, MS_NODIRATIME },/* Do not update dir access times */
+#endif
+#ifdef MS_RELATIME
+ { "relatime", 0, 0, MS_RELATIME }, /* Update access times relative to
+ mtime/ctime */
+ { "norelatime", 0, 1, MS_RELATIME }, /* Update access time without regard
+ to mtime/ctime */
+#endif
+ { "noquota", 0, 0, MS_DUMMY }, /* Don't enforce quota */
+ { "quota", 0, 0, MS_DUMMY }, /* Enforce user quota */
+ { "usrquota", 0, 0, MS_DUMMY }, /* Enforce user quota */
+ { "grpquota", 0, 0, MS_DUMMY }, /* Enforce group quota */
+ { NULL, 0, 0, 0 }
+};
+
+static void parse_opts(const char *options, int *flags, char **extra_opts);
+
+/*
+ * Build a canonical mount option string for /etc/mtab.
+ */
+static char *fix_opts_string(int flags, const char *extra_opts)
+{
+ const struct opt_map *om;
+ char *new_opts;
+
+ new_opts = xstrdup((flags & MS_RDONLY) ? "ro" : "rw");
+ if (flags & MS_USER) {
+ /* record who mounted this so they can unmount */
+ struct passwd *pw = getpwuid(getuid());
+ if(pw)
+ new_opts = xstrconcat3(new_opts, ",user=", pw->pw_name);
+ }
+ if (flags & MS_USERS)
+ new_opts = xstrconcat3(new_opts, ",users", "");
+
+ for (om = opt_map; om->opt != NULL; om++) {
+ if (om->skip)
+ continue;
+ if (om->inv || !om->mask || (flags & om->mask) != om->mask)
+ continue;
+ new_opts = xstrconcat3(new_opts, ",", om->opt);
+ flags &= ~om->mask;
+ }
+ if (extra_opts && *extra_opts) {
+ new_opts = xstrconcat3(new_opts, ",", extra_opts);
+ }
+ return new_opts;
+}
+
+static void
+init_mntent(struct mntent *mnt, char *fsname, char *dir, char *type,
+ int flags, char *opts)
+{
+ mnt->mnt_fsname = fsname;
+ mnt->mnt_dir = dir;
+ mnt->mnt_type = type;
+ mnt->mnt_opts = fix_opts_string(flags & ~MS_NOMTAB, opts);
+
+ /* these are always zero for NFS */
+ mnt->mnt_freq = 0;
+ mnt->mnt_passno = 0;
+}
+
+/* Create mtab with a root entry. */
+static void
+create_mtab (void) {
+ struct mntentchn *fstab;
+ struct mntent mnt;
+ int flags;
+ mntFILE *mfp;
+
+ /* Avoid writing if the mtab is a symlink to /proc/mounts, since
+ that would create a file /proc/mounts in case the proc filesystem
+ is not mounted, and the fchmod below would also fail. */
+ if (mtab_is_a_symlink()) {
+ return;
+ }
+
+ lock_mtab();
+
+ mfp = nfs_setmntent (MOUNTED, "a+");
+ if (mfp == NULL || mfp->mntent_fp == NULL) {
+ int errsv = errno;
+ die (EX_FILEIO, _("mount: can't open %s for writing: %s"),
+ MOUNTED, strerror (errsv));
+ }
+
+ /* Find the root entry by looking it up in fstab */
+ if ((fstab = getfsfile ("/")) || (fstab = getfsfile ("root"))) {
+ char *extra_opts;
+ parse_opts (fstab->m.mnt_opts, &flags, &extra_opts);
+ init_mntent(&mnt, xstrdup(fstab->m.mnt_fsname), "/",
+ fstab->m.mnt_type, flags, extra_opts);
+ free(extra_opts);
+
+ if (nfs_addmntent (mfp, &mnt) == 1) {
+ int errsv = errno;
+ die (EX_FILEIO, _("mount: error writing %s: %s"),
+ _PATH_MOUNTED, strerror (errsv));
+ }
+ }
+ if (fchmod (fileno (mfp->mntent_fp), 0644) < 0)
+ if (errno != EROFS) {
+ int errsv = errno;
+ die (EX_FILEIO,
+ _("mount: error changing mode of %s: %s"),
+ _PATH_MOUNTED, strerror (errsv));
+ }
+ nfs_endmntent (mfp);
+
+ unlock_mtab();
+
+ reset_mtab_info();
+}
+
+static int add_mtab(char *spec, char *mount_point, char *fstype,
+ int flags, char *opts)
+{
+ struct mntent ment;
+ int result = EX_SUCCESS;
+
+ init_mntent(&ment, spec, mount_point, fstype, flags, opts);
+
+ if (!nomtab && mtab_does_not_exist()) {
+ if (verbose > 1)
+ printf(_("mount: no %s found - creating it..\n"),
+ MOUNTED);
+ create_mtab ();
+ }
+
+ if (!nomtab && mtab_is_writable()) {
+ if (flags & MS_REMOUNT)
+ update_mtab(ment.mnt_dir, &ment);
+ else {
+ mntFILE *mtab;
+
+ lock_mtab();
+ mtab = nfs_setmntent(MOUNTED, "a+");
+ if (mtab == NULL || mtab->mntent_fp == NULL) {
+ nfs_error(_("Can't open mtab: %s"),
+ strerror(errno));
+ result = EX_FILEIO;
+ } else {
+ if (nfs_addmntent(mtab, &ment) == 1) {
+ nfs_error(_("Can't write mount entry to mtab: %s"),
+ strerror(errno));
+ result = EX_FILEIO;
+ }
+ }
+ nfs_endmntent(mtab);
+ unlock_mtab();
+ }
+ }
+
+ free(ment.mnt_opts);
+
+ return result;
+}
+
+static void parse_opt(const char *opt, int *mask, char *extra_opts, size_t len)
+{
+ const struct opt_map *om;
+
+ for (om = opt_map; om->opt != NULL; om++) {
+ if (!strcmp (opt, om->opt)) {
+ if (om->inv)
+ *mask &= ~om->mask;
+ else
+ *mask |= om->mask;
+ return;
+ }
+ }
+
+ len -= strlen(extra_opts);
+
+ if (*extra_opts && --len > 0)
+ strcat(extra_opts, ",");
+
+ if ((len -= strlen(opt)) > 0)
+ strcat(extra_opts, opt);
+}
+
+/*
+ * Convert the provided mount command-line options into the 4th &
+ * 5th arguments to mount(2). Output parameter "@flags" gets the
+ * standard options (indicated by MS_ bits), and output parameter
+ * "@extra_opts" gets all the filesystem-specific options.
+ */
+static void parse_opts(const char *options, int *flags, char **extra_opts)
+{
+ if (options != NULL) {
+ char *opts = xstrdup(options);
+ char *opt, *p;
+ size_t len = strlen(opts) + 1; /* include room for a null */
+ int open_quote = 0;
+
+ *extra_opts = xmalloc(len);
+ **extra_opts = '\0';
+
+ for (p = opts, opt = NULL; p && *p; p++) {
+ if (!opt)
+ opt = p; /* begin of the option item */
+ if (*p == '"')
+ open_quote ^= 1; /* reverse the status */
+ if (open_quote)
+ continue; /* still in a quoted block */
+ if (*p == ',')
+ *p = '\0'; /* terminate the option item */
+
+ /* end of option item or last item */
+ if (*p == '\0' || *(p + 1) == '\0') {
+ parse_opt(opt, flags, *extra_opts, len);
+ opt = NULL;
+ }
+ }
+ free(opts);
+ }
+}
+
+static int try_mount(char *spec, char *mount_point, int flags,
+ char *fs_type, char **extra_opts, char *mount_opts,
+ int fake, int bg)
+{
+ int ret;
+
+ if (string)
+ ret = nfsmount_string(spec, mount_point, fs_type, flags,
+ extra_opts, fake, bg);
+ else {
+ if (strcmp(fs_type, "nfs4") == 0)
+ ret = nfs4mount(spec, mount_point, flags,
+ extra_opts, fake, bg);
+ else
+ ret = nfsmount(spec, mount_point, flags,
+ extra_opts, fake, bg);
+ }
+
+ if (ret)
+ return ret;
+
+ if (!fake)
+ print_one(spec, mount_point, fs_type, mount_opts);
+
+ return add_mtab(spec, mount_point, fs_type, flags, *extra_opts);
+}
+
+int main(int argc, char *argv[])
+{
+ int c, flags = 0, mnt_err = 1, fake = 0;
+ char *spec = NULL, *mount_point = NULL, *fs_type = "nfs";
+ char *extra_opts = NULL, *mount_opts = NULL;
+ uid_t uid = getuid();
+
+ progname = basename(argv[0]);
+
+ nfs_mount_data_version = discover_nfs_mount_data_version(&string);
+
+ if(!strncmp(progname, "umount", strlen("umount")))
+ exit(nfsumount(argc, argv));
+
+ mount_config_init(progname);
+
+ while ((c = getopt_long(argc, argv, "rvVwfno:hs",
+ longopts, NULL)) != -1) {
+ switch (c) {
+ case 'r':
+ flags |= MS_RDONLY;
+ break;
+ case 'v':
+ ++verbose;
+ break;
+ case 'V':
+ printf("%s: ("PACKAGE_STRING")\n", progname);
+ exit(EX_SUCCESS);
+ case 'w':
+ flags &= ~MS_RDONLY;
+ break;
+ case 'f':
+ ++fake;
+ break;
+ case 'n':
+ ++nomtab;
+ break;
+ case 'o': /* specify mount options */
+ if (mount_opts)
+ mount_opts = xstrconcat3(mount_opts, ",", optarg);
+ else
+ mount_opts = xstrdup(optarg);
+ break;
+ case 's':
+ ++sloppy;
+ break;
+ case 'h':
+ default:
+ mount_usage();
+ goto out_usage;
+ }
+ }
+
+ if ((argc < 3)) {
+ mount_usage();
+ exit(EX_USAGE);
+ }
+
+ /*
+ * Extra non-option words at the end are bogus...
+ */
+ if (optind != argc - 2) {
+ mount_usage();
+ goto out_usage;
+ } else {
+ while (optind < argc) {
+ if (!spec)
+ spec = argv[optind];
+ else
+ mount_point = argv[optind];
+ optind++;
+ }
+ }
+
+ if (strcmp(progname, "mount.nfs4") == 0)
+ fs_type = "nfs4";
+
+ /*
+ * If a non-root user is attempting to mount, make sure the
+ * user's requested options match the options specified in
+ * /etc/fstab; otherwise, don't allow the mount.
+ */
+ if (uid != 0) {
+ struct mntentchn *mc;
+
+ if ((mc = getfsfile(mount_point)) == NULL ||
+ strcmp(mc->m.mnt_fsname, spec) != 0 ||
+ strcmp(mc->m.mnt_type, fs_type) != 0) {
+ nfs_error(_("%s: permission denied: no match for %s "
+ "found in /etc/fstab"), progname, mount_point);
+ goto out_usage;
+ }
+
+ /*
+ * 'mount' munges the options from fstab before passing them
+ * to us, so it is non-trivial to test that we have the correct
+ * set of options and we don't want to trust what the user
+ * gave us, so just take whatever is in /etc/fstab.
+ */
+ mount_opts = strdup(mc->m.mnt_opts);
+ }
+
+ mount_point = canonicalize(mount_point);
+ if (!mount_point) {
+ nfs_error(_("%s: no mount point provided"), progname);
+ goto out_usage;
+ }
+ if (mount_point[0] != '/') {
+ nfs_error(_("%s: unrecognized mount point %s"),
+ progname, mount_point);
+ mnt_err = EX_USAGE;
+ goto out;
+ }
+ /*
+ * Concatenate mount options from the configuration file
+ */
+ mount_opts = mount_config_opts(spec, mount_point, mount_opts);
+
+ parse_opts(mount_opts, &flags, &extra_opts);
+
+ if (uid != 0) {
+ if (!(flags & (MS_USERS|MS_USER))) {
+ nfs_error(_("%s: permission denied"), progname);
+ mnt_err = EX_USAGE;
+ goto out;
+ }
+
+ if (geteuid() != 0) {
+ nfs_error(_("%s: not installed setuid - "
+ "\"user\" NFS mounts not supported."), progname);
+ exit(EX_FAIL);
+ }
+ }
+
+ if (chk_mountpoint(mount_point)) {
+ mnt_err = EX_USAGE;
+ goto out;
+ }
+
+ mnt_err = try_mount(spec, mount_point, flags, fs_type, &extra_opts,
+ mount_opts, fake, FOREGROUND);
+ if (mnt_err == EX_BG) {
+ printf(_("%s: backgrounding \"%s\"\n"),
+ progname, spec);
+ printf(_("%s: mount options: \"%s\"\n"),
+ progname, extra_opts);
+
+ fflush(stdout);
+
+ /*
+ * Parent exits immediately with success.
+ */
+ if (daemon(0, 0)) {
+ nfs_error(_("%s: failed to start "
+ "background process: %s\n"),
+ progname, strerror(errno));
+ exit(EX_FAIL);
+ }
+
+ mnt_err = try_mount(spec, mount_point, flags, fs_type,
+ &extra_opts, mount_opts, fake,
+ BACKGROUND);
+ if (verbose && mnt_err)
+ printf(_("%s: giving up \"%s\"\n"),
+ progname, spec);
+ }
+
+out:
+ free(mount_opts);
+ free(extra_opts);
+ free(mount_point);
+ exit(mnt_err);
+
+out_usage:
+ free(mount_opts);
+ exit(EX_USAGE);
+}
diff --git a/utils/mount/mount.nfs.man b/utils/mount/mount.nfs.man
new file mode 100644
index 0000000..a78a3b0
--- /dev/null
+++ b/utils/mount/mount.nfs.man
@@ -0,0 +1,93 @@
+.\"@(#)mount.nfs.8"
+.TH MOUNT.NFS 8 "5 Jun 2006"
+.SH NAME
+mount.nfs, mount.nfs4 \- mount a Network File System
+.SH SYNOPSIS
+.BI "mount.nfs" " remotetarget dir" " [\-rvVwfnsh ] [\-o " options "]
+.SH DESCRIPTION
+.BR mount.nfs
+is a part of
+.BR nfs (5)
+utilities package, which provides NFS client functionality.
+
+.BR mount.nfs
+is meant to be used by the
+.BR mount (8)
+command for mounting NFS shares. This subcommand, however, can also be used as a standalone command with limited functionality.
+
+.I remotetarget
+is a server share usually in the form of
+.BR servername:/path/to/share.
+.I dir
+is the directory on which the file system is to be mounted.
+
+Under Linux 2.6.32 and later kernel versions,
+.BR mount.nfs
+can mount all NFS file system versions. Under earlier Linux kernel versions,
+.BR mount.nfs4
+must be used for mounting NFSv4 file systems while
+.BR mount.nfs
+must be used for NFSv3.
+
+.SH OPTIONS
+.TP
+.BI "\-r"
+Mount file system readonly.
+.TP
+.BI "\-v"
+Be verbose.
+.TP
+.BI "\-V"
+Print version.
+.TP
+.BI "\-w"
+Mount file system read-write.
+.TP
+.BI "\-f"
+Fake mount. Don't actually call the mount system call.
+.TP
+.BI "\-n"
+Do not update
+.I /etc/mtab.
+By default, an entry is created in
+.I /etc/mtab
+for every mounted file system. Use this option to skip making an entry.
+.TP
+.BI "\-s"
+Tolerate sloppy mount options rather than fail.
+.TP
+.BI "\-h"
+Print help message.
+.TP
+.BI "nfsoptions"
+Refer to
+.BR nfs (5)
+or
+.BR mount (8)
+manual pages.
+
+.SH NOTE
+For further information please refer
+.BR nfs (5)
+and
+.BR mount (8)
+manual pages.
+
+.SH FILES
+.TP 18n
+.I /etc/fstab
+file system table
+.TP
+.I /etc/mtab
+table of mounted file systems
+.TP
+.I /etc/nfsmount.conf
+Configuration file for NFS mounts
+.PD
+.SH "SEE ALSO"
+.BR nfs (5),
+.BR nfsmount.conf (5),
+.BR mount (8),
+
+.SH "AUTHOR"
+Amit Gud <agud@redhat.com>
diff --git a/utils/mount/mount_config.h b/utils/mount/mount_config.h
new file mode 100644
index 0000000..7cc72fc
--- /dev/null
+++ b/utils/mount/mount_config.h
@@ -0,0 +1,55 @@
+#ifndef _LINUX_MOUNT_CONFIG_H
+#define _LINUX_MOUNT_CONFIG_H
+/*
+ * mount_config.h -- mount configuration file routines
+ * Copyright (C) 2008 Red Hat, Inc <nfs@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#ifdef MOUNT_CONFIG
+#include "conffile.h"
+#include "xlog.h"
+
+#ifndef MOUNTOPTS_CONFFILE
+#define MOUNTOPTS_CONFFILE "/etc/nfsmount.conf"
+#endif
+
+extern char *conf_get_mntopts(char *, char *, char *);
+
+static inline void mount_config_init(char *program)
+{
+ xlog_open(program);
+ /*
+ * Read the the default mount options
+ */
+ conf_init_file(MOUNTOPTS_CONFFILE);
+}
+
+static inline char *mount_config_opts(char *spec,
+ char *mount_point, char *mount_opts)
+{
+ return conf_get_mntopts(spec, mount_point, mount_opts);
+}
+
+#else /* MOUNT_CONFIG */
+
+static inline void mount_config_init(__attribute__ ((unused)) char *program) { }
+
+static inline char *mount_config_opts(__attribute__ ((unused)) char *spec,
+ __attribute__ ((unused)) char *mount_point, char *mount_opts)
+{
+ return mount_opts;
+}
+#endif /* MOUNT_CONFIG */
+
+#endif /* _LINUX_MOUNT_CONFIG_H */
diff --git a/utils/mount/mount_constants.h b/utils/mount/mount_constants.h
new file mode 100644
index 0000000..4d050d8
--- /dev/null
+++ b/utils/mount/mount_constants.h
@@ -0,0 +1,71 @@
+#ifndef _NFS_UTILS_MOUNT_CONSTANTS_H
+#define _NFS_UTILS_MOUNT_CONSTANTS_H
+
+#ifndef MS_DIRSYNC
+#define MS_DIRSYNC 128 /* Directory modifications are synchronous */
+#endif
+
+#ifndef MS_ACTION_MASK
+#define MS_ACTION_MASK 0x380
+/* Remount, but new filesystem may be different from old. Atomic
+ (i.e. there is no interval when nothing is mounted at the mountpoint).
+ If new fs differs from the old one and old is busy - -EBUSY. */
+#define MS_REPLACE 0x080 /* 128 */
+/* After, Before: as soon as we get unions these will add a new member
+ in the end or beginning of the chain. Fail if there is a stack
+ on the mountpoint. */
+#define MS_AFTER 0x100 /* 256 */
+#define MS_BEFORE 0x180
+/* Over: if nothing mounted on a mountpoint - same as if none of these
+flags had been set; if we have a union with more than one element - fail;
+if we have a stack or plain mount - mount atop of it, forming a stack. */
+#define MS_OVER 0x200 /* 512 */
+#endif
+#ifndef MS_NOATIME
+#define MS_NOATIME 0x400 /* 1024: Do not update access times. */
+#endif
+#ifndef MS_NODIRATIME
+#define MS_NODIRATIME 0x800 /* 2048: Don't update directory access times */
+#endif
+#ifndef MS_BIND
+#define MS_BIND 0x1000 /* 4096: Mount existing tree also elsewhere */
+#endif
+#ifndef MS_MOVE
+#define MS_MOVE 0x2000 /* 8192: Atomically move tree */
+#endif
+#ifndef MS_REC
+#define MS_REC 0x4000 /* 16384: Recursive loopback */
+#endif
+#ifndef MS_VERBOSE
+#define MS_VERBOSE 0x8000 /* 32768 */
+#endif
+#ifndef MS_RELATIME
+#define MS_RELATIME 0x200000 /* 200000: Update access times relative
+ to mtime/ctime */
+#endif
+/*
+ * NFS fs-specific mount option flags
+ *
+ * MS_DUMMY is assigned to mount options that are not to be
+ * passed to the kernel via the "flags" argument. These are
+ * generally ignored or handled entirely in user space.
+ */
+#define MS_DUMMY 0x00000000
+#define MS_USERS 0x40000000
+#define MS_USER 0x80000000
+
+/*
+ * Magic mount flag number. Had to be or-ed to the flag values.
+ */
+#ifndef MS_MGC_VAL
+#define MS_MGC_VAL 0xC0ED0000 /* magic flag number to indicate "new" flags */
+#endif
+#ifndef MS_MGC_MSK
+#define MS_MGC_MSK 0xffff0000 /* magic flag number mask */
+#endif
+
+/* Generic options that are prevented from appearing
+ * in the options field in /etc/mtab. */
+#define MS_NOMTAB (MS_REMOUNT)
+
+#endif /* _NFS_UTILS_MOUNT_CONSTANTS_H */
diff --git a/utils/mount/mount_libmount.c b/utils/mount/mount_libmount.c
new file mode 100644
index 0000000..aa4ac5c
--- /dev/null
+++ b/utils/mount/mount_libmount.c
@@ -0,0 +1,461 @@
+/*
+ * mount_libmount.c -- Linux NFS [u]mount based on libmount
+ *
+ * Copyright (C) 2011 Karel Zak <kzak@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <getopt.h>
+#include <libgen.h>
+
+#include <libmount/libmount.h>
+
+#include "nls.h"
+#include "mount_config.h"
+
+#include "nfs_mount.h"
+#include "nfs4_mount.h"
+#include "stropts.h"
+#include "version.h"
+#include "xcommon.h"
+
+#include "error.h"
+#include "utils.h"
+
+char *retrieve_mount_options(struct libmnt_fs *fs);
+
+char *progname;
+int nfs_mount_data_version;
+int verbose;
+int sloppy;
+int string;
+int nomtab;
+
+#define FOREGROUND (0)
+#define BACKGROUND (1)
+
+/*
+ * Store mount options to mtab (or /dev/.mount/utab), called from mount.nfs.
+ *
+ * Note that on systems without /etc/mtab the fs-specific options are not
+ * managed by libmount at all. We have to use "mount attributes" that are
+ * private for mount.<type> helpers.
+ */
+static void store_mount_options(struct libmnt_fs *fs, const char *nfs_opts)
+{
+ char *o = NULL;
+
+ mnt_fs_set_attributes(fs, nfs_opts); /* for non-mtab systems */
+
+ /* for mtab create a new options list */
+ mnt_optstr_append_option(&o, mnt_fs_get_vfs_options(fs), NULL);
+ mnt_optstr_append_option(&o, nfs_opts, NULL);
+ mnt_optstr_append_option(&o, mnt_fs_get_user_options(fs), NULL);
+
+ mnt_fs_set_options(fs, o);
+ free(o);
+}
+
+/*
+ * Retrieve mount options from mtab (or /dev/.mount/utab) called from umount.nfs.
+ *
+ * The result can passed to free().
+ */
+char *retrieve_mount_options(struct libmnt_fs *fs)
+{
+ const char *opts;
+
+ if (!fs)
+ return NULL;
+
+ opts = mnt_fs_get_attributes(fs); /* /dev/.mount/utab */
+ if (opts)
+ return strdup(opts);
+
+ return mnt_fs_strdup_options(fs); /* /etc/mtab */
+}
+
+static int try_mount(struct libmnt_context *cxt, int bg)
+{
+ struct libmnt_fs *fs;
+ const char *p;
+ char *src = NULL, *tgt = NULL, *type = NULL, *opts = NULL;
+ unsigned long flags = 0;
+ int fake, ret = 0;
+
+ fs = mnt_context_get_fs(cxt);
+
+ /* libmount returns read-only pointers (const char)
+ * so, reallocate for nfsmount() functions.
+ */
+ if ((p = mnt_fs_get_source(fs))) /* spec */
+ src = strdup(p);
+ if ((p = mnt_fs_get_target(fs))) /* mountpoint */
+ tgt = strdup(p);
+ if ((p = mnt_fs_get_fstype(fs))) /* FS type */
+ type = strdup(p);
+ if ((p = mnt_fs_get_fs_options(fs))) /* mount options */
+ opts = strdup(p);
+
+ mnt_context_get_mflags(cxt, &flags); /* mount(2) flags */
+ fake = mnt_context_is_fake(cxt);
+
+ if (string)
+ ret = nfsmount_string(src, tgt, type, flags, &opts, fake, bg);
+
+ else if (strcmp(type, "nfs4") == 0)
+ ret = nfs4mount(src, tgt, flags, &opts, fake, bg);
+ else
+ ret = nfsmount(src, tgt, flags, &opts, fake, bg);
+
+ /* Store mount options if not called with mount --no-mtab */
+ if (!ret && !mnt_context_is_nomtab(cxt))
+ store_mount_options(fs, opts);
+
+ free(src);
+ free(tgt);
+ free(type);
+ free(opts);
+
+ return ret;
+}
+
+/* returns: error = -1, success = 1 , not vers4 == 0 */
+static int is_vers4(struct libmnt_context *cxt)
+{
+ struct libmnt_fs *fs = mnt_context_get_fs(cxt);
+ struct libmnt_table *tb = NULL;
+ const char *src = mnt_context_get_source(cxt),
+ *tgt = mnt_context_get_target(cxt);
+ int rc = 0;
+
+ if (!src || !tgt)
+ return -1;
+
+ if (!mnt_fs_is_kernel(fs)) {
+ struct libmnt_table *tb = mnt_new_table_from_file("/proc/mounts");
+
+ if (!tb)
+ return -1;
+ fs = mnt_table_find_pair(tb, src, tgt, MNT_ITER_BACKWARD);
+ }
+
+ if (fs) {
+ const char *type = mnt_fs_get_fstype(fs);
+ if (type && strcmp(type, "nfs4") == 0)
+ rc = 1;
+ }
+ mnt_free_table(tb);
+ return rc;
+}
+
+static int umount_main(struct libmnt_context *cxt, int argc, char **argv)
+{
+ int rc, c;
+ char *spec = NULL, *opts = NULL;
+ int ret = EX_FAIL, verbose = 0;
+
+ static const struct option longopts[] = {
+ { "force", 0, 0, 'f' },
+ { "help", 0, 0, 'h' },
+ { "no-mtab", 0, 0, 'n' },
+ { "verbose", 0, 0, 'v' },
+ { "read-only", 0, 0, 'r' },
+ { "lazy", 0, 0, 'l' },
+ { "types", 1, 0, 't' },
+ { NULL, 0, 0, 0 }
+ };
+
+ mnt_context_init_helper(cxt, MNT_ACT_UMOUNT, 0);
+ mnt_context_disable_canonicalize(cxt, 1);
+
+ while ((c = getopt_long (argc, argv, "fvnrlh", longopts, NULL)) != -1) {
+
+ rc = mnt_context_helper_setopt(cxt, c, optarg);
+ if (rc == 0) /* valid option */
+ continue;
+ if (rc < 0) /* error (probably ENOMEM) */
+ goto err;
+ /* rc==1 means unknow option */
+ umount_usage();
+ return EX_USAGE;
+ }
+
+ verbose = mnt_context_is_verbose(cxt);
+
+ if (optind < argc)
+ spec = argv[optind++];
+
+ if (!spec || (*spec != '/' && strchr(spec,':') == NULL)) {
+ nfs_error(_("%s: no mount point provided"), progname);
+ umount_usage();
+ return EX_USAGE;
+ }
+
+ if (mnt_context_set_target(cxt, spec))
+ goto err;
+
+ /* read mtab/fstab, evaluate permissions, etc. */
+ rc = mnt_context_prepare_umount(cxt);
+ if (rc) {
+ nfs_error(_("%s: failed to prepare umount: %s\n"),
+ progname, strerror(-rc));
+ goto err;
+ }
+
+ if (mnt_context_get_fstype(cxt) &&
+ !mnt_match_fstype(mnt_context_get_fstype(cxt), "nfs,nfs4")) {
+
+ nfs_error(_("%s: %s: is not an NFS filesystem"), progname, spec);
+ ret = EX_USAGE;
+ goto err;
+ }
+
+ if (verbose)
+ printf(_("%s: %s mount point detected\n"), spec,
+ mnt_context_get_fstype(cxt));
+
+ opts = retrieve_mount_options(mnt_context_get_fs(cxt));
+
+ if (!mnt_context_is_lazy(cxt)) {
+ if (opts) {
+ /* we have full FS description (e.g. from mtab or /proc) */
+ switch (is_vers4(cxt)) {
+ case 0:
+ /* We ignore the error from nfs_umount23.
+ * If the actual umount succeeds (in del_mtab),
+ * we don't want to signal an error, as that
+ * could cause /sbin/mount to retry!
+ */
+ nfs_umount23(mnt_context_get_source(cxt), opts);
+ break;
+ case 1: /* unknown */
+ break;
+ default: /* error */
+ goto err;
+ }
+ } else
+ /* strange, no entry in mtab or /proc not mounted */
+ nfs_umount23(spec, "tcp,v3");
+ }
+
+ ret = EX_FILEIO;
+ rc = mnt_context_do_umount(cxt); /* call umount(2) syscall */
+ mnt_context_finalize_mount(cxt); /* mtab update */
+
+ if (rc && !mnt_context_get_status(cxt)) {
+ /* mnt_context_do_umount() returns errno if umount(2) failed */
+ umount_error(rc, spec);
+ goto err;
+ }
+ ret = EX_SUCCESS;
+err:
+ if (verbose) {
+ if (ret == EX_SUCCESS)
+ printf(_("%s: umounted\n"), spec);
+ else
+ printf(_("%s: umount failed\n"), spec);
+ }
+ free(opts);
+ return ret;
+}
+
+static int mount_main(struct libmnt_context *cxt, int argc, char **argv)
+{
+ int rc, c;
+ struct libmnt_fs *fs;
+ char *spec = NULL, *mount_point = NULL, *opts = NULL;
+
+ static const struct option longopts[] = {
+ { "fake", 0, 0, 'f' },
+ { "help", 0, 0, 'h' },
+ { "no-mtab", 0, 0, 'n' },
+ { "read-only", 0, 0, 'r' },
+ { "ro", 0, 0, 'r' },
+ { "verbose", 0, 0, 'v' },
+ { "version", 0, 0, 'V' },
+ { "read-write", 0, 0, 'w' },
+ { "rw", 0, 0, 'w' },
+ { "options", 1, 0, 'o' },
+ { "sloppy", 0, 0, 's' },
+ { NULL, 0, 0, 0 }
+ };
+
+ mount_config_init(progname);
+ mnt_context_init_helper(cxt, MNT_ACT_MOUNT, 0);
+
+ while ((c = getopt_long(argc, argv, "fhnrVvwo:s", longopts, NULL)) != -1) {
+
+ rc = mnt_context_helper_setopt(cxt, c, optarg);
+ if (rc == 0) /* valid option */
+ continue;
+ if (rc < 0) /* error (probably ENOMEM) */
+ goto err;
+ /* rc==1 means unknow option */
+ switch (c) {
+ case 'V':
+ printf("%s: ("PACKAGE_STRING")\n", progname);
+ return EX_SUCCESS;
+ case 'h':
+ default:
+ mount_usage();
+ return EX_USAGE;
+ }
+ }
+
+ if (optind < argc)
+ spec = argv[optind++];
+ if (optind < argc)
+ mount_point = argv[optind++];
+
+ if (!mount_point) {
+ nfs_error(_("%s: no mount point provided"), progname);
+ mount_usage();
+ goto err;
+ }
+ if (!spec) {
+ nfs_error(_("%s: no mount spec provided"), progname);
+ goto err;
+ }
+
+ if (geteuid() != 0) {
+ nfs_error(_("%s: not installed setuid - "
+ "\"user\" NFS mounts not supported."), progname);
+ goto err;
+ }
+
+ verbose = mnt_context_is_verbose(cxt);
+ sloppy = mnt_context_is_sloppy(cxt);
+ nomtab = mnt_context_is_nomtab(cxt);
+
+ if (strcmp(progname, "mount.nfs4") == 0)
+ mnt_context_set_fstype(cxt, "nfs4");
+ else
+ mnt_context_set_fstype(cxt, "nfs"); /* default */
+
+ rc = mnt_context_set_source(cxt, spec);
+ if (!rc)
+ mnt_context_set_target(cxt, mount_point);
+ if (rc) {
+ nfs_error(_("%s: failed to set spec or mountpoint: %s"),
+ progname, strerror(errno));
+ goto err;
+ }
+
+ mount_point = mnt_resolve_path(mount_point,
+ mnt_context_get_cache(cxt));
+
+ if (chk_mountpoint(mount_point))
+ goto err;
+
+ /*
+ * The libmount strictly uses only options from fstab if running in
+ * restricted mode (suid, non-root user). This is done in
+ * mnt_context_prepare_mount() by default.
+ *
+ * We have to read fstab before nfsmount.conf, otherwise the options
+ * from nfsmount.conf will be ignored (overwrited).
+ */
+ rc = mnt_context_apply_fstab(cxt);
+ if (rc) {
+ nfs_error(_("%s: failed to apply fstab options\n"), progname);
+ goto err;
+ }
+
+ /*
+ * Concatenate mount options from the configuration file
+ */
+ fs = mnt_context_get_fs(cxt);
+ if (fs) {
+ opts = mnt_fs_strdup_options(fs);
+
+ opts = mount_config_opts(spec, mount_point, opts);
+ mnt_fs_set_options(fs, opts);
+ }
+
+ rc = mnt_context_prepare_mount(cxt);
+ if (rc) {
+ nfs_error(_("%s: failed to prepare mount: %s\n"),
+ progname, strerror(-rc));
+ goto err;
+ }
+
+ rc = try_mount(cxt, FOREGROUND);
+
+ if (rc == EX_BG) {
+ printf(_("%s: backgrounding \"%s\"\n"),
+ progname, mnt_context_get_source(cxt));
+ printf(_("%s: mount options: \"%s\"\n"),
+ progname, opts);
+
+ fflush(stdout);
+
+ if (daemon(0, 0)) {
+ nfs_error(_("%s: failed to start "
+ "background process: %s\n"),
+ progname, strerror(errno));
+ exit(EX_FAIL);
+ }
+
+ rc = try_mount(cxt, BACKGROUND);
+
+ if (verbose && rc)
+ printf(_("%s: giving up \"%s\"\n"),
+ progname, mnt_context_get_source(cxt));
+ }
+
+ mnt_context_set_syscall_status(cxt, rc == EX_SUCCESS ? 0 : -1);
+ mnt_context_finalize_mount(cxt); /* mtab update */
+ return rc;
+err:
+ return EX_FAIL;
+}
+
+int main(int argc, char *argv[])
+{
+ struct libmnt_context *cxt;
+ int rc;
+
+ mnt_init_debug(0);
+ cxt = mnt_new_context();
+ if (!cxt) {
+ nfs_error(_("Can't initilize libmount: %s"),
+ strerror(errno));
+ rc = EX_FAIL;
+ goto done;
+ }
+
+ progname = basename(argv[0]);
+ nfs_mount_data_version = discover_nfs_mount_data_version(&string);
+
+ if(strncmp(progname, "umount", 6) == 0)
+ rc = umount_main(cxt, argc, argv);
+ else
+ rc = mount_main(cxt, argc, argv);
+done:
+ mnt_free_context(cxt);
+ return rc;
+}
diff --git a/utils/mount/network.c b/utils/mount/network.c
new file mode 100644
index 0000000..01ead49
--- /dev/null
+++ b/utils/mount/network.c
@@ -0,0 +1,1801 @@
+/*
+ * network.c -- Provide common network functions for NFS mount/umount
+ *
+ * Copyright (C) 2007 Oracle. All rights reserved.
+ * Copyright (C) 2007 Chuck Lever <chuck.lever@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <netdb.h>
+#include <time.h>
+#include <grp.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#if defined(__GLIBC__) && ((__GLIBC__ < 2) || (__GLIBC__ == 2 && __GLIBC_MINOR__ < 24))
+/* Cannot safely include linux/in6.h in old glibc, so hardcode the needed values */
+# define IPV6_PREFER_SRC_PUBLIC 2
+# define IPV6_ADDR_PREFERENCES 72
+#else
+# include <linux/in6.h>
+#endif
+#include <netinet/in.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_prot.h>
+#include <rpc/pmap_clnt.h>
+#include <net/if.h>
+#include <ifaddrs.h>
+
+#include "sockaddr.h"
+#include "xcommon.h"
+#include "mount.h"
+#include "nls.h"
+#include "nfs_mount.h"
+#include "mount_constants.h"
+#include "nfsrpc.h"
+#include "parse_opt.h"
+#include "network.h"
+#include "conffile.h"
+#include "nfslib.h"
+
+#define PMAP_TIMEOUT (10)
+#define CONNECT_TIMEOUT (20)
+#define MOUNT_TIMEOUT (30)
+#define STATD_TIMEOUT (10)
+
+#define SAFE_SOCKADDR(x) (struct sockaddr *)(char *)(x)
+
+extern int nfs_mount_data_version;
+extern char *progname;
+extern int verbose;
+
+static const char *nfs_mnt_pgmtbl[] = {
+ "mount",
+ "mountd",
+ NULL,
+};
+
+static const char *nfs_nfs_pgmtbl[] = {
+ "nfs",
+ "nfsprog",
+ NULL,
+};
+
+static const char *nfs_transport_opttbl[] = {
+ "udp",
+ "tcp",
+ "rdma",
+ "proto",
+ NULL,
+};
+
+static const char *nfs_version_opttbl[] = {
+ "v2", /* no longer supported */
+ "v3",
+ "v4",
+ "vers",
+ "nfsvers",
+ NULL,
+};
+
+static const unsigned long nfs_to_mnt[] = {
+ 0,
+ 0,
+ 1,
+ 3,
+};
+
+static const unsigned long mnt_to_nfs[] = {
+ 0,
+ 2,
+ 2,
+ 3,
+};
+
+/*
+ * Map an NFS version into the corresponding Mountd version
+ */
+unsigned long nfsvers_to_mnt(const unsigned long vers)
+{
+ if (vers <= 3)
+ return nfs_to_mnt[vers];
+ return 0;
+}
+
+/*
+ * Map a Mountd version into the corresponding NFS version
+ */
+static unsigned long mntvers_to_nfs(const unsigned long vers)
+{
+ if (vers <= 3)
+ return mnt_to_nfs[vers];
+ return 0;
+}
+
+static const unsigned int probe_udp_only[] = {
+ IPPROTO_UDP,
+ 0,
+};
+
+static const unsigned int probe_udp_first[] = {
+ IPPROTO_UDP,
+ IPPROTO_TCP,
+ 0,
+};
+
+static const unsigned int probe_tcp_first[] = {
+ IPPROTO_TCP,
+ IPPROTO_UDP,
+ 0,
+};
+
+static const unsigned long probe_nfs2_only[] = {
+ 2,
+ 0,
+};
+
+static const unsigned long probe_nfs3_only[] = {
+ 3,
+ 0,
+};
+
+static const unsigned long probe_mnt1_first[] = {
+ 1,
+ 2,
+ 0,
+};
+
+static const unsigned long probe_mnt3_only[] = {
+ 3,
+ 0,
+};
+
+static const unsigned int *nfs_default_proto(void);
+#ifdef MOUNT_CONFIG
+static const unsigned int *nfs_default_proto(void)
+{
+ extern unsigned long config_default_proto;
+ /*
+ * If the default proto has been set and
+ * its not TCP, start with UDP
+ */
+ if (config_default_proto && config_default_proto != IPPROTO_TCP)
+ return probe_udp_first;
+
+ return probe_tcp_first;
+}
+#else
+static const unsigned int *nfs_default_proto()
+{
+ return probe_tcp_first;
+}
+#endif /* MOUNT_CONFIG */
+
+/**
+ * nfs_lookup - resolve hostname to an IPv4 or IPv6 socket address
+ * @hostname: pointer to C string containing DNS hostname to resolve
+ * @family: address family hint
+ * @sap: pointer to buffer to fill with socket address
+ * @len: IN: size of buffer to fill; OUT: size of socket address
+ *
+ * Returns 1 and places a socket address at @sap if successful;
+ * otherwise zero.
+ */
+int nfs_lookup(const char *hostname, const sa_family_t family,
+ struct sockaddr *sap, socklen_t *salen)
+{
+ struct addrinfo *gai_results;
+ struct addrinfo gai_hint = {
+ .ai_family = family,
+ };
+ socklen_t len = *salen;
+ int error, ret = 0;
+
+ *salen = 0;
+
+ error = getaddrinfo(hostname, NULL, &gai_hint, &gai_results);
+ switch (error) {
+ case 0:
+ break;
+ case EAI_SYSTEM:
+ nfs_error(_("%s: DNS resolution failed for %s: %s"),
+ progname, hostname, strerror(errno));
+ return ret;
+ default:
+ nfs_error(_("%s: DNS resolution failed for %s: %s"),
+ progname, hostname, gai_strerror(error));
+ return ret;
+ }
+
+ switch (gai_results->ai_addr->sa_family) {
+ case AF_INET:
+ case AF_INET6:
+ if (len >= gai_results->ai_addrlen) {
+ *salen = gai_results->ai_addrlen;
+ memcpy(sap, gai_results->ai_addr, *salen);
+ ret = 1;
+ }
+ break;
+ default:
+ /* things are really broken if we get here, so warn */
+ nfs_error(_("%s: unrecognized DNS resolution results for %s"),
+ progname, hostname);
+ break;
+ }
+
+ nfs_freeaddrinfo(gai_results);
+ return ret;
+}
+
+/**
+ * nfs_gethostbyname - resolve a hostname to an IPv4 address
+ * @hostname: pointer to a C string containing a DNS hostname
+ * @sin: returns an IPv4 address
+ *
+ * Returns 1 if successful, otherwise zero.
+ */
+int nfs_gethostbyname(const char *hostname, struct sockaddr_in *sin)
+{
+ socklen_t len = sizeof(*sin);
+
+ return nfs_lookup(hostname, AF_INET, (struct sockaddr *)sin, &len);
+}
+
+/**
+ * nfs_string_to_sockaddr - convert string address to sockaddr
+ * @address: pointer to presentation format address to convert
+ * @sap: pointer to socket address buffer to fill in
+ * @salen: IN: length of address buffer
+ * OUT: length of converted socket address
+ *
+ * Convert a presentation format address string to a socket address.
+ * Similar to nfs_lookup(), but the DNS query is squelched, and it
+ * won't make any noise if the getaddrinfo() call fails.
+ *
+ * Returns 1 and fills in @sap and @salen if successful; otherwise zero.
+ *
+ * See RFC 4038 section 5.1 or RFC 3513 section 2.2 for more details
+ * on presenting IPv6 addresses as text strings.
+ */
+int nfs_string_to_sockaddr(const char *address, struct sockaddr *sap,
+ socklen_t *salen)
+{
+ struct addrinfo *gai_results;
+ struct addrinfo gai_hint = {
+ .ai_flags = AI_NUMERICHOST,
+ };
+ socklen_t len = *salen;
+ int ret = 0;
+
+ *salen = 0;
+
+ if (getaddrinfo(address, NULL, &gai_hint, &gai_results) == 0) {
+ switch (gai_results->ai_addr->sa_family) {
+ case AF_INET:
+ case AF_INET6:
+ if (len >= gai_results->ai_addrlen) {
+ *salen = gai_results->ai_addrlen;
+ memcpy(sap, gai_results->ai_addr, *salen);
+ ret = 1;
+ }
+ break;
+ }
+ nfs_freeaddrinfo(gai_results);
+ }
+
+ return ret;
+}
+
+/**
+ * nfs_present_sockaddr - convert sockaddr to string
+ * @sap: pointer to socket address to convert
+ * @salen: length of socket address
+ * @buf: pointer to buffer to fill in
+ * @buflen: length of buffer
+ *
+ * Convert the passed-in sockaddr-style address to presentation format.
+ * The presentation format address is placed in @buf and is
+ * '\0'-terminated.
+ *
+ * Returns 1 if successful; otherwise zero.
+ *
+ * See RFC 4038 section 5.1 or RFC 3513 section 2.2 for more details
+ * on presenting IPv6 addresses as text strings.
+ */
+int nfs_present_sockaddr(const struct sockaddr *sap, const socklen_t salen,
+ char *buf, const size_t buflen)
+{
+#ifdef HAVE_GETNAMEINFO
+ int result;
+
+ result = getnameinfo(sap, salen, buf, buflen,
+ NULL, 0, NI_NUMERICHOST);
+ if (!result)
+ return 1;
+
+ nfs_error(_("%s: invalid server address: %s"), progname,
+ gai_strerror(result));
+ return 0;
+#else /* HAVE_GETNAMEINFO */
+ char *addr;
+
+ if (sap->sa_family == AF_INET) {
+ addr = inet_ntoa(((struct sockaddr_in *)sap)->sin_addr);
+ if (addr && strlen(addr) < buflen) {
+ strcpy(buf, addr);
+ return 1;
+ }
+ }
+
+ nfs_error(_("%s: invalid server address"), progname);
+ return 0;
+#endif /* HAVE_GETNAMEINFO */
+}
+
+/*
+ * Attempt to connect a socket, but time out after "timeout" seconds.
+ *
+ * On error return, caller closes the socket.
+ */
+static int connect_to(int fd, struct sockaddr *addr,
+ socklen_t addrlen, int timeout)
+{
+ int ret, saved;
+ fd_set rset, wset;
+ struct timeval tv = {
+ .tv_sec = timeout,
+ };
+
+ saved = fcntl(fd, F_GETFL, 0);
+ fcntl(fd, F_SETFL, saved | O_NONBLOCK);
+
+ ret = connect(fd, addr, addrlen);
+ if (ret < 0 && errno != EINPROGRESS)
+ return -1;
+ if (ret == 0)
+ goto out;
+
+ FD_ZERO(&rset);
+ FD_SET(fd, &rset);
+ wset = rset;
+ ret = select(fd + 1, &rset, &wset, NULL, &tv);
+ if (ret == 0) {
+ errno = ETIMEDOUT;
+ return -1;
+ }
+ if (FD_ISSET(fd, &rset) || FD_ISSET(fd, &wset)) {
+ int error;
+ socklen_t len = sizeof(error);
+ if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &len) < 0)
+ return -1;
+ if (error) {
+ errno = error;
+ return -1;
+ }
+ } else
+ return -1;
+
+out:
+ fcntl(fd, F_SETFL, saved);
+ return 0;
+}
+
+/*
+ * Create a socket that is locally bound to a reserved or non-reserved port.
+ *
+ * The caller should check rpc_createerr to determine the cause of any error.
+ */
+static int get_socket(struct sockaddr_in *saddr, unsigned int p_prot,
+ unsigned int timeout, int resvp, int conn)
+{
+ int so, cc, type;
+ struct sockaddr_in laddr;
+ socklen_t namelen = sizeof(laddr);
+
+ type = (p_prot == IPPROTO_UDP ? SOCK_DGRAM : SOCK_STREAM);
+ if ((so = socket (AF_INET, type, p_prot)) < 0)
+ goto err_socket;
+
+ laddr.sin_family = AF_INET;
+ laddr.sin_port = 0;
+ laddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ if (resvp) {
+ if (bindresvport(so, &laddr) < 0)
+ goto err_bindresvport;
+ }
+ if (type == SOCK_STREAM || (conn && type == SOCK_DGRAM)) {
+ cc = connect_to(so, SAFE_SOCKADDR(saddr), namelen,
+ timeout);
+ if (cc < 0)
+ goto err_connect;
+ }
+ return so;
+
+err_socket:
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ if (verbose) {
+ nfs_error(_("%s: Unable to create %s socket: errno %d (%s)\n"),
+ progname, p_prot == IPPROTO_UDP ? _("UDP") : _("TCP"),
+ errno, strerror(errno));
+ }
+ return RPC_ANYSOCK;
+
+err_bindresvport:
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ if (verbose) {
+ nfs_error(_("%s: Unable to bindresvport %s socket: errno %d"
+ " (%s)\n"),
+ progname, p_prot == IPPROTO_UDP ? _("UDP") : _("TCP"),
+ errno, strerror(errno));
+ }
+ close(so);
+ return RPC_ANYSOCK;
+
+err_connect:
+ rpc_createerr.cf_stat = RPC_SYSTEMERROR;
+ rpc_createerr.cf_error.re_errno = errno;
+ if (verbose) {
+ nfs_error(_("%s: Unable to connect to %s:%d, errno %d (%s)\n"),
+ progname, inet_ntoa(saddr->sin_addr),
+ ntohs(saddr->sin_port), errno, strerror(errno));
+ }
+ close(so);
+ return RPC_ANYSOCK;
+}
+
+static void nfs_pp_debug(const struct sockaddr *sap, const socklen_t salen,
+ const rpcprog_t program, const rpcvers_t version,
+ const unsigned short protocol,
+ const unsigned short port)
+{
+ char buf[NI_MAXHOST];
+
+ if (!verbose)
+ return;
+
+ if (nfs_present_sockaddr(sap, salen, buf, sizeof(buf)) == 0) {
+ buf[0] = '\0';
+ strcat(buf, "unknown host");
+ }
+
+ fprintf(stderr, _("%s: trying %s prog %lu vers %lu prot %s port %d\n"),
+ progname, buf, (unsigned long)program,
+ (unsigned long)version,
+ (protocol == IPPROTO_UDP ? _("UDP") : _("TCP")),
+ port);
+}
+
+static void nfs_pp_debug2(const char *str)
+{
+ if (!verbose)
+ return;
+
+ if (rpc_createerr.cf_error.re_status == RPC_CANTRECV ||
+ rpc_createerr.cf_error.re_status == RPC_CANTSEND)
+ nfs_error(_("%s: portmap query %s%s - %s"),
+ progname, str, clnt_spcreateerror(""),
+ strerror(rpc_createerr.cf_error.re_errno));
+ else
+ nfs_error(_("%s: portmap query %s%s"),
+ progname, str, clnt_spcreateerror(""));
+}
+
+/*
+ * Use the portmapper to discover whether or not the service we want is
+ * available. The lists 'versions' and 'protos' define ordered sequences
+ * of service versions and udp/tcp protocols to probe for.
+ *
+ * Returns 1 if the requested service port is unambiguous and pingable;
+ * @pmap is filled in with the version, port, and transport protocol used
+ * during the successful ping. Note that if a port is already specified
+ * in @pmap and it matches the rpcbind query result, nfs_probe_port() does
+ * not perform an RPC ping.
+ *
+ * If an error occurs or the requested service isn't available, zero is
+ * returned; rpccreateerr.cf_stat is set to reflect the nature of the error.
+ */
+static int nfs_probe_port(const struct sockaddr *sap, const socklen_t salen,
+ struct pmap *pmap, const unsigned long *versions,
+ const unsigned int *protos)
+{
+ union nfs_sockaddr address;
+ struct sockaddr *saddr = &address.sa;
+ const unsigned long prog = pmap->pm_prog, *p_vers;
+ const unsigned int prot = (u_int)pmap->pm_prot, *p_prot;
+ const u_short port = (u_short) pmap->pm_port;
+ unsigned long vers = pmap->pm_vers;
+ unsigned short p_port;
+
+ memcpy(saddr, sap, salen);
+ p_prot = prot ? &prot : protos;
+ p_vers = vers ? &vers : versions;
+
+ for (;;) {
+ if (verbose)
+ printf(_("%s: prog %lu, trying vers=%lu, prot=%u\n"),
+ progname, prog, *p_vers, *p_prot);
+ p_port = nfs_getport(saddr, salen, prog, *p_vers, *p_prot);
+ if (p_port) {
+ if (!port || port == p_port) {
+ nfs_set_port(saddr, p_port);
+ nfs_pp_debug(saddr, salen, prog, *p_vers,
+ *p_prot, p_port);
+ if (nfs_rpc_ping(saddr, salen, prog,
+ *p_vers, *p_prot, NULL))
+ goto out_ok;
+ } else
+ rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
+ }
+ if (rpc_createerr.cf_stat != RPC_PROGNOTREGISTERED &&
+ rpc_createerr.cf_stat != RPC_TIMEDOUT &&
+ rpc_createerr.cf_stat != RPC_CANTRECV &&
+ rpc_createerr.cf_stat != RPC_PROGVERSMISMATCH)
+ break;
+
+ if (!prot) {
+ if (*++p_prot) {
+ nfs_pp_debug2("retrying");
+ continue;
+ }
+ p_prot = protos;
+ }
+ if (rpc_createerr.cf_stat == RPC_TIMEDOUT ||
+ rpc_createerr.cf_stat == RPC_CANTRECV)
+ break;
+
+ if (vers || !*++p_vers)
+ break;
+ }
+
+ nfs_pp_debug2("failed");
+ return 0;
+
+out_ok:
+ if (!vers)
+ pmap->pm_vers = *p_vers;
+ if (!prot)
+ pmap->pm_prot = *p_prot;
+ if (!port)
+ pmap->pm_port = p_port;
+ nfs_clear_rpc_createerr();
+ return 1;
+}
+/*
+ * Probe a server's NFS service to determine which versions and
+ * transport protocols are supported.
+ *
+ * Returns 1 if the requested service port is unambiguous and pingable;
+ * @pmap is filled in with the version, port, and transport protocol used
+ * during the successful ping. If all three are already specified, simply
+ * return success without an rpcbind query or RPC ping (we may be trying
+ * to mount an NFS service that is not advertised via rpcbind).
+ *
+ * If an error occurs or the requested service isn't available, zero is
+ * returned; rpccreateerr.cf_stat is set to reflect the nature of the error.
+ */
+static int nfs_probe_nfsport(const struct sockaddr *sap, const socklen_t salen,
+ struct pmap *pmap, int checkv4)
+{
+ if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port)
+ return 1;
+
+ if (nfs_mount_data_version >= 4) {
+ const unsigned int *probe_proto;
+ int ret;
+ struct sockaddr_storage save_sa;
+
+ probe_proto = nfs_default_proto();
+ memcpy(&save_sa, sap, salen);
+
+ ret = nfs_probe_port(sap, salen, pmap,
+ probe_nfs3_only, probe_proto);
+ if (!ret || !checkv4 || probe_proto != probe_tcp_first)
+ return ret;
+
+ nfs_set_port((struct sockaddr *)&save_sa, NFS_PORT);
+ ret = nfs_rpc_ping((struct sockaddr *)&save_sa, salen,
+ NFS_PROGRAM, 4, IPPROTO_TCP, NULL);
+ if (ret) {
+ rpc_createerr.cf_stat = RPC_FAILED;
+ rpc_createerr.cf_error.re_errno = EAGAIN;
+ return 0;
+ }
+ return 1;
+ } else
+ return nfs_probe_port(sap, salen, pmap,
+ probe_nfs2_only, probe_udp_only);
+}
+
+/*
+ * Probe a server's mountd service to determine which versions and
+ * transport protocols are supported.
+ *
+ * Returns 1 if the requested service port is unambiguous and pingable;
+ * @pmap is filled in with the version, port, and transport protocol used
+ * during the successful ping. If all three are already specified, simply
+ * return success without an rpcbind query or RPC ping (we may be trying
+ * to mount an NFS service that is not advertised via rpcbind).
+ *
+ * If an error occurs or the requested service isn't available, zero is
+ * returned; rpccreateerr.cf_stat is set to reflect the nature of the error.
+ */
+static int nfs_probe_mntport(const struct sockaddr *sap, const socklen_t salen,
+ struct pmap *pmap)
+{
+ if (pmap->pm_vers && pmap->pm_prot && pmap->pm_port)
+ return 1;
+
+ if (nfs_mount_data_version >= 4)
+ return nfs_probe_port(sap, salen, pmap,
+ probe_mnt3_only, probe_udp_first);
+ else
+ return nfs_probe_port(sap, salen, pmap,
+ probe_mnt1_first, probe_udp_only);
+}
+
+/*
+ * Probe a server's mountd service to determine which versions and
+ * transport protocols are supported. Invoked when the protocol
+ * version is already known for both the NFS and mountd service.
+ *
+ * Returns 1 and fills in both @pmap structs if the requested service
+ * ports are unambiguous and pingable. Otherwise zero is returned;
+ * rpccreateerr.cf_stat is set to reflect the nature of the error.
+ */
+static int nfs_probe_version_fixed(const struct sockaddr *mnt_saddr,
+ const socklen_t mnt_salen,
+ struct pmap *mnt_pmap,
+ const struct sockaddr *nfs_saddr,
+ const socklen_t nfs_salen,
+ struct pmap *nfs_pmap)
+{
+ if (!nfs_probe_nfsport(nfs_saddr, nfs_salen, nfs_pmap, 0))
+ return 0;
+ return nfs_probe_mntport(mnt_saddr, mnt_salen, mnt_pmap);
+}
+
+/**
+ * nfs_probe_bothports - discover the RPC endpoints of mountd and NFS server
+ * @mnt_saddr: pointer to socket address of mountd server
+ * @mnt_salen: length of mountd server's address
+ * @mnt_pmap: IN: partially filled-in mountd RPC service tuple;
+ * OUT: fully filled-in mountd RPC service tuple
+ * @nfs_saddr: pointer to socket address of NFS server
+ * @nfs_salen: length of NFS server's address
+ * @nfs_pmap: IN: partially filled-in NFS RPC service tuple;
+ * OUT: fully filled-in NFS RPC service tuple
+ * @checkv4: Flag indicating that if v3 is available we must also
+ * check v4, and if that is available, set re_errno to EAGAIN.
+ *
+ * Returns 1 and fills in both @pmap structs if the requested service
+ * ports are unambiguous and pingable. Otherwise zero is returned;
+ * rpccreateerr.cf_stat is set to reflect the nature of the error.
+ */
+int nfs_probe_bothports(const struct sockaddr *mnt_saddr,
+ const socklen_t mnt_salen,
+ struct pmap *mnt_pmap,
+ const struct sockaddr *nfs_saddr,
+ const socklen_t nfs_salen,
+ struct pmap *nfs_pmap,
+ int checkv4)
+{
+ struct pmap save_nfs, save_mnt;
+ const unsigned long *probe_vers;
+
+ if (mnt_pmap->pm_vers && !nfs_pmap->pm_vers)
+ nfs_pmap->pm_vers = mntvers_to_nfs(mnt_pmap->pm_vers);
+ else if (nfs_pmap->pm_vers && !mnt_pmap->pm_vers)
+ mnt_pmap->pm_vers = nfsvers_to_mnt(nfs_pmap->pm_vers);
+
+ if (nfs_pmap->pm_vers)
+ return nfs_probe_version_fixed(mnt_saddr, mnt_salen, mnt_pmap,
+ nfs_saddr, nfs_salen, nfs_pmap);
+
+ memcpy(&save_nfs, nfs_pmap, sizeof(save_nfs));
+ memcpy(&save_mnt, mnt_pmap, sizeof(save_mnt));
+ probe_vers = (nfs_mount_data_version >= 4) ?
+ probe_mnt3_only : probe_mnt1_first;
+
+ for (; *probe_vers; probe_vers++) {
+ nfs_pmap->pm_vers = mntvers_to_nfs(*probe_vers);
+ if (nfs_probe_nfsport(nfs_saddr, nfs_salen, nfs_pmap, checkv4) != 0) {
+ mnt_pmap->pm_vers = *probe_vers;
+ if (nfs_probe_mntport(mnt_saddr, mnt_salen, mnt_pmap) != 0)
+ return 1;
+ memcpy(mnt_pmap, &save_mnt, sizeof(*mnt_pmap));
+ }
+ switch (rpc_createerr.cf_stat) {
+ case RPC_PROGVERSMISMATCH:
+ case RPC_PROGNOTREGISTERED:
+ break;
+ default:
+ return 0;
+ }
+ memcpy(nfs_pmap, &save_nfs, sizeof(*nfs_pmap));
+ }
+
+ return 0;
+}
+
+/**
+ * probe_bothports - discover the RPC endpoints of mountd and NFS server
+ * @mnt_server: pointer to address and pmap argument for mountd results
+ * @nfs_server: pointer to address and pmap argument for NFS server
+ *
+ * This is the legacy API that takes "clnt_addr_t" for both servers,
+ * but supports only AF_INET addresses.
+ *
+ * Returns 1 and fills in the pmap field in both clnt_addr_t structs
+ * if the requested service ports are unambiguous and pingable.
+ * Otherwise zero is returned; rpccreateerr.cf_stat is set to reflect
+ * the nature of the error.
+ */
+int probe_bothports(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server)
+{
+ struct sockaddr *mnt_addr = SAFE_SOCKADDR(&mnt_server->saddr);
+ struct sockaddr *nfs_addr = SAFE_SOCKADDR(&nfs_server->saddr);
+
+ return nfs_probe_bothports(mnt_addr, sizeof(mnt_server->saddr),
+ &mnt_server->pmap,
+ nfs_addr, sizeof(nfs_server->saddr),
+ &nfs_server->pmap, 0);
+}
+
+/**
+ * start_statd - attempt to start rpc.statd
+ *
+ * Returns 1 if statd is running; otherwise zero.
+ */
+int start_statd(void)
+{
+#ifdef START_STATD
+ struct stat stb;
+#endif
+
+ if (nfs_probe_statd())
+ return 1;
+
+#ifdef START_STATD
+ if (stat(START_STATD, &stb) == 0) {
+ if (S_ISREG(stb.st_mode) && (stb.st_mode & S_IXUSR)) {
+ int cnt = STATD_TIMEOUT * 10;
+ int status = 0;
+ char * const envp[1] = { NULL };
+ const struct timespec ts = {
+ .tv_sec = 0,
+ .tv_nsec = 100000000,
+ };
+ pid_t pid = fork();
+ switch (pid) {
+ case 0: /* child */
+ setgroups(0, NULL);
+ if (setgid(0) < 0)
+ nfs_error(_("%s: setgid(0) failed: %s"),
+ progname, strerror(errno));
+ if (setuid(0) < 0)
+ nfs_error(_("%s: setuid(0) failed: %s"),
+ progname, strerror(errno));
+ execle(START_STATD, START_STATD, NULL, envp);
+ exit(1);
+ case -1: /* error */
+ nfs_error(_("%s: fork failed: %s"),
+ progname, strerror(errno));
+ break;
+ default: /* parent */
+ if (waitpid(pid, &status,0) == pid &&
+ status == 0)
+ /* assume it worked */
+ return 1;
+ break;
+ }
+ while (1) {
+ if (nfs_probe_statd())
+ return 1;
+ if (! cnt--)
+ return 0;
+ nanosleep(&ts, NULL);
+ }
+ }
+ }
+#endif
+
+ return 0;
+}
+
+/**
+ * nfs_advise_umount - ask the server to remove a share from it's rmtab
+ * @sap: pointer to IP address of server to call
+ * @salen: length of server address
+ * @pmap: partially filled-in mountd RPC service tuple
+ * @argp: directory path of share to "unmount"
+ *
+ * Returns one if the unmount call succeeded; zero if the unmount
+ * failed for any reason; rpccreateerr.cf_stat is set to reflect
+ * the nature of the error.
+ *
+ * We use a fast timeout since this call is advisory only.
+ */
+int nfs_advise_umount(const struct sockaddr *sap, const socklen_t salen,
+ const struct pmap *pmap, const dirpath *argp)
+{
+ union nfs_sockaddr address;
+ struct sockaddr *saddr = &address.sa;
+ struct pmap mnt_pmap = *pmap;
+ struct timeval timeout = {
+ .tv_sec = MOUNT_TIMEOUT >> 3,
+ };
+ CLIENT *client;
+ enum clnt_stat res = 0;
+
+ memcpy(saddr, sap, salen);
+ if (nfs_probe_mntport(saddr, salen, &mnt_pmap) == 0) {
+ if (verbose)
+ nfs_error(_("%s: Failed to discover mountd port%s"),
+ progname, clnt_spcreateerror(""));
+ return 0;
+ }
+ nfs_set_port(saddr, mnt_pmap.pm_port);
+
+ client = nfs_get_priv_rpcclient(saddr, salen, mnt_pmap.pm_prot,
+ mnt_pmap.pm_prog, mnt_pmap.pm_vers,
+ &timeout);
+ if (client == NULL) {
+ if (verbose)
+ nfs_error(_("%s: Failed to create RPC client%s"),
+ progname, clnt_spcreateerror(""));
+ return 0;
+ }
+
+ client->cl_auth = nfs_authsys_create();
+ if (client->cl_auth == NULL) {
+ if (verbose)
+ nfs_error(_("%s: Failed to create RPC auth handle"),
+ progname);
+ CLNT_DESTROY(client);
+ return 0;
+ }
+
+ res = CLNT_CALL(client, MOUNTPROC_UMNT,
+ (xdrproc_t)xdr_dirpath, (caddr_t)argp,
+ (xdrproc_t)xdr_void, NULL,
+ timeout);
+ if (res != RPC_SUCCESS) {
+ rpc_createerr.cf_stat = res;
+ CLNT_GETERR(client, &rpc_createerr.cf_error);
+ if (verbose)
+ nfs_error(_("%s: UMNT call failed: %s"),
+ progname, clnt_sperrno(res));
+
+ }
+ auth_destroy(client->cl_auth);
+ CLNT_DESTROY(client);
+
+ if (res != RPC_SUCCESS)
+ return 0;
+ return 1;
+}
+
+/**
+ * nfs_call_umount - ask the server to remove a share from it's rmtab
+ * @mnt_server: address of RPC MNT program server
+ * @argp: directory path of share to "unmount"
+ *
+ * Returns one if the unmount call succeeded; zero if the unmount
+ * failed for any reason.
+ *
+ * Note that a side effect of calling this function is that rpccreateerr
+ * is set.
+ */
+int nfs_call_umount(clnt_addr_t *mnt_server, dirpath *argp)
+{
+ struct sockaddr *sap = SAFE_SOCKADDR(&mnt_server->saddr);
+ socklen_t salen = sizeof(mnt_server->saddr);
+ struct pmap *pmap = &mnt_server->pmap;
+ CLIENT *clnt;
+ enum clnt_stat res = 0;
+ int msock;
+
+ if (!nfs_probe_mntport(sap, salen, pmap))
+ return 0;
+ clnt = mnt_openclnt(mnt_server, &msock);
+ if (!clnt)
+ return 0;
+ res = clnt_call(clnt, MOUNTPROC_UMNT,
+ (xdrproc_t)xdr_dirpath, (caddr_t)argp,
+ (xdrproc_t)xdr_void, NULL,
+ TIMEOUT);
+ mnt_closeclnt(clnt, msock);
+
+ if (res == RPC_SUCCESS)
+ return 1;
+ return 0;
+}
+
+/**
+ * mnt_openclnt - get a handle for a remote mountd service
+ * @mnt_server: address and pmap arguments of mountd service
+ * @msock: returns a file descriptor of the underlying transport socket
+ *
+ * Returns an active handle for the remote's mountd service
+ */
+CLIENT *mnt_openclnt(clnt_addr_t *mnt_server, int *msock)
+{
+ struct sockaddr_in *mnt_saddr = &mnt_server->saddr;
+ struct pmap *mnt_pmap = &mnt_server->pmap;
+ CLIENT *clnt = NULL;
+
+ mnt_saddr->sin_port = htons((u_short)mnt_pmap->pm_port);
+ *msock = get_socket(mnt_saddr, mnt_pmap->pm_prot, MOUNT_TIMEOUT,
+ TRUE, FALSE);
+ if (*msock == RPC_ANYSOCK) {
+ if (rpc_createerr.cf_error.re_errno == EADDRINUSE)
+ /*
+ * Probably in-use by a TIME_WAIT connection,
+ * It is worth waiting a while and trying again.
+ */
+ rpc_createerr.cf_stat = RPC_TIMEDOUT;
+ return NULL;
+ }
+
+ switch (mnt_pmap->pm_prot) {
+ case IPPROTO_UDP:
+ clnt = clntudp_bufcreate(mnt_saddr,
+ mnt_pmap->pm_prog, mnt_pmap->pm_vers,
+ RETRY_TIMEOUT, msock,
+ MNT_SENDBUFSIZE, MNT_RECVBUFSIZE);
+ break;
+ case IPPROTO_TCP:
+ clnt = clnttcp_create(mnt_saddr,
+ mnt_pmap->pm_prog, mnt_pmap->pm_vers,
+ msock,
+ MNT_SENDBUFSIZE, MNT_RECVBUFSIZE);
+ break;
+ }
+ if (clnt) {
+ /* try to mount hostname:dirname */
+ clnt->cl_auth = nfs_authsys_create();
+ if (clnt->cl_auth)
+ return clnt;
+ CLNT_DESTROY(clnt);
+ }
+ return NULL;
+}
+
+/**
+ * mnt_closeclnt - terminate a handle for a remote mountd service
+ * @clnt: pointer to an active handle for a remote mountd service
+ * @msock: file descriptor of the underlying transport socket
+ *
+ */
+void mnt_closeclnt(CLIENT *clnt, int msock)
+{
+ auth_destroy(clnt->cl_auth);
+ clnt_destroy(clnt);
+ close(msock);
+}
+
+/**
+ * clnt_ping - send an RPC ping to the remote RPC service endpoint
+ * @saddr: server's address
+ * @prog: target RPC program number
+ * @vers: target RPC version number
+ * @prot: target RPC protocol
+ * @caddr: filled in with our network address
+ *
+ * Sigh... GETPORT queries don't actually check the version number.
+ * In order to make sure that the server actually supports the service
+ * we're requesting, we open an RPC client, and fire off a NULL
+ * RPC call.
+ *
+ * caddr is the network address that the server will use to call us back.
+ * On multi-homed clients, this address depends on which NIC we use to
+ * route requests to the server.
+ *
+ * Returns one if successful, otherwise zero.
+ */
+int clnt_ping(struct sockaddr_in *saddr, const unsigned long prog,
+ const unsigned long vers, const unsigned int prot,
+ struct sockaddr_in *caddr)
+{
+ CLIENT *clnt = NULL;
+ int sock, status;
+ static char clnt_res;
+ struct sockaddr dissolve;
+
+ rpc_createerr.cf_stat = status = 0;
+ sock = get_socket(saddr, prot, CONNECT_TIMEOUT, FALSE, TRUE);
+ if (sock == RPC_ANYSOCK) {
+ if (rpc_createerr.cf_error.re_errno == ETIMEDOUT) {
+ /*
+ * TCP timeout. Bubble up the error to see
+ * how it should be handled.
+ */
+ rpc_createerr.cf_stat = RPC_TIMEDOUT;
+ }
+ return 0;
+ }
+
+ if (caddr) {
+ /* Get the address of our end of this connection */
+ socklen_t len = sizeof(*caddr);
+ if (getsockname(sock, (struct sockaddr *) caddr, &len) != 0)
+ caddr->sin_family = 0;
+ }
+
+ switch(prot) {
+ case IPPROTO_UDP:
+ /* The socket is connected (so we could getsockname successfully),
+ * but some servers on multi-homed hosts reply from
+ * the wrong address, so if we stay connected, we lose the reply.
+ */
+ dissolve.sa_family = AF_UNSPEC;
+ connect(sock, &dissolve, sizeof(dissolve));
+
+ clnt = clntudp_bufcreate(saddr, prog, vers,
+ RETRY_TIMEOUT, &sock,
+ RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
+ break;
+ case IPPROTO_TCP:
+ clnt = clnttcp_create(saddr, prog, vers, &sock,
+ RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
+ break;
+ }
+ if (!clnt) {
+ close(sock);
+ return 0;
+ }
+ memset(&clnt_res, 0, sizeof(clnt_res));
+ status = clnt_call(clnt, NULLPROC,
+ (xdrproc_t)xdr_void, (caddr_t)NULL,
+ (xdrproc_t)xdr_void, (caddr_t)&clnt_res,
+ TIMEOUT);
+ if (status) {
+ clnt_geterr(clnt, &rpc_createerr.cf_error);
+ rpc_createerr.cf_stat = status;
+ }
+ clnt_destroy(clnt);
+ close(sock);
+
+ if (status == RPC_SUCCESS)
+ return 1;
+ else
+ return 0;
+}
+
+/*
+ * Try a getsockname() on a connected datagram socket.
+ *
+ * Returns 1 and fills in @buf if successful; otherwise, zero.
+ *
+ * A connected datagram socket prevents leaving a socket in TIME_WAIT.
+ * This conserves the ephemeral port number space, helping reduce failed
+ * socket binds during mount storms.
+ */
+static int nfs_ca_sockname(const struct sockaddr *sap, const socklen_t salen,
+ struct sockaddr *buf, socklen_t *buflen)
+{
+ struct sockaddr_in sin = {
+ .sin_family = AF_INET,
+ .sin_addr.s_addr = htonl(INADDR_ANY),
+ };
+ struct sockaddr_in6 sin6 = {
+ .sin6_family = AF_INET6,
+ .sin6_addr = IN6ADDR_ANY_INIT,
+ };
+ int sock, result = 0;
+ int val;
+
+ sock = socket(sap->sa_family, SOCK_DGRAM, IPPROTO_UDP);
+ if (sock < 0)
+ return 0;
+
+ switch (sap->sa_family) {
+ case AF_INET:
+ if (bind(sock, SAFE_SOCKADDR(&sin), sizeof(sin)) < 0)
+ goto out;
+ break;
+ case AF_INET6:
+ /* Make sure the call-back address is public/permanent */
+ val = IPV6_PREFER_SRC_PUBLIC;
+ setsockopt(sock, SOL_IPV6, IPV6_ADDR_PREFERENCES, &val, sizeof(val));
+ if (bind(sock, SAFE_SOCKADDR(&sin6), sizeof(sin6)) < 0)
+ goto out;
+ break;
+ default:
+ errno = EAFNOSUPPORT;
+ goto out;
+ }
+
+ if (connect(sock, sap, salen) < 0)
+ goto out;
+
+ result = !getsockname(sock, buf, buflen);
+
+out:
+ close(sock);
+ return result;
+}
+
+/*
+ * Try to generate an address that prevents the server from calling us.
+ *
+ * Returns 1 and fills in @buf if successful; otherwise, zero.
+ */
+static int nfs_ca_gai(const struct sockaddr *sap,
+ struct sockaddr *buf, socklen_t *buflen)
+{
+ struct addrinfo *gai_results;
+ struct addrinfo gai_hint = {
+ .ai_family = sap->sa_family,
+ .ai_flags = AI_PASSIVE, /* ANYADDR */
+ };
+
+ if (getaddrinfo(NULL, "", &gai_hint, &gai_results))
+ return 0;
+
+ *buflen = gai_results->ai_addrlen;
+ memcpy(buf, gai_results->ai_addr, *buflen);
+
+ nfs_freeaddrinfo(gai_results);
+
+ return 1;
+}
+
+/**
+ * nfs_callback_address - acquire our local network address
+ * @sap: pointer to address of remote
+ * @sap_len: length of address
+ * @buf: pointer to buffer to be filled in with local network address
+ * @buflen: IN: length of buffer to fill in; OUT: length of filled-in address
+ *
+ * Discover a network address that an NFSv4 server can use to call us back.
+ * On multi-homed clients, this address depends on which NIC we use to
+ * route requests to the server.
+ *
+ * Returns 1 and fills in @buf if an unambiguous local address is
+ * available; returns 1 and fills in an appropriate ANYADDR address
+ * if a local address isn't available; otherwise, returns zero.
+ */
+int nfs_callback_address(const struct sockaddr *sap, const socklen_t salen,
+ struct sockaddr *buf, socklen_t *buflen)
+{
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)buf;
+
+ if (nfs_ca_sockname(sap, salen, buf, buflen) == 0)
+ if (nfs_ca_gai(sap, buf, buflen) == 0)
+ goto out_failed;
+
+ /*
+ * The server can't use an interface ID that was generated
+ * here on the client, so always clear sin6_scope_id.
+ */
+ if (sin6->sin6_family == AF_INET6)
+ sin6->sin6_scope_id = 0;
+
+ return 1;
+
+out_failed:
+ *buflen = 0;
+ if (verbose)
+ nfs_error(_("%s: failed to construct callback address"),
+ progname);
+ return 0;
+}
+
+/*
+ * "nfsprog" is supported only by the legacy mount command. The
+ * kernel mount client does not support this option.
+ *
+ * Returns TRUE if @program contains a valid value for this option,
+ * or FALSE if the option was specified with an invalid value.
+ */
+static int
+nfs_nfs_program(struct mount_options *options, unsigned long *program)
+{
+ long tmp;
+
+ switch (po_get_numeric(options, "nfsprog", &tmp)) {
+ case PO_NOT_FOUND:
+ break;
+ case PO_FOUND:
+ if (tmp > 0) {
+ *program = tmp;
+ return 1;
+ }
+ /* FALLTHRU */
+ case PO_BAD_VALUE:
+ nfs_error(_("%s: invalid value for 'nfsprog=' option"),
+ progname);
+ return 0;
+ }
+
+ /*
+ * NFS RPC program wasn't specified. The RPC program
+ * cannot be determined via an rpcbind query.
+ */
+ *program = nfs_getrpcbyname(NFSPROG, nfs_nfs_pgmtbl);
+ return 1;
+}
+
+/*
+ * Returns TRUE if @version contains a valid value for this option,
+ * or FALSE if the option was specified with an invalid value.
+ */
+int
+nfs_nfs_version(char *type, struct mount_options *options, struct nfs_version *version)
+{
+ char *version_key, *version_val = NULL, *cptr;
+ int i, found = -1;
+
+ version->v_mode = V_DEFAULT;
+
+ for (i = 0; nfs_version_opttbl[i]; i++) {
+ if (po_contains_prefix(options, nfs_version_opttbl[i],
+ &version_key, 0) == PO_FOUND) {
+ if (found >= 0)
+ goto ret_error_multiple;
+ if (po_contains_prefix(options, nfs_version_opttbl[i],
+ NULL, 1) == PO_FOUND)
+ goto ret_error_multiple;
+ found = i;
+ }
+ }
+
+ if (found < 0 && strcmp(type, "nfs4") == 0)
+ version_val = type + 3;
+ else if (found < 0)
+ return 1;
+ else if (found <= 2 ) {
+ /* v3, v4 */
+ version_val = version_key + 1;
+ version->v_mode = V_SPECIFIC;
+ } else if (found > 2 ) {
+ /* vers=, nfsvers= */
+ version_val = po_get(options, version_key);
+ }
+
+ if (!version_val)
+ goto ret_error;
+
+ version->major = strtol(version_val, &cptr, 10);
+ if (cptr == version_val || (*cptr && *cptr != '.'))
+ goto ret_error;
+ if (version->major == 4 && *cptr != '.' &&
+ (version_val = po_get(options, "minorversion")) != NULL) {
+ version->minor = strtol(version_val, &cptr, 10);
+ found = -1;
+ if (*cptr)
+ goto ret_error;
+ version->v_mode = V_SPECIFIC;
+ } else if (version->major < 4)
+ version->v_mode = V_SPECIFIC;
+ else if (*cptr == '.') {
+ version_val = ++cptr;
+ if (!(version->minor = strtol(version_val, &cptr, 10)) && cptr == version_val)
+ goto ret_error;
+ version->v_mode = V_SPECIFIC;
+ } else if (version->major > 3 && *cptr == '\0') {
+ version_val = po_get(options, "minorversion");
+ if (version_val != NULL) {
+ version->minor = strtol(version_val, &cptr, 10);
+ version->v_mode = V_SPECIFIC;
+ } else
+ version->v_mode = V_GENERAL;
+ }
+ if (*cptr != '\0')
+ goto ret_error;
+
+ return 1;
+
+ret_error_multiple:
+ nfs_error(_("%s: multiple version options not permitted"),
+ progname);
+ found = 10; /* avoid other errors */
+ret_error:
+ if (found < 0) {
+ nfs_error(_("%s: parsing error on 'minorversion=' option"),
+ progname);
+ } else if (found <= 2 ) {
+ nfs_error(_("%s: parsing error on 'v' option"),
+ progname);
+ } else if (found == 3 ) {
+ nfs_error(_("%s: parsing error on 'vers=' option"),
+ progname);
+ } else if (found == 4) {
+ nfs_error(_("%s: parsing error on 'nfsvers=' option"),
+ progname);
+ }
+ version->v_mode = V_PARSE_ERR;
+ errno = EINVAL;
+ return 0;
+}
+
+/*
+ * Returns TRUE if @protocol contains a valid value for this option,
+ * or FALSE if the option was specified with an invalid value. On
+ * error, errno is set.
+ */
+int
+nfs_nfs_protocol(struct mount_options *options, unsigned long *protocol)
+{
+ sa_family_t family;
+ char *option;
+
+ switch (po_rightmost(options, nfs_transport_opttbl)) {
+ case 0: /* udp */
+ *protocol = IPPROTO_UDP;
+ return 1;
+ case 1: /* tcp */
+ *protocol = IPPROTO_TCP;
+ return 1;
+ case 2: /* rdma */
+ *protocol = NFSPROTO_RDMA;
+ return 1;
+ case 3: /* proto */
+ option = po_get(options, "proto");
+ if (option != NULL) {
+ if (!nfs_get_proto(option, &family, protocol)) {
+ errno = EPROTONOSUPPORT;
+ nfs_error(_("%s: Failed to find '%s' protocol"),
+ progname, option);
+ return 0;
+ }
+ return 1;
+ }
+ }
+
+ /*
+ * NFS transport protocol wasn't specified. The pmap
+ * protocol value will be filled in later by an rpcbind
+ * query in this case.
+ */
+ *protocol = 0;
+ return 1;
+}
+
+/*
+ * Returns TRUE if @port contains a valid value for this option,
+ * or FALSE if the option was specified with an invalid value.
+ */
+static int
+nfs_nfs_port(struct mount_options *options, unsigned long *port)
+{
+ long tmp;
+
+ switch (po_get_numeric(options, "port", &tmp)) {
+ case PO_NOT_FOUND:
+ break;
+ case PO_FOUND:
+ if (tmp >= 0 && tmp <= 65535) {
+ *port = tmp;
+ return 1;
+ }
+ /* FALLTHRU */
+ case PO_BAD_VALUE:
+ nfs_error(_("%s: invalid value for 'port=' option"),
+ progname);
+ return 0;
+ }
+
+ /*
+ * NFS service port wasn't specified. The pmap port value
+ * will be filled in later by an rpcbind query in this case.
+ */
+ *port = 0;
+ return 1;
+}
+
+#ifdef IPV6_SUPPORTED
+sa_family_t config_default_family = AF_UNSPEC;
+
+static int
+nfs_verify_family(sa_family_t UNUSED(family))
+{
+ return 1;
+}
+#else /* IPV6_SUPPORTED */
+sa_family_t config_default_family = AF_INET;
+
+static int
+nfs_verify_family(sa_family_t family)
+{
+ if (family != AF_INET)
+ return 0;
+
+ return 1;
+}
+#endif /* IPV6_SUPPORTED */
+
+/*
+ * Returns TRUE and fills in @family if a valid NFS protocol option
+ * is found, or FALSE if the option was specified with an invalid value
+ * or if the protocol family isn't supported. On error, errno is set.
+ */
+int nfs_nfs_proto_family(struct mount_options *options,
+ sa_family_t *family)
+{
+ unsigned long protocol;
+ char *option;
+ sa_family_t tmp_family = config_default_family;
+
+ switch (po_rightmost(options, nfs_transport_opttbl)) {
+ case 0: /* udp */
+ case 1: /* tcp */
+ case 2: /* rdma */
+ /* for compatibility; these are always AF_INET */
+ *family = AF_INET;
+ return 1;
+ case 3: /* proto */
+ option = po_get(options, "proto");
+ if (option != NULL &&
+ !nfs_get_proto(option, &tmp_family, &protocol)) {
+
+ nfs_error(_("%s: Failed to find '%s' protocol"),
+ progname, option);
+ errno = EPROTONOSUPPORT;
+ return 0;
+ }
+ }
+
+ if (!nfs_verify_family(tmp_family))
+ goto out_err;
+ *family = tmp_family;
+ return 1;
+out_err:
+ errno = EAFNOSUPPORT;
+ return 0;
+}
+
+/*
+ * "mountprog" is supported only by the legacy mount command. The
+ * kernel mount client does not support this option.
+ *
+ * Returns TRUE if @program contains a valid value for this option,
+ * or FALSE if the option was specified with an invalid value.
+ */
+static int
+nfs_mount_program(struct mount_options *options, unsigned long *program)
+{
+ long tmp;
+
+ switch (po_get_numeric(options, "mountprog", &tmp)) {
+ case PO_NOT_FOUND:
+ break;
+ case PO_FOUND:
+ if (tmp > 0) {
+ *program = tmp;
+ return 1;
+ }
+ /* FALLTHRU */
+ case PO_BAD_VALUE:
+ nfs_error(_("%s: invalid value for 'mountprog=' option"),
+ progname);
+ return 0;
+ }
+
+ /*
+ * MNT RPC program wasn't specified. The RPC program
+ * cannot be determined via an rpcbind query.
+ */
+ *program = nfs_getrpcbyname(MOUNTPROG, nfs_mnt_pgmtbl);
+ return 1;
+}
+
+/*
+ * Returns TRUE if @version contains a valid value for this option,
+ * or FALSE if the option was specified with an invalid value.
+ */
+static int
+nfs_mount_version(struct mount_options *options, unsigned long *version)
+{
+ long tmp;
+
+ switch (po_get_numeric(options, "mountvers", &tmp)) {
+ case PO_NOT_FOUND:
+ break;
+ case PO_FOUND:
+ if (tmp >= 1 && tmp <= 4) {
+ *version = tmp;
+ return 1;
+ }
+ /* FALLTHRU */
+ case PO_BAD_VALUE:
+ nfs_error(_("%s: invalid value for 'mountvers=' option"),
+ progname);
+ return 0;
+ }
+
+ /*
+ * MNT version wasn't specified. The pmap version value
+ * will be filled in later by an rpcbind query in this case.
+ */
+ *version = 0;
+ return 1;
+}
+
+/*
+ * Returns TRUE if @protocol contains a valid value for this option,
+ * or FALSE if the option was specified with an invalid value. On
+ * error, errno is set.
+ */
+static int
+nfs_mount_protocol(struct mount_options *options, unsigned long *protocol)
+{
+ sa_family_t family;
+ char *option;
+
+ option = po_get(options, "mountproto");
+ if (option != NULL) {
+ if (!nfs_get_proto(option, &family, protocol)) {
+ errno = EPROTONOSUPPORT;
+ nfs_error(_("%s: Failed to find '%s' protocol"),
+ progname, option);
+ return 0;
+ }
+ return 1;
+ }
+
+ /*
+ * MNT transport protocol wasn't specified. If the NFS
+ * transport protocol was specified, use that; otherwise
+ * set @protocol to zero. The pmap protocol value will
+ * be filled in later by an rpcbind query in this case.
+ */
+ if (!nfs_nfs_protocol(options, protocol))
+ return 0;
+ if (*protocol == NFSPROTO_RDMA)
+ *protocol = IPPROTO_TCP;
+ return 1;
+}
+
+/*
+ * Returns TRUE if @port contains a valid value for this option,
+ * or FALSE if the option was specified with an invalid value.
+ */
+static int
+nfs_mount_port(struct mount_options *options, unsigned long *port)
+{
+ long tmp;
+
+ switch (po_get_numeric(options, "mountport", &tmp)) {
+ case PO_NOT_FOUND:
+ break;
+ case PO_FOUND:
+ if (tmp >= 0 && tmp <= 65535) {
+ *port = tmp;
+ return 1;
+ }
+ /* FALLTHRU */
+ case PO_BAD_VALUE:
+ nfs_error(_("%s: invalid value for 'mountport=' option"),
+ progname);
+ return 0;
+ }
+
+ /*
+ * MNT service port wasn't specified. The pmap port value
+ * will be filled in later by an rpcbind query in this case.
+ */
+ *port = 0;
+ return 1;
+}
+
+/*
+ * Returns TRUE and fills in @family if a valid MNT protocol option
+ * is found, or FALSE if the option was specified with an invalid value
+ * or if the protocol family isn't supported. On error, errno is set.
+ */
+int nfs_mount_proto_family(struct mount_options *options,
+ sa_family_t *family)
+{
+ unsigned long protocol;
+ char *option;
+ sa_family_t tmp_family = config_default_family;
+
+ option = po_get(options, "mountproto");
+ if (option != NULL) {
+ if (!nfs_get_proto(option, &tmp_family, &protocol)) {
+ nfs_error(_("%s: Failed to find '%s' protocol"),
+ progname, option);
+ errno = EPROTONOSUPPORT;
+ goto out_err;
+ }
+ if (!nfs_verify_family(tmp_family))
+ goto out_err;
+ *family = tmp_family;
+ return 1;
+ }
+
+ /*
+ * MNT transport protocol wasn't specified. If the NFS
+ * transport protocol was specified, derive the family
+ * from that; otherwise, return the default family for
+ * NFS.
+ */
+ return nfs_nfs_proto_family(options, family);
+out_err:
+ errno = EAFNOSUPPORT;
+ return 0;
+}
+
+/**
+ * nfs_options2pmap - set up pmap structs based on mount options
+ * @options: pointer to mount options
+ * @nfs_pmap: OUT: pointer to pmap arguments for NFS server
+ * @mnt_pmap: OUT: pointer to pmap arguments for mountd server
+ *
+ * Returns TRUE if the pmap options specified in @options have valid
+ * values; otherwise FALSE is returned.
+ */
+int nfs_options2pmap(struct mount_options *options,
+ struct pmap *nfs_pmap, struct pmap *mnt_pmap)
+{
+ struct nfs_version version;
+ memset(&version, 0, sizeof(version));
+
+ if (!nfs_nfs_program(options, &nfs_pmap->pm_prog))
+ return 0;
+ if (!nfs_nfs_version("nfs", options, &version))
+ return 0;
+ if (version.v_mode == V_DEFAULT)
+ nfs_pmap->pm_vers = 0;
+ else
+ nfs_pmap->pm_vers = version.major;
+ if (!nfs_nfs_protocol(options, &nfs_pmap->pm_prot))
+ return 0;
+ if (!nfs_nfs_port(options, &nfs_pmap->pm_port))
+ return 0;
+
+ if (!nfs_mount_program(options, &mnt_pmap->pm_prog))
+ return 0;
+ if (!nfs_mount_version(options, &mnt_pmap->pm_vers))
+ return 0;
+ if (!nfs_mount_protocol(options, &mnt_pmap->pm_prot))
+ return 0;
+ if (!nfs_mount_port(options, &mnt_pmap->pm_port))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Discover mount server's hostname/address by examining mount options
+ *
+ * Returns a pointer to a string that the caller must free, on
+ * success; otherwise NULL is returned.
+ */
+static char *nfs_umount_hostname(struct mount_options *options,
+ char *hostname)
+{
+ char *option;
+
+ option = po_get(options, "mountaddr");
+ if (option)
+ goto out;
+ option = po_get(options, "mounthost");
+ if (option)
+ goto out;
+ option = po_get(options, "addr");
+ if (option)
+ goto out;
+
+ return hostname;
+
+out:
+ free(hostname);
+ return strdup(option);
+}
+
+
+/*
+ * Returns EX_SUCCESS if mount options and device name have been
+ * parsed successfully; otherwise EX_FAIL.
+ */
+int nfs_umount_do_umnt(struct mount_options *options,
+ char **hostname, char **dirname)
+{
+ union nfs_sockaddr address;
+ struct sockaddr *sap = &address.sa;
+ socklen_t salen = sizeof(address);
+ struct pmap nfs_pmap, mnt_pmap;
+ sa_family_t family;
+
+ if (!nfs_options2pmap(options, &nfs_pmap, &mnt_pmap))
+ return EX_FAIL;
+
+ /* Skip UMNT call for vers=4 mounts */
+ if (nfs_pmap.pm_vers == 4)
+ return EX_SUCCESS;
+
+ *hostname = nfs_umount_hostname(options, *hostname);
+ if (!*hostname) {
+ nfs_error(_("%s: out of memory"), progname);
+ return EX_FAIL;
+ }
+
+ if (!nfs_mount_proto_family(options, &family))
+ return 0;
+ if (!nfs_lookup(*hostname, family, sap, &salen))
+ /* nfs_lookup reports any errors */
+ return EX_FAIL;
+
+ if (nfs_advise_umount(sap, salen, &mnt_pmap, dirname) == 0)
+ /* nfs_advise_umount reports any errors */
+ return EX_FAIL;
+
+ return EX_SUCCESS;
+}
+
+int nfs_is_inaddr_any(struct sockaddr *nfs_saddr)
+{
+ switch (nfs_saddr->sa_family) {
+ case AF_INET: {
+ if (((struct sockaddr_in *)nfs_saddr)->sin_addr.s_addr ==
+ INADDR_ANY)
+ return 1;
+ break;
+ }
+ case AF_INET6:
+ if (!memcmp(&((struct sockaddr_in6 *)nfs_saddr)->sin6_addr,
+ &in6addr_any, sizeof(in6addr_any)))
+ return 1;
+ break;
+ }
+ return 0;
+}
+
+int nfs_addr_matches_localips(struct sockaddr *nfs_saddr)
+{
+ struct ifaddrs *myaddrs, *ifa;
+ int found = 0;
+
+ /* acquire exiting network interfaces */
+ if (getifaddrs(&myaddrs) != 0)
+ return 0;
+
+ /* interate over the available interfaces and check if we
+ * we find a match to the supplied clientaddr value
+ */
+ for (ifa = myaddrs; ifa != NULL; ifa = ifa->ifa_next) {
+ if (ifa->ifa_addr == NULL)
+ continue;
+ if (!(ifa->ifa_flags & IFF_UP))
+ continue;
+ if (!memcmp(ifa->ifa_addr, nfs_saddr,
+ sizeof(struct sockaddr))) {
+ found = 1;
+ break;
+ }
+ }
+ freeifaddrs(myaddrs);
+ return found;
+}
diff --git a/utils/mount/network.h b/utils/mount/network.h
new file mode 100644
index 0000000..0fc98ac
--- /dev/null
+++ b/utils/mount/network.h
@@ -0,0 +1,96 @@
+/*
+ * network.h -- Provide common network functions for NFS mount/umount
+ *
+ * Copyright (C) 2007 Oracle. All rights reserved.
+ * Copyright (C) 2007 Chuck Lever <chuck.lever@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA
+ *
+ */
+
+#ifndef _NFS_UTILS_MOUNT_NETWORK_H
+#define _NFS_UTILS_MOUNT_NETWORK_H
+
+#include <rpc/pmap_prot.h>
+
+#define MNT_SENDBUFSIZE (2048U)
+#define MNT_RECVBUFSIZE (1024U)
+
+typedef struct {
+ char **hostname;
+ struct sockaddr_in saddr;
+ struct pmap pmap;
+} clnt_addr_t;
+
+/* RPC call timeout values */
+static const struct timeval TIMEOUT = { 20, 0 };
+static const struct timeval RETRY_TIMEOUT = { 3, 0 };
+
+int probe_bothports(clnt_addr_t *, clnt_addr_t *);
+int nfs_probe_bothports(const struct sockaddr *, const socklen_t,
+ struct pmap *, const struct sockaddr *,
+ const socklen_t, struct pmap *, int);
+int nfs_gethostbyname(const char *, struct sockaddr_in *);
+int nfs_lookup(const char *hostname, const sa_family_t family,
+ struct sockaddr *sap, socklen_t *salen);
+int nfs_string_to_sockaddr(const char *, struct sockaddr *, socklen_t *);
+int nfs_present_sockaddr(const struct sockaddr *,
+ const socklen_t, char *, const size_t);
+int nfs_callback_address(const struct sockaddr *, const socklen_t,
+ struct sockaddr *, socklen_t *);
+int clnt_ping(struct sockaddr_in *, const unsigned long,
+ const unsigned long, const unsigned int,
+ struct sockaddr_in *);
+int nfs_is_inaddr_any(struct sockaddr *);
+int nfs_addr_matches_localips(struct sockaddr *);
+
+struct mount_options;
+
+enum {
+ V_DEFAULT = 0,
+ V_GENERAL,
+ V_SPECIFIC,
+ V_PARSE_ERR,
+};
+
+struct nfs_version {
+ unsigned long major;
+ unsigned long minor;
+ int v_mode;
+};
+
+int nfs_nfs_proto_family(struct mount_options *options, sa_family_t *family);
+int nfs_mount_proto_family(struct mount_options *options, sa_family_t *family);
+int nfs_nfs_version(char *type, struct mount_options *options, struct nfs_version *version);
+int nfs_nfs_protocol(struct mount_options *options, unsigned long *protocol);
+
+int nfs_options2pmap(struct mount_options *,
+ struct pmap *, struct pmap *);
+
+int start_statd(void);
+
+unsigned long nfsvers_to_mnt(const unsigned long);
+
+int nfs_call_umount(clnt_addr_t *, dirpath *);
+int nfs_advise_umount(const struct sockaddr *, const socklen_t,
+ const struct pmap *, const dirpath *);
+CLIENT *mnt_openclnt(clnt_addr_t *, int *);
+void mnt_closeclnt(CLIENT *, int);
+
+int nfs_umount_do_umnt(struct mount_options *options,
+ char **hostname, char **dirname);
+
+#endif /* _NFS_UTILS_MOUNT_NETWORK_H */
diff --git a/utils/mount/nfs.man b/utils/mount/nfs.man
new file mode 100644
index 0000000..c0ba4d0
--- /dev/null
+++ b/utils/mount/nfs.man
@@ -0,0 +1,1968 @@
+.\"@(#)nfs.5"
+.TH NFS 5 "9 October 2012"
+.SH NAME
+nfs \- fstab format and options for the
+.B nfs
+file systems
+.SH SYNOPSIS
+.I /etc/fstab
+.SH DESCRIPTION
+NFS is an Internet Standard protocol
+created by Sun Microsystems in 1984. NFS was developed
+to allow file sharing between systems residing
+on a local area network.
+Depending on kernel configuration, the Linux NFS client may
+support NFS versions 3, 4.0, 4.1, or 4.2.
+.P
+The
+.BR mount (8)
+command attaches a file system to the system's
+name space hierarchy at a given mount point.
+The
+.I /etc/fstab
+file describes how
+.BR mount (8)
+should assemble a system's file name hierarchy
+from various independent file systems
+(including file systems exported by NFS servers).
+Each line in the
+.I /etc/fstab
+file describes a single file system, its mount point,
+and a set of default mount options for that mount point.
+.P
+For NFS file system mounts, a line in the
+.I /etc/fstab
+file specifies the server name,
+the path name of the exported server directory to mount,
+the local directory that is the mount point,
+the type of file system that is being mounted,
+and a list of mount options that control
+the way the filesystem is mounted and
+how the NFS client behaves when accessing
+files on this mount point.
+The fifth and sixth fields on each line are not used
+by NFS, thus conventionally each contain the digit zero. For example:
+.P
+.nf
+.ta 8n +14n +14n +9n +20n
+ server:path /mountpoint fstype option,option,... 0 0
+.fi
+.P
+The server's hostname and export pathname
+are separated by a colon, while
+the mount options are separated by commas. The remaining fields
+are separated by blanks or tabs.
+.P
+The server's hostname can be an unqualified hostname,
+a fully qualified domain name,
+a dotted quad IPv4 address, or
+an IPv6 address enclosed in square brackets.
+Link-local and site-local IPv6 addresses must be accompanied by an
+interface identifier.
+See
+.BR ipv6 (7)
+for details on specifying raw IPv6 addresses.
+.P
+The
+.I fstype
+field contains "nfs". Use of the "nfs4" fstype in
+.I /etc/fstab
+is deprecated.
+.SH "MOUNT OPTIONS"
+Refer to
+.BR mount (8)
+for a description of generic mount options
+available for all file systems. If you do not need to
+specify any mount options, use the generic option
+.B defaults
+in
+.IR /etc/fstab .
+.DT
+.SS "Options supported by all versions"
+These options are valid to use with any NFS version.
+.TP 1.5i
+.BI nfsvers= n
+The NFS protocol version number used to contact the server's NFS service.
+If the server does not support the requested version, the mount request
+fails.
+If this option is not specified, the client tries version 4.2 first,
+then negotiates down until it finds a version supported by the server.
+.TP 1.5i
+.BI vers= n
+This option is an alternative to the
+.B nfsvers
+option.
+It is included for compatibility with other operating systems
+.TP 1.5i
+.BR soft " / " softerr " / " hard
+Determines the recovery behavior of the NFS client
+after an NFS request times out.
+If no option is specified (or if the
+.B hard
+option is specified), NFS requests are retried indefinitely.
+If either the
+.BR soft " or " softerr
+option is specified, then the NFS client fails an NFS request
+after
+.B retrans
+retransmissions have been sent,
+causing the NFS client to return either the error
+.B EIO
+(for the
+.B soft
+option) or
+.B ETIMEDOUT
+(for the
+.B softerr
+option) to the calling application.
+.IP
+.I NB:
+A so-called "soft" timeout can cause
+silent data corruption in certain cases. As such, use the
+.BR soft " or " softerr
+option only when client responsiveness
+is more important than data integrity.
+Using NFS over TCP or increasing the value of the
+.B retrans
+option may mitigate some of the risks of using the
+.BR soft " or " softerr
+option.
+.TP 1.5i
+.BR softreval " / " nosoftreval
+In cases where the NFS server is down, it may be useful to
+allow the NFS client to continue to serve up paths and
+attributes from cache after
+.B retrans
+attempts to revalidate that cache have timed out.
+This may, for instance, be helpful when trying to unmount a
+filesystem tree from a server that is permanently down.
+.IP
+It is possible to combine
+.BR softreval
+with the
+.B soft
+mount option, in which case operations that cannot be served up
+from cache will time out and return an error after
+.B retrans
+attempts. The combination with the default
+.B hard
+mount option implies those uncached operations will continue to
+retry until a response is received from the server.
+.IP
+Note: the default mount option is
+.BR nosoftreval
+which disallows fallback to cache when revalidation fails, and
+instead follows the behavior dictated by the
+.B hard
+or
+.B soft
+mount option.
+.TP 1.5i
+.BR intr " / " nointr
+This option is provided for backward compatibility.
+It is ignored after kernel 2.6.25.
+.TP 1.5i
+.BI timeo= n
+The time in deciseconds (tenths of a second) the NFS client waits for a
+response before it retries an NFS request.
+.IP
+For NFS over TCP the default
+.B timeo
+value is 600 (60 seconds).
+The NFS client performs linear backoff: After each retransmission the
+timeout is increased by
+.BR timeo
+up to the maximum of 600 seconds.
+.IP
+However, for NFS over UDP, the client uses an adaptive
+algorithm to estimate an appropriate timeout value for frequently used
+request types (such as READ and WRITE requests), but uses the
+.B timeo
+setting for infrequently used request types (such as FSINFO requests).
+If the
+.B timeo
+option is not specified,
+infrequently used request types are retried after 1.1 seconds.
+After each retransmission, the NFS client doubles the timeout for
+that request,
+up to a maximum timeout length of 60 seconds.
+.TP 1.5i
+.BI retrans= n
+The number of times the NFS client retries a request before
+it attempts further recovery action. If the
+.B retrans
+option is not specified, the NFS client tries each UDP request
+three times and each TCP request twice.
+.IP
+The NFS client generates a "server not responding" message
+after
+.B retrans
+retries, then attempts further recovery (depending on whether the
+.B hard
+mount option is in effect).
+.TP 1.5i
+.BI rsize= n
+The maximum number of bytes in each network READ request
+that the NFS client can receive when reading data from a file
+on an NFS server.
+The actual data payload size of each NFS READ request is equal to
+or smaller than the
+.B rsize
+setting. The largest read payload supported by the Linux NFS client
+is 1,048,576 bytes (one megabyte).
+.IP
+The
+.B rsize
+value is a positive integral multiple of 1024.
+Specified
+.B rsize
+values lower than 1024 are replaced with 4096; values larger than
+1048576 are replaced with 1048576. If a specified value is within the supported
+range but not a multiple of 1024, it is rounded down to the nearest
+multiple of 1024.
+.IP
+If an
+.B rsize
+value is not specified, or if the specified
+.B rsize
+value is larger than the maximum that either client or server can support,
+the client and server negotiate the largest
+.B rsize
+value that they can both support.
+.IP
+The
+.B rsize
+mount option as specified on the
+.BR mount (8)
+command line appears in the
+.I /etc/mtab
+file. However, the effective
+.B rsize
+value negotiated by the client and server is reported in the
+.I /proc/mounts
+file.
+.TP 1.5i
+.BI wsize= n
+The maximum number of bytes per network WRITE request
+that the NFS client can send when writing data to a file
+on an NFS server. The actual data payload size of each
+NFS WRITE request is equal to
+or smaller than the
+.B wsize
+setting. The largest write payload supported by the Linux NFS client
+is 1,048,576 bytes (one megabyte).
+.IP
+Similar to
+.B rsize
+, the
+.B wsize
+value is a positive integral multiple of 1024.
+Specified
+.B wsize
+values lower than 1024 are replaced with 4096; values larger than
+1048576 are replaced with 1048576. If a specified value is within the supported
+range but not a multiple of 1024, it is rounded down to the nearest
+multiple of 1024.
+.IP
+If a
+.B wsize
+value is not specified, or if the specified
+.B wsize
+value is larger than the maximum that either client or server can support,
+the client and server negotiate the largest
+.B wsize
+value that they can both support.
+.IP
+The
+.B wsize
+mount option as specified on the
+.BR mount (8)
+command line appears in the
+.I /etc/mtab
+file. However, the effective
+.B wsize
+value negotiated by the client and server is reported in the
+.I /proc/mounts
+file.
+.TP 1.5i
+.BR ac " / " noac
+Selects whether the client may cache file attributes. If neither
+option is specified (or if
+.B ac
+is specified), the client caches file
+attributes.
+.IP
+To improve performance, NFS clients cache file
+attributes. Every few seconds, an NFS client checks the server's version of each
+file's attributes for updates. Changes that occur on the server in
+those small intervals remain undetected until the client checks the
+server again. The
+.B noac
+option prevents clients from caching file
+attributes so that applications can more quickly detect file changes
+on the server.
+.IP
+In addition to preventing the client from caching file attributes,
+the
+.B noac
+option forces application writes to become synchronous so
+that local changes to a file become visible on the server
+immediately. That way, other clients can quickly detect recent
+writes when they check the file's attributes.
+.IP
+Using the
+.B noac
+option provides greater cache coherence among NFS clients
+accessing the same files,
+but it extracts a significant performance penalty.
+As such, judicious use of file locking is encouraged instead.
+The DATA AND METADATA COHERENCE section contains a detailed discussion
+of these trade-offs.
+.TP 1.5i
+.BI acregmin= n
+The minimum time (in seconds) that the NFS client caches
+attributes of a regular file before it requests
+fresh attribute information from a server.
+If this option is not specified, the NFS client uses
+a 3-second minimum.
+See the DATA AND METADATA COHERENCE section
+for a full discussion of attribute caching.
+.TP 1.5i
+.BI acregmax= n
+The maximum time (in seconds) that the NFS client caches
+attributes of a regular file before it requests
+fresh attribute information from a server.
+If this option is not specified, the NFS client uses
+a 60-second maximum.
+See the DATA AND METADATA COHERENCE section
+for a full discussion of attribute caching.
+.TP 1.5i
+.BI acdirmin= n
+The minimum time (in seconds) that the NFS client caches
+attributes of a directory before it requests
+fresh attribute information from a server.
+If this option is not specified, the NFS client uses
+a 30-second minimum.
+See the DATA AND METADATA COHERENCE section
+for a full discussion of attribute caching.
+.TP 1.5i
+.BI acdirmax= n
+The maximum time (in seconds) that the NFS client caches
+attributes of a directory before it requests
+fresh attribute information from a server.
+If this option is not specified, the NFS client uses
+a 60-second maximum.
+See the DATA AND METADATA COHERENCE section
+for a full discussion of attribute caching.
+.TP 1.5i
+.BI actimeo= n
+Using
+.B actimeo
+sets all of
+.BR acregmin ,
+.BR acregmax ,
+.BR acdirmin ,
+and
+.B acdirmax
+to the same value.
+If this option is not specified, the NFS client uses
+the defaults for each of these options listed above.
+.TP 1.5i
+.BR bg " / " fg
+Determines how the
+.BR mount (8)
+command behaves if an attempt to mount an export fails.
+The
+.B fg
+option causes
+.BR mount (8)
+to exit with an error status if any part of the mount request
+times out or fails outright.
+This is called a "foreground" mount,
+and is the default behavior if neither the
+.B fg
+nor
+.B bg
+mount option is specified.
+.IP
+If the
+.B bg
+option is specified, a timeout or failure causes the
+.BR mount (8)
+command to fork a child which continues to attempt
+to mount the export.
+The parent immediately returns with a zero exit code.
+This is known as a "background" mount.
+.IP
+If the local mount point directory is missing, the
+.BR mount (8)
+command acts as if the mount request timed out.
+This permits nested NFS mounts specified in
+.I /etc/fstab
+to proceed in any order during system initialization,
+even if some NFS servers are not yet available.
+Alternatively these issues can be addressed
+using an automounter (refer to
+.BR automount (8)
+for details).
+.TP 1.5i
+.BR nconnect= n
+When using a connection oriented protocol such as TCP, it may
+sometimes be advantageous to set up multiple connections between
+the client and server. For instance, if your clients and/or servers
+are equipped with multiple network interface cards (NICs), using multiple
+connections to spread the load may improve overall performance.
+In such cases, the
+.BR nconnect
+option allows the user to specify the number of connections
+that should be established between the client and server up to
+a limit of 16.
+.IP
+Note that the
+.BR nconnect
+option may also be used by some pNFS drivers to decide how many
+connections to set up to the data servers.
+.TP 1.5i
+.BR rdirplus " / " nordirplus
+Selects whether to use NFS v3 or v4 READDIRPLUS requests.
+If this option is not specified, the NFS client uses READDIRPLUS requests
+on NFS v3 or v4 mounts to read small directories.
+Some applications perform better if the client uses only READDIR requests
+for all directories.
+.TP 1.5i
+.BI retry= n
+The number of minutes that the
+.BR mount (8)
+command retries an NFS mount operation
+in the foreground or background before giving up.
+If this option is not specified, the default value for foreground mounts
+is 2 minutes, and the default value for background mounts is 10000 minutes
+(80 minutes shy of one week).
+If a value of zero is specified, the
+.BR mount (8)
+command exits immediately after the first failure.
+.IP
+Note that this only affects how many retries are made and doesn't
+affect the delay caused by each retry. For UDP each retry takes the
+time determined by the
+.BR timeo
+and
+.BR retrans
+options, which by default will be about 7 seconds. For TCP the
+default is 3 minutes, but system TCP connection timeouts will
+sometimes limit the timeout of each retransmission to around 2 minutes.
+.TP 1.5i
+.BI sec= flavors
+A colon-separated list of one or more security flavors to use for accessing
+files on the mounted export. If the server does not support any of these
+flavors, the mount operation fails.
+If
+.B sec=
+is not specified, the client attempts to find
+a security flavor that both the client and the server supports.
+Valid
+.I flavors
+are
+.BR none ,
+.BR sys ,
+.BR krb5 ,
+.BR krb5i ,
+and
+.BR krb5p .
+Refer to the SECURITY CONSIDERATIONS section for details.
+.TP 1.5i
+.BR sharecache " / " nosharecache
+Determines how the client's data cache and attribute cache are shared
+when mounting the same export more than once concurrently. Using the
+same cache reduces memory requirements on the client and presents
+identical file contents to applications when the same remote file is
+accessed via different mount points.
+.IP
+If neither option is specified, or if the
+.B sharecache
+option is
+specified, then a single cache is used for all mount points that
+access the same export. If the
+.B nosharecache
+option is specified,
+then that mount point gets a unique cache. Note that when data and
+attribute caches are shared, the mount options from the first mount
+point take effect for subsequent concurrent mounts of the same export.
+.IP
+As of kernel 2.6.18, the behavior specified by
+.B nosharecache
+is legacy caching behavior. This
+is considered a data risk since multiple cached copies
+of the same file on the same client can become out of sync
+following a local update of one of the copies.
+.TP 1.5i
+.BR resvport " / " noresvport
+Specifies whether the NFS client should use a privileged source port
+when communicating with an NFS server for this mount point.
+If this option is not specified, or the
+.B resvport
+option is specified, the NFS client uses a privileged source port.
+If the
+.B noresvport
+option is specified, the NFS client uses a non-privileged source port.
+This option is supported in kernels 2.6.28 and later.
+.IP
+Using non-privileged source ports helps increase the maximum number of
+NFS mount points allowed on a client, but NFS servers must be configured
+to allow clients to connect via non-privileged source ports.
+.IP
+Refer to the SECURITY CONSIDERATIONS section for important details.
+.TP 1.5i
+.BI lookupcache= mode
+Specifies how the kernel manages its cache of directory entries
+for a given mount point.
+.I mode
+can be one of
+.BR all ,
+.BR none ,
+.BR pos ,
+or
+.BR positive .
+This option is supported in kernels 2.6.28 and later.
+.IP
+The Linux NFS client caches the result of all NFS LOOKUP requests.
+If the requested directory entry exists on the server,
+the result is referred to as
+.IR positive .
+If the requested directory entry does not exist on the server,
+the result is referred to as
+.IR negative .
+.IP
+If this option is not specified, or if
+.B all
+is specified, the client assumes both types of directory cache entries
+are valid until their parent directory's cached attributes expire.
+.IP
+If
+.BR pos " or " positive
+is specified, the client assumes positive entries are valid
+until their parent directory's cached attributes expire, but
+always revalidates negative entires before an application
+can use them.
+.IP
+If
+.B none
+is specified,
+the client revalidates both types of directory cache entries
+before an application can use them.
+This permits quick detection of files that were created or removed
+by other clients, but can impact application and server performance.
+.IP
+The DATA AND METADATA COHERENCE section contains a
+detailed discussion of these trade-offs.
+.TP 1.5i
+.BR fsc " / " nofsc
+Enable/Disables the cache of (read-only) data pages to the local disk
+using the FS-Cache facility. See cachefilesd(8)
+and <kernel_source>/Documentation/filesystems/caching
+for detail on how to configure the FS-Cache facility.
+Default value is nofsc.
+.TP 1.5i
+.B sloppy
+The
+.B sloppy
+option is an alternative to specifying
+.BR mount.nfs " -s " option.
+.TP 1.5i
+.BI xprtsec= policy
+Specifies the use of transport layer security to protect NFS network
+traffic on behalf of this mount point.
+.I policy
+can be one of
+.BR none ,
+.BR tls ,
+or
+.BR mtls .
+.IP
+If
+.B none
+is specified,
+transport layer security is forced off, even if the NFS server supports
+transport layer security.
+.IP
+If
+.B tls
+is specified, the client uses RPC-with-TLS to provide in-transit
+confidentiality.
+.IP
+If
+.B mtls
+is specified, the client uses RPC-with-TLS to authenticate itself and
+to provide in-transit confidentiality.
+.IP
+If either
+.B tls
+or
+.B mtls
+is specified and the server does not support RPC-with-TLS or peer
+authentication fails, the mount attempt fails.
+.IP
+If the
+.B xprtsec=
+option is not specified,
+the default behavior depends on the kernel version,
+but is usually equivalent to
+.BR "xprtsec=none" .
+.SS "Options for NFS versions 2 and 3 only"
+Use these options, along with the options in the above subsection,
+for NFS versions 2 and 3 only.
+.TP 1.5i
+.BI proto= netid
+The
+.I netid
+determines the transport that is used to communicate with the NFS
+server. Available options are
+.BR udp ", " udp6 ", "tcp ", " tcp6 ", " rdma ", and " rdma6 .
+Those which end in
+.B 6
+use IPv6 addresses and are only available if support for TI-RPC is
+built in. Others use IPv4 addresses.
+.IP
+Each transport protocol uses different default
+.B retrans
+and
+.B timeo
+settings.
+Refer to the description of these two mount options for details.
+.IP
+In addition to controlling how the NFS client transmits requests to
+the server, this mount option also controls how the
+.BR mount (8)
+command communicates with the server's rpcbind and mountd services.
+Specifying a netid that uses TCP forces all traffic from the
+.BR mount (8)
+command and the NFS client to use TCP.
+Specifying a netid that uses UDP forces all traffic types to use UDP.
+.IP
+.B Before using NFS over UDP, refer to the TRANSPORT METHODS section.
+.IP
+If the
+.B proto
+mount option is not specified, the
+.BR mount (8)
+command discovers which protocols the server supports
+and chooses an appropriate transport for each service.
+Refer to the TRANSPORT METHODS section for more details.
+.TP 1.5i
+.B udp
+The
+.B udp
+option is an alternative to specifying
+.BR proto=udp.
+It is included for compatibility with other operating systems.
+.IP
+.B Before using NFS over UDP, refer to the TRANSPORT METHODS section.
+.TP 1.5i
+.B tcp
+The
+.B tcp
+option is an alternative to specifying
+.BR proto=tcp.
+It is included for compatibility with other operating systems.
+.TP 1.5i
+.B rdma
+The
+.B rdma
+option is an alternative to specifying
+.BR proto=rdma.
+.TP 1.5i
+.BI port= n
+The numeric value of the server's NFS service port.
+If the server's NFS service is not available on the specified port,
+the mount request fails.
+.IP
+If this option is not specified, or if the specified port value is 0,
+then the NFS client uses the NFS service port number
+advertised by the server's rpcbind service.
+The mount request fails if the server's rpcbind service is not available,
+the server's NFS service is not registered with its rpcbind service,
+or the server's NFS service is not available on the advertised port.
+.TP 1.5i
+.BI mountport= n
+The numeric value of the server's mountd port.
+If the server's mountd service is not available on the specified port,
+the mount request fails.
+.IP
+If this option is not specified,
+or if the specified port value is 0, then the
+.BR mount (8)
+command uses the mountd service port number
+advertised by the server's rpcbind service.
+The mount request fails if the server's rpcbind service is not available,
+the server's mountd service is not registered with its rpcbind service,
+or the server's mountd service is not available on the advertised port.
+.IP
+This option can be used when mounting an NFS server
+through a firewall that blocks the rpcbind protocol.
+.TP 1.5i
+.BI mountproto= netid
+The transport the NFS client uses
+to transmit requests to the NFS server's mountd service when performing
+this mount request, and when later unmounting this mount point.
+.IP
+.I netid
+may be one of
+.BR udp ", and " tcp
+which use IPv4 address or, if TI-RPC is built into the
+.B mount.nfs
+command,
+.BR udp6 ", and " tcp6
+which use IPv6 addresses.
+.IP
+This option can be used when mounting an NFS server
+through a firewall that blocks a particular transport.
+When used in combination with the
+.B proto
+option, different transports for mountd requests and NFS requests
+can be specified.
+If the server's mountd service is not available via the specified
+transport, the mount request fails.
+.IP
+Refer to the TRANSPORT METHODS section for more on how the
+.B mountproto
+mount option interacts with the
+.B proto
+mount option.
+.TP 1.5i
+.BI mounthost= name
+The hostname of the host running mountd.
+If this option is not specified, the
+.BR mount (8)
+command assumes that the mountd service runs
+on the same host as the NFS service.
+.TP 1.5i
+.BI mountvers= n
+The RPC version number used to contact the server's mountd.
+If this option is not specified, the client uses a version number
+appropriate to the requested NFS version.
+This option is useful when multiple NFS services
+are running on the same remote server host.
+.TP 1.5i
+.BI namlen= n
+The maximum length of a pathname component on this mount.
+If this option is not specified, the maximum length is negotiated
+with the server. In most cases, this maximum length is 255 characters.
+.IP
+Some early versions of NFS did not support this negotiation.
+Using this option ensures that
+.BR pathconf (3)
+reports the proper maximum component length to applications
+in such cases.
+.TP 1.5i
+.BR lock " / " nolock
+Selects whether to use the NLM sideband protocol to lock files on the server.
+If neither option is specified (or if
+.B lock
+is specified), NLM locking is used for this mount point.
+When using the
+.B nolock
+option, applications can lock files,
+but such locks provide exclusion only against other applications
+running on the same client.
+Remote applications are not affected by these locks.
+.IP
+NLM locking must be disabled with the
+.B nolock
+option when using NFS to mount
+.I /var
+because
+.I /var
+contains files used by the NLM implementation on Linux.
+Using the
+.B nolock
+option is also required when mounting exports on NFS servers
+that do not support the NLM protocol.
+.TP 1.5i
+.BR cto " / " nocto
+Selects whether to use close-to-open cache coherence semantics.
+If neither option is specified (or if
+.B cto
+is specified), the client uses close-to-open
+cache coherence semantics. If the
+.B nocto
+option is specified, the client uses a non-standard heuristic to determine when
+files on the server have changed.
+.IP
+Using the
+.B nocto
+option may improve performance for read-only mounts,
+but should be used only if the data on the server changes only occasionally.
+The DATA AND METADATA COHERENCE section discusses the behavior
+of this option in more detail.
+.TP 1.5i
+.BR acl " / " noacl
+Selects whether to use the NFSACL sideband protocol on this mount point.
+The NFSACL sideband protocol is a proprietary protocol
+implemented in Solaris that manages Access Control Lists. NFSACL was never
+made a standard part of the NFS protocol specification.
+.IP
+If neither
+.B acl
+nor
+.B noacl
+option is specified,
+the NFS client negotiates with the server
+to see if the NFSACL protocol is supported,
+and uses it if the server supports it.
+Disabling the NFSACL sideband protocol may be necessary
+if the negotiation causes problems on the client or server.
+Refer to the SECURITY CONSIDERATIONS section for more details.
+.TP 1.5i
+.BR local_lock= mechanism
+Specifies whether to use local locking for any or both of the flock and the
+POSIX locking mechanisms.
+.I mechanism
+can be one of
+.BR all ,
+.BR flock ,
+.BR posix ,
+or
+.BR none .
+This option is supported in kernels 2.6.37 and later.
+.IP
+The Linux NFS client provides a way to make locks local. This means, the
+applications can lock files, but such locks provide exclusion only against
+other applications running on the same client. Remote applications are not
+affected by these locks.
+.IP
+If this option is not specified, or if
+.B none
+is specified, the client assumes that the locks are not local.
+.IP
+If
+.BR all
+is specified, the client assumes that both flock and POSIX locks are local.
+.IP
+If
+.BR flock
+is specified, the client assumes that only flock locks are local and uses
+NLM sideband protocol to lock files when POSIX locks are used.
+.IP
+If
+.BR posix
+is specified, the client assumes that POSIX locks are local and uses NLM
+sideband protocol to lock files when flock locks are used.
+.IP
+To support legacy flock behavior similar to that of NFS clients < 2.6.12,
+use 'local_lock=flock'. This option is required when exporting NFS mounts via
+Samba as Samba maps Windows share mode locks as flock. Since NFS clients >
+2.6.12 implement flock by emulating POSIX locks, this will result in
+conflicting locks.
+.IP
+NOTE: When used together, the 'local_lock' mount option will be overridden
+by 'nolock'/'lock' mount option.
+.SS "Options for NFS version 4 only"
+Use these options, along with the options in the first subsection above,
+for NFS version 4.0 and newer.
+.TP 1.5i
+.BI proto= netid
+The
+.I netid
+determines the transport that is used to communicate with the NFS
+server. Supported options are
+.BR tcp ", " tcp6 ", " rdma ", and " rdma6 .
+.B tcp6
+use IPv6 addresses and is only available if support for TI-RPC is
+built in. Both others use IPv4 addresses.
+.IP
+All NFS version 4 servers are required to support TCP,
+so if this mount option is not specified, the NFS version 4 client
+uses the TCP protocol.
+Refer to the TRANSPORT METHODS section for more details.
+.TP 1.5i
+.BI minorversion= n
+Specifies the protocol minor version number.
+NFSv4 introduces "minor versioning," where NFS protocol enhancements can
+be introduced without bumping the NFS protocol version number.
+Before kernel 2.6.38, the minor version is always zero, and this
+option is not recognized.
+After this kernel, specifying "minorversion=1" enables a number of
+advanced features, such as NFSv4 sessions.
+.IP
+Recent kernels allow the minor version to be specified using the
+.B vers=
+option.
+For example, specifying
+.B vers=4.1
+is the same as specifying
+.BR vers=4,minorversion=1 .
+.TP 1.5i
+.BI port= n
+The numeric value of the server's NFS service port.
+If the server's NFS service is not available on the specified port,
+the mount request fails.
+.IP
+If this mount option is not specified,
+the NFS client uses the standard NFS port number of 2049
+without first checking the server's rpcbind service.
+This allows an NFS version 4 client to contact an NFS version 4
+server through a firewall that may block rpcbind requests.
+.IP
+If the specified port value is 0,
+then the NFS client uses the NFS service port number
+advertised by the server's rpcbind service.
+The mount request fails if the server's rpcbind service is not available,
+the server's NFS service is not registered with its rpcbind service,
+or the server's NFS service is not available on the advertised port.
+.TP 1.5i
+.BR cto " / " nocto
+Selects whether to use close-to-open cache coherence semantics
+for NFS directories on this mount point.
+If neither
+.B cto
+nor
+.B nocto
+is specified,
+the default is to use close-to-open cache coherence
+semantics for directories.
+.IP
+File data caching behavior is not affected by this option.
+The DATA AND METADATA COHERENCE section discusses
+the behavior of this option in more detail.
+.TP 1.5i
+.BI clientaddr= n.n.n.n
+.TP 1.5i
+.BI clientaddr= n:n: ... :n
+Specifies a single IPv4 address (in dotted-quad form),
+or a non-link-local IPv6 address,
+that the NFS client advertises to allow servers
+to perform NFS version 4.0 callback requests against
+files on this mount point. If the server is unable to
+establish callback connections to clients, performance
+may degrade, or accesses to files may temporarily hang.
+Can specify a value of IPv4_ANY (0.0.0.0) or equivalent
+IPv6 any address which will signal to the NFS server that
+this NFS client does not want delegations.
+.IP
+If this option is not specified, the
+.BR mount (8)
+command attempts to discover an appropriate callback address automatically.
+The automatic discovery process is not perfect, however.
+In the presence of multiple client network interfaces,
+special routing policies,
+or atypical network topologies,
+the exact address to use for callbacks may be nontrivial to determine.
+.IP
+NFS protocol versions 4.1 and 4.2 use the client-established
+TCP connection for callback requests, so do not require the server to
+connect to the client. This option is therefore only affect NFS version
+4.0 mounts.
+.TP 1.5i
+.BR migration " / " nomigration
+Selects whether the client uses an identification string that is compatible
+with NFSv4 Transparent State Migration (TSM).
+If the mounted server supports NFSv4 migration with TSM, specify the
+.B migration
+option.
+.IP
+Some server features misbehave in the face of a migration-compatible
+identification string.
+The
+.B nomigration
+option retains the use of a traditional client indentification string
+which is compatible with legacy NFS servers.
+This is also the behavior if neither option is specified.
+A client's open and lock state cannot be migrated transparently
+when it identifies itself via a traditional identification string.
+.IP
+This mount option has no effect with NFSv4 minor versions newer than zero,
+which always use TSM-compatible client identification strings.
+.TP 1.5i
+.BR max_connect= n
+While
+.BR nconnect
+option sets a limit on the number of connections that can be established
+to a given server IP,
+.BR max_connect
+option allows the user to specify maximum number of connections to different
+server IPs that belong to the same NFSv4.1+ server (session trunkable
+connections) up to a limit of 16. When client discovers that it established
+a client ID to an already existing server, instead of dropping the newly
+created network transport, the client will add this new connection to the
+list of available transports for that RPC client.
+.TP 1.5i
+.BR trunkdiscovery " / " notrunkdiscovery
+When the client discovers a new filesystem on a NFSv4.1+ server, the
+.BR trunkdiscovery
+mount option will cause it to send a GETATTR for the fs_locations attribute.
+If is receives a non-zero length reply, it will iterate through the response,
+and for each server location it will establish a connection, send an
+EXCHANGE_ID, and test for session trunking. If the trunking test succeeds,
+the connection will be added to the existing set of transports for the server,
+subject to the limit specified by the
+.BR max_connect
+option. The default is
+.BR notrunkdiscovery .
+.SH nfs4 FILE SYSTEM TYPE
+The
+.BR nfs4
+file system type is an old syntax for specifying NFSv4 usage. It can still
+be used with all NFSv4-specific and common options, excepted the
+.B nfsvers
+mount option.
+.SH MOUNT CONFIGURATION FILE
+If the mount command is configured to do so, all of the mount options
+described in the previous section can also be configured in the
+.I /etc/nfsmount.conf
+file. See
+.BR nfsmount.conf(5)
+for details.
+.SH EXAMPLES
+To mount using NFS version 3,
+use the
+.B nfs
+file system type and specify the
+.B nfsvers=3
+mount option.
+To mount using NFS version 4,
+use either the
+.B nfs
+file system type, with the
+.B nfsvers=4
+mount option, or the
+.B nfs4
+file system type.
+.P
+The following example from an
+.I /etc/fstab
+file causes the mount command to negotiate
+reasonable defaults for NFS behavior.
+.P
+.nf
+.ta 8n +16n +6n +6n +30n
+ server:/export /mnt nfs defaults 0 0
+.fi
+.P
+This example shows how to mount using NFS version 4 over TCP
+with Kerberos 5 mutual authentication.
+.P
+.nf
+.ta 8n +16n +6n +6n +30n
+ server:/export /mnt nfs4 sec=krb5 0 0
+.fi
+.P
+This example shows how to mount using NFS version 4 over TCP
+with Kerberos 5 privacy or data integrity mode.
+.P
+.nf
+.ta 8n +16n +6n +6n +30n
+ server:/export /mnt nfs4 sec=krb5p:krb5i 0 0
+.fi
+.P
+This example can be used to mount /usr over NFS.
+.P
+.nf
+.ta 8n +16n +6n +6n +30n
+ server:/export /usr nfs ro,nolock,nocto,actimeo=3600 0 0
+.fi
+.P
+This example shows how to mount an NFS server
+using a raw IPv6 link-local address.
+.P
+.nf
+.ta 8n +40n +5n +4n +9n
+ [fe80::215:c5ff:fb3e:e2b1%eth0]:/export /mnt nfs defaults 0 0
+.fi
+.SH "TRANSPORT METHODS"
+NFS clients send requests to NFS servers via
+Remote Procedure Calls, or
+.IR RPCs .
+The RPC client discovers remote service endpoints automatically,
+handles per-request authentication,
+adjusts request parameters for different byte endianness on client and server,
+and retransmits requests that may have been lost by the network or server.
+RPC requests and replies flow over a network transport.
+.P
+In most cases, the
+.BR mount (8)
+command, NFS client, and NFS server
+can automatically negotiate proper transport
+and data transfer size settings for a mount point.
+In some cases, however, it pays to specify
+these settings explicitly using mount options.
+.P
+Traditionally, NFS clients used the UDP transport exclusively for
+transmitting requests to servers. Though its implementation is
+simple, NFS over UDP has many limitations that prevent smooth
+operation and good performance in some common deployment
+environments. Even an insignificant packet loss rate results in the
+loss of whole NFS requests; as such, retransmit timeouts are usually
+in the subsecond range to allow clients to recover quickly from
+dropped requests, but this can result in extraneous network traffic
+and server load.
+.P
+However, UDP can be quite effective in specialized settings where
+the networks MTU is large relative to NFSs data transfer size (such
+as network environments that enable jumbo Ethernet frames). In such
+environments, trimming the
+.B rsize
+and
+.B wsize
+settings so that each
+NFS read or write request fits in just a few network frames (or even
+in a single frame) is advised. This reduces the probability that
+the loss of a single MTU-sized network frame results in the loss of
+an entire large read or write request.
+.P
+TCP is the default transport protocol used for all modern NFS
+implementations. It performs well in almost every conceivable
+network environment and provides excellent guarantees against data
+corruption caused by network unreliability. TCP is often a
+requirement for mounting a server through a network firewall.
+.P
+Under normal circumstances, networks drop packets much more
+frequently than NFS servers drop requests. As such, an aggressive
+retransmit timeout setting for NFS over TCP is unnecessary. Typical
+timeout settings for NFS over TCP are between one and ten minutes.
+After the client exhausts its retransmits (the value of the
+.B retrans
+mount option), it assumes a network partition has occurred,
+and attempts to reconnect to the server on a fresh socket. Since
+TCP itself makes network data transfer reliable,
+.B rsize
+and
+.B wsize
+can safely be allowed to default to the largest values supported by
+both client and server, independent of the network's MTU size.
+.SS "Using the mountproto mount option"
+This section applies only to NFS version 3 mounts
+since NFS version 4 does not use a separate protocol for mount
+requests.
+.P
+The Linux NFS client can use a different transport for
+contacting an NFS server's rpcbind service, its mountd service,
+its Network Lock Manager (NLM) service, and its NFS service.
+The exact transports employed by the Linux NFS client for
+each mount point depends on the settings of the transport
+mount options, which include
+.BR proto ,
+.BR mountproto ,
+.BR udp ", and " tcp .
+.P
+The client sends Network Status Manager (NSM) notifications
+via UDP no matter what transport options are specified, but
+listens for server NSM notifications on both UDP and TCP.
+The NFS Access Control List (NFSACL) protocol shares the same
+transport as the main NFS service.
+.P
+If no transport options are specified, the Linux NFS client
+uses UDP to contact the server's mountd service, and TCP to
+contact its NLM and NFS services by default.
+.P
+If the server does not support these transports for these services, the
+.BR mount (8)
+command attempts to discover what the server supports, and then retries
+the mount request once using the discovered transports.
+If the server does not advertise any transport supported by the client
+or is misconfigured, the mount request fails.
+If the
+.B bg
+option is in effect, the mount command backgrounds itself and continues
+to attempt the specified mount request.
+.P
+When the
+.B proto
+option, the
+.B udp
+option, or the
+.B tcp
+option is specified but the
+.B mountproto
+option is not, the specified transport is used to contact
+both the server's mountd service and for the NLM and NFS services.
+.P
+If the
+.B mountproto
+option is specified but none of the
+.BR proto ", " udp " or " tcp
+options are specified, then the specified transport is used for the
+initial mountd request, but the mount command attempts to discover
+what the server supports for the NFS protocol, preferring TCP if
+both transports are supported.
+.P
+If both the
+.BR mountproto " and " proto
+(or
+.BR udp " or " tcp )
+options are specified, then the transport specified by the
+.B mountproto
+option is used for the initial mountd request, and the transport
+specified by the
+.B proto
+option (or the
+.BR udp " or " tcp " options)"
+is used for NFS, no matter what order these options appear.
+No automatic service discovery is performed if these options are
+specified.
+.P
+If any of the
+.BR proto ", " udp ", " tcp ", "
+or
+.B mountproto
+options are specified more than once on the same mount command line,
+then the value of the rightmost instance of each of these options
+takes effect.
+.SS "Using NFS over UDP on high-speed links"
+Using NFS over UDP on high-speed links such as Gigabit
+.BR "can cause silent data corruption" .
+.P
+The problem can be triggered at high loads, and is caused by problems in
+IP fragment reassembly. NFS read and writes typically transmit UDP packets
+of 4 Kilobytes or more, which have to be broken up into several fragments
+in order to be sent over the Ethernet link, which limits packets to 1500
+bytes by default. This process happens at the IP network layer and is
+called fragmentation.
+.P
+In order to identify fragments that belong together, IP assigns a 16bit
+.I IP ID
+value to each packet; fragments generated from the same UDP packet
+will have the same IP ID. The receiving system will collect these
+fragments and combine them to form the original UDP packet. This process
+is called reassembly. The default timeout for packet reassembly is
+30 seconds; if the network stack does not receive all fragments of
+a given packet within this interval, it assumes the missing fragment(s)
+got lost and discards those it already received.
+.P
+The problem this creates over high-speed links is that it is possible
+to send more than 65536 packets within 30 seconds. In fact, with
+heavy NFS traffic one can observe that the IP IDs repeat after about
+5 seconds.
+.P
+This has serious effects on reassembly: if one fragment gets lost,
+another fragment
+.I from a different packet
+but with the
+.I same IP ID
+will arrive within the 30 second timeout, and the network stack will
+combine these fragments to form a new packet. Most of the time, network
+layers above IP will detect this mismatched reassembly - in the case
+of UDP, the UDP checksum, which is a 16 bit checksum over the entire
+packet payload, will usually not match, and UDP will discard the
+bad packet.
+.P
+However, the UDP checksum is 16 bit only, so there is a chance of 1 in
+65536 that it will match even if the packet payload is completely
+random (which very often isn't the case). If that is the case,
+silent data corruption will occur.
+.P
+This potential should be taken seriously, at least on Gigabit
+Ethernet.
+Network speeds of 100Mbit/s should be considered less
+problematic, because with most traffic patterns IP ID wrap around
+will take much longer than 30 seconds.
+.P
+It is therefore strongly recommended to use
+.BR "NFS over TCP where possible" ,
+since TCP does not perform fragmentation.
+.P
+If you absolutely have to use NFS over UDP over Gigabit Ethernet,
+some steps can be taken to mitigate the problem and reduce the
+probability of corruption:
+.TP +1.5i
+.I Jumbo frames:
+Many Gigabit network cards are capable of transmitting
+frames bigger than the 1500 byte limit of traditional Ethernet, typically
+9000 bytes. Using jumbo frames of 9000 bytes will allow you to run NFS over
+UDP at a page size of 8K without fragmentation. Of course, this is
+only feasible if all involved stations support jumbo frames.
+.IP
+To enable a machine to send jumbo frames on cards that support it,
+it is sufficient to configure the interface for a MTU value of 9000.
+.TP +1.5i
+.I Lower reassembly timeout:
+By lowering this timeout below the time it takes the IP ID counter
+to wrap around, incorrect reassembly of fragments can be prevented
+as well. To do so, simply write the new timeout value (in seconds)
+to the file
+.BR /proc/sys/net/ipv4/ipfrag_time .
+.IP
+A value of 2 seconds will greatly reduce the probability of IPID clashes on
+a single Gigabit link, while still allowing for a reasonable timeout
+when receiving fragmented traffic from distant peers.
+.SH "DATA AND METADATA COHERENCE"
+Some modern cluster file systems provide
+perfect cache coherence among their clients.
+Perfect cache coherence among disparate NFS clients
+is expensive to achieve, especially on wide area networks.
+As such, NFS settles for weaker cache coherence that
+satisfies the requirements of most file sharing types.
+.SS "Close-to-open cache consistency"
+Typically file sharing is completely sequential.
+First client A opens a file, writes something to it, then closes it.
+Then client B opens the same file, and reads the changes.
+.P
+When an application opens a file stored on an NFS version 3 server,
+the NFS client checks that the file exists on the server
+and is permitted to the opener by sending a GETATTR or ACCESS request.
+The NFS client sends these requests
+regardless of the freshness of the file's cached attributes.
+.P
+When the application closes the file,
+the NFS client writes back any pending changes
+to the file so that the next opener can view the changes.
+This also gives the NFS client an opportunity to report
+write errors to the application via the return code from
+.BR close (2).
+.P
+The behavior of checking at open time and flushing at close time
+is referred to as
+.IR "close-to-open cache consistency" ,
+or
+.IR CTO .
+It can be disabled for an entire mount point using the
+.B nocto
+mount option.
+.SS "Weak cache consistency"
+There are still opportunities for a client's data cache
+to contain stale data.
+The NFS version 3 protocol introduced "weak cache consistency"
+(also known as WCC) which provides a way of efficiently checking
+a file's attributes before and after a single request.
+This allows a client to help identify changes
+that could have been made by other clients.
+.P
+When a client is using many concurrent operations
+that update the same file at the same time
+(for example, during asynchronous write behind),
+it is still difficult to tell whether it was
+that client's updates or some other client's updates
+that altered the file.
+.SS "Attribute caching"
+Use the
+.B noac
+mount option to achieve attribute cache coherence
+among multiple clients.
+Almost every file system operation checks
+file attribute information.
+The client keeps this information cached
+for a period of time to reduce network and server load.
+When
+.B noac
+is in effect, a client's file attribute cache is disabled,
+so each operation that needs to check a file's attributes
+is forced to go back to the server.
+This permits a client to see changes to a file very quickly,
+at the cost of many extra network operations.
+.P
+Be careful not to confuse the
+.B noac
+option with "no data caching."
+The
+.B noac
+mount option prevents the client from caching file metadata,
+but there are still races that may result in data cache incoherence
+between client and server.
+.P
+The NFS protocol is not designed to support
+true cluster file system cache coherence
+without some type of application serialization.
+If absolute cache coherence among clients is required,
+applications should use file locking. Alternatively, applications
+can also open their files with the O_DIRECT flag
+to disable data caching entirely.
+.SS "File timestamp maintenance"
+NFS servers are responsible for managing file and directory timestamps
+.RB ( atime ,
+.BR ctime ", and"
+.BR mtime ).
+When a file is accessed or updated on an NFS server,
+the file's timestamps are updated just like they would be on a filesystem
+local to an application.
+.P
+NFS clients cache file attributes, including timestamps.
+A file's timestamps are updated on NFS clients when its attributes
+are retrieved from the NFS server.
+Thus there may be some delay before timestamp updates
+on an NFS server appear to applications on NFS clients.
+.P
+To comply with the POSIX filesystem standard, the Linux NFS client
+relies on NFS servers to keep a file's
+.B mtime
+and
+.B ctime
+timestamps properly up to date.
+It does this by flushing local data changes to the server
+before reporting
+.B mtime
+to applications via system calls such as
+.BR stat (2).
+.P
+The Linux client handles
+.B atime
+updates more loosely, however.
+NFS clients maintain good performance by caching data,
+but that means that application reads, which normally update
+.BR atime ,
+are not reflected to the server where a file's
+.B atime
+is actually maintained.
+.P
+Because of this caching behavior,
+the Linux NFS client does not support generic atime-related mount options.
+See
+.BR mount (8)
+for details on these options.
+.P
+In particular, the
+.BR atime / noatime ,
+.BR diratime / nodiratime ,
+.BR relatime / norelatime ,
+and
+.BR strictatime / nostrictatime
+mount options have no effect on NFS mounts.
+.P
+.I /proc/mounts
+may report that the
+.B relatime
+mount option is set on NFS mounts, but in fact the
+.B atime
+semantics are always as described here, and are not like
+.B relatime
+semantics.
+.SS "Directory entry caching"
+The Linux NFS client caches the result of all NFS LOOKUP requests.
+If the requested directory entry exists on the server,
+the result is referred to as a
+.IR positive " lookup result.
+If the requested directory entry does not exist on the server
+(that is, the server returned ENOENT),
+the result is referred to as
+.IR negative " lookup result.
+.P
+To detect when directory entries have been added or removed
+on the server,
+the Linux NFS client watches a directory's mtime.
+If the client detects a change in a directory's mtime,
+the client drops all cached LOOKUP results for that directory.
+Since the directory's mtime is a cached attribute, it may
+take some time before a client notices it has changed.
+See the descriptions of the
+.BR acdirmin ", " acdirmax ", and " noac
+mount options for more information about
+how long a directory's mtime is cached.
+.P
+Caching directory entries improves the performance of applications that
+do not share files with applications on other clients.
+Using cached information about directories can interfere
+with applications that run concurrently on multiple clients and
+need to detect the creation or removal of files quickly, however.
+The
+.B lookupcache
+mount option allows some tuning of directory entry caching behavior.
+.P
+Before kernel release 2.6.28,
+the Linux NFS client tracked only positive lookup results.
+This permitted applications to detect new directory entries
+created by other clients quickly while still providing some of the
+performance benefits of caching.
+If an application depends on the previous lookup caching behavior
+of the Linux NFS client, you can use
+.BR lookupcache=positive .
+.P
+If the client ignores its cache and validates every application
+lookup request with the server,
+that client can immediately detect when a new directory
+entry has been either created or removed by another client.
+You can specify this behavior using
+.BR lookupcache=none .
+The extra NFS requests needed if the client does not
+cache directory entries can exact a performance penalty.
+Disabling lookup caching
+should result in less of a performance penalty than using
+.BR noac ,
+and has no effect on how the NFS client caches the attributes of files.
+.P
+.SS "The sync mount option"
+The NFS client treats the
+.B sync
+mount option differently than some other file systems
+(refer to
+.BR mount (8)
+for a description of the generic
+.B sync
+and
+.B async
+mount options).
+If neither
+.B sync
+nor
+.B async
+is specified (or if the
+.B async
+option is specified),
+the NFS client delays sending application
+writes to the server
+until any of these events occur:
+.IP
+Memory pressure forces reclamation of system memory resources.
+.IP
+An application flushes file data explicitly with
+.BR sync (2),
+.BR msync (2),
+or
+.BR fsync (3).
+.IP
+An application closes a file with
+.BR close (2).
+.IP
+The file is locked/unlocked via
+.BR fcntl (2).
+.P
+In other words, under normal circumstances,
+data written by an application may not immediately appear
+on the server that hosts the file.
+.P
+If the
+.B sync
+option is specified on a mount point,
+any system call that writes data to files on that mount point
+causes that data to be flushed to the server
+before the system call returns control to user space.
+This provides greater data cache coherence among clients,
+but at a significant performance cost.
+.P
+Applications can use the O_SYNC open flag to force application
+writes to individual files to go to the server immediately without
+the use of the
+.B sync
+mount option.
+.SS "Using file locks with NFS"
+The Network Lock Manager protocol is a separate sideband protocol
+used to manage file locks in NFS version 3.
+To support lock recovery after a client or server reboot,
+a second sideband protocol --
+known as the Network Status Manager protocol --
+is also required.
+In NFS version 4,
+file locking is supported directly in the main NFS protocol,
+and the NLM and NSM sideband protocols are not used.
+.P
+In most cases, NLM and NSM services are started automatically,
+and no extra configuration is required.
+Configure all NFS clients with fully-qualified domain names
+to ensure that NFS servers can find clients to notify them of server reboots.
+.P
+NLM supports advisory file locks only.
+To lock NFS files, use
+.BR fcntl (2)
+with the F_GETLK and F_SETLK commands.
+The NFS client converts file locks obtained via
+.BR flock (2)
+to advisory locks.
+.P
+When mounting servers that do not support the NLM protocol,
+or when mounting an NFS server through a firewall
+that blocks the NLM service port,
+specify the
+.B nolock
+mount option. NLM locking must be disabled with the
+.B nolock
+option when using NFS to mount
+.I /var
+because
+.I /var
+contains files used by the NLM implementation on Linux.
+.P
+Specifying the
+.B nolock
+option may also be advised to improve the performance
+of a proprietary application which runs on a single client
+and uses file locks extensively.
+.SS "NFS version 4 caching features"
+The data and metadata caching behavior of NFS version 4
+clients is similar to that of earlier versions.
+However, NFS version 4 adds two features that improve
+cache behavior:
+.I change attributes
+and
+.IR "file delegation" .
+.P
+The
+.I change attribute
+is a new part of NFS file and directory metadata
+which tracks data changes.
+It replaces the use of a file's modification
+and change time stamps
+as a way for clients to validate the content
+of their caches.
+Change attributes are independent of the time stamp
+resolution on either the server or client, however.
+.P
+A
+.I file delegation
+is a contract between an NFS version 4 client
+and server that allows the client to treat a file temporarily
+as if no other client is accessing it.
+The server promises to notify the client (via a callback request) if another client
+attempts to access that file.
+Once a file has been delegated to a client, the client can
+cache that file's data and metadata aggressively without
+contacting the server.
+.P
+File delegations come in two flavors:
+.I read
+and
+.IR write .
+A
+.I read
+delegation means that the server notifies the client
+about any other clients that want to write to the file.
+A
+.I write
+delegation means that the client gets notified about
+either read or write accessors.
+.P
+Servers grant file delegations when a file is opened,
+and can recall delegations at any time when another
+client wants access to the file that conflicts with
+any delegations already granted.
+Delegations on directories are not supported.
+.P
+In order to support delegation callback, the server
+checks the network return path to the client during
+the client's initial contact with the server.
+If contact with the client cannot be established,
+the server simply does not grant any delegations to
+that client.
+.SH "SECURITY CONSIDERATIONS"
+NFS servers control access to file data,
+but they depend on their RPC implementation
+to provide authentication of NFS requests.
+Traditional NFS access control mimics
+the standard mode bit access control provided in local file systems.
+Traditional RPC authentication uses a number
+to represent each user
+(usually the user's own uid),
+a number to represent the user's group (the user's gid),
+and a set of up to 16 auxiliary group numbers
+to represent other groups of which the user may be a member.
+.P
+Typically, file data and user ID values appear unencrypted
+(i.e. "in the clear") on the network.
+Moreover, NFS versions 2 and 3 use
+separate sideband protocols for mounting,
+locking and unlocking files,
+and reporting system status of clients and servers.
+These auxiliary protocols use no authentication.
+.P
+In addition to combining these sideband protocols with the main NFS protocol,
+NFS version 4 introduces more advanced forms of access control,
+authentication, and in-transit data protection.
+The NFS version 4 specification mandates support for
+strong authentication and security flavors
+that provide per-RPC integrity checking and encryption.
+Because NFS version 4 combines the
+function of the sideband protocols into the main NFS protocol,
+the new security features apply to all NFS version 4 operations
+including mounting, file locking, and so on.
+RPCGSS authentication can also be used with NFS versions 2 and 3,
+but it does not protect their sideband protocols.
+.P
+The
+.B sec
+mount option specifies the security flavor used for operations
+on behalf of users on that NFS mount point.
+Specifying
+.B sec=krb5
+provides cryptographic proof of a user's identity in each RPC request.
+This provides strong verification of the identity of users
+accessing data on the server.
+Note that additional configuration besides adding this mount option
+is required in order to enable Kerberos security.
+Refer to the
+.BR rpc.gssd (8)
+man page for details.
+.P
+Two additional flavors of Kerberos security are supported:
+.B krb5i
+and
+.BR krb5p .
+The
+.B krb5i
+security flavor provides a cryptographically strong guarantee
+that the data in each RPC request has not been tampered with.
+The
+.B krb5p
+security flavor encrypts every RPC request
+to prevent data exposure during network transit; however,
+expect some performance impact
+when using integrity checking or encryption.
+Similar support for other forms of cryptographic security
+is also available.
+.SS "NFS version 4 filesystem crossing"
+The NFS version 4 protocol allows
+a client to renegotiate the security flavor
+when the client crosses into a new filesystem on the server.
+The newly negotiated flavor effects only accesses of the new filesystem.
+.P
+Such negotiation typically occurs when a client crosses
+from a server's pseudo-fs
+into one of the server's exported physical filesystems,
+which often have more restrictive security settings than the pseudo-fs.
+.SS "NFS version 4 Leases"
+In NFS version 4, a lease is a period during which a server
+irrevocably grants a client file locks.
+Once the lease expires, the server may revoke those locks.
+Clients periodically renew their leases to prevent lock revocation.
+.P
+After an NFS version 4 server reboots, each client tells the
+server about existing file open and lock state under its lease
+before operation can continue.
+If a client reboots, the server frees all open and lock state
+associated with that client's lease.
+.P
+When establishing a lease, therefore,
+a client must identify itself to a server.
+Each client presents an arbitrary string
+to distinguish itself from other clients.
+The client administrator can
+supplement the default identity string using the
+.I nfs4.nfs4_unique_id
+module parameter to avoid collisions
+with other client identity strings.
+.P
+A client also uses a unique security flavor and principal
+when it establishes its lease.
+If two clients present the same identity string,
+a server can use client principals to distinguish between them,
+thus securely preventing one client from interfering with the other's lease.
+.P
+The Linux NFS client establishes one lease on each NFS version 4 server.
+Lease management operations, such as lease renewal, are not
+done on behalf of a particular file, lock, user, or mount
+point, but on behalf of the client that owns that lease.
+A client uses a consistent identity string, security flavor,
+and principal across client reboots to ensure that the server
+can promptly reap expired lease state.
+.P
+When Kerberos is configured on a Linux NFS client
+(i.e., there is a
+.I /etc/krb5.keytab
+on that client), the client attempts to use a Kerberos
+security flavor for its lease management operations.
+Kerberos provides secure authentication of each client.
+By default, the client uses the
+.I host/
+or
+.I nfs/
+service principal in its
+.I /etc/krb5.keytab
+for this purpose, as described in
+.BR rpc.gssd (8).
+.P
+If the client has Kerberos configured, but the server
+does not, or if the client does not have a keytab or
+the requisite service principals, the client uses
+.I AUTH_SYS
+and UID 0 for lease management.
+.SS "Using non-privileged source ports"
+NFS clients usually communicate with NFS servers via network sockets.
+Each end of a socket is assigned a port value, which is simply a number
+between 1 and 65535 that distinguishes socket endpoints at the same
+IP address.
+A socket is uniquely defined by a tuple that includes the transport
+protocol (TCP or UDP) and the port values and IP addresses of both
+endpoints.
+.P
+The NFS client can choose any source port value for its sockets,
+but usually chooses a
+.I privileged
+port.
+A privileged port is a port value less than 1024.
+Only a process with root privileges may create a socket
+with a privileged source port.
+.P
+The exact range of privileged source ports that can be chosen is
+set by a pair of sysctls to avoid choosing a well-known port, such as
+the port used by ssh.
+This means the number of source ports available for the NFS client,
+and therefore the number of socket connections that can be used
+at the same time,
+is practically limited to only a few hundred.
+.P
+As described above, the traditional default NFS authentication scheme,
+known as AUTH_SYS, relies on sending local UID and GID numbers to identify
+users making NFS requests.
+An NFS server assumes that if a connection comes from a privileged port,
+the UID and GID numbers in the NFS requests on this connection have been
+verified by the client's kernel or some other local authority.
+This is an easy system to spoof, but on a trusted physical network between
+trusted hosts, it is entirely adequate.
+.P
+Roughly speaking, one socket is used for each NFS mount point.
+If a client could use non-privileged source ports as well,
+the number of sockets allowed,
+and thus the maximum number of concurrent mount points,
+would be much larger.
+.P
+Using non-privileged source ports may compromise server security somewhat,
+since any user on AUTH_SYS mount points can now pretend to be any other
+when making NFS requests.
+Thus NFS servers do not support this by default.
+They explicitly allow it usually via an export option.
+.P
+To retain good security while allowing as many mount points as possible,
+it is best to allow non-privileged client connections only if the server
+and client both require strong authentication, such as Kerberos.
+.SS "Mounting through a firewall"
+A firewall may reside between an NFS client and server,
+or the client or server may block some of its own ports via IP
+filter rules.
+It is still possible to mount an NFS server through a firewall,
+though some of the
+.BR mount (8)
+command's automatic service endpoint discovery mechanisms may not work; this
+requires you to provide specific endpoint details via NFS mount options.
+.P
+NFS servers normally run a portmapper or rpcbind daemon to advertise
+their service endpoints to clients. Clients use the rpcbind daemon to determine:
+.IP
+What network port each RPC-based service is using
+.IP
+What transport protocols each RPC-based service supports
+.P
+The rpcbind daemon uses a well-known port number (111) to help clients find a service endpoint.
+Although NFS often uses a standard port number (2049),
+auxiliary services such as the NLM service can choose
+any unused port number at random.
+.P
+Common firewall configurations block the well-known rpcbind port.
+In the absense of an rpcbind service,
+the server administrator fixes the port number
+of NFS-related services so that the firewall
+can allow access to specific NFS service ports.
+Client administrators then specify the port number
+for the mountd service via the
+.BR mount (8)
+command's
+.B mountport
+option.
+It may also be necessary to enforce the use of TCP or UDP
+if the firewall blocks one of those transports.
+.SS "NFS Access Control Lists"
+Solaris allows NFS version 3 clients direct access
+to POSIX Access Control Lists stored in its local file systems.
+This proprietary sideband protocol, known as NFSACL,
+provides richer access control than mode bits.
+Linux implements this protocol
+for compatibility with the Solaris NFS implementation.
+The NFSACL protocol never became a standard part
+of the NFS version 3 specification, however.
+.P
+The NFS version 4 specification mandates a new version
+of Access Control Lists that are semantically richer than POSIX ACLs.
+NFS version 4 ACLs are not fully compatible with POSIX ACLs; as such,
+some translation between the two is required
+in an environment that mixes POSIX ACLs and NFS version 4.
+.SH "THE REMOUNT OPTION"
+Generic mount options such as
+.BR rw " and " sync
+can be modified on NFS mount points using the
+.BR remount
+option.
+See
+.BR mount (8)
+for more information on generic mount options.
+.P
+With few exceptions, NFS-specific options
+are not able to be modified during a remount.
+The underlying transport or NFS version
+cannot be changed by a remount, for example.
+.P
+Performing a remount on an NFS file system mounted with the
+.B noac
+option may have unintended consequences.
+The
+.B noac
+option is a combination of the generic option
+.BR sync ,
+and the NFS-specific option
+.BR actimeo=0 .
+.SS "Unmounting after a remount"
+For mount points that use NFS versions 2 or 3, the NFS umount subcommand
+depends on knowing the original set of mount options used to perform the
+MNT operation.
+These options are stored on disk by the NFS mount subcommand,
+and can be erased by a remount.
+.P
+To ensure that the saved mount options are not erased during a remount,
+specify either the local mount directory, or the server hostname and
+export pathname, but not both, during a remount. For example,
+.P
+.nf
+.ta 8n
+ mount -o remount,ro /mnt
+.fi
+.P
+merges the mount option
+.B ro
+with the mount options already saved on disk for the NFS server mounted at /mnt.
+.SH FILES
+.TP 1.5i
+.I /etc/fstab
+file system table
+.TP 1.5i
+.I /etc/nfsmount.conf
+Configuration file for NFS mounts
+.SH NOTES
+Before 2.4.7, the Linux NFS client did not support NFS over TCP.
+.P
+Before 2.4.20, the Linux NFS client used a heuristic
+to determine whether cached file data was still valid
+rather than using the standard close-to-open cache coherency method
+described above.
+.P
+Starting with 2.4.22, the Linux NFS client employs
+a Van Jacobsen-based RTT estimator to determine retransmit
+timeout values when using NFS over UDP.
+.P
+Before 2.6.0, the Linux NFS client did not support NFS version 4.
+.P
+Before 2.6.8, the Linux NFS client used only synchronous reads and writes
+when the
+.BR rsize " and " wsize
+settings were smaller than the system's page size.
+.P
+The Linux client's support for protocol versions depend on whether the
+kernel was built with options CONFIG_NFS_V2, CONFIG_NFS_V3,
+CONFIG_NFS_V4, CONFIG_NFS_V4_1, and CONFIG_NFS_V4_2.
+.SH "SEE ALSO"
+.BR fstab (5),
+.BR mount (8),
+.BR umount (8),
+.BR mount.nfs (5),
+.BR umount.nfs (5),
+.BR exports (5),
+.BR nfsmount.conf (5),
+.BR netconfig (5),
+.BR ipv6 (7),
+.BR nfsd (8),
+.BR sm-notify (8),
+.BR rpc.statd (8),
+.BR rpc.idmapd (8),
+.BR rpc.gssd (8),
+.BR rpc.svcgssd (8),
+.BR kerberos (1)
+.sp
+RFC 768 for the UDP specification.
+.br
+RFC 793 for the TCP specification.
+.br
+RFC 1813 for the NFS version 3 specification.
+.br
+RFC 1832 for the XDR specification.
+.br
+RFC 1833 for the RPC bind specification.
+.br
+RFC 2203 for the RPCSEC GSS API protocol specification.
+.br
+RFC 7530 for the NFS version 4.0 specification.
+.br
+RFC 5661 for the NFS version 4.1 specification.
+.br
+RFC 7862 for the NFS version 4.2 specification.
diff --git a/utils/mount/nfs4_mount.h b/utils/mount/nfs4_mount.h
new file mode 100644
index 0000000..b03792e
--- /dev/null
+++ b/utils/mount/nfs4_mount.h
@@ -0,0 +1,73 @@
+#ifndef _LINUX_NFS4_MOUNT_H
+#define _LINUX_NFS4_MOUNT_H
+
+/*
+ * linux/include/linux/nfs4_mount.h
+ *
+ * Copyright (C) 2002 Trond Myklebust
+ *
+ * structure passed from user-space to kernel-space during an nfsv4 mount
+ */
+
+/*
+ * WARNING! Do not delete or change the order of these fields. If
+ * a new field is required then add it to the end. The version field
+ * tracks which fields are present. This will ensure some measure of
+ * mount-to-kernel version compatibility. Some of these aren't used yet
+ * but here they are anyway.
+ */
+#define NFS4_MOUNT_VERSION 1
+
+struct nfs_string {
+ unsigned int len;
+ const char* data;
+};
+
+struct nfs4_mount_data {
+ int version; /* 1 */
+ int flags; /* 1 */
+ int rsize; /* 1 */
+ int wsize; /* 1 */
+ int timeo; /* 1 */
+ int retrans; /* 1 */
+ int acregmin; /* 1 */
+ int acregmax; /* 1 */
+ int acdirmin; /* 1 */
+ int acdirmax; /* 1 */
+
+ /* see the definition of 'struct clientaddr4' in RFC3010 */
+ struct nfs_string client_addr; /* 1 */
+
+ /* Mount path */
+ struct nfs_string mnt_path; /* 1 */
+
+ /* Server details */
+ struct nfs_string hostname; /* 1 */
+ /* Server IP address */
+ unsigned int host_addrlen; /* 1 */
+ struct sockaddr* host_addr; /* 1 */
+
+ /* Transport protocol to use */
+ int proto; /* 1 */
+
+ /* Pseudo-flavours to use for authentication. See RFC2623 */
+ int auth_flavourlen; /* 1 */
+ int *auth_flavours; /* 1 */
+};
+
+/* bits in the flags field */
+/* Note: the fields that correspond to existing NFSv2/v3 mount options
+ * should mirror the values from include/linux/nfs_mount.h
+ */
+
+#define NFS4_MOUNT_SOFT 0x0001 /* 1 */
+#define NFS4_MOUNT_INTR 0x0002 /* 1 */
+#define NFS4_MOUNT_NOCTO 0x0010 /* 1 */
+#define NFS4_MOUNT_NOAC 0x0020 /* 1 */
+#define NFS4_MOUNT_STRICTLOCK 0x1000 /* 1 */
+#define NFS4_MOUNT_UNSHARED 0x8000 /* 5 */
+#define NFS4_MOUNT_FLAGMASK 0xFFFF
+
+int nfs4mount(const char *, const char *, int, char **, int, int);
+
+#endif
diff --git a/utils/mount/nfs4mount.c b/utils/mount/nfs4mount.c
new file mode 100644
index 0000000..3e4f1e2
--- /dev/null
+++ b/utils/mount/nfs4mount.c
@@ -0,0 +1,481 @@
+/*
+ * nfs4mount.c -- Linux NFS mount
+ * Copyright (C) 2002 Trond Myklebust <trond.myklebust@fys.uio.no>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Note: this file based on the original nfsmount.c
+ *
+ * 2006-06-06 Amit Gud <agud@redhat.com>
+ * - Moved to nfs-utils/utils/mount from util-linux/mount.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <netdb.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <sys/mount.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <rpc/auth.h>
+#include <rpc/rpc.h>
+
+#ifdef HAVE_RPCSVC_NFS_PROT_H
+#include <rpcsvc/nfs_prot.h>
+#else
+#include <linux/nfs.h>
+#define nfsstat nfs_stat
+#endif
+
+#include "pseudoflavors.h"
+#include "nls.h"
+#include "xcommon.h"
+
+#include "mount.h"
+#include "mount_constants.h"
+#include "nfs4_mount.h"
+#include "nfs_mount.h"
+#include "error.h"
+#include "network.h"
+
+#if defined(VAR_LOCK_DIR)
+#define DEFAULT_DIR VAR_LOCK_DIR
+#else
+#define DEFAULT_DIR "/var/lock/subsys"
+#endif
+
+extern char *progname;
+extern int verbose;
+extern int sloppy;
+
+char *IDMAPLCK = DEFAULT_DIR "/rpcidmapd";
+#define idmapd_check() do { \
+ if (access(IDMAPLCK, F_OK)) { \
+ printf(_("Warning: rpc.idmapd appears not to be running.\n" \
+ " All uids will be mapped to the nobody uid.\n")); \
+ } \
+} while(0);
+
+char *GSSDLCK = DEFAULT_DIR "/rpcgssd";
+#define gssd_check() do { \
+ if (access(GSSDLCK, F_OK)) { \
+ printf(_("Warning: rpc.gssd appears not to be running.\n")); \
+ } \
+} while(0);
+
+#ifndef NFS_PORT
+#define NFS_PORT 2049
+#endif
+
+#define MAX_USER_FLAVOUR 16
+
+static int parse_sec(char *sec, int *pseudoflavour)
+{
+ int i, num_flavour = 0;
+
+ for (sec = strtok(sec, ":"); sec; sec = strtok(NULL, ":")) {
+ if (num_flavour >= MAX_USER_FLAVOUR) {
+ nfs_error(_("%s: maximum number of security flavors "
+ "exceeded"), progname);
+ return 0;
+ }
+ for (i = 0; i < flav_map_size; i++) {
+ if (strcmp(sec, flav_map[i].flavour) == 0) {
+ pseudoflavour[num_flavour++] = flav_map[i].fnum;
+ break;
+ }
+ }
+ if (i == flav_map_size) {
+ nfs_error(_("%s: unknown security type %s\n"),
+ progname, sec);
+ return 0;
+ }
+ }
+ if (!num_flavour)
+ nfs_error(_("%s: no security flavors passed to sec= option"),
+ progname);
+ return num_flavour;
+}
+
+static int parse_devname(char *hostdir, char **hostname, char **dirname)
+{
+ char *s;
+
+ if (!(s = strchr(hostdir, ':'))) {
+ nfs_error(_("%s: directory to mount not in host:dir format"),
+ progname);
+ return -1;
+ }
+ *hostname = hostdir;
+ *dirname = s + 1;
+ *s = '\0';
+ /* Ignore all but first hostname in replicated mounts
+ until they can be fully supported. (mack@sgi.com) */
+ if ((s = strchr(hostdir, ','))) {
+ *s = '\0';
+ nfs_error(_("%s: warning: multiple hostnames not supported"),
+ progname);
+ }
+ return 0;
+}
+
+static int fill_ipv4_sockaddr(const char *hostname, struct sockaddr_in *addr)
+{
+ struct hostent *hp;
+ addr->sin_family = AF_INET;
+
+ if (inet_aton(hostname, &addr->sin_addr))
+ return 0;
+ if ((hp = gethostbyname(hostname)) == NULL) {
+ nfs_error(_("%s: can't get address for %s\n"),
+ progname, hostname);
+ return -1;
+ }
+ if (hp->h_length > (int)sizeof(struct in_addr)) {
+ nfs_error(_("%s: got bad hp->h_length"), progname);
+ hp->h_length = sizeof(struct in_addr);
+ }
+ memcpy(&addr->sin_addr, hp->h_addr, hp->h_length);
+ return 0;
+}
+
+static int get_my_ipv4addr(char *ip_addr, int len)
+{
+ char myname[1024];
+ struct sockaddr_in myaddr;
+
+ if (gethostname(myname, sizeof(myname))) {
+ nfs_error(_("%s: can't determine client address\n"),
+ progname);
+ return -1;
+ }
+ if (fill_ipv4_sockaddr(myname, &myaddr))
+ return -1;
+ snprintf(ip_addr, len, "%s", inet_ntoa(myaddr.sin_addr));
+ ip_addr[len-1] = '\0';
+ return 0;
+}
+
+int nfs4mount(const char *spec, const char *node, int flags,
+ char **extra_opts, int fake, int running_bg)
+{
+ static struct nfs4_mount_data data;
+ static char hostdir[1024];
+ static char ip_addr[16] = "127.0.0.1";
+ static struct sockaddr_in server_addr, client_addr;
+ static int pseudoflavour[MAX_USER_FLAVOUR];
+ int num_flavour = 0;
+ int ip_addr_in_opts = 0;
+
+ char *hostname, *dirname, *old_opts;
+ char new_opts[1024];
+ char *opt, *opteq;
+ char *s;
+ int val;
+ int bg, soft, intr;
+ int nocto, noac, unshared;
+ int retry;
+ int retval = EX_FAIL;
+ time_t timeout, t;
+
+ if (strlen(spec) >= sizeof(hostdir)) {
+ nfs_error(_("%s: excessively long host:dir argument\n"),
+ progname);
+ goto fail;
+ }
+ strcpy(hostdir, spec);
+ if (parse_devname(hostdir, &hostname, &dirname))
+ goto fail;
+
+ if (fill_ipv4_sockaddr(hostname, &server_addr))
+ goto fail;
+ if (get_my_ipv4addr(ip_addr, sizeof(ip_addr)))
+ goto fail;
+
+ /* add IP address to mtab options for use when unmounting */
+ s = inet_ntoa(server_addr.sin_addr);
+ old_opts = *extra_opts;
+ if (!old_opts)
+ old_opts = "";
+ if (strlen(old_opts) + strlen(s) + 10 >= sizeof(new_opts)) {
+ nfs_error(_("%s: excessively long option argument\n"),
+ progname);
+ goto fail;
+ }
+ if (running_bg)
+ strncpy(new_opts, old_opts, sizeof(new_opts)-1);
+ else
+ snprintf(new_opts, sizeof(new_opts), "%s%saddr=%s",
+ old_opts, *old_opts ? "," : "", s);
+ *extra_opts = xstrdup(new_opts);
+
+ /* Set default options.
+ * rsize/wsize and timeo are left 0 in order to
+ * let the kernel decide.
+ */
+ memset(&data, 0, sizeof(data));
+ data.retrans = 3;
+ data.acregmin = 3;
+ data.acregmax = 60;
+ data.acdirmin = 30;
+ data.acdirmax = 60;
+ data.proto = IPPROTO_TCP;
+
+ bg = 0;
+ soft = 0;
+ intr = NFS4_MOUNT_INTR;
+ nocto = 0;
+ noac = 0;
+ unshared = 0;
+ retry = -1;
+
+ /*
+ * NFSv4 specifies that the default port should be 2049
+ */
+ server_addr.sin_port = htons(NFS_PORT);
+
+ /* parse options */
+
+ for (opt = strtok(old_opts, ","); opt; opt = strtok(NULL, ",")) {
+ if ((opteq = strchr(opt, '='))) {
+ val = atoi(opteq + 1);
+ *opteq = '\0';
+ if (!strcmp(opt, "rsize"))
+ data.rsize = val;
+ else if (!strcmp(opt, "wsize"))
+ data.wsize = val;
+ else if (!strcmp(opt, "timeo"))
+ data.timeo = val;
+ else if (!strcmp(opt, "retrans"))
+ data.retrans = val;
+ else if (!strcmp(opt, "acregmin"))
+ data.acregmin = val;
+ else if (!strcmp(opt, "acregmax"))
+ data.acregmax = val;
+ else if (!strcmp(opt, "acdirmin"))
+ data.acdirmin = val;
+ else if (!strcmp(opt, "acdirmax"))
+ data.acdirmax = val;
+ else if (!strcmp(opt, "actimeo")) {
+ data.acregmin = val;
+ data.acregmax = val;
+ data.acdirmin = val;
+ data.acdirmax = val;
+ }
+ else if (!strcmp(opt, "retry"))
+ retry = val;
+ else if (!strcmp(opt, "port"))
+ server_addr.sin_port = htons(val);
+ else if (!strcmp(opt, "proto")) {
+ if (!strncmp(opteq+1, "tcp", 3))
+ data.proto = IPPROTO_TCP;
+ else if (!strncmp(opteq+1, "udp", 3))
+ data.proto = IPPROTO_UDP;
+ else
+ printf(_("Warning: Unrecognized proto= option.\n"));
+ } else if (!strcmp(opt, "clientaddr")) {
+ if (strlen(opteq+1) >= sizeof(ip_addr))
+ printf(_("Invalid client address %s"),
+ opteq+1);
+ strncpy(ip_addr,opteq+1, sizeof(ip_addr));
+ ip_addr[sizeof(ip_addr)-1] = '\0';
+ ip_addr_in_opts = 1;
+ } else if (!strcmp(opt, "sec")) {
+ num_flavour = parse_sec(opteq+1, pseudoflavour);
+ if (!num_flavour)
+ goto fail;
+ } else if (!strcmp(opt, "addr") || sloppy) {
+ /* ignore */;
+ } else {
+ printf(_("unknown nfs mount parameter: "
+ "%s=%d\n"), opt, val);
+ goto fail;
+ }
+ } else {
+ val = 1;
+ if (!strncmp(opt, "no", 2)) {
+ val = 0;
+ opt += 2;
+ }
+ if (!strcmp(opt, "bg"))
+ bg = val;
+ else if (!strcmp(opt, "fg"))
+ bg = !val;
+ else if (!strcmp(opt, "soft"))
+ soft = val;
+ else if (!strcmp(opt, "hard"))
+ soft = !val;
+ else if (!strcmp(opt, "intr"))
+ intr = val;
+ else if (!strcmp(opt, "cto"))
+ nocto = !val;
+ else if (!strcmp(opt, "ac"))
+ noac = !val;
+ else if (!strcmp(opt, "sharecache"))
+ unshared = !val;
+ else if (!sloppy) {
+ printf(_("unknown nfs mount option: %s%s\n"),
+ val ? "" : "no", opt);
+ goto fail;
+ }
+ }
+ }
+
+ /* if retry is still -1, then it wasn't set via an option */
+ if (retry == -1) {
+ if (bg)
+ retry = 10000; /* 10000 mins == ~1 week */
+ else
+ retry = 2; /* 2 min default on fg mounts */
+ }
+
+ data.flags = (soft ? NFS4_MOUNT_SOFT : 0)
+ | (intr ? NFS4_MOUNT_INTR : 0)
+ | (nocto ? NFS4_MOUNT_NOCTO : 0)
+ | (noac ? NFS4_MOUNT_NOAC : 0)
+ | (unshared ? NFS4_MOUNT_UNSHARED : 0);
+
+ /*
+ * Give a warning if the rpc.idmapd daemon is not running
+ */
+#if 0
+ /* We shouldn't have these checks as nothing in this package
+ * creates the files that are checked
+ */
+ idmapd_check();
+
+ if (num_flavour == 0)
+ pseudoflavour[num_flavour++] = AUTH_UNIX;
+ else {
+ /*
+ * ditto with rpc.gssd daemon
+ */
+ gssd_check();
+ }
+#endif
+ data.auth_flavourlen = num_flavour;
+ data.auth_flavours = pseudoflavour;
+
+ data.client_addr.data = ip_addr;
+ data.client_addr.len = strlen(ip_addr);
+
+ data.mnt_path.data = dirname;
+ data.mnt_path.len = strlen(dirname);
+
+ data.hostname.data = hostname;
+ data.hostname.len = strlen(hostname);
+ data.host_addr = (struct sockaddr *)&server_addr;
+ data.host_addrlen = sizeof(server_addr);
+
+#ifdef NFS_MOUNT_DEBUG
+ printf(_("rsize = %d, wsize = %d, timeo = %d, retrans = %d\n"),
+ data.rsize, data.wsize, data.timeo, data.retrans);
+ printf(_("acreg (min, max) = (%d, %d), acdir (min, max) = (%d, %d)\n"),
+ data.acregmin, data.acregmax, data.acdirmin, data.acdirmax);
+ printf(_("port = %d, bg = %d, retry = %d, flags = %.8x\n"),
+ ntohs(server_addr.sin_port), bg, retry, data.flags);
+ printf(_("soft = %d, intr = %d, nocto = %d, noac = %d, "
+ "nosharecache = %d\n"),
+ (data.flags & NFS4_MOUNT_SOFT) != 0,
+ (data.flags & NFS4_MOUNT_INTR) != 0,
+ (data.flags & NFS4_MOUNT_NOCTO) != 0,
+ (data.flags & NFS4_MOUNT_NOAC) != 0,
+ (data.flags & NFS4_MOUNT_UNSHARED) != 0);
+
+ if (num_flavour > 0) {
+ int pf_cnt, i;
+
+ printf(_("sec = "));
+ for (pf_cnt = 0; pf_cnt < num_flavour; pf_cnt++) {
+ for (i = 0; i < flav_map_size; i++) {
+ if (flav_map[i].fnum == pseudoflavour[pf_cnt]) {
+ printf("%s", flav_map[i].flavour);
+ break;
+ }
+ }
+ printf("%s", (pf_cnt < num_flavour-1) ? ":" : "\n");
+ }
+ }
+ printf(_("proto = %s\n"), (data.proto == IPPROTO_TCP) ? _("tcp") : _("udp"));
+#endif
+
+ timeout = time(NULL) + 60 * retry;
+ data.version = NFS4_MOUNT_VERSION;
+ for (;;) {
+ if (verbose) {
+ printf(_("%s: pinging: prog %d vers %d prot %s port %d\n"),
+ progname, NFS_PROGRAM, 4,
+ data.proto == IPPROTO_UDP ? "udp" : "tcp",
+ ntohs(server_addr.sin_port));
+ }
+ client_addr.sin_family = 0;
+ client_addr.sin_addr.s_addr = 0;
+ clnt_ping(&server_addr, NFS_PROGRAM, 4, data.proto, &client_addr);
+ if (rpc_createerr.cf_stat == RPC_SUCCESS) {
+ if (!ip_addr_in_opts &&
+ client_addr.sin_family != 0 &&
+ client_addr.sin_addr.s_addr != 0) {
+ snprintf(ip_addr, sizeof(ip_addr), "%s",
+ inet_ntoa(client_addr.sin_addr));
+ data.client_addr.len = strlen(ip_addr);
+ }
+ break;
+ }
+
+ if (!bg) {
+ switch(rpc_createerr.cf_stat) {
+ case RPC_TIMEDOUT:
+ break;
+ case RPC_SYSTEMERROR:
+ if (errno == ETIMEDOUT)
+ break;
+ /* FALLTHRU */
+ default:
+ rpc_mount_errors(hostname, 0, bg);
+ goto fail;
+ }
+ }
+
+ if (bg && !running_bg) {
+ if (retry > 0)
+ retval = EX_BG;
+ goto fail;
+ }
+
+ t = time(NULL);
+ if (t >= timeout) {
+ rpc_mount_errors(hostname, 0, bg);
+ goto fail;
+ }
+ rpc_mount_errors(hostname, 1, bg);
+ continue;
+ }
+
+ if (!fake) {
+ if (mount(spec, node, "nfs4",
+ flags & ~(MS_USER|MS_USERS), &data)) {
+ mount_error(spec, node, errno);
+ goto fail;
+ }
+ }
+
+ return EX_SUCCESS;
+
+fail:
+ return retval;
+}
diff --git a/utils/mount/nfs_mount.h b/utils/mount/nfs_mount.h
new file mode 100644
index 0000000..ec30c9b
--- /dev/null
+++ b/utils/mount/nfs_mount.h
@@ -0,0 +1,83 @@
+/*
+ * We want to be able to compile mount on old kernels in such a way
+ * that the binary will work well on more recent kernels.
+ * Thus, if necessary we teach nfsmount.c the structure of new fields
+ * that will come later.
+ *
+ * Moreover, the new kernel includes conflict with glibc includes
+ * so it is easiest to ignore the kernel altogether (at compile time).
+ */
+
+#ifndef _NFS_UTILS_MOUNT_NFS_MOUNT_H
+#define _NFS_UTILS_MOUNT_NFS_MOUNT_H
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#define NFS_MOUNT_VERSION 6
+#define NFS_MAX_CONTEXT_LEN 256
+
+struct nfs2_fh {
+ char data[32];
+};
+struct nfs3_fh {
+ unsigned short size;
+ unsigned char data[64];
+};
+
+struct nfs_mount_data {
+ int version; /* 1 */
+ int fd; /* 1 */
+ struct nfs2_fh old_root; /* 1 */
+ int flags; /* 1 */
+ int rsize; /* 1 */
+ int wsize; /* 1 */
+ int timeo; /* 1 */
+ int retrans; /* 1 */
+ int acregmin; /* 1 */
+ int acregmax; /* 1 */
+ int acdirmin; /* 1 */
+ int acdirmax; /* 1 */
+ struct sockaddr_in addr; /* 1 */
+ char hostname[256]; /* 1 */
+ int namlen; /* 2 */
+ unsigned int bsize; /* 3 */
+ struct nfs3_fh root; /* 4 */
+ int pseudoflavor; /* 5 */
+ char context[NFS_MAX_CONTEXT_LEN + 1]; /* 6 */
+
+};
+
+/* bits in the flags field */
+
+#define NFS_MOUNT_SOFT 0x0001 /* 1 */
+#define NFS_MOUNT_INTR 0x0002 /* 1 */
+#define NFS_MOUNT_SECURE 0x0004 /* 1 */
+#define NFS_MOUNT_POSIX 0x0008 /* 1 */
+#define NFS_MOUNT_NOCTO 0x0010 /* 1 */
+#define NFS_MOUNT_NOAC 0x0020 /* 1 */
+#define NFS_MOUNT_TCP 0x0040 /* 2 */
+#define NFS_MOUNT_VER3 0x0080 /* 3 */
+#define NFS_MOUNT_KERBEROS 0x0100 /* 3 */
+#define NFS_MOUNT_NONLM 0x0200 /* 3 */
+#define NFS_MOUNT_BROKEN_SUID 0x0400 /* 4 */
+#define NFS_MOUNT_NOACL 0x0800 /* 4 */
+#define NFS_MOUNT_SECFLAVOUR 0x2000 /* 5 */
+#define NFS_MOUNT_NORDIRPLUS 0x4000 /* 5 */
+#define NFS_MOUNT_UNSHARED 0x8000 /* 5 */
+
+/* security pseudoflavors */
+
+#ifndef AUTH_GSS_KRB5
+#define AUTH_GSS_KRB5 390003
+#define AUTH_GSS_KRB5I 390004
+#define AUTH_GSS_KRB5P 390005
+#define AUTH_GSS_LKEY 390006
+#define AUTH_GSS_LKEYI 390007
+#define AUTH_GSS_LKEYP 390008
+#endif
+
+int nfsmount(const char *, const char *, int , char **, int, int);
+int nfsumount(int, char **);
+
+#endif /* _NFS_UTILS_MOUNT_NFS_MOUNT_H */
diff --git a/utils/mount/nfsmount.c b/utils/mount/nfsmount.c
new file mode 100644
index 0000000..3d95da9
--- /dev/null
+++ b/utils/mount/nfsmount.c
@@ -0,0 +1,873 @@
+/*
+ * nfsmount.c -- Linux NFS mount
+ * Copyright (C) 1993 Rick Sladkey <jrs@world.std.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * Wed Feb 8 12:51:48 1995, biro@yggdrasil.com (Ross Biro): allow all port
+ * numbers to be specified on the command line.
+ *
+ * Fri, 8 Mar 1996 18:01:39, Swen Thuemmler <swen@uni-paderborn.de>:
+ * Omit the call to connect() for Linux version 1.3.11 or later.
+ *
+ * Wed Oct 1 23:55:28 1997: Dick Streefland <dick_streefland@tasking.com>
+ * Implemented the "bg", "fg" and "retry" mount options for NFS.
+ *
+ * 1999-02-22 Arkadiusz Miskiewicz <misiek@pld.ORG.PL>
+ * - added Native Language Support
+ *
+ * Modified by Olaf Kirch and Trond Myklebust for new NFS code,
+ * plus NFSv3 stuff.
+ *
+ * 2006-06-06 Amit Gud <agud@redhat.com>
+ * - Moved with modifcations to nfs-utils/utils/mount from util-linux/mount.
+ */
+
+/*
+ * nfsmount.c,v 1.1.1.1 1993/11/18 08:40:51 jrs Exp
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <netdb.h>
+#include <time.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_prot.h>
+#include <rpc/pmap_clnt.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <mntent.h>
+#include <sys/mount.h>
+#include <paths.h>
+#include <syslog.h>
+
+#include "xcommon.h"
+#include "mount.h"
+#include "nfs_mount.h"
+#include "mount_constants.h"
+#include "nls.h"
+#include "error.h"
+#include "network.h"
+#include "version.h"
+
+#ifdef HAVE_RPCSVC_NFS_PROT_H
+#include <rpcsvc/nfs_prot.h>
+#else
+#include <linux/nfs.h>
+#define nfsstat nfs_stat
+#endif
+
+#ifndef NFS_PORT
+#define NFS_PORT 2049
+#endif
+#ifndef NFS_FHSIZE
+#define NFS_FHSIZE 32
+#endif
+
+#ifndef HAVE_INET_ATON
+#define inet_aton(a,b) (0)
+#endif
+
+typedef dirpath mnt2arg_t;
+typedef dirpath mnt3arg_t;
+typedef dirpath mntarg_t;
+
+typedef struct fhstatus mnt2res_t;
+typedef struct mountres3 mnt3res_t;
+typedef union {
+ mnt2res_t nfsv2;
+ mnt3res_t nfsv3;
+} mntres_t;
+
+extern int nfs_mount_data_version;
+extern char *progname;
+extern int verbose;
+extern int sloppy;
+
+static inline enum clnt_stat
+nfs3_mount(CLIENT *clnt, mnt3arg_t *mnt3arg, mnt3res_t *mnt3res)
+{
+ return clnt_call(clnt, MOUNTPROC3_MNT,
+ (xdrproc_t) xdr_dirpath, (caddr_t) mnt3arg,
+ (xdrproc_t) xdr_mountres3, (caddr_t) mnt3res,
+ TIMEOUT);
+}
+
+static inline enum clnt_stat
+nfs2_mount(CLIENT *clnt, mnt2arg_t *mnt2arg, mnt2res_t *mnt2res)
+{
+ return clnt_call(clnt, MOUNTPROC_MNT,
+ (xdrproc_t) xdr_dirpath, (caddr_t) mnt2arg,
+ (xdrproc_t) xdr_fhstatus, (caddr_t) mnt2res,
+ TIMEOUT);
+}
+
+static int
+nfs_call_mount(clnt_addr_t *mnt_server, clnt_addr_t *nfs_server,
+ mntarg_t *mntarg, mntres_t *mntres)
+{
+ CLIENT *clnt;
+ enum clnt_stat stat;
+ int msock;
+
+ if (!probe_bothports(mnt_server, nfs_server))
+ goto out_bad;
+
+ clnt = mnt_openclnt(mnt_server, &msock);
+ if (!clnt)
+ goto out_bad;
+ /* make pointers in xdr_mountres3 NULL so
+ * that xdr_array allocates memory for us
+ */
+ memset(mntres, 0, sizeof(*mntres));
+ switch (mnt_server->pmap.pm_vers) {
+ case 3:
+ stat = nfs3_mount(clnt, mntarg, &mntres->nfsv3);
+ break;
+ case 2:
+ case 1:
+ stat = nfs2_mount(clnt, mntarg, &mntres->nfsv2);
+ break;
+ default:
+ goto out_bad;
+ }
+ if (stat != RPC_SUCCESS) {
+ clnt_geterr(clnt, &rpc_createerr.cf_error);
+ rpc_createerr.cf_stat = stat;
+ }
+ mnt_closeclnt(clnt, msock);
+ if (stat == RPC_SUCCESS)
+ return 1;
+ out_bad:
+ return 0;
+}
+
+static int
+parse_options(char *old_opts, struct nfs_mount_data *data,
+ int *bg, int *retry, clnt_addr_t *mnt_server,
+ clnt_addr_t *nfs_server, char *new_opts, const int opt_size)
+{
+ struct sockaddr_in *mnt_saddr = &mnt_server->saddr;
+ struct pmap *mnt_pmap = &mnt_server->pmap;
+ struct pmap *nfs_pmap = &nfs_server->pmap;
+ int len;
+ char *opt, *opteq, *p, *opt_b, *tmp_opts;
+ char *mounthost = NULL;
+ char cbuf[128];
+ int open_quote = 0;
+
+ data->flags = 0;
+ *bg = 0;
+
+ len = strlen(new_opts);
+ tmp_opts = xstrdup(old_opts);
+ for (p=tmp_opts, opt_b=NULL; p && *p; p++) {
+ if (!opt_b)
+ opt_b = p; /* begin of the option item */
+ if (*p == '"')
+ open_quote ^= 1; /* reverse the status */
+ if (open_quote)
+ continue; /* still in a quoted block */
+ if (*p == ',')
+ *p = '\0'; /* terminate the option item */
+ if (*p == '\0' || *(p+1) == '\0') {
+ opt = opt_b; /* opt is useful now */
+ opt_b = NULL;
+ }
+ else
+ continue; /* still somewhere in the option item */
+
+ if (strlen(opt) >= sizeof(cbuf))
+ goto bad_parameter;
+ if ((opteq = strchr(opt, '=')) && isdigit(opteq[1])) {
+ int val = atoi(opteq + 1);
+ *opteq = '\0';
+ if (!strcmp(opt, "rsize"))
+ data->rsize = val;
+ else if (!strcmp(opt, "wsize"))
+ data->wsize = val;
+ else if (!strcmp(opt, "timeo"))
+ data->timeo = val;
+ else if (!strcmp(opt, "retrans"))
+ data->retrans = val;
+ else if (!strcmp(opt, "acregmin"))
+ data->acregmin = val;
+ else if (!strcmp(opt, "acregmax"))
+ data->acregmax = val;
+ else if (!strcmp(opt, "acdirmin"))
+ data->acdirmin = val;
+ else if (!strcmp(opt, "acdirmax"))
+ data->acdirmax = val;
+ else if (!strcmp(opt, "actimeo")) {
+ data->acregmin = val;
+ data->acregmax = val;
+ data->acdirmin = val;
+ data->acdirmax = val;
+ }
+ else if (!strcmp(opt, "retry"))
+ *retry = val;
+ else if (!strcmp(opt, "port"))
+ nfs_pmap->pm_port = val;
+ else if (!strcmp(opt, "mountport"))
+ mnt_pmap->pm_port = val;
+ else if (!strcmp(opt, "mountprog"))
+ mnt_pmap->pm_prog = val;
+ else if (!strcmp(opt, "mountvers"))
+ mnt_pmap->pm_vers = val;
+ else if (!strcmp(opt, "mounthost"))
+ mounthost=xstrndup(opteq+1, strcspn(opteq+1," \t\n\r,"));
+ else if (!strcmp(opt, "nfsprog"))
+ nfs_pmap->pm_prog = val;
+ else if (!strcmp(opt, "nfsvers") ||
+ !strcmp(opt, "vers")) {
+ nfs_pmap->pm_vers = val;
+ opt = "nfsvers";
+#if NFS_MOUNT_VERSION >= 2
+ } else if (!strcmp(opt, "namlen")) {
+ if (nfs_mount_data_version >= 2)
+ data->namlen = val;
+ else if (sloppy)
+ continue;
+ else
+ goto bad_parameter;
+#endif
+ } else if (!strcmp(opt, "addr")) {
+ /* ignore */;
+ continue;
+ } else if (sloppy)
+ continue;
+ else
+ goto bad_parameter;
+ sprintf(cbuf, "%s=%s,", opt, opteq+1);
+ } else if (opteq) {
+ *opteq = '\0';
+ if (!strcmp(opt, "proto")) {
+ if (!strcmp(opteq+1, "udp")) {
+ nfs_pmap->pm_prot = IPPROTO_UDP;
+ mnt_pmap->pm_prot = IPPROTO_UDP;
+#if NFS_MOUNT_VERSION >= 2
+ data->flags &= ~NFS_MOUNT_TCP;
+ } else if (!strcmp(opteq+1, "tcp") &&
+ nfs_mount_data_version > 2) {
+ nfs_pmap->pm_prot = IPPROTO_TCP;
+ mnt_pmap->pm_prot = IPPROTO_TCP;
+ data->flags |= NFS_MOUNT_TCP;
+#endif
+ } else if (sloppy)
+ continue;
+ else
+ goto bad_parameter;
+#if NFS_MOUNT_VERSION >= 5
+ } else if (!strcmp(opt, "sec")) {
+ char *secflavor = opteq+1;
+ /* see RFC 2623 */
+ if (nfs_mount_data_version < 5) {
+ printf(_("Warning: ignoring sec=%s option\n"),
+ secflavor);
+ continue;
+ } else if (!strcmp(secflavor, "none"))
+ data->pseudoflavor = AUTH_NONE;
+ else if (!strcmp(secflavor, "sys"))
+ data->pseudoflavor = AUTH_SYS;
+ else if (!strcmp(secflavor, "krb5"))
+ data->pseudoflavor = AUTH_GSS_KRB5;
+ else if (!strcmp(secflavor, "krb5i"))
+ data->pseudoflavor = AUTH_GSS_KRB5I;
+ else if (!strcmp(secflavor, "krb5p"))
+ data->pseudoflavor = AUTH_GSS_KRB5P;
+ else if (sloppy)
+ continue;
+ else {
+ printf(_("Warning: Unrecognized security flavor %s.\n"),
+ secflavor);
+ goto bad_parameter;
+ }
+ data->flags |= NFS_MOUNT_SECFLAVOUR;
+#endif
+ } else if (!strcmp(opt, "mounthost"))
+ mounthost=xstrndup(opteq+1,
+ strcspn(opteq+1," \t\n\r,"));
+ else if (!strcmp(opt, "context")) {
+ char *context = opteq + 1;
+ int ctxlen = strlen(context);
+
+ if (ctxlen > NFS_MAX_CONTEXT_LEN) {
+ nfs_error(_("context parameter exceeds"
+ " limit of %d"),
+ NFS_MAX_CONTEXT_LEN);
+ goto bad_parameter;
+ }
+ /* The context string is in the format of
+ * "system_u:object_r:...". We only want
+ * the context str between the quotes.
+ */
+ if (*context == '"')
+ strncpy(data->context, context+1,
+ ctxlen-2);
+ else
+ strncpy(data->context, context,
+ NFS_MAX_CONTEXT_LEN);
+ } else if (sloppy)
+ continue;
+ else
+ goto bad_parameter;
+ sprintf(cbuf, "%s=%s,", opt, opteq+1);
+ } else {
+ int val = 1;
+ if (!strncmp(opt, "no", 2)) {
+ val = 0;
+ opt += 2;
+ }
+ if (!strcmp(opt, "bg"))
+ *bg = val;
+ else if (!strcmp(opt, "fg"))
+ *bg = !val;
+ else if (!strcmp(opt, "soft")) {
+ data->flags &= ~NFS_MOUNT_SOFT;
+ if (val)
+ data->flags |= NFS_MOUNT_SOFT;
+ } else if (!strcmp(opt, "hard")) {
+ data->flags &= ~NFS_MOUNT_SOFT;
+ if (!val)
+ data->flags |= NFS_MOUNT_SOFT;
+ } else if (!strcmp(opt, "intr")) {
+ data->flags &= ~NFS_MOUNT_INTR;
+ if (val)
+ data->flags |= NFS_MOUNT_INTR;
+ } else if (!strcmp(opt, "posix")) {
+ data->flags &= ~NFS_MOUNT_POSIX;
+ if (val)
+ data->flags |= NFS_MOUNT_POSIX;
+ } else if (!strcmp(opt, "cto")) {
+ data->flags &= ~NFS_MOUNT_NOCTO;
+ if (!val)
+ data->flags |= NFS_MOUNT_NOCTO;
+ } else if (!strcmp(opt, "ac")) {
+ data->flags &= ~NFS_MOUNT_NOAC;
+ if (!val)
+ data->flags |= NFS_MOUNT_NOAC;
+#if NFS_MOUNT_VERSION >= 2
+ } else if (!strcmp(opt, "tcp")) {
+ data->flags &= ~NFS_MOUNT_TCP;
+ if (val) {
+ if (nfs_mount_data_version < 2)
+ goto bad_option;
+ nfs_pmap->pm_prot = IPPROTO_TCP;
+ mnt_pmap->pm_prot = IPPROTO_TCP;
+ data->flags |= NFS_MOUNT_TCP;
+ } else {
+ mnt_pmap->pm_prot = IPPROTO_UDP;
+ nfs_pmap->pm_prot = IPPROTO_UDP;
+ }
+ } else if (!strcmp(opt, "udp")) {
+ data->flags &= ~NFS_MOUNT_TCP;
+ if (!val) {
+ if (nfs_mount_data_version < 2)
+ goto bad_option;
+ nfs_pmap->pm_prot = IPPROTO_TCP;
+ mnt_pmap->pm_prot = IPPROTO_TCP;
+ data->flags |= NFS_MOUNT_TCP;
+ } else {
+ nfs_pmap->pm_prot = IPPROTO_UDP;
+ mnt_pmap->pm_prot = IPPROTO_UDP;
+ }
+#endif
+#if NFS_MOUNT_VERSION >= 3
+ } else if (!strcmp(opt, "lock")) {
+ data->flags &= ~NFS_MOUNT_NONLM;
+ if (!val) {
+ if (nfs_mount_data_version < 3)
+ goto bad_option;
+ data->flags |= NFS_MOUNT_NONLM;
+ }
+#endif
+#if NFS_MOUNT_VERSION >= 4
+ } else if (!strcmp(opt, "broken_suid")) {
+ data->flags &= ~NFS_MOUNT_BROKEN_SUID;
+ if (val) {
+ if (nfs_mount_data_version < 4)
+ goto bad_option;
+ data->flags |= NFS_MOUNT_BROKEN_SUID;
+ }
+ } else if (!strcmp(opt, "acl")) {
+ data->flags &= ~NFS_MOUNT_NOACL;
+ if (!val)
+ data->flags |= NFS_MOUNT_NOACL;
+ } else if (!strcmp(opt, "rdirplus")) {
+ data->flags &= ~NFS_MOUNT_NORDIRPLUS;
+ if (!val)
+ data->flags |= NFS_MOUNT_NORDIRPLUS;
+ } else if (!strcmp(opt, "sharecache")) {
+ data->flags &= ~NFS_MOUNT_UNSHARED;
+ if (!val)
+ data->flags |= NFS_MOUNT_UNSHARED;
+#endif
+ } else {
+ bad_option:
+ if (sloppy)
+ continue;
+ nfs_error(_("%s: Unsupported nfs mount option:"
+ " %s%s"), progname,
+ val ? "" : "no", opt);
+ goto out_bad;
+ }
+ sprintf(cbuf, val ? "%s," : "no%s,", opt);
+ }
+ len += strlen(cbuf);
+ if (len >= opt_size) {
+ nfs_error(_("%s: excessively long option argument"),
+ progname);
+ goto out_bad;
+ }
+ strcat(new_opts, cbuf);
+ }
+ /* See if the nfs host = mount host. */
+ if (mounthost) {
+ if (!nfs_gethostbyname(mounthost, mnt_saddr))
+ goto out_bad;
+ *mnt_server->hostname = mounthost;
+ }
+ free(tmp_opts);
+ return 1;
+ bad_parameter:
+ nfs_error(_("%s: Bad nfs mount parameter: %s\n"), progname, opt);
+ out_bad:
+ free(tmp_opts);
+ free(mounthost);
+ return 0;
+}
+
+static int nfsmnt_check_compat(const struct pmap *nfs_pmap,
+ const struct pmap *mnt_pmap)
+{
+ unsigned int max_nfs_vers = (nfs_mount_data_version >= 4) ? 3 : 2;
+ unsigned int max_mnt_vers = (nfs_mount_data_version >= 4) ? 3 : 2;
+
+ if (nfs_pmap->pm_vers == 4) {
+ nfs_error(_("%s: Please use '-t nfs4' "
+ "instead of '-o vers=4'"), progname);
+ goto out_bad;
+ }
+
+ if (nfs_pmap->pm_vers) {
+ if (nfs_pmap->pm_vers > max_nfs_vers || nfs_pmap->pm_vers < 2) {
+ nfs_error(_("%s: NFS version %ld is not supported"),
+ progname, nfs_pmap->pm_vers);
+ goto out_bad;
+ }
+ }
+
+ if (mnt_pmap->pm_vers > max_mnt_vers) {
+ nfs_error(_("%s: NFS mount version %ld is not supported"),
+ progname, mnt_pmap->pm_vers);
+ goto out_bad;
+ }
+
+ return 1;
+
+out_bad:
+ return 0;
+}
+
+int
+nfsmount(const char *spec, const char *node, int flags,
+ char **extra_opts, int fake, int running_bg)
+{
+ char hostdir[1024];
+ char *hostname, *dirname, *old_opts, *mounthost = NULL;
+ char new_opts[1024], cbuf[1024];
+ static struct nfs_mount_data data;
+ int val;
+ static int doonce = 0;
+
+ clnt_addr_t mnt_server = {
+ .hostname = &mounthost
+ };
+ clnt_addr_t nfs_server = {
+ .hostname = &hostname
+ };
+ struct sockaddr_in *nfs_saddr = &nfs_server.saddr;
+ struct pmap *mnt_pmap = &mnt_server.pmap,
+ *nfs_pmap = &nfs_server.pmap;
+ struct pmap save_mnt, save_nfs;
+
+ int fsock = -1;
+
+ mntres_t mntres;
+
+ struct stat statbuf;
+ char *s;
+ int bg, retry;
+ int retval = EX_FAIL;
+ time_t t;
+ time_t prevt;
+ time_t timeout;
+
+ if (strlen(spec) >= sizeof(hostdir)) {
+ nfs_error(_("%s: excessively long host:dir argument"),
+ progname);
+ goto fail;
+ }
+ strcpy(hostdir, spec);
+ if ((s = strchr(hostdir, ':'))) {
+ hostname = hostdir;
+ dirname = s + 1;
+ *s = '\0';
+ /* Ignore all but first hostname in replicated mounts
+ until they can be fully supported. (mack@sgi.com) */
+ if ((s = strchr(hostdir, ','))) {
+ *s = '\0';
+ nfs_error(_("%s: warning: "
+ "multiple hostnames not supported"),
+ progname);
+ }
+ } else {
+ nfs_error(_("%s: directory to mount not in host:dir format"),
+ progname);
+ goto fail;
+ }
+
+ if (!nfs_gethostbyname(hostname, nfs_saddr))
+ goto fail;
+ mounthost = hostname;
+ memcpy (&mnt_server.saddr, nfs_saddr, sizeof (mnt_server.saddr));
+
+ /* add IP address to mtab options for use when unmounting */
+
+ s = inet_ntoa(nfs_saddr->sin_addr);
+ old_opts = *extra_opts;
+ if (!old_opts)
+ old_opts = "";
+
+ /* Set default options.
+ * rsize/wsize (and bsize, for ver >= 3) are left 0 in order to
+ * let the kernel decide.
+ * timeo is filled in after we know whether it'll be TCP or UDP. */
+ memset(&data, 0, sizeof(data));
+ data.acregmin = 3;
+ data.acregmax = 60;
+ data.acdirmin = 30;
+ data.acdirmax = 60;
+#if NFS_MOUNT_VERSION >= 2
+ data.namlen = NAME_MAX;
+#endif
+
+ bg = 0;
+ retry = -1;
+
+ memset(mnt_pmap, 0, sizeof(*mnt_pmap));
+ mnt_pmap->pm_prog = MOUNTPROG;
+ memset(nfs_pmap, 0, sizeof(*nfs_pmap));
+ nfs_pmap->pm_prog = NFS_PROGRAM;
+
+ /* parse options */
+ new_opts[0] = 0;
+ if (!parse_options(old_opts, &data, &bg, &retry, &mnt_server, &nfs_server,
+ new_opts, sizeof(new_opts)))
+ goto fail;
+ if (!nfsmnt_check_compat(nfs_pmap, mnt_pmap))
+ goto fail;
+
+ if (retry == -1) {
+ if (bg)
+ retry = 10000; /* 10000 mins == ~1 week*/
+ else
+ retry = 2; /* 2 min default on fg mounts */
+ }
+
+#ifdef NFS_MOUNT_DEBUG
+ printf(_("rsize = %d, wsize = %d, timeo = %d, retrans = %d\n"),
+ data.rsize, data.wsize, data.timeo, data.retrans);
+ printf(_("acreg (min, max) = (%d, %d), acdir (min, max) = (%d, %d)\n"),
+ data.acregmin, data.acregmax, data.acdirmin, data.acdirmax);
+ printf(_("port = %lu, bg = %d, retry = %d, flags = %.8x\n"),
+ nfs_pmap->pm_port, bg, retry, data.flags);
+ printf(_("mountprog = %lu, mountvers = %lu, nfsprog = %lu, nfsvers = %lu\n"),
+ mnt_pmap->pm_prog, mnt_pmap->pm_vers,
+ nfs_pmap->pm_prog, nfs_pmap->pm_vers);
+ printf(_("soft = %d, intr = %d, posix = %d, nocto = %d, noac = %d"),
+ (data.flags & NFS_MOUNT_SOFT) != 0,
+ (data.flags & NFS_MOUNT_INTR) != 0,
+ (data.flags & NFS_MOUNT_POSIX) != 0,
+ (data.flags & NFS_MOUNT_NOCTO) != 0,
+ (data.flags & NFS_MOUNT_NOAC) != 0);
+#if NFS_MOUNT_VERSION >= 2
+ printf(_(", tcp = %d"),
+ (data.flags & NFS_MOUNT_TCP) != 0);
+#endif
+#if NFS_MOUNT_VERSION >= 4
+ printf(_(", noacl = %d"), (data.flags & NFS_MOUNT_NOACL) != 0);
+#endif
+#if NFS_MOUNT_VERSION >= 5
+ printf(_(", sec = %u"), data.pseudoflavor);
+ printf(_(", readdirplus = %d"), (data.flags & NFS_MOUNT_NORDIRPLUS) != 0);
+#endif
+ printf("\n");
+#endif
+
+ data.version = nfs_mount_data_version;
+
+ if (flags & MS_REMOUNT)
+ goto out_ok;
+
+ /* create mount deamon client */
+
+ /*
+ * The following loop implements the mount retries. On the first
+ * call, "running_bg" is 0. When the mount times out, and the
+ * "bg" option is set, the exit status EX_BG will be returned.
+ * For a backgrounded mount, there will be a second call by the
+ * child process with "running_bg" set to 1.
+ *
+ * The case where the mount point is not present and the "bg"
+ * option is set, is treated as a timeout. This is done to
+ * support nested mounts.
+ *
+ * The "retry" count specified by the user is the number of
+ * minutes to retry before giving up.
+ *
+ * Only the first error message will be displayed.
+ */
+ timeout = time(NULL) + 60 * retry;
+ prevt = 0;
+ t = 30;
+ val = 1;
+
+ memcpy(&save_nfs, nfs_pmap, sizeof(save_nfs));
+ memcpy(&save_mnt, mnt_pmap, sizeof(save_mnt));
+ for (;;) {
+ if (bg && stat(node, &statbuf) == -1) {
+ /* no mount point yet - sleep */
+ if (running_bg) {
+ sleep(val); /* 1, 2, 4, 8, 16, 30, ... */
+ val *= 2;
+ if (val > 30)
+ val = 30;
+ }
+ } else {
+ int stat;
+ /* be careful not to use too many CPU cycles */
+ if (t - prevt < 30)
+ sleep(30);
+
+ stat = nfs_call_mount(&mnt_server, &nfs_server,
+ &dirname, &mntres);
+ if (stat)
+ break;
+ memcpy(nfs_pmap, &save_nfs, sizeof(*nfs_pmap));
+ memcpy(mnt_pmap, &save_mnt, sizeof(*mnt_pmap));
+ prevt = t;
+ }
+ if (!bg) {
+ switch(rpc_createerr.cf_stat){
+ case RPC_TIMEDOUT:
+ break;
+ case RPC_SYSTEMERROR:
+ if (errno == ETIMEDOUT)
+ break;
+ /* FALLTHRU */
+ default:
+ rpc_mount_errors(*nfs_server.hostname, 0, bg);
+ goto fail;
+ }
+ t = time(NULL);
+ if (t >= timeout) {
+ rpc_mount_errors(*nfs_server.hostname, 0, bg);
+ goto fail;
+ }
+ rpc_mount_errors(*nfs_server.hostname, 1, bg);
+ continue;
+ }
+ if (!running_bg) {
+ if (retry > 0)
+ retval = EX_BG;
+ goto fail;
+ }
+ t = time(NULL);
+ if (t >= timeout) {
+ rpc_mount_errors(*nfs_server.hostname, 0, bg);
+ goto fail;
+ }
+ if (doonce++ < 1)
+ rpc_mount_errors(*nfs_server.hostname, 1, bg);
+ }
+
+ if (mnt_pmap->pm_vers <= 2) {
+ if (mntres.nfsv2.fhs_status != 0) {
+ nfs_error(_("%s: %s:%s failed, reason given by server: %s"),
+ progname, hostname, dirname,
+ nfs_strerror(mntres.nfsv2.fhs_status));
+ goto fail;
+ }
+ memcpy(data.root.data,
+ (char *) mntres.nfsv2.fhstatus_u.fhs_fhandle,
+ NFS_FHSIZE);
+#if NFS_MOUNT_VERSION >= 4
+ data.root.size = NFS_FHSIZE;
+ memcpy(data.old_root.data,
+ (char *) mntres.nfsv2.fhstatus_u.fhs_fhandle,
+ NFS_FHSIZE);
+#endif
+ } else {
+#if NFS_MOUNT_VERSION >= 4
+ mountres3_ok *mountres;
+ fhandle3 *fhandle;
+ int i, n_flavors, *flavor, yum = 0;
+ if (mntres.nfsv3.fhs_status != 0) {
+ nfs_error(_("%s: %s:%s failed, reason given by server: %s"),
+ progname, hostname, dirname,
+ nfs_strerror(mntres.nfsv3.fhs_status));
+ goto fail;
+ }
+#if NFS_MOUNT_VERSION >= 5
+ mountres = &mntres.nfsv3.mountres3_u.mountinfo;
+ n_flavors = mountres->auth_flavors.auth_flavors_len;
+ if (n_flavors <= 0)
+ goto noauth_flavors;
+
+ flavor = mountres->auth_flavors.auth_flavors_val;
+ for (i = 0; i < n_flavors; ++i) {
+ /*
+ * Per RFC2623, section 2.7, we should prefer the
+ * flavour listed first.
+ * If no flavour requested, use the first simple
+ * flavour that is offered.
+ */
+ if (! (data.flags & NFS_MOUNT_SECFLAVOUR) &&
+ (flavor[i] == AUTH_SYS ||
+ flavor[i] == AUTH_NONE)) {
+ data.pseudoflavor = flavor[i];
+ data.flags |= NFS_MOUNT_SECFLAVOUR;
+ }
+ if (flavor[i] == data.pseudoflavor)
+ yum = 1;
+#ifdef NFS_MOUNT_DEBUG
+ printf(_("auth flavor %d: %d\n"), i, flavor[i]);
+#endif
+ }
+ if (!yum) {
+ nfs_error(_("%s: %s:%s failed, security flavor "
+ "not supported"),
+ progname, hostname, dirname);
+ /* server has registered us in rmtab, send umount */
+ nfs_call_umount(&mnt_server, &dirname);
+ goto fail;
+ }
+noauth_flavors:
+#endif
+ fhandle = &mntres.nfsv3.mountres3_u.mountinfo.fhandle;
+ memset(data.old_root.data, 0, NFS_FHSIZE);
+ memset(&data.root, 0, sizeof(data.root));
+ data.root.size = fhandle->fhandle3_len;
+ memcpy(data.root.data,
+ (char *) fhandle->fhandle3_val,
+ fhandle->fhandle3_len);
+
+ data.flags |= NFS_MOUNT_VER3;
+#endif
+ }
+
+ if (nfs_mount_data_version == 1) {
+ /* create nfs socket for kernel */
+ if (nfs_pmap->pm_prot == IPPROTO_TCP)
+ fsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ else
+ fsock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (fsock < 0) {
+ perror(_("nfs socket"));
+ goto fail;
+ }
+ if (bindresvport(fsock, 0) < 0) {
+ perror(_("nfs bindresvport"));
+ goto fail;
+ }
+ }
+
+#ifdef NFS_MOUNT_DEBUG
+ printf(_("using port %lu for nfs deamon\n"), nfs_pmap->pm_port);
+#endif
+ nfs_saddr->sin_port = htons(nfs_pmap->pm_port);
+ /*
+ * connect() the socket for kernels 1.3.10 and below only,
+ * to avoid problems with multihomed hosts.
+ * --Swen
+ */
+ if (linux_version_code() <= MAKE_VERSION(1, 3, 10) && fsock != -1
+ && connect(fsock, (struct sockaddr *) nfs_saddr,
+ sizeof (*nfs_saddr)) < 0) {
+ perror(_("nfs connect"));
+ goto fail;
+ }
+
+#if NFS_MOUNT_VERSION >= 2
+ if (nfs_pmap->pm_prot == IPPROTO_TCP)
+ data.flags |= NFS_MOUNT_TCP;
+ else
+ data.flags &= ~NFS_MOUNT_TCP;
+#endif
+
+ /* prepare data structure for kernel */
+
+ data.fd = fsock;
+ memcpy((char *) &data.addr, (char *) nfs_saddr, sizeof(data.addr));
+ strncpy(data.hostname, hostname, sizeof(data.hostname)-1);
+
+ out_ok:
+ /* Ensure we have enough padding for the following strcat()s */
+ if (strlen(new_opts) + strlen(s) + 30 >= sizeof(new_opts)) {
+ nfs_error(_("%s: excessively long option argument"),
+ progname);
+ goto fail;
+ }
+
+ snprintf(cbuf, sizeof(cbuf)-1, "addr=%s", s);
+ strcat(new_opts, cbuf);
+
+ *extra_opts = xstrdup(new_opts);
+
+ if (!fake && !(data.flags & NFS_MOUNT_NONLM)) {
+ if (!start_statd()) {
+ nfs_error(_("%s: rpc.statd is not running but is "
+ "required for remote locking.\n"
+ " Either use '-o nolock' to keep "
+ "locks local, or start statd."),
+ progname);
+ goto fail;
+ }
+ }
+
+ if (!fake) {
+ if (mount(spec, node, "nfs",
+ flags & ~(MS_USER|MS_USERS), &data)) {
+ mount_error(spec, node, errno);
+ goto fail;
+ }
+ }
+
+ return EX_SUCCESS;
+
+ /* abort */
+ fail:
+ if (fsock != -1)
+ close(fsock);
+ return retval;
+}
diff --git a/utils/mount/nfsmount.conf b/utils/mount/nfsmount.conf
new file mode 100644
index 0000000..c498eb8
--- /dev/null
+++ b/utils/mount/nfsmount.conf
@@ -0,0 +1,144 @@
+#
+# /etc/nfsmount.conf - see nfsmount.conf(5) for details
+#
+# This is an NFS mount configuration file. This file can be broken
+# up into three different sections: Mount, Server and Global
+#
+# [ MountPoint "Mount_point" ]
+# This section defines all the mount options that
+# should be used on a particular mount point. The '<Mount_Point>'
+# string need to be an exact match of the path in the mount
+# command. Example:
+# [ MountPoint "/export/home" ]
+# background=True
+# Would cause all mount to /export/home would be done in
+# the background
+#
+# [ Server "Server_Name" ]
+# This section defines all the mount options that
+# should be used on mounts to a particular NFS server.
+# Example:
+# [ Server "nfsserver.foo.com" ]
+# rsize=32k
+# wsize=32k
+# All reads and writes to the 'nfsserver.foo.com' server
+# will be done with 32k (32768 bytes) block sizes.
+#
+[ NFSMount_Global_Options ]
+# This statically named section defines global mount
+# options that can be applied on all NFS mount.
+#
+# Protocol Version [3,4]
+# This defines the default protocol version which will
+# be used to start the negotiation with the server.
+# Defaultvers=4
+#
+# Setting this option makes it mandatory the server supports the
+# given version. The mount will fail if the given version is
+# not support by the server.
+# Nfsvers=4
+#
+# Network Protocol [udp,tcp,rdma] (Note: values are case sensitive)
+# This defines the default network protocol which will
+# be used to start the negotiation with the server.
+# Defaultproto=tcp
+#
+# Setting this option makes it mandatory the server supports the
+# given network protocol. The mount will fail if the given network
+# protocol is not supported by the server.
+# Proto=tcp
+#
+# The number of times a request will be retired before
+# generating a timeout
+# Retrans=2
+#
+# The number of minutes that will retry mount
+# Retry=2
+#
+# The minimum time (in seconds) file attributes are cached
+# acregmin=30
+#
+# The Maximum time (in seconds) file attributes are cached
+# acregmax=60
+#
+# The minimum time (in seconds) directory attributes are cached
+# acdirmin=30
+#
+# The Maximum time (in seconds) directory attributes are cached
+# acdirmax=60
+#
+# Enable Access Control Lists
+# Acl=False
+#
+# Enable Attribute Caching
+# Ac=True
+#
+# Do mounts in background (i.e. asynchronously)
+# Background=False
+#
+# Close-To-Open cache coherence
+# Cto=True
+#
+# Do mounts in foreground (i.e. synchronously)
+# Foreground=True
+#
+# How to handle times out from servers (Hard is STRONGLY suggested)
+# Hard=True
+# Soft=False
+#
+# Enable File Locking
+# Lock=True
+#
+# Enable READDIRPLUS on NFS version 3 mounts
+# Rdirplus=True
+#
+# Maximum Read Size (in Bytes)
+# Rsize=8k
+#
+# Maximum Write Size (in Bytes)
+# Wsize=8k
+#
+# Maximum Server Block Size (in Bytes)
+# Bsize=8k
+#
+# Ignore unknown mount options
+# Sloppy=False
+#
+# Share Data and Attribute Caches
+# Sharecache=True
+#
+# The amount of time, in tenths of a seconds, the client
+# will wait for a response from the server before retransmitting
+# the request.
+# Timeo=600
+#
+# Sets all attributes times to the same time (in seconds)
+# actimeo=30
+#
+# Server Mountd port mountport
+# mountport=4001
+#
+# Server Mountd Protocol
+# mountproto=tcp
+#
+# Server Mountd Version
+# mountvers=3
+#
+# Server Mountd Host
+# mounthost=hostname
+#
+# Server Port
+# Port=2049
+#
+# RPCGSS security flavors
+# [none, sys, krb5, krb5i, krb5p ]
+# Sec=sys
+#
+# Allow Signals to interrupt file operations
+# Intr=True
+#
+# Specifies how the kernel manages its cache of directory
+# Lookupcache=all|none|pos|positive
+#
+# Turn of the caching of that access time
+# noatime=True
diff --git a/utils/mount/nfsmount.conf.man b/utils/mount/nfsmount.conf.man
new file mode 100644
index 0000000..34879c8
--- /dev/null
+++ b/utils/mount/nfsmount.conf.man
@@ -0,0 +1,131 @@
+.\" @(#)nfsmount.conf.5"
+.TH NFSMOUNT.CONF 5 "16 December 2020"
+.SH NAME
+nfsmount.conf - Configuration file for NFS mounts
+.SH SYNOPSIS
+Configuration file for NFS mounts that allows options
+to be set globally, per server or per mount point.
+.SH DESCRIPTION
+The configuration file is made up of multiple section headers
+followed by variable assignments associated with that section.
+A section header is defined by a string enclosed by
+.BR [
+and
+.BR ]
+brackets.
+Variable assignments are assignment statements that assign values
+to particular variables using the
+.BR =
+operator, as in
+.BR Proto=Tcp .
+The variables that can be assigned are the set of NFS specific
+mount options listed in
+.BR nfs (5)
+together with the filesystem-independant mount options listed in
+.BR mount (8)
+and three additions:
+.B Sloppy=True
+has the same effect as the
+.B -s
+option to
+.IR mount ,
+and
+.B Foreground=True
+and
+.B Background=True
+have the same effect as
+.B bg
+and
+.BR fg .
+.PP
+Options in the config file may be given in upper, lower, or mixed case
+and will be shifted to lower case before being passed to the filesystem.
+.PP
+Boolean mount options which do not need an equals sign must be given as
+.RI \[dq] option =True".
+Instead of preceeding such an option with
+.RB \[dq] no \[dq]
+its negation must be given as
+.RI \[dq] option =False".
+.PP
+Sections are broken up into three basic categories:
+Global options, Server options and Mount Point options.
+.HP
+.B [ NFSMount_Global_Options ]
+- This statically named section
+defines all of the global mount options that can be
+applied to every NFS mount.
+.HP
+.B [ Server \[dq]Server_Name\[dq] ]
+- This section defines all the mount options that should
+be used on mounts to a particular NFS server. The
+.I \[dq]Server_Name\[dq]
+strings needs to be surrounded by '\[dq]' and be an exact match
+(ignoring case) of the server name used in the
+.B mount
+command.
+.HP
+.B [ MountPoint \[dq]Mount_Point\[dq] ]
+- This section defines all the mount options that
+should be used on a particular mount point.
+The
+.I \[dq]Mount_Point\[dq]
+string needs to be surrounded by '\[dq]' and be an
+exact match of the mount point used in the
+.BR mount
+command. Though path names are usually case-sensitive, the Mount_Point
+name is matched insensitive to case.
+.PP
+The sections are processed in the reverse of the order listed above, and
+any options already seen, either in a previous section or on the
+command line, will be ignored when seen again.
+.SH EXAMPLES
+.PP
+These are some example lines of how sections and variables
+are defined in the configuration file.
+.PP
+[ NFSMount_Global_Options ]
+.br
+ Proto=Tcp
+.RS
+.PP
+The TCP/IPv4 protocol will be used on every NFS mount.
+.RE
+.PP
+[ Server \[dq]nfsserver.foo.com\[dq] ]
+.br
+ rsize=32k
+.br
+ wsize=32k
+.br
+ proto=udp6
+.RS
+.PP
+A 32k (32768 bytes) block size will be used as the read and write
+size on all mounts to the 'nfsserver.foo.com' server. UDP/IPv6
+is the protocol to be used.
+.RE
+.PP
+[ MountPoint \[dq]/export/home\[dq] ]
+.br
+ Background=True
+.RS
+.PP
+All mounts to the '/export/home' export will be performed in
+the background (i.e. done asynchronously).
+.RE
+.SH FILES
+.TP 10n
+.I /etc/nfsmount.conf
+Default NFS mount configuration file
+.TP 10n
+.I /etc/nfsmount.conf.d
+When this directory exists and files ending
+with ".conf" exist, those files will be
+used to set configuration variables. These
+files will override variables set
+in /etc/nfsmount.conf
+.PD
+.SH SEE ALSO
+.BR nfs (5),
+.BR mount (8),
diff --git a/utils/mount/nfsumount.c b/utils/mount/nfsumount.c
new file mode 100644
index 0000000..e16fb6a
--- /dev/null
+++ b/utils/mount/nfsumount.c
@@ -0,0 +1,351 @@
+/*
+ * nfsumount.c -- Linux NFS umount
+ * Copyright (C) 2006 Amit Gud <agud@redhat.com>
+ *
+ * - Basic code and wrapper around NFS umount code originally
+ * in util-linux/mount/nfsmount.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include <getopt.h>
+#include <mntent.h>
+#include <sys/mount.h>
+#include <ctype.h>
+#include <pwd.h>
+
+#include "xcommon.h"
+#include "fstab.h"
+#include "nls.h"
+
+#include "mount_constants.h"
+#include "nfs_mount.h"
+#include "mount.h"
+#include "error.h"
+#include "network.h"
+#include "parse_opt.h"
+#include "parse_dev.h"
+#include "utils.h"
+
+#define MOUNTSFILE "/proc/mounts"
+#define LINELEN (4096)
+
+#if !defined(MNT_FORCE)
+/* dare not try to include <linux/mount.h> -- lots of errors */
+#define MNT_FORCE 1
+#endif
+
+#if !defined(MNT_DETACH)
+#define MNT_DETACH 2
+#endif
+
+extern char *progname;
+extern int nomtab;
+extern int verbose;
+int force;
+int lazy;
+int remount;
+
+
+static int try_remount(const char *spec, const char *node)
+{
+ int res;
+
+ res = mount(spec, node, NULL,
+ MS_MGC_VAL | MS_REMOUNT | MS_RDONLY, NULL);
+ if (res == 0) {
+ struct mntent remnt;
+ nfs_error(_("%s: %s busy - remounted read-only"),
+ progname, spec);
+ remnt.mnt_type = remnt.mnt_fsname = NULL;
+ remnt.mnt_dir = xstrdup(node);
+ remnt.mnt_opts = xstrdup("ro");
+ if (!nomtab)
+ update_mtab(node, &remnt);
+ } else if (errno != EBUSY) { /* hmm ... */
+ perror(_("remount"));
+ nfs_error(_("%s: could not remount %s read-only"),
+ progname, spec);
+ }
+ return res;
+}
+
+static int del_mtab(const char *spec, const char *node)
+{
+ int umnt_err, res;
+
+ umnt_err = 0;
+ if (lazy) {
+ res = umount2 (node, MNT_DETACH);
+ if (res < 0)
+ umnt_err = errno;
+ goto writemtab;
+ }
+
+ if (force) {
+ res = umount2 (node, MNT_FORCE);
+ if (res == -1) {
+ int errsv = errno;
+ perror(_("umount2"));
+ errno = errsv;
+ if (errno == ENOSYS) {
+ if (verbose)
+ printf(_("no umount2, trying umount...\n"));
+ res = umount (node);
+ }
+ }
+ } else
+ res = umount (node);
+
+ if (res < 0) {
+ if (remount && errno == EBUSY && spec) {
+ res = try_remount(spec, node);
+ if (res)
+ goto writemtab;
+ return EX_SUCCESS;
+ } else
+ umnt_err = errno;
+ }
+
+ if (res >= 0) {
+ /* Umount succeeded */
+ if (verbose)
+ printf(_("%s umounted\n"), spec ? spec : node);
+ }
+
+ writemtab:
+ if (!nomtab &&
+ (umnt_err == 0 || umnt_err == EINVAL || umnt_err == ENOENT)) {
+ update_mtab(node, NULL);
+ }
+
+ if (res >= 0)
+ return EX_SUCCESS;
+
+ if (umnt_err)
+ umount_error(umnt_err, node);
+ return EX_FILEIO;
+}
+
+/*
+ * Detect NFSv4 mounts.
+ *
+ * Consult /proc/mounts to determine if the mount point
+ * is an NFSv4 mount. The kernel is authoritative about
+ * what type of mount this is.
+ *
+ * Returns 1 if "mc" is an NFSv4 mount, zero if not, and
+ * -1 if some error occurred.
+ */
+static int nfs_umount_is_vers4(const struct mntentchn *mc)
+{
+ struct mntentchn *pmc;
+ struct mount_options *options;
+ int retval;
+
+ retval = -1;
+ pmc = getprocmntdirbackward(mc->m.mnt_dir, NULL);
+ if (!pmc)
+ goto not_found;
+
+ do {
+ size_t nlen = strlen(pmc->m.mnt_fsname);
+
+ /*
+ * It's possible the mount location string in /proc/mounts
+ * ends with a '/'. In this case, if the entry came from
+ * /etc/mtab, it won't have the trailing '/' so deal with
+ * it.
+ */
+ while (pmc->m.mnt_fsname[nlen - 1] == '/')
+ nlen--;
+ if (strncmp(pmc->m.mnt_fsname, mc->m.mnt_fsname, nlen) != 0)
+ continue;
+
+ if (strcmp(pmc->m.mnt_type, "nfs4") == 0)
+ goto out_nfs4;
+
+ options = po_split(pmc->m.mnt_opts);
+ if (options != NULL) {
+ struct nfs_version version;
+ int rc = nfs_nfs_version("nfs", options, &version);
+ po_destroy(options);
+ if (rc && version.major == 4)
+ goto out_nfs4;
+ }
+
+ if (strcmp(pmc->m.mnt_type, "nfs") == 0)
+ goto out_nfs;
+ } while ((pmc = getprocmntdirbackward(mc->m.mnt_dir, pmc)) != NULL);
+
+ if (retval == -1) {
+not_found:
+ fprintf(stderr, "%s was not found in %s\n",
+ mc->m.mnt_dir, MOUNTSFILE);
+ goto out;
+ }
+
+out_nfs4:
+ if (verbose)
+ fprintf(stderr, "NFSv4 mount point detected\n");
+ retval = 1;
+ goto out;
+
+out_nfs:
+ if (verbose)
+ fprintf(stderr, "Legacy NFS mount point detected\n");
+ retval = 0;
+
+out:
+ return retval;
+}
+
+static struct option umount_longopts[] =
+{
+ { "force", 0, 0, 'f' },
+ { "help", 0, 0, 'h' },
+ { "no-mtab", 0, 0, 'n' },
+ { "verbose", 0, 0, 'v' },
+ { "read-only", 0, 0, 'r' },
+ { NULL, 0, 0, 0 }
+};
+
+int nfsumount(int argc, char *argv[])
+{
+ int c, ret;
+ char *spec;
+ struct mntentchn *mc;
+
+ if (argc < 2) {
+ umount_usage();
+ return EX_USAGE;
+ }
+
+ spec = argv[1];
+
+ argv += 1;
+ argc -= 1;
+
+ argv[0] = argv[-1]; /* So that getopt error messages are correct */
+ while ((c = getopt_long (argc, argv, "fvnrlh",
+ umount_longopts, NULL)) != -1) {
+
+ switch (c) {
+ case 'f':
+ ++force;
+ break;
+ case 'v':
+ ++verbose;
+ break;
+ case 'n':
+ ++nomtab;
+ break;
+ case 'r':
+ ++remount;
+ break;
+ case 'l':
+ ++lazy;
+ break;
+ case 'h':
+ default:
+ umount_usage();
+ return EX_USAGE;
+ }
+ }
+ if (optind != argc) {
+ umount_usage();
+ return EX_USAGE;
+ }
+
+ if (spec == NULL || (*spec != '/' && strchr(spec,':') == NULL)) {
+ nfs_error(_("%s: %s: not found\n"), progname, spec);
+ return EX_USAGE;
+ }
+
+ if (*spec == '/')
+ mc = getmntdirbackward(spec, NULL);
+ else
+ mc = getmntdevbackward(spec, NULL);
+ if (!mc && verbose)
+ printf(_("Could not find %s in mtab\n"), spec);
+
+ if (mc && strcmp(mc->m.mnt_type, "nfs") != 0 &&
+ strcmp(mc->m.mnt_type, "nfs4") != 0) {
+ nfs_error(_("%s: %s on %s is not an NFS filesystem"),
+ progname, mc->m.mnt_fsname, mc->m.mnt_dir);
+ return EX_USAGE;
+ }
+
+ if (getuid() != 0) {
+ /* only permitted if "user=" or "users" is in mount options */
+ if (!mc) {
+ /* umount might call us twice. The second time there will
+ * be no entry in mtab and we should just exit quietly
+ */
+ return EX_SUCCESS;
+
+ only_root:
+ nfs_error(_("%s: You are not permitted to unmount %s"),
+ progname, spec);
+ return EX_USAGE;
+ }
+ if (hasmntopt(&mc->m, "users") == NULL) {
+ char *opt = hasmntopt(&mc->m, "user");
+ struct passwd *pw;
+ char *comma;
+ size_t len;
+ if (!opt)
+ goto only_root;
+ if (opt[4] != '=')
+ goto only_root;
+ comma = strchr(opt, ',');
+ if (comma)
+ len = comma - (opt + 5);
+ else
+ len = strlen(opt+5);
+ pw = getpwuid(getuid());
+ if (pw == NULL || strlen(pw->pw_name) != len
+ || strncmp(pw->pw_name, opt+5, len) != 0)
+ goto only_root;
+ }
+ }
+
+ ret = EX_SUCCESS;
+ if (mc) {
+ if (!lazy) {
+ switch (nfs_umount_is_vers4(mc)) {
+ case 0:
+ /* We ignore the error from nfs_umount23.
+ * If the actual umount succeeds (in del_mtab),
+ * we don't want to signal an error, as that
+ * could cause /sbin/mount to retry!
+ */
+ nfs_umount23(mc->m.mnt_fsname, mc->m.mnt_opts);
+ break;
+ case 1:
+ break;
+ default:
+ return EX_FAIL;
+ }
+ }
+ ret = del_mtab(mc->m.mnt_fsname, mc->m.mnt_dir);
+ } else if (*spec != '/') {
+ if (!lazy)
+ ret = nfs_umount23(spec, "tcp,v3");
+ } else
+ ret = del_mtab(NULL, spec);
+
+ return ret;
+}
diff --git a/utils/mount/parse_dev.c b/utils/mount/parse_dev.c
new file mode 100644
index 0000000..2ade5d5
--- /dev/null
+++ b/utils/mount/parse_dev.c
@@ -0,0 +1,233 @@
+/*
+ * parse_dev.c -- parse device name into hostname and export path
+ *
+ * Copyright (C) 2008 Oracle. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "xcommon.h"
+#include "nls.h"
+#include "parse_dev.h"
+
+#ifndef NFS_MAXHOSTNAME
+#define NFS_MAXHOSTNAME (255)
+#endif
+
+#ifndef NFS_MAXPATHNAME
+#define NFS_MAXPATHNAME (1024)
+#endif
+
+extern char *progname;
+extern int verbose;
+
+static int nfs_pdn_no_devname_err(void)
+{
+ nfs_error(_("%s: no device name was provided"), progname);
+ return 0;
+}
+
+static int nfs_pdn_hostname_too_long_err(void)
+{
+ nfs_error(_("%s: server hostname is too long"), progname);
+ return 0;
+}
+
+static int nfs_pdn_pathname_too_long_err(void)
+{
+ nfs_error(_("%s: export pathname is too long"), progname);
+ return 0;
+}
+
+static int nfs_pdn_bad_format_err(void)
+{
+ nfs_error(_("%s: remote share not in 'host:dir' format"), progname);
+ return 0;
+}
+
+static int nfs_pdn_nomem_err(void)
+{
+ nfs_error(_("%s: no memory available to parse devname"), progname);
+ return 0;
+}
+
+static int nfs_pdn_missing_brace_err(void)
+{
+ nfs_error(_("%s: closing bracket missing from server address"),
+ progname);
+ return 0;
+}
+
+/*
+ * Standard hostname:path format
+ */
+static int nfs_parse_simple_hostname(const char *dev,
+ char **hostname, char **pathname)
+{
+ size_t host_len, path_len;
+ char *colon, *comma;
+
+ /* Must have a colon */
+ colon = strchr(dev, ':');
+ if (colon == NULL)
+ return nfs_pdn_bad_format_err();
+ *colon = '\0';
+ host_len = colon - dev;
+
+ if (host_len > NFS_MAXHOSTNAME)
+ return nfs_pdn_hostname_too_long_err();
+
+ /* If there's a comma before the colon, take only the
+ * first name in list */
+ comma = strchr(dev, ',');
+ if (comma != NULL) {
+ *comma = '\0';
+ host_len = comma - dev;
+ nfs_error(_("%s: warning: multiple hostnames not supported"),
+ progname);
+ } else
+
+ colon++;
+ path_len = strlen(colon);
+ if (path_len > NFS_MAXPATHNAME)
+ return nfs_pdn_pathname_too_long_err();
+
+ if (hostname) {
+ *hostname = strndup(dev, host_len);
+ if (*hostname == NULL)
+ return nfs_pdn_nomem_err();
+ }
+ if (pathname) {
+ *pathname = strndup(colon, path_len);
+ if (*pathname == NULL) {
+ if (hostname)
+ free(*hostname);
+ return nfs_pdn_nomem_err();
+ }
+ }
+ return 1;
+}
+
+/*
+ * To handle raw IPv6 addresses (which contain colons), the
+ * server's address is enclosed in square brackets. Return
+ * what's between the brackets.
+ *
+ * There could be anything in between the brackets, but we'll
+ * let DNS resolution sort it out later.
+ */
+static int nfs_parse_square_bracket(const char *dev,
+ char **hostname, char **pathname)
+{
+ size_t host_len, path_len;
+ char *cbrace;
+
+ dev++;
+
+ /* Must have a closing square bracket */
+ cbrace = strchr(dev, ']');
+ if (cbrace == NULL)
+ return nfs_pdn_missing_brace_err();
+ *cbrace = '\0';
+ host_len = cbrace - dev;
+
+ /* Must have a colon just after the closing bracket */
+ cbrace++;
+ if (*cbrace != ':')
+ return nfs_pdn_bad_format_err();
+
+ if (host_len > NFS_MAXHOSTNAME)
+ return nfs_pdn_hostname_too_long_err();
+
+ cbrace++;
+ path_len = strlen(cbrace);
+ if (path_len > NFS_MAXPATHNAME)
+ return nfs_pdn_pathname_too_long_err();
+
+ if (hostname) {
+ *hostname = strndup(dev, host_len);
+ if (*hostname == NULL)
+ return nfs_pdn_nomem_err();
+ }
+ if (pathname) {
+ *pathname = strndup(cbrace, path_len);
+ if (*pathname == NULL) {
+ if (hostname)
+ free(*hostname);
+ return nfs_pdn_nomem_err();
+ }
+ }
+ return 1;
+}
+
+/*
+ * RFC 2224 says an NFS client must grok "public file handles" to
+ * support NFS URLs. Linux doesn't do that yet. Print a somewhat
+ * helpful error message in this case instead of pressing forward
+ * with the mount request and failing with a cryptic error message
+ * later.
+ */
+static int nfs_parse_nfs_url(__attribute__((unused)) const char *dev,
+ __attribute__((unused)) char **hostname,
+ __attribute__((unused)) char **pathname)
+{
+ nfs_error(_("%s: NFS URLs are not supported"), progname);
+ return 0;
+}
+
+/**
+ * nfs_parse_devname - Determine the server's hostname by looking at "devname".
+ * @devname: pointer to mounted device name (first argument of mount command)
+ * @hostname: OUT: pointer to server's hostname
+ * @pathname: OUT: pointer to export path on server
+ *
+ * Returns 1 if succesful, or zero if some error occurred. On success,
+ * @hostname and @pathname point to dynamically allocated buffers containing
+ * the hostname of the server and the export pathname (both '\0'-terminated).
+ *
+ * @hostname or @pathname may be NULL if caller doesn't want a copy of those
+ * parts of @devname.
+ *
+ * Note that this will not work if @devname is a wide-character string.
+ */
+int nfs_parse_devname(const char *devname,
+ char **hostname, char **pathname)
+{
+ char *dev;
+ int result;
+
+ if (devname == NULL)
+ return nfs_pdn_no_devname_err();
+
+ /* Parser is destructive, so operate on a copy of the device name. */
+ dev = strdup(devname);
+ if (dev == NULL)
+ return nfs_pdn_nomem_err();
+ if (*dev == '[')
+ result = nfs_parse_square_bracket(dev, hostname, pathname);
+ else if (strncmp(dev, "nfs://", 6) == 0)
+ result = nfs_parse_nfs_url(dev, hostname, pathname);
+ else
+ result = nfs_parse_simple_hostname(dev, hostname, pathname);
+
+ free(dev);
+ return result;
+}
diff --git a/utils/mount/parse_dev.h b/utils/mount/parse_dev.h
new file mode 100644
index 0000000..f9857bc
--- /dev/null
+++ b/utils/mount/parse_dev.h
@@ -0,0 +1,28 @@
+/*
+ * parse_dev.c -- parse device name into hostname and export path
+ *
+ * Copyright (C) 2008 Oracle. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA
+ *
+ */
+
+#ifndef __NFS_UTILS_PARSE_DEV_HEADER
+#define __NFS_UTILS_PARSE_DEV_HEADER
+
+extern int nfs_parse_devname(const char *, char **, char **);
+
+#endif /* __NFS_UTILS_PARSE_DEV */
diff --git a/utils/mount/parse_opt.c b/utils/mount/parse_opt.c
new file mode 100644
index 0000000..d2d0b65
--- /dev/null
+++ b/utils/mount/parse_opt.c
@@ -0,0 +1,610 @@
+/*
+ * parse_opt.c -- mount option string parsing helpers
+ *
+ * Copyright (C) 2007 Oracle. All rights reserved.
+ * Copyright (C) 2007 Chuck Lever <chuck.lever@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA
+ *
+ */
+
+/*
+ * Converting a C string containing mount options to a data object
+ * and manipulating that object is cleaner in C than manipulating
+ * the C string itself. This is similar to the way Python handles
+ * string manipulation.
+ *
+ * The current implementation uses a linked list as the data object
+ * since lists are simple, and we don't need to worry about more
+ * than ten or twenty options at a time.
+ *
+ * Hopefully the interface is abstract enough that the underlying
+ * data structure can be replaced if needed without changing the API.
+ */
+
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "parse_opt.h"
+#include "token.h"
+
+
+struct mount_option {
+ struct mount_option *next, *prev;
+ char *keyword;
+ char *value;
+};
+
+struct mount_options {
+ struct mount_option *head, *tail;
+ unsigned int count;
+};
+
+static struct mount_option *option_create(char *str)
+{
+ struct mount_option *option;
+ char *opteq;
+
+ if (!str)
+ return NULL;
+
+ option = malloc(sizeof(*option));
+ if (!option)
+ return NULL;
+
+ option->next = NULL;
+ option->prev = NULL;
+
+ opteq = strchr(str, '=');
+ if (opteq) {
+ option->keyword = strndup(str, opteq - str);
+ if (!option->keyword)
+ goto fail;
+ option->value = strdup(opteq + 1);
+ if (!option->value) {
+ free(option->keyword);
+ goto fail;
+ }
+ } else {
+ option->keyword = strdup(str);
+ if (!option->keyword)
+ goto fail;
+ option->value = NULL;
+ }
+
+ return option;
+
+fail:
+ free(option);
+ return NULL;
+}
+
+static struct mount_option *option_dup(const struct mount_option *option)
+{
+ struct mount_option *new;
+
+ new = malloc(sizeof(*new));
+ if (!new)
+ return NULL;
+
+ new->next = NULL;
+ new->prev = NULL;
+
+ new->keyword = strdup(option->keyword);
+ if (!new->keyword)
+ goto fail;
+
+ new->value = NULL;
+ if (option->value) {
+ new->value = strdup(option->value);
+ if (!new->value) {
+ free(new->keyword);
+ goto fail;
+ }
+ }
+
+ return new;
+
+fail:
+ free(new);
+ return NULL;
+}
+
+static void option_destroy(struct mount_option *option)
+{
+ free(option->keyword);
+ free(option->value);
+ free(option);
+}
+
+static void options_init(struct mount_options *options)
+{
+ options->head = options->tail = NULL;
+ options->count = 0;
+}
+
+static struct mount_options *options_create(void)
+{
+ struct mount_options *options;
+
+ options = malloc(sizeof(*options));
+ if (options)
+ options_init(options);
+
+ return options;
+}
+
+static int options_empty(struct mount_options *options)
+{
+ return options->count == 0;
+}
+
+static void options_tail_insert(struct mount_options *options,
+ struct mount_option *option)
+{
+ struct mount_option *prev = options->tail;
+
+ option->next = NULL;
+ option->prev = prev;
+
+ if (prev)
+ prev->next = option;
+ else
+ options->head = option;
+ options->tail = option;
+
+ options->count++;
+}
+
+static void options_head_insert(struct mount_options *options,
+ struct mount_option *option)
+{
+ struct mount_option *ohead = options->head;
+
+ option->prev = NULL;
+ option->next = ohead;
+ if (ohead)
+ ohead->prev = option;
+ else
+ options->tail = option;
+ options->head = option;
+
+ options->count++;
+}
+
+static void options_delete(struct mount_options *options,
+ struct mount_option *option)
+{
+ struct mount_option *prev = option->prev;
+ struct mount_option *next = option->next;
+
+ if (!options_empty(options)) {
+ if (prev)
+ prev->next = option->next;
+ if (next)
+ next->prev = option->prev;
+
+ if (options->head == option)
+ options->head = option->next;
+ if (options->tail == option)
+ options->tail = prev;
+
+ options->count--;
+
+ option_destroy(option);
+ }
+}
+
+
+/**
+ * po_destroy - deallocate a group of mount options
+ * @options: pointer to mount options to free
+ *
+ */
+void po_destroy(struct mount_options *options)
+{
+ if (options) {
+ while (!options_empty(options))
+ options_delete(options, options->head);
+ free(options);
+ }
+}
+
+/**
+ * po_split - split options string into group of options
+ * @options: pointer to C string containing zero or more comma-delimited options
+ *
+ * Convert our mount options string to a list to make it easier
+ * to adjust the options as we go. This is just an exercise in
+ * lexical parsing -- this function doesn't pay attention to the
+ * meaning of the options themselves.
+ *
+ * Returns a new group of mount options if successful; otherwise NULL
+ * is returned if some failure occurred.
+ */
+struct mount_options *po_split(char *str)
+{
+ struct mount_options *options;
+ struct tokenizer_state *tstate;
+ char *opt;
+
+ if (!str)
+ return options_create();
+
+ options = options_create();
+ if (options) {
+ tstate = init_tokenizer(str, ',');
+ for (opt = next_token(tstate); opt; opt = next_token(tstate)) {
+ struct mount_option *option = option_create(opt);
+ free(opt);
+ if (!option)
+ goto fail;
+ options_tail_insert(options, option);
+ }
+ if (tokenizer_error(tstate))
+ goto fail;
+ end_tokenizer(tstate);
+ }
+ return options;
+
+fail:
+ end_tokenizer(tstate);
+ po_destroy(options);
+ return NULL;
+}
+
+/**
+ * po_dup - duplicate an existing list of options
+ * @options: pointer to mount options
+ *
+ */
+struct mount_options *po_dup(struct mount_options *source)
+{
+ struct mount_options *target;
+ struct mount_option *current;
+
+ if (!source)
+ return NULL;
+
+ target = options_create();
+ if (options_empty(source) || target == NULL)
+ return target;
+
+ current = source->head;
+ while (target->count < source->count) {
+ struct mount_option *option;
+
+ option = option_dup(current);
+ if (!option) {
+ po_destroy(target);
+ return NULL;
+ }
+
+ options_tail_insert(target, option);
+ current = current->next;
+ }
+
+ return target;
+}
+
+/**
+ * po_replace - replace mount options in one mount_options object with another
+ * @target: pointer to previously instantiated object to replace
+ * @source: pointer to object containing source mount options
+ *
+ * Side effect: the object referred to by source is emptied.
+ */
+void po_replace(struct mount_options *target, struct mount_options *source)
+{
+ if (target) {
+ while (!options_empty(target))
+ options_delete(target, target->head);
+
+ if (source) {
+ target->head = source->head;
+ target->tail = source->tail;
+ target->count = source->count;
+
+ options_init(source);
+ }
+ }
+}
+
+/**
+ * po_join - recombine group of mount options into a C string
+ * @options: pointer to mount options to recombine
+ * @str: handle on string to replace (input and output)
+ *
+ * Convert our mount options object back into a string that the
+ * rest of the world can use.
+ *
+ * Upon return, @string contains the address of a replacement
+ * C string containing a comma-delimited list of mount options
+ * and values; or the passed-in string is freed and NULL is
+ * returned if some failure occurred.
+ */
+po_return_t po_join(struct mount_options *options, char **str)
+{
+ size_t len = 0;
+ struct mount_option *option;
+
+ if (!str || !options)
+ return PO_FAILED;
+
+ free(*str);
+ *str = NULL;
+
+ if (options_empty(options)) {
+ *str = strdup("");
+ return *str ? PO_SUCCEEDED : PO_FAILED;
+ }
+
+ for (option = options->head; option; option = option->next) {
+ len += strlen(option->keyword);
+ if (option->value)
+ len +=strlen(option->value) + 1; /* equals sign */
+ if (option->next)
+ len++; /* comma */
+ }
+
+ len++; /* NULL on the end */
+
+ *str = malloc(len);
+ if (!*str)
+ return PO_FAILED;
+ *str[0] = '\0';
+
+ for (option = options->head; option; option = option->next) {
+ strcat(*str, option->keyword);
+ if (option->value) {
+ strcat(*str, "=");
+ strcat(*str, option->value);
+ }
+ if (option->next)
+ strcat(*str, ",");
+ }
+
+ return PO_SUCCEEDED;
+}
+
+/**
+ * po_insert - insert an option into a group of options
+ * @options: pointer to mount options
+ * @option: pointer to a C string containing the option to add
+ *
+ */
+po_return_t po_insert(struct mount_options *options, char *str)
+{
+ struct mount_option *option = option_create(str);
+
+ if (option) {
+ options_head_insert(options, option);
+ return PO_SUCCEEDED;
+ }
+ return PO_FAILED;
+}
+
+/**
+ * po_append - concatenate an option onto a group of options
+ * @options: pointer to mount options
+ * @option: pointer to a C string containing the option to add
+ *
+ */
+po_return_t po_append(struct mount_options *options, char *str)
+{
+ struct mount_option *option = option_create(str);
+
+ if (option) {
+ options_tail_insert(options, option);
+ return PO_SUCCEEDED;
+ }
+ return PO_FAILED;
+}
+
+/**
+ * po_contains - check for presence of an option in a group
+ * @options: pointer to mount options
+ * @keyword: pointer to a C string containing option keyword for which to search
+ *
+ */
+po_found_t po_contains(struct mount_options *options, char *keyword)
+{
+ struct mount_option *option;
+
+ if (options && keyword) {
+ for (option = options->head; option; option = option->next)
+ if (strcmp(option->keyword, keyword) == 0)
+ return PO_FOUND;
+ }
+
+ return PO_NOT_FOUND;
+}
+
+/**
+ * po_contains_prefix - check for presence of an option matching a prefix
+ * @options: pointer to mount options
+ * @prefix: pointer to prefix to match against a keyword
+ * @keyword: pointer to a C string containing the option keyword if found
+ * @n: number of instances to skip, so '0' returns the first.
+ *
+ * On success, *keyword contains the pointer of the matching option's keyword.
+ */
+po_found_t po_contains_prefix(struct mount_options *options,
+ const char *prefix, char **keyword, int n)
+{
+ struct mount_option *option;
+
+ if (options && prefix) {
+ for (option = options->head; option; option = option->next)
+ if (strncmp(option->keyword, prefix, strlen(prefix)) == 0) {
+ if (n > 0) {
+ n -= 1;
+ } else {
+ if (keyword)
+ *keyword = option->keyword;
+ return PO_FOUND;
+ }
+ }
+ }
+
+ return PO_NOT_FOUND;
+}
+
+/**
+ * po_get - return the value of the rightmost instance of an option
+ * @options: pointer to mount options
+ * @keyword: pointer to a C string containing option keyword for which to search
+ *
+ * If multiple instances of the same option are present in a mount option
+ * list, the rightmost instance is always the effective one.
+ *
+ * Returns pointer to C string containing the value of the option.
+ * Returns NULL if the option isn't found, or if the option doesn't
+ * have a value.
+ */
+char *po_get(struct mount_options *options, char *keyword)
+{
+ struct mount_option *option;
+
+ if (options && keyword) {
+ for (option = options->tail; option; option = option->prev)
+ if (strcmp(option->keyword, keyword) == 0)
+ return option->value;
+ }
+
+ return NULL;
+}
+
+/**
+ * po_get_numeric - return numeric value of rightmost instance of keyword option
+ * @options: pointer to mount options
+ * @keyword: pointer to a C string containing option keyword for which to search
+ * @value: OUT: set to the value of the keyword
+ *
+ * This is specifically for parsing keyword options that take only a numeric
+ * value. If multiple instances of the same option are present in a mount
+ * option list, the rightmost instance is always the effective one.
+ *
+ * Returns:
+ * * PO_FOUND if the keyword was found and the value is numeric; @value is
+ * set to the keyword's value
+ * * PO_NOT_FOUND if the keyword was not found
+ * * PO_BAD_VALUE if the keyword was found, but the value is not numeric
+ *
+ * These last two are separate in case the caller wants to warn about bad mount
+ * options instead of silently using a default.
+ */
+#ifdef HAVE_STRTOL
+po_found_t po_get_numeric(struct mount_options *options, char *keyword, long *value)
+{
+ char *option, *endptr;
+ long tmp;
+
+ option = po_get(options, keyword);
+ if (option == NULL)
+ return PO_NOT_FOUND;
+
+ errno = 0;
+ tmp = strtol(option, &endptr, 10);
+ if (errno == 0 && endptr != option) {
+ *value = tmp;
+ return PO_FOUND;
+ }
+ return PO_BAD_VALUE;
+}
+#else /* HAVE_STRTOL */
+po_found_t po_get_numeric(struct mount_options *options, char *keyword, long *value)
+{
+ char *option;
+
+ option = po_get(options, keyword);
+ if (option == NULL)
+ return PO_NOT_FOUND;
+
+ *value = atoi(option);
+ return PO_FOUND;
+}
+#endif /* HAVE_STRTOL */
+
+/**
+ * po_rightmost - determine the relative position of several options
+ * @options: pointer to mount options
+ * @keys: pointer to an array of C strings containing option keywords
+ *
+ * This function can be used to determine which of several similar
+ * options will be the one to take effect.
+ *
+ * The kernel parses the mount option string from left to right.
+ * If an option is specified more than once (for example, "intr"
+ * and "nointr", the rightmost option is the last to be parsed,
+ * and it therefore takes precedence over previous similar options.
+ *
+ * This can also distinguish among multiple synonymous options, such
+ * as "proto=," "udp" and "tcp."
+ *
+ * Returns the index into @keys of the option that is rightmost.
+ * If none of the options listed in @keys is present in @options, or
+ * if @options is NULL, returns -1.
+ */
+int po_rightmost(struct mount_options *options, const char *keys[])
+{
+ struct mount_option *option;
+ int i;
+
+ if (options) {
+ for (option = options->tail; option; option = option->prev) {
+ for (i = 0; keys[i] != NULL; i++)
+ if (strcmp(option->keyword, keys[i]) == 0)
+ return i;
+ }
+ }
+
+ return -1;
+}
+
+/**
+ * po_remove_all - remove instances of an option from a group
+ * @options: pointer to mount options
+ * @keyword: pointer to a C string containing an option keyword to remove
+ *
+ * Side-effect: the passed-in list is truncated on success.
+ */
+po_found_t po_remove_all(struct mount_options *options, char *keyword)
+{
+ struct mount_option *option, *next;
+ int found = PO_NOT_FOUND;
+
+ if (options && keyword) {
+ for (option = options->head; option; option = next) {
+ next = option->next;
+ if (strcmp(option->keyword, keyword) == 0) {
+ options_delete(options, option);
+ found = PO_FOUND;
+ }
+ }
+ }
+
+ return found;
+}
diff --git a/utils/mount/parse_opt.h b/utils/mount/parse_opt.h
new file mode 100644
index 0000000..181e7bf
--- /dev/null
+++ b/utils/mount/parse_opt.h
@@ -0,0 +1,60 @@
+/*
+ * parse_opt.h -- mount option string parsing helpers
+ *
+ * Copyright (C) 2007 Oracle. All rights reserved.
+ * Copyright (C) 2007 Chuck Lever <chuck.lever@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA
+ *
+ */
+
+#ifndef _NFS_UTILS_PARSE_OPT_H
+#define _NFS_UTILS_PARSE_OPT_H
+
+typedef enum {
+ PO_FAILED = 0,
+ PO_SUCCEEDED = 1,
+} po_return_t;
+
+typedef enum {
+ PO_NOT_FOUND = 0,
+ PO_FOUND = 1,
+ PO_BAD_VALUE = 2,
+} po_found_t;
+
+struct mount_options;
+
+struct mount_options * po_split(char *);
+struct mount_options * po_dup(struct mount_options *);
+void po_replace(struct mount_options *,
+ struct mount_options *);
+po_return_t po_join(struct mount_options *, char **);
+
+po_return_t po_insert(struct mount_options *, char *);
+po_return_t po_append(struct mount_options *, char *);
+po_found_t po_contains(struct mount_options *, char *);
+po_found_t po_contains_prefix(struct mount_options *options,
+ const char *prefix, char **keyword,
+ int n);
+char * po_get(struct mount_options *, char *);
+po_found_t po_get_numeric(struct mount_options *,
+ char *, long *);
+int po_rightmost(struct mount_options *,
+ const char *keys[]);
+po_found_t po_remove_all(struct mount_options *, char *);
+void po_destroy(struct mount_options *);
+
+#endif /* _NFS_UTILS_PARSE_OPT_H */
diff --git a/utils/mount/stropts.c b/utils/mount/stropts.c
new file mode 100644
index 0000000..dbdd11e
--- /dev/null
+++ b/utils/mount/stropts.c
@@ -0,0 +1,1310 @@
+/*
+ * stropts.c -- NFS mount using C string to pass options to kernel
+ *
+ * Copyright (C) 2007 Oracle. All rights reserved.
+ * Copyright (C) 2007 Chuck Lever <chuck.lever@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <errno.h>
+#include <netdb.h>
+#include <time.h>
+
+#include <sys/socket.h>
+#include <sys/mount.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "nfslib.h"
+#include "sockaddr.h"
+#include "xcommon.h"
+#include "mount.h"
+#include "nls.h"
+#include "nfsrpc.h"
+#include "mount_constants.h"
+#include "stropts.h"
+#include "error.h"
+#include "network.h"
+#include "parse_opt.h"
+#include "version.h"
+#include "parse_dev.h"
+#include "conffile.h"
+#include "misc.h"
+
+#ifndef NFS_PROGRAM
+#define NFS_PROGRAM (100003)
+#endif
+
+#ifndef NFS_PORT
+#define NFS_PORT (2049)
+#endif
+
+#ifndef NFS_MAXHOSTNAME
+#define NFS_MAXHOSTNAME (255)
+#endif
+
+#ifndef NFS_MAXPATHNAME
+#define NFS_MAXPATHNAME (1024)
+#endif
+
+#ifndef NFS_DEF_FG_TIMEOUT_MINUTES
+#define NFS_DEF_FG_TIMEOUT_MINUTES (2u)
+#endif
+
+#ifndef NFS_DEF_BG_TIMEOUT_MINUTES
+#define NFS_DEF_BG_TIMEOUT_MINUTES (10000u)
+#endif
+
+#ifndef NFS_DEFAULT_MAJOR
+#define NFS_DEFAULT_MAJOR 4
+#endif
+#ifndef NFS_DEFAULT_MINOR
+#define NFS_DEFAULT_MINOR 2
+#endif
+
+extern int nfs_mount_data_version;
+extern char *progname;
+extern int verbose;
+extern int sloppy;
+
+struct nfsmount_info {
+ const char *spec, /* server:/path */
+ *node; /* mounted-on dir */
+ char *type; /* "nfs" or "nfs4" */
+ char *hostname; /* server's hostname */
+ struct addrinfo *address; /* server's addresses */
+ sa_family_t family; /* Address family */
+
+ struct mount_options *options; /* parsed mount options */
+ char **extra_opts; /* string for /etc/mtab */
+
+ struct nfs_version version; /* NFS version */
+ int flags, /* MS_ flags */
+ fake, /* actually do the mount? */
+ child; /* forked bg child? */
+};
+
+
+static void nfs_default_version(struct nfsmount_info *mi)
+{
+#ifdef MOUNT_CONFIG
+ extern struct nfs_version config_default_vers;
+ /*
+ * Use the default value set in the config file when
+ * the version has not been explicitly set.
+ */
+ if (config_default_vers.v_mode == V_PARSE_ERR) {
+ mi->version.v_mode = V_PARSE_ERR;
+ return;
+ }
+
+ if (mi->version.v_mode == V_DEFAULT &&
+ config_default_vers.v_mode != V_DEFAULT) {
+ mi->version.major = config_default_vers.major;
+ if (config_default_vers.v_mode == V_SPECIFIC)
+ mi->version.minor = config_default_vers.minor;
+ else
+ mi->version.minor = NFS_DEFAULT_MINOR;
+ return;
+ }
+
+ if (mi->version.v_mode == V_GENERAL) {
+ if (config_default_vers.v_mode != V_DEFAULT &&
+ mi->version.major == config_default_vers.major) {
+ if (config_default_vers.v_mode == V_SPECIFIC)
+ mi->version.minor = config_default_vers.minor;
+ else
+ mi->version.minor = NFS_DEFAULT_MINOR;
+ } else
+ mi->version.minor = NFS_DEFAULT_MINOR;
+ return;
+ }
+
+#endif /* MOUNT_CONFIG */
+ mi->version.major = NFS_DEFAULT_MAJOR;
+ mi->version.minor = NFS_DEFAULT_MINOR;
+}
+
+/*
+ * Obtain a retry timeout value based on the value of the "retry=" option.
+ *
+ * Returns a time_t timeout timestamp, in seconds.
+ */
+static time_t nfs_parse_retry_option(struct mount_options *options,
+ const time_t default_timeout)
+{
+ time_t timeout_minutes;
+ long tmp;
+
+ timeout_minutes = default_timeout;
+ switch (po_get_numeric(options, "retry", &tmp)) {
+ case PO_NOT_FOUND:
+ break;
+ case PO_FOUND:
+ if (tmp >= 0) {
+ timeout_minutes = tmp;
+ break;
+ }
+ /*FALLTHROUGH*/
+ case PO_BAD_VALUE:
+ if (verbose)
+ nfs_error(_("%s: invalid retry timeout was specified; "
+ "using default timeout"), progname);
+ break;
+ }
+
+ return time(NULL) + (timeout_minutes * 60);
+}
+
+/*
+ * Convert the passed-in sockaddr-style address to presentation
+ * format, then append an option of the form "keyword=address".
+ *
+ * Returns 1 if the option was appended successfully; otherwise zero.
+ */
+static int nfs_append_generic_address_option(const struct sockaddr *sap,
+ const socklen_t salen,
+ const char *keyword,
+ struct mount_options *options)
+{
+ char address[NI_MAXHOST];
+ char new_option[512];
+ int len;
+
+ if (!nfs_present_sockaddr(sap, salen, address, sizeof(address)))
+ goto out_err;
+
+ len = snprintf(new_option, sizeof(new_option), "%s=%s",
+ keyword, address);
+ if (len < 0 || (size_t)len >= sizeof(new_option))
+ goto out_err;
+
+ if (po_append(options, new_option) != PO_SUCCEEDED)
+ goto out_err;
+
+ return 1;
+
+out_err:
+ nfs_error(_("%s: failed to construct %s option"), progname, keyword);
+ return 0;
+}
+
+/*
+ * Append the 'addr=' option to the options string to pass a resolved
+ * server address to the kernel. After a successful mount, this address
+ * is also added to /etc/mtab for use when unmounting.
+ *
+ * If 'addr=' is already present, we strip it out. This prevents users
+ * from setting a bogus 'addr=' option themselves, and also allows bg
+ * retries to recompute the server's address, in case it has changed.
+ *
+ * Returns 1 if 'addr=' option appended successfully;
+ * otherwise zero.
+ */
+static int nfs_append_addr_option(const struct sockaddr *sap,
+ socklen_t salen,
+ struct mount_options *options)
+{
+ po_remove_all(options, "addr");
+ return nfs_append_generic_address_option(sap, salen, "addr", options);
+}
+
+/*
+ * Called to discover our address and append an appropriate 'clientaddr='
+ * option to the options string. If the supplied 'clientaddr=' value does
+ * not match either IPV4/IPv6 any or a local address, then fail the mount.
+ *
+ * Returns 1 if 'clientaddr=' option created successfully or if
+ * 'clientaddr=' option is already present; otherwise zero.
+ */
+static int nfs_append_clientaddr_option(const struct sockaddr *sap,
+ socklen_t salen,
+ struct mount_options *options)
+{
+ union nfs_sockaddr address;
+ struct sockaddr *my_addr = &address.sa;
+ socklen_t my_len = sizeof(address);
+
+ if (po_contains(options, "clientaddr") == PO_FOUND) {
+ char *addr = po_get(options, "clientaddr");
+ union nfs_sockaddr nfs_address;
+ struct sockaddr *nfs_saddr = &nfs_address.sa;
+ socklen_t nfs_salen = sizeof(nfs_address);
+
+ /* translate the input for clientaddr to nfs_sockaddr */
+ if (!nfs_string_to_sockaddr(addr, nfs_saddr, &nfs_salen))
+ return 0;
+
+ /* check for IPV4_ANY and IPV6_ANY */
+ if (nfs_is_inaddr_any(nfs_saddr))
+ return 1;
+
+ /* check if ip matches local network addresses */
+ if (!nfs_addr_matches_localips(nfs_saddr))
+ nfs_error(_("%s: [warning] supplied clientaddr=%s "
+ "does not match any existing network "
+ "addresses"), progname, addr);
+ return 1;
+ }
+
+ nfs_callback_address(sap, salen, my_addr, &my_len);
+
+ return nfs_append_generic_address_option(my_addr, my_len,
+ "clientaddr", options);
+}
+
+/*
+ * Determine whether to append a 'mountaddr=' option. The option is needed if:
+ *
+ * 1. "mounthost=" was specified, or
+ * 2. The address families for proto= and mountproto= are different.
+ */
+static int nfs_fix_mounthost_option(struct mount_options *options,
+ const char *nfs_hostname)
+{
+ union nfs_sockaddr address;
+ struct sockaddr *sap = &address.sa;
+ socklen_t salen = sizeof(address);
+ sa_family_t nfs_family, mnt_family;
+ char *mounthost;
+
+ if (!nfs_nfs_proto_family(options, &nfs_family))
+ return 0;
+ if (!nfs_mount_proto_family(options, &mnt_family))
+ return 0;
+
+ mounthost = po_get(options, "mounthost");
+ if (mounthost == NULL) {
+ if (nfs_family == mnt_family)
+ return 1;
+ mounthost = (char *)nfs_hostname;
+ }
+
+ if (!nfs_lookup(mounthost, mnt_family, sap, &salen)) {
+ nfs_error(_("%s: unable to determine mount server's address"),
+ progname);
+ return 0;
+ }
+
+ return nfs_append_generic_address_option(sap, salen,
+ "mountaddr", options);
+}
+
+/*
+ * Returns zero if the "lock" option is in effect, but statd
+ * can't be started. Otherwise, returns 1.
+ */
+static const char *nfs_lock_opttbl[] = {
+ "nolock",
+ "lock",
+ NULL,
+};
+
+static int nfs_verify_lock_option(struct mount_options *options)
+{
+ if (po_rightmost(options, nfs_lock_opttbl) == 0)
+ return 1;
+
+ if (!start_statd()) {
+ nfs_error(_("%s: rpc.statd is not running but is "
+ "required for remote locking."), progname);
+ nfs_error(_("%s: Either use '-o nolock' to keep "
+ "locks local, or start statd."), progname);
+ errno = EALREADY; /* Don't print further error message */
+ return 0;
+ }
+
+ return 1;
+}
+
+static int nfs_insert_sloppy_option(struct mount_options *options)
+{
+ if (linux_version_code() < MAKE_VERSION(2, 6, 27))
+ return 1;
+
+ if (po_contains(options, "sloppy")) {
+ po_remove_all(options, "sloppy");
+ sloppy++;
+ }
+
+ if (sloppy) {
+ if (po_insert(options, "sloppy") == PO_FAILED)
+ return 0;
+ }
+
+ return 1;
+}
+
+static int nfs_set_version(struct nfsmount_info *mi)
+{
+
+ if (!nfs_nfs_version(mi->type, mi->options, &mi->version))
+ return 0;
+
+ /*
+ * Before 2.6.32, the kernel NFS client didn't
+ * support "-t nfs vers=4" mounts, so NFS version
+ * 4 cannot be included when autonegotiating
+ * while running on those kernels.
+ */
+ if (mi->version.v_mode == V_DEFAULT &&
+ linux_version_code() <= MAKE_VERSION(2, 6, 31)) {
+ mi->version.major = 3;
+ mi->version.v_mode = V_SPECIFIC;
+ }
+
+ /*
+ * If we still don't know, check for version-specific
+ * mount options.
+ */
+ if (mi->version.v_mode == V_DEFAULT) {
+ if (po_contains(mi->options, "mounthost") ||
+ po_contains(mi->options, "mountaddr") ||
+ po_contains(mi->options, "mountvers") ||
+ po_contains(mi->options, "mountproto")) {
+ mi->version.major = 3;
+ mi->version.v_mode = V_SPECIFIC;
+ }
+ }
+
+ /*
+ * If enabled, see if the default version was
+ * set in the config file
+ */
+ if (mi->version.v_mode != V_SPECIFIC) {
+ nfs_default_version(mi);
+ /*
+ * If the version was not specifically set, it will
+ * be set by autonegotiation later, so remove it now:
+ */
+ po_remove_all(mi->options, "v4");
+ po_remove_all(mi->options, "vers");
+ po_remove_all(mi->options, "nfsvers");
+ }
+
+ if (mi->version.v_mode == V_PARSE_ERR)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Set up mandatory non-version specific NFS mount options.
+ *
+ * Returns 1 if successful; otherwise zero.
+ */
+static int nfs_validate_options(struct nfsmount_info *mi)
+{
+ /* For remount, ignore mi->spec: the kernel will. */
+ if (!(mi->flags & MS_REMOUNT) &&
+ !nfs_parse_devname(mi->spec, &mi->hostname, NULL))
+ return 0;
+
+ if (!nfs_nfs_proto_family(mi->options, &mi->family))
+ return 0;
+
+ /*
+ * A remount is not going to be able to change the server's address,
+ * nor should we try to resolve another address for the server as we
+ * may end up with a different address.
+ * A non-remount will set 'addr' from ->hostname
+ */
+ po_remove_all(mi->options, "addr");
+
+ if (!nfs_set_version(mi))
+ return 0;
+
+ if (!nfs_insert_sloppy_option(mi->options))
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Get NFS/mnt server addresses from mount options
+ *
+ * Returns 1 and fills in @nfs_saddr, @nfs_salen, @mnt_saddr, and @mnt_salen
+ * if all goes well; otherwise zero.
+ */
+static int nfs_extract_server_addresses(struct mount_options *options,
+ struct sockaddr *nfs_saddr,
+ socklen_t *nfs_salen,
+ struct sockaddr *mnt_saddr,
+ socklen_t *mnt_salen)
+{
+ char *option;
+
+ option = po_get(options, "addr");
+ if (option == NULL)
+ return 0;
+ if (!nfs_string_to_sockaddr(option, nfs_saddr, nfs_salen))
+ return 0;
+
+ option = po_get(options, "mountaddr");
+ if (option == NULL) {
+ memcpy(mnt_saddr, nfs_saddr, *nfs_salen);
+ *mnt_salen = *nfs_salen;
+ } else if (!nfs_string_to_sockaddr(option, mnt_saddr, mnt_salen))
+ return 0;
+
+ return 1;
+}
+
+static int nfs_construct_new_options(struct mount_options *options,
+ struct sockaddr *nfs_saddr,
+ struct pmap *nfs_pmap,
+ struct sockaddr *mnt_saddr,
+ struct pmap *mnt_pmap)
+{
+ char new_option[64];
+ char *netid;
+
+ po_remove_all(options, "nfsprog");
+ po_remove_all(options, "mountprog");
+
+ po_remove_all(options, "v2");
+ po_remove_all(options, "v3");
+ po_remove_all(options, "vers");
+ po_remove_all(options, "nfsvers");
+ snprintf(new_option, sizeof(new_option) - 1,
+ "vers=%lu", nfs_pmap->pm_vers);
+ if (po_append(options, new_option) == PO_FAILED)
+ return 0;
+
+ po_remove_all(options, "proto");
+ po_remove_all(options, "udp");
+ po_remove_all(options, "tcp");
+ netid = nfs_get_netid(nfs_saddr->sa_family, nfs_pmap->pm_prot);
+ if (netid == NULL)
+ return 0;
+ snprintf(new_option, sizeof(new_option) - 1,
+ "proto=%s", netid);
+ free(netid);
+ if (po_append(options, new_option) == PO_FAILED)
+ return 0;
+
+ if(po_remove_all(options, "port") == PO_FOUND ||
+ nfs_pmap->pm_port != NFS_PORT) {
+ snprintf(new_option, sizeof(new_option) - 1,
+ "port=%lu", nfs_pmap->pm_port);
+ if (po_append(options, new_option) == PO_FAILED)
+ return 0;
+ }
+
+ po_remove_all(options, "mountvers");
+ snprintf(new_option, sizeof(new_option) - 1,
+ "mountvers=%lu", mnt_pmap->pm_vers);
+ if (po_append(options, new_option) == PO_FAILED)
+ return 0;
+
+ po_remove_all(options, "mountproto");
+ netid = nfs_get_netid(mnt_saddr->sa_family, mnt_pmap->pm_prot);
+ if (netid == NULL)
+ return 0;
+ snprintf(new_option, sizeof(new_option) - 1,
+ "mountproto=%s", netid);
+ free(netid);
+ if (po_append(options, new_option) == PO_FAILED)
+ return 0;
+
+ po_remove_all(options, "mountport");
+ snprintf(new_option, sizeof(new_option) - 1,
+ "mountport=%lu", mnt_pmap->pm_port);
+ if (po_append(options, new_option) == PO_FAILED)
+ return 0;
+
+ return 1;
+}
+
+/*
+ * Reconstruct the mount option string based on a portmapper probe
+ * of the server. Returns one if the server's portmapper returned
+ * something we can use, otherwise zero.
+ *
+ * To handle version and transport protocol fallback properly, we
+ * need to parse some of the mount options in order to set up a
+ * portmap probe. Mount options that nfs_rewrite_pmap_mount_options()
+ * doesn't recognize are left alone.
+ *
+ * Returns TRUE if rewriting was successful; otherwise
+ * FALSE is returned if some failure occurred.
+ */
+static int
+nfs_rewrite_pmap_mount_options(struct mount_options *options, int checkv4)
+{
+ union nfs_sockaddr nfs_address;
+ struct sockaddr *nfs_saddr = &nfs_address.sa;
+ socklen_t nfs_salen = sizeof(nfs_address);
+ struct pmap nfs_pmap;
+ union nfs_sockaddr mnt_address;
+ struct sockaddr *mnt_saddr = &mnt_address.sa;
+ socklen_t mnt_salen = sizeof(mnt_address);
+ unsigned long protocol;
+ struct pmap mnt_pmap;
+
+ /* initialize structs */
+ memset(&nfs_pmap, 0, sizeof(struct pmap));
+ memset(&mnt_pmap, 0, sizeof(struct pmap));
+
+ /*
+ * Version and transport negotiation is not required
+ * and does not work for RDMA mounts.
+ */
+ if (!nfs_nfs_protocol(options, &protocol)) {
+ errno = EINVAL;
+ return 0;
+ }
+ if (protocol == NFSPROTO_RDMA)
+ goto out;
+
+ /*
+ * Extract just the options needed to contact server.
+ * Bail now if any of these have bad values.
+ */
+ if (!nfs_extract_server_addresses(options, nfs_saddr, &nfs_salen,
+ mnt_saddr, &mnt_salen)) {
+ errno = EINVAL;
+ return 0;
+ }
+ if (!nfs_options2pmap(options, &nfs_pmap, &mnt_pmap)) {
+ errno = EINVAL;
+ return 0;
+ }
+
+ /*
+ * The kernel NFS client doesn't support changing the RPC
+ * program number for these services, so force the value of
+ * these fields before probing the server's ports.
+ */
+ nfs_pmap.pm_prog = NFS_PROGRAM;
+ mnt_pmap.pm_prog = MOUNTPROG;
+
+ /*
+ * If the server's rpcbind service isn't available, we can't
+ * negotiate. Bail now if we can't contact it.
+ */
+ if (!nfs_probe_bothports(mnt_saddr, mnt_salen, &mnt_pmap,
+ nfs_saddr, nfs_salen, &nfs_pmap, checkv4)) {
+ errno = ESPIPE;
+ if (rpc_createerr.cf_stat == RPC_PROGNOTREGISTERED)
+ errno = EOPNOTSUPP;
+ else if (rpc_createerr.cf_stat == RPC_AUTHERROR)
+ errno = EACCES;
+ else if (rpc_createerr.cf_stat == RPC_TIMEDOUT)
+ errno = ETIMEDOUT;
+ else if (rpc_createerr.cf_stat == RPC_PROGVERSMISMATCH)
+ errno = EPROTONOSUPPORT;
+ else if (rpc_createerr.cf_error.re_errno != 0)
+ errno = rpc_createerr.cf_error.re_errno;
+ return 0;
+ }
+
+ if (!nfs_construct_new_options(options, nfs_saddr, &nfs_pmap,
+ mnt_saddr, &mnt_pmap)) {
+ if (rpc_createerr.cf_stat == RPC_UNKNOWNPROTO)
+ errno = EPROTONOSUPPORT;
+ else
+ errno = EINVAL;
+ return 0;
+ }
+
+out:
+ errno = 0;
+ return 1;
+}
+
+/*
+ * Do the mount(2) system call.
+ *
+ * Returns TRUE if successful, otherwise FALSE.
+ * "errno" is set to reflect the individual error.
+ */
+static int nfs_sys_mount(struct nfsmount_info *mi, struct mount_options *opts)
+{
+ char *options = NULL;
+ int result;
+
+ if (mi->fake)
+ return 1;
+
+ if (po_join(opts, &options) == PO_FAILED) {
+ errno = EIO;
+ return 0;
+ }
+
+ result = mount(mi->spec, mi->node, mi->type,
+ mi->flags & ~(MS_USER|MS_USERS), options);
+ free(options);
+
+ if (verbose && result) {
+ int save = errno;
+ nfs_error(_("%s: mount(2): %s"), progname, strerror(save));
+ errno = save;
+ }
+ return !result;
+}
+
+static int nfs_do_mount_v3v2(struct nfsmount_info *mi,
+ struct sockaddr *sap, socklen_t salen,
+ int checkv4)
+{
+ struct mount_options *options = po_dup(mi->options);
+ int result = 0;
+
+ if (!options) {
+ errno = ENOMEM;
+ return result;
+ }
+ errno = 0;
+ if (!nfs_append_addr_option(sap, salen, options)) {
+ if (errno == 0)
+ errno = EINVAL;
+ goto out_fail;
+ }
+
+ if (!nfs_fix_mounthost_option(options, mi->hostname)) {
+ if (errno == 0)
+ errno = EINVAL;
+ goto out_fail;
+ }
+ if (!mi->fake && !nfs_verify_lock_option(options)) {
+ if (errno == 0)
+ errno = EINVAL;
+ goto out_fail;
+ }
+
+ /*
+ * Options we negotiate below may be stale by the time this
+ * file system is unmounted. In order to force umount.nfs
+ * to renegotiate with the server, only write the user-
+ * specified options, and not negotiated options, to /etc/mtab.
+ */
+ if (po_join(options, mi->extra_opts) == PO_FAILED) {
+ errno = ENOMEM;
+ goto out_fail;
+ }
+
+ if (verbose)
+ printf(_("%s: trying text-based options '%s'\n"),
+ progname, *mi->extra_opts);
+
+ if (!nfs_rewrite_pmap_mount_options(options, checkv4))
+ goto out_fail;
+
+ result = nfs_sys_mount(mi, options);
+
+out_fail:
+ po_destroy(options);
+ return result;
+}
+
+/*
+ * Attempt a "-t nfs vers=2" or "-t nfs vers=3" mount.
+ *
+ * Returns TRUE if successful, otherwise FALSE.
+ * "errno" is set to reflect the individual error.
+ */
+static int nfs_try_mount_v3v2(struct nfsmount_info *mi, int checkv4)
+{
+ struct addrinfo *ai;
+ int ret = 0;
+
+ for (ai = mi->address; ai != NULL; ai = ai->ai_next) {
+ ret = nfs_do_mount_v3v2(mi, ai->ai_addr, ai->ai_addrlen, checkv4);
+ if (ret != 0)
+ return ret;
+
+ switch (errno) {
+ case ECONNREFUSED:
+ case EOPNOTSUPP:
+ case EHOSTUNREACH:
+ case ETIMEDOUT:
+ case EACCES:
+ continue;
+ default:
+ goto out;
+ }
+ }
+out:
+ return ret;
+}
+
+static int nfs_do_mount_v4(struct nfsmount_info *mi,
+ struct sockaddr *sap, socklen_t salen)
+{
+ struct mount_options *options = po_dup(mi->options);
+ int result = 0;
+ char version_opt[32];
+ char *extra_opts = NULL;
+
+ if (!options) {
+ errno = ENOMEM;
+ return result;
+ }
+
+ if (po_contains(options, "mounthost") ||
+ po_contains(options, "mountaddr") ||
+ po_contains(options, "mountvers") ||
+ po_contains(options, "mountport") ||
+ po_contains(options, "mountproto")) {
+ /*
+ * Since these mountd options are set assume version 3
+ * is wanted so error out with EPROTONOSUPPORT so the
+ * protocol negation starts with v3.
+ */
+ if (verbose) {
+ printf(_("%s: Unsupported nfs4 mount option(s) passed '%s'\n"),
+ progname, *mi->extra_opts);
+ }
+ errno = EPROTONOSUPPORT;
+ goto out_fail;
+ }
+
+ if (mi->version.v_mode != V_SPECIFIC) {
+ char *fmt;
+ switch (mi->version.minor) {
+ /* Old kernels don't support the new "vers=x.y"
+ * option, but do support old versions of NFS4.
+ * So use the format that is most widely understood.
+ */
+ case 0:
+ fmt = "vers=%lu";
+ break;
+ case 1:
+ fmt = "vers=%lu,minorversion=%lu";
+ break;
+ default:
+ fmt = "vers=%lu.%lu";
+ break;
+ }
+#pragma GCC diagnostic ignored "-Wformat-nonliteral"
+ snprintf(version_opt, sizeof(version_opt) - 1,
+ fmt, mi->version.major,
+ mi->version.minor);
+#pragma GCC diagnostic warning "-Wformat-nonliteral"
+
+ if (po_append(options, version_opt) == PO_FAILED) {
+ errno = EINVAL;
+ goto out_fail;
+ }
+ } else if (po_get(options, "minorversion") &&
+ linux_version_code() > MAKE_VERSION(3, 4, 0)) {
+ /*
+ * convert minorversion= into vers=4.x
+ */
+ po_remove_all(options, "minorversion");
+
+ snprintf(version_opt, sizeof(version_opt) - 1,
+ "vers=%lu.%lu", mi->version.major,
+ mi->version.minor);
+
+ if (po_append(options, version_opt) == PO_FAILED) {
+ errno = EINVAL;
+ goto out_fail;
+ }
+ }
+
+ if (!nfs_append_addr_option(sap, salen, options)) {
+ errno = EINVAL;
+ goto out_fail;
+ }
+
+ if (!nfs_append_clientaddr_option(sap, salen, options)) {
+ errno = EINVAL;
+ goto out_fail;
+ }
+
+ if (po_join(options, &extra_opts) == PO_FAILED) {
+ errno = ENOMEM;
+ goto out_fail;
+ }
+
+ if (verbose)
+ printf(_("%s: trying text-based options '%s'\n"),
+ progname, extra_opts);
+
+ result = nfs_sys_mount(mi, options);
+
+ /*
+ * If success, update option string to be recorded in /etc/mtab.
+ */
+ if (result) {
+ free(*mi->extra_opts);
+ *mi->extra_opts = extra_opts;
+ } else
+ free(extra_opts);
+
+out_fail:
+ po_destroy(options);
+ return result;
+}
+
+/*
+ * Attempt a "-t nfs -o vers=4" or "-t nfs4" mount.
+ *
+ * Returns TRUE if successful, otherwise FALSE.
+ * "errno" is set to reflect the individual error.
+ */
+static int nfs_try_mount_v4(struct nfsmount_info *mi)
+{
+ struct addrinfo *ai;
+ int ret = 0;
+
+ for (ai = mi->address; ai != NULL; ai = ai->ai_next) {
+ ret = nfs_do_mount_v4(mi, ai->ai_addr, ai->ai_addrlen);
+ if (ret != 0)
+ return ret;
+
+ switch (errno) {
+ case ECONNREFUSED:
+ case EHOSTUNREACH:
+ case ETIMEDOUT:
+ case EACCES:
+ continue;
+ default:
+ goto out;
+ }
+ }
+out:
+ return ret;
+}
+
+/*
+ * Handle NFS version and transport protocol
+ * autonegotiation.
+ *
+ * When no version or protocol is specified on the
+ * command line, mount.nfs negotiates with the server
+ * to determine appropriate settings for the new
+ * mount point.
+ *
+ * Returns TRUE if successful, otherwise FALSE.
+ * "errno" is set to reflect the individual error.
+ */
+static int nfs_autonegotiate(struct nfsmount_info *mi)
+{
+ int result, olderrno;
+
+ result = nfs_try_mount_v4(mi);
+check_result:
+ if (result)
+ return result;
+
+ switch (errno) {
+ case EPROTONOSUPPORT:
+ /* A clear indication that the server or our
+ * client does not support NFS version 4 and minor */
+ case EINVAL:
+ /* A less clear indication that our client
+ * does not support NFSv4 minor version. */
+ case EACCES:
+ /* An unclear indication that the server
+ * may not support NFSv4 minor version. */
+ if (mi->version.v_mode != V_SPECIFIC) {
+ if (mi->version.minor > 0) {
+ mi->version.minor--;
+ result = nfs_try_mount_v4(mi);
+ goto check_result;
+ }
+ }
+
+ goto fall_back;
+ case ENOENT:
+ /* Legacy Linux servers don't export an NFS
+ * version 4 pseudoroot. */
+ goto fall_back;
+ case EPERM:
+ /* Linux servers prior to 2.6.25 may return
+ * EPERM when NFS version 4 is not supported. */
+ goto fall_back;
+ case ECONNREFUSED:
+ /* UDP-Only servers won't support v4, but maybe it
+ * just isn't ready yet. So try v3, but double-check
+ * with rpcbind for v4. */
+ if (mi->version.v_mode == V_GENERAL)
+ /* Mustn't try v2,v3 */
+ return result;
+ result = nfs_try_mount_v3v2(mi, TRUE);
+ if (result == 0 && errno == EAGAIN) {
+ /* v4 server seems to be registered now. */
+ result = nfs_try_mount_v4(mi);
+ if (result == 0 && errno != ECONNREFUSED)
+ goto check_result;
+ } else if (result == 0)
+ /* Restore original errno with v3 failures */
+ errno = ECONNREFUSED;
+
+ return result;
+ default:
+ return result;
+ }
+
+fall_back:
+ if (mi->version.v_mode == V_GENERAL)
+ /* v2,3 fallback not allowed */
+ return result;
+
+ /*
+ * Save the original errno in case the v3
+ * mount fails from one of the fall_back cases.
+ * Report the first failure not the v3 mount failure
+ */
+ olderrno = errno;
+ if ((result = nfs_try_mount_v3v2(mi, FALSE)))
+ return result;
+
+ if (errno != EBUSY && errno != EACCES)
+ errno = olderrno;
+
+ return result;
+}
+
+/*
+ * This is a single pass through the fg/bg loop.
+ *
+ * Returns TRUE if successful, otherwise FALSE.
+ * "errno" is set to reflect the individual error.
+ */
+static int nfs_try_mount(struct nfsmount_info *mi)
+{
+ int result = 0;
+
+ if (mi->address == NULL) {
+ struct addrinfo hint = {
+ .ai_protocol = (int)IPPROTO_UDP,
+ };
+ int error;
+ struct addrinfo *address;
+
+ hint.ai_family = (int)mi->family;
+ error = getaddrinfo(mi->hostname, NULL, &hint, &address);
+ if (error != 0) {
+ if (error == EAI_AGAIN)
+ errno = EAGAIN;
+ else {
+ nfs_error(_("%s: Failed to resolve server %s: %s"),
+ progname, mi->hostname, gai_strerror(error));
+ errno = EALREADY;
+ }
+ return 0;
+ }
+
+ if (!nfs_append_addr_option(address->ai_addr,
+ address->ai_addrlen, mi->options)) {
+ nfs_freeaddrinfo(address);
+ errno = ENOMEM;
+ return 0;
+ }
+ mi->address = address;
+ }
+
+ switch (mi->version.major) {
+ case 3:
+ result = nfs_try_mount_v3v2(mi, FALSE);
+ break;
+ case 4:
+ if (mi->version.v_mode != V_SPECIFIC)
+ result = nfs_autonegotiate(mi);
+ else
+ result = nfs_try_mount_v4(mi);
+ break;
+ default:
+ errno = EIO;
+ }
+
+ return result;
+}
+
+/*
+ * Distinguish between permanent and temporary errors.
+ *
+ * Basically, we retry if communication with the server has
+ * failed so far, but fail immediately if there is a local
+ * error (like a bad mount option).
+ *
+ * If there is a remote error, like ESTALE or RPC_PROGNOTREGISTERED
+ * then it is probably permanent, but there is a small chance
+ * the it is temporary can we caught the server at an awkward
+ * time during start-up. So require that we see three of those
+ * before treating them as permanent.
+ * For ECONNREFUSED, wait a bit longer as there is often a longer
+ * gap between the network being ready and the NFS server starting.
+ *
+ * Returns 1 if we should fail immediately, or 0 if we
+ * should retry.
+ */
+static int nfs_is_permanent_error(int error)
+{
+ static int prev_error;
+ static int rpt_cnt;
+
+ if (error == prev_error)
+ rpt_cnt += 1;
+ else
+ rpt_cnt = 1;
+ prev_error = error;
+
+ switch (error) {
+ case ESTALE:
+ case EOPNOTSUPP: /* aka RPC_PROGNOTREGISTERED */
+ /* If two in a row, assume permanent */
+ return rpt_cnt >= 3;
+ case ECONNREFUSED:
+ /* Like the above, this can be temporary during a
+ * small window. However it is typically a larger
+ * window than for the others, and we have historically
+ * treated this as a temporary (i.e. long timeout)
+ * error with no complaints, so continue to treat
+ * it as temporary.
+ */
+ return 0; /* temporary */
+ case ETIMEDOUT:
+ case EHOSTUNREACH:
+ case EAGAIN:
+ return 0; /* temporary */
+ default:
+ return 1; /* permanent */
+ }
+}
+
+/*
+ * Handle "foreground" NFS mounts.
+ *
+ * Retry the mount request for as long as the 'retry=' option says.
+ *
+ * Returns a valid mount command exit code.
+ */
+static int nfsmount_fg(struct nfsmount_info *mi)
+{
+ unsigned int secs = 1;
+ time_t timeout;
+
+ timeout = nfs_parse_retry_option(mi->options,
+ NFS_DEF_FG_TIMEOUT_MINUTES);
+ if (verbose)
+ printf(_("%s: timeout set for %s"),
+ progname, ctime(&timeout));
+
+ for (;;) {
+ if (nfs_try_mount(mi))
+ return EX_SUCCESS;
+
+ if (errno == EBUSY && is_mountpoint(mi->node)) {
+ /*
+ * EBUSY can happen when mounting a filesystem that
+ * is already mounted or when the context= are
+ * different when using the -o sharecache
+ *
+ * Only error out in the latter case.
+ */
+ return EX_SUCCESS;
+ }
+
+ if (nfs_is_permanent_error(errno))
+ break;
+
+ if (time(NULL) > timeout)
+ break;
+
+ if (errno != ETIMEDOUT) {
+ if (sleep(secs))
+ break;
+ secs <<= 1;
+ if (secs > 10)
+ secs = 10;
+ }
+ }
+
+ mount_error(mi->spec, mi->node, errno);
+ return EX_FAIL;
+}
+
+/*
+ * Handle "background" NFS mount [first try]
+ *
+ * Returns a valid mount command exit code.
+ *
+ * EX_BG should cause the caller to fork and invoke nfsmount_child.
+ */
+static int nfsmount_parent(struct nfsmount_info *mi)
+{
+ if (nfs_try_mount(mi))
+ return EX_SUCCESS;
+
+ if (nfs_is_permanent_error(errno)) {
+ mount_error(mi->spec, mi->node, errno);
+ return EX_FAIL;
+ }
+
+ sys_mount_errors(mi->hostname, errno, 1, 1);
+ return EX_BG;
+}
+
+/*
+ * Handle "background" NFS mount [retry daemon]
+ *
+ * Returns a valid mount command exit code: EX_SUCCESS if successful,
+ * EX_FAIL if a failure occurred. There's nothing to catch the
+ * error return, though, so we use sys_mount_errors to log the
+ * failure.
+ */
+static int nfsmount_child(struct nfsmount_info *mi)
+{
+ unsigned int secs = 1;
+ time_t timeout;
+
+ timeout = nfs_parse_retry_option(mi->options,
+ NFS_DEF_BG_TIMEOUT_MINUTES);
+
+ for (;;) {
+ if (sleep(secs))
+ break;
+ secs <<= 1;
+ if (secs > 120)
+ secs = 120;
+
+ if (nfs_try_mount(mi))
+ return EX_SUCCESS;
+
+ if (nfs_is_permanent_error(errno))
+ break;
+
+ if (time(NULL) > timeout)
+ break;
+
+ sys_mount_errors(mi->hostname, errno, 1, 1);
+ };
+
+ sys_mount_errors(mi->hostname, errno, 1, 0);
+ return EX_FAIL;
+}
+
+/*
+ * Handle "background" NFS mount
+ *
+ * Returns a valid mount command exit code.
+ */
+static int nfsmount_bg(struct nfsmount_info *mi)
+{
+ if (!mi->child)
+ return nfsmount_parent(mi);
+ else
+ return nfsmount_child(mi);
+}
+
+/*
+ * Usually all that is needed for an NFS remount is to change
+ * generic mount options like "sync" or "ro". These generic
+ * options are controlled by mi->flags, not by text-based
+ * options, and no contact with the server is needed.
+ *
+ * Take care with the /etc/mtab entry for this mount; just
+ * calling update_mtab() will change an "-t nfs -o vers=4"
+ * mount to an "-t nfs -o remount" mount, and that will
+ * confuse umount.nfs.
+ *
+ * Returns a valid mount command exit code.
+ */
+static int nfs_remount(struct nfsmount_info *mi)
+{
+ if (nfs_sys_mount(mi, mi->options))
+ return EX_SUCCESS;
+ mount_error(mi->spec, mi->node, errno);
+ return EX_FAIL;
+}
+
+/*
+ * Process mount options and try a mount system call.
+ *
+ * Returns a valid mount command exit code.
+ */
+static const char *nfs_background_opttbl[] = {
+ "bg",
+ "fg",
+ NULL,
+};
+
+static int nfsmount_start(struct nfsmount_info *mi)
+{
+ if (!nfs_validate_options(mi))
+ return EX_FAIL;
+
+ /*
+ * NFS v2 has been deprecated
+ */
+ if (mi->version.major == 2) {
+ mount_error(mi->spec, mi->node, EOPNOTSUPP);
+ return EX_FAIL;
+ }
+
+ /*
+ * Avoid retry and negotiation logic when remounting
+ */
+ if (mi->flags & MS_REMOUNT)
+ return nfs_remount(mi);
+
+ if (po_rightmost(mi->options, nfs_background_opttbl) == 0)
+ return nfsmount_bg(mi);
+ else
+ return nfsmount_fg(mi);
+}
+
+/**
+ * nfsmount_string - Mount an NFS file system using C string options
+ * @spec: C string specifying remote share to mount ("hostname:path")
+ * @node: C string pathname of local mounted-on directory
+ * @type: C string that represents file system type ("nfs" or "nfs4")
+ * @flags: MS_ style mount flags
+ * @extra_opts: pointer to C string containing fs-specific mount options
+ * (input and output argument)
+ * @fake: flag indicating whether to carry out the whole operation
+ * @child: one if this is a mount daemon (bg)
+ *
+ * Returns a valid mount command exit code.
+ */
+int nfsmount_string(const char *spec, const char *node, char *type,
+ int flags, char **extra_opts, int fake, int child)
+{
+ struct nfsmount_info mi = {
+ .spec = spec,
+ .node = node,
+ .address = NULL,
+ .type = type,
+ .extra_opts = extra_opts,
+ .flags = flags,
+ .fake = fake,
+ .child = child,
+ };
+ int retval = EX_FAIL;
+
+ mi.options = po_split(*extra_opts);
+ if (mi.options) {
+ retval = nfsmount_start(&mi);
+ po_destroy(mi.options);
+ } else
+ nfs_error(_("%s: internal option parsing error"), progname);
+
+ nfs_freeaddrinfo(mi.address);
+ free(mi.hostname);
+ return retval;
+}
diff --git a/utils/mount/stropts.h b/utils/mount/stropts.h
new file mode 100644
index 0000000..6acd2ac
--- /dev/null
+++ b/utils/mount/stropts.h
@@ -0,0 +1,30 @@
+/*
+ * stropts.h -- Provide common network functions for NFS mount/umount
+ *
+ * Copyright (C) 2007 Oracle. All rights reserved.
+ * Copyright (C) 2007 Chuck Lever <chuck.lever@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA
+ *
+ */
+
+#ifndef _NFS_UTILS_MOUNT_STROPTS_H
+#define _NFS_UTILS_MOUNT_STROPTS_H
+
+int nfsmount_string(const char *, const char *, char *, int,
+ char **, int, int);
+
+#endif /* _NFS_UTILS_MOUNT_STROPTS_H */
diff --git a/utils/mount/token.c b/utils/mount/token.c
new file mode 100644
index 0000000..d7e2f4a
--- /dev/null
+++ b/utils/mount/token.c
@@ -0,0 +1,157 @@
+/*
+ * token.c -- tokenize strings, a la strtok(3)
+ *
+ * Copyright (C) 2007 Oracle. All rights reserved.
+ * Copyright (C) 2007 Chuck Lever <chuck.lever@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA
+ *
+ */
+
+/*
+ * We've constructed a simple string tokenizer that is better than
+ * strtok(3) in several ways:
+ *
+ * 1. It doesn't interfere with ongoing tokenizations using strtok(3).
+ * 2. It's re-entrant so we can nest tokenizations, if needed.
+ * 3. It can handle double-quoted delimiters (needed for 'context="sd,fslj"').
+ * 4. It doesn't alter the string we're tokenizing, so it can work
+ * on write-protected strings as well as writable strings.
+ */
+
+
+#include <ctype.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+
+#include "token.h"
+
+
+struct tokenizer_state {
+ char *pos;
+ char delimiter;
+ int error;
+};
+
+static void find_next_nondelimiter(struct tokenizer_state *tstate)
+{
+ while (*tstate->pos != '\0' && *tstate->pos == tstate->delimiter)
+ tstate->pos++;
+}
+
+static size_t find_next_delimiter(struct tokenizer_state *tstate)
+{
+ size_t len = 0;
+ int quote_seen = 0;
+
+ while (*tstate->pos != '\0') {
+ if (*tstate->pos == '"')
+ quote_seen ^= 1;
+
+ if (!quote_seen && *tstate->pos == tstate->delimiter)
+ break;
+
+ len++;
+ tstate->pos++;
+ }
+
+ /* did the string terminate before the close quote? */
+ if (quote_seen) {
+ tstate->error = EINVAL;
+ return 0;
+ }
+
+ return len;
+}
+
+/**
+ * next_token - find the next token in a string and return it
+ * @tstate: pointer to tokenizer context object
+ *
+ * Returns the next token found in the current string.
+ * Returns NULL if there are no more tokens in the string,
+ * or if an error occurs.
+ *
+ * Side effect: tstate is updated
+ */
+char *next_token(struct tokenizer_state *tstate)
+{
+ char *token;
+ size_t len;
+
+ if (!tstate || !tstate->pos || tstate->error)
+ return NULL;
+
+ find_next_nondelimiter(tstate);
+ if (*tstate->pos == '\0')
+ goto fail;
+ token = tstate->pos;
+
+ len = find_next_delimiter(tstate);
+ if (len) {
+ token = strndup(token, len);
+ if (token)
+ return token;
+ tstate->error = ENOMEM;
+ }
+
+fail:
+ tstate->pos = NULL;
+ return NULL; /* no tokens found in this string */
+}
+
+/**
+ * init_tokenizer - return an initialized tokenizer context object
+ * @string: pointer to C string
+ * @delimiter: single character that delimits tokens in @string
+ *
+ * Returns an initialized tokenizer context object
+ */
+struct tokenizer_state *init_tokenizer(char *string, char delimiter)
+{
+ struct tokenizer_state *tstate;
+
+ tstate = malloc(sizeof(*tstate));
+ if (tstate) {
+ tstate->pos = string;
+ tstate->delimiter = delimiter;
+ tstate->error = 0;
+ }
+ return tstate;
+}
+
+/**
+ * tokenizer_error - digs error value out of tokenizer context
+ * @tstate: pointer to tokenizer context object
+ *
+ */
+int tokenizer_error(struct tokenizer_state *tstate)
+{
+ return tstate ? tstate->error : 0;
+}
+
+/**
+ * end_tokenizer - free a tokenizer context object
+ * @tstate: pointer to tokenizer context object
+ *
+ */
+void end_tokenizer(struct tokenizer_state *tstate)
+{
+ free(tstate);
+}
diff --git a/utils/mount/token.h b/utils/mount/token.h
new file mode 100644
index 0000000..17a9c15
--- /dev/null
+++ b/utils/mount/token.h
@@ -0,0 +1,34 @@
+/*
+ * token.h -- tokenize strings, a la strtok(3)
+ *
+ * Copyright (C) 2007 Oracle. All rights reserved.
+ * Copyright (C) 2007 Chuck Lever <chuck.lever@oracle.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA
+ *
+ */
+
+#ifndef _NFS_UTILS_MOUNT_TOKEN_H
+#define _NFS_UTILS_MOUNT_TOKEN_H
+
+struct tokenizer_state;
+
+char *next_token(struct tokenizer_state *);
+struct tokenizer_state *init_tokenizer(char *, char);
+int tokenizer_error(struct tokenizer_state *);
+void end_tokenizer(struct tokenizer_state *);
+
+#endif /* _NFS_UTILS_MOUNT_TOKEN_H */
diff --git a/utils/mount/umount.nfs.man b/utils/mount/umount.nfs.man
new file mode 100644
index 0000000..15addfa
--- /dev/null
+++ b/utils/mount/umount.nfs.man
@@ -0,0 +1,70 @@
+.\"@(#)umount.nfs.8"
+.TH UMOUNT.NFS 8 "6 Jun 2006"
+.SH NAME
+umount.nfs, umount.nfs4 \- unmount a Network File System
+.SH SYNOPSIS
+.BI "umount.nfs" " dir" " [\-fvnrlh ]"
+.SH DESCRIPTION
+.BR umount.nfs
+and
+.BR umount.nfs4
+are a part of
+.BR nfs (5)
+utilities package, which provides NFS client functionality.
+
+.BR umount.nfs4
+and
+.BR umount.nfs
+are meant to be used by the
+.BR umount (8)
+command for unmounting NFS shares. This subcommand, however, can also be used as a standalone command with limited functionality.
+
+.I dir
+is the directory on which the file system is mounted.
+
+.SH OPTIONS
+.TP
+.BI "\-f"
+Force unmount the file system in case of unreachable NFS system.
+.TP
+.BI "\-v"
+Be verbose.
+.TP
+.BI "\-n"
+Do not update
+.I /etc/mtab.
+By default, an entry is created in
+.I /etc/mtab
+for every mounted file system. Use this option to skip deleting an entry.
+.TP
+.BI "\-r"
+In case unmounting fails, try to mount read-only.
+.TP
+.BI "\-l"
+Lazy unmount. Detach the file system from the file system hierarchy now, and cleanup all references to the file system as soon as it is not busy anymore.
+.TP
+.BI "\-h"
+Print help message.
+
+.SH NOTE
+For further information please refer
+.BR nfs (5)
+and
+.BR umount (8)
+manual pages.
+
+.SH FILES
+.TP 18n
+.I /etc/fstab
+file system table
+.TP
+.I /etc/mtab
+table of mounted file systems
+
+.PD
+.SH "SEE ALSO"
+.BR nfs (5),
+.BR umount (8),
+
+.SH "AUTHOR"
+Amit Gud <agud@redhat.com>
diff --git a/utils/mount/utils.c b/utils/mount/utils.c
new file mode 100644
index 0000000..865a4a0
--- /dev/null
+++ b/utils/mount/utils.c
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "sockaddr.h"
+#include "nfs_mount.h"
+#include "nls.h"
+#include "xcommon.h"
+#include "version.h"
+#include "error.h"
+#include "utils.h"
+#include "mount.h"
+#include "network.h"
+#include "parse_dev.h"
+
+extern int verbose;
+extern char *progname;
+
+/*
+ * Choose the version of the nfs_mount_data structure that is appropriate
+ * for the kernel that is doing the mount.
+ *
+ * NFS_MOUNT_VERSION: maximum version supported by these sources
+ * nfs_mount_data_version: maximum version supported by the running kernel
+ */
+int discover_nfs_mount_data_version(int *string_ver)
+{
+ unsigned int kernel_version = linux_version_code();
+ int ver = 0;
+
+ *string_ver = 0;
+
+ if (kernel_version) {
+ if (kernel_version < MAKE_VERSION(2, 1, 32))
+ ver = 1;
+ else if (kernel_version < MAKE_VERSION(2, 2, 18))
+ ver = 3;
+ else if (kernel_version < MAKE_VERSION(2, 3, 0))
+ ver = 4;
+ else if (kernel_version < MAKE_VERSION(2, 3, 99))
+ ver = 3;
+ else if (kernel_version < MAKE_VERSION(2, 6, 3))
+ ver = 4;
+ else
+ ver = 6;
+ }
+ if (ver > NFS_MOUNT_VERSION)
+ ver = NFS_MOUNT_VERSION;
+ else
+ if (kernel_version > MAKE_VERSION(2, 6, 22))
+ (*string_ver)++;
+
+ return ver;
+}
+
+void print_one(char *spec, char *node, char *type, char *opts)
+{
+ if (!verbose)
+ return;
+
+ if (opts)
+ printf(_("%s on %s type %s (%s)\n"), spec, node, type, opts);
+ else
+ printf(_("%s on %s type %s\n"), spec, node, type);
+}
+
+void mount_usage(void)
+{
+ printf(_("usage: %s remotetarget dir [-rvVwfnsh] [-o nfsoptions]\n"),
+ progname);
+ printf(_("options:\n"));
+ printf(_("\t-r\t\tMount file system readonly\n"));
+ printf(_("\t-v\t\tVerbose\n"));
+ printf(_("\t-V\t\tPrint version\n"));
+ printf(_("\t-w\t\tMount file system read-write\n"));
+ printf(_("\t-f\t\tFake mount, do not actually mount\n"));
+ printf(_("\t-n\t\tDo not update /etc/mtab\n"));
+ printf(_("\t-s\t\tTolerate sloppy mount options rather than fail\n"));
+ printf(_("\t-h\t\tPrint this help\n"));
+ printf(_("\tnfsoptions\tRefer to mount.nfs(8) or nfs(5)\n\n"));
+}
+
+void umount_usage(void)
+{
+ printf(_("usage: %s dir [-fvnrlh]\n"), progname);
+ printf(_("options:\n\t-f\tforce unmount\n"));
+ printf(_("\t-v\tverbose\n"));
+ printf(_("\t-n\tDo not update /etc/mtab\n"));
+ printf(_("\t-r\tremount\n"));
+ printf(_("\t-l\tlazy unmount\n"));
+ printf(_("\t-h\tprint this help\n\n"));
+}
+
+int chk_mountpoint(const char *mount_point)
+{
+ struct stat sb;
+
+ if (stat(mount_point, &sb) < 0){
+ mount_error(NULL, mount_point, errno);
+ return 1;
+ }
+ if (S_ISDIR(sb.st_mode) == 0){
+ mount_error(NULL, mount_point, ENOTDIR);
+ return 1;
+ }
+ if (getuid() != 0 && geteuid() != 0 && access(mount_point, X_OK) < 0) {
+ mount_error(NULL, mount_point, errno);
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * Pick up certain mount options used during the original mount
+ * from /etc/mtab. The basics include the server's IP address and
+ * the server pathname of the share to unregister.
+ *
+ * These options might also describe the mount port, mount protocol
+ * version, and transport protocol used to punch through a firewall.
+ * We will need this information to get through the firewall again
+ * to do the umount.
+ *
+ * Note that option parsing failures won't necessarily cause the
+ * umount request to fail. Those values will be left zero in the
+ * pmap tuple. If the GETPORT call later fails to disambiguate them,
+ * then we fail.
+ */
+int nfs_umount23(const char *devname, char *string)
+{
+ char *hostname = NULL, *dirname = NULL;
+ struct mount_options *options;
+ int result = EX_FAIL;
+
+ if (!nfs_parse_devname(devname, &hostname, &dirname))
+ return EX_USAGE;
+
+ options = po_split(string);
+ if (options) {
+ result = nfs_umount_do_umnt(options, &hostname, &dirname);
+ po_destroy(options);
+ } else
+ nfs_error(_("%s: option parsing error"), progname);
+
+ free(hostname);
+ free(dirname);
+ return result;
+}
diff --git a/utils/mount/utils.h b/utils/mount/utils.h
new file mode 100644
index 0000000..224918a
--- /dev/null
+++ b/utils/mount/utils.h
@@ -0,0 +1,36 @@
+/*
+ * utils.h -- misc utils for mount and umount
+ *
+ * Copyright (C) 2010 Karel Zak <kzak@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA
+ *
+ */
+
+#ifndef _NFS_UTILS_MOUNT_UTILS_H
+#define _NFS_UTILS_MOUNT_UTILS_H
+
+#include "parse_opt.h"
+
+int discover_nfs_mount_data_version(int *string_ver);
+void print_one(char *spec, char *node, char *type, char *opts);
+void mount_usage(void);
+void umount_usage(void);
+int chk_mountpoint(const char *mount_point);
+
+int nfs_umount23(const char *devname, char *string);
+
+#endif /* !_NFS_UTILS_MOUNT_UTILS_H */
diff --git a/utils/mount/version.h b/utils/mount/version.h
new file mode 100644
index 0000000..d7cf680
--- /dev/null
+++ b/utils/mount/version.h
@@ -0,0 +1,53 @@
+/*
+ * version.h -- get running kernel version
+ *
+ * Copyright (C) 2008 Oracle. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public
+ * License along with this program; if not, write to the
+ * Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 0211-1301 USA
+ *
+ */
+
+#ifndef _NFS_UTILS_MOUNT_VERSION_H
+#define _NFS_UTILS_MOUNT_VERSION_H
+
+#include <stdio.h>
+#include <limits.h>
+
+#include <sys/utsname.h>
+
+static inline unsigned int MAKE_VERSION(unsigned int p, unsigned int q,
+ unsigned int r)
+{
+ return (65536 * p) + (256 * q) + r;
+}
+
+static inline unsigned int linux_version_code(void)
+{
+ struct utsname my_utsname;
+ unsigned int p, q = 0, r = 0;
+
+ /* UINT_MAX as backward compatibility code should not be run */
+ if (uname(&my_utsname))
+ return UINT_MAX;
+
+ /* UINT_MAX as future versions might not start with an integer */
+ if (sscanf(my_utsname.release, "%u.%u.%u", &p, &q, &r) < 1)
+ return UINT_MAX;
+
+ return MAKE_VERSION(p, q, r);
+}
+
+#endif /* _NFS_UTILS_MOUNT_VERSION_H */