diff options
| -rw-r--r-- | include/linux/string.h | 3 | ||||
| -rw-r--r-- | lib/string.c | 16 | ||||
| -rw-r--r-- | net/core/dev.c | 67 |
3 files changed, 65 insertions, 21 deletions
diff --git a/include/linux/string.h b/include/linux/string.h index f37b7a6813d3..6ad4e5c32f22 100644 --- a/include/linux/string.h +++ b/include/linux/string.h @@ -52,6 +52,9 @@ extern int strnicmp(const char *, const char *, __kernel_size_t); #ifndef __HAVE_ARCH_STRCHR extern char * strchr(const char *,int); #endif +#ifndef __HAVE_ARCH_STRNCHR +extern char * strnchr(const char *, size_t, int); +#endif #ifndef __HAVE_ARCH_STRRCHR extern char * strrchr(const char *,int); #endif diff --git a/lib/string.c b/lib/string.c index e660de079a57..d2f23f2c1e69 100644 --- a/lib/string.c +++ b/lib/string.c @@ -273,6 +273,22 @@ char * strrchr(const char * s, int c) } #endif +#ifndef __HAVE_ARCH_STRNCHR +/** + * strnchr - Find a character in a length limited string + * @s: The string to be searched + * @count: The number of characters to be searched + * @c: The character to search for + */ +char *strnchr(const char *s, size_t count, int c) +{ + for (; count-- && *s != '\0'; ++s) + if (*s == (char) c) + return (char *) s; + return NULL; +} +#endif + #ifndef __HAVE_ARCH_STRLEN /** * strlen - Find the length of a string diff --git a/net/core/dev.c b/net/core/dev.c index 17e1fa1b1815..d1dfcef63c5a 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -720,30 +720,55 @@ int dev_valid_name(const char *name) int dev_alloc_name(struct net_device *dev, const char *name) { - int i; - char buf[32]; - char *p; - - /* - * Verify the string as this thing may have come from - * the user. There must be either one "%d" and no other "%" - * characters, or no "%" characters at all. - */ - p = strchr(name, '%'); - if (p && (p[1] != 'd' || strchr(p + 2, '%'))) - return -EINVAL; + int i = 0; + char buf[IFNAMSIZ]; + const char *p; + const int max_netdevices = 8*PAGE_SIZE; + long *inuse; + struct net_device *d; + + p = strnchr(name, IFNAMSIZ-1, '%'); + if (p) { + /* + * Verify the string as this thing may have come from + * the user. There must be either one "%d" and no other "%" + * characters. + */ + if (p[1] != 'd' || strchr(p + 2, '%')) + return -EINVAL; - /* - * If you need over 100 please also fix the algorithm... - */ - for (i = 0; i < 100; i++) { - snprintf(buf, sizeof(buf), name, i); - if (!__dev_get_by_name(buf)) { - strcpy(dev->name, buf); - return i; + /* Use one page as a bit array of possible slots */ + inuse = (long *) get_zeroed_page(GFP_ATOMIC); + if (!inuse) + return -ENOMEM; + + for (d = dev_base; d; d = d->next) { + if (!sscanf(d->name, name, &i)) + continue; + if (i < 0 || i >= max_netdevices) + continue; + + /* avoid cases where sscanf is not exact inverse of printf */ + snprintf(buf, sizeof(buf), name, i); + if (!strncmp(buf, d->name, IFNAMSIZ)) + set_bit(i, inuse); } + + i = find_first_zero_bit(inuse, max_netdevices); + free_page((unsigned long) inuse); } - return -ENFILE; /* Over 100 of the things .. bail out! */ + + snprintf(buf, sizeof(buf), name, i); + if (!__dev_get_by_name(buf)) { + strlcpy(dev->name, buf, IFNAMSIZ); + return i; + } + + /* It is possible to run out of possible slots + * when the name is long and there isn't enough space left + * for the digits, or if all bits are used. + */ + return -ENFILE; } |
