[net.sources] New wild & crazy pseudo-device driver for 4.2BSD

fred@gyre.UUCP (Fred Blonder) (10/16/84)

I once posted a version of this for 4.1BSD. Here's the new improved
version for 4.2B SD.

This is a really strange device driver, which can be put to all sorts
of mischevious uses. It consists of the series of files: /dev/fd0 -
/dev/fd19. Opening /dev/fdN opens the file (if such exists) associated
with file descriptor N in the current process, even if you don't know
its name, or even if it's been unlinked. If the file descriptor
references a socket, the driver fakes a ``dup()'' sys call on file
descriptor N. The driver, if the open is succesful, deletes all
references to itself from the process which opened it, so it consists
of only the ``open'' routine, since future references to the new file
descriptor will access the file which has been indirected to.

This can be used to force programs which don't normally do so, to read
their standard input, or write to their standard output.

Ex:

	make -f /dev/fd0
	<type makescript here>

or

	ln -s /dev/fd0 foo.c
	<command that generates C source> | cc foo.c -o /dev/fd1 \
						| <something real strange>

To install, add to these files:

==========================================================================
/sys/conf/<SYSNAME>:
			pseudo-device	fd

==========================================================================
/sys/conf/files:
			sys/dev_fd.c		optional fd

==========================================================================
/sys/vax/conf.c:
	299a309,315
	> #include "fd.h"
	> #if NFD > 0
	> int	fdopen();
	> #else
	> #define	fdopen	nodev
	> #endif NFD
	> 
	729a746,749
	> 
	> 	fdopen,		nodev,		nodev,		nodev,
	> 	nodev,		nodev,		nodev,		0,
	> 	nodev,		nodev,

==========================================================================

and then install these files:

==========================================================================
: Run this shell script with "sh" not "csh"
PATH=:/bin:/usr/bin:/usr/ucb
export PATH
all=FALSE
if [ $1x = -ax ]; then
	all=TRUE
fi
/bin/echo 'Extracting dev_fd.c'
sed 's/^X//' <<'//go.sysin dd *' >dev_fd.c
static char sccsid[] = "@(#)$Header: /usr/sys/sys/RCS/dev_fd.c,v 1.4 84/10/11 17:25:34 bin Exp $";

X/*
 * fd.c		Fred Blonder - U of Maryland	11-Sep-1984
 *
 * ``File Descriptor'' pseudo-device driver, rewritten for Berkeley 4.2.
 *
 * Opening minor device N opens the file (if any) connected to file-descriptor
 * N belonging to the calling process. Note that this driver consists of only
 * the ``open()'' routine, because all subsequent references to this file will
 * be direct to the other driver.
 *
 * $Log:	dev_fd.c,v $
 * Revision 1.4  84/10/11  17:25:34  bin
 * Added casts to get rid of compile-time diagnostics.
 * 
 * Revision 1.3  84/10/11  17:03:01  bin
 * Added code to check access modes when doing a fake ``dup()''.
 * Fixed bug in test to perform ``dup()''.
 * 
 * Revision 1.2  84/10/10  18:40:03  bin
 * Added code to fake a ``dup()'' sys call if the requested
 * descriptor refers to a socket.
 * 
 * Fixed a bug in the error-exit code.
 * 
 * Revision 1.1  84/10/08  15:11:02  bin
 * Initial revision
 * 
 */

#include "fd.h"
#if NFD > 0

#include "../h/param.h"
#include "../h/inode.h"
#include "../h/file.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/errno.h"

fdopen(dev, mode)
dev_t dev;
int mode;
{
	struct file *fp, *wfp;
	struct inode *ip, *wip;

	/* Note the horrid kludge here: u.u_r.r_val1 contains the value
	   of the new file descriptor, which has not been disturbed since
	   it was allocated. */
	if ((fp = getf(u.u_r.r_val1)) == NULL)
		return(u.u_error);

	if ((wfp = getf(minor(dev))) == NULL)
		return(u.u_error);

	/* We must explicitly test for this case because ufalloc() may
	   have allocated us the same file desriptor we are referring
	   to, if the proccess referred to an invalid (closed) descriptor.
	   Ordinarily this would be caught by getf(), but by the time we
	   reach this routine u_pofile[minor(dev)] could already be set
	   to point to our file struct. */
	if (fp == wfp)
		return(u.u_error = EBADF);

	ip = (struct inode *)fp->f_data;

	/* Special handling for sockets: Fake a ``dup()'' sys call
	   instead of opening an inode. */
	if (wfp->f_type != DTYPE_INODE) {
		/* Check that the mode the file is being opened
		   for is consistent with the mode of the existing
		   descriptor. This isn't as clean as it should be,
		   but this entire driver is a real kludge anyway. */
		if ((fp->f_flag & mode & (FREAD|FWRITE)) !=
						(mode & (FREAD|FWRITE))) {
			return(u.u_error = EACCES);
			}

		/* Delete references to this pseudo-device. */
		irele(ip);		/* Chuck the inode. */
		fp->f_count = 0;	/* Chuck the file structure. */
		/* Dup the file descriptor. */
		dupit(u.u_r.r_val1, wfp, u.u_pofile[minor(dev)]);
		return(0);
		}

	wip = (struct inode *)wfp->f_data;

	/* I'm not sure that we really need to lock the inode here,
	   but why not be paranoid? */
	ilock(wip);

	/* Since we're opening a file again, we run through all the
	   permission checks so this can't be used as a loophole to
	   get access to a file we shouldn't have. */
	if (mode&FREAD && access(wip, IREAD))
		goto bad;
	if (mode&(FWRITE|FTRUNC)) {
		if ((ip->i_mode&IFMT) == IFDIR) {
			u.u_error = EISDIR;
			goto bad;
			}
		if (access(wip, IWRITE))
			goto bad;
		}

	/* The file must always exist, so we don't even bother testing
	   for its presence. */
	if (mode&(FCREAT|FEXCL)) {
		u.u_error = EEXIST;
		goto bad;
		}

	/* This may not make any sense, but I'm paranoid and figure that
	   it's probably an error. */
	if (mode&FTRUNC) {
		u.u_error = EBUSY;
		goto bad;
		}

	/* Call the device-specific open routine, if any. */
	if ((u.u_error = openi(wip, mode)) != 0)
		goto bad;

	/* Made it this far, now switch the inode pointers in the
	   file descriptors around, to make this file open refer
	   to the other file. */
	irele(ip); /* We don't need this anymore. */
	fp->f_data = (caddr_t)wip;
	wip->i_count++;
	iunlock(wip);
	return(0);

    bad:
	iunlock(wip);
	return(u.u_error);
}

#endif
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 dev_fd.c
	/bin/echo -n '	'; /bin/ls -ld dev_fd.c
fi
/bin/echo 'Extracting fd.4'
sed 's/^X//' <<'//go.sysin dd *' >fd.4
X.TH FD 4l "13-Oct-1984 (U of Maryland)"
X.UC 4
X.SH NAME
X/dev/fd* \- ``file descriptor'' driver
X.SH DESCRIPTION
X.lg
X.I /dev/fd0
-
X.I /dev/fd19
are special files that reference the files associated with a process'
open file descriptors.
That is, opening the file: /dev/fd\fIN\fP is equivalent to opening whatever
file you opened to get the file descriptor \fIN\fP, even though you may not
know the file's true name (or even if it has one).
This can be used to force a program which opens files by name, to connect
itself to open file descriptors (which you have thoughtfully provided) in
weird and wonderful ways. A simple use would be to force a program which
only accepts input from files, to read from its standard input.
For instance:
X.sp
X.ti +10
cat /dev/fd0
X.sp
produces the same result as:
X.sp
X.ti +10
cat -
X.sp
which would be useful if \fIcat\fP didn't use the ``-'' convention.
X.sp 2
If the open file descriptor references a socket, the driver fakes a ``dup()''
system call instead of actually opening a file. In this case it
checks to see that the read/write mode you are attempting to open
the file with, is compatible with the mode of the existing file descriptor.
That is: if descriptor 5 refers to a socket and
is open for writing, you cannot open ``/dev/fd5''
for reading.
X.SH AUTHOR
Fred Blonder <Fred@Maryland>
X.SH FILES
X/dev/fd*
X.SH BUGS
Since, for sockets,
the driver fakes a ``dup'' system call rather than actually re-opening
the file, the new descriptor is a copy of the dup-ed descriptor, and shares
its properties. Specifically: it will have the same read/write mode as the
dup-ed descriptor. If descriptor 0 is open for reading and writing, opening
``/dev/fd0'' for reading will return a writable, as well as readable, file
descriptor.
Also: the descriptors share the same read/write pointer, so seeks, reads &
writes on one will affect the other.
X.sp
While not a bug in the driver specifically, use of these files can produce odd
interactions with programs which don't expect to have their file
descriptors surreptitiously aliased.
//go.sysin dd *
made=TRUE
if [ $made = TRUE ]; then
	/bin/chmod 644 fd.4
	/bin/echo -n '	'; /bin/ls -ld fd.4
fi
-- 
					Fred Blonder (301) 454-7690
					harpo!seismo!umcp-cs!fred
					Fred@Maryland.ARPA