162306a36Sopenharmony_ci// SPDX-License-Identifier: LGPL-2.1
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci *   Copyright (C) International Business Machines  Corp., 2009, 2013
562306a36Sopenharmony_ci *                 Etersoft, 2012
662306a36Sopenharmony_ci *   Author(s): Steve French (sfrench@us.ibm.com)
762306a36Sopenharmony_ci *              Pavel Shilovsky (pshilovsky@samba.org) 2012
862306a36Sopenharmony_ci *
962306a36Sopenharmony_ci *   Contains the routines for constructing the SMB2 PDUs themselves
1062306a36Sopenharmony_ci *
1162306a36Sopenharmony_ci */
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci /* SMB2 PDU handling routines here - except for leftovers (eg session setup) */
1462306a36Sopenharmony_ci /* Note that there are handle based routines which must be		      */
1562306a36Sopenharmony_ci /* treated slightly differently for reconnection purposes since we never     */
1662306a36Sopenharmony_ci /* want to reuse a stale file handle and only the caller knows the file info */
1762306a36Sopenharmony_ci
1862306a36Sopenharmony_ci#include <linux/fs.h>
1962306a36Sopenharmony_ci#include <linux/kernel.h>
2062306a36Sopenharmony_ci#include <linux/vfs.h>
2162306a36Sopenharmony_ci#include <linux/task_io_accounting_ops.h>
2262306a36Sopenharmony_ci#include <linux/uaccess.h>
2362306a36Sopenharmony_ci#include <linux/uuid.h>
2462306a36Sopenharmony_ci#include <linux/pagemap.h>
2562306a36Sopenharmony_ci#include <linux/xattr.h>
2662306a36Sopenharmony_ci#include "cifsglob.h"
2762306a36Sopenharmony_ci#include "cifsacl.h"
2862306a36Sopenharmony_ci#include "cifsproto.h"
2962306a36Sopenharmony_ci#include "smb2proto.h"
3062306a36Sopenharmony_ci#include "cifs_unicode.h"
3162306a36Sopenharmony_ci#include "cifs_debug.h"
3262306a36Sopenharmony_ci#include "ntlmssp.h"
3362306a36Sopenharmony_ci#include "smb2status.h"
3462306a36Sopenharmony_ci#include "smb2glob.h"
3562306a36Sopenharmony_ci#include "cifspdu.h"
3662306a36Sopenharmony_ci#include "cifs_spnego.h"
3762306a36Sopenharmony_ci#include "smbdirect.h"
3862306a36Sopenharmony_ci#include "trace.h"
3962306a36Sopenharmony_ci#ifdef CONFIG_CIFS_DFS_UPCALL
4062306a36Sopenharmony_ci#include "dfs_cache.h"
4162306a36Sopenharmony_ci#endif
4262306a36Sopenharmony_ci#include "cached_dir.h"
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci/*
4562306a36Sopenharmony_ci *  The following table defines the expected "StructureSize" of SMB2 requests
4662306a36Sopenharmony_ci *  in order by SMB2 command.  This is similar to "wct" in SMB/CIFS requests.
4762306a36Sopenharmony_ci *
4862306a36Sopenharmony_ci *  Note that commands are defined in smb2pdu.h in le16 but the array below is
4962306a36Sopenharmony_ci *  indexed by command in host byte order.
5062306a36Sopenharmony_ci */
5162306a36Sopenharmony_cistatic const int smb2_req_struct_sizes[NUMBER_OF_SMB2_COMMANDS] = {
5262306a36Sopenharmony_ci	/* SMB2_NEGOTIATE */ 36,
5362306a36Sopenharmony_ci	/* SMB2_SESSION_SETUP */ 25,
5462306a36Sopenharmony_ci	/* SMB2_LOGOFF */ 4,
5562306a36Sopenharmony_ci	/* SMB2_TREE_CONNECT */	9,
5662306a36Sopenharmony_ci	/* SMB2_TREE_DISCONNECT */ 4,
5762306a36Sopenharmony_ci	/* SMB2_CREATE */ 57,
5862306a36Sopenharmony_ci	/* SMB2_CLOSE */ 24,
5962306a36Sopenharmony_ci	/* SMB2_FLUSH */ 24,
6062306a36Sopenharmony_ci	/* SMB2_READ */	49,
6162306a36Sopenharmony_ci	/* SMB2_WRITE */ 49,
6262306a36Sopenharmony_ci	/* SMB2_LOCK */	48,
6362306a36Sopenharmony_ci	/* SMB2_IOCTL */ 57,
6462306a36Sopenharmony_ci	/* SMB2_CANCEL */ 4,
6562306a36Sopenharmony_ci	/* SMB2_ECHO */ 4,
6662306a36Sopenharmony_ci	/* SMB2_QUERY_DIRECTORY */ 33,
6762306a36Sopenharmony_ci	/* SMB2_CHANGE_NOTIFY */ 32,
6862306a36Sopenharmony_ci	/* SMB2_QUERY_INFO */ 41,
6962306a36Sopenharmony_ci	/* SMB2_SET_INFO */ 33,
7062306a36Sopenharmony_ci	/* SMB2_OPLOCK_BREAK */ 24 /* BB this is 36 for LEASE_BREAK variant */
7162306a36Sopenharmony_ci};
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ciint smb3_encryption_required(const struct cifs_tcon *tcon)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	if (!tcon || !tcon->ses)
7662306a36Sopenharmony_ci		return 0;
7762306a36Sopenharmony_ci	if ((tcon->ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA) ||
7862306a36Sopenharmony_ci	    (tcon->share_flags & SHI1005_FLAGS_ENCRYPT_DATA))
7962306a36Sopenharmony_ci		return 1;
8062306a36Sopenharmony_ci	if (tcon->seal &&
8162306a36Sopenharmony_ci	    (tcon->ses->server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION))
8262306a36Sopenharmony_ci		return 1;
8362306a36Sopenharmony_ci	return 0;
8462306a36Sopenharmony_ci}
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_cistatic void
8762306a36Sopenharmony_cismb2_hdr_assemble(struct smb2_hdr *shdr, __le16 smb2_cmd,
8862306a36Sopenharmony_ci		  const struct cifs_tcon *tcon,
8962306a36Sopenharmony_ci		  struct TCP_Server_Info *server)
9062306a36Sopenharmony_ci{
9162306a36Sopenharmony_ci	struct smb3_hdr_req *smb3_hdr;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	shdr->ProtocolId = SMB2_PROTO_NUMBER;
9462306a36Sopenharmony_ci	shdr->StructureSize = cpu_to_le16(64);
9562306a36Sopenharmony_ci	shdr->Command = smb2_cmd;
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	if (server) {
9862306a36Sopenharmony_ci		/* After reconnect SMB3 must set ChannelSequence on subsequent reqs */
9962306a36Sopenharmony_ci		if (server->dialect >= SMB30_PROT_ID) {
10062306a36Sopenharmony_ci			smb3_hdr = (struct smb3_hdr_req *)shdr;
10162306a36Sopenharmony_ci			/*
10262306a36Sopenharmony_ci			 * if primary channel is not set yet, use default
10362306a36Sopenharmony_ci			 * channel for chan sequence num
10462306a36Sopenharmony_ci			 */
10562306a36Sopenharmony_ci			if (SERVER_IS_CHAN(server))
10662306a36Sopenharmony_ci				smb3_hdr->ChannelSequence =
10762306a36Sopenharmony_ci					cpu_to_le16(server->primary_server->channel_sequence_num);
10862306a36Sopenharmony_ci			else
10962306a36Sopenharmony_ci				smb3_hdr->ChannelSequence =
11062306a36Sopenharmony_ci					cpu_to_le16(server->channel_sequence_num);
11162306a36Sopenharmony_ci		}
11262306a36Sopenharmony_ci		spin_lock(&server->req_lock);
11362306a36Sopenharmony_ci		/* Request up to 10 credits but don't go over the limit. */
11462306a36Sopenharmony_ci		if (server->credits >= server->max_credits)
11562306a36Sopenharmony_ci			shdr->CreditRequest = cpu_to_le16(0);
11662306a36Sopenharmony_ci		else
11762306a36Sopenharmony_ci			shdr->CreditRequest = cpu_to_le16(
11862306a36Sopenharmony_ci				min_t(int, server->max_credits -
11962306a36Sopenharmony_ci						server->credits, 10));
12062306a36Sopenharmony_ci		spin_unlock(&server->req_lock);
12162306a36Sopenharmony_ci	} else {
12262306a36Sopenharmony_ci		shdr->CreditRequest = cpu_to_le16(2);
12362306a36Sopenharmony_ci	}
12462306a36Sopenharmony_ci	shdr->Id.SyncId.ProcessId = cpu_to_le32((__u16)current->tgid);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	if (!tcon)
12762306a36Sopenharmony_ci		goto out;
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	/* GLOBAL_CAP_LARGE_MTU will only be set if dialect > SMB2.02 */
13062306a36Sopenharmony_ci	/* See sections 2.2.4 and 3.2.4.1.5 of MS-SMB2 */
13162306a36Sopenharmony_ci	if (server && (server->capabilities & SMB2_GLOBAL_CAP_LARGE_MTU))
13262306a36Sopenharmony_ci		shdr->CreditCharge = cpu_to_le16(1);
13362306a36Sopenharmony_ci	/* else CreditCharge MBZ */
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	shdr->Id.SyncId.TreeId = cpu_to_le32(tcon->tid);
13662306a36Sopenharmony_ci	/* Uid is not converted */
13762306a36Sopenharmony_ci	if (tcon->ses)
13862306a36Sopenharmony_ci		shdr->SessionId = cpu_to_le64(tcon->ses->Suid);
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	/*
14162306a36Sopenharmony_ci	 * If we would set SMB2_FLAGS_DFS_OPERATIONS on open we also would have
14262306a36Sopenharmony_ci	 * to pass the path on the Open SMB prefixed by \\server\share.
14362306a36Sopenharmony_ci	 * Not sure when we would need to do the augmented path (if ever) and
14462306a36Sopenharmony_ci	 * setting this flag breaks the SMB2 open operation since it is
14562306a36Sopenharmony_ci	 * illegal to send an empty path name (without \\server\share prefix)
14662306a36Sopenharmony_ci	 * when the DFS flag is set in the SMB open header. We could
14762306a36Sopenharmony_ci	 * consider setting the flag on all operations other than open
14862306a36Sopenharmony_ci	 * but it is safer to net set it for now.
14962306a36Sopenharmony_ci	 */
15062306a36Sopenharmony_ci/*	if (tcon->share_flags & SHI1005_FLAGS_DFS)
15162306a36Sopenharmony_ci		shdr->Flags |= SMB2_FLAGS_DFS_OPERATIONS; */
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	if (server && server->sign && !smb3_encryption_required(tcon))
15462306a36Sopenharmony_ci		shdr->Flags |= SMB2_FLAGS_SIGNED;
15562306a36Sopenharmony_ciout:
15662306a36Sopenharmony_ci	return;
15762306a36Sopenharmony_ci}
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci/* helper function for code reuse */
16062306a36Sopenharmony_cistatic int
16162306a36Sopenharmony_cicifs_chan_skip_or_disable(struct cifs_ses *ses,
16262306a36Sopenharmony_ci			  struct TCP_Server_Info *server,
16362306a36Sopenharmony_ci			  bool from_reconnect)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	struct TCP_Server_Info *pserver;
16662306a36Sopenharmony_ci	unsigned int chan_index;
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	if (SERVER_IS_CHAN(server)) {
16962306a36Sopenharmony_ci		cifs_dbg(VFS,
17062306a36Sopenharmony_ci			"server %s does not support multichannel anymore. Skip secondary channel\n",
17162306a36Sopenharmony_ci			 ses->server->hostname);
17262306a36Sopenharmony_ci
17362306a36Sopenharmony_ci		spin_lock(&ses->chan_lock);
17462306a36Sopenharmony_ci		chan_index = cifs_ses_get_chan_index(ses, server);
17562306a36Sopenharmony_ci		if (chan_index == CIFS_INVAL_CHAN_INDEX) {
17662306a36Sopenharmony_ci			spin_unlock(&ses->chan_lock);
17762306a36Sopenharmony_ci			goto skip_terminate;
17862306a36Sopenharmony_ci		}
17962306a36Sopenharmony_ci
18062306a36Sopenharmony_ci		ses->chans[chan_index].server = NULL;
18162306a36Sopenharmony_ci		server->terminate = true;
18262306a36Sopenharmony_ci		spin_unlock(&ses->chan_lock);
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci		/*
18562306a36Sopenharmony_ci		 * the above reference of server by channel
18662306a36Sopenharmony_ci		 * needs to be dropped without holding chan_lock
18762306a36Sopenharmony_ci		 * as cifs_put_tcp_session takes a higher lock
18862306a36Sopenharmony_ci		 * i.e. cifs_tcp_ses_lock
18962306a36Sopenharmony_ci		 */
19062306a36Sopenharmony_ci		cifs_put_tcp_session(server, from_reconnect);
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci		cifs_signal_cifsd_for_reconnect(server, false);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci		/* mark primary server as needing reconnect */
19562306a36Sopenharmony_ci		pserver = server->primary_server;
19662306a36Sopenharmony_ci		cifs_signal_cifsd_for_reconnect(pserver, false);
19762306a36Sopenharmony_ciskip_terminate:
19862306a36Sopenharmony_ci		return -EHOSTDOWN;
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	cifs_server_dbg(VFS,
20262306a36Sopenharmony_ci		"server does not support multichannel anymore. Disable all other channels\n");
20362306a36Sopenharmony_ci	cifs_disable_secondary_channels(ses);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	return 0;
20762306a36Sopenharmony_ci}
20862306a36Sopenharmony_ci
20962306a36Sopenharmony_cistatic int
21062306a36Sopenharmony_cismb2_reconnect(__le16 smb2_command, struct cifs_tcon *tcon,
21162306a36Sopenharmony_ci	       struct TCP_Server_Info *server, bool from_reconnect)
21262306a36Sopenharmony_ci{
21362306a36Sopenharmony_ci	int rc = 0;
21462306a36Sopenharmony_ci	struct nls_table *nls_codepage = NULL;
21562306a36Sopenharmony_ci	struct cifs_ses *ses;
21662306a36Sopenharmony_ci	int xid;
21762306a36Sopenharmony_ci
21862306a36Sopenharmony_ci	/*
21962306a36Sopenharmony_ci	 * SMB2s NegProt, SessSetup, Logoff do not have tcon yet so
22062306a36Sopenharmony_ci	 * check for tcp and smb session status done differently
22162306a36Sopenharmony_ci	 * for those three - in the calling routine.
22262306a36Sopenharmony_ci	 */
22362306a36Sopenharmony_ci	if (tcon == NULL)
22462306a36Sopenharmony_ci		return 0;
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	/*
22762306a36Sopenharmony_ci	 * Need to also skip SMB2_IOCTL because it is used for checking nested dfs links in
22862306a36Sopenharmony_ci	 * cifs_tree_connect().
22962306a36Sopenharmony_ci	 */
23062306a36Sopenharmony_ci	if (smb2_command == SMB2_TREE_CONNECT || smb2_command == SMB2_IOCTL)
23162306a36Sopenharmony_ci		return 0;
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci	spin_lock(&tcon->tc_lock);
23462306a36Sopenharmony_ci	if (tcon->status == TID_EXITING) {
23562306a36Sopenharmony_ci		/*
23662306a36Sopenharmony_ci		 * only tree disconnect allowed when disconnecting ...
23762306a36Sopenharmony_ci		 */
23862306a36Sopenharmony_ci		if (smb2_command != SMB2_TREE_DISCONNECT) {
23962306a36Sopenharmony_ci			spin_unlock(&tcon->tc_lock);
24062306a36Sopenharmony_ci			cifs_dbg(FYI, "can not send cmd %d while umounting\n",
24162306a36Sopenharmony_ci				 smb2_command);
24262306a36Sopenharmony_ci			return -ENODEV;
24362306a36Sopenharmony_ci		}
24462306a36Sopenharmony_ci	}
24562306a36Sopenharmony_ci	spin_unlock(&tcon->tc_lock);
24662306a36Sopenharmony_ci
24762306a36Sopenharmony_ci	ses = tcon->ses;
24862306a36Sopenharmony_ci	if (!ses)
24962306a36Sopenharmony_ci		return -EIO;
25062306a36Sopenharmony_ci	spin_lock(&ses->ses_lock);
25162306a36Sopenharmony_ci	if (ses->ses_status == SES_EXITING) {
25262306a36Sopenharmony_ci		spin_unlock(&ses->ses_lock);
25362306a36Sopenharmony_ci		return -EIO;
25462306a36Sopenharmony_ci	}
25562306a36Sopenharmony_ci	spin_unlock(&ses->ses_lock);
25662306a36Sopenharmony_ci	if (!ses->server || !server)
25762306a36Sopenharmony_ci		return -EIO;
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	spin_lock(&server->srv_lock);
26062306a36Sopenharmony_ci	if (server->tcpStatus == CifsNeedReconnect) {
26162306a36Sopenharmony_ci		/*
26262306a36Sopenharmony_ci		 * Return to caller for TREE_DISCONNECT and LOGOFF and CLOSE
26362306a36Sopenharmony_ci		 * here since they are implicitly done when session drops.
26462306a36Sopenharmony_ci		 */
26562306a36Sopenharmony_ci		switch (smb2_command) {
26662306a36Sopenharmony_ci		/*
26762306a36Sopenharmony_ci		 * BB Should we keep oplock break and add flush to exceptions?
26862306a36Sopenharmony_ci		 */
26962306a36Sopenharmony_ci		case SMB2_TREE_DISCONNECT:
27062306a36Sopenharmony_ci		case SMB2_CANCEL:
27162306a36Sopenharmony_ci		case SMB2_CLOSE:
27262306a36Sopenharmony_ci		case SMB2_OPLOCK_BREAK:
27362306a36Sopenharmony_ci			spin_unlock(&server->srv_lock);
27462306a36Sopenharmony_ci			return -EAGAIN;
27562306a36Sopenharmony_ci		}
27662306a36Sopenharmony_ci	}
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	/* if server is marked for termination, cifsd will cleanup */
27962306a36Sopenharmony_ci	if (server->terminate) {
28062306a36Sopenharmony_ci		spin_unlock(&server->srv_lock);
28162306a36Sopenharmony_ci		return -EHOSTDOWN;
28262306a36Sopenharmony_ci	}
28362306a36Sopenharmony_ci	spin_unlock(&server->srv_lock);
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ciagain:
28662306a36Sopenharmony_ci	rc = cifs_wait_for_server_reconnect(server, tcon->retry);
28762306a36Sopenharmony_ci	if (rc)
28862306a36Sopenharmony_ci		return rc;
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	spin_lock(&ses->chan_lock);
29162306a36Sopenharmony_ci	if (!cifs_chan_needs_reconnect(ses, server) && !tcon->need_reconnect) {
29262306a36Sopenharmony_ci		spin_unlock(&ses->chan_lock);
29362306a36Sopenharmony_ci		return 0;
29462306a36Sopenharmony_ci	}
29562306a36Sopenharmony_ci	spin_unlock(&ses->chan_lock);
29662306a36Sopenharmony_ci	cifs_dbg(FYI, "sess reconnect mask: 0x%lx, tcon reconnect: %d",
29762306a36Sopenharmony_ci		 tcon->ses->chans_need_reconnect,
29862306a36Sopenharmony_ci		 tcon->need_reconnect);
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	mutex_lock(&ses->session_mutex);
30162306a36Sopenharmony_ci	/*
30262306a36Sopenharmony_ci	 * if this is called by delayed work, and the channel has been disabled
30362306a36Sopenharmony_ci	 * in parallel, the delayed work can continue to execute in parallel
30462306a36Sopenharmony_ci	 * there's a chance that this channel may not exist anymore
30562306a36Sopenharmony_ci	 */
30662306a36Sopenharmony_ci	spin_lock(&server->srv_lock);
30762306a36Sopenharmony_ci	if (server->tcpStatus == CifsExiting) {
30862306a36Sopenharmony_ci		spin_unlock(&server->srv_lock);
30962306a36Sopenharmony_ci		mutex_unlock(&ses->session_mutex);
31062306a36Sopenharmony_ci		rc = -EHOSTDOWN;
31162306a36Sopenharmony_ci		goto out;
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	/*
31562306a36Sopenharmony_ci	 * Recheck after acquire mutex. If another thread is negotiating
31662306a36Sopenharmony_ci	 * and the server never sends an answer the socket will be closed
31762306a36Sopenharmony_ci	 * and tcpStatus set to reconnect.
31862306a36Sopenharmony_ci	 */
31962306a36Sopenharmony_ci	if (server->tcpStatus == CifsNeedReconnect) {
32062306a36Sopenharmony_ci		spin_unlock(&server->srv_lock);
32162306a36Sopenharmony_ci		mutex_unlock(&ses->session_mutex);
32262306a36Sopenharmony_ci
32362306a36Sopenharmony_ci		if (tcon->retry)
32462306a36Sopenharmony_ci			goto again;
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci		rc = -EHOSTDOWN;
32762306a36Sopenharmony_ci		goto out;
32862306a36Sopenharmony_ci	}
32962306a36Sopenharmony_ci	spin_unlock(&server->srv_lock);
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	nls_codepage = ses->local_nls;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	/*
33462306a36Sopenharmony_ci	 * need to prevent multiple threads trying to simultaneously
33562306a36Sopenharmony_ci	 * reconnect the same SMB session
33662306a36Sopenharmony_ci	 */
33762306a36Sopenharmony_ci	spin_lock(&ses->ses_lock);
33862306a36Sopenharmony_ci	spin_lock(&ses->chan_lock);
33962306a36Sopenharmony_ci	if (!cifs_chan_needs_reconnect(ses, server) &&
34062306a36Sopenharmony_ci	    ses->ses_status == SES_GOOD) {
34162306a36Sopenharmony_ci		spin_unlock(&ses->chan_lock);
34262306a36Sopenharmony_ci		spin_unlock(&ses->ses_lock);
34362306a36Sopenharmony_ci		/* this means that we only need to tree connect */
34462306a36Sopenharmony_ci		if (tcon->need_reconnect)
34562306a36Sopenharmony_ci			goto skip_sess_setup;
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci		mutex_unlock(&ses->session_mutex);
34862306a36Sopenharmony_ci		goto out;
34962306a36Sopenharmony_ci	}
35062306a36Sopenharmony_ci	spin_unlock(&ses->chan_lock);
35162306a36Sopenharmony_ci	spin_unlock(&ses->ses_lock);
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	rc = cifs_negotiate_protocol(0, ses, server);
35462306a36Sopenharmony_ci	if (!rc) {
35562306a36Sopenharmony_ci		/*
35662306a36Sopenharmony_ci		 * if server stopped supporting multichannel
35762306a36Sopenharmony_ci		 * and the first channel reconnected, disable all the others.
35862306a36Sopenharmony_ci		 */
35962306a36Sopenharmony_ci		if (ses->chan_count > 1 &&
36062306a36Sopenharmony_ci		    !(server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) {
36162306a36Sopenharmony_ci			rc = cifs_chan_skip_or_disable(ses, server,
36262306a36Sopenharmony_ci						       from_reconnect);
36362306a36Sopenharmony_ci			if (rc) {
36462306a36Sopenharmony_ci				mutex_unlock(&ses->session_mutex);
36562306a36Sopenharmony_ci				goto out;
36662306a36Sopenharmony_ci			}
36762306a36Sopenharmony_ci		}
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci		rc = cifs_setup_session(0, ses, server, nls_codepage);
37062306a36Sopenharmony_ci		if ((rc == -EACCES) && !tcon->retry) {
37162306a36Sopenharmony_ci			mutex_unlock(&ses->session_mutex);
37262306a36Sopenharmony_ci			rc = -EHOSTDOWN;
37362306a36Sopenharmony_ci			goto failed;
37462306a36Sopenharmony_ci		} else if (rc) {
37562306a36Sopenharmony_ci			mutex_unlock(&ses->session_mutex);
37662306a36Sopenharmony_ci			goto out;
37762306a36Sopenharmony_ci		}
37862306a36Sopenharmony_ci	} else {
37962306a36Sopenharmony_ci		mutex_unlock(&ses->session_mutex);
38062306a36Sopenharmony_ci		goto out;
38162306a36Sopenharmony_ci	}
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ciskip_sess_setup:
38462306a36Sopenharmony_ci	if (!tcon->need_reconnect) {
38562306a36Sopenharmony_ci		mutex_unlock(&ses->session_mutex);
38662306a36Sopenharmony_ci		goto out;
38762306a36Sopenharmony_ci	}
38862306a36Sopenharmony_ci	cifs_mark_open_files_invalid(tcon);
38962306a36Sopenharmony_ci	if (tcon->use_persistent)
39062306a36Sopenharmony_ci		tcon->need_reopen_files = true;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	rc = cifs_tree_connect(0, tcon, nls_codepage);
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	cifs_dbg(FYI, "reconnect tcon rc = %d\n", rc);
39562306a36Sopenharmony_ci	if (rc) {
39662306a36Sopenharmony_ci		/* If sess reconnected but tcon didn't, something strange ... */
39762306a36Sopenharmony_ci		mutex_unlock(&ses->session_mutex);
39862306a36Sopenharmony_ci		cifs_dbg(VFS, "reconnect tcon failed rc = %d\n", rc);
39962306a36Sopenharmony_ci		goto out;
40062306a36Sopenharmony_ci	}
40162306a36Sopenharmony_ci
40262306a36Sopenharmony_ci	spin_lock(&ses->ses_lock);
40362306a36Sopenharmony_ci	if (ses->flags & CIFS_SES_FLAG_SCALE_CHANNELS) {
40462306a36Sopenharmony_ci		spin_unlock(&ses->ses_lock);
40562306a36Sopenharmony_ci		mutex_unlock(&ses->session_mutex);
40662306a36Sopenharmony_ci		goto skip_add_channels;
40762306a36Sopenharmony_ci	}
40862306a36Sopenharmony_ci	ses->flags |= CIFS_SES_FLAG_SCALE_CHANNELS;
40962306a36Sopenharmony_ci	spin_unlock(&ses->ses_lock);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	if (!rc &&
41262306a36Sopenharmony_ci	    (server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) {
41362306a36Sopenharmony_ci		mutex_unlock(&ses->session_mutex);
41462306a36Sopenharmony_ci
41562306a36Sopenharmony_ci		/*
41662306a36Sopenharmony_ci		 * query server network interfaces, in case they change
41762306a36Sopenharmony_ci		 */
41862306a36Sopenharmony_ci		xid = get_xid();
41962306a36Sopenharmony_ci		rc = SMB3_request_interfaces(xid, tcon, false);
42062306a36Sopenharmony_ci		free_xid(xid);
42162306a36Sopenharmony_ci
42262306a36Sopenharmony_ci		if (rc == -EOPNOTSUPP && ses->chan_count > 1) {
42362306a36Sopenharmony_ci			/*
42462306a36Sopenharmony_ci			 * some servers like Azure SMB server do not advertise
42562306a36Sopenharmony_ci			 * that multichannel has been disabled with server
42662306a36Sopenharmony_ci			 * capabilities, rather return STATUS_NOT_IMPLEMENTED.
42762306a36Sopenharmony_ci			 * treat this as server not supporting multichannel
42862306a36Sopenharmony_ci			 */
42962306a36Sopenharmony_ci
43062306a36Sopenharmony_ci			rc = cifs_chan_skip_or_disable(ses, server,
43162306a36Sopenharmony_ci						       from_reconnect);
43262306a36Sopenharmony_ci			goto skip_add_channels;
43362306a36Sopenharmony_ci		} else if (rc)
43462306a36Sopenharmony_ci			cifs_dbg(FYI, "%s: failed to query server interfaces: %d\n",
43562306a36Sopenharmony_ci				 __func__, rc);
43662306a36Sopenharmony_ci
43762306a36Sopenharmony_ci		if (ses->chan_max > ses->chan_count &&
43862306a36Sopenharmony_ci		    ses->iface_count &&
43962306a36Sopenharmony_ci		    !SERVER_IS_CHAN(server)) {
44062306a36Sopenharmony_ci			if (ses->chan_count == 1) {
44162306a36Sopenharmony_ci				cifs_server_dbg(VFS, "supports multichannel now\n");
44262306a36Sopenharmony_ci				queue_delayed_work(cifsiod_wq, &tcon->query_interfaces,
44362306a36Sopenharmony_ci						 (SMB_INTERFACE_POLL_INTERVAL * HZ));
44462306a36Sopenharmony_ci			}
44562306a36Sopenharmony_ci
44662306a36Sopenharmony_ci			cifs_try_adding_channels(ses);
44762306a36Sopenharmony_ci		}
44862306a36Sopenharmony_ci	} else {
44962306a36Sopenharmony_ci		mutex_unlock(&ses->session_mutex);
45062306a36Sopenharmony_ci	}
45162306a36Sopenharmony_ci
45262306a36Sopenharmony_ciskip_add_channels:
45362306a36Sopenharmony_ci	spin_lock(&ses->ses_lock);
45462306a36Sopenharmony_ci	ses->flags &= ~CIFS_SES_FLAG_SCALE_CHANNELS;
45562306a36Sopenharmony_ci	spin_unlock(&ses->ses_lock);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	if (smb2_command != SMB2_INTERNAL_CMD)
45862306a36Sopenharmony_ci		mod_delayed_work(cifsiod_wq, &server->reconnect, 0);
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_ci	atomic_inc(&tconInfoReconnectCount);
46162306a36Sopenharmony_ciout:
46262306a36Sopenharmony_ci	/*
46362306a36Sopenharmony_ci	 * Check if handle based operation so we know whether we can continue
46462306a36Sopenharmony_ci	 * or not without returning to caller to reset file handle.
46562306a36Sopenharmony_ci	 */
46662306a36Sopenharmony_ci	/*
46762306a36Sopenharmony_ci	 * BB Is flush done by server on drop of tcp session? Should we special
46862306a36Sopenharmony_ci	 * case it and skip above?
46962306a36Sopenharmony_ci	 */
47062306a36Sopenharmony_ci	switch (smb2_command) {
47162306a36Sopenharmony_ci	case SMB2_FLUSH:
47262306a36Sopenharmony_ci	case SMB2_READ:
47362306a36Sopenharmony_ci	case SMB2_WRITE:
47462306a36Sopenharmony_ci	case SMB2_LOCK:
47562306a36Sopenharmony_ci	case SMB2_QUERY_DIRECTORY:
47662306a36Sopenharmony_ci	case SMB2_CHANGE_NOTIFY:
47762306a36Sopenharmony_ci	case SMB2_QUERY_INFO:
47862306a36Sopenharmony_ci	case SMB2_SET_INFO:
47962306a36Sopenharmony_ci		rc = -EAGAIN;
48062306a36Sopenharmony_ci	}
48162306a36Sopenharmony_cifailed:
48262306a36Sopenharmony_ci	return rc;
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_cistatic void
48662306a36Sopenharmony_cifill_small_buf(__le16 smb2_command, struct cifs_tcon *tcon,
48762306a36Sopenharmony_ci	       struct TCP_Server_Info *server,
48862306a36Sopenharmony_ci	       void *buf,
48962306a36Sopenharmony_ci	       unsigned int *total_len)
49062306a36Sopenharmony_ci{
49162306a36Sopenharmony_ci	struct smb2_pdu *spdu = buf;
49262306a36Sopenharmony_ci	/* lookup word count ie StructureSize from table */
49362306a36Sopenharmony_ci	__u16 parmsize = smb2_req_struct_sizes[le16_to_cpu(smb2_command)];
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	/*
49662306a36Sopenharmony_ci	 * smaller than SMALL_BUFFER_SIZE but bigger than fixed area of
49762306a36Sopenharmony_ci	 * largest operations (Create)
49862306a36Sopenharmony_ci	 */
49962306a36Sopenharmony_ci	memset(buf, 0, 256);
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	smb2_hdr_assemble(&spdu->hdr, smb2_command, tcon, server);
50262306a36Sopenharmony_ci	spdu->StructureSize2 = cpu_to_le16(parmsize);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	*total_len = parmsize + sizeof(struct smb2_hdr);
50562306a36Sopenharmony_ci}
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci/*
50862306a36Sopenharmony_ci * Allocate and return pointer to an SMB request hdr, and set basic
50962306a36Sopenharmony_ci * SMB information in the SMB header. If the return code is zero, this
51062306a36Sopenharmony_ci * function must have filled in request_buf pointer.
51162306a36Sopenharmony_ci */
51262306a36Sopenharmony_cistatic int __smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon,
51362306a36Sopenharmony_ci				 struct TCP_Server_Info *server,
51462306a36Sopenharmony_ci				 void **request_buf, unsigned int *total_len)
51562306a36Sopenharmony_ci{
51662306a36Sopenharmony_ci	/* BB eventually switch this to SMB2 specific small buf size */
51762306a36Sopenharmony_ci	switch (smb2_command) {
51862306a36Sopenharmony_ci	case SMB2_SET_INFO:
51962306a36Sopenharmony_ci	case SMB2_QUERY_INFO:
52062306a36Sopenharmony_ci		*request_buf = cifs_buf_get();
52162306a36Sopenharmony_ci		break;
52262306a36Sopenharmony_ci	default:
52362306a36Sopenharmony_ci		*request_buf = cifs_small_buf_get();
52462306a36Sopenharmony_ci		break;
52562306a36Sopenharmony_ci	}
52662306a36Sopenharmony_ci	if (*request_buf == NULL) {
52762306a36Sopenharmony_ci		/* BB should we add a retry in here if not a writepage? */
52862306a36Sopenharmony_ci		return -ENOMEM;
52962306a36Sopenharmony_ci	}
53062306a36Sopenharmony_ci
53162306a36Sopenharmony_ci	fill_small_buf(smb2_command, tcon, server,
53262306a36Sopenharmony_ci		       (struct smb2_hdr *)(*request_buf),
53362306a36Sopenharmony_ci		       total_len);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	if (tcon != NULL) {
53662306a36Sopenharmony_ci		uint16_t com_code = le16_to_cpu(smb2_command);
53762306a36Sopenharmony_ci		cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_sent[com_code]);
53862306a36Sopenharmony_ci		cifs_stats_inc(&tcon->num_smbs_sent);
53962306a36Sopenharmony_ci	}
54062306a36Sopenharmony_ci
54162306a36Sopenharmony_ci	return 0;
54262306a36Sopenharmony_ci}
54362306a36Sopenharmony_ci
54462306a36Sopenharmony_cistatic int smb2_plain_req_init(__le16 smb2_command, struct cifs_tcon *tcon,
54562306a36Sopenharmony_ci			       struct TCP_Server_Info *server,
54662306a36Sopenharmony_ci			       void **request_buf, unsigned int *total_len)
54762306a36Sopenharmony_ci{
54862306a36Sopenharmony_ci	int rc;
54962306a36Sopenharmony_ci
55062306a36Sopenharmony_ci	rc = smb2_reconnect(smb2_command, tcon, server, false);
55162306a36Sopenharmony_ci	if (rc)
55262306a36Sopenharmony_ci		return rc;
55362306a36Sopenharmony_ci
55462306a36Sopenharmony_ci	return __smb2_plain_req_init(smb2_command, tcon, server, request_buf,
55562306a36Sopenharmony_ci				     total_len);
55662306a36Sopenharmony_ci}
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_cistatic int smb2_ioctl_req_init(u32 opcode, struct cifs_tcon *tcon,
55962306a36Sopenharmony_ci			       struct TCP_Server_Info *server,
56062306a36Sopenharmony_ci			       void **request_buf, unsigned int *total_len)
56162306a36Sopenharmony_ci{
56262306a36Sopenharmony_ci	/* Skip reconnect only for FSCTL_VALIDATE_NEGOTIATE_INFO IOCTLs */
56362306a36Sopenharmony_ci	if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO) {
56462306a36Sopenharmony_ci		return __smb2_plain_req_init(SMB2_IOCTL, tcon, server,
56562306a36Sopenharmony_ci					     request_buf, total_len);
56662306a36Sopenharmony_ci	}
56762306a36Sopenharmony_ci	return smb2_plain_req_init(SMB2_IOCTL, tcon, server,
56862306a36Sopenharmony_ci				   request_buf, total_len);
56962306a36Sopenharmony_ci}
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci/* For explanation of negotiate contexts see MS-SMB2 section 2.2.3.1 */
57262306a36Sopenharmony_ci
57362306a36Sopenharmony_cistatic void
57462306a36Sopenharmony_cibuild_preauth_ctxt(struct smb2_preauth_neg_context *pneg_ctxt)
57562306a36Sopenharmony_ci{
57662306a36Sopenharmony_ci	pneg_ctxt->ContextType = SMB2_PREAUTH_INTEGRITY_CAPABILITIES;
57762306a36Sopenharmony_ci	pneg_ctxt->DataLength = cpu_to_le16(38);
57862306a36Sopenharmony_ci	pneg_ctxt->HashAlgorithmCount = cpu_to_le16(1);
57962306a36Sopenharmony_ci	pneg_ctxt->SaltLength = cpu_to_le16(SMB311_SALT_SIZE);
58062306a36Sopenharmony_ci	get_random_bytes(pneg_ctxt->Salt, SMB311_SALT_SIZE);
58162306a36Sopenharmony_ci	pneg_ctxt->HashAlgorithms = SMB2_PREAUTH_INTEGRITY_SHA512;
58262306a36Sopenharmony_ci}
58362306a36Sopenharmony_ci
58462306a36Sopenharmony_cistatic void
58562306a36Sopenharmony_cibuild_compression_ctxt(struct smb2_compression_capabilities_context *pneg_ctxt)
58662306a36Sopenharmony_ci{
58762306a36Sopenharmony_ci	pneg_ctxt->ContextType = SMB2_COMPRESSION_CAPABILITIES;
58862306a36Sopenharmony_ci	pneg_ctxt->DataLength =
58962306a36Sopenharmony_ci		cpu_to_le16(sizeof(struct smb2_compression_capabilities_context)
59062306a36Sopenharmony_ci			  - sizeof(struct smb2_neg_context));
59162306a36Sopenharmony_ci	pneg_ctxt->CompressionAlgorithmCount = cpu_to_le16(3);
59262306a36Sopenharmony_ci	pneg_ctxt->CompressionAlgorithms[0] = SMB3_COMPRESS_LZ77;
59362306a36Sopenharmony_ci	pneg_ctxt->CompressionAlgorithms[1] = SMB3_COMPRESS_LZ77_HUFF;
59462306a36Sopenharmony_ci	pneg_ctxt->CompressionAlgorithms[2] = SMB3_COMPRESS_LZNT1;
59562306a36Sopenharmony_ci}
59662306a36Sopenharmony_ci
59762306a36Sopenharmony_cistatic unsigned int
59862306a36Sopenharmony_cibuild_signing_ctxt(struct smb2_signing_capabilities *pneg_ctxt)
59962306a36Sopenharmony_ci{
60062306a36Sopenharmony_ci	unsigned int ctxt_len = sizeof(struct smb2_signing_capabilities);
60162306a36Sopenharmony_ci	unsigned short num_algs = 1; /* number of signing algorithms sent */
60262306a36Sopenharmony_ci
60362306a36Sopenharmony_ci	pneg_ctxt->ContextType = SMB2_SIGNING_CAPABILITIES;
60462306a36Sopenharmony_ci	/*
60562306a36Sopenharmony_ci	 * Context Data length must be rounded to multiple of 8 for some servers
60662306a36Sopenharmony_ci	 */
60762306a36Sopenharmony_ci	pneg_ctxt->DataLength = cpu_to_le16(ALIGN(sizeof(struct smb2_signing_capabilities) -
60862306a36Sopenharmony_ci					    sizeof(struct smb2_neg_context) +
60962306a36Sopenharmony_ci					    (num_algs * sizeof(u16)), 8));
61062306a36Sopenharmony_ci	pneg_ctxt->SigningAlgorithmCount = cpu_to_le16(num_algs);
61162306a36Sopenharmony_ci	pneg_ctxt->SigningAlgorithms[0] = cpu_to_le16(SIGNING_ALG_AES_CMAC);
61262306a36Sopenharmony_ci
61362306a36Sopenharmony_ci	ctxt_len += sizeof(__le16) * num_algs;
61462306a36Sopenharmony_ci	ctxt_len = ALIGN(ctxt_len, 8);
61562306a36Sopenharmony_ci	return ctxt_len;
61662306a36Sopenharmony_ci	/* TBD add SIGNING_ALG_AES_GMAC and/or SIGNING_ALG_HMAC_SHA256 */
61762306a36Sopenharmony_ci}
61862306a36Sopenharmony_ci
61962306a36Sopenharmony_cistatic void
62062306a36Sopenharmony_cibuild_encrypt_ctxt(struct smb2_encryption_neg_context *pneg_ctxt)
62162306a36Sopenharmony_ci{
62262306a36Sopenharmony_ci	pneg_ctxt->ContextType = SMB2_ENCRYPTION_CAPABILITIES;
62362306a36Sopenharmony_ci	if (require_gcm_256) {
62462306a36Sopenharmony_ci		pneg_ctxt->DataLength = cpu_to_le16(4); /* Cipher Count + 1 cipher */
62562306a36Sopenharmony_ci		pneg_ctxt->CipherCount = cpu_to_le16(1);
62662306a36Sopenharmony_ci		pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES256_GCM;
62762306a36Sopenharmony_ci	} else if (enable_gcm_256) {
62862306a36Sopenharmony_ci		pneg_ctxt->DataLength = cpu_to_le16(8); /* Cipher Count + 3 ciphers */
62962306a36Sopenharmony_ci		pneg_ctxt->CipherCount = cpu_to_le16(3);
63062306a36Sopenharmony_ci		pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES128_GCM;
63162306a36Sopenharmony_ci		pneg_ctxt->Ciphers[1] = SMB2_ENCRYPTION_AES256_GCM;
63262306a36Sopenharmony_ci		pneg_ctxt->Ciphers[2] = SMB2_ENCRYPTION_AES128_CCM;
63362306a36Sopenharmony_ci	} else {
63462306a36Sopenharmony_ci		pneg_ctxt->DataLength = cpu_to_le16(6); /* Cipher Count + 2 ciphers */
63562306a36Sopenharmony_ci		pneg_ctxt->CipherCount = cpu_to_le16(2);
63662306a36Sopenharmony_ci		pneg_ctxt->Ciphers[0] = SMB2_ENCRYPTION_AES128_GCM;
63762306a36Sopenharmony_ci		pneg_ctxt->Ciphers[1] = SMB2_ENCRYPTION_AES128_CCM;
63862306a36Sopenharmony_ci	}
63962306a36Sopenharmony_ci}
64062306a36Sopenharmony_ci
64162306a36Sopenharmony_cistatic unsigned int
64262306a36Sopenharmony_cibuild_netname_ctxt(struct smb2_netname_neg_context *pneg_ctxt, char *hostname)
64362306a36Sopenharmony_ci{
64462306a36Sopenharmony_ci	struct nls_table *cp = load_nls_default();
64562306a36Sopenharmony_ci
64662306a36Sopenharmony_ci	pneg_ctxt->ContextType = SMB2_NETNAME_NEGOTIATE_CONTEXT_ID;
64762306a36Sopenharmony_ci
64862306a36Sopenharmony_ci	/* copy up to max of first 100 bytes of server name to NetName field */
64962306a36Sopenharmony_ci	pneg_ctxt->DataLength = cpu_to_le16(2 * cifs_strtoUTF16(pneg_ctxt->NetName, hostname, 100, cp));
65062306a36Sopenharmony_ci	/* context size is DataLength + minimal smb2_neg_context */
65162306a36Sopenharmony_ci	return ALIGN(le16_to_cpu(pneg_ctxt->DataLength) + sizeof(struct smb2_neg_context), 8);
65262306a36Sopenharmony_ci}
65362306a36Sopenharmony_ci
65462306a36Sopenharmony_cistatic void
65562306a36Sopenharmony_cibuild_posix_ctxt(struct smb2_posix_neg_context *pneg_ctxt)
65662306a36Sopenharmony_ci{
65762306a36Sopenharmony_ci	pneg_ctxt->ContextType = SMB2_POSIX_EXTENSIONS_AVAILABLE;
65862306a36Sopenharmony_ci	pneg_ctxt->DataLength = cpu_to_le16(POSIX_CTXT_DATA_LEN);
65962306a36Sopenharmony_ci	/* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */
66062306a36Sopenharmony_ci	pneg_ctxt->Name[0] = 0x93;
66162306a36Sopenharmony_ci	pneg_ctxt->Name[1] = 0xAD;
66262306a36Sopenharmony_ci	pneg_ctxt->Name[2] = 0x25;
66362306a36Sopenharmony_ci	pneg_ctxt->Name[3] = 0x50;
66462306a36Sopenharmony_ci	pneg_ctxt->Name[4] = 0x9C;
66562306a36Sopenharmony_ci	pneg_ctxt->Name[5] = 0xB4;
66662306a36Sopenharmony_ci	pneg_ctxt->Name[6] = 0x11;
66762306a36Sopenharmony_ci	pneg_ctxt->Name[7] = 0xE7;
66862306a36Sopenharmony_ci	pneg_ctxt->Name[8] = 0xB4;
66962306a36Sopenharmony_ci	pneg_ctxt->Name[9] = 0x23;
67062306a36Sopenharmony_ci	pneg_ctxt->Name[10] = 0x83;
67162306a36Sopenharmony_ci	pneg_ctxt->Name[11] = 0xDE;
67262306a36Sopenharmony_ci	pneg_ctxt->Name[12] = 0x96;
67362306a36Sopenharmony_ci	pneg_ctxt->Name[13] = 0x8B;
67462306a36Sopenharmony_ci	pneg_ctxt->Name[14] = 0xCD;
67562306a36Sopenharmony_ci	pneg_ctxt->Name[15] = 0x7C;
67662306a36Sopenharmony_ci}
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_cistatic void
67962306a36Sopenharmony_ciassemble_neg_contexts(struct smb2_negotiate_req *req,
68062306a36Sopenharmony_ci		      struct TCP_Server_Info *server, unsigned int *total_len)
68162306a36Sopenharmony_ci{
68262306a36Sopenharmony_ci	unsigned int ctxt_len, neg_context_count;
68362306a36Sopenharmony_ci	struct TCP_Server_Info *pserver;
68462306a36Sopenharmony_ci	char *pneg_ctxt;
68562306a36Sopenharmony_ci	char *hostname;
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_ci	if (*total_len > 200) {
68862306a36Sopenharmony_ci		/* In case length corrupted don't want to overrun smb buffer */
68962306a36Sopenharmony_ci		cifs_server_dbg(VFS, "Bad frame length assembling neg contexts\n");
69062306a36Sopenharmony_ci		return;
69162306a36Sopenharmony_ci	}
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	/*
69462306a36Sopenharmony_ci	 * round up total_len of fixed part of SMB3 negotiate request to 8
69562306a36Sopenharmony_ci	 * byte boundary before adding negotiate contexts
69662306a36Sopenharmony_ci	 */
69762306a36Sopenharmony_ci	*total_len = ALIGN(*total_len, 8);
69862306a36Sopenharmony_ci
69962306a36Sopenharmony_ci	pneg_ctxt = (*total_len) + (char *)req;
70062306a36Sopenharmony_ci	req->NegotiateContextOffset = cpu_to_le32(*total_len);
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci	build_preauth_ctxt((struct smb2_preauth_neg_context *)pneg_ctxt);
70362306a36Sopenharmony_ci	ctxt_len = ALIGN(sizeof(struct smb2_preauth_neg_context), 8);
70462306a36Sopenharmony_ci	*total_len += ctxt_len;
70562306a36Sopenharmony_ci	pneg_ctxt += ctxt_len;
70662306a36Sopenharmony_ci
70762306a36Sopenharmony_ci	build_encrypt_ctxt((struct smb2_encryption_neg_context *)pneg_ctxt);
70862306a36Sopenharmony_ci	ctxt_len = ALIGN(sizeof(struct smb2_encryption_neg_context), 8);
70962306a36Sopenharmony_ci	*total_len += ctxt_len;
71062306a36Sopenharmony_ci	pneg_ctxt += ctxt_len;
71162306a36Sopenharmony_ci
71262306a36Sopenharmony_ci	/*
71362306a36Sopenharmony_ci	 * secondary channels don't have the hostname field populated
71462306a36Sopenharmony_ci	 * use the hostname field in the primary channel instead
71562306a36Sopenharmony_ci	 */
71662306a36Sopenharmony_ci	pserver = SERVER_IS_CHAN(server) ? server->primary_server : server;
71762306a36Sopenharmony_ci	cifs_server_lock(pserver);
71862306a36Sopenharmony_ci	hostname = pserver->hostname;
71962306a36Sopenharmony_ci	if (hostname && (hostname[0] != 0)) {
72062306a36Sopenharmony_ci		ctxt_len = build_netname_ctxt((struct smb2_netname_neg_context *)pneg_ctxt,
72162306a36Sopenharmony_ci					      hostname);
72262306a36Sopenharmony_ci		*total_len += ctxt_len;
72362306a36Sopenharmony_ci		pneg_ctxt += ctxt_len;
72462306a36Sopenharmony_ci		neg_context_count = 3;
72562306a36Sopenharmony_ci	} else
72662306a36Sopenharmony_ci		neg_context_count = 2;
72762306a36Sopenharmony_ci	cifs_server_unlock(pserver);
72862306a36Sopenharmony_ci
72962306a36Sopenharmony_ci	build_posix_ctxt((struct smb2_posix_neg_context *)pneg_ctxt);
73062306a36Sopenharmony_ci	*total_len += sizeof(struct smb2_posix_neg_context);
73162306a36Sopenharmony_ci	pneg_ctxt += sizeof(struct smb2_posix_neg_context);
73262306a36Sopenharmony_ci	neg_context_count++;
73362306a36Sopenharmony_ci
73462306a36Sopenharmony_ci	if (server->compress_algorithm) {
73562306a36Sopenharmony_ci		build_compression_ctxt((struct smb2_compression_capabilities_context *)
73662306a36Sopenharmony_ci				pneg_ctxt);
73762306a36Sopenharmony_ci		ctxt_len = ALIGN(sizeof(struct smb2_compression_capabilities_context), 8);
73862306a36Sopenharmony_ci		*total_len += ctxt_len;
73962306a36Sopenharmony_ci		pneg_ctxt += ctxt_len;
74062306a36Sopenharmony_ci		neg_context_count++;
74162306a36Sopenharmony_ci	}
74262306a36Sopenharmony_ci
74362306a36Sopenharmony_ci	if (enable_negotiate_signing) {
74462306a36Sopenharmony_ci		ctxt_len = build_signing_ctxt((struct smb2_signing_capabilities *)
74562306a36Sopenharmony_ci				pneg_ctxt);
74662306a36Sopenharmony_ci		*total_len += ctxt_len;
74762306a36Sopenharmony_ci		pneg_ctxt += ctxt_len;
74862306a36Sopenharmony_ci		neg_context_count++;
74962306a36Sopenharmony_ci	}
75062306a36Sopenharmony_ci
75162306a36Sopenharmony_ci	/* check for and add transport_capabilities and signing capabilities */
75262306a36Sopenharmony_ci	req->NegotiateContextCount = cpu_to_le16(neg_context_count);
75362306a36Sopenharmony_ci
75462306a36Sopenharmony_ci}
75562306a36Sopenharmony_ci
75662306a36Sopenharmony_ci/* If invalid preauth context warn but use what we requested, SHA-512 */
75762306a36Sopenharmony_cistatic void decode_preauth_context(struct smb2_preauth_neg_context *ctxt)
75862306a36Sopenharmony_ci{
75962306a36Sopenharmony_ci	unsigned int len = le16_to_cpu(ctxt->DataLength);
76062306a36Sopenharmony_ci
76162306a36Sopenharmony_ci	/*
76262306a36Sopenharmony_ci	 * Caller checked that DataLength remains within SMB boundary. We still
76362306a36Sopenharmony_ci	 * need to confirm that one HashAlgorithms member is accounted for.
76462306a36Sopenharmony_ci	 */
76562306a36Sopenharmony_ci	if (len < MIN_PREAUTH_CTXT_DATA_LEN) {
76662306a36Sopenharmony_ci		pr_warn_once("server sent bad preauth context\n");
76762306a36Sopenharmony_ci		return;
76862306a36Sopenharmony_ci	} else if (len < MIN_PREAUTH_CTXT_DATA_LEN + le16_to_cpu(ctxt->SaltLength)) {
76962306a36Sopenharmony_ci		pr_warn_once("server sent invalid SaltLength\n");
77062306a36Sopenharmony_ci		return;
77162306a36Sopenharmony_ci	}
77262306a36Sopenharmony_ci	if (le16_to_cpu(ctxt->HashAlgorithmCount) != 1)
77362306a36Sopenharmony_ci		pr_warn_once("Invalid SMB3 hash algorithm count\n");
77462306a36Sopenharmony_ci	if (ctxt->HashAlgorithms != SMB2_PREAUTH_INTEGRITY_SHA512)
77562306a36Sopenharmony_ci		pr_warn_once("unknown SMB3 hash algorithm\n");
77662306a36Sopenharmony_ci}
77762306a36Sopenharmony_ci
77862306a36Sopenharmony_cistatic void decode_compress_ctx(struct TCP_Server_Info *server,
77962306a36Sopenharmony_ci			 struct smb2_compression_capabilities_context *ctxt)
78062306a36Sopenharmony_ci{
78162306a36Sopenharmony_ci	unsigned int len = le16_to_cpu(ctxt->DataLength);
78262306a36Sopenharmony_ci
78362306a36Sopenharmony_ci	/*
78462306a36Sopenharmony_ci	 * Caller checked that DataLength remains within SMB boundary. We still
78562306a36Sopenharmony_ci	 * need to confirm that one CompressionAlgorithms member is accounted
78662306a36Sopenharmony_ci	 * for.
78762306a36Sopenharmony_ci	 */
78862306a36Sopenharmony_ci	if (len < 10) {
78962306a36Sopenharmony_ci		pr_warn_once("server sent bad compression cntxt\n");
79062306a36Sopenharmony_ci		return;
79162306a36Sopenharmony_ci	}
79262306a36Sopenharmony_ci	if (le16_to_cpu(ctxt->CompressionAlgorithmCount) != 1) {
79362306a36Sopenharmony_ci		pr_warn_once("Invalid SMB3 compress algorithm count\n");
79462306a36Sopenharmony_ci		return;
79562306a36Sopenharmony_ci	}
79662306a36Sopenharmony_ci	if (le16_to_cpu(ctxt->CompressionAlgorithms[0]) > 3) {
79762306a36Sopenharmony_ci		pr_warn_once("unknown compression algorithm\n");
79862306a36Sopenharmony_ci		return;
79962306a36Sopenharmony_ci	}
80062306a36Sopenharmony_ci	server->compress_algorithm = ctxt->CompressionAlgorithms[0];
80162306a36Sopenharmony_ci}
80262306a36Sopenharmony_ci
80362306a36Sopenharmony_cistatic int decode_encrypt_ctx(struct TCP_Server_Info *server,
80462306a36Sopenharmony_ci			      struct smb2_encryption_neg_context *ctxt)
80562306a36Sopenharmony_ci{
80662306a36Sopenharmony_ci	unsigned int len = le16_to_cpu(ctxt->DataLength);
80762306a36Sopenharmony_ci
80862306a36Sopenharmony_ci	cifs_dbg(FYI, "decode SMB3.11 encryption neg context of len %d\n", len);
80962306a36Sopenharmony_ci	/*
81062306a36Sopenharmony_ci	 * Caller checked that DataLength remains within SMB boundary. We still
81162306a36Sopenharmony_ci	 * need to confirm that one Cipher flexible array member is accounted
81262306a36Sopenharmony_ci	 * for.
81362306a36Sopenharmony_ci	 */
81462306a36Sopenharmony_ci	if (len < MIN_ENCRYPT_CTXT_DATA_LEN) {
81562306a36Sopenharmony_ci		pr_warn_once("server sent bad crypto ctxt len\n");
81662306a36Sopenharmony_ci		return -EINVAL;
81762306a36Sopenharmony_ci	}
81862306a36Sopenharmony_ci
81962306a36Sopenharmony_ci	if (le16_to_cpu(ctxt->CipherCount) != 1) {
82062306a36Sopenharmony_ci		pr_warn_once("Invalid SMB3.11 cipher count\n");
82162306a36Sopenharmony_ci		return -EINVAL;
82262306a36Sopenharmony_ci	}
82362306a36Sopenharmony_ci	cifs_dbg(FYI, "SMB311 cipher type:%d\n", le16_to_cpu(ctxt->Ciphers[0]));
82462306a36Sopenharmony_ci	if (require_gcm_256) {
82562306a36Sopenharmony_ci		if (ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES256_GCM) {
82662306a36Sopenharmony_ci			cifs_dbg(VFS, "Server does not support requested encryption type (AES256 GCM)\n");
82762306a36Sopenharmony_ci			return -EOPNOTSUPP;
82862306a36Sopenharmony_ci		}
82962306a36Sopenharmony_ci	} else if (ctxt->Ciphers[0] == 0) {
83062306a36Sopenharmony_ci		/*
83162306a36Sopenharmony_ci		 * e.g. if server only supported AES256_CCM (very unlikely)
83262306a36Sopenharmony_ci		 * or server supported no encryption types or had all disabled.
83362306a36Sopenharmony_ci		 * Since GLOBAL_CAP_ENCRYPTION will be not set, in the case
83462306a36Sopenharmony_ci		 * in which mount requested encryption ("seal") checks later
83562306a36Sopenharmony_ci		 * on during tree connection will return proper rc, but if
83662306a36Sopenharmony_ci		 * seal not requested by client, since server is allowed to
83762306a36Sopenharmony_ci		 * return 0 to indicate no supported cipher, we can't fail here
83862306a36Sopenharmony_ci		 */
83962306a36Sopenharmony_ci		server->cipher_type = 0;
84062306a36Sopenharmony_ci		server->capabilities &= ~SMB2_GLOBAL_CAP_ENCRYPTION;
84162306a36Sopenharmony_ci		pr_warn_once("Server does not support requested encryption types\n");
84262306a36Sopenharmony_ci		return 0;
84362306a36Sopenharmony_ci	} else if ((ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES128_CCM) &&
84462306a36Sopenharmony_ci		   (ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES128_GCM) &&
84562306a36Sopenharmony_ci		   (ctxt->Ciphers[0] != SMB2_ENCRYPTION_AES256_GCM)) {
84662306a36Sopenharmony_ci		/* server returned a cipher we didn't ask for */
84762306a36Sopenharmony_ci		pr_warn_once("Invalid SMB3.11 cipher returned\n");
84862306a36Sopenharmony_ci		return -EINVAL;
84962306a36Sopenharmony_ci	}
85062306a36Sopenharmony_ci	server->cipher_type = ctxt->Ciphers[0];
85162306a36Sopenharmony_ci	server->capabilities |= SMB2_GLOBAL_CAP_ENCRYPTION;
85262306a36Sopenharmony_ci	return 0;
85362306a36Sopenharmony_ci}
85462306a36Sopenharmony_ci
85562306a36Sopenharmony_cistatic void decode_signing_ctx(struct TCP_Server_Info *server,
85662306a36Sopenharmony_ci			       struct smb2_signing_capabilities *pctxt)
85762306a36Sopenharmony_ci{
85862306a36Sopenharmony_ci	unsigned int len = le16_to_cpu(pctxt->DataLength);
85962306a36Sopenharmony_ci
86062306a36Sopenharmony_ci	/*
86162306a36Sopenharmony_ci	 * Caller checked that DataLength remains within SMB boundary. We still
86262306a36Sopenharmony_ci	 * need to confirm that one SigningAlgorithms flexible array member is
86362306a36Sopenharmony_ci	 * accounted for.
86462306a36Sopenharmony_ci	 */
86562306a36Sopenharmony_ci	if ((len < 4) || (len > 16)) {
86662306a36Sopenharmony_ci		pr_warn_once("server sent bad signing negcontext\n");
86762306a36Sopenharmony_ci		return;
86862306a36Sopenharmony_ci	}
86962306a36Sopenharmony_ci	if (le16_to_cpu(pctxt->SigningAlgorithmCount) != 1) {
87062306a36Sopenharmony_ci		pr_warn_once("Invalid signing algorithm count\n");
87162306a36Sopenharmony_ci		return;
87262306a36Sopenharmony_ci	}
87362306a36Sopenharmony_ci	if (le16_to_cpu(pctxt->SigningAlgorithms[0]) > 2) {
87462306a36Sopenharmony_ci		pr_warn_once("unknown signing algorithm\n");
87562306a36Sopenharmony_ci		return;
87662306a36Sopenharmony_ci	}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_ci	server->signing_negotiated = true;
87962306a36Sopenharmony_ci	server->signing_algorithm = le16_to_cpu(pctxt->SigningAlgorithms[0]);
88062306a36Sopenharmony_ci	cifs_dbg(FYI, "signing algorithm %d chosen\n",
88162306a36Sopenharmony_ci		     server->signing_algorithm);
88262306a36Sopenharmony_ci}
88362306a36Sopenharmony_ci
88462306a36Sopenharmony_ci
88562306a36Sopenharmony_cistatic int smb311_decode_neg_context(struct smb2_negotiate_rsp *rsp,
88662306a36Sopenharmony_ci				     struct TCP_Server_Info *server,
88762306a36Sopenharmony_ci				     unsigned int len_of_smb)
88862306a36Sopenharmony_ci{
88962306a36Sopenharmony_ci	struct smb2_neg_context *pctx;
89062306a36Sopenharmony_ci	unsigned int offset = le32_to_cpu(rsp->NegotiateContextOffset);
89162306a36Sopenharmony_ci	unsigned int ctxt_cnt = le16_to_cpu(rsp->NegotiateContextCount);
89262306a36Sopenharmony_ci	unsigned int len_of_ctxts, i;
89362306a36Sopenharmony_ci	int rc = 0;
89462306a36Sopenharmony_ci
89562306a36Sopenharmony_ci	cifs_dbg(FYI, "decoding %d negotiate contexts\n", ctxt_cnt);
89662306a36Sopenharmony_ci	if (len_of_smb <= offset) {
89762306a36Sopenharmony_ci		cifs_server_dbg(VFS, "Invalid response: negotiate context offset\n");
89862306a36Sopenharmony_ci		return -EINVAL;
89962306a36Sopenharmony_ci	}
90062306a36Sopenharmony_ci
90162306a36Sopenharmony_ci	len_of_ctxts = len_of_smb - offset;
90262306a36Sopenharmony_ci
90362306a36Sopenharmony_ci	for (i = 0; i < ctxt_cnt; i++) {
90462306a36Sopenharmony_ci		int clen;
90562306a36Sopenharmony_ci		/* check that offset is not beyond end of SMB */
90662306a36Sopenharmony_ci		if (len_of_ctxts < sizeof(struct smb2_neg_context))
90762306a36Sopenharmony_ci			break;
90862306a36Sopenharmony_ci
90962306a36Sopenharmony_ci		pctx = (struct smb2_neg_context *)(offset + (char *)rsp);
91062306a36Sopenharmony_ci		clen = sizeof(struct smb2_neg_context)
91162306a36Sopenharmony_ci			+ le16_to_cpu(pctx->DataLength);
91262306a36Sopenharmony_ci		/*
91362306a36Sopenharmony_ci		 * 2.2.4 SMB2 NEGOTIATE Response
91462306a36Sopenharmony_ci		 * Subsequent negotiate contexts MUST appear at the first 8-byte
91562306a36Sopenharmony_ci		 * aligned offset following the previous negotiate context.
91662306a36Sopenharmony_ci		 */
91762306a36Sopenharmony_ci		if (i + 1 != ctxt_cnt)
91862306a36Sopenharmony_ci			clen = ALIGN(clen, 8);
91962306a36Sopenharmony_ci		if (clen > len_of_ctxts)
92062306a36Sopenharmony_ci			break;
92162306a36Sopenharmony_ci
92262306a36Sopenharmony_ci		if (pctx->ContextType == SMB2_PREAUTH_INTEGRITY_CAPABILITIES)
92362306a36Sopenharmony_ci			decode_preauth_context(
92462306a36Sopenharmony_ci				(struct smb2_preauth_neg_context *)pctx);
92562306a36Sopenharmony_ci		else if (pctx->ContextType == SMB2_ENCRYPTION_CAPABILITIES)
92662306a36Sopenharmony_ci			rc = decode_encrypt_ctx(server,
92762306a36Sopenharmony_ci				(struct smb2_encryption_neg_context *)pctx);
92862306a36Sopenharmony_ci		else if (pctx->ContextType == SMB2_COMPRESSION_CAPABILITIES)
92962306a36Sopenharmony_ci			decode_compress_ctx(server,
93062306a36Sopenharmony_ci				(struct smb2_compression_capabilities_context *)pctx);
93162306a36Sopenharmony_ci		else if (pctx->ContextType == SMB2_POSIX_EXTENSIONS_AVAILABLE)
93262306a36Sopenharmony_ci			server->posix_ext_supported = true;
93362306a36Sopenharmony_ci		else if (pctx->ContextType == SMB2_SIGNING_CAPABILITIES)
93462306a36Sopenharmony_ci			decode_signing_ctx(server,
93562306a36Sopenharmony_ci				(struct smb2_signing_capabilities *)pctx);
93662306a36Sopenharmony_ci		else
93762306a36Sopenharmony_ci			cifs_server_dbg(VFS, "unknown negcontext of type %d ignored\n",
93862306a36Sopenharmony_ci				le16_to_cpu(pctx->ContextType));
93962306a36Sopenharmony_ci		if (rc)
94062306a36Sopenharmony_ci			break;
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci		offset += clen;
94362306a36Sopenharmony_ci		len_of_ctxts -= clen;
94462306a36Sopenharmony_ci	}
94562306a36Sopenharmony_ci	return rc;
94662306a36Sopenharmony_ci}
94762306a36Sopenharmony_ci
94862306a36Sopenharmony_cistatic struct create_posix *
94962306a36Sopenharmony_cicreate_posix_buf(umode_t mode)
95062306a36Sopenharmony_ci{
95162306a36Sopenharmony_ci	struct create_posix *buf;
95262306a36Sopenharmony_ci
95362306a36Sopenharmony_ci	buf = kzalloc(sizeof(struct create_posix),
95462306a36Sopenharmony_ci			GFP_KERNEL);
95562306a36Sopenharmony_ci	if (!buf)
95662306a36Sopenharmony_ci		return NULL;
95762306a36Sopenharmony_ci
95862306a36Sopenharmony_ci	buf->ccontext.DataOffset =
95962306a36Sopenharmony_ci		cpu_to_le16(offsetof(struct create_posix, Mode));
96062306a36Sopenharmony_ci	buf->ccontext.DataLength = cpu_to_le32(4);
96162306a36Sopenharmony_ci	buf->ccontext.NameOffset =
96262306a36Sopenharmony_ci		cpu_to_le16(offsetof(struct create_posix, Name));
96362306a36Sopenharmony_ci	buf->ccontext.NameLength = cpu_to_le16(16);
96462306a36Sopenharmony_ci
96562306a36Sopenharmony_ci	/* SMB2_CREATE_TAG_POSIX is "0x93AD25509CB411E7B42383DE968BCD7C" */
96662306a36Sopenharmony_ci	buf->Name[0] = 0x93;
96762306a36Sopenharmony_ci	buf->Name[1] = 0xAD;
96862306a36Sopenharmony_ci	buf->Name[2] = 0x25;
96962306a36Sopenharmony_ci	buf->Name[3] = 0x50;
97062306a36Sopenharmony_ci	buf->Name[4] = 0x9C;
97162306a36Sopenharmony_ci	buf->Name[5] = 0xB4;
97262306a36Sopenharmony_ci	buf->Name[6] = 0x11;
97362306a36Sopenharmony_ci	buf->Name[7] = 0xE7;
97462306a36Sopenharmony_ci	buf->Name[8] = 0xB4;
97562306a36Sopenharmony_ci	buf->Name[9] = 0x23;
97662306a36Sopenharmony_ci	buf->Name[10] = 0x83;
97762306a36Sopenharmony_ci	buf->Name[11] = 0xDE;
97862306a36Sopenharmony_ci	buf->Name[12] = 0x96;
97962306a36Sopenharmony_ci	buf->Name[13] = 0x8B;
98062306a36Sopenharmony_ci	buf->Name[14] = 0xCD;
98162306a36Sopenharmony_ci	buf->Name[15] = 0x7C;
98262306a36Sopenharmony_ci	buf->Mode = cpu_to_le32(mode);
98362306a36Sopenharmony_ci	cifs_dbg(FYI, "mode on posix create 0%o\n", mode);
98462306a36Sopenharmony_ci	return buf;
98562306a36Sopenharmony_ci}
98662306a36Sopenharmony_ci
98762306a36Sopenharmony_cistatic int
98862306a36Sopenharmony_ciadd_posix_context(struct kvec *iov, unsigned int *num_iovec, umode_t mode)
98962306a36Sopenharmony_ci{
99062306a36Sopenharmony_ci	unsigned int num = *num_iovec;
99162306a36Sopenharmony_ci
99262306a36Sopenharmony_ci	iov[num].iov_base = create_posix_buf(mode);
99362306a36Sopenharmony_ci	if (mode == ACL_NO_MODE)
99462306a36Sopenharmony_ci		cifs_dbg(FYI, "%s: no mode\n", __func__);
99562306a36Sopenharmony_ci	if (iov[num].iov_base == NULL)
99662306a36Sopenharmony_ci		return -ENOMEM;
99762306a36Sopenharmony_ci	iov[num].iov_len = sizeof(struct create_posix);
99862306a36Sopenharmony_ci	*num_iovec = num + 1;
99962306a36Sopenharmony_ci	return 0;
100062306a36Sopenharmony_ci}
100162306a36Sopenharmony_ci
100262306a36Sopenharmony_ci
100362306a36Sopenharmony_ci/*
100462306a36Sopenharmony_ci *
100562306a36Sopenharmony_ci *	SMB2 Worker functions follow:
100662306a36Sopenharmony_ci *
100762306a36Sopenharmony_ci *	The general structure of the worker functions is:
100862306a36Sopenharmony_ci *	1) Call smb2_init (assembles SMB2 header)
100962306a36Sopenharmony_ci *	2) Initialize SMB2 command specific fields in fixed length area of SMB
101062306a36Sopenharmony_ci *	3) Call smb_sendrcv2 (sends request on socket and waits for response)
101162306a36Sopenharmony_ci *	4) Decode SMB2 command specific fields in the fixed length area
101262306a36Sopenharmony_ci *	5) Decode variable length data area (if any for this SMB2 command type)
101362306a36Sopenharmony_ci *	6) Call free smb buffer
101462306a36Sopenharmony_ci *	7) return
101562306a36Sopenharmony_ci *
101662306a36Sopenharmony_ci */
101762306a36Sopenharmony_ci
101862306a36Sopenharmony_ciint
101962306a36Sopenharmony_ciSMB2_negotiate(const unsigned int xid,
102062306a36Sopenharmony_ci	       struct cifs_ses *ses,
102162306a36Sopenharmony_ci	       struct TCP_Server_Info *server)
102262306a36Sopenharmony_ci{
102362306a36Sopenharmony_ci	struct smb_rqst rqst;
102462306a36Sopenharmony_ci	struct smb2_negotiate_req *req;
102562306a36Sopenharmony_ci	struct smb2_negotiate_rsp *rsp;
102662306a36Sopenharmony_ci	struct kvec iov[1];
102762306a36Sopenharmony_ci	struct kvec rsp_iov;
102862306a36Sopenharmony_ci	int rc;
102962306a36Sopenharmony_ci	int resp_buftype;
103062306a36Sopenharmony_ci	int blob_offset, blob_length;
103162306a36Sopenharmony_ci	char *security_blob;
103262306a36Sopenharmony_ci	int flags = CIFS_NEG_OP;
103362306a36Sopenharmony_ci	unsigned int total_len;
103462306a36Sopenharmony_ci
103562306a36Sopenharmony_ci	cifs_dbg(FYI, "Negotiate protocol\n");
103662306a36Sopenharmony_ci
103762306a36Sopenharmony_ci	if (!server) {
103862306a36Sopenharmony_ci		WARN(1, "%s: server is NULL!\n", __func__);
103962306a36Sopenharmony_ci		return -EIO;
104062306a36Sopenharmony_ci	}
104162306a36Sopenharmony_ci
104262306a36Sopenharmony_ci	rc = smb2_plain_req_init(SMB2_NEGOTIATE, NULL, server,
104362306a36Sopenharmony_ci				 (void **) &req, &total_len);
104462306a36Sopenharmony_ci	if (rc)
104562306a36Sopenharmony_ci		return rc;
104662306a36Sopenharmony_ci
104762306a36Sopenharmony_ci	req->hdr.SessionId = 0;
104862306a36Sopenharmony_ci
104962306a36Sopenharmony_ci	memset(server->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE);
105062306a36Sopenharmony_ci	memset(ses->preauth_sha_hash, 0, SMB2_PREAUTH_HASH_SIZE);
105162306a36Sopenharmony_ci
105262306a36Sopenharmony_ci	if (strcmp(server->vals->version_string,
105362306a36Sopenharmony_ci		   SMB3ANY_VERSION_STRING) == 0) {
105462306a36Sopenharmony_ci		req->Dialects[0] = cpu_to_le16(SMB30_PROT_ID);
105562306a36Sopenharmony_ci		req->Dialects[1] = cpu_to_le16(SMB302_PROT_ID);
105662306a36Sopenharmony_ci		req->Dialects[2] = cpu_to_le16(SMB311_PROT_ID);
105762306a36Sopenharmony_ci		req->DialectCount = cpu_to_le16(3);
105862306a36Sopenharmony_ci		total_len += 6;
105962306a36Sopenharmony_ci	} else if (strcmp(server->vals->version_string,
106062306a36Sopenharmony_ci		   SMBDEFAULT_VERSION_STRING) == 0) {
106162306a36Sopenharmony_ci		req->Dialects[0] = cpu_to_le16(SMB21_PROT_ID);
106262306a36Sopenharmony_ci		req->Dialects[1] = cpu_to_le16(SMB30_PROT_ID);
106362306a36Sopenharmony_ci		req->Dialects[2] = cpu_to_le16(SMB302_PROT_ID);
106462306a36Sopenharmony_ci		req->Dialects[3] = cpu_to_le16(SMB311_PROT_ID);
106562306a36Sopenharmony_ci		req->DialectCount = cpu_to_le16(4);
106662306a36Sopenharmony_ci		total_len += 8;
106762306a36Sopenharmony_ci	} else {
106862306a36Sopenharmony_ci		/* otherwise send specific dialect */
106962306a36Sopenharmony_ci		req->Dialects[0] = cpu_to_le16(server->vals->protocol_id);
107062306a36Sopenharmony_ci		req->DialectCount = cpu_to_le16(1);
107162306a36Sopenharmony_ci		total_len += 2;
107262306a36Sopenharmony_ci	}
107362306a36Sopenharmony_ci
107462306a36Sopenharmony_ci	/* only one of SMB2 signing flags may be set in SMB2 request */
107562306a36Sopenharmony_ci	if (ses->sign)
107662306a36Sopenharmony_ci		req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED);
107762306a36Sopenharmony_ci	else if (global_secflags & CIFSSEC_MAY_SIGN)
107862306a36Sopenharmony_ci		req->SecurityMode = cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED);
107962306a36Sopenharmony_ci	else
108062306a36Sopenharmony_ci		req->SecurityMode = 0;
108162306a36Sopenharmony_ci
108262306a36Sopenharmony_ci	req->Capabilities = cpu_to_le32(server->vals->req_capabilities);
108362306a36Sopenharmony_ci	if (ses->chan_max > 1)
108462306a36Sopenharmony_ci		req->Capabilities |= cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL);
108562306a36Sopenharmony_ci
108662306a36Sopenharmony_ci	/* ClientGUID must be zero for SMB2.02 dialect */
108762306a36Sopenharmony_ci	if (server->vals->protocol_id == SMB20_PROT_ID)
108862306a36Sopenharmony_ci		memset(req->ClientGUID, 0, SMB2_CLIENT_GUID_SIZE);
108962306a36Sopenharmony_ci	else {
109062306a36Sopenharmony_ci		memcpy(req->ClientGUID, server->client_guid,
109162306a36Sopenharmony_ci			SMB2_CLIENT_GUID_SIZE);
109262306a36Sopenharmony_ci		if ((server->vals->protocol_id == SMB311_PROT_ID) ||
109362306a36Sopenharmony_ci		    (strcmp(server->vals->version_string,
109462306a36Sopenharmony_ci		     SMB3ANY_VERSION_STRING) == 0) ||
109562306a36Sopenharmony_ci		    (strcmp(server->vals->version_string,
109662306a36Sopenharmony_ci		     SMBDEFAULT_VERSION_STRING) == 0))
109762306a36Sopenharmony_ci			assemble_neg_contexts(req, server, &total_len);
109862306a36Sopenharmony_ci	}
109962306a36Sopenharmony_ci	iov[0].iov_base = (char *)req;
110062306a36Sopenharmony_ci	iov[0].iov_len = total_len;
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	memset(&rqst, 0, sizeof(struct smb_rqst));
110362306a36Sopenharmony_ci	rqst.rq_iov = iov;
110462306a36Sopenharmony_ci	rqst.rq_nvec = 1;
110562306a36Sopenharmony_ci
110662306a36Sopenharmony_ci	rc = cifs_send_recv(xid, ses, server,
110762306a36Sopenharmony_ci			    &rqst, &resp_buftype, flags, &rsp_iov);
110862306a36Sopenharmony_ci	cifs_small_buf_release(req);
110962306a36Sopenharmony_ci	rsp = (struct smb2_negotiate_rsp *)rsp_iov.iov_base;
111062306a36Sopenharmony_ci	/*
111162306a36Sopenharmony_ci	 * No tcon so can't do
111262306a36Sopenharmony_ci	 * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]);
111362306a36Sopenharmony_ci	 */
111462306a36Sopenharmony_ci	if (rc == -EOPNOTSUPP) {
111562306a36Sopenharmony_ci		cifs_server_dbg(VFS, "Dialect not supported by server. Consider  specifying vers=1.0 or vers=2.0 on mount for accessing older servers\n");
111662306a36Sopenharmony_ci		goto neg_exit;
111762306a36Sopenharmony_ci	} else if (rc != 0)
111862306a36Sopenharmony_ci		goto neg_exit;
111962306a36Sopenharmony_ci
112062306a36Sopenharmony_ci	rc = -EIO;
112162306a36Sopenharmony_ci	if (strcmp(server->vals->version_string,
112262306a36Sopenharmony_ci		   SMB3ANY_VERSION_STRING) == 0) {
112362306a36Sopenharmony_ci		if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) {
112462306a36Sopenharmony_ci			cifs_server_dbg(VFS,
112562306a36Sopenharmony_ci				"SMB2 dialect returned but not requested\n");
112662306a36Sopenharmony_ci			goto neg_exit;
112762306a36Sopenharmony_ci		} else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) {
112862306a36Sopenharmony_ci			cifs_server_dbg(VFS,
112962306a36Sopenharmony_ci				"SMB2.1 dialect returned but not requested\n");
113062306a36Sopenharmony_ci			goto neg_exit;
113162306a36Sopenharmony_ci		} else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) {
113262306a36Sopenharmony_ci			/* ops set to 3.0 by default for default so update */
113362306a36Sopenharmony_ci			server->ops = &smb311_operations;
113462306a36Sopenharmony_ci			server->vals = &smb311_values;
113562306a36Sopenharmony_ci		}
113662306a36Sopenharmony_ci	} else if (strcmp(server->vals->version_string,
113762306a36Sopenharmony_ci		   SMBDEFAULT_VERSION_STRING) == 0) {
113862306a36Sopenharmony_ci		if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID)) {
113962306a36Sopenharmony_ci			cifs_server_dbg(VFS,
114062306a36Sopenharmony_ci				"SMB2 dialect returned but not requested\n");
114162306a36Sopenharmony_ci			goto neg_exit;
114262306a36Sopenharmony_ci		} else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID)) {
114362306a36Sopenharmony_ci			/* ops set to 3.0 by default for default so update */
114462306a36Sopenharmony_ci			server->ops = &smb21_operations;
114562306a36Sopenharmony_ci			server->vals = &smb21_values;
114662306a36Sopenharmony_ci		} else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) {
114762306a36Sopenharmony_ci			server->ops = &smb311_operations;
114862306a36Sopenharmony_ci			server->vals = &smb311_values;
114962306a36Sopenharmony_ci		}
115062306a36Sopenharmony_ci	} else if (le16_to_cpu(rsp->DialectRevision) !=
115162306a36Sopenharmony_ci				server->vals->protocol_id) {
115262306a36Sopenharmony_ci		/* if requested single dialect ensure returned dialect matched */
115362306a36Sopenharmony_ci		cifs_server_dbg(VFS, "Invalid 0x%x dialect returned: not requested\n",
115462306a36Sopenharmony_ci				le16_to_cpu(rsp->DialectRevision));
115562306a36Sopenharmony_ci		goto neg_exit;
115662306a36Sopenharmony_ci	}
115762306a36Sopenharmony_ci
115862306a36Sopenharmony_ci	cifs_dbg(FYI, "mode 0x%x\n", rsp->SecurityMode);
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	if (rsp->DialectRevision == cpu_to_le16(SMB20_PROT_ID))
116162306a36Sopenharmony_ci		cifs_dbg(FYI, "negotiated smb2.0 dialect\n");
116262306a36Sopenharmony_ci	else if (rsp->DialectRevision == cpu_to_le16(SMB21_PROT_ID))
116362306a36Sopenharmony_ci		cifs_dbg(FYI, "negotiated smb2.1 dialect\n");
116462306a36Sopenharmony_ci	else if (rsp->DialectRevision == cpu_to_le16(SMB30_PROT_ID))
116562306a36Sopenharmony_ci		cifs_dbg(FYI, "negotiated smb3.0 dialect\n");
116662306a36Sopenharmony_ci	else if (rsp->DialectRevision == cpu_to_le16(SMB302_PROT_ID))
116762306a36Sopenharmony_ci		cifs_dbg(FYI, "negotiated smb3.02 dialect\n");
116862306a36Sopenharmony_ci	else if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID))
116962306a36Sopenharmony_ci		cifs_dbg(FYI, "negotiated smb3.1.1 dialect\n");
117062306a36Sopenharmony_ci	else {
117162306a36Sopenharmony_ci		cifs_server_dbg(VFS, "Invalid dialect returned by server 0x%x\n",
117262306a36Sopenharmony_ci				le16_to_cpu(rsp->DialectRevision));
117362306a36Sopenharmony_ci		goto neg_exit;
117462306a36Sopenharmony_ci	}
117562306a36Sopenharmony_ci
117662306a36Sopenharmony_ci	rc = 0;
117762306a36Sopenharmony_ci	server->dialect = le16_to_cpu(rsp->DialectRevision);
117862306a36Sopenharmony_ci
117962306a36Sopenharmony_ci	/*
118062306a36Sopenharmony_ci	 * Keep a copy of the hash after negprot. This hash will be
118162306a36Sopenharmony_ci	 * the starting hash value for all sessions made from this
118262306a36Sopenharmony_ci	 * server.
118362306a36Sopenharmony_ci	 */
118462306a36Sopenharmony_ci	memcpy(server->preauth_sha_hash, ses->preauth_sha_hash,
118562306a36Sopenharmony_ci	       SMB2_PREAUTH_HASH_SIZE);
118662306a36Sopenharmony_ci
118762306a36Sopenharmony_ci	/* SMB2 only has an extended negflavor */
118862306a36Sopenharmony_ci	server->negflavor = CIFS_NEGFLAVOR_EXTENDED;
118962306a36Sopenharmony_ci	/* set it to the maximum buffer size value we can send with 1 credit */
119062306a36Sopenharmony_ci	server->maxBuf = min_t(unsigned int, le32_to_cpu(rsp->MaxTransactSize),
119162306a36Sopenharmony_ci			       SMB2_MAX_BUFFER_SIZE);
119262306a36Sopenharmony_ci	server->max_read = le32_to_cpu(rsp->MaxReadSize);
119362306a36Sopenharmony_ci	server->max_write = le32_to_cpu(rsp->MaxWriteSize);
119462306a36Sopenharmony_ci	server->sec_mode = le16_to_cpu(rsp->SecurityMode);
119562306a36Sopenharmony_ci	if ((server->sec_mode & SMB2_SEC_MODE_FLAGS_ALL) != server->sec_mode)
119662306a36Sopenharmony_ci		cifs_dbg(FYI, "Server returned unexpected security mode 0x%x\n",
119762306a36Sopenharmony_ci				server->sec_mode);
119862306a36Sopenharmony_ci	server->capabilities = le32_to_cpu(rsp->Capabilities);
119962306a36Sopenharmony_ci	/* Internal types */
120062306a36Sopenharmony_ci	server->capabilities |= SMB2_NT_FIND | SMB2_LARGE_FILES;
120162306a36Sopenharmony_ci
120262306a36Sopenharmony_ci	/*
120362306a36Sopenharmony_ci	 * SMB3.0 supports only 1 cipher and doesn't have a encryption neg context
120462306a36Sopenharmony_ci	 * Set the cipher type manually.
120562306a36Sopenharmony_ci	 */
120662306a36Sopenharmony_ci	if (server->dialect == SMB30_PROT_ID && (server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION))
120762306a36Sopenharmony_ci		server->cipher_type = SMB2_ENCRYPTION_AES128_CCM;
120862306a36Sopenharmony_ci
120962306a36Sopenharmony_ci	security_blob = smb2_get_data_area_len(&blob_offset, &blob_length,
121062306a36Sopenharmony_ci					       (struct smb2_hdr *)rsp);
121162306a36Sopenharmony_ci	/*
121262306a36Sopenharmony_ci	 * See MS-SMB2 section 2.2.4: if no blob, client picks default which
121362306a36Sopenharmony_ci	 * for us will be
121462306a36Sopenharmony_ci	 *	ses->sectype = RawNTLMSSP;
121562306a36Sopenharmony_ci	 * but for time being this is our only auth choice so doesn't matter.
121662306a36Sopenharmony_ci	 * We just found a server which sets blob length to zero expecting raw.
121762306a36Sopenharmony_ci	 */
121862306a36Sopenharmony_ci	if (blob_length == 0) {
121962306a36Sopenharmony_ci		cifs_dbg(FYI, "missing security blob on negprot\n");
122062306a36Sopenharmony_ci		server->sec_ntlmssp = true;
122162306a36Sopenharmony_ci	}
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci	rc = cifs_enable_signing(server, ses->sign);
122462306a36Sopenharmony_ci	if (rc)
122562306a36Sopenharmony_ci		goto neg_exit;
122662306a36Sopenharmony_ci	if (blob_length) {
122762306a36Sopenharmony_ci		rc = decode_negTokenInit(security_blob, blob_length, server);
122862306a36Sopenharmony_ci		if (rc == 1)
122962306a36Sopenharmony_ci			rc = 0;
123062306a36Sopenharmony_ci		else if (rc == 0)
123162306a36Sopenharmony_ci			rc = -EIO;
123262306a36Sopenharmony_ci	}
123362306a36Sopenharmony_ci
123462306a36Sopenharmony_ci	if (rsp->DialectRevision == cpu_to_le16(SMB311_PROT_ID)) {
123562306a36Sopenharmony_ci		if (rsp->NegotiateContextCount)
123662306a36Sopenharmony_ci			rc = smb311_decode_neg_context(rsp, server,
123762306a36Sopenharmony_ci						       rsp_iov.iov_len);
123862306a36Sopenharmony_ci		else
123962306a36Sopenharmony_ci			cifs_server_dbg(VFS, "Missing expected negotiate contexts\n");
124062306a36Sopenharmony_ci	}
124162306a36Sopenharmony_cineg_exit:
124262306a36Sopenharmony_ci	free_rsp_buf(resp_buftype, rsp);
124362306a36Sopenharmony_ci	return rc;
124462306a36Sopenharmony_ci}
124562306a36Sopenharmony_ci
124662306a36Sopenharmony_ciint smb3_validate_negotiate(const unsigned int xid, struct cifs_tcon *tcon)
124762306a36Sopenharmony_ci{
124862306a36Sopenharmony_ci	int rc;
124962306a36Sopenharmony_ci	struct validate_negotiate_info_req *pneg_inbuf;
125062306a36Sopenharmony_ci	struct validate_negotiate_info_rsp *pneg_rsp = NULL;
125162306a36Sopenharmony_ci	u32 rsplen;
125262306a36Sopenharmony_ci	u32 inbuflen; /* max of 4 dialects */
125362306a36Sopenharmony_ci	struct TCP_Server_Info *server = tcon->ses->server;
125462306a36Sopenharmony_ci
125562306a36Sopenharmony_ci	cifs_dbg(FYI, "validate negotiate\n");
125662306a36Sopenharmony_ci
125762306a36Sopenharmony_ci	/* In SMB3.11 preauth integrity supersedes validate negotiate */
125862306a36Sopenharmony_ci	if (server->dialect == SMB311_PROT_ID)
125962306a36Sopenharmony_ci		return 0;
126062306a36Sopenharmony_ci
126162306a36Sopenharmony_ci	/*
126262306a36Sopenharmony_ci	 * validation ioctl must be signed, so no point sending this if we
126362306a36Sopenharmony_ci	 * can not sign it (ie are not known user).  Even if signing is not
126462306a36Sopenharmony_ci	 * required (enabled but not negotiated), in those cases we selectively
126562306a36Sopenharmony_ci	 * sign just this, the first and only signed request on a connection.
126662306a36Sopenharmony_ci	 * Having validation of negotiate info  helps reduce attack vectors.
126762306a36Sopenharmony_ci	 */
126862306a36Sopenharmony_ci	if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST)
126962306a36Sopenharmony_ci		return 0; /* validation requires signing */
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci	if (tcon->ses->user_name == NULL) {
127262306a36Sopenharmony_ci		cifs_dbg(FYI, "Can't validate negotiate: null user mount\n");
127362306a36Sopenharmony_ci		return 0; /* validation requires signing */
127462306a36Sopenharmony_ci	}
127562306a36Sopenharmony_ci
127662306a36Sopenharmony_ci	if (tcon->ses->session_flags & SMB2_SESSION_FLAG_IS_NULL)
127762306a36Sopenharmony_ci		cifs_tcon_dbg(VFS, "Unexpected null user (anonymous) auth flag sent by server\n");
127862306a36Sopenharmony_ci
127962306a36Sopenharmony_ci	pneg_inbuf = kmalloc(sizeof(*pneg_inbuf), GFP_NOFS);
128062306a36Sopenharmony_ci	if (!pneg_inbuf)
128162306a36Sopenharmony_ci		return -ENOMEM;
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci	pneg_inbuf->Capabilities =
128462306a36Sopenharmony_ci			cpu_to_le32(server->vals->req_capabilities);
128562306a36Sopenharmony_ci	if (tcon->ses->chan_max > 1)
128662306a36Sopenharmony_ci		pneg_inbuf->Capabilities |= cpu_to_le32(SMB2_GLOBAL_CAP_MULTI_CHANNEL);
128762306a36Sopenharmony_ci
128862306a36Sopenharmony_ci	memcpy(pneg_inbuf->Guid, server->client_guid,
128962306a36Sopenharmony_ci					SMB2_CLIENT_GUID_SIZE);
129062306a36Sopenharmony_ci
129162306a36Sopenharmony_ci	if (tcon->ses->sign)
129262306a36Sopenharmony_ci		pneg_inbuf->SecurityMode =
129362306a36Sopenharmony_ci			cpu_to_le16(SMB2_NEGOTIATE_SIGNING_REQUIRED);
129462306a36Sopenharmony_ci	else if (global_secflags & CIFSSEC_MAY_SIGN)
129562306a36Sopenharmony_ci		pneg_inbuf->SecurityMode =
129662306a36Sopenharmony_ci			cpu_to_le16(SMB2_NEGOTIATE_SIGNING_ENABLED);
129762306a36Sopenharmony_ci	else
129862306a36Sopenharmony_ci		pneg_inbuf->SecurityMode = 0;
129962306a36Sopenharmony_ci
130062306a36Sopenharmony_ci
130162306a36Sopenharmony_ci	if (strcmp(server->vals->version_string,
130262306a36Sopenharmony_ci		SMB3ANY_VERSION_STRING) == 0) {
130362306a36Sopenharmony_ci		pneg_inbuf->Dialects[0] = cpu_to_le16(SMB30_PROT_ID);
130462306a36Sopenharmony_ci		pneg_inbuf->Dialects[1] = cpu_to_le16(SMB302_PROT_ID);
130562306a36Sopenharmony_ci		pneg_inbuf->Dialects[2] = cpu_to_le16(SMB311_PROT_ID);
130662306a36Sopenharmony_ci		pneg_inbuf->DialectCount = cpu_to_le16(3);
130762306a36Sopenharmony_ci		/* SMB 2.1 not included so subtract one dialect from len */
130862306a36Sopenharmony_ci		inbuflen = sizeof(*pneg_inbuf) -
130962306a36Sopenharmony_ci				(sizeof(pneg_inbuf->Dialects[0]));
131062306a36Sopenharmony_ci	} else if (strcmp(server->vals->version_string,
131162306a36Sopenharmony_ci		SMBDEFAULT_VERSION_STRING) == 0) {
131262306a36Sopenharmony_ci		pneg_inbuf->Dialects[0] = cpu_to_le16(SMB21_PROT_ID);
131362306a36Sopenharmony_ci		pneg_inbuf->Dialects[1] = cpu_to_le16(SMB30_PROT_ID);
131462306a36Sopenharmony_ci		pneg_inbuf->Dialects[2] = cpu_to_le16(SMB302_PROT_ID);
131562306a36Sopenharmony_ci		pneg_inbuf->Dialects[3] = cpu_to_le16(SMB311_PROT_ID);
131662306a36Sopenharmony_ci		pneg_inbuf->DialectCount = cpu_to_le16(4);
131762306a36Sopenharmony_ci		/* structure is big enough for 4 dialects */
131862306a36Sopenharmony_ci		inbuflen = sizeof(*pneg_inbuf);
131962306a36Sopenharmony_ci	} else {
132062306a36Sopenharmony_ci		/* otherwise specific dialect was requested */
132162306a36Sopenharmony_ci		pneg_inbuf->Dialects[0] =
132262306a36Sopenharmony_ci			cpu_to_le16(server->vals->protocol_id);
132362306a36Sopenharmony_ci		pneg_inbuf->DialectCount = cpu_to_le16(1);
132462306a36Sopenharmony_ci		/* structure is big enough for 4 dialects, sending only 1 */
132562306a36Sopenharmony_ci		inbuflen = sizeof(*pneg_inbuf) -
132662306a36Sopenharmony_ci				sizeof(pneg_inbuf->Dialects[0]) * 3;
132762306a36Sopenharmony_ci	}
132862306a36Sopenharmony_ci
132962306a36Sopenharmony_ci	rc = SMB2_ioctl(xid, tcon, NO_FILE_ID, NO_FILE_ID,
133062306a36Sopenharmony_ci		FSCTL_VALIDATE_NEGOTIATE_INFO,
133162306a36Sopenharmony_ci		(char *)pneg_inbuf, inbuflen, CIFSMaxBufSize,
133262306a36Sopenharmony_ci		(char **)&pneg_rsp, &rsplen);
133362306a36Sopenharmony_ci	if (rc == -EOPNOTSUPP) {
133462306a36Sopenharmony_ci		/*
133562306a36Sopenharmony_ci		 * Old Windows versions or Netapp SMB server can return
133662306a36Sopenharmony_ci		 * not supported error. Client should accept it.
133762306a36Sopenharmony_ci		 */
133862306a36Sopenharmony_ci		cifs_tcon_dbg(VFS, "Server does not support validate negotiate\n");
133962306a36Sopenharmony_ci		rc = 0;
134062306a36Sopenharmony_ci		goto out_free_inbuf;
134162306a36Sopenharmony_ci	} else if (rc != 0) {
134262306a36Sopenharmony_ci		cifs_tcon_dbg(VFS, "validate protocol negotiate failed: %d\n",
134362306a36Sopenharmony_ci			      rc);
134462306a36Sopenharmony_ci		rc = -EIO;
134562306a36Sopenharmony_ci		goto out_free_inbuf;
134662306a36Sopenharmony_ci	}
134762306a36Sopenharmony_ci
134862306a36Sopenharmony_ci	rc = -EIO;
134962306a36Sopenharmony_ci	if (rsplen != sizeof(*pneg_rsp)) {
135062306a36Sopenharmony_ci		cifs_tcon_dbg(VFS, "Invalid protocol negotiate response size: %d\n",
135162306a36Sopenharmony_ci			      rsplen);
135262306a36Sopenharmony_ci
135362306a36Sopenharmony_ci		/* relax check since Mac returns max bufsize allowed on ioctl */
135462306a36Sopenharmony_ci		if (rsplen > CIFSMaxBufSize || rsplen < sizeof(*pneg_rsp))
135562306a36Sopenharmony_ci			goto out_free_rsp;
135662306a36Sopenharmony_ci	}
135762306a36Sopenharmony_ci
135862306a36Sopenharmony_ci	/* check validate negotiate info response matches what we got earlier */
135962306a36Sopenharmony_ci	if (pneg_rsp->Dialect != cpu_to_le16(server->dialect))
136062306a36Sopenharmony_ci		goto vneg_out;
136162306a36Sopenharmony_ci
136262306a36Sopenharmony_ci	if (pneg_rsp->SecurityMode != cpu_to_le16(server->sec_mode))
136362306a36Sopenharmony_ci		goto vneg_out;
136462306a36Sopenharmony_ci
136562306a36Sopenharmony_ci	/* do not validate server guid because not saved at negprot time yet */
136662306a36Sopenharmony_ci
136762306a36Sopenharmony_ci	if ((le32_to_cpu(pneg_rsp->Capabilities) | SMB2_NT_FIND |
136862306a36Sopenharmony_ci	      SMB2_LARGE_FILES) != server->capabilities)
136962306a36Sopenharmony_ci		goto vneg_out;
137062306a36Sopenharmony_ci
137162306a36Sopenharmony_ci	/* validate negotiate successful */
137262306a36Sopenharmony_ci	rc = 0;
137362306a36Sopenharmony_ci	cifs_dbg(FYI, "validate negotiate info successful\n");
137462306a36Sopenharmony_ci	goto out_free_rsp;
137562306a36Sopenharmony_ci
137662306a36Sopenharmony_civneg_out:
137762306a36Sopenharmony_ci	cifs_tcon_dbg(VFS, "protocol revalidation - security settings mismatch\n");
137862306a36Sopenharmony_ciout_free_rsp:
137962306a36Sopenharmony_ci	kfree(pneg_rsp);
138062306a36Sopenharmony_ciout_free_inbuf:
138162306a36Sopenharmony_ci	kfree(pneg_inbuf);
138262306a36Sopenharmony_ci	return rc;
138362306a36Sopenharmony_ci}
138462306a36Sopenharmony_ci
138562306a36Sopenharmony_cienum securityEnum
138662306a36Sopenharmony_cismb2_select_sectype(struct TCP_Server_Info *server, enum securityEnum requested)
138762306a36Sopenharmony_ci{
138862306a36Sopenharmony_ci	switch (requested) {
138962306a36Sopenharmony_ci	case Kerberos:
139062306a36Sopenharmony_ci	case RawNTLMSSP:
139162306a36Sopenharmony_ci		return requested;
139262306a36Sopenharmony_ci	case NTLMv2:
139362306a36Sopenharmony_ci		return RawNTLMSSP;
139462306a36Sopenharmony_ci	case Unspecified:
139562306a36Sopenharmony_ci		if (server->sec_ntlmssp &&
139662306a36Sopenharmony_ci			(global_secflags & CIFSSEC_MAY_NTLMSSP))
139762306a36Sopenharmony_ci			return RawNTLMSSP;
139862306a36Sopenharmony_ci		if ((server->sec_kerberos || server->sec_mskerberos) &&
139962306a36Sopenharmony_ci			(global_secflags & CIFSSEC_MAY_KRB5))
140062306a36Sopenharmony_ci			return Kerberos;
140162306a36Sopenharmony_ci		fallthrough;
140262306a36Sopenharmony_ci	default:
140362306a36Sopenharmony_ci		return Unspecified;
140462306a36Sopenharmony_ci	}
140562306a36Sopenharmony_ci}
140662306a36Sopenharmony_ci
140762306a36Sopenharmony_cistruct SMB2_sess_data {
140862306a36Sopenharmony_ci	unsigned int xid;
140962306a36Sopenharmony_ci	struct cifs_ses *ses;
141062306a36Sopenharmony_ci	struct TCP_Server_Info *server;
141162306a36Sopenharmony_ci	struct nls_table *nls_cp;
141262306a36Sopenharmony_ci	void (*func)(struct SMB2_sess_data *);
141362306a36Sopenharmony_ci	int result;
141462306a36Sopenharmony_ci	u64 previous_session;
141562306a36Sopenharmony_ci
141662306a36Sopenharmony_ci	/* we will send the SMB in three pieces:
141762306a36Sopenharmony_ci	 * a fixed length beginning part, an optional
141862306a36Sopenharmony_ci	 * SPNEGO blob (which can be zero length), and a
141962306a36Sopenharmony_ci	 * last part which will include the strings
142062306a36Sopenharmony_ci	 * and rest of bcc area. This allows us to avoid
142162306a36Sopenharmony_ci	 * a large buffer 17K allocation
142262306a36Sopenharmony_ci	 */
142362306a36Sopenharmony_ci	int buf0_type;
142462306a36Sopenharmony_ci	struct kvec iov[2];
142562306a36Sopenharmony_ci};
142662306a36Sopenharmony_ci
142762306a36Sopenharmony_cistatic int
142862306a36Sopenharmony_ciSMB2_sess_alloc_buffer(struct SMB2_sess_data *sess_data)
142962306a36Sopenharmony_ci{
143062306a36Sopenharmony_ci	int rc;
143162306a36Sopenharmony_ci	struct cifs_ses *ses = sess_data->ses;
143262306a36Sopenharmony_ci	struct TCP_Server_Info *server = sess_data->server;
143362306a36Sopenharmony_ci	struct smb2_sess_setup_req *req;
143462306a36Sopenharmony_ci	unsigned int total_len;
143562306a36Sopenharmony_ci	bool is_binding = false;
143662306a36Sopenharmony_ci
143762306a36Sopenharmony_ci	rc = smb2_plain_req_init(SMB2_SESSION_SETUP, NULL, server,
143862306a36Sopenharmony_ci				 (void **) &req,
143962306a36Sopenharmony_ci				 &total_len);
144062306a36Sopenharmony_ci	if (rc)
144162306a36Sopenharmony_ci		return rc;
144262306a36Sopenharmony_ci
144362306a36Sopenharmony_ci	spin_lock(&ses->ses_lock);
144462306a36Sopenharmony_ci	is_binding = (ses->ses_status == SES_GOOD);
144562306a36Sopenharmony_ci	spin_unlock(&ses->ses_lock);
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	if (is_binding) {
144862306a36Sopenharmony_ci		req->hdr.SessionId = cpu_to_le64(ses->Suid);
144962306a36Sopenharmony_ci		req->hdr.Flags |= SMB2_FLAGS_SIGNED;
145062306a36Sopenharmony_ci		req->PreviousSessionId = 0;
145162306a36Sopenharmony_ci		req->Flags = SMB2_SESSION_REQ_FLAG_BINDING;
145262306a36Sopenharmony_ci		cifs_dbg(FYI, "Binding to sess id: %llx\n", ses->Suid);
145362306a36Sopenharmony_ci	} else {
145462306a36Sopenharmony_ci		/* First session, not a reauthenticate */
145562306a36Sopenharmony_ci		req->hdr.SessionId = 0;
145662306a36Sopenharmony_ci		/*
145762306a36Sopenharmony_ci		 * if reconnect, we need to send previous sess id
145862306a36Sopenharmony_ci		 * otherwise it is 0
145962306a36Sopenharmony_ci		 */
146062306a36Sopenharmony_ci		req->PreviousSessionId = cpu_to_le64(sess_data->previous_session);
146162306a36Sopenharmony_ci		req->Flags = 0; /* MBZ */
146262306a36Sopenharmony_ci		cifs_dbg(FYI, "Fresh session. Previous: %llx\n",
146362306a36Sopenharmony_ci			 sess_data->previous_session);
146462306a36Sopenharmony_ci	}
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_ci	/* enough to enable echos and oplocks and one max size write */
146762306a36Sopenharmony_ci	if (server->credits >= server->max_credits)
146862306a36Sopenharmony_ci		req->hdr.CreditRequest = cpu_to_le16(0);
146962306a36Sopenharmony_ci	else
147062306a36Sopenharmony_ci		req->hdr.CreditRequest = cpu_to_le16(
147162306a36Sopenharmony_ci			min_t(int, server->max_credits -
147262306a36Sopenharmony_ci			      server->credits, 130));
147362306a36Sopenharmony_ci
147462306a36Sopenharmony_ci	/* only one of SMB2 signing flags may be set in SMB2 request */
147562306a36Sopenharmony_ci	if (server->sign)
147662306a36Sopenharmony_ci		req->SecurityMode = SMB2_NEGOTIATE_SIGNING_REQUIRED;
147762306a36Sopenharmony_ci	else if (global_secflags & CIFSSEC_MAY_SIGN) /* one flag unlike MUST_ */
147862306a36Sopenharmony_ci		req->SecurityMode = SMB2_NEGOTIATE_SIGNING_ENABLED;
147962306a36Sopenharmony_ci	else
148062306a36Sopenharmony_ci		req->SecurityMode = 0;
148162306a36Sopenharmony_ci
148262306a36Sopenharmony_ci#ifdef CONFIG_CIFS_DFS_UPCALL
148362306a36Sopenharmony_ci	req->Capabilities = cpu_to_le32(SMB2_GLOBAL_CAP_DFS);
148462306a36Sopenharmony_ci#else
148562306a36Sopenharmony_ci	req->Capabilities = 0;
148662306a36Sopenharmony_ci#endif /* DFS_UPCALL */
148762306a36Sopenharmony_ci
148862306a36Sopenharmony_ci	req->Channel = 0; /* MBZ */
148962306a36Sopenharmony_ci
149062306a36Sopenharmony_ci	sess_data->iov[0].iov_base = (char *)req;
149162306a36Sopenharmony_ci	/* 1 for pad */
149262306a36Sopenharmony_ci	sess_data->iov[0].iov_len = total_len - 1;
149362306a36Sopenharmony_ci	/*
149462306a36Sopenharmony_ci	 * This variable will be used to clear the buffer
149562306a36Sopenharmony_ci	 * allocated above in case of any error in the calling function.
149662306a36Sopenharmony_ci	 */
149762306a36Sopenharmony_ci	sess_data->buf0_type = CIFS_SMALL_BUFFER;
149862306a36Sopenharmony_ci
149962306a36Sopenharmony_ci	return 0;
150062306a36Sopenharmony_ci}
150162306a36Sopenharmony_ci
150262306a36Sopenharmony_cistatic void
150362306a36Sopenharmony_ciSMB2_sess_free_buffer(struct SMB2_sess_data *sess_data)
150462306a36Sopenharmony_ci{
150562306a36Sopenharmony_ci	struct kvec *iov = sess_data->iov;
150662306a36Sopenharmony_ci
150762306a36Sopenharmony_ci	/* iov[1] is already freed by caller */
150862306a36Sopenharmony_ci	if (sess_data->buf0_type != CIFS_NO_BUFFER && iov[0].iov_base)
150962306a36Sopenharmony_ci		memzero_explicit(iov[0].iov_base, iov[0].iov_len);
151062306a36Sopenharmony_ci
151162306a36Sopenharmony_ci	free_rsp_buf(sess_data->buf0_type, iov[0].iov_base);
151262306a36Sopenharmony_ci	sess_data->buf0_type = CIFS_NO_BUFFER;
151362306a36Sopenharmony_ci}
151462306a36Sopenharmony_ci
151562306a36Sopenharmony_cistatic int
151662306a36Sopenharmony_ciSMB2_sess_sendreceive(struct SMB2_sess_data *sess_data)
151762306a36Sopenharmony_ci{
151862306a36Sopenharmony_ci	int rc;
151962306a36Sopenharmony_ci	struct smb_rqst rqst;
152062306a36Sopenharmony_ci	struct smb2_sess_setup_req *req = sess_data->iov[0].iov_base;
152162306a36Sopenharmony_ci	struct kvec rsp_iov = { NULL, 0 };
152262306a36Sopenharmony_ci
152362306a36Sopenharmony_ci	/* Testing shows that buffer offset must be at location of Buffer[0] */
152462306a36Sopenharmony_ci	req->SecurityBufferOffset =
152562306a36Sopenharmony_ci		cpu_to_le16(sizeof(struct smb2_sess_setup_req));
152662306a36Sopenharmony_ci	req->SecurityBufferLength = cpu_to_le16(sess_data->iov[1].iov_len);
152762306a36Sopenharmony_ci
152862306a36Sopenharmony_ci	memset(&rqst, 0, sizeof(struct smb_rqst));
152962306a36Sopenharmony_ci	rqst.rq_iov = sess_data->iov;
153062306a36Sopenharmony_ci	rqst.rq_nvec = 2;
153162306a36Sopenharmony_ci
153262306a36Sopenharmony_ci	/* BB add code to build os and lm fields */
153362306a36Sopenharmony_ci	rc = cifs_send_recv(sess_data->xid, sess_data->ses,
153462306a36Sopenharmony_ci			    sess_data->server,
153562306a36Sopenharmony_ci			    &rqst,
153662306a36Sopenharmony_ci			    &sess_data->buf0_type,
153762306a36Sopenharmony_ci			    CIFS_LOG_ERROR | CIFS_SESS_OP, &rsp_iov);
153862306a36Sopenharmony_ci	cifs_small_buf_release(sess_data->iov[0].iov_base);
153962306a36Sopenharmony_ci	if (rc == 0)
154062306a36Sopenharmony_ci		sess_data->ses->expired_pwd = false;
154162306a36Sopenharmony_ci	else if ((rc == -EACCES) || (rc == -EKEYEXPIRED) || (rc == -EKEYREVOKED))
154262306a36Sopenharmony_ci		sess_data->ses->expired_pwd = true;
154362306a36Sopenharmony_ci
154462306a36Sopenharmony_ci	memcpy(&sess_data->iov[0], &rsp_iov, sizeof(struct kvec));
154562306a36Sopenharmony_ci
154662306a36Sopenharmony_ci	return rc;
154762306a36Sopenharmony_ci}
154862306a36Sopenharmony_ci
154962306a36Sopenharmony_cistatic int
155062306a36Sopenharmony_ciSMB2_sess_establish_session(struct SMB2_sess_data *sess_data)
155162306a36Sopenharmony_ci{
155262306a36Sopenharmony_ci	int rc = 0;
155362306a36Sopenharmony_ci	struct cifs_ses *ses = sess_data->ses;
155462306a36Sopenharmony_ci	struct TCP_Server_Info *server = sess_data->server;
155562306a36Sopenharmony_ci
155662306a36Sopenharmony_ci	cifs_server_lock(server);
155762306a36Sopenharmony_ci	if (server->ops->generate_signingkey) {
155862306a36Sopenharmony_ci		rc = server->ops->generate_signingkey(ses, server);
155962306a36Sopenharmony_ci		if (rc) {
156062306a36Sopenharmony_ci			cifs_dbg(FYI,
156162306a36Sopenharmony_ci				"SMB3 session key generation failed\n");
156262306a36Sopenharmony_ci			cifs_server_unlock(server);
156362306a36Sopenharmony_ci			return rc;
156462306a36Sopenharmony_ci		}
156562306a36Sopenharmony_ci	}
156662306a36Sopenharmony_ci	if (!server->session_estab) {
156762306a36Sopenharmony_ci		server->sequence_number = 0x2;
156862306a36Sopenharmony_ci		server->session_estab = true;
156962306a36Sopenharmony_ci	}
157062306a36Sopenharmony_ci	cifs_server_unlock(server);
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci	cifs_dbg(FYI, "SMB2/3 session established successfully\n");
157362306a36Sopenharmony_ci	return rc;
157462306a36Sopenharmony_ci}
157562306a36Sopenharmony_ci
157662306a36Sopenharmony_ci#ifdef CONFIG_CIFS_UPCALL
157762306a36Sopenharmony_cistatic void
157862306a36Sopenharmony_ciSMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
157962306a36Sopenharmony_ci{
158062306a36Sopenharmony_ci	int rc;
158162306a36Sopenharmony_ci	struct cifs_ses *ses = sess_data->ses;
158262306a36Sopenharmony_ci	struct TCP_Server_Info *server = sess_data->server;
158362306a36Sopenharmony_ci	struct cifs_spnego_msg *msg;
158462306a36Sopenharmony_ci	struct key *spnego_key = NULL;
158562306a36Sopenharmony_ci	struct smb2_sess_setup_rsp *rsp = NULL;
158662306a36Sopenharmony_ci	bool is_binding = false;
158762306a36Sopenharmony_ci
158862306a36Sopenharmony_ci	rc = SMB2_sess_alloc_buffer(sess_data);
158962306a36Sopenharmony_ci	if (rc)
159062306a36Sopenharmony_ci		goto out;
159162306a36Sopenharmony_ci
159262306a36Sopenharmony_ci	spnego_key = cifs_get_spnego_key(ses, server);
159362306a36Sopenharmony_ci	if (IS_ERR(spnego_key)) {
159462306a36Sopenharmony_ci		rc = PTR_ERR(spnego_key);
159562306a36Sopenharmony_ci		if (rc == -ENOKEY)
159662306a36Sopenharmony_ci			cifs_dbg(VFS, "Verify user has a krb5 ticket and keyutils is installed\n");
159762306a36Sopenharmony_ci		spnego_key = NULL;
159862306a36Sopenharmony_ci		goto out;
159962306a36Sopenharmony_ci	}
160062306a36Sopenharmony_ci
160162306a36Sopenharmony_ci	msg = spnego_key->payload.data[0];
160262306a36Sopenharmony_ci	/*
160362306a36Sopenharmony_ci	 * check version field to make sure that cifs.upcall is
160462306a36Sopenharmony_ci	 * sending us a response in an expected form
160562306a36Sopenharmony_ci	 */
160662306a36Sopenharmony_ci	if (msg->version != CIFS_SPNEGO_UPCALL_VERSION) {
160762306a36Sopenharmony_ci		cifs_dbg(VFS, "bad cifs.upcall version. Expected %d got %d\n",
160862306a36Sopenharmony_ci			 CIFS_SPNEGO_UPCALL_VERSION, msg->version);
160962306a36Sopenharmony_ci		rc = -EKEYREJECTED;
161062306a36Sopenharmony_ci		goto out_put_spnego_key;
161162306a36Sopenharmony_ci	}
161262306a36Sopenharmony_ci
161362306a36Sopenharmony_ci	spin_lock(&ses->ses_lock);
161462306a36Sopenharmony_ci	is_binding = (ses->ses_status == SES_GOOD);
161562306a36Sopenharmony_ci	spin_unlock(&ses->ses_lock);
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_ci	/* keep session key if binding */
161862306a36Sopenharmony_ci	if (!is_binding) {
161962306a36Sopenharmony_ci		kfree_sensitive(ses->auth_key.response);
162062306a36Sopenharmony_ci		ses->auth_key.response = kmemdup(msg->data, msg->sesskey_len,
162162306a36Sopenharmony_ci						 GFP_KERNEL);
162262306a36Sopenharmony_ci		if (!ses->auth_key.response) {
162362306a36Sopenharmony_ci			cifs_dbg(VFS, "Kerberos can't allocate (%u bytes) memory\n",
162462306a36Sopenharmony_ci				 msg->sesskey_len);
162562306a36Sopenharmony_ci			rc = -ENOMEM;
162662306a36Sopenharmony_ci			goto out_put_spnego_key;
162762306a36Sopenharmony_ci		}
162862306a36Sopenharmony_ci		ses->auth_key.len = msg->sesskey_len;
162962306a36Sopenharmony_ci	}
163062306a36Sopenharmony_ci
163162306a36Sopenharmony_ci	sess_data->iov[1].iov_base = msg->data + msg->sesskey_len;
163262306a36Sopenharmony_ci	sess_data->iov[1].iov_len = msg->secblob_len;
163362306a36Sopenharmony_ci
163462306a36Sopenharmony_ci	rc = SMB2_sess_sendreceive(sess_data);
163562306a36Sopenharmony_ci	if (rc)
163662306a36Sopenharmony_ci		goto out_put_spnego_key;
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci	rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
163962306a36Sopenharmony_ci	/* keep session id and flags if binding */
164062306a36Sopenharmony_ci	if (!is_binding) {
164162306a36Sopenharmony_ci		ses->Suid = le64_to_cpu(rsp->hdr.SessionId);
164262306a36Sopenharmony_ci		ses->session_flags = le16_to_cpu(rsp->SessionFlags);
164362306a36Sopenharmony_ci	}
164462306a36Sopenharmony_ci
164562306a36Sopenharmony_ci	rc = SMB2_sess_establish_session(sess_data);
164662306a36Sopenharmony_ciout_put_spnego_key:
164762306a36Sopenharmony_ci	key_invalidate(spnego_key);
164862306a36Sopenharmony_ci	key_put(spnego_key);
164962306a36Sopenharmony_ci	if (rc) {
165062306a36Sopenharmony_ci		kfree_sensitive(ses->auth_key.response);
165162306a36Sopenharmony_ci		ses->auth_key.response = NULL;
165262306a36Sopenharmony_ci		ses->auth_key.len = 0;
165362306a36Sopenharmony_ci	}
165462306a36Sopenharmony_ciout:
165562306a36Sopenharmony_ci	sess_data->result = rc;
165662306a36Sopenharmony_ci	sess_data->func = NULL;
165762306a36Sopenharmony_ci	SMB2_sess_free_buffer(sess_data);
165862306a36Sopenharmony_ci}
165962306a36Sopenharmony_ci#else
166062306a36Sopenharmony_cistatic void
166162306a36Sopenharmony_ciSMB2_auth_kerberos(struct SMB2_sess_data *sess_data)
166262306a36Sopenharmony_ci{
166362306a36Sopenharmony_ci	cifs_dbg(VFS, "Kerberos negotiated but upcall support disabled!\n");
166462306a36Sopenharmony_ci	sess_data->result = -EOPNOTSUPP;
166562306a36Sopenharmony_ci	sess_data->func = NULL;
166662306a36Sopenharmony_ci}
166762306a36Sopenharmony_ci#endif
166862306a36Sopenharmony_ci
166962306a36Sopenharmony_cistatic void
167062306a36Sopenharmony_ciSMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data);
167162306a36Sopenharmony_ci
167262306a36Sopenharmony_cistatic void
167362306a36Sopenharmony_ciSMB2_sess_auth_rawntlmssp_negotiate(struct SMB2_sess_data *sess_data)
167462306a36Sopenharmony_ci{
167562306a36Sopenharmony_ci	int rc;
167662306a36Sopenharmony_ci	struct cifs_ses *ses = sess_data->ses;
167762306a36Sopenharmony_ci	struct TCP_Server_Info *server = sess_data->server;
167862306a36Sopenharmony_ci	struct smb2_sess_setup_rsp *rsp = NULL;
167962306a36Sopenharmony_ci	unsigned char *ntlmssp_blob = NULL;
168062306a36Sopenharmony_ci	bool use_spnego = false; /* else use raw ntlmssp */
168162306a36Sopenharmony_ci	u16 blob_length = 0;
168262306a36Sopenharmony_ci	bool is_binding = false;
168362306a36Sopenharmony_ci
168462306a36Sopenharmony_ci	/*
168562306a36Sopenharmony_ci	 * If memory allocation is successful, caller of this function
168662306a36Sopenharmony_ci	 * frees it.
168762306a36Sopenharmony_ci	 */
168862306a36Sopenharmony_ci	ses->ntlmssp = kmalloc(sizeof(struct ntlmssp_auth), GFP_KERNEL);
168962306a36Sopenharmony_ci	if (!ses->ntlmssp) {
169062306a36Sopenharmony_ci		rc = -ENOMEM;
169162306a36Sopenharmony_ci		goto out_err;
169262306a36Sopenharmony_ci	}
169362306a36Sopenharmony_ci	ses->ntlmssp->sesskey_per_smbsess = true;
169462306a36Sopenharmony_ci
169562306a36Sopenharmony_ci	rc = SMB2_sess_alloc_buffer(sess_data);
169662306a36Sopenharmony_ci	if (rc)
169762306a36Sopenharmony_ci		goto out_err;
169862306a36Sopenharmony_ci
169962306a36Sopenharmony_ci	rc = build_ntlmssp_smb3_negotiate_blob(&ntlmssp_blob,
170062306a36Sopenharmony_ci					  &blob_length, ses, server,
170162306a36Sopenharmony_ci					  sess_data->nls_cp);
170262306a36Sopenharmony_ci	if (rc)
170362306a36Sopenharmony_ci		goto out;
170462306a36Sopenharmony_ci
170562306a36Sopenharmony_ci	if (use_spnego) {
170662306a36Sopenharmony_ci		/* BB eventually need to add this */
170762306a36Sopenharmony_ci		cifs_dbg(VFS, "spnego not supported for SMB2 yet\n");
170862306a36Sopenharmony_ci		rc = -EOPNOTSUPP;
170962306a36Sopenharmony_ci		goto out;
171062306a36Sopenharmony_ci	}
171162306a36Sopenharmony_ci	sess_data->iov[1].iov_base = ntlmssp_blob;
171262306a36Sopenharmony_ci	sess_data->iov[1].iov_len = blob_length;
171362306a36Sopenharmony_ci
171462306a36Sopenharmony_ci	rc = SMB2_sess_sendreceive(sess_data);
171562306a36Sopenharmony_ci	rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
171662306a36Sopenharmony_ci
171762306a36Sopenharmony_ci	/* If true, rc here is expected and not an error */
171862306a36Sopenharmony_ci	if (sess_data->buf0_type != CIFS_NO_BUFFER &&
171962306a36Sopenharmony_ci		rsp->hdr.Status == STATUS_MORE_PROCESSING_REQUIRED)
172062306a36Sopenharmony_ci		rc = 0;
172162306a36Sopenharmony_ci
172262306a36Sopenharmony_ci	if (rc)
172362306a36Sopenharmony_ci		goto out;
172462306a36Sopenharmony_ci
172562306a36Sopenharmony_ci	if (offsetof(struct smb2_sess_setup_rsp, Buffer) !=
172662306a36Sopenharmony_ci			le16_to_cpu(rsp->SecurityBufferOffset)) {
172762306a36Sopenharmony_ci		cifs_dbg(VFS, "Invalid security buffer offset %d\n",
172862306a36Sopenharmony_ci			le16_to_cpu(rsp->SecurityBufferOffset));
172962306a36Sopenharmony_ci		rc = -EIO;
173062306a36Sopenharmony_ci		goto out;
173162306a36Sopenharmony_ci	}
173262306a36Sopenharmony_ci	rc = decode_ntlmssp_challenge(rsp->Buffer,
173362306a36Sopenharmony_ci			le16_to_cpu(rsp->SecurityBufferLength), ses);
173462306a36Sopenharmony_ci	if (rc)
173562306a36Sopenharmony_ci		goto out;
173662306a36Sopenharmony_ci
173762306a36Sopenharmony_ci	cifs_dbg(FYI, "rawntlmssp session setup challenge phase\n");
173862306a36Sopenharmony_ci
173962306a36Sopenharmony_ci	spin_lock(&ses->ses_lock);
174062306a36Sopenharmony_ci	is_binding = (ses->ses_status == SES_GOOD);
174162306a36Sopenharmony_ci	spin_unlock(&ses->ses_lock);
174262306a36Sopenharmony_ci
174362306a36Sopenharmony_ci	/* keep existing ses id and flags if binding */
174462306a36Sopenharmony_ci	if (!is_binding) {
174562306a36Sopenharmony_ci		ses->Suid = le64_to_cpu(rsp->hdr.SessionId);
174662306a36Sopenharmony_ci		ses->session_flags = le16_to_cpu(rsp->SessionFlags);
174762306a36Sopenharmony_ci	}
174862306a36Sopenharmony_ci
174962306a36Sopenharmony_ciout:
175062306a36Sopenharmony_ci	kfree_sensitive(ntlmssp_blob);
175162306a36Sopenharmony_ci	SMB2_sess_free_buffer(sess_data);
175262306a36Sopenharmony_ci	if (!rc) {
175362306a36Sopenharmony_ci		sess_data->result = 0;
175462306a36Sopenharmony_ci		sess_data->func = SMB2_sess_auth_rawntlmssp_authenticate;
175562306a36Sopenharmony_ci		return;
175662306a36Sopenharmony_ci	}
175762306a36Sopenharmony_ciout_err:
175862306a36Sopenharmony_ci	kfree_sensitive(ses->ntlmssp);
175962306a36Sopenharmony_ci	ses->ntlmssp = NULL;
176062306a36Sopenharmony_ci	sess_data->result = rc;
176162306a36Sopenharmony_ci	sess_data->func = NULL;
176262306a36Sopenharmony_ci}
176362306a36Sopenharmony_ci
176462306a36Sopenharmony_cistatic void
176562306a36Sopenharmony_ciSMB2_sess_auth_rawntlmssp_authenticate(struct SMB2_sess_data *sess_data)
176662306a36Sopenharmony_ci{
176762306a36Sopenharmony_ci	int rc;
176862306a36Sopenharmony_ci	struct cifs_ses *ses = sess_data->ses;
176962306a36Sopenharmony_ci	struct TCP_Server_Info *server = sess_data->server;
177062306a36Sopenharmony_ci	struct smb2_sess_setup_req *req;
177162306a36Sopenharmony_ci	struct smb2_sess_setup_rsp *rsp = NULL;
177262306a36Sopenharmony_ci	unsigned char *ntlmssp_blob = NULL;
177362306a36Sopenharmony_ci	bool use_spnego = false; /* else use raw ntlmssp */
177462306a36Sopenharmony_ci	u16 blob_length = 0;
177562306a36Sopenharmony_ci	bool is_binding = false;
177662306a36Sopenharmony_ci
177762306a36Sopenharmony_ci	rc = SMB2_sess_alloc_buffer(sess_data);
177862306a36Sopenharmony_ci	if (rc)
177962306a36Sopenharmony_ci		goto out;
178062306a36Sopenharmony_ci
178162306a36Sopenharmony_ci	req = (struct smb2_sess_setup_req *) sess_data->iov[0].iov_base;
178262306a36Sopenharmony_ci	req->hdr.SessionId = cpu_to_le64(ses->Suid);
178362306a36Sopenharmony_ci
178462306a36Sopenharmony_ci	rc = build_ntlmssp_auth_blob(&ntlmssp_blob, &blob_length,
178562306a36Sopenharmony_ci				     ses, server,
178662306a36Sopenharmony_ci				     sess_data->nls_cp);
178762306a36Sopenharmony_ci	if (rc) {
178862306a36Sopenharmony_ci		cifs_dbg(FYI, "build_ntlmssp_auth_blob failed %d\n", rc);
178962306a36Sopenharmony_ci		goto out;
179062306a36Sopenharmony_ci	}
179162306a36Sopenharmony_ci
179262306a36Sopenharmony_ci	if (use_spnego) {
179362306a36Sopenharmony_ci		/* BB eventually need to add this */
179462306a36Sopenharmony_ci		cifs_dbg(VFS, "spnego not supported for SMB2 yet\n");
179562306a36Sopenharmony_ci		rc = -EOPNOTSUPP;
179662306a36Sopenharmony_ci		goto out;
179762306a36Sopenharmony_ci	}
179862306a36Sopenharmony_ci	sess_data->iov[1].iov_base = ntlmssp_blob;
179962306a36Sopenharmony_ci	sess_data->iov[1].iov_len = blob_length;
180062306a36Sopenharmony_ci
180162306a36Sopenharmony_ci	rc = SMB2_sess_sendreceive(sess_data);
180262306a36Sopenharmony_ci	if (rc)
180362306a36Sopenharmony_ci		goto out;
180462306a36Sopenharmony_ci
180562306a36Sopenharmony_ci	rsp = (struct smb2_sess_setup_rsp *)sess_data->iov[0].iov_base;
180662306a36Sopenharmony_ci
180762306a36Sopenharmony_ci	spin_lock(&ses->ses_lock);
180862306a36Sopenharmony_ci	is_binding = (ses->ses_status == SES_GOOD);
180962306a36Sopenharmony_ci	spin_unlock(&ses->ses_lock);
181062306a36Sopenharmony_ci
181162306a36Sopenharmony_ci	/* keep existing ses id and flags if binding */
181262306a36Sopenharmony_ci	if (!is_binding) {
181362306a36Sopenharmony_ci		ses->Suid = le64_to_cpu(rsp->hdr.SessionId);
181462306a36Sopenharmony_ci		ses->session_flags = le16_to_cpu(rsp->SessionFlags);
181562306a36Sopenharmony_ci	}
181662306a36Sopenharmony_ci
181762306a36Sopenharmony_ci	rc = SMB2_sess_establish_session(sess_data);
181862306a36Sopenharmony_ci#ifdef CONFIG_CIFS_DEBUG_DUMP_KEYS
181962306a36Sopenharmony_ci	if (ses->server->dialect < SMB30_PROT_ID) {
182062306a36Sopenharmony_ci		cifs_dbg(VFS, "%s: dumping generated SMB2 session keys\n", __func__);
182162306a36Sopenharmony_ci		/*
182262306a36Sopenharmony_ci		 * The session id is opaque in terms of endianness, so we can't
182362306a36Sopenharmony_ci		 * print it as a long long. we dump it as we got it on the wire
182462306a36Sopenharmony_ci		 */
182562306a36Sopenharmony_ci		cifs_dbg(VFS, "Session Id    %*ph\n", (int)sizeof(ses->Suid),
182662306a36Sopenharmony_ci			 &ses->Suid);
182762306a36Sopenharmony_ci		cifs_dbg(VFS, "Session Key   %*ph\n",
182862306a36Sopenharmony_ci			 SMB2_NTLMV2_SESSKEY_SIZE, ses->auth_key.response);
182962306a36Sopenharmony_ci		cifs_dbg(VFS, "Signing Key   %*ph\n",
183062306a36Sopenharmony_ci			 SMB3_SIGN_KEY_SIZE, ses->auth_key.response);
183162306a36Sopenharmony_ci	}
183262306a36Sopenharmony_ci#endif
183362306a36Sopenharmony_ciout:
183462306a36Sopenharmony_ci	kfree_sensitive(ntlmssp_blob);
183562306a36Sopenharmony_ci	SMB2_sess_free_buffer(sess_data);
183662306a36Sopenharmony_ci	kfree_sensitive(ses->ntlmssp);
183762306a36Sopenharmony_ci	ses->ntlmssp = NULL;
183862306a36Sopenharmony_ci	sess_data->result = rc;
183962306a36Sopenharmony_ci	sess_data->func = NULL;
184062306a36Sopenharmony_ci}
184162306a36Sopenharmony_ci
184262306a36Sopenharmony_cistatic int
184362306a36Sopenharmony_ciSMB2_select_sec(struct SMB2_sess_data *sess_data)
184462306a36Sopenharmony_ci{
184562306a36Sopenharmony_ci	int type;
184662306a36Sopenharmony_ci	struct cifs_ses *ses = sess_data->ses;
184762306a36Sopenharmony_ci	struct TCP_Server_Info *server = sess_data->server;
184862306a36Sopenharmony_ci
184962306a36Sopenharmony_ci	type = smb2_select_sectype(server, ses->sectype);
185062306a36Sopenharmony_ci	cifs_dbg(FYI, "sess setup type %d\n", type);
185162306a36Sopenharmony_ci	if (type == Unspecified) {
185262306a36Sopenharmony_ci		cifs_dbg(VFS, "Unable to select appropriate authentication method!\n");
185362306a36Sopenharmony_ci		return -EINVAL;
185462306a36Sopenharmony_ci	}
185562306a36Sopenharmony_ci
185662306a36Sopenharmony_ci	switch (type) {
185762306a36Sopenharmony_ci	case Kerberos:
185862306a36Sopenharmony_ci		sess_data->func = SMB2_auth_kerberos;
185962306a36Sopenharmony_ci		break;
186062306a36Sopenharmony_ci	case RawNTLMSSP:
186162306a36Sopenharmony_ci		sess_data->func = SMB2_sess_auth_rawntlmssp_negotiate;
186262306a36Sopenharmony_ci		break;
186362306a36Sopenharmony_ci	default:
186462306a36Sopenharmony_ci		cifs_dbg(VFS, "secType %d not supported!\n", type);
186562306a36Sopenharmony_ci		return -EOPNOTSUPP;
186662306a36Sopenharmony_ci	}
186762306a36Sopenharmony_ci
186862306a36Sopenharmony_ci	return 0;
186962306a36Sopenharmony_ci}
187062306a36Sopenharmony_ci
187162306a36Sopenharmony_ciint
187262306a36Sopenharmony_ciSMB2_sess_setup(const unsigned int xid, struct cifs_ses *ses,
187362306a36Sopenharmony_ci		struct TCP_Server_Info *server,
187462306a36Sopenharmony_ci		const struct nls_table *nls_cp)
187562306a36Sopenharmony_ci{
187662306a36Sopenharmony_ci	int rc = 0;
187762306a36Sopenharmony_ci	struct SMB2_sess_data *sess_data;
187862306a36Sopenharmony_ci
187962306a36Sopenharmony_ci	cifs_dbg(FYI, "Session Setup\n");
188062306a36Sopenharmony_ci
188162306a36Sopenharmony_ci	if (!server) {
188262306a36Sopenharmony_ci		WARN(1, "%s: server is NULL!\n", __func__);
188362306a36Sopenharmony_ci		return -EIO;
188462306a36Sopenharmony_ci	}
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_ci	sess_data = kzalloc(sizeof(struct SMB2_sess_data), GFP_KERNEL);
188762306a36Sopenharmony_ci	if (!sess_data)
188862306a36Sopenharmony_ci		return -ENOMEM;
188962306a36Sopenharmony_ci
189062306a36Sopenharmony_ci	sess_data->xid = xid;
189162306a36Sopenharmony_ci	sess_data->ses = ses;
189262306a36Sopenharmony_ci	sess_data->server = server;
189362306a36Sopenharmony_ci	sess_data->buf0_type = CIFS_NO_BUFFER;
189462306a36Sopenharmony_ci	sess_data->nls_cp = (struct nls_table *) nls_cp;
189562306a36Sopenharmony_ci	sess_data->previous_session = ses->Suid;
189662306a36Sopenharmony_ci
189762306a36Sopenharmony_ci	rc = SMB2_select_sec(sess_data);
189862306a36Sopenharmony_ci	if (rc)
189962306a36Sopenharmony_ci		goto out;
190062306a36Sopenharmony_ci
190162306a36Sopenharmony_ci	/*
190262306a36Sopenharmony_ci	 * Initialize the session hash with the server one.
190362306a36Sopenharmony_ci	 */
190462306a36Sopenharmony_ci	memcpy(ses->preauth_sha_hash, server->preauth_sha_hash,
190562306a36Sopenharmony_ci	       SMB2_PREAUTH_HASH_SIZE);
190662306a36Sopenharmony_ci
190762306a36Sopenharmony_ci	while (sess_data->func)
190862306a36Sopenharmony_ci		sess_data->func(sess_data);
190962306a36Sopenharmony_ci
191062306a36Sopenharmony_ci	if ((ses->session_flags & SMB2_SESSION_FLAG_IS_GUEST) && (ses->sign))
191162306a36Sopenharmony_ci		cifs_server_dbg(VFS, "signing requested but authenticated as guest\n");
191262306a36Sopenharmony_ci	rc = sess_data->result;
191362306a36Sopenharmony_ciout:
191462306a36Sopenharmony_ci	kfree_sensitive(sess_data);
191562306a36Sopenharmony_ci	return rc;
191662306a36Sopenharmony_ci}
191762306a36Sopenharmony_ci
191862306a36Sopenharmony_ciint
191962306a36Sopenharmony_ciSMB2_logoff(const unsigned int xid, struct cifs_ses *ses)
192062306a36Sopenharmony_ci{
192162306a36Sopenharmony_ci	struct smb_rqst rqst;
192262306a36Sopenharmony_ci	struct smb2_logoff_req *req; /* response is also trivial struct */
192362306a36Sopenharmony_ci	int rc = 0;
192462306a36Sopenharmony_ci	struct TCP_Server_Info *server;
192562306a36Sopenharmony_ci	int flags = 0;
192662306a36Sopenharmony_ci	unsigned int total_len;
192762306a36Sopenharmony_ci	struct kvec iov[1];
192862306a36Sopenharmony_ci	struct kvec rsp_iov;
192962306a36Sopenharmony_ci	int resp_buf_type;
193062306a36Sopenharmony_ci
193162306a36Sopenharmony_ci	cifs_dbg(FYI, "disconnect session %p\n", ses);
193262306a36Sopenharmony_ci
193362306a36Sopenharmony_ci	if (ses && (ses->server))
193462306a36Sopenharmony_ci		server = ses->server;
193562306a36Sopenharmony_ci	else
193662306a36Sopenharmony_ci		return -EIO;
193762306a36Sopenharmony_ci
193862306a36Sopenharmony_ci	/* no need to send SMB logoff if uid already closed due to reconnect */
193962306a36Sopenharmony_ci	spin_lock(&ses->chan_lock);
194062306a36Sopenharmony_ci	if (CIFS_ALL_CHANS_NEED_RECONNECT(ses)) {
194162306a36Sopenharmony_ci		spin_unlock(&ses->chan_lock);
194262306a36Sopenharmony_ci		goto smb2_session_already_dead;
194362306a36Sopenharmony_ci	}
194462306a36Sopenharmony_ci	spin_unlock(&ses->chan_lock);
194562306a36Sopenharmony_ci
194662306a36Sopenharmony_ci	rc = smb2_plain_req_init(SMB2_LOGOFF, NULL, ses->server,
194762306a36Sopenharmony_ci				 (void **) &req, &total_len);
194862306a36Sopenharmony_ci	if (rc)
194962306a36Sopenharmony_ci		return rc;
195062306a36Sopenharmony_ci
195162306a36Sopenharmony_ci	 /* since no tcon, smb2_init can not do this, so do here */
195262306a36Sopenharmony_ci	req->hdr.SessionId = cpu_to_le64(ses->Suid);
195362306a36Sopenharmony_ci
195462306a36Sopenharmony_ci	if (ses->session_flags & SMB2_SESSION_FLAG_ENCRYPT_DATA)
195562306a36Sopenharmony_ci		flags |= CIFS_TRANSFORM_REQ;
195662306a36Sopenharmony_ci	else if (server->sign)
195762306a36Sopenharmony_ci		req->hdr.Flags |= SMB2_FLAGS_SIGNED;
195862306a36Sopenharmony_ci
195962306a36Sopenharmony_ci	flags |= CIFS_NO_RSP_BUF;
196062306a36Sopenharmony_ci
196162306a36Sopenharmony_ci	iov[0].iov_base = (char *)req;
196262306a36Sopenharmony_ci	iov[0].iov_len = total_len;
196362306a36Sopenharmony_ci
196462306a36Sopenharmony_ci	memset(&rqst, 0, sizeof(struct smb_rqst));
196562306a36Sopenharmony_ci	rqst.rq_iov = iov;
196662306a36Sopenharmony_ci	rqst.rq_nvec = 1;
196762306a36Sopenharmony_ci
196862306a36Sopenharmony_ci	rc = cifs_send_recv(xid, ses, ses->server,
196962306a36Sopenharmony_ci			    &rqst, &resp_buf_type, flags, &rsp_iov);
197062306a36Sopenharmony_ci	cifs_small_buf_release(req);
197162306a36Sopenharmony_ci	/*
197262306a36Sopenharmony_ci	 * No tcon so can't do
197362306a36Sopenharmony_ci	 * cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_fail[SMB2...]);
197462306a36Sopenharmony_ci	 */
197562306a36Sopenharmony_ci
197662306a36Sopenharmony_cismb2_session_already_dead:
197762306a36Sopenharmony_ci	return rc;
197862306a36Sopenharmony_ci}
197962306a36Sopenharmony_ci
198062306a36Sopenharmony_cistatic inline void cifs_stats_fail_inc(struct cifs_tcon *tcon, uint16_t code)
198162306a36Sopenharmony_ci{
198262306a36Sopenharmony_ci	cifs_stats_inc(&tcon->stats.smb2_stats.smb2_com_failed[code]);
198362306a36Sopenharmony_ci}
198462306a36Sopenharmony_ci
198562306a36Sopenharmony_ci#define MAX_SHARENAME_LENGTH (255 /* server */ + 80 /* share */ + 1 /* NULL */)
198662306a36Sopenharmony_ci
198762306a36Sopenharmony_ci/* These are similar values to what Windows uses */
198862306a36Sopenharmony_cistatic inline void init_copy_chunk_defaults(struct cifs_tcon *tcon)
198962306a36Sopenharmony_ci{
199062306a36Sopenharmony_ci	tcon->max_chunks = 256;
199162306a36Sopenharmony_ci	tcon->max_bytes_chunk = 1048576;
199262306a36Sopenharmony_ci	tcon->max_bytes_copy = 16777216;
199362306a36Sopenharmony_ci}
199462306a36Sopenharmony_ci
199562306a36Sopenharmony_ciint
199662306a36Sopenharmony_ciSMB2_tcon(const unsigned int xid, struct cifs_ses *ses, const char *tree,
199762306a36Sopenharmony_ci	  struct cifs_tcon *tcon, const struct nls_table *cp)
199862306a36Sopenharmony_ci{
199962306a36Sopenharmony_ci	struct smb_rqst rqst;
200062306a36Sopenharmony_ci	struct smb2_tree_connect_req *req;
200162306a36Sopenharmony_ci	struct smb2_tree_connect_rsp *rsp = NULL;
200262306a36Sopenharmony_ci	struct kvec iov[2];
200362306a36Sopenharmony_ci	struct kvec rsp_iov = { NULL, 0 };
200462306a36Sopenharmony_ci	int rc = 0;
200562306a36Sopenharmony_ci	int resp_buftype;
200662306a36Sopenharmony_ci	int unc_path_len;
200762306a36Sopenharmony_ci	__le16 *unc_path = NULL;
200862306a36Sopenharmony_ci	int flags = 0;
200962306a36Sopenharmony_ci	unsigned int total_len;
201062306a36Sopenharmony_ci	struct TCP_Server_Info *server;
201162306a36Sopenharmony_ci
201262306a36Sopenharmony_ci	/* always use master channel */
201362306a36Sopenharmony_ci	server = ses->server;
201462306a36Sopenharmony_ci
201562306a36Sopenharmony_ci	cifs_dbg(FYI, "TCON\n");
201662306a36Sopenharmony_ci
201762306a36Sopenharmony_ci	if (!server || !tree)
201862306a36Sopenharmony_ci		return -EIO;
201962306a36Sopenharmony_ci
202062306a36Sopenharmony_ci	unc_path = kmalloc(MAX_SHARENAME_LENGTH * 2, GFP_KERNEL);
202162306a36Sopenharmony_ci	if (unc_path == NULL)
202262306a36Sopenharmony_ci		return -ENOMEM;
202362306a36Sopenharmony_ci
202462306a36Sopenharmony_ci	unc_path_len = cifs_strtoUTF16(unc_path, tree, strlen(tree), cp);
202562306a36Sopenharmony_ci	if (unc_path_len <= 0) {
202662306a36Sopenharmony_ci		kfree(unc_path);
202762306a36Sopenharmony_ci		return -EINVAL;
202862306a36Sopenharmony_ci	}
202962306a36Sopenharmony_ci	unc_path_len *= 2;
203062306a36Sopenharmony_ci
203162306a36Sopenharmony_ci	/* SMB2 TREE_CONNECT request must be called with TreeId == 0 */
203262306a36Sopenharmony_ci	tcon->tid = 0;
203362306a36Sopenharmony_ci	atomic_set(&tcon->num_remote_opens, 0);
203462306a36Sopenharmony_ci	rc = smb2_plain_req_init(SMB2_TREE_CONNECT, tcon, server,
203562306a36Sopenharmony_ci				 (void **) &req, &total_len);
203662306a36Sopenharmony_ci	if (rc) {
203762306a36Sopenharmony_ci		kfree(unc_path);
203862306a36Sopenharmony_ci		return rc;
203962306a36Sopenharmony_ci	}
204062306a36Sopenharmony_ci
204162306a36Sopenharmony_ci	if (smb3_encryption_required(tcon))
204262306a36Sopenharmony_ci		flags |= CIFS_TRANSFORM_REQ;
204362306a36Sopenharmony_ci
204462306a36Sopenharmony_ci	iov[0].iov_base = (char *)req;
204562306a36Sopenharmony_ci	/* 1 for pad */
204662306a36Sopenharmony_ci	iov[0].iov_len = total_len - 1;
204762306a36Sopenharmony_ci
204862306a36Sopenharmony_ci	/* Testing shows that buffer offset must be at location of Buffer[0] */
204962306a36Sopenharmony_ci	req->PathOffset = cpu_to_le16(sizeof(struct smb2_tree_connect_req));
205062306a36Sopenharmony_ci	req->PathLength = cpu_to_le16(unc_path_len);
205162306a36Sopenharmony_ci	iov[1].iov_base = unc_path;
205262306a36Sopenharmony_ci	iov[1].iov_len = unc_path_len;
205362306a36Sopenharmony_ci
205462306a36Sopenharmony_ci	/*
205562306a36Sopenharmony_ci	 * 3.11 tcon req must be signed if not encrypted. See MS-SMB2 3.2.4.1.1
205662306a36Sopenharmony_ci	 * unless it is guest or anonymous user. See MS-SMB2 3.2.5.3.1
205762306a36Sopenharmony_ci	 * (Samba servers don't always set the flag so also check if null user)
205862306a36Sopenharmony_ci	 */
205962306a36Sopenharmony_ci	if ((server->dialect == SMB311_PROT_ID) &&
206062306a36Sopenharmony_ci	    !smb3_encryption_required(tcon) &&
206162306a36Sopenharmony_ci	    !(ses->session_flags &
206262306a36Sopenharmony_ci		    (SMB2_SESSION_FLAG_IS_GUEST|SMB2_SESSION_FLAG_IS_NULL)) &&
206362306a36Sopenharmony_ci	    ((ses->user_name != NULL) || (ses->sectype == Kerberos)))
206462306a36Sopenharmony_ci		req->hdr.Flags |= SMB2_FLAGS_SIGNED;
206562306a36Sopenharmony_ci
206662306a36Sopenharmony_ci	memset(&rqst, 0, sizeof(struct smb_rqst));
206762306a36Sopenharmony_ci	rqst.rq_iov = iov;
206862306a36Sopenharmony_ci	rqst.rq_nvec = 2;
206962306a36Sopenharmony_ci
207062306a36Sopenharmony_ci	/* Need 64 for max size write so ask for more in case not there yet */
207162306a36Sopenharmony_ci	if (server->credits >= server->max_credits)
207262306a36Sopenharmony_ci		req->hdr.CreditRequest = cpu_to_le16(0);
207362306a36Sopenharmony_ci	else
207462306a36Sopenharmony_ci		req->hdr.CreditRequest = cpu_to_le16(
207562306a36Sopenharmony_ci			min_t(int, server->max_credits -
207662306a36Sopenharmony_ci			      server->credits, 64));
207762306a36Sopenharmony_ci
207862306a36Sopenharmony_ci	rc = cifs_send_recv(xid, ses, server,
207962306a36Sopenharmony_ci			    &rqst, &resp_buftype, flags, &rsp_iov);
208062306a36Sopenharmony_ci	cifs_small_buf_release(req);
208162306a36Sopenharmony_ci	rsp = (struct smb2_tree_connect_rsp *)rsp_iov.iov_base;
208262306a36Sopenharmony_ci	trace_smb3_tcon(xid, tcon->tid, ses->Suid, tree, rc);
208362306a36Sopenharmony_ci	if ((rc != 0) || (rsp == NULL)) {
208462306a36Sopenharmony_ci		cifs_stats_fail_inc(tcon, SMB2_TREE_CONNECT_HE);
208562306a36Sopenharmony_ci		tcon->need_reconnect = true;
208662306a36Sopenharmony_ci		goto tcon_error_exit;
208762306a36Sopenharmony_ci	}
208862306a36Sopenharmony_ci
208962306a36Sopenharmony_ci	switch (rsp->ShareType) {
209062306a36Sopenharmony_ci	case SMB2_SHARE_TYPE_DISK:
209162306a36Sopenharmony_ci		cifs_dbg(FYI, "connection to disk share\n");
209262306a36Sopenharmony_ci		break;
209362306a36Sopenharmony_ci	case SMB2_SHARE_TYPE_PIPE:
209462306a36Sopenharmony_ci		tcon->pipe = true;
209562306a36Sopenharmony_ci		cifs_dbg(FYI, "connection to pipe share\n");
209662306a36Sopenharmony_ci		break;
209762306a36Sopenharmony_ci	case SMB2_SHARE_TYPE_PRINT:
209862306a36Sopenharmony_ci		tcon->print = true;
209962306a36Sopenharmony_ci		cifs_dbg(FYI, "connection to printer\n");
210062306a36Sopenharmony_ci		break;
210162306a36Sopenharmony_ci	default:
210262306a36Sopenharmony_ci		cifs_server_dbg(VFS, "unknown share type %d\n", rsp->ShareType);
210362306a36Sopenharmony_ci		rc = -EOPNOTSUPP;
210462306a36Sopenharmony_ci		goto tcon_error_exit;
210562306a36Sopenharmony_ci	}
210662306a36Sopenharmony_ci
210762306a36Sopenharmony_ci	tcon->share_flags = le32_to_cpu(rsp->ShareFlags);
210862306a36Sopenharmony_ci	tcon->capabilities = rsp->Capabilities; /* we keep caps little endian */
210962306a36Sopenharmony_ci	tcon->maximal_access = le32_to_cpu(rsp->MaximalAccess);
211062306a36Sopenharmony_ci	tcon->tid = le32_to_cpu(rsp->hdr.Id.SyncId.TreeId);
211162306a36Sopenharmony_ci	strscpy(tcon->tree_name, tree, sizeof(tcon->tree_name));
211262306a36Sopenharmony_ci
211362306a36Sopenharmony_ci	if ((rsp->Capabilities & SMB2_SHARE_CAP_DFS) &&
211462306a36Sopenharmony_ci	    ((tcon->share_flags & SHI1005_FLAGS_DFS) == 0))
211562306a36Sopenharmony_ci		cifs_tcon_dbg(VFS, "DFS capability contradicts DFS flag\n");
211662306a36Sopenharmony_ci
211762306a36Sopenharmony_ci	if (tcon->seal &&
211862306a36Sopenharmony_ci	    !(server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION))
211962306a36Sopenharmony_ci		cifs_tcon_dbg(VFS, "Encryption is requested but not supported\n");
212062306a36Sopenharmony_ci
212162306a36Sopenharmony_ci	init_copy_chunk_defaults(tcon);
212262306a36Sopenharmony_ci	if (server->ops->validate_negotiate)
212362306a36Sopenharmony_ci		rc = server->ops->validate_negotiate(xid, tcon);
212462306a36Sopenharmony_ci	if (rc == 0) /* See MS-SMB2 2.2.10 and 3.2.5.5 */
212562306a36Sopenharmony_ci		if (tcon->share_flags & SMB2_SHAREFLAG_ISOLATED_TRANSPORT)
212662306a36Sopenharmony_ci			server->nosharesock = true;
212762306a36Sopenharmony_citcon_exit:
212862306a36Sopenharmony_ci
212962306a36Sopenharmony_ci	free_rsp_buf(resp_buftype, rsp);
213062306a36Sopenharmony_ci	kfree(unc_path);
213162306a36Sopenharmony_ci	return rc;
213262306a36Sopenharmony_ci
213362306a36Sopenharmony_citcon_error_exit:
213462306a36Sopenharmony_ci	if (rsp && rsp->hdr.Status == STATUS_BAD_NETWORK_NAME)
213562306a36Sopenharmony_ci		cifs_tcon_dbg(VFS, "BAD_NETWORK_NAME: %s\n", tree);
213662306a36Sopenharmony_ci	goto tcon_exit;
213762306a36Sopenharmony_ci}
213862306a36Sopenharmony_ci
213962306a36Sopenharmony_ciint
214062306a36Sopenharmony_ciSMB2_tdis(const unsigned int xid, struct cifs_tcon *tcon)
214162306a36Sopenharmony_ci{
214262306a36Sopenharmony_ci	struct smb_rqst rqst;
214362306a36Sopenharmony_ci	struct smb2_tree_disconnect_req *req; /* response is trivial */
214462306a36Sopenharmony_ci	int rc = 0;
214562306a36Sopenharmony_ci	struct cifs_ses *ses = tcon->ses;
214662306a36Sopenharmony_ci	int flags = 0;
214762306a36Sopenharmony_ci	unsigned int total_len;
214862306a36Sopenharmony_ci	struct kvec iov[1];
214962306a36Sopenharmony_ci	struct kvec rsp_iov;
215062306a36Sopenharmony_ci	int resp_buf_type;
215162306a36Sopenharmony_ci
215262306a36Sopenharmony_ci	cifs_dbg(FYI, "Tree Disconnect\n");
215362306a36Sopenharmony_ci
215462306a36Sopenharmony_ci	if (!ses || !(ses->server))
215562306a36Sopenharmony_ci		return -EIO;
215662306a36Sopenharmony_ci
215762306a36Sopenharmony_ci	trace_smb3_tdis_enter(xid, tcon->tid, ses->Suid, tcon->tree_name);
215862306a36Sopenharmony_ci	spin_lock(&ses->chan_lock);
215962306a36Sopenharmony_ci	if ((tcon->need_reconnect) ||
216062306a36Sopenharmony_ci	    (CIFS_ALL_CHANS_NEED_RECONNECT(tcon->ses))) {
216162306a36Sopenharmony_ci		spin_unlock(&ses->chan_lock);
216262306a36Sopenharmony_ci		return 0;
216362306a36Sopenharmony_ci	}
216462306a36Sopenharmony_ci	spin_unlock(&ses->chan_lock);
216562306a36Sopenharmony_ci
216662306a36Sopenharmony_ci	invalidate_all_cached_dirs(tcon);
216762306a36Sopenharmony_ci
216862306a36Sopenharmony_ci	rc = smb2_plain_req_init(SMB2_TREE_DISCONNECT, tcon, ses->server,
216962306a36Sopenharmony_ci				 (void **) &req,
217062306a36Sopenharmony_ci				 &total_len);
217162306a36Sopenharmony_ci	if (rc)
217262306a36Sopenharmony_ci		return rc;
217362306a36Sopenharmony_ci
217462306a36Sopenharmony_ci	if (smb3_encryption_required(tcon))
217562306a36Sopenharmony_ci		flags |= CIFS_TRANSFORM_REQ;
217662306a36Sopenharmony_ci
217762306a36Sopenharmony_ci	flags |= CIFS_NO_RSP_BUF;
217862306a36Sopenharmony_ci
217962306a36Sopenharmony_ci	iov[0].iov_base = (char *)req;
218062306a36Sopenharmony_ci	iov[0].iov_len = total_len;
218162306a36Sopenharmony_ci
218262306a36Sopenharmony_ci	memset(&rqst, 0, sizeof(struct smb_rqst));
218362306a36Sopenharmony_ci	rqst.rq_iov = iov;
218462306a36Sopenharmony_ci	rqst.rq_nvec = 1;
218562306a36Sopenharmony_ci
218662306a36Sopenharmony_ci	rc = cifs_send_recv(xid, ses, ses->server,
218762306a36Sopenharmony_ci			    &rqst, &resp_buf_type, flags, &rsp_iov);
218862306a36Sopenharmony_ci	cifs_small_buf_release(req);
218962306a36Sopenharmony_ci	if (rc) {
219062306a36Sopenharmony_ci		cifs_stats_fail_inc(tcon, SMB2_TREE_DISCONNECT_HE);
219162306a36Sopenharmony_ci		trace_smb3_tdis_err(xid, tcon->tid, ses->Suid, rc);
219262306a36Sopenharmony_ci	}
219362306a36Sopenharmony_ci	trace_smb3_tdis_done(xid, tcon->tid, ses->Suid);
219462306a36Sopenharmony_ci
219562306a36Sopenharmony_ci	return rc;
219662306a36Sopenharmony_ci}
219762306a36Sopenharmony_ci
219862306a36Sopenharmony_ci
219962306a36Sopenharmony_cistatic struct create_durable *
220062306a36Sopenharmony_cicreate_durable_buf(void)
220162306a36Sopenharmony_ci{
220262306a36Sopenharmony_ci	struct create_durable *buf;
220362306a36Sopenharmony_ci
220462306a36Sopenharmony_ci	buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL);
220562306a36Sopenharmony_ci	if (!buf)
220662306a36Sopenharmony_ci		return NULL;
220762306a36Sopenharmony_ci
220862306a36Sopenharmony_ci	buf->ccontext.DataOffset = cpu_to_le16(offsetof
220962306a36Sopenharmony_ci					(struct create_durable, Data));
221062306a36Sopenharmony_ci	buf->ccontext.DataLength = cpu_to_le32(16);
221162306a36Sopenharmony_ci	buf->ccontext.NameOffset = cpu_to_le16(offsetof
221262306a36Sopenharmony_ci				(struct create_durable, Name));
221362306a36Sopenharmony_ci	buf->ccontext.NameLength = cpu_to_le16(4);
221462306a36Sopenharmony_ci	/* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DHnQ" */
221562306a36Sopenharmony_ci	buf->Name[0] = 'D';
221662306a36Sopenharmony_ci	buf->Name[1] = 'H';
221762306a36Sopenharmony_ci	buf->Name[2] = 'n';
221862306a36Sopenharmony_ci	buf->Name[3] = 'Q';
221962306a36Sopenharmony_ci	return buf;
222062306a36Sopenharmony_ci}
222162306a36Sopenharmony_ci
222262306a36Sopenharmony_cistatic struct create_durable *
222362306a36Sopenharmony_cicreate_reconnect_durable_buf(struct cifs_fid *fid)
222462306a36Sopenharmony_ci{
222562306a36Sopenharmony_ci	struct create_durable *buf;
222662306a36Sopenharmony_ci
222762306a36Sopenharmony_ci	buf = kzalloc(sizeof(struct create_durable), GFP_KERNEL);
222862306a36Sopenharmony_ci	if (!buf)
222962306a36Sopenharmony_ci		return NULL;
223062306a36Sopenharmony_ci
223162306a36Sopenharmony_ci	buf->ccontext.DataOffset = cpu_to_le16(offsetof
223262306a36Sopenharmony_ci					(struct create_durable, Data));
223362306a36Sopenharmony_ci	buf->ccontext.DataLength = cpu_to_le32(16);
223462306a36Sopenharmony_ci	buf->ccontext.NameOffset = cpu_to_le16(offsetof
223562306a36Sopenharmony_ci				(struct create_durable, Name));
223662306a36Sopenharmony_ci	buf->ccontext.NameLength = cpu_to_le16(4);
223762306a36Sopenharmony_ci	buf->Data.Fid.PersistentFileId = fid->persistent_fid;
223862306a36Sopenharmony_ci	buf->Data.Fid.VolatileFileId = fid->volatile_fid;
223962306a36Sopenharmony_ci	/* SMB2_CREATE_DURABLE_HANDLE_RECONNECT is "DHnC" */
224062306a36Sopenharmony_ci	buf->Name[0] = 'D';
224162306a36Sopenharmony_ci	buf->Name[1] = 'H';
224262306a36Sopenharmony_ci	buf->Name[2] = 'n';
224362306a36Sopenharmony_ci	buf->Name[3] = 'C';
224462306a36Sopenharmony_ci	return buf;
224562306a36Sopenharmony_ci}
224662306a36Sopenharmony_ci
224762306a36Sopenharmony_cistatic void
224862306a36Sopenharmony_ciparse_query_id_ctxt(struct create_context *cc, struct smb2_file_all_info *buf)
224962306a36Sopenharmony_ci{
225062306a36Sopenharmony_ci	struct create_disk_id_rsp *pdisk_id = (struct create_disk_id_rsp *)cc;
225162306a36Sopenharmony_ci
225262306a36Sopenharmony_ci	cifs_dbg(FYI, "parse query id context 0x%llx 0x%llx\n",
225362306a36Sopenharmony_ci		pdisk_id->DiskFileId, pdisk_id->VolumeId);
225462306a36Sopenharmony_ci	buf->IndexNumber = pdisk_id->DiskFileId;
225562306a36Sopenharmony_ci}
225662306a36Sopenharmony_ci
225762306a36Sopenharmony_cistatic void
225862306a36Sopenharmony_ciparse_posix_ctxt(struct create_context *cc, struct smb2_file_all_info *info,
225962306a36Sopenharmony_ci		 struct create_posix_rsp *posix)
226062306a36Sopenharmony_ci{
226162306a36Sopenharmony_ci	int sid_len;
226262306a36Sopenharmony_ci	u8 *beg = (u8 *)cc + le16_to_cpu(cc->DataOffset);
226362306a36Sopenharmony_ci	u8 *end = beg + le32_to_cpu(cc->DataLength);
226462306a36Sopenharmony_ci	u8 *sid;
226562306a36Sopenharmony_ci
226662306a36Sopenharmony_ci	memset(posix, 0, sizeof(*posix));
226762306a36Sopenharmony_ci
226862306a36Sopenharmony_ci	posix->nlink = le32_to_cpu(*(__le32 *)(beg + 0));
226962306a36Sopenharmony_ci	posix->reparse_tag = le32_to_cpu(*(__le32 *)(beg + 4));
227062306a36Sopenharmony_ci	posix->mode = le32_to_cpu(*(__le32 *)(beg + 8));
227162306a36Sopenharmony_ci
227262306a36Sopenharmony_ci	sid = beg + 12;
227362306a36Sopenharmony_ci	sid_len = posix_info_sid_size(sid, end);
227462306a36Sopenharmony_ci	if (sid_len < 0) {
227562306a36Sopenharmony_ci		cifs_dbg(VFS, "bad owner sid in posix create response\n");
227662306a36Sopenharmony_ci		return;
227762306a36Sopenharmony_ci	}
227862306a36Sopenharmony_ci	memcpy(&posix->owner, sid, sid_len);
227962306a36Sopenharmony_ci
228062306a36Sopenharmony_ci	sid = sid + sid_len;
228162306a36Sopenharmony_ci	sid_len = posix_info_sid_size(sid, end);
228262306a36Sopenharmony_ci	if (sid_len < 0) {
228362306a36Sopenharmony_ci		cifs_dbg(VFS, "bad group sid in posix create response\n");
228462306a36Sopenharmony_ci		return;
228562306a36Sopenharmony_ci	}
228662306a36Sopenharmony_ci	memcpy(&posix->group, sid, sid_len);
228762306a36Sopenharmony_ci
228862306a36Sopenharmony_ci	cifs_dbg(FYI, "nlink=%d mode=%o reparse_tag=%x\n",
228962306a36Sopenharmony_ci		 posix->nlink, posix->mode, posix->reparse_tag);
229062306a36Sopenharmony_ci}
229162306a36Sopenharmony_ci
229262306a36Sopenharmony_ciint smb2_parse_contexts(struct TCP_Server_Info *server,
229362306a36Sopenharmony_ci			struct kvec *rsp_iov,
229462306a36Sopenharmony_ci			unsigned int *epoch,
229562306a36Sopenharmony_ci			char *lease_key, __u8 *oplock,
229662306a36Sopenharmony_ci			struct smb2_file_all_info *buf,
229762306a36Sopenharmony_ci			struct create_posix_rsp *posix)
229862306a36Sopenharmony_ci{
229962306a36Sopenharmony_ci	struct smb2_create_rsp *rsp = rsp_iov->iov_base;
230062306a36Sopenharmony_ci	struct create_context *cc;
230162306a36Sopenharmony_ci	size_t rem, off, len;
230262306a36Sopenharmony_ci	size_t doff, dlen;
230362306a36Sopenharmony_ci	size_t noff, nlen;
230462306a36Sopenharmony_ci	char *name;
230562306a36Sopenharmony_ci	static const char smb3_create_tag_posix[] = {
230662306a36Sopenharmony_ci		0x93, 0xAD, 0x25, 0x50, 0x9C,
230762306a36Sopenharmony_ci		0xB4, 0x11, 0xE7, 0xB4, 0x23, 0x83,
230862306a36Sopenharmony_ci		0xDE, 0x96, 0x8B, 0xCD, 0x7C
230962306a36Sopenharmony_ci	};
231062306a36Sopenharmony_ci
231162306a36Sopenharmony_ci	*oplock = 0;
231262306a36Sopenharmony_ci
231362306a36Sopenharmony_ci	off = le32_to_cpu(rsp->CreateContextsOffset);
231462306a36Sopenharmony_ci	rem = le32_to_cpu(rsp->CreateContextsLength);
231562306a36Sopenharmony_ci	if (check_add_overflow(off, rem, &len) || len > rsp_iov->iov_len)
231662306a36Sopenharmony_ci		return -EINVAL;
231762306a36Sopenharmony_ci	cc = (struct create_context *)((u8 *)rsp + off);
231862306a36Sopenharmony_ci
231962306a36Sopenharmony_ci	/* Initialize inode number to 0 in case no valid data in qfid context */
232062306a36Sopenharmony_ci	if (buf)
232162306a36Sopenharmony_ci		buf->IndexNumber = 0;
232262306a36Sopenharmony_ci
232362306a36Sopenharmony_ci	while (rem >= sizeof(*cc)) {
232462306a36Sopenharmony_ci		doff = le16_to_cpu(cc->DataOffset);
232562306a36Sopenharmony_ci		dlen = le32_to_cpu(cc->DataLength);
232662306a36Sopenharmony_ci		if (check_add_overflow(doff, dlen, &len) || len > rem)
232762306a36Sopenharmony_ci			return -EINVAL;
232862306a36Sopenharmony_ci
232962306a36Sopenharmony_ci		noff = le16_to_cpu(cc->NameOffset);
233062306a36Sopenharmony_ci		nlen = le16_to_cpu(cc->NameLength);
233162306a36Sopenharmony_ci		if (noff + nlen > doff)
233262306a36Sopenharmony_ci			return -EINVAL;
233362306a36Sopenharmony_ci
233462306a36Sopenharmony_ci		name = (char *)cc + noff;
233562306a36Sopenharmony_ci		switch (nlen) {
233662306a36Sopenharmony_ci		case 4:
233762306a36Sopenharmony_ci			if (!strncmp(name, SMB2_CREATE_REQUEST_LEASE, 4)) {
233862306a36Sopenharmony_ci				*oplock = server->ops->parse_lease_buf(cc, epoch,
233962306a36Sopenharmony_ci								       lease_key);
234062306a36Sopenharmony_ci			} else if (buf &&
234162306a36Sopenharmony_ci				   !strncmp(name, SMB2_CREATE_QUERY_ON_DISK_ID, 4)) {
234262306a36Sopenharmony_ci				parse_query_id_ctxt(cc, buf);
234362306a36Sopenharmony_ci			}
234462306a36Sopenharmony_ci			break;
234562306a36Sopenharmony_ci		case 16:
234662306a36Sopenharmony_ci			if (posix && !memcmp(name, smb3_create_tag_posix, 16))
234762306a36Sopenharmony_ci				parse_posix_ctxt(cc, buf, posix);
234862306a36Sopenharmony_ci			break;
234962306a36Sopenharmony_ci		default:
235062306a36Sopenharmony_ci			cifs_dbg(FYI, "%s: unhandled context (nlen=%zu dlen=%zu)\n",
235162306a36Sopenharmony_ci				 __func__, nlen, dlen);
235262306a36Sopenharmony_ci			if (IS_ENABLED(CONFIG_CIFS_DEBUG2))
235362306a36Sopenharmony_ci				cifs_dump_mem("context data: ", cc, dlen);
235462306a36Sopenharmony_ci			break;
235562306a36Sopenharmony_ci		}
235662306a36Sopenharmony_ci
235762306a36Sopenharmony_ci		off = le32_to_cpu(cc->Next);
235862306a36Sopenharmony_ci		if (!off)
235962306a36Sopenharmony_ci			break;
236062306a36Sopenharmony_ci		if (check_sub_overflow(rem, off, &rem))
236162306a36Sopenharmony_ci			return -EINVAL;
236262306a36Sopenharmony_ci		cc = (struct create_context *)((u8 *)cc + off);
236362306a36Sopenharmony_ci	}
236462306a36Sopenharmony_ci
236562306a36Sopenharmony_ci	if (rsp->OplockLevel != SMB2_OPLOCK_LEVEL_LEASE)
236662306a36Sopenharmony_ci		*oplock = rsp->OplockLevel;
236762306a36Sopenharmony_ci
236862306a36Sopenharmony_ci	return 0;
236962306a36Sopenharmony_ci}
237062306a36Sopenharmony_ci
237162306a36Sopenharmony_cistatic int
237262306a36Sopenharmony_ciadd_lease_context(struct TCP_Server_Info *server,
237362306a36Sopenharmony_ci		  struct smb2_create_req *req,
237462306a36Sopenharmony_ci		  struct kvec *iov,
237562306a36Sopenharmony_ci		  unsigned int *num_iovec, u8 *lease_key, __u8 *oplock)
237662306a36Sopenharmony_ci{
237762306a36Sopenharmony_ci	unsigned int num = *num_iovec;
237862306a36Sopenharmony_ci
237962306a36Sopenharmony_ci	iov[num].iov_base = server->ops->create_lease_buf(lease_key, *oplock);
238062306a36Sopenharmony_ci	if (iov[num].iov_base == NULL)
238162306a36Sopenharmony_ci		return -ENOMEM;
238262306a36Sopenharmony_ci	iov[num].iov_len = server->vals->create_lease_size;
238362306a36Sopenharmony_ci	req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_LEASE;
238462306a36Sopenharmony_ci	*num_iovec = num + 1;
238562306a36Sopenharmony_ci	return 0;
238662306a36Sopenharmony_ci}
238762306a36Sopenharmony_ci
238862306a36Sopenharmony_cistatic struct create_durable_v2 *
238962306a36Sopenharmony_cicreate_durable_v2_buf(struct cifs_open_parms *oparms)
239062306a36Sopenharmony_ci{
239162306a36Sopenharmony_ci	struct cifs_fid *pfid = oparms->fid;
239262306a36Sopenharmony_ci	struct create_durable_v2 *buf;
239362306a36Sopenharmony_ci
239462306a36Sopenharmony_ci	buf = kzalloc(sizeof(struct create_durable_v2), GFP_KERNEL);
239562306a36Sopenharmony_ci	if (!buf)
239662306a36Sopenharmony_ci		return NULL;
239762306a36Sopenharmony_ci
239862306a36Sopenharmony_ci	buf->ccontext.DataOffset = cpu_to_le16(offsetof
239962306a36Sopenharmony_ci					(struct create_durable_v2, dcontext));
240062306a36Sopenharmony_ci	buf->ccontext.DataLength = cpu_to_le32(sizeof(struct durable_context_v2));
240162306a36Sopenharmony_ci	buf->ccontext.NameOffset = cpu_to_le16(offsetof
240262306a36Sopenharmony_ci				(struct create_durable_v2, Name));
240362306a36Sopenharmony_ci	buf->ccontext.NameLength = cpu_to_le16(4);
240462306a36Sopenharmony_ci
240562306a36Sopenharmony_ci	/*
240662306a36Sopenharmony_ci	 * NB: Handle timeout defaults to 0, which allows server to choose
240762306a36Sopenharmony_ci	 * (most servers default to 120 seconds) and most clients default to 0.
240862306a36Sopenharmony_ci	 * This can be overridden at mount ("handletimeout=") if the user wants
240962306a36Sopenharmony_ci	 * a different persistent (or resilient) handle timeout for all opens
241062306a36Sopenharmony_ci	 * on a particular SMB3 mount.
241162306a36Sopenharmony_ci	 */
241262306a36Sopenharmony_ci	buf->dcontext.Timeout = cpu_to_le32(oparms->tcon->handle_timeout);
241362306a36Sopenharmony_ci	buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT);
241462306a36Sopenharmony_ci	generate_random_uuid(buf->dcontext.CreateGuid);
241562306a36Sopenharmony_ci	memcpy(pfid->create_guid, buf->dcontext.CreateGuid, 16);
241662306a36Sopenharmony_ci
241762306a36Sopenharmony_ci	/* SMB2_CREATE_DURABLE_HANDLE_REQUEST is "DH2Q" */
241862306a36Sopenharmony_ci	buf->Name[0] = 'D';
241962306a36Sopenharmony_ci	buf->Name[1] = 'H';
242062306a36Sopenharmony_ci	buf->Name[2] = '2';
242162306a36Sopenharmony_ci	buf->Name[3] = 'Q';
242262306a36Sopenharmony_ci	return buf;
242362306a36Sopenharmony_ci}
242462306a36Sopenharmony_ci
242562306a36Sopenharmony_cistatic struct create_durable_handle_reconnect_v2 *
242662306a36Sopenharmony_cicreate_reconnect_durable_v2_buf(struct cifs_fid *fid)
242762306a36Sopenharmony_ci{
242862306a36Sopenharmony_ci	struct create_durable_handle_reconnect_v2 *buf;
242962306a36Sopenharmony_ci
243062306a36Sopenharmony_ci	buf = kzalloc(sizeof(struct create_durable_handle_reconnect_v2),
243162306a36Sopenharmony_ci			GFP_KERNEL);
243262306a36Sopenharmony_ci	if (!buf)
243362306a36Sopenharmony_ci		return NULL;
243462306a36Sopenharmony_ci
243562306a36Sopenharmony_ci	buf->ccontext.DataOffset =
243662306a36Sopenharmony_ci		cpu_to_le16(offsetof(struct create_durable_handle_reconnect_v2,
243762306a36Sopenharmony_ci				     dcontext));
243862306a36Sopenharmony_ci	buf->ccontext.DataLength =
243962306a36Sopenharmony_ci		cpu_to_le32(sizeof(struct durable_reconnect_context_v2));
244062306a36Sopenharmony_ci	buf->ccontext.NameOffset =
244162306a36Sopenharmony_ci		cpu_to_le16(offsetof(struct create_durable_handle_reconnect_v2,
244262306a36Sopenharmony_ci			    Name));
244362306a36Sopenharmony_ci	buf->ccontext.NameLength = cpu_to_le16(4);
244462306a36Sopenharmony_ci
244562306a36Sopenharmony_ci	buf->dcontext.Fid.PersistentFileId = fid->persistent_fid;
244662306a36Sopenharmony_ci	buf->dcontext.Fid.VolatileFileId = fid->volatile_fid;
244762306a36Sopenharmony_ci	buf->dcontext.Flags = cpu_to_le32(SMB2_DHANDLE_FLAG_PERSISTENT);
244862306a36Sopenharmony_ci	memcpy(buf->dcontext.CreateGuid, fid->create_guid, 16);
244962306a36Sopenharmony_ci
245062306a36Sopenharmony_ci	/* SMB2_CREATE_DURABLE_HANDLE_RECONNECT_V2 is "DH2C" */
245162306a36Sopenharmony_ci	buf->Name[0] = 'D';
245262306a36Sopenharmony_ci	buf->Name[1] = 'H';
245362306a36Sopenharmony_ci	buf->Name[2] = '2';
245462306a36Sopenharmony_ci	buf->Name[3] = 'C';
245562306a36Sopenharmony_ci	return buf;
245662306a36Sopenharmony_ci}
245762306a36Sopenharmony_ci
245862306a36Sopenharmony_cistatic int
245962306a36Sopenharmony_ciadd_durable_v2_context(struct kvec *iov, unsigned int *num_iovec,
246062306a36Sopenharmony_ci		    struct cifs_open_parms *oparms)
246162306a36Sopenharmony_ci{
246262306a36Sopenharmony_ci	unsigned int num = *num_iovec;
246362306a36Sopenharmony_ci
246462306a36Sopenharmony_ci	iov[num].iov_base = create_durable_v2_buf(oparms);
246562306a36Sopenharmony_ci	if (iov[num].iov_base == NULL)
246662306a36Sopenharmony_ci		return -ENOMEM;
246762306a36Sopenharmony_ci	iov[num].iov_len = sizeof(struct create_durable_v2);
246862306a36Sopenharmony_ci	*num_iovec = num + 1;
246962306a36Sopenharmony_ci	return 0;
247062306a36Sopenharmony_ci}
247162306a36Sopenharmony_ci
247262306a36Sopenharmony_cistatic int
247362306a36Sopenharmony_ciadd_durable_reconnect_v2_context(struct kvec *iov, unsigned int *num_iovec,
247462306a36Sopenharmony_ci		    struct cifs_open_parms *oparms)
247562306a36Sopenharmony_ci{
247662306a36Sopenharmony_ci	unsigned int num = *num_iovec;
247762306a36Sopenharmony_ci
247862306a36Sopenharmony_ci	/* indicate that we don't need to relock the file */
247962306a36Sopenharmony_ci	oparms->reconnect = false;
248062306a36Sopenharmony_ci
248162306a36Sopenharmony_ci	iov[num].iov_base = create_reconnect_durable_v2_buf(oparms->fid);
248262306a36Sopenharmony_ci	if (iov[num].iov_base == NULL)
248362306a36Sopenharmony_ci		return -ENOMEM;
248462306a36Sopenharmony_ci	iov[num].iov_len = sizeof(struct create_durable_handle_reconnect_v2);
248562306a36Sopenharmony_ci	*num_iovec = num + 1;
248662306a36Sopenharmony_ci	return 0;
248762306a36Sopenharmony_ci}
248862306a36Sopenharmony_ci
248962306a36Sopenharmony_cistatic int
249062306a36Sopenharmony_ciadd_durable_context(struct kvec *iov, unsigned int *num_iovec,
249162306a36Sopenharmony_ci		    struct cifs_open_parms *oparms, bool use_persistent)
249262306a36Sopenharmony_ci{
249362306a36Sopenharmony_ci	unsigned int num = *num_iovec;
249462306a36Sopenharmony_ci
249562306a36Sopenharmony_ci	if (use_persistent) {
249662306a36Sopenharmony_ci		if (oparms->reconnect)
249762306a36Sopenharmony_ci			return add_durable_reconnect_v2_context(iov, num_iovec,
249862306a36Sopenharmony_ci								oparms);
249962306a36Sopenharmony_ci		else
250062306a36Sopenharmony_ci			return add_durable_v2_context(iov, num_iovec, oparms);
250162306a36Sopenharmony_ci	}
250262306a36Sopenharmony_ci
250362306a36Sopenharmony_ci	if (oparms->reconnect) {
250462306a36Sopenharmony_ci		iov[num].iov_base = create_reconnect_durable_buf(oparms->fid);
250562306a36Sopenharmony_ci		/* indicate that we don't need to relock the file */
250662306a36Sopenharmony_ci		oparms->reconnect = false;
250762306a36Sopenharmony_ci	} else
250862306a36Sopenharmony_ci		iov[num].iov_base = create_durable_buf();
250962306a36Sopenharmony_ci	if (iov[num].iov_base == NULL)
251062306a36Sopenharmony_ci		return -ENOMEM;
251162306a36Sopenharmony_ci	iov[num].iov_len = sizeof(struct create_durable);
251262306a36Sopenharmony_ci	*num_iovec = num + 1;
251362306a36Sopenharmony_ci	return 0;
251462306a36Sopenharmony_ci}
251562306a36Sopenharmony_ci
251662306a36Sopenharmony_ci/* See MS-SMB2 2.2.13.2.7 */
251762306a36Sopenharmony_cistatic struct crt_twarp_ctxt *
251862306a36Sopenharmony_cicreate_twarp_buf(__u64 timewarp)
251962306a36Sopenharmony_ci{
252062306a36Sopenharmony_ci	struct crt_twarp_ctxt *buf;
252162306a36Sopenharmony_ci
252262306a36Sopenharmony_ci	buf = kzalloc(sizeof(struct crt_twarp_ctxt), GFP_KERNEL);
252362306a36Sopenharmony_ci	if (!buf)
252462306a36Sopenharmony_ci		return NULL;
252562306a36Sopenharmony_ci
252662306a36Sopenharmony_ci	buf->ccontext.DataOffset = cpu_to_le16(offsetof
252762306a36Sopenharmony_ci					(struct crt_twarp_ctxt, Timestamp));
252862306a36Sopenharmony_ci	buf->ccontext.DataLength = cpu_to_le32(8);
252962306a36Sopenharmony_ci	buf->ccontext.NameOffset = cpu_to_le16(offsetof
253062306a36Sopenharmony_ci				(struct crt_twarp_ctxt, Name));
253162306a36Sopenharmony_ci	buf->ccontext.NameLength = cpu_to_le16(4);
253262306a36Sopenharmony_ci	/* SMB2_CREATE_TIMEWARP_TOKEN is "TWrp" */
253362306a36Sopenharmony_ci	buf->Name[0] = 'T';
253462306a36Sopenharmony_ci	buf->Name[1] = 'W';
253562306a36Sopenharmony_ci	buf->Name[2] = 'r';
253662306a36Sopenharmony_ci	buf->Name[3] = 'p';
253762306a36Sopenharmony_ci	buf->Timestamp = cpu_to_le64(timewarp);
253862306a36Sopenharmony_ci	return buf;
253962306a36Sopenharmony_ci}
254062306a36Sopenharmony_ci
254162306a36Sopenharmony_ci/* See MS-SMB2 2.2.13.2.7 */
254262306a36Sopenharmony_cistatic int
254362306a36Sopenharmony_ciadd_twarp_context(struct kvec *iov, unsigned int *num_iovec, __u64 timewarp)
254462306a36Sopenharmony_ci{
254562306a36Sopenharmony_ci	unsigned int num = *num_iovec;
254662306a36Sopenharmony_ci
254762306a36Sopenharmony_ci	iov[num].iov_base = create_twarp_buf(timewarp);
254862306a36Sopenharmony_ci	if (iov[num].iov_base == NULL)
254962306a36Sopenharmony_ci		return -ENOMEM;
255062306a36Sopenharmony_ci	iov[num].iov_len = sizeof(struct crt_twarp_ctxt);
255162306a36Sopenharmony_ci	*num_iovec = num + 1;
255262306a36Sopenharmony_ci	return 0;
255362306a36Sopenharmony_ci}
255462306a36Sopenharmony_ci
255562306a36Sopenharmony_ci/* See http://technet.microsoft.com/en-us/library/hh509017(v=ws.10).aspx */
255662306a36Sopenharmony_cistatic void setup_owner_group_sids(char *buf)
255762306a36Sopenharmony_ci{
255862306a36Sopenharmony_ci	struct owner_group_sids *sids = (struct owner_group_sids *)buf;
255962306a36Sopenharmony_ci
256062306a36Sopenharmony_ci	/* Populate the user ownership fields S-1-5-88-1 */
256162306a36Sopenharmony_ci	sids->owner.Revision = 1;
256262306a36Sopenharmony_ci	sids->owner.NumAuth = 3;
256362306a36Sopenharmony_ci	sids->owner.Authority[5] = 5;
256462306a36Sopenharmony_ci	sids->owner.SubAuthorities[0] = cpu_to_le32(88);
256562306a36Sopenharmony_ci	sids->owner.SubAuthorities[1] = cpu_to_le32(1);
256662306a36Sopenharmony_ci	sids->owner.SubAuthorities[2] = cpu_to_le32(current_fsuid().val);
256762306a36Sopenharmony_ci
256862306a36Sopenharmony_ci	/* Populate the group ownership fields S-1-5-88-2 */
256962306a36Sopenharmony_ci	sids->group.Revision = 1;
257062306a36Sopenharmony_ci	sids->group.NumAuth = 3;
257162306a36Sopenharmony_ci	sids->group.Authority[5] = 5;
257262306a36Sopenharmony_ci	sids->group.SubAuthorities[0] = cpu_to_le32(88);
257362306a36Sopenharmony_ci	sids->group.SubAuthorities[1] = cpu_to_le32(2);
257462306a36Sopenharmony_ci	sids->group.SubAuthorities[2] = cpu_to_le32(current_fsgid().val);
257562306a36Sopenharmony_ci
257662306a36Sopenharmony_ci	cifs_dbg(FYI, "owner S-1-5-88-1-%d, group S-1-5-88-2-%d\n", current_fsuid().val, current_fsgid().val);
257762306a36Sopenharmony_ci}
257862306a36Sopenharmony_ci
257962306a36Sopenharmony_ci/* See MS-SMB2 2.2.13.2.2 and MS-DTYP 2.4.6 */
258062306a36Sopenharmony_cistatic struct crt_sd_ctxt *
258162306a36Sopenharmony_cicreate_sd_buf(umode_t mode, bool set_owner, unsigned int *len)
258262306a36Sopenharmony_ci{
258362306a36Sopenharmony_ci	struct crt_sd_ctxt *buf;
258462306a36Sopenharmony_ci	__u8 *ptr, *aclptr;
258562306a36Sopenharmony_ci	unsigned int acelen, acl_size, ace_count;
258662306a36Sopenharmony_ci	unsigned int owner_offset = 0;
258762306a36Sopenharmony_ci	unsigned int group_offset = 0;
258862306a36Sopenharmony_ci	struct smb3_acl acl = {};
258962306a36Sopenharmony_ci
259062306a36Sopenharmony_ci	*len = round_up(sizeof(struct crt_sd_ctxt) + (sizeof(struct cifs_ace) * 4), 8);
259162306a36Sopenharmony_ci
259262306a36Sopenharmony_ci	if (set_owner) {
259362306a36Sopenharmony_ci		/* sizeof(struct owner_group_sids) is already multiple of 8 so no need to round */
259462306a36Sopenharmony_ci		*len += sizeof(struct owner_group_sids);
259562306a36Sopenharmony_ci	}
259662306a36Sopenharmony_ci
259762306a36Sopenharmony_ci	buf = kzalloc(*len, GFP_KERNEL);
259862306a36Sopenharmony_ci	if (buf == NULL)
259962306a36Sopenharmony_ci		return buf;
260062306a36Sopenharmony_ci
260162306a36Sopenharmony_ci	ptr = (__u8 *)&buf[1];
260262306a36Sopenharmony_ci	if (set_owner) {
260362306a36Sopenharmony_ci		/* offset fields are from beginning of security descriptor not of create context */
260462306a36Sopenharmony_ci		owner_offset = ptr - (__u8 *)&buf->sd;
260562306a36Sopenharmony_ci		buf->sd.OffsetOwner = cpu_to_le32(owner_offset);
260662306a36Sopenharmony_ci		group_offset = owner_offset + offsetof(struct owner_group_sids, group);
260762306a36Sopenharmony_ci		buf->sd.OffsetGroup = cpu_to_le32(group_offset);
260862306a36Sopenharmony_ci
260962306a36Sopenharmony_ci		setup_owner_group_sids(ptr);
261062306a36Sopenharmony_ci		ptr += sizeof(struct owner_group_sids);
261162306a36Sopenharmony_ci	} else {
261262306a36Sopenharmony_ci		buf->sd.OffsetOwner = 0;
261362306a36Sopenharmony_ci		buf->sd.OffsetGroup = 0;
261462306a36Sopenharmony_ci	}
261562306a36Sopenharmony_ci
261662306a36Sopenharmony_ci	buf->ccontext.DataOffset = cpu_to_le16(offsetof(struct crt_sd_ctxt, sd));
261762306a36Sopenharmony_ci	buf->ccontext.NameOffset = cpu_to_le16(offsetof(struct crt_sd_ctxt, Name));
261862306a36Sopenharmony_ci	buf->ccontext.NameLength = cpu_to_le16(4);
261962306a36Sopenharmony_ci	/* SMB2_CREATE_SD_BUFFER_TOKEN is "SecD" */
262062306a36Sopenharmony_ci	buf->Name[0] = 'S';
262162306a36Sopenharmony_ci	buf->Name[1] = 'e';
262262306a36Sopenharmony_ci	buf->Name[2] = 'c';
262362306a36Sopenharmony_ci	buf->Name[3] = 'D';
262462306a36Sopenharmony_ci	buf->sd.Revision = 1;  /* Must be one see MS-DTYP 2.4.6 */
262562306a36Sopenharmony_ci
262662306a36Sopenharmony_ci	/*
262762306a36Sopenharmony_ci	 * ACL is "self relative" ie ACL is stored in contiguous block of memory
262862306a36Sopenharmony_ci	 * and "DP" ie the DACL is present
262962306a36Sopenharmony_ci	 */
263062306a36Sopenharmony_ci	buf->sd.Control = cpu_to_le16(ACL_CONTROL_SR | ACL_CONTROL_DP);
263162306a36Sopenharmony_ci
263262306a36Sopenharmony_ci	/* offset owner, group and Sbz1 and SACL are all zero */
263362306a36Sopenharmony_ci	buf->sd.OffsetDacl = cpu_to_le32(ptr - (__u8 *)&buf->sd);
263462306a36Sopenharmony_ci	/* Ship the ACL for now. we will copy it into buf later. */
263562306a36Sopenharmony_ci	aclptr = ptr;
263662306a36Sopenharmony_ci	ptr += sizeof(struct smb3_acl);
263762306a36Sopenharmony_ci
263862306a36Sopenharmony_ci	/* create one ACE to hold the mode embedded in reserved special SID */
263962306a36Sopenharmony_ci	acelen = setup_special_mode_ACE((struct cifs_ace *)ptr, (__u64)mode);
264062306a36Sopenharmony_ci	ptr += acelen;
264162306a36Sopenharmony_ci	acl_size = acelen + sizeof(struct smb3_acl);
264262306a36Sopenharmony_ci	ace_count = 1;
264362306a36Sopenharmony_ci
264462306a36Sopenharmony_ci	if (set_owner) {
264562306a36Sopenharmony_ci		/* we do not need to reallocate buffer to add the two more ACEs. plenty of space */
264662306a36Sopenharmony_ci		acelen = setup_special_user_owner_ACE((struct cifs_ace *)ptr);
264762306a36Sopenharmony_ci		ptr += acelen;
264862306a36Sopenharmony_ci		acl_size += acelen;
264962306a36Sopenharmony_ci		ace_count += 1;
265062306a36Sopenharmony_ci	}
265162306a36Sopenharmony_ci
265262306a36Sopenharmony_ci	/* and one more ACE to allow access for authenticated users */
265362306a36Sopenharmony_ci	acelen = setup_authusers_ACE((struct cifs_ace *)ptr);
265462306a36Sopenharmony_ci	ptr += acelen;
265562306a36Sopenharmony_ci	acl_size += acelen;
265662306a36Sopenharmony_ci	ace_count += 1;
265762306a36Sopenharmony_ci
265862306a36Sopenharmony_ci	acl.AclRevision = ACL_REVISION; /* See 2.4.4.1 of MS-DTYP */
265962306a36Sopenharmony_ci	acl.AclSize = cpu_to_le16(acl_size);
266062306a36Sopenharmony_ci	acl.AceCount = cpu_to_le16(ace_count);
266162306a36Sopenharmony_ci	/* acl.Sbz1 and Sbz2 MBZ so are not set here, but initialized above */
266262306a36Sopenharmony_ci	memcpy(aclptr, &acl, sizeof(struct smb3_acl));
266362306a36Sopenharmony_ci
266462306a36Sopenharmony_ci	buf->ccontext.DataLength = cpu_to_le32(ptr - (__u8 *)&buf->sd);
266562306a36Sopenharmony_ci	*len = round_up((unsigned int)(ptr - (__u8 *)buf), 8);
266662306a36Sopenharmony_ci
266762306a36Sopenharmony_ci	return buf;
266862306a36Sopenharmony_ci}
266962306a36Sopenharmony_ci
267062306a36Sopenharmony_cistatic int
267162306a36Sopenharmony_ciadd_sd_context(struct kvec *iov, unsigned int *num_iovec, umode_t mode, bool set_owner)
267262306a36Sopenharmony_ci{
267362306a36Sopenharmony_ci	unsigned int num = *num_iovec;
267462306a36Sopenharmony_ci	unsigned int len = 0;
267562306a36Sopenharmony_ci
267662306a36Sopenharmony_ci	iov[num].iov_base = create_sd_buf(mode, set_owner, &len);
267762306a36Sopenharmony_ci	if (iov[num].iov_base == NULL)
267862306a36Sopenharmony_ci		return -ENOMEM;
267962306a36Sopenharmony_ci	iov[num].iov_len = len;
268062306a36Sopenharmony_ci	*num_iovec = num + 1;
268162306a36Sopenharmony_ci	return 0;
268262306a36Sopenharmony_ci}
268362306a36Sopenharmony_ci
268462306a36Sopenharmony_cistatic struct crt_query_id_ctxt *
268562306a36Sopenharmony_cicreate_query_id_buf(void)
268662306a36Sopenharmony_ci{
268762306a36Sopenharmony_ci	struct crt_query_id_ctxt *buf;
268862306a36Sopenharmony_ci
268962306a36Sopenharmony_ci	buf = kzalloc(sizeof(struct crt_query_id_ctxt), GFP_KERNEL);
269062306a36Sopenharmony_ci	if (!buf)
269162306a36Sopenharmony_ci		return NULL;
269262306a36Sopenharmony_ci
269362306a36Sopenharmony_ci	buf->ccontext.DataOffset = cpu_to_le16(0);
269462306a36Sopenharmony_ci	buf->ccontext.DataLength = cpu_to_le32(0);
269562306a36Sopenharmony_ci	buf->ccontext.NameOffset = cpu_to_le16(offsetof
269662306a36Sopenharmony_ci				(struct crt_query_id_ctxt, Name));
269762306a36Sopenharmony_ci	buf->ccontext.NameLength = cpu_to_le16(4);
269862306a36Sopenharmony_ci	/* SMB2_CREATE_QUERY_ON_DISK_ID is "QFid" */
269962306a36Sopenharmony_ci	buf->Name[0] = 'Q';
270062306a36Sopenharmony_ci	buf->Name[1] = 'F';
270162306a36Sopenharmony_ci	buf->Name[2] = 'i';
270262306a36Sopenharmony_ci	buf->Name[3] = 'd';
270362306a36Sopenharmony_ci	return buf;
270462306a36Sopenharmony_ci}
270562306a36Sopenharmony_ci
270662306a36Sopenharmony_ci/* See MS-SMB2 2.2.13.2.9 */
270762306a36Sopenharmony_cistatic int
270862306a36Sopenharmony_ciadd_query_id_context(struct kvec *iov, unsigned int *num_iovec)
270962306a36Sopenharmony_ci{
271062306a36Sopenharmony_ci	unsigned int num = *num_iovec;
271162306a36Sopenharmony_ci
271262306a36Sopenharmony_ci	iov[num].iov_base = create_query_id_buf();
271362306a36Sopenharmony_ci	if (iov[num].iov_base == NULL)
271462306a36Sopenharmony_ci		return -ENOMEM;
271562306a36Sopenharmony_ci	iov[num].iov_len = sizeof(struct crt_query_id_ctxt);
271662306a36Sopenharmony_ci	*num_iovec = num + 1;
271762306a36Sopenharmony_ci	return 0;
271862306a36Sopenharmony_ci}
271962306a36Sopenharmony_ci
272062306a36Sopenharmony_cistatic int
272162306a36Sopenharmony_cialloc_path_with_tree_prefix(__le16 **out_path, int *out_size, int *out_len,
272262306a36Sopenharmony_ci			    const char *treename, const __le16 *path)
272362306a36Sopenharmony_ci{
272462306a36Sopenharmony_ci	int treename_len, path_len;
272562306a36Sopenharmony_ci	struct nls_table *cp;
272662306a36Sopenharmony_ci	const __le16 sep[] = {cpu_to_le16('\\'), cpu_to_le16(0x0000)};
272762306a36Sopenharmony_ci
272862306a36Sopenharmony_ci	/*
272962306a36Sopenharmony_ci	 * skip leading "\\"
273062306a36Sopenharmony_ci	 */
273162306a36Sopenharmony_ci	treename_len = strlen(treename);
273262306a36Sopenharmony_ci	if (treename_len < 2 || !(treename[0] == '\\' && treename[1] == '\\'))
273362306a36Sopenharmony_ci		return -EINVAL;
273462306a36Sopenharmony_ci
273562306a36Sopenharmony_ci	treename += 2;
273662306a36Sopenharmony_ci	treename_len -= 2;
273762306a36Sopenharmony_ci
273862306a36Sopenharmony_ci	path_len = UniStrnlen((wchar_t *)path, PATH_MAX);
273962306a36Sopenharmony_ci
274062306a36Sopenharmony_ci	/* make room for one path separator only if @path isn't empty */
274162306a36Sopenharmony_ci	*out_len = treename_len + (path[0] ? 1 : 0) + path_len;
274262306a36Sopenharmony_ci
274362306a36Sopenharmony_ci	/*
274462306a36Sopenharmony_ci	 * final path needs to be 8-byte aligned as specified in
274562306a36Sopenharmony_ci	 * MS-SMB2 2.2.13 SMB2 CREATE Request.
274662306a36Sopenharmony_ci	 */
274762306a36Sopenharmony_ci	*out_size = round_up(*out_len * sizeof(__le16), 8);
274862306a36Sopenharmony_ci	*out_path = kzalloc(*out_size + sizeof(__le16) /* null */, GFP_KERNEL);
274962306a36Sopenharmony_ci	if (!*out_path)
275062306a36Sopenharmony_ci		return -ENOMEM;
275162306a36Sopenharmony_ci
275262306a36Sopenharmony_ci	cp = load_nls_default();
275362306a36Sopenharmony_ci	cifs_strtoUTF16(*out_path, treename, treename_len, cp);
275462306a36Sopenharmony_ci
275562306a36Sopenharmony_ci	/* Do not append the separator if the path is empty */
275662306a36Sopenharmony_ci	if (path[0] != cpu_to_le16(0x0000)) {
275762306a36Sopenharmony_ci		UniStrcat((wchar_t *)*out_path, (wchar_t *)sep);
275862306a36Sopenharmony_ci		UniStrcat((wchar_t *)*out_path, (wchar_t *)path);
275962306a36Sopenharmony_ci	}
276062306a36Sopenharmony_ci
276162306a36Sopenharmony_ci	unload_nls(cp);
276262306a36Sopenharmony_ci
276362306a36Sopenharmony_ci	return 0;
276462306a36Sopenharmony_ci}
276562306a36Sopenharmony_ci
276662306a36Sopenharmony_ciint smb311_posix_mkdir(const unsigned int xid, struct inode *inode,
276762306a36Sopenharmony_ci			       umode_t mode, struct cifs_tcon *tcon,
276862306a36Sopenharmony_ci			       const char *full_path,
276962306a36Sopenharmony_ci			       struct cifs_sb_info *cifs_sb)
277062306a36Sopenharmony_ci{
277162306a36Sopenharmony_ci	struct smb_rqst rqst;
277262306a36Sopenharmony_ci	struct smb2_create_req *req;
277362306a36Sopenharmony_ci	struct smb2_create_rsp *rsp = NULL;
277462306a36Sopenharmony_ci	struct cifs_ses *ses = tcon->ses;
277562306a36Sopenharmony_ci	struct kvec iov[3]; /* make sure at least one for each open context */
277662306a36Sopenharmony_ci	struct kvec rsp_iov = {NULL, 0};
277762306a36Sopenharmony_ci	int resp_buftype;
277862306a36Sopenharmony_ci	int uni_path_len;
277962306a36Sopenharmony_ci	__le16 *copy_path = NULL;
278062306a36Sopenharmony_ci	int copy_size;
278162306a36Sopenharmony_ci	int rc = 0;
278262306a36Sopenharmony_ci	unsigned int n_iov = 2;
278362306a36Sopenharmony_ci	__u32 file_attributes = 0;
278462306a36Sopenharmony_ci	char *pc_buf = NULL;
278562306a36Sopenharmony_ci	int flags = 0;
278662306a36Sopenharmony_ci	unsigned int total_len;
278762306a36Sopenharmony_ci	__le16 *utf16_path = NULL;
278862306a36Sopenharmony_ci	struct TCP_Server_Info *server = cifs_pick_channel(ses);
278962306a36Sopenharmony_ci
279062306a36Sopenharmony_ci	cifs_dbg(FYI, "mkdir\n");
279162306a36Sopenharmony_ci
279262306a36Sopenharmony_ci	/* resource #1: path allocation */
279362306a36Sopenharmony_ci	utf16_path = cifs_convert_path_to_utf16(full_path, cifs_sb);
279462306a36Sopenharmony_ci	if (!utf16_path)
279562306a36Sopenharmony_ci		return -ENOMEM;
279662306a36Sopenharmony_ci
279762306a36Sopenharmony_ci	if (!ses || !server) {
279862306a36Sopenharmony_ci		rc = -EIO;
279962306a36Sopenharmony_ci		goto err_free_path;
280062306a36Sopenharmony_ci	}
280162306a36Sopenharmony_ci
280262306a36Sopenharmony_ci	/* resource #2: request */
280362306a36Sopenharmony_ci	rc = smb2_plain_req_init(SMB2_CREATE, tcon, server,
280462306a36Sopenharmony_ci				 (void **) &req, &total_len);
280562306a36Sopenharmony_ci	if (rc)
280662306a36Sopenharmony_ci		goto err_free_path;
280762306a36Sopenharmony_ci
280862306a36Sopenharmony_ci
280962306a36Sopenharmony_ci	if (smb3_encryption_required(tcon))
281062306a36Sopenharmony_ci		flags |= CIFS_TRANSFORM_REQ;
281162306a36Sopenharmony_ci
281262306a36Sopenharmony_ci	req->ImpersonationLevel = IL_IMPERSONATION;
281362306a36Sopenharmony_ci	req->DesiredAccess = cpu_to_le32(FILE_WRITE_ATTRIBUTES);
281462306a36Sopenharmony_ci	/* File attributes ignored on open (used in create though) */
281562306a36Sopenharmony_ci	req->FileAttributes = cpu_to_le32(file_attributes);
281662306a36Sopenharmony_ci	req->ShareAccess = FILE_SHARE_ALL_LE;
281762306a36Sopenharmony_ci	req->CreateDisposition = cpu_to_le32(FILE_CREATE);
281862306a36Sopenharmony_ci	req->CreateOptions = cpu_to_le32(CREATE_NOT_FILE);
281962306a36Sopenharmony_ci
282062306a36Sopenharmony_ci	iov[0].iov_base = (char *)req;
282162306a36Sopenharmony_ci	/* -1 since last byte is buf[0] which is sent below (path) */
282262306a36Sopenharmony_ci	iov[0].iov_len = total_len - 1;
282362306a36Sopenharmony_ci
282462306a36Sopenharmony_ci	req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req));
282562306a36Sopenharmony_ci
282662306a36Sopenharmony_ci	/* [MS-SMB2] 2.2.13 NameOffset:
282762306a36Sopenharmony_ci	 * If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of
282862306a36Sopenharmony_ci	 * the SMB2 header, the file name includes a prefix that will
282962306a36Sopenharmony_ci	 * be processed during DFS name normalization as specified in
283062306a36Sopenharmony_ci	 * section 3.3.5.9. Otherwise, the file name is relative to
283162306a36Sopenharmony_ci	 * the share that is identified by the TreeId in the SMB2
283262306a36Sopenharmony_ci	 * header.
283362306a36Sopenharmony_ci	 */
283462306a36Sopenharmony_ci	if (tcon->share_flags & SHI1005_FLAGS_DFS) {
283562306a36Sopenharmony_ci		int name_len;
283662306a36Sopenharmony_ci
283762306a36Sopenharmony_ci		req->hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS;
283862306a36Sopenharmony_ci		rc = alloc_path_with_tree_prefix(&copy_path, &copy_size,
283962306a36Sopenharmony_ci						 &name_len,
284062306a36Sopenharmony_ci						 tcon->tree_name, utf16_path);
284162306a36Sopenharmony_ci		if (rc)
284262306a36Sopenharmony_ci			goto err_free_req;
284362306a36Sopenharmony_ci
284462306a36Sopenharmony_ci		req->NameLength = cpu_to_le16(name_len * 2);
284562306a36Sopenharmony_ci		uni_path_len = copy_size;
284662306a36Sopenharmony_ci		/* free before overwriting resource */
284762306a36Sopenharmony_ci		kfree(utf16_path);
284862306a36Sopenharmony_ci		utf16_path = copy_path;
284962306a36Sopenharmony_ci	} else {
285062306a36Sopenharmony_ci		uni_path_len = (2 * UniStrnlen((wchar_t *)utf16_path, PATH_MAX)) + 2;
285162306a36Sopenharmony_ci		/* MUST set path len (NameLength) to 0 opening root of share */
285262306a36Sopenharmony_ci		req->NameLength = cpu_to_le16(uni_path_len - 2);
285362306a36Sopenharmony_ci		if (uni_path_len % 8 != 0) {
285462306a36Sopenharmony_ci			copy_size = roundup(uni_path_len, 8);
285562306a36Sopenharmony_ci			copy_path = kzalloc(copy_size, GFP_KERNEL);
285662306a36Sopenharmony_ci			if (!copy_path) {
285762306a36Sopenharmony_ci				rc = -ENOMEM;
285862306a36Sopenharmony_ci				goto err_free_req;
285962306a36Sopenharmony_ci			}
286062306a36Sopenharmony_ci			memcpy((char *)copy_path, (const char *)utf16_path,
286162306a36Sopenharmony_ci			       uni_path_len);
286262306a36Sopenharmony_ci			uni_path_len = copy_size;
286362306a36Sopenharmony_ci			/* free before overwriting resource */
286462306a36Sopenharmony_ci			kfree(utf16_path);
286562306a36Sopenharmony_ci			utf16_path = copy_path;
286662306a36Sopenharmony_ci		}
286762306a36Sopenharmony_ci	}
286862306a36Sopenharmony_ci
286962306a36Sopenharmony_ci	iov[1].iov_len = uni_path_len;
287062306a36Sopenharmony_ci	iov[1].iov_base = utf16_path;
287162306a36Sopenharmony_ci	req->RequestedOplockLevel = SMB2_OPLOCK_LEVEL_NONE;
287262306a36Sopenharmony_ci
287362306a36Sopenharmony_ci	if (tcon->posix_extensions) {
287462306a36Sopenharmony_ci		/* resource #3: posix buf */
287562306a36Sopenharmony_ci		rc = add_posix_context(iov, &n_iov, mode);
287662306a36Sopenharmony_ci		if (rc)
287762306a36Sopenharmony_ci			goto err_free_req;
287862306a36Sopenharmony_ci		req->CreateContextsOffset = cpu_to_le32(
287962306a36Sopenharmony_ci			sizeof(struct smb2_create_req) +
288062306a36Sopenharmony_ci			iov[1].iov_len);
288162306a36Sopenharmony_ci		pc_buf = iov[n_iov-1].iov_base;
288262306a36Sopenharmony_ci	}
288362306a36Sopenharmony_ci
288462306a36Sopenharmony_ci
288562306a36Sopenharmony_ci	memset(&rqst, 0, sizeof(struct smb_rqst));
288662306a36Sopenharmony_ci	rqst.rq_iov = iov;
288762306a36Sopenharmony_ci	rqst.rq_nvec = n_iov;
288862306a36Sopenharmony_ci
288962306a36Sopenharmony_ci	/* no need to inc num_remote_opens because we close it just below */
289062306a36Sopenharmony_ci	trace_smb3_posix_mkdir_enter(xid, tcon->tid, ses->Suid, full_path, CREATE_NOT_FILE,
289162306a36Sopenharmony_ci				    FILE_WRITE_ATTRIBUTES);
289262306a36Sopenharmony_ci	/* resource #4: response buffer */
289362306a36Sopenharmony_ci	rc = cifs_send_recv(xid, ses, server,
289462306a36Sopenharmony_ci			    &rqst, &resp_buftype, flags, &rsp_iov);
289562306a36Sopenharmony_ci	if (rc) {
289662306a36Sopenharmony_ci		cifs_stats_fail_inc(tcon, SMB2_CREATE_HE);
289762306a36Sopenharmony_ci		trace_smb3_posix_mkdir_err(xid, tcon->tid, ses->Suid,
289862306a36Sopenharmony_ci					   CREATE_NOT_FILE,
289962306a36Sopenharmony_ci					   FILE_WRITE_ATTRIBUTES, rc);
290062306a36Sopenharmony_ci		goto err_free_rsp_buf;
290162306a36Sopenharmony_ci	}
290262306a36Sopenharmony_ci
290362306a36Sopenharmony_ci	/*
290462306a36Sopenharmony_ci	 * Although unlikely to be possible for rsp to be null and rc not set,
290562306a36Sopenharmony_ci	 * adding check below is slightly safer long term (and quiets Coverity
290662306a36Sopenharmony_ci	 * warning)
290762306a36Sopenharmony_ci	 */
290862306a36Sopenharmony_ci	rsp = (struct smb2_create_rsp *)rsp_iov.iov_base;
290962306a36Sopenharmony_ci	if (rsp == NULL) {
291062306a36Sopenharmony_ci		rc = -EIO;
291162306a36Sopenharmony_ci		kfree(pc_buf);
291262306a36Sopenharmony_ci		goto err_free_req;
291362306a36Sopenharmony_ci	}
291462306a36Sopenharmony_ci
291562306a36Sopenharmony_ci	trace_smb3_posix_mkdir_done(xid, rsp->PersistentFileId, tcon->tid, ses->Suid,
291662306a36Sopenharmony_ci				    CREATE_NOT_FILE, FILE_WRITE_ATTRIBUTES);
291762306a36Sopenharmony_ci
291862306a36Sopenharmony_ci	SMB2_close(xid, tcon, rsp->PersistentFileId, rsp->VolatileFileId);
291962306a36Sopenharmony_ci
292062306a36Sopenharmony_ci	/* Eventually save off posix specific response info and timestaps */
292162306a36Sopenharmony_ci
292262306a36Sopenharmony_cierr_free_rsp_buf:
292362306a36Sopenharmony_ci	free_rsp_buf(resp_buftype, rsp);
292462306a36Sopenharmony_ci	kfree(pc_buf);
292562306a36Sopenharmony_cierr_free_req:
292662306a36Sopenharmony_ci	cifs_small_buf_release(req);
292762306a36Sopenharmony_cierr_free_path:
292862306a36Sopenharmony_ci	kfree(utf16_path);
292962306a36Sopenharmony_ci	return rc;
293062306a36Sopenharmony_ci}
293162306a36Sopenharmony_ci
293262306a36Sopenharmony_ciint
293362306a36Sopenharmony_ciSMB2_open_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,
293462306a36Sopenharmony_ci	       struct smb_rqst *rqst, __u8 *oplock,
293562306a36Sopenharmony_ci	       struct cifs_open_parms *oparms, __le16 *path)
293662306a36Sopenharmony_ci{
293762306a36Sopenharmony_ci	struct smb2_create_req *req;
293862306a36Sopenharmony_ci	unsigned int n_iov = 2;
293962306a36Sopenharmony_ci	__u32 file_attributes = 0;
294062306a36Sopenharmony_ci	int copy_size;
294162306a36Sopenharmony_ci	int uni_path_len;
294262306a36Sopenharmony_ci	unsigned int total_len;
294362306a36Sopenharmony_ci	struct kvec *iov = rqst->rq_iov;
294462306a36Sopenharmony_ci	__le16 *copy_path;
294562306a36Sopenharmony_ci	int rc;
294662306a36Sopenharmony_ci
294762306a36Sopenharmony_ci	rc = smb2_plain_req_init(SMB2_CREATE, tcon, server,
294862306a36Sopenharmony_ci				 (void **) &req, &total_len);
294962306a36Sopenharmony_ci	if (rc)
295062306a36Sopenharmony_ci		return rc;
295162306a36Sopenharmony_ci
295262306a36Sopenharmony_ci	iov[0].iov_base = (char *)req;
295362306a36Sopenharmony_ci	/* -1 since last byte is buf[0] which is sent below (path) */
295462306a36Sopenharmony_ci	iov[0].iov_len = total_len - 1;
295562306a36Sopenharmony_ci
295662306a36Sopenharmony_ci	if (oparms->create_options & CREATE_OPTION_READONLY)
295762306a36Sopenharmony_ci		file_attributes |= ATTR_READONLY;
295862306a36Sopenharmony_ci	if (oparms->create_options & CREATE_OPTION_SPECIAL)
295962306a36Sopenharmony_ci		file_attributes |= ATTR_SYSTEM;
296062306a36Sopenharmony_ci
296162306a36Sopenharmony_ci	req->ImpersonationLevel = IL_IMPERSONATION;
296262306a36Sopenharmony_ci	req->DesiredAccess = cpu_to_le32(oparms->desired_access);
296362306a36Sopenharmony_ci	/* File attributes ignored on open (used in create though) */
296462306a36Sopenharmony_ci	req->FileAttributes = cpu_to_le32(file_attributes);
296562306a36Sopenharmony_ci	req->ShareAccess = FILE_SHARE_ALL_LE;
296662306a36Sopenharmony_ci
296762306a36Sopenharmony_ci	req->CreateDisposition = cpu_to_le32(oparms->disposition);
296862306a36Sopenharmony_ci	req->CreateOptions = cpu_to_le32(oparms->create_options & CREATE_OPTIONS_MASK);
296962306a36Sopenharmony_ci	req->NameOffset = cpu_to_le16(sizeof(struct smb2_create_req));
297062306a36Sopenharmony_ci
297162306a36Sopenharmony_ci	/* [MS-SMB2] 2.2.13 NameOffset:
297262306a36Sopenharmony_ci	 * If SMB2_FLAGS_DFS_OPERATIONS is set in the Flags field of
297362306a36Sopenharmony_ci	 * the SMB2 header, the file name includes a prefix that will
297462306a36Sopenharmony_ci	 * be processed during DFS name normalization as specified in
297562306a36Sopenharmony_ci	 * section 3.3.5.9. Otherwise, the file name is relative to
297662306a36Sopenharmony_ci	 * the share that is identified by the TreeId in the SMB2
297762306a36Sopenharmony_ci	 * header.
297862306a36Sopenharmony_ci	 */
297962306a36Sopenharmony_ci	if (tcon->share_flags & SHI1005_FLAGS_DFS) {
298062306a36Sopenharmony_ci		int name_len;
298162306a36Sopenharmony_ci
298262306a36Sopenharmony_ci		req->hdr.Flags |= SMB2_FLAGS_DFS_OPERATIONS;
298362306a36Sopenharmony_ci		rc = alloc_path_with_tree_prefix(&copy_path, &copy_size,
298462306a36Sopenharmony_ci						 &name_len,
298562306a36Sopenharmony_ci						 tcon->tree_name, path);
298662306a36Sopenharmony_ci		if (rc)
298762306a36Sopenharmony_ci			return rc;
298862306a36Sopenharmony_ci		req->NameLength = cpu_to_le16(name_len * 2);
298962306a36Sopenharmony_ci		uni_path_len = copy_size;
299062306a36Sopenharmony_ci		path = copy_path;
299162306a36Sopenharmony_ci	} else {
299262306a36Sopenharmony_ci		uni_path_len = (2 * UniStrnlen((wchar_t *)path, PATH_MAX)) + 2;
299362306a36Sopenharmony_ci		/* MUST set path len (NameLength) to 0 opening root of share */
299462306a36Sopenharmony_ci		req->NameLength = cpu_to_le16(uni_path_len - 2);
299562306a36Sopenharmony_ci		copy_size = round_up(uni_path_len, 8);
299662306a36Sopenharmony_ci		copy_path = kzalloc(copy_size, GFP_KERNEL);
299762306a36Sopenharmony_ci		if (!copy_path)
299862306a36Sopenharmony_ci			return -ENOMEM;
299962306a36Sopenharmony_ci		memcpy((char *)copy_path, (const char *)path,
300062306a36Sopenharmony_ci		       uni_path_len);
300162306a36Sopenharmony_ci		uni_path_len = copy_size;
300262306a36Sopenharmony_ci		path = copy_path;
300362306a36Sopenharmony_ci	}
300462306a36Sopenharmony_ci
300562306a36Sopenharmony_ci	iov[1].iov_len = uni_path_len;
300662306a36Sopenharmony_ci	iov[1].iov_base = path;
300762306a36Sopenharmony_ci
300862306a36Sopenharmony_ci	if ((!server->oplocks) || (tcon->no_lease))
300962306a36Sopenharmony_ci		*oplock = SMB2_OPLOCK_LEVEL_NONE;
301062306a36Sopenharmony_ci
301162306a36Sopenharmony_ci	if (!(server->capabilities & SMB2_GLOBAL_CAP_LEASING) ||
301262306a36Sopenharmony_ci	    *oplock == SMB2_OPLOCK_LEVEL_NONE)
301362306a36Sopenharmony_ci		req->RequestedOplockLevel = *oplock;
301462306a36Sopenharmony_ci	else if (!(server->capabilities & SMB2_GLOBAL_CAP_DIRECTORY_LEASING) &&
301562306a36Sopenharmony_ci		  (oparms->create_options & CREATE_NOT_FILE))
301662306a36Sopenharmony_ci		req->RequestedOplockLevel = *oplock; /* no srv lease support */
301762306a36Sopenharmony_ci	else {
301862306a36Sopenharmony_ci		rc = add_lease_context(server, req, iov, &n_iov,
301962306a36Sopenharmony_ci				       oparms->fid->lease_key, oplock);
302062306a36Sopenharmony_ci		if (rc)
302162306a36Sopenharmony_ci			return rc;
302262306a36Sopenharmony_ci	}
302362306a36Sopenharmony_ci
302462306a36Sopenharmony_ci	if (*oplock == SMB2_OPLOCK_LEVEL_BATCH) {
302562306a36Sopenharmony_ci		rc = add_durable_context(iov, &n_iov, oparms,
302662306a36Sopenharmony_ci					tcon->use_persistent);
302762306a36Sopenharmony_ci		if (rc)
302862306a36Sopenharmony_ci			return rc;
302962306a36Sopenharmony_ci	}
303062306a36Sopenharmony_ci
303162306a36Sopenharmony_ci	if (tcon->posix_extensions) {
303262306a36Sopenharmony_ci		rc = add_posix_context(iov, &n_iov, oparms->mode);
303362306a36Sopenharmony_ci		if (rc)
303462306a36Sopenharmony_ci			return rc;
303562306a36Sopenharmony_ci	}
303662306a36Sopenharmony_ci
303762306a36Sopenharmony_ci	if (tcon->snapshot_time) {
303862306a36Sopenharmony_ci		cifs_dbg(FYI, "adding snapshot context\n");
303962306a36Sopenharmony_ci		rc = add_twarp_context(iov, &n_iov, tcon->snapshot_time);
304062306a36Sopenharmony_ci		if (rc)
304162306a36Sopenharmony_ci			return rc;
304262306a36Sopenharmony_ci	}
304362306a36Sopenharmony_ci
304462306a36Sopenharmony_ci	if ((oparms->disposition != FILE_OPEN) && (oparms->cifs_sb)) {
304562306a36Sopenharmony_ci		bool set_mode;
304662306a36Sopenharmony_ci		bool set_owner;
304762306a36Sopenharmony_ci
304862306a36Sopenharmony_ci		if ((oparms->cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MODE_FROM_SID) &&
304962306a36Sopenharmony_ci		    (oparms->mode != ACL_NO_MODE))
305062306a36Sopenharmony_ci			set_mode = true;
305162306a36Sopenharmony_ci		else {
305262306a36Sopenharmony_ci			set_mode = false;
305362306a36Sopenharmony_ci			oparms->mode = ACL_NO_MODE;
305462306a36Sopenharmony_ci		}
305562306a36Sopenharmony_ci
305662306a36Sopenharmony_ci		if (oparms->cifs_sb->mnt_cifs_flags & CIFS_MOUNT_UID_FROM_ACL)
305762306a36Sopenharmony_ci			set_owner = true;
305862306a36Sopenharmony_ci		else
305962306a36Sopenharmony_ci			set_owner = false;
306062306a36Sopenharmony_ci
306162306a36Sopenharmony_ci		if (set_owner | set_mode) {
306262306a36Sopenharmony_ci			cifs_dbg(FYI, "add sd with mode 0x%x\n", oparms->mode);
306362306a36Sopenharmony_ci			rc = add_sd_context(iov, &n_iov, oparms->mode, set_owner);
306462306a36Sopenharmony_ci			if (rc)
306562306a36Sopenharmony_ci				return rc;
306662306a36Sopenharmony_ci		}
306762306a36Sopenharmony_ci	}
306862306a36Sopenharmony_ci
306962306a36Sopenharmony_ci	add_query_id_context(iov, &n_iov);
307062306a36Sopenharmony_ci
307162306a36Sopenharmony_ci	if (n_iov > 2) {
307262306a36Sopenharmony_ci		/*
307362306a36Sopenharmony_ci		 * We have create contexts behind iov[1] (the file
307462306a36Sopenharmony_ci		 * name), point at them from the main create request
307562306a36Sopenharmony_ci		 */
307662306a36Sopenharmony_ci		req->CreateContextsOffset = cpu_to_le32(
307762306a36Sopenharmony_ci			sizeof(struct smb2_create_req) +
307862306a36Sopenharmony_ci			iov[1].iov_len);
307962306a36Sopenharmony_ci		req->CreateContextsLength = 0;
308062306a36Sopenharmony_ci
308162306a36Sopenharmony_ci		for (unsigned int i = 2; i < (n_iov-1); i++) {
308262306a36Sopenharmony_ci			struct kvec *v = &iov[i];
308362306a36Sopenharmony_ci			size_t len = v->iov_len;
308462306a36Sopenharmony_ci			struct create_context *cctx =
308562306a36Sopenharmony_ci				(struct create_context *)v->iov_base;
308662306a36Sopenharmony_ci
308762306a36Sopenharmony_ci			cctx->Next = cpu_to_le32(len);
308862306a36Sopenharmony_ci			le32_add_cpu(&req->CreateContextsLength, len);
308962306a36Sopenharmony_ci		}
309062306a36Sopenharmony_ci		le32_add_cpu(&req->CreateContextsLength,
309162306a36Sopenharmony_ci			     iov[n_iov-1].iov_len);
309262306a36Sopenharmony_ci	}
309362306a36Sopenharmony_ci
309462306a36Sopenharmony_ci	rqst->rq_nvec = n_iov;
309562306a36Sopenharmony_ci	return 0;
309662306a36Sopenharmony_ci}
309762306a36Sopenharmony_ci
309862306a36Sopenharmony_ci/* rq_iov[0] is the request and is released by cifs_small_buf_release().
309962306a36Sopenharmony_ci * All other vectors are freed by kfree().
310062306a36Sopenharmony_ci */
310162306a36Sopenharmony_civoid
310262306a36Sopenharmony_ciSMB2_open_free(struct smb_rqst *rqst)
310362306a36Sopenharmony_ci{
310462306a36Sopenharmony_ci	int i;
310562306a36Sopenharmony_ci
310662306a36Sopenharmony_ci	if (rqst && rqst->rq_iov) {
310762306a36Sopenharmony_ci		cifs_small_buf_release(rqst->rq_iov[0].iov_base);
310862306a36Sopenharmony_ci		for (i = 1; i < rqst->rq_nvec; i++)
310962306a36Sopenharmony_ci			if (rqst->rq_iov[i].iov_base != smb2_padding)
311062306a36Sopenharmony_ci				kfree(rqst->rq_iov[i].iov_base);
311162306a36Sopenharmony_ci	}
311262306a36Sopenharmony_ci}
311362306a36Sopenharmony_ci
311462306a36Sopenharmony_ciint
311562306a36Sopenharmony_ciSMB2_open(const unsigned int xid, struct cifs_open_parms *oparms, __le16 *path,
311662306a36Sopenharmony_ci	  __u8 *oplock, struct smb2_file_all_info *buf,
311762306a36Sopenharmony_ci	  struct create_posix_rsp *posix,
311862306a36Sopenharmony_ci	  struct kvec *err_iov, int *buftype)
311962306a36Sopenharmony_ci{
312062306a36Sopenharmony_ci	struct smb_rqst rqst;
312162306a36Sopenharmony_ci	struct smb2_create_rsp *rsp = NULL;
312262306a36Sopenharmony_ci	struct cifs_tcon *tcon = oparms->tcon;
312362306a36Sopenharmony_ci	struct cifs_ses *ses = tcon->ses;
312462306a36Sopenharmony_ci	struct TCP_Server_Info *server = cifs_pick_channel(ses);
312562306a36Sopenharmony_ci	struct kvec iov[SMB2_CREATE_IOV_SIZE];
312662306a36Sopenharmony_ci	struct kvec rsp_iov = {NULL, 0};
312762306a36Sopenharmony_ci	int resp_buftype = CIFS_NO_BUFFER;
312862306a36Sopenharmony_ci	int rc = 0;
312962306a36Sopenharmony_ci	int flags = 0;
313062306a36Sopenharmony_ci
313162306a36Sopenharmony_ci	cifs_dbg(FYI, "create/open\n");
313262306a36Sopenharmony_ci	if (!ses || !server)
313362306a36Sopenharmony_ci		return -EIO;
313462306a36Sopenharmony_ci
313562306a36Sopenharmony_ci	if (smb3_encryption_required(tcon))
313662306a36Sopenharmony_ci		flags |= CIFS_TRANSFORM_REQ;
313762306a36Sopenharmony_ci
313862306a36Sopenharmony_ci	memset(&rqst, 0, sizeof(struct smb_rqst));
313962306a36Sopenharmony_ci	memset(&iov, 0, sizeof(iov));
314062306a36Sopenharmony_ci	rqst.rq_iov = iov;
314162306a36Sopenharmony_ci	rqst.rq_nvec = SMB2_CREATE_IOV_SIZE;
314262306a36Sopenharmony_ci
314362306a36Sopenharmony_ci	rc = SMB2_open_init(tcon, server,
314462306a36Sopenharmony_ci			    &rqst, oplock, oparms, path);
314562306a36Sopenharmony_ci	if (rc)
314662306a36Sopenharmony_ci		goto creat_exit;
314762306a36Sopenharmony_ci
314862306a36Sopenharmony_ci	trace_smb3_open_enter(xid, tcon->tid, tcon->ses->Suid, oparms->path,
314962306a36Sopenharmony_ci		oparms->create_options, oparms->desired_access);
315062306a36Sopenharmony_ci
315162306a36Sopenharmony_ci	rc = cifs_send_recv(xid, ses, server,
315262306a36Sopenharmony_ci			    &rqst, &resp_buftype, flags,
315362306a36Sopenharmony_ci			    &rsp_iov);
315462306a36Sopenharmony_ci	rsp = (struct smb2_create_rsp *)rsp_iov.iov_base;
315562306a36Sopenharmony_ci
315662306a36Sopenharmony_ci	if (rc != 0) {
315762306a36Sopenharmony_ci		cifs_stats_fail_inc(tcon, SMB2_CREATE_HE);
315862306a36Sopenharmony_ci		if (err_iov && rsp) {
315962306a36Sopenharmony_ci			*err_iov = rsp_iov;
316062306a36Sopenharmony_ci			*buftype = resp_buftype;
316162306a36Sopenharmony_ci			resp_buftype = CIFS_NO_BUFFER;
316262306a36Sopenharmony_ci			rsp = NULL;
316362306a36Sopenharmony_ci		}
316462306a36Sopenharmony_ci		trace_smb3_open_err(xid, tcon->tid, ses->Suid,
316562306a36Sopenharmony_ci				    oparms->create_options, oparms->desired_access, rc);
316662306a36Sopenharmony_ci		if (rc == -EREMCHG) {
316762306a36Sopenharmony_ci			pr_warn_once("server share %s deleted\n",
316862306a36Sopenharmony_ci				     tcon->tree_name);
316962306a36Sopenharmony_ci			tcon->need_reconnect = true;
317062306a36Sopenharmony_ci		}
317162306a36Sopenharmony_ci		goto creat_exit;
317262306a36Sopenharmony_ci	} else if (rsp == NULL) /* unlikely to happen, but safer to check */
317362306a36Sopenharmony_ci		goto creat_exit;
317462306a36Sopenharmony_ci	else
317562306a36Sopenharmony_ci		trace_smb3_open_done(xid, rsp->PersistentFileId, tcon->tid, ses->Suid,
317662306a36Sopenharmony_ci				     oparms->create_options, oparms->desired_access);
317762306a36Sopenharmony_ci
317862306a36Sopenharmony_ci	atomic_inc(&tcon->num_remote_opens);
317962306a36Sopenharmony_ci	oparms->fid->persistent_fid = rsp->PersistentFileId;
318062306a36Sopenharmony_ci	oparms->fid->volatile_fid = rsp->VolatileFileId;
318162306a36Sopenharmony_ci	oparms->fid->access = oparms->desired_access;
318262306a36Sopenharmony_ci#ifdef CONFIG_CIFS_DEBUG2
318362306a36Sopenharmony_ci	oparms->fid->mid = le64_to_cpu(rsp->hdr.MessageId);
318462306a36Sopenharmony_ci#endif /* CIFS_DEBUG2 */
318562306a36Sopenharmony_ci
318662306a36Sopenharmony_ci	if (buf) {
318762306a36Sopenharmony_ci		buf->CreationTime = rsp->CreationTime;
318862306a36Sopenharmony_ci		buf->LastAccessTime = rsp->LastAccessTime;
318962306a36Sopenharmony_ci		buf->LastWriteTime = rsp->LastWriteTime;
319062306a36Sopenharmony_ci		buf->ChangeTime = rsp->ChangeTime;
319162306a36Sopenharmony_ci		buf->AllocationSize = rsp->AllocationSize;
319262306a36Sopenharmony_ci		buf->EndOfFile = rsp->EndofFile;
319362306a36Sopenharmony_ci		buf->Attributes = rsp->FileAttributes;
319462306a36Sopenharmony_ci		buf->NumberOfLinks = cpu_to_le32(1);
319562306a36Sopenharmony_ci		buf->DeletePending = 0;
319662306a36Sopenharmony_ci	}
319762306a36Sopenharmony_ci
319862306a36Sopenharmony_ci
319962306a36Sopenharmony_ci	rc = smb2_parse_contexts(server, &rsp_iov, &oparms->fid->epoch,
320062306a36Sopenharmony_ci				 oparms->fid->lease_key, oplock, buf, posix);
320162306a36Sopenharmony_cicreat_exit:
320262306a36Sopenharmony_ci	SMB2_open_free(&rqst);
320362306a36Sopenharmony_ci	free_rsp_buf(resp_buftype, rsp);
320462306a36Sopenharmony_ci	return rc;
320562306a36Sopenharmony_ci}
320662306a36Sopenharmony_ci
320762306a36Sopenharmony_ciint
320862306a36Sopenharmony_ciSMB2_ioctl_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,
320962306a36Sopenharmony_ci		struct smb_rqst *rqst,
321062306a36Sopenharmony_ci		u64 persistent_fid, u64 volatile_fid, u32 opcode,
321162306a36Sopenharmony_ci		char *in_data, u32 indatalen,
321262306a36Sopenharmony_ci		__u32 max_response_size)
321362306a36Sopenharmony_ci{
321462306a36Sopenharmony_ci	struct smb2_ioctl_req *req;
321562306a36Sopenharmony_ci	struct kvec *iov = rqst->rq_iov;
321662306a36Sopenharmony_ci	unsigned int total_len;
321762306a36Sopenharmony_ci	int rc;
321862306a36Sopenharmony_ci	char *in_data_buf;
321962306a36Sopenharmony_ci
322062306a36Sopenharmony_ci	rc = smb2_ioctl_req_init(opcode, tcon, server,
322162306a36Sopenharmony_ci				 (void **) &req, &total_len);
322262306a36Sopenharmony_ci	if (rc)
322362306a36Sopenharmony_ci		return rc;
322462306a36Sopenharmony_ci
322562306a36Sopenharmony_ci	if (indatalen) {
322662306a36Sopenharmony_ci		/*
322762306a36Sopenharmony_ci		 * indatalen is usually small at a couple of bytes max, so
322862306a36Sopenharmony_ci		 * just allocate through generic pool
322962306a36Sopenharmony_ci		 */
323062306a36Sopenharmony_ci		in_data_buf = kmemdup(in_data, indatalen, GFP_NOFS);
323162306a36Sopenharmony_ci		if (!in_data_buf) {
323262306a36Sopenharmony_ci			cifs_small_buf_release(req);
323362306a36Sopenharmony_ci			return -ENOMEM;
323462306a36Sopenharmony_ci		}
323562306a36Sopenharmony_ci	}
323662306a36Sopenharmony_ci
323762306a36Sopenharmony_ci	req->CtlCode = cpu_to_le32(opcode);
323862306a36Sopenharmony_ci	req->PersistentFileId = persistent_fid;
323962306a36Sopenharmony_ci	req->VolatileFileId = volatile_fid;
324062306a36Sopenharmony_ci
324162306a36Sopenharmony_ci	iov[0].iov_base = (char *)req;
324262306a36Sopenharmony_ci	/*
324362306a36Sopenharmony_ci	 * If no input data, the size of ioctl struct in
324462306a36Sopenharmony_ci	 * protocol spec still includes a 1 byte data buffer,
324562306a36Sopenharmony_ci	 * but if input data passed to ioctl, we do not
324662306a36Sopenharmony_ci	 * want to double count this, so we do not send
324762306a36Sopenharmony_ci	 * the dummy one byte of data in iovec[0] if sending
324862306a36Sopenharmony_ci	 * input data (in iovec[1]).
324962306a36Sopenharmony_ci	 */
325062306a36Sopenharmony_ci	if (indatalen) {
325162306a36Sopenharmony_ci		req->InputCount = cpu_to_le32(indatalen);
325262306a36Sopenharmony_ci		/* do not set InputOffset if no input data */
325362306a36Sopenharmony_ci		req->InputOffset =
325462306a36Sopenharmony_ci		       cpu_to_le32(offsetof(struct smb2_ioctl_req, Buffer));
325562306a36Sopenharmony_ci		rqst->rq_nvec = 2;
325662306a36Sopenharmony_ci		iov[0].iov_len = total_len - 1;
325762306a36Sopenharmony_ci		iov[1].iov_base = in_data_buf;
325862306a36Sopenharmony_ci		iov[1].iov_len = indatalen;
325962306a36Sopenharmony_ci	} else {
326062306a36Sopenharmony_ci		rqst->rq_nvec = 1;
326162306a36Sopenharmony_ci		iov[0].iov_len = total_len;
326262306a36Sopenharmony_ci	}
326362306a36Sopenharmony_ci
326462306a36Sopenharmony_ci	req->OutputOffset = 0;
326562306a36Sopenharmony_ci	req->OutputCount = 0; /* MBZ */
326662306a36Sopenharmony_ci
326762306a36Sopenharmony_ci	/*
326862306a36Sopenharmony_ci	 * In most cases max_response_size is set to 16K (CIFSMaxBufSize)
326962306a36Sopenharmony_ci	 * We Could increase default MaxOutputResponse, but that could require
327062306a36Sopenharmony_ci	 * more credits. Windows typically sets this smaller, but for some
327162306a36Sopenharmony_ci	 * ioctls it may be useful to allow server to send more. No point
327262306a36Sopenharmony_ci	 * limiting what the server can send as long as fits in one credit
327362306a36Sopenharmony_ci	 * We can not handle more than CIFS_MAX_BUF_SIZE yet but may want
327462306a36Sopenharmony_ci	 * to increase this limit up in the future.
327562306a36Sopenharmony_ci	 * Note that for snapshot queries that servers like Azure expect that
327662306a36Sopenharmony_ci	 * the first query be minimal size (and just used to get the number/size
327762306a36Sopenharmony_ci	 * of previous versions) so response size must be specified as EXACTLY
327862306a36Sopenharmony_ci	 * sizeof(struct snapshot_array) which is 16 when rounded up to multiple
327962306a36Sopenharmony_ci	 * of eight bytes.  Currently that is the only case where we set max
328062306a36Sopenharmony_ci	 * response size smaller.
328162306a36Sopenharmony_ci	 */
328262306a36Sopenharmony_ci	req->MaxOutputResponse = cpu_to_le32(max_response_size);
328362306a36Sopenharmony_ci	req->hdr.CreditCharge =
328462306a36Sopenharmony_ci		cpu_to_le16(DIV_ROUND_UP(max(indatalen, max_response_size),
328562306a36Sopenharmony_ci					 SMB2_MAX_BUFFER_SIZE));
328662306a36Sopenharmony_ci	/* always an FSCTL (for now) */
328762306a36Sopenharmony_ci	req->Flags = cpu_to_le32(SMB2_0_IOCTL_IS_FSCTL);
328862306a36Sopenharmony_ci
328962306a36Sopenharmony_ci	/* validate negotiate request must be signed - see MS-SMB2 3.2.5.5 */
329062306a36Sopenharmony_ci	if (opcode == FSCTL_VALIDATE_NEGOTIATE_INFO)
329162306a36Sopenharmony_ci		req->hdr.Flags |= SMB2_FLAGS_SIGNED;
329262306a36Sopenharmony_ci
329362306a36Sopenharmony_ci	return 0;
329462306a36Sopenharmony_ci}
329562306a36Sopenharmony_ci
329662306a36Sopenharmony_civoid
329762306a36Sopenharmony_ciSMB2_ioctl_free(struct smb_rqst *rqst)
329862306a36Sopenharmony_ci{
329962306a36Sopenharmony_ci	int i;
330062306a36Sopenharmony_ci
330162306a36Sopenharmony_ci	if (rqst && rqst->rq_iov) {
330262306a36Sopenharmony_ci		cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */
330362306a36Sopenharmony_ci		for (i = 1; i < rqst->rq_nvec; i++)
330462306a36Sopenharmony_ci			if (rqst->rq_iov[i].iov_base != smb2_padding)
330562306a36Sopenharmony_ci				kfree(rqst->rq_iov[i].iov_base);
330662306a36Sopenharmony_ci	}
330762306a36Sopenharmony_ci}
330862306a36Sopenharmony_ci
330962306a36Sopenharmony_ci
331062306a36Sopenharmony_ci/*
331162306a36Sopenharmony_ci *	SMB2 IOCTL is used for both IOCTLs and FSCTLs
331262306a36Sopenharmony_ci */
331362306a36Sopenharmony_ciint
331462306a36Sopenharmony_ciSMB2_ioctl(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
331562306a36Sopenharmony_ci	   u64 volatile_fid, u32 opcode, char *in_data, u32 indatalen,
331662306a36Sopenharmony_ci	   u32 max_out_data_len, char **out_data,
331762306a36Sopenharmony_ci	   u32 *plen /* returned data len */)
331862306a36Sopenharmony_ci{
331962306a36Sopenharmony_ci	struct smb_rqst rqst;
332062306a36Sopenharmony_ci	struct smb2_ioctl_rsp *rsp = NULL;
332162306a36Sopenharmony_ci	struct cifs_ses *ses;
332262306a36Sopenharmony_ci	struct TCP_Server_Info *server;
332362306a36Sopenharmony_ci	struct kvec iov[SMB2_IOCTL_IOV_SIZE];
332462306a36Sopenharmony_ci	struct kvec rsp_iov = {NULL, 0};
332562306a36Sopenharmony_ci	int resp_buftype = CIFS_NO_BUFFER;
332662306a36Sopenharmony_ci	int rc = 0;
332762306a36Sopenharmony_ci	int flags = 0;
332862306a36Sopenharmony_ci
332962306a36Sopenharmony_ci	cifs_dbg(FYI, "SMB2 IOCTL\n");
333062306a36Sopenharmony_ci
333162306a36Sopenharmony_ci	if (out_data != NULL)
333262306a36Sopenharmony_ci		*out_data = NULL;
333362306a36Sopenharmony_ci
333462306a36Sopenharmony_ci	/* zero out returned data len, in case of error */
333562306a36Sopenharmony_ci	if (plen)
333662306a36Sopenharmony_ci		*plen = 0;
333762306a36Sopenharmony_ci
333862306a36Sopenharmony_ci	if (!tcon)
333962306a36Sopenharmony_ci		return -EIO;
334062306a36Sopenharmony_ci
334162306a36Sopenharmony_ci	ses = tcon->ses;
334262306a36Sopenharmony_ci	if (!ses)
334362306a36Sopenharmony_ci		return -EIO;
334462306a36Sopenharmony_ci
334562306a36Sopenharmony_ci	server = cifs_pick_channel(ses);
334662306a36Sopenharmony_ci	if (!server)
334762306a36Sopenharmony_ci		return -EIO;
334862306a36Sopenharmony_ci
334962306a36Sopenharmony_ci	if (smb3_encryption_required(tcon))
335062306a36Sopenharmony_ci		flags |= CIFS_TRANSFORM_REQ;
335162306a36Sopenharmony_ci
335262306a36Sopenharmony_ci	memset(&rqst, 0, sizeof(struct smb_rqst));
335362306a36Sopenharmony_ci	memset(&iov, 0, sizeof(iov));
335462306a36Sopenharmony_ci	rqst.rq_iov = iov;
335562306a36Sopenharmony_ci	rqst.rq_nvec = SMB2_IOCTL_IOV_SIZE;
335662306a36Sopenharmony_ci
335762306a36Sopenharmony_ci	rc = SMB2_ioctl_init(tcon, server,
335862306a36Sopenharmony_ci			     &rqst, persistent_fid, volatile_fid, opcode,
335962306a36Sopenharmony_ci			     in_data, indatalen, max_out_data_len);
336062306a36Sopenharmony_ci	if (rc)
336162306a36Sopenharmony_ci		goto ioctl_exit;
336262306a36Sopenharmony_ci
336362306a36Sopenharmony_ci	rc = cifs_send_recv(xid, ses, server,
336462306a36Sopenharmony_ci			    &rqst, &resp_buftype, flags,
336562306a36Sopenharmony_ci			    &rsp_iov);
336662306a36Sopenharmony_ci	rsp = (struct smb2_ioctl_rsp *)rsp_iov.iov_base;
336762306a36Sopenharmony_ci
336862306a36Sopenharmony_ci	if (rc != 0)
336962306a36Sopenharmony_ci		trace_smb3_fsctl_err(xid, persistent_fid, tcon->tid,
337062306a36Sopenharmony_ci				ses->Suid, 0, opcode, rc);
337162306a36Sopenharmony_ci
337262306a36Sopenharmony_ci	if ((rc != 0) && (rc != -EINVAL) && (rc != -E2BIG)) {
337362306a36Sopenharmony_ci		cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
337462306a36Sopenharmony_ci		goto ioctl_exit;
337562306a36Sopenharmony_ci	} else if (rc == -EINVAL) {
337662306a36Sopenharmony_ci		if ((opcode != FSCTL_SRV_COPYCHUNK_WRITE) &&
337762306a36Sopenharmony_ci		    (opcode != FSCTL_SRV_COPYCHUNK)) {
337862306a36Sopenharmony_ci			cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
337962306a36Sopenharmony_ci			goto ioctl_exit;
338062306a36Sopenharmony_ci		}
338162306a36Sopenharmony_ci	} else if (rc == -E2BIG) {
338262306a36Sopenharmony_ci		if (opcode != FSCTL_QUERY_ALLOCATED_RANGES) {
338362306a36Sopenharmony_ci			cifs_stats_fail_inc(tcon, SMB2_IOCTL_HE);
338462306a36Sopenharmony_ci			goto ioctl_exit;
338562306a36Sopenharmony_ci		}
338662306a36Sopenharmony_ci	}
338762306a36Sopenharmony_ci
338862306a36Sopenharmony_ci	/* check if caller wants to look at return data or just return rc */
338962306a36Sopenharmony_ci	if ((plen == NULL) || (out_data == NULL))
339062306a36Sopenharmony_ci		goto ioctl_exit;
339162306a36Sopenharmony_ci
339262306a36Sopenharmony_ci	/*
339362306a36Sopenharmony_ci	 * Although unlikely to be possible for rsp to be null and rc not set,
339462306a36Sopenharmony_ci	 * adding check below is slightly safer long term (and quiets Coverity
339562306a36Sopenharmony_ci	 * warning)
339662306a36Sopenharmony_ci	 */
339762306a36Sopenharmony_ci	if (rsp == NULL) {
339862306a36Sopenharmony_ci		rc = -EIO;
339962306a36Sopenharmony_ci		goto ioctl_exit;
340062306a36Sopenharmony_ci	}
340162306a36Sopenharmony_ci
340262306a36Sopenharmony_ci	*plen = le32_to_cpu(rsp->OutputCount);
340362306a36Sopenharmony_ci
340462306a36Sopenharmony_ci	/* We check for obvious errors in the output buffer length and offset */
340562306a36Sopenharmony_ci	if (*plen == 0)
340662306a36Sopenharmony_ci		goto ioctl_exit; /* server returned no data */
340762306a36Sopenharmony_ci	else if (*plen > rsp_iov.iov_len || *plen > 0xFF00) {
340862306a36Sopenharmony_ci		cifs_tcon_dbg(VFS, "srv returned invalid ioctl length: %d\n", *plen);
340962306a36Sopenharmony_ci		*plen = 0;
341062306a36Sopenharmony_ci		rc = -EIO;
341162306a36Sopenharmony_ci		goto ioctl_exit;
341262306a36Sopenharmony_ci	}
341362306a36Sopenharmony_ci
341462306a36Sopenharmony_ci	if (rsp_iov.iov_len - *plen < le32_to_cpu(rsp->OutputOffset)) {
341562306a36Sopenharmony_ci		cifs_tcon_dbg(VFS, "Malformed ioctl resp: len %d offset %d\n", *plen,
341662306a36Sopenharmony_ci			le32_to_cpu(rsp->OutputOffset));
341762306a36Sopenharmony_ci		*plen = 0;
341862306a36Sopenharmony_ci		rc = -EIO;
341962306a36Sopenharmony_ci		goto ioctl_exit;
342062306a36Sopenharmony_ci	}
342162306a36Sopenharmony_ci
342262306a36Sopenharmony_ci	*out_data = kmemdup((char *)rsp + le32_to_cpu(rsp->OutputOffset),
342362306a36Sopenharmony_ci			    *plen, GFP_KERNEL);
342462306a36Sopenharmony_ci	if (*out_data == NULL) {
342562306a36Sopenharmony_ci		rc = -ENOMEM;
342662306a36Sopenharmony_ci		goto ioctl_exit;
342762306a36Sopenharmony_ci	}
342862306a36Sopenharmony_ci
342962306a36Sopenharmony_ciioctl_exit:
343062306a36Sopenharmony_ci	SMB2_ioctl_free(&rqst);
343162306a36Sopenharmony_ci	free_rsp_buf(resp_buftype, rsp);
343262306a36Sopenharmony_ci	return rc;
343362306a36Sopenharmony_ci}
343462306a36Sopenharmony_ci
343562306a36Sopenharmony_ci/*
343662306a36Sopenharmony_ci *   Individual callers to ioctl worker function follow
343762306a36Sopenharmony_ci */
343862306a36Sopenharmony_ci
343962306a36Sopenharmony_ciint
344062306a36Sopenharmony_ciSMB2_set_compression(const unsigned int xid, struct cifs_tcon *tcon,
344162306a36Sopenharmony_ci		     u64 persistent_fid, u64 volatile_fid)
344262306a36Sopenharmony_ci{
344362306a36Sopenharmony_ci	int rc;
344462306a36Sopenharmony_ci	struct  compress_ioctl fsctl_input;
344562306a36Sopenharmony_ci	char *ret_data = NULL;
344662306a36Sopenharmony_ci
344762306a36Sopenharmony_ci	fsctl_input.CompressionState =
344862306a36Sopenharmony_ci			cpu_to_le16(COMPRESSION_FORMAT_DEFAULT);
344962306a36Sopenharmony_ci
345062306a36Sopenharmony_ci	rc = SMB2_ioctl(xid, tcon, persistent_fid, volatile_fid,
345162306a36Sopenharmony_ci			FSCTL_SET_COMPRESSION,
345262306a36Sopenharmony_ci			(char *)&fsctl_input /* data input */,
345362306a36Sopenharmony_ci			2 /* in data len */, CIFSMaxBufSize /* max out data */,
345462306a36Sopenharmony_ci			&ret_data /* out data */, NULL);
345562306a36Sopenharmony_ci
345662306a36Sopenharmony_ci	cifs_dbg(FYI, "set compression rc %d\n", rc);
345762306a36Sopenharmony_ci
345862306a36Sopenharmony_ci	return rc;
345962306a36Sopenharmony_ci}
346062306a36Sopenharmony_ci
346162306a36Sopenharmony_ciint
346262306a36Sopenharmony_ciSMB2_close_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,
346362306a36Sopenharmony_ci		struct smb_rqst *rqst,
346462306a36Sopenharmony_ci		u64 persistent_fid, u64 volatile_fid, bool query_attrs)
346562306a36Sopenharmony_ci{
346662306a36Sopenharmony_ci	struct smb2_close_req *req;
346762306a36Sopenharmony_ci	struct kvec *iov = rqst->rq_iov;
346862306a36Sopenharmony_ci	unsigned int total_len;
346962306a36Sopenharmony_ci	int rc;
347062306a36Sopenharmony_ci
347162306a36Sopenharmony_ci	rc = smb2_plain_req_init(SMB2_CLOSE, tcon, server,
347262306a36Sopenharmony_ci				 (void **) &req, &total_len);
347362306a36Sopenharmony_ci	if (rc)
347462306a36Sopenharmony_ci		return rc;
347562306a36Sopenharmony_ci
347662306a36Sopenharmony_ci	req->PersistentFileId = persistent_fid;
347762306a36Sopenharmony_ci	req->VolatileFileId = volatile_fid;
347862306a36Sopenharmony_ci	if (query_attrs)
347962306a36Sopenharmony_ci		req->Flags = SMB2_CLOSE_FLAG_POSTQUERY_ATTRIB;
348062306a36Sopenharmony_ci	else
348162306a36Sopenharmony_ci		req->Flags = 0;
348262306a36Sopenharmony_ci	iov[0].iov_base = (char *)req;
348362306a36Sopenharmony_ci	iov[0].iov_len = total_len;
348462306a36Sopenharmony_ci
348562306a36Sopenharmony_ci	return 0;
348662306a36Sopenharmony_ci}
348762306a36Sopenharmony_ci
348862306a36Sopenharmony_civoid
348962306a36Sopenharmony_ciSMB2_close_free(struct smb_rqst *rqst)
349062306a36Sopenharmony_ci{
349162306a36Sopenharmony_ci	if (rqst && rqst->rq_iov)
349262306a36Sopenharmony_ci		cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */
349362306a36Sopenharmony_ci}
349462306a36Sopenharmony_ci
349562306a36Sopenharmony_ciint
349662306a36Sopenharmony_ci__SMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
349762306a36Sopenharmony_ci	     u64 persistent_fid, u64 volatile_fid,
349862306a36Sopenharmony_ci	     struct smb2_file_network_open_info *pbuf)
349962306a36Sopenharmony_ci{
350062306a36Sopenharmony_ci	struct smb_rqst rqst;
350162306a36Sopenharmony_ci	struct smb2_close_rsp *rsp = NULL;
350262306a36Sopenharmony_ci	struct cifs_ses *ses = tcon->ses;
350362306a36Sopenharmony_ci	struct TCP_Server_Info *server = cifs_pick_channel(ses);
350462306a36Sopenharmony_ci	struct kvec iov[1];
350562306a36Sopenharmony_ci	struct kvec rsp_iov;
350662306a36Sopenharmony_ci	int resp_buftype = CIFS_NO_BUFFER;
350762306a36Sopenharmony_ci	int rc = 0;
350862306a36Sopenharmony_ci	int flags = 0;
350962306a36Sopenharmony_ci	bool query_attrs = false;
351062306a36Sopenharmony_ci
351162306a36Sopenharmony_ci	cifs_dbg(FYI, "Close\n");
351262306a36Sopenharmony_ci
351362306a36Sopenharmony_ci	if (!ses || !server)
351462306a36Sopenharmony_ci		return -EIO;
351562306a36Sopenharmony_ci
351662306a36Sopenharmony_ci	if (smb3_encryption_required(tcon))
351762306a36Sopenharmony_ci		flags |= CIFS_TRANSFORM_REQ;
351862306a36Sopenharmony_ci
351962306a36Sopenharmony_ci	memset(&rqst, 0, sizeof(struct smb_rqst));
352062306a36Sopenharmony_ci	memset(&iov, 0, sizeof(iov));
352162306a36Sopenharmony_ci	rqst.rq_iov = iov;
352262306a36Sopenharmony_ci	rqst.rq_nvec = 1;
352362306a36Sopenharmony_ci
352462306a36Sopenharmony_ci	/* check if need to ask server to return timestamps in close response */
352562306a36Sopenharmony_ci	if (pbuf)
352662306a36Sopenharmony_ci		query_attrs = true;
352762306a36Sopenharmony_ci
352862306a36Sopenharmony_ci	trace_smb3_close_enter(xid, persistent_fid, tcon->tid, ses->Suid);
352962306a36Sopenharmony_ci	rc = SMB2_close_init(tcon, server,
353062306a36Sopenharmony_ci			     &rqst, persistent_fid, volatile_fid,
353162306a36Sopenharmony_ci			     query_attrs);
353262306a36Sopenharmony_ci	if (rc)
353362306a36Sopenharmony_ci		goto close_exit;
353462306a36Sopenharmony_ci
353562306a36Sopenharmony_ci	rc = cifs_send_recv(xid, ses, server,
353662306a36Sopenharmony_ci			    &rqst, &resp_buftype, flags, &rsp_iov);
353762306a36Sopenharmony_ci	rsp = (struct smb2_close_rsp *)rsp_iov.iov_base;
353862306a36Sopenharmony_ci
353962306a36Sopenharmony_ci	if (rc != 0) {
354062306a36Sopenharmony_ci		cifs_stats_fail_inc(tcon, SMB2_CLOSE_HE);
354162306a36Sopenharmony_ci		trace_smb3_close_err(xid, persistent_fid, tcon->tid, ses->Suid,
354262306a36Sopenharmony_ci				     rc);
354362306a36Sopenharmony_ci		goto close_exit;
354462306a36Sopenharmony_ci	} else {
354562306a36Sopenharmony_ci		trace_smb3_close_done(xid, persistent_fid, tcon->tid,
354662306a36Sopenharmony_ci				      ses->Suid);
354762306a36Sopenharmony_ci		if (pbuf)
354862306a36Sopenharmony_ci			memcpy(&pbuf->network_open_info,
354962306a36Sopenharmony_ci			       &rsp->network_open_info,
355062306a36Sopenharmony_ci			       sizeof(pbuf->network_open_info));
355162306a36Sopenharmony_ci	}
355262306a36Sopenharmony_ci
355362306a36Sopenharmony_ci	atomic_dec(&tcon->num_remote_opens);
355462306a36Sopenharmony_ciclose_exit:
355562306a36Sopenharmony_ci	SMB2_close_free(&rqst);
355662306a36Sopenharmony_ci	free_rsp_buf(resp_buftype, rsp);
355762306a36Sopenharmony_ci
355862306a36Sopenharmony_ci	/* retry close in a worker thread if this one is interrupted */
355962306a36Sopenharmony_ci	if (is_interrupt_error(rc)) {
356062306a36Sopenharmony_ci		int tmp_rc;
356162306a36Sopenharmony_ci
356262306a36Sopenharmony_ci		tmp_rc = smb2_handle_cancelled_close(tcon, persistent_fid,
356362306a36Sopenharmony_ci						     volatile_fid);
356462306a36Sopenharmony_ci		if (tmp_rc)
356562306a36Sopenharmony_ci			cifs_dbg(VFS, "handle cancelled close fid 0x%llx returned error %d\n",
356662306a36Sopenharmony_ci				 persistent_fid, tmp_rc);
356762306a36Sopenharmony_ci	}
356862306a36Sopenharmony_ci	return rc;
356962306a36Sopenharmony_ci}
357062306a36Sopenharmony_ci
357162306a36Sopenharmony_ciint
357262306a36Sopenharmony_ciSMB2_close(const unsigned int xid, struct cifs_tcon *tcon,
357362306a36Sopenharmony_ci		u64 persistent_fid, u64 volatile_fid)
357462306a36Sopenharmony_ci{
357562306a36Sopenharmony_ci	return __SMB2_close(xid, tcon, persistent_fid, volatile_fid, NULL);
357662306a36Sopenharmony_ci}
357762306a36Sopenharmony_ci
357862306a36Sopenharmony_ciint
357962306a36Sopenharmony_cismb2_validate_iov(unsigned int offset, unsigned int buffer_length,
358062306a36Sopenharmony_ci		  struct kvec *iov, unsigned int min_buf_size)
358162306a36Sopenharmony_ci{
358262306a36Sopenharmony_ci	unsigned int smb_len = iov->iov_len;
358362306a36Sopenharmony_ci	char *end_of_smb = smb_len + (char *)iov->iov_base;
358462306a36Sopenharmony_ci	char *begin_of_buf = offset + (char *)iov->iov_base;
358562306a36Sopenharmony_ci	char *end_of_buf = begin_of_buf + buffer_length;
358662306a36Sopenharmony_ci
358762306a36Sopenharmony_ci
358862306a36Sopenharmony_ci	if (buffer_length < min_buf_size) {
358962306a36Sopenharmony_ci		cifs_dbg(VFS, "buffer length %d smaller than minimum size %d\n",
359062306a36Sopenharmony_ci			 buffer_length, min_buf_size);
359162306a36Sopenharmony_ci		return -EINVAL;
359262306a36Sopenharmony_ci	}
359362306a36Sopenharmony_ci
359462306a36Sopenharmony_ci	/* check if beyond RFC1001 maximum length */
359562306a36Sopenharmony_ci	if ((smb_len > 0x7FFFFF) || (buffer_length > 0x7FFFFF)) {
359662306a36Sopenharmony_ci		cifs_dbg(VFS, "buffer length %d or smb length %d too large\n",
359762306a36Sopenharmony_ci			 buffer_length, smb_len);
359862306a36Sopenharmony_ci		return -EINVAL;
359962306a36Sopenharmony_ci	}
360062306a36Sopenharmony_ci
360162306a36Sopenharmony_ci	if ((begin_of_buf > end_of_smb) || (end_of_buf > end_of_smb)) {
360262306a36Sopenharmony_ci		cifs_dbg(VFS, "Invalid server response, bad offset to data\n");
360362306a36Sopenharmony_ci		return -EINVAL;
360462306a36Sopenharmony_ci	}
360562306a36Sopenharmony_ci
360662306a36Sopenharmony_ci	return 0;
360762306a36Sopenharmony_ci}
360862306a36Sopenharmony_ci
360962306a36Sopenharmony_ci/*
361062306a36Sopenharmony_ci * If SMB buffer fields are valid, copy into temporary buffer to hold result.
361162306a36Sopenharmony_ci * Caller must free buffer.
361262306a36Sopenharmony_ci */
361362306a36Sopenharmony_ciint
361462306a36Sopenharmony_cismb2_validate_and_copy_iov(unsigned int offset, unsigned int buffer_length,
361562306a36Sopenharmony_ci			   struct kvec *iov, unsigned int minbufsize,
361662306a36Sopenharmony_ci			   char *data)
361762306a36Sopenharmony_ci{
361862306a36Sopenharmony_ci	char *begin_of_buf = offset + (char *)iov->iov_base;
361962306a36Sopenharmony_ci	int rc;
362062306a36Sopenharmony_ci
362162306a36Sopenharmony_ci	if (!data)
362262306a36Sopenharmony_ci		return -EINVAL;
362362306a36Sopenharmony_ci
362462306a36Sopenharmony_ci	rc = smb2_validate_iov(offset, buffer_length, iov, minbufsize);
362562306a36Sopenharmony_ci	if (rc)
362662306a36Sopenharmony_ci		return rc;
362762306a36Sopenharmony_ci
362862306a36Sopenharmony_ci	memcpy(data, begin_of_buf, minbufsize);
362962306a36Sopenharmony_ci
363062306a36Sopenharmony_ci	return 0;
363162306a36Sopenharmony_ci}
363262306a36Sopenharmony_ci
363362306a36Sopenharmony_ciint
363462306a36Sopenharmony_ciSMB2_query_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,
363562306a36Sopenharmony_ci		     struct smb_rqst *rqst,
363662306a36Sopenharmony_ci		     u64 persistent_fid, u64 volatile_fid,
363762306a36Sopenharmony_ci		     u8 info_class, u8 info_type, u32 additional_info,
363862306a36Sopenharmony_ci		     size_t output_len, size_t input_len, void *input)
363962306a36Sopenharmony_ci{
364062306a36Sopenharmony_ci	struct smb2_query_info_req *req;
364162306a36Sopenharmony_ci	struct kvec *iov = rqst->rq_iov;
364262306a36Sopenharmony_ci	unsigned int total_len;
364362306a36Sopenharmony_ci	size_t len;
364462306a36Sopenharmony_ci	int rc;
364562306a36Sopenharmony_ci
364662306a36Sopenharmony_ci	if (unlikely(check_add_overflow(input_len, sizeof(*req), &len) ||
364762306a36Sopenharmony_ci		     len > CIFSMaxBufSize))
364862306a36Sopenharmony_ci		return -EINVAL;
364962306a36Sopenharmony_ci
365062306a36Sopenharmony_ci	rc = smb2_plain_req_init(SMB2_QUERY_INFO, tcon, server,
365162306a36Sopenharmony_ci				 (void **) &req, &total_len);
365262306a36Sopenharmony_ci	if (rc)
365362306a36Sopenharmony_ci		return rc;
365462306a36Sopenharmony_ci
365562306a36Sopenharmony_ci	req->InfoType = info_type;
365662306a36Sopenharmony_ci	req->FileInfoClass = info_class;
365762306a36Sopenharmony_ci	req->PersistentFileId = persistent_fid;
365862306a36Sopenharmony_ci	req->VolatileFileId = volatile_fid;
365962306a36Sopenharmony_ci	req->AdditionalInformation = cpu_to_le32(additional_info);
366062306a36Sopenharmony_ci
366162306a36Sopenharmony_ci	req->OutputBufferLength = cpu_to_le32(output_len);
366262306a36Sopenharmony_ci	if (input_len) {
366362306a36Sopenharmony_ci		req->InputBufferLength = cpu_to_le32(input_len);
366462306a36Sopenharmony_ci		/* total_len for smb query request never close to le16 max */
366562306a36Sopenharmony_ci		req->InputBufferOffset = cpu_to_le16(total_len - 1);
366662306a36Sopenharmony_ci		memcpy(req->Buffer, input, input_len);
366762306a36Sopenharmony_ci	}
366862306a36Sopenharmony_ci
366962306a36Sopenharmony_ci	iov[0].iov_base = (char *)req;
367062306a36Sopenharmony_ci	/* 1 for Buffer */
367162306a36Sopenharmony_ci	iov[0].iov_len = len;
367262306a36Sopenharmony_ci	return 0;
367362306a36Sopenharmony_ci}
367462306a36Sopenharmony_ci
367562306a36Sopenharmony_civoid
367662306a36Sopenharmony_ciSMB2_query_info_free(struct smb_rqst *rqst)
367762306a36Sopenharmony_ci{
367862306a36Sopenharmony_ci	if (rqst && rqst->rq_iov)
367962306a36Sopenharmony_ci		cifs_buf_release(rqst->rq_iov[0].iov_base); /* request */
368062306a36Sopenharmony_ci}
368162306a36Sopenharmony_ci
368262306a36Sopenharmony_cistatic int
368362306a36Sopenharmony_ciquery_info(const unsigned int xid, struct cifs_tcon *tcon,
368462306a36Sopenharmony_ci	   u64 persistent_fid, u64 volatile_fid, u8 info_class, u8 info_type,
368562306a36Sopenharmony_ci	   u32 additional_info, size_t output_len, size_t min_len, void **data,
368662306a36Sopenharmony_ci		u32 *dlen)
368762306a36Sopenharmony_ci{
368862306a36Sopenharmony_ci	struct smb_rqst rqst;
368962306a36Sopenharmony_ci	struct smb2_query_info_rsp *rsp = NULL;
369062306a36Sopenharmony_ci	struct kvec iov[1];
369162306a36Sopenharmony_ci	struct kvec rsp_iov;
369262306a36Sopenharmony_ci	int rc = 0;
369362306a36Sopenharmony_ci	int resp_buftype = CIFS_NO_BUFFER;
369462306a36Sopenharmony_ci	struct cifs_ses *ses = tcon->ses;
369562306a36Sopenharmony_ci	struct TCP_Server_Info *server;
369662306a36Sopenharmony_ci	int flags = 0;
369762306a36Sopenharmony_ci	bool allocated = false;
369862306a36Sopenharmony_ci
369962306a36Sopenharmony_ci	cifs_dbg(FYI, "Query Info\n");
370062306a36Sopenharmony_ci
370162306a36Sopenharmony_ci	if (!ses)
370262306a36Sopenharmony_ci		return -EIO;
370362306a36Sopenharmony_ci	server = cifs_pick_channel(ses);
370462306a36Sopenharmony_ci	if (!server)
370562306a36Sopenharmony_ci		return -EIO;
370662306a36Sopenharmony_ci
370762306a36Sopenharmony_ci	if (smb3_encryption_required(tcon))
370862306a36Sopenharmony_ci		flags |= CIFS_TRANSFORM_REQ;
370962306a36Sopenharmony_ci
371062306a36Sopenharmony_ci	memset(&rqst, 0, sizeof(struct smb_rqst));
371162306a36Sopenharmony_ci	memset(&iov, 0, sizeof(iov));
371262306a36Sopenharmony_ci	rqst.rq_iov = iov;
371362306a36Sopenharmony_ci	rqst.rq_nvec = 1;
371462306a36Sopenharmony_ci
371562306a36Sopenharmony_ci	rc = SMB2_query_info_init(tcon, server,
371662306a36Sopenharmony_ci				  &rqst, persistent_fid, volatile_fid,
371762306a36Sopenharmony_ci				  info_class, info_type, additional_info,
371862306a36Sopenharmony_ci				  output_len, 0, NULL);
371962306a36Sopenharmony_ci	if (rc)
372062306a36Sopenharmony_ci		goto qinf_exit;
372162306a36Sopenharmony_ci
372262306a36Sopenharmony_ci	trace_smb3_query_info_enter(xid, persistent_fid, tcon->tid,
372362306a36Sopenharmony_ci				    ses->Suid, info_class, (__u32)info_type);
372462306a36Sopenharmony_ci
372562306a36Sopenharmony_ci	rc = cifs_send_recv(xid, ses, server,
372662306a36Sopenharmony_ci			    &rqst, &resp_buftype, flags, &rsp_iov);
372762306a36Sopenharmony_ci	rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base;
372862306a36Sopenharmony_ci
372962306a36Sopenharmony_ci	if (rc) {
373062306a36Sopenharmony_ci		cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE);
373162306a36Sopenharmony_ci		trace_smb3_query_info_err(xid, persistent_fid, tcon->tid,
373262306a36Sopenharmony_ci				ses->Suid, info_class, (__u32)info_type, rc);
373362306a36Sopenharmony_ci		goto qinf_exit;
373462306a36Sopenharmony_ci	}
373562306a36Sopenharmony_ci
373662306a36Sopenharmony_ci	trace_smb3_query_info_done(xid, persistent_fid, tcon->tid,
373762306a36Sopenharmony_ci				ses->Suid, info_class, (__u32)info_type);
373862306a36Sopenharmony_ci
373962306a36Sopenharmony_ci	if (dlen) {
374062306a36Sopenharmony_ci		*dlen = le32_to_cpu(rsp->OutputBufferLength);
374162306a36Sopenharmony_ci		if (!*data) {
374262306a36Sopenharmony_ci			*data = kmalloc(*dlen, GFP_KERNEL);
374362306a36Sopenharmony_ci			if (!*data) {
374462306a36Sopenharmony_ci				cifs_tcon_dbg(VFS,
374562306a36Sopenharmony_ci					"Error %d allocating memory for acl\n",
374662306a36Sopenharmony_ci					rc);
374762306a36Sopenharmony_ci				*dlen = 0;
374862306a36Sopenharmony_ci				rc = -ENOMEM;
374962306a36Sopenharmony_ci				goto qinf_exit;
375062306a36Sopenharmony_ci			}
375162306a36Sopenharmony_ci			allocated = true;
375262306a36Sopenharmony_ci		}
375362306a36Sopenharmony_ci	}
375462306a36Sopenharmony_ci
375562306a36Sopenharmony_ci	rc = smb2_validate_and_copy_iov(le16_to_cpu(rsp->OutputBufferOffset),
375662306a36Sopenharmony_ci					le32_to_cpu(rsp->OutputBufferLength),
375762306a36Sopenharmony_ci					&rsp_iov, dlen ? *dlen : min_len, *data);
375862306a36Sopenharmony_ci	if (rc && allocated) {
375962306a36Sopenharmony_ci		kfree(*data);
376062306a36Sopenharmony_ci		*data = NULL;
376162306a36Sopenharmony_ci		*dlen = 0;
376262306a36Sopenharmony_ci	}
376362306a36Sopenharmony_ci
376462306a36Sopenharmony_ciqinf_exit:
376562306a36Sopenharmony_ci	SMB2_query_info_free(&rqst);
376662306a36Sopenharmony_ci	free_rsp_buf(resp_buftype, rsp);
376762306a36Sopenharmony_ci	return rc;
376862306a36Sopenharmony_ci}
376962306a36Sopenharmony_ci
377062306a36Sopenharmony_ciint SMB2_query_info(const unsigned int xid, struct cifs_tcon *tcon,
377162306a36Sopenharmony_ci	u64 persistent_fid, u64 volatile_fid, struct smb2_file_all_info *data)
377262306a36Sopenharmony_ci{
377362306a36Sopenharmony_ci	return query_info(xid, tcon, persistent_fid, volatile_fid,
377462306a36Sopenharmony_ci			  FILE_ALL_INFORMATION, SMB2_O_INFO_FILE, 0,
377562306a36Sopenharmony_ci			  sizeof(struct smb2_file_all_info) + PATH_MAX * 2,
377662306a36Sopenharmony_ci			  sizeof(struct smb2_file_all_info), (void **)&data,
377762306a36Sopenharmony_ci			  NULL);
377862306a36Sopenharmony_ci}
377962306a36Sopenharmony_ci
378062306a36Sopenharmony_ci#if 0
378162306a36Sopenharmony_ci/* currently unused, as now we are doing compounding instead (see smb311_posix_query_path_info) */
378262306a36Sopenharmony_ciint
378362306a36Sopenharmony_ciSMB311_posix_query_info(const unsigned int xid, struct cifs_tcon *tcon,
378462306a36Sopenharmony_ci		u64 persistent_fid, u64 volatile_fid, struct smb311_posix_qinfo *data, u32 *plen)
378562306a36Sopenharmony_ci{
378662306a36Sopenharmony_ci	size_t output_len = sizeof(struct smb311_posix_qinfo *) +
378762306a36Sopenharmony_ci			(sizeof(struct cifs_sid) * 2) + (PATH_MAX * 2);
378862306a36Sopenharmony_ci	*plen = 0;
378962306a36Sopenharmony_ci
379062306a36Sopenharmony_ci	return query_info(xid, tcon, persistent_fid, volatile_fid,
379162306a36Sopenharmony_ci			  SMB_FIND_FILE_POSIX_INFO, SMB2_O_INFO_FILE, 0,
379262306a36Sopenharmony_ci			  output_len, sizeof(struct smb311_posix_qinfo), (void **)&data, plen);
379362306a36Sopenharmony_ci	/* Note caller must free "data" (passed in above). It may be allocated in query_info call */
379462306a36Sopenharmony_ci}
379562306a36Sopenharmony_ci#endif
379662306a36Sopenharmony_ci
379762306a36Sopenharmony_ciint
379862306a36Sopenharmony_ciSMB2_query_acl(const unsigned int xid, struct cifs_tcon *tcon,
379962306a36Sopenharmony_ci	       u64 persistent_fid, u64 volatile_fid,
380062306a36Sopenharmony_ci	       void **data, u32 *plen, u32 extra_info)
380162306a36Sopenharmony_ci{
380262306a36Sopenharmony_ci	__u32 additional_info = OWNER_SECINFO | GROUP_SECINFO | DACL_SECINFO |
380362306a36Sopenharmony_ci				extra_info;
380462306a36Sopenharmony_ci	*plen = 0;
380562306a36Sopenharmony_ci
380662306a36Sopenharmony_ci	return query_info(xid, tcon, persistent_fid, volatile_fid,
380762306a36Sopenharmony_ci			  0, SMB2_O_INFO_SECURITY, additional_info,
380862306a36Sopenharmony_ci			  SMB2_MAX_BUFFER_SIZE, MIN_SEC_DESC_LEN, data, plen);
380962306a36Sopenharmony_ci}
381062306a36Sopenharmony_ci
381162306a36Sopenharmony_ciint
381262306a36Sopenharmony_ciSMB2_get_srv_num(const unsigned int xid, struct cifs_tcon *tcon,
381362306a36Sopenharmony_ci		 u64 persistent_fid, u64 volatile_fid, __le64 *uniqueid)
381462306a36Sopenharmony_ci{
381562306a36Sopenharmony_ci	return query_info(xid, tcon, persistent_fid, volatile_fid,
381662306a36Sopenharmony_ci			  FILE_INTERNAL_INFORMATION, SMB2_O_INFO_FILE, 0,
381762306a36Sopenharmony_ci			  sizeof(struct smb2_file_internal_info),
381862306a36Sopenharmony_ci			  sizeof(struct smb2_file_internal_info),
381962306a36Sopenharmony_ci			  (void **)&uniqueid, NULL);
382062306a36Sopenharmony_ci}
382162306a36Sopenharmony_ci
382262306a36Sopenharmony_ci/*
382362306a36Sopenharmony_ci * CHANGE_NOTIFY Request is sent to get notifications on changes to a directory
382462306a36Sopenharmony_ci * See MS-SMB2 2.2.35 and 2.2.36
382562306a36Sopenharmony_ci */
382662306a36Sopenharmony_ci
382762306a36Sopenharmony_cistatic int
382862306a36Sopenharmony_ciSMB2_notify_init(const unsigned int xid, struct smb_rqst *rqst,
382962306a36Sopenharmony_ci		 struct cifs_tcon *tcon, struct TCP_Server_Info *server,
383062306a36Sopenharmony_ci		 u64 persistent_fid, u64 volatile_fid,
383162306a36Sopenharmony_ci		 u32 completion_filter, bool watch_tree)
383262306a36Sopenharmony_ci{
383362306a36Sopenharmony_ci	struct smb2_change_notify_req *req;
383462306a36Sopenharmony_ci	struct kvec *iov = rqst->rq_iov;
383562306a36Sopenharmony_ci	unsigned int total_len;
383662306a36Sopenharmony_ci	int rc;
383762306a36Sopenharmony_ci
383862306a36Sopenharmony_ci	rc = smb2_plain_req_init(SMB2_CHANGE_NOTIFY, tcon, server,
383962306a36Sopenharmony_ci				 (void **) &req, &total_len);
384062306a36Sopenharmony_ci	if (rc)
384162306a36Sopenharmony_ci		return rc;
384262306a36Sopenharmony_ci
384362306a36Sopenharmony_ci	req->PersistentFileId = persistent_fid;
384462306a36Sopenharmony_ci	req->VolatileFileId = volatile_fid;
384562306a36Sopenharmony_ci	/* See note 354 of MS-SMB2, 64K max */
384662306a36Sopenharmony_ci	req->OutputBufferLength =
384762306a36Sopenharmony_ci		cpu_to_le32(SMB2_MAX_BUFFER_SIZE - MAX_SMB2_HDR_SIZE);
384862306a36Sopenharmony_ci	req->CompletionFilter = cpu_to_le32(completion_filter);
384962306a36Sopenharmony_ci	if (watch_tree)
385062306a36Sopenharmony_ci		req->Flags = cpu_to_le16(SMB2_WATCH_TREE);
385162306a36Sopenharmony_ci	else
385262306a36Sopenharmony_ci		req->Flags = 0;
385362306a36Sopenharmony_ci
385462306a36Sopenharmony_ci	iov[0].iov_base = (char *)req;
385562306a36Sopenharmony_ci	iov[0].iov_len = total_len;
385662306a36Sopenharmony_ci
385762306a36Sopenharmony_ci	return 0;
385862306a36Sopenharmony_ci}
385962306a36Sopenharmony_ci
386062306a36Sopenharmony_ciint
386162306a36Sopenharmony_ciSMB2_change_notify(const unsigned int xid, struct cifs_tcon *tcon,
386262306a36Sopenharmony_ci		u64 persistent_fid, u64 volatile_fid, bool watch_tree,
386362306a36Sopenharmony_ci		u32 completion_filter, u32 max_out_data_len, char **out_data,
386462306a36Sopenharmony_ci		u32 *plen /* returned data len */)
386562306a36Sopenharmony_ci{
386662306a36Sopenharmony_ci	struct cifs_ses *ses = tcon->ses;
386762306a36Sopenharmony_ci	struct TCP_Server_Info *server = cifs_pick_channel(ses);
386862306a36Sopenharmony_ci	struct smb_rqst rqst;
386962306a36Sopenharmony_ci	struct smb2_change_notify_rsp *smb_rsp;
387062306a36Sopenharmony_ci	struct kvec iov[1];
387162306a36Sopenharmony_ci	struct kvec rsp_iov = {NULL, 0};
387262306a36Sopenharmony_ci	int resp_buftype = CIFS_NO_BUFFER;
387362306a36Sopenharmony_ci	int flags = 0;
387462306a36Sopenharmony_ci	int rc = 0;
387562306a36Sopenharmony_ci
387662306a36Sopenharmony_ci	cifs_dbg(FYI, "change notify\n");
387762306a36Sopenharmony_ci	if (!ses || !server)
387862306a36Sopenharmony_ci		return -EIO;
387962306a36Sopenharmony_ci
388062306a36Sopenharmony_ci	if (smb3_encryption_required(tcon))
388162306a36Sopenharmony_ci		flags |= CIFS_TRANSFORM_REQ;
388262306a36Sopenharmony_ci
388362306a36Sopenharmony_ci	memset(&rqst, 0, sizeof(struct smb_rqst));
388462306a36Sopenharmony_ci	memset(&iov, 0, sizeof(iov));
388562306a36Sopenharmony_ci	if (plen)
388662306a36Sopenharmony_ci		*plen = 0;
388762306a36Sopenharmony_ci
388862306a36Sopenharmony_ci	rqst.rq_iov = iov;
388962306a36Sopenharmony_ci	rqst.rq_nvec = 1;
389062306a36Sopenharmony_ci
389162306a36Sopenharmony_ci	rc = SMB2_notify_init(xid, &rqst, tcon, server,
389262306a36Sopenharmony_ci			      persistent_fid, volatile_fid,
389362306a36Sopenharmony_ci			      completion_filter, watch_tree);
389462306a36Sopenharmony_ci	if (rc)
389562306a36Sopenharmony_ci		goto cnotify_exit;
389662306a36Sopenharmony_ci
389762306a36Sopenharmony_ci	trace_smb3_notify_enter(xid, persistent_fid, tcon->tid, ses->Suid,
389862306a36Sopenharmony_ci				(u8)watch_tree, completion_filter);
389962306a36Sopenharmony_ci	rc = cifs_send_recv(xid, ses, server,
390062306a36Sopenharmony_ci			    &rqst, &resp_buftype, flags, &rsp_iov);
390162306a36Sopenharmony_ci
390262306a36Sopenharmony_ci	if (rc != 0) {
390362306a36Sopenharmony_ci		cifs_stats_fail_inc(tcon, SMB2_CHANGE_NOTIFY_HE);
390462306a36Sopenharmony_ci		trace_smb3_notify_err(xid, persistent_fid, tcon->tid, ses->Suid,
390562306a36Sopenharmony_ci				(u8)watch_tree, completion_filter, rc);
390662306a36Sopenharmony_ci	} else {
390762306a36Sopenharmony_ci		trace_smb3_notify_done(xid, persistent_fid, tcon->tid,
390862306a36Sopenharmony_ci			ses->Suid, (u8)watch_tree, completion_filter);
390962306a36Sopenharmony_ci		/* validate that notify information is plausible */
391062306a36Sopenharmony_ci		if ((rsp_iov.iov_base == NULL) ||
391162306a36Sopenharmony_ci		    (rsp_iov.iov_len < sizeof(struct smb2_change_notify_rsp) + 1))
391262306a36Sopenharmony_ci			goto cnotify_exit;
391362306a36Sopenharmony_ci
391462306a36Sopenharmony_ci		smb_rsp = (struct smb2_change_notify_rsp *)rsp_iov.iov_base;
391562306a36Sopenharmony_ci
391662306a36Sopenharmony_ci		smb2_validate_iov(le16_to_cpu(smb_rsp->OutputBufferOffset),
391762306a36Sopenharmony_ci				le32_to_cpu(smb_rsp->OutputBufferLength), &rsp_iov,
391862306a36Sopenharmony_ci				sizeof(struct file_notify_information));
391962306a36Sopenharmony_ci
392062306a36Sopenharmony_ci		*out_data = kmemdup((char *)smb_rsp + le16_to_cpu(smb_rsp->OutputBufferOffset),
392162306a36Sopenharmony_ci				le32_to_cpu(smb_rsp->OutputBufferLength), GFP_KERNEL);
392262306a36Sopenharmony_ci		if (*out_data == NULL) {
392362306a36Sopenharmony_ci			rc = -ENOMEM;
392462306a36Sopenharmony_ci			goto cnotify_exit;
392562306a36Sopenharmony_ci		} else if (plen)
392662306a36Sopenharmony_ci			*plen = le32_to_cpu(smb_rsp->OutputBufferLength);
392762306a36Sopenharmony_ci	}
392862306a36Sopenharmony_ci
392962306a36Sopenharmony_ci cnotify_exit:
393062306a36Sopenharmony_ci	if (rqst.rq_iov)
393162306a36Sopenharmony_ci		cifs_small_buf_release(rqst.rq_iov[0].iov_base); /* request */
393262306a36Sopenharmony_ci	free_rsp_buf(resp_buftype, rsp_iov.iov_base);
393362306a36Sopenharmony_ci	return rc;
393462306a36Sopenharmony_ci}
393562306a36Sopenharmony_ci
393662306a36Sopenharmony_ci
393762306a36Sopenharmony_ci
393862306a36Sopenharmony_ci/*
393962306a36Sopenharmony_ci * This is a no-op for now. We're not really interested in the reply, but
394062306a36Sopenharmony_ci * rather in the fact that the server sent one and that server->lstrp
394162306a36Sopenharmony_ci * gets updated.
394262306a36Sopenharmony_ci *
394362306a36Sopenharmony_ci * FIXME: maybe we should consider checking that the reply matches request?
394462306a36Sopenharmony_ci */
394562306a36Sopenharmony_cistatic void
394662306a36Sopenharmony_cismb2_echo_callback(struct mid_q_entry *mid)
394762306a36Sopenharmony_ci{
394862306a36Sopenharmony_ci	struct TCP_Server_Info *server = mid->callback_data;
394962306a36Sopenharmony_ci	struct smb2_echo_rsp *rsp = (struct smb2_echo_rsp *)mid->resp_buf;
395062306a36Sopenharmony_ci	struct cifs_credits credits = { .value = 0, .instance = 0 };
395162306a36Sopenharmony_ci
395262306a36Sopenharmony_ci	if (mid->mid_state == MID_RESPONSE_RECEIVED
395362306a36Sopenharmony_ci	    || mid->mid_state == MID_RESPONSE_MALFORMED) {
395462306a36Sopenharmony_ci		credits.value = le16_to_cpu(rsp->hdr.CreditRequest);
395562306a36Sopenharmony_ci		credits.instance = server->reconnect_instance;
395662306a36Sopenharmony_ci	}
395762306a36Sopenharmony_ci
395862306a36Sopenharmony_ci	release_mid(mid);
395962306a36Sopenharmony_ci	add_credits(server, &credits, CIFS_ECHO_OP);
396062306a36Sopenharmony_ci}
396162306a36Sopenharmony_ci
396262306a36Sopenharmony_civoid smb2_reconnect_server(struct work_struct *work)
396362306a36Sopenharmony_ci{
396462306a36Sopenharmony_ci	struct TCP_Server_Info *server = container_of(work,
396562306a36Sopenharmony_ci					struct TCP_Server_Info, reconnect.work);
396662306a36Sopenharmony_ci	struct TCP_Server_Info *pserver;
396762306a36Sopenharmony_ci	struct cifs_ses *ses, *ses2;
396862306a36Sopenharmony_ci	struct cifs_tcon *tcon, *tcon2;
396962306a36Sopenharmony_ci	struct list_head tmp_list, tmp_ses_list;
397062306a36Sopenharmony_ci	bool tcon_exist = false, ses_exist = false;
397162306a36Sopenharmony_ci	bool tcon_selected = false;
397262306a36Sopenharmony_ci	int rc;
397362306a36Sopenharmony_ci	bool resched = false;
397462306a36Sopenharmony_ci
397562306a36Sopenharmony_ci	/* first check if ref count has reached 0, if not inc ref count */
397662306a36Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
397762306a36Sopenharmony_ci	if (!server->srv_count) {
397862306a36Sopenharmony_ci		spin_unlock(&cifs_tcp_ses_lock);
397962306a36Sopenharmony_ci		return;
398062306a36Sopenharmony_ci	}
398162306a36Sopenharmony_ci	server->srv_count++;
398262306a36Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
398362306a36Sopenharmony_ci
398462306a36Sopenharmony_ci	/* If server is a channel, select the primary channel */
398562306a36Sopenharmony_ci	pserver = SERVER_IS_CHAN(server) ? server->primary_server : server;
398662306a36Sopenharmony_ci
398762306a36Sopenharmony_ci	/* Prevent simultaneous reconnects that can corrupt tcon->rlist list */
398862306a36Sopenharmony_ci	mutex_lock(&pserver->reconnect_mutex);
398962306a36Sopenharmony_ci
399062306a36Sopenharmony_ci	/* if the server is marked for termination, drop the ref count here */
399162306a36Sopenharmony_ci	if (server->terminate) {
399262306a36Sopenharmony_ci		cifs_put_tcp_session(server, true);
399362306a36Sopenharmony_ci		mutex_unlock(&pserver->reconnect_mutex);
399462306a36Sopenharmony_ci		return;
399562306a36Sopenharmony_ci	}
399662306a36Sopenharmony_ci
399762306a36Sopenharmony_ci	INIT_LIST_HEAD(&tmp_list);
399862306a36Sopenharmony_ci	INIT_LIST_HEAD(&tmp_ses_list);
399962306a36Sopenharmony_ci	cifs_dbg(FYI, "Reconnecting tcons and channels\n");
400062306a36Sopenharmony_ci
400162306a36Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
400262306a36Sopenharmony_ci	list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
400362306a36Sopenharmony_ci		spin_lock(&ses->ses_lock);
400462306a36Sopenharmony_ci		if (ses->ses_status == SES_EXITING) {
400562306a36Sopenharmony_ci			spin_unlock(&ses->ses_lock);
400662306a36Sopenharmony_ci			continue;
400762306a36Sopenharmony_ci		}
400862306a36Sopenharmony_ci		spin_unlock(&ses->ses_lock);
400962306a36Sopenharmony_ci
401062306a36Sopenharmony_ci		tcon_selected = false;
401162306a36Sopenharmony_ci
401262306a36Sopenharmony_ci		list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
401362306a36Sopenharmony_ci			if (tcon->need_reconnect || tcon->need_reopen_files) {
401462306a36Sopenharmony_ci				tcon->tc_count++;
401562306a36Sopenharmony_ci				list_add_tail(&tcon->rlist, &tmp_list);
401662306a36Sopenharmony_ci				tcon_selected = tcon_exist = true;
401762306a36Sopenharmony_ci			}
401862306a36Sopenharmony_ci		}
401962306a36Sopenharmony_ci		/*
402062306a36Sopenharmony_ci		 * IPC has the same lifetime as its session and uses its
402162306a36Sopenharmony_ci		 * refcount.
402262306a36Sopenharmony_ci		 */
402362306a36Sopenharmony_ci		if (ses->tcon_ipc && ses->tcon_ipc->need_reconnect) {
402462306a36Sopenharmony_ci			list_add_tail(&ses->tcon_ipc->rlist, &tmp_list);
402562306a36Sopenharmony_ci			tcon_selected = tcon_exist = true;
402662306a36Sopenharmony_ci			cifs_smb_ses_inc_refcount(ses);
402762306a36Sopenharmony_ci		}
402862306a36Sopenharmony_ci		/*
402962306a36Sopenharmony_ci		 * handle the case where channel needs to reconnect
403062306a36Sopenharmony_ci		 * binding session, but tcon is healthy (some other channel
403162306a36Sopenharmony_ci		 * is active)
403262306a36Sopenharmony_ci		 */
403362306a36Sopenharmony_ci		spin_lock(&ses->chan_lock);
403462306a36Sopenharmony_ci		if (!tcon_selected && cifs_chan_needs_reconnect(ses, server)) {
403562306a36Sopenharmony_ci			list_add_tail(&ses->rlist, &tmp_ses_list);
403662306a36Sopenharmony_ci			ses_exist = true;
403762306a36Sopenharmony_ci			cifs_smb_ses_inc_refcount(ses);
403862306a36Sopenharmony_ci		}
403962306a36Sopenharmony_ci		spin_unlock(&ses->chan_lock);
404062306a36Sopenharmony_ci	}
404162306a36Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
404262306a36Sopenharmony_ci
404362306a36Sopenharmony_ci	list_for_each_entry_safe(tcon, tcon2, &tmp_list, rlist) {
404462306a36Sopenharmony_ci		rc = smb2_reconnect(SMB2_INTERNAL_CMD, tcon, server, true);
404562306a36Sopenharmony_ci		if (!rc)
404662306a36Sopenharmony_ci			cifs_reopen_persistent_handles(tcon);
404762306a36Sopenharmony_ci		else
404862306a36Sopenharmony_ci			resched = true;
404962306a36Sopenharmony_ci		list_del_init(&tcon->rlist);
405062306a36Sopenharmony_ci		if (tcon->ipc)
405162306a36Sopenharmony_ci			cifs_put_smb_ses(tcon->ses);
405262306a36Sopenharmony_ci		else
405362306a36Sopenharmony_ci			cifs_put_tcon(tcon);
405462306a36Sopenharmony_ci	}
405562306a36Sopenharmony_ci
405662306a36Sopenharmony_ci	if (!ses_exist)
405762306a36Sopenharmony_ci		goto done;
405862306a36Sopenharmony_ci
405962306a36Sopenharmony_ci	/* allocate a dummy tcon struct used for reconnect */
406062306a36Sopenharmony_ci	tcon = tcon_info_alloc(false);
406162306a36Sopenharmony_ci	if (!tcon) {
406262306a36Sopenharmony_ci		resched = true;
406362306a36Sopenharmony_ci		list_for_each_entry_safe(ses, ses2, &tmp_ses_list, rlist) {
406462306a36Sopenharmony_ci			list_del_init(&ses->rlist);
406562306a36Sopenharmony_ci			cifs_put_smb_ses(ses);
406662306a36Sopenharmony_ci		}
406762306a36Sopenharmony_ci		goto done;
406862306a36Sopenharmony_ci	}
406962306a36Sopenharmony_ci
407062306a36Sopenharmony_ci	tcon->status = TID_GOOD;
407162306a36Sopenharmony_ci	tcon->retry = false;
407262306a36Sopenharmony_ci	tcon->need_reconnect = false;
407362306a36Sopenharmony_ci
407462306a36Sopenharmony_ci	/* now reconnect sessions for necessary channels */
407562306a36Sopenharmony_ci	list_for_each_entry_safe(ses, ses2, &tmp_ses_list, rlist) {
407662306a36Sopenharmony_ci		tcon->ses = ses;
407762306a36Sopenharmony_ci		rc = smb2_reconnect(SMB2_INTERNAL_CMD, tcon, server, true);
407862306a36Sopenharmony_ci		if (rc)
407962306a36Sopenharmony_ci			resched = true;
408062306a36Sopenharmony_ci		list_del_init(&ses->rlist);
408162306a36Sopenharmony_ci		cifs_put_smb_ses(ses);
408262306a36Sopenharmony_ci	}
408362306a36Sopenharmony_ci	tconInfoFree(tcon);
408462306a36Sopenharmony_ci
408562306a36Sopenharmony_cidone:
408662306a36Sopenharmony_ci	cifs_dbg(FYI, "Reconnecting tcons and channels finished\n");
408762306a36Sopenharmony_ci	if (resched)
408862306a36Sopenharmony_ci		queue_delayed_work(cifsiod_wq, &server->reconnect, 2 * HZ);
408962306a36Sopenharmony_ci	mutex_unlock(&pserver->reconnect_mutex);
409062306a36Sopenharmony_ci
409162306a36Sopenharmony_ci	/* now we can safely release srv struct */
409262306a36Sopenharmony_ci	cifs_put_tcp_session(server, true);
409362306a36Sopenharmony_ci}
409462306a36Sopenharmony_ci
409562306a36Sopenharmony_ciint
409662306a36Sopenharmony_ciSMB2_echo(struct TCP_Server_Info *server)
409762306a36Sopenharmony_ci{
409862306a36Sopenharmony_ci	struct smb2_echo_req *req;
409962306a36Sopenharmony_ci	int rc = 0;
410062306a36Sopenharmony_ci	struct kvec iov[1];
410162306a36Sopenharmony_ci	struct smb_rqst rqst = { .rq_iov = iov,
410262306a36Sopenharmony_ci				 .rq_nvec = 1 };
410362306a36Sopenharmony_ci	unsigned int total_len;
410462306a36Sopenharmony_ci
410562306a36Sopenharmony_ci	cifs_dbg(FYI, "In echo request for conn_id %lld\n", server->conn_id);
410662306a36Sopenharmony_ci
410762306a36Sopenharmony_ci	spin_lock(&server->srv_lock);
410862306a36Sopenharmony_ci	if (server->ops->need_neg &&
410962306a36Sopenharmony_ci	    server->ops->need_neg(server)) {
411062306a36Sopenharmony_ci		spin_unlock(&server->srv_lock);
411162306a36Sopenharmony_ci		/* No need to send echo on newly established connections */
411262306a36Sopenharmony_ci		mod_delayed_work(cifsiod_wq, &server->reconnect, 0);
411362306a36Sopenharmony_ci		return rc;
411462306a36Sopenharmony_ci	}
411562306a36Sopenharmony_ci	spin_unlock(&server->srv_lock);
411662306a36Sopenharmony_ci
411762306a36Sopenharmony_ci	rc = smb2_plain_req_init(SMB2_ECHO, NULL, server,
411862306a36Sopenharmony_ci				 (void **)&req, &total_len);
411962306a36Sopenharmony_ci	if (rc)
412062306a36Sopenharmony_ci		return rc;
412162306a36Sopenharmony_ci
412262306a36Sopenharmony_ci	req->hdr.CreditRequest = cpu_to_le16(1);
412362306a36Sopenharmony_ci
412462306a36Sopenharmony_ci	iov[0].iov_len = total_len;
412562306a36Sopenharmony_ci	iov[0].iov_base = (char *)req;
412662306a36Sopenharmony_ci
412762306a36Sopenharmony_ci	rc = cifs_call_async(server, &rqst, NULL, smb2_echo_callback, NULL,
412862306a36Sopenharmony_ci			     server, CIFS_ECHO_OP, NULL);
412962306a36Sopenharmony_ci	if (rc)
413062306a36Sopenharmony_ci		cifs_dbg(FYI, "Echo request failed: %d\n", rc);
413162306a36Sopenharmony_ci
413262306a36Sopenharmony_ci	cifs_small_buf_release(req);
413362306a36Sopenharmony_ci	return rc;
413462306a36Sopenharmony_ci}
413562306a36Sopenharmony_ci
413662306a36Sopenharmony_civoid
413762306a36Sopenharmony_ciSMB2_flush_free(struct smb_rqst *rqst)
413862306a36Sopenharmony_ci{
413962306a36Sopenharmony_ci	if (rqst && rqst->rq_iov)
414062306a36Sopenharmony_ci		cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */
414162306a36Sopenharmony_ci}
414262306a36Sopenharmony_ci
414362306a36Sopenharmony_ciint
414462306a36Sopenharmony_ciSMB2_flush_init(const unsigned int xid, struct smb_rqst *rqst,
414562306a36Sopenharmony_ci		struct cifs_tcon *tcon, struct TCP_Server_Info *server,
414662306a36Sopenharmony_ci		u64 persistent_fid, u64 volatile_fid)
414762306a36Sopenharmony_ci{
414862306a36Sopenharmony_ci	struct smb2_flush_req *req;
414962306a36Sopenharmony_ci	struct kvec *iov = rqst->rq_iov;
415062306a36Sopenharmony_ci	unsigned int total_len;
415162306a36Sopenharmony_ci	int rc;
415262306a36Sopenharmony_ci
415362306a36Sopenharmony_ci	rc = smb2_plain_req_init(SMB2_FLUSH, tcon, server,
415462306a36Sopenharmony_ci				 (void **) &req, &total_len);
415562306a36Sopenharmony_ci	if (rc)
415662306a36Sopenharmony_ci		return rc;
415762306a36Sopenharmony_ci
415862306a36Sopenharmony_ci	req->PersistentFileId = persistent_fid;
415962306a36Sopenharmony_ci	req->VolatileFileId = volatile_fid;
416062306a36Sopenharmony_ci
416162306a36Sopenharmony_ci	iov[0].iov_base = (char *)req;
416262306a36Sopenharmony_ci	iov[0].iov_len = total_len;
416362306a36Sopenharmony_ci
416462306a36Sopenharmony_ci	return 0;
416562306a36Sopenharmony_ci}
416662306a36Sopenharmony_ci
416762306a36Sopenharmony_ciint
416862306a36Sopenharmony_ciSMB2_flush(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
416962306a36Sopenharmony_ci	   u64 volatile_fid)
417062306a36Sopenharmony_ci{
417162306a36Sopenharmony_ci	struct cifs_ses *ses = tcon->ses;
417262306a36Sopenharmony_ci	struct smb_rqst rqst;
417362306a36Sopenharmony_ci	struct kvec iov[1];
417462306a36Sopenharmony_ci	struct kvec rsp_iov = {NULL, 0};
417562306a36Sopenharmony_ci	struct TCP_Server_Info *server = cifs_pick_channel(ses);
417662306a36Sopenharmony_ci	int resp_buftype = CIFS_NO_BUFFER;
417762306a36Sopenharmony_ci	int flags = 0;
417862306a36Sopenharmony_ci	int rc = 0;
417962306a36Sopenharmony_ci
418062306a36Sopenharmony_ci	cifs_dbg(FYI, "flush\n");
418162306a36Sopenharmony_ci	if (!ses || !(ses->server))
418262306a36Sopenharmony_ci		return -EIO;
418362306a36Sopenharmony_ci
418462306a36Sopenharmony_ci	if (smb3_encryption_required(tcon))
418562306a36Sopenharmony_ci		flags |= CIFS_TRANSFORM_REQ;
418662306a36Sopenharmony_ci
418762306a36Sopenharmony_ci	memset(&rqst, 0, sizeof(struct smb_rqst));
418862306a36Sopenharmony_ci	memset(&iov, 0, sizeof(iov));
418962306a36Sopenharmony_ci	rqst.rq_iov = iov;
419062306a36Sopenharmony_ci	rqst.rq_nvec = 1;
419162306a36Sopenharmony_ci
419262306a36Sopenharmony_ci	rc = SMB2_flush_init(xid, &rqst, tcon, server,
419362306a36Sopenharmony_ci			     persistent_fid, volatile_fid);
419462306a36Sopenharmony_ci	if (rc)
419562306a36Sopenharmony_ci		goto flush_exit;
419662306a36Sopenharmony_ci
419762306a36Sopenharmony_ci	trace_smb3_flush_enter(xid, persistent_fid, tcon->tid, ses->Suid);
419862306a36Sopenharmony_ci	rc = cifs_send_recv(xid, ses, server,
419962306a36Sopenharmony_ci			    &rqst, &resp_buftype, flags, &rsp_iov);
420062306a36Sopenharmony_ci
420162306a36Sopenharmony_ci	if (rc != 0) {
420262306a36Sopenharmony_ci		cifs_stats_fail_inc(tcon, SMB2_FLUSH_HE);
420362306a36Sopenharmony_ci		trace_smb3_flush_err(xid, persistent_fid, tcon->tid, ses->Suid,
420462306a36Sopenharmony_ci				     rc);
420562306a36Sopenharmony_ci	} else
420662306a36Sopenharmony_ci		trace_smb3_flush_done(xid, persistent_fid, tcon->tid,
420762306a36Sopenharmony_ci				      ses->Suid);
420862306a36Sopenharmony_ci
420962306a36Sopenharmony_ci flush_exit:
421062306a36Sopenharmony_ci	SMB2_flush_free(&rqst);
421162306a36Sopenharmony_ci	free_rsp_buf(resp_buftype, rsp_iov.iov_base);
421262306a36Sopenharmony_ci	return rc;
421362306a36Sopenharmony_ci}
421462306a36Sopenharmony_ci
421562306a36Sopenharmony_ci#ifdef CONFIG_CIFS_SMB_DIRECT
421662306a36Sopenharmony_cistatic inline bool smb3_use_rdma_offload(struct cifs_io_parms *io_parms)
421762306a36Sopenharmony_ci{
421862306a36Sopenharmony_ci	struct TCP_Server_Info *server = io_parms->server;
421962306a36Sopenharmony_ci	struct cifs_tcon *tcon = io_parms->tcon;
422062306a36Sopenharmony_ci
422162306a36Sopenharmony_ci	/* we can only offload if we're connected */
422262306a36Sopenharmony_ci	if (!server || !tcon)
422362306a36Sopenharmony_ci		return false;
422462306a36Sopenharmony_ci
422562306a36Sopenharmony_ci	/* we can only offload on an rdma connection */
422662306a36Sopenharmony_ci	if (!server->rdma || !server->smbd_conn)
422762306a36Sopenharmony_ci		return false;
422862306a36Sopenharmony_ci
422962306a36Sopenharmony_ci	/* we don't support signed offload yet */
423062306a36Sopenharmony_ci	if (server->sign)
423162306a36Sopenharmony_ci		return false;
423262306a36Sopenharmony_ci
423362306a36Sopenharmony_ci	/* we don't support encrypted offload yet */
423462306a36Sopenharmony_ci	if (smb3_encryption_required(tcon))
423562306a36Sopenharmony_ci		return false;
423662306a36Sopenharmony_ci
423762306a36Sopenharmony_ci	/* offload also has its overhead, so only do it if desired */
423862306a36Sopenharmony_ci	if (io_parms->length < server->smbd_conn->rdma_readwrite_threshold)
423962306a36Sopenharmony_ci		return false;
424062306a36Sopenharmony_ci
424162306a36Sopenharmony_ci	return true;
424262306a36Sopenharmony_ci}
424362306a36Sopenharmony_ci#endif /* CONFIG_CIFS_SMB_DIRECT */
424462306a36Sopenharmony_ci
424562306a36Sopenharmony_ci/*
424662306a36Sopenharmony_ci * To form a chain of read requests, any read requests after the first should
424762306a36Sopenharmony_ci * have the end_of_chain boolean set to true.
424862306a36Sopenharmony_ci */
424962306a36Sopenharmony_cistatic int
425062306a36Sopenharmony_cismb2_new_read_req(void **buf, unsigned int *total_len,
425162306a36Sopenharmony_ci	struct cifs_io_parms *io_parms, struct cifs_readdata *rdata,
425262306a36Sopenharmony_ci	unsigned int remaining_bytes, int request_type)
425362306a36Sopenharmony_ci{
425462306a36Sopenharmony_ci	int rc = -EACCES;
425562306a36Sopenharmony_ci	struct smb2_read_req *req = NULL;
425662306a36Sopenharmony_ci	struct smb2_hdr *shdr;
425762306a36Sopenharmony_ci	struct TCP_Server_Info *server = io_parms->server;
425862306a36Sopenharmony_ci
425962306a36Sopenharmony_ci	rc = smb2_plain_req_init(SMB2_READ, io_parms->tcon, server,
426062306a36Sopenharmony_ci				 (void **) &req, total_len);
426162306a36Sopenharmony_ci	if (rc)
426262306a36Sopenharmony_ci		return rc;
426362306a36Sopenharmony_ci
426462306a36Sopenharmony_ci	if (server == NULL)
426562306a36Sopenharmony_ci		return -ECONNABORTED;
426662306a36Sopenharmony_ci
426762306a36Sopenharmony_ci	shdr = &req->hdr;
426862306a36Sopenharmony_ci	shdr->Id.SyncId.ProcessId = cpu_to_le32(io_parms->pid);
426962306a36Sopenharmony_ci
427062306a36Sopenharmony_ci	req->PersistentFileId = io_parms->persistent_fid;
427162306a36Sopenharmony_ci	req->VolatileFileId = io_parms->volatile_fid;
427262306a36Sopenharmony_ci	req->ReadChannelInfoOffset = 0; /* reserved */
427362306a36Sopenharmony_ci	req->ReadChannelInfoLength = 0; /* reserved */
427462306a36Sopenharmony_ci	req->Channel = 0; /* reserved */
427562306a36Sopenharmony_ci	req->MinimumCount = 0;
427662306a36Sopenharmony_ci	req->Length = cpu_to_le32(io_parms->length);
427762306a36Sopenharmony_ci	req->Offset = cpu_to_le64(io_parms->offset);
427862306a36Sopenharmony_ci
427962306a36Sopenharmony_ci	trace_smb3_read_enter(0 /* xid */,
428062306a36Sopenharmony_ci			io_parms->persistent_fid,
428162306a36Sopenharmony_ci			io_parms->tcon->tid, io_parms->tcon->ses->Suid,
428262306a36Sopenharmony_ci			io_parms->offset, io_parms->length);
428362306a36Sopenharmony_ci#ifdef CONFIG_CIFS_SMB_DIRECT
428462306a36Sopenharmony_ci	/*
428562306a36Sopenharmony_ci	 * If we want to do a RDMA write, fill in and append
428662306a36Sopenharmony_ci	 * smbd_buffer_descriptor_v1 to the end of read request
428762306a36Sopenharmony_ci	 */
428862306a36Sopenharmony_ci	if (smb3_use_rdma_offload(io_parms)) {
428962306a36Sopenharmony_ci		struct smbd_buffer_descriptor_v1 *v1;
429062306a36Sopenharmony_ci		bool need_invalidate = server->dialect == SMB30_PROT_ID;
429162306a36Sopenharmony_ci
429262306a36Sopenharmony_ci		rdata->mr = smbd_register_mr(server->smbd_conn, &rdata->iter,
429362306a36Sopenharmony_ci					     true, need_invalidate);
429462306a36Sopenharmony_ci		if (!rdata->mr)
429562306a36Sopenharmony_ci			return -EAGAIN;
429662306a36Sopenharmony_ci
429762306a36Sopenharmony_ci		req->Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE;
429862306a36Sopenharmony_ci		if (need_invalidate)
429962306a36Sopenharmony_ci			req->Channel = SMB2_CHANNEL_RDMA_V1;
430062306a36Sopenharmony_ci		req->ReadChannelInfoOffset =
430162306a36Sopenharmony_ci			cpu_to_le16(offsetof(struct smb2_read_req, Buffer));
430262306a36Sopenharmony_ci		req->ReadChannelInfoLength =
430362306a36Sopenharmony_ci			cpu_to_le16(sizeof(struct smbd_buffer_descriptor_v1));
430462306a36Sopenharmony_ci		v1 = (struct smbd_buffer_descriptor_v1 *) &req->Buffer[0];
430562306a36Sopenharmony_ci		v1->offset = cpu_to_le64(rdata->mr->mr->iova);
430662306a36Sopenharmony_ci		v1->token = cpu_to_le32(rdata->mr->mr->rkey);
430762306a36Sopenharmony_ci		v1->length = cpu_to_le32(rdata->mr->mr->length);
430862306a36Sopenharmony_ci
430962306a36Sopenharmony_ci		*total_len += sizeof(*v1) - 1;
431062306a36Sopenharmony_ci	}
431162306a36Sopenharmony_ci#endif
431262306a36Sopenharmony_ci	if (request_type & CHAINED_REQUEST) {
431362306a36Sopenharmony_ci		if (!(request_type & END_OF_CHAIN)) {
431462306a36Sopenharmony_ci			/* next 8-byte aligned request */
431562306a36Sopenharmony_ci			*total_len = ALIGN(*total_len, 8);
431662306a36Sopenharmony_ci			shdr->NextCommand = cpu_to_le32(*total_len);
431762306a36Sopenharmony_ci		} else /* END_OF_CHAIN */
431862306a36Sopenharmony_ci			shdr->NextCommand = 0;
431962306a36Sopenharmony_ci		if (request_type & RELATED_REQUEST) {
432062306a36Sopenharmony_ci			shdr->Flags |= SMB2_FLAGS_RELATED_OPERATIONS;
432162306a36Sopenharmony_ci			/*
432262306a36Sopenharmony_ci			 * Related requests use info from previous read request
432362306a36Sopenharmony_ci			 * in chain.
432462306a36Sopenharmony_ci			 */
432562306a36Sopenharmony_ci			shdr->SessionId = cpu_to_le64(0xFFFFFFFFFFFFFFFF);
432662306a36Sopenharmony_ci			shdr->Id.SyncId.TreeId = cpu_to_le32(0xFFFFFFFF);
432762306a36Sopenharmony_ci			req->PersistentFileId = (u64)-1;
432862306a36Sopenharmony_ci			req->VolatileFileId = (u64)-1;
432962306a36Sopenharmony_ci		}
433062306a36Sopenharmony_ci	}
433162306a36Sopenharmony_ci	if (remaining_bytes > io_parms->length)
433262306a36Sopenharmony_ci		req->RemainingBytes = cpu_to_le32(remaining_bytes);
433362306a36Sopenharmony_ci	else
433462306a36Sopenharmony_ci		req->RemainingBytes = 0;
433562306a36Sopenharmony_ci
433662306a36Sopenharmony_ci	*buf = req;
433762306a36Sopenharmony_ci	return rc;
433862306a36Sopenharmony_ci}
433962306a36Sopenharmony_ci
434062306a36Sopenharmony_cistatic void
434162306a36Sopenharmony_cismb2_readv_callback(struct mid_q_entry *mid)
434262306a36Sopenharmony_ci{
434362306a36Sopenharmony_ci	struct cifs_readdata *rdata = mid->callback_data;
434462306a36Sopenharmony_ci	struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink);
434562306a36Sopenharmony_ci	struct TCP_Server_Info *server = rdata->server;
434662306a36Sopenharmony_ci	struct smb2_hdr *shdr =
434762306a36Sopenharmony_ci				(struct smb2_hdr *)rdata->iov[0].iov_base;
434862306a36Sopenharmony_ci	struct cifs_credits credits = { .value = 0, .instance = 0 };
434962306a36Sopenharmony_ci	struct smb_rqst rqst = { .rq_iov = &rdata->iov[1], .rq_nvec = 1 };
435062306a36Sopenharmony_ci
435162306a36Sopenharmony_ci	if (rdata->got_bytes) {
435262306a36Sopenharmony_ci		rqst.rq_iter	  = rdata->iter;
435362306a36Sopenharmony_ci		rqst.rq_iter_size = iov_iter_count(&rdata->iter);
435462306a36Sopenharmony_ci	}
435562306a36Sopenharmony_ci
435662306a36Sopenharmony_ci	WARN_ONCE(rdata->server != mid->server,
435762306a36Sopenharmony_ci		  "rdata server %p != mid server %p",
435862306a36Sopenharmony_ci		  rdata->server, mid->server);
435962306a36Sopenharmony_ci
436062306a36Sopenharmony_ci	cifs_dbg(FYI, "%s: mid=%llu state=%d result=%d bytes=%u\n",
436162306a36Sopenharmony_ci		 __func__, mid->mid, mid->mid_state, rdata->result,
436262306a36Sopenharmony_ci		 rdata->bytes);
436362306a36Sopenharmony_ci
436462306a36Sopenharmony_ci	switch (mid->mid_state) {
436562306a36Sopenharmony_ci	case MID_RESPONSE_RECEIVED:
436662306a36Sopenharmony_ci		credits.value = le16_to_cpu(shdr->CreditRequest);
436762306a36Sopenharmony_ci		credits.instance = server->reconnect_instance;
436862306a36Sopenharmony_ci		/* result already set, check signature */
436962306a36Sopenharmony_ci		if (server->sign && !mid->decrypted) {
437062306a36Sopenharmony_ci			int rc;
437162306a36Sopenharmony_ci
437262306a36Sopenharmony_ci			iov_iter_revert(&rqst.rq_iter, rdata->got_bytes);
437362306a36Sopenharmony_ci			iov_iter_truncate(&rqst.rq_iter, rdata->got_bytes);
437462306a36Sopenharmony_ci			rc = smb2_verify_signature(&rqst, server);
437562306a36Sopenharmony_ci			if (rc)
437662306a36Sopenharmony_ci				cifs_tcon_dbg(VFS, "SMB signature verification returned error = %d\n",
437762306a36Sopenharmony_ci					 rc);
437862306a36Sopenharmony_ci		}
437962306a36Sopenharmony_ci		/* FIXME: should this be counted toward the initiating task? */
438062306a36Sopenharmony_ci		task_io_account_read(rdata->got_bytes);
438162306a36Sopenharmony_ci		cifs_stats_bytes_read(tcon, rdata->got_bytes);
438262306a36Sopenharmony_ci		break;
438362306a36Sopenharmony_ci	case MID_REQUEST_SUBMITTED:
438462306a36Sopenharmony_ci	case MID_RETRY_NEEDED:
438562306a36Sopenharmony_ci		rdata->result = -EAGAIN;
438662306a36Sopenharmony_ci		if (server->sign && rdata->got_bytes)
438762306a36Sopenharmony_ci			/* reset bytes number since we can not check a sign */
438862306a36Sopenharmony_ci			rdata->got_bytes = 0;
438962306a36Sopenharmony_ci		/* FIXME: should this be counted toward the initiating task? */
439062306a36Sopenharmony_ci		task_io_account_read(rdata->got_bytes);
439162306a36Sopenharmony_ci		cifs_stats_bytes_read(tcon, rdata->got_bytes);
439262306a36Sopenharmony_ci		break;
439362306a36Sopenharmony_ci	case MID_RESPONSE_MALFORMED:
439462306a36Sopenharmony_ci		credits.value = le16_to_cpu(shdr->CreditRequest);
439562306a36Sopenharmony_ci		credits.instance = server->reconnect_instance;
439662306a36Sopenharmony_ci		fallthrough;
439762306a36Sopenharmony_ci	default:
439862306a36Sopenharmony_ci		rdata->result = -EIO;
439962306a36Sopenharmony_ci	}
440062306a36Sopenharmony_ci#ifdef CONFIG_CIFS_SMB_DIRECT
440162306a36Sopenharmony_ci	/*
440262306a36Sopenharmony_ci	 * If this rdata has a memmory registered, the MR can be freed
440362306a36Sopenharmony_ci	 * MR needs to be freed as soon as I/O finishes to prevent deadlock
440462306a36Sopenharmony_ci	 * because they have limited number and are used for future I/Os
440562306a36Sopenharmony_ci	 */
440662306a36Sopenharmony_ci	if (rdata->mr) {
440762306a36Sopenharmony_ci		smbd_deregister_mr(rdata->mr);
440862306a36Sopenharmony_ci		rdata->mr = NULL;
440962306a36Sopenharmony_ci	}
441062306a36Sopenharmony_ci#endif
441162306a36Sopenharmony_ci	if (rdata->result && rdata->result != -ENODATA) {
441262306a36Sopenharmony_ci		cifs_stats_fail_inc(tcon, SMB2_READ_HE);
441362306a36Sopenharmony_ci		trace_smb3_read_err(0 /* xid */,
441462306a36Sopenharmony_ci				    rdata->cfile->fid.persistent_fid,
441562306a36Sopenharmony_ci				    tcon->tid, tcon->ses->Suid, rdata->offset,
441662306a36Sopenharmony_ci				    rdata->bytes, rdata->result);
441762306a36Sopenharmony_ci	} else
441862306a36Sopenharmony_ci		trace_smb3_read_done(0 /* xid */,
441962306a36Sopenharmony_ci				     rdata->cfile->fid.persistent_fid,
442062306a36Sopenharmony_ci				     tcon->tid, tcon->ses->Suid,
442162306a36Sopenharmony_ci				     rdata->offset, rdata->got_bytes);
442262306a36Sopenharmony_ci
442362306a36Sopenharmony_ci	queue_work(cifsiod_wq, &rdata->work);
442462306a36Sopenharmony_ci	release_mid(mid);
442562306a36Sopenharmony_ci	add_credits(server, &credits, 0);
442662306a36Sopenharmony_ci}
442762306a36Sopenharmony_ci
442862306a36Sopenharmony_ci/* smb2_async_readv - send an async read, and set up mid to handle result */
442962306a36Sopenharmony_ciint
443062306a36Sopenharmony_cismb2_async_readv(struct cifs_readdata *rdata)
443162306a36Sopenharmony_ci{
443262306a36Sopenharmony_ci	int rc, flags = 0;
443362306a36Sopenharmony_ci	char *buf;
443462306a36Sopenharmony_ci	struct smb2_hdr *shdr;
443562306a36Sopenharmony_ci	struct cifs_io_parms io_parms;
443662306a36Sopenharmony_ci	struct smb_rqst rqst = { .rq_iov = rdata->iov,
443762306a36Sopenharmony_ci				 .rq_nvec = 1 };
443862306a36Sopenharmony_ci	struct TCP_Server_Info *server;
443962306a36Sopenharmony_ci	struct cifs_tcon *tcon = tlink_tcon(rdata->cfile->tlink);
444062306a36Sopenharmony_ci	unsigned int total_len;
444162306a36Sopenharmony_ci	int credit_request;
444262306a36Sopenharmony_ci
444362306a36Sopenharmony_ci	cifs_dbg(FYI, "%s: offset=%llu bytes=%u\n",
444462306a36Sopenharmony_ci		 __func__, rdata->offset, rdata->bytes);
444562306a36Sopenharmony_ci
444662306a36Sopenharmony_ci	if (!rdata->server)
444762306a36Sopenharmony_ci		rdata->server = cifs_pick_channel(tcon->ses);
444862306a36Sopenharmony_ci
444962306a36Sopenharmony_ci	io_parms.tcon = tlink_tcon(rdata->cfile->tlink);
445062306a36Sopenharmony_ci	io_parms.server = server = rdata->server;
445162306a36Sopenharmony_ci	io_parms.offset = rdata->offset;
445262306a36Sopenharmony_ci	io_parms.length = rdata->bytes;
445362306a36Sopenharmony_ci	io_parms.persistent_fid = rdata->cfile->fid.persistent_fid;
445462306a36Sopenharmony_ci	io_parms.volatile_fid = rdata->cfile->fid.volatile_fid;
445562306a36Sopenharmony_ci	io_parms.pid = rdata->pid;
445662306a36Sopenharmony_ci
445762306a36Sopenharmony_ci	rc = smb2_new_read_req(
445862306a36Sopenharmony_ci		(void **) &buf, &total_len, &io_parms, rdata, 0, 0);
445962306a36Sopenharmony_ci	if (rc)
446062306a36Sopenharmony_ci		return rc;
446162306a36Sopenharmony_ci
446262306a36Sopenharmony_ci	if (smb3_encryption_required(io_parms.tcon))
446362306a36Sopenharmony_ci		flags |= CIFS_TRANSFORM_REQ;
446462306a36Sopenharmony_ci
446562306a36Sopenharmony_ci	rdata->iov[0].iov_base = buf;
446662306a36Sopenharmony_ci	rdata->iov[0].iov_len = total_len;
446762306a36Sopenharmony_ci
446862306a36Sopenharmony_ci	shdr = (struct smb2_hdr *)buf;
446962306a36Sopenharmony_ci
447062306a36Sopenharmony_ci	if (rdata->credits.value > 0) {
447162306a36Sopenharmony_ci		shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(rdata->bytes,
447262306a36Sopenharmony_ci						SMB2_MAX_BUFFER_SIZE));
447362306a36Sopenharmony_ci		credit_request = le16_to_cpu(shdr->CreditCharge) + 8;
447462306a36Sopenharmony_ci		if (server->credits >= server->max_credits)
447562306a36Sopenharmony_ci			shdr->CreditRequest = cpu_to_le16(0);
447662306a36Sopenharmony_ci		else
447762306a36Sopenharmony_ci			shdr->CreditRequest = cpu_to_le16(
447862306a36Sopenharmony_ci				min_t(int, server->max_credits -
447962306a36Sopenharmony_ci						server->credits, credit_request));
448062306a36Sopenharmony_ci
448162306a36Sopenharmony_ci		rc = adjust_credits(server, &rdata->credits, rdata->bytes);
448262306a36Sopenharmony_ci		if (rc)
448362306a36Sopenharmony_ci			goto async_readv_out;
448462306a36Sopenharmony_ci
448562306a36Sopenharmony_ci		flags |= CIFS_HAS_CREDITS;
448662306a36Sopenharmony_ci	}
448762306a36Sopenharmony_ci
448862306a36Sopenharmony_ci	kref_get(&rdata->refcount);
448962306a36Sopenharmony_ci	rc = cifs_call_async(server, &rqst,
449062306a36Sopenharmony_ci			     cifs_readv_receive, smb2_readv_callback,
449162306a36Sopenharmony_ci			     smb3_handle_read_data, rdata, flags,
449262306a36Sopenharmony_ci			     &rdata->credits);
449362306a36Sopenharmony_ci	if (rc) {
449462306a36Sopenharmony_ci		kref_put(&rdata->refcount, cifs_readdata_release);
449562306a36Sopenharmony_ci		cifs_stats_fail_inc(io_parms.tcon, SMB2_READ_HE);
449662306a36Sopenharmony_ci		trace_smb3_read_err(0 /* xid */, io_parms.persistent_fid,
449762306a36Sopenharmony_ci				    io_parms.tcon->tid,
449862306a36Sopenharmony_ci				    io_parms.tcon->ses->Suid,
449962306a36Sopenharmony_ci				    io_parms.offset, io_parms.length, rc);
450062306a36Sopenharmony_ci	}
450162306a36Sopenharmony_ci
450262306a36Sopenharmony_ciasync_readv_out:
450362306a36Sopenharmony_ci	cifs_small_buf_release(buf);
450462306a36Sopenharmony_ci	return rc;
450562306a36Sopenharmony_ci}
450662306a36Sopenharmony_ci
450762306a36Sopenharmony_ciint
450862306a36Sopenharmony_ciSMB2_read(const unsigned int xid, struct cifs_io_parms *io_parms,
450962306a36Sopenharmony_ci	  unsigned int *nbytes, char **buf, int *buf_type)
451062306a36Sopenharmony_ci{
451162306a36Sopenharmony_ci	struct smb_rqst rqst;
451262306a36Sopenharmony_ci	int resp_buftype, rc;
451362306a36Sopenharmony_ci	struct smb2_read_req *req = NULL;
451462306a36Sopenharmony_ci	struct smb2_read_rsp *rsp = NULL;
451562306a36Sopenharmony_ci	struct kvec iov[1];
451662306a36Sopenharmony_ci	struct kvec rsp_iov;
451762306a36Sopenharmony_ci	unsigned int total_len;
451862306a36Sopenharmony_ci	int flags = CIFS_LOG_ERROR;
451962306a36Sopenharmony_ci	struct cifs_ses *ses = io_parms->tcon->ses;
452062306a36Sopenharmony_ci
452162306a36Sopenharmony_ci	if (!io_parms->server)
452262306a36Sopenharmony_ci		io_parms->server = cifs_pick_channel(io_parms->tcon->ses);
452362306a36Sopenharmony_ci
452462306a36Sopenharmony_ci	*nbytes = 0;
452562306a36Sopenharmony_ci	rc = smb2_new_read_req((void **)&req, &total_len, io_parms, NULL, 0, 0);
452662306a36Sopenharmony_ci	if (rc)
452762306a36Sopenharmony_ci		return rc;
452862306a36Sopenharmony_ci
452962306a36Sopenharmony_ci	if (smb3_encryption_required(io_parms->tcon))
453062306a36Sopenharmony_ci		flags |= CIFS_TRANSFORM_REQ;
453162306a36Sopenharmony_ci
453262306a36Sopenharmony_ci	iov[0].iov_base = (char *)req;
453362306a36Sopenharmony_ci	iov[0].iov_len = total_len;
453462306a36Sopenharmony_ci
453562306a36Sopenharmony_ci	memset(&rqst, 0, sizeof(struct smb_rqst));
453662306a36Sopenharmony_ci	rqst.rq_iov = iov;
453762306a36Sopenharmony_ci	rqst.rq_nvec = 1;
453862306a36Sopenharmony_ci
453962306a36Sopenharmony_ci	rc = cifs_send_recv(xid, ses, io_parms->server,
454062306a36Sopenharmony_ci			    &rqst, &resp_buftype, flags, &rsp_iov);
454162306a36Sopenharmony_ci	rsp = (struct smb2_read_rsp *)rsp_iov.iov_base;
454262306a36Sopenharmony_ci
454362306a36Sopenharmony_ci	if (rc) {
454462306a36Sopenharmony_ci		if (rc != -ENODATA) {
454562306a36Sopenharmony_ci			cifs_stats_fail_inc(io_parms->tcon, SMB2_READ_HE);
454662306a36Sopenharmony_ci			cifs_dbg(VFS, "Send error in read = %d\n", rc);
454762306a36Sopenharmony_ci			trace_smb3_read_err(xid,
454862306a36Sopenharmony_ci					    req->PersistentFileId,
454962306a36Sopenharmony_ci					    io_parms->tcon->tid, ses->Suid,
455062306a36Sopenharmony_ci					    io_parms->offset, io_parms->length,
455162306a36Sopenharmony_ci					    rc);
455262306a36Sopenharmony_ci		} else
455362306a36Sopenharmony_ci			trace_smb3_read_done(xid, req->PersistentFileId, io_parms->tcon->tid,
455462306a36Sopenharmony_ci					     ses->Suid, io_parms->offset, 0);
455562306a36Sopenharmony_ci		free_rsp_buf(resp_buftype, rsp_iov.iov_base);
455662306a36Sopenharmony_ci		cifs_small_buf_release(req);
455762306a36Sopenharmony_ci		return rc == -ENODATA ? 0 : rc;
455862306a36Sopenharmony_ci	} else
455962306a36Sopenharmony_ci		trace_smb3_read_done(xid,
456062306a36Sopenharmony_ci				    req->PersistentFileId,
456162306a36Sopenharmony_ci				    io_parms->tcon->tid, ses->Suid,
456262306a36Sopenharmony_ci				    io_parms->offset, io_parms->length);
456362306a36Sopenharmony_ci
456462306a36Sopenharmony_ci	cifs_small_buf_release(req);
456562306a36Sopenharmony_ci
456662306a36Sopenharmony_ci	*nbytes = le32_to_cpu(rsp->DataLength);
456762306a36Sopenharmony_ci	if ((*nbytes > CIFS_MAX_MSGSIZE) ||
456862306a36Sopenharmony_ci	    (*nbytes > io_parms->length)) {
456962306a36Sopenharmony_ci		cifs_dbg(FYI, "bad length %d for count %d\n",
457062306a36Sopenharmony_ci			 *nbytes, io_parms->length);
457162306a36Sopenharmony_ci		rc = -EIO;
457262306a36Sopenharmony_ci		*nbytes = 0;
457362306a36Sopenharmony_ci	}
457462306a36Sopenharmony_ci
457562306a36Sopenharmony_ci	if (*buf) {
457662306a36Sopenharmony_ci		memcpy(*buf, (char *)rsp + rsp->DataOffset, *nbytes);
457762306a36Sopenharmony_ci		free_rsp_buf(resp_buftype, rsp_iov.iov_base);
457862306a36Sopenharmony_ci	} else if (resp_buftype != CIFS_NO_BUFFER) {
457962306a36Sopenharmony_ci		*buf = rsp_iov.iov_base;
458062306a36Sopenharmony_ci		if (resp_buftype == CIFS_SMALL_BUFFER)
458162306a36Sopenharmony_ci			*buf_type = CIFS_SMALL_BUFFER;
458262306a36Sopenharmony_ci		else if (resp_buftype == CIFS_LARGE_BUFFER)
458362306a36Sopenharmony_ci			*buf_type = CIFS_LARGE_BUFFER;
458462306a36Sopenharmony_ci	}
458562306a36Sopenharmony_ci	return rc;
458662306a36Sopenharmony_ci}
458762306a36Sopenharmony_ci
458862306a36Sopenharmony_ci/*
458962306a36Sopenharmony_ci * Check the mid_state and signature on received buffer (if any), and queue the
459062306a36Sopenharmony_ci * workqueue completion task.
459162306a36Sopenharmony_ci */
459262306a36Sopenharmony_cistatic void
459362306a36Sopenharmony_cismb2_writev_callback(struct mid_q_entry *mid)
459462306a36Sopenharmony_ci{
459562306a36Sopenharmony_ci	struct cifs_writedata *wdata = mid->callback_data;
459662306a36Sopenharmony_ci	struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
459762306a36Sopenharmony_ci	struct TCP_Server_Info *server = wdata->server;
459862306a36Sopenharmony_ci	unsigned int written;
459962306a36Sopenharmony_ci	struct smb2_write_rsp *rsp = (struct smb2_write_rsp *)mid->resp_buf;
460062306a36Sopenharmony_ci	struct cifs_credits credits = { .value = 0, .instance = 0 };
460162306a36Sopenharmony_ci
460262306a36Sopenharmony_ci	WARN_ONCE(wdata->server != mid->server,
460362306a36Sopenharmony_ci		  "wdata server %p != mid server %p",
460462306a36Sopenharmony_ci		  wdata->server, mid->server);
460562306a36Sopenharmony_ci
460662306a36Sopenharmony_ci	switch (mid->mid_state) {
460762306a36Sopenharmony_ci	case MID_RESPONSE_RECEIVED:
460862306a36Sopenharmony_ci		credits.value = le16_to_cpu(rsp->hdr.CreditRequest);
460962306a36Sopenharmony_ci		credits.instance = server->reconnect_instance;
461062306a36Sopenharmony_ci		wdata->result = smb2_check_receive(mid, server, 0);
461162306a36Sopenharmony_ci		if (wdata->result != 0)
461262306a36Sopenharmony_ci			break;
461362306a36Sopenharmony_ci
461462306a36Sopenharmony_ci		written = le32_to_cpu(rsp->DataLength);
461562306a36Sopenharmony_ci		/*
461662306a36Sopenharmony_ci		 * Mask off high 16 bits when bytes written as returned
461762306a36Sopenharmony_ci		 * by the server is greater than bytes requested by the
461862306a36Sopenharmony_ci		 * client. OS/2 servers are known to set incorrect
461962306a36Sopenharmony_ci		 * CountHigh values.
462062306a36Sopenharmony_ci		 */
462162306a36Sopenharmony_ci		if (written > wdata->bytes)
462262306a36Sopenharmony_ci			written &= 0xFFFF;
462362306a36Sopenharmony_ci
462462306a36Sopenharmony_ci		if (written < wdata->bytes)
462562306a36Sopenharmony_ci			wdata->result = -ENOSPC;
462662306a36Sopenharmony_ci		else
462762306a36Sopenharmony_ci			wdata->bytes = written;
462862306a36Sopenharmony_ci		break;
462962306a36Sopenharmony_ci	case MID_REQUEST_SUBMITTED:
463062306a36Sopenharmony_ci	case MID_RETRY_NEEDED:
463162306a36Sopenharmony_ci		wdata->result = -EAGAIN;
463262306a36Sopenharmony_ci		break;
463362306a36Sopenharmony_ci	case MID_RESPONSE_MALFORMED:
463462306a36Sopenharmony_ci		credits.value = le16_to_cpu(rsp->hdr.CreditRequest);
463562306a36Sopenharmony_ci		credits.instance = server->reconnect_instance;
463662306a36Sopenharmony_ci		fallthrough;
463762306a36Sopenharmony_ci	default:
463862306a36Sopenharmony_ci		wdata->result = -EIO;
463962306a36Sopenharmony_ci		break;
464062306a36Sopenharmony_ci	}
464162306a36Sopenharmony_ci#ifdef CONFIG_CIFS_SMB_DIRECT
464262306a36Sopenharmony_ci	/*
464362306a36Sopenharmony_ci	 * If this wdata has a memory registered, the MR can be freed
464462306a36Sopenharmony_ci	 * The number of MRs available is limited, it's important to recover
464562306a36Sopenharmony_ci	 * used MR as soon as I/O is finished. Hold MR longer in the later
464662306a36Sopenharmony_ci	 * I/O process can possibly result in I/O deadlock due to lack of MR
464762306a36Sopenharmony_ci	 * to send request on I/O retry
464862306a36Sopenharmony_ci	 */
464962306a36Sopenharmony_ci	if (wdata->mr) {
465062306a36Sopenharmony_ci		smbd_deregister_mr(wdata->mr);
465162306a36Sopenharmony_ci		wdata->mr = NULL;
465262306a36Sopenharmony_ci	}
465362306a36Sopenharmony_ci#endif
465462306a36Sopenharmony_ci	if (wdata->result) {
465562306a36Sopenharmony_ci		cifs_stats_fail_inc(tcon, SMB2_WRITE_HE);
465662306a36Sopenharmony_ci		trace_smb3_write_err(0 /* no xid */,
465762306a36Sopenharmony_ci				     wdata->cfile->fid.persistent_fid,
465862306a36Sopenharmony_ci				     tcon->tid, tcon->ses->Suid, wdata->offset,
465962306a36Sopenharmony_ci				     wdata->bytes, wdata->result);
466062306a36Sopenharmony_ci		if (wdata->result == -ENOSPC)
466162306a36Sopenharmony_ci			pr_warn_once("Out of space writing to %s\n",
466262306a36Sopenharmony_ci				     tcon->tree_name);
466362306a36Sopenharmony_ci	} else
466462306a36Sopenharmony_ci		trace_smb3_write_done(0 /* no xid */,
466562306a36Sopenharmony_ci				      wdata->cfile->fid.persistent_fid,
466662306a36Sopenharmony_ci				      tcon->tid, tcon->ses->Suid,
466762306a36Sopenharmony_ci				      wdata->offset, wdata->bytes);
466862306a36Sopenharmony_ci
466962306a36Sopenharmony_ci	queue_work(cifsiod_wq, &wdata->work);
467062306a36Sopenharmony_ci	release_mid(mid);
467162306a36Sopenharmony_ci	add_credits(server, &credits, 0);
467262306a36Sopenharmony_ci}
467362306a36Sopenharmony_ci
467462306a36Sopenharmony_ci/* smb2_async_writev - send an async write, and set up mid to handle result */
467562306a36Sopenharmony_ciint
467662306a36Sopenharmony_cismb2_async_writev(struct cifs_writedata *wdata,
467762306a36Sopenharmony_ci		  void (*release)(struct kref *kref))
467862306a36Sopenharmony_ci{
467962306a36Sopenharmony_ci	int rc = -EACCES, flags = 0;
468062306a36Sopenharmony_ci	struct smb2_write_req *req = NULL;
468162306a36Sopenharmony_ci	struct smb2_hdr *shdr;
468262306a36Sopenharmony_ci	struct cifs_tcon *tcon = tlink_tcon(wdata->cfile->tlink);
468362306a36Sopenharmony_ci	struct TCP_Server_Info *server = wdata->server;
468462306a36Sopenharmony_ci	struct kvec iov[1];
468562306a36Sopenharmony_ci	struct smb_rqst rqst = { };
468662306a36Sopenharmony_ci	unsigned int total_len;
468762306a36Sopenharmony_ci	struct cifs_io_parms _io_parms;
468862306a36Sopenharmony_ci	struct cifs_io_parms *io_parms = NULL;
468962306a36Sopenharmony_ci	int credit_request;
469062306a36Sopenharmony_ci
469162306a36Sopenharmony_ci	if (!wdata->server)
469262306a36Sopenharmony_ci		server = wdata->server = cifs_pick_channel(tcon->ses);
469362306a36Sopenharmony_ci
469462306a36Sopenharmony_ci	/*
469562306a36Sopenharmony_ci	 * in future we may get cifs_io_parms passed in from the caller,
469662306a36Sopenharmony_ci	 * but for now we construct it here...
469762306a36Sopenharmony_ci	 */
469862306a36Sopenharmony_ci	_io_parms = (struct cifs_io_parms) {
469962306a36Sopenharmony_ci		.tcon = tcon,
470062306a36Sopenharmony_ci		.server = server,
470162306a36Sopenharmony_ci		.offset = wdata->offset,
470262306a36Sopenharmony_ci		.length = wdata->bytes,
470362306a36Sopenharmony_ci		.persistent_fid = wdata->cfile->fid.persistent_fid,
470462306a36Sopenharmony_ci		.volatile_fid = wdata->cfile->fid.volatile_fid,
470562306a36Sopenharmony_ci		.pid = wdata->pid,
470662306a36Sopenharmony_ci	};
470762306a36Sopenharmony_ci	io_parms = &_io_parms;
470862306a36Sopenharmony_ci
470962306a36Sopenharmony_ci	rc = smb2_plain_req_init(SMB2_WRITE, tcon, server,
471062306a36Sopenharmony_ci				 (void **) &req, &total_len);
471162306a36Sopenharmony_ci	if (rc)
471262306a36Sopenharmony_ci		return rc;
471362306a36Sopenharmony_ci
471462306a36Sopenharmony_ci	if (smb3_encryption_required(tcon))
471562306a36Sopenharmony_ci		flags |= CIFS_TRANSFORM_REQ;
471662306a36Sopenharmony_ci
471762306a36Sopenharmony_ci	shdr = (struct smb2_hdr *)req;
471862306a36Sopenharmony_ci	shdr->Id.SyncId.ProcessId = cpu_to_le32(io_parms->pid);
471962306a36Sopenharmony_ci
472062306a36Sopenharmony_ci	req->PersistentFileId = io_parms->persistent_fid;
472162306a36Sopenharmony_ci	req->VolatileFileId = io_parms->volatile_fid;
472262306a36Sopenharmony_ci	req->WriteChannelInfoOffset = 0;
472362306a36Sopenharmony_ci	req->WriteChannelInfoLength = 0;
472462306a36Sopenharmony_ci	req->Channel = SMB2_CHANNEL_NONE;
472562306a36Sopenharmony_ci	req->Offset = cpu_to_le64(io_parms->offset);
472662306a36Sopenharmony_ci	req->DataOffset = cpu_to_le16(
472762306a36Sopenharmony_ci				offsetof(struct smb2_write_req, Buffer));
472862306a36Sopenharmony_ci	req->RemainingBytes = 0;
472962306a36Sopenharmony_ci
473062306a36Sopenharmony_ci	trace_smb3_write_enter(0 /* xid */,
473162306a36Sopenharmony_ci			       io_parms->persistent_fid,
473262306a36Sopenharmony_ci			       io_parms->tcon->tid,
473362306a36Sopenharmony_ci			       io_parms->tcon->ses->Suid,
473462306a36Sopenharmony_ci			       io_parms->offset,
473562306a36Sopenharmony_ci			       io_parms->length);
473662306a36Sopenharmony_ci
473762306a36Sopenharmony_ci#ifdef CONFIG_CIFS_SMB_DIRECT
473862306a36Sopenharmony_ci	/*
473962306a36Sopenharmony_ci	 * If we want to do a server RDMA read, fill in and append
474062306a36Sopenharmony_ci	 * smbd_buffer_descriptor_v1 to the end of write request
474162306a36Sopenharmony_ci	 */
474262306a36Sopenharmony_ci	if (smb3_use_rdma_offload(io_parms)) {
474362306a36Sopenharmony_ci		struct smbd_buffer_descriptor_v1 *v1;
474462306a36Sopenharmony_ci		size_t data_size = iov_iter_count(&wdata->iter);
474562306a36Sopenharmony_ci		bool need_invalidate = server->dialect == SMB30_PROT_ID;
474662306a36Sopenharmony_ci
474762306a36Sopenharmony_ci		wdata->mr = smbd_register_mr(server->smbd_conn, &wdata->iter,
474862306a36Sopenharmony_ci					     false, need_invalidate);
474962306a36Sopenharmony_ci		if (!wdata->mr) {
475062306a36Sopenharmony_ci			rc = -EAGAIN;
475162306a36Sopenharmony_ci			goto async_writev_out;
475262306a36Sopenharmony_ci		}
475362306a36Sopenharmony_ci		req->Length = 0;
475462306a36Sopenharmony_ci		req->DataOffset = 0;
475562306a36Sopenharmony_ci		req->RemainingBytes = cpu_to_le32(data_size);
475662306a36Sopenharmony_ci		req->Channel = SMB2_CHANNEL_RDMA_V1_INVALIDATE;
475762306a36Sopenharmony_ci		if (need_invalidate)
475862306a36Sopenharmony_ci			req->Channel = SMB2_CHANNEL_RDMA_V1;
475962306a36Sopenharmony_ci		req->WriteChannelInfoOffset =
476062306a36Sopenharmony_ci			cpu_to_le16(offsetof(struct smb2_write_req, Buffer));
476162306a36Sopenharmony_ci		req->WriteChannelInfoLength =
476262306a36Sopenharmony_ci			cpu_to_le16(sizeof(struct smbd_buffer_descriptor_v1));
476362306a36Sopenharmony_ci		v1 = (struct smbd_buffer_descriptor_v1 *) &req->Buffer[0];
476462306a36Sopenharmony_ci		v1->offset = cpu_to_le64(wdata->mr->mr->iova);
476562306a36Sopenharmony_ci		v1->token = cpu_to_le32(wdata->mr->mr->rkey);
476662306a36Sopenharmony_ci		v1->length = cpu_to_le32(wdata->mr->mr->length);
476762306a36Sopenharmony_ci	}
476862306a36Sopenharmony_ci#endif
476962306a36Sopenharmony_ci	iov[0].iov_len = total_len - 1;
477062306a36Sopenharmony_ci	iov[0].iov_base = (char *)req;
477162306a36Sopenharmony_ci
477262306a36Sopenharmony_ci	rqst.rq_iov = iov;
477362306a36Sopenharmony_ci	rqst.rq_nvec = 1;
477462306a36Sopenharmony_ci	rqst.rq_iter = wdata->iter;
477562306a36Sopenharmony_ci	rqst.rq_iter_size = iov_iter_count(&rqst.rq_iter);
477662306a36Sopenharmony_ci#ifdef CONFIG_CIFS_SMB_DIRECT
477762306a36Sopenharmony_ci	if (wdata->mr)
477862306a36Sopenharmony_ci		iov[0].iov_len += sizeof(struct smbd_buffer_descriptor_v1);
477962306a36Sopenharmony_ci#endif
478062306a36Sopenharmony_ci	cifs_dbg(FYI, "async write at %llu %u bytes iter=%zx\n",
478162306a36Sopenharmony_ci		 io_parms->offset, io_parms->length, iov_iter_count(&rqst.rq_iter));
478262306a36Sopenharmony_ci
478362306a36Sopenharmony_ci#ifdef CONFIG_CIFS_SMB_DIRECT
478462306a36Sopenharmony_ci	/* For RDMA read, I/O size is in RemainingBytes not in Length */
478562306a36Sopenharmony_ci	if (!wdata->mr)
478662306a36Sopenharmony_ci		req->Length = cpu_to_le32(io_parms->length);
478762306a36Sopenharmony_ci#else
478862306a36Sopenharmony_ci	req->Length = cpu_to_le32(io_parms->length);
478962306a36Sopenharmony_ci#endif
479062306a36Sopenharmony_ci
479162306a36Sopenharmony_ci	if (wdata->credits.value > 0) {
479262306a36Sopenharmony_ci		shdr->CreditCharge = cpu_to_le16(DIV_ROUND_UP(wdata->bytes,
479362306a36Sopenharmony_ci						    SMB2_MAX_BUFFER_SIZE));
479462306a36Sopenharmony_ci		credit_request = le16_to_cpu(shdr->CreditCharge) + 8;
479562306a36Sopenharmony_ci		if (server->credits >= server->max_credits)
479662306a36Sopenharmony_ci			shdr->CreditRequest = cpu_to_le16(0);
479762306a36Sopenharmony_ci		else
479862306a36Sopenharmony_ci			shdr->CreditRequest = cpu_to_le16(
479962306a36Sopenharmony_ci				min_t(int, server->max_credits -
480062306a36Sopenharmony_ci						server->credits, credit_request));
480162306a36Sopenharmony_ci
480262306a36Sopenharmony_ci		rc = adjust_credits(server, &wdata->credits, io_parms->length);
480362306a36Sopenharmony_ci		if (rc)
480462306a36Sopenharmony_ci			goto async_writev_out;
480562306a36Sopenharmony_ci
480662306a36Sopenharmony_ci		flags |= CIFS_HAS_CREDITS;
480762306a36Sopenharmony_ci	}
480862306a36Sopenharmony_ci
480962306a36Sopenharmony_ci	kref_get(&wdata->refcount);
481062306a36Sopenharmony_ci	rc = cifs_call_async(server, &rqst, NULL, smb2_writev_callback, NULL,
481162306a36Sopenharmony_ci			     wdata, flags, &wdata->credits);
481262306a36Sopenharmony_ci
481362306a36Sopenharmony_ci	if (rc) {
481462306a36Sopenharmony_ci		trace_smb3_write_err(0 /* no xid */,
481562306a36Sopenharmony_ci				     io_parms->persistent_fid,
481662306a36Sopenharmony_ci				     io_parms->tcon->tid,
481762306a36Sopenharmony_ci				     io_parms->tcon->ses->Suid,
481862306a36Sopenharmony_ci				     io_parms->offset,
481962306a36Sopenharmony_ci				     io_parms->length,
482062306a36Sopenharmony_ci				     rc);
482162306a36Sopenharmony_ci		kref_put(&wdata->refcount, release);
482262306a36Sopenharmony_ci		cifs_stats_fail_inc(tcon, SMB2_WRITE_HE);
482362306a36Sopenharmony_ci	}
482462306a36Sopenharmony_ci
482562306a36Sopenharmony_ciasync_writev_out:
482662306a36Sopenharmony_ci	cifs_small_buf_release(req);
482762306a36Sopenharmony_ci	return rc;
482862306a36Sopenharmony_ci}
482962306a36Sopenharmony_ci
483062306a36Sopenharmony_ci/*
483162306a36Sopenharmony_ci * SMB2_write function gets iov pointer to kvec array with n_vec as a length.
483262306a36Sopenharmony_ci * The length field from io_parms must be at least 1 and indicates a number of
483362306a36Sopenharmony_ci * elements with data to write that begins with position 1 in iov array. All
483462306a36Sopenharmony_ci * data length is specified by count.
483562306a36Sopenharmony_ci */
483662306a36Sopenharmony_ciint
483762306a36Sopenharmony_ciSMB2_write(const unsigned int xid, struct cifs_io_parms *io_parms,
483862306a36Sopenharmony_ci	   unsigned int *nbytes, struct kvec *iov, int n_vec)
483962306a36Sopenharmony_ci{
484062306a36Sopenharmony_ci	struct smb_rqst rqst;
484162306a36Sopenharmony_ci	int rc = 0;
484262306a36Sopenharmony_ci	struct smb2_write_req *req = NULL;
484362306a36Sopenharmony_ci	struct smb2_write_rsp *rsp = NULL;
484462306a36Sopenharmony_ci	int resp_buftype;
484562306a36Sopenharmony_ci	struct kvec rsp_iov;
484662306a36Sopenharmony_ci	int flags = 0;
484762306a36Sopenharmony_ci	unsigned int total_len;
484862306a36Sopenharmony_ci	struct TCP_Server_Info *server;
484962306a36Sopenharmony_ci
485062306a36Sopenharmony_ci	*nbytes = 0;
485162306a36Sopenharmony_ci
485262306a36Sopenharmony_ci	if (n_vec < 1)
485362306a36Sopenharmony_ci		return rc;
485462306a36Sopenharmony_ci
485562306a36Sopenharmony_ci	if (!io_parms->server)
485662306a36Sopenharmony_ci		io_parms->server = cifs_pick_channel(io_parms->tcon->ses);
485762306a36Sopenharmony_ci	server = io_parms->server;
485862306a36Sopenharmony_ci	if (server == NULL)
485962306a36Sopenharmony_ci		return -ECONNABORTED;
486062306a36Sopenharmony_ci
486162306a36Sopenharmony_ci	rc = smb2_plain_req_init(SMB2_WRITE, io_parms->tcon, server,
486262306a36Sopenharmony_ci				 (void **) &req, &total_len);
486362306a36Sopenharmony_ci	if (rc)
486462306a36Sopenharmony_ci		return rc;
486562306a36Sopenharmony_ci
486662306a36Sopenharmony_ci	if (smb3_encryption_required(io_parms->tcon))
486762306a36Sopenharmony_ci		flags |= CIFS_TRANSFORM_REQ;
486862306a36Sopenharmony_ci
486962306a36Sopenharmony_ci	req->hdr.Id.SyncId.ProcessId = cpu_to_le32(io_parms->pid);
487062306a36Sopenharmony_ci
487162306a36Sopenharmony_ci	req->PersistentFileId = io_parms->persistent_fid;
487262306a36Sopenharmony_ci	req->VolatileFileId = io_parms->volatile_fid;
487362306a36Sopenharmony_ci	req->WriteChannelInfoOffset = 0;
487462306a36Sopenharmony_ci	req->WriteChannelInfoLength = 0;
487562306a36Sopenharmony_ci	req->Channel = 0;
487662306a36Sopenharmony_ci	req->Length = cpu_to_le32(io_parms->length);
487762306a36Sopenharmony_ci	req->Offset = cpu_to_le64(io_parms->offset);
487862306a36Sopenharmony_ci	req->DataOffset = cpu_to_le16(
487962306a36Sopenharmony_ci				offsetof(struct smb2_write_req, Buffer));
488062306a36Sopenharmony_ci	req->RemainingBytes = 0;
488162306a36Sopenharmony_ci
488262306a36Sopenharmony_ci	trace_smb3_write_enter(xid, io_parms->persistent_fid,
488362306a36Sopenharmony_ci		io_parms->tcon->tid, io_parms->tcon->ses->Suid,
488462306a36Sopenharmony_ci		io_parms->offset, io_parms->length);
488562306a36Sopenharmony_ci
488662306a36Sopenharmony_ci	iov[0].iov_base = (char *)req;
488762306a36Sopenharmony_ci	/* 1 for Buffer */
488862306a36Sopenharmony_ci	iov[0].iov_len = total_len - 1;
488962306a36Sopenharmony_ci
489062306a36Sopenharmony_ci	memset(&rqst, 0, sizeof(struct smb_rqst));
489162306a36Sopenharmony_ci	rqst.rq_iov = iov;
489262306a36Sopenharmony_ci	rqst.rq_nvec = n_vec + 1;
489362306a36Sopenharmony_ci
489462306a36Sopenharmony_ci	rc = cifs_send_recv(xid, io_parms->tcon->ses, server,
489562306a36Sopenharmony_ci			    &rqst,
489662306a36Sopenharmony_ci			    &resp_buftype, flags, &rsp_iov);
489762306a36Sopenharmony_ci	rsp = (struct smb2_write_rsp *)rsp_iov.iov_base;
489862306a36Sopenharmony_ci
489962306a36Sopenharmony_ci	if (rc) {
490062306a36Sopenharmony_ci		trace_smb3_write_err(xid,
490162306a36Sopenharmony_ci				     req->PersistentFileId,
490262306a36Sopenharmony_ci				     io_parms->tcon->tid,
490362306a36Sopenharmony_ci				     io_parms->tcon->ses->Suid,
490462306a36Sopenharmony_ci				     io_parms->offset, io_parms->length, rc);
490562306a36Sopenharmony_ci		cifs_stats_fail_inc(io_parms->tcon, SMB2_WRITE_HE);
490662306a36Sopenharmony_ci		cifs_dbg(VFS, "Send error in write = %d\n", rc);
490762306a36Sopenharmony_ci	} else {
490862306a36Sopenharmony_ci		*nbytes = le32_to_cpu(rsp->DataLength);
490962306a36Sopenharmony_ci		trace_smb3_write_done(xid,
491062306a36Sopenharmony_ci				      req->PersistentFileId,
491162306a36Sopenharmony_ci				      io_parms->tcon->tid,
491262306a36Sopenharmony_ci				      io_parms->tcon->ses->Suid,
491362306a36Sopenharmony_ci				      io_parms->offset, *nbytes);
491462306a36Sopenharmony_ci	}
491562306a36Sopenharmony_ci
491662306a36Sopenharmony_ci	cifs_small_buf_release(req);
491762306a36Sopenharmony_ci	free_rsp_buf(resp_buftype, rsp);
491862306a36Sopenharmony_ci	return rc;
491962306a36Sopenharmony_ci}
492062306a36Sopenharmony_ci
492162306a36Sopenharmony_ciint posix_info_sid_size(const void *beg, const void *end)
492262306a36Sopenharmony_ci{
492362306a36Sopenharmony_ci	size_t subauth;
492462306a36Sopenharmony_ci	int total;
492562306a36Sopenharmony_ci
492662306a36Sopenharmony_ci	if (beg + 1 > end)
492762306a36Sopenharmony_ci		return -1;
492862306a36Sopenharmony_ci
492962306a36Sopenharmony_ci	subauth = *(u8 *)(beg+1);
493062306a36Sopenharmony_ci	if (subauth < 1 || subauth > 15)
493162306a36Sopenharmony_ci		return -1;
493262306a36Sopenharmony_ci
493362306a36Sopenharmony_ci	total = 1 + 1 + 6 + 4*subauth;
493462306a36Sopenharmony_ci	if (beg + total > end)
493562306a36Sopenharmony_ci		return -1;
493662306a36Sopenharmony_ci
493762306a36Sopenharmony_ci	return total;
493862306a36Sopenharmony_ci}
493962306a36Sopenharmony_ci
494062306a36Sopenharmony_ciint posix_info_parse(const void *beg, const void *end,
494162306a36Sopenharmony_ci		     struct smb2_posix_info_parsed *out)
494262306a36Sopenharmony_ci
494362306a36Sopenharmony_ci{
494462306a36Sopenharmony_ci	int total_len = 0;
494562306a36Sopenharmony_ci	int owner_len, group_len;
494662306a36Sopenharmony_ci	int name_len;
494762306a36Sopenharmony_ci	const void *owner_sid;
494862306a36Sopenharmony_ci	const void *group_sid;
494962306a36Sopenharmony_ci	const void *name;
495062306a36Sopenharmony_ci
495162306a36Sopenharmony_ci	/* if no end bound given, assume payload to be correct */
495262306a36Sopenharmony_ci	if (!end) {
495362306a36Sopenharmony_ci		const struct smb2_posix_info *p = beg;
495462306a36Sopenharmony_ci
495562306a36Sopenharmony_ci		end = beg + le32_to_cpu(p->NextEntryOffset);
495662306a36Sopenharmony_ci		/* last element will have a 0 offset, pick a sensible bound */
495762306a36Sopenharmony_ci		if (end == beg)
495862306a36Sopenharmony_ci			end += 0xFFFF;
495962306a36Sopenharmony_ci	}
496062306a36Sopenharmony_ci
496162306a36Sopenharmony_ci	/* check base buf */
496262306a36Sopenharmony_ci	if (beg + sizeof(struct smb2_posix_info) > end)
496362306a36Sopenharmony_ci		return -1;
496462306a36Sopenharmony_ci	total_len = sizeof(struct smb2_posix_info);
496562306a36Sopenharmony_ci
496662306a36Sopenharmony_ci	/* check owner sid */
496762306a36Sopenharmony_ci	owner_sid = beg + total_len;
496862306a36Sopenharmony_ci	owner_len = posix_info_sid_size(owner_sid, end);
496962306a36Sopenharmony_ci	if (owner_len < 0)
497062306a36Sopenharmony_ci		return -1;
497162306a36Sopenharmony_ci	total_len += owner_len;
497262306a36Sopenharmony_ci
497362306a36Sopenharmony_ci	/* check group sid */
497462306a36Sopenharmony_ci	group_sid = beg + total_len;
497562306a36Sopenharmony_ci	group_len = posix_info_sid_size(group_sid, end);
497662306a36Sopenharmony_ci	if (group_len < 0)
497762306a36Sopenharmony_ci		return -1;
497862306a36Sopenharmony_ci	total_len += group_len;
497962306a36Sopenharmony_ci
498062306a36Sopenharmony_ci	/* check name len */
498162306a36Sopenharmony_ci	if (beg + total_len + 4 > end)
498262306a36Sopenharmony_ci		return -1;
498362306a36Sopenharmony_ci	name_len = le32_to_cpu(*(__le32 *)(beg + total_len));
498462306a36Sopenharmony_ci	if (name_len < 1 || name_len > 0xFFFF)
498562306a36Sopenharmony_ci		return -1;
498662306a36Sopenharmony_ci	total_len += 4;
498762306a36Sopenharmony_ci
498862306a36Sopenharmony_ci	/* check name */
498962306a36Sopenharmony_ci	name = beg + total_len;
499062306a36Sopenharmony_ci	if (name + name_len > end)
499162306a36Sopenharmony_ci		return -1;
499262306a36Sopenharmony_ci	total_len += name_len;
499362306a36Sopenharmony_ci
499462306a36Sopenharmony_ci	if (out) {
499562306a36Sopenharmony_ci		out->base = beg;
499662306a36Sopenharmony_ci		out->size = total_len;
499762306a36Sopenharmony_ci		out->name_len = name_len;
499862306a36Sopenharmony_ci		out->name = name;
499962306a36Sopenharmony_ci		memcpy(&out->owner, owner_sid, owner_len);
500062306a36Sopenharmony_ci		memcpy(&out->group, group_sid, group_len);
500162306a36Sopenharmony_ci	}
500262306a36Sopenharmony_ci	return total_len;
500362306a36Sopenharmony_ci}
500462306a36Sopenharmony_ci
500562306a36Sopenharmony_cistatic int posix_info_extra_size(const void *beg, const void *end)
500662306a36Sopenharmony_ci{
500762306a36Sopenharmony_ci	int len = posix_info_parse(beg, end, NULL);
500862306a36Sopenharmony_ci
500962306a36Sopenharmony_ci	if (len < 0)
501062306a36Sopenharmony_ci		return -1;
501162306a36Sopenharmony_ci	return len - sizeof(struct smb2_posix_info);
501262306a36Sopenharmony_ci}
501362306a36Sopenharmony_ci
501462306a36Sopenharmony_cistatic unsigned int
501562306a36Sopenharmony_cinum_entries(int infotype, char *bufstart, char *end_of_buf, char **lastentry,
501662306a36Sopenharmony_ci	    size_t size)
501762306a36Sopenharmony_ci{
501862306a36Sopenharmony_ci	int len;
501962306a36Sopenharmony_ci	unsigned int entrycount = 0;
502062306a36Sopenharmony_ci	unsigned int next_offset = 0;
502162306a36Sopenharmony_ci	char *entryptr;
502262306a36Sopenharmony_ci	FILE_DIRECTORY_INFO *dir_info;
502362306a36Sopenharmony_ci
502462306a36Sopenharmony_ci	if (bufstart == NULL)
502562306a36Sopenharmony_ci		return 0;
502662306a36Sopenharmony_ci
502762306a36Sopenharmony_ci	entryptr = bufstart;
502862306a36Sopenharmony_ci
502962306a36Sopenharmony_ci	while (1) {
503062306a36Sopenharmony_ci		if (entryptr + next_offset < entryptr ||
503162306a36Sopenharmony_ci		    entryptr + next_offset > end_of_buf ||
503262306a36Sopenharmony_ci		    entryptr + next_offset + size > end_of_buf) {
503362306a36Sopenharmony_ci			cifs_dbg(VFS, "malformed search entry would overflow\n");
503462306a36Sopenharmony_ci			break;
503562306a36Sopenharmony_ci		}
503662306a36Sopenharmony_ci
503762306a36Sopenharmony_ci		entryptr = entryptr + next_offset;
503862306a36Sopenharmony_ci		dir_info = (FILE_DIRECTORY_INFO *)entryptr;
503962306a36Sopenharmony_ci
504062306a36Sopenharmony_ci		if (infotype == SMB_FIND_FILE_POSIX_INFO)
504162306a36Sopenharmony_ci			len = posix_info_extra_size(entryptr, end_of_buf);
504262306a36Sopenharmony_ci		else
504362306a36Sopenharmony_ci			len = le32_to_cpu(dir_info->FileNameLength);
504462306a36Sopenharmony_ci
504562306a36Sopenharmony_ci		if (len < 0 ||
504662306a36Sopenharmony_ci		    entryptr + len < entryptr ||
504762306a36Sopenharmony_ci		    entryptr + len > end_of_buf ||
504862306a36Sopenharmony_ci		    entryptr + len + size > end_of_buf) {
504962306a36Sopenharmony_ci			cifs_dbg(VFS, "directory entry name would overflow frame end of buf %p\n",
505062306a36Sopenharmony_ci				 end_of_buf);
505162306a36Sopenharmony_ci			break;
505262306a36Sopenharmony_ci		}
505362306a36Sopenharmony_ci
505462306a36Sopenharmony_ci		*lastentry = entryptr;
505562306a36Sopenharmony_ci		entrycount++;
505662306a36Sopenharmony_ci
505762306a36Sopenharmony_ci		next_offset = le32_to_cpu(dir_info->NextEntryOffset);
505862306a36Sopenharmony_ci		if (!next_offset)
505962306a36Sopenharmony_ci			break;
506062306a36Sopenharmony_ci	}
506162306a36Sopenharmony_ci
506262306a36Sopenharmony_ci	return entrycount;
506362306a36Sopenharmony_ci}
506462306a36Sopenharmony_ci
506562306a36Sopenharmony_ci/*
506662306a36Sopenharmony_ci * Readdir/FindFirst
506762306a36Sopenharmony_ci */
506862306a36Sopenharmony_ciint SMB2_query_directory_init(const unsigned int xid,
506962306a36Sopenharmony_ci			      struct cifs_tcon *tcon,
507062306a36Sopenharmony_ci			      struct TCP_Server_Info *server,
507162306a36Sopenharmony_ci			      struct smb_rqst *rqst,
507262306a36Sopenharmony_ci			      u64 persistent_fid, u64 volatile_fid,
507362306a36Sopenharmony_ci			      int index, int info_level)
507462306a36Sopenharmony_ci{
507562306a36Sopenharmony_ci	struct smb2_query_directory_req *req;
507662306a36Sopenharmony_ci	unsigned char *bufptr;
507762306a36Sopenharmony_ci	__le16 asteriks = cpu_to_le16('*');
507862306a36Sopenharmony_ci	unsigned int output_size = CIFSMaxBufSize -
507962306a36Sopenharmony_ci		MAX_SMB2_CREATE_RESPONSE_SIZE -
508062306a36Sopenharmony_ci		MAX_SMB2_CLOSE_RESPONSE_SIZE;
508162306a36Sopenharmony_ci	unsigned int total_len;
508262306a36Sopenharmony_ci	struct kvec *iov = rqst->rq_iov;
508362306a36Sopenharmony_ci	int len, rc;
508462306a36Sopenharmony_ci
508562306a36Sopenharmony_ci	rc = smb2_plain_req_init(SMB2_QUERY_DIRECTORY, tcon, server,
508662306a36Sopenharmony_ci				 (void **) &req, &total_len);
508762306a36Sopenharmony_ci	if (rc)
508862306a36Sopenharmony_ci		return rc;
508962306a36Sopenharmony_ci
509062306a36Sopenharmony_ci	switch (info_level) {
509162306a36Sopenharmony_ci	case SMB_FIND_FILE_DIRECTORY_INFO:
509262306a36Sopenharmony_ci		req->FileInformationClass = FILE_DIRECTORY_INFORMATION;
509362306a36Sopenharmony_ci		break;
509462306a36Sopenharmony_ci	case SMB_FIND_FILE_ID_FULL_DIR_INFO:
509562306a36Sopenharmony_ci		req->FileInformationClass = FILEID_FULL_DIRECTORY_INFORMATION;
509662306a36Sopenharmony_ci		break;
509762306a36Sopenharmony_ci	case SMB_FIND_FILE_POSIX_INFO:
509862306a36Sopenharmony_ci		req->FileInformationClass = SMB_FIND_FILE_POSIX_INFO;
509962306a36Sopenharmony_ci		break;
510062306a36Sopenharmony_ci	case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
510162306a36Sopenharmony_ci		req->FileInformationClass = FILE_FULL_DIRECTORY_INFORMATION;
510262306a36Sopenharmony_ci		break;
510362306a36Sopenharmony_ci	default:
510462306a36Sopenharmony_ci		cifs_tcon_dbg(VFS, "info level %u isn't supported\n",
510562306a36Sopenharmony_ci			info_level);
510662306a36Sopenharmony_ci		return -EINVAL;
510762306a36Sopenharmony_ci	}
510862306a36Sopenharmony_ci
510962306a36Sopenharmony_ci	req->FileIndex = cpu_to_le32(index);
511062306a36Sopenharmony_ci	req->PersistentFileId = persistent_fid;
511162306a36Sopenharmony_ci	req->VolatileFileId = volatile_fid;
511262306a36Sopenharmony_ci
511362306a36Sopenharmony_ci	len = 0x2;
511462306a36Sopenharmony_ci	bufptr = req->Buffer;
511562306a36Sopenharmony_ci	memcpy(bufptr, &asteriks, len);
511662306a36Sopenharmony_ci
511762306a36Sopenharmony_ci	req->FileNameOffset =
511862306a36Sopenharmony_ci		cpu_to_le16(sizeof(struct smb2_query_directory_req));
511962306a36Sopenharmony_ci	req->FileNameLength = cpu_to_le16(len);
512062306a36Sopenharmony_ci	/*
512162306a36Sopenharmony_ci	 * BB could be 30 bytes or so longer if we used SMB2 specific
512262306a36Sopenharmony_ci	 * buffer lengths, but this is safe and close enough.
512362306a36Sopenharmony_ci	 */
512462306a36Sopenharmony_ci	output_size = min_t(unsigned int, output_size, server->maxBuf);
512562306a36Sopenharmony_ci	output_size = min_t(unsigned int, output_size, 2 << 15);
512662306a36Sopenharmony_ci	req->OutputBufferLength = cpu_to_le32(output_size);
512762306a36Sopenharmony_ci
512862306a36Sopenharmony_ci	iov[0].iov_base = (char *)req;
512962306a36Sopenharmony_ci	/* 1 for Buffer */
513062306a36Sopenharmony_ci	iov[0].iov_len = total_len - 1;
513162306a36Sopenharmony_ci
513262306a36Sopenharmony_ci	iov[1].iov_base = (char *)(req->Buffer);
513362306a36Sopenharmony_ci	iov[1].iov_len = len;
513462306a36Sopenharmony_ci
513562306a36Sopenharmony_ci	trace_smb3_query_dir_enter(xid, persistent_fid, tcon->tid,
513662306a36Sopenharmony_ci			tcon->ses->Suid, index, output_size);
513762306a36Sopenharmony_ci
513862306a36Sopenharmony_ci	return 0;
513962306a36Sopenharmony_ci}
514062306a36Sopenharmony_ci
514162306a36Sopenharmony_civoid SMB2_query_directory_free(struct smb_rqst *rqst)
514262306a36Sopenharmony_ci{
514362306a36Sopenharmony_ci	if (rqst && rqst->rq_iov) {
514462306a36Sopenharmony_ci		cifs_small_buf_release(rqst->rq_iov[0].iov_base); /* request */
514562306a36Sopenharmony_ci	}
514662306a36Sopenharmony_ci}
514762306a36Sopenharmony_ci
514862306a36Sopenharmony_ciint
514962306a36Sopenharmony_cismb2_parse_query_directory(struct cifs_tcon *tcon,
515062306a36Sopenharmony_ci			   struct kvec *rsp_iov,
515162306a36Sopenharmony_ci			   int resp_buftype,
515262306a36Sopenharmony_ci			   struct cifs_search_info *srch_inf)
515362306a36Sopenharmony_ci{
515462306a36Sopenharmony_ci	struct smb2_query_directory_rsp *rsp;
515562306a36Sopenharmony_ci	size_t info_buf_size;
515662306a36Sopenharmony_ci	char *end_of_smb;
515762306a36Sopenharmony_ci	int rc;
515862306a36Sopenharmony_ci
515962306a36Sopenharmony_ci	rsp = (struct smb2_query_directory_rsp *)rsp_iov->iov_base;
516062306a36Sopenharmony_ci
516162306a36Sopenharmony_ci	switch (srch_inf->info_level) {
516262306a36Sopenharmony_ci	case SMB_FIND_FILE_DIRECTORY_INFO:
516362306a36Sopenharmony_ci		info_buf_size = sizeof(FILE_DIRECTORY_INFO);
516462306a36Sopenharmony_ci		break;
516562306a36Sopenharmony_ci	case SMB_FIND_FILE_ID_FULL_DIR_INFO:
516662306a36Sopenharmony_ci		info_buf_size = sizeof(SEARCH_ID_FULL_DIR_INFO);
516762306a36Sopenharmony_ci		break;
516862306a36Sopenharmony_ci	case SMB_FIND_FILE_POSIX_INFO:
516962306a36Sopenharmony_ci		/* note that posix payload are variable size */
517062306a36Sopenharmony_ci		info_buf_size = sizeof(struct smb2_posix_info);
517162306a36Sopenharmony_ci		break;
517262306a36Sopenharmony_ci	case SMB_FIND_FILE_FULL_DIRECTORY_INFO:
517362306a36Sopenharmony_ci		info_buf_size = sizeof(FILE_FULL_DIRECTORY_INFO);
517462306a36Sopenharmony_ci		break;
517562306a36Sopenharmony_ci	default:
517662306a36Sopenharmony_ci		cifs_tcon_dbg(VFS, "info level %u isn't supported\n",
517762306a36Sopenharmony_ci			 srch_inf->info_level);
517862306a36Sopenharmony_ci		return -EINVAL;
517962306a36Sopenharmony_ci	}
518062306a36Sopenharmony_ci
518162306a36Sopenharmony_ci	rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset),
518262306a36Sopenharmony_ci			       le32_to_cpu(rsp->OutputBufferLength), rsp_iov,
518362306a36Sopenharmony_ci			       info_buf_size);
518462306a36Sopenharmony_ci	if (rc) {
518562306a36Sopenharmony_ci		cifs_tcon_dbg(VFS, "bad info payload");
518662306a36Sopenharmony_ci		return rc;
518762306a36Sopenharmony_ci	}
518862306a36Sopenharmony_ci
518962306a36Sopenharmony_ci	srch_inf->unicode = true;
519062306a36Sopenharmony_ci
519162306a36Sopenharmony_ci	if (srch_inf->ntwrk_buf_start) {
519262306a36Sopenharmony_ci		if (srch_inf->smallBuf)
519362306a36Sopenharmony_ci			cifs_small_buf_release(srch_inf->ntwrk_buf_start);
519462306a36Sopenharmony_ci		else
519562306a36Sopenharmony_ci			cifs_buf_release(srch_inf->ntwrk_buf_start);
519662306a36Sopenharmony_ci	}
519762306a36Sopenharmony_ci	srch_inf->ntwrk_buf_start = (char *)rsp;
519862306a36Sopenharmony_ci	srch_inf->srch_entries_start = srch_inf->last_entry =
519962306a36Sopenharmony_ci		(char *)rsp + le16_to_cpu(rsp->OutputBufferOffset);
520062306a36Sopenharmony_ci	end_of_smb = rsp_iov->iov_len + (char *)rsp;
520162306a36Sopenharmony_ci
520262306a36Sopenharmony_ci	srch_inf->entries_in_buffer = num_entries(
520362306a36Sopenharmony_ci		srch_inf->info_level,
520462306a36Sopenharmony_ci		srch_inf->srch_entries_start,
520562306a36Sopenharmony_ci		end_of_smb,
520662306a36Sopenharmony_ci		&srch_inf->last_entry,
520762306a36Sopenharmony_ci		info_buf_size);
520862306a36Sopenharmony_ci
520962306a36Sopenharmony_ci	srch_inf->index_of_last_entry += srch_inf->entries_in_buffer;
521062306a36Sopenharmony_ci	cifs_dbg(FYI, "num entries %d last_index %lld srch start %p srch end %p\n",
521162306a36Sopenharmony_ci		 srch_inf->entries_in_buffer, srch_inf->index_of_last_entry,
521262306a36Sopenharmony_ci		 srch_inf->srch_entries_start, srch_inf->last_entry);
521362306a36Sopenharmony_ci	if (resp_buftype == CIFS_LARGE_BUFFER)
521462306a36Sopenharmony_ci		srch_inf->smallBuf = false;
521562306a36Sopenharmony_ci	else if (resp_buftype == CIFS_SMALL_BUFFER)
521662306a36Sopenharmony_ci		srch_inf->smallBuf = true;
521762306a36Sopenharmony_ci	else
521862306a36Sopenharmony_ci		cifs_tcon_dbg(VFS, "Invalid search buffer type\n");
521962306a36Sopenharmony_ci
522062306a36Sopenharmony_ci	return 0;
522162306a36Sopenharmony_ci}
522262306a36Sopenharmony_ci
522362306a36Sopenharmony_ciint
522462306a36Sopenharmony_ciSMB2_query_directory(const unsigned int xid, struct cifs_tcon *tcon,
522562306a36Sopenharmony_ci		     u64 persistent_fid, u64 volatile_fid, int index,
522662306a36Sopenharmony_ci		     struct cifs_search_info *srch_inf)
522762306a36Sopenharmony_ci{
522862306a36Sopenharmony_ci	struct smb_rqst rqst;
522962306a36Sopenharmony_ci	struct kvec iov[SMB2_QUERY_DIRECTORY_IOV_SIZE];
523062306a36Sopenharmony_ci	struct smb2_query_directory_rsp *rsp = NULL;
523162306a36Sopenharmony_ci	int resp_buftype = CIFS_NO_BUFFER;
523262306a36Sopenharmony_ci	struct kvec rsp_iov;
523362306a36Sopenharmony_ci	int rc = 0;
523462306a36Sopenharmony_ci	struct cifs_ses *ses = tcon->ses;
523562306a36Sopenharmony_ci	struct TCP_Server_Info *server = cifs_pick_channel(ses);
523662306a36Sopenharmony_ci	int flags = 0;
523762306a36Sopenharmony_ci
523862306a36Sopenharmony_ci	if (!ses || !(ses->server))
523962306a36Sopenharmony_ci		return -EIO;
524062306a36Sopenharmony_ci
524162306a36Sopenharmony_ci	if (smb3_encryption_required(tcon))
524262306a36Sopenharmony_ci		flags |= CIFS_TRANSFORM_REQ;
524362306a36Sopenharmony_ci
524462306a36Sopenharmony_ci	memset(&rqst, 0, sizeof(struct smb_rqst));
524562306a36Sopenharmony_ci	memset(&iov, 0, sizeof(iov));
524662306a36Sopenharmony_ci	rqst.rq_iov = iov;
524762306a36Sopenharmony_ci	rqst.rq_nvec = SMB2_QUERY_DIRECTORY_IOV_SIZE;
524862306a36Sopenharmony_ci
524962306a36Sopenharmony_ci	rc = SMB2_query_directory_init(xid, tcon, server,
525062306a36Sopenharmony_ci				       &rqst, persistent_fid,
525162306a36Sopenharmony_ci				       volatile_fid, index,
525262306a36Sopenharmony_ci				       srch_inf->info_level);
525362306a36Sopenharmony_ci	if (rc)
525462306a36Sopenharmony_ci		goto qdir_exit;
525562306a36Sopenharmony_ci
525662306a36Sopenharmony_ci	rc = cifs_send_recv(xid, ses, server,
525762306a36Sopenharmony_ci			    &rqst, &resp_buftype, flags, &rsp_iov);
525862306a36Sopenharmony_ci	rsp = (struct smb2_query_directory_rsp *)rsp_iov.iov_base;
525962306a36Sopenharmony_ci
526062306a36Sopenharmony_ci	if (rc) {
526162306a36Sopenharmony_ci		if (rc == -ENODATA &&
526262306a36Sopenharmony_ci		    rsp->hdr.Status == STATUS_NO_MORE_FILES) {
526362306a36Sopenharmony_ci			trace_smb3_query_dir_done(xid, persistent_fid,
526462306a36Sopenharmony_ci				tcon->tid, tcon->ses->Suid, index, 0);
526562306a36Sopenharmony_ci			srch_inf->endOfSearch = true;
526662306a36Sopenharmony_ci			rc = 0;
526762306a36Sopenharmony_ci		} else {
526862306a36Sopenharmony_ci			trace_smb3_query_dir_err(xid, persistent_fid, tcon->tid,
526962306a36Sopenharmony_ci				tcon->ses->Suid, index, 0, rc);
527062306a36Sopenharmony_ci			cifs_stats_fail_inc(tcon, SMB2_QUERY_DIRECTORY_HE);
527162306a36Sopenharmony_ci		}
527262306a36Sopenharmony_ci		goto qdir_exit;
527362306a36Sopenharmony_ci	}
527462306a36Sopenharmony_ci
527562306a36Sopenharmony_ci	rc = smb2_parse_query_directory(tcon, &rsp_iov,	resp_buftype,
527662306a36Sopenharmony_ci					srch_inf);
527762306a36Sopenharmony_ci	if (rc) {
527862306a36Sopenharmony_ci		trace_smb3_query_dir_err(xid, persistent_fid, tcon->tid,
527962306a36Sopenharmony_ci			tcon->ses->Suid, index, 0, rc);
528062306a36Sopenharmony_ci		goto qdir_exit;
528162306a36Sopenharmony_ci	}
528262306a36Sopenharmony_ci	resp_buftype = CIFS_NO_BUFFER;
528362306a36Sopenharmony_ci
528462306a36Sopenharmony_ci	trace_smb3_query_dir_done(xid, persistent_fid, tcon->tid,
528562306a36Sopenharmony_ci			tcon->ses->Suid, index, srch_inf->entries_in_buffer);
528662306a36Sopenharmony_ci
528762306a36Sopenharmony_ciqdir_exit:
528862306a36Sopenharmony_ci	SMB2_query_directory_free(&rqst);
528962306a36Sopenharmony_ci	free_rsp_buf(resp_buftype, rsp);
529062306a36Sopenharmony_ci	return rc;
529162306a36Sopenharmony_ci}
529262306a36Sopenharmony_ci
529362306a36Sopenharmony_ciint
529462306a36Sopenharmony_ciSMB2_set_info_init(struct cifs_tcon *tcon, struct TCP_Server_Info *server,
529562306a36Sopenharmony_ci		   struct smb_rqst *rqst,
529662306a36Sopenharmony_ci		   u64 persistent_fid, u64 volatile_fid, u32 pid,
529762306a36Sopenharmony_ci		   u8 info_class, u8 info_type, u32 additional_info,
529862306a36Sopenharmony_ci		   void **data, unsigned int *size)
529962306a36Sopenharmony_ci{
530062306a36Sopenharmony_ci	struct smb2_set_info_req *req;
530162306a36Sopenharmony_ci	struct kvec *iov = rqst->rq_iov;
530262306a36Sopenharmony_ci	unsigned int i, total_len;
530362306a36Sopenharmony_ci	int rc;
530462306a36Sopenharmony_ci
530562306a36Sopenharmony_ci	rc = smb2_plain_req_init(SMB2_SET_INFO, tcon, server,
530662306a36Sopenharmony_ci				 (void **) &req, &total_len);
530762306a36Sopenharmony_ci	if (rc)
530862306a36Sopenharmony_ci		return rc;
530962306a36Sopenharmony_ci
531062306a36Sopenharmony_ci	req->hdr.Id.SyncId.ProcessId = cpu_to_le32(pid);
531162306a36Sopenharmony_ci	req->InfoType = info_type;
531262306a36Sopenharmony_ci	req->FileInfoClass = info_class;
531362306a36Sopenharmony_ci	req->PersistentFileId = persistent_fid;
531462306a36Sopenharmony_ci	req->VolatileFileId = volatile_fid;
531562306a36Sopenharmony_ci	req->AdditionalInformation = cpu_to_le32(additional_info);
531662306a36Sopenharmony_ci
531762306a36Sopenharmony_ci	req->BufferOffset = cpu_to_le16(sizeof(struct smb2_set_info_req));
531862306a36Sopenharmony_ci	req->BufferLength = cpu_to_le32(*size);
531962306a36Sopenharmony_ci
532062306a36Sopenharmony_ci	memcpy(req->Buffer, *data, *size);
532162306a36Sopenharmony_ci	total_len += *size;
532262306a36Sopenharmony_ci
532362306a36Sopenharmony_ci	iov[0].iov_base = (char *)req;
532462306a36Sopenharmony_ci	/* 1 for Buffer */
532562306a36Sopenharmony_ci	iov[0].iov_len = total_len - 1;
532662306a36Sopenharmony_ci
532762306a36Sopenharmony_ci	for (i = 1; i < rqst->rq_nvec; i++) {
532862306a36Sopenharmony_ci		le32_add_cpu(&req->BufferLength, size[i]);
532962306a36Sopenharmony_ci		iov[i].iov_base = (char *)data[i];
533062306a36Sopenharmony_ci		iov[i].iov_len = size[i];
533162306a36Sopenharmony_ci	}
533262306a36Sopenharmony_ci
533362306a36Sopenharmony_ci	return 0;
533462306a36Sopenharmony_ci}
533562306a36Sopenharmony_ci
533662306a36Sopenharmony_civoid
533762306a36Sopenharmony_ciSMB2_set_info_free(struct smb_rqst *rqst)
533862306a36Sopenharmony_ci{
533962306a36Sopenharmony_ci	if (rqst && rqst->rq_iov)
534062306a36Sopenharmony_ci		cifs_buf_release(rqst->rq_iov[0].iov_base); /* request */
534162306a36Sopenharmony_ci}
534262306a36Sopenharmony_ci
534362306a36Sopenharmony_cistatic int
534462306a36Sopenharmony_cisend_set_info(const unsigned int xid, struct cifs_tcon *tcon,
534562306a36Sopenharmony_ci	       u64 persistent_fid, u64 volatile_fid, u32 pid, u8 info_class,
534662306a36Sopenharmony_ci	       u8 info_type, u32 additional_info, unsigned int num,
534762306a36Sopenharmony_ci		void **data, unsigned int *size)
534862306a36Sopenharmony_ci{
534962306a36Sopenharmony_ci	struct smb_rqst rqst;
535062306a36Sopenharmony_ci	struct smb2_set_info_rsp *rsp = NULL;
535162306a36Sopenharmony_ci	struct kvec *iov;
535262306a36Sopenharmony_ci	struct kvec rsp_iov;
535362306a36Sopenharmony_ci	int rc = 0;
535462306a36Sopenharmony_ci	int resp_buftype;
535562306a36Sopenharmony_ci	struct cifs_ses *ses = tcon->ses;
535662306a36Sopenharmony_ci	struct TCP_Server_Info *server = cifs_pick_channel(ses);
535762306a36Sopenharmony_ci	int flags = 0;
535862306a36Sopenharmony_ci
535962306a36Sopenharmony_ci	if (!ses || !server)
536062306a36Sopenharmony_ci		return -EIO;
536162306a36Sopenharmony_ci
536262306a36Sopenharmony_ci	if (!num)
536362306a36Sopenharmony_ci		return -EINVAL;
536462306a36Sopenharmony_ci
536562306a36Sopenharmony_ci	if (smb3_encryption_required(tcon))
536662306a36Sopenharmony_ci		flags |= CIFS_TRANSFORM_REQ;
536762306a36Sopenharmony_ci
536862306a36Sopenharmony_ci	iov = kmalloc_array(num, sizeof(struct kvec), GFP_KERNEL);
536962306a36Sopenharmony_ci	if (!iov)
537062306a36Sopenharmony_ci		return -ENOMEM;
537162306a36Sopenharmony_ci
537262306a36Sopenharmony_ci	memset(&rqst, 0, sizeof(struct smb_rqst));
537362306a36Sopenharmony_ci	rqst.rq_iov = iov;
537462306a36Sopenharmony_ci	rqst.rq_nvec = num;
537562306a36Sopenharmony_ci
537662306a36Sopenharmony_ci	rc = SMB2_set_info_init(tcon, server,
537762306a36Sopenharmony_ci				&rqst, persistent_fid, volatile_fid, pid,
537862306a36Sopenharmony_ci				info_class, info_type, additional_info,
537962306a36Sopenharmony_ci				data, size);
538062306a36Sopenharmony_ci	if (rc) {
538162306a36Sopenharmony_ci		kfree(iov);
538262306a36Sopenharmony_ci		return rc;
538362306a36Sopenharmony_ci	}
538462306a36Sopenharmony_ci
538562306a36Sopenharmony_ci
538662306a36Sopenharmony_ci	rc = cifs_send_recv(xid, ses, server,
538762306a36Sopenharmony_ci			    &rqst, &resp_buftype, flags,
538862306a36Sopenharmony_ci			    &rsp_iov);
538962306a36Sopenharmony_ci	SMB2_set_info_free(&rqst);
539062306a36Sopenharmony_ci	rsp = (struct smb2_set_info_rsp *)rsp_iov.iov_base;
539162306a36Sopenharmony_ci
539262306a36Sopenharmony_ci	if (rc != 0) {
539362306a36Sopenharmony_ci		cifs_stats_fail_inc(tcon, SMB2_SET_INFO_HE);
539462306a36Sopenharmony_ci		trace_smb3_set_info_err(xid, persistent_fid, tcon->tid,
539562306a36Sopenharmony_ci				ses->Suid, info_class, (__u32)info_type, rc);
539662306a36Sopenharmony_ci	}
539762306a36Sopenharmony_ci
539862306a36Sopenharmony_ci	free_rsp_buf(resp_buftype, rsp);
539962306a36Sopenharmony_ci	kfree(iov);
540062306a36Sopenharmony_ci	return rc;
540162306a36Sopenharmony_ci}
540262306a36Sopenharmony_ci
540362306a36Sopenharmony_ciint
540462306a36Sopenharmony_ciSMB2_set_eof(const unsigned int xid, struct cifs_tcon *tcon, u64 persistent_fid,
540562306a36Sopenharmony_ci	     u64 volatile_fid, u32 pid, __le64 *eof)
540662306a36Sopenharmony_ci{
540762306a36Sopenharmony_ci	struct smb2_file_eof_info info;
540862306a36Sopenharmony_ci	void *data;
540962306a36Sopenharmony_ci	unsigned int size;
541062306a36Sopenharmony_ci
541162306a36Sopenharmony_ci	info.EndOfFile = *eof;
541262306a36Sopenharmony_ci
541362306a36Sopenharmony_ci	data = &info;
541462306a36Sopenharmony_ci	size = sizeof(struct smb2_file_eof_info);
541562306a36Sopenharmony_ci
541662306a36Sopenharmony_ci	trace_smb3_set_eof(xid, persistent_fid, tcon->tid, tcon->ses->Suid, le64_to_cpu(*eof));
541762306a36Sopenharmony_ci
541862306a36Sopenharmony_ci	return send_set_info(xid, tcon, persistent_fid, volatile_fid,
541962306a36Sopenharmony_ci			pid, FILE_END_OF_FILE_INFORMATION, SMB2_O_INFO_FILE,
542062306a36Sopenharmony_ci			0, 1, &data, &size);
542162306a36Sopenharmony_ci}
542262306a36Sopenharmony_ci
542362306a36Sopenharmony_ciint
542462306a36Sopenharmony_ciSMB2_set_acl(const unsigned int xid, struct cifs_tcon *tcon,
542562306a36Sopenharmony_ci		u64 persistent_fid, u64 volatile_fid,
542662306a36Sopenharmony_ci		struct cifs_ntsd *pnntsd, int pacllen, int aclflag)
542762306a36Sopenharmony_ci{
542862306a36Sopenharmony_ci	return send_set_info(xid, tcon, persistent_fid, volatile_fid,
542962306a36Sopenharmony_ci			current->tgid, 0, SMB2_O_INFO_SECURITY, aclflag,
543062306a36Sopenharmony_ci			1, (void **)&pnntsd, &pacllen);
543162306a36Sopenharmony_ci}
543262306a36Sopenharmony_ci
543362306a36Sopenharmony_ciint
543462306a36Sopenharmony_ciSMB2_set_ea(const unsigned int xid, struct cifs_tcon *tcon,
543562306a36Sopenharmony_ci	    u64 persistent_fid, u64 volatile_fid,
543662306a36Sopenharmony_ci	    struct smb2_file_full_ea_info *buf, int len)
543762306a36Sopenharmony_ci{
543862306a36Sopenharmony_ci	return send_set_info(xid, tcon, persistent_fid, volatile_fid,
543962306a36Sopenharmony_ci		current->tgid, FILE_FULL_EA_INFORMATION, SMB2_O_INFO_FILE,
544062306a36Sopenharmony_ci		0, 1, (void **)&buf, &len);
544162306a36Sopenharmony_ci}
544262306a36Sopenharmony_ci
544362306a36Sopenharmony_ciint
544462306a36Sopenharmony_ciSMB2_oplock_break(const unsigned int xid, struct cifs_tcon *tcon,
544562306a36Sopenharmony_ci		  const u64 persistent_fid, const u64 volatile_fid,
544662306a36Sopenharmony_ci		  __u8 oplock_level)
544762306a36Sopenharmony_ci{
544862306a36Sopenharmony_ci	struct smb_rqst rqst;
544962306a36Sopenharmony_ci	int rc;
545062306a36Sopenharmony_ci	struct smb2_oplock_break *req = NULL;
545162306a36Sopenharmony_ci	struct cifs_ses *ses = tcon->ses;
545262306a36Sopenharmony_ci	struct TCP_Server_Info *server = cifs_pick_channel(ses);
545362306a36Sopenharmony_ci	int flags = CIFS_OBREAK_OP;
545462306a36Sopenharmony_ci	unsigned int total_len;
545562306a36Sopenharmony_ci	struct kvec iov[1];
545662306a36Sopenharmony_ci	struct kvec rsp_iov;
545762306a36Sopenharmony_ci	int resp_buf_type;
545862306a36Sopenharmony_ci
545962306a36Sopenharmony_ci	cifs_dbg(FYI, "SMB2_oplock_break\n");
546062306a36Sopenharmony_ci	rc = smb2_plain_req_init(SMB2_OPLOCK_BREAK, tcon, server,
546162306a36Sopenharmony_ci				 (void **) &req, &total_len);
546262306a36Sopenharmony_ci	if (rc)
546362306a36Sopenharmony_ci		return rc;
546462306a36Sopenharmony_ci
546562306a36Sopenharmony_ci	if (smb3_encryption_required(tcon))
546662306a36Sopenharmony_ci		flags |= CIFS_TRANSFORM_REQ;
546762306a36Sopenharmony_ci
546862306a36Sopenharmony_ci	req->VolatileFid = volatile_fid;
546962306a36Sopenharmony_ci	req->PersistentFid = persistent_fid;
547062306a36Sopenharmony_ci	req->OplockLevel = oplock_level;
547162306a36Sopenharmony_ci	req->hdr.CreditRequest = cpu_to_le16(1);
547262306a36Sopenharmony_ci
547362306a36Sopenharmony_ci	flags |= CIFS_NO_RSP_BUF;
547462306a36Sopenharmony_ci
547562306a36Sopenharmony_ci	iov[0].iov_base = (char *)req;
547662306a36Sopenharmony_ci	iov[0].iov_len = total_len;
547762306a36Sopenharmony_ci
547862306a36Sopenharmony_ci	memset(&rqst, 0, sizeof(struct smb_rqst));
547962306a36Sopenharmony_ci	rqst.rq_iov = iov;
548062306a36Sopenharmony_ci	rqst.rq_nvec = 1;
548162306a36Sopenharmony_ci
548262306a36Sopenharmony_ci	rc = cifs_send_recv(xid, ses, server,
548362306a36Sopenharmony_ci			    &rqst, &resp_buf_type, flags, &rsp_iov);
548462306a36Sopenharmony_ci	cifs_small_buf_release(req);
548562306a36Sopenharmony_ci
548662306a36Sopenharmony_ci	if (rc) {
548762306a36Sopenharmony_ci		cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE);
548862306a36Sopenharmony_ci		cifs_dbg(FYI, "Send error in Oplock Break = %d\n", rc);
548962306a36Sopenharmony_ci	}
549062306a36Sopenharmony_ci
549162306a36Sopenharmony_ci	return rc;
549262306a36Sopenharmony_ci}
549362306a36Sopenharmony_ci
549462306a36Sopenharmony_civoid
549562306a36Sopenharmony_cismb2_copy_fs_info_to_kstatfs(struct smb2_fs_full_size_info *pfs_inf,
549662306a36Sopenharmony_ci			     struct kstatfs *kst)
549762306a36Sopenharmony_ci{
549862306a36Sopenharmony_ci	kst->f_bsize = le32_to_cpu(pfs_inf->BytesPerSector) *
549962306a36Sopenharmony_ci			  le32_to_cpu(pfs_inf->SectorsPerAllocationUnit);
550062306a36Sopenharmony_ci	kst->f_blocks = le64_to_cpu(pfs_inf->TotalAllocationUnits);
550162306a36Sopenharmony_ci	kst->f_bfree  = kst->f_bavail =
550262306a36Sopenharmony_ci			le64_to_cpu(pfs_inf->CallerAvailableAllocationUnits);
550362306a36Sopenharmony_ci	return;
550462306a36Sopenharmony_ci}
550562306a36Sopenharmony_ci
550662306a36Sopenharmony_cistatic void
550762306a36Sopenharmony_cicopy_posix_fs_info_to_kstatfs(FILE_SYSTEM_POSIX_INFO *response_data,
550862306a36Sopenharmony_ci			struct kstatfs *kst)
550962306a36Sopenharmony_ci{
551062306a36Sopenharmony_ci	kst->f_bsize = le32_to_cpu(response_data->BlockSize);
551162306a36Sopenharmony_ci	kst->f_blocks = le64_to_cpu(response_data->TotalBlocks);
551262306a36Sopenharmony_ci	kst->f_bfree =  le64_to_cpu(response_data->BlocksAvail);
551362306a36Sopenharmony_ci	if (response_data->UserBlocksAvail == cpu_to_le64(-1))
551462306a36Sopenharmony_ci		kst->f_bavail = kst->f_bfree;
551562306a36Sopenharmony_ci	else
551662306a36Sopenharmony_ci		kst->f_bavail = le64_to_cpu(response_data->UserBlocksAvail);
551762306a36Sopenharmony_ci	if (response_data->TotalFileNodes != cpu_to_le64(-1))
551862306a36Sopenharmony_ci		kst->f_files = le64_to_cpu(response_data->TotalFileNodes);
551962306a36Sopenharmony_ci	if (response_data->FreeFileNodes != cpu_to_le64(-1))
552062306a36Sopenharmony_ci		kst->f_ffree = le64_to_cpu(response_data->FreeFileNodes);
552162306a36Sopenharmony_ci
552262306a36Sopenharmony_ci	return;
552362306a36Sopenharmony_ci}
552462306a36Sopenharmony_ci
552562306a36Sopenharmony_cistatic int
552662306a36Sopenharmony_cibuild_qfs_info_req(struct kvec *iov, struct cifs_tcon *tcon,
552762306a36Sopenharmony_ci		   struct TCP_Server_Info *server,
552862306a36Sopenharmony_ci		   int level, int outbuf_len, u64 persistent_fid,
552962306a36Sopenharmony_ci		   u64 volatile_fid)
553062306a36Sopenharmony_ci{
553162306a36Sopenharmony_ci	int rc;
553262306a36Sopenharmony_ci	struct smb2_query_info_req *req;
553362306a36Sopenharmony_ci	unsigned int total_len;
553462306a36Sopenharmony_ci
553562306a36Sopenharmony_ci	cifs_dbg(FYI, "Query FSInfo level %d\n", level);
553662306a36Sopenharmony_ci
553762306a36Sopenharmony_ci	if ((tcon->ses == NULL) || server == NULL)
553862306a36Sopenharmony_ci		return -EIO;
553962306a36Sopenharmony_ci
554062306a36Sopenharmony_ci	rc = smb2_plain_req_init(SMB2_QUERY_INFO, tcon, server,
554162306a36Sopenharmony_ci				 (void **) &req, &total_len);
554262306a36Sopenharmony_ci	if (rc)
554362306a36Sopenharmony_ci		return rc;
554462306a36Sopenharmony_ci
554562306a36Sopenharmony_ci	req->InfoType = SMB2_O_INFO_FILESYSTEM;
554662306a36Sopenharmony_ci	req->FileInfoClass = level;
554762306a36Sopenharmony_ci	req->PersistentFileId = persistent_fid;
554862306a36Sopenharmony_ci	req->VolatileFileId = volatile_fid;
554962306a36Sopenharmony_ci	/* 1 for pad */
555062306a36Sopenharmony_ci	req->InputBufferOffset =
555162306a36Sopenharmony_ci			cpu_to_le16(sizeof(struct smb2_query_info_req));
555262306a36Sopenharmony_ci	req->OutputBufferLength = cpu_to_le32(
555362306a36Sopenharmony_ci		outbuf_len + sizeof(struct smb2_query_info_rsp));
555462306a36Sopenharmony_ci
555562306a36Sopenharmony_ci	iov->iov_base = (char *)req;
555662306a36Sopenharmony_ci	iov->iov_len = total_len;
555762306a36Sopenharmony_ci	return 0;
555862306a36Sopenharmony_ci}
555962306a36Sopenharmony_ci
556062306a36Sopenharmony_cistatic inline void free_qfs_info_req(struct kvec *iov)
556162306a36Sopenharmony_ci{
556262306a36Sopenharmony_ci	cifs_buf_release(iov->iov_base);
556362306a36Sopenharmony_ci}
556462306a36Sopenharmony_ci
556562306a36Sopenharmony_ciint
556662306a36Sopenharmony_ciSMB311_posix_qfs_info(const unsigned int xid, struct cifs_tcon *tcon,
556762306a36Sopenharmony_ci	      u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata)
556862306a36Sopenharmony_ci{
556962306a36Sopenharmony_ci	struct smb_rqst rqst;
557062306a36Sopenharmony_ci	struct smb2_query_info_rsp *rsp = NULL;
557162306a36Sopenharmony_ci	struct kvec iov;
557262306a36Sopenharmony_ci	struct kvec rsp_iov;
557362306a36Sopenharmony_ci	int rc = 0;
557462306a36Sopenharmony_ci	int resp_buftype;
557562306a36Sopenharmony_ci	struct cifs_ses *ses = tcon->ses;
557662306a36Sopenharmony_ci	struct TCP_Server_Info *server = cifs_pick_channel(ses);
557762306a36Sopenharmony_ci	FILE_SYSTEM_POSIX_INFO *info = NULL;
557862306a36Sopenharmony_ci	int flags = 0;
557962306a36Sopenharmony_ci
558062306a36Sopenharmony_ci	rc = build_qfs_info_req(&iov, tcon, server,
558162306a36Sopenharmony_ci				FS_POSIX_INFORMATION,
558262306a36Sopenharmony_ci				sizeof(FILE_SYSTEM_POSIX_INFO),
558362306a36Sopenharmony_ci				persistent_fid, volatile_fid);
558462306a36Sopenharmony_ci	if (rc)
558562306a36Sopenharmony_ci		return rc;
558662306a36Sopenharmony_ci
558762306a36Sopenharmony_ci	if (smb3_encryption_required(tcon))
558862306a36Sopenharmony_ci		flags |= CIFS_TRANSFORM_REQ;
558962306a36Sopenharmony_ci
559062306a36Sopenharmony_ci	memset(&rqst, 0, sizeof(struct smb_rqst));
559162306a36Sopenharmony_ci	rqst.rq_iov = &iov;
559262306a36Sopenharmony_ci	rqst.rq_nvec = 1;
559362306a36Sopenharmony_ci
559462306a36Sopenharmony_ci	rc = cifs_send_recv(xid, ses, server,
559562306a36Sopenharmony_ci			    &rqst, &resp_buftype, flags, &rsp_iov);
559662306a36Sopenharmony_ci	free_qfs_info_req(&iov);
559762306a36Sopenharmony_ci	if (rc) {
559862306a36Sopenharmony_ci		cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE);
559962306a36Sopenharmony_ci		goto posix_qfsinf_exit;
560062306a36Sopenharmony_ci	}
560162306a36Sopenharmony_ci	rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base;
560262306a36Sopenharmony_ci
560362306a36Sopenharmony_ci	info = (FILE_SYSTEM_POSIX_INFO *)(
560462306a36Sopenharmony_ci		le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp);
560562306a36Sopenharmony_ci	rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset),
560662306a36Sopenharmony_ci			       le32_to_cpu(rsp->OutputBufferLength), &rsp_iov,
560762306a36Sopenharmony_ci			       sizeof(FILE_SYSTEM_POSIX_INFO));
560862306a36Sopenharmony_ci	if (!rc)
560962306a36Sopenharmony_ci		copy_posix_fs_info_to_kstatfs(info, fsdata);
561062306a36Sopenharmony_ci
561162306a36Sopenharmony_ciposix_qfsinf_exit:
561262306a36Sopenharmony_ci	free_rsp_buf(resp_buftype, rsp_iov.iov_base);
561362306a36Sopenharmony_ci	return rc;
561462306a36Sopenharmony_ci}
561562306a36Sopenharmony_ci
561662306a36Sopenharmony_ciint
561762306a36Sopenharmony_ciSMB2_QFS_info(const unsigned int xid, struct cifs_tcon *tcon,
561862306a36Sopenharmony_ci	      u64 persistent_fid, u64 volatile_fid, struct kstatfs *fsdata)
561962306a36Sopenharmony_ci{
562062306a36Sopenharmony_ci	struct smb_rqst rqst;
562162306a36Sopenharmony_ci	struct smb2_query_info_rsp *rsp = NULL;
562262306a36Sopenharmony_ci	struct kvec iov;
562362306a36Sopenharmony_ci	struct kvec rsp_iov;
562462306a36Sopenharmony_ci	int rc = 0;
562562306a36Sopenharmony_ci	int resp_buftype;
562662306a36Sopenharmony_ci	struct cifs_ses *ses = tcon->ses;
562762306a36Sopenharmony_ci	struct TCP_Server_Info *server = cifs_pick_channel(ses);
562862306a36Sopenharmony_ci	struct smb2_fs_full_size_info *info = NULL;
562962306a36Sopenharmony_ci	int flags = 0;
563062306a36Sopenharmony_ci
563162306a36Sopenharmony_ci	rc = build_qfs_info_req(&iov, tcon, server,
563262306a36Sopenharmony_ci				FS_FULL_SIZE_INFORMATION,
563362306a36Sopenharmony_ci				sizeof(struct smb2_fs_full_size_info),
563462306a36Sopenharmony_ci				persistent_fid, volatile_fid);
563562306a36Sopenharmony_ci	if (rc)
563662306a36Sopenharmony_ci		return rc;
563762306a36Sopenharmony_ci
563862306a36Sopenharmony_ci	if (smb3_encryption_required(tcon))
563962306a36Sopenharmony_ci		flags |= CIFS_TRANSFORM_REQ;
564062306a36Sopenharmony_ci
564162306a36Sopenharmony_ci	memset(&rqst, 0, sizeof(struct smb_rqst));
564262306a36Sopenharmony_ci	rqst.rq_iov = &iov;
564362306a36Sopenharmony_ci	rqst.rq_nvec = 1;
564462306a36Sopenharmony_ci
564562306a36Sopenharmony_ci	rc = cifs_send_recv(xid, ses, server,
564662306a36Sopenharmony_ci			    &rqst, &resp_buftype, flags, &rsp_iov);
564762306a36Sopenharmony_ci	free_qfs_info_req(&iov);
564862306a36Sopenharmony_ci	if (rc) {
564962306a36Sopenharmony_ci		cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE);
565062306a36Sopenharmony_ci		goto qfsinf_exit;
565162306a36Sopenharmony_ci	}
565262306a36Sopenharmony_ci	rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base;
565362306a36Sopenharmony_ci
565462306a36Sopenharmony_ci	info = (struct smb2_fs_full_size_info *)(
565562306a36Sopenharmony_ci		le16_to_cpu(rsp->OutputBufferOffset) + (char *)rsp);
565662306a36Sopenharmony_ci	rc = smb2_validate_iov(le16_to_cpu(rsp->OutputBufferOffset),
565762306a36Sopenharmony_ci			       le32_to_cpu(rsp->OutputBufferLength), &rsp_iov,
565862306a36Sopenharmony_ci			       sizeof(struct smb2_fs_full_size_info));
565962306a36Sopenharmony_ci	if (!rc)
566062306a36Sopenharmony_ci		smb2_copy_fs_info_to_kstatfs(info, fsdata);
566162306a36Sopenharmony_ci
566262306a36Sopenharmony_ciqfsinf_exit:
566362306a36Sopenharmony_ci	free_rsp_buf(resp_buftype, rsp_iov.iov_base);
566462306a36Sopenharmony_ci	return rc;
566562306a36Sopenharmony_ci}
566662306a36Sopenharmony_ci
566762306a36Sopenharmony_ciint
566862306a36Sopenharmony_ciSMB2_QFS_attr(const unsigned int xid, struct cifs_tcon *tcon,
566962306a36Sopenharmony_ci	      u64 persistent_fid, u64 volatile_fid, int level)
567062306a36Sopenharmony_ci{
567162306a36Sopenharmony_ci	struct smb_rqst rqst;
567262306a36Sopenharmony_ci	struct smb2_query_info_rsp *rsp = NULL;
567362306a36Sopenharmony_ci	struct kvec iov;
567462306a36Sopenharmony_ci	struct kvec rsp_iov;
567562306a36Sopenharmony_ci	int rc = 0;
567662306a36Sopenharmony_ci	int resp_buftype, max_len, min_len;
567762306a36Sopenharmony_ci	struct cifs_ses *ses = tcon->ses;
567862306a36Sopenharmony_ci	struct TCP_Server_Info *server = cifs_pick_channel(ses);
567962306a36Sopenharmony_ci	unsigned int rsp_len, offset;
568062306a36Sopenharmony_ci	int flags = 0;
568162306a36Sopenharmony_ci
568262306a36Sopenharmony_ci	if (level == FS_DEVICE_INFORMATION) {
568362306a36Sopenharmony_ci		max_len = sizeof(FILE_SYSTEM_DEVICE_INFO);
568462306a36Sopenharmony_ci		min_len = sizeof(FILE_SYSTEM_DEVICE_INFO);
568562306a36Sopenharmony_ci	} else if (level == FS_ATTRIBUTE_INFORMATION) {
568662306a36Sopenharmony_ci		max_len = sizeof(FILE_SYSTEM_ATTRIBUTE_INFO);
568762306a36Sopenharmony_ci		min_len = MIN_FS_ATTR_INFO_SIZE;
568862306a36Sopenharmony_ci	} else if (level == FS_SECTOR_SIZE_INFORMATION) {
568962306a36Sopenharmony_ci		max_len = sizeof(struct smb3_fs_ss_info);
569062306a36Sopenharmony_ci		min_len = sizeof(struct smb3_fs_ss_info);
569162306a36Sopenharmony_ci	} else if (level == FS_VOLUME_INFORMATION) {
569262306a36Sopenharmony_ci		max_len = sizeof(struct smb3_fs_vol_info) + MAX_VOL_LABEL_LEN;
569362306a36Sopenharmony_ci		min_len = sizeof(struct smb3_fs_vol_info);
569462306a36Sopenharmony_ci	} else {
569562306a36Sopenharmony_ci		cifs_dbg(FYI, "Invalid qfsinfo level %d\n", level);
569662306a36Sopenharmony_ci		return -EINVAL;
569762306a36Sopenharmony_ci	}
569862306a36Sopenharmony_ci
569962306a36Sopenharmony_ci	rc = build_qfs_info_req(&iov, tcon, server,
570062306a36Sopenharmony_ci				level, max_len,
570162306a36Sopenharmony_ci				persistent_fid, volatile_fid);
570262306a36Sopenharmony_ci	if (rc)
570362306a36Sopenharmony_ci		return rc;
570462306a36Sopenharmony_ci
570562306a36Sopenharmony_ci	if (smb3_encryption_required(tcon))
570662306a36Sopenharmony_ci		flags |= CIFS_TRANSFORM_REQ;
570762306a36Sopenharmony_ci
570862306a36Sopenharmony_ci	memset(&rqst, 0, sizeof(struct smb_rqst));
570962306a36Sopenharmony_ci	rqst.rq_iov = &iov;
571062306a36Sopenharmony_ci	rqst.rq_nvec = 1;
571162306a36Sopenharmony_ci
571262306a36Sopenharmony_ci	rc = cifs_send_recv(xid, ses, server,
571362306a36Sopenharmony_ci			    &rqst, &resp_buftype, flags, &rsp_iov);
571462306a36Sopenharmony_ci	free_qfs_info_req(&iov);
571562306a36Sopenharmony_ci	if (rc) {
571662306a36Sopenharmony_ci		cifs_stats_fail_inc(tcon, SMB2_QUERY_INFO_HE);
571762306a36Sopenharmony_ci		goto qfsattr_exit;
571862306a36Sopenharmony_ci	}
571962306a36Sopenharmony_ci	rsp = (struct smb2_query_info_rsp *)rsp_iov.iov_base;
572062306a36Sopenharmony_ci
572162306a36Sopenharmony_ci	rsp_len = le32_to_cpu(rsp->OutputBufferLength);
572262306a36Sopenharmony_ci	offset = le16_to_cpu(rsp->OutputBufferOffset);
572362306a36Sopenharmony_ci	rc = smb2_validate_iov(offset, rsp_len, &rsp_iov, min_len);
572462306a36Sopenharmony_ci	if (rc)
572562306a36Sopenharmony_ci		goto qfsattr_exit;
572662306a36Sopenharmony_ci
572762306a36Sopenharmony_ci	if (level == FS_ATTRIBUTE_INFORMATION)
572862306a36Sopenharmony_ci		memcpy(&tcon->fsAttrInfo, offset
572962306a36Sopenharmony_ci			+ (char *)rsp, min_t(unsigned int,
573062306a36Sopenharmony_ci			rsp_len, max_len));
573162306a36Sopenharmony_ci	else if (level == FS_DEVICE_INFORMATION)
573262306a36Sopenharmony_ci		memcpy(&tcon->fsDevInfo, offset
573362306a36Sopenharmony_ci			+ (char *)rsp, sizeof(FILE_SYSTEM_DEVICE_INFO));
573462306a36Sopenharmony_ci	else if (level == FS_SECTOR_SIZE_INFORMATION) {
573562306a36Sopenharmony_ci		struct smb3_fs_ss_info *ss_info = (struct smb3_fs_ss_info *)
573662306a36Sopenharmony_ci			(offset + (char *)rsp);
573762306a36Sopenharmony_ci		tcon->ss_flags = le32_to_cpu(ss_info->Flags);
573862306a36Sopenharmony_ci		tcon->perf_sector_size =
573962306a36Sopenharmony_ci			le32_to_cpu(ss_info->PhysicalBytesPerSectorForPerf);
574062306a36Sopenharmony_ci	} else if (level == FS_VOLUME_INFORMATION) {
574162306a36Sopenharmony_ci		struct smb3_fs_vol_info *vol_info = (struct smb3_fs_vol_info *)
574262306a36Sopenharmony_ci			(offset + (char *)rsp);
574362306a36Sopenharmony_ci		tcon->vol_serial_number = vol_info->VolumeSerialNumber;
574462306a36Sopenharmony_ci		tcon->vol_create_time = vol_info->VolumeCreationTime;
574562306a36Sopenharmony_ci	}
574662306a36Sopenharmony_ci
574762306a36Sopenharmony_ciqfsattr_exit:
574862306a36Sopenharmony_ci	free_rsp_buf(resp_buftype, rsp_iov.iov_base);
574962306a36Sopenharmony_ci	return rc;
575062306a36Sopenharmony_ci}
575162306a36Sopenharmony_ci
575262306a36Sopenharmony_ciint
575362306a36Sopenharmony_cismb2_lockv(const unsigned int xid, struct cifs_tcon *tcon,
575462306a36Sopenharmony_ci	   const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid,
575562306a36Sopenharmony_ci	   const __u32 num_lock, struct smb2_lock_element *buf)
575662306a36Sopenharmony_ci{
575762306a36Sopenharmony_ci	struct smb_rqst rqst;
575862306a36Sopenharmony_ci	int rc = 0;
575962306a36Sopenharmony_ci	struct smb2_lock_req *req = NULL;
576062306a36Sopenharmony_ci	struct kvec iov[2];
576162306a36Sopenharmony_ci	struct kvec rsp_iov;
576262306a36Sopenharmony_ci	int resp_buf_type;
576362306a36Sopenharmony_ci	unsigned int count;
576462306a36Sopenharmony_ci	int flags = CIFS_NO_RSP_BUF;
576562306a36Sopenharmony_ci	unsigned int total_len;
576662306a36Sopenharmony_ci	struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses);
576762306a36Sopenharmony_ci
576862306a36Sopenharmony_ci	cifs_dbg(FYI, "smb2_lockv num lock %d\n", num_lock);
576962306a36Sopenharmony_ci
577062306a36Sopenharmony_ci	rc = smb2_plain_req_init(SMB2_LOCK, tcon, server,
577162306a36Sopenharmony_ci				 (void **) &req, &total_len);
577262306a36Sopenharmony_ci	if (rc)
577362306a36Sopenharmony_ci		return rc;
577462306a36Sopenharmony_ci
577562306a36Sopenharmony_ci	if (smb3_encryption_required(tcon))
577662306a36Sopenharmony_ci		flags |= CIFS_TRANSFORM_REQ;
577762306a36Sopenharmony_ci
577862306a36Sopenharmony_ci	req->hdr.Id.SyncId.ProcessId = cpu_to_le32(pid);
577962306a36Sopenharmony_ci	req->LockCount = cpu_to_le16(num_lock);
578062306a36Sopenharmony_ci
578162306a36Sopenharmony_ci	req->PersistentFileId = persist_fid;
578262306a36Sopenharmony_ci	req->VolatileFileId = volatile_fid;
578362306a36Sopenharmony_ci
578462306a36Sopenharmony_ci	count = num_lock * sizeof(struct smb2_lock_element);
578562306a36Sopenharmony_ci
578662306a36Sopenharmony_ci	iov[0].iov_base = (char *)req;
578762306a36Sopenharmony_ci	iov[0].iov_len = total_len - sizeof(struct smb2_lock_element);
578862306a36Sopenharmony_ci	iov[1].iov_base = (char *)buf;
578962306a36Sopenharmony_ci	iov[1].iov_len = count;
579062306a36Sopenharmony_ci
579162306a36Sopenharmony_ci	cifs_stats_inc(&tcon->stats.cifs_stats.num_locks);
579262306a36Sopenharmony_ci
579362306a36Sopenharmony_ci	memset(&rqst, 0, sizeof(struct smb_rqst));
579462306a36Sopenharmony_ci	rqst.rq_iov = iov;
579562306a36Sopenharmony_ci	rqst.rq_nvec = 2;
579662306a36Sopenharmony_ci
579762306a36Sopenharmony_ci	rc = cifs_send_recv(xid, tcon->ses, server,
579862306a36Sopenharmony_ci			    &rqst, &resp_buf_type, flags,
579962306a36Sopenharmony_ci			    &rsp_iov);
580062306a36Sopenharmony_ci	cifs_small_buf_release(req);
580162306a36Sopenharmony_ci	if (rc) {
580262306a36Sopenharmony_ci		cifs_dbg(FYI, "Send error in smb2_lockv = %d\n", rc);
580362306a36Sopenharmony_ci		cifs_stats_fail_inc(tcon, SMB2_LOCK_HE);
580462306a36Sopenharmony_ci		trace_smb3_lock_err(xid, persist_fid, tcon->tid,
580562306a36Sopenharmony_ci				    tcon->ses->Suid, rc);
580662306a36Sopenharmony_ci	}
580762306a36Sopenharmony_ci
580862306a36Sopenharmony_ci	return rc;
580962306a36Sopenharmony_ci}
581062306a36Sopenharmony_ci
581162306a36Sopenharmony_ciint
581262306a36Sopenharmony_ciSMB2_lock(const unsigned int xid, struct cifs_tcon *tcon,
581362306a36Sopenharmony_ci	  const __u64 persist_fid, const __u64 volatile_fid, const __u32 pid,
581462306a36Sopenharmony_ci	  const __u64 length, const __u64 offset, const __u32 lock_flags,
581562306a36Sopenharmony_ci	  const bool wait)
581662306a36Sopenharmony_ci{
581762306a36Sopenharmony_ci	struct smb2_lock_element lock;
581862306a36Sopenharmony_ci
581962306a36Sopenharmony_ci	lock.Offset = cpu_to_le64(offset);
582062306a36Sopenharmony_ci	lock.Length = cpu_to_le64(length);
582162306a36Sopenharmony_ci	lock.Flags = cpu_to_le32(lock_flags);
582262306a36Sopenharmony_ci	if (!wait && lock_flags != SMB2_LOCKFLAG_UNLOCK)
582362306a36Sopenharmony_ci		lock.Flags |= cpu_to_le32(SMB2_LOCKFLAG_FAIL_IMMEDIATELY);
582462306a36Sopenharmony_ci
582562306a36Sopenharmony_ci	return smb2_lockv(xid, tcon, persist_fid, volatile_fid, pid, 1, &lock);
582662306a36Sopenharmony_ci}
582762306a36Sopenharmony_ci
582862306a36Sopenharmony_ciint
582962306a36Sopenharmony_ciSMB2_lease_break(const unsigned int xid, struct cifs_tcon *tcon,
583062306a36Sopenharmony_ci		 __u8 *lease_key, const __le32 lease_state)
583162306a36Sopenharmony_ci{
583262306a36Sopenharmony_ci	struct smb_rqst rqst;
583362306a36Sopenharmony_ci	int rc;
583462306a36Sopenharmony_ci	struct smb2_lease_ack *req = NULL;
583562306a36Sopenharmony_ci	struct cifs_ses *ses = tcon->ses;
583662306a36Sopenharmony_ci	int flags = CIFS_OBREAK_OP;
583762306a36Sopenharmony_ci	unsigned int total_len;
583862306a36Sopenharmony_ci	struct kvec iov[1];
583962306a36Sopenharmony_ci	struct kvec rsp_iov;
584062306a36Sopenharmony_ci	int resp_buf_type;
584162306a36Sopenharmony_ci	__u64 *please_key_high;
584262306a36Sopenharmony_ci	__u64 *please_key_low;
584362306a36Sopenharmony_ci	struct TCP_Server_Info *server = cifs_pick_channel(tcon->ses);
584462306a36Sopenharmony_ci
584562306a36Sopenharmony_ci	cifs_dbg(FYI, "SMB2_lease_break\n");
584662306a36Sopenharmony_ci	rc = smb2_plain_req_init(SMB2_OPLOCK_BREAK, tcon, server,
584762306a36Sopenharmony_ci				 (void **) &req, &total_len);
584862306a36Sopenharmony_ci	if (rc)
584962306a36Sopenharmony_ci		return rc;
585062306a36Sopenharmony_ci
585162306a36Sopenharmony_ci	if (smb3_encryption_required(tcon))
585262306a36Sopenharmony_ci		flags |= CIFS_TRANSFORM_REQ;
585362306a36Sopenharmony_ci
585462306a36Sopenharmony_ci	req->hdr.CreditRequest = cpu_to_le16(1);
585562306a36Sopenharmony_ci	req->StructureSize = cpu_to_le16(36);
585662306a36Sopenharmony_ci	total_len += 12;
585762306a36Sopenharmony_ci
585862306a36Sopenharmony_ci	memcpy(req->LeaseKey, lease_key, 16);
585962306a36Sopenharmony_ci	req->LeaseState = lease_state;
586062306a36Sopenharmony_ci
586162306a36Sopenharmony_ci	flags |= CIFS_NO_RSP_BUF;
586262306a36Sopenharmony_ci
586362306a36Sopenharmony_ci	iov[0].iov_base = (char *)req;
586462306a36Sopenharmony_ci	iov[0].iov_len = total_len;
586562306a36Sopenharmony_ci
586662306a36Sopenharmony_ci	memset(&rqst, 0, sizeof(struct smb_rqst));
586762306a36Sopenharmony_ci	rqst.rq_iov = iov;
586862306a36Sopenharmony_ci	rqst.rq_nvec = 1;
586962306a36Sopenharmony_ci
587062306a36Sopenharmony_ci	rc = cifs_send_recv(xid, ses, server,
587162306a36Sopenharmony_ci			    &rqst, &resp_buf_type, flags, &rsp_iov);
587262306a36Sopenharmony_ci	cifs_small_buf_release(req);
587362306a36Sopenharmony_ci
587462306a36Sopenharmony_ci	please_key_low = (__u64 *)lease_key;
587562306a36Sopenharmony_ci	please_key_high = (__u64 *)(lease_key+8);
587662306a36Sopenharmony_ci	if (rc) {
587762306a36Sopenharmony_ci		cifs_stats_fail_inc(tcon, SMB2_OPLOCK_BREAK_HE);
587862306a36Sopenharmony_ci		trace_smb3_lease_err(le32_to_cpu(lease_state), tcon->tid,
587962306a36Sopenharmony_ci			ses->Suid, *please_key_low, *please_key_high, rc);
588062306a36Sopenharmony_ci		cifs_dbg(FYI, "Send error in Lease Break = %d\n", rc);
588162306a36Sopenharmony_ci	} else
588262306a36Sopenharmony_ci		trace_smb3_lease_done(le32_to_cpu(lease_state), tcon->tid,
588362306a36Sopenharmony_ci			ses->Suid, *please_key_low, *please_key_high);
588462306a36Sopenharmony_ci
588562306a36Sopenharmony_ci	return rc;
588662306a36Sopenharmony_ci}
5887