This patch series allows user-specified mount commands to be sent in via kernel command line ("kinit_mount=...") or via an embedded /etc/fstab file. The first patch is a cleanup of a patch sent last November by San Mehat (http://web.archiveorange.com/archive/v/EazJNBMORV2U7E0coh5h); the next two are small improvements or bug fixes.
Curt Wohlgemuth
2012-Mar-08 23:11 UTC
[klibc] [PATCH 1/3] kinit: Add ability to mount filesystems via /etc/fstab or cmdline
This patch adds the ability to mount filesystems via an embedded fstab or via the kernel command line. When using the kernel command-line, the following format is required: 'kinit_mount=<fs_dev>;<fs_dir>;<fs_type>;<fs_opts>' Multiple mount options can be specified; they are evaluated in the order they appear in the command-line. Mount directories ('fs_dir') sent in will have '/root' prepended to them before the actual mount(2) call is made. Tested: Sent mount commands via kinit_mount= command line; also used an embedded /etc/fstab file. Signed-off-by: San Mehat <san at google.com> Signed-off-by: Curt Wohlgemuth <curtw at google.com> --- usr/kinit/do_mounts.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 files changed, 127 insertions(+), 2 deletions(-) diff --git a/usr/kinit/do_mounts.c b/usr/kinit/do_mounts.c index 3ffac91..9c149fd 100644 --- a/usr/kinit/do_mounts.c +++ b/usr/kinit/do_mounts.c @@ -7,6 +7,7 @@ #include <string.h> #include <unistd.h> #include <inttypes.h> +#include <mntent.h> #include "do_mounts.h" #include "kinit.h" @@ -199,12 +200,120 @@ mount_root(int argc, char *argv[], dev_t root_dev, const char *root_dev_name) return ret; } +/* Allocate a buffer and prepend '/root' onto 'src'. */ +static char *prepend_root_dir(const char *src) +{ + size_t len = strlen(src) + 6; /* "/root" */ + char *p = malloc(len); + + if (!p) + return NULL; + + strcpy(p, "/root"); + strcat(p, src); + return p; +} + +int do_cmdline_mounts(int argc, char *argv[]) +{ + int arg_i; + + for (arg_i = 0; arg_i < argc; arg_i++) { + const char *fs_dev, *fs_dir, *fs_type; + char *fs_opts; + unsigned long flags = 0; + char new_fs_opts[128] = { 0 }; + char *saveptr = NULL; + char *fs_opts_savedptr = NULL; + int opt_first = 1; + const char *opt; + char *new_dir; + + if (strncmp(argv[arg_i], "kinit_mount=", 12)) + continue; + /* + * Format: + * <fs_dev>;<dir>;<fs_type>;[opt1],[optn...] + */ + fs_dev = strtok_r(&argv[arg_i][12], ";", &saveptr); + if (!fs_dev) { + fprintf(stderr, "Failed to parse fs_dev\n"); + continue; + } + fs_dir = strtok_r(NULL, ";", &saveptr); + if (!fs_dir) { + fprintf(stderr, "Failed to parse fs_dir\n"); + continue; + } + fs_type = strtok_r(NULL, ";", &saveptr); + if (!fs_type) { + fprintf(stderr, "Failed to parse fs_type\n"); + continue; + } + fs_opts = strtok_r(NULL, ";", &saveptr); + if (!fs_opts) { + fprintf(stderr, "Failed to parse fs_opts\n"); + continue; + } + + /* If 'fs_opts' specifies 'ro' or 'bind', gobble it up. */ + while ((opt = strtok_r((opt_first ? fs_opts : NULL), + ",", &fs_opts_savedptr))) { + if (!strcmp(opt, "ro")) + flags |= MS_RDONLY; + else if (!strcmp(opt, "bind")) + flags |= MS_BIND; + else { + if (!opt_first) + strcat(new_fs_opts, ","); + strcat(new_fs_opts, opt); + } + if (opt_first) + opt_first = 0; + } + new_dir = prepend_root_dir(fs_dir); + if (! new_dir) + return -ENOMEM; + + if (!mount_block(fs_dev, new_dir, fs_type, + flags, new_fs_opts)) + fprintf(stderr, "Skipping failed mount '%s'\n", fs_dev); + + free(new_dir); + } + return 0; +} + +int do_fstab_mounts(FILE *fp) +{ + struct mntent *ent = NULL; + char *new_dir; + + while ((ent = getmntent(fp))) { + new_dir = prepend_root_dir(ent->mnt_dir); + if (! new_dir) + return -ENOMEM; + if (!mount_block(ent->mnt_fsname, + new_dir, + ent->mnt_type, + 0, + ent->mnt_opts)) { + fprintf(stderr, "Skipping failed mount '%s'\n", + ent->mnt_fsname); + } + free(new_dir); + } + return 0; +} + int do_mounts(int argc, char *argv[]) { const char *root_dev_name = get_arg(argc, argv, "root="); const char *root_delay = get_arg(argc, argv, "rootdelay="); const char *load_ramdisk = get_arg(argc, argv, "load_ramdisk="); dev_t root_dev = 0; + int err; + FILE *fp; dprintf("kinit: do_mounts\n"); @@ -241,6 +350,22 @@ int do_mounts(int argc, char *argv[]) } if (root_dev == Root_MULTI) - return mount_roots(argc, argv, root_dev_name); - return mount_root(argc, argv, root_dev, root_dev_name); + err = mount_roots(argc, argv, root_dev_name); + else + err = mount_root(argc, argv, root_dev, root_dev_name); + + if (err) + return err; + + if ((fp = setmntent("/etc/fstab", "r"))) { + err = do_fstab_mounts(fp); + fclose(fp); + } + + if (err) + return err; + + if (get_arg(argc, argv, "kinit_mount=")) + err = do_cmdline_mounts(argc, argv); + return err; } -- 1.7.7.3
Curt Wohlgemuth
2012-Mar-08 23:11 UTC
[klibc] [PATCH 2/3] kinit: Create block device for mount commands if needed.
For mount commands coming from the "kinit_mount=" option or from an embedded /etc/fstab, try to create the block device before we issue the mount() command. Tested: Tested with a variety of mount commands (cmdline + fstab), using both block devices and 9p mounts. Signed-off-by: Curt Wohlgemuth <curtw at google.com> --- usr/kinit/do_mounts.c | 30 ++++++++++++++++++++++++++++-- 1 files changed, 28 insertions(+), 2 deletions(-) diff --git a/usr/kinit/do_mounts.c b/usr/kinit/do_mounts.c index 9c149fd..d12b07a 100644 --- a/usr/kinit/do_mounts.c +++ b/usr/kinit/do_mounts.c @@ -21,6 +21,24 @@ int create_dev(const char *name, dev_t dev) return mknod(name, S_IFBLK | 0600, dev); } + +/* + * If there is not a block device for the input 'name', try to create one; if + * we can't that's okay. + */ +static void create_dev_if_not_present(const char *name) +{ + struct stat st; + dev_t dev; + + if (stat(name, &st) == 0) /* file present; we're done */ + return; + dev = name_to_dev_t(name); + if (dev) + (void) create_dev(name, dev); +} + + /* mount a filesystem, possibly trying a set of different types */ const char *mount_block(const char *source, const char *target, const char *type, unsigned long flags, @@ -32,9 +50,15 @@ const char *mount_block(const char *source, const char *target, int fd; if (type) { - dprintf("kinit: trying to mount %s on %s with type %s\n", - source, target, type); + dprintf("kinit: trying to mount %s on %s " + "with type %s, flags 0x%lx, data '%s'\n", + source, target, type, flags, (char *)data); int rv = mount(source, target, type, flags, data); + + if (rv != 0) + dprintf("kinit: mount %s on %s failed " + "with errno = %d\n", + source, target, errno); /* Mount readonly if necessary */ if (rv == -1 && errno == EACCES && !(flags & MS_RDONLY)) rv = mount(source, target, type, flags | MS_RDONLY, @@ -274,6 +298,7 @@ int do_cmdline_mounts(int argc, char *argv[]) new_dir = prepend_root_dir(fs_dir); if (! new_dir) return -ENOMEM; + create_dev_if_not_present(fs_dev); if (!mount_block(fs_dev, new_dir, fs_type, flags, new_fs_opts)) @@ -293,6 +318,7 @@ int do_fstab_mounts(FILE *fp) new_dir = prepend_root_dir(ent->mnt_dir); if (! new_dir) return -ENOMEM; + create_dev_if_not_present(ent->mnt_fsname); if (!mount_block(ent->mnt_fsname, new_dir, ent->mnt_type, -- 1.7.7.3
Curt Wohlgemuth
2012-Mar-08 23:11 UTC
[klibc] [PATCH 3/3] kinit: Parse mount command options properly.
Mount options sent via "kinit_mount=" command line args, or an embedded fstab file, were not parsed properly to turn option strings into mount flags where possible. Stole and modified code from usr/utils/mount_opts.c to handle this. Tested: Tested using a variety of command line and fstab mounts and options. Signed-off-by: Curt Wohlgemuth <curtw at google.com> --- usr/kinit/do_mounts.c | 192 +++++++++++++++++++++++++++++++++++++++++------- 1 files changed, 164 insertions(+), 28 deletions(-) diff --git a/usr/kinit/do_mounts.c b/usr/kinit/do_mounts.c index d12b07a..b648299 100644 --- a/usr/kinit/do_mounts.c +++ b/usr/kinit/do_mounts.c @@ -14,6 +14,148 @@ #include "fstype.h" #include "zlib.h" +#ifndef MS_RELATIME +# define MS_RELATIME (1<<21) /* Update atime relative to mtime/ctime. */ +#endif + +#ifndef MS_STRICTATIME +# define MS_STRICTATIME (1<<24) /* Always perform atime updates */ +#endif + +/* + * The following mount option parsing was stolen from + * + * usr/utils/mount_opts.c + * + * and adapted to add some later mount flags. + */ +#define ARRAY_SIZE(x) (sizeof(x) / sizeof(x[0])) + +struct mount_opts { + const char str[16]; + unsigned long rwmask; + unsigned long rwset; + unsigned long rwnoset; +}; + +struct extra_opts { + char *str; + char *end; + int used_size; + int alloc_size; +}; + +/* + * These options define the function of "mount(2)". + */ +#define MS_TYPE (MS_REMOUNT|MS_BIND|MS_MOVE) + + +/* These must be in alphabetic order! */ +static const struct mount_opts options[] = { + /* name mask set noset */ + {"async", MS_SYNCHRONOUS, 0, MS_SYNCHRONOUS}, + {"atime", MS_NOATIME, 0, MS_NOATIME}, + {"bind", MS_TYPE, MS_BIND, 0,}, + {"dev", MS_NODEV, 0, MS_NODEV}, + {"diratime", MS_NODIRATIME, 0, MS_NODIRATIME}, + {"dirsync", MS_DIRSYNC, MS_DIRSYNC, 0}, + {"exec", MS_NOEXEC, 0, MS_NOEXEC}, + {"move", MS_TYPE, MS_MOVE, 0}, + {"nodev", MS_NODEV, MS_NODEV, 0}, + {"noexec", MS_NOEXEC, MS_NOEXEC, 0}, + {"nosuid", MS_NOSUID, MS_NOSUID, 0}, + {"recurse", MS_REC, MS_REC, 0}, + {"relatime", MS_RELATIME, MS_RELATIME, 0}, + {"remount", MS_TYPE, MS_REMOUNT, 0}, + {"ro", MS_RDONLY, MS_RDONLY, 0}, + {"rw", MS_RDONLY, 0, MS_RDONLY}, + {"strictatime", MS_STRICTATIME, MS_STRICTATIME, 0}, + {"suid", MS_NOSUID, 0, MS_NOSUID}, + {"sync", MS_SYNCHRONOUS, MS_SYNCHRONOUS, 0}, + {"verbose", MS_VERBOSE, MS_VERBOSE, 0}, +}; + +/* + * Append 's' to 'extra->str'. 's' is a mount option that can't be turned into + * a flag. Return 0 on success, -1 on error. + */ +static int add_extra_option(struct extra_opts *extra, char *s) +{ + int len = strlen(s); + int newlen = extra->used_size + len; + + if (extra->str) + len++; /* +1 for ',' */ + + if (newlen >= extra->alloc_size) { + char *new; + + new = realloc(extra->str, newlen + 1); /* +1 for NUL */ + if (!new) { + if (extra->str) + free(extra->str); + return -1; + } + + extra->str = new; + extra->end = extra->str + extra->used_size; + extra->alloc_size = newlen; + } + + if (extra->used_size) { + *extra->end = ','; + extra->end++; + } + strcpy(extra->end, s); + extra->used_size += len; + + return 0; +} + +/* + * Parse the options in 'arg'; put numeric mount flags into 'flags' and + * the rest into 'extra'. Return 0 on success, -1 on error. + */ +static int +parse_mount_options(char *arg, unsigned long *flags, struct extra_opts *extra) +{ + char *s; + + while ((s = strsep(&arg, ",")) != NULL) { + char *opt = s; + unsigned int i; + int res; + int no = (s[0] == 'n' && s[1] == 'o'); + int found = 0; + + if (no) + s += 2; + + for (i = 0; i < ARRAY_SIZE(options); i++) { + + res = strcmp(s, options[i].str); + if (res == 0) { + found = 1; + *flags &= ~options[i].rwmask; + if (no) + *flags |= options[i].rwnoset; + else + *flags |= options[i].rwset; + break; + + /* If we're beyond 's' alphabetically, we're done */ + } else if (res < 0) + break; + } + if (! found) + if (add_extra_option(extra, opt) != 0) + return -1; + } + + return 0; +} + /* Create the device node "name" */ int create_dev(const char *name, dev_t dev) { @@ -241,17 +383,15 @@ static char *prepend_root_dir(const char *src) int do_cmdline_mounts(int argc, char *argv[]) { int arg_i; + int ret = 0; for (arg_i = 0; arg_i < argc; arg_i++) { const char *fs_dev, *fs_dir, *fs_type; char *fs_opts; unsigned long flags = 0; - char new_fs_opts[128] = { 0 }; char *saveptr = NULL; - char *fs_opts_savedptr = NULL; - int opt_first = 1; - const char *opt; char *new_dir; + struct extra_opts extra = { 0, 0, 0, 0 }; if (strncmp(argv[arg_i], "kinit_mount=", 12)) continue; @@ -275,59 +415,55 @@ int do_cmdline_mounts(int argc, char *argv[]) continue; } fs_opts = strtok_r(NULL, ";", &saveptr); - if (!fs_opts) { - fprintf(stderr, "Failed to parse fs_opts\n"); - continue; - } + /* Don't error if there is no option string sent */ - /* If 'fs_opts' specifies 'ro' or 'bind', gobble it up. */ - while ((opt = strtok_r((opt_first ? fs_opts : NULL), - ",", &fs_opts_savedptr))) { - if (!strcmp(opt, "ro")) - flags |= MS_RDONLY; - else if (!strcmp(opt, "bind")) - flags |= MS_BIND; - else { - if (!opt_first) - strcat(new_fs_opts, ","); - strcat(new_fs_opts, opt); - } - if (opt_first) - opt_first = 0; - } new_dir = prepend_root_dir(fs_dir); if (! new_dir) return -ENOMEM; create_dev_if_not_present(fs_dev); + ret = parse_mount_options(fs_opts, &flags, &extra); + if (ret != 0) + break; if (!mount_block(fs_dev, new_dir, fs_type, - flags, new_fs_opts)) + flags, extra.str)) fprintf(stderr, "Skipping failed mount '%s'\n", fs_dev); - free(new_dir); + if (extra.str) + free(extra.str); } - return 0; + return ret; } int do_fstab_mounts(FILE *fp) { struct mntent *ent = NULL; char *new_dir; + int ret = 0; while ((ent = getmntent(fp))) { + unsigned long flags = 0; + struct extra_opts extra = { 0, 0, 0, 0 }; + new_dir = prepend_root_dir(ent->mnt_dir); if (! new_dir) return -ENOMEM; create_dev_if_not_present(ent->mnt_fsname); + ret = parse_mount_options(ent->mnt_opts, &flags, &extra); + if (ret != 0) + break; + if (!mount_block(ent->mnt_fsname, new_dir, ent->mnt_type, - 0, - ent->mnt_opts)) { + flags, + extra.str)) { fprintf(stderr, "Skipping failed mount '%s'\n", ent->mnt_fsname); } free(new_dir); + if (extra.str) + free(extra.str); } return 0; } -- 1.7.7.3