From 79d94ccef063b2d1d0b2fcf4bb2089ac7dbde3a1 Mon Sep 17 00:00:00 2001 From: meganz009 Date: Sat, 24 Jun 2023 16:08:31 +0800 Subject: [PATCH 1/4] devtmpfs: don't mix {ramfs,shmem}_fill_super() with mount_single() commit d401727ea0d7a48eaa01a8089f6b91a8121dcaac upstream. Create an internal-only type matching the current devtmpfs, never register it and have one kernel-internal mount done. That thing gets mounted only once, so it is free to use mount_nodev(). The "public" devtmpfs (the one we do register, and only after the internal mount of the real thing is done) simply gets and returns an extra reference to the internal superblock. Signed-off-by: Al Viro Signed-off-by: Wenya Zhang Reviewed-by: Huang Jian --- drivers/base/devtmpfs.c | 7 +++---- include/linux/device.h | 4 ++-- init/do_mounts.c | 2 +- 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index aaf5f416deb8..7aec727bb812 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -362,7 +362,7 @@ static int handle_remove(const char *nodename, struct device *dev) * If configured, or requested by the commandline, devtmpfs will be * auto-mounted after the kernel mounted the root filesystem. */ -int devtmpfs_mount(const char *mntdir) +int devtmpfs_mount(void) { int err; @@ -372,8 +372,7 @@ int devtmpfs_mount(const char *mntdir) if (!thread) return 0; - err = ksys_mount("devtmpfs", (char *)mntdir, "devtmpfs", MS_SILENT, - NULL); + err = do_mount("devtmpfs", "dev", "devtmpfs", MS_SILENT, NULL); if (err) printk(KERN_INFO "devtmpfs: error mounting %i\n", err); else @@ -398,7 +397,7 @@ static int devtmpfsd(void *p) *err = ksys_unshare(CLONE_NEWNS); if (*err) goto out; - *err = ksys_mount("devtmpfs", "/", "devtmpfs", MS_SILENT, NULL); + *err = do_mount("devtmpfs", "/", "devtmpfs", MS_SILENT, NULL); if (*err) goto out; ksys_chdir("/.."); /* will traverse into overmounted root */ diff --git a/include/linux/device.h b/include/linux/device.h index c86a40f6b745..9e4678fd99b2 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -1340,11 +1340,11 @@ extern bool kill_device(struct device *dev); #ifdef CONFIG_DEVTMPFS extern int devtmpfs_create_node(struct device *dev); extern int devtmpfs_delete_node(struct device *dev); -extern int devtmpfs_mount(const char *mntdir); +extern int devtmpfs_mount(void); #else static inline int devtmpfs_create_node(struct device *dev) { return 0; } static inline int devtmpfs_delete_node(struct device *dev) { return 0; } -static inline int devtmpfs_mount(const char *mountpoint) { return 0; } +static inline int devtmpfs_mount(void) { return 0; } #endif /* drivers/base/power/shutdown.c */ diff --git a/init/do_mounts.c b/init/do_mounts.c index e1c9afa9d8c9..662167964659 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -588,7 +588,7 @@ void __init prepare_namespace(void) mount_root(); out: - devtmpfs_mount("dev"); + devtmpfs_mount(); ksys_mount(".", "/", NULL, MS_MOVE, NULL); ksys_chroot("."); } -- Gitee From 5751e21bb5d0fae12895af221d92382959e02bbc Mon Sep 17 00:00:00 2001 From: meganz009 Date: Sat, 24 Jun 2023 16:10:07 +0800 Subject: [PATCH 2/4] initrd: use do_mount() instead of ksys_mount() commit d4440aac83d12f87df9bcc51e992b9c28c7f4fa5 upstream. All three calls to ksys_mount() in initrd-related kernel code can be switched over to do_mount(): - the first and third arguments are const strings in the kernel, and do not need to be copied over from userspace; - the fifth argument is NULL, and therefore no page needs to be, copied over from userspace; - the second and fourth argument are passed through anyway. Signed-off-by: Dominik Brodowski Signed-off-by: Wenya Zhang Reviewed-by: Huang Jian --- init/do_mounts_initrd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/init/do_mounts_initrd.c b/init/do_mounts_initrd.c index d1a5d885ce13..9835192ca4b8 100644 --- a/init/do_mounts_initrd.c +++ b/init/do_mounts_initrd.c @@ -33,7 +33,7 @@ static int init_linuxrc(struct subprocess_info *info, struct cred *new) ksys_dup(0); /* move initrd over / and chdir/chroot in initrd root */ ksys_chdir("/root"); - ksys_mount(".", "/", NULL, MS_MOVE, NULL); + do_mount(".", "/", NULL, MS_MOVE, NULL); ksys_chroot("."); ksys_setsid(); return 0; @@ -71,7 +71,7 @@ static void __init handle_initrd(void) current->flags &= ~PF_FREEZER_SKIP; /* move initrd to rootfs' /old */ - ksys_mount("..", ".", NULL, MS_MOVE, NULL); + do_mount("..", ".", NULL, MS_MOVE, NULL); /* switch root and cwd back to / of rootfs */ ksys_chroot(".."); @@ -85,7 +85,7 @@ static void __init handle_initrd(void) mount_root(); printk(KERN_NOTICE "Trying to move old root to /initrd ... "); - error = ksys_mount("/old", "/root/initrd", NULL, MS_MOVE, NULL); + error = do_mount("/old", "/root/initrd", NULL, MS_MOVE, NULL); if (!error) printk("okay\n"); else { -- Gitee From 75200134c2d4569345c17722ea3eefd916199542 Mon Sep 17 00:00:00 2001 From: meganz009 Date: Sat, 24 Jun 2023 16:19:47 +0800 Subject: [PATCH 3/4] init: use do_mount() instead of ksys_mount() commit cccaa5e33525fc07f4a2ce0518e50b9ddf435e47 upstream. In prepare_namespace(), do_mount() can be used instead of ksys_mount() as the first and third argument are const strings in the kernel, the second and fourth argument are passed through anyway, and the fifth argument is NULL. In do_mount_root(), ksys_mount() is called with the first and third argument being already kernelspace strings, which do not need to be copied over from userspace to kernelspace (again). The second and fourth arguments are passed through to do_mount() anyway. The fifth argument, while already residing in kernelspace, needs to be put into a page of its own. Then, do_mount() can be used instead of ksys_mount(). Once this is done, there are no in-kernel users to ksys_mount() left, which can therefore be removed. Signed-off-by: Dominik Brodowski Signed-off-by: Wenya Zhang Reviewed-by: Huang Jian --- fs/namespace.c | 10 ++-------- include/linux/syscalls.h | 2 -- init/do_mounts.c | 28 ++++++++++++++++++++++------ 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/fs/namespace.c b/fs/namespace.c index 48f675938c34..fb9746b62f82 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -3165,8 +3165,8 @@ struct dentry *mount_subtree(struct vfsmount *m, const char *name) } EXPORT_SYMBOL(mount_subtree); -int ksys_mount(char __user *dev_name, char __user *dir_name, char __user *type, - unsigned long flags, void __user *data) +SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name, + char __user *, type, unsigned long, flags, void __user *, data) { int ret; char *kernel_type; @@ -3199,12 +3199,6 @@ int ksys_mount(char __user *dev_name, char __user *dir_name, char __user *type, return ret; } -SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name, - char __user *, type, unsigned long, flags, void __user *, data) -{ - return ksys_mount(dev_name, dir_name, type, flags, data); -} - /* * Move a mount from one place to another. * In combination with open_tree(OPEN_TREE_CLONE [| AT_RECURSIVE]) it can be diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index ca6fa5e07866..a26f74036855 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -1142,8 +1142,6 @@ asmlinkage long sys_ni_syscall(void); * the ksys_xyzyyz() functions prototyped below. */ -int ksys_mount(char __user *dev_name, char __user *dir_name, char __user *type, - unsigned long flags, void __user *data); int ksys_umount(char __user *name, int flags); int ksys_dup(unsigned int fildes); int ksys_chroot(const char __user *filename); diff --git a/init/do_mounts.c b/init/do_mounts.c index 662167964659..16a567f6862d 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -350,12 +350,25 @@ static void __init get_fs_names(char *page) *s = '\0'; } -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 = ksys_mount(name, "/root", fs, flags, data); - if (err) - return err; + char *data_page; + struct page *p; + int ret; + + /* do_mount() requires a full page as fifth argument */ + p = alloc_page(GFP_KERNEL); + if (!p) + return -ENOMEM; + + data_page = page_address(p); + strncpy(data_page, data, PAGE_SIZE - 1); + + ret = do_mount(name, "/root", fs, flags, data_page); + if (ret) + goto out; ksys_chdir("/root"); s = current->fs->pwd.dentry->d_sb; @@ -365,7 +378,10 @@ static int __init do_mount_root(char *name, char *fs, int flags, void *data) s->s_type->name, sb_rdonly(s) ? " readonly" : "", MAJOR(ROOT_DEV), MINOR(ROOT_DEV)); - return 0; + +out: + put_page(p); + return ret; } void __init mount_block_root(char *name, int flags) @@ -589,7 +605,7 @@ void __init prepare_namespace(void) mount_root(); out: devtmpfs_mount(); - ksys_mount(".", "/", NULL, MS_MOVE, NULL); + do_mount(".", "/", NULL, MS_MOVE, NULL); ksys_chroot("."); } -- Gitee From 4ca1b1b873c1852d73f4b128f931e6a6db385f8b Mon Sep 17 00:00:00 2001 From: meganz009 Date: Sat, 24 Jun 2023 16:25:59 +0800 Subject: [PATCH 4/4] init: unify opening /dev/console as stdin/stdout/stderr commit b49a733d684e0096340b93e9dfd471f0e3ddc06d upstream. Merge the two instances where /dev/console is opened as stdin/stdout/stderr. Signed-off-by: Dominik Brodowski Signed-off-by: Wenya Zhang Reviewed-by: Huang Jian --- include/linux/initrd.h | 2 ++ init/do_mounts_initrd.c | 5 +---- init/main.c | 17 ++++++++++++----- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/include/linux/initrd.h b/include/linux/initrd.h index 84b423044088..02625c850b76 100644 --- a/include/linux/initrd.h +++ b/include/linux/initrd.h @@ -22,3 +22,5 @@ extern unsigned long initrd_start, initrd_end; extern void free_initrd_mem(unsigned long, unsigned long); extern unsigned int real_root_dev; + +void console_on_rootfs(void); diff --git a/init/do_mounts_initrd.c b/init/do_mounts_initrd.c index 9835192ca4b8..68ab321cf9ab 100644 --- a/init/do_mounts_initrd.c +++ b/init/do_mounts_initrd.c @@ -27,10 +27,7 @@ __setup("noinitrd", no_initrd); static int init_linuxrc(struct subprocess_info *info, struct cred *new) { ksys_unshare(CLONE_FS | CLONE_FILES); - /* stdin/stdout/stderr for /linuxrc */ - ksys_open("/dev/console", O_RDWR, 0); - ksys_dup(0); - ksys_dup(0); + console_on_rootfs(); /* move initrd over / and chdir/chroot in initrd root */ ksys_chdir("/root"); do_mount(".", "/", NULL, MS_MOVE, NULL); diff --git a/init/main.c b/init/main.c index c11aed805ef5..35a42c105eb9 100644 --- a/init/main.c +++ b/init/main.c @@ -1116,6 +1116,17 @@ static int __ref kernel_init(void *unused) "See Linux Documentation/admin-guide/init.rst for guidance."); } +void console_on_rootfs(void) +{ + /* Open the /dev/console as stdin, this should never fail */ + if (ksys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) + pr_err("Warning: unable to open an initial console.\n"); + + /* create stdout/stderr */ + (void) ksys_dup(0); + (void) ksys_dup(0); +} + static noinline void __init kernel_init_freeable(void) { /* @@ -1152,12 +1163,8 @@ static noinline void __init kernel_init_freeable(void) lockup_detector_init(); - /* Open the /dev/console on the rootfs, this should never fail */ - if (ksys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0) - pr_err("Warning: unable to open an initial console.\n"); + console_on_rootfs(); - (void) ksys_dup(0); - (void) ksys_dup(0); /* * check if there is an early userspace init. If yes, let it do all * the work -- Gitee