[7891] in Athena Bugs

home help back first fref pref prev next nref lref last post

rpc.mountd

daemon@ATHENA.MIT.EDU (John Carr)
Fri Aug 9 10:12:33 1991

To: bugs@ATHENA.MIT.EDU
Date: Fri, 09 Aug 91 10:04:30 EDT
From: John Carr <jfc@ATHENA.MIT.EDU>


These are some changes I've made to rpc.mountd and mkcred.  The
interesting changes:

	. Use syslog to report errors.
	. If BSD_NDBM is defined, notice immediately when the credentials
	  database is modified.

(The second change is useful because the next message I send to bugs
will be patches to nfsc to add a user to the credentials database
without running mkcred.)

Also included are AIX 1.2 support and code to log requests (enabled if
LOG_REQUESTS is defined).


*** ref/dbmcred.h	Tue Feb  2 15:18:14 1988
--- dbmcred.h	Mon Jul 29 16:14:39 1991
***************
*** 5,10 ****
--- 5,14 ----
   *	$Header: dbmcred.h,v 1.2 88/02/02 15:17:54 kubitron Exp $
   */
  
+ #ifdef _AIX
+ typedef int rpc_uid_t, rpc_gid_t;
+ #endif
+ 
  struct dbmcred {
  	rpc_uid_t	uid;
  	rpc_gid_t	gid;
*** ref/mkcred.c	Tue Jul 16 14:47:47 1991
--- mkcred.c	Fri Aug  9 09:29:48 1991
***************
*** 8,15 ****
--- 8,20 ----
  #include <stdio.h>
  #include <dbm.h>
  #include <sys/types.h>
+ #ifdef _AIX
+ #include <rpc/rpctypes.h>
+ #else
  #include <rpc/types.h>
+ #endif
  #include <sys/param.h>
+ #include "dbmcred.h"
  
  #define LINESIZE 4096
  
***************
*** 36,48 ****
  	exit(0);
  }
  
- struct ucred {
- 	rpc_uid_t	uid;
- 	rpc_gid_t	gid;
- 	int		glen;
- 	rpc_gid_t	gids[NGROUPS];
- };
- 
  open_cred(name)
  char *name;
  {
--- 41,46 ----
***************
*** 59,68 ****
  }
  
  add_cred(str)
! char *str;
  {
  	char *name;
! 	struct ucred u;
  	datum k, d;
  	int i;
  	
--- 57,66 ----
  }
  
  add_cred(str)
! register char *str;
  {
  	char *name;
! 	struct dbmcred u;
  	datum k, d;
  	int i;
  	
***************
*** 90,94 ****
  	k.dsize = strlen(name);
  	d.dptr = (char *) &u;
  	d.dsize = sizeof(u);
! 	store(k, d);
  }
--- 88,93 ----
  	k.dsize = strlen(name);
  	d.dptr = (char *) &u;
  	d.dsize = sizeof(u);
! 	if (store(k, d) < 0)
! 	  fprintf (stderr, "Can't store entry for user \"%s\".\n", name);
  }
*** ref/xdr_krb.c	Fri Aug  7 21:26:03 1987
--- xdr_krb.c	Mon Jul 29 16:21:08 1991
***************
*** 1,5 ****
--- 1,9 ----
  #include <krb.h>
+ #ifdef _AIX
+ #include <rpc/rpctypes.h>
+ #else
  #include <rpc/types.h>
+ #endif
  #include <rpc/rpc.h>
  
  bool_t xdr_krbtkt(xdrs, authp)
*** ref/rpc.mountd.c	Sun Jun 30 21:46:34 1991
--- rpc.mountd.c	Fri Aug  9 09:59:11 1991
***************
*** 23,30 ****
--- 23,36 ----
      
  /* NFS server */
  
+ #define	NFSSERVER
+ 
  #include <sys/param.h>
+ #ifdef _AIX
+ #include <sys/filsys.h>
+ #else
  #include <ufs/fs.h>
+ #endif
  #include <rpc/rpc.h>
  #include <sys/stat.h>
  #include <sys/socket.h>
***************
*** 36,49 ****
  #include <sys/wait.h>
  #include <sys/ioctl.h>
  #include <sys/errno.h>
  #include <nfs/nfs.h>
  #include <sys/vfs.h>			/* ... from kernel sources */
  #include <rpcsvc/mount.h>
  #include <netdb.h>
  #ifdef KERBEROS
  #define CREDFILE "/usr/etc/credentials"
! /* Extra time (in secs) beyond the authenticator to keep the mapping */
! /* We use 5 minutes so as the default in case this machine is a translator */
  #ifndef KRB_SLOP_TIME
  #define KRB_SLOP_TIME 300
  #endif
--- 42,67 ----
  #include <sys/wait.h>
  #include <sys/ioctl.h>
  #include <sys/errno.h>
+ #ifdef _AIX
+ #include <rpc/nfs.h>
+ #include <rpc/nfs_clnt.h>
+ #else
  #include <nfs/nfs.h>
+ #endif
  #include <sys/vfs.h>			/* ... from kernel sources */
+ #ifdef _AIX
+ #include <rpc/nfsmount.h>
+ #include "mount.h"
+ #else
  #include <rpcsvc/mount.h>
+ #endif
  #include <netdb.h>
  #ifdef KERBEROS
+ #ifdef _AIX
+ #define	CREDFILE "/local/athena/credentials/credentials"
+ #else
  #define CREDFILE "/usr/etc/credentials"
! #endif
  #ifndef KRB_SLOP_TIME
  #define KRB_SLOP_TIME 300
  #endif
***************
*** 57,62 ****
--- 75,83 ----
  #define RMTAB	"/etc/rmtab"
  #endif
  
+ #include <syslog.h>
+ #include <arpa/inet.h>
+ 
  extern int errno;
  
  int mnt();
***************
*** 80,89 ****
--- 101,121 ----
  
  #ifdef KERBEROS
  DBM *dbmfile;
+ #ifdef BSD_NDBM
+ long cred_ctime;
+ #else
  struct timeval timep;
  struct timeval savetime;
  #endif
+ #endif
+ #ifndef __STDC__
+ #define const
+ #endif
  
+ static const char *mountproc_names[] = 
+ { "null", "mount", "dump", "unmount", "unmountall", "export", "exportall",
+   "uid map", "uid unmap", "uid purge", "uid user-purge"};
+ 
  usage()
  {
      fprintf(stderr, "Usage: %s [-d] [-s]\n", prog);
***************
*** 97,103 ****
  	int len = sizeof(struct sockaddr_in);
  	int i,j;
  	SVCXPRT *transp;
! #ifdef KERBEROS
  	int sig_handler();
  	struct itimerval itimer;
  #endif
--- 129,135 ----
  	int len = sizeof(struct sockaddr_in);
  	int i,j;
  	SVCXPRT *transp;
! #if defined(KERBEROS) && !defined(BSD_NDBM)
  	int sig_handler();
  	struct itimerval itimer;
  #endif
***************
*** 126,131 ****
--- 158,165 ----
  	    exit(1);
  	}
  
+ 	openlog ("mountd", LOG_PID, LOG_DAEMON);
+ 	
  	if (set_exports_only) {
  	    set_exports();
  	    exit(exports ? 0 : 1);
***************
*** 159,173 ****
  	}
  
  	if (getsockname(0, &addr, &len) != 0) {
  		perror("rstat: getsockname");
  		exit(1);
  	}
  	if ((transp = svcudp_create(0)) == NULL) {
  		fprintf(stderr, "couldn't create udp transport\n");
  		exit(1);
  	}
  	if (!svc_register(transp, MOUNTPROG, MOUNTVERS, mnt, 0)) {
! 		fprintf(stderr, "couldn't register MOUNTPROG");
  		exit(1);
  	}
  
--- 193,212 ----
  	}
  
  	if (getsockname(0, &addr, &len) != 0) {
+ 		int oerr = errno;
  		perror("rstat: getsockname");
+ 		errno = oerr;
+ 		syslog (LOG_ERR, "getsockname: %m");
  		exit(1);
  	}
  	if ((transp = svcudp_create(0)) == NULL) {
  		fprintf(stderr, "couldn't create udp transport\n");
+ 		syslog (LOG_ERR, "couldn't create udp transport\n");
  		exit(1);
  	}
  	if (!svc_register(transp, MOUNTPROG, MOUNTVERS, mnt, 0)) {
! 		fprintf(stderr, "couldn't register MOUNTPROG\n");
! 		syslog(LOG_ERR, "couldn't register MOUNTPROG\n");
  		exit(1);
  	}
  
***************
*** 183,188 ****
--- 222,239 ----
  	set_exports();
  #ifdef KERBEROS
  	dbmfile = dbm_open(CREDFILE, DBM_INSERT, 0600);
+ #ifdef BSD_NDBM
+ 	if (dbmfile)
+ 	  {
+ 	    struct stat st;
+ 	    fstat(dbmfile->dbm_pagf, &st);
+ 	    cred_ctime = st.st_ctime;
+ #ifdef DEBUG
+ 	    fprintf (stderr,"Credentials file fd %d has mtime %u.\n",
+ 		     dbmfile->dbm_pags, cred_ctime);
+ #endif
+ 	  }
+ #else
  	(void) gettimeofday(&savetime, (struct timezone *)0);
  	(void) signal(SIGALRM, sig_handler);
  
***************
*** 191,199 ****
--- 242,254 ----
  	itimer.it_interval.tv_sec = 1800;
  	itimer.it_interval.tv_usec = 0;
  	if (setitimer(ITIMER_REAL, &itimer, (struct itimerval *)NULL)) {
+ 	    int oerr = errno;
  	    perror("rpc.mountd: setitimer");
+ 	    errno = oerr;
+ 	    syslog(LOG_WARNING, "setitimer: %m");
  	}
  #endif
+ #endif
  
  	/*
  	 * Start serving
***************
*** 200,205 ****
--- 255,261 ----
  	 */
  	svc_run();
  	fprintf(stderr, "Error: svc_run shouldn't have returned\n");
+ 	syslog(LOG_ERR, "Error: svc_run shouldn't have returned\n");
  	abort();
  }
  
***************
*** 210,216 ****
--- 266,294 ----
  	struct svc_req *rqstp;
  	SVCXPRT *transp;
  {
+ 	register int proc = rqstp->rq_proc;
+ 
  #ifdef KERBEROS
+ #ifdef BSD_NDBM
+ 	if (dbmfile)
+ 	  {
+ 	    struct stat st;
+ 	    fstat(dbmfile->dbm_pagf, &st);
+ 	    if (st.st_ctime != cred_ctime)
+ 	      {
+ 		dbm_close(dbmfile);
+ 		dbmfile = dbm_open(CREDFILE, DBM_INSERT, 0600);
+ 		fstat(dbmfile->dbm_pagf, &st);
+ 		cred_ctime = st.st_ctime;
+ 	      }
+ 	  }
+ 	else
+ 	  {
+ 	    struct stat st;
+ 	    if (stat(CREDFILE, &st) != -1)
+ 	      dbmfile = dbm_open(CREDFILE, DBM_INSERT, 0600);
+ 	  }
+ #else
  	gettimeofday(&timep, (struct timezone *)0);
  	if (timep.tv_sec - savetime.tv_sec > 60*5) {
  		if (debug)
***************
*** 219,228 ****
  		dbmfile = dbm_open(CREDFILE, DBM_INSERT, 0600);
  		savetime.tv_sec = timep.tv_sec;
  	}
! #endif KERBEROS
  	switch(rqstp->rq_proc) {
  		case NULLPROC:
  			if (!svc_sendreply(transp, xdr_void, 0)) {
  			    	fprintf(stderr,
  				     "couldn't reply to rpc call\n");
  				return;
--- 297,315 ----
  		dbmfile = dbm_open(CREDFILE, DBM_INSERT, 0600);
  		savetime.tv_sec = timep.tv_sec;
  	}
! #endif
! #endif
! #ifdef LOG_REQUESTS
! 	syslog(LOG_INFO, "request: procedure %x (%s) from host %s", proc,
! 			(proc > 10) ? "???" : mountproc_names[proc],
! 			inet_ntoa(svc_getcaller(transp)->sin_addr));
! #endif
  	switch(rqstp->rq_proc) {
  		case NULLPROC:
  			if (!svc_sendreply(transp, xdr_void, 0)) {
+ 				syslog(LOG_WARNING,
+ 					"couldn't reply to NULLPROC call");
+ 				if (debug)
  			    		fprintf(stderr,
  				     		"couldn't reply to rpc call\n");
  				return;
***************
*** 232,237 ****
--- 319,328 ----
  			if (debug)
  				fprintf(stdout, "about to do a mount\n");
  			if (imposter(rqstp,transp)) {
+ 				syslog (LOG_NOTICE, "bad mount request");
+ #ifdef DEBUG
+ 				fprintf (stderr, "bad mount request");
+ #endif
  				svcerr_weakauth(transp);
  				return;
  			}
***************
*** 242,247 ****
--- 333,340 ----
  			if (debug)
  				fprintf(stdout, "about to do a dump\n");
  			if (!svc_sendreply(transp,xdr_mountlist,&mountlist)) {
+ 				syslog(LOG_NOTICE, "dump failed");
+ 			    	if (debug)
  					fprintf(stderr,
  				    		"couldn't reply to rpc call\n");
  				return;
***************
*** 251,256 ****
--- 344,353 ----
  			if (debug)
  				fprintf(stdout, "about to do an unmount\n");
  			if (imposter(rqstp,transp)) {
+ #ifdef DEBUG
+ 				fprintf (stderr, "bad unmount request");
+ #endif
+ 				syslog (LOG_NOTICE, "bad unmount request");
  				svcerr_weakauth(transp);
  				return;
  			}
***************
*** 260,265 ****
--- 357,363 ----
  			if (debug)
  				fprintf(stdout, "about to do an unmountall\n");
  			if (imposter(rqstp,transp)) {
+ 				syslog (LOG_NOTICE, "bad unmountall request");
  				svcerr_weakauth(transp);
  				return;
  			}
***************
*** 318,323 ****
--- 416,425 ----
  	
  
  	if (rqstp->rq_cred.oa_flavor != AUTH_UNIX) {
+ 		syslog(LOG_WARNING, "bad auth type %d",rqstp->rq_cred.oa_flavor);
+ #ifdef DEBUG
+ 		fprintf (stderr, "bad auth type %d",rqstp->rq_cred.oa_flavor);
+ #endif
  		svcerr_weakauth(transp);
  		return(1);
  	}
***************
*** 324,329 ****
--- 426,437 ----
  	actual = *svc_getcaller(transp);
  	if (nfs_portmon) {
  		if (ntohs(actual.sin_port) >= IPPORT_RESERVED) {
+ #ifdef DEBUG
+ 			fprintf(stderr,"request from unprivileged port %s:%d\n",
+ 			inet_ntoa(actual.sin_addr), ntohs(actual.sin_port));
+ #endif
+ 			syslog(LOG_WARNING, "request from unprivileged port %s:%d\n",
+ 				inet_ntoa(actual.sin_addr), ntohs(actual.sin_port));
  			return(1);
  		}
  	}
***************
*** 337,347 ****
  			rqstp->rq_clntcred)->aup_machname;
  		hp = gethostbyname(machine);
  		if (hp == NULL) { 
  			svcerr_auth(transp,AUTH_BADCRED);
  			return(1);
  		}
  		bcopy(hp->h_addr,&claim,sizeof(struct in_addr));
! 		if (actual.sin_addr.s_addr != claim.s_addr) {;
  			svcerr_auth(transp,AUTH_REJECTEDVERF);
  			return(1);
  		}
--- 445,466 ----
  			rqstp->rq_clntcred)->aup_machname;
  		hp = gethostbyname(machine);
  		if (hp == NULL) { 
+ #ifdef DEBUG
+ 		fprintf(stderr, "unable to resolve hostname \"%s\"", machine);
+ #endif
+ 			syslog(LOG_WARNING, "unable to resolve hostname \"%s\"",
+ 				machine);
  			svcerr_auth(transp,AUTH_BADCRED);
  			return(1);
  		}
  		bcopy(hp->h_addr,&claim,sizeof(struct in_addr));
! 		if (actual.sin_addr.s_addr != claim.s_addr) {
! #ifdef DEBUG
! 			fprintf(stderr, "bad request: %s (%s) != %s",
! 			inet_ntoa(claim), machine, inet_ntoa(actual.sin_addr));
! #endif
! 			syslog(LOG_WARNING, "bad request: %s (%s) != %s",
! 			inet_ntoa(claim), machine, inet_ntoa(actual.sin_addr));
  			svcerr_auth(transp,AUTH_REJECTEDVERF);
  			return(1);
  		}
***************
*** 394,399 ****
--- 513,521 ----
  		goto fail;
  	}
  	close(fd);
+ #ifdef LOG_REQUESTS
+ 	syslog(LOG_INFO, "mount request for %s from %s", path, machine);
+ #endif
  	for(ex = exports; ex != NULL; ex = ex->ex_next) {
  		if (debug)
  			fprintf(stdout, "checking %s %o for %o\n", ex->ex_name, ex->ex_dev, statbuf.st_dev);
***************
*** 824,829 ****
--- 946,952 ----
  	fclose(fp);
  	exports = ex;
  
+ #ifndef _AIX
  	while (ex) {
  		if (debug)
  			printf("exportfs: fs=%s, rootid=%d, flags=%x\n",
***************
*** 832,838 ****
  		ex = ex->ex_next;
  	}
  	ex = exports;
! 	
  	return;
  }
  
--- 955,961 ----
  		ex = ex->ex_next;
  	}
  	ex = exports;
! #endif
  	return;
  }
  
***************
*** 840,850 ****
--- 963,975 ----
  	char *str;
  	struct exports *ex;
  {
+ #ifdef EX_FASCIST
  	if (!strcmp(str,"fascist") || !strcmp(str,"restricted")) {
  		ex->ex_flags |= EX_FASCIST;
  		fascist++;
  		return;
  	}
+ #endif
  	if (!strcmp(str,"ro")) {
  		ex->ex_flags |= EX_RDONLY;
  		return;
***************
*** 905,911 ****
    	register struct authunix_parms *credp = 
  		(struct authunix_parms *) rqstp->rq_clntcred;
  	struct authunix_parms ncred, *np = &ncred;
- 	struct passwd *pw, pwbuf;
  	static char thishost[255];
  	rpc_uid_t uid;
  	gid_t *initgroups();
--- 1030,1035 ----
***************
*** 915,921 ****
  	long time();
  
  	if (thishost[0] == '\0')
! 		gethostname(thishost, 255);
  	if (!svc_getargs(transp, xdr_krbtkt, &kauth)) {
  		svcerr_decode(transp);
  		return;
--- 1039,1045 ----
  	long time();
  
  	if (thishost[0] == '\0')
! 		gethostname(thishost, sizeof (thishost));
  	if (!svc_getargs(transp, xdr_krbtkt, &kauth)) {
  		svcerr_decode(transp);
  		return;
***************
*** 945,951 ****
  		if (d.dptr == NULL) {
  #ifdef OLDFASCIST
  			if (fascist) break;
! #endif OLDFASCIST
  			svc_freeargs(transp, xdr_krbtkt, &kauth);
  			svcerr_weakauth(transp);
  			return;
--- 1069,1078 ----
  		if (d.dptr == NULL) {
  #ifdef OLDFASCIST
  			if (fascist) break;
! #endif
! #ifdef DEBUG
! 			fprintf (stderr, "Couldn't find user \"%s\".\n", user);
! #endif
  			svc_freeargs(transp, xdr_krbtkt, &kauth);
  			svcerr_weakauth(transp);
  			return;
***************
*** 963,969 ****
  		np->aup_gid = up.gid;
  		np->aup_len = up.glen;
  		np->aup_gids = up.gids;
- 
  		if (debug) {
  		    fprintf(stdout,
  			    "Mapping user \"%s\", uid %d on %s to uid %d\n",
--- 1090,1095 ----
***************
*** 972,978 ****
  		    fflush(stdout);
  		    disp_cred(np);
  		}
! 
  		errno = 0;
  		if (nfsmapctl(NFSMC_SET, &client_addr,
  				sizeof(struct sockaddr_in), uid, np) < 0) {
--- 1098,1107 ----
  		    fflush(stdout);
  		    disp_cred(np);
  		}
! #ifdef LOG_REQUESTS
! 		syslog (LOG_INFO, "map %s:%d to local uid %d",
! 			inet_ntoa(client_addr.sin_addr), uid, up.uid);
! #endif
  		errno = 0;
  		if (nfsmapctl(NFSMC_SET, &client_addr,
  				sizeof(struct sockaddr_in), uid, np) < 0) {
***************
*** 983,988 ****
--- 1112,1121 ----
  
  	case KUID_DELETE:
  		uid = credp->aup_uid;
+ #ifdef LOG_REQUESTS
+ 		syslog (LOG_INFO, "unmap %s:%d",
+ 			inet_ntoa(client_addr.sin_addr), uid);
+ #endif
  		if (nfsmapctl (NFSMC_DELETE, &client_addr,
  			sizeof(struct sockaddr_in), uid, NULL) < 0) {
  			perror("nfs_mapctl(NFSMC_DELETE)");
***************
*** 992,997 ****
--- 1125,1134 ----
  
  	case KUID_PURGE:
  		uid = credp->aup_uid;
+ #ifdef LOG_REQUESTS
+ 		syslog (LOG_INFO, "purge %s:%d",
+ 			inet_ntoa(client_addr.sin_addr), uid);
+ #endif
  		if (nfsmapctl (NFSMC_FLUSH, &client_addr,
  			sizeof(struct sockaddr_in), uid, NULL) < 0) {
  			perror("nfs_mapctl(NFSMC_FLUSH)");
***************
*** 1021,1027 ****
  			uid, inet_ntoa(client_addr.sin_addr));
  		    fflush(stdout);
  		}
! 		
  		if (nfsmapctl(NFSMC_UFLUSH, &client_addr,
  				sizeof(struct sockaddr_in), uid, NULL) < 0) {
  			perror("nfs_mapctl(NFSMC_UFLUSH)");
--- 1158,1166 ----
  			uid, inet_ntoa(client_addr.sin_addr));
  		    fflush(stdout);
  		}
! #ifdef LOG_REQUESTS
! 		syslog (LOG_INFO, "user-purge uid %d", uid);
! #endif
  		if (nfsmapctl(NFSMC_UFLUSH, &client_addr,
  				sizeof(struct sockaddr_in), uid, NULL) < 0) {
  			perror("nfs_mapctl(NFSMC_UFLUSH)");
***************
*** 1080,1089 ****
  	}
  	if (nfsmapctl(NFSMC_EXPIRE,
  		      (struct sockaddr_in *)NULL, sizeof(struct sockaddr_in),
! 		      0, (struct authunix_parms *)NULL) < 0) {
! 	    perror("nfs_mapctl(NFSMC_EXPIRE)");
! 	    fprintf(stderr, "Errno = %d\n", errno);
! 	}
  
  	break;
  
--- 1219,1226 ----
  	}
  	if (nfsmapctl(NFSMC_EXPIRE,
  		      (struct sockaddr_in *)NULL, sizeof(struct sockaddr_in),
! 		      0, (struct authunix_parms *)NULL) < 0)
! 	    syslog (LOG_ERR, "nfs_mapctl(NFSMC_EXPIRE): %m");
  
  	break;
  
***************
*** 1091,1095 ****
--- 1228,1279 ----
  	break;
      }
      return;
+ }
+ #endif
+ 
+ #ifdef _AIX
+ 
+ #include <sys/dstat.h>
+ #include <sys/dustat.h>
+ 
+ /*
+  * Manufacture a file system handle
+  */
+ getfh(fd, filehp)
+ 	int fd;	
+ 	struct svcfh *filehp;
+ {
+ 	struct dstat dbuf;
+ 	struct dustat ubuf;
+ 	register int i;
+ 	char *point;
+ 
+ 	/* zero out the file handle */
+ 	point = (char *)filehp;
+ 	for (i=0; i<NFS_FHSIZE; i++)
+ 		point[i] = 0;
+ 
+ 	if (dfstat(fd, &dbuf, sizeof(dbuf)) < 0)
+ 		return -1;
+ 	else	{
+ 		filehp->fh_gfs = dbuf.dst_gfs;
+ 		filehp->fh_inode = dbuf.dst_ino;
+ 		filehp->fh_uniqid = dbuf.dst_uniqid;
+ 	/*	filehp->fh_root = TRUE; 	*/
+ 		if (site(0) == dbuf.dst_css)
+ 			filehp->fh_local = 1;
+ 		else {
+ 			ubuf.du_pckno = -1;
+ 			dustat(dbuf.dst_gfs, -1, &ubuf, sizeof(ubuf));
+ 			/* errno = ENOSTORE if this site
+ 			 * does not have a copy of the file system
+ 		         */
+ 			if ((!errno) && ((int)ubuf.du_flags & SB_REPLTYPE))
+ 				filehp->fh_local = 2;
+ 			else
+ 				filehp->fh_local = 3;
+ 		}
+ 	}
+ 	return 0;
  }
  #endif

home help back first fref pref prev next nref lref last post