svcrdma: Shrink scope of spinlock on RQ CQ
[safe/jmp/linux-2.6] / fs / namei.c
index 7346e99..32fd965 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/capability.h>
 #include <linux/file.h>
 #include <linux/fcntl.h>
+#include <linux/device_cgroup.h>
 #include <asm/namei.h>
 #include <asm/uaccess.h>
 
@@ -281,6 +282,10 @@ int permission(struct inode *inode, int mask, struct nameidata *nd)
        if (retval)
                return retval;
 
+       retval = devcgroup_inode_permission(inode, mask);
+       if (retval)
+               return retval;
+
        return security_inode_permission(inode, mask, nd);
 }
 
@@ -1365,13 +1370,13 @@ static int __lookup_one_len(const char *name, struct qstr *this,
 }
 
 /**
- * lookup_one_len filesystem helper to lookup single pathname component
+ * lookup_one_len - filesystem helper to lookup single pathname component
  * @name:      pathname component to lookup
  * @base:      base directory to lookup from
  * @len:       maximum length @len should be interpreted to
  *
- * Note that this routine is purely a helper for filesystem useage and should
- * not be called by generic code.  Also note that by using this function to
+ * Note that this routine is purely a helper for filesystem usage and should
+ * not be called by generic code.  Also note that by using this function the
  * nameidata argument is passed to the filesystem methods and a filesystem
  * using this helper needs to be prepared for that.
  */
@@ -1623,8 +1628,7 @@ int may_open(struct nameidata *nd, int acc_mode, int flag)
                        return -EACCES;
 
                flag &= ~O_TRUNC;
-       } else if (IS_RDONLY(inode) && (acc_mode & MAY_WRITE))
-               return -EROFS;
+       }
 
        error = vfs_permission(nd, acc_mode);
        if (error)
@@ -1677,7 +1681,12 @@ int may_open(struct nameidata *nd, int acc_mode, int flag)
        return 0;
 }
 
-static int open_namei_create(struct nameidata *nd, struct path *path,
+/*
+ * Be careful about ever adding any more callers of this
+ * function.  Its flags must be in the namei format, not
+ * what get passed to sys_open().
+ */
+static int __open_namei_create(struct nameidata *nd, struct path *path,
                                int flag, int mode)
 {
        int error;
@@ -1696,26 +1705,56 @@ static int open_namei_create(struct nameidata *nd, struct path *path,
 }
 
 /*
- *     open_namei()
+ * Note that while the flag value (low two bits) for sys_open means:
+ *     00 - read-only
+ *     01 - write-only
+ *     10 - read-write
+ *     11 - special
+ * it is changed into
+ *     00 - no permissions needed
+ *     01 - read-permission
+ *     10 - write-permission
+ *     11 - read-write
+ * for the internal routines (ie open_namei()/follow_link() etc)
+ * This is more logical, and also allows the 00 "no perm needed"
+ * to be used for symlinks (where the permissions are checked
+ * later).
  *
- * namei for open - this is in fact almost the whole open-routine.
- *
- * Note that the low bits of "flag" aren't the same as in the open
- * system call - they are 00 - no permissions needed
- *                       01 - read permission needed
- *                       10 - write permission needed
- *                       11 - read/write permissions needed
- * which is a lot more logical, and also allows the "no perm" needed
- * for symlinks (where the permissions are checked later).
- * SMP-safe
+*/
+static inline int open_to_namei_flags(int flag)
+{
+       if ((flag+1) & O_ACCMODE)
+               flag++;
+       return flag;
+}
+
+static int open_will_write_to_fs(int flag, struct inode *inode)
+{
+       /*
+        * We'll never write to the fs underlying
+        * a device file.
+        */
+       if (special_file(inode->i_mode))
+               return 0;
+       return (flag & O_TRUNC);
+}
+
+/*
+ * Note that the low bits of the passed in "open_flag"
+ * are not the same as in the local variable "flag". See
+ * open_to_namei_flags() for more details.
  */
-int open_namei(int dfd, const char *pathname, int flag,
-               int mode, struct nameidata *nd)
+struct file *do_filp_open(int dfd, const char *pathname,
+               int open_flag, int mode)
 {
+       struct file *filp;
+       struct nameidata nd;
        int acc_mode, error;
        struct path path;
        struct dentry *dir;
        int count = 0;
+       int will_write;
+       int flag = open_to_namei_flags(open_flag);
 
        acc_mode = ACC_MODE(flag);
 
@@ -1733,18 +1772,19 @@ int open_namei(int dfd, const char *pathname, int flag,
         */
        if (!(flag & O_CREAT)) {
                error = path_lookup_open(dfd, pathname, lookup_flags(flag),
-                                        nd, flag);
+                                        &nd, flag);
                if (error)
-                       return error;
+                       return ERR_PTR(error);
                goto ok;
        }
 
        /*
         * Create - we need to know the parent.
         */
-       error = path_lookup_create(dfd,pathname,LOOKUP_PARENT,nd,flag,mode);
+       error = path_lookup_create(dfd, pathname, LOOKUP_PARENT,
+                                  &nd, flag, mode);
        if (error)
-               return error;
+               return ERR_PTR(error);
 
        /*
         * We have the parent and last component. First of all, check
@@ -1752,14 +1792,14 @@ int open_namei(int dfd, const char *pathname, int flag,
         * will not do.
         */
        error = -EISDIR;
-       if (nd->last_type != LAST_NORM || nd->last.name[nd->last.len])
+       if (nd.last_type != LAST_NORM || nd.last.name[nd.last.len])
                goto exit;
 
-       dir = nd->path.dentry;
-       nd->flags &= ~LOOKUP_PARENT;
+       dir = nd.path.dentry;
+       nd.flags &= ~LOOKUP_PARENT;
        mutex_lock(&dir->d_inode->i_mutex);
-       path.dentry = lookup_hash(nd);
-       path.mnt = nd->path.mnt;
+       path.dentry = lookup_hash(&nd);
+       path.mnt = nd.path.mnt;
 
 do_last:
        error = PTR_ERR(path.dentry);
@@ -1768,18 +1808,31 @@ do_last:
                goto exit;
        }
 
-       if (IS_ERR(nd->intent.open.file)) {
-               mutex_unlock(&dir->d_inode->i_mutex);
-               error = PTR_ERR(nd->intent.open.file);
-               goto exit_dput;
+       if (IS_ERR(nd.intent.open.file)) {
+               error = PTR_ERR(nd.intent.open.file);
+               goto exit_mutex_unlock;
        }
 
        /* Negative dentry, just create the file */
        if (!path.dentry->d_inode) {
-               error = open_namei_create(nd, &path, flag, mode);
+               /*
+                * This write is needed to ensure that a
+                * ro->rw transition does not occur between
+                * the time when the file is created and when
+                * a permanent write count is taken through
+                * the 'struct file' in nameidata_to_filp().
+                */
+               error = mnt_want_write(nd.path.mnt);
                if (error)
+                       goto exit_mutex_unlock;
+               error = __open_namei_create(&nd, &path, flag, mode);
+               if (error) {
+                       mnt_drop_write(nd.path.mnt);
                        goto exit;
-               return 0;
+               }
+               filp = nameidata_to_filp(&nd, open_flag);
+               mnt_drop_write(nd.path.mnt);
+               return filp;
        }
 
        /*
@@ -1804,23 +1857,52 @@ do_last:
        if (path.dentry->d_inode->i_op && path.dentry->d_inode->i_op->follow_link)
                goto do_link;
 
-       path_to_nameidata(&path, nd);
+       path_to_nameidata(&path, &nd);
        error = -EISDIR;
        if (path.dentry->d_inode && S_ISDIR(path.dentry->d_inode->i_mode))
                goto exit;
 ok:
-       error = may_open(nd, acc_mode, flag);
-       if (error)
+       /*
+        * Consider:
+        * 1. may_open() truncates a file
+        * 2. a rw->ro mount transition occurs
+        * 3. nameidata_to_filp() fails due to
+        *    the ro mount.
+        * That would be inconsistent, and should
+        * be avoided. Taking this mnt write here
+        * ensures that (2) can not occur.
+        */
+       will_write = open_will_write_to_fs(flag, nd.path.dentry->d_inode);
+       if (will_write) {
+               error = mnt_want_write(nd.path.mnt);
+               if (error)
+                       goto exit;
+       }
+       error = may_open(&nd, acc_mode, flag);
+       if (error) {
+               if (will_write)
+                       mnt_drop_write(nd.path.mnt);
                goto exit;
-       return 0;
+       }
+       filp = nameidata_to_filp(&nd, open_flag);
+       /*
+        * It is now safe to drop the mnt write
+        * because the filp has had a write taken
+        * on its behalf.
+        */
+       if (will_write)
+               mnt_drop_write(nd.path.mnt);
+       return filp;
 
+exit_mutex_unlock:
+       mutex_unlock(&dir->d_inode->i_mutex);
 exit_dput:
-       path_put_conditional(&path, nd);
+       path_put_conditional(&path, &nd);
 exit:
-       if (!IS_ERR(nd->intent.open.file))
-               release_open_intent(nd);
-       path_put(&nd->path);
-       return error;
+       if (!IS_ERR(nd.intent.open.file))
+               release_open_intent(&nd);
+       path_put(&nd.path);
+       return ERR_PTR(error);
 
 do_link:
        error = -ELOOP;
@@ -1836,43 +1918,60 @@ do_link:
         * stored in nd->last.name and we will have to putname() it when we
         * are done. Procfs-like symlinks just set LAST_BIND.
         */
-       nd->flags |= LOOKUP_PARENT;
-       error = security_inode_follow_link(path.dentry, nd);
+       nd.flags |= LOOKUP_PARENT;
+       error = security_inode_follow_link(path.dentry, &nd);
        if (error)
                goto exit_dput;
-       error = __do_follow_link(&path, nd);
+       error = __do_follow_link(&path, &nd);
        if (error) {
                /* Does someone understand code flow here? Or it is only
                 * me so stupid? Anathema to whoever designed this non-sense
                 * with "intent.open".
                 */
-               release_open_intent(nd);
-               return error;
+               release_open_intent(&nd);
+               return ERR_PTR(error);
        }
-       nd->flags &= ~LOOKUP_PARENT;
-       if (nd->last_type == LAST_BIND)
+       nd.flags &= ~LOOKUP_PARENT;
+       if (nd.last_type == LAST_BIND)
                goto ok;
        error = -EISDIR;
-       if (nd->last_type != LAST_NORM)
+       if (nd.last_type != LAST_NORM)
                goto exit;
-       if (nd->last.name[nd->last.len]) {
-               __putname(nd->last.name);
+       if (nd.last.name[nd.last.len]) {
+               __putname(nd.last.name);
                goto exit;
        }
        error = -ELOOP;
        if (count++==32) {
-               __putname(nd->last.name);
+               __putname(nd.last.name);
                goto exit;
        }
-       dir = nd->path.dentry;
+       dir = nd.path.dentry;
        mutex_lock(&dir->d_inode->i_mutex);
-       path.dentry = lookup_hash(nd);
-       path.mnt = nd->path.mnt;
-       __putname(nd->last.name);
+       path.dentry = lookup_hash(&nd);
+       path.mnt = nd.path.mnt;
+       __putname(nd.last.name);
        goto do_last;
 }
 
 /**
+ * filp_open - open file and return file pointer
+ *
+ * @filename:  path to open
+ * @flags:     open flags as per the open(2) second argument
+ * @mode:      mode for the new file if O_CREAT is set, else ignored
+ *
+ * This is the helper to open a file from kernelspace if you really
+ * have to.  But in generally you should not do this, so please move
+ * along, nothing to see here..
+ */
+struct file *filp_open(const char *filename, int flags, int mode)
+{
+       return do_filp_open(AT_FDCWD, filename, flags, mode);
+}
+EXPORT_SYMBOL(filp_open);
+
+/**
  * lookup_create - lookup a dentry, creating it if it doesn't exist
  * @nd: nameidata info
  * @is_dir: directory flag
@@ -1934,6 +2033,10 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
        if (!dir->i_op || !dir->i_op->mknod)
                return -EPERM;
 
+       error = devcgroup_inode_mknod(mode, dev);
+       if (error)
+               return error;
+
        error = security_inode_mknod(dir, dentry, mode, dev);
        if (error)
                return error;
@@ -1945,6 +2048,23 @@ int vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
        return error;
 }
 
+static int may_mknod(mode_t mode)
+{
+       switch (mode & S_IFMT) {
+       case S_IFREG:
+       case S_IFCHR:
+       case S_IFBLK:
+       case S_IFIFO:
+       case S_IFSOCK:
+       case 0: /* zero mode translates to S_IFREG */
+               return 0;
+       case S_IFDIR:
+               return -EPERM;
+       default:
+               return -EINVAL;
+       }
+}
+
 asmlinkage long sys_mknodat(int dfd, const char __user *filename, int mode,
                                unsigned dev)
 {
@@ -1963,12 +2083,19 @@ asmlinkage long sys_mknodat(int dfd, const char __user *filename, int mode,
        if (error)
                goto out;
        dentry = lookup_create(&nd, 0);
-       error = PTR_ERR(dentry);
-
+       if (IS_ERR(dentry)) {
+               error = PTR_ERR(dentry);
+               goto out_unlock;
+       }
        if (!IS_POSIXACL(nd.path.dentry->d_inode))
                mode &= ~current->fs->umask;
-       if (!IS_ERR(dentry)) {
-               switch (mode & S_IFMT) {
+       error = may_mknod(mode);
+       if (error)
+               goto out_dput;
+       error = mnt_want_write(nd.path.mnt);
+       if (error)
+               goto out_dput;
+       switch (mode & S_IFMT) {
                case 0: case S_IFREG:
                        error = vfs_create(nd.path.dentry->d_inode,dentry,mode,&nd);
                        break;
@@ -1979,14 +2106,11 @@ asmlinkage long sys_mknodat(int dfd, const char __user *filename, int mode,
                case S_IFIFO: case S_IFSOCK:
                        error = vfs_mknod(nd.path.dentry->d_inode,dentry,mode,0);
                        break;
-               case S_IFDIR:
-                       error = -EPERM;
-                       break;
-               default:
-                       error = -EINVAL;
-               }
-               dput(dentry);
        }
+       mnt_drop_write(nd.path.mnt);
+out_dput:
+       dput(dentry);
+out_unlock:
        mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
        path_put(&nd.path);
 out:
@@ -2044,7 +2168,12 @@ asmlinkage long sys_mkdirat(int dfd, const char __user *pathname, int mode)
 
        if (!IS_POSIXACL(nd.path.dentry->d_inode))
                mode &= ~current->fs->umask;
+       error = mnt_want_write(nd.path.mnt);
+       if (error)
+               goto out_dput;
        error = vfs_mkdir(nd.path.dentry->d_inode, dentry, mode);
+       mnt_drop_write(nd.path.mnt);
+out_dput:
        dput(dentry);
 out_unlock:
        mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
@@ -2151,7 +2280,12 @@ static long do_rmdir(int dfd, const char __user *pathname)
        error = PTR_ERR(dentry);
        if (IS_ERR(dentry))
                goto exit2;
+       error = mnt_want_write(nd.path.mnt);
+       if (error)
+               goto exit3;
        error = vfs_rmdir(nd.path.dentry->d_inode, dentry);
+       mnt_drop_write(nd.path.mnt);
+exit3:
        dput(dentry);
 exit2:
        mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
@@ -2232,7 +2366,11 @@ static long do_unlinkat(int dfd, const char __user *pathname)
                inode = dentry->d_inode;
                if (inode)
                        atomic_inc(&inode->i_count);
+               error = mnt_want_write(nd.path.mnt);
+               if (error)
+                       goto exit2;
                error = vfs_unlink(nd.path.dentry->d_inode, dentry);
+               mnt_drop_write(nd.path.mnt);
        exit2:
                dput(dentry);
        }
@@ -2313,7 +2451,12 @@ asmlinkage long sys_symlinkat(const char __user *oldname,
        if (IS_ERR(dentry))
                goto out_unlock;
 
+       error = mnt_want_write(nd.path.mnt);
+       if (error)
+               goto out_dput;
        error = vfs_symlink(nd.path.dentry->d_inode, dentry, from, S_IALLUGO);
+       mnt_drop_write(nd.path.mnt);
+out_dput:
        dput(dentry);
 out_unlock:
        mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
@@ -2408,7 +2551,12 @@ asmlinkage long sys_linkat(int olddfd, const char __user *oldname,
        error = PTR_ERR(new_dentry);
        if (IS_ERR(new_dentry))
                goto out_unlock;
+       error = mnt_want_write(nd.path.mnt);
+       if (error)
+               goto out_dput;
        error = vfs_link(old_nd.path.dentry, nd.path.dentry->d_inode, new_dentry);
+       mnt_drop_write(nd.path.mnt);
+out_dput:
        dput(new_dentry);
 out_unlock:
        mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
@@ -2634,8 +2782,12 @@ static int do_rename(int olddfd, const char *oldname,
        if (new_dentry == trap)
                goto exit5;
 
+       error = mnt_want_write(oldnd.path.mnt);
+       if (error)
+               goto exit5;
        error = vfs_rename(old_dir->d_inode, old_dentry,
                                   new_dir->d_inode, new_dentry);
+       mnt_drop_write(oldnd.path.mnt);
 exit5:
        dput(new_dentry);
 exit4: