Upstream-Status: Inappropriate [other]
Upstream is not making further releases of this software.

Signed-off-by: Scott Garman <scott.a.garman@intel.com>

# Patch origin: nfs-server source RPM from openSUSE 10.3

diff -urN nfs-server-2.2beta47/.version nfs-server-2.2beta51/.version
--- nfs-server-2.2beta47/.version	Tue Sep  7 09:47:27 1999
+++ nfs-server-2.2beta51/.version	Fri Nov  8 14:45:36 2002
@@ -1 +1 @@
-2.2beta46
+2.2beta51
diff -urN nfs-server-2.2beta47/ChangeLog nfs-server-2.2beta51/ChangeLog
--- nfs-server-2.2beta47/ChangeLog	Wed Nov 10 10:17:51 1999
+++ nfs-server-2.2beta51/ChangeLog	Fri Nov  8 14:45:36 2002
@@ -1,8 +1,59 @@
+Thu Nov  9 17:03:05 2000
+
+	* No longer use OPEN_MAX
+
+	* Reworked configure.in, BUILD script no longer needed
+	  (nor functioning)
+
+	* Be more anal about matching cached fh's and real files.
+	  In addition to the psi, we also store dev/ino/type now
+	  and match that in fh_find.
+
+	* Write pidfiles
+
+	* Support nosetuid
+
+Wed Feb  9 14:52:34 2000
+
+	* auth_init.c didn't properly parse options--rot_squash
+	  which is obviously a typo was parsed as ro.
+	  Thanks to Jan Steffan for complaining about this :-)
+
+Mon Jan 31 11:48:34 2000
+
+	* Fixed Y2K bug in logging.c.
+	  Thanks to Jonathan Hankins <jhankins@homewood.k12.al.us>.
+
+Thu Dec  9 11:14:21 1999
+
+	* Fix handling of NFS-mounted and /proc directories.
+	  They weren't properly hidden.
+	  Thanks to Dick Streefland <dick_streefland@tasking.com>
+	  for the report and a first patch.
+
 Wed Nov 10 10:17:16 1999
 
 	* Security fix for buffer overflow in fh_buildpath
 	  No thanks to Mariusz who reported it to bugtraq
 	  rather than me.
+
+Wed Nov 09 17:10:00 1999
+
+	* Workaround for broken Solaris clients that can't handle
+	  atime/mtime/ctime of 0.
+	  Thanks to Frank Wuebbelin for his problem report and
+	  testing the fix.
+
+	* Fixed typo in exports.man
+
+Tue Nov  2 10:31:14 1999
+
+	* Patch for mode 0100 and 0100 executables by
+	  Michael Deutschmann <michael@talamasca.wkpowerlink.com>
+
+	* Common startup stuff for all daemons.
+	  Inspired by code sent to me by someone (sorry, I forgot
+	  your name, and the mail's gone!)
 
 Wed Sep  8 09:07:38 1999
 
diff -urN nfs-server-2.2beta47/Makefile.in nfs-server-2.2beta51/Makefile.in
--- nfs-server-2.2beta47/Makefile.in	Tue Jun 22 14:53:10 1999
+++ nfs-server-2.2beta51/Makefile.in	Fri Nov  8 14:45:36 2002
@@ -17,23 +17,30 @@
 
 #### Start of system configuration section. ####
 
-srcdir = @srcdir@
-VPATH = @srcdir@
+srcdir		= @srcdir@
+VPATH		= @srcdir@
 
-CC = @CC@
-AR = ar
-RANLIB = @RANLIB@
-
-INSTALL = @INSTALL@
-INSTALL_PROGRAM = @INSTALL_PROGRAM@ -m 755
-INSTALL_DATA = @INSTALL_DATA@
-MAKEINFO = makeinfo
-TEXI2DVI = texi2dvi
-RPCGEN = @RPCGEN@ @RPCGEN_C@
+CC		= @CC@
+AR		= ar
+RANLIB		= @RANLIB@
+
+INSTALL		= @INSTALL@
+INSTALL_PROGRAM	= @INSTALL_PROGRAM@ -m 755
+INSTALL_DATA	= @INSTALL_DATA@
+MAKEINFO	= makeinfo
+TEXI2DVI	= texi2dvi
+RPCGEN		= @RPCGEN@ @RPCGEN_C@
 
 # General compile options and libs:
-DEFS = @DEFS@ $(NFSD_DEFS)
-LIBS = libnfs.a @LIBS@
+DEFS		= @DEFS@ $(NFSD_DEFS)
+LIBS		= libnfs.a @LIBS@
+
+# Ugidd support
+UGIDD_PROG	= @UGIDD_PROG@
+UGIDD_MAN	= @UGIDD_MAN@
+
+# New inode mapping scheme
+DEVTAB_FILE	= $(install_prefix)@PATH_DEVTAB@
 
 # Compile options for nfsd:
 # CALL_PROFILING
@@ -80,9 +87,6 @@
 
 #### End of system configuration section. ####
 
-# include site-specific defintions generated by BUILD.
-include site.mk
-
 SHELL = /bin/sh
 
 SRCS		= version.c logging.c fh.c devtab.c \
@@ -96,19 +100,19 @@
 		  utimes.c mkdir.c rename.c getopt.c getopt_long.c \
 		  alloca.c mountlist.c xmalloc.c \
 		  xstrdup.c strdup.c strstr.c nfsmounted.c faccess.c \
-		  haccess.c failsafe.c signals.c
+		  haccess.c daemon.c signals.c
 XDRFILES	= mount.x nfs_prot.x
 GENFILES	= mount.h mount_xdr.c mount_svc.c nfs_prot.h nfs_prot_xdr.c \
 		  ugid.h ugid_xdr.c ugid_clnt.c
 HDRS		= system.h nfsd.h auth.h fh.h logging.h fakefsuid.h \
 		  rpcmisc.h faccess.h rquotad.h rquota.h haccess.h
-LIBHDRS		= fsusage.h getopt.h mountlist.h failsafe.h signals.h
+LIBHDRS		= fsusage.h getopt.h mountlist.h daemon.h signals.h
 MANPAGES5	= exports
 MANPAGES8p	= mountd nfsd $(UGIDD_MAN)
 MANPAGES8	= showmount
 MANPAGES	= $(MANPAGES5) $(MANPAGES8p) $(MANPAGES8)
 LIBOBJS		= version.o fsusage.o mountlist.o xmalloc.o xstrdup.o \
-		  nfsmounted.o faccess.o haccess.o failsafe.o \
+		  nfsmounted.o faccess.o haccess.o daemon.o \
 		  signals.o @LIBOBJS@ @ALLOCA@
 OBJS		= logging.o fh.o devtab.o auth_init.o auth_clnt.o auth.o
 NFSD_OBJS	= nfsd.o rpcmisc.o nfs_dispatch.o getattr.o setattr.o \
@@ -174,15 +178,13 @@
 	${srcdir}/mkinstalldirs $(bindir) $(man5dir) $(man8dir)
 
 $(rpcprefix)mountd: $(MOUNTD_OBJS) libnfs.a
-	$(CC) $(LDFLAGS) -o $@ $(MOUNTD_OBJS) $(LIBS) \
-		$(LIBWRAP_DIR) $(LIBWRAP_LIB)
+	$(CC) $(LDFLAGS) -o $@ $(MOUNTD_OBJS) $(LIBS)
 
 $(rpcprefix)nfsd: $(NFSD_OBJS) libnfs.a
 	$(CC) $(LDFLAGS) -o $@ $(NFSD_OBJS) $(LIBS)
 
 $(rpcprefix)ugidd: $(UGIDD_OBJS) libnfs.a
-	$(CC) $(LDFLAGS) -o $@ $(UGIDD_OBJS) $(LIBS) \
-		$(LIBWRAP_DIR) $(LIBWRAP_LIB)
+	$(CC) $(LDFLAGS) -o $@ $(UGIDD_OBJS) $(LIBS)
 
 showmount: $(SHOWMOUNT_OBJS) libnfs.a
 	$(CC) $(LDFLAGS) -o $@ $(SHOWMOUNT_OBJS) $(LIBS)
diff -urN nfs-server-2.2beta47/aclocal.m4 nfs-server-2.2beta51/aclocal.m4
--- nfs-server-2.2beta47/aclocal.m4	Fri Jun 11 12:04:22 1999
+++ nfs-server-2.2beta51/aclocal.m4	Fri Nov  8 14:45:36 2002
@@ -221,20 +221,14 @@
 ])dnl
 dnl *********** libwrap bug **************
 define(AC_LIBWRAP_BUG,
-  [if test -f site.mk; then
-    . ./site.mk
-  fi
-  if test ! -z "$LIBWRAP_DIR"; then
+  [if test "$ac_cv_lib_wrap_main" = yes; then
     AC_MSG_CHECKING(for link problem with libwrap.a)
     AC_CACHE_VAL(nfsd_cv_lib_wrap_bug,
-      [ac_save_LIBS=$LIBS
-      LIBS="$LIBS $LIBWRAP_DIR $LIBWRAP_LIB"
-      AC_TRY_LINK([
+      [AC_TRY_LINK([
         extern int deny_severity;
       ],[
         deny_severity=1;
       ], nfsd_cv_lib_wrap_bug=no, nfsd_cv_lib_wrap_bug=yes)
-      LIBS=$ac_save_LIBS
     ]) dnl
     AC_MSG_RESULT($nfsd_cv_lib_wrap_bug)
     test $nfsd_cv_lib_wrap_bug = yes && AC_DEFINE(HAVE_LIBWRAP_BUG)
diff -urN nfs-server-2.2beta47/auth.c nfs-server-2.2beta51/auth.c
--- nfs-server-2.2beta47/auth.c	Mon Sep 13 16:56:03 1999
+++ nfs-server-2.2beta51/auth.c	Fri Nov  8 14:45:36 2002
@@ -84,8 +84,9 @@
 					0,		/* relative links */
 					0,		/* noaccess */
 					1,		/* cross_mounts */
-					(uid_t)-2,	/* default uid */
-					(gid_t)-2,	/* default gid */
+					1,		/* allow setuid */
+					65534,		/* default uid */
+					65534,		/* default gid */
 					0,		/* no NIS domain */
 				};
 
@@ -99,8 +100,9 @@
 					0,		/* relative links */
 					0,		/* noaccess */
 					1,		/* cross_mounts */
-					(uid_t)-2,	/* default uid */
-					(gid_t)-2,	/* default gid */
+					0,		/* allow setuid */
+					65534,		/* default uid */
+					65534,		/* default gid */
 					0,		/* no NIS domain */
 				};
 
@@ -673,6 +675,7 @@
 		cpp = &unknown_clients;
 	} else {
 		cpp = &known_clients;
+		cp->clnt_addr = *(struct in_addr *) hp->h_addr;
 		auth_hash_host(cp, hp);
 	}
 	cp->next = *cpp;
diff -urN nfs-server-2.2beta47/auth.h nfs-server-2.2beta51/auth.h
--- nfs-server-2.2beta47/auth.h	Thu Apr  8 14:47:56 1999
+++ nfs-server-2.2beta51/auth.h	Fri Nov  8 14:45:36 2002
@@ -23,14 +23,6 @@
 extern char *			public_root_path;
 extern struct nfs_fh		public_root;
 
-#if defined(linux) && defined(i386) && !defined(HAVE_SETFSUID)
-#   define MAYBE_HAVE_SETFSUID
-#endif
-
-#ifdef MAYBE_HAVE_SETFSUID
-extern int			have_setfsuid;
-#endif
-
 /*
  * These externs are set in the dispatcher (dispatch.c) and auth_fh
  * (nfsd.c) so that we can determine access rights, export options,
@@ -59,6 +51,7 @@
 	int			link_relative;
 	int			noaccess;
 	int			cross_mounts;
+	int			allow_setuid;
 	uid_t			nobody_uid;
 	gid_t			nobody_gid;
 	char *			clnt_nisdomain;
@@ -112,7 +105,7 @@
 extern void	  auth_free_lists(void);
 extern nfs_client *auth_clnt(struct svc_req *rqstp);
 extern nfs_mount  *auth_path(nfs_client *, struct svc_req *, char *);
-extern void       auth_user(nfs_mount *, struct svc_req *);
+extern int	  auth_user(nfs_mount *, struct svc_req *);
 
 extern nfs_client *auth_get_client(char *);
 extern nfs_mount  *auth_match_mount(nfs_client *, char *);
diff -urN nfs-server-2.2beta47/auth_clnt.c nfs-server-2.2beta51/auth_clnt.c
--- nfs-server-2.2beta47/auth_clnt.c	Wed Nov 10 10:18:06 1999
+++ nfs-server-2.2beta51/auth_clnt.c	Fri Nov  8 14:45:36 2002
@@ -12,20 +12,17 @@
  */
 
 
+#include <sys/fsuid.h>
 #include "system.h"
 #include "nfsd.h"
-#include "fakefsuid.h"
-
-#ifndef svc_getcaller
-#define svc_getcaller(x) ((struct sockaddr_in *) &(x)->xp_rtaddr.buf)
-#endif
+#include "rpcmisc.h"
 
 
-#if defined(HAVE_SETFSUID) || defined(MAYBE_HAVE_SETFSUID)
-static void setfsids(uid_t, gid_t, gid_t *, int);
+#if defined(HAVE_SETFSUID)
+static int	setfsids(uid_t, gid_t, gid_t *, int);
 #endif
 #ifndef HAVE_SETFSUID
-static void seteids(uid_t, gid_t, gid_t *, int);
+static int	seteids(uid_t, gid_t, gid_t *, int);
 #endif
 
 uid_t		auth_uid = 0;		/* Current effective user ids */
@@ -43,6 +40,17 @@
 				short *gid, short *nrgids, int *groups);
 #endif
 
+/*
+ * The following crap is required for glibc 2.1 which has 32bit uids
+ * in user land mapped to 16bit uids in the Linux kernel
+ */
+#if defined(HAVE_BROKEN_SETFSUID)
+# define	native_uid(u)	((unsigned short)(u))
+# define	native_gid(g)	((unsigned short)(g))
+#else
+# define	native_uid(u)	(u)
+# define	native_gid(g)	(g)
+#endif
 
 /*
  * For an RPC request, look up the NFS client info along with the
@@ -92,8 +100,9 @@
 	}
 
 	if (logging_enabled(D_AUTH)) {
-		Dprintf(D_AUTH, "auth_path(%s): mount point %s, (%s%s%s%s%s)\n",
-			path, mp->path,
+		Dprintf(D_AUTH, "auth_path(%s, %s): "
+				"mount point %s, (%s%s%s%s%s)\n",
+			inet_ntoa(cp->clnt_addr), path, mp->path,
 			mp->o.all_squash? "all_squash " : (
 			 mp->o.root_squash? "root_squash " : ""),
 			(mp->o.uidmap == map_daemon)? "uidmap " : "",
@@ -105,7 +114,8 @@
 	return mp;
 }
 
-void auth_user(nfs_mount *mp, struct svc_req *rqstp)
+int
+auth_user(nfs_mount *mp, struct svc_req *rqstp)
 {
 	uid_t		cuid;
 	gid_t		cgid;
@@ -160,23 +170,18 @@
 		else if (cred_len > NGRPS)
 			cred_len = NGRPS;
 
-		cuid = luid(cred_uid, mp, rqstp);
-		cgid = lgid(cred_gid, mp, rqstp);
+		cuid = luid(native_uid(cred_uid), mp, rqstp);
+		cgid = lgid(native_gid(cred_gid), mp, rqstp);
 		clen = cred_len;
 		for (i = 0; i < cred_len; i++)
-			cgids[i] = lgid(cred_gids[i], mp, rqstp);
+			cgids[i] = lgid(native_gid(cred_gids[i]), mp, rqstp);
 	} else {
 		/* On systems that have 32bit uid_t in user space but
 		 * 16bit in the kernel, we need to truncate the
 		 * nobody ID (default -2).
 		 */
-#if !defined(HAVE_BROKEN_SETFSUID)
-		cuid = mp->o.nobody_uid;
-		cgid = mp->o.nobody_gid;
-#else
-		cuid = (unsigned short) mp->o.nobody_uid;
-		cgid = (unsigned short) mp->o.nobody_gid;
-#endif
+		cuid = native_uid(mp->o.nobody_uid);
+		cgid = native_gid(mp->o.nobody_gid);
 		/* Construct a list of one gid. */
 		cgids[0] = cgid;
 		clen = 1;
@@ -193,14 +198,9 @@
 	 * upper 16 bits set (including our default nobody uid -2).
 	 */
 #if defined(HAVE_SETFSUID)
-	setfsids(cuid, cgid, cgids, clen);
+	return setfsids(cuid, cgid, cgids, clen);
 #else
-#if defined(MAYBE_HAVE_SETFSUID)
-	if (have_setfsuid)
-		setfsids(cuid, cgid, cgids, clen);
-	else
-#endif
-		seteids(cuid, cgid, cgids, clen);
+	return seteids(cuid, cgid, cgids, clen);
 #endif
 }
 
@@ -210,6 +210,8 @@
 void
 auth_override_uid(uid_t uid)
 {
+	int	res;
+
 	/* extension hooks: */
 	efs_setfsuid(uid);
 
@@ -217,19 +219,18 @@
 	uid = (unsigned short) uid;
 #endif
 #if defined(HAVE_SETFSUID)
-	setfsuid(uid);
+	res = setfsuid(uid);
 #else
-#if defined(MAYBE_HAVE_SETFSUID)
-	if (have_setfsuid)
-		setfsuid(uid);
-	else
-#endif
-		seteuid(uid);
+	res = seteuid(uid);
 #endif
+	/* should never happen */
+	if (res < 0)
+		Dprintf(L_FATAL, "auth_override_uid(%d) failed: %s",
+				uid, strerror(errno));
 }
 
-#if defined(HAVE_SETFSUID) || defined(MAYBE_HAVE_SETFSUID)
-static void
+#if defined(HAVE_SETFSUID)
+static int
 setfsids(uid_t cred_uid, gid_t cred_gid, gid_t *cred_gids, int cred_len)
 {
 	/* extension hooks: */
@@ -238,43 +239,47 @@
 
 	/* First, set the user ID. */
 	if (auth_uid != cred_uid) {
-		if (setfsuid(cred_uid) < 0)
+		if (setfsuid(cred_uid) < 0) {
 			Dprintf(L_ERROR, "Unable to setfsuid %d: %s\n",
 			    cred_uid, strerror(errno));
-		else
-			auth_uid = cred_uid;
+			return 0;
+		}
+		auth_uid = cred_uid;
 	}
 
 	/* Next, the group ID. */
 	if (auth_gid != cred_gid) {
-		if (setfsgid(cred_gid) < 0)
+		if (setfsgid(cred_gid) < 0) {
 			Dprintf(L_ERROR, "Unable to setfsgid %d: %s\n",
 			    cred_gid, strerror(errno));
-		else
-			auth_gid = cred_gid;
+			return 0;
+		}
+		auth_gid = cred_gid;
 	}
 
 #ifdef HAVE_SETGROUPS
 	/* Finally, set the supplementary group IDs if possible. */
-	if (cred_len < 0 || cred_len > NGRPS)
+	if (cred_len < 0 || cred_len > NGRPS) {
 		Dprintf(L_ERROR, "Negative or huge cred_len: %d\n", cred_len);
-	else if (cred_len != auth_gidlen
-	    || memcmp(cred_gids, auth_gids, auth_gidlen*sizeof(gid_t))) {
-		if (setgroups(cred_len, cred_gids) < 0)
+		return 0;
+	}
+	if (cred_len != auth_gidlen
+	 || memcmp(cred_gids, auth_gids, auth_gidlen*sizeof(gid_t))) {
+		if (setgroups(cred_len, cred_gids) < 0) {
 			Dprintf(L_ERROR, "Unable to setgroups: %s\n",
 			    strerror(errno));
-		else {
-			memcpy(auth_gids, cred_gids, cred_len*sizeof(gid_t));
-			auth_gidlen = cred_len;
+			return 0;
 		}
+		memcpy(auth_gids, cred_gids, cred_len*sizeof(gid_t));
+		auth_gidlen = cred_len;
 	}
 #endif /* HAVE_SETGROUPS */
-
+	return 1;
 }
 #endif
 
 #if !defined(HAVE_SETFSUID)
-static void
+static int
 seteids(uid_t cred_uid, gid_t cred_gid, gid_t *cred_gids, int cred_len)
 {
 	/* extension hooks: */
@@ -286,52 +291,62 @@
 	/* First set the group ID. */
 	if (auth_gid != cred_gid) {
 		if (auth_uid != ROOT_UID) {
-			if (seteuid(ROOT_UID) < 0)
+			if (seteuid(ROOT_UID) < 0) {
 				Dprintf(L_ERROR, "Unable to seteuid(%d): %s\n",
 				    ROOT_UID, strerror(errno));
-			else
-				auth_uid = ROOT_UID;
+				return 0;
+			}
+			auth_uid = ROOT_UID;
 		}
-		if (setegid(cred_gid) < 0)
+		if (setegid(cred_gid) < 0) {
 			Dprintf(L_ERROR, "Unable to setegid(%d): %s\n",
 			    cred_gid, strerror(errno));
-		else
-			auth_gid = cred_gid;
+			return 0;
+		}
+		auth_gid = cred_gid;
 	}
 
 #ifdef HAVE_SETGROUPS
 	/* Next set the supplementary group IDs if possible. */
-	if (cred_len < 0 || cred_len > NGRPS)
+	if (cred_len < 0 || cred_len > NGRPS) {
 		Dprintf(L_ERROR, "Negative or huge cred_len: %d\n", cred_len);
-	else if (cred_len != auth_gidlen
-	    || memcmp(cred_gids, auth_gids, auth_gidlen*sizeof(gid_t))) {
+		return 0;
+	}
+	if (cred_len != auth_gidlen
+	 || memcmp(cred_gids, auth_gids, auth_gidlen*sizeof(gid_t))) {
 		if (auth_uid != ROOT_UID) {
-			if (seteuid(ROOT_UID) < 0)
+			if (seteuid(ROOT_UID) < 0) {
 				Dprintf(L_ERROR, "Unable to seteuid(%d): %s\n",
 				    ROOT_UID, strerror(errno));
-			else
-				auth_uid = ROOT_UID;
+				return 0;
+			}
+			auth_uid = ROOT_UID;
 		}
-		if (setgroups(cred_len, cred_gids) < 0)
+		if (setgroups(cred_len, cred_gids) < 0) {
 			Dprintf(L_ERROR, "Unable to setgroups: %s\n",
 			    strerror(errno));
-		else {
-			memcpy(auth_gids, cred_gids, cred_len*sizeof(gid_t));
-			auth_gidlen = cred_len;
+			return 0;
 		}
+		memcpy(auth_gids, cred_gids, cred_len*sizeof(gid_t));
+		auth_gidlen = cred_len;
 	}
 #endif /* HAVE_SETGROUPS */
 
 	/* Finally, set the user ID. */
 	if (auth_uid != cred_uid) {
-		if (auth_uid != ROOT_UID && seteuid(ROOT_UID) < 0)
+		if (auth_uid != ROOT_UID && seteuid(ROOT_UID) < 0) {
 			Dprintf(L_ERROR, "Unable to seteuid(%d): %s\n", 
 				ROOT_UID, strerror(errno));
-		if (seteuid(cred_uid) < 0)
+			return 0;
+		}
+		if (seteuid(cred_uid) < 0) {
 			Dprintf(L_ERROR, "Unable to seteuid(%d): %s\n",
 			    cred_uid, strerror(errno));
-		else
-			auth_uid = cred_uid;
+			return 0;
+		}
+		auth_uid = cred_uid;
 	}
+
+	return 1;
 }
 #endif
diff -urN nfs-server-2.2beta47/auth_init.c nfs-server-2.2beta51/auth_init.c
--- nfs-server-2.2beta47/auth_init.c	Mon Apr 19 14:01:21 1999
+++ nfs-server-2.2beta51/auth_init.c	Fri Nov  8 14:45:36 2002
@@ -13,7 +13,6 @@
  */
 
 #include "nfsd.h"
-#include "fakefsuid.h"
 #include <pwd.h>
 
 #define LINE_SIZE	1024
@@ -263,55 +262,63 @@
 		cp++;
 	while (*cp != terminator) {
 		kwd = cp;
-		while (isalpha(*cp) || *cp == '_' || *cp == '=') {
-			/* break out of loop after = sign */
-			if (*cp++ == '=')
-				break;
-		}
+		/* Gobble up keyword and "=" if there is one */
+		while (isalpha(*cp) || *cp == '_')
+			++cp;
+		if (*cp == '=')
+			++cp;
+
 		klen = cp - kwd;
 
 		/* process keyword */
-		if (strncmp(kwd, "secure", 6) == 0)
+#define ifkwd(n, string) \
+	if (klen == (n) && !strncmp(kwd, string, (n)))
+
+		ifkwd(2, "ro")
+			mp->o.read_only = 1;
+		else ifkwd(2, "rw")
+			mp->o.read_only = 0;
+		else ifkwd(6, "secure")
 			mp->o.secure_port = 1;
-		else if (strncmp(kwd, "insecure", 8) == 0)
+		else ifkwd(8, "insecure")
 			mp->o.secure_port = 0;
-		else if (strncmp(kwd, "root_squash", 11) == 0)
+		else ifkwd(11, "root_squash")
 			mp->o.root_squash = 1;
-		else if (strncmp(kwd, "no_root_squash", 14) == 0)
+		else ifkwd(14, "no_root_squash")
 			mp->o.root_squash = 0;
-		else if (strncmp(kwd, "ro", 2) == 0)
-			mp->o.read_only = 1;
-		else if (strncmp(kwd, "rw", 2) == 0)
-			mp->o.read_only = 0;
-		else if (strncmp(kwd, "link_relative", 13) == 0)
+		else ifkwd(13, "link_relative")
 			mp->o.link_relative = 1;
-		else if (strncmp(kwd, "link_absolute", 13) == 0)
+		else ifkwd(13, "link_absolute")
 			mp->o.link_relative = 0;
-		else if (strncmp(kwd, "map_daemon", 10) == 0)
+		else ifkwd(10, "map_daemon")
 			mp->o.uidmap = map_daemon;
-		else if (strncmp(kwd, "map_nis=", 8) == 0)
+		else ifkwd(8, "map_nis=")
 			parse_nis_uidmap(mp, &cp);
-		else if (strncmp(kwd, "map_static=", 11) == 0)
+		else ifkwd(11, "map_static=")
 			parse_static_uidmap(mp, &cp);
-		else if (strncmp(kwd, "map_identity", 12) == 0)
+		else ifkwd(12, "map_identity")
 			mp->o.uidmap = identity;
-		else if (strncmp(kwd, "all_squash", 10) == 0)
+		else ifkwd(10, "all_squash")
 			mp->o.all_squash = 1;
-		else if (strncmp(kwd, "no_all_squash", 13) == 0)
+		else ifkwd(13, "no_all_squash")
 			mp->o.all_squash = 0;
-		else if (strncmp(kwd, "noaccess", 8) == 0)
+		else ifkwd(8, "noaccess")
 			mp->o.noaccess = 1;
-		else if (strncmp(kwd, "squash_uids=", 12) == 0)
+		else ifkwd(12, "squash_uids=")
 			parse_squash(mp, 1, &cp);
-		else if (strncmp(kwd, "squash_gids=", 12) == 0)
+		else ifkwd(12, "squash_gids=")
 			parse_squash(mp, 0, &cp);
-		else if (strncmp(kwd, "anonuid=", 8) == 0)
+		else ifkwd(8, "anonuid=")
 			mp->o.nobody_uid = parse_num(&cp);
-		else if (strncmp(kwd, "anongid=", 8) == 0)
+		else ifkwd(8, "anongid=")
 			mp->o.nobody_gid = parse_num(&cp);
-		else if (strncmp(kwd, "async", 5) == 0)
+		else ifkwd(6, "setuid")
+			mp->o.allow_setuid = 1;
+		else ifkwd(8, "nosetuid")
+			mp->o.allow_setuid = 0;
+		else ifkwd(5, "async")
 			/* knfsd compatibility, ignore */;
-		else if (strncmp(kwd, "sync", 4) == 0)
+		else ifkwd(4, "sync")
 			/* knfsd compatibility, ignore */;
 		else {
 			Dprintf(L_ERROR,
@@ -566,11 +573,6 @@
 	auth_check_all_wildcards();
 	auth_sort_all_mountlists();
 	auth_log_all();
-
-#if defined(MAYBE_HAVE_SETFSUID) && !defined(HAVE_SETFSUID)
-	/* check if the a.out setfsuid syscall works on this machine */
-	have_setfsuid = (setfsuid(0) >= 0);
-#endif
 
 	auth_initialized = 1;
 }
diff -urN nfs-server-2.2beta47/config.h.in nfs-server-2.2beta51/config.h.in
--- nfs-server-2.2beta47/config.h.in	Fri Jun 11 12:01:22 1999
+++ nfs-server-2.2beta51/config.h.in	Fri Nov  8 14:45:36 2002
@@ -3,7 +3,7 @@
 /* Define if on AIX 3.
    System headers sometimes define this.
    We just want to avoid a redefinition error message.  */
-#ifndef _ALL_SOURCE
+#ifdef _ALL_SOURCE
 #undef _ALL_SOURCE
 #endif
 
diff -urN nfs-server-2.2beta47/configure.in nfs-server-2.2beta51/configure.in
--- nfs-server-2.2beta47/configure.in	Fri Jun 11 11:58:10 1999
+++ nfs-server-2.2beta51/configure.in	Fri Nov  8 14:45:36 2002
@@ -2,7 +2,36 @@
 dnl Updated for autoconf 2.
 dnl
 AC_INIT(nfsd.c)
-AC_CONFIG_HEADER(config.h)
+AC_CONFIG_HEADER(config.h site.h)
+
+dnl **************************************************************
+dnl * handle --enable options
+dnl **************************************************************
+AC_ARG_ENABLE(new-inodes,
+   [  --enable-new-inodes     Enable new-style inode inodes])
+AC_ARG_WITH(devtab,
+   [  --with-devtab=file      Specify location for devtab [/var/lib/nfs/devtab]],
+   PATH_DEVTAB=$withval,
+   PATH_DEVTAB=/var/lib/nfs/devtab)
+AC_ARG_ENABLE(ugid-dynamic,
+   [  --enable-ugid-dynamic   Enable uid mapping using rpc.ugidd (not recommended)])
+AC_ARG_ENABLE(ugid-nis,
+   [  --enable-ugid-nis       Enable NIS-based uid mapping])
+AC_ARG_ENABLE(host-access,
+   [  --enable-host-access    Enable host access checking])
+AC_ARG_ENABLE(mount-logging,
+   [  --disable-mount-logging Do not log mount operations to syslog],,
+   enable_mount_logging=yes)
+AC_ARG_WITH(exports-uid,
+   [  --with-exports-uid=N    Make sure that /etc/exports is owned by uid N],,
+   with_exports_uid=0)
+AC_ARG_WITH(exports-gid,
+   [  --with-exports-gid=N    Make sure that /etc/exports is owned by gid N],,
+   with_exports_gid=0)
+
+dnl **************************************************************
+dnl * Check for all kinds of stuff
+dnl **************************************************************
 AC_PROG_CC
 # If we're using gcc, we want warning flags
 test -n "$GCC" &&
@@ -19,7 +48,7 @@
 AC_MINIX
 AC_ISC_POSIX
 AC_PROG_INSTALL
-AC_CROSS_CHECK
+dnl AC_CROSS_CHECK
 AC_STDC_HEADERS
 AC_GNULIBC
 AC_CONST
@@ -52,14 +81,45 @@
 AC_CHECK_LIB(rpc, main)
 AC_CHECK_LIB(crypt, main)
 AC_CHECK_LIB(nys, main)
-AC_REPLACE_FUNCS(strerror realpath mkdir rename utimes strdup strstr getopt getopt_long)
 AC_HAVE_FUNCS(getcwd seteuid setreuid getdtablesize setgroups lchown setsid setfsuid setfsgid innetgr quotactl authdes_getucred)
 AC_AUTHDES_GETUCRED
 AC_BROKEN_SETFSUID
 AC_MOUNTLIST
 AC_FSUSAGE
+AC_CHECK_LIB(wrap, main)
 AC_LIBWRAP_BUG
 AC_BSD_SIGNALS
+
+dnl **************************************************************
+dnl * Munge user specified options
+dnl **************************************************************
+if test "$enable_new_inodes" = yes; then
+	AC_DEFINE(ENABLE_DEVTAB)
+fi
+if test "$enable_ugid_dynamic" = yes; then
+	AC_DEFINE(ENABLE_UGID_DAEMON)
+	UGIDD_PROG=\${rpcprefix}.ugidd
+	UGIDD_MAN=ugidd
+fi
+if test "$enable_ugid_nis" = yes; then
+	AC_DEFINE(ENABLE_UGID_NIS)
+fi
+if test "$enable_host_access" = yes; then
+	AC_DEFINE(HOSTS_ACCESS)
+fi
+if test "$enable_mount_logging" = yes; then
+	AC_DEFINE(WANT_LOG_MOUNTS)
+fi
+AC_DEFINE_UNQUOTED(EXPORTSOWNERUID, $with_exports_uid)
+AC_DEFINE_UNQUOTED(EXPORTSOWNERGID, $with_exports_gid)
+AC_SUBST(PATH_DEVTAB)
+AC_SUBST(UGIDD_PROG)
+AC_SUBST(UGIDD_MAN)
+
+dnl **************************************************************
+dnl * Output CFLAGS and LDFLAGS
+dnl **************************************************************
 AC_SUBST(LDFLAGS)
 AC_SUBST(CFLAGS)
+
 AC_OUTPUT(Makefile)
diff -urN nfs-server-2.2beta47/daemon.c nfs-server-2.2beta51/daemon.c
--- nfs-server-2.2beta47/daemon.c	Thu Jan  1 01:00:00 1970
+++ nfs-server-2.2beta51/daemon.c	Fri Nov  8 14:45:52 2002
@@ -0,0 +1,270 @@
+/*
+ * daemon.c
+ *
+ * Copyright (C) 1998, <okir@monad.swb.de>
+ *
+ * Implements common daemon stuff and
+ * fail-safe mode for nfsd/mountd.
+ */
+
+#include "system.h"
+#include "logging.h"
+#include "signals.h"
+#include <sys/wait.h>
+
+static const char *	pidfilename = 0;
+static const char *	get_signame(int signo);
+
+/*
+ * Do the Crawley Thing
+ */
+void
+daemonize(void)
+{
+	int	c;
+
+	/* Ignore SIGHUP so the parent can exit while we're still
+	 * in limbo */
+	ignore_signal(SIGHUP);
+
+	/* Now fork */
+	c = fork();
+	if (c < 0)
+		Dprintf(L_FATAL, "unable to fork: %s", strerror(errno));
+
+	/* Parent process: exit */
+	if (c > 0)
+		exit(0);
+
+	/* Do the session stuff */
+	close(0);
+	close(1);
+	close(2);
+#ifdef HAVE_SETSID
+	setsid();
+#else
+	if ((c = open("/dev/tty", O_RDWR)) >= 0) {
+		ioctl(c, TIOCNOTTY, (char *) NULL);
+		close(c);
+	}
+#endif
+
+	/* Stop stderr logging */
+	background_logging();
+}
+
+void
+setpidpath(const char *filename)
+{
+	pidfilename = filename;
+}
+
+void
+writepid(pid_t pid, int clear)
+{
+	FILE	*fp;
+
+	fp = fopen(pidfilename, clear? "w" : "a");
+	if (fp == NULL)
+		Dprintf(L_FATAL, "Unable to open %s: %m", pidfilename);
+	fprintf(fp, "%d\n", pid);
+	fclose(fp);
+	return;
+}
+
+void
+failsafe(int level, int ncopies)
+{
+	int	*servers, running, child, i;
+	int	pid, signo, status;
+	time_t	last_restart = 0, now;
+	int	restarts = 0, backoff = 60;
+
+	servers = (int *) xmalloc(ncopies * sizeof(int));
+	memset(servers, 0, ncopies * sizeof(int));
+
+	/* Loop forever, until we get SIGTERM */
+	running = 0;
+	while (1) {
+		/* Rewrite the pidfile */
+		writepid(getpid(), 1);
+		for (i = 0; i < ncopies; i++) {
+			if (servers[i] != 0)
+				writepid(servers[i], 0);
+		}
+
+		while (running < ncopies) {
+			if ((now = time(NULL)) == last_restart) {
+				if (++restarts > 2 * ncopies) {
+					Dprintf(L_ERROR,
+						"Servers restarting too "
+						"quickly, backing off.");
+					if (backoff < 60 * 60)
+						backoff <<= 1;
+					sleep(backoff);
+				}
+			} else {
+				last_restart = now;
+				restarts = 0;
+				backoff = 60;
+			}
+
+			/* Locate a free pid slot */
+			for (i = 0, child = -1; i < ncopies; i++) {
+				if (servers[i] == 0) {
+					child = i;
+					break;
+				}
+			}
+
+			if (child < 0)
+				Dprintf(L_FATAL, "failsafe: no pid slot?!");
+
+			Dprintf(D_GENERAL,
+				"starting server thread %d...\n", child + 1);
+
+			pid = fork();
+			if (pid < 0)
+				Dprintf(L_FATAL,
+					"Unable to fork for failsafe: %s",
+					strerror(errno));
+
+			if (pid == 0) {
+				/* Child process: continue with execution. */
+				return;
+			}
+
+			writepid(pid, 0);
+			servers[child] = pid;
+			running++;
+		}
+
+		/* Ignore some signals */
+		ignore_signal(SIGTERM);
+		ignore_signal(SIGHUP);
+		ignore_signal(SIGINT);
+		ignore_signal(SIGCHLD);
+
+		if ((pid = wait(&status)) < 0) {
+			Dprintf((errno == ECHILD)? L_FATAL : L_WARNING,
+				"failsafe: wait(): %s", strerror(errno));
+			continue;
+		}
+
+		/* Locate the child */
+		for (i = 0, child = -1; i < ncopies; i++) {
+			if (servers[i] == pid) {
+				child = i;
+				break;
+			}
+		}
+
+		if (child < 0) {
+			Dprintf(L_WARNING,
+				"failsafe: unknown child (pid %d) terminated",
+				pid);
+			continue;
+		}
+
+		/* Book-keeping */
+		servers[child] = 0;
+		running--;
+
+		if (WIFSIGNALED(status)) {
+			signo = WTERMSIG(status);
+			if (signo == SIGTERM) {
+				Dprintf(L_NOTICE, "failsafe: "
+					"child %d terminated by SIGTERM. %s.",
+					pid, running? "Continue" : "Exit");
+			} else {
+				Dprintf(L_WARNING, "failsafe: "
+					"child %d terminated by %s. "
+					"Restarting.",
+					pid, get_signame(signo));
+				child = -1; /* Restart */
+			}
+		} else if (WIFEXITED(status)) {
+			Dprintf(L_NOTICE, "failsafe: "
+				"child %d exited, status %d.",
+				pid, WEXITSTATUS(status));
+		} else {
+			Dprintf(L_ERROR, "failsafe: "
+				"abnormal child termination, "
+				"pid=%d status=%d. Restarting.",
+				pid, status);
+			child = -1; /* Restart */
+		}
+
+		/* If child >= 0, we should not restart */
+		if (child >= 0) {
+			if (!running) {
+				Dprintf(D_GENERAL,
+					"No more children, exiting.");
+				exit(0);
+			}
+			for (i = child; i < ncopies-1; i++)
+				servers[i] = servers[i+1];
+			ncopies--; /* Make sure we start no new servers */
+		}
+	}
+}
+
+/*
+ * Failsafe session, catch core file.
+ *
+ * Not yet implemented.
+ * General outline: we need to fork first, because nfsd changes
+ * uids frequently, and the kernel won't write out a core file after
+ * that. The forked proc starts out with a clean dumpable flag though.
+ *
+ * After the fork, we might want to make sure we end up in some common
+ * directory that the failsafe loop knows about.
+ */
+void
+failsafe_loop(int level, void (*function)(void))
+{
+	/* NOP */
+}
+
+static const char *
+get_signame(int signo)
+{
+	static char	namebuf[30];
+
+	switch (signo) {
+	case SIGHUP:	return "SIGHUP";
+	case SIGINT:	return "SIGINT";
+	case SIGQUIT:	return "SIGQUIT";
+	case SIGILL:	return "SIGILL";
+	case SIGTRAP:	return "SIGTRAP";
+	case SIGIOT:	return "SIGIOT";
+	case SIGBUS:	return "SIGBUS";
+	case SIGFPE:	return "SIGFPE";
+	case SIGKILL:	return "SIGKILL";
+	case SIGUSR1:	return "SIGUSR1";
+	case SIGSEGV:	return "SIGSEGV";
+	case SIGUSR2:	return "SIGUSR2";
+	case SIGPIPE:	return "SIGPIPE";
+	case SIGALRM:	return "SIGALRM";
+	case SIGTERM:	return "SIGTERM";
+	case SIGCHLD:	return "SIGCHLD";
+	case SIGCONT:	return "SIGCONT";
+	case SIGSTOP:	return "SIGSTOP";
+	case SIGTSTP:	return "SIGTSTP";
+	case SIGTTIN:	return "SIGTTIN";
+	case SIGTTOU:	return "SIGTTOU";
+	case SIGURG:	return "SIGURG";
+	case SIGXCPU:	return "SIGXCPU";
+	case SIGXFSZ:	return "SIGXFSZ";
+	case SIGVTALRM:	return "SIGVTALRM";
+	case SIGPROF:	return "SIGPROF";
+	case SIGWINCH:	return "SIGWINCH";
+	case SIGIO:	return "SIGIO";
+#ifdef SIGPWR
+	case SIGPWR:	return "SIGPWR";
+#endif
+	}
+
+	sprintf(namebuf, "signal #%d", signo);
+	return namebuf;
+}
diff -urN nfs-server-2.2beta47/daemon.h nfs-server-2.2beta51/daemon.h
--- nfs-server-2.2beta47/daemon.h	Thu Jan  1 01:00:00 1970
+++ nfs-server-2.2beta51/daemon.h	Fri Nov  8 14:45:52 2002
@@ -0,0 +1,18 @@
+/*
+ * daemon.h
+ *
+ * Daemon support
+ */
+
+#ifndef CRAWLEY_H
+#define CRAWLEY_H
+
+#define _PATH_NFSD_PIDFILE	"/var/run/nfsd.pid"
+#define _PATH_MOUNTD_PIDFILE	"/var/run/mountd.pid"
+
+extern void	daemonize(void);
+extern void	setpidpath(const char *);
+extern void	writepid(pid_t, int);
+extern void	failsafe(int level, int ncopies);
+
+#endif /* CRAWLEY_H */
diff -urN nfs-server-2.2beta47/exports.man nfs-server-2.2beta51/exports.man
--- nfs-server-2.2beta47/exports.man	Wed Nov 10 10:18:49 1999
+++ nfs-server-2.2beta51/exports.man	Fri Nov  8 14:45:36 2002
@@ -45,6 +45,12 @@
 simultaneously. This is done by specifying an IP address and netmask pair
 as
 .IR address/netmask .
+.IP "world
+You can export a directory to the world (i.e. to all computers that
+are able to reach your NFS server network-wise) by using the empty
+hostname. When exporting to the world, the
+.BR root_squash ", " all_squash ", " ro " and " nosetuid
+options are turned on by default.
 .TP
 .B =public
 This is a special ``hostname'' that identifies the given directory name
@@ -81,6 +87,12 @@
 by using the
 .IR ro " option.
 .TP
+.I setuid
+This allows clients to assert the setuid and setgid bits on regular
+files. For non-anonymous exports, this option is on by default.
+For anonymous exports, the default is
+.IR nosetuid .
+.TP
 .I noaccess
 This makes everything below the directory inaccessible for the named
 client.  This is useful when you want to export a directory hierarchy to
@@ -296,6 +308,22 @@
 .I /usr/X11R6 
 entry apply. This is also true when the latter is a wildcard or netgroup
 entry.
+.PP
+You should also be careful about where you place spaces in the
+exports file. For instance, the following may appear as if you've
+exported
+.BR /pub " readonly to host " foozle ,
+but what this does in fact is export the directory to
+.B foozle
+with the default options,
+.I and
+export it to the world with the readonly option:
+.PP
+.nf
+.ta +3i
+# bad: export to the world
+/pub            foozle (ro)
+.fi
 .SH FILES
 /etc/exports
 .SH DIAGNOSTICS
diff -urN nfs-server-2.2beta47/fh.c nfs-server-2.2beta51/fh.c
--- nfs-server-2.2beta47/fh.c	Wed Nov 10 10:41:14 1999
+++ nfs-server-2.2beta51/fh.c	Fri Nov  8 14:45:36 2002
@@ -95,17 +95,14 @@
 static int			fh_list_size;
 static time_t			curtime;
 
-#ifndef FOPEN_MAX
-#define FOPEN_MAX		256
-#endif
-
 #ifndef FHTRACE
 #undef	D_FHTRACE
 #define D_FHTRACE		D_FHCACHE
 #endif
 
-static fhcache *		fd_cache[FOPEN_MAX] = { NULL };
+static fhcache **		fd_cache = NULL;
 static int			fd_cache_size = 0;
+static int			fd_cache_max = 0;
 
 #ifndef NFSERR_INVAL			/* that Sun forgot */
 #define NFSERR_INVAL	22
@@ -141,10 +138,13 @@
 
 /* Forward declared local functions */
 static psi_t	path_psi(char *, nfsstat *, struct stat *, int);
+static psi_t	path_psi_m(char *, nfsstat *, struct stat *,
+					      struct stat *, int);
 static int	fh_flush_fds(void);
 static char *	fh_dump(svc_fh *);
 static void	fh_insert_fdcache(fhcache *fhc);
 static void	fh_unlink_fdcache(fhcache *fhc);
+static void	fh_complain(const char *msg, fhcache *fhc);
 
 static void
 fh_move_to_front(fhcache *fhc)
@@ -192,6 +192,13 @@
 static void
 fh_insert_fdcache(fhcache *fhc)
 {
+#ifdef FHTRACE
+	Dprintf(D_FHTRACE, "insert fh %x into fdcache @%d\n", fhc->h.psi, fhc->fd);
+	if (fhc->fd < 0) {
+		fh_complain("fd cache bug: bad fd", fhc);
+		return;
+	}
+#endif
 	if (fhc == fd_lru_head)
 		return;
 	if (fhc->fd_next || fhc->fd_prev)
@@ -203,9 +210,20 @@
 	fhc->fd_next = fd_lru_head;
 	fd_lru_head = fhc;
 
+	if (fhc->fd >= fd_cache_max) {
+		int	oldmax = fd_cache_max, newmax;
+
+		newmax = (fhc->fd + 8) & ~7;
+		fd_cache = (fhcache **) xrealloc(fd_cache, newmax * sizeof(fhcache *));
+		memset(fd_cache + oldmax, 0, (newmax - oldmax) * sizeof(fhcache *));
+		fd_cache_max = newmax;
+	}
+
 #ifdef FHTRACE
 	if (fd_cache[fhc->fd] != NULL) {
-		Dprintf(L_ERROR, "fd cache inconsistency!\n");
+		Dprintf(L_ERROR, "fd cache inconsistency (two fh's for same fd)");
+		fh_complain("new fh", fhc);
+		fh_complain("old fh", fd_cache[fhc->fd]);
 		return;
 	}
 #endif
@@ -225,7 +243,7 @@
 	} else if (fd_lru_tail == fhc) {
 		fd_lru_tail = prev;
 	} else {
-		Dprintf(L_ERROR, "fd cache inconsistency\n");
+		fh_complain("fd cache inconsistency (no next and not at tail)", fhc);
 		return;
 	}
 	if (prev) {
@@ -233,13 +251,13 @@
 	} else if (fd_lru_head == fhc) {
 		fd_lru_head = next;
 	} else {
-		Dprintf(L_ERROR, "fd cache inconsistency\n");
+		fh_complain("fd cache inconsistency (no prev and not at head)", fhc);
 		return;
 	}
 
 #ifdef FHTRACE
 	if (fd_cache[fhc->fd] != fhc) {
-		Dprintf(L_ERROR, "fd cache inconsistency!\n");
+		fh_complain("fd cache inconsistency (fd cache ptr mismatch)", fhc);
 		return;
 	}
 #endif
@@ -285,7 +303,7 @@
 		hash_slot = &((*hash_slot)->hash_next);
 	if (*hash_slot == NULL)
 		Dprintf(L_ERROR,
-			"internal inconsistency -- fhc(%x) not in hash table\n",
+			"internal inconsistency -- fhc(%x) not in hash table!\n",
 			fhc);
 	else
 		*hash_slot = fhc->hash_next;
@@ -572,7 +590,7 @@
 				efs_seekdir(dir, cookie_stack[i]);
 			while ((dp = efs_readdir(dir))) {
 				char	*name = dp->d_name;
-				int	n = strlen(name);
+				int	n = strlen(name); /* or: dp->d_reclen */
 
 				if (pathlen + n + 1 >= NFS_MAXPATHLEN
 				 || (name[0] == '.'
@@ -738,7 +756,16 @@
 static psi_t
 path_psi(char *path, nfsstat *status, struct stat *sbp, int svalid)
 {
-	struct stat sbuf;
+	struct stat smounted;
+
+	return path_psi_m(path, status, sbp, &smounted, svalid);
+}
+
+static psi_t
+path_psi_m(char *path, nfsstat *status,
+		struct stat *sbp, struct stat *mbp, int svalid)
+{
+	struct stat sbuf, ddbuf;
 
 	if (sbp == NULL)
 		sbp = &sbuf;
@@ -746,10 +773,10 @@
 		*status = nfs_errno();
 		return (0);
 	}
+	*mbp = *sbp;
 	if (S_ISDIR(sbp->st_mode) && strcmp(path, "/") != 0) {
 		/* Special case for directories--test for mount point. */
-		struct stat ddbuf;
-		char *fname;
+		char	*fname;
 
 		/* Find start of last component of path. */
 #if 1
@@ -819,6 +846,19 @@
 	return (pseudo_inode(sbp->st_ino, sbp->st_dev));
 }
 
+/*
+ * Match attributes to make sure we're still referring to the original file
+ */
+static inline int
+fh_attrmatch(struct fhcache *fhc, struct stat *attr)
+{
+	if (fhc->dev == attr->st_dev
+	 && fhc->ino == attr->st_ino
+	 && fhc->type == (attr->st_mode & S_IFMT))
+		return 1;
+	return 0;
+}
+
 fhcache *
 fh_find(svc_fh *h, int mode)
 {
@@ -838,6 +878,9 @@
 	ex_state = active;
 	time(&curtime);
 	while ((fhc = fh_lookup(h->psi)) != NULL) {
+		struct stat	sbuf, *s = NULL;
+		nfsstat		dummy;
+
 		Dprintf(D_FHCACHE, "fh_find: psi=%lx... found '%s', fd=%d\n",
 			(unsigned long) h->psi,
 			fhc->path ? fhc->path : "<unnamed>",
@@ -857,33 +900,27 @@
 		 * If it doesn't try to rebuild the path.
 		 */
 		if (check) {
-			struct stat	*s = &fhc->attrs;
-			psi_t		psi;
-			nfsstat		dummy;
-
+			s = &sbuf;
 			if (efs_lstat(fhc->path, s) < 0) {
 				Dprintf(D_FHTRACE,
 					"fh_find: stale fh: lstat: %m\n");
 			} else {
-				fhc->flags |= FHC_ATTRVALID;
-				/* If pseudo-inos don't match, we fhc->path
-				 * may be a mount point (hence lstat() returns
+				/* If device/ino don't match, fhc->path may
+				 * be a mount point (hence lstat() returns
 				 * a different inode number than the readdir()
 				 * stuff used in path_psi)
 				 */
-				psi = pseudo_inode(s->st_ino, s->st_dev);
-				if (h->psi == psi)
+				if (fh_attrmatch(fhc, s))
 					goto fh_return;
 
-				/* Try again by computing the path psi */
-				psi = path_psi(fhc->path, &dummy, s, 1);
-				if (h->psi == psi)
+				/* Get the dev/ino of the underlying
+				 * mount point. */
+				path_psi(fhc->path, &dummy, s, 1);
+				if (fh_attrmatch(fhc, s))
 					goto fh_return;
 
-				Dprintf(D_FHTRACE, "fh_find: stale fh: "
-					"dev/ino %x/%lx psi %lx",
-					s->st_dev, s->st_ino,
-					(unsigned long) psi);
+				Dprintf(D_FHTRACE, "fh_find: stale fh: %lx",
+					(unsigned long) h->psi);
 			}
 
 		fh_discard:
@@ -896,6 +933,12 @@
 		}
 
 	fh_return:
+		/* Valid attributes; cache them */
+		if (s != NULL) {
+			memcpy(&fhc->attrs, s, sizeof(*s));
+			fhc->flags |= FHC_ATTRVALID;
+		}
+
 		/* The cached fh seems valid */
 		if (fhc != fh_head.next)
 			fh_move_to_front(fhc);
@@ -905,7 +948,8 @@
 	}
 
 	Dprintf(D_FHCACHE, "fh_find: psi=%lx... not found\n",
-		(unsigned long) h->psi);
+			(unsigned long) h->psi);
+
 	if (mode == FHFIND_FCACHED) {
 		ex_state = inactive;
 		return NULL;
@@ -918,6 +962,7 @@
 		fhc = flush->prev;
 		fh_delete(flush);
 	}
+
 	fhc = (fhcache *) xmalloc(sizeof *fhc);
 	if (mode == FHFIND_FCREATE) {
 		/* File will be created */
@@ -937,11 +982,31 @@
 		}
 		fhc->path = path;
 	}
+
 	fhc->flags = 0;
 	if (fhc->path && efs_lstat(fhc->path, &fhc->attrs) >= 0) {
-		if (re_export && nfsmounted(fhc->path, &fhc->attrs))
+		if (nfsmounted(fhc->path, &fhc->attrs)) {
 			fhc->flags |= FHC_NFSMOUNTED;
+#if 0
+			/* We must allow the client to send us the
+			 * file handle for the NFS mount point itself,
+			 * but not for entries within an NFS mount.
+			 * XXX: needs fixing.
+			 */
+			if (!re_export) {
+				Dprintf(D_FHTRACE,
+					"Attempt to use %s (non-exportable)\n",
+					fhc->path);
+				free(fhc);
+				ex_state = inactive;
+				return NULL;
+			}
+#endif
+		}
 		fhc->flags |= FHC_ATTRVALID;
+		fhc->dev   = fhc->attrs.st_dev;
+		fhc->ino   = fhc->attrs.st_ino;
+		fhc->type  = fhc->attrs.st_mode & S_IFMT;
 	}
 	fhc->fd = -1;
 	fhc->last_used = curtime;
@@ -993,6 +1058,14 @@
 	return buf;
 }
 
+static void
+fh_complain(const char *msg, fhcache *fhc)
+{
+	Dprintf(L_ERROR, "%s: ptr=%p fd=%d path=%s\n", msg,
+			fhc, fhc->fd,
+			fhc->path? fhc->path : "<unnamed>");
+}
+
 /*
  * This routine is only used by the mount daemon.
  * It creates the initial file handle.
@@ -1000,23 +1073,25 @@
 int
 fh_create(nfs_fh *fh, char *path)
 {
-	svc_fh	key;
-	fhcache	*h;
-	psi_t	psi;
-	nfsstat	status;
-	char	*s;
+	struct stat	stb;
+	svc_fh		key;
+	fhcache		*h;
+	psi_t		psi;
+	nfsstat		status;
+	char		*s;
 
 	memset(&key, 0, sizeof(key));
 	status = NFS_OK;
-	if ((psi = path_psi("/", &status, NULL, 0)) == 0)
+	if ((psi = path_psi("/", &status, &stb, 0)) == 0)
 		return ((int) status);
+
 	s = path;
 	while ((s = strchr(s + 1, '/')) != NULL) {
 		if (++(key.hash_path[0]) >= HP_LEN)
 			return ((int) NFSERR_NAMETOOLONG);
 		key.hash_path[key.hash_path[0]] = hash_psi(psi);
 		*s = '\0';
-		if ((psi = path_psi(path, &status, NULL, 0)) == 0)
+		if ((psi = path_psi(path, &status, &stb, 0)) == 0)
 			return ((int) status);
 		*s = '/';
 	}
@@ -1024,7 +1099,7 @@
 		if (++(key.hash_path[0]) >= HP_LEN)
 			return ((int) NFSERR_NAMETOOLONG);
 		key.hash_path[key.hash_path[0]] = hash_psi(psi);
-		if ((psi = path_psi(path, &status, NULL, 0)) == 0)
+		if ((psi = path_psi(path, &status, &stb, 0)) == 0)
 			return ((int) status);
 	}
 	key.psi = psi;
@@ -1037,9 +1112,12 @@
 
 	/* assert(h != NULL); */
 	if (h->path == NULL) {
-		h->fd = -1;
-		h->path = xstrdup(path);
+		h->fd    = -1;
+		h->path  = xstrdup(path);
 		h->flags = 0;
+		h->dev   = stb.st_dev;
+		h->ino   = stb.st_ino;
+		h->type  = stb.st_mode & S_IFMT;
 	}
 	memcpy(fh, &key, sizeof(key));
 	return ((int) status);
@@ -1064,6 +1142,44 @@
 	return ((nfs_fh*)&(h->h));
 }
 
+
+static inline int
+access_override(int omode, int perm, struct stat *buf)
+{
+	/* Be suspicous of flags, particularly O_CREAT/O_TRUNC.  A previous 
+	 * comment said:
+	 *
+	 * "[Not checking this] would truncate read-only files on creat() 
+	 * calls. Of course, ftruncate(fd, 0) should still be legal for
+	 * the user when the file was chmoded *after* opening it, but we
+	 * have no way to tell, and a semi-succeding `cp foo readonly-file'
+	 * is much more unintuitive and destructive than a failing
+	 * ftruncate()."
+	 */
+	if (omode & ~O_ACCMODE)
+		return 0;
+
+	/* Users can do anything to their own files.  Harmless (since they 
+	 * could chown anyway), and helps to mask NFSes statelessness.
+	 *
+	 * (in passing, this also handles mode 0100 execution)
+	 */
+	if (buf->st_uid == auth_uid)
+		return 1;
+
+	/* Henceforth, we are considering granting read access to facilitate 
+	 * exec access. This is read only */
+	if (omode != O_RDONLY)
+		return 0;
+
+	/* Mode 0110 execution */
+	if (buf->st_gid == auth_gid)
+		return (buf->st_mode & S_IXGRP) != 0;
+
+	/* Mode 0111 execution */
+	return (buf->st_mode & S_IXOTH) != 0;
+}
+	
 int
 path_open(char *path, int omode, int perm)
 {
@@ -1113,30 +1229,15 @@
 	 * lishes two things: first, it gives the file owner r/w access to
 	 * the file whatever the permissions are, so that files are still
 	 * accessible after an fchown(fd, 0). The second part of the
-	 * condition allows read access to mode 0111 executables.
-	 *
-	 * The old conditon read like this:
-	 * if (fd < 0 && oerrno == EACCES) {
-	 *	if (oerrno == EACCES && (buf.st_uid == auth_uid
-	 *	    || (omode == O_RDONLY && (buf.st_mode & S_IXOTH)))) {
-	 *		override uid; etc...
-	 *	}
-	 * }
-	 * This would truncate read-only files on creat() calls. Now
-	 * ftruncate(fd, 0) should still be legal for the user when the
-	 * file was chmoded *after* opening it, but we have no way to tell,
-	 * and a semi-succeding `cp foo readonly-file' is much more
-	 * unintuitive and destructive than a failing ftruncate().
+	 * condition allows read access to `execute-only' files.
 	 */
-	if (fd < 0 && oerrno == EACCES && !(omode & (O_CREAT|O_TRUNC))) {
-		if ((buf.st_uid == auth_uid && (omode & O_ACCMODE) == omode)
-		 || ((buf.st_mode & S_IXOTH) && omode == O_RDONLY)) {
-			auth_override_uid(ROOT_UID);
-			fd = efs_open(path, omode, perm);
-			oerrno = errno;
-			auth_override_uid(auth_uid);
-		}
+	if (fd < 0 && oerrno == EACCES && access_override(omode, perm, &buf)) {
+		auth_override_uid(ROOT_UID);
+		fd = efs_open(path, omode, perm);
+		oerrno = errno;
+		auth_override_uid(auth_uid);
 	}
+	
 
 	if (fd < 0) {
 		Dprintf(D_FHCACHE,
@@ -1241,7 +1342,7 @@
 	char		*sindx;
 	int		is_dd;
 	nfsstat		ret;
-	struct stat	sbuf;
+	struct stat	sbuf, smount;
 	char		pathbuf[PATH_MAX + NAME_MAX + 1], *fname;
 
 	/* should not happen */
@@ -1318,7 +1419,7 @@
 
 	*new_fh = dopa->dir;
 	key = (svc_fh *) new_fh;
-	if ((key->psi = path_psi(pathbuf, &ret, sbp, 0)) == 0)
+	if ((key->psi = path_psi_m(pathbuf, &ret, sbp, &smount, 0)) == 0)
 		return (ret);
 
 	if (is_dd) {
@@ -1344,6 +1445,10 @@
 					h->h.hash_path[0]);
 		return NFSERR_STALE;
 	}
+	if (sbp->st_dev != smount.st_dev) {
+		Dprintf(D_FHTRACE, "fh_compose: %s hit%s mount point\n",
+			pathbuf, nfsmounted(pathbuf, &smount)? " NFS" : "");
+	}
 #endif
 
 	/* New code added by Don Becker */
@@ -1356,7 +1461,8 @@
 		if (!h) return NFSERR_STALE;
 #endif
 		if (h->path)
-			Dprintf(L_ERROR, "Internal inconsistency: double entry (path '%s', now '%s').\n",
+			Dprintf(L_ERROR,
+				"internal inconsistency: double entry (path '%s', now '%s').\n",
 				h->path, pathbuf);
 	}
 	Dprintf(D_FHCACHE, "fh_compose: using  handle %x ('%s', fd=%d)\n",
@@ -1365,9 +1471,18 @@
 
 	/* assert(h != NULL); */
 	if (h->path == 0) {
-		h->path = xstrdup(pathbuf);
+		h->path  = xstrdup(pathbuf);
 		h->flags = 0;
-		if (!re_export && nfsmounted(pathbuf, sbp))
+		h->dev   = sbp->st_dev;
+		h->ino   = sbp->st_ino;
+		h->type  = sbp->st_mode & S_IFMT;
+
+		/* Note: in the case of a mount point,
+		 * sbp contains the stats of the mount point, while
+		 * ddbuf has the dev/ino of the underlying directory
+		 */
+		if (sbp->st_dev != smount.st_dev
+		 && nfsmounted(pathbuf, &smount))
 			h->flags |= FHC_NFSMOUNTED;
 #ifdef FHTRACE
 		Dprintf(D_FHTRACE, "fh_compose: created handle %s\n", h->path);
diff -urN nfs-server-2.2beta47/fh.h nfs-server-2.2beta51/fh.h
--- nfs-server-2.2beta47/fh.h	Mon Nov 23 12:15:43 1998
+++ nfs-server-2.2beta51/fh.h	Fri Nov  8 14:45:36 2002
@@ -97,7 +97,13 @@
 	struct fhcache *	hash_next;
 	struct fhcache *	fd_next;
 	struct fhcache *	fd_prev;
+
+	/* These are fixed during the lifetime of this object */
 	svc_fh			h;
+	dev_t			dev;
+	ino_t			ino;
+	mode_t			type;	/* st_mode & S_IFMT */
+
 	int			fd;
 	int			omode;
 	char *			path;
diff -urN nfs-server-2.2beta47/getattr.c nfs-server-2.2beta51/getattr.c
--- nfs-server-2.2beta47/getattr.c	Fri Oct 30 18:10:11 1998
+++ nfs-server-2.2beta51/getattr.c	Fri Nov  8 14:45:36 2002
@@ -115,6 +115,16 @@
 	attr->fsid   = 1;
 	attr->fileid = fh_psi((nfs_fh *)&(fhc->h));
 #endif
+
+	/* This may be needed by some Suns... testing */
+#define MINTIME	(24 * 2600)
+	if (s->st_atime < MINTIME)
+		s->st_atime = MINTIME;
+	if (s->st_mtime < MINTIME)
+		s->st_mtime = MINTIME;
+	if (s->st_ctime < MINTIME)
+		s->st_ctime = MINTIME;
+
 	attr->atime.seconds = s->st_atime;
 	attr->atime.useconds = 0;
 	attr->mtime.seconds = s->st_mtime;
diff -urN nfs-server-2.2beta47/logging.c nfs-server-2.2beta51/logging.c
--- nfs-server-2.2beta47/logging.c	Fri Oct 30 17:11:22 1998
+++ nfs-server-2.2beta51/logging.c	Fri Nov  8 14:45:36 2002
@@ -147,8 +147,9 @@
 		(void) time(&now);
 		tm = localtime(&now);
 		fprintf(log_fp, "%s %02d/%02d/%02d %02d:%02d %s",
-		      log_name, tm->tm_mon + 1, tm->tm_mday, tm->tm_year,
-			tm->tm_hour, tm->tm_min, buff);
+		      log_name, tm->tm_mon + 1, tm->tm_mday,
+		      tm->tm_year % 100,
+		      tm->tm_hour, tm->tm_min, buff);
 		if (strchr(buff, '\n') == NULL)
 			fputc('\n', log_fp);
 	}
@@ -182,7 +183,8 @@
 		tm = localtime(&unix_cred->aup_time);
 		snprintf(buffer + len, total - len,
 			"%d/%d/%d %02d:%02d:%02d %s %d.%d",
-			tm->tm_year, tm->tm_mon + 1, tm->tm_mday,
+			tm->tm_year %100,
+			tm->tm_mon + 1, tm->tm_mday,
 			tm->tm_hour, tm->tm_min, tm->tm_sec,
 			unix_cred->aup_machname,
 			unix_cred->aup_uid,
diff -urN nfs-server-2.2beta47/mountd.c nfs-server-2.2beta51/mountd.c
--- nfs-server-2.2beta47/mountd.c	Wed Jun  2 14:10:33 1999
+++ nfs-server-2.2beta51/mountd.c	Fri Nov  8 14:45:36 2002
@@ -32,7 +32,7 @@
 #include "rpcmisc.h"
 #include "rmtab.h"
 #include "haccess.h"
-#include "failsafe.h"
+#include "daemon.h"
 #include "signals.h"
 #include <rpc/pmap_clnt.h>
 
@@ -44,6 +44,8 @@
 /*
  * Option table for mountd
  */
+#define OPT_NOTCP	300
+#define OPT_LOOPBACK	301
 static struct option longopts[] =
 {
       { "debug",		required_argument,	0,	'd' },
@@ -56,6 +58,8 @@
       {	"no-spoof-trace",	0,			0,	't' },
       { "version",		0,			0,	'v' },
       { "fail-safe",		optional_argument,	0,	'z' },
+      { "no-tcp",		0,			0,	OPT_NOTCP },
+      {	"loopback-only",	0,			0,	OPT_LOOPBACK },
 
       { NULL,			0,			0,	0 }
 };
@@ -358,6 +362,12 @@
 			break;
 		case 0:
 			break;
+		case OPT_NOTCP:
+			udp_only = 1;
+			break;
+		case OPT_LOOPBACK:
+			loopback_only = 1;
+			break;
 		case '?':
 		default:
 			usage(stderr, 1);
@@ -384,38 +394,27 @@
 	/* Create services and register with portmapper */
 	rpc_init("mountd", MOUNTPROG, mountd_versions, mount_dispatch, port, 0);
 
-	if (!foreground && !_rpcpmstart) {
-#ifndef RPC_SVC_FG
-		/* We first fork off a child. */
-		if ((c = fork()) > 0)
-			exit(0);
-		if (c < 0) {
-			Dprintf(L_FATAL, "mountd: cannot fork: %s\n",
-						strerror(errno));
-		}
-		/* No more logging to stderr */
-		background_logging();
+	if (_rpcpmstart) {
+		/* Always foreground mode */
+		foreground = 1;
 
-		/* Now we remove ourselves from the foreground. */
-		(void) close(0);
-		(void) close(1);
-		(void) close(2);
-#ifdef TIOCNOTTY
-		if ((c = open("/dev/tty", O_RDWR)) >= 0) {
-			(void) ioctl(c, TIOCNOTTY, (char *) NULL);
-			(void) close(c);
-		}
-#else
-		setsid();
-#endif
-#endif /* not RPC_SVC_FG */
+		/* ... but no logging */
+		background_logging();
 	}
 
+	/* Become a daemon */
+	if (!foreground)
+		daemonize();
+
 	/* Initialize the FH module. */
 	fh_init();
 
 	/* Initialize the AUTH module. */
 	auth_init(auth_file);
+
+	/* Write pidfile */
+	setpidpath(_PATH_MOUNTD_PIDFILE);
+	writepid(getpid(), 1);
 
 	/* Failsafe mode */
 	if (failsafe_level)
diff -urN nfs-server-2.2beta47/mountd.man nfs-server-2.2beta51/mountd.man
--- nfs-server-2.2beta47/mountd.man	Wed Jun  2 14:12:21 1999
+++ nfs-server-2.2beta51/mountd.man	Fri Nov  8 14:45:36 2002
@@ -14,6 +14,8 @@
 .B "[\ \-\-allow\-non\-root\ ]"
 .B "[\ \-\-re\-export\ ]"
 .B "[\ \-\-no\-spoof\-trace\ ]"
+.B "[\ \-\-no\-tcp ]"
+.B "[\ \-\-loopback\-only ]"
 .B "[\ \-\-version\ ]"
 .ad b
 .SH DESCRIPTION
@@ -123,6 +125,18 @@
 .TP
 .BR \-v " or " \-\-version
 Report the current version number of the program.
+.TP
+.BR \-\-no\-tcp
+Force
+.I mountd
+to register only the UDP transport, but no TCP.
+This is an experimental option.
+.TP
+.BR \-\-loopback\-only
+Force
+.I mountd
+to bind to the loopback interface.
+This is an experimental option.
 .SS Access Control
 For enhanced security, access to
 .I mountd
diff -urN nfs-server-2.2beta47/nfsd.c nfs-server-2.2beta51/nfsd.c
--- nfs-server-2.2beta47/nfsd.c	Wed Nov 10 10:33:28 1999
+++ nfs-server-2.2beta51/nfsd.c	Fri Nov  8 14:45:36 2002
@@ -21,7 +21,7 @@
 #include "getopt.h"
 #include "fsusage.h"
 #include "rpcmisc.h"
-#include "failsafe.h"
+#include "daemon.h"
 #include "signals.h"
 #ifdef __linux__ /* XXX - MvS: for UNIX sockets. */
 #  include <sys/un.h>
@@ -30,7 +30,6 @@
 # include <syslog.h>
 #endif
 
-#define MULTIPLE_SERVERS
 
 /* Flags for auth_fh */
 #define CHK_READ	0
@@ -51,6 +50,8 @@
 /*
  * Option table
  */
+#define OPT_NOTCP	300
+#define OPT_LOOPBACK	301
 static struct option longopts[] = {
       { "auth-deamon",		required_argument,	0,	'a' },
       { "debug",		required_argument,	0,	'd' },
@@ -68,6 +69,9 @@
       { "version",		0,			0,	'v' },
       { "no-cross-mounts",	0,			0,	'x' },
       { "fail-safe",		optional_argument,	0,	'z' }, 
+      { "no-tcp",		0,			0,	OPT_NOTCP },
+      { "udp-only",		0,			0,	OPT_NOTCP },
+      {	"loopback-only",	0,			0,	OPT_LOOPBACK },
 
       { NULL,		0,	0, 0 }
 };
@@ -173,7 +177,10 @@
 		return NULL;
 	}
 
-	auth_user(nfsmount, rqstp);
+	if (!auth_user(nfsmount, rqstp)) {
+		*statp = NFSERR_ACCES;
+		return NULL;
+	}
 
 	*statp = NFS_OK;
 	return fhc;
@@ -211,7 +218,11 @@
 
 	if ((nfsmount = auth_path(nfsclient, rqstp, path)) == NULL)
 		return NFSERR_ACCES;
-	auth_user(nfsmount, rqstp);
+
+	/* XXX: really need to call it again here?
+	 * Already invoked in auth_fh */
+	if (!auth_user(nfsmount, rqstp))
+		return NFSERR_ACCES;
 
 	return (NFS_OK);
 }
@@ -575,7 +586,8 @@
 #endif
 
 			/* MvS: Some clients use chardev 0xFFFF for a FIFO. */
-			if (S_ISCHR(argp->attributes.mode) && dev == 0xFFFF) {
+			if (S_ISCHR(argp->attributes.mode)
+			 && (dev == 0xFFFF || dev == (dev_t) -1)) {
 				is_borc = 0;
 				dev = 0;
 				argp->attributes.mode &= ~S_IFMT;
@@ -623,7 +635,7 @@
 		flags = (argp->attributes.size == 0 ?
 			CREATE_OMODE | O_TRUNC : CREATE_OMODE);
 		if (!exists)
-			flags |= O_CREAT;
+			flags |= O_CREAT|O_EXCL;
 		tmpfd = path_open(pathbuf, flags, 
 				argp->attributes.mode & ~S_IFMT);
 		if (tmpfd < 0)
@@ -965,9 +977,7 @@
 	int	nfsport = 0;
 	int	failsafe_level = 0;
 	int	c;
-#ifdef MULTIPLE_SERVERS
 	int	i, ncopies = 1;
-#endif
 
 	program_name = argv[0];
 	chdir("/");
@@ -1031,12 +1041,17 @@
 			break;
 		case 0:
 			break;
+		case OPT_NOTCP:
+			udp_only = 1;
+			break;
+		case OPT_LOOPBACK:
+			loopback_only = 1;
+			break;
 		case '?':
 		default:
 			usage(stderr, 1);
 		}
 
-#ifdef MULTIPLE_SERVERS
 	if (optind == argc-1 && isdigit(argv[optind][0])) {
 		ncopies = atoi(argv[optind++]);
 		if (ncopies <= 0) {
@@ -1051,7 +1066,6 @@
 			ncopies = 1;
 		}
 	}
-#endif
 
 	/* No more arguments allowed. */
 	if (optind != argc)
@@ -1075,72 +1089,54 @@
 	rpc_init("nfsd", NFS_PROGRAM, nfsd_versions, nfs_dispatch,
 				nfsport, NFS_MAXDATA);
 
-	/* No more than 1 copy when run from inetd */
-	if (_rpcpmstart && ncopies > 1) {
-		Dprintf(L_WARNING,
-				"nfsd: warning: can run only "
-				"one server in inetd mode\n");
-		ncopies = 1;
+	if (_rpcpmstart) {
+		/* Always do foreground mode */
+		foreground = 1;
+
+		/* ... but don't log to stderr */
+		background_logging();
+
+		/* No more than 1 copy when run from inetd */
+		if (ncopies > 1) {
+			Dprintf(L_WARNING,
+					"nfsd: warning: can run only "
+					"one server in inetd mode\n");
+			ncopies = 1;
+		}
 	}
 
-#ifndef MULTIPLE_SERVERS_READWRITE
 	if (ncopies > 1)
 		read_only = 1;
-#endif
 
-	/* We first fork off a child. */
-	if (!foreground) {
-		if ((c = fork()) > 0)
-			exit(0);
-		if (c < 0) {
-			Dprintf(L_FATAL, "nfsd: cannot fork: %s\n",
-						strerror(errno));
-		}
-	}
+	/*
+	 * We first fork off a child and detach from tty
+	 */
+	if (!foreground)
+		daemonize();
 
 	/* Initialize the AUTH module. */
 	auth_init(auth_file);
 
+	setpidpath(_PATH_NFSD_PIDFILE);
 	if (failsafe_level == 0) {
 		/* Start multiple copies of the server */
+		writepid(getpid(), 1);
 		for (i = 1; i < ncopies; i++) {
+			pid_t	pid;
+
 			Dprintf(D_GENERAL, "Forking server thread...\n");
-			if ((c = fork()) < 0) {
+			if ((pid = fork()) < 0) {
 				Dprintf(L_ERROR, "Unable to fork: %s",
 					strerror(errno));
-			} else if (c == 0) {
-				/* Child process */
-				break;
+			} else if (pid != 0) {
+				writepid(pid, 0);
+			} else {
+				break; /* Child process */
 			}
 		}
 	} else {
 		/* Init for failsafe mode */
 		failsafe(failsafe_level, ncopies);
-	}
-
-	/* Now that we've done all the required forks, we make do all the
-	 * session magic.
-	 */
-	if (!foreground) {
-		/* No more logging to stderr */
-		background_logging();
-
-		/* Now we remove ourselves from the foreground. */
-		close(0);
-		close(1);
-		close(2);
-#ifdef HAVE_SETSID
-		setsid();
-#else
-		{
-			int fd;
-	
-			if ((fd = open("/dev/tty", O_RDWR)) >= 0) {
-				ioctl(fd, TIOCNOTTY, (char *) NULL);
-				close(fd);
-			}
-		}
-#endif
 	}
 
 	/*
diff -urN nfs-server-2.2beta47/nfsd.man nfs-server-2.2beta51/nfsd.man
--- nfs-server-2.2beta47/nfsd.man	Wed Jun  2 14:13:37 1999
+++ nfs-server-2.2beta51/nfsd.man	Fri Nov  8 14:45:36 2002
@@ -8,7 +8,7 @@
 .B "[\ \-d\ facility\ ]"
 .B "[\ \-P\ port\ ]"
 .B "[\ \-R\ dirname\ ]"
-.B "[\ \-Fhlnprstv\ ]"
+.B "[\ \-Fhlnprstuv\ ]"
 .B "[\ \-\-debug\ facility\ ]"
 .B "[\ \-\-exports\-file=file\ ]"
 .B "[\ \-\-foreground\ ]"
@@ -18,6 +18,8 @@
 .B "[\ \-\-public\-root\ dirname\ ]"
 .\".B "[\ \-\-synchronous\-writes\ ]"
 .B "[\ \-\-no\-spoof\-trace\ ]"
+.B "[\ \-\-no\-tcp ]"
+.B "[\ \-\-loopback-only ]"
 .B "[\ \-\-port\ port\ ]"
 .B "[\ \-\-log-transfers\ ]"
 .B "[\ \-\-version\ ]"
@@ -56,7 +58,7 @@
 .PP
 When run from
 .IR inetd ,
-.i nfsd
+.I nfsd
 will terminate after a certain period of inactivity.
 .SH OPTIONS
 .TP
@@ -167,6 +169,14 @@
 .BR \-v " or " \-\-version
 Report the current version number of the program.
 .TP
+.BR \-\-no\-tcp
+Force nfsd to only register a UDP transport, but not TCP.
+This is an experimental option.
+.TP
+.BR \-\-loopback\-only
+Force nfsd to bind to the loopback interface.
+This is an experimental option.
+.TP
 .BR numcopies
 This is an experimental feature that lets you run several instances of
 .I nfsd
@@ -174,15 +184,8 @@
 .B numcopies
 greater than one, 
 .I nfsd
-will fork as many times as specified by this value.
-However, the servers do not share a common file handle
-cache, which makes certain file operations impossible. 
-.IP
-For this reason,
-.I nfsd
-will disallow all write operations when invoked with this option. Although
-this is very limiting, this feature may still prove useful for exporting
-public FTP areas or Usenet News spools.
+will fork as many times as specified by this value so it is able to
+handle that many NFS requests in parallel.
 .SS WebNFS Support
 WebNFS is an extension to the normal NFS protocol developed by Sun
 that is particularly well-suited for file retrieval over the
@@ -268,6 +271,19 @@
 .I nfsd
 writes out a transfer record whenever it encounters a READ or WRITE
 request at offset zero.
+.SS Generating a debug trace
+When suspecting a bug in nfsd, it is helpful to look at a debug trace
+of what's going on. You can create such a trace by first killing nfsd,
+and then restarting it as
+.PP
+.nf
+.ta +3i
+/usr/sbin/rpc.nfsd -F -d all
+.fi
+.PP
+Instead of
+.BR all ,
+you can use less verbose debug facilities as described above.
 .SH "SEE ALSO"
 exports(5), mountd(8), ugidd(8C)
 .SH AUTHORS
diff -urN nfs-server-2.2beta47/rmtab.c nfs-server-2.2beta51/rmtab.c
--- nfs-server-2.2beta47/rmtab.c	Fri Feb  6 09:43:25 1998
+++ nfs-server-2.2beta51/rmtab.c	Fri Nov  8 14:45:36 2002
@@ -8,6 +8,7 @@
 
 #include "nfsd.h"
 #include "rmtab.h"
+#include "rpcmisc.h"
 
 static char *	rmtab_gethost(struct svc_req *);
 static int	rmtab_insert(char *, char *);
diff -urN nfs-server-2.2beta47/rpcmisc.c nfs-server-2.2beta51/rpcmisc.c
--- nfs-server-2.2beta47/rpcmisc.c	Tue Sep  7 10:42:58 1999
+++ nfs-server-2.2beta51/rpcmisc.c	Fri Nov  8 14:45:36 2002
@@ -39,6 +39,8 @@
 int		_rpcfdtype = 0;
 int		_rpcsvcdirty = 0;
 const char *	auth_daemon = 0;
+int		udp_only = 0;
+int		loopback_only = 0;
 
 #ifdef AUTH_DAEMON
 static bool_t	(*tcp_rendevouser)(SVCXPRT *, struct rpc_msg *);
@@ -96,7 +98,7 @@
 		}
 	}
 
-	if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_STREAM)) {
+	if ((_rpcfdtype == 0 && !udp_only) || (_rpcfdtype == SOCK_STREAM)) {
 		if (_rpcfdtype == 0 && defport != 0)
 			sock = makesock(defport, IPPROTO_TCP, bufsiz);
 		transp = svctcp_create(sock, 0, 0);
@@ -199,6 +201,9 @@
 	sin.sin_family = AF_INET;
 	sin.sin_addr.s_addr = INADDR_ANY;
 	sin.sin_port = htons(port);
+
+	if (loopback_only)
+		sin.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
 
 #ifdef DEBUG
 	{
diff -urN nfs-server-2.2beta47/rpcmisc.h nfs-server-2.2beta51/rpcmisc.h
--- nfs-server-2.2beta47/rpcmisc.h	Tue Sep  7 10:37:38 1999
+++ nfs-server-2.2beta51/rpcmisc.h	Fri Nov  8 14:45:36 2002
@@ -9,6 +9,8 @@
 extern int		_rpcpmstart;
 extern int		_rpcfdtype;
 extern int		_rpcsvcdirty;
+extern int		udp_only;
+extern int		loopback_only;
 extern const char *	auth_daemon;
 
 extern void		rpc_init(const char *name, int prog, int *verstbl,
@@ -16,5 +18,13 @@
 					int defport, int bufsize);
 extern void		rpc_exit(int prog, int *verstbl);
 extern void		rpc_closedown(void);
+
+/*
+ * Some older systems don't have svc_getcaller.
+ * Some, like glibc 2.2, have it but it returns some type that's
+ * not a sockaddr_in anymore.
+ */
+#undef svc_getcaller
+#define svc_getcaller(xprt)	((struct sockaddr_in *) (&(xprt)->xp_raddr))
 
 #endif /* RPCMISC_H */
diff -urN nfs-server-2.2beta47/setattr.c nfs-server-2.2beta51/setattr.c
--- nfs-server-2.2beta47/setattr.c	Fri Oct 30 18:29:42 1998
+++ nfs-server-2.2beta51/setattr.c	Fri Nov  8 14:45:36 2002
@@ -103,6 +103,10 @@
 	if (flags & SATTR_CHMOD) {
 		unsigned int	mode = attr->mode;
 
+		/* If setuid is not allowed, silently squash them */
+		if (!nfsmount->o.allow_setuid && S_ISREG(s->st_mode))
+			mode &= ~(S_ISUID|S_ISGID) | s->st_mode;
+
 		if (mode != -1 && mode != 0xFFFF /* ultrix bug */
 		 && (mode & 07777) != (s->st_mode & 07777)) {
 			if (efs_chmod(path, mode) < 0)
diff -urN nfs-server-2.2beta47/showmount.c nfs-server-2.2beta51/showmount.c
--- nfs-server-2.2beta47/showmount.c	Wed Jun 12 22:31:04 1996
+++ nfs-server-2.2beta51/showmount.c	Fri Nov  8 14:45:36 2002
@@ -162,17 +162,13 @@
 		break;
 	}
 
-	if (hostname[0] >= '0' && hostname[0] <= '9') {
-		server_addr.sin_family = AF_INET;
-		server_addr.sin_addr.s_addr = inet_addr(hostname);
-	}
-	else {
+	server_addr.sin_family = AF_INET;
+	if (!inet_aton(hostname, &server_addr.sin_addr)) {
 		if ((hp = gethostbyname(hostname)) == NULL) {
 			fprintf(stderr, "%s: can't get address for %s\n",
 				program_name, hostname);
 			exit(1);
 		}
-		server_addr.sin_family = AF_INET;
 		memcpy(&server_addr.sin_addr, hp->h_addr, hp->h_length);
 	}
 
diff -urN nfs-server-2.2beta47/site.h.in nfs-server-2.2beta51/site.h.in
--- nfs-server-2.2beta47/site.h.in	Thu Jan  1 01:00:00 1970
+++ nfs-server-2.2beta51/site.h.in	Fri Nov  8 14:45:57 2002
@@ -0,0 +1,50 @@
+/*
+ * Site-specific configuration options generated by BUILD.
+ * Please do not edit.
+ */
+
+/*
+ * If ENABLE_DEVTAB is defined, nfsd will use the new inode
+ * number generation scheme for avoiding inode number clashes
+ * on big hard disks.
+ */
+#undef ENABLE_DEVTAB
+
+/*
+ * If MULTIPLE_SERVER_READWRITE is defined, you will be able 
+ * to run several nfsd process in parallel servicing all NFS 
+ * requests.
+ */
+#define MULTIPLE_SERVERS_READWRITE
+
+/*
+ * If ENABLE_UGID_DAEMON is defined, the real rpc.ugidd is built, 
+ * nfsd is built to support ugidd queries.
+ * Otherwise, a dummy program is created
+ */
+#undef ENABLE_UGID_DAEMON 
+
+/*
+ * If ENABLE_UGID_NIS is defined, nfsd will support user mapping 
+ * vie the client's NIS server.
+ */
+#undef ENABLE_UGID_NIS
+
+/*
+ * if HOSTS_ACCESS is defined, ugidd uses host access control
+ * provided by libwrap.a from tcp_wrappers
+ */
+#define HOSTS_ACCESS
+
+/*
+ * Define correct ownership of export control file
+ */
+#define EXPORTSOWNERUID  0
+#define EXPORTSOWNERGID  0
+
+/*
+ * If WANT_LOG_MOUNTS is defined, every mount request will be logged
+ * to syslogd with the name of source site and a path that was
+ * it requested
+ */
+#define WANT_LOG_MOUNTS
diff -urN nfs-server-2.2beta47/ugidd.c nfs-server-2.2beta51/ugidd.c
--- nfs-server-2.2beta47/ugidd.c	Wed Dec 10 12:34:16 1997
+++ nfs-server-2.2beta51/ugidd.c	Fri Nov  8 14:45:36 2002
@@ -43,9 +43,7 @@
 };
 
 int
-main(argc, argv)
-int	argc;
-char	**argv;
+main(int argc, char **argv)
 {
 	SVCXPRT	*transp;
 	int	c, longind;
@@ -92,32 +90,11 @@
                 exit(1);
         }
 
-	if (!foreground) {
-		if ((c = fork()) > 0)
-			exit(0);
-		if (c < 0) {
-			fprintf(stderr, "ugidd: cannot fork: %s\n",
-						strerror(errno));
-			exit(-1);
-		}
-		close(0);
-		close(1);
-		close(2);
-#ifdef HAVE_SETSID
-		setsid();
-#else
-		{
-			int fd;
-
-			if ((fd = open("/dev/tty", O_RDWR)) >= 0) {
-				ioctl(fd, TIOCNOTTY, (char *) NULL);
-				close(fd);
-			}
-		}
-#endif
-	}
-
 	log_open("ugidd", foreground);
+
+	/* Become a daemon */
+	if (!foreground)
+		daemonize();
 
 	svc_run();
 	Dprintf(L_ERROR, "svc_run returned\n");
diff -urN nfs-server-2.2beta47/version.c nfs-server-2.2beta51/version.c
--- nfs-server-2.2beta47/version.c	Wed Nov 10 10:33:33 1999
+++ nfs-server-2.2beta51/version.c	Fri Nov  8 14:45:36 2002
@@ -1 +1 @@
-char version[] = "Universal NFS Server 2.2beta47";
+char version[] = "Universal NFS Server 2.2beta51";
