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:");
635229778Suqs		/* Should be equivalent 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