mirror of
https://github.com/edk2-porting/linux-next.git
synced 2025-01-24 06:35:44 +08:00
8 cifs/smb3 fixes including 3 for stable
-----BEGIN PGP SIGNATURE----- iQGzBAABCgAdFiEE6fsu8pdIjtWE/DpLiiy9cAdyT1EFAmDxrnAACgkQiiy9cAdy T1HlzQv/eGdv+jV5z6iMtrtcoOFOdcOvnj2ZUooGcZ5hETuoNDK5cKSsAOeAYACe vWxhBpRSgCDDJS6TyV0bJyQ154ygoH8ycze/PYNgEhVVdEbJ9lN0/7w+oNyvCy8l e8QWUriW+3kcF1fppT+5pdK471Tw3pKDqOwiivWq4Z0ca3Iod2Fo3RcsqX/gFhJu bRl9dFNQqmW4AMmlU18n0qDstBB5/v4Ak4040+nACEVVInBMZadj13wNcj26ek16 D2l/+3MN1eY7ewLBSNh367/ufxlDLbHwsb6W9k21iilxo/cdMYqHGgLEahTwLDXd 6QWukYlsJFHo71l+WswW8Zz3z+VVc6xVb7HxCNtPGoc+ksthqvvmq9wfw+ONYQ0w 0h+r/7THw5HLQwAwLrTcHEvb9ECpOExItPTsfSR71logRORW4xe1snfoj7xRfCSE BEWsVDOo/SagrTEMDpmSaNY+ePcUiPaeNM0GRbiB7BBnwKQDenswrCa7QS97YItc piX2ile0 =aa4P -----END PGP SIGNATURE----- Merge tag '5.14-rc1-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6 Pull cifs fixes from Steve French: "Eight cifs/smb3 fixes, including three for stable. Three are DFS related fixes, and two to fix problems pointed out by static checkers" * tag '5.14-rc1-smb3-fixes' of git://git.samba.org/sfrench/cifs-2.6: cifs: do not share tcp sessions of dfs connections SMB3.1.1: fix mount failure to some servers when compression enabled cifs: added WARN_ON for all the count decrements cifs: fix missing null session check in mount cifs: handle reconnect of tcon when there is no cached dfs referral cifs: fix the out of range assignment to bit fields in parse_server_interfaces cifs: Do not use the original cruid when following DFS links for multiuser mounts cifs: use the expiry output of dns_query to schedule next resolution
This commit is contained in:
commit
44cb60b425
@ -176,7 +176,7 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
|
||||
}
|
||||
}
|
||||
|
||||
rc = dns_resolve_server_name_to_ip(name, &srvIP);
|
||||
rc = dns_resolve_server_name_to_ip(name, &srvIP, NULL);
|
||||
if (rc < 0) {
|
||||
cifs_dbg(FYI, "%s: Failed to resolve server part of %s to IP: %d\n",
|
||||
__func__, name, rc);
|
||||
@ -211,6 +211,10 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
|
||||
else
|
||||
noff = tkn_e - (sb_mountdata + off) + 1;
|
||||
|
||||
if (strncasecmp(sb_mountdata + off, "cruid=", 6) == 0) {
|
||||
off += noff;
|
||||
continue;
|
||||
}
|
||||
if (strncasecmp(sb_mountdata + off, "unc=", 4) == 0) {
|
||||
off += noff;
|
||||
continue;
|
||||
|
@ -75,6 +75,9 @@
|
||||
#define SMB_ECHO_INTERVAL_MAX 600
|
||||
#define SMB_ECHO_INTERVAL_DEFAULT 60
|
||||
|
||||
/* dns resolution interval in seconds */
|
||||
#define SMB_DNS_RESOLVE_INTERVAL_DEFAULT 600
|
||||
|
||||
/* maximum number of PDUs in one compound */
|
||||
#define MAX_COMPOUND 5
|
||||
|
||||
@ -646,6 +649,7 @@ struct TCP_Server_Info {
|
||||
/* point to the SMBD connection if RDMA is used instead of socket */
|
||||
struct smbd_connection *smbd_conn;
|
||||
struct delayed_work echo; /* echo ping workqueue job */
|
||||
struct delayed_work resolve; /* dns resolution workqueue job */
|
||||
char *smallbuf; /* pointer to current "small" buffer */
|
||||
char *bigbuf; /* pointer to current "big" buffer */
|
||||
/* Total size of this PDU. Only valid from cifs_demultiplex_thread */
|
||||
@ -689,6 +693,9 @@ struct TCP_Server_Info {
|
||||
bool use_swn_dstaddr;
|
||||
struct sockaddr_storage swn_dstaddr;
|
||||
#endif
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
bool is_dfs_conn; /* if a dfs connection */
|
||||
#endif
|
||||
};
|
||||
|
||||
struct cifs_credits {
|
||||
|
@ -78,6 +78,8 @@ static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
|
||||
int rc;
|
||||
int len;
|
||||
char *unc, *ipaddr = NULL;
|
||||
time64_t expiry, now;
|
||||
unsigned long ttl = SMB_DNS_RESOLVE_INTERVAL_DEFAULT;
|
||||
|
||||
if (!server->hostname)
|
||||
return -EINVAL;
|
||||
@ -91,13 +93,13 @@ static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
|
||||
}
|
||||
scnprintf(unc, len, "\\\\%s", server->hostname);
|
||||
|
||||
rc = dns_resolve_server_name_to_ip(unc, &ipaddr);
|
||||
rc = dns_resolve_server_name_to_ip(unc, &ipaddr, &expiry);
|
||||
kfree(unc);
|
||||
|
||||
if (rc < 0) {
|
||||
cifs_dbg(FYI, "%s: failed to resolve server part of %s to IP: %d\n",
|
||||
__func__, server->hostname, rc);
|
||||
return rc;
|
||||
goto requeue_resolve;
|
||||
}
|
||||
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
@ -106,7 +108,45 @@ static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
kfree(ipaddr);
|
||||
|
||||
return !rc ? -1 : 0;
|
||||
/* rc == 1 means success here */
|
||||
if (rc) {
|
||||
now = ktime_get_real_seconds();
|
||||
if (expiry && expiry > now)
|
||||
/*
|
||||
* To make sure we don't use the cached entry, retry 1s
|
||||
* after expiry.
|
||||
*/
|
||||
ttl = (expiry - now + 1);
|
||||
}
|
||||
rc = !rc ? -1 : 0;
|
||||
|
||||
requeue_resolve:
|
||||
cifs_dbg(FYI, "%s: next dns resolution scheduled for %lu seconds in the future\n",
|
||||
__func__, ttl);
|
||||
mod_delayed_work(cifsiod_wq, &server->resolve, (ttl * HZ));
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static void cifs_resolve_server(struct work_struct *work)
|
||||
{
|
||||
int rc;
|
||||
struct TCP_Server_Info *server = container_of(work,
|
||||
struct TCP_Server_Info, resolve.work);
|
||||
|
||||
mutex_lock(&server->srv_mutex);
|
||||
|
||||
/*
|
||||
* Resolve the hostname again to make sure that IP address is up-to-date.
|
||||
*/
|
||||
rc = reconn_set_ipaddr_from_hostname(server);
|
||||
if (rc) {
|
||||
cifs_dbg(FYI, "%s: failed to resolve hostname: %d\n",
|
||||
__func__, rc);
|
||||
}
|
||||
|
||||
mutex_unlock(&server->srv_mutex);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
@ -680,6 +720,7 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server)
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
cancel_delayed_work_sync(&server->echo);
|
||||
cancel_delayed_work_sync(&server->resolve);
|
||||
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
server->tcpStatus = CifsExiting;
|
||||
@ -1227,6 +1268,16 @@ cifs_find_tcp_session(struct smb3_fs_context *ctx)
|
||||
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
/*
|
||||
* DFS failover implementation in cifs_reconnect() requires unique tcp sessions for
|
||||
* DFS connections to do failover properly, so avoid sharing them with regular
|
||||
* shares or even links that may connect to same server but having completely
|
||||
* different failover targets.
|
||||
*/
|
||||
if (server->is_dfs_conn)
|
||||
continue;
|
||||
#endif
|
||||
/*
|
||||
* Skip ses channels since they're only handled in lower layers
|
||||
* (e.g. cifs_send_recv).
|
||||
@ -1254,12 +1305,16 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
|
||||
return;
|
||||
}
|
||||
|
||||
/* srv_count can never go negative */
|
||||
WARN_ON(server->srv_count < 0);
|
||||
|
||||
put_net(cifs_net_ns(server));
|
||||
|
||||
list_del_init(&server->tcp_ses_list);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
cancel_delayed_work_sync(&server->echo);
|
||||
cancel_delayed_work_sync(&server->resolve);
|
||||
|
||||
if (from_reconnect)
|
||||
/*
|
||||
@ -1342,6 +1397,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx)
|
||||
INIT_LIST_HEAD(&tcp_ses->tcp_ses_list);
|
||||
INIT_LIST_HEAD(&tcp_ses->smb_ses_list);
|
||||
INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request);
|
||||
INIT_DELAYED_WORK(&tcp_ses->resolve, cifs_resolve_server);
|
||||
INIT_DELAYED_WORK(&tcp_ses->reconnect, smb2_reconnect_server);
|
||||
mutex_init(&tcp_ses->reconnect_mutex);
|
||||
memcpy(&tcp_ses->srcaddr, &ctx->srcaddr,
|
||||
@ -1427,6 +1483,12 @@ smbd_connected:
|
||||
/* queue echo request delayed work */
|
||||
queue_delayed_work(cifsiod_wq, &tcp_ses->echo, tcp_ses->echo_interval);
|
||||
|
||||
/* queue dns resolution delayed work */
|
||||
cifs_dbg(FYI, "%s: next dns resolution scheduled for %d seconds in the future\n",
|
||||
__func__, SMB_DNS_RESOLVE_INTERVAL_DEFAULT);
|
||||
|
||||
queue_delayed_work(cifsiod_wq, &tcp_ses->resolve, (SMB_DNS_RESOLVE_INTERVAL_DEFAULT * HZ));
|
||||
|
||||
return tcp_ses;
|
||||
|
||||
out_err_crypto_release:
|
||||
@ -1605,6 +1667,9 @@ void cifs_put_smb_ses(struct cifs_ses *ses)
|
||||
}
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
|
||||
/* ses_count can never go negative */
|
||||
WARN_ON(ses->ses_count < 0);
|
||||
|
||||
spin_lock(&GlobalMid_Lock);
|
||||
if (ses->status == CifsGood)
|
||||
ses->status = CifsExiting;
|
||||
@ -1972,6 +2037,9 @@ cifs_put_tcon(struct cifs_tcon *tcon)
|
||||
return;
|
||||
}
|
||||
|
||||
/* tc_count can never go negative */
|
||||
WARN_ON(tcon->tc_count < 0);
|
||||
|
||||
if (tcon->use_witness) {
|
||||
int rc;
|
||||
|
||||
@ -2910,6 +2978,23 @@ static int mount_setup_tlink(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
|
||||
}
|
||||
|
||||
#ifdef CONFIG_CIFS_DFS_UPCALL
|
||||
static int mount_get_dfs_conns(struct smb3_fs_context *ctx, struct cifs_sb_info *cifs_sb,
|
||||
unsigned int *xid, struct TCP_Server_Info **nserver,
|
||||
struct cifs_ses **nses, struct cifs_tcon **ntcon)
|
||||
{
|
||||
int rc;
|
||||
|
||||
ctx->nosharesock = true;
|
||||
rc = mount_get_conns(ctx, cifs_sb, xid, nserver, nses, ntcon);
|
||||
if (*nserver) {
|
||||
cifs_dbg(FYI, "%s: marking tcp session as a dfs connection\n", __func__);
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
(*nserver)->is_dfs_conn = true;
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* cifs_build_path_to_root returns full path to root when we do not have an
|
||||
* existing connection (tcon)
|
||||
@ -3105,7 +3190,7 @@ static int do_dfs_failover(const char *path, const char *full_path, struct cifs_
|
||||
tmp_ctx.prepath);
|
||||
|
||||
mount_put_conns(cifs_sb, *xid, *server, *ses, *tcon);
|
||||
rc = mount_get_conns(&tmp_ctx, cifs_sb, xid, server, ses, tcon);
|
||||
rc = mount_get_dfs_conns(&tmp_ctx, cifs_sb, xid, server, ses, tcon);
|
||||
if (!rc || (*server && *ses)) {
|
||||
/*
|
||||
* We were able to connect to new target server. Update current context with
|
||||
@ -3404,7 +3489,12 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
|
||||
goto error;
|
||||
}
|
||||
|
||||
ctx->nosharesock = true;
|
||||
mount_put_conns(cifs_sb, xid, server, ses, tcon);
|
||||
/*
|
||||
* Ignore error check here because we may failover to other targets from cached a
|
||||
* referral.
|
||||
*/
|
||||
(void)mount_get_dfs_conns(ctx, cifs_sb, &xid, &server, &ses, &tcon);
|
||||
|
||||
/* Get path of DFS root */
|
||||
ref_path = build_unc_path_to_root(ctx, cifs_sb, false);
|
||||
@ -3433,7 +3523,7 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
|
||||
/* Connect to new DFS target only if we were redirected */
|
||||
if (oldmnt != cifs_sb->ctx->mount_options) {
|
||||
mount_put_conns(cifs_sb, xid, server, ses, tcon);
|
||||
rc = mount_get_conns(ctx, cifs_sb, &xid, &server, &ses, &tcon);
|
||||
rc = mount_get_dfs_conns(ctx, cifs_sb, &xid, &server, &ses, &tcon);
|
||||
}
|
||||
if (rc && !server && !ses) {
|
||||
/* Failed to connect. Try to connect to other targets in the referral. */
|
||||
@ -3459,7 +3549,7 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
|
||||
rc = -ELOOP;
|
||||
} while (rc == -EREMOTE);
|
||||
|
||||
if (rc || !tcon)
|
||||
if (rc || !tcon || !ses)
|
||||
goto error;
|
||||
|
||||
kfree(ref_path);
|
||||
@ -4095,7 +4185,8 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
|
||||
if (!tree)
|
||||
return -ENOMEM;
|
||||
|
||||
if (!tcon->dfs_path) {
|
||||
/* If it is not dfs or there was no cached dfs referral, then reconnect to same share */
|
||||
if (!tcon->dfs_path || dfs_cache_noreq_find(tcon->dfs_path + 1, &ref, &tl)) {
|
||||
if (tcon->ipc) {
|
||||
scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname);
|
||||
rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc);
|
||||
@ -4105,9 +4196,6 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = dfs_cache_noreq_find(tcon->dfs_path + 1, &ref, &tl);
|
||||
if (rc)
|
||||
goto out;
|
||||
isroot = ref.server_type == DFS_TYPE_ROOT;
|
||||
free_dfs_info_param(&ref);
|
||||
|
||||
|
@ -24,6 +24,7 @@
|
||||
* dns_resolve_server_name_to_ip - Resolve UNC server name to ip address.
|
||||
* @unc: UNC path specifying the server (with '/' as delimiter)
|
||||
* @ip_addr: Where to return the IP address.
|
||||
* @expiry: Where to return the expiry time for the dns record.
|
||||
*
|
||||
* The IP address will be returned in string form, and the caller is
|
||||
* responsible for freeing it.
|
||||
@ -31,7 +32,7 @@
|
||||
* Returns length of result on success, -ve on error.
|
||||
*/
|
||||
int
|
||||
dns_resolve_server_name_to_ip(const char *unc, char **ip_addr)
|
||||
dns_resolve_server_name_to_ip(const char *unc, char **ip_addr, time64_t *expiry)
|
||||
{
|
||||
struct sockaddr_storage ss;
|
||||
const char *hostname, *sep;
|
||||
@ -66,13 +67,14 @@ dns_resolve_server_name_to_ip(const char *unc, char **ip_addr)
|
||||
|
||||
/* Perform the upcall */
|
||||
rc = dns_query(current->nsproxy->net_ns, NULL, hostname, len,
|
||||
NULL, ip_addr, NULL, false);
|
||||
NULL, ip_addr, expiry, false);
|
||||
if (rc < 0)
|
||||
cifs_dbg(FYI, "%s: unable to resolve: %*.*s\n",
|
||||
__func__, len, len, hostname);
|
||||
else
|
||||
cifs_dbg(FYI, "%s: resolved: %*.*s to %s\n",
|
||||
__func__, len, len, hostname, *ip_addr);
|
||||
cifs_dbg(FYI, "%s: resolved: %*.*s to %s expiry %llu\n",
|
||||
__func__, len, len, hostname, *ip_addr,
|
||||
expiry ? (*expiry) : 0);
|
||||
return rc;
|
||||
|
||||
name_is_IP_address:
|
||||
|
@ -12,7 +12,7 @@
|
||||
#define _DNS_RESOLVE_H
|
||||
|
||||
#ifdef __KERNEL__
|
||||
extern int dns_resolve_server_name_to_ip(const char *unc, char **ip_addr);
|
||||
extern int dns_resolve_server_name_to_ip(const char *unc, char **ip_addr, time64_t *expiry);
|
||||
#endif /* KERNEL */
|
||||
|
||||
#endif /* _DNS_RESOLVE_H */
|
||||
|
@ -1187,7 +1187,7 @@ int match_target_ip(struct TCP_Server_Info *server,
|
||||
|
||||
cifs_dbg(FYI, "%s: target name: %s\n", __func__, target + 2);
|
||||
|
||||
rc = dns_resolve_server_name_to_ip(target, &tip);
|
||||
rc = dns_resolve_server_name_to_ip(target, &tip, NULL);
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
|
||||
|
@ -557,8 +557,8 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
|
||||
p = buf;
|
||||
while (bytes_left >= sizeof(*p)) {
|
||||
info->speed = le64_to_cpu(p->LinkSpeed);
|
||||
info->rdma_capable = le32_to_cpu(p->Capability & RDMA_CAPABLE);
|
||||
info->rss_capable = le32_to_cpu(p->Capability & RSS_CAPABLE);
|
||||
info->rdma_capable = le32_to_cpu(p->Capability & RDMA_CAPABLE) ? 1 : 0;
|
||||
info->rss_capable = le32_to_cpu(p->Capability & RSS_CAPABLE) ? 1 : 0;
|
||||
|
||||
cifs_dbg(FYI, "%s: adding iface %zu\n", __func__, *iface_count);
|
||||
cifs_dbg(FYI, "%s: speed %zu bps\n", __func__, info->speed);
|
||||
@ -2910,6 +2910,8 @@ smb2_get_dfs_refer(const unsigned int xid, struct cifs_ses *ses,
|
||||
/* ipc tcons are not refcounted */
|
||||
spin_lock(&cifs_tcp_ses_lock);
|
||||
tcon->tc_count--;
|
||||
/* tc_count can never go negative */
|
||||
WARN_ON(tcon->tc_count < 0);
|
||||
spin_unlock(&cifs_tcp_ses_lock);
|
||||
}
|
||||
kfree(utf16_path);
|
||||
|
@ -394,6 +394,7 @@ struct smb2_compression_capabilities_context {
|
||||
__u16 Padding;
|
||||
__u32 Flags;
|
||||
__le16 CompressionAlgorithms[3];
|
||||
__u16 Pad; /* Some servers require pad to DataLen multiple of 8 */
|
||||
/* Check if pad needed */
|
||||
} __packed;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user