[net.sources] RFS: release #2, shar 2 of 7

toddb@tekcrl.UUCP (Todd Brunhoff) (03/11/86)

#!/bin/sh
#
# RFS, a kernel-resident remote file system.  Shar 2 of 7
#
#
# This is a shell archive, meaning:
# 1. Remove everything above the #!/bin/sh line.
# 2. Save the resulting text in a file.
# 3. Execute the file with /bin/sh (not csh) to create the files:
#	remote/doc/paper/appendixB
#	remote/doc/paper/fig1
#	remote/doc/paper/fig1.mag
#	remote/doc/paper/fig4
#	remote/doc/paper/fig5
#	remote/doc/paper/fig6
#	remote/doc/paper/remotefs
#	remote/doc/remotename.2
#	remote/doc/remoteon.2
#	remote/doc/rfs_server.8
#	remote/doc/rmtmnt.8
#	remote/file.c
#	remote/fileserver.c
#	remote/find.c
#
# remote/doc/paper/appendixB
#
if [ -f remote/doc/paper/appendixB ]; then 
	echo -n 'Hit <return> to overwrite remote/doc/paper/appendixB or ^C to quit' 
	read ans 
	rm -f remote/doc/paper/appendixB 
fi 
 
sed -e 's/^.//' << \SHAREOF > remote/doc/paper/appendixB
X.if "\*(.T"mag" \{\
X.	nr PS 12
X.	nr VS 14
X.	ps \n(PS
X.	vs \n(VS
X.	pl 9.5i
X.\}
X.ds LH \fBAppendix B\fP
X.BP
X.NH 1
XAppendix B
X.PP
XThese two tables contain the functions that must be ``changed'' for a remote
Xor distributed file system to work.
XThe column headings have the same meaning for both tables,
Xbut since some system calls have no before and after necessities (column 4),
Xit is clearer to put them by themselves in \fITable 1\fP.
X\fITable 2\fP has the rest of the system calls.
XThe first column, \fBSystem Call\fP, is the name of the system call
Xas found in section 2 of the UNIX User's Manual.
X.PP
XThe second column,
X\fBInteresting Arguments\fP,
Xlists the arguments that we are particularly interested in.
XFor example, \fIaccess()\fP, has two arguments, one a flag and one is
Xa path.
XThe path is noteworthy because we must find out what portion of the path
Xis on a remote host, and what remote host it is on.
XOther interesting arguments are those with two path names and those
Xhaving file descriptors.
XSome may not have any arguments worth considering,
Xbut are included because of important side effects.
X.PP
X\fBFollow Symlinks\fP shows whether \fInamei()\fP must follow
Xsymbolic links when determining ``remoteness'' (and is not applicable
Xto system calls that do not deal with path names).
XThis is an important point,
Xbut only for an implementation like \fBRemotefs\fP.
X\fBRemotefs\fP uses another system call, \fIisremote()\fP,
Xwhen determining what remote host a file is on and what portion
Xof the path is on that host.
XNow, the authors of 4.2
Xdecided when \fInamei()\fP may or may not follow a symbolic link,
Xand \fIisremote()\fP must do the same for whatever system call is
Xin operation.
X.PP
XThe last column, \fBSpecial Considerations Before & After the Syscall\fP,
Xshows what preparation
Xthe local host must do before
Xsending the system call to a remote host,
Xsuch as doing a local open to allocate a file descriptor
Xfor a \fIdup()\fP system call.
XThe fourth column shows the followup steps that should be taken
Xafter a successful remote system call.
X.PP
XThese descriptions assume that the system call has already been identified
Xas one that needs to be sent to a remote host.
XThat is,
Xat least one of the path names (for those system calls that deal with paths)
Xis remote,
Xor that the file descriptor passed as an argument to a system call is
Xto a remote file,
Xor even that there has been one or more remote system call requests
Xof some kind
X(\fIfork()\fP, \fIvfork()\fP, \fIumask()\fP, \fIexit()\fP).
X.BP
X.TS
Xtab(+) center box;
Xc | c | c
Xc | c | c
Xl | l | c.
X\fBSystem+Interesting+Follow\fR
X\fBCall+Arguments+Symlinks\fR
X=
X\fIaccess()\fP+One Path Name+yes+
X\fIstat()\fP+\^+\^
X\fIutimes()\fP+\^+\^
X\fItruncate()\fP+\^+\^
X_
X\fIchmod()\fP+One Path Name+no
X\fIchown()\fP+\^+\^
X\fIlstat()\fP+\^+\^
X\fImkdir()\fP+\^+\^
X\fImknod()\fP+\^+\^
X\fIrmdir()\fP+\^+\^
X\fIunlink()\fP+\^+\^
X_
X\fIfchmod()\fP+File Descriptor+n/a+
X\fIfchown()\fP+\^+\^
X\fIfcntl()\fP+\^+\^
X\fIflock()\fP+\^+\^
X\fIfstat()\fP+\^+\^
X\fIfsync()\fP+\^+\^
X\fIftruncate()\fP+\^+\^
X\fIioctl()\fP+\^+\^
X\fIlseek()\fP+\^+\^
X_
X.TE
X.ce 1
X\fITable 1\fP
X.BP
X.TS
Xtab(+) center box expand;
Xc | c | c | cw(3.5i)
Xc | c | c | cw(3.5i)
Xl | l | c | lw(3.5i).
X\fBSystem+Interesting+Follow+Special Considerations\fR
X\fBCall+Arguments+Symlinks+Before & After the Syscall\fR
X=
X\fIchdir()\fP+One Path Name+yes+T{
X\fBAfter:\fP
XMake note of the system and path name of the \fIchdir()\fP argument.
XT}
X_
X\fIclose()\fP+File Descriptor+n/a+T{
X\fBAfter:\fP
XClose the local file descriptor
XT}
X_
X\fIcreat()\fP+One Path Name+yes+T{
X\fBBefore:\fP
XAllocate a local file descriptor as a place\-holder.
X\fIDup2()\fP always closes the file descriptor in
Xits second argument in anticipation
Xof putting the new file descriptor at that ordinate value.
XThat file descriptor must be closed locally before sending
Xthe request to the remote host.
X.sp 1
X\fBAfter:\fP
XIf the system call was not successful, close the local one.
XAlso,
Xthere may need to be some mapping of file descriptors.
XFor instance,
Xthe local file descriptor may be 5 while the remote may be 6.
XHence, every request on fd 5 must be mapped to 6.
XAlternatively,
Xthe server may take care of the mapping if the local host sends
Xthe local file descriptor number to the remote host.
XT}
X\fIopen()\fP+One Path Name+\^+\^
X\fIdup()\fP+File Descriptor+\^+\^
X\fIdup2()\fP+File Descriptor+\^+\^
X_
X\fIexecv()\fP+One Path Name+yes+T{
X\fBAfter:\fP
XThe text for the program to be run must be copied
Xto the local swap space and executed from there.
XIf it is just a shell file, then it could be run normally,
Xwith the shell causing a remote open.
XT}
X\fIexecve()\fP+\^+\^+\^
X_
X\fIexit()\fP+None+n/a+T{
X\fBAfter:\fP
XThe \fIexit()\fP must be also run locally.
XT}
X_
X\fIfork()\fP+None+n/a+T{
X\fBBefore:\fP
XThe \fIfork()\fP or \fIvfork()\fP should be run locally first
Xto determine if the resources are available.
XT}
X\fIvfork()\fP+\^+\^+\^
X_
X\fIlink()\fP+Two Path Names+no+T{
X\fBBefore:\fP
XBoth path names must be on the same remote (or local) host.
XIf not, we can simulate failure locally.
XT}
X_
X\fIread()\fP+File Descriptor++T{
X\fBAfter:\fP
XThe data that was actually read by the system call
Xmust be gotten from the remote host.
XT}
X\fIreadlink()\fP+One Path Name+no+\^
X\fIreadv()\fP+File Descriptor++\^
X_
X\fIrename()\fP+Two Path Names+yes+T{
X\fBBefore:\fP
XThe two path names must both be on the same remote (or local)
Xhost.
XT}
X_
X\fIsymlink()\fP+Two Path Names+yes+T{
X\fBBefore:\fP
XOnly the second path name should be checked for ``remoteness''.
XT}
X_
X\fIumask()\fP+None+n/a+T{
X\fBAfter:\fP
X\fIumask()\fP must also be run locally.
XT}
X_
X\fIwrite()\fP+File Descriptor+n/a+T{
X\fBBefore:\fP
XThe data to be written must be sent to the remote host.
XT}
X\fIwritev()\fP+\^+\^+\^
X.TE
X.ce 1
X\fITable 2\fP
SHAREOF
chmod 664 remote/doc/paper/appendixB
#
# remote/doc/paper/fig1
#
if [ -f remote/doc/paper/fig1 ]; then 
	echo -n 'Hit <return> to overwrite remote/doc/paper/fig1 or ^C to quit' 
	read ans 
	rm -f remote/doc/paper/fig1 
fi 
 
sed -e 's/^.//' << \SHAREOF > remote/doc/paper/fig1
X.KF
X.if "\*(.T"mag" \{\
X.	nr PS 12
X.	nr VS 14
X.	ps \n(PS
X.	vs \n(VS
X.\}
X.PP
X.PS
X#ifdef figure1
Xboxht = .3i
Xboxwid = .8i
Xmovewid = .2i
XSyscalls: [
X	A: box "read()"; move
X	B: box "open()"; move
X	C: box "stat()"; move
X	D: box "..."
X]
X
Xboxht = last [].ht+1.2i
Xboxwid = last [].wid+.1i
XUserlevel: box dashed with .n at last [].n + (0i, .3i)
Xline from Syscalls.A.s to Userlevel.s
Xline from Syscalls.B.s to Userlevel.s
Xline from Syscalls.C.s to Userlevel.s
Xline from Syscalls.D.s to Userlevel.s
X"\s+4A User's Program\s-4" at Userlevel.se + (0, .075i) rjust
X#endif
X
X#ifdef figure1
Xboxwid = boxwid + 1.5i
Xboxht = boxht + 1i
XSystem: box dashed with .n at last box.s - (0, .3i)
X
X#else
Xboxwid = 5.5i
Xboxht = 2i
XSystem: box dashed
X#endif
X"\s+4UNIX Kernel\s-4" at System.se + (0, .075i) rjust
X
X#ifdef figure1
Xarrow from Userlevel.s to System.n
X#else
Xarrow from System.n + (0, .3i) to System.n
X#endif
Xboxht = .3i
Xboxwid = 1.5i
XSysinterface: box "Syscall Interface" dashed .02i with .n at System.n
X
Xboxht = .3i
Xboxwid = .8i
Xmovewid = .2i
XRwuio: box "rwuio()" with .w at System.w + (.1i, 0); move
XCopen: box "copen()"
X
Xmovewid = (-.2i)
XEtc: box "..." with .e at System.e - (.1i, 0)
XStat1: box "stat1()" with .e at Etc.w - (.2i, 0)
X
Xarrow from Sysinterface.s to Copen.n
X#ifdef figure1
Xarrow from Sysinterface.s to Rwuio.n
Xarrow from Sysinterface.s to Etc.n
Xarrow from Sysinterface.s to Stat1.n
X#endif
X
X#ifdef figure1
XNamei: box "namei()" at System.c - (0, .4i)
X#endif
X#ifdef figure2
XNamei: box "namei()" at System.c - (0, .4i)
X#endif
X#ifdef figure3
XNamei: box wid 1.8*boxwid ht 2.5*boxht at System.c - (0, .5i)
X"namei()" at Namei above
XRemotecheck: box wid 1.15*boxwid ht 1.3*boxht with .se at Namei.se
X"check for" at Remotecheck above
X"``remoteness''" at Remotecheck below
X#endif
X
Xspline -> right .4i from Copen.e then to Namei.n - (.1i, 0)
X#ifdef figure1
Xspline -> left .4i from Stat1.w then to Namei.n + (.1i, 0)
Xspline -> down .2i from Etc.s + (.025i, 0) then to Namei.e
X#endif
Xboxht = 1i
Xboxwid = 3i
X
XDevices: box dashed with .n at System.s - (0, .3i)
X"\s+4Device Drivers\s-4" at Devices.se + (0, .075i) rjust
Xboxht = .5i
Xboxwid = 1i
X
X#ifdef figure1
XDisk: box dashed .02i with .n at Devices.n
X#endif
X
X#ifdef figure2
XDisk: box dashed .02i with .ne at Devices.ne - (.2i, 0)
XPseudo: box dashed .02i with .nw at Devices.nw + (.2i, 0)
X"Pseudo\-Disk" at Pseudo above
X"Interface" at Pseudo below
X#endif
X
X#ifdef figure3
XDisk: box dashed .02i with .ne at Devices.ne - (.2i, 0)
XNet: box dashed .02i with .nw at Devices.nw + (.2i, 0)
X"Network" at Net above
X"Connection" at Net below
X#endif
X
X"Disk" at Disk above
X"Interface" at Disk below
X
X#ifdef figure1
Xarrow from Rwuio.s to Disk.n - (.025, 0)
Xarrow from Namei.s to Disk.n + (.025, 0)
X#endif
X#ifdef figure2
Xarrow from Rwuio.s to Pseudo.n - (.025, 0)
Xarrow from Namei.s to Pseudo.n + (.025, 0)
X#endif
X#ifdef figure3
Xarrow from Rwuio.s to Net.n - (.033, 0)
Xarrow from Copen.s to Net.n
Xarrow from Namei.s to Disk.n + (.025, 0)
X#endif
X.PE
X.ce
X#ifdef figure1
X\fIFigure 1\fP
X#endif
X#ifdef figure2
X\fIFigure 2\fP
X#endif
X#ifdef figure3
X\fIFigure 3\fP
X#endif
X.SP
X.KE
SHAREOF
chmod 664 remote/doc/paper/fig1
#
# remote/doc/paper/fig1.mag
#
if [ -f remote/doc/paper/fig1.mag ]; then 
	echo -n 'Hit <return> to overwrite remote/doc/paper/fig1.mag or ^C to quit' 
	read ans 
	rm -f remote/doc/paper/fig1.mag 
fi 
 
sed -e 's/^.//' << \SHAREOF > remote/doc/paper/fig1.mag
SHAREOF
chmod 644 remote/doc/paper/fig1.mag
#
# remote/doc/paper/fig4
#
if [ -f remote/doc/paper/fig4 ]; then 
	echo -n 'Hit <return> to overwrite remote/doc/paper/fig4 or ^C to quit' 
	read ans 
	rm -f remote/doc/paper/fig4 
fi 
 
sed -e 's/^.//' << \SHAREOF > remote/doc/paper/fig4
X.KF
X.if "\*(.T"mag" \{\
X.	nr PS 12
X.	nr VS 14
X.	ps \n(PS
X.	vs \n(VS
X.\}
X.PP
X.PS
Xboxht = .3i
Xboxwid = .8i
Xmovewid = .2i
XSyscalls: [
X	A: box "read()"; move
X	B: box "open()"; move
X	C: box "stat()"; move
X	D: box "..."
X]
Xboxwid = last [].wid
XRemotecheck: box with .nw at Syscalls.A.sw - (0, .15i)
X"check for ``remoteness''" at Remotecheck
X
Xboxht = last [].ht+1.2i
Xboxwid = last [].wid+.1i
XUserlevel: box dashed with .n at last [].n + (0i, .3i)
Xarrow from Syscalls.A.s to (Syscalls.A.s.x, Remotecheck.n.y)
Xarrow from Syscalls.B.s to (Syscalls.B.s.x, Remotecheck.n.y)
Xarrow from Syscalls.C.s to (Syscalls.C.s.x, Remotecheck.n.y)
Xarrow from Syscalls.D.s to (Syscalls.D.s.x, Remotecheck.n.y)
Xarrow from Remotecheck.s to Userlevel.s - (0, .3i)
X"\s+4A User's Program\s-4" at Userlevel.se + (0, .075i) rjust
X.PE
X.ce
X\fIFigure 4\fP
X.SP
X.KE
SHAREOF
chmod 664 remote/doc/paper/fig4
#
# remote/doc/paper/fig5
#
if [ -f remote/doc/paper/fig5 ]; then 
	echo -n 'Hit <return> to overwrite remote/doc/paper/fig5 or ^C to quit' 
	read ans 
	rm -f remote/doc/paper/fig5 
fi 
 
sed -e 's/^.//' << \SHAREOF > remote/doc/paper/fig5
X.DS
X.if "\*(.T"mag" \{\
X.	nr PS 12
X.	nr VS 14
X.	ps \n(PS
X.	vs \n(VS
X.\}
X.PS
Xboxht = boxht/2
Xboxwid = boxwid*2
XProto: [
X	A: box "request"
X	B: box "response" with .n at last box.s - (0, .1)
X	C: box "request" with .n at last box.s - (0, .1)
X	D: box "response" with .n at last box.s - (0, .1)
X	E: box "..." with .n at last box.s - (0, .1)
X]
XLocal: box "Local Host" with .e at last [].w - (1i, 0)
XRemote: box "Remote Host" with .w at last [].e + (1i, 0)
Xarrow from Local.e to Proto.A.w
Xarrow from Local.e to Proto.C.w
Xarrow from Local.e to Proto.E.w
Xarrow from Remote.w to Proto.B.e
Xarrow from Remote.w to Proto.D.e
X.PE
X.DE
SHAREOF
chmod 664 remote/doc/paper/fig5
#
# remote/doc/paper/fig6
#
if [ -f remote/doc/paper/fig6 ]; then 
	echo -n 'Hit <return> to overwrite remote/doc/paper/fig6 or ^C to quit' 
	read ans 
	rm -f remote/doc/paper/fig6 
fi 
 
sed -e 's/^.//' << \SHAREOF > remote/doc/paper/fig6
X.DS
X.if "\*(.T"mag" \{\
X.	nr PS 12
X.	nr VS 14
X.	ps \n(PS
X.	vs \n(VS
X.\}
X.PS
Xboxht = boxht/2
Xboxwid = boxwid*2
XProto: [
X	A: box "read request"
X	B: box "read response" with .n at last box.s - (0, .1)
X	C: box "read request" with .n at last box.s - (0, .1)
X	D: box "read response" with .n at last box.s - (0, .1)
X	E: box "continue" with .n at last box.s - (0, .1)
X	F: box "read response" with .n at last box.s - (0, .1)
X	G: box "read response" with .n at last box.s - (0, .1)
X	H: box "..." invis with .n at last box.s - (0, .1)
X	I: box "stop \fIn\fP" with .n at last box.s - (0, .1)
X	J: box "read response" with .n at last box.s - (0, .1)
X	K: box "read response" with .n at last box.s - (0, .1)
X	L: box "..." invis with .n at last box.s - (0, .1)
X	M: box "acknowledge" with .n at last box.s - (0, .1)
X]
XLocal: box "Local Host" with .e at last [].w - (1i, 0)
XRemote: box "Remote Host" with .w at last [].e + (1i, 0)
Xarrow from Local.e to Proto.A.w
Xarrow from Local.e to Proto.C.w
Xarrow from Local.e to Proto.E.w
Xarrow from Local.e to Proto.I.w
Xarrow from Remote.w to Proto.B.e
Xarrow from Remote.w to Proto.D.e
Xarrow from Remote.w to Proto.F.e
Xarrow from Remote.w to Proto.G.e
Xarrow from Remote.w to Proto.J.e
Xarrow from Remote.w to Proto.K.e
Xarrow from Remote.w to Proto.M.e
X.PE
X.DE
SHAREOF
chmod 664 remote/doc/paper/fig6
#
# remote/doc/paper/remotefs
#
if [ -f remote/doc/paper/remotefs ]; then 
	echo -n 'Hit <return> to overwrite remote/doc/paper/remotefs or ^C to quit' 
	read ans 
	rm -f remote/doc/paper/remotefs 
fi 
 
sed -e 's/^.//' << \SHAREOF > remote/doc/paper/remotefs
X.HS I
X.if "\*(.T"mag" \{\
X.	nr PS 12
X.	nr VS 14
X.\}
X.pl 9.75i
X.TR
X.DR
X.de CN
X..
X.TL
XDesign Considerations for Remote File Systems
X(Extended Abstract)
X.AU
XT. Brunhoff
X.AI
XComputer Environments Group
XApplied Research Group
XTektronix, inc.
X.AB
XThere have been several remote file systems written,
Xincluding one written by the author called \fBRemotefs\fR.
XThis paper covers the design choices
Xthat can be made at several software levels,
Xfrom where the hooks for a remote file system
Xlie in the operating system,
Xon up to the user interface,
Xand reveals those made by \fBRemotefs\fP.
XThe reader should have a strong familiarity
Xwith the 4.2 BSD kernel function \fInamei()\fP,
Xthe concept of mount points,
Xthe system call interface and
Xthe 4.2 BSD socket paradigm.
X.AE
X.SH
XHistory
X.PP
XThe Computer Research Labs within the Applied Research Group
Xhas approximately forty-five internally\-designed workstations,
Xcalled
X.I Magnolias,
Xtwenty newly announced Tektronix 4404 AI workstations,
Xcalled \fIPegasus\fP, a
X.I VAX 11/780
Xand a
X.I VAX 11/750.
XThe Computer Environments Group,
Xwithin the Computer Research Labs
Xcares for most of these machines and the software that runs on them.
X.PP
XAfter porting 4.2 BSD Unix to the
X.I Magnolia,
Xthe amount of software available quickly outgrew
Xthe capacity of its 35-megabyte winchester drives.
XTo alleviate this,
Xthe author designed and began in December of 1984 to write a
Xremote file system based on a implementation paradigm used
Xby K. McKusick in his implementation of the 4.2 BSD file system;
Xi.e., "write it in user-mode to fit in the kernel".
XThis paper is in part
Xabout that implementation,
Xand about design and
Ximplementation in general to achieve a remote (or distributed) file system.
XAt this writing,
Xthe design still lies mostly in the user level,
Xlinked in by the \fBld(1)\fP flag
X\fI\-lremote\fP,
Xwith a few new system calls.\(dg
X.FS \(dg
XThis remote file system,
Xknown simply as \fBRemotefs\fP,
Xshould not be confused with another,
Xmore complete remote file system,
Xcalled \fBDFS\fR.
XThe latter is available on the Tektronix 6000 series workstations.
X.FE
X.NH 1
XChoosing a Springboard for the Software
X.PP
XThe focus of I/O activity on 
X.B UNIX
Xis the inode;
Xeach time a file is open, read, written, locked, closed, etc.,
Xthe inode is referred to.
XThese system calls
Xconverge on the system call interface which dispatches calls to
Xthe appropriate internal routine.
XAny system calls that involve a path name must call \fInamei()\fP
Xfor the inode information, and similarly, any system calls that
Xdeal with file descriptors must refer to the inode information
Xgenerated by an \fIopen()\fP or \fIcreate()\fR.
XOnly then can the data on the disks be accessed.
X.so fig1.\*(.T
X.PP
XFor example, in Figure 1,
X\fIopen()\fP makes a request to the system call interface;
Xthe system call interface determines that the \fIopen()\fP system call must
Xbe executed (the kernel \fIopen()\fP is just a call to \fIcopen()\fP).
X\fICopen()\fP then calls \fInamei()\fP to get the inode information
Xwhich in turn calls the appropriate disk device driver to get the inode
Xfrom the correct disk.
XSubsequent \fIread()\fP
Xor \fIwrite()\fP calls use this information to access the disk.
XIt makes sense to make \fInamei()\fP the focal point for the remote
Xfile system implementation
Xbecause of its critical role.
XBut there are other approaches.
X.NH 2
X\&\.\.\. From the Device Driver
X.PP
XSince \fInamei()\fP gets its information from the
Xdisk via the disk driver,
Xwe have only to
Xreplace the disk driver with a \fIremote\fP disk driver.
XThis remote disk driver would be designed to send requests for disk
Xblocks directly to a remote host to be satisfied
Xfrom a single partition on its own disk.
X.so fig2.\*(.T
X.PP
XNow, following the previous example in Figure 2,
X\fInamei()\fP may instead encounter a mount point while
Xtrying to find an inode for a file,
Xand will get its inode information from the remote disk driver.
XSimilarly,
Xreads and writes
Xrequest blocks from the \fIremote\fP disk driver
Xusing this inode information.
X.PP
XThis is where early implementations put remote file systems.
XIt offers speed and a good deal of portability with
Xthe kernel changes limited to the device driver,
Xbut it limits usefulness because each partition
Xon every remote system must be mounted,
Xand access can only be read\-only.
X.NH 2
X\&\.\.\. In \fInamei()\fP
X.PP
XThere are two ways of checking for ``remoteness''
Xin \fInamei()\fP,
Xbut the key change to \fInamei()\fP is that it must fail in
Xits inode lookup when it encounters a path name component
Xon a remote machine; then it must return with a special error.
XThis \fInamei()\fP failure mechanism will be alluded to later.
X.PP
XOne method, depicted in Figure 3,
Xis to catch any reference to a special
Xsyntax of path name,
Xsuch as \fI/\.\.\|/host/pathname\fP,
X\fI/net/host/pathname\fP
Xor \fI//host/pathname\fP.
XThis is a cue to \fInamei()\fP
Xto return a special error code to the invoking system call.
XIt is then
Xthe responsibility of that system call to send
Xa request to a server on the remote host.
XThis special syntax is very convenient because the
X\fIhost\fP component of the path
Xneed not correspond to some existing ``mount point''.
XHence, hosts can be mounted and unmounted on demand
Xif the implementor cares to take the trouble.
X.so fig3.\*(.T
X.PP
XA second strategy is very similar
Xexcept that it uses a more natural
Xsyntax of \fI/host/pathname\fP
X(without needing symbolic links).
XAn important point is that the host cannot be ``mounted''
Xon a directory,
Xbut rather on a special mount point,
Xor even a plain file.
XThe reason for this is a bit obscure,
Xbut will be clarified shortly.
X.PP
XThe special path names like \fI/\.\.\|/host/pathname\fP
Xor mount points like \fI/host\fP are needed partly because no
XUNIX program should ever find these gateways through normal perusal of
Xa file system.
XImagine how long the command ``\fIfind / -print\fP'' would take
Xif it traversed every remote host as well as itself!
XFor this reason, using a directory for a mount point would
Xnot be appropriate.
X.PP
X\fBRemotefs\fP uses a plain file as a mount point because of some extra
Xbenefits:
Xthe simplicity of the code changes to \fInamei()\fP,
Xand not having to add another file type for \fBUNIX\fP utilities to learn.
XThe path name \fI/host\fP remains a valid local filename,
Xbut \fI/host/\fP or anything longer results in
Xa special case, which \fInamei\fP labels with the error
X\fBENOTDIR\fP
X(See Appendix A).
XIt is this place in the \fBUNIX\fP kernel
Xthat \fBRemotefs\fP detects all remote file references.
X.NH 3
XAn Aside: When to Follow a Symbolic Link in \fINamei()\fP.
X.NH 2
X\&\.\.\. At the User Level.
X.PP
XA slight variation of the above,
Xshown in Figure 4,
Xis to simply place the check for ``remoteness'' in
Xappropriate system calls with in the C runtime library,
X\fIlibc.a\fP.
X(see Appendix B for this list).
XUnfortunately,
Xthis requires the user level software to duplicate
Xwhat \fInamei()\fP does
Xwhenever a system call involving a path name returns the
Xerror \fBENOENT\fR or \fBENOTDIR\fR.
XThis implementation approach is typically slower,
Xbut very portable.
X.so fig4.\*(.T
X.NH 1
XFile Descriptors
X.PP
XOnce an \fIopen()\fP or \fIcreat()\fP has succeeded
Xon the remote host and returned a file descriptor, say \fIi\fP,
Xwe must allocate a real file descriptor, \fIj\fP, on the local machine.
XThis may be done in the kernel or user level code,
Xbut it is most important that the user's idea of the ordinate
Xvalue of \fIj\fP remain inviolate.
X.NH 2
XFile Descriptors Handled at User Level
X.NH 2
XFile Descriptors Handled at Kernel Level
X.NH 2
XInheriting File Descriptors Across a \fIfork()\fP or \fIexec()\fP
X.NH 2
XReading Directories on a Remote Host
X.NH 1
XChanging Directories
X.PP
XImplementing the ability to change
Xdirectories is a big win for any implementation
Xbecause interactive shells will then allow you to
Xperuse directories on remote hosts.
XHowever, inheritance of file descriptors must
Xbe implemented,
Xas explained in section 2.3.
XThe \fIchdir()\fP executing on the remote host
Xdoes nothing special.
XIf it succeeds, all is well.
XBut on the local side,
Xthe software cannot change state (the current
Xworking directory)
Xto match what has occurred on the remote machine.
XInstead,
Xit must simply be remembered it in some way.
X.NH 2
XInterpreting Pathnames
X.PP
XIf the remote file system software lies entirely in user\-level code,
Xthen the only solution is for the software
Xto remember \fIchdir()\fP's path name argument
Xand that the current directory is on a remote host.
XThen when a new path name is passed to a system call,
Xthe software need only to check to see if it is absolute
Xor relative (with or without a leading '/' character, respectively).
XIf it is relative,
Xthen the request must be sent to the remote host.
X.PP
XOn the other hand if the software uses a special
Xmount point like \fI/host\fP,
Xthe kernel can arrange
Xto make the process's working directory inode
Xto be the mount point's inode.
XThis is very convenient because no absolute vs. relative
Xchecks are necessary
Xand nothing need be added to \fInamei()\fP.
XFor example,
Xabsolute path names in a system call will still cause the mechanism
Xto function normally.
XAnd relative path names will immediately fail in \fInamei()\fP
X(remember our key change in section 1.2).
XSee Appendix A.
X.NH 2
XPwd(1) and Changing to ``/\.\.'' on the Remote Host
X.NH 1
XSpecial Problems
X.NH 2
X\fIExec()\fP
X.NH 2
X\fIFork()\fP and \fIvfork()\fP
X.NH 2
X\fISelect()\fP
X.NH 2
XUniqueness of Files Across Hosts
X.NH 1
XPermissions Across Hosts
X.NH 2
XDatabase Model
X.NH 2
XDynamic Model
X.NH 1
XServer Design
X.NH 2
XWhen to \fIfork()\fP
X.NH 2
XHow to Change Uid/Gid permissions
X.NH 2
XFile Descriptor Overload
X.NH 2
XCommunication Model
X.NH 3
XProtocol
X.NH 3
XWho Answers the Phone?
X.NH 3
XSpeed Improvements
X.PP
XImagine
Xa very loose view of the protocol (moving downward):
X.so fig5.\*(.T
XA remote file system implementation
Xhas a decidedly synchronous flavor to it,
Xand for most system calls,
Xnothing else is appropriate.
XBut \fIRead()\fP and \fIwrite()\fP system calls
Xlend themselves very well to optimization,
Xspecifically, lookahead.
X.PP
XA change in the protocol could be made
Xbased on expected requests.
XAfter,
Xsay, two consecutive \fIread()\fP requests
Xon the same file descriptor for the same number of bytes,
Xthe local host could ask the server to continue servicing
Xthe same request until further notice.
XThe response would contain the same information
Xthat would be expected on a normal request,
Xand would, of course,
Xterminate on an error or end\-of\-file.
XThe remote host, could easily detect and recover from a termination
Xof this kind, too.
XThe difficult part would be for the local host to try to stop
Xthe servicing before end\-of\-file.
XSo,
Xour protocol now would be
X.so fig6.\*(.T
XNotice that the responses may continue on beyond
Xthe request to stop,
Xbut the acknowledgment of the request to stop would put
Xthe hosts back in sync.
XThe remote host has only to reset its read pointer
Xback to the point where it had serviced
X\fIn\fP requests,
Xand the local host must read the responses up to and including
Xthe acknowledgment.
X.PP
XThe protocol also may
Xhave to refuse continuation service for file descriptors
Xthat read from devices.
X.PP
X\fIWrite()\fP is similar,
Xbut recovery when the remote host
Xreaches an end\-of\-file or encounters an error
Xwould be much more complicated, and in some cases impossible.
XThe local host,
Xon receipt of a request to stop from the remote host,
Xwould have to not only reset its idea of the write pointer,
Xbut perhaps the read pointer from which the data was gathered
Xto do the write.
XConsidering that the reading may have been done from multiple
Xfiles or the data was transformed in some way,
Xthe remote file system software may not be able to accomplish the task.
XIt appears to be only feasible if the implementor is willing to sacrifice
Xidentical behavior of user\-level software on remote vs. local file
Xsystems.
X.NH 1
XStatus of \fBRemotefs\fP
X.NH 1
XAppendix A
X.so appendixB.out
X.NH 1
XAppendix C
SHAREOF
chmod 664 remote/doc/paper/remotefs
#
# remote/doc/remotename.2
#
if [ -f remote/doc/remotename.2 ]; then 
	echo -n 'Hit <return> to overwrite remote/doc/remotename.2 or ^C to quit' 
	read ans 
	rm -f remote/doc/remotename.2 
fi 
 
sed -e 's/^.//' << \SHAREOF > remote/doc/remotename.2
X.TH REMOTENAME 2 "27 July 1983"
X.UC 4
X.SH NAME
Xremotename \- provide name information to the kernel
X.SH SYNOPSIS
X.nf
X.ft B
X#include <remote/remotefs.h>
X
Xremotename(action, name, namelen, path)
Xlong action;
Xcaddr_t name;
Xlong namelen;
Xchar *path;
X.fi
X.SH DESCRIPTION
X.I Remotename
Xis an interface for an exchange of information with
Xthe kernel about remote hosts.
XThe value of
X.I action,
Xdefined by NM_* symbolic constants in remote/remotefs.h,
Xdetermines what exchange takes place:
X.PP
XNM_SERVER
X.br
XThe current process is registered as the name server for the kernel.
XWhenever the kernel needs a path name translated into
Xan internet address,
Xthe current process will receive the SIGIO signal.
XArguments besides
X.I action
Xare ignored.
XIf there is already a process registered as the name server,
X.I remotename
Xwill fail.
X.PP
XNM_WHATNAME
X.br
XAfter receiving the SIGIO signal,
Xthe registered name server should
Xsupply this action to
X.I remotename
Xalong with a valid
Xcharacter pointer
Xin
X.I name
Xand its length in
X.I namelen.
XThe kernel will copy into that pointer
Xa null\-terminated string of the form "/single-component".
XIt is the name server's job to translate "single-component"
Xinto a valid internet address.
X.PP
XThe kernel obtains the single component from the second component
Xof a path name used in a system call by some user process.
XThe first component of that path name would have been a generic mount
Xpoint.
X.PP
XNM_NAMEIS
X.br
XAfter obtaining a valid internet address,
Xthe registered name server should
Xsupply this action to 
X.I remotename
Xalong with a valid
X.I name
Xand
X.I namelen
Xcontaining the internet address the way that
X.I connect(2)
Xwould expect it,
Xand a
X.I path
Xcontaining the nameserver's opion of what the null-terminated
Xmount point should
Xhave been.
X.PP
XNM_DEBUG
X.br
XTurns on debug level to the value in
X.I system.
X.SH "RETURN VALUE
X.I Remotename
Xreturns 0 if the action occurred, \-1 if
X.I name
Xor
X.I path
Xis an invalid address (when a valid one was expected),
Xor the user is not the super user.
X.SH ERRORS
X.TP 15
X[EPERM]
XThe caller is not the super-user
Xor the calling process is not the registered nameserver.
X.TP 15
X[EINVAL]
X.I Name
Xor
X.I path
Xare not valid addresses (if expected),
X.I namelen
Xis too long or
Xthe
X.I action
Xwas not recognized.
X.TP 15
X[ENOREMOTEFS]
XOn
X.I NM_WHATNAME
Xthere was no pathname for which the kernel needed a internet address.
X.TP 15
X[EBUSY]
XThe calling process is trying to register as the nameserver,
Xbut one already exists.
X.SH "SEE ALSO"
Xremoteon(2), remoteoff(2), rmtmnt(8), rfs_server(8)
X.SH BUGS
X.I NM_DEBUG
Xwill not be recognized unless the kernel is has the debug software
Xcompiled in.
SHAREOF
chmod 664 remote/doc/remotename.2
#
# remote/doc/remoteon.2
#
if [ -f remote/doc/remoteon.2 ]; then 
	echo -n 'Hit <return> to overwrite remote/doc/remoteon.2 or ^C to quit' 
	read ans 
	rm -f remote/doc/remoteon.2 
fi 
 
sed -e 's/^.//' << \SHAREOF > remote/doc/remoteon.2
X.TH REMOTEON 2 "27 July 1983"
X.UC 4
X.SH NAME
Xremoteon, remoteoff \- turn on and off remote file system
X.SH SYNOPSIS
X.nf
X.ft B
Xremoteon(path, pathlen, name, namelen)
Xchar *path;
Xint pathlen;
Xstruct sockaddr *name;
Xint namelen;
X.PP
X.ft B
Xremoteoff(path)
Xchar *path;
X.fi
X.SH DESCRIPTION
X.I Remoteon
Xannounces to the system that the file system
Xstarting with the root, aka "/", on the internet host
X.I name
Xhas been mounted on
Xthe plain file
X.I path;
Xfrom then on, references to any files below
X.I path
Xwill refer to
Xfiles below the root file system on the remote host,
X.I name.
X.I Path
Xis a pointer to a null-terminated string
Xcontaining the appropriate path name,
Xbut for storage purposes in the kernel,
X.I pathlen
Xmust also be provided.
X.I name
Xcan only be a valid internet address (this may be extended later),
Xand
X.I namelen
Xshould be the correct length,
Xnormally
X.I sizeof(struct sockaddr_in).
X.PP
X.I Path
Xmust exist already and be
Xa plain file.
XIts old contents
Xare still accessible while the remote file system
Xis mounted,
Xbut the file cannot be removed.
X.PP
XA special case for
X.I remoteon
Xand
X.I remoteoff
Xis when
X.I path
Xis a null pointer.
XThis tells the kernel to disallow
X.I (remoteoff)
Xor allow
X.I (remoteon)
Xremote access for the current
Xprocess,
Xand is intended primarily for use with a remote file server
Xto prevent remote file system loops.
XBy default,
Xall processes are allowed remote access.
XNote that while only the super-user may turn on or off the remote
Xfile system,
Xany user may turn on and off remote access for himself.
X.PP
X.I Remoteoff
Xannounces to the system that the
X.I path
Xfile is no longer to be a remote mount point.
XCurrently,
Xeven if
X.I remoteoff
Xfails,
Xthe remote file system is marked for closing and
Xno more usage is allowed.
XSystem calls that must be run on the remote system after
Xthis point will fail (return \-1).
X.SH "RETURN VALUE
X.I Remoteon
Xreturns 0 if the action occurred, \-1 if
X.I path
Xis inaccessible,
Xalready a remote mount point, not an appropriate file, if
X.I path
Xdoes not exist,
Xor if there are already too many remote file systems mounted.
X.PP
X.I Remoteoff
Xreturns 0 if the action occurred; \-1 if
Xif the file is inaccessible or
Xdoes not point to a remote file system,
Xor if there are active processes using the remote 
Xfile system.
X.SH ERRORS
X.I Remoteon
Xwill fail when one of the following occurs:
X.TP 15
X[EPERM]
XThe caller is not the super-user.
X.TP 15
X[ENOENT]
X.I Special
Xdoes not exist.
X.TP 15
X[EISDIR]
X.I Path
Xis not a plain file.
X.TP 15
X[EINVAL]
X.I Namelen
Xis too long.
X.TP 15
X[ENOBUFS]
Xthe system is out of mbuf structures.
X.TP 15
X[EFAULT]
X.I Name
Xpoints to a bad address.
X.TP 15
X[ETOOMANYREMOTE]
XThe action would overflow internal tables.
X.TP 15
X[EBUSY]
X.I Path
Xis already a remote mount point.
X.PP
X.I Remoteoff
Xmay fail with one of the following errors:
X.TP 15
X[EPERM]
XThe caller is not the super-user.
X.TP 15
X[ENOENT]
X.I Path
Xdoes not exist.
X.TP 15
X[EBUSY]
XA process is holding a reference to the remote file system.
X.SH "SEE ALSO"
Xrmtmnt(8), rfs_server(8)
SHAREOF
chmod 664 remote/doc/remoteon.2
#
# remote/doc/rfs_server.8
#
if [ -f remote/doc/rfs_server.8 ]; then 
	echo -n 'Hit <return> to overwrite remote/doc/rfs_server.8 or ^C to quit' 
	read ans 
	rm -f remote/doc/rfs_server.8 
fi 
 
sed -e 's/^.//' << \SHAREOF > remote/doc/rfs_server.8
X.TH RFS_SERVER 8  "18 October 1985"
X.UC 4
X.SH NAME
Xrfs_server \- remote file system server and kernel name server
X.SH SYNOPSIS
X.B /etc/rfs_server
X[ -s internet-service ]
X[ -v debug-level ]
X.SH DESCRIPTION
XThis is a server for the remote file system and
Xis intended to be started up in /etc/rc.
X.PP
X.PP
XThe optional flag
X.I \-s
Xand its argument,
X.I internet-service,
Xtells the server to accept calls on the named service
Xport described in /etc/services.
XWithout this argument, 
X.I rfs_server
Xuses the service
X.I remotefs.
X.PP
XThe optional flag
X.I \-v
Xand its argument,
X.I debug-level,
Xstarts up the server with the given initial debug level.
XThe argument should be a hexadecimal number containing one bit
Xfor each class of debug output desired.
X.PP
XThe server maintains three identities,
Xand each can be determined by the current command line using the
X.I ps(1)
Xcommand.
X.PP
XThe first identity is the
X.I sentry
Xof which there can only be one at any time.
XThe command line for this remains unaltered from the way it was started.
XThe function of the
X.I sentry
Xserver is to build a database of all hosts in /etc/hosts,
Xall users and groups in /etc/passwd and /etc/group,
Xand of all users' .rhost files.
XAfter this database has been built,
Xit waits 
Xfor connections from remote hosts.
X.PP
XThe second identity is a
X.I "gateway server"
Xand changes its command line
Xto tell which host it is a
X.I "gateway server"
Xfor.
XThis identity is the child of the
X.I sentry
Xafter the latter receives a connection from a remote host;
Xthere can only be one
X.I "gateway server"
Xfor each remote host.
XThe responsibilities of the
X.I "gateway server"
Xare to service context-free system calls for the remote host,
Xcreate other servers to handle context-sensitive system calls,
Xmaintain complete information about all remote processes being served,
Xand ensure at all times
Xthat only one server has control of the socket file
Xdescriptor to the remote machine being served.
X.PP
XThe third identity is that of a plain
X.I server,
Xand changes its command line to be similar to that
Xof the
X.I "gateway server"
Xexcept that the word
X.I gateway
Xis missing and it identifies its parent process.
XIts responsibilities
Xare to service context-free system calls for the remote host and
Xcreate other servers for remote processes that must inherit
Xcertain context information (e.g. remote current working directories,
Xand open file descriptors).
X.SH "DEBUG LEVELS"
XIt should be noted that the debug option is only useful with
Xa copy of the source code in hand and a server that has been
Xcompiled with the debug software turned on.
XThe current selection of the bits are:
X.PP
X.ta 1i 2.5i 4i
X	0x00000001	process switching
X.br
X	0x00000002	system calls
X.br
X	0x00000004	setuid/setgid, umask
X.br
X	0x00000008	file descriptor allocation
X.br
X	0x00000010	connections
X.br
X	0x00000020	server switching
X.br
X	0x00000040	nameserver
X.br
X	0x00000080	directory nuxi
X.br
X	0x00000100	message in and out
X.br
X	0x00000200	don't fork child for gateway (good for adb)
X.br
X	0x00000400	local/remote file decisions
X.br
X	0x00000800	don't remove log file on exit (except exit on error)
X.br
X	0x00001000	exec information
X.br
X	0x00002000	auto debug for 0x20 (server switching)
X.br
X	0x00004000	parsing messages to gateway
X.PP
XAnother method of setting the debug level in any identity of the
X.I rfs_server
Xis to place the desired hexidecimal level in the file /usr/tmp/rfs_debug
Xand to send the appropiate server signal number 5, aka
X.I SIGTRAP.
XThis method can be used with
X.I sentry
Xservers, any
X.I gateway
Xservers, and any other servers that are marked active.
XIt should not be used on sleeping servers.
X.PP
XIf any server receives SIGILL, SIGSEGV or SIGBUS,
Xit will arrange for its core file to be dumped in /usr/tmp.
X.SH "RESTARTING THE SERVER"
XThe best way to restart the
X.I sentry
Xserver (e.g. installing a new
X.I rfs_server),
Xis to simply run the new server.
XThe new server knows how to examine the previous log file
Xto discover the pid number of the previous server, and kill it.
X.PP
XIf you know that that will fail, then the second best way
Xis to send it the signal
X.I SIGTERM
X.PP
XSending
X.I SIGTERM
Xsignal to a
X.I "gateway server"
Xwill cause it and all of its children to gracefully go away.
X.SH "SEE ALSO"
Xremotename(2),
Xrmtmnt(8).
X.SH BUGS
XProbably.
SHAREOF
chmod 664 remote/doc/rfs_server.8
#
# remote/doc/rmtmnt.8
#
if [ -f remote/doc/rmtmnt.8 ]; then 
	echo -n 'Hit <return> to overwrite remote/doc/rmtmnt.8 or ^C to quit' 
	read ans 
	rm -f remote/doc/rmtmnt.8 
fi 
 
sed -e 's/^.//' << \SHAREOF > remote/doc/rmtmnt.8
X.TH RMTMNT 8  "18 October 1985"
X.UC 4
X.SH NAME
Xrmtmnt \- mount and dismount remote file systems
X.SH SYNOPSIS
X.B /etc/rmtmnt
X[ -s internet-service ] host path
X.PP
X.B /etc/rmtmnt
X-g path
X.PP
X.B /etc/rmtmnt
X-u path
X.PP
X.B /etc/rmtmnt
X.SH DESCRIPTION
XIn the first usage,
X.I rmtmnt
Xannounces to the system that the file system
Xstarting with the root, aka "/", on the internet host named
X.I host
Xhas been mounted on
Xthe plain file
X.I path;
Xfrom now on, references to any files below
X.I path
Xwill refer to
Xfiles below the root file system on the remote host,
X.I host.
XThe file
X.I path
Xmust exist already; it must be a file.
XIt becomes the name of the newly mounted root.
X.PP
XThe optional flag
X.I \-s
Xand argument
X.I internet-service
Xindicates that the kernel should use
Xthe named internet service (defined in /etc/services)
Xwhen connecting to that remote host.
X.PP
XThe second usage with the
X.I -g
Xflag indicates that the mount point is to be "generic".
XBy convention,
Xthe name of this path ought to be
X.I /net,
Xbut may be any valid path name.
XNo specific host is associated with the
Xpath name.
XInstead,
Xthe component following
X.I /net
Xis handed to the nameserver, usually
X.I rfs_server(8),
Xfor translation to an internet address.
XWhen the kernel receives the new address,
Xan implicit mount is made by the kernel.
X.PP
XThe third usage with the
X.I -u
Xflag informs the kernel that the internet host
Xmounted on
X.I path
Xshould be unmounted.
X.PP
XThe last usage,
Xhaving no arguments or flags
Xwill print out a complete status of all current mount points.
X.SH "SEE ALSO"
Xremoteon(2),
Xrfs_server(8).
X.SH BUGS
XImlicit mount points cannot be unmounted.
X.PP
XIf a command which has a current working directory
Xon a remote machine through an
X.I implicit
Xmount point
Xattempts to find the current working directory,
Xit will produce a pathname missing the second component,
Xand, hence, will fail.
XExplicit mount points work fine.
SHAREOF
chmod 664 remote/doc/rmtmnt.8
#
# remote/file.c
#
if [ -f remote/file.c ]; then 
	echo -n 'Hit <return> to overwrite remote/file.c or ^C to quit' 
	read ans 
	rm -f remote/file.c 
fi 
 
sed -e 's/^.//' << \SHAREOF > remote/file.c
X/*
X * Copyright 1985, Todd Brunhoff.
X *
X * This software was written at Tektronix Computer Research Laboratories
X * as partial fulfillment of a Master's degree at the University of Denver.
X * This is not Tektronix proprietary software and should not be
X * confused with any software product sold by Tektronix.  No warranty is
X * expressed or implied on the reliability of this software; the author,
X * the University of Denver, and Tektronix, inc. accept no liability for
X * any damage done directly or indirectly by this software.  This software
X * may be copied, modified or used in any way, without fee, provided this
X * notice remains an unaltered part of the software.
X *
X * $Log:	file.c,v $
X * Revision 2.0  85/12/07  18:21:11  toddb
X * First public release.
X * 
X */
Xstatic char	*rcsid = "$Header: file.c,v 2.0 85/12/07 18:21:11 toddb Rel $";
X#include	"server.h"
X#include	<errno.h>
X
Xextern int	fds_in_use;
Xextern int	errno;
X
X/*
X * here we allocate a file descriptor to a process and make note of our
X * total number of open file descriptors.  'fd' is the file descriptor
X * that the server itself got back, and remotefd is the file descriptor
X * that the remote process is expecting.
X */
Xallocate_fd(fd, proc, remotefd)
X	register int	fd,
X			remotefd;
X	register process	*proc;
X{
X	if (fd != -1)
X	{
X		proc->p_fds[ remotefd ] = fd;
X		debug3("allocate local fd %d, remote fd %d\n",
X			fd, remotefd);
X		fds_in_use++;
X		checkfiletype(fd);
X	}
X	else
X		remotefd = -1;
X	return(remotefd);
X}
X
Xdeallocate_fd(proc, remotefd)
X	register process	*proc;
X	register long	remotefd;
X{
X	register char	fd;
X	register long	retval;
X
X	if ((unsigned)remotefd >= NOFILE)
X	{
X		errno = EBADF;
X		return(-1);
X	}
X	fd = proc->p_fds[ remotefd ];
X	proc->p_fds[ remotefd ] = -1;
X	retval = close(fd);
X	fds_in_use--;
X	return(retval);
X}
SHAREOF
chmod 444 remote/file.c
#
# remote/fileserver.c
#
if [ -f remote/fileserver.c ]; then 
	echo -n 'Hit <return> to overwrite remote/fileserver.c or ^C to quit' 
	read ans 
	rm -f remote/fileserver.c 
fi 
 
sed -e 's/^.//' << \SHAREOF > remote/fileserver.c
X/*
X * Copyright 1985, Todd Brunhoff.
X *
X * This software was written at Tektronix Computer Research Laboratories
X * as partial fulfillment of a Master's degree at the University of Denver.
X * This is not Tektronix proprietary software and should not be
X * confused with any software product sold by Tektronix.  No warranty is
X * expressed or implied on the reliability of this software; the author,
X * the University of Denver, and Tektronix, inc. accept no liability for
X * any damage done directly or indirectly by this software.  This software
X * may be copied, modified or used in any way, without fee, provided this
X * notice remains an unaltered part of the software.
X *
X * $Log:	fileserver.c,v $
X * Revision 2.2  86/03/01  22:51:52  toddb
X * Put the newly added remoteoff(NULL) lines between #ifdef CANREMOTE lines.
X * 
X * Revision 2.1  86/02/17  15:11:03  toddb
X * The SNOREMOTE bit is cleared in the children of the server, so a call
X * to remoteoff(NULL) was added to server() and to become_server().
X * Joe Othmer (vax135!jo).
X * 
X * Revision 2.0  85/12/07  18:21:14  toddb
X * First public release.
X * 
X */
Xstatic char	*rcsid = "$Header: fileserver.c,v 2.2 86/03/01 22:51:52 toddb Exp $";
X#include <errno.h>
X#include <stdio.h>
X#include <ctype.h>
X#include <signal.h>
X#include "server.h"
X#include <sys/file.h>
X
Xextern long	errno;
Xextern long	from_servers;
Xextern long	to_gateway;
Xextern long	so_listen;
Xextern long	blocking_servers;
Xextern short	gateway_server;
Xextern short	current_ppid;
Xextern short	current_pid;
Xextern short	current_uid;
Xextern short	current_server;
Xextern char	mntpt[ MAXPATHLEN ];
Xextern char	*program;
Xextern char	*last_argaddr;
Xextern char	*logfile;
Xextern char	*service;
Xextern char	*syscallnames[];
Xextern boolean	i_am_gateway;
Xextern boolean	i_am_asleep;
Xextern boolean	i_have_control;
Xextern boolean	route_to_gateway;
Xextern boolean	watch_for_lock;
Xextern boolean	gateway_needs_control;
Xextern syscallmap	smap[];
Xextern process	*wildcard;
Xextern hosts	*host;
X
Xmain(argc, argv)
X	int argc;
X	char **argv;
X{
X	register hosts	*h;
X
X	setopts(argc, argv);
X	if ((remote_debug & 0x200) == 0 && fork())
X		exit(0);
X	current_pid = getpid();
X	setlogfile();
X	if ((so_listen = tcppassive()) < 0)
X		log_fatal("cannot open socket\n");
X	init();
X	for (;;)
X	{
X		if ((h = tcpaccept(so_listen)) == NULL)
X			break;
X		debug4("call on fd %d, portno %d, from host \"%s\"\n",
X			h->h_cmdfd, h->h_portnum, h->h_names[0]);
X		dumphost(h);
X		if (server(h))
X			exit(0);
X	}
X}
X
X/*
X * This is the top lexical level for the server.  We decide here
X * when to call for a next request and whether we are running the
X * new format remote fs or not.
X *
X * We also decide whether this is a connection from the mount program
X * on a remote host or not.  If it is, then we just want to assemble the
X * info that it gives us and not become a child server.
X */
X
Xserver(h)
X	register hosts	*h;
X{
X	long		pipefd[ 2 ];
X	struct message	msgbuf,
X			*getmsg();
X	register struct message	*msg = &msgbuf;
X	register process	*proc;
X	register long	cmd, len;
X
X	/*
X	 * Get the first message from this connection.
X	 */
X	alarm(5);
X	if (! (msg = getmsg(h->h_cmdfd)))
X	{
X		log("connection initiation lost to \"%s\"\n", h->h_names[0]);
X		close(h->h_cmdfd);
X		h->h_cmdfd = -1;
X		alarm(0);
X		return(FALSE);
X	}
X	alarm(0);
X
X	/*
X	 * may be a special command
X	 */
X	len = msg->m_hdlen;
X	if (msg->m_syscall == RSYS_nosys)
X	{
X		cmd = msg->m_args[0];
X		debug5("new client; cmd=%d\n", cmd);
X		switch(cmd) {
X		case CMD_SERVICE:
X			break;
X		case CMD_MOUNT:
X			gobble_last_msg(h->h_cmdfd, msg);
X			getbyteorder(h);
X			getrusers(h);
X			return(FALSE);
X		case CMD_NEEDMOUNT:
X			sendmount(h);
X			dont_gobble_msg(msg);
X			return(FALSE);
X		default:
X			log_fatal("unknown server directive = %d\n", cmd);
X		}
X	}
X	else
X	{
X		debug5("new client, not mounted by rmtmnt\n");
X		dont_gobble_msg(msg);
X		if (!h->h_mounted)
X			getmount(h);
X	}
X
X	/*
X	 * If we reach this point, then we are to be the gateway server.
X	 * There may ba a server still running.  Kill it.
X	 */
X	if (h->h_serverpid)
X		sendsig(h->h_serverpid, SIGTERM);
X
X	if ((remote_debug & 0x200) == 0 && vfork())
X	{
X		wait(0);
X		/*
X		 * we are the parent... just return.
X		 */
X		close(h->h_cmdfd);
X		h->h_cmdfd = -1;
X		return(FALSE);
X	}
X	else if ((remote_debug & 0x200) == 0 && (h->h_serverpid = fork()))
X		exit(0);
X	host = h;
X	wildcard->p_handler = gateway_server = current_pid = getpid();
X	set_label("active");
X	if ((remote_debug & 0x200) == 0)
X	{
X		setlogfile();
X		close(so_listen);
X	}
X	if (pipe(pipefd) < 0)
X		log_fatal("Can't open pipe\n");
X	from_servers = pipefd[ 0 ];
X	to_gateway = pipefd[ 1 ];
X
X	/*
X	 * Ok, now be a server!
X	 */
X#ifdef CANREMOTE
X	remoteoff(NULL);
X#endif CANREMOTE
X	for(;;)
X	{
X		if (i_am_gateway && ! i_have_control)
X		{
X			gateway_listen();
X			continue;
X		}
X		/*
X		 * The gateway may be waiting for control.  Let's see.
X		 */
X		if (watch_for_lock)
X			if (gateway_needs_control
X			|| flock(2, LOCK_NB | LOCK_SH) < 0)
X			{
X				debug5("gateway wants control\n");
X				reroute(gateway_server, msg);
X				continue;
X			}
X			else
X				flock(2, LOCK_UN);
X			
X		if ((msg = getmsg(host->h_cmdfd)) == NULL)
X			break;
X		proc = change_to_proc(msg);
X		if (proc == NULL)
X			continue;
X
X		errno = 0;
X		(*smap[ msg->m_syscall ].s_server)(msg, proc);
X	}
X	debug5("done.\n");
X	if (i_am_gateway)
X		for (proc = host->h_proclist; proc; proc=proc->p_next)
X		{
X			/*
X			 * just hand it to the other server... he'll get eof
X			 */
X			if (proc->p_handler != current_pid)
X				sendsig(proc->p_handler, SIGIO);
X		}
X	else
X		say_something(S_EOF, 0);
X	if ((remote_debug & 0x800) == 0)
X		unlink(logfile);
X	return(TRUE);
X}
X
Xsetopts(argc, argv)
X	register int	argc;
X	register char	**argv;
X{
X	register int	error = FALSE;
X	register char	**p;
X	extern char	**environ;
X
X	program = argv[0];
X	last_argaddr = argv[argc-1];
X	for (argv++, argc--; argc; argv++, argc--)
X	{
X		if (**argv != '-')
X		{
X			log("arg \"%s\" is unknown\n",
X				*argv);
X			error = TRUE;
X		}
X		switch(argv[0][1]) {
X		case 'v':
X			if (argv[0][2])
X				remote_debug = atox(argv[0] + 2);
X			else if (isdigit(argv[1][0]))
X				argc--, remote_debug = atox(*(++argv));
X			break;
X		case 's':
X			if (argv[0][2])
X				service = argv[0] + 2;
X			else
X				argc--, service = *(++argv);
X			break;
X		default:
X			log("unknown flag = %s\n", *argv);
X			error = TRUE;
X		}
X	}
X	/*
X	 * Make sure that last_argaddr points to the last possible address
X	 */
X	p = environ;
X	while (*p)
X		p++;
X	if (p != environ)
X		p--;
X	if (*p)
X		last_argaddr = *p;
X	last_argaddr = (char *)ctob(btoc(last_argaddr)) - 1;
X	debug5("program addr=%x, last_argaddr=%x\n", program, last_argaddr);
X
X	if (error)
X		exit(1);
X}
X
X/*
X * ascii to hex
X */
Xatox(buf)
X	char    *buf;
X{
X	register char   *p;
X	register unsigned       num, nibble;
X
X	/*
X	 * now, take it out checking to make sure that the number is
X	 * valid.
X	 */
X	if (! buf)
X		return(0);
X	for(num=0, p = buf; *p; p++)
X	{
X		nibble = *p;
X		if (nibble >= 'A' && nibble <= 'F')
X			nibble -= 'A' - 10;
X		else if (nibble >= 'a' && nibble <= 'f')
X			nibble -= 'a' - 10;
X		else if (nibble >= '0' && nibble <= '9')
X			nibble -= '0';
X		else
X			return(0);
X		num = (num << 4) | nibble;
X	}
X	return(num);
X}
X
X/*
X * fork() and give the process on the top of the list to the child.
X */
Xbecome_server(msg)
X	register struct message	*msg;
X{
X	register long	pid, i;
X	register char	*fds;
X	register process	*proc = host->h_proclist;
X
X	/*
X	 * Have to change to uid 0 or we may be refused a fork
X	 */
X	change_to_uid(0);
X	i_am_asleep = TRUE;
X	if (pid = fork()) /* the parent loses control */
X	{
X		if (pid < 0)
X			log_fatal("cannot fork\n");
X		debug5("new server: pid=%d,mine=%d give him (%d)\n",
X			pid, current_pid, host->h_proclist->p_pid);
X		proc->p_handler = pid;
X		dont_gobble_msg(msg);
X		slumber(TRUE);
X		if (i_am_gateway)
X			current_server = pid;
X		return(FALSE);
X	}
X
X	/*
X	 * If we got this far, then we are no longer the gateway.  So set
X	 * our pid, etc.  Also, try to dup stderr so that flock will work.
X	 * If we can't do it, we are in big trouble.
X	 */
X#ifdef CANREMOTE
X	remoteoff(NULL);
X#endif CANREMOTE
X	current_ppid = current_pid;
X	current_pid = getpid();
X	if (i_am_gateway)
X	{
X		close(from_servers);
X		if (blocking_servers)
X		{
X			watch_for_lock = TRUE;
X			route_to_gateway = TRUE;
X		}
X	}
X	else
X		say_something(S_NEWSERVER, proc->p_pid);
X	i_am_gateway = FALSE;
X	set_label("active");
X	wildcard->p_handler = current_pid;
X	proc->p_handler = current_pid;
X	if ((i = dup(2)) < 0)
X		log_fatal("cannot dup(2)\n");
X	dup2(i, 2);
X	close(i);
X	debug5("new server: pid=%d, ppid=%d\n", current_pid, getppid());
X
X	return(TRUE);
X}
X
X#ifdef RFSDEBUG
Xdumphost(h)
X	register hosts	*h;
X{
X	register rusers	*ruser;
X
X	if ((remote_debug & 0x10) == 0)
X		return;
X	log("host %s, local user = %s, ruser@%x\n",
X		*h->h_names,
X		h->h_default_user ? h->h_default_user->u_name : "default",
X		ruser = h->h_default_ruser);
X	log("\tr %s(%d)-->%s(%d)\n",
X		ruser->r_name, ruser->r_uid,
X		ruser->r_user->u_name, ruser->r_user->u_local_uid);
X	for(ruser = h->h_rusers; ruser; ruser=ruser->r_next)
X		log("\tr %s(%d)-->%s(%d)\n",
X			ruser->r_name, ruser->r_uid,
X			ruser->r_user->u_name, ruser->r_user->u_local_uid);
X}
X#endif RFSDEBUG
X
Xset_label(string)
X	register char	*string;
X{
X	char		process_label[ 100 ];
X	static char	*pend;
X	static short	pid;
X	register char	*pfrom, *pto;
X	register long	i;
X
X	if (pid != current_pid)
X	{
X		if (i_am_gateway)
X			sprintf(process_label, "%s gateway server: ",
X				host->h_names[0]);
X		else
X			sprintf(process_label, "%s server via %d: ",
X				host->h_names[0], pid);
X		pid = current_pid;
X		pend = program + strlen(process_label);
X		strncpy(program, process_label, last_argaddr - program);
X		pto = pend;
X		while (pto < last_argaddr)
X			*pto++ = ' ';
X	}
X	pto = pend;
X	pfrom = string;
X	while (pto < last_argaddr && *pfrom)
X		*pto++ = *pfrom++;
X	for (i=0; pto < last_argaddr && i<10; i++)
X		*pto++ = ' ';
X	if (pto <= last_argaddr)
X		*pto = '\0';
X}
SHAREOF
chmod 444 remote/fileserver.c
#
# remote/find.c
#
if [ -f remote/find.c ]; then 
	echo -n 'Hit <return> to overwrite remote/find.c or ^C to quit' 
	read ans 
	rm -f remote/find.c 
fi 
 
sed -e 's/^.//' << \SHAREOF > remote/find.c
X/*
X * Copyright 1985, Todd Brunhoff.
X *
X * This software was written at Tektronix Computer Research Laboratories
X * as partial fulfillment of a Master's degree at the University of Denver.
X * This is not Tektronix proprietary software and should not be
X * confused with any software product sold by Tektronix.  No warranty is
X * expressed or implied on the reliability of this software; the author,
X * the University of Denver, and Tektronix, inc. accept no liability for
X * any damage done directly or indirectly by this software.  This software
X * may be copied, modified or used in any way, without fee, provided this
X * notice remains an unaltered part of the software.
X *
X * $Log:	find.c,v $
X * Revision 2.1  86/01/27  11:26:34  toddb
X * Changed the h_addr component of the hosts structure to be h_iaddr so
X * as not to conflict with the 4.3 define if h_addr in netdb.h.
X * 
X * Revision 2.0  85/12/07  18:21:23  toddb
X * First public release.
X * 
X */
Xstatic char	*rcsid = "$Header: find.c,v 2.1 86/01/27 11:26:34 toddb Exp $";
X#include	"server.h"
X#include	<stdio.h>
X#include	<signal.h>
X
Xextern hosts	*hostlist;
Xextern hosts	*host;
Xextern users	*userlist;
Xextern boolean	i_am_gateway;
Xextern short	current_pid;
X
Xprocess *findprocess(pid, uid)
X	register short	pid;
X	register short	uid;
X{
X	register process	*p;
X	register rusers	*ruser;
X
X	debug0("findproc: ", dumpprocs(host->h_proclist));
X	for(p = host->h_proclist; p; p=p->p_next)
X		if (p->p_pid == pid)
X		{
X			debug0("found pid %d\n", pid);
X			/*
X			 * If the user changes uid, then change with him.
X			 */
X			if (uid >= 0 && uid != p->p_uid)
X			{
X				debug2("pid %d changes uid %d->%d\n",
X					pid, p->p_uid, uid);
X				p->p_uid = uid;
X				if (ruser = findremuid(&host->h_rusers, uid))
X					p->p_ruser = ruser;
X				else
X					p->p_ruser = host->h_default_ruser;
X				debug2(" locally mapped to %s(%d)\n",
X					p->p_ruser->r_user->u_name,
X					p->p_ruser->r_user->u_local_uid);
X			}
X			toplist(&host->h_proclist, p);
X			return(p);
X		}
X	return(NULL);
X}
X
X/*
X * find the user structure whose name is 'name'.
X */
Xusers *findusername(name)
X	register char	*name;
X{
X	register users	*user;
X
X	for(user=userlist; user; user=user->u_next)
X		if (strcmp(user->u_name, name) == 0)
X		{
X			toplist(&userlist, user);
X			return(user);
X		}
X	return(NULL);
X}
X
Xhosts *findhostname(name)
X	register char	*name;
X{
X	register hosts	*h;
X	register int	i;
X	register char	**hnames;
X
X	for(h=hostlist; h; h=h->h_next)
X		for (i=0, hnames=h->h_names; hnames[ i ]; i++)
X			if (strcmp(hnames[ i ], name) == 0)
X			{
X				toplist(&hostlist, h);
X				return(h);
X			}
X	return(NULL);
X}
X
Xhosts *findhostaddr(addr)
X	register struct in_addr	*addr;
X{
X	register hosts	*h;
X
X	debug4("find %s...\n", inet_ntoa(*addr));
X	for(h=hostlist; h; h=h->h_next)
X		if (bcmp(addr, &h->h_iaddr, sizeof(struct in_addr)) == 0)
X		{
X			toplist(&hostlist, h);
X			debug4("\tis %s (%s)\n",
X				h->h_names[0], inet_ntoa(h->h_iaddr));
X			return(h);
X		}
X		else
X			debug4("\tnot %s (%s)\n",
X				h->h_names[0], inet_ntoa(h->h_iaddr));
X	log("no host entry for %s, continuing anyway.\n", inet_ntoa(*addr));
X	/*
X	 * Kludge up a hosts structure for this guy
X	 */
X	h = newhost();
X	h->h_names = newname(NULL, BOGUSHOST);
X	bcopy(addr, &h->h_iaddr, sizeof(struct in_addr));
X	addlist(&hostlist, h);
X	return(h);
X}
X
Xrusers *findremuid(list, uid)
X	register rusers	**list;
X	register int	uid;
X{
X	register rusers	*ruser;
X
X	for (ruser = *list; ruser; ruser=ruser->r_next)
X		if (ruser->r_uid == uid)
X		{
X			toplist(list, ruser);
X			return(ruser);
X		}
X	return(NULL);
X}
X
X/*
X * find the ruser structure whose name is 'name'.
X */
Xrusers *findrusername(list, name)
X	register rusers	**list;
X	register char	*name;
X{
X	register rusers	*ruser;
X
X	for(ruser = *list; ruser; ruser=ruser->r_next)
X		if (strcmp(ruser->r_name, name) == 0)
X		{
X			toplist(list, ruser);
X			return(ruser);
X		}
X	return(NULL);
X}
X
X#ifdef RFSDEBUG
Xdumpprocs(p)
X	register process	*p;
X{
X	register long	i, fd;
X
X	while(p)
X	{
X		log("proc@%x,pid=%d,uid=%d,next@%x,prev@%x,handler=%d\n",
X			p, p->p_pid, p->p_uid, p->p_next, p->p_prev,
X			p->p_handler);
X		log("\t%s(%d)->%s(%d),fds=",
X			p->p_ruser->r_name, p->p_ruser->r_uid,
X			p->p_ruser->r_user->u_name,
X			p->p_ruser->r_user->u_local_uid);
X		for (i=0; i<NOFILE; i++)
X			if ((fd = p->p_fds[ i ]) >= 0)
X				log("%d->%d ", i, fd);
X		log("\n");
X		p=p->p_next;
X	}
X}
X#endif RFSDEBUG
SHAREOF
chmod 444 remote/find.c