[PATCH 02/24] fs: add a do_kern_mount helper

From: Christoph Hellwig
Date: Mon Jul 20 2020 - 12:29:56 EST


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 <hch@xxxxxx>
---
drivers/base/devtmpfs.c | 4 +-
fs/namespace.c | 81 +++++++++++++++++++++++++----------------
include/linux/fs.h | 2 +
init/do_mounts.c | 8 ++--
init/do_mounts_initrd.c | 6 +--
5 files changed, 60 insertions(+), 41 deletions(-)

diff --git a/drivers/base/devtmpfs.c b/drivers/base/devtmpfs.c
index c9017e0584c003..03221ca708c91c 100644
--- a/drivers/base/devtmpfs.c
+++ b/drivers/base/devtmpfs.c
@@ -359,7 +359,7 @@ int __init devtmpfs_mount(void)
if (!thread)
return 0;

- err = do_mount("devtmpfs", "dev", "devtmpfs", MS_SILENT, NULL);
+ err = do_kern_mount("devtmpfs", "dev", "devtmpfs", MS_SILENT, NULL);
if (err)
printk(KERN_INFO "devtmpfs: error mounting %i\n", err);
else
@@ -385,7 +385,7 @@ static int devtmpfs_setup(void *p)
err = ksys_unshare(CLONE_NEWNS);
if (err)
goto out;
- err = do_mount("devtmpfs", "/", "devtmpfs", MS_SILENT, NULL);
+ err = do_kern_mount("devtmpfs", "/", "devtmpfs", MS_SILENT, NULL);
if (err)
goto out;
ksys_chdir("/.."); /* will traverse into overmounted root */
diff --git a/fs/namespace.c b/fs/namespace.c
index f30ed401cc6d7a..d208a389aac3c0 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 = 0;

/* 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,46 @@ 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;
+}
+
+int do_kern_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;
}

static struct ucounts *inc_mnt_namespaces(struct user_namespace *ns)
diff --git a/include/linux/fs.h b/include/linux/fs.h
index a1d2685a487868..8f628f9868711d 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -2337,6 +2337,8 @@ extern int may_umount_tree(struct vfsmount *);
extern int may_umount(struct vfsmount *);
extern long do_mount(const char *, const char __user *,
const char *, unsigned long, void *);
+int do_kern_mount(const char *dev_name, const char *dir_name,
+ const char *type_page, unsigned long flags, void *data_page);
extern struct vfsmount *collect_mounts(const struct path *);
extern void drop_collected_mounts(struct vfsmount *);
extern int iterate_mounts(int (*)(struct vfsmount *, void *), void *,
diff --git a/init/do_mounts.c b/init/do_mounts.c
index 4f4ceb35805503..2fef92a8ed3c15 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 */
+ /* do_kern_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. do_kern_mount() will make sure it's terminated */
strncpy(data_page, data, PAGE_SIZE);
}

- ret = do_mount(name, "/root", fs, flags, data_page);
+ ret = do_kern_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);
+ do_kern_mount(".", "/", NULL, MS_MOVE, NULL);
ksys_chroot(".");
}

diff --git a/init/do_mounts_initrd.c b/init/do_mounts_initrd.c
index e08669187d63be..604ce78af9acfa 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);
+ do_kern_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);
+ do_kern_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 = do_kern_mount("/old", "/root/initrd", NULL, MS_MOVE, NULL);
if (!error)
printk("okay\n");
else {
--
2.27.0