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