xref: /minix3/minix/commands/vol/vol.c (revision 433d6423c39e34ec4b79c950597bb2d236f886be)
1 /* vol - break stdin into volumes	Author: Andy Tanenbaum */
2 
3 /* This program reads standard input and writes it onto diskettes, pausing
4  * at the start of each one.  It's main use is for saving files that are
5  * larger than a single diskette.  Vol just writes its standard input onto
6  * a diskette, and prompts for a new one when it is full.  This mechanism
7  * is transparent to the process producing vol's standard input. For example,
8  *	tar cf - . | vol -w 360 /dev/fd0
9  * puts the tar output as as many diskettes as needed.  To read them back in,
10  * use
11  *	vol -r 360 /dev/fd0 | tar xf -
12  *
13  * Changed 17 Nov 1993 by Kees J. Bot to handle buffering to slow devices.
14  * Changed 27 Jul 1994 by Kees J. Bot to auto discover data direction + -rw.
15  * Changed 19 Sep 1995 by Kees J. Bot to do better buffering to tapes.
16  */
17 
18 #include <sys/types.h>
19 #include <fcntl.h>
20 #include <sys/stat.h>
21 #include <errno.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <stdio.h>
25 #include <limits.h>
26 #include <string.h>
27 #include <sys/ioctl.h>
28 #include <sys/mtio.h>
29 #include <minix/partition.h>
30 
31 /* Preferred block size to variable block length tapes, block devices or files.
32  */
33 #define VAR_BLKSIZ	   8192
34 
35 /* Required block size multiple of fixed block size tapes (usually updated by
36  * 'mt status' data) and character devices.
37  */
38 #define FIX_BLKSIZ	    512
39 
40 /* Maximum multiple block size. */
41 #if __minix_vmd
42 #define MULT_MAX	1048576
43 #else
44 #define MULT_MAX	((ssize_t) (SSIZE_MAX < 65536L ? SSIZE_MAX : 65536L))
45 #endif
46 
47 char *buffer = NULL;
48 size_t block_size = 0, mult_max = 0;
49 size_t buffer_size;
50 u64_t volume_size;
51 char *str_vol_size;
52 int rflag = 0, wflag = 0, oneflag = 0, variable = 0;
53 
54 int main(int argc, char **argv);
55 void usage(void);
56 long str2size(char *name, char *str, long min, long max, int assume_kb);
57 void tape_inquire(char *name, int fd);
58 void allocate_buffer(void);
59 void diskio(int fd1, int fd2, char *file1, char *file2);
60 
main(argc,argv)61 int main(argc, argv)
62 int argc;
63 char *argv[];
64 {
65   int volume = 1, fd, tty, i, init, autovolsize;
66   char *p, *name;
67   struct stat stb;
68   struct part_geom part;
69   char key;
70 
71   /* Fetch and verify the arguments. */
72   i = 1;
73   while (i < argc && argv[i][0] == '-') {
74 	p = argv[i++] + 1;
75 	if (p[0] == '-' && p[1] == 0) {
76 		/* -- */
77 		i++;
78 		break;
79 	}
80 	while (*p != '\0') {
81 		switch (*p++) {
82 		case 'r':
83 		case 'u':
84 			rflag = 1;
85 			break;
86 		case 'w':
87 			wflag = 1;
88 			break;
89 		case '1':
90 			oneflag = 1;
91 			break;
92 		case 'b':
93 			if (*p == 0) {
94 				if (i == argc) usage();
95 				p = argv[i++];
96 			}
97 			block_size = str2size("block", p,
98 						1L, (long) SSIZE_MAX, 0);
99 			p= "";
100 			break;
101 		case 'm':
102 			if (*p == 0) {
103 				if (i == argc) usage();
104 				p = argv[i++];
105 			}
106 			mult_max = str2size("maximum", p,
107 						1L, (long) SSIZE_MAX, 0);
108 			p= "";
109 			break;
110 		default:
111 			usage();
112 		}
113 	}
114   }
115   if (i < argc - 1) {
116 	str_vol_size = argv[i++];
117 	volume_size = str2size("volume", str_vol_size, 1L, LONG_MAX, 1);
118 	autovolsize = 0;
119   } else {
120 	volume_size = 0;	/* unlimited (long tape) or use DIOCGETP */
121 	autovolsize = 1;
122   }
123 
124   if (i >= argc) usage();
125   name = argv[i];
126 
127   if (!rflag && !wflag) {
128 	/* Auto direction.  If there is a terminal at one side then data is
129 	 * to go out at the other side.
130 	 */
131 	if (isatty(0)) rflag = 1;
132 	if (isatty(1)) wflag = 1;
133   }
134 
135   if (rflag == wflag) {
136 	fprintf(stderr, "vol: should %s be read or written?\n", name);
137 	usage();
138   }
139 
140   if (stat(name, &stb) < 0) {
141 	fprintf(stderr, "vol: %s: %s\n", name, strerror(errno));
142 	exit(1);
143   }
144   if (!S_ISBLK(stb.st_mode) && !S_ISCHR(stb.st_mode)) {
145 	fprintf(stderr, "vol: %s is not a device\n", name);
146 	exit(1);
147   }
148   variable = !S_ISCHR(stb.st_mode);
149 
150   if (!oneflag) {
151 	tty = open("/dev/tty", O_RDONLY);
152 	if (tty < 0) {
153 		fprintf(stderr, "vol: cannot open /dev/tty\n");
154 		exit(1);
155 	}
156   }
157 
158   /* Buffer initializations are yet to be done. */
159   init = 0;
160 
161   while (1) {
162 	sleep(1);
163 	if (oneflag) {
164 		if (volume != 1) {
165 			if (rflag) exit(0);
166 			fprintf(stderr,
167 				"vol: can't continue, volume is full\n");
168 			exit(1);
169 		}
170 	} else {
171 		fprintf(stderr,
172 			"\007Please insert %sput volume %d and hit return\n",
173 			rflag ? "in" : "out", volume);
174 		while (read(tty, &key, sizeof(key)) == 1 && key != '\n') {}
175 	}
176 
177 	/* Open the special file. */
178 	fd = open(name, rflag ? O_RDONLY : O_WRONLY);
179 	if (fd < 0) {
180 		fprintf(stderr, "vol: %s: %s\n", name, strerror(errno));
181 		exit(1);
182 	}
183 
184 	if (!init) {
185 		/* Ask for the tape block size and allocate a buffer. */
186 		if (S_ISCHR(stb.st_mode)) tape_inquire(name, fd);
187 		allocate_buffer();
188 		init = 1;
189 	}
190 
191 	if (autovolsize) {
192 		/* Ask the driver how big the volume is. */
193 		if (ioctl(fd, DIOCGETP, &part) < 0) {
194 			autovolsize = 0;
195 		} else {
196 			volume_size = part.size;
197 		}
198 	}
199 
200 	/* Read or write the requisite number of blocks. */
201 	if (rflag) {
202 		diskio(fd, 1, name, "stdout");	/* vol -r | tar xf - */
203 	} else {
204 		diskio(0, fd, "stdin", name);	/* tar cf - | vol -w */
205 	}
206 	close(fd);
207 	volume++;
208   }
209 }
210 
usage()211 void usage()
212 {
213   fprintf(stderr,
214 	"Usage: vol [-rw1] [-b blocksize] [-m max] [size] block-special\n");
215   exit(1);
216 }
217 
str2size(name,str,min,max,assume_kb)218 long str2size(name, str, min, max, assume_kb)
219 char *name;
220 char *str;
221 long min, max;
222 int assume_kb;
223 {
224   /* Convert a string to a size.  The number may be followed by 'm', 'k', 'b'
225    * or 'w' to multiply the size as shown below.  If 'assume_kb' is set then
226    * kilobytes is the default.
227    */
228   long size, factor;
229   char *ptr;
230   int bad;
231 
232   errno = 0;
233   size = strtol(str, &ptr, 10);
234   bad = (errno != 0 || ptr == str || size < min || size > max);
235   if (*ptr == 0 && assume_kb) ptr = "k";
236   while (!bad && *ptr != 0) {
237 	switch (*ptr++) {
238 	case 'm':
239 	case 'M':
240 		factor = 1024*1024L; break;
241 	case 'k':
242 	case 'K':
243 		factor = 1024; break;
244 	case 'b':
245 	case 'B':
246 		factor = 512; break;
247 	case 'w':
248 	case 'W':
249 		factor = 2; break;
250 	default:
251 		factor = 1; bad = 1;
252 	}
253 	if (size <= max / factor) size *= factor; else bad = 1;
254   }
255   if (bad) {
256 	fprintf(stderr, "vol: bad %s size '%s'\n", name, str);
257 	exit(1);
258   }
259   return size;
260 }
261 
tape_inquire(name,fd)262 void tape_inquire(name, fd)
263 char *name;
264 int fd;
265 {
266   /* If the device happens to be a tape, then what is its block size? */
267   struct mtget mtget;
268 
269   if (ioctl(fd, MTIOCGET, &mtget) < 0) {
270 	if (errno != ENOTTY) {
271 		fprintf(stderr, "vol: %s: %s\n", name,
272 					strerror(errno));
273 		exit(1);
274 	}
275   } else {
276 	if (mtget.mt_blksiz > SSIZE_MAX) {
277 		fprintf(stderr,
278 		"vol: %s: tape block size (%lu) is too large to handle\n",
279 			name, (unsigned long) mtget.mt_blksiz);
280 		exit(1);
281 	}
282 	if (mtget.mt_blksiz == 0) {
283 		variable = 1;
284 	} else {
285 		/* fixed */
286 		block_size = mtget.mt_blksiz;
287 	}
288   }
289 }
290 
allocate_buffer()291 void allocate_buffer()
292 {
293   /* Set block size and maximum multiple. */
294   if (block_size == 0) block_size = variable ? 1 : FIX_BLKSIZ;
295   if (mult_max == 0) mult_max = variable ? VAR_BLKSIZ : MULT_MAX;
296 
297   /* Stretch the buffer size to the max. */
298   buffer_size = mult_max / block_size * block_size;
299   if (buffer_size == 0) buffer_size = block_size;
300 
301   if (volume_size % block_size != 0) {
302 	fprintf(stderr,
303 	"vol: volume size (%s) is not a multiple of the block size (%lu)\n",
304 		str_vol_size, (unsigned long) block_size);
305 	exit(1);
306   }
307 
308   buffer = (char *) malloc(buffer_size);
309   if (buffer == NULL) {
310 	fprintf(stderr, "vol: cannot allocate a %luk buffer\n",
311 		(unsigned long) buffer_size / 1024);
312 	exit(1);
313   }
314 }
315 
diskio(fd1,fd2,file1,file2)316 void diskio(fd1, fd2, file1, file2)
317 int fd1, fd2;
318 char *file1, *file2;
319 {
320 /* Read 'volume_size' bytes from 'fd1' and write them on 'fd2'.  Watch out for
321  * the fact that reads on pipes can return less than the desired data.
322  */
323 
324   ssize_t n, in_needed, in_count, out_count;
325   long needed = volume_size;
326   int eof = 0;
327 
328   for (;;) {
329 	if (volume_size == 0) needed = buffer_size;
330 
331 	if (needed == 0) break;
332 
333 	in_count = 0;
334 	in_needed = needed > buffer_size ? buffer_size : needed;
335 	while (in_count < in_needed) {
336 		n = in_needed - in_count;
337 		n = eof ? 0 : read(fd1, buffer + in_count, n);
338 		if (n == 0) {
339 			eof = 1;
340 			if ((n = in_count % block_size) > 0) {
341 				n = block_size - n;
342 				memset(buffer + in_count, '\0', n);
343 				if ((in_count += n) > in_needed)
344 					in_count = in_needed;
345 			}
346 			break;
347 		}
348 		if (n < 0) {
349 			fprintf(stderr, "vol: %s: %s\n",
350 						file1, strerror(errno));
351 			exit(1);
352 		}
353 		in_count += n;
354 	}
355 	if (in_count == 0) exit(0);	/* EOF */
356 	out_count = 0;
357 	while (out_count < in_count) {
358 		n = in_count - out_count;
359 		n = write(fd2, buffer + out_count, n);
360 		if (n < 0) {
361 			fprintf(stderr, "vol: %s: %s\n",
362 						file2, strerror(errno));
363 			exit(1);
364 		}
365 		out_count += n;
366 	}
367 	needed -= in_count;
368   }
369 }
370