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