Skip to content

Commit 4f42a8b

Browse files
Paulo AlcantaraSteve French
authored andcommitted
smb: client: fix DFS interlink failover
The DFS interlinks point to different DFS namespaces so make sure to use the correct DFS root server to chase any DFS links under it by storing the SMB session in dfs_ref_walk structure and then using it on every referral walk. Signed-off-by: Paulo Alcantara (Red Hat) <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 9190cc0 commit 4f42a8b

File tree

9 files changed

+94
-86
lines changed

9 files changed

+94
-86
lines changed

fs/smb/client/cifsglob.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -821,6 +821,7 @@ struct TCP_Server_Info {
821821
* format: \\HOST\SHARE[\OPTIONAL PATH]
822822
*/
823823
char *leaf_fullpath;
824+
bool dfs_conn:1;
824825
};
825826

826827
static inline bool is_smb1(struct TCP_Server_Info *server)
@@ -1059,6 +1060,7 @@ struct cifs_ses {
10591060
struct list_head smb_ses_list;
10601061
struct list_head rlist; /* reconnect list */
10611062
struct list_head tcon_list;
1063+
struct list_head dlist; /* dfs list */
10621064
struct cifs_tcon *tcon_ipc;
10631065
spinlock_t ses_lock; /* protect anything here that is not protected */
10641066
struct mutex session_mutex;
@@ -1287,6 +1289,7 @@ struct cifs_tcon {
12871289
/* BB add field for back pointer to sb struct(s)? */
12881290
#ifdef CONFIG_CIFS_DFS_UPCALL
12891291
struct delayed_work dfs_cache_work;
1292+
struct list_head dfs_ses_list;
12901293
#endif
12911294
struct delayed_work query_interfaces; /* query interfaces workqueue job */
12921295
char *origin_fullpath; /* canonical copy of smb3_fs_context::source */

fs/smb/client/cifsproto.h

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -724,15 +724,9 @@ static inline int cifs_create_options(struct cifs_sb_info *cifs_sb, int options)
724724

725725
int cifs_wait_for_server_reconnect(struct TCP_Server_Info *server, bool retry);
726726

727-
/* Put references of @ses and its children */
728727
static inline void cifs_put_smb_ses(struct cifs_ses *ses)
729728
{
730-
struct cifs_ses *next;
731-
732-
do {
733-
next = ses->dfs_root_ses;
734-
__cifs_put_smb_ses(ses);
735-
} while ((ses = next));
729+
__cifs_put_smb_ses(ses);
736730
}
737731

738732
/* Get an active reference of @ses and its children.
@@ -746,9 +740,7 @@ static inline void cifs_put_smb_ses(struct cifs_ses *ses)
746740
static inline void cifs_smb_ses_inc_refcount(struct cifs_ses *ses)
747741
{
748742
lockdep_assert_held(&cifs_tcp_ses_lock);
749-
750-
for (; ses; ses = ses->dfs_root_ses)
751-
ses->ses_count++;
743+
ses->ses_count++;
752744
}
753745

754746
static inline bool dfs_src_pathname_equal(const char *s1, const char *s2)

fs/smb/client/connect.c

Lines changed: 20 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1530,6 +1530,9 @@ static int match_server(struct TCP_Server_Info *server,
15301530
if (server->nosharesock)
15311531
return 0;
15321532

1533+
if (!match_super && (ctx->dfs_conn || server->dfs_conn))
1534+
return 0;
1535+
15331536
/* If multidialect negotiation see if existing sessions match one */
15341537
if (strcmp(ctx->vals->version_string, SMB3ANY_VERSION_STRING) == 0) {
15351538
if (server->vals->protocol_id < SMB30_PROT_ID)
@@ -1723,6 +1726,7 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
17231726

17241727
if (ctx->nosharesock)
17251728
tcp_ses->nosharesock = true;
1729+
tcp_ses->dfs_conn = ctx->dfs_conn;
17261730

17271731
tcp_ses->ops = ctx->ops;
17281732
tcp_ses->vals = ctx->vals;
@@ -1873,13 +1877,15 @@ cifs_get_tcp_session(struct smb3_fs_context *ctx,
18731877
}
18741878

18751879
/* this function must be called with ses_lock and chan_lock held */
1876-
static int match_session(struct cifs_ses *ses, struct smb3_fs_context *ctx)
1880+
static int match_session(struct cifs_ses *ses,
1881+
struct smb3_fs_context *ctx,
1882+
bool match_super)
18771883
{
18781884
if (ctx->sectype != Unspecified &&
18791885
ctx->sectype != ses->sectype)
18801886
return 0;
18811887

1882-
if (ctx->dfs_root_ses != ses->dfs_root_ses)
1888+
if (!match_super && ctx->dfs_root_ses != ses->dfs_root_ses)
18831889
return 0;
18841890

18851891
/*
@@ -1998,7 +2004,7 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
19982004
continue;
19992005
}
20002006
spin_lock(&ses->chan_lock);
2001-
if (match_session(ses, ctx)) {
2007+
if (match_session(ses, ctx, false)) {
20022008
spin_unlock(&ses->chan_lock);
20032009
spin_unlock(&ses->ses_lock);
20042010
ret = ses;
@@ -2382,8 +2388,6 @@ cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb3_fs_context *ctx)
23822388
* need to lock before changing something in the session.
23832389
*/
23842390
spin_lock(&cifs_tcp_ses_lock);
2385-
if (ctx->dfs_root_ses)
2386-
cifs_smb_ses_inc_refcount(ctx->dfs_root_ses);
23872391
ses->dfs_root_ses = ctx->dfs_root_ses;
23882392
list_add(&ses->smb_ses_list, &server->smb_ses_list);
23892393
spin_unlock(&cifs_tcp_ses_lock);
@@ -2458,6 +2462,7 @@ cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace)
24582462
{
24592463
unsigned int xid;
24602464
struct cifs_ses *ses;
2465+
LIST_HEAD(ses_list);
24612466

24622467
/*
24632468
* IPC tcon share the lifetime of their session and are
@@ -2482,6 +2487,9 @@ cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace)
24822487

24832488
list_del_init(&tcon->tcon_list);
24842489
tcon->status = TID_EXITING;
2490+
#ifdef CONFIG_CIFS_DFS_UPCALL
2491+
list_replace_init(&tcon->dfs_ses_list, &ses_list);
2492+
#endif
24852493
spin_unlock(&tcon->tc_lock);
24862494
spin_unlock(&cifs_tcp_ses_lock);
24872495

@@ -2509,6 +2517,9 @@ cifs_put_tcon(struct cifs_tcon *tcon, enum smb3_tcon_ref_trace trace)
25092517
cifs_fscache_release_super_cookie(tcon);
25102518
tconInfoFree(tcon, netfs_trace_tcon_ref_free);
25112519
cifs_put_smb_ses(ses);
2520+
#ifdef CONFIG_CIFS_DFS_UPCALL
2521+
dfs_put_root_smb_sessions(&ses_list);
2522+
#endif
25122523
}
25132524

25142525
/**
@@ -2892,7 +2903,7 @@ cifs_match_super(struct super_block *sb, void *data)
28922903
spin_lock(&ses->chan_lock);
28932904
spin_lock(&tcon->tc_lock);
28942905
if (!match_server(tcp_srv, ctx, true) ||
2895-
!match_session(ses, ctx) ||
2906+
!match_session(ses, ctx, true) ||
28962907
!match_tcon(tcon, ctx) ||
28972908
!match_prepath(sb, tcon, mnt_data)) {
28982909
rc = 0;
@@ -3623,13 +3634,12 @@ int cifs_is_path_remote(struct cifs_mount_ctx *mnt_ctx)
36233634
int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
36243635
{
36253636
struct cifs_mount_ctx mnt_ctx = { .cifs_sb = cifs_sb, .fs_ctx = ctx, };
3626-
bool isdfs;
36273637
int rc;
36283638

3629-
rc = dfs_mount_share(&mnt_ctx, &isdfs);
3639+
rc = dfs_mount_share(&mnt_ctx);
36303640
if (rc)
36313641
goto error;
3632-
if (!isdfs)
3642+
if (!ctx->dfs_conn)
36333643
goto out;
36343644

36353645
/*
@@ -4034,7 +4044,7 @@ cifs_set_vol_auth(struct smb3_fs_context *ctx, struct cifs_ses *ses)
40344044
}
40354045

40364046
static struct cifs_tcon *
4037-
__cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)
4047+
cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)
40384048
{
40394049
int rc;
40404050
struct cifs_tcon *master_tcon = cifs_sb_master_tcon(cifs_sb);
@@ -4132,17 +4142,6 @@ __cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)
41324142
return tcon;
41334143
}
41344144

4135-
static struct cifs_tcon *
4136-
cifs_construct_tcon(struct cifs_sb_info *cifs_sb, kuid_t fsuid)
4137-
{
4138-
struct cifs_tcon *ret;
4139-
4140-
cifs_mount_lock();
4141-
ret = __cifs_construct_tcon(cifs_sb, fsuid);
4142-
cifs_mount_unlock();
4143-
return ret;
4144-
}
4145-
41464145
struct cifs_tcon *
41474146
cifs_sb_master_tcon(struct cifs_sb_info *cifs_sb)
41484147
{

fs/smb/client/dfs.c

Lines changed: 35 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ static int get_session(struct cifs_mount_ctx *mnt_ctx, const char *full_path)
6969
* Get an active reference of @ses so that next call to cifs_put_tcon() won't
7070
* release it as any new DFS referrals must go through its IPC tcon.
7171
*/
72-
static void add_root_smb_session(struct cifs_mount_ctx *mnt_ctx)
72+
static void set_root_smb_session(struct cifs_mount_ctx *mnt_ctx)
7373
{
7474
struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
7575
struct cifs_ses *ses = mnt_ctx->ses;
@@ -95,7 +95,7 @@ static inline int parse_dfs_target(struct smb3_fs_context *ctx,
9595
return rc;
9696
}
9797

98-
static int set_ref_paths(struct cifs_mount_ctx *mnt_ctx,
98+
static int setup_dfs_ref(struct cifs_mount_ctx *mnt_ctx,
9999
struct dfs_info3_param *tgt,
100100
struct dfs_ref_walk *rw)
101101
{
@@ -120,6 +120,7 @@ static int set_ref_paths(struct cifs_mount_ctx *mnt_ctx,
120120
}
121121
ref_walk_path(rw) = ref_path;
122122
ref_walk_fpath(rw) = full_path;
123+
ref_walk_ses(rw) = ctx->dfs_root_ses;
123124
return 0;
124125
}
125126

@@ -128,11 +129,11 @@ static int __dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx,
128129
{
129130
struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
130131
struct dfs_info3_param tgt = {};
131-
bool is_refsrv;
132132
int rc = -ENOENT;
133133

134134
again:
135135
do {
136+
ctx->dfs_root_ses = ref_walk_ses(rw);
136137
if (ref_walk_empty(rw)) {
137138
rc = dfs_get_referral(mnt_ctx, ref_walk_path(rw) + 1,
138139
NULL, ref_walk_tl(rw));
@@ -158,10 +159,7 @@ static int __dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx,
158159
if (rc)
159160
continue;
160161

161-
is_refsrv = tgt.server_type == DFS_TYPE_ROOT ||
162-
DFS_INTERLINK(tgt.flags);
163162
ref_walk_set_tgt_hint(rw);
164-
165163
if (tgt.flags & DFSREF_STORAGE_SERVER) {
166164
rc = cifs_mount_get_tcon(mnt_ctx);
167165
if (!rc)
@@ -172,12 +170,10 @@ static int __dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx,
172170
continue;
173171
}
174172

175-
if (is_refsrv)
176-
add_root_smb_session(mnt_ctx);
177-
173+
set_root_smb_session(mnt_ctx);
178174
rc = ref_walk_advance(rw);
179175
if (!rc) {
180-
rc = set_ref_paths(mnt_ctx, &tgt, rw);
176+
rc = setup_dfs_ref(mnt_ctx, &tgt, rw);
181177
if (!rc) {
182178
rc = -EREMOTE;
183179
goto again;
@@ -193,37 +189,39 @@ static int __dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx,
193189
return rc;
194190
}
195191

196-
static int dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx)
192+
static int dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx,
193+
struct dfs_ref_walk **rw)
197194
{
198-
struct dfs_ref_walk *rw;
199195
int rc;
200196

201-
rw = ref_walk_alloc();
202-
if (IS_ERR(rw))
203-
return PTR_ERR(rw);
197+
*rw = ref_walk_alloc();
198+
if (IS_ERR(*rw)) {
199+
rc = PTR_ERR(*rw);
200+
*rw = NULL;
201+
return rc;
202+
}
204203

205-
ref_walk_init(rw);
206-
rc = set_ref_paths(mnt_ctx, NULL, rw);
204+
ref_walk_init(*rw);
205+
rc = setup_dfs_ref(mnt_ctx, NULL, *rw);
207206
if (!rc)
208-
rc = __dfs_referral_walk(mnt_ctx, rw);
209-
ref_walk_free(rw);
207+
rc = __dfs_referral_walk(mnt_ctx, *rw);
210208
return rc;
211209
}
212210

213211
static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
214212
{
215213
struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
216214
struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
215+
struct dfs_ref_walk *rw = NULL;
217216
struct cifs_tcon *tcon;
218217
char *origin_fullpath;
219-
bool new_tcon = true;
220218
int rc;
221219

222220
origin_fullpath = dfs_get_path(cifs_sb, ctx->source);
223221
if (IS_ERR(origin_fullpath))
224222
return PTR_ERR(origin_fullpath);
225223

226-
rc = dfs_referral_walk(mnt_ctx);
224+
rc = dfs_referral_walk(mnt_ctx, &rw);
227225
if (!rc) {
228226
/*
229227
* Prevent superblock from being created with any missing
@@ -241,21 +239,16 @@ static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
241239

242240
tcon = mnt_ctx->tcon;
243241
spin_lock(&tcon->tc_lock);
244-
if (!tcon->origin_fullpath) {
245-
tcon->origin_fullpath = origin_fullpath;
246-
origin_fullpath = NULL;
247-
} else {
248-
new_tcon = false;
249-
}
242+
tcon->origin_fullpath = origin_fullpath;
243+
origin_fullpath = NULL;
244+
ref_walk_set_tcon(rw, tcon);
250245
spin_unlock(&tcon->tc_lock);
251-
252-
if (new_tcon) {
253-
queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work,
254-
dfs_cache_get_ttl() * HZ);
255-
}
246+
queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work,
247+
dfs_cache_get_ttl() * HZ);
256248

257249
out:
258250
kfree(origin_fullpath);
251+
ref_walk_free(rw);
259252
return rc;
260253
}
261254

@@ -279,7 +272,7 @@ static int update_fs_context_dstaddr(struct smb3_fs_context *ctx)
279272
return rc;
280273
}
281274

282-
int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs)
275+
int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
283276
{
284277
struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
285278
bool nodfs = ctx->nodfs;
@@ -289,7 +282,6 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs)
289282
if (rc)
290283
return rc;
291284

292-
*isdfs = false;
293285
rc = get_session(mnt_ctx, NULL);
294286
if (rc)
295287
return rc;
@@ -317,10 +309,15 @@ int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs)
317309
return rc;
318310
}
319311

320-
*isdfs = true;
321-
add_root_smb_session(mnt_ctx);
322-
rc = __dfs_mount_share(mnt_ctx);
323-
dfs_put_root_smb_sessions(mnt_ctx);
312+
if (!ctx->dfs_conn) {
313+
ctx->dfs_conn = true;
314+
cifs_mount_put_conns(mnt_ctx);
315+
rc = get_session(mnt_ctx, NULL);
316+
}
317+
if (!rc) {
318+
set_root_smb_session(mnt_ctx);
319+
rc = __dfs_mount_share(mnt_ctx);
320+
}
324321
return rc;
325322
}
326323

0 commit comments

Comments
 (0)