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