diff options
author | Miklos Szeredi <mszeredi@suse.cz> | 2014-04-28 10:43:44 -0400 |
---|---|---|
committer | Miklos Szeredi <mszeredi@suse.cz> | 2014-04-28 10:43:44 -0400 |
commit | 1560c974dcd40a8d3f193283acd7cc6aee13dc13 (patch) | |
tree | 844409c63c329b8fdee462ead3ff87aac3201405 | |
parent | 4ace1f85a7cdab5453c2e12029ff978dd4cd6155 (diff) |
fuse: add renameat2 support
Support RENAME_EXCHANGE and RENAME_NOREPLACE flags on the userspace ABI.
Signed-off-by: Miklos Szeredi <mszeredi@suse.cz>
-rw-r--r-- | fs/fuse/dir.c | 55 | ||||
-rw-r--r-- | fs/fuse/fuse_i.h | 3 | ||||
-rw-r--r-- | include/uapi/linux/fuse.h | 8 |
3 files changed, 58 insertions, 8 deletions
diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 843dcf1222eb..42198359fa1b 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c | |||
@@ -752,23 +752,26 @@ static int fuse_rmdir(struct inode *dir, struct dentry *entry) | |||
752 | return err; | 752 | return err; |
753 | } | 753 | } |
754 | 754 | ||
755 | static int fuse_rename(struct inode *olddir, struct dentry *oldent, | 755 | static int fuse_rename_common(struct inode *olddir, struct dentry *oldent, |
756 | struct inode *newdir, struct dentry *newent) | 756 | struct inode *newdir, struct dentry *newent, |
757 | unsigned int flags, int opcode, size_t argsize) | ||
757 | { | 758 | { |
758 | int err; | 759 | int err; |
759 | struct fuse_rename_in inarg; | 760 | struct fuse_rename2_in inarg; |
760 | struct fuse_conn *fc = get_fuse_conn(olddir); | 761 | struct fuse_conn *fc = get_fuse_conn(olddir); |
761 | struct fuse_req *req = fuse_get_req_nopages(fc); | 762 | struct fuse_req *req; |
762 | 763 | ||
764 | req = fuse_get_req_nopages(fc); | ||
763 | if (IS_ERR(req)) | 765 | if (IS_ERR(req)) |
764 | return PTR_ERR(req); | 766 | return PTR_ERR(req); |
765 | 767 | ||
766 | memset(&inarg, 0, sizeof(inarg)); | 768 | memset(&inarg, 0, argsize); |
767 | inarg.newdir = get_node_id(newdir); | 769 | inarg.newdir = get_node_id(newdir); |
768 | req->in.h.opcode = FUSE_RENAME; | 770 | inarg.flags = flags; |
771 | req->in.h.opcode = opcode; | ||
769 | req->in.h.nodeid = get_node_id(olddir); | 772 | req->in.h.nodeid = get_node_id(olddir); |
770 | req->in.numargs = 3; | 773 | req->in.numargs = 3; |
771 | req->in.args[0].size = sizeof(inarg); | 774 | req->in.args[0].size = argsize; |
772 | req->in.args[0].value = &inarg; | 775 | req->in.args[0].value = &inarg; |
773 | req->in.args[1].size = oldent->d_name.len + 1; | 776 | req->in.args[1].size = oldent->d_name.len + 1; |
774 | req->in.args[1].value = oldent->d_name.name; | 777 | req->in.args[1].value = oldent->d_name.name; |
@@ -782,12 +785,17 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent, | |||
782 | fuse_invalidate_attr(oldent->d_inode); | 785 | fuse_invalidate_attr(oldent->d_inode); |
783 | fuse_update_ctime(oldent->d_inode); | 786 | fuse_update_ctime(oldent->d_inode); |
784 | 787 | ||
788 | if (flags & RENAME_EXCHANGE) { | ||
789 | fuse_invalidate_attr(newent->d_inode); | ||
790 | fuse_update_ctime(newent->d_inode); | ||
791 | } | ||
792 | |||
785 | fuse_invalidate_attr(olddir); | 793 | fuse_invalidate_attr(olddir); |
786 | if (olddir != newdir) | 794 | if (olddir != newdir) |
787 | fuse_invalidate_attr(newdir); | 795 | fuse_invalidate_attr(newdir); |
788 | 796 | ||
789 | /* newent will end up negative */ | 797 | /* newent will end up negative */ |
790 | if (newent->d_inode) { | 798 | if (!(flags & RENAME_EXCHANGE) && newent->d_inode) { |
791 | fuse_invalidate_attr(newent->d_inode); | 799 | fuse_invalidate_attr(newent->d_inode); |
792 | fuse_invalidate_entry_cache(newent); | 800 | fuse_invalidate_entry_cache(newent); |
793 | fuse_update_ctime(newent->d_inode); | 801 | fuse_update_ctime(newent->d_inode); |
@@ -806,6 +814,36 @@ static int fuse_rename(struct inode *olddir, struct dentry *oldent, | |||
806 | return err; | 814 | return err; |
807 | } | 815 | } |
808 | 816 | ||
817 | static int fuse_rename(struct inode *olddir, struct dentry *oldent, | ||
818 | struct inode *newdir, struct dentry *newent) | ||
819 | { | ||
820 | return fuse_rename_common(olddir, oldent, newdir, newent, 0, | ||
821 | FUSE_RENAME, sizeof(struct fuse_rename_in)); | ||
822 | } | ||
823 | |||
824 | static int fuse_rename2(struct inode *olddir, struct dentry *oldent, | ||
825 | struct inode *newdir, struct dentry *newent, | ||
826 | unsigned int flags) | ||
827 | { | ||
828 | struct fuse_conn *fc = get_fuse_conn(olddir); | ||
829 | int err; | ||
830 | |||
831 | if (flags & ~(RENAME_NOREPLACE | RENAME_EXCHANGE)) | ||
832 | return -EINVAL; | ||
833 | |||
834 | if (fc->no_rename2 || fc->minor < 23) | ||
835 | return -EINVAL; | ||
836 | |||
837 | err = fuse_rename_common(olddir, oldent, newdir, newent, flags, | ||
838 | FUSE_RENAME2, sizeof(struct fuse_rename2_in)); | ||
839 | if (err == -ENOSYS) { | ||
840 | fc->no_rename2 = 1; | ||
841 | err = -EINVAL; | ||
842 | } | ||
843 | return err; | ||
844 | |||
845 | } | ||
846 | |||
809 | static int fuse_link(struct dentry *entry, struct inode *newdir, | 847 | static int fuse_link(struct dentry *entry, struct inode *newdir, |
810 | struct dentry *newent) | 848 | struct dentry *newent) |
811 | { | 849 | { |
@@ -1980,6 +2018,7 @@ static const struct inode_operations fuse_dir_inode_operations = { | |||
1980 | .unlink = fuse_unlink, | 2018 | .unlink = fuse_unlink, |
1981 | .rmdir = fuse_rmdir, | 2019 | .rmdir = fuse_rmdir, |
1982 | .rename = fuse_rename, | 2020 | .rename = fuse_rename, |
2021 | .rename2 = fuse_rename2, | ||
1983 | .link = fuse_link, | 2022 | .link = fuse_link, |
1984 | .setattr = fuse_setattr, | 2023 | .setattr = fuse_setattr, |
1985 | .create = fuse_create, | 2024 | .create = fuse_create, |
diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 40677e33504f..7aa5c75e0de1 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h | |||
@@ -542,6 +542,9 @@ struct fuse_conn { | |||
542 | /** Is fallocate not implemented by fs? */ | 542 | /** Is fallocate not implemented by fs? */ |
543 | unsigned no_fallocate:1; | 543 | unsigned no_fallocate:1; |
544 | 544 | ||
545 | /** Is rename with flags implemented by fs? */ | ||
546 | unsigned no_rename2:1; | ||
547 | |||
545 | /** Use enhanced/automatic page cache invalidation. */ | 548 | /** Use enhanced/automatic page cache invalidation. */ |
546 | unsigned auto_inval_data:1; | 549 | unsigned auto_inval_data:1; |
547 | 550 | ||
diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index e86a21acef75..40b5ca8a1b1f 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h | |||
@@ -100,6 +100,7 @@ | |||
100 | * - add reserved space to fuse_init_out | 100 | * - add reserved space to fuse_init_out |
101 | * - add FATTR_CTIME | 101 | * - add FATTR_CTIME |
102 | * - add ctime and ctimensec to fuse_setattr_in | 102 | * - add ctime and ctimensec to fuse_setattr_in |
103 | * - add FUSE_RENAME2 request | ||
103 | */ | 104 | */ |
104 | 105 | ||
105 | #ifndef _LINUX_FUSE_H | 106 | #ifndef _LINUX_FUSE_H |
@@ -353,6 +354,7 @@ enum fuse_opcode { | |||
353 | FUSE_BATCH_FORGET = 42, | 354 | FUSE_BATCH_FORGET = 42, |
354 | FUSE_FALLOCATE = 43, | 355 | FUSE_FALLOCATE = 43, |
355 | FUSE_READDIRPLUS = 44, | 356 | FUSE_READDIRPLUS = 44, |
357 | FUSE_RENAME2 = 45, | ||
356 | 358 | ||
357 | /* CUSE specific operations */ | 359 | /* CUSE specific operations */ |
358 | CUSE_INIT = 4096, | 360 | CUSE_INIT = 4096, |
@@ -431,6 +433,12 @@ struct fuse_rename_in { | |||
431 | uint64_t newdir; | 433 | uint64_t newdir; |
432 | }; | 434 | }; |
433 | 435 | ||
436 | struct fuse_rename2_in { | ||
437 | uint64_t newdir; | ||
438 | uint32_t flags; | ||
439 | uint32_t padding; | ||
440 | }; | ||
441 | |||
434 | struct fuse_link_in { | 442 | struct fuse_link_in { |
435 | uint64_t oldnodeid; | 443 | uint64_t oldnodeid; |
436 | }; | 444 | }; |