xref: /kernel/linux/linux-5.10/fs/cifs/connect.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci *   fs/cifs/connect.c
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci *   Copyright (C) International Business Machines  Corp., 2002,2011
58c2ecf20Sopenharmony_ci *   Author(s): Steve French (sfrench@us.ibm.com)
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci *   This library is free software; you can redistribute it and/or modify
88c2ecf20Sopenharmony_ci *   it under the terms of the GNU Lesser General Public License as published
98c2ecf20Sopenharmony_ci *   by the Free Software Foundation; either version 2.1 of the License, or
108c2ecf20Sopenharmony_ci *   (at your option) any later version.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci *   This library is distributed in the hope that it will be useful,
138c2ecf20Sopenharmony_ci *   but WITHOUT ANY WARRANTY; without even the implied warranty of
148c2ecf20Sopenharmony_ci *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
158c2ecf20Sopenharmony_ci *   the GNU Lesser General Public License for more details.
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci *   You should have received a copy of the GNU Lesser General Public License
188c2ecf20Sopenharmony_ci *   along with this library; if not, write to the Free Software
198c2ecf20Sopenharmony_ci *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
208c2ecf20Sopenharmony_ci */
218c2ecf20Sopenharmony_ci#include <linux/fs.h>
228c2ecf20Sopenharmony_ci#include <linux/net.h>
238c2ecf20Sopenharmony_ci#include <linux/string.h>
248c2ecf20Sopenharmony_ci#include <linux/sched/mm.h>
258c2ecf20Sopenharmony_ci#include <linux/sched/signal.h>
268c2ecf20Sopenharmony_ci#include <linux/list.h>
278c2ecf20Sopenharmony_ci#include <linux/wait.h>
288c2ecf20Sopenharmony_ci#include <linux/slab.h>
298c2ecf20Sopenharmony_ci#include <linux/pagemap.h>
308c2ecf20Sopenharmony_ci#include <linux/ctype.h>
318c2ecf20Sopenharmony_ci#include <linux/utsname.h>
328c2ecf20Sopenharmony_ci#include <linux/mempool.h>
338c2ecf20Sopenharmony_ci#include <linux/delay.h>
348c2ecf20Sopenharmony_ci#include <linux/completion.h>
358c2ecf20Sopenharmony_ci#include <linux/kthread.h>
368c2ecf20Sopenharmony_ci#include <linux/pagevec.h>
378c2ecf20Sopenharmony_ci#include <linux/freezer.h>
388c2ecf20Sopenharmony_ci#include <linux/namei.h>
398c2ecf20Sopenharmony_ci#include <linux/uuid.h>
408c2ecf20Sopenharmony_ci#include <linux/uaccess.h>
418c2ecf20Sopenharmony_ci#include <asm/processor.h>
428c2ecf20Sopenharmony_ci#include <linux/inet.h>
438c2ecf20Sopenharmony_ci#include <linux/module.h>
448c2ecf20Sopenharmony_ci#include <keys/user-type.h>
458c2ecf20Sopenharmony_ci#include <net/ipv6.h>
468c2ecf20Sopenharmony_ci#include <linux/parser.h>
478c2ecf20Sopenharmony_ci#include <linux/bvec.h>
488c2ecf20Sopenharmony_ci#include "cifspdu.h"
498c2ecf20Sopenharmony_ci#include "cifsglob.h"
508c2ecf20Sopenharmony_ci#include "cifsproto.h"
518c2ecf20Sopenharmony_ci#include "cifs_unicode.h"
528c2ecf20Sopenharmony_ci#include "cifs_debug.h"
538c2ecf20Sopenharmony_ci#include "cifs_fs_sb.h"
548c2ecf20Sopenharmony_ci#include "ntlmssp.h"
558c2ecf20Sopenharmony_ci#include "nterr.h"
568c2ecf20Sopenharmony_ci#include "rfc1002pdu.h"
578c2ecf20Sopenharmony_ci#include "fscache.h"
588c2ecf20Sopenharmony_ci#include "smb2proto.h"
598c2ecf20Sopenharmony_ci#include "smbdirect.h"
608c2ecf20Sopenharmony_ci#include "dns_resolve.h"
618c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_DFS_UPCALL
628c2ecf20Sopenharmony_ci#include "dfs_cache.h"
638c2ecf20Sopenharmony_ci#endif
648c2ecf20Sopenharmony_ci#include "fs_context.h"
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ciextern mempool_t *cifs_req_poolp;
678c2ecf20Sopenharmony_ciextern bool disable_legacy_dialects;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci/* FIXME: should these be tunable? */
708c2ecf20Sopenharmony_ci#define TLINK_ERROR_EXPIRE	(1 * HZ)
718c2ecf20Sopenharmony_ci#define TLINK_IDLE_EXPIRE	(600 * HZ)
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci/* Drop the connection to not overload the server */
748c2ecf20Sopenharmony_ci#define NUM_STATUS_IO_TIMEOUT   5
758c2ecf20Sopenharmony_ci
768c2ecf20Sopenharmony_cienum {
778c2ecf20Sopenharmony_ci	/* Mount options that take no arguments */
788c2ecf20Sopenharmony_ci	Opt_user_xattr, Opt_nouser_xattr,
798c2ecf20Sopenharmony_ci	Opt_forceuid, Opt_noforceuid,
808c2ecf20Sopenharmony_ci	Opt_forcegid, Opt_noforcegid,
818c2ecf20Sopenharmony_ci	Opt_noblocksend, Opt_noautotune, Opt_nolease,
828c2ecf20Sopenharmony_ci	Opt_hard, Opt_soft, Opt_perm, Opt_noperm, Opt_nodelete,
838c2ecf20Sopenharmony_ci	Opt_mapposix, Opt_nomapposix,
848c2ecf20Sopenharmony_ci	Opt_mapchars, Opt_nomapchars, Opt_sfu,
858c2ecf20Sopenharmony_ci	Opt_nosfu, Opt_nodfs, Opt_posixpaths,
868c2ecf20Sopenharmony_ci	Opt_noposixpaths, Opt_nounix, Opt_unix,
878c2ecf20Sopenharmony_ci	Opt_nocase,
888c2ecf20Sopenharmony_ci	Opt_brl, Opt_nobrl,
898c2ecf20Sopenharmony_ci	Opt_handlecache, Opt_nohandlecache,
908c2ecf20Sopenharmony_ci	Opt_forcemandatorylock, Opt_setuidfromacl, Opt_setuids,
918c2ecf20Sopenharmony_ci	Opt_nosetuids, Opt_dynperm, Opt_nodynperm,
928c2ecf20Sopenharmony_ci	Opt_nohard, Opt_nosoft,
938c2ecf20Sopenharmony_ci	Opt_nointr, Opt_intr,
948c2ecf20Sopenharmony_ci	Opt_nostrictsync, Opt_strictsync,
958c2ecf20Sopenharmony_ci	Opt_serverino, Opt_noserverino,
968c2ecf20Sopenharmony_ci	Opt_rwpidforward, Opt_cifsacl, Opt_nocifsacl,
978c2ecf20Sopenharmony_ci	Opt_acl, Opt_noacl, Opt_locallease,
988c2ecf20Sopenharmony_ci	Opt_sign, Opt_ignore_signature, Opt_seal, Opt_noac,
998c2ecf20Sopenharmony_ci	Opt_fsc, Opt_mfsymlinks,
1008c2ecf20Sopenharmony_ci	Opt_multiuser, Opt_sloppy, Opt_nosharesock,
1018c2ecf20Sopenharmony_ci	Opt_persistent, Opt_nopersistent,
1028c2ecf20Sopenharmony_ci	Opt_resilient, Opt_noresilient,
1038c2ecf20Sopenharmony_ci	Opt_domainauto, Opt_rdma, Opt_modesid, Opt_rootfs,
1048c2ecf20Sopenharmony_ci	Opt_multichannel, Opt_nomultichannel,
1058c2ecf20Sopenharmony_ci	Opt_compress,
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	/* Mount options which take numeric value */
1088c2ecf20Sopenharmony_ci	Opt_backupuid, Opt_backupgid, Opt_uid,
1098c2ecf20Sopenharmony_ci	Opt_cruid, Opt_gid, Opt_file_mode,
1108c2ecf20Sopenharmony_ci	Opt_dirmode, Opt_port,
1118c2ecf20Sopenharmony_ci	Opt_min_enc_offload,
1128c2ecf20Sopenharmony_ci	Opt_blocksize, Opt_rsize, Opt_wsize, Opt_actimeo,
1138c2ecf20Sopenharmony_ci	Opt_echo_interval, Opt_max_credits, Opt_handletimeout,
1148c2ecf20Sopenharmony_ci	Opt_snapshot, Opt_max_channels,
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	/* Mount options which take string value */
1178c2ecf20Sopenharmony_ci	Opt_user, Opt_pass, Opt_ip,
1188c2ecf20Sopenharmony_ci	Opt_domain, Opt_srcaddr, Opt_iocharset,
1198c2ecf20Sopenharmony_ci	Opt_netbiosname, Opt_servern,
1208c2ecf20Sopenharmony_ci	Opt_ver, Opt_vers, Opt_sec, Opt_cache,
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci	/* Mount options to be ignored */
1238c2ecf20Sopenharmony_ci	Opt_ignore,
1248c2ecf20Sopenharmony_ci
1258c2ecf20Sopenharmony_ci	/* Options which could be blank */
1268c2ecf20Sopenharmony_ci	Opt_blank_pass,
1278c2ecf20Sopenharmony_ci	Opt_blank_user,
1288c2ecf20Sopenharmony_ci	Opt_blank_ip,
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	Opt_err
1318c2ecf20Sopenharmony_ci};
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_cistatic const match_table_t cifs_mount_option_tokens = {
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_ci	{ Opt_user_xattr, "user_xattr" },
1368c2ecf20Sopenharmony_ci	{ Opt_nouser_xattr, "nouser_xattr" },
1378c2ecf20Sopenharmony_ci	{ Opt_forceuid, "forceuid" },
1388c2ecf20Sopenharmony_ci	{ Opt_noforceuid, "noforceuid" },
1398c2ecf20Sopenharmony_ci	{ Opt_forcegid, "forcegid" },
1408c2ecf20Sopenharmony_ci	{ Opt_noforcegid, "noforcegid" },
1418c2ecf20Sopenharmony_ci	{ Opt_noblocksend, "noblocksend" },
1428c2ecf20Sopenharmony_ci	{ Opt_noautotune, "noautotune" },
1438c2ecf20Sopenharmony_ci	{ Opt_nolease, "nolease" },
1448c2ecf20Sopenharmony_ci	{ Opt_hard, "hard" },
1458c2ecf20Sopenharmony_ci	{ Opt_soft, "soft" },
1468c2ecf20Sopenharmony_ci	{ Opt_perm, "perm" },
1478c2ecf20Sopenharmony_ci	{ Opt_noperm, "noperm" },
1488c2ecf20Sopenharmony_ci	{ Opt_nodelete, "nodelete" },
1498c2ecf20Sopenharmony_ci	{ Opt_mapchars, "mapchars" }, /* SFU style */
1508c2ecf20Sopenharmony_ci	{ Opt_nomapchars, "nomapchars" },
1518c2ecf20Sopenharmony_ci	{ Opt_mapposix, "mapposix" }, /* SFM style */
1528c2ecf20Sopenharmony_ci	{ Opt_nomapposix, "nomapposix" },
1538c2ecf20Sopenharmony_ci	{ Opt_sfu, "sfu" },
1548c2ecf20Sopenharmony_ci	{ Opt_nosfu, "nosfu" },
1558c2ecf20Sopenharmony_ci	{ Opt_nodfs, "nodfs" },
1568c2ecf20Sopenharmony_ci	{ Opt_posixpaths, "posixpaths" },
1578c2ecf20Sopenharmony_ci	{ Opt_noposixpaths, "noposixpaths" },
1588c2ecf20Sopenharmony_ci	{ Opt_nounix, "nounix" },
1598c2ecf20Sopenharmony_ci	{ Opt_nounix, "nolinux" },
1608c2ecf20Sopenharmony_ci	{ Opt_nounix, "noposix" },
1618c2ecf20Sopenharmony_ci	{ Opt_unix, "unix" },
1628c2ecf20Sopenharmony_ci	{ Opt_unix, "linux" },
1638c2ecf20Sopenharmony_ci	{ Opt_unix, "posix" },
1648c2ecf20Sopenharmony_ci	{ Opt_nocase, "nocase" },
1658c2ecf20Sopenharmony_ci	{ Opt_nocase, "ignorecase" },
1668c2ecf20Sopenharmony_ci	{ Opt_brl, "brl" },
1678c2ecf20Sopenharmony_ci	{ Opt_nobrl, "nobrl" },
1688c2ecf20Sopenharmony_ci	{ Opt_handlecache, "handlecache" },
1698c2ecf20Sopenharmony_ci	{ Opt_nohandlecache, "nohandlecache" },
1708c2ecf20Sopenharmony_ci	{ Opt_nobrl, "nolock" },
1718c2ecf20Sopenharmony_ci	{ Opt_forcemandatorylock, "forcemandatorylock" },
1728c2ecf20Sopenharmony_ci	{ Opt_forcemandatorylock, "forcemand" },
1738c2ecf20Sopenharmony_ci	{ Opt_setuids, "setuids" },
1748c2ecf20Sopenharmony_ci	{ Opt_nosetuids, "nosetuids" },
1758c2ecf20Sopenharmony_ci	{ Opt_setuidfromacl, "idsfromsid" },
1768c2ecf20Sopenharmony_ci	{ Opt_dynperm, "dynperm" },
1778c2ecf20Sopenharmony_ci	{ Opt_nodynperm, "nodynperm" },
1788c2ecf20Sopenharmony_ci	{ Opt_nohard, "nohard" },
1798c2ecf20Sopenharmony_ci	{ Opt_nosoft, "nosoft" },
1808c2ecf20Sopenharmony_ci	{ Opt_nointr, "nointr" },
1818c2ecf20Sopenharmony_ci	{ Opt_intr, "intr" },
1828c2ecf20Sopenharmony_ci	{ Opt_nostrictsync, "nostrictsync" },
1838c2ecf20Sopenharmony_ci	{ Opt_strictsync, "strictsync" },
1848c2ecf20Sopenharmony_ci	{ Opt_serverino, "serverino" },
1858c2ecf20Sopenharmony_ci	{ Opt_noserverino, "noserverino" },
1868c2ecf20Sopenharmony_ci	{ Opt_rwpidforward, "rwpidforward" },
1878c2ecf20Sopenharmony_ci	{ Opt_modesid, "modefromsid" },
1888c2ecf20Sopenharmony_ci	{ Opt_cifsacl, "cifsacl" },
1898c2ecf20Sopenharmony_ci	{ Opt_nocifsacl, "nocifsacl" },
1908c2ecf20Sopenharmony_ci	{ Opt_acl, "acl" },
1918c2ecf20Sopenharmony_ci	{ Opt_noacl, "noacl" },
1928c2ecf20Sopenharmony_ci	{ Opt_locallease, "locallease" },
1938c2ecf20Sopenharmony_ci	{ Opt_sign, "sign" },
1948c2ecf20Sopenharmony_ci	{ Opt_ignore_signature, "signloosely" },
1958c2ecf20Sopenharmony_ci	{ Opt_seal, "seal" },
1968c2ecf20Sopenharmony_ci	{ Opt_noac, "noac" },
1978c2ecf20Sopenharmony_ci	{ Opt_fsc, "fsc" },
1988c2ecf20Sopenharmony_ci	{ Opt_mfsymlinks, "mfsymlinks" },
1998c2ecf20Sopenharmony_ci	{ Opt_multiuser, "multiuser" },
2008c2ecf20Sopenharmony_ci	{ Opt_sloppy, "sloppy" },
2018c2ecf20Sopenharmony_ci	{ Opt_nosharesock, "nosharesock" },
2028c2ecf20Sopenharmony_ci	{ Opt_persistent, "persistenthandles"},
2038c2ecf20Sopenharmony_ci	{ Opt_nopersistent, "nopersistenthandles"},
2048c2ecf20Sopenharmony_ci	{ Opt_resilient, "resilienthandles"},
2058c2ecf20Sopenharmony_ci	{ Opt_noresilient, "noresilienthandles"},
2068c2ecf20Sopenharmony_ci	{ Opt_domainauto, "domainauto"},
2078c2ecf20Sopenharmony_ci	{ Opt_rdma, "rdma"},
2088c2ecf20Sopenharmony_ci	{ Opt_multichannel, "multichannel" },
2098c2ecf20Sopenharmony_ci	{ Opt_nomultichannel, "nomultichannel" },
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci	{ Opt_backupuid, "backupuid=%s" },
2128c2ecf20Sopenharmony_ci	{ Opt_backupgid, "backupgid=%s" },
2138c2ecf20Sopenharmony_ci	{ Opt_uid, "uid=%s" },
2148c2ecf20Sopenharmony_ci	{ Opt_cruid, "cruid=%s" },
2158c2ecf20Sopenharmony_ci	{ Opt_gid, "gid=%s" },
2168c2ecf20Sopenharmony_ci	{ Opt_file_mode, "file_mode=%s" },
2178c2ecf20Sopenharmony_ci	{ Opt_dirmode, "dirmode=%s" },
2188c2ecf20Sopenharmony_ci	{ Opt_dirmode, "dir_mode=%s" },
2198c2ecf20Sopenharmony_ci	{ Opt_port, "port=%s" },
2208c2ecf20Sopenharmony_ci	{ Opt_min_enc_offload, "esize=%s" },
2218c2ecf20Sopenharmony_ci	{ Opt_blocksize, "bsize=%s" },
2228c2ecf20Sopenharmony_ci	{ Opt_rsize, "rsize=%s" },
2238c2ecf20Sopenharmony_ci	{ Opt_wsize, "wsize=%s" },
2248c2ecf20Sopenharmony_ci	{ Opt_actimeo, "actimeo=%s" },
2258c2ecf20Sopenharmony_ci	{ Opt_handletimeout, "handletimeout=%s" },
2268c2ecf20Sopenharmony_ci	{ Opt_echo_interval, "echo_interval=%s" },
2278c2ecf20Sopenharmony_ci	{ Opt_max_credits, "max_credits=%s" },
2288c2ecf20Sopenharmony_ci	{ Opt_snapshot, "snapshot=%s" },
2298c2ecf20Sopenharmony_ci	{ Opt_max_channels, "max_channels=%s" },
2308c2ecf20Sopenharmony_ci	{ Opt_compress, "compress=%s" },
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_ci	{ Opt_blank_user, "user=" },
2338c2ecf20Sopenharmony_ci	{ Opt_blank_user, "username=" },
2348c2ecf20Sopenharmony_ci	{ Opt_user, "user=%s" },
2358c2ecf20Sopenharmony_ci	{ Opt_user, "username=%s" },
2368c2ecf20Sopenharmony_ci	{ Opt_blank_pass, "pass=" },
2378c2ecf20Sopenharmony_ci	{ Opt_blank_pass, "password=" },
2388c2ecf20Sopenharmony_ci	{ Opt_pass, "pass=%s" },
2398c2ecf20Sopenharmony_ci	{ Opt_pass, "password=%s" },
2408c2ecf20Sopenharmony_ci	{ Opt_blank_ip, "ip=" },
2418c2ecf20Sopenharmony_ci	{ Opt_blank_ip, "addr=" },
2428c2ecf20Sopenharmony_ci	{ Opt_ip, "ip=%s" },
2438c2ecf20Sopenharmony_ci	{ Opt_ip, "addr=%s" },
2448c2ecf20Sopenharmony_ci	{ Opt_ignore, "unc=%s" },
2458c2ecf20Sopenharmony_ci	{ Opt_ignore, "target=%s" },
2468c2ecf20Sopenharmony_ci	{ Opt_ignore, "path=%s" },
2478c2ecf20Sopenharmony_ci	{ Opt_domain, "dom=%s" },
2488c2ecf20Sopenharmony_ci	{ Opt_domain, "domain=%s" },
2498c2ecf20Sopenharmony_ci	{ Opt_domain, "workgroup=%s" },
2508c2ecf20Sopenharmony_ci	{ Opt_srcaddr, "srcaddr=%s" },
2518c2ecf20Sopenharmony_ci	{ Opt_ignore, "prefixpath=%s" },
2528c2ecf20Sopenharmony_ci	{ Opt_iocharset, "iocharset=%s" },
2538c2ecf20Sopenharmony_ci	{ Opt_netbiosname, "netbiosname=%s" },
2548c2ecf20Sopenharmony_ci	{ Opt_servern, "servern=%s" },
2558c2ecf20Sopenharmony_ci	{ Opt_ver, "ver=%s" },
2568c2ecf20Sopenharmony_ci	{ Opt_vers, "vers=%s" },
2578c2ecf20Sopenharmony_ci	{ Opt_sec, "sec=%s" },
2588c2ecf20Sopenharmony_ci	{ Opt_cache, "cache=%s" },
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	{ Opt_ignore, "cred" },
2618c2ecf20Sopenharmony_ci	{ Opt_ignore, "credentials" },
2628c2ecf20Sopenharmony_ci	{ Opt_ignore, "cred=%s" },
2638c2ecf20Sopenharmony_ci	{ Opt_ignore, "credentials=%s" },
2648c2ecf20Sopenharmony_ci	{ Opt_ignore, "guest" },
2658c2ecf20Sopenharmony_ci	{ Opt_ignore, "rw" },
2668c2ecf20Sopenharmony_ci	{ Opt_ignore, "ro" },
2678c2ecf20Sopenharmony_ci	{ Opt_ignore, "suid" },
2688c2ecf20Sopenharmony_ci	{ Opt_ignore, "nosuid" },
2698c2ecf20Sopenharmony_ci	{ Opt_ignore, "exec" },
2708c2ecf20Sopenharmony_ci	{ Opt_ignore, "noexec" },
2718c2ecf20Sopenharmony_ci	{ Opt_ignore, "nodev" },
2728c2ecf20Sopenharmony_ci	{ Opt_ignore, "noauto" },
2738c2ecf20Sopenharmony_ci	{ Opt_ignore, "dev" },
2748c2ecf20Sopenharmony_ci	{ Opt_ignore, "mand" },
2758c2ecf20Sopenharmony_ci	{ Opt_ignore, "nomand" },
2768c2ecf20Sopenharmony_ci	{ Opt_ignore, "relatime" },
2778c2ecf20Sopenharmony_ci	{ Opt_ignore, "_netdev" },
2788c2ecf20Sopenharmony_ci	{ Opt_rootfs, "rootfs" },
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	{ Opt_err, NULL }
2818c2ecf20Sopenharmony_ci};
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_cistatic int ip_connect(struct TCP_Server_Info *server);
2848c2ecf20Sopenharmony_cistatic int generic_ip_connect(struct TCP_Server_Info *server);
2858c2ecf20Sopenharmony_cistatic void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink);
2868c2ecf20Sopenharmony_cistatic void cifs_prune_tlinks(struct work_struct *work);
2878c2ecf20Sopenharmony_cistatic char *extract_hostname(const char *unc);
2888c2ecf20Sopenharmony_ci
2898c2ecf20Sopenharmony_ci/*
2908c2ecf20Sopenharmony_ci * Resolve hostname and set ip addr in tcp ses. Useful for hostnames that may
2918c2ecf20Sopenharmony_ci * get their ip addresses changed at some point.
2928c2ecf20Sopenharmony_ci *
2938c2ecf20Sopenharmony_ci * This should be called with server->srv_mutex held.
2948c2ecf20Sopenharmony_ci */
2958c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_DFS_UPCALL
2968c2ecf20Sopenharmony_cistatic int reconn_set_ipaddr(struct TCP_Server_Info *server)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	int rc;
2998c2ecf20Sopenharmony_ci	int len;
3008c2ecf20Sopenharmony_ci	char *unc, *ipaddr = NULL;
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	if (!server->hostname)
3038c2ecf20Sopenharmony_ci		return -EINVAL;
3048c2ecf20Sopenharmony_ci
3058c2ecf20Sopenharmony_ci	len = strlen(server->hostname) + 3;
3068c2ecf20Sopenharmony_ci
3078c2ecf20Sopenharmony_ci	unc = kmalloc(len, GFP_KERNEL);
3088c2ecf20Sopenharmony_ci	if (!unc) {
3098c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "%s: failed to create UNC path\n", __func__);
3108c2ecf20Sopenharmony_ci		return -ENOMEM;
3118c2ecf20Sopenharmony_ci	}
3128c2ecf20Sopenharmony_ci	scnprintf(unc, len, "\\\\%s", server->hostname);
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	rc = dns_resolve_server_name_to_ip(unc, &ipaddr);
3158c2ecf20Sopenharmony_ci	kfree(unc);
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci	if (rc < 0) {
3188c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "%s: failed to resolve server part of %s to IP: %d\n",
3198c2ecf20Sopenharmony_ci			 __func__, server->hostname, rc);
3208c2ecf20Sopenharmony_ci		return rc;
3218c2ecf20Sopenharmony_ci	}
3228c2ecf20Sopenharmony_ci
3238c2ecf20Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
3248c2ecf20Sopenharmony_ci	rc = cifs_convert_address((struct sockaddr *)&server->dstaddr, ipaddr,
3258c2ecf20Sopenharmony_ci				  strlen(ipaddr));
3268c2ecf20Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
3278c2ecf20Sopenharmony_ci	kfree(ipaddr);
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	return !rc ? -1 : 0;
3308c2ecf20Sopenharmony_ci}
3318c2ecf20Sopenharmony_ci#else
3328c2ecf20Sopenharmony_cistatic inline int reconn_set_ipaddr(struct TCP_Server_Info *server)
3338c2ecf20Sopenharmony_ci{
3348c2ecf20Sopenharmony_ci	return 0;
3358c2ecf20Sopenharmony_ci}
3368c2ecf20Sopenharmony_ci#endif
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_DFS_UPCALL
3398c2ecf20Sopenharmony_ci/* These functions must be called with server->srv_mutex held */
3408c2ecf20Sopenharmony_cistatic void reconn_set_next_dfs_target(struct TCP_Server_Info *server,
3418c2ecf20Sopenharmony_ci				       struct cifs_sb_info *cifs_sb,
3428c2ecf20Sopenharmony_ci				       struct dfs_cache_tgt_list *tgt_list,
3438c2ecf20Sopenharmony_ci				       struct dfs_cache_tgt_iterator **tgt_it)
3448c2ecf20Sopenharmony_ci{
3458c2ecf20Sopenharmony_ci	const char *name;
3468c2ecf20Sopenharmony_ci
3478c2ecf20Sopenharmony_ci	if (!cifs_sb || !cifs_sb->origin_fullpath)
3488c2ecf20Sopenharmony_ci		return;
3498c2ecf20Sopenharmony_ci
3508c2ecf20Sopenharmony_ci	if (!*tgt_it) {
3518c2ecf20Sopenharmony_ci		*tgt_it = dfs_cache_get_tgt_iterator(tgt_list);
3528c2ecf20Sopenharmony_ci	} else {
3538c2ecf20Sopenharmony_ci		*tgt_it = dfs_cache_get_next_tgt(tgt_list, *tgt_it);
3548c2ecf20Sopenharmony_ci		if (!*tgt_it)
3558c2ecf20Sopenharmony_ci			*tgt_it = dfs_cache_get_tgt_iterator(tgt_list);
3568c2ecf20Sopenharmony_ci	}
3578c2ecf20Sopenharmony_ci
3588c2ecf20Sopenharmony_ci	cifs_dbg(FYI, "%s: UNC: %s\n", __func__, cifs_sb->origin_fullpath);
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	name = dfs_cache_get_tgt_name(*tgt_it);
3618c2ecf20Sopenharmony_ci
3628c2ecf20Sopenharmony_ci	kfree(server->hostname);
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	server->hostname = extract_hostname(name);
3658c2ecf20Sopenharmony_ci	if (IS_ERR(server->hostname)) {
3668c2ecf20Sopenharmony_ci		cifs_dbg(FYI,
3678c2ecf20Sopenharmony_ci			 "%s: failed to extract hostname from target: %ld\n",
3688c2ecf20Sopenharmony_ci			 __func__, PTR_ERR(server->hostname));
3698c2ecf20Sopenharmony_ci	}
3708c2ecf20Sopenharmony_ci}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_cistatic inline int reconn_setup_dfs_targets(struct cifs_sb_info *cifs_sb,
3738c2ecf20Sopenharmony_ci					   struct dfs_cache_tgt_list *tl)
3748c2ecf20Sopenharmony_ci{
3758c2ecf20Sopenharmony_ci	if (!cifs_sb->origin_fullpath)
3768c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
3778c2ecf20Sopenharmony_ci	return dfs_cache_noreq_find(cifs_sb->origin_fullpath + 1, NULL, tl);
3788c2ecf20Sopenharmony_ci}
3798c2ecf20Sopenharmony_ci#endif
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci/*
3828c2ecf20Sopenharmony_ci * cifs tcp session reconnection
3838c2ecf20Sopenharmony_ci *
3848c2ecf20Sopenharmony_ci * mark tcp session as reconnecting so temporarily locked
3858c2ecf20Sopenharmony_ci * mark all smb sessions as reconnecting for tcp session
3868c2ecf20Sopenharmony_ci * reconnect tcp session
3878c2ecf20Sopenharmony_ci * wake up waiters on reconnection? - (not needed currently)
3888c2ecf20Sopenharmony_ci */
3898c2ecf20Sopenharmony_ciint
3908c2ecf20Sopenharmony_cicifs_reconnect(struct TCP_Server_Info *server)
3918c2ecf20Sopenharmony_ci{
3928c2ecf20Sopenharmony_ci	int rc = 0;
3938c2ecf20Sopenharmony_ci	struct list_head *tmp, *tmp2;
3948c2ecf20Sopenharmony_ci	struct cifs_ses *ses;
3958c2ecf20Sopenharmony_ci	struct cifs_tcon *tcon;
3968c2ecf20Sopenharmony_ci	struct mid_q_entry *mid_entry;
3978c2ecf20Sopenharmony_ci	struct list_head retry_list;
3988c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_DFS_UPCALL
3998c2ecf20Sopenharmony_ci	struct super_block *sb = NULL;
4008c2ecf20Sopenharmony_ci	struct cifs_sb_info *cifs_sb = NULL;
4018c2ecf20Sopenharmony_ci	struct dfs_cache_tgt_list tgt_list = {0};
4028c2ecf20Sopenharmony_ci	struct dfs_cache_tgt_iterator *tgt_it = NULL;
4038c2ecf20Sopenharmony_ci#endif
4048c2ecf20Sopenharmony_ci
4058c2ecf20Sopenharmony_ci	spin_lock(&GlobalMid_Lock);
4068c2ecf20Sopenharmony_ci	server->nr_targets = 1;
4078c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_DFS_UPCALL
4088c2ecf20Sopenharmony_ci	spin_unlock(&GlobalMid_Lock);
4098c2ecf20Sopenharmony_ci	sb = cifs_get_tcp_super(server);
4108c2ecf20Sopenharmony_ci	if (IS_ERR(sb)) {
4118c2ecf20Sopenharmony_ci		rc = PTR_ERR(sb);
4128c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "%s: will not do DFS failover: rc = %d\n",
4138c2ecf20Sopenharmony_ci			 __func__, rc);
4148c2ecf20Sopenharmony_ci		sb = NULL;
4158c2ecf20Sopenharmony_ci	} else {
4168c2ecf20Sopenharmony_ci		cifs_sb = CIFS_SB(sb);
4178c2ecf20Sopenharmony_ci		rc = reconn_setup_dfs_targets(cifs_sb, &tgt_list);
4188c2ecf20Sopenharmony_ci		if (rc) {
4198c2ecf20Sopenharmony_ci			cifs_sb = NULL;
4208c2ecf20Sopenharmony_ci			if (rc != -EOPNOTSUPP) {
4218c2ecf20Sopenharmony_ci				cifs_server_dbg(VFS, "%s: no target servers for DFS failover\n",
4228c2ecf20Sopenharmony_ci						__func__);
4238c2ecf20Sopenharmony_ci			}
4248c2ecf20Sopenharmony_ci		} else {
4258c2ecf20Sopenharmony_ci			server->nr_targets = dfs_cache_get_nr_tgts(&tgt_list);
4268c2ecf20Sopenharmony_ci		}
4278c2ecf20Sopenharmony_ci	}
4288c2ecf20Sopenharmony_ci	cifs_dbg(FYI, "%s: will retry %d target(s)\n", __func__,
4298c2ecf20Sopenharmony_ci		 server->nr_targets);
4308c2ecf20Sopenharmony_ci	spin_lock(&GlobalMid_Lock);
4318c2ecf20Sopenharmony_ci#endif
4328c2ecf20Sopenharmony_ci	if (server->tcpStatus == CifsExiting) {
4338c2ecf20Sopenharmony_ci		/* the demux thread will exit normally
4348c2ecf20Sopenharmony_ci		next time through the loop */
4358c2ecf20Sopenharmony_ci		spin_unlock(&GlobalMid_Lock);
4368c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_DFS_UPCALL
4378c2ecf20Sopenharmony_ci		dfs_cache_free_tgts(&tgt_list);
4388c2ecf20Sopenharmony_ci		cifs_put_tcp_super(sb);
4398c2ecf20Sopenharmony_ci#endif
4408c2ecf20Sopenharmony_ci		wake_up(&server->response_q);
4418c2ecf20Sopenharmony_ci		return rc;
4428c2ecf20Sopenharmony_ci	} else
4438c2ecf20Sopenharmony_ci		server->tcpStatus = CifsNeedReconnect;
4448c2ecf20Sopenharmony_ci	spin_unlock(&GlobalMid_Lock);
4458c2ecf20Sopenharmony_ci	server->maxBuf = 0;
4468c2ecf20Sopenharmony_ci	server->max_read = 0;
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	cifs_dbg(FYI, "Mark tcp session as need reconnect\n");
4498c2ecf20Sopenharmony_ci	trace_smb3_reconnect(server->CurrentMid, server->hostname);
4508c2ecf20Sopenharmony_ci
4518c2ecf20Sopenharmony_ci	/* before reconnecting the tcp session, mark the smb session (uid)
4528c2ecf20Sopenharmony_ci		and the tid bad so they are not used until reconnected */
4538c2ecf20Sopenharmony_ci	cifs_dbg(FYI, "%s: marking sessions and tcons for reconnect\n",
4548c2ecf20Sopenharmony_ci		 __func__);
4558c2ecf20Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
4568c2ecf20Sopenharmony_ci	list_for_each(tmp, &server->smb_ses_list) {
4578c2ecf20Sopenharmony_ci		ses = list_entry(tmp, struct cifs_ses, smb_ses_list);
4588c2ecf20Sopenharmony_ci		ses->need_reconnect = true;
4598c2ecf20Sopenharmony_ci		list_for_each(tmp2, &ses->tcon_list) {
4608c2ecf20Sopenharmony_ci			tcon = list_entry(tmp2, struct cifs_tcon, tcon_list);
4618c2ecf20Sopenharmony_ci			tcon->need_reconnect = true;
4628c2ecf20Sopenharmony_ci		}
4638c2ecf20Sopenharmony_ci		if (ses->tcon_ipc)
4648c2ecf20Sopenharmony_ci			ses->tcon_ipc->need_reconnect = true;
4658c2ecf20Sopenharmony_ci	}
4668c2ecf20Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
4678c2ecf20Sopenharmony_ci
4688c2ecf20Sopenharmony_ci	/* do not want to be sending data on a socket we are freeing */
4698c2ecf20Sopenharmony_ci	cifs_dbg(FYI, "%s: tearing down socket\n", __func__);
4708c2ecf20Sopenharmony_ci	mutex_lock(&server->srv_mutex);
4718c2ecf20Sopenharmony_ci	if (server->ssocket) {
4728c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "State: 0x%x Flags: 0x%lx\n",
4738c2ecf20Sopenharmony_ci			 server->ssocket->state, server->ssocket->flags);
4748c2ecf20Sopenharmony_ci		kernel_sock_shutdown(server->ssocket, SHUT_WR);
4758c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "Post shutdown state: 0x%x Flags: 0x%lx\n",
4768c2ecf20Sopenharmony_ci			 server->ssocket->state, server->ssocket->flags);
4778c2ecf20Sopenharmony_ci		sock_release(server->ssocket);
4788c2ecf20Sopenharmony_ci		server->ssocket = NULL;
4798c2ecf20Sopenharmony_ci	}
4808c2ecf20Sopenharmony_ci	server->sequence_number = 0;
4818c2ecf20Sopenharmony_ci	server->session_estab = false;
4828c2ecf20Sopenharmony_ci	kfree(server->session_key.response);
4838c2ecf20Sopenharmony_ci	server->session_key.response = NULL;
4848c2ecf20Sopenharmony_ci	server->session_key.len = 0;
4858c2ecf20Sopenharmony_ci	server->lstrp = jiffies;
4868c2ecf20Sopenharmony_ci
4878c2ecf20Sopenharmony_ci	/* mark submitted MIDs for retry and issue callback */
4888c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&retry_list);
4898c2ecf20Sopenharmony_ci	cifs_dbg(FYI, "%s: moving mids to private list\n", __func__);
4908c2ecf20Sopenharmony_ci	spin_lock(&GlobalMid_Lock);
4918c2ecf20Sopenharmony_ci	list_for_each_safe(tmp, tmp2, &server->pending_mid_q) {
4928c2ecf20Sopenharmony_ci		mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
4938c2ecf20Sopenharmony_ci		kref_get(&mid_entry->refcount);
4948c2ecf20Sopenharmony_ci		if (mid_entry->mid_state == MID_REQUEST_SUBMITTED)
4958c2ecf20Sopenharmony_ci			mid_entry->mid_state = MID_RETRY_NEEDED;
4968c2ecf20Sopenharmony_ci		list_move(&mid_entry->qhead, &retry_list);
4978c2ecf20Sopenharmony_ci		mid_entry->mid_flags |= MID_DELETED;
4988c2ecf20Sopenharmony_ci	}
4998c2ecf20Sopenharmony_ci	spin_unlock(&GlobalMid_Lock);
5008c2ecf20Sopenharmony_ci	mutex_unlock(&server->srv_mutex);
5018c2ecf20Sopenharmony_ci
5028c2ecf20Sopenharmony_ci	cifs_dbg(FYI, "%s: issuing mid callbacks\n", __func__);
5038c2ecf20Sopenharmony_ci	list_for_each_safe(tmp, tmp2, &retry_list) {
5048c2ecf20Sopenharmony_ci		mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
5058c2ecf20Sopenharmony_ci		list_del_init(&mid_entry->qhead);
5068c2ecf20Sopenharmony_ci		mid_entry->callback(mid_entry);
5078c2ecf20Sopenharmony_ci		cifs_mid_q_entry_release(mid_entry);
5088c2ecf20Sopenharmony_ci	}
5098c2ecf20Sopenharmony_ci
5108c2ecf20Sopenharmony_ci	if (cifs_rdma_enabled(server)) {
5118c2ecf20Sopenharmony_ci		mutex_lock(&server->srv_mutex);
5128c2ecf20Sopenharmony_ci		smbd_destroy(server);
5138c2ecf20Sopenharmony_ci		mutex_unlock(&server->srv_mutex);
5148c2ecf20Sopenharmony_ci	}
5158c2ecf20Sopenharmony_ci
5168c2ecf20Sopenharmony_ci	do {
5178c2ecf20Sopenharmony_ci		try_to_freeze();
5188c2ecf20Sopenharmony_ci
5198c2ecf20Sopenharmony_ci		mutex_lock(&server->srv_mutex);
5208c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_DFS_UPCALL
5218c2ecf20Sopenharmony_ci		/*
5228c2ecf20Sopenharmony_ci		 * Set up next DFS target server (if any) for reconnect. If DFS
5238c2ecf20Sopenharmony_ci		 * feature is disabled, then we will retry last server we
5248c2ecf20Sopenharmony_ci		 * connected to before.
5258c2ecf20Sopenharmony_ci		 */
5268c2ecf20Sopenharmony_ci		reconn_set_next_dfs_target(server, cifs_sb, &tgt_list, &tgt_it);
5278c2ecf20Sopenharmony_ci#endif
5288c2ecf20Sopenharmony_ci		rc = reconn_set_ipaddr(server);
5298c2ecf20Sopenharmony_ci		if (rc) {
5308c2ecf20Sopenharmony_ci			cifs_dbg(FYI, "%s: failed to resolve hostname: %d\n",
5318c2ecf20Sopenharmony_ci				 __func__, rc);
5328c2ecf20Sopenharmony_ci		}
5338c2ecf20Sopenharmony_ci
5348c2ecf20Sopenharmony_ci		if (cifs_rdma_enabled(server))
5358c2ecf20Sopenharmony_ci			rc = smbd_reconnect(server);
5368c2ecf20Sopenharmony_ci		else
5378c2ecf20Sopenharmony_ci			rc = generic_ip_connect(server);
5388c2ecf20Sopenharmony_ci		if (rc) {
5398c2ecf20Sopenharmony_ci			cifs_dbg(FYI, "reconnect error %d\n", rc);
5408c2ecf20Sopenharmony_ci			mutex_unlock(&server->srv_mutex);
5418c2ecf20Sopenharmony_ci			msleep(3000);
5428c2ecf20Sopenharmony_ci		} else {
5438c2ecf20Sopenharmony_ci			atomic_inc(&tcpSesReconnectCount);
5448c2ecf20Sopenharmony_ci			set_credits(server, 1);
5458c2ecf20Sopenharmony_ci			spin_lock(&GlobalMid_Lock);
5468c2ecf20Sopenharmony_ci			if (server->tcpStatus != CifsExiting)
5478c2ecf20Sopenharmony_ci				server->tcpStatus = CifsNeedNegotiate;
5488c2ecf20Sopenharmony_ci			spin_unlock(&GlobalMid_Lock);
5498c2ecf20Sopenharmony_ci			mutex_unlock(&server->srv_mutex);
5508c2ecf20Sopenharmony_ci		}
5518c2ecf20Sopenharmony_ci	} while (server->tcpStatus == CifsNeedReconnect);
5528c2ecf20Sopenharmony_ci
5538c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_DFS_UPCALL
5548c2ecf20Sopenharmony_ci	if (tgt_it) {
5558c2ecf20Sopenharmony_ci		rc = dfs_cache_noreq_update_tgthint(cifs_sb->origin_fullpath + 1,
5568c2ecf20Sopenharmony_ci						    tgt_it);
5578c2ecf20Sopenharmony_ci		if (rc) {
5588c2ecf20Sopenharmony_ci			cifs_server_dbg(VFS, "%s: failed to update DFS target hint: rc = %d\n",
5598c2ecf20Sopenharmony_ci				 __func__, rc);
5608c2ecf20Sopenharmony_ci		}
5618c2ecf20Sopenharmony_ci		rc = dfs_cache_update_vol(cifs_sb->origin_fullpath, server);
5628c2ecf20Sopenharmony_ci		if (rc) {
5638c2ecf20Sopenharmony_ci			cifs_server_dbg(VFS, "%s: failed to update vol info in DFS cache: rc = %d\n",
5648c2ecf20Sopenharmony_ci				 __func__, rc);
5658c2ecf20Sopenharmony_ci		}
5668c2ecf20Sopenharmony_ci		dfs_cache_free_tgts(&tgt_list);
5678c2ecf20Sopenharmony_ci
5688c2ecf20Sopenharmony_ci	}
5698c2ecf20Sopenharmony_ci
5708c2ecf20Sopenharmony_ci	cifs_put_tcp_super(sb);
5718c2ecf20Sopenharmony_ci#endif
5728c2ecf20Sopenharmony_ci	if (server->tcpStatus == CifsNeedNegotiate)
5738c2ecf20Sopenharmony_ci		mod_delayed_work(cifsiod_wq, &server->echo, 0);
5748c2ecf20Sopenharmony_ci
5758c2ecf20Sopenharmony_ci	wake_up(&server->response_q);
5768c2ecf20Sopenharmony_ci	return rc;
5778c2ecf20Sopenharmony_ci}
5788c2ecf20Sopenharmony_ci
5798c2ecf20Sopenharmony_cistatic void
5808c2ecf20Sopenharmony_cicifs_echo_request(struct work_struct *work)
5818c2ecf20Sopenharmony_ci{
5828c2ecf20Sopenharmony_ci	int rc;
5838c2ecf20Sopenharmony_ci	struct TCP_Server_Info *server = container_of(work,
5848c2ecf20Sopenharmony_ci					struct TCP_Server_Info, echo.work);
5858c2ecf20Sopenharmony_ci	unsigned long echo_interval;
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	/*
5888c2ecf20Sopenharmony_ci	 * If we need to renegotiate, set echo interval to zero to
5898c2ecf20Sopenharmony_ci	 * immediately call echo service where we can renegotiate.
5908c2ecf20Sopenharmony_ci	 */
5918c2ecf20Sopenharmony_ci	if (server->tcpStatus == CifsNeedNegotiate)
5928c2ecf20Sopenharmony_ci		echo_interval = 0;
5938c2ecf20Sopenharmony_ci	else
5948c2ecf20Sopenharmony_ci		echo_interval = server->echo_interval;
5958c2ecf20Sopenharmony_ci
5968c2ecf20Sopenharmony_ci	/*
5978c2ecf20Sopenharmony_ci	 * We cannot send an echo if it is disabled.
5988c2ecf20Sopenharmony_ci	 * Also, no need to ping if we got a response recently.
5998c2ecf20Sopenharmony_ci	 */
6008c2ecf20Sopenharmony_ci
6018c2ecf20Sopenharmony_ci	if (server->tcpStatus == CifsNeedReconnect ||
6028c2ecf20Sopenharmony_ci	    server->tcpStatus == CifsExiting ||
6038c2ecf20Sopenharmony_ci	    server->tcpStatus == CifsNew ||
6048c2ecf20Sopenharmony_ci	    (server->ops->can_echo && !server->ops->can_echo(server)) ||
6058c2ecf20Sopenharmony_ci	    time_before(jiffies, server->lstrp + echo_interval - HZ))
6068c2ecf20Sopenharmony_ci		goto requeue_echo;
6078c2ecf20Sopenharmony_ci
6088c2ecf20Sopenharmony_ci	rc = server->ops->echo ? server->ops->echo(server) : -ENOSYS;
6098c2ecf20Sopenharmony_ci	if (rc)
6108c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "Unable to send echo request to server: %s\n",
6118c2ecf20Sopenharmony_ci			 server->hostname);
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_cirequeue_echo:
6148c2ecf20Sopenharmony_ci	queue_delayed_work(cifsiod_wq, &server->echo, server->echo_interval);
6158c2ecf20Sopenharmony_ci}
6168c2ecf20Sopenharmony_ci
6178c2ecf20Sopenharmony_cistatic bool
6188c2ecf20Sopenharmony_ciallocate_buffers(struct TCP_Server_Info *server)
6198c2ecf20Sopenharmony_ci{
6208c2ecf20Sopenharmony_ci	if (!server->bigbuf) {
6218c2ecf20Sopenharmony_ci		server->bigbuf = (char *)cifs_buf_get();
6228c2ecf20Sopenharmony_ci		if (!server->bigbuf) {
6238c2ecf20Sopenharmony_ci			cifs_server_dbg(VFS, "No memory for large SMB response\n");
6248c2ecf20Sopenharmony_ci			msleep(3000);
6258c2ecf20Sopenharmony_ci			/* retry will check if exiting */
6268c2ecf20Sopenharmony_ci			return false;
6278c2ecf20Sopenharmony_ci		}
6288c2ecf20Sopenharmony_ci	} else if (server->large_buf) {
6298c2ecf20Sopenharmony_ci		/* we are reusing a dirty large buf, clear its start */
6308c2ecf20Sopenharmony_ci		memset(server->bigbuf, 0, HEADER_SIZE(server));
6318c2ecf20Sopenharmony_ci	}
6328c2ecf20Sopenharmony_ci
6338c2ecf20Sopenharmony_ci	if (!server->smallbuf) {
6348c2ecf20Sopenharmony_ci		server->smallbuf = (char *)cifs_small_buf_get();
6358c2ecf20Sopenharmony_ci		if (!server->smallbuf) {
6368c2ecf20Sopenharmony_ci			cifs_server_dbg(VFS, "No memory for SMB response\n");
6378c2ecf20Sopenharmony_ci			msleep(1000);
6388c2ecf20Sopenharmony_ci			/* retry will check if exiting */
6398c2ecf20Sopenharmony_ci			return false;
6408c2ecf20Sopenharmony_ci		}
6418c2ecf20Sopenharmony_ci		/* beginning of smb buffer is cleared in our buf_get */
6428c2ecf20Sopenharmony_ci	} else {
6438c2ecf20Sopenharmony_ci		/* if existing small buf clear beginning */
6448c2ecf20Sopenharmony_ci		memset(server->smallbuf, 0, HEADER_SIZE(server));
6458c2ecf20Sopenharmony_ci	}
6468c2ecf20Sopenharmony_ci
6478c2ecf20Sopenharmony_ci	return true;
6488c2ecf20Sopenharmony_ci}
6498c2ecf20Sopenharmony_ci
6508c2ecf20Sopenharmony_cistatic bool
6518c2ecf20Sopenharmony_ciserver_unresponsive(struct TCP_Server_Info *server)
6528c2ecf20Sopenharmony_ci{
6538c2ecf20Sopenharmony_ci	/*
6548c2ecf20Sopenharmony_ci	 * We need to wait 3 echo intervals to make sure we handle such
6558c2ecf20Sopenharmony_ci	 * situations right:
6568c2ecf20Sopenharmony_ci	 * 1s  client sends a normal SMB request
6578c2ecf20Sopenharmony_ci	 * 2s  client gets a response
6588c2ecf20Sopenharmony_ci	 * 30s echo workqueue job pops, and decides we got a response recently
6598c2ecf20Sopenharmony_ci	 *     and don't need to send another
6608c2ecf20Sopenharmony_ci	 * ...
6618c2ecf20Sopenharmony_ci	 * 65s kernel_recvmsg times out, and we see that we haven't gotten
6628c2ecf20Sopenharmony_ci	 *     a response in >60s.
6638c2ecf20Sopenharmony_ci	 */
6648c2ecf20Sopenharmony_ci	if ((server->tcpStatus == CifsGood ||
6658c2ecf20Sopenharmony_ci	    server->tcpStatus == CifsNeedNegotiate) &&
6668c2ecf20Sopenharmony_ci	    (!server->ops->can_echo || server->ops->can_echo(server)) &&
6678c2ecf20Sopenharmony_ci	    time_after(jiffies, server->lstrp + 3 * server->echo_interval)) {
6688c2ecf20Sopenharmony_ci		cifs_server_dbg(VFS, "has not responded in %lu seconds. Reconnecting...\n",
6698c2ecf20Sopenharmony_ci			 (3 * server->echo_interval) / HZ);
6708c2ecf20Sopenharmony_ci		cifs_reconnect(server);
6718c2ecf20Sopenharmony_ci		return true;
6728c2ecf20Sopenharmony_ci	}
6738c2ecf20Sopenharmony_ci
6748c2ecf20Sopenharmony_ci	return false;
6758c2ecf20Sopenharmony_ci}
6768c2ecf20Sopenharmony_ci
6778c2ecf20Sopenharmony_cistatic inline bool
6788c2ecf20Sopenharmony_cizero_credits(struct TCP_Server_Info *server)
6798c2ecf20Sopenharmony_ci{
6808c2ecf20Sopenharmony_ci	int val;
6818c2ecf20Sopenharmony_ci
6828c2ecf20Sopenharmony_ci	spin_lock(&server->req_lock);
6838c2ecf20Sopenharmony_ci	val = server->credits + server->echo_credits + server->oplock_credits;
6848c2ecf20Sopenharmony_ci	if (server->in_flight == 0 && val == 0) {
6858c2ecf20Sopenharmony_ci		spin_unlock(&server->req_lock);
6868c2ecf20Sopenharmony_ci		return true;
6878c2ecf20Sopenharmony_ci	}
6888c2ecf20Sopenharmony_ci	spin_unlock(&server->req_lock);
6898c2ecf20Sopenharmony_ci	return false;
6908c2ecf20Sopenharmony_ci}
6918c2ecf20Sopenharmony_ci
6928c2ecf20Sopenharmony_cistatic int
6938c2ecf20Sopenharmony_cicifs_readv_from_socket(struct TCP_Server_Info *server, struct msghdr *smb_msg)
6948c2ecf20Sopenharmony_ci{
6958c2ecf20Sopenharmony_ci	int length = 0;
6968c2ecf20Sopenharmony_ci	int total_read;
6978c2ecf20Sopenharmony_ci
6988c2ecf20Sopenharmony_ci	for (total_read = 0; msg_data_left(smb_msg); total_read += length) {
6998c2ecf20Sopenharmony_ci		try_to_freeze();
7008c2ecf20Sopenharmony_ci
7018c2ecf20Sopenharmony_ci		/* reconnect if no credits and no requests in flight */
7028c2ecf20Sopenharmony_ci		if (zero_credits(server)) {
7038c2ecf20Sopenharmony_ci			cifs_reconnect(server);
7048c2ecf20Sopenharmony_ci			return -ECONNABORTED;
7058c2ecf20Sopenharmony_ci		}
7068c2ecf20Sopenharmony_ci
7078c2ecf20Sopenharmony_ci		if (server_unresponsive(server))
7088c2ecf20Sopenharmony_ci			return -ECONNABORTED;
7098c2ecf20Sopenharmony_ci		if (cifs_rdma_enabled(server) && server->smbd_conn)
7108c2ecf20Sopenharmony_ci			length = smbd_recv(server->smbd_conn, smb_msg);
7118c2ecf20Sopenharmony_ci		else
7128c2ecf20Sopenharmony_ci			length = sock_recvmsg(server->ssocket, smb_msg, 0);
7138c2ecf20Sopenharmony_ci
7148c2ecf20Sopenharmony_ci		if (server->tcpStatus == CifsExiting)
7158c2ecf20Sopenharmony_ci			return -ESHUTDOWN;
7168c2ecf20Sopenharmony_ci
7178c2ecf20Sopenharmony_ci		if (server->tcpStatus == CifsNeedReconnect) {
7188c2ecf20Sopenharmony_ci			cifs_reconnect(server);
7198c2ecf20Sopenharmony_ci			return -ECONNABORTED;
7208c2ecf20Sopenharmony_ci		}
7218c2ecf20Sopenharmony_ci
7228c2ecf20Sopenharmony_ci		if (length == -ERESTARTSYS ||
7238c2ecf20Sopenharmony_ci		    length == -EAGAIN ||
7248c2ecf20Sopenharmony_ci		    length == -EINTR) {
7258c2ecf20Sopenharmony_ci			/*
7268c2ecf20Sopenharmony_ci			 * Minimum sleep to prevent looping, allowing socket
7278c2ecf20Sopenharmony_ci			 * to clear and app threads to set tcpStatus
7288c2ecf20Sopenharmony_ci			 * CifsNeedReconnect if server hung.
7298c2ecf20Sopenharmony_ci			 */
7308c2ecf20Sopenharmony_ci			usleep_range(1000, 2000);
7318c2ecf20Sopenharmony_ci			length = 0;
7328c2ecf20Sopenharmony_ci			continue;
7338c2ecf20Sopenharmony_ci		}
7348c2ecf20Sopenharmony_ci
7358c2ecf20Sopenharmony_ci		if (length <= 0) {
7368c2ecf20Sopenharmony_ci			cifs_dbg(FYI, "Received no data or error: %d\n", length);
7378c2ecf20Sopenharmony_ci			cifs_reconnect(server);
7388c2ecf20Sopenharmony_ci			return -ECONNABORTED;
7398c2ecf20Sopenharmony_ci		}
7408c2ecf20Sopenharmony_ci	}
7418c2ecf20Sopenharmony_ci	return total_read;
7428c2ecf20Sopenharmony_ci}
7438c2ecf20Sopenharmony_ci
7448c2ecf20Sopenharmony_ciint
7458c2ecf20Sopenharmony_cicifs_read_from_socket(struct TCP_Server_Info *server, char *buf,
7468c2ecf20Sopenharmony_ci		      unsigned int to_read)
7478c2ecf20Sopenharmony_ci{
7488c2ecf20Sopenharmony_ci	struct msghdr smb_msg = {};
7498c2ecf20Sopenharmony_ci	struct kvec iov = {.iov_base = buf, .iov_len = to_read};
7508c2ecf20Sopenharmony_ci	iov_iter_kvec(&smb_msg.msg_iter, READ, &iov, 1, to_read);
7518c2ecf20Sopenharmony_ci
7528c2ecf20Sopenharmony_ci	return cifs_readv_from_socket(server, &smb_msg);
7538c2ecf20Sopenharmony_ci}
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_cissize_t
7568c2ecf20Sopenharmony_cicifs_discard_from_socket(struct TCP_Server_Info *server, size_t to_read)
7578c2ecf20Sopenharmony_ci{
7588c2ecf20Sopenharmony_ci	struct msghdr smb_msg = {};
7598c2ecf20Sopenharmony_ci
7608c2ecf20Sopenharmony_ci	/*
7618c2ecf20Sopenharmony_ci	 *  iov_iter_discard already sets smb_msg.type and count and iov_offset
7628c2ecf20Sopenharmony_ci	 *  and cifs_readv_from_socket sets msg_control and msg_controllen
7638c2ecf20Sopenharmony_ci	 *  so little to initialize in struct msghdr
7648c2ecf20Sopenharmony_ci	 */
7658c2ecf20Sopenharmony_ci	iov_iter_discard(&smb_msg.msg_iter, READ, to_read);
7668c2ecf20Sopenharmony_ci
7678c2ecf20Sopenharmony_ci	return cifs_readv_from_socket(server, &smb_msg);
7688c2ecf20Sopenharmony_ci}
7698c2ecf20Sopenharmony_ci
7708c2ecf20Sopenharmony_ciint
7718c2ecf20Sopenharmony_cicifs_read_page_from_socket(struct TCP_Server_Info *server, struct page *page,
7728c2ecf20Sopenharmony_ci	unsigned int page_offset, unsigned int to_read)
7738c2ecf20Sopenharmony_ci{
7748c2ecf20Sopenharmony_ci	struct msghdr smb_msg = {};
7758c2ecf20Sopenharmony_ci	struct bio_vec bv = {
7768c2ecf20Sopenharmony_ci		.bv_page = page, .bv_len = to_read, .bv_offset = page_offset};
7778c2ecf20Sopenharmony_ci	iov_iter_bvec(&smb_msg.msg_iter, READ, &bv, 1, to_read);
7788c2ecf20Sopenharmony_ci	return cifs_readv_from_socket(server, &smb_msg);
7798c2ecf20Sopenharmony_ci}
7808c2ecf20Sopenharmony_ci
7818c2ecf20Sopenharmony_cistatic bool
7828c2ecf20Sopenharmony_ciis_smb_response(struct TCP_Server_Info *server, unsigned char type)
7838c2ecf20Sopenharmony_ci{
7848c2ecf20Sopenharmony_ci	/*
7858c2ecf20Sopenharmony_ci	 * The first byte big endian of the length field,
7868c2ecf20Sopenharmony_ci	 * is actually not part of the length but the type
7878c2ecf20Sopenharmony_ci	 * with the most common, zero, as regular data.
7888c2ecf20Sopenharmony_ci	 */
7898c2ecf20Sopenharmony_ci	switch (type) {
7908c2ecf20Sopenharmony_ci	case RFC1002_SESSION_MESSAGE:
7918c2ecf20Sopenharmony_ci		/* Regular SMB response */
7928c2ecf20Sopenharmony_ci		return true;
7938c2ecf20Sopenharmony_ci	case RFC1002_SESSION_KEEP_ALIVE:
7948c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "RFC 1002 session keep alive\n");
7958c2ecf20Sopenharmony_ci		break;
7968c2ecf20Sopenharmony_ci	case RFC1002_POSITIVE_SESSION_RESPONSE:
7978c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "RFC 1002 positive session response\n");
7988c2ecf20Sopenharmony_ci		break;
7998c2ecf20Sopenharmony_ci	case RFC1002_NEGATIVE_SESSION_RESPONSE:
8008c2ecf20Sopenharmony_ci		/*
8018c2ecf20Sopenharmony_ci		 * We get this from Windows 98 instead of an error on
8028c2ecf20Sopenharmony_ci		 * SMB negprot response.
8038c2ecf20Sopenharmony_ci		 */
8048c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "RFC 1002 negative session response\n");
8058c2ecf20Sopenharmony_ci		/* give server a second to clean up */
8068c2ecf20Sopenharmony_ci		msleep(1000);
8078c2ecf20Sopenharmony_ci		/*
8088c2ecf20Sopenharmony_ci		 * Always try 445 first on reconnect since we get NACK
8098c2ecf20Sopenharmony_ci		 * on some if we ever connected to port 139 (the NACK
8108c2ecf20Sopenharmony_ci		 * is since we do not begin with RFC1001 session
8118c2ecf20Sopenharmony_ci		 * initialize frame).
8128c2ecf20Sopenharmony_ci		 */
8138c2ecf20Sopenharmony_ci		cifs_set_port((struct sockaddr *)&server->dstaddr, CIFS_PORT);
8148c2ecf20Sopenharmony_ci		cifs_reconnect(server);
8158c2ecf20Sopenharmony_ci		break;
8168c2ecf20Sopenharmony_ci	default:
8178c2ecf20Sopenharmony_ci		cifs_server_dbg(VFS, "RFC 1002 unknown response type 0x%x\n", type);
8188c2ecf20Sopenharmony_ci		cifs_reconnect(server);
8198c2ecf20Sopenharmony_ci	}
8208c2ecf20Sopenharmony_ci
8218c2ecf20Sopenharmony_ci	return false;
8228c2ecf20Sopenharmony_ci}
8238c2ecf20Sopenharmony_ci
8248c2ecf20Sopenharmony_civoid
8258c2ecf20Sopenharmony_cidequeue_mid(struct mid_q_entry *mid, bool malformed)
8268c2ecf20Sopenharmony_ci{
8278c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_STATS2
8288c2ecf20Sopenharmony_ci	mid->when_received = jiffies;
8298c2ecf20Sopenharmony_ci#endif
8308c2ecf20Sopenharmony_ci	spin_lock(&GlobalMid_Lock);
8318c2ecf20Sopenharmony_ci	if (!malformed)
8328c2ecf20Sopenharmony_ci		mid->mid_state = MID_RESPONSE_RECEIVED;
8338c2ecf20Sopenharmony_ci	else
8348c2ecf20Sopenharmony_ci		mid->mid_state = MID_RESPONSE_MALFORMED;
8358c2ecf20Sopenharmony_ci	/*
8368c2ecf20Sopenharmony_ci	 * Trying to handle/dequeue a mid after the send_recv()
8378c2ecf20Sopenharmony_ci	 * function has finished processing it is a bug.
8388c2ecf20Sopenharmony_ci	 */
8398c2ecf20Sopenharmony_ci	if (mid->mid_flags & MID_DELETED)
8408c2ecf20Sopenharmony_ci		pr_warn_once("trying to dequeue a deleted mid\n");
8418c2ecf20Sopenharmony_ci	else {
8428c2ecf20Sopenharmony_ci		list_del_init(&mid->qhead);
8438c2ecf20Sopenharmony_ci		mid->mid_flags |= MID_DELETED;
8448c2ecf20Sopenharmony_ci	}
8458c2ecf20Sopenharmony_ci	spin_unlock(&GlobalMid_Lock);
8468c2ecf20Sopenharmony_ci}
8478c2ecf20Sopenharmony_ci
8488c2ecf20Sopenharmony_cistatic unsigned int
8498c2ecf20Sopenharmony_cismb2_get_credits_from_hdr(char *buffer, struct TCP_Server_Info *server)
8508c2ecf20Sopenharmony_ci{
8518c2ecf20Sopenharmony_ci	struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buffer;
8528c2ecf20Sopenharmony_ci
8538c2ecf20Sopenharmony_ci	/*
8548c2ecf20Sopenharmony_ci	 * SMB1 does not use credits.
8558c2ecf20Sopenharmony_ci	 */
8568c2ecf20Sopenharmony_ci	if (server->vals->header_preamble_size)
8578c2ecf20Sopenharmony_ci		return 0;
8588c2ecf20Sopenharmony_ci
8598c2ecf20Sopenharmony_ci	return le16_to_cpu(shdr->CreditRequest);
8608c2ecf20Sopenharmony_ci}
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_cistatic void
8638c2ecf20Sopenharmony_cihandle_mid(struct mid_q_entry *mid, struct TCP_Server_Info *server,
8648c2ecf20Sopenharmony_ci	   char *buf, int malformed)
8658c2ecf20Sopenharmony_ci{
8668c2ecf20Sopenharmony_ci	if (server->ops->check_trans2 &&
8678c2ecf20Sopenharmony_ci	    server->ops->check_trans2(mid, server, buf, malformed))
8688c2ecf20Sopenharmony_ci		return;
8698c2ecf20Sopenharmony_ci	mid->credits_received = smb2_get_credits_from_hdr(buf, server);
8708c2ecf20Sopenharmony_ci	mid->resp_buf = buf;
8718c2ecf20Sopenharmony_ci	mid->large_buf = server->large_buf;
8728c2ecf20Sopenharmony_ci	/* Was previous buf put in mpx struct for multi-rsp? */
8738c2ecf20Sopenharmony_ci	if (!mid->multiRsp) {
8748c2ecf20Sopenharmony_ci		/* smb buffer will be freed by user thread */
8758c2ecf20Sopenharmony_ci		if (server->large_buf)
8768c2ecf20Sopenharmony_ci			server->bigbuf = NULL;
8778c2ecf20Sopenharmony_ci		else
8788c2ecf20Sopenharmony_ci			server->smallbuf = NULL;
8798c2ecf20Sopenharmony_ci	}
8808c2ecf20Sopenharmony_ci	dequeue_mid(mid, malformed);
8818c2ecf20Sopenharmony_ci}
8828c2ecf20Sopenharmony_ci
8838c2ecf20Sopenharmony_cistatic void clean_demultiplex_info(struct TCP_Server_Info *server)
8848c2ecf20Sopenharmony_ci{
8858c2ecf20Sopenharmony_ci	int length;
8868c2ecf20Sopenharmony_ci
8878c2ecf20Sopenharmony_ci	/* take it off the list, if it's not already */
8888c2ecf20Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
8898c2ecf20Sopenharmony_ci	list_del_init(&server->tcp_ses_list);
8908c2ecf20Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
8918c2ecf20Sopenharmony_ci
8928c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&server->echo);
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	spin_lock(&GlobalMid_Lock);
8958c2ecf20Sopenharmony_ci	server->tcpStatus = CifsExiting;
8968c2ecf20Sopenharmony_ci	spin_unlock(&GlobalMid_Lock);
8978c2ecf20Sopenharmony_ci	wake_up_all(&server->response_q);
8988c2ecf20Sopenharmony_ci
8998c2ecf20Sopenharmony_ci	/* check if we have blocked requests that need to free */
9008c2ecf20Sopenharmony_ci	spin_lock(&server->req_lock);
9018c2ecf20Sopenharmony_ci	if (server->credits <= 0)
9028c2ecf20Sopenharmony_ci		server->credits = 1;
9038c2ecf20Sopenharmony_ci	spin_unlock(&server->req_lock);
9048c2ecf20Sopenharmony_ci	/*
9058c2ecf20Sopenharmony_ci	 * Although there should not be any requests blocked on this queue it
9068c2ecf20Sopenharmony_ci	 * can not hurt to be paranoid and try to wake up requests that may
9078c2ecf20Sopenharmony_ci	 * haven been blocked when more than 50 at time were on the wire to the
9088c2ecf20Sopenharmony_ci	 * same server - they now will see the session is in exit state and get
9098c2ecf20Sopenharmony_ci	 * out of SendReceive.
9108c2ecf20Sopenharmony_ci	 */
9118c2ecf20Sopenharmony_ci	wake_up_all(&server->request_q);
9128c2ecf20Sopenharmony_ci	/* give those requests time to exit */
9138c2ecf20Sopenharmony_ci	msleep(125);
9148c2ecf20Sopenharmony_ci	if (cifs_rdma_enabled(server))
9158c2ecf20Sopenharmony_ci		smbd_destroy(server);
9168c2ecf20Sopenharmony_ci	if (server->ssocket) {
9178c2ecf20Sopenharmony_ci		sock_release(server->ssocket);
9188c2ecf20Sopenharmony_ci		server->ssocket = NULL;
9198c2ecf20Sopenharmony_ci	}
9208c2ecf20Sopenharmony_ci
9218c2ecf20Sopenharmony_ci	if (!list_empty(&server->pending_mid_q)) {
9228c2ecf20Sopenharmony_ci		struct list_head dispose_list;
9238c2ecf20Sopenharmony_ci		struct mid_q_entry *mid_entry;
9248c2ecf20Sopenharmony_ci		struct list_head *tmp, *tmp2;
9258c2ecf20Sopenharmony_ci
9268c2ecf20Sopenharmony_ci		INIT_LIST_HEAD(&dispose_list);
9278c2ecf20Sopenharmony_ci		spin_lock(&GlobalMid_Lock);
9288c2ecf20Sopenharmony_ci		list_for_each_safe(tmp, tmp2, &server->pending_mid_q) {
9298c2ecf20Sopenharmony_ci			mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
9308c2ecf20Sopenharmony_ci			cifs_dbg(FYI, "Clearing mid 0x%llx\n", mid_entry->mid);
9318c2ecf20Sopenharmony_ci			kref_get(&mid_entry->refcount);
9328c2ecf20Sopenharmony_ci			mid_entry->mid_state = MID_SHUTDOWN;
9338c2ecf20Sopenharmony_ci			list_move(&mid_entry->qhead, &dispose_list);
9348c2ecf20Sopenharmony_ci			mid_entry->mid_flags |= MID_DELETED;
9358c2ecf20Sopenharmony_ci		}
9368c2ecf20Sopenharmony_ci		spin_unlock(&GlobalMid_Lock);
9378c2ecf20Sopenharmony_ci
9388c2ecf20Sopenharmony_ci		/* now walk dispose list and issue callbacks */
9398c2ecf20Sopenharmony_ci		list_for_each_safe(tmp, tmp2, &dispose_list) {
9408c2ecf20Sopenharmony_ci			mid_entry = list_entry(tmp, struct mid_q_entry, qhead);
9418c2ecf20Sopenharmony_ci			cifs_dbg(FYI, "Callback mid 0x%llx\n", mid_entry->mid);
9428c2ecf20Sopenharmony_ci			list_del_init(&mid_entry->qhead);
9438c2ecf20Sopenharmony_ci			mid_entry->callback(mid_entry);
9448c2ecf20Sopenharmony_ci			cifs_mid_q_entry_release(mid_entry);
9458c2ecf20Sopenharmony_ci		}
9468c2ecf20Sopenharmony_ci		/* 1/8th of sec is more than enough time for them to exit */
9478c2ecf20Sopenharmony_ci		msleep(125);
9488c2ecf20Sopenharmony_ci	}
9498c2ecf20Sopenharmony_ci
9508c2ecf20Sopenharmony_ci	if (!list_empty(&server->pending_mid_q)) {
9518c2ecf20Sopenharmony_ci		/*
9528c2ecf20Sopenharmony_ci		 * mpx threads have not exited yet give them at least the smb
9538c2ecf20Sopenharmony_ci		 * send timeout time for long ops.
9548c2ecf20Sopenharmony_ci		 *
9558c2ecf20Sopenharmony_ci		 * Due to delays on oplock break requests, we need to wait at
9568c2ecf20Sopenharmony_ci		 * least 45 seconds before giving up on a request getting a
9578c2ecf20Sopenharmony_ci		 * response and going ahead and killing cifsd.
9588c2ecf20Sopenharmony_ci		 */
9598c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "Wait for exit from demultiplex thread\n");
9608c2ecf20Sopenharmony_ci		msleep(46000);
9618c2ecf20Sopenharmony_ci		/*
9628c2ecf20Sopenharmony_ci		 * If threads still have not exited they are probably never
9638c2ecf20Sopenharmony_ci		 * coming home not much else we can do but free the memory.
9648c2ecf20Sopenharmony_ci		 */
9658c2ecf20Sopenharmony_ci	}
9668c2ecf20Sopenharmony_ci
9678c2ecf20Sopenharmony_ci	kfree(server->hostname);
9688c2ecf20Sopenharmony_ci	kfree(server);
9698c2ecf20Sopenharmony_ci
9708c2ecf20Sopenharmony_ci	length = atomic_dec_return(&tcpSesAllocCount);
9718c2ecf20Sopenharmony_ci	if (length > 0)
9728c2ecf20Sopenharmony_ci		mempool_resize(cifs_req_poolp, length + cifs_min_rcv);
9738c2ecf20Sopenharmony_ci}
9748c2ecf20Sopenharmony_ci
9758c2ecf20Sopenharmony_cistatic int
9768c2ecf20Sopenharmony_cistandard_receive3(struct TCP_Server_Info *server, struct mid_q_entry *mid)
9778c2ecf20Sopenharmony_ci{
9788c2ecf20Sopenharmony_ci	int length;
9798c2ecf20Sopenharmony_ci	char *buf = server->smallbuf;
9808c2ecf20Sopenharmony_ci	unsigned int pdu_length = server->pdu_size;
9818c2ecf20Sopenharmony_ci
9828c2ecf20Sopenharmony_ci	/* make sure this will fit in a large buffer */
9838c2ecf20Sopenharmony_ci	if (pdu_length > CIFSMaxBufSize + MAX_HEADER_SIZE(server) -
9848c2ecf20Sopenharmony_ci		server->vals->header_preamble_size) {
9858c2ecf20Sopenharmony_ci		cifs_server_dbg(VFS, "SMB response too long (%u bytes)\n", pdu_length);
9868c2ecf20Sopenharmony_ci		cifs_reconnect(server);
9878c2ecf20Sopenharmony_ci		return -ECONNABORTED;
9888c2ecf20Sopenharmony_ci	}
9898c2ecf20Sopenharmony_ci
9908c2ecf20Sopenharmony_ci	/* switch to large buffer if too big for a small one */
9918c2ecf20Sopenharmony_ci	if (pdu_length > MAX_CIFS_SMALL_BUFFER_SIZE - 4) {
9928c2ecf20Sopenharmony_ci		server->large_buf = true;
9938c2ecf20Sopenharmony_ci		memcpy(server->bigbuf, buf, server->total_read);
9948c2ecf20Sopenharmony_ci		buf = server->bigbuf;
9958c2ecf20Sopenharmony_ci	}
9968c2ecf20Sopenharmony_ci
9978c2ecf20Sopenharmony_ci	/* now read the rest */
9988c2ecf20Sopenharmony_ci	length = cifs_read_from_socket(server, buf + HEADER_SIZE(server) - 1,
9998c2ecf20Sopenharmony_ci				       pdu_length - HEADER_SIZE(server) + 1
10008c2ecf20Sopenharmony_ci				       + server->vals->header_preamble_size);
10018c2ecf20Sopenharmony_ci
10028c2ecf20Sopenharmony_ci	if (length < 0)
10038c2ecf20Sopenharmony_ci		return length;
10048c2ecf20Sopenharmony_ci	server->total_read += length;
10058c2ecf20Sopenharmony_ci
10068c2ecf20Sopenharmony_ci	dump_smb(buf, server->total_read);
10078c2ecf20Sopenharmony_ci
10088c2ecf20Sopenharmony_ci	return cifs_handle_standard(server, mid);
10098c2ecf20Sopenharmony_ci}
10108c2ecf20Sopenharmony_ci
10118c2ecf20Sopenharmony_ciint
10128c2ecf20Sopenharmony_cicifs_handle_standard(struct TCP_Server_Info *server, struct mid_q_entry *mid)
10138c2ecf20Sopenharmony_ci{
10148c2ecf20Sopenharmony_ci	char *buf = server->large_buf ? server->bigbuf : server->smallbuf;
10158c2ecf20Sopenharmony_ci	int length;
10168c2ecf20Sopenharmony_ci
10178c2ecf20Sopenharmony_ci	/*
10188c2ecf20Sopenharmony_ci	 * We know that we received enough to get to the MID as we
10198c2ecf20Sopenharmony_ci	 * checked the pdu_length earlier. Now check to see
10208c2ecf20Sopenharmony_ci	 * if the rest of the header is OK. We borrow the length
10218c2ecf20Sopenharmony_ci	 * var for the rest of the loop to avoid a new stack var.
10228c2ecf20Sopenharmony_ci	 *
10238c2ecf20Sopenharmony_ci	 * 48 bytes is enough to display the header and a little bit
10248c2ecf20Sopenharmony_ci	 * into the payload for debugging purposes.
10258c2ecf20Sopenharmony_ci	 */
10268c2ecf20Sopenharmony_ci	length = server->ops->check_message(buf, server->total_read, server);
10278c2ecf20Sopenharmony_ci	if (length != 0)
10288c2ecf20Sopenharmony_ci		cifs_dump_mem("Bad SMB: ", buf,
10298c2ecf20Sopenharmony_ci			min_t(unsigned int, server->total_read, 48));
10308c2ecf20Sopenharmony_ci
10318c2ecf20Sopenharmony_ci	if (server->ops->is_session_expired &&
10328c2ecf20Sopenharmony_ci	    server->ops->is_session_expired(buf)) {
10338c2ecf20Sopenharmony_ci		cifs_reconnect(server);
10348c2ecf20Sopenharmony_ci		return -1;
10358c2ecf20Sopenharmony_ci	}
10368c2ecf20Sopenharmony_ci
10378c2ecf20Sopenharmony_ci	if (server->ops->is_status_pending &&
10388c2ecf20Sopenharmony_ci	    server->ops->is_status_pending(buf, server))
10398c2ecf20Sopenharmony_ci		return -1;
10408c2ecf20Sopenharmony_ci
10418c2ecf20Sopenharmony_ci	if (!mid)
10428c2ecf20Sopenharmony_ci		return length;
10438c2ecf20Sopenharmony_ci
10448c2ecf20Sopenharmony_ci	handle_mid(mid, server, buf, length);
10458c2ecf20Sopenharmony_ci	return 0;
10468c2ecf20Sopenharmony_ci}
10478c2ecf20Sopenharmony_ci
10488c2ecf20Sopenharmony_cistatic void
10498c2ecf20Sopenharmony_cismb2_add_credits_from_hdr(char *buffer, struct TCP_Server_Info *server)
10508c2ecf20Sopenharmony_ci{
10518c2ecf20Sopenharmony_ci	struct smb2_sync_hdr *shdr = (struct smb2_sync_hdr *)buffer;
10528c2ecf20Sopenharmony_ci
10538c2ecf20Sopenharmony_ci	/*
10548c2ecf20Sopenharmony_ci	 * SMB1 does not use credits.
10558c2ecf20Sopenharmony_ci	 */
10568c2ecf20Sopenharmony_ci	if (server->vals->header_preamble_size)
10578c2ecf20Sopenharmony_ci		return;
10588c2ecf20Sopenharmony_ci
10598c2ecf20Sopenharmony_ci	if (shdr->CreditRequest) {
10608c2ecf20Sopenharmony_ci		spin_lock(&server->req_lock);
10618c2ecf20Sopenharmony_ci		server->credits += le16_to_cpu(shdr->CreditRequest);
10628c2ecf20Sopenharmony_ci		spin_unlock(&server->req_lock);
10638c2ecf20Sopenharmony_ci		wake_up(&server->request_q);
10648c2ecf20Sopenharmony_ci	}
10658c2ecf20Sopenharmony_ci}
10668c2ecf20Sopenharmony_ci
10678c2ecf20Sopenharmony_ci
10688c2ecf20Sopenharmony_cistatic int
10698c2ecf20Sopenharmony_cicifs_demultiplex_thread(void *p)
10708c2ecf20Sopenharmony_ci{
10718c2ecf20Sopenharmony_ci	int i, num_mids, length;
10728c2ecf20Sopenharmony_ci	struct TCP_Server_Info *server = p;
10738c2ecf20Sopenharmony_ci	unsigned int pdu_length;
10748c2ecf20Sopenharmony_ci	unsigned int next_offset;
10758c2ecf20Sopenharmony_ci	char *buf = NULL;
10768c2ecf20Sopenharmony_ci	struct task_struct *task_to_wake = NULL;
10778c2ecf20Sopenharmony_ci	struct mid_q_entry *mids[MAX_COMPOUND];
10788c2ecf20Sopenharmony_ci	char *bufs[MAX_COMPOUND];
10798c2ecf20Sopenharmony_ci	unsigned int noreclaim_flag, num_io_timeout = 0;
10808c2ecf20Sopenharmony_ci
10818c2ecf20Sopenharmony_ci	noreclaim_flag = memalloc_noreclaim_save();
10828c2ecf20Sopenharmony_ci	cifs_dbg(FYI, "Demultiplex PID: %d\n", task_pid_nr(current));
10838c2ecf20Sopenharmony_ci
10848c2ecf20Sopenharmony_ci	length = atomic_inc_return(&tcpSesAllocCount);
10858c2ecf20Sopenharmony_ci	if (length > 1)
10868c2ecf20Sopenharmony_ci		mempool_resize(cifs_req_poolp, length + cifs_min_rcv);
10878c2ecf20Sopenharmony_ci
10888c2ecf20Sopenharmony_ci	set_freezable();
10898c2ecf20Sopenharmony_ci	allow_kernel_signal(SIGKILL);
10908c2ecf20Sopenharmony_ci	while (server->tcpStatus != CifsExiting) {
10918c2ecf20Sopenharmony_ci		if (try_to_freeze())
10928c2ecf20Sopenharmony_ci			continue;
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci		if (!allocate_buffers(server))
10958c2ecf20Sopenharmony_ci			continue;
10968c2ecf20Sopenharmony_ci
10978c2ecf20Sopenharmony_ci		server->large_buf = false;
10988c2ecf20Sopenharmony_ci		buf = server->smallbuf;
10998c2ecf20Sopenharmony_ci		pdu_length = 4; /* enough to get RFC1001 header */
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci		length = cifs_read_from_socket(server, buf, pdu_length);
11028c2ecf20Sopenharmony_ci		if (length < 0)
11038c2ecf20Sopenharmony_ci			continue;
11048c2ecf20Sopenharmony_ci
11058c2ecf20Sopenharmony_ci		if (server->vals->header_preamble_size == 0)
11068c2ecf20Sopenharmony_ci			server->total_read = 0;
11078c2ecf20Sopenharmony_ci		else
11088c2ecf20Sopenharmony_ci			server->total_read = length;
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_ci		/*
11118c2ecf20Sopenharmony_ci		 * The right amount was read from socket - 4 bytes,
11128c2ecf20Sopenharmony_ci		 * so we can now interpret the length field.
11138c2ecf20Sopenharmony_ci		 */
11148c2ecf20Sopenharmony_ci		pdu_length = get_rfc1002_length(buf);
11158c2ecf20Sopenharmony_ci
11168c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "RFC1002 header 0x%x\n", pdu_length);
11178c2ecf20Sopenharmony_ci		if (!is_smb_response(server, buf[0]))
11188c2ecf20Sopenharmony_ci			continue;
11198c2ecf20Sopenharmony_cinext_pdu:
11208c2ecf20Sopenharmony_ci		server->pdu_size = pdu_length;
11218c2ecf20Sopenharmony_ci
11228c2ecf20Sopenharmony_ci		/* make sure we have enough to get to the MID */
11238c2ecf20Sopenharmony_ci		if (server->pdu_size < HEADER_SIZE(server) - 1 -
11248c2ecf20Sopenharmony_ci		    server->vals->header_preamble_size) {
11258c2ecf20Sopenharmony_ci			cifs_server_dbg(VFS, "SMB response too short (%u bytes)\n",
11268c2ecf20Sopenharmony_ci				 server->pdu_size);
11278c2ecf20Sopenharmony_ci			cifs_reconnect(server);
11288c2ecf20Sopenharmony_ci			continue;
11298c2ecf20Sopenharmony_ci		}
11308c2ecf20Sopenharmony_ci
11318c2ecf20Sopenharmony_ci		/* read down to the MID */
11328c2ecf20Sopenharmony_ci		length = cifs_read_from_socket(server,
11338c2ecf20Sopenharmony_ci			     buf + server->vals->header_preamble_size,
11348c2ecf20Sopenharmony_ci			     HEADER_SIZE(server) - 1
11358c2ecf20Sopenharmony_ci			     - server->vals->header_preamble_size);
11368c2ecf20Sopenharmony_ci		if (length < 0)
11378c2ecf20Sopenharmony_ci			continue;
11388c2ecf20Sopenharmony_ci		server->total_read += length;
11398c2ecf20Sopenharmony_ci
11408c2ecf20Sopenharmony_ci		if (server->ops->next_header) {
11418c2ecf20Sopenharmony_ci			next_offset = server->ops->next_header(buf);
11428c2ecf20Sopenharmony_ci			if (next_offset)
11438c2ecf20Sopenharmony_ci				server->pdu_size = next_offset;
11448c2ecf20Sopenharmony_ci		}
11458c2ecf20Sopenharmony_ci
11468c2ecf20Sopenharmony_ci		memset(mids, 0, sizeof(mids));
11478c2ecf20Sopenharmony_ci		memset(bufs, 0, sizeof(bufs));
11488c2ecf20Sopenharmony_ci		num_mids = 0;
11498c2ecf20Sopenharmony_ci
11508c2ecf20Sopenharmony_ci		if (server->ops->is_transform_hdr &&
11518c2ecf20Sopenharmony_ci		    server->ops->receive_transform &&
11528c2ecf20Sopenharmony_ci		    server->ops->is_transform_hdr(buf)) {
11538c2ecf20Sopenharmony_ci			length = server->ops->receive_transform(server,
11548c2ecf20Sopenharmony_ci								mids,
11558c2ecf20Sopenharmony_ci								bufs,
11568c2ecf20Sopenharmony_ci								&num_mids);
11578c2ecf20Sopenharmony_ci		} else {
11588c2ecf20Sopenharmony_ci			mids[0] = server->ops->find_mid(server, buf);
11598c2ecf20Sopenharmony_ci			bufs[0] = buf;
11608c2ecf20Sopenharmony_ci			num_mids = 1;
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci			if (!mids[0] || !mids[0]->receive)
11638c2ecf20Sopenharmony_ci				length = standard_receive3(server, mids[0]);
11648c2ecf20Sopenharmony_ci			else
11658c2ecf20Sopenharmony_ci				length = mids[0]->receive(server, mids[0]);
11668c2ecf20Sopenharmony_ci		}
11678c2ecf20Sopenharmony_ci
11688c2ecf20Sopenharmony_ci		if (length < 0) {
11698c2ecf20Sopenharmony_ci			for (i = 0; i < num_mids; i++)
11708c2ecf20Sopenharmony_ci				if (mids[i])
11718c2ecf20Sopenharmony_ci					cifs_mid_q_entry_release(mids[i]);
11728c2ecf20Sopenharmony_ci			continue;
11738c2ecf20Sopenharmony_ci		}
11748c2ecf20Sopenharmony_ci
11758c2ecf20Sopenharmony_ci		if (server->ops->is_status_io_timeout &&
11768c2ecf20Sopenharmony_ci		    server->ops->is_status_io_timeout(buf)) {
11778c2ecf20Sopenharmony_ci			num_io_timeout++;
11788c2ecf20Sopenharmony_ci			if (num_io_timeout > NUM_STATUS_IO_TIMEOUT) {
11798c2ecf20Sopenharmony_ci				cifs_reconnect(server);
11808c2ecf20Sopenharmony_ci				num_io_timeout = 0;
11818c2ecf20Sopenharmony_ci				continue;
11828c2ecf20Sopenharmony_ci			}
11838c2ecf20Sopenharmony_ci		}
11848c2ecf20Sopenharmony_ci
11858c2ecf20Sopenharmony_ci		server->lstrp = jiffies;
11868c2ecf20Sopenharmony_ci
11878c2ecf20Sopenharmony_ci		for (i = 0; i < num_mids; i++) {
11888c2ecf20Sopenharmony_ci			if (mids[i] != NULL) {
11898c2ecf20Sopenharmony_ci				mids[i]->resp_buf_size = server->pdu_size;
11908c2ecf20Sopenharmony_ci
11918c2ecf20Sopenharmony_ci				if (!mids[i]->multiRsp || mids[i]->multiEnd)
11928c2ecf20Sopenharmony_ci					mids[i]->callback(mids[i]);
11938c2ecf20Sopenharmony_ci
11948c2ecf20Sopenharmony_ci				cifs_mid_q_entry_release(mids[i]);
11958c2ecf20Sopenharmony_ci			} else if (server->ops->is_oplock_break &&
11968c2ecf20Sopenharmony_ci				   server->ops->is_oplock_break(bufs[i],
11978c2ecf20Sopenharmony_ci								server)) {
11988c2ecf20Sopenharmony_ci				smb2_add_credits_from_hdr(bufs[i], server);
11998c2ecf20Sopenharmony_ci				cifs_dbg(FYI, "Received oplock break\n");
12008c2ecf20Sopenharmony_ci			} else {
12018c2ecf20Sopenharmony_ci				cifs_server_dbg(VFS, "No task to wake, unknown frame received! NumMids %d\n",
12028c2ecf20Sopenharmony_ci						atomic_read(&midCount));
12038c2ecf20Sopenharmony_ci				cifs_dump_mem("Received Data is: ", bufs[i],
12048c2ecf20Sopenharmony_ci					      HEADER_SIZE(server));
12058c2ecf20Sopenharmony_ci				smb2_add_credits_from_hdr(bufs[i], server);
12068c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_DEBUG2
12078c2ecf20Sopenharmony_ci				if (server->ops->dump_detail)
12088c2ecf20Sopenharmony_ci					server->ops->dump_detail(bufs[i],
12098c2ecf20Sopenharmony_ci								 server);
12108c2ecf20Sopenharmony_ci				cifs_dump_mids(server);
12118c2ecf20Sopenharmony_ci#endif /* CIFS_DEBUG2 */
12128c2ecf20Sopenharmony_ci			}
12138c2ecf20Sopenharmony_ci		}
12148c2ecf20Sopenharmony_ci
12158c2ecf20Sopenharmony_ci		if (pdu_length > server->pdu_size) {
12168c2ecf20Sopenharmony_ci			if (!allocate_buffers(server))
12178c2ecf20Sopenharmony_ci				continue;
12188c2ecf20Sopenharmony_ci			pdu_length -= server->pdu_size;
12198c2ecf20Sopenharmony_ci			server->total_read = 0;
12208c2ecf20Sopenharmony_ci			server->large_buf = false;
12218c2ecf20Sopenharmony_ci			buf = server->smallbuf;
12228c2ecf20Sopenharmony_ci			goto next_pdu;
12238c2ecf20Sopenharmony_ci		}
12248c2ecf20Sopenharmony_ci	} /* end while !EXITING */
12258c2ecf20Sopenharmony_ci
12268c2ecf20Sopenharmony_ci	/* buffer usually freed in free_mid - need to free it here on exit */
12278c2ecf20Sopenharmony_ci	cifs_buf_release(server->bigbuf);
12288c2ecf20Sopenharmony_ci	if (server->smallbuf) /* no sense logging a debug message if NULL */
12298c2ecf20Sopenharmony_ci		cifs_small_buf_release(server->smallbuf);
12308c2ecf20Sopenharmony_ci
12318c2ecf20Sopenharmony_ci	task_to_wake = xchg(&server->tsk, NULL);
12328c2ecf20Sopenharmony_ci	clean_demultiplex_info(server);
12338c2ecf20Sopenharmony_ci
12348c2ecf20Sopenharmony_ci	/* if server->tsk was NULL then wait for a signal before exiting */
12358c2ecf20Sopenharmony_ci	if (!task_to_wake) {
12368c2ecf20Sopenharmony_ci		set_current_state(TASK_INTERRUPTIBLE);
12378c2ecf20Sopenharmony_ci		while (!signal_pending(current)) {
12388c2ecf20Sopenharmony_ci			schedule();
12398c2ecf20Sopenharmony_ci			set_current_state(TASK_INTERRUPTIBLE);
12408c2ecf20Sopenharmony_ci		}
12418c2ecf20Sopenharmony_ci		set_current_state(TASK_RUNNING);
12428c2ecf20Sopenharmony_ci	}
12438c2ecf20Sopenharmony_ci
12448c2ecf20Sopenharmony_ci	memalloc_noreclaim_restore(noreclaim_flag);
12458c2ecf20Sopenharmony_ci	module_put_and_exit(0);
12468c2ecf20Sopenharmony_ci}
12478c2ecf20Sopenharmony_ci
12488c2ecf20Sopenharmony_ci/* extract the host portion of the UNC string */
12498c2ecf20Sopenharmony_cistatic char *
12508c2ecf20Sopenharmony_ciextract_hostname(const char *unc)
12518c2ecf20Sopenharmony_ci{
12528c2ecf20Sopenharmony_ci	const char *src;
12538c2ecf20Sopenharmony_ci	char *dst, *delim;
12548c2ecf20Sopenharmony_ci	unsigned int len;
12558c2ecf20Sopenharmony_ci
12568c2ecf20Sopenharmony_ci	/* skip double chars at beginning of string */
12578c2ecf20Sopenharmony_ci	/* BB: check validity of these bytes? */
12588c2ecf20Sopenharmony_ci	if (strlen(unc) < 3)
12598c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
12608c2ecf20Sopenharmony_ci	for (src = unc; *src && *src == '\\'; src++)
12618c2ecf20Sopenharmony_ci		;
12628c2ecf20Sopenharmony_ci	if (!*src)
12638c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_ci	/* delimiter between hostname and sharename is always '\\' now */
12668c2ecf20Sopenharmony_ci	delim = strchr(src, '\\');
12678c2ecf20Sopenharmony_ci	if (!delim)
12688c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
12698c2ecf20Sopenharmony_ci
12708c2ecf20Sopenharmony_ci	len = delim - src;
12718c2ecf20Sopenharmony_ci	dst = kmalloc((len + 1), GFP_KERNEL);
12728c2ecf20Sopenharmony_ci	if (dst == NULL)
12738c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ci	memcpy(dst, src, len);
12768c2ecf20Sopenharmony_ci	dst[len] = '\0';
12778c2ecf20Sopenharmony_ci
12788c2ecf20Sopenharmony_ci	return dst;
12798c2ecf20Sopenharmony_ci}
12808c2ecf20Sopenharmony_ci
12818c2ecf20Sopenharmony_cistatic int get_option_ul(substring_t args[], unsigned long *option)
12828c2ecf20Sopenharmony_ci{
12838c2ecf20Sopenharmony_ci	int rc;
12848c2ecf20Sopenharmony_ci	char *string;
12858c2ecf20Sopenharmony_ci
12868c2ecf20Sopenharmony_ci	string = match_strdup(args);
12878c2ecf20Sopenharmony_ci	if (string == NULL)
12888c2ecf20Sopenharmony_ci		return -ENOMEM;
12898c2ecf20Sopenharmony_ci	rc = kstrtoul(string, 0, option);
12908c2ecf20Sopenharmony_ci	kfree(string);
12918c2ecf20Sopenharmony_ci
12928c2ecf20Sopenharmony_ci	return rc;
12938c2ecf20Sopenharmony_ci}
12948c2ecf20Sopenharmony_ci
12958c2ecf20Sopenharmony_cistatic int get_option_uid(substring_t args[], kuid_t *result)
12968c2ecf20Sopenharmony_ci{
12978c2ecf20Sopenharmony_ci	unsigned long value;
12988c2ecf20Sopenharmony_ci	kuid_t uid;
12998c2ecf20Sopenharmony_ci	int rc;
13008c2ecf20Sopenharmony_ci
13018c2ecf20Sopenharmony_ci	rc = get_option_ul(args, &value);
13028c2ecf20Sopenharmony_ci	if (rc)
13038c2ecf20Sopenharmony_ci		return rc;
13048c2ecf20Sopenharmony_ci
13058c2ecf20Sopenharmony_ci	uid = make_kuid(current_user_ns(), value);
13068c2ecf20Sopenharmony_ci	if (!uid_valid(uid))
13078c2ecf20Sopenharmony_ci		return -EINVAL;
13088c2ecf20Sopenharmony_ci
13098c2ecf20Sopenharmony_ci	*result = uid;
13108c2ecf20Sopenharmony_ci	return 0;
13118c2ecf20Sopenharmony_ci}
13128c2ecf20Sopenharmony_ci
13138c2ecf20Sopenharmony_cistatic int get_option_gid(substring_t args[], kgid_t *result)
13148c2ecf20Sopenharmony_ci{
13158c2ecf20Sopenharmony_ci	unsigned long value;
13168c2ecf20Sopenharmony_ci	kgid_t gid;
13178c2ecf20Sopenharmony_ci	int rc;
13188c2ecf20Sopenharmony_ci
13198c2ecf20Sopenharmony_ci	rc = get_option_ul(args, &value);
13208c2ecf20Sopenharmony_ci	if (rc)
13218c2ecf20Sopenharmony_ci		return rc;
13228c2ecf20Sopenharmony_ci
13238c2ecf20Sopenharmony_ci	gid = make_kgid(current_user_ns(), value);
13248c2ecf20Sopenharmony_ci	if (!gid_valid(gid))
13258c2ecf20Sopenharmony_ci		return -EINVAL;
13268c2ecf20Sopenharmony_ci
13278c2ecf20Sopenharmony_ci	*result = gid;
13288c2ecf20Sopenharmony_ci	return 0;
13298c2ecf20Sopenharmony_ci}
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_ci/*
13328c2ecf20Sopenharmony_ci * Parse a devname into substrings and populate the vol->UNC and vol->prepath
13338c2ecf20Sopenharmony_ci * fields with the result. Returns 0 on success and an error otherwise.
13348c2ecf20Sopenharmony_ci */
13358c2ecf20Sopenharmony_cistatic int
13368c2ecf20Sopenharmony_cicifs_parse_devname(const char *devname, struct smb_vol *vol)
13378c2ecf20Sopenharmony_ci{
13388c2ecf20Sopenharmony_ci	char *pos;
13398c2ecf20Sopenharmony_ci	const char *delims = "/\\";
13408c2ecf20Sopenharmony_ci	size_t len;
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_ci	if (unlikely(!devname || !*devname)) {
13438c2ecf20Sopenharmony_ci		cifs_dbg(VFS, "Device name not specified\n");
13448c2ecf20Sopenharmony_ci		return -EINVAL;
13458c2ecf20Sopenharmony_ci	}
13468c2ecf20Sopenharmony_ci
13478c2ecf20Sopenharmony_ci	/* make sure we have a valid UNC double delimiter prefix */
13488c2ecf20Sopenharmony_ci	len = strspn(devname, delims);
13498c2ecf20Sopenharmony_ci	if (len != 2)
13508c2ecf20Sopenharmony_ci		return -EINVAL;
13518c2ecf20Sopenharmony_ci
13528c2ecf20Sopenharmony_ci	/* find delimiter between host and sharename */
13538c2ecf20Sopenharmony_ci	pos = strpbrk(devname + 2, delims);
13548c2ecf20Sopenharmony_ci	if (!pos)
13558c2ecf20Sopenharmony_ci		return -EINVAL;
13568c2ecf20Sopenharmony_ci
13578c2ecf20Sopenharmony_ci	/* skip past delimiter */
13588c2ecf20Sopenharmony_ci	++pos;
13598c2ecf20Sopenharmony_ci
13608c2ecf20Sopenharmony_ci	/* now go until next delimiter or end of string */
13618c2ecf20Sopenharmony_ci	len = strcspn(pos, delims);
13628c2ecf20Sopenharmony_ci
13638c2ecf20Sopenharmony_ci	/* move "pos" up to delimiter or NULL */
13648c2ecf20Sopenharmony_ci	pos += len;
13658c2ecf20Sopenharmony_ci	vol->UNC = kstrndup(devname, pos - devname, GFP_KERNEL);
13668c2ecf20Sopenharmony_ci	if (!vol->UNC)
13678c2ecf20Sopenharmony_ci		return -ENOMEM;
13688c2ecf20Sopenharmony_ci
13698c2ecf20Sopenharmony_ci	convert_delimiter(vol->UNC, '\\');
13708c2ecf20Sopenharmony_ci
13718c2ecf20Sopenharmony_ci	/* skip any delimiter */
13728c2ecf20Sopenharmony_ci	if (*pos == '/' || *pos == '\\')
13738c2ecf20Sopenharmony_ci		pos++;
13748c2ecf20Sopenharmony_ci
13758c2ecf20Sopenharmony_ci	/* If pos is NULL then no prepath */
13768c2ecf20Sopenharmony_ci	if (!*pos)
13778c2ecf20Sopenharmony_ci		return 0;
13788c2ecf20Sopenharmony_ci
13798c2ecf20Sopenharmony_ci	vol->prepath = kstrdup(pos, GFP_KERNEL);
13808c2ecf20Sopenharmony_ci	if (!vol->prepath)
13818c2ecf20Sopenharmony_ci		return -ENOMEM;
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_ci	return 0;
13848c2ecf20Sopenharmony_ci}
13858c2ecf20Sopenharmony_ci
13868c2ecf20Sopenharmony_cistatic int
13878c2ecf20Sopenharmony_cicifs_parse_mount_options(const char *mountdata, const char *devname,
13888c2ecf20Sopenharmony_ci			 struct smb_vol *vol, bool is_smb3)
13898c2ecf20Sopenharmony_ci{
13908c2ecf20Sopenharmony_ci	char *data, *end;
13918c2ecf20Sopenharmony_ci	char *mountdata_copy = NULL, *options;
13928c2ecf20Sopenharmony_ci	unsigned int  temp_len, i, j;
13938c2ecf20Sopenharmony_ci	char separator[2];
13948c2ecf20Sopenharmony_ci	short int override_uid = -1;
13958c2ecf20Sopenharmony_ci	short int override_gid = -1;
13968c2ecf20Sopenharmony_ci	bool uid_specified = false;
13978c2ecf20Sopenharmony_ci	bool gid_specified = false;
13988c2ecf20Sopenharmony_ci	bool sloppy = false;
13998c2ecf20Sopenharmony_ci	char *invalid = NULL;
14008c2ecf20Sopenharmony_ci	char *nodename = utsname()->nodename;
14018c2ecf20Sopenharmony_ci	char *string = NULL;
14028c2ecf20Sopenharmony_ci	char *tmp_end, *value;
14038c2ecf20Sopenharmony_ci	char delim;
14048c2ecf20Sopenharmony_ci	bool got_ip = false;
14058c2ecf20Sopenharmony_ci	bool got_version = false;
14068c2ecf20Sopenharmony_ci	unsigned short port = 0;
14078c2ecf20Sopenharmony_ci	struct sockaddr *dstaddr = (struct sockaddr *)&vol->dstaddr;
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci	separator[0] = ',';
14108c2ecf20Sopenharmony_ci	separator[1] = 0;
14118c2ecf20Sopenharmony_ci	delim = separator[0];
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_ci	/* ensure we always start with zeroed-out smb_vol */
14148c2ecf20Sopenharmony_ci	memset(vol, 0, sizeof(*vol));
14158c2ecf20Sopenharmony_ci
14168c2ecf20Sopenharmony_ci	/*
14178c2ecf20Sopenharmony_ci	 * does not have to be perfect mapping since field is
14188c2ecf20Sopenharmony_ci	 * informational, only used for servers that do not support
14198c2ecf20Sopenharmony_ci	 * port 445 and it can be overridden at mount time
14208c2ecf20Sopenharmony_ci	 */
14218c2ecf20Sopenharmony_ci	memset(vol->source_rfc1001_name, 0x20, RFC1001_NAME_LEN);
14228c2ecf20Sopenharmony_ci	for (i = 0; i < strnlen(nodename, RFC1001_NAME_LEN); i++)
14238c2ecf20Sopenharmony_ci		vol->source_rfc1001_name[i] = toupper(nodename[i]);
14248c2ecf20Sopenharmony_ci
14258c2ecf20Sopenharmony_ci	vol->source_rfc1001_name[RFC1001_NAME_LEN] = 0;
14268c2ecf20Sopenharmony_ci	/* null target name indicates to use *SMBSERVR default called name
14278c2ecf20Sopenharmony_ci	   if we end up sending RFC1001 session initialize */
14288c2ecf20Sopenharmony_ci	vol->target_rfc1001_name[0] = 0;
14298c2ecf20Sopenharmony_ci	vol->cred_uid = current_uid();
14308c2ecf20Sopenharmony_ci	vol->linux_uid = current_uid();
14318c2ecf20Sopenharmony_ci	vol->linux_gid = current_gid();
14328c2ecf20Sopenharmony_ci	vol->bsize = 1024 * 1024; /* can improve cp performance significantly */
14338c2ecf20Sopenharmony_ci	/*
14348c2ecf20Sopenharmony_ci	 * default to SFM style remapping of seven reserved characters
14358c2ecf20Sopenharmony_ci	 * unless user overrides it or we negotiate CIFS POSIX where
14368c2ecf20Sopenharmony_ci	 * it is unnecessary.  Can not simultaneously use more than one mapping
14378c2ecf20Sopenharmony_ci	 * since then readdir could list files that open could not open
14388c2ecf20Sopenharmony_ci	 */
14398c2ecf20Sopenharmony_ci	vol->remap = true;
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci	/* default to only allowing write access to owner of the mount */
14428c2ecf20Sopenharmony_ci	vol->dir_mode = vol->file_mode = S_IRUGO | S_IXUGO | S_IWUSR;
14438c2ecf20Sopenharmony_ci
14448c2ecf20Sopenharmony_ci	/* vol->retry default is 0 (i.e. "soft" limited retry not hard retry) */
14458c2ecf20Sopenharmony_ci	/* default is always to request posix paths. */
14468c2ecf20Sopenharmony_ci	vol->posix_paths = 1;
14478c2ecf20Sopenharmony_ci	/* default to using server inode numbers where available */
14488c2ecf20Sopenharmony_ci	vol->server_ino = 1;
14498c2ecf20Sopenharmony_ci
14508c2ecf20Sopenharmony_ci	/* default is to use strict cifs caching semantics */
14518c2ecf20Sopenharmony_ci	vol->strict_io = true;
14528c2ecf20Sopenharmony_ci
14538c2ecf20Sopenharmony_ci	vol->actimeo = CIFS_DEF_ACTIMEO;
14548c2ecf20Sopenharmony_ci
14558c2ecf20Sopenharmony_ci	/* Most clients set timeout to 0, allows server to use its default */
14568c2ecf20Sopenharmony_ci	vol->handle_timeout = 0; /* See MS-SMB2 spec section 2.2.14.2.12 */
14578c2ecf20Sopenharmony_ci
14588c2ecf20Sopenharmony_ci	/* offer SMB2.1 and later (SMB3 etc). Secure and widely accepted */
14598c2ecf20Sopenharmony_ci	vol->ops = &smb30_operations;
14608c2ecf20Sopenharmony_ci	vol->vals = &smbdefault_values;
14618c2ecf20Sopenharmony_ci
14628c2ecf20Sopenharmony_ci	vol->echo_interval = SMB_ECHO_INTERVAL_DEFAULT;
14638c2ecf20Sopenharmony_ci
14648c2ecf20Sopenharmony_ci	/* default to no multichannel (single server connection) */
14658c2ecf20Sopenharmony_ci	vol->multichannel = false;
14668c2ecf20Sopenharmony_ci	vol->max_channels = 1;
14678c2ecf20Sopenharmony_ci
14688c2ecf20Sopenharmony_ci	if (!mountdata)
14698c2ecf20Sopenharmony_ci		goto cifs_parse_mount_err;
14708c2ecf20Sopenharmony_ci
14718c2ecf20Sopenharmony_ci	mountdata_copy = kstrndup(mountdata, PAGE_SIZE, GFP_KERNEL);
14728c2ecf20Sopenharmony_ci	if (!mountdata_copy)
14738c2ecf20Sopenharmony_ci		goto cifs_parse_mount_err;
14748c2ecf20Sopenharmony_ci
14758c2ecf20Sopenharmony_ci	options = mountdata_copy;
14768c2ecf20Sopenharmony_ci	end = options + strlen(options);
14778c2ecf20Sopenharmony_ci
14788c2ecf20Sopenharmony_ci	if (strncmp(options, "sep=", 4) == 0) {
14798c2ecf20Sopenharmony_ci		if (options[4] != 0) {
14808c2ecf20Sopenharmony_ci			separator[0] = options[4];
14818c2ecf20Sopenharmony_ci			options += 5;
14828c2ecf20Sopenharmony_ci		} else {
14838c2ecf20Sopenharmony_ci			cifs_dbg(FYI, "Null separator not allowed\n");
14848c2ecf20Sopenharmony_ci		}
14858c2ecf20Sopenharmony_ci	}
14868c2ecf20Sopenharmony_ci	vol->backupuid_specified = false; /* no backup intent for a user */
14878c2ecf20Sopenharmony_ci	vol->backupgid_specified = false; /* no backup intent for a group */
14888c2ecf20Sopenharmony_ci
14898c2ecf20Sopenharmony_ci	switch (cifs_parse_devname(devname, vol)) {
14908c2ecf20Sopenharmony_ci	case 0:
14918c2ecf20Sopenharmony_ci		break;
14928c2ecf20Sopenharmony_ci	case -ENOMEM:
14938c2ecf20Sopenharmony_ci		cifs_dbg(VFS, "Unable to allocate memory for devname\n");
14948c2ecf20Sopenharmony_ci		goto cifs_parse_mount_err;
14958c2ecf20Sopenharmony_ci	case -EINVAL:
14968c2ecf20Sopenharmony_ci		cifs_dbg(VFS, "Malformed UNC in devname\n");
14978c2ecf20Sopenharmony_ci		goto cifs_parse_mount_err;
14988c2ecf20Sopenharmony_ci	default:
14998c2ecf20Sopenharmony_ci		cifs_dbg(VFS, "Unknown error parsing devname\n");
15008c2ecf20Sopenharmony_ci		goto cifs_parse_mount_err;
15018c2ecf20Sopenharmony_ci	}
15028c2ecf20Sopenharmony_ci
15038c2ecf20Sopenharmony_ci	while ((data = strsep(&options, separator)) != NULL) {
15048c2ecf20Sopenharmony_ci		substring_t args[MAX_OPT_ARGS];
15058c2ecf20Sopenharmony_ci		unsigned long option;
15068c2ecf20Sopenharmony_ci		int token;
15078c2ecf20Sopenharmony_ci
15088c2ecf20Sopenharmony_ci		if (!*data)
15098c2ecf20Sopenharmony_ci			continue;
15108c2ecf20Sopenharmony_ci
15118c2ecf20Sopenharmony_ci		token = match_token(data, cifs_mount_option_tokens, args);
15128c2ecf20Sopenharmony_ci
15138c2ecf20Sopenharmony_ci		switch (token) {
15148c2ecf20Sopenharmony_ci
15158c2ecf20Sopenharmony_ci		/* Ingnore the following */
15168c2ecf20Sopenharmony_ci		case Opt_ignore:
15178c2ecf20Sopenharmony_ci			break;
15188c2ecf20Sopenharmony_ci
15198c2ecf20Sopenharmony_ci		/* Boolean values */
15208c2ecf20Sopenharmony_ci		case Opt_user_xattr:
15218c2ecf20Sopenharmony_ci			vol->no_xattr = 0;
15228c2ecf20Sopenharmony_ci			break;
15238c2ecf20Sopenharmony_ci		case Opt_nouser_xattr:
15248c2ecf20Sopenharmony_ci			vol->no_xattr = 1;
15258c2ecf20Sopenharmony_ci			break;
15268c2ecf20Sopenharmony_ci		case Opt_forceuid:
15278c2ecf20Sopenharmony_ci			override_uid = 1;
15288c2ecf20Sopenharmony_ci			break;
15298c2ecf20Sopenharmony_ci		case Opt_noforceuid:
15308c2ecf20Sopenharmony_ci			override_uid = 0;
15318c2ecf20Sopenharmony_ci			break;
15328c2ecf20Sopenharmony_ci		case Opt_forcegid:
15338c2ecf20Sopenharmony_ci			override_gid = 1;
15348c2ecf20Sopenharmony_ci			break;
15358c2ecf20Sopenharmony_ci		case Opt_noforcegid:
15368c2ecf20Sopenharmony_ci			override_gid = 0;
15378c2ecf20Sopenharmony_ci			break;
15388c2ecf20Sopenharmony_ci		case Opt_noblocksend:
15398c2ecf20Sopenharmony_ci			vol->noblocksnd = 1;
15408c2ecf20Sopenharmony_ci			break;
15418c2ecf20Sopenharmony_ci		case Opt_noautotune:
15428c2ecf20Sopenharmony_ci			vol->noautotune = 1;
15438c2ecf20Sopenharmony_ci			break;
15448c2ecf20Sopenharmony_ci		case Opt_nolease:
15458c2ecf20Sopenharmony_ci			vol->no_lease = 1;
15468c2ecf20Sopenharmony_ci			break;
15478c2ecf20Sopenharmony_ci		case Opt_hard:
15488c2ecf20Sopenharmony_ci			vol->retry = 1;
15498c2ecf20Sopenharmony_ci			break;
15508c2ecf20Sopenharmony_ci		case Opt_soft:
15518c2ecf20Sopenharmony_ci			vol->retry = 0;
15528c2ecf20Sopenharmony_ci			break;
15538c2ecf20Sopenharmony_ci		case Opt_perm:
15548c2ecf20Sopenharmony_ci			vol->noperm = 0;
15558c2ecf20Sopenharmony_ci			break;
15568c2ecf20Sopenharmony_ci		case Opt_noperm:
15578c2ecf20Sopenharmony_ci			vol->noperm = 1;
15588c2ecf20Sopenharmony_ci			break;
15598c2ecf20Sopenharmony_ci		case Opt_nodelete:
15608c2ecf20Sopenharmony_ci			vol->nodelete = 1;
15618c2ecf20Sopenharmony_ci			break;
15628c2ecf20Sopenharmony_ci		case Opt_mapchars:
15638c2ecf20Sopenharmony_ci			vol->sfu_remap = true;
15648c2ecf20Sopenharmony_ci			vol->remap = false; /* disable SFM mapping */
15658c2ecf20Sopenharmony_ci			break;
15668c2ecf20Sopenharmony_ci		case Opt_nomapchars:
15678c2ecf20Sopenharmony_ci			vol->sfu_remap = false;
15688c2ecf20Sopenharmony_ci			break;
15698c2ecf20Sopenharmony_ci		case Opt_mapposix:
15708c2ecf20Sopenharmony_ci			vol->remap = true;
15718c2ecf20Sopenharmony_ci			vol->sfu_remap = false; /* disable SFU mapping */
15728c2ecf20Sopenharmony_ci			break;
15738c2ecf20Sopenharmony_ci		case Opt_nomapposix:
15748c2ecf20Sopenharmony_ci			vol->remap = false;
15758c2ecf20Sopenharmony_ci			break;
15768c2ecf20Sopenharmony_ci		case Opt_sfu:
15778c2ecf20Sopenharmony_ci			vol->sfu_emul = 1;
15788c2ecf20Sopenharmony_ci			break;
15798c2ecf20Sopenharmony_ci		case Opt_nosfu:
15808c2ecf20Sopenharmony_ci			vol->sfu_emul = 0;
15818c2ecf20Sopenharmony_ci			break;
15828c2ecf20Sopenharmony_ci		case Opt_nodfs:
15838c2ecf20Sopenharmony_ci			vol->nodfs = 1;
15848c2ecf20Sopenharmony_ci			break;
15858c2ecf20Sopenharmony_ci		case Opt_rootfs:
15868c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_ROOT
15878c2ecf20Sopenharmony_ci			vol->rootfs = true;
15888c2ecf20Sopenharmony_ci#endif
15898c2ecf20Sopenharmony_ci			break;
15908c2ecf20Sopenharmony_ci		case Opt_posixpaths:
15918c2ecf20Sopenharmony_ci			vol->posix_paths = 1;
15928c2ecf20Sopenharmony_ci			break;
15938c2ecf20Sopenharmony_ci		case Opt_noposixpaths:
15948c2ecf20Sopenharmony_ci			vol->posix_paths = 0;
15958c2ecf20Sopenharmony_ci			break;
15968c2ecf20Sopenharmony_ci		case Opt_nounix:
15978c2ecf20Sopenharmony_ci			if (vol->linux_ext)
15988c2ecf20Sopenharmony_ci				cifs_dbg(VFS,
15998c2ecf20Sopenharmony_ci					"conflicting unix mount options\n");
16008c2ecf20Sopenharmony_ci			vol->no_linux_ext = 1;
16018c2ecf20Sopenharmony_ci			break;
16028c2ecf20Sopenharmony_ci		case Opt_unix:
16038c2ecf20Sopenharmony_ci			if (vol->no_linux_ext)
16048c2ecf20Sopenharmony_ci				cifs_dbg(VFS,
16058c2ecf20Sopenharmony_ci					"conflicting unix mount options\n");
16068c2ecf20Sopenharmony_ci			vol->linux_ext = 1;
16078c2ecf20Sopenharmony_ci			break;
16088c2ecf20Sopenharmony_ci		case Opt_nocase:
16098c2ecf20Sopenharmony_ci			vol->nocase = 1;
16108c2ecf20Sopenharmony_ci			break;
16118c2ecf20Sopenharmony_ci		case Opt_brl:
16128c2ecf20Sopenharmony_ci			vol->nobrl =  0;
16138c2ecf20Sopenharmony_ci			break;
16148c2ecf20Sopenharmony_ci		case Opt_nobrl:
16158c2ecf20Sopenharmony_ci			vol->nobrl =  1;
16168c2ecf20Sopenharmony_ci			/*
16178c2ecf20Sopenharmony_ci			 * turn off mandatory locking in mode
16188c2ecf20Sopenharmony_ci			 * if remote locking is turned off since the
16198c2ecf20Sopenharmony_ci			 * local vfs will do advisory
16208c2ecf20Sopenharmony_ci			 */
16218c2ecf20Sopenharmony_ci			if (vol->file_mode ==
16228c2ecf20Sopenharmony_ci				(S_IALLUGO & ~(S_ISUID | S_IXGRP)))
16238c2ecf20Sopenharmony_ci				vol->file_mode = S_IALLUGO;
16248c2ecf20Sopenharmony_ci			break;
16258c2ecf20Sopenharmony_ci		case Opt_nohandlecache:
16268c2ecf20Sopenharmony_ci			vol->nohandlecache = 1;
16278c2ecf20Sopenharmony_ci			break;
16288c2ecf20Sopenharmony_ci		case Opt_handlecache:
16298c2ecf20Sopenharmony_ci			vol->nohandlecache = 0;
16308c2ecf20Sopenharmony_ci			break;
16318c2ecf20Sopenharmony_ci		case Opt_forcemandatorylock:
16328c2ecf20Sopenharmony_ci			vol->mand_lock = 1;
16338c2ecf20Sopenharmony_ci			break;
16348c2ecf20Sopenharmony_ci		case Opt_setuids:
16358c2ecf20Sopenharmony_ci			vol->setuids = 1;
16368c2ecf20Sopenharmony_ci			break;
16378c2ecf20Sopenharmony_ci		case Opt_nosetuids:
16388c2ecf20Sopenharmony_ci			vol->setuids = 0;
16398c2ecf20Sopenharmony_ci			break;
16408c2ecf20Sopenharmony_ci		case Opt_setuidfromacl:
16418c2ecf20Sopenharmony_ci			vol->setuidfromacl = 1;
16428c2ecf20Sopenharmony_ci			break;
16438c2ecf20Sopenharmony_ci		case Opt_dynperm:
16448c2ecf20Sopenharmony_ci			vol->dynperm = true;
16458c2ecf20Sopenharmony_ci			break;
16468c2ecf20Sopenharmony_ci		case Opt_nodynperm:
16478c2ecf20Sopenharmony_ci			vol->dynperm = false;
16488c2ecf20Sopenharmony_ci			break;
16498c2ecf20Sopenharmony_ci		case Opt_nohard:
16508c2ecf20Sopenharmony_ci			vol->retry = 0;
16518c2ecf20Sopenharmony_ci			break;
16528c2ecf20Sopenharmony_ci		case Opt_nosoft:
16538c2ecf20Sopenharmony_ci			vol->retry = 1;
16548c2ecf20Sopenharmony_ci			break;
16558c2ecf20Sopenharmony_ci		case Opt_nointr:
16568c2ecf20Sopenharmony_ci			vol->intr = 0;
16578c2ecf20Sopenharmony_ci			break;
16588c2ecf20Sopenharmony_ci		case Opt_intr:
16598c2ecf20Sopenharmony_ci			vol->intr = 1;
16608c2ecf20Sopenharmony_ci			break;
16618c2ecf20Sopenharmony_ci		case Opt_nostrictsync:
16628c2ecf20Sopenharmony_ci			vol->nostrictsync = 1;
16638c2ecf20Sopenharmony_ci			break;
16648c2ecf20Sopenharmony_ci		case Opt_strictsync:
16658c2ecf20Sopenharmony_ci			vol->nostrictsync = 0;
16668c2ecf20Sopenharmony_ci			break;
16678c2ecf20Sopenharmony_ci		case Opt_serverino:
16688c2ecf20Sopenharmony_ci			vol->server_ino = 1;
16698c2ecf20Sopenharmony_ci			break;
16708c2ecf20Sopenharmony_ci		case Opt_noserverino:
16718c2ecf20Sopenharmony_ci			vol->server_ino = 0;
16728c2ecf20Sopenharmony_ci			break;
16738c2ecf20Sopenharmony_ci		case Opt_rwpidforward:
16748c2ecf20Sopenharmony_ci			vol->rwpidforward = 1;
16758c2ecf20Sopenharmony_ci			break;
16768c2ecf20Sopenharmony_ci		case Opt_modesid:
16778c2ecf20Sopenharmony_ci			vol->mode_ace = 1;
16788c2ecf20Sopenharmony_ci			break;
16798c2ecf20Sopenharmony_ci		case Opt_cifsacl:
16808c2ecf20Sopenharmony_ci			vol->cifs_acl = 1;
16818c2ecf20Sopenharmony_ci			break;
16828c2ecf20Sopenharmony_ci		case Opt_nocifsacl:
16838c2ecf20Sopenharmony_ci			vol->cifs_acl = 0;
16848c2ecf20Sopenharmony_ci			break;
16858c2ecf20Sopenharmony_ci		case Opt_acl:
16868c2ecf20Sopenharmony_ci			vol->no_psx_acl = 0;
16878c2ecf20Sopenharmony_ci			break;
16888c2ecf20Sopenharmony_ci		case Opt_noacl:
16898c2ecf20Sopenharmony_ci			vol->no_psx_acl = 1;
16908c2ecf20Sopenharmony_ci			break;
16918c2ecf20Sopenharmony_ci		case Opt_locallease:
16928c2ecf20Sopenharmony_ci			vol->local_lease = 1;
16938c2ecf20Sopenharmony_ci			break;
16948c2ecf20Sopenharmony_ci		case Opt_sign:
16958c2ecf20Sopenharmony_ci			vol->sign = true;
16968c2ecf20Sopenharmony_ci			break;
16978c2ecf20Sopenharmony_ci		case Opt_ignore_signature:
16988c2ecf20Sopenharmony_ci			vol->sign = true;
16998c2ecf20Sopenharmony_ci			vol->ignore_signature = true;
17008c2ecf20Sopenharmony_ci			break;
17018c2ecf20Sopenharmony_ci		case Opt_seal:
17028c2ecf20Sopenharmony_ci			/* we do not do the following in secFlags because seal
17038c2ecf20Sopenharmony_ci			 * is a per tree connection (mount) not a per socket
17048c2ecf20Sopenharmony_ci			 * or per-smb connection option in the protocol
17058c2ecf20Sopenharmony_ci			 * vol->secFlg |= CIFSSEC_MUST_SEAL;
17068c2ecf20Sopenharmony_ci			 */
17078c2ecf20Sopenharmony_ci			vol->seal = 1;
17088c2ecf20Sopenharmony_ci			break;
17098c2ecf20Sopenharmony_ci		case Opt_noac:
17108c2ecf20Sopenharmony_ci			pr_warn("Mount option noac not supported. Instead set /proc/fs/cifs/LookupCacheEnabled to 0\n");
17118c2ecf20Sopenharmony_ci			break;
17128c2ecf20Sopenharmony_ci		case Opt_fsc:
17138c2ecf20Sopenharmony_ci#ifndef CONFIG_CIFS_FSCACHE
17148c2ecf20Sopenharmony_ci			cifs_dbg(VFS, "FS-Cache support needs CONFIG_CIFS_FSCACHE kernel config option set\n");
17158c2ecf20Sopenharmony_ci			goto cifs_parse_mount_err;
17168c2ecf20Sopenharmony_ci#endif
17178c2ecf20Sopenharmony_ci			vol->fsc = true;
17188c2ecf20Sopenharmony_ci			break;
17198c2ecf20Sopenharmony_ci		case Opt_mfsymlinks:
17208c2ecf20Sopenharmony_ci			vol->mfsymlinks = true;
17218c2ecf20Sopenharmony_ci			break;
17228c2ecf20Sopenharmony_ci		case Opt_multiuser:
17238c2ecf20Sopenharmony_ci			vol->multiuser = true;
17248c2ecf20Sopenharmony_ci			break;
17258c2ecf20Sopenharmony_ci		case Opt_sloppy:
17268c2ecf20Sopenharmony_ci			sloppy = true;
17278c2ecf20Sopenharmony_ci			break;
17288c2ecf20Sopenharmony_ci		case Opt_nosharesock:
17298c2ecf20Sopenharmony_ci			vol->nosharesock = true;
17308c2ecf20Sopenharmony_ci			break;
17318c2ecf20Sopenharmony_ci		case Opt_nopersistent:
17328c2ecf20Sopenharmony_ci			vol->nopersistent = true;
17338c2ecf20Sopenharmony_ci			if (vol->persistent) {
17348c2ecf20Sopenharmony_ci				cifs_dbg(VFS,
17358c2ecf20Sopenharmony_ci				  "persistenthandles mount options conflict\n");
17368c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
17378c2ecf20Sopenharmony_ci			}
17388c2ecf20Sopenharmony_ci			break;
17398c2ecf20Sopenharmony_ci		case Opt_persistent:
17408c2ecf20Sopenharmony_ci			vol->persistent = true;
17418c2ecf20Sopenharmony_ci			if ((vol->nopersistent) || (vol->resilient)) {
17428c2ecf20Sopenharmony_ci				cifs_dbg(VFS,
17438c2ecf20Sopenharmony_ci				  "persistenthandles mount options conflict\n");
17448c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
17458c2ecf20Sopenharmony_ci			}
17468c2ecf20Sopenharmony_ci			break;
17478c2ecf20Sopenharmony_ci		case Opt_resilient:
17488c2ecf20Sopenharmony_ci			vol->resilient = true;
17498c2ecf20Sopenharmony_ci			if (vol->persistent) {
17508c2ecf20Sopenharmony_ci				cifs_dbg(VFS,
17518c2ecf20Sopenharmony_ci				  "persistenthandles mount options conflict\n");
17528c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
17538c2ecf20Sopenharmony_ci			}
17548c2ecf20Sopenharmony_ci			break;
17558c2ecf20Sopenharmony_ci		case Opt_noresilient:
17568c2ecf20Sopenharmony_ci			vol->resilient = false; /* already the default */
17578c2ecf20Sopenharmony_ci			break;
17588c2ecf20Sopenharmony_ci		case Opt_domainauto:
17598c2ecf20Sopenharmony_ci			vol->domainauto = true;
17608c2ecf20Sopenharmony_ci			break;
17618c2ecf20Sopenharmony_ci		case Opt_rdma:
17628c2ecf20Sopenharmony_ci			vol->rdma = true;
17638c2ecf20Sopenharmony_ci			break;
17648c2ecf20Sopenharmony_ci		case Opt_multichannel:
17658c2ecf20Sopenharmony_ci			vol->multichannel = true;
17668c2ecf20Sopenharmony_ci			/* if number of channels not specified, default to 2 */
17678c2ecf20Sopenharmony_ci			if (vol->max_channels < 2)
17688c2ecf20Sopenharmony_ci				vol->max_channels = 2;
17698c2ecf20Sopenharmony_ci			break;
17708c2ecf20Sopenharmony_ci		case Opt_nomultichannel:
17718c2ecf20Sopenharmony_ci			vol->multichannel = false;
17728c2ecf20Sopenharmony_ci			vol->max_channels = 1;
17738c2ecf20Sopenharmony_ci			break;
17748c2ecf20Sopenharmony_ci		case Opt_compress:
17758c2ecf20Sopenharmony_ci			vol->compression = UNKNOWN_TYPE;
17768c2ecf20Sopenharmony_ci			cifs_dbg(VFS,
17778c2ecf20Sopenharmony_ci				"SMB3 compression support is experimental\n");
17788c2ecf20Sopenharmony_ci			break;
17798c2ecf20Sopenharmony_ci
17808c2ecf20Sopenharmony_ci		/* Numeric Values */
17818c2ecf20Sopenharmony_ci		case Opt_backupuid:
17828c2ecf20Sopenharmony_ci			if (get_option_uid(args, &vol->backupuid)) {
17838c2ecf20Sopenharmony_ci				cifs_dbg(VFS, "%s: Invalid backupuid value\n",
17848c2ecf20Sopenharmony_ci					 __func__);
17858c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
17868c2ecf20Sopenharmony_ci			}
17878c2ecf20Sopenharmony_ci			vol->backupuid_specified = true;
17888c2ecf20Sopenharmony_ci			break;
17898c2ecf20Sopenharmony_ci		case Opt_backupgid:
17908c2ecf20Sopenharmony_ci			if (get_option_gid(args, &vol->backupgid)) {
17918c2ecf20Sopenharmony_ci				cifs_dbg(VFS, "%s: Invalid backupgid value\n",
17928c2ecf20Sopenharmony_ci					 __func__);
17938c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
17948c2ecf20Sopenharmony_ci			}
17958c2ecf20Sopenharmony_ci			vol->backupgid_specified = true;
17968c2ecf20Sopenharmony_ci			break;
17978c2ecf20Sopenharmony_ci		case Opt_uid:
17988c2ecf20Sopenharmony_ci			if (get_option_uid(args, &vol->linux_uid)) {
17998c2ecf20Sopenharmony_ci				cifs_dbg(VFS, "%s: Invalid uid value\n",
18008c2ecf20Sopenharmony_ci					 __func__);
18018c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
18028c2ecf20Sopenharmony_ci			}
18038c2ecf20Sopenharmony_ci			uid_specified = true;
18048c2ecf20Sopenharmony_ci			break;
18058c2ecf20Sopenharmony_ci		case Opt_cruid:
18068c2ecf20Sopenharmony_ci			if (get_option_uid(args, &vol->cred_uid)) {
18078c2ecf20Sopenharmony_ci				cifs_dbg(VFS, "%s: Invalid cruid value\n",
18088c2ecf20Sopenharmony_ci					 __func__);
18098c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
18108c2ecf20Sopenharmony_ci			}
18118c2ecf20Sopenharmony_ci			break;
18128c2ecf20Sopenharmony_ci		case Opt_gid:
18138c2ecf20Sopenharmony_ci			if (get_option_gid(args, &vol->linux_gid)) {
18148c2ecf20Sopenharmony_ci				cifs_dbg(VFS, "%s: Invalid gid value\n",
18158c2ecf20Sopenharmony_ci					 __func__);
18168c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
18178c2ecf20Sopenharmony_ci			}
18188c2ecf20Sopenharmony_ci			gid_specified = true;
18198c2ecf20Sopenharmony_ci			break;
18208c2ecf20Sopenharmony_ci		case Opt_file_mode:
18218c2ecf20Sopenharmony_ci			if (get_option_ul(args, &option)) {
18228c2ecf20Sopenharmony_ci				cifs_dbg(VFS, "%s: Invalid file_mode value\n",
18238c2ecf20Sopenharmony_ci					 __func__);
18248c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
18258c2ecf20Sopenharmony_ci			}
18268c2ecf20Sopenharmony_ci			vol->file_mode = option;
18278c2ecf20Sopenharmony_ci			break;
18288c2ecf20Sopenharmony_ci		case Opt_dirmode:
18298c2ecf20Sopenharmony_ci			if (get_option_ul(args, &option)) {
18308c2ecf20Sopenharmony_ci				cifs_dbg(VFS, "%s: Invalid dir_mode value\n",
18318c2ecf20Sopenharmony_ci					 __func__);
18328c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
18338c2ecf20Sopenharmony_ci			}
18348c2ecf20Sopenharmony_ci			vol->dir_mode = option;
18358c2ecf20Sopenharmony_ci			break;
18368c2ecf20Sopenharmony_ci		case Opt_port:
18378c2ecf20Sopenharmony_ci			if (get_option_ul(args, &option) ||
18388c2ecf20Sopenharmony_ci			    option > USHRT_MAX) {
18398c2ecf20Sopenharmony_ci				cifs_dbg(VFS, "%s: Invalid port value\n",
18408c2ecf20Sopenharmony_ci					 __func__);
18418c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
18428c2ecf20Sopenharmony_ci			}
18438c2ecf20Sopenharmony_ci			port = (unsigned short)option;
18448c2ecf20Sopenharmony_ci			break;
18458c2ecf20Sopenharmony_ci		case Opt_min_enc_offload:
18468c2ecf20Sopenharmony_ci			if (get_option_ul(args, &option)) {
18478c2ecf20Sopenharmony_ci				cifs_dbg(VFS, "Invalid minimum encrypted read offload size (esize)\n");
18488c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
18498c2ecf20Sopenharmony_ci			}
18508c2ecf20Sopenharmony_ci			vol->min_offload = option;
18518c2ecf20Sopenharmony_ci			break;
18528c2ecf20Sopenharmony_ci		case Opt_blocksize:
18538c2ecf20Sopenharmony_ci			if (get_option_ul(args, &option)) {
18548c2ecf20Sopenharmony_ci				cifs_dbg(VFS, "%s: Invalid blocksize value\n",
18558c2ecf20Sopenharmony_ci					__func__);
18568c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
18578c2ecf20Sopenharmony_ci			}
18588c2ecf20Sopenharmony_ci			/*
18598c2ecf20Sopenharmony_ci			 * inode blocksize realistically should never need to be
18608c2ecf20Sopenharmony_ci			 * less than 16K or greater than 16M and default is 1MB.
18618c2ecf20Sopenharmony_ci			 * Note that small inode block sizes (e.g. 64K) can lead
18628c2ecf20Sopenharmony_ci			 * to very poor performance of common tools like cp and scp
18638c2ecf20Sopenharmony_ci			 */
18648c2ecf20Sopenharmony_ci			if ((option < CIFS_MAX_MSGSIZE) ||
18658c2ecf20Sopenharmony_ci			   (option > (4 * SMB3_DEFAULT_IOSIZE))) {
18668c2ecf20Sopenharmony_ci				cifs_dbg(VFS, "%s: Invalid blocksize\n",
18678c2ecf20Sopenharmony_ci					__func__);
18688c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
18698c2ecf20Sopenharmony_ci			}
18708c2ecf20Sopenharmony_ci			vol->bsize = option;
18718c2ecf20Sopenharmony_ci			break;
18728c2ecf20Sopenharmony_ci		case Opt_rsize:
18738c2ecf20Sopenharmony_ci			if (get_option_ul(args, &option)) {
18748c2ecf20Sopenharmony_ci				cifs_dbg(VFS, "%s: Invalid rsize value\n",
18758c2ecf20Sopenharmony_ci					 __func__);
18768c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
18778c2ecf20Sopenharmony_ci			}
18788c2ecf20Sopenharmony_ci			vol->rsize = option;
18798c2ecf20Sopenharmony_ci			break;
18808c2ecf20Sopenharmony_ci		case Opt_wsize:
18818c2ecf20Sopenharmony_ci			if (get_option_ul(args, &option)) {
18828c2ecf20Sopenharmony_ci				cifs_dbg(VFS, "%s: Invalid wsize value\n",
18838c2ecf20Sopenharmony_ci					 __func__);
18848c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
18858c2ecf20Sopenharmony_ci			}
18868c2ecf20Sopenharmony_ci			vol->wsize = option;
18878c2ecf20Sopenharmony_ci			break;
18888c2ecf20Sopenharmony_ci		case Opt_actimeo:
18898c2ecf20Sopenharmony_ci			if (get_option_ul(args, &option)) {
18908c2ecf20Sopenharmony_ci				cifs_dbg(VFS, "%s: Invalid actimeo value\n",
18918c2ecf20Sopenharmony_ci					 __func__);
18928c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
18938c2ecf20Sopenharmony_ci			}
18948c2ecf20Sopenharmony_ci			vol->actimeo = HZ * option;
18958c2ecf20Sopenharmony_ci			if (vol->actimeo > CIFS_MAX_ACTIMEO) {
18968c2ecf20Sopenharmony_ci				cifs_dbg(VFS, "attribute cache timeout too large\n");
18978c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
18988c2ecf20Sopenharmony_ci			}
18998c2ecf20Sopenharmony_ci			break;
19008c2ecf20Sopenharmony_ci		case Opt_handletimeout:
19018c2ecf20Sopenharmony_ci			if (get_option_ul(args, &option)) {
19028c2ecf20Sopenharmony_ci				cifs_dbg(VFS, "%s: Invalid handletimeout value\n",
19038c2ecf20Sopenharmony_ci					 __func__);
19048c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
19058c2ecf20Sopenharmony_ci			}
19068c2ecf20Sopenharmony_ci			vol->handle_timeout = option;
19078c2ecf20Sopenharmony_ci			if (vol->handle_timeout > SMB3_MAX_HANDLE_TIMEOUT) {
19088c2ecf20Sopenharmony_ci				cifs_dbg(VFS, "Invalid handle cache timeout, longer than 16 minutes\n");
19098c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
19108c2ecf20Sopenharmony_ci			}
19118c2ecf20Sopenharmony_ci			break;
19128c2ecf20Sopenharmony_ci		case Opt_echo_interval:
19138c2ecf20Sopenharmony_ci			if (get_option_ul(args, &option)) {
19148c2ecf20Sopenharmony_ci				cifs_dbg(VFS, "%s: Invalid echo interval value\n",
19158c2ecf20Sopenharmony_ci					 __func__);
19168c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
19178c2ecf20Sopenharmony_ci			}
19188c2ecf20Sopenharmony_ci			vol->echo_interval = option;
19198c2ecf20Sopenharmony_ci			break;
19208c2ecf20Sopenharmony_ci		case Opt_snapshot:
19218c2ecf20Sopenharmony_ci			if (get_option_ul(args, &option)) {
19228c2ecf20Sopenharmony_ci				cifs_dbg(VFS, "%s: Invalid snapshot time\n",
19238c2ecf20Sopenharmony_ci					 __func__);
19248c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
19258c2ecf20Sopenharmony_ci			}
19268c2ecf20Sopenharmony_ci			vol->snapshot_time = option;
19278c2ecf20Sopenharmony_ci			break;
19288c2ecf20Sopenharmony_ci		case Opt_max_credits:
19298c2ecf20Sopenharmony_ci			if (get_option_ul(args, &option) || (option < 20) ||
19308c2ecf20Sopenharmony_ci			    (option > 60000)) {
19318c2ecf20Sopenharmony_ci				cifs_dbg(VFS, "%s: Invalid max_credits value\n",
19328c2ecf20Sopenharmony_ci					 __func__);
19338c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
19348c2ecf20Sopenharmony_ci			}
19358c2ecf20Sopenharmony_ci			vol->max_credits = option;
19368c2ecf20Sopenharmony_ci			break;
19378c2ecf20Sopenharmony_ci		case Opt_max_channels:
19388c2ecf20Sopenharmony_ci			if (get_option_ul(args, &option) || option < 1 ||
19398c2ecf20Sopenharmony_ci				option > CIFS_MAX_CHANNELS) {
19408c2ecf20Sopenharmony_ci				cifs_dbg(VFS, "%s: Invalid max_channels value, needs to be 1-%d\n",
19418c2ecf20Sopenharmony_ci					 __func__, CIFS_MAX_CHANNELS);
19428c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
19438c2ecf20Sopenharmony_ci			}
19448c2ecf20Sopenharmony_ci			vol->max_channels = option;
19458c2ecf20Sopenharmony_ci			break;
19468c2ecf20Sopenharmony_ci
19478c2ecf20Sopenharmony_ci		/* String Arguments */
19488c2ecf20Sopenharmony_ci
19498c2ecf20Sopenharmony_ci		case Opt_blank_user:
19508c2ecf20Sopenharmony_ci			/* null user, ie. anonymous authentication */
19518c2ecf20Sopenharmony_ci			vol->nullauth = 1;
19528c2ecf20Sopenharmony_ci			vol->username = NULL;
19538c2ecf20Sopenharmony_ci			break;
19548c2ecf20Sopenharmony_ci		case Opt_user:
19558c2ecf20Sopenharmony_ci			string = match_strdup(args);
19568c2ecf20Sopenharmony_ci			if (string == NULL)
19578c2ecf20Sopenharmony_ci				goto out_nomem;
19588c2ecf20Sopenharmony_ci
19598c2ecf20Sopenharmony_ci			if (strnlen(string, CIFS_MAX_USERNAME_LEN) >
19608c2ecf20Sopenharmony_ci							CIFS_MAX_USERNAME_LEN) {
19618c2ecf20Sopenharmony_ci				pr_warn("username too long\n");
19628c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
19638c2ecf20Sopenharmony_ci			}
19648c2ecf20Sopenharmony_ci
19658c2ecf20Sopenharmony_ci			kfree(vol->username);
19668c2ecf20Sopenharmony_ci			vol->username = kstrdup(string, GFP_KERNEL);
19678c2ecf20Sopenharmony_ci			if (!vol->username)
19688c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
19698c2ecf20Sopenharmony_ci			break;
19708c2ecf20Sopenharmony_ci		case Opt_blank_pass:
19718c2ecf20Sopenharmony_ci			/* passwords have to be handled differently
19728c2ecf20Sopenharmony_ci			 * to allow the character used for deliminator
19738c2ecf20Sopenharmony_ci			 * to be passed within them
19748c2ecf20Sopenharmony_ci			 */
19758c2ecf20Sopenharmony_ci
19768c2ecf20Sopenharmony_ci			/*
19778c2ecf20Sopenharmony_ci			 * Check if this is a case where the  password
19788c2ecf20Sopenharmony_ci			 * starts with a delimiter
19798c2ecf20Sopenharmony_ci			 */
19808c2ecf20Sopenharmony_ci			tmp_end = strchr(data, '=');
19818c2ecf20Sopenharmony_ci			tmp_end++;
19828c2ecf20Sopenharmony_ci			if (!(tmp_end < end && tmp_end[1] == delim)) {
19838c2ecf20Sopenharmony_ci				/* No it is not. Set the password to NULL */
19848c2ecf20Sopenharmony_ci				kfree_sensitive(vol->password);
19858c2ecf20Sopenharmony_ci				vol->password = NULL;
19868c2ecf20Sopenharmony_ci				break;
19878c2ecf20Sopenharmony_ci			}
19888c2ecf20Sopenharmony_ci			fallthrough;	/* to Opt_pass below */
19898c2ecf20Sopenharmony_ci		case Opt_pass:
19908c2ecf20Sopenharmony_ci			/* Obtain the value string */
19918c2ecf20Sopenharmony_ci			value = strchr(data, '=');
19928c2ecf20Sopenharmony_ci			value++;
19938c2ecf20Sopenharmony_ci
19948c2ecf20Sopenharmony_ci			/* Set tmp_end to end of the string */
19958c2ecf20Sopenharmony_ci			tmp_end = (char *) value + strlen(value);
19968c2ecf20Sopenharmony_ci
19978c2ecf20Sopenharmony_ci			/* Check if following character is the deliminator
19988c2ecf20Sopenharmony_ci			 * If yes, we have encountered a double deliminator
19998c2ecf20Sopenharmony_ci			 * reset the NULL character to the deliminator
20008c2ecf20Sopenharmony_ci			 */
20018c2ecf20Sopenharmony_ci			if (tmp_end < end && tmp_end[1] == delim) {
20028c2ecf20Sopenharmony_ci				tmp_end[0] = delim;
20038c2ecf20Sopenharmony_ci
20048c2ecf20Sopenharmony_ci				/* Keep iterating until we get to a single
20058c2ecf20Sopenharmony_ci				 * deliminator OR the end
20068c2ecf20Sopenharmony_ci				 */
20078c2ecf20Sopenharmony_ci				while ((tmp_end = strchr(tmp_end, delim))
20088c2ecf20Sopenharmony_ci					!= NULL && (tmp_end[1] == delim)) {
20098c2ecf20Sopenharmony_ci						tmp_end = (char *) &tmp_end[2];
20108c2ecf20Sopenharmony_ci				}
20118c2ecf20Sopenharmony_ci
20128c2ecf20Sopenharmony_ci				/* Reset var options to point to next element */
20138c2ecf20Sopenharmony_ci				if (tmp_end) {
20148c2ecf20Sopenharmony_ci					tmp_end[0] = '\0';
20158c2ecf20Sopenharmony_ci					options = (char *) &tmp_end[1];
20168c2ecf20Sopenharmony_ci				} else
20178c2ecf20Sopenharmony_ci					/* Reached the end of the mount option
20188c2ecf20Sopenharmony_ci					 * string */
20198c2ecf20Sopenharmony_ci					options = end;
20208c2ecf20Sopenharmony_ci			}
20218c2ecf20Sopenharmony_ci
20228c2ecf20Sopenharmony_ci			kfree_sensitive(vol->password);
20238c2ecf20Sopenharmony_ci			/* Now build new password string */
20248c2ecf20Sopenharmony_ci			temp_len = strlen(value);
20258c2ecf20Sopenharmony_ci			vol->password = kzalloc(temp_len+1, GFP_KERNEL);
20268c2ecf20Sopenharmony_ci			if (vol->password == NULL) {
20278c2ecf20Sopenharmony_ci				pr_warn("no memory for password\n");
20288c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
20298c2ecf20Sopenharmony_ci			}
20308c2ecf20Sopenharmony_ci
20318c2ecf20Sopenharmony_ci			for (i = 0, j = 0; i < temp_len; i++, j++) {
20328c2ecf20Sopenharmony_ci				vol->password[j] = value[i];
20338c2ecf20Sopenharmony_ci				if ((value[i] == delim) &&
20348c2ecf20Sopenharmony_ci				     value[i+1] == delim)
20358c2ecf20Sopenharmony_ci					/* skip the second deliminator */
20368c2ecf20Sopenharmony_ci					i++;
20378c2ecf20Sopenharmony_ci			}
20388c2ecf20Sopenharmony_ci			vol->password[j] = '\0';
20398c2ecf20Sopenharmony_ci			break;
20408c2ecf20Sopenharmony_ci		case Opt_blank_ip:
20418c2ecf20Sopenharmony_ci			/* FIXME: should this be an error instead? */
20428c2ecf20Sopenharmony_ci			got_ip = false;
20438c2ecf20Sopenharmony_ci			break;
20448c2ecf20Sopenharmony_ci		case Opt_ip:
20458c2ecf20Sopenharmony_ci			string = match_strdup(args);
20468c2ecf20Sopenharmony_ci			if (string == NULL)
20478c2ecf20Sopenharmony_ci				goto out_nomem;
20488c2ecf20Sopenharmony_ci
20498c2ecf20Sopenharmony_ci			if (!cifs_convert_address(dstaddr, string,
20508c2ecf20Sopenharmony_ci					strlen(string))) {
20518c2ecf20Sopenharmony_ci				pr_err("bad ip= option (%s)\n", string);
20528c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
20538c2ecf20Sopenharmony_ci			}
20548c2ecf20Sopenharmony_ci			got_ip = true;
20558c2ecf20Sopenharmony_ci			break;
20568c2ecf20Sopenharmony_ci		case Opt_domain:
20578c2ecf20Sopenharmony_ci			string = match_strdup(args);
20588c2ecf20Sopenharmony_ci			if (string == NULL)
20598c2ecf20Sopenharmony_ci				goto out_nomem;
20608c2ecf20Sopenharmony_ci
20618c2ecf20Sopenharmony_ci			if (strnlen(string, CIFS_MAX_DOMAINNAME_LEN)
20628c2ecf20Sopenharmony_ci					== CIFS_MAX_DOMAINNAME_LEN) {
20638c2ecf20Sopenharmony_ci				pr_warn("domain name too long\n");
20648c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
20658c2ecf20Sopenharmony_ci			}
20668c2ecf20Sopenharmony_ci
20678c2ecf20Sopenharmony_ci			kfree(vol->domainname);
20688c2ecf20Sopenharmony_ci			vol->domainname = kstrdup(string, GFP_KERNEL);
20698c2ecf20Sopenharmony_ci			if (!vol->domainname) {
20708c2ecf20Sopenharmony_ci				pr_warn("no memory for domainname\n");
20718c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
20728c2ecf20Sopenharmony_ci			}
20738c2ecf20Sopenharmony_ci			cifs_dbg(FYI, "Domain name set\n");
20748c2ecf20Sopenharmony_ci			break;
20758c2ecf20Sopenharmony_ci		case Opt_srcaddr:
20768c2ecf20Sopenharmony_ci			string = match_strdup(args);
20778c2ecf20Sopenharmony_ci			if (string == NULL)
20788c2ecf20Sopenharmony_ci				goto out_nomem;
20798c2ecf20Sopenharmony_ci
20808c2ecf20Sopenharmony_ci			if (!cifs_convert_address(
20818c2ecf20Sopenharmony_ci					(struct sockaddr *)&vol->srcaddr,
20828c2ecf20Sopenharmony_ci					string, strlen(string))) {
20838c2ecf20Sopenharmony_ci				pr_warn("Could not parse srcaddr: %s\n",
20848c2ecf20Sopenharmony_ci					string);
20858c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
20868c2ecf20Sopenharmony_ci			}
20878c2ecf20Sopenharmony_ci			break;
20888c2ecf20Sopenharmony_ci		case Opt_iocharset:
20898c2ecf20Sopenharmony_ci			string = match_strdup(args);
20908c2ecf20Sopenharmony_ci			if (string == NULL)
20918c2ecf20Sopenharmony_ci				goto out_nomem;
20928c2ecf20Sopenharmony_ci
20938c2ecf20Sopenharmony_ci			if (strnlen(string, 1024) >= 65) {
20948c2ecf20Sopenharmony_ci				pr_warn("iocharset name too long\n");
20958c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
20968c2ecf20Sopenharmony_ci			}
20978c2ecf20Sopenharmony_ci
20988c2ecf20Sopenharmony_ci			 if (strncasecmp(string, "default", 7) != 0) {
20998c2ecf20Sopenharmony_ci				kfree(vol->iocharset);
21008c2ecf20Sopenharmony_ci				vol->iocharset = kstrdup(string,
21018c2ecf20Sopenharmony_ci							 GFP_KERNEL);
21028c2ecf20Sopenharmony_ci				if (!vol->iocharset) {
21038c2ecf20Sopenharmony_ci					pr_warn("no memory for charset\n");
21048c2ecf20Sopenharmony_ci					goto cifs_parse_mount_err;
21058c2ecf20Sopenharmony_ci				}
21068c2ecf20Sopenharmony_ci			}
21078c2ecf20Sopenharmony_ci			/* if iocharset not set then load_nls_default
21088c2ecf20Sopenharmony_ci			 * is used by caller
21098c2ecf20Sopenharmony_ci			 */
21108c2ecf20Sopenharmony_ci			 cifs_dbg(FYI, "iocharset set to %s\n", string);
21118c2ecf20Sopenharmony_ci			break;
21128c2ecf20Sopenharmony_ci		case Opt_netbiosname:
21138c2ecf20Sopenharmony_ci			string = match_strdup(args);
21148c2ecf20Sopenharmony_ci			if (string == NULL)
21158c2ecf20Sopenharmony_ci				goto out_nomem;
21168c2ecf20Sopenharmony_ci
21178c2ecf20Sopenharmony_ci			memset(vol->source_rfc1001_name, 0x20,
21188c2ecf20Sopenharmony_ci				RFC1001_NAME_LEN);
21198c2ecf20Sopenharmony_ci			/*
21208c2ecf20Sopenharmony_ci			 * FIXME: are there cases in which a comma can
21218c2ecf20Sopenharmony_ci			 * be valid in workstation netbios name (and
21228c2ecf20Sopenharmony_ci			 * need special handling)?
21238c2ecf20Sopenharmony_ci			 */
21248c2ecf20Sopenharmony_ci			for (i = 0; i < RFC1001_NAME_LEN; i++) {
21258c2ecf20Sopenharmony_ci				/* don't ucase netbiosname for user */
21268c2ecf20Sopenharmony_ci				if (string[i] == 0)
21278c2ecf20Sopenharmony_ci					break;
21288c2ecf20Sopenharmony_ci				vol->source_rfc1001_name[i] = string[i];
21298c2ecf20Sopenharmony_ci			}
21308c2ecf20Sopenharmony_ci			/* The string has 16th byte zero still from
21318c2ecf20Sopenharmony_ci			 * set at top of the function
21328c2ecf20Sopenharmony_ci			 */
21338c2ecf20Sopenharmony_ci			if (i == RFC1001_NAME_LEN && string[i] != 0)
21348c2ecf20Sopenharmony_ci				pr_warn("netbiosname longer than 15 truncated\n");
21358c2ecf20Sopenharmony_ci			break;
21368c2ecf20Sopenharmony_ci		case Opt_servern:
21378c2ecf20Sopenharmony_ci			/* servernetbiosname specified override *SMBSERVER */
21388c2ecf20Sopenharmony_ci			string = match_strdup(args);
21398c2ecf20Sopenharmony_ci			if (string == NULL)
21408c2ecf20Sopenharmony_ci				goto out_nomem;
21418c2ecf20Sopenharmony_ci
21428c2ecf20Sopenharmony_ci			/* last byte, type, is 0x20 for servr type */
21438c2ecf20Sopenharmony_ci			memset(vol->target_rfc1001_name, 0x20,
21448c2ecf20Sopenharmony_ci				RFC1001_NAME_LEN_WITH_NULL);
21458c2ecf20Sopenharmony_ci
21468c2ecf20Sopenharmony_ci			/* BB are there cases in which a comma can be
21478c2ecf20Sopenharmony_ci			   valid in this workstation netbios name
21488c2ecf20Sopenharmony_ci			   (and need special handling)? */
21498c2ecf20Sopenharmony_ci
21508c2ecf20Sopenharmony_ci			/* user or mount helper must uppercase the
21518c2ecf20Sopenharmony_ci			   netbios name */
21528c2ecf20Sopenharmony_ci			for (i = 0; i < 15; i++) {
21538c2ecf20Sopenharmony_ci				if (string[i] == 0)
21548c2ecf20Sopenharmony_ci					break;
21558c2ecf20Sopenharmony_ci				vol->target_rfc1001_name[i] = string[i];
21568c2ecf20Sopenharmony_ci			}
21578c2ecf20Sopenharmony_ci			/* The string has 16th byte zero still from
21588c2ecf20Sopenharmony_ci			   set at top of the function  */
21598c2ecf20Sopenharmony_ci			if (i == RFC1001_NAME_LEN && string[i] != 0)
21608c2ecf20Sopenharmony_ci				pr_warn("server netbiosname longer than 15 truncated\n");
21618c2ecf20Sopenharmony_ci			break;
21628c2ecf20Sopenharmony_ci		case Opt_ver:
21638c2ecf20Sopenharmony_ci			/* version of mount userspace tools, not dialect */
21648c2ecf20Sopenharmony_ci			string = match_strdup(args);
21658c2ecf20Sopenharmony_ci			if (string == NULL)
21668c2ecf20Sopenharmony_ci				goto out_nomem;
21678c2ecf20Sopenharmony_ci
21688c2ecf20Sopenharmony_ci			/* If interface changes in mount.cifs bump to new ver */
21698c2ecf20Sopenharmony_ci			if (strncasecmp(string, "1", 1) == 0) {
21708c2ecf20Sopenharmony_ci				if (strlen(string) > 1) {
21718c2ecf20Sopenharmony_ci					pr_warn("Bad mount helper ver=%s. Did you want SMB1 (CIFS) dialect and mean to type vers=1.0 instead?\n",
21728c2ecf20Sopenharmony_ci						string);
21738c2ecf20Sopenharmony_ci					goto cifs_parse_mount_err;
21748c2ecf20Sopenharmony_ci				}
21758c2ecf20Sopenharmony_ci				/* This is the default */
21768c2ecf20Sopenharmony_ci				break;
21778c2ecf20Sopenharmony_ci			}
21788c2ecf20Sopenharmony_ci			/* For all other value, error */
21798c2ecf20Sopenharmony_ci			pr_warn("Invalid mount helper version specified\n");
21808c2ecf20Sopenharmony_ci			goto cifs_parse_mount_err;
21818c2ecf20Sopenharmony_ci		case Opt_vers:
21828c2ecf20Sopenharmony_ci			/* protocol version (dialect) */
21838c2ecf20Sopenharmony_ci			string = match_strdup(args);
21848c2ecf20Sopenharmony_ci			if (string == NULL)
21858c2ecf20Sopenharmony_ci				goto out_nomem;
21868c2ecf20Sopenharmony_ci
21878c2ecf20Sopenharmony_ci			if (cifs_parse_smb_version(string, vol, is_smb3) != 0)
21888c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
21898c2ecf20Sopenharmony_ci			got_version = true;
21908c2ecf20Sopenharmony_ci			break;
21918c2ecf20Sopenharmony_ci		case Opt_sec:
21928c2ecf20Sopenharmony_ci			string = match_strdup(args);
21938c2ecf20Sopenharmony_ci			if (string == NULL)
21948c2ecf20Sopenharmony_ci				goto out_nomem;
21958c2ecf20Sopenharmony_ci
21968c2ecf20Sopenharmony_ci			if (cifs_parse_security_flavors(string, vol) != 0)
21978c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
21988c2ecf20Sopenharmony_ci			break;
21998c2ecf20Sopenharmony_ci		case Opt_cache:
22008c2ecf20Sopenharmony_ci			string = match_strdup(args);
22018c2ecf20Sopenharmony_ci			if (string == NULL)
22028c2ecf20Sopenharmony_ci				goto out_nomem;
22038c2ecf20Sopenharmony_ci
22048c2ecf20Sopenharmony_ci			if (cifs_parse_cache_flavor(string, vol) != 0)
22058c2ecf20Sopenharmony_ci				goto cifs_parse_mount_err;
22068c2ecf20Sopenharmony_ci			break;
22078c2ecf20Sopenharmony_ci		default:
22088c2ecf20Sopenharmony_ci			/*
22098c2ecf20Sopenharmony_ci			 * An option we don't recognize. Save it off for later
22108c2ecf20Sopenharmony_ci			 * if we haven't already found one
22118c2ecf20Sopenharmony_ci			 */
22128c2ecf20Sopenharmony_ci			if (!invalid)
22138c2ecf20Sopenharmony_ci				invalid = data;
22148c2ecf20Sopenharmony_ci			break;
22158c2ecf20Sopenharmony_ci		}
22168c2ecf20Sopenharmony_ci		/* Free up any allocated string */
22178c2ecf20Sopenharmony_ci		kfree(string);
22188c2ecf20Sopenharmony_ci		string = NULL;
22198c2ecf20Sopenharmony_ci	}
22208c2ecf20Sopenharmony_ci
22218c2ecf20Sopenharmony_ci	if (!sloppy && invalid) {
22228c2ecf20Sopenharmony_ci		pr_err("Unknown mount option \"%s\"\n", invalid);
22238c2ecf20Sopenharmony_ci		goto cifs_parse_mount_err;
22248c2ecf20Sopenharmony_ci	}
22258c2ecf20Sopenharmony_ci
22268c2ecf20Sopenharmony_ci	if (vol->rdma && vol->vals->protocol_id < SMB30_PROT_ID) {
22278c2ecf20Sopenharmony_ci		cifs_dbg(VFS, "SMB Direct requires Version >=3.0\n");
22288c2ecf20Sopenharmony_ci		goto cifs_parse_mount_err;
22298c2ecf20Sopenharmony_ci	}
22308c2ecf20Sopenharmony_ci
22318c2ecf20Sopenharmony_ci#ifndef CONFIG_KEYS
22328c2ecf20Sopenharmony_ci	/* Muliuser mounts require CONFIG_KEYS support */
22338c2ecf20Sopenharmony_ci	if (vol->multiuser) {
22348c2ecf20Sopenharmony_ci		cifs_dbg(VFS, "Multiuser mounts require kernels with CONFIG_KEYS enabled\n");
22358c2ecf20Sopenharmony_ci		goto cifs_parse_mount_err;
22368c2ecf20Sopenharmony_ci	}
22378c2ecf20Sopenharmony_ci#endif
22388c2ecf20Sopenharmony_ci	if (!vol->UNC) {
22398c2ecf20Sopenharmony_ci		cifs_dbg(VFS, "CIFS mount error: No usable UNC path provided in device string!\n");
22408c2ecf20Sopenharmony_ci		goto cifs_parse_mount_err;
22418c2ecf20Sopenharmony_ci	}
22428c2ecf20Sopenharmony_ci
22438c2ecf20Sopenharmony_ci	/* make sure UNC has a share name */
22448c2ecf20Sopenharmony_ci	if (!strchr(vol->UNC + 3, '\\')) {
22458c2ecf20Sopenharmony_ci		cifs_dbg(VFS, "Malformed UNC. Unable to find share name.\n");
22468c2ecf20Sopenharmony_ci		goto cifs_parse_mount_err;
22478c2ecf20Sopenharmony_ci	}
22488c2ecf20Sopenharmony_ci
22498c2ecf20Sopenharmony_ci	if (!got_ip) {
22508c2ecf20Sopenharmony_ci		int len;
22518c2ecf20Sopenharmony_ci		const char *slash;
22528c2ecf20Sopenharmony_ci
22538c2ecf20Sopenharmony_ci		/* No ip= option specified? Try to get it from UNC */
22548c2ecf20Sopenharmony_ci		/* Use the address part of the UNC. */
22558c2ecf20Sopenharmony_ci		slash = strchr(&vol->UNC[2], '\\');
22568c2ecf20Sopenharmony_ci		len = slash - &vol->UNC[2];
22578c2ecf20Sopenharmony_ci		if (!cifs_convert_address(dstaddr, &vol->UNC[2], len)) {
22588c2ecf20Sopenharmony_ci			pr_err("Unable to determine destination address\n");
22598c2ecf20Sopenharmony_ci			goto cifs_parse_mount_err;
22608c2ecf20Sopenharmony_ci		}
22618c2ecf20Sopenharmony_ci	}
22628c2ecf20Sopenharmony_ci
22638c2ecf20Sopenharmony_ci	/* set the port that we got earlier */
22648c2ecf20Sopenharmony_ci	cifs_set_port(dstaddr, port);
22658c2ecf20Sopenharmony_ci
22668c2ecf20Sopenharmony_ci	if (uid_specified)
22678c2ecf20Sopenharmony_ci		vol->override_uid = override_uid;
22688c2ecf20Sopenharmony_ci	else if (override_uid == 1)
22698c2ecf20Sopenharmony_ci		pr_notice("ignoring forceuid mount option specified with no uid= option\n");
22708c2ecf20Sopenharmony_ci
22718c2ecf20Sopenharmony_ci	if (gid_specified)
22728c2ecf20Sopenharmony_ci		vol->override_gid = override_gid;
22738c2ecf20Sopenharmony_ci	else if (override_gid == 1)
22748c2ecf20Sopenharmony_ci		pr_notice("ignoring forcegid mount option specified with no gid= option\n");
22758c2ecf20Sopenharmony_ci
22768c2ecf20Sopenharmony_ci	if (got_version == false)
22778c2ecf20Sopenharmony_ci		pr_warn_once("No dialect specified on mount. Default has changed to a more secure dialect, SMB2.1 or later (e.g. SMB3.1.1), from CIFS (SMB1). To use the less secure SMB1 dialect to access old servers which do not support SMB3.1.1 (or even SMB3 or SMB2.1) specify vers=1.0 on mount.\n");
22788c2ecf20Sopenharmony_ci
22798c2ecf20Sopenharmony_ci	kfree(mountdata_copy);
22808c2ecf20Sopenharmony_ci	return 0;
22818c2ecf20Sopenharmony_ci
22828c2ecf20Sopenharmony_ciout_nomem:
22838c2ecf20Sopenharmony_ci	pr_warn("Could not allocate temporary buffer\n");
22848c2ecf20Sopenharmony_cicifs_parse_mount_err:
22858c2ecf20Sopenharmony_ci	kfree(string);
22868c2ecf20Sopenharmony_ci	kfree(mountdata_copy);
22878c2ecf20Sopenharmony_ci	return 1;
22888c2ecf20Sopenharmony_ci}
22898c2ecf20Sopenharmony_ci
22908c2ecf20Sopenharmony_ci/** Returns true if srcaddr isn't specified and rhs isn't
22918c2ecf20Sopenharmony_ci * specified, or if srcaddr is specified and
22928c2ecf20Sopenharmony_ci * matches the IP address of the rhs argument.
22938c2ecf20Sopenharmony_ci */
22948c2ecf20Sopenharmony_cibool
22958c2ecf20Sopenharmony_cicifs_match_ipaddr(struct sockaddr *srcaddr, struct sockaddr *rhs)
22968c2ecf20Sopenharmony_ci{
22978c2ecf20Sopenharmony_ci	switch (srcaddr->sa_family) {
22988c2ecf20Sopenharmony_ci	case AF_UNSPEC:
22998c2ecf20Sopenharmony_ci		return (rhs->sa_family == AF_UNSPEC);
23008c2ecf20Sopenharmony_ci	case AF_INET: {
23018c2ecf20Sopenharmony_ci		struct sockaddr_in *saddr4 = (struct sockaddr_in *)srcaddr;
23028c2ecf20Sopenharmony_ci		struct sockaddr_in *vaddr4 = (struct sockaddr_in *)rhs;
23038c2ecf20Sopenharmony_ci		return (saddr4->sin_addr.s_addr == vaddr4->sin_addr.s_addr);
23048c2ecf20Sopenharmony_ci	}
23058c2ecf20Sopenharmony_ci	case AF_INET6: {
23068c2ecf20Sopenharmony_ci		struct sockaddr_in6 *saddr6 = (struct sockaddr_in6 *)srcaddr;
23078c2ecf20Sopenharmony_ci		struct sockaddr_in6 *vaddr6 = (struct sockaddr_in6 *)rhs;
23088c2ecf20Sopenharmony_ci		return ipv6_addr_equal(&saddr6->sin6_addr, &vaddr6->sin6_addr);
23098c2ecf20Sopenharmony_ci	}
23108c2ecf20Sopenharmony_ci	default:
23118c2ecf20Sopenharmony_ci		WARN_ON(1);
23128c2ecf20Sopenharmony_ci		return false; /* don't expect to be here */
23138c2ecf20Sopenharmony_ci	}
23148c2ecf20Sopenharmony_ci}
23158c2ecf20Sopenharmony_ci
23168c2ecf20Sopenharmony_ci/*
23178c2ecf20Sopenharmony_ci * If no port is specified in addr structure, we try to match with 445 port
23188c2ecf20Sopenharmony_ci * and if it fails - with 139 ports. It should be called only if address
23198c2ecf20Sopenharmony_ci * families of server and addr are equal.
23208c2ecf20Sopenharmony_ci */
23218c2ecf20Sopenharmony_cistatic bool
23228c2ecf20Sopenharmony_cimatch_port(struct TCP_Server_Info *server, struct sockaddr *addr)
23238c2ecf20Sopenharmony_ci{
23248c2ecf20Sopenharmony_ci	__be16 port, *sport;
23258c2ecf20Sopenharmony_ci
23268c2ecf20Sopenharmony_ci	/* SMBDirect manages its own ports, don't match it here */
23278c2ecf20Sopenharmony_ci	if (server->rdma)
23288c2ecf20Sopenharmony_ci		return true;
23298c2ecf20Sopenharmony_ci
23308c2ecf20Sopenharmony_ci	switch (addr->sa_family) {
23318c2ecf20Sopenharmony_ci	case AF_INET:
23328c2ecf20Sopenharmony_ci		sport = &((struct sockaddr_in *) &server->dstaddr)->sin_port;
23338c2ecf20Sopenharmony_ci		port = ((struct sockaddr_in *) addr)->sin_port;
23348c2ecf20Sopenharmony_ci		break;
23358c2ecf20Sopenharmony_ci	case AF_INET6:
23368c2ecf20Sopenharmony_ci		sport = &((struct sockaddr_in6 *) &server->dstaddr)->sin6_port;
23378c2ecf20Sopenharmony_ci		port = ((struct sockaddr_in6 *) addr)->sin6_port;
23388c2ecf20Sopenharmony_ci		break;
23398c2ecf20Sopenharmony_ci	default:
23408c2ecf20Sopenharmony_ci		WARN_ON(1);
23418c2ecf20Sopenharmony_ci		return false;
23428c2ecf20Sopenharmony_ci	}
23438c2ecf20Sopenharmony_ci
23448c2ecf20Sopenharmony_ci	if (!port) {
23458c2ecf20Sopenharmony_ci		port = htons(CIFS_PORT);
23468c2ecf20Sopenharmony_ci		if (port == *sport)
23478c2ecf20Sopenharmony_ci			return true;
23488c2ecf20Sopenharmony_ci
23498c2ecf20Sopenharmony_ci		port = htons(RFC1001_PORT);
23508c2ecf20Sopenharmony_ci	}
23518c2ecf20Sopenharmony_ci
23528c2ecf20Sopenharmony_ci	return port == *sport;
23538c2ecf20Sopenharmony_ci}
23548c2ecf20Sopenharmony_ci
23558c2ecf20Sopenharmony_cistatic bool
23568c2ecf20Sopenharmony_cimatch_address(struct TCP_Server_Info *server, struct sockaddr *addr,
23578c2ecf20Sopenharmony_ci	      struct sockaddr *srcaddr)
23588c2ecf20Sopenharmony_ci{
23598c2ecf20Sopenharmony_ci	switch (addr->sa_family) {
23608c2ecf20Sopenharmony_ci	case AF_INET: {
23618c2ecf20Sopenharmony_ci		struct sockaddr_in *addr4 = (struct sockaddr_in *)addr;
23628c2ecf20Sopenharmony_ci		struct sockaddr_in *srv_addr4 =
23638c2ecf20Sopenharmony_ci					(struct sockaddr_in *)&server->dstaddr;
23648c2ecf20Sopenharmony_ci
23658c2ecf20Sopenharmony_ci		if (addr4->sin_addr.s_addr != srv_addr4->sin_addr.s_addr)
23668c2ecf20Sopenharmony_ci			return false;
23678c2ecf20Sopenharmony_ci		break;
23688c2ecf20Sopenharmony_ci	}
23698c2ecf20Sopenharmony_ci	case AF_INET6: {
23708c2ecf20Sopenharmony_ci		struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)addr;
23718c2ecf20Sopenharmony_ci		struct sockaddr_in6 *srv_addr6 =
23728c2ecf20Sopenharmony_ci					(struct sockaddr_in6 *)&server->dstaddr;
23738c2ecf20Sopenharmony_ci
23748c2ecf20Sopenharmony_ci		if (!ipv6_addr_equal(&addr6->sin6_addr,
23758c2ecf20Sopenharmony_ci				     &srv_addr6->sin6_addr))
23768c2ecf20Sopenharmony_ci			return false;
23778c2ecf20Sopenharmony_ci		if (addr6->sin6_scope_id != srv_addr6->sin6_scope_id)
23788c2ecf20Sopenharmony_ci			return false;
23798c2ecf20Sopenharmony_ci		break;
23808c2ecf20Sopenharmony_ci	}
23818c2ecf20Sopenharmony_ci	default:
23828c2ecf20Sopenharmony_ci		WARN_ON(1);
23838c2ecf20Sopenharmony_ci		return false; /* don't expect to be here */
23848c2ecf20Sopenharmony_ci	}
23858c2ecf20Sopenharmony_ci
23868c2ecf20Sopenharmony_ci	if (!cifs_match_ipaddr(srcaddr, (struct sockaddr *)&server->srcaddr))
23878c2ecf20Sopenharmony_ci		return false;
23888c2ecf20Sopenharmony_ci
23898c2ecf20Sopenharmony_ci	return true;
23908c2ecf20Sopenharmony_ci}
23918c2ecf20Sopenharmony_ci
23928c2ecf20Sopenharmony_cistatic bool
23938c2ecf20Sopenharmony_cimatch_security(struct TCP_Server_Info *server, struct smb_vol *vol)
23948c2ecf20Sopenharmony_ci{
23958c2ecf20Sopenharmony_ci	/*
23968c2ecf20Sopenharmony_ci	 * The select_sectype function should either return the vol->sectype
23978c2ecf20Sopenharmony_ci	 * that was specified, or "Unspecified" if that sectype was not
23988c2ecf20Sopenharmony_ci	 * compatible with the given NEGOTIATE request.
23998c2ecf20Sopenharmony_ci	 */
24008c2ecf20Sopenharmony_ci	if (server->ops->select_sectype(server, vol->sectype)
24018c2ecf20Sopenharmony_ci	     == Unspecified)
24028c2ecf20Sopenharmony_ci		return false;
24038c2ecf20Sopenharmony_ci
24048c2ecf20Sopenharmony_ci	/*
24058c2ecf20Sopenharmony_ci	 * Now check if signing mode is acceptable. No need to check
24068c2ecf20Sopenharmony_ci	 * global_secflags at this point since if MUST_SIGN is set then
24078c2ecf20Sopenharmony_ci	 * the server->sign had better be too.
24088c2ecf20Sopenharmony_ci	 */
24098c2ecf20Sopenharmony_ci	if (vol->sign && !server->sign)
24108c2ecf20Sopenharmony_ci		return false;
24118c2ecf20Sopenharmony_ci
24128c2ecf20Sopenharmony_ci	return true;
24138c2ecf20Sopenharmony_ci}
24148c2ecf20Sopenharmony_ci
24158c2ecf20Sopenharmony_cistatic int match_server(struct TCP_Server_Info *server, struct smb_vol *vol)
24168c2ecf20Sopenharmony_ci{
24178c2ecf20Sopenharmony_ci	struct sockaddr *addr = (struct sockaddr *)&vol->dstaddr;
24188c2ecf20Sopenharmony_ci
24198c2ecf20Sopenharmony_ci	if (vol->nosharesock)
24208c2ecf20Sopenharmony_ci		return 0;
24218c2ecf20Sopenharmony_ci
24228c2ecf20Sopenharmony_ci	/* If multidialect negotiation see if existing sessions match one */
24238c2ecf20Sopenharmony_ci	if (strcmp(vol->vals->version_string, SMB3ANY_VERSION_STRING) == 0) {
24248c2ecf20Sopenharmony_ci		if (server->vals->protocol_id < SMB30_PROT_ID)
24258c2ecf20Sopenharmony_ci			return 0;
24268c2ecf20Sopenharmony_ci	} else if (strcmp(vol->vals->version_string,
24278c2ecf20Sopenharmony_ci		   SMBDEFAULT_VERSION_STRING) == 0) {
24288c2ecf20Sopenharmony_ci		if (server->vals->protocol_id < SMB21_PROT_ID)
24298c2ecf20Sopenharmony_ci			return 0;
24308c2ecf20Sopenharmony_ci	} else if ((server->vals != vol->vals) || (server->ops != vol->ops))
24318c2ecf20Sopenharmony_ci		return 0;
24328c2ecf20Sopenharmony_ci
24338c2ecf20Sopenharmony_ci	if (!net_eq(cifs_net_ns(server), current->nsproxy->net_ns))
24348c2ecf20Sopenharmony_ci		return 0;
24358c2ecf20Sopenharmony_ci
24368c2ecf20Sopenharmony_ci	if (!match_address(server, addr,
24378c2ecf20Sopenharmony_ci			   (struct sockaddr *)&vol->srcaddr))
24388c2ecf20Sopenharmony_ci		return 0;
24398c2ecf20Sopenharmony_ci
24408c2ecf20Sopenharmony_ci	if (!match_port(server, addr))
24418c2ecf20Sopenharmony_ci		return 0;
24428c2ecf20Sopenharmony_ci
24438c2ecf20Sopenharmony_ci	if (!match_security(server, vol))
24448c2ecf20Sopenharmony_ci		return 0;
24458c2ecf20Sopenharmony_ci
24468c2ecf20Sopenharmony_ci	if (server->echo_interval != vol->echo_interval * HZ)
24478c2ecf20Sopenharmony_ci		return 0;
24488c2ecf20Sopenharmony_ci
24498c2ecf20Sopenharmony_ci	if (server->rdma != vol->rdma)
24508c2ecf20Sopenharmony_ci		return 0;
24518c2ecf20Sopenharmony_ci
24528c2ecf20Sopenharmony_ci	if (server->ignore_signature != vol->ignore_signature)
24538c2ecf20Sopenharmony_ci		return 0;
24548c2ecf20Sopenharmony_ci
24558c2ecf20Sopenharmony_ci	if (server->min_offload != vol->min_offload)
24568c2ecf20Sopenharmony_ci		return 0;
24578c2ecf20Sopenharmony_ci
24588c2ecf20Sopenharmony_ci	return 1;
24598c2ecf20Sopenharmony_ci}
24608c2ecf20Sopenharmony_ci
24618c2ecf20Sopenharmony_cistruct TCP_Server_Info *
24628c2ecf20Sopenharmony_cicifs_find_tcp_session(struct smb_vol *vol)
24638c2ecf20Sopenharmony_ci{
24648c2ecf20Sopenharmony_ci	struct TCP_Server_Info *server;
24658c2ecf20Sopenharmony_ci
24668c2ecf20Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
24678c2ecf20Sopenharmony_ci	list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
24688c2ecf20Sopenharmony_ci		/*
24698c2ecf20Sopenharmony_ci		 * Skip ses channels since they're only handled in lower layers
24708c2ecf20Sopenharmony_ci		 * (e.g. cifs_send_recv).
24718c2ecf20Sopenharmony_ci		 */
24728c2ecf20Sopenharmony_ci		if (server->is_channel || !match_server(server, vol))
24738c2ecf20Sopenharmony_ci			continue;
24748c2ecf20Sopenharmony_ci
24758c2ecf20Sopenharmony_ci		++server->srv_count;
24768c2ecf20Sopenharmony_ci		spin_unlock(&cifs_tcp_ses_lock);
24778c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "Existing tcp session with server found\n");
24788c2ecf20Sopenharmony_ci		return server;
24798c2ecf20Sopenharmony_ci	}
24808c2ecf20Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
24818c2ecf20Sopenharmony_ci	return NULL;
24828c2ecf20Sopenharmony_ci}
24838c2ecf20Sopenharmony_ci
24848c2ecf20Sopenharmony_civoid
24858c2ecf20Sopenharmony_cicifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
24868c2ecf20Sopenharmony_ci{
24878c2ecf20Sopenharmony_ci	struct task_struct *task;
24888c2ecf20Sopenharmony_ci
24898c2ecf20Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
24908c2ecf20Sopenharmony_ci	if (--server->srv_count > 0) {
24918c2ecf20Sopenharmony_ci		spin_unlock(&cifs_tcp_ses_lock);
24928c2ecf20Sopenharmony_ci		return;
24938c2ecf20Sopenharmony_ci	}
24948c2ecf20Sopenharmony_ci
24958c2ecf20Sopenharmony_ci	put_net(cifs_net_ns(server));
24968c2ecf20Sopenharmony_ci
24978c2ecf20Sopenharmony_ci	list_del_init(&server->tcp_ses_list);
24988c2ecf20Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
24998c2ecf20Sopenharmony_ci
25008c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&server->echo);
25018c2ecf20Sopenharmony_ci
25028c2ecf20Sopenharmony_ci	if (from_reconnect)
25038c2ecf20Sopenharmony_ci		/*
25048c2ecf20Sopenharmony_ci		 * Avoid deadlock here: reconnect work calls
25058c2ecf20Sopenharmony_ci		 * cifs_put_tcp_session() at its end. Need to be sure
25068c2ecf20Sopenharmony_ci		 * that reconnect work does nothing with server pointer after
25078c2ecf20Sopenharmony_ci		 * that step.
25088c2ecf20Sopenharmony_ci		 */
25098c2ecf20Sopenharmony_ci		cancel_delayed_work(&server->reconnect);
25108c2ecf20Sopenharmony_ci	else
25118c2ecf20Sopenharmony_ci		cancel_delayed_work_sync(&server->reconnect);
25128c2ecf20Sopenharmony_ci
25138c2ecf20Sopenharmony_ci	spin_lock(&GlobalMid_Lock);
25148c2ecf20Sopenharmony_ci	server->tcpStatus = CifsExiting;
25158c2ecf20Sopenharmony_ci	spin_unlock(&GlobalMid_Lock);
25168c2ecf20Sopenharmony_ci
25178c2ecf20Sopenharmony_ci	cifs_crypto_secmech_release(server);
25188c2ecf20Sopenharmony_ci	cifs_fscache_release_client_cookie(server);
25198c2ecf20Sopenharmony_ci
25208c2ecf20Sopenharmony_ci	kfree(server->session_key.response);
25218c2ecf20Sopenharmony_ci	server->session_key.response = NULL;
25228c2ecf20Sopenharmony_ci	server->session_key.len = 0;
25238c2ecf20Sopenharmony_ci
25248c2ecf20Sopenharmony_ci	task = xchg(&server->tsk, NULL);
25258c2ecf20Sopenharmony_ci	if (task)
25268c2ecf20Sopenharmony_ci		send_sig(SIGKILL, task, 1);
25278c2ecf20Sopenharmony_ci}
25288c2ecf20Sopenharmony_ci
25298c2ecf20Sopenharmony_cistruct TCP_Server_Info *
25308c2ecf20Sopenharmony_cicifs_get_tcp_session(struct smb_vol *volume_info)
25318c2ecf20Sopenharmony_ci{
25328c2ecf20Sopenharmony_ci	struct TCP_Server_Info *tcp_ses = NULL;
25338c2ecf20Sopenharmony_ci	int rc;
25348c2ecf20Sopenharmony_ci
25358c2ecf20Sopenharmony_ci	cifs_dbg(FYI, "UNC: %s\n", volume_info->UNC);
25368c2ecf20Sopenharmony_ci
25378c2ecf20Sopenharmony_ci	/* see if we already have a matching tcp_ses */
25388c2ecf20Sopenharmony_ci	tcp_ses = cifs_find_tcp_session(volume_info);
25398c2ecf20Sopenharmony_ci	if (tcp_ses)
25408c2ecf20Sopenharmony_ci		return tcp_ses;
25418c2ecf20Sopenharmony_ci
25428c2ecf20Sopenharmony_ci	tcp_ses = kzalloc(sizeof(struct TCP_Server_Info), GFP_KERNEL);
25438c2ecf20Sopenharmony_ci	if (!tcp_ses) {
25448c2ecf20Sopenharmony_ci		rc = -ENOMEM;
25458c2ecf20Sopenharmony_ci		goto out_err;
25468c2ecf20Sopenharmony_ci	}
25478c2ecf20Sopenharmony_ci
25488c2ecf20Sopenharmony_ci	tcp_ses->ops = volume_info->ops;
25498c2ecf20Sopenharmony_ci	tcp_ses->vals = volume_info->vals;
25508c2ecf20Sopenharmony_ci	cifs_set_net_ns(tcp_ses, get_net(current->nsproxy->net_ns));
25518c2ecf20Sopenharmony_ci	tcp_ses->hostname = extract_hostname(volume_info->UNC);
25528c2ecf20Sopenharmony_ci	if (IS_ERR(tcp_ses->hostname)) {
25538c2ecf20Sopenharmony_ci		rc = PTR_ERR(tcp_ses->hostname);
25548c2ecf20Sopenharmony_ci		goto out_err_crypto_release;
25558c2ecf20Sopenharmony_ci	}
25568c2ecf20Sopenharmony_ci
25578c2ecf20Sopenharmony_ci	tcp_ses->noblockcnt = volume_info->rootfs;
25588c2ecf20Sopenharmony_ci	tcp_ses->noblocksnd = volume_info->noblocksnd || volume_info->rootfs;
25598c2ecf20Sopenharmony_ci	tcp_ses->noautotune = volume_info->noautotune;
25608c2ecf20Sopenharmony_ci	tcp_ses->tcp_nodelay = volume_info->sockopt_tcp_nodelay;
25618c2ecf20Sopenharmony_ci	tcp_ses->rdma = volume_info->rdma;
25628c2ecf20Sopenharmony_ci	tcp_ses->in_flight = 0;
25638c2ecf20Sopenharmony_ci	tcp_ses->max_in_flight = 0;
25648c2ecf20Sopenharmony_ci	tcp_ses->credits = 1;
25658c2ecf20Sopenharmony_ci	init_waitqueue_head(&tcp_ses->response_q);
25668c2ecf20Sopenharmony_ci	init_waitqueue_head(&tcp_ses->request_q);
25678c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&tcp_ses->pending_mid_q);
25688c2ecf20Sopenharmony_ci	mutex_init(&tcp_ses->srv_mutex);
25698c2ecf20Sopenharmony_ci	memcpy(tcp_ses->workstation_RFC1001_name,
25708c2ecf20Sopenharmony_ci		volume_info->source_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL);
25718c2ecf20Sopenharmony_ci	memcpy(tcp_ses->server_RFC1001_name,
25728c2ecf20Sopenharmony_ci		volume_info->target_rfc1001_name, RFC1001_NAME_LEN_WITH_NULL);
25738c2ecf20Sopenharmony_ci	tcp_ses->session_estab = false;
25748c2ecf20Sopenharmony_ci	tcp_ses->sequence_number = 0;
25758c2ecf20Sopenharmony_ci	tcp_ses->reconnect_instance = 1;
25768c2ecf20Sopenharmony_ci	tcp_ses->lstrp = jiffies;
25778c2ecf20Sopenharmony_ci	tcp_ses->compress_algorithm = cpu_to_le16(volume_info->compression);
25788c2ecf20Sopenharmony_ci	spin_lock_init(&tcp_ses->req_lock);
25798c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&tcp_ses->tcp_ses_list);
25808c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&tcp_ses->smb_ses_list);
25818c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request);
25828c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&tcp_ses->reconnect, smb2_reconnect_server);
25838c2ecf20Sopenharmony_ci	mutex_init(&tcp_ses->reconnect_mutex);
25848c2ecf20Sopenharmony_ci	memcpy(&tcp_ses->srcaddr, &volume_info->srcaddr,
25858c2ecf20Sopenharmony_ci	       sizeof(tcp_ses->srcaddr));
25868c2ecf20Sopenharmony_ci	memcpy(&tcp_ses->dstaddr, &volume_info->dstaddr,
25878c2ecf20Sopenharmony_ci		sizeof(tcp_ses->dstaddr));
25888c2ecf20Sopenharmony_ci	if (volume_info->use_client_guid)
25898c2ecf20Sopenharmony_ci		memcpy(tcp_ses->client_guid, volume_info->client_guid,
25908c2ecf20Sopenharmony_ci		       SMB2_CLIENT_GUID_SIZE);
25918c2ecf20Sopenharmony_ci	else
25928c2ecf20Sopenharmony_ci		generate_random_uuid(tcp_ses->client_guid);
25938c2ecf20Sopenharmony_ci	/*
25948c2ecf20Sopenharmony_ci	 * at this point we are the only ones with the pointer
25958c2ecf20Sopenharmony_ci	 * to the struct since the kernel thread not created yet
25968c2ecf20Sopenharmony_ci	 * no need to spinlock this init of tcpStatus or srv_count
25978c2ecf20Sopenharmony_ci	 */
25988c2ecf20Sopenharmony_ci	tcp_ses->tcpStatus = CifsNew;
25998c2ecf20Sopenharmony_ci	++tcp_ses->srv_count;
26008c2ecf20Sopenharmony_ci
26018c2ecf20Sopenharmony_ci	if (volume_info->echo_interval >= SMB_ECHO_INTERVAL_MIN &&
26028c2ecf20Sopenharmony_ci		volume_info->echo_interval <= SMB_ECHO_INTERVAL_MAX)
26038c2ecf20Sopenharmony_ci		tcp_ses->echo_interval = volume_info->echo_interval * HZ;
26048c2ecf20Sopenharmony_ci	else
26058c2ecf20Sopenharmony_ci		tcp_ses->echo_interval = SMB_ECHO_INTERVAL_DEFAULT * HZ;
26068c2ecf20Sopenharmony_ci	if (tcp_ses->rdma) {
26078c2ecf20Sopenharmony_ci#ifndef CONFIG_CIFS_SMB_DIRECT
26088c2ecf20Sopenharmony_ci		cifs_dbg(VFS, "CONFIG_CIFS_SMB_DIRECT is not enabled\n");
26098c2ecf20Sopenharmony_ci		rc = -ENOENT;
26108c2ecf20Sopenharmony_ci		goto out_err_crypto_release;
26118c2ecf20Sopenharmony_ci#endif
26128c2ecf20Sopenharmony_ci		tcp_ses->smbd_conn = smbd_get_connection(
26138c2ecf20Sopenharmony_ci			tcp_ses, (struct sockaddr *)&volume_info->dstaddr);
26148c2ecf20Sopenharmony_ci		if (tcp_ses->smbd_conn) {
26158c2ecf20Sopenharmony_ci			cifs_dbg(VFS, "RDMA transport established\n");
26168c2ecf20Sopenharmony_ci			rc = 0;
26178c2ecf20Sopenharmony_ci			goto smbd_connected;
26188c2ecf20Sopenharmony_ci		} else {
26198c2ecf20Sopenharmony_ci			rc = -ENOENT;
26208c2ecf20Sopenharmony_ci			goto out_err_crypto_release;
26218c2ecf20Sopenharmony_ci		}
26228c2ecf20Sopenharmony_ci	}
26238c2ecf20Sopenharmony_ci	rc = ip_connect(tcp_ses);
26248c2ecf20Sopenharmony_ci	if (rc < 0) {
26258c2ecf20Sopenharmony_ci		cifs_dbg(VFS, "Error connecting to socket. Aborting operation.\n");
26268c2ecf20Sopenharmony_ci		goto out_err_crypto_release;
26278c2ecf20Sopenharmony_ci	}
26288c2ecf20Sopenharmony_cismbd_connected:
26298c2ecf20Sopenharmony_ci	/*
26308c2ecf20Sopenharmony_ci	 * since we're in a cifs function already, we know that
26318c2ecf20Sopenharmony_ci	 * this will succeed. No need for try_module_get().
26328c2ecf20Sopenharmony_ci	 */
26338c2ecf20Sopenharmony_ci	__module_get(THIS_MODULE);
26348c2ecf20Sopenharmony_ci	tcp_ses->tsk = kthread_run(cifs_demultiplex_thread,
26358c2ecf20Sopenharmony_ci				  tcp_ses, "cifsd");
26368c2ecf20Sopenharmony_ci	if (IS_ERR(tcp_ses->tsk)) {
26378c2ecf20Sopenharmony_ci		rc = PTR_ERR(tcp_ses->tsk);
26388c2ecf20Sopenharmony_ci		cifs_dbg(VFS, "error %d create cifsd thread\n", rc);
26398c2ecf20Sopenharmony_ci		module_put(THIS_MODULE);
26408c2ecf20Sopenharmony_ci		goto out_err_crypto_release;
26418c2ecf20Sopenharmony_ci	}
26428c2ecf20Sopenharmony_ci	tcp_ses->min_offload = volume_info->min_offload;
26438c2ecf20Sopenharmony_ci	tcp_ses->tcpStatus = CifsNeedNegotiate;
26448c2ecf20Sopenharmony_ci
26458c2ecf20Sopenharmony_ci	if ((volume_info->max_credits < 20) || (volume_info->max_credits > 60000))
26468c2ecf20Sopenharmony_ci		tcp_ses->max_credits = SMB2_MAX_CREDITS_AVAILABLE;
26478c2ecf20Sopenharmony_ci	else
26488c2ecf20Sopenharmony_ci		tcp_ses->max_credits = volume_info->max_credits;
26498c2ecf20Sopenharmony_ci
26508c2ecf20Sopenharmony_ci	tcp_ses->nr_targets = 1;
26518c2ecf20Sopenharmony_ci	tcp_ses->ignore_signature = volume_info->ignore_signature;
26528c2ecf20Sopenharmony_ci	/* thread spawned, put it on the list */
26538c2ecf20Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
26548c2ecf20Sopenharmony_ci	list_add(&tcp_ses->tcp_ses_list, &cifs_tcp_ses_list);
26558c2ecf20Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
26568c2ecf20Sopenharmony_ci
26578c2ecf20Sopenharmony_ci	cifs_fscache_get_client_cookie(tcp_ses);
26588c2ecf20Sopenharmony_ci
26598c2ecf20Sopenharmony_ci	/* queue echo request delayed work */
26608c2ecf20Sopenharmony_ci	queue_delayed_work(cifsiod_wq, &tcp_ses->echo, tcp_ses->echo_interval);
26618c2ecf20Sopenharmony_ci
26628c2ecf20Sopenharmony_ci	return tcp_ses;
26638c2ecf20Sopenharmony_ci
26648c2ecf20Sopenharmony_ciout_err_crypto_release:
26658c2ecf20Sopenharmony_ci	cifs_crypto_secmech_release(tcp_ses);
26668c2ecf20Sopenharmony_ci
26678c2ecf20Sopenharmony_ci	put_net(cifs_net_ns(tcp_ses));
26688c2ecf20Sopenharmony_ci
26698c2ecf20Sopenharmony_ciout_err:
26708c2ecf20Sopenharmony_ci	if (tcp_ses) {
26718c2ecf20Sopenharmony_ci		if (!IS_ERR(tcp_ses->hostname))
26728c2ecf20Sopenharmony_ci			kfree(tcp_ses->hostname);
26738c2ecf20Sopenharmony_ci		if (tcp_ses->ssocket)
26748c2ecf20Sopenharmony_ci			sock_release(tcp_ses->ssocket);
26758c2ecf20Sopenharmony_ci		kfree(tcp_ses);
26768c2ecf20Sopenharmony_ci	}
26778c2ecf20Sopenharmony_ci	return ERR_PTR(rc);
26788c2ecf20Sopenharmony_ci}
26798c2ecf20Sopenharmony_ci
26808c2ecf20Sopenharmony_cistatic int match_session(struct cifs_ses *ses, struct smb_vol *vol)
26818c2ecf20Sopenharmony_ci{
26828c2ecf20Sopenharmony_ci	if (vol->sectype != Unspecified &&
26838c2ecf20Sopenharmony_ci	    vol->sectype != ses->sectype)
26848c2ecf20Sopenharmony_ci		return 0;
26858c2ecf20Sopenharmony_ci
26868c2ecf20Sopenharmony_ci	/*
26878c2ecf20Sopenharmony_ci	 * If an existing session is limited to less channels than
26888c2ecf20Sopenharmony_ci	 * requested, it should not be reused
26898c2ecf20Sopenharmony_ci	 */
26908c2ecf20Sopenharmony_ci	if (ses->chan_max < vol->max_channels)
26918c2ecf20Sopenharmony_ci		return 0;
26928c2ecf20Sopenharmony_ci
26938c2ecf20Sopenharmony_ci	switch (ses->sectype) {
26948c2ecf20Sopenharmony_ci	case Kerberos:
26958c2ecf20Sopenharmony_ci		if (!uid_eq(vol->cred_uid, ses->cred_uid))
26968c2ecf20Sopenharmony_ci			return 0;
26978c2ecf20Sopenharmony_ci		break;
26988c2ecf20Sopenharmony_ci	default:
26998c2ecf20Sopenharmony_ci		/* NULL username means anonymous session */
27008c2ecf20Sopenharmony_ci		if (ses->user_name == NULL) {
27018c2ecf20Sopenharmony_ci			if (!vol->nullauth)
27028c2ecf20Sopenharmony_ci				return 0;
27038c2ecf20Sopenharmony_ci			break;
27048c2ecf20Sopenharmony_ci		}
27058c2ecf20Sopenharmony_ci
27068c2ecf20Sopenharmony_ci		/* anything else takes username/password */
27078c2ecf20Sopenharmony_ci		if (strncmp(ses->user_name,
27088c2ecf20Sopenharmony_ci			    vol->username ? vol->username : "",
27098c2ecf20Sopenharmony_ci			    CIFS_MAX_USERNAME_LEN))
27108c2ecf20Sopenharmony_ci			return 0;
27118c2ecf20Sopenharmony_ci		if ((vol->username && strlen(vol->username) != 0) &&
27128c2ecf20Sopenharmony_ci		    ses->password != NULL &&
27138c2ecf20Sopenharmony_ci		    strncmp(ses->password,
27148c2ecf20Sopenharmony_ci			    vol->password ? vol->password : "",
27158c2ecf20Sopenharmony_ci			    CIFS_MAX_PASSWORD_LEN))
27168c2ecf20Sopenharmony_ci			return 0;
27178c2ecf20Sopenharmony_ci	}
27188c2ecf20Sopenharmony_ci	return 1;
27198c2ecf20Sopenharmony_ci}
27208c2ecf20Sopenharmony_ci
27218c2ecf20Sopenharmony_ci/**
27228c2ecf20Sopenharmony_ci * cifs_setup_ipc - helper to setup the IPC tcon for the session
27238c2ecf20Sopenharmony_ci *
27248c2ecf20Sopenharmony_ci * A new IPC connection is made and stored in the session
27258c2ecf20Sopenharmony_ci * tcon_ipc. The IPC tcon has the same lifetime as the session.
27268c2ecf20Sopenharmony_ci */
27278c2ecf20Sopenharmony_cistatic int
27288c2ecf20Sopenharmony_cicifs_setup_ipc(struct cifs_ses *ses, struct smb_vol *volume_info)
27298c2ecf20Sopenharmony_ci{
27308c2ecf20Sopenharmony_ci	int rc = 0, xid;
27318c2ecf20Sopenharmony_ci	struct cifs_tcon *tcon;
27328c2ecf20Sopenharmony_ci	struct nls_table *nls_codepage;
27338c2ecf20Sopenharmony_ci	char unc[SERVER_NAME_LENGTH + sizeof("//x/IPC$")] = {0};
27348c2ecf20Sopenharmony_ci	bool seal = false;
27358c2ecf20Sopenharmony_ci	struct TCP_Server_Info *server = ses->server;
27368c2ecf20Sopenharmony_ci
27378c2ecf20Sopenharmony_ci	/*
27388c2ecf20Sopenharmony_ci	 * If the mount request that resulted in the creation of the
27398c2ecf20Sopenharmony_ci	 * session requires encryption, force IPC to be encrypted too.
27408c2ecf20Sopenharmony_ci	 */
27418c2ecf20Sopenharmony_ci	if (volume_info->seal) {
27428c2ecf20Sopenharmony_ci		if (server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)
27438c2ecf20Sopenharmony_ci			seal = true;
27448c2ecf20Sopenharmony_ci		else {
27458c2ecf20Sopenharmony_ci			cifs_server_dbg(VFS,
27468c2ecf20Sopenharmony_ci				 "IPC: server doesn't support encryption\n");
27478c2ecf20Sopenharmony_ci			return -EOPNOTSUPP;
27488c2ecf20Sopenharmony_ci		}
27498c2ecf20Sopenharmony_ci	}
27508c2ecf20Sopenharmony_ci
27518c2ecf20Sopenharmony_ci	tcon = tconInfoAlloc();
27528c2ecf20Sopenharmony_ci	if (tcon == NULL)
27538c2ecf20Sopenharmony_ci		return -ENOMEM;
27548c2ecf20Sopenharmony_ci
27558c2ecf20Sopenharmony_ci	scnprintf(unc, sizeof(unc), "\\\\%s\\IPC$", server->hostname);
27568c2ecf20Sopenharmony_ci
27578c2ecf20Sopenharmony_ci	/* cannot fail */
27588c2ecf20Sopenharmony_ci	nls_codepage = load_nls_default();
27598c2ecf20Sopenharmony_ci
27608c2ecf20Sopenharmony_ci	xid = get_xid();
27618c2ecf20Sopenharmony_ci	tcon->ses = ses;
27628c2ecf20Sopenharmony_ci	tcon->ipc = true;
27638c2ecf20Sopenharmony_ci	tcon->seal = seal;
27648c2ecf20Sopenharmony_ci	rc = server->ops->tree_connect(xid, ses, unc, tcon, nls_codepage);
27658c2ecf20Sopenharmony_ci	free_xid(xid);
27668c2ecf20Sopenharmony_ci
27678c2ecf20Sopenharmony_ci	if (rc) {
27688c2ecf20Sopenharmony_ci		cifs_server_dbg(VFS, "failed to connect to IPC (rc=%d)\n", rc);
27698c2ecf20Sopenharmony_ci		tconInfoFree(tcon);
27708c2ecf20Sopenharmony_ci		goto out;
27718c2ecf20Sopenharmony_ci	}
27728c2ecf20Sopenharmony_ci
27738c2ecf20Sopenharmony_ci	cifs_dbg(FYI, "IPC tcon rc = %d ipc tid = %d\n", rc, tcon->tid);
27748c2ecf20Sopenharmony_ci
27758c2ecf20Sopenharmony_ci	ses->tcon_ipc = tcon;
27768c2ecf20Sopenharmony_ciout:
27778c2ecf20Sopenharmony_ci	unload_nls(nls_codepage);
27788c2ecf20Sopenharmony_ci	return rc;
27798c2ecf20Sopenharmony_ci}
27808c2ecf20Sopenharmony_ci
27818c2ecf20Sopenharmony_ci/**
27828c2ecf20Sopenharmony_ci * cifs_free_ipc - helper to release the session IPC tcon
27838c2ecf20Sopenharmony_ci *
27848c2ecf20Sopenharmony_ci * Needs to be called everytime a session is destroyed
27858c2ecf20Sopenharmony_ci */
27868c2ecf20Sopenharmony_cistatic int
27878c2ecf20Sopenharmony_cicifs_free_ipc(struct cifs_ses *ses)
27888c2ecf20Sopenharmony_ci{
27898c2ecf20Sopenharmony_ci	int rc = 0, xid;
27908c2ecf20Sopenharmony_ci	struct cifs_tcon *tcon = ses->tcon_ipc;
27918c2ecf20Sopenharmony_ci
27928c2ecf20Sopenharmony_ci	if (tcon == NULL)
27938c2ecf20Sopenharmony_ci		return 0;
27948c2ecf20Sopenharmony_ci
27958c2ecf20Sopenharmony_ci	if (ses->server->ops->tree_disconnect) {
27968c2ecf20Sopenharmony_ci		xid = get_xid();
27978c2ecf20Sopenharmony_ci		rc = ses->server->ops->tree_disconnect(xid, tcon);
27988c2ecf20Sopenharmony_ci		free_xid(xid);
27998c2ecf20Sopenharmony_ci	}
28008c2ecf20Sopenharmony_ci
28018c2ecf20Sopenharmony_ci	if (rc)
28028c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "failed to disconnect IPC tcon (rc=%d)\n", rc);
28038c2ecf20Sopenharmony_ci
28048c2ecf20Sopenharmony_ci	tconInfoFree(tcon);
28058c2ecf20Sopenharmony_ci	ses->tcon_ipc = NULL;
28068c2ecf20Sopenharmony_ci	return rc;
28078c2ecf20Sopenharmony_ci}
28088c2ecf20Sopenharmony_ci
28098c2ecf20Sopenharmony_cistatic struct cifs_ses *
28108c2ecf20Sopenharmony_cicifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol)
28118c2ecf20Sopenharmony_ci{
28128c2ecf20Sopenharmony_ci	struct cifs_ses *ses;
28138c2ecf20Sopenharmony_ci
28148c2ecf20Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
28158c2ecf20Sopenharmony_ci	list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
28168c2ecf20Sopenharmony_ci		if (ses->status == CifsExiting)
28178c2ecf20Sopenharmony_ci			continue;
28188c2ecf20Sopenharmony_ci		if (!match_session(ses, vol))
28198c2ecf20Sopenharmony_ci			continue;
28208c2ecf20Sopenharmony_ci		++ses->ses_count;
28218c2ecf20Sopenharmony_ci		spin_unlock(&cifs_tcp_ses_lock);
28228c2ecf20Sopenharmony_ci		return ses;
28238c2ecf20Sopenharmony_ci	}
28248c2ecf20Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
28258c2ecf20Sopenharmony_ci	return NULL;
28268c2ecf20Sopenharmony_ci}
28278c2ecf20Sopenharmony_ci
28288c2ecf20Sopenharmony_civoid cifs_put_smb_ses(struct cifs_ses *ses)
28298c2ecf20Sopenharmony_ci{
28308c2ecf20Sopenharmony_ci	unsigned int rc, xid;
28318c2ecf20Sopenharmony_ci	struct TCP_Server_Info *server = ses->server;
28328c2ecf20Sopenharmony_ci
28338c2ecf20Sopenharmony_ci	cifs_dbg(FYI, "%s: ses_count=%d\n", __func__, ses->ses_count);
28348c2ecf20Sopenharmony_ci
28358c2ecf20Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
28368c2ecf20Sopenharmony_ci	if (ses->status == CifsExiting) {
28378c2ecf20Sopenharmony_ci		spin_unlock(&cifs_tcp_ses_lock);
28388c2ecf20Sopenharmony_ci		return;
28398c2ecf20Sopenharmony_ci	}
28408c2ecf20Sopenharmony_ci	if (--ses->ses_count > 0) {
28418c2ecf20Sopenharmony_ci		spin_unlock(&cifs_tcp_ses_lock);
28428c2ecf20Sopenharmony_ci		return;
28438c2ecf20Sopenharmony_ci	}
28448c2ecf20Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
28458c2ecf20Sopenharmony_ci
28468c2ecf20Sopenharmony_ci	spin_lock(&GlobalMid_Lock);
28478c2ecf20Sopenharmony_ci	if (ses->status == CifsGood)
28488c2ecf20Sopenharmony_ci		ses->status = CifsExiting;
28498c2ecf20Sopenharmony_ci	spin_unlock(&GlobalMid_Lock);
28508c2ecf20Sopenharmony_ci
28518c2ecf20Sopenharmony_ci	cifs_free_ipc(ses);
28528c2ecf20Sopenharmony_ci
28538c2ecf20Sopenharmony_ci	if (ses->status == CifsExiting && server->ops->logoff) {
28548c2ecf20Sopenharmony_ci		xid = get_xid();
28558c2ecf20Sopenharmony_ci		rc = server->ops->logoff(xid, ses);
28568c2ecf20Sopenharmony_ci		if (rc)
28578c2ecf20Sopenharmony_ci			cifs_server_dbg(VFS, "%s: Session Logoff failure rc=%d\n",
28588c2ecf20Sopenharmony_ci				__func__, rc);
28598c2ecf20Sopenharmony_ci		_free_xid(xid);
28608c2ecf20Sopenharmony_ci	}
28618c2ecf20Sopenharmony_ci
28628c2ecf20Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
28638c2ecf20Sopenharmony_ci	list_del_init(&ses->smb_ses_list);
28648c2ecf20Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
28658c2ecf20Sopenharmony_ci
28668c2ecf20Sopenharmony_ci	/* close any extra channels */
28678c2ecf20Sopenharmony_ci	if (ses->chan_count > 1) {
28688c2ecf20Sopenharmony_ci		int i;
28698c2ecf20Sopenharmony_ci
28708c2ecf20Sopenharmony_ci		for (i = 1; i < ses->chan_count; i++)
28718c2ecf20Sopenharmony_ci			cifs_put_tcp_session(ses->chans[i].server, 0);
28728c2ecf20Sopenharmony_ci	}
28738c2ecf20Sopenharmony_ci
28748c2ecf20Sopenharmony_ci	sesInfoFree(ses);
28758c2ecf20Sopenharmony_ci	cifs_put_tcp_session(server, 0);
28768c2ecf20Sopenharmony_ci}
28778c2ecf20Sopenharmony_ci
28788c2ecf20Sopenharmony_ci#ifdef CONFIG_KEYS
28798c2ecf20Sopenharmony_ci
28808c2ecf20Sopenharmony_ci/* strlen("cifs:a:") + CIFS_MAX_DOMAINNAME_LEN + 1 */
28818c2ecf20Sopenharmony_ci#define CIFSCREDS_DESC_SIZE (7 + CIFS_MAX_DOMAINNAME_LEN + 1)
28828c2ecf20Sopenharmony_ci
28838c2ecf20Sopenharmony_ci/* Populate username and pw fields from keyring if possible */
28848c2ecf20Sopenharmony_cistatic int
28858c2ecf20Sopenharmony_cicifs_set_cifscreds(struct smb_vol *vol, struct cifs_ses *ses)
28868c2ecf20Sopenharmony_ci{
28878c2ecf20Sopenharmony_ci	int rc = 0;
28888c2ecf20Sopenharmony_ci	int is_domain = 0;
28898c2ecf20Sopenharmony_ci	const char *delim, *payload;
28908c2ecf20Sopenharmony_ci	char *desc;
28918c2ecf20Sopenharmony_ci	ssize_t len;
28928c2ecf20Sopenharmony_ci	struct key *key;
28938c2ecf20Sopenharmony_ci	struct TCP_Server_Info *server = ses->server;
28948c2ecf20Sopenharmony_ci	struct sockaddr_in *sa;
28958c2ecf20Sopenharmony_ci	struct sockaddr_in6 *sa6;
28968c2ecf20Sopenharmony_ci	const struct user_key_payload *upayload;
28978c2ecf20Sopenharmony_ci
28988c2ecf20Sopenharmony_ci	desc = kmalloc(CIFSCREDS_DESC_SIZE, GFP_KERNEL);
28998c2ecf20Sopenharmony_ci	if (!desc)
29008c2ecf20Sopenharmony_ci		return -ENOMEM;
29018c2ecf20Sopenharmony_ci
29028c2ecf20Sopenharmony_ci	/* try to find an address key first */
29038c2ecf20Sopenharmony_ci	switch (server->dstaddr.ss_family) {
29048c2ecf20Sopenharmony_ci	case AF_INET:
29058c2ecf20Sopenharmony_ci		sa = (struct sockaddr_in *)&server->dstaddr;
29068c2ecf20Sopenharmony_ci		sprintf(desc, "cifs:a:%pI4", &sa->sin_addr.s_addr);
29078c2ecf20Sopenharmony_ci		break;
29088c2ecf20Sopenharmony_ci	case AF_INET6:
29098c2ecf20Sopenharmony_ci		sa6 = (struct sockaddr_in6 *)&server->dstaddr;
29108c2ecf20Sopenharmony_ci		sprintf(desc, "cifs:a:%pI6c", &sa6->sin6_addr.s6_addr);
29118c2ecf20Sopenharmony_ci		break;
29128c2ecf20Sopenharmony_ci	default:
29138c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "Bad ss_family (%hu)\n",
29148c2ecf20Sopenharmony_ci			 server->dstaddr.ss_family);
29158c2ecf20Sopenharmony_ci		rc = -EINVAL;
29168c2ecf20Sopenharmony_ci		goto out_err;
29178c2ecf20Sopenharmony_ci	}
29188c2ecf20Sopenharmony_ci
29198c2ecf20Sopenharmony_ci	cifs_dbg(FYI, "%s: desc=%s\n", __func__, desc);
29208c2ecf20Sopenharmony_ci	key = request_key(&key_type_logon, desc, "");
29218c2ecf20Sopenharmony_ci	if (IS_ERR(key)) {
29228c2ecf20Sopenharmony_ci		if (!ses->domainName) {
29238c2ecf20Sopenharmony_ci			cifs_dbg(FYI, "domainName is NULL\n");
29248c2ecf20Sopenharmony_ci			rc = PTR_ERR(key);
29258c2ecf20Sopenharmony_ci			goto out_err;
29268c2ecf20Sopenharmony_ci		}
29278c2ecf20Sopenharmony_ci
29288c2ecf20Sopenharmony_ci		/* didn't work, try to find a domain key */
29298c2ecf20Sopenharmony_ci		sprintf(desc, "cifs:d:%s", ses->domainName);
29308c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "%s: desc=%s\n", __func__, desc);
29318c2ecf20Sopenharmony_ci		key = request_key(&key_type_logon, desc, "");
29328c2ecf20Sopenharmony_ci		if (IS_ERR(key)) {
29338c2ecf20Sopenharmony_ci			rc = PTR_ERR(key);
29348c2ecf20Sopenharmony_ci			goto out_err;
29358c2ecf20Sopenharmony_ci		}
29368c2ecf20Sopenharmony_ci		is_domain = 1;
29378c2ecf20Sopenharmony_ci	}
29388c2ecf20Sopenharmony_ci
29398c2ecf20Sopenharmony_ci	down_read(&key->sem);
29408c2ecf20Sopenharmony_ci	upayload = user_key_payload_locked(key);
29418c2ecf20Sopenharmony_ci	if (IS_ERR_OR_NULL(upayload)) {
29428c2ecf20Sopenharmony_ci		rc = upayload ? PTR_ERR(upayload) : -EINVAL;
29438c2ecf20Sopenharmony_ci		goto out_key_put;
29448c2ecf20Sopenharmony_ci	}
29458c2ecf20Sopenharmony_ci
29468c2ecf20Sopenharmony_ci	/* find first : in payload */
29478c2ecf20Sopenharmony_ci	payload = upayload->data;
29488c2ecf20Sopenharmony_ci	delim = strnchr(payload, upayload->datalen, ':');
29498c2ecf20Sopenharmony_ci	cifs_dbg(FYI, "payload=%s\n", payload);
29508c2ecf20Sopenharmony_ci	if (!delim) {
29518c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "Unable to find ':' in payload (datalen=%d)\n",
29528c2ecf20Sopenharmony_ci			 upayload->datalen);
29538c2ecf20Sopenharmony_ci		rc = -EINVAL;
29548c2ecf20Sopenharmony_ci		goto out_key_put;
29558c2ecf20Sopenharmony_ci	}
29568c2ecf20Sopenharmony_ci
29578c2ecf20Sopenharmony_ci	len = delim - payload;
29588c2ecf20Sopenharmony_ci	if (len > CIFS_MAX_USERNAME_LEN || len <= 0) {
29598c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "Bad value from username search (len=%zd)\n",
29608c2ecf20Sopenharmony_ci			 len);
29618c2ecf20Sopenharmony_ci		rc = -EINVAL;
29628c2ecf20Sopenharmony_ci		goto out_key_put;
29638c2ecf20Sopenharmony_ci	}
29648c2ecf20Sopenharmony_ci
29658c2ecf20Sopenharmony_ci	vol->username = kstrndup(payload, len, GFP_KERNEL);
29668c2ecf20Sopenharmony_ci	if (!vol->username) {
29678c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "Unable to allocate %zd bytes for username\n",
29688c2ecf20Sopenharmony_ci			 len);
29698c2ecf20Sopenharmony_ci		rc = -ENOMEM;
29708c2ecf20Sopenharmony_ci		goto out_key_put;
29718c2ecf20Sopenharmony_ci	}
29728c2ecf20Sopenharmony_ci	cifs_dbg(FYI, "%s: username=%s\n", __func__, vol->username);
29738c2ecf20Sopenharmony_ci
29748c2ecf20Sopenharmony_ci	len = key->datalen - (len + 1);
29758c2ecf20Sopenharmony_ci	if (len > CIFS_MAX_PASSWORD_LEN || len <= 0) {
29768c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "Bad len for password search (len=%zd)\n", len);
29778c2ecf20Sopenharmony_ci		rc = -EINVAL;
29788c2ecf20Sopenharmony_ci		kfree(vol->username);
29798c2ecf20Sopenharmony_ci		vol->username = NULL;
29808c2ecf20Sopenharmony_ci		goto out_key_put;
29818c2ecf20Sopenharmony_ci	}
29828c2ecf20Sopenharmony_ci
29838c2ecf20Sopenharmony_ci	++delim;
29848c2ecf20Sopenharmony_ci	vol->password = kstrndup(delim, len, GFP_KERNEL);
29858c2ecf20Sopenharmony_ci	if (!vol->password) {
29868c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "Unable to allocate %zd bytes for password\n",
29878c2ecf20Sopenharmony_ci			 len);
29888c2ecf20Sopenharmony_ci		rc = -ENOMEM;
29898c2ecf20Sopenharmony_ci		kfree(vol->username);
29908c2ecf20Sopenharmony_ci		vol->username = NULL;
29918c2ecf20Sopenharmony_ci		goto out_key_put;
29928c2ecf20Sopenharmony_ci	}
29938c2ecf20Sopenharmony_ci
29948c2ecf20Sopenharmony_ci	/*
29958c2ecf20Sopenharmony_ci	 * If we have a domain key then we must set the domainName in the
29968c2ecf20Sopenharmony_ci	 * for the request.
29978c2ecf20Sopenharmony_ci	 */
29988c2ecf20Sopenharmony_ci	if (is_domain && ses->domainName) {
29998c2ecf20Sopenharmony_ci		vol->domainname = kstrndup(ses->domainName,
30008c2ecf20Sopenharmony_ci					   strlen(ses->domainName),
30018c2ecf20Sopenharmony_ci					   GFP_KERNEL);
30028c2ecf20Sopenharmony_ci		if (!vol->domainname) {
30038c2ecf20Sopenharmony_ci			cifs_dbg(FYI, "Unable to allocate %zd bytes for domain\n",
30048c2ecf20Sopenharmony_ci				 len);
30058c2ecf20Sopenharmony_ci			rc = -ENOMEM;
30068c2ecf20Sopenharmony_ci			kfree(vol->username);
30078c2ecf20Sopenharmony_ci			vol->username = NULL;
30088c2ecf20Sopenharmony_ci			kfree_sensitive(vol->password);
30098c2ecf20Sopenharmony_ci			vol->password = NULL;
30108c2ecf20Sopenharmony_ci			goto out_key_put;
30118c2ecf20Sopenharmony_ci		}
30128c2ecf20Sopenharmony_ci	}
30138c2ecf20Sopenharmony_ci
30148c2ecf20Sopenharmony_ciout_key_put:
30158c2ecf20Sopenharmony_ci	up_read(&key->sem);
30168c2ecf20Sopenharmony_ci	key_put(key);
30178c2ecf20Sopenharmony_ciout_err:
30188c2ecf20Sopenharmony_ci	kfree(desc);
30198c2ecf20Sopenharmony_ci	cifs_dbg(FYI, "%s: returning %d\n", __func__, rc);
30208c2ecf20Sopenharmony_ci	return rc;
30218c2ecf20Sopenharmony_ci}
30228c2ecf20Sopenharmony_ci#else /* ! CONFIG_KEYS */
30238c2ecf20Sopenharmony_cistatic inline int
30248c2ecf20Sopenharmony_cicifs_set_cifscreds(struct smb_vol *vol __attribute__((unused)),
30258c2ecf20Sopenharmony_ci		   struct cifs_ses *ses __attribute__((unused)))
30268c2ecf20Sopenharmony_ci{
30278c2ecf20Sopenharmony_ci	return -ENOSYS;
30288c2ecf20Sopenharmony_ci}
30298c2ecf20Sopenharmony_ci#endif /* CONFIG_KEYS */
30308c2ecf20Sopenharmony_ci
30318c2ecf20Sopenharmony_ci/**
30328c2ecf20Sopenharmony_ci * cifs_get_smb_ses - get a session matching @volume_info data from @server
30338c2ecf20Sopenharmony_ci *
30348c2ecf20Sopenharmony_ci * This function assumes it is being called from cifs_mount() where we
30358c2ecf20Sopenharmony_ci * already got a server reference (server refcount +1). See
30368c2ecf20Sopenharmony_ci * cifs_get_tcon() for refcount explanations.
30378c2ecf20Sopenharmony_ci */
30388c2ecf20Sopenharmony_cistruct cifs_ses *
30398c2ecf20Sopenharmony_cicifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)
30408c2ecf20Sopenharmony_ci{
30418c2ecf20Sopenharmony_ci	int rc = 0;
30428c2ecf20Sopenharmony_ci	unsigned int xid;
30438c2ecf20Sopenharmony_ci	struct cifs_ses *ses;
30448c2ecf20Sopenharmony_ci	struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr;
30458c2ecf20Sopenharmony_ci	struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&server->dstaddr;
30468c2ecf20Sopenharmony_ci
30478c2ecf20Sopenharmony_ci	xid = get_xid();
30488c2ecf20Sopenharmony_ci
30498c2ecf20Sopenharmony_ci	ses = cifs_find_smb_ses(server, volume_info);
30508c2ecf20Sopenharmony_ci	if (ses) {
30518c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "Existing smb sess found (status=%d)\n",
30528c2ecf20Sopenharmony_ci			 ses->status);
30538c2ecf20Sopenharmony_ci
30548c2ecf20Sopenharmony_ci		mutex_lock(&ses->session_mutex);
30558c2ecf20Sopenharmony_ci		rc = cifs_negotiate_protocol(xid, ses);
30568c2ecf20Sopenharmony_ci		if (rc) {
30578c2ecf20Sopenharmony_ci			mutex_unlock(&ses->session_mutex);
30588c2ecf20Sopenharmony_ci			/* problem -- put our ses reference */
30598c2ecf20Sopenharmony_ci			cifs_put_smb_ses(ses);
30608c2ecf20Sopenharmony_ci			free_xid(xid);
30618c2ecf20Sopenharmony_ci			return ERR_PTR(rc);
30628c2ecf20Sopenharmony_ci		}
30638c2ecf20Sopenharmony_ci		if (ses->need_reconnect) {
30648c2ecf20Sopenharmony_ci			cifs_dbg(FYI, "Session needs reconnect\n");
30658c2ecf20Sopenharmony_ci			rc = cifs_setup_session(xid, ses,
30668c2ecf20Sopenharmony_ci						volume_info->local_nls);
30678c2ecf20Sopenharmony_ci			if (rc) {
30688c2ecf20Sopenharmony_ci				mutex_unlock(&ses->session_mutex);
30698c2ecf20Sopenharmony_ci				/* problem -- put our reference */
30708c2ecf20Sopenharmony_ci				cifs_put_smb_ses(ses);
30718c2ecf20Sopenharmony_ci				free_xid(xid);
30728c2ecf20Sopenharmony_ci				return ERR_PTR(rc);
30738c2ecf20Sopenharmony_ci			}
30748c2ecf20Sopenharmony_ci		}
30758c2ecf20Sopenharmony_ci		mutex_unlock(&ses->session_mutex);
30768c2ecf20Sopenharmony_ci
30778c2ecf20Sopenharmony_ci		/* existing SMB ses has a server reference already */
30788c2ecf20Sopenharmony_ci		cifs_put_tcp_session(server, 0);
30798c2ecf20Sopenharmony_ci		free_xid(xid);
30808c2ecf20Sopenharmony_ci		return ses;
30818c2ecf20Sopenharmony_ci	}
30828c2ecf20Sopenharmony_ci
30838c2ecf20Sopenharmony_ci	rc = -ENOMEM;
30848c2ecf20Sopenharmony_ci
30858c2ecf20Sopenharmony_ci	cifs_dbg(FYI, "Existing smb sess not found\n");
30868c2ecf20Sopenharmony_ci	ses = sesInfoAlloc();
30878c2ecf20Sopenharmony_ci	if (ses == NULL)
30888c2ecf20Sopenharmony_ci		goto get_ses_fail;
30898c2ecf20Sopenharmony_ci
30908c2ecf20Sopenharmony_ci	/* new SMB session uses our server ref */
30918c2ecf20Sopenharmony_ci	ses->server = server;
30928c2ecf20Sopenharmony_ci	if (server->dstaddr.ss_family == AF_INET6)
30938c2ecf20Sopenharmony_ci		sprintf(ses->serverName, "%pI6", &addr6->sin6_addr);
30948c2ecf20Sopenharmony_ci	else
30958c2ecf20Sopenharmony_ci		sprintf(ses->serverName, "%pI4", &addr->sin_addr);
30968c2ecf20Sopenharmony_ci
30978c2ecf20Sopenharmony_ci	if (volume_info->username) {
30988c2ecf20Sopenharmony_ci		ses->user_name = kstrdup(volume_info->username, GFP_KERNEL);
30998c2ecf20Sopenharmony_ci		if (!ses->user_name)
31008c2ecf20Sopenharmony_ci			goto get_ses_fail;
31018c2ecf20Sopenharmony_ci	}
31028c2ecf20Sopenharmony_ci
31038c2ecf20Sopenharmony_ci	/* volume_info->password freed at unmount */
31048c2ecf20Sopenharmony_ci	if (volume_info->password) {
31058c2ecf20Sopenharmony_ci		ses->password = kstrdup(volume_info->password, GFP_KERNEL);
31068c2ecf20Sopenharmony_ci		if (!ses->password)
31078c2ecf20Sopenharmony_ci			goto get_ses_fail;
31088c2ecf20Sopenharmony_ci	}
31098c2ecf20Sopenharmony_ci	if (volume_info->domainname) {
31108c2ecf20Sopenharmony_ci		ses->domainName = kstrdup(volume_info->domainname, GFP_KERNEL);
31118c2ecf20Sopenharmony_ci		if (!ses->domainName)
31128c2ecf20Sopenharmony_ci			goto get_ses_fail;
31138c2ecf20Sopenharmony_ci	}
31148c2ecf20Sopenharmony_ci	if (volume_info->domainauto)
31158c2ecf20Sopenharmony_ci		ses->domainAuto = volume_info->domainauto;
31168c2ecf20Sopenharmony_ci	ses->cred_uid = volume_info->cred_uid;
31178c2ecf20Sopenharmony_ci	ses->linux_uid = volume_info->linux_uid;
31188c2ecf20Sopenharmony_ci
31198c2ecf20Sopenharmony_ci	ses->sectype = volume_info->sectype;
31208c2ecf20Sopenharmony_ci	ses->sign = volume_info->sign;
31218c2ecf20Sopenharmony_ci	mutex_lock(&ses->session_mutex);
31228c2ecf20Sopenharmony_ci
31238c2ecf20Sopenharmony_ci	/* add server as first channel */
31248c2ecf20Sopenharmony_ci	ses->chans[0].server = server;
31258c2ecf20Sopenharmony_ci	ses->chan_count = 1;
31268c2ecf20Sopenharmony_ci	ses->chan_max = volume_info->multichannel ? volume_info->max_channels:1;
31278c2ecf20Sopenharmony_ci
31288c2ecf20Sopenharmony_ci	rc = cifs_negotiate_protocol(xid, ses);
31298c2ecf20Sopenharmony_ci	if (!rc)
31308c2ecf20Sopenharmony_ci		rc = cifs_setup_session(xid, ses, volume_info->local_nls);
31318c2ecf20Sopenharmony_ci
31328c2ecf20Sopenharmony_ci	/* each channel uses a different signing key */
31338c2ecf20Sopenharmony_ci	memcpy(ses->chans[0].signkey, ses->smb3signingkey,
31348c2ecf20Sopenharmony_ci	       sizeof(ses->smb3signingkey));
31358c2ecf20Sopenharmony_ci
31368c2ecf20Sopenharmony_ci	mutex_unlock(&ses->session_mutex);
31378c2ecf20Sopenharmony_ci	if (rc)
31388c2ecf20Sopenharmony_ci		goto get_ses_fail;
31398c2ecf20Sopenharmony_ci
31408c2ecf20Sopenharmony_ci	/* success, put it on the list and add it as first channel */
31418c2ecf20Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
31428c2ecf20Sopenharmony_ci	list_add(&ses->smb_ses_list, &server->smb_ses_list);
31438c2ecf20Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
31448c2ecf20Sopenharmony_ci
31458c2ecf20Sopenharmony_ci	free_xid(xid);
31468c2ecf20Sopenharmony_ci
31478c2ecf20Sopenharmony_ci	cifs_setup_ipc(ses, volume_info);
31488c2ecf20Sopenharmony_ci
31498c2ecf20Sopenharmony_ci	return ses;
31508c2ecf20Sopenharmony_ci
31518c2ecf20Sopenharmony_ciget_ses_fail:
31528c2ecf20Sopenharmony_ci	sesInfoFree(ses);
31538c2ecf20Sopenharmony_ci	free_xid(xid);
31548c2ecf20Sopenharmony_ci	return ERR_PTR(rc);
31558c2ecf20Sopenharmony_ci}
31568c2ecf20Sopenharmony_ci
31578c2ecf20Sopenharmony_cistatic int match_tcon(struct cifs_tcon *tcon, struct smb_vol *volume_info)
31588c2ecf20Sopenharmony_ci{
31598c2ecf20Sopenharmony_ci	if (tcon->tidStatus == CifsExiting)
31608c2ecf20Sopenharmony_ci		return 0;
31618c2ecf20Sopenharmony_ci	if (strncmp(tcon->treeName, volume_info->UNC, MAX_TREE_SIZE))
31628c2ecf20Sopenharmony_ci		return 0;
31638c2ecf20Sopenharmony_ci	if (tcon->seal != volume_info->seal)
31648c2ecf20Sopenharmony_ci		return 0;
31658c2ecf20Sopenharmony_ci	if (tcon->snapshot_time != volume_info->snapshot_time)
31668c2ecf20Sopenharmony_ci		return 0;
31678c2ecf20Sopenharmony_ci	if (tcon->handle_timeout != volume_info->handle_timeout)
31688c2ecf20Sopenharmony_ci		return 0;
31698c2ecf20Sopenharmony_ci	if (tcon->no_lease != volume_info->no_lease)
31708c2ecf20Sopenharmony_ci		return 0;
31718c2ecf20Sopenharmony_ci	if (tcon->nodelete != volume_info->nodelete)
31728c2ecf20Sopenharmony_ci		return 0;
31738c2ecf20Sopenharmony_ci	return 1;
31748c2ecf20Sopenharmony_ci}
31758c2ecf20Sopenharmony_ci
31768c2ecf20Sopenharmony_cistatic struct cifs_tcon *
31778c2ecf20Sopenharmony_cicifs_find_tcon(struct cifs_ses *ses, struct smb_vol *volume_info)
31788c2ecf20Sopenharmony_ci{
31798c2ecf20Sopenharmony_ci	struct list_head *tmp;
31808c2ecf20Sopenharmony_ci	struct cifs_tcon *tcon;
31818c2ecf20Sopenharmony_ci
31828c2ecf20Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
31838c2ecf20Sopenharmony_ci	list_for_each(tmp, &ses->tcon_list) {
31848c2ecf20Sopenharmony_ci		tcon = list_entry(tmp, struct cifs_tcon, tcon_list);
31858c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_DFS_UPCALL
31868c2ecf20Sopenharmony_ci		if (tcon->dfs_path)
31878c2ecf20Sopenharmony_ci			continue;
31888c2ecf20Sopenharmony_ci#endif
31898c2ecf20Sopenharmony_ci		if (!match_tcon(tcon, volume_info))
31908c2ecf20Sopenharmony_ci			continue;
31918c2ecf20Sopenharmony_ci		++tcon->tc_count;
31928c2ecf20Sopenharmony_ci		spin_unlock(&cifs_tcp_ses_lock);
31938c2ecf20Sopenharmony_ci		return tcon;
31948c2ecf20Sopenharmony_ci	}
31958c2ecf20Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
31968c2ecf20Sopenharmony_ci	return NULL;
31978c2ecf20Sopenharmony_ci}
31988c2ecf20Sopenharmony_ci
31998c2ecf20Sopenharmony_civoid
32008c2ecf20Sopenharmony_cicifs_put_tcon(struct cifs_tcon *tcon)
32018c2ecf20Sopenharmony_ci{
32028c2ecf20Sopenharmony_ci	unsigned int xid;
32038c2ecf20Sopenharmony_ci	struct cifs_ses *ses;
32048c2ecf20Sopenharmony_ci
32058c2ecf20Sopenharmony_ci	/*
32068c2ecf20Sopenharmony_ci	 * IPC tcon share the lifetime of their session and are
32078c2ecf20Sopenharmony_ci	 * destroyed in the session put function
32088c2ecf20Sopenharmony_ci	 */
32098c2ecf20Sopenharmony_ci	if (tcon == NULL || tcon->ipc)
32108c2ecf20Sopenharmony_ci		return;
32118c2ecf20Sopenharmony_ci
32128c2ecf20Sopenharmony_ci	ses = tcon->ses;
32138c2ecf20Sopenharmony_ci	cifs_dbg(FYI, "%s: tc_count=%d\n", __func__, tcon->tc_count);
32148c2ecf20Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
32158c2ecf20Sopenharmony_ci	if (--tcon->tc_count > 0) {
32168c2ecf20Sopenharmony_ci		spin_unlock(&cifs_tcp_ses_lock);
32178c2ecf20Sopenharmony_ci		return;
32188c2ecf20Sopenharmony_ci	}
32198c2ecf20Sopenharmony_ci
32208c2ecf20Sopenharmony_ci	list_del_init(&tcon->tcon_list);
32218c2ecf20Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
32228c2ecf20Sopenharmony_ci
32238c2ecf20Sopenharmony_ci	xid = get_xid();
32248c2ecf20Sopenharmony_ci	if (ses->server->ops->tree_disconnect)
32258c2ecf20Sopenharmony_ci		ses->server->ops->tree_disconnect(xid, tcon);
32268c2ecf20Sopenharmony_ci	_free_xid(xid);
32278c2ecf20Sopenharmony_ci
32288c2ecf20Sopenharmony_ci	cifs_fscache_release_super_cookie(tcon);
32298c2ecf20Sopenharmony_ci	tconInfoFree(tcon);
32308c2ecf20Sopenharmony_ci	cifs_put_smb_ses(ses);
32318c2ecf20Sopenharmony_ci}
32328c2ecf20Sopenharmony_ci
32338c2ecf20Sopenharmony_ci/**
32348c2ecf20Sopenharmony_ci * cifs_get_tcon - get a tcon matching @volume_info data from @ses
32358c2ecf20Sopenharmony_ci *
32368c2ecf20Sopenharmony_ci * - tcon refcount is the number of mount points using the tcon.
32378c2ecf20Sopenharmony_ci * - ses refcount is the number of tcon using the session.
32388c2ecf20Sopenharmony_ci *
32398c2ecf20Sopenharmony_ci * 1. This function assumes it is being called from cifs_mount() where
32408c2ecf20Sopenharmony_ci *    we already got a session reference (ses refcount +1).
32418c2ecf20Sopenharmony_ci *
32428c2ecf20Sopenharmony_ci * 2. Since we're in the context of adding a mount point, the end
32438c2ecf20Sopenharmony_ci *    result should be either:
32448c2ecf20Sopenharmony_ci *
32458c2ecf20Sopenharmony_ci * a) a new tcon already allocated with refcount=1 (1 mount point) and
32468c2ecf20Sopenharmony_ci *    its session refcount incremented (1 new tcon). This +1 was
32478c2ecf20Sopenharmony_ci *    already done in (1).
32488c2ecf20Sopenharmony_ci *
32498c2ecf20Sopenharmony_ci * b) an existing tcon with refcount+1 (add a mount point to it) and
32508c2ecf20Sopenharmony_ci *    identical ses refcount (no new tcon). Because of (1) we need to
32518c2ecf20Sopenharmony_ci *    decrement the ses refcount.
32528c2ecf20Sopenharmony_ci */
32538c2ecf20Sopenharmony_cistatic struct cifs_tcon *
32548c2ecf20Sopenharmony_cicifs_get_tcon(struct cifs_ses *ses, struct smb_vol *volume_info)
32558c2ecf20Sopenharmony_ci{
32568c2ecf20Sopenharmony_ci	int rc, xid;
32578c2ecf20Sopenharmony_ci	struct cifs_tcon *tcon;
32588c2ecf20Sopenharmony_ci
32598c2ecf20Sopenharmony_ci	tcon = cifs_find_tcon(ses, volume_info);
32608c2ecf20Sopenharmony_ci	if (tcon) {
32618c2ecf20Sopenharmony_ci		/*
32628c2ecf20Sopenharmony_ci		 * tcon has refcount already incremented but we need to
32638c2ecf20Sopenharmony_ci		 * decrement extra ses reference gotten by caller (case b)
32648c2ecf20Sopenharmony_ci		 */
32658c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "Found match on UNC path\n");
32668c2ecf20Sopenharmony_ci		cifs_put_smb_ses(ses);
32678c2ecf20Sopenharmony_ci		return tcon;
32688c2ecf20Sopenharmony_ci	}
32698c2ecf20Sopenharmony_ci
32708c2ecf20Sopenharmony_ci	if (!ses->server->ops->tree_connect) {
32718c2ecf20Sopenharmony_ci		rc = -ENOSYS;
32728c2ecf20Sopenharmony_ci		goto out_fail;
32738c2ecf20Sopenharmony_ci	}
32748c2ecf20Sopenharmony_ci
32758c2ecf20Sopenharmony_ci	tcon = tconInfoAlloc();
32768c2ecf20Sopenharmony_ci	if (tcon == NULL) {
32778c2ecf20Sopenharmony_ci		rc = -ENOMEM;
32788c2ecf20Sopenharmony_ci		goto out_fail;
32798c2ecf20Sopenharmony_ci	}
32808c2ecf20Sopenharmony_ci
32818c2ecf20Sopenharmony_ci	if (volume_info->snapshot_time) {
32828c2ecf20Sopenharmony_ci		if (ses->server->vals->protocol_id == 0) {
32838c2ecf20Sopenharmony_ci			cifs_dbg(VFS,
32848c2ecf20Sopenharmony_ci			     "Use SMB2 or later for snapshot mount option\n");
32858c2ecf20Sopenharmony_ci			rc = -EOPNOTSUPP;
32868c2ecf20Sopenharmony_ci			goto out_fail;
32878c2ecf20Sopenharmony_ci		} else
32888c2ecf20Sopenharmony_ci			tcon->snapshot_time = volume_info->snapshot_time;
32898c2ecf20Sopenharmony_ci	}
32908c2ecf20Sopenharmony_ci
32918c2ecf20Sopenharmony_ci	if (volume_info->handle_timeout) {
32928c2ecf20Sopenharmony_ci		if (ses->server->vals->protocol_id == 0) {
32938c2ecf20Sopenharmony_ci			cifs_dbg(VFS,
32948c2ecf20Sopenharmony_ci			     "Use SMB2.1 or later for handle timeout option\n");
32958c2ecf20Sopenharmony_ci			rc = -EOPNOTSUPP;
32968c2ecf20Sopenharmony_ci			goto out_fail;
32978c2ecf20Sopenharmony_ci		} else
32988c2ecf20Sopenharmony_ci			tcon->handle_timeout = volume_info->handle_timeout;
32998c2ecf20Sopenharmony_ci	}
33008c2ecf20Sopenharmony_ci
33018c2ecf20Sopenharmony_ci	tcon->ses = ses;
33028c2ecf20Sopenharmony_ci	if (volume_info->password) {
33038c2ecf20Sopenharmony_ci		tcon->password = kstrdup(volume_info->password, GFP_KERNEL);
33048c2ecf20Sopenharmony_ci		if (!tcon->password) {
33058c2ecf20Sopenharmony_ci			rc = -ENOMEM;
33068c2ecf20Sopenharmony_ci			goto out_fail;
33078c2ecf20Sopenharmony_ci		}
33088c2ecf20Sopenharmony_ci	}
33098c2ecf20Sopenharmony_ci
33108c2ecf20Sopenharmony_ci	if (volume_info->seal) {
33118c2ecf20Sopenharmony_ci		if (ses->server->vals->protocol_id == 0) {
33128c2ecf20Sopenharmony_ci			cifs_dbg(VFS,
33138c2ecf20Sopenharmony_ci				 "SMB3 or later required for encryption\n");
33148c2ecf20Sopenharmony_ci			rc = -EOPNOTSUPP;
33158c2ecf20Sopenharmony_ci			goto out_fail;
33168c2ecf20Sopenharmony_ci		} else if (tcon->ses->server->capabilities &
33178c2ecf20Sopenharmony_ci					SMB2_GLOBAL_CAP_ENCRYPTION)
33188c2ecf20Sopenharmony_ci			tcon->seal = true;
33198c2ecf20Sopenharmony_ci		else {
33208c2ecf20Sopenharmony_ci			cifs_dbg(VFS, "Encryption is not supported on share\n");
33218c2ecf20Sopenharmony_ci			rc = -EOPNOTSUPP;
33228c2ecf20Sopenharmony_ci			goto out_fail;
33238c2ecf20Sopenharmony_ci		}
33248c2ecf20Sopenharmony_ci	}
33258c2ecf20Sopenharmony_ci
33268c2ecf20Sopenharmony_ci	if (volume_info->linux_ext) {
33278c2ecf20Sopenharmony_ci		if (ses->server->posix_ext_supported) {
33288c2ecf20Sopenharmony_ci			tcon->posix_extensions = true;
33298c2ecf20Sopenharmony_ci			pr_warn_once("SMB3.11 POSIX Extensions are experimental\n");
33308c2ecf20Sopenharmony_ci		} else {
33318c2ecf20Sopenharmony_ci			cifs_dbg(VFS, "Server does not support mounting with posix SMB3.11 extensions\n");
33328c2ecf20Sopenharmony_ci			rc = -EOPNOTSUPP;
33338c2ecf20Sopenharmony_ci			goto out_fail;
33348c2ecf20Sopenharmony_ci		}
33358c2ecf20Sopenharmony_ci	}
33368c2ecf20Sopenharmony_ci
33378c2ecf20Sopenharmony_ci	/*
33388c2ecf20Sopenharmony_ci	 * BB Do we need to wrap session_mutex around this TCon call and Unix
33398c2ecf20Sopenharmony_ci	 * SetFS as we do on SessSetup and reconnect?
33408c2ecf20Sopenharmony_ci	 */
33418c2ecf20Sopenharmony_ci	xid = get_xid();
33428c2ecf20Sopenharmony_ci	rc = ses->server->ops->tree_connect(xid, ses, volume_info->UNC, tcon,
33438c2ecf20Sopenharmony_ci					    volume_info->local_nls);
33448c2ecf20Sopenharmony_ci	free_xid(xid);
33458c2ecf20Sopenharmony_ci	cifs_dbg(FYI, "Tcon rc = %d\n", rc);
33468c2ecf20Sopenharmony_ci	if (rc)
33478c2ecf20Sopenharmony_ci		goto out_fail;
33488c2ecf20Sopenharmony_ci
33498c2ecf20Sopenharmony_ci	tcon->use_persistent = false;
33508c2ecf20Sopenharmony_ci	/* check if SMB2 or later, CIFS does not support persistent handles */
33518c2ecf20Sopenharmony_ci	if (volume_info->persistent) {
33528c2ecf20Sopenharmony_ci		if (ses->server->vals->protocol_id == 0) {
33538c2ecf20Sopenharmony_ci			cifs_dbg(VFS,
33548c2ecf20Sopenharmony_ci			     "SMB3 or later required for persistent handles\n");
33558c2ecf20Sopenharmony_ci			rc = -EOPNOTSUPP;
33568c2ecf20Sopenharmony_ci			goto out_fail;
33578c2ecf20Sopenharmony_ci		} else if (ses->server->capabilities &
33588c2ecf20Sopenharmony_ci			   SMB2_GLOBAL_CAP_PERSISTENT_HANDLES)
33598c2ecf20Sopenharmony_ci			tcon->use_persistent = true;
33608c2ecf20Sopenharmony_ci		else /* persistent handles requested but not supported */ {
33618c2ecf20Sopenharmony_ci			cifs_dbg(VFS,
33628c2ecf20Sopenharmony_ci				"Persistent handles not supported on share\n");
33638c2ecf20Sopenharmony_ci			rc = -EOPNOTSUPP;
33648c2ecf20Sopenharmony_ci			goto out_fail;
33658c2ecf20Sopenharmony_ci		}
33668c2ecf20Sopenharmony_ci	} else if ((tcon->capabilities & SMB2_SHARE_CAP_CONTINUOUS_AVAILABILITY)
33678c2ecf20Sopenharmony_ci	     && (ses->server->capabilities & SMB2_GLOBAL_CAP_PERSISTENT_HANDLES)
33688c2ecf20Sopenharmony_ci	     && (volume_info->nopersistent == false)) {
33698c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "enabling persistent handles\n");
33708c2ecf20Sopenharmony_ci		tcon->use_persistent = true;
33718c2ecf20Sopenharmony_ci	} else if (volume_info->resilient) {
33728c2ecf20Sopenharmony_ci		if (ses->server->vals->protocol_id == 0) {
33738c2ecf20Sopenharmony_ci			cifs_dbg(VFS,
33748c2ecf20Sopenharmony_ci			     "SMB2.1 or later required for resilient handles\n");
33758c2ecf20Sopenharmony_ci			rc = -EOPNOTSUPP;
33768c2ecf20Sopenharmony_ci			goto out_fail;
33778c2ecf20Sopenharmony_ci		}
33788c2ecf20Sopenharmony_ci		tcon->use_resilient = true;
33798c2ecf20Sopenharmony_ci	}
33808c2ecf20Sopenharmony_ci
33818c2ecf20Sopenharmony_ci	/* If the user really knows what they are doing they can override */
33828c2ecf20Sopenharmony_ci	if (tcon->share_flags & SMB2_SHAREFLAG_NO_CACHING) {
33838c2ecf20Sopenharmony_ci		if (volume_info->cache_ro)
33848c2ecf20Sopenharmony_ci			cifs_dbg(VFS, "cache=ro requested on mount but NO_CACHING flag set on share\n");
33858c2ecf20Sopenharmony_ci		else if (volume_info->cache_rw)
33868c2ecf20Sopenharmony_ci			cifs_dbg(VFS, "cache=singleclient requested on mount but NO_CACHING flag set on share\n");
33878c2ecf20Sopenharmony_ci	}
33888c2ecf20Sopenharmony_ci
33898c2ecf20Sopenharmony_ci	if (volume_info->no_lease) {
33908c2ecf20Sopenharmony_ci		if (ses->server->vals->protocol_id == 0) {
33918c2ecf20Sopenharmony_ci			cifs_dbg(VFS,
33928c2ecf20Sopenharmony_ci				"SMB2 or later required for nolease option\n");
33938c2ecf20Sopenharmony_ci			rc = -EOPNOTSUPP;
33948c2ecf20Sopenharmony_ci			goto out_fail;
33958c2ecf20Sopenharmony_ci		} else
33968c2ecf20Sopenharmony_ci			tcon->no_lease = volume_info->no_lease;
33978c2ecf20Sopenharmony_ci	}
33988c2ecf20Sopenharmony_ci
33998c2ecf20Sopenharmony_ci	/*
34008c2ecf20Sopenharmony_ci	 * We can have only one retry value for a connection to a share so for
34018c2ecf20Sopenharmony_ci	 * resources mounted more than once to the same server share the last
34028c2ecf20Sopenharmony_ci	 * value passed in for the retry flag is used.
34038c2ecf20Sopenharmony_ci	 */
34048c2ecf20Sopenharmony_ci	tcon->retry = volume_info->retry;
34058c2ecf20Sopenharmony_ci	tcon->nocase = volume_info->nocase;
34068c2ecf20Sopenharmony_ci	if (ses->server->capabilities & SMB2_GLOBAL_CAP_DIRECTORY_LEASING)
34078c2ecf20Sopenharmony_ci		tcon->nohandlecache = volume_info->nohandlecache;
34088c2ecf20Sopenharmony_ci	else
34098c2ecf20Sopenharmony_ci		tcon->nohandlecache = 1;
34108c2ecf20Sopenharmony_ci	tcon->nodelete = volume_info->nodelete;
34118c2ecf20Sopenharmony_ci	tcon->local_lease = volume_info->local_lease;
34128c2ecf20Sopenharmony_ci	INIT_LIST_HEAD(&tcon->pending_opens);
34138c2ecf20Sopenharmony_ci
34148c2ecf20Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
34158c2ecf20Sopenharmony_ci	list_add(&tcon->tcon_list, &ses->tcon_list);
34168c2ecf20Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
34178c2ecf20Sopenharmony_ci
34188c2ecf20Sopenharmony_ci	cifs_fscache_get_super_cookie(tcon);
34198c2ecf20Sopenharmony_ci
34208c2ecf20Sopenharmony_ci	return tcon;
34218c2ecf20Sopenharmony_ci
34228c2ecf20Sopenharmony_ciout_fail:
34238c2ecf20Sopenharmony_ci	tconInfoFree(tcon);
34248c2ecf20Sopenharmony_ci	return ERR_PTR(rc);
34258c2ecf20Sopenharmony_ci}
34268c2ecf20Sopenharmony_ci
34278c2ecf20Sopenharmony_civoid
34288c2ecf20Sopenharmony_cicifs_put_tlink(struct tcon_link *tlink)
34298c2ecf20Sopenharmony_ci{
34308c2ecf20Sopenharmony_ci	if (!tlink || IS_ERR(tlink))
34318c2ecf20Sopenharmony_ci		return;
34328c2ecf20Sopenharmony_ci
34338c2ecf20Sopenharmony_ci	if (!atomic_dec_and_test(&tlink->tl_count) ||
34348c2ecf20Sopenharmony_ci	    test_bit(TCON_LINK_IN_TREE, &tlink->tl_flags)) {
34358c2ecf20Sopenharmony_ci		tlink->tl_time = jiffies;
34368c2ecf20Sopenharmony_ci		return;
34378c2ecf20Sopenharmony_ci	}
34388c2ecf20Sopenharmony_ci
34398c2ecf20Sopenharmony_ci	if (!IS_ERR(tlink_tcon(tlink)))
34408c2ecf20Sopenharmony_ci		cifs_put_tcon(tlink_tcon(tlink));
34418c2ecf20Sopenharmony_ci	kfree(tlink);
34428c2ecf20Sopenharmony_ci	return;
34438c2ecf20Sopenharmony_ci}
34448c2ecf20Sopenharmony_ci
34458c2ecf20Sopenharmony_cistatic int
34468c2ecf20Sopenharmony_cicompare_mount_options(struct super_block *sb, struct cifs_mnt_data *mnt_data)
34478c2ecf20Sopenharmony_ci{
34488c2ecf20Sopenharmony_ci	struct cifs_sb_info *old = CIFS_SB(sb);
34498c2ecf20Sopenharmony_ci	struct cifs_sb_info *new = mnt_data->cifs_sb;
34508c2ecf20Sopenharmony_ci	unsigned int oldflags = old->mnt_cifs_flags & CIFS_MOUNT_MASK;
34518c2ecf20Sopenharmony_ci	unsigned int newflags = new->mnt_cifs_flags & CIFS_MOUNT_MASK;
34528c2ecf20Sopenharmony_ci
34538c2ecf20Sopenharmony_ci	if ((sb->s_flags & CIFS_MS_MASK) != (mnt_data->flags & CIFS_MS_MASK))
34548c2ecf20Sopenharmony_ci		return 0;
34558c2ecf20Sopenharmony_ci
34568c2ecf20Sopenharmony_ci	if (old->mnt_cifs_serverino_autodisabled)
34578c2ecf20Sopenharmony_ci		newflags &= ~CIFS_MOUNT_SERVER_INUM;
34588c2ecf20Sopenharmony_ci
34598c2ecf20Sopenharmony_ci	if (oldflags != newflags)
34608c2ecf20Sopenharmony_ci		return 0;
34618c2ecf20Sopenharmony_ci
34628c2ecf20Sopenharmony_ci	/*
34638c2ecf20Sopenharmony_ci	 * We want to share sb only if we don't specify an r/wsize or
34648c2ecf20Sopenharmony_ci	 * specified r/wsize is greater than or equal to existing one.
34658c2ecf20Sopenharmony_ci	 */
34668c2ecf20Sopenharmony_ci	if (new->wsize && new->wsize < old->wsize)
34678c2ecf20Sopenharmony_ci		return 0;
34688c2ecf20Sopenharmony_ci
34698c2ecf20Sopenharmony_ci	if (new->rsize && new->rsize < old->rsize)
34708c2ecf20Sopenharmony_ci		return 0;
34718c2ecf20Sopenharmony_ci
34728c2ecf20Sopenharmony_ci	if (!uid_eq(old->mnt_uid, new->mnt_uid) || !gid_eq(old->mnt_gid, new->mnt_gid))
34738c2ecf20Sopenharmony_ci		return 0;
34748c2ecf20Sopenharmony_ci
34758c2ecf20Sopenharmony_ci	if (old->mnt_file_mode != new->mnt_file_mode ||
34768c2ecf20Sopenharmony_ci	    old->mnt_dir_mode != new->mnt_dir_mode)
34778c2ecf20Sopenharmony_ci		return 0;
34788c2ecf20Sopenharmony_ci
34798c2ecf20Sopenharmony_ci	if (strcmp(old->local_nls->charset, new->local_nls->charset))
34808c2ecf20Sopenharmony_ci		return 0;
34818c2ecf20Sopenharmony_ci
34828c2ecf20Sopenharmony_ci	if (old->actimeo != new->actimeo)
34838c2ecf20Sopenharmony_ci		return 0;
34848c2ecf20Sopenharmony_ci
34858c2ecf20Sopenharmony_ci	return 1;
34868c2ecf20Sopenharmony_ci}
34878c2ecf20Sopenharmony_ci
34888c2ecf20Sopenharmony_cistatic int
34898c2ecf20Sopenharmony_cimatch_prepath(struct super_block *sb, struct cifs_mnt_data *mnt_data)
34908c2ecf20Sopenharmony_ci{
34918c2ecf20Sopenharmony_ci	struct cifs_sb_info *old = CIFS_SB(sb);
34928c2ecf20Sopenharmony_ci	struct cifs_sb_info *new = mnt_data->cifs_sb;
34938c2ecf20Sopenharmony_ci	bool old_set = (old->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) &&
34948c2ecf20Sopenharmony_ci		old->prepath;
34958c2ecf20Sopenharmony_ci	bool new_set = (new->mnt_cifs_flags & CIFS_MOUNT_USE_PREFIX_PATH) &&
34968c2ecf20Sopenharmony_ci		new->prepath;
34978c2ecf20Sopenharmony_ci
34988c2ecf20Sopenharmony_ci	if (old_set && new_set && !strcmp(new->prepath, old->prepath))
34998c2ecf20Sopenharmony_ci		return 1;
35008c2ecf20Sopenharmony_ci	else if (!old_set && !new_set)
35018c2ecf20Sopenharmony_ci		return 1;
35028c2ecf20Sopenharmony_ci
35038c2ecf20Sopenharmony_ci	return 0;
35048c2ecf20Sopenharmony_ci}
35058c2ecf20Sopenharmony_ci
35068c2ecf20Sopenharmony_ciint
35078c2ecf20Sopenharmony_cicifs_match_super(struct super_block *sb, void *data)
35088c2ecf20Sopenharmony_ci{
35098c2ecf20Sopenharmony_ci	struct cifs_mnt_data *mnt_data = (struct cifs_mnt_data *)data;
35108c2ecf20Sopenharmony_ci	struct smb_vol *volume_info;
35118c2ecf20Sopenharmony_ci	struct cifs_sb_info *cifs_sb;
35128c2ecf20Sopenharmony_ci	struct TCP_Server_Info *tcp_srv;
35138c2ecf20Sopenharmony_ci	struct cifs_ses *ses;
35148c2ecf20Sopenharmony_ci	struct cifs_tcon *tcon;
35158c2ecf20Sopenharmony_ci	struct tcon_link *tlink;
35168c2ecf20Sopenharmony_ci	int rc = 0;
35178c2ecf20Sopenharmony_ci
35188c2ecf20Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
35198c2ecf20Sopenharmony_ci	cifs_sb = CIFS_SB(sb);
35208c2ecf20Sopenharmony_ci	tlink = cifs_get_tlink(cifs_sb_master_tlink(cifs_sb));
35218c2ecf20Sopenharmony_ci	if (tlink == NULL) {
35228c2ecf20Sopenharmony_ci		/* can not match superblock if tlink were ever null */
35238c2ecf20Sopenharmony_ci		spin_unlock(&cifs_tcp_ses_lock);
35248c2ecf20Sopenharmony_ci		return 0;
35258c2ecf20Sopenharmony_ci	}
35268c2ecf20Sopenharmony_ci	tcon = tlink_tcon(tlink);
35278c2ecf20Sopenharmony_ci	ses = tcon->ses;
35288c2ecf20Sopenharmony_ci	tcp_srv = ses->server;
35298c2ecf20Sopenharmony_ci
35308c2ecf20Sopenharmony_ci	volume_info = mnt_data->vol;
35318c2ecf20Sopenharmony_ci
35328c2ecf20Sopenharmony_ci	if (!match_server(tcp_srv, volume_info) ||
35338c2ecf20Sopenharmony_ci	    !match_session(ses, volume_info) ||
35348c2ecf20Sopenharmony_ci	    !match_tcon(tcon, volume_info) ||
35358c2ecf20Sopenharmony_ci	    !match_prepath(sb, mnt_data)) {
35368c2ecf20Sopenharmony_ci		rc = 0;
35378c2ecf20Sopenharmony_ci		goto out;
35388c2ecf20Sopenharmony_ci	}
35398c2ecf20Sopenharmony_ci
35408c2ecf20Sopenharmony_ci	rc = compare_mount_options(sb, mnt_data);
35418c2ecf20Sopenharmony_ciout:
35428c2ecf20Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
35438c2ecf20Sopenharmony_ci	cifs_put_tlink(tlink);
35448c2ecf20Sopenharmony_ci	return rc;
35458c2ecf20Sopenharmony_ci}
35468c2ecf20Sopenharmony_ci
35478c2ecf20Sopenharmony_ci#ifdef CONFIG_DEBUG_LOCK_ALLOC
35488c2ecf20Sopenharmony_cistatic struct lock_class_key cifs_key[2];
35498c2ecf20Sopenharmony_cistatic struct lock_class_key cifs_slock_key[2];
35508c2ecf20Sopenharmony_ci
35518c2ecf20Sopenharmony_cistatic inline void
35528c2ecf20Sopenharmony_cicifs_reclassify_socket4(struct socket *sock)
35538c2ecf20Sopenharmony_ci{
35548c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
35558c2ecf20Sopenharmony_ci	BUG_ON(!sock_allow_reclassification(sk));
35568c2ecf20Sopenharmony_ci	sock_lock_init_class_and_name(sk, "slock-AF_INET-CIFS",
35578c2ecf20Sopenharmony_ci		&cifs_slock_key[0], "sk_lock-AF_INET-CIFS", &cifs_key[0]);
35588c2ecf20Sopenharmony_ci}
35598c2ecf20Sopenharmony_ci
35608c2ecf20Sopenharmony_cistatic inline void
35618c2ecf20Sopenharmony_cicifs_reclassify_socket6(struct socket *sock)
35628c2ecf20Sopenharmony_ci{
35638c2ecf20Sopenharmony_ci	struct sock *sk = sock->sk;
35648c2ecf20Sopenharmony_ci	BUG_ON(!sock_allow_reclassification(sk));
35658c2ecf20Sopenharmony_ci	sock_lock_init_class_and_name(sk, "slock-AF_INET6-CIFS",
35668c2ecf20Sopenharmony_ci		&cifs_slock_key[1], "sk_lock-AF_INET6-CIFS", &cifs_key[1]);
35678c2ecf20Sopenharmony_ci}
35688c2ecf20Sopenharmony_ci#else
35698c2ecf20Sopenharmony_cistatic inline void
35708c2ecf20Sopenharmony_cicifs_reclassify_socket4(struct socket *sock)
35718c2ecf20Sopenharmony_ci{
35728c2ecf20Sopenharmony_ci}
35738c2ecf20Sopenharmony_ci
35748c2ecf20Sopenharmony_cistatic inline void
35758c2ecf20Sopenharmony_cicifs_reclassify_socket6(struct socket *sock)
35768c2ecf20Sopenharmony_ci{
35778c2ecf20Sopenharmony_ci}
35788c2ecf20Sopenharmony_ci#endif
35798c2ecf20Sopenharmony_ci
35808c2ecf20Sopenharmony_ci/* See RFC1001 section 14 on representation of Netbios names */
35818c2ecf20Sopenharmony_cistatic void rfc1002mangle(char *target, char *source, unsigned int length)
35828c2ecf20Sopenharmony_ci{
35838c2ecf20Sopenharmony_ci	unsigned int i, j;
35848c2ecf20Sopenharmony_ci
35858c2ecf20Sopenharmony_ci	for (i = 0, j = 0; i < (length); i++) {
35868c2ecf20Sopenharmony_ci		/* mask a nibble at a time and encode */
35878c2ecf20Sopenharmony_ci		target[j] = 'A' + (0x0F & (source[i] >> 4));
35888c2ecf20Sopenharmony_ci		target[j+1] = 'A' + (0x0F & source[i]);
35898c2ecf20Sopenharmony_ci		j += 2;
35908c2ecf20Sopenharmony_ci	}
35918c2ecf20Sopenharmony_ci
35928c2ecf20Sopenharmony_ci}
35938c2ecf20Sopenharmony_ci
35948c2ecf20Sopenharmony_cistatic int
35958c2ecf20Sopenharmony_cibind_socket(struct TCP_Server_Info *server)
35968c2ecf20Sopenharmony_ci{
35978c2ecf20Sopenharmony_ci	int rc = 0;
35988c2ecf20Sopenharmony_ci	if (server->srcaddr.ss_family != AF_UNSPEC) {
35998c2ecf20Sopenharmony_ci		/* Bind to the specified local IP address */
36008c2ecf20Sopenharmony_ci		struct socket *socket = server->ssocket;
36018c2ecf20Sopenharmony_ci		rc = socket->ops->bind(socket,
36028c2ecf20Sopenharmony_ci				       (struct sockaddr *) &server->srcaddr,
36038c2ecf20Sopenharmony_ci				       sizeof(server->srcaddr));
36048c2ecf20Sopenharmony_ci		if (rc < 0) {
36058c2ecf20Sopenharmony_ci			struct sockaddr_in *saddr4;
36068c2ecf20Sopenharmony_ci			struct sockaddr_in6 *saddr6;
36078c2ecf20Sopenharmony_ci			saddr4 = (struct sockaddr_in *)&server->srcaddr;
36088c2ecf20Sopenharmony_ci			saddr6 = (struct sockaddr_in6 *)&server->srcaddr;
36098c2ecf20Sopenharmony_ci			if (saddr6->sin6_family == AF_INET6)
36108c2ecf20Sopenharmony_ci				cifs_server_dbg(VFS, "Failed to bind to: %pI6c, error: %d\n",
36118c2ecf20Sopenharmony_ci					 &saddr6->sin6_addr, rc);
36128c2ecf20Sopenharmony_ci			else
36138c2ecf20Sopenharmony_ci				cifs_server_dbg(VFS, "Failed to bind to: %pI4, error: %d\n",
36148c2ecf20Sopenharmony_ci					 &saddr4->sin_addr.s_addr, rc);
36158c2ecf20Sopenharmony_ci		}
36168c2ecf20Sopenharmony_ci	}
36178c2ecf20Sopenharmony_ci	return rc;
36188c2ecf20Sopenharmony_ci}
36198c2ecf20Sopenharmony_ci
36208c2ecf20Sopenharmony_cistatic int
36218c2ecf20Sopenharmony_ciip_rfc1001_connect(struct TCP_Server_Info *server)
36228c2ecf20Sopenharmony_ci{
36238c2ecf20Sopenharmony_ci	int rc = 0;
36248c2ecf20Sopenharmony_ci	/*
36258c2ecf20Sopenharmony_ci	 * some servers require RFC1001 sessinit before sending
36268c2ecf20Sopenharmony_ci	 * negprot - BB check reconnection in case where second
36278c2ecf20Sopenharmony_ci	 * sessinit is sent but no second negprot
36288c2ecf20Sopenharmony_ci	 */
36298c2ecf20Sopenharmony_ci	struct rfc1002_session_packet *ses_init_buf;
36308c2ecf20Sopenharmony_ci	struct smb_hdr *smb_buf;
36318c2ecf20Sopenharmony_ci	ses_init_buf = kzalloc(sizeof(struct rfc1002_session_packet),
36328c2ecf20Sopenharmony_ci			       GFP_KERNEL);
36338c2ecf20Sopenharmony_ci	if (ses_init_buf) {
36348c2ecf20Sopenharmony_ci		ses_init_buf->trailer.session_req.called_len = 32;
36358c2ecf20Sopenharmony_ci
36368c2ecf20Sopenharmony_ci		if (server->server_RFC1001_name[0] != 0)
36378c2ecf20Sopenharmony_ci			rfc1002mangle(ses_init_buf->trailer.
36388c2ecf20Sopenharmony_ci				      session_req.called_name,
36398c2ecf20Sopenharmony_ci				      server->server_RFC1001_name,
36408c2ecf20Sopenharmony_ci				      RFC1001_NAME_LEN_WITH_NULL);
36418c2ecf20Sopenharmony_ci		else
36428c2ecf20Sopenharmony_ci			rfc1002mangle(ses_init_buf->trailer.
36438c2ecf20Sopenharmony_ci				      session_req.called_name,
36448c2ecf20Sopenharmony_ci				      DEFAULT_CIFS_CALLED_NAME,
36458c2ecf20Sopenharmony_ci				      RFC1001_NAME_LEN_WITH_NULL);
36468c2ecf20Sopenharmony_ci
36478c2ecf20Sopenharmony_ci		ses_init_buf->trailer.session_req.calling_len = 32;
36488c2ecf20Sopenharmony_ci
36498c2ecf20Sopenharmony_ci		/*
36508c2ecf20Sopenharmony_ci		 * calling name ends in null (byte 16) from old smb
36518c2ecf20Sopenharmony_ci		 * convention.
36528c2ecf20Sopenharmony_ci		 */
36538c2ecf20Sopenharmony_ci		if (server->workstation_RFC1001_name[0] != 0)
36548c2ecf20Sopenharmony_ci			rfc1002mangle(ses_init_buf->trailer.
36558c2ecf20Sopenharmony_ci				      session_req.calling_name,
36568c2ecf20Sopenharmony_ci				      server->workstation_RFC1001_name,
36578c2ecf20Sopenharmony_ci				      RFC1001_NAME_LEN_WITH_NULL);
36588c2ecf20Sopenharmony_ci		else
36598c2ecf20Sopenharmony_ci			rfc1002mangle(ses_init_buf->trailer.
36608c2ecf20Sopenharmony_ci				      session_req.calling_name,
36618c2ecf20Sopenharmony_ci				      "LINUX_CIFS_CLNT",
36628c2ecf20Sopenharmony_ci				      RFC1001_NAME_LEN_WITH_NULL);
36638c2ecf20Sopenharmony_ci
36648c2ecf20Sopenharmony_ci		ses_init_buf->trailer.session_req.scope1 = 0;
36658c2ecf20Sopenharmony_ci		ses_init_buf->trailer.session_req.scope2 = 0;
36668c2ecf20Sopenharmony_ci		smb_buf = (struct smb_hdr *)ses_init_buf;
36678c2ecf20Sopenharmony_ci
36688c2ecf20Sopenharmony_ci		/* sizeof RFC1002_SESSION_REQUEST with no scope */
36698c2ecf20Sopenharmony_ci		smb_buf->smb_buf_length = cpu_to_be32(0x81000044);
36708c2ecf20Sopenharmony_ci		rc = smb_send(server, smb_buf, 0x44);
36718c2ecf20Sopenharmony_ci		kfree(ses_init_buf);
36728c2ecf20Sopenharmony_ci		/*
36738c2ecf20Sopenharmony_ci		 * RFC1001 layer in at least one server
36748c2ecf20Sopenharmony_ci		 * requires very short break before negprot
36758c2ecf20Sopenharmony_ci		 * presumably because not expecting negprot
36768c2ecf20Sopenharmony_ci		 * to follow so fast.  This is a simple
36778c2ecf20Sopenharmony_ci		 * solution that works without
36788c2ecf20Sopenharmony_ci		 * complicating the code and causes no
36798c2ecf20Sopenharmony_ci		 * significant slowing down on mount
36808c2ecf20Sopenharmony_ci		 * for everyone else
36818c2ecf20Sopenharmony_ci		 */
36828c2ecf20Sopenharmony_ci		usleep_range(1000, 2000);
36838c2ecf20Sopenharmony_ci	}
36848c2ecf20Sopenharmony_ci	/*
36858c2ecf20Sopenharmony_ci	 * else the negprot may still work without this
36868c2ecf20Sopenharmony_ci	 * even though malloc failed
36878c2ecf20Sopenharmony_ci	 */
36888c2ecf20Sopenharmony_ci
36898c2ecf20Sopenharmony_ci	return rc;
36908c2ecf20Sopenharmony_ci}
36918c2ecf20Sopenharmony_ci
36928c2ecf20Sopenharmony_cistatic int
36938c2ecf20Sopenharmony_cigeneric_ip_connect(struct TCP_Server_Info *server)
36948c2ecf20Sopenharmony_ci{
36958c2ecf20Sopenharmony_ci	int rc = 0;
36968c2ecf20Sopenharmony_ci	__be16 sport;
36978c2ecf20Sopenharmony_ci	int slen, sfamily;
36988c2ecf20Sopenharmony_ci	struct socket *socket = server->ssocket;
36998c2ecf20Sopenharmony_ci	struct sockaddr *saddr;
37008c2ecf20Sopenharmony_ci
37018c2ecf20Sopenharmony_ci	saddr = (struct sockaddr *) &server->dstaddr;
37028c2ecf20Sopenharmony_ci
37038c2ecf20Sopenharmony_ci	if (server->dstaddr.ss_family == AF_INET6) {
37048c2ecf20Sopenharmony_ci		struct sockaddr_in6 *ipv6 = (struct sockaddr_in6 *)&server->dstaddr;
37058c2ecf20Sopenharmony_ci
37068c2ecf20Sopenharmony_ci		sport = ipv6->sin6_port;
37078c2ecf20Sopenharmony_ci		slen = sizeof(struct sockaddr_in6);
37088c2ecf20Sopenharmony_ci		sfamily = AF_INET6;
37098c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "%s: connecting to [%pI6]:%d\n", __func__, &ipv6->sin6_addr,
37108c2ecf20Sopenharmony_ci				ntohs(sport));
37118c2ecf20Sopenharmony_ci	} else {
37128c2ecf20Sopenharmony_ci		struct sockaddr_in *ipv4 = (struct sockaddr_in *)&server->dstaddr;
37138c2ecf20Sopenharmony_ci
37148c2ecf20Sopenharmony_ci		sport = ipv4->sin_port;
37158c2ecf20Sopenharmony_ci		slen = sizeof(struct sockaddr_in);
37168c2ecf20Sopenharmony_ci		sfamily = AF_INET;
37178c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "%s: connecting to %pI4:%d\n", __func__, &ipv4->sin_addr,
37188c2ecf20Sopenharmony_ci				ntohs(sport));
37198c2ecf20Sopenharmony_ci	}
37208c2ecf20Sopenharmony_ci
37218c2ecf20Sopenharmony_ci	if (socket == NULL) {
37228c2ecf20Sopenharmony_ci		rc = __sock_create(cifs_net_ns(server), sfamily, SOCK_STREAM,
37238c2ecf20Sopenharmony_ci				   IPPROTO_TCP, &socket, 1);
37248c2ecf20Sopenharmony_ci		if (rc < 0) {
37258c2ecf20Sopenharmony_ci			cifs_server_dbg(VFS, "Error %d creating socket\n", rc);
37268c2ecf20Sopenharmony_ci			server->ssocket = NULL;
37278c2ecf20Sopenharmony_ci			return rc;
37288c2ecf20Sopenharmony_ci		}
37298c2ecf20Sopenharmony_ci
37308c2ecf20Sopenharmony_ci		/* BB other socket options to set KEEPALIVE, NODELAY? */
37318c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "Socket created\n");
37328c2ecf20Sopenharmony_ci		server->ssocket = socket;
37338c2ecf20Sopenharmony_ci		socket->sk->sk_allocation = GFP_NOFS;
37348c2ecf20Sopenharmony_ci		if (sfamily == AF_INET6)
37358c2ecf20Sopenharmony_ci			cifs_reclassify_socket6(socket);
37368c2ecf20Sopenharmony_ci		else
37378c2ecf20Sopenharmony_ci			cifs_reclassify_socket4(socket);
37388c2ecf20Sopenharmony_ci	}
37398c2ecf20Sopenharmony_ci
37408c2ecf20Sopenharmony_ci	rc = bind_socket(server);
37418c2ecf20Sopenharmony_ci	if (rc < 0)
37428c2ecf20Sopenharmony_ci		return rc;
37438c2ecf20Sopenharmony_ci
37448c2ecf20Sopenharmony_ci	/*
37458c2ecf20Sopenharmony_ci	 * Eventually check for other socket options to change from
37468c2ecf20Sopenharmony_ci	 * the default. sock_setsockopt not used because it expects
37478c2ecf20Sopenharmony_ci	 * user space buffer
37488c2ecf20Sopenharmony_ci	 */
37498c2ecf20Sopenharmony_ci	socket->sk->sk_rcvtimeo = 7 * HZ;
37508c2ecf20Sopenharmony_ci	socket->sk->sk_sndtimeo = 5 * HZ;
37518c2ecf20Sopenharmony_ci
37528c2ecf20Sopenharmony_ci	/* make the bufsizes depend on wsize/rsize and max requests */
37538c2ecf20Sopenharmony_ci	if (server->noautotune) {
37548c2ecf20Sopenharmony_ci		if (socket->sk->sk_sndbuf < (200 * 1024))
37558c2ecf20Sopenharmony_ci			socket->sk->sk_sndbuf = 200 * 1024;
37568c2ecf20Sopenharmony_ci		if (socket->sk->sk_rcvbuf < (140 * 1024))
37578c2ecf20Sopenharmony_ci			socket->sk->sk_rcvbuf = 140 * 1024;
37588c2ecf20Sopenharmony_ci	}
37598c2ecf20Sopenharmony_ci
37608c2ecf20Sopenharmony_ci	if (server->tcp_nodelay)
37618c2ecf20Sopenharmony_ci		tcp_sock_set_nodelay(socket->sk);
37628c2ecf20Sopenharmony_ci
37638c2ecf20Sopenharmony_ci	cifs_dbg(FYI, "sndbuf %d rcvbuf %d rcvtimeo 0x%lx\n",
37648c2ecf20Sopenharmony_ci		 socket->sk->sk_sndbuf,
37658c2ecf20Sopenharmony_ci		 socket->sk->sk_rcvbuf, socket->sk->sk_rcvtimeo);
37668c2ecf20Sopenharmony_ci
37678c2ecf20Sopenharmony_ci	rc = socket->ops->connect(socket, saddr, slen,
37688c2ecf20Sopenharmony_ci				  server->noblockcnt ? O_NONBLOCK : 0);
37698c2ecf20Sopenharmony_ci	/*
37708c2ecf20Sopenharmony_ci	 * When mounting SMB root file systems, we do not want to block in
37718c2ecf20Sopenharmony_ci	 * connect. Otherwise bail out and then let cifs_reconnect() perform
37728c2ecf20Sopenharmony_ci	 * reconnect failover - if possible.
37738c2ecf20Sopenharmony_ci	 */
37748c2ecf20Sopenharmony_ci	if (server->noblockcnt && rc == -EINPROGRESS)
37758c2ecf20Sopenharmony_ci		rc = 0;
37768c2ecf20Sopenharmony_ci	if (rc < 0) {
37778c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "Error %d connecting to server\n", rc);
37788c2ecf20Sopenharmony_ci		sock_release(socket);
37798c2ecf20Sopenharmony_ci		server->ssocket = NULL;
37808c2ecf20Sopenharmony_ci		return rc;
37818c2ecf20Sopenharmony_ci	}
37828c2ecf20Sopenharmony_ci
37838c2ecf20Sopenharmony_ci	if (sport == htons(RFC1001_PORT))
37848c2ecf20Sopenharmony_ci		rc = ip_rfc1001_connect(server);
37858c2ecf20Sopenharmony_ci
37868c2ecf20Sopenharmony_ci	return rc;
37878c2ecf20Sopenharmony_ci}
37888c2ecf20Sopenharmony_ci
37898c2ecf20Sopenharmony_cistatic int
37908c2ecf20Sopenharmony_ciip_connect(struct TCP_Server_Info *server)
37918c2ecf20Sopenharmony_ci{
37928c2ecf20Sopenharmony_ci	__be16 *sport;
37938c2ecf20Sopenharmony_ci	struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&server->dstaddr;
37948c2ecf20Sopenharmony_ci	struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr;
37958c2ecf20Sopenharmony_ci
37968c2ecf20Sopenharmony_ci	if (server->dstaddr.ss_family == AF_INET6)
37978c2ecf20Sopenharmony_ci		sport = &addr6->sin6_port;
37988c2ecf20Sopenharmony_ci	else
37998c2ecf20Sopenharmony_ci		sport = &addr->sin_port;
38008c2ecf20Sopenharmony_ci
38018c2ecf20Sopenharmony_ci	if (*sport == 0) {
38028c2ecf20Sopenharmony_ci		int rc;
38038c2ecf20Sopenharmony_ci
38048c2ecf20Sopenharmony_ci		/* try with 445 port at first */
38058c2ecf20Sopenharmony_ci		*sport = htons(CIFS_PORT);
38068c2ecf20Sopenharmony_ci
38078c2ecf20Sopenharmony_ci		rc = generic_ip_connect(server);
38088c2ecf20Sopenharmony_ci		if (rc >= 0)
38098c2ecf20Sopenharmony_ci			return rc;
38108c2ecf20Sopenharmony_ci
38118c2ecf20Sopenharmony_ci		/* if it failed, try with 139 port */
38128c2ecf20Sopenharmony_ci		*sport = htons(RFC1001_PORT);
38138c2ecf20Sopenharmony_ci	}
38148c2ecf20Sopenharmony_ci
38158c2ecf20Sopenharmony_ci	return generic_ip_connect(server);
38168c2ecf20Sopenharmony_ci}
38178c2ecf20Sopenharmony_ci
38188c2ecf20Sopenharmony_civoid reset_cifs_unix_caps(unsigned int xid, struct cifs_tcon *tcon,
38198c2ecf20Sopenharmony_ci			  struct cifs_sb_info *cifs_sb, struct smb_vol *vol_info)
38208c2ecf20Sopenharmony_ci{
38218c2ecf20Sopenharmony_ci	/* if we are reconnecting then should we check to see if
38228c2ecf20Sopenharmony_ci	 * any requested capabilities changed locally e.g. via
38238c2ecf20Sopenharmony_ci	 * remount but we can not do much about it here
38248c2ecf20Sopenharmony_ci	 * if they have (even if we could detect it by the following)
38258c2ecf20Sopenharmony_ci	 * Perhaps we could add a backpointer to array of sb from tcon
38268c2ecf20Sopenharmony_ci	 * or if we change to make all sb to same share the same
38278c2ecf20Sopenharmony_ci	 * sb as NFS - then we only have one backpointer to sb.
38288c2ecf20Sopenharmony_ci	 * What if we wanted to mount the server share twice once with
38298c2ecf20Sopenharmony_ci	 * and once without posixacls or posix paths? */
38308c2ecf20Sopenharmony_ci	__u64 saved_cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
38318c2ecf20Sopenharmony_ci
38328c2ecf20Sopenharmony_ci	if (vol_info && vol_info->no_linux_ext) {
38338c2ecf20Sopenharmony_ci		tcon->fsUnixInfo.Capability = 0;
38348c2ecf20Sopenharmony_ci		tcon->unix_ext = 0; /* Unix Extensions disabled */
38358c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "Linux protocol extensions disabled\n");
38368c2ecf20Sopenharmony_ci		return;
38378c2ecf20Sopenharmony_ci	} else if (vol_info)
38388c2ecf20Sopenharmony_ci		tcon->unix_ext = 1; /* Unix Extensions supported */
38398c2ecf20Sopenharmony_ci
38408c2ecf20Sopenharmony_ci	if (tcon->unix_ext == 0) {
38418c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "Unix extensions disabled so not set on reconnect\n");
38428c2ecf20Sopenharmony_ci		return;
38438c2ecf20Sopenharmony_ci	}
38448c2ecf20Sopenharmony_ci
38458c2ecf20Sopenharmony_ci	if (!CIFSSMBQFSUnixInfo(xid, tcon)) {
38468c2ecf20Sopenharmony_ci		__u64 cap = le64_to_cpu(tcon->fsUnixInfo.Capability);
38478c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "unix caps which server supports %lld\n", cap);
38488c2ecf20Sopenharmony_ci		/* check for reconnect case in which we do not
38498c2ecf20Sopenharmony_ci		   want to change the mount behavior if we can avoid it */
38508c2ecf20Sopenharmony_ci		if (vol_info == NULL) {
38518c2ecf20Sopenharmony_ci			/* turn off POSIX ACL and PATHNAMES if not set
38528c2ecf20Sopenharmony_ci			   originally at mount time */
38538c2ecf20Sopenharmony_ci			if ((saved_cap & CIFS_UNIX_POSIX_ACL_CAP) == 0)
38548c2ecf20Sopenharmony_ci				cap &= ~CIFS_UNIX_POSIX_ACL_CAP;
38558c2ecf20Sopenharmony_ci			if ((saved_cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0) {
38568c2ecf20Sopenharmony_ci				if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP)
38578c2ecf20Sopenharmony_ci					cifs_dbg(VFS, "POSIXPATH support change\n");
38588c2ecf20Sopenharmony_ci				cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP;
38598c2ecf20Sopenharmony_ci			} else if ((cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) == 0) {
38608c2ecf20Sopenharmony_ci				cifs_dbg(VFS, "possible reconnect error\n");
38618c2ecf20Sopenharmony_ci				cifs_dbg(VFS, "server disabled POSIX path support\n");
38628c2ecf20Sopenharmony_ci			}
38638c2ecf20Sopenharmony_ci		}
38648c2ecf20Sopenharmony_ci
38658c2ecf20Sopenharmony_ci		if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP)
38668c2ecf20Sopenharmony_ci			cifs_dbg(VFS, "per-share encryption not supported yet\n");
38678c2ecf20Sopenharmony_ci
38688c2ecf20Sopenharmony_ci		cap &= CIFS_UNIX_CAP_MASK;
38698c2ecf20Sopenharmony_ci		if (vol_info && vol_info->no_psx_acl)
38708c2ecf20Sopenharmony_ci			cap &= ~CIFS_UNIX_POSIX_ACL_CAP;
38718c2ecf20Sopenharmony_ci		else if (CIFS_UNIX_POSIX_ACL_CAP & cap) {
38728c2ecf20Sopenharmony_ci			cifs_dbg(FYI, "negotiated posix acl support\n");
38738c2ecf20Sopenharmony_ci			if (cifs_sb)
38748c2ecf20Sopenharmony_ci				cifs_sb->mnt_cifs_flags |=
38758c2ecf20Sopenharmony_ci					CIFS_MOUNT_POSIXACL;
38768c2ecf20Sopenharmony_ci		}
38778c2ecf20Sopenharmony_ci
38788c2ecf20Sopenharmony_ci		if (vol_info && vol_info->posix_paths == 0)
38798c2ecf20Sopenharmony_ci			cap &= ~CIFS_UNIX_POSIX_PATHNAMES_CAP;
38808c2ecf20Sopenharmony_ci		else if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP) {
38818c2ecf20Sopenharmony_ci			cifs_dbg(FYI, "negotiate posix pathnames\n");
38828c2ecf20Sopenharmony_ci			if (cifs_sb)
38838c2ecf20Sopenharmony_ci				cifs_sb->mnt_cifs_flags |=
38848c2ecf20Sopenharmony_ci					CIFS_MOUNT_POSIX_PATHS;
38858c2ecf20Sopenharmony_ci		}
38868c2ecf20Sopenharmony_ci
38878c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "Negotiate caps 0x%x\n", (int)cap);
38888c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_DEBUG2
38898c2ecf20Sopenharmony_ci		if (cap & CIFS_UNIX_FCNTL_CAP)
38908c2ecf20Sopenharmony_ci			cifs_dbg(FYI, "FCNTL cap\n");
38918c2ecf20Sopenharmony_ci		if (cap & CIFS_UNIX_EXTATTR_CAP)
38928c2ecf20Sopenharmony_ci			cifs_dbg(FYI, "EXTATTR cap\n");
38938c2ecf20Sopenharmony_ci		if (cap & CIFS_UNIX_POSIX_PATHNAMES_CAP)
38948c2ecf20Sopenharmony_ci			cifs_dbg(FYI, "POSIX path cap\n");
38958c2ecf20Sopenharmony_ci		if (cap & CIFS_UNIX_XATTR_CAP)
38968c2ecf20Sopenharmony_ci			cifs_dbg(FYI, "XATTR cap\n");
38978c2ecf20Sopenharmony_ci		if (cap & CIFS_UNIX_POSIX_ACL_CAP)
38988c2ecf20Sopenharmony_ci			cifs_dbg(FYI, "POSIX ACL cap\n");
38998c2ecf20Sopenharmony_ci		if (cap & CIFS_UNIX_LARGE_READ_CAP)
39008c2ecf20Sopenharmony_ci			cifs_dbg(FYI, "very large read cap\n");
39018c2ecf20Sopenharmony_ci		if (cap & CIFS_UNIX_LARGE_WRITE_CAP)
39028c2ecf20Sopenharmony_ci			cifs_dbg(FYI, "very large write cap\n");
39038c2ecf20Sopenharmony_ci		if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_CAP)
39048c2ecf20Sopenharmony_ci			cifs_dbg(FYI, "transport encryption cap\n");
39058c2ecf20Sopenharmony_ci		if (cap & CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP)
39068c2ecf20Sopenharmony_ci			cifs_dbg(FYI, "mandatory transport encryption cap\n");
39078c2ecf20Sopenharmony_ci#endif /* CIFS_DEBUG2 */
39088c2ecf20Sopenharmony_ci		if (CIFSSMBSetFSUnixInfo(xid, tcon, cap)) {
39098c2ecf20Sopenharmony_ci			if (vol_info == NULL) {
39108c2ecf20Sopenharmony_ci				cifs_dbg(FYI, "resetting capabilities failed\n");
39118c2ecf20Sopenharmony_ci			} else
39128c2ecf20Sopenharmony_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");
39138c2ecf20Sopenharmony_ci
39148c2ecf20Sopenharmony_ci		}
39158c2ecf20Sopenharmony_ci	}
39168c2ecf20Sopenharmony_ci}
39178c2ecf20Sopenharmony_ci
39188c2ecf20Sopenharmony_ciint cifs_setup_cifs_sb(struct smb_vol *pvolume_info,
39198c2ecf20Sopenharmony_ci			struct cifs_sb_info *cifs_sb)
39208c2ecf20Sopenharmony_ci{
39218c2ecf20Sopenharmony_ci	INIT_DELAYED_WORK(&cifs_sb->prune_tlinks, cifs_prune_tlinks);
39228c2ecf20Sopenharmony_ci
39238c2ecf20Sopenharmony_ci	spin_lock_init(&cifs_sb->tlink_tree_lock);
39248c2ecf20Sopenharmony_ci	cifs_sb->tlink_tree = RB_ROOT;
39258c2ecf20Sopenharmony_ci
39268c2ecf20Sopenharmony_ci	cifs_sb->bsize = pvolume_info->bsize;
39278c2ecf20Sopenharmony_ci	/*
39288c2ecf20Sopenharmony_ci	 * Temporarily set r/wsize for matching superblock. If we end up using
39298c2ecf20Sopenharmony_ci	 * new sb then client will later negotiate it downward if needed.
39308c2ecf20Sopenharmony_ci	 */
39318c2ecf20Sopenharmony_ci	cifs_sb->rsize = pvolume_info->rsize;
39328c2ecf20Sopenharmony_ci	cifs_sb->wsize = pvolume_info->wsize;
39338c2ecf20Sopenharmony_ci
39348c2ecf20Sopenharmony_ci	cifs_sb->mnt_uid = pvolume_info->linux_uid;
39358c2ecf20Sopenharmony_ci	cifs_sb->mnt_gid = pvolume_info->linux_gid;
39368c2ecf20Sopenharmony_ci	cifs_sb->mnt_file_mode = pvolume_info->file_mode;
39378c2ecf20Sopenharmony_ci	cifs_sb->mnt_dir_mode = pvolume_info->dir_mode;
39388c2ecf20Sopenharmony_ci	cifs_dbg(FYI, "file mode: %04ho  dir mode: %04ho\n",
39398c2ecf20Sopenharmony_ci		 cifs_sb->mnt_file_mode, cifs_sb->mnt_dir_mode);
39408c2ecf20Sopenharmony_ci
39418c2ecf20Sopenharmony_ci	cifs_sb->actimeo = pvolume_info->actimeo;
39428c2ecf20Sopenharmony_ci	cifs_sb->local_nls = pvolume_info->local_nls;
39438c2ecf20Sopenharmony_ci
39448c2ecf20Sopenharmony_ci	if (pvolume_info->nodfs)
39458c2ecf20Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_DFS;
39468c2ecf20Sopenharmony_ci	if (pvolume_info->noperm)
39478c2ecf20Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_PERM;
39488c2ecf20Sopenharmony_ci	if (pvolume_info->setuids)
39498c2ecf20Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SET_UID;
39508c2ecf20Sopenharmony_ci	if (pvolume_info->setuidfromacl)
39518c2ecf20Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UID_FROM_ACL;
39528c2ecf20Sopenharmony_ci	if (pvolume_info->server_ino)
39538c2ecf20Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_SERVER_INUM;
39548c2ecf20Sopenharmony_ci	if (pvolume_info->remap)
39558c2ecf20Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SFM_CHR;
39568c2ecf20Sopenharmony_ci	if (pvolume_info->sfu_remap)
39578c2ecf20Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MAP_SPECIAL_CHR;
39588c2ecf20Sopenharmony_ci	if (pvolume_info->no_xattr)
39598c2ecf20Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_XATTR;
39608c2ecf20Sopenharmony_ci	if (pvolume_info->sfu_emul)
39618c2ecf20Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_UNX_EMUL;
39628c2ecf20Sopenharmony_ci	if (pvolume_info->nobrl)
39638c2ecf20Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_BRL;
39648c2ecf20Sopenharmony_ci	if (pvolume_info->nohandlecache)
39658c2ecf20Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NO_HANDLE_CACHE;
39668c2ecf20Sopenharmony_ci	if (pvolume_info->nostrictsync)
39678c2ecf20Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NOSSYNC;
39688c2ecf20Sopenharmony_ci	if (pvolume_info->mand_lock)
39698c2ecf20Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_NOPOSIXBRL;
39708c2ecf20Sopenharmony_ci	if (pvolume_info->rwpidforward)
39718c2ecf20Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_RWPIDFORWARD;
39728c2ecf20Sopenharmony_ci	if (pvolume_info->mode_ace)
39738c2ecf20Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MODE_FROM_SID;
39748c2ecf20Sopenharmony_ci	if (pvolume_info->cifs_acl)
39758c2ecf20Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_ACL;
39768c2ecf20Sopenharmony_ci	if (pvolume_info->backupuid_specified) {
39778c2ecf20Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_BACKUPUID;
39788c2ecf20Sopenharmony_ci		cifs_sb->mnt_backupuid = pvolume_info->backupuid;
39798c2ecf20Sopenharmony_ci	}
39808c2ecf20Sopenharmony_ci	if (pvolume_info->backupgid_specified) {
39818c2ecf20Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_CIFS_BACKUPGID;
39828c2ecf20Sopenharmony_ci		cifs_sb->mnt_backupgid = pvolume_info->backupgid;
39838c2ecf20Sopenharmony_ci	}
39848c2ecf20Sopenharmony_ci	if (pvolume_info->override_uid)
39858c2ecf20Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_UID;
39868c2ecf20Sopenharmony_ci	if (pvolume_info->override_gid)
39878c2ecf20Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_OVERR_GID;
39888c2ecf20Sopenharmony_ci	if (pvolume_info->dynperm)
39898c2ecf20Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DYNPERM;
39908c2ecf20Sopenharmony_ci	if (pvolume_info->fsc)
39918c2ecf20Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_FSCACHE;
39928c2ecf20Sopenharmony_ci	if (pvolume_info->multiuser)
39938c2ecf20Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= (CIFS_MOUNT_MULTIUSER |
39948c2ecf20Sopenharmony_ci					    CIFS_MOUNT_NO_PERM);
39958c2ecf20Sopenharmony_ci	if (pvolume_info->strict_io)
39968c2ecf20Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_STRICT_IO;
39978c2ecf20Sopenharmony_ci	if (pvolume_info->direct_io) {
39988c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "mounting share using direct i/o\n");
39998c2ecf20Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_DIRECT_IO;
40008c2ecf20Sopenharmony_ci	}
40018c2ecf20Sopenharmony_ci	if (pvolume_info->cache_ro) {
40028c2ecf20Sopenharmony_ci		cifs_dbg(VFS, "mounting share with read only caching. Ensure that the share will not be modified while in use.\n");
40038c2ecf20Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_RO_CACHE;
40048c2ecf20Sopenharmony_ci	} else if (pvolume_info->cache_rw) {
40058c2ecf20Sopenharmony_ci		cifs_dbg(VFS, "mounting share in single client RW caching mode. Ensure that no other systems will be accessing the share.\n");
40068c2ecf20Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= (CIFS_MOUNT_RO_CACHE |
40078c2ecf20Sopenharmony_ci					    CIFS_MOUNT_RW_CACHE);
40088c2ecf20Sopenharmony_ci	}
40098c2ecf20Sopenharmony_ci	if (pvolume_info->mfsymlinks) {
40108c2ecf20Sopenharmony_ci		if (pvolume_info->sfu_emul) {
40118c2ecf20Sopenharmony_ci			/*
40128c2ecf20Sopenharmony_ci			 * Our SFU ("Services for Unix" emulation does not allow
40138c2ecf20Sopenharmony_ci			 * creating symlinks but does allow reading existing SFU
40148c2ecf20Sopenharmony_ci			 * symlinks (it does allow both creating and reading SFU
40158c2ecf20Sopenharmony_ci			 * style mknod and FIFOs though). When "mfsymlinks" and
40168c2ecf20Sopenharmony_ci			 * "sfu" are both enabled at the same time, it allows
40178c2ecf20Sopenharmony_ci			 * reading both types of symlinks, but will only create
40188c2ecf20Sopenharmony_ci			 * them with mfsymlinks format. This allows better
40198c2ecf20Sopenharmony_ci			 * Apple compatibility (probably better for Samba too)
40208c2ecf20Sopenharmony_ci			 * while still recognizing old Windows style symlinks.
40218c2ecf20Sopenharmony_ci			 */
40228c2ecf20Sopenharmony_ci			cifs_dbg(VFS, "mount options mfsymlinks and sfu both enabled\n");
40238c2ecf20Sopenharmony_ci		}
40248c2ecf20Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_MF_SYMLINKS;
40258c2ecf20Sopenharmony_ci	}
40268c2ecf20Sopenharmony_ci
40278c2ecf20Sopenharmony_ci	if ((pvolume_info->cifs_acl) && (pvolume_info->dynperm))
40288c2ecf20Sopenharmony_ci		cifs_dbg(VFS, "mount option dynperm ignored if cifsacl mount option supported\n");
40298c2ecf20Sopenharmony_ci
40308c2ecf20Sopenharmony_ci	if (pvolume_info->prepath) {
40318c2ecf20Sopenharmony_ci		cifs_sb->prepath = kstrdup(pvolume_info->prepath, GFP_KERNEL);
40328c2ecf20Sopenharmony_ci		if (cifs_sb->prepath == NULL)
40338c2ecf20Sopenharmony_ci			return -ENOMEM;
40348c2ecf20Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
40358c2ecf20Sopenharmony_ci	}
40368c2ecf20Sopenharmony_ci
40378c2ecf20Sopenharmony_ci	return 0;
40388c2ecf20Sopenharmony_ci}
40398c2ecf20Sopenharmony_ci
40408c2ecf20Sopenharmony_civoid
40418c2ecf20Sopenharmony_cicifs_cleanup_volume_info_contents(struct smb_vol *volume_info)
40428c2ecf20Sopenharmony_ci{
40438c2ecf20Sopenharmony_ci	kfree(volume_info->username);
40448c2ecf20Sopenharmony_ci	kfree_sensitive(volume_info->password);
40458c2ecf20Sopenharmony_ci	kfree(volume_info->UNC);
40468c2ecf20Sopenharmony_ci	kfree(volume_info->domainname);
40478c2ecf20Sopenharmony_ci	kfree(volume_info->iocharset);
40488c2ecf20Sopenharmony_ci	kfree(volume_info->prepath);
40498c2ecf20Sopenharmony_ci}
40508c2ecf20Sopenharmony_ci
40518c2ecf20Sopenharmony_civoid
40528c2ecf20Sopenharmony_cicifs_cleanup_volume_info(struct smb_vol *volume_info)
40538c2ecf20Sopenharmony_ci{
40548c2ecf20Sopenharmony_ci	if (!volume_info)
40558c2ecf20Sopenharmony_ci		return;
40568c2ecf20Sopenharmony_ci	cifs_cleanup_volume_info_contents(volume_info);
40578c2ecf20Sopenharmony_ci	kfree(volume_info);
40588c2ecf20Sopenharmony_ci}
40598c2ecf20Sopenharmony_ci
40608c2ecf20Sopenharmony_ci/* Release all succeed connections */
40618c2ecf20Sopenharmony_cistatic inline void mount_put_conns(struct cifs_sb_info *cifs_sb,
40628c2ecf20Sopenharmony_ci				   unsigned int xid,
40638c2ecf20Sopenharmony_ci				   struct TCP_Server_Info *server,
40648c2ecf20Sopenharmony_ci				   struct cifs_ses *ses, struct cifs_tcon *tcon)
40658c2ecf20Sopenharmony_ci{
40668c2ecf20Sopenharmony_ci	int rc = 0;
40678c2ecf20Sopenharmony_ci
40688c2ecf20Sopenharmony_ci	if (tcon)
40698c2ecf20Sopenharmony_ci		cifs_put_tcon(tcon);
40708c2ecf20Sopenharmony_ci	else if (ses)
40718c2ecf20Sopenharmony_ci		cifs_put_smb_ses(ses);
40728c2ecf20Sopenharmony_ci	else if (server)
40738c2ecf20Sopenharmony_ci		cifs_put_tcp_session(server, 0);
40748c2ecf20Sopenharmony_ci	cifs_sb->mnt_cifs_flags &= ~CIFS_MOUNT_POSIX_PATHS;
40758c2ecf20Sopenharmony_ci	free_xid(xid);
40768c2ecf20Sopenharmony_ci}
40778c2ecf20Sopenharmony_ci
40788c2ecf20Sopenharmony_ci/* Get connections for tcp, ses and tcon */
40798c2ecf20Sopenharmony_cistatic int mount_get_conns(struct smb_vol *vol, struct cifs_sb_info *cifs_sb,
40808c2ecf20Sopenharmony_ci			   unsigned int *xid,
40818c2ecf20Sopenharmony_ci			   struct TCP_Server_Info **nserver,
40828c2ecf20Sopenharmony_ci			   struct cifs_ses **nses, struct cifs_tcon **ntcon)
40838c2ecf20Sopenharmony_ci{
40848c2ecf20Sopenharmony_ci	int rc = 0;
40858c2ecf20Sopenharmony_ci	struct TCP_Server_Info *server;
40868c2ecf20Sopenharmony_ci	struct cifs_ses *ses;
40878c2ecf20Sopenharmony_ci	struct cifs_tcon *tcon;
40888c2ecf20Sopenharmony_ci
40898c2ecf20Sopenharmony_ci	*nserver = NULL;
40908c2ecf20Sopenharmony_ci	*nses = NULL;
40918c2ecf20Sopenharmony_ci	*ntcon = NULL;
40928c2ecf20Sopenharmony_ci
40938c2ecf20Sopenharmony_ci	*xid = get_xid();
40948c2ecf20Sopenharmony_ci
40958c2ecf20Sopenharmony_ci	/* get a reference to a tcp session */
40968c2ecf20Sopenharmony_ci	server = cifs_get_tcp_session(vol);
40978c2ecf20Sopenharmony_ci	if (IS_ERR(server)) {
40988c2ecf20Sopenharmony_ci		rc = PTR_ERR(server);
40998c2ecf20Sopenharmony_ci		return rc;
41008c2ecf20Sopenharmony_ci	}
41018c2ecf20Sopenharmony_ci
41028c2ecf20Sopenharmony_ci	*nserver = server;
41038c2ecf20Sopenharmony_ci
41048c2ecf20Sopenharmony_ci	/* get a reference to a SMB session */
41058c2ecf20Sopenharmony_ci	ses = cifs_get_smb_ses(server, vol);
41068c2ecf20Sopenharmony_ci	if (IS_ERR(ses)) {
41078c2ecf20Sopenharmony_ci		rc = PTR_ERR(ses);
41088c2ecf20Sopenharmony_ci		return rc;
41098c2ecf20Sopenharmony_ci	}
41108c2ecf20Sopenharmony_ci
41118c2ecf20Sopenharmony_ci	*nses = ses;
41128c2ecf20Sopenharmony_ci
41138c2ecf20Sopenharmony_ci	if ((vol->persistent == true) && (!(ses->server->capabilities &
41148c2ecf20Sopenharmony_ci					    SMB2_GLOBAL_CAP_PERSISTENT_HANDLES))) {
41158c2ecf20Sopenharmony_ci		cifs_server_dbg(VFS, "persistent handles not supported by server\n");
41168c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
41178c2ecf20Sopenharmony_ci	}
41188c2ecf20Sopenharmony_ci
41198c2ecf20Sopenharmony_ci	/* search for existing tcon to this server share */
41208c2ecf20Sopenharmony_ci	tcon = cifs_get_tcon(ses, vol);
41218c2ecf20Sopenharmony_ci	if (IS_ERR(tcon)) {
41228c2ecf20Sopenharmony_ci		rc = PTR_ERR(tcon);
41238c2ecf20Sopenharmony_ci		return rc;
41248c2ecf20Sopenharmony_ci	}
41258c2ecf20Sopenharmony_ci
41268c2ecf20Sopenharmony_ci	*ntcon = tcon;
41278c2ecf20Sopenharmony_ci
41288c2ecf20Sopenharmony_ci	/* if new SMB3.11 POSIX extensions are supported do not remap / and \ */
41298c2ecf20Sopenharmony_ci	if (tcon->posix_extensions)
41308c2ecf20Sopenharmony_ci		cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_POSIX_PATHS;
41318c2ecf20Sopenharmony_ci
41328c2ecf20Sopenharmony_ci	/* tell server which Unix caps we support */
41338c2ecf20Sopenharmony_ci	if (cap_unix(tcon->ses)) {
41348c2ecf20Sopenharmony_ci		/*
41358c2ecf20Sopenharmony_ci		 * reset of caps checks mount to see if unix extensions disabled
41368c2ecf20Sopenharmony_ci		 * for just this mount.
41378c2ecf20Sopenharmony_ci		 */
41388c2ecf20Sopenharmony_ci		reset_cifs_unix_caps(*xid, tcon, cifs_sb, vol);
41398c2ecf20Sopenharmony_ci		if ((tcon->ses->server->tcpStatus == CifsNeedReconnect) &&
41408c2ecf20Sopenharmony_ci		    (le64_to_cpu(tcon->fsUnixInfo.Capability) &
41418c2ecf20Sopenharmony_ci		     CIFS_UNIX_TRANSPORT_ENCRYPTION_MANDATORY_CAP))
41428c2ecf20Sopenharmony_ci			return -EACCES;
41438c2ecf20Sopenharmony_ci	} else
41448c2ecf20Sopenharmony_ci		tcon->unix_ext = 0; /* server does not support them */
41458c2ecf20Sopenharmony_ci
41468c2ecf20Sopenharmony_ci	/* do not care if a following call succeed - informational */
41478c2ecf20Sopenharmony_ci	if (!tcon->pipe && server->ops->qfs_tcon) {
41488c2ecf20Sopenharmony_ci		server->ops->qfs_tcon(*xid, tcon, cifs_sb);
41498c2ecf20Sopenharmony_ci		if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_RO_CACHE) {
41508c2ecf20Sopenharmony_ci			if (tcon->fsDevInfo.DeviceCharacteristics &
41518c2ecf20Sopenharmony_ci			    cpu_to_le32(FILE_READ_ONLY_DEVICE))
41528c2ecf20Sopenharmony_ci				cifs_dbg(VFS, "mounted to read only share\n");
41538c2ecf20Sopenharmony_ci			else if ((cifs_sb->mnt_cifs_flags &
41548c2ecf20Sopenharmony_ci				  CIFS_MOUNT_RW_CACHE) == 0)
41558c2ecf20Sopenharmony_ci				cifs_dbg(VFS, "read only mount of RW share\n");
41568c2ecf20Sopenharmony_ci			/* no need to log a RW mount of a typical RW share */
41578c2ecf20Sopenharmony_ci		}
41588c2ecf20Sopenharmony_ci	}
41598c2ecf20Sopenharmony_ci
41608c2ecf20Sopenharmony_ci	cifs_sb->wsize = server->ops->negotiate_wsize(tcon, vol);
41618c2ecf20Sopenharmony_ci	cifs_sb->rsize = server->ops->negotiate_rsize(tcon, vol);
41628c2ecf20Sopenharmony_ci
41638c2ecf20Sopenharmony_ci	return 0;
41648c2ecf20Sopenharmony_ci}
41658c2ecf20Sopenharmony_ci
41668c2ecf20Sopenharmony_cistatic int mount_setup_tlink(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
41678c2ecf20Sopenharmony_ci			     struct cifs_tcon *tcon)
41688c2ecf20Sopenharmony_ci{
41698c2ecf20Sopenharmony_ci	struct tcon_link *tlink;
41708c2ecf20Sopenharmony_ci
41718c2ecf20Sopenharmony_ci	/* hang the tcon off of the superblock */
41728c2ecf20Sopenharmony_ci	tlink = kzalloc(sizeof(*tlink), GFP_KERNEL);
41738c2ecf20Sopenharmony_ci	if (tlink == NULL)
41748c2ecf20Sopenharmony_ci		return -ENOMEM;
41758c2ecf20Sopenharmony_ci
41768c2ecf20Sopenharmony_ci	tlink->tl_uid = ses->linux_uid;
41778c2ecf20Sopenharmony_ci	tlink->tl_tcon = tcon;
41788c2ecf20Sopenharmony_ci	tlink->tl_time = jiffies;
41798c2ecf20Sopenharmony_ci	set_bit(TCON_LINK_MASTER, &tlink->tl_flags);
41808c2ecf20Sopenharmony_ci	set_bit(TCON_LINK_IN_TREE, &tlink->tl_flags);
41818c2ecf20Sopenharmony_ci
41828c2ecf20Sopenharmony_ci	cifs_sb->master_tlink = tlink;
41838c2ecf20Sopenharmony_ci	spin_lock(&cifs_sb->tlink_tree_lock);
41848c2ecf20Sopenharmony_ci	tlink_rb_insert(&cifs_sb->tlink_tree, tlink);
41858c2ecf20Sopenharmony_ci	spin_unlock(&cifs_sb->tlink_tree_lock);
41868c2ecf20Sopenharmony_ci
41878c2ecf20Sopenharmony_ci	queue_delayed_work(cifsiod_wq, &cifs_sb->prune_tlinks,
41888c2ecf20Sopenharmony_ci				TLINK_IDLE_EXPIRE);
41898c2ecf20Sopenharmony_ci	return 0;
41908c2ecf20Sopenharmony_ci}
41918c2ecf20Sopenharmony_ci
41928c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_DFS_UPCALL
41938c2ecf20Sopenharmony_ci/*
41948c2ecf20Sopenharmony_ci * cifs_build_path_to_root returns full path to root when we do not have an
41958c2ecf20Sopenharmony_ci * exiting connection (tcon)
41968c2ecf20Sopenharmony_ci */
41978c2ecf20Sopenharmony_cistatic char *
41988c2ecf20Sopenharmony_cibuild_unc_path_to_root(const struct smb_vol *vol,
41998c2ecf20Sopenharmony_ci		       const struct cifs_sb_info *cifs_sb, bool useppath)
42008c2ecf20Sopenharmony_ci{
42018c2ecf20Sopenharmony_ci	char *full_path, *pos;
42028c2ecf20Sopenharmony_ci	unsigned int pplen = useppath && vol->prepath ?
42038c2ecf20Sopenharmony_ci		strlen(vol->prepath) + 1 : 0;
42048c2ecf20Sopenharmony_ci	unsigned int unc_len = strnlen(vol->UNC, MAX_TREE_SIZE + 1);
42058c2ecf20Sopenharmony_ci
42068c2ecf20Sopenharmony_ci	if (unc_len > MAX_TREE_SIZE)
42078c2ecf20Sopenharmony_ci		return ERR_PTR(-EINVAL);
42088c2ecf20Sopenharmony_ci
42098c2ecf20Sopenharmony_ci	full_path = kmalloc(unc_len + pplen + 1, GFP_KERNEL);
42108c2ecf20Sopenharmony_ci	if (full_path == NULL)
42118c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
42128c2ecf20Sopenharmony_ci
42138c2ecf20Sopenharmony_ci	memcpy(full_path, vol->UNC, unc_len);
42148c2ecf20Sopenharmony_ci	pos = full_path + unc_len;
42158c2ecf20Sopenharmony_ci
42168c2ecf20Sopenharmony_ci	if (pplen) {
42178c2ecf20Sopenharmony_ci		*pos = CIFS_DIR_SEP(cifs_sb);
42188c2ecf20Sopenharmony_ci		memcpy(pos + 1, vol->prepath, pplen);
42198c2ecf20Sopenharmony_ci		pos += pplen;
42208c2ecf20Sopenharmony_ci	}
42218c2ecf20Sopenharmony_ci
42228c2ecf20Sopenharmony_ci	*pos = '\0'; /* add trailing null */
42238c2ecf20Sopenharmony_ci	convert_delimiter(full_path, CIFS_DIR_SEP(cifs_sb));
42248c2ecf20Sopenharmony_ci	cifs_dbg(FYI, "%s: full_path=%s\n", __func__, full_path);
42258c2ecf20Sopenharmony_ci	return full_path;
42268c2ecf20Sopenharmony_ci}
42278c2ecf20Sopenharmony_ci
42288c2ecf20Sopenharmony_ci/**
42298c2ecf20Sopenharmony_ci * expand_dfs_referral - Perform a dfs referral query and update the cifs_sb
42308c2ecf20Sopenharmony_ci *
42318c2ecf20Sopenharmony_ci *
42328c2ecf20Sopenharmony_ci * If a referral is found, cifs_sb->mountdata will be (re-)allocated
42338c2ecf20Sopenharmony_ci * to a string containing updated options for the submount.  Otherwise it
42348c2ecf20Sopenharmony_ci * will be left untouched.
42358c2ecf20Sopenharmony_ci *
42368c2ecf20Sopenharmony_ci * Returns the rc from get_dfs_path to the caller, which can be used to
42378c2ecf20Sopenharmony_ci * determine whether there were referrals.
42388c2ecf20Sopenharmony_ci */
42398c2ecf20Sopenharmony_cistatic int
42408c2ecf20Sopenharmony_ciexpand_dfs_referral(const unsigned int xid, struct cifs_ses *ses,
42418c2ecf20Sopenharmony_ci		    struct smb_vol *volume_info, struct cifs_sb_info *cifs_sb,
42428c2ecf20Sopenharmony_ci		    char *ref_path)
42438c2ecf20Sopenharmony_ci{
42448c2ecf20Sopenharmony_ci	int rc;
42458c2ecf20Sopenharmony_ci	struct dfs_info3_param referral = {0};
42468c2ecf20Sopenharmony_ci	char *full_path = NULL, *mdata = NULL;
42478c2ecf20Sopenharmony_ci
42488c2ecf20Sopenharmony_ci	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS)
42498c2ecf20Sopenharmony_ci		return -EREMOTE;
42508c2ecf20Sopenharmony_ci
42518c2ecf20Sopenharmony_ci	full_path = build_unc_path_to_root(volume_info, cifs_sb, true);
42528c2ecf20Sopenharmony_ci	if (IS_ERR(full_path))
42538c2ecf20Sopenharmony_ci		return PTR_ERR(full_path);
42548c2ecf20Sopenharmony_ci
42558c2ecf20Sopenharmony_ci	rc = dfs_cache_find(xid, ses, cifs_sb->local_nls, cifs_remap(cifs_sb),
42568c2ecf20Sopenharmony_ci			    ref_path, &referral, NULL);
42578c2ecf20Sopenharmony_ci	if (!rc) {
42588c2ecf20Sopenharmony_ci		char *fake_devname = NULL;
42598c2ecf20Sopenharmony_ci
42608c2ecf20Sopenharmony_ci		mdata = cifs_compose_mount_options(cifs_sb->mountdata,
42618c2ecf20Sopenharmony_ci						   full_path + 1, &referral,
42628c2ecf20Sopenharmony_ci						   &fake_devname);
42638c2ecf20Sopenharmony_ci		free_dfs_info_param(&referral);
42648c2ecf20Sopenharmony_ci
42658c2ecf20Sopenharmony_ci		if (IS_ERR(mdata)) {
42668c2ecf20Sopenharmony_ci			rc = PTR_ERR(mdata);
42678c2ecf20Sopenharmony_ci			mdata = NULL;
42688c2ecf20Sopenharmony_ci		} else {
42698c2ecf20Sopenharmony_ci			cifs_cleanup_volume_info_contents(volume_info);
42708c2ecf20Sopenharmony_ci			rc = cifs_setup_volume_info(volume_info, mdata,
42718c2ecf20Sopenharmony_ci						    fake_devname, false);
42728c2ecf20Sopenharmony_ci		}
42738c2ecf20Sopenharmony_ci		kfree(fake_devname);
42748c2ecf20Sopenharmony_ci		kfree(cifs_sb->mountdata);
42758c2ecf20Sopenharmony_ci		cifs_sb->mountdata = mdata;
42768c2ecf20Sopenharmony_ci	}
42778c2ecf20Sopenharmony_ci	kfree(full_path);
42788c2ecf20Sopenharmony_ci	return rc;
42798c2ecf20Sopenharmony_ci}
42808c2ecf20Sopenharmony_ci
42818c2ecf20Sopenharmony_cistatic inline int get_next_dfs_tgt(const char *path,
42828c2ecf20Sopenharmony_ci				   struct dfs_cache_tgt_list *tgt_list,
42838c2ecf20Sopenharmony_ci				   struct dfs_cache_tgt_iterator **tgt_it)
42848c2ecf20Sopenharmony_ci{
42858c2ecf20Sopenharmony_ci	if (!*tgt_it)
42868c2ecf20Sopenharmony_ci		*tgt_it = dfs_cache_get_tgt_iterator(tgt_list);
42878c2ecf20Sopenharmony_ci	else
42888c2ecf20Sopenharmony_ci		*tgt_it = dfs_cache_get_next_tgt(tgt_list, *tgt_it);
42898c2ecf20Sopenharmony_ci	return !*tgt_it ? -EHOSTDOWN : 0;
42908c2ecf20Sopenharmony_ci}
42918c2ecf20Sopenharmony_ci
42928c2ecf20Sopenharmony_cistatic int update_vol_info(const struct dfs_cache_tgt_iterator *tgt_it,
42938c2ecf20Sopenharmony_ci			   struct smb_vol *fake_vol, struct smb_vol *vol)
42948c2ecf20Sopenharmony_ci{
42958c2ecf20Sopenharmony_ci	const char *tgt = dfs_cache_get_tgt_name(tgt_it);
42968c2ecf20Sopenharmony_ci	int len = strlen(tgt) + 2;
42978c2ecf20Sopenharmony_ci	char *new_unc;
42988c2ecf20Sopenharmony_ci
42998c2ecf20Sopenharmony_ci	new_unc = kmalloc(len, GFP_KERNEL);
43008c2ecf20Sopenharmony_ci	if (!new_unc)
43018c2ecf20Sopenharmony_ci		return -ENOMEM;
43028c2ecf20Sopenharmony_ci	scnprintf(new_unc, len, "\\%s", tgt);
43038c2ecf20Sopenharmony_ci
43048c2ecf20Sopenharmony_ci	kfree(vol->UNC);
43058c2ecf20Sopenharmony_ci	vol->UNC = new_unc;
43068c2ecf20Sopenharmony_ci
43078c2ecf20Sopenharmony_ci	if (fake_vol->prepath) {
43088c2ecf20Sopenharmony_ci		kfree(vol->prepath);
43098c2ecf20Sopenharmony_ci		vol->prepath = fake_vol->prepath;
43108c2ecf20Sopenharmony_ci		fake_vol->prepath = NULL;
43118c2ecf20Sopenharmony_ci	}
43128c2ecf20Sopenharmony_ci	memcpy(&vol->dstaddr, &fake_vol->dstaddr, sizeof(vol->dstaddr));
43138c2ecf20Sopenharmony_ci
43148c2ecf20Sopenharmony_ci	return 0;
43158c2ecf20Sopenharmony_ci}
43168c2ecf20Sopenharmony_ci
43178c2ecf20Sopenharmony_cistatic int setup_dfs_tgt_conn(const char *path, const char *full_path,
43188c2ecf20Sopenharmony_ci			      const struct dfs_cache_tgt_iterator *tgt_it,
43198c2ecf20Sopenharmony_ci			      struct cifs_sb_info *cifs_sb, struct smb_vol *vol, unsigned int *xid,
43208c2ecf20Sopenharmony_ci			      struct TCP_Server_Info **server, struct cifs_ses **ses,
43218c2ecf20Sopenharmony_ci			      struct cifs_tcon **tcon)
43228c2ecf20Sopenharmony_ci{
43238c2ecf20Sopenharmony_ci	int rc;
43248c2ecf20Sopenharmony_ci	struct dfs_info3_param ref = {0};
43258c2ecf20Sopenharmony_ci	char *mdata = NULL, *fake_devname = NULL;
43268c2ecf20Sopenharmony_ci	struct smb_vol fake_vol = {NULL};
43278c2ecf20Sopenharmony_ci
43288c2ecf20Sopenharmony_ci	cifs_dbg(FYI, "%s: dfs path: %s\n", __func__, path);
43298c2ecf20Sopenharmony_ci
43308c2ecf20Sopenharmony_ci	rc = dfs_cache_get_tgt_referral(path, tgt_it, &ref);
43318c2ecf20Sopenharmony_ci	if (rc)
43328c2ecf20Sopenharmony_ci		return rc;
43338c2ecf20Sopenharmony_ci
43348c2ecf20Sopenharmony_ci	mdata = cifs_compose_mount_options(cifs_sb->mountdata, full_path + 1, &ref, &fake_devname);
43358c2ecf20Sopenharmony_ci	free_dfs_info_param(&ref);
43368c2ecf20Sopenharmony_ci
43378c2ecf20Sopenharmony_ci	if (IS_ERR(mdata)) {
43388c2ecf20Sopenharmony_ci		rc = PTR_ERR(mdata);
43398c2ecf20Sopenharmony_ci		mdata = NULL;
43408c2ecf20Sopenharmony_ci	} else {
43418c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "%s: fake_devname: %s\n", __func__, fake_devname);
43428c2ecf20Sopenharmony_ci		rc = cifs_setup_volume_info(&fake_vol, mdata, fake_devname,
43438c2ecf20Sopenharmony_ci					    false);
43448c2ecf20Sopenharmony_ci	}
43458c2ecf20Sopenharmony_ci	kfree(mdata);
43468c2ecf20Sopenharmony_ci	kfree(fake_devname);
43478c2ecf20Sopenharmony_ci
43488c2ecf20Sopenharmony_ci	if (!rc) {
43498c2ecf20Sopenharmony_ci		/*
43508c2ecf20Sopenharmony_ci		 * We use a 'fake_vol' here because we need pass it down to the
43518c2ecf20Sopenharmony_ci		 * mount_{get,put} functions to test connection against new DFS
43528c2ecf20Sopenharmony_ci		 * targets.
43538c2ecf20Sopenharmony_ci		 */
43548c2ecf20Sopenharmony_ci		mount_put_conns(cifs_sb, *xid, *server, *ses, *tcon);
43558c2ecf20Sopenharmony_ci		rc = mount_get_conns(&fake_vol, cifs_sb, xid, server, ses,
43568c2ecf20Sopenharmony_ci				     tcon);
43578c2ecf20Sopenharmony_ci		if (!rc || (*server && *ses)) {
43588c2ecf20Sopenharmony_ci			/*
43598c2ecf20Sopenharmony_ci			 * We were able to connect to new target server.
43608c2ecf20Sopenharmony_ci			 * Update current volume info with new target server.
43618c2ecf20Sopenharmony_ci			 */
43628c2ecf20Sopenharmony_ci			rc = update_vol_info(tgt_it, &fake_vol, vol);
43638c2ecf20Sopenharmony_ci		}
43648c2ecf20Sopenharmony_ci	}
43658c2ecf20Sopenharmony_ci	cifs_cleanup_volume_info_contents(&fake_vol);
43668c2ecf20Sopenharmony_ci	return rc;
43678c2ecf20Sopenharmony_ci}
43688c2ecf20Sopenharmony_ci
43698c2ecf20Sopenharmony_cistatic int do_dfs_failover(const char *path, const char *full_path, struct cifs_sb_info *cifs_sb,
43708c2ecf20Sopenharmony_ci			   struct smb_vol *vol, struct cifs_ses *root_ses, unsigned int *xid,
43718c2ecf20Sopenharmony_ci			   struct TCP_Server_Info **server, struct cifs_ses **ses,
43728c2ecf20Sopenharmony_ci			   struct cifs_tcon **tcon)
43738c2ecf20Sopenharmony_ci{
43748c2ecf20Sopenharmony_ci	int rc;
43758c2ecf20Sopenharmony_ci	struct dfs_cache_tgt_list tgt_list;
43768c2ecf20Sopenharmony_ci	struct dfs_cache_tgt_iterator *tgt_it = NULL;
43778c2ecf20Sopenharmony_ci
43788c2ecf20Sopenharmony_ci	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS)
43798c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
43808c2ecf20Sopenharmony_ci
43818c2ecf20Sopenharmony_ci	rc = dfs_cache_noreq_find(path, NULL, &tgt_list);
43828c2ecf20Sopenharmony_ci	if (rc)
43838c2ecf20Sopenharmony_ci		return rc;
43848c2ecf20Sopenharmony_ci
43858c2ecf20Sopenharmony_ci	for (;;) {
43868c2ecf20Sopenharmony_ci		/* Get next DFS target server - if any */
43878c2ecf20Sopenharmony_ci		rc = get_next_dfs_tgt(path, &tgt_list, &tgt_it);
43888c2ecf20Sopenharmony_ci		if (rc)
43898c2ecf20Sopenharmony_ci			break;
43908c2ecf20Sopenharmony_ci		/* Connect to next DFS target */
43918c2ecf20Sopenharmony_ci		rc = setup_dfs_tgt_conn(path, full_path, tgt_it, cifs_sb, vol, xid, server, ses,
43928c2ecf20Sopenharmony_ci					tcon);
43938c2ecf20Sopenharmony_ci		if (!rc || (*server && *ses))
43948c2ecf20Sopenharmony_ci			break;
43958c2ecf20Sopenharmony_ci	}
43968c2ecf20Sopenharmony_ci	if (!rc) {
43978c2ecf20Sopenharmony_ci		/*
43988c2ecf20Sopenharmony_ci		 * Update DFS target hint in DFS referral cache with the target
43998c2ecf20Sopenharmony_ci		 * server we successfully reconnected to.
44008c2ecf20Sopenharmony_ci		 */
44018c2ecf20Sopenharmony_ci		rc = dfs_cache_update_tgthint(*xid, root_ses ? root_ses : *ses,
44028c2ecf20Sopenharmony_ci					      cifs_sb->local_nls,
44038c2ecf20Sopenharmony_ci					      cifs_remap(cifs_sb), path,
44048c2ecf20Sopenharmony_ci					      tgt_it);
44058c2ecf20Sopenharmony_ci	}
44068c2ecf20Sopenharmony_ci	dfs_cache_free_tgts(&tgt_list);
44078c2ecf20Sopenharmony_ci	return rc;
44088c2ecf20Sopenharmony_ci}
44098c2ecf20Sopenharmony_ci#endif
44108c2ecf20Sopenharmony_ci
44118c2ecf20Sopenharmony_ciint
44128c2ecf20Sopenharmony_cicifs_setup_volume_info(struct smb_vol *volume_info, char *mount_data,
44138c2ecf20Sopenharmony_ci			const char *devname, bool is_smb3)
44148c2ecf20Sopenharmony_ci{
44158c2ecf20Sopenharmony_ci	int rc = 0;
44168c2ecf20Sopenharmony_ci
44178c2ecf20Sopenharmony_ci	if (cifs_parse_mount_options(mount_data, devname, volume_info, is_smb3))
44188c2ecf20Sopenharmony_ci		return -EINVAL;
44198c2ecf20Sopenharmony_ci
44208c2ecf20Sopenharmony_ci	if (volume_info->nullauth) {
44218c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "Anonymous login\n");
44228c2ecf20Sopenharmony_ci		kfree(volume_info->username);
44238c2ecf20Sopenharmony_ci		volume_info->username = NULL;
44248c2ecf20Sopenharmony_ci	} else if (volume_info->username) {
44258c2ecf20Sopenharmony_ci		/* BB fixme parse for domain name here */
44268c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "Username: %s\n", volume_info->username);
44278c2ecf20Sopenharmony_ci	} else {
44288c2ecf20Sopenharmony_ci		cifs_dbg(VFS, "No username specified\n");
44298c2ecf20Sopenharmony_ci	/* In userspace mount helper we can get user name from alternate
44308c2ecf20Sopenharmony_ci	   locations such as env variables and files on disk */
44318c2ecf20Sopenharmony_ci		return -EINVAL;
44328c2ecf20Sopenharmony_ci	}
44338c2ecf20Sopenharmony_ci
44348c2ecf20Sopenharmony_ci	/* this is needed for ASCII cp to Unicode converts */
44358c2ecf20Sopenharmony_ci	if (volume_info->iocharset == NULL) {
44368c2ecf20Sopenharmony_ci		/* load_nls_default cannot return null */
44378c2ecf20Sopenharmony_ci		volume_info->local_nls = load_nls_default();
44388c2ecf20Sopenharmony_ci	} else {
44398c2ecf20Sopenharmony_ci		volume_info->local_nls = load_nls(volume_info->iocharset);
44408c2ecf20Sopenharmony_ci		if (volume_info->local_nls == NULL) {
44418c2ecf20Sopenharmony_ci			cifs_dbg(VFS, "CIFS mount error: iocharset %s not found\n",
44428c2ecf20Sopenharmony_ci				 volume_info->iocharset);
44438c2ecf20Sopenharmony_ci			return -ELIBACC;
44448c2ecf20Sopenharmony_ci		}
44458c2ecf20Sopenharmony_ci	}
44468c2ecf20Sopenharmony_ci
44478c2ecf20Sopenharmony_ci	return rc;
44488c2ecf20Sopenharmony_ci}
44498c2ecf20Sopenharmony_ci
44508c2ecf20Sopenharmony_cistruct smb_vol *
44518c2ecf20Sopenharmony_cicifs_get_volume_info(char *mount_data, const char *devname, bool is_smb3)
44528c2ecf20Sopenharmony_ci{
44538c2ecf20Sopenharmony_ci	int rc;
44548c2ecf20Sopenharmony_ci	struct smb_vol *volume_info;
44558c2ecf20Sopenharmony_ci
44568c2ecf20Sopenharmony_ci	volume_info = kmalloc(sizeof(struct smb_vol), GFP_KERNEL);
44578c2ecf20Sopenharmony_ci	if (!volume_info)
44588c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
44598c2ecf20Sopenharmony_ci
44608c2ecf20Sopenharmony_ci	rc = cifs_setup_volume_info(volume_info, mount_data, devname, is_smb3);
44618c2ecf20Sopenharmony_ci	if (rc) {
44628c2ecf20Sopenharmony_ci		cifs_cleanup_volume_info(volume_info);
44638c2ecf20Sopenharmony_ci		volume_info = ERR_PTR(rc);
44648c2ecf20Sopenharmony_ci	}
44658c2ecf20Sopenharmony_ci
44668c2ecf20Sopenharmony_ci	return volume_info;
44678c2ecf20Sopenharmony_ci}
44688c2ecf20Sopenharmony_ci
44698c2ecf20Sopenharmony_cistatic int
44708c2ecf20Sopenharmony_cicifs_are_all_path_components_accessible(struct TCP_Server_Info *server,
44718c2ecf20Sopenharmony_ci					unsigned int xid,
44728c2ecf20Sopenharmony_ci					struct cifs_tcon *tcon,
44738c2ecf20Sopenharmony_ci					struct cifs_sb_info *cifs_sb,
44748c2ecf20Sopenharmony_ci					char *full_path,
44758c2ecf20Sopenharmony_ci					int added_treename)
44768c2ecf20Sopenharmony_ci{
44778c2ecf20Sopenharmony_ci	int rc;
44788c2ecf20Sopenharmony_ci	char *s;
44798c2ecf20Sopenharmony_ci	char sep, tmp;
44808c2ecf20Sopenharmony_ci	int skip = added_treename ? 1 : 0;
44818c2ecf20Sopenharmony_ci
44828c2ecf20Sopenharmony_ci	sep = CIFS_DIR_SEP(cifs_sb);
44838c2ecf20Sopenharmony_ci	s = full_path;
44848c2ecf20Sopenharmony_ci
44858c2ecf20Sopenharmony_ci	rc = server->ops->is_path_accessible(xid, tcon, cifs_sb, "");
44868c2ecf20Sopenharmony_ci	while (rc == 0) {
44878c2ecf20Sopenharmony_ci		/* skip separators */
44888c2ecf20Sopenharmony_ci		while (*s == sep)
44898c2ecf20Sopenharmony_ci			s++;
44908c2ecf20Sopenharmony_ci		if (!*s)
44918c2ecf20Sopenharmony_ci			break;
44928c2ecf20Sopenharmony_ci		/* next separator */
44938c2ecf20Sopenharmony_ci		while (*s && *s != sep)
44948c2ecf20Sopenharmony_ci			s++;
44958c2ecf20Sopenharmony_ci		/*
44968c2ecf20Sopenharmony_ci		 * if the treename is added, we then have to skip the first
44978c2ecf20Sopenharmony_ci		 * part within the separators
44988c2ecf20Sopenharmony_ci		 */
44998c2ecf20Sopenharmony_ci		if (skip) {
45008c2ecf20Sopenharmony_ci			skip = 0;
45018c2ecf20Sopenharmony_ci			continue;
45028c2ecf20Sopenharmony_ci		}
45038c2ecf20Sopenharmony_ci		/*
45048c2ecf20Sopenharmony_ci		 * temporarily null-terminate the path at the end of
45058c2ecf20Sopenharmony_ci		 * the current component
45068c2ecf20Sopenharmony_ci		 */
45078c2ecf20Sopenharmony_ci		tmp = *s;
45088c2ecf20Sopenharmony_ci		*s = 0;
45098c2ecf20Sopenharmony_ci		rc = server->ops->is_path_accessible(xid, tcon, cifs_sb,
45108c2ecf20Sopenharmony_ci						     full_path);
45118c2ecf20Sopenharmony_ci		*s = tmp;
45128c2ecf20Sopenharmony_ci	}
45138c2ecf20Sopenharmony_ci	return rc;
45148c2ecf20Sopenharmony_ci}
45158c2ecf20Sopenharmony_ci
45168c2ecf20Sopenharmony_ci/*
45178c2ecf20Sopenharmony_ci * Check if path is remote (e.g. a DFS share). Return -EREMOTE if it is,
45188c2ecf20Sopenharmony_ci * otherwise 0.
45198c2ecf20Sopenharmony_ci */
45208c2ecf20Sopenharmony_cistatic int is_path_remote(struct cifs_sb_info *cifs_sb, struct smb_vol *vol,
45218c2ecf20Sopenharmony_ci			  const unsigned int xid,
45228c2ecf20Sopenharmony_ci			  struct TCP_Server_Info *server,
45238c2ecf20Sopenharmony_ci			  struct cifs_tcon *tcon)
45248c2ecf20Sopenharmony_ci{
45258c2ecf20Sopenharmony_ci	int rc;
45268c2ecf20Sopenharmony_ci	char *full_path;
45278c2ecf20Sopenharmony_ci
45288c2ecf20Sopenharmony_ci	if (!server->ops->is_path_accessible)
45298c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
45308c2ecf20Sopenharmony_ci
45318c2ecf20Sopenharmony_ci	/*
45328c2ecf20Sopenharmony_ci	 * cifs_build_path_to_root works only when we have a valid tcon
45338c2ecf20Sopenharmony_ci	 */
45348c2ecf20Sopenharmony_ci	full_path = cifs_build_path_to_root(vol, cifs_sb, tcon,
45358c2ecf20Sopenharmony_ci					    tcon->Flags & SMB_SHARE_IS_IN_DFS);
45368c2ecf20Sopenharmony_ci	if (full_path == NULL)
45378c2ecf20Sopenharmony_ci		return -ENOMEM;
45388c2ecf20Sopenharmony_ci
45398c2ecf20Sopenharmony_ci	cifs_dbg(FYI, "%s: full_path: %s\n", __func__, full_path);
45408c2ecf20Sopenharmony_ci
45418c2ecf20Sopenharmony_ci	rc = server->ops->is_path_accessible(xid, tcon, cifs_sb,
45428c2ecf20Sopenharmony_ci					     full_path);
45438c2ecf20Sopenharmony_ci	if (rc != 0 && rc != -EREMOTE) {
45448c2ecf20Sopenharmony_ci		kfree(full_path);
45458c2ecf20Sopenharmony_ci		return rc;
45468c2ecf20Sopenharmony_ci	}
45478c2ecf20Sopenharmony_ci
45488c2ecf20Sopenharmony_ci	if (rc != -EREMOTE) {
45498c2ecf20Sopenharmony_ci		rc = cifs_are_all_path_components_accessible(server, xid, tcon,
45508c2ecf20Sopenharmony_ci			cifs_sb, full_path, tcon->Flags & SMB_SHARE_IS_IN_DFS);
45518c2ecf20Sopenharmony_ci		if (rc != 0) {
45528c2ecf20Sopenharmony_ci			cifs_server_dbg(VFS, "cannot query dirs between root and final path, enabling CIFS_MOUNT_USE_PREFIX_PATH\n");
45538c2ecf20Sopenharmony_ci			cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
45548c2ecf20Sopenharmony_ci			rc = 0;
45558c2ecf20Sopenharmony_ci		}
45568c2ecf20Sopenharmony_ci	}
45578c2ecf20Sopenharmony_ci
45588c2ecf20Sopenharmony_ci	kfree(full_path);
45598c2ecf20Sopenharmony_ci	return rc;
45608c2ecf20Sopenharmony_ci}
45618c2ecf20Sopenharmony_ci
45628c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_DFS_UPCALL
45638c2ecf20Sopenharmony_cistatic void set_root_ses(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
45648c2ecf20Sopenharmony_ci			 struct cifs_ses **root_ses)
45658c2ecf20Sopenharmony_ci{
45668c2ecf20Sopenharmony_ci	if (ses) {
45678c2ecf20Sopenharmony_ci		spin_lock(&cifs_tcp_ses_lock);
45688c2ecf20Sopenharmony_ci		ses->ses_count++;
45698c2ecf20Sopenharmony_ci		if (ses->tcon_ipc)
45708c2ecf20Sopenharmony_ci			ses->tcon_ipc->remap = cifs_remap(cifs_sb);
45718c2ecf20Sopenharmony_ci		spin_unlock(&cifs_tcp_ses_lock);
45728c2ecf20Sopenharmony_ci	}
45738c2ecf20Sopenharmony_ci	*root_ses = ses;
45748c2ecf20Sopenharmony_ci}
45758c2ecf20Sopenharmony_ci
45768c2ecf20Sopenharmony_cistatic void put_root_ses(struct cifs_ses *ses)
45778c2ecf20Sopenharmony_ci{
45788c2ecf20Sopenharmony_ci	if (ses)
45798c2ecf20Sopenharmony_ci		cifs_put_smb_ses(ses);
45808c2ecf20Sopenharmony_ci}
45818c2ecf20Sopenharmony_ci
45828c2ecf20Sopenharmony_ci/* Check if a path component is remote and then update @dfs_path accordingly */
45838c2ecf20Sopenharmony_cistatic int check_dfs_prepath(struct cifs_sb_info *cifs_sb, struct smb_vol *vol,
45848c2ecf20Sopenharmony_ci			     const unsigned int xid, struct TCP_Server_Info *server,
45858c2ecf20Sopenharmony_ci			     struct cifs_tcon *tcon, char **dfs_path)
45868c2ecf20Sopenharmony_ci{
45878c2ecf20Sopenharmony_ci	char *path, *s;
45888c2ecf20Sopenharmony_ci	char sep = CIFS_DIR_SEP(cifs_sb), tmp;
45898c2ecf20Sopenharmony_ci	char *npath;
45908c2ecf20Sopenharmony_ci	int rc = 0;
45918c2ecf20Sopenharmony_ci	int added_treename = tcon->Flags & SMB_SHARE_IS_IN_DFS;
45928c2ecf20Sopenharmony_ci	int skip = added_treename;
45938c2ecf20Sopenharmony_ci
45948c2ecf20Sopenharmony_ci	path = cifs_build_path_to_root(vol, cifs_sb, tcon, added_treename);
45958c2ecf20Sopenharmony_ci	if (!path)
45968c2ecf20Sopenharmony_ci		return -ENOMEM;
45978c2ecf20Sopenharmony_ci
45988c2ecf20Sopenharmony_ci	/*
45998c2ecf20Sopenharmony_ci	 * Walk through the path components in @path and check if they're accessible. In case any of
46008c2ecf20Sopenharmony_ci	 * the components is -EREMOTE, then update @dfs_path with the next DFS referral request path
46018c2ecf20Sopenharmony_ci	 * (NOT including the remaining components).
46028c2ecf20Sopenharmony_ci	 */
46038c2ecf20Sopenharmony_ci	s = path;
46048c2ecf20Sopenharmony_ci	do {
46058c2ecf20Sopenharmony_ci		/* skip separators */
46068c2ecf20Sopenharmony_ci		while (*s && *s == sep)
46078c2ecf20Sopenharmony_ci			s++;
46088c2ecf20Sopenharmony_ci		if (!*s)
46098c2ecf20Sopenharmony_ci			break;
46108c2ecf20Sopenharmony_ci		/* next separator */
46118c2ecf20Sopenharmony_ci		while (*s && *s != sep)
46128c2ecf20Sopenharmony_ci			s++;
46138c2ecf20Sopenharmony_ci		/*
46148c2ecf20Sopenharmony_ci		 * if the treename is added, we then have to skip the first
46158c2ecf20Sopenharmony_ci		 * part within the separators
46168c2ecf20Sopenharmony_ci		 */
46178c2ecf20Sopenharmony_ci		if (skip) {
46188c2ecf20Sopenharmony_ci			skip = 0;
46198c2ecf20Sopenharmony_ci			continue;
46208c2ecf20Sopenharmony_ci		}
46218c2ecf20Sopenharmony_ci		tmp = *s;
46228c2ecf20Sopenharmony_ci		*s = 0;
46238c2ecf20Sopenharmony_ci		rc = server->ops->is_path_accessible(xid, tcon, cifs_sb, path);
46248c2ecf20Sopenharmony_ci		if (rc && rc == -EREMOTE) {
46258c2ecf20Sopenharmony_ci			struct smb_vol v = {NULL};
46268c2ecf20Sopenharmony_ci			/* if @path contains a tree name, skip it in the prefix path */
46278c2ecf20Sopenharmony_ci			if (added_treename) {
46288c2ecf20Sopenharmony_ci				rc = cifs_parse_devname(path, &v);
46298c2ecf20Sopenharmony_ci				if (rc)
46308c2ecf20Sopenharmony_ci					break;
46318c2ecf20Sopenharmony_ci				rc = -EREMOTE;
46328c2ecf20Sopenharmony_ci				npath = build_unc_path_to_root(&v, cifs_sb, true);
46338c2ecf20Sopenharmony_ci				cifs_cleanup_volume_info_contents(&v);
46348c2ecf20Sopenharmony_ci			} else {
46358c2ecf20Sopenharmony_ci				v.UNC = vol->UNC;
46368c2ecf20Sopenharmony_ci				v.prepath = path + 1;
46378c2ecf20Sopenharmony_ci				npath = build_unc_path_to_root(&v, cifs_sb, true);
46388c2ecf20Sopenharmony_ci			}
46398c2ecf20Sopenharmony_ci			if (IS_ERR(npath)) {
46408c2ecf20Sopenharmony_ci				rc = PTR_ERR(npath);
46418c2ecf20Sopenharmony_ci				break;
46428c2ecf20Sopenharmony_ci			}
46438c2ecf20Sopenharmony_ci			kfree(*dfs_path);
46448c2ecf20Sopenharmony_ci			*dfs_path = npath;
46458c2ecf20Sopenharmony_ci		}
46468c2ecf20Sopenharmony_ci		*s = tmp;
46478c2ecf20Sopenharmony_ci	} while (rc == 0);
46488c2ecf20Sopenharmony_ci
46498c2ecf20Sopenharmony_ci	kfree(path);
46508c2ecf20Sopenharmony_ci	return rc;
46518c2ecf20Sopenharmony_ci}
46528c2ecf20Sopenharmony_ci
46538c2ecf20Sopenharmony_ciint cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol)
46548c2ecf20Sopenharmony_ci{
46558c2ecf20Sopenharmony_ci	int rc = 0;
46568c2ecf20Sopenharmony_ci	unsigned int xid;
46578c2ecf20Sopenharmony_ci	struct TCP_Server_Info *server = NULL;
46588c2ecf20Sopenharmony_ci	struct cifs_ses *ses = NULL, *root_ses = NULL;
46598c2ecf20Sopenharmony_ci	struct cifs_tcon *tcon = NULL;
46608c2ecf20Sopenharmony_ci	int count = 0;
46618c2ecf20Sopenharmony_ci	char *ref_path = NULL, *full_path = NULL;
46628c2ecf20Sopenharmony_ci	char *oldmnt = NULL;
46638c2ecf20Sopenharmony_ci	char *mntdata = NULL;
46648c2ecf20Sopenharmony_ci
46658c2ecf20Sopenharmony_ci	rc = mount_get_conns(vol, cifs_sb, &xid, &server, &ses, &tcon);
46668c2ecf20Sopenharmony_ci	/*
46678c2ecf20Sopenharmony_ci	 * Unconditionally try to get an DFS referral (even cached) to determine whether it is an
46688c2ecf20Sopenharmony_ci	 * DFS mount.
46698c2ecf20Sopenharmony_ci	 *
46708c2ecf20Sopenharmony_ci	 * Skip prefix path to provide support for DFS referrals from w2k8 servers which don't seem
46718c2ecf20Sopenharmony_ci	 * to respond with PATH_NOT_COVERED to requests that include the prefix.
46728c2ecf20Sopenharmony_ci	 */
46738c2ecf20Sopenharmony_ci	if (dfs_cache_find(xid, ses, cifs_sb->local_nls, cifs_remap(cifs_sb), vol->UNC + 1, NULL,
46748c2ecf20Sopenharmony_ci			   NULL)) {
46758c2ecf20Sopenharmony_ci		/* No DFS referral was returned.  Looks like a regular share. */
46768c2ecf20Sopenharmony_ci		if (rc)
46778c2ecf20Sopenharmony_ci			goto error;
46788c2ecf20Sopenharmony_ci		/* Check if it is fully accessible and then mount it */
46798c2ecf20Sopenharmony_ci		rc = is_path_remote(cifs_sb, vol, xid, server, tcon);
46808c2ecf20Sopenharmony_ci		if (!rc)
46818c2ecf20Sopenharmony_ci			goto out;
46828c2ecf20Sopenharmony_ci		if (rc != -EREMOTE)
46838c2ecf20Sopenharmony_ci			goto error;
46848c2ecf20Sopenharmony_ci	}
46858c2ecf20Sopenharmony_ci	/* Save mount options */
46868c2ecf20Sopenharmony_ci	mntdata = kstrndup(cifs_sb->mountdata, strlen(cifs_sb->mountdata), GFP_KERNEL);
46878c2ecf20Sopenharmony_ci	if (!mntdata) {
46888c2ecf20Sopenharmony_ci		rc = -ENOMEM;
46898c2ecf20Sopenharmony_ci		goto error;
46908c2ecf20Sopenharmony_ci	}
46918c2ecf20Sopenharmony_ci	/* Get path of DFS root */
46928c2ecf20Sopenharmony_ci	ref_path = build_unc_path_to_root(vol, cifs_sb, false);
46938c2ecf20Sopenharmony_ci	if (IS_ERR(ref_path)) {
46948c2ecf20Sopenharmony_ci		rc = PTR_ERR(ref_path);
46958c2ecf20Sopenharmony_ci		ref_path = NULL;
46968c2ecf20Sopenharmony_ci		goto error;
46978c2ecf20Sopenharmony_ci	}
46988c2ecf20Sopenharmony_ci
46998c2ecf20Sopenharmony_ci	set_root_ses(cifs_sb, ses, &root_ses);
47008c2ecf20Sopenharmony_ci	do {
47018c2ecf20Sopenharmony_ci		/* Save full path of last DFS path we used to resolve final target server */
47028c2ecf20Sopenharmony_ci		kfree(full_path);
47038c2ecf20Sopenharmony_ci		full_path = build_unc_path_to_root(vol, cifs_sb, !!count);
47048c2ecf20Sopenharmony_ci		if (IS_ERR(full_path)) {
47058c2ecf20Sopenharmony_ci			rc = PTR_ERR(full_path);
47068c2ecf20Sopenharmony_ci			full_path = NULL;
47078c2ecf20Sopenharmony_ci			break;
47088c2ecf20Sopenharmony_ci		}
47098c2ecf20Sopenharmony_ci		/* Chase referral */
47108c2ecf20Sopenharmony_ci		oldmnt = cifs_sb->mountdata;
47118c2ecf20Sopenharmony_ci		rc = expand_dfs_referral(xid, root_ses, vol, cifs_sb, ref_path + 1);
47128c2ecf20Sopenharmony_ci		if (rc)
47138c2ecf20Sopenharmony_ci			break;
47148c2ecf20Sopenharmony_ci		/* Connect to new DFS target only if we were redirected */
47158c2ecf20Sopenharmony_ci		if (oldmnt != cifs_sb->mountdata) {
47168c2ecf20Sopenharmony_ci			mount_put_conns(cifs_sb, xid, server, ses, tcon);
47178c2ecf20Sopenharmony_ci			rc = mount_get_conns(vol, cifs_sb, &xid, &server, &ses, &tcon);
47188c2ecf20Sopenharmony_ci		}
47198c2ecf20Sopenharmony_ci		if (rc && !server && !ses) {
47208c2ecf20Sopenharmony_ci			/* Failed to connect. Try to connect to other targets in the referral. */
47218c2ecf20Sopenharmony_ci			rc = do_dfs_failover(ref_path + 1, full_path, cifs_sb, vol, root_ses, &xid,
47228c2ecf20Sopenharmony_ci					     &server, &ses, &tcon);
47238c2ecf20Sopenharmony_ci		}
47248c2ecf20Sopenharmony_ci		if (rc == -EACCES || rc == -EOPNOTSUPP || !server || !ses)
47258c2ecf20Sopenharmony_ci			break;
47268c2ecf20Sopenharmony_ci		if (!tcon)
47278c2ecf20Sopenharmony_ci			continue;
47288c2ecf20Sopenharmony_ci		/* Make sure that requests go through new root servers */
47298c2ecf20Sopenharmony_ci		if (is_tcon_dfs(tcon)) {
47308c2ecf20Sopenharmony_ci			put_root_ses(root_ses);
47318c2ecf20Sopenharmony_ci			set_root_ses(cifs_sb, ses, &root_ses);
47328c2ecf20Sopenharmony_ci		}
47338c2ecf20Sopenharmony_ci		/* Check for remaining path components and then continue chasing them (-EREMOTE) */
47348c2ecf20Sopenharmony_ci		rc = check_dfs_prepath(cifs_sb, vol, xid, server, tcon, &ref_path);
47358c2ecf20Sopenharmony_ci		/* Prevent recursion on broken link referrals */
47368c2ecf20Sopenharmony_ci		if (rc == -EREMOTE && ++count > MAX_NESTED_LINKS)
47378c2ecf20Sopenharmony_ci			rc = -ELOOP;
47388c2ecf20Sopenharmony_ci	} while (rc == -EREMOTE);
47398c2ecf20Sopenharmony_ci
47408c2ecf20Sopenharmony_ci	if (rc)
47418c2ecf20Sopenharmony_ci		goto error;
47428c2ecf20Sopenharmony_ci	put_root_ses(root_ses);
47438c2ecf20Sopenharmony_ci	root_ses = NULL;
47448c2ecf20Sopenharmony_ci	kfree(ref_path);
47458c2ecf20Sopenharmony_ci	ref_path = NULL;
47468c2ecf20Sopenharmony_ci	/*
47478c2ecf20Sopenharmony_ci	 * Store DFS full path in both superblock and tree connect structures.
47488c2ecf20Sopenharmony_ci	 *
47498c2ecf20Sopenharmony_ci	 * For DFS root mounts, the prefix path (cifs_sb->prepath) is preserved during reconnect so
47508c2ecf20Sopenharmony_ci	 * only the root path is set in cifs_sb->origin_fullpath and tcon->dfs_path. And for DFS
47518c2ecf20Sopenharmony_ci	 * links, the prefix path is included in both and may be changed during reconnect.  See
47528c2ecf20Sopenharmony_ci	 * cifs_tree_connect().
47538c2ecf20Sopenharmony_ci	 */
47548c2ecf20Sopenharmony_ci	cifs_sb->origin_fullpath = kstrndup(full_path, strlen(full_path), GFP_KERNEL);
47558c2ecf20Sopenharmony_ci	if (!cifs_sb->origin_fullpath) {
47568c2ecf20Sopenharmony_ci		rc = -ENOMEM;
47578c2ecf20Sopenharmony_ci		goto error;
47588c2ecf20Sopenharmony_ci	}
47598c2ecf20Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
47608c2ecf20Sopenharmony_ci	tcon->dfs_path = full_path;
47618c2ecf20Sopenharmony_ci	full_path = NULL;
47628c2ecf20Sopenharmony_ci	tcon->remap = cifs_remap(cifs_sb);
47638c2ecf20Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
47648c2ecf20Sopenharmony_ci
47658c2ecf20Sopenharmony_ci	/* Add original volume information for DFS cache to be used when refreshing referrals */
47668c2ecf20Sopenharmony_ci	rc = dfs_cache_add_vol(mntdata, vol, cifs_sb->origin_fullpath);
47678c2ecf20Sopenharmony_ci	if (rc)
47688c2ecf20Sopenharmony_ci		goto error;
47698c2ecf20Sopenharmony_ci	/*
47708c2ecf20Sopenharmony_ci	 * After reconnecting to a different server, unique ids won't
47718c2ecf20Sopenharmony_ci	 * match anymore, so we disable serverino. This prevents
47728c2ecf20Sopenharmony_ci	 * dentry revalidation to think the dentry are stale (ESTALE).
47738c2ecf20Sopenharmony_ci	 */
47748c2ecf20Sopenharmony_ci	cifs_autodisable_serverino(cifs_sb);
47758c2ecf20Sopenharmony_ci	/*
47768c2ecf20Sopenharmony_ci	 * Force the use of prefix path to support failover on DFS paths that
47778c2ecf20Sopenharmony_ci	 * resolve to targets that have different prefix paths.
47788c2ecf20Sopenharmony_ci	 */
47798c2ecf20Sopenharmony_ci	cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
47808c2ecf20Sopenharmony_ci	kfree(cifs_sb->prepath);
47818c2ecf20Sopenharmony_ci	cifs_sb->prepath = vol->prepath;
47828c2ecf20Sopenharmony_ci	vol->prepath = NULL;
47838c2ecf20Sopenharmony_ci
47848c2ecf20Sopenharmony_ciout:
47858c2ecf20Sopenharmony_ci	free_xid(xid);
47868c2ecf20Sopenharmony_ci	cifs_try_adding_channels(ses);
47878c2ecf20Sopenharmony_ci	return mount_setup_tlink(cifs_sb, ses, tcon);
47888c2ecf20Sopenharmony_ci
47898c2ecf20Sopenharmony_cierror:
47908c2ecf20Sopenharmony_ci	kfree(ref_path);
47918c2ecf20Sopenharmony_ci	kfree(full_path);
47928c2ecf20Sopenharmony_ci	kfree(mntdata);
47938c2ecf20Sopenharmony_ci	kfree(cifs_sb->origin_fullpath);
47948c2ecf20Sopenharmony_ci	put_root_ses(root_ses);
47958c2ecf20Sopenharmony_ci	mount_put_conns(cifs_sb, xid, server, ses, tcon);
47968c2ecf20Sopenharmony_ci	return rc;
47978c2ecf20Sopenharmony_ci}
47988c2ecf20Sopenharmony_ci#else
47998c2ecf20Sopenharmony_ciint cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol)
48008c2ecf20Sopenharmony_ci{
48018c2ecf20Sopenharmony_ci	int rc = 0;
48028c2ecf20Sopenharmony_ci	unsigned int xid;
48038c2ecf20Sopenharmony_ci	struct cifs_ses *ses;
48048c2ecf20Sopenharmony_ci	struct cifs_tcon *tcon;
48058c2ecf20Sopenharmony_ci	struct TCP_Server_Info *server;
48068c2ecf20Sopenharmony_ci
48078c2ecf20Sopenharmony_ci	rc = mount_get_conns(vol, cifs_sb, &xid, &server, &ses, &tcon);
48088c2ecf20Sopenharmony_ci	if (rc)
48098c2ecf20Sopenharmony_ci		goto error;
48108c2ecf20Sopenharmony_ci
48118c2ecf20Sopenharmony_ci	if (tcon) {
48128c2ecf20Sopenharmony_ci		rc = is_path_remote(cifs_sb, vol, xid, server, tcon);
48138c2ecf20Sopenharmony_ci		if (rc == -EREMOTE)
48148c2ecf20Sopenharmony_ci			rc = -EOPNOTSUPP;
48158c2ecf20Sopenharmony_ci		if (rc)
48168c2ecf20Sopenharmony_ci			goto error;
48178c2ecf20Sopenharmony_ci	}
48188c2ecf20Sopenharmony_ci
48198c2ecf20Sopenharmony_ci	free_xid(xid);
48208c2ecf20Sopenharmony_ci
48218c2ecf20Sopenharmony_ci	return mount_setup_tlink(cifs_sb, ses, tcon);
48228c2ecf20Sopenharmony_ci
48238c2ecf20Sopenharmony_cierror:
48248c2ecf20Sopenharmony_ci	mount_put_conns(cifs_sb, xid, server, ses, tcon);
48258c2ecf20Sopenharmony_ci	return rc;
48268c2ecf20Sopenharmony_ci}
48278c2ecf20Sopenharmony_ci#endif
48288c2ecf20Sopenharmony_ci
48298c2ecf20Sopenharmony_ci/*
48308c2ecf20Sopenharmony_ci * Issue a TREE_CONNECT request.
48318c2ecf20Sopenharmony_ci */
48328c2ecf20Sopenharmony_ciint
48338c2ecf20Sopenharmony_ciCIFSTCon(const unsigned int xid, struct cifs_ses *ses,
48348c2ecf20Sopenharmony_ci	 const char *tree, struct cifs_tcon *tcon,
48358c2ecf20Sopenharmony_ci	 const struct nls_table *nls_codepage)
48368c2ecf20Sopenharmony_ci{
48378c2ecf20Sopenharmony_ci	struct smb_hdr *smb_buffer;
48388c2ecf20Sopenharmony_ci	struct smb_hdr *smb_buffer_response;
48398c2ecf20Sopenharmony_ci	TCONX_REQ *pSMB;
48408c2ecf20Sopenharmony_ci	TCONX_RSP *pSMBr;
48418c2ecf20Sopenharmony_ci	unsigned char *bcc_ptr;
48428c2ecf20Sopenharmony_ci	int rc = 0;
48438c2ecf20Sopenharmony_ci	int length;
48448c2ecf20Sopenharmony_ci	__u16 bytes_left, count;
48458c2ecf20Sopenharmony_ci
48468c2ecf20Sopenharmony_ci	if (ses == NULL)
48478c2ecf20Sopenharmony_ci		return -EIO;
48488c2ecf20Sopenharmony_ci
48498c2ecf20Sopenharmony_ci	smb_buffer = cifs_buf_get();
48508c2ecf20Sopenharmony_ci	if (smb_buffer == NULL)
48518c2ecf20Sopenharmony_ci		return -ENOMEM;
48528c2ecf20Sopenharmony_ci
48538c2ecf20Sopenharmony_ci	smb_buffer_response = smb_buffer;
48548c2ecf20Sopenharmony_ci
48558c2ecf20Sopenharmony_ci	header_assemble(smb_buffer, SMB_COM_TREE_CONNECT_ANDX,
48568c2ecf20Sopenharmony_ci			NULL /*no tid */ , 4 /*wct */ );
48578c2ecf20Sopenharmony_ci
48588c2ecf20Sopenharmony_ci	smb_buffer->Mid = get_next_mid(ses->server);
48598c2ecf20Sopenharmony_ci	smb_buffer->Uid = ses->Suid;
48608c2ecf20Sopenharmony_ci	pSMB = (TCONX_REQ *) smb_buffer;
48618c2ecf20Sopenharmony_ci	pSMBr = (TCONX_RSP *) smb_buffer_response;
48628c2ecf20Sopenharmony_ci
48638c2ecf20Sopenharmony_ci	pSMB->AndXCommand = 0xFF;
48648c2ecf20Sopenharmony_ci	pSMB->Flags = cpu_to_le16(TCON_EXTENDED_SECINFO);
48658c2ecf20Sopenharmony_ci	bcc_ptr = &pSMB->Password[0];
48668c2ecf20Sopenharmony_ci	if (tcon->pipe || (ses->server->sec_mode & SECMODE_USER)) {
48678c2ecf20Sopenharmony_ci		pSMB->PasswordLength = cpu_to_le16(1);	/* minimum */
48688c2ecf20Sopenharmony_ci		*bcc_ptr = 0; /* password is null byte */
48698c2ecf20Sopenharmony_ci		bcc_ptr++;              /* skip password */
48708c2ecf20Sopenharmony_ci		/* already aligned so no need to do it below */
48718c2ecf20Sopenharmony_ci	} else {
48728c2ecf20Sopenharmony_ci		pSMB->PasswordLength = cpu_to_le16(CIFS_AUTH_RESP_SIZE);
48738c2ecf20Sopenharmony_ci		/* BB FIXME add code to fail this if NTLMv2 or Kerberos
48748c2ecf20Sopenharmony_ci		   specified as required (when that support is added to
48758c2ecf20Sopenharmony_ci		   the vfs in the future) as only NTLM or the much
48768c2ecf20Sopenharmony_ci		   weaker LANMAN (which we do not send by default) is accepted
48778c2ecf20Sopenharmony_ci		   by Samba (not sure whether other servers allow
48788c2ecf20Sopenharmony_ci		   NTLMv2 password here) */
48798c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_WEAK_PW_HASH
48808c2ecf20Sopenharmony_ci		if ((global_secflags & CIFSSEC_MAY_LANMAN) &&
48818c2ecf20Sopenharmony_ci		    (ses->sectype == LANMAN))
48828c2ecf20Sopenharmony_ci			calc_lanman_hash(tcon->password, ses->server->cryptkey,
48838c2ecf20Sopenharmony_ci					 ses->server->sec_mode &
48848c2ecf20Sopenharmony_ci					    SECMODE_PW_ENCRYPT ? true : false,
48858c2ecf20Sopenharmony_ci					 bcc_ptr);
48868c2ecf20Sopenharmony_ci		else
48878c2ecf20Sopenharmony_ci#endif /* CIFS_WEAK_PW_HASH */
48888c2ecf20Sopenharmony_ci		rc = SMBNTencrypt(tcon->password, ses->server->cryptkey,
48898c2ecf20Sopenharmony_ci					bcc_ptr, nls_codepage);
48908c2ecf20Sopenharmony_ci		if (rc) {
48918c2ecf20Sopenharmony_ci			cifs_dbg(FYI, "%s Can't generate NTLM rsp. Error: %d\n",
48928c2ecf20Sopenharmony_ci				 __func__, rc);
48938c2ecf20Sopenharmony_ci			cifs_buf_release(smb_buffer);
48948c2ecf20Sopenharmony_ci			return rc;
48958c2ecf20Sopenharmony_ci		}
48968c2ecf20Sopenharmony_ci
48978c2ecf20Sopenharmony_ci		bcc_ptr += CIFS_AUTH_RESP_SIZE;
48988c2ecf20Sopenharmony_ci		if (ses->capabilities & CAP_UNICODE) {
48998c2ecf20Sopenharmony_ci			/* must align unicode strings */
49008c2ecf20Sopenharmony_ci			*bcc_ptr = 0; /* null byte password */
49018c2ecf20Sopenharmony_ci			bcc_ptr++;
49028c2ecf20Sopenharmony_ci		}
49038c2ecf20Sopenharmony_ci	}
49048c2ecf20Sopenharmony_ci
49058c2ecf20Sopenharmony_ci	if (ses->server->sign)
49068c2ecf20Sopenharmony_ci		smb_buffer->Flags2 |= SMBFLG2_SECURITY_SIGNATURE;
49078c2ecf20Sopenharmony_ci
49088c2ecf20Sopenharmony_ci	if (ses->capabilities & CAP_STATUS32) {
49098c2ecf20Sopenharmony_ci		smb_buffer->Flags2 |= SMBFLG2_ERR_STATUS;
49108c2ecf20Sopenharmony_ci	}
49118c2ecf20Sopenharmony_ci	if (ses->capabilities & CAP_DFS) {
49128c2ecf20Sopenharmony_ci		smb_buffer->Flags2 |= SMBFLG2_DFS;
49138c2ecf20Sopenharmony_ci	}
49148c2ecf20Sopenharmony_ci	if (ses->capabilities & CAP_UNICODE) {
49158c2ecf20Sopenharmony_ci		smb_buffer->Flags2 |= SMBFLG2_UNICODE;
49168c2ecf20Sopenharmony_ci		length =
49178c2ecf20Sopenharmony_ci		    cifs_strtoUTF16((__le16 *) bcc_ptr, tree,
49188c2ecf20Sopenharmony_ci			6 /* max utf8 char length in bytes */ *
49198c2ecf20Sopenharmony_ci			(/* server len*/ + 256 /* share len */), nls_codepage);
49208c2ecf20Sopenharmony_ci		bcc_ptr += 2 * length;	/* convert num 16 bit words to bytes */
49218c2ecf20Sopenharmony_ci		bcc_ptr += 2;	/* skip trailing null */
49228c2ecf20Sopenharmony_ci	} else {		/* ASCII */
49238c2ecf20Sopenharmony_ci		strcpy(bcc_ptr, tree);
49248c2ecf20Sopenharmony_ci		bcc_ptr += strlen(tree) + 1;
49258c2ecf20Sopenharmony_ci	}
49268c2ecf20Sopenharmony_ci	strcpy(bcc_ptr, "?????");
49278c2ecf20Sopenharmony_ci	bcc_ptr += strlen("?????");
49288c2ecf20Sopenharmony_ci	bcc_ptr += 1;
49298c2ecf20Sopenharmony_ci	count = bcc_ptr - &pSMB->Password[0];
49308c2ecf20Sopenharmony_ci	be32_add_cpu(&pSMB->hdr.smb_buf_length, count);
49318c2ecf20Sopenharmony_ci	pSMB->ByteCount = cpu_to_le16(count);
49328c2ecf20Sopenharmony_ci
49338c2ecf20Sopenharmony_ci	rc = SendReceive(xid, ses, smb_buffer, smb_buffer_response, &length,
49348c2ecf20Sopenharmony_ci			 0);
49358c2ecf20Sopenharmony_ci
49368c2ecf20Sopenharmony_ci	/* above now done in SendReceive */
49378c2ecf20Sopenharmony_ci	if (rc == 0) {
49388c2ecf20Sopenharmony_ci		bool is_unicode;
49398c2ecf20Sopenharmony_ci
49408c2ecf20Sopenharmony_ci		tcon->tidStatus = CifsGood;
49418c2ecf20Sopenharmony_ci		tcon->need_reconnect = false;
49428c2ecf20Sopenharmony_ci		tcon->tid = smb_buffer_response->Tid;
49438c2ecf20Sopenharmony_ci		bcc_ptr = pByteArea(smb_buffer_response);
49448c2ecf20Sopenharmony_ci		bytes_left = get_bcc(smb_buffer_response);
49458c2ecf20Sopenharmony_ci		length = strnlen(bcc_ptr, bytes_left - 2);
49468c2ecf20Sopenharmony_ci		if (smb_buffer->Flags2 & SMBFLG2_UNICODE)
49478c2ecf20Sopenharmony_ci			is_unicode = true;
49488c2ecf20Sopenharmony_ci		else
49498c2ecf20Sopenharmony_ci			is_unicode = false;
49508c2ecf20Sopenharmony_ci
49518c2ecf20Sopenharmony_ci
49528c2ecf20Sopenharmony_ci		/* skip service field (NB: this field is always ASCII) */
49538c2ecf20Sopenharmony_ci		if (length == 3) {
49548c2ecf20Sopenharmony_ci			if ((bcc_ptr[0] == 'I') && (bcc_ptr[1] == 'P') &&
49558c2ecf20Sopenharmony_ci			    (bcc_ptr[2] == 'C')) {
49568c2ecf20Sopenharmony_ci				cifs_dbg(FYI, "IPC connection\n");
49578c2ecf20Sopenharmony_ci				tcon->ipc = true;
49588c2ecf20Sopenharmony_ci				tcon->pipe = true;
49598c2ecf20Sopenharmony_ci			}
49608c2ecf20Sopenharmony_ci		} else if (length == 2) {
49618c2ecf20Sopenharmony_ci			if ((bcc_ptr[0] == 'A') && (bcc_ptr[1] == ':')) {
49628c2ecf20Sopenharmony_ci				/* the most common case */
49638c2ecf20Sopenharmony_ci				cifs_dbg(FYI, "disk share connection\n");
49648c2ecf20Sopenharmony_ci			}
49658c2ecf20Sopenharmony_ci		}
49668c2ecf20Sopenharmony_ci		bcc_ptr += length + 1;
49678c2ecf20Sopenharmony_ci		bytes_left -= (length + 1);
49688c2ecf20Sopenharmony_ci		strlcpy(tcon->treeName, tree, sizeof(tcon->treeName));
49698c2ecf20Sopenharmony_ci
49708c2ecf20Sopenharmony_ci		/* mostly informational -- no need to fail on error here */
49718c2ecf20Sopenharmony_ci		kfree(tcon->nativeFileSystem);
49728c2ecf20Sopenharmony_ci		tcon->nativeFileSystem = cifs_strndup_from_utf16(bcc_ptr,
49738c2ecf20Sopenharmony_ci						      bytes_left, is_unicode,
49748c2ecf20Sopenharmony_ci						      nls_codepage);
49758c2ecf20Sopenharmony_ci
49768c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "nativeFileSystem=%s\n", tcon->nativeFileSystem);
49778c2ecf20Sopenharmony_ci
49788c2ecf20Sopenharmony_ci		if ((smb_buffer_response->WordCount == 3) ||
49798c2ecf20Sopenharmony_ci			 (smb_buffer_response->WordCount == 7))
49808c2ecf20Sopenharmony_ci			/* field is in same location */
49818c2ecf20Sopenharmony_ci			tcon->Flags = le16_to_cpu(pSMBr->OptionalSupport);
49828c2ecf20Sopenharmony_ci		else
49838c2ecf20Sopenharmony_ci			tcon->Flags = 0;
49848c2ecf20Sopenharmony_ci		cifs_dbg(FYI, "Tcon flags: 0x%x\n", tcon->Flags);
49858c2ecf20Sopenharmony_ci	}
49868c2ecf20Sopenharmony_ci
49878c2ecf20Sopenharmony_ci	cifs_buf_release(smb_buffer);
49888c2ecf20Sopenharmony_ci	return rc;
49898c2ecf20Sopenharmony_ci}
49908c2ecf20Sopenharmony_ci
49918c2ecf20Sopenharmony_cistatic void delayed_free(struct rcu_head *p)
49928c2ecf20Sopenharmony_ci{
49938c2ecf20Sopenharmony_ci	struct cifs_sb_info *sbi = container_of(p, struct cifs_sb_info, rcu);
49948c2ecf20Sopenharmony_ci	unload_nls(sbi->local_nls);
49958c2ecf20Sopenharmony_ci	kfree(sbi);
49968c2ecf20Sopenharmony_ci}
49978c2ecf20Sopenharmony_ci
49988c2ecf20Sopenharmony_civoid
49998c2ecf20Sopenharmony_cicifs_umount(struct cifs_sb_info *cifs_sb)
50008c2ecf20Sopenharmony_ci{
50018c2ecf20Sopenharmony_ci	struct rb_root *root = &cifs_sb->tlink_tree;
50028c2ecf20Sopenharmony_ci	struct rb_node *node;
50038c2ecf20Sopenharmony_ci	struct tcon_link *tlink;
50048c2ecf20Sopenharmony_ci
50058c2ecf20Sopenharmony_ci	cancel_delayed_work_sync(&cifs_sb->prune_tlinks);
50068c2ecf20Sopenharmony_ci
50078c2ecf20Sopenharmony_ci	spin_lock(&cifs_sb->tlink_tree_lock);
50088c2ecf20Sopenharmony_ci	while ((node = rb_first(root))) {
50098c2ecf20Sopenharmony_ci		tlink = rb_entry(node, struct tcon_link, tl_rbnode);
50108c2ecf20Sopenharmony_ci		cifs_get_tlink(tlink);
50118c2ecf20Sopenharmony_ci		clear_bit(TCON_LINK_IN_TREE, &tlink->tl_flags);
50128c2ecf20Sopenharmony_ci		rb_erase(node, root);
50138c2ecf20Sopenharmony_ci
50148c2ecf20Sopenharmony_ci		spin_unlock(&cifs_sb->tlink_tree_lock);
50158c2ecf20Sopenharmony_ci		cifs_put_tlink(tlink);
50168c2ecf20Sopenharmony_ci		spin_lock(&cifs_sb->tlink_tree_lock);
50178c2ecf20Sopenharmony_ci	}
50188c2ecf20Sopenharmony_ci	spin_unlock(&cifs_sb->tlink_tree_lock);
50198c2ecf20Sopenharmony_ci
50208c2ecf20Sopenharmony_ci	kfree(cifs_sb->mountdata);
50218c2ecf20Sopenharmony_ci	kfree(cifs_sb->prepath);
50228c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_DFS_UPCALL
50238c2ecf20Sopenharmony_ci	dfs_cache_del_vol(cifs_sb->origin_fullpath);
50248c2ecf20Sopenharmony_ci	kfree(cifs_sb->origin_fullpath);
50258c2ecf20Sopenharmony_ci#endif
50268c2ecf20Sopenharmony_ci	call_rcu(&cifs_sb->rcu, delayed_free);
50278c2ecf20Sopenharmony_ci}
50288c2ecf20Sopenharmony_ci
50298c2ecf20Sopenharmony_ciint
50308c2ecf20Sopenharmony_cicifs_negotiate_protocol(const unsigned int xid, struct cifs_ses *ses)
50318c2ecf20Sopenharmony_ci{
50328c2ecf20Sopenharmony_ci	int rc = 0;
50338c2ecf20Sopenharmony_ci	struct TCP_Server_Info *server = cifs_ses_server(ses);
50348c2ecf20Sopenharmony_ci
50358c2ecf20Sopenharmony_ci	if (!server->ops->need_neg || !server->ops->negotiate)
50368c2ecf20Sopenharmony_ci		return -ENOSYS;
50378c2ecf20Sopenharmony_ci
50388c2ecf20Sopenharmony_ci	/* only send once per connect */
50398c2ecf20Sopenharmony_ci	if (!server->ops->need_neg(server))
50408c2ecf20Sopenharmony_ci		return 0;
50418c2ecf20Sopenharmony_ci
50428c2ecf20Sopenharmony_ci	rc = server->ops->negotiate(xid, ses);
50438c2ecf20Sopenharmony_ci	if (rc == 0) {
50448c2ecf20Sopenharmony_ci		spin_lock(&GlobalMid_Lock);
50458c2ecf20Sopenharmony_ci		if (server->tcpStatus == CifsNeedNegotiate)
50468c2ecf20Sopenharmony_ci			server->tcpStatus = CifsGood;
50478c2ecf20Sopenharmony_ci		else
50488c2ecf20Sopenharmony_ci			rc = -EHOSTDOWN;
50498c2ecf20Sopenharmony_ci		spin_unlock(&GlobalMid_Lock);
50508c2ecf20Sopenharmony_ci	}
50518c2ecf20Sopenharmony_ci
50528c2ecf20Sopenharmony_ci	return rc;
50538c2ecf20Sopenharmony_ci}
50548c2ecf20Sopenharmony_ci
50558c2ecf20Sopenharmony_ciint
50568c2ecf20Sopenharmony_cicifs_setup_session(const unsigned int xid, struct cifs_ses *ses,
50578c2ecf20Sopenharmony_ci		   struct nls_table *nls_info)
50588c2ecf20Sopenharmony_ci{
50598c2ecf20Sopenharmony_ci	int rc = -ENOSYS;
50608c2ecf20Sopenharmony_ci	struct TCP_Server_Info *server = cifs_ses_server(ses);
50618c2ecf20Sopenharmony_ci
50628c2ecf20Sopenharmony_ci	if (!ses->binding) {
50638c2ecf20Sopenharmony_ci		ses->capabilities = server->capabilities;
50648c2ecf20Sopenharmony_ci		if (linuxExtEnabled == 0)
50658c2ecf20Sopenharmony_ci			ses->capabilities &= (~server->vals->cap_unix);
50668c2ecf20Sopenharmony_ci
50678c2ecf20Sopenharmony_ci		if (ses->auth_key.response) {
50688c2ecf20Sopenharmony_ci			cifs_dbg(FYI, "Free previous auth_key.response = %p\n",
50698c2ecf20Sopenharmony_ci				 ses->auth_key.response);
50708c2ecf20Sopenharmony_ci			kfree(ses->auth_key.response);
50718c2ecf20Sopenharmony_ci			ses->auth_key.response = NULL;
50728c2ecf20Sopenharmony_ci			ses->auth_key.len = 0;
50738c2ecf20Sopenharmony_ci		}
50748c2ecf20Sopenharmony_ci	}
50758c2ecf20Sopenharmony_ci
50768c2ecf20Sopenharmony_ci	cifs_dbg(FYI, "Security Mode: 0x%x Capabilities: 0x%x TimeAdjust: %d\n",
50778c2ecf20Sopenharmony_ci		 server->sec_mode, server->capabilities, server->timeAdj);
50788c2ecf20Sopenharmony_ci
50798c2ecf20Sopenharmony_ci	if (server->ops->sess_setup)
50808c2ecf20Sopenharmony_ci		rc = server->ops->sess_setup(xid, ses, nls_info);
50818c2ecf20Sopenharmony_ci
50828c2ecf20Sopenharmony_ci	if (rc)
50838c2ecf20Sopenharmony_ci		cifs_server_dbg(VFS, "Send error in SessSetup = %d\n", rc);
50848c2ecf20Sopenharmony_ci
50858c2ecf20Sopenharmony_ci	return rc;
50868c2ecf20Sopenharmony_ci}
50878c2ecf20Sopenharmony_ci
50888c2ecf20Sopenharmony_cistatic int
50898c2ecf20Sopenharmony_cicifs_set_vol_auth(struct smb_vol *vol, struct cifs_ses *ses)
50908c2ecf20Sopenharmony_ci{
50918c2ecf20Sopenharmony_ci	vol->sectype = ses->sectype;
50928c2ecf20Sopenharmony_ci
50938c2ecf20Sopenharmony_ci	/* krb5 is special, since we don't need username or pw */
50948c2ecf20Sopenharmony_ci	if (vol->sectype == Kerberos)
50958c2ecf20Sopenharmony_ci		return 0;
50968c2ecf20Sopenharmony_ci
50978c2ecf20Sopenharmony_ci	return cifs_set_cifscreds(vol, ses);
50988c2ecf20Sopenharmony_ci}
50998c2ecf20Sopenharmony_ci
51008c2ecf20Sopenharmony_cistatic struct cifs_tcon *
51018c2ecf20Sopenharmony_cicifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)
51028c2ecf20Sopenharmony_ci{
51038c2ecf20Sopenharmony_ci	int rc;
51048c2ecf20Sopenharmony_ci	struct cifs_tcon *master_tcon = cifs_sb_master_tcon(cifs_sb);
51058c2ecf20Sopenharmony_ci	struct cifs_ses *ses;
51068c2ecf20Sopenharmony_ci	struct cifs_tcon *tcon = NULL;
51078c2ecf20Sopenharmony_ci	struct smb_vol *vol_info;
51088c2ecf20Sopenharmony_ci
51098c2ecf20Sopenharmony_ci	vol_info = kzalloc(sizeof(*vol_info), GFP_KERNEL);
51108c2ecf20Sopenharmony_ci	if (vol_info == NULL)
51118c2ecf20Sopenharmony_ci		return ERR_PTR(-ENOMEM);
51128c2ecf20Sopenharmony_ci
51138c2ecf20Sopenharmony_ci	vol_info->local_nls = cifs_sb->local_nls;
51148c2ecf20Sopenharmony_ci	vol_info->linux_uid = fsuid;
51158c2ecf20Sopenharmony_ci	vol_info->cred_uid = fsuid;
51168c2ecf20Sopenharmony_ci	vol_info->UNC = master_tcon->treeName;
51178c2ecf20Sopenharmony_ci	vol_info->retry = master_tcon->retry;
51188c2ecf20Sopenharmony_ci	vol_info->nocase = master_tcon->nocase;
51198c2ecf20Sopenharmony_ci	vol_info->nohandlecache = master_tcon->nohandlecache;
51208c2ecf20Sopenharmony_ci	vol_info->local_lease = master_tcon->local_lease;
51218c2ecf20Sopenharmony_ci	vol_info->no_lease = master_tcon->no_lease;
51228c2ecf20Sopenharmony_ci	vol_info->resilient = master_tcon->use_resilient;
51238c2ecf20Sopenharmony_ci	vol_info->persistent = master_tcon->use_persistent;
51248c2ecf20Sopenharmony_ci	vol_info->handle_timeout = master_tcon->handle_timeout;
51258c2ecf20Sopenharmony_ci	vol_info->no_linux_ext = !master_tcon->unix_ext;
51268c2ecf20Sopenharmony_ci	vol_info->linux_ext = master_tcon->posix_extensions;
51278c2ecf20Sopenharmony_ci	vol_info->sectype = master_tcon->ses->sectype;
51288c2ecf20Sopenharmony_ci	vol_info->sign = master_tcon->ses->sign;
51298c2ecf20Sopenharmony_ci	vol_info->seal = master_tcon->seal;
51308c2ecf20Sopenharmony_ci
51318c2ecf20Sopenharmony_ci	rc = cifs_set_vol_auth(vol_info, master_tcon->ses);
51328c2ecf20Sopenharmony_ci	if (rc) {
51338c2ecf20Sopenharmony_ci		tcon = ERR_PTR(rc);
51348c2ecf20Sopenharmony_ci		goto out;
51358c2ecf20Sopenharmony_ci	}
51368c2ecf20Sopenharmony_ci
51378c2ecf20Sopenharmony_ci	/* get a reference for the same TCP session */
51388c2ecf20Sopenharmony_ci	spin_lock(&cifs_tcp_ses_lock);
51398c2ecf20Sopenharmony_ci	++master_tcon->ses->server->srv_count;
51408c2ecf20Sopenharmony_ci	spin_unlock(&cifs_tcp_ses_lock);
51418c2ecf20Sopenharmony_ci
51428c2ecf20Sopenharmony_ci	ses = cifs_get_smb_ses(master_tcon->ses->server, vol_info);
51438c2ecf20Sopenharmony_ci	if (IS_ERR(ses)) {
51448c2ecf20Sopenharmony_ci		tcon = (struct cifs_tcon *)ses;
51458c2ecf20Sopenharmony_ci		cifs_put_tcp_session(master_tcon->ses->server, 0);
51468c2ecf20Sopenharmony_ci		goto out;
51478c2ecf20Sopenharmony_ci	}
51488c2ecf20Sopenharmony_ci
51498c2ecf20Sopenharmony_ci	tcon = cifs_get_tcon(ses, vol_info);
51508c2ecf20Sopenharmony_ci	if (IS_ERR(tcon)) {
51518c2ecf20Sopenharmony_ci		cifs_put_smb_ses(ses);
51528c2ecf20Sopenharmony_ci		goto out;
51538c2ecf20Sopenharmony_ci	}
51548c2ecf20Sopenharmony_ci
51558c2ecf20Sopenharmony_ci	if (cap_unix(ses))
51568c2ecf20Sopenharmony_ci		reset_cifs_unix_caps(0, tcon, NULL, vol_info);
51578c2ecf20Sopenharmony_ci
51588c2ecf20Sopenharmony_ciout:
51598c2ecf20Sopenharmony_ci	kfree(vol_info->username);
51608c2ecf20Sopenharmony_ci	kfree_sensitive(vol_info->password);
51618c2ecf20Sopenharmony_ci	kfree(vol_info);
51628c2ecf20Sopenharmony_ci
51638c2ecf20Sopenharmony_ci	return tcon;
51648c2ecf20Sopenharmony_ci}
51658c2ecf20Sopenharmony_ci
51668c2ecf20Sopenharmony_cistruct cifs_tcon *
51678c2ecf20Sopenharmony_cicifs_sb_master_tcon(struct cifs_sb_info *cifs_sb)
51688c2ecf20Sopenharmony_ci{
51698c2ecf20Sopenharmony_ci	return tlink_tcon(cifs_sb_master_tlink(cifs_sb));
51708c2ecf20Sopenharmony_ci}
51718c2ecf20Sopenharmony_ci
51728c2ecf20Sopenharmony_ci/* find and return a tlink with given uid */
51738c2ecf20Sopenharmony_cistatic struct tcon_link *
51748c2ecf20Sopenharmony_citlink_rb_search(struct rb_root *root, kuid_t uid)
51758c2ecf20Sopenharmony_ci{
51768c2ecf20Sopenharmony_ci	struct rb_node *node = root->rb_node;
51778c2ecf20Sopenharmony_ci	struct tcon_link *tlink;
51788c2ecf20Sopenharmony_ci
51798c2ecf20Sopenharmony_ci	while (node) {
51808c2ecf20Sopenharmony_ci		tlink = rb_entry(node, struct tcon_link, tl_rbnode);
51818c2ecf20Sopenharmony_ci
51828c2ecf20Sopenharmony_ci		if (uid_gt(tlink->tl_uid, uid))
51838c2ecf20Sopenharmony_ci			node = node->rb_left;
51848c2ecf20Sopenharmony_ci		else if (uid_lt(tlink->tl_uid, uid))
51858c2ecf20Sopenharmony_ci			node = node->rb_right;
51868c2ecf20Sopenharmony_ci		else
51878c2ecf20Sopenharmony_ci			return tlink;
51888c2ecf20Sopenharmony_ci	}
51898c2ecf20Sopenharmony_ci	return NULL;
51908c2ecf20Sopenharmony_ci}
51918c2ecf20Sopenharmony_ci
51928c2ecf20Sopenharmony_ci/* insert a tcon_link into the tree */
51938c2ecf20Sopenharmony_cistatic void
51948c2ecf20Sopenharmony_citlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink)
51958c2ecf20Sopenharmony_ci{
51968c2ecf20Sopenharmony_ci	struct rb_node **new = &(root->rb_node), *parent = NULL;
51978c2ecf20Sopenharmony_ci	struct tcon_link *tlink;
51988c2ecf20Sopenharmony_ci
51998c2ecf20Sopenharmony_ci	while (*new) {
52008c2ecf20Sopenharmony_ci		tlink = rb_entry(*new, struct tcon_link, tl_rbnode);
52018c2ecf20Sopenharmony_ci		parent = *new;
52028c2ecf20Sopenharmony_ci
52038c2ecf20Sopenharmony_ci		if (uid_gt(tlink->tl_uid, new_tlink->tl_uid))
52048c2ecf20Sopenharmony_ci			new = &((*new)->rb_left);
52058c2ecf20Sopenharmony_ci		else
52068c2ecf20Sopenharmony_ci			new = &((*new)->rb_right);
52078c2ecf20Sopenharmony_ci	}
52088c2ecf20Sopenharmony_ci
52098c2ecf20Sopenharmony_ci	rb_link_node(&new_tlink->tl_rbnode, parent, new);
52108c2ecf20Sopenharmony_ci	rb_insert_color(&new_tlink->tl_rbnode, root);
52118c2ecf20Sopenharmony_ci}
52128c2ecf20Sopenharmony_ci
52138c2ecf20Sopenharmony_ci/*
52148c2ecf20Sopenharmony_ci * Find or construct an appropriate tcon given a cifs_sb and the fsuid of the
52158c2ecf20Sopenharmony_ci * current task.
52168c2ecf20Sopenharmony_ci *
52178c2ecf20Sopenharmony_ci * If the superblock doesn't refer to a multiuser mount, then just return
52188c2ecf20Sopenharmony_ci * the master tcon for the mount.
52198c2ecf20Sopenharmony_ci *
52208c2ecf20Sopenharmony_ci * First, search the rbtree for an existing tcon for this fsuid. If one
52218c2ecf20Sopenharmony_ci * exists, then check to see if it's pending construction. If it is then wait
52228c2ecf20Sopenharmony_ci * for construction to complete. Once it's no longer pending, check to see if
52238c2ecf20Sopenharmony_ci * it failed and either return an error or retry construction, depending on
52248c2ecf20Sopenharmony_ci * the timeout.
52258c2ecf20Sopenharmony_ci *
52268c2ecf20Sopenharmony_ci * If one doesn't exist then insert a new tcon_link struct into the tree and
52278c2ecf20Sopenharmony_ci * try to construct a new one.
52288c2ecf20Sopenharmony_ci */
52298c2ecf20Sopenharmony_cistruct tcon_link *
52308c2ecf20Sopenharmony_cicifs_sb_tlink(struct cifs_sb_info *cifs_sb)
52318c2ecf20Sopenharmony_ci{
52328c2ecf20Sopenharmony_ci	int ret;
52338c2ecf20Sopenharmony_ci	kuid_t fsuid = current_fsuid();
52348c2ecf20Sopenharmony_ci	struct tcon_link *tlink, *newtlink;
52358c2ecf20Sopenharmony_ci
52368c2ecf20Sopenharmony_ci	if (!(cifs_sb->mnt_cifs_flags & CIFS_MOUNT_MULTIUSER))
52378c2ecf20Sopenharmony_ci		return cifs_get_tlink(cifs_sb_master_tlink(cifs_sb));
52388c2ecf20Sopenharmony_ci
52398c2ecf20Sopenharmony_ci	spin_lock(&cifs_sb->tlink_tree_lock);
52408c2ecf20Sopenharmony_ci	tlink = tlink_rb_search(&cifs_sb->tlink_tree, fsuid);
52418c2ecf20Sopenharmony_ci	if (tlink)
52428c2ecf20Sopenharmony_ci		cifs_get_tlink(tlink);
52438c2ecf20Sopenharmony_ci	spin_unlock(&cifs_sb->tlink_tree_lock);
52448c2ecf20Sopenharmony_ci
52458c2ecf20Sopenharmony_ci	if (tlink == NULL) {
52468c2ecf20Sopenharmony_ci		newtlink = kzalloc(sizeof(*tlink), GFP_KERNEL);
52478c2ecf20Sopenharmony_ci		if (newtlink == NULL)
52488c2ecf20Sopenharmony_ci			return ERR_PTR(-ENOMEM);
52498c2ecf20Sopenharmony_ci		newtlink->tl_uid = fsuid;
52508c2ecf20Sopenharmony_ci		newtlink->tl_tcon = ERR_PTR(-EACCES);
52518c2ecf20Sopenharmony_ci		set_bit(TCON_LINK_PENDING, &newtlink->tl_flags);
52528c2ecf20Sopenharmony_ci		set_bit(TCON_LINK_IN_TREE, &newtlink->tl_flags);
52538c2ecf20Sopenharmony_ci		cifs_get_tlink(newtlink);
52548c2ecf20Sopenharmony_ci
52558c2ecf20Sopenharmony_ci		spin_lock(&cifs_sb->tlink_tree_lock);
52568c2ecf20Sopenharmony_ci		/* was one inserted after previous search? */
52578c2ecf20Sopenharmony_ci		tlink = tlink_rb_search(&cifs_sb->tlink_tree, fsuid);
52588c2ecf20Sopenharmony_ci		if (tlink) {
52598c2ecf20Sopenharmony_ci			cifs_get_tlink(tlink);
52608c2ecf20Sopenharmony_ci			spin_unlock(&cifs_sb->tlink_tree_lock);
52618c2ecf20Sopenharmony_ci			kfree(newtlink);
52628c2ecf20Sopenharmony_ci			goto wait_for_construction;
52638c2ecf20Sopenharmony_ci		}
52648c2ecf20Sopenharmony_ci		tlink = newtlink;
52658c2ecf20Sopenharmony_ci		tlink_rb_insert(&cifs_sb->tlink_tree, tlink);
52668c2ecf20Sopenharmony_ci		spin_unlock(&cifs_sb->tlink_tree_lock);
52678c2ecf20Sopenharmony_ci	} else {
52688c2ecf20Sopenharmony_ciwait_for_construction:
52698c2ecf20Sopenharmony_ci		ret = wait_on_bit(&tlink->tl_flags, TCON_LINK_PENDING,
52708c2ecf20Sopenharmony_ci				  TASK_INTERRUPTIBLE);
52718c2ecf20Sopenharmony_ci		if (ret) {
52728c2ecf20Sopenharmony_ci			cifs_put_tlink(tlink);
52738c2ecf20Sopenharmony_ci			return ERR_PTR(-ERESTARTSYS);
52748c2ecf20Sopenharmony_ci		}
52758c2ecf20Sopenharmony_ci
52768c2ecf20Sopenharmony_ci		/* if it's good, return it */
52778c2ecf20Sopenharmony_ci		if (!IS_ERR(tlink->tl_tcon))
52788c2ecf20Sopenharmony_ci			return tlink;
52798c2ecf20Sopenharmony_ci
52808c2ecf20Sopenharmony_ci		/* return error if we tried this already recently */
52818c2ecf20Sopenharmony_ci		if (time_before(jiffies, tlink->tl_time + TLINK_ERROR_EXPIRE)) {
52828c2ecf20Sopenharmony_ci			cifs_put_tlink(tlink);
52838c2ecf20Sopenharmony_ci			return ERR_PTR(-EACCES);
52848c2ecf20Sopenharmony_ci		}
52858c2ecf20Sopenharmony_ci
52868c2ecf20Sopenharmony_ci		if (test_and_set_bit(TCON_LINK_PENDING, &tlink->tl_flags))
52878c2ecf20Sopenharmony_ci			goto wait_for_construction;
52888c2ecf20Sopenharmony_ci	}
52898c2ecf20Sopenharmony_ci
52908c2ecf20Sopenharmony_ci	tlink->tl_tcon = cifs_construct_tcon(cifs_sb, fsuid);
52918c2ecf20Sopenharmony_ci	clear_bit(TCON_LINK_PENDING, &tlink->tl_flags);
52928c2ecf20Sopenharmony_ci	wake_up_bit(&tlink->tl_flags, TCON_LINK_PENDING);
52938c2ecf20Sopenharmony_ci
52948c2ecf20Sopenharmony_ci	if (IS_ERR(tlink->tl_tcon)) {
52958c2ecf20Sopenharmony_ci		cifs_put_tlink(tlink);
52968c2ecf20Sopenharmony_ci		return ERR_PTR(-EACCES);
52978c2ecf20Sopenharmony_ci	}
52988c2ecf20Sopenharmony_ci
52998c2ecf20Sopenharmony_ci	return tlink;
53008c2ecf20Sopenharmony_ci}
53018c2ecf20Sopenharmony_ci
53028c2ecf20Sopenharmony_ci/*
53038c2ecf20Sopenharmony_ci * periodic workqueue job that scans tcon_tree for a superblock and closes
53048c2ecf20Sopenharmony_ci * out tcons.
53058c2ecf20Sopenharmony_ci */
53068c2ecf20Sopenharmony_cistatic void
53078c2ecf20Sopenharmony_cicifs_prune_tlinks(struct work_struct *work)
53088c2ecf20Sopenharmony_ci{
53098c2ecf20Sopenharmony_ci	struct cifs_sb_info *cifs_sb = container_of(work, struct cifs_sb_info,
53108c2ecf20Sopenharmony_ci						    prune_tlinks.work);
53118c2ecf20Sopenharmony_ci	struct rb_root *root = &cifs_sb->tlink_tree;
53128c2ecf20Sopenharmony_ci	struct rb_node *node;
53138c2ecf20Sopenharmony_ci	struct rb_node *tmp;
53148c2ecf20Sopenharmony_ci	struct tcon_link *tlink;
53158c2ecf20Sopenharmony_ci
53168c2ecf20Sopenharmony_ci	/*
53178c2ecf20Sopenharmony_ci	 * Because we drop the spinlock in the loop in order to put the tlink
53188c2ecf20Sopenharmony_ci	 * it's not guarded against removal of links from the tree. The only
53198c2ecf20Sopenharmony_ci	 * places that remove entries from the tree are this function and
53208c2ecf20Sopenharmony_ci	 * umounts. Because this function is non-reentrant and is canceled
53218c2ecf20Sopenharmony_ci	 * before umount can proceed, this is safe.
53228c2ecf20Sopenharmony_ci	 */
53238c2ecf20Sopenharmony_ci	spin_lock(&cifs_sb->tlink_tree_lock);
53248c2ecf20Sopenharmony_ci	node = rb_first(root);
53258c2ecf20Sopenharmony_ci	while (node != NULL) {
53268c2ecf20Sopenharmony_ci		tmp = node;
53278c2ecf20Sopenharmony_ci		node = rb_next(tmp);
53288c2ecf20Sopenharmony_ci		tlink = rb_entry(tmp, struct tcon_link, tl_rbnode);
53298c2ecf20Sopenharmony_ci
53308c2ecf20Sopenharmony_ci		if (test_bit(TCON_LINK_MASTER, &tlink->tl_flags) ||
53318c2ecf20Sopenharmony_ci		    atomic_read(&tlink->tl_count) != 0 ||
53328c2ecf20Sopenharmony_ci		    time_after(tlink->tl_time + TLINK_IDLE_EXPIRE, jiffies))
53338c2ecf20Sopenharmony_ci			continue;
53348c2ecf20Sopenharmony_ci
53358c2ecf20Sopenharmony_ci		cifs_get_tlink(tlink);
53368c2ecf20Sopenharmony_ci		clear_bit(TCON_LINK_IN_TREE, &tlink->tl_flags);
53378c2ecf20Sopenharmony_ci		rb_erase(tmp, root);
53388c2ecf20Sopenharmony_ci
53398c2ecf20Sopenharmony_ci		spin_unlock(&cifs_sb->tlink_tree_lock);
53408c2ecf20Sopenharmony_ci		cifs_put_tlink(tlink);
53418c2ecf20Sopenharmony_ci		spin_lock(&cifs_sb->tlink_tree_lock);
53428c2ecf20Sopenharmony_ci	}
53438c2ecf20Sopenharmony_ci	spin_unlock(&cifs_sb->tlink_tree_lock);
53448c2ecf20Sopenharmony_ci
53458c2ecf20Sopenharmony_ci	queue_delayed_work(cifsiod_wq, &cifs_sb->prune_tlinks,
53468c2ecf20Sopenharmony_ci				TLINK_IDLE_EXPIRE);
53478c2ecf20Sopenharmony_ci}
53488c2ecf20Sopenharmony_ci
53498c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_DFS_UPCALL
53508c2ecf20Sopenharmony_ciint cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc)
53518c2ecf20Sopenharmony_ci{
53528c2ecf20Sopenharmony_ci	int rc;
53538c2ecf20Sopenharmony_ci	struct TCP_Server_Info *server = tcon->ses->server;
53548c2ecf20Sopenharmony_ci	const struct smb_version_operations *ops = server->ops;
53558c2ecf20Sopenharmony_ci	struct dfs_cache_tgt_list tl;
53568c2ecf20Sopenharmony_ci	struct dfs_cache_tgt_iterator *it = NULL;
53578c2ecf20Sopenharmony_ci	char *tree;
53588c2ecf20Sopenharmony_ci	const char *tcp_host;
53598c2ecf20Sopenharmony_ci	size_t tcp_host_len;
53608c2ecf20Sopenharmony_ci	const char *dfs_host;
53618c2ecf20Sopenharmony_ci	size_t dfs_host_len;
53628c2ecf20Sopenharmony_ci	char *share = NULL, *prefix = NULL;
53638c2ecf20Sopenharmony_ci	struct dfs_info3_param ref = {0};
53648c2ecf20Sopenharmony_ci	bool isroot;
53658c2ecf20Sopenharmony_ci
53668c2ecf20Sopenharmony_ci	tree = kzalloc(MAX_TREE_SIZE, GFP_KERNEL);
53678c2ecf20Sopenharmony_ci	if (!tree)
53688c2ecf20Sopenharmony_ci		return -ENOMEM;
53698c2ecf20Sopenharmony_ci
53708c2ecf20Sopenharmony_ci	/* If it is not dfs or there was no cached dfs referral, then reconnect to same share */
53718c2ecf20Sopenharmony_ci	if (!tcon->dfs_path || dfs_cache_noreq_find(tcon->dfs_path + 1, &ref, &tl)) {
53728c2ecf20Sopenharmony_ci		if (tcon->ipc) {
53738c2ecf20Sopenharmony_ci			scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname);
53748c2ecf20Sopenharmony_ci			rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc);
53758c2ecf20Sopenharmony_ci		} else {
53768c2ecf20Sopenharmony_ci			rc = ops->tree_connect(xid, tcon->ses, tcon->treeName, tcon, nlsc);
53778c2ecf20Sopenharmony_ci		}
53788c2ecf20Sopenharmony_ci		goto out;
53798c2ecf20Sopenharmony_ci	}
53808c2ecf20Sopenharmony_ci
53818c2ecf20Sopenharmony_ci	isroot = ref.server_type == DFS_TYPE_ROOT;
53828c2ecf20Sopenharmony_ci	free_dfs_info_param(&ref);
53838c2ecf20Sopenharmony_ci
53848c2ecf20Sopenharmony_ci	extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len);
53858c2ecf20Sopenharmony_ci
53868c2ecf20Sopenharmony_ci	for (it = dfs_cache_get_tgt_iterator(&tl); it; it = dfs_cache_get_next_tgt(&tl, it)) {
53878c2ecf20Sopenharmony_ci		bool target_match;
53888c2ecf20Sopenharmony_ci
53898c2ecf20Sopenharmony_ci		kfree(share);
53908c2ecf20Sopenharmony_ci		kfree(prefix);
53918c2ecf20Sopenharmony_ci		share = NULL;
53928c2ecf20Sopenharmony_ci		prefix = NULL;
53938c2ecf20Sopenharmony_ci
53948c2ecf20Sopenharmony_ci		rc = dfs_cache_get_tgt_share(tcon->dfs_path + 1, it, &share, &prefix);
53958c2ecf20Sopenharmony_ci		if (rc) {
53968c2ecf20Sopenharmony_ci			cifs_dbg(VFS, "%s: failed to parse target share %d\n",
53978c2ecf20Sopenharmony_ci				 __func__, rc);
53988c2ecf20Sopenharmony_ci			continue;
53998c2ecf20Sopenharmony_ci		}
54008c2ecf20Sopenharmony_ci
54018c2ecf20Sopenharmony_ci		extract_unc_hostname(share, &dfs_host, &dfs_host_len);
54028c2ecf20Sopenharmony_ci
54038c2ecf20Sopenharmony_ci		if (dfs_host_len != tcp_host_len
54048c2ecf20Sopenharmony_ci		    || strncasecmp(dfs_host, tcp_host, dfs_host_len) != 0) {
54058c2ecf20Sopenharmony_ci			cifs_dbg(FYI, "%s: %.*s doesn't match %.*s\n", __func__, (int)dfs_host_len,
54068c2ecf20Sopenharmony_ci				 dfs_host, (int)tcp_host_len, tcp_host);
54078c2ecf20Sopenharmony_ci
54088c2ecf20Sopenharmony_ci			rc = match_target_ip(server, dfs_host, dfs_host_len, &target_match);
54098c2ecf20Sopenharmony_ci			if (rc) {
54108c2ecf20Sopenharmony_ci				cifs_dbg(VFS, "%s: failed to match target ip: %d\n", __func__, rc);
54118c2ecf20Sopenharmony_ci				break;
54128c2ecf20Sopenharmony_ci			}
54138c2ecf20Sopenharmony_ci
54148c2ecf20Sopenharmony_ci			if (!target_match) {
54158c2ecf20Sopenharmony_ci				cifs_dbg(FYI, "%s: skipping target\n", __func__);
54168c2ecf20Sopenharmony_ci				continue;
54178c2ecf20Sopenharmony_ci			}
54188c2ecf20Sopenharmony_ci		}
54198c2ecf20Sopenharmony_ci
54208c2ecf20Sopenharmony_ci		if (tcon->ipc) {
54218c2ecf20Sopenharmony_ci			scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", share);
54228c2ecf20Sopenharmony_ci			rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc);
54238c2ecf20Sopenharmony_ci		} else {
54248c2ecf20Sopenharmony_ci			scnprintf(tree, MAX_TREE_SIZE, "\\%s", share);
54258c2ecf20Sopenharmony_ci			rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc);
54268c2ecf20Sopenharmony_ci			/* Only handle prefix paths of DFS link targets */
54278c2ecf20Sopenharmony_ci			if (!rc && !isroot) {
54288c2ecf20Sopenharmony_ci				rc = update_super_prepath(tcon, prefix);
54298c2ecf20Sopenharmony_ci				break;
54308c2ecf20Sopenharmony_ci			}
54318c2ecf20Sopenharmony_ci		}
54328c2ecf20Sopenharmony_ci		if (rc == -EREMOTE)
54338c2ecf20Sopenharmony_ci			break;
54348c2ecf20Sopenharmony_ci	}
54358c2ecf20Sopenharmony_ci
54368c2ecf20Sopenharmony_ci	kfree(share);
54378c2ecf20Sopenharmony_ci	kfree(prefix);
54388c2ecf20Sopenharmony_ci
54398c2ecf20Sopenharmony_ci	if (!rc) {
54408c2ecf20Sopenharmony_ci		if (it)
54418c2ecf20Sopenharmony_ci			rc = dfs_cache_noreq_update_tgthint(tcon->dfs_path + 1, it);
54428c2ecf20Sopenharmony_ci		else
54438c2ecf20Sopenharmony_ci			rc = -ENOENT;
54448c2ecf20Sopenharmony_ci	}
54458c2ecf20Sopenharmony_ci	dfs_cache_free_tgts(&tl);
54468c2ecf20Sopenharmony_ciout:
54478c2ecf20Sopenharmony_ci	kfree(tree);
54488c2ecf20Sopenharmony_ci	return rc;
54498c2ecf20Sopenharmony_ci}
54508c2ecf20Sopenharmony_ci#else
54518c2ecf20Sopenharmony_ciint cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc)
54528c2ecf20Sopenharmony_ci{
54538c2ecf20Sopenharmony_ci	const struct smb_version_operations *ops = tcon->ses->server->ops;
54548c2ecf20Sopenharmony_ci
54558c2ecf20Sopenharmony_ci	return ops->tree_connect(xid, tcon->ses, tcon->treeName, tcon, nlsc);
54568c2ecf20Sopenharmony_ci}
54578c2ecf20Sopenharmony_ci#endif
5458