X-Git-Url: http://erislabs.net/gitweb/?a=blobdiff_plain;f=lib%2Fstrerror_r.c;h=e86d272a4f480ba3e530f19aba63c0e02cfcb152;hb=a8a1dc286152661e640f0839e3ccd79e0898db6d;hp=fe1185ba4e06b03877ca86f8a331aa496e56b93a;hpb=b79d34835799d78d05b99916bfc363850905601c;p=gnulib.git diff --git a/lib/strerror_r.c b/lib/strerror_r.c index fe1185ba4..e86d272a4 100644 --- a/lib/strerror_r.c +++ b/lib/strerror_r.c @@ -1,6 +1,6 @@ /* strerror_r.c --- POSIX compatible system error routine - Copyright (C) 2010-2011 Free Software Foundation, Inc. + Copyright (C) 2010-2013 Free Software Foundation, Inc. This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -26,19 +26,17 @@ #include #include +#include +#include -# if GNULIB_defined_ESOCK /* native Windows platforms */ -# if HAVE_WINSOCK2_H -# include -# endif -# endif - +#include "strerror-override.h" #if (__GLIBC__ >= 2 || defined __UCLIBC__ || defined __CYGWIN__) && HAVE___XPG_STRERROR_R /* glibc >= 2.3.4, cygwin >= 1.7.9 */ # define USE_XPG_STRERROR_R 1 +extern int __xpg_strerror_r (int errnum, char *buf, size_t buflen); -#elif HAVE_DECL_STRERROR_R && !(__GLIBC__ >= 2 || defined __UCLIBC__) +#elif HAVE_DECL_STRERROR_R && !(__GLIBC__ >= 2 || defined __UCLIBC__ || defined __CYGWIN__) /* The system's strerror_r function is OK, except that its third argument is 'int', not 'size_t', or its return type is wrong. */ @@ -47,14 +45,16 @@ # define USE_SYSTEM_STRERROR_R 1 -#else /* (__GLIBC__ >= 2 || defined __UCLIBC__ ? !HAVE___XPG_STRERROR_R : !HAVE_DECL_STRERROR_R) */ +#else /* (__GLIBC__ >= 2 || defined __UCLIBC__ || defined __CYGWIN__ ? !HAVE___XPG_STRERROR_R : !HAVE_DECL_STRERROR_R) */ -/* Use the system's strerror(). */ +/* Use the system's strerror(). Exclude glibc and cygwin because the + system strerror_r has the wrong return type, and cygwin 1.7.9 + strerror_r clobbers strerror. */ # undef strerror # define USE_SYSTEM_STRERROR 1 -# if defined __NetBSD__ || defined __hpux || ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) || defined __sgi || (defined __sun && !defined _LP64) +# if defined __NetBSD__ || defined __hpux || ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) || defined __sgi || (defined __sun && !defined _LP64) || defined __CYGWIN__ /* No locking needed. */ @@ -75,9 +75,6 @@ extern char *sys_errlist[]; extern int sys_nerr; # endif -/* Get sys_nerr, sys_errlist on native Windows. */ -# include - # else # include "glthread/lock.h" @@ -90,436 +87,169 @@ gl_lock_define_initialized(static, strerror_lock) #endif - -int -strerror_r (int errnum, char *buf, size_t buflen) -#undef strerror_r +/* On MSVC, there is no snprintf() function, just a _snprintf(). + It is of lower quality, but sufficient for the simple use here. + We only have to make sure to NUL terminate the result (_snprintf + does not NUL terminate, like strncpy). */ +#if !HAVE_SNPRINTF +static int +local_snprintf (char *buf, size_t buflen, const char *format, ...) { -#if GNULIB_defined_ETXTBSY \ - || GNULIB_defined_ESOCK \ - || GNULIB_defined_ENOMSG \ - || GNULIB_defined_EIDRM \ - || GNULIB_defined_ENOLINK \ - || GNULIB_defined_EPROTO \ - || GNULIB_defined_EMULTIHOP \ - || GNULIB_defined_EBADMSG \ - || GNULIB_defined_EOVERFLOW \ - || GNULIB_defined_ENOTSUP \ - || GNULIB_defined_ESTALE \ - || GNULIB_defined_EDQUOT \ - || GNULIB_defined_ECANCELED - { - char const *msg = NULL; - /* These error messages are taken from glibc/sysdeps/gnu/errlist.c. */ - switch (errnum) - { -# if GNULIB_defined_ETXTBSY - case ETXTBSY: - msg = "Text file busy"; - break; -# endif - -# if GNULIB_defined_ESOCK /* native Windows platforms */ - /* EWOULDBLOCK is the same as EAGAIN. */ - case EINPROGRESS: - msg = "Operation now in progress"; - break; - case EALREADY: - msg = "Operation already in progress"; - break; - case ENOTSOCK: - msg = "Socket operation on non-socket"; - break; - case EDESTADDRREQ: - msg = "Destination address required"; - break; - case EMSGSIZE: - msg = "Message too long"; - break; - case EPROTOTYPE: - msg = "Protocol wrong type for socket"; - break; - case ENOPROTOOPT: - msg = "Protocol not available"; - break; - case EPROTONOSUPPORT: - msg = "Protocol not supported"; - break; - case ESOCKTNOSUPPORT: - msg = "Socket type not supported"; - break; - case EOPNOTSUPP: - msg = "Operation not supported"; - break; - case EPFNOSUPPORT: - msg = "Protocol family not supported"; - break; - case EAFNOSUPPORT: - msg = "Address family not supported by protocol"; - break; - case EADDRINUSE: - msg = "Address already in use"; - break; - case EADDRNOTAVAIL: - msg = "Cannot assign requested address"; - break; - case ENETDOWN: - msg = "Network is down"; - break; - case ENETUNREACH: - msg = "Network is unreachable"; - break; - case ENETRESET: - msg = "Network dropped connection on reset"; - break; - case ECONNABORTED: - msg = "Software caused connection abort"; - break; - case ECONNRESET: - msg = "Connection reset by peer"; - break; - case ENOBUFS: - msg = "No buffer space available"; - break; - case EISCONN: - msg = "Transport endpoint is already connected"; - break; - case ENOTCONN: - msg = "Transport endpoint is not connected"; - break; - case ESHUTDOWN: - msg = "Cannot send after transport endpoint shutdown"; - break; - case ETOOMANYREFS: - msg = "Too many references: cannot splice"; - break; - case ETIMEDOUT: - msg = "Connection timed out"; - break; - case ECONNREFUSED: - msg = "Connection refused"; - break; - case ELOOP: - msg = "Too many levels of symbolic links"; - break; - case EHOSTDOWN: - msg = "Host is down"; - break; - case EHOSTUNREACH: - msg = "No route to host"; - break; - case EPROCLIM: - msg = "Too many processes"; - break; - case EUSERS: - msg = "Too many users"; - break; - case EDQUOT: - msg = "Disk quota exceeded"; - break; - case ESTALE: - msg = "Stale NFS file handle"; - break; - case EREMOTE: - msg = "Object is remote"; - break; -# if HAVE_WINSOCK2_H - /* WSA_INVALID_HANDLE maps to EBADF */ - /* WSA_NOT_ENOUGH_MEMORY maps to ENOMEM */ - /* WSA_INVALID_PARAMETER maps to EINVAL */ - case WSA_OPERATION_ABORTED: - msg = "Overlapped operation aborted"; - break; - case WSA_IO_INCOMPLETE: - msg = "Overlapped I/O event object not in signaled state"; - break; - case WSA_IO_PENDING: - msg = "Overlapped operations will complete later"; - break; - /* WSAEINTR maps to EINTR */ - /* WSAEBADF maps to EBADF */ - /* WSAEACCES maps to EACCES */ - /* WSAEFAULT maps to EFAULT */ - /* WSAEINVAL maps to EINVAL */ - /* WSAEMFILE maps to EMFILE */ - /* WSAEWOULDBLOCK maps to EWOULDBLOCK */ - /* WSAEINPROGRESS is EINPROGRESS */ - /* WSAEALREADY is EALREADY */ - /* WSAENOTSOCK is ENOTSOCK */ - /* WSAEDESTADDRREQ is EDESTADDRREQ */ - /* WSAEMSGSIZE is EMSGSIZE */ - /* WSAEPROTOTYPE is EPROTOTYPE */ - /* WSAENOPROTOOPT is ENOPROTOOPT */ - /* WSAEPROTONOSUPPORT is EPROTONOSUPPORT */ - /* WSAESOCKTNOSUPPORT is ESOCKTNOSUPPORT */ - /* WSAEOPNOTSUPP is EOPNOTSUPP */ - /* WSAEPFNOSUPPORT is EPFNOSUPPORT */ - /* WSAEAFNOSUPPORT is EAFNOSUPPORT */ - /* WSAEADDRINUSE is EADDRINUSE */ - /* WSAEADDRNOTAVAIL is EADDRNOTAVAIL */ - /* WSAENETDOWN is ENETDOWN */ - /* WSAENETUNREACH is ENETUNREACH */ - /* WSAENETRESET is ENETRESET */ - /* WSAECONNABORTED is ECONNABORTED */ - /* WSAECONNRESET is ECONNRESET */ - /* WSAENOBUFS is ENOBUFS */ - /* WSAEISCONN is EISCONN */ - /* WSAENOTCONN is ENOTCONN */ - /* WSAESHUTDOWN is ESHUTDOWN */ - /* WSAETOOMANYREFS is ETOOMANYREFS */ - /* WSAETIMEDOUT is ETIMEDOUT */ - /* WSAECONNREFUSED is ECONNREFUSED */ - /* WSAELOOP is ELOOP */ - /* WSAENAMETOOLONG maps to ENAMETOOLONG */ - /* WSAEHOSTDOWN is EHOSTDOWN */ - /* WSAEHOSTUNREACH is EHOSTUNREACH */ - /* WSAENOTEMPTY maps to ENOTEMPTY */ - /* WSAEPROCLIM is EPROCLIM */ - /* WSAEUSERS is EUSERS */ - /* WSAEDQUOT is EDQUOT */ - /* WSAESTALE is ESTALE */ - /* WSAEREMOTE is EREMOTE */ - case WSASYSNOTREADY: - msg = "Network subsystem is unavailable"; - break; - case WSAVERNOTSUPPORTED: - msg = "Winsock.dll version out of range"; - break; - case WSANOTINITIALISED: - msg = "Successful WSAStartup not yet performed"; - break; - case WSAEDISCON: - msg = "Graceful shutdown in progress"; - break; - case WSAENOMORE: case WSA_E_NO_MORE: - msg = "No more results"; - break; - case WSAECANCELLED: case WSA_E_CANCELLED: - msg = "Call was canceled"; - break; - case WSAEINVALIDPROCTABLE: - msg = "Procedure call table is invalid"; - break; - case WSAEINVALIDPROVIDER: - msg = "Service provider is invalid"; - break; - case WSAEPROVIDERFAILEDINIT: - msg = "Service provider failed to initialize"; - break; - case WSASYSCALLFAILURE: - msg = "System call failure"; - break; - case WSASERVICE_NOT_FOUND: - msg = "Service not found"; - break; - case WSATYPE_NOT_FOUND: - msg = "Class type not found"; - break; - case WSAEREFUSED: - msg = "Database query was refused"; - break; - case WSAHOST_NOT_FOUND: - msg = "Host not found"; - break; - case WSATRY_AGAIN: - msg = "Nonauthoritative host not found"; - break; - case WSANO_RECOVERY: - msg = "Nonrecoverable error"; - break; - case WSANO_DATA: - msg = "Valid name, no data record of requested type"; - break; - /* WSA_QOS_* omitted */ -# endif -# endif - -# if GNULIB_defined_ENOMSG - case ENOMSG: - msg = "No message of desired type"; - break; -# endif - -# if GNULIB_defined_EIDRM - case EIDRM: - msg = "Identifier removed"; - break; -# endif - -# if GNULIB_defined_ENOLINK - case ENOLINK: - msg = "Link has been severed"; - break; -# endif - -# if GNULIB_defined_EPROTO - case EPROTO: - msg = "Protocol error"; - break; -# endif - -# if GNULIB_defined_EMULTIHOP - case EMULTIHOP: - msg = "Multihop attempted"; - break; -# endif - -# if GNULIB_defined_EBADMSG - case EBADMSG: - msg = "Bad message"; - break; -# endif + va_list args; + int result; + + va_start (args, format); + result = _vsnprintf (buf, buflen, format, args); + va_end (args); + if (buflen > 0 && (result < 0 || result >= buflen)) + buf[buflen - 1] = '\0'; + return result; +} +# define snprintf local_snprintf +#endif -# if GNULIB_defined_EOVERFLOW - case EOVERFLOW: - msg = "Value too large for defined data type"; - break; -# endif +/* Copy as much of MSG into BUF as possible, without corrupting errno. + Return 0 if MSG fit in BUFLEN, otherwise return ERANGE. */ +static int +safe_copy (char *buf, size_t buflen, const char *msg) +{ + size_t len = strlen (msg); + int ret; -# if GNULIB_defined_ENOTSUP - case ENOTSUP: - msg = "Not supported"; - break; -# endif + if (len < buflen) + { + /* Although POSIX allows memcpy() to corrupt errno, we don't + know of any implementation where this is a real problem. */ + memcpy (buf, msg, len + 1); + ret = 0; + } + else + { + memcpy (buf, msg, buflen - 1); + buf[buflen - 1] = '\0'; + ret = ERANGE; + } + return ret; +} -# if GNULIB_defined_ESTALE - case ESTALE: - msg = "Stale NFS file handle"; - break; -# endif -# if GNULIB_defined_EDQUOT - case EDQUOT: - msg = "Disk quota exceeded"; - break; -# endif +int +strerror_r (int errnum, char *buf, size_t buflen) +#undef strerror_r +{ + /* Filter this out now, so that rest of this replacement knows that + there is room for a non-empty message and trailing NUL. */ + if (buflen <= 1) + { + if (buflen) + *buf = '\0'; + return ERANGE; + } + *buf = '\0'; -# if GNULIB_defined_ECANCELED - case ECANCELED: - msg = "Operation canceled"; - break; -# endif - } + /* Check for gnulib overrides. */ + { + char const *msg = strerror_override (errnum); if (msg) - { - size_t len = strlen (msg); - - if (len < buflen) - { - memcpy (buf, msg, len + 1); - return 0; - } - else - return ERANGE; - } + return safe_copy (buf, buflen, msg); } -#endif { int ret; + int saved_errno = errno; -#if USE_SYSTEM_STRERROR_R +#if USE_XPG_STRERROR_R + + { + ret = __xpg_strerror_r (errnum, buf, buflen); + if (ret < 0) + ret = errno; + if (!*buf) + { + /* glibc 2.13 would not touch buf on err, so we have to fall + back to GNU strerror_r which always returns a thread-safe + untruncated string to (partially) copy into our buf. */ + safe_copy (buf, buflen, strerror_r (errnum, buf, buflen)); + } + } + +#elif USE_SYSTEM_STRERROR_R if (buflen > INT_MAX) buflen = INT_MAX; # ifdef __hpux - /* On HP-UX 11.31, strerror_r always fails when buflen < 80. */ + /* On HP-UX 11.31, strerror_r always fails when buflen < 80; it + also fails to change buf on EINVAL. */ { char stackbuf[80]; - if (buflen < sizeof (stackbuf)) + if (buflen < sizeof stackbuf) { - ret = strerror_r (errnum, stackbuf, sizeof (stackbuf)); + ret = strerror_r (errnum, stackbuf, sizeof stackbuf); if (ret == 0) - { - size_t len = strlen (stackbuf); - - if (len < buflen) - memcpy (buf, stackbuf, len + 1); - else - ret = ERANGE; - } + ret = safe_copy (buf, buflen, stackbuf); } else ret = strerror_r (errnum, buf, buflen); } -# elif defined __CYGWIN__ - /* Cygwin 1.7.8 only provides the glibc interface, is thread-safe, and - always succeeds (although it may truncate). */ - strerror_r (errnum, buf, buflen); - ret = 0; # else ret = strerror_r (errnum, buf, buflen); -# endif - -# ifdef _AIX - /* On AIX 6.1, strerror_r returns -1 and sets errno to EINVAL - if buflen <= 1. */ - if (ret < 0 && errno == EINVAL && buflen <= 1) - { - /* Retry with a larger buffer. */ - char largerbuf[10]; - ret = strerror_r (errnum, largerbuf, sizeof (largerbuf)); - if (ret < 0 && errno == EINVAL) - { - /* errnum was out of range. */ - ret = EINVAL; - } - else - { - /* buf was too small. */ - ret = ERANGE; - } - } -# endif /* Some old implementations may return (-1, EINVAL) instead of EINVAL. */ if (ret < 0) ret = errno; +# endif - /* FreeBSD rejects 0; see http://austingroupbugs.net/view.php?id=382. */ - if (errnum == 0 && ret == EINVAL) +# ifdef _AIX + /* AIX returns 0 rather than ERANGE when truncating strings; try + again until we are sure we got the entire string. */ + if (!ret && strlen (buf) == buflen - 1) { - if (buflen <= strlen ("Success")) - { - ret = ERANGE; - if (buflen) - buf[0] = 0; - } - else - { - ret = 0; - strcpy (buf, "Success"); - } + char stackbuf[STACKBUF_LEN]; + size_t len; + strerror_r (errnum, stackbuf, sizeof stackbuf); + len = strlen (stackbuf); + /* STACKBUF_LEN should have been large enough. */ + if (len + 1 == sizeof stackbuf) + abort (); + if (buflen <= len) + ret = ERANGE; } +# else + /* Solaris 10 does not populate buf on ERANGE. OpenBSD 4.7 + truncates early on ERANGE rather than return a partial integer. + We prefer the maximal string. We set buf[0] earlier, and we + know of no implementation that modifies buf to be an + unterminated string, so this strlen should be portable in + practice (rather than pulling in a safer strnlen). */ + if (ret == ERANGE && strlen (buf) < buflen - 1) + { + char stackbuf[STACKBUF_LEN]; -#elif USE_XPG_STRERROR_R - - { - extern int __xpg_strerror_r (int errnum, char *buf, size_t buflen); - - ret = __xpg_strerror_r (errnum, buf, buflen); - if (ret < 0) - ret = errno; - } + /* STACKBUF_LEN should have been large enough. */ + if (strerror_r (errnum, stackbuf, sizeof stackbuf) == ERANGE) + abort (); + safe_copy (buf, buflen, stackbuf); + } +# endif #else /* USE_SYSTEM_STRERROR */ /* Try to do what strerror (errnum) does, but without clobbering the buffer used by strerror(). */ -# if defined __NetBSD__ || defined __hpux || ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) /* NetBSD, HP-UX, native Win32 */ +# if defined __NetBSD__ || defined __hpux || ((defined _WIN32 || defined __WIN32__) && !defined __CYGWIN__) || defined __CYGWIN__ /* NetBSD, HP-UX, native Windows, Cygwin */ - /* NetBSD: sys_nerr, sys_errlist are declared through _NETBSD_SOURCE - and above. - HP-UX: sys_nerr, sys_errlist are declared explicitly above. - native Win32: sys_nerr, sys_errlist are declared in . */ + /* NetBSD: sys_nerr, sys_errlist are declared through _NETBSD_SOURCE + and above. + HP-UX: sys_nerr, sys_errlist are declared explicitly above. + native Windows: sys_nerr, sys_errlist are declared in . + Cygwin: sys_nerr, sys_errlist are declared in . */ if (errnum >= 0 && errnum < sys_nerr) { # if HAVE_CATGETS && (defined __NetBSD__ || defined __hpux) - int saved_errno = errno; # if defined __NetBSD__ nl_catd catd = catopen ("libc", NL_CAT_LOCALE); const char *errmsg = @@ -540,21 +270,10 @@ strerror_r (int errnum, char *buf, size_t buflen) if (errmsg == NULL || *errmsg == '\0') ret = EINVAL; else - { - size_t len = strlen (errmsg); - - if (len < buflen) - { - memcpy (buf, errmsg, len + 1); - ret = 0; - } - else - ret = ERANGE; - } + ret = safe_copy (buf, buflen, errmsg); # if HAVE_CATGETS && (defined __NetBSD__ || defined __hpux) if (catd != (nl_catd)-1) catclose (catd); - errno = saved_errno; # endif } else @@ -571,17 +290,7 @@ strerror_r (int errnum, char *buf, size_t buflen) if (errmsg == NULL || *errmsg == '\0') ret = EINVAL; else - { - size_t len = strlen (errmsg); - - if (len < buflen) - { - memcpy (buf, errmsg, len + 1); - ret = 0; - } - else - ret = ERANGE; - } + ret = safe_copy (buf, buflen, errmsg); } else ret = EINVAL; @@ -599,17 +308,7 @@ strerror_r (int errnum, char *buf, size_t buflen) if (errmsg == NULL || *errmsg == '\0') ret = EINVAL; else - { - size_t len = strlen (errmsg); - - if (len < buflen) - { - memcpy (buf, errmsg, len + 1); - ret = 0; - } - else - ret = ERANGE; - } + ret = safe_copy (buf, buflen, errmsg); } gl_lock_unlock (strerror_lock); @@ -618,6 +317,10 @@ strerror_r (int errnum, char *buf, size_t buflen) #endif + if (ret == EINVAL && !*buf) + snprintf (buf, buflen, "Unknown error %d", errnum); + + errno = saved_errno; return ret; } }