// SPDX-License-Identifier: GPL-2.0
/*
* DFS referral cache routines
*
* Copyright (c) 2018-2019 Paulo Alcantara <palcantara@suse.de>
*/
#include <linux/jhash.h>
#include <linux/ktime.h>
#include <linux/slab.h>
#include <linux/proc_fs.h>
#include <linux/nls.h>
#include <linux/workqueue.h>
#include <linux/uuid.h>
#include "cifsglob.h"
#include "smb2pdu.h"
#include "smb2proto.h"
#include "cifsproto.h"
#include "cifs_debug.h"
#include "cifs_unicode.h"
#include "smb2glob.h"
#include "dns_resolve.h"
#include "dfs.h"
#include "dfs_cache.h"
#define CACHE_HTABLE_SIZE 32
#define CACHE_MAX_ENTRIES 64
#define CACHE_MIN_TTL 120 /* 2 minutes */
#define CACHE_DEFAULT_TTL 300 /* 5 minutes */
struct cache_dfs_tgt {
char *name;
int path_consumed;
struct list_head list;
};
struct cache_entry {
struct hlist_node hlist;
const char *path;
int hdr_flags; /* RESP_GET_DFS_REFERRAL.ReferralHeaderFlags */
int ttl; /* DFS_REREFERRAL_V3.TimeToLive */
int srvtype; /* DFS_REREFERRAL_V3.ServerType */
int ref_flags; /* DFS_REREFERRAL_V3.ReferralEntryFlags */
struct timespec64 etime;
int path_consumed; /* RESP_GET_DFS_REFERRAL.PathConsumed */
int numtgts;
struct list_head tlist;
struct cache_dfs_tgt *tgthint;
};
static struct kmem_cache *cache_slab __read_mostly;
struct workqueue_struct *dfscache_wq;
atomic_t dfs_cache_ttl;
static struct nls_table *cache_cp;
/*
* Number of entries in the cache
*/
static atomic_t cache_count;
static struct hlist_head cache_htable[CACHE_HTABLE_SIZE];
static DECLARE_RWSEM(htable_rw_lock);
/**
* dfs_cache_canonical_path - get a canonical DFS path
*
* @path: DFS path
* @cp: codepage
* @remap: mapping type
*
* Return canonical path if success, otherwise error.
*/
char *dfs_cache_canonical_path(const char *path, const struct nls_table *cp, int remap)
{
char *tmp;
int plen = 0;
char *npath;
if (!path || strlen(path) < 3 || (*path != '\\' && *path != '/'))
return ERR_PTR(-EINVAL);
if (unlikely(strcmp(cp->charset, cache_cp->charset))) {
tmp = (char *)cifs_strndup_to_utf16(path, strlen(path), &plen, cp, remap);
if (!tmp) {
cifs_dbg(VFS, "%s: failed to convert path to utf16\n", __func__);
return ERR_PTR(-EINVAL);
}
npath = cifs_strndup_from_utf16(tmp, plen, true, cache_cp);
kfree(tmp);
if (!npath) {
cifs_dbg(VFS, "%s: failed to convert path from utf16\n", __func__);
return ERR_PTR(-EI