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