[9] | 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 |
|
---|
| 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;
|
---|
| 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 |
|
---|
| 63 | int main(argc, argv)
|
---|
| 64 | int argc;
|
---|
| 65 | char *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 |
|
---|
| 213 | void usage()
|
---|
| 214 | {
|
---|
| 215 | fprintf(stderr,
|
---|
| 216 | "Usage: vol [-rw1] [-b blocksize] [-m max] [size] block-special\n");
|
---|
| 217 | exit(1);
|
---|
| 218 | }
|
---|
| 219 |
|
---|
| 220 | long str2size(name, str, min, max, assume_kb)
|
---|
| 221 | char *name;
|
---|
| 222 | char *str;
|
---|
| 223 | long min, max;
|
---|
| 224 | int 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 |
|
---|
| 264 | void tape_inquire(name, fd)
|
---|
| 265 | char *name;
|
---|
| 266 | int 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 |
|
---|
| 293 | void 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 |
|
---|
| 318 | void diskio(fd1, fd2, file1, file2)
|
---|
| 319 | int fd1, fd2;
|
---|
| 320 | char *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 | }
|
---|