mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2025-01-08 14:23:19 +00:00
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)
|
||||
* @fullpath: full path in UNC format
|
||||
* @ref: server's referral
|
||||
* @ref: optional server's referral
|
||||
* @devname: optional pointer for saving device name
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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,
|
||||
const char *fullpath,
|
||||
@ -150,18 +150,27 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
|
||||
if (sb_mountdata == NULL)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (strlen(fullpath) - ref->path_consumed) {
|
||||
prepath = fullpath + ref->path_consumed;
|
||||
/* skip initial delimiter */
|
||||
if (*prepath == '/' || *prepath == '\\')
|
||||
prepath++;
|
||||
}
|
||||
if (ref) {
|
||||
if (strlen(fullpath) - ref->path_consumed) {
|
||||
prepath = fullpath + ref->path_consumed;
|
||||
/* skip initial delimiter */
|
||||
if (*prepath == '/' || *prepath == '\\')
|
||||
prepath++;
|
||||
}
|
||||
|
||||
name = cifs_build_devname(ref->node_name, prepath);
|
||||
if (IS_ERR(name)) {
|
||||
rc = PTR_ERR(name);
|
||||
name = NULL;
|
||||
goto compose_mount_options_err;
|
||||
name = cifs_build_devname(ref->node_name, prepath);
|
||||
if (IS_ERR(name)) {
|
||||
rc = PTR_ERR(name);
|
||||
name = NULL;
|
||||
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);
|
||||
@ -225,6 +234,8 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
|
||||
|
||||
if (devname)
|
||||
*devname = name;
|
||||
else
|
||||
kfree(name);
|
||||
|
||||
/*cifs_dbg(FYI, "%s: parent mountdata: %s\n", __func__, sb_mountdata);*/
|
||||
/*cifs_dbg(FYI, "%s: submount mountdata: %s\n", __func__, mountdata );*/
|
||||
@ -241,23 +252,23 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @fullpath: full path in UNC format
|
||||
* @ref: server's referral
|
||||
*/
|
||||
static struct vfsmount *cifs_dfs_do_refmount(struct dentry *mntpt,
|
||||
struct cifs_sb_info *cifs_sb,
|
||||
const char *fullpath, const struct dfs_info3_param *ref)
|
||||
static struct vfsmount *cifs_dfs_do_mount(struct dentry *mntpt,
|
||||
struct cifs_sb_info *cifs_sb,
|
||||
const char *fullpath)
|
||||
{
|
||||
struct vfsmount *mnt;
|
||||
char *mountdata;
|
||||
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);
|
||||
if (!devname)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
@ -266,7 +277,7 @@ static struct vfsmount *cifs_dfs_do_refmount(struct dentry *mntpt,
|
||||
|
||||
/* strip first '\' from fullpath */
|
||||
mountdata = cifs_compose_mount_options(cifs_sb->mountdata,
|
||||
fullpath + 1, ref, NULL);
|
||||
fullpath + 1, NULL, NULL);
|
||||
if (IS_ERR(mountdata)) {
|
||||
kfree(devname);
|
||||
return (struct vfsmount *)mountdata;
|
||||
@ -278,28 +289,16 @@ static struct vfsmount *cifs_dfs_do_refmount(struct dentry *mntpt,
|
||||
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
|
||||
*/
|
||||
static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
|
||||
{
|
||||
struct dfs_info3_param referral = {0};
|
||||
struct cifs_sb_info *cifs_sb;
|
||||
struct cifs_ses *ses;
|
||||
struct cifs_tcon *tcon;
|
||||
char *full_path, *root_path;
|
||||
unsigned int xid;
|
||||
int len;
|
||||
int rc;
|
||||
struct vfsmount *mnt;
|
||||
|
||||
@ -357,7 +356,7 @@ static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
|
||||
if (!rc) {
|
||||
rc = dfs_cache_find(xid, ses, cifs_sb->local_nls,
|
||||
cifs_remap(cifs_sb), full_path + 1,
|
||||
&referral, NULL);
|
||||
NULL, NULL);
|
||||
}
|
||||
|
||||
free_xid(xid);
|
||||
@ -366,26 +365,16 @@ static struct vfsmount *cifs_dfs_do_automount(struct dentry *mntpt)
|
||||
mnt = ERR_PTR(rc);
|
||||
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
|
||||
* of failures.
|
||||
* OK - we were able to get and cache a referral for @full_path.
|
||||
*
|
||||
* 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);
|
||||
cifs_dbg(FYI, "%s: cifs_dfs_do_refmount:%s , mnt:%p\n", __func__,
|
||||
referral.node_name, mnt);
|
||||
mnt = cifs_dfs_do_mount(mntpt, cifs_sb, full_path);
|
||||
cifs_dbg(FYI, "%s: cifs_dfs_do_mount:%s , mnt:%p\n", __func__,
|
||||
full_path + 1, mnt);
|
||||
|
||||
free_dfs_ref:
|
||||
free_dfs_info_param(&referral);
|
||||
free_root_path:
|
||||
kfree(root_path);
|
||||
free_full_path:
|
||||
|
Loading…
Reference in New Issue
Block a user