--- lib/safe-read.c.orig	2007-11-18 03:46:56.000000000 -0700
+++ lib/safe-read.c	2011-09-27 14:46:37.000000000 -0600
@@ -55,23 +55,37 @@
 size_t
 safe_rw (int fd, void const *buf, size_t count)
 {
+  size_t total = 0, nbyte = count;
   /* Work around a bug in Tru64 5.1.  Attempting to read more than
      INT_MAX bytes fails with errno == EINVAL.  See
      <http://lists.gnu.org/archive/html/bug-gnu-utils/2002-04/msg00010.html>.
-     When decreasing COUNT, keep it block-aligned.  */
+     The SUSv2 also reports undefined behaviour for reads over SSIZE_MAX.
+     When decreasing nbyte, keep it block-aligned.  */
   enum { BUGGY_READ_MAXIMUM = INT_MAX & ~8191 };
 
+  if (SSIZE_MAX & ~8191 < nbyte)
+    nbyte = SSIZE_MAX & ~8191;
+
   for (;;)
     {
-      ssize_t result = rw (fd, buf, count);
+      ssize_t result = rw (fd, buf, nbyte);
 
       if (0 <= result)
-	return result;
+        {
+	  total += result;
+	  buf += result;
+
+	  if (0 == result || total >= count)
+	    return total;
+
+	  if (count - total < nbyte)
+	    nbyte = count - total;
+        }
       else if (IS_EINTR (errno))
 	continue;
-      else if (errno == EINVAL && BUGGY_READ_MAXIMUM < count)
-	count = BUGGY_READ_MAXIMUM;
+      else if (errno == EINVAL && BUGGY_READ_MAXIMUM < nbyte)
+	nbyte = BUGGY_READ_MAXIMUM;
       else
-	return result;
+	return total > 0 ? total : result;
     }
 }
