Skip to content

Commit 99a1141

Browse files
rimruldscho
authored andcommitted
compat/mingw: handle WSA errors in strerror
We map WSAGetLastError() errors to errno errors in winsock_error_to_errno(), but the MSVC strerror() implementation only produces "Unknown error" for most of them. Produce some more meaningful error messages in these cases. Our builds for ARM64 link against the newer UCRT strerror() that does know these errors, so we won't change the strerror() used there. The wording of the messages is copied from glibc strerror() messages. Reported-by: M Hickford <[email protected]> Signed-off-by: Matthias Aßhauer <[email protected]> Signed-off-by: Johannes Schindelin <[email protected]>
1 parent 44d1375 commit 99a1141

File tree

5 files changed

+164
-0
lines changed

5 files changed

+164
-0
lines changed

Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1357,6 +1357,7 @@ THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/%
13571357
THIRD_PARTY_SOURCES += $(UNIT_TEST_DIR)/clar/clar/%
13581358

13591359
CLAR_TEST_SUITES += ctype
1360+
CLAR_TEST_SUITES += mingw
13601361
CLAR_TEST_SUITES += strvec
13611362
CLAR_TEST_PROG = $(UNIT_TEST_BIN)/unit-tests$(X)
13621363
CLAR_TEST_OBJS = $(patsubst %,$(UNIT_TEST_DIR)/%.o,$(CLAR_TEST_SUITES))

compat/mingw.c

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2672,6 +2672,91 @@ static inline int winsock_return(int ret)
26722672

26732673
#define WINSOCK_RETURN(x) do { return winsock_return(x); } while (0)
26742674

2675+
#undef strerror
2676+
char *mingw_strerror(int errnum)
2677+
{
2678+
static char buf[41] ="";
2679+
switch (errnum) {
2680+
case EWOULDBLOCK:
2681+
xsnprintf(buf, 41, "%s", "Operation would block");
2682+
break;
2683+
case EINPROGRESS:
2684+
xsnprintf(buf, 41, "%s", "Operation now in progress");
2685+
break;
2686+
case EALREADY:
2687+
xsnprintf(buf, 41, "%s", "Operation already in progress");
2688+
break;
2689+
case ENOTSOCK:
2690+
xsnprintf(buf, 41, "%s", "Socket operation on non-socket");
2691+
break;
2692+
case EDESTADDRREQ:
2693+
xsnprintf(buf, 41, "%s", "Destination address required");
2694+
break;
2695+
case EMSGSIZE:
2696+
xsnprintf(buf, 41, "%s", "Message too long");
2697+
break;
2698+
case EPROTOTYPE:
2699+
xsnprintf(buf, 41, "%s", "Protocol wrong type for socket");
2700+
break;
2701+
case ENOPROTOOPT:
2702+
xsnprintf(buf, 41, "%s", "Protocol not available");
2703+
break;
2704+
case EPROTONOSUPPORT:
2705+
xsnprintf(buf, 41, "%s", "Protocol not supported");
2706+
break;
2707+
case EOPNOTSUPP:
2708+
xsnprintf(buf, 41, "%s", "Operation not supported");
2709+
break;
2710+
case EAFNOSUPPORT:
2711+
xsnprintf(buf, 41, "%s", "Address family not supported by protocol");
2712+
break;
2713+
case EADDRINUSE:
2714+
xsnprintf(buf, 41, "%s", "Address already in use");
2715+
break;
2716+
case EADDRNOTAVAIL:
2717+
xsnprintf(buf, 41, "%s", "Cannot assign requested address");
2718+
break;
2719+
case ENETDOWN:
2720+
xsnprintf(buf, 41, "%s", "Network is down");
2721+
break;
2722+
case ENETUNREACH:
2723+
xsnprintf(buf, 41, "%s", "Network is unreachable");
2724+
break;
2725+
case ENETRESET:
2726+
xsnprintf(buf, 41, "%s", "Network dropped connection on reset");
2727+
break;
2728+
case ECONNABORTED:
2729+
xsnprintf(buf, 41, "%s", "Software caused connection abort");
2730+
break;
2731+
case ECONNRESET:
2732+
xsnprintf(buf, 41, "%s", "Connection reset by peer");
2733+
break;
2734+
case ENOBUFS:
2735+
xsnprintf(buf, 41, "%s", "No buffer space available");
2736+
break;
2737+
case EISCONN:
2738+
xsnprintf(buf, 41, "%s", "Transport endpoint is already connected");
2739+
break;
2740+
case ENOTCONN:
2741+
xsnprintf(buf, 41, "%s", "Transport endpoint is not connected");
2742+
break;
2743+
case ETIMEDOUT:
2744+
xsnprintf(buf, 41, "%s", "Connection timed out");
2745+
break;
2746+
case ECONNREFUSED:
2747+
xsnprintf(buf, 41, "%s", "Connection refused");
2748+
break;
2749+
case ELOOP:
2750+
xsnprintf(buf, 41, "%s", "Too many levels of symbolic links");
2751+
break;
2752+
case EHOSTUNREACH:
2753+
xsnprintf(buf, 41, "%s", "No route to host");
2754+
break;
2755+
default: return strerror(errnum);
2756+
}
2757+
return buf;
2758+
}
2759+
26752760
#undef gethostname
26762761
int mingw_gethostname(char *name, int namelen)
26772762
{

compat/mingw.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,11 @@ int mingw_socket(int domain, int type, int protocol);
314314
int mingw_connect(int sockfd, struct sockaddr *sa, size_t sz);
315315
#define connect mingw_connect
316316

317+
char *mingw_strerror(int errnum);
318+
#ifndef _UCRT
319+
#define strerror mingw_strerror
320+
#endif
321+
317322
int mingw_bind(int sockfd, struct sockaddr *sa, size_t sz);
318323
#define bind mingw_bind
319324

t/meson.build

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
clar_test_suites = [
22
'unit-tests/ctype.c',
3+
'unit-tests/mingw.c',
34
'unit-tests/strvec.c',
45
]
56

t/unit-tests/mingw.c

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
#include "unit-test.h"
2+
3+
#if defined(GIT_WINDOWS_NATIVE) && !defined(_UCRT)
4+
#undef strerror
5+
int errnos_contains(int);
6+
static int errnos [53]={
7+
/* errnos in err_win_to_posix */
8+
EACCES, EBUSY, EEXIST, ERANGE, EIO, ENODEV, ENXIO, ENOEXEC, EINVAL, ENOENT,
9+
EPIPE, ENAMETOOLONG, ENOSYS, ENOTEMPTY, ENOSPC, EFAULT, EBADF, EPERM, EINTR,
10+
E2BIG, ESPIPE, ENOMEM, EXDEV, EAGAIN, ENFILE, EMFILE, ECHILD, EROFS,
11+
/* errnos only in winsock_error_to_errno */
12+
EWOULDBLOCK, EINPROGRESS, EALREADY, ENOTSOCK, EDESTADDRREQ, EMSGSIZE,
13+
EPROTOTYPE, ENOPROTOOPT, EPROTONOSUPPORT, EOPNOTSUPP, EAFNOSUPPORT,
14+
EADDRINUSE, EADDRNOTAVAIL, ENETDOWN, ENETUNREACH, ENETRESET, ECONNABORTED,
15+
ECONNRESET, ENOBUFS, EISCONN, ENOTCONN, ETIMEDOUT, ECONNREFUSED, ELOOP,
16+
EHOSTUNREACH
17+
};
18+
19+
int errnos_contains(int errnum)
20+
{
21+
for(int i=0;i<53;i++)
22+
if(errnos[i]==errnum)
23+
return 1;
24+
return 0;
25+
}
26+
#endif
27+
28+
void test_mingw__no_strerror_shim_on_ucrt(void)
29+
{
30+
#if defined(GIT_WINDOWS_NATIVE) && defined(_UCRT)
31+
cl_assert_(strerror != mingw_strerror,
32+
"mingw_strerror is unnescessary when building against UCRT");
33+
#else
34+
cl_skip();
35+
#endif
36+
}
37+
38+
void test_mingw__strerror(void)
39+
{
40+
#if defined(GIT_WINDOWS_NATIVE) && !defined(_UCRT)
41+
for(int i=0;i<53;i++)
42+
{
43+
char *crt;
44+
char *mingw;
45+
mingw = mingw_strerror(errnos[i]);
46+
crt = strerror(errnos[i]);
47+
cl_assert_(!strcasestr(mingw, "unknown error"),
48+
"mingw_strerror should know all errno values we care about");
49+
if(!strcasestr(crt, "unknown error"))
50+
cl_assert_equal_s(crt,mingw);
51+
}
52+
#else
53+
cl_skip();
54+
#endif
55+
}
56+
57+
void test_mingw__errno_translation(void)
58+
{
59+
#if defined(GIT_WINDOWS_NATIVE) && !defined(_UCRT)
60+
/* GetLastError() return values are currently defined from 0 to 15841,
61+
testing up to 20000 covers some room for future expansion */
62+
for (int i=0;i<20000;i++)
63+
{
64+
if(i!=ERROR_SUCCESS)
65+
cl_assert_(errnos_contains(err_win_to_posix(i)),
66+
"all err_win_to_posix return values should be tested against mingw_strerror");
67+
/* ideally we'd test the same for winsock_error_to_errno, but it's static */
68+
}
69+
#else
70+
cl_skip();
71+
#endif
72+
}

0 commit comments

Comments
 (0)