vfs: create vfs helper vfs_tmpfile()
authorAmir Goldstein <amir73il@gmail.com>
Tue, 17 Jan 2017 04:34:52 +0000 (06:34 +0200)
committerMiklos Szeredi <mszeredi@redhat.com>
Tue, 7 Feb 2017 14:05:04 +0000 (15:05 +0100)
Factor out some common vfs bits from do_tmpfile()
to be used by overlayfs for concurrent copy up.

Signed-off-by: Amir Goldstein <amir73il@gmail.com>
Cc: Al Viro <viro@zeniv.linux.org.uk>
Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
fs/namei.c
include/linux/fs.h

index ad74877e1442c0c9ea5fca87b065e59090088b10..7d87699c3e2ee18ebcc2879e556d5e5b74657ee2 100644 (file)
@@ -3353,13 +3353,50 @@ out:
        return error;
 }
 
+struct dentry *vfs_tmpfile(struct dentry *dentry, umode_t mode, int open_flag)
+{
+       static const struct qstr name = QSTR_INIT("/", 1);
+       struct dentry *child = NULL;
+       struct inode *dir = dentry->d_inode;
+       struct inode *inode;
+       int error;
+
+       /* we want directory to be writable */
+       error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
+       if (error)
+               goto out_err;
+       error = -EOPNOTSUPP;
+       if (!dir->i_op->tmpfile)
+               goto out_err;
+       error = -ENOMEM;
+       child = d_alloc(dentry, &name);
+       if (unlikely(!child))
+               goto out_err;
+       error = dir->i_op->tmpfile(dir, child, mode);
+       if (error)
+               goto out_err;
+       error = -ENOENT;
+       inode = child->d_inode;
+       if (unlikely(!inode))
+               goto out_err;
+       if (!(open_flag & O_EXCL)) {
+               spin_lock(&inode->i_lock);
+               inode->i_state |= I_LINKABLE;
+               spin_unlock(&inode->i_lock);
+       }
+       return child;
+
+out_err:
+       dput(child);
+       return ERR_PTR(error);
+}
+EXPORT_SYMBOL(vfs_tmpfile);
+
 static int do_tmpfile(struct nameidata *nd, unsigned flags,
                const struct open_flags *op,
                struct file *file, int *opened)
 {
-       static const struct qstr name = QSTR_INIT("/", 1);
        struct dentry *child;
-       struct inode *dir;
        struct path path;
        int error = path_lookupat(nd, flags | LOOKUP_DIRECTORY, &path);
        if (unlikely(error))
@@ -3367,25 +3404,12 @@ static int do_tmpfile(struct nameidata *nd, unsigned flags,
        error = mnt_want_write(path.mnt);
        if (unlikely(error))
                goto out;
-       dir = path.dentry->d_inode;
-       /* we want directory to be writable */
-       error = inode_permission(dir, MAY_WRITE | MAY_EXEC);
-       if (error)
-               goto out2;
-       if (!dir->i_op->tmpfile) {
-               error = -EOPNOTSUPP;
-               goto out2;
-       }
-       child = d_alloc(path.dentry, &name);
-       if (unlikely(!child)) {
-               error = -ENOMEM;
+       child = vfs_tmpfile(path.dentry, op->mode, op->open_flag);
+       error = PTR_ERR(child);
+       if (unlikely(IS_ERR(child)))
                goto out2;
-       }
        dput(path.dentry);
        path.dentry = child;
-       error = dir->i_op->tmpfile(dir, child, op->mode);
-       if (error)
-               goto out2;
        audit_inode(nd->name, child, 0);
        /* Don't check for other permissions, the inode was just created */
        error = may_open(&path, 0, op->open_flag);
@@ -3396,14 +3420,8 @@ static int do_tmpfile(struct nameidata *nd, unsigned flags,
        if (error)
                goto out2;
        error = open_check_o_direct(file);
-       if (error) {
+       if (error)
                fput(file);
-       } else if (!(op->open_flag & O_EXCL)) {
-               struct inode *inode = file_inode(file);
-               spin_lock(&inode->i_lock);
-               inode->i_state |= I_LINKABLE;
-               spin_unlock(&inode->i_lock);
-       }
 out2:
        mnt_drop_write(path.mnt);
 out:
index 2ba074328894cea30d6273a574417d789fae271d..4a7f3cc9edab4b29f3a0656e8d9504eee0c73dde 100644 (file)
@@ -1561,6 +1561,9 @@ extern int vfs_unlink(struct inode *, struct dentry *, struct inode **);
 extern int vfs_rename(struct inode *, struct dentry *, struct inode *, struct dentry *, struct inode **, unsigned int);
 extern int vfs_whiteout(struct inode *, struct dentry *);
 
+extern struct dentry *vfs_tmpfile(struct dentry *dentry, umode_t mode,
+                                 int open_flag);
+
 /*
  * VFS file helper functions.
  */