[PATCH RESEND 1/2] smb/client: implement fileattr_set for compression flags
Huiwen He <[email protected]> Wed, 5 Aug 2026 09:54:46 +0800
| Newsgroups | gmane.linux.file-systems,gmane.linux.kernel.cifs |
|---|---|
| Message-ID | <[email protected]> |
From: Huiwen He <[email protected]> CIFS handles FS_IOC_SETFLAGS in cifs_ioctl() because it does not implement ->fileattr_set. This causes the request to fall back from the generic fileattr path and bypass its permission and serialization checks. Implement ->fileattr_set for regular files and directories and use the existing compression helper for FS_COMPR_FL. Accept an unchanged FS_CASEFOLD_FL reported by fileattr_get so chattr can update compression on case-insensitive shares. Tested chattr +c and -c on files and directories against Samba. Non-owner and read-only mount requests were rejected by the VFS checks. Signed-off-by: Huiwen He <[email protected]> Reviewed-by: ChenXiaoSong <[email protected]> --- fs/smb/client/cifsfs.c | 4 +- fs/smb/client/cifsfs.h | 2 + fs/smb/client/ioctl.c | 89 ++++++++++++++++++++++++++++++++++++------ 3 files changed, 81 insertions(+), 14 deletions(-) diff --git a/fs/smb/client/cifsfs.c b/fs/smb/client/cifsfs.c index 66b9104e7ca2..0a1135602697 100644 --- a/fs/smb/client/cifsfs.c +++ b/fs/smb/client/cifsfs.c @@ -1195,7 +1195,7 @@ int cifs_fileattr_get(struct dentry *dentry, struct file_kattr *fa) u32 attrs; /* Preserve FS_COMPR_FL previously reported by cifs_ioctl(). */ - if (CIFS_I(inode)->cifsAttrs & ATTR_COMPRESSED) + if (READ_ONCE(CIFS_I(inode)->cifsAttrs) & ATTR_COMPRESSED) fa->flags |= FS_COMPR_FL; /* @@ -1256,6 +1256,7 @@ const struct inode_operations cifs_dir_inode_ops = { .get_acl = cifs_get_acl, .set_acl = cifs_set_acl, .fileattr_get = cifs_fileattr_get, + .fileattr_set = cifs_fileattr_set, }; const struct inode_operations cifs_file_inode_ops = { @@ -1267,6 +1268,7 @@ const struct inode_operations cifs_file_inode_ops = { .get_acl = cifs_get_acl, .set_acl = cifs_set_acl, .fileattr_get = cifs_fileattr_get, + .fileattr_set = cifs_fileattr_set, }; const char *cifs_get_link(struct dentry *dentry, struct inode *inode, diff --git a/fs/smb/client/cifsfs.h b/fs/smb/client/cifsfs.h index 854e672a4e37..10dcf51378d3 100644 --- a/fs/smb/client/cifsfs.h +++ b/fs/smb/client/cifsfs.h @@ -91,6 +91,8 @@ extern const struct inode_operations cifs_namespace_inode_operations; struct file_kattr; int cifs_fileattr_get(struct dentry *dentry, struct file_kattr *fa); +int cifs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry, + struct file_kattr *fa); /* Functions related to files and directories */ diff --git a/fs/smb/client/ioctl.c b/fs/smb/client/ioctl.c index 9fa743be3652..8b2ca0c7c06c 100644 --- a/fs/smb/client/ioctl.c +++ b/fs/smb/client/ioctl.c @@ -10,6 +10,7 @@ #include <linux/fs.h> #include <linux/file.h> +#include <linux/fileattr.h> #include <linux/mount.h> #include <linux/mm.h> #include <linux/pagemap.h> @@ -67,11 +68,11 @@ static long cifs_ioctl_query_info(unsigned int xid, struct file *filep, return rc; } -static int cifs_set_compression_by_path(unsigned int xid, struct file *filep, +static int cifs_set_compression_by_path(unsigned int xid, struct dentry *dentry, struct cifs_tcon *tcon, __u16 compression_state) { - struct inode *inode = file_inode(filep); + struct inode *inode = d_inode(dentry); struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); struct TCP_Server_Info *server = tcon->ses->server; struct cifs_open_parms oparms; @@ -92,11 +93,11 @@ static int cifs_set_compression_by_path(unsigned int xid, struct file *filep, cifs_sb->mnt_cifs_serverino_autodisabled) return -EOPNOTSUPP; - if (d_unhashed(filep->f_path.dentry)) + if (d_unhashed(dentry)) return -ESTALE; page = alloc_dentry_path(); - full_path = build_path_from_dentry(filep->f_path.dentry, page); + full_path = build_path_from_dentry(dentry, page); if (IS_ERR(full_path)) { free_dentry_path(page); return PTR_ERR(full_path); @@ -123,6 +124,10 @@ static int cifs_set_compression_by_path(unsigned int xid, struct file *filep, goto close; uniqueid = le64_to_cpu(data.fi.IndexNumber); + if (!uniqueid) { + rc = -EOPNOTSUPP; + goto close; + } if (uniqueid != CIFS_I(inode)->uniqueid) { rc = -ESTALE; goto close; @@ -141,14 +146,14 @@ static int cifs_set_compression_by_path(unsigned int xid, struct file *filep, return rc; } -static int cifs_ioctl_set_compression(unsigned int xid, struct file *filep, - struct cifs_tcon *tcon, - struct cifsFileInfo *cfile, - __u16 compression_state) +static int cifs_set_compression(unsigned int xid, struct dentry *dentry, + struct cifs_tcon *tcon, + struct cifsFileInfo *cfile, + __u16 compression_state) { struct cifsFileInfo *wfile; struct cifs_tcon *wtcon; - struct inode *inode = file_inode(filep); + struct inode *inode = d_inode(dentry); int rc; if (!tcon->ses->server->ops->set_compression) @@ -173,10 +178,68 @@ static int cifs_ioctl_set_compression(unsigned int xid, struct file *filep, return rc; } - return cifs_set_compression_by_path(xid, filep, tcon, + return cifs_set_compression_by_path(xid, dentry, tcon, compression_state); } +int cifs_fileattr_set(struct mnt_idmap *idmap, struct dentry *dentry, + struct file_kattr *fa) +{ + struct inode *inode = d_inode(dentry); + struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb); + struct file_kattr current_fa = {}; + struct tcon_link *tlink; + struct cifs_tcon *tcon; + __u16 compression_state; + bool enable_compression; + u32 allowed = FS_COMPR_FL; + unsigned int xid; + int rc; + + if (!fa->flags_valid) + return -EOPNOTSUPP; + + /* + * chattr writes back all flags returned by fileattr_get(). Accept + * FS_CASEFOLD_FL only when it reflects the share's casefold state. + */ + if (fa->flags & FS_CASEFOLD_FL) { + rc = cifs_fileattr_get(dentry, ¤t_fa); + if (rc) + return rc; + if (current_fa.flags & FS_CASEFOLD_FL) + allowed |= FS_CASEFOLD_FL; + } + if (fa->flags & ~allowed) + return -EOPNOTSUPP; + + enable_compression = fa->flags & FS_COMPR_FL; + compression_state = enable_compression ? COMPRESSION_FORMAT_DEFAULT : + COMPRESSION_FORMAT_NONE; + + tlink = cifs_sb_tlink(cifs_sb); + if (IS_ERR(tlink)) + return PTR_ERR(tlink); + tcon = tlink_tcon(tlink); + + xid = get_xid(); + rc = cifs_set_compression(xid, dentry, tcon, NULL, compression_state); + free_xid(xid); + cifs_put_tlink(tlink); + + if (!rc) { + spin_lock(&inode->i_lock); + if (enable_compression) + CIFS_I(inode)->cifsAttrs |= FILE_ATTRIBUTE_COMPRESSED; + else + CIFS_I(inode)->cifsAttrs &= ~FILE_ATTRIBUTE_COMPRESSED; + spin_unlock(&inode->i_lock); + } + + cifs_dbg(FYI, "set compress flag rc %d\n", rc); + return rc; +} + static long cifs_ioctl_copychunk(unsigned int xid, struct file *dst_file, unsigned long srcfd) { @@ -542,9 +605,9 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg) COMPRESSION_FORMAT_DEFAULT : COMPRESSION_FORMAT_NONE; - rc = cifs_ioctl_set_compression(xid, filep, tcon, - pSMBFile, - compression_state); + rc = cifs_set_compression(xid, filep->f_path.dentry, + tcon, pSMBFile, + compression_state); if (rc == 0) { spin_lock(&inode->i_lock); if (enable_compression) -- 2.43.0