forked from luck/tmp_suning_uos_patched
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: Paulo Alcantara (SUSE) <pc@cjr.nz> Reviewed-by: Aurelien Aptel <aaptel@suse.com> Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
parent
a52930353e
commit
7548e1da8d
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue
Block a user