xref: /openbmc/linux/fs/smb/client/dfs.c (revision 38c8a9a52082579090e34c033d439ed2cd1a462d)
1*38c8a9a5SSteve French // SPDX-License-Identifier: GPL-2.0
2*38c8a9a5SSteve French /*
3*38c8a9a5SSteve French  * Copyright (c) 2022 Paulo Alcantara <palcantara@suse.de>
4*38c8a9a5SSteve French  */
5*38c8a9a5SSteve French 
6*38c8a9a5SSteve French #include <linux/namei.h>
7*38c8a9a5SSteve French #include "cifsproto.h"
8*38c8a9a5SSteve French #include "cifs_debug.h"
9*38c8a9a5SSteve French #include "dns_resolve.h"
10*38c8a9a5SSteve French #include "fs_context.h"
11*38c8a9a5SSteve French #include "dfs.h"
12*38c8a9a5SSteve French 
13*38c8a9a5SSteve French /**
14*38c8a9a5SSteve French  * dfs_parse_target_referral - set fs context for dfs target referral
15*38c8a9a5SSteve French  *
16*38c8a9a5SSteve French  * @full_path: full path in UNC format.
17*38c8a9a5SSteve French  * @ref: dfs referral pointer.
18*38c8a9a5SSteve French  * @ctx: smb3 fs context pointer.
19*38c8a9a5SSteve French  *
20*38c8a9a5SSteve French  * Return zero if dfs referral was parsed correctly, otherwise non-zero.
21*38c8a9a5SSteve French  */
22*38c8a9a5SSteve French int dfs_parse_target_referral(const char *full_path, const struct dfs_info3_param *ref,
23*38c8a9a5SSteve French 			      struct smb3_fs_context *ctx)
24*38c8a9a5SSteve French {
25*38c8a9a5SSteve French 	int rc;
26*38c8a9a5SSteve French 	const char *prepath = NULL;
27*38c8a9a5SSteve French 	char *path;
28*38c8a9a5SSteve French 
29*38c8a9a5SSteve French 	if (!full_path || !*full_path || !ref || !ctx)
30*38c8a9a5SSteve French 		return -EINVAL;
31*38c8a9a5SSteve French 
32*38c8a9a5SSteve French 	if (WARN_ON_ONCE(!ref->node_name || ref->path_consumed < 0))
33*38c8a9a5SSteve French 		return -EINVAL;
34*38c8a9a5SSteve French 
35*38c8a9a5SSteve French 	if (strlen(full_path) - ref->path_consumed) {
36*38c8a9a5SSteve French 		prepath = full_path + ref->path_consumed;
37*38c8a9a5SSteve French 		/* skip initial delimiter */
38*38c8a9a5SSteve French 		if (*prepath == '/' || *prepath == '\\')
39*38c8a9a5SSteve French 			prepath++;
40*38c8a9a5SSteve French 	}
41*38c8a9a5SSteve French 
42*38c8a9a5SSteve French 	path = cifs_build_devname(ref->node_name, prepath);
43*38c8a9a5SSteve French 	if (IS_ERR(path))
44*38c8a9a5SSteve French 		return PTR_ERR(path);
45*38c8a9a5SSteve French 
46*38c8a9a5SSteve French 	rc = smb3_parse_devname(path, ctx);
47*38c8a9a5SSteve French 	if (rc)
48*38c8a9a5SSteve French 		goto out;
49*38c8a9a5SSteve French 
50*38c8a9a5SSteve French 	rc = dns_resolve_server_name_to_ip(path, (struct sockaddr *)&ctx->dstaddr, NULL);
51*38c8a9a5SSteve French 
52*38c8a9a5SSteve French out:
53*38c8a9a5SSteve French 	kfree(path);
54*38c8a9a5SSteve French 	return rc;
55*38c8a9a5SSteve French }
56*38c8a9a5SSteve French 
57*38c8a9a5SSteve French /*
58*38c8a9a5SSteve French  * cifs_build_path_to_root returns full path to root when we do not have an
59*38c8a9a5SSteve French  * existing connection (tcon)
60*38c8a9a5SSteve French  */
61*38c8a9a5SSteve French static char *build_unc_path_to_root(const struct smb3_fs_context *ctx,
62*38c8a9a5SSteve French 				    const struct cifs_sb_info *cifs_sb, bool useppath)
63*38c8a9a5SSteve French {
64*38c8a9a5SSteve French 	char *full_path, *pos;
65*38c8a9a5SSteve French 	unsigned int pplen = useppath && ctx->prepath ? strlen(ctx->prepath) + 1 : 0;
66*38c8a9a5SSteve French 	unsigned int unc_len = strnlen(ctx->UNC, MAX_TREE_SIZE + 1);
67*38c8a9a5SSteve French 
68*38c8a9a5SSteve French 	if (unc_len > MAX_TREE_SIZE)
69*38c8a9a5SSteve French 		return ERR_PTR(-EINVAL);
70*38c8a9a5SSteve French 
71*38c8a9a5SSteve French 	full_path = kmalloc(unc_len + pplen + 1, GFP_KERNEL);
72*38c8a9a5SSteve French 	if (full_path == NULL)
73*38c8a9a5SSteve French 		return ERR_PTR(-ENOMEM);
74*38c8a9a5SSteve French 
75*38c8a9a5SSteve French 	memcpy(full_path, ctx->UNC, unc_len);
76*38c8a9a5SSteve French 	pos = full_path + unc_len;
77*38c8a9a5SSteve French 
78*38c8a9a5SSteve French 	if (pplen) {
79*38c8a9a5SSteve French 		*pos = CIFS_DIR_SEP(cifs_sb);
80*38c8a9a5SSteve French 		memcpy(pos + 1, ctx->prepath, pplen);
81*38c8a9a5SSteve French 		pos += pplen;
82*38c8a9a5SSteve French 	}
83*38c8a9a5SSteve French 
84*38c8a9a5SSteve French 	*pos = '\0'; /* add trailing null */
85*38c8a9a5SSteve French 	convert_delimiter(full_path, CIFS_DIR_SEP(cifs_sb));
86*38c8a9a5SSteve French 	cifs_dbg(FYI, "%s: full_path=%s\n", __func__, full_path);
87*38c8a9a5SSteve French 	return full_path;
88*38c8a9a5SSteve French }
89*38c8a9a5SSteve French 
90*38c8a9a5SSteve French static int get_session(struct cifs_mount_ctx *mnt_ctx, const char *full_path)
91*38c8a9a5SSteve French {
92*38c8a9a5SSteve French 	struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
93*38c8a9a5SSteve French 	int rc;
94*38c8a9a5SSteve French 
95*38c8a9a5SSteve French 	ctx->leaf_fullpath = (char *)full_path;
96*38c8a9a5SSteve French 	rc = cifs_mount_get_session(mnt_ctx);
97*38c8a9a5SSteve French 	ctx->leaf_fullpath = NULL;
98*38c8a9a5SSteve French 
99*38c8a9a5SSteve French 	return rc;
100*38c8a9a5SSteve French }
101*38c8a9a5SSteve French 
102*38c8a9a5SSteve French static int add_root_smb_session(struct cifs_mount_ctx *mnt_ctx)
103*38c8a9a5SSteve French {
104*38c8a9a5SSteve French 	struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
105*38c8a9a5SSteve French 	struct dfs_root_ses *root_ses;
106*38c8a9a5SSteve French 	struct cifs_ses *ses = mnt_ctx->ses;
107*38c8a9a5SSteve French 
108*38c8a9a5SSteve French 	if (ses) {
109*38c8a9a5SSteve French 		root_ses = kmalloc(sizeof(*root_ses), GFP_KERNEL);
110*38c8a9a5SSteve French 		if (!root_ses)
111*38c8a9a5SSteve French 			return -ENOMEM;
112*38c8a9a5SSteve French 
113*38c8a9a5SSteve French 		INIT_LIST_HEAD(&root_ses->list);
114*38c8a9a5SSteve French 
115*38c8a9a5SSteve French 		spin_lock(&cifs_tcp_ses_lock);
116*38c8a9a5SSteve French 		ses->ses_count++;
117*38c8a9a5SSteve French 		spin_unlock(&cifs_tcp_ses_lock);
118*38c8a9a5SSteve French 		root_ses->ses = ses;
119*38c8a9a5SSteve French 		list_add_tail(&root_ses->list, &mnt_ctx->dfs_ses_list);
120*38c8a9a5SSteve French 	}
121*38c8a9a5SSteve French 	ctx->dfs_root_ses = ses;
122*38c8a9a5SSteve French 	return 0;
123*38c8a9a5SSteve French }
124*38c8a9a5SSteve French 
125*38c8a9a5SSteve French static int get_dfs_conn(struct cifs_mount_ctx *mnt_ctx, const char *ref_path, const char *full_path,
126*38c8a9a5SSteve French 			const struct dfs_cache_tgt_iterator *tit)
127*38c8a9a5SSteve French {
128*38c8a9a5SSteve French 	struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
129*38c8a9a5SSteve French 	struct dfs_info3_param ref = {};
130*38c8a9a5SSteve French 	bool is_refsrv;
131*38c8a9a5SSteve French 	int rc, rc2;
132*38c8a9a5SSteve French 
133*38c8a9a5SSteve French 	rc = dfs_cache_get_tgt_referral(ref_path + 1, tit, &ref);
134*38c8a9a5SSteve French 	if (rc)
135*38c8a9a5SSteve French 		return rc;
136*38c8a9a5SSteve French 
137*38c8a9a5SSteve French 	rc = dfs_parse_target_referral(full_path + 1, &ref, ctx);
138*38c8a9a5SSteve French 	if (rc)
139*38c8a9a5SSteve French 		goto out;
140*38c8a9a5SSteve French 
141*38c8a9a5SSteve French 	cifs_mount_put_conns(mnt_ctx);
142*38c8a9a5SSteve French 	rc = get_session(mnt_ctx, ref_path);
143*38c8a9a5SSteve French 	if (rc)
144*38c8a9a5SSteve French 		goto out;
145*38c8a9a5SSteve French 
146*38c8a9a5SSteve French 	is_refsrv = !!(ref.flags & DFSREF_REFERRAL_SERVER);
147*38c8a9a5SSteve French 
148*38c8a9a5SSteve French 	rc = -EREMOTE;
149*38c8a9a5SSteve French 	if (ref.flags & DFSREF_STORAGE_SERVER) {
150*38c8a9a5SSteve French 		rc = cifs_mount_get_tcon(mnt_ctx);
151*38c8a9a5SSteve French 		if (rc)
152*38c8a9a5SSteve French 			goto out;
153*38c8a9a5SSteve French 
154*38c8a9a5SSteve French 		/* some servers may not advertise referral capability under ref.flags */
155*38c8a9a5SSteve French 		is_refsrv |= is_tcon_dfs(mnt_ctx->tcon);
156*38c8a9a5SSteve French 
157*38c8a9a5SSteve French 		rc = cifs_is_path_remote(mnt_ctx);
158*38c8a9a5SSteve French 	}
159*38c8a9a5SSteve French 
160*38c8a9a5SSteve French 	dfs_cache_noreq_update_tgthint(ref_path + 1, tit);
161*38c8a9a5SSteve French 
162*38c8a9a5SSteve French 	if (rc == -EREMOTE && is_refsrv) {
163*38c8a9a5SSteve French 		rc2 = add_root_smb_session(mnt_ctx);
164*38c8a9a5SSteve French 		if (rc2)
165*38c8a9a5SSteve French 			rc = rc2;
166*38c8a9a5SSteve French 	}
167*38c8a9a5SSteve French 
168*38c8a9a5SSteve French out:
169*38c8a9a5SSteve French 	free_dfs_info_param(&ref);
170*38c8a9a5SSteve French 	return rc;
171*38c8a9a5SSteve French }
172*38c8a9a5SSteve French 
173*38c8a9a5SSteve French static int __dfs_mount_share(struct cifs_mount_ctx *mnt_ctx)
174*38c8a9a5SSteve French {
175*38c8a9a5SSteve French 	struct cifs_sb_info *cifs_sb = mnt_ctx->cifs_sb;
176*38c8a9a5SSteve French 	struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
177*38c8a9a5SSteve French 	char *ref_path = NULL, *full_path = NULL;
178*38c8a9a5SSteve French 	struct dfs_cache_tgt_iterator *tit;
179*38c8a9a5SSteve French 	struct TCP_Server_Info *server;
180*38c8a9a5SSteve French 	struct cifs_tcon *tcon;
181*38c8a9a5SSteve French 	char *origin_fullpath = NULL;
182*38c8a9a5SSteve French 	int num_links = 0;
183*38c8a9a5SSteve French 	int rc;
184*38c8a9a5SSteve French 
185*38c8a9a5SSteve French 	ref_path = dfs_get_path(cifs_sb, ctx->UNC);
186*38c8a9a5SSteve French 	if (IS_ERR(ref_path))
187*38c8a9a5SSteve French 		return PTR_ERR(ref_path);
188*38c8a9a5SSteve French 
189*38c8a9a5SSteve French 	full_path = build_unc_path_to_root(ctx, cifs_sb, true);
190*38c8a9a5SSteve French 	if (IS_ERR(full_path)) {
191*38c8a9a5SSteve French 		rc = PTR_ERR(full_path);
192*38c8a9a5SSteve French 		full_path = NULL;
193*38c8a9a5SSteve French 		goto out;
194*38c8a9a5SSteve French 	}
195*38c8a9a5SSteve French 
196*38c8a9a5SSteve French 	origin_fullpath = kstrdup(full_path, GFP_KERNEL);
197*38c8a9a5SSteve French 	if (!origin_fullpath) {
198*38c8a9a5SSteve French 		rc = -ENOMEM;
199*38c8a9a5SSteve French 		goto out;
200*38c8a9a5SSteve French 	}
201*38c8a9a5SSteve French 
202*38c8a9a5SSteve French 	do {
203*38c8a9a5SSteve French 		struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl);
204*38c8a9a5SSteve French 
205*38c8a9a5SSteve French 		rc = dfs_get_referral(mnt_ctx, ref_path + 1, NULL, &tl);
206*38c8a9a5SSteve French 		if (rc)
207*38c8a9a5SSteve French 			break;
208*38c8a9a5SSteve French 
209*38c8a9a5SSteve French 		tit = dfs_cache_get_tgt_iterator(&tl);
210*38c8a9a5SSteve French 		if (!tit) {
211*38c8a9a5SSteve French 			cifs_dbg(VFS, "%s: dfs referral (%s) with no targets\n", __func__,
212*38c8a9a5SSteve French 				 ref_path + 1);
213*38c8a9a5SSteve French 			rc = -ENOENT;
214*38c8a9a5SSteve French 			dfs_cache_free_tgts(&tl);
215*38c8a9a5SSteve French 			break;
216*38c8a9a5SSteve French 		}
217*38c8a9a5SSteve French 
218*38c8a9a5SSteve French 		do {
219*38c8a9a5SSteve French 			rc = get_dfs_conn(mnt_ctx, ref_path, full_path, tit);
220*38c8a9a5SSteve French 			if (!rc)
221*38c8a9a5SSteve French 				break;
222*38c8a9a5SSteve French 			if (rc == -EREMOTE) {
223*38c8a9a5SSteve French 				if (++num_links > MAX_NESTED_LINKS) {
224*38c8a9a5SSteve French 					rc = -ELOOP;
225*38c8a9a5SSteve French 					break;
226*38c8a9a5SSteve French 				}
227*38c8a9a5SSteve French 				kfree(ref_path);
228*38c8a9a5SSteve French 				kfree(full_path);
229*38c8a9a5SSteve French 				ref_path = full_path = NULL;
230*38c8a9a5SSteve French 
231*38c8a9a5SSteve French 				full_path = build_unc_path_to_root(ctx, cifs_sb, true);
232*38c8a9a5SSteve French 				if (IS_ERR(full_path)) {
233*38c8a9a5SSteve French 					rc = PTR_ERR(full_path);
234*38c8a9a5SSteve French 					full_path = NULL;
235*38c8a9a5SSteve French 				} else {
236*38c8a9a5SSteve French 					ref_path = dfs_get_path(cifs_sb, full_path);
237*38c8a9a5SSteve French 					if (IS_ERR(ref_path)) {
238*38c8a9a5SSteve French 						rc = PTR_ERR(ref_path);
239*38c8a9a5SSteve French 						ref_path = NULL;
240*38c8a9a5SSteve French 					}
241*38c8a9a5SSteve French 				}
242*38c8a9a5SSteve French 				break;
243*38c8a9a5SSteve French 			}
244*38c8a9a5SSteve French 		} while ((tit = dfs_cache_get_next_tgt(&tl, tit)));
245*38c8a9a5SSteve French 		dfs_cache_free_tgts(&tl);
246*38c8a9a5SSteve French 	} while (rc == -EREMOTE);
247*38c8a9a5SSteve French 
248*38c8a9a5SSteve French 	if (!rc) {
249*38c8a9a5SSteve French 		server = mnt_ctx->server;
250*38c8a9a5SSteve French 		tcon = mnt_ctx->tcon;
251*38c8a9a5SSteve French 
252*38c8a9a5SSteve French 		mutex_lock(&server->refpath_lock);
253*38c8a9a5SSteve French 		spin_lock(&server->srv_lock);
254*38c8a9a5SSteve French 		if (!server->origin_fullpath) {
255*38c8a9a5SSteve French 			server->origin_fullpath = origin_fullpath;
256*38c8a9a5SSteve French 			origin_fullpath = NULL;
257*38c8a9a5SSteve French 		}
258*38c8a9a5SSteve French 		spin_unlock(&server->srv_lock);
259*38c8a9a5SSteve French 		mutex_unlock(&server->refpath_lock);
260*38c8a9a5SSteve French 
261*38c8a9a5SSteve French 		if (list_empty(&tcon->dfs_ses_list)) {
262*38c8a9a5SSteve French 			list_replace_init(&mnt_ctx->dfs_ses_list,
263*38c8a9a5SSteve French 					  &tcon->dfs_ses_list);
264*38c8a9a5SSteve French 			queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work,
265*38c8a9a5SSteve French 					   dfs_cache_get_ttl() * HZ);
266*38c8a9a5SSteve French 		} else {
267*38c8a9a5SSteve French 			dfs_put_root_smb_sessions(&mnt_ctx->dfs_ses_list);
268*38c8a9a5SSteve French 		}
269*38c8a9a5SSteve French 	}
270*38c8a9a5SSteve French 
271*38c8a9a5SSteve French out:
272*38c8a9a5SSteve French 	kfree(origin_fullpath);
273*38c8a9a5SSteve French 	kfree(ref_path);
274*38c8a9a5SSteve French 	kfree(full_path);
275*38c8a9a5SSteve French 	return rc;
276*38c8a9a5SSteve French }
277*38c8a9a5SSteve French 
278*38c8a9a5SSteve French int dfs_mount_share(struct cifs_mount_ctx *mnt_ctx, bool *isdfs)
279*38c8a9a5SSteve French {
280*38c8a9a5SSteve French 	struct smb3_fs_context *ctx = mnt_ctx->fs_ctx;
281*38c8a9a5SSteve French 	struct cifs_ses *ses;
282*38c8a9a5SSteve French 	char *source = ctx->source;
283*38c8a9a5SSteve French 	bool nodfs = ctx->nodfs;
284*38c8a9a5SSteve French 	int rc;
285*38c8a9a5SSteve French 
286*38c8a9a5SSteve French 	*isdfs = false;
287*38c8a9a5SSteve French 	/* Temporarily set @ctx->source to NULL as we're not matching DFS
288*38c8a9a5SSteve French 	 * superblocks yet.  See cifs_match_super() and match_server().
289*38c8a9a5SSteve French 	 */
290*38c8a9a5SSteve French 	ctx->source = NULL;
291*38c8a9a5SSteve French 	rc = get_session(mnt_ctx, NULL);
292*38c8a9a5SSteve French 	if (rc)
293*38c8a9a5SSteve French 		goto out;
294*38c8a9a5SSteve French 
295*38c8a9a5SSteve French 	ctx->dfs_root_ses = mnt_ctx->ses;
296*38c8a9a5SSteve French 	/*
297*38c8a9a5SSteve French 	 * If called with 'nodfs' mount option, then skip DFS resolving.  Otherwise unconditionally
298*38c8a9a5SSteve French 	 * try to get an DFS referral (even cached) to determine whether it is an DFS mount.
299*38c8a9a5SSteve French 	 *
300*38c8a9a5SSteve French 	 * Skip prefix path to provide support for DFS referrals from w2k8 servers which don't seem
301*38c8a9a5SSteve French 	 * to respond with PATH_NOT_COVERED to requests that include the prefix.
302*38c8a9a5SSteve French 	 */
303*38c8a9a5SSteve French 	if (!nodfs) {
304*38c8a9a5SSteve French 		rc = dfs_get_referral(mnt_ctx, ctx->UNC + 1, NULL, NULL);
305*38c8a9a5SSteve French 		if (rc) {
306*38c8a9a5SSteve French 			if (rc != -ENOENT && rc != -EOPNOTSUPP && rc != -EIO)
307*38c8a9a5SSteve French 				goto out;
308*38c8a9a5SSteve French 			nodfs = true;
309*38c8a9a5SSteve French 		}
310*38c8a9a5SSteve French 	}
311*38c8a9a5SSteve French 	if (nodfs) {
312*38c8a9a5SSteve French 		rc = cifs_mount_get_tcon(mnt_ctx);
313*38c8a9a5SSteve French 		if (!rc)
314*38c8a9a5SSteve French 			rc = cifs_is_path_remote(mnt_ctx);
315*38c8a9a5SSteve French 		goto out;
316*38c8a9a5SSteve French 	}
317*38c8a9a5SSteve French 
318*38c8a9a5SSteve French 	*isdfs = true;
319*38c8a9a5SSteve French 	/*
320*38c8a9a5SSteve French 	 * Prevent DFS root session of being put in the first call to
321*38c8a9a5SSteve French 	 * cifs_mount_put_conns().  If another DFS root server was not found
322*38c8a9a5SSteve French 	 * while chasing the referrals (@ctx->dfs_root_ses == @ses), then we
323*38c8a9a5SSteve French 	 * can safely put extra refcount of @ses.
324*38c8a9a5SSteve French 	 */
325*38c8a9a5SSteve French 	ses = mnt_ctx->ses;
326*38c8a9a5SSteve French 	mnt_ctx->ses = NULL;
327*38c8a9a5SSteve French 	mnt_ctx->server = NULL;
328*38c8a9a5SSteve French 	rc = __dfs_mount_share(mnt_ctx);
329*38c8a9a5SSteve French 	if (ses == ctx->dfs_root_ses)
330*38c8a9a5SSteve French 		cifs_put_smb_ses(ses);
331*38c8a9a5SSteve French out:
332*38c8a9a5SSteve French 	/*
333*38c8a9a5SSteve French 	 * Restore previous value of @ctx->source so DFS superblock can be
334*38c8a9a5SSteve French 	 * matched in cifs_match_super().
335*38c8a9a5SSteve French 	 */
336*38c8a9a5SSteve French 	ctx->source = source;
337*38c8a9a5SSteve French 	return rc;
338*38c8a9a5SSteve French }
339*38c8a9a5SSteve French 
340*38c8a9a5SSteve French /* Update dfs referral path of superblock */
341*38c8a9a5SSteve French static int update_server_fullpath(struct TCP_Server_Info *server, struct cifs_sb_info *cifs_sb,
342*38c8a9a5SSteve French 				  const char *target)
343*38c8a9a5SSteve French {
344*38c8a9a5SSteve French 	int rc = 0;
345*38c8a9a5SSteve French 	size_t len = strlen(target);
346*38c8a9a5SSteve French 	char *refpath, *npath;
347*38c8a9a5SSteve French 
348*38c8a9a5SSteve French 	if (unlikely(len < 2 || *target != '\\'))
349*38c8a9a5SSteve French 		return -EINVAL;
350*38c8a9a5SSteve French 
351*38c8a9a5SSteve French 	if (target[1] == '\\') {
352*38c8a9a5SSteve French 		len += 1;
353*38c8a9a5SSteve French 		refpath = kmalloc(len, GFP_KERNEL);
354*38c8a9a5SSteve French 		if (!refpath)
355*38c8a9a5SSteve French 			return -ENOMEM;
356*38c8a9a5SSteve French 
357*38c8a9a5SSteve French 		scnprintf(refpath, len, "%s", target);
358*38c8a9a5SSteve French 	} else {
359*38c8a9a5SSteve French 		len += sizeof("\\");
360*38c8a9a5SSteve French 		refpath = kmalloc(len, GFP_KERNEL);
361*38c8a9a5SSteve French 		if (!refpath)
362*38c8a9a5SSteve French 			return -ENOMEM;
363*38c8a9a5SSteve French 
364*38c8a9a5SSteve French 		scnprintf(refpath, len, "\\%s", target);
365*38c8a9a5SSteve French 	}
366*38c8a9a5SSteve French 
367*38c8a9a5SSteve French 	npath = dfs_cache_canonical_path(refpath, cifs_sb->local_nls, cifs_remap(cifs_sb));
368*38c8a9a5SSteve French 	kfree(refpath);
369*38c8a9a5SSteve French 
370*38c8a9a5SSteve French 	if (IS_ERR(npath)) {
371*38c8a9a5SSteve French 		rc = PTR_ERR(npath);
372*38c8a9a5SSteve French 	} else {
373*38c8a9a5SSteve French 		mutex_lock(&server->refpath_lock);
374*38c8a9a5SSteve French 		spin_lock(&server->srv_lock);
375*38c8a9a5SSteve French 		kfree(server->leaf_fullpath);
376*38c8a9a5SSteve French 		server->leaf_fullpath = npath;
377*38c8a9a5SSteve French 		spin_unlock(&server->srv_lock);
378*38c8a9a5SSteve French 		mutex_unlock(&server->refpath_lock);
379*38c8a9a5SSteve French 	}
380*38c8a9a5SSteve French 	return rc;
381*38c8a9a5SSteve French }
382*38c8a9a5SSteve French 
383*38c8a9a5SSteve French static int target_share_matches_server(struct TCP_Server_Info *server, char *share,
384*38c8a9a5SSteve French 				       bool *target_match)
385*38c8a9a5SSteve French {
386*38c8a9a5SSteve French 	int rc = 0;
387*38c8a9a5SSteve French 	const char *dfs_host;
388*38c8a9a5SSteve French 	size_t dfs_host_len;
389*38c8a9a5SSteve French 
390*38c8a9a5SSteve French 	*target_match = true;
391*38c8a9a5SSteve French 	extract_unc_hostname(share, &dfs_host, &dfs_host_len);
392*38c8a9a5SSteve French 
393*38c8a9a5SSteve French 	/* Check if hostnames or addresses match */
394*38c8a9a5SSteve French 	cifs_server_lock(server);
395*38c8a9a5SSteve French 	if (dfs_host_len != strlen(server->hostname) ||
396*38c8a9a5SSteve French 	    strncasecmp(dfs_host, server->hostname, dfs_host_len)) {
397*38c8a9a5SSteve French 		cifs_dbg(FYI, "%s: %.*s doesn't match %s\n", __func__,
398*38c8a9a5SSteve French 			 (int)dfs_host_len, dfs_host, server->hostname);
399*38c8a9a5SSteve French 		rc = match_target_ip(server, dfs_host, dfs_host_len, target_match);
400*38c8a9a5SSteve French 		if (rc)
401*38c8a9a5SSteve French 			cifs_dbg(VFS, "%s: failed to match target ip: %d\n", __func__, rc);
402*38c8a9a5SSteve French 	}
403*38c8a9a5SSteve French 	cifs_server_unlock(server);
404*38c8a9a5SSteve French 	return rc;
405*38c8a9a5SSteve French }
406*38c8a9a5SSteve French 
407*38c8a9a5SSteve French static void __tree_connect_ipc(const unsigned int xid, char *tree,
408*38c8a9a5SSteve French 			       struct cifs_sb_info *cifs_sb,
409*38c8a9a5SSteve French 			       struct cifs_ses *ses)
410*38c8a9a5SSteve French {
411*38c8a9a5SSteve French 	struct TCP_Server_Info *server = ses->server;
412*38c8a9a5SSteve French 	struct cifs_tcon *tcon = ses->tcon_ipc;
413*38c8a9a5SSteve French 	int rc;
414*38c8a9a5SSteve French 
415*38c8a9a5SSteve French 	spin_lock(&ses->ses_lock);
416*38c8a9a5SSteve French 	spin_lock(&ses->chan_lock);
417*38c8a9a5SSteve French 	if (cifs_chan_needs_reconnect(ses, server) ||
418*38c8a9a5SSteve French 	    ses->ses_status != SES_GOOD) {
419*38c8a9a5SSteve French 		spin_unlock(&ses->chan_lock);
420*38c8a9a5SSteve French 		spin_unlock(&ses->ses_lock);
421*38c8a9a5SSteve French 		cifs_server_dbg(FYI, "%s: skipping ipc reconnect due to disconnected ses\n",
422*38c8a9a5SSteve French 				__func__);
423*38c8a9a5SSteve French 		return;
424*38c8a9a5SSteve French 	}
425*38c8a9a5SSteve French 	spin_unlock(&ses->chan_lock);
426*38c8a9a5SSteve French 	spin_unlock(&ses->ses_lock);
427*38c8a9a5SSteve French 
428*38c8a9a5SSteve French 	cifs_server_lock(server);
429*38c8a9a5SSteve French 	scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname);
430*38c8a9a5SSteve French 	cifs_server_unlock(server);
431*38c8a9a5SSteve French 
432*38c8a9a5SSteve French 	rc = server->ops->tree_connect(xid, ses, tree, tcon,
433*38c8a9a5SSteve French 				       cifs_sb->local_nls);
434*38c8a9a5SSteve French 	cifs_server_dbg(FYI, "%s: tree_reconnect %s: %d\n", __func__, tree, rc);
435*38c8a9a5SSteve French 	spin_lock(&tcon->tc_lock);
436*38c8a9a5SSteve French 	if (rc) {
437*38c8a9a5SSteve French 		tcon->status = TID_NEED_TCON;
438*38c8a9a5SSteve French 	} else {
439*38c8a9a5SSteve French 		tcon->status = TID_GOOD;
440*38c8a9a5SSteve French 		tcon->need_reconnect = false;
441*38c8a9a5SSteve French 	}
442*38c8a9a5SSteve French 	spin_unlock(&tcon->tc_lock);
443*38c8a9a5SSteve French }
444*38c8a9a5SSteve French 
445*38c8a9a5SSteve French static void tree_connect_ipc(const unsigned int xid, char *tree,
446*38c8a9a5SSteve French 			     struct cifs_sb_info *cifs_sb,
447*38c8a9a5SSteve French 			     struct cifs_tcon *tcon)
448*38c8a9a5SSteve French {
449*38c8a9a5SSteve French 	struct cifs_ses *ses = tcon->ses;
450*38c8a9a5SSteve French 
451*38c8a9a5SSteve French 	__tree_connect_ipc(xid, tree, cifs_sb, ses);
452*38c8a9a5SSteve French 	__tree_connect_ipc(xid, tree, cifs_sb, CIFS_DFS_ROOT_SES(ses));
453*38c8a9a5SSteve French }
454*38c8a9a5SSteve French 
455*38c8a9a5SSteve French static int __tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon,
456*38c8a9a5SSteve French 				     struct cifs_sb_info *cifs_sb, char *tree, bool islink,
457*38c8a9a5SSteve French 				     struct dfs_cache_tgt_list *tl)
458*38c8a9a5SSteve French {
459*38c8a9a5SSteve French 	int rc;
460*38c8a9a5SSteve French 	struct TCP_Server_Info *server = tcon->ses->server;
461*38c8a9a5SSteve French 	const struct smb_version_operations *ops = server->ops;
462*38c8a9a5SSteve French 	struct cifs_ses *root_ses = CIFS_DFS_ROOT_SES(tcon->ses);
463*38c8a9a5SSteve French 	char *share = NULL, *prefix = NULL;
464*38c8a9a5SSteve French 	struct dfs_cache_tgt_iterator *tit;
465*38c8a9a5SSteve French 	bool target_match;
466*38c8a9a5SSteve French 
467*38c8a9a5SSteve French 	tit = dfs_cache_get_tgt_iterator(tl);
468*38c8a9a5SSteve French 	if (!tit) {
469*38c8a9a5SSteve French 		rc = -ENOENT;
470*38c8a9a5SSteve French 		goto out;
471*38c8a9a5SSteve French 	}
472*38c8a9a5SSteve French 
473*38c8a9a5SSteve French 	/* Try to tree connect to all dfs targets */
474*38c8a9a5SSteve French 	for (; tit; tit = dfs_cache_get_next_tgt(tl, tit)) {
475*38c8a9a5SSteve French 		const char *target = dfs_cache_get_tgt_name(tit);
476*38c8a9a5SSteve French 		struct dfs_cache_tgt_list ntl = DFS_CACHE_TGT_LIST_INIT(ntl);
477*38c8a9a5SSteve French 
478*38c8a9a5SSteve French 		kfree(share);
479*38c8a9a5SSteve French 		kfree(prefix);
480*38c8a9a5SSteve French 		share = prefix = NULL;
481*38c8a9a5SSteve French 
482*38c8a9a5SSteve French 		/* Check if share matches with tcp ses */
483*38c8a9a5SSteve French 		rc = dfs_cache_get_tgt_share(server->leaf_fullpath + 1, tit, &share, &prefix);
484*38c8a9a5SSteve French 		if (rc) {
485*38c8a9a5SSteve French 			cifs_dbg(VFS, "%s: failed to parse target share: %d\n", __func__, rc);
486*38c8a9a5SSteve French 			break;
487*38c8a9a5SSteve French 		}
488*38c8a9a5SSteve French 
489*38c8a9a5SSteve French 		rc = target_share_matches_server(server, share, &target_match);
490*38c8a9a5SSteve French 		if (rc)
491*38c8a9a5SSteve French 			break;
492*38c8a9a5SSteve French 		if (!target_match) {
493*38c8a9a5SSteve French 			rc = -EHOSTUNREACH;
494*38c8a9a5SSteve French 			continue;
495*38c8a9a5SSteve French 		}
496*38c8a9a5SSteve French 
497*38c8a9a5SSteve French 		dfs_cache_noreq_update_tgthint(server->leaf_fullpath + 1, tit);
498*38c8a9a5SSteve French 		tree_connect_ipc(xid, tree, cifs_sb, tcon);
499*38c8a9a5SSteve French 
500*38c8a9a5SSteve French 		scnprintf(tree, MAX_TREE_SIZE, "\\%s", share);
501*38c8a9a5SSteve French 		if (!islink) {
502*38c8a9a5SSteve French 			rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls);
503*38c8a9a5SSteve French 			break;
504*38c8a9a5SSteve French 		}
505*38c8a9a5SSteve French 
506*38c8a9a5SSteve French 		/*
507*38c8a9a5SSteve French 		 * If no dfs referrals were returned from link target, then just do a TREE_CONNECT
508*38c8a9a5SSteve French 		 * to it.  Otherwise, cache the dfs referral and then mark current tcp ses for
509*38c8a9a5SSteve French 		 * reconnect so either the demultiplex thread or the echo worker will reconnect to
510*38c8a9a5SSteve French 		 * newly resolved target.
511*38c8a9a5SSteve French 		 */
512*38c8a9a5SSteve French 		if (dfs_cache_find(xid, root_ses, cifs_sb->local_nls, cifs_remap(cifs_sb), target,
513*38c8a9a5SSteve French 				   NULL, &ntl)) {
514*38c8a9a5SSteve French 			rc = ops->tree_connect(xid, tcon->ses, tree, tcon, cifs_sb->local_nls);
515*38c8a9a5SSteve French 			if (rc)
516*38c8a9a5SSteve French 				continue;
517*38c8a9a5SSteve French 
518*38c8a9a5SSteve French 			rc = cifs_update_super_prepath(cifs_sb, prefix);
519*38c8a9a5SSteve French 		} else {
520*38c8a9a5SSteve French 			/* Target is another dfs share */
521*38c8a9a5SSteve French 			rc = update_server_fullpath(server, cifs_sb, target);
522*38c8a9a5SSteve French 			dfs_cache_free_tgts(tl);
523*38c8a9a5SSteve French 
524*38c8a9a5SSteve French 			if (!rc) {
525*38c8a9a5SSteve French 				rc = -EREMOTE;
526*38c8a9a5SSteve French 				list_replace_init(&ntl.tl_list, &tl->tl_list);
527*38c8a9a5SSteve French 			} else
528*38c8a9a5SSteve French 				dfs_cache_free_tgts(&ntl);
529*38c8a9a5SSteve French 		}
530*38c8a9a5SSteve French 		break;
531*38c8a9a5SSteve French 	}
532*38c8a9a5SSteve French 
533*38c8a9a5SSteve French out:
534*38c8a9a5SSteve French 	kfree(share);
535*38c8a9a5SSteve French 	kfree(prefix);
536*38c8a9a5SSteve French 
537*38c8a9a5SSteve French 	return rc;
538*38c8a9a5SSteve French }
539*38c8a9a5SSteve French 
540*38c8a9a5SSteve French static int tree_connect_dfs_target(const unsigned int xid, struct cifs_tcon *tcon,
541*38c8a9a5SSteve French 				   struct cifs_sb_info *cifs_sb, char *tree, bool islink,
542*38c8a9a5SSteve French 				   struct dfs_cache_tgt_list *tl)
543*38c8a9a5SSteve French {
544*38c8a9a5SSteve French 	int rc;
545*38c8a9a5SSteve French 	int num_links = 0;
546*38c8a9a5SSteve French 	struct TCP_Server_Info *server = tcon->ses->server;
547*38c8a9a5SSteve French 	char *old_fullpath = server->leaf_fullpath;
548*38c8a9a5SSteve French 
549*38c8a9a5SSteve French 	do {
550*38c8a9a5SSteve French 		rc = __tree_connect_dfs_target(xid, tcon, cifs_sb, tree, islink, tl);
551*38c8a9a5SSteve French 		if (!rc || rc != -EREMOTE)
552*38c8a9a5SSteve French 			break;
553*38c8a9a5SSteve French 	} while (rc = -ELOOP, ++num_links < MAX_NESTED_LINKS);
554*38c8a9a5SSteve French 	/*
555*38c8a9a5SSteve French 	 * If we couldn't tree connect to any targets from last referral path, then
556*38c8a9a5SSteve French 	 * retry it from newly resolved dfs referral.
557*38c8a9a5SSteve French 	 */
558*38c8a9a5SSteve French 	if (rc && server->leaf_fullpath != old_fullpath)
559*38c8a9a5SSteve French 		cifs_signal_cifsd_for_reconnect(server, true);
560*38c8a9a5SSteve French 
561*38c8a9a5SSteve French 	dfs_cache_free_tgts(tl);
562*38c8a9a5SSteve French 	return rc;
563*38c8a9a5SSteve French }
564*38c8a9a5SSteve French 
565*38c8a9a5SSteve French int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const struct nls_table *nlsc)
566*38c8a9a5SSteve French {
567*38c8a9a5SSteve French 	int rc;
568*38c8a9a5SSteve French 	struct TCP_Server_Info *server = tcon->ses->server;
569*38c8a9a5SSteve French 	const struct smb_version_operations *ops = server->ops;
570*38c8a9a5SSteve French 	struct super_block *sb = NULL;
571*38c8a9a5SSteve French 	struct cifs_sb_info *cifs_sb;
572*38c8a9a5SSteve French 	struct dfs_cache_tgt_list tl = DFS_CACHE_TGT_LIST_INIT(tl);
573*38c8a9a5SSteve French 	char *tree;
574*38c8a9a5SSteve French 	struct dfs_info3_param ref = {0};
575*38c8a9a5SSteve French 
576*38c8a9a5SSteve French 	/* only send once per connect */
577*38c8a9a5SSteve French 	spin_lock(&tcon->tc_lock);
578*38c8a9a5SSteve French 	if (tcon->status != TID_NEW &&
579*38c8a9a5SSteve French 	    tcon->status != TID_NEED_TCON) {
580*38c8a9a5SSteve French 		spin_unlock(&tcon->tc_lock);
581*38c8a9a5SSteve French 		return -EHOSTDOWN;
582*38c8a9a5SSteve French 	}
583*38c8a9a5SSteve French 
584*38c8a9a5SSteve French 	if (tcon->status == TID_GOOD) {
585*38c8a9a5SSteve French 		spin_unlock(&tcon->tc_lock);
586*38c8a9a5SSteve French 		return 0;
587*38c8a9a5SSteve French 	}
588*38c8a9a5SSteve French 	tcon->status = TID_IN_TCON;
589*38c8a9a5SSteve French 	spin_unlock(&tcon->tc_lock);
590*38c8a9a5SSteve French 
591*38c8a9a5SSteve French 	tree = kzalloc(MAX_TREE_SIZE, GFP_KERNEL);
592*38c8a9a5SSteve French 	if (!tree) {
593*38c8a9a5SSteve French 		rc = -ENOMEM;
594*38c8a9a5SSteve French 		goto out;
595*38c8a9a5SSteve French 	}
596*38c8a9a5SSteve French 
597*38c8a9a5SSteve French 	if (tcon->ipc) {
598*38c8a9a5SSteve French 		cifs_server_lock(server);
599*38c8a9a5SSteve French 		scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", server->hostname);
600*38c8a9a5SSteve French 		cifs_server_unlock(server);
601*38c8a9a5SSteve French 		rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc);
602*38c8a9a5SSteve French 		goto out;
603*38c8a9a5SSteve French 	}
604*38c8a9a5SSteve French 
605*38c8a9a5SSteve French 	sb = cifs_get_tcp_super(server);
606*38c8a9a5SSteve French 	if (IS_ERR(sb)) {
607*38c8a9a5SSteve French 		rc = PTR_ERR(sb);
608*38c8a9a5SSteve French 		cifs_dbg(VFS, "%s: could not find superblock: %d\n", __func__, rc);
609*38c8a9a5SSteve French 		goto out;
610*38c8a9a5SSteve French 	}
611*38c8a9a5SSteve French 
612*38c8a9a5SSteve French 	cifs_sb = CIFS_SB(sb);
613*38c8a9a5SSteve French 
614*38c8a9a5SSteve French 	/* If it is not dfs or there was no cached dfs referral, then reconnect to same share */
615*38c8a9a5SSteve French 	if (!server->leaf_fullpath ||
616*38c8a9a5SSteve French 	    dfs_cache_noreq_find(server->leaf_fullpath + 1, &ref, &tl)) {
617*38c8a9a5SSteve French 		rc = ops->tree_connect(xid, tcon->ses, tcon->tree_name, tcon, cifs_sb->local_nls);
618*38c8a9a5SSteve French 		goto out;
619*38c8a9a5SSteve French 	}
620*38c8a9a5SSteve French 
621*38c8a9a5SSteve French 	rc = tree_connect_dfs_target(xid, tcon, cifs_sb, tree, ref.server_type == DFS_TYPE_LINK,
622*38c8a9a5SSteve French 				     &tl);
623*38c8a9a5SSteve French 	free_dfs_info_param(&ref);
624*38c8a9a5SSteve French 
625*38c8a9a5SSteve French out:
626*38c8a9a5SSteve French 	kfree(tree);
627*38c8a9a5SSteve French 	cifs_put_tcp_super(sb);
628*38c8a9a5SSteve French 
629*38c8a9a5SSteve French 	if (rc) {
630*38c8a9a5SSteve French 		spin_lock(&tcon->tc_lock);
631*38c8a9a5SSteve French 		if (tcon->status == TID_IN_TCON)
632*38c8a9a5SSteve French 			tcon->status = TID_NEED_TCON;
633*38c8a9a5SSteve French 		spin_unlock(&tcon->tc_lock);
634*38c8a9a5SSteve French 	} else {
635*38c8a9a5SSteve French 		spin_lock(&tcon->tc_lock);
636*38c8a9a5SSteve French 		if (tcon->status == TID_IN_TCON)
637*38c8a9a5SSteve French 			tcon->status = TID_GOOD;
638*38c8a9a5SSteve French 		spin_unlock(&tcon->tc_lock);
639*38c8a9a5SSteve French 		tcon->need_reconnect = false;
640*38c8a9a5SSteve French 	}
641*38c8a9a5SSteve French 
642*38c8a9a5SSteve French 	return rc;
643*38c8a9a5SSteve French }
644