cifs: ensure correct super block for DFS reconnect

This patch is basically fixing the lookup of tcons (DFS specific) during
reconnect (smb2pdu.c:__smb2_reconnect) to update their prefix paths.

Previously, we relied on the TCP_Server_Info pointer
(misc.c:tcp_super_cb) to determine which tcon to update the prefix path

We could not rely on TCP server pointer to determine which super block
to update the prefix path when reconnecting tcons since it might map
to different tcons that share same TCP connection.

Instead, walk through all cifs super blocks and compare their DFS full
paths with the tcon being updated to.

Signed-off-by: Paulo Alcantara (SUSE) <pc@cjr.nz>
Signed-off-by: Steve French <stfrench@microsoft.com>
Reviewed-by: Ronnie Sahlberg <lsahlber@redhat.com>
This commit is contained in:
Paulo Alcantara 2020-04-20 19:43:04 -03:00 committed by Steve French
parent 65303de829
commit 3786f4bddc
1 changed files with 65 additions and 17 deletions

View File

@ -1025,51 +1025,99 @@ int copy_path_name(char *dst, const char *src)
} }
struct super_cb_data { struct super_cb_data {
struct TCP_Server_Info *server; void *data;
struct super_block *sb; struct super_block *sb;
}; };
static void super_cb(struct super_block *sb, void *arg) static void tcp_super_cb(struct super_block *sb, void *arg)
{ {
struct super_cb_data *d = arg; struct super_cb_data *sd = arg;
struct TCP_Server_Info *server = sd->data;
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb;
struct cifs_tcon *tcon; struct cifs_tcon *tcon;
if (d->sb) if (sd->sb)
return; return;
cifs_sb = CIFS_SB(sb); cifs_sb = CIFS_SB(sb);
tcon = cifs_sb_master_tcon(cifs_sb); tcon = cifs_sb_master_tcon(cifs_sb);
if (tcon->ses->server == d->server) if (tcon->ses->server == server)
d->sb = sb; sd->sb = sb;
} }
struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server) static struct super_block *__cifs_get_super(void (*f)(struct super_block *, void *),
void *data)
{ {
struct super_cb_data d = { struct super_cb_data sd = {
.server = server, .data = data,
.sb = NULL, .sb = NULL,
}; };
iterate_supers_type(&cifs_fs_type, super_cb, &d); iterate_supers_type(&cifs_fs_type, f, &sd);
if (unlikely(!d.sb)) if (!sd.sb)
return ERR_PTR(-ENOENT); return ERR_PTR(-EINVAL);
/* /*
* Grab an active reference in order to prevent automounts (DFS links) * Grab an active reference in order to prevent automounts (DFS links)
* of expiring and then freeing up our cifs superblock pointer while * of expiring and then freeing up our cifs superblock pointer while
* we're doing failover. * we're doing failover.
*/ */
cifs_sb_active(d.sb); cifs_sb_active(sd.sb);
return d.sb; return sd.sb;
} }
void cifs_put_tcp_super(struct super_block *sb) static void __cifs_put_super(struct super_block *sb)
{ {
if (!IS_ERR_OR_NULL(sb)) if (!IS_ERR_OR_NULL(sb))
cifs_sb_deactive(sb); cifs_sb_deactive(sb);
} }
struct super_block *cifs_get_tcp_super(struct TCP_Server_Info *server)
{
return __cifs_get_super(tcp_super_cb, server);
}
void cifs_put_tcp_super(struct super_block *sb)
{
__cifs_put_super(sb);
}
#ifdef CONFIG_CIFS_DFS_UPCALL
static void tcon_super_cb(struct super_block *sb, void *arg)
{
struct super_cb_data *sd = arg;
struct cifs_tcon *tcon = sd->data;
struct cifs_sb_info *cifs_sb;
if (sd->sb)
return;
cifs_sb = CIFS_SB(sb);
if (tcon->dfs_path && cifs_sb->origin_fullpath &&
!strcasecmp(tcon->dfs_path, cifs_sb->origin_fullpath))
sd->sb = sb;
}
static inline struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon)
{
return __cifs_get_super(tcon_super_cb, tcon);
}
static inline void cifs_put_tcon_super(struct super_block *sb)
{
__cifs_put_super(sb);
}
#else
static inline struct super_block *cifs_get_tcon_super(struct cifs_tcon *tcon)
{
return ERR_PTR(-EOPNOTSUPP);
}
static inline void cifs_put_tcon_super(struct super_block *sb)
{
}
#endif
int update_super_prepath(struct cifs_tcon *tcon, const char *prefix, int update_super_prepath(struct cifs_tcon *tcon, const char *prefix,
size_t prefix_len) size_t prefix_len)
{ {
@ -1077,7 +1125,7 @@ int update_super_prepath(struct cifs_tcon *tcon, const char *prefix,
struct cifs_sb_info *cifs_sb; struct cifs_sb_info *cifs_sb;
int rc = 0; int rc = 0;
sb = cifs_get_tcp_super(tcon->ses->server); sb = cifs_get_tcon_super(tcon);
if (IS_ERR(sb)) if (IS_ERR(sb))
return PTR_ERR(sb); return PTR_ERR(sb);
@ -1099,6 +1147,6 @@ int update_super_prepath(struct cifs_tcon *tcon, const char *prefix,
cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH; cifs_sb->mnt_cifs_flags |= CIFS_MOUNT_USE_PREFIX_PATH;
out: out:
cifs_put_tcp_super(sb); cifs_put_tcon_super(sb);
return rc; return rc;
} }