1 /* 2 * Copyright (c) 1993 University of Utah. 3 * Copyright (c) 1990, 1993 4 * The Regents of the University of California. All rights reserved. 5 * 6 * This code is derived from software contributed to Berkeley by 7 * the Systems Programming Group of the University of Utah Computer 8 * Science Department. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. All advertising materials mentioning features or use of this software 19 * must display the following acknowledgement: 20 * This product includes software developed by the University of 21 * California, Berkeley and its contributors. 22 * 4. Neither the name of the University nor the names of its contributors 23 * may be used to endorse or promote products derived from this software 24 * without specific prior written permission. 25 * 26 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 29 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 36 * SUCH DAMAGE. 37 * 38 * from: Utah $Hdr: vnconfig.c 1.1 93/12/15$ 39 */ 40 41 #ifndef lint 42 #if 0 43 static char sccsid[] = "@(#)vnconfig.c 8.1 (Berkeley) 12/15/93"; 44 #endif 45 static const char rcsid[] = 46 "$FreeBSD: src/usr.sbin/vnconfig/vnconfig.c,v 1.13.2.7 2003/06/02 09:10:27 maxim Exp $"; 47 #endif /* not lint */ 48 49 #include <ctype.h> 50 #include <err.h> 51 #include <errno.h> 52 #include <paths.h> 53 #include <stdio.h> 54 #include <stdlib.h> 55 #include <fcntl.h> 56 #include <string.h> 57 #include <unistd.h> 58 #include <sys/param.h> 59 #include <sys/ioctl.h> 60 #include <sys/linker.h> 61 #include <sys/mount.h> 62 #include <sys/module.h> 63 #include <sys/stat.h> 64 #include <sys/vnioctl.h> 65 #include <ufs/ufs/ufsmount.h> 66 67 #define LINESIZE 1024 68 #define ZBUFSIZE 32768 69 70 struct vndisk { 71 char *dev; 72 char *file; 73 char *autolabel; 74 int flags; 75 int size; 76 char *oarg; 77 } *vndisks; 78 79 #define VN_CONFIG 0x01 80 #define VN_UNCONFIG 0x02 81 #define VN_ENABLE 0x04 82 #define VN_DISABLE 0x08 83 #define VN_SWAP 0x10 84 #define VN_MOUNTRO 0x20 85 #define VN_MOUNTRW 0x40 86 #define VN_IGNORE 0x80 87 #define VN_SET 0x100 88 #define VN_RESET 0x200 89 #define VN_TRUNCATE 0x400 90 #define VN_ZERO 0x800 91 92 int nvndisks; 93 94 int all = 0; 95 int verbose = 0; 96 int global = 0; 97 u_long setopt = 0; 98 u_long resetopt = 0; 99 char *configfile; 100 101 int config __P((struct vndisk *)); 102 void getoptions __P((struct vndisk *, char *)); 103 char *rawdevice __P((char *)); 104 void readconfig __P((int)); 105 static void usage __P((void)); 106 static int getsize(const char *arg); 107 static void do_autolabel(const char *dev, const char *label); 108 int what_opt __P((char *, u_long *)); 109 110 int 111 main(argc, argv) 112 char **argv; 113 { 114 register int i, rv; 115 int flags = 0; 116 int size = 0; 117 char *autolabel = NULL; 118 char *s; 119 120 configfile = _PATH_VNTAB; 121 while ((i = getopt(argc, argv, "acdef:gr:s:S:TZL:uv")) != -1) 122 switch (i) { 123 124 /* all -- use config file */ 125 case 'a': 126 all++; 127 break; 128 129 /* configure */ 130 case 'c': 131 flags |= VN_CONFIG; 132 flags &= ~VN_UNCONFIG; 133 break; 134 135 /* disable */ 136 case 'd': 137 flags |= VN_DISABLE; 138 flags &= ~VN_ENABLE; 139 break; 140 141 /* enable */ 142 case 'e': 143 flags |= (VN_ENABLE|VN_CONFIG); 144 flags &= ~(VN_DISABLE|VN_UNCONFIG); 145 break; 146 147 /* alternate config file */ 148 case 'f': 149 configfile = optarg; 150 break; 151 152 /* fiddle global options */ 153 case 'g': 154 global = 1 - global; 155 break; 156 157 /* reset options */ 158 case 'r': 159 for (s = strtok(optarg, ","); s; s = strtok(NULL, ",")) { 160 if (what_opt(s, &resetopt)) 161 errx(1, "invalid options '%s'", s); 162 } 163 flags |= VN_RESET; 164 break; 165 166 /* set options */ 167 case 's': 168 for (s = strtok(optarg, ","); s; s = strtok(NULL, ",")) { 169 if (what_opt(s, &setopt)) 170 errx(1, "invalid options '%s'", s); 171 } 172 flags |= VN_SET; 173 break; 174 175 /* unconfigure */ 176 case 'u': 177 flags |= (VN_DISABLE|VN_UNCONFIG); 178 flags &= ~(VN_ENABLE|VN_CONFIG); 179 break; 180 181 /* verbose */ 182 case 'v': 183 verbose++; 184 break; 185 186 case 'S': 187 size = getsize(optarg); 188 flags |= VN_CONFIG; 189 flags &= ~VN_UNCONFIG; 190 break; 191 192 case 'T': 193 flags |= VN_TRUNCATE; 194 break; 195 196 case 'Z': 197 flags |= VN_ZERO; 198 break; 199 200 case 'L': 201 autolabel = optarg; 202 break; 203 204 default: 205 usage(); 206 } 207 208 if (modfind("vn") < 0) 209 if (kldload("vn") < 0 || modfind("vn") < 0) 210 warnx( "cannot find or load \"vn\" kernel module"); 211 212 if (flags == 0) 213 flags = VN_CONFIG; 214 if (all) { 215 readconfig(flags); 216 } else { 217 vndisks = calloc(sizeof(struct vndisk), 1); 218 if (argc < optind + 1) 219 usage(); 220 vndisks[0].dev = argv[optind++]; 221 vndisks[0].file = argv[optind++]; /* may be NULL */ 222 vndisks[0].flags = flags; 223 vndisks[0].size = size; 224 vndisks[0].autolabel = autolabel; 225 if (optind < argc) 226 getoptions(&vndisks[0], argv[optind]); 227 nvndisks = 1; 228 } 229 rv = 0; 230 for (i = 0; i < nvndisks; i++) 231 rv += config(&vndisks[i]); 232 exit(rv); 233 } 234 235 int 236 what_opt(str,p) 237 char *str; 238 u_long *p; 239 { 240 if (!strcmp(str,"reserve")) { *p |= VN_RESERVE; return 0; } 241 if (!strcmp(str,"labels")) { *p |= VN_LABELS; return 0; } 242 if (!strcmp(str,"follow")) { *p |= VN_FOLLOW; return 0; } 243 if (!strcmp(str,"debug")) { *p |= VN_DEBUG; return 0; } 244 if (!strcmp(str,"io")) { *p |= VN_IO; return 0; } 245 if (!strcmp(str,"all")) { *p |= ~0; return 0; } 246 if (!strcmp(str,"none")) { *p |= 0; return 0; } 247 return 1; 248 } 249 250 int 251 config(vnp) 252 struct vndisk *vnp; 253 { 254 char *dev, *file, *oarg; 255 int flags, status; 256 struct vn_ioctl vnio; 257 register int rv; 258 char *rdev; 259 FILE *f; 260 u_long l; 261 int pgsize = getpagesize(); 262 263 status = rv = 0; 264 265 /* 266 * Prepend "/dev/" to the specified device name, if necessary. 267 * Operate on vnp->dev because it is used later. 268 */ 269 if (vnp->dev[0] != '/' && vnp->dev[0] != '.') 270 (void)asprintf(&vnp->dev, "%s%s", _PATH_DEV, vnp->dev); 271 dev = vnp->dev; 272 file = vnp->file; 273 flags = vnp->flags; 274 oarg = vnp->oarg; 275 276 if (flags & VN_IGNORE) 277 return(0); 278 279 /* 280 * When a regular file has been specified, do any requested setup 281 * of the file. Truncation (also creates the file if necessary), 282 * sizing, and zeroing. 283 */ 284 285 if (file && vnp->size != 0 && (flags & VN_CONFIG)) { 286 int fd; 287 struct stat st; 288 289 if (flags & VN_TRUNCATE) 290 fd = open(file, O_RDWR|O_CREAT|O_TRUNC); 291 else 292 fd = open(file, O_RDWR); 293 if (fd >= 0 && fstat(fd, &st) == 0 && S_ISREG(st.st_mode)) { 294 if (st.st_size < (off_t)vnp->size * pgsize) 295 ftruncate(fd, (off_t)vnp->size * pgsize); 296 if (vnp->size != 0) 297 st.st_size = (off_t)vnp->size * pgsize; 298 299 if (flags & VN_ZERO) { 300 char *buf = malloc(ZBUFSIZE); 301 bzero(buf, ZBUFSIZE); 302 while (st.st_size > 0) { 303 int n = (st.st_size > ZBUFSIZE) ? 304 ZBUFSIZE : (int)st.st_size; 305 if (write(fd, buf, n) != n) { 306 ftruncate(fd, 0); 307 printf("Unable to ZERO file %s\n", file); 308 return(0); 309 } 310 st.st_size -= (off_t)n; 311 } 312 } 313 close(fd); 314 } else { 315 printf("Unable to open file %s\n", file); 316 return(0); 317 } 318 } 319 320 rdev = rawdevice(dev); 321 f = fopen(rdev, "rw"); 322 if (f == NULL) { 323 warn("%s", dev); 324 return(1); 325 } 326 vnio.vn_file = file; 327 vnio.vn_size = vnp->size; /* non-zero only if swap backed */ 328 329 /* 330 * Disable the device 331 */ 332 if (flags & VN_DISABLE) { 333 if (flags & (VN_MOUNTRO|VN_MOUNTRW)) { 334 rv = unmount(oarg, 0); 335 if (rv) { 336 status--; 337 if (errno == EBUSY) 338 flags &= ~VN_UNCONFIG; 339 if ((flags & VN_UNCONFIG) == 0) 340 warn("umount"); 341 } else if (verbose) 342 printf("%s: unmounted\n", dev); 343 } 344 } 345 /* 346 * Clear (un-configure) the device 347 */ 348 if (flags & VN_UNCONFIG) { 349 rv = ioctl(fileno(f), VNIOCDETACH, &vnio); 350 if (rv) { 351 if (errno == ENODEV) { 352 if (verbose) 353 printf("%s: not configured\n", dev); 354 rv = 0; 355 } else { 356 status--; 357 warn("VNIOCDETACH"); 358 } 359 } else if (verbose) 360 printf("%s: cleared\n", dev); 361 } 362 /* 363 * Set specified options 364 */ 365 if (flags & VN_SET) { 366 l = setopt; 367 if (global) 368 rv = ioctl(fileno(f), VNIOCGSET, &l); 369 else 370 rv = ioctl(fileno(f), VNIOCUSET, &l); 371 if (rv) { 372 status--; 373 warn("VNIO[GU]SET"); 374 } else if (verbose) 375 printf("%s: flags now=%08x\n",dev,l); 376 } 377 /* 378 * Reset specified options 379 */ 380 if (flags & VN_RESET) { 381 l = resetopt; 382 if (global) 383 rv = ioctl(fileno(f), VNIOCGCLEAR, &l); 384 else 385 rv = ioctl(fileno(f), VNIOCUCLEAR, &l); 386 if (rv) { 387 status--; 388 warn("VNIO[GU]CLEAR"); 389 } else if (verbose) 390 printf("%s: flags now=%08x\n",dev,l); 391 } 392 /* 393 * Configure the device 394 */ 395 if (flags & VN_CONFIG) { 396 rv = ioctl(fileno(f), VNIOCATTACH, &vnio); 397 if (rv) { 398 status--; 399 warn("VNIOCATTACH"); 400 flags &= ~VN_ENABLE; 401 } else { 402 if (verbose) { 403 printf( 404 "%s: %d bytes on %s\n", 405 dev, vnio.vn_size, file 406 ); 407 } 408 /* 409 * autolabel 410 */ 411 if (vnp->autolabel) { 412 do_autolabel(vnp->dev, vnp->autolabel); 413 } 414 } 415 } 416 /* 417 * Set an option 418 */ 419 if (flags & VN_SET) { 420 l = setopt; 421 if (global) 422 rv = ioctl(fileno(f), VNIOCGSET, &l); 423 else 424 rv = ioctl(fileno(f), VNIOCUSET, &l); 425 if (rv) { 426 status--; 427 warn("VNIO[GU]SET"); 428 } else if (verbose) 429 printf("%s: flags now=%08lx\n",dev,l); 430 } 431 /* 432 * Reset an option 433 */ 434 if (flags & VN_RESET) { 435 l = resetopt; 436 if (global) 437 rv = ioctl(fileno(f), VNIOCGCLEAR, &l); 438 else 439 rv = ioctl(fileno(f), VNIOCUCLEAR, &l); 440 if (rv) { 441 status--; 442 warn("VNIO[GU]CLEAR"); 443 } else if (verbose) 444 printf("%s: flags now=%08lx\n",dev,l); 445 } 446 447 /* 448 * Close the device now, as we may want to mount it. 449 */ 450 fclose(f); 451 452 /* 453 * Enable special functions on the device 454 */ 455 if (flags & VN_ENABLE) { 456 if (flags & VN_SWAP) { 457 rv = swapon(dev); 458 if (rv) { 459 status--; 460 warn("swapon"); 461 } 462 else if (verbose) 463 printf("%s: swapping enabled\n", dev); 464 } 465 if (flags & (VN_MOUNTRO|VN_MOUNTRW)) { 466 struct ufs_args args; 467 int mflags; 468 469 args.fspec = dev; 470 mflags = (flags & VN_MOUNTRO) ? MNT_RDONLY : 0; 471 rv = mount("ufs", oarg, mflags, &args); 472 if (rv) { 473 status--; 474 warn("mount"); 475 } 476 else if (verbose) 477 printf("%s: mounted on %s\n", dev, oarg); 478 } 479 } 480 /* done: */ 481 fflush(stdout); 482 return(status < 0); 483 } 484 485 #define EOL(c) ((c) == '\0' || (c) == '\n') 486 #define WHITE(c) ((c) == ' ' || (c) == '\t' || (c) == '\n') 487 488 void 489 readconfig(flags) 490 int flags; 491 { 492 char buf[LINESIZE]; 493 FILE *f; 494 register char *cp, *sp; 495 register int ix; 496 int ax; 497 498 f = fopen(configfile, "r"); 499 if (f == NULL) 500 err(1, "%s", configfile); 501 ix = 0; /* number of elements */ 502 ax = 0; /* allocated elements */ 503 while (fgets(buf, LINESIZE, f) != NULL) { 504 cp = buf; 505 if (*cp == '#') 506 continue; 507 while (!EOL(*cp) && WHITE(*cp)) 508 cp++; 509 if (EOL(*cp)) 510 continue; 511 sp = cp; 512 while (!EOL(*cp) && !WHITE(*cp)) 513 cp++; 514 if (EOL(*cp)) 515 continue; 516 *cp++ = '\0'; 517 518 if (ix == ax) { 519 ax = ax + 16; 520 vndisks = realloc(vndisks, ax * sizeof(struct vndisk)); 521 bzero(&vndisks[ix], (ax - ix) * sizeof(struct vndisk)); 522 } 523 vndisks[ix].dev = malloc(cp - sp); 524 strcpy(vndisks[ix].dev, sp); 525 while (!EOL(*cp) && WHITE(*cp)) 526 cp++; 527 if (EOL(*cp)) 528 continue; 529 sp = cp; 530 while (!EOL(*cp) && !WHITE(*cp)) 531 cp++; 532 *cp++ = '\0'; 533 534 if (*sp == '%' && strtol(sp + 1, NULL, 0) > 0) { 535 vndisks[ix].size = getsize(sp + 1); 536 } else { 537 vndisks[ix].file = malloc(cp - sp); 538 strcpy(vndisks[ix].file, sp); 539 } 540 541 while (!EOL(*cp) && WHITE(*cp)) 542 cp++; 543 vndisks[ix].flags = flags; 544 if (!EOL(*cp)) { 545 sp = cp; 546 while (!EOL(*cp) && !WHITE(*cp)) 547 cp++; 548 *cp++ = '\0'; 549 getoptions(&vndisks[ix], sp); 550 } 551 nvndisks++; 552 ix++; 553 } 554 } 555 556 void 557 getoptions(vnp, fstr) 558 struct vndisk *vnp; 559 char *fstr; 560 { 561 int flags = 0; 562 char *oarg = NULL; 563 564 if (strcmp(fstr, "swap") == 0) 565 flags |= VN_SWAP; 566 else if (strncmp(fstr, "mount=", 6) == 0) { 567 flags |= VN_MOUNTRW; 568 oarg = &fstr[6]; 569 } else if (strncmp(fstr, "mountrw=", 8) == 0) { 570 flags |= VN_MOUNTRW; 571 oarg = &fstr[8]; 572 } else if (strncmp(fstr, "mountro=", 8) == 0) { 573 flags |= VN_MOUNTRO; 574 oarg = &fstr[8]; 575 } else if (strcmp(fstr, "ignore") == 0) 576 flags |= VN_IGNORE; 577 vnp->flags |= flags; 578 if (oarg) { 579 vnp->oarg = malloc(strlen(oarg) + 1); 580 strcpy(vnp->oarg, oarg); 581 } else 582 vnp->oarg = NULL; 583 } 584 585 char * 586 rawdevice(dev) 587 char *dev; 588 { 589 register char *rawbuf, *dp, *ep; 590 struct stat sb; 591 int len; 592 593 len = strlen(dev); 594 rawbuf = malloc(len + 2); 595 strcpy(rawbuf, dev); 596 if (stat(rawbuf, &sb) != 0 || !S_ISCHR(sb.st_mode)) { 597 dp = rindex(rawbuf, '/'); 598 if (dp) { 599 for (ep = &rawbuf[len]; ep > dp; --ep) 600 *(ep+1) = *ep; 601 *++ep = 'r'; 602 } 603 } 604 return (rawbuf); 605 } 606 607 static void 608 usage() 609 { 610 fprintf(stderr, "%s\n%s\n%s\n", 611 "usage: vnconfig [-cdeguv] [-s option] [-r option] [-S value] special_file", 612 " [regular_file] [feature]", 613 " vnconfig -a [-cdeguv] [-s option] [-r option] [-f config_file]"); 614 exit(1); 615 } 616 617 static int 618 getsize(const char *arg) 619 { 620 char *ptr; 621 int pgsize = getpagesize(); 622 quad_t size = strtoq(arg, &ptr, 0); 623 624 switch(tolower(*ptr)) { 625 case 't': 626 /* 627 * GULP! Terrabytes. It's actually possible to create 628 * a 7.9 TB VN device, though newfs can't handle any single 629 * filesystem larger then 1 TB. 630 */ 631 size *= 1024; 632 /* fall through */ 633 case 'g': 634 size *= 1024; 635 /* fall through */ 636 default: 637 case 'm': 638 size *= 1024; 639 /* fall through */ 640 case 'k': 641 size *= 1024; 642 /* fall through */ 643 case 'c': 644 break; 645 } 646 size = (size + pgsize - 1) / pgsize; 647 return((int)size); 648 } 649 650 /* 651 * DO_AUTOLABEL 652 * 653 * Automatically label the device. This will wipe any preexisting 654 * label. 655 */ 656 657 static void 658 do_autolabel(const char *dev, const char *label) 659 { 660 /* XXX not yet implemented */ 661 fprintf(stderr, "autolabel not yet implemented, sorry\n"); 662 exit(1); 663 } 664 665