mdmfs.c revision 95258
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/*
2878447Sdd * mdmfs (md/MFS) is a wrapper around mdconfig(8), disklabel(8),
2978447Sdd * newfs(8), and mount(8) that mimics the command line option set of
3078447Sdd * the deprecated mount_mfs(8).
3178447Sdd */
3278447Sdd
3378447Sdd#ifndef lint
3478447Sddstatic const char rcsid[] =
3578447Sdd  "$FreeBSD: head/sbin/mdmfs/mdmfs.c 95258 2002-04-22 13:44:47Z des $";
3678447Sdd#endif /* not lint */
3778447Sdd
3878447Sdd#include <sys/param.h>
3978447Sdd#include <sys/mdioctl.h>
4078447Sdd#include <sys/stat.h>
4178447Sdd#include <sys/wait.h>
4278447Sdd
4378447Sdd#include <assert.h>
4478447Sdd#include <err.h>
4578447Sdd#include <fcntl.h>
4678447Sdd#include <grp.h>
4778447Sdd#include <paths.h>
4878447Sdd#include <pwd.h>
4978447Sdd#include <stdarg.h>
5078447Sdd#include <stdio.h>
5178447Sdd#include <stdlib.h>
5278447Sdd#include <string.h>
5378447Sdd#include <unistd.h>
5478447Sdd
5578447Sdd#include "pathnames.h"
5678447Sdd
5778447Sddtypedef enum { false, true } bool;
5878447Sdd
5978447Sddstruct mtpt_info {
6078447Sdd	uid_t		 mi_uid;
6178447Sdd	bool		 mi_have_uid;
6278447Sdd	gid_t		 mi_gid;
6378447Sdd	bool		 mi_have_gid;
6478447Sdd	mode_t		 mi_mode;
6578447Sdd	bool		 mi_have_mode;
6678447Sdd};
6778447Sdd
6881742Sddstatic	bool compat;		/* Full compatibility with mount_mfs? */
6978447Sddstatic	bool debug;		/* Emit debugging information? */
7078447Sddstatic	bool loudsubs;		/* Suppress output from helper programs? */
7178447Sddstatic	bool norun;		/* Actually run the helper programs? */
7278447Sddstatic	int unit;      		/* The unit we're working with. */
7378447Sddstatic	const char *mdname;	/* Name of memory disk device (e.g., "md"). */
7478447Sddstatic	size_t mdnamelen;	/* Length of mdname. */
7578447Sdd
7679052Skrisstatic void	 argappend(char **, const char *, ...) __printflike(2, 3);
7779052Skrisstatic void	 debugprintf(const char *, ...) __printflike(1, 2);
7878447Sddstatic void	 do_disklabel(void);
7978447Sddstatic void	 do_mdconfig_attach(const char *, const enum md_types);
8078447Sddstatic void	 do_mdconfig_attach_au(const char *, const enum md_types);
8178447Sddstatic void	 do_mdconfig_detach(void);
8278447Sddstatic void	 do_mount(const char *, const char *);
8378447Sddstatic void	 do_mtptsetup(const char *, struct mtpt_info *);
8478447Sddstatic void	 do_newfs(const char *);
8578447Sddstatic void	 extract_ugid(const char *, struct mtpt_info *);
8679052Skrisstatic int	 run(int *, const char *, ...) __printflike(2, 3);
8778447Sddstatic void	 usage(void);
8878447Sdd
8978447Sddint
9081628Sobrienmain(int argc, char **argv)
9178447Sdd{
9278447Sdd	struct mtpt_info mi;		/* Mountpoint info. */
9378447Sdd	char *mdconfig_arg, *newfs_arg,	/* Args to helper programs. */
9478447Sdd	    *mount_arg;
9578447Sdd	enum md_types mdtype;		/* The type of our memory disk. */
9678447Sdd	bool have_mdtype;
9778447Sdd	bool detach, softdep, autounit;
9878447Sdd	char *mtpoint, *unitstr;
9978447Sdd	char ch, *p;
10078447Sdd
10178447Sdd	/* Misc. initialization. */
10278447Sdd	(void)memset(&mi, '\0', sizeof(mi));
10378447Sdd	detach = true;
10478447Sdd	softdep = true;
10578447Sdd	autounit = false;
10678447Sdd	have_mdtype = false;
10778447Sdd	mdname = MD_NAME;
10878447Sdd	mdnamelen = strlen(mdname);
10978447Sdd	/*
11078447Sdd	 * Can't set these to NULL.  They may be passed to the
11178447Sdd	 * respective programs without modification.  I.e., we may not
11278447Sdd	 * receive any command-line options which will caused them to
11378447Sdd	 * be modified.
11478447Sdd	 */
11578447Sdd	mdconfig_arg = strdup("");
11678447Sdd	newfs_arg = strdup("");
11778447Sdd	mount_arg = strdup("");
11878447Sdd
11984167Siedowse	/* If we were started as mount_mfs or mfs, imply -C. */
12084167Siedowse	if (strcmp(getprogname(), "mount_mfs") == 0 ||
12184167Siedowse	    strcmp(getprogname(), "mfs") == 0)
12281742Sdd		compat = true;
12381742Sdd
12481628Sobrien	while ((ch = getopt(argc, argv,
12581742Sdd	    "a:b:Cc:Dd:e:F:f:hi:LMm:Nn:O:o:p:Ss:t:Uw:X")) != -1)
12678447Sdd		switch (ch) {
12778447Sdd		case 'a':
12878447Sdd			argappend(&newfs_arg, "-a %s", optarg);
12978447Sdd			break;
13078447Sdd		case 'b':
13178447Sdd			argappend(&newfs_arg, "-b %s", optarg);
13278447Sdd			break;
13381742Sdd		case 'C':
13481742Sdd			if (compat)
13581742Sdd				usage();
13681742Sdd			compat = true;
13781742Sdd			break;
13878447Sdd		case 'c':
13978447Sdd			argappend(&newfs_arg, "-c %s", optarg);
14078447Sdd			break;
14178447Sdd		case 'D':
14281742Sdd			if (compat)
14381742Sdd				usage();
14478447Sdd			detach = false;
14578447Sdd			break;
14678447Sdd		case 'd':
14778447Sdd			argappend(&newfs_arg, "-d %s", optarg);
14878447Sdd			break;
14978447Sdd		case 'e':
15078447Sdd			argappend(&newfs_arg, "-e %s", optarg);
15178447Sdd			break;
15278447Sdd		case 'F':
15378447Sdd			if (have_mdtype)
15478447Sdd				usage();
15578447Sdd			mdtype = MD_VNODE;
15678447Sdd			have_mdtype = true;
15778447Sdd			argappend(&mdconfig_arg, "-f %s", optarg);
15878447Sdd			break;
15978447Sdd		case 'f':
16078447Sdd			argappend(&newfs_arg, "-f %s", optarg);
16178447Sdd			break;
16278447Sdd		case 'h':
16378447Sdd			usage();
16478447Sdd			break;
16578447Sdd		case 'i':
16678447Sdd			argappend(&newfs_arg, "-i %s", optarg);
16778447Sdd			break;
16878447Sdd		case 'L':
16981742Sdd			if (compat)
17081742Sdd				usage();
17178447Sdd			loudsubs = true;
17278447Sdd			break;
17378447Sdd		case 'M':
17478447Sdd			if (have_mdtype)
17578447Sdd				usage();
17678447Sdd			mdtype = MD_MALLOC;
17778447Sdd			have_mdtype = true;
17878447Sdd			break;
17978447Sdd		case 'm':
18078447Sdd			argappend(&newfs_arg, "-m %s", optarg);
18178447Sdd			break;
18278447Sdd		case 'N':
18381742Sdd			if (compat)
18481742Sdd				usage();
18578447Sdd			norun = true;
18678447Sdd			break;
18778447Sdd		case 'n':
18878447Sdd			argappend(&newfs_arg, "-n %s", optarg);
18978447Sdd			break;
19078447Sdd		case 'O':
19178447Sdd			argappend(&newfs_arg, "-o %s", optarg);
19278447Sdd			break;
19378447Sdd		case 'o':
19478447Sdd			argappend(&mount_arg, "-o %s", optarg);
19578447Sdd			break;
19678447Sdd		case 'p':
19781742Sdd			if (compat)
19881742Sdd				usage();
19978447Sdd			if (*optarg >= '0' && *optarg <= '7')
20078447Sdd				mi.mi_mode = strtol(optarg, NULL, 8);
20178447Sdd			if ((mi.mi_mode & ~07777) != 0)
20278447Sdd				usage();
20378447Sdd			mi.mi_have_mode = true;
20478447Sdd			break;
20578447Sdd		case 'S':
20681742Sdd			if (compat)
20781742Sdd				usage();
20878447Sdd			softdep = false;
20978447Sdd			break;
21078447Sdd		case 's':
21178447Sdd			argappend(&mdconfig_arg, "-s %s", optarg);
21278447Sdd			break;
21381742Sdd		case 'U':
21481742Sdd			softdep = true;
21581742Sdd			break;
21678447Sdd		case 'w':
21781742Sdd			if (compat)
21881742Sdd				usage();
21978447Sdd			extract_ugid(optarg, &mi);
22078447Sdd			break;
22178447Sdd		case 'X':
22281742Sdd			if (compat)
22381742Sdd				usage();
22478447Sdd			debug = true;
22578447Sdd			break;
22678447Sdd		default:
22778447Sdd			usage();
22878447Sdd		}
22981628Sobrien	argc -= optind;
23081628Sobrien	argv += optind;
23181628Sobrien	if (argc < 2)
23278447Sdd		usage();
23378447Sdd
23481742Sdd	/* Make compatibility assumptions. */
23581742Sdd	if (compat) {
23681742Sdd		mi.mi_mode = 01777;
23781742Sdd		mi.mi_have_mode = true;
23881742Sdd	}
23981742Sdd
24078447Sdd	/* Derive 'unit' (global). */
24181628Sobrien	unitstr = argv[0];
24278447Sdd	if (strncmp(unitstr, "/dev/", 5) == 0)
24378447Sdd		unitstr += 5;
24478447Sdd	if (strncmp(unitstr, mdname, mdnamelen) == 0)
24578447Sdd		unitstr += mdnamelen;
24678447Sdd	if (*unitstr == '\0') {
24778447Sdd		autounit = true;
24878447Sdd		unit = -1;
24978447Sdd	} else {
25078447Sdd		unit = strtoul(unitstr, &p, 10);
25178447Sdd		if ((unsigned)unit == ULONG_MAX || *p != '\0')
25278447Sdd			errx(1, "bad device unit: %s", unitstr);
25378447Sdd	}
25478447Sdd
25581628Sobrien	mtpoint = argv[1];
25678447Sdd	if (!have_mdtype)
25778447Sdd		mdtype = MD_SWAP;
25878447Sdd	if (softdep)
25978447Sdd		argappend(&newfs_arg, "-U");
26078447Sdd
26178447Sdd	/* Do the work. */
26278447Sdd	if (detach && !autounit)
26378447Sdd		do_mdconfig_detach();
26478447Sdd	if (autounit)
26578447Sdd		do_mdconfig_attach_au(mdconfig_arg, mdtype);
26678447Sdd	else
26778447Sdd		do_mdconfig_attach(mdconfig_arg, mdtype);
26878447Sdd	do_disklabel();
26978447Sdd	do_newfs(newfs_arg);
27078447Sdd	do_mount(mount_arg, mtpoint);
27178447Sdd	do_mtptsetup(mtpoint, &mi);
27278447Sdd
27378447Sdd	return (0);
27478447Sdd}
27578447Sdd
27678447Sdd/*
27778447Sdd * Append the expansion of 'fmt' to the buffer pointed to by '*dstp';
27878447Sdd * reallocate as required.
27978447Sdd */
28078447Sddstatic void
28178447Sddargappend(char **dstp, const char *fmt, ...)
28278447Sdd{
28378447Sdd	char *old, *new;
28478447Sdd	va_list ap;
28578447Sdd
28678447Sdd	old = *dstp;
28778447Sdd	assert(old != NULL);
28878447Sdd
28978447Sdd	va_start(ap, fmt);
29078447Sdd	if (vasprintf(&new, fmt,ap) == -1)
29178447Sdd		errx(1, "vasprintf");
29278447Sdd	va_end(ap);
29378447Sdd
29478447Sdd	*dstp = new;
29578447Sdd	if (asprintf(&new, "%s %s", old, new) == -1)
29678447Sdd		errx(1, "asprintf");
29778447Sdd	free(*dstp);
29878447Sdd	free(old);
29978447Sdd
30078447Sdd	*dstp = new;
30178447Sdd}
30278447Sdd
30378447Sdd/*
30478447Sdd * If run-time debugging is enabled, print the expansion of 'fmt'.
30578447Sdd * Otherwise, do nothing.
30678447Sdd */
30778447Sddstatic void
30878447Sdddebugprintf(const char *fmt, ...)
30978447Sdd{
31078447Sdd	va_list ap;
31178447Sdd
31278447Sdd	if (!debug)
31378447Sdd		return;
31478447Sdd	fprintf(stderr, "DEBUG: ");
31578447Sdd	va_start(ap, fmt);
31678447Sdd	vfprintf(stderr, fmt, ap);
31778447Sdd	va_end(ap);
31878447Sdd	fprintf(stderr, "\n");
31978447Sdd	fflush(stderr);
32078447Sdd}
32178447Sdd
32278447Sdd/*
32378447Sdd * Label the memory disk.
32478447Sdd */
32578447Sddstatic void
32678447Sdddo_disklabel(void)
32778447Sdd{
32878447Sdd	int rv;
32978447Sdd
33078447Sdd	rv = run(NULL, "%s -r -w %s%d auto", PATH_DISKLABEL, mdname, unit);
33178447Sdd	if (rv)
33278447Sdd		errx(1, "disklabel exited with error code %d", rv);
33378447Sdd}
33478447Sdd
33578447Sdd/*
33678447Sdd * Attach a memory disk with a known unit.
33778447Sdd */
33878447Sddstatic void
33978447Sdddo_mdconfig_attach(const char *args, const enum md_types mdtype)
34078447Sdd{
34178447Sdd	int rv;
34278447Sdd	const char *ta;		/* Type arg. */
34378447Sdd
34478447Sdd	switch (mdtype) {
34578447Sdd	case MD_SWAP:
34678447Sdd		ta = "-t swap";
34778447Sdd		break;
34878447Sdd	case MD_VNODE:
34978447Sdd		ta = "-t vnode";
35078447Sdd		break;
35178447Sdd	case MD_MALLOC:
35278447Sdd		ta = "-t malloc";
35378447Sdd		break;
35478447Sdd	default:
35578447Sdd		abort();
35678447Sdd	}
35778447Sdd	rv = run(NULL, "%s -a %s%s -u %s%d", PATH_MDCONFIG, ta, args,
35878447Sdd	    mdname, unit);
35978447Sdd	if (rv)
36078447Sdd		errx(1, "mdconfig (attach) exited with error code %d", rv);
36178447Sdd}
36278447Sdd
36378447Sdd/*
36478447Sdd * Attach a memory disk with an unknown unit; use autounit.
36578447Sdd */
36678447Sddstatic void
36778447Sdddo_mdconfig_attach_au(const char *args, const enum md_types mdtype)
36878447Sdd{
36978447Sdd	const char *ta;		/* Type arg. */
37078447Sdd	char *linep, *linebuf; 	/* Line pointer, line buffer. */
37178447Sdd	int fd;			/* Standard output of mdconfig invocation. */
37278447Sdd	FILE *sfd;
37378447Sdd	int rv;
37478447Sdd	char *p;
37578447Sdd	size_t linelen;
37678447Sdd
37778447Sdd	switch (mdtype) {
37878447Sdd	case MD_SWAP:
37978447Sdd		ta = "-t swap";
38078447Sdd		break;
38178447Sdd	case MD_VNODE:
38278447Sdd		ta = "-t vnode";
38378447Sdd		break;
38478447Sdd	case MD_MALLOC:
38578447Sdd		ta = "-t malloc";
38678447Sdd		break;
38778447Sdd	default:
38878447Sdd		abort();
38978447Sdd	}
39078447Sdd	rv = run(&fd, "%s -a %s%s", PATH_MDCONFIG, ta, args);
39178447Sdd	if (rv)
39278447Sdd		errx(1, "mdconfig (attach) exited with error code %d", rv);
39378447Sdd
39478447Sdd	/* Receive the unit number. */
39578447Sdd	if (norun) {	/* Since we didn't run, we can't read.  Fake it. */
39678447Sdd		unit = -1;
39778447Sdd		return;
39878447Sdd	}
39978447Sdd	sfd = fdopen(fd, "r");
40078447Sdd	if (sfd == NULL)
40178447Sdd		err(1, "fdopen");
40278447Sdd	linep = fgetln(sfd, &linelen);
40378447Sdd	if (linep == NULL && linelen < mdnamelen + 1)
40478447Sdd		errx(1, "unexpected output from mdconfig (attach)");
40578447Sdd	/* If the output format changes, we want to know about it. */
40678447Sdd	assert(strncmp(linep, mdname, mdnamelen) == 0);
40778447Sdd	linebuf = malloc(linelen - mdnamelen + 1);
40878447Sdd	assert(linebuf != NULL);
40978447Sdd	/* Can't use strlcpy because linep is not NULL-terminated. */
41078447Sdd	strncpy(linebuf, linep + mdnamelen, linelen);
41178447Sdd	linebuf[linelen] = '\0';
41278447Sdd	unit = strtoul(linebuf, &p, 10);
41378447Sdd	if ((unsigned)unit == ULONG_MAX || *p != '\n')
41478447Sdd		errx(1, "unexpected output from mdconfig (attach)");
41578447Sdd
41678447Sdd	fclose(sfd);
41778447Sdd	close(fd);
41878447Sdd}
41978447Sdd
42078447Sdd/*
42178447Sdd * Detach a memory disk.
42278447Sdd */
42378447Sddstatic void
42478447Sdddo_mdconfig_detach(void)
42578447Sdd{
42678447Sdd	int rv;
42778447Sdd
42878447Sdd	rv = run(NULL, "%s -d -u %s%d", PATH_MDCONFIG, mdname, unit);
42978447Sdd	if (rv && debug)	/* This is allowed to fail. */
43078447Sdd		warnx("mdconfig (detach) exited with error code %d (ignored)",
43178447Sdd		      rv);
43278447Sdd}
43378447Sdd
43478447Sdd/*
43578447Sdd * Mount the configured memory disk.
43678447Sdd */
43778447Sddstatic void
43878447Sdddo_mount(const char *args, const char *mtpoint)
43978447Sdd{
44078447Sdd	int rv;
44178447Sdd
44278447Sdd	rv = run(NULL, "%s%s /dev/%s%dc %s", PATH_MOUNT, args,
44378447Sdd	    mdname, unit, mtpoint);
44478447Sdd	if (rv)
44578447Sdd		errx(1, "mount exited with error code %d", rv);
44678447Sdd}
44778447Sdd
44878447Sdd/*
44978447Sdd * Various configuration of the mountpoint.  Mostly, enact 'mip'.
45078447Sdd */
45178447Sddstatic void
45278447Sdddo_mtptsetup(const char *mtpoint, struct mtpt_info *mip)
45378447Sdd{
45478447Sdd
45578447Sdd	if (mip->mi_have_mode) {
45678447Sdd		debugprintf("changing mode of %s to %o.", mtpoint,
45778447Sdd		    mip->mi_mode);
45878447Sdd		if (!norun)
45978447Sdd			if (chmod(mtpoint, mip->mi_mode) == -1)
46078447Sdd				err(1, "chmod: %s", mtpoint);
46178447Sdd	}
46278447Sdd	/*
46378447Sdd	 * We have to do these separately because the user may have
46478447Sdd	 * only specified one of them.
46578447Sdd	 */
46678447Sdd	if (mip->mi_have_uid) {
46778447Sdd		debugprintf("changing owner (user) or %s to %u.", mtpoint,
46878447Sdd		    mip->mi_uid);
46978447Sdd		if (!norun)
47078447Sdd			if (chown(mtpoint, mip->mi_uid, -1) == -1)
47178447Sdd				err(1, "chown %s to %u (user)", mtpoint,
47278447Sdd				    mip->mi_uid);
47378447Sdd	}
47478447Sdd	if (mip->mi_have_gid) {
47578447Sdd		debugprintf("changing owner (group) or %s to %u.", mtpoint,
47678447Sdd		    mip->mi_gid);
47778447Sdd		if (!norun)
47878447Sdd			if (chown(mtpoint, -1, mip->mi_gid) == -1)
47978447Sdd				err(1, "chown %s to %u (group)", mtpoint,
48078447Sdd				    mip->mi_gid);
48178447Sdd	}
48278447Sdd}
48378447Sdd
48478447Sdd/*
48578447Sdd * Put a filesystem on the memory disk.
48678447Sdd */
48778447Sddstatic void
48878447Sdddo_newfs(const char *args)
48978447Sdd{
49078447Sdd	int rv;
49178447Sdd
49278447Sdd	rv = run(NULL, "%s%s /dev/%s%dc", PATH_NEWFS, args, mdname, unit);
49378447Sdd	if (rv)
49478447Sdd		errx(1, "newfs exited with error code %d", rv);
49578447Sdd}
49678447Sdd
49778447Sdd/*
49878447Sdd * 'str' should be a user and group name similar to the last argument
49978447Sdd * to chown(1); i.e., a user, followed by a colon, followed by a
50078447Sdd * group.  The user and group in 'str' may be either a [ug]id or a
50178447Sdd * name.  Upon return, the uid and gid fields in 'mip' will contain
50278447Sdd * the uid and gid of the user and group name in 'str', respectively.
50378447Sdd *
50478447Sdd * In other words, this derives a user and group id from a string
50578447Sdd * formatted like the last argument to chown(1).
50678447Sdd */
50778447Sddstatic void
50878447Sddextract_ugid(const char *str, struct mtpt_info *mip)
50978447Sdd{
51078447Sdd	char *ug;			/* Writable 'str'. */
51178447Sdd	char *user, *group;		/* Result of extracton. */
51278447Sdd	struct passwd *pw;
51378447Sdd	struct group *gr;
51478447Sdd	char *p;
51578447Sdd	uid_t *uid;
51678447Sdd	gid_t *gid;
51778447Sdd
51878447Sdd	uid = &mip->mi_uid;
51978447Sdd	gid = &mip->mi_gid;
52078447Sdd	mip->mi_have_uid = mip->mi_have_gid = false;
52178447Sdd
52278447Sdd	/* Extract the user and group from 'str'.  Format above. */
52378711Sdd	ug = strdup(str);
52478447Sdd	assert(ug != NULL);
52578447Sdd	group = ug;
52678447Sdd	user = strsep(&group, ":");
52778447Sdd	if (user == NULL || group == NULL || *user == '\0' || *group == '\0')
52878447Sdd		usage();
52978447Sdd
53078447Sdd	/* Derive uid. */
53178447Sdd	*uid = strtoul(user, &p, 10);
53278447Sdd	if ((unsigned)*uid == ULONG_MAX)
53378447Sdd		usage();
53478447Sdd	if (*p != '\0') {
53578447Sdd		pw = getpwnam(user);
53678447Sdd		if (pw == NULL)
53778447Sdd			errx(1, "invalid user: %s", user);
53878447Sdd		*uid = pw->pw_uid;
53978447Sdd		mip->mi_have_uid = true;
54078447Sdd	}
54178447Sdd
54278447Sdd	/* Derive gid. */
54378447Sdd	*gid = strtoul(group, &p, 10);
54478447Sdd	if ((unsigned)*gid == ULONG_MAX)
54578447Sdd		usage();
54678447Sdd	if (*p != '\0') {
54778447Sdd		gr = getgrnam(group);
54878447Sdd		if (gr == NULL)
54978447Sdd			errx(1, "invalid group: %s", group);
55078447Sdd		*gid = gr->gr_gid;
55178447Sdd		mip->mi_have_gid = true;
55278447Sdd	}
55378447Sdd
55478447Sdd	free(ug);
55578447Sdd	/*
55678447Sdd	 * At this point we don't support only a username or only a
55778447Sdd	 * group name.  do_mtptsetup already does, so when this
55878447Sdd	 * feature is desired, this is the only routine that needs to
55978447Sdd	 * be changed.
56078447Sdd	 */
56178447Sdd	assert(mip->mi_have_uid);
56278447Sdd	assert(mip->mi_have_gid);
56378447Sdd}
56478447Sdd
56578447Sdd/*
56678447Sdd * Run a process with command name and arguments pointed to by the
56778447Sdd * formatted string 'cmdline'.  Since system(3) is not used, the first
56878447Sdd * space-delimited token of 'cmdline' must be the full pathname of the
56978447Sdd * program to run.  The return value is the return code of the process
57078447Sdd * spawned.  If 'ofd' is non-NULL, it is set to the standard output of
57178447Sdd * the program spawned (i.e., you can read from ofd and get the output
57278447Sdd * of the program).
57378447Sdd */
57478447Sddstatic int
57578447Sddrun(int *ofd, const char *cmdline, ...)
57678447Sdd{
57781628Sobrien	char **argv, **argvp;		/* Result of splitting 'cmd'. */
57881628Sobrien	int argc;
57978447Sdd	char *cmd;			/* Expansion of 'cmdline'. */
58078447Sdd	int pid, status;		/* Child info. */
58178447Sdd	int pfd[2];			/* Pipe to the child. */
58278447Sdd	int nfd;			/* Null (/dev/null) file descriptor. */
58378447Sdd	bool dup2dn;			/* Dup /dev/null to stdout? */
58478447Sdd	va_list ap;
58578447Sdd	char *p;
58678447Sdd	int rv, i;
58778447Sdd
58878447Sdd	dup2dn = true;
58978447Sdd	va_start(ap, cmdline);
59078447Sdd	rv = vasprintf(&cmd, cmdline, ap);
59178447Sdd	if (rv == -1)
59278447Sdd		err(1, "vasprintf");
59378447Sdd	va_end(ap);
59478447Sdd
59581628Sobrien	/* Split up 'cmd' into 'argv' for use with execve. */
59681628Sobrien	for (argc = 1, p = cmd; (p = strchr(p, ' ')) != NULL; p++)
59781628Sobrien		argc++;		/* 'argc' generation loop. */
59881628Sobrien	argv = (char **)malloc(sizeof(*argv) * (argc + 1));
59981628Sobrien	assert(argv != NULL);
60081628Sobrien	for (p = cmd, argvp = argv; (*argvp = strsep(&p, " ")) != NULL;)
60181628Sobrien		if (**argv != '\0')
60281628Sobrien			if (++argvp >= &argv[argc]) {
60381628Sobrien				*argvp = NULL;
60478447Sdd				break;
60578447Sdd			}
60681628Sobrien	assert(*argv);
60778447Sdd
60878447Sdd	/* Make sure the above loop works as expected. */
60978447Sdd	if (debug) {
61078447Sdd		/*
61178447Sdd		 * We can't, but should, use debugprintf here.  First,
61278447Sdd		 * it appends a trailing newline to the output, and
61378447Sdd		 * second it prepends "DEBUG: " to the output.  The
61478447Sdd		 * former is a problem for this would-be first call,
61578447Sdd		 * and the latter for the would-be call inside the
61678447Sdd		 * loop.
61778447Sdd		 */
61878447Sdd		(void)fprintf(stderr, "DEBUG: running:");
61978447Sdd		/* Should be equivilent to 'cmd' (before strsep, of course). */
62081628Sobrien		for (i = 0; argv[i] != NULL; i++)
62181628Sobrien			(void)fprintf(stderr, " %s", argv[i]);
62278447Sdd		(void)fprintf(stderr, "\n");
62378447Sdd	}
62478447Sdd
62578447Sdd	/* Create a pipe if necessary and fork the helper program. */
62678447Sdd	if (ofd != NULL) {
62778447Sdd		if (pipe(&pfd[0]) == -1)
62878447Sdd			err(1, "pipe");
62978447Sdd		*ofd = pfd[0];
63078447Sdd		dup2dn = false;
63178447Sdd	}
63278447Sdd	pid = fork();
63378447Sdd	switch (pid) {
63478447Sdd	case 0:
63578447Sdd		/* XXX can we call err() in here? */
63678447Sdd		if (norun)
63778447Sdd			_exit(0);
63878447Sdd		if (ofd != NULL)
63978447Sdd			if (dup2(pfd[1], STDOUT_FILENO) < 0)
64078447Sdd				err(1, "dup2");
64178447Sdd		if (!loudsubs) {
64278447Sdd			nfd = open(_PATH_DEVNULL, O_RDWR);
64378447Sdd			if (nfd == -1)
64478447Sdd				err(1, "open: %s", _PATH_DEVNULL);
64578447Sdd			if (dup2(nfd, STDIN_FILENO) < 0)
64678447Sdd				err(1, "dup2");
64778447Sdd			if (dup2dn)
64878447Sdd				if (dup2(nfd, STDOUT_FILENO) < 0)
64978447Sdd				   err(1, "dup2");
65078447Sdd			if (dup2(nfd, STDERR_FILENO) < 0)
65178447Sdd				err(1, "dup2");
65278447Sdd		}
65378447Sdd
65481628Sobrien		(void)execv(argv[0], argv);
65581628Sobrien		warn("exec: %s", argv[0]);
65678447Sdd		_exit(-1);
65778447Sdd	case -1:
65878447Sdd		err(1, "fork");
65978447Sdd	}
66078447Sdd
66178447Sdd	free(cmd);
66281628Sobrien	free(argv);
66378447Sdd	while (waitpid(pid, &status, 0) != pid)
66478447Sdd		;
66578447Sdd	return (WEXITSTATUS(status));
66678447Sdd}
66778447Sdd
66878447Sddstatic void
66978447Sddusage(void)
67078447Sdd{
67181742Sdd	const char *name;
67278447Sdd
67381742Sdd	if (compat)
67481742Sdd		name = getprogname();
67581742Sdd	else
67681742Sdd		name = "mdmfs";
67781742Sdd	if (!compat)
67881742Sdd		fprintf(stderr,
67995258Sdes"usage: %s [-DLMNSUX] [-a maxcontig [-b block-size] [-c cylinders]\n"
68081742Sdd"\t[-d rotdelay] [-e maxbpg] [-F file] [-f frag-size] [-i bytes]\n"
68181742Sdd"\t[-m percent-free] [-n rotational-positions] [-O optimization]\n"
68281742Sdd"\t[-o mount-options] [-p permissions] [-s size] [-w user:group]\n"
68381742Sdd"\tmd-device mount-point\n", name);
68478447Sdd	fprintf(stderr,
68595258Sdes"usage: %s -C [-NU] [-a maxcontig] [-b block-size] [-c cylinders]\n"
68678447Sdd"\t[-d rotdelay] [-e maxbpg] [-F file] [-f frag-size] [-i bytes]\n"
68778447Sdd"\t[-m percent-free] [-n rotational-positions] [-O optimization]\n"
68881742Sdd"\t[-o mount-options] [-s size] md-device mount-point\n", name);
68978447Sdd	exit(1);
69078447Sdd}
691