Revision 3: - if (template) -> if (template[0] != '\0') (From Revision 2):
1. Added code to allow updating MNT_RDONLY status with M. If MNT_WANTRDWR is specified as well, MNT_RDONLY is unset. 2. Added check for strlcpy() call. 3. If getenv() is deemed "unsafe" to call, just use the default "/tmp" path. If a warning should be shown here, let me know and I will change this to include one. Index: sbin/mount_tmpfs/mount_tmpfs.c =================================================================== RCS file: /cvs/src/sbin/mount_tmpfs/mount_tmpfs.c,v retrieving revision 1.4 diff -u -p -r1.4 mount_tmpfs.c --- sbin/mount_tmpfs/mount_tmpfs.c 21 Jan 2014 21:58:27 -0000 1.4 +++ sbin/mount_tmpfs/mount_tmpfs.c 19 Sep 2014 11:58:34 -0000 @@ -39,6 +39,7 @@ __RCSID("$NetBSD: mount_tmpfs.c,v 1.24 2 #include <sys/param.h> #include <sys/mount.h> #include <sys/stat.h> +#include <sys/wait.h> #include <ctype.h> #include <err.h> @@ -51,6 +52,9 @@ __RCSID("$NetBSD: mount_tmpfs.c,v 1.24 2 #include <string.h> #include <unistd.h> #include <util.h> +#include <paths.h> + +#include "pathnames.h" #include "mount_tmpfs.h" @@ -74,6 +78,13 @@ static void pathadj(const char *, char * /* --------------------------------------------------------------------- */ +static int do_exec(const char *, const char *, char *const[]); +static int isdir(const char *); +static void tcopy(char *, char *, struct tmpfs_args *); +static int gettmpmnt(char *, size_t); + +/* --------------------------------------------------------------------- */ + void mount_tmpfs_parseargs(int argc, char *argv[], struct tmpfs_args *args, int *mntflags, @@ -176,13 +187,33 @@ mount_tmpfs(int argc, char *argv[]) { struct tmpfs_args args; char canon_dev[MAXPATHLEN], canon_dir[MAXPATHLEN]; - int mntflags; + char template[MAXPATHLEN] = {'\0'}; + int mntflags, rdonly; mount_tmpfs_parseargs(argc, argv, &args, &mntflags, canon_dev, canon_dir); + rdonly = mntflags & MNT_RDONLY; + + if(template[0] != '\0') + mntflags &= ~MNT_RDONLY; + if (mount(MOUNT_TMPFS, canon_dir, mntflags, &args) == -1) err(EXIT_FAILURE, "tmpfs on %s", canon_dir); + + if(template[0] != '\0') { + tcopy(template, canon_dir, &args); + + if (rdonly) { + mntflags |= MNT_RDONLY | MNT_UPDATE; + if (mount(MOUNT_TMPFS, canon_dir, mntflags, &args) < 0) { + warn("%s: mount (update, rdonly)", canon_dir); + if (unmount(canon_dir, 0) != 0) + warn("unmount %s", canon_dir); + exit(1); + } + } + } return EXIT_SUCCESS; } @@ -249,4 +280,139 @@ pathadj(const char *input, char *adjuste warnx("\"%s\" is a non-resolved or relative path.", input); warnx("using \"%s\" instead.", adjusted); } +} + +/* Code copied from sbin/newfs/newfs.c to copy a template. */ + +static int +do_exec(const char *dir, const char *cmd, char *const argv[]) +{ + pid_t pid; + int ret, status; + sig_t intsave, quitsave; + + switch (pid = fork()) { + case -1: + err(1, "fork"); + case 0: + if (dir != NULL && chdir(dir) != 0) + err(1, "chdir"); + if (execv(cmd, argv) != 0) + err(1, "%s", cmd); + break; + default: + intsave = signal(SIGINT, SIG_IGN); + quitsave = signal(SIGQUIT, SIG_IGN); + for (;;) { + ret = waitpid(pid, &status, 0); + if (ret == -1) + err(11, "waitpid"); + if (WIFEXITED(status)) { + status = WEXITSTATUS(status); + if (status != 0) + warnx("%s: exited", cmd); + break; + } else if (WIFSIGNALED(status)) { + warnx("%s: %s", cmd, + strsignal(WTERMSIG(status))); + status = 1; + break; + } + } + signal(SIGINT, intsave); + signal(SIGQUIT, quitsave); + return (status); + } + /* NOTREACHED */ + return (-1); +} + +static int +isdir(const char *path) +{ + struct stat st; + + if (stat(path, &st) != 0) + err(1, "cannot stat %s", path); + if (!S_ISDIR(st.st_mode) && !S_ISBLK(st.st_mode)) + errx(1, "%s: not a dir or a block device", path); + return (S_ISDIR(st.st_mode)); +} + +static void +tcopy(char *src, char *dst, struct tmpfs_args *args) +{ + int ret, dir, created = 0; + struct ufs_args mount_args; + char mountpoint[MNAMELEN]; + char *const argv[] = { "pax", "-rw", "-pe", ".", dst, NULL } ; + + dir = isdir(src); + if (dir) { + size_t sret; + sret = strlcpy(mountpoint, src, sizeof(mountpoint)); + if(sret >= sizeof(mountpoint)) + errx(1, "template path %s too long", src); + } + else { + created = gettmpmnt(mountpoint, sizeof(mountpoint)); + memset(&mount_args, 0, sizeof(mount_args)); + mount_args.fspec = src; + ret = mount(MOUNT_FFS, mountpoint, MNT_RDONLY, &mount_args); + if (ret != 0) { + if (created && rmdir(mountpoint) != 0) + warn("rmdir %s", mountpoint); + if (unmount(dst, 0) != 0) + warn("unmount %s", dst); + err(1, "mount %s %s", src, mountpoint); + } + } + ret = do_exec(mountpoint, "/bin/pax", argv); + if (!dir && unmount(mountpoint, 0) != 0) + warn("unmount %s", mountpoint); + if (created && rmdir(mountpoint) != 0) + warn("rmdir %s", mountpoint); + if (ret != 0) { + if (unmount(dst, 0) != 0) + warn("unmount %s", dst); + errx(1, "copy %s to %s failed", mountpoint, dst); + } +} + +static int +gettmpmnt(char *mountpoint, size_t len) +{ + const char *tmp = NULL; + const char *mnt = _PATH_MNT; + struct statfs fs; + size_t n; + + if (!issetugid()) + tmp = getenv("TMPDIR"); + + if (tmp == NULL || *tmp == '\0') + tmp = _PATH_TMP; + + if (statfs(tmp, &fs) != 0) + err(1, "statfs %s", tmp); + if (fs.f_flags & MNT_RDONLY) { + if (statfs(mnt, &fs) != 0) + err(1, "statfs %s", mnt); + if (strcmp(fs.f_mntonname, "/") != 0) + errx(1, "tmp mountpoint %s busy", mnt); + if (strlcpy(mountpoint, mnt, len) >= len) + errx(1, "tmp mountpoint %s too long", mnt); + return (0); + } + n = strlcpy(mountpoint, tmp, len); + if (n >= len) + errx(1, "tmp mount point too long"); + if (mountpoint[n - 1] != '/') + strlcat(mountpoint, "/", len); + n = strlcat(mountpoint, "mntXXXXXXXXXX", len); + if (n >= len) + errx(1, "tmp mount point too long"); + if (mkdtemp(mountpoint) == NULL) + err(1, "mkdtemp %s", mountpoint); + return (1); } Index: sys/tmpfs/tmpfs_vfsops.c =================================================================== RCS file: /cvs/src/sys/tmpfs/tmpfs_vfsops.c,v retrieving revision 1.4 diff -u -p -r1.4 tmpfs_vfsops.c --- sys/tmpfs/tmpfs_vfsops.c 12 Jul 2014 18:50:25 -0000 1.4 +++ sys/tmpfs/tmpfs_vfsops.c 19 Sep 2014 11:58:35 -0000 @@ -118,7 +118,21 @@ tmpfs_mount(struct mount *mp, const char } #endif + /* + * If updating, check whether changing from read-only to + * read/write; if there is no device name, that's all we do. + */ if (mp->mnt_flag & MNT_UPDATE) { + + /* Update if changing the read-only flag. */ + if(mp->mnt_flag & MNT_RDONLY) + return 0; + + if(mp->mnt_flag & MNT_WANTRDWR) { + mp->mnt_flag &= ~(MNT_WANTRDWR | MNT_RDONLY); + return 0; + } + /* TODO */ return EOPNOTSUPP; } Index: pathnames.h =================================================================== RCS file: pathnames.h diff -N pathnames.h --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ pathnames.h 18 Sep 2014 08:22:42 -0000 @@ -0,0 +1,33 @@ /* $OpenBSD: pathnames.h,v 1.2 2004/07/02 15:48:36 otto Exp $ */ /* $NetBSD: pathnames.h,v 1.1 1996/09/11 20:27:15 christos Exp $ */ + +/* + * Copyright (c) 1989, 1993, 1994 + * The Regents of the University of California. 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. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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. + */ + +#define _PATH_MNT "/mnt"