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>
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. */
43 #define MULT_MAX 1048576
45 #define MULT_MAX ((ssize_t) (SSIZE_MAX < 65536L ? SSIZE_MAX : 65536L))
49 size_t block_size
= 0, mult_max
= 0;
53 int rflag
= 0, wflag
= 0, oneflag
= 0, variable
= 0;
55 int main(int argc
, char **argv
);
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
);
66 int volume
= 1, fd
, tty
, i
, init
, autovolsize
;
69 struct partition part
;
72 /* Fetch and verify the arguments. */
74 while (i
< argc
&& argv
[i
][0] == '-') {
76 if (p
[0] == '-' && p
[1] == 0) {
95 if (i
== argc
) usage();
98 block_size
= str2size("block", p
,
99 1L, (long) SSIZE_MAX
, 0);
104 if (i
== argc
) usage();
107 mult_max
= str2size("maximum", p
,
108 1L, (long) SSIZE_MAX
, 0);
117 str_vol_size
= argv
[i
++];
118 volume_size
= str2size("volume", str_vol_size
, 1L, LONG_MAX
, 1);
121 volume_size
= 0; /* unlimited (long tape) or use DIOCGETP */
125 if (i
>= argc
) usage();
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
);
141 if (stat(name
, &stb
) < 0) {
142 fprintf(stderr
, "vol: %s: %s\n", name
, strerror(errno
));
145 if (!S_ISBLK(stb
.st_mode
) && !S_ISCHR(stb
.st_mode
)) {
146 fprintf(stderr
, "vol: %s is not a device\n", name
);
149 variable
= !S_ISCHR(stb
.st_mode
);
152 tty
= open("/dev/tty", O_RDONLY
);
154 fprintf(stderr
, "vol: cannot open /dev/tty\n");
159 /* Buffer initializations are yet to be done. */
168 "vol: can't continue, volume is full\n");
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
);
181 fprintf(stderr
, "vol: %s: %s\n", name
, strerror(errno
));
186 /* Ask for the tape block size and allocate a buffer. */
187 if (S_ISCHR(stb
.st_mode
)) tape_inquire(name
, fd
);
193 /* Ask the driver how big the volume is. */
194 if (ioctl(fd
, DIOCGETP
, &part
) < 0) {
197 volume_size
= cv64ul(part
.size
);
201 /* Read or write the requisite number of blocks. */
203 diskio(fd
, 1, name
, "stdout"); /* vol -r | tar xf - */
205 diskio(0, fd
, "stdin", name
); /* tar cf - | vol -w */
215 "Usage: vol [-rw1] [-b blocksize] [-m max] [size] block-special\n");
219 long str2size(name
, str
, min
, max
, 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.
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) {
241 factor
= 1024*1024L; break;
244 factor
= 1024; break;
254 if (size
<= max
/ factor
) size
*= factor
; else bad
= 1;
257 fprintf(stderr
, "vol: bad %s size '%s'\n", name
, str
);
263 void tape_inquire(name
, fd
)
267 /* If the device happens to be a tape, then what is its block size? */
270 if (ioctl(fd
, MTIOCGET
, &mtget
) < 0) {
271 if (errno
!= ENOTTY
) {
272 fprintf(stderr
, "vol: %s: %s\n", name
,
277 if (mtget
.mt_blksize
> SSIZE_MAX
) {
279 "vol: %s: tape block size (%lu) is too large to handle\n",
280 name
, (unsigned long) mtget
.mt_blksize
);
283 if (mtget
.mt_blksize
== 0) {
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) {
304 "vol: volume size (%s) is not a multiple of the block size (%lu)\n",
305 str_vol_size
, (unsigned long) block_size
);
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);
317 void diskio(fd1
, fd2
, 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
;
330 if (volume_size
== 0) needed
= buffer_size
;
332 if (needed
== 0) break;
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
);
341 if ((n
= in_count
% block_size
) > 0) {
343 memset(buffer
+ in_count
, '\0', n
);
344 if ((in_count
+= n
) > in_needed
)
345 in_count
= in_needed
;
350 fprintf(stderr
, "vol: %s: %s\n",
351 file1
, strerror(errno
));
356 if (in_count
== 0) exit(0); /* EOF */
358 while (out_count
< in_count
) {
359 n
= in_count
- out_count
;
360 n
= write(fd2
, buffer
+ out_count
, n
);
362 fprintf(stderr
, "vol: %s: %s\n",
363 file2
, strerror(errno
));