blob: 76ffe12d64f51bfcf04073cdb80f49d90dbf600f [file] [log] [blame]
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001// SPDX-License-Identifier: GPL-2.0
2/*
3 * DFS referral cache routines
4 *
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03005 * Copyright (c) 2018-2019 Paulo Alcantara <palcantara@suse.de>
Paulo Alcantara54be1f62018-11-14 16:01:21 -02006 */
7
Paulo Alcantara54be1f62018-11-14 16:01:21 -02008#include <linux/jhash.h>
9#include <linux/ktime.h>
10#include <linux/slab.h>
11#include <linux/nls.h>
12#include <linux/workqueue.h>
13#include "cifsglob.h"
14#include "smb2pdu.h"
15#include "smb2proto.h"
16#include "cifsproto.h"
17#include "cifs_debug.h"
18#include "cifs_unicode.h"
19#include "smb2glob.h"
20
21#include "dfs_cache.h"
22
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030023#define CACHE_HTABLE_SIZE 32
24#define CACHE_MAX_ENTRIES 64
Paulo Alcantara54be1f62018-11-14 16:01:21 -020025
26#define IS_INTERLINK_SET(v) ((v) & (DFSREF_REFERRAL_SERVER | \
27 DFSREF_STORAGE_SERVER))
28
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030029struct cache_dfs_tgt {
30 char *name;
31 struct list_head list;
Paulo Alcantara54be1f62018-11-14 16:01:21 -020032};
33
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030034struct cache_entry {
35 struct hlist_node hlist;
36 const char *path;
37 int ttl;
38 int srvtype;
39 int flags;
40 struct timespec64 etime;
41 int path_consumed;
42 int numtgts;
43 struct list_head tlist;
44 struct cache_dfs_tgt *tgthint;
Paulo Alcantara54be1f62018-11-14 16:01:21 -020045};
46
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030047struct vol_info {
48 char *fullpath;
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -030049 spinlock_t smb_vol_lock;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030050 struct smb_vol smb_vol;
51 char *mntdata;
52 struct list_head list;
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -030053 struct list_head rlist;
54 struct kref refcnt;
Paulo Alcantara54be1f62018-11-14 16:01:21 -020055};
56
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030057static struct kmem_cache *cache_slab __read_mostly;
58static struct workqueue_struct *dfscache_wq __read_mostly;
Paulo Alcantara54be1f62018-11-14 16:01:21 -020059
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030060static int cache_ttl;
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -030061static DEFINE_SPINLOCK(cache_ttl_lock);
62
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030063static struct nls_table *cache_nlsc;
Paulo Alcantara54be1f62018-11-14 16:01:21 -020064
65/*
66 * Number of entries in the cache
67 */
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -030068static atomic_t cache_count;
Paulo Alcantara54be1f62018-11-14 16:01:21 -020069
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030070static struct hlist_head cache_htable[CACHE_HTABLE_SIZE];
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -030071static DECLARE_RWSEM(htable_rw_lock);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030072
73static LIST_HEAD(vol_list);
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -030074static DEFINE_SPINLOCK(vol_list_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -020075
76static void refresh_cache_worker(struct work_struct *work);
77
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030078static DECLARE_DELAYED_WORK(refresh_task, refresh_cache_worker);
79
Paulo Alcantara (SUSE)ff2f7fc2019-12-04 17:38:01 -030080static int get_normalized_path(const char *path, char **npath)
Paulo Alcantara54be1f62018-11-14 16:01:21 -020081{
Paulo Alcantara (SUSE)ff2f7fc2019-12-04 17:38:01 -030082 if (!path || strlen(path) < 3 || (*path != '\\' && *path != '/'))
83 return -EINVAL;
Paulo Alcantara54be1f62018-11-14 16:01:21 -020084
Paulo Alcantara54be1f62018-11-14 16:01:21 -020085 if (*path == '\\') {
86 *npath = (char *)path;
87 } else {
88 *npath = kstrndup(path, strlen(path), GFP_KERNEL);
89 if (!*npath)
90 return -ENOMEM;
91 convert_delimiter(*npath, '\\');
92 }
93 return 0;
94}
95
96static inline void free_normalized_path(const char *path, char *npath)
97{
98 if (path != npath)
99 kfree(npath);
100}
101
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300102static inline bool cache_entry_expired(const struct cache_entry *ce)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200103{
104 struct timespec64 ts;
105
Stephen Rothwell54e4f732018-12-17 20:11:46 +1100106 ktime_get_coarse_real_ts64(&ts);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300107 return timespec64_compare(&ts, &ce->etime) >= 0;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200108}
109
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300110static inline void free_tgts(struct cache_entry *ce)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200111{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300112 struct cache_dfs_tgt *t, *n;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200113
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300114 list_for_each_entry_safe(t, n, &ce->tlist, list) {
115 list_del(&t->list);
116 kfree(t->name);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200117 kfree(t);
118 }
119}
120
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300121static inline void flush_cache_ent(struct cache_entry *ce)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200122{
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300123 hlist_del_init(&ce->hlist);
Paulo Alcantara (SUSE)199c6bd2019-12-04 17:37:59 -0300124 kfree(ce->path);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200125 free_tgts(ce);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300126 atomic_dec(&cache_count);
127 kmem_cache_free(cache_slab, ce);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200128}
129
130static void flush_cache_ents(void)
131{
132 int i;
133
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300134 for (i = 0; i < CACHE_HTABLE_SIZE; i++) {
135 struct hlist_head *l = &cache_htable[i];
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300136 struct hlist_node *n;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300137 struct cache_entry *ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200138
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300139 hlist_for_each_entry_safe(ce, n, l, hlist) {
140 if (!hlist_unhashed(&ce->hlist))
141 flush_cache_ent(ce);
142 }
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200143 }
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200144}
145
146/*
147 * dfs cache /proc file
148 */
149static int dfscache_proc_show(struct seq_file *m, void *v)
150{
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300151 int i;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300152 struct cache_entry *ce;
153 struct cache_dfs_tgt *t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200154
155 seq_puts(m, "DFS cache\n---------\n");
156
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300157 down_read(&htable_rw_lock);
158 for (i = 0; i < CACHE_HTABLE_SIZE; i++) {
159 struct hlist_head *l = &cache_htable[i];
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200160
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300161 hlist_for_each_entry(ce, l, hlist) {
162 if (hlist_unhashed(&ce->hlist))
163 continue;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200164
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300165 seq_printf(m,
166 "cache entry: path=%s,type=%s,ttl=%d,etime=%ld,"
167 "interlink=%s,path_consumed=%d,expired=%s\n",
168 ce->path,
169 ce->srvtype == DFS_TYPE_ROOT ? "root" : "link",
170 ce->ttl, ce->etime.tv_nsec,
171 IS_INTERLINK_SET(ce->flags) ? "yes" : "no",
172 ce->path_consumed,
173 cache_entry_expired(ce) ? "yes" : "no");
174
175 list_for_each_entry(t, &ce->tlist, list) {
176 seq_printf(m, " %s%s\n",
177 t->name,
178 ce->tgthint == t ? " (target hint)" : "");
179 }
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200180 }
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200181 }
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300182 up_read(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200183
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200184 return 0;
185}
186
187static ssize_t dfscache_proc_write(struct file *file, const char __user *buffer,
188 size_t count, loff_t *ppos)
189{
190 char c;
191 int rc;
192
193 rc = get_user(c, buffer);
194 if (rc)
195 return rc;
196
197 if (c != '0')
198 return -EINVAL;
199
200 cifs_dbg(FYI, "clearing dfs cache");
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300201
202 down_write(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200203 flush_cache_ents();
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300204 up_write(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200205
206 return count;
207}
208
209static int dfscache_proc_open(struct inode *inode, struct file *file)
210{
211 return single_open(file, dfscache_proc_show, NULL);
212}
213
214const struct file_operations dfscache_proc_fops = {
215 .open = dfscache_proc_open,
216 .read = seq_read,
217 .llseek = seq_lseek,
218 .release = single_release,
219 .write = dfscache_proc_write,
220};
221
222#ifdef CONFIG_CIFS_DEBUG2
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300223static inline void dump_tgts(const struct cache_entry *ce)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200224{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300225 struct cache_dfs_tgt *t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200226
227 cifs_dbg(FYI, "target list:\n");
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300228 list_for_each_entry(t, &ce->tlist, list) {
229 cifs_dbg(FYI, " %s%s\n", t->name,
230 ce->tgthint == t ? " (target hint)" : "");
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200231 }
232}
233
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300234static inline void dump_ce(const struct cache_entry *ce)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200235{
236 cifs_dbg(FYI, "cache entry: path=%s,type=%s,ttl=%d,etime=%ld,"
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300237 "interlink=%s,path_consumed=%d,expired=%s\n", ce->path,
238 ce->srvtype == DFS_TYPE_ROOT ? "root" : "link", ce->ttl,
239 ce->etime.tv_nsec,
240 IS_INTERLINK_SET(ce->flags) ? "yes" : "no",
241 ce->path_consumed,
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200242 cache_entry_expired(ce) ? "yes" : "no");
243 dump_tgts(ce);
244}
245
246static inline void dump_refs(const struct dfs_info3_param *refs, int numrefs)
247{
248 int i;
249
250 cifs_dbg(FYI, "DFS referrals returned by the server:\n");
251 for (i = 0; i < numrefs; i++) {
252 const struct dfs_info3_param *ref = &refs[i];
253
254 cifs_dbg(FYI,
255 "\n"
256 "flags: 0x%x\n"
257 "path_consumed: %d\n"
258 "server_type: 0x%x\n"
259 "ref_flag: 0x%x\n"
260 "path_name: %s\n"
261 "node_name: %s\n"
262 "ttl: %d (%dm)\n",
263 ref->flags, ref->path_consumed, ref->server_type,
264 ref->ref_flag, ref->path_name, ref->node_name,
265 ref->ttl, ref->ttl / 60);
266 }
267}
268#else
269#define dump_tgts(e)
270#define dump_ce(e)
271#define dump_refs(r, n)
272#endif
273
274/**
275 * dfs_cache_init - Initialize DFS referral cache.
276 *
277 * Return zero if initialized successfully, otherwise non-zero.
278 */
279int dfs_cache_init(void)
280{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300281 int rc;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200282 int i;
283
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300284 dfscache_wq = alloc_workqueue("cifs-dfscache",
285 WQ_FREEZABLE | WQ_MEM_RECLAIM, 1);
286 if (!dfscache_wq)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200287 return -ENOMEM;
288
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300289 cache_slab = kmem_cache_create("cifs_dfs_cache",
290 sizeof(struct cache_entry), 0,
291 SLAB_HWCACHE_ALIGN, NULL);
292 if (!cache_slab) {
293 rc = -ENOMEM;
294 goto out_destroy_wq;
295 }
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200296
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300297 for (i = 0; i < CACHE_HTABLE_SIZE; i++)
298 INIT_HLIST_HEAD(&cache_htable[i]);
299
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300300 atomic_set(&cache_count, 0);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300301 cache_nlsc = load_nls_default();
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200302
303 cifs_dbg(FYI, "%s: initialized DFS referral cache\n", __func__);
304 return 0;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300305
306out_destroy_wq:
307 destroy_workqueue(dfscache_wq);
308 return rc;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200309}
310
311static inline unsigned int cache_entry_hash(const void *data, int size)
312{
313 unsigned int h;
314
315 h = jhash(data, size, 0);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300316 return h & (CACHE_HTABLE_SIZE - 1);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200317}
318
319/* Check whether second path component of @path is SYSVOL or NETLOGON */
320static inline bool is_sysvol_or_netlogon(const char *path)
321{
322 const char *s;
323 char sep = path[0];
324
325 s = strchr(path + 1, sep) + 1;
326 return !strncasecmp(s, "sysvol", strlen("sysvol")) ||
327 !strncasecmp(s, "netlogon", strlen("netlogon"));
328}
329
330/* Return target hint of a DFS cache entry */
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300331static inline char *get_tgt_name(const struct cache_entry *ce)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200332{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300333 struct cache_dfs_tgt *t = ce->tgthint;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200334
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300335 return t ? t->name : ERR_PTR(-ENOENT);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200336}
337
338/* Return expire time out of a new entry's TTL */
339static inline struct timespec64 get_expire_time(int ttl)
340{
341 struct timespec64 ts = {
342 .tv_sec = ttl,
343 .tv_nsec = 0,
344 };
Stephen Rothwell54e4f732018-12-17 20:11:46 +1100345 struct timespec64 now;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200346
Stephen Rothwell54e4f732018-12-17 20:11:46 +1100347 ktime_get_coarse_real_ts64(&now);
348 return timespec64_add(now, ts);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200349}
350
351/* Allocate a new DFS target */
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300352static struct cache_dfs_tgt *alloc_target(const char *name)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200353{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300354 struct cache_dfs_tgt *t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200355
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300356 t = kmalloc(sizeof(*t), GFP_ATOMIC);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200357 if (!t)
358 return ERR_PTR(-ENOMEM);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300359 t->name = kstrndup(name, strlen(name), GFP_ATOMIC);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300360 if (!t->name) {
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200361 kfree(t);
362 return ERR_PTR(-ENOMEM);
363 }
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300364 INIT_LIST_HEAD(&t->list);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200365 return t;
366}
367
368/*
369 * Copy DFS referral information to a cache entry and conditionally update
370 * target hint.
371 */
372static int copy_ref_data(const struct dfs_info3_param *refs, int numrefs,
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300373 struct cache_entry *ce, const char *tgthint)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200374{
375 int i;
376
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300377 ce->ttl = refs[0].ttl;
378 ce->etime = get_expire_time(ce->ttl);
379 ce->srvtype = refs[0].server_type;
380 ce->flags = refs[0].ref_flag;
381 ce->path_consumed = refs[0].path_consumed;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200382
383 for (i = 0; i < numrefs; i++) {
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300384 struct cache_dfs_tgt *t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200385
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300386 t = alloc_target(refs[i].node_name);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200387 if (IS_ERR(t)) {
388 free_tgts(ce);
389 return PTR_ERR(t);
390 }
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300391 if (tgthint && !strcasecmp(t->name, tgthint)) {
392 list_add(&t->list, &ce->tlist);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200393 tgthint = NULL;
394 } else {
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300395 list_add_tail(&t->list, &ce->tlist);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200396 }
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300397 ce->numtgts++;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200398 }
399
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300400 ce->tgthint = list_first_entry_or_null(&ce->tlist,
401 struct cache_dfs_tgt, list);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200402
403 return 0;
404}
405
406/* Allocate a new cache entry */
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300407static struct cache_entry *alloc_cache_entry(const char *path,
408 const struct dfs_info3_param *refs,
409 int numrefs)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200410{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300411 struct cache_entry *ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200412 int rc;
413
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300414 ce = kmem_cache_zalloc(cache_slab, GFP_KERNEL);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200415 if (!ce)
416 return ERR_PTR(-ENOMEM);
417
Paulo Alcantara (SUSE)199c6bd2019-12-04 17:37:59 -0300418 ce->path = kstrndup(path, strlen(path), GFP_KERNEL);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300419 if (!ce->path) {
420 kmem_cache_free(cache_slab, ce);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200421 return ERR_PTR(-ENOMEM);
422 }
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300423 INIT_HLIST_NODE(&ce->hlist);
424 INIT_LIST_HEAD(&ce->tlist);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200425
426 rc = copy_ref_data(refs, numrefs, ce, NULL);
427 if (rc) {
Paulo Alcantara (SUSE)199c6bd2019-12-04 17:37:59 -0300428 kfree(ce->path);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300429 kmem_cache_free(cache_slab, ce);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200430 ce = ERR_PTR(rc);
431 }
432 return ce;
433}
434
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300435/* Must be called with htable_rw_lock held */
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200436static void remove_oldest_entry(void)
437{
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300438 int i;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300439 struct cache_entry *ce;
440 struct cache_entry *to_del = NULL;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200441
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300442 for (i = 0; i < CACHE_HTABLE_SIZE; i++) {
443 struct hlist_head *l = &cache_htable[i];
444
445 hlist_for_each_entry(ce, l, hlist) {
446 if (hlist_unhashed(&ce->hlist))
447 continue;
448 if (!to_del || timespec64_compare(&ce->etime,
449 &to_del->etime) < 0)
450 to_del = ce;
451 }
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200452 }
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300453
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200454 if (!to_del) {
455 cifs_dbg(FYI, "%s: no entry to remove", __func__);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300456 return;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200457 }
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300458
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200459 cifs_dbg(FYI, "%s: removing entry", __func__);
460 dump_ce(to_del);
461 flush_cache_ent(to_del);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200462}
463
464/* Add a new DFS cache entry */
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300465static int add_cache_entry(const char *path, unsigned int hash,
466 struct dfs_info3_param *refs, int numrefs)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200467{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300468 struct cache_entry *ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200469
470 ce = alloc_cache_entry(path, refs, numrefs);
471 if (IS_ERR(ce))
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300472 return PTR_ERR(ce);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200473
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -0300474 spin_lock(&cache_ttl_lock);
475 if (!cache_ttl) {
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300476 cache_ttl = ce->ttl;
477 queue_delayed_work(dfscache_wq, &refresh_task, cache_ttl * HZ);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200478 } else {
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300479 cache_ttl = min_t(int, cache_ttl, ce->ttl);
480 mod_delayed_work(dfscache_wq, &refresh_task, cache_ttl * HZ);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200481 }
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -0300482 spin_unlock(&cache_ttl_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200483
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300484 down_write(&htable_rw_lock);
485 hlist_add_head(&ce->hlist, &cache_htable[hash]);
486 dump_ce(ce);
487 up_write(&htable_rw_lock);
488
489 return 0;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200490}
491
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200492/*
493 * Find a DFS cache entry in hash table and optionally check prefix path against
494 * @path.
495 * Use whole path components in the match.
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300496 * Must be called with htable_rw_lock held.
497 *
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200498 * Return ERR_PTR(-ENOENT) if the entry is not found.
499 */
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300500static struct cache_entry *lookup_cache_entry(const char *path,
501 unsigned int *hash)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200502{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300503 struct cache_entry *ce;
504 unsigned int h;
505 bool found = false;
506
507 h = cache_entry_hash(path, strlen(path));
508
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300509 hlist_for_each_entry(ce, &cache_htable[h], hlist) {
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300510 if (!strcasecmp(path, ce->path)) {
511 found = true;
512 dump_ce(ce);
513 break;
514 }
515 }
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300516
517 if (!found)
518 ce = ERR_PTR(-ENOENT);
519 if (hash)
520 *hash = h;
521
522 return ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200523}
524
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -0300525static void __vol_release(struct vol_info *vi)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200526{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300527 kfree(vi->fullpath);
528 kfree(vi->mntdata);
529 cifs_cleanup_volume_info_contents(&vi->smb_vol);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200530 kfree(vi);
531}
532
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -0300533static void vol_release(struct kref *kref)
534{
535 struct vol_info *vi = container_of(kref, struct vol_info, refcnt);
536
537 spin_lock(&vol_list_lock);
538 list_del(&vi->list);
539 spin_unlock(&vol_list_lock);
540 __vol_release(vi);
541}
542
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200543static inline void free_vol_list(void)
544{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300545 struct vol_info *vi, *nvi;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200546
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -0300547 list_for_each_entry_safe(vi, nvi, &vol_list, list) {
548 list_del_init(&vi->list);
549 __vol_release(vi);
550 }
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200551}
552
553/**
554 * dfs_cache_destroy - destroy DFS referral cache
555 */
556void dfs_cache_destroy(void)
557{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300558 cancel_delayed_work_sync(&refresh_task);
559 unload_nls(cache_nlsc);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200560 free_vol_list();
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200561 flush_cache_ents();
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300562 kmem_cache_destroy(cache_slab);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300563 destroy_workqueue(dfscache_wq);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200564
565 cifs_dbg(FYI, "%s: destroyed DFS referral cache\n", __func__);
566}
567
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300568/* Must be called with htable_rw_lock held */
569static int __update_cache_entry(const char *path,
570 const struct dfs_info3_param *refs,
571 int numrefs)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200572{
573 int rc;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300574 struct cache_entry *ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200575 char *s, *th = NULL;
576
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300577 ce = lookup_cache_entry(path, NULL);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200578 if (IS_ERR(ce))
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300579 return PTR_ERR(ce);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200580
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300581 if (ce->tgthint) {
582 s = ce->tgthint->name;
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300583 th = kstrndup(s, strlen(s), GFP_ATOMIC);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200584 if (!th)
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300585 return -ENOMEM;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200586 }
587
588 free_tgts(ce);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300589 ce->numtgts = 0;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200590
591 rc = copy_ref_data(refs, numrefs, ce, th);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300592
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200593 kfree(th);
594
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300595 return 0;
596}
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200597
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300598static int get_dfs_referral(const unsigned int xid, struct cifs_ses *ses,
599 const struct nls_table *nls_codepage, int remap,
600 const char *path, struct dfs_info3_param **refs,
601 int *numrefs)
602{
603 cifs_dbg(FYI, "%s: get an DFS referral for %s\n", __func__, path);
604
605 if (!ses || !ses->server || !ses->server->ops->get_dfs_refer)
606 return -EOPNOTSUPP;
607 if (unlikely(!nls_codepage))
608 return -EINVAL;
609
610 *refs = NULL;
611 *numrefs = 0;
612
613 return ses->server->ops->get_dfs_refer(xid, ses, path, refs, numrefs,
614 nls_codepage, remap);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200615}
616
617/* Update an expired cache entry by getting a new DFS referral from server */
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300618static int update_cache_entry(const char *path,
619 const struct dfs_info3_param *refs,
620 int numrefs)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200621{
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300622
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200623 int rc;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200624
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300625 down_write(&htable_rw_lock);
626 rc = __update_cache_entry(path, refs, numrefs);
627 up_write(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200628
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300629 return rc;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200630}
631
632/*
633 * Find, create or update a DFS cache entry.
634 *
635 * If the entry wasn't found, it will create a new one. Or if it was found but
636 * expired, then it will update the entry accordingly.
637 *
638 * For interlinks, __cifs_dfs_mount() and expand_dfs_referral() are supposed to
639 * handle them properly.
640 */
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300641static int __dfs_cache_find(const unsigned int xid, struct cifs_ses *ses,
642 const struct nls_table *nls_codepage, int remap,
643 const char *path, bool noreq)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200644{
645 int rc;
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300646 unsigned int hash;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300647 struct cache_entry *ce;
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300648 struct dfs_info3_param *refs = NULL;
649 int numrefs = 0;
650 bool newent = false;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200651
652 cifs_dbg(FYI, "%s: search path: %s\n", __func__, path);
653
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300654 down_read(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200655
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300656 ce = lookup_cache_entry(path, &hash);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200657
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300658 /*
659 * If @noreq is set, no requests will be sent to the server. Just return
660 * the cache entry.
661 */
662 if (noreq) {
663 up_read(&htable_rw_lock);
664 return IS_ERR(ce) ? PTR_ERR(ce) : 0;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200665 }
666
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300667 if (!IS_ERR(ce)) {
668 if (!cache_entry_expired(ce)) {
669 dump_ce(ce);
670 up_read(&htable_rw_lock);
671 return 0;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200672 }
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300673 } else {
674 newent = true;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200675 }
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300676
677 up_read(&htable_rw_lock);
678
679 /*
680 * No entry was found.
681 *
682 * Request a new DFS referral in order to create a new cache entry, or
683 * updating an existing one.
684 */
685 rc = get_dfs_referral(xid, ses, nls_codepage, remap, path,
686 &refs, &numrefs);
687 if (rc)
688 return rc;
689
690 dump_refs(refs, numrefs);
691
692 if (!newent) {
693 rc = update_cache_entry(path, refs, numrefs);
694 goto out_free_refs;
695 }
696
697 if (atomic_read(&cache_count) >= CACHE_MAX_ENTRIES) {
698 cifs_dbg(FYI, "%s: reached max cache size (%d)", __func__,
699 CACHE_MAX_ENTRIES);
700 down_write(&htable_rw_lock);
701 remove_oldest_entry();
702 up_write(&htable_rw_lock);
703 }
704
705 rc = add_cache_entry(path, hash, refs, numrefs);
706 if (!rc)
707 atomic_inc(&cache_count);
708
709out_free_refs:
710 free_dfs_info_array(refs, numrefs);
711 return rc;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200712}
713
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300714/*
715 * Set up a DFS referral from a given cache entry.
716 *
717 * Must be called with htable_rw_lock held.
718 */
719static int setup_referral(const char *path, struct cache_entry *ce,
720 struct dfs_info3_param *ref, const char *target)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200721{
722 int rc;
723
724 cifs_dbg(FYI, "%s: set up new ref\n", __func__);
725
726 memset(ref, 0, sizeof(*ref));
727
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300728 ref->path_name = kstrndup(path, strlen(path), GFP_ATOMIC);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200729 if (!ref->path_name)
730 return -ENOMEM;
731
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300732 ref->node_name = kstrndup(target, strlen(target), GFP_ATOMIC);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200733 if (!ref->node_name) {
734 rc = -ENOMEM;
735 goto err_free_path;
736 }
737
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300738 ref->path_consumed = ce->path_consumed;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300739 ref->ttl = ce->ttl;
740 ref->server_type = ce->srvtype;
741 ref->ref_flag = ce->flags;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200742
743 return 0;
744
745err_free_path:
746 kfree(ref->path_name);
747 ref->path_name = NULL;
748 return rc;
749}
750
751/* Return target list of a DFS cache entry */
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300752static int get_targets(struct cache_entry *ce, struct dfs_cache_tgt_list *tl)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200753{
754 int rc;
755 struct list_head *head = &tl->tl_list;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300756 struct cache_dfs_tgt *t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200757 struct dfs_cache_tgt_iterator *it, *nit;
758
759 memset(tl, 0, sizeof(*tl));
760 INIT_LIST_HEAD(head);
761
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300762 list_for_each_entry(t, &ce->tlist, list) {
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300763 it = kzalloc(sizeof(*it), GFP_ATOMIC);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200764 if (!it) {
765 rc = -ENOMEM;
766 goto err_free_it;
767 }
768
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300769 it->it_name = kstrndup(t->name, strlen(t->name), GFP_ATOMIC);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200770 if (!it->it_name) {
Dan Carpenterc715f892019-01-05 21:18:03 +0300771 kfree(it);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200772 rc = -ENOMEM;
773 goto err_free_it;
774 }
775
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300776 if (ce->tgthint == t)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200777 list_add(&it->it_list, head);
778 else
779 list_add_tail(&it->it_list, head);
780 }
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300781
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300782 tl->tl_numtgts = ce->numtgts;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200783
784 return 0;
785
786err_free_it:
787 list_for_each_entry_safe(it, nit, head, it_list) {
788 kfree(it->it_name);
789 kfree(it);
790 }
791 return rc;
792}
793
794/**
795 * dfs_cache_find - find a DFS cache entry
796 *
797 * If it doesn't find the cache entry, then it will get a DFS referral
798 * for @path and create a new entry.
799 *
800 * In case the cache entry exists but expired, it will get a DFS referral
801 * for @path and then update the respective cache entry.
802 *
803 * These parameters are passed down to the get_dfs_refer() call if it
804 * needs to be issued:
805 * @xid: syscall xid
806 * @ses: smb session to issue the request on
807 * @nls_codepage: charset conversion
808 * @remap: path character remapping type
809 * @path: path to lookup in DFS referral cache.
810 *
811 * @ref: when non-NULL, store single DFS referral result in it.
812 * @tgt_list: when non-NULL, store complete DFS target list in it.
813 *
814 * Return zero if the target was found, otherwise non-zero.
815 */
816int dfs_cache_find(const unsigned int xid, struct cifs_ses *ses,
817 const struct nls_table *nls_codepage, int remap,
818 const char *path, struct dfs_info3_param *ref,
819 struct dfs_cache_tgt_list *tgt_list)
820{
821 int rc;
822 char *npath;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300823 struct cache_entry *ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200824
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200825 rc = get_normalized_path(path, &npath);
826 if (rc)
827 return rc;
828
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300829 rc = __dfs_cache_find(xid, ses, nls_codepage, remap, npath, false);
830 if (rc)
831 goto out_free_path;
832
833 down_read(&htable_rw_lock);
834
835 ce = lookup_cache_entry(npath, NULL);
836 if (IS_ERR(ce)) {
837 up_read(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200838 rc = PTR_ERR(ce);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300839 goto out_free_path;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200840 }
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300841
842 if (ref)
843 rc = setup_referral(path, ce, ref, get_tgt_name(ce));
844 else
845 rc = 0;
846 if (!rc && tgt_list)
847 rc = get_targets(ce, tgt_list);
848
849 up_read(&htable_rw_lock);
850
851out_free_path:
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200852 free_normalized_path(path, npath);
853 return rc;
854}
855
856/**
857 * dfs_cache_noreq_find - find a DFS cache entry without sending any requests to
858 * the currently connected server.
859 *
860 * NOTE: This function will neither update a cache entry in case it was
861 * expired, nor create a new cache entry if @path hasn't been found. It heavily
862 * relies on an existing cache entry.
863 *
864 * @path: path to lookup in the DFS referral cache.
865 * @ref: when non-NULL, store single DFS referral result in it.
866 * @tgt_list: when non-NULL, store complete DFS target list in it.
867 *
868 * Return 0 if successful.
869 * Return -ENOENT if the entry was not found.
870 * Return non-zero for other errors.
871 */
872int dfs_cache_noreq_find(const char *path, struct dfs_info3_param *ref,
873 struct dfs_cache_tgt_list *tgt_list)
874{
875 int rc;
876 char *npath;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300877 struct cache_entry *ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200878
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200879 rc = get_normalized_path(path, &npath);
880 if (rc)
881 return rc;
882
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300883 cifs_dbg(FYI, "%s: path: %s\n", __func__, npath);
884
885 down_read(&htable_rw_lock);
886
887 ce = lookup_cache_entry(npath, NULL);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200888 if (IS_ERR(ce)) {
889 rc = PTR_ERR(ce);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300890 goto out_unlock;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200891 }
892
893 if (ref)
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300894 rc = setup_referral(path, ce, ref, get_tgt_name(ce));
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200895 else
896 rc = 0;
897 if (!rc && tgt_list)
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300898 rc = get_targets(ce, tgt_list);
899
900out_unlock:
901 up_read(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200902 free_normalized_path(path, npath);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300903
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200904 return rc;
905}
906
907/**
908 * dfs_cache_update_tgthint - update target hint of a DFS cache entry
909 *
910 * If it doesn't find the cache entry, then it will get a DFS referral for @path
911 * and create a new entry.
912 *
913 * In case the cache entry exists but expired, it will get a DFS referral
914 * for @path and then update the respective cache entry.
915 *
916 * @xid: syscall id
917 * @ses: smb session
918 * @nls_codepage: charset conversion
919 * @remap: type of character remapping for paths
920 * @path: path to lookup in DFS referral cache.
921 * @it: DFS target iterator
922 *
923 * Return zero if the target hint was updated successfully, otherwise non-zero.
924 */
925int dfs_cache_update_tgthint(const unsigned int xid, struct cifs_ses *ses,
926 const struct nls_table *nls_codepage, int remap,
927 const char *path,
928 const struct dfs_cache_tgt_iterator *it)
929{
930 int rc;
931 char *npath;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300932 struct cache_entry *ce;
933 struct cache_dfs_tgt *t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200934
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200935 rc = get_normalized_path(path, &npath);
936 if (rc)
937 return rc;
938
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300939 cifs_dbg(FYI, "%s: update target hint - path: %s\n", __func__, npath);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200940
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300941 rc = __dfs_cache_find(xid, ses, nls_codepage, remap, npath, false);
942 if (rc)
943 goto out_free_path;
944
945 down_write(&htable_rw_lock);
946
947 ce = lookup_cache_entry(npath, NULL);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200948 if (IS_ERR(ce)) {
949 rc = PTR_ERR(ce);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300950 goto out_unlock;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200951 }
952
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300953 t = ce->tgthint;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200954
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300955 if (likely(!strcasecmp(it->it_name, t->name)))
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300956 goto out_unlock;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200957
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300958 list_for_each_entry(t, &ce->tlist, list) {
959 if (!strcasecmp(t->name, it->it_name)) {
960 ce->tgthint = t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200961 cifs_dbg(FYI, "%s: new target hint: %s\n", __func__,
962 it->it_name);
963 break;
964 }
965 }
966
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300967out_unlock:
968 up_write(&htable_rw_lock);
969out_free_path:
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200970 free_normalized_path(path, npath);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300971
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200972 return rc;
973}
974
975/**
976 * dfs_cache_noreq_update_tgthint - update target hint of a DFS cache entry
977 * without sending any requests to the currently connected server.
978 *
979 * NOTE: This function will neither update a cache entry in case it was
980 * expired, nor create a new cache entry if @path hasn't been found. It heavily
981 * relies on an existing cache entry.
982 *
983 * @path: path to lookup in DFS referral cache.
984 * @it: target iterator which contains the target hint to update the cache
985 * entry with.
986 *
987 * Return zero if the target hint was updated successfully, otherwise non-zero.
988 */
989int dfs_cache_noreq_update_tgthint(const char *path,
990 const struct dfs_cache_tgt_iterator *it)
991{
992 int rc;
993 char *npath;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300994 struct cache_entry *ce;
995 struct cache_dfs_tgt *t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200996
Paulo Alcantara (SUSE)ff2f7fc2019-12-04 17:38:01 -0300997 if (!it)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200998 return -EINVAL;
999
1000 rc = get_normalized_path(path, &npath);
1001 if (rc)
1002 return rc;
1003
1004 cifs_dbg(FYI, "%s: path: %s\n", __func__, npath);
1005
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001006 down_write(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001007
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001008 ce = lookup_cache_entry(npath, NULL);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001009 if (IS_ERR(ce)) {
1010 rc = PTR_ERR(ce);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001011 goto out_unlock;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001012 }
1013
1014 rc = 0;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001015 t = ce->tgthint;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001016
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001017 if (unlikely(!strcasecmp(it->it_name, t->name)))
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001018 goto out_unlock;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001019
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001020 list_for_each_entry(t, &ce->tlist, list) {
1021 if (!strcasecmp(t->name, it->it_name)) {
1022 ce->tgthint = t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001023 cifs_dbg(FYI, "%s: new target hint: %s\n", __func__,
1024 it->it_name);
1025 break;
1026 }
1027 }
1028
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001029out_unlock:
1030 up_write(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001031 free_normalized_path(path, npath);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001032
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001033 return rc;
1034}
1035
1036/**
1037 * dfs_cache_get_tgt_referral - returns a DFS referral (@ref) from a given
1038 * target iterator (@it).
1039 *
1040 * @path: path to lookup in DFS referral cache.
1041 * @it: DFS target iterator.
1042 * @ref: DFS referral pointer to set up the gathered information.
1043 *
1044 * Return zero if the DFS referral was set up correctly, otherwise non-zero.
1045 */
1046int dfs_cache_get_tgt_referral(const char *path,
1047 const struct dfs_cache_tgt_iterator *it,
1048 struct dfs_info3_param *ref)
1049{
1050 int rc;
1051 char *npath;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001052 struct cache_entry *ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001053
1054 if (!it || !ref)
1055 return -EINVAL;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001056
1057 rc = get_normalized_path(path, &npath);
1058 if (rc)
1059 return rc;
1060
1061 cifs_dbg(FYI, "%s: path: %s\n", __func__, npath);
1062
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001063 down_read(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001064
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001065 ce = lookup_cache_entry(npath, NULL);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001066 if (IS_ERR(ce)) {
1067 rc = PTR_ERR(ce);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001068 goto out_unlock;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001069 }
1070
1071 cifs_dbg(FYI, "%s: target name: %s\n", __func__, it->it_name);
1072
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001073 rc = setup_referral(path, ce, ref, it->it_name);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001074
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001075out_unlock:
1076 up_read(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001077 free_normalized_path(path, npath);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001078
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001079 return rc;
1080}
1081
1082static int dup_vol(struct smb_vol *vol, struct smb_vol *new)
1083{
1084 memcpy(new, vol, sizeof(*new));
1085
1086 if (vol->username) {
1087 new->username = kstrndup(vol->username, strlen(vol->username),
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001088 GFP_KERNEL);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001089 if (!new->username)
1090 return -ENOMEM;
1091 }
1092 if (vol->password) {
1093 new->password = kstrndup(vol->password, strlen(vol->password),
1094 GFP_KERNEL);
1095 if (!new->password)
1096 goto err_free_username;
1097 }
1098 if (vol->UNC) {
1099 cifs_dbg(FYI, "%s: vol->UNC: %s\n", __func__, vol->UNC);
1100 new->UNC = kstrndup(vol->UNC, strlen(vol->UNC), GFP_KERNEL);
1101 if (!new->UNC)
1102 goto err_free_password;
1103 }
1104 if (vol->domainname) {
1105 new->domainname = kstrndup(vol->domainname,
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001106 strlen(vol->domainname), GFP_KERNEL);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001107 if (!new->domainname)
1108 goto err_free_unc;
1109 }
1110 if (vol->iocharset) {
1111 new->iocharset = kstrndup(vol->iocharset,
1112 strlen(vol->iocharset), GFP_KERNEL);
1113 if (!new->iocharset)
1114 goto err_free_domainname;
1115 }
1116 if (vol->prepath) {
1117 cifs_dbg(FYI, "%s: vol->prepath: %s\n", __func__, vol->prepath);
1118 new->prepath = kstrndup(vol->prepath, strlen(vol->prepath),
1119 GFP_KERNEL);
1120 if (!new->prepath)
1121 goto err_free_iocharset;
1122 }
1123
1124 return 0;
1125
1126err_free_iocharset:
1127 kfree(new->iocharset);
1128err_free_domainname:
1129 kfree(new->domainname);
1130err_free_unc:
1131 kfree(new->UNC);
1132err_free_password:
Dan Carpenter34bca9b2018-12-20 14:32:43 +03001133 kzfree(new->password);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001134err_free_username:
1135 kfree(new->username);
1136 kfree(new);
1137 return -ENOMEM;
1138}
1139
1140/**
1141 * dfs_cache_add_vol - add a cifs volume during mount() that will be handled by
1142 * DFS cache refresh worker.
1143 *
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001144 * @mntdata: mount data.
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001145 * @vol: cifs volume.
1146 * @fullpath: origin full path.
1147 *
1148 * Return zero if volume was set up correctly, otherwise non-zero.
1149 */
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001150int dfs_cache_add_vol(char *mntdata, struct smb_vol *vol, const char *fullpath)
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001151{
1152 int rc;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001153 struct vol_info *vi;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001154
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001155 if (!vol || !fullpath || !mntdata)
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001156 return -EINVAL;
1157
1158 cifs_dbg(FYI, "%s: fullpath: %s\n", __func__, fullpath);
1159
1160 vi = kzalloc(sizeof(*vi), GFP_KERNEL);
1161 if (!vi)
1162 return -ENOMEM;
1163
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001164 vi->fullpath = kstrndup(fullpath, strlen(fullpath), GFP_KERNEL);
1165 if (!vi->fullpath) {
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001166 rc = -ENOMEM;
1167 goto err_free_vi;
1168 }
1169
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001170 rc = dup_vol(vol, &vi->smb_vol);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001171 if (rc)
1172 goto err_free_fullpath;
1173
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001174 vi->mntdata = mntdata;
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001175 spin_lock_init(&vi->smb_vol_lock);
1176 kref_init(&vi->refcnt);
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001177
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001178 spin_lock(&vol_list_lock);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001179 list_add_tail(&vi->list, &vol_list);
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001180 spin_unlock(&vol_list_lock);
1181
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001182 return 0;
1183
1184err_free_fullpath:
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001185 kfree(vi->fullpath);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001186err_free_vi:
1187 kfree(vi);
1188 return rc;
1189}
1190
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001191/* Must be called with vol_list_lock held */
1192static struct vol_info *find_vol(const char *fullpath)
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001193{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001194 struct vol_info *vi;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001195
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001196 list_for_each_entry(vi, &vol_list, list) {
1197 cifs_dbg(FYI, "%s: vi->fullpath: %s\n", __func__, vi->fullpath);
1198 if (!strcasecmp(vi->fullpath, fullpath))
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001199 return vi;
1200 }
1201 return ERR_PTR(-ENOENT);
1202}
1203
1204/**
1205 * dfs_cache_update_vol - update vol info in DFS cache after failover
1206 *
1207 * @fullpath: fullpath to look up in volume list.
1208 * @server: TCP ses pointer.
1209 *
1210 * Return zero if volume was updated, otherwise non-zero.
1211 */
1212int dfs_cache_update_vol(const char *fullpath, struct TCP_Server_Info *server)
1213{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001214 struct vol_info *vi;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001215
1216 if (!fullpath || !server)
1217 return -EINVAL;
1218
1219 cifs_dbg(FYI, "%s: fullpath: %s\n", __func__, fullpath);
1220
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001221 spin_lock(&vol_list_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001222 vi = find_vol(fullpath);
1223 if (IS_ERR(vi)) {
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001224 spin_unlock(&vol_list_lock);
1225 return PTR_ERR(vi);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001226 }
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001227 kref_get(&vi->refcnt);
1228 spin_unlock(&vol_list_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001229
1230 cifs_dbg(FYI, "%s: updating volume info\n", __func__);
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001231 spin_lock(&vi->smb_vol_lock);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001232 memcpy(&vi->smb_vol.dstaddr, &server->dstaddr,
1233 sizeof(vi->smb_vol.dstaddr));
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001234 spin_unlock(&vi->smb_vol_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001235
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001236 kref_put(&vi->refcnt, vol_release);
1237
1238 return 0;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001239}
1240
1241/**
1242 * dfs_cache_del_vol - remove volume info in DFS cache during umount()
1243 *
1244 * @fullpath: fullpath to look up in volume list.
1245 */
1246void dfs_cache_del_vol(const char *fullpath)
1247{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001248 struct vol_info *vi;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001249
1250 if (!fullpath || !*fullpath)
1251 return;
1252
1253 cifs_dbg(FYI, "%s: fullpath: %s\n", __func__, fullpath);
1254
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001255 spin_lock(&vol_list_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001256 vi = find_vol(fullpath);
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001257 spin_unlock(&vol_list_lock);
1258
1259 kref_put(&vi->refcnt, vol_release);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001260}
1261
1262/* Get all tcons that are within a DFS namespace and can be refreshed */
1263static void get_tcons(struct TCP_Server_Info *server, struct list_head *head)
1264{
1265 struct cifs_ses *ses;
1266 struct cifs_tcon *tcon;
1267
1268 INIT_LIST_HEAD(head);
1269
1270 spin_lock(&cifs_tcp_ses_lock);
1271 list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
1272 list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
1273 if (!tcon->need_reconnect && !tcon->need_reopen_files &&
1274 tcon->dfs_path) {
1275 tcon->tc_count++;
1276 list_add_tail(&tcon->ulist, head);
1277 }
1278 }
1279 if (ses->tcon_ipc && !ses->tcon_ipc->need_reconnect &&
1280 ses->tcon_ipc->dfs_path) {
1281 list_add_tail(&ses->tcon_ipc->ulist, head);
1282 }
1283 }
1284 spin_unlock(&cifs_tcp_ses_lock);
1285}
1286
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001287static bool is_dfs_link(const char *path)
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001288{
1289 char *s;
1290
1291 s = strchr(path + 1, '\\');
1292 if (!s)
1293 return false;
1294 return !!strchr(s + 1, '\\');
1295}
1296
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001297static char *get_dfs_root(const char *path)
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001298{
1299 char *s, *npath;
1300
1301 s = strchr(path + 1, '\\');
1302 if (!s)
1303 return ERR_PTR(-EINVAL);
1304
1305 s = strchr(s + 1, '\\');
1306 if (!s)
1307 return ERR_PTR(-EINVAL);
1308
1309 npath = kstrndup(path, s - path, GFP_KERNEL);
1310 if (!npath)
1311 return ERR_PTR(-ENOMEM);
1312
1313 return npath;
1314}
1315
Paulo Alcantara (SUSE)345c1a42019-12-04 17:38:00 -03001316static inline void put_tcp_server(struct TCP_Server_Info *server)
1317{
1318 cifs_put_tcp_session(server, 0);
1319}
1320
1321static struct TCP_Server_Info *get_tcp_server(struct smb_vol *vol)
1322{
1323 struct TCP_Server_Info *server;
1324
1325 server = cifs_find_tcp_session(vol);
1326 if (IS_ERR_OR_NULL(server))
1327 return NULL;
1328
1329 spin_lock(&GlobalMid_Lock);
1330 if (server->tcpStatus != CifsGood) {
1331 spin_unlock(&GlobalMid_Lock);
1332 put_tcp_server(server);
1333 return NULL;
1334 }
1335 spin_unlock(&GlobalMid_Lock);
1336
1337 return server;
1338}
1339
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001340/* Find root SMB session out of a DFS link path */
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001341static struct cifs_ses *find_root_ses(struct vol_info *vi,
1342 struct cifs_tcon *tcon,
1343 const char *path)
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001344{
1345 char *rpath;
1346 int rc;
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001347 struct cache_entry *ce;
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001348 struct dfs_info3_param ref = {0};
1349 char *mdata = NULL, *devname = NULL;
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001350 struct TCP_Server_Info *server;
1351 struct cifs_ses *ses;
1352 struct smb_vol vol;
1353
1354 rpath = get_dfs_root(path);
1355 if (IS_ERR(rpath))
1356 return ERR_CAST(rpath);
1357
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001358 down_read(&htable_rw_lock);
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001359
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001360 ce = lookup_cache_entry(rpath, NULL);
1361 if (IS_ERR(ce)) {
1362 up_read(&htable_rw_lock);
1363 ses = ERR_CAST(ce);
1364 goto out;
1365 }
1366
1367 rc = setup_referral(path, ce, &ref, get_tgt_name(ce));
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001368 if (rc) {
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001369 up_read(&htable_rw_lock);
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001370 ses = ERR_PTR(rc);
1371 goto out;
1372 }
1373
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001374 up_read(&htable_rw_lock);
1375
1376 mdata = cifs_compose_mount_options(vi->mntdata, rpath, &ref,
1377 &devname);
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001378 free_dfs_info_param(&ref);
1379
1380 if (IS_ERR(mdata)) {
1381 ses = ERR_CAST(mdata);
1382 mdata = NULL;
1383 goto out;
1384 }
1385
Paulo Alcantara (SUSE)df3df922019-11-22 12:30:52 -03001386 rc = cifs_setup_volume_info(&vol, mdata, devname, false);
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001387 kfree(devname);
1388
1389 if (rc) {
1390 ses = ERR_PTR(rc);
1391 goto out;
1392 }
1393
Paulo Alcantara (SUSE)345c1a42019-12-04 17:38:00 -03001394 server = get_tcp_server(&vol);
1395 if (!server) {
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001396 ses = ERR_PTR(-EHOSTDOWN);
1397 goto out;
1398 }
1399
1400 ses = cifs_get_smb_ses(server, &vol);
1401
1402out:
1403 cifs_cleanup_volume_info_contents(&vol);
1404 kfree(mdata);
1405 kfree(rpath);
1406
1407 return ses;
1408}
1409
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001410/* Refresh DFS cache entry from a given tcon */
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001411static int refresh_tcon(struct vol_info *vi, struct cifs_tcon *tcon)
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001412{
1413 int rc = 0;
1414 unsigned int xid;
1415 char *path, *npath;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001416 struct cache_entry *ce;
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001417 struct cifs_ses *root_ses = NULL, *ses;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001418 struct dfs_info3_param *refs = NULL;
1419 int numrefs = 0;
1420
1421 xid = get_xid();
1422
1423 path = tcon->dfs_path + 1;
1424
1425 rc = get_normalized_path(path, &npath);
1426 if (rc)
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001427 goto out_free_xid;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001428
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001429 down_read(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001430
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001431 ce = lookup_cache_entry(npath, NULL);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001432 if (IS_ERR(ce)) {
1433 rc = PTR_ERR(ce);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001434 up_read(&htable_rw_lock);
1435 goto out_free_path;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001436 }
1437
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001438 if (!cache_entry_expired(ce)) {
1439 up_read(&htable_rw_lock);
1440 goto out_free_path;
1441 }
1442
1443 up_read(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001444
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001445 /* If it's a DFS Link, then use root SMB session for refreshing it */
1446 if (is_dfs_link(npath)) {
1447 ses = root_ses = find_root_ses(vi, tcon, npath);
1448 if (IS_ERR(ses)) {
1449 rc = PTR_ERR(ses);
1450 root_ses = NULL;
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001451 goto out_free_path;
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001452 }
1453 } else {
1454 ses = tcon->ses;
1455 }
1456
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001457 rc = get_dfs_referral(xid, ses, cache_nlsc, tcon->remap, npath, &refs,
1458 &numrefs);
1459 if (!rc) {
1460 dump_refs(refs, numrefs);
1461 rc = update_cache_entry(npath, refs, numrefs);
1462 free_dfs_info_array(refs, numrefs);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001463 }
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001464
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001465 if (root_ses)
1466 cifs_put_smb_ses(root_ses);
1467
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001468out_free_path:
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001469 free_normalized_path(path, npath);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001470
1471out_free_xid:
1472 free_xid(xid);
1473 return rc;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001474}
1475
1476/*
1477 * Worker that will refresh DFS cache based on lowest TTL value from a DFS
1478 * referral.
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001479 */
1480static void refresh_cache_worker(struct work_struct *work)
1481{
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001482 struct vol_info *vi, *nvi;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001483 struct TCP_Server_Info *server;
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001484 LIST_HEAD(vols);
1485 LIST_HEAD(tcons);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001486 struct cifs_tcon *tcon, *ntcon;
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001487 int rc;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001488
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001489 /*
1490 * Find SMB volumes that are eligible (server->tcpStatus == CifsGood)
1491 * for refreshing.
1492 */
1493 spin_lock(&vol_list_lock);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001494 list_for_each_entry(vi, &vol_list, list) {
Paulo Alcantara (SUSE)345c1a42019-12-04 17:38:00 -03001495 server = get_tcp_server(&vi->smb_vol);
1496 if (!server)
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001497 continue;
Paulo Alcantara (SUSE)345c1a42019-12-04 17:38:00 -03001498
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001499 kref_get(&vi->refcnt);
1500 list_add_tail(&vi->rlist, &vols);
1501 put_tcp_server(server);
1502 }
1503 spin_unlock(&vol_list_lock);
1504
1505 /* Walk through all TCONs and refresh any expired cache entry */
1506 list_for_each_entry_safe(vi, nvi, &vols, rlist) {
1507 spin_lock(&vi->smb_vol_lock);
1508 server = get_tcp_server(&vi->smb_vol);
1509 spin_unlock(&vi->smb_vol_lock);
1510
1511 if (!server)
1512 goto next_vol;
1513
1514 get_tcons(server, &tcons);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001515 rc = 0;
1516
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001517 list_for_each_entry_safe(tcon, ntcon, &tcons, ulist) {
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001518 /*
1519 * Skip tcp server if any of its tcons failed to refresh
1520 * (possibily due to reconnects).
1521 */
1522 if (!rc)
1523 rc = refresh_tcon(vi, tcon);
1524
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001525 list_del_init(&tcon->ulist);
1526 cifs_put_tcon(tcon);
1527 }
Paulo Alcantara (SUSE)345c1a42019-12-04 17:38:00 -03001528
1529 put_tcp_server(server);
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001530
1531next_vol:
1532 list_del_init(&vi->rlist);
1533 kref_put(&vi->refcnt, vol_release);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001534 }
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001535
1536 spin_lock(&cache_ttl_lock);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001537 queue_delayed_work(dfscache_wq, &refresh_task, cache_ttl * HZ);
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001538 spin_unlock(&cache_ttl_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001539}