Skip to content

Commit 44cb60b

Browse files
committed
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
2 parents ccbb22b + cdc3363 commit 44cb60b

File tree

8 files changed

+124
-20
lines changed

8 files changed

+124
-20
lines changed

fs/cifs/cifs_dfs_ref.c

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
176176
}
177177
}
178178

179-
rc = dns_resolve_server_name_to_ip(name, &srvIP);
179+
rc = dns_resolve_server_name_to_ip(name, &srvIP, NULL);
180180
if (rc < 0) {
181181
cifs_dbg(FYI, "%s: Failed to resolve server part of %s to IP: %d\n",
182182
__func__, name, rc);
@@ -211,6 +211,10 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
211211
else
212212
noff = tkn_e - (sb_mountdata + off) + 1;
213213

214+
if (strncasecmp(sb_mountdata + off, "cruid=", 6) == 0) {
215+
off += noff;
216+
continue;
217+
}
214218
if (strncasecmp(sb_mountdata + off, "unc=", 4) == 0) {
215219
off += noff;
216220
continue;

fs/cifs/cifsglob.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,9 @@
7575
#define SMB_ECHO_INTERVAL_MAX 600
7676
#define SMB_ECHO_INTERVAL_DEFAULT 60
7777

78+
/* dns resolution interval in seconds */
79+
#define SMB_DNS_RESOLVE_INTERVAL_DEFAULT 600
80+
7881
/* maximum number of PDUs in one compound */
7982
#define MAX_COMPOUND 5
8083

@@ -646,6 +649,7 @@ struct TCP_Server_Info {
646649
/* point to the SMBD connection if RDMA is used instead of socket */
647650
struct smbd_connection *smbd_conn;
648651
struct delayed_work echo; /* echo ping workqueue job */
652+
struct delayed_work resolve; /* dns resolution workqueue job */
649653
char *smallbuf; /* pointer to current "small" buffer */
650654
char *bigbuf; /* pointer to current "big" buffer */
651655
/* Total size of this PDU. Only valid from cifs_demultiplex_thread */
@@ -689,6 +693,9 @@ struct TCP_Server_Info {
689693
bool use_swn_dstaddr;
690694
struct sockaddr_storage swn_dstaddr;
691695
#endif
696+
#ifdef CONFIG_CIFS_DFS_UPCALL
697+
bool is_dfs_conn; /* if a dfs connection */
698+
#endif
692699
};
693700

694701
struct cifs_credits {

fs/cifs/connect.c

Lines changed: 99 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
7878
int rc;
7979
int len;
8080
char *unc, *ipaddr = NULL;
81+
time64_t expiry, now;
82+
unsigned long ttl = SMB_DNS_RESOLVE_INTERVAL_DEFAULT;
8183

8284
if (!server->hostname)
8385
return -EINVAL;
@@ -91,13 +93,13 @@ static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
9193
}
9294
scnprintf(unc, len, "\\\\%s", server->hostname);
9395

94-
rc = dns_resolve_server_name_to_ip(unc, &ipaddr);
96+
rc = dns_resolve_server_name_to_ip(unc, &ipaddr, &expiry);
9597
kfree(unc);
9698

9799
if (rc < 0) {
98100
cifs_dbg(FYI, "%s: failed to resolve server part of %s to IP: %d\n",
99101
__func__, server->hostname, rc);
100-
return rc;
102+
goto requeue_resolve;
101103
}
102104

103105
spin_lock(&cifs_tcp_ses_lock);
@@ -106,7 +108,45 @@ static int reconn_set_ipaddr_from_hostname(struct TCP_Server_Info *server)
106108
spin_unlock(&cifs_tcp_ses_lock);
107109
kfree(ipaddr);
108110

109-
return !rc ? -1 : 0;
111+
/* rc == 1 means success here */
112+
if (rc) {
113+
now = ktime_get_real_seconds();
114+
if (expiry && expiry > now)
115+
/*
116+
* To make sure we don't use the cached entry, retry 1s
117+
* after expiry.
118+
*/
119+
ttl = (expiry - now + 1);
120+
}
121+
rc = !rc ? -1 : 0;
122+
123+
requeue_resolve:
124+
cifs_dbg(FYI, "%s: next dns resolution scheduled for %lu seconds in the future\n",
125+
__func__, ttl);
126+
mod_delayed_work(cifsiod_wq, &server->resolve, (ttl * HZ));
127+
128+
return rc;
129+
}
130+
131+
132+
static void cifs_resolve_server(struct work_struct *work)
133+
{
134+
int rc;
135+
struct TCP_Server_Info *server = container_of(work,
136+
struct TCP_Server_Info, resolve.work);
137+
138+
mutex_lock(&server->srv_mutex);
139+
140+
/*
141+
* Resolve the hostname again to make sure that IP address is up-to-date.
142+
*/
143+
rc = reconn_set_ipaddr_from_hostname(server);
144+
if (rc) {
145+
cifs_dbg(FYI, "%s: failed to resolve hostname: %d\n",
146+
__func__, rc);
147+
}
148+
149+
mutex_unlock(&server->srv_mutex);
110150
}
111151

112152
#ifdef CONFIG_CIFS_DFS_UPCALL
@@ -680,6 +720,7 @@ static void clean_demultiplex_info(struct TCP_Server_Info *server)
680720
spin_unlock(&cifs_tcp_ses_lock);
681721

682722
cancel_delayed_work_sync(&server->echo);
723+
cancel_delayed_work_sync(&server->resolve);
683724

684725
spin_lock(&GlobalMid_Lock);
685726
server->tcpStatus = CifsExiting;
@@ -1227,6 +1268,16 @@ cifs_find_tcp_session(struct smb3_fs_context *ctx)
12271268

12281269
spin_lock(&cifs_tcp_ses_lock);
12291270
list_for_each_entry(server, &cifs_tcp_ses_list, tcp_ses_list) {
1271+
#ifdef CONFIG_CIFS_DFS_UPCALL
1272+
/*
1273+
* DFS failover implementation in cifs_reconnect() requires unique tcp sessions for
1274+
* DFS connections to do failover properly, so avoid sharing them with regular
1275+
* shares or even links that may connect to same server but having completely
1276+
* different failover targets.
1277+
*/
1278+
if (server->is_dfs_conn)
1279+
continue;
1280+
#endif
12301281
/*
12311282
* Skip ses channels since they're only handled in lower layers
12321283
* (e.g. cifs_send_recv).
@@ -1254,12 +1305,16 @@ cifs_put_tcp_session(struct TCP_Server_Info *server, int from_reconnect)
12541305
return;
12551306
}
12561307

1308+
/* srv_count can never go negative */
1309+
WARN_ON(server->srv_count < 0);
1310+
12571311
put_net(cifs_net_ns(server));
12581312

12591313
list_del_init(&server->tcp_ses_list);
12601314
spin_unlock(&cifs_tcp_ses_lock);
12611315

12621316
cancel_delayed_work_sync(&server->echo);
1317+
cancel_delayed_work_sync(&server->resolve);
12631318

12641319
if (from_reconnect)
12651320
/*
@@ -1342,6 +1397,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx)
13421397
INIT_LIST_HEAD(&tcp_ses->tcp_ses_list);
13431398
INIT_LIST_HEAD(&tcp_ses->smb_ses_list);
13441399
INIT_DELAYED_WORK(&tcp_ses->echo, cifs_echo_request);
1400+
INIT_DELAYED_WORK(&tcp_ses->resolve, cifs_resolve_server);
13451401
INIT_DELAYED_WORK(&tcp_ses->reconnect, smb2_reconnect_server);
13461402
mutex_init(&tcp_ses->reconnect_mutex);
13471403
memcpy(&tcp_ses->srcaddr, &ctx->srcaddr,
@@ -1427,6 +1483,12 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx)
14271483
/* queue echo request delayed work */
14281484
queue_delayed_work(cifsiod_wq, &tcp_ses->echo, tcp_ses->echo_interval);
14291485

1486+
/* queue dns resolution delayed work */
1487+
cifs_dbg(FYI, "%s: next dns resolution scheduled for %d seconds in the future\n",
1488+
__func__, SMB_DNS_RESOLVE_INTERVAL_DEFAULT);
1489+
1490+
queue_delayed_work(cifsiod_wq, &tcp_ses->resolve, (SMB_DNS_RESOLVE_INTERVAL_DEFAULT * HZ));
1491+
14301492
return tcp_ses;
14311493

14321494
out_err_crypto_release:
@@ -1605,6 +1667,9 @@ void cifs_put_smb_ses(struct cifs_ses *ses)
16051667
}
16061668
spin_unlock(&cifs_tcp_ses_lock);
16071669

1670+
/* ses_count can never go negative */
1671+
WARN_ON(ses->ses_count < 0);
1672+
16081673
spin_lock(&GlobalMid_Lock);
16091674
if (ses->status == CifsGood)
16101675
ses->status = CifsExiting;
@@ -1972,6 +2037,9 @@ cifs_put_tcon(struct cifs_tcon *tcon)
19722037
return;
19732038
}
19742039

2040+
/* tc_count can never go negative */
2041+
WARN_ON(tcon->tc_count < 0);
2042+
19752043
if (tcon->use_witness) {
19762044
int rc;
19772045

@@ -2910,6 +2978,23 @@ static int mount_setup_tlink(struct cifs_sb_info *cifs_sb, struct cifs_ses *ses,
29102978
}
29112979

29122980
#ifdef CONFIG_CIFS_DFS_UPCALL
2981+
static int mount_get_dfs_conns(struct smb3_fs_context *ctx, struct cifs_sb_info *cifs_sb,
2982+
unsigned int *xid, struct TCP_Server_Info **nserver,
2983+
struct cifs_ses **nses, struct cifs_tcon **ntcon)
2984+
{
2985+
int rc;
2986+
2987+
ctx->nosharesock = true;
2988+
rc = mount_get_conns(ctx, cifs_sb, xid, nserver, nses, ntcon);
2989+
if (*nserver) {
2990+
cifs_dbg(FYI, "%s: marking tcp session as a dfs connection\n", __func__);
2991+
spin_lock(&cifs_tcp_ses_lock);
2992+
(*nserver)->is_dfs_conn = true;
2993+
spin_unlock(&cifs_tcp_ses_lock);
2994+
}
2995+
return rc;
2996+
}
2997+
29132998
/*
29142999
* cifs_build_path_to_root returns full path to root when we do not have an
29153000
* existing connection (tcon)
@@ -3105,7 +3190,7 @@ static int do_dfs_failover(const char *path, const char *full_path, struct cifs_
31053190
tmp_ctx.prepath);
31063191

31073192
mount_put_conns(cifs_sb, *xid, *server, *ses, *tcon);
3108-
rc = mount_get_conns(&tmp_ctx, cifs_sb, xid, server, ses, tcon);
3193+
rc = mount_get_dfs_conns(&tmp_ctx, cifs_sb, xid, server, ses, tcon);
31093194
if (!rc || (*server && *ses)) {
31103195
/*
31113196
* 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)
34043489
goto error;
34053490
}
34063491

3407-
ctx->nosharesock = true;
3492+
mount_put_conns(cifs_sb, xid, server, ses, tcon);
3493+
/*
3494+
* Ignore error check here because we may failover to other targets from cached a
3495+
* referral.
3496+
*/
3497+
(void)mount_get_dfs_conns(ctx, cifs_sb, &xid, &server, &ses, &tcon);
34083498

34093499
/* Get path of DFS root */
34103500
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)
34333523
/* Connect to new DFS target only if we were redirected */
34343524
if (oldmnt != cifs_sb->ctx->mount_options) {
34353525
mount_put_conns(cifs_sb, xid, server, ses, tcon);
3436-
rc = mount_get_conns(ctx, cifs_sb, &xid, &server, &ses, &tcon);
3526+
rc = mount_get_dfs_conns(ctx, cifs_sb, &xid, &server, &ses, &tcon);
34373527
}
34383528
if (rc && !server && !ses) {
34393529
/* 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)
34593549
rc = -ELOOP;
34603550
} while (rc == -EREMOTE);
34613551

3462-
if (rc || !tcon)
3552+
if (rc || !tcon || !ses)
34633553
goto error;
34643554

34653555
kfree(ref_path);
@@ -4095,7 +4185,8 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
40954185
if (!tree)
40964186
return -ENOMEM;
40974187

4098-
if (!tcon->dfs_path) {
4188+
/* If it is not dfs or there was no cached dfs referral, then reconnect to same share */
4189+
if (!tcon->dfs_path || dfs_cache_noreq_find(tcon->dfs_path + 1, &ref, &tl)) {
40994190
if (tcon->ipc) {
41004191
scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname);
41014192
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
41054196
goto out;
41064197
}
41074198

4108-
rc = dfs_cache_noreq_find(tcon->dfs_path + 1, &ref, &tl);
4109-
if (rc)
4110-
goto out;
41114199
isroot = ref.server_type == DFS_TYPE_ROOT;
41124200
free_dfs_info_param(&ref);
41134201

fs/cifs/dns_resolve.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,15 @@
2424
* dns_resolve_server_name_to_ip - Resolve UNC server name to ip address.
2525
* @unc: UNC path specifying the server (with '/' as delimiter)
2626
* @ip_addr: Where to return the IP address.
27+
* @expiry: Where to return the expiry time for the dns record.
2728
*
2829
* The IP address will be returned in string form, and the caller is
2930
* responsible for freeing it.
3031
*
3132
* Returns length of result on success, -ve on error.
3233
*/
3334
int
34-
dns_resolve_server_name_to_ip(const char *unc, char **ip_addr)
35+
dns_resolve_server_name_to_ip(const char *unc, char **ip_addr, time64_t *expiry)
3536
{
3637
struct sockaddr_storage ss;
3738
const char *hostname, *sep;
@@ -66,13 +67,14 @@ dns_resolve_server_name_to_ip(const char *unc, char **ip_addr)
6667

6768
/* Perform the upcall */
6869
rc = dns_query(current->nsproxy->net_ns, NULL, hostname, len,
69-
NULL, ip_addr, NULL, false);
70+
NULL, ip_addr, expiry, false);
7071
if (rc < 0)
7172
cifs_dbg(FYI, "%s: unable to resolve: %*.*s\n",
7273
__func__, len, len, hostname);
7374
else
74-
cifs_dbg(FYI, "%s: resolved: %*.*s to %s\n",
75-
__func__, len, len, hostname, *ip_addr);
75+
cifs_dbg(FYI, "%s: resolved: %*.*s to %s expiry %llu\n",
76+
__func__, len, len, hostname, *ip_addr,
77+
expiry ? (*expiry) : 0);
7678
return rc;
7779

7880
name_is_IP_address:

fs/cifs/dns_resolve.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
#define _DNS_RESOLVE_H
1313

1414
#ifdef __KERNEL__
15-
extern int dns_resolve_server_name_to_ip(const char *unc, char **ip_addr);
15+
extern int dns_resolve_server_name_to_ip(const char *unc, char **ip_addr, time64_t *expiry);
1616
#endif /* KERNEL */
1717

1818
#endif /* _DNS_RESOLVE_H */

fs/cifs/misc.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1187,7 +1187,7 @@ int match_target_ip(struct TCP_Server_Info *server,
11871187

11881188
cifs_dbg(FYI, "%s: target name: %s\n", __func__, target + 2);
11891189

1190-
rc = dns_resolve_server_name_to_ip(target, &tip);
1190+
rc = dns_resolve_server_name_to_ip(target, &tip, NULL);
11911191
if (rc < 0)
11921192
goto out;
11931193

fs/cifs/smb2ops.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -557,8 +557,8 @@ parse_server_interfaces(struct network_interface_info_ioctl_rsp *buf,
557557
p = buf;
558558
while (bytes_left >= sizeof(*p)) {
559559
info->speed = le64_to_cpu(p->LinkSpeed);
560-
info->rdma_capable = le32_to_cpu(p->Capability & RDMA_CAPABLE);
561-
info->rss_capable = le32_to_cpu(p->Capability & RSS_CAPABLE);
560+
info->rdma_capable = le32_to_cpu(p->Capability & RDMA_CAPABLE) ? 1 : 0;
561+
info->rss_capable = le32_to_cpu(p->Capability & RSS_CAPABLE) ? 1 : 0;
562562

563563
cifs_dbg(FYI, "%s: adding iface %zu\n", __func__, *iface_count);
564564
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,
29102910
/* ipc tcons are not refcounted */
29112911
spin_lock(&cifs_tcp_ses_lock);
29122912
tcon->tc_count--;
2913+
/* tc_count can never go negative */
2914+
WARN_ON(tcon->tc_count < 0);
29132915
spin_unlock(&cifs_tcp_ses_lock);
29142916
}
29152917
kfree(utf16_path);

fs/cifs/smb2pdu.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,7 @@ struct smb2_compression_capabilities_context {
394394
__u16 Padding;
395395
__u32 Flags;
396396
__le16 CompressionAlgorithms[3];
397+
__u16 Pad; /* Some servers require pad to DataLen multiple of 8 */
397398
/* Check if pad needed */
398399
} __packed;
399400

0 commit comments

Comments
 (0)