Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 9c5ede11 authored by Paulo Alcantara (SUSE)'s avatar Paulo Alcantara (SUSE) Committed by Greg Kroah-Hartman
Browse files

cifs: Fix mount options set in automount



[ Upstream commit 5739375ee4230980166807d347cc21c305532bbc ]

Starting from 4a367dc0, 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: default avatarPaulo Alcantara (SUSE) <pc@cjr.nz>
Reported-by: default avatarMartijn de Gouw <martijn.de.gouw@prodrive-technologies.com>
Fixes: 4a367dc0 ("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: default avatarMartijn de Gouw <martijn.de.gouw@prodrive-technologies.com>
Signed-off-by: default avatarSteve French <stfrench@microsoft.com>
Signed-off-by: default avatarSasha Levin <sashal@kernel.org>
parent 1d8e40cf
Loading
Loading
Loading
Loading
+43 −54
Original line number Diff line number Diff line
@@ -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,6 +150,7 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
	if (sb_mountdata == NULL)
		return ERR_PTR(-EINVAL);

	if (ref) {
		if (strlen(fullpath) - ref->path_consumed) {
			prepath = fullpath + ref->path_consumed;
			/* skip initial delimiter */
@@ -163,6 +164,14 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
			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);
	if (rc < 0) {
@@ -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,
static struct vfsmount *cifs_dfs_do_mount(struct dentry *mntpt,
					  struct cifs_sb_info *cifs_sb,
		const char *fullpath, const struct dfs_info3_param *ref)
					  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: