brintos

brintos / linux-shallow public Read only

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