Commit 28405cb5 authored by David Howells's avatar David Howells Committed by Steve French
Browse files

cifs: Replace SendReceiveBlockingLock() with SendReceive() plus flags



Replace the smb1 transport's SendReceiveBlockingLock() with SendReceive()
plus a couple of flags.  This will then allow that to pick up the transport
changes there.

The first flag, CIFS_INTERRUPTIBLE_WAIT, is added to indicate that the wait
should be interruptible and the second, CIFS_WINDOWS_LOCK, indicates that
we need to send a Lock command with unlock type rather than a Cancel.

send_lock_cancel() is then called from cifs_lock_cancel() which is called
from the main transport loop in compound_send_recv().

[!] I *think* the error code handling is probably right.

Signed-off-by: default avatarDavid Howells <dhowells@redhat.com>
Reviewed-by: default avatarPaulo Alcantara (Red Hat) <pc@manguebit.org>
cc: Shyam Prasad N <sprasad@microsoft.com>
cc: Tom Talpey <tom@talpey.com>
cc: linux-cifs@vger.kernel.org
cc: netfs@lists.linux.dev
cc: linux-fsdevel@vger.kernel.org
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent 62432a3f
Loading
Loading
Loading
Loading
+6 −2
Original line number Diff line number Diff line
@@ -311,8 +311,9 @@ struct cifs_open_parms;
struct cifs_credits;

struct smb_version_operations {
	int (*send_cancel)(struct TCP_Server_Info *, struct smb_rqst *,
			   struct mid_q_entry *);
	int (*send_cancel)(struct cifs_ses *ses, struct TCP_Server_Info *server,
			   struct smb_rqst *rqst, struct mid_q_entry *mid,
			   unsigned int xid);
	bool (*compare_fids)(struct cifsFileInfo *, struct cifsFileInfo *);
	/* setup request: allocate mid, sign message */
	struct mid_q_entry *(*setup_request)(struct cifs_ses *,
@@ -1689,6 +1690,7 @@ struct mid_q_entry {
	__u16 credits_received;	/* number of credits from the response */
	__u32 pid;		/* process id */
	__u32 sequence_number;  /* for CIFS signing */
	unsigned int sr_flags;	/* Flags passed to send_recv() */
	unsigned long when_alloc;  /* when mid was created */
#ifdef CONFIG_CIFS_STATS2
	unsigned long when_sent; /* time when smb send finished */
@@ -1900,6 +1902,8 @@ enum cifs_writable_file_flags {
#define   CIFS_TRANSFORM_REQ      0x0800 /* transform request before sending */
#define   CIFS_NO_SRV_RSP         0x1000 /* there is no server response */
#define   CIFS_COMPRESS_REQ       0x4000 /* compress request before sending */
#define   CIFS_INTERRUPTIBLE_WAIT 0x8000 /* Interruptible wait (e.g. lock request) */
#define   CIFS_WINDOWS_LOCK       0x10000 /* We're trying to get a Windows lock */

/* Security Flags: indicate type of session setup needed */
#define   CIFSSEC_MAY_SIGN	0x00001
+4 −6
Original line number Diff line number Diff line
@@ -130,11 +130,12 @@ extern int cifs_wait_mtu_credits(struct TCP_Server_Info *server,
				 struct cifs_credits *credits);

static inline int
send_cancel(struct TCP_Server_Info *server, struct smb_rqst *rqst,
	    struct mid_q_entry *mid)
send_cancel(struct cifs_ses *ses, struct TCP_Server_Info *server,
	    struct smb_rqst *rqst, struct mid_q_entry *mid,
	    unsigned int xid)
{
	return server->ops->send_cancel ?
				server->ops->send_cancel(server, rqst, mid) : 0;
		server->ops->send_cancel(ses, server, rqst, mid, xid) : 0;
}

int wait_for_response(struct TCP_Server_Info *server, struct mid_q_entry *midQ);
@@ -142,9 +143,6 @@ extern int SendReceive2(const unsigned int /* xid */ , struct cifs_ses *,
			struct kvec *, int /* nvec to send */,
			int * /* type of buf returned */, const int flags,
			struct kvec * /* resp vec */);
int SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
			    struct smb_hdr *in_buf, unsigned int in_len,
			    struct smb_hdr *out_buf, int *pbytes_returned);

void smb2_query_server_interfaces(struct work_struct *work);
void
+10 −8
Original line number Diff line number Diff line
@@ -2059,7 +2059,7 @@ CIFSSMBLock(const unsigned int xid, struct cifs_tcon *tcon,
/*	LOCK_RSP *pSMBr = NULL; */ /* No response data other than rc to parse */
	unsigned int in_len;
	int bytes_returned;
	int flags = 0;
	int flags = CIFS_WINDOWS_LOCK | CIFS_INTERRUPTIBLE_WAIT;
	__u16 count;

	cifs_dbg(FYI, "CIFSSMBLock timeout %d numLock %d\n",
@@ -2104,8 +2104,9 @@ CIFSSMBLock(const unsigned int xid, struct cifs_tcon *tcon,
	pSMB->ByteCount = cpu_to_le16(count);

	if (waitFlag)
		rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB, in_len,
			(struct smb_hdr *) pSMB, &bytes_returned);
		rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
				 (struct smb_hdr *) pSMB, &bytes_returned,
				 flags);
	else
		rc = SendReceiveNoRsp(xid, tcon->ses, (char *)pSMB, in_len, flags);
	cifs_small_buf_release(pSMB);
@@ -2130,7 +2131,7 @@ CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon,
	struct cifs_posix_lock *parm_data;
	unsigned int in_len;
	int rc = 0;
	int timeout = 0;
	int sr_flags = CIFS_INTERRUPTIBLE_WAIT;
	int bytes_returned = 0;
	int resp_buf_type = 0;
	__u16 params, param_offset, offset, byte_count, count;
@@ -2173,7 +2174,7 @@ CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon,

	parm_data->lock_type = cpu_to_le16(lock_type);
	if (waitFlag) {
		timeout = CIFS_BLOCKING_OP; /* blocking operation, no timeout */
		sr_flags |= CIFS_BLOCKING_OP; /* blocking operation, no timeout */
		parm_data->lock_flags = cpu_to_le16(1);
		pSMB->Timeout = cpu_to_le32(-1);
	} else
@@ -2190,13 +2191,14 @@ CIFSSMBPosixLock(const unsigned int xid, struct cifs_tcon *tcon,
	in_len += byte_count;
	pSMB->ByteCount = cpu_to_le16(byte_count);
	if (waitFlag) {
		rc = SendReceiveBlockingLock(xid, tcon, (struct smb_hdr *) pSMB, in_len,
			(struct smb_hdr *) pSMBr, &bytes_returned);
		rc = SendReceive(xid, tcon->ses, (struct smb_hdr *) pSMB, in_len,
				 (struct smb_hdr *) pSMBr, &bytes_returned,
				 sr_flags);
	} else {
		iov[0].iov_base = (char *)pSMB;
		iov[0].iov_len = in_len;
		rc = SendReceive2(xid, tcon->ses, iov, 1 /* num iovecs */,
				&resp_buf_type, timeout, &rsp_iov);
				&resp_buf_type, sr_flags, &rsp_iov);
		pSMBr = (struct smb_com_transaction2_sfi_rsp *)rsp_iov.iov_base;
	}
	cifs_small_buf_release(pSMB);
+5 −194
Original line number Diff line number Diff line
@@ -239,13 +239,6 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
		return -EIO;
	}

	spin_lock(&server->srv_lock);
	if (server->tcpStatus == CifsExiting) {
		spin_unlock(&server->srv_lock);
		return -ENOENT;
	}
	spin_unlock(&server->srv_lock);

	/* Ensure that we do not send more than 50 overlapping requests
	   to the same server. We may make this configurable later or
	   use ses->maxReq */
@@ -261,193 +254,11 @@ SendReceive(const unsigned int xid, struct cifs_ses *ses,
	if (rc < 0)
		return rc;

	if (out_buf) {
		*pbytes_returned = resp_iov.iov_len;
		if (resp_iov.iov_len)
			memcpy(out_buf, resp_iov.iov_base, resp_iov.iov_len);
	free_rsp_buf(resp_buf_type, resp_iov.iov_base);
	return rc;
}

/* We send a LOCKINGX_CANCEL_LOCK to cause the Windows
   blocking lock to return. */

static int
send_lock_cancel(const unsigned int xid, struct cifs_tcon *tcon,
		 struct smb_hdr *in_buf, unsigned int in_len,
		 struct smb_hdr *out_buf)
{
	int bytes_returned;
	struct cifs_ses *ses = tcon->ses;
	LOCK_REQ *pSMB = (LOCK_REQ *)in_buf;

	/* We just modify the current in_buf to change
	   the type of lock from LOCKING_ANDX_SHARED_LOCK
	   or LOCKING_ANDX_EXCLUSIVE_LOCK to
	   LOCKING_ANDX_CANCEL_LOCK. */

	pSMB->LockType = LOCKING_ANDX_CANCEL_LOCK|LOCKING_ANDX_LARGE_FILES;
	pSMB->Timeout = 0;
	pSMB->hdr.Mid = get_next_mid(ses->server);

	return SendReceive(xid, ses, in_buf, in_len, out_buf,
			&bytes_returned, 0);
}

int SendReceiveBlockingLock(const unsigned int xid, struct cifs_tcon *tcon,
			    struct smb_hdr *in_buf, unsigned int in_len,
			    struct smb_hdr *out_buf, int *pbytes_returned)
{
	int rc = 0;
	int rstart = 0;
	struct mid_q_entry *mid;
	struct cifs_ses *ses;
	struct kvec iov = { .iov_base = in_buf, .iov_len = in_len };
	struct smb_rqst rqst = { .rq_iov = &iov, .rq_nvec = 1 };
	unsigned int instance;
	struct TCP_Server_Info *server;

	if (WARN_ON_ONCE(in_len > 0xffffff))
		return -EIO;
	if (tcon == NULL || tcon->ses == NULL) {
		cifs_dbg(VFS, "Null smb session\n");
		return -EIO;
	}
	ses = tcon->ses;
	server = ses->server;

	if (server == NULL) {
		cifs_dbg(VFS, "Null tcp session\n");
		return -EIO;
	}

	spin_lock(&server->srv_lock);
	if (server->tcpStatus == CifsExiting) {
		spin_unlock(&server->srv_lock);
		return -ENOENT;
	}
	spin_unlock(&server->srv_lock);

	/* Ensure that we do not send more than 50 overlapping requests
	   to the same server. We may make this configurable later or
	   use ses->maxReq */

	if (in_len > CIFSMaxBufSize + MAX_CIFS_HDR_SIZE) {
		cifs_tcon_dbg(VFS, "Invalid length, greater than maximum frame, %d\n",
			      in_len);
		return -EIO;
	}

	rc = wait_for_free_request(server, CIFS_BLOCKING_OP, &instance);
	if (rc)
		return rc;

	/* make sure that we sign in the same order that we send on this socket
	   and avoid races inside tcp sendmsg code that could cause corruption
	   of smb data */

	cifs_server_lock(server);

	rc = allocate_mid(ses, in_buf, &mid);
	if (rc) {
		cifs_server_unlock(server);
		return rc;
	}

	rc = cifs_sign_rqst(&rqst, server, &mid->sequence_number);
	if (rc) {
		delete_mid(mid);
		cifs_server_unlock(server);
		return rc;
	}

	mid->mid_state = MID_REQUEST_SUBMITTED;
	rc = __smb_send_rqst(server, 1, &rqst);
	cifs_save_when_sent(mid);

	if (rc < 0)
		server->sequence_number -= 2;

	cifs_server_unlock(server);

	if (rc < 0) {
		delete_mid(mid);
		return rc;
	}

	/* Wait for a reply - allow signals to interrupt. */
	rc = wait_event_interruptible(server->response_q,
		(!(mid->mid_state == MID_REQUEST_SUBMITTED ||
		   mid->mid_state == MID_RESPONSE_RECEIVED)) ||
		((server->tcpStatus != CifsGood) &&
		 (server->tcpStatus != CifsNew)));

	/* Were we interrupted by a signal ? */
	spin_lock(&server->srv_lock);
	if ((rc == -ERESTARTSYS) &&
		(mid->mid_state == MID_REQUEST_SUBMITTED ||
		 mid->mid_state == MID_RESPONSE_RECEIVED) &&
		((server->tcpStatus == CifsGood) ||
		 (server->tcpStatus == CifsNew))) {
		spin_unlock(&server->srv_lock);

		if (in_buf->Command == SMB_COM_TRANSACTION2) {
			/* POSIX lock. We send a NT_CANCEL SMB to cause the
			   blocking lock to return. */
			rc = send_cancel(server, &rqst, mid);
			if (rc) {
				delete_mid(mid);
				return rc;
			}
		} else {
			/* Windows lock. We send a LOCKINGX_CANCEL_LOCK
			   to cause the blocking lock to return. */

			rc = send_lock_cancel(xid, tcon, in_buf, in_len, out_buf);

			/* If we get -ENOLCK back the lock may have
			   already been removed. Don't exit in this case. */
			if (rc && rc != -ENOLCK) {
				delete_mid(mid);
				return rc;
			}
		}

		rc = wait_for_response(server, mid);
		if (rc) {
			send_cancel(server, &rqst, mid);
			spin_lock(&mid->mid_lock);
			if (mid->callback) {
				/* no longer considered to be "in-flight" */
				mid->callback = release_mid;
				spin_unlock(&mid->mid_lock);
				return rc;
			}
			spin_unlock(&mid->mid_lock);
		}

		/* We got the response - restart system call. */
		rstart = 1;
		spin_lock(&server->srv_lock);
	}
	spin_unlock(&server->srv_lock);

	rc = cifs_sync_mid_result(mid, server);
	if (rc != 0)
		return rc;

	/* rcvd frame is ok */
	if (out_buf == NULL || mid->mid_state != MID_RESPONSE_READY) {
		rc = -EIO;
		cifs_tcon_dbg(VFS, "Bad MID state?\n");
		goto out;
	}

	*pbytes_returned = mid->response_pdu_len;
	memcpy(out_buf, mid->resp_buf, *pbytes_returned);
	rc = cifs_check_receive(mid, server, 0);
out:
	delete_mid(mid);
	if (rstart && rc == -EACCES)
		return -ERESTARTSYS;
	free_rsp_buf(resp_buf_type, resp_iov.iov_base);
	return rc;
}
+44 −3
Original line number Diff line number Diff line
@@ -30,8 +30,9 @@
 * SMB_COM_NT_CANCEL request and then sends it.
 */
static int
send_nt_cancel(struct TCP_Server_Info *server, struct smb_rqst *rqst,
	       struct mid_q_entry *mid)
send_nt_cancel(struct cifs_ses *ses, struct TCP_Server_Info *server,
	       struct smb_rqst *rqst, struct mid_q_entry *mid,
	       unsigned int xid)
{
	struct smb_hdr *in_buf = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
	struct kvec iov[1];
@@ -71,6 +72,46 @@ send_nt_cancel(struct TCP_Server_Info *server, struct smb_rqst *rqst,
	return rc;
}

/*
 * Send a LOCKINGX_CANCEL_LOCK to cause the Windows blocking lock to
 * return.
 */
static int
send_lock_cancel(struct cifs_ses *ses, struct TCP_Server_Info *server,
		 struct smb_rqst *rqst, struct mid_q_entry *mid,
		 unsigned int xid)
{
	struct smb_hdr *in_buf = (struct smb_hdr *)rqst->rq_iov[0].iov_base;
	unsigned int in_len = rqst->rq_iov[0].iov_len;
	LOCK_REQ *pSMB = (LOCK_REQ *)in_buf;
	int rc;

	/* We just modify the current in_buf to change
	 * the type of lock from LOCKING_ANDX_SHARED_LOCK
	 * or LOCKING_ANDX_EXCLUSIVE_LOCK to
	 * LOCKING_ANDX_CANCEL_LOCK.
	 */
	pSMB->LockType = LOCKING_ANDX_CANCEL_LOCK|LOCKING_ANDX_LARGE_FILES;
	pSMB->Timeout = 0;
	pSMB->hdr.Mid = get_next_mid(ses->server);

	rc = SendReceive(xid, ses, in_buf, in_len, NULL, NULL, 0);
	if (rc == -ENOLCK)
		rc = 0; /* If we get back -ENOLCK, it probably means we managed
			 * to cancel the lock command before it took effect.
			 */
	return rc;
}

static int cifs_send_cancel(struct cifs_ses *ses, struct TCP_Server_Info *server,
			    struct smb_rqst *rqst, struct mid_q_entry *mid,
			    unsigned int xid)
{
	if (mid->sr_flags & CIFS_WINDOWS_LOCK)
		return send_lock_cancel(ses, server, rqst, mid, xid);
	return send_nt_cancel(ses, server, rqst, mid, xid);
}

static bool
cifs_compare_fids(struct cifsFileInfo *ob1, struct cifsFileInfo *ob2)
{
@@ -1397,7 +1438,7 @@ cifs_is_network_name_deleted(char *buf, struct TCP_Server_Info *server)
}

struct smb_version_operations smb1_operations = {
	.send_cancel = send_nt_cancel,
	.send_cancel = cifs_send_cancel,
	.compare_fids = cifs_compare_fids,
	.setup_request = cifs_setup_request,
	.setup_async_request = cifs_setup_async_request,
Loading