cifs: Fix mount options set in automount
Starting from4a367dc044
, we must set the mount options based on the DFS full path rather than the resolved target, that is, cifs_mount() will be responsible for resolving the DFS link (cached) as well as performing failover to any other targets in the referral. Signed-off-by: Paulo Alcantara (SUSE) <pc@cjr.nz> Reported-by: Martijn de Gouw <martijn.de.gouw@prodrive-technologies.com> Fixes:4a367dc044
("cifs: Add support for failover in cifs_mount()") Link: https://lore.kernel.org/linux-cifs/39643d7d-2abb-14d3-ced6-c394fab9a777@prodrive-technologies.com Tested-by: Martijn de Gouw <martijn.de.gouw@prodrive-technologies.com> Signed-off-by: Steve French <stfrench@microsoft.com>
This commit is contained in:
parent
463a7b457c
commit
5739375ee4
|
@ -120,17 +120,17 @@ cifs_build_devname(char *nodename, const char *prepath)
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* cifs_compose_mount_options - creates mount options for refferral
|
* cifs_compose_mount_options - creates mount options for referral
|
||||||
* @sb_mountdata: parent/root DFS mount options (template)
|
* @sb_mountdata: parent/root DFS mount options (template)
|
||||||
* @fullpath: full path in UNC format
|
* @fullpath: full path in UNC format
|
||||||
* @ref: server's referral
|
* @ref: optional server's referral
|
||||||
* @devname: optional pointer for saving device name
|
* @devname: optional pointer for saving device name
|
||||||
*
|
*
|
||||||
* creates mount options for submount based on template options sb_mountdata
|
* creates mount options for submount based on template options sb_mountdata
|
||||||
* and replacing unc,ip,prefixpath options with ones we've got form ref_unc.
|
* and replacing unc,ip,prefixpath options with ones we've got form ref_unc.
|
||||||
*
|
*
|
||||||
* Returns: pointer to new mount options or ERR_PTR.
|
* Returns: pointer to new mount options or ERR_PTR.
|
||||||
* Caller is responcible for freeing retunrned value if it is not error.
|
* Caller is responsible for freeing returned value if it is not error.
|
||||||
*/
|
*/
|
||||||
char *cifs_compose_mount_options(const char *sb_mountdata,
|
char *cifs_compose_mount_options(const char *sb_mountdata,
|
||||||
const char *fullpath,
|
const char *fullpath,
|
||||||
|
@ -150,18 +150,27 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
|
||||||
if (sb_mountdata == NULL)
|
if (sb_mountdata == NULL)
|
||||||
return ERR_PTR(-EINVAL);
|
return ERR_PTR(-EINVAL);
|
||||||
|
|
||||||
if (strlen(fullpath) - ref->path_consumed) {
|
if (ref) {
|
||||||
prepath = fullpath + ref->path_consumed;
|
if (strlen(fullpath) - ref->path_consumed) {
|
||||||
/* skip initial delimiter */
|
prepath = fullpath + ref->path_consumed;
|
||||||
if (*prepath == '/' || *prepath == '\\')
|
/* skip initial delimiter */
|
||||||
prepath++;
|
if (*prepath == '/' || *prepath == '\\')
|
||||||
}
|
prepath++;
|
||||||
|
}
|
||||||
|
|
||||||
name = cifs_build_devname(ref->node_name, prepath);
|
name = cifs_build_devname(ref->node_name, prepath);
|
||||||
if (IS_ERR(name)) {
|
if (IS_ERR(name)) {
|
||||||
rc = PTR_ERR(name);
|
rc = PTR_ERR(name);
|
||||||
name = NULL;
|
name = NULL;
|
||||||
goto compose_mount_options_err;
|
goto compose_mount_options_err;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
name = cifs_build_devname((char *)fullpath, NULL);
|
||||||
|
if (IS_ERR(name)) {
|
||||||
|
rc = PTR_ERR(name);
|
||||||
|
name = NULL;
|
||||||
|
goto compose_mount_options_err;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = dns_resolve_server_name_to_ip(name, &srvIP);
|
rc = dns_resolve_server_name_to_ip(name, &srvIP);
|
||||||
|
@ -225,6 +234,8 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
|
||||||
|
|
||||||
if (devname)
|
if (devname)
|
||||||
*devname = name;
|
*devname = name;
|
||||||
|
else
|
||||||
|
kfree(name);
|
||||||
|
|
||||||
/*cifs_dbg(FYI, "%s: parent mountdata: %s\n", __func__, sb_mountdata);*/
|
/*cifs_dbg(FYI, "%s: parent mountdata: %s\n", __func__, sb_mountdata);*/
|
||||||
/*cifs_dbg(FYI, "%s: submount mountdata: %s\n", __func__, mountdata );*/
|
/*cifs_dbg(FYI, "%s: submount mountdata: %s\n", __func__, mountdata );*/
|
||||||
|
@ -241,23 +252,23 @@ compose_mount_options_err:
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* cifs_dfs_do_refmount - mounts specified path using provided refferal
|
* cifs_dfs_do_mount - mounts specified path using DFS full path
|
||||||
|
*
|
||||||
|
* Always pass down @fullpath to smb3_do_mount() so we can use the root server
|
||||||
|
* to perform failover in case we failed to connect to the first target in the
|
||||||
|
* referral.
|
||||||
|
*
|
||||||
* @cifs_sb: parent/root superblock
|
* @cifs_sb: parent/root superblock
|
||||||
* @fullpath: full path in UNC format
|
* @fullpath: full path in UNC format
|
||||||
* @ref: server's referral
|
|
||||||
*/
|
*/
|
||||||
static struct vfsmount *cifs_dfs_do_refmount(struct dentry *mntpt,
|
static struct vfsmount *cifs_dfs_do_mount(struct dentry *mntpt,
|
||||||
struct cifs_sb_info *cifs_sb,
|
struct cifs_sb_info *cifs_sb,
|
||||||
const char *fullpath, const struct dfs_info3_param *ref)
|
const char *fullpath)
|
||||||
{
|
{
|
||||||
struct vfsmount *mnt;
|
struct vfsmount *mnt;
|
||||||
char *mountdata;
|
char *mountdata;
|
||||||
char *devname;
|
char *devname;
|
||||||
|
|
||||||
/*
|
|
||||||
* Always pass down the DFS full path to smb3_do_mount() so we
|
|
||||||
* can use it later for failover.
|
|
||||||
*/
|
|
||||||
devname = kstrndup(fullpath, strlen(fullpath), GFP_KERNEL);
|
devname = kstrndup(fullpath, strlen(fullpath), GFP_KERNEL);
|
||||||
if (!devname)
|
if (!devname)
|
||||||
return ERR_PTR(-ENOMEM);
|
return ERR_PTR(-ENOMEM);
|
||||||
|
@ -266,7 +277,7 @@ static struct vfsmount *cifs_dfs_do_refmount(struct dentry *mntpt,
|
||||||
|
|
||||||
/* strip first '\' from fullpath */
|
/* strip first '\' from fullpath */
|
||||||
mountdata = cifs_compose_mount_options(cifs_sb->mountdata,
|
mountdata = cifs_compose_mount_options(cifs_sb->mountdata,
|
||||||
fullpath + 1, ref, NULL);
|
fullpath + 1, NULL, NULL);
|
||||||
if (IS_ERR(mountdata)) {
|
if (IS_ERR(mountdata)) {
|
||||||
kfree(devname);
|
kfree(devname);
|
||||||
return (struct vfsmount *)mountdata;
|
return (struct vfsmount *)mountdata;
|
||||||
|
@ -278,28 +289,16 @@ static struct vfsmount *cifs_dfs_do_refmount(struct dentry *mntpt,
|
||||||
return mnt;
|
return mnt;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void dump_referral(const struct dfs_info3_param *ref)
|
|
||||||
{
|
|
||||||
cifs_dbg(FYI, "DFS: ref path: %s\n", ref->path_name);
|
|
||||||
cifs_dbg(FYI, "DFS: node path: %s\n", ref->node_name);
|
|
||||||
cifs_dbg(FYI, "DFS: fl: %d, srv_type: %d\n",
|
|
||||||
ref->flags, ref->server_type);
|
|
||||||
cifs_dbg(FYI, "DFS: ref_flags: %d, path_consumed: %d\n",
|
|
||||||
ref->ref_flag, ref->path_consumed);
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create a vfsmount that we can automount
|
* Create a vfsmount that we can automount
|
||||||
*/
|
*/
|
||||||
static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
|
static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
|
||||||
{
|
{
|
||||||
struct dfs_info3_param referral = {0};
|
|
||||||
struct cifs_sb_info *cifs_sb;
|
struct cifs_sb_info *cifs_sb;
|
||||||
struct cifs_ses *ses;
|
struct cifs_ses *ses;
|
||||||
struct cifs_tcon *tcon;
|
struct cifs_tcon *tcon;
|
||||||
char *full_path, *root_path;
|
char *full_path, *root_path;
|
||||||
unsigned int xid;
|
unsigned int xid;
|
||||||
int len;
|
|
||||||
int rc;
|
int rc;
|
||||||
struct vfsmount *mnt;
|
struct vfsmount *mnt;
|
||||||
|
|
||||||
|
@ -357,7 +356,7 @@ static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
rc = dfs_cache_find(xid, ses, cifs_sb->local_nls,
|
rc = dfs_cache_find(xid, ses, cifs_sb->local_nls,
|
||||||
cifs_remap(cifs_sb), full_path + 1,
|
cifs_remap(cifs_sb), full_path + 1,
|
||||||
&referral, NULL);
|
NULL, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
free_xid(xid);
|
free_xid(xid);
|
||||||
|
@ -366,26 +365,16 @@ static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
|
||||||
mnt = ERR_PTR(rc);
|
mnt = ERR_PTR(rc);
|
||||||
goto free_root_path;
|
goto free_root_path;
|
||||||
}
|
}
|
||||||
|
|
||||||
dump_referral(&referral);
|
|
||||||
|
|
||||||
len = strlen(referral.node_name);
|
|
||||||
if (len < 2) {
|
|
||||||
cifs_dbg(VFS, "%s: Net Address path too short: %s\n",
|
|
||||||
__func__, referral.node_name);
|
|
||||||
mnt = ERR_PTR(-EINVAL);
|
|
||||||
goto free_dfs_ref;
|
|
||||||
}
|
|
||||||
/*
|
/*
|
||||||
* cifs_mount() will retry every available node server in case
|
* OK - we were able to get and cache a referral for @full_path.
|
||||||
* of failures.
|
*
|
||||||
|
* Now, pass it down to cifs_mount() and it will retry every available
|
||||||
|
* node server in case of failures - no need to do it here.
|
||||||
*/
|
*/
|
||||||
mnt = cifs_dfs_do_refmount(mntpt, cifs_sb, full_path, &referral);
|
mnt = cifs_dfs_do_mount(mntpt, cifs_sb, full_path);
|
||||||
cifs_dbg(FYI, "%s: cifs_dfs_do_refmount:%s , mnt:%p\n", __func__,
|
cifs_dbg(FYI, "%s: cifs_dfs_do_mount:%s , mnt:%p\n", __func__,
|
||||||
referral.node_name, mnt);
|
full_path + 1, mnt);
|
||||||
|
|
||||||
free_dfs_ref:
|
|
||||||
free_dfs_info_param(&referral);
|
|
||||||
free_root_path:
|
free_root_path:
|
||||||
kfree(root_path);
|
kfree(root_path);
|
||||||
free_full_path:
|
free_full_path:
|
||||||
|
|
Loading…
Reference in New Issue