linux-old/fs/fcntl.c
<<
>>
Prefs
   1/*
   2 *  linux/fs/fcntl.c
   3 *
   4 *  Copyright (C) 1991, 1992  Linus Torvalds
   5 */
   6
   7#include <linux/mm.h>
   8#include <linux/file.h>
   9#include <linux/smp_lock.h>
  10
  11#include <asm/uaccess.h>
  12
  13extern int sock_fcntl (struct file *, unsigned int cmd, unsigned long arg);
  14
  15static inline int dupfd(unsigned int fd, unsigned int start)
  16{
  17        struct files_struct * files = current->files;
  18        struct file * file;
  19        unsigned int newfd;
  20        int error;
  21
  22        error = -EINVAL;
  23        if (start >= NR_OPEN)
  24                goto out;
  25
  26        error = -EBADF;
  27        file = fget(fd);
  28        if (!file)
  29                goto out;
  30
  31repeat:
  32        error = -EMFILE;
  33        if (start < files->next_fd)
  34                start = files->next_fd;
  35        /* At this point, start MUST be <= max_fdset */
  36#if 1
  37        if (start > files->max_fdset)
  38                printk (KERN_ERR "dupfd: fd %d, max %d\n", 
  39                        start, files->max_fdset);
  40#endif
  41        newfd = find_next_zero_bit(files->open_fds->fds_bits, 
  42                                files->max_fdset,
  43                                start);
  44        if (newfd >= current->rlim[RLIMIT_NOFILE].rlim_cur)
  45                goto out_putf;
  46
  47        error = expand_files(files, newfd);
  48        if (error < 0)
  49                goto out_putf;
  50        if (error) /* If we might have blocked, try again. */
  51                goto repeat;
  52
  53        FD_SET(newfd, files->open_fds);
  54        FD_CLR(newfd, files->close_on_exec);
  55        if (start <= files->next_fd)
  56                files->next_fd = newfd + 1;
  57        fd_install(newfd, file);
  58        error = newfd;
  59out:
  60#ifdef FDSET_DEBUG      
  61        if (error < 0)
  62                printk (KERN_ERR __FUNCTION__ ": return %d\n", error);
  63#endif
  64        return error;
  65
  66out_putf:
  67        fput(file);
  68        goto out;
  69}
  70
  71asmlinkage int sys_dup2(unsigned int oldfd, unsigned int newfd)
  72{
  73        int err = -EBADF;
  74
  75        lock_kernel();
  76#ifdef FDSET_DEBUG      
  77        printk (KERN_ERR __FUNCTION__ " 0: oldfd = %d, newfd = %d\n", 
  78                oldfd, newfd);
  79#endif
  80        if (!fcheck(oldfd))
  81                goto out;
  82        if (newfd >= NR_OPEN)
  83                goto out;       /* following POSIX.1 6.2.1 */
  84
  85        err = newfd;
  86        if (newfd == oldfd)
  87                goto out;
  88
  89        /* We must be able to do the fd setting inside dupfd() without
  90           blocking after the sys_close(). */
  91        if ((err = expand_files(current->files, newfd)) < 0)
  92                goto out;
  93        
  94        sys_close(newfd);
  95        err = dupfd(oldfd, newfd);
  96out:
  97#ifdef FDSET_DEBUG      
  98        printk (KERN_ERR __FUNCTION__ ": return %d\n", err);
  99#endif
 100        unlock_kernel();
 101        return err;
 102}
 103
 104asmlinkage int sys_dup(unsigned int fildes)
 105{
 106        int ret;
 107
 108        lock_kernel();
 109        ret = dupfd(fildes, 0);
 110        unlock_kernel();
 111#ifdef FDSET_DEBUG      
 112        if (ret < 0)
 113                printk (KERN_ERR __FUNCTION__ ": return %d\n", ret);
 114#endif
 115        return ret;
 116}
 117
 118#define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | FASYNC)
 119
 120static int setfl(int fd, struct file * filp, unsigned long arg)
 121{
 122        struct inode * inode = filp->f_dentry->d_inode;
 123
 124        /*
 125         * In the case of an append-only file, O_APPEND
 126         * cannot be cleared
 127         */
 128        if (!(arg & O_APPEND) && IS_APPEND(inode))
 129                return -EPERM;
 130
 131        /* Did FASYNC state change? */
 132        if ((arg ^ filp->f_flags) & FASYNC) {
 133                if (filp->f_op && filp->f_op->fasync)
 134                        filp->f_op->fasync(fd, filp, (arg & FASYNC) != 0);
 135        }
 136
 137        /* required for strict SunOS emulation */
 138        if (O_NONBLOCK != O_NDELAY)
 139               if (arg & O_NDELAY)
 140                   arg |= O_NONBLOCK;
 141
 142        filp->f_flags = (arg & SETFL_MASK) | (filp->f_flags & ~SETFL_MASK);
 143        return 0;
 144}
 145
 146asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg)
 147{       
 148        struct file * filp;
 149        long err = -EBADF;
 150
 151        lock_kernel();
 152        filp = fget(fd);
 153        if (!filp)
 154                goto out;
 155
 156        err = 0;
 157        switch (cmd) {
 158                case F_DUPFD:
 159                        err = dupfd(fd, arg);
 160                        break;
 161                case F_GETFD:
 162                        err = FD_ISSET(fd, current->files->close_on_exec);
 163                        break;
 164                case F_SETFD:
 165                        if (arg&1)
 166                                FD_SET(fd, current->files->close_on_exec);
 167                        else
 168                                FD_CLR(fd, current->files->close_on_exec);
 169                        break;
 170                case F_GETFL:
 171                        err = filp->f_flags;
 172                        break;
 173                case F_SETFL:
 174                        err = setfl(fd, filp, arg);
 175                        break;
 176                case F_GETLK:
 177                        err = fcntl_getlk(fd, (struct flock *) arg);
 178                        break;
 179                case F_SETLK:
 180                        err = fcntl_setlk(fd, cmd, (struct flock *) arg);
 181                        break;
 182                case F_SETLKW:
 183                        err = fcntl_setlk(fd, cmd, (struct flock *) arg);
 184                        break;
 185                case F_GETOWN:
 186                        /*
 187                         * XXX If f_owner is a process group, the
 188                         * negative return value will get converted
 189                         * into an error.  Oops.  If we keep the
 190                         * current syscall conventions, the only way
 191                         * to fix this will be in libc.
 192                         */
 193                        err = filp->f_owner.pid;
 194                        break;
 195                case F_SETOWN:
 196                        filp->f_owner.pid = arg;
 197                        filp->f_owner.uid = current->uid;
 198                        filp->f_owner.euid = current->euid;
 199                        if (S_ISSOCK (filp->f_dentry->d_inode->i_mode))
 200                                err = sock_fcntl (filp, F_SETOWN, arg);
 201                        break;
 202                case F_GETSIG:
 203                        err = filp->f_owner.signum;
 204                        break;
 205                case F_SETSIG:
 206                        if (arg <= 0 || arg > _NSIG) {
 207                                err = -EINVAL;
 208                                break;
 209                        }
 210                        err = 0;
 211                        filp->f_owner.signum = arg;
 212                        break;
 213                default:
 214                        /* sockets need a few special fcntls. */
 215                        err = -EINVAL;
 216                        if (S_ISSOCK (filp->f_dentry->d_inode->i_mode))
 217                                err = sock_fcntl (filp, cmd, arg);
 218                        break;
 219        }
 220        fput(filp);
 221out:
 222        unlock_kernel();
 223        return err;
 224}
 225
 226static void send_sigio(struct fown_struct *fown, struct fasync_struct *fa)
 227{
 228        struct task_struct * p;
 229        int   pid       = fown->pid;
 230        uid_t uid       = fown->uid;
 231        uid_t euid      = fown->euid;
 232        
 233        read_lock(&tasklist_lock);
 234        for_each_task(p) {
 235                int match = p->pid;
 236                if (pid < 0)
 237                        match = -p->pgrp;
 238                if (pid != match)
 239                        continue;
 240                if ((euid != 0) &&
 241                    (euid ^ p->suid) && (euid ^ p->uid) &&
 242                    (uid ^ p->suid) && (uid ^ p->uid))
 243                        continue;
 244                switch (fown->signum) {
 245                        siginfo_t si;
 246                default:
 247                        /* Queue a rt signal with the appropriate fd as its
 248                           value.  We use SI_SIGIO as the source, not 
 249                           SI_KERNEL, since kernel signals always get 
 250                           delivered even if we can't queue.  Failure to
 251                           queue in this case _should_ be reported; we fall
 252                           back to SIGIO in that case. --sct */
 253                        si.si_signo = fown->signum;
 254                        si.si_errno = 0;
 255                        si.si_code  = SI_SIGIO;
 256                        si.si_pid   = pid;
 257                        si.si_uid   = uid;
 258                        si.si_fd    = fa->fa_fd;
 259                        if (!send_sig_info(fown->signum, &si, p))
 260                                break;
 261                /* fall-through: fall back on the old plain SIGIO signal */
 262                case 0:
 263                        send_sig(SIGIO, p, 1);
 264                }
 265        }
 266        read_unlock(&tasklist_lock);
 267}
 268
 269void kill_fasync(struct fasync_struct *fa, int sig)
 270{
 271        while (fa) {
 272                struct fown_struct * fown;
 273                if (fa->magic != FASYNC_MAGIC) {
 274                        printk("kill_fasync: bad magic number in "
 275                               "fasync_struct!\n");
 276                        return;
 277                }
 278                fown = &fa->fa_file->f_owner;
 279                if (fown->pid)
 280                        send_sigio(fown, fa);
 281                fa = fa->fa_next;
 282        }
 283}
 284
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.