All of lore.kernel.org
 help / color / mirror / Atom feed
From: Alan Jenkins <alan.christopher.jenkins@gmail.com>
To: David Howells <dhowells@redhat.com>, viro@zeniv.linux.org.uk
Cc: linux-api@vger.kernel.org, linux-fsdevel@vger.kernel.org,
	torvalds@linux-foundation.org, ebiederm@xmission.com,
	linux-security-module@vger.kernel.org
Subject: Re: [PATCH 02/10] vfs: syscall: Add move_mount(2) to move mounts around
Date: Wed, 20 Feb 2019 12:32:00 +0000	[thread overview]
Message-ID: <57a6b419-1541-6f97-6810-d0d376580def@gmail.com> (raw)
In-Reply-To: <155059611887.17079.12991580316407924257.stgit@warthog.procyon.org.uk>

On 19/02/2019 17:08, David Howells wrote:
> Add a move_mount() system call that will move a mount from one place to
> another and, in the next commit, allow to attach an unattached mount tree.
>
> The new system call looks like the following:
>
> 	int move_mount(int from_dfd, const char *from_path,
> 		       int to_dfd, const char *to_path,
> 		       unsigned int flags);
>
> Signed-off-by: David Howells <dhowells@redhat.com>
> cc: linux-api@vger.kernel.org
> Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
> ---
>
>   arch/x86/entry/syscalls/syscall_32.tbl |    1
>   arch/x86/entry/syscalls/syscall_64.tbl |    1
>   fs/namespace.c                         |  126 ++++++++++++++++++++++++--------
>   include/linux/lsm_hooks.h              |    6 ++
>   include/linux/security.h               |    7 ++
>   include/linux/syscalls.h               |    3 +
>   include/uapi/linux/mount.h             |   11 +++
>   security/security.c                    |    5 +
>   8 files changed, 129 insertions(+), 31 deletions(-)

> diff --git a/fs/namespace.c b/fs/namespace.c
> index 112d46f26fc3..f10122028a11 100644
> --- a/fs/namespace.c
> +++ b/fs/namespace.c
> @@ -2537,72 +2537,81 @@ static inline int tree_contains_unbindable(struct mount *mnt)
>   	return 0;
>   }
>   
> -static int do_move_mount(struct path *path, const char *old_name)
> +static int do_move_mount(struct path *old_path, struct path *new_path)
>   {
> -	struct path old_path, parent_path;
> +	struct path parent_path = {.mnt = NULL, .dentry = NULL};
>   	struct mount *p;
>   	struct mount *old;
>   	struct mountpoint *mp;
>   	int err;
> -	if (!old_name || !*old_name)
> -		return -EINVAL;
> -	err = kern_path(old_name, LOOKUP_FOLLOW, &old_path);
> -	if (err)
> -		return err;
>   
> -	mp = lock_mount(path);
> -	err = PTR_ERR(mp);
> +	mp = lock_mount(new_path);
>   	if (IS_ERR(mp))
> -		goto out;
> +		return PTR_ERR(mp);
>   
> -	old = real_mount(old_path.mnt);
> -	p = real_mount(path->mnt);
> +	old = real_mount(old_path->mnt);
> +	p = real_mount(new_path->mnt);
>   
>   	err = -EINVAL;
>   	if (!check_mnt(p) || !check_mnt(old))
> -		goto out1;
> +		goto out;
>   
> -	if (old->mnt.mnt_flags & MNT_LOCKED)
> -		goto out1;
> +	if (!mnt_has_parent(old))
> +		goto out;
>   
> -	err = -EINVAL;
> -	if (old_path.dentry != old_path.mnt->mnt_root)
> -		goto out1;
> +	if (old->mnt.mnt_flags & MNT_LOCKED)
> +		goto out;
>   
> -	if (!mnt_has_parent(old))
> -		goto out1;
> +	if (old_path->dentry != old_path->mnt->mnt_root)
> +		goto out;
>   
> -	if (d_is_dir(path->dentry) !=
> -	      d_is_dir(old_path.dentry))
> -		goto out1;
> +	if (d_is_dir(new_path->dentry) !=
> +	    d_is_dir(old_path->dentry))
> +		goto out;
>   	/*
>   	 * Don't move a mount residing in a shared parent.
>   	 */
>   	if (IS_MNT_SHARED(old->mnt_parent))
> -		goto out1;
> +		goto out;
>   	/*
>   	 * Don't move a mount tree containing unbindable mounts to a destination
>   	 * mount which is shared.
>   	 */
>   	if (IS_MNT_SHARED(p) && tree_contains_unbindable(old))
> -		goto out1;
> +		goto out;
>   	err = -ELOOP;
>   	for (; mnt_has_parent(p); p = p->mnt_parent)
>   		if (p == old)
> -			goto out1;
> +			goto out;
>   
> -	err = attach_recursive_mnt(old, real_mount(path->mnt), mp, &parent_path);
> +	err = attach_recursive_mnt(old, real_mount(new_path->mnt), mp,
> +				   &parent_path);
>   	if (err)
> -		goto out1;
> +		goto out;
>   
>   	/* if the mount is moved, it should no longer be expire
>   	 * automatically */
>   	list_del_init(&old->mnt_expire);
> -out1:
> -	unlock_mount(mp);
>   out:
> +	unlock_mount(mp);
>   	if (!err)
>   		path_put(&parent_path);
> +	return err;
> +}
> +
> +static int do_move_mount_old(struct path *path, const char *old_name)
> +{
> +	struct path old_path;
> +	int err;
> +
> +	if (!old_name || !*old_name)
> +		return -EINVAL;
> +
> +	err = kern_path(old_name, LOOKUP_FOLLOW, &old_path);
> +	if (err)
> +		return err;
> +
> +	err = do_move_mount(&old_path, path);
>   	path_put(&old_path);
>   	return err;
>   }
> @@ -3050,7 +3059,7 @@ long do_mount(const char *dev_name, const char __user *dir_name,
>   	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(&path, dev_name);
> +		retval = do_move_mount_old(&path, dev_name);
>   	else
>   		retval = do_new_mount(&path, type_page, sb_flags, mnt_flags,
>   				      dev_name, data_page);
> @@ -3278,6 +3287,61 @@ SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name,
>   	return ksys_mount(dev_name, dir_name, type, flags, data);
>   }
>   
> +/*
> + * Move a mount from one place to another.
> + *
> + * Note the flags value is a combination of MOVE_MOUNT_* flags.
> + */
> +SYSCALL_DEFINE5(move_mount,
> +		int, from_dfd, const char *, from_pathname,
> +		int, to_dfd, const char *, to_pathname,
> +		unsigned int, flags)
> +{
> +	struct path from_path, to_path;
> +	unsigned int lflags;
> +	int ret = 0;
> +
> +	if (!may_mount())
> +		return -EPERM;
> +
> +	if (flags & ~MOVE_MOUNT__MASK)
> +		return -EINVAL;
> +
> +	/* If someone gives a pathname, they aren't permitted to move
> +	 * from an fd that requires unmount as we can't get at the flag
> +	 * to clear it afterwards.
> +	 */

Comment is incorrect.

* FMODE_NEED_UNMOUNT is never cleared.

* Technically I don't see anything preventing them giving a pathname, 
but it needs to be "." or equivalent.  Otherwise it will fail the 
"!attached" check in the next patch.

* The only argument I remember for preventing this, was that it might 
confuse users (not the kernel).  If you are allowed to move from a 
sub-mount, then in certain programming styles - like my shell script 
test cases - you might accidentally close the original file too early.  
Then you won't be able to do move_mount() from the tree, because the 
tree was unmounted ("dissolved") when you closed it.

I think the description in the previous patch, for open_tree(), makes it 
clear though. "The detached tree will be dissolved on the final close of 
obtained file".

If there is a good reason, I expect we can simply remove the "!attached" 
part of the check.  If the constraint is generating more confusion than 
the added flexibility, I think that would be a good reason :-).

> +	lflags = 0;
> +	if (flags & MOVE_MOUNT_F_SYMLINKS)	lflags |= LOOKUP_FOLLOW;
> +	if (flags & MOVE_MOUNT_F_AUTOMOUNTS)	lflags |= LOOKUP_AUTOMOUNT;
> +	if (flags & MOVE_MOUNT_F_EMPTY_PATH)	lflags |= LOOKUP_EMPTY;
> +
> +	ret = user_path_at(from_dfd, from_pathname, lflags, &from_path);
> +	if (ret < 0)
> +		return ret;
> +
> +	lflags = 0;
> +	if (flags & MOVE_MOUNT_T_SYMLINKS)	lflags |= LOOKUP_FOLLOW;
> +	if (flags & MOVE_MOUNT_T_AUTOMOUNTS)	lflags |= LOOKUP_AUTOMOUNT;
> +	if (flags & MOVE_MOUNT_T_EMPTY_PATH)	lflags |= LOOKUP_EMPTY;
> +
> +	ret = user_path_at(to_dfd, to_pathname, lflags, &to_path);
> +	if (ret < 0)
> +		goto out_from;
> +
> +	ret = security_move_mount(&from_path, &to_path);
> +	if (ret < 0)
> +		goto out_to;
> +
> +	ret = do_move_mount(&from_path, &to_path);
> +
> +out_to:
> +	path_put(&to_path);
> +out_from:
> +	path_put(&from_path);
> +	return ret;
> +}
> +
>   /*
>    * Return true if path is reachable from root
>    *
>

  reply	other threads:[~2019-02-20 12:32 UTC|newest]

Thread overview: 33+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-02-19 17:08 [PATCH 00/10] VFS: Provide new mount UAPI David Howells
2019-02-19 17:08 ` [PATCH 01/10] vfs: syscall: Add open_tree(2) to reference or clone a mount David Howells
2019-02-19 17:08 ` [PATCH 02/10] vfs: syscall: Add move_mount(2) to move mounts around David Howells
2019-02-20 12:32   ` Alan Jenkins [this message]
2019-02-20 12:41     ` Alan Jenkins
2019-02-20 16:23   ` Jann Horn
2019-07-08 12:02   ` Tetsuo Handa
2019-07-08 13:18     ` Al Viro
2019-07-08 17:12       ` Eric W. Biederman
2019-07-08 18:01         ` Al Viro
2019-07-08 18:13           ` Al Viro
2019-07-08 20:21           ` Al Viro
2019-07-09  0:13             ` Eric W. Biederman
2019-07-09 10:51               ` Tetsuo Handa
2019-07-22 10:12                 ` Tetsuo Handa
2019-07-23  4:16                   ` John Johansen
2019-07-23 13:45                     ` Tetsuo Handa
2019-08-06 10:43                       ` Tetsuo Handa
2019-08-09 15:44                         ` [PATCH] LSM: Disable move_mount() syscall when TOMOYO or AppArmor is enabled Tetsuo Handa
2019-08-22  3:51                         ` [RFC][PATCH] fix d_absolute_path() interplay with fsmount() Al Viro
2019-08-30 10:11                           ` Tetsuo Handa
2019-07-23 21:45             ` [PATCH 02/10] vfs: syscall: Add move_mount(2) to move mounts around James Morris
2019-07-23 23:30               ` Al Viro
2019-02-19 17:08 ` [PATCH 03/10] teach move_mount(2) to work with OPEN_TREE_CLONE David Howells
2019-02-20 18:59   ` Alan Jenkins
2019-02-26 17:45   ` Alan Jenkins
2019-02-19 17:08 ` [PATCH 04/10] Make anon_inodes unconditional David Howells
2019-02-19 17:09 ` [PATCH 05/10] vfs: syscall: Add fsopen() to prepare for superblock creation David Howells
2019-02-19 17:09 ` [PATCH 06/10] vfs: Implement logging through fs_context David Howells
2019-02-19 17:09 ` [PATCH 07/10] vfs: syscall: Add fsconfig() for configuring and managing a context David Howells
2019-02-19 17:09 ` [PATCH 08/10] vfs: syscall: Add fsmount() to create a mount for a superblock David Howells
2019-02-19 17:09 ` [PATCH 09/10] vfs: syscall: Add fspick() to select a superblock for reconfiguration David Howells
2019-02-19 17:09 ` [PATCH 10/10] vfs: Add a sample program for the new mount API David Howells

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=57a6b419-1541-6f97-6810-d0d376580def@gmail.com \
    --to=alan.christopher.jenkins@gmail.com \
    --cc=dhowells@redhat.com \
    --cc=ebiederm@xmission.com \
    --cc=linux-api@vger.kernel.org \
    --cc=linux-fsdevel@vger.kernel.org \
    --cc=linux-security-module@vger.kernel.org \
    --cc=torvalds@linux-foundation.org \
    --cc=viro@zeniv.linux.org.uk \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.