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