summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMiklos Szeredi <mszeredi@suse.cz>2014-07-15 18:02:19 +0200
committerMiklos Szeredi <mszeredi@suse.cz>2014-07-15 18:02:19 +0200
commitb49cf754f9d2db3a426aeb6111fa94d90270fb93 (patch)
tree18aca31da248e19e1072e6c40d21cb198d05c5ac
parent978d10a42b2cfccf6bfc4df1cfad480ac3bbcb3d (diff)
downloadfuse-b49cf754f9d2db3a426aeb6111fa94d90270fb93.tar.gz
libfuse: add flags to ->rename()
See renameat2() system call in linux-3.15 and later kernels.
-rw-r--r--ChangeLog3
-rwxr-xr-xexample/fusexmp.c5
-rwxr-xr-xexample/fusexmp_fh.c6
-rw-r--r--include/fuse.h4
-rw-r--r--include/fuse_kernel.h8
-rw-r--r--include/fuse_lowlevel.h3
-rw-r--r--lib/fuse.c63
-rwxr-xr-xlib/fuse_lowlevel.c17
-rw-r--r--lib/modules/iconv.c4
-rw-r--r--lib/modules/subdir.c4
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 <miklos@szeredi.hu>
* 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);