diff options
Diffstat (limited to 'init/do_mounts.c')
| -rw-r--r-- | init/do_mounts.c | 676 |
1 files changed, 304 insertions, 372 deletions
diff --git a/init/do_mounts.c b/init/do_mounts.c index 816014c4627e..defbbf1d55f7 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -1,13 +1,4 @@ -/* - * Many of the syscalls used in this file expect some of the arguments - * to be __user pointers not __kernel pointers. To limit the sparse - * noise, turn off sparse checking for this file. - */ -#ifdef __CHECKER__ -#undef __CHECKER__ -#warning "Sparse checking disabled for this file" -#endif - +// SPDX-License-Identifier: GPL-2.0-only #include <linux/module.h> #include <linux/sched.h> #include <linux/ctype.h> @@ -17,7 +8,6 @@ #include <linux/root_dev.h> #include <linux/security.h> #include <linux/delay.h> -#include <linux/genhd.h> #include <linux/mount.h> #include <linux/device.h> #include <linux/init.h> @@ -26,17 +16,19 @@ #include <linux/async.h> #include <linux/fs_struct.h> #include <linux/slab.h> +#include <linux/ramfs.h> +#include <linux/shmem_fs.h> +#include <linux/ktime.h> #include <linux/nfs_fs.h> #include <linux/nfs_fs_sb.h> #include <linux/nfs_mount.h> +#include <linux/raid/detect.h> +#include <uapi/linux/mount.h> #include "do_mounts.h" -int __initdata rd_doload; /* 1 = load RAM disk, 0 = don't load */ - int root_mountflags = MS_RDONLY | MS_SILENT; -static char * __initdata root_device_name; static char __initdata saved_root_name[64]; static int root_wait; @@ -44,7 +36,7 @@ dev_t ROOT_DEV; static int __init load_ramdisk(char *str) { - rd_doload = simple_strtol(str,NULL,0) & 3; + pr_warn("ignoring the deprecated load_ramdisk= option\n"); return 1; } __setup("load_ramdisk=", load_ramdisk); @@ -68,237 +60,48 @@ static int __init readwrite(char *str) __setup("ro", readonly); __setup("rw", readwrite); -#ifdef CONFIG_BLOCK -struct uuidcmp { - const char *uuid; - int len; -}; - -/** - * match_dev_by_uuid - callback for finding a partition using its uuid - * @dev: device passed in by the caller - * @data: opaque pointer to the desired struct uuidcmp to match - * - * Returns 1 if the device matches, and 0 otherwise. - */ -static int match_dev_by_uuid(struct device *dev, const void *data) +static int __init root_dev_setup(char *line) { - const struct uuidcmp *cmp = data; - struct hd_struct *part = dev_to_part(dev); - - if (!part->info) - goto no_match; - - if (strncasecmp(cmp->uuid, part->info->uuid, cmp->len)) - goto no_match; - + strscpy(saved_root_name, line, sizeof(saved_root_name)); return 1; -no_match: - return 0; } +__setup("root=", root_dev_setup); -/** - * devt_from_partuuid - looks up the dev_t of a partition by its UUID - * @uuid: char array containing ascii UUID - * - * The function will return the first partition which contains a matching - * UUID value in its partition_meta_info struct. This does not search - * by filesystem UUIDs. - * - * If @uuid is followed by a "/PARTNROFF=%d", then the number will be - * extracted and used as an offset from the partition identified by the UUID. - * - * Returns the matching dev_t on success or 0 on failure. - */ -static dev_t devt_from_partuuid(const char *uuid_str) +static int __init rootwait_setup(char *str) { - dev_t res = 0; - struct uuidcmp cmp; - struct device *dev = NULL; - struct gendisk *disk; - struct hd_struct *part; - int offset = 0; - bool clear_root_wait = false; - char *slash; - - cmp.uuid = uuid_str; - - slash = strchr(uuid_str, '/'); - /* Check for optional partition number offset attributes. */ - if (slash) { - char c = 0; - /* Explicitly fail on poor PARTUUID syntax. */ - if (sscanf(slash + 1, - "PARTNROFF=%d%c", &offset, &c) != 1) { - clear_root_wait = true; - goto done; - } - cmp.len = slash - uuid_str; - } else { - cmp.len = strlen(uuid_str); - } - - if (!cmp.len) { - clear_root_wait = true; - goto done; - } - - dev = class_find_device(&block_class, NULL, &cmp, - &match_dev_by_uuid); - if (!dev) - goto done; - - res = dev->devt; - - /* Attempt to find the partition by offset. */ - if (!offset) - goto no_offset; - - res = 0; - disk = part_to_disk(dev_to_part(dev)); - part = disk_get_part(disk, dev_to_part(dev)->partno + offset); - if (part) { - res = part_devt(part); - put_device(part_to_dev(part)); - } - -no_offset: - put_device(dev); -done: - if (clear_root_wait) { - pr_err("VFS: PARTUUID= is invalid.\n" - "Expected PARTUUID=<valid-uuid-id>[/PARTNROFF=%%d]\n"); - if (root_wait) - pr_err("Disabling rootwait; root= is invalid.\n"); - root_wait = 0; - } - return res; + if (*str) + return 0; + root_wait = -1; + return 1; } -#endif -/* - * Convert a name into device number. We accept the following variants: - * - * 1) device number in hexadecimal represents itself - * 2) /dev/nfs represents Root_NFS (0xff) - * 3) /dev/<disk_name> represents the device number of disk - * 4) /dev/<disk_name><decimal> represents the device number - * of partition - device number of disk plus the partition number - * 5) /dev/<disk_name>p<decimal> - same as the above, that form is - * used when disk name of partitioned disk ends on a digit. - * 6) PARTUUID=00112233-4455-6677-8899-AABBCCDDEEFF representing the - * unique id of a partition if the partition table provides it. - * The UUID may be either an EFI/GPT UUID, or refer to an MSDOS - * partition using the format SSSSSSSS-PP, where SSSSSSSS is a zero- - * filled hex representation of the 32-bit "NT disk signature", and PP - * is a zero-filled hex representation of the 1-based partition number. - * 7) PARTUUID=<UUID>/PARTNROFF=<int> to select a partition in relation to - * a partition with a known unique id. - * - * If name doesn't have fall into the categories above, we return (0,0). - * block_class is used to check if something is a disk name. If the disk - * name contains slashes, the device name has them replaced with - * bangs. - */ +__setup("rootwait", rootwait_setup); -dev_t name_to_dev_t(char *name) +static int __init rootwait_timeout_setup(char *str) { - char s[32]; - char *p; - dev_t res = 0; - int part; + int sec; -#ifdef CONFIG_BLOCK - if (strncmp(name, "PARTUUID=", 9) == 0) { - name += 9; - res = devt_from_partuuid(name); - if (!res) - goto fail; - goto done; + if (kstrtoint(str, 0, &sec) || sec < 0) { + pr_warn("ignoring invalid rootwait value\n"); + goto ignore; } -#endif - if (strncmp(name, "/dev/", 5) != 0) { - unsigned maj, min; - - if (sscanf(name, "%u:%u", &maj, &min) == 2) { - res = MKDEV(maj, min); - if (maj != MAJOR(res) || min != MINOR(res)) - goto fail; - } else { - res = new_decode_dev(simple_strtoul(name, &p, 16)); - if (*p) - goto fail; - } - goto done; + if (check_mul_overflow(sec, MSEC_PER_SEC, &root_wait)) { + pr_warn("ignoring excessive rootwait value\n"); + goto ignore; } - name += 5; - res = Root_NFS; - if (strcmp(name, "nfs") == 0) - goto done; - res = Root_RAM0; - if (strcmp(name, "ram") == 0) - goto done; - - if (strlen(name) > 31) - goto fail; - strcpy(s, name); - for (p = s; *p; p++) - if (*p == '/') - *p = '!'; - res = blk_lookup_devt(s, 0); - if (res) - goto done; - - /* - * try non-existent, but valid partition, which may only exist - * after revalidating the disk, like partitioned md devices - */ - while (p > s && isdigit(p[-1])) - p--; - if (p == s || !*p || *p == '0') - goto fail; - - /* try disk name without <part number> */ - part = simple_strtoul(p, NULL, 10); - *p = '\0'; - res = blk_lookup_devt(s, part); - if (res) - goto done; - - /* try disk name without p<part number> */ - if (p < s + 2 || !isdigit(p[-2]) || p[-1] != 'p') - goto fail; - p[-1] = '\0'; - res = blk_lookup_devt(s, part); - if (res) - goto done; - -fail: - return 0; -done: - return res; -} - -static int __init root_dev_setup(char *line) -{ - strlcpy(saved_root_name, line, sizeof(saved_root_name)); return 1; -} -__setup("root=", root_dev_setup); +ignore: + /* Fallback to indefinite wait */ + root_wait = -1; -static int __init rootwait_setup(char *str) -{ - if (*str) - return 0; - root_wait = 1; return 1; } -__setup("rootwait", rootwait_setup); +__setup("rootwait=", rootwait_timeout_setup); static char * __initdata root_mount_data; static int __init root_data_setup(char *str) @@ -317,7 +120,8 @@ static int __init fs_names_setup(char *str) static unsigned int __initdata root_delay; static int __init root_delay_setup(char *str) { - root_delay = simple_strtoul(str, NULL, 0); + if (kstrtouint(str, 0, &root_delay)) + return 0; return 1; } @@ -325,74 +129,88 @@ __setup("rootflags=", root_data_setup); __setup("rootfstype=", fs_names_setup); __setup("rootdelay=", root_delay_setup); -static void __init get_fs_names(char *page) +/* This can return zero length strings. Caller should check */ +static int __init split_fs_names(char *page, size_t size) { - char *s = page; - - if (root_fs_names) { - strcpy(page, root_fs_names); - while (*s++) { - if (s[-1] == ',') - s[-1] = '\0'; - } - } else { - int len = get_filesystem_list(page); - char *p, *next; - - page[len] = '\0'; - for (p = page-1; p; p = next) { - next = strchr(++p, '\n'); - if (*p++ != '\t') - continue; - while ((*s++ = *p++) != '\n') - ; - s[-1] = '\0'; + int count = 1; + char *p = page; + + strscpy(p, root_fs_names, size); + while (*p++) { + if (p[-1] == ',') { + p[-1] = '\0'; + count++; } } - *s = '\0'; + + return count; } -static int __init do_mount_root(char *name, char *fs, int flags, void *data) +static int __init do_mount_root(const char *name, const char *fs, + const int flags, const void *data) { struct super_block *s; - int err = sys_mount(name, "/root", fs, flags, data); - if (err) - return err; + struct page *p = NULL; + char *data_page = NULL; + int ret; + + if (data) { + /* init_mount() requires a full page as fifth argument */ + p = alloc_page(GFP_KERNEL); + if (!p) + return -ENOMEM; + data_page = page_address(p); + strscpy_pad(data_page, data, PAGE_SIZE); + } - sys_chdir("/root"); + ret = init_mount(name, "/root", fs, flags, data_page); + if (ret) + goto out; + + init_chdir("/root"); s = current->fs->pwd.dentry->d_sb; ROOT_DEV = s->s_dev; printk(KERN_INFO "VFS: Mounted root (%s filesystem)%s on device %u:%u.\n", s->s_type->name, - s->s_flags & MS_RDONLY ? " readonly" : "", + sb_rdonly(s) ? " readonly" : "", MAJOR(ROOT_DEV), MINOR(ROOT_DEV)); - return 0; + +out: + if (p) + put_page(p); + return ret; } -void __init mount_block_root(char *name, int flags) +void __init mount_root_generic(char *name, char *pretty_name, int flags) { - struct page *page = alloc_page(GFP_KERNEL | - __GFP_NOTRACK_FALSE_POSITIVE); + struct page *page = alloc_page(GFP_KERNEL); char *fs_names = page_address(page); char *p; -#ifdef CONFIG_BLOCK char b[BDEVNAME_SIZE]; -#else - const char *b = name; -#endif - - get_fs_names(fs_names); + int num_fs, i; + + scnprintf(b, BDEVNAME_SIZE, "unknown-block(%u,%u)", + MAJOR(ROOT_DEV), MINOR(ROOT_DEV)); + if (root_fs_names) + num_fs = split_fs_names(fs_names, PAGE_SIZE); + else + num_fs = list_bdev_fs_names(fs_names, PAGE_SIZE); retry: - for (p = fs_names; *p; p += strlen(p)+1) { - int err = do_mount_root(name, p, flags, root_mount_data); + for (i = 0, p = fs_names; i < num_fs; i++, p += strlen(p)+1) { + int err; + + if (!*p) + continue; + err = do_mount_root(name, p, flags, root_mount_data); switch (err) { case 0: goto out; case -EACCES: - flags |= MS_RDONLY; - goto retry; case -EINVAL: +#ifdef CONFIG_BLOCK + init_flush_fput(); +#endif continue; } /* @@ -400,31 +218,36 @@ retry: * and bad superblock on root device. * and give them a list of the available devices */ -#ifdef CONFIG_BLOCK - __bdevname(ROOT_DEV, b); -#endif printk("VFS: Cannot open root device \"%s\" or %s: error %d\n", - root_device_name, b, err); + pretty_name, b, err); printk("Please append a correct \"root=\" boot option; here are the available partitions:\n"); - printk_all_partitions(); -#ifdef CONFIG_DEBUG_BLOCK_EXT_DEVT - printk("DEBUG_BLOCK_EXT_DEVT is enabled, you need to specify " - "explicit textual name for \"root=\" boot option.\n"); -#endif + + if (root_fs_names) + num_fs = list_bdev_fs_names(fs_names, PAGE_SIZE); + if (!num_fs) + pr_err("Can't find any bdev filesystem to be used for mount!\n"); + else { + pr_err("List of all bdev filesystems:\n"); + for (i = 0, p = fs_names; i < num_fs; i++, p += strlen(p)+1) + pr_err(" %s", p); + pr_err("\n"); + } + panic("VFS: Unable to mount root fs on %s", b); } + if (!(flags & SB_RDONLY)) { + flags |= SB_RDONLY; + goto retry; + } printk("List of all partitions:\n"); printk_all_partitions(); printk("No filesystem could mount root, tried: "); - for (p = fs_names; *p; p += strlen(p)+1) + for (i = 0, p = fs_names; i < num_fs; i++, p += strlen(p)+1) printk(" %s", p); printk("\n"); -#ifdef CONFIG_BLOCK - __bdevname(ROOT_DEV, b); -#endif - panic("VFS: Unable to mount root fs on %s", b); + panic("VFS: Unable to mount root fs on \"%s\" or %s", pretty_name, b); out: put_page(page); } @@ -435,15 +258,14 @@ out: #define NFSROOT_TIMEOUT_MAX 30 #define NFSROOT_RETRY_MAX 5 -static int __init mount_nfs_root(void) +static void __init mount_nfs_root(void) { char *root_dev, *root_data; unsigned int timeout; - int try, err; + int try; - err = nfs_root_data(&root_dev, &root_data); - if (err != 0) - return 0; + if (nfs_root_data(&root_dev, &root_data)) + goto fail; /* * The server or network may not be ready, so try several @@ -452,10 +274,8 @@ static int __init mount_nfs_root(void) */ timeout = NFSROOT_TIMEOUT_MIN; for (try = 1; ; try++) { - err = do_mount_root(root_dev, "nfs", - root_mountflags, root_data); - if (err == 0) - return 1; + if (!do_mount_root(root_dev, "nfs", root_mountflags, root_data)) + return; if (try > NFSROOT_RETRY_MAX) break; @@ -465,67 +285,178 @@ static int __init mount_nfs_root(void) if (timeout > NFSROOT_TIMEOUT_MAX) timeout = NFSROOT_TIMEOUT_MAX; } - return 0; +fail: + pr_err("VFS: Unable to mount root fs via NFS.\n"); } -#endif +#else +static inline void mount_nfs_root(void) +{ +} +#endif /* CONFIG_ROOT_NFS */ -#if defined(CONFIG_BLK_DEV_RAM) || defined(CONFIG_BLK_DEV_FD) -void __init change_floppy(char *fmt, ...) +#ifdef CONFIG_CIFS_ROOT + +#define CIFSROOT_TIMEOUT_MIN 5 +#define CIFSROOT_TIMEOUT_MAX 30 +#define CIFSROOT_RETRY_MAX 5 + +static void __init mount_cifs_root(void) { - struct termios termios; - char buf[80]; - char c; - int fd; - va_list args; - va_start(args, fmt); - vsprintf(buf, fmt, args); - va_end(args); - fd = sys_open("/dev/root", O_RDWR | O_NDELAY, 0); - if (fd >= 0) { - sys_ioctl(fd, FDEJECT, 0); - sys_close(fd); - } - printk(KERN_NOTICE "VFS: Insert %s and press ENTER\n", buf); - fd = sys_open("/dev/console", O_RDWR, 0); - if (fd >= 0) { - sys_ioctl(fd, TCGETS, (long)&termios); - termios.c_lflag &= ~ICANON; - sys_ioctl(fd, TCSETSF, (long)&termios); - sys_read(fd, &c, 1); - termios.c_lflag |= ICANON; - sys_ioctl(fd, TCSETSF, (long)&termios); - sys_close(fd); + char *root_dev, *root_data; + unsigned int timeout; + int try; + + if (cifs_root_data(&root_dev, &root_data)) + goto fail; + + timeout = CIFSROOT_TIMEOUT_MIN; + for (try = 1; ; try++) { + if (!do_mount_root(root_dev, "cifs", root_mountflags, + root_data)) + return; + if (try > CIFSROOT_RETRY_MAX) + break; + + ssleep(timeout); + timeout <<= 1; + if (timeout > CIFSROOT_TIMEOUT_MAX) + timeout = CIFSROOT_TIMEOUT_MAX; } +fail: + pr_err("VFS: Unable to mount root fs via SMB.\n"); } -#endif +#else +static inline void mount_cifs_root(void) +{ +} +#endif /* CONFIG_CIFS_ROOT */ -void __init mount_root(void) +static bool __init fs_is_nodev(char *fstype) { -#ifdef CONFIG_ROOT_NFS - if (ROOT_DEV == Root_NFS) { - if (mount_nfs_root()) - return; + struct file_system_type *fs = get_fs_type(fstype); + bool ret = false; - printk(KERN_ERR "VFS: Unable to mount root fs via NFS, trying floppy.\n"); - ROOT_DEV = Root_FD0; + if (fs) { + ret = !(fs->fs_flags & FS_REQUIRES_DEV); + put_filesystem(fs); } -#endif -#ifdef CONFIG_BLK_DEV_FD - if (MAJOR(ROOT_DEV) == FLOPPY_MAJOR) { - /* rd_doload is 2 for a dual initrd/ramload setup */ - if (rd_doload==2) { - if (rd_load_disk(1)) { - ROOT_DEV = Root_RAM1; - root_device_name = NULL; - } - } else - change_floppy("root floppy"); + + return ret; +} + +static int __init mount_nodev_root(char *root_device_name) +{ + char *fs_names, *fstype; + int err = -EINVAL; + int num_fs, i; + + fs_names = (void *)__get_free_page(GFP_KERNEL); + if (!fs_names) + return -EINVAL; + num_fs = split_fs_names(fs_names, PAGE_SIZE); + + for (i = 0, fstype = fs_names; i < num_fs; + i++, fstype += strlen(fstype) + 1) { + if (!*fstype) + continue; + if (!fs_is_nodev(fstype)) + continue; + err = do_mount_root(root_device_name, fstype, root_mountflags, + root_mount_data); + if (!err) + break; } -#endif + + free_page((unsigned long)fs_names); + return err; +} + #ifdef CONFIG_BLOCK - create_dev("/dev/root", ROOT_DEV); - mount_block_root("/dev/root", root_mountflags); -#endif +static void __init mount_block_root(char *root_device_name) +{ + int err = create_dev("/dev/root", ROOT_DEV); + + if (err < 0) + pr_emerg("Failed to create /dev/root: %d\n", err); + mount_root_generic("/dev/root", root_device_name, root_mountflags); +} +#else +static inline void mount_block_root(char *root_device_name) +{ +} +#endif /* CONFIG_BLOCK */ + +void __init mount_root(char *root_device_name) +{ + switch (ROOT_DEV) { + case Root_NFS: + mount_nfs_root(); + break; + case Root_CIFS: + mount_cifs_root(); + break; + case Root_Generic: + mount_root_generic(root_device_name, root_device_name, + root_mountflags); + break; + case 0: + if (root_device_name && root_fs_names && + mount_nodev_root(root_device_name) == 0) + break; + fallthrough; + default: + mount_block_root(root_device_name); + break; + } +} + +/* wait for any asynchronous scanning to complete */ +static void __init wait_for_root(char *root_device_name) +{ + ktime_t end; + + if (ROOT_DEV != 0) + return; + + pr_info("Waiting for root device %s...\n", root_device_name); + + end = ktime_add_ms(ktime_get_raw(), root_wait); + + while (!driver_probe_done() || + early_lookup_bdev(root_device_name, &ROOT_DEV) < 0) { + msleep(5); + if (root_wait > 0 && ktime_after(ktime_get_raw(), end)) + break; + } + + async_synchronize_full(); + +} + +static dev_t __init parse_root_device(char *root_device_name) +{ + int error; + dev_t dev; + + if (!strncmp(root_device_name, "mtd", 3) || + !strncmp(root_device_name, "ubi", 3)) + return Root_Generic; + if (strcmp(root_device_name, "/dev/nfs") == 0) + return Root_NFS; + if (strcmp(root_device_name, "/dev/cifs") == 0) + return Root_CIFS; + if (strcmp(root_device_name, "/dev/ram") == 0) + return Root_RAM0; + + error = early_lookup_bdev(root_device_name, &dev); + if (error) { + if (error == -EINVAL && root_wait) { + pr_err("Disabling rootwait; root= is invalid.\n"); + root_wait = 0; + } + return 0; + } + return dev; } /* @@ -533,8 +464,6 @@ void __init mount_root(void) */ void __init prepare_namespace(void) { - int is_floppy; - if (root_delay) { printk(KERN_INFO "Waiting %d sec before mounting root device...\n", root_delay); @@ -552,39 +481,42 @@ void __init prepare_namespace(void) md_run_setup(); - if (saved_root_name[0]) { - root_device_name = saved_root_name; - if (!strncmp(root_device_name, "mtd", 3) || - !strncmp(root_device_name, "ubi", 3)) { - mount_block_root(root_device_name, root_mountflags); - goto out; - } - ROOT_DEV = name_to_dev_t(root_device_name); - if (strncmp(root_device_name, "/dev/", 5) == 0) - root_device_name += 5; - } + if (saved_root_name[0]) + ROOT_DEV = parse_root_device(saved_root_name); - if (initrd_load()) + if (initrd_load(saved_root_name)) goto out; - /* wait for any asynchronous scanning to complete */ - if ((ROOT_DEV == 0) && root_wait) { - printk(KERN_INFO "Waiting for root device %s...\n", - saved_root_name); - while (driver_probe_done() != 0 || - (ROOT_DEV = name_to_dev_t(saved_root_name)) == 0) - msleep(100); - async_synchronize_full(); - } + if (root_wait) + wait_for_root(saved_root_name); + mount_root(saved_root_name); +out: + devtmpfs_mount(); + init_mount(".", "/", NULL, MS_MOVE, NULL); + init_chroot("."); +} + +static bool is_tmpfs; +static int rootfs_init_fs_context(struct fs_context *fc) +{ + if (IS_ENABLED(CONFIG_TMPFS) && is_tmpfs) + return shmem_init_fs_context(fc); - is_floppy = MAJOR(ROOT_DEV) == FLOPPY_MAJOR; + return ramfs_init_fs_context(fc); +} - if (is_floppy && rd_doload && rd_load_disk(0)) - ROOT_DEV = Root_RAM0; +struct file_system_type rootfs_fs_type = { + .name = "rootfs", + .init_fs_context = rootfs_init_fs_context, + .kill_sb = kill_anon_super, +}; - mount_root(); -out: - devtmpfs_mount("dev"); - sys_mount(".", "/", NULL, MS_MOVE, NULL); - sys_chroot("."); +void __init init_rootfs(void) +{ + if (IS_ENABLED(CONFIG_TMPFS)) { + if (!saved_root_name[0] && !root_fs_names) + is_tmpfs = true; + else if (root_fs_names && !!strstr(root_fs_names, "tmpfs")) + is_tmpfs = true; + } } |
