fuse: implement i_op->atomic_open()
authorMiklos Szeredi <mszeredi@suse.cz>
Tue, 5 Jun 2012 13:10:22 +0000 (15:10 +0200)
committerAl Viro <viro@zeniv.linux.org.uk>
Sat, 14 Jul 2012 12:33:14 +0000 (16:33 +0400)
Add an ->atomic_open implementation which replaces the atomic open+create
operation implemented via ->create.  No functionality is changed.

Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
Signed-off-by: Al Viro <viro@zeniv.linux.org.uk>
fs/fuse/dir.c

index f7543f72897e806f7826ad88a7ec5e8353e454c7..e42442f1da16a1d489cff8072d0486a4aefe9eef 100644 (file)
@@ -369,8 +369,9 @@ static struct dentry *fuse_lookup(struct inode *dir, struct dentry *entry,
  * If the filesystem doesn't support this, then fall back to separate
  * 'mknod' + 'open' requests.
  */
-static int fuse_create_open(struct inode *dir, struct dentry *entry,
-                           umode_t mode, struct nameidata *nd)
+static struct file *fuse_create_open(struct inode *dir, struct dentry *entry,
+                                    struct opendata *od, unsigned flags,
+                                    umode_t mode)
 {
        int err;
        struct inode *inode;
@@ -382,14 +383,11 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
        struct fuse_entry_out outentry;
        struct fuse_file *ff;
        struct file *file;
-       int flags = nd->intent.open.flags;
-
-       if (fc->no_create)
-               return -ENOSYS;
 
        forget = fuse_alloc_forget();
+       err = -ENOMEM;
        if (!forget)
-               return -ENOMEM;
+               goto out_err;
 
        req = fuse_get_req(fc);
        err = PTR_ERR(req);
@@ -428,11 +426,8 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
        req->out.args[1].value = &outopen;
        fuse_request_send(fc, req);
        err = req->out.h.error;
-       if (err) {
-               if (err == -ENOSYS)
-                       fc->no_create = 1;
+       if (err)
                goto out_free_ff;
-       }
 
        err = -EIO;
        if (!S_ISREG(outentry.attr.mode) || invalid_nodeid(outentry.nodeid))
@@ -448,28 +443,78 @@ static int fuse_create_open(struct inode *dir, struct dentry *entry,
                flags &= ~(O_CREAT | O_EXCL | O_TRUNC);
                fuse_sync_release(ff, flags);
                fuse_queue_forget(fc, forget, outentry.nodeid, 1);
-               return -ENOMEM;
+               err = -ENOMEM;
+               goto out_err;
        }
        kfree(forget);
        d_instantiate(entry, inode);
        fuse_change_entry_timeout(entry, &outentry);
        fuse_invalidate_attr(dir);
-       file = lookup_instantiate_filp(nd, entry, generic_file_open);
+       file = finish_open(od, entry, generic_file_open);
        if (IS_ERR(file)) {
                fuse_sync_release(ff, flags);
-               return PTR_ERR(file);
+       } else {
+               file->private_data = fuse_file_get(ff);
+               fuse_finish_open(inode, file);
        }
-       file->private_data = fuse_file_get(ff);
-       fuse_finish_open(inode, file);
-       return 0;
+       return file;
 
- out_free_ff:
+out_free_ff:
        fuse_file_free(ff);
- out_put_request:
+out_put_request:
        fuse_put_request(fc, req);
- out_put_forget_req:
+out_put_forget_req:
        kfree(forget);
-       return err;
+out_err:
+       return ERR_PTR(err);
+}
+
+static int fuse_mknod(struct inode *, struct dentry *, umode_t, dev_t);
+static struct file *fuse_atomic_open(struct inode *dir, struct dentry *entry,
+                                    struct opendata *od, unsigned flags,
+                                    umode_t mode, bool *created)
+{
+       int err;
+       struct fuse_conn *fc = get_fuse_conn(dir);
+       struct file *file;
+       struct dentry *res = NULL;
+
+       if (d_unhashed(entry)) {
+               res = fuse_lookup(dir, entry, NULL);
+               if (IS_ERR(res))
+                       return ERR_CAST(res);
+
+               if (res)
+                       entry = res;
+       }
+
+       if (!(flags & O_CREAT) || entry->d_inode)
+               goto no_open;
+
+       /* Only creates */
+       *created = true;
+
+       if (fc->no_create)
+               goto mknod;
+
+       file = fuse_create_open(dir, entry, od, flags, mode);
+       if (PTR_ERR(file) == -ENOSYS) {
+               fc->no_create = 1;
+               goto mknod;
+       }
+out_dput:
+       dput(res);
+       return file;
+
+mknod:
+       err = fuse_mknod(dir, entry, mode, 0);
+       if (err) {
+               file = ERR_PTR(err);
+               goto out_dput;
+       }
+no_open:
+       finish_no_open(od, res);
+       return NULL;
 }
 
 /*
@@ -573,12 +618,6 @@ static int fuse_mknod(struct inode *dir, struct dentry *entry, umode_t mode,
 static int fuse_create(struct inode *dir, struct dentry *entry, umode_t mode,
                       struct nameidata *nd)
 {
-       if (nd) {
-               int err = fuse_create_open(dir, entry, mode, nd);
-               if (err != -ENOSYS)
-                       return err;
-               /* Fall back on mknod */
-       }
        return fuse_mknod(dir, entry, mode, 0);
 }
 
@@ -1646,6 +1685,7 @@ static const struct inode_operations fuse_dir_inode_operations = {
        .link           = fuse_link,
        .setattr        = fuse_setattr,
        .create         = fuse_create,
+       .atomic_open    = fuse_atomic_open,
        .mknod          = fuse_mknod,
        .permission     = fuse_permission,
        .getattr        = fuse_getattr,