Bug 146205 - [patch] humanize_number(3): df fails to display total space on a 100PB filesystem correctly
Summary: [patch] humanize_number(3): df fails to display total space on a 100PB filesy...
Status: Closed FIXED
Alias: None
Product: Base System
Classification: Unclassified
Component: bin (show other bugs)
Version: 9.0-CURRENT
Hardware: Any Any
: Normal Affects Only Me
Assignee: Xin LI
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2010-05-01 01:00 UTC by Rebecca Cran
Modified: 2011-04-22 01:40 UTC (History)
0 users

See Also:


Attachments
humanize_number.c (319 bytes, text/x-csrc; charset=us-ascii)
2011-03-20 22:07 UTC, Alexander Best
no flags Details

Note You need to log in before you can comment on or make changes to this bug.
Description Rebecca Cran freebsd_committer freebsd_triage 2010-05-01 01:00:20 UTC
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.
Comment 1 Alexander Best freebsd_committer freebsd_triage 2011-03-20 21:42:44 UTC
Responsible Changed
From-To: freebsd-bugs->freebsd-fs
Comment 2 Alexander Best freebsd_committer freebsd_triage 2011-03-20 21:53:03 UTC
Responsible Changed
From-To: freebsd-fs->freebsd-bugs

Sorry my bad.
Comment 3 Alexander Best freebsd_committer freebsd_triage 2011-03-20 22:07:22 UTC
verified!

defenately a problem with humanize_number(3). here's a testcase.

cheers.
alex

-- 
a13x
Comment 4 Mark Linimon 2011-03-22 01:35:17 UTC
----- 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 -----
Comment 5 Xin LI freebsd_committer freebsd_triage 2011-03-23 21:13:16 UTC
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. 


Comment 6 Xin LI freebsd_committer freebsd_triage 2011-03-23 21:13:16 UTC
Responsible Changed
From-To: freebsd-bugs->delphij

Take.
Comment 7 Xin LI freebsd_committer freebsd_triage 2011-03-23 22:08:12 UTC
State Changed
From-To: analyzed->patched

Patched in -HEAD.
Comment 8 dfilter service freebsd_committer freebsd_triage 2011-03-23 22:08:15 UTC
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"
Comment 9 dfilter service freebsd_committer freebsd_triage 2011-04-22 01:31:22 UTC
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"
Comment 10 Xin LI freebsd_committer freebsd_triage 2011-04-22 01:32:25 UTC
State Changed
From-To: patched->closed

Fix merged to 7 and 8-STABLE.
Comment 11 dfilter service freebsd_committer freebsd_triage 2011-04-22 01:32:25 UTC
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"