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