Add GNU-compatible strerror_r.

We already had the POSIX strerror_r, but some third-party code defines
_GNU_SOURCE and expects to get the GNU strerror_r instead.

This exposed a bug in the libc internal logging functions where unlike
their standard brethren they wouldn't return the number of bytes they'd
have liked to have written.

Bug: 16243479
Change-Id: I1745752ccbdc569646d34f5071f6df2be066d5f4
diff --git a/libc/bionic/libc_logging.cpp b/libc/bionic/libc_logging.cpp
index 5655526..49a3762 100644
--- a/libc/bionic/libc_logging.cpp
+++ b/libc/bionic/libc_logging.cpp
@@ -75,10 +75,12 @@
       len = strlen(data);
     }
 
+    total += len;
+
     while (len > 0) {
       int avail = end_ - pos_;
       if (avail == 0) {
-        break;
+        return;
       }
       if (avail > len) {
         avail = len;
@@ -87,11 +89,10 @@
       pos_ += avail;
       pos_[0] = '\0';
       len -= avail;
-      total += avail;
     }
   }
 
-  int total;
+  size_t total;
 
  private:
   char* buffer_;
@@ -109,18 +110,19 @@
       len = strlen(data);
     }
 
+    total += len;
+
     while (len > 0) {
       int rc = TEMP_FAILURE_RETRY(write(fd_, data, len));
       if (rc == -1) {
-        break;
+        return;
       }
       data += rc;
       len -= rc;
-      total += rc;
     }
   }
 
-  int total;
+  size_t total;
 
  private:
   int fd_;
diff --git a/libc/bionic/strerror_r.cpp b/libc/bionic/strerror_r.cpp
index 1e57cc0..d419fb1 100644
--- a/libc/bionic/strerror_r.cpp
+++ b/libc/bionic/strerror_r.cpp
@@ -1,11 +1,16 @@
 /* $OpenBSD: strerror_r.c,v 1.6 2005/08/08 08:05:37 espie Exp $ */
 /* Public Domain <marc@snafu.org> */
 
+// G++ automatically defines _GNU_SOURCE, which then means that <string.h>
+// gives us the GNU variant.
+#undef _GNU_SOURCE
+
+#include <string.h>
+
 #include <errno.h>
 #include <limits.h>
 #include <signal.h>
 #include <stdio.h>
-#include <string.h>
 
 #include "private/ErrnoRestorer.h"
 #include "private/libc_logging.h"
@@ -62,6 +67,12 @@
   return 0;
 }
 
+extern "C" char* __gnu_strerror_r(int error_number, char* buf, size_t buf_len) {
+  ErrnoRestorer errno_restorer; // The glibc strerror_r doesn't set errno if it truncates...
+  strerror_r(error_number, buf, buf_len);
+  return buf; // ...and just returns whatever fit.
+}
+
 extern "C" __LIBC_HIDDEN__ const char* __strsignal(int signal_number, char* buf, size_t buf_len) {
   const char* signal_name = __strsignal_lookup(signal_number);
   if (signal_name != NULL) {
diff --git a/libc/include/string.h b/libc/include/string.h
index 0a1d653..b0643af 100644
--- a/libc/include/string.h
+++ b/libc/include/string.h
@@ -67,8 +67,12 @@
 extern char*  strtok(char* __restrict, const char* __restrict);
 extern char*  strtok_r(char* __restrict, const char* __restrict, char** __restrict);
 
-extern char*  strerror(int);
-extern int    strerror_r(int errnum, char *buf, size_t n);
+extern char* strerror(int);
+#if defined(__USE_GNU)
+extern char* strerror_r(int, char*, size_t) __RENAME(__gnu_strerror_r);
+#else /* POSIX */
+extern int strerror_r(int, char*, size_t);
+#endif
 
 extern size_t strnlen(const char *, size_t) __purefunc;
 extern char*  strncat(char* __restrict, const char* __restrict, size_t);