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