Skip to content

Commit fac56c4

Browse files
pcacjrsmfrench
authored andcommitted
smb: client: handle lack of IPC in dfs_cache_refresh()
In very rare cases, DFS mounts could end up with SMB sessions without any IPC connections. These mounts are only possible when having unexpired cached DFS referrals, hence not requiring any IPC connections during the mount process. Try to establish those missing IPC connections when refreshing DFS referrals. If the server is still rejecting it, then simply ignore and leave expired cached DFS referral for any potential DFS failovers. Reported-by: Jay Shin <[email protected]> Signed-off-by: Paulo Alcantara (Red Hat) <[email protected]> Cc: David Howells <[email protected]> Cc: [email protected] Signed-off-by: Steve French <[email protected]>
1 parent 5c76f99 commit fac56c4

File tree

3 files changed

+66
-29
lines changed

3 files changed

+66
-29
lines changed

fs/smb/client/cifsproto.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,8 @@ extern int E_md4hash(const unsigned char *passwd, unsigned char *p16,
616616
extern struct TCP_Server_Info *
617617
cifs_find_tcp_session(struct smb3_fs_context *ctx);
618618

619+
struct cifs_tcon *cifs_setup_ipc(struct cifs_ses *ses, bool seal);
620+
619621
void __cifs_put_smb_ses(struct cifs_ses *ses);
620622

621623
extern struct cifs_ses *

fs/smb/client/connect.c

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2015,39 +2015,31 @@ static int match_session(struct cifs_ses *ses,
20152015
/**
20162016
* cifs_setup_ipc - helper to setup the IPC tcon for the session
20172017
* @ses: smb session to issue the request on
2018-
* @ctx: the superblock configuration context to use for building the
2019-
* new tree connection for the IPC (interprocess communication RPC)
2018+
* @seal: if encryption is requested
20202019
*
20212020
* A new IPC connection is made and stored in the session
20222021
* tcon_ipc. The IPC tcon has the same lifetime as the session.
20232022
*/
2024-
static int
2025-
cifs_setup_ipc(struct cifs_ses *ses, struct smb3_fs_context *ctx)
2023+
struct cifs_tcon *cifs_setup_ipc(struct cifs_ses *ses, bool seal)
20262024
{
20272025
int rc = 0, xid;
20282026
struct cifs_tcon *tcon;
20292027
char unc[SERVER_NAME_LENGTH + sizeof("//x/IPC$")] = {0};
2030-
bool seal = false;
20312028
struct TCP_Server_Info *server = ses->server;
20322029

20332030
/*
20342031
* If the mount request that resulted in the creation of the
20352032
* session requires encryption, force IPC to be encrypted too.
20362033
*/
2037-
if (ctx->seal) {
2038-
if (server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)
2039-
seal = true;
2040-
else {
2041-
cifs_server_dbg(VFS,
2042-
"IPC: server doesn't support encryption\n");
2043-
return -EOPNOTSUPP;
2044-
}
2034+
if (seal && !(server->capabilities & SMB2_GLOBAL_CAP_ENCRYPTION)) {
2035+
cifs_server_dbg(VFS, "IPC: server doesn't support encryption\n");
2036+
return ERR_PTR(-EOPNOTSUPP);
20452037
}
20462038

20472039
/* no need to setup directory caching on IPC share, so pass in false */
20482040
tcon = tcon_info_alloc(false, netfs_trace_tcon_ref_new_ipc);
20492041
if (tcon == NULL)
2050-
return -ENOMEM;
2042+
return ERR_PTR(-ENOMEM);
20512043

20522044
spin_lock(&server->srv_lock);
20532045
scnprintf(unc, sizeof(unc), "\\\\%s\\IPC$", server->hostname);
@@ -2057,23 +2049,21 @@ cifs_setup_ipc(struct cifs_ses *ses, struct smb3_fs_context *ctx)
20572049
tcon->ses = ses;
20582050
tcon->ipc = true;
20592051
tcon->seal = seal;
2060-
rc = server->ops->tree_connect(xid, ses, unc, tcon, ctx->local_nls);
2052+
rc = server->ops->tree_connect(xid, ses, unc, tcon, ses->local_nls);
20612053
free_xid(xid);
20622054

20632055
if (rc) {
2064-
cifs_server_dbg(VFS, "failed to connect to IPC (rc=%d)\n", rc);
2056+
cifs_server_dbg(VFS | ONCE, "failed to connect to IPC (rc=%d)\n", rc);
20652057
tconInfoFree(tcon, netfs_trace_tcon_ref_free_ipc_fail);
2066-
goto out;
2058+
return ERR_PTR(rc);
20672059
}
20682060

20692061
cifs_dbg(FYI, "IPC tcon rc=%d ipc tid=0x%x\n", rc, tcon->tid);
20702062

20712063
spin_lock(&tcon->tc_lock);
20722064
tcon->status = TID_GOOD;
20732065
spin_unlock(&tcon->tc_lock);
2074-
ses->tcon_ipc = tcon;
2075-
out:
2076-
return rc;
2066+
return tcon;
20772067
}
20782068

20792069
static struct cifs_ses *
@@ -2347,6 +2337,7 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
23472337
{
23482338
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&server->dstaddr;
23492339
struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr;
2340+
struct cifs_tcon *ipc;
23502341
struct cifs_ses *ses;
23512342
unsigned int xid;
23522343
int retries = 0;
@@ -2525,7 +2516,12 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
25252516
list_add(&ses->smb_ses_list, &server->smb_ses_list);
25262517
spin_unlock(&cifs_tcp_ses_lock);
25272518

2528-
cifs_setup_ipc(ses, ctx);
2519+
ipc = cifs_setup_ipc(ses, ctx->seal);
2520+
spin_lock(&cifs_tcp_ses_lock);
2521+
spin_lock(&ses->ses_lock);
2522+
ses->tcon_ipc = !IS_ERR(ipc) ? ipc : NULL;
2523+
spin_unlock(&ses->ses_lock);
2524+
spin_unlock(&cifs_tcp_ses_lock);
25292525

25302526
free_xid(xid);
25312527

fs/smb/client/dfs_cache.c

Lines changed: 47 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,24 +1120,63 @@ static bool target_share_equal(struct cifs_tcon *tcon, const char *s1)
11201120
return match;
11211121
}
11221122

1123-
static bool is_ses_good(struct cifs_ses *ses)
1123+
static bool is_ses_good(struct cifs_tcon *tcon, struct cifs_ses *ses)
11241124
{
11251125
struct TCP_Server_Info *server = ses->server;
1126-
struct cifs_tcon *tcon = ses->tcon_ipc;
1126+
struct cifs_tcon *ipc = NULL;
11271127
bool ret;
11281128

1129+
spin_lock(&cifs_tcp_ses_lock);
11291130
spin_lock(&ses->ses_lock);
11301131
spin_lock(&ses->chan_lock);
1132+
11311133
ret = !cifs_chan_needs_reconnect(ses, server) &&
1132-
ses->ses_status == SES_GOOD &&
1133-
!tcon->need_reconnect;
1134+
ses->ses_status == SES_GOOD;
1135+
11341136
spin_unlock(&ses->chan_lock);
1137+
1138+
if (!ret)
1139+
goto out;
1140+
1141+
if (likely(ses->tcon_ipc)) {
1142+
if (ses->tcon_ipc->need_reconnect) {
1143+
ret = false;
1144+
goto out;
1145+
}
1146+
} else {
1147+
spin_unlock(&ses->ses_lock);
1148+
spin_unlock(&cifs_tcp_ses_lock);
1149+
1150+
ipc = cifs_setup_ipc(ses, tcon->seal);
1151+
1152+
spin_lock(&cifs_tcp_ses_lock);
1153+
spin_lock(&ses->ses_lock);
1154+
if (!IS_ERR(ipc)) {
1155+
if (!ses->tcon_ipc) {
1156+
ses->tcon_ipc = ipc;
1157+
ipc = NULL;
1158+
}
1159+
} else {
1160+
ret = false;
1161+
ipc = NULL;
1162+
}
1163+
}
1164+
1165+
out:
11351166
spin_unlock(&ses->ses_lock);
1167+
spin_unlock(&cifs_tcp_ses_lock);
1168+
if (ipc && server->ops->tree_disconnect) {
1169+
unsigned int xid = get_xid();
1170+
1171+
(void)server->ops->tree_disconnect(xid, ipc);
1172+
_free_xid(xid);
1173+
}
1174+
tconInfoFree(ipc, netfs_trace_tcon_ref_free_ipc);
11361175
return ret;
11371176
}
11381177

11391178
/* Refresh dfs referral of @ses */
1140-
static void refresh_ses_referral(struct cifs_ses *ses)
1179+
static void refresh_ses_referral(struct cifs_tcon *tcon, struct cifs_ses *ses)
11411180
{
11421181
struct cache_entry *ce;
11431182
unsigned int xid;
@@ -1153,7 +1192,7 @@ static void refresh_ses_referral(struct cifs_ses *ses)
11531192
}
11541193

11551194
ses = CIFS_DFS_ROOT_SES(ses);
1156-
if (!is_ses_good(ses)) {
1195+
if (!is_ses_good(tcon, ses)) {
11571196
cifs_dbg(FYI, "%s: skip cache refresh due to disconnected ipc\n",
11581197
__func__);
11591198
goto out;
@@ -1241,7 +1280,7 @@ static void refresh_tcon_referral(struct cifs_tcon *tcon, bool force_refresh)
12411280
up_read(&htable_rw_lock);
12421281

12431282
ses = CIFS_DFS_ROOT_SES(ses);
1244-
if (!is_ses_good(ses)) {
1283+
if (!is_ses_good(tcon, ses)) {
12451284
cifs_dbg(FYI, "%s: skip cache refresh due to disconnected ipc\n",
12461285
__func__);
12471286
goto out;
@@ -1309,7 +1348,7 @@ void dfs_cache_refresh(struct work_struct *work)
13091348
tcon = container_of(work, struct cifs_tcon, dfs_cache_work.work);
13101349

13111350
list_for_each_entry(ses, &tcon->dfs_ses_list, dlist)
1312-
refresh_ses_referral(ses);
1351+
refresh_ses_referral(tcon, ses);
13131352
refresh_tcon_referral(tcon, false);
13141353

13151354
queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work,

0 commit comments

Comments
 (0)