[PATCH 8/9] smb/client: send SMB2 cancel requests

ChenXiaoSong <[email protected]>
Newsgroups org.kernel.vger.linux-cifs
Message-ID <[email protected]>
From: ChenXiaoSong <[email protected]>

Wire up the generic cancel callback for SMB2 and later dialects. When a
synchronous wait is interrupted, the client can now send SMB2_CANCEL for
the outstanding mid instead of only marking the mid as cancelled locally.

Build cancel requests from the original request header so the message id,
session id, tree id, signing state, and any saved async id target the
request that is being abandoned. Encrypted shares wrap the cancel in a
transform request before sending it.

Signed-off-by: ChenXiaoSong <[email protected]>
---
 fs/smb/client/smb2ops.c       |   4 ++
 fs/smb/client/smb2proto.h     |   3 +
 fs/smb/client/smb2transport.c | 105 ++++++++++++++++++++++++++++++++++
 3 files changed, 112 insertions(+)

diff --git a/fs/smb/client/smb2ops.c b/fs/smb/client/smb2ops.c
index 40f1ae0e9735..1f5dc1d9897f 100644
--- a/fs/smb/client/smb2ops.c
+++ b/fs/smb/client/smb2ops.c
@@ -5484,6 +5484,7 @@ static int smb2_make_node(unsigned int xid, struct inode *inode,
 
 #ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
 struct smb_version_operations smb20_operations = {
+	.send_cancel = smb2_send_cancel,
 	.compare_fids = smb2_compare_fids,
 	.setup_request = smb2_setup_request,
 	.setup_async_request = smb2_setup_async_request,
@@ -5586,6 +5587,7 @@ struct smb_version_operations smb20_operations = {
 #endif /* CIFS_ALLOW_INSECURE_LEGACY */
 
 struct smb_version_operations smb21_operations = {
+	.send_cancel = smb2_send_cancel,
 	.compare_fids = smb2_compare_fids,
 	.setup_request = smb2_setup_request,
 	.setup_async_request = smb2_setup_async_request,
@@ -5690,6 +5692,7 @@ struct smb_version_operations smb21_operations = {
 };
 
 struct smb_version_operations smb30_operations = {
+	.send_cancel = smb2_send_cancel,
 	.compare_fids = smb2_compare_fids,
 	.setup_request = smb2_setup_request,
 	.setup_async_request = smb2_setup_async_request,
@@ -5806,6 +5809,7 @@ struct smb_version_operations smb30_operations = {
 };
 
 struct smb_version_operations smb311_operations = {
+	.send_cancel = smb2_send_cancel,
 	.compare_fids = smb2_compare_fids,
 	.setup_request = smb2_setup_request,
 	.setup_async_request = smb2_setup_async_request,
diff --git a/fs/smb/client/smb2proto.h b/fs/smb/client/smb2proto.h
index 78a4e1c340f9..855d5c4637f0 100644
--- a/fs/smb/client/smb2proto.h
+++ b/fs/smb/client/smb2proto.h
@@ -40,6 +40,9 @@ int smb2_verify_signature(struct smb_rqst *rqst,
 			  struct TCP_Server_Info *server);
 int smb2_check_receive(struct mid_q_entry *mid, struct TCP_Server_Info *server,
 		       bool log_error);
+int smb2_send_cancel(struct cifs_ses *ses, struct TCP_Server_Info *server,
+		     struct smb_rqst *rqst, struct mid_q_entry *mid,
+		     unsigned int xid);
 struct mid_q_entry *smb2_setup_request(struct cifs_ses *ses,
 				       struct TCP_Server_Info *server,
 				       struct smb_rqst *rqst);
diff --git a/fs/smb/client/smb2transport.c b/fs/smb/client/smb2transport.c
index 1143ee52470a..3c113e0e17b1 100644
--- a/fs/smb/client/smb2transport.c
+++ b/fs/smb/client/smb2transport.c
@@ -591,6 +591,111 @@ smb2_verify_signature(struct smb_rqst *rqst, struct TCP_Server_Info *server)
 		return 0;
 }
 
+int
+smb2_send_cancel(struct cifs_ses *ses, struct TCP_Server_Info *server,
+		 struct smb_rqst *rqst, struct mid_q_entry *mid,
+		 unsigned int xid)
+{
+	struct smb2_pdu *req;
+	struct smb2_hdr *shdr;
+	struct smb2_transform_hdr tr_hdr;
+	struct smb_rqst new_rqst[2] = {};
+	struct kvec tr_iov = {
+		.iov_base = &tr_hdr,
+		.iov_len = sizeof(tr_hdr),
+	};
+	struct kvec iov[1];
+	struct smb_rqst crqst = {
+		.rq_iov = iov,
+		.rq_nvec = 1,
+	};
+	struct cifs_tcon *tcon;
+	__le32 flags;
+	__le32 pid;
+	__le32 tid;
+	__le64 sid;
+	__u64 async_id;
+	bool async_cmd;
+	bool encrypt = false;
+	int rc;
+
+	if (!ses || !server || !rqst || !rqst->rq_iov || !mid)
+		return -EINVAL;
+
+	if (rqst->rq_iov[0].iov_len < sizeof(struct smb2_hdr) + 4)
+		return -EINVAL;
+
+	req = rqst->rq_iov[0].iov_base;
+	if (!req)
+		return -EINVAL;
+
+	shdr = &req->hdr;
+	flags = shdr->Flags & SMB2_FLAGS_SIGNED;
+	pid = shdr->Id.SyncId.ProcessId;
+	tid = shdr->Id.SyncId.TreeId;
+	sid = shdr->SessionId;
+	spin_lock(&mid->mid_lock);
+	async_cmd = mid->async_cmd;
+	async_id = mid->async_id;
+	spin_unlock(&mid->mid_lock);
+
+	tcon = smb2_find_smb_tcon(server, le64_to_cpu(sid), le32_to_cpu(tid));
+	if (tcon) {
+		encrypt = smb3_encryption_required(tcon);
+		cifs_put_tcon(tcon, netfs_trace_tcon_ref_put_cancelled_mid);
+	}
+	if (encrypt)
+		flags = 0;
+
+	/* SMB2_CANCEL targets an existing mid and does not get a response. */
+	memset(req, 0, sizeof(struct smb2_hdr) + 4);
+	shdr->ProtocolId = SMB2_PROTO_NUMBER;
+	shdr->StructureSize = SMB2_HEADER_STRUCTURE_SIZE;
+	shdr->Command = SMB2_CANCEL;
+	shdr->Flags = flags;
+	shdr->MessageId = cpu_to_le64(mid->mid);
+	if (async_cmd) {
+		shdr->Flags |= SMB2_FLAGS_ASYNC_COMMAND;
+		shdr->Id.AsyncId = cpu_to_le64(async_id);
+	} else {
+		shdr->Id.SyncId.ProcessId = pid;
+		shdr->Id.SyncId.TreeId = tid;
+	}
+	shdr->SessionId = sid;
+	req->StructureSize2 = cpu_to_le16(4);
+
+	iov[0].iov_base = req;
+	iov[0].iov_len = sizeof(struct smb2_hdr) + 4;
+
+	cifs_server_lock(server);
+	if (encrypt) {
+		if (!server->ops->init_transform_rq) {
+			rc = smb_EIO(smb_eio_trace_tx_need_transform);
+			goto unlock;
+		}
+
+		new_rqst[0].rq_iov = &tr_iov;
+		new_rqst[0].rq_nvec = 1;
+		rc = server->ops->init_transform_rq(server, 2, new_rqst,
+						    &crqst);
+		if (!rc) {
+			rc = __smb_send_cancel_rqst(server, 2, new_rqst);
+			smb3_free_compound_rqst(1, &new_rqst[1]);
+		}
+	} else {
+		rc = smb2_sign_rqst(&crqst, server);
+		if (!rc)
+			rc = __smb_send_cancel_rqst(server, 1, &crqst);
+	}
+
+unlock:
+	cifs_server_unlock(server);
+
+	cifs_dbg(FYI, "issued SMB2_CANCEL for mid %llu xid=%u rc=%d\n",
+		 mid->mid, xid, rc);
+	return rc;
+}
+
 /*
  * Set message id for the request. Should be called after wait_for_free_request
  * and when srv_mutex is held.
-- 
2.54.0
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.