cifs: Fix DFS cache refresher for DFS links

As per MS-DFSC, when a DFS cache entry is expired and it is a DFS
link, then a new DFS referral must be sent to root server in order to
refresh the expired entry.

This patch ensures that all new DFS referrals for refreshing the cache
are sent to DFS root.

Signed-off-by: Paulo Alcantara (SUSE) <paulo@paulo.ac>
Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
Paulo Alcantara (SUSE)
2019-03-19 16:54:29 -03:00
committed by Steve French
parent f5307104e7
commit 5072010ccf
4 changed files with 151 additions and 25 deletions

View File

@@ -323,8 +323,6 @@ static int ip_connect(struct TCP_Server_Info *server);
static int generic_ip_connect(struct TCP_Server_Info *server);
static void tlink_rb_insert(struct rb_root *root, struct tcon_link *new_tlink);
static void cifs_prune_tlinks(struct work_struct *work);
static int cifs_setup_volume_info(struct smb_vol *volume_info, char *mount_data,
const char *devname, bool is_smb3);
static char *extract_hostname(const char *unc);
/*
@@ -2904,8 +2902,7 @@ cifs_find_smb_ses(struct TCP_Server_Info *server, struct smb_vol *vol)
return NULL;
}
static void
cifs_put_smb_ses(struct cifs_ses *ses)
void cifs_put_smb_ses(struct cifs_ses *ses)
{
unsigned int rc, xid;
struct TCP_Server_Info *server = ses->server;
@@ -3082,7 +3079,7 @@ cifs_set_cifscreds(struct smb_vol *vol __attribute__((unused)),
* already got a server reference (server refcount +1). See
* cifs_get_tcon() for refcount explanations.
*/
static struct cifs_ses *
struct cifs_ses *
cifs_get_smb_ses(struct TCP_Server_Info *server, struct smb_vol *volume_info)
{
int rc = -ENOMEM;
@@ -4389,7 +4386,7 @@ static int mount_do_dfs_failover(const char *path,
}
#endif
static int
int
cifs_setup_volume_info(struct smb_vol *volume_info, char *mount_data,
const char *devname, bool is_smb3)
{
@@ -4543,7 +4540,7 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol)
struct cifs_tcon *tcon = NULL;
struct TCP_Server_Info *server;
char *root_path = NULL, *full_path = NULL;
char *old_mountdata;
char *old_mountdata, *origin_mountdata = NULL;
int count;
rc = mount_get_conns(vol, cifs_sb, &xid, &server, &ses, &tcon);
@@ -4602,6 +4599,14 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol)
goto error;
}
/* Save DFS root volume information for DFS refresh worker */
origin_mountdata = kstrndup(cifs_sb->mountdata,
strlen(cifs_sb->mountdata), GFP_KERNEL);
if (!origin_mountdata) {
rc = -ENOMEM;
goto error;
}
if (cifs_sb->mountdata != old_mountdata) {
/* If we were redirected, reconnect to new target server */
mount_put_conns(cifs_sb, xid, server, ses, tcon);
@@ -4710,7 +4715,7 @@ int cifs_mount(struct cifs_sb_info *cifs_sb, struct smb_vol *vol)
}
spin_unlock(&cifs_tcp_ses_lock);
rc = dfs_cache_add_vol(vol, cifs_sb->origin_fullpath);
rc = dfs_cache_add_vol(origin_mountdata, vol, cifs_sb->origin_fullpath);
if (rc) {
kfree(cifs_sb->origin_fullpath);
goto error;
@@ -4728,6 +4733,7 @@ out:
error:
kfree(full_path);
kfree(root_path);
kfree(origin_mountdata);
mount_put_conns(cifs_sb, xid, server, ses, tcon);
return rc;
}