fuse: add renameat2 support
[firefly-linux-kernel-4.4.55.git] / fs / fuse / dir.c
index 843dcf1222eb0101792c905cbc8fb2c0897b9193..42198359fa1b472557e44f325e9f55c305237e99 100644 (file)
@@ -752,23 +752,26 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry)
        return err;
 }
 
-static int fuse_rename(struct inode *olddir, struct dentry *oldent,
-                      struct inode *newdir, struct dentry *newent)
+static int fuse_rename_common(struct inode *olddir, struct dentry *oldent,
+                             struct inode *newdir, struct dentry *newent,
+                             unsigned int flags, int opcode, size_t argsize)
 {
        int err;
-       struct fuse_rename_in inarg;
+       struct fuse_rename2_in inarg;
        struct fuse_conn *fc = get_fuse_conn(olddir);
-       struct fuse_req *req = fuse_get_req_nopages(fc);
+       struct fuse_req *req;
 
+       req = fuse_get_req_nopages(fc);
        if (IS_ERR(req))
                return PTR_ERR(req);
 
-       memset(&inarg, 0, sizeof(inarg));
+       memset(&inarg, 0, argsize);
        inarg.newdir = get_node_id(newdir);
-       req->in.h.opcode = FUSE_RENAME;
+       inarg.flags = flags;
+       req->in.h.opcode = opcode;
        req->in.h.nodeid = get_node_id(olddir);
        req->in.numargs = 3;
-       req->in.args[0].size = sizeof(inarg);
+       req->in.args[0].size = argsize;
        req->in.args[0].value = &inarg;
        req->in.args[1].size = oldent->d_name.len + 1;
        req->in.args[1].value = oldent->d_name.name;
@@ -782,12 +785,17 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent,
                fuse_invalidate_attr(oldent->d_inode);
                fuse_update_ctime(oldent->d_inode);
 
+               if (flags & RENAME_EXCHANGE) {
+                       fuse_invalidate_attr(newent->d_inode);
+                       fuse_update_ctime(newent->d_inode);
+               }
+
                fuse_invalidate_attr(olddir);
                if (olddir != newdir)
                        fuse_invalidate_attr(newdir);
 
                /* newent will end up negative */
-               if (newent->d_inode) {
+               if (!(flags & RENAME_EXCHANGE) && newent->d_inode) {
                        fuse_invalidate_attr(newent->d_inode);
                        fuse_invalidate_entry_cache(newent);
                        fuse_update_ctime(newent->d_inode);
@@ -806,6 +814,36 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent,
        return err;
 }
 
+static int fuse_rename(struct inode *olddir, struct dentry *oldent,
+                      struct inode *newdir, struct dentry *newent)
+{
+       return fuse_rename_common(olddir, oldent, newdir, newent, 0,
+                                 FUSE_RENAME, sizeof(struct fuse_rename_in));
+}
+
+static int fuse_rename2(struct inode *olddir, struct dentry *oldent,
+                       struct inode *newdir, struct dentry *newent,
+                       unsigned int flags)
+{
+       struct fuse_conn *fc = get_fuse_conn(olddir);
+       int err;
+
+       if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE))
+               return -EINVAL;
+
+       if (fc->no_rename2 || fc->minor < 23)
+               return -EINVAL;
+
+       err = fuse_rename_common(olddir, oldent, newdir, newent, flags,
+                                FUSE_RENAME2, sizeof(struct fuse_rename2_in));
+       if (err == -ENOSYS) {
+               fc->no_rename2 = 1;
+               err = -EINVAL;
+       }
+       return err;
+
+}
+
 static int fuse_link(struct dentry *entry, struct inode *newdir,
                     struct dentry *newent)
 {
@@ -1980,6 +2018,7 @@ static const struct inode_operations fuse_dir_inode_operations = {
        .unlink         = fuse_unlink,
        .rmdir          = fuse_rmdir,
        .rename         = fuse_rename,
+       .rename2        = fuse_rename2,
        .link           = fuse_link,
        .setattr        = fuse_setattr,
        .create         = fuse_create,