138c8a9a5SSteve French // SPDX-License-Identifier: GPL-2.0 238c8a9a5SSteve French /* 338c8a9a5SSteve French * DFS referral cache routines 438c8a9a5SSteve French * 538c8a9a5SSteve French * Copyright (c) 2018-2019 Paulo Alcantara <palcantara@suse.de> 638c8a9a5SSteve French */ 738c8a9a5SSteve French 838c8a9a5SSteve French #include <linux/jhash.h> 938c8a9a5SSteve French #include <linux/ktime.h> 1038c8a9a5SSteve French #include <linux/slab.h> 1138c8a9a5SSteve French #include <linux/proc_fs.h> 1238c8a9a5SSteve French #include <linux/nls.h> 1338c8a9a5SSteve French #include <linux/workqueue.h> 1438c8a9a5SSteve French #include <linux/uuid.h> 1538c8a9a5SSteve French #include "cifsglob.h" 1638c8a9a5SSteve French #include "smb2pdu.h" 1738c8a9a5SSteve French #include "smb2proto.h" 1838c8a9a5SSteve French #include "cifsproto.h" 1938c8a9a5SSteve French #include "cifs_debug.h" 2038c8a9a5SSteve French #include "cifs_unicode.h" 2138c8a9a5SSteve French #include "smb2glob.h" 2238c8a9a5SSteve French #include "dns_resolve.h" 2338c8a9a5SSteve French #include "dfs.h" 2438c8a9a5SSteve French 2538c8a9a5SSteve French #include "dfs_cache.h" 2638c8a9a5SSteve French 2738c8a9a5SSteve French #define CACHE_HTABLE_SIZE 32 2838c8a9a5SSteve French #define CACHE_MAX_ENTRIES 64 2938c8a9a5SSteve French #define CACHE_MIN_TTL 120 /* 2 minutes */ 3038c8a9a5SSteve French #define CACHE_DEFAULT_TTL 300 /* 5 minutes */ 3138c8a9a5SSteve French 3238c8a9a5SSteve French #define IS_DFS_INTERLINK(v) (((v) & DFSREF_REFERRAL_SERVER) && !((v) & DFSREF_STORAGE_SERVER)) 3338c8a9a5SSteve French 3438c8a9a5SSteve French struct cache_dfs_tgt { 3538c8a9a5SSteve French char *name; 3638c8a9a5SSteve French int path_consumed; 3738c8a9a5SSteve French struct list_head list; 3838c8a9a5SSteve French }; 3938c8a9a5SSteve French 4038c8a9a5SSteve French struct cache_entry { 4138c8a9a5SSteve French struct hlist_node hlist; 4238c8a9a5SSteve French const char *path; 4338c8a9a5SSteve French int hdr_flags; /* RESP_GET_DFS_REFERRAL.ReferralHeaderFlags */ 4438c8a9a5SSteve French int ttl; /* DFS_REREFERRAL_V3.TimeToLive */ 4538c8a9a5SSteve French int srvtype; /* DFS_REREFERRAL_V3.ServerType */ 4638c8a9a5SSteve French int ref_flags; /* DFS_REREFERRAL_V3.ReferralEntryFlags */ 4738c8a9a5SSteve French struct timespec64 etime; 4838c8a9a5SSteve French int path_consumed; /* RESP_GET_DFS_REFERRAL.PathConsumed */ 4938c8a9a5SSteve French int numtgts; 5038c8a9a5SSteve French struct list_head tlist; 5138c8a9a5SSteve French struct cache_dfs_tgt *tgthint; 5238c8a9a5SSteve French }; 5338c8a9a5SSteve French 5438c8a9a5SSteve French static struct kmem_cache *cache_slab __read_mostly; 5538c8a9a5SSteve French struct workqueue_struct *dfscache_wq; 5638c8a9a5SSteve French 5738c8a9a5SSteve French atomic_t dfs_cache_ttl; 5838c8a9a5SSteve French 5938c8a9a5SSteve French static struct nls_table *cache_cp; 6038c8a9a5SSteve French 6138c8a9a5SSteve French /* 6238c8a9a5SSteve French * Number of entries in the cache 6338c8a9a5SSteve French */ 6438c8a9a5SSteve French static atomic_t cache_count; 6538c8a9a5SSteve French 6638c8a9a5SSteve French static struct hlist_head cache_htable[CACHE_HTABLE_SIZE]; 6738c8a9a5SSteve French static DECLARE_RWSEM(htable_rw_lock); 6838c8a9a5SSteve French 6938c8a9a5SSteve French /** 7038c8a9a5SSteve French * dfs_cache_canonical_path - get a canonical DFS path 7138c8a9a5SSteve French * 7238c8a9a5SSteve French * @path: DFS path 7338c8a9a5SSteve French * @cp: codepage 7438c8a9a5SSteve French * @remap: mapping type 7538c8a9a5SSteve French * 7638c8a9a5SSteve French * Return canonical path if success, otherwise error. 7738c8a9a5SSteve French */ 7838c8a9a5SSteve French char *dfs_cache_canonical_path(const char *path, const struct nls_table *cp, int remap) 7938c8a9a5SSteve French { 8038c8a9a5SSteve French char *tmp; 8138c8a9a5SSteve French int plen = 0; 8238c8a9a5SSteve French char *npath; 8338c8a9a5SSteve French 8438c8a9a5SSteve French if (!path || strlen(path) < 3 || (*path != '\\' && *path != '/')) 8538c8a9a5SSteve French return ERR_PTR(-EINVAL); 8638c8a9a5SSteve French 8738c8a9a5SSteve French if (unlikely(strcmp(cp->charset, cache_cp->charset))) { 8838c8a9a5SSteve French tmp = (char *)cifs_strndup_to_utf16(path, strlen(path), &plen, cp, remap); 8938c8a9a5SSteve French if (!tmp) { 9038c8a9a5SSteve French cifs_dbg(VFS, "%s: failed to convert path to utf16\n", __func__); 9138c8a9a5SSteve French return ERR_PTR(-EINVAL); 9238c8a9a5SSteve French } 9338c8a9a5SSteve French 9438c8a9a5SSteve French npath = cifs_strndup_from_utf16(tmp, plen, true, cache_cp); 9538c8a9a5SSteve French kfree(tmp); 9638c8a9a5SSteve French 9738c8a9a5SSteve French if (!npath) { 9838c8a9a5SSteve French cifs_dbg(VFS, "%s: failed to convert path from utf16\n", __func__); 9938c8a9a5SSteve French return ERR_PTR(-EINVAL); 10038c8a9a5SSteve French } 10138c8a9a5SSteve French } else { 10238c8a9a5SSteve French npath = kstrdup(path, GFP_KERNEL); 10338c8a9a5SSteve French if (!npath) 10438c8a9a5SSteve French return ERR_PTR(-ENOMEM); 10538c8a9a5SSteve French } 10638c8a9a5SSteve French convert_delimiter(npath, '\\'); 10738c8a9a5SSteve French return npath; 10838c8a9a5SSteve French } 10938c8a9a5SSteve French 11038c8a9a5SSteve French static inline bool cache_entry_expired(const struct cache_entry *ce) 11138c8a9a5SSteve French { 11238c8a9a5SSteve French struct timespec64 ts; 11338c8a9a5SSteve French 11438c8a9a5SSteve French ktime_get_coarse_real_ts64(&ts); 11538c8a9a5SSteve French return timespec64_compare(&ts, &ce->etime) >= 0; 11638c8a9a5SSteve French } 11738c8a9a5SSteve French 11838c8a9a5SSteve French static inline void free_tgts(struct cache_entry *ce) 11938c8a9a5SSteve French { 12038c8a9a5SSteve French struct cache_dfs_tgt *t, *n; 12138c8a9a5SSteve French 12238c8a9a5SSteve French list_for_each_entry_safe(t, n, &ce->tlist, list) { 12338c8a9a5SSteve French list_del(&t->list); 12438c8a9a5SSteve French kfree(t->name); 12538c8a9a5SSteve French kfree(t); 12638c8a9a5SSteve French } 12738c8a9a5SSteve French } 12838c8a9a5SSteve French 12938c8a9a5SSteve French static inline void flush_cache_ent(struct cache_entry *ce) 13038c8a9a5SSteve French { 13138c8a9a5SSteve French hlist_del_init(&ce->hlist); 13238c8a9a5SSteve French kfree(ce->path); 13338c8a9a5SSteve French free_tgts(ce); 13438c8a9a5SSteve French atomic_dec(&cache_count); 13538c8a9a5SSteve French kmem_cache_free(cache_slab, ce); 13638c8a9a5SSteve French } 13738c8a9a5SSteve French 13838c8a9a5SSteve French static void flush_cache_ents(void) 13938c8a9a5SSteve French { 14038c8a9a5SSteve French int i; 14138c8a9a5SSteve French 14238c8a9a5SSteve French for (i = 0; i < CACHE_HTABLE_SIZE; i++) { 14338c8a9a5SSteve French struct hlist_head *l = &cache_htable[i]; 14438c8a9a5SSteve French struct hlist_node *n; 14538c8a9a5SSteve French struct cache_entry *ce; 14638c8a9a5SSteve French 14738c8a9a5SSteve French hlist_for_each_entry_safe(ce, n, l, hlist) { 14838c8a9a5SSteve French if (!hlist_unhashed(&ce->hlist)) 14938c8a9a5SSteve French flush_cache_ent(ce); 15038c8a9a5SSteve French } 15138c8a9a5SSteve French } 15238c8a9a5SSteve French } 15338c8a9a5SSteve French 15438c8a9a5SSteve French /* 15538c8a9a5SSteve French * dfs cache /proc file 15638c8a9a5SSteve French */ 15738c8a9a5SSteve French static int dfscache_proc_show(struct seq_file *m, void *v) 15838c8a9a5SSteve French { 15938c8a9a5SSteve French int i; 16038c8a9a5SSteve French struct cache_entry *ce; 16138c8a9a5SSteve French struct cache_dfs_tgt *t; 16238c8a9a5SSteve French 16338c8a9a5SSteve French seq_puts(m, "DFS cache\n---------\n"); 16438c8a9a5SSteve French 16538c8a9a5SSteve French down_read(&htable_rw_lock); 16638c8a9a5SSteve French for (i = 0; i < CACHE_HTABLE_SIZE; i++) { 16738c8a9a5SSteve French struct hlist_head *l = &cache_htable[i]; 16838c8a9a5SSteve French 16938c8a9a5SSteve French hlist_for_each_entry(ce, l, hlist) { 17038c8a9a5SSteve French if (hlist_unhashed(&ce->hlist)) 17138c8a9a5SSteve French continue; 17238c8a9a5SSteve French 17338c8a9a5SSteve French seq_printf(m, 17438c8a9a5SSteve 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", 17538c8a9a5SSteve French ce->path, ce->srvtype == DFS_TYPE_ROOT ? "root" : "link", 17638c8a9a5SSteve French ce->ttl, ce->etime.tv_nsec, ce->hdr_flags, ce->ref_flags, 17738c8a9a5SSteve French IS_DFS_INTERLINK(ce->hdr_flags) ? "yes" : "no", 17838c8a9a5SSteve French ce->path_consumed, cache_entry_expired(ce) ? "yes" : "no"); 17938c8a9a5SSteve French 18038c8a9a5SSteve French list_for_each_entry(t, &ce->tlist, list) { 18138c8a9a5SSteve French seq_printf(m, " %s%s\n", 18238c8a9a5SSteve French t->name, 18338c8a9a5SSteve French READ_ONCE(ce->tgthint) == t ? " (target hint)" : ""); 18438c8a9a5SSteve French } 18538c8a9a5SSteve French } 18638c8a9a5SSteve French } 18738c8a9a5SSteve French up_read(&htable_rw_lock); 18838c8a9a5SSteve French 18938c8a9a5SSteve French return 0; 19038c8a9a5SSteve French } 19138c8a9a5SSteve French 19238c8a9a5SSteve French static ssize_t dfscache_proc_write(struct file *file, const char __user *buffer, 19338c8a9a5SSteve French size_t count, loff_t *ppos) 19438c8a9a5SSteve French { 19538c8a9a5SSteve French char c; 19638c8a9a5SSteve French int rc; 19738c8a9a5SSteve French 19838c8a9a5SSteve French rc = get_user(c, buffer); 19938c8a9a5SSteve French if (rc) 20038c8a9a5SSteve French return rc; 20138c8a9a5SSteve French 20238c8a9a5SSteve French if (c != '0') 20338c8a9a5SSteve French return -EINVAL; 20438c8a9a5SSteve French 20538c8a9a5SSteve French cifs_dbg(FYI, "clearing dfs cache\n"); 20638c8a9a5SSteve French 20738c8a9a5SSteve French down_write(&htable_rw_lock); 20838c8a9a5SSteve French flush_cache_ents(); 20938c8a9a5SSteve French up_write(&htable_rw_lock); 21038c8a9a5SSteve French 21138c8a9a5SSteve French return count; 21238c8a9a5SSteve French } 21338c8a9a5SSteve French 21438c8a9a5SSteve French static int dfscache_proc_open(struct inode *inode, struct file *file) 21538c8a9a5SSteve French { 21638c8a9a5SSteve French return single_open(file, dfscache_proc_show, NULL); 21738c8a9a5SSteve French } 21838c8a9a5SSteve French 21938c8a9a5SSteve French const struct proc_ops dfscache_proc_ops = { 22038c8a9a5SSteve French .proc_open = dfscache_proc_open, 22138c8a9a5SSteve French .proc_read = seq_read, 22238c8a9a5SSteve French .proc_lseek = seq_lseek, 22338c8a9a5SSteve French .proc_release = single_release, 22438c8a9a5SSteve French .proc_write = dfscache_proc_write, 22538c8a9a5SSteve French }; 22638c8a9a5SSteve French 22738c8a9a5SSteve French #ifdef CONFIG_CIFS_DEBUG2 22838c8a9a5SSteve French static inline void dump_tgts(const struct cache_entry *ce) 22938c8a9a5SSteve French { 23038c8a9a5SSteve French struct cache_dfs_tgt *t; 23138c8a9a5SSteve French 23238c8a9a5SSteve French cifs_dbg(FYI, "target list:\n"); 23338c8a9a5SSteve French list_for_each_entry(t, &ce->tlist, list) { 23438c8a9a5SSteve French cifs_dbg(FYI, " %s%s\n", t->name, 23538c8a9a5SSteve French READ_ONCE(ce->tgthint) == t ? " (target hint)" : ""); 23638c8a9a5SSteve French } 23738c8a9a5SSteve French } 23838c8a9a5SSteve French 23938c8a9a5SSteve French static inline void dump_ce(const struct cache_entry *ce) 24038c8a9a5SSteve French { 24138c8a9a5SSteve 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", 24238c8a9a5SSteve French ce->path, 24338c8a9a5SSteve French ce->srvtype == DFS_TYPE_ROOT ? "root" : "link", ce->ttl, 24438c8a9a5SSteve French ce->etime.tv_nsec, 24538c8a9a5SSteve French ce->hdr_flags, ce->ref_flags, 24638c8a9a5SSteve French IS_DFS_INTERLINK(ce->hdr_flags) ? "yes" : "no", 24738c8a9a5SSteve French ce->path_consumed, 24838c8a9a5SSteve French cache_entry_expired(ce) ? "yes" : "no"); 24938c8a9a5SSteve French dump_tgts(ce); 25038c8a9a5SSteve French } 25138c8a9a5SSteve French 25238c8a9a5SSteve French static inline void dump_refs(const struct dfs_info3_param *refs, int numrefs) 25338c8a9a5SSteve French { 25438c8a9a5SSteve French int i; 25538c8a9a5SSteve French 25638c8a9a5SSteve French cifs_dbg(FYI, "DFS referrals returned by the server:\n"); 25738c8a9a5SSteve French for (i = 0; i < numrefs; i++) { 25838c8a9a5SSteve French const struct dfs_info3_param *ref = &refs[i]; 25938c8a9a5SSteve French 26038c8a9a5SSteve French cifs_dbg(FYI, 26138c8a9a5SSteve French "\n" 26238c8a9a5SSteve French "flags: 0x%x\n" 26338c8a9a5SSteve French "path_consumed: %d\n" 26438c8a9a5SSteve French "server_type: 0x%x\n" 26538c8a9a5SSteve French "ref_flag: 0x%x\n" 26638c8a9a5SSteve French "path_name: %s\n" 26738c8a9a5SSteve French "node_name: %s\n" 26838c8a9a5SSteve French "ttl: %d (%dm)\n", 26938c8a9a5SSteve French ref->flags, ref->path_consumed, ref->server_type, 27038c8a9a5SSteve French ref->ref_flag, ref->path_name, ref->node_name, 27138c8a9a5SSteve French ref->ttl, ref->ttl / 60); 27238c8a9a5SSteve French } 27338c8a9a5SSteve French } 27438c8a9a5SSteve French #else 27538c8a9a5SSteve French #define dump_tgts(e) 27638c8a9a5SSteve French #define dump_ce(e) 27738c8a9a5SSteve French #define dump_refs(r, n) 27838c8a9a5SSteve French #endif 27938c8a9a5SSteve French 28038c8a9a5SSteve French /** 28138c8a9a5SSteve French * dfs_cache_init - Initialize DFS referral cache. 28238c8a9a5SSteve French * 28338c8a9a5SSteve French * Return zero if initialized successfully, otherwise non-zero. 28438c8a9a5SSteve French */ 28538c8a9a5SSteve French int dfs_cache_init(void) 28638c8a9a5SSteve French { 28738c8a9a5SSteve French int rc; 28838c8a9a5SSteve French int i; 28938c8a9a5SSteve French 29038c8a9a5SSteve French dfscache_wq = alloc_workqueue("cifs-dfscache", 29138c8a9a5SSteve French WQ_UNBOUND|WQ_FREEZABLE|WQ_MEM_RECLAIM, 29238c8a9a5SSteve French 0); 29338c8a9a5SSteve French if (!dfscache_wq) 29438c8a9a5SSteve French return -ENOMEM; 29538c8a9a5SSteve French 29638c8a9a5SSteve French cache_slab = kmem_cache_create("cifs_dfs_cache", 29738c8a9a5SSteve French sizeof(struct cache_entry), 0, 29838c8a9a5SSteve French SLAB_HWCACHE_ALIGN, NULL); 29938c8a9a5SSteve French if (!cache_slab) { 30038c8a9a5SSteve French rc = -ENOMEM; 30138c8a9a5SSteve French goto out_destroy_wq; 30238c8a9a5SSteve French } 30338c8a9a5SSteve French 30438c8a9a5SSteve French for (i = 0; i < CACHE_HTABLE_SIZE; i++) 30538c8a9a5SSteve French INIT_HLIST_HEAD(&cache_htable[i]); 30638c8a9a5SSteve French 30738c8a9a5SSteve French atomic_set(&cache_count, 0); 30838c8a9a5SSteve French atomic_set(&dfs_cache_ttl, CACHE_DEFAULT_TTL); 30938c8a9a5SSteve French cache_cp = load_nls("utf8"); 31038c8a9a5SSteve French if (!cache_cp) 31138c8a9a5SSteve French cache_cp = load_nls_default(); 31238c8a9a5SSteve French 31338c8a9a5SSteve French cifs_dbg(FYI, "%s: initialized DFS referral cache\n", __func__); 31438c8a9a5SSteve French return 0; 31538c8a9a5SSteve French 31638c8a9a5SSteve French out_destroy_wq: 31738c8a9a5SSteve French destroy_workqueue(dfscache_wq); 31838c8a9a5SSteve French return rc; 31938c8a9a5SSteve French } 32038c8a9a5SSteve French 32138c8a9a5SSteve French static int cache_entry_hash(const void *data, int size, unsigned int *hash) 32238c8a9a5SSteve French { 32338c8a9a5SSteve French int i, clen; 32438c8a9a5SSteve French const unsigned char *s = data; 32538c8a9a5SSteve French wchar_t c; 32638c8a9a5SSteve French unsigned int h = 0; 32738c8a9a5SSteve French 32838c8a9a5SSteve French for (i = 0; i < size; i += clen) { 32938c8a9a5SSteve French clen = cache_cp->char2uni(&s[i], size - i, &c); 33038c8a9a5SSteve French if (unlikely(clen < 0)) { 33138c8a9a5SSteve French cifs_dbg(VFS, "%s: can't convert char\n", __func__); 33238c8a9a5SSteve French return clen; 33338c8a9a5SSteve French } 33438c8a9a5SSteve French c = cifs_toupper(c); 33538c8a9a5SSteve French h = jhash(&c, sizeof(c), h); 33638c8a9a5SSteve French } 33738c8a9a5SSteve French *hash = h % CACHE_HTABLE_SIZE; 33838c8a9a5SSteve French return 0; 33938c8a9a5SSteve French } 34038c8a9a5SSteve French 34138c8a9a5SSteve French /* Return target hint of a DFS cache entry */ 34238c8a9a5SSteve French static inline char *get_tgt_name(const struct cache_entry *ce) 34338c8a9a5SSteve French { 34438c8a9a5SSteve French struct cache_dfs_tgt *t = READ_ONCE(ce->tgthint); 34538c8a9a5SSteve French 34638c8a9a5SSteve French return t ? t->name : ERR_PTR(-ENOENT); 34738c8a9a5SSteve French } 34838c8a9a5SSteve French 34938c8a9a5SSteve French /* Return expire time out of a new entry's TTL */ 35038c8a9a5SSteve French static inline struct timespec64 get_expire_time(int ttl) 35138c8a9a5SSteve French { 35238c8a9a5SSteve French struct timespec64 ts = { 35338c8a9a5SSteve French .tv_sec = ttl, 35438c8a9a5SSteve French .tv_nsec = 0, 35538c8a9a5SSteve French }; 35638c8a9a5SSteve French struct timespec64 now; 35738c8a9a5SSteve French 35838c8a9a5SSteve French ktime_get_coarse_real_ts64(&now); 35938c8a9a5SSteve French return timespec64_add(now, ts); 36038c8a9a5SSteve French } 36138c8a9a5SSteve French 36238c8a9a5SSteve French /* Allocate a new DFS target */ 36338c8a9a5SSteve French static struct cache_dfs_tgt *alloc_target(const char *name, int path_consumed) 36438c8a9a5SSteve French { 36538c8a9a5SSteve French struct cache_dfs_tgt *t; 36638c8a9a5SSteve French 36738c8a9a5SSteve French t = kmalloc(sizeof(*t), GFP_ATOMIC); 36838c8a9a5SSteve French if (!t) 36938c8a9a5SSteve French return ERR_PTR(-ENOMEM); 37038c8a9a5SSteve French t->name = kstrdup(name, GFP_ATOMIC); 37138c8a9a5SSteve French if (!t->name) { 37238c8a9a5SSteve French kfree(t); 37338c8a9a5SSteve French return ERR_PTR(-ENOMEM); 37438c8a9a5SSteve French } 37538c8a9a5SSteve French t->path_consumed = path_consumed; 37638c8a9a5SSteve French INIT_LIST_HEAD(&t->list); 37738c8a9a5SSteve French return t; 37838c8a9a5SSteve French } 37938c8a9a5SSteve French 38038c8a9a5SSteve French /* 38138c8a9a5SSteve French * Copy DFS referral information to a cache entry and conditionally update 38238c8a9a5SSteve French * target hint. 38338c8a9a5SSteve French */ 38438c8a9a5SSteve French static int copy_ref_data(const struct dfs_info3_param *refs, int numrefs, 38538c8a9a5SSteve French struct cache_entry *ce, const char *tgthint) 38638c8a9a5SSteve French { 38738c8a9a5SSteve French struct cache_dfs_tgt *target; 38838c8a9a5SSteve French int i; 38938c8a9a5SSteve French 39038c8a9a5SSteve French ce->ttl = max_t(int, refs[0].ttl, CACHE_MIN_TTL); 39138c8a9a5SSteve French ce->etime = get_expire_time(ce->ttl); 39238c8a9a5SSteve French ce->srvtype = refs[0].server_type; 39338c8a9a5SSteve French ce->hdr_flags = refs[0].flags; 39438c8a9a5SSteve French ce->ref_flags = refs[0].ref_flag; 39538c8a9a5SSteve French ce->path_consumed = refs[0].path_consumed; 39638c8a9a5SSteve French 39738c8a9a5SSteve French for (i = 0; i < numrefs; i++) { 39838c8a9a5SSteve French struct cache_dfs_tgt *t; 39938c8a9a5SSteve French 40038c8a9a5SSteve French t = alloc_target(refs[i].node_name, refs[i].path_consumed); 40138c8a9a5SSteve French if (IS_ERR(t)) { 40238c8a9a5SSteve French free_tgts(ce); 40338c8a9a5SSteve French return PTR_ERR(t); 40438c8a9a5SSteve French } 40538c8a9a5SSteve French if (tgthint && !strcasecmp(t->name, tgthint)) { 40638c8a9a5SSteve French list_add(&t->list, &ce->tlist); 40738c8a9a5SSteve French tgthint = NULL; 40838c8a9a5SSteve French } else { 40938c8a9a5SSteve French list_add_tail(&t->list, &ce->tlist); 41038c8a9a5SSteve French } 41138c8a9a5SSteve French ce->numtgts++; 41238c8a9a5SSteve French } 41338c8a9a5SSteve French 41438c8a9a5SSteve French target = list_first_entry_or_null(&ce->tlist, struct cache_dfs_tgt, 41538c8a9a5SSteve French list); 41638c8a9a5SSteve French WRITE_ONCE(ce->tgthint, target); 41738c8a9a5SSteve French 41838c8a9a5SSteve French return 0; 41938c8a9a5SSteve French } 42038c8a9a5SSteve French 42138c8a9a5SSteve French /* Allocate a new cache entry */ 42238c8a9a5SSteve French static struct cache_entry *alloc_cache_entry(struct dfs_info3_param *refs, int numrefs) 42338c8a9a5SSteve French { 42438c8a9a5SSteve French struct cache_entry *ce; 42538c8a9a5SSteve French int rc; 42638c8a9a5SSteve French 42738c8a9a5SSteve French ce = kmem_cache_zalloc(cache_slab, GFP_KERNEL); 42838c8a9a5SSteve French if (!ce) 42938c8a9a5SSteve French return ERR_PTR(-ENOMEM); 43038c8a9a5SSteve French 43138c8a9a5SSteve French ce->path = refs[0].path_name; 43238c8a9a5SSteve French refs[0].path_name = NULL; 43338c8a9a5SSteve French 43438c8a9a5SSteve French INIT_HLIST_NODE(&ce->hlist); 43538c8a9a5SSteve French INIT_LIST_HEAD(&ce->tlist); 43638c8a9a5SSteve French 43738c8a9a5SSteve French rc = copy_ref_data(refs, numrefs, ce, NULL); 43838c8a9a5SSteve French if (rc) { 43938c8a9a5SSteve French kfree(ce->path); 44038c8a9a5SSteve French kmem_cache_free(cache_slab, ce); 44138c8a9a5SSteve French ce = ERR_PTR(rc); 44238c8a9a5SSteve French } 44338c8a9a5SSteve French return ce; 44438c8a9a5SSteve French } 44538c8a9a5SSteve French 44638c8a9a5SSteve French static void remove_oldest_entry_locked(void) 44738c8a9a5SSteve French { 44838c8a9a5SSteve French int i; 44938c8a9a5SSteve French struct cache_entry *ce; 45038c8a9a5SSteve French struct cache_entry *to_del = NULL; 45138c8a9a5SSteve French 45238c8a9a5SSteve French WARN_ON(!rwsem_is_locked(&htable_rw_lock)); 45338c8a9a5SSteve French 45438c8a9a5SSteve French for (i = 0; i < CACHE_HTABLE_SIZE; i++) { 45538c8a9a5SSteve French struct hlist_head *l = &cache_htable[i]; 45638c8a9a5SSteve French 45738c8a9a5SSteve French hlist_for_each_entry(ce, l, hlist) { 45838c8a9a5SSteve French if (hlist_unhashed(&ce->hlist)) 45938c8a9a5SSteve French continue; 46038c8a9a5SSteve French if (!to_del || timespec64_compare(&ce->etime, 46138c8a9a5SSteve French &to_del->etime) < 0) 46238c8a9a5SSteve French to_del = ce; 46338c8a9a5SSteve French } 46438c8a9a5SSteve French } 46538c8a9a5SSteve French 46638c8a9a5SSteve French if (!to_del) { 46738c8a9a5SSteve French cifs_dbg(FYI, "%s: no entry to remove\n", __func__); 46838c8a9a5SSteve French return; 46938c8a9a5SSteve French } 47038c8a9a5SSteve French 47138c8a9a5SSteve French cifs_dbg(FYI, "%s: removing entry\n", __func__); 47238c8a9a5SSteve French dump_ce(to_del); 47338c8a9a5SSteve French flush_cache_ent(to_del); 47438c8a9a5SSteve French } 47538c8a9a5SSteve French 47638c8a9a5SSteve French /* Add a new DFS cache entry */ 47738c8a9a5SSteve French static struct cache_entry *add_cache_entry_locked(struct dfs_info3_param *refs, 47838c8a9a5SSteve French int numrefs) 47938c8a9a5SSteve French { 48038c8a9a5SSteve French int rc; 48138c8a9a5SSteve French struct cache_entry *ce; 48238c8a9a5SSteve French unsigned int hash; 48338c8a9a5SSteve French int ttl; 48438c8a9a5SSteve French 48538c8a9a5SSteve French WARN_ON(!rwsem_is_locked(&htable_rw_lock)); 48638c8a9a5SSteve French 48738c8a9a5SSteve French if (atomic_read(&cache_count) >= CACHE_MAX_ENTRIES) { 48838c8a9a5SSteve French cifs_dbg(FYI, "%s: reached max cache size (%d)\n", __func__, CACHE_MAX_ENTRIES); 48938c8a9a5SSteve French remove_oldest_entry_locked(); 49038c8a9a5SSteve French } 49138c8a9a5SSteve French 49238c8a9a5SSteve French rc = cache_entry_hash(refs[0].path_name, strlen(refs[0].path_name), &hash); 49338c8a9a5SSteve French if (rc) 49438c8a9a5SSteve French return ERR_PTR(rc); 49538c8a9a5SSteve French 49638c8a9a5SSteve French ce = alloc_cache_entry(refs, numrefs); 49738c8a9a5SSteve French if (IS_ERR(ce)) 49838c8a9a5SSteve French return ce; 49938c8a9a5SSteve French 50038c8a9a5SSteve French ttl = min_t(int, atomic_read(&dfs_cache_ttl), ce->ttl); 50138c8a9a5SSteve French atomic_set(&dfs_cache_ttl, ttl); 50238c8a9a5SSteve French 50338c8a9a5SSteve French hlist_add_head(&ce->hlist, &cache_htable[hash]); 50438c8a9a5SSteve French dump_ce(ce); 50538c8a9a5SSteve French 50638c8a9a5SSteve French atomic_inc(&cache_count); 50738c8a9a5SSteve French 50838c8a9a5SSteve French return ce; 50938c8a9a5SSteve French } 51038c8a9a5SSteve French 51138c8a9a5SSteve French /* Check if two DFS paths are equal. @s1 and @s2 are expected to be in @cache_cp's charset */ 51238c8a9a5SSteve French static bool dfs_path_equal(const char *s1, int len1, const char *s2, int len2) 51338c8a9a5SSteve French { 51438c8a9a5SSteve French int i, l1, l2; 51538c8a9a5SSteve French wchar_t c1, c2; 51638c8a9a5SSteve French 51738c8a9a5SSteve French if (len1 != len2) 51838c8a9a5SSteve French return false; 51938c8a9a5SSteve French 52038c8a9a5SSteve French for (i = 0; i < len1; i += l1) { 52138c8a9a5SSteve French l1 = cache_cp->char2uni(&s1[i], len1 - i, &c1); 52238c8a9a5SSteve French l2 = cache_cp->char2uni(&s2[i], len2 - i, &c2); 52338c8a9a5SSteve French if (unlikely(l1 < 0 && l2 < 0)) { 52438c8a9a5SSteve French if (s1[i] != s2[i]) 52538c8a9a5SSteve French return false; 52638c8a9a5SSteve French l1 = 1; 52738c8a9a5SSteve French continue; 52838c8a9a5SSteve French } 52938c8a9a5SSteve French if (l1 != l2) 53038c8a9a5SSteve French return false; 53138c8a9a5SSteve French if (cifs_toupper(c1) != cifs_toupper(c2)) 53238c8a9a5SSteve French return false; 53338c8a9a5SSteve French } 53438c8a9a5SSteve French return true; 53538c8a9a5SSteve French } 53638c8a9a5SSteve French 53738c8a9a5SSteve French static struct cache_entry *__lookup_cache_entry(const char *path, unsigned int hash, int len) 53838c8a9a5SSteve French { 53938c8a9a5SSteve French struct cache_entry *ce; 54038c8a9a5SSteve French 54138c8a9a5SSteve French hlist_for_each_entry(ce, &cache_htable[hash], hlist) { 54238c8a9a5SSteve French if (dfs_path_equal(ce->path, strlen(ce->path), path, len)) { 54338c8a9a5SSteve French dump_ce(ce); 54438c8a9a5SSteve French return ce; 54538c8a9a5SSteve French } 54638c8a9a5SSteve French } 54738c8a9a5SSteve French return ERR_PTR(-ENOENT); 54838c8a9a5SSteve French } 54938c8a9a5SSteve French 55038c8a9a5SSteve French /* 55138c8a9a5SSteve French * Find a DFS cache entry in hash table and optionally check prefix path against normalized @path. 55238c8a9a5SSteve French * 55338c8a9a5SSteve French * Use whole path components in the match. Must be called with htable_rw_lock held. 55438c8a9a5SSteve French * 55538c8a9a5SSteve French * Return cached entry if successful. 55638c8a9a5SSteve French * Return ERR_PTR(-ENOENT) if the entry is not found. 55738c8a9a5SSteve French * Return error ptr otherwise. 55838c8a9a5SSteve French */ 55938c8a9a5SSteve French static struct cache_entry *lookup_cache_entry(const char *path) 56038c8a9a5SSteve French { 56138c8a9a5SSteve French struct cache_entry *ce; 56238c8a9a5SSteve French int cnt = 0; 56338c8a9a5SSteve French const char *s = path, *e; 56438c8a9a5SSteve French char sep = *s; 56538c8a9a5SSteve French unsigned int hash; 56638c8a9a5SSteve French int rc; 56738c8a9a5SSteve French 56838c8a9a5SSteve French while ((s = strchr(s, sep)) && ++cnt < 3) 56938c8a9a5SSteve French s++; 57038c8a9a5SSteve French 57138c8a9a5SSteve French if (cnt < 3) { 57238c8a9a5SSteve French rc = cache_entry_hash(path, strlen(path), &hash); 57338c8a9a5SSteve French if (rc) 57438c8a9a5SSteve French return ERR_PTR(rc); 57538c8a9a5SSteve French return __lookup_cache_entry(path, hash, strlen(path)); 57638c8a9a5SSteve French } 57738c8a9a5SSteve French /* 57838c8a9a5SSteve French * Handle paths that have more than two path components and are a complete prefix of the DFS 57938c8a9a5SSteve French * referral request path (@path). 58038c8a9a5SSteve French * 58138c8a9a5SSteve French * See MS-DFSC 3.2.5.5 "Receiving a Root Referral Request or Link Referral Request". 58238c8a9a5SSteve French */ 58338c8a9a5SSteve French e = path + strlen(path) - 1; 58438c8a9a5SSteve French while (e > s) { 58538c8a9a5SSteve French int len; 58638c8a9a5SSteve French 58738c8a9a5SSteve French /* skip separators */ 58838c8a9a5SSteve French while (e > s && *e == sep) 58938c8a9a5SSteve French e--; 59038c8a9a5SSteve French if (e == s) 59138c8a9a5SSteve French break; 59238c8a9a5SSteve French 59338c8a9a5SSteve French len = e + 1 - path; 59438c8a9a5SSteve French rc = cache_entry_hash(path, len, &hash); 59538c8a9a5SSteve French if (rc) 59638c8a9a5SSteve French return ERR_PTR(rc); 59738c8a9a5SSteve French ce = __lookup_cache_entry(path, hash, len); 59838c8a9a5SSteve French if (!IS_ERR(ce)) 59938c8a9a5SSteve French return ce; 60038c8a9a5SSteve French 60138c8a9a5SSteve French /* backward until separator */ 60238c8a9a5SSteve French while (e > s && *e != sep) 60338c8a9a5SSteve French e--; 60438c8a9a5SSteve French } 60538c8a9a5SSteve French return ERR_PTR(-ENOENT); 60638c8a9a5SSteve French } 60738c8a9a5SSteve French 60838c8a9a5SSteve French /** 60938c8a9a5SSteve French * dfs_cache_destroy - destroy DFS referral cache 61038c8a9a5SSteve French */ 61138c8a9a5SSteve French void dfs_cache_destroy(void) 61238c8a9a5SSteve French { 61338c8a9a5SSteve French unload_nls(cache_cp); 61438c8a9a5SSteve French flush_cache_ents(); 61538c8a9a5SSteve French kmem_cache_destroy(cache_slab); 61638c8a9a5SSteve French destroy_workqueue(dfscache_wq); 61738c8a9a5SSteve French 61838c8a9a5SSteve French cifs_dbg(FYI, "%s: destroyed DFS referral cache\n", __func__); 61938c8a9a5SSteve French } 62038c8a9a5SSteve French 62138c8a9a5SSteve French /* Update a cache entry with the new referral in @refs */ 62238c8a9a5SSteve French static int update_cache_entry_locked(struct cache_entry *ce, const struct dfs_info3_param *refs, 62338c8a9a5SSteve French int numrefs) 62438c8a9a5SSteve French { 62538c8a9a5SSteve French struct cache_dfs_tgt *target; 62638c8a9a5SSteve French char *th = NULL; 62738c8a9a5SSteve French int rc; 62838c8a9a5SSteve French 62938c8a9a5SSteve French WARN_ON(!rwsem_is_locked(&htable_rw_lock)); 63038c8a9a5SSteve French 63138c8a9a5SSteve French target = READ_ONCE(ce->tgthint); 63238c8a9a5SSteve French if (target) { 63338c8a9a5SSteve French th = kstrdup(target->name, GFP_ATOMIC); 63438c8a9a5SSteve French if (!th) 63538c8a9a5SSteve French return -ENOMEM; 63638c8a9a5SSteve French } 63738c8a9a5SSteve French 63838c8a9a5SSteve French free_tgts(ce); 63938c8a9a5SSteve French ce->numtgts = 0; 64038c8a9a5SSteve French 64138c8a9a5SSteve French rc = copy_ref_data(refs, numrefs, ce, th); 64238c8a9a5SSteve French 64338c8a9a5SSteve French kfree(th); 64438c8a9a5SSteve French 64538c8a9a5SSteve French return rc; 64638c8a9a5SSteve French } 64738c8a9a5SSteve French 64838c8a9a5SSteve French static int get_dfs_referral(const unsigned int xid, struct cifs_ses *ses, const char *path, 64938c8a9a5SSteve French struct dfs_info3_param **refs, int *numrefs) 65038c8a9a5SSteve French { 65138c8a9a5SSteve French int rc; 65238c8a9a5SSteve French int i; 65338c8a9a5SSteve French 65438c8a9a5SSteve French *refs = NULL; 65538c8a9a5SSteve French *numrefs = 0; 65638c8a9a5SSteve French 65738c8a9a5SSteve French if (!ses || !ses->server || !ses->server->ops->get_dfs_refer) 65838c8a9a5SSteve French return -EOPNOTSUPP; 65938c8a9a5SSteve French if (unlikely(!cache_cp)) 66038c8a9a5SSteve French return -EINVAL; 66138c8a9a5SSteve French 66238c8a9a5SSteve French cifs_dbg(FYI, "%s: ipc=%s referral=%s\n", __func__, ses->tcon_ipc->tree_name, path); 66338c8a9a5SSteve French rc = ses->server->ops->get_dfs_refer(xid, ses, path, refs, numrefs, cache_cp, 66438c8a9a5SSteve French NO_MAP_UNI_RSVD); 66538c8a9a5SSteve French if (!rc) { 66638c8a9a5SSteve French struct dfs_info3_param *ref = *refs; 66738c8a9a5SSteve French 66838c8a9a5SSteve French for (i = 0; i < *numrefs; i++) 66938c8a9a5SSteve French convert_delimiter(ref[i].path_name, '\\'); 67038c8a9a5SSteve French } 67138c8a9a5SSteve French return rc; 67238c8a9a5SSteve French } 67338c8a9a5SSteve French 67438c8a9a5SSteve French /* 67538c8a9a5SSteve French * Find, create or update a DFS cache entry. 67638c8a9a5SSteve French * 67738c8a9a5SSteve French * If the entry wasn't found, it will create a new one. Or if it was found but 67838c8a9a5SSteve French * expired, then it will update the entry accordingly. 67938c8a9a5SSteve French * 68038c8a9a5SSteve French * For interlinks, cifs_mount() and expand_dfs_referral() are supposed to 68138c8a9a5SSteve French * handle them properly. 68238c8a9a5SSteve French * 68338c8a9a5SSteve French * On success, return entry with acquired lock for reading, otherwise error ptr. 68438c8a9a5SSteve French */ 68538c8a9a5SSteve French static struct cache_entry *cache_refresh_path(const unsigned int xid, 68638c8a9a5SSteve French struct cifs_ses *ses, 68738c8a9a5SSteve French const char *path, 68838c8a9a5SSteve French bool force_refresh) 68938c8a9a5SSteve French { 69038c8a9a5SSteve French struct dfs_info3_param *refs = NULL; 69138c8a9a5SSteve French struct cache_entry *ce; 69238c8a9a5SSteve French int numrefs = 0; 69338c8a9a5SSteve French int rc; 69438c8a9a5SSteve French 69538c8a9a5SSteve French cifs_dbg(FYI, "%s: search path: %s\n", __func__, path); 69638c8a9a5SSteve French 69738c8a9a5SSteve French down_read(&htable_rw_lock); 69838c8a9a5SSteve French 69938c8a9a5SSteve French ce = lookup_cache_entry(path); 70038c8a9a5SSteve French if (!IS_ERR(ce)) { 70138c8a9a5SSteve French if (!force_refresh && !cache_entry_expired(ce)) 70238c8a9a5SSteve French return ce; 70338c8a9a5SSteve French } else if (PTR_ERR(ce) != -ENOENT) { 70438c8a9a5SSteve French up_read(&htable_rw_lock); 70538c8a9a5SSteve French return ce; 70638c8a9a5SSteve French } 70738c8a9a5SSteve French 70838c8a9a5SSteve French /* 70938c8a9a5SSteve French * Unlock shared access as we don't want to hold any locks while getting 71038c8a9a5SSteve French * a new referral. The @ses used for performing the I/O could be 71138c8a9a5SSteve French * reconnecting and it acquires @htable_rw_lock to look up the dfs cache 71238c8a9a5SSteve French * in order to failover -- if necessary. 71338c8a9a5SSteve French */ 71438c8a9a5SSteve French up_read(&htable_rw_lock); 71538c8a9a5SSteve French 71638c8a9a5SSteve French /* 71738c8a9a5SSteve French * Either the entry was not found, or it is expired, or it is a forced 71838c8a9a5SSteve French * refresh. 71938c8a9a5SSteve French * Request a new DFS referral in order to create or update a cache entry. 72038c8a9a5SSteve French */ 72138c8a9a5SSteve French rc = get_dfs_referral(xid, ses, path, &refs, &numrefs); 72238c8a9a5SSteve French if (rc) { 72338c8a9a5SSteve French ce = ERR_PTR(rc); 72438c8a9a5SSteve French goto out; 72538c8a9a5SSteve French } 72638c8a9a5SSteve French 72738c8a9a5SSteve French dump_refs(refs, numrefs); 72838c8a9a5SSteve French 72938c8a9a5SSteve French down_write(&htable_rw_lock); 73038c8a9a5SSteve French /* Re-check as another task might have it added or refreshed already */ 73138c8a9a5SSteve French ce = lookup_cache_entry(path); 73238c8a9a5SSteve French if (!IS_ERR(ce)) { 73338c8a9a5SSteve French if (force_refresh || cache_entry_expired(ce)) { 73438c8a9a5SSteve French rc = update_cache_entry_locked(ce, refs, numrefs); 73538c8a9a5SSteve French if (rc) 73638c8a9a5SSteve French ce = ERR_PTR(rc); 73738c8a9a5SSteve French } 73838c8a9a5SSteve French } else if (PTR_ERR(ce) == -ENOENT) { 73938c8a9a5SSteve French ce = add_cache_entry_locked(refs, numrefs); 74038c8a9a5SSteve French } 74138c8a9a5SSteve French 74238c8a9a5SSteve French if (IS_ERR(ce)) { 74338c8a9a5SSteve French up_write(&htable_rw_lock); 74438c8a9a5SSteve French goto out; 74538c8a9a5SSteve French } 74638c8a9a5SSteve French 74738c8a9a5SSteve French downgrade_write(&htable_rw_lock); 74838c8a9a5SSteve French out: 74938c8a9a5SSteve French free_dfs_info_array(refs, numrefs); 75038c8a9a5SSteve French return ce; 75138c8a9a5SSteve French } 75238c8a9a5SSteve French 75338c8a9a5SSteve French /* 75438c8a9a5SSteve French * Set up a DFS referral from a given cache entry. 75538c8a9a5SSteve French * 75638c8a9a5SSteve French * Must be called with htable_rw_lock held. 75738c8a9a5SSteve French */ 75838c8a9a5SSteve French static int setup_referral(const char *path, struct cache_entry *ce, 75938c8a9a5SSteve French struct dfs_info3_param *ref, const char *target) 76038c8a9a5SSteve French { 76138c8a9a5SSteve French int rc; 76238c8a9a5SSteve French 76338c8a9a5SSteve French cifs_dbg(FYI, "%s: set up new ref\n", __func__); 76438c8a9a5SSteve French 76538c8a9a5SSteve French memset(ref, 0, sizeof(*ref)); 76638c8a9a5SSteve French 76738c8a9a5SSteve French ref->path_name = kstrdup(path, GFP_ATOMIC); 76838c8a9a5SSteve French if (!ref->path_name) 76938c8a9a5SSteve French return -ENOMEM; 77038c8a9a5SSteve French 77138c8a9a5SSteve French ref->node_name = kstrdup(target, GFP_ATOMIC); 77238c8a9a5SSteve French if (!ref->node_name) { 77338c8a9a5SSteve French rc = -ENOMEM; 77438c8a9a5SSteve French goto err_free_path; 77538c8a9a5SSteve French } 77638c8a9a5SSteve French 77738c8a9a5SSteve French ref->path_consumed = ce->path_consumed; 77838c8a9a5SSteve French ref->ttl = ce->ttl; 77938c8a9a5SSteve French ref->server_type = ce->srvtype; 78038c8a9a5SSteve French ref->ref_flag = ce->ref_flags; 78138c8a9a5SSteve French ref->flags = ce->hdr_flags; 78238c8a9a5SSteve French 78338c8a9a5SSteve French return 0; 78438c8a9a5SSteve French 78538c8a9a5SSteve French err_free_path: 78638c8a9a5SSteve French kfree(ref->path_name); 78738c8a9a5SSteve French ref->path_name = NULL; 78838c8a9a5SSteve French return rc; 78938c8a9a5SSteve French } 79038c8a9a5SSteve French 79138c8a9a5SSteve French /* Return target list of a DFS cache entry */ 79238c8a9a5SSteve French static int get_targets(struct cache_entry *ce, struct dfs_cache_tgt_list *tl) 79338c8a9a5SSteve French { 79438c8a9a5SSteve French int rc; 79538c8a9a5SSteve French struct list_head *head = &tl->tl_list; 79638c8a9a5SSteve French struct cache_dfs_tgt *t; 79738c8a9a5SSteve French struct dfs_cache_tgt_iterator *it, *nit; 79838c8a9a5SSteve French 79938c8a9a5SSteve French memset(tl, 0, sizeof(*tl)); 80038c8a9a5SSteve French INIT_LIST_HEAD(head); 80138c8a9a5SSteve French 80238c8a9a5SSteve French list_for_each_entry(t, &ce->tlist, list) { 80338c8a9a5SSteve French it = kzalloc(sizeof(*it), GFP_ATOMIC); 80438c8a9a5SSteve French if (!it) { 80538c8a9a5SSteve French rc = -ENOMEM; 80638c8a9a5SSteve French goto err_free_it; 80738c8a9a5SSteve French } 80838c8a9a5SSteve French 80938c8a9a5SSteve French it->it_name = kstrdup(t->name, GFP_ATOMIC); 81038c8a9a5SSteve French if (!it->it_name) { 81138c8a9a5SSteve French kfree(it); 81238c8a9a5SSteve French rc = -ENOMEM; 81338c8a9a5SSteve French goto err_free_it; 81438c8a9a5SSteve French } 81538c8a9a5SSteve French it->it_path_consumed = t->path_consumed; 81638c8a9a5SSteve French 81738c8a9a5SSteve French if (READ_ONCE(ce->tgthint) == t) 81838c8a9a5SSteve French list_add(&it->it_list, head); 81938c8a9a5SSteve French else 82038c8a9a5SSteve French list_add_tail(&it->it_list, head); 82138c8a9a5SSteve French } 82238c8a9a5SSteve French 82338c8a9a5SSteve French tl->tl_numtgts = ce->numtgts; 82438c8a9a5SSteve French 82538c8a9a5SSteve French return 0; 82638c8a9a5SSteve French 82738c8a9a5SSteve French err_free_it: 82838c8a9a5SSteve French list_for_each_entry_safe(it, nit, head, it_list) { 82938c8a9a5SSteve French list_del(&it->it_list); 83038c8a9a5SSteve French kfree(it->it_name); 83138c8a9a5SSteve French kfree(it); 83238c8a9a5SSteve French } 83338c8a9a5SSteve French return rc; 83438c8a9a5SSteve French } 83538c8a9a5SSteve French 83638c8a9a5SSteve French /** 83738c8a9a5SSteve French * dfs_cache_find - find a DFS cache entry 83838c8a9a5SSteve French * 83938c8a9a5SSteve French * If it doesn't find the cache entry, then it will get a DFS referral 84038c8a9a5SSteve French * for @path and create a new entry. 84138c8a9a5SSteve French * 84238c8a9a5SSteve French * In case the cache entry exists but expired, it will get a DFS referral 84338c8a9a5SSteve French * for @path and then update the respective cache entry. 84438c8a9a5SSteve French * 84538c8a9a5SSteve French * These parameters are passed down to the get_dfs_refer() call if it 84638c8a9a5SSteve French * needs to be issued: 84738c8a9a5SSteve French * @xid: syscall xid 84838c8a9a5SSteve French * @ses: smb session to issue the request on 84938c8a9a5SSteve French * @cp: codepage 85038c8a9a5SSteve French * @remap: path character remapping type 85138c8a9a5SSteve French * @path: path to lookup in DFS referral cache. 85238c8a9a5SSteve French * 85338c8a9a5SSteve French * @ref: when non-NULL, store single DFS referral result in it. 85438c8a9a5SSteve French * @tgt_list: when non-NULL, store complete DFS target list in it. 85538c8a9a5SSteve French * 85638c8a9a5SSteve French * Return zero if the target was found, otherwise non-zero. 85738c8a9a5SSteve French */ 85838c8a9a5SSteve French int dfs_cache_find(const unsigned int xid, struct cifs_ses *ses, const struct nls_table *cp, 85938c8a9a5SSteve French int remap, const char *path, struct dfs_info3_param *ref, 86038c8a9a5SSteve French struct dfs_cache_tgt_list *tgt_list) 86138c8a9a5SSteve French { 86238c8a9a5SSteve French int rc; 86338c8a9a5SSteve French const char *npath; 86438c8a9a5SSteve French struct cache_entry *ce; 86538c8a9a5SSteve French 86638c8a9a5SSteve French npath = dfs_cache_canonical_path(path, cp, remap); 86738c8a9a5SSteve French if (IS_ERR(npath)) 86838c8a9a5SSteve French return PTR_ERR(npath); 86938c8a9a5SSteve French 87038c8a9a5SSteve French ce = cache_refresh_path(xid, ses, npath, false); 87138c8a9a5SSteve French if (IS_ERR(ce)) { 87238c8a9a5SSteve French rc = PTR_ERR(ce); 87338c8a9a5SSteve French goto out_free_path; 87438c8a9a5SSteve French } 87538c8a9a5SSteve French 87638c8a9a5SSteve French if (ref) 87738c8a9a5SSteve French rc = setup_referral(path, ce, ref, get_tgt_name(ce)); 87838c8a9a5SSteve French else 87938c8a9a5SSteve French rc = 0; 88038c8a9a5SSteve French if (!rc && tgt_list) 88138c8a9a5SSteve French rc = get_targets(ce, tgt_list); 88238c8a9a5SSteve French 88338c8a9a5SSteve French up_read(&htable_rw_lock); 88438c8a9a5SSteve French 88538c8a9a5SSteve French out_free_path: 88638c8a9a5SSteve French kfree(npath); 88738c8a9a5SSteve French return rc; 88838c8a9a5SSteve French } 88938c8a9a5SSteve French 89038c8a9a5SSteve French /** 89138c8a9a5SSteve French * dfs_cache_noreq_find - find a DFS cache entry without sending any requests to 89238c8a9a5SSteve French * the currently connected server. 89338c8a9a5SSteve French * 89438c8a9a5SSteve French * NOTE: This function will neither update a cache entry in case it was 89538c8a9a5SSteve French * expired, nor create a new cache entry if @path hasn't been found. It heavily 89638c8a9a5SSteve French * relies on an existing cache entry. 89738c8a9a5SSteve French * 89838c8a9a5SSteve French * @path: canonical DFS path to lookup in the DFS referral cache. 89938c8a9a5SSteve French * @ref: when non-NULL, store single DFS referral result in it. 90038c8a9a5SSteve French * @tgt_list: when non-NULL, store complete DFS target list in it. 90138c8a9a5SSteve French * 90238c8a9a5SSteve French * Return 0 if successful. 90338c8a9a5SSteve French * Return -ENOENT if the entry was not found. 90438c8a9a5SSteve French * Return non-zero for other errors. 90538c8a9a5SSteve French */ 90638c8a9a5SSteve French int dfs_cache_noreq_find(const char *path, struct dfs_info3_param *ref, 90738c8a9a5SSteve French struct dfs_cache_tgt_list *tgt_list) 90838c8a9a5SSteve French { 90938c8a9a5SSteve French int rc; 91038c8a9a5SSteve French struct cache_entry *ce; 91138c8a9a5SSteve French 91238c8a9a5SSteve French cifs_dbg(FYI, "%s: path: %s\n", __func__, path); 91338c8a9a5SSteve French 91438c8a9a5SSteve French down_read(&htable_rw_lock); 91538c8a9a5SSteve French 91638c8a9a5SSteve French ce = lookup_cache_entry(path); 91738c8a9a5SSteve French if (IS_ERR(ce)) { 91838c8a9a5SSteve French rc = PTR_ERR(ce); 91938c8a9a5SSteve French goto out_unlock; 92038c8a9a5SSteve French } 92138c8a9a5SSteve French 92238c8a9a5SSteve French if (ref) 92338c8a9a5SSteve French rc = setup_referral(path, ce, ref, get_tgt_name(ce)); 92438c8a9a5SSteve French else 92538c8a9a5SSteve French rc = 0; 92638c8a9a5SSteve French if (!rc && tgt_list) 92738c8a9a5SSteve French rc = get_targets(ce, tgt_list); 92838c8a9a5SSteve French 92938c8a9a5SSteve French out_unlock: 93038c8a9a5SSteve French up_read(&htable_rw_lock); 93138c8a9a5SSteve French return rc; 93238c8a9a5SSteve French } 93338c8a9a5SSteve French 93438c8a9a5SSteve French /** 93538c8a9a5SSteve French * dfs_cache_noreq_update_tgthint - update target hint of a DFS cache entry 93638c8a9a5SSteve French * without sending any requests to the currently connected server. 93738c8a9a5SSteve French * 93838c8a9a5SSteve French * NOTE: This function will neither update a cache entry in case it was 93938c8a9a5SSteve French * expired, nor create a new cache entry if @path hasn't been found. It heavily 94038c8a9a5SSteve French * relies on an existing cache entry. 94138c8a9a5SSteve French * 94238c8a9a5SSteve French * @path: canonical DFS path to lookup in DFS referral cache. 94338c8a9a5SSteve French * @it: target iterator which contains the target hint to update the cache 94438c8a9a5SSteve French * entry with. 94538c8a9a5SSteve French * 94638c8a9a5SSteve French * Return zero if the target hint was updated successfully, otherwise non-zero. 94738c8a9a5SSteve French */ 94838c8a9a5SSteve French void dfs_cache_noreq_update_tgthint(const char *path, const struct dfs_cache_tgt_iterator *it) 94938c8a9a5SSteve French { 95038c8a9a5SSteve French struct cache_dfs_tgt *t; 95138c8a9a5SSteve French struct cache_entry *ce; 95238c8a9a5SSteve French 95338c8a9a5SSteve French if (!path || !it) 95438c8a9a5SSteve French return; 95538c8a9a5SSteve French 95638c8a9a5SSteve French cifs_dbg(FYI, "%s: path: %s\n", __func__, path); 95738c8a9a5SSteve French 95838c8a9a5SSteve French down_read(&htable_rw_lock); 95938c8a9a5SSteve French 96038c8a9a5SSteve French ce = lookup_cache_entry(path); 96138c8a9a5SSteve French if (IS_ERR(ce)) 96238c8a9a5SSteve French goto out_unlock; 96338c8a9a5SSteve French 96438c8a9a5SSteve French t = READ_ONCE(ce->tgthint); 96538c8a9a5SSteve French 96638c8a9a5SSteve French if (unlikely(!strcasecmp(it->it_name, t->name))) 96738c8a9a5SSteve French goto out_unlock; 96838c8a9a5SSteve French 96938c8a9a5SSteve French list_for_each_entry(t, &ce->tlist, list) { 97038c8a9a5SSteve French if (!strcasecmp(t->name, it->it_name)) { 97138c8a9a5SSteve French WRITE_ONCE(ce->tgthint, t); 97238c8a9a5SSteve French cifs_dbg(FYI, "%s: new target hint: %s\n", __func__, 97338c8a9a5SSteve French it->it_name); 97438c8a9a5SSteve French break; 97538c8a9a5SSteve French } 97638c8a9a5SSteve French } 97738c8a9a5SSteve French 97838c8a9a5SSteve French out_unlock: 97938c8a9a5SSteve French up_read(&htable_rw_lock); 98038c8a9a5SSteve French } 98138c8a9a5SSteve French 98238c8a9a5SSteve French /** 98338c8a9a5SSteve French * dfs_cache_get_tgt_referral - returns a DFS referral (@ref) from a given 98438c8a9a5SSteve French * target iterator (@it). 98538c8a9a5SSteve French * 98638c8a9a5SSteve French * @path: canonical DFS path to lookup in DFS referral cache. 98738c8a9a5SSteve French * @it: DFS target iterator. 98838c8a9a5SSteve French * @ref: DFS referral pointer to set up the gathered information. 98938c8a9a5SSteve French * 99038c8a9a5SSteve French * Return zero if the DFS referral was set up correctly, otherwise non-zero. 99138c8a9a5SSteve French */ 99238c8a9a5SSteve French int dfs_cache_get_tgt_referral(const char *path, const struct dfs_cache_tgt_iterator *it, 99338c8a9a5SSteve French struct dfs_info3_param *ref) 99438c8a9a5SSteve French { 99538c8a9a5SSteve French int rc; 99638c8a9a5SSteve French struct cache_entry *ce; 99738c8a9a5SSteve French 99838c8a9a5SSteve French if (!it || !ref) 99938c8a9a5SSteve French return -EINVAL; 100038c8a9a5SSteve French 100138c8a9a5SSteve French cifs_dbg(FYI, "%s: path: %s\n", __func__, path); 100238c8a9a5SSteve French 100338c8a9a5SSteve French down_read(&htable_rw_lock); 100438c8a9a5SSteve French 100538c8a9a5SSteve French ce = lookup_cache_entry(path); 100638c8a9a5SSteve French if (IS_ERR(ce)) { 100738c8a9a5SSteve French rc = PTR_ERR(ce); 100838c8a9a5SSteve French goto out_unlock; 100938c8a9a5SSteve French } 101038c8a9a5SSteve French 101138c8a9a5SSteve French cifs_dbg(FYI, "%s: target name: %s\n", __func__, it->it_name); 101238c8a9a5SSteve French 101338c8a9a5SSteve French rc = setup_referral(path, ce, ref, it->it_name); 101438c8a9a5SSteve French 101538c8a9a5SSteve French out_unlock: 101638c8a9a5SSteve French up_read(&htable_rw_lock); 101738c8a9a5SSteve French return rc; 101838c8a9a5SSteve French } 101938c8a9a5SSteve French 102038c8a9a5SSteve French /* Extract share from DFS target and return a pointer to prefix path or NULL */ 102138c8a9a5SSteve French static const char *parse_target_share(const char *target, char **share) 102238c8a9a5SSteve French { 102338c8a9a5SSteve French const char *s, *seps = "/\\"; 102438c8a9a5SSteve French size_t len; 102538c8a9a5SSteve French 102638c8a9a5SSteve French s = strpbrk(target + 1, seps); 102738c8a9a5SSteve French if (!s) 102838c8a9a5SSteve French return ERR_PTR(-EINVAL); 102938c8a9a5SSteve French 103038c8a9a5SSteve French len = strcspn(s + 1, seps); 103138c8a9a5SSteve French if (!len) 103238c8a9a5SSteve French return ERR_PTR(-EINVAL); 103338c8a9a5SSteve French s += len; 103438c8a9a5SSteve French 103538c8a9a5SSteve French len = s - target + 1; 103638c8a9a5SSteve French *share = kstrndup(target, len, GFP_KERNEL); 103738c8a9a5SSteve French if (!*share) 103838c8a9a5SSteve French return ERR_PTR(-ENOMEM); 103938c8a9a5SSteve French 104038c8a9a5SSteve French s = target + len; 104138c8a9a5SSteve French return s + strspn(s, seps); 104238c8a9a5SSteve French } 104338c8a9a5SSteve French 104438c8a9a5SSteve French /** 104538c8a9a5SSteve French * dfs_cache_get_tgt_share - parse a DFS target 104638c8a9a5SSteve French * 104738c8a9a5SSteve French * @path: DFS full path 104838c8a9a5SSteve French * @it: DFS target iterator. 104938c8a9a5SSteve French * @share: tree name. 105038c8a9a5SSteve French * @prefix: prefix path. 105138c8a9a5SSteve French * 105238c8a9a5SSteve French * Return zero if target was parsed correctly, otherwise non-zero. 105338c8a9a5SSteve French */ 105438c8a9a5SSteve French int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it, char **share, 105538c8a9a5SSteve French char **prefix) 105638c8a9a5SSteve French { 105738c8a9a5SSteve French char sep; 105838c8a9a5SSteve French char *target_share; 105938c8a9a5SSteve French char *ppath = NULL; 106038c8a9a5SSteve French const char *target_ppath, *dfsref_ppath; 106138c8a9a5SSteve French size_t target_pplen, dfsref_pplen; 106238c8a9a5SSteve French size_t len, c; 106338c8a9a5SSteve French 106438c8a9a5SSteve French if (!it || !path || !share || !prefix || strlen(path) < it->it_path_consumed) 106538c8a9a5SSteve French return -EINVAL; 106638c8a9a5SSteve French 106738c8a9a5SSteve French sep = it->it_name[0]; 106838c8a9a5SSteve French if (sep != '\\' && sep != '/') 106938c8a9a5SSteve French return -EINVAL; 107038c8a9a5SSteve French 107138c8a9a5SSteve French target_ppath = parse_target_share(it->it_name, &target_share); 107238c8a9a5SSteve French if (IS_ERR(target_ppath)) 107338c8a9a5SSteve French return PTR_ERR(target_ppath); 107438c8a9a5SSteve French 107538c8a9a5SSteve French /* point to prefix in DFS referral path */ 107638c8a9a5SSteve French dfsref_ppath = path + it->it_path_consumed; 107738c8a9a5SSteve French dfsref_ppath += strspn(dfsref_ppath, "/\\"); 107838c8a9a5SSteve French 107938c8a9a5SSteve French target_pplen = strlen(target_ppath); 108038c8a9a5SSteve French dfsref_pplen = strlen(dfsref_ppath); 108138c8a9a5SSteve French 108238c8a9a5SSteve French /* merge prefix paths from DFS referral path and target node */ 108338c8a9a5SSteve French if (target_pplen || dfsref_pplen) { 108438c8a9a5SSteve French len = target_pplen + dfsref_pplen + 2; 108538c8a9a5SSteve French ppath = kzalloc(len, GFP_KERNEL); 108638c8a9a5SSteve French if (!ppath) { 108738c8a9a5SSteve French kfree(target_share); 108838c8a9a5SSteve French return -ENOMEM; 108938c8a9a5SSteve French } 109038c8a9a5SSteve French c = strscpy(ppath, target_ppath, len); 109138c8a9a5SSteve French if (c && dfsref_pplen) 109238c8a9a5SSteve French ppath[c] = sep; 109338c8a9a5SSteve French strlcat(ppath, dfsref_ppath, len); 109438c8a9a5SSteve French } 109538c8a9a5SSteve French *share = target_share; 109638c8a9a5SSteve French *prefix = ppath; 109738c8a9a5SSteve French return 0; 109838c8a9a5SSteve French } 109938c8a9a5SSteve French 110038c8a9a5SSteve French static bool target_share_equal(struct TCP_Server_Info *server, const char *s1, const char *s2) 110138c8a9a5SSteve French { 110238c8a9a5SSteve French char unc[sizeof("\\\\") + SERVER_NAME_LENGTH] = {0}; 110338c8a9a5SSteve French const char *host; 110438c8a9a5SSteve French size_t hostlen; 110538c8a9a5SSteve French struct sockaddr_storage ss; 110638c8a9a5SSteve French bool match; 110738c8a9a5SSteve French int rc; 110838c8a9a5SSteve French 110938c8a9a5SSteve French if (strcasecmp(s1, s2)) 111038c8a9a5SSteve French return false; 111138c8a9a5SSteve French 111238c8a9a5SSteve French /* 111338c8a9a5SSteve French * Resolve share's hostname and check if server address matches. Otherwise just ignore it 111438c8a9a5SSteve French * as we could not have upcall to resolve hostname or failed to convert ip address. 111538c8a9a5SSteve French */ 111638c8a9a5SSteve French extract_unc_hostname(s1, &host, &hostlen); 111738c8a9a5SSteve French scnprintf(unc, sizeof(unc), "\\\\%.*s", (int)hostlen, host); 111838c8a9a5SSteve French 111938c8a9a5SSteve French rc = dns_resolve_server_name_to_ip(unc, (struct sockaddr *)&ss, NULL); 112038c8a9a5SSteve French if (rc < 0) { 112138c8a9a5SSteve French cifs_dbg(FYI, "%s: could not resolve %.*s. assuming server address matches.\n", 112238c8a9a5SSteve French __func__, (int)hostlen, host); 112338c8a9a5SSteve French return true; 112438c8a9a5SSteve French } 112538c8a9a5SSteve French 112638c8a9a5SSteve French cifs_server_lock(server); 112738c8a9a5SSteve French match = cifs_match_ipaddr((struct sockaddr *)&server->dstaddr, (struct sockaddr *)&ss); 112838c8a9a5SSteve French cifs_server_unlock(server); 112938c8a9a5SSteve French 113038c8a9a5SSteve French return match; 113138c8a9a5SSteve French } 113238c8a9a5SSteve French 113338c8a9a5SSteve French /* 113438c8a9a5SSteve French * Mark dfs tcon for reconnecting when the currently connected tcon does not match any of the new 113538c8a9a5SSteve French * target shares in @refs. 113638c8a9a5SSteve French */ 113738c8a9a5SSteve French static void mark_for_reconnect_if_needed(struct TCP_Server_Info *server, 113838c8a9a5SSteve French const char *path, 113938c8a9a5SSteve French struct dfs_cache_tgt_list *old_tl, 114038c8a9a5SSteve French struct dfs_cache_tgt_list *new_tl) 114138c8a9a5SSteve French { 114238c8a9a5SSteve French struct dfs_cache_tgt_iterator *oit, *nit; 114338c8a9a5SSteve French 114438c8a9a5SSteve French for (oit = dfs_cache_get_tgt_iterator(old_tl); oit; 114538c8a9a5SSteve French oit = dfs_cache_get_next_tgt(old_tl, oit)) { 114638c8a9a5SSteve French for (nit = dfs_cache_get_tgt_iterator(new_tl); nit; 114738c8a9a5SSteve French nit = dfs_cache_get_next_tgt(new_tl, nit)) { 114838c8a9a5SSteve French if (target_share_equal(server, 114938c8a9a5SSteve French dfs_cache_get_tgt_name(oit), 115038c8a9a5SSteve French dfs_cache_get_tgt_name(nit))) { 115138c8a9a5SSteve French dfs_cache_noreq_update_tgthint(path, nit); 115238c8a9a5SSteve French return; 115338c8a9a5SSteve French } 115438c8a9a5SSteve French } 115538c8a9a5SSteve French } 115638c8a9a5SSteve French 115738c8a9a5SSteve French cifs_dbg(FYI, "%s: no cached or matched targets. mark dfs share for reconnect.\n", __func__); 115838c8a9a5SSteve French cifs_signal_cifsd_for_reconnect(server, true); 115938c8a9a5SSteve French } 116038c8a9a5SSteve French 116138c8a9a5SSteve French static bool is_ses_good(struct cifs_ses *ses) 116238c8a9a5SSteve French { 116338c8a9a5SSteve French struct TCP_Server_Info *server = ses->server; 116438c8a9a5SSteve French struct cifs_tcon *tcon = ses->tcon_ipc; 116538c8a9a5SSteve French bool ret; 116638c8a9a5SSteve French 116738c8a9a5SSteve French spin_lock(&ses->ses_lock); 116838c8a9a5SSteve French spin_lock(&ses->chan_lock); 116938c8a9a5SSteve French ret = !cifs_chan_needs_reconnect(ses, server) && 117038c8a9a5SSteve French ses->ses_status == SES_GOOD && 117138c8a9a5SSteve French !tcon->need_reconnect; 117238c8a9a5SSteve French spin_unlock(&ses->chan_lock); 117338c8a9a5SSteve French spin_unlock(&ses->ses_lock); 117438c8a9a5SSteve French return ret; 117538c8a9a5SSteve French } 117638c8a9a5SSteve French 117738c8a9a5SSteve French /* Refresh dfs referral of tcon and mark it for reconnect if needed */ 117838c8a9a5SSteve French static int __refresh_tcon(const char *path, struct cifs_ses *ses, bool force_refresh) 117938c8a9a5SSteve French { 118038c8a9a5SSteve French struct dfs_cache_tgt_list old_tl = DFS_CACHE_TGT_LIST_INIT(old_tl); 118138c8a9a5SSteve French struct dfs_cache_tgt_list new_tl = DFS_CACHE_TGT_LIST_INIT(new_tl); 118238c8a9a5SSteve French struct TCP_Server_Info *server = ses->server; 118338c8a9a5SSteve French bool needs_refresh = false; 118438c8a9a5SSteve French struct cache_entry *ce; 118538c8a9a5SSteve French unsigned int xid; 118638c8a9a5SSteve French int rc = 0; 118738c8a9a5SSteve French 118838c8a9a5SSteve French xid = get_xid(); 118938c8a9a5SSteve French 119038c8a9a5SSteve French down_read(&htable_rw_lock); 119138c8a9a5SSteve French ce = lookup_cache_entry(path); 119238c8a9a5SSteve French needs_refresh = force_refresh || IS_ERR(ce) || cache_entry_expired(ce); 119338c8a9a5SSteve French if (!IS_ERR(ce)) { 119438c8a9a5SSteve French rc = get_targets(ce, &old_tl); 119538c8a9a5SSteve French cifs_dbg(FYI, "%s: get_targets: %d\n", __func__, rc); 119638c8a9a5SSteve French } 119738c8a9a5SSteve French up_read(&htable_rw_lock); 119838c8a9a5SSteve French 119938c8a9a5SSteve French if (!needs_refresh) { 120038c8a9a5SSteve French rc = 0; 120138c8a9a5SSteve French goto out; 120238c8a9a5SSteve French } 120338c8a9a5SSteve French 120438c8a9a5SSteve French ses = CIFS_DFS_ROOT_SES(ses); 120538c8a9a5SSteve French if (!is_ses_good(ses)) { 120638c8a9a5SSteve French cifs_dbg(FYI, "%s: skip cache refresh due to disconnected ipc\n", 120738c8a9a5SSteve French __func__); 120838c8a9a5SSteve French goto out; 120938c8a9a5SSteve French } 121038c8a9a5SSteve French 121138c8a9a5SSteve French ce = cache_refresh_path(xid, ses, path, true); 121238c8a9a5SSteve French if (!IS_ERR(ce)) { 121338c8a9a5SSteve French rc = get_targets(ce, &new_tl); 121438c8a9a5SSteve French up_read(&htable_rw_lock); 121538c8a9a5SSteve French cifs_dbg(FYI, "%s: get_targets: %d\n", __func__, rc); 121638c8a9a5SSteve French mark_for_reconnect_if_needed(server, path, &old_tl, &new_tl); 121738c8a9a5SSteve French } 121838c8a9a5SSteve French 121938c8a9a5SSteve French out: 122038c8a9a5SSteve French free_xid(xid); 122138c8a9a5SSteve French dfs_cache_free_tgts(&old_tl); 122238c8a9a5SSteve French dfs_cache_free_tgts(&new_tl); 122338c8a9a5SSteve French return rc; 122438c8a9a5SSteve French } 122538c8a9a5SSteve French 122638c8a9a5SSteve French static int refresh_tcon(struct cifs_tcon *tcon, bool force_refresh) 122738c8a9a5SSteve French { 122838c8a9a5SSteve French struct TCP_Server_Info *server = tcon->ses->server; 122938c8a9a5SSteve French struct cifs_ses *ses = tcon->ses; 123038c8a9a5SSteve French 123138c8a9a5SSteve French mutex_lock(&server->refpath_lock); 123238c8a9a5SSteve French if (server->leaf_fullpath) 123338c8a9a5SSteve French __refresh_tcon(server->leaf_fullpath + 1, ses, force_refresh); 123438c8a9a5SSteve French mutex_unlock(&server->refpath_lock); 123538c8a9a5SSteve French return 0; 123638c8a9a5SSteve French } 123738c8a9a5SSteve French 123838c8a9a5SSteve French /** 123938c8a9a5SSteve French * dfs_cache_remount_fs - remount a DFS share 124038c8a9a5SSteve French * 124138c8a9a5SSteve French * Reconfigure dfs mount by forcing a new DFS referral and if the currently cached targets do not 124238c8a9a5SSteve French * match any of the new targets, mark it for reconnect. 124338c8a9a5SSteve French * 124438c8a9a5SSteve French * @cifs_sb: cifs superblock. 124538c8a9a5SSteve French * 124638c8a9a5SSteve French * Return zero if remounted, otherwise non-zero. 124738c8a9a5SSteve French */ 124838c8a9a5SSteve French int dfs_cache_remount_fs(struct cifs_sb_info *cifs_sb) 124938c8a9a5SSteve French { 125038c8a9a5SSteve French struct cifs_tcon *tcon; 125138c8a9a5SSteve French 125238c8a9a5SSteve French if (!cifs_sb || !cifs_sb->master_tlink) 125338c8a9a5SSteve French return -EINVAL; 125438c8a9a5SSteve French 125538c8a9a5SSteve French tcon = cifs_sb_master_tcon(cifs_sb); 125638c8a9a5SSteve French 1257*3ae872deSPaulo Alcantara spin_lock(&tcon->tc_lock); 1258*3ae872deSPaulo Alcantara if (!tcon->origin_fullpath) { 1259*3ae872deSPaulo Alcantara spin_unlock(&tcon->tc_lock); 126038c8a9a5SSteve French cifs_dbg(FYI, "%s: not a dfs mount\n", __func__); 126138c8a9a5SSteve French return 0; 126238c8a9a5SSteve French } 1263*3ae872deSPaulo Alcantara spin_unlock(&tcon->tc_lock); 1264*3ae872deSPaulo Alcantara 126538c8a9a5SSteve French /* 126638c8a9a5SSteve French * After reconnecting to a different server, unique ids won't match anymore, so we disable 126738c8a9a5SSteve French * serverino. This prevents dentry revalidation to think the dentry are stale (ESTALE). 126838c8a9a5SSteve French */ 126938c8a9a5SSteve French cifs_autodisable_serverino(cifs_sb); 127038c8a9a5SSteve French /* 127138c8a9a5SSteve French * Force the use of prefix path to support failover on DFS paths that resolve to targets 127238c8a9a5SSteve French * that have different prefix paths. 127338c8a9a5SSteve French */ 127438c8a9a5SSteve French cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; 127538c8a9a5SSteve French 127638c8a9a5SSteve French return refresh_tcon(tcon, true); 127738c8a9a5SSteve French } 127838c8a9a5SSteve French 127938c8a9a5SSteve French /* Refresh all DFS referrals related to DFS tcon */ 128038c8a9a5SSteve French void dfs_cache_refresh(struct work_struct *work) 128138c8a9a5SSteve French { 128238c8a9a5SSteve French struct TCP_Server_Info *server; 128338c8a9a5SSteve French struct dfs_root_ses *rses; 128438c8a9a5SSteve French struct cifs_tcon *tcon; 128538c8a9a5SSteve French struct cifs_ses *ses; 128638c8a9a5SSteve French 128738c8a9a5SSteve French tcon = container_of(work, struct cifs_tcon, dfs_cache_work.work); 128838c8a9a5SSteve French ses = tcon->ses; 128938c8a9a5SSteve French server = ses->server; 129038c8a9a5SSteve French 129138c8a9a5SSteve French mutex_lock(&server->refpath_lock); 129238c8a9a5SSteve French if (server->leaf_fullpath) 129338c8a9a5SSteve French __refresh_tcon(server->leaf_fullpath + 1, ses, false); 129438c8a9a5SSteve French mutex_unlock(&server->refpath_lock); 129538c8a9a5SSteve French 129638c8a9a5SSteve French list_for_each_entry(rses, &tcon->dfs_ses_list, list) { 129738c8a9a5SSteve French ses = rses->ses; 129838c8a9a5SSteve French server = ses->server; 129938c8a9a5SSteve French mutex_lock(&server->refpath_lock); 130038c8a9a5SSteve French if (server->leaf_fullpath) 130138c8a9a5SSteve French __refresh_tcon(server->leaf_fullpath + 1, ses, false); 130238c8a9a5SSteve French mutex_unlock(&server->refpath_lock); 130338c8a9a5SSteve French } 130438c8a9a5SSteve French 130538c8a9a5SSteve French queue_delayed_work(dfscache_wq, &tcon->dfs_cache_work, 130638c8a9a5SSteve French atomic_read(&dfs_cache_ttl) * HZ); 130738c8a9a5SSteve French } 1308