Welcome to mirror list, hosted at ThFree Co, Russian Federation.

errno.c « support - github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 76b4b388e9862170e2647ed127d1bf71017f1fbd (plain)
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/*
 * <errno.h> wrapper functions.
 */

#include <errno.h>
#include <string.h>
#include "map.h"
#include "mph.h"
#include <stdio.h>

G_BEGIN_DECLS

int
Mono_Posix_Stdlib_GetLastError (void)
{
	return errno;
}

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 */
#if HOST_ANDROID
	/* Android NDK defines _GNU_SOURCE but strerror_r follows the XSI semantics
	 * not the GNU one. XSI version returns an integer, as opposed to the GNU one
	 * which returns pointer to the buffer.
	 */
	if (strerror_r (errnum, ebuf, sizeof(ebuf)) == -1) {
		/* XSI strerror_r will return -1 if errno is set, but if we leave the value
		 * alone it breaks Mono.Posix StdioFileStream tests, so we'll ignore the value
		 * and set errno as below
		 */
		errno = EINVAL;
		return -1;
	}
	r = ebuf;
#else
	r = strerror_r (errnum, ebuf, sizeof(ebuf));
#endif
	if (!r) {
		errno = EINVAL;
		return -1;
	} 
	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
 */