1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
|
/*
* <errno.h> wrapper functions.
*/
#include <errno.h>
#include <string.h>
#include "mph.h"
#include <stdio.h>
G_BEGIN_DECLS
void
Mono_Posix_Stdlib_SetLastError (int error_number)
{
errno = error_number;
}
#ifdef HAVE_STRERROR_R
/*
* There are two versions of strerror_r:
* - the GNU version: char *strerror_r (int errnum, char *buf, size_t n);
* - the XPG version: int strerror_r (int errnum, char *buf, size_t n);
*
* Ideally I could stick with the XPG version, but we need to support
* Red Hat 9, which only supports the GNU version.
*
* Furthermore, I do NOT want to export the GNU version in Mono.Posix.dll,
* as that's supposed to contain *standard* function definitions (give or
* take a few GNU extensions). Portability trumps all.
*
* Consequently, we export the functionality of the XPG version.
* Internally, we se the GNU version if _GNU_SOURCE is defined, otherwise
* we assume that the XPG version is present.
*/
#ifdef _GNU_SOURCE
#define mph_min(x,y) ((x) <= (y) ? (x) : (y))
/* If you pass an invalid errno value to glibc 2.3.2's strerror_r, you get
* back the string "Unknown error" with the error value appended. */
static const char mph_unknown[] = "Unknown error ";
/*
* Translate the GNU semantics to the XPG semantics.
*
* From reading the (RH9-using) GLibc 2.3.2 sysdeps/generic/_strerror.c,
* we can say the following:
* - If errnum is a valid error number, a pointer to a constant string is
* returned. Thus, the prototype *lies* (it's not really a char*).
* `buf' is unchanged (WTF?).
* - If errnum is an *invalid* error number, an error message is copied
* into `buf' and `buf' is returned. The error message returned is
* "Unknown error %i", where %i is the input errnum.
*
* Meanwhile, XPG always modifies `buf' if there's enough space, and either
* returns 0 (success) or -1 (error) with errno = EINVAL (bad errnum) or
* ERANGE (`buf' isn't big enough). Also, GLibc 2.3.3 (which has the XPG
* version) first checks the validity of errnum first, then does the copy.
*
* Assuming that the GNU implementation doesn't change much (ha!), we can
* check for EINVAL by comparing the strerror_r return to `buf', OR by
* comparing the return value to "Uknown error". (This assumes that
* strerror_r will always only return the input buffer for errors.)
*
* Check for ERANGE by comparing the string length returned by strerror_r to
* `n'.
*
* Then pray that this actually works...
*/
gint32
Mono_Posix_Syscall_strerror_r (int errnum, char *buf, mph_size_t n)
{
char *r;
char ebuf [sizeof(mph_unknown)];
size_t len;
size_t blen;
mph_return_if_size_t_overflow (n);
/* first, check for valid errnum */
r = strerror_r (errnum, ebuf, sizeof(ebuf));
len = strlen (r);
if (r == ebuf ||
strncmp (r, mph_unknown, mph_min (len, sizeof(mph_unknown))) == 0) {
errno = EINVAL;
return -1;
}
/* valid errnum (we hope); is buffer big enough? */
blen = (size_t) n;
if ((len+1) > blen) {
errno = ERANGE;
return -1;
}
strncpy (buf, r, len);
buf[len] = '\0';
return 0;
}
#else /* !def _GNU_SOURCE */
gint32
Mono_Posix_Syscall_strerror_r (int errnum, char *buf, mph_size_t n)
{
mph_return_if_size_t_overflow (n);
return strerror_r (errnum, buf, (size_t) n);
}
#endif /* def _GNU_SOURCE */
#endif /* def HAVE_STRERROR_R */
G_END_DECLS
/*
* vim: noexpandtab
*/
|