1183 lines · 26.5 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0/* *  linux/fs/fcntl.c * *  Copyright (C) 1991, 1992  Linus Torvalds */ #include <linux/syscalls.h>#include <linux/init.h>#include <linux/mm.h>#include <linux/sched/task.h>#include <linux/fs.h>#include <linux/filelock.h>#include <linux/file.h>#include <linux/capability.h>#include <linux/dnotify.h>#include <linux/slab.h>#include <linux/module.h>#include <linux/pipe_fs_i.h>#include <linux/security.h>#include <linux/ptrace.h>#include <linux/signal.h>#include <linux/rcupdate.h>#include <linux/pid_namespace.h>#include <linux/user_namespace.h>#include <linux/memfd.h>#include <linux/compat.h>#include <linux/mount.h>#include <linux/rw_hint.h> #include <linux/poll.h>#include <asm/siginfo.h>#include <linux/uaccess.h> #include "internal.h" #define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | O_DIRECT | O_NOATIME) static int setfl(int fd, struct file * filp, unsigned int arg){	struct inode * inode = file_inode(filp);	int error = 0; 	/*	 * O_APPEND cannot be cleared if the file is marked as append-only	 * and the file is open for write.	 */	if (((arg ^ filp->f_flags) & O_APPEND) && IS_APPEND(inode))		return -EPERM; 	/* O_NOATIME can only be set by the owner or superuser */	if ((arg & O_NOATIME) && !(filp->f_flags & O_NOATIME))		if (!inode_owner_or_capable(file_mnt_idmap(filp), inode))			return -EPERM; 	/* required for strict SunOS emulation */	if (O_NONBLOCK != O_NDELAY)	       if (arg & O_NDELAY)		   arg |= O_NONBLOCK; 	/* Pipe packetized mode is controlled by O_DIRECT flag */	if (!S_ISFIFO(inode->i_mode) &&	    (arg & O_DIRECT) &&	    !(filp->f_mode & FMODE_CAN_ODIRECT))		return -EINVAL; 	if (filp->f_op->check_flags)		error = filp->f_op->check_flags(arg);	if (error)		return error; 	/*	 * ->fasync() is responsible for setting the FASYNC bit.	 */	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;		if (error > 0)			error = 0;	}	spin_lock(&filp->f_lock);	filp->f_flags = (arg & SETFL_MASK) | (filp->f_flags & ~SETFL_MASK);	filp->f_iocb_flags = iocb_flags(filp);	spin_unlock(&filp->f_lock);  out:	return error;} /* * Allocate an file->f_owner struct if it doesn't exist, handling racing * allocations correctly. */int file_f_owner_allocate(struct file *file){	struct fown_struct *f_owner; 	f_owner = file_f_owner(file);	if (f_owner)		return 0; 	f_owner = kzalloc_obj(struct fown_struct);	if (!f_owner)		return -ENOMEM; 	rwlock_init(&f_owner->lock);	f_owner->file = file;	/* If someone else raced us, drop our allocation. */	if (unlikely(cmpxchg(&file->f_owner, NULL, f_owner)))		kfree(f_owner);	return 0;}EXPORT_SYMBOL(file_f_owner_allocate); void file_f_owner_release(struct file *file){	struct fown_struct *f_owner; 	f_owner = file_f_owner(file);	if (f_owner) {		put_pid(f_owner->pid);		kfree(f_owner);	}} void __f_setown(struct file *filp, struct pid *pid, enum pid_type type,		int force){	struct fown_struct *f_owner; 	f_owner = file_f_owner(filp);	if (WARN_ON_ONCE(!f_owner))		return; 	write_lock_irq(&f_owner->lock);	if (force || !f_owner->pid) {		put_pid(f_owner->pid);		f_owner->pid = get_pid(pid);		f_owner->pid_type = type; 		if (pid) {			const struct cred *cred = current_cred();			security_file_set_fowner(filp);			f_owner->uid = cred->uid;			f_owner->euid = cred->euid;		}	}	write_unlock_irq(&f_owner->lock);}EXPORT_SYMBOL(__f_setown); int f_setown(struct file *filp, int who, int force){	enum pid_type type;	struct pid *pid = NULL;	int ret = 0; 	might_sleep(); 	type = PIDTYPE_TGID;	if (who < 0) {		/* avoid overflow below */		if (who == INT_MIN)			return -EINVAL; 		type = PIDTYPE_PGID;		who = -who;	} 	ret = file_f_owner_allocate(filp);	if (ret)		return ret; 	rcu_read_lock();	if (who) {		pid = find_vpid(who);		if (!pid)			ret = -ESRCH;	} 	if (!ret)		__f_setown(filp, pid, type, force);	rcu_read_unlock(); 	return ret;}EXPORT_SYMBOL(f_setown); void f_delown(struct file *filp){	__f_setown(filp, NULL, PIDTYPE_TGID, 1);} pid_t f_getown(struct file *filp){	pid_t pid = 0;	struct fown_struct *f_owner; 	f_owner = file_f_owner(filp);	if (!f_owner)		return pid; 	read_lock_irq(&f_owner->lock);	rcu_read_lock();	if (pid_task(f_owner->pid, f_owner->pid_type)) {		pid = pid_vnr(f_owner->pid);		if (f_owner->pid_type == PIDTYPE_PGID)			pid = -pid;	}	rcu_read_unlock();	read_unlock_irq(&f_owner->lock);	return pid;} 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 owner;	struct pid *pid;	int type;	int ret; 	ret = copy_from_user(&owner, owner_p, sizeof(owner));	if (ret)		return -EFAULT; 	switch (owner.type) {	case F_OWNER_TID:		type = PIDTYPE_PID;		break; 	case F_OWNER_PID:		type = PIDTYPE_TGID;		break; 	case F_OWNER_PGRP:		type = PIDTYPE_PGID;		break; 	default:		return -EINVAL;	} 	ret = file_f_owner_allocate(filp);	if (ret)		return ret; 	rcu_read_lock();	pid = find_vpid(owner.pid);	if (owner.pid && !pid)		ret = -ESRCH;	else		 __f_setown(filp, pid, type, 1);	rcu_read_unlock(); 	return ret;} 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 owner = {};	int ret = 0;	struct fown_struct *f_owner;	enum pid_type pid_type = PIDTYPE_PID; 	f_owner = file_f_owner(filp);	if (f_owner) {		read_lock_irq(&f_owner->lock);		rcu_read_lock();		if (pid_task(f_owner->pid, f_owner->pid_type))			owner.pid = pid_vnr(f_owner->pid);		rcu_read_unlock();		pid_type = f_owner->pid_type;	} 	switch (pid_type) {	case PIDTYPE_PID:		owner.type = F_OWNER_TID;		break; 	case PIDTYPE_TGID:		owner.type = F_OWNER_PID;		break; 	case PIDTYPE_PGID:		owner.type = F_OWNER_PGRP;		break; 	default:		WARN_ON(1);		ret = -EINVAL;		break;	}	if (f_owner)		read_unlock_irq(&f_owner->lock); 	if (!ret) {		ret = copy_to_user(owner_p, &owner, sizeof(owner));		if (ret)			ret = -EFAULT;	}	return ret;} #ifdef CONFIG_CHECKPOINT_RESTOREstatic int f_getowner_uids(struct file *filp, unsigned long arg){	struct user_namespace *user_ns = current_user_ns();	struct fown_struct *f_owner;	uid_t __user *dst = (void __user *)arg;	uid_t src[2] = {0, 0};	int err; 	f_owner = file_f_owner(filp);	if (f_owner) {		read_lock_irq(&f_owner->lock);		src[0] = from_kuid(user_ns, f_owner->uid);		src[1] = from_kuid(user_ns, f_owner->euid);		read_unlock_irq(&f_owner->lock);	} 	err  = put_user(src[0], &dst[0]);	err |= put_user(src[1], &dst[1]); 	return err;}#elsestatic int f_getowner_uids(struct file *filp, unsigned long arg){	return -EINVAL;}#endif static bool rw_hint_valid(u64 hint){	BUILD_BUG_ON(WRITE_LIFE_NOT_SET != RWH_WRITE_LIFE_NOT_SET);	BUILD_BUG_ON(WRITE_LIFE_NONE != RWH_WRITE_LIFE_NONE);	BUILD_BUG_ON(WRITE_LIFE_SHORT != RWH_WRITE_LIFE_SHORT);	BUILD_BUG_ON(WRITE_LIFE_MEDIUM != RWH_WRITE_LIFE_MEDIUM);	BUILD_BUG_ON(WRITE_LIFE_LONG != RWH_WRITE_LIFE_LONG);	BUILD_BUG_ON(WRITE_LIFE_EXTREME != RWH_WRITE_LIFE_EXTREME); 	switch (hint) {	case RWH_WRITE_LIFE_NOT_SET:	case RWH_WRITE_LIFE_NONE:	case RWH_WRITE_LIFE_SHORT:	case RWH_WRITE_LIFE_MEDIUM:	case RWH_WRITE_LIFE_LONG:	case RWH_WRITE_LIFE_EXTREME:		return true;	default:		return false;	}} static long fcntl_get_rw_hint(struct file *file, unsigned long arg){	struct inode *inode = file_inode(file);	u64 __user *argp = (u64 __user *)arg;	u64 hint = READ_ONCE(inode->i_write_hint); 	if (copy_to_user(argp, &hint, sizeof(*argp)))		return -EFAULT;	return 0;} static long fcntl_set_rw_hint(struct file *file, unsigned long arg){	struct inode *inode = file_inode(file);	u64 __user *argp = (u64 __user *)arg;	u64 hint; 	if (!inode_owner_or_capable(file_mnt_idmap(file), inode))		return -EPERM; 	if (copy_from_user(&hint, argp, sizeof(hint)))		return -EFAULT;	if (!rw_hint_valid(hint))		return -EINVAL; 	WRITE_ONCE(inode->i_write_hint, hint); 	/*	 * file->f_mapping->host may differ from inode. As an example,	 * blkdev_open() modifies file->f_mapping.	 */	if (file->f_mapping->host != inode)		WRITE_ONCE(file->f_mapping->host->i_write_hint, hint); 	return 0;} /* Is the file descriptor a dup of the file? */static long f_dupfd_query(int fd, struct file *filp){	CLASS(fd_raw, f)(fd); 	if (fd_empty(f))		return -EBADF; 	/*	 * We can do the 'fdput()' immediately, as the only thing that	 * matters is the pointer value which isn't changed by the fdput.	 *	 * Technically we didn't need a ref at all, and 'fdget()' was	 * overkill, but given our lockless file pointer lookup, the	 * alternatives are complicated.	 */	return fd_file(f) == filp;} /* Let the caller figure out whether a given file was just created. */static long f_created_query(const struct file *filp){	return !!(filp->f_mode & FMODE_CREATED);} static int f_owner_sig(struct file *filp, int signum, bool setsig){	int ret = 0;	struct fown_struct *f_owner; 	might_sleep(); 	if (setsig) {		if (!valid_signal(signum))			return -EINVAL; 		ret = file_f_owner_allocate(filp);		if (ret)			return ret;	} 	f_owner = file_f_owner(filp);	if (setsig)		f_owner->signum = signum;	else if (f_owner)		ret = f_owner->signum;	return ret;} static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,		struct file *filp){	void __user *argp = (void __user *)arg;	struct delegation deleg;	int argi = (int)arg;	struct flock flock;	long err = -EINVAL; 	switch (cmd) {	case F_CREATED_QUERY:		err = f_created_query(filp);		break;	case F_DUPFD:		err = f_dupfd(argi, filp, 0);		break;	case F_DUPFD_CLOEXEC:		err = f_dupfd(argi, filp, O_CLOEXEC);		break;	case F_DUPFD_QUERY:		err = f_dupfd_query(argi, filp);		break;	case F_GETFD:		err = get_close_on_exec(fd) ? FD_CLOEXEC : 0;		break;	case F_SETFD:		err = 0;		set_close_on_exec(fd, argi & FD_CLOEXEC);		break;	case F_GETFL:		err = filp->f_flags;		break;	case F_SETFL:		err = setfl(fd, filp, argi);		break;#if BITS_PER_LONG != 32	/* 32-bit arches must use fcntl64() */	case F_OFD_GETLK:#endif	case F_GETLK:		if (copy_from_user(&flock, argp, sizeof(flock)))			return -EFAULT;		err = fcntl_getlk(filp, cmd, &flock);		if (!err && copy_to_user(argp, &flock, sizeof(flock)))			return -EFAULT;		break;#if BITS_PER_LONG != 32	/* 32-bit arches must use fcntl64() */	case F_OFD_SETLK:	case F_OFD_SETLKW:		fallthrough;#endif	case F_SETLK:	case F_SETLKW:		if (copy_from_user(&flock, argp, sizeof(flock)))			return -EFAULT;		err = fcntl_setlk(fd, filp, cmd, &flock);

Lines 1–500 of 1183.