Add passthrough setattr which sets attributes directly on the backing inode through backing_inode_setattr() instead of sending FUSE_SETATTR to the server. Passthrough setattr is checked before the handle_killpriv/handle_killpriv_v2 suid/sgid stripping because the stripping is handled natively by notify_change() on the backing inode. Signed-off-by: Joanne Koong --- fs/fuse/dir.c | 3 +++ fs/fuse/fuse_i.h | 3 ++- fs/fuse/passthrough.c | 28 ++++++++++++++++++++++++++++ include/uapi/linux/fuse.h | 1 + 4 files changed, 34 insertions(+), 1 deletion(-) diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 4c7e3e1604af..b67b3b334e69 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -2506,6 +2506,9 @@ static int fuse_setattr(struct mnt_idmap *idmap, struct dentry *entry, if (!fuse_allow_current_process(get_fuse_conn(inode))) return -EACCES; + if (fuse_passthrough_op(inode, FUSE_SETATTR)) + return fuse_passthrough_setattr(entry, attr); + if (attr->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) { attr->ia_valid &= ~(ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_MODE); diff --git a/fs/fuse/fuse_i.h b/fs/fuse/fuse_i.h index 89c9333e9702..0d978a9837a0 100644 --- a/fs/fuse/fuse_i.h +++ b/fs/fuse/fuse_i.h @@ -1279,7 +1279,7 @@ ssize_t fuse_direct_write_iter(struct kiocb *iocb, struct iov_iter *from); /* Inode passthrough operations for backing file attached to inode */ #define FUSE_PASSTHROUGH_INODE_OPS \ - (FUSE_PASSTHROUGH_OP_GETATTR) + (FUSE_PASSTHROUGH_OP_GETATTR | FUSE_PASSTHROUGH_OP_SETATTR) #define FUSE_BACKING_MAP_OP(map, op) \ ((map)->ops_mask & FUSE_PASSTHROUGH_OP(op)) @@ -1367,6 +1367,7 @@ static inline bool fuse_passthrough_op(struct inode *inode, enum fuse_opcode op) int fuse_passthrough_getattr(struct inode *inode, struct kstat *stat, u32 request_mask, unsigned int flags); +int fuse_passthrough_setattr(struct dentry *entry, struct iattr *attr); static inline bool fuse_use_entry2(struct fuse_conn *fc) { diff --git a/fs/fuse/passthrough.c b/fs/fuse/passthrough.c index ae0137caa06d..c083ab68537e 100644 --- a/fs/fuse/passthrough.c +++ b/fs/fuse/passthrough.c @@ -9,6 +9,8 @@ #include #include +#include +#include #include static void fuse_file_accessed(struct file *file) @@ -275,3 +277,29 @@ int fuse_passthrough_getattr(struct inode *inode, struct kstat *stat, return 0; } + +int fuse_passthrough_setattr(struct dentry *entry, struct iattr *attr) +{ + struct inode *inode = d_inode(entry); + struct fuse_conn *fc = get_fuse_conn(inode); + struct fuse_inode *fi = get_fuse_inode(inode); + struct fuse_backing *fb = fuse_inode_backing(fi); + struct path path; + int err; + + err = setattr_prepare(&nop_mnt_idmap, entry, attr); + if (err) + return err; + + path.mnt = fb->file->f_path.mnt; + path.dentry = fb->file->f_path.dentry; + + err = backing_inode_setattr(entry, &path, attr, fb->cred); + if (err) + return err; + + if (fc->posix_acl) + forget_all_cached_acls(inode); + + return 0; +} diff --git a/include/uapi/linux/fuse.h b/include/uapi/linux/fuse.h index 3963631558f9..040fee549bb9 100644 --- a/include/uapi/linux/fuse.h +++ b/include/uapi/linux/fuse.h @@ -1158,6 +1158,7 @@ struct fuse_backing_map { #define FUSE_PASSTHROUGH_OP_WRITE FUSE_PASSTHROUGH_OP(FUSE_WRITE) #define FUSE_PASSTHROUGH_OP_READDIR FUSE_PASSTHROUGH_OP(FUSE_READDIR) #define FUSE_PASSTHROUGH_OP_GETATTR FUSE_PASSTHROUGH_OP(FUSE_GETATTR) +#define FUSE_PASSTHROUGH_OP_SETATTR FUSE_PASSTHROUGH_OP(FUSE_SETATTR) /* Device ioctls: */ #define FUSE_DEV_IOC_MAGIC 229 -- 2.52.0