[PATCH 11/15] smb: client: prepare EA opens for inode lease reuse

Ze Tan <[email protected]> Fri, 24 Jul 2026 18:40:05 +0800
Newsgroups org.kernel.vger.linux-cifs
Message-ID <b770d67c4cd3e17c553439d63430335f64b2eaa0.1784888897.git.tanze@kylinos.cn>
Extend the query_all_EAs and set_EA callbacks, and
smb2_query_info_compound(), with an optional inode. For SMB2 and SMB3,
smb2_reuse_inode_lease() can use that inode to attach an existing lease
key to temporary QUERY_INFO and SET_INFO opens.

Signed-off-by: Ze Tan <[email protected]>
---
 fs/smb/client/cifsglob.h  |  5 +++--
 fs/smb/client/cifssmb.c   |  7 ++++---
 fs/smb/client/inode.c     |  2 +-
 fs/smb/client/smb1ops.c   |  4 ++--
 fs/smb/client/smb1proto.h |  5 +++--
 fs/smb/client/smb2ops.c   | 31 ++++++++++++++++++++++++-------
 fs/smb/client/smb2proto.h |  3 ++-
 fs/smb/client/xattr.c     |  6 +++---
 8 files changed, 42 insertions(+), 21 deletions(-)

diff --git a/fs/smb/client/cifsglob.h b/fs/smb/client/cifsglob.h
index 79e4e84f8985..5e752d8731a9 100644
--- a/fs/smb/client/cifsglob.h
+++ b/fs/smb/client/cifsglob.h
@@ -568,10 +568,11 @@ struct smb_version_operations {
 	int (*validate_negotiate)(const unsigned int, struct cifs_tcon *);
 	ssize_t (*query_all_EAs)(const unsigned int, struct cifs_tcon *,
 			const unsigned char *, const unsigned char *, char *,
-			size_t, struct cifs_sb_info *);
+			size_t, struct cifs_sb_info *, struct inode *);
 	int (*set_EA)(const unsigned int, struct cifs_tcon *, const char *,
 			const char *, const void *, const __u16,
-			const struct nls_table *, struct cifs_sb_info *);
+			const struct nls_table *, struct cifs_sb_info *,
+			struct inode *);
 	struct smb_ntsd * (*get_acl)(struct cifs_sb_info *cifssb, struct inode *ino,
 			const char *patch, u32 *plen, u32 info);
 	struct smb_ntsd * (*get_acl_by_fid)(struct cifs_sb_info *cifssmb,
diff --git a/fs/smb/client/cifssmb.c b/fs/smb/client/cifssmb.c
index 40162d5554ea..82dfb024b6bf 100644
--- a/fs/smb/client/cifssmb.c
+++ b/fs/smb/client/cifssmb.c
@@ -3142,7 +3142,8 @@ struct inode *cifs_create_reparse_inode(struct cifs_open_info_data *data,
 					  &ea->ea_data[ea->ea_name_length+1],
 					  le16_to_cpu(ea->ea_value_length),
 					  cifs_sb->local_nls,
-					  cifs_sb);
+					  cifs_sb,
+					  NULL);
 			if (rc)
 				goto out_close;
 			if (le32_to_cpu(ea->next_entry_offset) == 0)
@@ -6113,7 +6114,7 @@ ssize_t
 CIFSSMBQAllEAs(const unsigned int xid, struct cifs_tcon *tcon,
 		const unsigned char *searchName, const unsigned char *ea_name,
 		char *EAData, size_t buf_size,
-		struct cifs_sb_info *cifs_sb)
+		struct cifs_sb_info *cifs_sb, struct inode *inode)
 {
 		/* BB assumes one setup word */
 	TRANSACTION2_QPI_REQ *pSMB = NULL;
@@ -6304,7 +6305,7 @@ int
 CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon,
 	     const char *fileName, const char *ea_name, const void *ea_value,
 	     const __u16 ea_value_len, const struct nls_table *nls_codepage,
-	     struct cifs_sb_info *cifs_sb)
+	     struct cifs_sb_info *cifs_sb, struct inode *inode)
 {
 	struct smb_com_transaction2_spi_req *pSMB = NULL;
 	struct smb_com_transaction2_spi_rsp *pSMBr = NULL;
diff --git a/fs/smb/client/inode.c b/fs/smb/client/inode.c
index b2806371bfde..a18c327afe03 100644
--- a/fs/smb/client/inode.c
+++ b/fs/smb/client/inode.c
@@ -742,7 +742,7 @@ static int cifs_sfu_mode(struct cifs_fattr *fattr, const unsigned char *path,
 
 	rc = tcon->ses->server->ops->query_all_EAs(xid, tcon, path,
 			"SETFILEBITS", ea_value, 4 /* size of buf */,
-			cifs_sb);
+			cifs_sb, NULL);
 	cifs_put_tlink(tlink);
 	if (rc < 0)
 		return (int)rc;
diff --git a/fs/smb/client/smb1ops.c b/fs/smb/client/smb1ops.c
index dc5a8c1da623..cbe1f4cc00e0 100644
--- a/fs/smb/client/smb1ops.c
+++ b/fs/smb/client/smb1ops.c
@@ -663,7 +663,7 @@ static int cifs_query_path_info(const unsigned int xid,
 
 		rc = CIFSSMBQAllEAs(xid, tcon, full_path, SMB2_WSL_XATTR_MODE,
 				    &ea->ea_data[SMB2_WSL_XATTR_NAME_LEN + 1],
-				    SMB2_WSL_XATTR_MODE_SIZE, cifs_sb);
+				    SMB2_WSL_XATTR_MODE_SIZE, cifs_sb, NULL);
 		if (rc == SMB2_WSL_XATTR_MODE_SIZE) {
 			ea->next_entry_offset = cpu_to_le32(0);
 			ea->flags = 0;
@@ -709,7 +709,7 @@ static int cifs_query_path_info(const unsigned int xid,
 
 		rc = CIFSSMBQAllEAs(xid, tcon, full_path, SMB2_WSL_XATTR_DEV,
 				    &ea->ea_data[SMB2_WSL_XATTR_NAME_LEN + 1],
-				    SMB2_WSL_XATTR_DEV_SIZE, cifs_sb);
+				    SMB2_WSL_XATTR_DEV_SIZE, cifs_sb, NULL);
 		if (rc == SMB2_WSL_XATTR_DEV_SIZE) {
 			ea->next_entry_offset = cpu_to_le32(0);
 			ea->flags = 0;
diff --git a/fs/smb/client/smb1proto.h b/fs/smb/client/smb1proto.h
index 80eaeb3dd2ec..cc3fb7370ab4 100644
--- a/fs/smb/client/smb1proto.h
+++ b/fs/smb/client/smb1proto.h
@@ -208,12 +208,13 @@ int CIFSSMBUnixSetPathInfo(const unsigned int xid, struct cifs_tcon *tcon,
 ssize_t CIFSSMBQAllEAs(const unsigned int xid, struct cifs_tcon *tcon,
 		       const unsigned char *searchName,
 		       const unsigned char *ea_name, char *EAData,
-		       size_t buf_size, struct cifs_sb_info *cifs_sb);
+		       size_t buf_size, struct cifs_sb_info *cifs_sb,
+		       struct inode *inode);
 int CIFSSMBSetEA(const unsigned int xid, struct cifs_tcon *tcon,
 		 const char *fileName, const char *ea_name,
 		 const void *ea_value, const __u16 ea_value_len,
 		 const struct nls_table *nls_codepage,
-		 struct cifs_sb_info *cifs_sb);
+		 struct cifs_sb_info *cifs_sb, struct inode *inode);
 
 /*
  * smb1debug.c
diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index cbd51a08e97e..e85d6b5a4564 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -1042,6 +1042,20 @@ static int smb2_query_file_info(const unsigned int xid, struct cifs_tcon *tcon,
 	return SMB2_query_info(xid, tcon, fid->persistent_fid, fid->volatile_fid, &data->fi);
 }
 
+static void smb2_reuse_inode_lease(struct cifs_tcon *tcon,
+				   struct TCP_Server_Info *server,
+				   struct inode *inode, struct cifs_fid *fid,
+				   u8 *oplock)
+{
+	if (!inode || !tcon->posix_extensions ||
+	    !(server->capabilities & SMB2_GLOBAL_CAP_LEASING) ||
+	    !CIFS_I(inode)->lease_granted || !server->ops->get_lease_key)
+		return;
+
+	*oplock = SMB2_OPLOCK_LEVEL_LEASE;
+	server->ops->get_lease_key(inode, fid);
+}
+
 #ifdef CONFIG_CIFS_XATTR
 static ssize_t
 move_smb2_ea_to_cifs(char *dst, size_t dst_size,
@@ -1132,7 +1146,7 @@ static ssize_t
 smb2_query_eas(const unsigned int xid, struct cifs_tcon *tcon,
 	       const unsigned char *path, const unsigned char *ea_name,
 	       char *ea_data, size_t buf_size,
-	       struct cifs_sb_info *cifs_sb)
+	       struct cifs_sb_info *cifs_sb, struct inode *inode)
 {
 	int rc;
 	struct kvec rsp_iov = {NULL, 0};
@@ -1147,7 +1161,7 @@ smb2_query_eas(const unsigned int xid, struct cifs_tcon *tcon,
 				      CIFSMaxBufSize -
 				      MAX_SMB2_CREATE_RESPONSE_SIZE -
 				      MAX_SMB2_CLOSE_RESPONSE_SIZE,
-				      &rsp_iov, &buftype, cifs_sb);
+				      &rsp_iov, &buftype, cifs_sb, inode);
 	if (rc) {
 		/*
 		 * If ea_name is NULL (listxattr) and there are no EAs,
@@ -1181,7 +1195,7 @@ static int
 smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
 	    const char *path, const char *ea_name, const void *ea_value,
 	    const __u16 ea_value_len, const struct nls_table *nls_codepage,
-	    struct cifs_sb_info *cifs_sb)
+	    struct cifs_sb_info *cifs_sb, struct inode *inode)
 {
 	struct smb2_compound_vars *vars;
 	struct cifs_ses *ses = tcon->ses;
@@ -1234,7 +1248,7 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
 		if (!ea_value) {
 			rc = ses->server->ops->query_all_EAs(xid, tcon, path,
 							     ea_name, NULL, 0,
-							     cifs_sb);
+							     cifs_sb, inode);
 			if (rc == -ENODATA)
 				goto sea_exit;
 		} else {
@@ -1250,7 +1264,8 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
 				      CIFSMaxBufSize -
 				      MAX_SMB2_CREATE_RESPONSE_SIZE -
 				      MAX_SMB2_CLOSE_RESPONSE_SIZE,
-				      &rsp_iov[1], &resp_buftype[1], cifs_sb);
+				      &rsp_iov[1], &resp_buftype[1], cifs_sb,
+				      inode);
 			if (rc == 0) {
 				rsp = (struct smb2_query_info_rsp *)rsp_iov[1].iov_base;
 				used_len = le32_to_cpu(rsp->OutputBufferLength);
@@ -1286,6 +1301,7 @@ smb2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
 		.replay = !!(retries),
 	};
 
+	smb2_reuse_inode_lease(tcon, server, inode, &fid, &oplock);
 	rc = SMB2_open_init(tcon, server,
 			    &rqst[0], &oplock, &oparms, utf16_path);
 	if (rc)
@@ -2861,7 +2877,7 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
 			 const char *path, u32 desired_access,
 			 u32 class, u32 type, u32 output_len,
 			 struct kvec *rsp, int *buftype,
-			 struct cifs_sb_info *cifs_sb)
+			 struct cifs_sb_info *cifs_sb, struct inode *inode)
 {
 	struct smb2_compound_vars *vars;
 	struct cifs_ses *ses = tcon->ses;
@@ -2923,6 +2939,7 @@ smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
 		.replay = !!(retries),
 	};
 
+	smb2_reuse_inode_lease(tcon, server, inode, &fid, &oplock);
 	rc = SMB2_open_init(tcon, server,
 			    &rqst[0], &oplock, &oparms, utf16_path);
 	if (rc)
@@ -3032,7 +3049,7 @@ smb2_queryfs(const unsigned int xid, struct cifs_tcon *tcon,
 				      FS_FULL_SIZE_INFORMATION,
 				      SMB2_O_INFO_FILESYSTEM,
 				      sizeof(struct smb2_fs_full_size_info),
-				      &rsp_iov, &buftype, cifs_sb);
+				      &rsp_iov, &buftype, cifs_sb, NULL);
 	if (rc)
 		goto qfs_exit;
 
diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h
index 2e9f70096825..99978e63df68 100644
--- a/fs/smb/client/smb2proto.h
+++ b/fs/smb/client/smb2proto.h
@@ -267,7 +267,8 @@ void smb311_update_preauth_hash(struct cifs_ses *ses,
 int smb2_query_info_compound(const unsigned int xid, struct cifs_tcon *tcon,
 			     const char *path, u32 desired_access, u32 class,
 			     u32 type, u32 output_len, struct kvec *rsp,
-			     int *buftype, struct cifs_sb_info *cifs_sb);
+			     int *buftype, struct cifs_sb_info *cifs_sb,
+			     struct inode *inode);
 /* query path info from the server using SMB311 POSIX extensions*/
 int posix_info_parse(const void *beg, const void *end,
 		     struct smb2_posix_info_parsed *out);
diff --git a/fs/smb/client/xattr.c b/fs/smb/client/xattr.c
index 5091f6c0d7fe..d52c52866651 100644
--- a/fs/smb/client/xattr.c
+++ b/fs/smb/client/xattr.c
@@ -155,7 +155,7 @@ static int cifs_xattr_set(const struct xattr_handler *handler,
 		if (pTcon->ses->server->ops->set_EA) {
 			rc = pTcon->ses->server->ops->set_EA(xid, pTcon,
 				full_path, name, value, (__u16)size,
-				cifs_sb->local_nls, cifs_sb);
+				cifs_sb->local_nls, cifs_sb, NULL);
 			if (rc == 0)
 				inode_set_ctime_current(inode);
 		}
@@ -314,7 +314,7 @@ static int cifs_xattr_get(const struct xattr_handler *handler,
 
 		if (pTcon->ses->server->ops->query_all_EAs)
 			rc = pTcon->ses->server->ops->query_all_EAs(xid, pTcon,
-				full_path, name, value, size, cifs_sb);
+				full_path, name, value, size, cifs_sb, NULL);
 		break;
 
 	case XATTR_CIFS_ACL:
@@ -423,7 +423,7 @@ ssize_t cifs_listxattr(struct dentry *direntry, char *data, size_t buf_size)
 
 	if (pTcon->ses->server->ops->query_all_EAs)
 		rc = pTcon->ses->server->ops->query_all_EAs(xid, pTcon,
-				full_path, NULL, data, buf_size, cifs_sb);
+				full_path, NULL, data, buf_size, cifs_sb, NULL);
 list_ea_exit:
 	free_dentry_path(page);
 	free_xid(xid);
-- 
2.43.0