aboutsummaryrefslogtreecommitdiff
path: root/fs/fcntl.c
diff options
context:
space:
mode:
Diffstat (limited to 'fs/fcntl.c')
-rw-r--r--fs/fcntl.c272
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",