1 /* 2 * Copyright (c) 2001 Dima Dorfman. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24 * SUCH DAMAGE. 25 */ 26 27 /* 28 * mdmfs (md/MFS) is a wrapper around mdconfig(8), 29 * newfs(8), and mount(8) that mimics the command line option set of 30 * the deprecated mount_mfs(8). 31 */ 32 33 #include <sys/cdefs.h> 34 __FBSDID("$FreeBSD$"); 35 36 #include <sys/param.h> 37 #include <sys/linker.h> 38 #include <sys/mdioctl.h> 39 #include <sys/module.h> 40 #include <sys/mount.h> 41 #include <sys/stat.h> 42 #include <sys/wait.h> 43 44 #include <assert.h> 45 #include <err.h> 46 #include <errno.h> 47 #include <fcntl.h> 48 #include <grp.h> 49 #include <inttypes.h> 50 #include <paths.h> 51 #include <pwd.h> 52 #include <stdarg.h> 53 #include <stdio.h> 54 #include <stdlib.h> 55 #include <string.h> 56 #include <ctype.h> 57 #include <unistd.h> 58 59 typedef enum { false, true } bool; 60 61 struct mtpt_info { 62 uid_t mi_uid; 63 bool mi_have_uid; 64 gid_t mi_gid; 65 bool mi_have_gid; 66 mode_t mi_mode; 67 bool mi_have_mode; 68 bool mi_forced_pw; 69 }; 70 71 static bool debug; /* Emit debugging information? */ 72 static bool loudsubs; /* Suppress output from helper programs? */ 73 static bool norun; /* Actually run the helper programs? */ 74 static int unit; /* The unit we're working with. */ 75 static const char *mdname; /* Name of memory disk device (e.g., "md"). */ 76 static const char *mdsuffix; /* Suffix of memory disk device (e.g., ".uzip"). */ 77 static size_t mdnamelen; /* Length of mdname. */ 78 static const char *path_mdconfig =_PATH_MDCONFIG; 79 80 static void argappend(char **, const char *, ...) __printflike(2, 3); 81 static void debugprintf(const char *, ...) __printflike(1, 2); 82 static void do_mdconfig_attach(const char *, const enum md_types); 83 static void do_mdconfig_attach_au(const char *, const enum md_types); 84 static void do_mdconfig_detach(void); 85 static void do_mount_md(const char *, const char *); 86 static void do_mount_tmpfs(const char *, const char *); 87 static void do_mtptsetup(const char *, struct mtpt_info *); 88 static void do_newfs(const char *); 89 static void extract_ugid(const char *, struct mtpt_info *); 90 static int run(int *, const char *, ...) __printflike(2, 3); 91 static void usage(void); 92 93 int 94 main(int argc, char **argv) 95 { 96 struct mtpt_info mi; /* Mountpoint info. */ 97 intmax_t mdsize; 98 char *mdconfig_arg, *newfs_arg, /* Args to helper programs. */ 99 *mount_arg; 100 enum md_types mdtype; /* The type of our memory disk. */ 101 bool have_mdtype, mlmac; 102 bool detach, softdep, autounit, newfs; 103 const char *mtpoint, *size_arg, *unitstr; 104 char *p; 105 int ch, idx; 106 void *set; 107 unsigned long ul; 108 109 /* Misc. initialization. */ 110 (void)memset(&mi, '\0', sizeof(mi)); 111 detach = true; 112 softdep = true; 113 autounit = false; 114 mlmac = false; 115 newfs = true; 116 have_mdtype = false; 117 mdtype = MD_SWAP; 118 mdname = MD_NAME; 119 mdnamelen = strlen(mdname); 120 mdsize = 0; 121 /* 122 * Can't set these to NULL. They may be passed to the 123 * respective programs without modification. I.e., we may not 124 * receive any command-line options which will caused them to 125 * be modified. 126 */ 127 mdconfig_arg = strdup(""); 128 newfs_arg = strdup(""); 129 mount_arg = strdup(""); 130 size_arg = NULL; 131 132 /* If we were started as mount_mfs or mfs, imply -C. */ 133 if (strcmp(getprogname(), "mount_mfs") == 0 || 134 strcmp(getprogname(), "mfs") == 0) { 135 /* Make compatibility assumptions. */ 136 mi.mi_mode = 01777; 137 mi.mi_have_mode = true; 138 } 139 140 while ((ch = getopt(argc, argv, 141 "a:b:Cc:Dd:E:e:F:f:hi:LlMm:NnO:o:Pp:Ss:tT:Uv:w:X")) != -1) 142 switch (ch) { 143 case 'a': 144 argappend(&newfs_arg, "-a %s", optarg); 145 break; 146 case 'b': 147 argappend(&newfs_arg, "-b %s", optarg); 148 break; 149 case 'C': 150 /* Ignored for compatibility. */ 151 break; 152 case 'c': 153 argappend(&newfs_arg, "-c %s", optarg); 154 break; 155 case 'D': 156 detach = false; 157 break; 158 case 'd': 159 argappend(&newfs_arg, "-d %s", optarg); 160 break; 161 case 'E': 162 path_mdconfig = optarg; 163 break; 164 case 'e': 165 argappend(&newfs_arg, "-e %s", optarg); 166 break; 167 case 'F': 168 if (have_mdtype) 169 usage(); 170 mdtype = MD_VNODE; 171 have_mdtype = true; 172 argappend(&mdconfig_arg, "-f %s", optarg); 173 break; 174 case 'f': 175 argappend(&newfs_arg, "-f %s", optarg); 176 break; 177 case 'h': 178 usage(); 179 break; 180 case 'i': 181 argappend(&newfs_arg, "-i %s", optarg); 182 break; 183 case 'L': 184 loudsubs = true; 185 break; 186 case 'l': 187 mlmac = true; 188 argappend(&newfs_arg, "-l"); 189 break; 190 case 'M': 191 if (have_mdtype) 192 usage(); 193 mdtype = MD_MALLOC; 194 have_mdtype = true; 195 break; 196 case 'm': 197 argappend(&newfs_arg, "-m %s", optarg); 198 break; 199 case 'N': 200 norun = true; 201 break; 202 case 'n': 203 argappend(&newfs_arg, "-n"); 204 break; 205 case 'O': 206 argappend(&newfs_arg, "-o %s", optarg); 207 break; 208 case 'o': 209 argappend(&mount_arg, "-o %s", optarg); 210 break; 211 case 'P': 212 newfs = false; 213 break; 214 case 'p': 215 if ((set = setmode(optarg)) == NULL) 216 usage(); 217 mi.mi_mode = getmode(set, S_IRWXU | S_IRWXG | S_IRWXO); 218 mi.mi_have_mode = true; 219 mi.mi_forced_pw = true; 220 free(set); 221 break; 222 case 'S': 223 softdep = false; 224 break; 225 case 's': 226 size_arg = optarg; 227 break; 228 case 't': 229 argappend(&newfs_arg, "-t"); 230 break; 231 case 'T': 232 argappend(&mount_arg, "-t %s", optarg); 233 break; 234 case 'U': 235 softdep = true; 236 break; 237 case 'v': 238 argappend(&newfs_arg, "-O %s", optarg); 239 break; 240 case 'w': 241 extract_ugid(optarg, &mi); 242 mi.mi_forced_pw = true; 243 break; 244 case 'X': 245 debug = true; 246 break; 247 default: 248 usage(); 249 } 250 argc -= optind; 251 argv += optind; 252 if (argc < 2) 253 usage(); 254 255 /* 256 * Historically our size arg was passed directly to mdconfig, which 257 * treats a number without a suffix as a count of 512-byte sectors; 258 * tmpfs would treat it as a count of bytes. To get predictable 259 * behavior for 'auto' we document that the size always uses mdconfig 260 * rules. To make that work, decode the size here so it can be passed 261 * to either tmpfs or mdconfig as a count of bytes. 262 */ 263 if (size_arg != NULL) { 264 mdsize = (intmax_t)strtoumax(size_arg, &p, 0); 265 if (p == size_arg || (p[0] != 0 && p[1] != 0) || mdsize < 0) 266 errx(1, "invalid size '%s'", size_arg); 267 switch (*p) { 268 case 'p': 269 case 'P': 270 mdsize *= 1024; 271 case 't': 272 case 'T': 273 mdsize *= 1024; 274 case 'g': 275 case 'G': 276 mdsize *= 1024; 277 case 'm': 278 case 'M': 279 mdsize *= 1024; 280 case 'k': 281 case 'K': 282 mdsize *= 1024; 283 case 'b': 284 case 'B': 285 break; 286 case '\0': 287 mdsize *= 512; 288 break; 289 default: 290 errx(1, "invalid size suffix on '%s'", size_arg); 291 } 292 } 293 294 /* 295 * Based on the command line 'md-device' either mount a tmpfs filesystem 296 * or configure the md device then format and mount a filesystem on it. 297 * If the device is 'auto' use tmpfs if it is available and there is no 298 * request for multilabel MAC (which tmpfs does not support). 299 */ 300 unitstr = argv[0]; 301 mtpoint = argv[1]; 302 303 if (strcmp(unitstr, "auto") == 0) { 304 if (mlmac) 305 idx = -1; /* Must use md for mlmac. */ 306 else if ((idx = modfind("tmpfs")) == -1) 307 idx = kldload("tmpfs"); 308 if (idx == -1) 309 unitstr = "md"; 310 else 311 unitstr = "tmpfs"; 312 } 313 314 if (strcmp(unitstr, "tmpfs") == 0) { 315 if (size_arg != NULL && mdsize != 0) 316 argappend(&mount_arg, "-o size=%jd", mdsize); 317 do_mount_tmpfs(mount_arg, mtpoint); 318 } else { 319 if (size_arg != NULL) 320 argappend(&mdconfig_arg, "-s %jdB", mdsize); 321 if (strncmp(unitstr, "/dev/", 5) == 0) 322 unitstr += 5; 323 if (strncmp(unitstr, mdname, mdnamelen) == 0) 324 unitstr += mdnamelen; 325 if (!isdigit(*unitstr)) { 326 autounit = true; 327 unit = -1; 328 mdsuffix = unitstr; 329 } else { 330 ul = strtoul(unitstr, &p, 10); 331 if (ul == ULONG_MAX) 332 errx(1, "bad device unit: %s", unitstr); 333 unit = ul; 334 mdsuffix = p; /* can be empty */ 335 } 336 337 if (!have_mdtype) 338 mdtype = MD_SWAP; 339 if (softdep) 340 argappend(&newfs_arg, "-U"); 341 if (mdtype != MD_VNODE && !newfs) 342 errx(1, "-P requires a vnode-backed disk"); 343 344 /* Do the work. */ 345 if (detach && !autounit) 346 do_mdconfig_detach(); 347 if (autounit) 348 do_mdconfig_attach_au(mdconfig_arg, mdtype); 349 else 350 do_mdconfig_attach(mdconfig_arg, mdtype); 351 if (newfs) 352 do_newfs(newfs_arg); 353 do_mount_md(mount_arg, mtpoint); 354 } 355 356 do_mtptsetup(mtpoint, &mi); 357 358 return (0); 359 } 360 361 /* 362 * Append the expansion of 'fmt' to the buffer pointed to by '*dstp'; 363 * reallocate as required. 364 */ 365 static void 366 argappend(char **dstp, const char *fmt, ...) 367 { 368 char *old, *new; 369 va_list ap; 370 371 old = *dstp; 372 assert(old != NULL); 373 374 va_start(ap, fmt); 375 if (vasprintf(&new, fmt,ap) == -1) 376 errx(1, "vasprintf"); 377 va_end(ap); 378 379 *dstp = new; 380 if (asprintf(&new, "%s %s", old, new) == -1) 381 errx(1, "asprintf"); 382 free(*dstp); 383 free(old); 384 385 *dstp = new; 386 } 387 388 /* 389 * If run-time debugging is enabled, print the expansion of 'fmt'. 390 * Otherwise, do nothing. 391 */ 392 static void 393 debugprintf(const char *fmt, ...) 394 { 395 va_list ap; 396 397 if (!debug) 398 return; 399 fprintf(stderr, "DEBUG: "); 400 va_start(ap, fmt); 401 vfprintf(stderr, fmt, ap); 402 va_end(ap); 403 fprintf(stderr, "\n"); 404 fflush(stderr); 405 } 406 407 /* 408 * Attach a memory disk with a known unit. 409 */ 410 static void 411 do_mdconfig_attach(const char *args, const enum md_types mdtype) 412 { 413 int rv; 414 const char *ta; /* Type arg. */ 415 416 switch (mdtype) { 417 case MD_SWAP: 418 ta = "-t swap"; 419 break; 420 case MD_VNODE: 421 ta = "-t vnode"; 422 break; 423 case MD_MALLOC: 424 ta = "-t malloc"; 425 break; 426 default: 427 abort(); 428 } 429 rv = run(NULL, "%s -a %s%s -u %s%d", path_mdconfig, ta, args, 430 mdname, unit); 431 if (rv) 432 errx(1, "mdconfig (attach) exited with error code %d", rv); 433 } 434 435 /* 436 * Attach a memory disk with an unknown unit; use autounit. 437 */ 438 static void 439 do_mdconfig_attach_au(const char *args, const enum md_types mdtype) 440 { 441 const char *ta; /* Type arg. */ 442 char *linep, *linebuf; /* Line pointer, line buffer. */ 443 int fd; /* Standard output of mdconfig invocation. */ 444 FILE *sfd; 445 int rv; 446 char *p; 447 size_t linelen; 448 unsigned long ul; 449 450 switch (mdtype) { 451 case MD_SWAP: 452 ta = "-t swap"; 453 break; 454 case MD_VNODE: 455 ta = "-t vnode"; 456 break; 457 case MD_MALLOC: 458 ta = "-t malloc"; 459 break; 460 default: 461 abort(); 462 } 463 rv = run(&fd, "%s -a %s%s", path_mdconfig, ta, args); 464 if (rv) 465 errx(1, "mdconfig (attach) exited with error code %d", rv); 466 467 /* Receive the unit number. */ 468 if (norun) { /* Since we didn't run, we can't read. Fake it. */ 469 unit = 0; 470 return; 471 } 472 sfd = fdopen(fd, "r"); 473 if (sfd == NULL) 474 err(1, "fdopen"); 475 linep = fgetln(sfd, &linelen); 476 if (linep == NULL && linelen < mdnamelen + 1) 477 errx(1, "unexpected output from mdconfig (attach)"); 478 /* If the output format changes, we want to know about it. */ 479 assert(strncmp(linep, mdname, mdnamelen) == 0); 480 linebuf = malloc(linelen - mdnamelen + 1); 481 assert(linebuf != NULL); 482 /* Can't use strlcpy because linep is not NULL-terminated. */ 483 strncpy(linebuf, linep + mdnamelen, linelen); 484 linebuf[linelen] = '\0'; 485 ul = strtoul(linebuf, &p, 10); 486 if (ul == ULONG_MAX || *p != '\n') 487 errx(1, "unexpected output from mdconfig (attach)"); 488 unit = ul; 489 490 fclose(sfd); 491 close(fd); 492 } 493 494 /* 495 * Detach a memory disk. 496 */ 497 static void 498 do_mdconfig_detach(void) 499 { 500 int rv; 501 502 rv = run(NULL, "%s -d -u %s%d", path_mdconfig, mdname, unit); 503 if (rv && debug) /* This is allowed to fail. */ 504 warnx("mdconfig (detach) exited with error code %d (ignored)", 505 rv); 506 } 507 508 /* 509 * Mount the configured memory disk. 510 */ 511 static void 512 do_mount_md(const char *args, const char *mtpoint) 513 { 514 int rv; 515 516 rv = run(NULL, "%s%s /dev/%s%d%s %s", _PATH_MOUNT, args, 517 mdname, unit, mdsuffix, mtpoint); 518 if (rv) 519 errx(1, "mount exited with error code %d", rv); 520 } 521 522 /* 523 * Mount the configured tmpfs. 524 */ 525 static void 526 do_mount_tmpfs(const char *args, const char *mtpoint) 527 { 528 int rv; 529 530 rv = run(NULL, "%s -t tmpfs %s tmp %s", _PATH_MOUNT, args, mtpoint); 531 if (rv) 532 errx(1, "tmpfs mount exited with error code %d", rv); 533 } 534 535 /* 536 * Various configuration of the mountpoint. Mostly, enact 'mip'. 537 */ 538 static void 539 do_mtptsetup(const char *mtpoint, struct mtpt_info *mip) 540 { 541 struct statfs sfs; 542 543 if (!mip->mi_have_mode && !mip->mi_have_uid && !mip->mi_have_gid) 544 return; 545 546 if (!norun) { 547 if (statfs(mtpoint, &sfs) == -1) { 548 warn("statfs: %s", mtpoint); 549 return; 550 } 551 if ((sfs.f_flags & MNT_RDONLY) != 0) { 552 if (mip->mi_forced_pw) { 553 warnx( 554 "Not changing mode/owner of %s since it is read-only", 555 mtpoint); 556 } else { 557 debugprintf( 558 "Not changing mode/owner of %s since it is read-only", 559 mtpoint); 560 } 561 return; 562 } 563 } 564 565 if (mip->mi_have_mode) { 566 debugprintf("changing mode of %s to %o.", mtpoint, 567 mip->mi_mode); 568 if (!norun) 569 if (chmod(mtpoint, mip->mi_mode) == -1) 570 err(1, "chmod: %s", mtpoint); 571 } 572 /* 573 * We have to do these separately because the user may have 574 * only specified one of them. 575 */ 576 if (mip->mi_have_uid) { 577 debugprintf("changing owner (user) or %s to %u.", mtpoint, 578 mip->mi_uid); 579 if (!norun) 580 if (chown(mtpoint, mip->mi_uid, -1) == -1) 581 err(1, "chown %s to %u (user)", mtpoint, 582 mip->mi_uid); 583 } 584 if (mip->mi_have_gid) { 585 debugprintf("changing owner (group) or %s to %u.", mtpoint, 586 mip->mi_gid); 587 if (!norun) 588 if (chown(mtpoint, -1, mip->mi_gid) == -1) 589 err(1, "chown %s to %u (group)", mtpoint, 590 mip->mi_gid); 591 } 592 } 593 594 /* 595 * Put a file system on the memory disk. 596 */ 597 static void 598 do_newfs(const char *args) 599 { 600 int rv; 601 602 rv = run(NULL, "%s%s /dev/%s%d", _PATH_NEWFS, args, mdname, unit); 603 if (rv) 604 errx(1, "newfs exited with error code %d", rv); 605 } 606 607 /* 608 * 'str' should be a user and group name similar to the last argument 609 * to chown(1); i.e., a user, followed by a colon, followed by a 610 * group. The user and group in 'str' may be either a [ug]id or a 611 * name. Upon return, the uid and gid fields in 'mip' will contain 612 * the uid and gid of the user and group name in 'str', respectively. 613 * 614 * In other words, this derives a user and group id from a string 615 * formatted like the last argument to chown(1). 616 * 617 * Notice: At this point we don't support only a username or only a 618 * group name. do_mtptsetup already does, so when this feature is 619 * desired, this is the only routine that needs to be changed. 620 */ 621 static void 622 extract_ugid(const char *str, struct mtpt_info *mip) 623 { 624 char *ug; /* Writable 'str'. */ 625 char *user, *group; /* Result of extracton. */ 626 struct passwd *pw; 627 struct group *gr; 628 char *p; 629 uid_t *uid; 630 gid_t *gid; 631 632 uid = &mip->mi_uid; 633 gid = &mip->mi_gid; 634 mip->mi_have_uid = mip->mi_have_gid = false; 635 636 /* Extract the user and group from 'str'. Format above. */ 637 ug = strdup(str); 638 assert(ug != NULL); 639 group = ug; 640 user = strsep(&group, ":"); 641 if (user == NULL || group == NULL || *user == '\0' || *group == '\0') 642 usage(); 643 644 /* Derive uid. */ 645 *uid = strtoul(user, &p, 10); 646 if (*uid == (uid_t)ULONG_MAX) 647 usage(); 648 if (*p != '\0') { 649 pw = getpwnam(user); 650 if (pw == NULL) 651 errx(1, "invalid user: %s", user); 652 *uid = pw->pw_uid; 653 } 654 mip->mi_have_uid = true; 655 656 /* Derive gid. */ 657 *gid = strtoul(group, &p, 10); 658 if (*gid == (gid_t)ULONG_MAX) 659 usage(); 660 if (*p != '\0') { 661 gr = getgrnam(group); 662 if (gr == NULL) 663 errx(1, "invalid group: %s", group); 664 *gid = gr->gr_gid; 665 } 666 mip->mi_have_gid = true; 667 668 free(ug); 669 } 670 671 /* 672 * Run a process with command name and arguments pointed to by the 673 * formatted string 'cmdline'. Since system(3) is not used, the first 674 * space-delimited token of 'cmdline' must be the full pathname of the 675 * program to run. The return value is the return code of the process 676 * spawned. If 'ofd' is non-NULL, it is set to the standard output of 677 * the program spawned (i.e., you can read from ofd and get the output 678 * of the program). 679 */ 680 static int 681 run(int *ofd, const char *cmdline, ...) 682 { 683 char **argv, **argvp; /* Result of splitting 'cmd'. */ 684 int argc; 685 char *cmd; /* Expansion of 'cmdline'. */ 686 int pid, status; /* Child info. */ 687 int pfd[2]; /* Pipe to the child. */ 688 int nfd; /* Null (/dev/null) file descriptor. */ 689 bool dup2dn; /* Dup /dev/null to stdout? */ 690 va_list ap; 691 char *p; 692 int rv, i; 693 694 dup2dn = true; 695 va_start(ap, cmdline); 696 rv = vasprintf(&cmd, cmdline, ap); 697 if (rv == -1) 698 err(1, "vasprintf"); 699 va_end(ap); 700 701 /* Split up 'cmd' into 'argv' for use with execve. */ 702 for (argc = 1, p = cmd; (p = strchr(p, ' ')) != NULL; p++) 703 argc++; /* 'argc' generation loop. */ 704 argv = (char **)malloc(sizeof(*argv) * (argc + 1)); 705 assert(argv != NULL); 706 for (p = cmd, argvp = argv; (*argvp = strsep(&p, " ")) != NULL;) 707 if (**argvp != '\0') 708 if (++argvp >= &argv[argc]) { 709 *argvp = NULL; 710 break; 711 } 712 assert(*argv); 713 /* The argv array ends up NULL-terminated here. */ 714 715 /* Make sure the above loop works as expected. */ 716 if (debug) { 717 /* 718 * We can't, but should, use debugprintf here. First, 719 * it appends a trailing newline to the output, and 720 * second it prepends "DEBUG: " to the output. The 721 * former is a problem for this would-be first call, 722 * and the latter for the would-be call inside the 723 * loop. 724 */ 725 (void)fprintf(stderr, "DEBUG: running:"); 726 /* Should be equivalent to 'cmd' (before strsep, of course). */ 727 for (i = 0; argv[i] != NULL; i++) 728 (void)fprintf(stderr, " %s", argv[i]); 729 (void)fprintf(stderr, "\n"); 730 } 731 732 /* Create a pipe if necessary and fork the helper program. */ 733 if (ofd != NULL) { 734 if (pipe(&pfd[0]) == -1) 735 err(1, "pipe"); 736 *ofd = pfd[0]; 737 dup2dn = false; 738 } 739 pid = fork(); 740 switch (pid) { 741 case 0: 742 /* XXX can we call err() in here? */ 743 if (norun) 744 _exit(0); 745 if (ofd != NULL) 746 if (dup2(pfd[1], STDOUT_FILENO) < 0) 747 err(1, "dup2"); 748 if (!loudsubs) { 749 nfd = open(_PATH_DEVNULL, O_RDWR); 750 if (nfd == -1) 751 err(1, "open: %s", _PATH_DEVNULL); 752 if (dup2(nfd, STDIN_FILENO) < 0) 753 err(1, "dup2"); 754 if (dup2dn) 755 if (dup2(nfd, STDOUT_FILENO) < 0) 756 err(1, "dup2"); 757 if (dup2(nfd, STDERR_FILENO) < 0) 758 err(1, "dup2"); 759 } 760 761 (void)execv(argv[0], argv); 762 warn("exec: %s", argv[0]); 763 _exit(-1); 764 case -1: 765 err(1, "fork"); 766 } 767 768 free(cmd); 769 free(argv); 770 while (waitpid(pid, &status, 0) != pid) 771 ; 772 return (WEXITSTATUS(status)); 773 } 774 775 static void 776 usage(void) 777 { 778 779 fprintf(stderr, 780 "usage: %s [-DLlMNnPStUX] [-a maxcontig] [-b block-size]\n" 781 "\t[-c blocks-per-cylinder-group][-d max-extent-size] [-E path-mdconfig]\n" 782 "\t[-e maxbpg] [-F file] [-f frag-size] [-i bytes] [-m percent-free]\n" 783 "\t[-O optimization] [-o mount-options]\n" 784 "\t[-p permissions] [-s size] [-v version] [-w user:group]\n" 785 "\tmd-device mount-point\n", getprogname()); 786 exit(1); 787 } 788