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 _PROTOTYPE(int main
, (int argc
, char **argv
));
56 _PROTOTYPE(void usage
, (void));
57 _PROTOTYPE(long str2size
, (char *name
, char *str
, long min
, long max
,
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
));
67 int volume
= 1, fd
, tty
, i
, init
, autovolsize
;
70 struct partition part
;
73 /* Fetch and verify the arguments. */
75 while (i
< argc
&& argv
[i
][0] == '-') {
77 if (p
[0] == '-' && p
[1] == 0) {
96 if (i
== argc
) usage();
99 block_size
= str2size("block", p
,
100 1L, (long) SSIZE_MAX
, 0);
105 if (i
== argc
) usage();
108 mult_max
= str2size("maximum", p
,
109 1L, (long) SSIZE_MAX
, 0);
118 str_vol_size
= argv
[i
++];
119 volume_size
= str2size("volume", str_vol_size
, 1L, LONG_MAX
, 1);
122 volume_size
= 0; /* unlimited (long tape) or use DIOCGETP */
126 if (i
>= argc
) usage();
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.
133 if (isatty(0)) rflag
= 1;
134 if (isatty(1)) wflag
= 1;
137 if (rflag
== wflag
) {
138 fprintf(stderr
, "vol: should %s be read or written?\n", name
);
142 if (stat(name
, &stb
) < 0) {
143 fprintf(stderr
, "vol: %s: %s\n", name
, strerror(errno
));
146 if (!S_ISBLK(stb
.st_mode
) && !S_ISCHR(stb
.st_mode
)) {
147 fprintf(stderr
, "vol: %s is not a device\n", name
);
150 variable
= !S_ISCHR(stb
.st_mode
);
153 tty
= open("/dev/tty", O_RDONLY
);
155 fprintf(stderr
, "vol: cannot open /dev/tty\n");
160 /* Buffer initializations are yet to be done. */
169 "vol: can't continue, volume is full\n");
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') {}
179 /* Open the special file. */
180 fd
= open(name
, rflag
? O_RDONLY
: O_WRONLY
);
182 fprintf(stderr
, "vol: %s: %s\n", name
, strerror(errno
));
187 /* Ask for the tape block size and allocate a buffer. */
188 if (S_ISCHR(stb
.st_mode
)) tape_inquire(name
, fd
);
194 /* Ask the driver how big the volume is. */
195 if (ioctl(fd
, DIOCGETP
, &part
) < 0) {
198 volume_size
= cv64ul(part
.size
);
202 /* Read or write the requisite number of blocks. */
204 diskio(fd
, 1, name
, "stdout"); /* vol -r | tar xf - */
206 diskio(0, fd
, "stdin", name
); /* tar cf - | vol -w */
216 "Usage: vol [-rw1] [-b blocksize] [-m max] [size] block-special\n");
220 long str2size(name
, str
, min
, max
, assume_kb
)
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.
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) {
242 factor
= 1024*1024L; break;
245 factor
= 1024; break;
255 if (size
<= max
/ factor
) size
*= factor
; else bad
= 1;
258 fprintf(stderr
, "vol: bad %s size '%s'\n", name
, str
);
264 void tape_inquire(name
, fd
)
268 /* If the device happens to be a tape, then what is its block size? */
271 if (ioctl(fd
, MTIOCGET
, &mtget
) < 0) {
272 if (errno
!= ENOTTY
) {
273 fprintf(stderr
, "vol: %s: %s\n", name
,
278 if (mtget
.mt_blksize
> SSIZE_MAX
) {
280 "vol: %s: tape block size (%lu) is too large to handle\n",
281 name
, (unsigned long) mtget
.mt_blksize
);
284 if (mtget
.mt_blksize
== 0) {
288 block_size
= mtget
.mt_blksize
;
293 void allocate_buffer()
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
;
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
;
303 if (volume_size
% block_size
!= 0) {
305 "vol: volume size (%s) is not a multiple of the block size (%lu)\n",
306 str_vol_size
, (unsigned long) block_size
);
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);
318 void diskio(fd1
, fd2
, file1
, file2
)
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.
326 ssize_t n
, in_needed
, in_count
, out_count
;
327 long needed
= volume_size
;
331 if (volume_size
== 0) needed
= buffer_size
;
333 if (needed
== 0) break;
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
);
342 if ((n
= in_count
% block_size
) > 0) {
344 memset(buffer
+ in_count
, '\0', n
);
345 if ((in_count
+= n
) > in_needed
)
346 in_count
= in_needed
;
351 fprintf(stderr
, "vol: %s: %s\n",
352 file1
, strerror(errno
));
357 if (in_count
== 0) exit(0); /* EOF */
359 while (out_count
< in_count
) {
360 n
= in_count
- out_count
;
361 n
= write(fd2
, buffer
+ out_count
, n
);
363 fprintf(stderr
, "vol: %s: %s\n",
364 file2
, strerror(errno
));