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 | }; |
