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