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

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
...@@ -49,58 +49,74 @@ void cifs_dfs_release_automount_timer(void) ...@@ -49,58 +49,74 @@ void cifs_dfs_release_automount_timer(void)
} }
/** /**
* cifs_get_share_name - extracts share name from UNC * cifs_build_devname - build a devicename from a UNC and optional prepath
* @node_name: pointer to UNC string * @nodename: pointer to UNC string
* @prepath: pointer to prefixpath (or NULL if there isn't one)
* *
* Extracts sharename form full UNC. * Build a new cifs devicename after chasing a DFS referral. Allocate a buffer
* i.e. strips from UNC trailing path that is not part of share * big enough to hold the final thing. Copy the UNC from the nodename, and
* name and fixup missing '\' in the beginning of DFS node refferal * concatenate the prepath onto the end of it if there is one.
* if necessary. *
* Returns pointer to share name on success or ERR_PTR on error. * Returns pointer to the built string, or a ERR_PTR. Caller is responsible
* Caller is responsible for freeing returned string. * 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; size_t pplen;
char *UNC; size_t unclen;
char *pSep; char *dev;
char *pos;
len = strlen(node_name);
UNC = kmalloc(len+2 /*for term null and additional \ if it's missed */, /* skip over any preceding delimiters */
GFP_KERNEL); nodename += strspn(nodename, "\\");
if (!UNC) if (!*nodename)
return ERR_PTR(-ENOMEM); return ERR_PTR(-EINVAL);
/* get share name and server name */ /* get length of UNC and set pos to last char */
if (node_name[1] != '\\') { unclen = strlen(nodename);
UNC[0] = '\\'; pos = nodename + unclen - 1;
strncpy(UNC+1, node_name, len);
len++;
UNC[len] = 0;
} else {
strncpy(UNC, node_name, len);
UNC[len] = 0;
}
/* find server name end */ /* trim off any trailing delimiters */
pSep = memchr(UNC+2, '\\', len-2); while (*pos == '\\') {
if (!pSep) { --pos;
cifs_dbg(VFS, "%s: no server name end in node name: %s\n", --unclen;
__func__, node_name);
kfree(UNC);
return ERR_PTR(-EINVAL);
} }
/* find sharename end */ /* allocate a buffer:
pSep++; * +2 for preceding "//"
pSep = memchr(UNC+(pSep-UNC), '\\', len-(pSep-UNC)); * +1 for delimiter between UNC and prepath
if (pSep) { * +1 for trailing NULL
/* trim path up to sharename end */
* now we have share name in UNC */ pplen = prepath ? strlen(prepath) : 0;
*pSep = 0; dev = kmalloc(2 + unclen + 1 + pplen + 1, GFP_KERNEL);
if (!dev)
return ERR_PTR(-ENOMEM);
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, ...@@ -124,6 +140,7 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
{ {
int rc; int rc;
char *mountdata = NULL; char *mountdata = NULL;
const char *prepath = NULL;
int md_len; int md_len;
char *tkn_e; char *tkn_e;
char *srvIP = NULL; char *srvIP = NULL;
...@@ -133,7 +150,10 @@ char *cifs_compose_mount_options(const char *sb_mountdata, ...@@ -133,7 +150,10 @@ 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);
*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)) { if (IS_ERR(*devname)) {
rc = PTR_ERR(*devname); rc = PTR_ERR(*devname);
*devname = NULL; *devname = NULL;
...@@ -147,13 +167,14 @@ char *cifs_compose_mount_options(const char *sb_mountdata, ...@@ -147,13 +167,14 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
goto compose_mount_options_err; goto compose_mount_options_err;
} }
/* md_len = strlen(...) + 12 for 'sep+prefixpath=' /*
* assuming that we have 'unc=' and 'ip=' in * In most cases, we'll be building a shorter string than the original,
* the original sb_mountdata * 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 + md_len = strlen(sb_mountdata) + INET6_ADDRSTRLEN;
INET6_ADDRSTRLEN; mountdata = kzalloc(md_len + 1, GFP_KERNEL);
mountdata = kzalloc(md_len+1, GFP_KERNEL);
if (mountdata == NULL) { if (mountdata == NULL) {
rc = -ENOMEM; rc = -ENOMEM;
goto compose_mount_options_err; goto compose_mount_options_err;
...@@ -197,26 +218,6 @@ char *cifs_compose_mount_options(const char *sb_mountdata, ...@@ -197,26 +218,6 @@ char *cifs_compose_mount_options(const char *sb_mountdata,
strncat(mountdata, &sep, 1); strncat(mountdata, &sep, 1);
strcat(mountdata, "ip="); strcat(mountdata, "ip=");
strcat(mountdata, srvIP); 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: 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 );*/
......
...@@ -34,7 +34,7 @@ ...@@ -34,7 +34,7 @@
/** /**
* dns_resolve_server_name_to_ip - Resolve UNC server name to ip address. * 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. * @ip_addr: Where to return the IP address.
* *
* The IP address will be returned in string form, and the caller is * 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) ...@@ -64,7 +64,7 @@ dns_resolve_server_name_to_ip(const char *unc, char **ip_addr)
hostname = unc + 2; hostname = unc + 2;
/* Search for server name delimiter */ /* Search for server name delimiter */
sep = memchr(hostname, '\\', len); sep = memchr(hostname, '/', len);
if (sep) if (sep)
len = sep - hostname; len = sep - hostname;
else else
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment