From aa5f6ed8c21ec1aa5fd688118d8d5cd87c5ffc1d Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 31 May 2023 14:55:12 +0200 Subject: driver core: return bool from driver_probe_done bool is the most sensible return value for a yes/no return. Also add __init as this funtion is only called from the early boot code. Signed-off-by: Christoph Hellwig Acked-by: Greg Kroah-Hartman Link: https://lore.kernel.org/r/20230531125535.676098-2-hch@lst.de Signed-off-by: Jens Axboe --- init/do_mounts.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'init') diff --git a/init/do_mounts.c b/init/do_mounts.c index 811e94daf0a8..2fe7901b5bcf 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -635,7 +635,7 @@ void __init prepare_namespace(void) if ((ROOT_DEV == 0) && root_wait) { printk(KERN_INFO "Waiting for root device %s...\n", saved_root_name); - while (driver_probe_done() != 0 || + while (!driver_probe_done() || (ROOT_DEV = name_to_dev_t(saved_root_name)) == 0) msleep(5); async_synchronize_full(); -- cgit v1.2.3 From e3102722ffe77094ba9e7e46380792b3dd8a7abd Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 31 May 2023 14:55:17 +0200 Subject: init: rename mount_block_root to mount_root_generic mount_block_root is also used to mount non-block file systems, so give it a better name. Signed-off-by: Christoph Hellwig Link: https://lore.kernel.org/r/20230531125535.676098-7-hch@lst.de Signed-off-by: Jens Axboe --- init/do_mounts.c | 6 +++--- init/do_mounts.h | 2 +- init/do_mounts_initrd.c | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'init') diff --git a/init/do_mounts.c b/init/do_mounts.c index 2fe7901b5bcf..a2c0baace099 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -391,7 +391,7 @@ out: return ret; } -void __init mount_block_root(char *name, int flags) +void __init mount_root_generic(char *name, int flags) { struct page *page = alloc_page(GFP_KERNEL); char *fs_names = page_address(page); @@ -589,7 +589,7 @@ void __init mount_root(void) if (err < 0) pr_emerg("Failed to create /dev/root: %d\n", err); - mount_block_root("/dev/root", root_mountflags); + mount_root_generic("/dev/root", root_mountflags); } #endif } @@ -620,7 +620,7 @@ void __init prepare_namespace(void) 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); + mount_root_generic(root_device_name, root_mountflags); goto out; } ROOT_DEV = name_to_dev_t(root_device_name); diff --git a/init/do_mounts.h b/init/do_mounts.h index 7a29ac3e427b..33623025f695 100644 --- a/init/do_mounts.h +++ b/init/do_mounts.h @@ -10,7 +10,7 @@ #include #include -void mount_block_root(char *name, int flags); +void mount_root_generic(char *name, int flags); void mount_root(void); extern int root_mountflags; diff --git a/init/do_mounts_initrd.c b/init/do_mounts_initrd.c index 34731241377d..686d1ff3af4b 100644 --- a/init/do_mounts_initrd.c +++ b/init/do_mounts_initrd.c @@ -95,7 +95,7 @@ static void __init handle_initrd(void) real_root_dev = new_encode_dev(ROOT_DEV); create_dev("/dev/root.old", Root_RAM0); /* mount initrd on rootfs' /root */ - mount_block_root("/dev/root.old", root_mountflags & ~MS_RDONLY); + mount_root_generic("/dev/root.old", root_mountflags & ~MS_RDONLY); init_mkdir("/old", 0700); init_chdir("/old"); -- cgit v1.2.3 From a6a41d39c2d91ff2543d31b6cc6070f3957e3aea Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 31 May 2023 14:55:18 +0200 Subject: init: refactor mount_root Provide stubs for all the lower level mount helpers, and just switch on ROOT_DEV in the main function. Signed-off-by: Christoph Hellwig Link: https://lore.kernel.org/r/20230531125535.676098-8-hch@lst.de Signed-off-by: Jens Axboe --- init/do_mounts.c | 104 ++++++++++++++++++++++++++++++------------------------- 1 file changed, 56 insertions(+), 48 deletions(-) (limited to 'init') diff --git a/init/do_mounts.c b/init/do_mounts.c index a2c0baace099..e708b02d9d65 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -453,15 +453,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 @@ -470,10 +469,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; @@ -483,9 +480,14 @@ 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 */ #ifdef CONFIG_CIFS_ROOT @@ -495,22 +497,20 @@ extern int cifs_root_data(char **dev, char **opts); #define CIFSROOT_TIMEOUT_MAX 30 #define CIFSROOT_RETRY_MAX 5 -static int __init mount_cifs_root(void) +static void __init mount_cifs_root(void) { char *root_dev, *root_data; unsigned int timeout; - int try, err; + int try; - err = cifs_root_data(&root_dev, &root_data); - if (err != 0) - return 0; + if (cifs_root_data(&root_dev, &root_data)) + goto fail; timeout = CIFSROOT_TIMEOUT_MIN; for (try = 1; ; try++) { - err = do_mount_root(root_dev, "cifs", root_mountflags, - root_data); - if (err == 0) - return 1; + if (!do_mount_root(root_dev, "cifs", root_mountflags, + root_data)) + return; if (try > CIFSROOT_RETRY_MAX) break; @@ -519,9 +519,14 @@ static int __init mount_cifs_root(void) if (timeout > CIFSROOT_TIMEOUT_MAX) timeout = CIFSROOT_TIMEOUT_MAX; } - return 0; +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 */ static bool __init fs_is_nodev(char *fstype) { @@ -563,35 +568,38 @@ static int __init mount_nodev_root(void) return err; } -void __init mount_root(void) -{ -#ifdef CONFIG_ROOT_NFS - if (ROOT_DEV == Root_NFS) { - if (!mount_nfs_root()) - printk(KERN_ERR "VFS: Unable to mount root fs via NFS.\n"); - return; - } -#endif -#ifdef CONFIG_CIFS_ROOT - if (ROOT_DEV == Root_CIFS) { - if (!mount_cifs_root()) - printk(KERN_ERR "VFS: Unable to mount root fs via SMB.\n"); - return; - } -#endif - if (ROOT_DEV == 0 && root_device_name && root_fs_names) { - if (mount_nodev_root() == 0) - return; - } #ifdef CONFIG_BLOCK - { - int err = create_dev("/dev/root", ROOT_DEV); +static void __init mount_block_root(void) +{ + 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_mountflags); +} +#else +static inline void mount_block_root(void) +{ +} +#endif /* CONFIG_BLOCK */ - if (err < 0) - pr_emerg("Failed to create /dev/root: %d\n", err); - mount_root_generic("/dev/root", root_mountflags); +void __init mount_root(void) +{ + switch (ROOT_DEV) { + case Root_NFS: + mount_nfs_root(); + break; + case Root_CIFS: + mount_cifs_root(); + break; + case 0: + if (root_device_name && root_fs_names && mount_nodev_root() == 0) + break; + fallthrough; + default: + mount_block_root(); + break; } -#endif } /* -- cgit v1.2.3 From c8643c72bc42781fc169c6498a3902bec447099e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 31 May 2023 14:55:19 +0200 Subject: init: pass root_device_name explicitly Instead of declaring root_device_name as a global variable pass it as an argument to the functions using it. Signed-off-by: Christoph Hellwig Link: https://lore.kernel.org/r/20230531125535.676098-9-hch@lst.de Signed-off-by: Jens Axboe --- init/do_mounts.c | 29 ++++++++++++++++------------- init/do_mounts.h | 14 +++++++------- init/do_mounts_initrd.c | 11 ++++++----- 3 files changed, 29 insertions(+), 25 deletions(-) (limited to 'init') diff --git a/init/do_mounts.c b/init/do_mounts.c index e708b02d9d65..1405ee7218bf 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -28,7 +28,6 @@ #include "do_mounts.h" int root_mountflags = MS_RDONLY | MS_SILENT; -static char * __initdata root_device_name; static char __initdata saved_root_name[64]; static int root_wait; @@ -391,7 +390,7 @@ out: return ret; } -void __init mount_root_generic(char *name, int flags) +void __init mount_root_generic(char *name, char *pretty_name, int flags) { struct page *page = alloc_page(GFP_KERNEL); char *fs_names = page_address(page); @@ -425,7 +424,7 @@ retry: * and give them a list of the available devices */ 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(); @@ -541,7 +540,7 @@ static bool __init fs_is_nodev(char *fstype) return ret; } -static int __init mount_nodev_root(void) +static int __init mount_nodev_root(char *root_device_name) { char *fs_names, *fstype; int err = -EINVAL; @@ -569,21 +568,21 @@ static int __init mount_nodev_root(void) } #ifdef CONFIG_BLOCK -static void __init mount_block_root(void) +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_mountflags); + mount_root_generic("/dev/root", root_device_name, root_mountflags); } #else -static inline void mount_block_root(void) +static inline void mount_block_root(char *root_device_name) { } #endif /* CONFIG_BLOCK */ -void __init mount_root(void) +void __init mount_root(char *root_device_name) { switch (ROOT_DEV) { case Root_NFS: @@ -593,11 +592,12 @@ void __init mount_root(void) mount_cifs_root(); break; case 0: - if (root_device_name && root_fs_names && mount_nodev_root() == 0) + if (root_device_name && root_fs_names && + mount_nodev_root(root_device_name) == 0) break; fallthrough; default: - mount_block_root(); + mount_block_root(root_device_name); break; } } @@ -607,6 +607,8 @@ void __init mount_root(void) */ void __init prepare_namespace(void) { + char *root_device_name; + if (root_delay) { printk(KERN_INFO "Waiting %d sec before mounting root device...\n", root_delay); @@ -628,7 +630,8 @@ void __init prepare_namespace(void) root_device_name = saved_root_name; if (!strncmp(root_device_name, "mtd", 3) || !strncmp(root_device_name, "ubi", 3)) { - mount_root_generic(root_device_name, root_mountflags); + mount_root_generic(root_device_name, root_device_name, + root_mountflags); goto out; } ROOT_DEV = name_to_dev_t(root_device_name); @@ -636,7 +639,7 @@ void __init prepare_namespace(void) root_device_name += 5; } - if (initrd_load()) + if (initrd_load(root_device_name)) goto out; /* wait for any asynchronous scanning to complete */ @@ -649,7 +652,7 @@ void __init prepare_namespace(void) async_synchronize_full(); } - mount_root(); + mount_root(root_device_name); out: devtmpfs_mount(); init_mount(".", "/", NULL, MS_MOVE, NULL); diff --git a/init/do_mounts.h b/init/do_mounts.h index 33623025f695..15e372b00ce7 100644 --- a/init/do_mounts.h +++ b/init/do_mounts.h @@ -10,8 +10,8 @@ #include #include -void mount_root_generic(char *name, int flags); -void mount_root(void); +void mount_root_generic(char *name, char *pretty_name, int flags); +void mount_root(char *root_device_name); extern int root_mountflags; static inline __init int create_dev(char *name, dev_t dev) @@ -33,11 +33,11 @@ static inline int rd_load_image(char *from) { return 0; } #endif #ifdef CONFIG_BLK_DEV_INITRD - -bool __init initrd_load(void); - +bool __init initrd_load(char *root_device_name); #else - -static inline bool initrd_load(void) { return false; } +static inline bool initrd_load(char *root_device_name) +{ + return false; + } #endif diff --git a/init/do_mounts_initrd.c b/init/do_mounts_initrd.c index 686d1ff3af4b..425f4bcf4b77 100644 --- a/init/do_mounts_initrd.c +++ b/init/do_mounts_initrd.c @@ -83,7 +83,7 @@ static int __init init_linuxrc(struct subprocess_info *info, struct cred *new) return 0; } -static void __init handle_initrd(void) +static void __init handle_initrd(char *root_device_name) { struct subprocess_info *info; static char *argv[] = { "linuxrc", NULL, }; @@ -95,7 +95,8 @@ static void __init handle_initrd(void) real_root_dev = new_encode_dev(ROOT_DEV); create_dev("/dev/root.old", Root_RAM0); /* mount initrd on rootfs' /root */ - mount_root_generic("/dev/root.old", root_mountflags & ~MS_RDONLY); + mount_root_generic("/dev/root.old", root_device_name, + root_mountflags & ~MS_RDONLY); init_mkdir("/old", 0700); init_chdir("/old"); @@ -117,7 +118,7 @@ static void __init handle_initrd(void) init_chdir("/"); ROOT_DEV = new_decode_dev(real_root_dev); - mount_root(); + mount_root(root_device_name); printk(KERN_NOTICE "Trying to move old root to /initrd ... "); error = init_mount("/old", "/root/initrd", NULL, MS_MOVE, NULL); @@ -133,7 +134,7 @@ static void __init handle_initrd(void) } } -bool __init initrd_load(void) +bool __init initrd_load(char *root_device_name) { if (mount_initrd) { create_dev("/dev/ram", Root_RAM0); @@ -145,7 +146,7 @@ bool __init initrd_load(void) */ if (rd_load_image("/initrd.image") && ROOT_DEV != Root_RAM0) { init_unlink("/initrd.image"); - handle_initrd(); + handle_initrd(root_device_name); return true; } } -- cgit v1.2.3 From 73231b58b1b496d631fa0ecf9fa7f64f5a07c6e3 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 31 May 2023 14:55:20 +0200 Subject: init: don't remove the /dev/ prefix from error messages Remove the code that drops the /dev/ prefix from root_device_name, which is only used for error messages when mounting the root device fails. Signed-off-by: Christoph Hellwig Link: https://lore.kernel.org/r/20230531125535.676098-10-hch@lst.de Signed-off-by: Jens Axboe --- init/do_mounts.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) (limited to 'init') diff --git a/init/do_mounts.c b/init/do_mounts.c index 1405ee7218bf..74cc96bffbdd 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -607,8 +607,6 @@ void __init mount_root(char *root_device_name) */ void __init prepare_namespace(void) { - char *root_device_name; - if (root_delay) { printk(KERN_INFO "Waiting %d sec before mounting root device...\n", root_delay); @@ -627,19 +625,16 @@ 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_root_generic(root_device_name, root_device_name, + if (!strncmp(saved_root_name, "mtd", 3) || + !strncmp(saved_root_name, "ubi", 3)) { + mount_root_generic(saved_root_name, saved_root_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; + ROOT_DEV = name_to_dev_t(saved_root_name); } - if (initrd_load(root_device_name)) + if (initrd_load(saved_root_name)) goto out; /* wait for any asynchronous scanning to complete */ @@ -652,7 +647,7 @@ void __init prepare_namespace(void) async_synchronize_full(); } - mount_root(root_device_name); + mount_root(saved_root_name); out: devtmpfs_mount(); init_mount(".", "/", NULL, MS_MOVE, NULL); -- cgit v1.2.3 From 07d63cbb67cdb5e2a7720fdd8579b3be979c2d66 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 31 May 2023 14:55:21 +0200 Subject: init: handle ubi/mtd root mounting like all other root types Assign a Root_Generic magic value for UBI/MTD root and handle the root mounting in mount_root like all other root types. Besides making the code more clear this also means that UBI/MTD root can be used together with an initrd (not that anyone should care). Also factor parsing of the root name into a helper now that it can be easily done and will get more complicated with subsequent patches. Signed-off-by: Christoph Hellwig Link: https://lore.kernel.org/r/20230531125535.676098-11-hch@lst.de Signed-off-by: Jens Axboe --- include/linux/root_dev.h | 1 + init/do_mounts.c | 23 ++++++++++++++--------- 2 files changed, 15 insertions(+), 9 deletions(-) (limited to 'init') diff --git a/include/linux/root_dev.h b/include/linux/root_dev.h index ed3ea8da6429..847c9a06101b 100644 --- a/include/linux/root_dev.h +++ b/include/linux/root_dev.h @@ -9,6 +9,7 @@ enum { Root_NFS = MKDEV(UNNAMED_MAJOR, 255), Root_CIFS = MKDEV(UNNAMED_MAJOR, 254), + Root_Generic = MKDEV(UNNAMED_MAJOR, 253), Root_RAM0 = MKDEV(RAMDISK_MAJOR, 0), }; diff --git a/init/do_mounts.c b/init/do_mounts.c index 74cc96bffbdd..be6d14733ba0 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -591,6 +591,10 @@ void __init mount_root(char *root_device_name) 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) @@ -602,6 +606,14 @@ void __init mount_root(char *root_device_name) } } +static dev_t __init parse_root_device(char *root_device_name) +{ + if (!strncmp(root_device_name, "mtd", 3) || + !strncmp(root_device_name, "ubi", 3)) + return Root_Generic; + return name_to_dev_t(root_device_name); +} + /* * Prepare the namespace - decide what/where to mount, load ramdisks, etc. */ @@ -624,15 +636,8 @@ void __init prepare_namespace(void) md_run_setup(); - if (saved_root_name[0]) { - if (!strncmp(saved_root_name, "mtd", 3) || - !strncmp(saved_root_name, "ubi", 3)) { - mount_root_generic(saved_root_name, saved_root_name, - root_mountflags); - goto out; - } - ROOT_DEV = name_to_dev_t(saved_root_name); - } + if (saved_root_name[0]) + ROOT_DEV = parse_root_device(saved_root_name); if (initrd_load(saved_root_name)) goto out; -- cgit v1.2.3 From 3701c600a3e735b9fbac6f7a73e4c086090c97ca Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 31 May 2023 14:55:22 +0200 Subject: init: factor the root_wait logic in prepare_namespace into a helper The root_wait logic is a bit obsfucated right now. Expand it and move it into a helper. Signed-off-by: Christoph Hellwig Link: https://lore.kernel.org/r/20230531125535.676098-12-hch@lst.de Signed-off-by: Jens Axboe --- init/do_mounts.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) (limited to 'init') diff --git a/init/do_mounts.c b/init/do_mounts.c index be6d14733ba0..d5c06c1546e8 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -606,6 +606,26 @@ void __init mount_root(char *root_device_name) } } +/* wait for any asynchronous scanning to complete */ +static void __init wait_for_root(char *root_device_name) +{ + if (ROOT_DEV != 0) + return; + + pr_info("Waiting for root device %s...\n", root_device_name); + + for (;;) { + if (driver_probe_done()) { + ROOT_DEV = name_to_dev_t(root_device_name); + if (ROOT_DEV) + break; + } + msleep(5); + } + async_synchronize_full(); + +} + static dev_t __init parse_root_device(char *root_device_name) { if (!strncmp(root_device_name, "mtd", 3) || @@ -642,16 +662,8 @@ void __init prepare_namespace(void) 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() || - (ROOT_DEV = name_to_dev_t(saved_root_name)) == 0) - msleep(5); - async_synchronize_full(); - } - + if (root_wait) + wait_for_root(saved_root_name); mount_root(saved_root_name); out: devtmpfs_mount(); -- cgit v1.2.3 From c0c1a7dcb6f5db4500e6574294674213bc24940c Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 31 May 2023 14:55:23 +0200 Subject: init: move the nfs/cifs/ram special cases out of name_to_dev_t The nfs/cifs/ram special case only needs to be parsed once, and only in the boot code. Move them out of name_to_dev_t and into prepare_namespace. Signed-off-by: Christoph Hellwig Link: https://lore.kernel.org/r/20230531125535.676098-13-hch@lst.de Signed-off-by: Jens Axboe --- Documentation/admin-guide/kernel-parameters.txt | 7 ++++++- init/do_mounts.c | 14 ++++++-------- 2 files changed, 12 insertions(+), 9 deletions(-) (limited to 'init') diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 9e5bab29685f..457c342d1597 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -5452,7 +5452,12 @@ port and the regular usb controller gets disabled. root= [KNL] Root filesystem - See name_to_dev_t comment in init/do_mounts.c. + Usually this a a block device specifier of some kind, + see the name_to_dev_t comment in init/do_mounts.c for + details. + Alternatively this can be "ram" for the legacy initial + ramdisk, "nfs" and "cifs" for root on a network file + system, or "mtd" and "ubi" for mounting from raw flash. rootdelay= [KNL] Delay (in seconds) to pause before attempting to mount the root filesystem diff --git a/init/do_mounts.c b/init/do_mounts.c index d5c06c1546e8..86599faf2bf8 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -248,7 +248,6 @@ static dev_t devt_from_devnum(const char *name) * * 1) device number in hexadecimal represents itself * no leading 0x, for example b302. - * 2) /dev/nfs represents Root_NFS (0xff) * 3) /dev/ represents the device number of disk * 4) /dev/ represents the device number * of partition - device number of disk plus the partition number @@ -266,7 +265,6 @@ static dev_t devt_from_devnum(const char *name) * a colon. * 9) PARTLABEL= with name being the GPT partition label. * MSDOS partitions do not support labels! - * 10) /dev/cifs represents Root_CIFS (0xfe) * * 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 @@ -275,12 +273,6 @@ static dev_t devt_from_devnum(const char *name) */ dev_t name_to_dev_t(const char *name) { - if (strcmp(name, "/dev/nfs") == 0) - return Root_NFS; - if (strcmp(name, "/dev/cifs") == 0) - return Root_CIFS; - if (strcmp(name, "/dev/ram") == 0) - return Root_RAM0; #ifdef CONFIG_BLOCK if (strncmp(name, "PARTUUID=", 9) == 0) return devt_from_partuuid(name + 9); @@ -631,6 +623,12 @@ static dev_t __init parse_root_device(char *root_device_name) 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; return name_to_dev_t(root_device_name); } -- cgit v1.2.3 From cf056a43121559d3642419917d405c3237ded90a Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 31 May 2023 14:55:24 +0200 Subject: init: improve the name_to_dev_t interface name_to_dev_t has a very misleading name, that doesn't make clear it should only be used by the early init code, and also has a bad calling convention that doesn't allow returning different kinds of errors. Rename it to early_lookup_bdev to make the use case clear, and return an errno, where -EINVAL means the string could not be parsed, and -ENODEV means it the string was valid, but there was no device found for it. Also stub out the whole call for !CONFIG_BLOCK as all the non-block root cases are always covered in the caller. Signed-off-by: Christoph Hellwig Link: https://lore.kernel.org/r/20230531125535.676098-14-hch@lst.de Signed-off-by: Jens Axboe --- Documentation/admin-guide/kernel-parameters.txt | 4 +- drivers/md/dm-table.c | 5 +- drivers/md/md-autodetect.c | 3 +- drivers/mtd/devices/block2mtd.c | 3 +- fs/pstore/blk.c | 4 +- include/linux/blkdev.h | 5 ++ include/linux/mount.h | 1 - init/do_mounts.c | 102 ++++++++++++------------ kernel/power/hibernate.c | 22 +++-- 9 files changed, 74 insertions(+), 75 deletions(-) (limited to 'init') diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 457c342d1597..a6bc31349cbb 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -5453,8 +5453,8 @@ root= [KNL] Root filesystem Usually this a a block device specifier of some kind, - see the name_to_dev_t comment in init/do_mounts.c for - details. + see the early_lookup_bdev comment in init/do_mounts.c + for details. Alternatively this can be "ram" for the legacy initial ramdisk, "nfs" and "cifs" for root on a network file system, or "mtd" and "ubi" for mounting from raw flash. diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c index 1398f1d6e83e..05aa16da43b0 100644 --- a/drivers/md/dm-table.c +++ b/drivers/md/dm-table.c @@ -330,8 +330,9 @@ dev_t dm_get_dev_t(const char *path) { dev_t dev; - if (lookup_bdev(path, &dev)) - dev = name_to_dev_t(path); + if (lookup_bdev(path, &dev) && + early_lookup_bdev(path, &dev)) + return 0; return dev; } EXPORT_SYMBOL_GPL(dm_get_dev_t); diff --git a/drivers/md/md-autodetect.c b/drivers/md/md-autodetect.c index 91836e6de326..6eaa0eab40f9 100644 --- a/drivers/md/md-autodetect.c +++ b/drivers/md/md-autodetect.c @@ -147,7 +147,8 @@ static void __init md_setup_drive(struct md_setup_args *args) if (p) *p++ = 0; - dev = name_to_dev_t(devname); + if (early_lookup_bdev(devname, &dev)) + dev = 0; if (strncmp(devname, "/dev/", 5) == 0) devname += 5; snprintf(comp_name, 63, "/dev/%s", devname); diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index 7ac82c6fe350..a127cdde03b7 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -254,8 +254,7 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size, msleep(1000); wait_for_device_probe(); - devt = name_to_dev_t(devname); - if (!devt) + if (early_lookup_bdev(devname, &devt)) continue; bdev = blkdev_get_by_dev(devt, mode, dev, NULL); } diff --git a/fs/pstore/blk.c b/fs/pstore/blk.c index 4ae0cfcd15f2..de8cf5d75f34 100644 --- a/fs/pstore/blk.c +++ b/fs/pstore/blk.c @@ -263,9 +263,9 @@ static __init const char *early_boot_devpath(const char *initial_devname) * same scheme to find the device that we use for mounting * the root file system. */ - dev_t dev = name_to_dev_t(initial_devname); + dev_t dev; - if (!dev) { + if (early_lookup_bdev(initial_devname, &dev)) { pr_err("failed to resolve '%s'!\n", initial_devname); return initial_devname; } diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 9e9a9e4edee9..d682e233fd66 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1501,6 +1501,7 @@ int sync_blockdev_nowait(struct block_device *bdev); void sync_bdevs(bool wait); void bdev_statx_dioalign(struct inode *inode, struct kstat *stat); void printk_all_partitions(void); +int early_lookup_bdev(const char *pathname, dev_t *dev); #else static inline void invalidate_bdev(struct block_device *bdev) { @@ -1522,6 +1523,10 @@ static inline void bdev_statx_dioalign(struct inode *inode, struct kstat *stat) static inline void printk_all_partitions(void) { } +static inline int early_lookup_bdev(const char *pathname, dev_t *dev) +{ + return -EINVAL; +} #endif /* CONFIG_BLOCK */ int fsync_bdev(struct block_device *bdev); diff --git a/include/linux/mount.h b/include/linux/mount.h index 1ea326c368f7..4b81ea90440e 100644 --- a/include/linux/mount.h +++ b/include/linux/mount.h @@ -107,7 +107,6 @@ extern struct vfsmount *vfs_submount(const struct dentry *mountpoint, extern void mnt_set_expiry(struct vfsmount *mnt, struct list_head *expiry_list); extern void mark_mounts_for_expiry(struct list_head *mounts); -extern dev_t name_to_dev_t(const char *name); extern bool path_is_mountpoint(const struct path *path); extern bool our_mnt(struct vfsmount *mnt); diff --git a/init/do_mounts.c b/init/do_mounts.c index 86599faf2bf8..f1953aeb3219 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -96,11 +96,10 @@ static int match_dev_by_uuid(struct device *dev, const void *data) * * Returns the matching dev_t on success or 0 on failure. */ -static dev_t devt_from_partuuid(const char *uuid_str) +static int devt_from_partuuid(const char *uuid_str, dev_t *devt) { struct uuidcmp cmp; struct device *dev = NULL; - dev_t devt = 0; int offset = 0; char *slash; @@ -124,21 +123,21 @@ static dev_t devt_from_partuuid(const char *uuid_str) dev = class_find_device(&block_class, NULL, &cmp, &match_dev_by_uuid); if (!dev) - return 0; + return -ENODEV; if (offset) { /* * Attempt to find the requested partition by adding an offset * to the partition number found by UUID. */ - devt = part_devt(dev_to_disk(dev), - dev_to_bdev(dev)->bd_partno + offset); + *devt = part_devt(dev_to_disk(dev), + dev_to_bdev(dev)->bd_partno + offset); } else { - devt = dev->devt; + *devt = dev->devt; } put_device(dev); - return devt; + return 0; clear_root_wait: pr_err("VFS: PARTUUID= is invalid.\n" @@ -146,7 +145,7 @@ clear_root_wait: if (root_wait) pr_err("Disabling rootwait; root= is invalid.\n"); root_wait = 0; - return 0; + return -EINVAL; } /** @@ -166,38 +165,35 @@ static int match_dev_by_label(struct device *dev, const void *data) return 1; } -static dev_t devt_from_partlabel(const char *label) +static int devt_from_partlabel(const char *label, dev_t *devt) { struct device *dev; - dev_t devt = 0; dev = class_find_device(&block_class, NULL, label, &match_dev_by_label); - if (dev) { - devt = dev->devt; - put_device(dev); - } - - return devt; + if (!dev) + return -ENODEV; + *devt = dev->devt; + put_device(dev); + return 0; } -static dev_t devt_from_devname(const char *name) +static int devt_from_devname(const char *name, dev_t *devt) { - dev_t devt = 0; int part; char s[32]; char *p; if (strlen(name) > 31) - return 0; + return -EINVAL; strcpy(s, name); for (p = s; *p; p++) { if (*p == '/') *p = '!'; } - devt = blk_lookup_devt(s, 0); - if (devt) - return devt; + *devt = blk_lookup_devt(s, 0); + if (*devt) + return 0; /* * Try non-existent, but valid partition, which may only exist after @@ -206,41 +202,42 @@ static dev_t devt_from_devname(const char *name) while (p > s && isdigit(p[-1])) p--; if (p == s || !*p || *p == '0') - return 0; + return -EINVAL; /* try disk name without */ part = simple_strtoul(p, NULL, 10); *p = '\0'; - devt = blk_lookup_devt(s, part); - if (devt) - return devt; + *devt = blk_lookup_devt(s, part); + if (*devt) + return 0; /* try disk name without p */ if (p < s + 2 || !isdigit(p[-2]) || p[-1] != 'p') - return 0; + return -EINVAL; p[-1] = '\0'; - return blk_lookup_devt(s, part); + *devt = blk_lookup_devt(s, part); + if (*devt) + return 0; + return -EINVAL; } -#endif /* CONFIG_BLOCK */ -static dev_t devt_from_devnum(const char *name) +static int devt_from_devnum(const char *name, dev_t *devt) { unsigned maj, min, offset; - dev_t devt = 0; char *p, dummy; if (sscanf(name, "%u:%u%c", &maj, &min, &dummy) == 2 || sscanf(name, "%u:%u:%u:%c", &maj, &min, &offset, &dummy) == 3) { - devt = MKDEV(maj, min); - if (maj != MAJOR(devt) || min != MINOR(devt)) - return 0; + *devt = MKDEV(maj, min); + if (maj != MAJOR(*devt) || min != MINOR(*devt)) + return -EINVAL; } else { - devt = new_decode_dev(simple_strtoul(name, &p, 16)); + *devt = new_decode_dev(simple_strtoul(name, &p, 16)); if (*p) - return 0; + return -EINVAL; } - return devt; + return 0; } /* @@ -271,19 +268,18 @@ static dev_t devt_from_devnum(const char *name) * name contains slashes, the device name has them replaced with * bangs. */ -dev_t name_to_dev_t(const char *name) +int early_lookup_bdev(const char *name, dev_t *devt) { -#ifdef CONFIG_BLOCK if (strncmp(name, "PARTUUID=", 9) == 0) - return devt_from_partuuid(name + 9); + return devt_from_partuuid(name + 9, devt); if (strncmp(name, "PARTLABEL=", 10) == 0) - return devt_from_partlabel(name + 10); + return devt_from_partlabel(name + 10, devt); if (strncmp(name, "/dev/", 5) == 0) - return devt_from_devname(name + 5); -#endif - return devt_from_devnum(name); + return devt_from_devname(name + 5, devt); + return devt_from_devnum(name, devt); } -EXPORT_SYMBOL_GPL(name_to_dev_t); +EXPORT_SYMBOL_GPL(early_lookup_bdev); +#endif static int __init root_dev_setup(char *line) { @@ -606,20 +602,17 @@ static void __init wait_for_root(char *root_device_name) pr_info("Waiting for root device %s...\n", root_device_name); - for (;;) { - if (driver_probe_done()) { - ROOT_DEV = name_to_dev_t(root_device_name); - if (ROOT_DEV) - break; - } + while (!driver_probe_done() || + early_lookup_bdev(root_device_name, &ROOT_DEV) < 0) msleep(5); - } async_synchronize_full(); } static dev_t __init parse_root_device(char *root_device_name) { + dev_t dev; + if (!strncmp(root_device_name, "mtd", 3) || !strncmp(root_device_name, "ubi", 3)) return Root_Generic; @@ -629,7 +622,10 @@ static dev_t __init parse_root_device(char *root_device_name) return Root_CIFS; if (strcmp(root_device_name, "/dev/ram") == 0) return Root_RAM0; - return name_to_dev_t(root_device_name); + + if (early_lookup_bdev(root_device_name, &dev)) + return 0; + return dev; } /* diff --git a/kernel/power/hibernate.c b/kernel/power/hibernate.c index 45e24b02cd50..c52dedb9f7c8 100644 --- a/kernel/power/hibernate.c +++ b/kernel/power/hibernate.c @@ -11,6 +11,7 @@ #define pr_fmt(fmt) "PM: hibernation: " fmt +#include #include #include #include @@ -921,8 +922,7 @@ static int __init find_resume_device(void) } /* Check if the device is there */ - swsusp_resume_device = name_to_dev_t(resume_file); - if (swsusp_resume_device) + if (!early_lookup_bdev(resume_file, &swsusp_resume_device)) return 0; /* @@ -931,15 +931,12 @@ static int __init find_resume_device(void) */ wait_for_device_probe(); if (resume_wait) { - while (!(swsusp_resume_device = name_to_dev_t(resume_file))) + while (early_lookup_bdev(resume_file, &swsusp_resume_device)) msleep(10); async_synchronize_full(); } - swsusp_resume_device = name_to_dev_t(resume_file); - if (!swsusp_resume_device) - return -ENODEV; - return 0; + return early_lookup_bdev(resume_file, &swsusp_resume_device); } static int software_resume(void) @@ -1169,7 +1166,8 @@ static ssize_t resume_store(struct kobject *kobj, struct kobj_attribute *attr, unsigned int sleep_flags; int len = n; char *name; - dev_t res; + dev_t dev; + int error; if (!hibernation_available()) return 0; @@ -1180,13 +1178,13 @@ static ssize_t resume_store(struct kobject *kobj, struct kobj_attribute *attr, if (!name) return -ENOMEM; - res = name_to_dev_t(name); + error = early_lookup_bdev(name, &dev); kfree(name); - if (!res) - return -EINVAL; + if (error) + return error; sleep_flags = lock_system_sleep(); - swsusp_resume_device = res; + swsusp_resume_device = dev; unlock_system_sleep(sleep_flags); pm_pr_dbg("Configured hibernation resume from disk to %u\n", -- cgit v1.2.3 From 079caa35f7863cd9958b4555ae873ea4d352a502 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 31 May 2023 14:55:25 +0200 Subject: init: clear root_wait on all invalid root= strings Instead of only clearing root_wait in devt_from_partuuid when the UUID format was invalid, do that in parse_root_device for all strings that failed to parse. Signed-off-by: Christoph Hellwig Link: https://lore.kernel.org/r/20230531125535.676098-15-hch@lst.de Signed-off-by: Jens Axboe --- init/do_mounts.c | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) (limited to 'init') diff --git a/init/do_mounts.c b/init/do_mounts.c index f1953aeb3219..0b36a5f39ee8 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -112,14 +112,14 @@ static int devt_from_partuuid(const char *uuid_str, dev_t *devt) /* Explicitly fail on poor PARTUUID syntax. */ if (sscanf(slash + 1, "PARTNROFF=%d%c", &offset, &c) != 1) - goto clear_root_wait; + goto out_invalid; cmp.len = slash - uuid_str; } else { cmp.len = strlen(uuid_str); } if (!cmp.len) - goto clear_root_wait; + goto out_invalid; dev = class_find_device(&block_class, NULL, &cmp, &match_dev_by_uuid); if (!dev) @@ -139,12 +139,9 @@ static int devt_from_partuuid(const char *uuid_str, dev_t *devt) put_device(dev); return 0; -clear_root_wait: +out_invalid: pr_err("VFS: PARTUUID= is invalid.\n" "Expected PARTUUID=[/PARTNROFF=%%d]\n"); - if (root_wait) - pr_err("Disabling rootwait; root= is invalid.\n"); - root_wait = 0; return -EINVAL; } @@ -611,6 +608,7 @@ static void __init wait_for_root(char *root_device_name) static dev_t __init parse_root_device(char *root_device_name) { + int error; dev_t dev; if (!strncmp(root_device_name, "mtd", 3) || @@ -623,8 +621,14 @@ static dev_t __init parse_root_device(char *root_device_name) if (strcmp(root_device_name, "/dev/ram") == 0) return Root_RAM0; - if (early_lookup_bdev(root_device_name, &dev)) + 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; } -- cgit v1.2.3 From 702f3189e454b3c3c2f3c99dbf30acf41aab707c Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Wed, 31 May 2023 14:55:26 +0200 Subject: block: move the code to do early boot lookup of block devices to block/ Create a new block/early-lookup.c to keep the early block device lookup code instead of having this code sit with the early mount code. Signed-off-by: Christoph Hellwig Link: https://lore.kernel.org/r/20230531125535.676098-16-hch@lst.de Signed-off-by: Jens Axboe --- Documentation/admin-guide/kernel-parameters.txt | 4 +- block/Makefile | 2 +- block/early-lookup.c | 224 ++++++++++++++++++++++++ init/do_mounts.c | 219 ----------------------- 4 files changed, 227 insertions(+), 222 deletions(-) create mode 100644 block/early-lookup.c (limited to 'init') diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index a6bc31349cbb..911c54829c7c 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -5453,8 +5453,8 @@ root= [KNL] Root filesystem Usually this a a block device specifier of some kind, - see the early_lookup_bdev comment in init/do_mounts.c - for details. + see the early_lookup_bdev comment in + block/early-lookup.c for details. Alternatively this can be "ram" for the legacy initial ramdisk, "nfs" and "cifs" for root on a network file system, or "mtd" and "ubi" for mounting from raw flash. diff --git a/block/Makefile b/block/Makefile index b31b05390749..46ada9dc8bbf 100644 --- a/block/Makefile +++ b/block/Makefile @@ -9,7 +9,7 @@ obj-y := bdev.o fops.o bio.o elevator.o blk-core.o blk-sysfs.o \ blk-lib.o blk-mq.o blk-mq-tag.o blk-stat.o \ blk-mq-sysfs.o blk-mq-cpumap.o blk-mq-sched.o ioctl.o \ genhd.o ioprio.o badblocks.o partitions/ blk-rq-qos.o \ - disk-events.o blk-ia-ranges.o + disk-events.o blk-ia-ranges.o early-lookup.o obj-$(CONFIG_BOUNCE) += bounce.o obj-$(CONFIG_BLK_DEV_BSG_COMMON) += bsg.o diff --git a/block/early-lookup.c b/block/early-lookup.c new file mode 100644 index 000000000000..9fc30d039508 --- /dev/null +++ b/block/early-lookup.c @@ -0,0 +1,224 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Code for looking up block devices in the early boot code before mounting the + * root file system. Unfortunately currently also abused in a few other places. + */ +#include +#include + +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) +{ + struct block_device *bdev = dev_to_bdev(dev); + const struct uuidcmp *cmp = data; + + if (!bdev->bd_meta_info || + strncasecmp(cmp->uuid, bdev->bd_meta_info->uuid, cmp->len)) + return 0; + return 1; +} + +/** + * devt_from_partuuid - looks up the dev_t of a partition by its UUID + * @uuid_str: 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_str 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 int devt_from_partuuid(const char *uuid_str, dev_t *devt) +{ + struct uuidcmp cmp; + struct device *dev = NULL; + int offset = 0; + 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) + goto out_invalid; + cmp.len = slash - uuid_str; + } else { + cmp.len = strlen(uuid_str); + } + + if (!cmp.len) + goto out_invalid; + + dev = class_find_device(&block_class, NULL, &cmp, &match_dev_by_uuid); + if (!dev) + return -ENODEV; + + if (offset) { + /* + * Attempt to find the requested partition by adding an offset + * to the partition number found by UUID. + */ + *devt = part_devt(dev_to_disk(dev), + dev_to_bdev(dev)->bd_partno + offset); + } else { + *devt = dev->devt; + } + + put_device(dev); + return 0; + +out_invalid: + pr_err("VFS: PARTUUID= is invalid.\n" + "Expected PARTUUID=[/PARTNROFF=%%d]\n"); + return -EINVAL; +} + +/** + * match_dev_by_label - callback for finding a partition using its label + * @dev: device passed in by the caller + * @data: opaque pointer to the label to match + * + * Returns 1 if the device matches, and 0 otherwise. + */ +static int match_dev_by_label(struct device *dev, const void *data) +{ + struct block_device *bdev = dev_to_bdev(dev); + const char *label = data; + + if (!bdev->bd_meta_info || strcmp(label, bdev->bd_meta_info->volname)) + return 0; + return 1; +} + +static int devt_from_partlabel(const char *label, dev_t *devt) +{ + struct device *dev; + + dev = class_find_device(&block_class, NULL, label, &match_dev_by_label); + if (!dev) + return -ENODEV; + *devt = dev->devt; + put_device(dev); + return 0; +} + +static int devt_from_devname(const char *name, dev_t *devt) +{ + int part; + char s[32]; + char *p; + + if (strlen(name) > 31) + return -EINVAL; + strcpy(s, name); + for (p = s; *p; p++) { + if (*p == '/') + *p = '!'; + } + + *devt = blk_lookup_devt(s, 0); + if (*devt) + return 0; + + /* + * Try non-existent, but valid partition, which may only exist after + * opening the device, like partitioned md devices. + */ + while (p > s && isdigit(p[-1])) + p--; + if (p == s || !*p || *p == '0') + return -EINVAL; + + /* try disk name without */ + part = simple_strtoul(p, NULL, 10); + *p = '\0'; + *devt = blk_lookup_devt(s, part); + if (*devt) + return 0; + + /* try disk name without p */ + if (p < s + 2 || !isdigit(p[-2]) || p[-1] != 'p') + return -EINVAL; + p[-1] = '\0'; + *devt = blk_lookup_devt(s, part); + if (*devt) + return 0; + return -EINVAL; +} + +static int devt_from_devnum(const char *name, dev_t *devt) +{ + unsigned maj, min, offset; + char *p, dummy; + + if (sscanf(name, "%u:%u%c", &maj, &min, &dummy) == 2 || + sscanf(name, "%u:%u:%u:%c", &maj, &min, &offset, &dummy) == 3) { + *devt = MKDEV(maj, min); + if (maj != MAJOR(*devt) || min != MINOR(*devt)) + return -EINVAL; + } else { + *devt = new_decode_dev(simple_strtoul(name, &p, 16)); + if (*p) + return -EINVAL; + } + + return 0; +} + +/* + * Convert a name into device number. We accept the following variants: + * + * 1) device number in hexadecimal represents itself + * no leading 0x, for example b302. + * 3) /dev/ represents the device number of disk + * 4) /dev/ represents the device number + * of partition - device number of disk plus the partition number + * 5) /dev/p - 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=/PARTNROFF= to select a partition in relation to + * a partition with a known unique id. + * 8) : major and minor number of the device separated by + * a colon. + * 9) PARTLABEL= with name being the GPT partition label. + * MSDOS partitions do not support labels! + * + * 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. + */ +int early_lookup_bdev(const char *name, dev_t *devt) +{ + if (strncmp(name, "PARTUUID=", 9) == 0) + return devt_from_partuuid(name + 9, devt); + if (strncmp(name, "PARTLABEL=", 10) == 0) + return devt_from_partlabel(name + 10, devt); + if (strncmp(name, "/dev/", 5) == 0) + return devt_from_devname(name + 5, devt); + return devt_from_devnum(name, devt); +} +EXPORT_SYMBOL_GPL(early_lookup_bdev); diff --git a/init/do_mounts.c b/init/do_mounts.c index 0b36a5f39ee8..780546a6cbfb 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -59,225 +59,6 @@ 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) -{ - struct block_device *bdev = dev_to_bdev(dev); - const struct uuidcmp *cmp = data; - - if (!bdev->bd_meta_info || - strncasecmp(cmp->uuid, bdev->bd_meta_info->uuid, cmp->len)) - return 0; - return 1; -} - -/** - * devt_from_partuuid - looks up the dev_t of a partition by its UUID - * @uuid_str: 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_str 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 int devt_from_partuuid(const char *uuid_str, dev_t *devt) -{ - struct uuidcmp cmp; - struct device *dev = NULL; - int offset = 0; - 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) - goto out_invalid; - cmp.len = slash - uuid_str; - } else { - cmp.len = strlen(uuid_str); - } - - if (!cmp.len) - goto out_invalid; - - dev = class_find_device(&block_class, NULL, &cmp, &match_dev_by_uuid); - if (!dev) - return -ENODEV; - - if (offset) { - /* - * Attempt to find the requested partition by adding an offset - * to the partition number found by UUID. - */ - *devt = part_devt(dev_to_disk(dev), - dev_to_bdev(dev)->bd_partno + offset); - } else { - *devt = dev->devt; - } - - put_device(dev); - return 0; - -out_invalid: - pr_err("VFS: PARTUUID= is invalid.\n" - "Expected PARTUUID=[/PARTNROFF=%%d]\n"); - return -EINVAL; -} - -/** - * match_dev_by_label - callback for finding a partition using its label - * @dev: device passed in by the caller - * @data: opaque pointer to the label to match - * - * Returns 1 if the device matches, and 0 otherwise. - */ -static int match_dev_by_label(struct device *dev, const void *data) -{ - struct block_device *bdev = dev_to_bdev(dev); - const char *label = data; - - if (!bdev->bd_meta_info || strcmp(label, bdev->bd_meta_info->volname)) - return 0; - return 1; -} - -static int devt_from_partlabel(const char *label, dev_t *devt) -{ - struct device *dev; - - dev = class_find_device(&block_class, NULL, label, &match_dev_by_label); - if (!dev) - return -ENODEV; - *devt = dev->devt; - put_device(dev); - return 0; -} - -static int devt_from_devname(const char *name, dev_t *devt) -{ - int part; - char s[32]; - char *p; - - if (strlen(name) > 31) - return -EINVAL; - strcpy(s, name); - for (p = s; *p; p++) { - if (*p == '/') - *p = '!'; - } - - *devt = blk_lookup_devt(s, 0); - if (*devt) - return 0; - - /* - * Try non-existent, but valid partition, which may only exist after - * opening the device, like partitioned md devices. - */ - while (p > s && isdigit(p[-1])) - p--; - if (p == s || !*p || *p == '0') - return -EINVAL; - - /* try disk name without */ - part = simple_strtoul(p, NULL, 10); - *p = '\0'; - *devt = blk_lookup_devt(s, part); - if (*devt) - return 0; - - /* try disk name without p */ - if (p < s + 2 || !isdigit(p[-2]) || p[-1] != 'p') - return -EINVAL; - p[-1] = '\0'; - *devt = blk_lookup_devt(s, part); - if (*devt) - return 0; - return -EINVAL; -} - -static int devt_from_devnum(const char *name, dev_t *devt) -{ - unsigned maj, min, offset; - char *p, dummy; - - if (sscanf(name, "%u:%u%c", &maj, &min, &dummy) == 2 || - sscanf(name, "%u:%u:%u:%c", &maj, &min, &offset, &dummy) == 3) { - *devt = MKDEV(maj, min); - if (maj != MAJOR(*devt) || min != MINOR(*devt)) - return -EINVAL; - } else { - *devt = new_decode_dev(simple_strtoul(name, &p, 16)); - if (*p) - return -EINVAL; - } - - return 0; -} - -/* - * Convert a name into device number. We accept the following variants: - * - * 1) device number in hexadecimal represents itself - * no leading 0x, for example b302. - * 3) /dev/ represents the device number of disk - * 4) /dev/ represents the device number - * of partition - device number of disk plus the partition number - * 5) /dev/p - 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=/PARTNROFF= to select a partition in relation to - * a partition with a known unique id. - * 8) : major and minor number of the device separated by - * a colon. - * 9) PARTLABEL= with name being the GPT partition label. - * MSDOS partitions do not support labels! - * - * 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. - */ -int early_lookup_bdev(const char *name, dev_t *devt) -{ - if (strncmp(name, "PARTUUID=", 9) == 0) - return devt_from_partuuid(name + 9, devt); - if (strncmp(name, "PARTLABEL=", 10) == 0) - return devt_from_partlabel(name + 10, devt); - if (strncmp(name, "/dev/", 5) == 0) - return devt_from_devname(name + 5, devt); - return devt_from_devnum(name, devt); -} -EXPORT_SYMBOL_GPL(early_lookup_bdev); -#endif - static int __init root_dev_setup(char *line) { strscpy(saved_root_name, line, sizeof(saved_root_name)); -- cgit v1.2.3