18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * DFS referral cache routines 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2018-2019 Paulo Alcantara <palcantara@suse.de> 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/jhash.h> 98c2ecf20Sopenharmony_ci#include <linux/ktime.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 128c2ecf20Sopenharmony_ci#include <linux/nls.h> 138c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 148c2ecf20Sopenharmony_ci#include "cifsglob.h" 158c2ecf20Sopenharmony_ci#include "smb2pdu.h" 168c2ecf20Sopenharmony_ci#include "smb2proto.h" 178c2ecf20Sopenharmony_ci#include "cifsproto.h" 188c2ecf20Sopenharmony_ci#include "cifs_debug.h" 198c2ecf20Sopenharmony_ci#include "cifs_unicode.h" 208c2ecf20Sopenharmony_ci#include "smb2glob.h" 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include "dfs_cache.h" 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#define CACHE_HTABLE_SIZE 32 258c2ecf20Sopenharmony_ci#define CACHE_MAX_ENTRIES 64 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#define IS_INTERLINK_SET(v) ((v) & (DFSREF_REFERRAL_SERVER | \ 288c2ecf20Sopenharmony_ci DFSREF_STORAGE_SERVER)) 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_cistruct cache_dfs_tgt { 318c2ecf20Sopenharmony_ci char *name; 328c2ecf20Sopenharmony_ci int path_consumed; 338c2ecf20Sopenharmony_ci struct list_head list; 348c2ecf20Sopenharmony_ci}; 358c2ecf20Sopenharmony_ci 368c2ecf20Sopenharmony_cistruct cache_entry { 378c2ecf20Sopenharmony_ci struct hlist_node hlist; 388c2ecf20Sopenharmony_ci const char *path; 398c2ecf20Sopenharmony_ci int ttl; 408c2ecf20Sopenharmony_ci int srvtype; 418c2ecf20Sopenharmony_ci int flags; 428c2ecf20Sopenharmony_ci struct timespec64 etime; 438c2ecf20Sopenharmony_ci int path_consumed; 448c2ecf20Sopenharmony_ci int numtgts; 458c2ecf20Sopenharmony_ci struct list_head tlist; 468c2ecf20Sopenharmony_ci struct cache_dfs_tgt *tgthint; 478c2ecf20Sopenharmony_ci}; 488c2ecf20Sopenharmony_ci 498c2ecf20Sopenharmony_cistruct vol_info { 508c2ecf20Sopenharmony_ci char *fullpath; 518c2ecf20Sopenharmony_ci spinlock_t smb_vol_lock; 528c2ecf20Sopenharmony_ci struct smb_vol smb_vol; 538c2ecf20Sopenharmony_ci char *mntdata; 548c2ecf20Sopenharmony_ci struct list_head list; 558c2ecf20Sopenharmony_ci struct list_head rlist; 568c2ecf20Sopenharmony_ci struct kref refcnt; 578c2ecf20Sopenharmony_ci}; 588c2ecf20Sopenharmony_ci 598c2ecf20Sopenharmony_cistatic struct kmem_cache *cache_slab __read_mostly; 608c2ecf20Sopenharmony_cistatic struct workqueue_struct *dfscache_wq __read_mostly; 618c2ecf20Sopenharmony_ci 628c2ecf20Sopenharmony_cistatic int cache_ttl; 638c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(cache_ttl_lock); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cistatic struct nls_table *cache_nlsc; 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci/* 688c2ecf20Sopenharmony_ci * Number of entries in the cache 698c2ecf20Sopenharmony_ci */ 708c2ecf20Sopenharmony_cistatic atomic_t cache_count; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_cistatic struct hlist_head cache_htable[CACHE_HTABLE_SIZE]; 738c2ecf20Sopenharmony_cistatic DECLARE_RWSEM(htable_rw_lock); 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_cistatic LIST_HEAD(vol_list); 768c2ecf20Sopenharmony_cistatic DEFINE_SPINLOCK(vol_list_lock); 778c2ecf20Sopenharmony_ci 788c2ecf20Sopenharmony_cistatic void refresh_cache_worker(struct work_struct *work); 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic DECLARE_DELAYED_WORK(refresh_task, refresh_cache_worker); 818c2ecf20Sopenharmony_ci 828c2ecf20Sopenharmony_cistatic int get_normalized_path(const char *path, char **npath) 838c2ecf20Sopenharmony_ci{ 848c2ecf20Sopenharmony_ci if (!path || strlen(path) < 3 || (*path != '\\' && *path != '/')) 858c2ecf20Sopenharmony_ci return -EINVAL; 868c2ecf20Sopenharmony_ci 878c2ecf20Sopenharmony_ci if (*path == '\\') { 888c2ecf20Sopenharmony_ci *npath = (char *)path; 898c2ecf20Sopenharmony_ci } else { 908c2ecf20Sopenharmony_ci *npath = kstrndup(path, strlen(path), GFP_KERNEL); 918c2ecf20Sopenharmony_ci if (!*npath) 928c2ecf20Sopenharmony_ci return -ENOMEM; 938c2ecf20Sopenharmony_ci convert_delimiter(*npath, '\\'); 948c2ecf20Sopenharmony_ci } 958c2ecf20Sopenharmony_ci return 0; 968c2ecf20Sopenharmony_ci} 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_cistatic inline void free_normalized_path(const char *path, char *npath) 998c2ecf20Sopenharmony_ci{ 1008c2ecf20Sopenharmony_ci if (path != npath) 1018c2ecf20Sopenharmony_ci kfree(npath); 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_cistatic inline bool cache_entry_expired(const struct cache_entry *ce) 1058c2ecf20Sopenharmony_ci{ 1068c2ecf20Sopenharmony_ci struct timespec64 ts; 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci ktime_get_coarse_real_ts64(&ts); 1098c2ecf20Sopenharmony_ci return timespec64_compare(&ts, &ce->etime) >= 0; 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic inline void free_tgts(struct cache_entry *ce) 1138c2ecf20Sopenharmony_ci{ 1148c2ecf20Sopenharmony_ci struct cache_dfs_tgt *t, *n; 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci list_for_each_entry_safe(t, n, &ce->tlist, list) { 1178c2ecf20Sopenharmony_ci list_del(&t->list); 1188c2ecf20Sopenharmony_ci kfree(t->name); 1198c2ecf20Sopenharmony_ci kfree(t); 1208c2ecf20Sopenharmony_ci } 1218c2ecf20Sopenharmony_ci} 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_cistatic inline void flush_cache_ent(struct cache_entry *ce) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci hlist_del_init(&ce->hlist); 1268c2ecf20Sopenharmony_ci kfree(ce->path); 1278c2ecf20Sopenharmony_ci free_tgts(ce); 1288c2ecf20Sopenharmony_ci atomic_dec(&cache_count); 1298c2ecf20Sopenharmony_ci kmem_cache_free(cache_slab, ce); 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic void flush_cache_ents(void) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci int i; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ci for (i = 0; i < CACHE_HTABLE_SIZE; i++) { 1378c2ecf20Sopenharmony_ci struct hlist_head *l = &cache_htable[i]; 1388c2ecf20Sopenharmony_ci struct hlist_node *n; 1398c2ecf20Sopenharmony_ci struct cache_entry *ce; 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_ci hlist_for_each_entry_safe(ce, n, l, hlist) { 1428c2ecf20Sopenharmony_ci if (!hlist_unhashed(&ce->hlist)) 1438c2ecf20Sopenharmony_ci flush_cache_ent(ce); 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci } 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci/* 1498c2ecf20Sopenharmony_ci * dfs cache /proc file 1508c2ecf20Sopenharmony_ci */ 1518c2ecf20Sopenharmony_cistatic int dfscache_proc_show(struct seq_file *m, void *v) 1528c2ecf20Sopenharmony_ci{ 1538c2ecf20Sopenharmony_ci int i; 1548c2ecf20Sopenharmony_ci struct cache_entry *ce; 1558c2ecf20Sopenharmony_ci struct cache_dfs_tgt *t; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci seq_puts(m, "DFS cache\n---------\n"); 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci down_read(&htable_rw_lock); 1608c2ecf20Sopenharmony_ci for (i = 0; i < CACHE_HTABLE_SIZE; i++) { 1618c2ecf20Sopenharmony_ci struct hlist_head *l = &cache_htable[i]; 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci hlist_for_each_entry(ce, l, hlist) { 1648c2ecf20Sopenharmony_ci if (hlist_unhashed(&ce->hlist)) 1658c2ecf20Sopenharmony_ci continue; 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci seq_printf(m, 1688c2ecf20Sopenharmony_ci "cache entry: path=%s,type=%s,ttl=%d,etime=%ld," 1698c2ecf20Sopenharmony_ci "interlink=%s,path_consumed=%d,expired=%s\n", 1708c2ecf20Sopenharmony_ci ce->path, 1718c2ecf20Sopenharmony_ci ce->srvtype == DFS_TYPE_ROOT ? "root" : "link", 1728c2ecf20Sopenharmony_ci ce->ttl, ce->etime.tv_nsec, 1738c2ecf20Sopenharmony_ci IS_INTERLINK_SET(ce->flags) ? "yes" : "no", 1748c2ecf20Sopenharmony_ci ce->path_consumed, 1758c2ecf20Sopenharmony_ci cache_entry_expired(ce) ? "yes" : "no"); 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci list_for_each_entry(t, &ce->tlist, list) { 1788c2ecf20Sopenharmony_ci seq_printf(m, " %s%s\n", 1798c2ecf20Sopenharmony_ci t->name, 1808c2ecf20Sopenharmony_ci ce->tgthint == t ? " (target hint)" : ""); 1818c2ecf20Sopenharmony_ci } 1828c2ecf20Sopenharmony_ci } 1838c2ecf20Sopenharmony_ci } 1848c2ecf20Sopenharmony_ci up_read(&htable_rw_lock); 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_ci return 0; 1878c2ecf20Sopenharmony_ci} 1888c2ecf20Sopenharmony_ci 1898c2ecf20Sopenharmony_cistatic ssize_t dfscache_proc_write(struct file *file, const char __user *buffer, 1908c2ecf20Sopenharmony_ci size_t count, loff_t *ppos) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci char c; 1938c2ecf20Sopenharmony_ci int rc; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci rc = get_user(c, buffer); 1968c2ecf20Sopenharmony_ci if (rc) 1978c2ecf20Sopenharmony_ci return rc; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (c != '0') 2008c2ecf20Sopenharmony_ci return -EINVAL; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci cifs_dbg(FYI, "clearing dfs cache\n"); 2038c2ecf20Sopenharmony_ci 2048c2ecf20Sopenharmony_ci down_write(&htable_rw_lock); 2058c2ecf20Sopenharmony_ci flush_cache_ents(); 2068c2ecf20Sopenharmony_ci up_write(&htable_rw_lock); 2078c2ecf20Sopenharmony_ci 2088c2ecf20Sopenharmony_ci return count; 2098c2ecf20Sopenharmony_ci} 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_cistatic int dfscache_proc_open(struct inode *inode, struct file *file) 2128c2ecf20Sopenharmony_ci{ 2138c2ecf20Sopenharmony_ci return single_open(file, dfscache_proc_show, NULL); 2148c2ecf20Sopenharmony_ci} 2158c2ecf20Sopenharmony_ci 2168c2ecf20Sopenharmony_ciconst struct proc_ops dfscache_proc_ops = { 2178c2ecf20Sopenharmony_ci .proc_open = dfscache_proc_open, 2188c2ecf20Sopenharmony_ci .proc_read = seq_read, 2198c2ecf20Sopenharmony_ci .proc_lseek = seq_lseek, 2208c2ecf20Sopenharmony_ci .proc_release = single_release, 2218c2ecf20Sopenharmony_ci .proc_write = dfscache_proc_write, 2228c2ecf20Sopenharmony_ci}; 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_ci#ifdef CONFIG_CIFS_DEBUG2 2258c2ecf20Sopenharmony_cistatic inline void dump_tgts(const struct cache_entry *ce) 2268c2ecf20Sopenharmony_ci{ 2278c2ecf20Sopenharmony_ci struct cache_dfs_tgt *t; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci cifs_dbg(FYI, "target list:\n"); 2308c2ecf20Sopenharmony_ci list_for_each_entry(t, &ce->tlist, list) { 2318c2ecf20Sopenharmony_ci cifs_dbg(FYI, " %s%s\n", t->name, 2328c2ecf20Sopenharmony_ci ce->tgthint == t ? " (target hint)" : ""); 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic inline void dump_ce(const struct cache_entry *ce) 2378c2ecf20Sopenharmony_ci{ 2388c2ecf20Sopenharmony_ci cifs_dbg(FYI, "cache entry: path=%s,type=%s,ttl=%d,etime=%ld,interlink=%s,path_consumed=%d,expired=%s\n", 2398c2ecf20Sopenharmony_ci ce->path, 2408c2ecf20Sopenharmony_ci ce->srvtype == DFS_TYPE_ROOT ? "root" : "link", ce->ttl, 2418c2ecf20Sopenharmony_ci ce->etime.tv_nsec, 2428c2ecf20Sopenharmony_ci IS_INTERLINK_SET(ce->flags) ? "yes" : "no", 2438c2ecf20Sopenharmony_ci ce->path_consumed, 2448c2ecf20Sopenharmony_ci cache_entry_expired(ce) ? "yes" : "no"); 2458c2ecf20Sopenharmony_ci dump_tgts(ce); 2468c2ecf20Sopenharmony_ci} 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_cistatic inline void dump_refs(const struct dfs_info3_param *refs, int numrefs) 2498c2ecf20Sopenharmony_ci{ 2508c2ecf20Sopenharmony_ci int i; 2518c2ecf20Sopenharmony_ci 2528c2ecf20Sopenharmony_ci cifs_dbg(FYI, "DFS referrals returned by the server:\n"); 2538c2ecf20Sopenharmony_ci for (i = 0; i < numrefs; i++) { 2548c2ecf20Sopenharmony_ci const struct dfs_info3_param *ref = &refs[i]; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci cifs_dbg(FYI, 2578c2ecf20Sopenharmony_ci "\n" 2588c2ecf20Sopenharmony_ci "flags: 0x%x\n" 2598c2ecf20Sopenharmony_ci "path_consumed: %d\n" 2608c2ecf20Sopenharmony_ci "server_type: 0x%x\n" 2618c2ecf20Sopenharmony_ci "ref_flag: 0x%x\n" 2628c2ecf20Sopenharmony_ci "path_name: %s\n" 2638c2ecf20Sopenharmony_ci "node_name: %s\n" 2648c2ecf20Sopenharmony_ci "ttl: %d (%dm)\n", 2658c2ecf20Sopenharmony_ci ref->flags, ref->path_consumed, ref->server_type, 2668c2ecf20Sopenharmony_ci ref->ref_flag, ref->path_name, ref->node_name, 2678c2ecf20Sopenharmony_ci ref->ttl, ref->ttl / 60); 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci} 2708c2ecf20Sopenharmony_ci#else 2718c2ecf20Sopenharmony_ci#define dump_tgts(e) 2728c2ecf20Sopenharmony_ci#define dump_ce(e) 2738c2ecf20Sopenharmony_ci#define dump_refs(r, n) 2748c2ecf20Sopenharmony_ci#endif 2758c2ecf20Sopenharmony_ci 2768c2ecf20Sopenharmony_ci/** 2778c2ecf20Sopenharmony_ci * dfs_cache_init - Initialize DFS referral cache. 2788c2ecf20Sopenharmony_ci * 2798c2ecf20Sopenharmony_ci * Return zero if initialized successfully, otherwise non-zero. 2808c2ecf20Sopenharmony_ci */ 2818c2ecf20Sopenharmony_ciint dfs_cache_init(void) 2828c2ecf20Sopenharmony_ci{ 2838c2ecf20Sopenharmony_ci int rc; 2848c2ecf20Sopenharmony_ci int i; 2858c2ecf20Sopenharmony_ci 2868c2ecf20Sopenharmony_ci dfscache_wq = alloc_workqueue("cifs-dfscache", 2878c2ecf20Sopenharmony_ci WQ_FREEZABLE | WQ_MEM_RECLAIM, 1); 2888c2ecf20Sopenharmony_ci if (!dfscache_wq) 2898c2ecf20Sopenharmony_ci return -ENOMEM; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci cache_slab = kmem_cache_create("cifs_dfs_cache", 2928c2ecf20Sopenharmony_ci sizeof(struct cache_entry), 0, 2938c2ecf20Sopenharmony_ci SLAB_HWCACHE_ALIGN, NULL); 2948c2ecf20Sopenharmony_ci if (!cache_slab) { 2958c2ecf20Sopenharmony_ci rc = -ENOMEM; 2968c2ecf20Sopenharmony_ci goto out_destroy_wq; 2978c2ecf20Sopenharmony_ci } 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_ci for (i = 0; i < CACHE_HTABLE_SIZE; i++) 3008c2ecf20Sopenharmony_ci INIT_HLIST_HEAD(&cache_htable[i]); 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci atomic_set(&cache_count, 0); 3038c2ecf20Sopenharmony_ci cache_nlsc = load_nls_default(); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: initialized DFS referral cache\n", __func__); 3068c2ecf20Sopenharmony_ci return 0; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ciout_destroy_wq: 3098c2ecf20Sopenharmony_ci destroy_workqueue(dfscache_wq); 3108c2ecf20Sopenharmony_ci return rc; 3118c2ecf20Sopenharmony_ci} 3128c2ecf20Sopenharmony_ci 3138c2ecf20Sopenharmony_cistatic inline unsigned int cache_entry_hash(const void *data, int size) 3148c2ecf20Sopenharmony_ci{ 3158c2ecf20Sopenharmony_ci unsigned int h; 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci h = jhash(data, size, 0); 3188c2ecf20Sopenharmony_ci return h & (CACHE_HTABLE_SIZE - 1); 3198c2ecf20Sopenharmony_ci} 3208c2ecf20Sopenharmony_ci 3218c2ecf20Sopenharmony_ci/* Check whether second path component of @path is SYSVOL or NETLOGON */ 3228c2ecf20Sopenharmony_cistatic inline bool is_sysvol_or_netlogon(const char *path) 3238c2ecf20Sopenharmony_ci{ 3248c2ecf20Sopenharmony_ci const char *s; 3258c2ecf20Sopenharmony_ci char sep = path[0]; 3268c2ecf20Sopenharmony_ci 3278c2ecf20Sopenharmony_ci s = strchr(path + 1, sep) + 1; 3288c2ecf20Sopenharmony_ci return !strncasecmp(s, "sysvol", strlen("sysvol")) || 3298c2ecf20Sopenharmony_ci !strncasecmp(s, "netlogon", strlen("netlogon")); 3308c2ecf20Sopenharmony_ci} 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci/* Return target hint of a DFS cache entry */ 3338c2ecf20Sopenharmony_cistatic inline char *get_tgt_name(const struct cache_entry *ce) 3348c2ecf20Sopenharmony_ci{ 3358c2ecf20Sopenharmony_ci struct cache_dfs_tgt *t = ce->tgthint; 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci return t ? t->name : ERR_PTR(-ENOENT); 3388c2ecf20Sopenharmony_ci} 3398c2ecf20Sopenharmony_ci 3408c2ecf20Sopenharmony_ci/* Return expire time out of a new entry's TTL */ 3418c2ecf20Sopenharmony_cistatic inline struct timespec64 get_expire_time(int ttl) 3428c2ecf20Sopenharmony_ci{ 3438c2ecf20Sopenharmony_ci struct timespec64 ts = { 3448c2ecf20Sopenharmony_ci .tv_sec = ttl, 3458c2ecf20Sopenharmony_ci .tv_nsec = 0, 3468c2ecf20Sopenharmony_ci }; 3478c2ecf20Sopenharmony_ci struct timespec64 now; 3488c2ecf20Sopenharmony_ci 3498c2ecf20Sopenharmony_ci ktime_get_coarse_real_ts64(&now); 3508c2ecf20Sopenharmony_ci return timespec64_add(now, ts); 3518c2ecf20Sopenharmony_ci} 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_ci/* Allocate a new DFS target */ 3548c2ecf20Sopenharmony_cistatic struct cache_dfs_tgt *alloc_target(const char *name, int path_consumed) 3558c2ecf20Sopenharmony_ci{ 3568c2ecf20Sopenharmony_ci struct cache_dfs_tgt *t; 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_ci t = kmalloc(sizeof(*t), GFP_ATOMIC); 3598c2ecf20Sopenharmony_ci if (!t) 3608c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3618c2ecf20Sopenharmony_ci t->name = kstrndup(name, strlen(name), GFP_ATOMIC); 3628c2ecf20Sopenharmony_ci if (!t->name) { 3638c2ecf20Sopenharmony_ci kfree(t); 3648c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 3658c2ecf20Sopenharmony_ci } 3668c2ecf20Sopenharmony_ci t->path_consumed = path_consumed; 3678c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&t->list); 3688c2ecf20Sopenharmony_ci return t; 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci/* 3728c2ecf20Sopenharmony_ci * Copy DFS referral information to a cache entry and conditionally update 3738c2ecf20Sopenharmony_ci * target hint. 3748c2ecf20Sopenharmony_ci */ 3758c2ecf20Sopenharmony_cistatic int copy_ref_data(const struct dfs_info3_param *refs, int numrefs, 3768c2ecf20Sopenharmony_ci struct cache_entry *ce, const char *tgthint) 3778c2ecf20Sopenharmony_ci{ 3788c2ecf20Sopenharmony_ci int i; 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci ce->ttl = refs[0].ttl; 3818c2ecf20Sopenharmony_ci ce->etime = get_expire_time(ce->ttl); 3828c2ecf20Sopenharmony_ci ce->srvtype = refs[0].server_type; 3838c2ecf20Sopenharmony_ci ce->flags = refs[0].ref_flag; 3848c2ecf20Sopenharmony_ci ce->path_consumed = refs[0].path_consumed; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci for (i = 0; i < numrefs; i++) { 3878c2ecf20Sopenharmony_ci struct cache_dfs_tgt *t; 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci t = alloc_target(refs[i].node_name, refs[i].path_consumed); 3908c2ecf20Sopenharmony_ci if (IS_ERR(t)) { 3918c2ecf20Sopenharmony_ci free_tgts(ce); 3928c2ecf20Sopenharmony_ci return PTR_ERR(t); 3938c2ecf20Sopenharmony_ci } 3948c2ecf20Sopenharmony_ci if (tgthint && !strcasecmp(t->name, tgthint)) { 3958c2ecf20Sopenharmony_ci list_add(&t->list, &ce->tlist); 3968c2ecf20Sopenharmony_ci tgthint = NULL; 3978c2ecf20Sopenharmony_ci } else { 3988c2ecf20Sopenharmony_ci list_add_tail(&t->list, &ce->tlist); 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci ce->numtgts++; 4018c2ecf20Sopenharmony_ci } 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci ce->tgthint = list_first_entry_or_null(&ce->tlist, 4048c2ecf20Sopenharmony_ci struct cache_dfs_tgt, list); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci return 0; 4078c2ecf20Sopenharmony_ci} 4088c2ecf20Sopenharmony_ci 4098c2ecf20Sopenharmony_ci/* Allocate a new cache entry */ 4108c2ecf20Sopenharmony_cistatic struct cache_entry *alloc_cache_entry(const char *path, 4118c2ecf20Sopenharmony_ci const struct dfs_info3_param *refs, 4128c2ecf20Sopenharmony_ci int numrefs) 4138c2ecf20Sopenharmony_ci{ 4148c2ecf20Sopenharmony_ci struct cache_entry *ce; 4158c2ecf20Sopenharmony_ci int rc; 4168c2ecf20Sopenharmony_ci 4178c2ecf20Sopenharmony_ci ce = kmem_cache_zalloc(cache_slab, GFP_KERNEL); 4188c2ecf20Sopenharmony_ci if (!ce) 4198c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci ce->path = kstrndup(path, strlen(path), GFP_KERNEL); 4228c2ecf20Sopenharmony_ci if (!ce->path) { 4238c2ecf20Sopenharmony_ci kmem_cache_free(cache_slab, ce); 4248c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 4258c2ecf20Sopenharmony_ci } 4268c2ecf20Sopenharmony_ci INIT_HLIST_NODE(&ce->hlist); 4278c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&ce->tlist); 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci rc = copy_ref_data(refs, numrefs, ce, NULL); 4308c2ecf20Sopenharmony_ci if (rc) { 4318c2ecf20Sopenharmony_ci kfree(ce->path); 4328c2ecf20Sopenharmony_ci kmem_cache_free(cache_slab, ce); 4338c2ecf20Sopenharmony_ci ce = ERR_PTR(rc); 4348c2ecf20Sopenharmony_ci } 4358c2ecf20Sopenharmony_ci return ce; 4368c2ecf20Sopenharmony_ci} 4378c2ecf20Sopenharmony_ci 4388c2ecf20Sopenharmony_ci/* Must be called with htable_rw_lock held */ 4398c2ecf20Sopenharmony_cistatic void remove_oldest_entry(void) 4408c2ecf20Sopenharmony_ci{ 4418c2ecf20Sopenharmony_ci int i; 4428c2ecf20Sopenharmony_ci struct cache_entry *ce; 4438c2ecf20Sopenharmony_ci struct cache_entry *to_del = NULL; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci for (i = 0; i < CACHE_HTABLE_SIZE; i++) { 4468c2ecf20Sopenharmony_ci struct hlist_head *l = &cache_htable[i]; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci hlist_for_each_entry(ce, l, hlist) { 4498c2ecf20Sopenharmony_ci if (hlist_unhashed(&ce->hlist)) 4508c2ecf20Sopenharmony_ci continue; 4518c2ecf20Sopenharmony_ci if (!to_del || timespec64_compare(&ce->etime, 4528c2ecf20Sopenharmony_ci &to_del->etime) < 0) 4538c2ecf20Sopenharmony_ci to_del = ce; 4548c2ecf20Sopenharmony_ci } 4558c2ecf20Sopenharmony_ci } 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci if (!to_del) { 4588c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: no entry to remove\n", __func__); 4598c2ecf20Sopenharmony_ci return; 4608c2ecf20Sopenharmony_ci } 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: removing entry\n", __func__); 4638c2ecf20Sopenharmony_ci dump_ce(to_del); 4648c2ecf20Sopenharmony_ci flush_cache_ent(to_del); 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci/* Add a new DFS cache entry */ 4688c2ecf20Sopenharmony_cistatic int add_cache_entry(const char *path, unsigned int hash, 4698c2ecf20Sopenharmony_ci struct dfs_info3_param *refs, int numrefs) 4708c2ecf20Sopenharmony_ci{ 4718c2ecf20Sopenharmony_ci struct cache_entry *ce; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci ce = alloc_cache_entry(path, refs, numrefs); 4748c2ecf20Sopenharmony_ci if (IS_ERR(ce)) 4758c2ecf20Sopenharmony_ci return PTR_ERR(ce); 4768c2ecf20Sopenharmony_ci 4778c2ecf20Sopenharmony_ci spin_lock(&cache_ttl_lock); 4788c2ecf20Sopenharmony_ci if (!cache_ttl) { 4798c2ecf20Sopenharmony_ci cache_ttl = ce->ttl; 4808c2ecf20Sopenharmony_ci queue_delayed_work(dfscache_wq, &refresh_task, cache_ttl * HZ); 4818c2ecf20Sopenharmony_ci } else { 4828c2ecf20Sopenharmony_ci cache_ttl = min_t(int, cache_ttl, ce->ttl); 4838c2ecf20Sopenharmony_ci mod_delayed_work(dfscache_wq, &refresh_task, cache_ttl * HZ); 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci spin_unlock(&cache_ttl_lock); 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci down_write(&htable_rw_lock); 4888c2ecf20Sopenharmony_ci hlist_add_head(&ce->hlist, &cache_htable[hash]); 4898c2ecf20Sopenharmony_ci dump_ce(ce); 4908c2ecf20Sopenharmony_ci up_write(&htable_rw_lock); 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci return 0; 4938c2ecf20Sopenharmony_ci} 4948c2ecf20Sopenharmony_ci 4958c2ecf20Sopenharmony_cistatic struct cache_entry *__lookup_cache_entry(const char *path) 4968c2ecf20Sopenharmony_ci{ 4978c2ecf20Sopenharmony_ci struct cache_entry *ce; 4988c2ecf20Sopenharmony_ci unsigned int h; 4998c2ecf20Sopenharmony_ci bool found = false; 5008c2ecf20Sopenharmony_ci 5018c2ecf20Sopenharmony_ci h = cache_entry_hash(path, strlen(path)); 5028c2ecf20Sopenharmony_ci 5038c2ecf20Sopenharmony_ci hlist_for_each_entry(ce, &cache_htable[h], hlist) { 5048c2ecf20Sopenharmony_ci if (!strcasecmp(path, ce->path)) { 5058c2ecf20Sopenharmony_ci found = true; 5068c2ecf20Sopenharmony_ci dump_ce(ce); 5078c2ecf20Sopenharmony_ci break; 5088c2ecf20Sopenharmony_ci } 5098c2ecf20Sopenharmony_ci } 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci if (!found) 5128c2ecf20Sopenharmony_ci ce = ERR_PTR(-ENOENT); 5138c2ecf20Sopenharmony_ci return ce; 5148c2ecf20Sopenharmony_ci} 5158c2ecf20Sopenharmony_ci 5168c2ecf20Sopenharmony_ci/* 5178c2ecf20Sopenharmony_ci * Find a DFS cache entry in hash table and optionally check prefix path against 5188c2ecf20Sopenharmony_ci * @path. 5198c2ecf20Sopenharmony_ci * Use whole path components in the match. 5208c2ecf20Sopenharmony_ci * Must be called with htable_rw_lock held. 5218c2ecf20Sopenharmony_ci * 5228c2ecf20Sopenharmony_ci * Return ERR_PTR(-ENOENT) if the entry is not found. 5238c2ecf20Sopenharmony_ci */ 5248c2ecf20Sopenharmony_cistatic struct cache_entry *lookup_cache_entry(const char *path, unsigned int *hash) 5258c2ecf20Sopenharmony_ci{ 5268c2ecf20Sopenharmony_ci struct cache_entry *ce = ERR_PTR(-ENOENT); 5278c2ecf20Sopenharmony_ci unsigned int h; 5288c2ecf20Sopenharmony_ci int cnt = 0; 5298c2ecf20Sopenharmony_ci char *npath; 5308c2ecf20Sopenharmony_ci char *s, *e; 5318c2ecf20Sopenharmony_ci char sep; 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci npath = kstrndup(path, strlen(path), GFP_KERNEL); 5348c2ecf20Sopenharmony_ci if (!npath) 5358c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci s = npath; 5388c2ecf20Sopenharmony_ci sep = *npath; 5398c2ecf20Sopenharmony_ci while ((s = strchr(s, sep)) && ++cnt < 3) 5408c2ecf20Sopenharmony_ci s++; 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci if (cnt < 3) { 5438c2ecf20Sopenharmony_ci h = cache_entry_hash(path, strlen(path)); 5448c2ecf20Sopenharmony_ci ce = __lookup_cache_entry(path); 5458c2ecf20Sopenharmony_ci goto out; 5468c2ecf20Sopenharmony_ci } 5478c2ecf20Sopenharmony_ci /* 5488c2ecf20Sopenharmony_ci * Handle paths that have more than two path components and are a complete prefix of the DFS 5498c2ecf20Sopenharmony_ci * referral request path (@path). 5508c2ecf20Sopenharmony_ci * 5518c2ecf20Sopenharmony_ci * See MS-DFSC 3.2.5.5 "Receiving a Root Referral Request or Link Referral Request". 5528c2ecf20Sopenharmony_ci */ 5538c2ecf20Sopenharmony_ci h = cache_entry_hash(npath, strlen(npath)); 5548c2ecf20Sopenharmony_ci e = npath + strlen(npath) - 1; 5558c2ecf20Sopenharmony_ci while (e > s) { 5568c2ecf20Sopenharmony_ci char tmp; 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci /* skip separators */ 5598c2ecf20Sopenharmony_ci while (e > s && *e == sep) 5608c2ecf20Sopenharmony_ci e--; 5618c2ecf20Sopenharmony_ci if (e == s) 5628c2ecf20Sopenharmony_ci goto out; 5638c2ecf20Sopenharmony_ci 5648c2ecf20Sopenharmony_ci tmp = *(e+1); 5658c2ecf20Sopenharmony_ci *(e+1) = 0; 5668c2ecf20Sopenharmony_ci 5678c2ecf20Sopenharmony_ci ce = __lookup_cache_entry(npath); 5688c2ecf20Sopenharmony_ci if (!IS_ERR(ce)) { 5698c2ecf20Sopenharmony_ci h = cache_entry_hash(npath, strlen(npath)); 5708c2ecf20Sopenharmony_ci break; 5718c2ecf20Sopenharmony_ci } 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci *(e+1) = tmp; 5748c2ecf20Sopenharmony_ci /* backward until separator */ 5758c2ecf20Sopenharmony_ci while (e > s && *e != sep) 5768c2ecf20Sopenharmony_ci e--; 5778c2ecf20Sopenharmony_ci } 5788c2ecf20Sopenharmony_ciout: 5798c2ecf20Sopenharmony_ci if (hash) 5808c2ecf20Sopenharmony_ci *hash = h; 5818c2ecf20Sopenharmony_ci kfree(npath); 5828c2ecf20Sopenharmony_ci return ce; 5838c2ecf20Sopenharmony_ci} 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_cistatic void __vol_release(struct vol_info *vi) 5868c2ecf20Sopenharmony_ci{ 5878c2ecf20Sopenharmony_ci kfree(vi->fullpath); 5888c2ecf20Sopenharmony_ci kfree(vi->mntdata); 5898c2ecf20Sopenharmony_ci cifs_cleanup_volume_info_contents(&vi->smb_vol); 5908c2ecf20Sopenharmony_ci kfree(vi); 5918c2ecf20Sopenharmony_ci} 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_cistatic void vol_release(struct kref *kref) 5948c2ecf20Sopenharmony_ci{ 5958c2ecf20Sopenharmony_ci struct vol_info *vi = container_of(kref, struct vol_info, refcnt); 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci spin_lock(&vol_list_lock); 5988c2ecf20Sopenharmony_ci list_del(&vi->list); 5998c2ecf20Sopenharmony_ci spin_unlock(&vol_list_lock); 6008c2ecf20Sopenharmony_ci __vol_release(vi); 6018c2ecf20Sopenharmony_ci} 6028c2ecf20Sopenharmony_ci 6038c2ecf20Sopenharmony_cistatic inline void free_vol_list(void) 6048c2ecf20Sopenharmony_ci{ 6058c2ecf20Sopenharmony_ci struct vol_info *vi, *nvi; 6068c2ecf20Sopenharmony_ci 6078c2ecf20Sopenharmony_ci list_for_each_entry_safe(vi, nvi, &vol_list, list) { 6088c2ecf20Sopenharmony_ci list_del_init(&vi->list); 6098c2ecf20Sopenharmony_ci __vol_release(vi); 6108c2ecf20Sopenharmony_ci } 6118c2ecf20Sopenharmony_ci} 6128c2ecf20Sopenharmony_ci 6138c2ecf20Sopenharmony_ci/** 6148c2ecf20Sopenharmony_ci * dfs_cache_destroy - destroy DFS referral cache 6158c2ecf20Sopenharmony_ci */ 6168c2ecf20Sopenharmony_civoid dfs_cache_destroy(void) 6178c2ecf20Sopenharmony_ci{ 6188c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&refresh_task); 6198c2ecf20Sopenharmony_ci unload_nls(cache_nlsc); 6208c2ecf20Sopenharmony_ci free_vol_list(); 6218c2ecf20Sopenharmony_ci flush_cache_ents(); 6228c2ecf20Sopenharmony_ci kmem_cache_destroy(cache_slab); 6238c2ecf20Sopenharmony_ci destroy_workqueue(dfscache_wq); 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: destroyed DFS referral cache\n", __func__); 6268c2ecf20Sopenharmony_ci} 6278c2ecf20Sopenharmony_ci 6288c2ecf20Sopenharmony_ci/* Must be called with htable_rw_lock held */ 6298c2ecf20Sopenharmony_cistatic int __update_cache_entry(const char *path, 6308c2ecf20Sopenharmony_ci const struct dfs_info3_param *refs, 6318c2ecf20Sopenharmony_ci int numrefs) 6328c2ecf20Sopenharmony_ci{ 6338c2ecf20Sopenharmony_ci int rc; 6348c2ecf20Sopenharmony_ci struct cache_entry *ce; 6358c2ecf20Sopenharmony_ci char *s, *th = NULL; 6368c2ecf20Sopenharmony_ci 6378c2ecf20Sopenharmony_ci ce = lookup_cache_entry(path, NULL); 6388c2ecf20Sopenharmony_ci if (IS_ERR(ce)) 6398c2ecf20Sopenharmony_ci return PTR_ERR(ce); 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci if (ce->tgthint) { 6428c2ecf20Sopenharmony_ci s = ce->tgthint->name; 6438c2ecf20Sopenharmony_ci th = kstrndup(s, strlen(s), GFP_ATOMIC); 6448c2ecf20Sopenharmony_ci if (!th) 6458c2ecf20Sopenharmony_ci return -ENOMEM; 6468c2ecf20Sopenharmony_ci } 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci free_tgts(ce); 6498c2ecf20Sopenharmony_ci ce->numtgts = 0; 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci rc = copy_ref_data(refs, numrefs, ce, th); 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci kfree(th); 6548c2ecf20Sopenharmony_ci 6558c2ecf20Sopenharmony_ci return rc; 6568c2ecf20Sopenharmony_ci} 6578c2ecf20Sopenharmony_ci 6588c2ecf20Sopenharmony_cistatic int get_dfs_referral(const unsigned int xid, struct cifs_ses *ses, 6598c2ecf20Sopenharmony_ci const struct nls_table *nls_codepage, int remap, 6608c2ecf20Sopenharmony_ci const char *path, struct dfs_info3_param **refs, 6618c2ecf20Sopenharmony_ci int *numrefs) 6628c2ecf20Sopenharmony_ci{ 6638c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: get an DFS referral for %s\n", __func__, path); 6648c2ecf20Sopenharmony_ci 6658c2ecf20Sopenharmony_ci if (!ses || !ses->server || !ses->server->ops->get_dfs_refer) 6668c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 6678c2ecf20Sopenharmony_ci if (unlikely(!nls_codepage)) 6688c2ecf20Sopenharmony_ci return -EINVAL; 6698c2ecf20Sopenharmony_ci 6708c2ecf20Sopenharmony_ci *refs = NULL; 6718c2ecf20Sopenharmony_ci *numrefs = 0; 6728c2ecf20Sopenharmony_ci 6738c2ecf20Sopenharmony_ci return ses->server->ops->get_dfs_refer(xid, ses, path, refs, numrefs, 6748c2ecf20Sopenharmony_ci nls_codepage, remap); 6758c2ecf20Sopenharmony_ci} 6768c2ecf20Sopenharmony_ci 6778c2ecf20Sopenharmony_ci/* Update an expired cache entry by getting a new DFS referral from server */ 6788c2ecf20Sopenharmony_cistatic int update_cache_entry(const char *path, 6798c2ecf20Sopenharmony_ci const struct dfs_info3_param *refs, 6808c2ecf20Sopenharmony_ci int numrefs) 6818c2ecf20Sopenharmony_ci{ 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_ci int rc; 6848c2ecf20Sopenharmony_ci 6858c2ecf20Sopenharmony_ci down_write(&htable_rw_lock); 6868c2ecf20Sopenharmony_ci rc = __update_cache_entry(path, refs, numrefs); 6878c2ecf20Sopenharmony_ci up_write(&htable_rw_lock); 6888c2ecf20Sopenharmony_ci 6898c2ecf20Sopenharmony_ci return rc; 6908c2ecf20Sopenharmony_ci} 6918c2ecf20Sopenharmony_ci 6928c2ecf20Sopenharmony_ci/* 6938c2ecf20Sopenharmony_ci * Find, create or update a DFS cache entry. 6948c2ecf20Sopenharmony_ci * 6958c2ecf20Sopenharmony_ci * If the entry wasn't found, it will create a new one. Or if it was found but 6968c2ecf20Sopenharmony_ci * expired, then it will update the entry accordingly. 6978c2ecf20Sopenharmony_ci * 6988c2ecf20Sopenharmony_ci * For interlinks, __cifs_dfs_mount() and expand_dfs_referral() are supposed to 6998c2ecf20Sopenharmony_ci * handle them properly. 7008c2ecf20Sopenharmony_ci */ 7018c2ecf20Sopenharmony_cistatic int __dfs_cache_find(const unsigned int xid, struct cifs_ses *ses, 7028c2ecf20Sopenharmony_ci const struct nls_table *nls_codepage, int remap, 7038c2ecf20Sopenharmony_ci const char *path, bool noreq) 7048c2ecf20Sopenharmony_ci{ 7058c2ecf20Sopenharmony_ci int rc; 7068c2ecf20Sopenharmony_ci unsigned int hash; 7078c2ecf20Sopenharmony_ci struct cache_entry *ce; 7088c2ecf20Sopenharmony_ci struct dfs_info3_param *refs = NULL; 7098c2ecf20Sopenharmony_ci int numrefs = 0; 7108c2ecf20Sopenharmony_ci bool newent = false; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: search path: %s\n", __func__, path); 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci down_read(&htable_rw_lock); 7158c2ecf20Sopenharmony_ci 7168c2ecf20Sopenharmony_ci ce = lookup_cache_entry(path, &hash); 7178c2ecf20Sopenharmony_ci 7188c2ecf20Sopenharmony_ci /* 7198c2ecf20Sopenharmony_ci * If @noreq is set, no requests will be sent to the server. Just return 7208c2ecf20Sopenharmony_ci * the cache entry. 7218c2ecf20Sopenharmony_ci */ 7228c2ecf20Sopenharmony_ci if (noreq) { 7238c2ecf20Sopenharmony_ci up_read(&htable_rw_lock); 7248c2ecf20Sopenharmony_ci return PTR_ERR_OR_ZERO(ce); 7258c2ecf20Sopenharmony_ci } 7268c2ecf20Sopenharmony_ci 7278c2ecf20Sopenharmony_ci if (!IS_ERR(ce)) { 7288c2ecf20Sopenharmony_ci if (!cache_entry_expired(ce)) { 7298c2ecf20Sopenharmony_ci dump_ce(ce); 7308c2ecf20Sopenharmony_ci up_read(&htable_rw_lock); 7318c2ecf20Sopenharmony_ci return 0; 7328c2ecf20Sopenharmony_ci } 7338c2ecf20Sopenharmony_ci } else { 7348c2ecf20Sopenharmony_ci newent = true; 7358c2ecf20Sopenharmony_ci } 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_ci up_read(&htable_rw_lock); 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci /* 7408c2ecf20Sopenharmony_ci * No entry was found. 7418c2ecf20Sopenharmony_ci * 7428c2ecf20Sopenharmony_ci * Request a new DFS referral in order to create a new cache entry, or 7438c2ecf20Sopenharmony_ci * updating an existing one. 7448c2ecf20Sopenharmony_ci */ 7458c2ecf20Sopenharmony_ci rc = get_dfs_referral(xid, ses, nls_codepage, remap, path, 7468c2ecf20Sopenharmony_ci &refs, &numrefs); 7478c2ecf20Sopenharmony_ci if (rc) 7488c2ecf20Sopenharmony_ci return rc; 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_ci dump_refs(refs, numrefs); 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci if (!newent) { 7538c2ecf20Sopenharmony_ci rc = update_cache_entry(path, refs, numrefs); 7548c2ecf20Sopenharmony_ci goto out_free_refs; 7558c2ecf20Sopenharmony_ci } 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci if (atomic_read(&cache_count) >= CACHE_MAX_ENTRIES) { 7588c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: reached max cache size (%d)\n", 7598c2ecf20Sopenharmony_ci __func__, CACHE_MAX_ENTRIES); 7608c2ecf20Sopenharmony_ci down_write(&htable_rw_lock); 7618c2ecf20Sopenharmony_ci remove_oldest_entry(); 7628c2ecf20Sopenharmony_ci up_write(&htable_rw_lock); 7638c2ecf20Sopenharmony_ci } 7648c2ecf20Sopenharmony_ci 7658c2ecf20Sopenharmony_ci rc = add_cache_entry(path, hash, refs, numrefs); 7668c2ecf20Sopenharmony_ci if (!rc) 7678c2ecf20Sopenharmony_ci atomic_inc(&cache_count); 7688c2ecf20Sopenharmony_ci 7698c2ecf20Sopenharmony_ciout_free_refs: 7708c2ecf20Sopenharmony_ci free_dfs_info_array(refs, numrefs); 7718c2ecf20Sopenharmony_ci return rc; 7728c2ecf20Sopenharmony_ci} 7738c2ecf20Sopenharmony_ci 7748c2ecf20Sopenharmony_ci/* 7758c2ecf20Sopenharmony_ci * Set up a DFS referral from a given cache entry. 7768c2ecf20Sopenharmony_ci * 7778c2ecf20Sopenharmony_ci * Must be called with htable_rw_lock held. 7788c2ecf20Sopenharmony_ci */ 7798c2ecf20Sopenharmony_cistatic int setup_referral(const char *path, struct cache_entry *ce, 7808c2ecf20Sopenharmony_ci struct dfs_info3_param *ref, const char *target) 7818c2ecf20Sopenharmony_ci{ 7828c2ecf20Sopenharmony_ci int rc; 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: set up new ref\n", __func__); 7858c2ecf20Sopenharmony_ci 7868c2ecf20Sopenharmony_ci memset(ref, 0, sizeof(*ref)); 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci ref->path_name = kstrndup(path, strlen(path), GFP_ATOMIC); 7898c2ecf20Sopenharmony_ci if (!ref->path_name) 7908c2ecf20Sopenharmony_ci return -ENOMEM; 7918c2ecf20Sopenharmony_ci 7928c2ecf20Sopenharmony_ci ref->node_name = kstrndup(target, strlen(target), GFP_ATOMIC); 7938c2ecf20Sopenharmony_ci if (!ref->node_name) { 7948c2ecf20Sopenharmony_ci rc = -ENOMEM; 7958c2ecf20Sopenharmony_ci goto err_free_path; 7968c2ecf20Sopenharmony_ci } 7978c2ecf20Sopenharmony_ci 7988c2ecf20Sopenharmony_ci ref->path_consumed = ce->path_consumed; 7998c2ecf20Sopenharmony_ci ref->ttl = ce->ttl; 8008c2ecf20Sopenharmony_ci ref->server_type = ce->srvtype; 8018c2ecf20Sopenharmony_ci ref->ref_flag = ce->flags; 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci return 0; 8048c2ecf20Sopenharmony_ci 8058c2ecf20Sopenharmony_cierr_free_path: 8068c2ecf20Sopenharmony_ci kfree(ref->path_name); 8078c2ecf20Sopenharmony_ci ref->path_name = NULL; 8088c2ecf20Sopenharmony_ci return rc; 8098c2ecf20Sopenharmony_ci} 8108c2ecf20Sopenharmony_ci 8118c2ecf20Sopenharmony_ci/* Return target list of a DFS cache entry */ 8128c2ecf20Sopenharmony_cistatic int get_targets(struct cache_entry *ce, struct dfs_cache_tgt_list *tl) 8138c2ecf20Sopenharmony_ci{ 8148c2ecf20Sopenharmony_ci int rc; 8158c2ecf20Sopenharmony_ci struct list_head *head = &tl->tl_list; 8168c2ecf20Sopenharmony_ci struct cache_dfs_tgt *t; 8178c2ecf20Sopenharmony_ci struct dfs_cache_tgt_iterator *it, *nit; 8188c2ecf20Sopenharmony_ci 8198c2ecf20Sopenharmony_ci memset(tl, 0, sizeof(*tl)); 8208c2ecf20Sopenharmony_ci INIT_LIST_HEAD(head); 8218c2ecf20Sopenharmony_ci 8228c2ecf20Sopenharmony_ci list_for_each_entry(t, &ce->tlist, list) { 8238c2ecf20Sopenharmony_ci it = kzalloc(sizeof(*it), GFP_ATOMIC); 8248c2ecf20Sopenharmony_ci if (!it) { 8258c2ecf20Sopenharmony_ci rc = -ENOMEM; 8268c2ecf20Sopenharmony_ci goto err_free_it; 8278c2ecf20Sopenharmony_ci } 8288c2ecf20Sopenharmony_ci 8298c2ecf20Sopenharmony_ci it->it_name = kstrndup(t->name, strlen(t->name), GFP_ATOMIC); 8308c2ecf20Sopenharmony_ci if (!it->it_name) { 8318c2ecf20Sopenharmony_ci kfree(it); 8328c2ecf20Sopenharmony_ci rc = -ENOMEM; 8338c2ecf20Sopenharmony_ci goto err_free_it; 8348c2ecf20Sopenharmony_ci } 8358c2ecf20Sopenharmony_ci it->it_path_consumed = t->path_consumed; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci if (ce->tgthint == t) 8388c2ecf20Sopenharmony_ci list_add(&it->it_list, head); 8398c2ecf20Sopenharmony_ci else 8408c2ecf20Sopenharmony_ci list_add_tail(&it->it_list, head); 8418c2ecf20Sopenharmony_ci } 8428c2ecf20Sopenharmony_ci 8438c2ecf20Sopenharmony_ci tl->tl_numtgts = ce->numtgts; 8448c2ecf20Sopenharmony_ci 8458c2ecf20Sopenharmony_ci return 0; 8468c2ecf20Sopenharmony_ci 8478c2ecf20Sopenharmony_cierr_free_it: 8488c2ecf20Sopenharmony_ci list_for_each_entry_safe(it, nit, head, it_list) { 8498c2ecf20Sopenharmony_ci kfree(it->it_name); 8508c2ecf20Sopenharmony_ci kfree(it); 8518c2ecf20Sopenharmony_ci } 8528c2ecf20Sopenharmony_ci return rc; 8538c2ecf20Sopenharmony_ci} 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci/** 8568c2ecf20Sopenharmony_ci * dfs_cache_find - find a DFS cache entry 8578c2ecf20Sopenharmony_ci * 8588c2ecf20Sopenharmony_ci * If it doesn't find the cache entry, then it will get a DFS referral 8598c2ecf20Sopenharmony_ci * for @path and create a new entry. 8608c2ecf20Sopenharmony_ci * 8618c2ecf20Sopenharmony_ci * In case the cache entry exists but expired, it will get a DFS referral 8628c2ecf20Sopenharmony_ci * for @path and then update the respective cache entry. 8638c2ecf20Sopenharmony_ci * 8648c2ecf20Sopenharmony_ci * These parameters are passed down to the get_dfs_refer() call if it 8658c2ecf20Sopenharmony_ci * needs to be issued: 8668c2ecf20Sopenharmony_ci * @xid: syscall xid 8678c2ecf20Sopenharmony_ci * @ses: smb session to issue the request on 8688c2ecf20Sopenharmony_ci * @nls_codepage: charset conversion 8698c2ecf20Sopenharmony_ci * @remap: path character remapping type 8708c2ecf20Sopenharmony_ci * @path: path to lookup in DFS referral cache. 8718c2ecf20Sopenharmony_ci * 8728c2ecf20Sopenharmony_ci * @ref: when non-NULL, store single DFS referral result in it. 8738c2ecf20Sopenharmony_ci * @tgt_list: when non-NULL, store complete DFS target list in it. 8748c2ecf20Sopenharmony_ci * 8758c2ecf20Sopenharmony_ci * Return zero if the target was found, otherwise non-zero. 8768c2ecf20Sopenharmony_ci */ 8778c2ecf20Sopenharmony_ciint dfs_cache_find(const unsigned int xid, struct cifs_ses *ses, 8788c2ecf20Sopenharmony_ci const struct nls_table *nls_codepage, int remap, 8798c2ecf20Sopenharmony_ci const char *path, struct dfs_info3_param *ref, 8808c2ecf20Sopenharmony_ci struct dfs_cache_tgt_list *tgt_list) 8818c2ecf20Sopenharmony_ci{ 8828c2ecf20Sopenharmony_ci int rc; 8838c2ecf20Sopenharmony_ci char *npath; 8848c2ecf20Sopenharmony_ci struct cache_entry *ce; 8858c2ecf20Sopenharmony_ci 8868c2ecf20Sopenharmony_ci rc = get_normalized_path(path, &npath); 8878c2ecf20Sopenharmony_ci if (rc) 8888c2ecf20Sopenharmony_ci return rc; 8898c2ecf20Sopenharmony_ci 8908c2ecf20Sopenharmony_ci rc = __dfs_cache_find(xid, ses, nls_codepage, remap, npath, false); 8918c2ecf20Sopenharmony_ci if (rc) 8928c2ecf20Sopenharmony_ci goto out_free_path; 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci down_read(&htable_rw_lock); 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci ce = lookup_cache_entry(npath, NULL); 8978c2ecf20Sopenharmony_ci if (IS_ERR(ce)) { 8988c2ecf20Sopenharmony_ci up_read(&htable_rw_lock); 8998c2ecf20Sopenharmony_ci rc = PTR_ERR(ce); 9008c2ecf20Sopenharmony_ci goto out_free_path; 9018c2ecf20Sopenharmony_ci } 9028c2ecf20Sopenharmony_ci 9038c2ecf20Sopenharmony_ci if (ref) 9048c2ecf20Sopenharmony_ci rc = setup_referral(path, ce, ref, get_tgt_name(ce)); 9058c2ecf20Sopenharmony_ci else 9068c2ecf20Sopenharmony_ci rc = 0; 9078c2ecf20Sopenharmony_ci if (!rc && tgt_list) 9088c2ecf20Sopenharmony_ci rc = get_targets(ce, tgt_list); 9098c2ecf20Sopenharmony_ci 9108c2ecf20Sopenharmony_ci up_read(&htable_rw_lock); 9118c2ecf20Sopenharmony_ci 9128c2ecf20Sopenharmony_ciout_free_path: 9138c2ecf20Sopenharmony_ci free_normalized_path(path, npath); 9148c2ecf20Sopenharmony_ci return rc; 9158c2ecf20Sopenharmony_ci} 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci/** 9188c2ecf20Sopenharmony_ci * dfs_cache_noreq_find - find a DFS cache entry without sending any requests to 9198c2ecf20Sopenharmony_ci * the currently connected server. 9208c2ecf20Sopenharmony_ci * 9218c2ecf20Sopenharmony_ci * NOTE: This function will neither update a cache entry in case it was 9228c2ecf20Sopenharmony_ci * expired, nor create a new cache entry if @path hasn't been found. It heavily 9238c2ecf20Sopenharmony_ci * relies on an existing cache entry. 9248c2ecf20Sopenharmony_ci * 9258c2ecf20Sopenharmony_ci * @path: path to lookup in the DFS referral cache. 9268c2ecf20Sopenharmony_ci * @ref: when non-NULL, store single DFS referral result in it. 9278c2ecf20Sopenharmony_ci * @tgt_list: when non-NULL, store complete DFS target list in it. 9288c2ecf20Sopenharmony_ci * 9298c2ecf20Sopenharmony_ci * Return 0 if successful. 9308c2ecf20Sopenharmony_ci * Return -ENOENT if the entry was not found. 9318c2ecf20Sopenharmony_ci * Return non-zero for other errors. 9328c2ecf20Sopenharmony_ci */ 9338c2ecf20Sopenharmony_ciint dfs_cache_noreq_find(const char *path, struct dfs_info3_param *ref, 9348c2ecf20Sopenharmony_ci struct dfs_cache_tgt_list *tgt_list) 9358c2ecf20Sopenharmony_ci{ 9368c2ecf20Sopenharmony_ci int rc; 9378c2ecf20Sopenharmony_ci char *npath; 9388c2ecf20Sopenharmony_ci struct cache_entry *ce; 9398c2ecf20Sopenharmony_ci 9408c2ecf20Sopenharmony_ci rc = get_normalized_path(path, &npath); 9418c2ecf20Sopenharmony_ci if (rc) 9428c2ecf20Sopenharmony_ci return rc; 9438c2ecf20Sopenharmony_ci 9448c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: path: %s\n", __func__, npath); 9458c2ecf20Sopenharmony_ci 9468c2ecf20Sopenharmony_ci down_read(&htable_rw_lock); 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci ce = lookup_cache_entry(npath, NULL); 9498c2ecf20Sopenharmony_ci if (IS_ERR(ce)) { 9508c2ecf20Sopenharmony_ci rc = PTR_ERR(ce); 9518c2ecf20Sopenharmony_ci goto out_unlock; 9528c2ecf20Sopenharmony_ci } 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci if (ref) 9558c2ecf20Sopenharmony_ci rc = setup_referral(path, ce, ref, get_tgt_name(ce)); 9568c2ecf20Sopenharmony_ci else 9578c2ecf20Sopenharmony_ci rc = 0; 9588c2ecf20Sopenharmony_ci if (!rc && tgt_list) 9598c2ecf20Sopenharmony_ci rc = get_targets(ce, tgt_list); 9608c2ecf20Sopenharmony_ci 9618c2ecf20Sopenharmony_ciout_unlock: 9628c2ecf20Sopenharmony_ci up_read(&htable_rw_lock); 9638c2ecf20Sopenharmony_ci free_normalized_path(path, npath); 9648c2ecf20Sopenharmony_ci 9658c2ecf20Sopenharmony_ci return rc; 9668c2ecf20Sopenharmony_ci} 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci/** 9698c2ecf20Sopenharmony_ci * dfs_cache_update_tgthint - update target hint of a DFS cache entry 9708c2ecf20Sopenharmony_ci * 9718c2ecf20Sopenharmony_ci * If it doesn't find the cache entry, then it will get a DFS referral for @path 9728c2ecf20Sopenharmony_ci * and create a new entry. 9738c2ecf20Sopenharmony_ci * 9748c2ecf20Sopenharmony_ci * In case the cache entry exists but expired, it will get a DFS referral 9758c2ecf20Sopenharmony_ci * for @path and then update the respective cache entry. 9768c2ecf20Sopenharmony_ci * 9778c2ecf20Sopenharmony_ci * @xid: syscall id 9788c2ecf20Sopenharmony_ci * @ses: smb session 9798c2ecf20Sopenharmony_ci * @nls_codepage: charset conversion 9808c2ecf20Sopenharmony_ci * @remap: type of character remapping for paths 9818c2ecf20Sopenharmony_ci * @path: path to lookup in DFS referral cache. 9828c2ecf20Sopenharmony_ci * @it: DFS target iterator 9838c2ecf20Sopenharmony_ci * 9848c2ecf20Sopenharmony_ci * Return zero if the target hint was updated successfully, otherwise non-zero. 9858c2ecf20Sopenharmony_ci */ 9868c2ecf20Sopenharmony_ciint dfs_cache_update_tgthint(const unsigned int xid, struct cifs_ses *ses, 9878c2ecf20Sopenharmony_ci const struct nls_table *nls_codepage, int remap, 9888c2ecf20Sopenharmony_ci const char *path, 9898c2ecf20Sopenharmony_ci const struct dfs_cache_tgt_iterator *it) 9908c2ecf20Sopenharmony_ci{ 9918c2ecf20Sopenharmony_ci int rc; 9928c2ecf20Sopenharmony_ci char *npath; 9938c2ecf20Sopenharmony_ci struct cache_entry *ce; 9948c2ecf20Sopenharmony_ci struct cache_dfs_tgt *t; 9958c2ecf20Sopenharmony_ci 9968c2ecf20Sopenharmony_ci rc = get_normalized_path(path, &npath); 9978c2ecf20Sopenharmony_ci if (rc) 9988c2ecf20Sopenharmony_ci return rc; 9998c2ecf20Sopenharmony_ci 10008c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: update target hint - path: %s\n", __func__, npath); 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci rc = __dfs_cache_find(xid, ses, nls_codepage, remap, npath, false); 10038c2ecf20Sopenharmony_ci if (rc) 10048c2ecf20Sopenharmony_ci goto out_free_path; 10058c2ecf20Sopenharmony_ci 10068c2ecf20Sopenharmony_ci down_write(&htable_rw_lock); 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_ci ce = lookup_cache_entry(npath, NULL); 10098c2ecf20Sopenharmony_ci if (IS_ERR(ce)) { 10108c2ecf20Sopenharmony_ci rc = PTR_ERR(ce); 10118c2ecf20Sopenharmony_ci goto out_unlock; 10128c2ecf20Sopenharmony_ci } 10138c2ecf20Sopenharmony_ci 10148c2ecf20Sopenharmony_ci t = ce->tgthint; 10158c2ecf20Sopenharmony_ci 10168c2ecf20Sopenharmony_ci if (likely(!strcasecmp(it->it_name, t->name))) 10178c2ecf20Sopenharmony_ci goto out_unlock; 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci list_for_each_entry(t, &ce->tlist, list) { 10208c2ecf20Sopenharmony_ci if (!strcasecmp(t->name, it->it_name)) { 10218c2ecf20Sopenharmony_ci ce->tgthint = t; 10228c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: new target hint: %s\n", __func__, 10238c2ecf20Sopenharmony_ci it->it_name); 10248c2ecf20Sopenharmony_ci break; 10258c2ecf20Sopenharmony_ci } 10268c2ecf20Sopenharmony_ci } 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ciout_unlock: 10298c2ecf20Sopenharmony_ci up_write(&htable_rw_lock); 10308c2ecf20Sopenharmony_ciout_free_path: 10318c2ecf20Sopenharmony_ci free_normalized_path(path, npath); 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci return rc; 10348c2ecf20Sopenharmony_ci} 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci/** 10378c2ecf20Sopenharmony_ci * dfs_cache_noreq_update_tgthint - update target hint of a DFS cache entry 10388c2ecf20Sopenharmony_ci * without sending any requests to the currently connected server. 10398c2ecf20Sopenharmony_ci * 10408c2ecf20Sopenharmony_ci * NOTE: This function will neither update a cache entry in case it was 10418c2ecf20Sopenharmony_ci * expired, nor create a new cache entry if @path hasn't been found. It heavily 10428c2ecf20Sopenharmony_ci * relies on an existing cache entry. 10438c2ecf20Sopenharmony_ci * 10448c2ecf20Sopenharmony_ci * @path: path to lookup in DFS referral cache. 10458c2ecf20Sopenharmony_ci * @it: target iterator which contains the target hint to update the cache 10468c2ecf20Sopenharmony_ci * entry with. 10478c2ecf20Sopenharmony_ci * 10488c2ecf20Sopenharmony_ci * Return zero if the target hint was updated successfully, otherwise non-zero. 10498c2ecf20Sopenharmony_ci */ 10508c2ecf20Sopenharmony_ciint dfs_cache_noreq_update_tgthint(const char *path, 10518c2ecf20Sopenharmony_ci const struct dfs_cache_tgt_iterator *it) 10528c2ecf20Sopenharmony_ci{ 10538c2ecf20Sopenharmony_ci int rc; 10548c2ecf20Sopenharmony_ci char *npath; 10558c2ecf20Sopenharmony_ci struct cache_entry *ce; 10568c2ecf20Sopenharmony_ci struct cache_dfs_tgt *t; 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci if (!it) 10598c2ecf20Sopenharmony_ci return -EINVAL; 10608c2ecf20Sopenharmony_ci 10618c2ecf20Sopenharmony_ci rc = get_normalized_path(path, &npath); 10628c2ecf20Sopenharmony_ci if (rc) 10638c2ecf20Sopenharmony_ci return rc; 10648c2ecf20Sopenharmony_ci 10658c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: path: %s\n", __func__, npath); 10668c2ecf20Sopenharmony_ci 10678c2ecf20Sopenharmony_ci down_write(&htable_rw_lock); 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci ce = lookup_cache_entry(npath, NULL); 10708c2ecf20Sopenharmony_ci if (IS_ERR(ce)) { 10718c2ecf20Sopenharmony_ci rc = PTR_ERR(ce); 10728c2ecf20Sopenharmony_ci goto out_unlock; 10738c2ecf20Sopenharmony_ci } 10748c2ecf20Sopenharmony_ci 10758c2ecf20Sopenharmony_ci rc = 0; 10768c2ecf20Sopenharmony_ci t = ce->tgthint; 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci if (unlikely(!strcasecmp(it->it_name, t->name))) 10798c2ecf20Sopenharmony_ci goto out_unlock; 10808c2ecf20Sopenharmony_ci 10818c2ecf20Sopenharmony_ci list_for_each_entry(t, &ce->tlist, list) { 10828c2ecf20Sopenharmony_ci if (!strcasecmp(t->name, it->it_name)) { 10838c2ecf20Sopenharmony_ci ce->tgthint = t; 10848c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: new target hint: %s\n", __func__, 10858c2ecf20Sopenharmony_ci it->it_name); 10868c2ecf20Sopenharmony_ci break; 10878c2ecf20Sopenharmony_ci } 10888c2ecf20Sopenharmony_ci } 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ciout_unlock: 10918c2ecf20Sopenharmony_ci up_write(&htable_rw_lock); 10928c2ecf20Sopenharmony_ci free_normalized_path(path, npath); 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ci return rc; 10958c2ecf20Sopenharmony_ci} 10968c2ecf20Sopenharmony_ci 10978c2ecf20Sopenharmony_ci/** 10988c2ecf20Sopenharmony_ci * dfs_cache_get_tgt_referral - returns a DFS referral (@ref) from a given 10998c2ecf20Sopenharmony_ci * target iterator (@it). 11008c2ecf20Sopenharmony_ci * 11018c2ecf20Sopenharmony_ci * @path: path to lookup in DFS referral cache. 11028c2ecf20Sopenharmony_ci * @it: DFS target iterator. 11038c2ecf20Sopenharmony_ci * @ref: DFS referral pointer to set up the gathered information. 11048c2ecf20Sopenharmony_ci * 11058c2ecf20Sopenharmony_ci * Return zero if the DFS referral was set up correctly, otherwise non-zero. 11068c2ecf20Sopenharmony_ci */ 11078c2ecf20Sopenharmony_ciint dfs_cache_get_tgt_referral(const char *path, 11088c2ecf20Sopenharmony_ci const struct dfs_cache_tgt_iterator *it, 11098c2ecf20Sopenharmony_ci struct dfs_info3_param *ref) 11108c2ecf20Sopenharmony_ci{ 11118c2ecf20Sopenharmony_ci int rc; 11128c2ecf20Sopenharmony_ci char *npath; 11138c2ecf20Sopenharmony_ci struct cache_entry *ce; 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_ci if (!it || !ref) 11168c2ecf20Sopenharmony_ci return -EINVAL; 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci rc = get_normalized_path(path, &npath); 11198c2ecf20Sopenharmony_ci if (rc) 11208c2ecf20Sopenharmony_ci return rc; 11218c2ecf20Sopenharmony_ci 11228c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: path: %s\n", __func__, npath); 11238c2ecf20Sopenharmony_ci 11248c2ecf20Sopenharmony_ci down_read(&htable_rw_lock); 11258c2ecf20Sopenharmony_ci 11268c2ecf20Sopenharmony_ci ce = lookup_cache_entry(npath, NULL); 11278c2ecf20Sopenharmony_ci if (IS_ERR(ce)) { 11288c2ecf20Sopenharmony_ci rc = PTR_ERR(ce); 11298c2ecf20Sopenharmony_ci goto out_unlock; 11308c2ecf20Sopenharmony_ci } 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: target name: %s\n", __func__, it->it_name); 11338c2ecf20Sopenharmony_ci 11348c2ecf20Sopenharmony_ci rc = setup_referral(path, ce, ref, it->it_name); 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ciout_unlock: 11378c2ecf20Sopenharmony_ci up_read(&htable_rw_lock); 11388c2ecf20Sopenharmony_ci free_normalized_path(path, npath); 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci return rc; 11418c2ecf20Sopenharmony_ci} 11428c2ecf20Sopenharmony_ci 11438c2ecf20Sopenharmony_cistatic int dup_vol(struct smb_vol *vol, struct smb_vol *new) 11448c2ecf20Sopenharmony_ci{ 11458c2ecf20Sopenharmony_ci memcpy(new, vol, sizeof(*new)); 11468c2ecf20Sopenharmony_ci 11478c2ecf20Sopenharmony_ci if (vol->username) { 11488c2ecf20Sopenharmony_ci new->username = kstrndup(vol->username, strlen(vol->username), 11498c2ecf20Sopenharmony_ci GFP_KERNEL); 11508c2ecf20Sopenharmony_ci if (!new->username) 11518c2ecf20Sopenharmony_ci return -ENOMEM; 11528c2ecf20Sopenharmony_ci } 11538c2ecf20Sopenharmony_ci if (vol->password) { 11548c2ecf20Sopenharmony_ci new->password = kstrndup(vol->password, strlen(vol->password), 11558c2ecf20Sopenharmony_ci GFP_KERNEL); 11568c2ecf20Sopenharmony_ci if (!new->password) 11578c2ecf20Sopenharmony_ci goto err_free_username; 11588c2ecf20Sopenharmony_ci } 11598c2ecf20Sopenharmony_ci if (vol->UNC) { 11608c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: vol->UNC: %s\n", __func__, vol->UNC); 11618c2ecf20Sopenharmony_ci new->UNC = kstrndup(vol->UNC, strlen(vol->UNC), GFP_KERNEL); 11628c2ecf20Sopenharmony_ci if (!new->UNC) 11638c2ecf20Sopenharmony_ci goto err_free_password; 11648c2ecf20Sopenharmony_ci } 11658c2ecf20Sopenharmony_ci if (vol->domainname) { 11668c2ecf20Sopenharmony_ci new->domainname = kstrndup(vol->domainname, 11678c2ecf20Sopenharmony_ci strlen(vol->domainname), GFP_KERNEL); 11688c2ecf20Sopenharmony_ci if (!new->domainname) 11698c2ecf20Sopenharmony_ci goto err_free_unc; 11708c2ecf20Sopenharmony_ci } 11718c2ecf20Sopenharmony_ci if (vol->iocharset) { 11728c2ecf20Sopenharmony_ci new->iocharset = kstrndup(vol->iocharset, 11738c2ecf20Sopenharmony_ci strlen(vol->iocharset), GFP_KERNEL); 11748c2ecf20Sopenharmony_ci if (!new->iocharset) 11758c2ecf20Sopenharmony_ci goto err_free_domainname; 11768c2ecf20Sopenharmony_ci } 11778c2ecf20Sopenharmony_ci if (vol->prepath) { 11788c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: vol->prepath: %s\n", __func__, vol->prepath); 11798c2ecf20Sopenharmony_ci new->prepath = kstrndup(vol->prepath, strlen(vol->prepath), 11808c2ecf20Sopenharmony_ci GFP_KERNEL); 11818c2ecf20Sopenharmony_ci if (!new->prepath) 11828c2ecf20Sopenharmony_ci goto err_free_iocharset; 11838c2ecf20Sopenharmony_ci } 11848c2ecf20Sopenharmony_ci 11858c2ecf20Sopenharmony_ci return 0; 11868c2ecf20Sopenharmony_ci 11878c2ecf20Sopenharmony_cierr_free_iocharset: 11888c2ecf20Sopenharmony_ci kfree(new->iocharset); 11898c2ecf20Sopenharmony_cierr_free_domainname: 11908c2ecf20Sopenharmony_ci kfree(new->domainname); 11918c2ecf20Sopenharmony_cierr_free_unc: 11928c2ecf20Sopenharmony_ci kfree(new->UNC); 11938c2ecf20Sopenharmony_cierr_free_password: 11948c2ecf20Sopenharmony_ci kfree_sensitive(new->password); 11958c2ecf20Sopenharmony_cierr_free_username: 11968c2ecf20Sopenharmony_ci kfree(new->username); 11978c2ecf20Sopenharmony_ci kfree(new); 11988c2ecf20Sopenharmony_ci return -ENOMEM; 11998c2ecf20Sopenharmony_ci} 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci/** 12028c2ecf20Sopenharmony_ci * dfs_cache_add_vol - add a cifs volume during mount() that will be handled by 12038c2ecf20Sopenharmony_ci * DFS cache refresh worker. 12048c2ecf20Sopenharmony_ci * 12058c2ecf20Sopenharmony_ci * @mntdata: mount data. 12068c2ecf20Sopenharmony_ci * @vol: cifs volume. 12078c2ecf20Sopenharmony_ci * @fullpath: origin full path. 12088c2ecf20Sopenharmony_ci * 12098c2ecf20Sopenharmony_ci * Return zero if volume was set up correctly, otherwise non-zero. 12108c2ecf20Sopenharmony_ci */ 12118c2ecf20Sopenharmony_ciint dfs_cache_add_vol(char *mntdata, struct smb_vol *vol, const char *fullpath) 12128c2ecf20Sopenharmony_ci{ 12138c2ecf20Sopenharmony_ci int rc; 12148c2ecf20Sopenharmony_ci struct vol_info *vi; 12158c2ecf20Sopenharmony_ci 12168c2ecf20Sopenharmony_ci if (!vol || !fullpath || !mntdata) 12178c2ecf20Sopenharmony_ci return -EINVAL; 12188c2ecf20Sopenharmony_ci 12198c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: fullpath: %s\n", __func__, fullpath); 12208c2ecf20Sopenharmony_ci 12218c2ecf20Sopenharmony_ci vi = kzalloc(sizeof(*vi), GFP_KERNEL); 12228c2ecf20Sopenharmony_ci if (!vi) 12238c2ecf20Sopenharmony_ci return -ENOMEM; 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_ci vi->fullpath = kstrndup(fullpath, strlen(fullpath), GFP_KERNEL); 12268c2ecf20Sopenharmony_ci if (!vi->fullpath) { 12278c2ecf20Sopenharmony_ci rc = -ENOMEM; 12288c2ecf20Sopenharmony_ci goto err_free_vi; 12298c2ecf20Sopenharmony_ci } 12308c2ecf20Sopenharmony_ci 12318c2ecf20Sopenharmony_ci rc = dup_vol(vol, &vi->smb_vol); 12328c2ecf20Sopenharmony_ci if (rc) 12338c2ecf20Sopenharmony_ci goto err_free_fullpath; 12348c2ecf20Sopenharmony_ci 12358c2ecf20Sopenharmony_ci vi->mntdata = mntdata; 12368c2ecf20Sopenharmony_ci spin_lock_init(&vi->smb_vol_lock); 12378c2ecf20Sopenharmony_ci kref_init(&vi->refcnt); 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci spin_lock(&vol_list_lock); 12408c2ecf20Sopenharmony_ci list_add_tail(&vi->list, &vol_list); 12418c2ecf20Sopenharmony_ci spin_unlock(&vol_list_lock); 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci return 0; 12448c2ecf20Sopenharmony_ci 12458c2ecf20Sopenharmony_cierr_free_fullpath: 12468c2ecf20Sopenharmony_ci kfree(vi->fullpath); 12478c2ecf20Sopenharmony_cierr_free_vi: 12488c2ecf20Sopenharmony_ci kfree(vi); 12498c2ecf20Sopenharmony_ci return rc; 12508c2ecf20Sopenharmony_ci} 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci/* Must be called with vol_list_lock held */ 12538c2ecf20Sopenharmony_cistatic struct vol_info *find_vol(const char *fullpath) 12548c2ecf20Sopenharmony_ci{ 12558c2ecf20Sopenharmony_ci struct vol_info *vi; 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci list_for_each_entry(vi, &vol_list, list) { 12588c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: vi->fullpath: %s\n", __func__, vi->fullpath); 12598c2ecf20Sopenharmony_ci if (!strcasecmp(vi->fullpath, fullpath)) 12608c2ecf20Sopenharmony_ci return vi; 12618c2ecf20Sopenharmony_ci } 12628c2ecf20Sopenharmony_ci return ERR_PTR(-ENOENT); 12638c2ecf20Sopenharmony_ci} 12648c2ecf20Sopenharmony_ci 12658c2ecf20Sopenharmony_ci/** 12668c2ecf20Sopenharmony_ci * dfs_cache_update_vol - update vol info in DFS cache after failover 12678c2ecf20Sopenharmony_ci * 12688c2ecf20Sopenharmony_ci * @fullpath: fullpath to look up in volume list. 12698c2ecf20Sopenharmony_ci * @server: TCP ses pointer. 12708c2ecf20Sopenharmony_ci * 12718c2ecf20Sopenharmony_ci * Return zero if volume was updated, otherwise non-zero. 12728c2ecf20Sopenharmony_ci */ 12738c2ecf20Sopenharmony_ciint dfs_cache_update_vol(const char *fullpath, struct TCP_Server_Info *server) 12748c2ecf20Sopenharmony_ci{ 12758c2ecf20Sopenharmony_ci struct vol_info *vi; 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci if (!fullpath || !server) 12788c2ecf20Sopenharmony_ci return -EINVAL; 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: fullpath: %s\n", __func__, fullpath); 12818c2ecf20Sopenharmony_ci 12828c2ecf20Sopenharmony_ci spin_lock(&vol_list_lock); 12838c2ecf20Sopenharmony_ci vi = find_vol(fullpath); 12848c2ecf20Sopenharmony_ci if (IS_ERR(vi)) { 12858c2ecf20Sopenharmony_ci spin_unlock(&vol_list_lock); 12868c2ecf20Sopenharmony_ci return PTR_ERR(vi); 12878c2ecf20Sopenharmony_ci } 12888c2ecf20Sopenharmony_ci kref_get(&vi->refcnt); 12898c2ecf20Sopenharmony_ci spin_unlock(&vol_list_lock); 12908c2ecf20Sopenharmony_ci 12918c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: updating volume info\n", __func__); 12928c2ecf20Sopenharmony_ci spin_lock(&vi->smb_vol_lock); 12938c2ecf20Sopenharmony_ci memcpy(&vi->smb_vol.dstaddr, &server->dstaddr, 12948c2ecf20Sopenharmony_ci sizeof(vi->smb_vol.dstaddr)); 12958c2ecf20Sopenharmony_ci spin_unlock(&vi->smb_vol_lock); 12968c2ecf20Sopenharmony_ci 12978c2ecf20Sopenharmony_ci kref_put(&vi->refcnt, vol_release); 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci return 0; 13008c2ecf20Sopenharmony_ci} 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci/** 13038c2ecf20Sopenharmony_ci * dfs_cache_del_vol - remove volume info in DFS cache during umount() 13048c2ecf20Sopenharmony_ci * 13058c2ecf20Sopenharmony_ci * @fullpath: fullpath to look up in volume list. 13068c2ecf20Sopenharmony_ci */ 13078c2ecf20Sopenharmony_civoid dfs_cache_del_vol(const char *fullpath) 13088c2ecf20Sopenharmony_ci{ 13098c2ecf20Sopenharmony_ci struct vol_info *vi; 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci if (!fullpath || !*fullpath) 13128c2ecf20Sopenharmony_ci return; 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci cifs_dbg(FYI, "%s: fullpath: %s\n", __func__, fullpath); 13158c2ecf20Sopenharmony_ci 13168c2ecf20Sopenharmony_ci spin_lock(&vol_list_lock); 13178c2ecf20Sopenharmony_ci vi = find_vol(fullpath); 13188c2ecf20Sopenharmony_ci spin_unlock(&vol_list_lock); 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci if (!IS_ERR(vi)) 13218c2ecf20Sopenharmony_ci kref_put(&vi->refcnt, vol_release); 13228c2ecf20Sopenharmony_ci} 13238c2ecf20Sopenharmony_ci 13248c2ecf20Sopenharmony_ci/** 13258c2ecf20Sopenharmony_ci * dfs_cache_get_tgt_share - parse a DFS target 13268c2ecf20Sopenharmony_ci * 13278c2ecf20Sopenharmony_ci * @path: DFS full path 13288c2ecf20Sopenharmony_ci * @it: DFS target iterator. 13298c2ecf20Sopenharmony_ci * @share: tree name. 13308c2ecf20Sopenharmony_ci * @prefix: prefix path. 13318c2ecf20Sopenharmony_ci * 13328c2ecf20Sopenharmony_ci * Return zero if target was parsed correctly, otherwise non-zero. 13338c2ecf20Sopenharmony_ci */ 13348c2ecf20Sopenharmony_ciint dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it, 13358c2ecf20Sopenharmony_ci char **share, char **prefix) 13368c2ecf20Sopenharmony_ci{ 13378c2ecf20Sopenharmony_ci char *s, sep, *p; 13388c2ecf20Sopenharmony_ci size_t len; 13398c2ecf20Sopenharmony_ci size_t plen1, plen2; 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci if (!it || !path || !share || !prefix || strlen(path) < it->it_path_consumed) 13428c2ecf20Sopenharmony_ci return -EINVAL; 13438c2ecf20Sopenharmony_ci 13448c2ecf20Sopenharmony_ci *share = NULL; 13458c2ecf20Sopenharmony_ci *prefix = NULL; 13468c2ecf20Sopenharmony_ci 13478c2ecf20Sopenharmony_ci sep = it->it_name[0]; 13488c2ecf20Sopenharmony_ci if (sep != '\\' && sep != '/') 13498c2ecf20Sopenharmony_ci return -EINVAL; 13508c2ecf20Sopenharmony_ci 13518c2ecf20Sopenharmony_ci s = strchr(it->it_name + 1, sep); 13528c2ecf20Sopenharmony_ci if (!s) 13538c2ecf20Sopenharmony_ci return -EINVAL; 13548c2ecf20Sopenharmony_ci 13558c2ecf20Sopenharmony_ci /* point to prefix in target node */ 13568c2ecf20Sopenharmony_ci s = strchrnul(s + 1, sep); 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ci /* extract target share */ 13598c2ecf20Sopenharmony_ci *share = kstrndup(it->it_name, s - it->it_name, GFP_KERNEL); 13608c2ecf20Sopenharmony_ci if (!*share) 13618c2ecf20Sopenharmony_ci return -ENOMEM; 13628c2ecf20Sopenharmony_ci 13638c2ecf20Sopenharmony_ci /* skip separator */ 13648c2ecf20Sopenharmony_ci if (*s) 13658c2ecf20Sopenharmony_ci s++; 13668c2ecf20Sopenharmony_ci /* point to prefix in DFS path */ 13678c2ecf20Sopenharmony_ci p = path + it->it_path_consumed; 13688c2ecf20Sopenharmony_ci if (*p == sep) 13698c2ecf20Sopenharmony_ci p++; 13708c2ecf20Sopenharmony_ci 13718c2ecf20Sopenharmony_ci /* merge prefix paths from DFS path and target node */ 13728c2ecf20Sopenharmony_ci plen1 = it->it_name + strlen(it->it_name) - s; 13738c2ecf20Sopenharmony_ci plen2 = path + strlen(path) - p; 13748c2ecf20Sopenharmony_ci if (plen1 || plen2) { 13758c2ecf20Sopenharmony_ci len = plen1 + plen2 + 2; 13768c2ecf20Sopenharmony_ci *prefix = kmalloc(len, GFP_KERNEL); 13778c2ecf20Sopenharmony_ci if (!*prefix) { 13788c2ecf20Sopenharmony_ci kfree(*share); 13798c2ecf20Sopenharmony_ci *share = NULL; 13808c2ecf20Sopenharmony_ci return -ENOMEM; 13818c2ecf20Sopenharmony_ci } 13828c2ecf20Sopenharmony_ci if (plen1) 13838c2ecf20Sopenharmony_ci scnprintf(*prefix, len, "%.*s%c%.*s", (int)plen1, s, sep, (int)plen2, p); 13848c2ecf20Sopenharmony_ci else 13858c2ecf20Sopenharmony_ci strscpy(*prefix, p, len); 13868c2ecf20Sopenharmony_ci } 13878c2ecf20Sopenharmony_ci return 0; 13888c2ecf20Sopenharmony_ci} 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci/* Get all tcons that are within a DFS namespace and can be refreshed */ 13918c2ecf20Sopenharmony_cistatic void get_tcons(struct TCP_Server_Info *server, struct list_head *head) 13928c2ecf20Sopenharmony_ci{ 13938c2ecf20Sopenharmony_ci struct cifs_ses *ses; 13948c2ecf20Sopenharmony_ci struct cifs_tcon *tcon; 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci INIT_LIST_HEAD(head); 13978c2ecf20Sopenharmony_ci 13988c2ecf20Sopenharmony_ci spin_lock(&cifs_tcp_ses_lock); 13998c2ecf20Sopenharmony_ci list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) { 14008c2ecf20Sopenharmony_ci list_for_each_entry(tcon, &ses->tcon_list, tcon_list) { 14018c2ecf20Sopenharmony_ci if (!tcon->need_reconnect && !tcon->need_reopen_files && 14028c2ecf20Sopenharmony_ci tcon->dfs_path) { 14038c2ecf20Sopenharmony_ci tcon->tc_count++; 14048c2ecf20Sopenharmony_ci list_add_tail(&tcon->ulist, head); 14058c2ecf20Sopenharmony_ci } 14068c2ecf20Sopenharmony_ci } 14078c2ecf20Sopenharmony_ci if (ses->tcon_ipc && !ses->tcon_ipc->need_reconnect && 14088c2ecf20Sopenharmony_ci ses->tcon_ipc->dfs_path) { 14098c2ecf20Sopenharmony_ci list_add_tail(&ses->tcon_ipc->ulist, head); 14108c2ecf20Sopenharmony_ci } 14118c2ecf20Sopenharmony_ci } 14128c2ecf20Sopenharmony_ci spin_unlock(&cifs_tcp_ses_lock); 14138c2ecf20Sopenharmony_ci} 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_cistatic bool is_dfs_link(const char *path) 14168c2ecf20Sopenharmony_ci{ 14178c2ecf20Sopenharmony_ci char *s; 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci s = strchr(path + 1, '\\'); 14208c2ecf20Sopenharmony_ci if (!s) 14218c2ecf20Sopenharmony_ci return false; 14228c2ecf20Sopenharmony_ci return !!strchr(s + 1, '\\'); 14238c2ecf20Sopenharmony_ci} 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_cistatic char *get_dfs_root(const char *path) 14268c2ecf20Sopenharmony_ci{ 14278c2ecf20Sopenharmony_ci char *s, *npath; 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci s = strchr(path + 1, '\\'); 14308c2ecf20Sopenharmony_ci if (!s) 14318c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 14328c2ecf20Sopenharmony_ci 14338c2ecf20Sopenharmony_ci s = strchr(s + 1, '\\'); 14348c2ecf20Sopenharmony_ci if (!s) 14358c2ecf20Sopenharmony_ci return ERR_PTR(-EINVAL); 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci npath = kstrndup(path, s - path, GFP_KERNEL); 14388c2ecf20Sopenharmony_ci if (!npath) 14398c2ecf20Sopenharmony_ci return ERR_PTR(-ENOMEM); 14408c2ecf20Sopenharmony_ci 14418c2ecf20Sopenharmony_ci return npath; 14428c2ecf20Sopenharmony_ci} 14438c2ecf20Sopenharmony_ci 14448c2ecf20Sopenharmony_cistatic inline void put_tcp_server(struct TCP_Server_Info *server) 14458c2ecf20Sopenharmony_ci{ 14468c2ecf20Sopenharmony_ci cifs_put_tcp_session(server, 0); 14478c2ecf20Sopenharmony_ci} 14488c2ecf20Sopenharmony_ci 14498c2ecf20Sopenharmony_cistatic struct TCP_Server_Info *get_tcp_server(struct smb_vol *vol) 14508c2ecf20Sopenharmony_ci{ 14518c2ecf20Sopenharmony_ci struct TCP_Server_Info *server; 14528c2ecf20Sopenharmony_ci 14538c2ecf20Sopenharmony_ci server = cifs_find_tcp_session(vol); 14548c2ecf20Sopenharmony_ci if (IS_ERR_OR_NULL(server)) 14558c2ecf20Sopenharmony_ci return NULL; 14568c2ecf20Sopenharmony_ci 14578c2ecf20Sopenharmony_ci spin_lock(&GlobalMid_Lock); 14588c2ecf20Sopenharmony_ci if (server->tcpStatus != CifsGood) { 14598c2ecf20Sopenharmony_ci spin_unlock(&GlobalMid_Lock); 14608c2ecf20Sopenharmony_ci put_tcp_server(server); 14618c2ecf20Sopenharmony_ci return NULL; 14628c2ecf20Sopenharmony_ci } 14638c2ecf20Sopenharmony_ci spin_unlock(&GlobalMid_Lock); 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_ci return server; 14668c2ecf20Sopenharmony_ci} 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci/* Find root SMB session out of a DFS link path */ 14698c2ecf20Sopenharmony_cistatic struct cifs_ses *find_root_ses(struct vol_info *vi, 14708c2ecf20Sopenharmony_ci struct cifs_tcon *tcon, 14718c2ecf20Sopenharmony_ci const char *path) 14728c2ecf20Sopenharmony_ci{ 14738c2ecf20Sopenharmony_ci char *rpath; 14748c2ecf20Sopenharmony_ci int rc; 14758c2ecf20Sopenharmony_ci struct cache_entry *ce; 14768c2ecf20Sopenharmony_ci struct dfs_info3_param ref = {0}; 14778c2ecf20Sopenharmony_ci char *mdata = NULL, *devname = NULL; 14788c2ecf20Sopenharmony_ci struct TCP_Server_Info *server; 14798c2ecf20Sopenharmony_ci struct cifs_ses *ses; 14808c2ecf20Sopenharmony_ci struct smb_vol vol = {NULL}; 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci rpath = get_dfs_root(path); 14838c2ecf20Sopenharmony_ci if (IS_ERR(rpath)) 14848c2ecf20Sopenharmony_ci return ERR_CAST(rpath); 14858c2ecf20Sopenharmony_ci 14868c2ecf20Sopenharmony_ci down_read(&htable_rw_lock); 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_ci ce = lookup_cache_entry(rpath, NULL); 14898c2ecf20Sopenharmony_ci if (IS_ERR(ce)) { 14908c2ecf20Sopenharmony_ci up_read(&htable_rw_lock); 14918c2ecf20Sopenharmony_ci ses = ERR_CAST(ce); 14928c2ecf20Sopenharmony_ci goto out; 14938c2ecf20Sopenharmony_ci } 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_ci rc = setup_referral(path, ce, &ref, get_tgt_name(ce)); 14968c2ecf20Sopenharmony_ci if (rc) { 14978c2ecf20Sopenharmony_ci up_read(&htable_rw_lock); 14988c2ecf20Sopenharmony_ci ses = ERR_PTR(rc); 14998c2ecf20Sopenharmony_ci goto out; 15008c2ecf20Sopenharmony_ci } 15018c2ecf20Sopenharmony_ci 15028c2ecf20Sopenharmony_ci up_read(&htable_rw_lock); 15038c2ecf20Sopenharmony_ci 15048c2ecf20Sopenharmony_ci mdata = cifs_compose_mount_options(vi->mntdata, rpath, &ref, 15058c2ecf20Sopenharmony_ci &devname); 15068c2ecf20Sopenharmony_ci free_dfs_info_param(&ref); 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci if (IS_ERR(mdata)) { 15098c2ecf20Sopenharmony_ci ses = ERR_CAST(mdata); 15108c2ecf20Sopenharmony_ci mdata = NULL; 15118c2ecf20Sopenharmony_ci goto out; 15128c2ecf20Sopenharmony_ci } 15138c2ecf20Sopenharmony_ci 15148c2ecf20Sopenharmony_ci rc = cifs_setup_volume_info(&vol, mdata, devname, false); 15158c2ecf20Sopenharmony_ci kfree(devname); 15168c2ecf20Sopenharmony_ci 15178c2ecf20Sopenharmony_ci if (rc) { 15188c2ecf20Sopenharmony_ci ses = ERR_PTR(rc); 15198c2ecf20Sopenharmony_ci goto out; 15208c2ecf20Sopenharmony_ci } 15218c2ecf20Sopenharmony_ci 15228c2ecf20Sopenharmony_ci server = get_tcp_server(&vol); 15238c2ecf20Sopenharmony_ci if (!server) { 15248c2ecf20Sopenharmony_ci ses = ERR_PTR(-EHOSTDOWN); 15258c2ecf20Sopenharmony_ci goto out; 15268c2ecf20Sopenharmony_ci } 15278c2ecf20Sopenharmony_ci 15288c2ecf20Sopenharmony_ci ses = cifs_get_smb_ses(server, &vol); 15298c2ecf20Sopenharmony_ci 15308c2ecf20Sopenharmony_ciout: 15318c2ecf20Sopenharmony_ci cifs_cleanup_volume_info_contents(&vol); 15328c2ecf20Sopenharmony_ci kfree(mdata); 15338c2ecf20Sopenharmony_ci kfree(rpath); 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_ci return ses; 15368c2ecf20Sopenharmony_ci} 15378c2ecf20Sopenharmony_ci 15388c2ecf20Sopenharmony_ci/* Refresh DFS cache entry from a given tcon */ 15398c2ecf20Sopenharmony_cistatic int refresh_tcon(struct vol_info *vi, struct cifs_tcon *tcon) 15408c2ecf20Sopenharmony_ci{ 15418c2ecf20Sopenharmony_ci int rc = 0; 15428c2ecf20Sopenharmony_ci unsigned int xid; 15438c2ecf20Sopenharmony_ci char *path, *npath; 15448c2ecf20Sopenharmony_ci struct cache_entry *ce; 15458c2ecf20Sopenharmony_ci struct cifs_ses *root_ses = NULL, *ses; 15468c2ecf20Sopenharmony_ci struct dfs_info3_param *refs = NULL; 15478c2ecf20Sopenharmony_ci int numrefs = 0; 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_ci xid = get_xid(); 15508c2ecf20Sopenharmony_ci 15518c2ecf20Sopenharmony_ci path = tcon->dfs_path + 1; 15528c2ecf20Sopenharmony_ci 15538c2ecf20Sopenharmony_ci rc = get_normalized_path(path, &npath); 15548c2ecf20Sopenharmony_ci if (rc) 15558c2ecf20Sopenharmony_ci goto out_free_xid; 15568c2ecf20Sopenharmony_ci 15578c2ecf20Sopenharmony_ci down_read(&htable_rw_lock); 15588c2ecf20Sopenharmony_ci 15598c2ecf20Sopenharmony_ci ce = lookup_cache_entry(npath, NULL); 15608c2ecf20Sopenharmony_ci if (IS_ERR(ce)) { 15618c2ecf20Sopenharmony_ci rc = PTR_ERR(ce); 15628c2ecf20Sopenharmony_ci up_read(&htable_rw_lock); 15638c2ecf20Sopenharmony_ci goto out_free_path; 15648c2ecf20Sopenharmony_ci } 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_ci if (!cache_entry_expired(ce)) { 15678c2ecf20Sopenharmony_ci up_read(&htable_rw_lock); 15688c2ecf20Sopenharmony_ci goto out_free_path; 15698c2ecf20Sopenharmony_ci } 15708c2ecf20Sopenharmony_ci 15718c2ecf20Sopenharmony_ci up_read(&htable_rw_lock); 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci /* If it's a DFS Link, then use root SMB session for refreshing it */ 15748c2ecf20Sopenharmony_ci if (is_dfs_link(npath)) { 15758c2ecf20Sopenharmony_ci ses = root_ses = find_root_ses(vi, tcon, npath); 15768c2ecf20Sopenharmony_ci if (IS_ERR(ses)) { 15778c2ecf20Sopenharmony_ci rc = PTR_ERR(ses); 15788c2ecf20Sopenharmony_ci root_ses = NULL; 15798c2ecf20Sopenharmony_ci goto out_free_path; 15808c2ecf20Sopenharmony_ci } 15818c2ecf20Sopenharmony_ci } else { 15828c2ecf20Sopenharmony_ci ses = tcon->ses; 15838c2ecf20Sopenharmony_ci } 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci rc = get_dfs_referral(xid, ses, cache_nlsc, tcon->remap, npath, &refs, 15868c2ecf20Sopenharmony_ci &numrefs); 15878c2ecf20Sopenharmony_ci if (!rc) { 15888c2ecf20Sopenharmony_ci dump_refs(refs, numrefs); 15898c2ecf20Sopenharmony_ci rc = update_cache_entry(npath, refs, numrefs); 15908c2ecf20Sopenharmony_ci free_dfs_info_array(refs, numrefs); 15918c2ecf20Sopenharmony_ci } 15928c2ecf20Sopenharmony_ci 15938c2ecf20Sopenharmony_ci if (root_ses) 15948c2ecf20Sopenharmony_ci cifs_put_smb_ses(root_ses); 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_ciout_free_path: 15978c2ecf20Sopenharmony_ci free_normalized_path(path, npath); 15988c2ecf20Sopenharmony_ci 15998c2ecf20Sopenharmony_ciout_free_xid: 16008c2ecf20Sopenharmony_ci free_xid(xid); 16018c2ecf20Sopenharmony_ci return rc; 16028c2ecf20Sopenharmony_ci} 16038c2ecf20Sopenharmony_ci 16048c2ecf20Sopenharmony_ci/* 16058c2ecf20Sopenharmony_ci * Worker that will refresh DFS cache based on lowest TTL value from a DFS 16068c2ecf20Sopenharmony_ci * referral. 16078c2ecf20Sopenharmony_ci */ 16088c2ecf20Sopenharmony_cistatic void refresh_cache_worker(struct work_struct *work) 16098c2ecf20Sopenharmony_ci{ 16108c2ecf20Sopenharmony_ci struct vol_info *vi, *nvi; 16118c2ecf20Sopenharmony_ci struct TCP_Server_Info *server; 16128c2ecf20Sopenharmony_ci LIST_HEAD(vols); 16138c2ecf20Sopenharmony_ci LIST_HEAD(tcons); 16148c2ecf20Sopenharmony_ci struct cifs_tcon *tcon, *ntcon; 16158c2ecf20Sopenharmony_ci int rc; 16168c2ecf20Sopenharmony_ci 16178c2ecf20Sopenharmony_ci /* 16188c2ecf20Sopenharmony_ci * Find SMB volumes that are eligible (server->tcpStatus == CifsGood) 16198c2ecf20Sopenharmony_ci * for refreshing. 16208c2ecf20Sopenharmony_ci */ 16218c2ecf20Sopenharmony_ci spin_lock(&vol_list_lock); 16228c2ecf20Sopenharmony_ci list_for_each_entry(vi, &vol_list, list) { 16238c2ecf20Sopenharmony_ci server = get_tcp_server(&vi->smb_vol); 16248c2ecf20Sopenharmony_ci if (!server) 16258c2ecf20Sopenharmony_ci continue; 16268c2ecf20Sopenharmony_ci 16278c2ecf20Sopenharmony_ci kref_get(&vi->refcnt); 16288c2ecf20Sopenharmony_ci list_add_tail(&vi->rlist, &vols); 16298c2ecf20Sopenharmony_ci put_tcp_server(server); 16308c2ecf20Sopenharmony_ci } 16318c2ecf20Sopenharmony_ci spin_unlock(&vol_list_lock); 16328c2ecf20Sopenharmony_ci 16338c2ecf20Sopenharmony_ci /* Walk through all TCONs and refresh any expired cache entry */ 16348c2ecf20Sopenharmony_ci list_for_each_entry_safe(vi, nvi, &vols, rlist) { 16358c2ecf20Sopenharmony_ci spin_lock(&vi->smb_vol_lock); 16368c2ecf20Sopenharmony_ci server = get_tcp_server(&vi->smb_vol); 16378c2ecf20Sopenharmony_ci spin_unlock(&vi->smb_vol_lock); 16388c2ecf20Sopenharmony_ci 16398c2ecf20Sopenharmony_ci if (!server) 16408c2ecf20Sopenharmony_ci goto next_vol; 16418c2ecf20Sopenharmony_ci 16428c2ecf20Sopenharmony_ci get_tcons(server, &tcons); 16438c2ecf20Sopenharmony_ci rc = 0; 16448c2ecf20Sopenharmony_ci 16458c2ecf20Sopenharmony_ci list_for_each_entry_safe(tcon, ntcon, &tcons, ulist) { 16468c2ecf20Sopenharmony_ci /* 16478c2ecf20Sopenharmony_ci * Skip tcp server if any of its tcons failed to refresh 16488c2ecf20Sopenharmony_ci * (possibily due to reconnects). 16498c2ecf20Sopenharmony_ci */ 16508c2ecf20Sopenharmony_ci if (!rc) 16518c2ecf20Sopenharmony_ci rc = refresh_tcon(vi, tcon); 16528c2ecf20Sopenharmony_ci 16538c2ecf20Sopenharmony_ci list_del_init(&tcon->ulist); 16548c2ecf20Sopenharmony_ci cifs_put_tcon(tcon); 16558c2ecf20Sopenharmony_ci } 16568c2ecf20Sopenharmony_ci 16578c2ecf20Sopenharmony_ci put_tcp_server(server); 16588c2ecf20Sopenharmony_ci 16598c2ecf20Sopenharmony_cinext_vol: 16608c2ecf20Sopenharmony_ci list_del_init(&vi->rlist); 16618c2ecf20Sopenharmony_ci kref_put(&vi->refcnt, vol_release); 16628c2ecf20Sopenharmony_ci } 16638c2ecf20Sopenharmony_ci 16648c2ecf20Sopenharmony_ci spin_lock(&cache_ttl_lock); 16658c2ecf20Sopenharmony_ci queue_delayed_work(dfscache_wq, &refresh_task, cache_ttl * HZ); 16668c2ecf20Sopenharmony_ci spin_unlock(&cache_ttl_lock); 16678c2ecf20Sopenharmony_ci} 1668