#include #include #include #include #include #include #include #include #include #include #include "do_mounts.h" #include "kinit.h" #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) { unlink(name); 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, const void *data) { char *fslist, *p, *ep; const char *rp; ssize_t fsbytes; int fd; if (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, data); return rv ? NULL : type; } /* If no type given, try to identify the type first; this also takes care of specific ordering requirements, like ext3 before ext2... */ fd = open(source, O_RDONLY); if (fd >= 0) { int err = identify_fs(fd, &type, NULL, 0); close(fd); if (!err && type) { dprintf("kinit: %s appears to be a %s filesystem\n", source, type); type = mount_block(source, target, type, flags, data); if (type) return type; } } dprintf("kinit: failed to identify filesystem %s, trying all\n", source); fsbytes = readfile("/proc/filesystems", &fslist); errno = EINVAL; if (fsbytes < 0) return NULL; p = fslist; ep = fslist + fsbytes; rp = NULL; while (p < ep) { type = p; p = strchr(p, '\n'); if (!p) break; *p++ = '\0'; /* We can't mount a block device as a "nodev" fs */ if (*type != '\t') continue; type++; rp = mount_block(source, target, type, flags, data); if (rp) break; if (errno != EINVAL) break; } free(fslist); return rp; } /* mount the root filesystem from a block device */ static int mount_block_root(int argc, char *argv[], dev_t root_dev, const char *type, unsigned long flags) { const char *data, *rp; data = get_arg(argc, argv, "rootflags="); create_dev("/dev/root", root_dev); errno = 0; if (type) { if ((rp = mount_block("/dev/root", "/root", type, flags, data))) goto ok; if (errno != EINVAL) goto bad; } if (!errno && (rp = mount_block("/dev/root", "/root", NULL, flags, data))) goto ok; bad: if (errno != EINVAL) { /* * Allow the user to distinguish between failed open * and bad superblock on root device. */ fprintf(stderr, "%s: Cannot open root device %s\n", progname, bdevname(root_dev)); return -errno; } else { fprintf(stderr, "%s: Unable to mount root fs on device %s\n", progname, bdevname(root_dev)); return -ESRCH; } ok: printf("%s: Mounted root (%s filesystem)%s.\n", progname, rp, (flags & MS_RDONLY) ? " readonly" : ""); return 0; } static int mount_roots(int argc, char *argv[], const char *root_dev_name) { char *roots = strdup(root_dev_name); char *root; const char *sep = ","; char *saveptr; int ret = -ESRCH; root = strtok_r(roots, sep, &saveptr); while (root) { dev_t root_dev; dprintf("kinit: trying to mount %s\n", root); root_dev = name_to_dev_t(root); ret = mount_root(argc, argv, root_dev, root); if (!ret) break; root = strtok_r(NULL, sep, &saveptr); } free(roots); return ret; } int mount_root(int argc, char *argv[], dev_t root_dev, const char *root_dev_name) { unsigned long flags = MS_RDONLY | MS_VERBOSE; int ret; const char *type = get_arg(argc, argv, "rootfstype="); if (get_flag(argc, argv, "rw") > get_flag(argc, argv, "ro")) { dprintf("kinit: mounting root rw\n"); flags &= ~MS_RDONLY; } if (type) { if (!strcmp(type, "nfs")) root_dev = Root_NFS; else if (!strcmp(type, "jffs2") && !major(root_dev)) root_dev = Root_MTD; } switch (root_dev) { case Root_NFS: ret = mount_nfs_root(argc, argv, flags); break; case Root_MTD: ret = mount_mtd_root(argc, argv, root_dev_name, type, flags); break; default: ret = mount_block_root(argc, argv, root_dev, type, flags); break; } if (!ret) chdir("/root"); 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; 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 *saveptr = NULL; char *new_dir; struct extra_opts extra = { 0, 0, 0, 0 }; if (strncmp(argv[arg_i], "kinit_mount=", 12)) continue; /* * Format: * ;;;[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); /* Don't error if there is no option string sent */ 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, extra.str)) fprintf(stderr, "Skipping failed mount '%s'\n", fs_dev); free(new_dir); if (extra.str) free(extra.str); } 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, flags, extra.str)) { fprintf(stderr, "Skipping failed mount '%s'\n", ent->mnt_fsname); } free(new_dir); if (extra.str) free(extra.str); } 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"); if (root_delay) { int delay = atoi(root_delay); fprintf(stderr, "Waiting %d s before mounting root device...\n", delay); sleep(delay); } md_run(argc, argv); if (root_dev_name) { root_dev = name_to_dev_t(root_dev_name); } else if (get_arg(argc, argv, "nfsroot=") || get_arg(argc, argv, "nfsaddrs=")) { root_dev = Root_NFS; } else { long rootdev; getintfile("/proc/sys/kernel/real-root-dev", &rootdev); root_dev = (dev_t) rootdev; } dprintf("kinit: root_dev = %s\n", bdevname(root_dev)); if (initrd_load(argc, argv, root_dev)) { dprintf("initrd loaded\n"); return 0; } if (load_ramdisk && atoi(load_ramdisk)) { if (ramdisk_load(argc, argv)) root_dev = Root_RAM0; } if (root_dev == Root_MULTI) 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; }