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:
parent
65303de829
commit
3786f4bddc
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue