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