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, metze@samba.org, chenxiaosong@kylinos.cn
Cc: linux-cifs@vger.kernel.org
Subject: [PATCH v2 2/3] smb/client: use writable handle for FS_IOC_SETFLAGS compression
Date: Mon,  8 Jun 2026 23:57:30 +0800	[thread overview]
Message-ID: <20260608155731.634984-3-huiwen.he@linux.dev> (raw)
In-Reply-To: <20260608155731.634984-1-huiwen.he@linux.dev>

From: Huiwen He <hehuiwen@kylinos.cn>

Setting the compressed flag on a CIFS mount can fail with -EACCES:

	[compress_share]
		vfs objects = btrfs

        $ touch test.bin
        $ chattr +c test.bin
        chattr: Permission denied while setting flags on test.bin

This can be reproduced against a Samba share backed by a filesystem that
supports compression, such as btrfs.

FS_IOC_SETFLAGS is issued on the file handle opened by userspace.  chattr
opens the target read-only before setting FS_COMPR_FL, so the SMB client
currently sends FSCTL_SET_COMPRESSION on a handle that may not have
FILE_WRITE_DATA access.  Samba requires FILE_WRITE_DATA for
FSCTL_SET_COMPRESSION and rejects the request.

Use the current handle only if it already has FILE_WRITE_DATA.  Otherwise
try an existing writable handle for the inode.  If none is available, open
a temporary FILE_WRITE_DATA handle for the compression request.

After FSCTL_SET_COMPRESSION succeeds, update the cached compressed
attribute immediately, matching how smb2_set_sparse() updates
FILE_ATTRIBUTE_SPARSE_FILE after a successful FSCTL_SET_SPARSE.

Signed-off-by: Huiwen He <hehuiwen@kylinos.cn>
Reviewed-by: ChenXiaoSong <chenxiaosong@kylinos.cn>
---
 fs/smb/client/ioctl.c | 116 ++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 112 insertions(+), 4 deletions(-)

diff --git a/fs/smb/client/ioctl.c b/fs/smb/client/ioctl.c
index 746d70091f3d..886e6893a552 100644
--- a/fs/smb/client/ioctl.c
+++ b/fs/smb/client/ioctl.c
@@ -67,6 +67,110 @@ 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,
+					struct cifs_tcon *tcon)
+{
+	struct inode *inode = file_inode(filep);
+	struct cifs_sb_info *cifs_sb = CIFS_SB(inode->i_sb);
+	struct TCP_Server_Info *server = tcon->ses->server;
+	struct cifs_open_parms oparms;
+	struct cifs_open_info_data data = {};
+	struct cifsFileInfo *tmp_cfile = NULL;
+	struct cifs_fid fid = {};
+	const char *full_path;
+	__u32 oplock = 0;
+	u64 uniqueid;
+	void *page;
+	int rc;
+
+	if (!server->ops->open || !server->ops->close ||
+	    !server->ops->query_file_info)
+		return -EOPNOTSUPP;
+
+	if (!(cifs_sb_flags(cifs_sb) & CIFS_MOUNT_SERVER_INUM) ||
+	    cifs_sb->mnt_cifs_serverino_autodisabled)
+		return -EOPNOTSUPP;
+
+	if (d_unhashed(filep->f_path.dentry))
+		return -ESTALE;
+
+	page = alloc_dentry_path();
+	full_path = build_path_from_dentry(filep->f_path.dentry, page);
+	if (IS_ERR(full_path)) {
+		free_dentry_path(page);
+		return PTR_ERR(full_path);
+	}
+
+	oparms = CIFS_OPARMS(cifs_sb, tcon, full_path, FILE_WRITE_DATA |
+			     FILE_READ_ATTRIBUTES,
+			     FILE_OPEN, 0, ACL_NO_MODE);
+	oparms.fid = &fid;
+
+	rc = server->ops->open(xid, &oparms, &oplock, NULL);
+	if (rc)
+		goto out;
+
+	tmp_cfile = kzalloc_obj(*tmp_cfile);
+	if (!tmp_cfile) {
+		rc = -ENOMEM;
+		goto close;
+	}
+
+	tmp_cfile->fid = fid;
+	rc = server->ops->query_file_info(xid, tcon, tmp_cfile, &data);
+	if (rc)
+		goto close;
+
+	uniqueid = le64_to_cpu(data.fi.IndexNumber);
+	if (uniqueid != CIFS_I(inode)->uniqueid) {
+		rc = -ESTALE;
+		goto close;
+	}
+
+	rc = server->ops->set_compression(xid, tcon, tmp_cfile);
+
+close:
+	server->ops->close(xid, tcon, &fid);
+	if (tmp_cfile)
+		kfree(tmp_cfile);
+	cifs_free_open_info(&data);
+out:
+	free_dentry_path(page);
+	return rc;
+}
+
+static int cifs_ioctl_set_compression(unsigned int xid, struct file *filep,
+				      struct cifs_tcon *tcon,
+				      struct cifsFileInfo *cfile)
+{
+	struct cifsFileInfo *wfile;
+	struct cifs_tcon *wtcon;
+	struct inode *inode = file_inode(filep);
+	int rc;
+
+	if (!tcon->ses->server->ops->set_compression)
+		return -EOPNOTSUPP;
+
+	if (cfile && (cfile->fid.access & FILE_WRITE_DATA)) {
+		rc = tcon->ses->server->ops->set_compression(xid, tcon, cfile);
+		if (rc != -EACCES)
+			return rc;
+	}
+
+	rc = cifs_get_writable_file(CIFS_I(inode), FIND_FSUID_ONLY, &wfile);
+	if (!rc) {
+		wtcon = tlink_tcon(wfile->tlink);
+		rc = wtcon->ses->server->ops->set_compression(xid, wtcon, wfile);
+		cifsFileInfo_put(wfile);
+		if (rc != -EACCES)
+			return rc;
+	} else if (rc != -EBADF) {
+		return rc;
+	}
+
+	return cifs_set_compression_by_path(xid, filep, tcon);
+}
+
 static long cifs_ioctl_copychunk(unsigned int xid, struct file *dst_file,
 			unsigned long srcfd)
 {
@@ -424,11 +528,15 @@ long cifs_ioctl(struct file *filep, unsigned int command, unsigned long arg)
 				break;
 
 			/* Try to set compress flag */
-			if (tcon->ses->server->ops->set_compression) {
-				rc = tcon->ses->server->ops->set_compression(
-							xid, tcon, pSMBFile);
-				cifs_dbg(FYI, "set compress flag rc %d\n", rc);
+			rc = cifs_ioctl_set_compression(xid, filep, tcon,
+							pSMBFile);
+			if (rc == 0) {
+				spin_lock(&inode->i_lock);
+				CIFS_I(inode)->cifsAttrs |=
+					FILE_ATTRIBUTE_COMPRESSED;
+				spin_unlock(&inode->i_lock);
 			}
+			cifs_dbg(FYI, "set compress flag rc %d\n", rc);
 			break;
 		case CIFS_IOC_COPYCHUNK_FILE:
 			rc = cifs_ioctl_copychunk(xid, filep, arg);
-- 
2.43.0


  parent reply	other threads:[~2026-06-08 15:58 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-06-08 15:57 [PATCH v2 0/3] smb: client: fix file compression flag ioctls Huiwen He
2026-06-08 15:57 ` [PATCH v2 1/3] smb/client: always return a value for FS_IOC_GETFLAGS Huiwen He
2026-06-08 15:57 ` Huiwen He [this message]
2026-06-08 15:57 ` [PATCH v2 3/3] smb/client: allow FS_IOC_SETFLAGS to clear compression 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=20260608155731.634984-3-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=metze@samba.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