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