178447Sdd/* 280607Sdd * Copyright (c) 2001 Dima Dorfman. 378447Sdd * All rights reserved. 478447Sdd * 578447Sdd * Redistribution and use in source and binary forms, with or without 678447Sdd * modification, are permitted provided that the following conditions 778447Sdd * are met: 878447Sdd * 1. Redistributions of source code must retain the above copyright 978447Sdd * notice, this list of conditions and the following disclaimer. 1078447Sdd * 2. Redistributions in binary form must reproduce the above copyright 1178447Sdd * notice, this list of conditions and the following disclaimer in the 1278447Sdd * documentation and/or other materials provided with the distribution. 1378447Sdd * 1478447Sdd * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 1578447Sdd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 1678447Sdd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 1778447Sdd * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 1878447Sdd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 1978447Sdd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 2078447Sdd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 2178447Sdd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 2278447Sdd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 2378447Sdd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 2478447Sdd * SUCH DAMAGE. 2578447Sdd */ 2678447Sdd 2778447Sdd/* 28103798Sphk * mdmfs (md/MFS) is a wrapper around mdconfig(8), 2978447Sdd * newfs(8), and mount(8) that mimics the command line option set of 3078447Sdd * the deprecated mount_mfs(8). 3178447Sdd */ 3278447Sdd 33114589Sobrien#include <sys/cdefs.h> 34114589Sobrien__FBSDID("$FreeBSD$"); 3578447Sdd 3678447Sdd#include <sys/param.h> 3778447Sdd#include <sys/mdioctl.h> 38225534Skib#include <sys/mount.h> 3978447Sdd#include <sys/stat.h> 4078447Sdd#include <sys/wait.h> 4178447Sdd 4278447Sdd#include <assert.h> 4378447Sdd#include <err.h> 4478447Sdd#include <fcntl.h> 4578447Sdd#include <grp.h> 4678447Sdd#include <paths.h> 4778447Sdd#include <pwd.h> 4878447Sdd#include <stdarg.h> 4978447Sdd#include <stdio.h> 5078447Sdd#include <stdlib.h> 5178447Sdd#include <string.h> 52166749Smatteo#include <ctype.h> 5378447Sdd#include <unistd.h> 5478447Sdd 5578447Sddtypedef enum { false, true } bool; 5678447Sdd 5778447Sddstruct mtpt_info { 5878447Sdd uid_t mi_uid; 5978447Sdd bool mi_have_uid; 6078447Sdd gid_t mi_gid; 6178447Sdd bool mi_have_gid; 6278447Sdd mode_t mi_mode; 6378447Sdd bool mi_have_mode; 64225534Skib bool mi_forced_pw; 6578447Sdd}; 6678447Sdd 6778447Sddstatic bool debug; /* Emit debugging information? */ 6878447Sddstatic bool loudsubs; /* Suppress output from helper programs? */ 6978447Sddstatic bool norun; /* Actually run the helper programs? */ 7078447Sddstatic int unit; /* The unit we're working with. */ 7178447Sddstatic const char *mdname; /* Name of memory disk device (e.g., "md"). */ 72166749Smatteostatic const char *mdsuffix; /* Suffix of memory disk device (e.g., ".uzip"). */ 7378447Sddstatic size_t mdnamelen; /* Length of mdname. */ 74155769Ssobomaxstatic const char *path_mdconfig =_PATH_MDCONFIG; 7578447Sdd 7679052Skrisstatic void argappend(char **, const char *, ...) __printflike(2, 3); 7779052Skrisstatic void debugprintf(const char *, ...) __printflike(1, 2); 7878447Sddstatic void do_mdconfig_attach(const char *, const enum md_types); 7978447Sddstatic void do_mdconfig_attach_au(const char *, const enum md_types); 8078447Sddstatic void do_mdconfig_detach(void); 8178447Sddstatic void do_mount(const char *, const char *); 8278447Sddstatic void do_mtptsetup(const char *, struct mtpt_info *); 8378447Sddstatic void do_newfs(const char *); 8478447Sddstatic void extract_ugid(const char *, struct mtpt_info *); 8579052Skrisstatic int run(int *, const char *, ...) __printflike(2, 3); 8678447Sddstatic void usage(void); 8778447Sdd 8878447Sddint 8981628Sobrienmain(int argc, char **argv) 9078447Sdd{ 9178447Sdd struct mtpt_info mi; /* Mountpoint info. */ 9278447Sdd char *mdconfig_arg, *newfs_arg, /* Args to helper programs. */ 9378447Sdd *mount_arg; 9478447Sdd enum md_types mdtype; /* The type of our memory disk. */ 9578447Sdd bool have_mdtype; 96153961Sdd bool detach, softdep, autounit, newfs; 9778447Sdd char *mtpoint, *unitstr; 98124830Sgrehan char *p; 99124830Sgrehan int ch; 100118500Syar void *set; 101141082Sssouhlal unsigned long ul; 10278447Sdd 10378447Sdd /* Misc. initialization. */ 10478447Sdd (void)memset(&mi, '\0', sizeof(mi)); 10578447Sdd detach = true; 10678447Sdd softdep = true; 10778447Sdd autounit = false; 108153961Sdd newfs = true; 10978447Sdd have_mdtype = false; 110140815Sssouhlal mdtype = MD_SWAP; 11178447Sdd mdname = MD_NAME; 11278447Sdd mdnamelen = strlen(mdname); 11378447Sdd /* 11478447Sdd * Can't set these to NULL. They may be passed to the 11578447Sdd * respective programs without modification. I.e., we may not 11678447Sdd * receive any command-line options which will caused them to 11778447Sdd * be modified. 11878447Sdd */ 11978447Sdd mdconfig_arg = strdup(""); 12078447Sdd newfs_arg = strdup(""); 12178447Sdd mount_arg = strdup(""); 12278447Sdd 12384167Siedowse /* If we were started as mount_mfs or mfs, imply -C. */ 12484167Siedowse if (strcmp(getprogname(), "mount_mfs") == 0 || 125163952Sru strcmp(getprogname(), "mfs") == 0) { 126163952Sru /* Make compatibility assumptions. */ 127163952Sru mi.mi_mode = 01777; 128163952Sru mi.mi_have_mode = true; 129163952Sru } 13081742Sdd 13181628Sobrien while ((ch = getopt(argc, argv, 132225416Skib "a:b:Cc:Dd:E:e:F:f:hi:LlMm:NnO:o:Pp:Ss:tUv:w:X")) != -1) 13378447Sdd switch (ch) { 13478447Sdd case 'a': 13578447Sdd argappend(&newfs_arg, "-a %s", optarg); 13678447Sdd break; 13778447Sdd case 'b': 13878447Sdd argappend(&newfs_arg, "-b %s", optarg); 13978447Sdd break; 14081742Sdd case 'C': 141163952Sru /* Ignored for compatibility. */ 14281742Sdd break; 14378447Sdd case 'c': 14478447Sdd argappend(&newfs_arg, "-c %s", optarg); 14578447Sdd break; 14678447Sdd case 'D': 14778447Sdd detach = false; 14878447Sdd break; 14978447Sdd case 'd': 15078447Sdd argappend(&newfs_arg, "-d %s", optarg); 15178447Sdd break; 152155769Ssobomax case 'E': 153155769Ssobomax path_mdconfig = optarg; 154155769Ssobomax break; 15578447Sdd case 'e': 15678447Sdd argappend(&newfs_arg, "-e %s", optarg); 15778447Sdd break; 15878447Sdd case 'F': 15978447Sdd if (have_mdtype) 16078447Sdd usage(); 16178447Sdd mdtype = MD_VNODE; 16278447Sdd have_mdtype = true; 16378447Sdd argappend(&mdconfig_arg, "-f %s", optarg); 16478447Sdd break; 16578447Sdd case 'f': 16678447Sdd argappend(&newfs_arg, "-f %s", optarg); 16778447Sdd break; 16878447Sdd case 'h': 16978447Sdd usage(); 17078447Sdd break; 17178447Sdd case 'i': 17278447Sdd argappend(&newfs_arg, "-i %s", optarg); 17378447Sdd break; 17478447Sdd case 'L': 17578447Sdd loudsubs = true; 17678447Sdd break; 177126255Srwatson case 'l': 178126255Srwatson argappend(&newfs_arg, "-l"); 179126255Srwatson break; 18078447Sdd case 'M': 18178447Sdd if (have_mdtype) 18278447Sdd usage(); 18378447Sdd mdtype = MD_MALLOC; 18478447Sdd have_mdtype = true; 18578447Sdd break; 18678447Sdd case 'm': 18778447Sdd argappend(&newfs_arg, "-m %s", optarg); 18878447Sdd break; 18978447Sdd case 'N': 19078447Sdd norun = true; 19178447Sdd break; 19278447Sdd case 'n': 193169560Sremko argappend(&newfs_arg, "-n"); 19478447Sdd break; 19578447Sdd case 'O': 19678447Sdd argappend(&newfs_arg, "-o %s", optarg); 19778447Sdd break; 19878447Sdd case 'o': 19978447Sdd argappend(&mount_arg, "-o %s", optarg); 20078447Sdd break; 201153961Sdd case 'P': 202153961Sdd newfs = false; 203153961Sdd break; 20478447Sdd case 'p': 205118500Syar if ((set = setmode(optarg)) == NULL) 20678447Sdd usage(); 207118500Syar mi.mi_mode = getmode(set, S_IRWXU | S_IRWXG | S_IRWXO); 20878447Sdd mi.mi_have_mode = true; 209225534Skib mi.mi_forced_pw = true; 210118500Syar free(set); 21178447Sdd break; 21278447Sdd case 'S': 21378447Sdd softdep = false; 21478447Sdd break; 21578447Sdd case 's': 21678447Sdd argappend(&mdconfig_arg, "-s %s", optarg); 21778447Sdd break; 218225416Skib case 't': 219225416Skib argappend(&newfs_arg, "-t"); 220225416Skib break; 22181742Sdd case 'U': 22281742Sdd softdep = true; 22381742Sdd break; 224107475Srwatson case 'v': 225107475Srwatson argappend(&newfs_arg, "-O %s", optarg); 226107475Srwatson break; 22778447Sdd case 'w': 22878447Sdd extract_ugid(optarg, &mi); 229225534Skib mi.mi_forced_pw = true; 23078447Sdd break; 23178447Sdd case 'X': 23278447Sdd debug = true; 23378447Sdd break; 23478447Sdd default: 23578447Sdd usage(); 23678447Sdd } 23781628Sobrien argc -= optind; 23881628Sobrien argv += optind; 23981628Sobrien if (argc < 2) 24078447Sdd usage(); 24178447Sdd 24278447Sdd /* Derive 'unit' (global). */ 24381628Sobrien unitstr = argv[0]; 24478447Sdd if (strncmp(unitstr, "/dev/", 5) == 0) 24578447Sdd unitstr += 5; 24678447Sdd if (strncmp(unitstr, mdname, mdnamelen) == 0) 24778447Sdd unitstr += mdnamelen; 248166749Smatteo if (!isdigit(*unitstr)) { 24978447Sdd autounit = true; 25078447Sdd unit = -1; 251166749Smatteo mdsuffix = unitstr; 25278447Sdd } else { 253141082Sssouhlal ul = strtoul(unitstr, &p, 10); 254166749Smatteo if (ul == ULONG_MAX) 25578447Sdd errx(1, "bad device unit: %s", unitstr); 256141082Sssouhlal unit = ul; 257167286Syar mdsuffix = p; /* can be empty */ 25878447Sdd } 25978447Sdd 26081628Sobrien mtpoint = argv[1]; 26178447Sdd if (!have_mdtype) 26278447Sdd mdtype = MD_SWAP; 26378447Sdd if (softdep) 26478447Sdd argappend(&newfs_arg, "-U"); 265153961Sdd if (mdtype != MD_VNODE && !newfs) 266153961Sdd errx(1, "-P requires a vnode-backed disk"); 26778447Sdd 26878447Sdd /* Do the work. */ 26978447Sdd if (detach && !autounit) 27078447Sdd do_mdconfig_detach(); 27178447Sdd if (autounit) 27278447Sdd do_mdconfig_attach_au(mdconfig_arg, mdtype); 27378447Sdd else 27478447Sdd do_mdconfig_attach(mdconfig_arg, mdtype); 275153961Sdd if (newfs) 276153961Sdd do_newfs(newfs_arg); 27778447Sdd do_mount(mount_arg, mtpoint); 27878447Sdd do_mtptsetup(mtpoint, &mi); 27978447Sdd 28078447Sdd return (0); 28178447Sdd} 28278447Sdd 28378447Sdd/* 28478447Sdd * Append the expansion of 'fmt' to the buffer pointed to by '*dstp'; 28578447Sdd * reallocate as required. 28678447Sdd */ 28778447Sddstatic void 28878447Sddargappend(char **dstp, const char *fmt, ...) 28978447Sdd{ 29078447Sdd char *old, *new; 29178447Sdd va_list ap; 292225416Skib 29378447Sdd old = *dstp; 29478447Sdd assert(old != NULL); 29578447Sdd 29678447Sdd va_start(ap, fmt); 29778447Sdd if (vasprintf(&new, fmt,ap) == -1) 29878447Sdd errx(1, "vasprintf"); 29978447Sdd va_end(ap); 30078447Sdd 30178447Sdd *dstp = new; 30278447Sdd if (asprintf(&new, "%s %s", old, new) == -1) 30378447Sdd errx(1, "asprintf"); 30478447Sdd free(*dstp); 30578447Sdd free(old); 30678447Sdd 30778447Sdd *dstp = new; 30878447Sdd} 30978447Sdd 31078447Sdd/* 31178447Sdd * If run-time debugging is enabled, print the expansion of 'fmt'. 31278447Sdd * Otherwise, do nothing. 31378447Sdd */ 31478447Sddstatic void 31578447Sdddebugprintf(const char *fmt, ...) 31678447Sdd{ 31778447Sdd va_list ap; 31878447Sdd 31978447Sdd if (!debug) 32078447Sdd return; 32178447Sdd fprintf(stderr, "DEBUG: "); 32278447Sdd va_start(ap, fmt); 32378447Sdd vfprintf(stderr, fmt, ap); 32478447Sdd va_end(ap); 32578447Sdd fprintf(stderr, "\n"); 32678447Sdd fflush(stderr); 32778447Sdd} 32878447Sdd 32978447Sdd/* 33078447Sdd * Attach a memory disk with a known unit. 33178447Sdd */ 33278447Sddstatic void 33378447Sdddo_mdconfig_attach(const char *args, const enum md_types mdtype) 33478447Sdd{ 33578447Sdd int rv; 33678447Sdd const char *ta; /* Type arg. */ 33778447Sdd 33878447Sdd switch (mdtype) { 33978447Sdd case MD_SWAP: 34078447Sdd ta = "-t swap"; 34178447Sdd break; 34278447Sdd case MD_VNODE: 34378447Sdd ta = "-t vnode"; 34478447Sdd break; 34578447Sdd case MD_MALLOC: 34678447Sdd ta = "-t malloc"; 34778447Sdd break; 34878447Sdd default: 34978447Sdd abort(); 35078447Sdd } 351155769Ssobomax rv = run(NULL, "%s -a %s%s -u %s%d", path_mdconfig, ta, args, 35278447Sdd mdname, unit); 35378447Sdd if (rv) 35478447Sdd errx(1, "mdconfig (attach) exited with error code %d", rv); 35578447Sdd} 35678447Sdd 35778447Sdd/* 35878447Sdd * Attach a memory disk with an unknown unit; use autounit. 35978447Sdd */ 36078447Sddstatic void 36178447Sdddo_mdconfig_attach_au(const char *args, const enum md_types mdtype) 36278447Sdd{ 36378447Sdd const char *ta; /* Type arg. */ 36478447Sdd char *linep, *linebuf; /* Line pointer, line buffer. */ 36578447Sdd int fd; /* Standard output of mdconfig invocation. */ 36678447Sdd FILE *sfd; 36778447Sdd int rv; 36878447Sdd char *p; 36978447Sdd size_t linelen; 370141082Sssouhlal unsigned long ul; 37178447Sdd 37278447Sdd switch (mdtype) { 37378447Sdd case MD_SWAP: 37478447Sdd ta = "-t swap"; 37578447Sdd break; 37678447Sdd case MD_VNODE: 37778447Sdd ta = "-t vnode"; 37878447Sdd break; 37978447Sdd case MD_MALLOC: 38078447Sdd ta = "-t malloc"; 38178447Sdd break; 38278447Sdd default: 38378447Sdd abort(); 38478447Sdd } 385155769Ssobomax rv = run(&fd, "%s -a %s%s", path_mdconfig, ta, args); 38678447Sdd if (rv) 38778447Sdd errx(1, "mdconfig (attach) exited with error code %d", rv); 38878447Sdd 38978447Sdd /* Receive the unit number. */ 39078447Sdd if (norun) { /* Since we didn't run, we can't read. Fake it. */ 391153637Sdd unit = 0; 39278447Sdd return; 39378447Sdd } 39478447Sdd sfd = fdopen(fd, "r"); 39578447Sdd if (sfd == NULL) 39678447Sdd err(1, "fdopen"); 39778447Sdd linep = fgetln(sfd, &linelen); 39878447Sdd if (linep == NULL && linelen < mdnamelen + 1) 39978447Sdd errx(1, "unexpected output from mdconfig (attach)"); 40078447Sdd /* If the output format changes, we want to know about it. */ 40178447Sdd assert(strncmp(linep, mdname, mdnamelen) == 0); 40278447Sdd linebuf = malloc(linelen - mdnamelen + 1); 40378447Sdd assert(linebuf != NULL); 40478447Sdd /* Can't use strlcpy because linep is not NULL-terminated. */ 40578447Sdd strncpy(linebuf, linep + mdnamelen, linelen); 40678447Sdd linebuf[linelen] = '\0'; 407141082Sssouhlal ul = strtoul(linebuf, &p, 10); 408141082Sssouhlal if (ul == ULONG_MAX || *p != '\n') 40978447Sdd errx(1, "unexpected output from mdconfig (attach)"); 410141082Sssouhlal unit = ul; 41178447Sdd 41278447Sdd fclose(sfd); 41378447Sdd close(fd); 41478447Sdd} 41578447Sdd 41678447Sdd/* 41778447Sdd * Detach a memory disk. 41878447Sdd */ 41978447Sddstatic void 42078447Sdddo_mdconfig_detach(void) 42178447Sdd{ 42278447Sdd int rv; 42378447Sdd 424155769Ssobomax rv = run(NULL, "%s -d -u %s%d", path_mdconfig, mdname, unit); 42578447Sdd if (rv && debug) /* This is allowed to fail. */ 42678447Sdd warnx("mdconfig (detach) exited with error code %d (ignored)", 427225416Skib rv); 42878447Sdd} 42978447Sdd 43078447Sdd/* 43178447Sdd * Mount the configured memory disk. 43278447Sdd */ 43378447Sddstatic void 43478447Sdddo_mount(const char *args, const char *mtpoint) 43578447Sdd{ 43678447Sdd int rv; 43778447Sdd 438166749Smatteo rv = run(NULL, "%s%s /dev/%s%d%s %s", _PATH_MOUNT, args, 439166749Smatteo mdname, unit, mdsuffix, mtpoint); 44078447Sdd if (rv) 44178447Sdd errx(1, "mount exited with error code %d", rv); 44278447Sdd} 44378447Sdd 44478447Sdd/* 44578447Sdd * Various configuration of the mountpoint. Mostly, enact 'mip'. 44678447Sdd */ 44778447Sddstatic void 44878447Sdddo_mtptsetup(const char *mtpoint, struct mtpt_info *mip) 44978447Sdd{ 450225534Skib struct statfs sfs; 45178447Sdd 452225534Skib if (!mip->mi_have_mode && !mip->mi_have_uid && !mip->mi_have_gid) 453225534Skib return; 454225534Skib 455225534Skib if (!norun) { 456225534Skib if (statfs(mtpoint, &sfs) == -1) { 457225534Skib warn("statfs: %s", mtpoint); 458225534Skib return; 459225534Skib } 460225534Skib if ((sfs.f_flags & MNT_RDONLY) != 0) { 461225534Skib if (mip->mi_forced_pw) { 462225534Skib warnx( 463225534Skib "Not changing mode/owner of %s since it is read-only", 464225534Skib mtpoint); 465225534Skib } else { 466225534Skib debugprintf( 467225534Skib "Not changing mode/owner of %s since it is read-only", 468225534Skib mtpoint); 469225534Skib } 470225534Skib return; 471225534Skib } 472225534Skib } 473225534Skib 47478447Sdd if (mip->mi_have_mode) { 47578447Sdd debugprintf("changing mode of %s to %o.", mtpoint, 47678447Sdd mip->mi_mode); 47778447Sdd if (!norun) 47878447Sdd if (chmod(mtpoint, mip->mi_mode) == -1) 47978447Sdd err(1, "chmod: %s", mtpoint); 48078447Sdd } 48178447Sdd /* 48278447Sdd * We have to do these separately because the user may have 48378447Sdd * only specified one of them. 48478447Sdd */ 48578447Sdd if (mip->mi_have_uid) { 48678447Sdd debugprintf("changing owner (user) or %s to %u.", mtpoint, 48778447Sdd mip->mi_uid); 48878447Sdd if (!norun) 48978447Sdd if (chown(mtpoint, mip->mi_uid, -1) == -1) 49078447Sdd err(1, "chown %s to %u (user)", mtpoint, 49178447Sdd mip->mi_uid); 49278447Sdd } 49378447Sdd if (mip->mi_have_gid) { 49478447Sdd debugprintf("changing owner (group) or %s to %u.", mtpoint, 49578447Sdd mip->mi_gid); 49678447Sdd if (!norun) 49778447Sdd if (chown(mtpoint, -1, mip->mi_gid) == -1) 49878447Sdd err(1, "chown %s to %u (group)", mtpoint, 49978447Sdd mip->mi_gid); 50078447Sdd } 50178447Sdd} 50278447Sdd 50378447Sdd/* 504102231Strhodes * Put a file system on the memory disk. 50578447Sdd */ 50678447Sddstatic void 50778447Sdddo_newfs(const char *args) 50878447Sdd{ 50978447Sdd int rv; 51078447Sdd 511117033Sgordon rv = run(NULL, "%s%s /dev/%s%d", _PATH_NEWFS, args, mdname, unit); 51278447Sdd if (rv) 51378447Sdd errx(1, "newfs exited with error code %d", rv); 51478447Sdd} 51578447Sdd 51678447Sdd/* 51778447Sdd * 'str' should be a user and group name similar to the last argument 51878447Sdd * to chown(1); i.e., a user, followed by a colon, followed by a 51978447Sdd * group. The user and group in 'str' may be either a [ug]id or a 52078447Sdd * name. Upon return, the uid and gid fields in 'mip' will contain 52178447Sdd * the uid and gid of the user and group name in 'str', respectively. 52278447Sdd * 52378447Sdd * In other words, this derives a user and group id from a string 52478447Sdd * formatted like the last argument to chown(1). 525151315Srse * 526151315Srse * Notice: At this point we don't support only a username or only a 527151315Srse * group name. do_mtptsetup already does, so when this feature is 528151315Srse * desired, this is the only routine that needs to be changed. 52978447Sdd */ 53078447Sddstatic void 53178447Sddextract_ugid(const char *str, struct mtpt_info *mip) 53278447Sdd{ 53378447Sdd char *ug; /* Writable 'str'. */ 53478447Sdd char *user, *group; /* Result of extracton. */ 53578447Sdd struct passwd *pw; 53678447Sdd struct group *gr; 53778447Sdd char *p; 53878447Sdd uid_t *uid; 53978447Sdd gid_t *gid; 54078447Sdd 54178447Sdd uid = &mip->mi_uid; 54278447Sdd gid = &mip->mi_gid; 54378447Sdd mip->mi_have_uid = mip->mi_have_gid = false; 54478447Sdd 54578447Sdd /* Extract the user and group from 'str'. Format above. */ 54678711Sdd ug = strdup(str); 54778447Sdd assert(ug != NULL); 54878447Sdd group = ug; 54978447Sdd user = strsep(&group, ":"); 55078447Sdd if (user == NULL || group == NULL || *user == '\0' || *group == '\0') 55178447Sdd usage(); 55278447Sdd 55378447Sdd /* Derive uid. */ 55478447Sdd *uid = strtoul(user, &p, 10); 555117430Skan if (*uid == (uid_t)ULONG_MAX) 55678447Sdd usage(); 55778447Sdd if (*p != '\0') { 55878447Sdd pw = getpwnam(user); 55978447Sdd if (pw == NULL) 56078447Sdd errx(1, "invalid user: %s", user); 56178447Sdd *uid = pw->pw_uid; 56278447Sdd } 563151315Srse mip->mi_have_uid = true; 56478447Sdd 56578447Sdd /* Derive gid. */ 56678447Sdd *gid = strtoul(group, &p, 10); 567117430Skan if (*gid == (gid_t)ULONG_MAX) 56878447Sdd usage(); 56978447Sdd if (*p != '\0') { 57078447Sdd gr = getgrnam(group); 57178447Sdd if (gr == NULL) 57278447Sdd errx(1, "invalid group: %s", group); 57378447Sdd *gid = gr->gr_gid; 57478447Sdd } 575151315Srse mip->mi_have_gid = true; 57678447Sdd 57778447Sdd free(ug); 57878447Sdd} 57978447Sdd 58078447Sdd/* 58178447Sdd * Run a process with command name and arguments pointed to by the 58278447Sdd * formatted string 'cmdline'. Since system(3) is not used, the first 58378447Sdd * space-delimited token of 'cmdline' must be the full pathname of the 58478447Sdd * program to run. The return value is the return code of the process 58578447Sdd * spawned. If 'ofd' is non-NULL, it is set to the standard output of 58678447Sdd * the program spawned (i.e., you can read from ofd and get the output 58778447Sdd * of the program). 58878447Sdd */ 58978447Sddstatic int 59078447Sddrun(int *ofd, const char *cmdline, ...) 59178447Sdd{ 59281628Sobrien char **argv, **argvp; /* Result of splitting 'cmd'. */ 59381628Sobrien int argc; 59478447Sdd char *cmd; /* Expansion of 'cmdline'. */ 59578447Sdd int pid, status; /* Child info. */ 59678447Sdd int pfd[2]; /* Pipe to the child. */ 59778447Sdd int nfd; /* Null (/dev/null) file descriptor. */ 59878447Sdd bool dup2dn; /* Dup /dev/null to stdout? */ 59978447Sdd va_list ap; 60078447Sdd char *p; 60178447Sdd int rv, i; 60278447Sdd 60378447Sdd dup2dn = true; 60478447Sdd va_start(ap, cmdline); 60578447Sdd rv = vasprintf(&cmd, cmdline, ap); 60678447Sdd if (rv == -1) 60778447Sdd err(1, "vasprintf"); 60878447Sdd va_end(ap); 60978447Sdd 61081628Sobrien /* Split up 'cmd' into 'argv' for use with execve. */ 61181628Sobrien for (argc = 1, p = cmd; (p = strchr(p, ' ')) != NULL; p++) 61281628Sobrien argc++; /* 'argc' generation loop. */ 61381628Sobrien argv = (char **)malloc(sizeof(*argv) * (argc + 1)); 61481628Sobrien assert(argv != NULL); 61581628Sobrien for (p = cmd, argvp = argv; (*argvp = strsep(&p, " ")) != NULL;) 616169129Syar if (**argvp != '\0') 61781628Sobrien if (++argvp >= &argv[argc]) { 61881628Sobrien *argvp = NULL; 61978447Sdd break; 62078447Sdd } 62181628Sobrien assert(*argv); 622169129Syar /* The argv array ends up NULL-terminated here. */ 62378447Sdd 62478447Sdd /* Make sure the above loop works as expected. */ 62578447Sdd if (debug) { 62678447Sdd /* 62778447Sdd * We can't, but should, use debugprintf here. First, 62878447Sdd * it appends a trailing newline to the output, and 62978447Sdd * second it prepends "DEBUG: " to the output. The 63078447Sdd * former is a problem for this would-be first call, 63178447Sdd * and the latter for the would-be call inside the 63278447Sdd * loop. 63378447Sdd */ 63478447Sdd (void)fprintf(stderr, "DEBUG: running:"); 63578447Sdd /* Should be equivilent to 'cmd' (before strsep, of course). */ 63681628Sobrien for (i = 0; argv[i] != NULL; i++) 63781628Sobrien (void)fprintf(stderr, " %s", argv[i]); 63878447Sdd (void)fprintf(stderr, "\n"); 63978447Sdd } 64078447Sdd 64178447Sdd /* Create a pipe if necessary and fork the helper program. */ 64278447Sdd if (ofd != NULL) { 64378447Sdd if (pipe(&pfd[0]) == -1) 64478447Sdd err(1, "pipe"); 64578447Sdd *ofd = pfd[0]; 64678447Sdd dup2dn = false; 64778447Sdd } 64878447Sdd pid = fork(); 64978447Sdd switch (pid) { 65078447Sdd case 0: 65178447Sdd /* XXX can we call err() in here? */ 65278447Sdd if (norun) 65378447Sdd _exit(0); 65478447Sdd if (ofd != NULL) 65578447Sdd if (dup2(pfd[1], STDOUT_FILENO) < 0) 65678447Sdd err(1, "dup2"); 65778447Sdd if (!loudsubs) { 65878447Sdd nfd = open(_PATH_DEVNULL, O_RDWR); 65978447Sdd if (nfd == -1) 66078447Sdd err(1, "open: %s", _PATH_DEVNULL); 66178447Sdd if (dup2(nfd, STDIN_FILENO) < 0) 66278447Sdd err(1, "dup2"); 66378447Sdd if (dup2dn) 66478447Sdd if (dup2(nfd, STDOUT_FILENO) < 0) 66578447Sdd err(1, "dup2"); 66678447Sdd if (dup2(nfd, STDERR_FILENO) < 0) 66778447Sdd err(1, "dup2"); 66878447Sdd } 66978447Sdd 67081628Sobrien (void)execv(argv[0], argv); 67181628Sobrien warn("exec: %s", argv[0]); 67278447Sdd _exit(-1); 67378447Sdd case -1: 67478447Sdd err(1, "fork"); 67578447Sdd } 67678447Sdd 67778447Sdd free(cmd); 67881628Sobrien free(argv); 67978447Sdd while (waitpid(pid, &status, 0) != pid) 68078447Sdd ; 68178447Sdd return (WEXITSTATUS(status)); 68278447Sdd} 68378447Sdd 68478447Sddstatic void 68578447Sddusage(void) 68678447Sdd{ 68778447Sdd 688163952Sru fprintf(stderr, 689225416Skib"usage: %s [-DLlMNnPStUX] [-a maxcontig] [-b block-size]\n" 690166752Smatteo"\t[-c blocks-per-cylinder-group][-d max-extent-size] [-E path-mdconfig]\n" 691166752Smatteo"\t[-e maxbpg] [-F file] [-f frag-size] [-i bytes] [-m percent-free]\n" 692169560Sremko"\t[-O optimization] [-o mount-options]\n" 693166752Smatteo"\t[-p permissions] [-s size] [-v version] [-w user:group]\n" 694166752Smatteo"\tmd-device mount-point\n", getprogname()); 69578447Sdd exit(1); 69678447Sdd} 697