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.5 2004/03/10 17:54:16 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 <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, *oarg; 247 int flags, status; 248 struct vn_ioctl vnio; 249 int rv; 250 char *rdev; 251 FILE *f; 252 u_long l; 253 int pgsize = getpagesize(); 254 255 status = rv = 0; 256 257 /* 258 * Prepend "/dev/" to the specified device name, if necessary. 259 * Operate on vnp->dev because it is used later. 260 */ 261 if (vnp->dev[0] != '/' && vnp->dev[0] != '.') 262 (void)asprintf(&vnp->dev, "%s%s", _PATH_DEV, vnp->dev); 263 dev = vnp->dev; 264 file = vnp->file; 265 flags = vnp->flags; 266 oarg = vnp->oarg; 267 268 if (flags & VN_IGNORE) 269 return(0); 270 271 /* 272 * When a regular file has been specified, do any requested setup 273 * of the file. Truncation (also creates the file if necessary), 274 * sizing, and zeroing. 275 */ 276 277 if (file && vnp->size != 0 && (flags & VN_CONFIG)) { 278 int fd; 279 struct stat st; 280 281 if (flags & VN_TRUNCATE) 282 fd = open(file, O_RDWR|O_CREAT|O_TRUNC); 283 else 284 fd = open(file, O_RDWR); 285 if (fd >= 0 && fstat(fd, &st) == 0 && S_ISREG(st.st_mode)) { 286 if (st.st_size < (off_t)vnp->size * pgsize) 287 ftruncate(fd, (off_t)vnp->size * pgsize); 288 if (vnp->size != 0) 289 st.st_size = (off_t)vnp->size * pgsize; 290 291 if (flags & VN_ZERO) { 292 char *buf = malloc(ZBUFSIZE); 293 bzero(buf, ZBUFSIZE); 294 while (st.st_size > 0) { 295 int n = (st.st_size > ZBUFSIZE) ? 296 ZBUFSIZE : (int)st.st_size; 297 if (write(fd, buf, n) != n) { 298 ftruncate(fd, 0); 299 printf("Unable to ZERO file %s\n", file); 300 return(0); 301 } 302 st.st_size -= (off_t)n; 303 } 304 } 305 close(fd); 306 } else { 307 printf("Unable to open file %s\n", file); 308 return(0); 309 } 310 } 311 312 rdev = rawdevice(dev); 313 f = fopen(rdev, "rw"); 314 if (f == NULL) { 315 warn("%s", dev); 316 return(1); 317 } 318 vnio.vn_file = file; 319 vnio.vn_size = vnp->size; /* non-zero only if swap backed */ 320 321 /* 322 * Disable the device 323 */ 324 if (flags & VN_DISABLE) { 325 if (flags & (VN_MOUNTRO|VN_MOUNTRW)) { 326 rv = unmount(oarg, 0); 327 if (rv) { 328 status--; 329 if (errno == EBUSY) 330 flags &= ~VN_UNCONFIG; 331 if ((flags & VN_UNCONFIG) == 0) 332 warn("umount"); 333 } else if (verbose) 334 printf("%s: unmounted\n", dev); 335 } 336 } 337 /* 338 * Clear (un-configure) the device 339 */ 340 if (flags & VN_UNCONFIG) { 341 rv = ioctl(fileno(f), VNIOCDETACH, &vnio); 342 if (rv) { 343 if (errno == ENODEV) { 344 if (verbose) 345 printf("%s: not configured\n", dev); 346 rv = 0; 347 } else { 348 status--; 349 warn("VNIOCDETACH"); 350 } 351 } else if (verbose) 352 printf("%s: cleared\n", dev); 353 } 354 /* 355 * Set specified options 356 */ 357 if (flags & VN_SET) { 358 l = setopt; 359 if (global) 360 rv = ioctl(fileno(f), VNIOCGSET, &l); 361 else 362 rv = ioctl(fileno(f), VNIOCUSET, &l); 363 if (rv) { 364 status--; 365 warn("VNIO[GU]SET"); 366 } else if (verbose) 367 printf("%s: flags now=%08x\n",dev,l); 368 } 369 /* 370 * Reset specified options 371 */ 372 if (flags & VN_RESET) { 373 l = resetopt; 374 if (global) 375 rv = ioctl(fileno(f), VNIOCGCLEAR, &l); 376 else 377 rv = ioctl(fileno(f), VNIOCUCLEAR, &l); 378 if (rv) { 379 status--; 380 warn("VNIO[GU]CLEAR"); 381 } else if (verbose) 382 printf("%s: flags now=%08x\n",dev,l); 383 } 384 /* 385 * Configure the device 386 */ 387 if (flags & VN_CONFIG) { 388 rv = ioctl(fileno(f), VNIOCATTACH, &vnio); 389 if (rv) { 390 status--; 391 warn("VNIOCATTACH"); 392 flags &= ~VN_ENABLE; 393 } else { 394 if (verbose) { 395 printf( 396 "%s: %d bytes on %s\n", 397 dev, vnio.vn_size, file 398 ); 399 } 400 /* 401 * autolabel 402 */ 403 if (vnp->autolabel) { 404 do_autolabel(vnp->dev, vnp->autolabel); 405 } 406 } 407 } 408 /* 409 * Set an option 410 */ 411 if (flags & VN_SET) { 412 l = setopt; 413 if (global) 414 rv = ioctl(fileno(f), VNIOCGSET, &l); 415 else 416 rv = ioctl(fileno(f), VNIOCUSET, &l); 417 if (rv) { 418 status--; 419 warn("VNIO[GU]SET"); 420 } else if (verbose) 421 printf("%s: flags now=%08lx\n",dev,l); 422 } 423 /* 424 * Reset an option 425 */ 426 if (flags & VN_RESET) { 427 l = resetopt; 428 if (global) 429 rv = ioctl(fileno(f), VNIOCGCLEAR, &l); 430 else 431 rv = ioctl(fileno(f), VNIOCUCLEAR, &l); 432 if (rv) { 433 status--; 434 warn("VNIO[GU]CLEAR"); 435 } else if (verbose) 436 printf("%s: flags now=%08lx\n",dev,l); 437 } 438 439 /* 440 * Close the device now, as we may want to mount it. 441 */ 442 fclose(f); 443 444 /* 445 * Enable special functions on the device 446 */ 447 if (flags & VN_ENABLE) { 448 if (flags & VN_SWAP) { 449 rv = swapon(dev); 450 if (rv) { 451 status--; 452 warn("swapon"); 453 } 454 else if (verbose) 455 printf("%s: swapping enabled\n", dev); 456 } 457 if (flags & (VN_MOUNTRO|VN_MOUNTRW)) { 458 struct ufs_args args; 459 int mflags; 460 461 args.fspec = dev; 462 mflags = (flags & VN_MOUNTRO) ? MNT_RDONLY : 0; 463 rv = mount("ufs", oarg, mflags, &args); 464 if (rv) { 465 status--; 466 warn("mount"); 467 } 468 else if (verbose) 469 printf("%s: mounted on %s\n", dev, oarg); 470 } 471 } 472 /* done: */ 473 fflush(stdout); 474 return(status < 0); 475 } 476 477 #define EOL(c) ((c) == '\0' || (c) == '\n') 478 #define WHITE(c) ((c) == ' ' || (c) == '\t' || (c) == '\n') 479 480 void 481 readconfig(int flags) 482 { 483 char buf[LINESIZE]; 484 FILE *f; 485 char *cp, *sp; 486 int ix; 487 int ax; 488 489 f = fopen(configfile, "r"); 490 if (f == NULL) 491 err(1, "%s", configfile); 492 ix = 0; /* number of elements */ 493 ax = 0; /* allocated elements */ 494 while (fgets(buf, LINESIZE, f) != NULL) { 495 cp = buf; 496 if (*cp == '#') 497 continue; 498 while (!EOL(*cp) && WHITE(*cp)) 499 cp++; 500 if (EOL(*cp)) 501 continue; 502 sp = cp; 503 while (!EOL(*cp) && !WHITE(*cp)) 504 cp++; 505 if (EOL(*cp)) 506 continue; 507 *cp++ = '\0'; 508 509 if (ix == ax) { 510 ax = ax + 16; 511 vndisks = realloc(vndisks, ax * sizeof(struct vndisk)); 512 bzero(&vndisks[ix], (ax - ix) * sizeof(struct vndisk)); 513 } 514 vndisks[ix].dev = malloc(cp - sp); 515 strcpy(vndisks[ix].dev, sp); 516 while (!EOL(*cp) && WHITE(*cp)) 517 cp++; 518 if (EOL(*cp)) 519 continue; 520 sp = cp; 521 while (!EOL(*cp) && !WHITE(*cp)) 522 cp++; 523 *cp++ = '\0'; 524 525 if (*sp == '%' && strtol(sp + 1, NULL, 0) > 0) { 526 vndisks[ix].size = getsize(sp + 1); 527 } else { 528 vndisks[ix].file = malloc(cp - sp); 529 strcpy(vndisks[ix].file, sp); 530 } 531 532 while (!EOL(*cp) && WHITE(*cp)) 533 cp++; 534 vndisks[ix].flags = flags; 535 if (!EOL(*cp)) { 536 sp = cp; 537 while (!EOL(*cp) && !WHITE(*cp)) 538 cp++; 539 *cp++ = '\0'; 540 getoptions(&vndisks[ix], sp); 541 } 542 nvndisks++; 543 ix++; 544 } 545 } 546 547 void 548 getoptions(struct vndisk *vnp, char *fstr) 549 { 550 int flags = 0; 551 char *oarg = NULL; 552 553 if (strcmp(fstr, "swap") == 0) 554 flags |= VN_SWAP; 555 else if (strncmp(fstr, "mount=", 6) == 0) { 556 flags |= VN_MOUNTRW; 557 oarg = &fstr[6]; 558 } else if (strncmp(fstr, "mountrw=", 8) == 0) { 559 flags |= VN_MOUNTRW; 560 oarg = &fstr[8]; 561 } else if (strncmp(fstr, "mountro=", 8) == 0) { 562 flags |= VN_MOUNTRO; 563 oarg = &fstr[8]; 564 } else if (strcmp(fstr, "ignore") == 0) 565 flags |= VN_IGNORE; 566 vnp->flags |= flags; 567 if (oarg) { 568 vnp->oarg = malloc(strlen(oarg) + 1); 569 strcpy(vnp->oarg, oarg); 570 } else 571 vnp->oarg = NULL; 572 } 573 574 char * 575 rawdevice(char *dev) 576 { 577 char *rawbuf, *dp, *ep; 578 struct stat sb; 579 int len; 580 581 len = strlen(dev); 582 rawbuf = malloc(len + 2); 583 strcpy(rawbuf, dev); 584 if (stat(rawbuf, &sb) != 0 || !S_ISCHR(sb.st_mode)) { 585 dp = rindex(rawbuf, '/'); 586 if (dp) { 587 for (ep = &rawbuf[len]; ep > dp; --ep) 588 *(ep+1) = *ep; 589 *++ep = 'r'; 590 } 591 } 592 return (rawbuf); 593 } 594 595 static void 596 usage() 597 { 598 fprintf(stderr, "%s\n%s\n%s\n", 599 "usage: vnconfig [-cdeguv] [-s option] [-r option] [-S value] special_file", 600 " [regular_file] [feature]", 601 " vnconfig -a [-cdeguv] [-s option] [-r option] [-f config_file]"); 602 exit(1); 603 } 604 605 static int 606 getsize(const char *arg) 607 { 608 char *ptr; 609 int pgsize = getpagesize(); 610 quad_t size = strtoq(arg, &ptr, 0); 611 612 switch(tolower(*ptr)) { 613 case 't': 614 /* 615 * GULP! Terrabytes. It's actually possible to create 616 * a 7.9 TB VN device, though newfs can't handle any single 617 * filesystem larger then 1 TB. 618 */ 619 size *= 1024; 620 /* fall through */ 621 case 'g': 622 size *= 1024; 623 /* fall through */ 624 default: 625 case 'm': 626 size *= 1024; 627 /* fall through */ 628 case 'k': 629 size *= 1024; 630 /* fall through */ 631 case 'c': 632 break; 633 } 634 size = (size + pgsize - 1) / pgsize; 635 return((int)size); 636 } 637 638 /* 639 * DO_AUTOLABEL 640 * 641 * Automatically label the device. This will wipe any preexisting 642 * label. 643 */ 644 645 static void 646 do_autolabel(const char *dev, const char *label) 647 { 648 /* XXX not yet implemented */ 649 fprintf(stderr, "autolabel not yet implemented, sorry\n"); 650 exit(1); 651 } 652 653