coverity appeasement
[minix.git] / commands / vol / vol.c
blobebede7900008e88a16820aeb1f533c12050ebc10
1 /* vol - break stdin into volumes Author: Andy Tanenbaum */
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 -
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.
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 #include <minix/u64.h>
32 /* Preferred block size to variable block length tapes, block devices or files.
34 #define VAR_BLKSIZ 8192
36 /* Required block size multiple of fixed block size tapes (usually updated by
37 * 'mt status' data) and character devices.
39 #define FIX_BLKSIZ 512
41 /* Maximum multiple block size. */
42 #if __minix_vmd
43 #define MULT_MAX 1048576
44 #else
45 #define MULT_MAX ((ssize_t) (SSIZE_MAX < 65536L ? SSIZE_MAX : 65536L))
46 #endif
48 char *buffer = NULL;
49 size_t block_size = 0, mult_max = 0;
50 size_t buffer_size;
51 long volume_size;
52 char *str_vol_size;
53 int rflag = 0, wflag = 0, oneflag = 0, variable = 0;
55 int main(int argc, char **argv);
56 void usage(void);
57 long str2size(char *name, char *str, long min, long max, int assume_kb);
58 void tape_inquire(char *name, int fd);
59 void allocate_buffer(void);
60 void diskio(int fd1, int fd2, char *file1, char *file2);
62 int main(argc, argv)
63 int argc;
64 char *argv[];
66 int volume = 1, fd, tty, i, init, autovolsize;
67 char *p, *name;
68 struct stat stb;
69 struct partition part;
70 char key;
72 /* Fetch and verify the arguments. */
73 i = 1;
74 while (i < argc && argv[i][0] == '-') {
75 p = argv[i++] + 1;
76 if (p[0] == '-' && p[1] == 0) {
77 /* -- */
78 i++;
79 break;
81 while (*p != '\0') {
82 switch (*p++) {
83 case 'r':
84 case 'u':
85 rflag = 1;
86 break;
87 case 'w':
88 wflag = 1;
89 break;
90 case '1':
91 oneflag = 1;
92 break;
93 case 'b':
94 if (*p == 0) {
95 if (i == argc) usage();
96 p = argv[i++];
98 block_size = str2size("block", p,
99 1L, (long) SSIZE_MAX, 0);
100 p= "";
101 break;
102 case 'm':
103 if (*p == 0) {
104 if (i == argc) usage();
105 p = argv[i++];
107 mult_max = str2size("maximum", p,
108 1L, (long) SSIZE_MAX, 0);
109 p= "";
110 break;
111 default:
112 usage();
116 if (i < argc - 1) {
117 str_vol_size = argv[i++];
118 volume_size = str2size("volume", str_vol_size, 1L, LONG_MAX, 1);
119 autovolsize = 0;
120 } else {
121 volume_size = 0; /* unlimited (long tape) or use DIOCGETP */
122 autovolsize = 1;
125 if (i >= argc) usage();
126 name = argv[i];
128 if (!rflag && !wflag) {
129 /* Auto direction. If there is a terminal at one side then data is
130 * to go out at the other side.
132 if (isatty(0)) rflag = 1;
133 if (isatty(1)) wflag = 1;
136 if (rflag == wflag) {
137 fprintf(stderr, "vol: should %s be read or written?\n", name);
138 usage();
141 if (stat(name, &stb) < 0) {
142 fprintf(stderr, "vol: %s: %s\n", name, strerror(errno));
143 exit(1);
145 if (!S_ISBLK(stb.st_mode) && !S_ISCHR(stb.st_mode)) {
146 fprintf(stderr, "vol: %s is not a device\n", name);
147 exit(1);
149 variable = !S_ISCHR(stb.st_mode);
151 if (!oneflag) {
152 tty = open("/dev/tty", O_RDONLY);
153 if (tty < 0) {
154 fprintf(stderr, "vol: cannot open /dev/tty\n");
155 exit(1);
159 /* Buffer initializations are yet to be done. */
160 init = 0;
162 while (1) {
163 sleep(1);
164 if (oneflag) {
165 if (volume != 1) {
166 if (rflag) exit(0);
167 fprintf(stderr,
168 "vol: can't continue, volume is full\n");
169 exit(1);
171 } else {
172 fprintf(stderr,
173 "\007Please insert %sput volume %d and hit return\n",
174 rflag ? "in" : "out", volume);
175 while (read(tty, &key, sizeof(key)) == 1 && key != '\n') {}
178 /* Open the special file. */
179 fd = open(name, rflag ? O_RDONLY : O_WRONLY);
180 if (fd < 0) {
181 fprintf(stderr, "vol: %s: %s\n", name, strerror(errno));
182 exit(1);
185 if (!init) {
186 /* Ask for the tape block size and allocate a buffer. */
187 if (S_ISCHR(stb.st_mode)) tape_inquire(name, fd);
188 allocate_buffer();
189 init = 1;
192 if (autovolsize) {
193 /* Ask the driver how big the volume is. */
194 if (ioctl(fd, DIOCGETP, &part) < 0) {
195 autovolsize = 0;
196 } else {
197 volume_size = cv64ul(part.size);
201 /* Read or write the requisite number of blocks. */
202 if (rflag) {
203 diskio(fd, 1, name, "stdout"); /* vol -r | tar xf - */
204 } else {
205 diskio(0, fd, "stdin", name); /* tar cf - | vol -w */
207 close(fd);
208 volume++;
212 void usage()
214 fprintf(stderr,
215 "Usage: vol [-rw1] [-b blocksize] [-m max] [size] block-special\n");
216 exit(1);
219 long str2size(name, str, min, max, assume_kb)
220 char *name;
221 char *str;
222 long min, max;
223 int assume_kb;
225 /* Convert a string to a size. The number may be followed by 'm', 'k', 'b'
226 * or 'w' to multiply the size as shown below. If 'assume_kb' is set then
227 * kilobytes is the default.
229 long size, factor;
230 char *ptr;
231 int bad;
233 errno = 0;
234 size = strtol(str, &ptr, 10);
235 bad = (errno != 0 || ptr == str || size < min || size > max);
236 if (*ptr == 0 && assume_kb) ptr = "k";
237 while (!bad && *ptr != 0) {
238 switch (*ptr++) {
239 case 'm':
240 case 'M':
241 factor = 1024*1024L; break;
242 case 'k':
243 case 'K':
244 factor = 1024; break;
245 case 'b':
246 case 'B':
247 factor = 512; break;
248 case 'w':
249 case 'W':
250 factor = 2; break;
251 default:
252 factor = 1; bad = 1;
254 if (size <= max / factor) size *= factor; else bad = 1;
256 if (bad) {
257 fprintf(stderr, "vol: bad %s size '%s'\n", name, str);
258 exit(1);
260 return size;
263 void tape_inquire(name, fd)
264 char *name;
265 int fd;
267 /* If the device happens to be a tape, then what is its block size? */
268 struct mtget mtget;
270 if (ioctl(fd, MTIOCGET, &mtget) < 0) {
271 if (errno != ENOTTY) {
272 fprintf(stderr, "vol: %s: %s\n", name,
273 strerror(errno));
274 exit(1);
276 } else {
277 if (mtget.mt_blksize > SSIZE_MAX) {
278 fprintf(stderr,
279 "vol: %s: tape block size (%lu) is too large to handle\n",
280 name, (unsigned long) mtget.mt_blksize);
281 exit(1);
283 if (mtget.mt_blksize == 0) {
284 variable = 1;
285 } else {
286 /* fixed */
287 block_size = mtget.mt_blksize;
292 void allocate_buffer()
294 /* Set block size and maximum multiple. */
295 if (block_size == 0) block_size = variable ? 1 : FIX_BLKSIZ;
296 if (mult_max == 0) mult_max = variable ? VAR_BLKSIZ : MULT_MAX;
298 /* Stretch the buffer size to the max. */
299 buffer_size = mult_max / block_size * block_size;
300 if (buffer_size == 0) buffer_size = block_size;
302 if (volume_size % block_size != 0) {
303 fprintf(stderr,
304 "vol: volume size (%s) is not a multiple of the block size (%lu)\n",
305 str_vol_size, (unsigned long) block_size);
306 exit(1);
309 buffer = (char *) malloc(buffer_size);
310 if (buffer == NULL) {
311 fprintf(stderr, "vol: cannot allocate a %luk buffer\n",
312 (unsigned long) buffer_size / 1024);
313 exit(1);
317 void diskio(fd1, fd2, file1, file2)
318 int fd1, fd2;
319 char *file1, *file2;
321 /* Read 'volume_size' bytes from 'fd1' and write them on 'fd2'. Watch out for
322 * the fact that reads on pipes can return less than the desired data.
325 ssize_t n, in_needed, in_count, out_count;
326 long needed = volume_size;
327 int eof = 0;
329 for (;;) {
330 if (volume_size == 0) needed = buffer_size;
332 if (needed == 0) break;
334 in_count = 0;
335 in_needed = needed > buffer_size ? buffer_size : needed;
336 while (in_count < in_needed) {
337 n = in_needed - in_count;
338 n = eof ? 0 : read(fd1, buffer + in_count, n);
339 if (n == 0) {
340 eof = 1;
341 if ((n = in_count % block_size) > 0) {
342 n = block_size - n;
343 memset(buffer + in_count, '\0', n);
344 if ((in_count += n) > in_needed)
345 in_count = in_needed;
347 break;
349 if (n < 0) {
350 fprintf(stderr, "vol: %s: %s\n",
351 file1, strerror(errno));
352 exit(1);
354 in_count += n;
356 if (in_count == 0) exit(0); /* EOF */
357 out_count = 0;
358 while (out_count < in_count) {
359 n = in_count - out_count;
360 n = write(fd2, buffer + out_count, n);
361 if (n < 0) {
362 fprintf(stderr, "vol: %s: %s\n",
363 file2, strerror(errno));
364 exit(1);
366 out_count += n;
368 needed -= in_count;