cifs: cifs_parse_mount_options: do not tokenize mount options in-place

To keep strings passed to cifs_parse_mount_options re-usable (which is
needed to clean up the DFS referral handling), tokenize a copy of the
mount options instead.  If values are needed from this tokenized string,
they too must be duplicated (previously, some options were copied and
others duplicated).

Since we are not on the critical path and any cleanup is relatively easy,
the extra memory usage shouldn't be a problem (and it is a bit simpler
than trying to implement something smarter).

Reviewed-by: Jeff Layton <jlayton@redhat.com>
Signed-off-by: Sean Finney <seanius@seanius.net>
Signed-off-by: Pavel Shilovsky <piastry@etersoft.ru>
Signed-off-by: Steve French <sfrench@us.ibm.com>
This commit is contained in:
Sean Finney 2011-04-11 13:19:32 +00:00 коммит произвёл Steve French
Родитель c1508ca236
Коммит b946845a9d
1 изменённых файлов: 74 добавлений и 35 удалений

Просмотреть файл

@ -818,10 +818,11 @@ extract_hostname(const char *unc)
} }
static int static int
cifs_parse_mount_options(char *options, const char *devname, cifs_parse_mount_options(const char *mountdata, const char *devname,
struct smb_vol *vol) struct smb_vol *vol)
{ {
char *value, *data, *end; char *value, *data, *end;
char *mountdata_copy, *options;
unsigned int temp_len, i, j; unsigned int temp_len, i, j;
char separator[2]; char separator[2];
short int override_uid = -1; short int override_uid = -1;
@ -861,9 +862,14 @@ cifs_parse_mount_options(char *options, const char *devname,
vol->actimeo = CIFS_DEF_ACTIMEO; vol->actimeo = CIFS_DEF_ACTIMEO;
if (!options) if (!mountdata)
return 1; goto cifs_parse_mount_err;
mountdata_copy = kstrndup(mountdata, PAGE_SIZE, GFP_KERNEL);
if (!mountdata_copy)
goto cifs_parse_mount_err;
options = mountdata_copy;
end = options + strlen(options); end = options + strlen(options);
if (strncmp(options, "sep=", 4) == 0) { if (strncmp(options, "sep=", 4) == 0) {
if (options[4] != 0) { if (options[4] != 0) {
@ -889,17 +895,22 @@ cifs_parse_mount_options(char *options, const char *devname,
if (!value) { if (!value) {
printk(KERN_WARNING printk(KERN_WARNING
"CIFS: invalid or missing username\n"); "CIFS: invalid or missing username\n");
return 1; /* needs_arg; */ goto cifs_parse_mount_err;
} else if (!*value) { } else if (!*value) {
/* null user, ie anonymous, authentication */ /* null user, ie anonymous, authentication */
vol->nullauth = 1; vol->nullauth = 1;
} }
if (strnlen(value, MAX_USERNAME_SIZE) < if (strnlen(value, MAX_USERNAME_SIZE) <
MAX_USERNAME_SIZE) { MAX_USERNAME_SIZE) {
vol->username = value; vol->username = kstrdup(value, GFP_KERNEL);
if (!vol->username) {
printk(KERN_WARNING "CIFS: no memory "
"for username\n");
goto cifs_parse_mount_err;
}
} else { } else {
printk(KERN_WARNING "CIFS: username too long\n"); printk(KERN_WARNING "CIFS: username too long\n");
return 1; goto cifs_parse_mount_err;
} }
} else if (strnicmp(data, "pass", 4) == 0) { } else if (strnicmp(data, "pass", 4) == 0) {
if (!value) { if (!value) {
@ -963,7 +974,7 @@ cifs_parse_mount_options(char *options, const char *devname,
if (vol->password == NULL) { if (vol->password == NULL) {
printk(KERN_WARNING "CIFS: no memory " printk(KERN_WARNING "CIFS: no memory "
"for password\n"); "for password\n");
return 1; goto cifs_parse_mount_err;
} }
for (i = 0, j = 0; i < temp_len; i++, j++) { for (i = 0, j = 0; i < temp_len; i++, j++) {
vol->password[j] = value[i]; vol->password[j] = value[i];
@ -979,7 +990,7 @@ cifs_parse_mount_options(char *options, const char *devname,
if (vol->password == NULL) { if (vol->password == NULL) {
printk(KERN_WARNING "CIFS: no memory " printk(KERN_WARNING "CIFS: no memory "
"for password\n"); "for password\n");
return 1; goto cifs_parse_mount_err;
} }
strcpy(vol->password, value); strcpy(vol->password, value);
} }
@ -989,11 +1000,16 @@ cifs_parse_mount_options(char *options, const char *devname,
vol->UNCip = NULL; vol->UNCip = NULL;
} else if (strnlen(value, INET6_ADDRSTRLEN) < } else if (strnlen(value, INET6_ADDRSTRLEN) <
INET6_ADDRSTRLEN) { INET6_ADDRSTRLEN) {
vol->UNCip = value; vol->UNCip = kstrdup(value, GFP_KERNEL);
if (!vol->UNCip) {
printk(KERN_WARNING "CIFS: no memory "
"for UNC IP\n");
goto cifs_parse_mount_err;
}
} else { } else {
printk(KERN_WARNING "CIFS: ip address " printk(KERN_WARNING "CIFS: ip address "
"too long\n"); "too long\n");
return 1; goto cifs_parse_mount_err;
} }
} else if (strnicmp(data, "sec", 3) == 0) { } else if (strnicmp(data, "sec", 3) == 0) {
if (!value || !*value) { if (!value || !*value) {
@ -1006,7 +1022,7 @@ cifs_parse_mount_options(char *options, const char *devname,
/* vol->secFlg |= CIFSSEC_MUST_SEAL | /* vol->secFlg |= CIFSSEC_MUST_SEAL |
CIFSSEC_MAY_KRB5; */ CIFSSEC_MAY_KRB5; */
cERROR(1, "Krb5 cifs privacy not supported"); cERROR(1, "Krb5 cifs privacy not supported");
return 1; goto cifs_parse_mount_err;
} else if (strnicmp(value, "krb5", 4) == 0) { } else if (strnicmp(value, "krb5", 4) == 0) {
vol->secFlg |= CIFSSEC_MAY_KRB5; vol->secFlg |= CIFSSEC_MAY_KRB5;
} else if (strnicmp(value, "ntlmsspi", 8) == 0) { } else if (strnicmp(value, "ntlmsspi", 8) == 0) {
@ -1036,7 +1052,7 @@ cifs_parse_mount_options(char *options, const char *devname,
vol->nullauth = 1; vol->nullauth = 1;
} else { } else {
cERROR(1, "bad security option: %s", value); cERROR(1, "bad security option: %s", value);
return 1; goto cifs_parse_mount_err;
} }
} else if (strnicmp(data, "vers", 3) == 0) { } else if (strnicmp(data, "vers", 3) == 0) {
if (!value || !*value) { if (!value || !*value) {
@ -1060,12 +1076,12 @@ cifs_parse_mount_options(char *options, const char *devname,
if (!value || !*value) { if (!value || !*value) {
printk(KERN_WARNING "CIFS: invalid path to " printk(KERN_WARNING "CIFS: invalid path to "
"network resource\n"); "network resource\n");
return 1; /* needs_arg; */ goto cifs_parse_mount_err;
} }
if ((temp_len = strnlen(value, 300)) < 300) { if ((temp_len = strnlen(value, 300)) < 300) {
vol->UNC = kmalloc(temp_len+1, GFP_KERNEL); vol->UNC = kmalloc(temp_len+1, GFP_KERNEL);
if (vol->UNC == NULL) if (vol->UNC == NULL)
return 1; goto cifs_parse_mount_err;
strcpy(vol->UNC, value); strcpy(vol->UNC, value);
if (strncmp(vol->UNC, "//", 2) == 0) { if (strncmp(vol->UNC, "//", 2) == 0) {
vol->UNC[0] = '\\'; vol->UNC[0] = '\\';
@ -1074,27 +1090,32 @@ cifs_parse_mount_options(char *options, const char *devname,
printk(KERN_WARNING printk(KERN_WARNING
"CIFS: UNC Path does not begin " "CIFS: UNC Path does not begin "
"with // or \\\\ \n"); "with // or \\\\ \n");
return 1; goto cifs_parse_mount_err;
} }
} else { } else {
printk(KERN_WARNING "CIFS: UNC name too long\n"); printk(KERN_WARNING "CIFS: UNC name too long\n");
return 1; goto cifs_parse_mount_err;
} }
} else if ((strnicmp(data, "domain", 3) == 0) } else if ((strnicmp(data, "domain", 3) == 0)
|| (strnicmp(data, "workgroup", 5) == 0)) { || (strnicmp(data, "workgroup", 5) == 0)) {
if (!value || !*value) { if (!value || !*value) {
printk(KERN_WARNING "CIFS: invalid domain name\n"); printk(KERN_WARNING "CIFS: invalid domain name\n");
return 1; /* needs_arg; */ goto cifs_parse_mount_err;
} }
/* BB are there cases in which a comma can be valid in /* BB are there cases in which a comma can be valid in
a domain name and need special handling? */ a domain name and need special handling? */
if (strnlen(value, 256) < 256) { if (strnlen(value, 256) < 256) {
vol->domainname = value; vol->domainname = kstrdup(value, GFP_KERNEL);
if (!vol->domainname) {
printk(KERN_WARNING "CIFS: no memory "
"for domainname\n");
goto cifs_parse_mount_err;
}
cFYI(1, "Domain name set"); cFYI(1, "Domain name set");
} else { } else {
printk(KERN_WARNING "CIFS: domain name too " printk(KERN_WARNING "CIFS: domain name too "
"long\n"); "long\n");
return 1; goto cifs_parse_mount_err;
} }
} else if (strnicmp(data, "srcaddr", 7) == 0) { } else if (strnicmp(data, "srcaddr", 7) == 0) {
vol->srcaddr.ss_family = AF_UNSPEC; vol->srcaddr.ss_family = AF_UNSPEC;
@ -1102,7 +1123,7 @@ cifs_parse_mount_options(char *options, const char *devname,
if (!value || !*value) { if (!value || !*value) {
printk(KERN_WARNING "CIFS: srcaddr value" printk(KERN_WARNING "CIFS: srcaddr value"
" not specified.\n"); " not specified.\n");
return 1; /* needs_arg; */ goto cifs_parse_mount_err;
} }
i = cifs_convert_address((struct sockaddr *)&vol->srcaddr, i = cifs_convert_address((struct sockaddr *)&vol->srcaddr,
value, strlen(value)); value, strlen(value));
@ -1110,20 +1131,20 @@ cifs_parse_mount_options(char *options, const char *devname,
printk(KERN_WARNING "CIFS: Could not parse" printk(KERN_WARNING "CIFS: Could not parse"
" srcaddr: %s\n", " srcaddr: %s\n",
value); value);
return 1; goto cifs_parse_mount_err;
} }
} else if (strnicmp(data, "prefixpath", 10) == 0) { } else if (strnicmp(data, "prefixpath", 10) == 0) {
if (!value || !*value) { if (!value || !*value) {
printk(KERN_WARNING printk(KERN_WARNING
"CIFS: invalid path prefix\n"); "CIFS: invalid path prefix\n");
return 1; /* needs_argument */ goto cifs_parse_mount_err;
} }
if ((temp_len = strnlen(value, 1024)) < 1024) { if ((temp_len = strnlen(value, 1024)) < 1024) {
if (value[0] != '/') if (value[0] != '/')
temp_len++; /* missing leading slash */ temp_len++; /* missing leading slash */
vol->prepath = kmalloc(temp_len+1, GFP_KERNEL); vol->prepath = kmalloc(temp_len+1, GFP_KERNEL);
if (vol->prepath == NULL) if (vol->prepath == NULL)
return 1; goto cifs_parse_mount_err;
if (value[0] != '/') { if (value[0] != '/') {
vol->prepath[0] = '/'; vol->prepath[0] = '/';
strcpy(vol->prepath+1, value); strcpy(vol->prepath+1, value);
@ -1132,24 +1153,33 @@ cifs_parse_mount_options(char *options, const char *devname,
cFYI(1, "prefix path %s", vol->prepath); cFYI(1, "prefix path %s", vol->prepath);
} else { } else {
printk(KERN_WARNING "CIFS: prefix too long\n"); printk(KERN_WARNING "CIFS: prefix too long\n");
return 1; goto cifs_parse_mount_err;
} }
} else if (strnicmp(data, "iocharset", 9) == 0) { } else if (strnicmp(data, "iocharset", 9) == 0) {
if (!value || !*value) { if (!value || !*value) {
printk(KERN_WARNING "CIFS: invalid iocharset " printk(KERN_WARNING "CIFS: invalid iocharset "
"specified\n"); "specified\n");
return 1; /* needs_arg; */ goto cifs_parse_mount_err;
} }
if (strnlen(value, 65) < 65) { if (strnlen(value, 65) < 65) {
if (strnicmp(value, "default", 7)) if (strnicmp(value, "default", 7)) {
vol->iocharset = value; vol->iocharset = kstrdup(value,
GFP_KERNEL);
if (!vol->iocharset) {
printk(KERN_WARNING "CIFS: no "
"memory for"
"charset\n");
goto cifs_parse_mount_err;
}
}
/* if iocharset not set then load_nls_default /* if iocharset not set then load_nls_default
is used by caller */ is used by caller */
cFYI(1, "iocharset set to %s", value); cFYI(1, "iocharset set to %s", value);
} else { } else {
printk(KERN_WARNING "CIFS: iocharset name " printk(KERN_WARNING "CIFS: iocharset name "
"too long.\n"); "too long.\n");
return 1; goto cifs_parse_mount_err;
} }
} else if (!strnicmp(data, "uid", 3) && value && *value) { } else if (!strnicmp(data, "uid", 3) && value && *value) {
vol->linux_uid = simple_strtoul(value, &value, 0); vol->linux_uid = simple_strtoul(value, &value, 0);
@ -1262,7 +1292,7 @@ cifs_parse_mount_options(char *options, const char *devname,
if (vol->actimeo > CIFS_MAX_ACTIMEO) { if (vol->actimeo > CIFS_MAX_ACTIMEO) {
cERROR(1, "CIFS: attribute cache" cERROR(1, "CIFS: attribute cache"
"timeout too large"); "timeout too large");
return 1; goto cifs_parse_mount_err;
} }
} }
} else if (strnicmp(data, "credentials", 4) == 0) { } else if (strnicmp(data, "credentials", 4) == 0) {
@ -1406,7 +1436,7 @@ cifs_parse_mount_options(char *options, const char *devname,
#ifndef CONFIG_CIFS_FSCACHE #ifndef CONFIG_CIFS_FSCACHE
cERROR(1, "FS-Cache support needs CONFIG_CIFS_FSCACHE" cERROR(1, "FS-Cache support needs CONFIG_CIFS_FSCACHE"
"kernel config option set"); "kernel config option set");
return 1; goto cifs_parse_mount_err;
#endif #endif
vol->fsc = true; vol->fsc = true;
} else if (strnicmp(data, "mfsymlinks", 10) == 0) { } else if (strnicmp(data, "mfsymlinks", 10) == 0) {
@ -1421,12 +1451,12 @@ cifs_parse_mount_options(char *options, const char *devname,
if (devname == NULL) { if (devname == NULL) {
printk(KERN_WARNING "CIFS: Missing UNC name for mount " printk(KERN_WARNING "CIFS: Missing UNC name for mount "
"target\n"); "target\n");
return 1; goto cifs_parse_mount_err;
} }
if ((temp_len = strnlen(devname, 300)) < 300) { if ((temp_len = strnlen(devname, 300)) < 300) {
vol->UNC = kmalloc(temp_len+1, GFP_KERNEL); vol->UNC = kmalloc(temp_len+1, GFP_KERNEL);
if (vol->UNC == NULL) if (vol->UNC == NULL)
return 1; goto cifs_parse_mount_err;
strcpy(vol->UNC, devname); strcpy(vol->UNC, devname);
if (strncmp(vol->UNC, "//", 2) == 0) { if (strncmp(vol->UNC, "//", 2) == 0) {
vol->UNC[0] = '\\'; vol->UNC[0] = '\\';
@ -1434,21 +1464,21 @@ cifs_parse_mount_options(char *options, const char *devname,
} else if (strncmp(vol->UNC, "\\\\", 2) != 0) { } else if (strncmp(vol->UNC, "\\\\", 2) != 0) {
printk(KERN_WARNING "CIFS: UNC Path does not " printk(KERN_WARNING "CIFS: UNC Path does not "
"begin with // or \\\\ \n"); "begin with // or \\\\ \n");
return 1; goto cifs_parse_mount_err;
} }
value = strpbrk(vol->UNC+2, "/\\"); value = strpbrk(vol->UNC+2, "/\\");
if (value) if (value)
*value = '\\'; *value = '\\';
} else { } else {
printk(KERN_WARNING "CIFS: UNC name too long\n"); printk(KERN_WARNING "CIFS: UNC name too long\n");
return 1; goto cifs_parse_mount_err;
} }
} }
if (vol->multiuser && !(vol->secFlg & CIFSSEC_MAY_KRB5)) { if (vol->multiuser && !(vol->secFlg & CIFSSEC_MAY_KRB5)) {
cERROR(1, "Multiuser mounts currently require krb5 " cERROR(1, "Multiuser mounts currently require krb5 "
"authentication!"); "authentication!");
return 1; goto cifs_parse_mount_err;
} }
if (vol->UNCip == NULL) if (vol->UNCip == NULL)
@ -1466,7 +1496,12 @@ cifs_parse_mount_options(char *options, const char *devname,
printk(KERN_NOTICE "CIFS: ignoring forcegid mount option " printk(KERN_NOTICE "CIFS: ignoring forcegid mount option "
"specified with no gid= option.\n"); "specified with no gid= option.\n");
kfree(mountdata_copy);
return 0; return 0;
cifs_parse_mount_err:
kfree(mountdata_copy);
return 1;
} }
/** Returns true if srcaddr isn't specified and rhs isn't /** Returns true if srcaddr isn't specified and rhs isn't
@ -2707,8 +2742,12 @@ cleanup_volume_info(struct smb_vol **pvolume_info)
return; return;
volume_info = *pvolume_info; volume_info = *pvolume_info;
kfree(volume_info->username);
kzfree(volume_info->password); kzfree(volume_info->password);
kfree(volume_info->UNC); kfree(volume_info->UNC);
kfree(volume_info->UNCip);
kfree(volume_info->domainname);
kfree(volume_info->iocharset);
kfree(volume_info->prepath); kfree(volume_info->prepath);
kfree(volume_info); kfree(volume_info);
*pvolume_info = NULL; *pvolume_info = NULL;