blob: 7b6db272fd0b8180676a07bb1a5e1490d12af1c4 [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>
Alexey Dobriyan97a32532020-02-03 17:37:17 -080011#include <linux/proc_fs.h>
Paulo Alcantara54be1f62018-11-14 16:01:21 -020012#include <linux/nls.h>
13#include <linux/workqueue.h>
14#include "cifsglob.h"
15#include "smb2pdu.h"
16#include "smb2proto.h"
17#include "cifsproto.h"
18#include "cifs_debug.h"
19#include "cifs_unicode.h"
20#include "smb2glob.h"
21
22#include "dfs_cache.h"
23
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030024#define CACHE_HTABLE_SIZE 32
25#define CACHE_MAX_ENTRIES 64
Paulo Alcantara54be1f62018-11-14 16:01:21 -020026
27#define IS_INTERLINK_SET(v) ((v) & (DFSREF_REFERRAL_SERVER | \
28 DFSREF_STORAGE_SERVER))
29
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030030struct cache_dfs_tgt {
31 char *name;
Paulo Alcantara7548e1d2020-07-21 09:36:42 -030032 int path_consumed;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030033 struct list_head list;
Paulo Alcantara54be1f62018-11-14 16:01:21 -020034};
35
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030036struct cache_entry {
37 struct hlist_node hlist;
38 const char *path;
39 int ttl;
40 int srvtype;
41 int flags;
42 struct timespec64 etime;
43 int path_consumed;
44 int numtgts;
45 struct list_head tlist;
46 struct cache_dfs_tgt *tgthint;
Paulo Alcantara54be1f62018-11-14 16:01:21 -020047};
48
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030049struct vol_info {
50 char *fullpath;
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -030051 spinlock_t smb_vol_lock;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030052 struct smb_vol smb_vol;
53 char *mntdata;
54 struct list_head list;
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -030055 struct list_head rlist;
56 struct kref refcnt;
Paulo Alcantara54be1f62018-11-14 16:01:21 -020057};
58
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030059static struct kmem_cache *cache_slab __read_mostly;
60static struct workqueue_struct *dfscache_wq __read_mostly;
Paulo Alcantara54be1f62018-11-14 16:01:21 -020061
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030062static int cache_ttl;
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -030063static DEFINE_SPINLOCK(cache_ttl_lock);
64
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030065static struct nls_table *cache_nlsc;
Paulo Alcantara54be1f62018-11-14 16:01:21 -020066
67/*
68 * Number of entries in the cache
69 */
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -030070static atomic_t cache_count;
Paulo Alcantara54be1f62018-11-14 16:01:21 -020071
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030072static struct hlist_head cache_htable[CACHE_HTABLE_SIZE];
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -030073static DECLARE_RWSEM(htable_rw_lock);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030074
75static LIST_HEAD(vol_list);
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -030076static DEFINE_SPINLOCK(vol_list_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -020077
78static void refresh_cache_worker(struct work_struct *work);
79
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030080static DECLARE_DELAYED_WORK(refresh_task, refresh_cache_worker);
81
Paulo Alcantara (SUSE)ff2f7fc2019-12-04 17:38:01 -030082static int get_normalized_path(const char *path, char **npath)
Paulo Alcantara54be1f62018-11-14 16:01:21 -020083{
Paulo Alcantara (SUSE)ff2f7fc2019-12-04 17:38:01 -030084 if (!path || strlen(path) < 3 || (*path != '\\' && *path != '/'))
85 return -EINVAL;
Paulo Alcantara54be1f62018-11-14 16:01:21 -020086
Paulo Alcantara54be1f62018-11-14 16:01:21 -020087 if (*path == '\\') {
88 *npath = (char *)path;
89 } else {
90 *npath = kstrndup(path, strlen(path), GFP_KERNEL);
91 if (!*npath)
92 return -ENOMEM;
93 convert_delimiter(*npath, '\\');
94 }
95 return 0;
96}
97
98static inline void free_normalized_path(const char *path, char *npath)
99{
100 if (path != npath)
101 kfree(npath);
102}
103
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300104static inline bool cache_entry_expired(const struct cache_entry *ce)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200105{
106 struct timespec64 ts;
107
Stephen Rothwell54e4f732018-12-17 20:11:46 +1100108 ktime_get_coarse_real_ts64(&ts);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300109 return timespec64_compare(&ts, &ce->etime) >= 0;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200110}
111
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300112static inline void free_tgts(struct cache_entry *ce)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200113{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300114 struct cache_dfs_tgt *t, *n;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200115
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300116 list_for_each_entry_safe(t, n, &ce->tlist, list) {
117 list_del(&t->list);
118 kfree(t->name);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200119 kfree(t);
120 }
121}
122
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300123static inline void flush_cache_ent(struct cache_entry *ce)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200124{
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300125 hlist_del_init(&ce->hlist);
Paulo Alcantara (SUSE)199c6bd2019-12-04 17:37:59 -0300126 kfree(ce->path);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200127 free_tgts(ce);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300128 atomic_dec(&cache_count);
129 kmem_cache_free(cache_slab, ce);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200130}
131
132static void flush_cache_ents(void)
133{
134 int i;
135
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300136 for (i = 0; i < CACHE_HTABLE_SIZE; i++) {
137 struct hlist_head *l = &cache_htable[i];
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300138 struct hlist_node *n;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300139 struct cache_entry *ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200140
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300141 hlist_for_each_entry_safe(ce, n, l, hlist) {
142 if (!hlist_unhashed(&ce->hlist))
143 flush_cache_ent(ce);
144 }
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200145 }
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200146}
147
148/*
149 * dfs cache /proc file
150 */
151static int dfscache_proc_show(struct seq_file *m, void *v)
152{
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300153 int i;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300154 struct cache_entry *ce;
155 struct cache_dfs_tgt *t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200156
157 seq_puts(m, "DFS cache\n---------\n");
158
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300159 down_read(&htable_rw_lock);
160 for (i = 0; i < CACHE_HTABLE_SIZE; i++) {
161 struct hlist_head *l = &cache_htable[i];
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200162
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300163 hlist_for_each_entry(ce, l, hlist) {
164 if (hlist_unhashed(&ce->hlist))
165 continue;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200166
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300167 seq_printf(m,
168 "cache entry: path=%s,type=%s,ttl=%d,etime=%ld,"
169 "interlink=%s,path_consumed=%d,expired=%s\n",
170 ce->path,
171 ce->srvtype == DFS_TYPE_ROOT ? "root" : "link",
172 ce->ttl, ce->etime.tv_nsec,
173 IS_INTERLINK_SET(ce->flags) ? "yes" : "no",
174 ce->path_consumed,
175 cache_entry_expired(ce) ? "yes" : "no");
176
177 list_for_each_entry(t, &ce->tlist, list) {
178 seq_printf(m, " %s%s\n",
179 t->name,
180 ce->tgthint == t ? " (target hint)" : "");
181 }
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200182 }
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200183 }
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300184 up_read(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200185
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200186 return 0;
187}
188
189static ssize_t dfscache_proc_write(struct file *file, const char __user *buffer,
190 size_t count, loff_t *ppos)
191{
192 char c;
193 int rc;
194
195 rc = get_user(c, buffer);
196 if (rc)
197 return rc;
198
199 if (c != '0')
200 return -EINVAL;
201
Joe Perchesa0a30362020-04-14 22:42:53 -0700202 cifs_dbg(FYI, "clearing dfs cache\n");
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300203
204 down_write(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200205 flush_cache_ents();
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300206 up_write(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200207
208 return count;
209}
210
211static int dfscache_proc_open(struct inode *inode, struct file *file)
212{
213 return single_open(file, dfscache_proc_show, NULL);
214}
215
Alexey Dobriyan97a32532020-02-03 17:37:17 -0800216const struct proc_ops dfscache_proc_ops = {
217 .proc_open = dfscache_proc_open,
218 .proc_read = seq_read,
219 .proc_lseek = seq_lseek,
220 .proc_release = single_release,
221 .proc_write = dfscache_proc_write,
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200222};
223
224#ifdef CONFIG_CIFS_DEBUG2
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300225static inline void dump_tgts(const struct cache_entry *ce)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200226{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300227 struct cache_dfs_tgt *t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200228
229 cifs_dbg(FYI, "target list:\n");
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300230 list_for_each_entry(t, &ce->tlist, list) {
231 cifs_dbg(FYI, " %s%s\n", t->name,
232 ce->tgthint == t ? " (target hint)" : "");
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200233 }
234}
235
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300236static inline void dump_ce(const struct cache_entry *ce)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200237{
Joe Perchesa0a30362020-04-14 22:42:53 -0700238 cifs_dbg(FYI, "cache entry: path=%s,type=%s,ttl=%d,etime=%ld,interlink=%s,path_consumed=%d,expired=%s\n",
239 ce->path,
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300240 ce->srvtype == DFS_TYPE_ROOT ? "root" : "link", ce->ttl,
241 ce->etime.tv_nsec,
242 IS_INTERLINK_SET(ce->flags) ? "yes" : "no",
243 ce->path_consumed,
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200244 cache_entry_expired(ce) ? "yes" : "no");
245 dump_tgts(ce);
246}
247
248static inline void dump_refs(const struct dfs_info3_param *refs, int numrefs)
249{
250 int i;
251
252 cifs_dbg(FYI, "DFS referrals returned by the server:\n");
253 for (i = 0; i < numrefs; i++) {
254 const struct dfs_info3_param *ref = &refs[i];
255
256 cifs_dbg(FYI,
257 "\n"
258 "flags: 0x%x\n"
259 "path_consumed: %d\n"
260 "server_type: 0x%x\n"
261 "ref_flag: 0x%x\n"
262 "path_name: %s\n"
263 "node_name: %s\n"
264 "ttl: %d (%dm)\n",
265 ref->flags, ref->path_consumed, ref->server_type,
266 ref->ref_flag, ref->path_name, ref->node_name,
267 ref->ttl, ref->ttl / 60);
268 }
269}
270#else
271#define dump_tgts(e)
272#define dump_ce(e)
273#define dump_refs(r, n)
274#endif
275
276/**
277 * dfs_cache_init - Initialize DFS referral cache.
278 *
279 * Return zero if initialized successfully, otherwise non-zero.
280 */
281int dfs_cache_init(void)
282{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300283 int rc;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200284 int i;
285
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300286 dfscache_wq = alloc_workqueue("cifs-dfscache",
287 WQ_FREEZABLE | WQ_MEM_RECLAIM, 1);
288 if (!dfscache_wq)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200289 return -ENOMEM;
290
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300291 cache_slab = kmem_cache_create("cifs_dfs_cache",
292 sizeof(struct cache_entry), 0,
293 SLAB_HWCACHE_ALIGN, NULL);
294 if (!cache_slab) {
295 rc = -ENOMEM;
296 goto out_destroy_wq;
297 }
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200298
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300299 for (i = 0; i < CACHE_HTABLE_SIZE; i++)
300 INIT_HLIST_HEAD(&cache_htable[i]);
301
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300302 atomic_set(&cache_count, 0);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300303 cache_nlsc = load_nls_default();
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200304
305 cifs_dbg(FYI, "%s: initialized DFS referral cache\n", __func__);
306 return 0;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300307
308out_destroy_wq:
309 destroy_workqueue(dfscache_wq);
310 return rc;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200311}
312
313static inline unsigned int cache_entry_hash(const void *data, int size)
314{
315 unsigned int h;
316
317 h = jhash(data, size, 0);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300318 return h & (CACHE_HTABLE_SIZE - 1);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200319}
320
321/* Check whether second path component of @path is SYSVOL or NETLOGON */
322static inline bool is_sysvol_or_netlogon(const char *path)
323{
324 const char *s;
325 char sep = path[0];
326
327 s = strchr(path + 1, sep) + 1;
328 return !strncasecmp(s, "sysvol", strlen("sysvol")) ||
329 !strncasecmp(s, "netlogon", strlen("netlogon"));
330}
331
332/* Return target hint of a DFS cache entry */
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300333static inline char *get_tgt_name(const struct cache_entry *ce)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200334{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300335 struct cache_dfs_tgt *t = ce->tgthint;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200336
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300337 return t ? t->name : ERR_PTR(-ENOENT);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200338}
339
340/* Return expire time out of a new entry's TTL */
341static inline struct timespec64 get_expire_time(int ttl)
342{
343 struct timespec64 ts = {
344 .tv_sec = ttl,
345 .tv_nsec = 0,
346 };
Stephen Rothwell54e4f732018-12-17 20:11:46 +1100347 struct timespec64 now;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200348
Stephen Rothwell54e4f732018-12-17 20:11:46 +1100349 ktime_get_coarse_real_ts64(&now);
350 return timespec64_add(now, ts);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200351}
352
353/* Allocate a new DFS target */
Paulo Alcantara7548e1d2020-07-21 09:36:42 -0300354static struct cache_dfs_tgt *alloc_target(const char *name, int path_consumed)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200355{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300356 struct cache_dfs_tgt *t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200357
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300358 t = kmalloc(sizeof(*t), GFP_ATOMIC);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200359 if (!t)
360 return ERR_PTR(-ENOMEM);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300361 t->name = kstrndup(name, strlen(name), GFP_ATOMIC);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300362 if (!t->name) {
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200363 kfree(t);
364 return ERR_PTR(-ENOMEM);
365 }
Paulo Alcantara7548e1d2020-07-21 09:36:42 -0300366 t->path_consumed = path_consumed;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300367 INIT_LIST_HEAD(&t->list);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200368 return t;
369}
370
371/*
372 * Copy DFS referral information to a cache entry and conditionally update
373 * target hint.
374 */
375static int copy_ref_data(const struct dfs_info3_param *refs, int numrefs,
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300376 struct cache_entry *ce, const char *tgthint)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200377{
378 int i;
379
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300380 ce->ttl = refs[0].ttl;
381 ce->etime = get_expire_time(ce->ttl);
382 ce->srvtype = refs[0].server_type;
383 ce->flags = refs[0].ref_flag;
384 ce->path_consumed = refs[0].path_consumed;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200385
386 for (i = 0; i < numrefs; i++) {
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300387 struct cache_dfs_tgt *t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200388
Paulo Alcantara7548e1d2020-07-21 09:36:42 -0300389 t = alloc_target(refs[i].node_name, refs[i].path_consumed);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200390 if (IS_ERR(t)) {
391 free_tgts(ce);
392 return PTR_ERR(t);
393 }
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300394 if (tgthint && !strcasecmp(t->name, tgthint)) {
395 list_add(&t->list, &ce->tlist);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200396 tgthint = NULL;
397 } else {
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300398 list_add_tail(&t->list, &ce->tlist);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200399 }
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300400 ce->numtgts++;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200401 }
402
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300403 ce->tgthint = list_first_entry_or_null(&ce->tlist,
404 struct cache_dfs_tgt, list);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200405
406 return 0;
407}
408
409/* Allocate a new cache entry */
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300410static struct cache_entry *alloc_cache_entry(const char *path,
411 const struct dfs_info3_param *refs,
412 int numrefs)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200413{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300414 struct cache_entry *ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200415 int rc;
416
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300417 ce = kmem_cache_zalloc(cache_slab, GFP_KERNEL);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200418 if (!ce)
419 return ERR_PTR(-ENOMEM);
420
Paulo Alcantara (SUSE)199c6bd2019-12-04 17:37:59 -0300421 ce->path = kstrndup(path, strlen(path), GFP_KERNEL);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300422 if (!ce->path) {
423 kmem_cache_free(cache_slab, ce);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200424 return ERR_PTR(-ENOMEM);
425 }
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300426 INIT_HLIST_NODE(&ce->hlist);
427 INIT_LIST_HEAD(&ce->tlist);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200428
429 rc = copy_ref_data(refs, numrefs, ce, NULL);
430 if (rc) {
Paulo Alcantara (SUSE)199c6bd2019-12-04 17:37:59 -0300431 kfree(ce->path);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300432 kmem_cache_free(cache_slab, ce);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200433 ce = ERR_PTR(rc);
434 }
435 return ce;
436}
437
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300438/* Must be called with htable_rw_lock held */
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200439static void remove_oldest_entry(void)
440{
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300441 int i;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300442 struct cache_entry *ce;
443 struct cache_entry *to_del = NULL;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200444
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300445 for (i = 0; i < CACHE_HTABLE_SIZE; i++) {
446 struct hlist_head *l = &cache_htable[i];
447
448 hlist_for_each_entry(ce, l, hlist) {
449 if (hlist_unhashed(&ce->hlist))
450 continue;
451 if (!to_del || timespec64_compare(&ce->etime,
452 &to_del->etime) < 0)
453 to_del = ce;
454 }
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200455 }
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300456
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200457 if (!to_del) {
Joe Perchesa0a30362020-04-14 22:42:53 -0700458 cifs_dbg(FYI, "%s: no entry to remove\n", __func__);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300459 return;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200460 }
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300461
Joe Perchesa0a30362020-04-14 22:42:53 -0700462 cifs_dbg(FYI, "%s: removing entry\n", __func__);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200463 dump_ce(to_del);
464 flush_cache_ent(to_del);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200465}
466
467/* Add a new DFS cache entry */
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300468static int add_cache_entry(const char *path, unsigned int hash,
469 struct dfs_info3_param *refs, int numrefs)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200470{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300471 struct cache_entry *ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200472
473 ce = alloc_cache_entry(path, refs, numrefs);
474 if (IS_ERR(ce))
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300475 return PTR_ERR(ce);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200476
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -0300477 spin_lock(&cache_ttl_lock);
478 if (!cache_ttl) {
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300479 cache_ttl = ce->ttl;
480 queue_delayed_work(dfscache_wq, &refresh_task, cache_ttl * HZ);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200481 } else {
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300482 cache_ttl = min_t(int, cache_ttl, ce->ttl);
483 mod_delayed_work(dfscache_wq, &refresh_task, cache_ttl * HZ);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200484 }
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -0300485 spin_unlock(&cache_ttl_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200486
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300487 down_write(&htable_rw_lock);
488 hlist_add_head(&ce->hlist, &cache_htable[hash]);
489 dump_ce(ce);
490 up_write(&htable_rw_lock);
491
492 return 0;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200493}
494
Paulo Alcantara2e5de422020-07-21 09:36:39 -0300495static struct cache_entry *__lookup_cache_entry(const char *path)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200496{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300497 struct cache_entry *ce;
498 unsigned int h;
499 bool found = false;
500
501 h = cache_entry_hash(path, strlen(path));
502
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300503 hlist_for_each_entry(ce, &cache_htable[h], hlist) {
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300504 if (!strcasecmp(path, ce->path)) {
505 found = true;
506 dump_ce(ce);
507 break;
508 }
509 }
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300510
511 if (!found)
512 ce = ERR_PTR(-ENOENT);
Paulo Alcantara2e5de422020-07-21 09:36:39 -0300513 return ce;
514}
515
516/*
517 * Find a DFS cache entry in hash table and optionally check prefix path against
518 * @path.
519 * Use whole path components in the match.
520 * Must be called with htable_rw_lock held.
521 *
522 * Return ERR_PTR(-ENOENT) if the entry is not found.
523 */
524static struct cache_entry *lookup_cache_entry(const char *path, unsigned int *hash)
525{
526 struct cache_entry *ce = ERR_PTR(-ENOENT);
527 unsigned int h;
528 int cnt = 0;
529 char *npath;
530 char *s, *e;
531 char sep;
532
533 npath = kstrndup(path, strlen(path), GFP_KERNEL);
534 if (!npath)
535 return ERR_PTR(-ENOMEM);
536
537 s = npath;
538 sep = *npath;
539 while ((s = strchr(s, sep)) && ++cnt < 3)
540 s++;
541
542 if (cnt < 3) {
543 h = cache_entry_hash(path, strlen(path));
544 ce = __lookup_cache_entry(path);
545 goto out;
546 }
547 /*
548 * Handle paths that have more than two path components and are a complete prefix of the DFS
549 * referral request path (@path).
550 *
551 * See MS-DFSC 3.2.5.5 "Receiving a Root Referral Request or Link Referral Request".
552 */
553 h = cache_entry_hash(npath, strlen(npath));
554 e = npath + strlen(npath) - 1;
555 while (e > s) {
556 char tmp;
557
558 /* skip separators */
559 while (e > s && *e == sep)
560 e--;
561 if (e == s)
562 goto out;
563
564 tmp = *(e+1);
565 *(e+1) = 0;
566
567 ce = __lookup_cache_entry(npath);
568 if (!IS_ERR(ce)) {
569 h = cache_entry_hash(npath, strlen(npath));
570 break;
571 }
572
573 *(e+1) = tmp;
574 /* backward until separator */
575 while (e > s && *e != sep)
576 e--;
577 }
578out:
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300579 if (hash)
580 *hash = h;
Paulo Alcantara2e5de422020-07-21 09:36:39 -0300581 kfree(npath);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300582 return ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200583}
584
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -0300585static void __vol_release(struct vol_info *vi)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200586{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300587 kfree(vi->fullpath);
588 kfree(vi->mntdata);
589 cifs_cleanup_volume_info_contents(&vi->smb_vol);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200590 kfree(vi);
591}
592
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -0300593static void vol_release(struct kref *kref)
594{
595 struct vol_info *vi = container_of(kref, struct vol_info, refcnt);
596
597 spin_lock(&vol_list_lock);
598 list_del(&vi->list);
599 spin_unlock(&vol_list_lock);
600 __vol_release(vi);
601}
602
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200603static inline void free_vol_list(void)
604{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300605 struct vol_info *vi, *nvi;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200606
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -0300607 list_for_each_entry_safe(vi, nvi, &vol_list, list) {
608 list_del_init(&vi->list);
609 __vol_release(vi);
610 }
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200611}
612
613/**
614 * dfs_cache_destroy - destroy DFS referral cache
615 */
616void dfs_cache_destroy(void)
617{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300618 cancel_delayed_work_sync(&refresh_task);
619 unload_nls(cache_nlsc);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200620 free_vol_list();
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200621 flush_cache_ents();
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300622 kmem_cache_destroy(cache_slab);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300623 destroy_workqueue(dfscache_wq);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200624
625 cifs_dbg(FYI, "%s: destroyed DFS referral cache\n", __func__);
626}
627
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300628/* Must be called with htable_rw_lock held */
629static int __update_cache_entry(const char *path,
630 const struct dfs_info3_param *refs,
631 int numrefs)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200632{
633 int rc;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300634 struct cache_entry *ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200635 char *s, *th = NULL;
636
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300637 ce = lookup_cache_entry(path, NULL);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200638 if (IS_ERR(ce))
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300639 return PTR_ERR(ce);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200640
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300641 if (ce->tgthint) {
642 s = ce->tgthint->name;
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300643 th = kstrndup(s, strlen(s), GFP_ATOMIC);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200644 if (!th)
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300645 return -ENOMEM;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200646 }
647
648 free_tgts(ce);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300649 ce->numtgts = 0;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200650
651 rc = copy_ref_data(refs, numrefs, ce, th);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300652
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200653 kfree(th);
654
YueHaibingeecfc572020-01-17 10:21:56 +0800655 return rc;
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300656}
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200657
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300658static int get_dfs_referral(const unsigned int xid, struct cifs_ses *ses,
659 const struct nls_table *nls_codepage, int remap,
660 const char *path, struct dfs_info3_param **refs,
661 int *numrefs)
662{
663 cifs_dbg(FYI, "%s: get an DFS referral for %s\n", __func__, path);
664
665 if (!ses || !ses->server || !ses->server->ops->get_dfs_refer)
666 return -EOPNOTSUPP;
667 if (unlikely(!nls_codepage))
668 return -EINVAL;
669
670 *refs = NULL;
671 *numrefs = 0;
672
673 return ses->server->ops->get_dfs_refer(xid, ses, path, refs, numrefs,
674 nls_codepage, remap);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200675}
676
677/* Update an expired cache entry by getting a new DFS referral from server */
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300678static int update_cache_entry(const char *path,
679 const struct dfs_info3_param *refs,
680 int numrefs)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200681{
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300682
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200683 int rc;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200684
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300685 down_write(&htable_rw_lock);
686 rc = __update_cache_entry(path, refs, numrefs);
687 up_write(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200688
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300689 return rc;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200690}
691
692/*
693 * Find, create or update a DFS cache entry.
694 *
695 * If the entry wasn't found, it will create a new one. Or if it was found but
696 * expired, then it will update the entry accordingly.
697 *
698 * For interlinks, __cifs_dfs_mount() and expand_dfs_referral() are supposed to
699 * handle them properly.
700 */
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300701static int __dfs_cache_find(const unsigned int xid, struct cifs_ses *ses,
702 const struct nls_table *nls_codepage, int remap,
703 const char *path, bool noreq)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200704{
705 int rc;
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300706 unsigned int hash;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300707 struct cache_entry *ce;
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300708 struct dfs_info3_param *refs = NULL;
709 int numrefs = 0;
710 bool newent = false;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200711
712 cifs_dbg(FYI, "%s: search path: %s\n", __func__, path);
713
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300714 down_read(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200715
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300716 ce = lookup_cache_entry(path, &hash);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200717
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300718 /*
719 * If @noreq is set, no requests will be sent to the server. Just return
720 * the cache entry.
721 */
722 if (noreq) {
723 up_read(&htable_rw_lock);
Chen Zhou050d2a82020-01-22 18:20:30 +0800724 return PTR_ERR_OR_ZERO(ce);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200725 }
726
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300727 if (!IS_ERR(ce)) {
728 if (!cache_entry_expired(ce)) {
729 dump_ce(ce);
730 up_read(&htable_rw_lock);
731 return 0;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200732 }
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300733 } else {
734 newent = true;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200735 }
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300736
737 up_read(&htable_rw_lock);
738
739 /*
740 * No entry was found.
741 *
742 * Request a new DFS referral in order to create a new cache entry, or
743 * updating an existing one.
744 */
745 rc = get_dfs_referral(xid, ses, nls_codepage, remap, path,
746 &refs, &numrefs);
747 if (rc)
748 return rc;
749
750 dump_refs(refs, numrefs);
751
752 if (!newent) {
753 rc = update_cache_entry(path, refs, numrefs);
754 goto out_free_refs;
755 }
756
757 if (atomic_read(&cache_count) >= CACHE_MAX_ENTRIES) {
Joe Perchesa0a30362020-04-14 22:42:53 -0700758 cifs_dbg(FYI, "%s: reached max cache size (%d)\n",
759 __func__, CACHE_MAX_ENTRIES);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300760 down_write(&htable_rw_lock);
761 remove_oldest_entry();
762 up_write(&htable_rw_lock);
763 }
764
765 rc = add_cache_entry(path, hash, refs, numrefs);
766 if (!rc)
767 atomic_inc(&cache_count);
768
769out_free_refs:
770 free_dfs_info_array(refs, numrefs);
771 return rc;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200772}
773
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300774/*
775 * Set up a DFS referral from a given cache entry.
776 *
777 * Must be called with htable_rw_lock held.
778 */
779static int setup_referral(const char *path, struct cache_entry *ce,
780 struct dfs_info3_param *ref, const char *target)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200781{
782 int rc;
783
784 cifs_dbg(FYI, "%s: set up new ref\n", __func__);
785
786 memset(ref, 0, sizeof(*ref));
787
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300788 ref->path_name = kstrndup(path, strlen(path), GFP_ATOMIC);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200789 if (!ref->path_name)
790 return -ENOMEM;
791
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300792 ref->node_name = kstrndup(target, strlen(target), GFP_ATOMIC);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200793 if (!ref->node_name) {
794 rc = -ENOMEM;
795 goto err_free_path;
796 }
797
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300798 ref->path_consumed = ce->path_consumed;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300799 ref->ttl = ce->ttl;
800 ref->server_type = ce->srvtype;
801 ref->ref_flag = ce->flags;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200802
803 return 0;
804
805err_free_path:
806 kfree(ref->path_name);
807 ref->path_name = NULL;
808 return rc;
809}
810
811/* Return target list of a DFS cache entry */
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300812static int get_targets(struct cache_entry *ce, struct dfs_cache_tgt_list *tl)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200813{
814 int rc;
815 struct list_head *head = &tl->tl_list;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300816 struct cache_dfs_tgt *t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200817 struct dfs_cache_tgt_iterator *it, *nit;
818
819 memset(tl, 0, sizeof(*tl));
820 INIT_LIST_HEAD(head);
821
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300822 list_for_each_entry(t, &ce->tlist, list) {
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300823 it = kzalloc(sizeof(*it), GFP_ATOMIC);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200824 if (!it) {
825 rc = -ENOMEM;
826 goto err_free_it;
827 }
828
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300829 it->it_name = kstrndup(t->name, strlen(t->name), GFP_ATOMIC);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200830 if (!it->it_name) {
Dan Carpenterc715f892019-01-05 21:18:03 +0300831 kfree(it);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200832 rc = -ENOMEM;
833 goto err_free_it;
834 }
Paulo Alcantara7548e1d2020-07-21 09:36:42 -0300835 it->it_path_consumed = t->path_consumed;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200836
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300837 if (ce->tgthint == t)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200838 list_add(&it->it_list, head);
839 else
840 list_add_tail(&it->it_list, head);
841 }
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300842
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300843 tl->tl_numtgts = ce->numtgts;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200844
845 return 0;
846
847err_free_it:
848 list_for_each_entry_safe(it, nit, head, it_list) {
849 kfree(it->it_name);
850 kfree(it);
851 }
852 return rc;
853}
854
855/**
856 * dfs_cache_find - find a DFS cache entry
857 *
858 * If it doesn't find the cache entry, then it will get a DFS referral
859 * for @path and create a new entry.
860 *
861 * In case the cache entry exists but expired, it will get a DFS referral
862 * for @path and then update the respective cache entry.
863 *
864 * These parameters are passed down to the get_dfs_refer() call if it
865 * needs to be issued:
866 * @xid: syscall xid
867 * @ses: smb session to issue the request on
868 * @nls_codepage: charset conversion
869 * @remap: path character remapping type
870 * @path: path to lookup in DFS referral cache.
871 *
872 * @ref: when non-NULL, store single DFS referral result in it.
873 * @tgt_list: when non-NULL, store complete DFS target list in it.
874 *
875 * Return zero if the target was found, otherwise non-zero.
876 */
877int dfs_cache_find(const unsigned int xid, struct cifs_ses *ses,
878 const struct nls_table *nls_codepage, int remap,
879 const char *path, struct dfs_info3_param *ref,
880 struct dfs_cache_tgt_list *tgt_list)
881{
882 int rc;
883 char *npath;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300884 struct cache_entry *ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200885
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200886 rc = get_normalized_path(path, &npath);
887 if (rc)
888 return rc;
889
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300890 rc = __dfs_cache_find(xid, ses, nls_codepage, remap, npath, false);
891 if (rc)
892 goto out_free_path;
893
894 down_read(&htable_rw_lock);
895
896 ce = lookup_cache_entry(npath, NULL);
897 if (IS_ERR(ce)) {
898 up_read(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200899 rc = PTR_ERR(ce);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300900 goto out_free_path;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200901 }
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300902
903 if (ref)
904 rc = setup_referral(path, ce, ref, get_tgt_name(ce));
905 else
906 rc = 0;
907 if (!rc && tgt_list)
908 rc = get_targets(ce, tgt_list);
909
910 up_read(&htable_rw_lock);
911
912out_free_path:
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200913 free_normalized_path(path, npath);
914 return rc;
915}
916
917/**
918 * dfs_cache_noreq_find - find a DFS cache entry without sending any requests to
919 * the currently connected server.
920 *
921 * NOTE: This function will neither update a cache entry in case it was
922 * expired, nor create a new cache entry if @path hasn't been found. It heavily
923 * relies on an existing cache entry.
924 *
925 * @path: path to lookup in the DFS referral cache.
926 * @ref: when non-NULL, store single DFS referral result in it.
927 * @tgt_list: when non-NULL, store complete DFS target list in it.
928 *
929 * Return 0 if successful.
930 * Return -ENOENT if the entry was not found.
931 * Return non-zero for other errors.
932 */
933int dfs_cache_noreq_find(const char *path, struct dfs_info3_param *ref,
934 struct dfs_cache_tgt_list *tgt_list)
935{
936 int rc;
937 char *npath;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300938 struct cache_entry *ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200939
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200940 rc = get_normalized_path(path, &npath);
941 if (rc)
942 return rc;
943
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300944 cifs_dbg(FYI, "%s: path: %s\n", __func__, npath);
945
946 down_read(&htable_rw_lock);
947
948 ce = lookup_cache_entry(npath, NULL);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200949 if (IS_ERR(ce)) {
950 rc = PTR_ERR(ce);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300951 goto out_unlock;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200952 }
953
954 if (ref)
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300955 rc = setup_referral(path, ce, ref, get_tgt_name(ce));
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200956 else
957 rc = 0;
958 if (!rc && tgt_list)
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300959 rc = get_targets(ce, tgt_list);
960
961out_unlock:
962 up_read(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200963 free_normalized_path(path, npath);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300964
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200965 return rc;
966}
967
968/**
969 * dfs_cache_update_tgthint - update target hint of a DFS cache entry
970 *
971 * If it doesn't find the cache entry, then it will get a DFS referral for @path
972 * and create a new entry.
973 *
974 * In case the cache entry exists but expired, it will get a DFS referral
975 * for @path and then update the respective cache entry.
976 *
977 * @xid: syscall id
978 * @ses: smb session
979 * @nls_codepage: charset conversion
980 * @remap: type of character remapping for paths
981 * @path: path to lookup in DFS referral cache.
982 * @it: DFS target iterator
983 *
984 * Return zero if the target hint was updated successfully, otherwise non-zero.
985 */
986int dfs_cache_update_tgthint(const unsigned int xid, struct cifs_ses *ses,
987 const struct nls_table *nls_codepage, int remap,
988 const char *path,
989 const struct dfs_cache_tgt_iterator *it)
990{
991 int rc;
992 char *npath;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300993 struct cache_entry *ce;
994 struct cache_dfs_tgt *t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200995
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200996 rc = get_normalized_path(path, &npath);
997 if (rc)
998 return rc;
999
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001000 cifs_dbg(FYI, "%s: update target hint - path: %s\n", __func__, npath);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001001
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001002 rc = __dfs_cache_find(xid, ses, nls_codepage, remap, npath, false);
1003 if (rc)
1004 goto out_free_path;
1005
1006 down_write(&htable_rw_lock);
1007
1008 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
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001014 t = ce->tgthint;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001015
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001016 if (likely(!strcasecmp(it->it_name, t->name)))
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001017 goto out_unlock;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001018
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001019 list_for_each_entry(t, &ce->tlist, list) {
1020 if (!strcasecmp(t->name, it->it_name)) {
1021 ce->tgthint = t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001022 cifs_dbg(FYI, "%s: new target hint: %s\n", __func__,
1023 it->it_name);
1024 break;
1025 }
1026 }
1027
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001028out_unlock:
1029 up_write(&htable_rw_lock);
1030out_free_path:
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_noreq_update_tgthint - update target hint of a DFS cache entry
1038 * without sending any requests to the currently connected server.
1039 *
1040 * NOTE: This function will neither update a cache entry in case it was
1041 * expired, nor create a new cache entry if @path hasn't been found. It heavily
1042 * relies on an existing cache entry.
1043 *
1044 * @path: path to lookup in DFS referral cache.
1045 * @it: target iterator which contains the target hint to update the cache
1046 * entry with.
1047 *
1048 * Return zero if the target hint was updated successfully, otherwise non-zero.
1049 */
1050int dfs_cache_noreq_update_tgthint(const char *path,
1051 const struct dfs_cache_tgt_iterator *it)
1052{
1053 int rc;
1054 char *npath;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001055 struct cache_entry *ce;
1056 struct cache_dfs_tgt *t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001057
Paulo Alcantara (SUSE)ff2f7fc2019-12-04 17:38:01 -03001058 if (!it)
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001059 return -EINVAL;
1060
1061 rc = get_normalized_path(path, &npath);
1062 if (rc)
1063 return rc;
1064
1065 cifs_dbg(FYI, "%s: path: %s\n", __func__, npath);
1066
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001067 down_write(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001068
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001069 ce = lookup_cache_entry(npath, NULL);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001070 if (IS_ERR(ce)) {
1071 rc = PTR_ERR(ce);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001072 goto out_unlock;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001073 }
1074
1075 rc = 0;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001076 t = ce->tgthint;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001077
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001078 if (unlikely(!strcasecmp(it->it_name, t->name)))
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001079 goto out_unlock;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001080
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001081 list_for_each_entry(t, &ce->tlist, list) {
1082 if (!strcasecmp(t->name, it->it_name)) {
1083 ce->tgthint = t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001084 cifs_dbg(FYI, "%s: new target hint: %s\n", __func__,
1085 it->it_name);
1086 break;
1087 }
1088 }
1089
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001090out_unlock:
1091 up_write(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001092 free_normalized_path(path, npath);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001093
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001094 return rc;
1095}
1096
1097/**
1098 * dfs_cache_get_tgt_referral - returns a DFS referral (@ref) from a given
1099 * target iterator (@it).
1100 *
1101 * @path: path to lookup in DFS referral cache.
1102 * @it: DFS target iterator.
1103 * @ref: DFS referral pointer to set up the gathered information.
1104 *
1105 * Return zero if the DFS referral was set up correctly, otherwise non-zero.
1106 */
1107int dfs_cache_get_tgt_referral(const char *path,
1108 const struct dfs_cache_tgt_iterator *it,
1109 struct dfs_info3_param *ref)
1110{
1111 int rc;
1112 char *npath;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001113 struct cache_entry *ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001114
1115 if (!it || !ref)
1116 return -EINVAL;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001117
1118 rc = get_normalized_path(path, &npath);
1119 if (rc)
1120 return rc;
1121
1122 cifs_dbg(FYI, "%s: path: %s\n", __func__, npath);
1123
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001124 down_read(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001125
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001126 ce = lookup_cache_entry(npath, NULL);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001127 if (IS_ERR(ce)) {
1128 rc = PTR_ERR(ce);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001129 goto out_unlock;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001130 }
1131
1132 cifs_dbg(FYI, "%s: target name: %s\n", __func__, it->it_name);
1133
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001134 rc = setup_referral(path, ce, ref, it->it_name);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001135
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001136out_unlock:
1137 up_read(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001138 free_normalized_path(path, npath);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001139
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001140 return rc;
1141}
1142
1143static int dup_vol(struct smb_vol *vol, struct smb_vol *new)
1144{
1145 memcpy(new, vol, sizeof(*new));
1146
1147 if (vol->username) {
1148 new->username = kstrndup(vol->username, strlen(vol->username),
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001149 GFP_KERNEL);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001150 if (!new->username)
1151 return -ENOMEM;
1152 }
1153 if (vol->password) {
1154 new->password = kstrndup(vol->password, strlen(vol->password),
1155 GFP_KERNEL);
1156 if (!new->password)
1157 goto err_free_username;
1158 }
1159 if (vol->UNC) {
1160 cifs_dbg(FYI, "%s: vol->UNC: %s\n", __func__, vol->UNC);
1161 new->UNC = kstrndup(vol->UNC, strlen(vol->UNC), GFP_KERNEL);
1162 if (!new->UNC)
1163 goto err_free_password;
1164 }
1165 if (vol->domainname) {
1166 new->domainname = kstrndup(vol->domainname,
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001167 strlen(vol->domainname), GFP_KERNEL);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001168 if (!new->domainname)
1169 goto err_free_unc;
1170 }
1171 if (vol->iocharset) {
1172 new->iocharset = kstrndup(vol->iocharset,
1173 strlen(vol->iocharset), GFP_KERNEL);
1174 if (!new->iocharset)
1175 goto err_free_domainname;
1176 }
1177 if (vol->prepath) {
1178 cifs_dbg(FYI, "%s: vol->prepath: %s\n", __func__, vol->prepath);
1179 new->prepath = kstrndup(vol->prepath, strlen(vol->prepath),
1180 GFP_KERNEL);
1181 if (!new->prepath)
1182 goto err_free_iocharset;
1183 }
1184
1185 return 0;
1186
1187err_free_iocharset:
1188 kfree(new->iocharset);
1189err_free_domainname:
1190 kfree(new->domainname);
1191err_free_unc:
1192 kfree(new->UNC);
1193err_free_password:
Waiman Long453431a2020-08-06 23:18:13 -07001194 kfree_sensitive(new->password);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001195err_free_username:
1196 kfree(new->username);
1197 kfree(new);
1198 return -ENOMEM;
1199}
1200
1201/**
1202 * dfs_cache_add_vol - add a cifs volume during mount() that will be handled by
1203 * DFS cache refresh worker.
1204 *
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001205 * @mntdata: mount data.
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001206 * @vol: cifs volume.
1207 * @fullpath: origin full path.
1208 *
1209 * Return zero if volume was set up correctly, otherwise non-zero.
1210 */
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001211int dfs_cache_add_vol(char *mntdata, struct smb_vol *vol, const char *fullpath)
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001212{
1213 int rc;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001214 struct vol_info *vi;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001215
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001216 if (!vol || !fullpath || !mntdata)
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001217 return -EINVAL;
1218
1219 cifs_dbg(FYI, "%s: fullpath: %s\n", __func__, fullpath);
1220
1221 vi = kzalloc(sizeof(*vi), GFP_KERNEL);
1222 if (!vi)
1223 return -ENOMEM;
1224
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001225 vi->fullpath = kstrndup(fullpath, strlen(fullpath), GFP_KERNEL);
1226 if (!vi->fullpath) {
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001227 rc = -ENOMEM;
1228 goto err_free_vi;
1229 }
1230
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001231 rc = dup_vol(vol, &vi->smb_vol);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001232 if (rc)
1233 goto err_free_fullpath;
1234
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001235 vi->mntdata = mntdata;
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001236 spin_lock_init(&vi->smb_vol_lock);
1237 kref_init(&vi->refcnt);
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001238
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001239 spin_lock(&vol_list_lock);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001240 list_add_tail(&vi->list, &vol_list);
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001241 spin_unlock(&vol_list_lock);
1242
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001243 return 0;
1244
1245err_free_fullpath:
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001246 kfree(vi->fullpath);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001247err_free_vi:
1248 kfree(vi);
1249 return rc;
1250}
1251
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001252/* Must be called with vol_list_lock held */
1253static struct vol_info *find_vol(const char *fullpath)
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001254{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001255 struct vol_info *vi;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001256
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001257 list_for_each_entry(vi, &vol_list, list) {
1258 cifs_dbg(FYI, "%s: vi->fullpath: %s\n", __func__, vi->fullpath);
1259 if (!strcasecmp(vi->fullpath, fullpath))
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001260 return vi;
1261 }
1262 return ERR_PTR(-ENOENT);
1263}
1264
1265/**
1266 * dfs_cache_update_vol - update vol info in DFS cache after failover
1267 *
1268 * @fullpath: fullpath to look up in volume list.
1269 * @server: TCP ses pointer.
1270 *
1271 * Return zero if volume was updated, otherwise non-zero.
1272 */
1273int dfs_cache_update_vol(const char *fullpath, struct TCP_Server_Info *server)
1274{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001275 struct vol_info *vi;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001276
1277 if (!fullpath || !server)
1278 return -EINVAL;
1279
1280 cifs_dbg(FYI, "%s: fullpath: %s\n", __func__, fullpath);
1281
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001282 spin_lock(&vol_list_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001283 vi = find_vol(fullpath);
1284 if (IS_ERR(vi)) {
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001285 spin_unlock(&vol_list_lock);
1286 return PTR_ERR(vi);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001287 }
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001288 kref_get(&vi->refcnt);
1289 spin_unlock(&vol_list_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001290
1291 cifs_dbg(FYI, "%s: updating volume info\n", __func__);
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001292 spin_lock(&vi->smb_vol_lock);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001293 memcpy(&vi->smb_vol.dstaddr, &server->dstaddr,
1294 sizeof(vi->smb_vol.dstaddr));
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001295 spin_unlock(&vi->smb_vol_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001296
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001297 kref_put(&vi->refcnt, vol_release);
1298
1299 return 0;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001300}
1301
1302/**
1303 * dfs_cache_del_vol - remove volume info in DFS cache during umount()
1304 *
1305 * @fullpath: fullpath to look up in volume list.
1306 */
1307void dfs_cache_del_vol(const char *fullpath)
1308{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001309 struct vol_info *vi;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001310
1311 if (!fullpath || !*fullpath)
1312 return;
1313
1314 cifs_dbg(FYI, "%s: fullpath: %s\n", __func__, fullpath);
1315
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001316 spin_lock(&vol_list_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001317 vi = find_vol(fullpath);
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001318 spin_unlock(&vol_list_lock);
1319
Tom Rix0e4c42c2021-01-05 12:21:26 -08001320 if (!IS_ERR(vi))
1321 kref_put(&vi->refcnt, vol_release);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001322}
1323
Paulo Alcantara (SUSE)bacd7042020-02-20 19:49:34 -03001324/**
1325 * dfs_cache_get_tgt_share - parse a DFS target
1326 *
Paulo Alcantara7548e1d2020-07-21 09:36:42 -03001327 * @path: DFS full path
Paulo Alcantara (SUSE)bacd7042020-02-20 19:49:34 -03001328 * @it: DFS target iterator.
1329 * @share: tree name.
Paulo Alcantara (SUSE)bacd7042020-02-20 19:49:34 -03001330 * @prefix: prefix path.
Paulo Alcantara (SUSE)bacd7042020-02-20 19:49:34 -03001331 *
1332 * Return zero if target was parsed correctly, otherwise non-zero.
1333 */
Paulo Alcantara7548e1d2020-07-21 09:36:42 -03001334int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it,
1335 char **share, char **prefix)
Paulo Alcantara (SUSE)bacd7042020-02-20 19:49:34 -03001336{
Paulo Alcantara7548e1d2020-07-21 09:36:42 -03001337 char *s, sep, *p;
1338 size_t len;
1339 size_t plen1, plen2;
Paulo Alcantara (SUSE)bacd7042020-02-20 19:49:34 -03001340
Paulo Alcantara7548e1d2020-07-21 09:36:42 -03001341 if (!it || !path || !share || !prefix || strlen(path) < it->it_path_consumed)
Paulo Alcantara (SUSE)bacd7042020-02-20 19:49:34 -03001342 return -EINVAL;
1343
Paulo Alcantara7548e1d2020-07-21 09:36:42 -03001344 *share = NULL;
1345 *prefix = NULL;
1346
Paulo Alcantara (SUSE)bacd7042020-02-20 19:49:34 -03001347 sep = it->it_name[0];
1348 if (sep != '\\' && sep != '/')
1349 return -EINVAL;
1350
1351 s = strchr(it->it_name + 1, sep);
1352 if (!s)
1353 return -EINVAL;
1354
Paulo Alcantara7548e1d2020-07-21 09:36:42 -03001355 /* point to prefix in target node */
Paulo Alcantara (SUSE)bacd7042020-02-20 19:49:34 -03001356 s = strchrnul(s + 1, sep);
1357
Paulo Alcantara7548e1d2020-07-21 09:36:42 -03001358 /* extract target share */
1359 *share = kstrndup(it->it_name, s - it->it_name, GFP_KERNEL);
1360 if (!*share)
1361 return -ENOMEM;
Paulo Alcantara (SUSE)bacd7042020-02-20 19:49:34 -03001362
Paulo Alcantara7548e1d2020-07-21 09:36:42 -03001363 /* skip separator */
1364 if (*s)
1365 s++;
1366 /* point to prefix in DFS path */
1367 p = path + it->it_path_consumed;
1368 if (*p == sep)
1369 p++;
1370
1371 /* merge prefix paths from DFS path and target node */
1372 plen1 = it->it_name + strlen(it->it_name) - s;
1373 plen2 = path + strlen(path) - p;
1374 if (plen1 || plen2) {
1375 len = plen1 + plen2 + 2;
1376 *prefix = kmalloc(len, GFP_KERNEL);
1377 if (!*prefix) {
1378 kfree(*share);
1379 *share = NULL;
1380 return -ENOMEM;
1381 }
1382 if (plen1)
1383 scnprintf(*prefix, len, "%.*s%c%.*s", (int)plen1, s, sep, (int)plen2, p);
1384 else
1385 strscpy(*prefix, p, len);
1386 }
Paulo Alcantara (SUSE)bacd7042020-02-20 19:49:34 -03001387 return 0;
1388}
1389
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001390/* Get all tcons that are within a DFS namespace and can be refreshed */
1391static void get_tcons(struct TCP_Server_Info *server, struct list_head *head)
1392{
1393 struct cifs_ses *ses;
1394 struct cifs_tcon *tcon;
1395
1396 INIT_LIST_HEAD(head);
1397
1398 spin_lock(&cifs_tcp_ses_lock);
1399 list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
1400 list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
1401 if (!tcon->need_reconnect && !tcon->need_reopen_files &&
1402 tcon->dfs_path) {
1403 tcon->tc_count++;
1404 list_add_tail(&tcon->ulist, head);
1405 }
1406 }
1407 if (ses->tcon_ipc && !ses->tcon_ipc->need_reconnect &&
1408 ses->tcon_ipc->dfs_path) {
1409 list_add_tail(&ses->tcon_ipc->ulist, head);
1410 }
1411 }
1412 spin_unlock(&cifs_tcp_ses_lock);
1413}
1414
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001415static bool is_dfs_link(const char *path)
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001416{
1417 char *s;
1418
1419 s = strchr(path + 1, '\\');
1420 if (!s)
1421 return false;
1422 return !!strchr(s + 1, '\\');
1423}
1424
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001425static char *get_dfs_root(const char *path)
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001426{
1427 char *s, *npath;
1428
1429 s = strchr(path + 1, '\\');
1430 if (!s)
1431 return ERR_PTR(-EINVAL);
1432
1433 s = strchr(s + 1, '\\');
1434 if (!s)
1435 return ERR_PTR(-EINVAL);
1436
1437 npath = kstrndup(path, s - path, GFP_KERNEL);
1438 if (!npath)
1439 return ERR_PTR(-ENOMEM);
1440
1441 return npath;
1442}
1443
Paulo Alcantara (SUSE)345c1a42019-12-04 17:38:00 -03001444static inline void put_tcp_server(struct TCP_Server_Info *server)
1445{
1446 cifs_put_tcp_session(server, 0);
1447}
1448
1449static struct TCP_Server_Info *get_tcp_server(struct smb_vol *vol)
1450{
1451 struct TCP_Server_Info *server;
1452
1453 server = cifs_find_tcp_session(vol);
1454 if (IS_ERR_OR_NULL(server))
1455 return NULL;
1456
1457 spin_lock(&GlobalMid_Lock);
1458 if (server->tcpStatus != CifsGood) {
1459 spin_unlock(&GlobalMid_Lock);
1460 put_tcp_server(server);
1461 return NULL;
1462 }
1463 spin_unlock(&GlobalMid_Lock);
1464
1465 return server;
1466}
1467
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001468/* Find root SMB session out of a DFS link path */
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001469static struct cifs_ses *find_root_ses(struct vol_info *vi,
1470 struct cifs_tcon *tcon,
1471 const char *path)
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001472{
1473 char *rpath;
1474 int rc;
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001475 struct cache_entry *ce;
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001476 struct dfs_info3_param ref = {0};
1477 char *mdata = NULL, *devname = NULL;
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001478 struct TCP_Server_Info *server;
1479 struct cifs_ses *ses;
Steve French463a7b42020-01-16 15:58:00 -06001480 struct smb_vol vol = {NULL};
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001481
1482 rpath = get_dfs_root(path);
1483 if (IS_ERR(rpath))
1484 return ERR_CAST(rpath);
1485
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001486 down_read(&htable_rw_lock);
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001487
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001488 ce = lookup_cache_entry(rpath, NULL);
1489 if (IS_ERR(ce)) {
1490 up_read(&htable_rw_lock);
1491 ses = ERR_CAST(ce);
1492 goto out;
1493 }
1494
1495 rc = setup_referral(path, ce, &ref, get_tgt_name(ce));
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001496 if (rc) {
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001497 up_read(&htable_rw_lock);
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001498 ses = ERR_PTR(rc);
1499 goto out;
1500 }
1501
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001502 up_read(&htable_rw_lock);
1503
1504 mdata = cifs_compose_mount_options(vi->mntdata, rpath, &ref,
1505 &devname);
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001506 free_dfs_info_param(&ref);
1507
1508 if (IS_ERR(mdata)) {
1509 ses = ERR_CAST(mdata);
1510 mdata = NULL;
1511 goto out;
1512 }
1513
Paulo Alcantara (SUSE)df3df922019-11-22 12:30:52 -03001514 rc = cifs_setup_volume_info(&vol, mdata, devname, false);
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001515 kfree(devname);
1516
1517 if (rc) {
1518 ses = ERR_PTR(rc);
1519 goto out;
1520 }
1521
Paulo Alcantara (SUSE)345c1a42019-12-04 17:38:00 -03001522 server = get_tcp_server(&vol);
1523 if (!server) {
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001524 ses = ERR_PTR(-EHOSTDOWN);
1525 goto out;
1526 }
1527
1528 ses = cifs_get_smb_ses(server, &vol);
1529
1530out:
1531 cifs_cleanup_volume_info_contents(&vol);
1532 kfree(mdata);
1533 kfree(rpath);
1534
1535 return ses;
1536}
1537
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001538/* Refresh DFS cache entry from a given tcon */
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001539static int refresh_tcon(struct vol_info *vi, struct cifs_tcon *tcon)
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001540{
1541 int rc = 0;
1542 unsigned int xid;
1543 char *path, *npath;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001544 struct cache_entry *ce;
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001545 struct cifs_ses *root_ses = NULL, *ses;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001546 struct dfs_info3_param *refs = NULL;
1547 int numrefs = 0;
1548
1549 xid = get_xid();
1550
1551 path = tcon->dfs_path + 1;
1552
1553 rc = get_normalized_path(path, &npath);
1554 if (rc)
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001555 goto out_free_xid;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001556
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001557 down_read(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001558
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001559 ce = lookup_cache_entry(npath, NULL);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001560 if (IS_ERR(ce)) {
1561 rc = PTR_ERR(ce);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001562 up_read(&htable_rw_lock);
1563 goto out_free_path;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001564 }
1565
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001566 if (!cache_entry_expired(ce)) {
1567 up_read(&htable_rw_lock);
1568 goto out_free_path;
1569 }
1570
1571 up_read(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001572
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001573 /* If it's a DFS Link, then use root SMB session for refreshing it */
1574 if (is_dfs_link(npath)) {
1575 ses = root_ses = find_root_ses(vi, tcon, npath);
1576 if (IS_ERR(ses)) {
1577 rc = PTR_ERR(ses);
1578 root_ses = NULL;
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001579 goto out_free_path;
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001580 }
1581 } else {
1582 ses = tcon->ses;
1583 }
1584
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001585 rc = get_dfs_referral(xid, ses, cache_nlsc, tcon->remap, npath, &refs,
1586 &numrefs);
1587 if (!rc) {
1588 dump_refs(refs, numrefs);
1589 rc = update_cache_entry(npath, refs, numrefs);
1590 free_dfs_info_array(refs, numrefs);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001591 }
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001592
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001593 if (root_ses)
1594 cifs_put_smb_ses(root_ses);
1595
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001596out_free_path:
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001597 free_normalized_path(path, npath);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001598
1599out_free_xid:
1600 free_xid(xid);
1601 return rc;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001602}
1603
1604/*
1605 * Worker that will refresh DFS cache based on lowest TTL value from a DFS
1606 * referral.
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001607 */
1608static void refresh_cache_worker(struct work_struct *work)
1609{
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001610 struct vol_info *vi, *nvi;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001611 struct TCP_Server_Info *server;
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001612 LIST_HEAD(vols);
1613 LIST_HEAD(tcons);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001614 struct cifs_tcon *tcon, *ntcon;
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001615 int rc;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001616
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001617 /*
1618 * Find SMB volumes that are eligible (server->tcpStatus == CifsGood)
1619 * for refreshing.
1620 */
1621 spin_lock(&vol_list_lock);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001622 list_for_each_entry(vi, &vol_list, list) {
Paulo Alcantara (SUSE)345c1a42019-12-04 17:38:00 -03001623 server = get_tcp_server(&vi->smb_vol);
1624 if (!server)
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001625 continue;
Paulo Alcantara (SUSE)345c1a42019-12-04 17:38:00 -03001626
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001627 kref_get(&vi->refcnt);
1628 list_add_tail(&vi->rlist, &vols);
1629 put_tcp_server(server);
1630 }
1631 spin_unlock(&vol_list_lock);
1632
1633 /* Walk through all TCONs and refresh any expired cache entry */
1634 list_for_each_entry_safe(vi, nvi, &vols, rlist) {
1635 spin_lock(&vi->smb_vol_lock);
1636 server = get_tcp_server(&vi->smb_vol);
1637 spin_unlock(&vi->smb_vol_lock);
1638
1639 if (!server)
1640 goto next_vol;
1641
1642 get_tcons(server, &tcons);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001643 rc = 0;
1644
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001645 list_for_each_entry_safe(tcon, ntcon, &tcons, ulist) {
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001646 /*
1647 * Skip tcp server if any of its tcons failed to refresh
1648 * (possibily due to reconnects).
1649 */
1650 if (!rc)
1651 rc = refresh_tcon(vi, tcon);
1652
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001653 list_del_init(&tcon->ulist);
1654 cifs_put_tcon(tcon);
1655 }
Paulo Alcantara (SUSE)345c1a42019-12-04 17:38:00 -03001656
1657 put_tcp_server(server);
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001658
1659next_vol:
1660 list_del_init(&vi->rlist);
1661 kref_put(&vi->refcnt, vol_release);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001662 }
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001663
1664 spin_lock(&cache_ttl_lock);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001665 queue_delayed_work(dfscache_wq, &refresh_task, cache_ttl * HZ);
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001666 spin_unlock(&cache_ttl_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001667}