fedora core 6 1.2949 + vserver 2.2.0
[linux-2.6.git] / fs / fcntl.c
index 3fe5cda..13b97ae 100644 (file)
@@ -4,16 +4,20 @@
  *  Copyright (C) 1991, 1992  Linus Torvalds
  */
 
+#include <linux/syscalls.h>
 #include <linux/init.h>
 #include <linux/mm.h>
 #include <linux/fs.h>
 #include <linux/file.h>
+#include <linux/capability.h>
 #include <linux/dnotify.h>
 #include <linux/smp_lock.h>
 #include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/security.h>
 #include <linux/ptrace.h>
+#include <linux/signal.h>
+#include <linux/rcupdate.h>
 #include <linux/vs_limit.h>
 
 #include <asm/poll.h>
 void fastcall 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, files->close_on_exec);
+               FD_SET(fd, fdt->close_on_exec);
        else
-               FD_CLR(fd, files->close_on_exec);
+               FD_CLR(fd, fdt->close_on_exec);
        spin_unlock(&files->file_lock);
 }
 
-static inline int get_close_on_exec(unsigned int fd)
+static int get_close_on_exec(unsigned int fd)
 {
        struct files_struct *files = current->files;
+       struct fdtable *fdt;
        int res;
-       spin_lock(&files->file_lock);
-       res = FD_ISSET(fd, files->close_on_exec);
-       spin_unlock(&files->file_lock);
+       rcu_read_lock();
+       fdt = files_fdtable(files);
+       res = FD_ISSET(fd, fdt->close_on_exec);
+       rcu_read_unlock();
        return res;
 }
 
-
-/* Expand files.  Return <0 on error; 0 nothing done; 1 files expanded,
- * we may have blocked. 
- *
- * Should be called with the files->file_lock spinlock held for write.
- */
-static int expand_files(struct files_struct *files, int nr)
-{
-       int err, expand = 0;
-#ifdef FDSET_DEBUG     
-       printk (KERN_ERR "%s %d: nr = %d\n", __FUNCTION__, current->pid, nr);
-#endif
-       
-       if (nr >= files->max_fdset) {
-               expand = 1;
-               if ((err = expand_fdset(files, nr)))
-                       goto out;
-       }
-       if (nr >= files->max_fds) {
-               expand = 1;
-               if ((err = expand_fd_array(files, nr)))
-                       goto out;
-       }
-       err = expand;
- out:
-#ifdef FDSET_DEBUG     
-       if (err)
-               printk (KERN_ERR "%s %d: return %d\n", __FUNCTION__, current->pid, err);
-#endif
-       return err;
-}
-
 /*
  * locate_fd finds a free file descriptor in the open_fds fdset,
  * expanding the fd arrays if necessary.  Must be called with the
@@ -85,28 +61,29 @@ static int locate_fd(struct files_struct *files,
        unsigned int newfd;
        unsigned int start;
        int error;
+       struct fdtable *fdt;
 
        error = -EINVAL;
-       if (orig_start >= current->rlim[RLIMIT_NOFILE].rlim_cur)
+       if (orig_start >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
                goto out;
 
 repeat:
+       fdt = files_fdtable(files);
        /*
         * Someone might have closed fd's in the range
-        * orig_start..files->next_fd
+        * orig_start..fdt->next_fd
         */
        start = orig_start;
        if (start < files->next_fd)
                start = files->next_fd;
 
        newfd = start;
-       if (start < files->max_fdset) {
-               newfd = find_next_zero_bit(files->open_fds->fds_bits,
-                       files->max_fdset, start);
-       }
+       if (start < fdt->max_fds)
+               newfd = find_next_zero_bit(fdt->open_fds->fds_bits,
+                                          fdt->max_fds, start);
        
        error = -EMFILE;
-       if (newfd >= current->rlim[RLIMIT_NOFILE].rlim_cur)
+       if (newfd >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
                goto out;
        if (!vx_files_avail(1))
                goto out;
@@ -122,9 +99,14 @@ repeat:
        if (error)
                goto repeat;
 
+       /*
+        * We reacquired files_lock, so we are safe as long as
+        * we reacquire the fdtable pointer and use it while holding
+        * the lock, no one can free it during that time.
+        */
        if (start <= files->next_fd)
                files->next_fd = newfd + 1;
-       
+
        error = newfd;
        
 out:
@@ -134,13 +116,16 @@ out:
 static int dupfd(struct file *file, unsigned int start)
 {
        struct files_struct * files = current->files;
+       struct fdtable *fdt;
        int fd;
 
        spin_lock(&files->file_lock);
        fd = locate_fd(files, file, start);
        if (fd >= 0) {
-               FD_SET(fd, files->open_fds);
-               FD_CLR(fd, files->close_on_exec);
+               /* locate_fd() may have expanded fdtable, load the ptr */
+               fdt = files_fdtable(files);
+               FD_SET(fd, fdt->open_fds);
+               FD_CLR(fd, fdt->close_on_exec);
                spin_unlock(&files->file_lock);
                vx_openfd_inc(fd);
                fd_install(fd, file);
@@ -157,6 +142,7 @@ asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd)
        int err = -EBADF;
        struct file * file, *tofree;
        struct files_struct * files = current->files;
+       struct fdtable *fdt;
 
        spin_lock(&files->file_lock);
        if (!(file = fcheck(oldfd)))
@@ -165,7 +151,7 @@ asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd)
        if (newfd == oldfd)
                goto out_unlock;
        err = -EBADF;
-       if (newfd >= current->rlim[RLIMIT_NOFILE].rlim_cur)
+       if (newfd >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
                goto out_unlock;
        get_file(file);                 /* We are now finished with oldfd */
 
@@ -182,18 +168,21 @@ asmlinkage long sys_dup2(unsigned int oldfd, unsigned int newfd)
 
        /* Yes. It's a race. In user space. Nothing sane to do */
        err = -EBUSY;
-       tofree = files->fd[newfd];
-       if (!tofree && FD_ISSET(newfd, files->open_fds))
+       fdt = files_fdtable(files);
+       tofree = fdt->fd[newfd];
+       if (!tofree && FD_ISSET(newfd, fdt->open_fds))
                goto out_fput;
 
-       files->fd[newfd] = file;
-       FD_SET(newfd, files->open_fds);
-       FD_CLR(newfd, files->close_on_exec);
+       rcu_assign_pointer(fdt->fd[newfd], file);
+       FD_SET(newfd, fdt->open_fds);
+       FD_CLR(newfd, fdt->close_on_exec);
        spin_unlock(&files->file_lock);
-       vx_openfd_inc(newfd);
 
        if (tofree)
                filp_close(tofree, files);
+       else
+               vx_openfd_inc(newfd);   /* fd was unused */
+
        err = newfd;
 out:
        return err;
@@ -217,17 +206,25 @@ asmlinkage long sys_dup(unsigned int fildes)
        return ret;
 }
 
-#define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | FASYNC | O_DIRECT)
+#define SETFL_MASK (O_APPEND | O_NONBLOCK | O_NDELAY | FASYNC | O_DIRECT | O_NOATIME)
 
 static int setfl(int fd, struct file * filp, unsigned long arg)
 {
-       struct inode * inode = filp->f_dentry->d_inode;
+       struct inode * inode = filp->f_path.dentry->d_inode;
        int error = 0;
 
-       /* O_APPEND cannot be cleared if the file is marked as append-only */
-       if (!(arg & O_APPEND) && IS_APPEND(inode))
+       /*
+        * O_APPEND cannot be cleared if the file is marked as append-only
+        * and the file is open for write.
+        */
+       if (((arg ^ filp->f_flags) & O_APPEND) && IS_APPEND(inode))
                return -EPERM;
 
+       /* O_NOATIME can only be set by the owner or superuser */
+       if ((arg & O_NOATIME) && !(filp->f_flags & O_NOATIME))
+               if (current->fsuid != inode->i_uid && !capable(CAP_FOWNER))
+                       return -EPERM;
+
        /* required for strict SunOS emulation */
        if (O_NONBLOCK != O_NDELAY)
               if (arg & O_NDELAY)
@@ -239,6 +236,11 @@ static int setfl(int fd, struct file * filp, unsigned long arg)
                                return -EINVAL;
        }
 
+       if (filp->f_op && filp->f_op->check_flags)
+               error = filp->f_op->check_flags(arg);
+       if (error)
+               return error;
+
        lock_kernel();
        if ((arg ^ filp->f_flags) & FASYNC) {
                if (filp->f_op && filp->f_op->fasync) {
@@ -254,19 +256,22 @@ static int setfl(int fd, struct file * filp, unsigned long arg)
        return error;
 }
 
-static void f_modown(struct file *filp, unsigned long pid,
+static void f_modown(struct file *filp, struct pid *pid, enum pid_type type,
                      uid_t uid, uid_t euid, int force)
 {
        write_lock_irq(&filp->f_owner.lock);
        if (force || !filp->f_owner.pid) {
-               filp->f_owner.pid = pid;
+               put_pid(filp->f_owner.pid);
+               filp->f_owner.pid = get_pid(pid);
+               filp->f_owner.pid_type = type;
                filp->f_owner.uid = uid;
                filp->f_owner.euid = euid;
        }
        write_unlock_irq(&filp->f_owner.lock);
 }
 
-int f_setown(struct file *filp, unsigned long arg, int force)
+int __f_setown(struct file *filp, struct pid *pid, enum pid_type type,
+               int force)
 {
        int err;
        
@@ -274,21 +279,48 @@ int f_setown(struct file *filp, unsigned long arg, int force)
        if (err)
                return err;
 
-       f_modown(filp, arg, current->uid, current->euid, force);
+       f_modown(filp, pid, type, current->uid, current->euid, force);
        return 0;
 }
+EXPORT_SYMBOL(__f_setown);
 
+int f_setown(struct file *filp, unsigned long arg, int force)
+{
+       enum pid_type type;
+       struct pid *pid;
+       int who = arg;
+       int result;
+       type = PIDTYPE_PID;
+       if (who < 0) {
+               type = PIDTYPE_PGID;
+               who = -who;
+       }
+       rcu_read_lock();
+       pid = find_pid(who);
+       result = __f_setown(filp, pid, type, force);
+       rcu_read_unlock();
+       return result;
+}
 EXPORT_SYMBOL(f_setown);
 
 void f_delown(struct file *filp)
 {
-       f_modown(filp, 0, 0, 0, 1);
+       f_modown(filp, NULL, PIDTYPE_PID, 0, 0, 1);
 }
 
-EXPORT_SYMBOL(f_delown);
+pid_t f_getown(struct file *filp)
+{
+       pid_t pid;
+       read_lock(&filp->f_owner.lock);
+       pid = pid_nr(filp->f_owner.pid);
+       if (filp->f_owner.pid_type == PIDTYPE_PGID)
+               pid = -pid;
+       read_unlock(&filp->f_owner.lock);
+       return pid;
+}
 
-long generic_file_fcntl(int fd, unsigned int cmd,
-                       unsigned long arg, struct file *filp)
+static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
+               struct file *filp)
 {
        long err = -EINVAL;
 
@@ -315,7 +347,7 @@ long generic_file_fcntl(int fd, unsigned int cmd,
                break;
        case F_SETLK:
        case F_SETLKW:
-               err = fcntl_setlk(filp, cmd, (struct flock __user *) arg);
+               err = fcntl_setlk(fd, filp, cmd, (struct flock __user *) arg);
                break;
        case F_GETOWN:
                /*
@@ -325,7 +357,7 @@ long generic_file_fcntl(int fd, unsigned int cmd,
                 * current syscall conventions, the only way
                 * to fix this will be in libc.
                 */
-               err = filp->f_owner.pid;
+               err = f_getown(filp);
                force_successful_syscall_return();
                break;
        case F_SETOWN:
@@ -336,7 +368,7 @@ long generic_file_fcntl(int fd, unsigned int cmd,
                break;
        case F_SETSIG:
                /* arg == 0 restores default behaviour. */
-               if (arg < 0 || arg > _NSIG) {
+               if (!valid_signal(arg)) {
                        break;
                }
                err = 0;
@@ -356,17 +388,8 @@ long generic_file_fcntl(int fd, unsigned int cmd,
        }
        return err;
 }
-EXPORT_SYMBOL(generic_file_fcntl);
-
-static long do_fcntl(int fd, unsigned int cmd,
-                       unsigned long arg, struct file *filp)
-{
-       if (filp->f_op && filp->f_op->fcntl)
-               return filp->f_op->fcntl(fd, cmd, arg, filp);
-       return generic_file_fcntl(fd, cmd, arg, filp);
-}
 
-asmlinkage long sys_fcntl(int fd, unsigned int cmd, unsigned long arg)
+asmlinkage long sys_fcntl(unsigned int fd, unsigned int cmd, unsigned long arg)
 {      
        struct file *filp;
        long err = -EBADF;
@@ -412,7 +435,8 @@ asmlinkage long sys_fcntl64(unsigned int fd, unsigned int cmd, unsigned long arg
                        break;
                case F_SETLK64:
                case F_SETLKW64:
-                       err = fcntl_setlk64(filp, cmd, (struct flock64 __user *) arg);
+                       err = fcntl_setlk64(fd, filp, cmd,
+                                       (struct flock64 __user *) arg);
                        break;
                default:
                        err = do_fcntl(fd, cmd, arg, filp);
@@ -426,7 +450,7 @@ out:
 
 /* Table to convert sigio signal codes into poll band bitmaps */
 
-static long band_table[NSIGPOLL] = {
+static const long band_table[NSIGPOLL] = {
        POLLIN | POLLRDNORM,                    /* POLL_IN */
        POLLOUT | POLLWRNORM | POLLWRBAND,      /* POLL_OUT */
        POLLIN | POLLRDNORM | POLLMSG,          /* POLL_MSG */
@@ -436,11 +460,12 @@ static long band_table[NSIGPOLL] = {
 };
 
 static inline int sigio_perm(struct task_struct *p,
-                             struct fown_struct *fown)
+                             struct fown_struct *fown, int sig)
 {
-       return ((fown->euid == 0) ||
-               (fown->euid == p->suid) || (fown->euid == p->uid) ||
-               (fown->uid == p->suid) || (fown->uid == p->uid));
+       return (((fown->euid == 0) ||
+                (fown->euid == p->suid) || (fown->euid == p->uid) ||
+                (fown->uid == p->suid) || (fown->uid == p->uid)) &&
+               !security_file_send_sigiotask(p, fown, sig));
 }
 
 static void send_sigio_to_task(struct task_struct *p,
@@ -448,10 +473,7 @@ static void send_sigio_to_task(struct task_struct *p,
                               int fd,
                               int reason)
 {
-       if (!sigio_perm(p, fown))
-               return;
-
-       if (security_file_send_sigiotask(p, fown, fd, reason))
+       if (!sigio_perm(p, fown, fown->signum))
                return;
 
        switch (fown->signum) {
@@ -469,44 +491,36 @@ static void send_sigio_to_task(struct task_struct *p,
                        /* Make sure we are called with one of the POLL_*
                           reasons, otherwise we could leak kernel stack into
                           userspace.  */
-                       if ((reason & __SI_MASK) != __SI_POLL)
-                               BUG();
+                       BUG_ON((reason & __SI_MASK) != __SI_POLL);
                        if (reason - POLL_IN >= NSIGPOLL)
                                si.si_band  = ~0L;
                        else
                                si.si_band = band_table[reason - POLL_IN];
                        si.si_fd    = fd;
-                       if (!send_sig_info(fown->signum, &si, p))
+                       if (!group_send_sig_info(fown->signum, &si, p))
                                break;
                /* fall-through: fall back on the old plain SIGIO signal */
                case 0:
-                       send_group_sig_info(SIGIO, SEND_SIG_PRIV, p);
+                       group_send_sig_info(SIGIO, SEND_SIG_PRIV, p);
        }
 }
 
 void send_sigio(struct fown_struct *fown, int fd, int band)
 {
        struct task_struct *p;
-       int pid;
+       enum pid_type type;
+       struct pid *pid;
        
        read_lock(&fown->lock);
+       type = fown->pid_type;
        pid = fown->pid;
        if (!pid)
                goto out_unlock_fown;
        
        read_lock(&tasklist_lock);
-       if (pid > 0) {
-               p = find_task_by_pid(pid);
-               if (p) {
-                       send_sigio_to_task(p, fown, fd, band);
-               }
-       } else {
-               struct list_head *l;
-               struct pid *pidptr;
-               for_each_task_pid(-pid, PIDTYPE_PGID, p, l, pidptr) {
-                       send_sigio_to_task(p, fown, fd, band);
-               }
-       }
+       do_each_pid_task(pid, type, p) {
+               send_sigio_to_task(p, fown, fd, band);
+       } while_each_pid_task(pid, type, p);
        read_unlock(&tasklist_lock);
  out_unlock_fown:
        read_unlock(&fown->lock);
@@ -515,16 +529,19 @@ void send_sigio(struct fown_struct *fown, int fd, int band)
 static void send_sigurg_to_task(struct task_struct *p,
                                 struct fown_struct *fown)
 {
-       if (sigio_perm(p, fown))
-               send_group_sig_info(SIGURG, SEND_SIG_PRIV, p);
+       if (sigio_perm(p, fown, SIGURG))
+               group_send_sig_info(SIGURG, SEND_SIG_PRIV, p);
 }
 
 int send_sigurg(struct fown_struct *fown)
 {
        struct task_struct *p;
-       int pid, ret = 0;
+       enum pid_type type;
+       struct pid *pid;
+       int ret = 0;
        
        read_lock(&fown->lock);
+       type = fown->pid_type;
        pid = fown->pid;
        if (!pid)
                goto out_unlock_fown;
@@ -532,26 +549,19 @@ int send_sigurg(struct fown_struct *fown)
        ret = 1;
        
        read_lock(&tasklist_lock);
-       if (pid > 0) {
-               p = find_task_by_pid(pid);
-               if (p) {
-                       send_sigurg_to_task(p, fown);
-               }
-       } else {
-               struct list_head *l;
-               struct pid *pidptr;
-               for_each_task_pid(-pid, PIDTYPE_PGID, p, l, pidptr) {
-                       send_sigurg_to_task(p, fown);
-               }
-       }
+       do_each_pid_task(pid, type, p) {
+               send_sigurg_to_task(p, fown);
+       } while_each_pid_task(pid, type, p);
        read_unlock(&tasklist_lock);
  out_unlock_fown:
        read_unlock(&fown->lock);
        return ret;
 }
 
-static rwlock_t fasync_lock = RW_LOCK_UNLOCKED;
-static kmem_cache_t *fasync_cache;
+EXPORT_SYMBOL(send_sigurg);
+
+static DEFINE_RWLOCK(fasync_lock);
+static struct kmem_cache *fasync_cache __read_mostly;
 
 /*
  * fasync_helper() is used by some character device drivers (mainly mice)
@@ -565,7 +575,7 @@ int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fap
        int result = 0;
 
        if (on) {
-               new = kmem_cache_alloc(fasync_cache, SLAB_KERNEL);
+               new = kmem_cache_alloc(fasync_cache, GFP_KERNEL);
                if (!new)
                        return -ENOMEM;
        }