Commit c870a8e7 authored by Paulo Alcantara's avatar Paulo Alcantara Committed by Steve French
Browse files

cifs: handle different charsets in dfs cache



Convert all dfs paths to dfs cache's local codepage (@cache_cp) and
avoid mixing them with different charsets.

Signed-off-by: default avatarPaulo Alcantara (SUSE) <pc@cjr.nz>
Reviewed-by: default avatarAurelien Aptel <aaptel@suse.com>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
parent c9f71103
Loading
Loading
Loading
Loading
+2 −3
Original line number Diff line number Diff line
@@ -72,9 +72,8 @@ struct cifs_sb_info {
	char *prepath;

	/*
	 * Path initially provided by the mount call. We might connect
	 * to something different via DFS but we want to keep it to do
	 * failover properly.
	 * Canonical DFS path initially provided by the mount call. We might connect to something
	 * different via DFS but we want to keep it to do failover properly.
	 */
	char *origin_fullpath; /* \\HOST\SHARE\[OPTIONAL PATH] */
	/* randomly generated 128-bit number for indexing dfs mount groups in referral cache */
+1 −2
Original line number Diff line number Diff line
@@ -1093,8 +1093,7 @@ struct cifs_tcon {
	struct cached_fid crfid; /* Cached root fid */
	/* BB add field for back pointer to sb struct(s)? */
#ifdef CONFIG_CIFS_DFS_UPCALL
	char *dfs_path;
	int remap:2;
	char *dfs_path; /* canonical DFS path */
	struct list_head ulist; /* cache update list */
#endif
};
+40 −23
Original line number Diff line number Diff line
@@ -3010,8 +3010,7 @@ expand_dfs_referral(const unsigned int xid, struct cifs_ses *ses,
	return rc;
}

static inline int get_next_dfs_tgt(const char *path,
				   struct dfs_cache_tgt_list *tgt_list,
static int get_next_dfs_tgt(struct dfs_cache_tgt_list *tgt_list,
			    struct dfs_cache_tgt_iterator **tgt_it)
{
	if (!*tgt_it)
@@ -3052,6 +3051,7 @@ static int do_dfs_failover(const char *path, const char *full_path, struct cifs_
			   struct cifs_ses **ses, struct cifs_tcon **tcon)
{
	int rc;
	char *npath = NULL;
	struct dfs_cache_tgt_list tgt_list = {0};
	struct dfs_cache_tgt_iterator *tgt_it = NULL;
	struct smb3_fs_context tmp_ctx = {NULL};
@@ -3059,11 +3059,15 @@ static int do_dfs_failover(const char *path, const char *full_path, struct cifs_
	if (cifs_sb->mnt_cifs_flags & CIFS_MOUNT_NO_DFS)
		return -EOPNOTSUPP;

	cifs_dbg(FYI, "%s: path=%s full_path=%s\n", __func__, path, full_path);
	npath = dfs_cache_canonical_path(path, cifs_sb->local_nls, cifs_remap(cifs_sb));
	if (IS_ERR(npath))
		return PTR_ERR(npath);

	cifs_dbg(FYI, "%s: path=%s full_path=%s\n", __func__, npath, full_path);

	rc = dfs_cache_noreq_find(path, NULL, &tgt_list);
	rc = dfs_cache_noreq_find(npath, NULL, &tgt_list);
	if (rc)
		return rc;
		goto out;
	/*
	 * We use a 'tmp_ctx' here because we need pass it down to the mount_{get,put} functions to
	 * test connection against new DFS targets.
@@ -3077,11 +3081,11 @@ static int do_dfs_failover(const char *path, const char *full_path, struct cifs_
		char *fake_devname = NULL, *mdata = NULL;

		/* Get next DFS target server - if any */
		rc = get_next_dfs_tgt(path, &tgt_list, &tgt_it);
		rc = get_next_dfs_tgt(&tgt_list, &tgt_it);
		if (rc)
			break;

		rc = dfs_cache_get_tgt_referral(path, tgt_it, &ref);
		rc = dfs_cache_get_tgt_referral(npath, tgt_it, &ref);
		if (rc)
			break;

@@ -3130,6 +3134,7 @@ static int do_dfs_failover(const char *path, const char *full_path, struct cifs_
	}

out:
	kfree(npath);
	smb3_cleanup_fs_context_contents(&tmp_ctx);
	dfs_cache_free_tgts(&tgt_list);
	return rc;
@@ -3287,11 +3292,6 @@ static void set_root_ses(struct cifs_sb_info *cifs_sb, const uuid_t *mount_id, s
	if (ses) {
		spin_lock(&cifs_tcp_ses_lock);
		ses->ses_count++;
		cifs_dbg(FYI, "%s: new ses_count=%d\n", __func__, ses->ses_count);
		if (ses->tcon_ipc) {
			cifs_dbg(FYI, "%s: ipc tcon: %s\n", __func__, ses->tcon_ipc->treeName);
			ses->tcon_ipc->remap = cifs_remap(cifs_sb);
		}
		spin_unlock(&cifs_tcp_ses_lock);
		dfs_cache_add_refsrv_session(mount_id, ses);
	}
@@ -3343,17 +3343,25 @@ static int next_dfs_prepath(struct cifs_sb_info *cifs_sb, struct smb3_fs_context
}

/* Check if resolved targets can handle any DFS referrals */
static int is_referral_server(const char *ref_path, struct cifs_tcon *tcon, bool *ref_server)
static int is_referral_server(const char *ref_path, struct cifs_sb_info *cifs_sb,
			      struct cifs_tcon *tcon, bool *ref_server)
{
	int rc;
	struct dfs_info3_param ref = {0};

	cifs_dbg(FYI, "%s: ref_path=%s\n", __func__, ref_path);

	if (is_tcon_dfs(tcon)) {
		*ref_server = true;
	} else {
		cifs_dbg(FYI, "%s: ref_path=%s\n", __func__, ref_path);
		char *npath;

		npath = dfs_cache_canonical_path(ref_path, cifs_sb->local_nls, cifs_remap(cifs_sb));
		if (IS_ERR(npath))
			return PTR_ERR(npath);

		rc = dfs_cache_noreq_find(ref_path, &ref, NULL);
		rc = dfs_cache_noreq_find(npath, &ref, NULL);
		kfree(npath);
		if (rc) {
			cifs_dbg(VFS, "%s: dfs_cache_noreq_find: failed (rc=%d)\n", __func__, rc);
			return rc;
@@ -3443,7 +3451,7 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
			continue;

		/* Make sure that requests go through new root servers */
		rc = is_referral_server(ref_path + 1, tcon, &ref_server);
		rc = is_referral_server(ref_path + 1, cifs_sb, tcon, &ref_server);
		if (rc)
			break;
		if (ref_server)
@@ -3460,7 +3468,6 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
		goto error;

	kfree(ref_path);
	ref_path = NULL;
	/*
	 * Store DFS full path in both superblock and tree connect structures.
	 *
@@ -3469,15 +3476,25 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb3_fs_context *ctx)
	 * links, the prefix path is included in both and may be changed during reconnect.  See
	 * cifs_tree_connect().
	 */
	cifs_sb->origin_fullpath = kstrdup(full_path, GFP_KERNEL);
	if (!cifs_sb->origin_fullpath) {
	ref_path = dfs_cache_canonical_path(full_path, cifs_sb->local_nls, cifs_remap(cifs_sb));
	kfree(full_path);
	full_path = NULL;

	if (IS_ERR(ref_path)) {
		rc = PTR_ERR(ref_path);
		ref_path = NULL;
		goto error;
	}
	cifs_sb->origin_fullpath = ref_path;

	ref_path = kstrdup(cifs_sb->origin_fullpath, GFP_KERNEL);
	if (!ref_path) {
		rc = -ENOMEM;
		goto error;
	}
	spin_lock(&cifs_tcp_ses_lock);
	tcon->dfs_path = full_path;
	full_path = NULL;
	tcon->remap = cifs_remap(cifs_sb);
	tcon->dfs_path = ref_path;
	ref_path = NULL;
	spin_unlock(&cifs_tcp_ses_lock);

	/*
+77 −88
Original line number Diff line number Diff line
@@ -65,7 +65,7 @@ static struct workqueue_struct *dfscache_wq __read_mostly;
static int cache_ttl;
static DEFINE_SPINLOCK(cache_ttl_lock);

static struct nls_table *cache_nlsc;
static struct nls_table *cache_cp;

/*
 * Number of entries in the cache
@@ -175,27 +175,45 @@ static void free_mount_group_list(void)
	}
}

static int get_normalized_path(const char *path, const char **npath)
/**
 * 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 -EINVAL;
		return ERR_PTR(-EINVAL);

	if (*path == '\\') {
		*npath = path;
	} else {
		char *s = kstrdup(path, GFP_KERNEL);
		if (!s)
			return -ENOMEM;
		convert_delimiter(s, '\\');
		*npath = s;
	}
	return 0;
	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);
		}

static inline void free_normalized_path(const char *path, const char *npath)
{
	if (path != npath)
		kfree(npath);
		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(-EINVAL);
		}
	} else {
		npath = kstrdup(path, GFP_KERNEL);
		if (!npath)
			return ERR_PTR(-ENOMEM);
	}
	convert_delimiter(npath, '\\');
	return npath;
}

static inline bool cache_entry_expired(const struct cache_entry *ce)
@@ -394,7 +412,9 @@ int dfs_cache_init(void)
		INIT_HLIST_HEAD(&cache_htable[i]);

	atomic_set(&cache_count, 0);
	cache_nlsc = load_nls_default();
	cache_cp = load_nls("utf8");
	if (!cache_cp)
		cache_cp = load_nls_default();

	cifs_dbg(FYI, "%s: initialized DFS referral cache\n", __func__);
	return 0;
@@ -669,7 +689,7 @@ static struct cache_entry *lookup_cache_entry(const char *path, unsigned int *ha
void dfs_cache_destroy(void)
{
	cancel_delayed_work_sync(&refresh_task);
	unload_nls(cache_nlsc);
	unload_nls(cache_cp);
	free_mount_group_list();
	flush_cache_ents();
	kmem_cache_destroy(cache_slab);
@@ -707,23 +727,21 @@ static int update_cache_entry_locked(const char *path, const struct dfs_info3_pa
	return rc;
}

static int get_dfs_referral(const unsigned int xid, struct cifs_ses *ses,
			    const struct nls_table *nls_codepage, int remap,
			    const char *path,  struct dfs_info3_param **refs,
			    int *numrefs)
static int get_dfs_referral(const unsigned int xid, struct cifs_ses *ses, const char *path,
			    struct dfs_info3_param **refs, int *numrefs)
{
	cifs_dbg(FYI, "%s: get an DFS referral for %s\n", __func__, path);

	if (!ses || !ses->server || !ses->server->ops->get_dfs_refer)
		return -EOPNOTSUPP;
	if (unlikely(!nls_codepage))
	if (unlikely(!cache_cp))
		return -EINVAL;

	*refs = NULL;
	*numrefs = 0;

	return ses->server->ops->get_dfs_refer(xid, ses, path, refs, numrefs,
					       nls_codepage, remap);
	return ses->server->ops->get_dfs_refer(xid, ses, path, refs, numrefs, cache_cp,
					       NO_MAP_UNI_RSVD);
}

/*
@@ -735,8 +753,7 @@ static int get_dfs_referral(const unsigned int xid, struct cifs_ses *ses,
 * For interlinks, cifs_mount() and expand_dfs_referral() are supposed to
 * handle them properly.
 */
static int cache_refresh_path(const unsigned int xid, struct cifs_ses *ses,
			      const struct nls_table *nls_codepage, int remap, const char *path)
static int cache_refresh_path(const unsigned int xid, struct cifs_ses *ses, const char *path)
{
	int rc;
	unsigned int hash;
@@ -764,8 +781,7 @@ static int cache_refresh_path(const unsigned int xid, struct cifs_ses *ses,
	 * Either the entry was not found, or it is expired.
	 * Request a new DFS referral in order to create or update a cache entry.
	 */
	rc = get_dfs_referral(xid, ses, nls_codepage, remap, path,
			      &refs, &numrefs);
	rc = get_dfs_referral(xid, ses, path, &refs, &numrefs);
	if (rc)
		goto out_unlock;

@@ -886,7 +902,7 @@ static int get_targets(struct cache_entry *ce, struct dfs_cache_tgt_list *tl)
 * needs to be issued:
 * @xid: syscall xid
 * @ses: smb session to issue the request on
 * @nls_codepage: charset conversion
 * @cp: codepage
 * @remap: path character remapping type
 * @path: path to lookup in DFS referral cache.
 *
@@ -895,20 +911,19 @@ static int get_targets(struct cache_entry *ce, struct dfs_cache_tgt_list *tl)
 *
 * Return zero if the target was found, otherwise non-zero.
 */
int dfs_cache_find(const unsigned int xid, struct cifs_ses *ses,
		   const struct nls_table *nls_codepage, int remap,
		   const char *path, struct dfs_info3_param *ref,
int dfs_cache_find(const unsigned int xid, struct cifs_ses *ses, const struct nls_table *cp,
		   int remap, const char *path, struct dfs_info3_param *ref,
		   struct dfs_cache_tgt_list *tgt_list)
{
	int rc;
	const char *npath;
	struct cache_entry *ce;

	rc = get_normalized_path(path, &npath);
	if (rc)
		return rc;
	npath = dfs_cache_canonical_path(path, cp, remap);
	if (IS_ERR(npath))
		return PTR_ERR(npath);

	rc = cache_refresh_path(xid, ses, nls_codepage, remap, npath);
	rc = cache_refresh_path(xid, ses, npath);
	if (rc)
		goto out_free_path;

@@ -931,7 +946,7 @@ int dfs_cache_find(const unsigned int xid, struct cifs_ses *ses,
	up_read(&htable_rw_lock);

out_free_path:
	free_normalized_path(path, npath);
	kfree(npath);
	return rc;
}

@@ -943,7 +958,7 @@ int dfs_cache_find(const unsigned int xid, struct cifs_ses *ses,
 * expired, nor create a new cache entry if @path hasn't been found. It heavily
 * relies on an existing cache entry.
 *
 * @path: path to lookup in the DFS referral cache.
 * @path: canonical DFS path to lookup in the DFS referral cache.
 * @ref: when non-NULL, store single DFS referral result in it.
 * @tgt_list: when non-NULL, store complete DFS target list in it.
 *
@@ -955,18 +970,13 @@ int dfs_cache_noreq_find(const char *path, struct dfs_info3_param *ref,
			 struct dfs_cache_tgt_list *tgt_list)
{
	int rc;
	const char *npath;
	struct cache_entry *ce;

	rc = get_normalized_path(path, &npath);
	if (rc)
		return rc;

	cifs_dbg(FYI, "%s: path: %s\n", __func__, npath);
	cifs_dbg(FYI, "%s: path: %s\n", __func__, path);

	down_read(&htable_rw_lock);

	ce = lookup_cache_entry(npath, NULL);
	ce = lookup_cache_entry(path, NULL);
	if (IS_ERR(ce)) {
		rc = PTR_ERR(ce);
		goto out_unlock;
@@ -981,8 +991,6 @@ int dfs_cache_noreq_find(const char *path, struct dfs_info3_param *ref,

out_unlock:
	up_read(&htable_rw_lock);
	free_normalized_path(path, npath);

	return rc;
}

@@ -997,16 +1005,15 @@ int dfs_cache_noreq_find(const char *path, struct dfs_info3_param *ref,
 *
 * @xid: syscall id
 * @ses: smb session
 * @nls_codepage: charset conversion
 * @cp: codepage
 * @remap: type of character remapping for paths
 * @path: path to lookup in DFS referral cache.
 * @path: path to lookup in DFS referral cache
 * @it: DFS target iterator
 *
 * Return zero if the target hint was updated successfully, otherwise non-zero.
 */
int dfs_cache_update_tgthint(const unsigned int xid, struct cifs_ses *ses,
			     const struct nls_table *nls_codepage, int remap,
			     const char *path,
			     const struct nls_table *cp, int remap, const char *path,
			     const struct dfs_cache_tgt_iterator *it)
{
	int rc;
@@ -1014,13 +1021,13 @@ int dfs_cache_update_tgthint(const unsigned int xid, struct cifs_ses *ses,
	struct cache_entry *ce;
	struct cache_dfs_tgt *t;

	rc = get_normalized_path(path, &npath);
	if (rc)
		return rc;
	npath = dfs_cache_canonical_path(path, cp, remap);
	if (IS_ERR(npath))
		return PTR_ERR(npath);

	cifs_dbg(FYI, "%s: update target hint - path: %s\n", __func__, npath);

	rc = cache_refresh_path(xid, ses, nls_codepage, remap, npath);
	rc = cache_refresh_path(xid, ses, npath);
	if (rc)
		goto out_free_path;

@@ -1049,8 +1056,7 @@ int dfs_cache_update_tgthint(const unsigned int xid, struct cifs_ses *ses,
out_unlock:
	up_write(&htable_rw_lock);
out_free_path:
	free_normalized_path(path, npath);

	kfree(npath);
	return rc;
}

@@ -1062,32 +1068,26 @@ int dfs_cache_update_tgthint(const unsigned int xid, struct cifs_ses *ses,
 * expired, nor create a new cache entry if @path hasn't been found. It heavily
 * relies on an existing cache entry.
 *
 * @path: path to lookup in DFS referral cache.
 * @path: canonical DFS path to lookup in DFS referral cache.
 * @it: target iterator which contains the target hint to update the cache
 * entry with.
 *
 * Return zero if the target hint was updated successfully, otherwise non-zero.
 */
int dfs_cache_noreq_update_tgthint(const char *path,
				   const struct dfs_cache_tgt_iterator *it)
int dfs_cache_noreq_update_tgthint(const char *path, const struct dfs_cache_tgt_iterator *it)
{
	int rc;
	const char *npath;
	struct cache_entry *ce;
	struct cache_dfs_tgt *t;

	if (!it)
		return -EINVAL;

	rc = get_normalized_path(path, &npath);
	if (rc)
		return rc;

	cifs_dbg(FYI, "%s: path: %s\n", __func__, npath);
	cifs_dbg(FYI, "%s: path: %s\n", __func__, path);

	down_write(&htable_rw_lock);

	ce = lookup_cache_entry(npath, NULL);
	ce = lookup_cache_entry(path, NULL);
	if (IS_ERR(ce)) {
		rc = PTR_ERR(ce);
		goto out_unlock;
@@ -1110,8 +1110,6 @@ int dfs_cache_noreq_update_tgthint(const char *path,

out_unlock:
	up_write(&htable_rw_lock);
	free_normalized_path(path, npath);

	return rc;
}

@@ -1119,32 +1117,26 @@ int dfs_cache_noreq_update_tgthint(const char *path,
 * dfs_cache_get_tgt_referral - returns a DFS referral (@ref) from a given
 * target iterator (@it).
 *
 * @path: path to lookup in DFS referral cache.
 * @path: canonical DFS path to lookup in DFS referral cache.
 * @it: DFS target iterator.
 * @ref: DFS referral pointer to set up the gathered information.
 *
 * Return zero if the DFS referral was set up correctly, otherwise non-zero.
 */
int dfs_cache_get_tgt_referral(const char *path,
			       const struct dfs_cache_tgt_iterator *it,
int dfs_cache_get_tgt_referral(const char *path, const struct dfs_cache_tgt_iterator *it,
			       struct dfs_info3_param *ref)
{
	int rc;
	const char *npath;
	struct cache_entry *ce;

	if (!it || !ref)
		return -EINVAL;

	rc = get_normalized_path(path, &npath);
	if (rc)
		return rc;

	cifs_dbg(FYI, "%s: path: %s\n", __func__, npath);
	cifs_dbg(FYI, "%s: path: %s\n", __func__, path);

	down_read(&htable_rw_lock);

	ce = lookup_cache_entry(npath, NULL);
	ce = lookup_cache_entry(path, NULL);
	if (IS_ERR(ce)) {
		rc = PTR_ERR(ce);
		goto out_unlock;
@@ -1156,8 +1148,6 @@ int dfs_cache_get_tgt_referral(const char *path,

out_unlock:
	up_read(&htable_rw_lock);
	free_normalized_path(path, npath);

	return rc;
}

@@ -1219,8 +1209,8 @@ void dfs_cache_put_refsrv_sessions(const uuid_t *mount_id)
 *
 * Return zero if target was parsed correctly, otherwise non-zero.
 */
int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it,
			    char **share, char **prefix)
int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it, char **share,
			    char **prefix)
{
	char *s, sep, *p;
	size_t len;
@@ -1310,7 +1300,7 @@ static void refresh_mounts(struct cifs_ses **sessions)
		ses = find_ipc_from_server_path(sessions, path);
		if (!IS_ERR(ses)) {
			xid = get_xid();
			cache_refresh_path(xid, ses, cache_nlsc, tcon->remap, path);
			cache_refresh_path(xid, ses, path);
			free_xid(xid);
		}
		cifs_put_tcon(tcon);
@@ -1346,8 +1336,7 @@ static void refresh_cache(struct cifs_ses **sessions)
				continue;

			xid = get_xid();
			rc = get_dfs_referral(xid, ses, cache_nlsc, NO_MAP_UNI_RSVD, ce->path,
					      &refs, &numrefs);
			rc = get_dfs_referral(xid, ses, ce->path, &refs, &numrefs);
			free_xid(xid);

			if (!rc)
+16 −21
Original line number Diff line number Diff line
@@ -24,31 +24,26 @@ struct dfs_cache_tgt_iterator {
	struct list_head it_list;
};

extern int dfs_cache_init(void);
extern void dfs_cache_destroy(void);
int dfs_cache_init(void);
void dfs_cache_destroy(void);
extern const struct proc_ops dfscache_proc_ops;

extern int dfs_cache_find(const unsigned int xid, struct cifs_ses *ses,
			  const struct nls_table *nls_codepage, int remap,
			  const char *path, struct dfs_info3_param *ref,
int dfs_cache_find(const unsigned int xid, struct cifs_ses *ses, const struct nls_table *cp,
		   int remap, const char *path, struct dfs_info3_param *ref,
		   struct dfs_cache_tgt_list *tgt_list);
extern int dfs_cache_noreq_find(const char *path, struct dfs_info3_param *ref,
int dfs_cache_noreq_find(const char *path, struct dfs_info3_param *ref,
			 struct dfs_cache_tgt_list *tgt_list);
extern int dfs_cache_update_tgthint(const unsigned int xid,
				    struct cifs_ses *ses,
				    const struct nls_table *nls_codepage,
				    int remap, const char *path,
int dfs_cache_update_tgthint(const unsigned int xid, struct cifs_ses *ses,
			     const struct nls_table *cp, int remap, const char *path,
			     const struct dfs_cache_tgt_iterator *it);
extern int
dfs_cache_noreq_update_tgthint(const char *path,
			       const struct dfs_cache_tgt_iterator *it);
extern int dfs_cache_get_tgt_referral(const char *path,
				      const struct dfs_cache_tgt_iterator *it,
int dfs_cache_noreq_update_tgthint(const char *path, const struct dfs_cache_tgt_iterator *it);
int dfs_cache_get_tgt_referral(const char *path, const struct dfs_cache_tgt_iterator *it,
			       struct dfs_info3_param *ref);
extern int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it,
				   char **share, char **prefix);
int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it, char **share,
			    char **prefix);
void dfs_cache_put_refsrv_sessions(const uuid_t *mount_id);
void dfs_cache_add_refsrv_session(const uuid_t *mount_id, struct cifs_ses *ses);
char *dfs_cache_canonical_path(const char *path, const struct nls_table *cp, int remap);

static inline struct dfs_cache_tgt_iterator *
dfs_cache_get_next_tgt(struct dfs_cache_tgt_list *tl,