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