source: trunk/minix/commands/simple/vol.c@ 20

Last change on this file since 20 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

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