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