libfuse: add flags to ->rename()
authorMiklos Szeredi <mszeredi@suse.cz>
Tue, 15 Jul 2014 16:02:19 +0000 (18:02 +0200)
committerMiklos Szeredi <mszeredi@suse.cz>
Tue, 15 Jul 2014 16:02:19 +0000 (18:02 +0200)
See renameat2() system call in linux-3.15 and later kernels.

ChangeLog
example/fusexmp.c
example/fusexmp_fh.c
include/fuse.h
include/fuse_kernel.h
include/fuse_lowlevel.h
lib/fuse.c
lib/fuse_lowlevel.c
lib/modules/iconv.c
lib/modules/subdir.c

index a5f611d8469d0b899b7a0048d422d99ab26d6da1..bde75cced90c037f1931be92b7d2f9ed75cfdc6c 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -14,6 +14,9 @@
 
        * libfuse: allow setting ctime in ->setattr()
 
+       * libfuse: add flags to ->rename().  See renameat2() system call
+       in linux-3.15 and later kernels.
+
 2014-03-26  Miklos Szeredi <miklos@szeredi.hu>
 
        * Initilaize stat buffer passed to ->getattr() and ->fgetattr() to
index fe20f5749eded568a867705ce38086347a381fa7..eae35623e5a6b1322ee70e2553d816ff657f6a0d 100755 (executable)
@@ -172,10 +172,13 @@ static int xmp_symlink(const char *from, const char *to)
        return 0;
 }
 
-static int xmp_rename(const char *from, const char *to)
+static int xmp_rename(const char *from, const char *to, unsigned int flags)
 {
        int res;
 
+       if (flags)
+               return -EINVAL;
+
        res = rename(from, to);
        if (res == -1)
                return -errno;
index 531438f0701241eff57f74eb48f61ec179007949..84fce3f4b84bacf99959634c5d84a0065e30109e 100755 (executable)
@@ -242,10 +242,14 @@ static int xmp_symlink(const char *from, const char *to)
        return 0;
 }
 
-static int xmp_rename(const char *from, const char *to)
+static int xmp_rename(const char *from, const char *to, unsigned int flags)
 {
        int res;
 
+       /* When we have renameat2() in libc, then we can implement flags */
+       if (flags)
+               return -EINVAL;
+
        res = rename(from, to);
        if (res == -1)
                return -errno;
index edece7786fafa7fb4f4e07f88dce7e430331f7ba..bf86bda972ddcc0165527312071d36fc173c2029 100644 (file)
@@ -162,7 +162,7 @@ struct fuse_operations {
        int (*symlink) (const char *, const char *);
 
        /** Rename a file */
-       int (*rename) (const char *, const char *);
+       int (*rename) (const char *, const char *, unsigned int);
 
        /** Create a hard link to a file */
        int (*link) (const char *, const char *);
@@ -808,7 +808,7 @@ int fuse_fs_getattr(struct fuse_fs *fs, const char *path, struct stat *buf);
 int fuse_fs_fgetattr(struct fuse_fs *fs, const char *path, struct stat *buf,
                     struct fuse_file_info *fi);
 int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath,
-                  const char *newpath);
+                  const char *newpath, unsigned int flags);
 int fuse_fs_unlink(struct fuse_fs *fs, const char *path);
 int fuse_fs_rmdir(struct fuse_fs *fs, const char *path);
 int fuse_fs_symlink(struct fuse_fs *fs, const char *linkname,
index e86a21acef75c485773b19fd48e9178f9dc83632..40b5ca8a1b1f3028e5e03c5b3372f98e39437422 100644 (file)
  *  - add reserved space to fuse_init_out
  *  - add FATTR_CTIME
  *  - add ctime and ctimensec to fuse_setattr_in
+ *  - add FUSE_RENAME2 request
  */
 
 #ifndef _LINUX_FUSE_H
@@ -353,6 +354,7 @@ enum fuse_opcode {
        FUSE_BATCH_FORGET  = 42,
        FUSE_FALLOCATE     = 43,
        FUSE_READDIRPLUS   = 44,
+       FUSE_RENAME2       = 45,
 
        /* CUSE specific operations */
        CUSE_INIT          = 4096,
@@ -431,6 +433,12 @@ struct fuse_rename_in {
        uint64_t        newdir;
 };
 
+struct fuse_rename2_in {
+       uint64_t        newdir;
+       uint32_t        flags;
+       uint32_t        padding;
+};
+
 struct fuse_link_in {
        uint64_t        oldnodeid;
 };
index 8eef9b91e099e46b3a26fb4a3c1e062a8bf51d69..b31868f54ed100fcabda94dfb48937186480778d 100644 (file)
@@ -393,7 +393,8 @@ struct fuse_lowlevel_ops {
         * @param newname new name
         */
        void (*rename) (fuse_req_t req, fuse_ino_t parent, const char *name,
-                       fuse_ino_t newparent, const char *newname);
+                       fuse_ino_t newparent, const char *newname,
+                       unsigned int flags);
 
        /**
         * Create a hard link
index ca6d3cd04a130661e90f7735e7ac300d39798b10..77da44609b18ce4f36e05c359615365a7419e799 100644 (file)
 #undef FUSE_NODE_SLAB
 #endif
 
+#ifndef RENAME_EXCHANGE
+#define RENAME_EXCHANGE                (1 << 1)        /* Exchange source and dest */
+#endif
+
 #define FUSE_DEFAULT_INTR_SIGNAL SIGUSR1
 
 #define FUSE_UNKNOWN_INO 0xffffffff
@@ -1414,6 +1418,37 @@ out:
        return err;
 }
 
+static int exchange_node(struct fuse *f, fuse_ino_t olddir, const char *oldname,
+                        fuse_ino_t newdir, const char *newname)
+{
+       struct node *oldnode;
+       struct node *newnode;
+       int err;
+
+       pthread_mutex_lock(&f->lock);
+       oldnode  = lookup_node(f, olddir, oldname);
+       newnode  = lookup_node(f, newdir, newname);
+
+       if (oldnode)
+               unhash_name(f, oldnode);
+       if (newnode)
+               unhash_name(f, newnode);
+
+       err = -ENOMEM;
+       if (oldnode) {
+               if (hash_name(f, oldnode, newdir, newname) == -1)
+                       goto out;
+       }
+       if (newnode) {
+               if (hash_name(f, newnode, olddir, oldname) == -1)
+                       goto out;
+       }
+       err = 0;
+out:
+       pthread_mutex_unlock(&f->lock);
+       return err;
+}
+
 static void set_stat(struct fuse *f, fuse_ino_t nodeid, struct stat *stbuf)
 {
        if (!f->conf.use_ino)
@@ -1533,14 +1568,15 @@ int fuse_fs_fgetattr(struct fuse_fs *fs, const char *path, struct stat *buf,
 }
 
 int fuse_fs_rename(struct fuse_fs *fs, const char *oldpath,
-                  const char *newpath)
+                  const char *newpath, unsigned int flags)
 {
        fuse_get_context()->private_data = fs->user_data;
        if (fs->op.rename) {
                if (fs->debug)
-                       fprintf(stderr, "rename %s %s\n", oldpath, newpath);
+                       fprintf(stderr, "rename %s %s 0x%x\n", oldpath, newpath,
+                               flags);
 
-               return fs->op.rename(oldpath, newpath);
+               return fs->op.rename(oldpath, newpath, flags);
        } else {
                return -ENOSYS;
        }
@@ -2298,7 +2334,7 @@ static int hide_node(struct fuse *f, const char *oldpath,
 
        newpath = hidden_name(f, dir, oldname, newname, sizeof(newname));
        if (newpath) {
-               err = fuse_fs_rename(f->fs, oldpath, newpath);
+               err = fuse_fs_rename(f->fs, oldpath, newpath, 0);
                if (!err)
                        err = rename_node(f, dir, oldname, dir, newname, 1);
                free(newpath);
@@ -2912,7 +2948,7 @@ static void fuse_lib_symlink(fuse_req_t req, const char *linkname,
 
 static void fuse_lib_rename(fuse_req_t req, fuse_ino_t olddir,
                            const char *oldname, fuse_ino_t newdir,
-                           const char *newname)
+                           const char *newname, unsigned int flags)
 {
        struct fuse *f = req_fuse_prepare(req);
        char *oldpath;
@@ -2927,13 +2963,20 @@ static void fuse_lib_rename(fuse_req_t req, fuse_ino_t olddir,
                struct fuse_intr_data d;
                err = 0;
                fuse_prepare_interrupt(f, req, &d);
-               if (!f->conf.hard_remove && is_open(f, newdir, newname))
+               if (!f->conf.hard_remove && !(flags & RENAME_EXCHANGE) &&
+                   is_open(f, newdir, newname))
                        err = hide_node(f, newpath, newdir, newname);
                if (!err) {
-                       err = fuse_fs_rename(f->fs, oldpath, newpath);
-                       if (!err)
-                               err = rename_node(f, olddir, oldname, newdir,
-                                                 newname, 0);
+                       err = fuse_fs_rename(f->fs, oldpath, newpath, flags);
+                       if (!err) {
+                               if (flags & RENAME_EXCHANGE) {
+                                       err = exchange_node(f, olddir, oldname,
+                                                           newdir, newname);
+                               } else {
+                                       err = rename_node(f, olddir, oldname,
+                                                         newdir, newname, 0);
+                               }
+                       }
                }
                fuse_finish_interrupt(f, req, &d);
                free_path2(f, olddir, newdir, wnode1, wnode2, oldpath, newpath);
index 91318119848f86e55a769c22705c4d7b04126875..191186356eed369d66230874f33754b8ca773454 100755 (executable)
@@ -1270,7 +1270,21 @@ static void do_rename(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
        char *newname = oldname + strlen(oldname) + 1;
 
        if (req->f->op.rename)
-               req->f->op.rename(req, nodeid, oldname, arg->newdir, newname);
+               req->f->op.rename(req, nodeid, oldname, arg->newdir, newname,
+                                 0);
+       else
+               fuse_reply_err(req, ENOSYS);
+}
+
+static void do_rename2(fuse_req_t req, fuse_ino_t nodeid, const void *inarg)
+{
+       struct fuse_rename2_in *arg = (struct fuse_rename2_in *) inarg;
+       char *oldname = PARAM(arg);
+       char *newname = oldname + strlen(oldname) + 1;
+
+       if (req->f->op.rename)
+               req->f->op.rename(req, nodeid, oldname, arg->newdir, newname,
+                                 arg->flags);
        else
                fuse_reply_err(req, ENOSYS);
 }
@@ -2431,6 +2445,7 @@ static struct {
        [FUSE_NOTIFY_REPLY] = { (void *) 1,    "NOTIFY_REPLY" },
        [FUSE_BATCH_FORGET] = { do_batch_forget, "BATCH_FORGET" },
        [FUSE_READDIRPLUS] = { do_readdirplus,  "READDIRPLUS"},
+       [FUSE_RENAME2]     = { do_rename2,      "RENAME2"    },
        [CUSE_INIT]        = { cuse_lowlevel_init, "CUSE_INIT"   },
 };
 
index 7d96c1755a99b3d1211efe9e835d822c30175374..174e2b9a008b292eb1c2401b8ea4a8a65fb139c8 100644 (file)
@@ -281,7 +281,7 @@ static int iconv_symlink(const char *from, const char *to)
        return err;
 }
 
-static int iconv_rename(const char *from, const char *to)
+static int iconv_rename(const char *from, const char *to, unsigned int flags)
 {
        struct iconv *ic = iconv_get();
        char *newfrom;
@@ -290,7 +290,7 @@ static int iconv_rename(const char *from, const char *to)
        if (!err) {
                err = iconv_convpath(ic, to, &newto, 0);
                if (!err) {
-                       err = fuse_fs_rename(ic->next, newfrom, newto);
+                       err = fuse_fs_rename(ic->next, newfrom, newto, flags);
                        free(newto);
                }
                free(newfrom);
index 05eccdf1779aeea43f73637a5bc75376286bebc8..a039b3cca6ae854de44307104556c3bb0d05bd0b 100644 (file)
@@ -267,7 +267,7 @@ static int subdir_symlink(const char *from, const char *path)
        return err;
 }
 
-static int subdir_rename(const char *from, const char *to)
+static int subdir_rename(const char *from, const char *to, unsigned int flags)
 {
        struct subdir *d = subdir_get();
        char *newfrom;
@@ -276,7 +276,7 @@ static int subdir_rename(const char *from, const char *to)
        if (!err) {
                err = subdir_addpath(d, to, &newto);
                if (!err) {
-                       err = fuse_fs_rename(d->next, newfrom, newto);
+                       err = fuse_fs_rename(d->next, newfrom, newto, flags);
                        free(newto);
                }
                free(newfrom);