372 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			372 lines
		
	
	
		
			8.4 KiB
		
	
	
	
		
			C
		
	
	
		
			Executable File
		
	
	
	
	
| /* vol - break stdin into volumes	Author: Andy Tanenbaum */
 | |
| 
 | |
| /* This program reads standard input and writes it onto diskettes, pausing
 | |
|  * at the start of each one.  It's main use is for saving files that are
 | |
|  * larger than a single diskette.  Vol just writes its standard input onto
 | |
|  * a diskette, and prompts for a new one when it is full.  This mechanism
 | |
|  * is transparent to the process producing vol's standard input. For example,
 | |
|  *	tar cf - . | vol -w 360 /dev/fd0
 | |
|  * puts the tar output as as many diskettes as needed.  To read them back in,
 | |
|  * use
 | |
|  *	vol -r 360 /dev/fd0 | tar xf -
 | |
|  *
 | |
|  * Changed 17 Nov 1993 by Kees J. Bot to handle buffering to slow devices.
 | |
|  * Changed 27 Jul 1994 by Kees J. Bot to auto discover data direction + -rw.
 | |
|  * Changed 19 Sep 1995 by Kees J. Bot to do better buffering to tapes.
 | |
|  */
 | |
| 
 | |
| #include <sys/types.h>
 | |
| #include <fcntl.h>
 | |
| #include <sys/stat.h>
 | |
| #include <errno.h>
 | |
| #include <stdlib.h>
 | |
| #include <unistd.h>
 | |
| #include <stdio.h>
 | |
| #include <limits.h>
 | |
| #include <string.h>
 | |
| #include <sys/ioctl.h>
 | |
| #include <sys/mtio.h>
 | |
| #include <minix/partition.h>
 | |
| #include <minix/u64.h>
 | |
| 
 | |
| /* Preferred block size to variable block length tapes, block devices or files.
 | |
|  */
 | |
| #define VAR_BLKSIZ	   8192
 | |
| 
 | |
| /* Required block size multiple of fixed block size tapes (usually updated by
 | |
|  * 'mt status' data) and character devices.
 | |
|  */
 | |
| #define FIX_BLKSIZ	    512
 | |
| 
 | |
| /* Maximum multiple block size. */
 | |
| #if __minix_vmd
 | |
| #define MULT_MAX	1048576
 | |
| #else
 | |
| #define MULT_MAX	((ssize_t) (SSIZE_MAX < 65536L ? SSIZE_MAX : 65536L))
 | |
| #endif
 | |
| 
 | |
| char *buffer = NULL;
 | |
| size_t block_size = 0, mult_max = 0;
 | |
| size_t buffer_size;
 | |
| long volume_size;
 | |
| char *str_vol_size;
 | |
| int rflag = 0, wflag = 0, oneflag = 0, variable = 0;
 | |
| 
 | |
| _PROTOTYPE(int main, (int argc, char **argv));
 | |
| _PROTOTYPE(void usage, (void));
 | |
| _PROTOTYPE(long str2size, (char *name, char *str, long min, long max,
 | |
| 							int assume_kb));
 | |
| _PROTOTYPE(void tape_inquire, (char *name, int fd));
 | |
| _PROTOTYPE(void allocate_buffer, (void));
 | |
| _PROTOTYPE(void diskio, (int fd1, int fd2, char *file1, char *file2));
 | |
| 
 | |
| int main(argc, argv)
 | |
| int argc;
 | |
| char *argv[];
 | |
| {
 | |
|   int volume = 1, fd, tty, i, init, autovolsize;
 | |
|   char *p, *name;
 | |
|   struct stat stb;
 | |
|   struct partition part;
 | |
|   char key;
 | |
| 
 | |
|   /* Fetch and verify the arguments. */
 | |
|   i = 1;
 | |
|   while (i < argc && argv[i][0] == '-') {
 | |
| 	p = argv[i++] + 1;
 | |
| 	if (p[0] == '-' && p[1] == 0) {
 | |
| 		/* -- */
 | |
| 		i++;
 | |
| 		break;
 | |
| 	}
 | |
| 	while (*p != '\0') {
 | |
| 		switch (*p++) {
 | |
| 		case 'r':
 | |
| 		case 'u':
 | |
| 			rflag = 1;
 | |
| 			break;
 | |
| 		case 'w':
 | |
| 			wflag = 1;
 | |
| 			break;
 | |
| 		case '1':
 | |
| 			oneflag = 1;
 | |
| 			break;
 | |
| 		case 'b':
 | |
| 			if (*p == 0) {
 | |
| 				if (i == argc) usage();
 | |
| 				p = argv[i++];
 | |
| 			}
 | |
| 			block_size = str2size("block", p,
 | |
| 						1L, (long) SSIZE_MAX, 0);
 | |
| 			p= "";
 | |
| 			break;
 | |
| 		case 'm':
 | |
| 			if (*p == 0) {
 | |
| 				if (i == argc) usage();
 | |
| 				p = argv[i++];
 | |
| 			}
 | |
| 			mult_max = str2size("maximum", p,
 | |
| 						1L, (long) SSIZE_MAX, 0);
 | |
| 			p= "";
 | |
| 			break;
 | |
| 		default:
 | |
| 			usage();
 | |
| 		}
 | |
| 	}
 | |
|   }
 | |
|   if (i < argc - 1) {
 | |
| 	str_vol_size = argv[i++];
 | |
| 	volume_size = str2size("volume", str_vol_size, 1L, LONG_MAX, 1);
 | |
| 	autovolsize = 0;
 | |
|   } else {
 | |
| 	volume_size = 0;	/* unlimited (long tape) or use DIOCGETP */
 | |
| 	autovolsize = 1;
 | |
|   }
 | |
| 
 | |
|   if (i >= argc) usage();
 | |
|   name = argv[i];
 | |
| 
 | |
|   if (!rflag && !wflag) {
 | |
| 	/* Auto direction.  If there is a terminal at one side then data is
 | |
| 	 * to go out at the other side.
 | |
| 	 */
 | |
| 	if (isatty(0)) rflag = 1;
 | |
| 	if (isatty(1)) wflag = 1;
 | |
|   }
 | |
| 
 | |
|   if (rflag == wflag) {
 | |
| 	fprintf(stderr, "vol: should %s be read or written?\n", name);
 | |
| 	usage();
 | |
|   }
 | |
| 
 | |
|   if (stat(name, &stb) < 0) {
 | |
| 	fprintf(stderr, "vol: %s: %s\n", name, strerror(errno));
 | |
| 	exit(1);
 | |
|   }
 | |
|   if (!S_ISBLK(stb.st_mode) && !S_ISCHR(stb.st_mode)) {
 | |
| 	fprintf(stderr, "vol: %s is not a device\n", name);
 | |
| 	exit(1);
 | |
|   }
 | |
|   variable = !S_ISCHR(stb.st_mode);
 | |
| 
 | |
|   if (!oneflag) {
 | |
| 	tty = open("/dev/tty", O_RDONLY);
 | |
| 	if (tty < 0) {
 | |
| 		fprintf(stderr, "vol: cannot open /dev/tty\n");
 | |
| 		exit(1);
 | |
| 	}
 | |
|   }
 | |
| 
 | |
|   /* Buffer initializations are yet to be done. */
 | |
|   init = 0;
 | |
| 
 | |
|   while (1) {
 | |
| 	sleep(1);
 | |
| 	if (oneflag) {
 | |
| 		if (volume != 1) {
 | |
| 			if (rflag) exit(0);
 | |
| 			fprintf(stderr,
 | |
| 				"vol: can't continue, volume is full\n");
 | |
| 			exit(1);
 | |
| 		}
 | |
| 	} else {
 | |
| 		fprintf(stderr,
 | |
| 			"\007Please insert %sput volume %d and hit return\n",
 | |
| 			rflag ? "in" : "out", volume);
 | |
| 		while (read(tty, &key, sizeof(key)) == 1 && key != '\n') {}
 | |
| 	}
 | |
| 
 | |
| 	/* Open the special file. */
 | |
| 	fd = open(name, rflag ? O_RDONLY : O_WRONLY);
 | |
| 	if (fd < 0) {
 | |
| 		fprintf(stderr, "vol: %s: %s\n", name, strerror(errno));
 | |
| 		exit(1);
 | |
| 	}
 | |
| 
 | |
| 	if (!init) {
 | |
| 		/* Ask for the tape block size and allocate a buffer. */
 | |
| 		if (S_ISCHR(stb.st_mode)) tape_inquire(name, fd);
 | |
| 		allocate_buffer();
 | |
| 		init = 1;
 | |
| 	}
 | |
| 
 | |
| 	if (autovolsize) {
 | |
| 		/* Ask the driver how big the volume is. */
 | |
| 		if (ioctl(fd, DIOCGETP, &part) < 0) {
 | |
| 			autovolsize = 0;
 | |
| 		} else {
 | |
| 			volume_size = cv64ul(part.size);
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	/* Read or write the requisite number of blocks. */
 | |
| 	if (rflag) {
 | |
| 		diskio(fd, 1, name, "stdout");	/* vol -r | tar xf - */
 | |
| 	} else {
 | |
| 		diskio(0, fd, "stdin", name);	/* tar cf - | vol -w */
 | |
| 	}
 | |
| 	close(fd);
 | |
| 	volume++;
 | |
|   }
 | |
| }
 | |
| 
 | |
| void usage()
 | |
| {
 | |
|   fprintf(stderr,
 | |
| 	"Usage: vol [-rw1] [-b blocksize] [-m max] [size] block-special\n");
 | |
|   exit(1);
 | |
| }
 | |
| 
 | |
| long str2size(name, str, min, max, assume_kb)
 | |
| char *name;
 | |
| char *str;
 | |
| long min, max;
 | |
| int assume_kb;
 | |
| {
 | |
|   /* Convert a string to a size.  The number may be followed by 'm', 'k', 'b'
 | |
|    * or 'w' to multiply the size as shown below.  If 'assume_kb' is set then
 | |
|    * kilobytes is the default.
 | |
|    */
 | |
|   long size, factor;
 | |
|   char *ptr;
 | |
|   int bad;
 | |
| 
 | |
|   errno = 0;
 | |
|   size = strtol(str, &ptr, 10);
 | |
|   bad = (errno != 0 || ptr == str || size < min || size > max);
 | |
|   if (*ptr == 0 && assume_kb) ptr = "k";
 | |
|   while (!bad && *ptr != 0) {
 | |
| 	switch (*ptr++) {
 | |
| 	case 'm':
 | |
| 	case 'M':
 | |
| 		factor = 1024*1024L; break;
 | |
| 	case 'k':
 | |
| 	case 'K':
 | |
| 		factor = 1024; break;
 | |
| 	case 'b':
 | |
| 	case 'B':
 | |
| 		factor = 512; break;
 | |
| 	case 'w':
 | |
| 	case 'W':
 | |
| 		factor = 2; break;
 | |
| 	default:
 | |
| 		factor = 1; bad = 1;
 | |
| 	}
 | |
| 	if (size <= max / factor) size *= factor; else bad = 1;
 | |
|   }
 | |
|   if (bad) {
 | |
| 	fprintf(stderr, "vol: bad %s size '%s'\n", name, str);
 | |
| 	exit(1);
 | |
|   }
 | |
|   return size;
 | |
| }
 | |
| 
 | |
| void tape_inquire(name, fd)
 | |
| char *name;
 | |
| int fd;
 | |
| {
 | |
|   /* If the device happens to be a tape, then what is its block size? */
 | |
|   struct mtget mtget;
 | |
| 
 | |
|   if (ioctl(fd, MTIOCGET, &mtget) < 0) {
 | |
| 	if (errno != ENOTTY) {
 | |
| 		fprintf(stderr, "vol: %s: %s\n", name,
 | |
| 					strerror(errno));
 | |
| 		exit(1);
 | |
| 	}
 | |
|   } else {
 | |
| 	if (mtget.mt_blksize > SSIZE_MAX) {
 | |
| 		fprintf(stderr,
 | |
| 		"vol: %s: tape block size (%lu) is too large to handle\n",
 | |
| 			name, (unsigned long) mtget.mt_blksize);
 | |
| 		exit(1);
 | |
| 	}
 | |
| 	if (mtget.mt_blksize == 0) {
 | |
| 		variable = 1;
 | |
| 	} else {
 | |
| 		/* fixed */
 | |
| 		block_size = mtget.mt_blksize;
 | |
| 	}
 | |
|   }
 | |
| }
 | |
| 
 | |
| void allocate_buffer()
 | |
| {
 | |
|   /* Set block size and maximum multiple. */
 | |
|   if (block_size == 0) block_size = variable ? 1 : FIX_BLKSIZ;
 | |
|   if (mult_max == 0) mult_max = variable ? VAR_BLKSIZ : MULT_MAX;
 | |
| 
 | |
|   /* Stretch the buffer size to the max. */
 | |
|   buffer_size = mult_max / block_size * block_size;
 | |
|   if (buffer_size == 0) buffer_size = block_size;
 | |
| 
 | |
|   if (volume_size % block_size != 0) {
 | |
| 	fprintf(stderr,
 | |
| 	"vol: volume size (%s) is not a multiple of the block size (%lu)\n",
 | |
| 		str_vol_size, (unsigned long) block_size);
 | |
| 	exit(1);
 | |
|   }
 | |
| 
 | |
|   buffer = (char *) malloc(buffer_size);
 | |
|   if (buffer == NULL) {
 | |
| 	fprintf(stderr, "vol: cannot allocate a %luk buffer\n",
 | |
| 		(unsigned long) buffer_size / 1024);
 | |
| 	exit(1);
 | |
|   }
 | |
| }
 | |
| 
 | |
| void diskio(fd1, fd2, file1, file2)
 | |
| int fd1, fd2;
 | |
| char *file1, *file2;
 | |
| {
 | |
| /* Read 'volume_size' bytes from 'fd1' and write them on 'fd2'.  Watch out for
 | |
|  * the fact that reads on pipes can return less than the desired data.
 | |
|  */
 | |
| 
 | |
|   ssize_t n, in_needed, in_count, out_count;
 | |
|   long needed = volume_size;
 | |
|   int eof = 0;
 | |
| 
 | |
|   for (;;) {
 | |
| 	if (volume_size == 0) needed = buffer_size;
 | |
| 
 | |
| 	if (needed == 0) break;
 | |
| 
 | |
| 	in_count = 0;
 | |
| 	in_needed = needed > buffer_size ? buffer_size : needed;
 | |
| 	while (in_count < in_needed) {
 | |
| 		n = in_needed - in_count;
 | |
| 		n = eof ? 0 : read(fd1, buffer + in_count, n);
 | |
| 		if (n == 0) {
 | |
| 			eof = 1;
 | |
| 			if ((n = in_count % block_size) > 0) {
 | |
| 				n = block_size - n;
 | |
| 				memset(buffer + in_count, '\0', n);
 | |
| 				if ((in_count += n) > in_needed)
 | |
| 					in_count = in_needed;
 | |
| 			}
 | |
| 			break;
 | |
| 		}
 | |
| 		if (n < 0) {
 | |
| 			fprintf(stderr, "vol: %s: %s\n",
 | |
| 						file1, strerror(errno));
 | |
| 			exit(1);
 | |
| 		}
 | |
| 		in_count += n;
 | |
| 	}
 | |
| 	if (in_count == 0) exit(0);	/* EOF */
 | |
| 	out_count = 0;
 | |
| 	while (out_count < in_count) {
 | |
| 		n = in_count - out_count;
 | |
| 		n = write(fd2, buffer + out_count, n);
 | |
| 		if (n < 0) {
 | |
| 			fprintf(stderr, "vol: %s: %s\n",
 | |
| 						file2, strerror(errno));
 | |
| 			exit(1);
 | |
| 		}
 | |
| 		out_count += n;
 | |
| 	}
 | |
| 	needed -= in_count;
 | |
|   }
 | |
| }
 | 
