From: Darrick J. Wong <djwong@xxxxxxxxxx> Let the kernel handle killing the suid/sgid bits because the write/falloc/truncate/chown code already does this, and we don't have to worry about external modifications that are only visible to the fuse server (i.e. we're not a cluster fs). Signed-off-by: "Darrick J. Wong" <djwong@xxxxxxxxxx> --- fs/fuse/fuse_trace.h | 72 ++++++++++++++++++++++++++++++++++++++++++++++++++ fs/fuse/dir.c | 15 ++++++++-- 2 files changed, 84 insertions(+), 3 deletions(-) diff --git a/fs/fuse/fuse_trace.h b/fs/fuse/fuse_trace.h index e5a41be1bfd6cf..c6b6757bd8bc3c 100644 --- a/fs/fuse/fuse_trace.h +++ b/fs/fuse/fuse_trace.h @@ -159,6 +159,78 @@ TRACE_EVENT(fuse_fileattr_update_inode, __entry->isize, __entry->old_iflags, __entry->new_iflags) ); +TRACE_EVENT(fuse_setattr_fill, + TP_PROTO(const struct inode *inode, + const struct fuse_setattr_in *inarg), + TP_ARGS(inode, inarg), + + TP_STRUCT__entry( + __field(dev_t, connection) + __field(uint64_t, ino) + __field(uint64_t, nodeid) + __field(umode_t, mode) + __field(loff_t, isize) + + __field(uint32_t, valid) + __field(umode_t, new_mode) + __field(uint64_t, new_size) + ), + + TP_fast_assign( + const struct fuse_inode *fi = get_fuse_inode_c(inode); + const struct fuse_mount *fm = get_fuse_mount_c(inode); + + __entry->connection = fm->fc->dev; + __entry->ino = fi->orig_ino; + __entry->nodeid = inode->i_ino; + __entry->isize = i_size_read(inode); + __entry->valid = inarg->valid; + __entry->new_mode = inarg->mode; + __entry->new_size = inarg->size; + ), + + TP_printk("connection %u ino %llu nodeid %llu mode 0%o isize 0x%llx valid 0x%x new_mode 0%o new_size 0x%llx", + __entry->connection, __entry->ino, __entry->nodeid, + __entry->mode, __entry->isize, __entry->valid, + __entry->new_mode, __entry->new_size) +); + +TRACE_EVENT(fuse_setattr, + TP_PROTO(const struct inode *inode, + const struct iattr *inarg), + TP_ARGS(inode, inarg), + + TP_STRUCT__entry( + __field(dev_t, connection) + __field(uint64_t, ino) + __field(uint64_t, nodeid) + __field(umode_t, mode) + __field(loff_t, isize) + + __field(uint32_t, valid) + __field(umode_t, new_mode) + __field(uint64_t, new_size) + ), + + TP_fast_assign( + const struct fuse_inode *fi = get_fuse_inode_c(inode); + const struct fuse_mount *fm = get_fuse_mount_c(inode); + + __entry->connection = fm->fc->dev; + __entry->ino = fi->orig_ino; + __entry->nodeid = inode->i_ino; + __entry->isize = i_size_read(inode); + __entry->valid = inarg->ia_valid; + __entry->new_mode = inarg->ia_mode; + __entry->new_size = inarg->ia_size; + ), + + TP_printk("connection %u ino %llu nodeid %llu mode 0%o isize 0x%llx valid 0x%x new_mode 0%o new_size 0x%llx", + __entry->connection, __entry->ino, __entry->nodeid, + __entry->mode, __entry->isize, __entry->valid, + __entry->new_mode, __entry->new_size) +); + #if IS_ENABLED(CONFIG_FUSE_IOMAP) struct fuse_iext_cursor; diff --git a/fs/fuse/dir.c b/fs/fuse/dir.c index 4cdd3ef0793379..8422310d070665 100644 --- a/fs/fuse/dir.c +++ b/fs/fuse/dir.c @@ -7,6 +7,7 @@ */ #include "fuse_i.h" +#include "fuse_trace.h" #include <linux/pagemap.h> #include <linux/file.h> @@ -1951,6 +1952,8 @@ static void fuse_setattr_fill(struct fuse_conn *fc, struct fuse_args *args, struct fuse_setattr_in *inarg_p, struct fuse_attr_out *outarg_p) { + trace_fuse_setattr_fill(inode, inarg_p); + args->opcode = FUSE_SETATTR; args->nodeid = get_node_id(inode); args->in_numargs = 1; @@ -2219,15 +2222,21 @@ static int fuse_setattr(struct mnt_idmap *idmap, struct dentry *entry, if (!fuse_allow_current_process(get_fuse_conn(inode))) return -EACCES; - if (attr->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID)) { + trace_fuse_setattr(inode, attr); + + if (!fuse_has_iomap(inode) && + (attr->ia_valid & (ATTR_KILL_SUID | ATTR_KILL_SGID))) { attr->ia_valid &= ~(ATTR_KILL_SUID | ATTR_KILL_SGID | ATTR_MODE); /* * The only sane way to reliably kill suid/sgid is to do it in - * the userspace filesystem + * the userspace filesystem if this isn't an iomap file. For + * iomap filesystems we let the kernel kill the setuid/setgid + * bits. * - * This should be done on write(), truncate() and chown(). + * This should be done on write(), truncate(), chown(), and + * fallocate(). */ if (!fc->handle_killpriv && !fc->handle_killpriv_v2) { /*