aboutsummaryrefslogtreecommitdiffstats
path: root/meta/recipes-devtools/unfs-server/unfs-server-2.2beta47/001-2.2b47-2.2b51.patch
diff options
context:
space:
mode:
Diffstat (limited to 'meta/recipes-devtools/unfs-server/unfs-server-2.2beta47/001-2.2b47-2.2b51.patch')
-rw-r--r--meta/recipes-devtools/unfs-server/unfs-server-2.2beta47/001-2.2b47-2.2b51.patch2344
1 files changed, 2344 insertions, 0 deletions
diff --git a/meta/recipes-devtools/unfs-server/unfs-server-2.2beta47/001-2.2b47-2.2b51.patch b/meta/recipes-devtools/unfs-server/unfs-server-2.2beta47/001-2.2b47-2.2b51.patch
new file mode 100644
index 0000000000..886ce92b34
--- /dev/null
+++ b/meta/recipes-devtools/unfs-server/unfs-server-2.2beta47/001-2.2b47-2.2b51.patch
@@ -0,0 +1,2344 @@
+# 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";