162306a36Sopenharmony_ci// SPDX-License-Identifier: LGPL-2.1
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci *
462306a36Sopenharmony_ci *   Copyright (C) International Business Machines  Corp., 2002,2011
562306a36Sopenharmony_ci *   Author(s): Steve French (sfrench@us.ibm.com)
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci */
862306a36Sopenharmony_ci#include <linux/fs.h>
962306a36Sopenharmony_ci#include <linux/net.h>
1062306a36Sopenharmony_ci#include <linux/string.h>
1162306a36Sopenharmony_ci#include <linux/sched/mm.h>
1262306a36Sopenharmony_ci#include <linux/sched/signal.h>
1362306a36Sopenharmony_ci#include <linux/list.h>
1462306a36Sopenharmony_ci#include <linux/wait.h>
1562306a36Sopenharmony_ci#include <linux/slab.h>
1662306a36Sopenharmony_ci#include <linux/pagemap.h>
1762306a36Sopenharmony_ci#include <linux/ctype.h>
1862306a36Sopenharmony_ci#include <linux/utsname.h>
1962306a36Sopenharmony_ci#include <linux/mempool.h>
2062306a36Sopenharmony_ci#include <linux/delay.h>
2162306a36Sopenharmony_ci#include <linux/completion.h>
2262306a36Sopenharmony_ci#include <linux/kthread.h>
2362306a36Sopenharmony_ci#include <linux/pagevec.h>
2462306a36Sopenharmony_ci#include <linux/freezer.h>
2562306a36Sopenharmony_ci#include <linux/namei.h>
2662306a36Sopenharmony_ci#include <linux/uuid.h>
2762306a36Sopenharmony_ci#include <linux/uaccess.h>
2862306a36Sopenharmony_ci#include <asm/processor.h>
2962306a36Sopenharmony_ci#include <linux/inet.h>
3062306a36Sopenharmony_ci#include <linux/module.h>
3162306a36Sopenharmony_ci#include <keys/user-type.h>
3262306a36Sopenharmony_ci#include <net/ipv6.h>
3362306a36Sopenharmony_ci#include <linux/parser.h>
3462306a36Sopenharmony_ci#include <linux/bvec.h>
3562306a36Sopenharmony_ci#include "cifspdu.h"
3662306a36Sopenharmony_ci#include "cifsglob.h"
3762306a36Sopenharmony_ci#include "cifsproto.h"
3862306a36Sopenharmony_ci#include "cifs_unicode.h"
3962306a36Sopenharmony_ci#include "cifs_debug.h"
4062306a36Sopenharmony_ci#include "cifs_fs_sb.h"
4162306a36Sopenharmony_ci#include "ntlmssp.h"
4262306a36Sopenharmony_ci#include "nterr.h"
4362306a36Sopenharmony_ci#include "rfc1002pdu.h"
4462306a36Sopenharmony_ci#include "fscache.h"
4562306a36Sopenharmony_ci#include "smb2proto.h"
4662306a36Sopenharmony_ci#include "smbdirect.h"
4762306a36Sopenharmony_ci#include "dns_resolve.h"
4862306a36Sopenharmony_ci#ifdef CONFIG_CIFS_DFS_UPCALL
4962306a36Sopenharmony_ci#include "dfs.h"
5062306a36Sopenharmony_ci#include "dfs_cache.h"
5162306a36Sopenharmony_ci#endif
5262306a36Sopenharmony_ci#include "fs_context.h"
5362306a36Sopenharmony_ci#include "cifs_swn.h"
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ciextern mempool_t *cifs_req_poolp;
5662306a36Sopenharmony_ciextern bool disable_legacy_dialects;
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci/* FIXME: should these be tunable? */
5962306a36Sopenharmony_ci#define TLINK_ERROR_EXPIRE	(1 * HZ)
6062306a36Sopenharmony_ci#define TLINK_IDLE_EXPIRE	(600 * HZ)
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_ci/* Drop the connection to not overload the server */
6362306a36Sopenharmony_ci#define MAX_STATUS_IO_TIMEOUT   5
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_cistatic int ip_connect(struct TCP_Server_Info *server);
6662306a36Sopenharmony_cistatic int generic_ip_connect(struct TCP_Server_Info *server);
6762306a36Sopenharmony_cistatic void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink);
6862306a36Sopenharmony_cistatic void cifs_prune_tlinks(struct work_struct *work);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci/*
7162306a36Sopenharmony_ci * Resolve hostname and set ip addr in tcp ses. Useful for hostnames that may
7262306a36Sopenharmony_ci * get their ip addresses changed at some point.
7362306a36Sopenharmony_ci *
7462306a36Sopenharmony_ci * This should be called with server->srv_mutex held.
7562306a36Sopenharmony_ci */
7662306a36Sopenharmony_cistatic int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
7762306a36Sopenharmony_ci{
7862306a36Sopenharmony_ci	int rc;
7962306a36Sopenharmony_ci	int len;
8062306a36Sopenharmony_ci	char *unc;
8162306a36Sopenharmony_ci	struct sockaddr_storage ss;
8262306a36Sopenharmony_ci
8362306a36Sopenharmony_ci	if (!server->hostname)
8462306a36Sopenharmony_ci		return -EINVAL;
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	/* if server hostname isn't populated, there's nothing to do here */
8762306a36Sopenharmony_ci	if (server->hostname[0] == '\0')
8862306a36Sopenharmony_ci		return 0;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	len = strlen(server->hostname) + 3;
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	unc = kmalloc(len, GFP_KERNEL);
9362306a36Sopenharmony_ci	if (!unc) {
9462306a36Sopenharmony_ci		cifs_dbg(FYI, "%s: failed to create UNC path\n", __func__);
9562306a36Sopenharmony_ci		return -ENOMEM;
9662306a36Sopenharmony_ci	}
9762306a36Sopenharmony_ci	scnprintf(unc, len, "\\\\%s", server->hostname);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	spin_lock(&server->srv_lock);
10062306a36Sopenharmony_ci	ss = server->dstaddr;
10162306a36Sopenharmony_ci	spin_unlock(&server->srv_lock);
10262306a36Sopenharmony_ci
10362306a36Sopenharmony_ci	rc = dns_resolve_server_name_to_ip(unc, (struct sockaddr *)&ss, NULL);
10462306a36Sopenharmony_ci	kfree(unc);
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci	if (rc < 0) {
10762306a36Sopenharmony_ci		cifs_dbg(FYI, "%s: failed to resolve server part of %s to IP: %d\n",
10862306a36Sopenharmony_ci			 __func__, server->hostname, rc);
10962306a36Sopenharmony_ci	} else {
11062306a36Sopenharmony_ci		spin_lock(&server->srv_lock);
11162306a36Sopenharmony_ci		memcpy(&server->dstaddr, &ss, sizeof(server->dstaddr));
11262306a36Sopenharmony_ci		spin_unlock(&server->srv_lock);
11362306a36Sopenharmony_ci		rc = 0;
11462306a36Sopenharmony_ci	}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	return rc;
11762306a36Sopenharmony_ci}
11862306a36Sopenharmony_ci
11962306a36Sopenharmony_cistatic void smb2_query_server_interfaces(struct work_struct *work)
12062306a36Sopenharmony_ci{
12162306a36Sopenharmony_ci	int rc;
12262306a36Sopenharmony_ci	struct cifs_tcon *tcon = container_of(work,
12362306a36Sopenharmony_ci					struct cifs_tcon,
12462306a36Sopenharmony_ci					query_interfaces.work);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	/*
12762306a36Sopenharmony_ci	 * query server network interfaces, in case they change
12862306a36Sopenharmony_ci	 */
12962306a36Sopenharmony_ci	rc = SMB3_request_interfaces(0, tcon, false);
13062306a36Sopenharmony_ci	if (rc) {
13162306a36Sopenharmony_ci		if (rc == -EOPNOTSUPP)
13262306a36Sopenharmony_ci			return;
13362306a36Sopenharmony_ci
13462306a36Sopenharmony_ci		cifs_dbg(FYI, "%s: failed to query server interfaces: %d\n",
13562306a36Sopenharmony_ci				__func__, rc);
13662306a36Sopenharmony_ci	}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	queue_delayed_work(cifsiod_wq, &tcon->query_interfaces,
13962306a36Sopenharmony_ci			   (SMB_INTERFACE_POLL_INTERVAL * HZ));
14062306a36Sopenharmony_ci}
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci/*
14362306a36Sopenharmony_ci * Update the tcpStatus for the server.
14462306a36Sopenharmony_ci * This is used to signal the cifsd thread to call cifs_reconnect
14562306a36Sopenharmony_ci * ONLY cifsd thread should call cifs_reconnect. For any other
14662306a36Sopenharmony_ci * thread, use this function
14762306a36Sopenharmony_ci *
14862306a36Sopenharmony_ci * @server: the tcp ses for which reconnect is needed
14962306a36Sopenharmony_ci * @all_channels: if this needs to be done for all channels
15062306a36Sopenharmony_ci */
15162306a36Sopenharmony_civoid
15262306a36Sopenharmony_cicifs_signal_cifsd_for_reconnect(struct TCP_Server_Info *server,
15362306a36Sopenharmony_ci				bool all_channels)
15462306a36Sopenharmony_ci{
15562306a36Sopenharmony_ci	struct TCP_Server_Info *pserver;
15662306a36Sopenharmony_ci	struct cifs_ses *ses;
15762306a36Sopenharmony_ci	int i;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	/* If server is a channel, select the primary channel */
16062306a36Sopenharmony_ci	pserver = SERVER_IS_CHAN(server) ? server->primary_server : server;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	/* if we need to signal just this channel */
16362306a36Sopenharmony_ci	if (!all_channels) {
16462306a36Sopenharmony_ci		spin_lock(&server->srv_lock);
16562306a36Sopenharmony_ci		if (server->tcpStatus != CifsExiting)
16662306a36Sopenharmony_ci			server->tcpStatus = CifsNeedReconnect;
16762306a36Sopenharmony_ci		spin_unlock(&server->srv_lock);
16862306a36Sopenharmony_ci		return;
16962306a36Sopenharmony_ci	}
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
17262306a36Sopenharmony_ci	list_for_each_entry(ses, &pserver->smb_ses_list, smb_ses_list) {
17362306a36Sopenharmony_ci		spin_lock(&ses->chan_lock);
17462306a36Sopenharmony_ci		for (i = 0; i < ses->chan_count; i++) {
17562306a36Sopenharmony_ci			if (!ses->chans[i].server)
17662306a36Sopenharmony_ci				continue;
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci			spin_lock(&ses->chans[i].server->srv_lock);
17962306a36Sopenharmony_ci			if (ses->chans[i].server->tcpStatus != CifsExiting)
18062306a36Sopenharmony_ci				ses->chans[i].server->tcpStatus = CifsNeedReconnect;
18162306a36Sopenharmony_ci			spin_unlock(&ses->chans[i].server->srv_lock);
18262306a36Sopenharmony_ci		}
18362306a36Sopenharmony_ci		spin_unlock(&ses->chan_lock);
18462306a36Sopenharmony_ci	}
18562306a36Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
18662306a36Sopenharmony_ci}
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci/*
18962306a36Sopenharmony_ci * Mark all sessions and tcons for reconnect.
19062306a36Sopenharmony_ci * IMPORTANT: make sure that this gets called only from
19162306a36Sopenharmony_ci * cifsd thread. For any other thread, use
19262306a36Sopenharmony_ci * cifs_signal_cifsd_for_reconnect
19362306a36Sopenharmony_ci *
19462306a36Sopenharmony_ci * @server: the tcp ses for which reconnect is needed
19562306a36Sopenharmony_ci * @server needs to be previously set to CifsNeedReconnect.
19662306a36Sopenharmony_ci * @mark_smb_session: whether even sessions need to be marked
19762306a36Sopenharmony_ci */
19862306a36Sopenharmony_civoid
19962306a36Sopenharmony_cicifs_mark_tcp_ses_conns_for_reconnect(struct TCP_Server_Info *server,
20062306a36Sopenharmony_ci				      bool mark_smb_session)
20162306a36Sopenharmony_ci{
20262306a36Sopenharmony_ci	struct TCP_Server_Info *pserver;
20362306a36Sopenharmony_ci	struct cifs_ses *ses, *nses;
20462306a36Sopenharmony_ci	struct cifs_tcon *tcon;
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	/*
20762306a36Sopenharmony_ci	 * before reconnecting the tcp session, mark the smb session (uid) and the tid bad so they
20862306a36Sopenharmony_ci	 * are not used until reconnected.
20962306a36Sopenharmony_ci	 */
21062306a36Sopenharmony_ci	cifs_dbg(FYI, "%s: marking necessary sessions and tcons for reconnect\n", __func__);
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	/* If server is a channel, select the primary channel */
21362306a36Sopenharmony_ci	pserver = SERVER_IS_CHAN(server) ? server->primary_server : server;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/*
21662306a36Sopenharmony_ci	 * if the server has been marked for termination, there is a
21762306a36Sopenharmony_ci	 * chance that the remaining channels all need reconnect. To be
21862306a36Sopenharmony_ci	 * on the safer side, mark the session and trees for reconnect
21962306a36Sopenharmony_ci	 * for this scenario. This might cause a few redundant session
22062306a36Sopenharmony_ci	 * setup and tree connect requests, but it is better than not doing
22162306a36Sopenharmony_ci	 * a tree connect when needed, and all following requests failing
22262306a36Sopenharmony_ci	 */
22362306a36Sopenharmony_ci	if (server->terminate) {
22462306a36Sopenharmony_ci		mark_smb_session = true;
22562306a36Sopenharmony_ci		server = pserver;
22662306a36Sopenharmony_ci	}
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
22962306a36Sopenharmony_ci	list_for_each_entry_safe(ses, nses, &pserver->smb_ses_list, smb_ses_list) {
23062306a36Sopenharmony_ci		/* check if iface is still active */
23162306a36Sopenharmony_ci		spin_lock(&ses->chan_lock);
23262306a36Sopenharmony_ci		if (cifs_ses_get_chan_index(ses, server) ==
23362306a36Sopenharmony_ci		    CIFS_INVAL_CHAN_INDEX) {
23462306a36Sopenharmony_ci			spin_unlock(&ses->chan_lock);
23562306a36Sopenharmony_ci			continue;
23662306a36Sopenharmony_ci		}
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci		if (!cifs_chan_is_iface_active(ses, server)) {
23962306a36Sopenharmony_ci			spin_unlock(&ses->chan_lock);
24062306a36Sopenharmony_ci			cifs_chan_update_iface(ses, server);
24162306a36Sopenharmony_ci			spin_lock(&ses->chan_lock);
24262306a36Sopenharmony_ci		}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci		if (!mark_smb_session && cifs_chan_needs_reconnect(ses, server)) {
24562306a36Sopenharmony_ci			spin_unlock(&ses->chan_lock);
24662306a36Sopenharmony_ci			continue;
24762306a36Sopenharmony_ci		}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci		if (mark_smb_session)
25062306a36Sopenharmony_ci			CIFS_SET_ALL_CHANS_NEED_RECONNECT(ses);
25162306a36Sopenharmony_ci		else
25262306a36Sopenharmony_ci			cifs_chan_set_need_reconnect(ses, server);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci		cifs_dbg(FYI, "%s: channel connect bitmap: 0x%lx\n",
25562306a36Sopenharmony_ci			 __func__, ses->chans_need_reconnect);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_ci		/* If all channels need reconnect, then tcon needs reconnect */
25862306a36Sopenharmony_ci		if (!mark_smb_session && !CIFS_ALL_CHANS_NEED_RECONNECT(ses)) {
25962306a36Sopenharmony_ci			spin_unlock(&ses->chan_lock);
26062306a36Sopenharmony_ci			continue;
26162306a36Sopenharmony_ci		}
26262306a36Sopenharmony_ci		spin_unlock(&ses->chan_lock);
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci		spin_lock(&ses->ses_lock);
26562306a36Sopenharmony_ci		ses->ses_status = SES_NEED_RECON;
26662306a36Sopenharmony_ci		spin_unlock(&ses->ses_lock);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ci		list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
26962306a36Sopenharmony_ci			tcon->need_reconnect = true;
27062306a36Sopenharmony_ci			spin_lock(&tcon->tc_lock);
27162306a36Sopenharmony_ci			tcon->status = TID_NEED_RECON;
27262306a36Sopenharmony_ci			spin_unlock(&tcon->tc_lock);
27362306a36Sopenharmony_ci
27462306a36Sopenharmony_ci			cancel_delayed_work(&tcon->query_interfaces);
27562306a36Sopenharmony_ci		}
27662306a36Sopenharmony_ci		if (ses->tcon_ipc) {
27762306a36Sopenharmony_ci			ses->tcon_ipc->need_reconnect = true;
27862306a36Sopenharmony_ci			spin_lock(&ses->tcon_ipc->tc_lock);
27962306a36Sopenharmony_ci			ses->tcon_ipc->status = TID_NEED_RECON;
28062306a36Sopenharmony_ci			spin_unlock(&ses->tcon_ipc->tc_lock);
28162306a36Sopenharmony_ci		}
28262306a36Sopenharmony_ci	}
28362306a36Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_cistatic void
28762306a36Sopenharmony_cicifs_abort_connection(struct TCP_Server_Info *server)
28862306a36Sopenharmony_ci{
28962306a36Sopenharmony_ci	struct mid_q_entry *mid, *nmid;
29062306a36Sopenharmony_ci	struct list_head retry_list;
29162306a36Sopenharmony_ci
29262306a36Sopenharmony_ci	server->maxBuf = 0;
29362306a36Sopenharmony_ci	server->max_read = 0;
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	/* do not want to be sending data on a socket we are freeing */
29662306a36Sopenharmony_ci	cifs_dbg(FYI, "%s: tearing down socket\n", __func__);
29762306a36Sopenharmony_ci	cifs_server_lock(server);
29862306a36Sopenharmony_ci	if (server->ssocket) {
29962306a36Sopenharmony_ci		cifs_dbg(FYI, "State: 0x%x Flags: 0x%lx\n", server->ssocket->state,
30062306a36Sopenharmony_ci			 server->ssocket->flags);
30162306a36Sopenharmony_ci		kernel_sock_shutdown(server->ssocket, SHUT_WR);
30262306a36Sopenharmony_ci		cifs_dbg(FYI, "Post shutdown state: 0x%x Flags: 0x%lx\n", server->ssocket->state,
30362306a36Sopenharmony_ci			 server->ssocket->flags);
30462306a36Sopenharmony_ci		sock_release(server->ssocket);
30562306a36Sopenharmony_ci		server->ssocket = NULL;
30662306a36Sopenharmony_ci	}
30762306a36Sopenharmony_ci	server->sequence_number = 0;
30862306a36Sopenharmony_ci	server->session_estab = false;
30962306a36Sopenharmony_ci	kfree_sensitive(server->session_key.response);
31062306a36Sopenharmony_ci	server->session_key.response = NULL;
31162306a36Sopenharmony_ci	server->session_key.len = 0;
31262306a36Sopenharmony_ci	server->lstrp = jiffies;
31362306a36Sopenharmony_ci
31462306a36Sopenharmony_ci	/* mark submitted MIDs for retry and issue callback */
31562306a36Sopenharmony_ci	INIT_LIST_HEAD(&retry_list);
31662306a36Sopenharmony_ci	cifs_dbg(FYI, "%s: moving mids to private list\n", __func__);
31762306a36Sopenharmony_ci	spin_lock(&server->mid_lock);
31862306a36Sopenharmony_ci	list_for_each_entry_safe(mid, nmid, &server->pending_mid_q, qhead) {
31962306a36Sopenharmony_ci		kref_get(&mid->refcount);
32062306a36Sopenharmony_ci		if (mid->mid_state == MID_REQUEST_SUBMITTED)
32162306a36Sopenharmony_ci			mid->mid_state = MID_RETRY_NEEDED;
32262306a36Sopenharmony_ci		list_move(&mid->qhead, &retry_list);
32362306a36Sopenharmony_ci		mid->mid_flags |= MID_DELETED;
32462306a36Sopenharmony_ci	}
32562306a36Sopenharmony_ci	spin_unlock(&server->mid_lock);
32662306a36Sopenharmony_ci	cifs_server_unlock(server);
32762306a36Sopenharmony_ci
32862306a36Sopenharmony_ci	cifs_dbg(FYI, "%s: issuing mid callbacks\n", __func__);
32962306a36Sopenharmony_ci	list_for_each_entry_safe(mid, nmid, &retry_list, qhead) {
33062306a36Sopenharmony_ci		list_del_init(&mid->qhead);
33162306a36Sopenharmony_ci		mid->callback(mid);
33262306a36Sopenharmony_ci		release_mid(mid);
33362306a36Sopenharmony_ci	}
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	if (cifs_rdma_enabled(server)) {
33662306a36Sopenharmony_ci		cifs_server_lock(server);
33762306a36Sopenharmony_ci		smbd_destroy(server);
33862306a36Sopenharmony_ci		cifs_server_unlock(server);
33962306a36Sopenharmony_ci	}
34062306a36Sopenharmony_ci}
34162306a36Sopenharmony_ci
34262306a36Sopenharmony_cistatic bool cifs_tcp_ses_needs_reconnect(struct TCP_Server_Info *server, int num_targets)
34362306a36Sopenharmony_ci{
34462306a36Sopenharmony_ci	spin_lock(&server->srv_lock);
34562306a36Sopenharmony_ci	server->nr_targets = num_targets;
34662306a36Sopenharmony_ci	if (server->tcpStatus == CifsExiting) {
34762306a36Sopenharmony_ci		/* the demux thread will exit normally next time through the loop */
34862306a36Sopenharmony_ci		spin_unlock(&server->srv_lock);
34962306a36Sopenharmony_ci		wake_up(&server->response_q);
35062306a36Sopenharmony_ci		return false;
35162306a36Sopenharmony_ci	}
35262306a36Sopenharmony_ci
35362306a36Sopenharmony_ci	cifs_dbg(FYI, "Mark tcp session as need reconnect\n");
35462306a36Sopenharmony_ci	trace_smb3_reconnect(server->CurrentMid, server->conn_id,
35562306a36Sopenharmony_ci			     server->hostname);
35662306a36Sopenharmony_ci	server->tcpStatus = CifsNeedReconnect;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	spin_unlock(&server->srv_lock);
35962306a36Sopenharmony_ci	return true;
36062306a36Sopenharmony_ci}
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_ci/*
36362306a36Sopenharmony_ci * cifs tcp session reconnection
36462306a36Sopenharmony_ci *
36562306a36Sopenharmony_ci * mark tcp session as reconnecting so temporarily locked
36662306a36Sopenharmony_ci * mark all smb sessions as reconnecting for tcp session
36762306a36Sopenharmony_ci * reconnect tcp session
36862306a36Sopenharmony_ci * wake up waiters on reconnection? - (not needed currently)
36962306a36Sopenharmony_ci *
37062306a36Sopenharmony_ci * if mark_smb_session is passed as true, unconditionally mark
37162306a36Sopenharmony_ci * the smb session (and tcon) for reconnect as well. This value
37262306a36Sopenharmony_ci * doesn't really matter for non-multichannel scenario.
37362306a36Sopenharmony_ci *
37462306a36Sopenharmony_ci */
37562306a36Sopenharmony_cistatic int __cifs_reconnect(struct TCP_Server_Info *server,
37662306a36Sopenharmony_ci			    bool mark_smb_session)
37762306a36Sopenharmony_ci{
37862306a36Sopenharmony_ci	int rc = 0;
37962306a36Sopenharmony_ci
38062306a36Sopenharmony_ci	if (!cifs_tcp_ses_needs_reconnect(server, 1))
38162306a36Sopenharmony_ci		return 0;
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	cifs_mark_tcp_ses_conns_for_reconnect(server, mark_smb_session);
38462306a36Sopenharmony_ci
38562306a36Sopenharmony_ci	cifs_abort_connection(server);
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	do {
38862306a36Sopenharmony_ci		try_to_freeze();
38962306a36Sopenharmony_ci		cifs_server_lock(server);
39062306a36Sopenharmony_ci
39162306a36Sopenharmony_ci		if (!cifs_swn_set_server_dstaddr(server)) {
39262306a36Sopenharmony_ci			/* resolve the hostname again to make sure that IP address is up-to-date */
39362306a36Sopenharmony_ci			rc = reconn_set_ipaddr_from_hostname(server);
39462306a36Sopenharmony_ci			cifs_dbg(FYI, "%s: reconn_set_ipaddr_from_hostname: rc=%d\n", __func__, rc);
39562306a36Sopenharmony_ci		}
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci		if (cifs_rdma_enabled(server))
39862306a36Sopenharmony_ci			rc = smbd_reconnect(server);
39962306a36Sopenharmony_ci		else
40062306a36Sopenharmony_ci			rc = generic_ip_connect(server);
40162306a36Sopenharmony_ci		if (rc) {
40262306a36Sopenharmony_ci			cifs_server_unlock(server);
40362306a36Sopenharmony_ci			cifs_dbg(FYI, "%s: reconnect error %d\n", __func__, rc);
40462306a36Sopenharmony_ci			msleep(3000);
40562306a36Sopenharmony_ci		} else {
40662306a36Sopenharmony_ci			atomic_inc(&tcpSesReconnectCount);
40762306a36Sopenharmony_ci			set_credits(server, 1);
40862306a36Sopenharmony_ci			spin_lock(&server->srv_lock);
40962306a36Sopenharmony_ci			if (server->tcpStatus != CifsExiting)
41062306a36Sopenharmony_ci				server->tcpStatus = CifsNeedNegotiate;
41162306a36Sopenharmony_ci			spin_unlock(&server->srv_lock);
41262306a36Sopenharmony_ci			cifs_swn_reset_server_dstaddr(server);
41362306a36Sopenharmony_ci			cifs_server_unlock(server);
41462306a36Sopenharmony_ci			mod_delayed_work(cifsiod_wq, &server->reconnect, 0);
41562306a36Sopenharmony_ci		}
41662306a36Sopenharmony_ci	} while (server->tcpStatus == CifsNeedReconnect);
41762306a36Sopenharmony_ci
41862306a36Sopenharmony_ci	spin_lock(&server->srv_lock);
41962306a36Sopenharmony_ci	if (server->tcpStatus == CifsNeedNegotiate)
42062306a36Sopenharmony_ci		mod_delayed_work(cifsiod_wq, &server->echo, 0);
42162306a36Sopenharmony_ci	spin_unlock(&server->srv_lock);
42262306a36Sopenharmony_ci
42362306a36Sopenharmony_ci	wake_up(&server->response_q);
42462306a36Sopenharmony_ci	return rc;
42562306a36Sopenharmony_ci}
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci#ifdef CONFIG_CIFS_DFS_UPCALL
42862306a36Sopenharmony_cistatic int __reconnect_target_unlocked(struct TCP_Server_Info *server, const char *target)
42962306a36Sopenharmony_ci{
43062306a36Sopenharmony_ci	int rc;
43162306a36Sopenharmony_ci	char *hostname;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	if (!cifs_swn_set_server_dstaddr(server)) {
43462306a36Sopenharmony_ci		if (server->hostname != target) {
43562306a36Sopenharmony_ci			hostname = extract_hostname(target);
43662306a36Sopenharmony_ci			if (!IS_ERR(hostname)) {
43762306a36Sopenharmony_ci				spin_lock(&server->srv_lock);
43862306a36Sopenharmony_ci				kfree(server->hostname);
43962306a36Sopenharmony_ci				server->hostname = hostname;
44062306a36Sopenharmony_ci				spin_unlock(&server->srv_lock);
44162306a36Sopenharmony_ci			} else {
44262306a36Sopenharmony_ci				cifs_dbg(FYI, "%s: couldn't extract hostname or address from dfs target: %ld\n",
44362306a36Sopenharmony_ci					 __func__, PTR_ERR(hostname));
44462306a36Sopenharmony_ci				cifs_dbg(FYI, "%s: default to last target server: %s\n", __func__,
44562306a36Sopenharmony_ci					 server->hostname);
44662306a36Sopenharmony_ci			}
44762306a36Sopenharmony_ci		}
44862306a36Sopenharmony_ci		/* resolve the hostname again to make sure that IP address is up-to-date. */
44962306a36Sopenharmony_ci		rc = reconn_set_ipaddr_from_hostname(server);
45062306a36Sopenharmony_ci		cifs_dbg(FYI, "%s: reconn_set_ipaddr_from_hostname: rc=%d\n", __func__, rc);
45162306a36Sopenharmony_ci	}
45262306a36Sopenharmony_ci	/* Reconnect the socket */
45362306a36Sopenharmony_ci	if (cifs_rdma_enabled(server))
45462306a36Sopenharmony_ci		rc = smbd_reconnect(server);
45562306a36Sopenharmony_ci	else
45662306a36Sopenharmony_ci		rc = generic_ip_connect(server);
45762306a36Sopenharmony_ci
45862306a36Sopenharmony_ci	return rc;
45962306a36Sopenharmony_ci}
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_cistatic int reconnect_target_unlocked(struct TCP_Server_Info *server, struct dfs_cache_tgt_list *tl,
46262306a36Sopenharmony_ci				     struct dfs_cache_tgt_iterator **target_hint)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	int rc;
46562306a36Sopenharmony_ci	struct dfs_cache_tgt_iterator *tit;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	*target_hint = NULL;
46862306a36Sopenharmony_ci
46962306a36Sopenharmony_ci	/* If dfs target list is empty, then reconnect to last server */
47062306a36Sopenharmony_ci	tit = dfs_cache_get_tgt_iterator(tl);
47162306a36Sopenharmony_ci	if (!tit)
47262306a36Sopenharmony_ci		return __reconnect_target_unlocked(server, server->hostname);
47362306a36Sopenharmony_ci
47462306a36Sopenharmony_ci	/* Otherwise, try every dfs target in @tl */
47562306a36Sopenharmony_ci	for (; tit; tit = dfs_cache_get_next_tgt(tl, tit)) {
47662306a36Sopenharmony_ci		rc = __reconnect_target_unlocked(server, dfs_cache_get_tgt_name(tit));
47762306a36Sopenharmony_ci		if (!rc) {
47862306a36Sopenharmony_ci			*target_hint = tit;
47962306a36Sopenharmony_ci			break;
48062306a36Sopenharmony_ci		}
48162306a36Sopenharmony_ci	}
48262306a36Sopenharmony_ci	return rc;
48362306a36Sopenharmony_ci}
48462306a36Sopenharmony_ci
48562306a36Sopenharmony_cistatic int reconnect_dfs_server(struct TCP_Server_Info *server)
48662306a36Sopenharmony_ci{
48762306a36Sopenharmony_ci	struct dfs_cache_tgt_iterator *target_hint = NULL;
48862306a36Sopenharmony_ci	DFS_CACHE_TGT_LIST(tl);
48962306a36Sopenharmony_ci	int num_targets = 0;
49062306a36Sopenharmony_ci	int rc = 0;
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_ci	/*
49362306a36Sopenharmony_ci	 * Determine the number of dfs targets the referral path in @cifs_sb resolves to.
49462306a36Sopenharmony_ci	 *
49562306a36Sopenharmony_ci	 * smb2_reconnect() needs to know how long it should wait based upon the number of dfs
49662306a36Sopenharmony_ci	 * targets (server->nr_targets).  It's also possible that the cached referral was cleared
49762306a36Sopenharmony_ci	 * through /proc/fs/cifs/dfscache or the target list is empty due to server settings after
49862306a36Sopenharmony_ci	 * refreshing the referral, so, in this case, default it to 1.
49962306a36Sopenharmony_ci	 */
50062306a36Sopenharmony_ci	mutex_lock(&server->refpath_lock);
50162306a36Sopenharmony_ci	if (!dfs_cache_noreq_find(server->leaf_fullpath + 1, NULL, &tl))
50262306a36Sopenharmony_ci		num_targets = dfs_cache_get_nr_tgts(&tl);
50362306a36Sopenharmony_ci	mutex_unlock(&server->refpath_lock);
50462306a36Sopenharmony_ci	if (!num_targets)
50562306a36Sopenharmony_ci		num_targets = 1;
50662306a36Sopenharmony_ci
50762306a36Sopenharmony_ci	if (!cifs_tcp_ses_needs_reconnect(server, num_targets))
50862306a36Sopenharmony_ci		return 0;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	/*
51162306a36Sopenharmony_ci	 * Unconditionally mark all sessions & tcons for reconnect as we might be connecting to a
51262306a36Sopenharmony_ci	 * different server or share during failover.  It could be improved by adding some logic to
51362306a36Sopenharmony_ci	 * only do that in case it connects to a different server or share, though.
51462306a36Sopenharmony_ci	 */
51562306a36Sopenharmony_ci	cifs_mark_tcp_ses_conns_for_reconnect(server, true);
51662306a36Sopenharmony_ci
51762306a36Sopenharmony_ci	cifs_abort_connection(server);
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	do {
52062306a36Sopenharmony_ci		try_to_freeze();
52162306a36Sopenharmony_ci		cifs_server_lock(server);
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci		rc = reconnect_target_unlocked(server, &tl, &target_hint);
52462306a36Sopenharmony_ci		if (rc) {
52562306a36Sopenharmony_ci			/* Failed to reconnect socket */
52662306a36Sopenharmony_ci			cifs_server_unlock(server);
52762306a36Sopenharmony_ci			cifs_dbg(FYI, "%s: reconnect error %d\n", __func__, rc);
52862306a36Sopenharmony_ci			msleep(3000);
52962306a36Sopenharmony_ci			continue;
53062306a36Sopenharmony_ci		}
53162306a36Sopenharmony_ci		/*
53262306a36Sopenharmony_ci		 * Socket was created.  Update tcp session status to CifsNeedNegotiate so that a
53362306a36Sopenharmony_ci		 * process waiting for reconnect will know it needs to re-establish session and tcon
53462306a36Sopenharmony_ci		 * through the reconnected target server.
53562306a36Sopenharmony_ci		 */
53662306a36Sopenharmony_ci		atomic_inc(&tcpSesReconnectCount);
53762306a36Sopenharmony_ci		set_credits(server, 1);
53862306a36Sopenharmony_ci		spin_lock(&server->srv_lock);
53962306a36Sopenharmony_ci		if (server->tcpStatus != CifsExiting)
54062306a36Sopenharmony_ci			server->tcpStatus = CifsNeedNegotiate;
54162306a36Sopenharmony_ci		spin_unlock(&server->srv_lock);
54262306a36Sopenharmony_ci		cifs_swn_reset_server_dstaddr(server);
54362306a36Sopenharmony_ci		cifs_server_unlock(server);
54462306a36Sopenharmony_ci		mod_delayed_work(cifsiod_wq, &server->reconnect, 0);
54562306a36Sopenharmony_ci	} while (server->tcpStatus == CifsNeedReconnect);
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	mutex_lock(&server->refpath_lock);
54862306a36Sopenharmony_ci	dfs_cache_noreq_update_tgthint(server->leaf_fullpath + 1, target_hint);
54962306a36Sopenharmony_ci	mutex_unlock(&server->refpath_lock);
55062306a36Sopenharmony_ci	dfs_cache_free_tgts(&tl);
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci	/* Need to set up echo worker again once connection has been established */
55362306a36Sopenharmony_ci	spin_lock(&server->srv_lock);
55462306a36Sopenharmony_ci	if (server->tcpStatus == CifsNeedNegotiate)
55562306a36Sopenharmony_ci		mod_delayed_work(cifsiod_wq, &server->echo, 0);
55662306a36Sopenharmony_ci	spin_unlock(&server->srv_lock);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	wake_up(&server->response_q);
55962306a36Sopenharmony_ci	return rc;
56062306a36Sopenharmony_ci}
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ciint cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session)
56362306a36Sopenharmony_ci{
56462306a36Sopenharmony_ci	mutex_lock(&server->refpath_lock);
56562306a36Sopenharmony_ci	if (!server->leaf_fullpath) {
56662306a36Sopenharmony_ci		mutex_unlock(&server->refpath_lock);
56762306a36Sopenharmony_ci		return __cifs_reconnect(server, mark_smb_session);
56862306a36Sopenharmony_ci	}
56962306a36Sopenharmony_ci	mutex_unlock(&server->refpath_lock);
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	return reconnect_dfs_server(server);
57262306a36Sopenharmony_ci}
57362306a36Sopenharmony_ci#else
57462306a36Sopenharmony_ciint cifs_reconnect(struct TCP_Server_Info *server, bool mark_smb_session)
57562306a36Sopenharmony_ci{
57662306a36Sopenharmony_ci	return __cifs_reconnect(server, mark_smb_session);
57762306a36Sopenharmony_ci}
57862306a36Sopenharmony_ci#endif
57962306a36Sopenharmony_ci
58062306a36Sopenharmony_cistatic void
58162306a36Sopenharmony_cicifs_echo_request(struct work_struct *work)
58262306a36Sopenharmony_ci{
58362306a36Sopenharmony_ci	int rc;
58462306a36Sopenharmony_ci	struct TCP_Server_Info *server = container_of(work,
58562306a36Sopenharmony_ci					struct TCP_Server_Info, echo.work);
58662306a36Sopenharmony_ci
58762306a36Sopenharmony_ci	/*
58862306a36Sopenharmony_ci	 * We cannot send an echo if it is disabled.
58962306a36Sopenharmony_ci	 * Also, no need to ping if we got a response recently.
59062306a36Sopenharmony_ci	 */
59162306a36Sopenharmony_ci
59262306a36Sopenharmony_ci	if (server->tcpStatus == CifsNeedReconnect ||
59362306a36Sopenharmony_ci	    server->tcpStatus == CifsExiting ||
59462306a36Sopenharmony_ci	    server->tcpStatus == CifsNew ||
59562306a36Sopenharmony_ci	    (server->ops->can_echo && !server->ops->can_echo(server)) ||
59662306a36Sopenharmony_ci	    time_before(jiffies, server->lstrp + server->echo_interval - HZ))
59762306a36Sopenharmony_ci		goto requeue_echo;
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	rc = server->ops->echo ? server->ops->echo(server) : -ENOSYS;
60062306a36Sopenharmony_ci	cifs_server_dbg(FYI, "send echo request: rc = %d\n", rc);
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	/* Check witness registrations */
60362306a36Sopenharmony_ci	cifs_swn_check();
60462306a36Sopenharmony_ci
60562306a36Sopenharmony_cirequeue_echo:
60662306a36Sopenharmony_ci	queue_delayed_work(cifsiod_wq, &server->echo, server->echo_interval);
60762306a36Sopenharmony_ci}
60862306a36Sopenharmony_ci
60962306a36Sopenharmony_cistatic bool
61062306a36Sopenharmony_ciallocate_buffers(struct TCP_Server_Info *server)
61162306a36Sopenharmony_ci{
61262306a36Sopenharmony_ci	if (!server->bigbuf) {
61362306a36Sopenharmony_ci		server->bigbuf = (char *)cifs_buf_get();
61462306a36Sopenharmony_ci		if (!server->bigbuf) {
61562306a36Sopenharmony_ci			cifs_server_dbg(VFS, "No memory for large SMB response\n");
61662306a36Sopenharmony_ci			msleep(3000);
61762306a36Sopenharmony_ci			/* retry will check if exiting */
61862306a36Sopenharmony_ci			return false;
61962306a36Sopenharmony_ci		}
62062306a36Sopenharmony_ci	} else if (server->large_buf) {
62162306a36Sopenharmony_ci		/* we are reusing a dirty large buf, clear its start */
62262306a36Sopenharmony_ci		memset(server->bigbuf, 0, HEADER_SIZE(server));
62362306a36Sopenharmony_ci	}
62462306a36Sopenharmony_ci
62562306a36Sopenharmony_ci	if (!server->smallbuf) {
62662306a36Sopenharmony_ci		server->smallbuf = (char *)cifs_small_buf_get();
62762306a36Sopenharmony_ci		if (!server->smallbuf) {
62862306a36Sopenharmony_ci			cifs_server_dbg(VFS, "No memory for SMB response\n");
62962306a36Sopenharmony_ci			msleep(1000);
63062306a36Sopenharmony_ci			/* retry will check if exiting */
63162306a36Sopenharmony_ci			return false;
63262306a36Sopenharmony_ci		}
63362306a36Sopenharmony_ci		/* beginning of smb buffer is cleared in our buf_get */
63462306a36Sopenharmony_ci	} else {
63562306a36Sopenharmony_ci		/* if existing small buf clear beginning */
63662306a36Sopenharmony_ci		memset(server->smallbuf, 0, HEADER_SIZE(server));
63762306a36Sopenharmony_ci	}
63862306a36Sopenharmony_ci
63962306a36Sopenharmony_ci	return true;
64062306a36Sopenharmony_ci}
64162306a36Sopenharmony_ci
64262306a36Sopenharmony_cistatic bool
64362306a36Sopenharmony_ciserver_unresponsive(struct TCP_Server_Info *server)
64462306a36Sopenharmony_ci{
64562306a36Sopenharmony_ci	/*
64662306a36Sopenharmony_ci	 * We need to wait 3 echo intervals to make sure we handle such
64762306a36Sopenharmony_ci	 * situations right:
64862306a36Sopenharmony_ci	 * 1s  client sends a normal SMB request
64962306a36Sopenharmony_ci	 * 2s  client gets a response
65062306a36Sopenharmony_ci	 * 30s echo workqueue job pops, and decides we got a response recently
65162306a36Sopenharmony_ci	 *     and don't need to send another
65262306a36Sopenharmony_ci	 * ...
65362306a36Sopenharmony_ci	 * 65s kernel_recvmsg times out, and we see that we haven't gotten
65462306a36Sopenharmony_ci	 *     a response in >60s.
65562306a36Sopenharmony_ci	 */
65662306a36Sopenharmony_ci	spin_lock(&server->srv_lock);
65762306a36Sopenharmony_ci	if ((server->tcpStatus == CifsGood ||
65862306a36Sopenharmony_ci	    server->tcpStatus == CifsNeedNegotiate) &&
65962306a36Sopenharmony_ci	    (!server->ops->can_echo || server->ops->can_echo(server)) &&
66062306a36Sopenharmony_ci	    time_after(jiffies, server->lstrp + 3 * server->echo_interval)) {
66162306a36Sopenharmony_ci		spin_unlock(&server->srv_lock);
66262306a36Sopenharmony_ci		cifs_server_dbg(VFS, "has not responded in %lu seconds. Reconnecting...\n",
66362306a36Sopenharmony_ci			 (3 * server->echo_interval) / HZ);
66462306a36Sopenharmony_ci		cifs_reconnect(server, false);
66562306a36Sopenharmony_ci		return true;
66662306a36Sopenharmony_ci	}
66762306a36Sopenharmony_ci	spin_unlock(&server->srv_lock);
66862306a36Sopenharmony_ci
66962306a36Sopenharmony_ci	return false;
67062306a36Sopenharmony_ci}
67162306a36Sopenharmony_ci
67262306a36Sopenharmony_cistatic inline bool
67362306a36Sopenharmony_cizero_credits(struct TCP_Server_Info *server)
67462306a36Sopenharmony_ci{
67562306a36Sopenharmony_ci	int val;
67662306a36Sopenharmony_ci
67762306a36Sopenharmony_ci	spin_lock(&server->req_lock);
67862306a36Sopenharmony_ci	val = server->credits + server->echo_credits + server->oplock_credits;
67962306a36Sopenharmony_ci	if (server->in_flight == 0 && val == 0) {
68062306a36Sopenharmony_ci		spin_unlock(&server->req_lock);
68162306a36Sopenharmony_ci		return true;
68262306a36Sopenharmony_ci	}
68362306a36Sopenharmony_ci	spin_unlock(&server->req_lock);
68462306a36Sopenharmony_ci	return false;
68562306a36Sopenharmony_ci}
68662306a36Sopenharmony_ci
68762306a36Sopenharmony_cistatic int
68862306a36Sopenharmony_cicifs_readv_from_socket(struct TCP_Server_Info *server, struct msghdr *smb_msg)
68962306a36Sopenharmony_ci{
69062306a36Sopenharmony_ci	int length = 0;
69162306a36Sopenharmony_ci	int total_read;
69262306a36Sopenharmony_ci
69362306a36Sopenharmony_ci	for (total_read = 0; msg_data_left(smb_msg); total_read += length) {
69462306a36Sopenharmony_ci		try_to_freeze();
69562306a36Sopenharmony_ci
69662306a36Sopenharmony_ci		/* reconnect if no credits and no requests in flight */
69762306a36Sopenharmony_ci		if (zero_credits(server)) {
69862306a36Sopenharmony_ci			cifs_reconnect(server, false);
69962306a36Sopenharmony_ci			return -ECONNABORTED;
70062306a36Sopenharmony_ci		}
70162306a36Sopenharmony_ci
70262306a36Sopenharmony_ci		if (server_unresponsive(server))
70362306a36Sopenharmony_ci			return -ECONNABORTED;
70462306a36Sopenharmony_ci		if (cifs_rdma_enabled(server) && server->smbd_conn)
70562306a36Sopenharmony_ci			length = smbd_recv(server->smbd_conn, smb_msg);
70662306a36Sopenharmony_ci		else
70762306a36Sopenharmony_ci			length = sock_recvmsg(server->ssocket, smb_msg, 0);
70862306a36Sopenharmony_ci
70962306a36Sopenharmony_ci		spin_lock(&server->srv_lock);
71062306a36Sopenharmony_ci		if (server->tcpStatus == CifsExiting) {
71162306a36Sopenharmony_ci			spin_unlock(&server->srv_lock);
71262306a36Sopenharmony_ci			return -ESHUTDOWN;
71362306a36Sopenharmony_ci		}
71462306a36Sopenharmony_ci
71562306a36Sopenharmony_ci		if (server->tcpStatus == CifsNeedReconnect) {
71662306a36Sopenharmony_ci			spin_unlock(&server->srv_lock);
71762306a36Sopenharmony_ci			cifs_reconnect(server, false);
71862306a36Sopenharmony_ci			return -ECONNABORTED;
71962306a36Sopenharmony_ci		}
72062306a36Sopenharmony_ci		spin_unlock(&server->srv_lock);
72162306a36Sopenharmony_ci
72262306a36Sopenharmony_ci		if (length == -ERESTARTSYS ||
72362306a36Sopenharmony_ci		    length == -EAGAIN ||
72462306a36Sopenharmony_ci		    length == -EINTR) {
72562306a36Sopenharmony_ci			/*
72662306a36Sopenharmony_ci			 * Minimum sleep to prevent looping, allowing socket
72762306a36Sopenharmony_ci			 * to clear and app threads to set tcpStatus
72862306a36Sopenharmony_ci			 * CifsNeedReconnect if server hung.
72962306a36Sopenharmony_ci			 */
73062306a36Sopenharmony_ci			usleep_range(1000, 2000);
73162306a36Sopenharmony_ci			length = 0;
73262306a36Sopenharmony_ci			continue;
73362306a36Sopenharmony_ci		}
73462306a36Sopenharmony_ci
73562306a36Sopenharmony_ci		if (length <= 0) {
73662306a36Sopenharmony_ci			cifs_dbg(FYI, "Received no data or error: %d\n", length);
73762306a36Sopenharmony_ci			cifs_reconnect(server, false);
73862306a36Sopenharmony_ci			return -ECONNABORTED;
73962306a36Sopenharmony_ci		}
74062306a36Sopenharmony_ci	}
74162306a36Sopenharmony_ci	return total_read;
74262306a36Sopenharmony_ci}
74362306a36Sopenharmony_ci
74462306a36Sopenharmony_ciint
74562306a36Sopenharmony_cicifs_read_from_socket(struct TCP_Server_Info *server, char *buf,
74662306a36Sopenharmony_ci		      unsigned int to_read)
74762306a36Sopenharmony_ci{
74862306a36Sopenharmony_ci	struct msghdr smb_msg = {};
74962306a36Sopenharmony_ci	struct kvec iov = {.iov_base = buf, .iov_len = to_read};
75062306a36Sopenharmony_ci	iov_iter_kvec(&smb_msg.msg_iter, ITER_DEST, &iov, 1, to_read);
75162306a36Sopenharmony_ci
75262306a36Sopenharmony_ci	return cifs_readv_from_socket(server, &smb_msg);
75362306a36Sopenharmony_ci}
75462306a36Sopenharmony_ci
75562306a36Sopenharmony_cissize_t
75662306a36Sopenharmony_cicifs_discard_from_socket(struct TCP_Server_Info *server, size_t to_read)
75762306a36Sopenharmony_ci{
75862306a36Sopenharmony_ci	struct msghdr smb_msg = {};
75962306a36Sopenharmony_ci
76062306a36Sopenharmony_ci	/*
76162306a36Sopenharmony_ci	 *  iov_iter_discard already sets smb_msg.type and count and iov_offset
76262306a36Sopenharmony_ci	 *  and cifs_readv_from_socket sets msg_control and msg_controllen
76362306a36Sopenharmony_ci	 *  so little to initialize in struct msghdr
76462306a36Sopenharmony_ci	 */
76562306a36Sopenharmony_ci	iov_iter_discard(&smb_msg.msg_iter, ITER_DEST, to_read);
76662306a36Sopenharmony_ci
76762306a36Sopenharmony_ci	return cifs_readv_from_socket(server, &smb_msg);
76862306a36Sopenharmony_ci}
76962306a36Sopenharmony_ci
77062306a36Sopenharmony_ciint
77162306a36Sopenharmony_cicifs_read_page_from_socket(struct TCP_Server_Info *server, struct page *page,
77262306a36Sopenharmony_ci	unsigned int page_offset, unsigned int to_read)
77362306a36Sopenharmony_ci{
77462306a36Sopenharmony_ci	struct msghdr smb_msg = {};
77562306a36Sopenharmony_ci	struct bio_vec bv;
77662306a36Sopenharmony_ci
77762306a36Sopenharmony_ci	bvec_set_page(&bv, page, to_read, page_offset);
77862306a36Sopenharmony_ci	iov_iter_bvec(&smb_msg.msg_iter, ITER_DEST, &bv, 1, to_read);
77962306a36Sopenharmony_ci	return cifs_readv_from_socket(server, &smb_msg);
78062306a36Sopenharmony_ci}
78162306a36Sopenharmony_ci
78262306a36Sopenharmony_ciint
78362306a36Sopenharmony_cicifs_read_iter_from_socket(struct TCP_Server_Info *server, struct iov_iter *iter,
78462306a36Sopenharmony_ci			   unsigned int to_read)
78562306a36Sopenharmony_ci{
78662306a36Sopenharmony_ci	struct msghdr smb_msg = { .msg_iter = *iter };
78762306a36Sopenharmony_ci	int ret;
78862306a36Sopenharmony_ci
78962306a36Sopenharmony_ci	iov_iter_truncate(&smb_msg.msg_iter, to_read);
79062306a36Sopenharmony_ci	ret = cifs_readv_from_socket(server, &smb_msg);
79162306a36Sopenharmony_ci	if (ret > 0)
79262306a36Sopenharmony_ci		iov_iter_advance(iter, ret);
79362306a36Sopenharmony_ci	return ret;
79462306a36Sopenharmony_ci}
79562306a36Sopenharmony_ci
79662306a36Sopenharmony_cistatic bool
79762306a36Sopenharmony_ciis_smb_response(struct TCP_Server_Info *server, unsigned char type)
79862306a36Sopenharmony_ci{
79962306a36Sopenharmony_ci	/*
80062306a36Sopenharmony_ci	 * The first byte big endian of the length field,
80162306a36Sopenharmony_ci	 * is actually not part of the length but the type
80262306a36Sopenharmony_ci	 * with the most common, zero, as regular data.
80362306a36Sopenharmony_ci	 */
80462306a36Sopenharmony_ci	switch (type) {
80562306a36Sopenharmony_ci	case RFC1002_SESSION_MESSAGE:
80662306a36Sopenharmony_ci		/* Regular SMB response */
80762306a36Sopenharmony_ci		return true;
80862306a36Sopenharmony_ci	case RFC1002_SESSION_KEEP_ALIVE:
80962306a36Sopenharmony_ci		cifs_dbg(FYI, "RFC 1002 session keep alive\n");
81062306a36Sopenharmony_ci		break;
81162306a36Sopenharmony_ci	case RFC1002_POSITIVE_SESSION_RESPONSE:
81262306a36Sopenharmony_ci		cifs_dbg(FYI, "RFC 1002 positive session response\n");
81362306a36Sopenharmony_ci		break;
81462306a36Sopenharmony_ci	case RFC1002_NEGATIVE_SESSION_RESPONSE:
81562306a36Sopenharmony_ci		/*
81662306a36Sopenharmony_ci		 * We get this from Windows 98 instead of an error on
81762306a36Sopenharmony_ci		 * SMB negprot response.
81862306a36Sopenharmony_ci		 */
81962306a36Sopenharmony_ci		cifs_dbg(FYI, "RFC 1002 negative session response\n");
82062306a36Sopenharmony_ci		/* give server a second to clean up */
82162306a36Sopenharmony_ci		msleep(1000);
82262306a36Sopenharmony_ci		/*
82362306a36Sopenharmony_ci		 * Always try 445 first on reconnect since we get NACK
82462306a36Sopenharmony_ci		 * on some if we ever connected to port 139 (the NACK
82562306a36Sopenharmony_ci		 * is since we do not begin with RFC1001 session
82662306a36Sopenharmony_ci		 * initialize frame).
82762306a36Sopenharmony_ci		 */
82862306a36Sopenharmony_ci		cifs_set_port((struct sockaddr *)&server->dstaddr, CIFS_PORT);
82962306a36Sopenharmony_ci		cifs_reconnect(server, true);
83062306a36Sopenharmony_ci		break;
83162306a36Sopenharmony_ci	default:
83262306a36Sopenharmony_ci		cifs_server_dbg(VFS, "RFC 1002 unknown response type 0x%x\n", type);
83362306a36Sopenharmony_ci		cifs_reconnect(server, true);
83462306a36Sopenharmony_ci	}
83562306a36Sopenharmony_ci
83662306a36Sopenharmony_ci	return false;
83762306a36Sopenharmony_ci}
83862306a36Sopenharmony_ci
83962306a36Sopenharmony_civoid
84062306a36Sopenharmony_cidequeue_mid(struct mid_q_entry *mid, bool malformed)
84162306a36Sopenharmony_ci{
84262306a36Sopenharmony_ci#ifdef CONFIG_CIFS_STATS2
84362306a36Sopenharmony_ci	mid->when_received = jiffies;
84462306a36Sopenharmony_ci#endif
84562306a36Sopenharmony_ci	spin_lock(&mid->server->mid_lock);
84662306a36Sopenharmony_ci	if (!malformed)
84762306a36Sopenharmony_ci		mid->mid_state = MID_RESPONSE_RECEIVED;
84862306a36Sopenharmony_ci	else
84962306a36Sopenharmony_ci		mid->mid_state = MID_RESPONSE_MALFORMED;
85062306a36Sopenharmony_ci	/*
85162306a36Sopenharmony_ci	 * Trying to handle/dequeue a mid after the send_recv()
85262306a36Sopenharmony_ci	 * function has finished processing it is a bug.
85362306a36Sopenharmony_ci	 */
85462306a36Sopenharmony_ci	if (mid->mid_flags & MID_DELETED) {
85562306a36Sopenharmony_ci		spin_unlock(&mid->server->mid_lock);
85662306a36Sopenharmony_ci		pr_warn_once("trying to dequeue a deleted mid\n");
85762306a36Sopenharmony_ci	} else {
85862306a36Sopenharmony_ci		list_del_init(&mid->qhead);
85962306a36Sopenharmony_ci		mid->mid_flags |= MID_DELETED;
86062306a36Sopenharmony_ci		spin_unlock(&mid->server->mid_lock);
86162306a36Sopenharmony_ci	}
86262306a36Sopenharmony_ci}
86362306a36Sopenharmony_ci
86462306a36Sopenharmony_cistatic unsigned int
86562306a36Sopenharmony_cismb2_get_credits_from_hdr(char *buffer, struct TCP_Server_Info *server)
86662306a36Sopenharmony_ci{
86762306a36Sopenharmony_ci	struct smb2_hdr *shdr = (struct smb2_hdr *)buffer;
86862306a36Sopenharmony_ci
86962306a36Sopenharmony_ci	/*
87062306a36Sopenharmony_ci	 * SMB1 does not use credits.
87162306a36Sopenharmony_ci	 */
87262306a36Sopenharmony_ci	if (is_smb1(server))
87362306a36Sopenharmony_ci		return 0;
87462306a36Sopenharmony_ci
87562306a36Sopenharmony_ci	return le16_to_cpu(shdr->CreditRequest);
87662306a36Sopenharmony_ci}
87762306a36Sopenharmony_ci
87862306a36Sopenharmony_cistatic void
87962306a36Sopenharmony_cihandle_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server,
88062306a36Sopenharmony_ci	   char *buf, int malformed)
88162306a36Sopenharmony_ci{
88262306a36Sopenharmony_ci	if (server->ops->check_trans2 &&
88362306a36Sopenharmony_ci	    server->ops->check_trans2(mid, server, buf, malformed))
88462306a36Sopenharmony_ci		return;
88562306a36Sopenharmony_ci	mid->credits_received = smb2_get_credits_from_hdr(buf, server);
88662306a36Sopenharmony_ci	mid->resp_buf = buf;
88762306a36Sopenharmony_ci	mid->large_buf = server->large_buf;
88862306a36Sopenharmony_ci	/* Was previous buf put in mpx struct for multi-rsp? */
88962306a36Sopenharmony_ci	if (!mid->multiRsp) {
89062306a36Sopenharmony_ci		/* smb buffer will be freed by user thread */
89162306a36Sopenharmony_ci		if (server->large_buf)
89262306a36Sopenharmony_ci			server->bigbuf = NULL;
89362306a36Sopenharmony_ci		else
89462306a36Sopenharmony_ci			server->smallbuf = NULL;
89562306a36Sopenharmony_ci	}
89662306a36Sopenharmony_ci	dequeue_mid(mid, malformed);
89762306a36Sopenharmony_ci}
89862306a36Sopenharmony_ci
89962306a36Sopenharmony_ciint
90062306a36Sopenharmony_cicifs_enable_signing(struct TCP_Server_Info *server, bool mnt_sign_required)
90162306a36Sopenharmony_ci{
90262306a36Sopenharmony_ci	bool srv_sign_required = server->sec_mode & server->vals->signing_required;
90362306a36Sopenharmony_ci	bool srv_sign_enabled = server->sec_mode & server->vals->signing_enabled;
90462306a36Sopenharmony_ci	bool mnt_sign_enabled;
90562306a36Sopenharmony_ci
90662306a36Sopenharmony_ci	/*
90762306a36Sopenharmony_ci	 * Is signing required by mnt options? If not then check
90862306a36Sopenharmony_ci	 * global_secflags to see if it is there.
90962306a36Sopenharmony_ci	 */
91062306a36Sopenharmony_ci	if (!mnt_sign_required)
91162306a36Sopenharmony_ci		mnt_sign_required = ((global_secflags & CIFSSEC_MUST_SIGN) ==
91262306a36Sopenharmony_ci						CIFSSEC_MUST_SIGN);
91362306a36Sopenharmony_ci
91462306a36Sopenharmony_ci	/*
91562306a36Sopenharmony_ci	 * If signing is required then it's automatically enabled too,
91662306a36Sopenharmony_ci	 * otherwise, check to see if the secflags allow it.
91762306a36Sopenharmony_ci	 */
91862306a36Sopenharmony_ci	mnt_sign_enabled = mnt_sign_required ? mnt_sign_required :
91962306a36Sopenharmony_ci				(global_secflags & CIFSSEC_MAY_SIGN);
92062306a36Sopenharmony_ci
92162306a36Sopenharmony_ci	/* If server requires signing, does client allow it? */
92262306a36Sopenharmony_ci	if (srv_sign_required) {
92362306a36Sopenharmony_ci		if (!mnt_sign_enabled) {
92462306a36Sopenharmony_ci			cifs_dbg(VFS, "Server requires signing, but it's disabled in SecurityFlags!\n");
92562306a36Sopenharmony_ci			return -EOPNOTSUPP;
92662306a36Sopenharmony_ci		}
92762306a36Sopenharmony_ci		server->sign = true;
92862306a36Sopenharmony_ci	}
92962306a36Sopenharmony_ci
93062306a36Sopenharmony_ci	/* If client requires signing, does server allow it? */
93162306a36Sopenharmony_ci	if (mnt_sign_required) {
93262306a36Sopenharmony_ci		if (!srv_sign_enabled) {
93362306a36Sopenharmony_ci			cifs_dbg(VFS, "Server does not support signing!\n");
93462306a36Sopenharmony_ci			return -EOPNOTSUPP;
93562306a36Sopenharmony_ci		}
93662306a36Sopenharmony_ci		server->sign = true;
93762306a36Sopenharmony_ci	}
93862306a36Sopenharmony_ci
93962306a36Sopenharmony_ci	if (cifs_rdma_enabled(server) && server->sign)
94062306a36Sopenharmony_ci		cifs_dbg(VFS, "Signing is enabled, and RDMA read/write will be disabled\n");
94162306a36Sopenharmony_ci
94262306a36Sopenharmony_ci	return 0;
94362306a36Sopenharmony_ci}
94462306a36Sopenharmony_ci
94562306a36Sopenharmony_cistatic noinline_for_stack void
94662306a36Sopenharmony_ciclean_demultiplex_info(struct TCP_Server_Info *server)
94762306a36Sopenharmony_ci{
94862306a36Sopenharmony_ci	int length;
94962306a36Sopenharmony_ci
95062306a36Sopenharmony_ci	/* take it off the list, if it's not already */
95162306a36Sopenharmony_ci	spin_lock(&server->srv_lock);
95262306a36Sopenharmony_ci	list_del_init(&server->tcp_ses_list);
95362306a36Sopenharmony_ci	spin_unlock(&server->srv_lock);
95462306a36Sopenharmony_ci
95562306a36Sopenharmony_ci	cancel_delayed_work_sync(&server->echo);
95662306a36Sopenharmony_ci
95762306a36Sopenharmony_ci	spin_lock(&server->srv_lock);
95862306a36Sopenharmony_ci	server->tcpStatus = CifsExiting;
95962306a36Sopenharmony_ci	spin_unlock(&server->srv_lock);
96062306a36Sopenharmony_ci	wake_up_all(&server->response_q);
96162306a36Sopenharmony_ci
96262306a36Sopenharmony_ci	/* check if we have blocked requests that need to free */
96362306a36Sopenharmony_ci	spin_lock(&server->req_lock);
96462306a36Sopenharmony_ci	if (server->credits <= 0)
96562306a36Sopenharmony_ci		server->credits = 1;
96662306a36Sopenharmony_ci	spin_unlock(&server->req_lock);
96762306a36Sopenharmony_ci	/*
96862306a36Sopenharmony_ci	 * Although there should not be any requests blocked on this queue it
96962306a36Sopenharmony_ci	 * can not hurt to be paranoid and try to wake up requests that may
97062306a36Sopenharmony_ci	 * haven been blocked when more than 50 at time were on the wire to the
97162306a36Sopenharmony_ci	 * same server - they now will see the session is in exit state and get
97262306a36Sopenharmony_ci	 * out of SendReceive.
97362306a36Sopenharmony_ci	 */
97462306a36Sopenharmony_ci	wake_up_all(&server->request_q);
97562306a36Sopenharmony_ci	/* give those requests time to exit */
97662306a36Sopenharmony_ci	msleep(125);
97762306a36Sopenharmony_ci	if (cifs_rdma_enabled(server))
97862306a36Sopenharmony_ci		smbd_destroy(server);
97962306a36Sopenharmony_ci	if (server->ssocket) {
98062306a36Sopenharmony_ci		sock_release(server->ssocket);
98162306a36Sopenharmony_ci		server->ssocket = NULL;
98262306a36Sopenharmony_ci	}
98362306a36Sopenharmony_ci
98462306a36Sopenharmony_ci	if (!list_empty(&server->pending_mid_q)) {
98562306a36Sopenharmony_ci		struct list_head dispose_list;
98662306a36Sopenharmony_ci		struct mid_q_entry *mid_entry;
98762306a36Sopenharmony_ci		struct list_head *tmp, *tmp2;
98862306a36Sopenharmony_ci
98962306a36Sopenharmony_ci		INIT_LIST_HEAD(&dispose_list);
99062306a36Sopenharmony_ci		spin_lock(&server->mid_lock);
99162306a36Sopenharmony_ci		list_for_each_safe(tmp, tmp2, &server->pending_mid_q) {
99262306a36Sopenharmony_ci			mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
99362306a36Sopenharmony_ci			cifs_dbg(FYI, "Clearing mid %llu\n", mid_entry->mid);
99462306a36Sopenharmony_ci			kref_get(&mid_entry->refcount);
99562306a36Sopenharmony_ci			mid_entry->mid_state = MID_SHUTDOWN;
99662306a36Sopenharmony_ci			list_move(&mid_entry->qhead, &dispose_list);
99762306a36Sopenharmony_ci			mid_entry->mid_flags |= MID_DELETED;
99862306a36Sopenharmony_ci		}
99962306a36Sopenharmony_ci		spin_unlock(&server->mid_lock);
100062306a36Sopenharmony_ci
100162306a36Sopenharmony_ci		/* now walk dispose list and issue callbacks */
100262306a36Sopenharmony_ci		list_for_each_safe(tmp, tmp2, &dispose_list) {
100362306a36Sopenharmony_ci			mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
100462306a36Sopenharmony_ci			cifs_dbg(FYI, "Callback mid %llu\n", mid_entry->mid);
100562306a36Sopenharmony_ci			list_del_init(&mid_entry->qhead);
100662306a36Sopenharmony_ci			mid_entry->callback(mid_entry);
100762306a36Sopenharmony_ci			release_mid(mid_entry);
100862306a36Sopenharmony_ci		}
100962306a36Sopenharmony_ci		/* 1/8th of sec is more than enough time for them to exit */
101062306a36Sopenharmony_ci		msleep(125);
101162306a36Sopenharmony_ci	}
101262306a36Sopenharmony_ci
101362306a36Sopenharmony_ci	if (!list_empty(&server->pending_mid_q)) {
101462306a36Sopenharmony_ci		/*
101562306a36Sopenharmony_ci		 * mpx threads have not exited yet give them at least the smb
101662306a36Sopenharmony_ci		 * send timeout time for long ops.
101762306a36Sopenharmony_ci		 *
101862306a36Sopenharmony_ci		 * Due to delays on oplock break requests, we need to wait at
101962306a36Sopenharmony_ci		 * least 45 seconds before giving up on a request getting a
102062306a36Sopenharmony_ci		 * response and going ahead and killing cifsd.
102162306a36Sopenharmony_ci		 */
102262306a36Sopenharmony_ci		cifs_dbg(FYI, "Wait for exit from demultiplex thread\n");
102362306a36Sopenharmony_ci		msleep(46000);
102462306a36Sopenharmony_ci		/*
102562306a36Sopenharmony_ci		 * If threads still have not exited they are probably never
102662306a36Sopenharmony_ci		 * coming home not much else we can do but free the memory.
102762306a36Sopenharmony_ci		 */
102862306a36Sopenharmony_ci	}
102962306a36Sopenharmony_ci
103062306a36Sopenharmony_ci	kfree(server->leaf_fullpath);
103162306a36Sopenharmony_ci	kfree(server);
103262306a36Sopenharmony_ci
103362306a36Sopenharmony_ci	length = atomic_dec_return(&tcpSesAllocCount);
103462306a36Sopenharmony_ci	if (length > 0)
103562306a36Sopenharmony_ci		mempool_resize(cifs_req_poolp, length + cifs_min_rcv);
103662306a36Sopenharmony_ci}
103762306a36Sopenharmony_ci
103862306a36Sopenharmony_cistatic int
103962306a36Sopenharmony_cistandard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
104062306a36Sopenharmony_ci{
104162306a36Sopenharmony_ci	int length;
104262306a36Sopenharmony_ci	char *buf = server->smallbuf;
104362306a36Sopenharmony_ci	unsigned int pdu_length = server->pdu_size;
104462306a36Sopenharmony_ci
104562306a36Sopenharmony_ci	/* make sure this will fit in a large buffer */
104662306a36Sopenharmony_ci	if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server) -
104762306a36Sopenharmony_ci	    HEADER_PREAMBLE_SIZE(server)) {
104862306a36Sopenharmony_ci		cifs_server_dbg(VFS, "SMB response too long (%u bytes)\n", pdu_length);
104962306a36Sopenharmony_ci		cifs_reconnect(server, true);
105062306a36Sopenharmony_ci		return -ECONNABORTED;
105162306a36Sopenharmony_ci	}
105262306a36Sopenharmony_ci
105362306a36Sopenharmony_ci	/* switch to large buffer if too big for a small one */
105462306a36Sopenharmony_ci	if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE - 4) {
105562306a36Sopenharmony_ci		server->large_buf = true;
105662306a36Sopenharmony_ci		memcpy(server->bigbuf, buf, server->total_read);
105762306a36Sopenharmony_ci		buf = server->bigbuf;
105862306a36Sopenharmony_ci	}
105962306a36Sopenharmony_ci
106062306a36Sopenharmony_ci	/* now read the rest */
106162306a36Sopenharmony_ci	length = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1,
106262306a36Sopenharmony_ci				       pdu_length - MID_HEADER_SIZE(server));
106362306a36Sopenharmony_ci
106462306a36Sopenharmony_ci	if (length < 0)
106562306a36Sopenharmony_ci		return length;
106662306a36Sopenharmony_ci	server->total_read += length;
106762306a36Sopenharmony_ci
106862306a36Sopenharmony_ci	dump_smb(buf, server->total_read);
106962306a36Sopenharmony_ci
107062306a36Sopenharmony_ci	return cifs_handle_standard(server, mid);
107162306a36Sopenharmony_ci}
107262306a36Sopenharmony_ci
107362306a36Sopenharmony_ciint
107462306a36Sopenharmony_cicifs_handle_standard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
107562306a36Sopenharmony_ci{
107662306a36Sopenharmony_ci	char *buf = server->large_buf ? server->bigbuf : server->smallbuf;
107762306a36Sopenharmony_ci	int rc;
107862306a36Sopenharmony_ci
107962306a36Sopenharmony_ci	/*
108062306a36Sopenharmony_ci	 * We know that we received enough to get to the MID as we
108162306a36Sopenharmony_ci	 * checked the pdu_length earlier. Now check to see
108262306a36Sopenharmony_ci	 * if the rest of the header is OK.
108362306a36Sopenharmony_ci	 *
108462306a36Sopenharmony_ci	 * 48 bytes is enough to display the header and a little bit
108562306a36Sopenharmony_ci	 * into the payload for debugging purposes.
108662306a36Sopenharmony_ci	 */
108762306a36Sopenharmony_ci	rc = server->ops->check_message(buf, server->total_read, server);
108862306a36Sopenharmony_ci	if (rc)
108962306a36Sopenharmony_ci		cifs_dump_mem("Bad SMB: ", buf,
109062306a36Sopenharmony_ci			min_t(unsigned int, server->total_read, 48));
109162306a36Sopenharmony_ci
109262306a36Sopenharmony_ci	if (server->ops->is_session_expired &&
109362306a36Sopenharmony_ci	    server->ops->is_session_expired(buf)) {
109462306a36Sopenharmony_ci		cifs_reconnect(server, true);
109562306a36Sopenharmony_ci		return -1;
109662306a36Sopenharmony_ci	}
109762306a36Sopenharmony_ci
109862306a36Sopenharmony_ci	if (server->ops->is_status_pending &&
109962306a36Sopenharmony_ci	    server->ops->is_status_pending(buf, server))
110062306a36Sopenharmony_ci		return -1;
110162306a36Sopenharmony_ci
110262306a36Sopenharmony_ci	if (!mid)
110362306a36Sopenharmony_ci		return rc;
110462306a36Sopenharmony_ci
110562306a36Sopenharmony_ci	handle_mid(mid, server, buf, rc);
110662306a36Sopenharmony_ci	return 0;
110762306a36Sopenharmony_ci}
110862306a36Sopenharmony_ci
110962306a36Sopenharmony_cistatic void
111062306a36Sopenharmony_cismb2_add_credits_from_hdr(char *buffer, struct TCP_Server_Info *server)
111162306a36Sopenharmony_ci{
111262306a36Sopenharmony_ci	struct smb2_hdr *shdr = (struct smb2_hdr *)buffer;
111362306a36Sopenharmony_ci	int scredits, in_flight;
111462306a36Sopenharmony_ci
111562306a36Sopenharmony_ci	/*
111662306a36Sopenharmony_ci	 * SMB1 does not use credits.
111762306a36Sopenharmony_ci	 */
111862306a36Sopenharmony_ci	if (is_smb1(server))
111962306a36Sopenharmony_ci		return;
112062306a36Sopenharmony_ci
112162306a36Sopenharmony_ci	if (shdr->CreditRequest) {
112262306a36Sopenharmony_ci		spin_lock(&server->req_lock);
112362306a36Sopenharmony_ci		server->credits += le16_to_cpu(shdr->CreditRequest);
112462306a36Sopenharmony_ci		scredits = server->credits;
112562306a36Sopenharmony_ci		in_flight = server->in_flight;
112662306a36Sopenharmony_ci		spin_unlock(&server->req_lock);
112762306a36Sopenharmony_ci		wake_up(&server->request_q);
112862306a36Sopenharmony_ci
112962306a36Sopenharmony_ci		trace_smb3_hdr_credits(server->CurrentMid,
113062306a36Sopenharmony_ci				server->conn_id, server->hostname, scredits,
113162306a36Sopenharmony_ci				le16_to_cpu(shdr->CreditRequest), in_flight);
113262306a36Sopenharmony_ci		cifs_server_dbg(FYI, "%s: added %u credits total=%d\n",
113362306a36Sopenharmony_ci				__func__, le16_to_cpu(shdr->CreditRequest),
113462306a36Sopenharmony_ci				scredits);
113562306a36Sopenharmony_ci	}
113662306a36Sopenharmony_ci}
113762306a36Sopenharmony_ci
113862306a36Sopenharmony_ci
113962306a36Sopenharmony_cistatic int
114062306a36Sopenharmony_cicifs_demultiplex_thread(void *p)
114162306a36Sopenharmony_ci{
114262306a36Sopenharmony_ci	int i, num_mids, length;
114362306a36Sopenharmony_ci	struct TCP_Server_Info *server = p;
114462306a36Sopenharmony_ci	unsigned int pdu_length;
114562306a36Sopenharmony_ci	unsigned int next_offset;
114662306a36Sopenharmony_ci	char *buf = NULL;
114762306a36Sopenharmony_ci	struct task_struct *task_to_wake = NULL;
114862306a36Sopenharmony_ci	struct mid_q_entry *mids[MAX_COMPOUND];
114962306a36Sopenharmony_ci	char *bufs[MAX_COMPOUND];
115062306a36Sopenharmony_ci	unsigned int noreclaim_flag, num_io_timeout = 0;
115162306a36Sopenharmony_ci	bool pending_reconnect = false;
115262306a36Sopenharmony_ci
115362306a36Sopenharmony_ci	noreclaim_flag = memalloc_noreclaim_save();
115462306a36Sopenharmony_ci	cifs_dbg(FYI, "Demultiplex PID: %d\n", task_pid_nr(current));
115562306a36Sopenharmony_ci
115662306a36Sopenharmony_ci	length = atomic_inc_return(&tcpSesAllocCount);
115762306a36Sopenharmony_ci	if (length > 1)
115862306a36Sopenharmony_ci		mempool_resize(cifs_req_poolp, length + cifs_min_rcv);
115962306a36Sopenharmony_ci
116062306a36Sopenharmony_ci	set_freezable();
116162306a36Sopenharmony_ci	allow_kernel_signal(SIGKILL);
116262306a36Sopenharmony_ci	while (server->tcpStatus != CifsExiting) {
116362306a36Sopenharmony_ci		if (try_to_freeze())
116462306a36Sopenharmony_ci			continue;
116562306a36Sopenharmony_ci
116662306a36Sopenharmony_ci		if (!allocate_buffers(server))
116762306a36Sopenharmony_ci			continue;
116862306a36Sopenharmony_ci
116962306a36Sopenharmony_ci		server->large_buf = false;
117062306a36Sopenharmony_ci		buf = server->smallbuf;
117162306a36Sopenharmony_ci		pdu_length = 4; /* enough to get RFC1001 header */
117262306a36Sopenharmony_ci
117362306a36Sopenharmony_ci		length = cifs_read_from_socket(server, buf, pdu_length);
117462306a36Sopenharmony_ci		if (length < 0)
117562306a36Sopenharmony_ci			continue;
117662306a36Sopenharmony_ci
117762306a36Sopenharmony_ci		if (is_smb1(server))
117862306a36Sopenharmony_ci			server->total_read = length;
117962306a36Sopenharmony_ci		else
118062306a36Sopenharmony_ci			server->total_read = 0;
118162306a36Sopenharmony_ci
118262306a36Sopenharmony_ci		/*
118362306a36Sopenharmony_ci		 * The right amount was read from socket - 4 bytes,
118462306a36Sopenharmony_ci		 * so we can now interpret the length field.
118562306a36Sopenharmony_ci		 */
118662306a36Sopenharmony_ci		pdu_length = get_rfc1002_length(buf);
118762306a36Sopenharmony_ci
118862306a36Sopenharmony_ci		cifs_dbg(FYI, "RFC1002 header 0x%x\n", pdu_length);
118962306a36Sopenharmony_ci		if (!is_smb_response(server, buf[0]))
119062306a36Sopenharmony_ci			continue;
119162306a36Sopenharmony_ci
119262306a36Sopenharmony_ci		pending_reconnect = false;
119362306a36Sopenharmony_cinext_pdu:
119462306a36Sopenharmony_ci		server->pdu_size = pdu_length;
119562306a36Sopenharmony_ci
119662306a36Sopenharmony_ci		/* make sure we have enough to get to the MID */
119762306a36Sopenharmony_ci		if (server->pdu_size < MID_HEADER_SIZE(server)) {
119862306a36Sopenharmony_ci			cifs_server_dbg(VFS, "SMB response too short (%u bytes)\n",
119962306a36Sopenharmony_ci				 server->pdu_size);
120062306a36Sopenharmony_ci			cifs_reconnect(server, true);
120162306a36Sopenharmony_ci			continue;
120262306a36Sopenharmony_ci		}
120362306a36Sopenharmony_ci
120462306a36Sopenharmony_ci		/* read down to the MID */
120562306a36Sopenharmony_ci		length = cifs_read_from_socket(server,
120662306a36Sopenharmony_ci			     buf + HEADER_PREAMBLE_SIZE(server),
120762306a36Sopenharmony_ci			     MID_HEADER_SIZE(server));
120862306a36Sopenharmony_ci		if (length < 0)
120962306a36Sopenharmony_ci			continue;
121062306a36Sopenharmony_ci		server->total_read += length;
121162306a36Sopenharmony_ci
121262306a36Sopenharmony_ci		if (server->ops->next_header) {
121362306a36Sopenharmony_ci			if (server->ops->next_header(server, buf, &next_offset)) {
121462306a36Sopenharmony_ci				cifs_dbg(VFS, "%s: malformed response (next_offset=%u)\n",
121562306a36Sopenharmony_ci					 __func__, next_offset);
121662306a36Sopenharmony_ci				cifs_reconnect(server, true);
121762306a36Sopenharmony_ci				continue;
121862306a36Sopenharmony_ci			}
121962306a36Sopenharmony_ci			if (next_offset)
122062306a36Sopenharmony_ci				server->pdu_size = next_offset;
122162306a36Sopenharmony_ci		}
122262306a36Sopenharmony_ci
122362306a36Sopenharmony_ci		memset(mids, 0, sizeof(mids));
122462306a36Sopenharmony_ci		memset(bufs, 0, sizeof(bufs));
122562306a36Sopenharmony_ci		num_mids = 0;
122662306a36Sopenharmony_ci
122762306a36Sopenharmony_ci		if (server->ops->is_transform_hdr &&
122862306a36Sopenharmony_ci		    server->ops->receive_transform &&
122962306a36Sopenharmony_ci		    server->ops->is_transform_hdr(buf)) {
123062306a36Sopenharmony_ci			length = server->ops->receive_transform(server,
123162306a36Sopenharmony_ci								mids,
123262306a36Sopenharmony_ci								bufs,
123362306a36Sopenharmony_ci								&num_mids);
123462306a36Sopenharmony_ci		} else {
123562306a36Sopenharmony_ci			mids[0] = server->ops->find_mid(server, buf);
123662306a36Sopenharmony_ci			bufs[0] = buf;
123762306a36Sopenharmony_ci			num_mids = 1;
123862306a36Sopenharmony_ci
123962306a36Sopenharmony_ci			if (!mids[0] || !mids[0]->receive)
124062306a36Sopenharmony_ci				length = standard_receive3(server, mids[0]);
124162306a36Sopenharmony_ci			else
124262306a36Sopenharmony_ci				length = mids[0]->receive(server, mids[0]);
124362306a36Sopenharmony_ci		}
124462306a36Sopenharmony_ci
124562306a36Sopenharmony_ci		if (length < 0) {
124662306a36Sopenharmony_ci			for (i = 0; i < num_mids; i++)
124762306a36Sopenharmony_ci				if (mids[i])
124862306a36Sopenharmony_ci					release_mid(mids[i]);
124962306a36Sopenharmony_ci			continue;
125062306a36Sopenharmony_ci		}
125162306a36Sopenharmony_ci
125262306a36Sopenharmony_ci		if (server->ops->is_status_io_timeout &&
125362306a36Sopenharmony_ci		    server->ops->is_status_io_timeout(buf)) {
125462306a36Sopenharmony_ci			num_io_timeout++;
125562306a36Sopenharmony_ci			if (num_io_timeout > MAX_STATUS_IO_TIMEOUT) {
125662306a36Sopenharmony_ci				cifs_server_dbg(VFS,
125762306a36Sopenharmony_ci						"Number of request timeouts exceeded %d. Reconnecting",
125862306a36Sopenharmony_ci						MAX_STATUS_IO_TIMEOUT);
125962306a36Sopenharmony_ci
126062306a36Sopenharmony_ci				pending_reconnect = true;
126162306a36Sopenharmony_ci				num_io_timeout = 0;
126262306a36Sopenharmony_ci			}
126362306a36Sopenharmony_ci		}
126462306a36Sopenharmony_ci
126562306a36Sopenharmony_ci		server->lstrp = jiffies;
126662306a36Sopenharmony_ci
126762306a36Sopenharmony_ci		for (i = 0; i < num_mids; i++) {
126862306a36Sopenharmony_ci			if (mids[i] != NULL) {
126962306a36Sopenharmony_ci				mids[i]->resp_buf_size = server->pdu_size;
127062306a36Sopenharmony_ci
127162306a36Sopenharmony_ci				if (bufs[i] != NULL) {
127262306a36Sopenharmony_ci					if (server->ops->is_network_name_deleted &&
127362306a36Sopenharmony_ci					    server->ops->is_network_name_deleted(bufs[i],
127462306a36Sopenharmony_ci										 server)) {
127562306a36Sopenharmony_ci						cifs_server_dbg(FYI,
127662306a36Sopenharmony_ci								"Share deleted. Reconnect needed");
127762306a36Sopenharmony_ci					}
127862306a36Sopenharmony_ci				}
127962306a36Sopenharmony_ci
128062306a36Sopenharmony_ci				if (!mids[i]->multiRsp || mids[i]->multiEnd)
128162306a36Sopenharmony_ci					mids[i]->callback(mids[i]);
128262306a36Sopenharmony_ci
128362306a36Sopenharmony_ci				release_mid(mids[i]);
128462306a36Sopenharmony_ci			} else if (server->ops->is_oplock_break &&
128562306a36Sopenharmony_ci				   server->ops->is_oplock_break(bufs[i],
128662306a36Sopenharmony_ci								server)) {
128762306a36Sopenharmony_ci				smb2_add_credits_from_hdr(bufs[i], server);
128862306a36Sopenharmony_ci				cifs_dbg(FYI, "Received oplock break\n");
128962306a36Sopenharmony_ci			} else {
129062306a36Sopenharmony_ci				cifs_server_dbg(VFS, "No task to wake, unknown frame received! NumMids %d\n",
129162306a36Sopenharmony_ci						atomic_read(&mid_count));
129262306a36Sopenharmony_ci				cifs_dump_mem("Received Data is: ", bufs[i],
129362306a36Sopenharmony_ci					      HEADER_SIZE(server));
129462306a36Sopenharmony_ci				smb2_add_credits_from_hdr(bufs[i], server);
129562306a36Sopenharmony_ci#ifdef CONFIG_CIFS_DEBUG2
129662306a36Sopenharmony_ci				if (server->ops->dump_detail)
129762306a36Sopenharmony_ci					server->ops->dump_detail(bufs[i],
129862306a36Sopenharmony_ci								 server);
129962306a36Sopenharmony_ci				cifs_dump_mids(server);
130062306a36Sopenharmony_ci#endif /* CIFS_DEBUG2 */
130162306a36Sopenharmony_ci			}
130262306a36Sopenharmony_ci		}
130362306a36Sopenharmony_ci
130462306a36Sopenharmony_ci		if (pdu_length > server->pdu_size) {
130562306a36Sopenharmony_ci			if (!allocate_buffers(server))
130662306a36Sopenharmony_ci				continue;
130762306a36Sopenharmony_ci			pdu_length -= server->pdu_size;
130862306a36Sopenharmony_ci			server->total_read = 0;
130962306a36Sopenharmony_ci			server->large_buf = false;
131062306a36Sopenharmony_ci			buf = server->smallbuf;
131162306a36Sopenharmony_ci			goto next_pdu;
131262306a36Sopenharmony_ci		}
131362306a36Sopenharmony_ci
131462306a36Sopenharmony_ci		/* do this reconnect at the very end after processing all MIDs */
131562306a36Sopenharmony_ci		if (pending_reconnect)
131662306a36Sopenharmony_ci			cifs_reconnect(server, true);
131762306a36Sopenharmony_ci
131862306a36Sopenharmony_ci	} /* end while !EXITING */
131962306a36Sopenharmony_ci
132062306a36Sopenharmony_ci	/* buffer usually freed in free_mid - need to free it here on exit */
132162306a36Sopenharmony_ci	cifs_buf_release(server->bigbuf);
132262306a36Sopenharmony_ci	if (server->smallbuf) /* no sense logging a debug message if NULL */
132362306a36Sopenharmony_ci		cifs_small_buf_release(server->smallbuf);
132462306a36Sopenharmony_ci
132562306a36Sopenharmony_ci	task_to_wake = xchg(&server->tsk, NULL);
132662306a36Sopenharmony_ci	clean_demultiplex_info(server);
132762306a36Sopenharmony_ci
132862306a36Sopenharmony_ci	/* if server->tsk was NULL then wait for a signal before exiting */
132962306a36Sopenharmony_ci	if (!task_to_wake) {
133062306a36Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
133162306a36Sopenharmony_ci		while (!signal_pending(current)) {
133262306a36Sopenharmony_ci			schedule();
133362306a36Sopenharmony_ci			set_current_state(TASK_INTERRUPTIBLE);
133462306a36Sopenharmony_ci		}
133562306a36Sopenharmony_ci		set_current_state(TASK_RUNNING);
133662306a36Sopenharmony_ci	}
133762306a36Sopenharmony_ci
133862306a36Sopenharmony_ci	memalloc_noreclaim_restore(noreclaim_flag);
133962306a36Sopenharmony_ci	module_put_and_kthread_exit(0);
134062306a36Sopenharmony_ci}
134162306a36Sopenharmony_ci
134262306a36Sopenharmony_ciint
134362306a36Sopenharmony_cicifs_ipaddr_cmp(struct sockaddr *srcaddr, struct sockaddr *rhs)
134462306a36Sopenharmony_ci{
134562306a36Sopenharmony_ci	struct sockaddr_in *saddr4 = (struct sockaddr_in *)srcaddr;
134662306a36Sopenharmony_ci	struct sockaddr_in *vaddr4 = (struct sockaddr_in *)rhs;
134762306a36Sopenharmony_ci	struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)srcaddr;
134862306a36Sopenharmony_ci	struct sockaddr_in6 *vaddr6 = (struct sockaddr_in6 *)rhs;
134962306a36Sopenharmony_ci
135062306a36Sopenharmony_ci	switch (srcaddr->sa_family) {
135162306a36Sopenharmony_ci	case AF_UNSPEC:
135262306a36Sopenharmony_ci		switch (rhs->sa_family) {
135362306a36Sopenharmony_ci		case AF_UNSPEC:
135462306a36Sopenharmony_ci			return 0;
135562306a36Sopenharmony_ci		case AF_INET:
135662306a36Sopenharmony_ci		case AF_INET6:
135762306a36Sopenharmony_ci			return 1;
135862306a36Sopenharmony_ci		default:
135962306a36Sopenharmony_ci			return -1;
136062306a36Sopenharmony_ci		}
136162306a36Sopenharmony_ci	case AF_INET: {
136262306a36Sopenharmony_ci		switch (rhs->sa_family) {
136362306a36Sopenharmony_ci		case AF_UNSPEC:
136462306a36Sopenharmony_ci			return -1;
136562306a36Sopenharmony_ci		case AF_INET:
136662306a36Sopenharmony_ci			return memcmp(saddr4, vaddr4,
136762306a36Sopenharmony_ci				      sizeof(struct sockaddr_in));
136862306a36Sopenharmony_ci		case AF_INET6:
136962306a36Sopenharmony_ci			return 1;
137062306a36Sopenharmony_ci		default:
137162306a36Sopenharmony_ci			return -1;
137262306a36Sopenharmony_ci		}
137362306a36Sopenharmony_ci	}
137462306a36Sopenharmony_ci	case AF_INET6: {
137562306a36Sopenharmony_ci		switch (rhs->sa_family) {
137662306a36Sopenharmony_ci		case AF_UNSPEC:
137762306a36Sopenharmony_ci		case AF_INET:
137862306a36Sopenharmony_ci			return -1;
137962306a36Sopenharmony_ci		case AF_INET6:
138062306a36Sopenharmony_ci			return memcmp(saddr6,
138162306a36Sopenharmony_ci				      vaddr6,
138262306a36Sopenharmony_ci				      sizeof(struct sockaddr_in6));
138362306a36Sopenharmony_ci		default:
138462306a36Sopenharmony_ci			return -1;
138562306a36Sopenharmony_ci		}
138662306a36Sopenharmony_ci	}
138762306a36Sopenharmony_ci	default:
138862306a36Sopenharmony_ci		return -1; /* don't expect to be here */
138962306a36Sopenharmony_ci	}
139062306a36Sopenharmony_ci}
139162306a36Sopenharmony_ci
139262306a36Sopenharmony_ci/*
139362306a36Sopenharmony_ci * Returns true if srcaddr isn't specified and rhs isn't specified, or
139462306a36Sopenharmony_ci * if srcaddr is specified and matches the IP address of the rhs argument
139562306a36Sopenharmony_ci */
139662306a36Sopenharmony_cibool
139762306a36Sopenharmony_cicifs_match_ipaddr(struct sockaddr *srcaddr, struct sockaddr *rhs)
139862306a36Sopenharmony_ci{
139962306a36Sopenharmony_ci	switch (srcaddr->sa_family) {
140062306a36Sopenharmony_ci	case AF_UNSPEC:
140162306a36Sopenharmony_ci		return (rhs->sa_family == AF_UNSPEC);
140262306a36Sopenharmony_ci	case AF_INET: {
140362306a36Sopenharmony_ci		struct sockaddr_in *saddr4 = (struct sockaddr_in *)srcaddr;
140462306a36Sopenharmony_ci		struct sockaddr_in *vaddr4 = (struct sockaddr_in *)rhs;
140562306a36Sopenharmony_ci		return (saddr4->sin_addr.s_addr == vaddr4->sin_addr.s_addr);
140662306a36Sopenharmony_ci	}
140762306a36Sopenharmony_ci	case AF_INET6: {
140862306a36Sopenharmony_ci		struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)srcaddr;
140962306a36Sopenharmony_ci		struct sockaddr_in6 *vaddr6 = (struct sockaddr_in6 *)rhs;
141062306a36Sopenharmony_ci		return (ipv6_addr_equal(&saddr6->sin6_addr, &vaddr6->sin6_addr)
141162306a36Sopenharmony_ci			&& saddr6->sin6_scope_id == vaddr6->sin6_scope_id);
141262306a36Sopenharmony_ci	}
141362306a36Sopenharmony_ci	default:
141462306a36Sopenharmony_ci		WARN_ON(1);
141562306a36Sopenharmony_ci		return false; /* don't expect to be here */
141662306a36Sopenharmony_ci	}
141762306a36Sopenharmony_ci}
141862306a36Sopenharmony_ci
141962306a36Sopenharmony_ci/*
142062306a36Sopenharmony_ci * If no port is specified in addr structure, we try to match with 445 port
142162306a36Sopenharmony_ci * and if it fails - with 139 ports. It should be called only if address
142262306a36Sopenharmony_ci * families of server and addr are equal.
142362306a36Sopenharmony_ci */
142462306a36Sopenharmony_cistatic bool
142562306a36Sopenharmony_cimatch_port(struct TCP_Server_Info *server, struct sockaddr *addr)
142662306a36Sopenharmony_ci{
142762306a36Sopenharmony_ci	__be16 port, *sport;
142862306a36Sopenharmony_ci
142962306a36Sopenharmony_ci	/* SMBDirect manages its own ports, don't match it here */
143062306a36Sopenharmony_ci	if (server->rdma)
143162306a36Sopenharmony_ci		return true;
143262306a36Sopenharmony_ci
143362306a36Sopenharmony_ci	switch (addr->sa_family) {
143462306a36Sopenharmony_ci	case AF_INET:
143562306a36Sopenharmony_ci		sport = &((struct sockaddr_in *) &server->dstaddr)->sin_port;
143662306a36Sopenharmony_ci		port = ((struct sockaddr_in *) addr)->sin_port;
143762306a36Sopenharmony_ci		break;
143862306a36Sopenharmony_ci	case AF_INET6:
143962306a36Sopenharmony_ci		sport = &((struct sockaddr_in6 *) &server->dstaddr)->sin6_port;
144062306a36Sopenharmony_ci		port = ((struct sockaddr_in6 *) addr)->sin6_port;
144162306a36Sopenharmony_ci		break;
144262306a36Sopenharmony_ci	default:
144362306a36Sopenharmony_ci		WARN_ON(1);
144462306a36Sopenharmony_ci		return false;
144562306a36Sopenharmony_ci	}
144662306a36Sopenharmony_ci
144762306a36Sopenharmony_ci	if (!port) {
144862306a36Sopenharmony_ci		port = htons(CIFS_PORT);
144962306a36Sopenharmony_ci		if (port == *sport)
145062306a36Sopenharmony_ci			return true;
145162306a36Sopenharmony_ci
145262306a36Sopenharmony_ci		port = htons(RFC1001_PORT);
145362306a36Sopenharmony_ci	}
145462306a36Sopenharmony_ci
145562306a36Sopenharmony_ci	return port == *sport;
145662306a36Sopenharmony_ci}
145762306a36Sopenharmony_ci
145862306a36Sopenharmony_cistatic bool match_server_address(struct TCP_Server_Info *server, struct sockaddr *addr)
145962306a36Sopenharmony_ci{
146062306a36Sopenharmony_ci	if (!cifs_match_ipaddr(addr, (struct sockaddr *)&server->dstaddr))
146162306a36Sopenharmony_ci		return false;
146262306a36Sopenharmony_ci
146362306a36Sopenharmony_ci	return true;
146462306a36Sopenharmony_ci}
146562306a36Sopenharmony_ci
146662306a36Sopenharmony_cistatic bool
146762306a36Sopenharmony_cimatch_security(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
146862306a36Sopenharmony_ci{
146962306a36Sopenharmony_ci	/*
147062306a36Sopenharmony_ci	 * The select_sectype function should either return the ctx->sectype
147162306a36Sopenharmony_ci	 * that was specified, or "Unspecified" if that sectype was not
147262306a36Sopenharmony_ci	 * compatible with the given NEGOTIATE request.
147362306a36Sopenharmony_ci	 */
147462306a36Sopenharmony_ci	if (server->ops->select_sectype(server, ctx->sectype)
147562306a36Sopenharmony_ci	     == Unspecified)
147662306a36Sopenharmony_ci		return false;
147762306a36Sopenharmony_ci
147862306a36Sopenharmony_ci	/*
147962306a36Sopenharmony_ci	 * Now check if signing mode is acceptable. No need to check
148062306a36Sopenharmony_ci	 * global_secflags at this point since if MUST_SIGN is set then
148162306a36Sopenharmony_ci	 * the server->sign had better be too.
148262306a36Sopenharmony_ci	 */
148362306a36Sopenharmony_ci	if (ctx->sign && !server->sign)
148462306a36Sopenharmony_ci		return false;
148562306a36Sopenharmony_ci
148662306a36Sopenharmony_ci	return true;
148762306a36Sopenharmony_ci}
148862306a36Sopenharmony_ci
148962306a36Sopenharmony_ci/* this function must be called with srv_lock held */
149062306a36Sopenharmony_cistatic int match_server(struct TCP_Server_Info *server,
149162306a36Sopenharmony_ci			struct smb3_fs_context *ctx,
149262306a36Sopenharmony_ci			bool match_super)
149362306a36Sopenharmony_ci{
149462306a36Sopenharmony_ci	struct sockaddr *addr = (struct sockaddr *)&ctx->dstaddr;
149562306a36Sopenharmony_ci
149662306a36Sopenharmony_ci	lockdep_assert_held(&server->srv_lock);
149762306a36Sopenharmony_ci
149862306a36Sopenharmony_ci	if (ctx->nosharesock)
149962306a36Sopenharmony_ci		return 0;
150062306a36Sopenharmony_ci
150162306a36Sopenharmony_ci	/* this server does not share socket */
150262306a36Sopenharmony_ci	if (server->nosharesock)
150362306a36Sopenharmony_ci		return 0;
150462306a36Sopenharmony_ci
150562306a36Sopenharmony_ci	/* If multidialect negotiation see if existing sessions match one */
150662306a36Sopenharmony_ci	if (strcmp(ctx->vals->version_string, SMB3ANY_VERSION_STRING) == 0) {
150762306a36Sopenharmony_ci		if (server->vals->protocol_id < SMB30_PROT_ID)
150862306a36Sopenharmony_ci			return 0;
150962306a36Sopenharmony_ci	} else if (strcmp(ctx->vals->version_string,
151062306a36Sopenharmony_ci		   SMBDEFAULT_VERSION_STRING) == 0) {
151162306a36Sopenharmony_ci		if (server->vals->protocol_id < SMB21_PROT_ID)
151262306a36Sopenharmony_ci			return 0;
151362306a36Sopenharmony_ci	} else if ((server->vals != ctx->vals) || (server->ops != ctx->ops))
151462306a36Sopenharmony_ci		return 0;
151562306a36Sopenharmony_ci
151662306a36Sopenharmony_ci	if (!net_eq(cifs_net_ns(server), current->nsproxy->net_ns))
151762306a36Sopenharmony_ci		return 0;
151862306a36Sopenharmony_ci
151962306a36Sopenharmony_ci	if (!cifs_match_ipaddr((struct sockaddr *)&ctx->srcaddr,
152062306a36Sopenharmony_ci			       (struct sockaddr *)&server->srcaddr))
152162306a36Sopenharmony_ci		return 0;
152262306a36Sopenharmony_ci	/*
152362306a36Sopenharmony_ci	 * When matching cifs.ko superblocks (@match_super == true), we can't
152462306a36Sopenharmony_ci	 * really match either @server->leaf_fullpath or @server->dstaddr
152562306a36Sopenharmony_ci	 * directly since this @server might belong to a completely different
152662306a36Sopenharmony_ci	 * server -- in case of domain-based DFS referrals or DFS links -- as
152762306a36Sopenharmony_ci	 * provided earlier by mount(2) through 'source' and 'ip' options.
152862306a36Sopenharmony_ci	 *
152962306a36Sopenharmony_ci	 * Otherwise, match the DFS referral in @server->leaf_fullpath or the
153062306a36Sopenharmony_ci	 * destination address in @server->dstaddr.
153162306a36Sopenharmony_ci	 *
153262306a36Sopenharmony_ci	 * When using 'nodfs' mount option, we avoid sharing it with DFS
153362306a36Sopenharmony_ci	 * connections as they might failover.
153462306a36Sopenharmony_ci	 */
153562306a36Sopenharmony_ci	if (!match_super) {
153662306a36Sopenharmony_ci		if (!ctx->nodfs) {
153762306a36Sopenharmony_ci			if (server->leaf_fullpath) {
153862306a36Sopenharmony_ci				if (!ctx->leaf_fullpath ||
153962306a36Sopenharmony_ci				    strcasecmp(server->leaf_fullpath,
154062306a36Sopenharmony_ci					       ctx->leaf_fullpath))
154162306a36Sopenharmony_ci					return 0;
154262306a36Sopenharmony_ci			} else if (ctx->leaf_fullpath) {
154362306a36Sopenharmony_ci				return 0;
154462306a36Sopenharmony_ci			}
154562306a36Sopenharmony_ci		} else if (server->leaf_fullpath) {
154662306a36Sopenharmony_ci			return 0;
154762306a36Sopenharmony_ci		}
154862306a36Sopenharmony_ci	}
154962306a36Sopenharmony_ci
155062306a36Sopenharmony_ci	/*
155162306a36Sopenharmony_ci	 * Match for a regular connection (address/hostname/port) which has no
155262306a36Sopenharmony_ci	 * DFS referrals set.
155362306a36Sopenharmony_ci	 */
155462306a36Sopenharmony_ci	if (!server->leaf_fullpath &&
155562306a36Sopenharmony_ci	    (strcasecmp(server->hostname, ctx->server_hostname) ||
155662306a36Sopenharmony_ci	     !match_server_address(server, addr) ||
155762306a36Sopenharmony_ci	     !match_port(server, addr)))
155862306a36Sopenharmony_ci		return 0;
155962306a36Sopenharmony_ci
156062306a36Sopenharmony_ci	if (!match_security(server, ctx))
156162306a36Sopenharmony_ci		return 0;
156262306a36Sopenharmony_ci
156362306a36Sopenharmony_ci	if (server->echo_interval != ctx->echo_interval * HZ)
156462306a36Sopenharmony_ci		return 0;
156562306a36Sopenharmony_ci
156662306a36Sopenharmony_ci	if (server->rdma != ctx->rdma)
156762306a36Sopenharmony_ci		return 0;
156862306a36Sopenharmony_ci
156962306a36Sopenharmony_ci	if (server->ignore_signature != ctx->ignore_signature)
157062306a36Sopenharmony_ci		return 0;
157162306a36Sopenharmony_ci
157262306a36Sopenharmony_ci	if (server->min_offload != ctx->min_offload)
157362306a36Sopenharmony_ci		return 0;
157462306a36Sopenharmony_ci
157562306a36Sopenharmony_ci	return 1;
157662306a36Sopenharmony_ci}
157762306a36Sopenharmony_ci
157862306a36Sopenharmony_cistruct TCP_Server_Info *
157962306a36Sopenharmony_cicifs_find_tcp_session(struct smb3_fs_context *ctx)
158062306a36Sopenharmony_ci{
158162306a36Sopenharmony_ci	struct TCP_Server_Info *server;
158262306a36Sopenharmony_ci
158362306a36Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
158462306a36Sopenharmony_ci	list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
158562306a36Sopenharmony_ci		spin_lock(&server->srv_lock);
158662306a36Sopenharmony_ci		/*
158762306a36Sopenharmony_ci		 * Skip ses channels since they're only handled in lower layers
158862306a36Sopenharmony_ci		 * (e.g. cifs_send_recv).
158962306a36Sopenharmony_ci		 */
159062306a36Sopenharmony_ci		if (SERVER_IS_CHAN(server) ||
159162306a36Sopenharmony_ci		    !match_server(server, ctx, false)) {
159262306a36Sopenharmony_ci			spin_unlock(&server->srv_lock);
159362306a36Sopenharmony_ci			continue;
159462306a36Sopenharmony_ci		}
159562306a36Sopenharmony_ci		spin_unlock(&server->srv_lock);
159662306a36Sopenharmony_ci
159762306a36Sopenharmony_ci		++server->srv_count;
159862306a36Sopenharmony_ci		spin_unlock(&cifs_tcp_ses_lock);
159962306a36Sopenharmony_ci		cifs_dbg(FYI, "Existing tcp session with server found\n");
160062306a36Sopenharmony_ci		return server;
160162306a36Sopenharmony_ci	}
160262306a36Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
160362306a36Sopenharmony_ci	return NULL;
160462306a36Sopenharmony_ci}
160562306a36Sopenharmony_ci
160662306a36Sopenharmony_civoid
160762306a36Sopenharmony_cicifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
160862306a36Sopenharmony_ci{
160962306a36Sopenharmony_ci	struct task_struct *task;
161062306a36Sopenharmony_ci
161162306a36Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
161262306a36Sopenharmony_ci	if (--server->srv_count > 0) {
161362306a36Sopenharmony_ci		spin_unlock(&cifs_tcp_ses_lock);
161462306a36Sopenharmony_ci		return;
161562306a36Sopenharmony_ci	}
161662306a36Sopenharmony_ci
161762306a36Sopenharmony_ci	/* srv_count can never go negative */
161862306a36Sopenharmony_ci	WARN_ON(server->srv_count < 0);
161962306a36Sopenharmony_ci
162062306a36Sopenharmony_ci	put_net(cifs_net_ns(server));
162162306a36Sopenharmony_ci
162262306a36Sopenharmony_ci	list_del_init(&server->tcp_ses_list);
162362306a36Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
162462306a36Sopenharmony_ci
162562306a36Sopenharmony_ci	cancel_delayed_work_sync(&server->echo);
162662306a36Sopenharmony_ci
162762306a36Sopenharmony_ci	if (from_reconnect)
162862306a36Sopenharmony_ci		/*
162962306a36Sopenharmony_ci		 * Avoid deadlock here: reconnect work calls
163062306a36Sopenharmony_ci		 * cifs_put_tcp_session() at its end. Need to be sure
163162306a36Sopenharmony_ci		 * that reconnect work does nothing with server pointer after
163262306a36Sopenharmony_ci		 * that step.
163362306a36Sopenharmony_ci		 */
163462306a36Sopenharmony_ci		cancel_delayed_work(&server->reconnect);
163562306a36Sopenharmony_ci	else
163662306a36Sopenharmony_ci		cancel_delayed_work_sync(&server->reconnect);
163762306a36Sopenharmony_ci
163862306a36Sopenharmony_ci	/* For secondary channels, we pick up ref-count on the primary server */
163962306a36Sopenharmony_ci	if (SERVER_IS_CHAN(server))
164062306a36Sopenharmony_ci		cifs_put_tcp_session(server->primary_server, from_reconnect);
164162306a36Sopenharmony_ci
164262306a36Sopenharmony_ci	spin_lock(&server->srv_lock);
164362306a36Sopenharmony_ci	server->tcpStatus = CifsExiting;
164462306a36Sopenharmony_ci	spin_unlock(&server->srv_lock);
164562306a36Sopenharmony_ci
164662306a36Sopenharmony_ci	cifs_crypto_secmech_release(server);
164762306a36Sopenharmony_ci
164862306a36Sopenharmony_ci	kfree_sensitive(server->session_key.response);
164962306a36Sopenharmony_ci	server->session_key.response = NULL;
165062306a36Sopenharmony_ci	server->session_key.len = 0;
165162306a36Sopenharmony_ci	kfree(server->hostname);
165262306a36Sopenharmony_ci	server->hostname = NULL;
165362306a36Sopenharmony_ci
165462306a36Sopenharmony_ci	task = xchg(&server->tsk, NULL);
165562306a36Sopenharmony_ci	if (task)
165662306a36Sopenharmony_ci		send_sig(SIGKILL, task, 1);
165762306a36Sopenharmony_ci}
165862306a36Sopenharmony_ci
165962306a36Sopenharmony_cistruct TCP_Server_Info *
166062306a36Sopenharmony_cicifs_get_tcp_session(struct smb3_fs_context *ctx,
166162306a36Sopenharmony_ci		     struct TCP_Server_Info *primary_server)
166262306a36Sopenharmony_ci{
166362306a36Sopenharmony_ci	struct TCP_Server_Info *tcp_ses = NULL;
166462306a36Sopenharmony_ci	int rc;
166562306a36Sopenharmony_ci
166662306a36Sopenharmony_ci	cifs_dbg(FYI, "UNC: %s\n", ctx->UNC);
166762306a36Sopenharmony_ci
166862306a36Sopenharmony_ci	/* see if we already have a matching tcp_ses */
166962306a36Sopenharmony_ci	tcp_ses = cifs_find_tcp_session(ctx);
167062306a36Sopenharmony_ci	if (tcp_ses)
167162306a36Sopenharmony_ci		return tcp_ses;
167262306a36Sopenharmony_ci
167362306a36Sopenharmony_ci	tcp_ses = kzalloc(sizeof(struct TCP_Server_Info), GFP_KERNEL);
167462306a36Sopenharmony_ci	if (!tcp_ses) {
167562306a36Sopenharmony_ci		rc = -ENOMEM;
167662306a36Sopenharmony_ci		goto out_err;
167762306a36Sopenharmony_ci	}
167862306a36Sopenharmony_ci
167962306a36Sopenharmony_ci	tcp_ses->hostname = kstrdup(ctx->server_hostname, GFP_KERNEL);
168062306a36Sopenharmony_ci	if (!tcp_ses->hostname) {
168162306a36Sopenharmony_ci		rc = -ENOMEM;
168262306a36Sopenharmony_ci		goto out_err;
168362306a36Sopenharmony_ci	}
168462306a36Sopenharmony_ci
168562306a36Sopenharmony_ci	if (ctx->leaf_fullpath) {
168662306a36Sopenharmony_ci		tcp_ses->leaf_fullpath = kstrdup(ctx->leaf_fullpath, GFP_KERNEL);
168762306a36Sopenharmony_ci		if (!tcp_ses->leaf_fullpath) {
168862306a36Sopenharmony_ci			rc = -ENOMEM;
168962306a36Sopenharmony_ci			goto out_err;
169062306a36Sopenharmony_ci		}
169162306a36Sopenharmony_ci	}
169262306a36Sopenharmony_ci
169362306a36Sopenharmony_ci	if (ctx->nosharesock)
169462306a36Sopenharmony_ci		tcp_ses->nosharesock = true;
169562306a36Sopenharmony_ci
169662306a36Sopenharmony_ci	tcp_ses->ops = ctx->ops;
169762306a36Sopenharmony_ci	tcp_ses->vals = ctx->vals;
169862306a36Sopenharmony_ci	cifs_set_net_ns(tcp_ses, get_net(current->nsproxy->net_ns));
169962306a36Sopenharmony_ci
170062306a36Sopenharmony_ci	tcp_ses->conn_id = atomic_inc_return(&tcpSesNextId);
170162306a36Sopenharmony_ci	tcp_ses->noblockcnt = ctx->rootfs;
170262306a36Sopenharmony_ci	tcp_ses->noblocksnd = ctx->noblocksnd || ctx->rootfs;
170362306a36Sopenharmony_ci	tcp_ses->noautotune = ctx->noautotune;
170462306a36Sopenharmony_ci	tcp_ses->tcp_nodelay = ctx->sockopt_tcp_nodelay;
170562306a36Sopenharmony_ci	tcp_ses->rdma = ctx->rdma;
170662306a36Sopenharmony_ci	tcp_ses->in_flight = 0;
170762306a36Sopenharmony_ci	tcp_ses->max_in_flight = 0;
170862306a36Sopenharmony_ci	tcp_ses->credits = 1;
170962306a36Sopenharmony_ci	if (primary_server) {
171062306a36Sopenharmony_ci		spin_lock(&cifs_tcp_ses_lock);
171162306a36Sopenharmony_ci		++primary_server->srv_count;
171262306a36Sopenharmony_ci		spin_unlock(&cifs_tcp_ses_lock);
171362306a36Sopenharmony_ci		tcp_ses->primary_server = primary_server;
171462306a36Sopenharmony_ci	}
171562306a36Sopenharmony_ci	init_waitqueue_head(&tcp_ses->response_q);
171662306a36Sopenharmony_ci	init_waitqueue_head(&tcp_ses->request_q);
171762306a36Sopenharmony_ci	INIT_LIST_HEAD(&tcp_ses->pending_mid_q);
171862306a36Sopenharmony_ci	mutex_init(&tcp_ses->_srv_mutex);
171962306a36Sopenharmony_ci	memcpy(tcp_ses->workstation_RFC1001_name,
172062306a36Sopenharmony_ci		ctx->source_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL);
172162306a36Sopenharmony_ci	memcpy(tcp_ses->server_RFC1001_name,
172262306a36Sopenharmony_ci		ctx->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL);
172362306a36Sopenharmony_ci	tcp_ses->session_estab = false;
172462306a36Sopenharmony_ci	tcp_ses->sequence_number = 0;
172562306a36Sopenharmony_ci	tcp_ses->channel_sequence_num = 0; /* only tracked for primary channel */
172662306a36Sopenharmony_ci	tcp_ses->reconnect_instance = 1;
172762306a36Sopenharmony_ci	tcp_ses->lstrp = jiffies;
172862306a36Sopenharmony_ci	tcp_ses->compress_algorithm = cpu_to_le16(ctx->compression);
172962306a36Sopenharmony_ci	spin_lock_init(&tcp_ses->req_lock);
173062306a36Sopenharmony_ci	spin_lock_init(&tcp_ses->srv_lock);
173162306a36Sopenharmony_ci	spin_lock_init(&tcp_ses->mid_lock);
173262306a36Sopenharmony_ci	INIT_LIST_HEAD(&tcp_ses->tcp_ses_list);
173362306a36Sopenharmony_ci	INIT_LIST_HEAD(&tcp_ses->smb_ses_list);
173462306a36Sopenharmony_ci	INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request);
173562306a36Sopenharmony_ci	INIT_DELAYED_WORK(&tcp_ses->reconnect, smb2_reconnect_server);
173662306a36Sopenharmony_ci	mutex_init(&tcp_ses->reconnect_mutex);
173762306a36Sopenharmony_ci#ifdef CONFIG_CIFS_DFS_UPCALL
173862306a36Sopenharmony_ci	mutex_init(&tcp_ses->refpath_lock);
173962306a36Sopenharmony_ci#endif
174062306a36Sopenharmony_ci	memcpy(&tcp_ses->srcaddr, &ctx->srcaddr,
174162306a36Sopenharmony_ci	       sizeof(tcp_ses->srcaddr));
174262306a36Sopenharmony_ci	memcpy(&tcp_ses->dstaddr, &ctx->dstaddr,
174362306a36Sopenharmony_ci		sizeof(tcp_ses->dstaddr));
174462306a36Sopenharmony_ci	if (ctx->use_client_guid)
174562306a36Sopenharmony_ci		memcpy(tcp_ses->client_guid, ctx->client_guid,
174662306a36Sopenharmony_ci		       SMB2_CLIENT_GUID_SIZE);
174762306a36Sopenharmony_ci	else
174862306a36Sopenharmony_ci		generate_random_uuid(tcp_ses->client_guid);
174962306a36Sopenharmony_ci	/*
175062306a36Sopenharmony_ci	 * at this point we are the only ones with the pointer
175162306a36Sopenharmony_ci	 * to the struct since the kernel thread not created yet
175262306a36Sopenharmony_ci	 * no need to spinlock this init of tcpStatus or srv_count
175362306a36Sopenharmony_ci	 */
175462306a36Sopenharmony_ci	tcp_ses->tcpStatus = CifsNew;
175562306a36Sopenharmony_ci	++tcp_ses->srv_count;
175662306a36Sopenharmony_ci
175762306a36Sopenharmony_ci	if (ctx->echo_interval >= SMB_ECHO_INTERVAL_MIN &&
175862306a36Sopenharmony_ci		ctx->echo_interval <= SMB_ECHO_INTERVAL_MAX)
175962306a36Sopenharmony_ci		tcp_ses->echo_interval = ctx->echo_interval * HZ;
176062306a36Sopenharmony_ci	else
176162306a36Sopenharmony_ci		tcp_ses->echo_interval = SMB_ECHO_INTERVAL_DEFAULT * HZ;
176262306a36Sopenharmony_ci	if (tcp_ses->rdma) {
176362306a36Sopenharmony_ci#ifndef CONFIG_CIFS_SMB_DIRECT
176462306a36Sopenharmony_ci		cifs_dbg(VFS, "CONFIG_CIFS_SMB_DIRECT is not enabled\n");
176562306a36Sopenharmony_ci		rc = -ENOENT;
176662306a36Sopenharmony_ci		goto out_err_crypto_release;
176762306a36Sopenharmony_ci#endif
176862306a36Sopenharmony_ci		tcp_ses->smbd_conn = smbd_get_connection(
176962306a36Sopenharmony_ci			tcp_ses, (struct sockaddr *)&ctx->dstaddr);
177062306a36Sopenharmony_ci		if (tcp_ses->smbd_conn) {
177162306a36Sopenharmony_ci			cifs_dbg(VFS, "RDMA transport established\n");
177262306a36Sopenharmony_ci			rc = 0;
177362306a36Sopenharmony_ci			goto smbd_connected;
177462306a36Sopenharmony_ci		} else {
177562306a36Sopenharmony_ci			rc = -ENOENT;
177662306a36Sopenharmony_ci			goto out_err_crypto_release;
177762306a36Sopenharmony_ci		}
177862306a36Sopenharmony_ci	}
177962306a36Sopenharmony_ci	rc = ip_connect(tcp_ses);
178062306a36Sopenharmony_ci	if (rc < 0) {
178162306a36Sopenharmony_ci		cifs_dbg(VFS, "Error connecting to socket. Aborting operation.\n");
178262306a36Sopenharmony_ci		goto out_err_crypto_release;
178362306a36Sopenharmony_ci	}
178462306a36Sopenharmony_cismbd_connected:
178562306a36Sopenharmony_ci	/*
178662306a36Sopenharmony_ci	 * since we're in a cifs function already, we know that
178762306a36Sopenharmony_ci	 * this will succeed. No need for try_module_get().
178862306a36Sopenharmony_ci	 */
178962306a36Sopenharmony_ci	__module_get(THIS_MODULE);
179062306a36Sopenharmony_ci	tcp_ses->tsk = kthread_run(cifs_demultiplex_thread,
179162306a36Sopenharmony_ci				  tcp_ses, "cifsd");
179262306a36Sopenharmony_ci	if (IS_ERR(tcp_ses->tsk)) {
179362306a36Sopenharmony_ci		rc = PTR_ERR(tcp_ses->tsk);
179462306a36Sopenharmony_ci		cifs_dbg(VFS, "error %d create cifsd thread\n", rc);
179562306a36Sopenharmony_ci		module_put(THIS_MODULE);
179662306a36Sopenharmony_ci		goto out_err_crypto_release;
179762306a36Sopenharmony_ci	}
179862306a36Sopenharmony_ci	tcp_ses->min_offload = ctx->min_offload;
179962306a36Sopenharmony_ci	/*
180062306a36Sopenharmony_ci	 * at this point we are the only ones with the pointer
180162306a36Sopenharmony_ci	 * to the struct since the kernel thread not created yet
180262306a36Sopenharmony_ci	 * no need to spinlock this update of tcpStatus
180362306a36Sopenharmony_ci	 */
180462306a36Sopenharmony_ci	spin_lock(&tcp_ses->srv_lock);
180562306a36Sopenharmony_ci	tcp_ses->tcpStatus = CifsNeedNegotiate;
180662306a36Sopenharmony_ci	spin_unlock(&tcp_ses->srv_lock);
180762306a36Sopenharmony_ci
180862306a36Sopenharmony_ci	if ((ctx->max_credits < 20) || (ctx->max_credits > 60000))
180962306a36Sopenharmony_ci		tcp_ses->max_credits = SMB2_MAX_CREDITS_AVAILABLE;
181062306a36Sopenharmony_ci	else
181162306a36Sopenharmony_ci		tcp_ses->max_credits = ctx->max_credits;
181262306a36Sopenharmony_ci
181362306a36Sopenharmony_ci	tcp_ses->nr_targets = 1;
181462306a36Sopenharmony_ci	tcp_ses->ignore_signature = ctx->ignore_signature;
181562306a36Sopenharmony_ci	/* thread spawned, put it on the list */
181662306a36Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
181762306a36Sopenharmony_ci	list_add(&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list);
181862306a36Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
181962306a36Sopenharmony_ci
182062306a36Sopenharmony_ci	/* queue echo request delayed work */
182162306a36Sopenharmony_ci	queue_delayed_work(cifsiod_wq, &tcp_ses->echo, tcp_ses->echo_interval);
182262306a36Sopenharmony_ci
182362306a36Sopenharmony_ci	return tcp_ses;
182462306a36Sopenharmony_ci
182562306a36Sopenharmony_ciout_err_crypto_release:
182662306a36Sopenharmony_ci	cifs_crypto_secmech_release(tcp_ses);
182762306a36Sopenharmony_ci
182862306a36Sopenharmony_ci	put_net(cifs_net_ns(tcp_ses));
182962306a36Sopenharmony_ci
183062306a36Sopenharmony_ciout_err:
183162306a36Sopenharmony_ci	if (tcp_ses) {
183262306a36Sopenharmony_ci		if (SERVER_IS_CHAN(tcp_ses))
183362306a36Sopenharmony_ci			cifs_put_tcp_session(tcp_ses->primary_server, false);
183462306a36Sopenharmony_ci		kfree(tcp_ses->hostname);
183562306a36Sopenharmony_ci		kfree(tcp_ses->leaf_fullpath);
183662306a36Sopenharmony_ci		if (tcp_ses->ssocket)
183762306a36Sopenharmony_ci			sock_release(tcp_ses->ssocket);
183862306a36Sopenharmony_ci		kfree(tcp_ses);
183962306a36Sopenharmony_ci	}
184062306a36Sopenharmony_ci	return ERR_PTR(rc);
184162306a36Sopenharmony_ci}
184262306a36Sopenharmony_ci
184362306a36Sopenharmony_ci/* this function must be called with ses_lock and chan_lock held */
184462306a36Sopenharmony_cistatic int match_session(struct cifs_ses *ses, struct smb3_fs_context *ctx)
184562306a36Sopenharmony_ci{
184662306a36Sopenharmony_ci	if (ctx->sectype != Unspecified &&
184762306a36Sopenharmony_ci	    ctx->sectype != ses->sectype)
184862306a36Sopenharmony_ci		return 0;
184962306a36Sopenharmony_ci
185062306a36Sopenharmony_ci	/*
185162306a36Sopenharmony_ci	 * If an existing session is limited to less channels than
185262306a36Sopenharmony_ci	 * requested, it should not be reused
185362306a36Sopenharmony_ci	 */
185462306a36Sopenharmony_ci	if (ses->chan_max < ctx->max_channels)
185562306a36Sopenharmony_ci		return 0;
185662306a36Sopenharmony_ci
185762306a36Sopenharmony_ci	switch (ses->sectype) {
185862306a36Sopenharmony_ci	case Kerberos:
185962306a36Sopenharmony_ci		if (!uid_eq(ctx->cred_uid, ses->cred_uid))
186062306a36Sopenharmony_ci			return 0;
186162306a36Sopenharmony_ci		break;
186262306a36Sopenharmony_ci	default:
186362306a36Sopenharmony_ci		/* NULL username means anonymous session */
186462306a36Sopenharmony_ci		if (ses->user_name == NULL) {
186562306a36Sopenharmony_ci			if (!ctx->nullauth)
186662306a36Sopenharmony_ci				return 0;
186762306a36Sopenharmony_ci			break;
186862306a36Sopenharmony_ci		}
186962306a36Sopenharmony_ci
187062306a36Sopenharmony_ci		/* anything else takes username/password */
187162306a36Sopenharmony_ci		if (strncmp(ses->user_name,
187262306a36Sopenharmony_ci			    ctx->username ? ctx->username : "",
187362306a36Sopenharmony_ci			    CIFS_MAX_USERNAME_LEN))
187462306a36Sopenharmony_ci			return 0;
187562306a36Sopenharmony_ci		if ((ctx->username && strlen(ctx->username) != 0) &&
187662306a36Sopenharmony_ci		    ses->password != NULL &&
187762306a36Sopenharmony_ci		    strncmp(ses->password,
187862306a36Sopenharmony_ci			    ctx->password ? ctx->password : "",
187962306a36Sopenharmony_ci			    CIFS_MAX_PASSWORD_LEN))
188062306a36Sopenharmony_ci			return 0;
188162306a36Sopenharmony_ci	}
188262306a36Sopenharmony_ci
188362306a36Sopenharmony_ci	if (strcmp(ctx->local_nls->charset, ses->local_nls->charset))
188462306a36Sopenharmony_ci		return 0;
188562306a36Sopenharmony_ci
188662306a36Sopenharmony_ci	return 1;
188762306a36Sopenharmony_ci}
188862306a36Sopenharmony_ci
188962306a36Sopenharmony_ci/**
189062306a36Sopenharmony_ci * cifs_setup_ipc - helper to setup the IPC tcon for the session
189162306a36Sopenharmony_ci * @ses: smb session to issue the request on
189262306a36Sopenharmony_ci * @ctx: the superblock configuration context to use for building the
189362306a36Sopenharmony_ci *       new tree connection for the IPC (interprocess communication RPC)
189462306a36Sopenharmony_ci *
189562306a36Sopenharmony_ci * A new IPC connection is made and stored in the session
189662306a36Sopenharmony_ci * tcon_ipc. The IPC tcon has the same lifetime as the session.
189762306a36Sopenharmony_ci */
189862306a36Sopenharmony_cistatic int
189962306a36Sopenharmony_cicifs_setup_ipc(struct cifs_ses *ses, struct smb3_fs_context *ctx)
190062306a36Sopenharmony_ci{
190162306a36Sopenharmony_ci	int rc = 0, xid;
190262306a36Sopenharmony_ci	struct cifs_tcon *tcon;
190362306a36Sopenharmony_ci	char unc[SERVER_NAME_LENGTH + sizeof("//x/IPC$")] = {0};
190462306a36Sopenharmony_ci	bool seal = false;
190562306a36Sopenharmony_ci	struct TCP_Server_Info *server = ses->server;
190662306a36Sopenharmony_ci
190762306a36Sopenharmony_ci	/*
190862306a36Sopenharmony_ci	 * If the mount request that resulted in the creation of the
190962306a36Sopenharmony_ci	 * session requires encryption, force IPC to be encrypted too.
191062306a36Sopenharmony_ci	 */
191162306a36Sopenharmony_ci	if (ctx->seal) {
191262306a36Sopenharmony_ci		if (server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)
191362306a36Sopenharmony_ci			seal = true;
191462306a36Sopenharmony_ci		else {
191562306a36Sopenharmony_ci			cifs_server_dbg(VFS,
191662306a36Sopenharmony_ci				 "IPC: server doesn't support encryption\n");
191762306a36Sopenharmony_ci			return -EOPNOTSUPP;
191862306a36Sopenharmony_ci		}
191962306a36Sopenharmony_ci	}
192062306a36Sopenharmony_ci
192162306a36Sopenharmony_ci	/* no need to setup directory caching on IPC share, so pass in false */
192262306a36Sopenharmony_ci	tcon = tcon_info_alloc(false);
192362306a36Sopenharmony_ci	if (tcon == NULL)
192462306a36Sopenharmony_ci		return -ENOMEM;
192562306a36Sopenharmony_ci
192662306a36Sopenharmony_ci	spin_lock(&server->srv_lock);
192762306a36Sopenharmony_ci	scnprintf(unc, sizeof(unc), "\\\\%s\\IPC$", server->hostname);
192862306a36Sopenharmony_ci	spin_unlock(&server->srv_lock);
192962306a36Sopenharmony_ci
193062306a36Sopenharmony_ci	xid = get_xid();
193162306a36Sopenharmony_ci	tcon->ses = ses;
193262306a36Sopenharmony_ci	tcon->ipc = true;
193362306a36Sopenharmony_ci	tcon->seal = seal;
193462306a36Sopenharmony_ci	rc = server->ops->tree_connect(xid, ses, unc, tcon, ctx->local_nls);
193562306a36Sopenharmony_ci	free_xid(xid);
193662306a36Sopenharmony_ci
193762306a36Sopenharmony_ci	if (rc) {
193862306a36Sopenharmony_ci		cifs_server_dbg(VFS, "failed to connect to IPC (rc=%d)\n", rc);
193962306a36Sopenharmony_ci		tconInfoFree(tcon);
194062306a36Sopenharmony_ci		goto out;
194162306a36Sopenharmony_ci	}
194262306a36Sopenharmony_ci
194362306a36Sopenharmony_ci	cifs_dbg(FYI, "IPC tcon rc=%d ipc tid=0x%x\n", rc, tcon->tid);
194462306a36Sopenharmony_ci
194562306a36Sopenharmony_ci	spin_lock(&tcon->tc_lock);
194662306a36Sopenharmony_ci	tcon->status = TID_GOOD;
194762306a36Sopenharmony_ci	spin_unlock(&tcon->tc_lock);
194862306a36Sopenharmony_ci	ses->tcon_ipc = tcon;
194962306a36Sopenharmony_ciout:
195062306a36Sopenharmony_ci	return rc;
195162306a36Sopenharmony_ci}
195262306a36Sopenharmony_ci
195362306a36Sopenharmony_ci/**
195462306a36Sopenharmony_ci * cifs_free_ipc - helper to release the session IPC tcon
195562306a36Sopenharmony_ci * @ses: smb session to unmount the IPC from
195662306a36Sopenharmony_ci *
195762306a36Sopenharmony_ci * Needs to be called everytime a session is destroyed.
195862306a36Sopenharmony_ci *
195962306a36Sopenharmony_ci * On session close, the IPC is closed and the server must release all tcons of the session.
196062306a36Sopenharmony_ci * No need to send a tree disconnect here.
196162306a36Sopenharmony_ci *
196262306a36Sopenharmony_ci * Besides, it will make the server to not close durable and resilient files on session close, as
196362306a36Sopenharmony_ci * specified in MS-SMB2 3.3.5.6 Receiving an SMB2 LOGOFF Request.
196462306a36Sopenharmony_ci */
196562306a36Sopenharmony_cistatic int
196662306a36Sopenharmony_cicifs_free_ipc(struct cifs_ses *ses)
196762306a36Sopenharmony_ci{
196862306a36Sopenharmony_ci	struct cifs_tcon *tcon = ses->tcon_ipc;
196962306a36Sopenharmony_ci
197062306a36Sopenharmony_ci	if (tcon == NULL)
197162306a36Sopenharmony_ci		return 0;
197262306a36Sopenharmony_ci
197362306a36Sopenharmony_ci	tconInfoFree(tcon);
197462306a36Sopenharmony_ci	ses->tcon_ipc = NULL;
197562306a36Sopenharmony_ci	return 0;
197662306a36Sopenharmony_ci}
197762306a36Sopenharmony_ci
197862306a36Sopenharmony_cistatic struct cifs_ses *
197962306a36Sopenharmony_cicifs_find_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
198062306a36Sopenharmony_ci{
198162306a36Sopenharmony_ci	struct cifs_ses *ses, *ret = NULL;
198262306a36Sopenharmony_ci
198362306a36Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
198462306a36Sopenharmony_ci	list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
198562306a36Sopenharmony_ci		spin_lock(&ses->ses_lock);
198662306a36Sopenharmony_ci		if (ses->ses_status == SES_EXITING) {
198762306a36Sopenharmony_ci			spin_unlock(&ses->ses_lock);
198862306a36Sopenharmony_ci			continue;
198962306a36Sopenharmony_ci		}
199062306a36Sopenharmony_ci		spin_lock(&ses->chan_lock);
199162306a36Sopenharmony_ci		if (match_session(ses, ctx)) {
199262306a36Sopenharmony_ci			spin_unlock(&ses->chan_lock);
199362306a36Sopenharmony_ci			spin_unlock(&ses->ses_lock);
199462306a36Sopenharmony_ci			ret = ses;
199562306a36Sopenharmony_ci			break;
199662306a36Sopenharmony_ci		}
199762306a36Sopenharmony_ci		spin_unlock(&ses->chan_lock);
199862306a36Sopenharmony_ci		spin_unlock(&ses->ses_lock);
199962306a36Sopenharmony_ci	}
200062306a36Sopenharmony_ci	if (ret)
200162306a36Sopenharmony_ci		cifs_smb_ses_inc_refcount(ret);
200262306a36Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
200362306a36Sopenharmony_ci	return ret;
200462306a36Sopenharmony_ci}
200562306a36Sopenharmony_ci
200662306a36Sopenharmony_civoid __cifs_put_smb_ses(struct cifs_ses *ses)
200762306a36Sopenharmony_ci{
200862306a36Sopenharmony_ci	unsigned int rc, xid;
200962306a36Sopenharmony_ci	unsigned int chan_count;
201062306a36Sopenharmony_ci	struct TCP_Server_Info *server = ses->server;
201162306a36Sopenharmony_ci
201262306a36Sopenharmony_ci	spin_lock(&ses->ses_lock);
201362306a36Sopenharmony_ci	if (ses->ses_status == SES_EXITING) {
201462306a36Sopenharmony_ci		spin_unlock(&ses->ses_lock);
201562306a36Sopenharmony_ci		return;
201662306a36Sopenharmony_ci	}
201762306a36Sopenharmony_ci	spin_unlock(&ses->ses_lock);
201862306a36Sopenharmony_ci
201962306a36Sopenharmony_ci	cifs_dbg(FYI, "%s: ses_count=%d\n", __func__, ses->ses_count);
202062306a36Sopenharmony_ci	cifs_dbg(FYI,
202162306a36Sopenharmony_ci		 "%s: ses ipc: %s\n", __func__, ses->tcon_ipc ? ses->tcon_ipc->tree_name : "NONE");
202262306a36Sopenharmony_ci
202362306a36Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
202462306a36Sopenharmony_ci	if (--ses->ses_count > 0) {
202562306a36Sopenharmony_ci		spin_unlock(&cifs_tcp_ses_lock);
202662306a36Sopenharmony_ci		return;
202762306a36Sopenharmony_ci	}
202862306a36Sopenharmony_ci	spin_lock(&ses->ses_lock);
202962306a36Sopenharmony_ci	if (ses->ses_status == SES_GOOD)
203062306a36Sopenharmony_ci		ses->ses_status = SES_EXITING;
203162306a36Sopenharmony_ci	spin_unlock(&ses->ses_lock);
203262306a36Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
203362306a36Sopenharmony_ci
203462306a36Sopenharmony_ci	/* ses_count can never go negative */
203562306a36Sopenharmony_ci	WARN_ON(ses->ses_count < 0);
203662306a36Sopenharmony_ci
203762306a36Sopenharmony_ci	spin_lock(&ses->ses_lock);
203862306a36Sopenharmony_ci	if (ses->ses_status == SES_EXITING && server->ops->logoff) {
203962306a36Sopenharmony_ci		spin_unlock(&ses->ses_lock);
204062306a36Sopenharmony_ci		cifs_free_ipc(ses);
204162306a36Sopenharmony_ci		xid = get_xid();
204262306a36Sopenharmony_ci		rc = server->ops->logoff(xid, ses);
204362306a36Sopenharmony_ci		if (rc)
204462306a36Sopenharmony_ci			cifs_server_dbg(VFS, "%s: Session Logoff failure rc=%d\n",
204562306a36Sopenharmony_ci				__func__, rc);
204662306a36Sopenharmony_ci		_free_xid(xid);
204762306a36Sopenharmony_ci	} else {
204862306a36Sopenharmony_ci		spin_unlock(&ses->ses_lock);
204962306a36Sopenharmony_ci		cifs_free_ipc(ses);
205062306a36Sopenharmony_ci	}
205162306a36Sopenharmony_ci
205262306a36Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
205362306a36Sopenharmony_ci	list_del_init(&ses->smb_ses_list);
205462306a36Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
205562306a36Sopenharmony_ci
205662306a36Sopenharmony_ci	chan_count = ses->chan_count;
205762306a36Sopenharmony_ci
205862306a36Sopenharmony_ci	/* close any extra channels */
205962306a36Sopenharmony_ci	if (chan_count > 1) {
206062306a36Sopenharmony_ci		int i;
206162306a36Sopenharmony_ci
206262306a36Sopenharmony_ci		for (i = 1; i < chan_count; i++) {
206362306a36Sopenharmony_ci			if (ses->chans[i].iface) {
206462306a36Sopenharmony_ci				kref_put(&ses->chans[i].iface->refcount, release_iface);
206562306a36Sopenharmony_ci				ses->chans[i].iface = NULL;
206662306a36Sopenharmony_ci			}
206762306a36Sopenharmony_ci			cifs_put_tcp_session(ses->chans[i].server, 0);
206862306a36Sopenharmony_ci			ses->chans[i].server = NULL;
206962306a36Sopenharmony_ci		}
207062306a36Sopenharmony_ci	}
207162306a36Sopenharmony_ci
207262306a36Sopenharmony_ci	/* we now account for primary channel in iface->refcount */
207362306a36Sopenharmony_ci	if (ses->chans[0].iface) {
207462306a36Sopenharmony_ci		kref_put(&ses->chans[0].iface->refcount, release_iface);
207562306a36Sopenharmony_ci		ses->chans[0].server = NULL;
207662306a36Sopenharmony_ci	}
207762306a36Sopenharmony_ci
207862306a36Sopenharmony_ci	sesInfoFree(ses);
207962306a36Sopenharmony_ci	cifs_put_tcp_session(server, 0);
208062306a36Sopenharmony_ci}
208162306a36Sopenharmony_ci
208262306a36Sopenharmony_ci#ifdef CONFIG_KEYS
208362306a36Sopenharmony_ci
208462306a36Sopenharmony_ci/* strlen("cifs:a:") + CIFS_MAX_DOMAINNAME_LEN + 1 */
208562306a36Sopenharmony_ci#define CIFSCREDS_DESC_SIZE (7 + CIFS_MAX_DOMAINNAME_LEN + 1)
208662306a36Sopenharmony_ci
208762306a36Sopenharmony_ci/* Populate username and pw fields from keyring if possible */
208862306a36Sopenharmony_cistatic int
208962306a36Sopenharmony_cicifs_set_cifscreds(struct smb3_fs_context *ctx, struct cifs_ses *ses)
209062306a36Sopenharmony_ci{
209162306a36Sopenharmony_ci	int rc = 0;
209262306a36Sopenharmony_ci	int is_domain = 0;
209362306a36Sopenharmony_ci	const char *delim, *payload;
209462306a36Sopenharmony_ci	char *desc;
209562306a36Sopenharmony_ci	ssize_t len;
209662306a36Sopenharmony_ci	struct key *key;
209762306a36Sopenharmony_ci	struct TCP_Server_Info *server = ses->server;
209862306a36Sopenharmony_ci	struct sockaddr_in *sa;
209962306a36Sopenharmony_ci	struct sockaddr_in6 *sa6;
210062306a36Sopenharmony_ci	const struct user_key_payload *upayload;
210162306a36Sopenharmony_ci
210262306a36Sopenharmony_ci	desc = kmalloc(CIFSCREDS_DESC_SIZE, GFP_KERNEL);
210362306a36Sopenharmony_ci	if (!desc)
210462306a36Sopenharmony_ci		return -ENOMEM;
210562306a36Sopenharmony_ci
210662306a36Sopenharmony_ci	/* try to find an address key first */
210762306a36Sopenharmony_ci	switch (server->dstaddr.ss_family) {
210862306a36Sopenharmony_ci	case AF_INET:
210962306a36Sopenharmony_ci		sa = (struct sockaddr_in *)&server->dstaddr;
211062306a36Sopenharmony_ci		sprintf(desc, "cifs:a:%pI4", &sa->sin_addr.s_addr);
211162306a36Sopenharmony_ci		break;
211262306a36Sopenharmony_ci	case AF_INET6:
211362306a36Sopenharmony_ci		sa6 = (struct sockaddr_in6 *)&server->dstaddr;
211462306a36Sopenharmony_ci		sprintf(desc, "cifs:a:%pI6c", &sa6->sin6_addr.s6_addr);
211562306a36Sopenharmony_ci		break;
211662306a36Sopenharmony_ci	default:
211762306a36Sopenharmony_ci		cifs_dbg(FYI, "Bad ss_family (%hu)\n",
211862306a36Sopenharmony_ci			 server->dstaddr.ss_family);
211962306a36Sopenharmony_ci		rc = -EINVAL;
212062306a36Sopenharmony_ci		goto out_err;
212162306a36Sopenharmony_ci	}
212262306a36Sopenharmony_ci
212362306a36Sopenharmony_ci	cifs_dbg(FYI, "%s: desc=%s\n", __func__, desc);
212462306a36Sopenharmony_ci	key = request_key(&key_type_logon, desc, "");
212562306a36Sopenharmony_ci	if (IS_ERR(key)) {
212662306a36Sopenharmony_ci		if (!ses->domainName) {
212762306a36Sopenharmony_ci			cifs_dbg(FYI, "domainName is NULL\n");
212862306a36Sopenharmony_ci			rc = PTR_ERR(key);
212962306a36Sopenharmony_ci			goto out_err;
213062306a36Sopenharmony_ci		}
213162306a36Sopenharmony_ci
213262306a36Sopenharmony_ci		/* didn't work, try to find a domain key */
213362306a36Sopenharmony_ci		sprintf(desc, "cifs:d:%s", ses->domainName);
213462306a36Sopenharmony_ci		cifs_dbg(FYI, "%s: desc=%s\n", __func__, desc);
213562306a36Sopenharmony_ci		key = request_key(&key_type_logon, desc, "");
213662306a36Sopenharmony_ci		if (IS_ERR(key)) {
213762306a36Sopenharmony_ci			rc = PTR_ERR(key);
213862306a36Sopenharmony_ci			goto out_err;
213962306a36Sopenharmony_ci		}
214062306a36Sopenharmony_ci		is_domain = 1;
214162306a36Sopenharmony_ci	}
214262306a36Sopenharmony_ci
214362306a36Sopenharmony_ci	down_read(&key->sem);
214462306a36Sopenharmony_ci	upayload = user_key_payload_locked(key);
214562306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(upayload)) {
214662306a36Sopenharmony_ci		rc = upayload ? PTR_ERR(upayload) : -EINVAL;
214762306a36Sopenharmony_ci		goto out_key_put;
214862306a36Sopenharmony_ci	}
214962306a36Sopenharmony_ci
215062306a36Sopenharmony_ci	/* find first : in payload */
215162306a36Sopenharmony_ci	payload = upayload->data;
215262306a36Sopenharmony_ci	delim = strnchr(payload, upayload->datalen, ':');
215362306a36Sopenharmony_ci	cifs_dbg(FYI, "payload=%s\n", payload);
215462306a36Sopenharmony_ci	if (!delim) {
215562306a36Sopenharmony_ci		cifs_dbg(FYI, "Unable to find ':' in payload (datalen=%d)\n",
215662306a36Sopenharmony_ci			 upayload->datalen);
215762306a36Sopenharmony_ci		rc = -EINVAL;
215862306a36Sopenharmony_ci		goto out_key_put;
215962306a36Sopenharmony_ci	}
216062306a36Sopenharmony_ci
216162306a36Sopenharmony_ci	len = delim - payload;
216262306a36Sopenharmony_ci	if (len > CIFS_MAX_USERNAME_LEN || len <= 0) {
216362306a36Sopenharmony_ci		cifs_dbg(FYI, "Bad value from username search (len=%zd)\n",
216462306a36Sopenharmony_ci			 len);
216562306a36Sopenharmony_ci		rc = -EINVAL;
216662306a36Sopenharmony_ci		goto out_key_put;
216762306a36Sopenharmony_ci	}
216862306a36Sopenharmony_ci
216962306a36Sopenharmony_ci	ctx->username = kstrndup(payload, len, GFP_KERNEL);
217062306a36Sopenharmony_ci	if (!ctx->username) {
217162306a36Sopenharmony_ci		cifs_dbg(FYI, "Unable to allocate %zd bytes for username\n",
217262306a36Sopenharmony_ci			 len);
217362306a36Sopenharmony_ci		rc = -ENOMEM;
217462306a36Sopenharmony_ci		goto out_key_put;
217562306a36Sopenharmony_ci	}
217662306a36Sopenharmony_ci	cifs_dbg(FYI, "%s: username=%s\n", __func__, ctx->username);
217762306a36Sopenharmony_ci
217862306a36Sopenharmony_ci	len = key->datalen - (len + 1);
217962306a36Sopenharmony_ci	if (len > CIFS_MAX_PASSWORD_LEN || len <= 0) {
218062306a36Sopenharmony_ci		cifs_dbg(FYI, "Bad len for password search (len=%zd)\n", len);
218162306a36Sopenharmony_ci		rc = -EINVAL;
218262306a36Sopenharmony_ci		kfree(ctx->username);
218362306a36Sopenharmony_ci		ctx->username = NULL;
218462306a36Sopenharmony_ci		goto out_key_put;
218562306a36Sopenharmony_ci	}
218662306a36Sopenharmony_ci
218762306a36Sopenharmony_ci	++delim;
218862306a36Sopenharmony_ci	ctx->password = kstrndup(delim, len, GFP_KERNEL);
218962306a36Sopenharmony_ci	if (!ctx->password) {
219062306a36Sopenharmony_ci		cifs_dbg(FYI, "Unable to allocate %zd bytes for password\n",
219162306a36Sopenharmony_ci			 len);
219262306a36Sopenharmony_ci		rc = -ENOMEM;
219362306a36Sopenharmony_ci		kfree(ctx->username);
219462306a36Sopenharmony_ci		ctx->username = NULL;
219562306a36Sopenharmony_ci		goto out_key_put;
219662306a36Sopenharmony_ci	}
219762306a36Sopenharmony_ci
219862306a36Sopenharmony_ci	/*
219962306a36Sopenharmony_ci	 * If we have a domain key then we must set the domainName in the
220062306a36Sopenharmony_ci	 * for the request.
220162306a36Sopenharmony_ci	 */
220262306a36Sopenharmony_ci	if (is_domain && ses->domainName) {
220362306a36Sopenharmony_ci		ctx->domainname = kstrdup(ses->domainName, GFP_KERNEL);
220462306a36Sopenharmony_ci		if (!ctx->domainname) {
220562306a36Sopenharmony_ci			cifs_dbg(FYI, "Unable to allocate %zd bytes for domain\n",
220662306a36Sopenharmony_ci				 len);
220762306a36Sopenharmony_ci			rc = -ENOMEM;
220862306a36Sopenharmony_ci			kfree(ctx->username);
220962306a36Sopenharmony_ci			ctx->username = NULL;
221062306a36Sopenharmony_ci			kfree_sensitive(ctx->password);
221162306a36Sopenharmony_ci			ctx->password = NULL;
221262306a36Sopenharmony_ci			goto out_key_put;
221362306a36Sopenharmony_ci		}
221462306a36Sopenharmony_ci	}
221562306a36Sopenharmony_ci
221662306a36Sopenharmony_ci	strscpy(ctx->workstation_name, ses->workstation_name, sizeof(ctx->workstation_name));
221762306a36Sopenharmony_ci
221862306a36Sopenharmony_ciout_key_put:
221962306a36Sopenharmony_ci	up_read(&key->sem);
222062306a36Sopenharmony_ci	key_put(key);
222162306a36Sopenharmony_ciout_err:
222262306a36Sopenharmony_ci	kfree(desc);
222362306a36Sopenharmony_ci	cifs_dbg(FYI, "%s: returning %d\n", __func__, rc);
222462306a36Sopenharmony_ci	return rc;
222562306a36Sopenharmony_ci}
222662306a36Sopenharmony_ci#else /* ! CONFIG_KEYS */
222762306a36Sopenharmony_cistatic inline int
222862306a36Sopenharmony_cicifs_set_cifscreds(struct smb3_fs_context *ctx __attribute__((unused)),
222962306a36Sopenharmony_ci		   struct cifs_ses *ses __attribute__((unused)))
223062306a36Sopenharmony_ci{
223162306a36Sopenharmony_ci	return -ENOSYS;
223262306a36Sopenharmony_ci}
223362306a36Sopenharmony_ci#endif /* CONFIG_KEYS */
223462306a36Sopenharmony_ci
223562306a36Sopenharmony_ci/**
223662306a36Sopenharmony_ci * cifs_get_smb_ses - get a session matching @ctx data from @server
223762306a36Sopenharmony_ci * @server: server to setup the session to
223862306a36Sopenharmony_ci * @ctx: superblock configuration context to use to setup the session
223962306a36Sopenharmony_ci *
224062306a36Sopenharmony_ci * This function assumes it is being called from cifs_mount() where we
224162306a36Sopenharmony_ci * already got a server reference (server refcount +1). See
224262306a36Sopenharmony_ci * cifs_get_tcon() for refcount explanations.
224362306a36Sopenharmony_ci */
224462306a36Sopenharmony_cistruct cifs_ses *
224562306a36Sopenharmony_cicifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
224662306a36Sopenharmony_ci{
224762306a36Sopenharmony_ci	int rc = 0;
224862306a36Sopenharmony_ci	unsigned int xid;
224962306a36Sopenharmony_ci	struct cifs_ses *ses;
225062306a36Sopenharmony_ci	struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr;
225162306a36Sopenharmony_ci	struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&server->dstaddr;
225262306a36Sopenharmony_ci
225362306a36Sopenharmony_ci	xid = get_xid();
225462306a36Sopenharmony_ci
225562306a36Sopenharmony_ci	ses = cifs_find_smb_ses(server, ctx);
225662306a36Sopenharmony_ci	if (ses) {
225762306a36Sopenharmony_ci		cifs_dbg(FYI, "Existing smb sess found (status=%d)\n",
225862306a36Sopenharmony_ci			 ses->ses_status);
225962306a36Sopenharmony_ci
226062306a36Sopenharmony_ci		spin_lock(&ses->chan_lock);
226162306a36Sopenharmony_ci		if (cifs_chan_needs_reconnect(ses, server)) {
226262306a36Sopenharmony_ci			spin_unlock(&ses->chan_lock);
226362306a36Sopenharmony_ci			cifs_dbg(FYI, "Session needs reconnect\n");
226462306a36Sopenharmony_ci
226562306a36Sopenharmony_ci			mutex_lock(&ses->session_mutex);
226662306a36Sopenharmony_ci			rc = cifs_negotiate_protocol(xid, ses, server);
226762306a36Sopenharmony_ci			if (rc) {
226862306a36Sopenharmony_ci				mutex_unlock(&ses->session_mutex);
226962306a36Sopenharmony_ci				/* problem -- put our ses reference */
227062306a36Sopenharmony_ci				cifs_put_smb_ses(ses);
227162306a36Sopenharmony_ci				free_xid(xid);
227262306a36Sopenharmony_ci				return ERR_PTR(rc);
227362306a36Sopenharmony_ci			}
227462306a36Sopenharmony_ci
227562306a36Sopenharmony_ci			rc = cifs_setup_session(xid, ses, server,
227662306a36Sopenharmony_ci						ctx->local_nls);
227762306a36Sopenharmony_ci			if (rc) {
227862306a36Sopenharmony_ci				mutex_unlock(&ses->session_mutex);
227962306a36Sopenharmony_ci				/* problem -- put our reference */
228062306a36Sopenharmony_ci				cifs_put_smb_ses(ses);
228162306a36Sopenharmony_ci				free_xid(xid);
228262306a36Sopenharmony_ci				return ERR_PTR(rc);
228362306a36Sopenharmony_ci			}
228462306a36Sopenharmony_ci			mutex_unlock(&ses->session_mutex);
228562306a36Sopenharmony_ci
228662306a36Sopenharmony_ci			spin_lock(&ses->chan_lock);
228762306a36Sopenharmony_ci		}
228862306a36Sopenharmony_ci		spin_unlock(&ses->chan_lock);
228962306a36Sopenharmony_ci
229062306a36Sopenharmony_ci		/* existing SMB ses has a server reference already */
229162306a36Sopenharmony_ci		cifs_put_tcp_session(server, 0);
229262306a36Sopenharmony_ci		free_xid(xid);
229362306a36Sopenharmony_ci		return ses;
229462306a36Sopenharmony_ci	}
229562306a36Sopenharmony_ci
229662306a36Sopenharmony_ci	rc = -ENOMEM;
229762306a36Sopenharmony_ci
229862306a36Sopenharmony_ci	cifs_dbg(FYI, "Existing smb sess not found\n");
229962306a36Sopenharmony_ci	ses = sesInfoAlloc();
230062306a36Sopenharmony_ci	if (ses == NULL)
230162306a36Sopenharmony_ci		goto get_ses_fail;
230262306a36Sopenharmony_ci
230362306a36Sopenharmony_ci	/* new SMB session uses our server ref */
230462306a36Sopenharmony_ci	ses->server = server;
230562306a36Sopenharmony_ci	if (server->dstaddr.ss_family == AF_INET6)
230662306a36Sopenharmony_ci		sprintf(ses->ip_addr, "%pI6", &addr6->sin6_addr);
230762306a36Sopenharmony_ci	else
230862306a36Sopenharmony_ci		sprintf(ses->ip_addr, "%pI4", &addr->sin_addr);
230962306a36Sopenharmony_ci
231062306a36Sopenharmony_ci	if (ctx->username) {
231162306a36Sopenharmony_ci		ses->user_name = kstrdup(ctx->username, GFP_KERNEL);
231262306a36Sopenharmony_ci		if (!ses->user_name)
231362306a36Sopenharmony_ci			goto get_ses_fail;
231462306a36Sopenharmony_ci	}
231562306a36Sopenharmony_ci
231662306a36Sopenharmony_ci	/* ctx->password freed at unmount */
231762306a36Sopenharmony_ci	if (ctx->password) {
231862306a36Sopenharmony_ci		ses->password = kstrdup(ctx->password, GFP_KERNEL);
231962306a36Sopenharmony_ci		if (!ses->password)
232062306a36Sopenharmony_ci			goto get_ses_fail;
232162306a36Sopenharmony_ci	}
232262306a36Sopenharmony_ci	if (ctx->domainname) {
232362306a36Sopenharmony_ci		ses->domainName = kstrdup(ctx->domainname, GFP_KERNEL);
232462306a36Sopenharmony_ci		if (!ses->domainName)
232562306a36Sopenharmony_ci			goto get_ses_fail;
232662306a36Sopenharmony_ci	}
232762306a36Sopenharmony_ci
232862306a36Sopenharmony_ci	strscpy(ses->workstation_name, ctx->workstation_name, sizeof(ses->workstation_name));
232962306a36Sopenharmony_ci
233062306a36Sopenharmony_ci	if (ctx->domainauto)
233162306a36Sopenharmony_ci		ses->domainAuto = ctx->domainauto;
233262306a36Sopenharmony_ci	ses->cred_uid = ctx->cred_uid;
233362306a36Sopenharmony_ci	ses->linux_uid = ctx->linux_uid;
233462306a36Sopenharmony_ci
233562306a36Sopenharmony_ci	ses->sectype = ctx->sectype;
233662306a36Sopenharmony_ci	ses->sign = ctx->sign;
233762306a36Sopenharmony_ci	ses->local_nls = load_nls(ctx->local_nls->charset);
233862306a36Sopenharmony_ci
233962306a36Sopenharmony_ci	/* add server as first channel */
234062306a36Sopenharmony_ci	spin_lock(&ses->chan_lock);
234162306a36Sopenharmony_ci	ses->chans[0].server = server;
234262306a36Sopenharmony_ci	ses->chan_count = 1;
234362306a36Sopenharmony_ci	ses->chan_max = ctx->multichannel ? ctx->max_channels:1;
234462306a36Sopenharmony_ci	ses->chans_need_reconnect = 1;
234562306a36Sopenharmony_ci	spin_unlock(&ses->chan_lock);
234662306a36Sopenharmony_ci
234762306a36Sopenharmony_ci	mutex_lock(&ses->session_mutex);
234862306a36Sopenharmony_ci	rc = cifs_negotiate_protocol(xid, ses, server);
234962306a36Sopenharmony_ci	if (!rc)
235062306a36Sopenharmony_ci		rc = cifs_setup_session(xid, ses, server, ctx->local_nls);
235162306a36Sopenharmony_ci	mutex_unlock(&ses->session_mutex);
235262306a36Sopenharmony_ci
235362306a36Sopenharmony_ci	/* each channel uses a different signing key */
235462306a36Sopenharmony_ci	spin_lock(&ses->chan_lock);
235562306a36Sopenharmony_ci	memcpy(ses->chans[0].signkey, ses->smb3signingkey,
235662306a36Sopenharmony_ci	       sizeof(ses->smb3signingkey));
235762306a36Sopenharmony_ci	spin_unlock(&ses->chan_lock);
235862306a36Sopenharmony_ci
235962306a36Sopenharmony_ci	if (rc)
236062306a36Sopenharmony_ci		goto get_ses_fail;
236162306a36Sopenharmony_ci
236262306a36Sopenharmony_ci	/*
236362306a36Sopenharmony_ci	 * success, put it on the list and add it as first channel
236462306a36Sopenharmony_ci	 * note: the session becomes active soon after this. So you'll
236562306a36Sopenharmony_ci	 * need to lock before changing something in the session.
236662306a36Sopenharmony_ci	 */
236762306a36Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
236862306a36Sopenharmony_ci	ses->dfs_root_ses = ctx->dfs_root_ses;
236962306a36Sopenharmony_ci	if (ses->dfs_root_ses)
237062306a36Sopenharmony_ci		ses->dfs_root_ses->ses_count++;
237162306a36Sopenharmony_ci	list_add(&ses->smb_ses_list, &server->smb_ses_list);
237262306a36Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
237362306a36Sopenharmony_ci
237462306a36Sopenharmony_ci	cifs_setup_ipc(ses, ctx);
237562306a36Sopenharmony_ci
237662306a36Sopenharmony_ci	free_xid(xid);
237762306a36Sopenharmony_ci
237862306a36Sopenharmony_ci	return ses;
237962306a36Sopenharmony_ci
238062306a36Sopenharmony_ciget_ses_fail:
238162306a36Sopenharmony_ci	sesInfoFree(ses);
238262306a36Sopenharmony_ci	free_xid(xid);
238362306a36Sopenharmony_ci	return ERR_PTR(rc);
238462306a36Sopenharmony_ci}
238562306a36Sopenharmony_ci
238662306a36Sopenharmony_ci/* this function must be called with tc_lock held */
238762306a36Sopenharmony_cistatic int match_tcon(struct cifs_tcon *tcon, struct smb3_fs_context *ctx)
238862306a36Sopenharmony_ci{
238962306a36Sopenharmony_ci	struct TCP_Server_Info *server = tcon->ses->server;
239062306a36Sopenharmony_ci
239162306a36Sopenharmony_ci	if (tcon->status == TID_EXITING)
239262306a36Sopenharmony_ci		return 0;
239362306a36Sopenharmony_ci
239462306a36Sopenharmony_ci	if (tcon->origin_fullpath) {
239562306a36Sopenharmony_ci		if (!ctx->source ||
239662306a36Sopenharmony_ci		    !dfs_src_pathname_equal(ctx->source,
239762306a36Sopenharmony_ci					    tcon->origin_fullpath))
239862306a36Sopenharmony_ci			return 0;
239962306a36Sopenharmony_ci	} else if (!server->leaf_fullpath &&
240062306a36Sopenharmony_ci		   strncmp(tcon->tree_name, ctx->UNC, MAX_TREE_SIZE)) {
240162306a36Sopenharmony_ci		return 0;
240262306a36Sopenharmony_ci	}
240362306a36Sopenharmony_ci	if (tcon->seal != ctx->seal)
240462306a36Sopenharmony_ci		return 0;
240562306a36Sopenharmony_ci	if (tcon->snapshot_time != ctx->snapshot_time)
240662306a36Sopenharmony_ci		return 0;
240762306a36Sopenharmony_ci	if (tcon->handle_timeout != ctx->handle_timeout)
240862306a36Sopenharmony_ci		return 0;
240962306a36Sopenharmony_ci	if (tcon->no_lease != ctx->no_lease)
241062306a36Sopenharmony_ci		return 0;
241162306a36Sopenharmony_ci	if (tcon->nodelete != ctx->nodelete)
241262306a36Sopenharmony_ci		return 0;
241362306a36Sopenharmony_ci	return 1;
241462306a36Sopenharmony_ci}
241562306a36Sopenharmony_ci
241662306a36Sopenharmony_cistatic struct cifs_tcon *
241762306a36Sopenharmony_cicifs_find_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx)
241862306a36Sopenharmony_ci{
241962306a36Sopenharmony_ci	struct cifs_tcon *tcon;
242062306a36Sopenharmony_ci
242162306a36Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
242262306a36Sopenharmony_ci	list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
242362306a36Sopenharmony_ci		spin_lock(&tcon->tc_lock);
242462306a36Sopenharmony_ci		if (!match_tcon(tcon, ctx)) {
242562306a36Sopenharmony_ci			spin_unlock(&tcon->tc_lock);
242662306a36Sopenharmony_ci			continue;
242762306a36Sopenharmony_ci		}
242862306a36Sopenharmony_ci		++tcon->tc_count;
242962306a36Sopenharmony_ci		spin_unlock(&tcon->tc_lock);
243062306a36Sopenharmony_ci		spin_unlock(&cifs_tcp_ses_lock);
243162306a36Sopenharmony_ci		return tcon;
243262306a36Sopenharmony_ci	}
243362306a36Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
243462306a36Sopenharmony_ci	return NULL;
243562306a36Sopenharmony_ci}
243662306a36Sopenharmony_ci
243762306a36Sopenharmony_civoid
243862306a36Sopenharmony_cicifs_put_tcon(struct cifs_tcon *tcon)
243962306a36Sopenharmony_ci{
244062306a36Sopenharmony_ci	unsigned int xid;
244162306a36Sopenharmony_ci	struct cifs_ses *ses;
244262306a36Sopenharmony_ci
244362306a36Sopenharmony_ci	/*
244462306a36Sopenharmony_ci	 * IPC tcon share the lifetime of their session and are
244562306a36Sopenharmony_ci	 * destroyed in the session put function
244662306a36Sopenharmony_ci	 */
244762306a36Sopenharmony_ci	if (tcon == NULL || tcon->ipc)
244862306a36Sopenharmony_ci		return;
244962306a36Sopenharmony_ci
245062306a36Sopenharmony_ci	ses = tcon->ses;
245162306a36Sopenharmony_ci	cifs_dbg(FYI, "%s: tc_count=%d\n", __func__, tcon->tc_count);
245262306a36Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
245362306a36Sopenharmony_ci	spin_lock(&tcon->tc_lock);
245462306a36Sopenharmony_ci	if (--tcon->tc_count > 0) {
245562306a36Sopenharmony_ci		spin_unlock(&tcon->tc_lock);
245662306a36Sopenharmony_ci		spin_unlock(&cifs_tcp_ses_lock);
245762306a36Sopenharmony_ci		return;
245862306a36Sopenharmony_ci	}
245962306a36Sopenharmony_ci
246062306a36Sopenharmony_ci	/* tc_count can never go negative */
246162306a36Sopenharmony_ci	WARN_ON(tcon->tc_count < 0);
246262306a36Sopenharmony_ci
246362306a36Sopenharmony_ci	list_del_init(&tcon->tcon_list);
246462306a36Sopenharmony_ci	tcon->status = TID_EXITING;
246562306a36Sopenharmony_ci	spin_unlock(&tcon->tc_lock);
246662306a36Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
246762306a36Sopenharmony_ci
246862306a36Sopenharmony_ci	/* cancel polling of interfaces */
246962306a36Sopenharmony_ci	cancel_delayed_work_sync(&tcon->query_interfaces);
247062306a36Sopenharmony_ci#ifdef CONFIG_CIFS_DFS_UPCALL
247162306a36Sopenharmony_ci	cancel_delayed_work_sync(&tcon->dfs_cache_work);
247262306a36Sopenharmony_ci#endif
247362306a36Sopenharmony_ci
247462306a36Sopenharmony_ci	if (tcon->use_witness) {
247562306a36Sopenharmony_ci		int rc;
247662306a36Sopenharmony_ci
247762306a36Sopenharmony_ci		rc = cifs_swn_unregister(tcon);
247862306a36Sopenharmony_ci		if (rc < 0) {
247962306a36Sopenharmony_ci			cifs_dbg(VFS, "%s: Failed to unregister for witness notifications: %d\n",
248062306a36Sopenharmony_ci					__func__, rc);
248162306a36Sopenharmony_ci		}
248262306a36Sopenharmony_ci	}
248362306a36Sopenharmony_ci
248462306a36Sopenharmony_ci	xid = get_xid();
248562306a36Sopenharmony_ci	if (ses->server->ops->tree_disconnect)
248662306a36Sopenharmony_ci		ses->server->ops->tree_disconnect(xid, tcon);
248762306a36Sopenharmony_ci	_free_xid(xid);
248862306a36Sopenharmony_ci
248962306a36Sopenharmony_ci	cifs_fscache_release_super_cookie(tcon);
249062306a36Sopenharmony_ci	tconInfoFree(tcon);
249162306a36Sopenharmony_ci	cifs_put_smb_ses(ses);
249262306a36Sopenharmony_ci}
249362306a36Sopenharmony_ci
249462306a36Sopenharmony_ci/**
249562306a36Sopenharmony_ci * cifs_get_tcon - get a tcon matching @ctx data from @ses
249662306a36Sopenharmony_ci * @ses: smb session to issue the request on
249762306a36Sopenharmony_ci * @ctx: the superblock configuration context to use for building the
249862306a36Sopenharmony_ci *
249962306a36Sopenharmony_ci * - tcon refcount is the number of mount points using the tcon.
250062306a36Sopenharmony_ci * - ses refcount is the number of tcon using the session.
250162306a36Sopenharmony_ci *
250262306a36Sopenharmony_ci * 1. This function assumes it is being called from cifs_mount() where
250362306a36Sopenharmony_ci *    we already got a session reference (ses refcount +1).
250462306a36Sopenharmony_ci *
250562306a36Sopenharmony_ci * 2. Since we're in the context of adding a mount point, the end
250662306a36Sopenharmony_ci *    result should be either:
250762306a36Sopenharmony_ci *
250862306a36Sopenharmony_ci * a) a new tcon already allocated with refcount=1 (1 mount point) and
250962306a36Sopenharmony_ci *    its session refcount incremented (1 new tcon). This +1 was
251062306a36Sopenharmony_ci *    already done in (1).
251162306a36Sopenharmony_ci *
251262306a36Sopenharmony_ci * b) an existing tcon with refcount+1 (add a mount point to it) and
251362306a36Sopenharmony_ci *    identical ses refcount (no new tcon). Because of (1) we need to
251462306a36Sopenharmony_ci *    decrement the ses refcount.
251562306a36Sopenharmony_ci */
251662306a36Sopenharmony_cistatic struct cifs_tcon *
251762306a36Sopenharmony_cicifs_get_tcon(struct cifs_ses *ses, struct smb3_fs_context *ctx)
251862306a36Sopenharmony_ci{
251962306a36Sopenharmony_ci	struct cifs_tcon *tcon;
252062306a36Sopenharmony_ci	bool nohandlecache;
252162306a36Sopenharmony_ci	int rc, xid;
252262306a36Sopenharmony_ci
252362306a36Sopenharmony_ci	tcon = cifs_find_tcon(ses, ctx);
252462306a36Sopenharmony_ci	if (tcon) {
252562306a36Sopenharmony_ci		/*
252662306a36Sopenharmony_ci		 * tcon has refcount already incremented but we need to
252762306a36Sopenharmony_ci		 * decrement extra ses reference gotten by caller (case b)
252862306a36Sopenharmony_ci		 */
252962306a36Sopenharmony_ci		cifs_dbg(FYI, "Found match on UNC path\n");
253062306a36Sopenharmony_ci		cifs_put_smb_ses(ses);
253162306a36Sopenharmony_ci		return tcon;
253262306a36Sopenharmony_ci	}
253362306a36Sopenharmony_ci
253462306a36Sopenharmony_ci	if (!ses->server->ops->tree_connect) {
253562306a36Sopenharmony_ci		rc = -ENOSYS;
253662306a36Sopenharmony_ci		goto out_fail;
253762306a36Sopenharmony_ci	}
253862306a36Sopenharmony_ci
253962306a36Sopenharmony_ci	if (ses->server->dialect >= SMB20_PROT_ID &&
254062306a36Sopenharmony_ci	    (ses->server->capabilities & SMB2_GLOBAL_CAP_DIRECTORY_LEASING))
254162306a36Sopenharmony_ci		nohandlecache = ctx->nohandlecache;
254262306a36Sopenharmony_ci	else
254362306a36Sopenharmony_ci		nohandlecache = true;
254462306a36Sopenharmony_ci	tcon = tcon_info_alloc(!nohandlecache);
254562306a36Sopenharmony_ci	if (tcon == NULL) {
254662306a36Sopenharmony_ci		rc = -ENOMEM;
254762306a36Sopenharmony_ci		goto out_fail;
254862306a36Sopenharmony_ci	}
254962306a36Sopenharmony_ci	tcon->nohandlecache = nohandlecache;
255062306a36Sopenharmony_ci
255162306a36Sopenharmony_ci	if (ctx->snapshot_time) {
255262306a36Sopenharmony_ci		if (ses->server->vals->protocol_id == 0) {
255362306a36Sopenharmony_ci			cifs_dbg(VFS,
255462306a36Sopenharmony_ci			     "Use SMB2 or later for snapshot mount option\n");
255562306a36Sopenharmony_ci			rc = -EOPNOTSUPP;
255662306a36Sopenharmony_ci			goto out_fail;
255762306a36Sopenharmony_ci		} else
255862306a36Sopenharmony_ci			tcon->snapshot_time = ctx->snapshot_time;
255962306a36Sopenharmony_ci	}
256062306a36Sopenharmony_ci
256162306a36Sopenharmony_ci	if (ctx->handle_timeout) {
256262306a36Sopenharmony_ci		if (ses->server->vals->protocol_id == 0) {
256362306a36Sopenharmony_ci			cifs_dbg(VFS,
256462306a36Sopenharmony_ci			     "Use SMB2.1 or later for handle timeout option\n");
256562306a36Sopenharmony_ci			rc = -EOPNOTSUPP;
256662306a36Sopenharmony_ci			goto out_fail;
256762306a36Sopenharmony_ci		} else
256862306a36Sopenharmony_ci			tcon->handle_timeout = ctx->handle_timeout;
256962306a36Sopenharmony_ci	}
257062306a36Sopenharmony_ci
257162306a36Sopenharmony_ci	tcon->ses = ses;
257262306a36Sopenharmony_ci	if (ctx->password) {
257362306a36Sopenharmony_ci		tcon->password = kstrdup(ctx->password, GFP_KERNEL);
257462306a36Sopenharmony_ci		if (!tcon->password) {
257562306a36Sopenharmony_ci			rc = -ENOMEM;
257662306a36Sopenharmony_ci			goto out_fail;
257762306a36Sopenharmony_ci		}
257862306a36Sopenharmony_ci	}
257962306a36Sopenharmony_ci
258062306a36Sopenharmony_ci	if (ctx->seal) {
258162306a36Sopenharmony_ci		if (ses->server->vals->protocol_id == 0) {
258262306a36Sopenharmony_ci			cifs_dbg(VFS,
258362306a36Sopenharmony_ci				 "SMB3 or later required for encryption\n");
258462306a36Sopenharmony_ci			rc = -EOPNOTSUPP;
258562306a36Sopenharmony_ci			goto out_fail;
258662306a36Sopenharmony_ci		} else if (tcon->ses->server->capabilities &
258762306a36Sopenharmony_ci					SMB2_GLOBAL_CAP_ENCRYPTION)
258862306a36Sopenharmony_ci			tcon->seal = true;
258962306a36Sopenharmony_ci		else {
259062306a36Sopenharmony_ci			cifs_dbg(VFS, "Encryption is not supported on share\n");
259162306a36Sopenharmony_ci			rc = -EOPNOTSUPP;
259262306a36Sopenharmony_ci			goto out_fail;
259362306a36Sopenharmony_ci		}
259462306a36Sopenharmony_ci	}
259562306a36Sopenharmony_ci
259662306a36Sopenharmony_ci	if (ctx->linux_ext) {
259762306a36Sopenharmony_ci		if (ses->server->posix_ext_supported) {
259862306a36Sopenharmony_ci			tcon->posix_extensions = true;
259962306a36Sopenharmony_ci			pr_warn_once("SMB3.11 POSIX Extensions are experimental\n");
260062306a36Sopenharmony_ci		} else if ((ses->server->vals->protocol_id == SMB311_PROT_ID) ||
260162306a36Sopenharmony_ci		    (strcmp(ses->server->vals->version_string,
260262306a36Sopenharmony_ci		     SMB3ANY_VERSION_STRING) == 0) ||
260362306a36Sopenharmony_ci		    (strcmp(ses->server->vals->version_string,
260462306a36Sopenharmony_ci		     SMBDEFAULT_VERSION_STRING) == 0)) {
260562306a36Sopenharmony_ci			cifs_dbg(VFS, "Server does not support mounting with posix SMB3.11 extensions\n");
260662306a36Sopenharmony_ci			rc = -EOPNOTSUPP;
260762306a36Sopenharmony_ci			goto out_fail;
260862306a36Sopenharmony_ci		} else {
260962306a36Sopenharmony_ci			cifs_dbg(VFS, "Check vers= mount option. SMB3.11 "
261062306a36Sopenharmony_ci				"disabled but required for POSIX extensions\n");
261162306a36Sopenharmony_ci			rc = -EOPNOTSUPP;
261262306a36Sopenharmony_ci			goto out_fail;
261362306a36Sopenharmony_ci		}
261462306a36Sopenharmony_ci	}
261562306a36Sopenharmony_ci
261662306a36Sopenharmony_ci	xid = get_xid();
261762306a36Sopenharmony_ci	rc = ses->server->ops->tree_connect(xid, ses, ctx->UNC, tcon,
261862306a36Sopenharmony_ci					    ctx->local_nls);
261962306a36Sopenharmony_ci	free_xid(xid);
262062306a36Sopenharmony_ci	cifs_dbg(FYI, "Tcon rc = %d\n", rc);
262162306a36Sopenharmony_ci	if (rc)
262262306a36Sopenharmony_ci		goto out_fail;
262362306a36Sopenharmony_ci
262462306a36Sopenharmony_ci	tcon->use_persistent = false;
262562306a36Sopenharmony_ci	/* check if SMB2 or later, CIFS does not support persistent handles */
262662306a36Sopenharmony_ci	if (ctx->persistent) {
262762306a36Sopenharmony_ci		if (ses->server->vals->protocol_id == 0) {
262862306a36Sopenharmony_ci			cifs_dbg(VFS,
262962306a36Sopenharmony_ci			     "SMB3 or later required for persistent handles\n");
263062306a36Sopenharmony_ci			rc = -EOPNOTSUPP;
263162306a36Sopenharmony_ci			goto out_fail;
263262306a36Sopenharmony_ci		} else if (ses->server->capabilities &
263362306a36Sopenharmony_ci			   SMB2_GLOBAL_CAP_PERSISTENT_HANDLES)
263462306a36Sopenharmony_ci			tcon->use_persistent = true;
263562306a36Sopenharmony_ci		else /* persistent handles requested but not supported */ {
263662306a36Sopenharmony_ci			cifs_dbg(VFS,
263762306a36Sopenharmony_ci				"Persistent handles not supported on share\n");
263862306a36Sopenharmony_ci			rc = -EOPNOTSUPP;
263962306a36Sopenharmony_ci			goto out_fail;
264062306a36Sopenharmony_ci		}
264162306a36Sopenharmony_ci	} else if ((tcon->capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY)
264262306a36Sopenharmony_ci	     && (ses->server->capabilities & SMB2_GLOBAL_CAP_PERSISTENT_HANDLES)
264362306a36Sopenharmony_ci	     && (ctx->nopersistent == false)) {
264462306a36Sopenharmony_ci		cifs_dbg(FYI, "enabling persistent handles\n");
264562306a36Sopenharmony_ci		tcon->use_persistent = true;
264662306a36Sopenharmony_ci	} else if (ctx->resilient) {
264762306a36Sopenharmony_ci		if (ses->server->vals->protocol_id == 0) {
264862306a36Sopenharmony_ci			cifs_dbg(VFS,
264962306a36Sopenharmony_ci			     "SMB2.1 or later required for resilient handles\n");
265062306a36Sopenharmony_ci			rc = -EOPNOTSUPP;
265162306a36Sopenharmony_ci			goto out_fail;
265262306a36Sopenharmony_ci		}
265362306a36Sopenharmony_ci		tcon->use_resilient = true;
265462306a36Sopenharmony_ci	}
265562306a36Sopenharmony_ci
265662306a36Sopenharmony_ci	tcon->use_witness = false;
265762306a36Sopenharmony_ci	if (IS_ENABLED(CONFIG_CIFS_SWN_UPCALL) && ctx->witness) {
265862306a36Sopenharmony_ci		if (ses->server->vals->protocol_id >= SMB30_PROT_ID) {
265962306a36Sopenharmony_ci			if (tcon->capabilities & SMB2_SHARE_CAP_CLUSTER) {
266062306a36Sopenharmony_ci				/*
266162306a36Sopenharmony_ci				 * Set witness in use flag in first place
266262306a36Sopenharmony_ci				 * to retry registration in the echo task
266362306a36Sopenharmony_ci				 */
266462306a36Sopenharmony_ci				tcon->use_witness = true;
266562306a36Sopenharmony_ci				/* And try to register immediately */
266662306a36Sopenharmony_ci				rc = cifs_swn_register(tcon);
266762306a36Sopenharmony_ci				if (rc < 0) {
266862306a36Sopenharmony_ci					cifs_dbg(VFS, "Failed to register for witness notifications: %d\n", rc);
266962306a36Sopenharmony_ci					goto out_fail;
267062306a36Sopenharmony_ci				}
267162306a36Sopenharmony_ci			} else {
267262306a36Sopenharmony_ci				/* TODO: try to extend for non-cluster uses (eg multichannel) */
267362306a36Sopenharmony_ci				cifs_dbg(VFS, "witness requested on mount but no CLUSTER capability on share\n");
267462306a36Sopenharmony_ci				rc = -EOPNOTSUPP;
267562306a36Sopenharmony_ci				goto out_fail;
267662306a36Sopenharmony_ci			}
267762306a36Sopenharmony_ci		} else {
267862306a36Sopenharmony_ci			cifs_dbg(VFS, "SMB3 or later required for witness option\n");
267962306a36Sopenharmony_ci			rc = -EOPNOTSUPP;
268062306a36Sopenharmony_ci			goto out_fail;
268162306a36Sopenharmony_ci		}
268262306a36Sopenharmony_ci	}
268362306a36Sopenharmony_ci
268462306a36Sopenharmony_ci	/* If the user really knows what they are doing they can override */
268562306a36Sopenharmony_ci	if (tcon->share_flags & SMB2_SHAREFLAG_NO_CACHING) {
268662306a36Sopenharmony_ci		if (ctx->cache_ro)
268762306a36Sopenharmony_ci			cifs_dbg(VFS, "cache=ro requested on mount but NO_CACHING flag set on share\n");
268862306a36Sopenharmony_ci		else if (ctx->cache_rw)
268962306a36Sopenharmony_ci			cifs_dbg(VFS, "cache=singleclient requested on mount but NO_CACHING flag set on share\n");
269062306a36Sopenharmony_ci	}
269162306a36Sopenharmony_ci
269262306a36Sopenharmony_ci	if (ctx->no_lease) {
269362306a36Sopenharmony_ci		if (ses->server->vals->protocol_id == 0) {
269462306a36Sopenharmony_ci			cifs_dbg(VFS,
269562306a36Sopenharmony_ci				"SMB2 or later required for nolease option\n");
269662306a36Sopenharmony_ci			rc = -EOPNOTSUPP;
269762306a36Sopenharmony_ci			goto out_fail;
269862306a36Sopenharmony_ci		} else
269962306a36Sopenharmony_ci			tcon->no_lease = ctx->no_lease;
270062306a36Sopenharmony_ci	}
270162306a36Sopenharmony_ci
270262306a36Sopenharmony_ci	/*
270362306a36Sopenharmony_ci	 * We can have only one retry value for a connection to a share so for
270462306a36Sopenharmony_ci	 * resources mounted more than once to the same server share the last
270562306a36Sopenharmony_ci	 * value passed in for the retry flag is used.
270662306a36Sopenharmony_ci	 */
270762306a36Sopenharmony_ci	tcon->retry = ctx->retry;
270862306a36Sopenharmony_ci	tcon->nocase = ctx->nocase;
270962306a36Sopenharmony_ci	tcon->broken_sparse_sup = ctx->no_sparse;
271062306a36Sopenharmony_ci	tcon->max_cached_dirs = ctx->max_cached_dirs;
271162306a36Sopenharmony_ci	tcon->nodelete = ctx->nodelete;
271262306a36Sopenharmony_ci	tcon->local_lease = ctx->local_lease;
271362306a36Sopenharmony_ci	INIT_LIST_HEAD(&tcon->pending_opens);
271462306a36Sopenharmony_ci	tcon->status = TID_GOOD;
271562306a36Sopenharmony_ci
271662306a36Sopenharmony_ci	INIT_DELAYED_WORK(&tcon->query_interfaces,
271762306a36Sopenharmony_ci			  smb2_query_server_interfaces);
271862306a36Sopenharmony_ci	if (ses->server->dialect >= SMB30_PROT_ID &&
271962306a36Sopenharmony_ci	    (ses->server->capabilities & SMB2_GLOBAL_CAP_MULTI_CHANNEL)) {
272062306a36Sopenharmony_ci		/* schedule query interfaces poll */
272162306a36Sopenharmony_ci		queue_delayed_work(cifsiod_wq, &tcon->query_interfaces,
272262306a36Sopenharmony_ci				   (SMB_INTERFACE_POLL_INTERVAL * HZ));
272362306a36Sopenharmony_ci	}
272462306a36Sopenharmony_ci#ifdef CONFIG_CIFS_DFS_UPCALL
272562306a36Sopenharmony_ci	INIT_DELAYED_WORK(&tcon->dfs_cache_work, dfs_cache_refresh);
272662306a36Sopenharmony_ci#endif
272762306a36Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
272862306a36Sopenharmony_ci	list_add(&tcon->tcon_list, &ses->tcon_list);
272962306a36Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
273062306a36Sopenharmony_ci
273162306a36Sopenharmony_ci	return tcon;
273262306a36Sopenharmony_ci
273362306a36Sopenharmony_ciout_fail:
273462306a36Sopenharmony_ci	tconInfoFree(tcon);
273562306a36Sopenharmony_ci	return ERR_PTR(rc);
273662306a36Sopenharmony_ci}
273762306a36Sopenharmony_ci
273862306a36Sopenharmony_civoid
273962306a36Sopenharmony_cicifs_put_tlink(struct tcon_link *tlink)
274062306a36Sopenharmony_ci{
274162306a36Sopenharmony_ci	if (!tlink || IS_ERR(tlink))
274262306a36Sopenharmony_ci		return;
274362306a36Sopenharmony_ci
274462306a36Sopenharmony_ci	if (!atomic_dec_and_test(&tlink->tl_count) ||
274562306a36Sopenharmony_ci	    test_bit(TCON_LINK_IN_TREE, &tlink->tl_flags)) {
274662306a36Sopenharmony_ci		tlink->tl_time = jiffies;
274762306a36Sopenharmony_ci		return;
274862306a36Sopenharmony_ci	}
274962306a36Sopenharmony_ci
275062306a36Sopenharmony_ci	if (!IS_ERR(tlink_tcon(tlink)))
275162306a36Sopenharmony_ci		cifs_put_tcon(tlink_tcon(tlink));
275262306a36Sopenharmony_ci	kfree(tlink);
275362306a36Sopenharmony_ci	return;
275462306a36Sopenharmony_ci}
275562306a36Sopenharmony_ci
275662306a36Sopenharmony_cistatic int
275762306a36Sopenharmony_cicompare_mount_options(struct super_block *sb, struct cifs_mnt_data *mnt_data)
275862306a36Sopenharmony_ci{
275962306a36Sopenharmony_ci	struct cifs_sb_info *old = CIFS_SB(sb);
276062306a36Sopenharmony_ci	struct cifs_sb_info *new = mnt_data->cifs_sb;
276162306a36Sopenharmony_ci	unsigned int oldflags = old->mnt_cifs_flags & CIFS_MOUNT_MASK;
276262306a36Sopenharmony_ci	unsigned int newflags = new->mnt_cifs_flags & CIFS_MOUNT_MASK;
276362306a36Sopenharmony_ci
276462306a36Sopenharmony_ci	if ((sb->s_flags & CIFS_MS_MASK) != (mnt_data->flags & CIFS_MS_MASK))
276562306a36Sopenharmony_ci		return 0;
276662306a36Sopenharmony_ci
276762306a36Sopenharmony_ci	if (old->mnt_cifs_serverino_autodisabled)
276862306a36Sopenharmony_ci		newflags &= ~CIFS_MOUNT_SERVER_INUM;
276962306a36Sopenharmony_ci
277062306a36Sopenharmony_ci	if (oldflags != newflags)
277162306a36Sopenharmony_ci		return 0;
277262306a36Sopenharmony_ci
277362306a36Sopenharmony_ci	/*
277462306a36Sopenharmony_ci	 * We want to share sb only if we don't specify an r/wsize or
277562306a36Sopenharmony_ci	 * specified r/wsize is greater than or equal to existing one.
277662306a36Sopenharmony_ci	 */
277762306a36Sopenharmony_ci	if (new->ctx->wsize && new->ctx->wsize < old->ctx->wsize)
277862306a36Sopenharmony_ci		return 0;
277962306a36Sopenharmony_ci
278062306a36Sopenharmony_ci	if (new->ctx->rsize && new->ctx->rsize < old->ctx->rsize)
278162306a36Sopenharmony_ci		return 0;
278262306a36Sopenharmony_ci
278362306a36Sopenharmony_ci	if (!uid_eq(old->ctx->linux_uid, new->ctx->linux_uid) ||
278462306a36Sopenharmony_ci	    !gid_eq(old->ctx->linux_gid, new->ctx->linux_gid))
278562306a36Sopenharmony_ci		return 0;
278662306a36Sopenharmony_ci
278762306a36Sopenharmony_ci	if (old->ctx->file_mode != new->ctx->file_mode ||
278862306a36Sopenharmony_ci	    old->ctx->dir_mode != new->ctx->dir_mode)
278962306a36Sopenharmony_ci		return 0;
279062306a36Sopenharmony_ci
279162306a36Sopenharmony_ci	if (strcmp(old->local_nls->charset, new->local_nls->charset))
279262306a36Sopenharmony_ci		return 0;
279362306a36Sopenharmony_ci
279462306a36Sopenharmony_ci	if (old->ctx->acregmax != new->ctx->acregmax)
279562306a36Sopenharmony_ci		return 0;
279662306a36Sopenharmony_ci	if (old->ctx->acdirmax != new->ctx->acdirmax)
279762306a36Sopenharmony_ci		return 0;
279862306a36Sopenharmony_ci	if (old->ctx->closetimeo != new->ctx->closetimeo)
279962306a36Sopenharmony_ci		return 0;
280062306a36Sopenharmony_ci
280162306a36Sopenharmony_ci	return 1;
280262306a36Sopenharmony_ci}
280362306a36Sopenharmony_ci
280462306a36Sopenharmony_cistatic int match_prepath(struct super_block *sb,
280562306a36Sopenharmony_ci			 struct cifs_tcon *tcon,
280662306a36Sopenharmony_ci			 struct cifs_mnt_data *mnt_data)
280762306a36Sopenharmony_ci{
280862306a36Sopenharmony_ci	struct smb3_fs_context *ctx = mnt_data->ctx;
280962306a36Sopenharmony_ci	struct cifs_sb_info *old = CIFS_SB(sb);
281062306a36Sopenharmony_ci	struct cifs_sb_info *new = mnt_data->cifs_sb;
281162306a36Sopenharmony_ci	bool old_set = (old->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) &&
281262306a36Sopenharmony_ci		old->prepath;
281362306a36Sopenharmony_ci	bool new_set = (new->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) &&
281462306a36Sopenharmony_ci		new->prepath;
281562306a36Sopenharmony_ci
281662306a36Sopenharmony_ci	if (tcon->origin_fullpath &&
281762306a36Sopenharmony_ci	    dfs_src_pathname_equal(tcon->origin_fullpath, ctx->source))
281862306a36Sopenharmony_ci		return 1;
281962306a36Sopenharmony_ci
282062306a36Sopenharmony_ci	if (old_set && new_set && !strcmp(new->prepath, old->prepath))
282162306a36Sopenharmony_ci		return 1;
282262306a36Sopenharmony_ci	else if (!old_set && !new_set)
282362306a36Sopenharmony_ci		return 1;
282462306a36Sopenharmony_ci
282562306a36Sopenharmony_ci	return 0;
282662306a36Sopenharmony_ci}
282762306a36Sopenharmony_ci
282862306a36Sopenharmony_ciint
282962306a36Sopenharmony_cicifs_match_super(struct super_block *sb, void *data)
283062306a36Sopenharmony_ci{
283162306a36Sopenharmony_ci	struct cifs_mnt_data *mnt_data = data;
283262306a36Sopenharmony_ci	struct smb3_fs_context *ctx;
283362306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb;
283462306a36Sopenharmony_ci	struct TCP_Server_Info *tcp_srv;
283562306a36Sopenharmony_ci	struct cifs_ses *ses;
283662306a36Sopenharmony_ci	struct cifs_tcon *tcon;
283762306a36Sopenharmony_ci	struct tcon_link *tlink;
283862306a36Sopenharmony_ci	int rc = 0;
283962306a36Sopenharmony_ci
284062306a36Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
284162306a36Sopenharmony_ci	cifs_sb = CIFS_SB(sb);
284262306a36Sopenharmony_ci
284362306a36Sopenharmony_ci	/* We do not want to use a superblock that has been shutdown */
284462306a36Sopenharmony_ci	if (CIFS_MOUNT_SHUTDOWN & cifs_sb->mnt_cifs_flags) {
284562306a36Sopenharmony_ci		spin_unlock(&cifs_tcp_ses_lock);
284662306a36Sopenharmony_ci		return 0;
284762306a36Sopenharmony_ci	}
284862306a36Sopenharmony_ci
284962306a36Sopenharmony_ci	tlink = cifs_get_tlink(cifs_sb_master_tlink(cifs_sb));
285062306a36Sopenharmony_ci	if (IS_ERR_OR_NULL(tlink)) {
285162306a36Sopenharmony_ci		pr_warn_once("%s: skip super matching due to bad tlink(%p)\n",
285262306a36Sopenharmony_ci			     __func__, tlink);
285362306a36Sopenharmony_ci		spin_unlock(&cifs_tcp_ses_lock);
285462306a36Sopenharmony_ci		return 0;
285562306a36Sopenharmony_ci	}
285662306a36Sopenharmony_ci	tcon = tlink_tcon(tlink);
285762306a36Sopenharmony_ci	ses = tcon->ses;
285862306a36Sopenharmony_ci	tcp_srv = ses->server;
285962306a36Sopenharmony_ci
286062306a36Sopenharmony_ci	ctx = mnt_data->ctx;
286162306a36Sopenharmony_ci
286262306a36Sopenharmony_ci	spin_lock(&tcp_srv->srv_lock);
286362306a36Sopenharmony_ci	spin_lock(&ses->ses_lock);
286462306a36Sopenharmony_ci	spin_lock(&ses->chan_lock);
286562306a36Sopenharmony_ci	spin_lock(&tcon->tc_lock);
286662306a36Sopenharmony_ci	if (!match_server(tcp_srv, ctx, true) ||
286762306a36Sopenharmony_ci	    !match_session(ses, ctx) ||
286862306a36Sopenharmony_ci	    !match_tcon(tcon, ctx) ||
286962306a36Sopenharmony_ci	    !match_prepath(sb, tcon, mnt_data)) {
287062306a36Sopenharmony_ci		rc = 0;
287162306a36Sopenharmony_ci		goto out;
287262306a36Sopenharmony_ci	}
287362306a36Sopenharmony_ci
287462306a36Sopenharmony_ci	rc = compare_mount_options(sb, mnt_data);
287562306a36Sopenharmony_ciout:
287662306a36Sopenharmony_ci	spin_unlock(&tcon->tc_lock);
287762306a36Sopenharmony_ci	spin_unlock(&ses->chan_lock);
287862306a36Sopenharmony_ci	spin_unlock(&ses->ses_lock);
287962306a36Sopenharmony_ci	spin_unlock(&tcp_srv->srv_lock);
288062306a36Sopenharmony_ci
288162306a36Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
288262306a36Sopenharmony_ci	cifs_put_tlink(tlink);
288362306a36Sopenharmony_ci	return rc;
288462306a36Sopenharmony_ci}
288562306a36Sopenharmony_ci
288662306a36Sopenharmony_ci#ifdef CONFIG_DEBUG_LOCK_ALLOC
288762306a36Sopenharmony_cistatic struct lock_class_key cifs_key[2];
288862306a36Sopenharmony_cistatic struct lock_class_key cifs_slock_key[2];
288962306a36Sopenharmony_ci
289062306a36Sopenharmony_cistatic inline void
289162306a36Sopenharmony_cicifs_reclassify_socket4(struct socket *sock)
289262306a36Sopenharmony_ci{
289362306a36Sopenharmony_ci	struct sock *sk = sock->sk;
289462306a36Sopenharmony_ci	BUG_ON(!sock_allow_reclassification(sk));
289562306a36Sopenharmony_ci	sock_lock_init_class_and_name(sk, "slock-AF_INET-CIFS",
289662306a36Sopenharmony_ci		&cifs_slock_key[0], "sk_lock-AF_INET-CIFS", &cifs_key[0]);
289762306a36Sopenharmony_ci}
289862306a36Sopenharmony_ci
289962306a36Sopenharmony_cistatic inline void
290062306a36Sopenharmony_cicifs_reclassify_socket6(struct socket *sock)
290162306a36Sopenharmony_ci{
290262306a36Sopenharmony_ci	struct sock *sk = sock->sk;
290362306a36Sopenharmony_ci	BUG_ON(!sock_allow_reclassification(sk));
290462306a36Sopenharmony_ci	sock_lock_init_class_and_name(sk, "slock-AF_INET6-CIFS",
290562306a36Sopenharmony_ci		&cifs_slock_key[1], "sk_lock-AF_INET6-CIFS", &cifs_key[1]);
290662306a36Sopenharmony_ci}
290762306a36Sopenharmony_ci#else
290862306a36Sopenharmony_cistatic inline void
290962306a36Sopenharmony_cicifs_reclassify_socket4(struct socket *sock)
291062306a36Sopenharmony_ci{
291162306a36Sopenharmony_ci}
291262306a36Sopenharmony_ci
291362306a36Sopenharmony_cistatic inline void
291462306a36Sopenharmony_cicifs_reclassify_socket6(struct socket *sock)
291562306a36Sopenharmony_ci{
291662306a36Sopenharmony_ci}
291762306a36Sopenharmony_ci#endif
291862306a36Sopenharmony_ci
291962306a36Sopenharmony_ci/* See RFC1001 section 14 on representation of Netbios names */
292062306a36Sopenharmony_cistatic void rfc1002mangle(char *target, char *source, unsigned int length)
292162306a36Sopenharmony_ci{
292262306a36Sopenharmony_ci	unsigned int i, j;
292362306a36Sopenharmony_ci
292462306a36Sopenharmony_ci	for (i = 0, j = 0; i < (length); i++) {
292562306a36Sopenharmony_ci		/* mask a nibble at a time and encode */
292662306a36Sopenharmony_ci		target[j] = 'A' + (0x0F & (source[i] >> 4));
292762306a36Sopenharmony_ci		target[j+1] = 'A' + (0x0F & source[i]);
292862306a36Sopenharmony_ci		j += 2;
292962306a36Sopenharmony_ci	}
293062306a36Sopenharmony_ci
293162306a36Sopenharmony_ci}
293262306a36Sopenharmony_ci
293362306a36Sopenharmony_cistatic int
293462306a36Sopenharmony_cibind_socket(struct TCP_Server_Info *server)
293562306a36Sopenharmony_ci{
293662306a36Sopenharmony_ci	int rc = 0;
293762306a36Sopenharmony_ci	if (server->srcaddr.ss_family != AF_UNSPEC) {
293862306a36Sopenharmony_ci		/* Bind to the specified local IP address */
293962306a36Sopenharmony_ci		struct socket *socket = server->ssocket;
294062306a36Sopenharmony_ci		rc = kernel_bind(socket,
294162306a36Sopenharmony_ci				 (struct sockaddr *) &server->srcaddr,
294262306a36Sopenharmony_ci				 sizeof(server->srcaddr));
294362306a36Sopenharmony_ci		if (rc < 0) {
294462306a36Sopenharmony_ci			struct sockaddr_in *saddr4;
294562306a36Sopenharmony_ci			struct sockaddr_in6 *saddr6;
294662306a36Sopenharmony_ci			saddr4 = (struct sockaddr_in *)&server->srcaddr;
294762306a36Sopenharmony_ci			saddr6 = (struct sockaddr_in6 *)&server->srcaddr;
294862306a36Sopenharmony_ci			if (saddr6->sin6_family == AF_INET6)
294962306a36Sopenharmony_ci				cifs_server_dbg(VFS, "Failed to bind to: %pI6c, error: %d\n",
295062306a36Sopenharmony_ci					 &saddr6->sin6_addr, rc);
295162306a36Sopenharmony_ci			else
295262306a36Sopenharmony_ci				cifs_server_dbg(VFS, "Failed to bind to: %pI4, error: %d\n",
295362306a36Sopenharmony_ci					 &saddr4->sin_addr.s_addr, rc);
295462306a36Sopenharmony_ci		}
295562306a36Sopenharmony_ci	}
295662306a36Sopenharmony_ci	return rc;
295762306a36Sopenharmony_ci}
295862306a36Sopenharmony_ci
295962306a36Sopenharmony_cistatic int
296062306a36Sopenharmony_ciip_rfc1001_connect(struct TCP_Server_Info *server)
296162306a36Sopenharmony_ci{
296262306a36Sopenharmony_ci	int rc = 0;
296362306a36Sopenharmony_ci	/*
296462306a36Sopenharmony_ci	 * some servers require RFC1001 sessinit before sending
296562306a36Sopenharmony_ci	 * negprot - BB check reconnection in case where second
296662306a36Sopenharmony_ci	 * sessinit is sent but no second negprot
296762306a36Sopenharmony_ci	 */
296862306a36Sopenharmony_ci	struct rfc1002_session_packet req = {};
296962306a36Sopenharmony_ci	struct smb_hdr *smb_buf = (struct smb_hdr *)&req;
297062306a36Sopenharmony_ci	unsigned int len;
297162306a36Sopenharmony_ci
297262306a36Sopenharmony_ci	req.trailer.session_req.called_len = sizeof(req.trailer.session_req.called_name);
297362306a36Sopenharmony_ci
297462306a36Sopenharmony_ci	if (server->server_RFC1001_name[0] != 0)
297562306a36Sopenharmony_ci		rfc1002mangle(req.trailer.session_req.called_name,
297662306a36Sopenharmony_ci			      server->server_RFC1001_name,
297762306a36Sopenharmony_ci			      RFC1001_NAME_LEN_WITH_NULL);
297862306a36Sopenharmony_ci	else
297962306a36Sopenharmony_ci		rfc1002mangle(req.trailer.session_req.called_name,
298062306a36Sopenharmony_ci			      DEFAULT_CIFS_CALLED_NAME,
298162306a36Sopenharmony_ci			      RFC1001_NAME_LEN_WITH_NULL);
298262306a36Sopenharmony_ci
298362306a36Sopenharmony_ci	req.trailer.session_req.calling_len = sizeof(req.trailer.session_req.calling_name);
298462306a36Sopenharmony_ci
298562306a36Sopenharmony_ci	/* calling name ends in null (byte 16) from old smb convention */
298662306a36Sopenharmony_ci	if (server->workstation_RFC1001_name[0] != 0)
298762306a36Sopenharmony_ci		rfc1002mangle(req.trailer.session_req.calling_name,
298862306a36Sopenharmony_ci			      server->workstation_RFC1001_name,
298962306a36Sopenharmony_ci			      RFC1001_NAME_LEN_WITH_NULL);
299062306a36Sopenharmony_ci	else
299162306a36Sopenharmony_ci		rfc1002mangle(req.trailer.session_req.calling_name,
299262306a36Sopenharmony_ci			      "LINUX_CIFS_CLNT",
299362306a36Sopenharmony_ci			      RFC1001_NAME_LEN_WITH_NULL);
299462306a36Sopenharmony_ci
299562306a36Sopenharmony_ci	/*
299662306a36Sopenharmony_ci	 * As per rfc1002, @len must be the number of bytes that follows the
299762306a36Sopenharmony_ci	 * length field of a rfc1002 session request payload.
299862306a36Sopenharmony_ci	 */
299962306a36Sopenharmony_ci	len = sizeof(req) - offsetof(struct rfc1002_session_packet, trailer.session_req);
300062306a36Sopenharmony_ci
300162306a36Sopenharmony_ci	smb_buf->smb_buf_length = cpu_to_be32((RFC1002_SESSION_REQUEST << 24) | len);
300262306a36Sopenharmony_ci	rc = smb_send(server, smb_buf, len);
300362306a36Sopenharmony_ci	/*
300462306a36Sopenharmony_ci	 * RFC1001 layer in at least one server requires very short break before
300562306a36Sopenharmony_ci	 * negprot presumably because not expecting negprot to follow so fast.
300662306a36Sopenharmony_ci	 * This is a simple solution that works without complicating the code
300762306a36Sopenharmony_ci	 * and causes no significant slowing down on mount for everyone else
300862306a36Sopenharmony_ci	 */
300962306a36Sopenharmony_ci	usleep_range(1000, 2000);
301062306a36Sopenharmony_ci
301162306a36Sopenharmony_ci	return rc;
301262306a36Sopenharmony_ci}
301362306a36Sopenharmony_ci
301462306a36Sopenharmony_cistatic int
301562306a36Sopenharmony_cigeneric_ip_connect(struct TCP_Server_Info *server)
301662306a36Sopenharmony_ci{
301762306a36Sopenharmony_ci	struct sockaddr *saddr;
301862306a36Sopenharmony_ci	struct socket *socket;
301962306a36Sopenharmony_ci	int slen, sfamily;
302062306a36Sopenharmony_ci	__be16 sport;
302162306a36Sopenharmony_ci	int rc = 0;
302262306a36Sopenharmony_ci
302362306a36Sopenharmony_ci	saddr = (struct sockaddr *) &server->dstaddr;
302462306a36Sopenharmony_ci
302562306a36Sopenharmony_ci	if (server->dstaddr.ss_family == AF_INET6) {
302662306a36Sopenharmony_ci		struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&server->dstaddr;
302762306a36Sopenharmony_ci
302862306a36Sopenharmony_ci		sport = ipv6->sin6_port;
302962306a36Sopenharmony_ci		slen = sizeof(struct sockaddr_in6);
303062306a36Sopenharmony_ci		sfamily = AF_INET6;
303162306a36Sopenharmony_ci		cifs_dbg(FYI, "%s: connecting to [%pI6]:%d\n", __func__, &ipv6->sin6_addr,
303262306a36Sopenharmony_ci				ntohs(sport));
303362306a36Sopenharmony_ci	} else {
303462306a36Sopenharmony_ci		struct sockaddr_in *ipv4 = (struct sockaddr_in *)&server->dstaddr;
303562306a36Sopenharmony_ci
303662306a36Sopenharmony_ci		sport = ipv4->sin_port;
303762306a36Sopenharmony_ci		slen = sizeof(struct sockaddr_in);
303862306a36Sopenharmony_ci		sfamily = AF_INET;
303962306a36Sopenharmony_ci		cifs_dbg(FYI, "%s: connecting to %pI4:%d\n", __func__, &ipv4->sin_addr,
304062306a36Sopenharmony_ci				ntohs(sport));
304162306a36Sopenharmony_ci	}
304262306a36Sopenharmony_ci
304362306a36Sopenharmony_ci	if (server->ssocket) {
304462306a36Sopenharmony_ci		socket = server->ssocket;
304562306a36Sopenharmony_ci	} else {
304662306a36Sopenharmony_ci		rc = __sock_create(cifs_net_ns(server), sfamily, SOCK_STREAM,
304762306a36Sopenharmony_ci				   IPPROTO_TCP, &server->ssocket, 1);
304862306a36Sopenharmony_ci		if (rc < 0) {
304962306a36Sopenharmony_ci			cifs_server_dbg(VFS, "Error %d creating socket\n", rc);
305062306a36Sopenharmony_ci			return rc;
305162306a36Sopenharmony_ci		}
305262306a36Sopenharmony_ci
305362306a36Sopenharmony_ci		/* BB other socket options to set KEEPALIVE, NODELAY? */
305462306a36Sopenharmony_ci		cifs_dbg(FYI, "Socket created\n");
305562306a36Sopenharmony_ci		socket = server->ssocket;
305662306a36Sopenharmony_ci		socket->sk->sk_allocation = GFP_NOFS;
305762306a36Sopenharmony_ci		socket->sk->sk_use_task_frag = false;
305862306a36Sopenharmony_ci		if (sfamily == AF_INET6)
305962306a36Sopenharmony_ci			cifs_reclassify_socket6(socket);
306062306a36Sopenharmony_ci		else
306162306a36Sopenharmony_ci			cifs_reclassify_socket4(socket);
306262306a36Sopenharmony_ci	}
306362306a36Sopenharmony_ci
306462306a36Sopenharmony_ci	rc = bind_socket(server);
306562306a36Sopenharmony_ci	if (rc < 0)
306662306a36Sopenharmony_ci		return rc;
306762306a36Sopenharmony_ci
306862306a36Sopenharmony_ci	/*
306962306a36Sopenharmony_ci	 * Eventually check for other socket options to change from
307062306a36Sopenharmony_ci	 * the default. sock_setsockopt not used because it expects
307162306a36Sopenharmony_ci	 * user space buffer
307262306a36Sopenharmony_ci	 */
307362306a36Sopenharmony_ci	socket->sk->sk_rcvtimeo = 7 * HZ;
307462306a36Sopenharmony_ci	socket->sk->sk_sndtimeo = 5 * HZ;
307562306a36Sopenharmony_ci
307662306a36Sopenharmony_ci	/* make the bufsizes depend on wsize/rsize and max requests */
307762306a36Sopenharmony_ci	if (server->noautotune) {
307862306a36Sopenharmony_ci		if (socket->sk->sk_sndbuf < (200 * 1024))
307962306a36Sopenharmony_ci			socket->sk->sk_sndbuf = 200 * 1024;
308062306a36Sopenharmony_ci		if (socket->sk->sk_rcvbuf < (140 * 1024))
308162306a36Sopenharmony_ci			socket->sk->sk_rcvbuf = 140 * 1024;
308262306a36Sopenharmony_ci	}
308362306a36Sopenharmony_ci
308462306a36Sopenharmony_ci	if (server->tcp_nodelay)
308562306a36Sopenharmony_ci		tcp_sock_set_nodelay(socket->sk);
308662306a36Sopenharmony_ci
308762306a36Sopenharmony_ci	cifs_dbg(FYI, "sndbuf %d rcvbuf %d rcvtimeo 0x%lx\n",
308862306a36Sopenharmony_ci		 socket->sk->sk_sndbuf,
308962306a36Sopenharmony_ci		 socket->sk->sk_rcvbuf, socket->sk->sk_rcvtimeo);
309062306a36Sopenharmony_ci
309162306a36Sopenharmony_ci	rc = kernel_connect(socket, saddr, slen,
309262306a36Sopenharmony_ci			    server->noblockcnt ? O_NONBLOCK : 0);
309362306a36Sopenharmony_ci	/*
309462306a36Sopenharmony_ci	 * When mounting SMB root file systems, we do not want to block in
309562306a36Sopenharmony_ci	 * connect. Otherwise bail out and then let cifs_reconnect() perform
309662306a36Sopenharmony_ci	 * reconnect failover - if possible.
309762306a36Sopenharmony_ci	 */
309862306a36Sopenharmony_ci	if (server->noblockcnt && rc == -EINPROGRESS)
309962306a36Sopenharmony_ci		rc = 0;
310062306a36Sopenharmony_ci	if (rc < 0) {
310162306a36Sopenharmony_ci		cifs_dbg(FYI, "Error %d connecting to server\n", rc);
310262306a36Sopenharmony_ci		trace_smb3_connect_err(server->hostname, server->conn_id, &server->dstaddr, rc);
310362306a36Sopenharmony_ci		sock_release(socket);
310462306a36Sopenharmony_ci		server->ssocket = NULL;
310562306a36Sopenharmony_ci		return rc;
310662306a36Sopenharmony_ci	}
310762306a36Sopenharmony_ci	trace_smb3_connect_done(server->hostname, server->conn_id, &server->dstaddr);
310862306a36Sopenharmony_ci	if (sport == htons(RFC1001_PORT))
310962306a36Sopenharmony_ci		rc = ip_rfc1001_connect(server);
311062306a36Sopenharmony_ci
311162306a36Sopenharmony_ci	return rc;
311262306a36Sopenharmony_ci}
311362306a36Sopenharmony_ci
311462306a36Sopenharmony_cistatic int
311562306a36Sopenharmony_ciip_connect(struct TCP_Server_Info *server)
311662306a36Sopenharmony_ci{
311762306a36Sopenharmony_ci	__be16 *sport;
311862306a36Sopenharmony_ci	struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&server->dstaddr;
311962306a36Sopenharmony_ci	struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr;
312062306a36Sopenharmony_ci
312162306a36Sopenharmony_ci	if (server->dstaddr.ss_family == AF_INET6)
312262306a36Sopenharmony_ci		sport = &addr6->sin6_port;
312362306a36Sopenharmony_ci	else
312462306a36Sopenharmony_ci		sport = &addr->sin_port;
312562306a36Sopenharmony_ci
312662306a36Sopenharmony_ci	if (*sport == 0) {
312762306a36Sopenharmony_ci		int rc;
312862306a36Sopenharmony_ci
312962306a36Sopenharmony_ci		/* try with 445 port at first */
313062306a36Sopenharmony_ci		*sport = htons(CIFS_PORT);
313162306a36Sopenharmony_ci
313262306a36Sopenharmony_ci		rc = generic_ip_connect(server);
313362306a36Sopenharmony_ci		if (rc >= 0)
313462306a36Sopenharmony_ci			return rc;
313562306a36Sopenharmony_ci
313662306a36Sopenharmony_ci		/* if it failed, try with 139 port */
313762306a36Sopenharmony_ci		*sport = htons(RFC1001_PORT);
313862306a36Sopenharmony_ci	}
313962306a36Sopenharmony_ci
314062306a36Sopenharmony_ci	return generic_ip_connect(server);
314162306a36Sopenharmony_ci}
314262306a36Sopenharmony_ci
314362306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
314462306a36Sopenharmony_civoid reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon,
314562306a36Sopenharmony_ci			  struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
314662306a36Sopenharmony_ci{
314762306a36Sopenharmony_ci	/*
314862306a36Sopenharmony_ci	 * If we are reconnecting then should we check to see if
314962306a36Sopenharmony_ci	 * any requested capabilities changed locally e.g. via
315062306a36Sopenharmony_ci	 * remount but we can not do much about it here
315162306a36Sopenharmony_ci	 * if they have (even if we could detect it by the following)
315262306a36Sopenharmony_ci	 * Perhaps we could add a backpointer to array of sb from tcon
315362306a36Sopenharmony_ci	 * or if we change to make all sb to same share the same
315462306a36Sopenharmony_ci	 * sb as NFS - then we only have one backpointer to sb.
315562306a36Sopenharmony_ci	 * What if we wanted to mount the server share twice once with
315662306a36Sopenharmony_ci	 * and once without posixacls or posix paths?
315762306a36Sopenharmony_ci	 */
315862306a36Sopenharmony_ci	__u64 saved_cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
315962306a36Sopenharmony_ci
316062306a36Sopenharmony_ci	if (ctx && ctx->no_linux_ext) {
316162306a36Sopenharmony_ci		tcon->fsUnixInfo.Capability = 0;
316262306a36Sopenharmony_ci		tcon->unix_ext = 0; /* Unix Extensions disabled */
316362306a36Sopenharmony_ci		cifs_dbg(FYI, "Linux protocol extensions disabled\n");
316462306a36Sopenharmony_ci		return;
316562306a36Sopenharmony_ci	} else if (ctx)
316662306a36Sopenharmony_ci		tcon->unix_ext = 1; /* Unix Extensions supported */
316762306a36Sopenharmony_ci
316862306a36Sopenharmony_ci	if (!tcon->unix_ext) {
316962306a36Sopenharmony_ci		cifs_dbg(FYI, "Unix extensions disabled so not set on reconnect\n");
317062306a36Sopenharmony_ci		return;
317162306a36Sopenharmony_ci	}
317262306a36Sopenharmony_ci
317362306a36Sopenharmony_ci	if (!CIFSSMBQFSUnixInfo(xid, tcon)) {
317462306a36Sopenharmony_ci		__u64 cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
317562306a36Sopenharmony_ci		cifs_dbg(FYI, "unix caps which server supports %lld\n", cap);
317662306a36Sopenharmony_ci		/*
317762306a36Sopenharmony_ci		 * check for reconnect case in which we do not
317862306a36Sopenharmony_ci		 * want to change the mount behavior if we can avoid it
317962306a36Sopenharmony_ci		 */
318062306a36Sopenharmony_ci		if (ctx == NULL) {
318162306a36Sopenharmony_ci			/*
318262306a36Sopenharmony_ci			 * turn off POSIX ACL and PATHNAMES if not set
318362306a36Sopenharmony_ci			 * originally at mount time
318462306a36Sopenharmony_ci			 */
318562306a36Sopenharmony_ci			if ((saved_cap & CIFS_UNIX_POSIX_ACL_CAP) == 0)
318662306a36Sopenharmony_ci				cap &= ~CIFS_UNIX_POSIX_ACL_CAP;
318762306a36Sopenharmony_ci			if ((saved_cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0) {
318862306a36Sopenharmony_ci				if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP)
318962306a36Sopenharmony_ci					cifs_dbg(VFS, "POSIXPATH support change\n");
319062306a36Sopenharmony_ci				cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP;
319162306a36Sopenharmony_ci			} else if ((cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0) {
319262306a36Sopenharmony_ci				cifs_dbg(VFS, "possible reconnect error\n");
319362306a36Sopenharmony_ci				cifs_dbg(VFS, "server disabled POSIX path support\n");
319462306a36Sopenharmony_ci			}
319562306a36Sopenharmony_ci		}
319662306a36Sopenharmony_ci
319762306a36Sopenharmony_ci		if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP)
319862306a36Sopenharmony_ci			cifs_dbg(VFS, "per-share encryption not supported yet\n");
319962306a36Sopenharmony_ci
320062306a36Sopenharmony_ci		cap &= CIFS_UNIX_CAP_MASK;
320162306a36Sopenharmony_ci		if (ctx && ctx->no_psx_acl)
320262306a36Sopenharmony_ci			cap &= ~CIFS_UNIX_POSIX_ACL_CAP;
320362306a36Sopenharmony_ci		else if (CIFS_UNIX_POSIX_ACL_CAP & cap) {
320462306a36Sopenharmony_ci			cifs_dbg(FYI, "negotiated posix acl support\n");
320562306a36Sopenharmony_ci			if (cifs_sb)
320662306a36Sopenharmony_ci				cifs_sb->mnt_cifs_flags |=
320762306a36Sopenharmony_ci					CIFS_MOUNT_POSIXACL;
320862306a36Sopenharmony_ci		}
320962306a36Sopenharmony_ci
321062306a36Sopenharmony_ci		if (ctx && ctx->posix_paths == 0)
321162306a36Sopenharmony_ci			cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP;
321262306a36Sopenharmony_ci		else if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) {
321362306a36Sopenharmony_ci			cifs_dbg(FYI, "negotiate posix pathnames\n");
321462306a36Sopenharmony_ci			if (cifs_sb)
321562306a36Sopenharmony_ci				cifs_sb->mnt_cifs_flags |=
321662306a36Sopenharmony_ci					CIFS_MOUNT_POSIX_PATHS;
321762306a36Sopenharmony_ci		}
321862306a36Sopenharmony_ci
321962306a36Sopenharmony_ci		cifs_dbg(FYI, "Negotiate caps 0x%x\n", (int)cap);
322062306a36Sopenharmony_ci#ifdef CONFIG_CIFS_DEBUG2
322162306a36Sopenharmony_ci		if (cap & CIFS_UNIX_FCNTL_CAP)
322262306a36Sopenharmony_ci			cifs_dbg(FYI, "FCNTL cap\n");
322362306a36Sopenharmony_ci		if (cap & CIFS_UNIX_EXTATTR_CAP)
322462306a36Sopenharmony_ci			cifs_dbg(FYI, "EXTATTR cap\n");
322562306a36Sopenharmony_ci		if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP)
322662306a36Sopenharmony_ci			cifs_dbg(FYI, "POSIX path cap\n");
322762306a36Sopenharmony_ci		if (cap & CIFS_UNIX_XATTR_CAP)
322862306a36Sopenharmony_ci			cifs_dbg(FYI, "XATTR cap\n");
322962306a36Sopenharmony_ci		if (cap & CIFS_UNIX_POSIX_ACL_CAP)
323062306a36Sopenharmony_ci			cifs_dbg(FYI, "POSIX ACL cap\n");
323162306a36Sopenharmony_ci		if (cap & CIFS_UNIX_LARGE_READ_CAP)
323262306a36Sopenharmony_ci			cifs_dbg(FYI, "very large read cap\n");
323362306a36Sopenharmony_ci		if (cap & CIFS_UNIX_LARGE_WRITE_CAP)
323462306a36Sopenharmony_ci			cifs_dbg(FYI, "very large write cap\n");
323562306a36Sopenharmony_ci		if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP)
323662306a36Sopenharmony_ci			cifs_dbg(FYI, "transport encryption cap\n");
323762306a36Sopenharmony_ci		if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP)
323862306a36Sopenharmony_ci			cifs_dbg(FYI, "mandatory transport encryption cap\n");
323962306a36Sopenharmony_ci#endif /* CIFS_DEBUG2 */
324062306a36Sopenharmony_ci		if (CIFSSMBSetFSUnixInfo(xid, tcon, cap)) {
324162306a36Sopenharmony_ci			if (ctx == NULL)
324262306a36Sopenharmony_ci				cifs_dbg(FYI, "resetting capabilities failed\n");
324362306a36Sopenharmony_ci			else
324462306a36Sopenharmony_ci				cifs_dbg(VFS, "Negotiating Unix capabilities with the server failed. Consider mounting with the Unix Extensions disabled if problems are found by specifying the nounix mount option.\n");
324562306a36Sopenharmony_ci
324662306a36Sopenharmony_ci		}
324762306a36Sopenharmony_ci	}
324862306a36Sopenharmony_ci}
324962306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
325062306a36Sopenharmony_ci
325162306a36Sopenharmony_ciint cifs_setup_cifs_sb(struct cifs_sb_info *cifs_sb)
325262306a36Sopenharmony_ci{
325362306a36Sopenharmony_ci	struct smb3_fs_context *ctx = cifs_sb->ctx;
325462306a36Sopenharmony_ci
325562306a36Sopenharmony_ci	INIT_DELAYED_WORK(&cifs_sb->prune_tlinks, cifs_prune_tlinks);
325662306a36Sopenharmony_ci
325762306a36Sopenharmony_ci	spin_lock_init(&cifs_sb->tlink_tree_lock);
325862306a36Sopenharmony_ci	cifs_sb->tlink_tree = RB_ROOT;
325962306a36Sopenharmony_ci
326062306a36Sopenharmony_ci	cifs_dbg(FYI, "file mode: %04ho  dir mode: %04ho\n",
326162306a36Sopenharmony_ci		 ctx->file_mode, ctx->dir_mode);
326262306a36Sopenharmony_ci
326362306a36Sopenharmony_ci	/* this is needed for ASCII cp to Unicode converts */
326462306a36Sopenharmony_ci	if (ctx->iocharset == NULL) {
326562306a36Sopenharmony_ci		/* load_nls_default cannot return null */
326662306a36Sopenharmony_ci		cifs_sb->local_nls = load_nls_default();
326762306a36Sopenharmony_ci	} else {
326862306a36Sopenharmony_ci		cifs_sb->local_nls = load_nls(ctx->iocharset);
326962306a36Sopenharmony_ci		if (cifs_sb->local_nls == NULL) {
327062306a36Sopenharmony_ci			cifs_dbg(VFS, "CIFS mount error: iocharset %s not found\n",
327162306a36Sopenharmony_ci				 ctx->iocharset);
327262306a36Sopenharmony_ci			return -ELIBACC;
327362306a36Sopenharmony_ci		}
327462306a36Sopenharmony_ci	}
327562306a36Sopenharmony_ci	ctx->local_nls = cifs_sb->local_nls;
327662306a36Sopenharmony_ci
327762306a36Sopenharmony_ci	smb3_update_mnt_flags(cifs_sb);
327862306a36Sopenharmony_ci
327962306a36Sopenharmony_ci	if (ctx->direct_io)
328062306a36Sopenharmony_ci		cifs_dbg(FYI, "mounting share using direct i/o\n");
328162306a36Sopenharmony_ci	if (ctx->cache_ro) {
328262306a36Sopenharmony_ci		cifs_dbg(VFS, "mounting share with read only caching. Ensure that the share will not be modified while in use.\n");
328362306a36Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_RO_CACHE;
328462306a36Sopenharmony_ci	} else if (ctx->cache_rw) {
328562306a36Sopenharmony_ci		cifs_dbg(VFS, "mounting share in single client RW caching mode. Ensure that no other systems will be accessing the share.\n");
328662306a36Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= (CIFS_MOUNT_RO_CACHE |
328762306a36Sopenharmony_ci					    CIFS_MOUNT_RW_CACHE);
328862306a36Sopenharmony_ci	}
328962306a36Sopenharmony_ci
329062306a36Sopenharmony_ci	if ((ctx->cifs_acl) && (ctx->dynperm))
329162306a36Sopenharmony_ci		cifs_dbg(VFS, "mount option dynperm ignored if cifsacl mount option supported\n");
329262306a36Sopenharmony_ci
329362306a36Sopenharmony_ci	if (ctx->prepath) {
329462306a36Sopenharmony_ci		cifs_sb->prepath = kstrdup(ctx->prepath, GFP_KERNEL);
329562306a36Sopenharmony_ci		if (cifs_sb->prepath == NULL)
329662306a36Sopenharmony_ci			return -ENOMEM;
329762306a36Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
329862306a36Sopenharmony_ci	}
329962306a36Sopenharmony_ci
330062306a36Sopenharmony_ci	return 0;
330162306a36Sopenharmony_ci}
330262306a36Sopenharmony_ci
330362306a36Sopenharmony_ci/* Release all succeed connections */
330462306a36Sopenharmony_civoid cifs_mount_put_conns(struct cifs_mount_ctx *mnt_ctx)
330562306a36Sopenharmony_ci{
330662306a36Sopenharmony_ci	int rc = 0;
330762306a36Sopenharmony_ci
330862306a36Sopenharmony_ci	if (mnt_ctx->tcon)
330962306a36Sopenharmony_ci		cifs_put_tcon(mnt_ctx->tcon);
331062306a36Sopenharmony_ci	else if (mnt_ctx->ses)
331162306a36Sopenharmony_ci		cifs_put_smb_ses(mnt_ctx->ses);
331262306a36Sopenharmony_ci	else if (mnt_ctx->server)
331362306a36Sopenharmony_ci		cifs_put_tcp_session(mnt_ctx->server, 0);
331462306a36Sopenharmony_ci	mnt_ctx->cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_POSIX_PATHS;
331562306a36Sopenharmony_ci	free_xid(mnt_ctx->xid);
331662306a36Sopenharmony_ci}
331762306a36Sopenharmony_ci
331862306a36Sopenharmony_ciint cifs_mount_get_session(struct cifs_mount_ctx *mnt_ctx)
331962306a36Sopenharmony_ci{
332062306a36Sopenharmony_ci	struct TCP_Server_Info *server = NULL;
332162306a36Sopenharmony_ci	struct smb3_fs_context *ctx;
332262306a36Sopenharmony_ci	struct cifs_ses *ses = NULL;
332362306a36Sopenharmony_ci	unsigned int xid;
332462306a36Sopenharmony_ci	int rc = 0;
332562306a36Sopenharmony_ci
332662306a36Sopenharmony_ci	xid = get_xid();
332762306a36Sopenharmony_ci
332862306a36Sopenharmony_ci	if (WARN_ON_ONCE(!mnt_ctx || !mnt_ctx->fs_ctx)) {
332962306a36Sopenharmony_ci		rc = -EINVAL;
333062306a36Sopenharmony_ci		goto out;
333162306a36Sopenharmony_ci	}
333262306a36Sopenharmony_ci	ctx = mnt_ctx->fs_ctx;
333362306a36Sopenharmony_ci
333462306a36Sopenharmony_ci	/* get a reference to a tcp session */
333562306a36Sopenharmony_ci	server = cifs_get_tcp_session(ctx, NULL);
333662306a36Sopenharmony_ci	if (IS_ERR(server)) {
333762306a36Sopenharmony_ci		rc = PTR_ERR(server);
333862306a36Sopenharmony_ci		server = NULL;
333962306a36Sopenharmony_ci		goto out;
334062306a36Sopenharmony_ci	}
334162306a36Sopenharmony_ci
334262306a36Sopenharmony_ci	/* get a reference to a SMB session */
334362306a36Sopenharmony_ci	ses = cifs_get_smb_ses(server, ctx);
334462306a36Sopenharmony_ci	if (IS_ERR(ses)) {
334562306a36Sopenharmony_ci		rc = PTR_ERR(ses);
334662306a36Sopenharmony_ci		ses = NULL;
334762306a36Sopenharmony_ci		goto out;
334862306a36Sopenharmony_ci	}
334962306a36Sopenharmony_ci
335062306a36Sopenharmony_ci	if ((ctx->persistent == true) && (!(ses->server->capabilities &
335162306a36Sopenharmony_ci					    SMB2_GLOBAL_CAP_PERSISTENT_HANDLES))) {
335262306a36Sopenharmony_ci		cifs_server_dbg(VFS, "persistent handles not supported by server\n");
335362306a36Sopenharmony_ci		rc = -EOPNOTSUPP;
335462306a36Sopenharmony_ci	}
335562306a36Sopenharmony_ci
335662306a36Sopenharmony_ciout:
335762306a36Sopenharmony_ci	mnt_ctx->xid = xid;
335862306a36Sopenharmony_ci	mnt_ctx->server = server;
335962306a36Sopenharmony_ci	mnt_ctx->ses = ses;
336062306a36Sopenharmony_ci	mnt_ctx->tcon = NULL;
336162306a36Sopenharmony_ci
336262306a36Sopenharmony_ci	return rc;
336362306a36Sopenharmony_ci}
336462306a36Sopenharmony_ci
336562306a36Sopenharmony_ciint cifs_mount_get_tcon(struct cifs_mount_ctx *mnt_ctx)
336662306a36Sopenharmony_ci{
336762306a36Sopenharmony_ci	struct TCP_Server_Info *server;
336862306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb;
336962306a36Sopenharmony_ci	struct smb3_fs_context *ctx;
337062306a36Sopenharmony_ci	struct cifs_tcon *tcon = NULL;
337162306a36Sopenharmony_ci	int rc = 0;
337262306a36Sopenharmony_ci
337362306a36Sopenharmony_ci	if (WARN_ON_ONCE(!mnt_ctx || !mnt_ctx->server || !mnt_ctx->ses || !mnt_ctx->fs_ctx ||
337462306a36Sopenharmony_ci			 !mnt_ctx->cifs_sb)) {
337562306a36Sopenharmony_ci		rc = -EINVAL;
337662306a36Sopenharmony_ci		goto out;
337762306a36Sopenharmony_ci	}
337862306a36Sopenharmony_ci	server = mnt_ctx->server;
337962306a36Sopenharmony_ci	ctx = mnt_ctx->fs_ctx;
338062306a36Sopenharmony_ci	cifs_sb = mnt_ctx->cifs_sb;
338162306a36Sopenharmony_ci
338262306a36Sopenharmony_ci	/* search for existing tcon to this server share */
338362306a36Sopenharmony_ci	tcon = cifs_get_tcon(mnt_ctx->ses, ctx);
338462306a36Sopenharmony_ci	if (IS_ERR(tcon)) {
338562306a36Sopenharmony_ci		rc = PTR_ERR(tcon);
338662306a36Sopenharmony_ci		tcon = NULL;
338762306a36Sopenharmony_ci		goto out;
338862306a36Sopenharmony_ci	}
338962306a36Sopenharmony_ci
339062306a36Sopenharmony_ci	/* if new SMB3.11 POSIX extensions are supported do not remap / and \ */
339162306a36Sopenharmony_ci	if (tcon->posix_extensions)
339262306a36Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_POSIX_PATHS;
339362306a36Sopenharmony_ci
339462306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
339562306a36Sopenharmony_ci	/* tell server which Unix caps we support */
339662306a36Sopenharmony_ci	if (cap_unix(tcon->ses)) {
339762306a36Sopenharmony_ci		/*
339862306a36Sopenharmony_ci		 * reset of caps checks mount to see if unix extensions disabled
339962306a36Sopenharmony_ci		 * for just this mount.
340062306a36Sopenharmony_ci		 */
340162306a36Sopenharmony_ci		reset_cifs_unix_caps(mnt_ctx->xid, tcon, cifs_sb, ctx);
340262306a36Sopenharmony_ci		spin_lock(&tcon->ses->server->srv_lock);
340362306a36Sopenharmony_ci		if ((tcon->ses->server->tcpStatus == CifsNeedReconnect) &&
340462306a36Sopenharmony_ci		    (le64_to_cpu(tcon->fsUnixInfo.Capability) &
340562306a36Sopenharmony_ci		     CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP)) {
340662306a36Sopenharmony_ci			spin_unlock(&tcon->ses->server->srv_lock);
340762306a36Sopenharmony_ci			rc = -EACCES;
340862306a36Sopenharmony_ci			goto out;
340962306a36Sopenharmony_ci		}
341062306a36Sopenharmony_ci		spin_unlock(&tcon->ses->server->srv_lock);
341162306a36Sopenharmony_ci	} else
341262306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
341362306a36Sopenharmony_ci		tcon->unix_ext = 0; /* server does not support them */
341462306a36Sopenharmony_ci
341562306a36Sopenharmony_ci	/* do not care if a following call succeed - informational */
341662306a36Sopenharmony_ci	if (!tcon->pipe && server->ops->qfs_tcon) {
341762306a36Sopenharmony_ci		server->ops->qfs_tcon(mnt_ctx->xid, tcon, cifs_sb);
341862306a36Sopenharmony_ci		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RO_CACHE) {
341962306a36Sopenharmony_ci			if (tcon->fsDevInfo.DeviceCharacteristics &
342062306a36Sopenharmony_ci			    cpu_to_le32(FILE_READ_ONLY_DEVICE))
342162306a36Sopenharmony_ci				cifs_dbg(VFS, "mounted to read only share\n");
342262306a36Sopenharmony_ci			else if ((cifs_sb->mnt_cifs_flags &
342362306a36Sopenharmony_ci				  CIFS_MOUNT_RW_CACHE) == 0)
342462306a36Sopenharmony_ci				cifs_dbg(VFS, "read only mount of RW share\n");
342562306a36Sopenharmony_ci			/* no need to log a RW mount of a typical RW share */
342662306a36Sopenharmony_ci		}
342762306a36Sopenharmony_ci	}
342862306a36Sopenharmony_ci
342962306a36Sopenharmony_ci	/*
343062306a36Sopenharmony_ci	 * Clamp the rsize/wsize mount arguments if they are too big for the server
343162306a36Sopenharmony_ci	 * and set the rsize/wsize to the negotiated values if not passed in by
343262306a36Sopenharmony_ci	 * the user on mount
343362306a36Sopenharmony_ci	 */
343462306a36Sopenharmony_ci	if ((cifs_sb->ctx->wsize == 0) ||
343562306a36Sopenharmony_ci	    (cifs_sb->ctx->wsize > server->ops->negotiate_wsize(tcon, ctx))) {
343662306a36Sopenharmony_ci		cifs_sb->ctx->wsize =
343762306a36Sopenharmony_ci			round_down(server->ops->negotiate_wsize(tcon, ctx), PAGE_SIZE);
343862306a36Sopenharmony_ci		/*
343962306a36Sopenharmony_ci		 * in the very unlikely event that the server sent a max write size under PAGE_SIZE,
344062306a36Sopenharmony_ci		 * (which would get rounded down to 0) then reset wsize to absolute minimum eg 4096
344162306a36Sopenharmony_ci		 */
344262306a36Sopenharmony_ci		if (cifs_sb->ctx->wsize == 0) {
344362306a36Sopenharmony_ci			cifs_sb->ctx->wsize = PAGE_SIZE;
344462306a36Sopenharmony_ci			cifs_dbg(VFS, "wsize too small, reset to minimum ie PAGE_SIZE, usually 4096\n");
344562306a36Sopenharmony_ci		}
344662306a36Sopenharmony_ci	}
344762306a36Sopenharmony_ci	if ((cifs_sb->ctx->rsize == 0) ||
344862306a36Sopenharmony_ci	    (cifs_sb->ctx->rsize > server->ops->negotiate_rsize(tcon, ctx)))
344962306a36Sopenharmony_ci		cifs_sb->ctx->rsize = server->ops->negotiate_rsize(tcon, ctx);
345062306a36Sopenharmony_ci
345162306a36Sopenharmony_ci	/*
345262306a36Sopenharmony_ci	 * The cookie is initialized from volume info returned above.
345362306a36Sopenharmony_ci	 * Inside cifs_fscache_get_super_cookie it checks
345462306a36Sopenharmony_ci	 * that we do not get super cookie twice.
345562306a36Sopenharmony_ci	 */
345662306a36Sopenharmony_ci	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_FSCACHE)
345762306a36Sopenharmony_ci		cifs_fscache_get_super_cookie(tcon);
345862306a36Sopenharmony_ci
345962306a36Sopenharmony_ciout:
346062306a36Sopenharmony_ci	mnt_ctx->tcon = tcon;
346162306a36Sopenharmony_ci	return rc;
346262306a36Sopenharmony_ci}
346362306a36Sopenharmony_ci
346462306a36Sopenharmony_cistatic int mount_setup_tlink(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
346562306a36Sopenharmony_ci			     struct cifs_tcon *tcon)
346662306a36Sopenharmony_ci{
346762306a36Sopenharmony_ci	struct tcon_link *tlink;
346862306a36Sopenharmony_ci
346962306a36Sopenharmony_ci	/* hang the tcon off of the superblock */
347062306a36Sopenharmony_ci	tlink = kzalloc(sizeof(*tlink), GFP_KERNEL);
347162306a36Sopenharmony_ci	if (tlink == NULL)
347262306a36Sopenharmony_ci		return -ENOMEM;
347362306a36Sopenharmony_ci
347462306a36Sopenharmony_ci	tlink->tl_uid = ses->linux_uid;
347562306a36Sopenharmony_ci	tlink->tl_tcon = tcon;
347662306a36Sopenharmony_ci	tlink->tl_time = jiffies;
347762306a36Sopenharmony_ci	set_bit(TCON_LINK_MASTER, &tlink->tl_flags);
347862306a36Sopenharmony_ci	set_bit(TCON_LINK_IN_TREE, &tlink->tl_flags);
347962306a36Sopenharmony_ci
348062306a36Sopenharmony_ci	cifs_sb->master_tlink = tlink;
348162306a36Sopenharmony_ci	spin_lock(&cifs_sb->tlink_tree_lock);
348262306a36Sopenharmony_ci	tlink_rb_insert(&cifs_sb->tlink_tree, tlink);
348362306a36Sopenharmony_ci	spin_unlock(&cifs_sb->tlink_tree_lock);
348462306a36Sopenharmony_ci
348562306a36Sopenharmony_ci	queue_delayed_work(cifsiod_wq, &cifs_sb->prune_tlinks,
348662306a36Sopenharmony_ci				TLINK_IDLE_EXPIRE);
348762306a36Sopenharmony_ci	return 0;
348862306a36Sopenharmony_ci}
348962306a36Sopenharmony_ci
349062306a36Sopenharmony_cistatic int
349162306a36Sopenharmony_cicifs_are_all_path_components_accessible(struct TCP_Server_Info *server,
349262306a36Sopenharmony_ci					unsigned int xid,
349362306a36Sopenharmony_ci					struct cifs_tcon *tcon,
349462306a36Sopenharmony_ci					struct cifs_sb_info *cifs_sb,
349562306a36Sopenharmony_ci					char *full_path,
349662306a36Sopenharmony_ci					int added_treename)
349762306a36Sopenharmony_ci{
349862306a36Sopenharmony_ci	int rc;
349962306a36Sopenharmony_ci	char *s;
350062306a36Sopenharmony_ci	char sep, tmp;
350162306a36Sopenharmony_ci	int skip = added_treename ? 1 : 0;
350262306a36Sopenharmony_ci
350362306a36Sopenharmony_ci	sep = CIFS_DIR_SEP(cifs_sb);
350462306a36Sopenharmony_ci	s = full_path;
350562306a36Sopenharmony_ci
350662306a36Sopenharmony_ci	rc = server->ops->is_path_accessible(xid, tcon, cifs_sb, "");
350762306a36Sopenharmony_ci	while (rc == 0) {
350862306a36Sopenharmony_ci		/* skip separators */
350962306a36Sopenharmony_ci		while (*s == sep)
351062306a36Sopenharmony_ci			s++;
351162306a36Sopenharmony_ci		if (!*s)
351262306a36Sopenharmony_ci			break;
351362306a36Sopenharmony_ci		/* next separator */
351462306a36Sopenharmony_ci		while (*s && *s != sep)
351562306a36Sopenharmony_ci			s++;
351662306a36Sopenharmony_ci		/*
351762306a36Sopenharmony_ci		 * if the treename is added, we then have to skip the first
351862306a36Sopenharmony_ci		 * part within the separators
351962306a36Sopenharmony_ci		 */
352062306a36Sopenharmony_ci		if (skip) {
352162306a36Sopenharmony_ci			skip = 0;
352262306a36Sopenharmony_ci			continue;
352362306a36Sopenharmony_ci		}
352462306a36Sopenharmony_ci		/*
352562306a36Sopenharmony_ci		 * temporarily null-terminate the path at the end of
352662306a36Sopenharmony_ci		 * the current component
352762306a36Sopenharmony_ci		 */
352862306a36Sopenharmony_ci		tmp = *s;
352962306a36Sopenharmony_ci		*s = 0;
353062306a36Sopenharmony_ci		rc = server->ops->is_path_accessible(xid, tcon, cifs_sb,
353162306a36Sopenharmony_ci						     full_path);
353262306a36Sopenharmony_ci		*s = tmp;
353362306a36Sopenharmony_ci	}
353462306a36Sopenharmony_ci	return rc;
353562306a36Sopenharmony_ci}
353662306a36Sopenharmony_ci
353762306a36Sopenharmony_ci/*
353862306a36Sopenharmony_ci * Check if path is remote (i.e. a DFS share).
353962306a36Sopenharmony_ci *
354062306a36Sopenharmony_ci * Return -EREMOTE if it is, otherwise 0 or -errno.
354162306a36Sopenharmony_ci */
354262306a36Sopenharmony_ciint cifs_is_path_remote(struct cifs_mount_ctx *mnt_ctx)
354362306a36Sopenharmony_ci{
354462306a36Sopenharmony_ci	int rc;
354562306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
354662306a36Sopenharmony_ci	struct TCP_Server_Info *server = mnt_ctx->server;
354762306a36Sopenharmony_ci	unsigned int xid = mnt_ctx->xid;
354862306a36Sopenharmony_ci	struct cifs_tcon *tcon = mnt_ctx->tcon;
354962306a36Sopenharmony_ci	struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
355062306a36Sopenharmony_ci	char *full_path;
355162306a36Sopenharmony_ci
355262306a36Sopenharmony_ci	if (!server->ops->is_path_accessible)
355362306a36Sopenharmony_ci		return -EOPNOTSUPP;
355462306a36Sopenharmony_ci
355562306a36Sopenharmony_ci	/*
355662306a36Sopenharmony_ci	 * cifs_build_path_to_root works only when we have a valid tcon
355762306a36Sopenharmony_ci	 */
355862306a36Sopenharmony_ci	full_path = cifs_build_path_to_root(ctx, cifs_sb, tcon,
355962306a36Sopenharmony_ci					    tcon->Flags & SMB_SHARE_IS_IN_DFS);
356062306a36Sopenharmony_ci	if (full_path == NULL)
356162306a36Sopenharmony_ci		return -ENOMEM;
356262306a36Sopenharmony_ci
356362306a36Sopenharmony_ci	cifs_dbg(FYI, "%s: full_path: %s\n", __func__, full_path);
356462306a36Sopenharmony_ci
356562306a36Sopenharmony_ci	rc = server->ops->is_path_accessible(xid, tcon, cifs_sb,
356662306a36Sopenharmony_ci					     full_path);
356762306a36Sopenharmony_ci	if (rc != 0 && rc != -EREMOTE)
356862306a36Sopenharmony_ci		goto out;
356962306a36Sopenharmony_ci
357062306a36Sopenharmony_ci	if (rc != -EREMOTE) {
357162306a36Sopenharmony_ci		rc = cifs_are_all_path_components_accessible(server, xid, tcon,
357262306a36Sopenharmony_ci			cifs_sb, full_path, tcon->Flags & SMB_SHARE_IS_IN_DFS);
357362306a36Sopenharmony_ci		if (rc != 0) {
357462306a36Sopenharmony_ci			cifs_server_dbg(VFS, "cannot query dirs between root and final path, enabling CIFS_MOUNT_USE_PREFIX_PATH\n");
357562306a36Sopenharmony_ci			cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
357662306a36Sopenharmony_ci			rc = 0;
357762306a36Sopenharmony_ci		}
357862306a36Sopenharmony_ci	}
357962306a36Sopenharmony_ci
358062306a36Sopenharmony_ciout:
358162306a36Sopenharmony_ci	kfree(full_path);
358262306a36Sopenharmony_ci	return rc;
358362306a36Sopenharmony_ci}
358462306a36Sopenharmony_ci
358562306a36Sopenharmony_ci#ifdef CONFIG_CIFS_DFS_UPCALL
358662306a36Sopenharmony_ciint cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
358762306a36Sopenharmony_ci{
358862306a36Sopenharmony_ci	struct cifs_mount_ctx mnt_ctx = { .cifs_sb = cifs_sb, .fs_ctx = ctx, };
358962306a36Sopenharmony_ci	bool isdfs;
359062306a36Sopenharmony_ci	int rc;
359162306a36Sopenharmony_ci
359262306a36Sopenharmony_ci	INIT_LIST_HEAD(&mnt_ctx.dfs_ses_list);
359362306a36Sopenharmony_ci
359462306a36Sopenharmony_ci	rc = dfs_mount_share(&mnt_ctx, &isdfs);
359562306a36Sopenharmony_ci	if (rc)
359662306a36Sopenharmony_ci		goto error;
359762306a36Sopenharmony_ci	if (!isdfs)
359862306a36Sopenharmony_ci		goto out;
359962306a36Sopenharmony_ci
360062306a36Sopenharmony_ci	/*
360162306a36Sopenharmony_ci	 * After reconnecting to a different server, unique ids won't match anymore, so we disable
360262306a36Sopenharmony_ci	 * serverino. This prevents dentry revalidation to think the dentry are stale (ESTALE).
360362306a36Sopenharmony_ci	 */
360462306a36Sopenharmony_ci	cifs_autodisable_serverino(cifs_sb);
360562306a36Sopenharmony_ci	/*
360662306a36Sopenharmony_ci	 * Force the use of prefix path to support failover on DFS paths that resolve to targets
360762306a36Sopenharmony_ci	 * that have different prefix paths.
360862306a36Sopenharmony_ci	 */
360962306a36Sopenharmony_ci	cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
361062306a36Sopenharmony_ci	kfree(cifs_sb->prepath);
361162306a36Sopenharmony_ci	cifs_sb->prepath = ctx->prepath;
361262306a36Sopenharmony_ci	ctx->prepath = NULL;
361362306a36Sopenharmony_ci
361462306a36Sopenharmony_ciout:
361562306a36Sopenharmony_ci	cifs_try_adding_channels(mnt_ctx.ses);
361662306a36Sopenharmony_ci	rc = mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon);
361762306a36Sopenharmony_ci	if (rc)
361862306a36Sopenharmony_ci		goto error;
361962306a36Sopenharmony_ci
362062306a36Sopenharmony_ci	free_xid(mnt_ctx.xid);
362162306a36Sopenharmony_ci	return rc;
362262306a36Sopenharmony_ci
362362306a36Sopenharmony_cierror:
362462306a36Sopenharmony_ci	dfs_put_root_smb_sessions(&mnt_ctx.dfs_ses_list);
362562306a36Sopenharmony_ci	cifs_mount_put_conns(&mnt_ctx);
362662306a36Sopenharmony_ci	return rc;
362762306a36Sopenharmony_ci}
362862306a36Sopenharmony_ci#else
362962306a36Sopenharmony_ciint cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
363062306a36Sopenharmony_ci{
363162306a36Sopenharmony_ci	int rc = 0;
363262306a36Sopenharmony_ci	struct cifs_mount_ctx mnt_ctx = { .cifs_sb = cifs_sb, .fs_ctx = ctx, };
363362306a36Sopenharmony_ci
363462306a36Sopenharmony_ci	rc = cifs_mount_get_session(&mnt_ctx);
363562306a36Sopenharmony_ci	if (rc)
363662306a36Sopenharmony_ci		goto error;
363762306a36Sopenharmony_ci
363862306a36Sopenharmony_ci	rc = cifs_mount_get_tcon(&mnt_ctx);
363962306a36Sopenharmony_ci	if (rc)
364062306a36Sopenharmony_ci		goto error;
364162306a36Sopenharmony_ci
364262306a36Sopenharmony_ci	rc = cifs_is_path_remote(&mnt_ctx);
364362306a36Sopenharmony_ci	if (rc == -EREMOTE)
364462306a36Sopenharmony_ci		rc = -EOPNOTSUPP;
364562306a36Sopenharmony_ci	if (rc)
364662306a36Sopenharmony_ci		goto error;
364762306a36Sopenharmony_ci
364862306a36Sopenharmony_ci	rc = mount_setup_tlink(cifs_sb, mnt_ctx.ses, mnt_ctx.tcon);
364962306a36Sopenharmony_ci	if (rc)
365062306a36Sopenharmony_ci		goto error;
365162306a36Sopenharmony_ci
365262306a36Sopenharmony_ci	free_xid(mnt_ctx.xid);
365362306a36Sopenharmony_ci	return rc;
365462306a36Sopenharmony_ci
365562306a36Sopenharmony_cierror:
365662306a36Sopenharmony_ci	cifs_mount_put_conns(&mnt_ctx);
365762306a36Sopenharmony_ci	return rc;
365862306a36Sopenharmony_ci}
365962306a36Sopenharmony_ci#endif
366062306a36Sopenharmony_ci
366162306a36Sopenharmony_ci/*
366262306a36Sopenharmony_ci * Issue a TREE_CONNECT request.
366362306a36Sopenharmony_ci */
366462306a36Sopenharmony_ciint
366562306a36Sopenharmony_ciCIFSTCon(const unsigned int xid, struct cifs_ses *ses,
366662306a36Sopenharmony_ci	 const char *tree, struct cifs_tcon *tcon,
366762306a36Sopenharmony_ci	 const struct nls_table *nls_codepage)
366862306a36Sopenharmony_ci{
366962306a36Sopenharmony_ci	struct smb_hdr *smb_buffer;
367062306a36Sopenharmony_ci	struct smb_hdr *smb_buffer_response;
367162306a36Sopenharmony_ci	TCONX_REQ *pSMB;
367262306a36Sopenharmony_ci	TCONX_RSP *pSMBr;
367362306a36Sopenharmony_ci	unsigned char *bcc_ptr;
367462306a36Sopenharmony_ci	int rc = 0;
367562306a36Sopenharmony_ci	int length;
367662306a36Sopenharmony_ci	__u16 bytes_left, count;
367762306a36Sopenharmony_ci
367862306a36Sopenharmony_ci	if (ses == NULL)
367962306a36Sopenharmony_ci		return -EIO;
368062306a36Sopenharmony_ci
368162306a36Sopenharmony_ci	smb_buffer = cifs_buf_get();
368262306a36Sopenharmony_ci	if (smb_buffer == NULL)
368362306a36Sopenharmony_ci		return -ENOMEM;
368462306a36Sopenharmony_ci
368562306a36Sopenharmony_ci	smb_buffer_response = smb_buffer;
368662306a36Sopenharmony_ci
368762306a36Sopenharmony_ci	header_assemble(smb_buffer, SMB_COM_TREE_CONNECT_ANDX,
368862306a36Sopenharmony_ci			NULL /*no tid */ , 4 /*wct */ );
368962306a36Sopenharmony_ci
369062306a36Sopenharmony_ci	smb_buffer->Mid = get_next_mid(ses->server);
369162306a36Sopenharmony_ci	smb_buffer->Uid = ses->Suid;
369262306a36Sopenharmony_ci	pSMB = (TCONX_REQ *) smb_buffer;
369362306a36Sopenharmony_ci	pSMBr = (TCONX_RSP *) smb_buffer_response;
369462306a36Sopenharmony_ci
369562306a36Sopenharmony_ci	pSMB->AndXCommand = 0xFF;
369662306a36Sopenharmony_ci	pSMB->Flags = cpu_to_le16(TCON_EXTENDED_SECINFO);
369762306a36Sopenharmony_ci	bcc_ptr = &pSMB->Password[0];
369862306a36Sopenharmony_ci
369962306a36Sopenharmony_ci	pSMB->PasswordLength = cpu_to_le16(1);	/* minimum */
370062306a36Sopenharmony_ci	*bcc_ptr = 0; /* password is null byte */
370162306a36Sopenharmony_ci	bcc_ptr++;              /* skip password */
370262306a36Sopenharmony_ci	/* already aligned so no need to do it below */
370362306a36Sopenharmony_ci
370462306a36Sopenharmony_ci	if (ses->server->sign)
370562306a36Sopenharmony_ci		smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
370662306a36Sopenharmony_ci
370762306a36Sopenharmony_ci	if (ses->capabilities & CAP_STATUS32) {
370862306a36Sopenharmony_ci		smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS;
370962306a36Sopenharmony_ci	}
371062306a36Sopenharmony_ci	if (ses->capabilities & CAP_DFS) {
371162306a36Sopenharmony_ci		smb_buffer->Flags2 |= SMBFLG2_DFS;
371262306a36Sopenharmony_ci	}
371362306a36Sopenharmony_ci	if (ses->capabilities & CAP_UNICODE) {
371462306a36Sopenharmony_ci		smb_buffer->Flags2 |= SMBFLG2_UNICODE;
371562306a36Sopenharmony_ci		length =
371662306a36Sopenharmony_ci		    cifs_strtoUTF16((__le16 *) bcc_ptr, tree,
371762306a36Sopenharmony_ci			6 /* max utf8 char length in bytes */ *
371862306a36Sopenharmony_ci			(/* server len*/ + 256 /* share len */), nls_codepage);
371962306a36Sopenharmony_ci		bcc_ptr += 2 * length;	/* convert num 16 bit words to bytes */
372062306a36Sopenharmony_ci		bcc_ptr += 2;	/* skip trailing null */
372162306a36Sopenharmony_ci	} else {		/* ASCII */
372262306a36Sopenharmony_ci		strcpy(bcc_ptr, tree);
372362306a36Sopenharmony_ci		bcc_ptr += strlen(tree) + 1;
372462306a36Sopenharmony_ci	}
372562306a36Sopenharmony_ci	strcpy(bcc_ptr, "?????");
372662306a36Sopenharmony_ci	bcc_ptr += strlen("?????");
372762306a36Sopenharmony_ci	bcc_ptr += 1;
372862306a36Sopenharmony_ci	count = bcc_ptr - &pSMB->Password[0];
372962306a36Sopenharmony_ci	be32_add_cpu(&pSMB->hdr.smb_buf_length, count);
373062306a36Sopenharmony_ci	pSMB->ByteCount = cpu_to_le16(count);
373162306a36Sopenharmony_ci
373262306a36Sopenharmony_ci	rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response, &length,
373362306a36Sopenharmony_ci			 0);
373462306a36Sopenharmony_ci
373562306a36Sopenharmony_ci	/* above now done in SendReceive */
373662306a36Sopenharmony_ci	if (rc == 0) {
373762306a36Sopenharmony_ci		bool is_unicode;
373862306a36Sopenharmony_ci
373962306a36Sopenharmony_ci		tcon->tid = smb_buffer_response->Tid;
374062306a36Sopenharmony_ci		bcc_ptr = pByteArea(smb_buffer_response);
374162306a36Sopenharmony_ci		bytes_left = get_bcc(smb_buffer_response);
374262306a36Sopenharmony_ci		length = strnlen(bcc_ptr, bytes_left - 2);
374362306a36Sopenharmony_ci		if (smb_buffer->Flags2 & SMBFLG2_UNICODE)
374462306a36Sopenharmony_ci			is_unicode = true;
374562306a36Sopenharmony_ci		else
374662306a36Sopenharmony_ci			is_unicode = false;
374762306a36Sopenharmony_ci
374862306a36Sopenharmony_ci
374962306a36Sopenharmony_ci		/* skip service field (NB: this field is always ASCII) */
375062306a36Sopenharmony_ci		if (length == 3) {
375162306a36Sopenharmony_ci			if ((bcc_ptr[0] == 'I') && (bcc_ptr[1] == 'P') &&
375262306a36Sopenharmony_ci			    (bcc_ptr[2] == 'C')) {
375362306a36Sopenharmony_ci				cifs_dbg(FYI, "IPC connection\n");
375462306a36Sopenharmony_ci				tcon->ipc = true;
375562306a36Sopenharmony_ci				tcon->pipe = true;
375662306a36Sopenharmony_ci			}
375762306a36Sopenharmony_ci		} else if (length == 2) {
375862306a36Sopenharmony_ci			if ((bcc_ptr[0] == 'A') && (bcc_ptr[1] == ':')) {
375962306a36Sopenharmony_ci				/* the most common case */
376062306a36Sopenharmony_ci				cifs_dbg(FYI, "disk share connection\n");
376162306a36Sopenharmony_ci			}
376262306a36Sopenharmony_ci		}
376362306a36Sopenharmony_ci		bcc_ptr += length + 1;
376462306a36Sopenharmony_ci		bytes_left -= (length + 1);
376562306a36Sopenharmony_ci		strscpy(tcon->tree_name, tree, sizeof(tcon->tree_name));
376662306a36Sopenharmony_ci
376762306a36Sopenharmony_ci		/* mostly informational -- no need to fail on error here */
376862306a36Sopenharmony_ci		kfree(tcon->nativeFileSystem);
376962306a36Sopenharmony_ci		tcon->nativeFileSystem = cifs_strndup_from_utf16(bcc_ptr,
377062306a36Sopenharmony_ci						      bytes_left, is_unicode,
377162306a36Sopenharmony_ci						      nls_codepage);
377262306a36Sopenharmony_ci
377362306a36Sopenharmony_ci		cifs_dbg(FYI, "nativeFileSystem=%s\n", tcon->nativeFileSystem);
377462306a36Sopenharmony_ci
377562306a36Sopenharmony_ci		if ((smb_buffer_response->WordCount == 3) ||
377662306a36Sopenharmony_ci			 (smb_buffer_response->WordCount == 7))
377762306a36Sopenharmony_ci			/* field is in same location */
377862306a36Sopenharmony_ci			tcon->Flags = le16_to_cpu(pSMBr->OptionalSupport);
377962306a36Sopenharmony_ci		else
378062306a36Sopenharmony_ci			tcon->Flags = 0;
378162306a36Sopenharmony_ci		cifs_dbg(FYI, "Tcon flags: 0x%x\n", tcon->Flags);
378262306a36Sopenharmony_ci	}
378362306a36Sopenharmony_ci
378462306a36Sopenharmony_ci	cifs_buf_release(smb_buffer);
378562306a36Sopenharmony_ci	return rc;
378662306a36Sopenharmony_ci}
378762306a36Sopenharmony_ci
378862306a36Sopenharmony_cistatic void delayed_free(struct rcu_head *p)
378962306a36Sopenharmony_ci{
379062306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = container_of(p, struct cifs_sb_info, rcu);
379162306a36Sopenharmony_ci
379262306a36Sopenharmony_ci	unload_nls(cifs_sb->local_nls);
379362306a36Sopenharmony_ci	smb3_cleanup_fs_context(cifs_sb->ctx);
379462306a36Sopenharmony_ci	kfree(cifs_sb);
379562306a36Sopenharmony_ci}
379662306a36Sopenharmony_ci
379762306a36Sopenharmony_civoid
379862306a36Sopenharmony_cicifs_umount(struct cifs_sb_info *cifs_sb)
379962306a36Sopenharmony_ci{
380062306a36Sopenharmony_ci	struct rb_root *root = &cifs_sb->tlink_tree;
380162306a36Sopenharmony_ci	struct rb_node *node;
380262306a36Sopenharmony_ci	struct tcon_link *tlink;
380362306a36Sopenharmony_ci
380462306a36Sopenharmony_ci	cancel_delayed_work_sync(&cifs_sb->prune_tlinks);
380562306a36Sopenharmony_ci
380662306a36Sopenharmony_ci	spin_lock(&cifs_sb->tlink_tree_lock);
380762306a36Sopenharmony_ci	while ((node = rb_first(root))) {
380862306a36Sopenharmony_ci		tlink = rb_entry(node, struct tcon_link, tl_rbnode);
380962306a36Sopenharmony_ci		cifs_get_tlink(tlink);
381062306a36Sopenharmony_ci		clear_bit(TCON_LINK_IN_TREE, &tlink->tl_flags);
381162306a36Sopenharmony_ci		rb_erase(node, root);
381262306a36Sopenharmony_ci
381362306a36Sopenharmony_ci		spin_unlock(&cifs_sb->tlink_tree_lock);
381462306a36Sopenharmony_ci		cifs_put_tlink(tlink);
381562306a36Sopenharmony_ci		spin_lock(&cifs_sb->tlink_tree_lock);
381662306a36Sopenharmony_ci	}
381762306a36Sopenharmony_ci	spin_unlock(&cifs_sb->tlink_tree_lock);
381862306a36Sopenharmony_ci
381962306a36Sopenharmony_ci	kfree(cifs_sb->prepath);
382062306a36Sopenharmony_ci	call_rcu(&cifs_sb->rcu, delayed_free);
382162306a36Sopenharmony_ci}
382262306a36Sopenharmony_ci
382362306a36Sopenharmony_ciint
382462306a36Sopenharmony_cicifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses,
382562306a36Sopenharmony_ci			struct TCP_Server_Info *server)
382662306a36Sopenharmony_ci{
382762306a36Sopenharmony_ci	int rc = 0;
382862306a36Sopenharmony_ci
382962306a36Sopenharmony_ci	if (!server->ops->need_neg || !server->ops->negotiate)
383062306a36Sopenharmony_ci		return -ENOSYS;
383162306a36Sopenharmony_ci
383262306a36Sopenharmony_ci	/* only send once per connect */
383362306a36Sopenharmony_ci	spin_lock(&server->srv_lock);
383462306a36Sopenharmony_ci	if (server->tcpStatus != CifsGood &&
383562306a36Sopenharmony_ci	    server->tcpStatus != CifsNew &&
383662306a36Sopenharmony_ci	    server->tcpStatus != CifsNeedNegotiate) {
383762306a36Sopenharmony_ci		spin_unlock(&server->srv_lock);
383862306a36Sopenharmony_ci		return -EHOSTDOWN;
383962306a36Sopenharmony_ci	}
384062306a36Sopenharmony_ci
384162306a36Sopenharmony_ci	if (!server->ops->need_neg(server) &&
384262306a36Sopenharmony_ci	    server->tcpStatus == CifsGood) {
384362306a36Sopenharmony_ci		spin_unlock(&server->srv_lock);
384462306a36Sopenharmony_ci		return 0;
384562306a36Sopenharmony_ci	}
384662306a36Sopenharmony_ci
384762306a36Sopenharmony_ci	server->tcpStatus = CifsInNegotiate;
384862306a36Sopenharmony_ci	spin_unlock(&server->srv_lock);
384962306a36Sopenharmony_ci
385062306a36Sopenharmony_ci	rc = server->ops->negotiate(xid, ses, server);
385162306a36Sopenharmony_ci	if (rc == 0) {
385262306a36Sopenharmony_ci		spin_lock(&server->srv_lock);
385362306a36Sopenharmony_ci		if (server->tcpStatus == CifsInNegotiate)
385462306a36Sopenharmony_ci			server->tcpStatus = CifsGood;
385562306a36Sopenharmony_ci		else
385662306a36Sopenharmony_ci			rc = -EHOSTDOWN;
385762306a36Sopenharmony_ci		spin_unlock(&server->srv_lock);
385862306a36Sopenharmony_ci	} else {
385962306a36Sopenharmony_ci		spin_lock(&server->srv_lock);
386062306a36Sopenharmony_ci		if (server->tcpStatus == CifsInNegotiate)
386162306a36Sopenharmony_ci			server->tcpStatus = CifsNeedNegotiate;
386262306a36Sopenharmony_ci		spin_unlock(&server->srv_lock);
386362306a36Sopenharmony_ci	}
386462306a36Sopenharmony_ci
386562306a36Sopenharmony_ci	return rc;
386662306a36Sopenharmony_ci}
386762306a36Sopenharmony_ci
386862306a36Sopenharmony_ciint
386962306a36Sopenharmony_cicifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
387062306a36Sopenharmony_ci		   struct TCP_Server_Info *server,
387162306a36Sopenharmony_ci		   struct nls_table *nls_info)
387262306a36Sopenharmony_ci{
387362306a36Sopenharmony_ci	int rc = -ENOSYS;
387462306a36Sopenharmony_ci	struct TCP_Server_Info *pserver = SERVER_IS_CHAN(server) ? server->primary_server : server;
387562306a36Sopenharmony_ci	struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&pserver->dstaddr;
387662306a36Sopenharmony_ci	struct sockaddr_in *addr = (struct sockaddr_in *)&pserver->dstaddr;
387762306a36Sopenharmony_ci	bool is_binding = false;
387862306a36Sopenharmony_ci
387962306a36Sopenharmony_ci	spin_lock(&ses->ses_lock);
388062306a36Sopenharmony_ci	cifs_dbg(FYI, "%s: channel connect bitmap: 0x%lx\n",
388162306a36Sopenharmony_ci		 __func__, ses->chans_need_reconnect);
388262306a36Sopenharmony_ci
388362306a36Sopenharmony_ci	if (ses->ses_status != SES_GOOD &&
388462306a36Sopenharmony_ci	    ses->ses_status != SES_NEW &&
388562306a36Sopenharmony_ci	    ses->ses_status != SES_NEED_RECON) {
388662306a36Sopenharmony_ci		spin_unlock(&ses->ses_lock);
388762306a36Sopenharmony_ci		return -EHOSTDOWN;
388862306a36Sopenharmony_ci	}
388962306a36Sopenharmony_ci
389062306a36Sopenharmony_ci	/* only send once per connect */
389162306a36Sopenharmony_ci	spin_lock(&ses->chan_lock);
389262306a36Sopenharmony_ci	if (CIFS_ALL_CHANS_GOOD(ses)) {
389362306a36Sopenharmony_ci		if (ses->ses_status == SES_NEED_RECON)
389462306a36Sopenharmony_ci			ses->ses_status = SES_GOOD;
389562306a36Sopenharmony_ci		spin_unlock(&ses->chan_lock);
389662306a36Sopenharmony_ci		spin_unlock(&ses->ses_lock);
389762306a36Sopenharmony_ci		return 0;
389862306a36Sopenharmony_ci	}
389962306a36Sopenharmony_ci
390062306a36Sopenharmony_ci	cifs_chan_set_in_reconnect(ses, server);
390162306a36Sopenharmony_ci	is_binding = !CIFS_ALL_CHANS_NEED_RECONNECT(ses);
390262306a36Sopenharmony_ci	spin_unlock(&ses->chan_lock);
390362306a36Sopenharmony_ci
390462306a36Sopenharmony_ci	if (!is_binding) {
390562306a36Sopenharmony_ci		ses->ses_status = SES_IN_SETUP;
390662306a36Sopenharmony_ci
390762306a36Sopenharmony_ci		/* force iface_list refresh */
390862306a36Sopenharmony_ci		ses->iface_last_update = 0;
390962306a36Sopenharmony_ci	}
391062306a36Sopenharmony_ci	spin_unlock(&ses->ses_lock);
391162306a36Sopenharmony_ci
391262306a36Sopenharmony_ci	/* update ses ip_addr only for primary chan */
391362306a36Sopenharmony_ci	if (server == pserver) {
391462306a36Sopenharmony_ci		if (server->dstaddr.ss_family == AF_INET6)
391562306a36Sopenharmony_ci			scnprintf(ses->ip_addr, sizeof(ses->ip_addr), "%pI6", &addr6->sin6_addr);
391662306a36Sopenharmony_ci		else
391762306a36Sopenharmony_ci			scnprintf(ses->ip_addr, sizeof(ses->ip_addr), "%pI4", &addr->sin_addr);
391862306a36Sopenharmony_ci	}
391962306a36Sopenharmony_ci
392062306a36Sopenharmony_ci	if (!is_binding) {
392162306a36Sopenharmony_ci		ses->capabilities = server->capabilities;
392262306a36Sopenharmony_ci		if (!linuxExtEnabled)
392362306a36Sopenharmony_ci			ses->capabilities &= (~server->vals->cap_unix);
392462306a36Sopenharmony_ci
392562306a36Sopenharmony_ci		if (ses->auth_key.response) {
392662306a36Sopenharmony_ci			cifs_dbg(FYI, "Free previous auth_key.response = %p\n",
392762306a36Sopenharmony_ci				 ses->auth_key.response);
392862306a36Sopenharmony_ci			kfree_sensitive(ses->auth_key.response);
392962306a36Sopenharmony_ci			ses->auth_key.response = NULL;
393062306a36Sopenharmony_ci			ses->auth_key.len = 0;
393162306a36Sopenharmony_ci		}
393262306a36Sopenharmony_ci	}
393362306a36Sopenharmony_ci
393462306a36Sopenharmony_ci	cifs_dbg(FYI, "Security Mode: 0x%x Capabilities: 0x%x TimeAdjust: %d\n",
393562306a36Sopenharmony_ci		 server->sec_mode, server->capabilities, server->timeAdj);
393662306a36Sopenharmony_ci
393762306a36Sopenharmony_ci	if (server->ops->sess_setup)
393862306a36Sopenharmony_ci		rc = server->ops->sess_setup(xid, ses, server, nls_info);
393962306a36Sopenharmony_ci
394062306a36Sopenharmony_ci	if (rc) {
394162306a36Sopenharmony_ci		cifs_server_dbg(VFS, "Send error in SessSetup = %d\n", rc);
394262306a36Sopenharmony_ci		spin_lock(&ses->ses_lock);
394362306a36Sopenharmony_ci		if (ses->ses_status == SES_IN_SETUP)
394462306a36Sopenharmony_ci			ses->ses_status = SES_NEED_RECON;
394562306a36Sopenharmony_ci		spin_lock(&ses->chan_lock);
394662306a36Sopenharmony_ci		cifs_chan_clear_in_reconnect(ses, server);
394762306a36Sopenharmony_ci		spin_unlock(&ses->chan_lock);
394862306a36Sopenharmony_ci		spin_unlock(&ses->ses_lock);
394962306a36Sopenharmony_ci	} else {
395062306a36Sopenharmony_ci		spin_lock(&ses->ses_lock);
395162306a36Sopenharmony_ci		if (ses->ses_status == SES_IN_SETUP)
395262306a36Sopenharmony_ci			ses->ses_status = SES_GOOD;
395362306a36Sopenharmony_ci		spin_lock(&ses->chan_lock);
395462306a36Sopenharmony_ci		cifs_chan_clear_in_reconnect(ses, server);
395562306a36Sopenharmony_ci		cifs_chan_clear_need_reconnect(ses, server);
395662306a36Sopenharmony_ci		spin_unlock(&ses->chan_lock);
395762306a36Sopenharmony_ci		spin_unlock(&ses->ses_lock);
395862306a36Sopenharmony_ci	}
395962306a36Sopenharmony_ci
396062306a36Sopenharmony_ci	return rc;
396162306a36Sopenharmony_ci}
396262306a36Sopenharmony_ci
396362306a36Sopenharmony_cistatic int
396462306a36Sopenharmony_cicifs_set_vol_auth(struct smb3_fs_context *ctx, struct cifs_ses *ses)
396562306a36Sopenharmony_ci{
396662306a36Sopenharmony_ci	ctx->sectype = ses->sectype;
396762306a36Sopenharmony_ci
396862306a36Sopenharmony_ci	/* krb5 is special, since we don't need username or pw */
396962306a36Sopenharmony_ci	if (ctx->sectype == Kerberos)
397062306a36Sopenharmony_ci		return 0;
397162306a36Sopenharmony_ci
397262306a36Sopenharmony_ci	return cifs_set_cifscreds(ctx, ses);
397362306a36Sopenharmony_ci}
397462306a36Sopenharmony_ci
397562306a36Sopenharmony_cistatic struct cifs_tcon *
397662306a36Sopenharmony_cicifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)
397762306a36Sopenharmony_ci{
397862306a36Sopenharmony_ci	int rc;
397962306a36Sopenharmony_ci	struct cifs_tcon *master_tcon = cifs_sb_master_tcon(cifs_sb);
398062306a36Sopenharmony_ci	struct cifs_ses *ses;
398162306a36Sopenharmony_ci	struct cifs_tcon *tcon = NULL;
398262306a36Sopenharmony_ci	struct smb3_fs_context *ctx;
398362306a36Sopenharmony_ci
398462306a36Sopenharmony_ci	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
398562306a36Sopenharmony_ci	if (ctx == NULL)
398662306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
398762306a36Sopenharmony_ci
398862306a36Sopenharmony_ci	ctx->local_nls = cifs_sb->local_nls;
398962306a36Sopenharmony_ci	ctx->linux_uid = fsuid;
399062306a36Sopenharmony_ci	ctx->cred_uid = fsuid;
399162306a36Sopenharmony_ci	ctx->UNC = master_tcon->tree_name;
399262306a36Sopenharmony_ci	ctx->retry = master_tcon->retry;
399362306a36Sopenharmony_ci	ctx->nocase = master_tcon->nocase;
399462306a36Sopenharmony_ci	ctx->nohandlecache = master_tcon->nohandlecache;
399562306a36Sopenharmony_ci	ctx->local_lease = master_tcon->local_lease;
399662306a36Sopenharmony_ci	ctx->no_lease = master_tcon->no_lease;
399762306a36Sopenharmony_ci	ctx->resilient = master_tcon->use_resilient;
399862306a36Sopenharmony_ci	ctx->persistent = master_tcon->use_persistent;
399962306a36Sopenharmony_ci	ctx->handle_timeout = master_tcon->handle_timeout;
400062306a36Sopenharmony_ci	ctx->no_linux_ext = !master_tcon->unix_ext;
400162306a36Sopenharmony_ci	ctx->linux_ext = master_tcon->posix_extensions;
400262306a36Sopenharmony_ci	ctx->sectype = master_tcon->ses->sectype;
400362306a36Sopenharmony_ci	ctx->sign = master_tcon->ses->sign;
400462306a36Sopenharmony_ci	ctx->seal = master_tcon->seal;
400562306a36Sopenharmony_ci	ctx->witness = master_tcon->use_witness;
400662306a36Sopenharmony_ci
400762306a36Sopenharmony_ci	rc = cifs_set_vol_auth(ctx, master_tcon->ses);
400862306a36Sopenharmony_ci	if (rc) {
400962306a36Sopenharmony_ci		tcon = ERR_PTR(rc);
401062306a36Sopenharmony_ci		goto out;
401162306a36Sopenharmony_ci	}
401262306a36Sopenharmony_ci
401362306a36Sopenharmony_ci	/* get a reference for the same TCP session */
401462306a36Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
401562306a36Sopenharmony_ci	++master_tcon->ses->server->srv_count;
401662306a36Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
401762306a36Sopenharmony_ci
401862306a36Sopenharmony_ci	ses = cifs_get_smb_ses(master_tcon->ses->server, ctx);
401962306a36Sopenharmony_ci	if (IS_ERR(ses)) {
402062306a36Sopenharmony_ci		tcon = (struct cifs_tcon *)ses;
402162306a36Sopenharmony_ci		cifs_put_tcp_session(master_tcon->ses->server, 0);
402262306a36Sopenharmony_ci		goto out;
402362306a36Sopenharmony_ci	}
402462306a36Sopenharmony_ci
402562306a36Sopenharmony_ci	tcon = cifs_get_tcon(ses, ctx);
402662306a36Sopenharmony_ci	if (IS_ERR(tcon)) {
402762306a36Sopenharmony_ci		cifs_put_smb_ses(ses);
402862306a36Sopenharmony_ci		goto out;
402962306a36Sopenharmony_ci	}
403062306a36Sopenharmony_ci
403162306a36Sopenharmony_ci#ifdef CONFIG_CIFS_ALLOW_INSECURE_LEGACY
403262306a36Sopenharmony_ci	if (cap_unix(ses))
403362306a36Sopenharmony_ci		reset_cifs_unix_caps(0, tcon, NULL, ctx);
403462306a36Sopenharmony_ci#endif /* CONFIG_CIFS_ALLOW_INSECURE_LEGACY */
403562306a36Sopenharmony_ci
403662306a36Sopenharmony_ciout:
403762306a36Sopenharmony_ci	kfree(ctx->username);
403862306a36Sopenharmony_ci	kfree_sensitive(ctx->password);
403962306a36Sopenharmony_ci	kfree(ctx);
404062306a36Sopenharmony_ci
404162306a36Sopenharmony_ci	return tcon;
404262306a36Sopenharmony_ci}
404362306a36Sopenharmony_ci
404462306a36Sopenharmony_cistruct cifs_tcon *
404562306a36Sopenharmony_cicifs_sb_master_tcon(struct cifs_sb_info *cifs_sb)
404662306a36Sopenharmony_ci{
404762306a36Sopenharmony_ci	return tlink_tcon(cifs_sb_master_tlink(cifs_sb));
404862306a36Sopenharmony_ci}
404962306a36Sopenharmony_ci
405062306a36Sopenharmony_ci/* find and return a tlink with given uid */
405162306a36Sopenharmony_cistatic struct tcon_link *
405262306a36Sopenharmony_citlink_rb_search(struct rb_root *root, kuid_t uid)
405362306a36Sopenharmony_ci{
405462306a36Sopenharmony_ci	struct rb_node *node = root->rb_node;
405562306a36Sopenharmony_ci	struct tcon_link *tlink;
405662306a36Sopenharmony_ci
405762306a36Sopenharmony_ci	while (node) {
405862306a36Sopenharmony_ci		tlink = rb_entry(node, struct tcon_link, tl_rbnode);
405962306a36Sopenharmony_ci
406062306a36Sopenharmony_ci		if (uid_gt(tlink->tl_uid, uid))
406162306a36Sopenharmony_ci			node = node->rb_left;
406262306a36Sopenharmony_ci		else if (uid_lt(tlink->tl_uid, uid))
406362306a36Sopenharmony_ci			node = node->rb_right;
406462306a36Sopenharmony_ci		else
406562306a36Sopenharmony_ci			return tlink;
406662306a36Sopenharmony_ci	}
406762306a36Sopenharmony_ci	return NULL;
406862306a36Sopenharmony_ci}
406962306a36Sopenharmony_ci
407062306a36Sopenharmony_ci/* insert a tcon_link into the tree */
407162306a36Sopenharmony_cistatic void
407262306a36Sopenharmony_citlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink)
407362306a36Sopenharmony_ci{
407462306a36Sopenharmony_ci	struct rb_node **new = &(root->rb_node), *parent = NULL;
407562306a36Sopenharmony_ci	struct tcon_link *tlink;
407662306a36Sopenharmony_ci
407762306a36Sopenharmony_ci	while (*new) {
407862306a36Sopenharmony_ci		tlink = rb_entry(*new, struct tcon_link, tl_rbnode);
407962306a36Sopenharmony_ci		parent = *new;
408062306a36Sopenharmony_ci
408162306a36Sopenharmony_ci		if (uid_gt(tlink->tl_uid, new_tlink->tl_uid))
408262306a36Sopenharmony_ci			new = &((*new)->rb_left);
408362306a36Sopenharmony_ci		else
408462306a36Sopenharmony_ci			new = &((*new)->rb_right);
408562306a36Sopenharmony_ci	}
408662306a36Sopenharmony_ci
408762306a36Sopenharmony_ci	rb_link_node(&new_tlink->tl_rbnode, parent, new);
408862306a36Sopenharmony_ci	rb_insert_color(&new_tlink->tl_rbnode, root);
408962306a36Sopenharmony_ci}
409062306a36Sopenharmony_ci
409162306a36Sopenharmony_ci/*
409262306a36Sopenharmony_ci * Find or construct an appropriate tcon given a cifs_sb and the fsuid of the
409362306a36Sopenharmony_ci * current task.
409462306a36Sopenharmony_ci *
409562306a36Sopenharmony_ci * If the superblock doesn't refer to a multiuser mount, then just return
409662306a36Sopenharmony_ci * the master tcon for the mount.
409762306a36Sopenharmony_ci *
409862306a36Sopenharmony_ci * First, search the rbtree for an existing tcon for this fsuid. If one
409962306a36Sopenharmony_ci * exists, then check to see if it's pending construction. If it is then wait
410062306a36Sopenharmony_ci * for construction to complete. Once it's no longer pending, check to see if
410162306a36Sopenharmony_ci * it failed and either return an error or retry construction, depending on
410262306a36Sopenharmony_ci * the timeout.
410362306a36Sopenharmony_ci *
410462306a36Sopenharmony_ci * If one doesn't exist then insert a new tcon_link struct into the tree and
410562306a36Sopenharmony_ci * try to construct a new one.
410662306a36Sopenharmony_ci */
410762306a36Sopenharmony_cistruct tcon_link *
410862306a36Sopenharmony_cicifs_sb_tlink(struct cifs_sb_info *cifs_sb)
410962306a36Sopenharmony_ci{
411062306a36Sopenharmony_ci	int ret;
411162306a36Sopenharmony_ci	kuid_t fsuid = current_fsuid();
411262306a36Sopenharmony_ci	struct tcon_link *tlink, *newtlink;
411362306a36Sopenharmony_ci
411462306a36Sopenharmony_ci	if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER))
411562306a36Sopenharmony_ci		return cifs_get_tlink(cifs_sb_master_tlink(cifs_sb));
411662306a36Sopenharmony_ci
411762306a36Sopenharmony_ci	spin_lock(&cifs_sb->tlink_tree_lock);
411862306a36Sopenharmony_ci	tlink = tlink_rb_search(&cifs_sb->tlink_tree, fsuid);
411962306a36Sopenharmony_ci	if (tlink)
412062306a36Sopenharmony_ci		cifs_get_tlink(tlink);
412162306a36Sopenharmony_ci	spin_unlock(&cifs_sb->tlink_tree_lock);
412262306a36Sopenharmony_ci
412362306a36Sopenharmony_ci	if (tlink == NULL) {
412462306a36Sopenharmony_ci		newtlink = kzalloc(sizeof(*tlink), GFP_KERNEL);
412562306a36Sopenharmony_ci		if (newtlink == NULL)
412662306a36Sopenharmony_ci			return ERR_PTR(-ENOMEM);
412762306a36Sopenharmony_ci		newtlink->tl_uid = fsuid;
412862306a36Sopenharmony_ci		newtlink->tl_tcon = ERR_PTR(-EACCES);
412962306a36Sopenharmony_ci		set_bit(TCON_LINK_PENDING, &newtlink->tl_flags);
413062306a36Sopenharmony_ci		set_bit(TCON_LINK_IN_TREE, &newtlink->tl_flags);
413162306a36Sopenharmony_ci		cifs_get_tlink(newtlink);
413262306a36Sopenharmony_ci
413362306a36Sopenharmony_ci		spin_lock(&cifs_sb->tlink_tree_lock);
413462306a36Sopenharmony_ci		/* was one inserted after previous search? */
413562306a36Sopenharmony_ci		tlink = tlink_rb_search(&cifs_sb->tlink_tree, fsuid);
413662306a36Sopenharmony_ci		if (tlink) {
413762306a36Sopenharmony_ci			cifs_get_tlink(tlink);
413862306a36Sopenharmony_ci			spin_unlock(&cifs_sb->tlink_tree_lock);
413962306a36Sopenharmony_ci			kfree(newtlink);
414062306a36Sopenharmony_ci			goto wait_for_construction;
414162306a36Sopenharmony_ci		}
414262306a36Sopenharmony_ci		tlink = newtlink;
414362306a36Sopenharmony_ci		tlink_rb_insert(&cifs_sb->tlink_tree, tlink);
414462306a36Sopenharmony_ci		spin_unlock(&cifs_sb->tlink_tree_lock);
414562306a36Sopenharmony_ci	} else {
414662306a36Sopenharmony_ciwait_for_construction:
414762306a36Sopenharmony_ci		ret = wait_on_bit(&tlink->tl_flags, TCON_LINK_PENDING,
414862306a36Sopenharmony_ci				  TASK_INTERRUPTIBLE);
414962306a36Sopenharmony_ci		if (ret) {
415062306a36Sopenharmony_ci			cifs_put_tlink(tlink);
415162306a36Sopenharmony_ci			return ERR_PTR(-ERESTARTSYS);
415262306a36Sopenharmony_ci		}
415362306a36Sopenharmony_ci
415462306a36Sopenharmony_ci		/* if it's good, return it */
415562306a36Sopenharmony_ci		if (!IS_ERR(tlink->tl_tcon))
415662306a36Sopenharmony_ci			return tlink;
415762306a36Sopenharmony_ci
415862306a36Sopenharmony_ci		/* return error if we tried this already recently */
415962306a36Sopenharmony_ci		if (time_before(jiffies, tlink->tl_time + TLINK_ERROR_EXPIRE)) {
416062306a36Sopenharmony_ci			cifs_put_tlink(tlink);
416162306a36Sopenharmony_ci			return ERR_PTR(-EACCES);
416262306a36Sopenharmony_ci		}
416362306a36Sopenharmony_ci
416462306a36Sopenharmony_ci		if (test_and_set_bit(TCON_LINK_PENDING, &tlink->tl_flags))
416562306a36Sopenharmony_ci			goto wait_for_construction;
416662306a36Sopenharmony_ci	}
416762306a36Sopenharmony_ci
416862306a36Sopenharmony_ci	tlink->tl_tcon = cifs_construct_tcon(cifs_sb, fsuid);
416962306a36Sopenharmony_ci	clear_bit(TCON_LINK_PENDING, &tlink->tl_flags);
417062306a36Sopenharmony_ci	wake_up_bit(&tlink->tl_flags, TCON_LINK_PENDING);
417162306a36Sopenharmony_ci
417262306a36Sopenharmony_ci	if (IS_ERR(tlink->tl_tcon)) {
417362306a36Sopenharmony_ci		cifs_put_tlink(tlink);
417462306a36Sopenharmony_ci		return ERR_PTR(-EACCES);
417562306a36Sopenharmony_ci	}
417662306a36Sopenharmony_ci
417762306a36Sopenharmony_ci	return tlink;
417862306a36Sopenharmony_ci}
417962306a36Sopenharmony_ci
418062306a36Sopenharmony_ci/*
418162306a36Sopenharmony_ci * periodic workqueue job that scans tcon_tree for a superblock and closes
418262306a36Sopenharmony_ci * out tcons.
418362306a36Sopenharmony_ci */
418462306a36Sopenharmony_cistatic void
418562306a36Sopenharmony_cicifs_prune_tlinks(struct work_struct *work)
418662306a36Sopenharmony_ci{
418762306a36Sopenharmony_ci	struct cifs_sb_info *cifs_sb = container_of(work, struct cifs_sb_info,
418862306a36Sopenharmony_ci						    prune_tlinks.work);
418962306a36Sopenharmony_ci	struct rb_root *root = &cifs_sb->tlink_tree;
419062306a36Sopenharmony_ci	struct rb_node *node;
419162306a36Sopenharmony_ci	struct rb_node *tmp;
419262306a36Sopenharmony_ci	struct tcon_link *tlink;
419362306a36Sopenharmony_ci
419462306a36Sopenharmony_ci	/*
419562306a36Sopenharmony_ci	 * Because we drop the spinlock in the loop in order to put the tlink
419662306a36Sopenharmony_ci	 * it's not guarded against removal of links from the tree. The only
419762306a36Sopenharmony_ci	 * places that remove entries from the tree are this function and
419862306a36Sopenharmony_ci	 * umounts. Because this function is non-reentrant and is canceled
419962306a36Sopenharmony_ci	 * before umount can proceed, this is safe.
420062306a36Sopenharmony_ci	 */
420162306a36Sopenharmony_ci	spin_lock(&cifs_sb->tlink_tree_lock);
420262306a36Sopenharmony_ci	node = rb_first(root);
420362306a36Sopenharmony_ci	while (node != NULL) {
420462306a36Sopenharmony_ci		tmp = node;
420562306a36Sopenharmony_ci		node = rb_next(tmp);
420662306a36Sopenharmony_ci		tlink = rb_entry(tmp, struct tcon_link, tl_rbnode);
420762306a36Sopenharmony_ci
420862306a36Sopenharmony_ci		if (test_bit(TCON_LINK_MASTER, &tlink->tl_flags) ||
420962306a36Sopenharmony_ci		    atomic_read(&tlink->tl_count) != 0 ||
421062306a36Sopenharmony_ci		    time_after(tlink->tl_time + TLINK_IDLE_EXPIRE, jiffies))
421162306a36Sopenharmony_ci			continue;
421262306a36Sopenharmony_ci
421362306a36Sopenharmony_ci		cifs_get_tlink(tlink);
421462306a36Sopenharmony_ci		clear_bit(TCON_LINK_IN_TREE, &tlink->tl_flags);
421562306a36Sopenharmony_ci		rb_erase(tmp, root);
421662306a36Sopenharmony_ci
421762306a36Sopenharmony_ci		spin_unlock(&cifs_sb->tlink_tree_lock);
421862306a36Sopenharmony_ci		cifs_put_tlink(tlink);
421962306a36Sopenharmony_ci		spin_lock(&cifs_sb->tlink_tree_lock);
422062306a36Sopenharmony_ci	}
422162306a36Sopenharmony_ci	spin_unlock(&cifs_sb->tlink_tree_lock);
422262306a36Sopenharmony_ci
422362306a36Sopenharmony_ci	queue_delayed_work(cifsiod_wq, &cifs_sb->prune_tlinks,
422462306a36Sopenharmony_ci				TLINK_IDLE_EXPIRE);
422562306a36Sopenharmony_ci}
422662306a36Sopenharmony_ci
422762306a36Sopenharmony_ci#ifndef CONFIG_CIFS_DFS_UPCALL
422862306a36Sopenharmony_ciint cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc)
422962306a36Sopenharmony_ci{
423062306a36Sopenharmony_ci	int rc;
423162306a36Sopenharmony_ci	const struct smb_version_operations *ops = tcon->ses->server->ops;
423262306a36Sopenharmony_ci
423362306a36Sopenharmony_ci	/* only send once per connect */
423462306a36Sopenharmony_ci	spin_lock(&tcon->tc_lock);
423562306a36Sopenharmony_ci
423662306a36Sopenharmony_ci	/* if tcon is marked for needing reconnect, update state */
423762306a36Sopenharmony_ci	if (tcon->need_reconnect)
423862306a36Sopenharmony_ci		tcon->status = TID_NEED_TCON;
423962306a36Sopenharmony_ci
424062306a36Sopenharmony_ci	if (tcon->status == TID_GOOD) {
424162306a36Sopenharmony_ci		spin_unlock(&tcon->tc_lock);
424262306a36Sopenharmony_ci		return 0;
424362306a36Sopenharmony_ci	}
424462306a36Sopenharmony_ci
424562306a36Sopenharmony_ci	if (tcon->status != TID_NEW &&
424662306a36Sopenharmony_ci	    tcon->status != TID_NEED_TCON) {
424762306a36Sopenharmony_ci		spin_unlock(&tcon->tc_lock);
424862306a36Sopenharmony_ci		return -EHOSTDOWN;
424962306a36Sopenharmony_ci	}
425062306a36Sopenharmony_ci
425162306a36Sopenharmony_ci	tcon->status = TID_IN_TCON;
425262306a36Sopenharmony_ci	spin_unlock(&tcon->tc_lock);
425362306a36Sopenharmony_ci
425462306a36Sopenharmony_ci	rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name, tcon, nlsc);
425562306a36Sopenharmony_ci	if (rc) {
425662306a36Sopenharmony_ci		spin_lock(&tcon->tc_lock);
425762306a36Sopenharmony_ci		if (tcon->status == TID_IN_TCON)
425862306a36Sopenharmony_ci			tcon->status = TID_NEED_TCON;
425962306a36Sopenharmony_ci		spin_unlock(&tcon->tc_lock);
426062306a36Sopenharmony_ci	} else {
426162306a36Sopenharmony_ci		spin_lock(&tcon->tc_lock);
426262306a36Sopenharmony_ci		if (tcon->status == TID_IN_TCON)
426362306a36Sopenharmony_ci			tcon->status = TID_GOOD;
426462306a36Sopenharmony_ci		tcon->need_reconnect = false;
426562306a36Sopenharmony_ci		spin_unlock(&tcon->tc_lock);
426662306a36Sopenharmony_ci	}
426762306a36Sopenharmony_ci
426862306a36Sopenharmony_ci	return rc;
426962306a36Sopenharmony_ci}
427062306a36Sopenharmony_ci#endif
4271