Introduce smb2 mounts as vers=2
[linux-2.6.git] / fs / fcntl.c
index 1ad7031..22764c7 100644 (file)
 #include <linux/dnotify.h>
 #include <linux/slab.h>
 #include <linux/module.h>
+#include <linux/pipe_fs_i.h>
 #include <linux/security.h>
 #include <linux/ptrace.h>
 #include <linux/signal.h>
 #include <linux/rcupdate.h>
 #include <linux/pid_namespace.h>
-#include <linux/smp_lock.h>
 
 #include <asm/poll.h>
 #include <asm/siginfo.h>
@@ -131,7 +131,7 @@ SYSCALL_DEFINE2(dup2, unsigned int, oldfd, unsigned int, newfd)
 SYSCALL_DEFINE1(dup, unsigned int, fildes)
 {
        int ret = -EBADF;
-       struct file *file = fget(fildes);
+       struct file *file = fget_raw(fildes);
 
        if (file) {
                ret = get_unused_fd();
@@ -159,7 +159,7 @@ static int setfl(int fd, struct file * filp, unsigned long arg)
 
        /* O_NOATIME can only be set by the owner or superuser */
        if ((arg & O_NOATIME) && !(filp->f_flags & O_NOATIME))
-               if (!is_owner_or_cap(inode))
+               if (!inode_owner_or_capable(inode))
                        return -EPERM;
 
        /* required for strict SunOS emulation */
@@ -198,15 +198,19 @@ static int setfl(int fd, struct file * filp, unsigned long arg)
 }
 
 static void f_modown(struct file *filp, struct pid *pid, enum pid_type type,
-                     uid_t uid, uid_t euid, int force)
+                     int force)
 {
        write_lock_irq(&filp->f_owner.lock);
        if (force || !filp->f_owner.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;
+
+               if (pid) {
+                       const struct cred *cred = current_cred();
+                       filp->f_owner.uid = cred->uid;
+                       filp->f_owner.euid = cred->euid;
+               }
        }
        write_unlock_irq(&filp->f_owner.lock);
 }
@@ -214,14 +218,13 @@ static void f_modown(struct file *filp, struct pid *pid, enum pid_type type,
 int __f_setown(struct file *filp, struct pid *pid, enum pid_type type,
                int force)
 {
-       const struct cred *cred = current_cred();
        int err;
-       
+
        err = security_file_set_fowner(filp);
        if (err)
                return err;
 
-       f_modown(filp, pid, type, cred->uid, cred->euid, force);
+       f_modown(filp, pid, type, force);
        return 0;
 }
 EXPORT_SYMBOL(__f_setown);
@@ -247,7 +250,7 @@ EXPORT_SYMBOL(f_setown);
 
 void f_delown(struct file *filp)
 {
-       f_modown(filp, NULL, PIDTYPE_PID, 0, 0, 1);
+       f_modown(filp, NULL, PIDTYPE_PID, 1);
 }
 
 pid_t f_getown(struct file *filp)
@@ -261,6 +264,82 @@ pid_t f_getown(struct file *filp)
        return pid;
 }
 
+static int f_setown_ex(struct file *filp, unsigned long arg)
+{
+       struct f_owner_ex * __user owner_p = (void * __user)arg;
+       struct f_owner_ex owner;
+       struct pid *pid;
+       int type;
+       int ret;
+
+       ret = copy_from_user(&owner, owner_p, sizeof(owner));
+       if (ret)
+               return -EFAULT;
+
+       switch (owner.type) {
+       case F_OWNER_TID:
+               type = PIDTYPE_MAX;
+               break;
+
+       case F_OWNER_PID:
+               type = PIDTYPE_PID;
+               break;
+
+       case F_OWNER_PGRP:
+               type = PIDTYPE_PGID;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       rcu_read_lock();
+       pid = find_vpid(owner.pid);
+       if (owner.pid && !pid)
+               ret = -ESRCH;
+       else
+               ret = __f_setown(filp, pid, type, 1);
+       rcu_read_unlock();
+
+       return ret;
+}
+
+static int f_getown_ex(struct file *filp, unsigned long arg)
+{
+       struct f_owner_ex * __user owner_p = (void * __user)arg;
+       struct f_owner_ex owner;
+       int ret = 0;
+
+       read_lock(&filp->f_owner.lock);
+       owner.pid = pid_vnr(filp->f_owner.pid);
+       switch (filp->f_owner.pid_type) {
+       case PIDTYPE_MAX:
+               owner.type = F_OWNER_TID;
+               break;
+
+       case PIDTYPE_PID:
+               owner.type = F_OWNER_PID;
+               break;
+
+       case PIDTYPE_PGID:
+               owner.type = F_OWNER_PGRP;
+               break;
+
+       default:
+               WARN_ON(1);
+               ret = -EINVAL;
+               break;
+       }
+       read_unlock(&filp->f_owner.lock);
+
+       if (!ret) {
+               ret = copy_to_user(owner_p, &owner, sizeof(owner));
+               if (ret)
+                       ret = -EFAULT;
+       }
+       return ret;
+}
+
 static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
                struct file *filp)
 {
@@ -269,7 +348,7 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
        switch (cmd) {
        case F_DUPFD:
        case F_DUPFD_CLOEXEC:
-               if (arg >= current->signal->rlim[RLIMIT_NOFILE].rlim_cur)
+               if (arg >= rlimit(RLIMIT_NOFILE))
                        break;
                err = alloc_fd(arg, cmd == F_DUPFD_CLOEXEC ? O_CLOEXEC : 0);
                if (err >= 0) {
@@ -311,6 +390,12 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
        case F_SETOWN:
                err = f_setown(filp, arg, 1);
                break;
+       case F_GETOWN_EX:
+               err = f_getown_ex(filp, arg);
+               break;
+       case F_SETOWN_EX:
+               err = f_setown_ex(filp, arg);
+               break;
        case F_GETSIG:
                err = filp->f_owner.signum;
                break;
@@ -331,21 +416,45 @@ static long do_fcntl(int fd, unsigned int cmd, unsigned long arg,
        case F_NOTIFY:
                err = fcntl_dirnotify(fd, filp, arg);
                break;
+       case F_SETPIPE_SZ:
+       case F_GETPIPE_SZ:
+               err = pipe_fcntl(filp, cmd, arg);
+               break;
        default:
                break;
        }
        return err;
 }
 
+static int check_fcntl_cmd(unsigned cmd)
+{
+       switch (cmd) {
+       case F_DUPFD:
+       case F_DUPFD_CLOEXEC:
+       case F_GETFD:
+       case F_SETFD:
+       case F_GETFL:
+               return 1;
+       }
+       return 0;
+}
+
 SYSCALL_DEFINE3(fcntl, unsigned int, fd, unsigned int, cmd, unsigned long, arg)
 {      
        struct file *filp;
        long err = -EBADF;
 
-       filp = fget(fd);
+       filp = fget_raw(fd);
        if (!filp)
                goto out;
 
+       if (unlikely(filp->f_mode & FMODE_PATH)) {
+               if (!check_fcntl_cmd(cmd)) {
+                       fput(filp);
+                       goto out;
+               }
+       }
+
        err = security_file_fcntl(filp, cmd, arg);
        if (err) {
                fput(filp);
@@ -367,10 +476,17 @@ SYSCALL_DEFINE3(fcntl64, unsigned int, fd, unsigned int, cmd,
        long err;
 
        err = -EBADF;
-       filp = fget(fd);
+       filp = fget_raw(fd);
        if (!filp)
                goto out;
 
+       if (unlikely(filp->f_mode & FMODE_PATH)) {
+               if (!check_fcntl_cmd(cmd)) {
+                       fput(filp);
+                       goto out;
+               }
+       }
+
        err = security_file_fcntl(filp, cmd, arg);
        if (err) {
                fput(filp);
@@ -425,14 +541,19 @@ static inline int sigio_perm(struct task_struct *p,
 }
 
 static void send_sigio_to_task(struct task_struct *p,
-                              struct fown_struct *fown, 
-                              int fd,
-                              int reason)
+                              struct fown_struct *fown,
+                              int fd, int reason, int group)
 {
-       if (!sigio_perm(p, fown, fown->signum))
+       /*
+        * F_SETSIG can change ->signum lockless in parallel, make
+        * sure we read it once and use the same value throughout.
+        */
+       int signum = ACCESS_ONCE(fown->signum);
+
+       if (!sigio_perm(p, fown, signum))
                return;
 
-       switch (fown->signum) {
+       switch (signum) {
                siginfo_t si;
                default:
                        /* Queue a rt signal with the appropriate fd as its
@@ -441,7 +562,7 @@ static void send_sigio_to_task(struct task_struct *p,
                           delivered even if we can't queue.  Failure to
                           queue in this case _should_ be reported; we fall
                           back to SIGIO in that case. --sct */
-                       si.si_signo = fown->signum;
+                       si.si_signo = signum;
                        si.si_errno = 0;
                        si.si_code  = reason;
                        /* Make sure we are called with one of the POLL_*
@@ -453,11 +574,11 @@ static void send_sigio_to_task(struct task_struct *p,
                        else
                                si.si_band = band_table[reason - POLL_IN];
                        si.si_fd    = fd;
-                       if (!group_send_sig_info(fown->signum, &si, p))
+                       if (!do_send_sig_info(signum, &si, p, group))
                                break;
                /* fall-through: fall back on the old plain SIGIO signal */
                case 0:
-                       group_send_sig_info(SIGIO, SEND_SIG_PRIV, p);
+                       do_send_sig_info(SIGIO, SEND_SIG_PRIV, p, group);
        }
 }
 
@@ -466,16 +587,23 @@ void send_sigio(struct fown_struct *fown, int fd, int band)
        struct task_struct *p;
        enum pid_type type;
        struct pid *pid;
+       int group = 1;
        
        read_lock(&fown->lock);
+
        type = fown->pid_type;
+       if (type == PIDTYPE_MAX) {
+               group = 0;
+               type = PIDTYPE_PID;
+       }
+
        pid = fown->pid;
        if (!pid)
                goto out_unlock_fown;
        
        read_lock(&tasklist_lock);
        do_each_pid_task(pid, type, p) {
-               send_sigio_to_task(p, fown, fd, band);
+               send_sigio_to_task(p, fown, fd, band, group);
        } while_each_pid_task(pid, type, p);
        read_unlock(&tasklist_lock);
  out_unlock_fown:
@@ -483,10 +611,10 @@ 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)
+                               struct fown_struct *fown, int group)
 {
        if (sigio_perm(p, fown, SIGURG))
-               group_send_sig_info(SIGURG, SEND_SIG_PRIV, p);
+               do_send_sig_info(SIGURG, SEND_SIG_PRIV, p, group);
 }
 
 int send_sigurg(struct fown_struct *fown)
@@ -494,10 +622,17 @@ int send_sigurg(struct fown_struct *fown)
        struct task_struct *p;
        enum pid_type type;
        struct pid *pid;
+       int group = 1;
        int ret = 0;
        
        read_lock(&fown->lock);
+
        type = fown->pid_type;
+       if (type == PIDTYPE_MAX) {
+               group = 0;
+               type = PIDTYPE_PID;
+       }
+
        pid = fown->pid;
        if (!pid)
                goto out_unlock_fown;
@@ -506,7 +641,7 @@ int send_sigurg(struct fown_struct *fown)
        
        read_lock(&tasklist_lock);
        do_each_pid_task(pid, type, p) {
-               send_sigurg_to_task(p, fown);
+               send_sigurg_to_task(p, fown, group);
        } while_each_pid_task(pid, type, p);
        read_unlock(&tasklist_lock);
  out_unlock_fown:
@@ -514,106 +649,205 @@ int send_sigurg(struct fown_struct *fown)
        return ret;
 }
 
-static DEFINE_RWLOCK(fasync_lock);
+static DEFINE_SPINLOCK(fasync_lock);
 static struct kmem_cache *fasync_cache __read_mostly;
 
+static void fasync_free_rcu(struct rcu_head *head)
+{
+       kmem_cache_free(fasync_cache,
+                       container_of(head, struct fasync_struct, fa_rcu));
+}
+
 /*
- * fasync_helper() is used by almost all character device drivers
- * to set up the fasync queue. It returns negative on error, 0 if it did
- * no changes and positive if it added/deleted the entry.
+ * Remove a fasync entry. If successfully removed, return
+ * positive and clear the FASYNC flag. If no entry exists,
+ * do nothing and return 0.
+ *
+ * NOTE! It is very important that the FASYNC flag always
+ * match the state "is the filp on a fasync list".
+ *
  */
-int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
+int fasync_remove_entry(struct file *filp, struct fasync_struct **fapp)
 {
        struct fasync_struct *fa, **fp;
-       struct fasync_struct *new = NULL;
        int result = 0;
 
-       if (on) {
-               new = kmem_cache_alloc(fasync_cache, GFP_KERNEL);
-               if (!new)
-                       return -ENOMEM;
+       spin_lock(&filp->f_lock);
+       spin_lock(&fasync_lock);
+       for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) {
+               if (fa->fa_file != filp)
+                       continue;
+
+               spin_lock_irq(&fa->fa_lock);
+               fa->fa_file = NULL;
+               spin_unlock_irq(&fa->fa_lock);
+
+               *fp = fa->fa_next;
+               call_rcu(&fa->fa_rcu, fasync_free_rcu);
+               filp->f_flags &= ~FASYNC;
+               result = 1;
+               break;
        }
+       spin_unlock(&fasync_lock);
+       spin_unlock(&filp->f_lock);
+       return result;
+}
+
+struct fasync_struct *fasync_alloc(void)
+{
+       return kmem_cache_alloc(fasync_cache, GFP_KERNEL);
+}
+
+/*
+ * NOTE! This can be used only for unused fasync entries:
+ * entries that actually got inserted on the fasync list
+ * need to be released by rcu - see fasync_remove_entry.
+ */
+void fasync_free(struct fasync_struct *new)
+{
+       kmem_cache_free(fasync_cache, new);
+}
+
+/*
+ * Insert a new entry into the fasync list.  Return the pointer to the
+ * old one if we didn't use the new one.
+ *
+ * NOTE! It is very important that the FASYNC flag always
+ * match the state "is the filp on a fasync list".
+ */
+struct fasync_struct *fasync_insert_entry(int fd, struct file *filp, struct fasync_struct **fapp, struct fasync_struct *new)
+{
+        struct fasync_struct *fa, **fp;
 
-       /*
-        * We need to take f_lock first since it's not an IRQ-safe
-        * lock.
-        */
        spin_lock(&filp->f_lock);
-       write_lock_irq(&fasync_lock);
+       spin_lock(&fasync_lock);
        for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) {
-               if (fa->fa_file == filp) {
-                       if(on) {
-                               fa->fa_fd = fd;
-                               kmem_cache_free(fasync_cache, new);
-                       } else {
-                               *fp = fa->fa_next;
-                               kmem_cache_free(fasync_cache, fa);
-                               result = 1;
-                       }
-                       goto out;
-               }
-       }
+               if (fa->fa_file != filp)
+                       continue;
 
-       if (on) {
-               new->magic = FASYNC_MAGIC;
-               new->fa_file = filp;
-               new->fa_fd = fd;
-               new->fa_next = *fapp;
-               *fapp = new;
-               result = 1;
+               spin_lock_irq(&fa->fa_lock);
+               fa->fa_fd = fd;
+               spin_unlock_irq(&fa->fa_lock);
+               goto out;
        }
+
+       spin_lock_init(&new->fa_lock);
+       new->magic = FASYNC_MAGIC;
+       new->fa_file = filp;
+       new->fa_fd = fd;
+       new->fa_next = *fapp;
+       rcu_assign_pointer(*fapp, new);
+       filp->f_flags |= FASYNC;
+
 out:
-       if (on)
-               filp->f_flags |= FASYNC;
-       else
-               filp->f_flags &= ~FASYNC;
-       write_unlock_irq(&fasync_lock);
+       spin_unlock(&fasync_lock);
        spin_unlock(&filp->f_lock);
-       return result;
+       return fa;
+}
+
+/*
+ * Add a fasync entry. Return negative on error, positive if
+ * added, and zero if did nothing but change an existing one.
+ */
+static int fasync_add_entry(int fd, struct file *filp, struct fasync_struct **fapp)
+{
+       struct fasync_struct *new;
+
+       new = fasync_alloc();
+       if (!new)
+               return -ENOMEM;
+
+       /*
+        * fasync_insert_entry() returns the old (update) entry if
+        * it existed.
+        *
+        * So free the (unused) new entry and return 0 to let the
+        * caller know that we didn't add any new fasync entries.
+        */
+       if (fasync_insert_entry(fd, filp, fapp, new)) {
+               fasync_free(new);
+               return 0;
+       }
+
+       return 1;
+}
+
+/*
+ * fasync_helper() is used by almost all character device drivers
+ * to set up the fasync queue, and for regular files by the file
+ * lease code. It returns negative on error, 0 if it did no changes
+ * and positive if it added/deleted the entry.
+ */
+int fasync_helper(int fd, struct file * filp, int on, struct fasync_struct **fapp)
+{
+       if (!on)
+               return fasync_remove_entry(filp, fapp);
+       return fasync_add_entry(fd, filp, fapp);
 }
 
 EXPORT_SYMBOL(fasync_helper);
 
-void __kill_fasync(struct fasync_struct *fa, int sig, int band)
+/*
+ * rcu_read_lock() is held
+ */
+static void kill_fasync_rcu(struct fasync_struct *fa, int sig, int band)
 {
        while (fa) {
-               struct fown_struct * fown;
+               struct fown_struct *fown;
+               unsigned long flags;
+
                if (fa->magic != FASYNC_MAGIC) {
                        printk(KERN_ERR "kill_fasync: bad magic number in "
                               "fasync_struct!\n");
                        return;
                }
-               fown = &fa->fa_file->f_owner;
-               /* Don't send SIGURG to processes which have not set a
-                  queued signum: SIGURG has its own default signalling
-                  mechanism. */
-               if (!(sig == SIGURG && fown->signum == 0))
-                       send_sigio(fown, fa->fa_fd, band);
-               fa = fa->fa_next;
+               spin_lock_irqsave(&fa->fa_lock, flags);
+               if (fa->fa_file) {
+                       fown = &fa->fa_file->f_owner;
+                       /* Don't send SIGURG to processes which have not set a
+                          queued signum: SIGURG has its own default signalling
+                          mechanism. */
+                       if (!(sig == SIGURG && fown->signum == 0))
+                               send_sigio(fown, fa->fa_fd, band);
+               }
+               spin_unlock_irqrestore(&fa->fa_lock, flags);
+               fa = rcu_dereference(fa->fa_next);
        }
 }
 
-EXPORT_SYMBOL(__kill_fasync);
-
 void kill_fasync(struct fasync_struct **fp, int sig, int band)
 {
        /* First a quick test without locking: usually
         * the list is empty.
         */
        if (*fp) {
-               read_lock(&fasync_lock);
-               /* reread *fp after obtaining the lock */
-               __kill_fasync(*fp, sig, band);
-               read_unlock(&fasync_lock);
+               rcu_read_lock();
+               kill_fasync_rcu(rcu_dereference(*fp), sig, band);
+               rcu_read_unlock();
        }
 }
 EXPORT_SYMBOL(kill_fasync);
 
-static int __init fasync_init(void)
+static int __init fcntl_init(void)
 {
+       /*
+        * Please add new bits here to ensure allocation uniqueness.
+        * Exceptions: O_NONBLOCK is a two bit define on parisc; O_NDELAY
+        * is defined as O_NONBLOCK on some platforms and not on others.
+        */
+       BUILD_BUG_ON(19 - 1 /* for O_RDONLY being 0 */ != HWEIGHT32(
+               O_RDONLY        | O_WRONLY      | O_RDWR        |
+               O_CREAT         | O_EXCL        | O_NOCTTY      |
+               O_TRUNC         | O_APPEND      | /* O_NONBLOCK | */
+               __O_SYNC        | O_DSYNC       | FASYNC        |
+               O_DIRECT        | O_LARGEFILE   | O_DIRECTORY   |
+               O_NOFOLLOW      | O_NOATIME     | O_CLOEXEC     |
+               __FMODE_EXEC    | O_PATH
+               ));
+
        fasync_cache = kmem_cache_create("fasync_cache",
                sizeof(struct fasync_struct), 0, SLAB_PANIC, NULL);
        return 0;
 }
 
-module_init(fasync_init)
+module_init(fcntl_init)