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