/*
* 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>
#include <asm/bitops.h>
extern int sock_fcntl (struct file *, unsigned int cmd, unsigned long arg);
/*
* funkcja wykorzystywana przez funkcje systemowe dup i dup2
* wykonuje przepisanie wskaznika do pliku zapisanego w tablicy current->fd
* na pozycji fd w nowe miejsce
*/
static inline int dupfd(unsigned int fd, unsigned int arg)
{
/* files jest wskaznikiem do struktury pamietanej w obszarze procesu, sluzacej do
* opisania zasobow plikowych procesu
*/
struct files_struct * files = current->files;
/* nie mozna kopiowac nielegalnego deskryptora
* (spoza tablicy deskryptorow procesu lub nie wskazujacego na otwarty plik)
*/
if (fd >= NR_OPEN || !files->fd[fd])
return -EBADF;
/*
* nie mozna kopiowac na nielegalny deskryptor
* (spoza tablicy deskryptorow procesu)
*/
if (arg >= NR_OPEN)
return -EINVAL;
/* znajdz pierwszy wolny deskryptor w tablicy deskryptorow procesu
* wykorzystaj mape bitowa wolnych deskryptorow (files->open_fds)
* pamietana w obszarze procesu; przegladaj ja
* poczynajac od bitu arg i nie przekraczajac bitu NR_OPEN
*/
arg = find_next_zero_bit(&files->open_fds, NR_OPEN, arg);
/*
* znaleziony deskryptor nie moze wykraczac poza limit przewidziany
* dla procesu na ilosc zasobow danego typu
* w przypadku liczby otwartych plikow oba pola (rlim_cur,rlim_max)
* inicjalizowane sa na NR_OPEN=256
*/
if (arg >= current->rlim[RLIMIT_NOFILE].rlim_cur)
return -EMFILE;
/*
* uaktualnij mape zajetosci deskryptorow pliku
*/
FD_SET(arg, &files->open_fds);
FD_CLR(arg, &files->close_on_exec);
/*
* przepisz stary desktyptor (wskaznik do struktury file) na nowy i
* zwieksz licznik otwarc pliku (patrz opis struktury file)
*/
(files->fd[arg] = files->fd[fd])->f_count++;
return arg;
}
/*
* funkcja systemowa dup
* argumenty:
* int filedes - numer deskryptora ktory ma zostac skopiowany
* wartosc zwracana:
* int - numer nowego deskryptora, wskazujacego na dokladnie te sama strukture file co filedes
*/
asmlinkage int sys_dup(unsigned int fildes)
{
/*
* wywolanie funkcji dokonujacej przepisania wartosci
*/
return dupfd(fildes,0);
}
/*
* funkcja systemowa dup2
* argumenty:
* int oldfd - deskryptor pliku ktory kopiujemy
* int newfd - deskryptor, na ktory chcemy skopiowac stary
*/
asmlinkage int sys_dup2(unsigned int oldfd, unsigned int newfd)
{
/*
* nie mozna kopiowac nielegalnego deskryptora
* (spoza tablicy deskryptorow lub nie wskazujacego na otwarty plik)
*/
if (oldfd >= NR_OPEN || !current->files->fd[oldfd])
return -EBADF;
/*
* kopiowanie siebie na siebie nic nie zmienia
*/
if (newfd == oldfd)
return newfd;
/*
* nie mozna kopiowac na nielegalny deskryptor
* (spoza tablicy deskryptorow)
*/
if (newfd >= NR_OPEN)
return -EBADF; /* following POSIX.1 6.2.1 */
/*
* nowy deskryptor jest legalny, wiec mogl wskazywac na otwarty plik - nalezy
* go zamknac teraz, gdyz informacja o tym pliku zostanlaby zgubiona
*/
sys_close(newfd);
/*
* wywolanie funkcji dokonujacej przepisania wartosci
*/
return dupfd(oldfd,newfd);
}
asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg)
{
struct file * filp;
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);
/* required for SunOS emulation */
if (O_NONBLOCK != O_NDELAY)
if (arg & O_NDELAY)
arg |= O_NONBLOCK;
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.pid;
case F_SETOWN:
filp->f_owner.pid = arg;
filp->f_owner.uid = current->uid;
filp->f_owner.euid = current->euid;
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;
}
}
static void send_sigio(int sig, int pid, uid_t uid, uid_t euid)
{
struct task_struct * p;
for_each_task(p) {
int match = p->pid;
if (pid < 0)
match = -p->pgrp;
if (pid != match)
continue;
if (!euid &&
(euid ^ p->suid) && (euid ^ p->uid) &&
(uid ^ p->suid) && (uid ^ p->uid))
continue;
p->signal |= 1 << (sig-1);
if (p->state == TASK_INTERRUPTIBLE && (p->signal & ~p->blocked))
wake_up_process(p);
}
}
void kill_fasync(struct fasync_struct *fa, int sig)
{
while (fa) {
struct fown_struct * fown;
if (fa->magic != FASYNC_MAGIC) {
printk("kill_fasync: bad magic number in "
"fasync_struct!\n");
return;
}
fown = &fa->fa_file->f_owner;
if (fown->pid)
send_sigio(sig, fown->pid, fown->uid, fown->euid);
fa = fa->fa_next;
}
}