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