xref: /openbmc/linux/fs/smb/client/dfs_cache.c (revision 38c8a9a52082579090e34c033d439ed2cd1a462d)
1*38c8a9a5SSteve French // SPDX-License-Identifier: GPL-2.0
2*38c8a9a5SSteve French /*
3*38c8a9a5SSteve French  * DFS referral cache routines
4*38c8a9a5SSteve French  *
5*38c8a9a5SSteve French  * Copyright (c) 2018-2019 Paulo Alcantara <palcantara@suse.de>
6*38c8a9a5SSteve French  */
7*38c8a9a5SSteve French 
8*38c8a9a5SSteve French #include <linux/jhash.h>
9*38c8a9a5SSteve French #include <linux/ktime.h>
10*38c8a9a5SSteve French #include <linux/slab.h>
11*38c8a9a5SSteve French #include <linux/proc_fs.h>
12*38c8a9a5SSteve French #include <linux/nls.h>
13*38c8a9a5SSteve French #include <linux/workqueue.h>
14*38c8a9a5SSteve French #include <linux/uuid.h>
15*38c8a9a5SSteve French #include "cifsglob.h"
16*38c8a9a5SSteve French #include "smb2pdu.h"
17*38c8a9a5SSteve French #include "smb2proto.h"
18*38c8a9a5SSteve French #include "cifsproto.h"
19*38c8a9a5SSteve French #include "cifs_debug.h"
20*38c8a9a5SSteve French #include "cifs_unicode.h"
21*38c8a9a5SSteve French #include "smb2glob.h"
22*38c8a9a5SSteve French #include "dns_resolve.h"
23*38c8a9a5SSteve French #include "dfs.h"
24*38c8a9a5SSteve French 
25*38c8a9a5SSteve French #include "dfs_cache.h"
26*38c8a9a5SSteve French 
27*38c8a9a5SSteve French #define CACHE_HTABLE_SIZE	32
28*38c8a9a5SSteve French #define CACHE_MAX_ENTRIES	64
29*38c8a9a5SSteve French #define CACHE_MIN_TTL		120 /* 2 minutes */
30*38c8a9a5SSteve French #define CACHE_DEFAULT_TTL	300 /* 5 minutes */
31*38c8a9a5SSteve French 
32*38c8a9a5SSteve French #define IS_DFS_INTERLINK(v) (((v) & DFSREF_REFERRAL_SERVER) && !((v) & DFSREF_STORAGE_SERVER))
33*38c8a9a5SSteve French 
34*38c8a9a5SSteve French struct cache_dfs_tgt {
35*38c8a9a5SSteve French 	char *name;
36*38c8a9a5SSteve French 	int path_consumed;
37*38c8a9a5SSteve French 	struct list_head list;
38*38c8a9a5SSteve French };
39*38c8a9a5SSteve French 
40*38c8a9a5SSteve French struct cache_entry {
41*38c8a9a5SSteve French 	struct hlist_node hlist;
42*38c8a9a5SSteve French 	const char *path;
43*38c8a9a5SSteve French 	int hdr_flags; /* RESP_GET_DFS_REFERRAL.ReferralHeaderFlags */
44*38c8a9a5SSteve French 	int ttl; /* DFS_REREFERRAL_V3.TimeToLive */
45*38c8a9a5SSteve French 	int srvtype; /* DFS_REREFERRAL_V3.ServerType */
46*38c8a9a5SSteve French 	int ref_flags; /* DFS_REREFERRAL_V3.ReferralEntryFlags */
47*38c8a9a5SSteve French 	struct timespec64 etime;
48*38c8a9a5SSteve French 	int path_consumed; /* RESP_GET_DFS_REFERRAL.PathConsumed */
49*38c8a9a5SSteve French 	int numtgts;
50*38c8a9a5SSteve French 	struct list_head tlist;
51*38c8a9a5SSteve French 	struct cache_dfs_tgt *tgthint;
52*38c8a9a5SSteve French };
53*38c8a9a5SSteve French 
54*38c8a9a5SSteve French static struct kmem_cache *cache_slab __read_mostly;
55*38c8a9a5SSteve French struct workqueue_struct *dfscache_wq;
56*38c8a9a5SSteve French 
57*38c8a9a5SSteve French atomic_t dfs_cache_ttl;
58*38c8a9a5SSteve French 
59*38c8a9a5SSteve French static struct nls_table *cache_cp;
60*38c8a9a5SSteve French 
61*38c8a9a5SSteve French /*
62*38c8a9a5SSteve French  * Number of entries in the cache
63*38c8a9a5SSteve French  */
64*38c8a9a5SSteve French static atomic_t cache_count;
65*38c8a9a5SSteve French 
66*38c8a9a5SSteve French static struct hlist_head cache_htable[CACHE_HTABLE_SIZE];
67*38c8a9a5SSteve French static DECLARE_RWSEM(htable_rw_lock);
68*38c8a9a5SSteve French 
69*38c8a9a5SSteve French /**
70*38c8a9a5SSteve French  * dfs_cache_canonical_path - get a canonical DFS path
71*38c8a9a5SSteve French  *
72*38c8a9a5SSteve French  * @path: DFS path
73*38c8a9a5SSteve French  * @cp: codepage
74*38c8a9a5SSteve French  * @remap: mapping type
75*38c8a9a5SSteve French  *
76*38c8a9a5SSteve French  * Return canonical path if success, otherwise error.
77*38c8a9a5SSteve French  */
78*38c8a9a5SSteve French char *dfs_cache_canonical_path(const char *path, const struct nls_table *cp, int remap)
79*38c8a9a5SSteve French {
80*38c8a9a5SSteve French 	char *tmp;
81*38c8a9a5SSteve French 	int plen = 0;
82*38c8a9a5SSteve French 	char *npath;
83*38c8a9a5SSteve French 
84*38c8a9a5SSteve French 	if (!path || strlen(path) < 3 || (*path != '\\' && *path != '/'))
85*38c8a9a5SSteve French 		return ERR_PTR(-EINVAL);
86*38c8a9a5SSteve French 
87*38c8a9a5SSteve French 	if (unlikely(strcmp(cp->charset, cache_cp->charset))) {
88*38c8a9a5SSteve French 		tmp = (char *)cifs_strndup_to_utf16(path, strlen(path), &plen, cp, remap);
89*38c8a9a5SSteve French 		if (!tmp) {
90*38c8a9a5SSteve French 			cifs_dbg(VFS, "%s: failed to convert path to utf16\n", __func__);
91*38c8a9a5SSteve French 			return ERR_PTR(-EINVAL);
92*38c8a9a5SSteve French 		}
93*38c8a9a5SSteve French 
94*38c8a9a5SSteve French 		npath = cifs_strndup_from_utf16(tmp, plen, true, cache_cp);
95*38c8a9a5SSteve French 		kfree(tmp);
96*38c8a9a5SSteve French 
97*38c8a9a5SSteve French 		if (!npath) {
98*38c8a9a5SSteve French 			cifs_dbg(VFS, "%s: failed to convert path from utf16\n", __func__);
99*38c8a9a5SSteve French 			return ERR_PTR(-EINVAL);
100*38c8a9a5SSteve French 		}
101*38c8a9a5SSteve French 	} else {
102*38c8a9a5SSteve French 		npath = kstrdup(path, GFP_KERNEL);
103*38c8a9a5SSteve French 		if (!npath)
104*38c8a9a5SSteve French 			return ERR_PTR(-ENOMEM);
105*38c8a9a5SSteve French 	}
106*38c8a9a5SSteve French 	convert_delimiter(npath, '\\');
107*38c8a9a5SSteve French 	return npath;
108*38c8a9a5SSteve French }
109*38c8a9a5SSteve French 
110*38c8a9a5SSteve French static inline bool cache_entry_expired(const struct cache_entry *ce)
111*38c8a9a5SSteve French {
112*38c8a9a5SSteve French 	struct timespec64 ts;
113*38c8a9a5SSteve French 
114*38c8a9a5SSteve French 	ktime_get_coarse_real_ts64(&ts);
115*38c8a9a5SSteve French 	return timespec64_compare(&ts, &ce->etime) >= 0;
116*38c8a9a5SSteve French }
117*38c8a9a5SSteve French 
118*38c8a9a5SSteve French static inline void free_tgts(struct cache_entry *ce)
119*38c8a9a5SSteve French {
120*38c8a9a5SSteve French 	struct cache_dfs_tgt *t, *n;
121*38c8a9a5SSteve French 
122*38c8a9a5SSteve French 	list_for_each_entry_safe(t, n, &ce->tlist, list) {
123*38c8a9a5SSteve French 		list_del(&t->list);
124*38c8a9a5SSteve French 		kfree(t->name);
125*38c8a9a5SSteve French 		kfree(t);
126*38c8a9a5SSteve French 	}
127*38c8a9a5SSteve French }
128*38c8a9a5SSteve French 
129*38c8a9a5SSteve French static inline void flush_cache_ent(struct cache_entry *ce)
130*38c8a9a5SSteve French {
131*38c8a9a5SSteve French 	hlist_del_init(&ce->hlist);
132*38c8a9a5SSteve French 	kfree(ce->path);
133*38c8a9a5SSteve French 	free_tgts(ce);
134*38c8a9a5SSteve French 	atomic_dec(&cache_count);
135*38c8a9a5SSteve French 	kmem_cache_free(cache_slab, ce);
136*38c8a9a5SSteve French }
137*38c8a9a5SSteve French 
138*38c8a9a5SSteve French static void flush_cache_ents(void)
139*38c8a9a5SSteve French {
140*38c8a9a5SSteve French 	int i;
141*38c8a9a5SSteve French 
142*38c8a9a5SSteve French 	for (i = 0; i < CACHE_HTABLE_SIZE; i++) {
143*38c8a9a5SSteve French 		struct hlist_head *l = &cache_htable[i];
144*38c8a9a5SSteve French 		struct hlist_node *n;
145*38c8a9a5SSteve French 		struct cache_entry *ce;
146*38c8a9a5SSteve French 
147*38c8a9a5SSteve French 		hlist_for_each_entry_safe(ce, n, l, hlist) {
148*38c8a9a5SSteve French 			if (!hlist_unhashed(&ce->hlist))
149*38c8a9a5SSteve French 				flush_cache_ent(ce);
150*38c8a9a5SSteve French 		}
151*38c8a9a5SSteve French 	}
152*38c8a9a5SSteve French }
153*38c8a9a5SSteve French 
154*38c8a9a5SSteve French /*
155*38c8a9a5SSteve French  * dfs cache /proc file
156*38c8a9a5SSteve French  */
157*38c8a9a5SSteve French static int dfscache_proc_show(struct seq_file *m, void *v)
158*38c8a9a5SSteve French {
159*38c8a9a5SSteve French 	int i;
160*38c8a9a5SSteve French 	struct cache_entry *ce;
161*38c8a9a5SSteve French 	struct cache_dfs_tgt *t;
162*38c8a9a5SSteve French 
163*38c8a9a5SSteve French 	seq_puts(m, "DFS cache\n---------\n");
164*38c8a9a5SSteve French 
165*38c8a9a5SSteve French 	down_read(&htable_rw_lock);
166*38c8a9a5SSteve French 	for (i = 0; i < CACHE_HTABLE_SIZE; i++) {
167*38c8a9a5SSteve French 		struct hlist_head *l = &cache_htable[i];
168*38c8a9a5SSteve French 
169*38c8a9a5SSteve French 		hlist_for_each_entry(ce, l, hlist) {
170*38c8a9a5SSteve French 			if (hlist_unhashed(&ce->hlist))
171*38c8a9a5SSteve French 				continue;
172*38c8a9a5SSteve French 
173*38c8a9a5SSteve French 			seq_printf(m,
174*38c8a9a5SSteve French 				   "cache entry: path=%s,type=%s,ttl=%d,etime=%ld,hdr_flags=0x%x,ref_flags=0x%x,interlink=%s,path_consumed=%d,expired=%s\n",
175*38c8a9a5SSteve French 				   ce->path, ce->srvtype == DFS_TYPE_ROOT ? "root" : "link",
176*38c8a9a5SSteve French 				   ce->ttl, ce->etime.tv_nsec, ce->hdr_flags, ce->ref_flags,
177*38c8a9a5SSteve French 				   IS_DFS_INTERLINK(ce->hdr_flags) ? "yes" : "no",
178*38c8a9a5SSteve French 				   ce->path_consumed, cache_entry_expired(ce) ? "yes" : "no");
179*38c8a9a5SSteve French 
180*38c8a9a5SSteve French 			list_for_each_entry(t, &ce->tlist, list) {
181*38c8a9a5SSteve French 				seq_printf(m, "  %s%s\n",
182*38c8a9a5SSteve French 					   t->name,
183*38c8a9a5SSteve French 					   READ_ONCE(ce->tgthint) == t ? " (target hint)" : "");
184*38c8a9a5SSteve French 			}
185*38c8a9a5SSteve French 		}
186*38c8a9a5SSteve French 	}
187*38c8a9a5SSteve French 	up_read(&htable_rw_lock);
188*38c8a9a5SSteve French 
189*38c8a9a5SSteve French 	return 0;
190*38c8a9a5SSteve French }
191*38c8a9a5SSteve French 
192*38c8a9a5SSteve French static ssize_t dfscache_proc_write(struct file *file, const char __user *buffer,
193*38c8a9a5SSteve French 				   size_t count, loff_t *ppos)
194*38c8a9a5SSteve French {
195*38c8a9a5SSteve French 	char c;
196*38c8a9a5SSteve French 	int rc;
197*38c8a9a5SSteve French 
198*38c8a9a5SSteve French 	rc = get_user(c, buffer);
199*38c8a9a5SSteve French 	if (rc)
200*38c8a9a5SSteve French 		return rc;
201*38c8a9a5SSteve French 
202*38c8a9a5SSteve French 	if (c != '0')
203*38c8a9a5SSteve French 		return -EINVAL;
204*38c8a9a5SSteve French 
205*38c8a9a5SSteve French 	cifs_dbg(FYI, "clearing dfs cache\n");
206*38c8a9a5SSteve French 
207*38c8a9a5SSteve French 	down_write(&htable_rw_lock);
208*38c8a9a5SSteve French 	flush_cache_ents();
209*38c8a9a5SSteve French 	up_write(&htable_rw_lock);
210*38c8a9a5SSteve French 
211*38c8a9a5SSteve French 	return count;
212*38c8a9a5SSteve French }
213*38c8a9a5SSteve French 
214*38c8a9a5SSteve French static int dfscache_proc_open(struct inode *inode, struct file *file)
215*38c8a9a5SSteve French {
216*38c8a9a5SSteve French 	return single_open(file, dfscache_proc_show, NULL);
217*38c8a9a5SSteve French }
218*38c8a9a5SSteve French 
219*38c8a9a5SSteve French const struct proc_ops dfscache_proc_ops = {
220*38c8a9a5SSteve French 	.proc_open	= dfscache_proc_open,
221*38c8a9a5SSteve French 	.proc_read	= seq_read,
222*38c8a9a5SSteve French 	.proc_lseek	= seq_lseek,
223*38c8a9a5SSteve French 	.proc_release	= single_release,
224*38c8a9a5SSteve French 	.proc_write	= dfscache_proc_write,
225*38c8a9a5SSteve French };
226*38c8a9a5SSteve French 
227*38c8a9a5SSteve French #ifdef CONFIG_CIFS_DEBUG2
228*38c8a9a5SSteve French static inline void dump_tgts(const struct cache_entry *ce)
229*38c8a9a5SSteve French {
230*38c8a9a5SSteve French 	struct cache_dfs_tgt *t;
231*38c8a9a5SSteve French 
232*38c8a9a5SSteve French 	cifs_dbg(FYI, "target list:\n");
233*38c8a9a5SSteve French 	list_for_each_entry(t, &ce->tlist, list) {
234*38c8a9a5SSteve French 		cifs_dbg(FYI, "  %s%s\n", t->name,
235*38c8a9a5SSteve French 			 READ_ONCE(ce->tgthint) == t ? " (target hint)" : "");
236*38c8a9a5SSteve French 	}
237*38c8a9a5SSteve French }
238*38c8a9a5SSteve French 
239*38c8a9a5SSteve French static inline void dump_ce(const struct cache_entry *ce)
240*38c8a9a5SSteve French {
241*38c8a9a5SSteve French 	cifs_dbg(FYI, "cache entry: path=%s,type=%s,ttl=%d,etime=%ld,hdr_flags=0x%x,ref_flags=0x%x,interlink=%s,path_consumed=%d,expired=%s\n",
242*38c8a9a5SSteve French 		 ce->path,
243*38c8a9a5SSteve French 		 ce->srvtype == DFS_TYPE_ROOT ? "root" : "link", ce->ttl,
244*38c8a9a5SSteve French 		 ce->etime.tv_nsec,
245*38c8a9a5SSteve French 		 ce->hdr_flags, ce->ref_flags,
246*38c8a9a5SSteve French 		 IS_DFS_INTERLINK(ce->hdr_flags) ? "yes" : "no",
247*38c8a9a5SSteve French 		 ce->path_consumed,
248*38c8a9a5SSteve French 		 cache_entry_expired(ce) ? "yes" : "no");
249*38c8a9a5SSteve French 	dump_tgts(ce);
250*38c8a9a5SSteve French }
251*38c8a9a5SSteve French 
252*38c8a9a5SSteve French static inline void dump_refs(const struct dfs_info3_param *refs, int numrefs)
253*38c8a9a5SSteve French {
254*38c8a9a5SSteve French 	int i;
255*38c8a9a5SSteve French 
256*38c8a9a5SSteve French 	cifs_dbg(FYI, "DFS referrals returned by the server:\n");
257*38c8a9a5SSteve French 	for (i = 0; i < numrefs; i++) {
258*38c8a9a5SSteve French 		const struct dfs_info3_param *ref = &refs[i];
259*38c8a9a5SSteve French 
260*38c8a9a5SSteve French 		cifs_dbg(FYI,
261*38c8a9a5SSteve French 			 "\n"
262*38c8a9a5SSteve French 			 "flags:         0x%x\n"
263*38c8a9a5SSteve French 			 "path_consumed: %d\n"
264*38c8a9a5SSteve French 			 "server_type:   0x%x\n"
265*38c8a9a5SSteve French 			 "ref_flag:      0x%x\n"
266*38c8a9a5SSteve French 			 "path_name:     %s\n"
267*38c8a9a5SSteve French 			 "node_name:     %s\n"
268*38c8a9a5SSteve French 			 "ttl:           %d (%dm)\n",
269*38c8a9a5SSteve French 			 ref->flags, ref->path_consumed, ref->server_type,
270*38c8a9a5SSteve French 			 ref->ref_flag, ref->path_name, ref->node_name,
271*38c8a9a5SSteve French 			 ref->ttl, ref->ttl / 60);
272*38c8a9a5SSteve French 	}
273*38c8a9a5SSteve French }
274*38c8a9a5SSteve French #else
275*38c8a9a5SSteve French #define dump_tgts(e)
276*38c8a9a5SSteve French #define dump_ce(e)
277*38c8a9a5SSteve French #define dump_refs(r, n)
278*38c8a9a5SSteve French #endif
279*38c8a9a5SSteve French 
280*38c8a9a5SSteve French /**
281*38c8a9a5SSteve French  * dfs_cache_init - Initialize DFS referral cache.
282*38c8a9a5SSteve French  *
283*38c8a9a5SSteve French  * Return zero if initialized successfully, otherwise non-zero.
284*38c8a9a5SSteve French  */
285*38c8a9a5SSteve French int dfs_cache_init(void)
286*38c8a9a5SSteve French {
287*38c8a9a5SSteve French 	int rc;
288*38c8a9a5SSteve French 	int i;
289*38c8a9a5SSteve French 
290*38c8a9a5SSteve French 	dfscache_wq = alloc_workqueue("cifs-dfscache",
291*38c8a9a5SSteve French 				      WQ_UNBOUND|WQ_FREEZABLE|WQ_MEM_RECLAIM,
292*38c8a9a5SSteve French 				      0);
293*38c8a9a5SSteve French 	if (!dfscache_wq)
294*38c8a9a5SSteve French 		return -ENOMEM;
295*38c8a9a5SSteve French 
296*38c8a9a5SSteve French 	cache_slab = kmem_cache_create("cifs_dfs_cache",
297*38c8a9a5SSteve French 				       sizeof(struct cache_entry), 0,
298*38c8a9a5SSteve French 				       SLAB_HWCACHE_ALIGN, NULL);
299*38c8a9a5SSteve French 	if (!cache_slab) {
300*38c8a9a5SSteve French 		rc = -ENOMEM;
301*38c8a9a5SSteve French 		goto out_destroy_wq;
302*38c8a9a5SSteve French 	}
303*38c8a9a5SSteve French 
304*38c8a9a5SSteve French 	for (i = 0; i < CACHE_HTABLE_SIZE; i++)
305*38c8a9a5SSteve French 		INIT_HLIST_HEAD(&cache_htable[i]);
306*38c8a9a5SSteve French 
307*38c8a9a5SSteve French 	atomic_set(&cache_count, 0);
308*38c8a9a5SSteve French 	atomic_set(&dfs_cache_ttl, CACHE_DEFAULT_TTL);
309*38c8a9a5SSteve French 	cache_cp = load_nls("utf8");
310*38c8a9a5SSteve French 	if (!cache_cp)
311*38c8a9a5SSteve French 		cache_cp = load_nls_default();
312*38c8a9a5SSteve French 
313*38c8a9a5SSteve French 	cifs_dbg(FYI, "%s: initialized DFS referral cache\n", __func__);
314*38c8a9a5SSteve French 	return 0;
315*38c8a9a5SSteve French 
316*38c8a9a5SSteve French out_destroy_wq:
317*38c8a9a5SSteve French 	destroy_workqueue(dfscache_wq);
318*38c8a9a5SSteve French 	return rc;
319*38c8a9a5SSteve French }
320*38c8a9a5SSteve French 
321*38c8a9a5SSteve French static int cache_entry_hash(const void *data, int size, unsigned int *hash)
322*38c8a9a5SSteve French {
323*38c8a9a5SSteve French 	int i, clen;
324*38c8a9a5SSteve French 	const unsigned char *s = data;
325*38c8a9a5SSteve French 	wchar_t c;
326*38c8a9a5SSteve French 	unsigned int h = 0;
327*38c8a9a5SSteve French 
328*38c8a9a5SSteve French 	for (i = 0; i < size; i += clen) {
329*38c8a9a5SSteve French 		clen = cache_cp->char2uni(&s[i], size - i, &c);
330*38c8a9a5SSteve French 		if (unlikely(clen < 0)) {
331*38c8a9a5SSteve French 			cifs_dbg(VFS, "%s: can't convert char\n", __func__);
332*38c8a9a5SSteve French 			return clen;
333*38c8a9a5SSteve French 		}
334*38c8a9a5SSteve French 		c = cifs_toupper(c);
335*38c8a9a5SSteve French 		h = jhash(&c, sizeof(c), h);
336*38c8a9a5SSteve French 	}
337*38c8a9a5SSteve French 	*hash = h % CACHE_HTABLE_SIZE;
338*38c8a9a5SSteve French 	return 0;
339*38c8a9a5SSteve French }
340*38c8a9a5SSteve French 
341*38c8a9a5SSteve French /* Return target hint of a DFS cache entry */
342*38c8a9a5SSteve French static inline char *get_tgt_name(const struct cache_entry *ce)
343*38c8a9a5SSteve French {
344*38c8a9a5SSteve French 	struct cache_dfs_tgt *t = READ_ONCE(ce->tgthint);
345*38c8a9a5SSteve French 
346*38c8a9a5SSteve French 	return t ? t->name : ERR_PTR(-ENOENT);
347*38c8a9a5SSteve French }
348*38c8a9a5SSteve French 
349*38c8a9a5SSteve French /* Return expire time out of a new entry's TTL */
350*38c8a9a5SSteve French static inline struct timespec64 get_expire_time(int ttl)
351*38c8a9a5SSteve French {
352*38c8a9a5SSteve French 	struct timespec64 ts = {
353*38c8a9a5SSteve French 		.tv_sec = ttl,
354*38c8a9a5SSteve French 		.tv_nsec = 0,
355*38c8a9a5SSteve French 	};
356*38c8a9a5SSteve French 	struct timespec64 now;
357*38c8a9a5SSteve French 
358*38c8a9a5SSteve French 	ktime_get_coarse_real_ts64(&now);
359*38c8a9a5SSteve French 	return timespec64_add(now, ts);
360*38c8a9a5SSteve French }
361*38c8a9a5SSteve French 
362*38c8a9a5SSteve French /* Allocate a new DFS target */
363*38c8a9a5SSteve French static struct cache_dfs_tgt *alloc_target(const char *name, int path_consumed)
364*38c8a9a5SSteve French {
365*38c8a9a5SSteve French 	struct cache_dfs_tgt *t;
366*38c8a9a5SSteve French 
367*38c8a9a5SSteve French 	t = kmalloc(sizeof(*t), GFP_ATOMIC);
368*38c8a9a5SSteve French 	if (!t)
369*38c8a9a5SSteve French 		return ERR_PTR(-ENOMEM);
370*38c8a9a5SSteve French 	t->name = kstrdup(name, GFP_ATOMIC);
371*38c8a9a5SSteve French 	if (!t->name) {
372*38c8a9a5SSteve French 		kfree(t);
373*38c8a9a5SSteve French 		return ERR_PTR(-ENOMEM);
374*38c8a9a5SSteve French 	}
375*38c8a9a5SSteve French 	t->path_consumed = path_consumed;
376*38c8a9a5SSteve French 	INIT_LIST_HEAD(&t->list);
377*38c8a9a5SSteve French 	return t;
378*38c8a9a5SSteve French }
379*38c8a9a5SSteve French 
380*38c8a9a5SSteve French /*
381*38c8a9a5SSteve French  * Copy DFS referral information to a cache entry and conditionally update
382*38c8a9a5SSteve French  * target hint.
383*38c8a9a5SSteve French  */
384*38c8a9a5SSteve French static int copy_ref_data(const struct dfs_info3_param *refs, int numrefs,
385*38c8a9a5SSteve French 			 struct cache_entry *ce, const char *tgthint)
386*38c8a9a5SSteve French {
387*38c8a9a5SSteve French 	struct cache_dfs_tgt *target;
388*38c8a9a5SSteve French 	int i;
389*38c8a9a5SSteve French 
390*38c8a9a5SSteve French 	ce->ttl = max_t(int, refs[0].ttl, CACHE_MIN_TTL);
391*38c8a9a5SSteve French 	ce->etime = get_expire_time(ce->ttl);
392*38c8a9a5SSteve French 	ce->srvtype = refs[0].server_type;
393*38c8a9a5SSteve French 	ce->hdr_flags = refs[0].flags;
394*38c8a9a5SSteve French 	ce->ref_flags = refs[0].ref_flag;
395*38c8a9a5SSteve French 	ce->path_consumed = refs[0].path_consumed;
396*38c8a9a5SSteve French 
397*38c8a9a5SSteve French 	for (i = 0; i < numrefs; i++) {
398*38c8a9a5SSteve French 		struct cache_dfs_tgt *t;
399*38c8a9a5SSteve French 
400*38c8a9a5SSteve French 		t = alloc_target(refs[i].node_name, refs[i].path_consumed);
401*38c8a9a5SSteve French 		if (IS_ERR(t)) {
402*38c8a9a5SSteve French 			free_tgts(ce);
403*38c8a9a5SSteve French 			return PTR_ERR(t);
404*38c8a9a5SSteve French 		}
405*38c8a9a5SSteve French 		if (tgthint && !strcasecmp(t->name, tgthint)) {
406*38c8a9a5SSteve French 			list_add(&t->list, &ce->tlist);
407*38c8a9a5SSteve French 			tgthint = NULL;
408*38c8a9a5SSteve French 		} else {
409*38c8a9a5SSteve French 			list_add_tail(&t->list, &ce->tlist);
410*38c8a9a5SSteve French 		}
411*38c8a9a5SSteve French 		ce->numtgts++;
412*38c8a9a5SSteve French 	}
413*38c8a9a5SSteve French 
414*38c8a9a5SSteve French 	target = list_first_entry_or_null(&ce->tlist, struct cache_dfs_tgt,
415*38c8a9a5SSteve French 					  list);
416*38c8a9a5SSteve French 	WRITE_ONCE(ce->tgthint, target);
417*38c8a9a5SSteve French 
418*38c8a9a5SSteve French 	return 0;
419*38c8a9a5SSteve French }
420*38c8a9a5SSteve French 
421*38c8a9a5SSteve French /* Allocate a new cache entry */
422*38c8a9a5SSteve French static struct cache_entry *alloc_cache_entry(struct dfs_info3_param *refs, int numrefs)
423*38c8a9a5SSteve French {
424*38c8a9a5SSteve French 	struct cache_entry *ce;
425*38c8a9a5SSteve French 	int rc;
426*38c8a9a5SSteve French 
427*38c8a9a5SSteve French 	ce = kmem_cache_zalloc(cache_slab, GFP_KERNEL);
428*38c8a9a5SSteve French 	if (!ce)
429*38c8a9a5SSteve French 		return ERR_PTR(-ENOMEM);
430*38c8a9a5SSteve French 
431*38c8a9a5SSteve French 	ce->path = refs[0].path_name;
432*38c8a9a5SSteve French 	refs[0].path_name = NULL;
433*38c8a9a5SSteve French 
434*38c8a9a5SSteve French 	INIT_HLIST_NODE(&ce->hlist);
435*38c8a9a5SSteve French 	INIT_LIST_HEAD(&ce->tlist);
436*38c8a9a5SSteve French 
437*38c8a9a5SSteve French 	rc = copy_ref_data(refs, numrefs, ce, NULL);
438*38c8a9a5SSteve French 	if (rc) {
439*38c8a9a5SSteve French 		kfree(ce->path);
440*38c8a9a5SSteve French 		kmem_cache_free(cache_slab, ce);
441*38c8a9a5SSteve French 		ce = ERR_PTR(rc);
442*38c8a9a5SSteve French 	}
443*38c8a9a5SSteve French 	return ce;
444*38c8a9a5SSteve French }
445*38c8a9a5SSteve French 
446*38c8a9a5SSteve French static void remove_oldest_entry_locked(void)
447*38c8a9a5SSteve French {
448*38c8a9a5SSteve French 	int i;
449*38c8a9a5SSteve French 	struct cache_entry *ce;
450*38c8a9a5SSteve French 	struct cache_entry *to_del = NULL;
451*38c8a9a5SSteve French 
452*38c8a9a5SSteve French 	WARN_ON(!rwsem_is_locked(&htable_rw_lock));
453*38c8a9a5SSteve French 
454*38c8a9a5SSteve French 	for (i = 0; i < CACHE_HTABLE_SIZE; i++) {
455*38c8a9a5SSteve French 		struct hlist_head *l = &cache_htable[i];
456*38c8a9a5SSteve French 
457*38c8a9a5SSteve French 		hlist_for_each_entry(ce, l, hlist) {
458*38c8a9a5SSteve French 			if (hlist_unhashed(&ce->hlist))
459*38c8a9a5SSteve French 				continue;
460*38c8a9a5SSteve French 			if (!to_del || timespec64_compare(&ce->etime,
461*38c8a9a5SSteve French 							  &to_del->etime) < 0)
462*38c8a9a5SSteve French 				to_del = ce;
463*38c8a9a5SSteve French 		}
464*38c8a9a5SSteve French 	}
465*38c8a9a5SSteve French 
466*38c8a9a5SSteve French 	if (!to_del) {
467*38c8a9a5SSteve French 		cifs_dbg(FYI, "%s: no entry to remove\n", __func__);
468*38c8a9a5SSteve French 		return;
469*38c8a9a5SSteve French 	}
470*38c8a9a5SSteve French 
471*38c8a9a5SSteve French 	cifs_dbg(FYI, "%s: removing entry\n", __func__);
472*38c8a9a5SSteve French 	dump_ce(to_del);
473*38c8a9a5SSteve French 	flush_cache_ent(to_del);
474*38c8a9a5SSteve French }
475*38c8a9a5SSteve French 
476*38c8a9a5SSteve French /* Add a new DFS cache entry */
477*38c8a9a5SSteve French static struct cache_entry *add_cache_entry_locked(struct dfs_info3_param *refs,
478*38c8a9a5SSteve French 						  int numrefs)
479*38c8a9a5SSteve French {
480*38c8a9a5SSteve French 	int rc;
481*38c8a9a5SSteve French 	struct cache_entry *ce;
482*38c8a9a5SSteve French 	unsigned int hash;
483*38c8a9a5SSteve French 	int ttl;
484*38c8a9a5SSteve French 
485*38c8a9a5SSteve French 	WARN_ON(!rwsem_is_locked(&htable_rw_lock));
486*38c8a9a5SSteve French 
487*38c8a9a5SSteve French 	if (atomic_read(&cache_count) >= CACHE_MAX_ENTRIES) {
488*38c8a9a5SSteve French 		cifs_dbg(FYI, "%s: reached max cache size (%d)\n", __func__, CACHE_MAX_ENTRIES);
489*38c8a9a5SSteve French 		remove_oldest_entry_locked();
490*38c8a9a5SSteve French 	}
491*38c8a9a5SSteve French 
492*38c8a9a5SSteve French 	rc = cache_entry_hash(refs[0].path_name, strlen(refs[0].path_name), &hash);
493*38c8a9a5SSteve French 	if (rc)
494*38c8a9a5SSteve French 		return ERR_PTR(rc);
495*38c8a9a5SSteve French 
496*38c8a9a5SSteve French 	ce = alloc_cache_entry(refs, numrefs);
497*38c8a9a5SSteve French 	if (IS_ERR(ce))
498*38c8a9a5SSteve French 		return ce;
499*38c8a9a5SSteve French 
500*38c8a9a5SSteve French 	ttl = min_t(int, atomic_read(&dfs_cache_ttl), ce->ttl);
501*38c8a9a5SSteve French 	atomic_set(&dfs_cache_ttl, ttl);
502*38c8a9a5SSteve French 
503*38c8a9a5SSteve French 	hlist_add_head(&ce->hlist, &cache_htable[hash]);
504*38c8a9a5SSteve French 	dump_ce(ce);
505*38c8a9a5SSteve French 
506*38c8a9a5SSteve French 	atomic_inc(&cache_count);
507*38c8a9a5SSteve French 
508*38c8a9a5SSteve French 	return ce;
509*38c8a9a5SSteve French }
510*38c8a9a5SSteve French 
511*38c8a9a5SSteve French /* Check if two DFS paths are equal.  @s1 and @s2 are expected to be in @cache_cp's charset */
512*38c8a9a5SSteve French static bool dfs_path_equal(const char *s1, int len1, const char *s2, int len2)
513*38c8a9a5SSteve French {
514*38c8a9a5SSteve French 	int i, l1, l2;
515*38c8a9a5SSteve French 	wchar_t c1, c2;
516*38c8a9a5SSteve French 
517*38c8a9a5SSteve French 	if (len1 != len2)
518*38c8a9a5SSteve French 		return false;
519*38c8a9a5SSteve French 
520*38c8a9a5SSteve French 	for (i = 0; i < len1; i += l1) {
521*38c8a9a5SSteve French 		l1 = cache_cp->char2uni(&s1[i], len1 - i, &c1);
522*38c8a9a5SSteve French 		l2 = cache_cp->char2uni(&s2[i], len2 - i, &c2);
523*38c8a9a5SSteve French 		if (unlikely(l1 < 0 && l2 < 0)) {
524*38c8a9a5SSteve French 			if (s1[i] != s2[i])
525*38c8a9a5SSteve French 				return false;
526*38c8a9a5SSteve French 			l1 = 1;
527*38c8a9a5SSteve French 			continue;
528*38c8a9a5SSteve French 		}
529*38c8a9a5SSteve French 		if (l1 != l2)
530*38c8a9a5SSteve French 			return false;
531*38c8a9a5SSteve French 		if (cifs_toupper(c1) != cifs_toupper(c2))
532*38c8a9a5SSteve French 			return false;
533*38c8a9a5SSteve French 	}
534*38c8a9a5SSteve French 	return true;
535*38c8a9a5SSteve French }
536*38c8a9a5SSteve French 
537*38c8a9a5SSteve French static struct cache_entry *__lookup_cache_entry(const char *path, unsigned int hash, int len)
538*38c8a9a5SSteve French {
539*38c8a9a5SSteve French 	struct cache_entry *ce;
540*38c8a9a5SSteve French 
541*38c8a9a5SSteve French 	hlist_for_each_entry(ce, &cache_htable[hash], hlist) {
542*38c8a9a5SSteve French 		if (dfs_path_equal(ce->path, strlen(ce->path), path, len)) {
543*38c8a9a5SSteve French 			dump_ce(ce);
544*38c8a9a5SSteve French 			return ce;
545*38c8a9a5SSteve French 		}
546*38c8a9a5SSteve French 	}
547*38c8a9a5SSteve French 	return ERR_PTR(-ENOENT);
548*38c8a9a5SSteve French }
549*38c8a9a5SSteve French 
550*38c8a9a5SSteve French /*
551*38c8a9a5SSteve French  * Find a DFS cache entry in hash table and optionally check prefix path against normalized @path.
552*38c8a9a5SSteve French  *
553*38c8a9a5SSteve French  * Use whole path components in the match.  Must be called with htable_rw_lock held.
554*38c8a9a5SSteve French  *
555*38c8a9a5SSteve French  * Return cached entry if successful.
556*38c8a9a5SSteve French  * Return ERR_PTR(-ENOENT) if the entry is not found.
557*38c8a9a5SSteve French  * Return error ptr otherwise.
558*38c8a9a5SSteve French  */
559*38c8a9a5SSteve French static struct cache_entry *lookup_cache_entry(const char *path)
560*38c8a9a5SSteve French {
561*38c8a9a5SSteve French 	struct cache_entry *ce;
562*38c8a9a5SSteve French 	int cnt = 0;
563*38c8a9a5SSteve French 	const char *s = path, *e;
564*38c8a9a5SSteve French 	char sep = *s;
565*38c8a9a5SSteve French 	unsigned int hash;
566*38c8a9a5SSteve French 	int rc;
567*38c8a9a5SSteve French 
568*38c8a9a5SSteve French 	while ((s = strchr(s, sep)) && ++cnt < 3)
569*38c8a9a5SSteve French 		s++;
570*38c8a9a5SSteve French 
571*38c8a9a5SSteve French 	if (cnt < 3) {
572*38c8a9a5SSteve French 		rc = cache_entry_hash(path, strlen(path), &hash);
573*38c8a9a5SSteve French 		if (rc)
574*38c8a9a5SSteve French 			return ERR_PTR(rc);
575*38c8a9a5SSteve French 		return __lookup_cache_entry(path, hash, strlen(path));
576*38c8a9a5SSteve French 	}
577*38c8a9a5SSteve French 	/*
578*38c8a9a5SSteve French 	 * Handle paths that have more than two path components and are a complete prefix of the DFS
579*38c8a9a5SSteve French 	 * referral request path (@path).
580*38c8a9a5SSteve French 	 *
581*38c8a9a5SSteve French 	 * See MS-DFSC 3.2.5.5 "Receiving a Root Referral Request or Link Referral Request".
582*38c8a9a5SSteve French 	 */
583*38c8a9a5SSteve French 	e = path + strlen(path) - 1;
584*38c8a9a5SSteve French 	while (e > s) {
585*38c8a9a5SSteve French 		int len;
586*38c8a9a5SSteve French 
587*38c8a9a5SSteve French 		/* skip separators */
588*38c8a9a5SSteve French 		while (e > s && *e == sep)
589*38c8a9a5SSteve French 			e--;
590*38c8a9a5SSteve French 		if (e == s)
591*38c8a9a5SSteve French 			break;
592*38c8a9a5SSteve French 
593*38c8a9a5SSteve French 		len = e + 1 - path;
594*38c8a9a5SSteve French 		rc = cache_entry_hash(path, len, &hash);
595*38c8a9a5SSteve French 		if (rc)
596*38c8a9a5SSteve French 			return ERR_PTR(rc);
597*38c8a9a5SSteve French 		ce = __lookup_cache_entry(path, hash, len);
598*38c8a9a5SSteve French 		if (!IS_ERR(ce))
599*38c8a9a5SSteve French 			return ce;
600*38c8a9a5SSteve French 
601*38c8a9a5SSteve French 		/* backward until separator */
602*38c8a9a5SSteve French 		while (e > s && *e != sep)
603*38c8a9a5SSteve French 			e--;
604*38c8a9a5SSteve French 	}
605*38c8a9a5SSteve French 	return ERR_PTR(-ENOENT);
606*38c8a9a5SSteve French }
607*38c8a9a5SSteve French 
608*38c8a9a5SSteve French /**
609*38c8a9a5SSteve French  * dfs_cache_destroy - destroy DFS referral cache
610*38c8a9a5SSteve French  */
611*38c8a9a5SSteve French void dfs_cache_destroy(void)
612*38c8a9a5SSteve French {
613*38c8a9a5SSteve French 	unload_nls(cache_cp);
614*38c8a9a5SSteve French 	flush_cache_ents();
615*38c8a9a5SSteve French 	kmem_cache_destroy(cache_slab);
616*38c8a9a5SSteve French 	destroy_workqueue(dfscache_wq);
617*38c8a9a5SSteve French 
618*38c8a9a5SSteve French 	cifs_dbg(FYI, "%s: destroyed DFS referral cache\n", __func__);
619*38c8a9a5SSteve French }
620*38c8a9a5SSteve French 
621*38c8a9a5SSteve French /* Update a cache entry with the new referral in @refs */
622*38c8a9a5SSteve French static int update_cache_entry_locked(struct cache_entry *ce, const struct dfs_info3_param *refs,
623*38c8a9a5SSteve French 				     int numrefs)
624*38c8a9a5SSteve French {
625*38c8a9a5SSteve French 	struct cache_dfs_tgt *target;
626*38c8a9a5SSteve French 	char *th = NULL;
627*38c8a9a5SSteve French 	int rc;
628*38c8a9a5SSteve French 
629*38c8a9a5SSteve French 	WARN_ON(!rwsem_is_locked(&htable_rw_lock));
630*38c8a9a5SSteve French 
631*38c8a9a5SSteve French 	target = READ_ONCE(ce->tgthint);
632*38c8a9a5SSteve French 	if (target) {
633*38c8a9a5SSteve French 		th = kstrdup(target->name, GFP_ATOMIC);
634*38c8a9a5SSteve French 		if (!th)
635*38c8a9a5SSteve French 			return -ENOMEM;
636*38c8a9a5SSteve French 	}
637*38c8a9a5SSteve French 
638*38c8a9a5SSteve French 	free_tgts(ce);
639*38c8a9a5SSteve French 	ce->numtgts = 0;
640*38c8a9a5SSteve French 
641*38c8a9a5SSteve French 	rc = copy_ref_data(refs, numrefs, ce, th);
642*38c8a9a5SSteve French 
643*38c8a9a5SSteve French 	kfree(th);
644*38c8a9a5SSteve French 
645*38c8a9a5SSteve French 	return rc;
646*38c8a9a5SSteve French }
647*38c8a9a5SSteve French 
648*38c8a9a5SSteve French static int get_dfs_referral(const unsigned int xid, struct cifs_ses *ses, const char *path,
649*38c8a9a5SSteve French 			    struct dfs_info3_param **refs, int *numrefs)
650*38c8a9a5SSteve French {
651*38c8a9a5SSteve French 	int rc;
652*38c8a9a5SSteve French 	int i;
653*38c8a9a5SSteve French 
654*38c8a9a5SSteve French 	*refs = NULL;
655*38c8a9a5SSteve French 	*numrefs = 0;
656*38c8a9a5SSteve French 
657*38c8a9a5SSteve French 	if (!ses || !ses->server || !ses->server->ops->get_dfs_refer)
658*38c8a9a5SSteve French 		return -EOPNOTSUPP;
659*38c8a9a5SSteve French 	if (unlikely(!cache_cp))
660*38c8a9a5SSteve French 		return -EINVAL;
661*38c8a9a5SSteve French 
662*38c8a9a5SSteve French 	cifs_dbg(FYI, "%s: ipc=%s referral=%s\n", __func__, ses->tcon_ipc->tree_name, path);
663*38c8a9a5SSteve French 	rc =  ses->server->ops->get_dfs_refer(xid, ses, path, refs, numrefs, cache_cp,
664*38c8a9a5SSteve French 					      NO_MAP_UNI_RSVD);
665*38c8a9a5SSteve French 	if (!rc) {
666*38c8a9a5SSteve French 		struct dfs_info3_param *ref = *refs;
667*38c8a9a5SSteve French 
668*38c8a9a5SSteve French 		for (i = 0; i < *numrefs; i++)
669*38c8a9a5SSteve French 			convert_delimiter(ref[i].path_name, '\\');
670*38c8a9a5SSteve French 	}
671*38c8a9a5SSteve French 	return rc;
672*38c8a9a5SSteve French }
673*38c8a9a5SSteve French 
674*38c8a9a5SSteve French /*
675*38c8a9a5SSteve French  * Find, create or update a DFS cache entry.
676*38c8a9a5SSteve French  *
677*38c8a9a5SSteve French  * If the entry wasn't found, it will create a new one. Or if it was found but
678*38c8a9a5SSteve French  * expired, then it will update the entry accordingly.
679*38c8a9a5SSteve French  *
680*38c8a9a5SSteve French  * For interlinks, cifs_mount() and expand_dfs_referral() are supposed to
681*38c8a9a5SSteve French  * handle them properly.
682*38c8a9a5SSteve French  *
683*38c8a9a5SSteve French  * On success, return entry with acquired lock for reading, otherwise error ptr.
684*38c8a9a5SSteve French  */
685*38c8a9a5SSteve French static struct cache_entry *cache_refresh_path(const unsigned int xid,
686*38c8a9a5SSteve French 					      struct cifs_ses *ses,
687*38c8a9a5SSteve French 					      const char *path,
688*38c8a9a5SSteve French 					      bool force_refresh)
689*38c8a9a5SSteve French {
690*38c8a9a5SSteve French 	struct dfs_info3_param *refs = NULL;
691*38c8a9a5SSteve French 	struct cache_entry *ce;
692*38c8a9a5SSteve French 	int numrefs = 0;
693*38c8a9a5SSteve French 	int rc;
694*38c8a9a5SSteve French 
695*38c8a9a5SSteve French 	cifs_dbg(FYI, "%s: search path: %s\n", __func__, path);
696*38c8a9a5SSteve French 
697*38c8a9a5SSteve French 	down_read(&htable_rw_lock);
698*38c8a9a5SSteve French 
699*38c8a9a5SSteve French 	ce = lookup_cache_entry(path);
700*38c8a9a5SSteve French 	if (!IS_ERR(ce)) {
701*38c8a9a5SSteve French 		if (!force_refresh && !cache_entry_expired(ce))
702*38c8a9a5SSteve French 			return ce;
703*38c8a9a5SSteve French 	} else if (PTR_ERR(ce) != -ENOENT) {
704*38c8a9a5SSteve French 		up_read(&htable_rw_lock);
705*38c8a9a5SSteve French 		return ce;
706*38c8a9a5SSteve French 	}
707*38c8a9a5SSteve French 
708*38c8a9a5SSteve French 	/*
709*38c8a9a5SSteve French 	 * Unlock shared access as we don't want to hold any locks while getting
710*38c8a9a5SSteve French 	 * a new referral.  The @ses used for performing the I/O could be
711*38c8a9a5SSteve French 	 * reconnecting and it acquires @htable_rw_lock to look up the dfs cache
712*38c8a9a5SSteve French 	 * in order to failover -- if necessary.
713*38c8a9a5SSteve French 	 */
714*38c8a9a5SSteve French 	up_read(&htable_rw_lock);
715*38c8a9a5SSteve French 
716*38c8a9a5SSteve French 	/*
717*38c8a9a5SSteve French 	 * Either the entry was not found, or it is expired, or it is a forced
718*38c8a9a5SSteve French 	 * refresh.
719*38c8a9a5SSteve French 	 * Request a new DFS referral in order to create or update a cache entry.
720*38c8a9a5SSteve French 	 */
721*38c8a9a5SSteve French 	rc = get_dfs_referral(xid, ses, path, &refs, &numrefs);
722*38c8a9a5SSteve French 	if (rc) {
723*38c8a9a5SSteve French 		ce = ERR_PTR(rc);
724*38c8a9a5SSteve French 		goto out;
725*38c8a9a5SSteve French 	}
726*38c8a9a5SSteve French 
727*38c8a9a5SSteve French 	dump_refs(refs, numrefs);
728*38c8a9a5SSteve French 
729*38c8a9a5SSteve French 	down_write(&htable_rw_lock);
730*38c8a9a5SSteve French 	/* Re-check as another task might have it added or refreshed already */
731*38c8a9a5SSteve French 	ce = lookup_cache_entry(path);
732*38c8a9a5SSteve French 	if (!IS_ERR(ce)) {
733*38c8a9a5SSteve French 		if (force_refresh || cache_entry_expired(ce)) {
734*38c8a9a5SSteve French 			rc = update_cache_entry_locked(ce, refs, numrefs);
735*38c8a9a5SSteve French 			if (rc)
736*38c8a9a5SSteve French 				ce = ERR_PTR(rc);
737*38c8a9a5SSteve French 		}
738*38c8a9a5SSteve French 	} else if (PTR_ERR(ce) == -ENOENT) {
739*38c8a9a5SSteve French 		ce = add_cache_entry_locked(refs, numrefs);
740*38c8a9a5SSteve French 	}
741*38c8a9a5SSteve French 
742*38c8a9a5SSteve French 	if (IS_ERR(ce)) {
743*38c8a9a5SSteve French 		up_write(&htable_rw_lock);
744*38c8a9a5SSteve French 		goto out;
745*38c8a9a5SSteve French 	}
746*38c8a9a5SSteve French 
747*38c8a9a5SSteve French 	downgrade_write(&htable_rw_lock);
748*38c8a9a5SSteve French out:
749*38c8a9a5SSteve French 	free_dfs_info_array(refs, numrefs);
750*38c8a9a5SSteve French 	return ce;
751*38c8a9a5SSteve French }
752*38c8a9a5SSteve French 
753*38c8a9a5SSteve French /*
754*38c8a9a5SSteve French  * Set up a DFS referral from a given cache entry.
755*38c8a9a5SSteve French  *
756*38c8a9a5SSteve French  * Must be called with htable_rw_lock held.
757*38c8a9a5SSteve French  */
758*38c8a9a5SSteve French static int setup_referral(const char *path, struct cache_entry *ce,
759*38c8a9a5SSteve French 			  struct dfs_info3_param *ref, const char *target)
760*38c8a9a5SSteve French {
761*38c8a9a5SSteve French 	int rc;
762*38c8a9a5SSteve French 
763*38c8a9a5SSteve French 	cifs_dbg(FYI, "%s: set up new ref\n", __func__);
764*38c8a9a5SSteve French 
765*38c8a9a5SSteve French 	memset(ref, 0, sizeof(*ref));
766*38c8a9a5SSteve French 
767*38c8a9a5SSteve French 	ref->path_name = kstrdup(path, GFP_ATOMIC);
768*38c8a9a5SSteve French 	if (!ref->path_name)
769*38c8a9a5SSteve French 		return -ENOMEM;
770*38c8a9a5SSteve French 
771*38c8a9a5SSteve French 	ref->node_name = kstrdup(target, GFP_ATOMIC);
772*38c8a9a5SSteve French 	if (!ref->node_name) {
773*38c8a9a5SSteve French 		rc = -ENOMEM;
774*38c8a9a5SSteve French 		goto err_free_path;
775*38c8a9a5SSteve French 	}
776*38c8a9a5SSteve French 
777*38c8a9a5SSteve French 	ref->path_consumed = ce->path_consumed;
778*38c8a9a5SSteve French 	ref->ttl = ce->ttl;
779*38c8a9a5SSteve French 	ref->server_type = ce->srvtype;
780*38c8a9a5SSteve French 	ref->ref_flag = ce->ref_flags;
781*38c8a9a5SSteve French 	ref->flags = ce->hdr_flags;
782*38c8a9a5SSteve French 
783*38c8a9a5SSteve French 	return 0;
784*38c8a9a5SSteve French 
785*38c8a9a5SSteve French err_free_path:
786*38c8a9a5SSteve French 	kfree(ref->path_name);
787*38c8a9a5SSteve French 	ref->path_name = NULL;
788*38c8a9a5SSteve French 	return rc;
789*38c8a9a5SSteve French }
790*38c8a9a5SSteve French 
791*38c8a9a5SSteve French /* Return target list of a DFS cache entry */
792*38c8a9a5SSteve French static int get_targets(struct cache_entry *ce, struct dfs_cache_tgt_list *tl)
793*38c8a9a5SSteve French {
794*38c8a9a5SSteve French 	int rc;
795*38c8a9a5SSteve French 	struct list_head *head = &tl->tl_list;
796*38c8a9a5SSteve French 	struct cache_dfs_tgt *t;
797*38c8a9a5SSteve French 	struct dfs_cache_tgt_iterator *it, *nit;
798*38c8a9a5SSteve French 
799*38c8a9a5SSteve French 	memset(tl, 0, sizeof(*tl));
800*38c8a9a5SSteve French 	INIT_LIST_HEAD(head);
801*38c8a9a5SSteve French 
802*38c8a9a5SSteve French 	list_for_each_entry(t, &ce->tlist, list) {
803*38c8a9a5SSteve French 		it = kzalloc(sizeof(*it), GFP_ATOMIC);
804*38c8a9a5SSteve French 		if (!it) {
805*38c8a9a5SSteve French 			rc = -ENOMEM;
806*38c8a9a5SSteve French 			goto err_free_it;
807*38c8a9a5SSteve French 		}
808*38c8a9a5SSteve French 
809*38c8a9a5SSteve French 		it->it_name = kstrdup(t->name, GFP_ATOMIC);
810*38c8a9a5SSteve French 		if (!it->it_name) {
811*38c8a9a5SSteve French 			kfree(it);
812*38c8a9a5SSteve French 			rc = -ENOMEM;
813*38c8a9a5SSteve French 			goto err_free_it;
814*38c8a9a5SSteve French 		}
815*38c8a9a5SSteve French 		it->it_path_consumed = t->path_consumed;
816*38c8a9a5SSteve French 
817*38c8a9a5SSteve French 		if (READ_ONCE(ce->tgthint) == t)
818*38c8a9a5SSteve French 			list_add(&it->it_list, head);
819*38c8a9a5SSteve French 		else
820*38c8a9a5SSteve French 			list_add_tail(&it->it_list, head);
821*38c8a9a5SSteve French 	}
822*38c8a9a5SSteve French 
823*38c8a9a5SSteve French 	tl->tl_numtgts = ce->numtgts;
824*38c8a9a5SSteve French 
825*38c8a9a5SSteve French 	return 0;
826*38c8a9a5SSteve French 
827*38c8a9a5SSteve French err_free_it:
828*38c8a9a5SSteve French 	list_for_each_entry_safe(it, nit, head, it_list) {
829*38c8a9a5SSteve French 		list_del(&it->it_list);
830*38c8a9a5SSteve French 		kfree(it->it_name);
831*38c8a9a5SSteve French 		kfree(it);
832*38c8a9a5SSteve French 	}
833*38c8a9a5SSteve French 	return rc;
834*38c8a9a5SSteve French }
835*38c8a9a5SSteve French 
836*38c8a9a5SSteve French /**
837*38c8a9a5SSteve French  * dfs_cache_find - find a DFS cache entry
838*38c8a9a5SSteve French  *
839*38c8a9a5SSteve French  * If it doesn't find the cache entry, then it will get a DFS referral
840*38c8a9a5SSteve French  * for @path and create a new entry.
841*38c8a9a5SSteve French  *
842*38c8a9a5SSteve French  * In case the cache entry exists but expired, it will get a DFS referral
843*38c8a9a5SSteve French  * for @path and then update the respective cache entry.
844*38c8a9a5SSteve French  *
845*38c8a9a5SSteve French  * These parameters are passed down to the get_dfs_refer() call if it
846*38c8a9a5SSteve French  * needs to be issued:
847*38c8a9a5SSteve French  * @xid: syscall xid
848*38c8a9a5SSteve French  * @ses: smb session to issue the request on
849*38c8a9a5SSteve French  * @cp: codepage
850*38c8a9a5SSteve French  * @remap: path character remapping type
851*38c8a9a5SSteve French  * @path: path to lookup in DFS referral cache.
852*38c8a9a5SSteve French  *
853*38c8a9a5SSteve French  * @ref: when non-NULL, store single DFS referral result in it.
854*38c8a9a5SSteve French  * @tgt_list: when non-NULL, store complete DFS target list in it.
855*38c8a9a5SSteve French  *
856*38c8a9a5SSteve French  * Return zero if the target was found, otherwise non-zero.
857*38c8a9a5SSteve French  */
858*38c8a9a5SSteve French int dfs_cache_find(const unsigned int xid, struct cifs_ses *ses, const struct nls_table *cp,
859*38c8a9a5SSteve French 		   int remap, const char *path, struct dfs_info3_param *ref,
860*38c8a9a5SSteve French 		   struct dfs_cache_tgt_list *tgt_list)
861*38c8a9a5SSteve French {
862*38c8a9a5SSteve French 	int rc;
863*38c8a9a5SSteve French 	const char *npath;
864*38c8a9a5SSteve French 	struct cache_entry *ce;
865*38c8a9a5SSteve French 
866*38c8a9a5SSteve French 	npath = dfs_cache_canonical_path(path, cp, remap);
867*38c8a9a5SSteve French 	if (IS_ERR(npath))
868*38c8a9a5SSteve French 		return PTR_ERR(npath);
869*38c8a9a5SSteve French 
870*38c8a9a5SSteve French 	ce = cache_refresh_path(xid, ses, npath, false);
871*38c8a9a5SSteve French 	if (IS_ERR(ce)) {
872*38c8a9a5SSteve French 		rc = PTR_ERR(ce);
873*38c8a9a5SSteve French 		goto out_free_path;
874*38c8a9a5SSteve French 	}
875*38c8a9a5SSteve French 
876*38c8a9a5SSteve French 	if (ref)
877*38c8a9a5SSteve French 		rc = setup_referral(path, ce, ref, get_tgt_name(ce));
878*38c8a9a5SSteve French 	else
879*38c8a9a5SSteve French 		rc = 0;
880*38c8a9a5SSteve French 	if (!rc && tgt_list)
881*38c8a9a5SSteve French 		rc = get_targets(ce, tgt_list);
882*38c8a9a5SSteve French 
883*38c8a9a5SSteve French 	up_read(&htable_rw_lock);
884*38c8a9a5SSteve French 
885*38c8a9a5SSteve French out_free_path:
886*38c8a9a5SSteve French 	kfree(npath);
887*38c8a9a5SSteve French 	return rc;
888*38c8a9a5SSteve French }
889*38c8a9a5SSteve French 
890*38c8a9a5SSteve French /**
891*38c8a9a5SSteve French  * dfs_cache_noreq_find - find a DFS cache entry without sending any requests to
892*38c8a9a5SSteve French  * the currently connected server.
893*38c8a9a5SSteve French  *
894*38c8a9a5SSteve French  * NOTE: This function will neither update a cache entry in case it was
895*38c8a9a5SSteve French  * expired, nor create a new cache entry if @path hasn't been found. It heavily
896*38c8a9a5SSteve French  * relies on an existing cache entry.
897*38c8a9a5SSteve French  *
898*38c8a9a5SSteve French  * @path: canonical DFS path to lookup in the DFS referral cache.
899*38c8a9a5SSteve French  * @ref: when non-NULL, store single DFS referral result in it.
900*38c8a9a5SSteve French  * @tgt_list: when non-NULL, store complete DFS target list in it.
901*38c8a9a5SSteve French  *
902*38c8a9a5SSteve French  * Return 0 if successful.
903*38c8a9a5SSteve French  * Return -ENOENT if the entry was not found.
904*38c8a9a5SSteve French  * Return non-zero for other errors.
905*38c8a9a5SSteve French  */
906*38c8a9a5SSteve French int dfs_cache_noreq_find(const char *path, struct dfs_info3_param *ref,
907*38c8a9a5SSteve French 			 struct dfs_cache_tgt_list *tgt_list)
908*38c8a9a5SSteve French {
909*38c8a9a5SSteve French 	int rc;
910*38c8a9a5SSteve French 	struct cache_entry *ce;
911*38c8a9a5SSteve French 
912*38c8a9a5SSteve French 	cifs_dbg(FYI, "%s: path: %s\n", __func__, path);
913*38c8a9a5SSteve French 
914*38c8a9a5SSteve French 	down_read(&htable_rw_lock);
915*38c8a9a5SSteve French 
916*38c8a9a5SSteve French 	ce = lookup_cache_entry(path);
917*38c8a9a5SSteve French 	if (IS_ERR(ce)) {
918*38c8a9a5SSteve French 		rc = PTR_ERR(ce);
919*38c8a9a5SSteve French 		goto out_unlock;
920*38c8a9a5SSteve French 	}
921*38c8a9a5SSteve French 
922*38c8a9a5SSteve French 	if (ref)
923*38c8a9a5SSteve French 		rc = setup_referral(path, ce, ref, get_tgt_name(ce));
924*38c8a9a5SSteve French 	else
925*38c8a9a5SSteve French 		rc = 0;
926*38c8a9a5SSteve French 	if (!rc && tgt_list)
927*38c8a9a5SSteve French 		rc = get_targets(ce, tgt_list);
928*38c8a9a5SSteve French 
929*38c8a9a5SSteve French out_unlock:
930*38c8a9a5SSteve French 	up_read(&htable_rw_lock);
931*38c8a9a5SSteve French 	return rc;
932*38c8a9a5SSteve French }
933*38c8a9a5SSteve French 
934*38c8a9a5SSteve French /**
935*38c8a9a5SSteve French  * dfs_cache_noreq_update_tgthint - update target hint of a DFS cache entry
936*38c8a9a5SSteve French  * without sending any requests to the currently connected server.
937*38c8a9a5SSteve French  *
938*38c8a9a5SSteve French  * NOTE: This function will neither update a cache entry in case it was
939*38c8a9a5SSteve French  * expired, nor create a new cache entry if @path hasn't been found. It heavily
940*38c8a9a5SSteve French  * relies on an existing cache entry.
941*38c8a9a5SSteve French  *
942*38c8a9a5SSteve French  * @path: canonical DFS path to lookup in DFS referral cache.
943*38c8a9a5SSteve French  * @it: target iterator which contains the target hint to update the cache
944*38c8a9a5SSteve French  * entry with.
945*38c8a9a5SSteve French  *
946*38c8a9a5SSteve French  * Return zero if the target hint was updated successfully, otherwise non-zero.
947*38c8a9a5SSteve French  */
948*38c8a9a5SSteve French void dfs_cache_noreq_update_tgthint(const char *path, const struct dfs_cache_tgt_iterator *it)
949*38c8a9a5SSteve French {
950*38c8a9a5SSteve French 	struct cache_dfs_tgt *t;
951*38c8a9a5SSteve French 	struct cache_entry *ce;
952*38c8a9a5SSteve French 
953*38c8a9a5SSteve French 	if (!path || !it)
954*38c8a9a5SSteve French 		return;
955*38c8a9a5SSteve French 
956*38c8a9a5SSteve French 	cifs_dbg(FYI, "%s: path: %s\n", __func__, path);
957*38c8a9a5SSteve French 
958*38c8a9a5SSteve French 	down_read(&htable_rw_lock);
959*38c8a9a5SSteve French 
960*38c8a9a5SSteve French 	ce = lookup_cache_entry(path);
961*38c8a9a5SSteve French 	if (IS_ERR(ce))
962*38c8a9a5SSteve French 		goto out_unlock;
963*38c8a9a5SSteve French 
964*38c8a9a5SSteve French 	t = READ_ONCE(ce->tgthint);
965*38c8a9a5SSteve French 
966*38c8a9a5SSteve French 	if (unlikely(!strcasecmp(it->it_name, t->name)))
967*38c8a9a5SSteve French 		goto out_unlock;
968*38c8a9a5SSteve French 
969*38c8a9a5SSteve French 	list_for_each_entry(t, &ce->tlist, list) {
970*38c8a9a5SSteve French 		if (!strcasecmp(t->name, it->it_name)) {
971*38c8a9a5SSteve French 			WRITE_ONCE(ce->tgthint, t);
972*38c8a9a5SSteve French 			cifs_dbg(FYI, "%s: new target hint: %s\n", __func__,
973*38c8a9a5SSteve French 				 it->it_name);
974*38c8a9a5SSteve French 			break;
975*38c8a9a5SSteve French 		}
976*38c8a9a5SSteve French 	}
977*38c8a9a5SSteve French 
978*38c8a9a5SSteve French out_unlock:
979*38c8a9a5SSteve French 	up_read(&htable_rw_lock);
980*38c8a9a5SSteve French }
981*38c8a9a5SSteve French 
982*38c8a9a5SSteve French /**
983*38c8a9a5SSteve French  * dfs_cache_get_tgt_referral - returns a DFS referral (@ref) from a given
984*38c8a9a5SSteve French  * target iterator (@it).
985*38c8a9a5SSteve French  *
986*38c8a9a5SSteve French  * @path: canonical DFS path to lookup in DFS referral cache.
987*38c8a9a5SSteve French  * @it: DFS target iterator.
988*38c8a9a5SSteve French  * @ref: DFS referral pointer to set up the gathered information.
989*38c8a9a5SSteve French  *
990*38c8a9a5SSteve French  * Return zero if the DFS referral was set up correctly, otherwise non-zero.
991*38c8a9a5SSteve French  */
992*38c8a9a5SSteve French int dfs_cache_get_tgt_referral(const char *path, const struct dfs_cache_tgt_iterator *it,
993*38c8a9a5SSteve French 			       struct dfs_info3_param *ref)
994*38c8a9a5SSteve French {
995*38c8a9a5SSteve French 	int rc;
996*38c8a9a5SSteve French 	struct cache_entry *ce;
997*38c8a9a5SSteve French 
998*38c8a9a5SSteve French 	if (!it || !ref)
999*38c8a9a5SSteve French 		return -EINVAL;
1000*38c8a9a5SSteve French 
1001*38c8a9a5SSteve French 	cifs_dbg(FYI, "%s: path: %s\n", __func__, path);
1002*38c8a9a5SSteve French 
1003*38c8a9a5SSteve French 	down_read(&htable_rw_lock);
1004*38c8a9a5SSteve French 
1005*38c8a9a5SSteve French 	ce = lookup_cache_entry(path);
1006*38c8a9a5SSteve French 	if (IS_ERR(ce)) {
1007*38c8a9a5SSteve French 		rc = PTR_ERR(ce);
1008*38c8a9a5SSteve French 		goto out_unlock;
1009*38c8a9a5SSteve French 	}
1010*38c8a9a5SSteve French 
1011*38c8a9a5SSteve French 	cifs_dbg(FYI, "%s: target name: %s\n", __func__, it->it_name);
1012*38c8a9a5SSteve French 
1013*38c8a9a5SSteve French 	rc = setup_referral(path, ce, ref, it->it_name);
1014*38c8a9a5SSteve French 
1015*38c8a9a5SSteve French out_unlock:
1016*38c8a9a5SSteve French 	up_read(&htable_rw_lock);
1017*38c8a9a5SSteve French 	return rc;
1018*38c8a9a5SSteve French }
1019*38c8a9a5SSteve French 
1020*38c8a9a5SSteve French /* Extract share from DFS target and return a pointer to prefix path or NULL */
1021*38c8a9a5SSteve French static const char *parse_target_share(const char *target, char **share)
1022*38c8a9a5SSteve French {
1023*38c8a9a5SSteve French 	const char *s, *seps = "/\\";
1024*38c8a9a5SSteve French 	size_t len;
1025*38c8a9a5SSteve French 
1026*38c8a9a5SSteve French 	s = strpbrk(target + 1, seps);
1027*38c8a9a5SSteve French 	if (!s)
1028*38c8a9a5SSteve French 		return ERR_PTR(-EINVAL);
1029*38c8a9a5SSteve French 
1030*38c8a9a5SSteve French 	len = strcspn(s + 1, seps);
1031*38c8a9a5SSteve French 	if (!len)
1032*38c8a9a5SSteve French 		return ERR_PTR(-EINVAL);
1033*38c8a9a5SSteve French 	s += len;
1034*38c8a9a5SSteve French 
1035*38c8a9a5SSteve French 	len = s - target + 1;
1036*38c8a9a5SSteve French 	*share = kstrndup(target, len, GFP_KERNEL);
1037*38c8a9a5SSteve French 	if (!*share)
1038*38c8a9a5SSteve French 		return ERR_PTR(-ENOMEM);
1039*38c8a9a5SSteve French 
1040*38c8a9a5SSteve French 	s = target + len;
1041*38c8a9a5SSteve French 	return s + strspn(s, seps);
1042*38c8a9a5SSteve French }
1043*38c8a9a5SSteve French 
1044*38c8a9a5SSteve French /**
1045*38c8a9a5SSteve French  * dfs_cache_get_tgt_share - parse a DFS target
1046*38c8a9a5SSteve French  *
1047*38c8a9a5SSteve French  * @path: DFS full path
1048*38c8a9a5SSteve French  * @it: DFS target iterator.
1049*38c8a9a5SSteve French  * @share: tree name.
1050*38c8a9a5SSteve French  * @prefix: prefix path.
1051*38c8a9a5SSteve French  *
1052*38c8a9a5SSteve French  * Return zero if target was parsed correctly, otherwise non-zero.
1053*38c8a9a5SSteve French  */
1054*38c8a9a5SSteve French int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it, char **share,
1055*38c8a9a5SSteve French 			    char **prefix)
1056*38c8a9a5SSteve French {
1057*38c8a9a5SSteve French 	char sep;
1058*38c8a9a5SSteve French 	char *target_share;
1059*38c8a9a5SSteve French 	char *ppath = NULL;
1060*38c8a9a5SSteve French 	const char *target_ppath, *dfsref_ppath;
1061*38c8a9a5SSteve French 	size_t target_pplen, dfsref_pplen;
1062*38c8a9a5SSteve French 	size_t len, c;
1063*38c8a9a5SSteve French 
1064*38c8a9a5SSteve French 	if (!it || !path || !share || !prefix || strlen(path) < it->it_path_consumed)
1065*38c8a9a5SSteve French 		return -EINVAL;
1066*38c8a9a5SSteve French 
1067*38c8a9a5SSteve French 	sep = it->it_name[0];
1068*38c8a9a5SSteve French 	if (sep != '\\' && sep != '/')
1069*38c8a9a5SSteve French 		return -EINVAL;
1070*38c8a9a5SSteve French 
1071*38c8a9a5SSteve French 	target_ppath = parse_target_share(it->it_name, &target_share);
1072*38c8a9a5SSteve French 	if (IS_ERR(target_ppath))
1073*38c8a9a5SSteve French 		return PTR_ERR(target_ppath);
1074*38c8a9a5SSteve French 
1075*38c8a9a5SSteve French 	/* point to prefix in DFS referral path */
1076*38c8a9a5SSteve French 	dfsref_ppath = path + it->it_path_consumed;
1077*38c8a9a5SSteve French 	dfsref_ppath += strspn(dfsref_ppath, "/\\");
1078*38c8a9a5SSteve French 
1079*38c8a9a5SSteve French 	target_pplen = strlen(target_ppath);
1080*38c8a9a5SSteve French 	dfsref_pplen = strlen(dfsref_ppath);
1081*38c8a9a5SSteve French 
1082*38c8a9a5SSteve French 	/* merge prefix paths from DFS referral path and target node */
1083*38c8a9a5SSteve French 	if (target_pplen || dfsref_pplen) {
1084*38c8a9a5SSteve French 		len = target_pplen + dfsref_pplen + 2;
1085*38c8a9a5SSteve French 		ppath = kzalloc(len, GFP_KERNEL);
1086*38c8a9a5SSteve French 		if (!ppath) {
1087*38c8a9a5SSteve French 			kfree(target_share);
1088*38c8a9a5SSteve French 			return -ENOMEM;
1089*38c8a9a5SSteve French 		}
1090*38c8a9a5SSteve French 		c = strscpy(ppath, target_ppath, len);
1091*38c8a9a5SSteve French 		if (c && dfsref_pplen)
1092*38c8a9a5SSteve French 			ppath[c] = sep;
1093*38c8a9a5SSteve French 		strlcat(ppath, dfsref_ppath, len);
1094*38c8a9a5SSteve French 	}
1095*38c8a9a5SSteve French 	*share = target_share;
1096*38c8a9a5SSteve French 	*prefix = ppath;
1097*38c8a9a5SSteve French 	return 0;
1098*38c8a9a5SSteve French }
1099*38c8a9a5SSteve French 
1100*38c8a9a5SSteve French static bool target_share_equal(struct TCP_Server_Info *server, const char *s1, const char *s2)
1101*38c8a9a5SSteve French {
1102*38c8a9a5SSteve French 	char unc[sizeof("\\\\") + SERVER_NAME_LENGTH] = {0};
1103*38c8a9a5SSteve French 	const char *host;
1104*38c8a9a5SSteve French 	size_t hostlen;
1105*38c8a9a5SSteve French 	struct sockaddr_storage ss;
1106*38c8a9a5SSteve French 	bool match;
1107*38c8a9a5SSteve French 	int rc;
1108*38c8a9a5SSteve French 
1109*38c8a9a5SSteve French 	if (strcasecmp(s1, s2))
1110*38c8a9a5SSteve French 		return false;
1111*38c8a9a5SSteve French 
1112*38c8a9a5SSteve French 	/*
1113*38c8a9a5SSteve French 	 * Resolve share's hostname and check if server address matches.  Otherwise just ignore it
1114*38c8a9a5SSteve French 	 * as we could not have upcall to resolve hostname or failed to convert ip address.
1115*38c8a9a5SSteve French 	 */
1116*38c8a9a5SSteve French 	extract_unc_hostname(s1, &host, &hostlen);
1117*38c8a9a5SSteve French 	scnprintf(unc, sizeof(unc), "\\\\%.*s", (int)hostlen, host);
1118*38c8a9a5SSteve French 
1119*38c8a9a5SSteve French 	rc = dns_resolve_server_name_to_ip(unc, (struct sockaddr *)&ss, NULL);
1120*38c8a9a5SSteve French 	if (rc < 0) {
1121*38c8a9a5SSteve French 		cifs_dbg(FYI, "%s: could not resolve %.*s. assuming server address matches.\n",
1122*38c8a9a5SSteve French 			 __func__, (int)hostlen, host);
1123*38c8a9a5SSteve French 		return true;
1124*38c8a9a5SSteve French 	}
1125*38c8a9a5SSteve French 
1126*38c8a9a5SSteve French 	cifs_server_lock(server);
1127*38c8a9a5SSteve French 	match = cifs_match_ipaddr((struct sockaddr *)&server->dstaddr, (struct sockaddr *)&ss);
1128*38c8a9a5SSteve French 	cifs_server_unlock(server);
1129*38c8a9a5SSteve French 
1130*38c8a9a5SSteve French 	return match;
1131*38c8a9a5SSteve French }
1132*38c8a9a5SSteve French 
1133*38c8a9a5SSteve French /*
1134*38c8a9a5SSteve French  * Mark dfs tcon for reconnecting when the currently connected tcon does not match any of the new
1135*38c8a9a5SSteve French  * target shares in @refs.
1136*38c8a9a5SSteve French  */
1137*38c8a9a5SSteve French static void mark_for_reconnect_if_needed(struct TCP_Server_Info *server,
1138*38c8a9a5SSteve French 					 const char *path,
1139*38c8a9a5SSteve French 					 struct dfs_cache_tgt_list *old_tl,
1140*38c8a9a5SSteve French 					 struct dfs_cache_tgt_list *new_tl)
1141*38c8a9a5SSteve French {
1142*38c8a9a5SSteve French 	struct dfs_cache_tgt_iterator *oit, *nit;
1143*38c8a9a5SSteve French 
1144*38c8a9a5SSteve French 	for (oit = dfs_cache_get_tgt_iterator(old_tl); oit;
1145*38c8a9a5SSteve French 	     oit = dfs_cache_get_next_tgt(old_tl, oit)) {
1146*38c8a9a5SSteve French 		for (nit = dfs_cache_get_tgt_iterator(new_tl); nit;
1147*38c8a9a5SSteve French 		     nit = dfs_cache_get_next_tgt(new_tl, nit)) {
1148*38c8a9a5SSteve French 			if (target_share_equal(server,
1149*38c8a9a5SSteve French 					       dfs_cache_get_tgt_name(oit),
1150*38c8a9a5SSteve French 					       dfs_cache_get_tgt_name(nit))) {
1151*38c8a9a5SSteve French 				dfs_cache_noreq_update_tgthint(path, nit);
1152*38c8a9a5SSteve French 				return;
1153*38c8a9a5SSteve French 			}
1154*38c8a9a5SSteve French 		}
1155*38c8a9a5SSteve French 	}
1156*38c8a9a5SSteve French 
1157*38c8a9a5SSteve French 	cifs_dbg(FYI, "%s: no cached or matched targets. mark dfs share for reconnect.\n", __func__);
1158*38c8a9a5SSteve French 	cifs_signal_cifsd_for_reconnect(server, true);
1159*38c8a9a5SSteve French }
1160*38c8a9a5SSteve French 
1161*38c8a9a5SSteve French static bool is_ses_good(struct cifs_ses *ses)
1162*38c8a9a5SSteve French {
1163*38c8a9a5SSteve French 	struct TCP_Server_Info *server = ses->server;
1164*38c8a9a5SSteve French 	struct cifs_tcon *tcon = ses->tcon_ipc;
1165*38c8a9a5SSteve French 	bool ret;
1166*38c8a9a5SSteve French 
1167*38c8a9a5SSteve French 	spin_lock(&ses->ses_lock);
1168*38c8a9a5SSteve French 	spin_lock(&ses->chan_lock);
1169*38c8a9a5SSteve French 	ret = !cifs_chan_needs_reconnect(ses, server) &&
1170*38c8a9a5SSteve French 		ses->ses_status == SES_GOOD &&
1171*38c8a9a5SSteve French 		!tcon->need_reconnect;
1172*38c8a9a5SSteve French 	spin_unlock(&ses->chan_lock);
1173*38c8a9a5SSteve French 	spin_unlock(&ses->ses_lock);
1174*38c8a9a5SSteve French 	return ret;
1175*38c8a9a5SSteve French }
1176*38c8a9a5SSteve French 
1177*38c8a9a5SSteve French /* Refresh dfs referral of tcon and mark it for reconnect if needed */
1178*38c8a9a5SSteve French static int __refresh_tcon(const char *path, struct cifs_ses *ses, bool force_refresh)
1179*38c8a9a5SSteve French {
1180*38c8a9a5SSteve French 	struct dfs_cache_tgt_list old_tl = DFS_CACHE_TGT_LIST_INIT(old_tl);
1181*38c8a9a5SSteve French 	struct dfs_cache_tgt_list new_tl = DFS_CACHE_TGT_LIST_INIT(new_tl);
1182*38c8a9a5SSteve French 	struct TCP_Server_Info *server = ses->server;
1183*38c8a9a5SSteve French 	bool needs_refresh = false;
1184*38c8a9a5SSteve French 	struct cache_entry *ce;
1185*38c8a9a5SSteve French 	unsigned int xid;
1186*38c8a9a5SSteve French 	int rc = 0;
1187*38c8a9a5SSteve French 
1188*38c8a9a5SSteve French 	xid = get_xid();
1189*38c8a9a5SSteve French 
1190*38c8a9a5SSteve French 	down_read(&htable_rw_lock);
1191*38c8a9a5SSteve French 	ce = lookup_cache_entry(path);
1192*38c8a9a5SSteve French 	needs_refresh = force_refresh || IS_ERR(ce) || cache_entry_expired(ce);
1193*38c8a9a5SSteve French 	if (!IS_ERR(ce)) {
1194*38c8a9a5SSteve French 		rc = get_targets(ce, &old_tl);
1195*38c8a9a5SSteve French 		cifs_dbg(FYI, "%s: get_targets: %d\n", __func__, rc);
1196*38c8a9a5SSteve French 	}
1197*38c8a9a5SSteve French 	up_read(&htable_rw_lock);
1198*38c8a9a5SSteve French 
1199*38c8a9a5SSteve French 	if (!needs_refresh) {
1200*38c8a9a5SSteve French 		rc = 0;
1201*38c8a9a5SSteve French 		goto out;
1202*38c8a9a5SSteve French 	}
1203*38c8a9a5SSteve French 
1204*38c8a9a5SSteve French 	ses = CIFS_DFS_ROOT_SES(ses);
1205*38c8a9a5SSteve French 	if (!is_ses_good(ses)) {
1206*38c8a9a5SSteve French 		cifs_dbg(FYI, "%s: skip cache refresh due to disconnected ipc\n",
1207*38c8a9a5SSteve French 			 __func__);
1208*38c8a9a5SSteve French 		goto out;
1209*38c8a9a5SSteve French 	}
1210*38c8a9a5SSteve French 
1211*38c8a9a5SSteve French 	ce = cache_refresh_path(xid, ses, path, true);
1212*38c8a9a5SSteve French 	if (!IS_ERR(ce)) {
1213*38c8a9a5SSteve French 		rc = get_targets(ce, &new_tl);
1214*38c8a9a5SSteve French 		up_read(&htable_rw_lock);
1215*38c8a9a5SSteve French 		cifs_dbg(FYI, "%s: get_targets: %d\n", __func__, rc);
1216*38c8a9a5SSteve French 		mark_for_reconnect_if_needed(server, path, &old_tl, &new_tl);
1217*38c8a9a5SSteve French 	}
1218*38c8a9a5SSteve French 
1219*38c8a9a5SSteve French out:
1220*38c8a9a5SSteve French 	free_xid(xid);
1221*38c8a9a5SSteve French 	dfs_cache_free_tgts(&old_tl);
1222*38c8a9a5SSteve French 	dfs_cache_free_tgts(&new_tl);
1223*38c8a9a5SSteve French 	return rc;
1224*38c8a9a5SSteve French }
1225*38c8a9a5SSteve French 
1226*38c8a9a5SSteve French static int refresh_tcon(struct cifs_tcon *tcon, bool force_refresh)
1227*38c8a9a5SSteve French {
1228*38c8a9a5SSteve French 	struct TCP_Server_Info *server = tcon->ses->server;
1229*38c8a9a5SSteve French 	struct cifs_ses *ses = tcon->ses;
1230*38c8a9a5SSteve French 
1231*38c8a9a5SSteve French 	mutex_lock(&server->refpath_lock);
1232*38c8a9a5SSteve French 	if (server->leaf_fullpath)
1233*38c8a9a5SSteve French 		__refresh_tcon(server->leaf_fullpath + 1, ses, force_refresh);
1234*38c8a9a5SSteve French 	mutex_unlock(&server->refpath_lock);
1235*38c8a9a5SSteve French 	return 0;
1236*38c8a9a5SSteve French }
1237*38c8a9a5SSteve French 
1238*38c8a9a5SSteve French /**
1239*38c8a9a5SSteve French  * dfs_cache_remount_fs - remount a DFS share
1240*38c8a9a5SSteve French  *
1241*38c8a9a5SSteve French  * Reconfigure dfs mount by forcing a new DFS referral and if the currently cached targets do not
1242*38c8a9a5SSteve French  * match any of the new targets, mark it for reconnect.
1243*38c8a9a5SSteve French  *
1244*38c8a9a5SSteve French  * @cifs_sb: cifs superblock.
1245*38c8a9a5SSteve French  *
1246*38c8a9a5SSteve French  * Return zero if remounted, otherwise non-zero.
1247*38c8a9a5SSteve French  */
1248*38c8a9a5SSteve French int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb)
1249*38c8a9a5SSteve French {
1250*38c8a9a5SSteve French 	struct cifs_tcon *tcon;
1251*38c8a9a5SSteve French 	struct TCP_Server_Info *server;
1252*38c8a9a5SSteve French 
1253*38c8a9a5SSteve French 	if (!cifs_sb || !cifs_sb->master_tlink)
1254*38c8a9a5SSteve French 		return -EINVAL;
1255*38c8a9a5SSteve French 
1256*38c8a9a5SSteve French 	tcon = cifs_sb_master_tcon(cifs_sb);
1257*38c8a9a5SSteve French 	server = tcon->ses->server;
1258*38c8a9a5SSteve French 
1259*38c8a9a5SSteve French 	if (!server->origin_fullpath) {
1260*38c8a9a5SSteve French 		cifs_dbg(FYI, "%s: not a dfs mount\n", __func__);
1261*38c8a9a5SSteve French 		return 0;
1262*38c8a9a5SSteve French 	}
1263*38c8a9a5SSteve French 	/*
1264*38c8a9a5SSteve French 	 * After reconnecting to a different server, unique ids won't match anymore, so we disable
1265*38c8a9a5SSteve French 	 * serverino. This prevents dentry revalidation to think the dentry are stale (ESTALE).
1266*38c8a9a5SSteve French 	 */
1267*38c8a9a5SSteve French 	cifs_autodisable_serverino(cifs_sb);
1268*38c8a9a5SSteve French 	/*
1269*38c8a9a5SSteve French 	 * Force the use of prefix path to support failover on DFS paths that resolve to targets
1270*38c8a9a5SSteve French 	 * that have different prefix paths.
1271*38c8a9a5SSteve French 	 */
1272*38c8a9a5SSteve French 	cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
1273*38c8a9a5SSteve French 
1274*38c8a9a5SSteve French 	return refresh_tcon(tcon, true);
1275*38c8a9a5SSteve French }
1276*38c8a9a5SSteve French 
1277*38c8a9a5SSteve French /* Refresh all DFS referrals related to DFS tcon */
1278*38c8a9a5SSteve French void dfs_cache_refresh(struct work_struct *work)
1279*38c8a9a5SSteve French {
1280*38c8a9a5SSteve French 	struct TCP_Server_Info *server;
1281*38c8a9a5SSteve French 	struct dfs_root_ses *rses;
1282*38c8a9a5SSteve French 	struct cifs_tcon *tcon;
1283*38c8a9a5SSteve French 	struct cifs_ses *ses;
1284*38c8a9a5SSteve French 
1285*38c8a9a5SSteve French 	tcon = container_of(work, struct cifs_tcon, dfs_cache_work.work);
1286*38c8a9a5SSteve French 	ses = tcon->ses;
1287*38c8a9a5SSteve French 	server = ses->server;
1288*38c8a9a5SSteve French 
1289*38c8a9a5SSteve French 	mutex_lock(&server->refpath_lock);
1290*38c8a9a5SSteve French 	if (server->leaf_fullpath)
1291*38c8a9a5SSteve French 		__refresh_tcon(server->leaf_fullpath + 1, ses, false);
1292*38c8a9a5SSteve French 	mutex_unlock(&server->refpath_lock);
1293*38c8a9a5SSteve French 
1294*38c8a9a5SSteve French 	list_for_each_entry(rses, &tcon->dfs_ses_list, list) {
1295*38c8a9a5SSteve French 		ses = rses->ses;
1296*38c8a9a5SSteve French 		server = ses->server;
1297*38c8a9a5SSteve French 		mutex_lock(&server->refpath_lock);
1298*38c8a9a5SSteve French 		if (server->leaf_fullpath)
1299*38c8a9a5SSteve French 			__refresh_tcon(server->leaf_fullpath + 1, ses, false);
1300*38c8a9a5SSteve French 		mutex_unlock(&server->refpath_lock);
1301*38c8a9a5SSteve French 	}
1302*38c8a9a5SSteve French 
1303*38c8a9a5SSteve French 	queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work,
1304*38c8a9a5SSteve French 			   atomic_read(&dfs_cache_ttl) * HZ);
1305*38c8a9a5SSteve French }
1306