diff options
Diffstat (limited to 'sbin/mdmfs')
-rw-r--r-- | sbin/mdmfs/Makefile | 9 | ||||
-rw-r--r-- | sbin/mdmfs/Makefile.depend | 18 | ||||
-rw-r--r-- | sbin/mdmfs/mdmfs.8 | 404 | ||||
-rw-r--r-- | sbin/mdmfs/mdmfs.c | 699 |
4 files changed, 1130 insertions, 0 deletions
diff --git a/sbin/mdmfs/Makefile b/sbin/mdmfs/Makefile new file mode 100644 index 000000000000..e8d2d7b46f3f --- /dev/null +++ b/sbin/mdmfs/Makefile @@ -0,0 +1,9 @@ +# $FreeBSD$ + +PACKAGE=runtime +PROG= mdmfs +LINKS= ${BINDIR}/${PROG} ${BINDIR}/mount_mfs +MAN= mdmfs.8 +MLINKS+= mdmfs.8 mount_mfs.8 + +.include <bsd.prog.mk> diff --git a/sbin/mdmfs/Makefile.depend b/sbin/mdmfs/Makefile.depend new file mode 100644 index 000000000000..3646e2e2b1af --- /dev/null +++ b/sbin/mdmfs/Makefile.depend @@ -0,0 +1,18 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + gnu/lib/csu \ + gnu/lib/libgcc \ + include \ + include/xlocale \ + lib/${CSU_DIR} \ + lib/libc \ + lib/libcompiler_rt \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/sbin/mdmfs/mdmfs.8 b/sbin/mdmfs/mdmfs.8 new file mode 100644 index 000000000000..30a81fa49cf6 --- /dev/null +++ b/sbin/mdmfs/mdmfs.8 @@ -0,0 +1,404 @@ +.\" +.\" Copyright (c) 2001 Dima Dorfman. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD$ +.\" +.Dd September 9, 2016 +.Dt MDMFS 8 +.Os +.Sh NAME +.Nm mdmfs , +.Nm mount_mfs +.Nd configure and mount an in-memory file system using the +.Xr md 4 +driver +.Sh SYNOPSIS +.Nm +.Op Fl DLlMNnPStTUX +.Op Fl a Ar maxcontig +.Op Fl b Ar block-size +.Op Fl c Ar blocks-per-cylinder-group +.Op Fl d Ar max-extent-size +.Op Fl E Ar path-mdconfig +.Op Fl e Ar maxbpg +.Op Fl F Ar file +.Op Fl f Ar frag-size +.Op Fl i Ar bytes +.Op Fl m Ar percent-free +.Op Fl O Ar optimization +.Op Fl o Ar mount-options +.Op Fl p Ar permissions +.Op Fl s Ar size +.Op Fl T Ar fstype +.Op Fl v Ar version +.Op Fl w Ar user : Ns Ar group +.Ar md-device +.Ar mount-point +.Sh DESCRIPTION +The +.Nm +utility is designed to be a work-alike and look-alike of the deprecated +.Xr mount_mfs 8 . +The end result is essentially the same, +but is accomplished in a completely different way. +The +.Nm +utility configures an +.Xr md 4 +disk using +.Xr mdconfig 8 , +puts a UFS file system on it (unless +.Fl P +was specified) using +.Xr newfs 8 , +and mounts it using +.Xr mount 8 . +It can handle +.Xr geom_uzip 4 +compressed disk images, as long as the kernel supports this GEOM class. +All the command line options are passed to the appropriate program +at the appropriate stage in order to achieve the desired effect. +.Pp +By default, +.Nm +creates a swap-based +.Pq Dv MD_SWAP +disk with soft-updates enabled +and mounts it on +.Ar mount-point . +It uses the +.Xr md 4 +device specified by +.Ar md-device . +If +.Ar md-device +is +.Ql md +(no unit number), +it will use +.Xr md 4 Ns 's +auto-unit feature to automatically select an unused device. +Unless otherwise specified with one of the options below, +it uses the default arguments to all the helper programs. +.Pp +The following options are available. +Where possible, +the option letter matches the one used by +.Xr mount_mfs 8 +for the same thing. +.Bl -tag -width indent +.It Fl a Ar maxcontig +Specify the maximum number of contiguous blocks that will be laid +out before forcing a rotational delay +(see the +.Fl d +option). +.It Fl b Ar block-size +The block size of the file system, in bytes. +.It Fl c Ar blocks-per-cylinder-group +The number of blocks per cylinder group in the file system. +.It Fl D +If not using auto-unit, +do not run +.Xr mdconfig 8 +to try to detach the unit before attaching it. +.It Fl d Ar max-extent-size +The file system may choose to store large files using extents. +This parameter specifies the largest extent size that may be +used. +It is presently limited to its default value which is 16 +times the file system blocksize. +.It Fl E Ar path-mdconfig +Use +.Ar path-mdconfig +as a location of the +.Xr mdconfig 8 +utility. +.It Fl e Ar maxbpg +Indicate the maximum number of blocks any single file can allocate +out of a cylinder group before it is forced to begin allocating +blocks from another cylinder group. +.It Fl F Ar file +Create a vnode-backed +.Pq Dv MD_VNODE +memory disk backed by +.Ar file . +.It Fl f Ar frag-size +The fragment size of the file system in bytes. +.It Fl i Ar bytes +Number of bytes per inode. +.It Fl l +Enable multilabel MAC on the new file system. +.It Fl L +Show the output of the helper programs. +By default, +it is sent to +.Pa /dev/null . +.It Fl M +Create a +.Xr malloc 9 +backed disk +.Pq Dv MD_MALLOC +instead of a swap-backed disk. +.It Fl m Ar percent-free +The percentage of space reserved for the superuser. +.It Fl N +Do not actually run the helper programs. +This is most useful in conjunction with +.Fl X . +.It Fl n +Do not create a +.Pa .snap +directory on the new file system. +.It Fl O Ar optimization +Select the optimization preference; +valid choices are +.Cm space +and +.Cm time , +which will optimize for minimum space fragmentation and +minimum time spent allocating blocks, +respectively. +.It Fl o Ar mount-options +Specify the mount options with which to mount the file system. +See +.Xr mount 8 +for more information. +.It Fl P +Preserve the existing file system; +do not run +.Xr newfs 8 . +This only makes sense if +.Fl F +is specified to create a vnode-backed disk. +.It Fl p Ar permissions +Set the file (directory) permissions of the mount point +.Ar mount-point +to +.Ar permissions . +The +.Ar permissions +argument can be in any of the mode formats recognized by +.Xr chmod 1 . +If symbolic permissions are specified, +the operation characters +.Dq + +and +.Dq - +are interpreted relative to the initial permissions of +.Dq a=rwx . +.It Fl S +Do not enable soft-updates on the file system. +.It Fl s Ar size +Specify the size of the disk to create. +This only makes sense if +.Fl F +is +.Em not +specified. +That is, +this will work for the default swap-backed +.Pq Dv MD_SWAP +disks, +and the optional +.Pq Fl M +.Xr malloc 9 +backed disks +.Pq Dv MD_MALLOC . +.It Fl t +Turn on the TRIM enable flag for +.Xr newfs 8 . +When used with a file system that issue BIO_DELETE bio requests, +.Xr md 4 +returns deleted blocks to the system memory pool. +.It Fl T Ar fstype +Specify a file system type for a vnode-backed memory disk. +Any file system supported by +.Xr mount 8 +command can be specified. +This option only makes sense when +.Fl F +and +.Fl P +are used. +.It Fl U +Enable soft-updates on the file system. +This is the default, and is accepted only +for compatibility. +It is only really useful to negate the +.Fl S +flag, should such a need occur. +.It Fl v Ar version +Specify the UFS version number for use on the file system; it may be +either +.Dv 1 +or +.Dv 2 . +The default is derived from the default of the +.Xr newfs 8 +command. +.It Fl w Ar user : Ns Ar group +Set the owner and group to +.Ar user +and +.Ar group , +respectively. +The arguments have the same semantics as with +.Xr chown 8 , +but specifying just a +.Ar user +or just a +.Ar group +is not supported. +.It Fl X +Print what command will be run before running it, and +other assorted debugging information. +.El +.Pp +The +.Fl F +and +.Fl s +options are passed to +.Xr mdconfig 8 +as +.Fl f +and +.Fl s , +respectively. +The +.Fl a , b , c , d , e , f , i , m +and +.Fl n +options are passed to +.Xr newfs 8 +with the same letter. +The +.Fl O +option is passed to +.Xr newfs 8 +as +.Fl o . +The +.Fl o +option is passed to +.Xr mount 8 +with the same letter. +The +.Fl T +option is passed to +.Xr mount 8 +as +.Fl t . +For information on semantics, refer to the documentation of the programs +that the options are passed to. +.Sh EXAMPLES +Create and mount a 32 megabyte swap-backed file system on +.Pa /tmp : +.Pp +.Dl "mdmfs -s 32m md /tmp" +.Pp +The same file system created as an entry in +.Pa /etc/fstab : +.Pp +.Dl "md /tmp mfs rw,-s32m 2 0" +.Pp +Create and mount a 16 megabyte malloc-backed file system on +.Pa /tmp +using the +.Pa /dev/md1 +device; +furthermore, +do not use soft-updates on it and mount it +.Cm async : +.Pp +.Dl "mdmfs -M -S -o async -s 16m md1 /tmp" +.Pp +Create and mount a +.Xr geom_uzip 4 +based compressed disk image: +.Pp +.Dl "mdmfs -P -F foo.uzip -oro md.uzip /tmp/" +.Pp +Mount the same image, specifying the +.Pa /dev/md1 +device: +.Pp +.Dl "mdmfs -P -F foo.uzip -oro md1.uzip /tmp/" +.Pp +Configure a vnode-backed file system and mount its first partition, +using automatic device numbering: +.Pp +.Dl "mdmfs -P -F foo.img mds1a /tmp/" +.Pp +Mount a vnode-backed cd9660 file system using automatic device numbering: +.Pp +.Dl "mdmfs -T cd9660 -P -F foo.iso md /tmp" +.Sh COMPATIBILITY +The +.Nm +utility, while designed to be compatible with +.Xr mount_mfs 8 , +can be useful by itself. +Since +.Xr mount_mfs 8 +had some silly defaults, a +.Dq compatibility +mode is provided for the case where bug-to-bug compatibility is desired. +.Pp +Compatibility is enabled by starting +.Nm +with the name +.Li mount_mfs +or +.Li mfs +(as returned by +.Xr getprogname 3 ) . +In this mode, the following behavior, as done by +.Xr mount_mfs 8 , +is duplicated: +.Bl -bullet -offset indent +.It +The file mode of +.Ar mount-point +is set by default to +.Li 01777 +as if +.Fl p Ar 1777 +was given on the command line. +.El +.Sh SEE ALSO +.Xr md 4 , +.Xr fstab 5 , +.Xr mdconfig 8 , +.Xr mount 8 , +.Xr newfs 8 +.Sh HISTORY +The +.Nm +utility appeared in +.Fx 5.0 . +.Sh AUTHORS +.An Dima Dorfman diff --git a/sbin/mdmfs/mdmfs.c b/sbin/mdmfs/mdmfs.c new file mode 100644 index 000000000000..4b3a012ff50f --- /dev/null +++ b/sbin/mdmfs/mdmfs.c @@ -0,0 +1,699 @@ +/* + * Copyright (c) 2001 Dima Dorfman. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* + * mdmfs (md/MFS) is a wrapper around mdconfig(8), + * newfs(8), and mount(8) that mimics the command line option set of + * the deprecated mount_mfs(8). + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/mdioctl.h> +#include <sys/mount.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <assert.h> +#include <err.h> +#include <fcntl.h> +#include <grp.h> +#include <paths.h> +#include <pwd.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <unistd.h> + +typedef enum { false, true } bool; + +struct mtpt_info { + uid_t mi_uid; + bool mi_have_uid; + gid_t mi_gid; + bool mi_have_gid; + mode_t mi_mode; + bool mi_have_mode; + bool mi_forced_pw; +}; + +static bool debug; /* Emit debugging information? */ +static bool loudsubs; /* Suppress output from helper programs? */ +static bool norun; /* Actually run the helper programs? */ +static int unit; /* The unit we're working with. */ +static const char *mdname; /* Name of memory disk device (e.g., "md"). */ +static const char *mdsuffix; /* Suffix of memory disk device (e.g., ".uzip"). */ +static size_t mdnamelen; /* Length of mdname. */ +static const char *path_mdconfig =_PATH_MDCONFIG; + +static void argappend(char **, const char *, ...) __printflike(2, 3); +static void debugprintf(const char *, ...) __printflike(1, 2); +static void do_mdconfig_attach(const char *, const enum md_types); +static void do_mdconfig_attach_au(const char *, const enum md_types); +static void do_mdconfig_detach(void); +static void do_mount(const char *, const char *); +static void do_mtptsetup(const char *, struct mtpt_info *); +static void do_newfs(const char *); +static void extract_ugid(const char *, struct mtpt_info *); +static int run(int *, const char *, ...) __printflike(2, 3); +static void usage(void); + +int +main(int argc, char **argv) +{ + struct mtpt_info mi; /* Mountpoint info. */ + char *mdconfig_arg, *newfs_arg, /* Args to helper programs. */ + *mount_arg; + enum md_types mdtype; /* The type of our memory disk. */ + bool have_mdtype; + bool detach, softdep, autounit, newfs; + char *mtpoint, *unitstr; + char *p; + int ch; + void *set; + unsigned long ul; + + /* Misc. initialization. */ + (void)memset(&mi, '\0', sizeof(mi)); + detach = true; + softdep = true; + autounit = false; + newfs = true; + have_mdtype = false; + mdtype = MD_SWAP; + mdname = MD_NAME; + mdnamelen = strlen(mdname); + /* + * Can't set these to NULL. They may be passed to the + * respective programs without modification. I.e., we may not + * receive any command-line options which will caused them to + * be modified. + */ + mdconfig_arg = strdup(""); + newfs_arg = strdup(""); + mount_arg = strdup(""); + + /* If we were started as mount_mfs or mfs, imply -C. */ + if (strcmp(getprogname(), "mount_mfs") == 0 || + strcmp(getprogname(), "mfs") == 0) { + /* Make compatibility assumptions. */ + mi.mi_mode = 01777; + mi.mi_have_mode = true; + } + + while ((ch = getopt(argc, argv, + "a:b:Cc:Dd:E:e:F:f:hi:LlMm:NnO:o:Pp:Ss:tT:Uv:w:X")) != -1) + switch (ch) { + case 'a': + argappend(&newfs_arg, "-a %s", optarg); + break; + case 'b': + argappend(&newfs_arg, "-b %s", optarg); + break; + case 'C': + /* Ignored for compatibility. */ + break; + case 'c': + argappend(&newfs_arg, "-c %s", optarg); + break; + case 'D': + detach = false; + break; + case 'd': + argappend(&newfs_arg, "-d %s", optarg); + break; + case 'E': + path_mdconfig = optarg; + break; + case 'e': + argappend(&newfs_arg, "-e %s", optarg); + break; + case 'F': + if (have_mdtype) + usage(); + mdtype = MD_VNODE; + have_mdtype = true; + argappend(&mdconfig_arg, "-f %s", optarg); + break; + case 'f': + argappend(&newfs_arg, "-f %s", optarg); + break; + case 'h': + usage(); + break; + case 'i': + argappend(&newfs_arg, "-i %s", optarg); + break; + case 'L': + loudsubs = true; + break; + case 'l': + argappend(&newfs_arg, "-l"); + break; + case 'M': + if (have_mdtype) + usage(); + mdtype = MD_MALLOC; + have_mdtype = true; + break; + case 'm': + argappend(&newfs_arg, "-m %s", optarg); + break; + case 'N': + norun = true; + break; + case 'n': + argappend(&newfs_arg, "-n"); + break; + case 'O': + argappend(&newfs_arg, "-o %s", optarg); + break; + case 'o': + argappend(&mount_arg, "-o %s", optarg); + break; + case 'P': + newfs = false; + break; + case 'p': + if ((set = setmode(optarg)) == NULL) + usage(); + mi.mi_mode = getmode(set, S_IRWXU | S_IRWXG | S_IRWXO); + mi.mi_have_mode = true; + mi.mi_forced_pw = true; + free(set); + break; + case 'S': + softdep = false; + break; + case 's': + argappend(&mdconfig_arg, "-s %s", optarg); + break; + case 't': + argappend(&newfs_arg, "-t"); + break; + case 'T': + argappend(&mount_arg, "-t %s", optarg); + break; + case 'U': + softdep = true; + break; + case 'v': + argappend(&newfs_arg, "-O %s", optarg); + break; + case 'w': + extract_ugid(optarg, &mi); + mi.mi_forced_pw = true; + break; + case 'X': + debug = true; + break; + default: + usage(); + } + argc -= optind; + argv += optind; + if (argc < 2) + usage(); + + /* Derive 'unit' (global). */ + unitstr = argv[0]; + if (strncmp(unitstr, "/dev/", 5) == 0) + unitstr += 5; + if (strncmp(unitstr, mdname, mdnamelen) == 0) + unitstr += mdnamelen; + if (!isdigit(*unitstr)) { + autounit = true; + unit = -1; + mdsuffix = unitstr; + } else { + ul = strtoul(unitstr, &p, 10); + if (ul == ULONG_MAX) + errx(1, "bad device unit: %s", unitstr); + unit = ul; + mdsuffix = p; /* can be empty */ + } + + mtpoint = argv[1]; + if (!have_mdtype) + mdtype = MD_SWAP; + if (softdep) + argappend(&newfs_arg, "-U"); + if (mdtype != MD_VNODE && !newfs) + errx(1, "-P requires a vnode-backed disk"); + + /* Do the work. */ + if (detach && !autounit) + do_mdconfig_detach(); + if (autounit) + do_mdconfig_attach_au(mdconfig_arg, mdtype); + else + do_mdconfig_attach(mdconfig_arg, mdtype); + if (newfs) + do_newfs(newfs_arg); + do_mount(mount_arg, mtpoint); + do_mtptsetup(mtpoint, &mi); + + return (0); +} + +/* + * Append the expansion of 'fmt' to the buffer pointed to by '*dstp'; + * reallocate as required. + */ +static void +argappend(char **dstp, const char *fmt, ...) +{ + char *old, *new; + va_list ap; + + old = *dstp; + assert(old != NULL); + + va_start(ap, fmt); + if (vasprintf(&new, fmt,ap) == -1) + errx(1, "vasprintf"); + va_end(ap); + + *dstp = new; + if (asprintf(&new, "%s %s", old, new) == -1) + errx(1, "asprintf"); + free(*dstp); + free(old); + + *dstp = new; +} + +/* + * If run-time debugging is enabled, print the expansion of 'fmt'. + * Otherwise, do nothing. + */ +static void +debugprintf(const char *fmt, ...) +{ + va_list ap; + + if (!debug) + return; + fprintf(stderr, "DEBUG: "); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + fprintf(stderr, "\n"); + fflush(stderr); +} + +/* + * Attach a memory disk with a known unit. + */ +static void +do_mdconfig_attach(const char *args, const enum md_types mdtype) +{ + int rv; + const char *ta; /* Type arg. */ + + switch (mdtype) { + case MD_SWAP: + ta = "-t swap"; + break; + case MD_VNODE: + ta = "-t vnode"; + break; + case MD_MALLOC: + ta = "-t malloc"; + break; + default: + abort(); + } + rv = run(NULL, "%s -a %s%s -u %s%d", path_mdconfig, ta, args, + mdname, unit); + if (rv) + errx(1, "mdconfig (attach) exited with error code %d", rv); +} + +/* + * Attach a memory disk with an unknown unit; use autounit. + */ +static void +do_mdconfig_attach_au(const char *args, const enum md_types mdtype) +{ + const char *ta; /* Type arg. */ + char *linep, *linebuf; /* Line pointer, line buffer. */ + int fd; /* Standard output of mdconfig invocation. */ + FILE *sfd; + int rv; + char *p; + size_t linelen; + unsigned long ul; + + switch (mdtype) { + case MD_SWAP: + ta = "-t swap"; + break; + case MD_VNODE: + ta = "-t vnode"; + break; + case MD_MALLOC: + ta = "-t malloc"; + break; + default: + abort(); + } + rv = run(&fd, "%s -a %s%s", path_mdconfig, ta, args); + if (rv) + errx(1, "mdconfig (attach) exited with error code %d", rv); + + /* Receive the unit number. */ + if (norun) { /* Since we didn't run, we can't read. Fake it. */ + unit = 0; + return; + } + sfd = fdopen(fd, "r"); + if (sfd == NULL) + err(1, "fdopen"); + linep = fgetln(sfd, &linelen); + if (linep == NULL && linelen < mdnamelen + 1) + errx(1, "unexpected output from mdconfig (attach)"); + /* If the output format changes, we want to know about it. */ + assert(strncmp(linep, mdname, mdnamelen) == 0); + linebuf = malloc(linelen - mdnamelen + 1); + assert(linebuf != NULL); + /* Can't use strlcpy because linep is not NULL-terminated. */ + strncpy(linebuf, linep + mdnamelen, linelen); + linebuf[linelen] = '\0'; + ul = strtoul(linebuf, &p, 10); + if (ul == ULONG_MAX || *p != '\n') + errx(1, "unexpected output from mdconfig (attach)"); + unit = ul; + + fclose(sfd); + close(fd); +} + +/* + * Detach a memory disk. + */ +static void +do_mdconfig_detach(void) +{ + int rv; + + rv = run(NULL, "%s -d -u %s%d", path_mdconfig, mdname, unit); + if (rv && debug) /* This is allowed to fail. */ + warnx("mdconfig (detach) exited with error code %d (ignored)", + rv); +} + +/* + * Mount the configured memory disk. + */ +static void +do_mount(const char *args, const char *mtpoint) +{ + int rv; + + rv = run(NULL, "%s%s /dev/%s%d%s %s", _PATH_MOUNT, args, + mdname, unit, mdsuffix, mtpoint); + if (rv) + errx(1, "mount exited with error code %d", rv); +} + +/* + * Various configuration of the mountpoint. Mostly, enact 'mip'. + */ +static void +do_mtptsetup(const char *mtpoint, struct mtpt_info *mip) +{ + struct statfs sfs; + + if (!mip->mi_have_mode && !mip->mi_have_uid && !mip->mi_have_gid) + return; + + if (!norun) { + if (statfs(mtpoint, &sfs) == -1) { + warn("statfs: %s", mtpoint); + return; + } + if ((sfs.f_flags & MNT_RDONLY) != 0) { + if (mip->mi_forced_pw) { + warnx( + "Not changing mode/owner of %s since it is read-only", + mtpoint); + } else { + debugprintf( + "Not changing mode/owner of %s since it is read-only", + mtpoint); + } + return; + } + } + + if (mip->mi_have_mode) { + debugprintf("changing mode of %s to %o.", mtpoint, + mip->mi_mode); + if (!norun) + if (chmod(mtpoint, mip->mi_mode) == -1) + err(1, "chmod: %s", mtpoint); + } + /* + * We have to do these separately because the user may have + * only specified one of them. + */ + if (mip->mi_have_uid) { + debugprintf("changing owner (user) or %s to %u.", mtpoint, + mip->mi_uid); + if (!norun) + if (chown(mtpoint, mip->mi_uid, -1) == -1) + err(1, "chown %s to %u (user)", mtpoint, + mip->mi_uid); + } + if (mip->mi_have_gid) { + debugprintf("changing owner (group) or %s to %u.", mtpoint, + mip->mi_gid); + if (!norun) + if (chown(mtpoint, -1, mip->mi_gid) == -1) + err(1, "chown %s to %u (group)", mtpoint, + mip->mi_gid); + } +} + +/* + * Put a file system on the memory disk. + */ +static void +do_newfs(const char *args) +{ + int rv; + + rv = run(NULL, "%s%s /dev/%s%d", _PATH_NEWFS, args, mdname, unit); + if (rv) + errx(1, "newfs exited with error code %d", rv); +} + +/* + * 'str' should be a user and group name similar to the last argument + * to chown(1); i.e., a user, followed by a colon, followed by a + * group. The user and group in 'str' may be either a [ug]id or a + * name. Upon return, the uid and gid fields in 'mip' will contain + * the uid and gid of the user and group name in 'str', respectively. + * + * In other words, this derives a user and group id from a string + * formatted like the last argument to chown(1). + * + * Notice: At this point we don't support only a username or only a + * group name. do_mtptsetup already does, so when this feature is + * desired, this is the only routine that needs to be changed. + */ +static void +extract_ugid(const char *str, struct mtpt_info *mip) +{ + char *ug; /* Writable 'str'. */ + char *user, *group; /* Result of extracton. */ + struct passwd *pw; + struct group *gr; + char *p; + uid_t *uid; + gid_t *gid; + + uid = &mip->mi_uid; + gid = &mip->mi_gid; + mip->mi_have_uid = mip->mi_have_gid = false; + + /* Extract the user and group from 'str'. Format above. */ + ug = strdup(str); + assert(ug != NULL); + group = ug; + user = strsep(&group, ":"); + if (user == NULL || group == NULL || *user == '\0' || *group == '\0') + usage(); + + /* Derive uid. */ + *uid = strtoul(user, &p, 10); + if (*uid == (uid_t)ULONG_MAX) + usage(); + if (*p != '\0') { + pw = getpwnam(user); + if (pw == NULL) + errx(1, "invalid user: %s", user); + *uid = pw->pw_uid; + } + mip->mi_have_uid = true; + + /* Derive gid. */ + *gid = strtoul(group, &p, 10); + if (*gid == (gid_t)ULONG_MAX) + usage(); + if (*p != '\0') { + gr = getgrnam(group); + if (gr == NULL) + errx(1, "invalid group: %s", group); + *gid = gr->gr_gid; + } + mip->mi_have_gid = true; + + free(ug); +} + +/* + * Run a process with command name and arguments pointed to by the + * formatted string 'cmdline'. Since system(3) is not used, the first + * space-delimited token of 'cmdline' must be the full pathname of the + * program to run. The return value is the return code of the process + * spawned. If 'ofd' is non-NULL, it is set to the standard output of + * the program spawned (i.e., you can read from ofd and get the output + * of the program). + */ +static int +run(int *ofd, const char *cmdline, ...) +{ + char **argv, **argvp; /* Result of splitting 'cmd'. */ + int argc; + char *cmd; /* Expansion of 'cmdline'. */ + int pid, status; /* Child info. */ + int pfd[2]; /* Pipe to the child. */ + int nfd; /* Null (/dev/null) file descriptor. */ + bool dup2dn; /* Dup /dev/null to stdout? */ + va_list ap; + char *p; + int rv, i; + + dup2dn = true; + va_start(ap, cmdline); + rv = vasprintf(&cmd, cmdline, ap); + if (rv == -1) + err(1, "vasprintf"); + va_end(ap); + + /* Split up 'cmd' into 'argv' for use with execve. */ + for (argc = 1, p = cmd; (p = strchr(p, ' ')) != NULL; p++) + argc++; /* 'argc' generation loop. */ + argv = (char **)malloc(sizeof(*argv) * (argc + 1)); + assert(argv != NULL); + for (p = cmd, argvp = argv; (*argvp = strsep(&p, " ")) != NULL;) + if (**argvp != '\0') + if (++argvp >= &argv[argc]) { + *argvp = NULL; + break; + } + assert(*argv); + /* The argv array ends up NULL-terminated here. */ + + /* Make sure the above loop works as expected. */ + if (debug) { + /* + * We can't, but should, use debugprintf here. First, + * it appends a trailing newline to the output, and + * second it prepends "DEBUG: " to the output. The + * former is a problem for this would-be first call, + * and the latter for the would-be call inside the + * loop. + */ + (void)fprintf(stderr, "DEBUG: running:"); + /* Should be equivalent to 'cmd' (before strsep, of course). */ + for (i = 0; argv[i] != NULL; i++) + (void)fprintf(stderr, " %s", argv[i]); + (void)fprintf(stderr, "\n"); + } + + /* Create a pipe if necessary and fork the helper program. */ + if (ofd != NULL) { + if (pipe(&pfd[0]) == -1) + err(1, "pipe"); + *ofd = pfd[0]; + dup2dn = false; + } + pid = fork(); + switch (pid) { + case 0: + /* XXX can we call err() in here? */ + if (norun) + _exit(0); + if (ofd != NULL) + if (dup2(pfd[1], STDOUT_FILENO) < 0) + err(1, "dup2"); + if (!loudsubs) { + nfd = open(_PATH_DEVNULL, O_RDWR); + if (nfd == -1) + err(1, "open: %s", _PATH_DEVNULL); + if (dup2(nfd, STDIN_FILENO) < 0) + err(1, "dup2"); + if (dup2dn) + if (dup2(nfd, STDOUT_FILENO) < 0) + err(1, "dup2"); + if (dup2(nfd, STDERR_FILENO) < 0) + err(1, "dup2"); + } + + (void)execv(argv[0], argv); + warn("exec: %s", argv[0]); + _exit(-1); + case -1: + err(1, "fork"); + } + + free(cmd); + free(argv); + while (waitpid(pid, &status, 0) != pid) + ; + return (WEXITSTATUS(status)); +} + +static void +usage(void) +{ + + fprintf(stderr, +"usage: %s [-DLlMNnPStUX] [-a maxcontig] [-b block-size]\n" +"\t[-c blocks-per-cylinder-group][-d max-extent-size] [-E path-mdconfig]\n" +"\t[-e maxbpg] [-F file] [-f frag-size] [-i bytes] [-m percent-free]\n" +"\t[-O optimization] [-o mount-options]\n" +"\t[-p permissions] [-s size] [-v version] [-w user:group]\n" +"\tmd-device mount-point\n", getprogname()); + exit(1); +} |