Diffs to tar to use a remote system's tape drive

sources-request at genrad.UUCP sources-request at genrad.UUCP
Tue Jul 9 01:35:11 AEST 1985


Mod.sources:  Volume 2, Issue 5
Submitted by: Arnold Robbins <linus!gatech!arnold>


* WARNING:  The man page rmt(8) for /etc/rmt documents the remote mag
* tape protocol which rdump and rrestore use.  Unfortunately, the man
* page is *WRONG*.  The author of the routines I'm including originally
* wrote his code just based on the man page, and it didn't work, so he
* went to the rdump source to figure out why.  The only thing he had to
* change was to check for the 'F' return code in addition to the 'E',
* and to separate the various arguments with \n instead of a space.  I
* personally don't think that this is much of a problem, but I wanted to
* point it out.
*
*   Arnold Robbins


The following context diffs to the 4.2 BSD tar.c and man page allow tar
to read and write tapes on a remote system's tape drive.  The routines
at the end that deal with /etc/rmt across a pipe are general purpose and
in the public domain.

It is interesting to note that what rmt(8) says about its protocol, and what
you actually have to do to get it to work are quite different.

The command line syntax has not changed.  Instead, the file name for the -f
option looks sorta like what rcp takes, e.g.

	$ tar -cvf gatech:/dev/rmt8 /usr/src

In particular, if "/dev/" does not follow the colon, tar decides it is using
a regular, local file.

The diffs are for the tar that comes with BRL Unix. Your line numbers
may vary.

Arnold Robbins		(rmt stuff courtesy of Jeff Lee, gatech!jeff)
arnold at gatech.{CSNET, UUCP}
--------------------- cut here ----------------------------
*** /usr/src/bin/tar.c	Wed Nov 14 00:09:23 1984
--- tar.c	Mon Jul  1 14:25:40 1985
***************
*** 115,120
  char	*getcwd();
  char	*getwd();
  
  main(argc, argv)
  int	argc;
  char	*argv[];

--- 115,141 -----
  char	*getcwd();
  char	*getwd();
  
+ int	open();
+ int	read();
+ int	write();
+ int	close();
+ int	ioctl();
+ long	lseek();
+ 
+ int	rmtopen();
+ int	rmtread();
+ int	rmtwrite();
+ int	rmtclose();
+ int	rmtioctl();
+ long	rmtlseek();
+ 
+ int	(*t_open)();
+ int	(*t_read)();
+ int	(*t_write)();
+ int	(*t_close)();
+ int	(*t_ioctl)();
+ long	(*t_lseek)();
+ 
  main(argc, argv)
  int	argc;
  char	*argv[];
***************
*** 264,269
  		    nblock);
  		done(1);
  	}
  	if (rflag) {
  		if (cflag && tfile != NULL)
  			usage();

--- 285,293 -----
  		    nblock);
  		done(1);
  	}
+ 
+ 	fix_remote();
+ 
  	if (rflag) {
  		if (cflag && tfile != NULL)
  			usage();
***************
*** 285,291
  			}
  			mt = dup(1);
  			nblock = 1;
! 		} else if ((mt = open(usefile, 2)) < 0) {
  			if (cflag == 0 || (mt =  creat(usefile, 0666)) < 0) {
  				fprintf(stderr,
  					"tar: cannot open %s\n", usefile);

--- 309,315 -----
  			}
  			mt = dup(1);
  			nblock = 1;
! 		} else if ((mt = (*t_open)(usefile, 2)) < 0) {
  			if (cflag == 0 || (mt =  creat(usefile, 0666)) < 0) {
  				fprintf(stderr,
  					"tar: cannot open %s\n", usefile);
***************
*** 298,304
  	if (strcmp(usefile, "-") == 0) {
  		mt = dup(0);
  		nblock = 1;
! 	} else if ((mt = open(usefile, 0)) < 0) {
  		fprintf(stderr, "tar: cannot open %s\n", usefile);
  		done(1);
  	}

--- 322,328 -----
  	if (strcmp(usefile, "-") == 0) {
  		mt = dup(0);
  		nblock = 1;
! 	} else if ((mt = (*t_open)(usefile, 0)) < 0) {
  		fprintf(stderr, "tar: cannot open %s\n", usefile);
  		done(1);
  	}
***************
*** 306,311
  		doxtract(argv);
  	else
  		dotable();
  	done(0);
  }
  

--- 330,336 -----
  		doxtract(argv);
  	else
  		dotable();
+ 	(*t_close) (mt);
  	done(0);
  }
  
***************
*** 1195,1201
  {
  	first = 1;
  	if (recno >= nblock) {
! 		if (write(mt, tbuf, TBLOCK*nblock) < 0) {
  			fprintf(stderr, "tar: tape write error\n");
  			done(2);
  		}

--- 1220,1226 -----
  {
  	first = 1;
  	if (recno >= nblock) {
! 		if ((*t_write)(mt, tbuf, TBLOCK*nblock) < 0) {
  			fprintf(stderr, "tar: tape write error\n");
  			done(2);
  		}
***************
*** 1209,1215
  	 *  residual to the tape buffer.
  	 */
  	while (recno == 0 && n >= nblock) {
! 		if (write(mt, buffer, TBLOCK*nblock) < 0) {
  			fprintf(stderr, "tar: tape write error\n");
  			done(2);
  		}

--- 1234,1240 -----
  	 *  residual to the tape buffer.
  	 */
  	while (recno == 0 && n >= nblock) {
! 		if ((*t_write)(mt, buffer, TBLOCK*nblock) < 0) {
  			fprintf(stderr, "tar: tape write error\n");
  			done(2);
  		}
***************
*** 1221,1227
  		bcopy(buffer, (char *)&tbuf[recno++], TBLOCK);
  		buffer += TBLOCK;
  		if (recno >= nblock) {
! 			if (write(mt, tbuf, TBLOCK*nblock) < 0) {
  				fprintf(stderr, "tar: tape write error\n");
  				done(2);
  			}

--- 1246,1252 -----
  		bcopy(buffer, (char *)&tbuf[recno++], TBLOCK);
  		buffer += TBLOCK;
  		if (recno >= nblock) {
! 			if ((*t_write)(mt, tbuf, TBLOCK*nblock) < 0) {
  				fprintf(stderr, "tar: tape write error\n");
  				done(2);
  			}
***************
*** 1240,1246
  	struct mtget mtget;
  
  	if (mtdev == 1)
! 		mtdev = ioctl(mt, MTIOCGET, &mtget);
  	if (mtdev == 0) {
  		if (ioctl(mt, MTIOCTOP, &mtop) < 0) {
  			fprintf(stderr, "tar: tape backspace error\n");

--- 1265,1271 -----
  	struct mtget mtget;
  
  	if (mtdev == 1)
! 		mtdev = (*t_ioctl)(mt, MTIOCGET, &mtget);
  	if (mtdev == 0) {
  		if ((*t_ioctl)(mt, MTIOCTOP, &mtop) < 0) {
  			fprintf(stderr, "tar: tape backspace error\n");
***************
*** 1242,1248
  	if (mtdev == 1)
  		mtdev = ioctl(mt, MTIOCGET, &mtget);
  	if (mtdev == 0) {
! 		if (ioctl(mt, MTIOCTOP, &mtop) < 0) {
  			fprintf(stderr, "tar: tape backspace error\n");
  			done(4);
  		}

--- 1267,1273 -----
  	if (mtdev == 1)
  		mtdev = (*t_ioctl)(mt, MTIOCGET, &mtget);
  	if (mtdev == 0) {
! 		if ((*t_ioctl)(mt, MTIOCTOP, &mtop) < 0) {
  			fprintf(stderr, "tar: tape backspace error\n");
  			done(4);
  		}
***************
*** 1247,1253
  			done(4);
  		}
  	} else
! 		lseek(mt, (long) -TBLOCK*nblock, 1);
  	recno--;
  }
  

--- 1272,1278 -----
  			done(4);
  		}
  	} else
! 		(*t_lseek)(mt, (long) -TBLOCK*nblock, 1);
  	recno--;
  }
  
***************
*** 1253,1259
  
  flushtape()
  {
! 	write(mt, tbuf, TBLOCK*nblock);
  }
  
  bread(fd, buf, size)

--- 1278,1284 -----
  
  flushtape()
  {
! 	(*t_write)(mt, tbuf, TBLOCK*nblock);
  }
  
  bread(fd, buf, size)
***************
*** 1265,1271
  	static int lastread = 0;
  
  	if (!Bflag)
! 		return (read(fd, buf, size));
  	for (count = 0; count < size; count += lastread) {
  		if (lastread < 0) {
  			if (count > 0)

--- 1290,1296 -----
  	static int lastread = 0;
  
  	if (!Bflag)
! 		return ((*t_read)(fd, buf, size));
  	for (count = 0; count < size; count += lastread) {
  		if (lastread < 0) {
  			if (count > 0)
***************
*** 1272,1278
  				return (count);
  			return (lastread);
  		}
! 		lastread = read(fd, buf, size - count);
  		buf += lastread;
  	}
  	return (count);

--- 1297,1303 -----
  				return (count);
  			return (lastread);
  		}
! 		lastread = (*t_read)(fd, buf, size - count);
  		buf += lastread;
  	}
  	return (count);
***************
*** 1288,1291
  		exit(1);
  	}
  	return (buf);
  }

--- 1313,1758 -----
  		exit(1);
  	}
  	return (buf);
+ }
+ 
+ fix_remote ()
+ {
+ 	char *cp, *index ();
+ 
+ 	if ((cp = index (usefile, ':')) == NULL ||
+ 		strncmp (cp + 1, "/dev/", 5) != 0)
+ 	{
+ 		t_open = open;
+ 		t_read = read;
+ 		t_write = write;
+ 		t_close = close;
+ 		t_ioctl = ioctl;
+ 		t_lseek = lseek;
+ 	}
+ 	else
+ 	{
+ 		t_open = rmtopen;
+ 		t_read = rmtread;
+ 		t_write = rmtwrite;
+ 		t_close = rmtclose;
+ 		t_ioctl = rmtioctl;
+ 		t_lseek = rmtlseek;
+ 	}
+ }
+ 
+ /*
+ *	rmt --- remote tape emulator subroutines
+ *
+ *	Originally written by Jeff Lee, modified some by Arnold Robbins
+ */
+ 
+ /* these are included above, except setjmp.h */
+ /*
+ #include <stdio.h>
+ #include <errno.h>
+ #include <setjmp.h>
+ #include <signal.h>
+ 
+ #include <sys/types.h>
+ #include <sys/ioctl.h>
+ #include <sys/mtio.h>
+ */
+ 
+ #include <setjmp.h>
+ 
+ /*
+ *	MAXUNIT --- Maximum number of remote tape file units
+ *
+ *	READ --- Return the number of the read side file descriptor
+ *	WRITE --- Return the number of the write side file descriptor
+ */
+ 
+ #define MAXUNIT	4
+ 
+ #define READ(fd)	(Ctp[fd][0])
+ #define WRITE(fd)	(Ptc[fd][1])
+ 
+ static int Ctp[MAXUNIT][2] = { -1, -1, -1, -1, -1, -1, -1, -1 };
+ static int Ptc[MAXUNIT][2] = { -1, -1, -1, -1, -1, -1, -1, -1 };
+ 
+ jmp_buf Jmpbuf;
+ extern int errno;
+ 
+ 
+ 
+ /*
+ *	abort --- close off a remote tape connection
+ */
+ 
+ static abort(tfd)
+ int tfd;
+ {
+ 	close(READ(tfd));
+ 	close(WRITE(tfd));
+ 	READ(tfd) = -1;
+ 	WRITE(tfd) = -1;
+ }
+ 
+ 
+ 
+ /*
+ *	command --- attempt to perform a remote tape command
+ */
+ 
+ static command(tfd, buf)
+ char *buf;
+ int tfd;
+ {
+ 	int blen;
+ 	int (*pstat)();
+ 
+ /*
+ *	save current pipe status and try to make the request
+ */
+ 
+ 	blen = strlen(buf);
+ 	pstat = signal(SIGPIPE, SIG_IGN);
+ 	if (write(WRITE(tfd), buf, blen) == blen)
+ 	{
+ 		signal(SIGPIPE, pstat);
+ 		return(0);
+ 	}
+ 
+ /*
+ *	something went wrong. close down and go home
+ */
+ 
+ 	signal(SIGPIPE, pstat);
+ 	abort(tfd);
+ 
+ 	errno = EIO;
+ 	return(-1);
+ }
+ 
+ 
+ 
+ /*
+ *	status --- retrieve the status from the pipe
+ */
+ 
+ static status(tfd)
+ int tfd;
+ {
+ 	int i;
+ 	char c, *cp;
+ 	char buf[64];
+ 
+ /*
+ *	read the reply command line
+ */
+ 
+ 	for (i = 0, cp = buf; i < 64; i++, cp++)
+ 	{
+ 		if (read(READ(tfd), cp, 1) != 1)
+ 		{
+ 			abort(tfd);
+ 			errno = EIO;
+ 			return(-1);
+ 		}
+ 		if (*cp == '\n')
+ 		{
+ 			*cp = 0;
+ 			break;
+ 		}
+ 	}
+ 
+ 	if (i == 64)
+ 	{
+ 		abort(tfd);
+ 		errno = EIO;
+ 		return(-1);
+ 	}
+ 
+ /*
+ *	check the return status
+ */
+ 
+ 	for (cp = buf; *cp; cp++)
+ 		if (*cp != ' ')
+ 			break;
+ 
+ 	if (*cp == 'E' || *cp == 'F')
+ 	{
+ 		errno = atoi(cp + 1);
+ 		while (read(READ(tfd), &c, 1) == 1)
+ 			if (c == '\n')
+ 				break;
+ 
+ 		if (*cp == 'F')
+ 			abort(tfd);
+ 
+ 		return(-1);
+ 	}
+ 
+ /*
+ *	check for mis-synced pipes
+ */
+ 
+ 	if (*cp != 'A')
+ 	{
+ 		abort(tfd);
+ 		errno = EIO;
+ 		return(-1);
+ 	}
+ 
+ 	return(atoi(cp + 1));
+ }
+ 
+ 
+ 
+ /*
+ *	rmtopen --- open a magtape device on system specified
+ */
+ 
+ rmtopen(dev, mode)
+ char *dev;
+ int mode;
+ {
+ 	int i, rc;
+ 	char buf[64];
+ 	char *sys;
+ 
+ /*
+ *	first, find an open pair of file descriptors
+ */
+ 
+ 	for (i = 0; i < MAXUNIT; i++)
+ 		if (READ(i) == -1 && WRITE(i) == -1)
+ 			break;
+ 
+ 	if (i == MAXUNIT)
+ 	{
+ 		errno = EMFILE;
+ 		return(-1);
+ 	}
+ 
+ /*
+ *	pull apart system and device
+ */
+ 	for (sys = dev; *dev != ':'; dev++)
+ 		;
+ 	*dev++ = '\0';
+ 
+ /*
+ *	setup the pipes for the 'rsh' command and fork
+ */
+ 
+ 	if (pipe(Ptc[i]) == -1 || pipe(Ctp[i]) == -1)
+ 		return(-1);
+ 
+ 	if ((rc = fork()) == -1)
+ 		return(-1);
+ 
+ 	if (rc == 0)
+ 	{
+ 		close(0);
+ 		dup(Ptc[i][0]);
+ 		close(Ptc[i][0]); close(Ptc[i][1]);
+ 		close(1);
+ 		dup(Ctp[i][1]);
+ 		close(Ctp[i][0]); close(Ctp[i][1]);
+ 
+ 		execl("/usr/ucb/rsh", "rsh", sys, "/etc/rmt", (char *) 0);
+ 
+ /*
+ *	bad problems if we get here
+ */
+ 
+ 		perror("exec");
+ 		exit(1);
+ 	}
+ 
+ 	close(Ptc[i][0]); close(Ctp[i][1]);
+ 
+ /*
+ *	now attempt to open the tape device
+ */
+ 
+ 	sprintf(buf, "O%s\n%d\n", dev, mode);
+ 	if (command(i, buf) == -1 || status(i) == -1)
+ 		return(-1);
+ 
+ 	return(i);
+ }
+ 
+ 
+ 
+ /*
+ *	rmtclose --- close a remote magtape unit and shut down
+ */
+ 
+ rmtclose(tfd)
+ int tfd;
+ {
+ 	int rc;
+ 
+ 	if (command(tfd, "C\n") != -1)
+ 	{
+ 		rc = status(tfd);
+ 
+ 		abort(tfd);
+ 		return(rc);
+ 	}
+ 
+ 	return(-1);
+ }
+ 
+ 
+ 
+ /*
+ *	rmtread --- read a buffer from a remote tape
+ */
+ 
+ rmtread(tfd, data, cnt)
+ int tfd, cnt;
+ char *data;
+ {
+ 	int rc, i;
+ 	char buf[64];
+ 
+ 	sprintf(buf, "R%d\n", cnt);
+ 	if (command(tfd, buf) == -1 || (rc = status(tfd)) == -1)
+ 		return(-1);
+ 
+ 	for (i = 0; i < rc; i += cnt, data += cnt)
+ 	{
+ 		cnt = read(READ(tfd), data, rc);
+ 		if (cnt <= 0)
+ 		{
+ 			abort(tfd);
+ 			errno = EIO;
+ 			return(-1);
+ 		}
+ 	}
+ 
+ 	return(rc);
+ }
+ 
+ 
+ 
+ /*
+ *	rmtwrite --- write a buffer to the remote tape
+ */
+ 
+ rmtwrite(tfd, data, cnt)
+ int tfd, cnt;
+ char *data;
+ {
+ 	int rc;
+ 	char buf[64];
+ 	int (*pstat)();
+ 
+ 	sprintf(buf, "W%d\n", cnt);
+ 	if (command(tfd, buf) == -1)
+ 		return(-1);
+ 
+ 	pstat = signal(SIGPIPE, SIG_IGN);
+ 	if (write(WRITE(tfd), data, cnt) == cnt)
+ 		return(status(tfd));
+ 
+ 	abort(tfd);
+ 	errno = EIO;
+ 	return(-1);
+ }
+ 
+ 
+ 
+ /*
+ *	rmtlseek --- perform an imitation lseek operation remotely
+ */
+ 
+ rmtlseek(tfd, wh, off)
+ int tfd, wh, off;
+ {
+ 	char buf[64];
+ 
+ 	sprintf(buf, "L%d\n%d\n", wh, off);
+ 	if (command(tfd, buf) == -1)
+ 		return(-1);
+ 
+ 	return(status(tfd));
+ }
+ 
+ 
+ 
+ /*
+ *	rmtioctl --- perform raw tape operations remotely
+ */
+ 
+ rmtioctl(tfd, op, arg)
+ int tfd, op;
+ char *arg;
+ {
+ 	char c;
+ 	int rc, cnt;
+ 	char buf[64];
+ 
+ /*
+ *	MTIOCOP is the easy one. nothing is transfered in binary
+ */
+ 
+ 	if (op == MTIOCTOP)
+ 	{
+ 		sprintf(buf, "I%d\n%d\n", ((struct mtop *) arg)->mt_op,
+ 			((struct mtop *) arg)->mt_count);
+ 		if (command(tfd, buf) == -1)
+ 			return(-1);
+ 		return(status(tfd));
+ 	}
+ 
+ /*
+ *	we can only handle 2 ops, if not the other one, punt
+ */
+ 
+ 	if (op != MTIOCGET)
+ 	{
+ 		errno = EINVAL;
+ 		return(-1);
+ 	}
+ 
+ /*
+ *	grab the status and read it directly into the structure
+ *	this assumes that the status buffer is (hopefully) not
+ *	padded and that 2 shorts fit in a long without any word
+ *	alignment problems, ie - the whole struct is contiguous
+ *	NOTE - this is probably NOT a good assumption.
+ */
+ 
+ 	if (command(tfd, "S\n") == -1 || (rc = status(tfd)) == -1)
+ 		return(-1);
+ 
+ 	for (; rc > 0; rc -= cnt, arg += cnt)
+ 	{
+ 		cnt = read(READ(tfd), arg, rc);
+ 		if (cnt <= 0)
+ 		{
+ 			abort(tfd);
+ 			errno = EIO;
+ 			return(-1);
+ 		}
+ 	}
+ 
+ /*
+ *	now we check for byte position. mt_type is a small integer field
+ *	(normally) so we will check its magnitude. if it is larger than
+ *	256, we will assume that the bytes are swapped and go through
+ *	and reverse all the bytes
+ */
+ 
+ 	if (((struct mtget *) arg)->mt_type < 256)
+ 		return(0);
+ 
+ 	for (cnt = 0; cnt < rc; cnt += 2)
+ 	{
+ 		c = arg[cnt];
+ 		arg[cnt] = arg[cnt+1];
+ 		arg[cnt+1] = c;
+ 	}
+ 
+ 	return(0);
  }
*** /usr/man/man1/tar.1	Mon Jun 27 00:35:14 1983
--- tar.1	Mon Jul  1 13:51:09 1985
***************
*** 95,101
  .B f
  .I Tar
  uses the next argument as the name of the archive instead of
! /dev/rmt?. If the name of the file is `\-', tar writes to standard output or
  reads from standard input, whichever is appropriate. Thus,
  .I tar
  can be used as the head or tail of a filter chain.

--- 95,113 -----
  .B f
  .I Tar
  uses the next argument as the name of the archive instead of
! /dev/rmt?.
! .sp
! If the file name has the form
! .IR system :/dev/???,
! .I tar
! will use the tape drive /dev/??? on the remote system
! .IR system ,
! via
! .IR rsh (1),
! and
! .IR rmt (8).
! .sp
! If the name of the file is `\-', tar writes to standard output or
  reads from standard input, whichever is appropriate. Thus,
  .I tar
  can be used as the head or tail of a filter chain.
***************
*** 179,181
  The current limit on file name length is 100 characters.
  .br
  There is no way to selectively follow symbolic links.

--- 191,195 -----
  The current limit on file name length is 100 characters.
  .br
  There is no way to selectively follow symbolic links.
+ .br
+ Using a remote system's tape drive can be slow.



More information about the Mod.sources mailing list