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

cifs: handle RESP_GET_DFS_REFERRAL.PathConsumed in reconnect



Use PathConsumed field when parsing prefixes of referral paths that
either match a cache entry or are a complete prefix path of an
existing entry.

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 a5293035
Loading
Loading
Loading
Loading
+1 −2
Original line number Diff line number Diff line
@@ -617,8 +617,7 @@ int smb2_parse_query_directory(struct cifs_tcon *tcon, struct kvec *rsp_iov,

struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server);
void cifs_put_tcp_super(struct super_block *sb);
int update_super_prepath(struct cifs_tcon *tcon, const char *prefix,
			 size_t prefix_len);
int update_super_prepath(struct cifs_tcon *tcon, char *prefix);

#ifdef CONFIG_CIFS_DFS_UPCALL
static inline int get_dfs_path(const unsigned int xid, struct cifs_ses *ses,
+11 −6
Original line number Diff line number Diff line
@@ -5547,6 +5547,7 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
	size_t tcp_host_len;
	const char *dfs_host;
	size_t dfs_host_len;
	char *share = NULL, *prefix = NULL;

	tree = kzalloc(MAX_TREE_SIZE, GFP_KERNEL);
	if (!tree)
@@ -5569,11 +5570,12 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
	extract_unc_hostname(server->hostname, &tcp_host, &tcp_host_len);

	for (it = dfs_cache_get_tgt_iterator(&tl); it; it = dfs_cache_get_next_tgt(&tl, it)) {
		const char *share, *prefix;
		size_t share_len, prefix_len;
		bool target_match;

		rc = dfs_cache_get_tgt_share(it, &share, &share_len, &prefix, &prefix_len);
		kfree(share);
		kfree(prefix);

		rc = dfs_cache_get_tgt_share(tcon->dfs_path + 1, it, &share, &prefix);
		if (rc) {
			cifs_dbg(VFS, "%s: failed to parse target share %d\n",
				 __func__, rc);
@@ -5600,13 +5602,13 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
		}

		if (tcon->ipc) {
			scnprintf(tree, MAX_TREE_SIZE, "\\\\%.*s\\IPC$", (int)share_len, share);
			scnprintf(tree, MAX_TREE_SIZE, "\\\\%s\\IPC$", share);
			rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc);
		} else {
			scnprintf(tree, MAX_TREE_SIZE, "\\%.*s", (int)share_len, share);
			scnprintf(tree, MAX_TREE_SIZE, "\\%s", share);
			rc = ops->tree_connect(xid, tcon->ses, tree, tcon, nlsc);
			if (!rc) {
				rc = update_super_prepath(tcon, prefix, prefix_len);
				rc = update_super_prepath(tcon, prefix);
				break;
			}
		}
@@ -5614,6 +5616,9 @@ int cifs_tree_connect(const unsigned int xid, struct cifs_tcon *tcon, const stru
			break;
	}

	kfree(share);
	kfree(prefix);

	if (!rc) {
		if (it)
			rc = dfs_cache_noreq_update_tgthint(tcon->dfs_path + 1, it);
+44 −13
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@

struct cache_dfs_tgt {
	char *name;
	int path_consumed;
	struct list_head list;
};

@@ -350,7 +351,7 @@ static inline struct timespec64 get_expire_time(int ttl)
}

/* Allocate a new DFS target */
static struct cache_dfs_tgt *alloc_target(const char *name)
static struct cache_dfs_tgt *alloc_target(const char *name, int path_consumed)
{
	struct cache_dfs_tgt *t;

@@ -362,6 +363,7 @@ static struct cache_dfs_tgt *alloc_target(const char *name)
		kfree(t);
		return ERR_PTR(-ENOMEM);
	}
	t->path_consumed = path_consumed;
	INIT_LIST_HEAD(&t->list);
	return t;
}
@@ -384,7 +386,7 @@ static int copy_ref_data(const struct dfs_info3_param *refs, int numrefs,
	for (i = 0; i < numrefs; i++) {
		struct cache_dfs_tgt *t;

		t = alloc_target(refs[i].node_name);
		t = alloc_target(refs[i].node_name, refs[i].path_consumed);
		if (IS_ERR(t)) {
			free_tgts(ce);
			return PTR_ERR(t);
@@ -830,6 +832,7 @@ static int get_targets(struct cache_entry *ce, struct dfs_cache_tgt_list *tl)
			rc = -ENOMEM;
			goto err_free_it;
		}
		it->it_path_consumed = t->path_consumed;

		if (ce->tgthint == t)
			list_add(&it->it_list, head);
@@ -1320,23 +1323,26 @@ void dfs_cache_del_vol(const char *fullpath)
/**
 * dfs_cache_get_tgt_share - parse a DFS target
 *
 * @path: DFS full path
 * @it: DFS target iterator.
 * @share: tree name.
 * @share_len: length of tree name.
 * @prefix: prefix path.
 * @prefix_len: length of prefix path.
 *
 * Return zero if target was parsed correctly, otherwise non-zero.
 */
int dfs_cache_get_tgt_share(const struct dfs_cache_tgt_iterator *it,
			    const char **share, size_t *share_len,
			    const char **prefix, size_t *prefix_len)
int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it,
			    char **share, char **prefix)
{
	char *s, sep;
	char *s, sep, *p;
	size_t len;
	size_t plen1, plen2;

	if (!it || !share || !share_len || !prefix || !prefix_len)
	if (!it || !path || !share || !prefix || strlen(path) < it->it_path_consumed)
		return -EINVAL;

	*share = NULL;
	*prefix = NULL;

	sep = it->it_name[0];
	if (sep != '\\' && sep != '/')
		return -EINVAL;
@@ -1345,13 +1351,38 @@ int dfs_cache_get_tgt_share(const struct dfs_cache_tgt_iterator *it,
	if (!s)
		return -EINVAL;

	/* point to prefix in target node */
	s = strchrnul(s + 1, sep);

	*share = it->it_name;
	*share_len = s - it->it_name;
	*prefix = *s ? s + 1 : s;
	*prefix_len = &it->it_name[strlen(it->it_name)] - *prefix;
	/* extract target share */
	*share = kstrndup(it->it_name, s - it->it_name, GFP_KERNEL);
	if (!*share)
		return -ENOMEM;

	/* skip separator */
	if (*s)
		s++;
	/* point to prefix in DFS path */
	p = path + it->it_path_consumed;
	if (*p == sep)
		p++;

	/* merge prefix paths from DFS path and target node */
	plen1 = it->it_name + strlen(it->it_name) - s;
	plen2 = path + strlen(path) - p;
	if (plen1 || plen2) {
		len = plen1 + plen2 + 2;
		*prefix = kmalloc(len, GFP_KERNEL);
		if (!*prefix) {
			kfree(*share);
			*share = NULL;
			return -ENOMEM;
		}
		if (plen1)
			scnprintf(*prefix, len, "%.*s%c%.*s", (int)plen1, s, sep, (int)plen2, p);
		else
			strscpy(*prefix, p, len);
	}
	return 0;
}

+3 −4
Original line number Diff line number Diff line
@@ -19,6 +19,7 @@ struct dfs_cache_tgt_list {

struct dfs_cache_tgt_iterator {
	char *it_name;
	int it_path_consumed;
	struct list_head it_list;
};

@@ -48,10 +49,8 @@ extern int dfs_cache_add_vol(char *mntdata, struct smb_vol *vol,
extern int dfs_cache_update_vol(const char *fullpath,
				struct TCP_Server_Info *server);
extern void dfs_cache_del_vol(const char *fullpath);

extern int dfs_cache_get_tgt_share(const struct dfs_cache_tgt_iterator *it,
				   const char **share, size_t *share_len,
				   const char **prefix, size_t *prefix_len);
extern int dfs_cache_get_tgt_share(char *path, const struct dfs_cache_tgt_iterator *it,
				   char **share, char **prefix);

static inline struct dfs_cache_tgt_iterator *
dfs_cache_get_next_tgt(struct dfs_cache_tgt_list *tl,
+3 −4
Original line number Diff line number Diff line
@@ -1164,8 +1164,7 @@ static inline void cifs_put_tcon_super(struct super_block *sb)
}
#endif

int update_super_prepath(struct cifs_tcon *tcon, const char *prefix,
			 size_t prefix_len)
int update_super_prepath(struct cifs_tcon *tcon, char *prefix)
{
	struct super_block *sb;
	struct cifs_sb_info *cifs_sb;
@@ -1179,8 +1178,8 @@ int update_super_prepath(struct cifs_tcon *tcon, const char *prefix,

	kfree(cifs_sb->prepath);

	if (*prefix && prefix_len) {
		cifs_sb->prepath = kstrndup(prefix, prefix_len, GFP_ATOMIC);
	if (prefix && *prefix) {
		cifs_sb->prepath = kstrndup(prefix, strlen(prefix), GFP_ATOMIC);
		if (!cifs_sb->prepath) {
			rc = -ENOMEM;
			goto out;