Home | History | Annotate | Line # | Download | only in librmt
rmtlib.c revision 1.12
      1 /*	$NetBSD: rmtlib.c,v 1.12 1999/09/16 11:45:49 lukem Exp $	*/
      2 
      3 /*
      4  *	rmt --- remote tape emulator subroutines
      5  *
      6  *	Originally written by Jeff Lee, modified some by Arnold Robbins
      7  *
      8  *	WARNING:  The man page rmt(8) for /etc/rmt documents the remote mag
      9  *	tape protocol which rdump and rrestore use.  Unfortunately, the man
     10  *	page is *WRONG*.  The author of the routines I'm including originally
     11  *	wrote his code just based on the man page, and it didn't work, so he
     12  *	went to the rdump source to figure out why.  The only thing he had to
     13  *	change was to check for the 'F' return code in addition to the 'E',
     14  *	and to separate the various arguments with \n instead of a space.  I
     15  *	personally don't think that this is much of a problem, but I wanted to
     16  *	point it out.
     17  *	-- Arnold Robbins
     18  *
     19  *	Redone as a library that can replace open, read, write, etc, by
     20  *	Fred Fish, with some additional work by Arnold Robbins.
     21  */
     22 
     23 /*
     24  *	MAXUNIT --- Maximum number of remote tape file units
     25  *
     26  *	READ --- Return the number of the read side file descriptor
     27  *	WRITE --- Return the number of the write side file descriptor
     28  */
     29 
     30 #define RMTIOCTL	1
     31 /* #define USE_REXEC	1 */	/* rexec code courtesy of Dan Kegel, srs!dan */
     32 
     33 #include <sys/types.h>
     34 #include <sys/stat.h>
     35 
     36 #ifdef RMTIOCTL
     37 #include <sys/ioctl.h>
     38 #include <sys/mtio.h>
     39 #endif
     40 
     41 #include <assert.h>
     42 #include <errno.h>
     43 #include <fcntl.h>
     44 #include <signal.h>
     45 #include <stdio.h>
     46 #include <stdlib.h>
     47 #include <string.h>
     48 #include <unistd.h>
     49 
     50 #ifdef USE_REXEC
     51 #include <netdb.h>
     52 #endif
     53 
     54 #define __RMTLIB_PRIVATE
     55 #include <rmt.h>		/* get prototypes for remapped functions */
     56 
     57 #ifdef __STDC__
     58 #include <stdarg.h>
     59 #else
     60 #include <varargs.h>
     61 #endif
     62 
     63 #include "pathnames.h"
     64 
     65 static	int	_rmt_close __P((int));
     66 static	int	_rmt_ioctl __P((int, unsigned long, char *));
     67 static	off_t	_rmt_lseek __P((int, off_t, int));
     68 static	int	_rmt_open __P((const char *, int, int));
     69 static	int	_rmt_read __P((int, char *, unsigned int));
     70 static	int	_rmt_write __P((int, const void *, unsigned int));
     71 static	int	command __P((int, char *));
     72 static	int	remdev __P((const char *));
     73 static	void	rmtabort __P((int));
     74 static	int	status __P((int));
     75 
     76 	int	isrmt __P((int));
     77 
     78 
     79 #define BUFMAGIC	64	/* a magic number for buffer sizes */
     80 #define MAXUNIT	4
     81 
     82 #define READ(fd)	(Ctp[fd][0])
     83 #define WRITE(fd)	(Ptc[fd][1])
     84 
     85 static int Ctp[MAXUNIT][2] = { {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1} };
     86 static int Ptc[MAXUNIT][2] = { {-1, -1}, {-1, -1}, {-1, -1}, {-1, -1} };
     87 
     88 
     89 /*
     90  *	rmtabort --- close off a remote tape connection
     91  */
     92 
     93 static void
     94 rmtabort(fildes)
     95 	int fildes;
     96 {
     97 	close(READ(fildes));
     98 	close(WRITE(fildes));
     99 	READ(fildes) = -1;
    100 	WRITE(fildes) = -1;
    101 }
    102 
    103 
    104 
    105 /*
    106  *	command --- attempt to perform a remote tape command
    107  */
    108 
    109 static int
    110 command(fildes, buf)
    111 	int fildes;
    112 	char *buf;
    113 {
    114 	int blen;
    115 	void (*pstat) __P((int));
    116 
    117 	_DIAGASSERT(buf != NULL);
    118 
    119 /*
    120  *	save current pipe status and try to make the request
    121  */
    122 
    123 	blen = strlen(buf);
    124 	pstat = signal(SIGPIPE, SIG_IGN);
    125 	if (write(WRITE(fildes), buf, blen) == blen)
    126 	{
    127 		signal(SIGPIPE, pstat);
    128 		return(0);
    129 	}
    130 
    131 /*
    132  *	something went wrong. close down and go home
    133  */
    134 
    135 	signal(SIGPIPE, pstat);
    136 	rmtabort(fildes);
    137 
    138 	errno = EIO;
    139 	return(-1);
    140 }
    141 
    142 
    143 
    144 /*
    145  *	status --- retrieve the status from the pipe
    146  */
    147 
    148 static int
    149 status(fildes)
    150 	int fildes;
    151 {
    152 	int i;
    153 	char c, *cp;
    154 	char buffer[BUFMAGIC];
    155 
    156 /*
    157  *	read the reply command line
    158  */
    159 
    160 	for (i = 0, cp = buffer; i < BUFMAGIC; i++, cp++)
    161 	{
    162 		if (read(READ(fildes), cp, 1) != 1)
    163 		{
    164 			rmtabort(fildes);
    165 			errno = EIO;
    166 			return(-1);
    167 		}
    168 		if (*cp == '\n')
    169 		{
    170 			*cp = 0;
    171 			break;
    172 		}
    173 	}
    174 
    175 	if (i == BUFMAGIC)
    176 	{
    177 		rmtabort(fildes);
    178 		errno = EIO;
    179 		return(-1);
    180 	}
    181 
    182 /*
    183  *	check the return status
    184  */
    185 
    186 	for (cp = buffer; *cp; cp++)
    187 		if (*cp != ' ')
    188 			break;
    189 
    190 	if (*cp == 'E' || *cp == 'F')
    191 	{
    192 		errno = atoi(cp + 1);
    193 		while (read(READ(fildes), &c, 1) == 1)
    194 			if (c == '\n')
    195 				break;
    196 
    197 		if (*cp == 'F')
    198 			rmtabort(fildes);
    199 
    200 		return(-1);
    201 	}
    202 
    203 /*
    204  *	check for mis-synced pipes
    205  */
    206 
    207 	if (*cp != 'A')
    208 	{
    209 		rmtabort(fildes);
    210 		errno = EIO;
    211 		return(-1);
    212 	}
    213 
    214 	return(atoi(cp + 1));
    215 }
    216 
    217 #ifdef USE_REXEC
    218 
    219 /*
    220  * _rmt_rexec
    221  *
    222  * execute /etc/rmt on a remote system using rexec().
    223  * Return file descriptor of bidirectional socket for stdin and stdout
    224  * If username is NULL, or an empty string, uses current username.
    225  *
    226  * ADR: By default, this code is not used, since it requires that
    227  * the user have a .netrc file in his/her home directory, or that the
    228  * application designer be willing to have rexec prompt for login and
    229  * password info. This may be unacceptable, and .rhosts files for use
    230  * with rsh are much more common on BSD systems.
    231  */
    232 
    233 static	int	_rmt_rexec __P((const char *, const char *));
    234 
    235 static int
    236 _rmt_rexec(host, user)
    237 	const char *host;
    238 	const char *user;		/* may be NULL */
    239 {
    240 	struct servent *rexecserv;
    241 
    242 	_DIAGASSERT(host != NULL);
    243 	/* user may be NULL */
    244 
    245 	rexecserv = getservbyname("exec", "tcp");
    246 	if (NULL == rexecserv) {
    247 		fprintf (stderr, "? exec/tcp: service not available.");
    248 		exit (-1);
    249 	}
    250 	if ((user != NULL) && *user == '\0')
    251 		user = (char *) NULL;
    252 	return rexec (&host, rexecserv->s_port, user, NULL,
    253 			"/etc/rmt", (int *)NULL);
    254 }
    255 #endif /* USE_REXEC */
    256 
    257 /*
    258  *	_rmt_open --- open a magtape device on system specified, as given user
    259  *
    260  *	file name has the form [user@]system:/dev/????
    261 #ifdef COMPAT
    262  *	file name has the form system[.user]:/dev/????
    263 #endif
    264  */
    265 
    266 #define MAXHOSTLEN	257	/* BSD allows very long host names... */
    267 
    268 static int
    269 _rmt_open(path, oflag, mode)
    270 	const char *path;
    271 	int oflag;
    272 	int mode;
    273 {
    274 	int i, rc;
    275 	char buffer[BUFMAGIC];
    276 	char system[MAXHOSTLEN];
    277 	char device[BUFMAGIC];
    278 	char login[BUFMAGIC];
    279 	char *sys, *dev, *user;
    280 
    281 	_DIAGASSERT(path != NULL);
    282 
    283 	sys = system;
    284 	dev = device;
    285 	user = login;
    286 
    287 /*
    288  *	first, find an open pair of file descriptors
    289  */
    290 
    291 	for (i = 0; i < MAXUNIT; i++)
    292 		if (READ(i) == -1 && WRITE(i) == -1)
    293 			break;
    294 
    295 	if (i == MAXUNIT)
    296 	{
    297 		errno = EMFILE;
    298 		return(-1);
    299 	}
    300 
    301 /*
    302  *	pull apart system and device, and optional user
    303  *	don't munge original string
    304  *	if COMPAT is defined, also handle old (4.2) style person.site notation.
    305  */
    306 
    307 	while (*path != '@'
    308 #ifdef COMPAT
    309 			&& *path != '.'
    310 #endif
    311 			&& *path != ':') {
    312 		*sys++ = *path++;
    313 	}
    314 	*sys = '\0';
    315 	path++;
    316 
    317 	if (*(path - 1) == '@')
    318 	{
    319 		(void)strncpy(user, system, sizeof(login) - 1);
    320 				/* saw user part of user@host */
    321 		sys = system;			/* start over */
    322 		while (*path != ':') {
    323 			*sys++ = *path++;
    324 		}
    325 		*sys = '\0';
    326 		path++;
    327 	}
    328 #ifdef COMPAT
    329 	else if (*(path - 1) == '.')
    330 	{
    331 		while (*path != ':') {
    332 			*user++ = *path++;
    333 		}
    334 		*user = '\0';
    335 		path++;
    336 	}
    337 #endif
    338 	else
    339 		*user = '\0';
    340 
    341 	while (*path) {
    342 		*dev++ = *path++;
    343 	}
    344 	*dev = '\0';
    345 
    346 #ifdef USE_REXEC
    347 /*
    348  *	Execute the remote command using rexec
    349  */
    350 	READ(i) = WRITE(i) = _rmt_rexec(system, login);
    351 	if (READ(i) < 0)
    352 		return -1;
    353 #else
    354 /*
    355  *	setup the pipes for the 'rsh' command and fork
    356  */
    357 
    358 	if (pipe(Ptc[i]) == -1 || pipe(Ctp[i]) == -1)
    359 		return(-1);
    360 
    361 	if ((rc = fork()) == -1)
    362 		return(-1);
    363 
    364 	if (rc == 0)
    365 	{
    366 		char	*rshpath, *rsh;
    367 
    368 		close(0);
    369 		dup(Ptc[i][0]);
    370 		close(Ptc[i][0]); close(Ptc[i][1]);
    371 		close(1);
    372 		dup(Ctp[i][1]);
    373 		close(Ctp[i][0]); close(Ctp[i][1]);
    374 		(void) setuid (getuid ());
    375 		(void) setgid (getgid ());
    376 
    377 		if ((rshpath = getenv("RCMD_CMD")) == NULL)
    378 			rshpath = _PATH_RSH;
    379 		if ((rsh = strrchr(rshpath, '/')) == NULL)
    380 			rsh = rshpath;
    381 		else
    382 			rsh++;
    383 
    384 		if (*login)
    385 		{
    386 			execl(rshpath, rsh, system, "-l", login,
    387 				_PATH_RMT, (char *) 0);
    388 		}
    389 		else
    390 		{
    391 			execl(rshpath, rsh, system,
    392 				_PATH_RMT, (char *) 0);
    393 		}
    394 
    395 /*
    396  *	bad problems if we get here
    397  */
    398 
    399 		perror("exec");
    400 		exit(1);
    401 	}
    402 
    403 	close(Ptc[i][0]); close(Ctp[i][1]);
    404 #endif
    405 
    406 /*
    407  *	now attempt to open the tape device
    408  */
    409 
    410 	(void)snprintf(buffer, sizeof(buffer), "O%s\n%d\n", device, oflag);
    411 	if (command(i, buffer) == -1 || status(i) == -1)
    412 		return(-1);
    413 
    414 	return(i);
    415 }
    416 
    417 
    418 
    419 /*
    420  *	_rmt_close --- close a remote magtape unit and shut down
    421  */
    422 
    423 static int
    424 _rmt_close(fildes)
    425 	int fildes;
    426 {
    427 	int rc;
    428 
    429 	if (command(fildes, "C\n") != -1)
    430 	{
    431 		rc = status(fildes);
    432 
    433 		rmtabort(fildes);
    434 		return(rc);
    435 	}
    436 
    437 	return(-1);
    438 }
    439 
    440 
    441 
    442 /*
    443  *	_rmt_read --- read a buffer from a remote tape
    444  */
    445 
    446 static int
    447 _rmt_read(fildes, buf, nbyte)
    448 	int fildes;
    449 	char *buf;
    450 	unsigned int nbyte;
    451 {
    452 	int rc, i;
    453 	char buffer[BUFMAGIC];
    454 
    455 	_DIAGASSERT(buf != NULL);
    456 
    457 	(void)snprintf(buffer, sizeof buffer, "R%d\n", nbyte);
    458 	if (command(fildes, buffer) == -1 || (rc = status(fildes)) == -1)
    459 		return(-1);
    460 
    461 	for (i = 0; i < rc; i += nbyte, buf += nbyte)
    462 	{
    463 		nbyte = read(READ(fildes), buf, rc);
    464 		if (nbyte <= 0)
    465 		{
    466 			rmtabort(fildes);
    467 			errno = EIO;
    468 			return(-1);
    469 		}
    470 	}
    471 
    472 	return(rc);
    473 }
    474 
    475 
    476 
    477 /*
    478  *	_rmt_write --- write a buffer to the remote tape
    479  */
    480 
    481 static int
    482 _rmt_write(fildes, buf, nbyte)
    483 	int fildes;
    484 	const void *buf;
    485 	unsigned int nbyte;
    486 {
    487 	char buffer[BUFMAGIC];
    488 	void (*pstat) __P((int));
    489 
    490 	_DIAGASSERT(buf != NULL);
    491 
    492 	(void)snprintf(buffer, sizeof buffer, "W%d\n", nbyte);
    493 	if (command(fildes, buffer) == -1)
    494 		return(-1);
    495 
    496 	pstat = signal(SIGPIPE, SIG_IGN);
    497 	if (write(WRITE(fildes), buf, nbyte) == nbyte)
    498 	{
    499 		signal (SIGPIPE, pstat);
    500 		return(status(fildes));
    501 	}
    502 
    503 	signal (SIGPIPE, pstat);
    504 	rmtabort(fildes);
    505 	errno = EIO;
    506 	return(-1);
    507 }
    508 
    509 
    510 
    511 /*
    512  *	_rmt_lseek --- perform an imitation lseek operation remotely
    513  */
    514 
    515 static off_t
    516 _rmt_lseek(fildes, offset, whence)
    517 	int fildes;
    518 	off_t offset;
    519 	int whence;
    520 {
    521 	char buffer[BUFMAGIC];
    522 
    523 	(void)snprintf(buffer, sizeof buffer, "L%qd\n%d\n", (long long)offset,
    524 	    whence);
    525 	if (command(fildes, buffer) == -1)
    526 		return(-1);
    527 
    528 	return(status(fildes));
    529 }
    530 
    531 
    532 /*
    533  *	_rmt_ioctl --- perform raw tape operations remotely
    534  */
    535 
    536 #ifdef RMTIOCTL
    537 static int
    538 _rmt_ioctl(fildes, op, arg)
    539 	int fildes;
    540 	unsigned long op;
    541 	char *arg;
    542 {
    543 	char c;
    544 	int rc, cnt;
    545 	char buffer[BUFMAGIC];
    546 
    547 	_DIAGASSERT(arg != NULL);
    548 
    549 /*
    550  *	MTIOCOP is the easy one. nothing is transfered in binary
    551  */
    552 
    553 	if (op == MTIOCTOP)
    554 	{
    555 		(void)snprintf(buffer, sizeof buffer, "I%d\n%d\n",
    556 		    ((struct mtop *)arg)->mt_op,
    557 		    ((struct mtop *)arg)->mt_count);
    558 		if (command(fildes, buffer) == -1)
    559 			return(-1);
    560 		return(status(fildes));
    561 	}
    562 
    563 /*
    564  *	we can only handle 2 ops, if not the other one, punt
    565  */
    566 
    567 	if (op != MTIOCGET)
    568 	{
    569 		errno = EINVAL;
    570 		return(-1);
    571 	}
    572 
    573 /*
    574  *	grab the status and read it directly into the structure
    575  *	this assumes that the status buffer is (hopefully) not
    576  *	padded and that 2 shorts fit in a long without any word
    577  *	alignment problems, ie - the whole struct is contiguous
    578  *	NOTE - this is probably NOT a good assumption.
    579  */
    580 
    581 	if (command(fildes, "S") == -1 || (rc = status(fildes)) == -1)
    582 		return(-1);
    583 
    584 	for (; rc > 0; rc -= cnt, arg += cnt)
    585 	{
    586 		cnt = read(READ(fildes), arg, rc);
    587 		if (cnt <= 0)
    588 		{
    589 			rmtabort(fildes);
    590 			errno = EIO;
    591 			return(-1);
    592 		}
    593 	}
    594 
    595 /*
    596  *	now we check for byte position. mt_type is a small integer field
    597  *	(normally) so we will check its magnitude. if it is larger than
    598  *	256, we will assume that the bytes are swapped and go through
    599  *	and reverse all the bytes
    600  */
    601 
    602 	if (((struct mtget *) arg)->mt_type < 256)
    603 		return(0);
    604 
    605 	for (cnt = 0; cnt < rc; cnt += 2)
    606 	{
    607 		c = arg[cnt];
    608 		arg[cnt] = arg[cnt+1];
    609 		arg[cnt+1] = c;
    610 	}
    611 
    612 	return(0);
    613   }
    614 #endif /* RMTIOCTL */
    615 
    616 /*
    617  *	Added routines to replace open(), close(), lseek(), ioctl(), etc.
    618  *	The preprocessor can be used to remap these the rmtopen(), etc
    619  *	thus minimizing source changes:
    620  *
    621  *		#ifdef <something>
    622  *		#  define access rmtaccess
    623  *		#  define close rmtclose
    624  *		#  define creat rmtcreat
    625  *		#  define dup rmtdup
    626  *		#  define fcntl rmtfcntl
    627  *		#  define fstat rmtfstat
    628  *		#  define ioctl rmtioctl
    629  *		#  define isatty rmtisatty
    630  *		#  define lseek rmtlseek
    631  *		#  define lstat rmtlstat
    632  *		#  define open rmtopen
    633  *		#  define read rmtread
    634  *		#  define stat rmtstat
    635  *		#  define write rmtwrite
    636  *		#endif
    637  *
    638  *	-- Fred Fish
    639  *
    640  *	ADR --- I set up a <rmt.h> include file for this
    641  *
    642  */
    643 
    644 /*
    645  *	Note that local vs remote file descriptors are distinquished
    646  *	by adding a bias to the remote descriptors.  This is a quick
    647  *	and dirty trick that may not be portable to some systems.
    648  */
    649 
    650 #define REM_BIAS 128
    651 
    652 
    653 /*
    654  *	Test pathname to see if it is local or remote.  A remote device
    655  *	is any string that contains ":/dev/".  Returns 1 if remote,
    656  *	0 otherwise.
    657  */
    658 
    659 static int
    660 remdev(path)
    661 	const char *path;
    662 {
    663 
    664 	_DIAGASSERT(path != NULL);
    665 
    666 	if ((path = strchr (path, ':')) != NULL)
    667 	{
    668 		if (strncmp (path + 1, "/dev/", 5) == 0)
    669 		{
    670 			return (1);
    671 		}
    672 	}
    673 	return (0);
    674 }
    675 
    676 
    677 /*
    678  *	Open a local or remote file.  Looks just like open(2) to
    679  *	caller.
    680  */
    681 
    682 int
    683 #ifdef __STDC__
    684 rmtopen(const char *path, int oflag, ...)
    685 #else
    686 rmtopen(va_alist)
    687 	va_decl
    688 #endif
    689 {
    690 	mode_t mode;
    691 	int fd;
    692 	va_list ap;
    693 #if __STDC__
    694 	va_start(ap, oflag);
    695 #else
    696 	const char *path;
    697 	int oflag;
    698 
    699 	va_start(ap);
    700 	path = va_arg(ap, const char *);
    701 	oflag = va_arg(ap, int);
    702 #endif
    703 
    704 	mode = va_arg(ap, mode_t);
    705 	va_end(ap);
    706 
    707 	_DIAGASSERT(path != NULL);
    708 #ifdef _DIAGNOSTIC
    709 	if (path == NULL || *path == '\0') {
    710 		errno = ENOENT;
    711 		return (-1);
    712 	}
    713 #endif
    714 
    715 	if (remdev (path))
    716 	{
    717 		fd = _rmt_open (path, oflag, mode);
    718 
    719 		return (fd == -1) ? -1 : (fd + REM_BIAS);
    720 	}
    721 	else
    722 	{
    723 		return (open (path, oflag, mode));
    724 	}
    725 }
    726 
    727 /*
    728  *	Test pathname for specified access.  Looks just like access(2)
    729  *	to caller.
    730  */
    731 
    732 int
    733 rmtaccess(path, amode)
    734 	const char *path;
    735 	int amode;
    736 {
    737 
    738 	_DIAGASSERT(path != NULL);
    739 #ifdef _DIAGNOSTIC
    740 	if (path == NULL || *path == '\0') {
    741 		errno = ENOENT;
    742 		return (-1);
    743 	}
    744 #endif
    745 
    746 	if (remdev (path))
    747 	{
    748 		return (0);		/* Let /etc/rmt find out */
    749 	}
    750 	else
    751 	{
    752 		return (access (path, amode));
    753 	}
    754 }
    755 
    756 
    757 /*
    758  *	Isrmt. Let a programmer know he has a remote device.
    759  */
    760 
    761 int
    762 isrmt(fd)
    763 	int fd;
    764 {
    765 	return (fd >= REM_BIAS);
    766 }
    767 
    768 
    769 /*
    770  *	Read from stream.  Looks just like read(2) to caller.
    771  */
    772 
    773 ssize_t
    774 rmtread(fildes, buf, nbyte)
    775 	int fildes;
    776 	void *buf;
    777 	size_t nbyte;
    778 {
    779 
    780 	_DIAGASSERT(buf != NULL);
    781 #ifdef _DIAGNOSTIC
    782 	if (buf == NULL) {
    783 		errno = EFAULT;
    784 		return (-1);
    785 	}
    786 #endif
    787 
    788 	if (isrmt (fildes))
    789 	{
    790 		return (_rmt_read (fildes - REM_BIAS, buf, nbyte));
    791 	}
    792 	else
    793 	{
    794 		return (read (fildes, buf, nbyte));
    795 	}
    796 }
    797 
    798 
    799 /*
    800  *	Write to stream.  Looks just like write(2) to caller.
    801  */
    802 
    803 ssize_t
    804 rmtwrite(fildes, buf, nbyte)
    805 	int fildes;
    806 	const void *buf;
    807 	size_t nbyte;
    808 {
    809 
    810 	_DIAGASSERT(buf != NULL);
    811 #ifdef _DIAGNOSTIC
    812 	if (buf == NULL) {
    813 		errno = EFAULT;
    814 		return (-1);
    815 	}
    816 #endif
    817 
    818 	if (isrmt (fildes))
    819 	{
    820 		return (_rmt_write (fildes - REM_BIAS, buf, nbyte));
    821 	}
    822 	else
    823 	{
    824 		return (write (fildes, buf, nbyte));
    825 	}
    826 }
    827 
    828 /*
    829  *	Perform lseek on file.  Looks just like lseek(2) to caller.
    830  */
    831 
    832 off_t
    833 rmtlseek(fildes, offset, whence)
    834 	int fildes;
    835 	off_t offset;
    836 	int whence;
    837 {
    838 	if (isrmt (fildes))
    839 	{
    840 		return (_rmt_lseek (fildes - REM_BIAS, offset, whence));
    841 	}
    842 	else
    843 	{
    844 		return (lseek (fildes, offset, whence));
    845 	}
    846 }
    847 
    848 
    849 /*
    850  *	Close a file.  Looks just like close(2) to caller.
    851  */
    852 
    853 int
    854 rmtclose(fildes)
    855 	int fildes;
    856 {
    857 	if (isrmt (fildes))
    858 	{
    859 		return (_rmt_close (fildes - REM_BIAS));
    860 	}
    861 	else
    862 	{
    863 		return (close (fildes));
    864 	}
    865 }
    866 
    867 /*
    868  *	Do ioctl on file.  Looks just like ioctl(2) to caller.
    869  */
    870 
    871 int
    872 #ifdef __STDC__
    873 rmtioctl(int fildes, unsigned long request, ...)
    874 #else
    875 rmtioctl(va_alist)
    876 	va_decl
    877 #endif
    878 {
    879 	char *arg;
    880 	va_list ap;
    881 #if __STDC__
    882 	va_start(ap, request);
    883 #else
    884 	int fildes;
    885 	unsigned long request;
    886 
    887 	va_start(ap);
    888 	filedes = va_arg(ap, int);
    889 	request = va_arg(ap, unsigned long);
    890 #endif
    891 
    892 	arg = va_arg(ap, char *);
    893 	va_end(ap);
    894 
    895 	/* XXX: arg may be NULL ? */
    896 
    897 	if (isrmt (fildes))
    898 	{
    899 #ifdef RMTIOCTL
    900 		return (_rmt_ioctl (fildes - REM_BIAS, request, arg));
    901 #else
    902 		errno = EOPNOTSUPP;
    903 		return (-1);		/* For now  (fnf) */
    904 #endif
    905 	}
    906 	else
    907 	{
    908 		return (ioctl (fildes, request, arg));
    909 	}
    910 }
    911 
    912 
    913 /*
    914  *	Duplicate an open file descriptor.  Looks just like dup(2)
    915  *	to caller.
    916  */
    917 
    918 int
    919 rmtdup(fildes)
    920 	int fildes;
    921 {
    922 	if (isrmt (fildes))
    923 	{
    924 		errno = EOPNOTSUPP;
    925 		return (-1);		/* For now (fnf) */
    926 	}
    927 	else
    928 	{
    929 		return (dup (fildes));
    930 	}
    931 }
    932 
    933 /*
    934  *	Get file status.  Looks just like fstat(2) to caller.
    935  */
    936 
    937 int
    938 rmtfstat(fildes, buf)
    939 	int fildes;
    940 	struct stat *buf;
    941 {
    942 
    943 	_DIAGASSERT(buf != NULL);
    944 #ifdef _DIAGNOSTIC
    945 	if (buf == NULL) {
    946 		errno = EFAULT;
    947 		return (-1);
    948 	}
    949 #endif
    950 
    951 	if (isrmt (fildes))
    952 	{
    953 		errno = EOPNOTSUPP;
    954 		return (-1);		/* For now (fnf) */
    955 	}
    956 	else
    957 	{
    958 		return (fstat (fildes, buf));
    959 	}
    960 }
    961 
    962 
    963 /*
    964  *	Get file status.  Looks just like stat(2) to caller.
    965  */
    966 
    967 int
    968 rmtstat(path, buf)
    969 	const char *path;
    970 	struct stat *buf;
    971 {
    972 
    973 	_DIAGASSERT(path != NULL);
    974 	_DIAGASSERT(buf != NULL);
    975 #ifdef _DIAGNOSTIC
    976 	if (path == NULL || *path == '\0') {
    977 		errno = ENOENT;
    978 		return (-1);
    979 	}
    980 	if (buf == NULL) {
    981 		errno = EFAULT;
    982 		return (-1);
    983 	}
    984 #endif
    985 
    986 	if (remdev (path))
    987 	{
    988 		errno = EOPNOTSUPP;
    989 		return (-1);		/* For now (fnf) */
    990 	}
    991 	else
    992 	{
    993 		return (stat (path, buf));
    994 	}
    995 }
    996 
    997 
    998 
    999 /*
   1000  *	Create a file from scratch.  Looks just like creat(2) to the caller.
   1001  */
   1002 
   1003 int
   1004 rmtcreat(path, mode)
   1005 	const char *path;
   1006 	mode_t mode;
   1007 {
   1008 
   1009 	_DIAGASSERT(path != NULL);
   1010 #ifdef _DIAGNOSTIC
   1011 	if (path == NULL || *path == '\0') {
   1012 		errno = ENOENT;
   1013 		return (-1);
   1014 	}
   1015 #endif
   1016 
   1017 	if (remdev (path))
   1018 	{
   1019 		return (rmtopen (path, 1 | O_CREAT, mode));
   1020 	}
   1021 	else
   1022 	{
   1023 		return (creat (path, mode));
   1024 	}
   1025 }
   1026 
   1027 /*
   1028  *	Rmtfcntl. Do a remote fcntl operation.
   1029  */
   1030 
   1031 int
   1032 #ifdef __STDC__
   1033 rmtfcntl(int fd, int cmd, ...)
   1034 #else
   1035 rmtfcntl(va_alist)
   1036 	va_decl
   1037 #endif
   1038 {
   1039 	void *arg;
   1040 	va_list ap;
   1041 #if __STDC__
   1042 	va_start(ap, cmd);
   1043 #else
   1044 	int fd, cmd;
   1045 
   1046 	va_start(ap);
   1047 	fd = va_arg(ap, int);
   1048 	cmd = va_arg(ap, int);
   1049 #endif
   1050 
   1051 	arg = va_arg(ap, void *);
   1052 	va_end(ap);
   1053 
   1054 	/* XXX: arg may be NULL ? */
   1055 
   1056 	if (isrmt (fd))
   1057 	{
   1058 		errno = EOPNOTSUPP;
   1059 		return (-1);
   1060 	}
   1061 	else
   1062 	{
   1063 		return (fcntl (fd, cmd, arg));
   1064 	}
   1065 }
   1066 
   1067 /*
   1068  *	Rmtisatty.  Do the isatty function.
   1069  */
   1070 
   1071 int
   1072 rmtisatty(fd)
   1073 	int fd;
   1074 {
   1075 	if (isrmt (fd))
   1076 		return (0);
   1077 	else
   1078 		return (isatty (fd));
   1079 }
   1080 
   1081 
   1082 /*
   1083  *	Get file status, even if symlink.  Looks just like lstat(2) to caller.
   1084  */
   1085 
   1086 int
   1087 rmtlstat(path, buf)
   1088 	const char *path;
   1089 	struct stat *buf;
   1090 {
   1091 
   1092 	_DIAGASSERT(path != NULL);
   1093 	_DIAGASSERT(buf != NULL);
   1094 #ifdef _DIAGNOSTIC
   1095 	if (path == NULL || *path == '\0') {
   1096 		errno = ENOENT;
   1097 		return (-1);
   1098 	}
   1099 	if (buf == NULL) {
   1100 		errno = EFAULT;
   1101 		return (-1);
   1102 	}
   1103 #endif
   1104 
   1105 	if (remdev (path))
   1106 	{
   1107 		errno = EOPNOTSUPP;
   1108 		return (-1);		/* For now (fnf) */
   1109 	}
   1110 	else
   1111 	{
   1112 		return (lstat (path, buf));
   1113 	}
   1114 }
   1115