[net.sources] Simple "remote file system" stuff for 4.2BSD

chris@umcp-cs.UUCP (Chris Torek) (06/09/85)

Here is a very simple set of remote file access routines which I'm
using for a very specific purpose (remote access to TeX fonts from
Suns) but which can be expanded easily to support more system calls.
The whole thing was written and debugged in under 8 hours (and it
probably shows) so it's not real production quality, but I figure
someone out there will probably find it useful.

Note that this is not a true remote file system in any real sense.  It
just happens to do what I want it to do...

Unfortunately, I use some code that is really Berkeley's, so you'll
have to do more work than just unbundling this script.  You also need
to copy /usr/src/lib/libc/vax/sys/SYS.h and make the changes given in
the diff listing, and you need to copy the following .c files from the
same directory, renaming them to start with a leading underscore and
a .s suffix instead of .c:

	access.c close.c dup.c dup2.c fstat.c lseek.c open.c read.c
	write.c

I have (by hand) made up a shell script which will hopefully do this
for you, but it's untested....  You can run setup.sh or do it yourself;
it's up to you.  In either case, you need to add a ``simple-rfs'' entry
in /etc/services.  (``Pick a port number, any port number.'')  I use:

	simple-rfs	1200/tcp	# simple remote file system stuff

On Suns, the same procedure suffices (but you need source, or careful
hand disassembly and recoding---which is what I used---to make the
system calls work).

The ``cat'' program (/usr/src/bin/cat.c) makes a reasonable test for
the rfs library.

The rest of this (up to the signature at the end) is the srfs source.

: Run this shell script with "sh" not "csh"
PATH=:/bin:/usr/bin:/usr/ucb
export PATH
all=FALSE
if [ $1x = -ax ]; then
	all=TRUE
fi
/bin/echo 'Extracting Makefile'
sed 's/^X//' <<'//go.sysin dd *' >Makefile
OBJS=	access.o close.o dup.o dup2.o\
	fstat.o lseek.o open.o read.o write.o\
	raccess.o rclose.o\
	rfstat.o rlseek.o ropen.o rread.o rwrite.o\
	rconnect.o rio.o\
	_access.o _close.o _dup.o _dup2.o\
	_fstat.o _lseek.o _open.o _read.o _write.o

CFLAGS=	-O -R

X.s.o:
	trap 'rm /tmp/cas$$$$' 0 1 2 3 15; /lib/cpp -E < $< > /tmp/cas$$$$; ${AS} -o $@ /tmp/cas$$$$
	-ld -x -r $*.o
	mv a.out $*.o

X.c.o:
	$(CC) $(CFLAGS) -c $<
	-ld -x -r $@
	mv a.out $@

all: rfsd librfs.a

librfs.a: $(OBJS)
	-mkdir libc
	cd libc; ar x /lib/libc.a
	rm -f librfs.a
	ar cr librfs.a libc/*.o
	ar r librfs.a $(OBJS)
	ranlib librfs.a
	rm -rf libc

rfsd: rfsd.c r.h
	$(CC) $(CFLAGS) -o rfsd rfsd.c

$(OBJS): r.h

install: rfsd librfs.a
	install -s rfsd /etc/rfsd
	install librfs.a /usr/local/lib/librfs.a
	ranlib /usr/local/lib/librfs.a

clean:
	rm -f *.o a.out core
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 Makefile
	/bin/echo -n '	'; /bin/ls -ld Makefile
fi
/bin/echo 'Extracting SYS.h.diff'
sed 's/^X//' <<'//go.sysin dd *' >SYS.h.diff
*** /usr/src/lib/libc/vax/sys/SYS.h	Thu Jun 30 19:11:37 1983
--- SYS.h	Sat Jun  8 23:36:00 1985
***************
*** 5,7
  #ifdef PROF
! #define	ENTRY(x)	.globl _/**/x; .align 2; _/**/x: .word 0; \
  			.data; 1:; .long 0; .text; moval 1b,r0; jsb mcount

--- 5,7 -----
  #ifdef PROF
! #define	ENTRY(x)	.globl __/**/x; .align 2; __/**/x: .word 0; \
  			.data; 1:; .long 0; .text; moval 1b,r0; jsb mcount
***************
*** 8,10
  #else
! #define	ENTRY(x)	.globl _/**/x; .align 2; _/**/x: .word 0
  #endif PROF

--- 8,10 -----
  #else
! #define	ENTRY(x)	.globl __/**/x; .align 2; __/**/x: .word 0
  #endif PROF
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 664 SYS.h.diff
	/bin/echo -n '	'; /bin/ls -ld SYS.h.diff
fi
/bin/echo 'Extracting access.c'
sed 's/^X//' <<'//go.sysin dd *' >access.c
#ifndef lint
static char rcsid[] = "$Header$";
#endif

#include "r.h"
#include <errno.h>

extern int errno;

X/*
 * access system call
 */
int
open (name, a)
char *name;
int a;
{
    int e;

    if (_access (name, a) == 0)	/* normal file */
	return 0;
    if (errno == ENOENT && *name == '/') {
	e = errno;
	if (_raccess (name, a) == 0)	/* rfs file */
	    return 0;
	errno = e;		/* return original error number */
    }
    return (-1);
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 access.c
	/bin/echo -n '	'; /bin/ls -ld access.c
fi
/bin/echo 'Extracting close.c'
sed 's/^X//' <<'//go.sysin dd *' >close.c
#ifndef lint
static char rcsid[] = "$Header$";
#endif

#include "r.h"

X/*
 * close system call
 */
int
close (fd)
int fd;
{
    int rv;

    if (_isremote (fd)) {
	rv = _rclose (fd);
	if (rv == 0)
	    _clrremote (fd);
    }
    else
	rv = _close (fd);
    return rv;
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 close.c
	/bin/echo -n '	'; /bin/ls -ld close.c
fi
/bin/echo 'Extracting dup.c'
sed 's/^X//' <<'//go.sysin dd *' >dup.c
#ifndef lint
static char rcsid[] = "$Header$";
#endif

#include "r.h"

X/*
 * dup system call
 */
int
dup (fd)
int fd;
{
    int rv;

    if ((rv = _dup (fd)) == 0)
	if (_isremote (fd))
	    _setremote (rv);
    return rv;
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 dup.c
	/bin/echo -n '	'; /bin/ls -ld dup.c
fi
/bin/echo 'Extracting dup2.c'
sed 's/^X//' <<'//go.sysin dd *' >dup2.c
#ifndef lint
static char rcsid[] = "$Header$";
#endif

#include "r.h"

X/*
 * dup2 system call
 */
int
dup2 (f1, f2)
int f1, f2;
{
    int rv, wasremote;

    wasremote = _isremote (f2);
    rv = _dup2 (f1, f2);
    if (rv == 0) {
	if (_isremote (f1))
	    _setremote (f2);
	else if (wasremote)
	    _clrremote (f2);
    }
    return rv;
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 dup2.c
	/bin/echo -n '	'; /bin/ls -ld dup2.c
fi
/bin/echo 'Extracting fstat.c'
sed 's/^X//' <<'//go.sysin dd *' >fstat.c
#ifndef lint
static char rcsid[] = "$Header$";
#endif

#include <sys/types.h>
#include <sys/stat.h>
#include "r.h"

X/*
 * fstat system call
 */
int
fstat (fd, statp)
int fd;
struct stat *statp;
{
    int rv;

    if (_isremote (fd))
	rv = _rfstat (fd, statp);
    else
	rv = _fstat (fd, statp);
    return rv;
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 fstat.c
	/bin/echo -n '	'; /bin/ls -ld fstat.c
fi
/bin/echo 'Extracting lseek.c'
sed 's/^X//' <<'//go.sysin dd *' >lseek.c
#ifndef lint
static char rcsid[] = "$Header$";
#endif

#include "r.h"

X/*
 * lseek system call
 */
long
lseek (fd, off, t)
int fd;
long off;
int t;
{
    int rv;

    if (_isremote (fd))
	rv = _rlseek (fd, off, t);
    else
	rv = _lseek (fd, off, t);
    return rv;
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 lseek.c
	/bin/echo -n '	'; /bin/ls -ld lseek.c
fi
/bin/echo 'Extracting open.c'
sed 's/^X//' <<'//go.sysin dd *' >open.c
#ifndef lint
static char rcsid[] = "$Header$";
#endif

#include "r.h"
#include <errno.h>

extern int errno;

X/*
 * open system call
 */
int
open (name, a, c)
char *name;
int a, c;
{
    register int rv;
    int e;

    if ((rv = _open (name, a, c)) >= 0)/* normal open */
	return rv;
    if (errno == ENOENT && *name == '/') {
	e = errno;
	if ((rv = _ropen (name, a, c)) >= 0) {/* rfs open */
	    _setremote (rv);
	    return rv;
	}
	errno = e;		/* return original error number */
    }
    return rv;
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 open.c
	/bin/echo -n '	'; /bin/ls -ld open.c
fi
/bin/echo 'Extracting r.h'
sed 's/^X//' <<'//go.sysin dd *' >r.h
X/*
 * simplerfs - simple remote file system stuff
 *
 * File descriptors are *not* passed across exec()s, etc.  This
 * is NOT a real remote file system!
 */

X/*
 * RFS requests are made as a ``stream'' using the following magic
 * codes.  Codes <= R_NOTOPEN are legal before a file is open.
 */
#define R_OPEN		1
#define R_ACCESS	2
#define R_CLOSE		3
#define R_READ		4
#define R_WRITE		5
#define R_SEEK		6
#define R_FSTAT		7

#define R_NOTOPEN	2

X/*
 * Arguments are either integers or character (byte) strings.
 * Integers are sent in ``network long'' order (BigEndian).
 * Strings are sent as counted byte thingamabobs; the count
 * is yet another ``network long''.
 *
 * To perform a request, one sends the R_ command, followed by
 * its arguments, then reads the return status (or in the case
 * of "read", the status followed by the bytes read).  E.g.:
 *
 *	putchar(R_OPEN); putnetlong(strlen("foo")); putstr("foo");
 *	putnetlong(0); putnetlong(0); rv = getnetlong();
 *
 * might open file "foo" for reading.
 *
 * The server switches uid's to ``guest'', thus can only access
 * publicly-accessible files.
 */

X/*
 * We need to keep track of which files are local, and which are
 * remote.  One way to do this is to simply use larger file descriptor
 * numbers than usual.  This may cause problems with program that
 * assume magic fd's (like 0, 1, 2), so I've elected to use flags
 * instead.  (N.B.: I've assumed at most 32 valid fd's, 0-31, and
 * 32 bit longs.)
 */
long _remotebits;

#define _isremote(fd)  (_remotebits &   (1L << (fd)))
#define _setremote(fd) (_remotebits |=  (1L << (fd)))
#define _clrremote(fd) (_remotebits &= ~(1L << (fd)))

X/*
 * _rresp() returns a response value from the server
 */
long _rresp ();

X/*
 * _rconnect() connects to the server, given a pathname, and returns
 * the modified pathname to be handed to the server.  It takes a
 * second argument (an int *) and fills that in with the server fd.
 * If the connection fails for some reason, _rconnect() returns NULL.
 */
char *_rconnect();

X/*
 * the ``stat'' format varies too much to use directly; we make up
 * one that has all "long"s.  (And we drop the spares.)
 */
struct rfsstat {
	long	st_dev;
	long	st_ino;
	long	st_mode;
	long	st_nlink;
	long	st_uid;
	long	st_gid;
	long	st_rdev;
	long	st_size;
	long	st_atime;
	long	st_mtime;
	long	st_ctime;
	long	st_blksize;
	long	st_blocks;
};
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 r.h
	/bin/echo -n '	'; /bin/ls -ld r.h
fi
/bin/echo 'Extracting raccess.c'
sed 's/^X//' <<'//go.sysin dd *' >raccess.c
#ifndef lint
static char rcsid[] = "$Header$";
#endif

#include "r.h"

X/*
 * remote access
 *
 * We pull off the leading part of the pathname and use that as
 * a machine name (/mimsy/foo/bar => mimsy, file /foo/bar)
 *
 * access.c has guaranteed that we get a name starting with /.
 */
int
_raccess (name, a)
register char *name;
int a;
{
    int s, rv;

    if ((name = _rconnect (name, &s)) != 0) {
    /* Send an open request, and get the return status (0=>success) */
	if (_wrequest (s, R_ACCESS) == 0 &&
		_wstring (s, name, strlen (name) + 1L) == 0 &&
		_wlong (s, (long) a) == 0)
	    rv = _rresp (s);
	else
	    rv = -1;
	(void) close (s);
    }
    return rv;
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 raccess.c
	/bin/echo -n '	'; /bin/ls -ld raccess.c
fi
/bin/echo 'Extracting rclose.c'
sed 's/^X//' <<'//go.sysin dd *' >rclose.c
#ifndef lint
static char rcsid[] = "$Header$";
#endif

#include "r.h"

#include <stdio.h>
X/*
 * remote close
 */
int
_rclose (fd)
int fd;
{
    (void) _wrequest (fd, R_CLOSE);
    (void) _close (fd);
    return (0);
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 rclose.c
	/bin/echo -n '	'; /bin/ls -ld rclose.c
fi
/bin/echo 'Extracting rconnect.c'
sed 's/^X//' <<'//go.sysin dd *' >rconnect.c
#ifndef lint
static char rcsid[] = "$Header$";
#endif

#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

X/*
 * remote connect
 *
 * We pull off the leading part of the pathname and use that as
 * a machine name (/mimsy/foo/bar => mimsy, file /foo/bar), connect to
 * it, fill in the supplied afd pointer, and return the tail of the
 * name (to be given to the other side).  If something goes wrong, we
 * return 0.
 */
char *
_rconnect (name, afd)
char *name;
int *afd;
{
    static int beenhere;	/* true => been thru here before */
    static int cando;		/* true => have a server addr */
    static struct sockaddr_in saddr;/* rfsd addr */

    if (!beenhere) {
	register struct servent *sp;

	beenhere++;
	if ((sp = getservbyname ("simple-rfs", 0)) != 0) {
	    saddr.sin_family = AF_INET;/* presumably! */
	    saddr.sin_port = sp->s_port;/* foreign port # */
	    cando++;
	}
    }
    if (cando) {
	register char *p = name;
	register char *cp;
	register int   l;
	register int   c;
	register struct hostent *hp;
	int s;
	long t;
	char hostname[40];

    /* Figure out the host name / address */
	while (*p == '/')
	    p++;
	cp = hostname;
	l = 0;
	while (*p && (c = *p++) != '/')
	    if (++l < sizeof hostname)
		*cp++ = c;
	*cp = 0;
	if ((hp = gethostbyname (hostname)) == 0)
	    return 0;
	bcopy (hp -> h_addr, &saddr.sin_addr, sizeof saddr.sin_addr);

    /* Get a tcp socket and connect to the RFS daemon */
	if ((s = socket (AF_INET, SOCK_STREAM, 0)) < 0)
	    return 0;
	if (connect (s, &saddr, sizeof saddr) >= 0) {
	    *afd = s;		/* Success! */
	    return p;
	}
	(void) close (s);
    }
    return 0;
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 rconnect.c
	/bin/echo -n '	'; /bin/ls -ld rconnect.c
fi
/bin/echo 'Extracting read.c'
sed 's/^X//' <<'//go.sysin dd *' >read.c
#ifndef lint
static char rcsid[] = "$Header$";
#endif

#include "r.h"

X/*
 * read system call
 */
int
read (fd, buf, n)
int fd;
char *buf;
int n;
{
    int rv;

    if (_isremote (fd))
	rv = _rread (fd, buf, n);
    else
	rv = _read (fd, buf, n);
    return rv;
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 read.c
	/bin/echo -n '	'; /bin/ls -ld read.c
fi
/bin/echo 'Extracting rfsd.c'
sed 's/^X//' <<'//go.sysin dd *' >rfsd.c
X/*
 * rfsd - simple RFS daemon
 *
 * N.B.: The other side sees a file system rooted at whatever the
 *	 current directory is when the daemon is started.  Thus,
 *	 the default startup should be (cd /; /etc/rfsd &) or similar.
 */

X/* #define TRACE */

char GUEST[]	= "guest";
char GUESTGRP[]	= "junk";

#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>
#include "r.h"

char  *ProgName;
extern int   errno;

X/*
 * error (from -lum library)
 *
 * Useful for printing error messages.  Will print the program name
 * and (optionally) the system error associated with the values in
 * <errno.h>.
 *
 * Note that the type (and even the existence!) of ``arg'' is undefined.
 */
error (quit, e, fmt, arg)
int quit;
register int e;
char *fmt;
{
    extern   char *sys_errlist[];
    extern   int   sys_nerr;
    register char *p = ProgName;

    if (p == NULL)
	p = "tomb of the unknown program";
    fprintf (stderr, "%s: ", p);
    _doprnt (fmt, &arg, stderr);	/* magic */
    if (e > 0) {
	p = e < sys_nerr ? sys_errlist[e] : "unknown error";
	fprintf (stderr, ": %s", p);
    }
    putc ('\n', stderr);
    fflush (stderr);
    if (quit)
	exit (quit);
}

X/*
 * globals
 */
int	servfd;			/* fd to service */

X/*
 **DANGER*****************
 * fuzzing long vs. int! *
 **DANGER*****************
 */
char   *string;
int	stringlen;

struct passwd *getpwnam ();
struct group  *getgrnam ();

reap () {
    union wait w;
    int pid;

    while ((pid = wait3 (&w, WNOHANG, (struct rusage *) 0)) > 0);
}

X/*
 * Try hard to fork.
 */
ffoorrkk () {
    register int p, n = 0;

    while ((p = fork ()) < 0) {
	if (errno == EAGAIN && ++n < 10) {
	    sleep (1);
	    continue;
	}
	return (-1);
    }
    return p;
}

X/*
 * Get a long
 */
long
getlong ()
{
    long l;

    if (read (servfd, &l, sizeof l) != sizeof l)
	error (1, errno, "getlong read");
    return ntohl (l);
}

X/*
 * Get a string, return the length.
 */
long
getstring ()
{
    long l;
    register int len;
    char *malloc ();

    if (read (servfd, &l, sizeof l) != sizeof l)
	error (1, errno, "getstring length read");
    l = ntohl (l);
    len = l;
    if (len > stringlen) {
	if (string)
	    free (string);
	if ((string = malloc (len)) == 0)
	    error (1, errno, "can't allocate %d bytes for string", len);
	stringlen = len;
    }
    if (read (servfd, string, len) != len)
	error (1, errno, "getstring string read");
    return l;
}

putlong (l)
long l;
{
    long l1 = htonl (l);

    if (write (servfd, &l1, sizeof l1) != sizeof l1)
	error (1, errno, "putlong write");
}

putstat (s)
register struct stat *s;
{
    struct rfsstat rst;

    rst.st_dev     = htonl ((long) s -> st_dev);
    rst.st_ino     = htonl ((long) s -> st_ino);
    rst.st_mode    = htonl ((long) s -> st_mode);
    rst.st_nlink   = htonl ((long) s -> st_nlink);
    rst.st_uid     = htonl ((long) s -> st_uid);
    rst.st_gid     = htonl ((long) s -> st_gid);
    rst.st_rdev    = htonl ((long) s -> st_rdev);
    rst.st_size    = htonl (s -> st_size);
    rst.st_atime   = htonl (s -> st_atime);
    rst.st_mtime   = htonl (s -> st_mtime);
    rst.st_ctime   = htonl (s -> st_ctime);
    rst.st_blksize = htonl (s -> st_blksize);
    rst.st_blocks  = htonl (s -> st_blocks);
    if (write (servfd, &rst, sizeof rst) != sizeof rst)
	error (1, errno, "fstat write");
}

X/*
 * Service the connection.  We had better get a code <= R_NOTOPEN first.
 * If it's R_OPEN, then we hang around, and we better not get any more
 * codes <= R_NOTOPEN.  We should get an R_CLOSE last.
 */
serve ()
{
    char ch;
    int isopen = 0;
    int a, c, localfd;
    long l;
    struct stat st;
    long lseek ();

    for (;;) {
	if (read (servfd, &ch, 1) != 1)
	    error (1, errno, "read");

#ifdef TRACE
	error (0, 0, "req. #%d", ch);
#endif

	if (ch > R_NOTOPEN) {
	    if (!isopen)
		error (1, 0, "request #%d (>R_NOTOPEN), no file open", ch);
	}
	else
	    if (isopen)
		error (1, 0, "request #%d (<=R_NOTOPEN), file open", ch);

	switch (ch) {
	    case R_OPEN:	/* open (str, int, int) */
#ifdef TRACE
		error (0, 0, "open");
#endif
		(void) getstring ();
		a = getlong ();
		c = getlong ();
#ifdef TRACE
		error (0, 0, "open(\"%s\",%d,%d)", string, a, c);
#endif
		localfd = open (string, a, c);
		if (localfd < 0) {
		    putlong (-1L);
		    (void) close (servfd);
		    exit (0);
		}
		putlong (0L);
		isopen++;
		break;

	    case R_ACCESS:	/* access (str, int) */
		(void) getstring ();
		a = getlong ();
		c = access (string, a);
#ifdef TRACE
		error (0, 0, "access(\"%s\",%d) => %d", string, a, c);
#endif
		putlong ((long) c);
		(void) close (servfd);
		exit (0);

#ifdef R_STAT
	    case R_STAT:	/* stat (str) */
		(void) getstring ();
		c = stat (string, &st);
#ifdef TRACE
		error (0, 0, "stat(\"%s\") => %d", string, c);
#endif
		putlong ((long) c);
		if (c == 0)
		    sendstats (&st);
		(void) close (servfd);
		exit (0);
#endif R_STAT

	    case R_CLOSE:	/* close () */
#ifdef TRACE
		error (0, 0, "close(%d)", localfd);
#endif
		(void) close (localfd);
		exit (0);

	    case R_READ:	/* read (int) */
#ifdef TRACE
		error (0, 0, "read");
#endif
		l = getlong ();
#ifdef TRACE
		error (0, 0, "read(%d,%ld)", localfd, l);
#endif
		if (l > stringlen) {
		    if (string)
			free (string);
		    string = malloc ((int) l);
		    if (string == 0)
			error (1, errno, "R_READ malloc %d failed", (int) l);
		    stringlen = l;
		}
		c = read (localfd, string, (int) l);
#ifdef TRACE
		error (0, 0, "read returned %d\n", c);
#endif
		putlong ((long) c);
		{
		    register char *p = string;
		    register int n;

		    while (c > 0) {
			if ((n = write (servfd, p, c)) < 0)
			    error (1, errno, "write");
			c -= n;
			p += n;
		    }
		}
		break;

	    case R_WRITE:	/* write (str) */
#ifdef TRACE
		error (0, 0, "write");
#endif
		l = getstring ();
#ifdef TRACE
		error (0, 0, "write(%d,,%ld)", localfd, l);
#endif
		putlong ((long) write (localfd, string, (int) l));
		break;

	    case R_SEEK:	/* lseek (int, int) */
#ifdef TRACE
		error (0, 0, "seek");
#endif
		l = getlong ();
		c = (int) getlong ();
#ifdef TRACE
		error (0, 0, "lseek(%d,%ld,%d)", localfd, l, c);
#endif
		putlong (lseek (localfd, l, c));
		break;

	    case R_FSTAT:	/* fstat */
#ifdef TRACE
		error (0, 0, "fstat(%d,)", localfd);
#endif
		(void) fstat (localfd, &st);
		putstat (&st);
		break;

	    default:
		error (1, 0, "unknown rfs req. #%d", ch);
	}
    }
}

X/*ARGSUSED*/
main (argc, argv)
int argc;
char **argv;
{
    int s, slen, pid, fd, guestuid;
    struct sockaddr_in saddr;
    struct servent *sp;
    struct passwd  *pw;
    struct group   *gr;

    ProgName = *argv;

    if ((pw = getpwnam (GUEST)) == 0)
	error (1, 0, "can't find user \"%s\"", GUEST);
    guestuid = pw -> pw_uid;
    if ((gr = getgrnam (GUESTGRP)) == 0)
	error (1, 0, "can't find group \"%s\"", GUESTGRP);
    (void) setgid (gr -> gr_gid);
    (void) setuid (guestuid);

 /* Find out who we are supposed to live as */
    if ((sp = getservbyname ("simple-rfs", 0)) == 0)
	error (1, errno, "can't find simple-rfs service info");
    saddr.sin_family = AF_INET;
    saddr.sin_port = sp -> s_port;

 /* Make the network connection */
    if ((s = socket (AF_INET, SOCK_STREAM, 0)) < 0)
	error (1, errno, "socket");

    if (bind (s, &saddr, sizeof saddr) < 0)
	error (1, errno, "bind");

    if (listen (s, 4) < 0)
	error (1, errno, "listen");

    signal (SIGCHLD, reap);
    slen = sizeof saddr;
    for (;;) {
	if ((fd = accept (s, &saddr, &slen)) < 0) {
	    if (errno == EINTR)
		continue;
	    break;
	}
	if ((pid = ffoorrkk ()) < 0) {
	    long l = -1;

	/* first thing should be an R_OPEN; we will answer it with "error" */
	    (void) write (fd, l, sizeof l);
	    (void) close (fd);
	    continue;
	}
	if (pid == 0) {
	    close (s);
	    servfd = fd;
	    serve ();
	}
	close (fd);
    }
    error (1, errno, "accept");
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 rfsd.c
	/bin/echo -n '	'; /bin/ls -ld rfsd.c
fi
/bin/echo 'Extracting rfstat.c'
sed 's/^X//' <<'//go.sysin dd *' >rfstat.c
#ifndef lint
static char rcsid[] = "$Header$";
#endif

#include <sys/types.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include "r.h"

X/*
 * remote fstat
 */
int
_rfstat (fd, s)
int fd;
register struct stat *s;
{
    struct rfsstat rst;

    if (_wrequest (fd, R_FSTAT))
	return (-1);
    if (_read (fd, &rst, sizeof rst) != sizeof rst)
	return (-1);
    s -> st_dev     = ntohl (rst.st_dev);
    s -> st_ino     = (u_long) ntohl (rst.st_ino);
    s -> st_mode    = (u_short) ntohl (rst.st_mode);
    s -> st_nlink   = ntohl (rst.st_nlink);
    s -> st_uid     = ntohl (rst.st_uid);
    s -> st_gid     = ntohl (rst.st_gid);
    s -> st_rdev    = ntohl (rst.st_rdev);
    s -> st_size    = ntohl (rst.st_size);
    s -> st_atime   = ntohl (rst.st_atime);
    s -> st_mtime   = ntohl (rst.st_mtime);
    s -> st_ctime   = ntohl (rst.st_ctime);
    s -> st_blksize = ntohl (rst.st_blksize);
    s -> st_blocks  = ntohl (rst.st_blocks);
    return 0;
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 rfstat.c
	/bin/echo -n '	'; /bin/ls -ld rfstat.c
fi
/bin/echo 'Extracting rio.c'
sed 's/^X//' <<'//go.sysin dd *' >rio.c
#ifndef lint
static char rcsid[] = "$Header$";
#endif

X/*
 * RFS I/O
 */

#include <sys/types.h>
#include <netinet/in.h>

X/*
 * _wrequest sends a (byte) request over the given fd.
 */
_wrequest (fd, req)
int fd, req;
{
    char c = req;

    if (_write (fd, &c, 1) != 1)
	return (-1);
    return 0;
}

X/*
 * _wstring sends a counted string
 */
_wstring (fd, s, len)
int fd;
char *s;
long len;
{
    long l;

    l = htonl (len);
    if (_write (fd, &l, sizeof l) != sizeof l)
	return (-1);
    if (_write (fd, s, len) != len)
	return (-1);
    return 0;
}

X/*
 * _wlong sends a long
 */
_wlong (fd, l)
int fd;
long l;
{
    long l1 = ntohl (l);

    if (_write (fd, &l1, sizeof l1) != sizeof l1)
	return (-1);
    return 0;
}

X/*
 * _rresp reads a long and returns it
 */
long
_rresp (fd)
int fd;
{
    long l;

    if (_read (fd, &l, sizeof l) != sizeof l)
	return (-1);	/* generic error stuff */
    l = ntohl (l);
    return l;
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 rio.c
	/bin/echo -n '	'; /bin/ls -ld rio.c
fi
/bin/echo 'Extracting rlseek.c'
sed 's/^X//' <<'//go.sysin dd *' >rlseek.c
#ifndef lint
static char rcsid[] = "$Header$";
#endif

#include "r.h"

X/*
 * remote lseek
 */
int
_rlseek (fd, off, t)
int fd;
long off;
int t;
{
    if (_wrequest (fd, R_SEEK) || _wlong (fd, off) || _wlong (fd, (long) t))
	return (-1);
    return _rresp (fd);
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 rlseek.c
	/bin/echo -n '	'; /bin/ls -ld rlseek.c
fi
/bin/echo 'Extracting ropen.c'
sed 's/^X//' <<'//go.sysin dd *' >ropen.c
#ifndef lint
static char rcsid[] = "$Header$";
#endif

#include "r.h"

X/*
 * remote open
 *
 * We pull off the leading part of the pathname and use that as
 * a machine name (/mimsy/foo/bar => mimsy, file /foo/bar)
 *
 * open.c has guaranteed that we get a name starting with /.
 */
int
_ropen (name, a, c)
register char *name;
int a, c;
{
    int s;

    if ((name = _rconnect (name, &s)) != 0) {
    /* Send an open request, and get the return status (0=>success) */
	if (_wrequest (s, R_OPEN) == 0 &&
		_wstring (s, name, strlen (name) + 1L) == 0 &&
		_wlong (s, (long) a) == 0 &&
		_wlong (s, (long) c) == 0 &&
		_rresp (s) == 0)
	    return s;			/* Success! */

	(void) close (s);		/* something went wrong */
    }
    return (-1);
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 ropen.c
	/bin/echo -n '	'; /bin/ls -ld ropen.c
fi
/bin/echo 'Extracting rread.c'
sed 's/^X//' <<'//go.sysin dd *' >rread.c
#ifndef lint
static char rcsid[] = "$Header$";
#endif

#include "r.h"

X/*
 * remote read
 *
 * This guy is a bit different from all the rest, since we have more
 * than just a ``long'' for return info.  We work this way: get the
 * return value, then read that many bytes into the caller's buffer.
 */
int
_rread (fd, buf, n)
int fd;
register char *buf;
int n;
{
    register int left;
    register int nn;
    int t;

    if (_wrequest (fd, R_READ) || _wlong (fd, (long) n))
	return (-1);
    t = left = _rresp (fd);
    while (left > 0) {
	if ((nn = _read (fd, buf, left)) <= 0)
	    return (-1);
	buf += nn;
	left -= nn;
    }
    return t;
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 rread.c
	/bin/echo -n '	'; /bin/ls -ld rread.c
fi
/bin/echo 'Extracting rwrite.c'
sed 's/^X//' <<'//go.sysin dd *' >rwrite.c
#ifndef lint
static char rcsid[] = "$Header$";
#endif

#include "r.h"

X/*
 * remote write
 */
int
_rwrite (fd, buf, n)
int fd;
char *buf;
int n;
{
    if (_wrequest (fd, R_WRITE) || _wstring (fd, buf, (long) n))
	return (-1);
    return _rresp (fd);
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 rwrite.c
	/bin/echo -n '	'; /bin/ls -ld rwrite.c
fi
/bin/echo 'Extracting write.c'
sed 's/^X//' <<'//go.sysin dd *' >write.c
#ifndef lint
static char rcsid[] = "$Header$";
#endif

#include "r.h"

X/*
 * write system call
 */
int
write (fd, buf, n)
int fd;
char *buf;
int n;
{
    int rv;

    if (_isremote (fd))
	rv = _rwrite (fd, buf, n);
    else
	rv = _write (fd, buf, n);
    return rv;
}
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 write.c
	/bin/echo -n '	'; /bin/ls -ld write.c
fi
/bin/echo 'Extracting setup.sh'
sed 's/^X//' <<'//go.sysin dd *' >setup.sh
#! /bin/sh
#
# setup.sh - set up the RFS directory by copying and editing the
# various BSD sources needed

src=/usr/src/lib/libc/vax/sys
routines=access close dup dup2 fstat lseek open read write

cp $src/SYS.h SYS.h
ed - SYS.h << 'eof'
g/ENTRY/s/_/__/g
w
q
eof

for i in $routines; do
	cp $src/$i.c _$i.s
done

echo done.
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 755 setup.sh
	/bin/echo -n '	'; /bin/ls -ld setup.sh
fi
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 4251)
UUCP:	seismo!umcp-cs!chris
CSNet:	chris@umcp-cs		ARPA:	chris@maryland