df(1) correctly displays the number of 1K blocks in a 100PB filesystem, but displays wrong results when run with the "-h" flag: > df tank Filesystem 1K-blocks Used Avail Capacity Mounted on tank 107692009979824 19 107692009979805 0% /tank > df -h tank Filesystem Size Used Avail Capacity Mounted on tank -741 19K -741 0% /tank I suspect this is due to bugs in humanize_number from libdisk. How-To-Repeat: On a ZFS filesystem, run "truncate -s100P mdfile && mdconfig -a -f mdfile" then create a pool on the md device.
Responsible Changed From-To: freebsd-bugs->freebsd-fs
Responsible Changed From-To: freebsd-fs->freebsd-bugs Sorry my bad.
verified! defenately a problem with humanize_number(3). here's a testcase. cheers. alex -- a13x
----- Forwarded message from Xin LI <delphij@delphij.net> ----- Date: Mon, 21 Mar 2011 17:44:43 -0700 From: Xin LI <delphij@delphij.net> To: d@delphij.net Cc: Alexander Best <arundel@freebsd.org>, freebsd-bugs@FreeBSD.org Subject: Re: bin/146205: df(1) fails to display total space on a 100PB filesystem correctly -----BEGIN PGP SIGNED MESSAGE----- Hash: SHA256 Revised version, I have mistakenly understood the old code, which only keeps one digit after the dot. Cheers, - -- Xin LI <delphij@delphij.net> http://www.delphij.net/ FreeBSD - The Power to Serve! Live free or die -----BEGIN PGP SIGNATURE----- Version: GnuPG v2.0.17 (FreeBSD) iQEcBAEBCAAGBQJNh/D6AAoJEATO+BI/yjfBbDoH/25nmc7rQ914Rgfp2iVTBTCo 0J6D29V1tRfqFnzg62zYH23tsoQfcYI/gesx5TyegdCOpR1nOAEUl7T/UA4CmUuh CN54KNQ631WUT/V5Rndf5ikIcAnu7L5IP14RNGUWH5CJhi70OmrbWt3d2XKvLMvO Uhtq+Uhl/NyoGaq2tE9/WyGyeb70Ribzd4zE6hMVSSY9rPKASpCj3oSpAn7ioSms X9MvcUCM/eOQxLMYtq1PN6Dz5GPqewiBfmoOWNGssbYnhqotYpIKNUAkDeKUuFop P940nVkhhiSmsZPGD4W67fgKwXhxzj7Ukw7ru+XOp0z3afK6xxkkUQAvAfGop4g= =mx5z -----END PGP SIGNATURE----- Index: humanize_number.c =================================================================== --- humanize_number.c (revision 219842) +++ humanize_number.c (working copy) @@ -47,8 +47,9 @@ const char *suffix, int scale, int flags) { const char *prefixes, *sep; - int b, i, r, maxscale, s1, s2, sign; + int i, r, maxscale, s1, s2, sign; int64_t divisor, max; + int64_t quotient = bytes, reminder = 0; size_t baselen; assert(buf != NULL); @@ -88,11 +89,10 @@ buf[0] = '\0'; if (bytes < 0) { sign = -1; - bytes *= -100; + quotient = -quotient; baselen = 3; /* sign, digit, prefix */ } else { sign = 1; - bytes *= 100; baselen = 2; /* digit, prefix */ } if (flags & HN_NOSPACE) @@ -109,7 +109,7 @@ if (scale & (HN_AUTOSCALE | HN_GETSCALE)) { /* See if there is additional columns can be used. */ - for (max = 100, i = len - baselen; i-- > 0;) + for (max = 1, i = len - baselen; i-- > 0;) max *= 10; /* @@ -117,29 +117,33 @@ * If there will be an overflow by the rounding below, * divide once more. */ - for (i = 0; bytes >= max - 50 && i < maxscale; i++) - bytes /= divisor; + for (i = 0; quotient > max && i < maxscale; i++) { + reminder = quotient % divisor; + quotient /= divisor; + } if (scale & HN_GETSCALE) return (i); - } else - for (i = 0; i < scale && i < maxscale; i++) - bytes /= divisor; + } else { + for (i = 0; i < scale && i < maxscale; i++) { + reminder = quotient % divisor; + quotient /= divisor; + } + } /* If a value <= 9.9 after rounding and ... */ - if (bytes < 995 && i > 0 && flags & HN_DECIMAL) { + if (quotient == 9 && reminder < 950 && i > 0 && flags & HN_DECIMAL) { /* baselen + \0 + .N */ if (len < baselen + 1 + 2) return (-1); - b = ((int)bytes + 5) / 10; - s1 = b / 10; - s2 = b % 10; + s1 = (int)quotient + (((int)reminder + 50) / 100); + s2 = (((int)reminder + 50) / 10) % 10; r = snprintf(buf, len, "%d%s%d%s%s%s", sign * s1, localeconv()->decimal_point, s2, sep, SCALE2PREFIX(i), suffix); } else r = snprintf(buf, len, "%" PRId64 "%s%s%s", - sign * ((bytes + 50) / 100), + sign * (quotient + (reminder + 50) / 100), sep, SCALE2PREFIX(i), suffix); return (r); _______________________________________________ freebsd-bugs@freebsd.org mailing list http://lists.freebsd.org/mailman/listinfo/freebsd-bugs To unsubscribe, send any mail to "freebsd-bugs-unsubscribe@freebsd.org" ----- End forwarded message -----
State Changed From-To: open->analyzed Bump to analyzed, have a pending patch and some test cases and this is likely to be fixed really soon.
Responsible Changed From-To: freebsd-bugs->delphij Take.
State Changed From-To: analyzed->patched Patched in -HEAD.
Author: delphij Date: Wed Mar 23 22:08:01 2011 New Revision: 219939 URL: http://svn.freebsd.org/changeset/base/219939 Log: humanize_number(3) multiply the input number by 100, which could cause an integer overflow when the input is very large (for example, 100 Pi would become about 10 Ei which exceeded signed int64_t). Solve this issue by splitting the division into two parts and avoid the multiplication. PR: bin/146205 Reviewed by: arundel MFC after: 1 month Modified: head/lib/libutil/humanize_number.c Modified: head/lib/libutil/humanize_number.c ============================================================================== --- head/lib/libutil/humanize_number.c Wed Mar 23 22:06:09 2011 (r219938) +++ head/lib/libutil/humanize_number.c Wed Mar 23 22:08:01 2011 (r219939) @@ -43,11 +43,11 @@ __FBSDID("$FreeBSD$"); #include <libutil.h> int -humanize_number(char *buf, size_t len, int64_t bytes, +humanize_number(char *buf, size_t len, int64_t quotient, const char *suffix, int scale, int flags) { const char *prefixes, *sep; - int b, i, r, maxscale, s1, s2, sign; + int i, r, remainder, maxscale, s1, s2, sign; int64_t divisor, max; size_t baselen; @@ -55,6 +55,8 @@ humanize_number(char *buf, size_t len, i assert(suffix != NULL); assert(scale >= 0); + remainder = 0; + if (flags & HN_DIVISOR_1000) { /* SI for decimal multiplies */ divisor = 1000; @@ -86,13 +88,12 @@ humanize_number(char *buf, size_t len, i if (len > 0) buf[0] = '\0'; - if (bytes < 0) { + if (quotient < 0) { sign = -1; - bytes *= -100; + quotient = -quotient; baselen = 3; /* sign, digit, prefix */ } else { sign = 1; - bytes *= 100; baselen = 2; /* digit, prefix */ } if (flags & HN_NOSPACE) @@ -109,7 +110,7 @@ humanize_number(char *buf, size_t len, i if (scale & (HN_AUTOSCALE | HN_GETSCALE)) { /* See if there is additional columns can be used. */ - for (max = 100, i = len - baselen; i-- > 0;) + for (max = 1, i = len - baselen; i-- > 0;) max *= 10; /* @@ -117,30 +118,37 @@ humanize_number(char *buf, size_t len, i * If there will be an overflow by the rounding below, * divide once more. */ - for (i = 0; bytes >= max - 50 && i < maxscale; i++) - bytes /= divisor; + for (i = 0; + (quotient >= max || (quotient == max - 1 && remainder >= 950)) && + i < maxscale; i++) { + remainder = quotient % divisor; + quotient /= divisor; + } if (scale & HN_GETSCALE) return (i); - } else - for (i = 0; i < scale && i < maxscale; i++) - bytes /= divisor; + } else { + for (i = 0; i < scale && i < maxscale; i++) { + remainder = quotient % divisor; + quotient /= divisor; + } + } /* If a value <= 9.9 after rounding and ... */ - if (bytes < 995 && i > 0 && flags & HN_DECIMAL) { + if (quotient <= 9 && remainder < 950 && i > 0 && flags & HN_DECIMAL) { /* baselen + \0 + .N */ if (len < baselen + 1 + 2) return (-1); - b = ((int)bytes + 5) / 10; - s1 = b / 10; - s2 = b % 10; + s1 = (int)quotient + ((remainder + 50) / 1000); + s2 = ((remainder + 50) / 100) % 10; r = snprintf(buf, len, "%d%s%d%s%s%s", sign * s1, localeconv()->decimal_point, s2, sep, SCALE2PREFIX(i), suffix); } else r = snprintf(buf, len, "%" PRId64 "%s%s%s", - sign * ((bytes + 50) / 100), + sign * (quotient + (remainder + 50) / 1000), sep, SCALE2PREFIX(i), suffix); return (r); } + _______________________________________________ svn-src-all@freebsd.org mailing list http://lists.freebsd.org/mailman/listinfo/svn-src-all To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
Author: delphij Date: Fri Apr 22 00:31:07 2011 New Revision: 220933 URL: http://svn.freebsd.org/changeset/base/220933 Log: MFC r219939: humanize_number(3) multiply the input number by 100, which could cause an integer overflow when the input is very large (for example, 100 Pi would become about 10 Ei which exceeded signed int64_t). Solve this issue by splitting the division into two parts and avoid the multiplication. PR: bin/146205 Reviewed by: arundel Modified: stable/8/lib/libutil/humanize_number.c Directory Properties: stable/8/lib/libutil/ (props changed) Modified: stable/8/lib/libutil/humanize_number.c ============================================================================== --- stable/8/lib/libutil/humanize_number.c Thu Apr 21 23:06:00 2011 (r220932) +++ stable/8/lib/libutil/humanize_number.c Fri Apr 22 00:31:07 2011 (r220933) @@ -43,11 +43,11 @@ __FBSDID("$FreeBSD$"); #include <libutil.h> int -humanize_number(char *buf, size_t len, int64_t bytes, +humanize_number(char *buf, size_t len, int64_t quotient, const char *suffix, int scale, int flags) { const char *prefixes, *sep; - int b, i, r, maxscale, s1, s2, sign; + int i, r, remainder, maxscale, s1, s2, sign; int64_t divisor, max; size_t baselen; @@ -55,6 +55,8 @@ humanize_number(char *buf, size_t len, i assert(suffix != NULL); assert(scale >= 0); + remainder = 0; + if (flags & HN_DIVISOR_1000) { /* SI for decimal multiplies */ divisor = 1000; @@ -86,13 +88,12 @@ humanize_number(char *buf, size_t len, i if (len > 0) buf[0] = '\0'; - if (bytes < 0) { + if (quotient < 0) { sign = -1; - bytes *= -100; + quotient = -quotient; baselen = 3; /* sign, digit, prefix */ } else { sign = 1; - bytes *= 100; baselen = 2; /* digit, prefix */ } if (flags & HN_NOSPACE) @@ -109,7 +110,7 @@ humanize_number(char *buf, size_t len, i if (scale & (HN_AUTOSCALE | HN_GETSCALE)) { /* See if there is additional columns can be used. */ - for (max = 100, i = len - baselen; i-- > 0;) + for (max = 1, i = len - baselen; i-- > 0;) max *= 10; /* @@ -117,30 +118,37 @@ humanize_number(char *buf, size_t len, i * If there will be an overflow by the rounding below, * divide once more. */ - for (i = 0; bytes >= max - 50 && i < maxscale; i++) - bytes /= divisor; + for (i = 0; + (quotient >= max || (quotient == max - 1 && remainder >= 950)) && + i < maxscale; i++) { + remainder = quotient % divisor; + quotient /= divisor; + } if (scale & HN_GETSCALE) return (i); - } else - for (i = 0; i < scale && i < maxscale; i++) - bytes /= divisor; + } else { + for (i = 0; i < scale && i < maxscale; i++) { + remainder = quotient % divisor; + quotient /= divisor; + } + } /* If a value <= 9.9 after rounding and ... */ - if (bytes < 995 && i > 0 && flags & HN_DECIMAL) { + if (quotient <= 9 && remainder < 950 && i > 0 && flags & HN_DECIMAL) { /* baselen + \0 + .N */ if (len < baselen + 1 + 2) return (-1); - b = ((int)bytes + 5) / 10; - s1 = b / 10; - s2 = b % 10; + s1 = (int)quotient + ((remainder + 50) / 1000); + s2 = ((remainder + 50) / 100) % 10; r = snprintf(buf, len, "%d%s%d%s%s%s", sign * s1, localeconv()->decimal_point, s2, sep, SCALE2PREFIX(i), suffix); } else r = snprintf(buf, len, "%" PRId64 "%s%s%s", - sign * ((bytes + 50) / 100), + sign * (quotient + (remainder + 50) / 1000), sep, SCALE2PREFIX(i), suffix); return (r); } + _______________________________________________ svn-src-all@freebsd.org mailing list http://lists.freebsd.org/mailman/listinfo/svn-src-all To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
State Changed From-To: patched->closed Fix merged to 7 and 8-STABLE.
Author: delphij Date: Fri Apr 22 00:32:16 2011 New Revision: 220934 URL: http://svn.freebsd.org/changeset/base/220934 Log: MFC r219939: humanize_number(3) multiply the input number by 100, which could cause an integer overflow when the input is very large (for example, 100 Pi would become about 10 Ei which exceeded signed int64_t). Solve this issue by splitting the division into two parts and avoid the multiplication. PR: bin/146205 Reviewed by: arundel Modified: stable/7/lib/libutil/humanize_number.c Directory Properties: stable/7/lib/libutil/ (props changed) Modified: stable/7/lib/libutil/humanize_number.c ============================================================================== --- stable/7/lib/libutil/humanize_number.c Fri Apr 22 00:31:07 2011 (r220933) +++ stable/7/lib/libutil/humanize_number.c Fri Apr 22 00:32:16 2011 (r220934) @@ -50,11 +50,11 @@ __FBSDID("$FreeBSD$"); #include <libutil.h> int -humanize_number(char *buf, size_t len, int64_t bytes, +humanize_number(char *buf, size_t len, int64_t quotient, const char *suffix, int scale, int flags) { const char *prefixes, *sep; - int b, i, r, maxscale, s1, s2, sign; + int i, r, remainder, maxscale, s1, s2, sign; int64_t divisor, max; size_t baselen; @@ -62,6 +62,8 @@ humanize_number(char *buf, size_t len, i assert(suffix != NULL); assert(scale >= 0); + remainder = 0; + if (flags & HN_DIVISOR_1000) { /* SI for decimal multiplies */ divisor = 1000; @@ -93,13 +95,12 @@ humanize_number(char *buf, size_t len, i if (len > 0) buf[0] = '\0'; - if (bytes < 0) { + if (quotient < 0) { sign = -1; - bytes *= -100; + quotient = -quotient; baselen = 3; /* sign, digit, prefix */ } else { sign = 1; - bytes *= 100; baselen = 2; /* digit, prefix */ } if (flags & HN_NOSPACE) @@ -116,7 +117,7 @@ humanize_number(char *buf, size_t len, i if (scale & (HN_AUTOSCALE | HN_GETSCALE)) { /* See if there is additional columns can be used. */ - for (max = 100, i = len - baselen; i-- > 0;) + for (max = 1, i = len - baselen; i-- > 0;) max *= 10; /* @@ -124,30 +125,37 @@ humanize_number(char *buf, size_t len, i * If there will be an overflow by the rounding below, * divide once more. */ - for (i = 0; bytes >= max - 50 && i < maxscale; i++) - bytes /= divisor; + for (i = 0; + (quotient >= max || (quotient == max - 1 && remainder >= 950)) && + i < maxscale; i++) { + remainder = quotient % divisor; + quotient /= divisor; + } if (scale & HN_GETSCALE) return (i); - } else - for (i = 0; i < scale && i < maxscale; i++) - bytes /= divisor; + } else { + for (i = 0; i < scale && i < maxscale; i++) { + remainder = quotient % divisor; + quotient /= divisor; + } + } /* If a value <= 9.9 after rounding and ... */ - if (bytes < 995 && i > 0 && flags & HN_DECIMAL) { + if (quotient <= 9 && remainder < 950 && i > 0 && flags & HN_DECIMAL) { /* baselen + \0 + .N */ if (len < baselen + 1 + 2) return (-1); - b = ((int)bytes + 5) / 10; - s1 = b / 10; - s2 = b % 10; + s1 = (int)quotient + ((remainder + 50) / 1000); + s2 = ((remainder + 50) / 100) % 10; r = snprintf(buf, len, "%d%s%d%s%s%s", sign * s1, localeconv()->decimal_point, s2, sep, SCALE2PREFIX(i), suffix); } else r = snprintf(buf, len, "%" PRId64 "%s%s%s", - sign * ((bytes + 50) / 100), + sign * (quotient + (remainder + 50) / 1000), sep, SCALE2PREFIX(i), suffix); return (r); } + _______________________________________________ svn-src-all@freebsd.org mailing list http://lists.freebsd.org/mailman/listinfo/svn-src-all To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"