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,
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>
27 #include <sys/ioctl.h>
29 #include <minix/partition.h>
31 /* Preferred block size to variable block length tapes, block devices or files.
33 #define VAR_BLKSIZ 8192
35 /* Required block size multiple of fixed block size tapes (usually updated by
36 * 'mt status' data) and character devices.
38 #define FIX_BLKSIZ 512
40 /* Maximum multiple block size. */
42 #define MULT_MAX 1048576
44 #define MULT_MAX ((ssize_t) (SSIZE_MAX < 65536L ? SSIZE_MAX : 65536L))
48 size_t block_size
= 0, mult_max
= 0;
52 int rflag
= 0, wflag
= 0, oneflag
= 0, variable
= 0;
54 int main(int argc
, char **argv
);
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
);
65 int volume
= 1, fd
, tty
, i
, init
, autovolsize
;
68 struct part_geom part
;
71 /* Fetch and verify the arguments. */
73 while (i
< argc
&& argv
[i
][0] == '-') {
75 if (p
[0] == '-' && p
[1] == 0) {
94 if (i
== argc
) usage();
97 block_size
= str2size("block", p
,
98 1L, (long) SSIZE_MAX
, 0);
103 if (i
== argc
) usage();
106 mult_max
= str2size("maximum", p
,
107 1L, (long) SSIZE_MAX
, 0);
116 str_vol_size
= argv
[i
++];
117 volume_size
= str2size("volume", str_vol_size
, 1L, LONG_MAX
, 1);
120 volume_size
= 0; /* unlimited (long tape) or use DIOCGETP */
124 if (i
>= argc
) usage();
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.
131 if (isatty(0)) rflag
= 1;
132 if (isatty(1)) wflag
= 1;
135 if (rflag
== wflag
) {
136 fprintf(stderr
, "vol: should %s be read or written?\n", name
);
140 if (stat(name
, &stb
) < 0) {
141 fprintf(stderr
, "vol: %s: %s\n", name
, strerror(errno
));
144 if (!S_ISBLK(stb
.st_mode
) && !S_ISCHR(stb
.st_mode
)) {
145 fprintf(stderr
, "vol: %s is not a device\n", name
);
148 variable
= !S_ISCHR(stb
.st_mode
);
151 tty
= open("/dev/tty", O_RDONLY
);
153 fprintf(stderr
, "vol: cannot open /dev/tty\n");
158 /* Buffer initializations are yet to be done. */
167 "vol: can't continue, volume is full\n");
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') {}
177 /* Open the special file. */
178 fd
= open(name
, rflag
? O_RDONLY
: O_WRONLY
);
180 fprintf(stderr
, "vol: %s: %s\n", name
, strerror(errno
));
185 /* Ask for the tape block size and allocate a buffer. */
186 if (S_ISCHR(stb
.st_mode
)) tape_inquire(name
, fd
);
192 /* Ask the driver how big the volume is. */
193 if (ioctl(fd
, DIOCGETP
, &part
) < 0) {
196 volume_size
= part
.size
;
200 /* Read or write the requisite number of blocks. */
202 diskio(fd
, 1, name
, "stdout"); /* vol -r | tar xf - */
204 diskio(0, fd
, "stdin", name
); /* tar cf - | vol -w */
214 "Usage: vol [-rw1] [-b blocksize] [-m max] [size] block-special\n");
218 long str2size(name
, str
, min
, max
, assume_kb
)
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.
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) {
240 factor
= 1024*1024L; break;
243 factor
= 1024; break;
253 if (size
<= max
/ factor
) size
*= factor
; else bad
= 1;
256 fprintf(stderr
, "vol: bad %s size '%s'\n", name
, str
);
262 void tape_inquire(name
, fd
)
266 /* If the device happens to be a tape, then what is its block size? */
269 if (ioctl(fd
, MTIOCGET
, &mtget
) < 0) {
270 if (errno
!= ENOTTY
) {
271 fprintf(stderr
, "vol: %s: %s\n", name
,
276 if (mtget
.mt_blksiz
> SSIZE_MAX
) {
278 "vol: %s: tape block size (%lu) is too large to handle\n",
279 name
, (unsigned long) mtget
.mt_blksiz
);
282 if (mtget
.mt_blksiz
== 0) {
286 block_size
= mtget
.mt_blksiz
;
291 void allocate_buffer()
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
;
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
;
301 if (volume_size
% block_size
!= 0) {
303 "vol: volume size (%s) is not a multiple of the block size (%lu)\n",
304 str_vol_size
, (unsigned long) block_size
);
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);
316 void diskio(fd1
, fd2
, file1
, file2
)
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.
324 ssize_t n
, in_needed
, in_count
, out_count
;
325 long needed
= volume_size
;
329 if (volume_size
== 0) needed
= buffer_size
;
331 if (needed
== 0) break;
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
);
340 if ((n
= in_count
% block_size
) > 0) {
342 memset(buffer
+ in_count
, '\0', n
);
343 if ((in_count
+= n
) > in_needed
)
344 in_count
= in_needed
;
349 fprintf(stderr
, "vol: %s: %s\n",
350 file1
, strerror(errno
));
355 if (in_count
== 0) exit(0); /* EOF */
357 while (out_count
< in_count
) {
358 n
= in_count
- out_count
;
359 n
= write(fd2
, buffer
+ out_count
, n
);
361 fprintf(stderr
, "vol: %s: %s\n",
362 file2
, strerror(errno
));