diff options
Diffstat (limited to 'fs/fcntl.c')
| -rw-r--r-- | fs/fcntl.c | 272 | 
1 files changed, 103 insertions, 169 deletions
diff --git a/fs/fcntl.c b/fs/fcntl.c index ecc8b3954ed..72c82f69b01 100644 --- a/fs/fcntl.c +++ b/fs/fcntl.c @@ -20,134 +20,17 @@  #include <linux/signal.h>  #include <linux/rcupdate.h>  #include <linux/pid_namespace.h> +#include <linux/user_namespace.h>  #include <asm/poll.h>  #include <asm/siginfo.h>  #include <asm/uaccess.h> -void set_close_on_exec(unsigned int fd, int flag) -{ -	struct files_struct *files = current->files; -	struct fdtable *fdt; -	spin_lock(&files->file_lock); -	fdt = files_fdtable(files); -	if (flag) -		FD_SET(fd, fdt->close_on_exec); -	else -		FD_CLR(fd, fdt->close_on_exec); -	spin_unlock(&files->file_lock); -} - -static int get_close_on_exec(unsigned int fd) -{ -	struct files_struct *files = current->files; -	struct fdtable *fdt; -	int res; -	rcu_read_lock(); -	fdt = files_fdtable(files); -	res = FD_ISSET(fd, fdt->close_on_exec); -	rcu_read_unlock(); -	return res; -} - -SYSCALL_DEFINE3(dup3, unsigned int, oldfd, unsigned int, newfd, int, flags) -{ -	int err = -EBADF; -	struct file * file, *tofree; -	struct files_struct * files = current->files; -	struct fdtable *fdt; - -	if ((flags & ~O_CLOEXEC) != 0) -		return -EINVAL; - -	if (unlikely(oldfd == newfd)) -		return -EINVAL; - -	spin_lock(&files->file_lock); -	err = expand_files(files, newfd); -	file = fcheck(oldfd); -	if (unlikely(!file)) -		goto Ebadf; -	if (unlikely(err < 0)) { -		if (err == -EMFILE) -			goto Ebadf; -		goto out_unlock; -	} -	/* -	 * We need to detect attempts to do dup2() over allocated but still -	 * not finished descriptor.  NB: OpenBSD avoids that at the price of -	 * extra work in their equivalent of fget() - they insert struct -	 * file immediately after grabbing descriptor, mark it larval if -	 * more work (e.g. actual opening) is needed and make sure that -	 * fget() treats larval files as absent.  Potentially interesting, -	 * but while extra work in fget() is trivial, locking implications -	 * and amount of surgery on open()-related paths in VFS are not. -	 * FreeBSD fails with -EBADF in the same situation, NetBSD "solution" -	 * deadlocks in rather amusing ways, AFAICS.  All of that is out of -	 * scope of POSIX or SUS, since neither considers shared descriptor -	 * tables and this condition does not arise without those. -	 */ -	err = -EBUSY; -	fdt = files_fdtable(files); -	tofree = fdt->fd[newfd]; -	if (!tofree && FD_ISSET(newfd, fdt->open_fds)) -		goto out_unlock; -	get_file(file); -	rcu_assign_pointer(fdt->fd[newfd], file); -	FD_SET(newfd, fdt->open_fds); -	if (flags & O_CLOEXEC) -		FD_SET(newfd, fdt->close_on_exec); -	else -		FD_CLR(newfd, fdt->close_on_exec); -	spin_unlock(&files->file_lock); - -	if (tofree) -		filp_close(tofree, files); - -	return newfd; - -Ebadf: -	err = -EBADF; -out_unlock: -	spin_unlock(&files->file_lock); -	return err; -} - -SYSCALL_DEFINE2(dup2, unsigned int, oldfd, unsigned int, newfd) -{ -	if (unlikely(newfd == oldfd)) { /* corner case */ -		struct files_struct *files = current->files; -		int retval = oldfd; - -		rcu_read_lock(); -		if (!fcheck_files(files, oldfd)) -			retval = -EBADF; -		rcu_read_unlock(); -		return retval; -	} -	return sys_dup3(oldfd, newfd, 0); -} - -SYSCALL_DEFINE1(dup, unsigned int, fildes) -{ -	int ret = -EBADF; -	struct file *file = fget(fildes); - -	if (file) { -		ret = get_unused_fd(); -		if (ret >= 0) -			fd_install(ret, file); -		else -			fput(file); -	} -	return ret; -} -  #define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | O_DIRECT | O_NOATIME)  static int setfl(int fd, struct file * filp, unsigned long arg)  { -	struct inode * inode = filp->f_path.dentry->d_inode; +	struct inode * inode = file_inode(filp);  	int error = 0;  	/* @@ -159,7 +42,7 @@ static int setfl(int fd, struct file * filp, unsigned long arg)  	/* O_NOATIME can only be set by the owner or superuser */  	if ((arg & O_NOATIME) && !(filp->f_flags & O_NOATIME)) -		if (!is_owner_or_cap(inode)) +		if (!inode_owner_or_capable(inode))  			return -EPERM;  	/* required for strict SunOS emulation */ @@ -173,7 +56,7 @@ static int setfl(int fd, struct file * filp, unsigned long arg)  				return -EINVAL;  	} -	if (filp->f_op && filp->f_op->check_flags) +	if (filp->f_op->check_flags)  		error = filp->f_op->check_flags(arg);  	if (error)  		return error; @@ -181,8 +64,7 @@ static int setfl(int fd, struct file * filp, unsigned long arg)  	/*  	 * ->fasync() is responsible for setting the FASYNC bit.  	 */ -	if (((arg ^ filp->f_flags) & FASYNC) && filp->f_op && -			filp->f_op->fasync) { +	if (((arg ^ filp->f_flags) & FASYNC) && filp->f_op->fasync) {  		error = filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0);  		if (error < 0)  			goto out; @@ -266,7 +148,7 @@ pid_t f_getown(struct file *filp)  static int f_setown_ex(struct file *filp, unsigned long arg)  { -	struct f_owner_ex * __user owner_p = (void * __user)arg; +	struct f_owner_ex __user *owner_p = (void __user *)arg;  	struct f_owner_ex owner;  	struct pid *pid;  	int type; @@ -306,7 +188,7 @@ static int f_setown_ex(struct file *filp, unsigned long arg)  static int f_getown_ex(struct file *filp, unsigned long arg)  { -	struct f_owner_ex * __user owner_p = (void * __user)arg; +	struct f_owner_ex __user *owner_p = (void __user *)arg;  	struct f_owner_ex owner;  	int ret = 0; @@ -340,6 +222,31 @@ static int f_getown_ex(struct file *filp, unsigned long arg)  	return ret;  } +#ifdef CONFIG_CHECKPOINT_RESTORE +static int f_getowner_uids(struct file *filp, unsigned long arg) +{ +	struct user_namespace *user_ns = current_user_ns(); +	uid_t __user *dst = (void __user *)arg; +	uid_t src[2]; +	int err; + +	read_lock(&filp->f_owner.lock); +	src[0] = from_kuid(user_ns, filp->f_owner.uid); +	src[1] = from_kuid(user_ns, filp->f_owner.euid); +	read_unlock(&filp->f_owner.lock); + +	err  = put_user(src[0], &dst[0]); +	err |= put_user(src[1], &dst[1]); + +	return err; +} +#else +static int f_getowner_uids(struct file *filp, unsigned long arg) +{ +	return -EINVAL; +} +#endif +  static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,  		struct file *filp)  { @@ -347,14 +254,10 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,  	switch (cmd) {  	case F_DUPFD: +		err = f_dupfd(arg, filp, 0); +		break;  	case F_DUPFD_CLOEXEC: -		if (arg >= rlimit(RLIMIT_NOFILE)) -			break; -		err = alloc_fd(arg, cmd == F_DUPFD_CLOEXEC ? O_CLOEXEC : 0); -		if (err >= 0) { -			get_file(filp); -			fd_install(err, filp); -		} +		err = f_dupfd(arg, filp, O_CLOEXEC);  		break;  	case F_GETFD:  		err = get_close_on_exec(fd) ? FD_CLOEXEC : 0; @@ -369,9 +272,19 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,  	case F_SETFL:  		err = setfl(fd, filp, arg);  		break; +#if BITS_PER_LONG != 32 +	/* 32-bit arches must use fcntl64() */ +	case F_OFD_GETLK: +#endif  	case F_GETLK: -		err = fcntl_getlk(filp, (struct flock __user *) arg); +		err = fcntl_getlk(filp, cmd, (struct flock __user *) arg);  		break; +#if BITS_PER_LONG != 32 +	/* 32-bit arches must use fcntl64() */ +	case F_OFD_SETLK: +	case F_OFD_SETLKW: +#endif +		/* Fallthrough */  	case F_SETLK:  	case F_SETLKW:  		err = fcntl_setlk(fd, filp, cmd, (struct flock __user *) arg); @@ -396,6 +309,9 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,  	case F_SETOWN_EX:  		err = f_setown_ex(filp, arg);  		break; +	case F_GETOWNER_UIDS: +		err = f_getowner_uids(filp, arg); +		break;  	case F_GETSIG:  		err = filp->f_owner.signum;  		break; @@ -426,24 +342,38 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,  	return err;  } +static int check_fcntl_cmd(unsigned cmd) +{ +	switch (cmd) { +	case F_DUPFD: +	case F_DUPFD_CLOEXEC: +	case F_GETFD: +	case F_SETFD: +	case F_GETFL: +		return 1; +	} +	return 0; +} +  SYSCALL_DEFINE3(fcntl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)  {	 -	struct file *filp; +	struct fd f = fdget_raw(fd);  	long err = -EBADF; -	filp = fget(fd); -	if (!filp) +	if (!f.file)  		goto out; -	err = security_file_fcntl(filp, cmd, arg); -	if (err) { -		fput(filp); -		return err; +	if (unlikely(f.file->f_mode & FMODE_PATH)) { +		if (!check_fcntl_cmd(cmd)) +			goto out1;  	} -	err = do_fcntl(fd, cmd, arg, filp); +	err = security_file_fcntl(f.file, cmd, arg); +	if (!err) +		err = do_fcntl(fd, cmd, arg, f.file); - 	fput(filp); +out1: + 	fdput(f);  out:  	return err;  } @@ -452,35 +382,39 @@ out:  SYSCALL_DEFINE3(fcntl64, unsigned int, fd, unsigned int, cmd,  		unsigned long, arg)  {	 -	struct file * filp; -	long err; +	struct fd f = fdget_raw(fd); +	long err = -EBADF; -	err = -EBADF; -	filp = fget(fd); -	if (!filp) +	if (!f.file)  		goto out; -	err = security_file_fcntl(filp, cmd, arg); -	if (err) { -		fput(filp); -		return err; +	if (unlikely(f.file->f_mode & FMODE_PATH)) { +		if (!check_fcntl_cmd(cmd)) +			goto out1;  	} -	err = -EBADF; + +	err = security_file_fcntl(f.file, cmd, arg); +	if (err) +		goto out1;  	switch (cmd) { -		case F_GETLK64: -			err = fcntl_getlk64(filp, (struct flock64 __user *) arg); -			break; -		case F_SETLK64: -		case F_SETLKW64: -			err = fcntl_setlk64(fd, filp, cmd, -					(struct flock64 __user *) arg); -			break; -		default: -			err = do_fcntl(fd, cmd, arg, filp); -			break; +	case F_GETLK64: +	case F_OFD_GETLK: +		err = fcntl_getlk64(f.file, cmd, (struct flock64 __user *) arg); +		break; +	case F_SETLK64: +	case F_SETLKW64: +	case F_OFD_SETLK: +	case F_OFD_SETLKW: +		err = fcntl_setlk64(fd, f.file, cmd, +				(struct flock64 __user *) arg); +		break; +	default: +		err = do_fcntl(fd, cmd, arg, f.file); +		break;  	} -	fput(filp); +out1: +	fdput(f);  out:  	return err;  } @@ -505,9 +439,9 @@ static inline int sigio_perm(struct task_struct *p,  	rcu_read_lock();  	cred = __task_cred(p); -	ret = ((fown->euid == 0 || -		fown->euid == cred->suid || fown->euid == cred->uid || -		fown->uid  == cred->suid || fown->uid  == cred->uid) && +	ret = ((uid_eq(fown->euid, GLOBAL_ROOT_UID) || +		uid_eq(fown->euid, cred->suid) || uid_eq(fown->euid, cred->uid) || +		uid_eq(fown->uid,  cred->suid) || uid_eq(fown->uid,  cred->uid)) &&  	       !security_file_send_sigiotask(p, fown, sig));  	rcu_read_unlock();  	return ret; @@ -808,14 +742,14 @@ static int __init fcntl_init(void)  	 * Exceptions: O_NONBLOCK is a two bit define on parisc; O_NDELAY  	 * is defined as O_NONBLOCK on some platforms and not on others.  	 */ -	BUILD_BUG_ON(18 - 1 /* for O_RDONLY being 0 */ != HWEIGHT32( +	BUILD_BUG_ON(20 - 1 /* for O_RDONLY being 0 */ != HWEIGHT32(  		O_RDONLY	| O_WRONLY	| O_RDWR	|  		O_CREAT		| O_EXCL	| O_NOCTTY	|  		O_TRUNC		| O_APPEND	| /* O_NONBLOCK	| */  		__O_SYNC	| O_DSYNC	| FASYNC	|  		O_DIRECT	| O_LARGEFILE	| O_DIRECTORY	|  		O_NOFOLLOW	| O_NOATIME	| O_CLOEXEC	| -		FMODE_EXEC +		__FMODE_EXEC	| O_PATH	| __O_TMPFILE  		));  	fasync_cache = kmem_cache_create("fasync_cache",  | 
