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