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