From b49cf754f9d2db3a426aeb6111fa94d90270fb93 Mon Sep 17 00:00:00 2001 From: Miklos Szeredi Date: Tue, 15 Jul 2014 18:02:19 +0200 Subject: [PATCH] libfuse: add flags to ->rename() See renameat2() system call in linux-3.15 and later kernels. --- ChangeLog | 3 ++ example/fusexmp.c | 5 +++- example/fusexmp_fh.c | 6 +++- include/fuse.h | 4 +-- include/fuse_kernel.h | 8 ++++++ include/fuse_lowlevel.h | 3 +- lib/fuse.c | 63 ++++++++++++++++++++++++++++++++++------- lib/fuse_lowlevel.c | 17 ++++++++++- lib/modules/iconv.c | 4 +-- lib/modules/subdir.c | 4 +-- 10 files changed, 97 insertions(+), 20 deletions(-) diff --git a/ChangeLog b/ChangeLog index a5f611d..bde75cc 100644 --- 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 * Initilaize stat buffer passed to ->getattr() and ->fgetattr() to diff --git a/example/fusexmp.c b/example/fusexmp.c index fe20f57..eae3562 100755 --- a/example/fusexmp.c +++ b/example/fusexmp.c @@ -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; diff --git a/example/fusexmp_fh.c b/example/fusexmp_fh.c index 531438f..84fce3f 100755 --- a/example/fusexmp_fh.c +++ b/example/fusexmp_fh.c @@ -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; diff --git a/include/fuse.h b/include/fuse.h index edece77..bf86bda 100644 --- a/include/fuse.h +++ b/include/fuse.h @@ -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, diff --git a/include/fuse_kernel.h b/include/fuse_kernel.h index e86a21a..40b5ca8 100644 --- a/include/fuse_kernel.h +++ b/include/fuse_kernel.h @@ -100,6 +100,7 @@ * - 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; }; diff --git a/include/fuse_lowlevel.h b/include/fuse_lowlevel.h index 8eef9b9..b31868f 100644 --- a/include/fuse_lowlevel.h +++ b/include/fuse_lowlevel.h @@ -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 diff --git a/lib/fuse.c b/lib/fuse.c index ca6d3cd..77da446 100644 --- a/lib/fuse.c +++ b/lib/fuse.c @@ -43,6 +43,10 @@ #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); diff --git a/lib/fuse_lowlevel.c b/lib/fuse_lowlevel.c index 9131811..1911863 100755 --- a/lib/fuse_lowlevel.c +++ b/lib/fuse_lowlevel.c @@ -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" }, }; diff --git a/lib/modules/iconv.c b/lib/modules/iconv.c index 7d96c17..174e2b9 100644 --- a/lib/modules/iconv.c +++ b/lib/modules/iconv.c @@ -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); diff --git a/lib/modules/subdir.c b/lib/modules/subdir.c index 05eccdf..a039b3c 100644 --- a/lib/modules/subdir.c +++ b/lib/modules/subdir.c @@ -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); -- 2.30.2