Home | History | Annotate | Line # | Download | only in librmt
rmtlib.c revision 1.1
      1 #ifndef lint
      2 static char *RCSid = "$Header: /tank/opengrok/rsync2/NetBSD/src/lib/librmt/rmtlib.c,v 1.1 1996/08/09 03:35:19 jtc Exp $";
      3 #endif
      4 
      5 /*
      6  * $Log: rmtlib.c,v $
      7  * Revision 1.1  1996/08/09 03:35:19  jtc
      8  * Remote mag tape library from volume 18 of comp.sources.unix.
      9  *
     10  * Revision 1.7  89/03/23  14:09:51  root
     11  * Fix from haynes (at) ucscc.ucsc.edu for use w/compat. ADR.
     12  *
     13  * Revision 1.6  88/10/25  17:04:29  root
     14  * rexec code and a bug fix from srs!dan, miscellanious cleanup. ADR.
     15  *
     16  * Revision 1.5  88/10/25  16:30:17  root
     17  * Fix from jeff (at) gatech.edu for getting user@host:dev right. ADR.
     18  *
     19  * Revision 1.4  87/10/30  10:36:12  root
     20  * Made 4.2 syntax a compile time option. ADR.
     21  *
     22  * Revision 1.3  87/04/22  11:16:48  root
     23  * Two fixes from parmelee (at) wayback.cs.cornell.edu to correctly
     24  * do fd biasing and rmt protocol on 'S' command. ADR.
     25  *
     26  * Revision 1.2  86/10/09  16:38:53  root
     27  * Changed to reflect 4.3BSD rcp syntax. ADR.
     28  *
     29  * Revision 1.1  86/10/09  16:17:35  root
     30  * Initial revision
     31  *
     32  */
     33 
     34 /*
     35  *	rmt --- remote tape emulator subroutines
     36  *
     37  *	Originally written by Jeff Lee, modified some by Arnold Robbins
     38  *
     39  *	WARNING:  The man page rmt(8) for /etc/rmt documents the remote mag
     40  *	tape protocol which rdump and rrestore use.  Unfortunately, the man
     41  *	page is *WRONG*.  The author of the routines I'm including originally
     42  *	wrote his code just based on the man page, and it didn't work, so he
     43  *	went to the rdump source to figure out why.  The only thing he had to
     44  *	change was to check for the 'F' return code in addition to the 'E',
     45  *	and to separate the various arguments with \n instead of a space.  I
     46  *	personally don't think that this is much of a problem, but I wanted to
     47  *	point it out.
     48  *	-- Arnold Robbins
     49  *
     50  *	Redone as a library that can replace open, read, write, etc, by
     51  *	Fred Fish, with some additional work by Arnold Robbins.
     52  */
     53 
     54 /*
     55  *	MAXUNIT --- Maximum number of remote tape file units
     56  *
     57  *	READ --- Return the number of the read side file descriptor
     58  *	WRITE --- Return the number of the write side file descriptor
     59  */
     60 
     61 #define RMTIOCTL	1
     62 /* #define USE_REXEC	1	/* rexec code courtesy of Dan Kegel, srs!dan */
     63 
     64 #include <stdio.h>
     65 #include <signal.h>
     66 #include <sys/types.h>
     67 
     68 #ifdef RMTIOCTL
     69 #include <sys/ioctl.h>
     70 #include <sys/mtio.h>
     71 #endif
     72 
     73 #ifdef USE_REXEC
     74 #include <netdb.h>
     75 #endif
     76 
     77 #include <errno.h>
     78 #include <setjmp.h>
     79 #include <sys/stat.h>
     80 
     81 #define BUFMAGIC	64	/* a magic number for buffer sizes */
     82 #define MAXUNIT	4
     83 
     84 #define READ(fd)	(Ctp[fd][0])
     85 #define WRITE(fd)	(Ptc[fd][1])
     86 
     87 static int Ctp[MAXUNIT][2] = { -1, -1, -1, -1, -1, -1, -1, -1 };
     88 static int Ptc[MAXUNIT][2] = { -1, -1, -1, -1, -1, -1, -1, -1 };
     89 
     90 static jmp_buf Jmpbuf;
     91 extern int errno;
     92 
     93 /*
     94  *	abort --- close off a remote tape connection
     95  */
     96 
     97 static void abort(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 command(fildes, buf)
    113 int fildes;
    114 char *buf;
    115 {
    116 	register int blen;
    117 	int (*pstat)();
    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 	abort(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 status(fildes)
    149 int fildes;
    150 {
    151 	int i;
    152 	char c, *cp;
    153 	char buffer[BUFMAGIC];
    154 
    155 /*
    156  *	read the reply command line
    157  */
    158 
    159 	for (i = 0, cp = buffer; i < BUFMAGIC; i++, cp++)
    160 	{
    161 		if (read(READ(fildes), cp, 1) != 1)
    162 		{
    163 			abort(fildes);
    164 			errno = EIO;
    165 			return(-1);
    166 		}
    167 		if (*cp == '\n')
    168 		{
    169 			*cp = 0;
    170 			break;
    171 		}
    172 	}
    173 
    174 	if (i == BUFMAGIC)
    175 	{
    176 		abort(fildes);
    177 		errno = EIO;
    178 		return(-1);
    179 	}
    180 
    181 /*
    182  *	check the return status
    183  */
    184 
    185 	for (cp = buffer; *cp; cp++)
    186 		if (*cp != ' ')
    187 			break;
    188 
    189 	if (*cp == 'E' || *cp == 'F')
    190 	{
    191 		errno = atoi(cp + 1);
    192 		while (read(READ(fildes), &c, 1) == 1)
    193 			if (c == '\n')
    194 				break;
    195 
    196 		if (*cp == 'F')
    197 			abort(fildes);
    198 
    199 		return(-1);
    200 	}
    201 
    202 /*
    203  *	check for mis-synced pipes
    204  */
    205 
    206 	if (*cp != 'A')
    207 	{
    208 		abort(fildes);
    209 		errno = EIO;
    210 		return(-1);
    211 	}
    212 
    213 	return(atoi(cp + 1));
    214 }
    215 
    216 #ifdef USE_REXEC
    217 
    218 /*
    219  * _rmt_rexec
    220  *
    221  * execute /etc/rmt on a remote system using rexec().
    222  * Return file descriptor of bidirectional socket for stdin and stdout
    223  * If username is NULL, or an empty string, uses current username.
    224  *
    225  * ADR: By default, this code is not used, since it requires that
    226  * the user have a .netrc file in his/her home directory, or that the
    227  * application designer be willing to have rexec prompt for login and
    228  * password info. This may be unacceptable, and .rhosts files for use
    229  * with rsh are much more common on BSD systems.
    230  */
    231 
    232 static int
    233 _rmt_rexec(host, user)
    234 char *host;
    235 char *user;		/* may be NULL */
    236 {
    237 	struct servent *rexecserv;
    238 
    239 	rexecserv = getservbyname("exec", "tcp");
    240 	if (NULL == rexecserv) {
    241 		fprintf (stderr, "? exec/tcp: service not available.");
    242 		exit (-1);
    243 	}
    244 	if ((user != NULL) && *user == '\0')
    245 		user = (char *) NULL;
    246 	return rexec (&host, rexecserv->s_port, user, NULL,
    247 			"/etc/rmt", (int *)NULL);
    248 }
    249 #endif /* USE_REXEC */
    250 
    251 /*
    252  *	_rmt_open --- open a magtape device on system specified, as given user
    253  *
    254  *	file name has the form [user@]system:/dev/????
    255 #ifdef COMPAT
    256  *	file name has the form system[.user]:/dev/????
    257 #endif
    258  */
    259 
    260 #define MAXHOSTLEN	257	/* BSD allows very long host names... */
    261 
    262 static int _rmt_open (path, oflag, mode)
    263 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) strcpy (user, system);	/* saw user part of user@host */
    311 		sys = system;			/* start over */
    312 		while (*path != ':') {
    313 			*sys++ = *path++;
    314 		}
    315 		*sys = '\0';
    316 		path++;
    317 	}
    318 #ifdef COMPAT
    319 	else if (*(path - 1) == '.')
    320 	{
    321 		while (*path != ':') {
    322 			*user++ = *path++;
    323 		}
    324 		*user = '\0';
    325 		path++;
    326 	}
    327 #endif
    328 	else
    329 		*user = '\0';
    330 
    331 	while (*path) {
    332 		*dev++ = *path++;
    333 	}
    334 	*dev = '\0';
    335 
    336 #ifdef USE_REXEC
    337 /*
    338  *	Execute the remote command using rexec
    339  */
    340 	READ(i) = WRITE(i) = _rmt_rexec(system, login);
    341 	if (READ(i) < 0)
    342 		return -1;
    343 #else
    344 /*
    345  *	setup the pipes for the 'rsh' command and fork
    346  */
    347 
    348 	if (pipe(Ptc[i]) == -1 || pipe(Ctp[i]) == -1)
    349 		return(-1);
    350 
    351 	if ((rc = fork()) == -1)
    352 		return(-1);
    353 
    354 	if (rc == 0)
    355 	{
    356 		close(0);
    357 		dup(Ptc[i][0]);
    358 		close(Ptc[i][0]); close(Ptc[i][1]);
    359 		close(1);
    360 		dup(Ctp[i][1]);
    361 		close(Ctp[i][0]); close(Ctp[i][1]);
    362 		(void) setuid (getuid ());
    363 		(void) setgid (getgid ());
    364 		if (*login)
    365 		{
    366 			execl("/usr/ucb/rsh", "rsh", system, "-l", login,
    367 				"/etc/rmt", (char *) 0);
    368 			execl("/usr/bin/remsh", "remsh", system, "-l", login,
    369 				"/etc/rmt", (char *) 0);
    370 		}
    371 		else
    372 		{
    373 			execl("/usr/ucb/rsh", "rsh", system,
    374 				"/etc/rmt", (char *) 0);
    375 			execl("/usr/bin/remsh", "remsh", system,
    376 				"/etc/rmt", (char *) 0);
    377 		}
    378 
    379 /*
    380  *	bad problems if we get here
    381  */
    382 
    383 		perror("exec");
    384 		exit(1);
    385 	}
    386 
    387 	close(Ptc[i][0]); close(Ctp[i][1]);
    388 #endif
    389 
    390 /*
    391  *	now attempt to open the tape device
    392  */
    393 
    394 	sprintf(buffer, "O%s\n%d\n", device, oflag);
    395 	if (command(i, buffer) == -1 || status(i) == -1)
    396 		return(-1);
    397 
    398 	return(i);
    399 }
    400 
    401 
    402 
    403 /*
    404  *	_rmt_close --- close a remote magtape unit and shut down
    405  */
    406 
    407 static int _rmt_close(fildes)
    408 int fildes;
    409 {
    410 	int rc;
    411 
    412 	if (command(fildes, "C\n") != -1)
    413 	{
    414 		rc = status(fildes);
    415 
    416 		abort(fildes);
    417 		return(rc);
    418 	}
    419 
    420 	return(-1);
    421 }
    422 
    423 
    424 
    425 /*
    426  *	_rmt_read --- read a buffer from a remote tape
    427  */
    428 
    429 static int _rmt_read(fildes, buf, nbyte)
    430 int fildes;
    431 char *buf;
    432 unsigned int nbyte;
    433 {
    434 	int rc, i;
    435 	char buffer[BUFMAGIC];
    436 
    437 	sprintf(buffer, "R%d\n", nbyte);
    438 	if (command(fildes, buffer) == -1 || (rc = status(fildes)) == -1)
    439 		return(-1);
    440 
    441 	for (i = 0; i < rc; i += nbyte, buf += nbyte)
    442 	{
    443 		nbyte = read(READ(fildes), buf, rc);
    444 		if (nbyte <= 0)
    445 		{
    446 			abort(fildes);
    447 			errno = EIO;
    448 			return(-1);
    449 		}
    450 	}
    451 
    452 	return(rc);
    453 }
    454 
    455 
    456 
    457 /*
    458  *	_rmt_write --- write a buffer to the remote tape
    459  */
    460 
    461 static int _rmt_write(fildes, buf, nbyte)
    462 int fildes;
    463 char *buf;
    464 unsigned int nbyte;
    465 {
    466 	int rc;
    467 	char buffer[BUFMAGIC];
    468 	int (*pstat)();
    469 
    470 	sprintf(buffer, "W%d\n", nbyte);
    471 	if (command(fildes, buffer) == -1)
    472 		return(-1);
    473 
    474 	pstat = signal(SIGPIPE, SIG_IGN);
    475 	if (write(WRITE(fildes), buf, nbyte) == nbyte)
    476 	{
    477 		signal (SIGPIPE, pstat);
    478 		return(status(fildes));
    479 	}
    480 
    481 	signal (SIGPIPE, pstat);
    482 	abort(fildes);
    483 	errno = EIO;
    484 	return(-1);
    485 }
    486 
    487 
    488 
    489 /*
    490  *	_rmt_lseek --- perform an imitation lseek operation remotely
    491  */
    492 
    493 static long _rmt_lseek(fildes, offset, whence)
    494 int fildes;
    495 long offset;
    496 int whence;
    497 {
    498 	char buffer[BUFMAGIC];
    499 
    500 	sprintf(buffer, "L%d\n%d\n", offset, whence);
    501 	if (command(fildes, buffer) == -1)
    502 		return(-1);
    503 
    504 	return(status(fildes));
    505 }
    506 
    507 
    508 /*
    509  *	_rmt_ioctl --- perform raw tape operations remotely
    510  */
    511 
    512 #ifdef RMTIOCTL
    513 static _rmt_ioctl(fildes, op, arg)
    514 int fildes, op;
    515 char *arg;
    516 {
    517 	char c;
    518 	int rc, cnt;
    519 	char buffer[BUFMAGIC];
    520 
    521 /*
    522  *	MTIOCOP is the easy one. nothing is transfered in binary
    523  */
    524 
    525 	if (op == MTIOCTOP)
    526 	{
    527 		sprintf(buffer, "I%d\n%d\n", ((struct mtop *) arg)->mt_op,
    528 			((struct mtop *) arg)->mt_count);
    529 		if (command(fildes, buffer) == -1)
    530 			return(-1);
    531 		return(status(fildes));
    532 	}
    533 
    534 /*
    535  *	we can only handle 2 ops, if not the other one, punt
    536  */
    537 
    538 	if (op != MTIOCGET)
    539 	{
    540 		errno = EINVAL;
    541 		return(-1);
    542 	}
    543 
    544 /*
    545  *	grab the status and read it directly into the structure
    546  *	this assumes that the status buffer is (hopefully) not
    547  *	padded and that 2 shorts fit in a long without any word
    548  *	alignment problems, ie - the whole struct is contiguous
    549  *	NOTE - this is probably NOT a good assumption.
    550  */
    551 
    552 	if (command(fildes, "S") == -1 || (rc = status(fildes)) == -1)
    553 		return(-1);
    554 
    555 	for (; rc > 0; rc -= cnt, arg += cnt)
    556 	{
    557 		cnt = read(READ(fildes), arg, rc);
    558 		if (cnt <= 0)
    559 		{
    560 			abort(fildes);
    561 			errno = EIO;
    562 			return(-1);
    563 		}
    564 	}
    565 
    566 /*
    567  *	now we check for byte position. mt_type is a small integer field
    568  *	(normally) so we will check its magnitude. if it is larger than
    569  *	256, we will assume that the bytes are swapped and go through
    570  *	and reverse all the bytes
    571  */
    572 
    573 	if (((struct mtget *) arg)->mt_type < 256)
    574 		return(0);
    575 
    576 	for (cnt = 0; cnt < rc; cnt += 2)
    577 	{
    578 		c = arg[cnt];
    579 		arg[cnt] = arg[cnt+1];
    580 		arg[cnt+1] = c;
    581 	}
    582 
    583 	return(0);
    584   }
    585 #endif /* RMTIOCTL */
    586 
    587 /*
    588  *	Added routines to replace open(), close(), lseek(), ioctl(), etc.
    589  *	The preprocessor can be used to remap these the rmtopen(), etc
    590  *	thus minimizing source changes:
    591  *
    592  *		#ifdef <something>
    593  *		#  define access rmtaccess
    594  *		#  define close rmtclose
    595  *		#  define creat rmtcreat
    596  *		#  define dup rmtdup
    597  *		#  define fcntl rmtfcntl
    598  *		#  define fstat rmtfstat
    599  *		#  define ioctl rmtioctl
    600  *		#  define isatty rmtisatty
    601  *		#  define lseek rmtlseek
    602  *		#  define lstat rmtlstat
    603  *		#  define open rmtopen
    604  *		#  define read rmtread
    605  *		#  define stat rmtstat
    606  *		#  define write rmtwrite
    607  *		#endif
    608  *
    609  *	-- Fred Fish
    610  *
    611  *	ADR --- I set up a <rmt.h> include file for this
    612  *
    613  */
    614 
    615 /*
    616  *	Note that local vs remote file descriptors are distinquished
    617  *	by adding a bias to the remote descriptors.  This is a quick
    618  *	and dirty trick that may not be portable to some systems.
    619  */
    620 
    621 #define REM_BIAS 128
    622 
    623 
    624 /*
    625  *	Test pathname to see if it is local or remote.  A remote device
    626  *	is any string that contains ":/dev/".  Returns 1 if remote,
    627  *	0 otherwise.
    628  */
    629 
    630 static int remdev (path)
    631 register char *path;
    632 {
    633 #define strchr	index
    634 	extern char *strchr ();
    635 
    636 	if ((path = strchr (path, ':')) != NULL)
    637 	{
    638 		if (strncmp (path + 1, "/dev/", 5) == 0)
    639 		{
    640 			return (1);
    641 		}
    642 	}
    643 	return (0);
    644 }
    645 
    646 
    647 /*
    648  *	Open a local or remote file.  Looks just like open(2) to
    649  *	caller.
    650  */
    651 
    652 int rmtopen (path, oflag, mode)
    653 char *path;
    654 int oflag;
    655 int mode;
    656 {
    657 	int fd;
    658 
    659 	if (remdev (path))
    660 	{
    661 		fd = _rmt_open (path, oflag, mode);
    662 
    663 		return (fd == -1) ? -1 : (fd + REM_BIAS);
    664 	}
    665 	else
    666 	{
    667 		return (open (path, oflag, mode));
    668 	}
    669 }
    670 
    671 /*
    672  *	Test pathname for specified access.  Looks just like access(2)
    673  *	to caller.
    674  */
    675 
    676 int rmtaccess (path, amode)
    677 char *path;
    678 int amode;
    679 {
    680 	if (remdev (path))
    681 	{
    682 		return (0);		/* Let /etc/rmt find out */
    683 	}
    684 	else
    685 	{
    686 		return (access (path, amode));
    687 	}
    688 }
    689 
    690 
    691 /*
    692  *	Read from stream.  Looks just like read(2) to caller.
    693  */
    694 
    695 int rmtread (fildes, buf, nbyte)
    696 int fildes;
    697 char *buf;
    698 unsigned int nbyte;
    699 {
    700 	if (isrmt (fildes))
    701 	{
    702 		return (_rmt_read (fildes - REM_BIAS, buf, nbyte));
    703 	}
    704 	else
    705 	{
    706 		return (read (fildes, buf, nbyte));
    707 	}
    708 }
    709 
    710 
    711 /*
    712  *	Write to stream.  Looks just like write(2) to caller.
    713  */
    714 
    715 int rmtwrite (fildes, buf, nbyte)
    716 int fildes;
    717 char *buf;
    718 unsigned int nbyte;
    719 {
    720 	if (isrmt (fildes))
    721 	{
    722 		return (_rmt_write (fildes - REM_BIAS, buf, nbyte));
    723 	}
    724 	else
    725 	{
    726 		return (write (fildes, buf, nbyte));
    727 	}
    728 }
    729 
    730 /*
    731  *	Perform lseek on file.  Looks just like lseek(2) to caller.
    732  */
    733 
    734 long rmtlseek (fildes, offset, whence)
    735 int fildes;
    736 long offset;
    737 int whence;
    738 {
    739 	if (isrmt (fildes))
    740 	{
    741 		return (_rmt_lseek (fildes - REM_BIAS, offset, whence));
    742 	}
    743 	else
    744 	{
    745 		return (lseek (fildes, offset, whence));
    746 	}
    747 }
    748 
    749 
    750 /*
    751  *	Close a file.  Looks just like close(2) to caller.
    752  */
    753 
    754 int rmtclose (fildes)
    755 int fildes;
    756 {
    757 	if (isrmt (fildes))
    758 	{
    759 		return (_rmt_close (fildes - REM_BIAS));
    760 	}
    761 	else
    762 	{
    763 		return (close (fildes));
    764 	}
    765 }
    766 
    767 /*
    768  *	Do ioctl on file.  Looks just like ioctl(2) to caller.
    769  */
    770 
    771 int rmtioctl (fildes, request, arg)
    772 int fildes;
    773 unsigned long request;
    774 char *arg;
    775 {
    776 	if (isrmt (fildes))
    777 	{
    778 #ifdef RMTIOCTL
    779 		return (_rmt_ioctl (fildes - REM_BIAS, request, arg));
    780 #else
    781 		errno = EOPNOTSUPP;
    782 		return (-1);		/* For now  (fnf) */
    783 #endif
    784 	}
    785 	else
    786 	{
    787 		return (ioctl (fildes, request, arg));
    788 	}
    789 }
    790 
    791 
    792 /*
    793  *	Duplicate an open file descriptor.  Looks just like dup(2)
    794  *	to caller.
    795  */
    796 
    797 int rmtdup (fildes)
    798 int fildes;
    799 {
    800 	if (isrmt (fildes))
    801 	{
    802 		errno = EOPNOTSUPP;
    803 		return (-1);		/* For now (fnf) */
    804 	}
    805 	else
    806 	{
    807 		return (dup (fildes));
    808 	}
    809 }
    810 
    811 /*
    812  *	Get file status.  Looks just like fstat(2) to caller.
    813  */
    814 
    815 int rmtfstat (fildes, buf)
    816 int fildes;
    817 struct stat *buf;
    818 {
    819 	if (isrmt (fildes))
    820 	{
    821 		errno = EOPNOTSUPP;
    822 		return (-1);		/* For now (fnf) */
    823 	}
    824 	else
    825 	{
    826 		return (fstat (fildes, buf));
    827 	}
    828 }
    829 
    830 
    831 /*
    832  *	Get file status.  Looks just like stat(2) to caller.
    833  */
    834 
    835 int rmtstat (path, buf)
    836 char *path;
    837 struct stat *buf;
    838 {
    839 	if (remdev (path))
    840 	{
    841 		errno = EOPNOTSUPP;
    842 		return (-1);		/* For now (fnf) */
    843 	}
    844 	else
    845 	{
    846 		return (stat (path, buf));
    847 	}
    848 }
    849 
    850 
    851 
    852 /*
    853  *	Create a file from scratch.  Looks just like creat(2) to the caller.
    854  */
    855 
    856 #include <sys/file.h>		/* BSD DEPENDANT!!! */
    857 /* #include <fcntl.h>		/* use this one for S5 with remote stuff */
    858 
    859 int rmtcreat (path, mode)
    860 char *path;
    861 int mode;
    862 {
    863 	if (remdev (path))
    864 	{
    865 		return (rmtopen (path, 1 | O_CREAT, mode));
    866 	}
    867 	else
    868 	{
    869 		return (creat (path, mode));
    870 	}
    871 }
    872 
    873 /*
    874  *	Isrmt. Let a programmer know he has a remote device.
    875  */
    876 
    877 int isrmt (fd)
    878 int fd;
    879 {
    880 	return (fd >= REM_BIAS);
    881 }
    882 
    883 /*
    884  *	Rmtfcntl. Do a remote fcntl operation.
    885  */
    886 
    887 int rmtfcntl (fd, cmd, arg)
    888 int fd, cmd, arg;
    889 {
    890 	if (isrmt (fd))
    891 	{
    892 		errno = EOPNOTSUPP;
    893 		return (-1);
    894 	}
    895 	else
    896 	{
    897 		return (fcntl (fd, cmd, arg));
    898 	}
    899 }
    900 
    901 /*
    902  *	Rmtisatty.  Do the isatty function.
    903  */
    904 
    905 int rmtisatty (fd)
    906 int fd;
    907 {
    908 	if (isrmt (fd))
    909 		return (0);
    910 	else
    911 		return (isatty (fd));
    912 }
    913 
    914 
    915 /*
    916  *	Get file status, even if symlink.  Looks just like lstat(2) to caller.
    917  */
    918 
    919 int rmtlstat (path, buf)
    920 char *path;
    921 struct stat *buf;
    922 {
    923 	if (remdev (path))
    924 	{
    925 		errno = EOPNOTSUPP;
    926 		return (-1);		/* For now (fnf) */
    927 	}
    928 	else
    929 	{
    930 		return (lstat (path, buf));
    931 	}
    932 }
    933