mqueue: lift mnt_want_write() outside ->i_mutex, clean up a bit
Al Viro [Mon, 6 Aug 2012 06:18:17 +0000 (10:18 +0400)]
the way it abuses ->d_fsdata still needs to be killed, but that's
a separate story.

Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>

ipc/mqueue.c

index f8e54f5..9a08acc 100644 (file)
@@ -726,7 +726,6 @@ static struct file *do_create(struct ipc_namespace *ipc_ns, struct inode *dir,
                        struct mq_attr *attr)
 {
        const struct cred *cred = current_cred();
-       struct file *result;
        int ret;
 
        if (attr) {
@@ -748,21 +747,11 @@ static struct file *do_create(struct ipc_namespace *ipc_ns, struct inode *dir,
        }
 
        mode &= ~current_umask();
-       ret = mnt_want_write(path->mnt);
-       if (ret)
-               return ERR_PTR(ret);
        ret = vfs_create(dir, path->dentry, mode, true);
        path->dentry->d_fsdata = NULL;
-       if (!ret)
-               result = dentry_open(path, oflag, cred);
-       else
-               result = ERR_PTR(ret);
-       /*
-        * dentry_open() took a persistent mnt_want_write(),
-        * so we can now drop this one.
-        */
-       mnt_drop_write(path->mnt);
-       return result;
+       if (ret)
+               return ERR_PTR(ret);
+       return dentry_open(path, oflag, cred);
 }
 
 /* Opens existing queue */
@@ -788,7 +777,9 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode,
        struct mq_attr attr;
        int fd, error;
        struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns;
-       struct dentry *root = ipc_ns->mq_mnt->mnt_root;
+       struct vfsmount *mnt = ipc_ns->mq_mnt;
+       struct dentry *root = mnt->mnt_root;
+       int ro;
 
        if (u_attr && copy_from_user(&attr, u_attr, sizeof(struct mq_attr)))
                return -EFAULT;
@@ -802,6 +793,7 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode,
        if (fd < 0)
                goto out_putname;
 
+       ro = mnt_want_write(mnt);       /* we'll drop it in any case */
        error = 0;
        mutex_lock(&root->d_inode->i_mutex);
        path.dentry = lookup_one_len(name, root, strlen(name));
@@ -809,7 +801,7 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode,
                error = PTR_ERR(path.dentry);
                goto out_putfd;
        }
-       path.mnt = mntget(ipc_ns->mq_mnt);
+       path.mnt = mntget(mnt);
 
        if (oflag & O_CREAT) {
                if (path.dentry->d_inode) {     /* entry already exists */
@@ -820,6 +812,10 @@ SYSCALL_DEFINE4(mq_open, const char __user *, u_name, int, oflag, umode_t, mode,
                        }
                        filp = do_open(&path, oflag);
                } else {
+                       if (ro) {
+                               error = ro;
+                               goto out;
+                       }
                        filp = do_create(ipc_ns, root->d_inode,
                                                &path, oflag, mode,
                                                u_attr ? &attr : NULL);
@@ -845,6 +841,7 @@ out_putfd:
                fd = error;
        }
        mutex_unlock(&root->d_inode->i_mutex);
+       mnt_drop_write(mnt);
 out_putname:
        putname(name);
        return fd;
@@ -857,40 +854,38 @@ SYSCALL_DEFINE1(mq_unlink, const char __user *, u_name)
        struct dentry *dentry;
        struct inode *inode = NULL;
        struct ipc_namespace *ipc_ns = current->nsproxy->ipc_ns;
+       struct vfsmount *mnt = ipc_ns->mq_mnt;
 
        name = getname(u_name);
        if (IS_ERR(name))
                return PTR_ERR(name);
 
-       mutex_lock_nested(&ipc_ns->mq_mnt->mnt_root->d_inode->i_mutex,
-                       I_MUTEX_PARENT);
-       dentry = lookup_one_len(name, ipc_ns->mq_mnt->mnt_root, strlen(name));
+       err = mnt_want_write(mnt);
+       if (err)
+               goto out_name;
+       mutex_lock_nested(&mnt->mnt_root->d_inode->i_mutex, I_MUTEX_PARENT);
+       dentry = lookup_one_len(name, mnt->mnt_root, strlen(name));
        if (IS_ERR(dentry)) {
                err = PTR_ERR(dentry);
                goto out_unlock;
        }
 
-       if (!dentry->d_inode) {
-               err = -ENOENT;
-               goto out_err;
-       }
-
        inode = dentry->d_inode;
-       if (inode)
+       if (!inode) {
+               err = -ENOENT;
+       } else {
                ihold(inode);
-       err = mnt_want_write(ipc_ns->mq_mnt);
-       if (err)
-               goto out_err;
-       err = vfs_unlink(dentry->d_parent->d_inode, dentry);
-       mnt_drop_write(ipc_ns->mq_mnt);
-out_err:
+               err = vfs_unlink(dentry->d_parent->d_inode, dentry);
+       }
        dput(dentry);
 
 out_unlock:
-       mutex_unlock(&ipc_ns->mq_mnt->mnt_root->d_inode->i_mutex);
-       putname(name);
+       mutex_unlock(&mnt->mnt_root->d_inode->i_mutex);
        if (inode)
                iput(inode);
+       mnt_drop_write(mnt);
+out_name:
+       putname(name);
 
        return err;
 }