diff options
Diffstat (limited to 'lib/bitmap.c')
-rw-r--r-- | lib/bitmap.c | 109 |
1 files changed, 97 insertions, 12 deletions
diff --git a/lib/bitmap.c b/lib/bitmap.c index 91e0ccfdb424..41baf02924e6 100644 --- a/lib/bitmap.c +++ b/lib/bitmap.c | |||
@@ -571,8 +571,11 @@ int bitmap_scnlistprintf(char *buf, unsigned int buflen, | |||
571 | EXPORT_SYMBOL(bitmap_scnlistprintf); | 571 | EXPORT_SYMBOL(bitmap_scnlistprintf); |
572 | 572 | ||
573 | /** | 573 | /** |
574 | * bitmap_parselist - convert list format ASCII string to bitmap | 574 | * __bitmap_parselist - convert list format ASCII string to bitmap |
575 | * @bp: read nul-terminated user string from this buffer | 575 | * @bp: read nul-terminated user string from this buffer |
576 | * @buflen: buffer size in bytes. If string is smaller than this | ||
577 | * then it must be terminated with a \0. | ||
578 | * @is_user: location of buffer, 0 indicates kernel space | ||
576 | * @maskp: write resulting mask here | 579 | * @maskp: write resulting mask here |
577 | * @nmaskbits: number of bits in mask to be written | 580 | * @nmaskbits: number of bits in mask to be written |
578 | * | 581 | * |
@@ -587,20 +590,63 @@ EXPORT_SYMBOL(bitmap_scnlistprintf); | |||
587 | * %-EINVAL: invalid character in string | 590 | * %-EINVAL: invalid character in string |
588 | * %-ERANGE: bit number specified too large for mask | 591 | * %-ERANGE: bit number specified too large for mask |
589 | */ | 592 | */ |
590 | int bitmap_parselist(const char *bp, unsigned long *maskp, int nmaskbits) | 593 | static int __bitmap_parselist(const char *buf, unsigned int buflen, |
594 | int is_user, unsigned long *maskp, | ||
595 | int nmaskbits) | ||
591 | { | 596 | { |
592 | unsigned a, b; | 597 | unsigned a, b; |
598 | int c, old_c, totaldigits; | ||
599 | const char __user *ubuf = buf; | ||
600 | int exp_digit, in_range; | ||
593 | 601 | ||
602 | totaldigits = c = 0; | ||
594 | bitmap_zero(maskp, nmaskbits); | 603 | bitmap_zero(maskp, nmaskbits); |
595 | do { | 604 | do { |
596 | if (!isdigit(*bp)) | 605 | exp_digit = 1; |
597 | return -EINVAL; | 606 | in_range = 0; |
598 | b = a = simple_strtoul(bp, (char **)&bp, BASEDEC); | 607 | a = b = 0; |
599 | if (*bp == '-') { | 608 | |
600 | bp++; | 609 | /* Get the next cpu# or a range of cpu#'s */ |
601 | if (!isdigit(*bp)) | 610 | while (buflen) { |
611 | old_c = c; | ||
612 | if (is_user) { | ||
613 | if (__get_user(c, ubuf++)) | ||
614 | return -EFAULT; | ||
615 | } else | ||
616 | c = *buf++; | ||
617 | buflen--; | ||
618 | if (isspace(c)) | ||
619 | continue; | ||
620 | |||
621 | /* | ||
622 | * If the last character was a space and the current | ||
623 | * character isn't '\0', we've got embedded whitespace. | ||
624 | * This is a no-no, so throw an error. | ||
625 | */ | ||
626 | if (totaldigits && c && isspace(old_c)) | ||
627 | return -EINVAL; | ||
628 | |||
629 | /* A '\0' or a ',' signal the end of a cpu# or range */ | ||
630 | if (c == '\0' || c == ',') | ||
631 | break; | ||
632 | |||
633 | if (c == '-') { | ||
634 | if (exp_digit || in_range) | ||
635 | return -EINVAL; | ||
636 | b = 0; | ||
637 | in_range = 1; | ||
638 | exp_digit = 1; | ||
639 | continue; | ||
640 | } | ||
641 | |||
642 | if (!isdigit(c)) | ||
602 | return -EINVAL; | 643 | return -EINVAL; |
603 | b = simple_strtoul(bp, (char **)&bp, BASEDEC); | 644 | |
645 | b = b * 10 + (c - '0'); | ||
646 | if (!in_range) | ||
647 | a = b; | ||
648 | exp_digit = 0; | ||
649 | totaldigits++; | ||
604 | } | 650 | } |
605 | if (!(a <= b)) | 651 | if (!(a <= b)) |
606 | return -EINVAL; | 652 | return -EINVAL; |
@@ -610,13 +656,52 @@ int bitmap_parselist(const char *bp, unsigned long *maskp, int nmaskbits) | |||
610 | set_bit(a, maskp); | 656 | set_bit(a, maskp); |
611 | a++; | 657 | a++; |
612 | } | 658 | } |
613 | if (*bp == ',') | 659 | } while (buflen && c == ','); |
614 | bp++; | ||
615 | } while (*bp != '\0' && *bp != '\n'); | ||
616 | return 0; | 660 | return 0; |
617 | } | 661 | } |
662 | |||
663 | int bitmap_parselist(const char *bp, unsigned long *maskp, int nmaskbits) | ||
664 | { | ||
665 | char *nl = strchr(bp, '\n'); | ||
666 | int len; | ||
667 | |||
668 | if (nl) | ||
669 | len = nl - bp; | ||
670 | else | ||
671 | len = strlen(bp); | ||
672 | |||
673 | return __bitmap_parselist(bp, len, 0, maskp, nmaskbits); | ||
674 | } | ||
618 | EXPORT_SYMBOL(bitmap_parselist); | 675 | EXPORT_SYMBOL(bitmap_parselist); |
619 | 676 | ||
677 | |||
678 | /** | ||
679 | * bitmap_parselist_user() | ||
680 | * | ||
681 | * @ubuf: pointer to user buffer containing string. | ||
682 | * @ulen: buffer size in bytes. If string is smaller than this | ||
683 | * then it must be terminated with a \0. | ||
684 | * @maskp: pointer to bitmap array that will contain result. | ||
685 | * @nmaskbits: size of bitmap, in bits. | ||
686 | * | ||
687 | * Wrapper for bitmap_parselist(), providing it with user buffer. | ||
688 | * | ||
689 | * We cannot have this as an inline function in bitmap.h because it needs | ||
690 | * linux/uaccess.h to get the access_ok() declaration and this causes | ||
691 | * cyclic dependencies. | ||
692 | */ | ||
693 | int bitmap_parselist_user(const char __user *ubuf, | ||
694 | unsigned int ulen, unsigned long *maskp, | ||
695 | int nmaskbits) | ||
696 | { | ||
697 | if (!access_ok(VERIFY_READ, ubuf, ulen)) | ||
698 | return -EFAULT; | ||
699 | return __bitmap_parselist((const char *)ubuf, | ||
700 | ulen, 1, maskp, nmaskbits); | ||
701 | } | ||
702 | EXPORT_SYMBOL(bitmap_parselist_user); | ||
703 | |||
704 | |||
620 | /** | 705 | /** |
621 | * bitmap_pos_to_ord - find ordinal of set bit at given position in bitmap | 706 | * bitmap_pos_to_ord - find ordinal of set bit at given position in bitmap |
622 | * @buf: pointer to a bitmap | 707 | * @buf: pointer to a bitmap |