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

Commit d9deef0a authored by Jeff Layton's avatar Jeff Layton Committed by Steve French
Browse files

cifs: fix composing of mount options for DFS referrals



With the change to ignore the unc= and prefixpath= mount options, there
is no longer any need to add them to the options string when mounting.
By the same token, we now need to build a device name that includes the
prefixpath when mounting.

To make things neater, the delimiters on the devicename are changed
to '/' since that's preferred when mounting anyway.

v2: fix some comments and don't bother looking at whether there is
    a prepath in the ref->node_name when deciding whether to pass
    a prepath to cifs_build_devname.

v3: rebase on top of potential buffer overrun fix for stable

Signed-off-by: default avatarJeff Layton <jlayton@redhat.com>
Signed-off-by: default avatarSteve French <sfrench@us.ibm.com>
parent 9c9c29e1
Loading
Loading
Loading
Loading
+71 −70
Original line number Diff line number Diff line
@@ -49,58 +49,74 @@ void cifs_dfs_release_automount_timer(void)
}

/**
 * cifs_get_share_name	-	extracts share name from UNC
 * @node_name:	pointer to UNC string
 * cifs_build_devname - build a devicename from a UNC and optional prepath
 * @nodename:	pointer to UNC string
 * @prepath:	pointer to prefixpath (or NULL if there isn't one)
 *
 * Extracts sharename form full UNC.
 * i.e. strips from UNC trailing path that is not part of share
 * name and fixup missing '\' in the beginning of DFS node refferal
 * if necessary.
 * Returns pointer to share name on success or ERR_PTR on error.
 * Caller is responsible for freeing returned string.
 * Build a new cifs devicename after chasing a DFS referral. Allocate a buffer
 * big enough to hold the final thing. Copy the UNC from the nodename, and
 * concatenate the prepath onto the end of it if there is one.
 *
 * Returns pointer to the built string, or a ERR_PTR. Caller is responsible
 * for freeing the returned string.
 */
static char *cifs_get_share_name(const char *node_name)
static char *
cifs_build_devname(char *nodename, const char *prepath)
{
	int len;
	char *UNC;
	char *pSep;
	size_t pplen;
	size_t unclen;
	char *dev;
	char *pos;

	/* skip over any preceding delimiters */
	nodename += strspn(nodename, "\\");
	if (!*nodename)
		return ERR_PTR(-EINVAL);

	len = strlen(node_name);
	UNC = kmalloc(len+2 /*for term null and additional \ if it's missed */,
			 GFP_KERNEL);
	if (!UNC)
		return ERR_PTR(-ENOMEM);
	/* get length of UNC and set pos to last char */
	unclen = strlen(nodename);
	pos = nodename + unclen - 1;

	/* get share name and server name */
	if (node_name[1] != '\\') {
		UNC[0] = '\\';
		strncpy(UNC+1, node_name, len);
		len++;
		UNC[len] = 0;
	} else {
		strncpy(UNC, node_name, len);
		UNC[len] = 0;
	/* trim off any trailing delimiters */
	while (*pos == '\\') {
		--pos;
		--unclen;
	}

	/* find server name end */
	pSep = memchr(UNC+2, '\\', len-2);
	if (!pSep) {
		cifs_dbg(VFS, "%s: no server name end in node name: %s\n",
			 __func__, node_name);
		kfree(UNC);
		return ERR_PTR(-EINVAL);
	}
	/* allocate a buffer:
	 * +2 for preceding "//"
	 * +1 for delimiter between UNC and prepath
	 * +1 for trailing NULL
	 */
	pplen = prepath ? strlen(prepath) : 0;
	dev = kmalloc(2 + unclen + 1 + pplen + 1, GFP_KERNEL);
	if (!dev)
		return ERR_PTR(-ENOMEM);

	/* find sharename end */
	pSep++;
	pSep = memchr(UNC+(pSep-UNC), '\\', len-(pSep-UNC));
	if (pSep) {
		/* trim path up to sharename end
		 * now we have share name in UNC */
		*pSep = 0;
	pos = dev;
	/* add the initial "//" */
	*pos = '/';
	++pos;
	*pos = '/';
	++pos;

	/* copy in the UNC portion from referral */
	memcpy(pos, nodename, unclen);
	pos += unclen;

	/* copy the prefixpath remainder (if there is one) */
	if (pplen) {
		*pos = '/';
		++pos;
		memcpy(pos, prepath, pplen);
		pos += pplen;
	}

	return UNC;
	/* NULL terminator */
	*pos = '\0';

	convert_delimiter(dev, '/');
	return dev;
}


@@ -124,6 +140,7 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
{
	int rc;
	char *mountdata = NULL;
	const char *prepath = NULL;
	int md_len;
	char *tkn_e;
	char *srvIP = NULL;
@@ -133,7 +150,10 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
	if (sb_mountdata == NULL)
		return ERR_PTR(-EINVAL);

	*devname = cifs_get_share_name(ref->node_name);
	if (strlen(fullpath) - ref->path_consumed)
		prepath = fullpath + ref->path_consumed;

	*devname = cifs_build_devname(ref->node_name, prepath);
	if (IS_ERR(*devname)) {
		rc = PTR_ERR(*devname);
		*devname = NULL;
@@ -147,12 +167,13 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
		goto compose_mount_options_err;
	}

	/* md_len = strlen(...) + 12 for 'sep+prefixpath='
	 * assuming that we have 'unc=' and 'ip=' in
	 * the original sb_mountdata
	/*
	 * In most cases, we'll be building a shorter string than the original,
	 * but we do have to assume that the address in the ip= option may be
	 * much longer than the original. Add the max length of an address
	 * string to the length of the original string to allow for worst case.
	 */
	md_len = strlen(sb_mountdata) + rc + strlen(ref->node_name) + 12 +
			INET6_ADDRSTRLEN;
	md_len = strlen(sb_mountdata) + INET6_ADDRSTRLEN;
	mountdata = kzalloc(md_len + 1, GFP_KERNEL);
	if (mountdata == NULL) {
		rc = -ENOMEM;
@@ -197,26 +218,6 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
		strncat(mountdata, &sep, 1);
	strcat(mountdata, "ip=");
	strcat(mountdata, srvIP);
	strncat(mountdata, &sep, 1);
	strcat(mountdata, "unc=");
	strcat(mountdata, *devname);

	/* find & copy prefixpath */
	tkn_e = strchr(ref->node_name + 2, '\\');
	if (tkn_e == NULL) {
		/* invalid unc, missing share name*/
		rc = -EINVAL;
		goto compose_mount_options_err;
	}

	tkn_e = strchr(tkn_e + 1, '\\');
	if (tkn_e || (strlen(fullpath) - ref->path_consumed)) {
		strncat(mountdata, &sep, 1);
		strcat(mountdata, "prefixpath=");
		if (tkn_e)
			strcat(mountdata, tkn_e + 1);
		strcat(mountdata, fullpath + ref->path_consumed);
	}

	/*cifs_dbg(FYI, "%s: parent mountdata: %s\n", __func__, sb_mountdata);*/
	/*cifs_dbg(FYI, "%s: submount mountdata: %s\n", __func__, mountdata );*/
+2 −2
Original line number Diff line number Diff line
@@ -34,7 +34,7 @@

/**
 * dns_resolve_server_name_to_ip - Resolve UNC server name to ip address.
 * @unc: UNC path specifying the server
 * @unc: UNC path specifying the server (with '/' as delimiter)
 * @ip_addr: Where to return the IP address.
 *
 * The IP address will be returned in string form, and the caller is
@@ -64,7 +64,7 @@ dns_resolve_server_name_to_ip(const char *unc, char **ip_addr)
	hostname = unc + 2;

	/* Search for server name delimiter */
	sep = memchr(hostname, '\\', len);
	sep = memchr(hostname, '/', len);
	if (sep)
		len = sep - hostname;
	else