Subject: RFS: remote file system (part 2 of 7) Newsgroups: mod.sources Approved: jpn@panda.UUCP Mod.sources: Volume 3, Issue 78 Submitted by: tektronix!tekcrl!toddb #!/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/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/info.c # remote/init.c # # remote/doc/paper/remotefs # if [ -f remote/doc/paper/remotefs ]; then echo -n 'Hit 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 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 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 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 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 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 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 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 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.0 85/12/07 18:21:14 toddb X * First public release. X * X */ Xstatic char *rcsid = "$Header: fileserver.c,v 2.0 85/12/07 18:21:14 toddb Rel $"; X#include X#include X#include X#include X#include "server.h" X#include 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 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 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 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.0 85/12/07 18:21:23 toddb X * First public release. X * X */ Xstatic char *rcsid = "$Header: find.c,v 2.0 85/12/07 18:21:23 toddb Rel $"; X#include "server.h" X#include X#include 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_addr, 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_addr)); X return(h); X } X else X debug4("\tnot %s (%s)\n", X h->h_names[0], inet_ntoa(h->h_addr)); 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_addr, 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; ip_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 # # remote/info.c # if [ -f remote/info.c ]; then echo -n 'Hit to overwrite remote/info.c or ^C to quit' read ans rm -f remote/info.c fi sed -e 's/^.//' << \SHAREOF > remote/info.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: info.c,v $ X * Revision 2.0 85/12/07 18:21:28 toddb X * First public release. X * X */ Xstatic char *rcsid = "$Header: info.c,v 2.0 85/12/07 18:21:28 toddb Rel $"; X#include "server.h" X#include X#include X#include X#include X#include X#include X Xextern users *default_user; Xextern hosts *host; Xextern hosts *thishost; Xextern char byteorder[]; Xextern char *logfile; Xextern long serviceport; Xextern long errno; Xextern short current_pid; Xextern process *wildcard; Xextern boolean in_root_directory; Xextern boolean i_am_asleep; X Xgetbyteorder(h) X register hosts *h; X{ X register char *p; X X /* X * Read the byte order info. X */ X alarm(5); X p = (char *)h->h_byteorder; X if (read(h->h_cmdfd, p, 4) != 4) X { X log("can't read mount info from \"%s\"\n", h->h_names[0]); X alarm(0); X bzero(p, 4); X close(h->h_cmdfd); X h->h_cmdfd = -1; X return; X } X alarm(0); X if (bcmp(p, byteorder, 4) == 0) X h->h_byteorderok = TRUE; X else X h->h_byteorderok = FALSE; X debug4("byteorder=%d,%d,%d,%d: %s ours\n", X p[0], p[1], p[2], p[3], X h->h_byteorderok ? "same as" : "different than"); X} X Xgetrusers(h) X register hosts *h; X{ X char buf[ BUFSIZ ]; X register rusers *ruser; X register FILE *input; X register char *p; X register int uid; X X errno = 0; X /* X * Read in the users from the remote host and squirrel them away. X * Actually it is the actual password file from the remote X * host, and we ignore most of the info, and save the user name and X * uid. X */ X if ((input = fdopen(h->h_cmdfd, "r")) == NULL) X log_fatal("getrusers: cannot fdopen\n"); X X alarm(30); X while (fgets(buf, BUFSIZ, input)) X { X /* X * First, the user name. X */ X for(p=buf; *p && *p != ':'; p++) ; X *p = '\0'; X X /* X * now the user id number X */ X for(p++; *p && *p != ':'; p++) ; X uid = atoi(p+1); X X /* X * Now we need to add the info to our database on this remote X * host. If the user is already present, just update the uid X * number. If the user is not present, and there is a default X * local user for this remote host, use that. If not, then X * use the default user entry for this host (where the server X * runs). X */ X if (ruser = findrusername(&h->h_rusers, buf)) X { X debug2("(existing) "); X ruser->r_uid = uid; X } X else X { X debug2("(new, "); X ruser = newruser(); X ruser->r_name = copy(buf); X ruser->r_uid = uid; X if (h->h_default_user) X { X debug2("%s default)", *h->h_names); X ruser->r_user = h->h_default_user; X } X else X { X ruser->r_user = default_user; X debug2("host default)"); X } X addlist(&h->h_rusers, ruser); X } X debug2("host %s: user %s (%d) -> local user %s (%d)\n", X h->h_names[ 0 ], ruser->r_name, ruser->r_uid, X ruser->r_user->u_name, ruser->r_user->u_local_uid); X } X fclose(input); X h->h_cmdfd = -1; X alarm(0); X if (errno == EINTR) X log("can't get remote users from \"%s\"\n", h->h_names[0]); X} X X/* X * Try to obtain mount information for host 'h'. X */ Xgetmount(h) X register hosts *h; X{ X long savefd = h->h_cmdfd; X struct message msgbuf; X register struct message *msg = &msgbuf; X register long len; X X if (thishost == h) /* our own machine */ X { X log("we are talking to ourselves\n"); X h->h_cmdfd = open("/etc/passwd", O_RDONLY); X bcopy(byteorder, h->h_byteorder, 4); X h->h_byteorderok = TRUE; X } X else X { X if ((h->h_cmdfd = tcpconnect(h)) < 0) X goto done; X len = R_MINRMSG + sizeof(long); X msg->m_hdlen = htons(len); X msg->m_totlen = htonl(len); X msg->m_syscall = htons(RSYS_nosys); X msg->m_args[ 0 ] = htonl(CMD_NEEDMOUNT); X if (!sndmsg(h->h_cmdfd, msg, len, 0, 0)) X { X log("can't ask for mount info\n"); X close(h->h_cmdfd); X goto done; X } X getbyteorder(h); X } X getrusers(h); /* getrusers() closes the file descriptor */ Xdone: X h->h_cmdfd = savefd; X} X X/* X * Send mount information. This includes a 4-byte header containing the X * byte order for our machine, followed by /etc/passwd. X */ Xsendmount(h) X register hosts *h; X{ X char buf[ BUFSIZ ]; X register long fd = open("/etc/passwd", O_RDONLY), X cnt; X register char *p = buf; X X write(h->h_cmdfd, byteorder, 4); X while ((cnt = read(fd, p, BUFSIZ)) > 0) X _rmtio(write, h->h_cmdfd, p, cnt); X close(h->h_cmdfd); X h->h_cmdfd = -1; X close(fd); X} X X/* X * Mourne the death of any children. X */ Xmourne() X{ X union wait status; X char buf[ BUFSIZ ]; X register char *p = buf; X register long pid; X X while ((pid = wait3(&status, WNOHANG, 0)) > 0) X { X sprintf(p, "server %d found dead", pid); X p += strlen(p); X if (status.w_termsig) X sprintf(p, " by sig #%d", status.w_termsig); X p += strlen(p); X if (status.w_coredump) X sprintf(p, " with core dump", status.w_termsig); X p += strlen(p); X sprintf(p, " exit=%d\n", status.w_retcode); X debug5("%s", buf); X p = buf; X } X} X X/* X * Catch signals and only report. X */ Xcatch(sig, code, scp) X register long sig, X code; X register struct sigcontext *scp; X{ X log("caught signal #%d...", sig); X if (sig == SIGILL || sig == SIGSEGV || sig == SIGBUS) X { X change_to_uid(0); X chdir("/usr/tmp"); X log("aborting: code=%d, scp=%x, sp=%x, pc=%x, end of scp=%x\n", X code, scp, scp->sc_sp, scp->sc_pc, scp+1); X sendsig(current_pid, SIGEMT); X log_fatal("could not abort\n"); X } X else if (sig == SIGTERM) /* quietly go away */ X { X /* X * unlink the file only if we are allowed to and if it is X * not the sentry server's logfile. X */ X if ((remote_debug & 0x800) == 0 && host != NULL) X unlink(logfile); X cleanup(); X log("goodbye.\n"); X exit(0); X } X else X log_fatal("exiting\n"); X} X X/* X * Receive a wakeup call. X */ Xwakeup_call() X{ X if (! i_am_asleep) X { X log("recieved spurious wakeup call!\n"); X return; X } X i_am_asleep = FALSE; X} X X/* X * Provide name server function for kernel. At this point we have just X * recieved a SIGURG signal because the kernel wants us to translate a X * name. X */ Xnameserver() X{ X#ifdef CANREMOTE X char path[ BUFSIZ ], X hostname[ BUFSIZ ]; X struct sockaddr_in sinbuf; X register char *p1, *p2, *name; X register hosts *h = NULL; X register struct sockaddr_in *sin; X X if (remotename(NM_WHATNAME, 0, BUFSIZ, path) < 0) X return; X /* X * Find the end of the '/' prefix and copy up to the next '/' or X * null character. X */ X p1 = path; X while (*p1 == '/') X p1++; X p2 = hostname; X *p2++ = '/'; X while (*p1 && *p1 != '/') X *p2++ = *p1++; X *p2 = '\0'; X X /* X * Now look it up. X */ X if (hostname[1]) X h = findhostname(hostname+1); X if (h == NULL) X { X debug6("cannot find host for path \"%s\"\n", path); X sin = NULL; X } X else X { X sin = &sinbuf; X debug6("path %s mapped to host %s\n", path, h->h_names[0]); X bzero((char *)sin, sizeof (struct sockaddr_in)); X bcopy(&h->h_addr, (char *)&sin->sin_addr, X sizeof(struct in_addr)); X sin->sin_family = AF_INET; X sin->sin_port = serviceport; X } X remotename(NM_NAMEIS, sin, sizeof(struct sockaddr_in), hostname); X#endif CANREMOTE X} X X/* X * Decide if a file is really a local file to the client or not. We only X * look for explicit references like X * name1/name2/../name3 ... X * And we then check to see whether name2 is the root directory. If it is X * then we send the request back to the client. X */ Xislocal(msg, type) X register struct message *msg; X{ X register char *p; X register boolean checktwopaths; X register short syscall = msg->m_syscall; X register long offset1, X offset2 = -1, X localcnt = 0; X register process *proc; X char buf[ BUFSIZ ]; X X debug10("cwd=%s\n", getwd(buf)); X checktwopaths = (type & NEED_2REMOTE); X X if (checktwopaths) X p = twopath1addr(msg); X else X p = path1addr(msg); X if ((offset1 = find_dotdot(p)) >= 0) X localcnt++; X if (checktwopaths) X if ((offset2 = find_dotdot(twopath2addr(msg))) >= 0) X localcnt++; X if (localcnt) X { X debug10("%d paths are remote: \"%s\" @ %d, \"%s\" @ %d\n", X localcnt, X checktwopaths ? twopath1addr(msg) : path1addr(msg), X offset1, checktwopaths ? twopath2addr(msg) : "", X offset2); X setup_proc(proc = wildcard, msg->m_uid, msg->m_pid); X proc->p_errno = -1; X proc->p_returnval = offset1; X sendreturn(proc, host->h_cmdfd, NULL, 1, offset2); X } X return(localcnt); X} X X/* X * kernel code stolen for speed. X */ Xmyaccess(st, user, perm) X register struct stat *st; X register long perm; X register users *user; X{ X register long *gp, i; X X perm <<= 6; X if (user->u_local_uid != st->st_uid) { X perm >>= 3; X gp = user->u_local_groups; X for (i=0; i < user->u_numgroups; i++, gp++) X if (st->st_gid == *gp) X goto found; X perm >>= 3; Xfound: X ; X } X if ((st->st_mode & perm) != 0) X return (TRUE); X return(FALSE); X} X X/* X * look for a component of ".." terminated by a '/' or a null character. X * If we find one, examine the previous component to see if it is our X * root directory. X */ Xfind_dotdot(path) X register char *path; X{ X struct stat statb; X register char *p; X register struct stat *statp = &statb; X register long retval; X extern struct stat root; X X for (p = path; *p;) X { X while (*p == '/') X p++; X if (p[0] == '.' && p[1] == '.' X && (p[2] == '\0' || p[2] == '/')) X { X if (p == path) X if (in_root_directory) X return(0); X else X goto next_component; X *p = '\0'; /* we know it is a '.' */ X retval = lstat(path, statp); X *p = '.'; X if (retval < 0) X return(retval); X if (isroot(statp)) X return(p - path + 1); X } Xnext_component: X while (*p && *p != '/') X p++; X } X return(-1); X} SHAREOF chmod 444 remote/info.c # # remote/init.c # if [ -f remote/init.c ]; then echo -n 'Hit to overwrite remote/init.c or ^C to quit' read ans rm -f remote/init.c fi sed -e 's/^.//' << \SHAREOF > remote/init.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: init.c,v $ X * Revision 2.1 86/01/05 18:13:54 toddb X * Added include for sys/stat.h because pyramid machines get upset. X * X * Revision 2.0 85/12/07 18:21:37 toddb X * First public release. X * X */ Xstatic char *rcsid = "$Header: init.c,v 2.1 86/01/05 18:13:54 toddb Exp $"; X#include "server.h" X#include X#include X#include X#include X#include X#include X#include X#include X#include X#include X Xextern hosts *hostlist; Xextern hosts *thishost; Xextern users *userlist; Xextern users *default_user; Xextern char hostname[]; Xextern char *service; Xextern short current_uid; Xextern short current_pid; Xextern process *wildcard; Xextern struct sigvec sig_vec; Xextern struct sigvec sig_name; Xextern struct sigvec sig_alarm; Xextern struct sigvec sig_ignore; Xextern struct sigvec sig_continue; X#ifdef RFSDEBUG Xextern struct sigvec sig_debug; X#endif Xextern struct stat root; X X/* X * Initialize the host tables and user tables. X */ Xinit() X{ X long tt; X struct hostent *gethostent(); X struct passwd *getpwent(); X struct group *getgrent(); X X /* X * catch signals. X */ X sigvec(SIGHUP, &sig_ignore, (struct sigvec *)0); X sigvec(SIGINT, &sig_vec, (struct sigvec *)0); X sigvec(SIGQUIT, &sig_vec, (struct sigvec *)0); X sigvec(SIGILL, &sig_vec, (struct sigvec *)0); X#ifdef RFSDEBUG X sigvec(SIGTRAP, &sig_debug, (struct sigvec *)0); X#endif RFSDEBUG X /* SIGIOT */ X /* SIGEMT */ X /* SIGFPE */ X /* SIGKILL */ X sigvec(SIGBUS, &sig_vec, (struct sigvec *)0); X sigvec(SIGSEGV, &sig_vec, (struct sigvec *)0); X sigvec(SIGSYS, &sig_vec, (struct sigvec *)0); X sigvec(SIGPIPE, &sig_vec, (struct sigvec *)0); X sigvec(SIGALRM, &sig_alarm, (struct sigvec *)0); X sigvec(SIGTERM, &sig_vec, (struct sigvec *)0); X sigvec(SIGURG, &sig_name, (struct sigvec *)0); X /* SIGSTOP */ X /* SIGTSTP */ X /* SIGCONT */ X /* SIGCHLD */ X sigvec(SIGTTIN, &sig_vec, (struct sigvec *)0); X sigvec(SIGTTOU, &sig_vec, (struct sigvec *)0); X sigvec(SIGIO, &sig_continue, (struct sigvec *)0); X sigvec(SIGXCPU, &sig_vec, (struct sigvec *)0); X sigvec(SIGXFSZ, &sig_vec, (struct sigvec *)0); X sigvec(SIGVTALRM, &sig_vec, (struct sigvec *)0); X /* SIGPROF */ X X /* X * set up some important global values, including uid, pid, X * the pipe file descriptors for messages to and from the gateway X * server. Register as the nameserver. Get host name. Get service. X * Get root stat info. X */ X if (chdir("/") == -1) X log_fatal("cannot chdir(\"/\")\n"); X wildcard = newprocess(); X fcntl(2, F_SETFL, FAPPEND); X close(0); X close(1); X change_to_uid(0); X if (gethostname(hostname, HOSTNAMELEN) < 0 || *hostname == '\0') X log_fatal("host name not set!\n"); X if (stat("/", &root) < 0) X log_fatal("cannot stat /\n"); X#ifdef CANREMOTE X if (remotename(NM_SERVER, 0, 0, 0) < 0) X log("cannot register as nameserver\n"); X /* X * Turn off remote access, if we have any. X */ X remoteoff(NULL); X#endif X tt = open("/dev/tty", 2); X X if (tt >= 0) X { X ioctl(tt, TIOCNOTTY, 0); X close(tt); X } X setpgrp(0,0); X X initusers(); X initgroups(); X inithosts(); X initrhosts(); X} X X/* X * build the list of users on this host (where the server runs). X */ Xinitusers() X{ X register struct passwd *pw; X register users *user; X char buf[ BUFSIZ ]; X register char *pbuf = buf; X X while(pw = getpwent()) X { X if (*pw->pw_dir == '\0' || *pw->pw_name == '\0') X { X log("login \"%s\" has problems, dir=\"%s\"\n", X pw->pw_name, pw->pw_dir); X continue; X } X user = newuser(); X user->u_local_uid = pw->pw_uid; X user->u_name = copy( pw->pw_name ); X addgroup(user, pw->pw_gid); X user->u_dir = copy( pw->pw_dir ); X sprintf(pbuf, "%s/.rhosts", pw->pw_dir); X user->u_rhosts = copy( pbuf ); X addlist(&userlist, user); X } X endpwent(); X if (user = findusername(DEFAULTUSER)) X default_user = user; X else X log_fatal("The user \"%s\" must be in /etc/passwd (%s)\n", X DEFAULTUSER, "for default permissions"); X} X X/* X * Build the list of groups that each user belongs to. X */ Xinitgroups() X{ X register struct group *gr; X register users *user; X register char **p; X X X while(gr = getgrent()) X { X for (p = gr->gr_mem; *p; p++) X if (user = findusername(*p)) X addgroup(user, gr->gr_gid); X else X log("group %s: bad user=%s\n", X gr->gr_name, *p); X } X endgrent(); X} X X/* X * Then build the list of all hosts. X */ Xinithosts() X{ X register struct hostent *h; X register rusers *ruser; X register hosts *hst; X register users *user; X register long i; X X while (h = gethostent()) X { X hst = newhost(); X hst->h_names = newname(hst->h_names, h->h_name); X for (i=0; h->h_aliases[ i ]; i++) X hst->h_names = newname(hst->h_names, X h->h_aliases[ i ]); X X hst->h_addr = *((struct in_addr *)(h->h_addr)); X addlist(&hostlist, hst); X X /* X * now if there exists a user on this machine having X * the same name as the name of this host (NOT AN X * ALIAS!), then that will be our defaut local user X * to map to. Be sure that we don't allow a machine X * to be mapped onto a user if the uid is real small: X * e.g. a machine named root, where all its user ids X * become root using the remote fs! X */ X user = findusername(hst->h_names[ 0 ]); X if (user && user->u_local_uid <= UID_TOO_LOW) X { X log("host/user %s: uid %d too low for alias\n", X hst->h_names[ 0 ], user->u_local_uid); X user = NULL; X } X else if (user) X { X hst->h_default_user = user; X debug2("default user for host %s (%s) is %s\n", X hst->h_names[ 0 ], X inet_ntoa(hst->h_addr), user->u_name); X } X ruser = hst->h_default_ruser = newruser(); X if (user) X ruser->r_user = user; X else X ruser->r_user = default_user; X ruser->r_uid = -1; X ruser->r_name = copy(BOGUSUSER); X } X endhostent(); X if ((thishost = findhostname(hostname)) == NULL) X log_fatal("this host (\"%s\") is not in host file\n", X hostname); X} X X/* X * Now for each user that has a .rhosts file, assemble the X * references and attach them to the appropriate host. X */ Xinitrhosts() X{ X register hosts *hst; X register rhost *rh; X register users *user; X char buf[ BUFSIZ ]; X register char *pbuf = buf; X X for (user=userlist; user; user=user->u_next) X { X setrhost(user->u_rhosts); X while (rh = getrhostent(pbuf)) X if (hst = findhostname(rh->rh_host)) X addremoteuser(hst, user, rh->rh_user); X endrhost(); X } X} X Xchar *copy(string) X register char *string; X{ X register char *ret = malloc( strlen(string)+1 ); X X if (ret == NULL) X log_fatal("cannot allocate space\n"); X strcpy(ret, string); X return(ret); X} X X/* X * Add a remote user to those recognized on a certain host. X */ Xaddremoteuser(h, user, remoteuser) X register hosts *h; X register users *user; X register char *remoteuser; X{ X register rusers *ruser; X register long old = FALSE; X X debug2("\t%s!%s --> %s ", *h->h_names, remoteuser, user->u_name); X if ((ruser = findrusername(&h->h_rusers, remoteuser)) == NULL) X { X debug2("\n"); X ruser = newruser(); X } X else X { X old = TRUE; X if (strcmp(remoteuser, user->u_name) != 0) X { X debug2("(old, ignored)\n"); X return; X } X else X debug2("(old)\n"); X } X ruser->r_name = copy(remoteuser); X ruser->r_uid = -1; X ruser->r_user = user; X if (! old) X addlist(&h->h_rusers, ruser); X} SHAREOF chmod 444 remote/init.c