Skip to content

Commit caa0ac9

Browse files
committed
smb: client: handle lack of IPC in dfs_cache_refresh()
JIRA: https://issues.redhat.com/browse/RHEL-119582 commit fac56c4 Author: Paulo Alcantara <pc@manguebit.org> Date: Thu Oct 23 18:59:47 2025 -0300 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 <jaeshin@redhat.com> Signed-off-by: Paulo Alcantara (Red Hat) <pc@manguebit.org> Cc: David Howells <dhowells@redhat.com> Cc: linux-cifs@vger.kernel.org Signed-off-by: Steve French <stfrench@microsoft.com> Signed-off-by: Paulo Alcantara <paalcant@redhat.com>
1 parent ec5f4d3 commit caa0ac9

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
@@ -600,6 +600,8 @@ extern int E_md4hash(const unsigned char *passwd, unsigned char *p16,
600600
extern struct TCP_Server_Info *
601601
cifs_find_tcp_session(struct smb3_fs_context *ctx);
602602

603+
struct cifs_tcon *cifs_setup_ipc(struct cifs_ses *ses, bool seal);
604+
603605
void __cifs_put_smb_ses(struct cifs_ses *ses);
604606

605607
extern struct cifs_ses *

fs/smb/client/connect.c

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2017,39 +2017,31 @@ static int match_session(struct cifs_ses *ses,
20172017
/**
20182018
* cifs_setup_ipc - helper to setup the IPC tcon for the session
20192019
* @ses: smb session to issue the request on
2020-
* @ctx: the superblock configuration context to use for building the
2021-
* new tree connection for the IPC (interprocess communication RPC)
2020+
* @seal: if encryption is requested
20222021
*
20232022
* A new IPC connection is made and stored in the session
20242023
* tcon_ipc. The IPC tcon has the same lifetime as the session.
20252024
*/
2026-
static int
2027-
cifs_setup_ipc(struct cifs_ses *ses, struct smb3_fs_context *ctx)
2025+
struct cifs_tcon *cifs_setup_ipc(struct cifs_ses *ses, bool seal)
20282026
{
20292027
int rc = 0, xid;
20302028
struct cifs_tcon *tcon;
20312029
char unc[SERVER_NAME_LENGTH + sizeof("//x/IPC$")] = {0};
2032-
bool seal = false;
20332030
struct TCP_Server_Info *server = ses->server;
20342031

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

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

20542046
spin_lock(&server->srv_lock);
20552047
scnprintf(unc, sizeof(unc), "\\\\%s\\IPC$", server->hostname);
@@ -2059,23 +2051,21 @@ cifs_setup_ipc(struct cifs_ses *ses, struct smb3_fs_context *ctx)
20592051
tcon->ses = ses;
20602052
tcon->ipc = true;
20612053
tcon->seal = seal;
2062-
rc = server->ops->tree_connect(xid, ses, unc, tcon, ctx->local_nls);
2054+
rc = server->ops->tree_connect(xid, ses, unc, tcon, ses->local_nls);
20632055
free_xid(xid);
20642056

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

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

20732065
spin_lock(&tcon->tc_lock);
20742066
tcon->status = TID_GOOD;
20752067
spin_unlock(&tcon->tc_lock);
2076-
ses->tcon_ipc = tcon;
2077-
out:
2078-
return rc;
2068+
return tcon;
20792069
}
20802070

20812071
static struct cifs_ses *
@@ -2349,6 +2339,7 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
23492339
{
23502340
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&server->dstaddr;
23512341
struct sockaddr_in *addr = (struct sockaddr_in *)&server->dstaddr;
2342+
struct cifs_tcon *ipc;
23522343
struct cifs_ses *ses;
23532344
unsigned int xid;
23542345
int retries = 0;
@@ -2527,7 +2518,12 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
25272518
list_add(&ses->smb_ses_list, &server->smb_ses_list);
25282519
spin_unlock(&cifs_tcp_ses_lock);
25292520

2530-
cifs_setup_ipc(ses, ctx);
2521+
ipc = cifs_setup_ipc(ses, ctx->seal);
2522+
spin_lock(&cifs_tcp_ses_lock);
2523+
spin_lock(&ses->ses_lock);
2524+
ses->tcon_ipc = !IS_ERR(ipc) ? ipc : NULL;
2525+
spin_unlock(&ses->ses_lock);
2526+
spin_unlock(&cifs_tcp_ses_lock);
25312527

25322528
free_xid(xid);
25332529

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)