From a1e6aaa3743a3daa98d0f9275df8243eef8bb666 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 4 Jun 2020 09:41:08 +0200 Subject: fs: refactor do_mount Factor out a path_mount helper that takes a struct path * instead of the actual file name. This will allow to convert the init and devtmpfs code to properly mount based on a kernel pointer instead of relying on the implicit set_fs(KERNEL_DS) during early init. Signed-off-by: Christoph Hellwig --- fs/namespace.c | 67 ++++++++++++++++++++++++++++++---------------------------- 1 file changed, 35 insertions(+), 32 deletions(-) (limited to 'fs/namespace.c') diff --git a/fs/namespace.c b/fs/namespace.c index f30ed401cc6d..6f8234f74bed 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -3115,12 +3115,11 @@ char *copy_mount_string(const void __user *data) * Therefore, if this magic number is present, it carries no information * and must be discarded. */ -long do_mount(const char *dev_name, const char __user *dir_name, +static int path_mount(const char *dev_name, struct path *path, const char *type_page, unsigned long flags, void *data_page) { - struct path path; unsigned int mnt_flags = 0, sb_flags; - int retval = 0; + int ret; /* Discard magic */ if ((flags & MS_MGC_MSK) == MS_MGC_VAL) @@ -3133,19 +3132,13 @@ long do_mount(const char *dev_name, const char __user *dir_name, if (flags & MS_NOUSER) return -EINVAL; - /* ... and get the mountpoint */ - retval = user_path_at(AT_FDCWD, dir_name, LOOKUP_FOLLOW, &path); - if (retval) - return retval; - - retval = security_sb_mount(dev_name, &path, - type_page, flags, data_page); - if (!retval && !may_mount()) - retval = -EPERM; - if (!retval && (flags & SB_MANDLOCK) && !may_mandlock()) - retval = -EPERM; - if (retval) - goto dput_out; + ret = security_sb_mount(dev_name, path, type_page, flags, data_page); + if (ret) + return ret; + if (!may_mount()) + return -EPERM; + if ((flags & SB_MANDLOCK) && !may_mandlock()) + return -EPERM; /* Default to relatime unless overriden */ if (!(flags & MS_NOATIME)) @@ -3172,7 +3165,7 @@ long do_mount(const char *dev_name, const char __user *dir_name, ((flags & (MS_NOATIME | MS_NODIRATIME | MS_RELATIME | MS_STRICTATIME)) == 0)) { mnt_flags &= ~MNT_ATIME_MASK; - mnt_flags |= path.mnt->mnt_flags & MNT_ATIME_MASK; + mnt_flags |= path->mnt->mnt_flags & MNT_ATIME_MASK; } sb_flags = flags & (SB_RDONLY | @@ -3185,22 +3178,32 @@ long do_mount(const char *dev_name, const char __user *dir_name, SB_I_VERSION); if ((flags & (MS_REMOUNT | MS_BIND)) == (MS_REMOUNT | MS_BIND)) - retval = do_reconfigure_mnt(&path, mnt_flags); - else if (flags & MS_REMOUNT) - retval = do_remount(&path, flags, sb_flags, mnt_flags, - data_page); - else if (flags & MS_BIND) - retval = do_loopback(&path, dev_name, flags & MS_REC); - else if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE)) - retval = do_change_type(&path, flags); - else if (flags & MS_MOVE) - retval = do_move_mount_old(&path, dev_name); - else - retval = do_new_mount(&path, type_page, sb_flags, mnt_flags, - dev_name, data_page); -dput_out: + return do_reconfigure_mnt(path, mnt_flags); + if (flags & MS_REMOUNT) + return do_remount(path, flags, sb_flags, mnt_flags, data_page); + if (flags & MS_BIND) + return do_loopback(path, dev_name, flags & MS_REC); + if (flags & (MS_SHARED | MS_PRIVATE | MS_SLAVE | MS_UNBINDABLE)) + return do_change_type(path, flags); + if (flags & MS_MOVE) + return do_move_mount_old(path, dev_name); + + return do_new_mount(path, type_page, sb_flags, mnt_flags, dev_name, + data_page); +} + +long do_mount(const char *dev_name, const char __user *dir_name, + const char *type_page, unsigned long flags, void *data_page) +{ + struct path path; + int ret; + + ret = user_path_at(AT_FDCWD, dir_name, LOOKUP_FOLLOW, &path); + if (ret) + return ret; + ret = path_mount(dev_name, &path, type_page, flags, data_page); path_put(&path); - return retval; + return ret; } static struct ucounts *inc_mnt_namespaces(struct user_namespace *ns) -- cgit v1.2.3 From 41525f56e2564c2feff4fb2824823900efb3a39f Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 21 Jul 2020 10:54:34 +0200 Subject: fs: refactor ksys_umount Factor out a path_umount helper that takes a struct path * instead of the actual file name. This will allow to convert the init and devtmpfs code to properly mount based on a kernel pointer instead of relying on the implicit set_fs(KERNEL_DS) during early init. Signed-off-by: Christoph Hellwig --- fs/namespace.c | 40 ++++++++++++++++++---------------------- 1 file changed, 18 insertions(+), 22 deletions(-) (limited to 'fs/namespace.c') diff --git a/fs/namespace.c b/fs/namespace.c index 6f8234f74bed..43834b59eff6 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1706,36 +1706,19 @@ static inline bool may_mandlock(void) } #endif -/* - * Now umount can handle mount points as well as block devices. - * This is important for filesystems which use unnamed block devices. - * - * We now support a flag for forced unmount like the other 'big iron' - * unixes. Our API is identical to OSF/1 to avoid making a mess of AMD - */ - -int ksys_umount(char __user *name, int flags) +static int path_umount(struct path *path, int flags) { - struct path path; struct mount *mnt; int retval; - int lookup_flags = LOOKUP_MOUNTPOINT; if (flags & ~(MNT_FORCE | MNT_DETACH | MNT_EXPIRE | UMOUNT_NOFOLLOW)) return -EINVAL; - if (!may_mount()) return -EPERM; - if (!(flags & UMOUNT_NOFOLLOW)) - lookup_flags |= LOOKUP_FOLLOW; - - retval = user_path_at(AT_FDCWD, name, lookup_flags, &path); - if (retval) - goto out; - mnt = real_mount(path.mnt); + mnt = real_mount(path->mnt); retval = -EINVAL; - if (path.dentry != path.mnt->mnt_root) + if (path->dentry != path->mnt->mnt_root) goto dput_and_out; if (!check_mnt(mnt)) goto dput_and_out; @@ -1748,12 +1731,25 @@ int ksys_umount(char __user *name, int flags) retval = do_umount(mnt, flags); dput_and_out: /* we mustn't call path_put() as that would clear mnt_expiry_mark */ - dput(path.dentry); + dput(path->dentry); mntput_no_expire(mnt); -out: return retval; } +int ksys_umount(char __user *name, int flags) +{ + int lookup_flags = LOOKUP_MOUNTPOINT; + struct path path; + int ret; + + if (!(flags & UMOUNT_NOFOLLOW)) + lookup_flags |= LOOKUP_FOLLOW; + ret = user_path_at(AT_FDCWD, name, lookup_flags, &path); + if (ret) + return ret; + return path_umount(&path, flags); +} + SYSCALL_DEFINE2(umount, char __user *, name, int, flags) { return ksys_umount(name, flags); -- cgit v1.2.3 From c60166f04283ffba7b88b45d824bbfb2bfccee24 Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Tue, 21 Jul 2020 11:12:08 +0200 Subject: init: add an init_mount helper Like do_mount, but takes a kernel pointer for the destination path. Switch over the mounts in the init code and devtmpfs to it, which just happen to work due to the implicit set_fs(KERNEL_DS) during early init right now. Signed-off-by: Christoph Hellwig --- drivers/base/devtmpfs.c | 5 +++-- fs/Makefile | 2 +- fs/init.c | 25 +++++++++++++++++++++++++ fs/internal.h | 4 ++++ fs/namespace.c | 2 +- include/linux/init_syscalls.h | 4 ++++ init/do_mounts.c | 8 ++++---- init/do_mounts.h | 1 + init/do_mounts_initrd.c | 6 +++--- 9 files changed, 46 insertions(+), 11 deletions(-) create mode 100644 fs/init.c create mode 100644 include/linux/init_syscalls.h (limited to 'fs/namespace.c') diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c index d697634bc0d4..32af6cb987b4 100644 --- a/drivers/base/devtmpfs.c +++ b/drivers/base/devtmpfs.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include "base.h" @@ -359,7 +360,7 @@ int __init devtmpfs_mount(void) if (!thread) return 0; - err = do_mount("devtmpfs", "dev", "devtmpfs", MS_SILENT, NULL); + err = init_mount("devtmpfs", "dev", "devtmpfs", MS_SILENT, NULL); if (err) printk(KERN_INFO "devtmpfs: error mounting %i\n", err); else @@ -408,7 +409,7 @@ static int __init devtmpfs_setup(void *p) err = ksys_unshare(CLONE_NEWNS); if (err) goto out; - err = do_mount("devtmpfs", "/", "devtmpfs", MS_SILENT, NULL); + err = init_mount("devtmpfs", "/", "devtmpfs", MS_SILENT, NULL); if (err) goto out; ksys_chdir("/.."); /* will traverse into overmounted root */ diff --git a/fs/Makefile b/fs/Makefile index 2ce5112b02c8..1c7b0e3f6daa 100644 --- a/fs/Makefile +++ b/fs/Makefile @@ -13,7 +13,7 @@ obj-y := open.o read_write.o file_table.o super.o \ seq_file.o xattr.o libfs.o fs-writeback.o \ pnode.o splice.o sync.o utimes.o d_path.o \ stack.o fs_struct.o statfs.o fs_pin.o nsfs.o \ - fs_types.o fs_context.o fs_parser.o fsopen.o + fs_types.o fs_context.o fs_parser.o fsopen.o init.o ifeq ($(CONFIG_BLOCK),y) obj-y += buffer.o block_dev.o direct-io.o mpage.o diff --git a/fs/init.c b/fs/init.c new file mode 100644 index 000000000000..c6eb724e1c7b --- /dev/null +++ b/fs/init.c @@ -0,0 +1,25 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Routines that mimic syscalls, but don't use the user address space or file + * descriptors. Only for init/ and related early init code. + */ +#include +#include +#include +#include +#include +#include "internal.h" + +int __init init_mount(const char *dev_name, const char *dir_name, + const char *type_page, unsigned long flags, void *data_page) +{ + struct path path; + int ret; + + ret = kern_path(dir_name, LOOKUP_FOLLOW, &path); + if (ret) + return ret; + ret = path_mount(dev_name, &path, type_page, flags, data_page); + path_put(&path); + return ret; +} diff --git a/fs/internal.h b/fs/internal.h index e903d5aae139..72ea0b6f7435 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -89,6 +89,10 @@ extern int __mnt_want_write_file(struct file *); extern void __mnt_drop_write_file(struct file *); extern void dissolve_on_fput(struct vfsmount *); + +int path_mount(const char *dev_name, struct path *path, + const char *type_page, unsigned long flags, void *data_page); + /* * fs_struct.c */ diff --git a/fs/namespace.c b/fs/namespace.c index 43834b59eff6..2c4d75920974 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -3111,7 +3111,7 @@ char *copy_mount_string(const void __user *data) * Therefore, if this magic number is present, it carries no information * and must be discarded. */ -static int path_mount(const char *dev_name, struct path *path, +int path_mount(const char *dev_name, struct path *path, const char *type_page, unsigned long flags, void *data_page) { unsigned int mnt_flags = 0, sb_flags; diff --git a/include/linux/init_syscalls.h b/include/linux/init_syscalls.h new file mode 100644 index 000000000000..af9ea88a60e0 --- /dev/null +++ b/include/linux/init_syscalls.h @@ -0,0 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ + +int __init init_mount(const char *dev_name, const char *dir_name, + const char *type_page, unsigned long flags, void *data_page); diff --git a/init/do_mounts.c b/init/do_mounts.c index a7f22cf58c7e..83db87b6e5d1 100644 --- a/init/do_mounts.c +++ b/init/do_mounts.c @@ -395,16 +395,16 @@ static int __init do_mount_root(const char *name, const char *fs, int ret; if (data) { - /* do_mount() requires a full page as fifth argument */ + /* init_mount() requires a full page as fifth argument */ p = alloc_page(GFP_KERNEL); if (!p) return -ENOMEM; data_page = page_address(p); - /* zero-pad. do_mount() will make sure it's terminated */ + /* zero-pad. init_mount() will make sure it's terminated */ strncpy(data_page, data, PAGE_SIZE); } - ret = do_mount(name, "/root", fs, flags, data_page); + ret = init_mount(name, "/root", fs, flags, data_page); if (ret) goto out; @@ -628,7 +628,7 @@ void __init prepare_namespace(void) mount_root(); out: devtmpfs_mount(); - do_mount(".", "/", NULL, MS_MOVE, NULL); + init_mount(".", "/", NULL, MS_MOVE, NULL); ksys_chroot("."); } diff --git a/init/do_mounts.h b/init/do_mounts.h index 021e2f60223e..20e7fec8cb49 100644 --- a/init/do_mounts.h +++ b/init/do_mounts.h @@ -8,6 +8,7 @@ #include #include #include +#include void mount_block_root(char *name, int flags); void mount_root(void); diff --git a/init/do_mounts_initrd.c b/init/do_mounts_initrd.c index e08669187d63..1f9336209ad9 100644 --- a/init/do_mounts_initrd.c +++ b/init/do_mounts_initrd.c @@ -62,7 +62,7 @@ static int __init init_linuxrc(struct subprocess_info *info, struct cred *new) console_on_rootfs(); /* move initrd over / and chdir/chroot in initrd root */ ksys_chdir("/root"); - do_mount(".", "/", NULL, MS_MOVE, NULL); + init_mount(".", "/", NULL, MS_MOVE, NULL); ksys_chroot("."); ksys_setsid(); return 0; @@ -99,7 +99,7 @@ static void __init handle_initrd(void) current->flags &= ~PF_FREEZER_SKIP; /* move initrd to rootfs' /old */ - do_mount("..", ".", NULL, MS_MOVE, NULL); + init_mount("..", ".", NULL, MS_MOVE, NULL); /* switch root and cwd back to / of rootfs */ ksys_chroot(".."); @@ -113,7 +113,7 @@ static void __init handle_initrd(void) mount_root(); printk(KERN_NOTICE "Trying to move old root to /initrd ... "); - error = do_mount("/old", "/root/initrd", NULL, MS_MOVE, NULL); + error = init_mount("/old", "/root/initrd", NULL, MS_MOVE, NULL); if (!error) printk("okay\n"); else { -- cgit v1.2.3 From 09267defa36aaff6ff829bd2fc8b043ec151cc3e Mon Sep 17 00:00:00 2001 From: Christoph Hellwig Date: Thu, 23 Jul 2020 08:23:08 +0200 Subject: init: add an init_umount helper Like ksys_umount, but takes a kernel pointer for the destination path. Switch over the umount in the init code, which just happen to work due to the implicit set_fs(KERNEL_DS) during early init right now. Signed-off-by: Christoph Hellwig --- fs/init.c | 14 ++++++++++++++ fs/internal.h | 1 + fs/namespace.c | 4 ++-- include/linux/init_syscalls.h | 1 + include/linux/syscalls.h | 1 - init/do_mounts_initrd.c | 2 +- 6 files changed, 19 insertions(+), 4 deletions(-) (limited to 'fs/namespace.c') diff --git a/fs/init.c b/fs/init.c index c6eb724e1c7b..9c8e31fdb048 100644 --- a/fs/init.c +++ b/fs/init.c @@ -23,3 +23,17 @@ int __init init_mount(const char *dev_name, const char *dir_name, path_put(&path); return ret; } + +int __init init_umount(const char *name, int flags) +{ + int lookup_flags = LOOKUP_MOUNTPOINT; + struct path path; + int ret; + + if (!(flags & UMOUNT_NOFOLLOW)) + lookup_flags |= LOOKUP_FOLLOW; + ret = kern_path(name, lookup_flags, &path); + if (ret) + return ret; + return path_umount(&path, flags); +} diff --git a/fs/internal.h b/fs/internal.h index 72ea0b6f7435..491d1e63809b 100644 --- a/fs/internal.h +++ b/fs/internal.h @@ -92,6 +92,7 @@ extern void dissolve_on_fput(struct vfsmount *); int path_mount(const char *dev_name, struct path *path, const char *type_page, unsigned long flags, void *data_page); +int path_umount(struct path *path, int flags); /* * fs_struct.c diff --git a/fs/namespace.c b/fs/namespace.c index 2c4d75920974..a7301790abb2 100644 --- a/fs/namespace.c +++ b/fs/namespace.c @@ -1706,7 +1706,7 @@ static inline bool may_mandlock(void) } #endif -static int path_umount(struct path *path, int flags) +int path_umount(struct path *path, int flags) { struct mount *mnt; int retval; @@ -1736,7 +1736,7 @@ dput_and_out: return retval; } -int ksys_umount(char __user *name, int flags) +static int ksys_umount(char __user *name, int flags) { int lookup_flags = LOOKUP_MOUNTPOINT; struct path path; diff --git a/include/linux/init_syscalls.h b/include/linux/init_syscalls.h index af9ea88a60e0..a5a2e7f19916 100644 --- a/include/linux/init_syscalls.h +++ b/include/linux/init_syscalls.h @@ -2,3 +2,4 @@ int __init init_mount(const char *dev_name, const char *dir_name, const char *type_page, unsigned long flags, void *data_page); +int __init init_umount(const char *name, int flags); diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index e43816198e60..1a4f5d8ee704 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -1236,7 +1236,6 @@ asmlinkage long sys_ni_syscall(void); * the ksys_xyzyyz() functions prototyped below. */ -int ksys_umount(char __user *name, int flags); int ksys_chroot(const char __user *filename); ssize_t ksys_write(unsigned int fd, const char __user *buf, size_t count); int ksys_chdir(const char __user *filename); diff --git a/init/do_mounts_initrd.c b/init/do_mounts_initrd.c index 1f9336209ad9..6b020a069902 100644 --- a/init/do_mounts_initrd.c +++ b/init/do_mounts_initrd.c @@ -122,7 +122,7 @@ static void __init handle_initrd(void) else printk("failed\n"); printk(KERN_NOTICE "Unmounting old root\n"); - ksys_umount("/old", MNT_DETACH); + init_umount("/old", MNT_DETACH); } } -- cgit v1.2.3