blob: b1fa30fefe1f6c263b14e6ce3d59048290c092cf [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"
Ronnie Sahlberg24e0a1e2020-12-10 00:06:02 -060021#include "fs_context.h"
Paulo Alcantara54be1f62018-11-14 16:01:21 -020022
23#include "dfs_cache.h"
24
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030025#define CACHE_HTABLE_SIZE 32
26#define CACHE_MAX_ENTRIES 64
Paulo Alcantara54be1f62018-11-14 16:01:21 -020027
28#define IS_INTERLINK_SET(v) ((v) & (DFSREF_REFERRAL_SERVER | \
29 DFSREF_STORAGE_SERVER))
30
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030031struct cache_dfs_tgt {
32 char *name;
Paulo Alcantara7548e1d2020-07-21 09:36:42 -030033 int path_consumed;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030034 struct list_head list;
Paulo Alcantara54be1f62018-11-14 16:01:21 -020035};
36
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030037struct cache_entry {
38 struct hlist_node hlist;
39 const char *path;
Paulo Alcantara5ff28362021-02-24 20:59:23 -030040 int hdr_flags; /* RESP_GET_DFS_REFERRAL.ReferralHeaderFlags */
41 int ttl; /* DFS_REREFERRAL_V3.TimeToLive */
42 int srvtype; /* DFS_REREFERRAL_V3.ServerType */
43 int ref_flags; /* DFS_REREFERRAL_V3.ReferralEntryFlags */
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030044 struct timespec64 etime;
Paulo Alcantara5ff28362021-02-24 20:59:23 -030045 int path_consumed; /* RESP_GET_DFS_REFERRAL.PathConsumed */
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030046 int numtgts;
47 struct list_head tlist;
48 struct cache_dfs_tgt *tgthint;
Paulo Alcantara54be1f62018-11-14 16:01:21 -020049};
50
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030051struct vol_info {
52 char *fullpath;
Ronnie Sahlberg3fa1c6d2020-12-09 23:07:12 -060053 spinlock_t ctx_lock;
54 struct smb3_fs_context ctx;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030055 char *mntdata;
56 struct list_head list;
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -030057 struct list_head rlist;
58 struct kref refcnt;
Paulo Alcantara54be1f62018-11-14 16:01:21 -020059};
60
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030061static struct kmem_cache *cache_slab __read_mostly;
62static struct workqueue_struct *dfscache_wq __read_mostly;
Paulo Alcantara54be1f62018-11-14 16:01:21 -020063
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030064static int cache_ttl;
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -030065static DEFINE_SPINLOCK(cache_ttl_lock);
66
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030067static struct nls_table *cache_nlsc;
Paulo Alcantara54be1f62018-11-14 16:01:21 -020068
69/*
70 * Number of entries in the cache
71 */
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -030072static atomic_t cache_count;
Paulo Alcantara54be1f62018-11-14 16:01:21 -020073
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030074static struct hlist_head cache_htable[CACHE_HTABLE_SIZE];
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -030075static DECLARE_RWSEM(htable_rw_lock);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030076
77static LIST_HEAD(vol_list);
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -030078static DEFINE_SPINLOCK(vol_list_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -020079
80static void refresh_cache_worker(struct work_struct *work);
81
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -030082static DECLARE_DELAYED_WORK(refresh_task, refresh_cache_worker);
83
Al Viro9cfdb1c2021-03-18 01:03:34 -040084static int get_normalized_path(const char *path, const char **npath)
Paulo Alcantara54be1f62018-11-14 16:01:21 -020085{
Paulo Alcantara (SUSE)ff2f7fc2019-12-04 17:38:01 -030086 if (!path || strlen(path) < 3 || (*path != '\\' && *path != '/'))
87 return -EINVAL;
Paulo Alcantara54be1f62018-11-14 16:01:21 -020088
Paulo Alcantara54be1f62018-11-14 16:01:21 -020089 if (*path == '\\') {
Al Viro9cfdb1c2021-03-18 01:03:34 -040090 *npath = path;
Paulo Alcantara54be1f62018-11-14 16:01:21 -020091 } else {
Al Viro9cfdb1c2021-03-18 01:03:34 -040092 char *s = kstrdup(path, GFP_KERNEL);
93 if (!s)
Paulo Alcantara54be1f62018-11-14 16:01:21 -020094 return -ENOMEM;
Al Viro9cfdb1c2021-03-18 01:03:34 -040095 convert_delimiter(s, '\\');
96 *npath = s;
Paulo Alcantara54be1f62018-11-14 16:01:21 -020097 }
98 return 0;
99}
100
Al Viro9cfdb1c2021-03-18 01:03:34 -0400101static inline void free_normalized_path(const char *path, const char *npath)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200102{
103 if (path != npath)
104 kfree(npath);
105}
106
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300107static inline bool cache_entry_expired(const struct cache_entry *ce)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200108{
109 struct timespec64 ts;
110
Stephen Rothwell54e4f732018-12-17 20:11:46 +1100111 ktime_get_coarse_real_ts64(&ts);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300112 return timespec64_compare(&ts, &ce->etime) >= 0;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200113}
114
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300115static inline void free_tgts(struct cache_entry *ce)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200116{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300117 struct cache_dfs_tgt *t, *n;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200118
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300119 list_for_each_entry_safe(t, n, &ce->tlist, list) {
120 list_del(&t->list);
121 kfree(t->name);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200122 kfree(t);
123 }
124}
125
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300126static inline void flush_cache_ent(struct cache_entry *ce)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200127{
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300128 hlist_del_init(&ce->hlist);
Paulo Alcantara (SUSE)199c6bd2019-12-04 17:37:59 -0300129 kfree(ce->path);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200130 free_tgts(ce);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300131 atomic_dec(&cache_count);
132 kmem_cache_free(cache_slab, ce);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200133}
134
135static void flush_cache_ents(void)
136{
137 int i;
138
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300139 for (i = 0; i < CACHE_HTABLE_SIZE; i++) {
140 struct hlist_head *l = &cache_htable[i];
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300141 struct hlist_node *n;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300142 struct cache_entry *ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200143
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300144 hlist_for_each_entry_safe(ce, n, l, hlist) {
145 if (!hlist_unhashed(&ce->hlist))
146 flush_cache_ent(ce);
147 }
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200148 }
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200149}
150
151/*
152 * dfs cache /proc file
153 */
154static int dfscache_proc_show(struct seq_file *m, void *v)
155{
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300156 int i;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300157 struct cache_entry *ce;
158 struct cache_dfs_tgt *t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200159
160 seq_puts(m, "DFS cache\n---------\n");
161
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300162 down_read(&htable_rw_lock);
163 for (i = 0; i < CACHE_HTABLE_SIZE; i++) {
164 struct hlist_head *l = &cache_htable[i];
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200165
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300166 hlist_for_each_entry(ce, l, hlist) {
167 if (hlist_unhashed(&ce->hlist))
168 continue;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200169
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300170 seq_printf(m,
Paulo Alcantara5ff28362021-02-24 20:59:23 -0300171 "cache entry: path=%s,type=%s,ttl=%d,etime=%ld,hdr_flags=0x%x,ref_flags=0x%x,interlink=%s,path_consumed=%d,expired=%s\n",
172 ce->path, ce->srvtype == DFS_TYPE_ROOT ? "root" : "link",
173 ce->ttl, ce->etime.tv_nsec, ce->ref_flags, ce->hdr_flags,
174 IS_INTERLINK_SET(ce->hdr_flags) ? "yes" : "no",
175 ce->path_consumed, cache_entry_expired(ce) ? "yes" : "no");
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300176
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{
Paulo Alcantara5ff28362021-02-24 20:59:23 -0300238 cifs_dbg(FYI, "cache entry: path=%s,type=%s,ttl=%d,etime=%ld,hdr_flags=0x%x,ref_flags=0x%x,interlink=%s,path_consumed=%d,expired=%s\n",
Joe Perchesa0a30362020-04-14 22:42:53 -0700239 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,
Paulo Alcantara5ff28362021-02-24 20:59:23 -0300242 ce->hdr_flags, ce->ref_flags,
243 IS_INTERLINK_SET(ce->hdr_flags) ? "yes" : "no",
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300244 ce->path_consumed,
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200245 cache_entry_expired(ce) ? "yes" : "no");
246 dump_tgts(ce);
247}
248
249static inline void dump_refs(const struct dfs_info3_param *refs, int numrefs)
250{
251 int i;
252
253 cifs_dbg(FYI, "DFS referrals returned by the server:\n");
254 for (i = 0; i < numrefs; i++) {
255 const struct dfs_info3_param *ref = &refs[i];
256
257 cifs_dbg(FYI,
258 "\n"
259 "flags: 0x%x\n"
260 "path_consumed: %d\n"
261 "server_type: 0x%x\n"
262 "ref_flag: 0x%x\n"
263 "path_name: %s\n"
264 "node_name: %s\n"
265 "ttl: %d (%dm)\n",
266 ref->flags, ref->path_consumed, ref->server_type,
267 ref->ref_flag, ref->path_name, ref->node_name,
268 ref->ttl, ref->ttl / 60);
269 }
270}
271#else
272#define dump_tgts(e)
273#define dump_ce(e)
274#define dump_refs(r, n)
275#endif
276
277/**
278 * dfs_cache_init - Initialize DFS referral cache.
279 *
280 * Return zero if initialized successfully, otherwise non-zero.
281 */
282int dfs_cache_init(void)
283{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300284 int rc;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200285 int i;
286
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300287 dfscache_wq = alloc_workqueue("cifs-dfscache",
288 WQ_FREEZABLE | WQ_MEM_RECLAIM, 1);
289 if (!dfscache_wq)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200290 return -ENOMEM;
291
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300292 cache_slab = kmem_cache_create("cifs_dfs_cache",
293 sizeof(struct cache_entry), 0,
294 SLAB_HWCACHE_ALIGN, NULL);
295 if (!cache_slab) {
296 rc = -ENOMEM;
297 goto out_destroy_wq;
298 }
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200299
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300300 for (i = 0; i < CACHE_HTABLE_SIZE; i++)
301 INIT_HLIST_HEAD(&cache_htable[i]);
302
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300303 atomic_set(&cache_count, 0);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300304 cache_nlsc = load_nls_default();
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200305
306 cifs_dbg(FYI, "%s: initialized DFS referral cache\n", __func__);
307 return 0;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300308
309out_destroy_wq:
310 destroy_workqueue(dfscache_wq);
311 return rc;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200312}
313
314static inline unsigned int cache_entry_hash(const void *data, int size)
315{
316 unsigned int h;
317
318 h = jhash(data, size, 0);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300319 return h & (CACHE_HTABLE_SIZE - 1);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200320}
321
322/* Check whether second path component of @path is SYSVOL or NETLOGON */
323static inline bool is_sysvol_or_netlogon(const char *path)
324{
325 const char *s;
326 char sep = path[0];
327
328 s = strchr(path + 1, sep) + 1;
329 return !strncasecmp(s, "sysvol", strlen("sysvol")) ||
330 !strncasecmp(s, "netlogon", strlen("netlogon"));
331}
332
333/* Return target hint of a DFS cache entry */
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300334static inline char *get_tgt_name(const struct cache_entry *ce)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200335{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300336 struct cache_dfs_tgt *t = ce->tgthint;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200337
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300338 return t ? t->name : ERR_PTR(-ENOENT);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200339}
340
341/* Return expire time out of a new entry's TTL */
342static inline struct timespec64 get_expire_time(int ttl)
343{
344 struct timespec64 ts = {
345 .tv_sec = ttl,
346 .tv_nsec = 0,
347 };
Stephen Rothwell54e4f732018-12-17 20:11:46 +1100348 struct timespec64 now;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200349
Stephen Rothwell54e4f732018-12-17 20:11:46 +1100350 ktime_get_coarse_real_ts64(&now);
351 return timespec64_add(now, ts);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200352}
353
354/* Allocate a new DFS target */
Paulo Alcantara7548e1d2020-07-21 09:36:42 -0300355static struct cache_dfs_tgt *alloc_target(const char *name, int path_consumed)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200356{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300357 struct cache_dfs_tgt *t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200358
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300359 t = kmalloc(sizeof(*t), GFP_ATOMIC);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200360 if (!t)
361 return ERR_PTR(-ENOMEM);
Al Viro8d767222021-03-05 15:02:34 -0500362 t->name = kstrdup(name, GFP_ATOMIC);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300363 if (!t->name) {
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200364 kfree(t);
365 return ERR_PTR(-ENOMEM);
366 }
Paulo Alcantara7548e1d2020-07-21 09:36:42 -0300367 t->path_consumed = path_consumed;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300368 INIT_LIST_HEAD(&t->list);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200369 return t;
370}
371
372/*
373 * Copy DFS referral information to a cache entry and conditionally update
374 * target hint.
375 */
376static int copy_ref_data(const struct dfs_info3_param *refs, int numrefs,
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300377 struct cache_entry *ce, const char *tgthint)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200378{
379 int i;
380
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300381 ce->ttl = refs[0].ttl;
382 ce->etime = get_expire_time(ce->ttl);
383 ce->srvtype = refs[0].server_type;
Paulo Alcantara5ff28362021-02-24 20:59:23 -0300384 ce->hdr_flags = refs[0].flags;
385 ce->ref_flags = refs[0].ref_flag;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300386 ce->path_consumed = refs[0].path_consumed;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200387
388 for (i = 0; i < numrefs; i++) {
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300389 struct cache_dfs_tgt *t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200390
Paulo Alcantara7548e1d2020-07-21 09:36:42 -0300391 t = alloc_target(refs[i].node_name, refs[i].path_consumed);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200392 if (IS_ERR(t)) {
393 free_tgts(ce);
394 return PTR_ERR(t);
395 }
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300396 if (tgthint && !strcasecmp(t->name, tgthint)) {
397 list_add(&t->list, &ce->tlist);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200398 tgthint = NULL;
399 } else {
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300400 list_add_tail(&t->list, &ce->tlist);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200401 }
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300402 ce->numtgts++;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200403 }
404
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300405 ce->tgthint = list_first_entry_or_null(&ce->tlist,
406 struct cache_dfs_tgt, list);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200407
408 return 0;
409}
410
411/* Allocate a new cache entry */
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300412static struct cache_entry *alloc_cache_entry(const char *path,
413 const struct dfs_info3_param *refs,
414 int numrefs)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200415{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300416 struct cache_entry *ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200417 int rc;
418
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300419 ce = kmem_cache_zalloc(cache_slab, GFP_KERNEL);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200420 if (!ce)
421 return ERR_PTR(-ENOMEM);
422
Al Viro8d767222021-03-05 15:02:34 -0500423 ce->path = kstrdup(path, GFP_KERNEL);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300424 if (!ce->path) {
425 kmem_cache_free(cache_slab, ce);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200426 return ERR_PTR(-ENOMEM);
427 }
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300428 INIT_HLIST_NODE(&ce->hlist);
429 INIT_LIST_HEAD(&ce->tlist);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200430
431 rc = copy_ref_data(refs, numrefs, ce, NULL);
432 if (rc) {
Paulo Alcantara (SUSE)199c6bd2019-12-04 17:37:59 -0300433 kfree(ce->path);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300434 kmem_cache_free(cache_slab, ce);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200435 ce = ERR_PTR(rc);
436 }
437 return ce;
438}
439
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300440/* Must be called with htable_rw_lock held */
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200441static void remove_oldest_entry(void)
442{
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300443 int i;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300444 struct cache_entry *ce;
445 struct cache_entry *to_del = NULL;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200446
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300447 for (i = 0; i < CACHE_HTABLE_SIZE; i++) {
448 struct hlist_head *l = &cache_htable[i];
449
450 hlist_for_each_entry(ce, l, hlist) {
451 if (hlist_unhashed(&ce->hlist))
452 continue;
453 if (!to_del || timespec64_compare(&ce->etime,
454 &to_del->etime) < 0)
455 to_del = ce;
456 }
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 if (!to_del) {
Joe Perchesa0a30362020-04-14 22:42:53 -0700460 cifs_dbg(FYI, "%s: no entry to remove\n", __func__);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300461 return;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200462 }
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300463
Joe Perchesa0a30362020-04-14 22:42:53 -0700464 cifs_dbg(FYI, "%s: removing entry\n", __func__);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200465 dump_ce(to_del);
466 flush_cache_ent(to_del);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200467}
468
469/* Add a new DFS cache entry */
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300470static int add_cache_entry(const char *path, unsigned int hash,
471 struct dfs_info3_param *refs, int numrefs)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200472{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300473 struct cache_entry *ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200474
475 ce = alloc_cache_entry(path, refs, numrefs);
476 if (IS_ERR(ce))
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300477 return PTR_ERR(ce);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200478
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -0300479 spin_lock(&cache_ttl_lock);
480 if (!cache_ttl) {
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300481 cache_ttl = ce->ttl;
482 queue_delayed_work(dfscache_wq, &refresh_task, cache_ttl * HZ);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200483 } else {
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300484 cache_ttl = min_t(int, cache_ttl, ce->ttl);
485 mod_delayed_work(dfscache_wq, &refresh_task, cache_ttl * HZ);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200486 }
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -0300487 spin_unlock(&cache_ttl_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200488
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300489 down_write(&htable_rw_lock);
490 hlist_add_head(&ce->hlist, &cache_htable[hash]);
491 dump_ce(ce);
492 up_write(&htable_rw_lock);
493
494 return 0;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200495}
496
Paulo Alcantara2e5de422020-07-21 09:36:39 -0300497static struct cache_entry *__lookup_cache_entry(const char *path)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200498{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300499 struct cache_entry *ce;
500 unsigned int h;
501 bool found = false;
502
503 h = cache_entry_hash(path, strlen(path));
504
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300505 hlist_for_each_entry(ce, &cache_htable[h], hlist) {
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300506 if (!strcasecmp(path, ce->path)) {
507 found = true;
508 dump_ce(ce);
509 break;
510 }
511 }
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300512
513 if (!found)
514 ce = ERR_PTR(-ENOENT);
Paulo Alcantara2e5de422020-07-21 09:36:39 -0300515 return ce;
516}
517
518/*
519 * Find a DFS cache entry in hash table and optionally check prefix path against
520 * @path.
521 * Use whole path components in the match.
522 * Must be called with htable_rw_lock held.
523 *
524 * Return ERR_PTR(-ENOENT) if the entry is not found.
525 */
526static struct cache_entry *lookup_cache_entry(const char *path, unsigned int *hash)
527{
528 struct cache_entry *ce = ERR_PTR(-ENOENT);
529 unsigned int h;
530 int cnt = 0;
531 char *npath;
532 char *s, *e;
533 char sep;
534
Al Viro8d767222021-03-05 15:02:34 -0500535 npath = kstrdup(path, GFP_KERNEL);
Paulo Alcantara2e5de422020-07-21 09:36:39 -0300536 if (!npath)
537 return ERR_PTR(-ENOMEM);
538
539 s = npath;
540 sep = *npath;
541 while ((s = strchr(s, sep)) && ++cnt < 3)
542 s++;
543
544 if (cnt < 3) {
545 h = cache_entry_hash(path, strlen(path));
546 ce = __lookup_cache_entry(path);
547 goto out;
548 }
549 /*
550 * Handle paths that have more than two path components and are a complete prefix of the DFS
551 * referral request path (@path).
552 *
553 * See MS-DFSC 3.2.5.5 "Receiving a Root Referral Request or Link Referral Request".
554 */
555 h = cache_entry_hash(npath, strlen(npath));
556 e = npath + strlen(npath) - 1;
557 while (e > s) {
558 char tmp;
559
560 /* skip separators */
561 while (e > s && *e == sep)
562 e--;
563 if (e == s)
564 goto out;
565
566 tmp = *(e+1);
567 *(e+1) = 0;
568
569 ce = __lookup_cache_entry(npath);
570 if (!IS_ERR(ce)) {
571 h = cache_entry_hash(npath, strlen(npath));
572 break;
573 }
574
575 *(e+1) = tmp;
576 /* backward until separator */
577 while (e > s && *e != sep)
578 e--;
579 }
580out:
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300581 if (hash)
582 *hash = h;
Paulo Alcantara2e5de422020-07-21 09:36:39 -0300583 kfree(npath);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300584 return ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200585}
586
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -0300587static void __vol_release(struct vol_info *vi)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200588{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300589 kfree(vi->fullpath);
590 kfree(vi->mntdata);
Ronnie Sahlbergc741cba2020-12-14 16:40:16 +1000591 smb3_cleanup_fs_context_contents(&vi->ctx);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200592 kfree(vi);
593}
594
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -0300595static void vol_release(struct kref *kref)
596{
597 struct vol_info *vi = container_of(kref, struct vol_info, refcnt);
598
599 spin_lock(&vol_list_lock);
600 list_del(&vi->list);
601 spin_unlock(&vol_list_lock);
602 __vol_release(vi);
603}
604
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200605static inline void free_vol_list(void)
606{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300607 struct vol_info *vi, *nvi;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200608
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -0300609 list_for_each_entry_safe(vi, nvi, &vol_list, list) {
610 list_del_init(&vi->list);
611 __vol_release(vi);
612 }
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200613}
614
615/**
616 * dfs_cache_destroy - destroy DFS referral cache
617 */
618void dfs_cache_destroy(void)
619{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300620 cancel_delayed_work_sync(&refresh_task);
621 unload_nls(cache_nlsc);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200622 free_vol_list();
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200623 flush_cache_ents();
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300624 kmem_cache_destroy(cache_slab);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300625 destroy_workqueue(dfscache_wq);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200626
627 cifs_dbg(FYI, "%s: destroyed DFS referral cache\n", __func__);
628}
629
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300630/* Must be called with htable_rw_lock held */
631static int __update_cache_entry(const char *path,
632 const struct dfs_info3_param *refs,
633 int numrefs)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200634{
635 int rc;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300636 struct cache_entry *ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200637 char *s, *th = NULL;
638
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300639 ce = lookup_cache_entry(path, NULL);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200640 if (IS_ERR(ce))
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300641 return PTR_ERR(ce);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200642
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300643 if (ce->tgthint) {
644 s = ce->tgthint->name;
Al Viro8d767222021-03-05 15:02:34 -0500645 th = kstrdup(s, GFP_ATOMIC);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200646 if (!th)
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300647 return -ENOMEM;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200648 }
649
650 free_tgts(ce);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300651 ce->numtgts = 0;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200652
653 rc = copy_ref_data(refs, numrefs, ce, th);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300654
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200655 kfree(th);
656
YueHaibingeecfc572020-01-17 10:21:56 +0800657 return rc;
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300658}
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200659
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300660static int get_dfs_referral(const unsigned int xid, struct cifs_ses *ses,
661 const struct nls_table *nls_codepage, int remap,
662 const char *path, struct dfs_info3_param **refs,
663 int *numrefs)
664{
665 cifs_dbg(FYI, "%s: get an DFS referral for %s\n", __func__, path);
666
667 if (!ses || !ses->server || !ses->server->ops->get_dfs_refer)
668 return -EOPNOTSUPP;
669 if (unlikely(!nls_codepage))
670 return -EINVAL;
671
672 *refs = NULL;
673 *numrefs = 0;
674
675 return ses->server->ops->get_dfs_refer(xid, ses, path, refs, numrefs,
676 nls_codepage, remap);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200677}
678
679/* Update an expired cache entry by getting a new DFS referral from server */
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300680static int update_cache_entry(const char *path,
681 const struct dfs_info3_param *refs,
682 int numrefs)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200683{
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300684
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200685 int rc;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200686
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300687 down_write(&htable_rw_lock);
688 rc = __update_cache_entry(path, refs, numrefs);
689 up_write(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200690
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300691 return rc;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200692}
693
694/*
695 * Find, create or update a DFS cache entry.
696 *
697 * If the entry wasn't found, it will create a new one. Or if it was found but
698 * expired, then it will update the entry accordingly.
699 *
700 * For interlinks, __cifs_dfs_mount() and expand_dfs_referral() are supposed to
701 * handle them properly.
702 */
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300703static int __dfs_cache_find(const unsigned int xid, struct cifs_ses *ses,
704 const struct nls_table *nls_codepage, int remap,
705 const char *path, bool noreq)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200706{
707 int rc;
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300708 unsigned int hash;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300709 struct cache_entry *ce;
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300710 struct dfs_info3_param *refs = NULL;
711 int numrefs = 0;
712 bool newent = false;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200713
714 cifs_dbg(FYI, "%s: search path: %s\n", __func__, path);
715
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300716 down_read(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200717
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300718 ce = lookup_cache_entry(path, &hash);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200719
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300720 /*
721 * If @noreq is set, no requests will be sent to the server. Just return
722 * the cache entry.
723 */
724 if (noreq) {
725 up_read(&htable_rw_lock);
Chen Zhou050d2a82020-01-22 18:20:30 +0800726 return PTR_ERR_OR_ZERO(ce);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200727 }
728
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300729 if (!IS_ERR(ce)) {
730 if (!cache_entry_expired(ce)) {
731 dump_ce(ce);
732 up_read(&htable_rw_lock);
733 return 0;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200734 }
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300735 } else {
736 newent = true;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200737 }
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300738
739 up_read(&htable_rw_lock);
740
741 /*
742 * No entry was found.
743 *
744 * Request a new DFS referral in order to create a new cache entry, or
745 * updating an existing one.
746 */
747 rc = get_dfs_referral(xid, ses, nls_codepage, remap, path,
748 &refs, &numrefs);
749 if (rc)
750 return rc;
751
752 dump_refs(refs, numrefs);
753
754 if (!newent) {
755 rc = update_cache_entry(path, refs, numrefs);
756 goto out_free_refs;
757 }
758
759 if (atomic_read(&cache_count) >= CACHE_MAX_ENTRIES) {
Joe Perchesa0a30362020-04-14 22:42:53 -0700760 cifs_dbg(FYI, "%s: reached max cache size (%d)\n",
761 __func__, CACHE_MAX_ENTRIES);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300762 down_write(&htable_rw_lock);
763 remove_oldest_entry();
764 up_write(&htable_rw_lock);
765 }
766
767 rc = add_cache_entry(path, hash, refs, numrefs);
768 if (!rc)
769 atomic_inc(&cache_count);
770
771out_free_refs:
772 free_dfs_info_array(refs, numrefs);
773 return rc;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200774}
775
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300776/*
777 * Set up a DFS referral from a given cache entry.
778 *
779 * Must be called with htable_rw_lock held.
780 */
781static int setup_referral(const char *path, struct cache_entry *ce,
782 struct dfs_info3_param *ref, const char *target)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200783{
784 int rc;
785
786 cifs_dbg(FYI, "%s: set up new ref\n", __func__);
787
788 memset(ref, 0, sizeof(*ref));
789
Al Viro8d767222021-03-05 15:02:34 -0500790 ref->path_name = kstrdup(path, GFP_ATOMIC);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200791 if (!ref->path_name)
792 return -ENOMEM;
793
Al Viro8d767222021-03-05 15:02:34 -0500794 ref->node_name = kstrdup(target, GFP_ATOMIC);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200795 if (!ref->node_name) {
796 rc = -ENOMEM;
797 goto err_free_path;
798 }
799
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300800 ref->path_consumed = ce->path_consumed;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300801 ref->ttl = ce->ttl;
802 ref->server_type = ce->srvtype;
Paulo Alcantara5ff28362021-02-24 20:59:23 -0300803 ref->ref_flag = ce->ref_flags;
804 ref->flags = ce->hdr_flags;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200805
806 return 0;
807
808err_free_path:
809 kfree(ref->path_name);
810 ref->path_name = NULL;
811 return rc;
812}
813
814/* Return target list of a DFS cache entry */
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300815static int get_targets(struct cache_entry *ce, struct dfs_cache_tgt_list *tl)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200816{
817 int rc;
818 struct list_head *head = &tl->tl_list;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300819 struct cache_dfs_tgt *t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200820 struct dfs_cache_tgt_iterator *it, *nit;
821
822 memset(tl, 0, sizeof(*tl));
823 INIT_LIST_HEAD(head);
824
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300825 list_for_each_entry(t, &ce->tlist, list) {
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300826 it = kzalloc(sizeof(*it), GFP_ATOMIC);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200827 if (!it) {
828 rc = -ENOMEM;
829 goto err_free_it;
830 }
831
Al Viro8d767222021-03-05 15:02:34 -0500832 it->it_name = kstrdup(t->name, GFP_ATOMIC);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200833 if (!it->it_name) {
Dan Carpenterc715f892019-01-05 21:18:03 +0300834 kfree(it);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200835 rc = -ENOMEM;
836 goto err_free_it;
837 }
Paulo Alcantara7548e1d2020-07-21 09:36:42 -0300838 it->it_path_consumed = t->path_consumed;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200839
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300840 if (ce->tgthint == t)
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200841 list_add(&it->it_list, head);
842 else
843 list_add_tail(&it->it_list, head);
844 }
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300845
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300846 tl->tl_numtgts = ce->numtgts;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200847
848 return 0;
849
850err_free_it:
851 list_for_each_entry_safe(it, nit, head, it_list) {
852 kfree(it->it_name);
853 kfree(it);
854 }
855 return rc;
856}
857
858/**
859 * dfs_cache_find - find a DFS cache entry
860 *
861 * If it doesn't find the cache entry, then it will get a DFS referral
862 * for @path and create a new entry.
863 *
864 * In case the cache entry exists but expired, it will get a DFS referral
865 * for @path and then update the respective cache entry.
866 *
867 * These parameters are passed down to the get_dfs_refer() call if it
868 * needs to be issued:
869 * @xid: syscall xid
870 * @ses: smb session to issue the request on
871 * @nls_codepage: charset conversion
872 * @remap: path character remapping type
873 * @path: path to lookup in DFS referral cache.
874 *
875 * @ref: when non-NULL, store single DFS referral result in it.
876 * @tgt_list: when non-NULL, store complete DFS target list in it.
877 *
878 * Return zero if the target was found, otherwise non-zero.
879 */
880int dfs_cache_find(const unsigned int xid, struct cifs_ses *ses,
881 const struct nls_table *nls_codepage, int remap,
882 const char *path, struct dfs_info3_param *ref,
883 struct dfs_cache_tgt_list *tgt_list)
884{
885 int rc;
Al Viro9cfdb1c2021-03-18 01:03:34 -0400886 const char *npath;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300887 struct cache_entry *ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200888
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200889 rc = get_normalized_path(path, &npath);
890 if (rc)
891 return rc;
892
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300893 rc = __dfs_cache_find(xid, ses, nls_codepage, remap, npath, false);
894 if (rc)
895 goto out_free_path;
896
897 down_read(&htable_rw_lock);
898
899 ce = lookup_cache_entry(npath, NULL);
900 if (IS_ERR(ce)) {
901 up_read(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200902 rc = PTR_ERR(ce);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300903 goto out_free_path;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200904 }
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300905
906 if (ref)
907 rc = setup_referral(path, ce, ref, get_tgt_name(ce));
908 else
909 rc = 0;
910 if (!rc && tgt_list)
911 rc = get_targets(ce, tgt_list);
912
913 up_read(&htable_rw_lock);
914
915out_free_path:
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200916 free_normalized_path(path, npath);
917 return rc;
918}
919
920/**
921 * dfs_cache_noreq_find - find a DFS cache entry without sending any requests to
922 * the currently connected server.
923 *
924 * NOTE: This function will neither update a cache entry in case it was
925 * expired, nor create a new cache entry if @path hasn't been found. It heavily
926 * relies on an existing cache entry.
927 *
928 * @path: path to lookup in the DFS referral cache.
929 * @ref: when non-NULL, store single DFS referral result in it.
930 * @tgt_list: when non-NULL, store complete DFS target list in it.
931 *
932 * Return 0 if successful.
933 * Return -ENOENT if the entry was not found.
934 * Return non-zero for other errors.
935 */
936int dfs_cache_noreq_find(const char *path, struct dfs_info3_param *ref,
937 struct dfs_cache_tgt_list *tgt_list)
938{
939 int rc;
Al Viro9cfdb1c2021-03-18 01:03:34 -0400940 const char *npath;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300941 struct cache_entry *ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200942
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200943 rc = get_normalized_path(path, &npath);
944 if (rc)
945 return rc;
946
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300947 cifs_dbg(FYI, "%s: path: %s\n", __func__, npath);
948
949 down_read(&htable_rw_lock);
950
951 ce = lookup_cache_entry(npath, NULL);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200952 if (IS_ERR(ce)) {
953 rc = PTR_ERR(ce);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300954 goto out_unlock;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200955 }
956
957 if (ref)
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300958 rc = setup_referral(path, ce, ref, get_tgt_name(ce));
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200959 else
960 rc = 0;
961 if (!rc && tgt_list)
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300962 rc = get_targets(ce, tgt_list);
963
964out_unlock:
965 up_read(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200966 free_normalized_path(path, npath);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -0300967
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200968 return rc;
969}
970
971/**
972 * dfs_cache_update_tgthint - update target hint of a DFS cache entry
973 *
974 * If it doesn't find the cache entry, then it will get a DFS referral for @path
975 * and create a new entry.
976 *
977 * In case the cache entry exists but expired, it will get a DFS referral
978 * for @path and then update the respective cache entry.
979 *
980 * @xid: syscall id
981 * @ses: smb session
982 * @nls_codepage: charset conversion
983 * @remap: type of character remapping for paths
984 * @path: path to lookup in DFS referral cache.
985 * @it: DFS target iterator
986 *
987 * Return zero if the target hint was updated successfully, otherwise non-zero.
988 */
989int dfs_cache_update_tgthint(const unsigned int xid, struct cifs_ses *ses,
990 const struct nls_table *nls_codepage, int remap,
991 const char *path,
992 const struct dfs_cache_tgt_iterator *it)
993{
994 int rc;
Al Viro9cfdb1c2021-03-18 01:03:34 -0400995 const char *npath;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -0300996 struct cache_entry *ce;
997 struct cache_dfs_tgt *t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200998
Paulo Alcantara54be1f62018-11-14 16:01:21 -0200999 rc = get_normalized_path(path, &npath);
1000 if (rc)
1001 return rc;
1002
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001003 cifs_dbg(FYI, "%s: update target hint - path: %s\n", __func__, npath);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001004
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001005 rc = __dfs_cache_find(xid, ses, nls_codepage, remap, npath, false);
1006 if (rc)
1007 goto out_free_path;
1008
1009 down_write(&htable_rw_lock);
1010
1011 ce = lookup_cache_entry(npath, NULL);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001012 if (IS_ERR(ce)) {
1013 rc = PTR_ERR(ce);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001014 goto out_unlock;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001015 }
1016
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001017 t = ce->tgthint;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001018
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001019 if (likely(!strcasecmp(it->it_name, t->name)))
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001020 goto out_unlock;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001021
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001022 list_for_each_entry(t, &ce->tlist, list) {
1023 if (!strcasecmp(t->name, it->it_name)) {
1024 ce->tgthint = t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001025 cifs_dbg(FYI, "%s: new target hint: %s\n", __func__,
1026 it->it_name);
1027 break;
1028 }
1029 }
1030
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001031out_unlock:
1032 up_write(&htable_rw_lock);
1033out_free_path:
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001034 free_normalized_path(path, npath);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001035
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001036 return rc;
1037}
1038
1039/**
1040 * dfs_cache_noreq_update_tgthint - update target hint of a DFS cache entry
1041 * without sending any requests to the currently connected server.
1042 *
1043 * NOTE: This function will neither update a cache entry in case it was
1044 * expired, nor create a new cache entry if @path hasn't been found. It heavily
1045 * relies on an existing cache entry.
1046 *
1047 * @path: path to lookup in DFS referral cache.
1048 * @it: target iterator which contains the target hint to update the cache
1049 * entry with.
1050 *
1051 * Return zero if the target hint was updated successfully, otherwise non-zero.
1052 */
1053int dfs_cache_noreq_update_tgthint(const char *path,
1054 const struct dfs_cache_tgt_iterator *it)
1055{
1056 int rc;
Al Viro9cfdb1c2021-03-18 01:03:34 -04001057 const char *npath;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001058 struct cache_entry *ce;
1059 struct cache_dfs_tgt *t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001060
Paulo Alcantara (SUSE)ff2f7fc2019-12-04 17:38:01 -03001061 if (!it)
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001062 return -EINVAL;
1063
1064 rc = get_normalized_path(path, &npath);
1065 if (rc)
1066 return rc;
1067
1068 cifs_dbg(FYI, "%s: path: %s\n", __func__, npath);
1069
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001070 down_write(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001071
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001072 ce = lookup_cache_entry(npath, NULL);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001073 if (IS_ERR(ce)) {
1074 rc = PTR_ERR(ce);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001075 goto out_unlock;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001076 }
1077
1078 rc = 0;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001079 t = ce->tgthint;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001080
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001081 if (unlikely(!strcasecmp(it->it_name, t->name)))
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001082 goto out_unlock;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001083
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001084 list_for_each_entry(t, &ce->tlist, list) {
1085 if (!strcasecmp(t->name, it->it_name)) {
1086 ce->tgthint = t;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001087 cifs_dbg(FYI, "%s: new target hint: %s\n", __func__,
1088 it->it_name);
1089 break;
1090 }
1091 }
1092
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001093out_unlock:
1094 up_write(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001095 free_normalized_path(path, npath);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001096
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001097 return rc;
1098}
1099
1100/**
1101 * dfs_cache_get_tgt_referral - returns a DFS referral (@ref) from a given
1102 * target iterator (@it).
1103 *
1104 * @path: path to lookup in DFS referral cache.
1105 * @it: DFS target iterator.
1106 * @ref: DFS referral pointer to set up the gathered information.
1107 *
1108 * Return zero if the DFS referral was set up correctly, otherwise non-zero.
1109 */
1110int dfs_cache_get_tgt_referral(const char *path,
1111 const struct dfs_cache_tgt_iterator *it,
1112 struct dfs_info3_param *ref)
1113{
1114 int rc;
Al Viro9cfdb1c2021-03-18 01:03:34 -04001115 const char *npath;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001116 struct cache_entry *ce;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001117
1118 if (!it || !ref)
1119 return -EINVAL;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001120
1121 rc = get_normalized_path(path, &npath);
1122 if (rc)
1123 return rc;
1124
1125 cifs_dbg(FYI, "%s: path: %s\n", __func__, npath);
1126
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001127 down_read(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001128
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001129 ce = lookup_cache_entry(npath, NULL);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001130 if (IS_ERR(ce)) {
1131 rc = PTR_ERR(ce);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001132 goto out_unlock;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001133 }
1134
1135 cifs_dbg(FYI, "%s: target name: %s\n", __func__, it->it_name);
1136
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001137 rc = setup_referral(path, ce, ref, it->it_name);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001138
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001139out_unlock:
1140 up_read(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001141 free_normalized_path(path, npath);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001142
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001143 return rc;
1144}
1145
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001146/**
Ronnie Sahlberg24e0a1e2020-12-10 00:06:02 -06001147 * dfs_cache_add_vol - add a cifs context during mount() that will be handled by
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001148 * DFS cache refresh worker.
1149 *
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001150 * @mntdata: mount data.
Ronnie Sahlberg3fa1c6d2020-12-09 23:07:12 -06001151 * @ctx: cifs context.
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001152 * @fullpath: origin full path.
1153 *
Ronnie Sahlberg24e0a1e2020-12-10 00:06:02 -06001154 * Return zero if context was set up correctly, otherwise non-zero.
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001155 */
Ronnie Sahlberg3fa1c6d2020-12-09 23:07:12 -06001156int dfs_cache_add_vol(char *mntdata, struct smb3_fs_context *ctx, const char *fullpath)
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001157{
1158 int rc;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001159 struct vol_info *vi;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001160
Ronnie Sahlberg3fa1c6d2020-12-09 23:07:12 -06001161 if (!ctx || !fullpath || !mntdata)
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001162 return -EINVAL;
1163
1164 cifs_dbg(FYI, "%s: fullpath: %s\n", __func__, fullpath);
1165
1166 vi = kzalloc(sizeof(*vi), GFP_KERNEL);
1167 if (!vi)
1168 return -ENOMEM;
1169
Al Viro8d767222021-03-05 15:02:34 -05001170 vi->fullpath = kstrdup(fullpath, GFP_KERNEL);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001171 if (!vi->fullpath) {
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001172 rc = -ENOMEM;
1173 goto err_free_vi;
1174 }
1175
Ronnie Sahlberg837e3a12020-11-02 09:36:24 +10001176 rc = smb3_fs_context_dup(&vi->ctx, ctx);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001177 if (rc)
1178 goto err_free_fullpath;
1179
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001180 vi->mntdata = mntdata;
Ronnie Sahlberg3fa1c6d2020-12-09 23:07:12 -06001181 spin_lock_init(&vi->ctx_lock);
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001182 kref_init(&vi->refcnt);
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001183
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001184 spin_lock(&vol_list_lock);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001185 list_add_tail(&vi->list, &vol_list);
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001186 spin_unlock(&vol_list_lock);
1187
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001188 return 0;
1189
1190err_free_fullpath:
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001191 kfree(vi->fullpath);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001192err_free_vi:
1193 kfree(vi);
1194 return rc;
1195}
1196
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001197/* Must be called with vol_list_lock held */
1198static struct vol_info *find_vol(const char *fullpath)
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001199{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001200 struct vol_info *vi;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001201
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001202 list_for_each_entry(vi, &vol_list, list) {
1203 cifs_dbg(FYI, "%s: vi->fullpath: %s\n", __func__, vi->fullpath);
1204 if (!strcasecmp(vi->fullpath, fullpath))
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001205 return vi;
1206 }
1207 return ERR_PTR(-ENOENT);
1208}
1209
1210/**
1211 * dfs_cache_update_vol - update vol info in DFS cache after failover
1212 *
1213 * @fullpath: fullpath to look up in volume list.
1214 * @server: TCP ses pointer.
1215 *
1216 * Return zero if volume was updated, otherwise non-zero.
1217 */
1218int dfs_cache_update_vol(const char *fullpath, struct TCP_Server_Info *server)
1219{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001220 struct vol_info *vi;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001221
1222 if (!fullpath || !server)
1223 return -EINVAL;
1224
1225 cifs_dbg(FYI, "%s: fullpath: %s\n", __func__, fullpath);
1226
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001227 spin_lock(&vol_list_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001228 vi = find_vol(fullpath);
1229 if (IS_ERR(vi)) {
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001230 spin_unlock(&vol_list_lock);
1231 return PTR_ERR(vi);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001232 }
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001233 kref_get(&vi->refcnt);
1234 spin_unlock(&vol_list_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001235
1236 cifs_dbg(FYI, "%s: updating volume info\n", __func__);
Ronnie Sahlberg3fa1c6d2020-12-09 23:07:12 -06001237 spin_lock(&vi->ctx_lock);
1238 memcpy(&vi->ctx.dstaddr, &server->dstaddr,
1239 sizeof(vi->ctx.dstaddr));
1240 spin_unlock(&vi->ctx_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001241
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001242 kref_put(&vi->refcnt, vol_release);
1243
1244 return 0;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001245}
1246
1247/**
1248 * dfs_cache_del_vol - remove volume info in DFS cache during umount()
1249 *
1250 * @fullpath: fullpath to look up in volume list.
1251 */
1252void dfs_cache_del_vol(const char *fullpath)
1253{
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001254 struct vol_info *vi;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001255
1256 if (!fullpath || !*fullpath)
1257 return;
1258
1259 cifs_dbg(FYI, "%s: fullpath: %s\n", __func__, fullpath);
1260
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001261 spin_lock(&vol_list_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001262 vi = find_vol(fullpath);
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001263 spin_unlock(&vol_list_lock);
1264
Tom Rix77b6ec02021-01-05 12:21:26 -08001265 if (!IS_ERR(vi))
1266 kref_put(&vi->refcnt, vol_release);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001267}
1268
Paulo Alcantara (SUSE)bacd7042020-02-20 19:49:34 -03001269/**
1270 * dfs_cache_get_tgt_share - parse a DFS target
1271 *
Paulo Alcantara7548e1d2020-07-21 09:36:42 -03001272 * @path: DFS full path
Paulo Alcantara (SUSE)bacd7042020-02-20 19:49:34 -03001273 * @it: DFS target iterator.
1274 * @share: tree name.
Paulo Alcantara (SUSE)bacd7042020-02-20 19:49:34 -03001275 * @prefix: prefix path.
Paulo Alcantara (SUSE)bacd7042020-02-20 19:49:34 -03001276 *
1277 * Return zero if target was parsed correctly, otherwise non-zero.
1278 */
Paulo Alcantara7548e1d2020-07-21 09:36:42 -03001279int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it,
1280 char **share, char **prefix)
Paulo Alcantara (SUSE)bacd7042020-02-20 19:49:34 -03001281{
Paulo Alcantara7548e1d2020-07-21 09:36:42 -03001282 char *s, sep, *p;
1283 size_t len;
1284 size_t plen1, plen2;
Paulo Alcantara (SUSE)bacd7042020-02-20 19:49:34 -03001285
Paulo Alcantara7548e1d2020-07-21 09:36:42 -03001286 if (!it || !path || !share || !prefix || strlen(path) < it->it_path_consumed)
Paulo Alcantara (SUSE)bacd7042020-02-20 19:49:34 -03001287 return -EINVAL;
1288
Paulo Alcantara7548e1d2020-07-21 09:36:42 -03001289 *share = NULL;
1290 *prefix = NULL;
1291
Paulo Alcantara (SUSE)bacd7042020-02-20 19:49:34 -03001292 sep = it->it_name[0];
1293 if (sep != '\\' && sep != '/')
1294 return -EINVAL;
1295
1296 s = strchr(it->it_name + 1, sep);
1297 if (!s)
1298 return -EINVAL;
1299
Paulo Alcantara7548e1d2020-07-21 09:36:42 -03001300 /* point to prefix in target node */
Paulo Alcantara (SUSE)bacd7042020-02-20 19:49:34 -03001301 s = strchrnul(s + 1, sep);
1302
Paulo Alcantara7548e1d2020-07-21 09:36:42 -03001303 /* extract target share */
1304 *share = kstrndup(it->it_name, s - it->it_name, GFP_KERNEL);
1305 if (!*share)
1306 return -ENOMEM;
Paulo Alcantara (SUSE)bacd7042020-02-20 19:49:34 -03001307
Paulo Alcantara7548e1d2020-07-21 09:36:42 -03001308 /* skip separator */
1309 if (*s)
1310 s++;
1311 /* point to prefix in DFS path */
1312 p = path + it->it_path_consumed;
1313 if (*p == sep)
1314 p++;
1315
1316 /* merge prefix paths from DFS path and target node */
1317 plen1 = it->it_name + strlen(it->it_name) - s;
1318 plen2 = path + strlen(path) - p;
1319 if (plen1 || plen2) {
1320 len = plen1 + plen2 + 2;
1321 *prefix = kmalloc(len, GFP_KERNEL);
1322 if (!*prefix) {
1323 kfree(*share);
1324 *share = NULL;
1325 return -ENOMEM;
1326 }
1327 if (plen1)
1328 scnprintf(*prefix, len, "%.*s%c%.*s", (int)plen1, s, sep, (int)plen2, p);
1329 else
1330 strscpy(*prefix, p, len);
1331 }
Paulo Alcantara (SUSE)bacd7042020-02-20 19:49:34 -03001332 return 0;
1333}
1334
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001335/* Get all tcons that are within a DFS namespace and can be refreshed */
1336static void get_tcons(struct TCP_Server_Info *server, struct list_head *head)
1337{
1338 struct cifs_ses *ses;
1339 struct cifs_tcon *tcon;
1340
1341 INIT_LIST_HEAD(head);
1342
1343 spin_lock(&cifs_tcp_ses_lock);
1344 list_for_each_entry(ses, &server->smb_ses_list, smb_ses_list) {
1345 list_for_each_entry(tcon, &ses->tcon_list, tcon_list) {
1346 if (!tcon->need_reconnect && !tcon->need_reopen_files &&
1347 tcon->dfs_path) {
1348 tcon->tc_count++;
1349 list_add_tail(&tcon->ulist, head);
1350 }
1351 }
1352 if (ses->tcon_ipc && !ses->tcon_ipc->need_reconnect &&
1353 ses->tcon_ipc->dfs_path) {
1354 list_add_tail(&ses->tcon_ipc->ulist, head);
1355 }
1356 }
1357 spin_unlock(&cifs_tcp_ses_lock);
1358}
1359
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001360static bool is_dfs_link(const char *path)
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001361{
1362 char *s;
1363
1364 s = strchr(path + 1, '\\');
1365 if (!s)
1366 return false;
1367 return !!strchr(s + 1, '\\');
1368}
1369
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001370static char *get_dfs_root(const char *path)
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001371{
1372 char *s, *npath;
1373
1374 s = strchr(path + 1, '\\');
1375 if (!s)
1376 return ERR_PTR(-EINVAL);
1377
1378 s = strchr(s + 1, '\\');
1379 if (!s)
1380 return ERR_PTR(-EINVAL);
1381
1382 npath = kstrndup(path, s - path, GFP_KERNEL);
1383 if (!npath)
1384 return ERR_PTR(-ENOMEM);
1385
1386 return npath;
1387}
1388
Paulo Alcantara (SUSE)345c1a42019-12-04 17:38:00 -03001389static inline void put_tcp_server(struct TCP_Server_Info *server)
1390{
1391 cifs_put_tcp_session(server, 0);
1392}
1393
Ronnie Sahlberg3fa1c6d2020-12-09 23:07:12 -06001394static struct TCP_Server_Info *get_tcp_server(struct smb3_fs_context *ctx)
Paulo Alcantara (SUSE)345c1a42019-12-04 17:38:00 -03001395{
1396 struct TCP_Server_Info *server;
1397
Ronnie Sahlberg3fa1c6d2020-12-09 23:07:12 -06001398 server = cifs_find_tcp_session(ctx);
Paulo Alcantara (SUSE)345c1a42019-12-04 17:38:00 -03001399 if (IS_ERR_OR_NULL(server))
1400 return NULL;
1401
1402 spin_lock(&GlobalMid_Lock);
1403 if (server->tcpStatus != CifsGood) {
1404 spin_unlock(&GlobalMid_Lock);
1405 put_tcp_server(server);
1406 return NULL;
1407 }
1408 spin_unlock(&GlobalMid_Lock);
1409
1410 return server;
1411}
1412
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001413/* Find root SMB session out of a DFS link path */
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001414static struct cifs_ses *find_root_ses(struct vol_info *vi,
1415 struct cifs_tcon *tcon,
1416 const char *path)
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001417{
1418 char *rpath;
1419 int rc;
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001420 struct cache_entry *ce;
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001421 struct dfs_info3_param ref = {0};
Ronnie Sahlberg0d4873f2021-01-28 21:35:10 -06001422 char *mdata = NULL, *devname = NULL;
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001423 struct TCP_Server_Info *server;
1424 struct cifs_ses *ses;
Ronnie Sahlberg3fa1c6d2020-12-09 23:07:12 -06001425 struct smb3_fs_context ctx = {NULL};
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001426
1427 rpath = get_dfs_root(path);
1428 if (IS_ERR(rpath))
1429 return ERR_CAST(rpath);
1430
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001431 down_read(&htable_rw_lock);
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001432
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001433 ce = lookup_cache_entry(rpath, NULL);
1434 if (IS_ERR(ce)) {
1435 up_read(&htable_rw_lock);
1436 ses = ERR_CAST(ce);
1437 goto out;
1438 }
1439
1440 rc = setup_referral(path, ce, &ref, get_tgt_name(ce));
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001441 if (rc) {
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001442 up_read(&htable_rw_lock);
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001443 ses = ERR_PTR(rc);
1444 goto out;
1445 }
1446
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001447 up_read(&htable_rw_lock);
1448
Ronnie Sahlberg0d4873f2021-01-28 21:35:10 -06001449 mdata = cifs_compose_mount_options(vi->mntdata, rpath, &ref,
1450 &devname);
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001451 free_dfs_info_param(&ref);
1452
1453 if (IS_ERR(mdata)) {
1454 ses = ERR_CAST(mdata);
1455 mdata = NULL;
1456 goto out;
1457 }
1458
Ronnie Sahlberg0d4873f2021-01-28 21:35:10 -06001459 rc = cifs_setup_volume_info(&ctx, NULL, devname);
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001460
1461 if (rc) {
1462 ses = ERR_PTR(rc);
1463 goto out;
1464 }
1465
Ronnie Sahlberg3fa1c6d2020-12-09 23:07:12 -06001466 server = get_tcp_server(&ctx);
Paulo Alcantara (SUSE)345c1a42019-12-04 17:38:00 -03001467 if (!server) {
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001468 ses = ERR_PTR(-EHOSTDOWN);
1469 goto out;
1470 }
1471
Ronnie Sahlberg3fa1c6d2020-12-09 23:07:12 -06001472 ses = cifs_get_smb_ses(server, &ctx);
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001473
1474out:
Ronnie Sahlbergc741cba2020-12-14 16:40:16 +10001475 smb3_cleanup_fs_context_contents(&ctx);
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001476 kfree(mdata);
1477 kfree(rpath);
Ronnie Sahlberg0d4873f2021-01-28 21:35:10 -06001478 kfree(devname);
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001479
1480 return ses;
1481}
1482
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001483/* Refresh DFS cache entry from a given tcon */
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001484static int refresh_tcon(struct vol_info *vi, struct cifs_tcon *tcon)
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001485{
1486 int rc = 0;
1487 unsigned int xid;
Al Viro9cfdb1c2021-03-18 01:03:34 -04001488 const char *path, *npath;
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001489 struct cache_entry *ce;
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001490 struct cifs_ses *root_ses = NULL, *ses;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001491 struct dfs_info3_param *refs = NULL;
1492 int numrefs = 0;
1493
1494 xid = get_xid();
1495
1496 path = tcon->dfs_path + 1;
1497
1498 rc = get_normalized_path(path, &npath);
1499 if (rc)
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001500 goto out_free_xid;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001501
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001502 down_read(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001503
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001504 ce = lookup_cache_entry(npath, NULL);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001505 if (IS_ERR(ce)) {
1506 rc = PTR_ERR(ce);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001507 up_read(&htable_rw_lock);
1508 goto out_free_path;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001509 }
1510
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001511 if (!cache_entry_expired(ce)) {
1512 up_read(&htable_rw_lock);
1513 goto out_free_path;
1514 }
1515
1516 up_read(&htable_rw_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001517
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001518 /* If it's a DFS Link, then use root SMB session for refreshing it */
1519 if (is_dfs_link(npath)) {
1520 ses = root_ses = find_root_ses(vi, tcon, npath);
1521 if (IS_ERR(ses)) {
1522 rc = PTR_ERR(ses);
1523 root_ses = NULL;
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001524 goto out_free_path;
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001525 }
1526 } else {
1527 ses = tcon->ses;
1528 }
1529
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001530 rc = get_dfs_referral(xid, ses, cache_nlsc, tcon->remap, npath, &refs,
1531 &numrefs);
1532 if (!rc) {
1533 dump_refs(refs, numrefs);
1534 rc = update_cache_entry(npath, refs, numrefs);
1535 free_dfs_info_array(refs, numrefs);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001536 }
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001537
Paulo Alcantara (SUSE)50720102019-03-19 16:54:29 -03001538 if (root_ses)
1539 cifs_put_smb_ses(root_ses);
1540
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001541out_free_path:
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001542 free_normalized_path(path, npath);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001543
1544out_free_xid:
1545 free_xid(xid);
1546 return rc;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001547}
1548
1549/*
1550 * Worker that will refresh DFS cache based on lowest TTL value from a DFS
1551 * referral.
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001552 */
1553static void refresh_cache_worker(struct work_struct *work)
1554{
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001555 struct vol_info *vi, *nvi;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001556 struct TCP_Server_Info *server;
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001557 LIST_HEAD(vols);
1558 LIST_HEAD(tcons);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001559 struct cifs_tcon *tcon, *ntcon;
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001560 int rc;
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001561
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001562 /*
1563 * Find SMB volumes that are eligible (server->tcpStatus == CifsGood)
1564 * for refreshing.
1565 */
1566 spin_lock(&vol_list_lock);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001567 list_for_each_entry(vi, &vol_list, list) {
Ronnie Sahlberg3fa1c6d2020-12-09 23:07:12 -06001568 server = get_tcp_server(&vi->ctx);
Paulo Alcantara (SUSE)345c1a42019-12-04 17:38:00 -03001569 if (!server)
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001570 continue;
Paulo Alcantara (SUSE)345c1a42019-12-04 17:38:00 -03001571
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001572 kref_get(&vi->refcnt);
1573 list_add_tail(&vi->rlist, &vols);
1574 put_tcp_server(server);
1575 }
1576 spin_unlock(&vol_list_lock);
1577
1578 /* Walk through all TCONs and refresh any expired cache entry */
1579 list_for_each_entry_safe(vi, nvi, &vols, rlist) {
Ronnie Sahlberg3fa1c6d2020-12-09 23:07:12 -06001580 spin_lock(&vi->ctx_lock);
1581 server = get_tcp_server(&vi->ctx);
1582 spin_unlock(&vi->ctx_lock);
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001583
1584 if (!server)
1585 goto next_vol;
1586
1587 get_tcons(server, &tcons);
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001588 rc = 0;
1589
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001590 list_for_each_entry_safe(tcon, ntcon, &tcons, ulist) {
Paulo Alcantara (SUSE)742d8de2019-12-04 17:38:03 -03001591 /*
1592 * Skip tcp server if any of its tcons failed to refresh
1593 * (possibily due to reconnects).
1594 */
1595 if (!rc)
1596 rc = refresh_tcon(vi, tcon);
1597
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001598 list_del_init(&tcon->ulist);
1599 cifs_put_tcon(tcon);
1600 }
Paulo Alcantara (SUSE)345c1a42019-12-04 17:38:00 -03001601
1602 put_tcp_server(server);
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001603
1604next_vol:
1605 list_del_init(&vi->rlist);
1606 kref_put(&vi->refcnt, vol_release);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001607 }
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001608
1609 spin_lock(&cache_ttl_lock);
Paulo Alcantara (SUSE)185352a2019-12-04 17:37:58 -03001610 queue_delayed_work(dfscache_wq, &refresh_task, cache_ttl * HZ);
Paulo Alcantara (SUSE)06d57372019-12-04 17:38:02 -03001611 spin_unlock(&cache_ttl_lock);
Paulo Alcantara54be1f62018-11-14 16:01:21 -02001612}