Linux CIFS filesystem development
 help / color / mirror / Atom feed
* [PATCH] cifs/client: allow FS_IOC_SETFLAGS to clear compression
@ 2026-06-05 17:00 Huiwen He
  0 siblings, 0 replies; only message in thread
From: Huiwen He @ 2026-06-05 17:00 UTC (permalink / raw)
  To: smfrench, linkinjeon, pc, ronniesahlberg, sprasad, tom, bharathsm,
	senozhatsky, dhowells, metze, chenxiaosong
  Cc: linux-cifs

From: Huiwen He <hehuiwen@kylinos.cn>

The CIFS FS_IOC_SETFLAGS path can set FS_COMPR_FL now, but it cannot
clear it again. This can be reproduced on a share backed by a filesystem
that supports compression, for example btrfs exported by Samba:

	[compress_share]
		vfs objects = btrfs

	$ touch test.bin
	$ chattr +c test.bin
	$ lsattr test.bin
	$ chattr -c test.bin

The final chattr -c fails with EOPNOTSUPP, and leaves the remote object
with the compressed attribute still set, because the client always sends
FSCTL_SET_COMPRESSION with COMPRESSION_FORMAT_DEFAULT. That is correct
for setting FS_COMPR_FL, but clearing FS_COMPR_FL requires sending
COMPRESSION_FORMAT_NONE.

Fix this by passing the requested compression state through the
set_compression operation.  The SMB1 and SMB2 helpers no longer hard-code
COMPRESSION_FORMAT_DEFAULT.

When FS_COMPR_FL is set, send COMPRESSION_FORMAT_DEFAULT.  When it is
cleared, send COMPRESSION_FORMAT_NONE.  If the server accepts the request,
update the cached FILE_ATTRIBUTE_COMPRESSED bit under i_lock so
FS_IOC_GETFLAGS reports the new state.

Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
---
 fs/smb/client/cifsglob.h  |  2 +-
 fs/smb/client/cifssmb.c   |  4 ++--
 fs/smb/client/ioctl.c     | 46 +++++++++++++++++++++++++++------------
 fs/smb/client/smb1ops.c   |  5 +++--
 fs/smb/client/smb1proto.h |  2 +-
 fs/smb/client/smb2ops.c   |  4 ++--
 fs/smb/client/smb2pdu.c   |  6 ++---
 fs/smb/client/smb2proto.h |  3 ++-
 8 files changed, 46 insertions(+), 26 deletions(-)

diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index 943b7cd2c096..a462c1590a9e 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -425,7 +425,7 @@ struct smb_version_operations {
 	int (*set_file_info)(struct inode *, const char *, FILE_BASIC_INFO *,
 			     const unsigned int);
 	int (*set_compression)(const unsigned int, struct cifs_tcon *,
-			       struct cifsFileInfo *);
+			       struct cifsFileInfo *, __u16);
 	/* check if we can send an echo or nor */
 	bool (*can_echo)(struct TCP_Server_Info *);
 	/* send echo request */
diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c
index 9e27bfa7376b..d39175cdf1b1 100644
--- a/fs/smb/client/cifssmb.c
+++ b/fs/smb/client/cifssmb.c
@@ -3207,7 +3207,7 @@ struct inode *cifs_create_reparse_inode(struct cifs_open_info_data *data,
 
 int
 CIFSSMB_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
-		    __u16 fid)
+		    __u16 fid, __u16 compression_state)
 {
 	int rc = 0;
 	int bytes_returned;
@@ -3222,7 +3222,7 @@ CIFSSMB_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
 		return rc;
 	in_len = rc;
 
-	pSMB->compression_state = cpu_to_le16(COMPRESSION_FORMAT_DEFAULT);
+	pSMB->compression_state = cpu_to_le16(compression_state);
 
 	pSMB->TotalParameterCount = 0;
 	pSMB->TotalDataCount = cpu_to_le32(2);
diff --git a/fs/smb/client/ioctl.c b/fs/smb/client/ioctl.c
index b64bcde6e766..bb512a04934e 100644
--- a/fs/smb/client/ioctl.c
+++ b/fs/smb/client/ioctl.c
@@ -69,18 +69,21 @@ static long cifs_ioctl_query_info(unsigned int xid, struct file *filep,
 
 static int cifs_set_compression_handle(unsigned int xid,
 				       struct cifs_tcon *tcon,
-				       struct cifsFileInfo *cfile)
+				       struct cifsFileInfo *cfile,
+				       __u16 compression_state)
 {
 	struct TCP_Server_Info *server = tcon->ses->server;
 
 	if (!server->ops->set_compression)
 		return -EOPNOTSUPP;
 
-	return server->ops->set_compression(xid, tcon, cfile);
+	return server->ops->set_compression(xid, tcon, cfile,
+					   compression_state);
 }
 
 static int cifs_set_compression_by_path(unsigned int xid, struct file *filep,
-					struct cifs_tcon *tcon)
+					struct cifs_tcon *tcon,
+					__u16 compression_state)
 {
 	struct inode *inode = file_inode(filep);
 	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
@@ -132,7 +135,8 @@ static int cifs_set_compression_by_path(unsigned int xid, struct file *filep,
 		goto close;
 	}
 
-	rc = cifs_set_compression_handle(xid, tcon, &tmp_cfile);
+	rc = cifs_set_compression_handle(xid, tcon, &tmp_cfile,
+					 compression_state);
 
 close:
 	server->ops->close(xid, tcon, &fid);
@@ -144,7 +148,8 @@ static int cifs_set_compression_by_path(unsigned int xid, struct file *filep,
 
 static int cifs_ioctl_set_compression(unsigned int xid, struct file *filep,
 				      struct cifs_tcon *tcon,
-				      struct cifsFileInfo *cfile)
+				      struct cifsFileInfo *cfile,
+				      __u16 compression_state)
 {
 	struct cifsFileInfo *wfile;
 	struct cifs_tcon *wtcon;
@@ -155,7 +160,8 @@ static int cifs_ioctl_set_compression(unsigned int xid, struct file *filep,
 		return -EOPNOTSUPP;
 
 	if (cfile && (cfile->fid.access & FILE_WRITE_DATA)) {
-		rc = cifs_set_compression_handle(xid, tcon, cfile);
+		rc = cifs_set_compression_handle(xid, tcon, cfile,
+						 compression_state);
 		if (rc != -EACCES)
 			return rc;
 	}
@@ -163,13 +169,15 @@ static int cifs_ioctl_set_compression(unsigned int xid, struct file *filep,
 	rc = cifs_get_writable_file(CIFS_I(inode), FIND_FSUID_ONLY, &wfile);
 	if (!rc) {
 		wtcon = tlink_tcon(wfile->tlink);
-		rc = cifs_set_compression_handle(xid, wtcon, wfile);
+		rc = cifs_set_compression_handle(xid, wtcon, wfile,
+						 compression_state);
 		cifsFileInfo_put(wfile);
 		if (rc != -EACCES)
 			return rc;
 	}
 
-	return cifs_set_compression_by_path(xid, filep, tcon);
+	return cifs_set_compression_by_path(xid, filep, tcon,
+					    compression_state);
 }
 
 static long cifs_ioctl_copychunk(unsigned int xid, struct file *dst_file,
@@ -526,18 +534,28 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
 			 *	break;
 			 */
 
-			/* Currently only flag we can set is compressed flag */
-			if ((ExtAttrBits & FS_COMPR_FL) == 0)
+			/* Currently only flag we can set or clear is compressed. */
+			if (ExtAttrBits & ~FS_COMPR_FL)
 				break;
 
-			/* Try to set compress flag */
 			if (tcon->ses->server->ops->set_compression) {
+				__u16 compression_state;
+
+				compression_state = (ExtAttrBits & FS_COMPR_FL) ?
+					COMPRESSION_FORMAT_DEFAULT :
+					COMPRESSION_FORMAT_NONE;
+
 				rc = cifs_ioctl_set_compression(xid, filep, tcon,
-								pSMBFile);
+								pSMBFile,
+								compression_state);
 				if (rc == 0) {
 					spin_lock(&inode->i_lock);
-					CIFS_I(inode)->cifsAttrs |=
-						FILE_ATTRIBUTE_COMPRESSED;
+					if (ExtAttrBits & FS_COMPR_FL)
+						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);
diff --git a/fs/smb/client/smb1ops.c b/fs/smb/client/smb1ops.c
index e198e3dda917..d34b3d99f6ed 100644
--- a/fs/smb/client/smb1ops.c
+++ b/fs/smb/client/smb1ops.c
@@ -1110,9 +1110,10 @@ smb_set_file_info(struct inode *inode, const char *full_path,
 
 static int
 cifs_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
-		   struct cifsFileInfo *cfile)
+		   struct cifsFileInfo *cfile, __u16 compression_state)
 {
-	return CIFSSMB_set_compression(xid, tcon, cfile->fid.netfid);
+	return CIFSSMB_set_compression(xid, tcon, cfile->fid.netfid,
+				       compression_state);
 }
 
 static int
diff --git a/fs/smb/client/smb1proto.h b/fs/smb/client/smb1proto.h
index 5f522d359952..80eaeb3dd2ec 100644
--- a/fs/smb/client/smb1proto.h
+++ b/fs/smb/client/smb1proto.h
@@ -117,7 +117,7 @@ struct inode *cifs_create_reparse_inode(struct cifs_open_info_data *data,
 					struct kvec *reparse_iov,
 					struct kvec *xattr_iov);
 int CIFSSMB_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
-			    __u16 fid);
+			    __u16 fid, __u16 compression_state);
 int cifs_do_get_acl(const unsigned int xid, struct cifs_tcon *tcon,
 		    const unsigned char *searchName, struct posix_acl **acl,
 		    const int acl_type, const struct nls_table *nls_codepage,
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 89230141b5dd..23807023c027 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -2246,10 +2246,10 @@ smb2_duplicate_extents(const unsigned int xid,
 
 static int
 smb2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
-		   struct cifsFileInfo *cfile)
+		   struct cifsFileInfo *cfile, __u16 compression_state)
 {
 	return SMB2_set_compression(xid, tcon, cfile->fid.persistent_fid,
-			    cfile->fid.volatile_fid);
+			    cfile->fid.volatile_fid, compression_state);
 }
 
 static int
diff --git a/fs/smb/client/smb2pdu.c b/fs/smb/client/smb2pdu.c
index fbeb2156ddb6..6a185b805c1e 100644
--- a/fs/smb/client/smb2pdu.c
+++ b/fs/smb/client/smb2pdu.c
@@ -3626,14 +3626,14 @@ SMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
 
 int
 SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
-		     u64 persistent_fid, u64 volatile_fid)
+		     u64 persistent_fid, u64 volatile_fid,
+		     __u16 compression_state)
 {
 	int rc;
 	struct  compress_ioctl fsctl_input;
 	char *ret_data = NULL;
 
-	fsctl_input.CompressionState =
-			cpu_to_le16(COMPRESSION_FORMAT_DEFAULT);
+	fsctl_input.CompressionState = cpu_to_le16(compression_state);
 
 	rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid,
 			FSCTL_SET_COMPRESSION,
diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h
index 1ceb95b907e6..78a4e1c340f9 100644
--- a/fs/smb/client/smb2proto.h
+++ b/fs/smb/client/smb2proto.h
@@ -216,7 +216,8 @@ int SMB2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
 		u64 persistent_fid, u64 volatile_fid,
 		struct smb2_file_full_ea_info *buf, int len);
 int SMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
-			 u64 persistent_fid, u64 volatile_fid);
+			 u64 persistent_fid, u64 volatile_fid,
+			 __u16 compression_state);
 int SMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
 		      const u64 persistent_fid, const u64 volatile_fid,
 		      __u8 oplock_level);
-- 
2.43.0


^ permalink raw reply related	[flat|nested] only message in thread

only message in thread, other threads:[~2026-06-05 17:01 UTC | newest]

Thread overview: (only message) (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-05 17:00 [PATCH] cifs/client: allow FS_IOC_SETFLAGS to clear compression Huiwen He

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox