Coverage Report

Created: 2025-08-12 06:43

/src/postgres/src/port/strerror.c
Line
Count
Source (jump to first uncovered line)
1
/*-------------------------------------------------------------------------
2
 *
3
 * strerror.c
4
 *    Replacements for standard strerror() and strerror_r() functions
5
 *
6
 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
7
 * Portions Copyright (c) 1994, Regents of the University of California
8
 *
9
 *
10
 * IDENTIFICATION
11
 *    src/port/strerror.c
12
 *
13
 *-------------------------------------------------------------------------
14
 */
15
#include "c.h"
16
17
/*
18
 * Within this file, "strerror" means the platform's function not pg_strerror,
19
 * and likewise for "strerror_r"
20
 */
21
#undef strerror
22
#undef strerror_r
23
24
static char *gnuish_strerror_r(int errnum, char *buf, size_t buflen);
25
static char *get_errno_symbol(int errnum);
26
#ifdef WIN32
27
static char *win32_socket_strerror(int errnum, char *buf, size_t buflen);
28
#endif
29
30
31
/*
32
 * A slightly cleaned-up version of strerror()
33
 */
34
char *
35
pg_strerror(int errnum)
36
0
{
37
0
  static char errorstr_buf[PG_STRERROR_R_BUFLEN];
38
39
0
  return pg_strerror_r(errnum, errorstr_buf, sizeof(errorstr_buf));
40
0
}
41
42
/*
43
 * A slightly cleaned-up version of strerror_r()
44
 */
45
char *
46
pg_strerror_r(int errnum, char *buf, size_t buflen)
47
0
{
48
0
  char     *str;
49
50
  /* If it's a Windows Winsock error, that needs special handling */
51
#ifdef WIN32
52
  /* Winsock error code range, per WinError.h */
53
  if (errnum >= 10000 && errnum <= 11999)
54
    return win32_socket_strerror(errnum, buf, buflen);
55
#endif
56
57
  /* Try the platform's strerror_r(), or maybe just strerror() */
58
0
  str = gnuish_strerror_r(errnum, buf, buflen);
59
60
  /*
61
   * Some strerror()s return an empty string for out-of-range errno.  This
62
   * is ANSI C spec compliant, but not exactly useful.  Also, we may get
63
   * back strings of question marks if libc cannot transcode the message to
64
   * the codeset specified by LC_CTYPE.  If we get nothing useful, first try
65
   * get_errno_symbol(), and if that fails, print the numeric errno.
66
   */
67
0
  if (str == NULL || *str == '\0' || *str == '?')
68
0
    str = get_errno_symbol(errnum);
69
70
0
  if (str == NULL)
71
0
  {
72
0
    snprintf(buf, buflen, _("operating system error %d"), errnum);
73
0
    str = buf;
74
0
  }
75
76
0
  return str;
77
0
}
78
79
/*
80
 * Simple wrapper to emulate GNU strerror_r if what the platform provides is
81
 * POSIX.  Also, if platform lacks strerror_r altogether, fall back to plain
82
 * strerror; it might not be very thread-safe, but tough luck.
83
 */
84
static char *
85
gnuish_strerror_r(int errnum, char *buf, size_t buflen)
86
0
{
87
0
#ifdef HAVE_STRERROR_R
88
#ifdef STRERROR_R_INT
89
  /* POSIX API */
90
  if (strerror_r(errnum, buf, buflen) == 0)
91
    return buf;
92
  return NULL;        /* let caller deal with failure */
93
#else
94
  /* GNU API */
95
0
  return strerror_r(errnum, buf, buflen);
96
0
#endif
97
#else             /* !HAVE_STRERROR_R */
98
  char     *sbuf = strerror(errnum);
99
100
  if (sbuf == NULL)     /* can this still happen anywhere? */
101
    return NULL;
102
  /* To minimize thread-unsafety hazard, copy into caller's buffer */
103
  strlcpy(buf, sbuf, buflen);
104
  return buf;
105
#endif
106
0
}
107
108
/*
109
 * Returns a symbol (e.g. "ENOENT") for an errno code.
110
 * Returns NULL if the code is unrecognized.
111
 */
112
static char *
113
get_errno_symbol(int errnum)
114
0
{
115
0
  switch (errnum)
116
0
  {
117
0
    case E2BIG:
118
0
      return "E2BIG";
119
0
    case EACCES:
120
0
      return "EACCES";
121
0
    case EADDRINUSE:
122
0
      return "EADDRINUSE";
123
0
    case EADDRNOTAVAIL:
124
0
      return "EADDRNOTAVAIL";
125
0
    case EAFNOSUPPORT:
126
0
      return "EAFNOSUPPORT";
127
0
#ifdef EAGAIN
128
0
    case EAGAIN:
129
0
      return "EAGAIN";
130
0
#endif
131
0
#ifdef EALREADY
132
0
    case EALREADY:
133
0
      return "EALREADY";
134
0
#endif
135
0
    case EBADF:
136
0
      return "EBADF";
137
0
#ifdef EBADMSG
138
0
    case EBADMSG:
139
0
      return "EBADMSG";
140
0
#endif
141
0
    case EBUSY:
142
0
      return "EBUSY";
143
0
    case ECHILD:
144
0
      return "ECHILD";
145
0
    case ECONNABORTED:
146
0
      return "ECONNABORTED";
147
0
    case ECONNREFUSED:
148
0
      return "ECONNREFUSED";
149
0
    case ECONNRESET:
150
0
      return "ECONNRESET";
151
0
    case EDEADLK:
152
0
      return "EDEADLK";
153
0
    case EDOM:
154
0
      return "EDOM";
155
0
    case EEXIST:
156
0
      return "EEXIST";
157
0
    case EFAULT:
158
0
      return "EFAULT";
159
0
    case EFBIG:
160
0
      return "EFBIG";
161
0
    case EHOSTDOWN:
162
0
      return "EHOSTDOWN";
163
0
    case EHOSTUNREACH:
164
0
      return "EHOSTUNREACH";
165
0
    case EIDRM:
166
0
      return "EIDRM";
167
0
    case EINPROGRESS:
168
0
      return "EINPROGRESS";
169
0
    case EINTR:
170
0
      return "EINTR";
171
0
    case EINVAL:
172
0
      return "EINVAL";
173
0
    case EIO:
174
0
      return "EIO";
175
0
    case EISCONN:
176
0
      return "EISCONN";
177
0
    case EISDIR:
178
0
      return "EISDIR";
179
0
#ifdef ELOOP
180
0
    case ELOOP:
181
0
      return "ELOOP";
182
0
#endif
183
0
    case EMFILE:
184
0
      return "EMFILE";
185
0
    case EMLINK:
186
0
      return "EMLINK";
187
0
    case EMSGSIZE:
188
0
      return "EMSGSIZE";
189
0
    case ENAMETOOLONG:
190
0
      return "ENAMETOOLONG";
191
0
    case ENETDOWN:
192
0
      return "ENETDOWN";
193
0
    case ENETRESET:
194
0
      return "ENETRESET";
195
0
    case ENETUNREACH:
196
0
      return "ENETUNREACH";
197
0
    case ENFILE:
198
0
      return "ENFILE";
199
0
    case ENOBUFS:
200
0
      return "ENOBUFS";
201
0
    case ENODEV:
202
0
      return "ENODEV";
203
0
    case ENOENT:
204
0
      return "ENOENT";
205
0
    case ENOEXEC:
206
0
      return "ENOEXEC";
207
0
    case ENOMEM:
208
0
      return "ENOMEM";
209
0
    case ENOSPC:
210
0
      return "ENOSPC";
211
0
    case ENOSYS:
212
0
      return "ENOSYS";
213
0
    case ENOTCONN:
214
0
      return "ENOTCONN";
215
0
    case ENOTDIR:
216
0
      return "ENOTDIR";
217
0
    case ENOTEMPTY:
218
0
      return "ENOTEMPTY";
219
0
    case ENOTSOCK:
220
0
      return "ENOTSOCK";
221
0
#ifdef ENOTSUP
222
0
    case ENOTSUP:
223
0
      return "ENOTSUP";
224
0
#endif
225
0
    case ENOTTY:
226
0
      return "ENOTTY";
227
0
    case ENXIO:
228
0
      return "ENXIO";
229
#if defined(EOPNOTSUPP) && (!defined(ENOTSUP) || (EOPNOTSUPP != ENOTSUP))
230
    case EOPNOTSUPP:
231
      return "EOPNOTSUPP";
232
#endif
233
0
#ifdef EOVERFLOW
234
0
    case EOVERFLOW:
235
0
      return "EOVERFLOW";
236
0
#endif
237
0
    case EPERM:
238
0
      return "EPERM";
239
0
    case EPIPE:
240
0
      return "EPIPE";
241
0
    case EPROTONOSUPPORT:
242
0
      return "EPROTONOSUPPORT";
243
0
    case ERANGE:
244
0
      return "ERANGE";
245
0
#ifdef EROFS
246
0
    case EROFS:
247
0
      return "EROFS";
248
0
#endif
249
0
    case ESRCH:
250
0
      return "ESRCH";
251
0
    case ETIMEDOUT:
252
0
      return "ETIMEDOUT";
253
0
#ifdef ETXTBSY
254
0
    case ETXTBSY:
255
0
      return "ETXTBSY";
256
0
#endif
257
#if defined(EWOULDBLOCK) && (!defined(EAGAIN) || (EWOULDBLOCK != EAGAIN))
258
    case EWOULDBLOCK:
259
      return "EWOULDBLOCK";
260
#endif
261
0
    case EXDEV:
262
0
      return "EXDEV";
263
0
  }
264
265
0
  return NULL;
266
0
}
267
268
269
#ifdef WIN32
270
271
/*
272
 * Windows' strerror() doesn't know the Winsock codes, so handle them this way
273
 */
274
static char *
275
win32_socket_strerror(int errnum, char *buf, size_t buflen)
276
{
277
  static HANDLE handleDLL = INVALID_HANDLE_VALUE;
278
279
  if (handleDLL == INVALID_HANDLE_VALUE)
280
  {
281
    handleDLL = LoadLibraryEx("netmsg.dll", NULL,
282
                  DONT_RESOLVE_DLL_REFERENCES | LOAD_LIBRARY_AS_DATAFILE);
283
    if (handleDLL == NULL)
284
    {
285
      snprintf(buf, buflen,
286
           "winsock error %d (could not load netmsg.dll to translate: error code %lu)",
287
           errnum, GetLastError());
288
      return buf;
289
    }
290
  }
291
292
  ZeroMemory(buf, buflen);
293
  if (FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS |
294
            FORMAT_MESSAGE_FROM_SYSTEM |
295
            FORMAT_MESSAGE_FROM_HMODULE,
296
            handleDLL,
297
            errnum,
298
            MAKELANGID(LANG_ENGLISH, SUBLANG_DEFAULT),
299
            buf,
300
            buflen - 1,
301
            NULL) == 0)
302
  {
303
    /* Failed to get id */
304
    snprintf(buf, buflen, "unrecognized winsock error %d", errnum);
305
  }
306
307
  return buf;
308
}
309
310
#endif              /* WIN32 */