Linux CIFS filesystem development
 help / color / mirror / Atom feed
From: Huiwen He <huiwen.he@linux.dev>
To: smfrench@gmail.com, linkinjeon@kernel.org, pc@manguebit.org,
	ronniesahlberg@gmail.com, sprasad@microsoft.com, tom@talpey.com,
	bharathsm@microsoft.com, senozhatsky@chromium.org,
	dhowells@redhat.com, chenxiaosong@kylinos.cn
Cc: linux-cifs@vger.kernel.org
Subject: [PATCH 1/2] smb/client: implement fileattr_set for compression flags
Date: Wed, 22 Jul 2026 16:53:32 +0800	[thread overview]
Message-ID: <20260722085333.86217-2-huiwen.he@linux.dev> (raw)
In-Reply-To: <20260722085333.86217-1-huiwen.he@linux.dev>

From: Huiwen He <hehuiwen@kylinos.cn>

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 <hehuiwen@kylinos.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
---
 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, &current_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


  reply	other threads:[~2026-07-22  8:54 UTC|newest]

Thread overview: 3+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-07-22  8:53 [PATCH 0/2] smb/client: handle compression flags through fileattr Huiwen He
2026-07-22  8:53 ` Huiwen He [this message]
2026-07-22  8:53 ` [PATCH 2/2] smb/client: remove unused file flags ioctl handlers Huiwen He

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260722085333.86217-2-huiwen.he@linux.dev \
    --to=huiwen.he@linux.dev \
    --cc=bharathsm@microsoft.com \
    --cc=chenxiaosong@kylinos.cn \
    --cc=dhowells@redhat.com \
    --cc=linkinjeon@kernel.org \
    --cc=linux-cifs@vger.kernel.org \
    --cc=pc@manguebit.org \
    --cc=ronniesahlberg@gmail.com \
    --cc=senozhatsky@chromium.org \
    --cc=smfrench@gmail.com \
    --cc=sprasad@microsoft.com \
    --cc=tom@talpey.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox