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 |
