diff options
author | Ralf Baechle <ralf@linux-mips.org> | 1994-11-28 11:59:19 +0000 |
---|---|---|
committer | <ralf@linux-mips.org> | 1994-11-28 11:59:19 +0000 |
commit | 1513ff9b7899ab588401c89db0e99903dbf5f886 (patch) | |
tree | f69cc81a940a502ea23d664c3ffb2d215a479667 /fs/fcntl.c |
Import of Linus's Linux 1.1.68
Diffstat (limited to 'fs/fcntl.c')
-rw-r--r-- | fs/fcntl.c | 188 |
1 files changed, 188 insertions, 0 deletions
diff --git a/fs/fcntl.c b/fs/fcntl.c new file mode 100644 index 000000000..d3226eb01 --- /dev/null +++ b/fs/fcntl.c @@ -0,0 +1,188 @@ +/* + * linux/fs/fcntl.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + */ + +#include <asm/segment.h> + +#include <linux/sched.h> +#include <linux/kernel.h> +#include <linux/errno.h> +#include <linux/stat.h> +#include <linux/fcntl.h> +#include <linux/string.h> + +extern int fcntl_getlk(unsigned int, struct flock *); +extern int fcntl_setlk(unsigned int, unsigned int, struct flock *); +extern int sock_fcntl (struct file *, unsigned int cmd, unsigned long arg); + +static int dupfd(unsigned int fd, unsigned int arg) +{ + if (fd >= NR_OPEN || !current->files->fd[fd]) + return -EBADF; + if (arg >= NR_OPEN) + return -EINVAL; + while (arg < NR_OPEN) + if (current->files->fd[arg]) + arg++; + else + break; + if (arg >= NR_OPEN) + return -EMFILE; + FD_CLR(arg, ¤t->files->close_on_exec); + (current->files->fd[arg] = current->files->fd[fd])->f_count++; + return arg; +} + +asmlinkage int sys_dup2(unsigned int oldfd, unsigned int newfd) +{ + if (oldfd >= NR_OPEN || !current->files->fd[oldfd]) + return -EBADF; + if (newfd == oldfd) + return newfd; + /* + * errno's for dup2() are slightly different than for fcntl(F_DUPFD) + * for historical reasons. + */ + if (newfd > NR_OPEN) /* historical botch - should have been >= */ + return -EBADF; /* dupfd() would return -EINVAL */ +#if 1 + if (newfd == NR_OPEN) + return -EBADF; /* dupfd() does return -EINVAL and that may + * even be the standard! But that is too + * weird for now. + */ +#endif + sys_close(newfd); + return dupfd(oldfd,newfd); +} + +asmlinkage int sys_dup(unsigned int fildes) +{ + return dupfd(fildes,0); +} + +asmlinkage int sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg) +{ + struct file * filp; + struct task_struct *p; + int task_found = 0; + + if (fd >= NR_OPEN || !(filp = current->files->fd[fd])) + return -EBADF; + switch (cmd) { + case F_DUPFD: + return dupfd(fd,arg); + case F_GETFD: + return FD_ISSET(fd, ¤t->files->close_on_exec); + case F_SETFD: + if (arg&1) + FD_SET(fd, ¤t->files->close_on_exec); + else + FD_CLR(fd, ¤t->files->close_on_exec); + return 0; + case F_GETFL: + return filp->f_flags; + case F_SETFL: + /* + * In the case of an append-only file, O_APPEND + * cannot be cleared + */ + if (IS_APPEND(filp->f_inode) && !(arg & O_APPEND)) + return -EPERM; + if ((arg & FASYNC) && !(filp->f_flags & FASYNC) && + filp->f_op->fasync) + filp->f_op->fasync(filp->f_inode, filp, 1); + if (!(arg & FASYNC) && (filp->f_flags & FASYNC) && + filp->f_op->fasync) + filp->f_op->fasync(filp->f_inode, filp, 0); + filp->f_flags &= ~(O_APPEND | O_NONBLOCK | FASYNC); + filp->f_flags |= arg & (O_APPEND | O_NONBLOCK | + FASYNC); + return 0; + case F_GETLK: + return fcntl_getlk(fd, (struct flock *) arg); + case F_SETLK: + return fcntl_setlk(fd, cmd, (struct flock *) arg); + case F_SETLKW: + return fcntl_setlk(fd, cmd, (struct flock *) arg); + case F_GETOWN: + /* + * XXX If f_owner is a process group, the + * negative return value will get converted + * into an error. Oops. If we keep the the + * current syscall conventions, the only way + * to fix this will be in libc. + */ + return filp->f_owner; + case F_SETOWN: + /* + * Add the security checks - AC. Without + * this there is a massive Linux security + * hole here - consider what happens if + * you do something like + * + * fcntl(0,F_SETOWN,some_root_process); + * getchar(); + * + * and input a line! + * + * BTW: Don't try this for fun. Several Unix + * systems I tried this on fall for the + * trick! + * + * I had to fix this botch job as Linux + * kill_fasync asserts priv making it a + * free all user process killer! + * + * Changed to make the security checks more + * liberal. -- TYT + */ + if (current->pgrp == -arg || current->pid == arg) + goto fasync_ok; + + for_each_task(p) { + if ((p->pid == arg) || (p->pid == -arg) || + (p->pgrp == -arg)) { + task_found++; + if ((p->session != current->session) && + (p->uid != current->uid) && + (p->euid != current->euid) && + !suser()) + return -EPERM; + break; + } + } + if ((task_found == 0) && !suser()) + return -EINVAL; + fasync_ok: + filp->f_owner = arg; + if (S_ISSOCK (filp->f_inode->i_mode)) + sock_fcntl (filp, F_SETOWN, arg); + return 0; + default: + /* sockets need a few special fcntls. */ + if (S_ISSOCK (filp->f_inode->i_mode)) + { + return (sock_fcntl (filp, cmd, arg)); + } + return -EINVAL; + } +} + +void kill_fasync(struct fasync_struct *fa, int sig) +{ + while (fa) { + if (fa->magic != FASYNC_MAGIC) { + printk("kill_fasync: bad magic number in " + "fasync_struct!\n"); + return; + } + if (fa->fa_file->f_owner > 0) + kill_proc(fa->fa_file->f_owner, sig, 1); + else + kill_pg(-fa->fa_file->f_owner, sig, 1); + fa = fa->fa_next; + } +} |