Folks,

The floating point output formatting routines used by rsync (snprintf)
contain a truncation issue, at least for gcc on Solaris (but probably
generally too).  This will result in values (e.g. bytes read, bytes
written, etc.) greater than LONG_MAX being truncated/mangled in the
summary reporting.  (Quite noticeable if you're mirroring BIG trees.)

  SunOS 5.6 Generic_105181-10 sun4u sparc SUNW,Ultra-250
  gcc version 2.95.2 19991024 (release)
  GNU assembler version 2.9.1 (sparc-sun-solaris2.5.1), using BFD version 2.9.1
  GNU ld version 2.9.1 (with BFD 2.9.1)

The problem arises from the fact that the snprintf implementation in
rsync chooses to convert the mantissa of a floating point number to a
long before formatting.  This is a reasonable and simple way to go
about doing this (although presumably we could grab some more
sophisticated routines from Linux glibc or something).

Here is a patch that provides a higher degree of precision for
machines with HAVE_LONGLONG (and is backward compatible for those
which do not).  Again, we could probably use a better implementation
overall if someone knows of one (as usual, I'm a bit short of cycles
myself at the moment .... :-)

--- rsync-2.4.3/lib/snprintf.c.orig     Tue May 30 15:29:50 2000
+++ rsync-2.4.3/lib/snprintf.c  Tue May 30 15:42:41 2000
@@ -89,10 +89,16 @@
 #define LDOUBLE long double
 #else
 #define LDOUBLE double
 #endif
 
+#if defined( HAVE_LONGLONG )
+# define LINTEGER long long
+#else
+# define LINTEGER long
+#endif
+
 /*int snprintf (char *str, size_t count, const char *fmt, ...);*/
 /*int vsnprintf (char *str, size_t count, const char *fmt, va_list arg);*/
 
 static void dopr (char *buffer, size_t maxlen, const char *format, 
                   va_list args);
@@ -555,19 +561,19 @@
 static void fmtfp (char *buffer, size_t *currlen, size_t maxlen,
                   LDOUBLE fvalue, int min, int max, int flags)
 {
   int signvalue = 0;
   LDOUBLE ufvalue;
-  char iconvert[20];
-  char fconvert[20];
+  char iconvert[30];
+  char fconvert[30];
   int iplace = 0;
   int fplace = 0;
   int padlen = 0; /* amount to pad */
   int zpadlen = 0; 
   int caps = 0;
-  long intpart;
-  long fracpart;
+  unsigned LINTEGER intpart;
+  unsigned LINTEGER fracpart;
   
   /* 
    * AIX manpage says the default is 0, but Solaris says the default
    * is 6, and sprintf on AIX defaults to 6
    */
@@ -587,11 +593,11 @@
 
 #if 0
   if (flags & DP_F_UP) caps = 1; /* Should characters be upper case? */
 #endif
 
-  intpart = (long)ufvalue;
+  intpart = ufvalue;
 
   /* 
    * Sorry, we only support 9 digits past the decimal because of our 
    * conversion method
    */
@@ -608,30 +614,33 @@
     intpart++;
     fracpart -= pow10 (max);
   }
 
 #ifdef DEBUG_SNPRINTF
-  printf("fmtfp: %g %d.%d min=%d max=%d\n", 
+# if !defined( HAVE_LONGLONG )
+  printf("fmtfp: %g %lu.%lu min=%d max=%d\n", 
+        (double)fvalue, intpart, fracpart, min, max);
+# else
+  printf("fmtfp: %g %llu.%llu min=%d max=%d\n", 
         (double)fvalue, intpart, fracpart, min, max);
+# endif
 #endif
 
   /* Convert integer part */
   do {
     iconvert[iplace++] =
       (caps? "0123456789ABCDEF":"0123456789abcdef")[intpart % 10];
     intpart = (intpart / 10);
-  } while(intpart && (iplace < 20));
-  if (iplace == 20) iplace--;
+  } while(intpart && (iplace < sizeof(iconvert)-1));
   iconvert[iplace] = 0;
 
   /* Convert fractional part */
   do {
     fconvert[fplace++] =
       (caps? "0123456789ABCDEF":"0123456789abcdef")[fracpart % 10];
     fracpart = (fracpart / 10);
-  } while(fracpart && (fplace < 20));
-  if (fplace == 20) fplace--;
+  } while(fracpart && (fplace < sizeof(fconvert)-1));
   fconvert[fplace] = 0;
 
   /* -1 for decimal point, another -1 if we are printing a sign */
   padlen = min - iplace - max - 1 - ((signvalue) ? 1 : 0); 
   zpadlen = max - fplace;

Regards,
Neil

-- 
Neil Schellenberger             | Voice : (613) 599-2300 ext. 8445
CrossKeys Systems Corporation   | Fax   : (613) 599-2330
350 Terry Fox Drive             | E-Mail: [EMAIL PROTECTED]
Kanata, Ont., Canada, K2K 2W5   | URL   : http://www.crosskeys.com/
    + Greg Moore (1975-1999), Gentleman racer and great Canadian +

Reply via email to