[comp.sources.unix] v15i001: unfsd - user-level NFS server, Part01/02

shand@cad.jmrc.eecs.unsw.oz (Mark Shand) (05/25/88)

Submitted by: shand@cad.jmrc.eecs.unsw.oz (Mark Shand)
Posting-number: Volume 15, Issue 1
Archive-name: unfsd/Part01

	[ Within the stated limitations, this seems to be a useful
	  tool.  It compiled and ran fine on my (slightly pre) 4.3bsd vax.

	  Nb: you need the Sun RPC version 3.9 from Volume 13 or
	  comp.sources.unix in order to use this submission.    ..kre ]


UNFSD - USER-LEVEL NFS SERVER
=============================

This package implements a simple user level NFS server based on the
sunrpc3.9 package that was posted to the net a few months ago.  The
current version only provides read access from the clients.  It has
been tested between a VAX11/780 running 4.3BSD (the server) and several
diskful SUN3/60 running SunOS 3.4 (the clients) and on a diskless
SUN3/50 running SunOS 3.2 remounting its own root at a lower level of
its file hierarchy.

#--------------------------------CUT HERE-------------------------------------
#! /bin/sh
#
# This is a shell archive.  Save this into a file, edit it
# and delete all lines above this comment.  Then give this
# file to sh by executing the command "sh file".  The files
# will be extracted into the current directory owned by
# you with default permissions.
#
# The files contained herein are:
#
# -rw-rw-r--  1 shand        2392 May 17 01:59 Makefile
# -rw-rw-r--  1 shand        6087 May 18 21:30 README
# -rw-r--r--  1 shand       18021 May 17 02:24 fh.c
# -rw-rw-r--  1 shand         727 May 17 02:24 fh.h
# -rw-rw-r--  1 shand        9203 May 17 02:25 init.c
# -rw-rw-r--  1 shand        4310 May 16 02:24 mount.x
#
echo 'x - Makefile'
if test -f Makefile; then echo 'shar: not overwriting Makefile'; else
sed 's/^X//' << '________This_Is_The_END________' > Makefile
X# For standard 4.3BSD systems with sunrpc3.9 installed
X#LIB=-lrpclib
X# For SunOS and other systems with rpc in the c library
XLIB=
X
X# Client authorization file
XEXPORTSFILE=/etc/unfsd_exports
X
X# Try -DDEBUG for a copious debug log to be written to /tmp/unfsd.log
XCFLAGS= -O -DREAD_ONLY -DEXPORTSFILE='"${EXPORTSFILE}"'
XLDFLAGS= -O
X
XRPCGEN=	rpcgen
XCC=	cc
X#------------------------------End of configuration section.
X
XNFSOBJS= nfs_prot_svc.o nfs_prot_xdr.o unfsd.o init.o \
X	fh.o ugid_map.o ugid_xdr.o ugid_clnt.o
X
Xall: unfsd unfsmntd
X
Xinstall: unfsd unfsmntd
X	cp unfsd ${INSDIR}/unfsd
X	cp unfsmntd ${INSDIR}/unfsmntd
X
Xunfsd:	${NFSOBJS}
X	${CC} $(LDFLAGS) -o unfsd ${NFSOBJS} ${LIB}
X
Xunfsmntd: mount_svc.o mount_xdr.o unfsmntd.o fh.o
X	${CC} $(LDFLAGS) -o unfsmntd mount_svc.o mount_xdr.o unfsmntd.o fh.o ${LIB}
X
Xugidd:	ugid_svc.o ugid_xdr.o ugidd.o
X	${CC} $(LDFLAGS) -o ugidd ugid_svc.o ugid_xdr.o ugidd.o ${LIB}
Xmprobe: mprobe.o mount_xdr.o nfs_prot_xdr.o
X	${CC} ${LDFLAGS} -o mprobe mprobe.o mount_xdr.o nfs_prot_xdr.o \
X		rpc/rpc/librpclib.a
X
Xnfs_prot.h: nfs_prot.x
X	${RPCGEN} -h -o $@ $?
X
Xnfs_prot_svc.c: nfs_prot.x
X	${RPCGEN} -s udp $? | \
X	sed \
X		-e 's/main()/main(argc,argv) int argc; char **argv;/' \
X		-e 's/RPC_ANYSOCK/makesock(NFS_PORT, NFS_MAXDATA)/' \
X		-e 's/svc_run();/unfsd_init(argc,argv); svc_run();/' \
X	    > $@
X
Xnfs_prot_xdr.c: nfs_prot.x
X	${RPCGEN} -c -o $@ $?
X
Xmount.h: mount.x
X	${RPCGEN} -h -o $@ $?
X
Xmount_svc.c: mount.x
X	${RPCGEN} -s udp $? | \
X	sed \
X		-e 's/main()/main(argc,argv) int argc; char **argv;/' \
X		-e 's/svc_run();/unfsmntd_init(argc,argv); svc_run();/' \
X	    > $@
X
Xmount_xdr.c: mount.x
X	${RPCGEN} -c -o $@ $?
X
Xugid.h:	ugid.x
X	${RPCGEN} -h -o $@ $?
X
Xugid_svc.c: ugid.x 
X	${RPCGEN} -s udp $? | \
X	sed \
X		-e 's/main()/main(argc,argv) int argc; char **argv;/' \
X		-e 's/RPC_ANYSOCK/run_mode_from_args(argc,argv)/' \
X	    > $@
X
Xugid_xdr.c: ugid.x
X	${RPCGEN} -c -o $@ $?
X
Xugid_clnt.c: ugid.x
X	${RPCGEN} -l -o $@ $?
Xclean:
X	rm -rf *.o nfs_prot_svc.c nfs_prot_xdr.c nfs_prot.h \
X		mount_svc.c mount_xdr.c mount.h \
X		ugid_svc.c ugid_xdr.c ugid_clnt.c ugid.h \
X		unfsd unfsmntd ugidd mprobe
X
X# include dependencies
Xnfs_prot_svc.o nfs_prot_xdr.o unfsd.o fh.o init.o: nfs_prot.h
X
Xmount_svc.o mount_xdr.o unfsmntd.o: mount.h
X
Xugid_map.o ugid_svc.o ugid_xdr.o ugidd.o unfsd.o init.o: ugid.h
X
Xmprobe.o: mount.h
X
Xfh.o unfsd.o: fh.h
X
Xunfsd.o ugid_map.o init.o: unfsd.h
________This_Is_The_END________
if test `wc -l < Makefile` -ne 97; then
	echo 'shar: Makefile was damaged during transit (should have been 97 bytes)'
fi
fi		; : end of overwriting check
echo 'x - README'
if test -f README; then echo 'shar: not overwriting README'; else
sed 's/^X//' << '________This_Is_The_END________' > README
XUNFSD - USER-LEVEL NFS SERVER
X=============================
X
XThis package implements a simple user level NFS server based on the
Xsunrpc3.9 package that was posted to the net a few months ago.  The
Xcurrent version only provides read access from the clients.  It has
Xbeen tested between a VAX11/780 running 4.3BSD (the server) and several
Xdiskful SUN3/60 running SunOS 3.4 (the clients) and on a diskless
XSUN3/50 running SunOS 3.2 remounting its own root at a lower level of
Xits file hierarchy.
X
XThe server is implemented by two programs unfsd and unfsmntd.
XUnfsmntd handles the mount protocol, and unfsd handles all subsequent
Xoperations.
X
XCurrently NFS is defined to use internet port 2049.  As a consequence
Xthere can only be one implementation of NFS active on a given machine.
XThus a machine cannot make it filesystem available through this server
Xwhile concurrently running some alternate server implementation.
X
XNormally the server would be run by the super-user.  I had hoped to be
Xable to run the server as a normal user, and indeed the server contains
Xcode to cope with paths containing directories that can be accessed but
Xnot searched, however the sunrpc portmapper listens on port 111 so some
Xsuperuser cooperation is required to at least gain support for sunrpc
Xon sites running standard 4.3 BSD.
X
XUnlike SUN's NFS-servers, the file hierarchy exported by unfsd treats
Xmount points within an exports filesystem transparently; thus the
Xclient sees the same file hierarchy as is seen from the server.
X
XCOPYRIGHT
X=========
X
XThese programs may be freely distributed provided they retain my
Xcopyright notices.  They are provided as is, with no warranty
Xexpressed or implied.
X
XINSTALLATION
X============
X
X1. If you have installed Van Jacobson's TCP/IP code be sure you have
X   installed the fix I posted to comp.bugs.4bsd (reproduced below).
X   Otherwise UDP packets larger than the maximum IP fragment size of
X   your net will mysteriously fail.
X
X2. Edit the first few lines of the Makefile to reflect local conditions.
X
X3. If your client and server machines have different uid/username
X   mappings you may wish to run ugidd on the client (see below).
X   Otherwise the server assumes  one-to-one correspondence between
X   uids and gids on the server.
X
X4. "make" and "make install".
X
X5. Create a /etc/unfsd_exports file to authorize clients of your server,
X   and set desired options (Note: options may also be set with the
X   command that starts the server see unfsd(8) and installation step 7).
X
X6. Run "unfsmntd" and "unfsd" and try to mount server file systems from
X   your authorized clients.
X
X7. Arrange for the server to be started at boot-time.  EG in /etc/rc.local
X   add:
X	if [ -f /etc/unfsmntd ]; then
X		/etc/unfsmntd
X	fi
X	if [ -f /etc/unfsd ]; then
X		/etc/unfsd
X	fi
X
X8. Edit client fstabs to mount from the new server at boot-time.
X
XUGIDD
X=====
X
XIf you have the same set of user group names on your client and server
Xmachines but differing uids and gids you may wish to run the ugidd on
Xthe clients and enable to server to map between local and remote ids
Xusing this pprogram.  Ugidd is a simple rpc-based service that supplies
Xtranslations between names and uid/gids on the client.  To guard against
Xtrojan horses the ugidd authenicates itself by signalling the unfsd on
Xa reserved internet port.
X
XTo install ugidd on a client:
X
X1. "make ugidd" and copy ugidd to standard place like /usr/etc/rpc.ugidd
X
X2. Either run it from /etc/rc.local with the command
X	/usr/etc/rpc.ugidd -d
X   or if using a version of inetd that supports sunrpc, invoke it
X   from inetd with the line
X	rpc     udp     /usr/etc/rpc.ugidd      545580417 1
X   in /etc/servers (SunOS 3.x) or /etc/inetd.conf (4.3 BSD).
X   (See ugidd(8)).
X
X3. Set the "map_daemon" option for this client in the servers
X   /etc/unfsd_exports file.  (See unfsd_exports(5)).
X
XNote: When ugidd fails to respond to a server, or the specified
Xname (or uid/gid) has no corresponding uid/gid (or name), the
Xserver assumes a mapping to the unprivelegded user "nobody" (uid: -2).
X
XTCP/IP FIX
X==========
X
XHere follows a fix to the Van Jacobson TCP/IP upgrade.
X
XSubject: bug in new tcp/ip code
X
X[ posted to comp.bugs.4bsd Thu Apr 21 16:36:04 1988 ]
X
XThere is a bug in the upgrade of 4.3BSD networking code that was
Xrecently posted to comp.bugs.4bsd.ucb-fixes.  When an IP packet is
Xtoo large for an interface and must therefore be fragmented, some of
Xthe fields of the IP header in the first packet are not converted to
Xnetwork byte-order.
X
XThe fix is as follows:
X
X*** /sys/netinet/ip_output.c.orig	Tue Apr 12 15:52:46 1988
X--- /sys/netinet/ip_output.c	Thu Apr 21 13:16:16 1988
X***************
X*** 232,239 ****
X  	 * and updating header, then send each fragment (in order).
X  	 */
X  	m_adj(m0, hlen + firstlen - ip->ip_len);
X! 	ip->ip_len = hlen + firstlen;
X! 	ip->ip_off |= IP_MF;
X  	ip->ip_sum = 0;
X  	ip->ip_sum = in_cksum(m0, hlen);
X  sendorfree:
X--- 232,239 ----
X  	 * and updating header, then send each fragment (in order).
X  	 */
X  	m_adj(m0, hlen + firstlen - ip->ip_len);
X! 	ip->ip_len = htons((u_short)(hlen + firstlen));
X! 	ip->ip_off = htons((u_short)(ip->ip_off | IP_MF));
X  	ip->ip_sum = 0;
X  	ip->ip_sum = in_cksum(m0, hlen);
X  sendorfree:
X
XSECURITY ISSUES
X===============
X
XWhen I first started looking at standard SunOS 3.x NFS I was pretty well
Xappalled by the lack of security.  I later found that Security could be
Ximproved considerably it was just that the defaults were (in my opinion)
Xwrong.  By default, unfsd insists that NFS requests originate from a
Xsecure port from a known internet address.
X
XUnfsd presumes that the kernel of the client deals correctly with access
Xpermission checks.  In my experience this seems to be true of SunOS 3.x
Xkernels for read accesses across NFS, but not for write accesses.  It is
Xfor this reason that write accesses remain unimplemented in the current
Xversion of the server.
X
X----------
X
XMark Shand
XJoint Microelectronics Research Centre
XSchool of Electrical Engineering
XUniversity of NSW
XPO Box 1
XKensington
XNSW 2033
XAUSTRALIA
X
X+61 2 697 4898
X
Xshand@cad.jmrc.eecs.unsw.oz
________This_Is_The_END________
if test `wc -l < README` -ne 171; then
	echo 'shar: README was damaged during transit (should have been 171 bytes)'
fi
fi		; : end of overwriting check
echo 'x - fh.c'
if test -f fh.c; then echo 'shar: not overwriting fh.c'; else
sed 's/^X//' << '________This_Is_The_END________' > fh.c
X/* UNFSD - copyright Mark A Shand, May 1988.
X * This software maybe be used for any purpose provided
X * the above copyright notice is retained.  It is supplied
X * as is, with no warranty expressed or implied.
X */
X
X/*
X *	FILE HANDLE PACKAGE FOR USER-LEVEL NFS SERVER
X *
X *	Interfaces:
X *	    pseudo_inode
X *		mostly used internally, but also called from unfsd.c
X *		when reporting directory contents.
X *	    fh_pr
X *		debugging primitive; converts file handle into a printable
X *		text string
X *	    fh_create
X *		establishes initial file handle; called from mount daemon
X *	    fh_path
X *		returns unix path corresponding to fh
X *	    fh_fd
X *		returns open file descriptor for given file handle;
X *		provides caching of open files
X *	    fd_idle
X *		provides mututal exclusion of normal file descriptor cache
X *		use, and alarm-driven cache flushing
X *	    fh_compose
X *		construct new file handle from existing file handle and
X *		directory entry
X *	    fh_psi
X *		returns pseudo_inode corresponding to file handle
X */
X
X#include <sys/param.h>
X#include <sys/types.h>
X#include <sys/file.h>
X#include <sys/time.h>
X#include <sys/stat.h>
X#include <rpc/rpc.h>
X#include <sys/dir.h>
X#include <strings.h>
X#include <errno.h>
X#include <syslog.h>
X
X/* mask SUNOS/BSD4.3 syslog incompatibilities */
X#ifndef LOG_DAEMON
X#define	LOG_DAEMON	0
X#endif /* LOG_DAEMON */
X
X#ifdef DEBUG
X#include <stdio.h>
XFILE *debuglog;
X#endif DEBUG
X
X#include "nfs_prot.h"
X
X#include "fh.h"
X
X#define	FHC_XONLY_PATH	01
X#define	FHC_BUSY	02	/* NOT USED */
X
X#define	CACHE_SIZE_LIMIT	500
X#define	LOWAT_CACHE_SIZE	3*CACHE_SIZE_LIMIT
X#define	HIWAT_CACHE_SIZE	4*CACHE_SIZE_LIMIT
X#define HASH_TAB_SIZE		(5*CACHE_SIZE_LIMIT | 03)
X
X/*
X * Paths constructed in this system always consist of real directories
X * (excepting the last element) i.e. they do not contain symbolic links.
X * This is guaranteed by the way NFS constructs the paths.
X * As a consequence we may assume that
X *	/x/y/z/.. == /x/y
X * and	/x/y/z/. == /x/y/z
X * provided that z != . && z != ..
X * These relations are exploited in fh_compose.
X *
X * Further assumptions:
X *	All cached pathnames consist of a leading /
X *	followed by zero or more / separated names
X *	s.t.
X *		name != .
X *		name != ..
X *		index(name, '/') == 0
X */
X
Xtypedef struct fhcache
X{
X	struct fhcache	*next;
X	struct fhcache	*prev;
X	struct fhcache	*hash_next;
X	svc_fh	h;
X	int	fd;
X	int	omode;
X	char	*path;
X	time_t	last_used;
X	int	flags;
X}
X	fhcache;
X
Xstatic int path_psi();
Xstatic fhcache fh_head, fh_tail, *last_flushable;
Xstatic fhcache *fh_hashed[HASH_TAB_SIZE];
Xstatic int fh_list_size;
Xstatic time_t	curtime;
Xextern int errno;
X
Xstatic void
Xfh_move_to_front(fhc)
Xfhcache	*fhc;
X{
X	/* Remove from current posn */
X	fhc->prev->next = fhc->next;
X	fhc->next->prev = fhc->prev;
X	/* Insert at head */
X	fhc->prev = &fh_head;
X	fhc->next = fh_head.next;
X	fhc->prev->next = fhc;
X	fhc->next->prev = fhc;
X}
X
Xstatic void
Xfh_inserthead(fhc)
Xfhcache	*fhc;
X{
X	fhcache	**hash_slot;
X
X	/* Insert at head */
X	fhc->prev = &fh_head;
X	fhc->next = fh_head.next;
X	fhc->prev->next = fhc;
X	fhc->next->prev = fhc;
X	fh_list_size++;
X
X	/* Insert into hash tab */
X	hash_slot = &(fh_hashed[fhc->h.psi % HASH_TAB_SIZE]);
X	fhc->hash_next = *hash_slot;
X	*hash_slot = fhc;
X}
X
Xstatic fhcache *
Xfh_lookup(psi)
Xu_long	psi;
X{
X	fhcache	*fhc;
X	fhc = fh_hashed[psi % HASH_TAB_SIZE];
X	while (fhc != NULL && fhc->h.psi != psi)
X		fhc = fhc->hash_next;
X	return fhc;
X}
X
Xstatic void
Xfh_delete(fhc)
Xfhcache *fhc;
X{
X	fhcache	**hash_slot;
X
X	/* Remove from current posn */
X	fhc->prev->next = fhc->next;
X	fhc->next->prev = fhc->prev;
X	fh_list_size--;
X
X	/* Remove from hash tab */
X	hash_slot = &(fh_hashed[fhc->h.psi % HASH_TAB_SIZE]);
X	while (*hash_slot != NULL && *hash_slot != fhc)
X		hash_slot = &((*hash_slot)->hash_next);
X	if (*hash_slot == NULL)
X		syslog(LOG_DAEMON|LOG_WARNING,
X			"internal inconsistency -- fhc(%x) not in hash table", fhc);
X	else
X		*hash_slot = fhc->hash_next;
X
X	/* Free storage */
X	if (fhc->path != NULL)
X	{
X#ifdef DEBUG
X		if (debuglog != NULL)
X			fprintf(debuglog, "flushing: %s\n",
X				fhc->path);
X#endif DEBUG
X		free(fhc->path);
X	}
X	free(fhc);
X}
X
X/*
X * INODES and DEVICES.  NFS assumes that each file within an NFS mounted
X * file-system has a unique inode number.  Thus to mount an entire file
X * hierarchy, as this server sets out to do, requires mapping from inode/devs
X * to pseudo-inode.  Furthermore mount points must be detected and so that
X *	pseudo-inode("name") == pseudo-inode(direntry("name/../name"))
X * One option is to force the directory entry inode to correspond to the
X * result of the stat call, but this involves stat'ing every directory entry
X * during a readdir.  Instead we force the stat call to corresopnd to the
X * directory entry inode (see inner_getattr).  Of course this technique
X * requires that the parent directory is readable.  If it is not the normal
X * stat call result is used.  There is no chance of conflict because the
X * directory can never be read.
X *
X * In theory unique pseudo-inodes cannot be guaranteed, since inode/dev
X * contains 48 bits of information which must be crammed into an inode
X * number constrained to 32 bits.  Fortunately inodes numbers tend to be
X * small (often < 64k, almost always < 512k)
X */
X
Xint
Xpseudo_inode(inode,dev)
Xu_long	inode;
Xu_short	dev;
X{
X	register dmajor,dminor;
X
X	/* Assuming major and minor numbers are small integers,
X	 * gravitate bits of dmajor & dminor device number to
X	 * high-order bits of word, to avoid clash with real inode num.
X	 */
X	/* reverse (byte-wise) */
X	dmajor = ((dev & 0xf0f) << 4) | ((dev & 0xf0f0) >> 4);
X	dmajor = ((dmajor & 0x3333) << 2) | ((dmajor & 0xcccc) >> 2);
X	dmajor = ((dmajor & 0x5555) << 1) | ((dmajor & 0xaaaa) >> 1);
X	/* spread low-16 -> 32 with 0's in even posn */
X	dmajor = ((dmajor & 0xff00) << 8) | (dmajor & 0xff);
X	dmajor = ((dmajor & 0xf000f0) << 4) | (dmajor & 0xf000f);
X	dmajor = ((dmajor & 0xc0c0c0c) << 2) | (dmajor & 0x3030303);
X	dmajor = ((dmajor & 0x22222222) << 1) | (dmajor & 0x11111111);
X	dminor = (dmajor & 0x5555) << 15;
X	dmajor = dmajor & 0x55550000;
X
X	return (dmajor | dminor) ^ inode;
X}
X
X#define hash_psi(psi) (((psi)^((psi)>>8)^((psi)>>16)^((psi)>>24)) & 0xff)
X
Xint
Xmallocfailed()
X{
X	syslog(LOG_DAEMON|LOG_WARNING, "malloc failed -- exiting");
X	exit(1);
X}
X
X/* flush_cache() is invoked periodically from SIGALRM, and on
X * demand from fh_find.  A simple form of mutual exclusion
X * protects this routine from multiple concurrent executions.
X * Since the preemption that occurs when a signal is received
X * is one-sided, we do need an atomic test and set.  If the 
X * signal arrives between the test and the set, the first
X * invocation safely stalls until the signal-caused invocation
X * completes.
X */
Xtypedef enum { idle, active } mutex;
X
Xstatic mutex ex_state = idle;
X
X/* The following affect execute-only directories */
X#define FLUSH_INTERVAL	(60*60*12)	/* Twice a day */
X#define BUSY_RETRY_INTERVAL	(60*10)	/* Ten minutes */
X#define DISCARD_INTERVAL (FLUSH_INTERVAL*2) /* Two days */
X
Xstatic int
Xflush_cache()
X{
X	fhcache	*h;
X
X#ifdef DEBUG
X	if (debuglog != NULL)
X	{
X		long	thing;
X		time(&thing);
X		fprintf(debuglog, "flushing cache at %s: state = %s\n",
X			ctime(&thing),
X			(ex_state == idle) ? "idle" : "active");
X	}
X#endif DEBUG
X	if (ex_state == idle)
X	{
X		int	cache_size = 0;
X
X		ex_state = active;
X		time(&curtime);
X		/* Single execution thread */
X		/* discard old open files */
X		fh_fd(NULL);
X
X		/* works in empty case because: fh_tail.next = &fh_tail */
X		h = fh_head.next->next;
X		while (h != &fh_tail)
X		{
X			if (cache_size > LOWAT_CACHE_SIZE
X			    || (cache_size > CACHE_SIZE_LIMIT
X				&& (h->flags & FHC_XONLY_PATH) == 0)
X			    || curtime > h->last_used + DISCARD_INTERVAL)
X			{
X				h = h->next;
X				fh_delete(h->prev);
X			}
X			else
X			{
X				cache_size++;
X				h = h->next;
X			}
X		}
X		if (fh_list_size != cache_size)
X			syslog(LOG_DAEMON|LOG_WARNING,
X				"internal inconsistency (fh_list_size=%d) != (cache_size=%d)",
X				fh_list_size, cache_size);
X		fh_list_size = cache_size;
X		ex_state = idle;
X		signal(SIGALRM, flush_cache);
X		alarm(FLUSH_INTERVAL);
X	}
X	else
X	{
X		signal(SIGALRM, flush_cache);
X		alarm(BUSY_RETRY_INTERVAL);
X	}
X}
X
Xvoid
Xfh_init()
X{
X	fh_head.next = fh_tail.next = &fh_tail;
X	fh_head.prev = fh_tail.prev = &fh_head;
X	last_flushable = &fh_tail;
X	fh_tail.flags = FHC_XONLY_PATH;
X	signal(SIGALRM, flush_cache);
X	alarm(FLUSH_INTERVAL);
X}
X
Xstatic char *fh_buildpath();
X
Xstatic fhcache *
Xfh_find(h, create)
Xsvc_fh	*h;
Xint	create;
X{
X	fhcache	*fhc;
X
X	ex_state = active;
X	time(&curtime);
X	while ((fhc = fh_lookup(h->psi)) != NULL)
X	{
X		/* but what if hash_paths are not the same? */
X		/* Something is stale */
X		if (bcmp(h->hash_path, fhc->h.hash_path, HP_LEN) != 0)
X		{
X			if (!create)
X				return NULL;
X			fh_delete(fhc);
X			break;
X		}
X		if (fhc != fh_head.next)
X			fh_move_to_front(fhc);
X		fhc->last_used = curtime;
X		ex_state = idle;
X		return fhc;
X	}
X	if (fh_list_size > CACHE_SIZE_LIMIT)
X	{
X		/* don't flush current head */
X		while (last_flushable != fh_head.next)
X		{
X			if ((last_flushable->flags & FHC_XONLY_PATH) == 0)
X			{
X				fhc = last_flushable;
X				last_flushable = last_flushable->prev;
X				fh_delete(fhc);
X				break;
X			}
X			last_flushable = last_flushable->prev;
X		}
X		last_flushable = last_flushable->next;
X	}
X	if (create)
X	{
X		if ((fhc = (fhcache *) malloc(sizeof *fhc)) == NULL)
X			mallocfailed();
X		fhc->path = NULL;
X		fhc->last_used = curtime;
X		fhc->h = *h;
X		fh_inserthead(fhc);
X	}
X	else
X	{
X		/* attempt to contruct from hash_path */
X		char	*path;
X
X		if ((path = fh_buildpath(h)) == NULL)
X			return NULL;
X		if ((fhc = (fhcache *) malloc(sizeof *fhc)) == NULL)
X			mallocfailed();
X		fhc->path = path;
X		fhc->fd = -1;
X		fhc->flags = 0;
X		fhc->last_used = curtime;
X		fhc->h = *h;
X		fh_inserthead(fhc);
X	}
X	ex_state = idle;
X	if (fh_list_size > HIWAT_CACHE_SIZE)
X		flush_cache();
X	return fhc;
X}
X
Xstatic char *
Xfh_buildpath(h)
Xsvc_fh	*h;
X{
X	int	i;
X	int	psi;
X	char	*path;
X	struct	stat	sbuf;
X	char	pathbuf[MAXPATHLEN+MAXNAMLEN+1];
X	long	cookie_stack[HP_LEN+1];
X	char	*slash_stack[HP_LEN];
X
X	if (stat("/", &sbuf) < 0)
X		return NULL;
X	psi = pseudo_inode(sbuf.st_ino, sbuf.st_dev);
X	if (h->hash_path[0] == 0)
X	{
X		if (psi != h->psi)
X			return NULL;
X		if ((path = malloc(2)) == NULL)
X			mallocfailed();
X		strcpy(path, "/");
X		return path;
X	}
X	/* else */
X	if (hash_psi(psi) != h->hash_path[1])
X		return NULL;
X	strcpy(pathbuf, "/");
X	cookie_stack[2] = 0;
X	for (i = 2; i <= h->hash_path[0]+1; i++)
X	{
X		DIR		*dir;
X		struct direct	*dp;
X
X	    backtrack:
X		if (stat(pathbuf, &sbuf) >= 0
X		    && (dir = opendir(pathbuf)) != NULL)
X		{
X			if (cookie_stack[i] != 0)
X				seekdir(dir, cookie_stack[i]);
X			while (dp = readdir(dir))
X			{
X				if (strcmp(dp->d_name, ".") != 0
X				 && strcmp(dp->d_name, "..") != 0)
X				{
X					psi = pseudo_inode(dp->d_ino, sbuf.st_dev);
X					if (i == h->hash_path[0]+1)
X					{
X						if (psi == h->psi)
X						{
X							/*GOT IT*/
X							strcat(pathbuf, dp->d_name);
X							if ((path = malloc(strlen(pathbuf)+1)) == NULL)
X								mallocfailed();
X							strcpy(path, pathbuf);
X							closedir(dir);
X							return path;
X						}
X					}
X					else
X					{
X						if (hash_psi(psi) == h->hash_path[i])
X						{
X							/*PERHAPS WE'VE GOT IT */
X							cookie_stack[i] = telldir(dir);
X							cookie_stack[i+1] = 0;
X							slash_stack[i] = pathbuf + strlen(pathbuf);
X							strcpy(slash_stack[i], dp->d_name);
X							strcat(pathbuf, "/");
X
X							closedir(dir);
X							goto deeper;
X						}
X					}
X				}
X			}
X			/* dp == NULL */
X			closedir(dir);
X		}
X		else if (i <= h->hash_path[0]
X			&& access(pathbuf, R_OK) != 0
X			&& access(pathbuf, X_OK) == 0)
X		{
X			/* Execute-only directory?  Maybe its in the cache. */
X			/* Note: cache is frozen for duration of fh_buildpath */
X			svc_fh	xh;
X			fhcache	*fhc;
X
X			xh = *h;
X			xh.hash_path[0] = i-1;
X			if (cookie_stack[i] == 0)
X				fhc = fh_head.next;
X			else
X				fhc = ((fhcache *)(cookie_stack[i]))->next;
X			while (fhc != &fh_tail)
X				if (bcmp(xh.hash_path, fhc->h.hash_path, i) == 0
X				    && xh.hash_path[i] == hash_psi(fhc->h.psi))
X					break;
X				else
X					fhc = fhc->next;
X			if (fhc != NULL)
X			{
X				strcpy(pathbuf, fhc->path);
X				cookie_stack[i] = (long) fhc;
X				cookie_stack[i+1] = 0;
X				slash_stack[i] = rindex(pathbuf,'/')+1;
X				strcat(pathbuf, "/");
X				goto deeper;
X			}
X		}
X		/* shallower */
X		i--;
X		if (i < 2)
X			return NULL; /* SEARCH EXHAUSTED */
X		/* Prune path */
X		*(slash_stack[i]) = '\0';
X		goto backtrack;
X	    deeper: ;
X	}
X	return NULL; /* Actually not reached */
X}
X
Xchar *
Xfh_pr(fh)
Xnfs_fh	*fh;
X{
X	char	*p;
X	nfsstat	status;
X
X	p = fh_path(fh, &status);
X	if (status != NFS_OK)
X		return "///STALE///";
X	else
X		return p;
X}
X
Xint
Xfh_create(fh, path)
Xnfs_fh	*fh;
Xchar	*path;
X{
X	svc_fh	*key = (svc_fh *) fh;
X	fhcache	*h;
X	int psi;
X	nfsstat	status;
X	char	*s;
X
X	bzero((char *) fh, sizeof fh);
X	key->hash_path[0] = 0;
X	status = NFS_OK;
X	if ((psi = path_psi("/", &status, NULL)) == 0)
X		return (int) status;
X	s = path;
X	while ((s = index(s+1, '/')) != NULL)
X	{
X		if (++(key->hash_path[0]) >= HP_LEN)
X			return (int) NFSERR_NAMETOOLONG;
X		key->hash_path[key->hash_path[0]] = hash_psi(psi);
X		*s = '\0';
X		if ((psi = path_psi(path, &status, NULL)) == 0)
X			return (int) status;
X		*s = '/';
X	}
X	if (*(rindex(path, '/')+1) != '\0')
X	{
X		if (++(key->hash_path[0]) >= HP_LEN)
X			return (int) NFSERR_NAMETOOLONG;
X		key->hash_path[key->hash_path[0]] = hash_psi(psi);
X		if ((psi = path_psi(path, &status, NULL)) == 0)
X			return (int) status;
X	}
X	key->psi = psi;
X	h = fh_find(key, 1);
X	/* assert(h != NULL); */
X	if (h->path == NULL)
X	{
X		h->fd = -1;
X		if ((h->path = malloc(strlen(path)+1)) == NULL)
X			mallocfailed();
X		strcpy(h->path, path);
X		h->flags = 0;
X	}
X	return (int) status;
X}
X
Xchar *
Xfh_path(fh, status)
Xnfs_fh	*fh;
Xnfsstat	*status;
X{
X	fhcache	*h;
X
X	if ((h = fh_find((svc_fh *) fh, 0)) == NULL)
X	{
X		*status = NFSERR_STALE;
X		return NULL;
X	}
X	*status = NFS_OK;
X	return h->path;
X}
X
Xstatic mutex io_state = idle;
X
Xint
Xfh_fd(fh, status, omode)
Xnfs_fh	*fh;
Xnfsstat	*status;
Xint	omode;
X{
X	/* Currently we cache 1 open file descriptor.
X	 * in the future we could allocate an array of size
X	 * getdtablesize() which would contain psi's to provide
X	 * an mapping from descriptor to psi's.
X	 * Then we could maintain many concurrently open files.
X	 */
X	fhcache		*h;
X	static int	fd = -1;
X	static int	psi = 0;
X
X	if (fh == NULL && io_state == idle)
X	{
X		/* special case -- request to flush fd cache */
X		if (fd >= 0)
X			close(fd);
X		fd = -1;
X		return -1;
X	}
X
X	if ((h = fh_find((svc_fh *) fh, 0)) == NULL)
X	{
X		*status = NFSERR_STALE;
X		return -1;
X	}
X	io_state = active;
X	if (fd >= 0)
X	{
X		if (psi == h->h.psi && h->omode == omode)
X			return fd;
X		close(fd);
X	}
X	errno = 0;
X	h->fd = fd = open(h->path, omode);
X	h->omode = omode;
X	psi = h->h.psi;
X	*status = (nfsstat) errno;
X	return fd;
X}
X
Xint
Xfd_idle(fd)
Xint	fd;
X{
X	io_state = idle;
X}
X
Xnfsstat
Xfh_compose(dopa, new_fh, sbpp)
Xdiropargs	*dopa;
Xnfs_fh	*new_fh;
Xstruct stat **sbpp;
X{
X	svc_fh	*key;
X	fhcache	*dirh, *h;
X	char	*sindx;
X	int	is_dd;
X	nfsstat	ret;
X	struct stat	sbuf;
X	char	pathbuf[MAXPATHLEN+MAXNAMLEN+1];
X
X	if ((dirh = fh_find((svc_fh *) &(dopa->dir), 0)) == NULL)
X		return NFSERR_STALE;
X
X	*new_fh = dopa->dir;
X	/* Construct path */
X
X	if (strcmp(dopa->name, ".") == 0)
X	{
X		*sbpp = NULL;
X		return NFS_OK;
X	}
X	if (strcmp(dopa->name, "..") == 0)
X	{
X		is_dd = 1;
X		sindx = rindex(dirh->path, '/');
X		if (sindx == dirh->path)
X			strcpy(pathbuf, "/");
X		else
X		{
X			int	len = sindx - dirh->path;
X			strncpy(pathbuf, dirh->path, len);
X			pathbuf[len] = '\0';
X		}
X	}
X	else
X	{
X		int	len = strlen(dirh->path);
X
X		is_dd = 0;
X		if (dirh->path[len-1] == '/')
X			len--;
X		strncpy(pathbuf, dirh->path, len);
X		pathbuf[len] = '/';
X		strcpy(pathbuf + (len+1), dopa->name);
X	}
X
X	key = (svc_fh *) new_fh;
X	if ((key->psi = path_psi(pathbuf, &ret, *sbpp)) == 0)
X		return ret;
X
X	if (is_dd)
X		key->hash_path[key->hash_path[0]--] = 0;
X	else
X	{
X		if (++(key->hash_path[0]) >= HP_LEN)
X			return NFSERR_NAMETOOLONG;
X		key->hash_path[key->hash_path[0]] = hash_psi(dirh->h.psi);
X	}
X	h = fh_find(key, 1);
X	/* assert(h != NULL); */
X	if (h->path == 0)
X	{
X		h->fd = -1;
X		if ((h->path = malloc(strlen(pathbuf)+1)) == NULL)
X			mallocfailed();
X		strcpy(h->path, pathbuf);
X		h->flags = 0;
X		if (!is_dd
X		    && access(dirh->path, R_OK) != 0
X		    && access(dirh->path, X_OK) == 0)
X			h->flags |= FHC_XONLY_PATH;
X	}
X	return NFS_OK;
X}
X
Xint
Xfh_psi(fh)
Xnfs_fh	*fh;
X{
X	svc_fh	*h = (svc_fh *) fh;
X	return h->psi;
X}
X
Xstatic int
Xpath_psi(path, status, sbp)
X	char	*path;
X	nfsstat	*status;
X	struct stat *sbp;
X{
X	struct stat sbuf;
X
X	if (sbp == NULL)
X		sbp = &sbuf;
X	if (lstat(path, sbp) < 0)
X		*status = (nfsstat) errno;
X		
X	if ((sbp->st_mode & S_IFMT) == S_IFDIR && strcmp(path, "/") != 0)
X	{
X		/* Special case for directories--test for mount point */
X		struct	stat	ddbuf;
X		char	*sindx;
X		char	*name;
X		char	squirrel;
X
X		/* find start of last component of path */
X		if ((sindx = rindex(path, '/')) == path)
X		{
X			sindx++;
X			name = sindx;
X		}
X		else
X			name = sindx + 1;
X		/* remove last element of path */
X		squirrel = *sindx;
X		*sindx = '\0';
X		if (lstat(path, &ddbuf) < 0)
X		{
X			*sindx = squirrel;
X			*status = (nfsstat) errno;
X			return 0;
X		}
X		/* sindx now points to directory entry name */
X		if (ddbuf.st_dev != sbp->st_dev)
X		{
X			/* directory is a mount point */
X			DIR	*dirp;
X			struct direct *dp;
X
X			errno = 0;
X			if ((dirp = opendir(path)) == NULL)
X			{
X				*sindx = squirrel; /* restore path */
X				if (errno == EACCES)
X					goto unreadable;
X				if (errno != 0)
X					*status = (nfsstat) errno;
X				else
X					*status = NFSERR_NOENT;
X			}
X			else
X			{
X				*sindx = squirrel; /* restore path */
X				*status = NFS_OK;
X				do
X				{
X					if ((dp = readdir(dirp)) == NULL)
X					{
X						*status = NFSERR_NOENT;
X						closedir(dirp);
X						return 0;
X					}
X				} while(strcmp(name, dp->d_name) != 0);
X				sbp->st_dev = ddbuf.st_dev;
X				sbp->st_ino = dp->d_ino;
X				closedir(dirp);
X			}
X		}
X		else
X			*sindx = squirrel; /* restore path */ 
X	    unreadable:
X		;
X	}
X	return pseudo_inode(sbp->st_ino, sbp->st_dev);
X}
________This_Is_The_END________
if test `wc -l < fh.c` -ne 825; then
	echo 'shar: fh.c was damaged during transit (should have been 825 bytes)'
fi
fi		; : end of overwriting check
echo 'x - fh.h'
if test -f fh.h; then echo 'shar: not overwriting fh.h'; else
sed 's/^X//' << '________This_Is_The_END________' > fh.h
X/* UNFSD - copyright Mark A Shand, May 1988.
X * This software maybe be used for any purpose provided
X * the above copyright notice is retained.  It is supplied
X * as is, with no warranty expressed or implied.
X */
X
X/* compatibility between mount and nfs_prot */
X#ifndef NFS_FHSIZE
X#define NFS_FHSIZE	FHSIZE
X#endif
X
X#define	HP_LEN	(NFS_FHSIZE - sizeof(u_long))
X
Xtypedef struct
X{
X	u_long	psi;
X	u_char	hash_path[HP_LEN];
X	/* Hashed search path to this file.
X	 * path is: hash_path[1] ... hash_path[hash_path[0]]
X	 *
X	 * hash_path[hash_path[0]+1] ... hash_path[HP_LEN-1] == 0
X	 */
X}
X	svc_fh;
X
Xextern void fh_init();
Xextern int fh_fd();
Xextern char *fh_path();
Xextern nfsstat fh_compose();
Xextern char *fh_pr();
Xextern int fh_psi();
________This_Is_The_END________
if test `wc -l < fh.h` -ne 31; then
	echo 'shar: fh.h was damaged during transit (should have been 31 bytes)'
fi
fi		; : end of overwriting check
echo 'x - init.c'
if test -f init.c; then echo 'shar: not overwriting init.c'; else
sed 's/^X//' << '________This_Is_The_END________' > init.c
X/* UNFSD - copyright Mark A Shand, May 1988.
X * This software maybe be used for any purpose provided
X * the above copyright notice is retained.  It is supplied
X * as is, with no warranty expressed or implied.
X */
X
X/*
X**	Udp socket establishment routine
X*/
X
X
X#ifndef	lint
Xstatic char	sccsid[]	= "@(#)makesock.c	1.0	88/03/12";
X#endif	lint
X
X#include "unfsd.h"
Xextern char	*malloc();
Xextern char	*realloc();
X
X#define DEFAULT_EXPORTSFILE	EXPORTSFILE
X
X#ifndef SYSERROR
X#define SYSERROR	(-1)
X#endif
X
Xint
Xmakesock(port,socksz)
Xint	port;
Xint	socksz;
X{
X	struct sockaddr_in	my_sock;
X	int			s;
X	extern int		errno;
X	extern char		*sys_errlist;
X
X	bzero((char *)&my_sock, sizeof(my_sock));
X	my_sock.sin_addr.s_addr = INADDR_ANY;
X	my_sock.sin_family = AF_INET;
X	my_sock.sin_port = htons(port);
X
X	if ((s = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
X	{
X		syslog(LOG_DAEMON|LOG_ERR, "could not make a socket: %s",
X			sys_errlist[errno]);
X		return SYSERROR;
X	}
X#ifdef SO_SNDBUF
X	{
X		int	sblen, rblen;
X
X		sblen = rblen = socksz + 1024;
X		/* 1024 for rpc & transport overheads */
X		if (
X		setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sblen, sizeof sblen) < 0
X		||
X		setsockopt(s, SOL_SOCKET, SO_RCVBUF, &rblen, sizeof sblen) < 0
X		)
X		syslog(LOG_DAEMON|LOG_ERR, "setsockopt failed",
X			sys_errlist[errno]);
X	}
X#endif
X
X	if (bind(s, &my_sock, sizeof(my_sock)) == -1)
X	{
X		syslog(LOG_DAEMON|LOG_ERR, "could not bind name to socket",
X			sys_errlist[errno]);
X		return SYSERROR;
X	}
X
X	return s;
X}
X
Xftype ft_map[16];
Xint svc_euid;
Xint svc_egid;
Xint cur_gid;
Xint svc_ngids;
Xint svc_gids[NGROUPS+2];
X
Xstatic clnt_param	*clients = NULL;
Xstatic clnt_param	*default_client = NULL;
X
X#define	LINE_SIZE	1024
X
Xstatic char *
Xparse_opts(s, terminator, o, client_name)
Xchar	*s;
Xchar	terminator;
Xoptions	*o;
Xchar	*client_name;
X{
X	/* parse option string pointed to by s and set o accordingly */
X	char	kwdbuf[LINE_SIZE];
X	char	*k;
X
X	/* skip white */
X	while (isspace(*s))
X		s++;
X	while (*s != terminator)
X	{
X		k = kwdbuf;
X		while (isalnum(*s) || *s == '_')
X			*k++ = *s++;
X		*k = '\0';
X		/* process keyword */
X		if (strcmp(kwdbuf, "secure") == 0)
X			o->secure_port = 1;
X		else if (strcmp(kwdbuf, "insecure") == 0)
X			o->secure_port = 0;
X		else if (strcmp(kwdbuf, "root_squash") == 0)
X			o->root_squash = 1;
X		else if (strcmp(kwdbuf, "no_root_squash") == 0)
X			o->root_squash = 0;
X		else if (strcmp(kwdbuf, "ro") == 0)
X			o->read_only = 1;
X		else if (strcmp(kwdbuf, "rw") == 0)
X			o->read_only = 0;
X		else if (strcmp(kwdbuf, "link_relative") == 0)
X			o->link_relative = 1;
X		else if (strcmp(kwdbuf, "link_absolute") == 0)
X			o->link_relative = 0;
X		else if (strcmp(kwdbuf, "map_daemon") == 0)
X			o->uidmap = map_daemon;
X		else if (strcmp(kwdbuf, "map_identity") == 0)
X			o->uidmap = identity;
X		else
X			syslog(LOG_DAEMON|LOG_ERR, "Unknown keyword \"%s\"", kwdbuf);
X		while (isspace(*s))
X			s++;
X		if (*s == ',')
X			s++;
X		else if (!isalnum(*s) && *s != '_' && *s != '\0')
X		{
X			if (client_name == NULL)
X				syslog(LOG_DAEMON|LOG_ERR,
X				    "comma expected in option list for default client (found %c)", *s);
X			else
X				syslog(LOG_DAEMON|LOG_ERR,
X				    "comma expected in option list for client %s (found %c)",
X				    client_name, *s);
X		}
X		while (isspace(*s))
X			s++;
X		if (*s == '\0' && *s != terminator)
X		{
X			syslog(LOG_DAEMON|LOG_ERR,
X				"missing terminator \"%c\" on option list",
X				terminator);
X			return s;
X		}
X	}
X	while (isspace(*s))
X		s++;
X	return s;
X}
X
Xstatic int
Xfilt_getc(f)
XFILE	*f;
X{
X	int	c;
X
X	c = getc(f);
X	if (c == '\\')
X	{
X		c = getc(f);
X		if (c == '\n')
X			c = ' ';
X		else if (c != EOF)
X			ungetc(c, f);
X		c = '\\';
X	}
X	else if (c == '#')
X	{
X		int lastc = c;
X		while ((c = getc(f)) != '\n' && c != EOF)
X			lastc = c;
X		if (c == '\n' && lastc == '\\')
X			c = getc(f);
X	}
X	return c;
X}
X
X#define CHUNK_SIZE	512
X
Xstatic int
Xgetline(lbuf, f)
Xchar	**lbuf;
XFILE	*f;
X{
X	register	c;
X	register char	*p;
X	char	*buf;
X	int	sz = CHUNK_SIZE;
X
X	if ((buf = malloc(CHUNK_SIZE)) == NULL)
X		mallocfailed();
X	p = buf;
X	while ((c = filt_getc(f)) != '\n' && c != EOF)
X	{
X		if (p - buf == sz-2)
X		{
X			if ((buf = realloc(buf, sz*2)) == NULL)
X				mallocfailed();
X			p = buf + sz-2;
X			sz *= 2;
X		}
X		*p++ = c;
X	}
X	if (c == EOF && p == buf)
X	{
X		free(buf);
X		*lbuf = NULL;
X		return 0;
X	}
X	*p++ = '\0';
X	*lbuf = buf;
X	return 1;
X}
X
Xunfsd_init(argc, argv)
Xint	argc;
Xchar	**argv;
X{
X	int	i, n;
X	FILE	*f;
X	char	*lbuf;
X	char	*p, *q, *r;
X	char	*exportsfile = DEFAULT_EXPORTSFILE;
X	struct hostent	*hent;
X	clnt_param *tmp;
X	char	*mount_point;
X
X	/* options */
X	int	promiscuous = 0;
X	char	*o_string = NULL;
X	options	def_opts;
X
X#ifndef DEBUG
X	{
X		int fd;
X
X		if (fork())
X			exit(0);
X		close(0);
X		close(1);
X		close(2);
X		if ((fd = open("/dev/tty", 2)) >= 0)
X		{
X			ioctl(fd, TIOCNOTTY, (char *)0);
X			(void) close(fd);
X		}
X	}
X#endif DEBUG
X	/* setup defaults */
X	def_opts.uidmap = identity;
X	def_opts.root_squash = 0;
X	def_opts.secure_port = 1;
X	def_opts.read_only = 1;
X	def_opts.link_relative = 1;
X
X	openlog("unfsd", LOG_PID|LOG_TIME);
X
X	argc--; argv++;
X	while (argc > 0)
X	{
X		if ((*argv)[0] == '-')
X		{
X			switch ((*argv)[1])
X			{
X				case 'o':
X					if ((*argv)[2] == '\0' && argc > 1)
X					{
X						argc--; argv++;
X						o_string = *argv;
X					}
X					else
X						o_string = *argv + 2;
X					break;
X				case 'p':
X					promiscuous = 1;
X					break;
X				case 'f':
X					if ((*argv)[2] == '\0' && argc > 1)
X					{
X						argc--; argv++;
X						exportsfile = *argv;
X					}
X					else
X						exportsfile = *argv + 2;
X					break;
X				default:
X					syslog(LOG_DAEMON|LOG_ERR, "Unknown option");
X			}
X		}
X		else
X			syslog(LOG_DAEMON|LOG_ERR, "Bad argument -- %s", *argv);
X		argc--; argv++;
X	}
X
X	fh_init();
X
X	ft_map[0] = NFNON;
X	for (i = 1; i < 16; i++)
X		ft_map[i] = NFBAD;
X#ifdef S_IFIFO
X	ft_map[ft_extr(S_IFIFO)] = NFFIFO;
X#endif
X	ft_map[ft_extr(S_IFCHR)] = NFCHR;
X	ft_map[ft_extr(S_IFDIR)] = NFDIR;
X	ft_map[ft_extr(S_IFBLK)] = NFBLK;
X	ft_map[ft_extr(S_IFREG)] = NFREG;
X	ft_map[ft_extr(S_IFLNK)] = NFLNK;
X	ft_map[ft_extr(S_IFSOCK)] = NFSOCK;
X
X	umask(0);
X
X	svc_euid = geteuid();
X	svc_ngids = getgroups(NGROUPS, svc_gids);
X	/* Does this always include gid and egid? I don't know. Play it safe */
X	if (svc_ngids < 0)
X		svc_ngids = 0;
X	n = getgid();
X	for (i = 0; i < svc_ngids; i++)
X		if (svc_gids[i] == n)
X			break;
X	if (i == svc_ngids)
X		svc_gids[svc_ngids++] = n;
X	n = svc_egid = getegid();
X	for (i = 0; i < svc_ngids; i++)
X		if (svc_gids[i] == n)
X			break;
X	if (i == svc_ngids)
X		svc_gids[svc_ngids++] = n;
X	cur_gid = svc_gids[0];
X	if (o_string != NULL)
X		parse_opts(o_string, '\0', &def_opts, NULL);
X
X	if (exportsfile != NULL)
X	{
X		if ((f = fopen(exportsfile, "r")) == NULL)
X		{
X			syslog(LOG_DAEMON|LOG_WARNING, "Could not open %s: %m", exportsfile);
X			exit(1);
X		}
X		/* process exports file */
X		while (getline(&lbuf, f))
X		{
X			p = lbuf;
X			while (isspace(*p))
X				p++;
X			q = p;
X			/* file-system name */
X			while (*q != '\0' && !isspace(*q))
X				q++;
X			if ((mount_point = malloc(q-p+1)) == NULL)
X				mallocfailed();
X			for (r = mount_point; p < q;)
X				*r++ = *p++;
X			*r = '\0';
X			p = q;
X			while (isspace(*p))
X				p++;
X			while (*p != '\0')
X			{
X				q = p;
X				/* host name */
X				while (*q != '\0' && !isspace(*q) && *q != '(')
X					q++;
X				if ((tmp = (clnt_param *) malloc(sizeof *tmp)) == NULL
X				 || (tmp->clnt_name = malloc(q-p+1)) == NULL)
X					mallocfailed();
X				for (r = tmp->clnt_name; p < q;)
X					*r++ = *p++;
X				*r = '\0';
X				tmp->mount_point = mount_point;
X				if ((hent = gethostbyname(tmp->clnt_name)) == NULL)
X				{
X					syslog(LOG_DAEMON|LOG_WARNING, "Unknown host %s in %s",
X						tmp->clnt_name, exportsfile);
X					free(tmp->clnt_name); free(tmp);
X					continue;
X				}
X				tmp->clnt_addr = *((struct in_addr *)hent->h_addr);
X				tmp->next = clients;
X				clients = tmp;
X				tmp->o = def_opts;
X				while (isspace(*p))
X					p++;
X				if (*p == '(')
X					p = parse_opts(p+1, ')', &(tmp->o), tmp->clnt_name);
X			}
X			free(lbuf);
X		}
X		fclose(f);
X	}
X	if (promiscuous)
X	{
X		if ((tmp = (clnt_param *) malloc(sizeof *tmp)) == NULL)
X			mallocfailed();
X		tmp->clnt_name = NULL;
X		tmp->mount_point = NULL;
X		default_client = tmp;
X		tmp->o = def_opts;
X	}
X}
X
Xint
X_in_gid_set(gid)
Xint	gid;
X{
X	int i;
X
X	for (i = 0; i < svc_ngids; i++)
X		if (svc_gids[i] == gid)
X		{
X			cur_gid = gid;
X			return 1;
X		}
X	return 0;
X}
X
Xclnt_param *
Xknownclient(rqstp)
X	struct svc_req *rqstp;
X{
X	clnt_param	**cpp, *cp;
X
X	/* find host parameter struct */
X	for (cpp = &clients; *cpp != NULL; cpp = &((*cpp)->next))
X	{
X		if ((*cpp)->clnt_addr.s_addr == svc_getcaller(rqstp->rq_xprt)->sin_addr.s_addr)
X		{
X			cp = *cpp;
X			if (cp != clients)
X			{
X				/* Move to front */
X				*cpp = cp->next;
X				cp->next = clients;
X				clients = cp;
X			}
X			goto found_it;
X		}
X	}
X	if (default_client != NULL)
X		cp = default_client;
X	else
X	{
X		syslog(LOG_DAEMON|LOG_CRIT, "Access attempt by unknown client %08X",
X			ntohl(svc_getcaller(rqstp->rq_xprt)->sin_addr));
X		return NULL;
X	}
X    found_it:
X	/* check request originated on a privileged port */
X	if (ntohs(svc_getcaller(rqstp->rq_xprt)->sin_port) >= IPPORT_RESERVED && cp->o.secure_port)
X	{
X		syslog(LOG_DAEMON|LOG_CRIT, "NFS request from %08X originated on insecure port",
X			ntohl(svc_getcaller(rqstp->rq_xprt)->sin_addr));
X		return NULL;
X	}
X	return cp;
X}
________This_Is_The_END________
if test `wc -l < init.c` -ne 462; then
	echo 'shar: init.c was damaged during transit (should have been 462 bytes)'
fi
fi		; : end of overwriting check
echo 'x - mount.x'
if test -f mount.x; then echo 'shar: not overwriting mount.x'; else
sed 's/^X//' << '________This_Is_The_END________' > mount.x
X/* @(#)mount.x	1.2 87/11/12 3.9 RPCSRC */
X/* @(#)mount.x 1.2 87/09/18 Copyr 1987 Sun Micro */
X
X/*
X * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
X * unrestricted use provided that this legend is included on all tape
X * media and as a part of the software program in whole or part.  Users
X * may copy or modify Sun RPC without charge, but are not authorized
X * to license or distribute it to anyone else except as part of a product or
X * program developed by the user.
X * 
X * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
X * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
X * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
X * 
X * Sun RPC is provided with no support and without any obligation on the
X * part of Sun Microsystems, Inc. to assist in its use, correction,
X * modification or enhancement.
X * 
X * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
X * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
X * OR ANY PART THEREOF.
X * 
X * In no event will Sun Microsystems, Inc. be liable for any lost revenue
X * or profits or other special, indirect and consequential damages, even if
X * Sun has been advised of the possibility of such damages.
X * 
X * Sun Microsystems, Inc.
X * 2550 Garcia Avenue
X * Mountain View, California  94043
X */
X
X/*
X * Protocol description for the mount program
X */
X
X
Xconst MNTPATHLEN = 1024;	/* maximum bytes in a pathname argument */
Xconst MNTNAMLEN = 255;		/* maximum bytes in a name argument */
Xconst FHSIZE = 32;		/* size in bytes of a file handle */
X
X/*
X * The fhandle is the file handle that the server passes to the client.
X * All file operations are done using the file handles to refer to a file
X * or a directory. The file handle can contain whatever information the
X * server needs to distinguish an individual file.
X */
Xstruct fhandle {
X	opaque data[FHSIZE];	
X};
X
X/*
X * If a status of zero is returned, the call completed successfully, and 
X * a file handle for the directory follows. A non-zero status indicates
X * some sort of error. The status corresponds with UNIX error numbers.
X */
Xunion fhstatus switch (unsigned fhs_status) {
Xcase 0:
X	struct fhandle fhs_fhandle;
Xdefault:
X	void;
X};
X
X/*
X * The type dirpath is the pathname of a directory
X */
Xtypedef string dirpath<MNTPATHLEN>;
X
X/*
X * The type name is used for arbitrary names (hostnames, groupnames)
X */
Xtypedef string name<MNTNAMLEN>;
X
X/*
X * A list of who has what mounted
X */
Xstruct mountlist {
X	name ml_hostname;
X	dirpath ml_directory;
X	mountlist *ml_next;
X};
X
X/*
X * A list of netgroups
X */
Xtypedef struct groupnode *groups;
Xstruct groupnode {
X	name gr_name;
X	groups *gr_next;
X};
X
X/*
X * A list of what is exported and to whom
X */
Xstruct exports {
X	dirpath ex_dir;
X	groups ex_groups;
X	exports *ex_next;
X};
X
Xprogram MOUNTPROG {
X	/*
X	 * Version one of the mount protocol communicates with version two
X	 * of the NFS protocol. The only connecting point is the fhandle 
X	 * structure, which is the same for both protocols.
X	 */
X	version MOUNTVERS {
X		/*
X		 * Does no work. It is made available in all RPC services
X		 * to allow server reponse testing and timing
X		 */
X		void
X		MOUNTPROC_NULL(void) = 0;
X
X		/*	
X		 * If fhs_status is 0, then fhs_fhandle contains the
X	 	 * file handle for the directory. This file handle may
X		 * be used in the NFS protocol. This procedure also adds
X		 * a new entry to the mount list for this client mounting
X		 * the directory.
X		 * Unix authentication required.
X		 */
X		fhstatus 
X		MOUNTPROC_MNT(dirpath) = 1;
X
X		/*
X		 * Returns the list of remotely mounted filesystems. The 
X		 * mountlist contains one entry for each hostname and 
X		 * directory pair.
X		 */
X		mountlist
X		MOUNTPROC_DUMP(void) = 2;
X
X		/*
X		 * Removes the mount list entry for the directory
X		 * Unix authentication required.
X		 */
X		void
X		MOUNTPROC_UMNT(dirpath) = 3;
X
X		/*
X		 * Removes all of the mount list entries for this client
X		 * Unix authentication required.
X		 */
X		void
X		MOUNTPROC_UMNTALL(void) = 4;
X
X		/*
X		 * Returns a list of all the exported filesystems, and which
X		 * machines are allowed to import it.
X		 */
X		exports
X		MOUNTPROC_EXPORT(void)  = 5;
X	
X		/*
X		 * Identical to MOUNTPROC_EXPORT above
X		 */
X		exports
X		MOUNTPROC_EXPORTALL(void) = 6;
X	} = 1;
X} = 100005;
________This_Is_The_END________
if test `wc -l < mount.x` -ne 161; then
	echo 'shar: mount.x was damaged during transit (should have been 161 bytes)'
fi
fi		; : end of overwriting check
exit 0