/*
|
/*
|
* linux/fs/fcntl.c
|
* linux/fs/fcntl.c
|
*
|
*
|
* Copyright (C) 1991, 1992 Linus Torvalds
|
* Copyright (C) 1991, 1992 Linus Torvalds
|
*/
|
*/
|
|
|
#include <asm/segment.h>
|
#include <asm/segment.h>
|
|
|
#include <linux/sched.h>
|
#include <linux/sched.h>
|
#include <linux/kernel.h>
|
#include <linux/kernel.h>
|
#include <linux/errno.h>
|
#include <linux/errno.h>
|
#include <linux/stat.h>
|
#include <linux/stat.h>
|
#include <linux/fcntl.h>
|
#include <linux/fcntl.h>
|
#include <linux/string.h>
|
#include <linux/string.h>
|
|
|
#include <asm/bitops.h>
|
#include <asm/bitops.h>
|
|
|
extern int sock_fcntl (struct file *, unsigned int cmd, unsigned long arg);
|
extern int sock_fcntl (struct file *, unsigned int cmd, unsigned long arg);
|
|
|
static inline int dupfd(unsigned int fd, unsigned int arg)
|
static inline int dupfd(unsigned int fd, unsigned int arg)
|
{
|
{
|
struct files_struct * files = current->files;
|
struct files_struct * files = current->files;
|
|
|
if (fd >= NR_OPEN || !files->fd[fd])
|
if (fd >= NR_OPEN || !files->fd[fd])
|
return -EBADF;
|
return -EBADF;
|
if (arg >= NR_OPEN)
|
if (arg >= NR_OPEN)
|
return -EINVAL;
|
return -EINVAL;
|
arg = find_next_zero_bit(&files->open_fds, NR_OPEN, arg);
|
arg = find_next_zero_bit(&files->open_fds, NR_OPEN, arg);
|
if (arg >= current->rlim[RLIMIT_NOFILE].rlim_cur)
|
if (arg >= current->rlim[RLIMIT_NOFILE].rlim_cur)
|
return -EMFILE;
|
return -EMFILE;
|
FD_SET(arg, &files->open_fds);
|
FD_SET(arg, &files->open_fds);
|
FD_CLR(arg, &files->close_on_exec);
|
FD_CLR(arg, &files->close_on_exec);
|
(files->fd[arg] = files->fd[fd])->f_count++;
|
(files->fd[arg] = files->fd[fd])->f_count++;
|
return arg;
|
return arg;
|
}
|
}
|
|
|
asmlinkage int sys_dup2(unsigned int oldfd, unsigned int newfd)
|
asmlinkage int sys_dup2(unsigned int oldfd, unsigned int newfd)
|
{
|
{
|
if (oldfd >= NR_OPEN || !current->files->fd[oldfd])
|
if (oldfd >= NR_OPEN || !current->files->fd[oldfd])
|
return -EBADF;
|
return -EBADF;
|
if (newfd == oldfd)
|
if (newfd == oldfd)
|
return newfd;
|
return newfd;
|
if (newfd >= NR_OPEN)
|
if (newfd >= NR_OPEN)
|
return -EBADF; /* following POSIX.1 6.2.1 */
|
return -EBADF; /* following POSIX.1 6.2.1 */
|
|
|
sys_close(newfd);
|
sys_close(newfd);
|
return dupfd(oldfd,newfd);
|
return dupfd(oldfd,newfd);
|
}
|
}
|
|
|
asmlinkage int sys_dup(unsigned int fildes)
|
asmlinkage int sys_dup(unsigned int fildes)
|
{
|
{
|
return dupfd(fildes,0);
|
return dupfd(fildes,0);
|
}
|
}
|
|
|
asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg)
|
asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg)
|
{
|
{
|
struct file * filp;
|
struct file * filp;
|
|
|
if (fd >= NR_OPEN || !(filp = current->files->fd[fd]))
|
if (fd >= NR_OPEN || !(filp = current->files->fd[fd]))
|
return -EBADF;
|
return -EBADF;
|
switch (cmd) {
|
switch (cmd) {
|
case F_DUPFD:
|
case F_DUPFD:
|
return dupfd(fd,arg);
|
return dupfd(fd,arg);
|
case F_GETFD:
|
case F_GETFD:
|
return FD_ISSET(fd, ¤t->files->close_on_exec);
|
return FD_ISSET(fd, ¤t->files->close_on_exec);
|
case F_SETFD:
|
case F_SETFD:
|
if (arg&1)
|
if (arg&1)
|
FD_SET(fd, ¤t->files->close_on_exec);
|
FD_SET(fd, ¤t->files->close_on_exec);
|
else
|
else
|
FD_CLR(fd, ¤t->files->close_on_exec);
|
FD_CLR(fd, ¤t->files->close_on_exec);
|
return 0;
|
return 0;
|
case F_GETFL:
|
case F_GETFL:
|
return filp->f_flags;
|
return filp->f_flags;
|
case F_SETFL:
|
case F_SETFL:
|
/*
|
/*
|
* In the case of an append-only file, O_APPEND
|
* In the case of an append-only file, O_APPEND
|
* cannot be cleared
|
* cannot be cleared
|
*/
|
*/
|
if (IS_APPEND(filp->f_inode) && !(arg & O_APPEND))
|
if (IS_APPEND(filp->f_inode) && !(arg & O_APPEND))
|
return -EPERM;
|
return -EPERM;
|
if ((arg & FASYNC) && !(filp->f_flags & FASYNC) &&
|
if ((arg & FASYNC) && !(filp->f_flags & FASYNC) &&
|
filp->f_op->fasync)
|
filp->f_op->fasync)
|
filp->f_op->fasync(filp->f_inode, filp, 1);
|
filp->f_op->fasync(filp->f_inode, filp, 1);
|
if (!(arg & FASYNC) && (filp->f_flags & FASYNC) &&
|
if (!(arg & FASYNC) && (filp->f_flags & FASYNC) &&
|
filp->f_op->fasync)
|
filp->f_op->fasync)
|
filp->f_op->fasync(filp->f_inode, filp, 0);
|
filp->f_op->fasync(filp->f_inode, filp, 0);
|
/* required for SunOS emulation */
|
/* required for SunOS emulation */
|
if (O_NONBLOCK != O_NDELAY)
|
if (O_NONBLOCK != O_NDELAY)
|
if (arg & O_NDELAY)
|
if (arg & O_NDELAY)
|
arg |= O_NONBLOCK;
|
arg |= O_NONBLOCK;
|
filp->f_flags &= ~(O_APPEND | O_NONBLOCK | FASYNC);
|
filp->f_flags &= ~(O_APPEND | O_NONBLOCK | FASYNC);
|
filp->f_flags |= arg & (O_APPEND | O_NONBLOCK |
|
filp->f_flags |= arg & (O_APPEND | O_NONBLOCK |
|
FASYNC);
|
FASYNC);
|
return 0;
|
return 0;
|
case F_GETLK:
|
case F_GETLK:
|
return fcntl_getlk(fd, (struct flock *) arg);
|
return fcntl_getlk(fd, (struct flock *) arg);
|
case F_SETLK:
|
case F_SETLK:
|
return fcntl_setlk(fd, cmd, (struct flock *) arg);
|
return fcntl_setlk(fd, cmd, (struct flock *) arg);
|
case F_SETLKW:
|
case F_SETLKW:
|
return fcntl_setlk(fd, cmd, (struct flock *) arg);
|
return fcntl_setlk(fd, cmd, (struct flock *) arg);
|
case F_GETOWN:
|
case F_GETOWN:
|
/*
|
/*
|
* XXX If f_owner is a process group, the
|
* XXX If f_owner is a process group, the
|
* negative return value will get converted
|
* negative return value will get converted
|
* into an error. Oops. If we keep the
|
* into an error. Oops. If we keep the
|
* current syscall conventions, the only way
|
* current syscall conventions, the only way
|
* to fix this will be in libc.
|
* to fix this will be in libc.
|
*/
|
*/
|
return filp->f_owner.pid;
|
return filp->f_owner.pid;
|
case F_SETOWN:
|
case F_SETOWN:
|
filp->f_owner.pid = arg;
|
filp->f_owner.pid = arg;
|
filp->f_owner.uid = current->uid;
|
filp->f_owner.uid = current->uid;
|
filp->f_owner.euid = current->euid;
|
filp->f_owner.euid = current->euid;
|
if (S_ISSOCK (filp->f_inode->i_mode))
|
if (S_ISSOCK (filp->f_inode->i_mode))
|
sock_fcntl (filp, F_SETOWN, arg);
|
sock_fcntl (filp, F_SETOWN, arg);
|
return 0;
|
return 0;
|
default:
|
default:
|
/* sockets need a few special fcntls. */
|
/* sockets need a few special fcntls. */
|
if (S_ISSOCK (filp->f_inode->i_mode))
|
if (S_ISSOCK (filp->f_inode->i_mode))
|
{
|
{
|
return (sock_fcntl (filp, cmd, arg));
|
return (sock_fcntl (filp, cmd, arg));
|
}
|
}
|
return -EINVAL;
|
return -EINVAL;
|
}
|
}
|
}
|
}
|
|
|
static void send_sigio(int sig, int pid, uid_t uid, uid_t euid)
|
static void send_sigio(int sig, int pid, uid_t uid, uid_t euid)
|
{
|
{
|
struct task_struct * p;
|
struct task_struct * p;
|
|
|
for_each_task(p) {
|
for_each_task(p) {
|
int match = p->pid;
|
int match = p->pid;
|
if (pid < 0)
|
if (pid < 0)
|
match = -p->pgrp;
|
match = -p->pgrp;
|
if (pid != match)
|
if (pid != match)
|
continue;
|
continue;
|
if ((euid != 0) &&
|
if ((euid != 0) &&
|
(euid ^ p->suid) && (euid ^ p->uid) &&
|
(euid ^ p->suid) && (euid ^ p->uid) &&
|
(uid ^ p->suid) && (uid ^ p->uid))
|
(uid ^ p->suid) && (uid ^ p->uid))
|
continue;
|
continue;
|
p->signal |= 1 << (sig-1);
|
p->signal |= 1 << (sig-1);
|
if (p->state == TASK_INTERRUPTIBLE && (p->signal & ~p->blocked))
|
if (p->state == TASK_INTERRUPTIBLE && (p->signal & ~p->blocked))
|
wake_up_process(p);
|
wake_up_process(p);
|
}
|
}
|
}
|
}
|
|
|
void kill_fasync(struct fasync_struct *fa, int sig)
|
void kill_fasync(struct fasync_struct *fa, int sig)
|
{
|
{
|
while (fa) {
|
while (fa) {
|
struct fown_struct * fown;
|
struct fown_struct * fown;
|
if (fa->magic != FASYNC_MAGIC) {
|
if (fa->magic != FASYNC_MAGIC) {
|
printk("kill_fasync: bad magic number in "
|
printk("kill_fasync: bad magic number in "
|
"fasync_struct!\n");
|
"fasync_struct!\n");
|
return;
|
return;
|
}
|
}
|
fown = &fa->fa_file->f_owner;
|
fown = &fa->fa_file->f_owner;
|
if (fown->pid)
|
if (fown->pid)
|
send_sigio(sig, fown->pid, fown->uid, fown->euid);
|
send_sigio(sig, fown->pid, fown->uid, fown->euid);
|
fa = fa->fa_next;
|
fa = fa->fa_next;
|
}
|
}
|
}
|
}
|
|
|