Skip to content

Commit ce04127

Browse files
Paulo Alcantarasmfrench
authored andcommitted
smb: client: ensure to try all targets when finding nested links
With current implementation, when a nested DFS link is found during mount(2), the client follows the referral and then try to connect to all of its targets. If all targets failed, the client bails out rather than retrying remaining targets from previous referral. Fix this by stacking all referrals and targets so the client can retry remaining targets from previous referrals in case all targets of current referral have failed. Thanks to samba, this can be easily tested like below * Run the following under dfs folder in samba server $ ln -s "msdfs:srv\\bad-share" link1 $ ln -s "msdfs:srv\\dfs\\link1,srv\\good-share" link0 * Before patch $ mount.cifs //srv/dfs/link0 /mnt -o ... mount error(2): No such file or directory Refer to the mount.cifs(8) manual page (e.g. man mount.cifs)... * After patch $ mount.cifs //srv/dfs/link0 /mnt -o ... # ls /mnt bar fileshare1 sub Signed-off-by: Paulo Alcantara (SUSE) <[email protected]> Signed-off-by: Steve French <[email protected]>
1 parent 3fea12f commit ce04127

File tree

5 files changed

+255
-128
lines changed

5 files changed

+255
-128
lines changed

fs/smb/client/cifsglob.h

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1721,11 +1721,23 @@ struct cifs_mount_ctx {
17211721
struct list_head dfs_ses_list;
17221722
};
17231723

1724+
static inline void __free_dfs_info_param(struct dfs_info3_param *param)
1725+
{
1726+
kfree(param->path_name);
1727+
kfree(param->node_name);
1728+
}
1729+
17241730
static inline void free_dfs_info_param(struct dfs_info3_param *param)
1731+
{
1732+
if (param)
1733+
__free_dfs_info_param(param);
1734+
}
1735+
1736+
static inline void zfree_dfs_info_param(struct dfs_info3_param *param)
17251737
{
17261738
if (param) {
1727-
kfree(param->path_name);
1728-
kfree(param->node_name);
1739+
__free_dfs_info_param(param);
1740+
memset(param, 0, sizeof(*param));
17291741
}
17301742
}
17311743

fs/smb/client/dfs.c

Lines changed: 132 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
* Copyright (c) 2022 Paulo Alcantara <[email protected]>
44
*/
55

6-
#include <linux/namei.h>
76
#include "cifsproto.h"
87
#include "cifs_debug.h"
98
#include "dns_resolve.h"
@@ -96,157 +95,171 @@ static int add_root_smb_session(struct cifs_mount_ctx *mnt_ctx)
9695
return 0;
9796
}
9897

99-
static int get_dfs_conn(struct cifs_mount_ctx *mnt_ctx, const char *ref_path, const char *full_path,
100-
const struct dfs_cache_tgt_iterator *tit)
98+
static inline int parse_dfs_target(struct smb3_fs_context *ctx,
99+
struct dfs_ref_walk *rw,
100+
struct dfs_info3_param *tgt)
101+
{
102+
int rc;
103+
const char *fpath = ref_walk_fpath(rw) + 1;
104+
105+
rc = ref_walk_get_tgt(rw, tgt);
106+
if (!rc)
107+
rc = dfs_parse_target_referral(fpath, tgt, ctx);
108+
return rc;
109+
}
110+
111+
static int set_ref_paths(struct cifs_mount_ctx *mnt_ctx,
112+
struct dfs_info3_param *tgt,
113+
struct dfs_ref_walk *rw)
101114
{
102115
struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
103-
struct dfs_info3_param ref = {};
104-
bool is_refsrv;
105-
int rc, rc2;
116+
struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
117+
char *ref_path, *full_path;
118+
int rc;
106119

107-
rc = dfs_cache_get_tgt_referral(ref_path + 1, tit, &ref);
108-
if (rc)
120+
full_path = smb3_fs_context_fullpath(ctx, CIFS_DIR_SEP(cifs_sb));
121+
if (IS_ERR(full_path))
122+
return PTR_ERR(full_path);
123+
124+
if (!tgt || (tgt->server_type == DFS_TYPE_LINK &&
125+
DFS_INTERLINK(tgt->flags)))
126+
ref_path = dfs_get_path(cifs_sb, ctx->UNC);
127+
else
128+
ref_path = dfs_get_path(cifs_sb, full_path);
129+
if (IS_ERR(ref_path)) {
130+
rc = PTR_ERR(ref_path);
131+
kfree(full_path);
109132
return rc;
133+
}
134+
ref_walk_path(rw) = ref_path;
135+
ref_walk_fpath(rw) = full_path;
136+
return 0;
137+
}
110138

111-
rc = dfs_parse_target_referral(full_path + 1, &ref, ctx);
112-
if (rc)
113-
goto out;
139+
static int __dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx,
140+
struct dfs_ref_walk *rw)
141+
{
142+
struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
143+
struct dfs_info3_param tgt = {};
144+
bool is_refsrv;
145+
int rc = -ENOENT;
114146

115-
cifs_mount_put_conns(mnt_ctx);
116-
rc = get_session(mnt_ctx, ref_path);
117-
if (rc)
118-
goto out;
147+
again:
148+
do {
149+
if (ref_walk_empty(rw)) {
150+
rc = dfs_get_referral(mnt_ctx, ref_walk_path(rw) + 1,
151+
NULL, ref_walk_tl(rw));
152+
if (rc) {
153+
rc = cifs_mount_get_tcon(mnt_ctx);
154+
if (!rc)
155+
rc = cifs_is_path_remote(mnt_ctx);
156+
continue;
157+
}
158+
if (!ref_walk_num_tgts(rw)) {
159+
rc = -ENOENT;
160+
continue;
161+
}
162+
}
119163

120-
is_refsrv = !!(ref.flags & DFSREF_REFERRAL_SERVER);
164+
while (ref_walk_next_tgt(rw)) {
165+
rc = parse_dfs_target(ctx, rw, &tgt);
166+
if (rc)
167+
continue;
121168

122-
rc = -EREMOTE;
123-
if (ref.flags & DFSREF_STORAGE_SERVER) {
124-
rc = cifs_mount_get_tcon(mnt_ctx);
125-
if (rc)
126-
goto out;
169+
cifs_mount_put_conns(mnt_ctx);
170+
rc = get_session(mnt_ctx, ref_walk_path(rw));
171+
if (rc)
172+
continue;
127173

128-
/* some servers may not advertise referral capability under ref.flags */
129-
is_refsrv |= is_tcon_dfs(mnt_ctx->tcon);
174+
is_refsrv = tgt.server_type == DFS_TYPE_ROOT ||
175+
DFS_INTERLINK(tgt.flags);
176+
ref_walk_set_tgt_hint(rw);
130177

131-
rc = cifs_is_path_remote(mnt_ctx);
132-
}
178+
if (tgt.flags & DFSREF_STORAGE_SERVER) {
179+
rc = cifs_mount_get_tcon(mnt_ctx);
180+
if (!rc)
181+
rc = cifs_is_path_remote(mnt_ctx);
182+
if (!rc)
183+
break;
184+
if (rc != -EREMOTE)
185+
continue;
186+
}
133187

134-
dfs_cache_noreq_update_tgthint(ref_path + 1, tit);
188+
if (is_refsrv) {
189+
rc = add_root_smb_session(mnt_ctx);
190+
if (rc)
191+
goto out;
192+
}
135193

136-
if (rc == -EREMOTE && is_refsrv) {
137-
rc2 = add_root_smb_session(mnt_ctx);
138-
if (rc2)
139-
rc = rc2;
140-
}
194+
rc = ref_walk_advance(rw);
195+
if (!rc) {
196+
rc = set_ref_paths(mnt_ctx, &tgt, rw);
197+
if (!rc) {
198+
rc = -EREMOTE;
199+
goto again;
200+
}
201+
}
202+
if (rc != -ELOOP)
203+
goto out;
204+
}
205+
} while (rc && ref_walk_descend(rw));
141206

142207
out:
143-
free_dfs_info_param(&ref);
208+
free_dfs_info_param(&tgt);
209+
return rc;
210+
}
211+
212+
static int dfs_referral_walk(struct cifs_mount_ctx *mnt_ctx)
213+
{
214+
struct dfs_ref_walk *rw;
215+
int rc;
216+
217+
rw = ref_walk_alloc();
218+
if (IS_ERR(rw))
219+
return PTR_ERR(rw);
220+
221+
ref_walk_init(rw);
222+
rc = set_ref_paths(mnt_ctx, NULL, rw);
223+
if (!rc)
224+
rc = __dfs_referral_walk(mnt_ctx, rw);
225+
ref_walk_free(rw);
144226
return rc;
145227
}
146228

147229
static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
148230
{
149231
struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
150232
struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
151-
char *ref_path = NULL, *full_path = NULL;
152-
struct dfs_cache_tgt_iterator *tit;
153233
struct cifs_tcon *tcon;
154-
char *origin_fullpath = NULL;
155-
char sep = CIFS_DIR_SEP(cifs_sb);
156-
int num_links = 0;
234+
char *origin_fullpath;
157235
int rc;
158236

159-
ref_path = dfs_get_path(cifs_sb, ctx->UNC);
160-
if (IS_ERR(ref_path))
161-
return PTR_ERR(ref_path);
237+
origin_fullpath = dfs_get_path(cifs_sb, ctx->source);
238+
if (IS_ERR(origin_fullpath))
239+
return PTR_ERR(origin_fullpath);
162240

163-
full_path = smb3_fs_context_fullpath(ctx, sep);
164-
if (IS_ERR(full_path)) {
165-
rc = PTR_ERR(full_path);
166-
full_path = NULL;
241+
rc = dfs_referral_walk(mnt_ctx);
242+
if (rc)
167243
goto out;
168-
}
169244

170-
origin_fullpath = kstrdup(full_path, GFP_KERNEL);
171-
if (!origin_fullpath) {
172-
rc = -ENOMEM;
173-
goto out;
245+
tcon = mnt_ctx->tcon;
246+
spin_lock(&tcon->tc_lock);
247+
if (!tcon->origin_fullpath) {
248+
tcon->origin_fullpath = origin_fullpath;
249+
origin_fullpath = NULL;
174250
}
251+
spin_unlock(&tcon->tc_lock);
175252

176-
do {
177-
DFS_CACHE_TGT_LIST(tl);
178-
179-
rc = dfs_get_referral(mnt_ctx, ref_path + 1, NULL, &tl);
180-
if (rc) {
181-
rc = cifs_mount_get_tcon(mnt_ctx);
182-
if (!rc)
183-
rc = cifs_is_path_remote(mnt_ctx);
184-
break;
185-
}
186-
187-
tit = dfs_cache_get_tgt_iterator(&tl);
188-
if (!tit) {
189-
cifs_dbg(VFS, "%s: dfs referral (%s) with no targets\n", __func__,
190-
ref_path + 1);
191-
rc = -ENOENT;
192-
dfs_cache_free_tgts(&tl);
193-
break;
194-
}
195-
196-
do {
197-
rc = get_dfs_conn(mnt_ctx, ref_path, full_path, tit);
198-
if (!rc)
199-
break;
200-
if (rc == -EREMOTE) {
201-
if (++num_links > MAX_NESTED_LINKS) {
202-
rc = -ELOOP;
203-
break;
204-
}
205-
kfree(ref_path);
206-
kfree(full_path);
207-
ref_path = full_path = NULL;
208-
209-
full_path = smb3_fs_context_fullpath(ctx, sep);
210-
if (IS_ERR(full_path)) {
211-
rc = PTR_ERR(full_path);
212-
full_path = NULL;
213-
} else {
214-
ref_path = dfs_get_path(cifs_sb, full_path);
215-
if (IS_ERR(ref_path)) {
216-
rc = PTR_ERR(ref_path);
217-
ref_path = NULL;
218-
}
219-
}
220-
break;
221-
}
222-
} while ((tit = dfs_cache_get_next_tgt(&tl, tit)));
223-
dfs_cache_free_tgts(&tl);
224-
} while (rc == -EREMOTE);
225-
226-
if (!rc) {
227-
tcon = mnt_ctx->tcon;
228-
229-
spin_lock(&tcon->tc_lock);
230-
if (!tcon->origin_fullpath) {
231-
tcon->origin_fullpath = origin_fullpath;
232-
origin_fullpath = NULL;
233-
}
234-
spin_unlock(&tcon->tc_lock);
235-
236-
if (list_empty(&tcon->dfs_ses_list)) {
237-
list_replace_init(&mnt_ctx->dfs_ses_list,
238-
&tcon->dfs_ses_list);
239-
queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work,
240-
dfs_cache_get_ttl() * HZ);
241-
} else {
242-
dfs_put_root_smb_sessions(&mnt_ctx->dfs_ses_list);
243-
}
253+
if (list_empty(&tcon->dfs_ses_list)) {
254+
list_replace_init(&mnt_ctx->dfs_ses_list, &tcon->dfs_ses_list);
255+
queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work,
256+
dfs_cache_get_ttl() * HZ);
257+
} else {
258+
dfs_put_root_smb_sessions(&mnt_ctx->dfs_ses_list);
244259
}
245260

246261
out:
247262
kfree(origin_fullpath);
248-
kfree(ref_path);
249-
kfree(full_path);
250263
return rc;
251264
}
252265

0 commit comments

Comments
 (0)