forked from mirrors/linux
		
	vfs: syscall: Add fsmount() to create a mount for a superblock
Provide a system call by which a filesystem opened with fsopen() and configured by a series of fsconfig() calls can have a detached mount object created for it. This mount object can then be attached to the VFS mount hierarchy using move_mount() by passing the returned file descriptor as the from directory fd. The system call looks like: int mfd = fsmount(int fsfd, unsigned int flags, unsigned int attr_flags); where fsfd is the file descriptor returned by fsopen(). flags can be 0 or FSMOUNT_CLOEXEC. attr_flags is a bitwise-OR of the following flags: MOUNT_ATTR_RDONLY Mount read-only MOUNT_ATTR_NOSUID Ignore suid and sgid bits MOUNT_ATTR_NODEV Disallow access to device special files MOUNT_ATTR_NOEXEC Disallow program execution MOUNT_ATTR__ATIME Setting on how atime should be updated MOUNT_ATTR_RELATIME - Update atime relative to mtime/ctime MOUNT_ATTR_NOATIME - Do not update access times MOUNT_ATTR_STRICTATIME - Always perform atime updates MOUNT_ATTR_NODIRATIME Do not update directory access times In the event that fsmount() fails, it may be possible to get an error message by calling read() on fsfd. If no message is available, ENODATA will be reported. Signed-off-by: David Howells <dhowells@redhat.com> cc: linux-api@vger.kernel.org Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
This commit is contained in:
		
							parent
							
								
									ecdab150fd
								
							
						
					
					
						commit
						93766fbd26
					
				
					 5 changed files with 165 additions and 4 deletions
				
			
		|  | @ -402,7 +402,8 @@ | |||
| 388	i386	move_mount		sys_move_mount			__ia32_sys_move_mount | ||||
| 389	i386	fsopen			sys_fsopen			__ia32_sys_fsopen | ||||
| 390	i386	fsconfig		sys_fsconfig			__ia32_sys_fsconfig | ||||
| # don't use numbers 391 through 392, add new calls at the end | ||||
| 391	i386	fsmount			sys_fsmount			__ia32_sys_fsmount | ||||
| # don't use number 392, add new calls at the end | ||||
| 393	i386	semget			sys_semget    			__ia32_sys_semget | ||||
| 394	i386	semctl			sys_semctl    			__ia32_compat_sys_semctl | ||||
| 395	i386	shmget			sys_shmget    			__ia32_sys_shmget | ||||
|  |  | |||
|  | @ -347,6 +347,7 @@ | |||
| 336	common	move_mount		__x64_sys_move_mount | ||||
| 337	common	fsopen			__x64_sys_fsopen | ||||
| 338	common	fsconfig		__x64_sys_fsconfig | ||||
| 339	common	fsmount			__x64_sys_fsmount | ||||
| # don't use numbers 387 through 423, add new calls after the last | ||||
| # 'common' entry | ||||
| 424	common	pidfd_send_signal	__x64_sys_pidfd_send_signal | ||||
|  |  | |||
							
								
								
									
										146
									
								
								fs/namespace.c
									
									
									
									
									
								
							
							
						
						
									
										146
									
								
								fs/namespace.c
									
									
									
									
									
								
							|  | @ -3334,9 +3334,149 @@ SYSCALL_DEFINE5(mount, char __user *, dev_name, char __user *, dir_name, | |||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Move a mount from one place to another. | ||||
|  * In combination with open_tree(OPEN_TREE_CLONE [| AT_RECURSIVE]) it can be | ||||
|  * used to copy a mount subtree. | ||||
|  * Create a kernel mount representation for a new, prepared superblock | ||||
|  * (specified by fs_fd) and attach to an open_tree-like file descriptor. | ||||
|  */ | ||||
| SYSCALL_DEFINE3(fsmount, int, fs_fd, unsigned int, flags, | ||||
| 		unsigned int, attr_flags) | ||||
| { | ||||
| 	struct mnt_namespace *ns; | ||||
| 	struct fs_context *fc; | ||||
| 	struct file *file; | ||||
| 	struct path newmount; | ||||
| 	struct mount *mnt; | ||||
| 	struct fd f; | ||||
| 	unsigned int mnt_flags = 0; | ||||
| 	long ret; | ||||
| 
 | ||||
| 	if (!may_mount()) | ||||
| 		return -EPERM; | ||||
| 
 | ||||
| 	if ((flags & ~(FSMOUNT_CLOEXEC)) != 0) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (attr_flags & ~(MOUNT_ATTR_RDONLY | | ||||
| 			   MOUNT_ATTR_NOSUID | | ||||
| 			   MOUNT_ATTR_NODEV | | ||||
| 			   MOUNT_ATTR_NOEXEC | | ||||
| 			   MOUNT_ATTR__ATIME | | ||||
| 			   MOUNT_ATTR_NODIRATIME)) | ||||
| 		return -EINVAL; | ||||
| 
 | ||||
| 	if (attr_flags & MOUNT_ATTR_RDONLY) | ||||
| 		mnt_flags |= MNT_READONLY; | ||||
| 	if (attr_flags & MOUNT_ATTR_NOSUID) | ||||
| 		mnt_flags |= MNT_NOSUID; | ||||
| 	if (attr_flags & MOUNT_ATTR_NODEV) | ||||
| 		mnt_flags |= MNT_NODEV; | ||||
| 	if (attr_flags & MOUNT_ATTR_NOEXEC) | ||||
| 		mnt_flags |= MNT_NOEXEC; | ||||
| 	if (attr_flags & MOUNT_ATTR_NODIRATIME) | ||||
| 		mnt_flags |= MNT_NODIRATIME; | ||||
| 
 | ||||
| 	switch (attr_flags & MOUNT_ATTR__ATIME) { | ||||
| 	case MOUNT_ATTR_STRICTATIME: | ||||
| 		break; | ||||
| 	case MOUNT_ATTR_NOATIME: | ||||
| 		mnt_flags |= MNT_NOATIME; | ||||
| 		break; | ||||
| 	case MOUNT_ATTR_RELATIME: | ||||
| 		mnt_flags |= MNT_RELATIME; | ||||
| 		break; | ||||
| 	default: | ||||
| 		return -EINVAL; | ||||
| 	} | ||||
| 
 | ||||
| 	f = fdget(fs_fd); | ||||
| 	if (!f.file) | ||||
| 		return -EBADF; | ||||
| 
 | ||||
| 	ret = -EINVAL; | ||||
| 	if (f.file->f_op != &fscontext_fops) | ||||
| 		goto err_fsfd; | ||||
| 
 | ||||
| 	fc = f.file->private_data; | ||||
| 
 | ||||
| 	ret = mutex_lock_interruptible(&fc->uapi_mutex); | ||||
| 	if (ret < 0) | ||||
| 		goto err_fsfd; | ||||
| 
 | ||||
| 	/* There must be a valid superblock or we can't mount it */ | ||||
| 	ret = -EINVAL; | ||||
| 	if (!fc->root) | ||||
| 		goto err_unlock; | ||||
| 
 | ||||
| 	ret = -EPERM; | ||||
| 	if (mount_too_revealing(fc->root->d_sb, &mnt_flags)) { | ||||
| 		pr_warn("VFS: Mount too revealing\n"); | ||||
| 		goto err_unlock; | ||||
| 	} | ||||
| 
 | ||||
| 	ret = -EBUSY; | ||||
| 	if (fc->phase != FS_CONTEXT_AWAITING_MOUNT) | ||||
| 		goto err_unlock; | ||||
| 
 | ||||
| 	ret = -EPERM; | ||||
| 	if ((fc->sb_flags & SB_MANDLOCK) && !may_mandlock()) | ||||
| 		goto err_unlock; | ||||
| 
 | ||||
| 	newmount.mnt = vfs_create_mount(fc); | ||||
| 	if (IS_ERR(newmount.mnt)) { | ||||
| 		ret = PTR_ERR(newmount.mnt); | ||||
| 		goto err_unlock; | ||||
| 	} | ||||
| 	newmount.dentry = dget(fc->root); | ||||
| 	newmount.mnt->mnt_flags = mnt_flags; | ||||
| 
 | ||||
| 	/* We've done the mount bit - now move the file context into more or
 | ||||
| 	 * less the same state as if we'd done an fspick().  We don't want to | ||||
| 	 * do any memory allocation or anything like that at this point as we | ||||
| 	 * don't want to have to handle any errors incurred. | ||||
| 	 */ | ||||
| 	vfs_clean_context(fc); | ||||
| 
 | ||||
| 	ns = alloc_mnt_ns(current->nsproxy->mnt_ns->user_ns, true); | ||||
| 	if (IS_ERR(ns)) { | ||||
| 		ret = PTR_ERR(ns); | ||||
| 		goto err_path; | ||||
| 	} | ||||
| 	mnt = real_mount(newmount.mnt); | ||||
| 	mnt->mnt_ns = ns; | ||||
| 	ns->root = mnt; | ||||
| 	ns->mounts = 1; | ||||
| 	list_add(&mnt->mnt_list, &ns->list); | ||||
| 
 | ||||
| 	/* Attach to an apparent O_PATH fd with a note that we need to unmount
 | ||||
| 	 * it, not just simply put it. | ||||
| 	 */ | ||||
| 	file = dentry_open(&newmount, O_PATH, fc->cred); | ||||
| 	if (IS_ERR(file)) { | ||||
| 		dissolve_on_fput(newmount.mnt); | ||||
| 		ret = PTR_ERR(file); | ||||
| 		goto err_path; | ||||
| 	} | ||||
| 	file->f_mode |= FMODE_NEED_UNMOUNT; | ||||
| 
 | ||||
| 	ret = get_unused_fd_flags((flags & FSMOUNT_CLOEXEC) ? O_CLOEXEC : 0); | ||||
| 	if (ret >= 0) | ||||
| 		fd_install(ret, file); | ||||
| 	else | ||||
| 		fput(file); | ||||
| 
 | ||||
| err_path: | ||||
| 	path_put(&newmount); | ||||
| err_unlock: | ||||
| 	mutex_unlock(&fc->uapi_mutex); | ||||
| err_fsfd: | ||||
| 	fdput(f); | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Move a mount from one place to another.  In combination with | ||||
|  * fsopen()/fsmount() this is used to install a new mount and in combination | ||||
|  * with open_tree(OPEN_TREE_CLONE [| AT_RECURSIVE]) it can be used to copy | ||||
|  * a mount subtree. | ||||
|  * | ||||
|  * Note the flags value is a combination of MOVE_MOUNT_* flags. | ||||
|  */ | ||||
|  |  | |||
|  | @ -992,6 +992,7 @@ asmlinkage long sys_move_mount(int from_dfd, const char __user *from_path, | |||
| asmlinkage long sys_fsopen(const char __user *fs_name, unsigned int flags); | ||||
| asmlinkage long sys_fsconfig(int fs_fd, unsigned int cmd, const char __user *key, | ||||
| 			     const void __user *value, int aux); | ||||
| asmlinkage long sys_fsmount(int fs_fd, unsigned int flags, unsigned int ms_flags); | ||||
| asmlinkage long sys_pidfd_send_signal(int pidfd, int sig, | ||||
| 				       siginfo_t __user *info, | ||||
| 				       unsigned int flags); | ||||
|  |  | |||
|  | @ -91,4 +91,22 @@ enum fsconfig_command { | |||
| 	FSCONFIG_CMD_RECONFIGURE = 7,	/* Invoke superblock reconfiguration */ | ||||
| }; | ||||
| 
 | ||||
| /*
 | ||||
|  * fsmount() flags. | ||||
|  */ | ||||
| #define FSMOUNT_CLOEXEC		0x00000001 | ||||
| 
 | ||||
| /*
 | ||||
|  * Mount attributes. | ||||
|  */ | ||||
| #define MOUNT_ATTR_RDONLY	0x00000001 /* Mount read-only */ | ||||
| #define MOUNT_ATTR_NOSUID	0x00000002 /* Ignore suid and sgid bits */ | ||||
| #define MOUNT_ATTR_NODEV	0x00000004 /* Disallow access to device special files */ | ||||
| #define MOUNT_ATTR_NOEXEC	0x00000008 /* Disallow program execution */ | ||||
| #define MOUNT_ATTR__ATIME	0x00000070 /* Setting on how atime should be updated */ | ||||
| #define MOUNT_ATTR_RELATIME	0x00000000 /* - Update atime relative to mtime/ctime. */ | ||||
| #define MOUNT_ATTR_NOATIME	0x00000010 /* - Do not update access times. */ | ||||
| #define MOUNT_ATTR_STRICTATIME	0x00000020 /* - Always perform atime updates */ | ||||
| #define MOUNT_ATTR_NODIRATIME	0x00000080 /* Do not update directory access times */ | ||||
| 
 | ||||
| #endif /* _UAPI_LINUX_MOUNT_H */ | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 David Howells
						David Howells