tests: add signature checks
[pspp] / tests / test-fcntl.c
1 /* Test of fcntl(2).
2    Copyright (C) 2009 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
16
17 /* Written by Eric Blake <ebb9@byu.net>, 2009.  */
18
19 #include <config.h>
20
21 /* Specification.  */
22 #include <fcntl.h>
23
24 #include "signature.h"
25 SIGNATURE_CHECK (fcntl, int, (int, int, ...));
26
27 /* Helpers.  */
28 #include <errno.h>
29 #include <stdarg.h>
30 #include <stdbool.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34
35 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
36 /* Get declarations of the Win32 API functions.  */
37 # define WIN32_LEAN_AND_MEAN
38 # include <windows.h>
39 #endif
40
41 #include "binary-io.h"
42
43 /* Use O_CLOEXEC if available, but test works without it.  */
44 #ifndef O_CLOEXEC
45 # define O_CLOEXEC 0
46 #endif
47
48 #if !O_BINARY
49 # define setmode(f,m) zero ()
50 static int zero (void) { return 0; }
51 #endif
52
53 #define ASSERT(expr) \
54   do                                                                         \
55     {                                                                        \
56       if (!(expr))                                                           \
57         {                                                                    \
58           fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \
59           fflush (stderr);                                                   \
60           abort ();                                                          \
61         }                                                                    \
62     }                                                                        \
63   while (0)
64
65 /* Return true if FD is open.  */
66 static bool
67 is_open (int fd)
68 {
69 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
70   /* On Win32, the initial state of unassigned standard file
71      descriptors is that they are open but point to an
72      INVALID_HANDLE_VALUE, and there is no fcntl.  */
73   return (HANDLE) _get_osfhandle (fd) != INVALID_HANDLE_VALUE;
74 #else
75 # ifndef F_GETFL
76 #  error Please port fcntl to your platform
77 # endif
78   return 0 <= fcntl (fd, F_GETFL);
79 #endif
80 }
81
82 /* Return true if FD is open and inheritable across exec/spawn.  */
83 static bool
84 is_inheritable (int fd)
85 {
86 #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
87   /* On Win32, the initial state of unassigned standard file
88      descriptors is that they are open but point to an
89      INVALID_HANDLE_VALUE, and there is no fcntl.  */
90   HANDLE h = (HANDLE) _get_osfhandle (fd);
91   DWORD flags;
92   if (h == INVALID_HANDLE_VALUE || GetHandleInformation (h, &flags) == 0)
93     return false;
94   return (flags & HANDLE_FLAG_INHERIT) != 0;
95 #else
96 # ifndef F_GETFD
97 #  error Please port fcntl to your platform
98 # endif
99   int i = fcntl (fd, F_GETFD);
100   return 0 <= i && (i & FD_CLOEXEC) == 0;
101 #endif
102 }
103
104 /* Return non-zero if FD is open in the given MODE, which is either
105    O_TEXT or O_BINARY.  */
106 static bool
107 is_mode (int fd, int mode)
108 {
109   int value = setmode (fd, O_BINARY);
110   setmode (fd, value);
111   return mode == value;
112 }
113
114 /* Since native fcntl can have more supported operations than our
115    replacement is aware of, and since various operations assign
116    different types to the vararg argument, a wrapper around fcntl must
117    be able to pass a vararg of unknown type on through to the original
118    fcntl.  Make sure that this works properly: func1 behaves like the
119    original fcntl interpreting the vararg as an int or a pointer to a
120    struct, and func2 behaves like rpl_fcntl that doesn't know what
121    type to forward.  */
122 struct dummy_struct
123 {
124   long filler;
125   int value;
126 };
127 static int
128 func1 (int a, ...)
129 {
130   va_list arg;
131   int i;
132   va_start (arg, a);
133   if (a < 4)
134     i = va_arg (arg, int);
135   else
136     {
137       struct dummy_struct *s = va_arg (arg, struct dummy_struct *);
138       i = s->value;
139     }
140   va_end (arg);
141   return i;
142 }
143 static int
144 func2 (int a, ...)
145 {
146   va_list arg;
147   void *p;
148   va_start (arg, a);
149   p = va_arg (arg, void *);
150   va_end (arg);
151   return func1 (a, p);
152 }
153
154 /* Ensure that all supported fcntl actions are distinct, and
155    usable in preprocessor expressions.  */
156 static void
157 check_flags (void)
158 {
159   switch (0)
160     {
161     case F_DUPFD:
162 #if F_DUPFD
163 #endif
164
165     case F_DUPFD_CLOEXEC:
166 #if F_DUPFD_CLOEXEC
167 #endif
168
169     case F_GETFD:
170 #if F_GETFD
171 #endif
172
173 #ifdef F_SETFD
174     case F_SETFD:
175 # if F_SETFD
176 # endif
177 #endif
178
179 #ifdef F_GETFL
180     case F_GETFL:
181 # if F_GETFL
182 # endif
183 #endif
184
185 #ifdef F_SETFL
186     case F_SETFL:
187 # if F_SETFL
188 # endif
189 #endif
190
191 #ifdef F_GETOWN
192     case F_GETOWN:
193 # if F_GETOWN
194 # endif
195 #endif
196
197 #ifdef F_SETOWN
198     case F_SETOWN:
199 # if F_SETOWN
200 # endif
201 #endif
202
203 #ifdef F_GETLK
204     case F_GETLK:
205 # if F_GETLK
206 # endif
207 #endif
208
209 #ifdef F_SETLK
210     case F_SETLK:
211 # if F_SETLK
212 # endif
213 #endif
214
215 #ifdef F_SETLKW
216     case F_SETLKW:
217 # if F_SETLKW
218 # endif
219 #endif
220
221       ;
222     }
223 }
224
225 int
226 main (void)
227 {
228   const char *file = "test-fcntl.tmp";
229   int fd;
230
231   /* Sanity check that rpl_fcntl is likely to work.  */
232   ASSERT (func2 (1, 2) == 2);
233   ASSERT (func2 (2, -2) == -2);
234   ASSERT (func2 (3, 0x80000000) == 0x80000000);
235   {
236     struct dummy_struct s = { 0L, 4 };
237     ASSERT (func2 (4, &s) == 4);
238   }
239   check_flags ();
240
241   /* Assume std descriptors were provided by invoker, and ignore fds
242      that might have been inherited.  */
243   fd = creat (file, 0600);
244   ASSERT (STDERR_FILENO < fd);
245   close (fd + 1);
246   close (fd + 2);
247
248   /* For F_DUPFD*, the source must be valid.  */
249   errno = 0;
250   ASSERT (fcntl (-1, F_DUPFD, 0) == -1);
251   ASSERT (errno == EBADF);
252   errno = 0;
253   ASSERT (fcntl (fd + 1, F_DUPFD, 0) == -1);
254   ASSERT (errno == EBADF);
255   errno = 0;
256   ASSERT (fcntl (10000000, F_DUPFD, 0) == -1);
257   ASSERT (errno == EBADF);
258   errno = 0;
259   ASSERT (fcntl (-1, F_DUPFD_CLOEXEC, 0) == -1);
260   ASSERT (errno == EBADF);
261   errno = 0;
262   ASSERT (fcntl (fd + 1, F_DUPFD_CLOEXEC, 0) == -1);
263   ASSERT (errno == EBADF);
264   errno = 0;
265   ASSERT (fcntl (10000000, F_DUPFD_CLOEXEC, 0) == -1);
266   ASSERT (errno == EBADF);
267
268   /* For F_DUPFD*, the destination must be valid.  */
269   ASSERT (getdtablesize () < 10000000);
270   errno = 0;
271   ASSERT (fcntl (fd, F_DUPFD, -1) == -1);
272   ASSERT (errno == EINVAL);
273   errno = 0;
274   ASSERT (fcntl (fd, F_DUPFD, 10000000) == -1);
275   ASSERT (errno == EINVAL);
276   ASSERT (getdtablesize () < 10000000);
277   errno = 0;
278   ASSERT (fcntl (fd, F_DUPFD_CLOEXEC, -1) == -1);
279   ASSERT (errno == EINVAL);
280   errno = 0;
281   ASSERT (fcntl (fd, F_DUPFD_CLOEXEC, 10000000) == -1);
282   ASSERT (errno == EINVAL);
283
284   /* For F_DUPFD*, check for correct inheritance, as well as
285      preservation of text vs. binary.  */
286   setmode (fd, O_BINARY);
287   ASSERT (is_open (fd));
288   ASSERT (!is_open (fd + 1));
289   ASSERT (!is_open (fd + 2));
290   ASSERT (is_inheritable (fd));
291   ASSERT (is_mode (fd, O_BINARY));
292
293   ASSERT (fcntl (fd, F_DUPFD, fd) == fd + 1);
294   ASSERT (is_open (fd));
295   ASSERT (is_open (fd + 1));
296   ASSERT (!is_open (fd + 2));
297   ASSERT (is_inheritable (fd + 1));
298   ASSERT (is_mode (fd, O_BINARY));
299   ASSERT (is_mode (fd + 1, O_BINARY));
300   ASSERT (close (fd + 1) == 0);
301
302   ASSERT (fcntl (fd, F_DUPFD_CLOEXEC, fd + 2) == fd + 2);
303   ASSERT (is_open (fd));
304   ASSERT (!is_open (fd + 1));
305   ASSERT (is_open (fd + 2));
306   ASSERT (is_inheritable (fd));
307   ASSERT (!is_inheritable (fd + 2));
308   ASSERT (is_mode (fd, O_BINARY));
309   ASSERT (is_mode (fd + 2, O_BINARY));
310   ASSERT (close (fd) == 0);
311
312   setmode (fd + 2, O_TEXT);
313   ASSERT (fcntl (fd + 2, F_DUPFD, fd + 1) == fd + 1);
314   ASSERT (!is_open (fd));
315   ASSERT (is_open (fd + 1));
316   ASSERT (is_open (fd + 2));
317   ASSERT (is_inheritable (fd + 1));
318   ASSERT (!is_inheritable (fd + 2));
319   ASSERT (is_mode (fd + 1, O_TEXT));
320   ASSERT (is_mode (fd + 2, O_TEXT));
321   ASSERT (close (fd + 1) == 0);
322
323   ASSERT (fcntl (fd + 2, F_DUPFD_CLOEXEC, 0) == fd);
324   ASSERT (is_open (fd));
325   ASSERT (!is_open (fd + 1));
326   ASSERT (is_open (fd + 2));
327   ASSERT (!is_inheritable (fd));
328   ASSERT (!is_inheritable (fd + 2));
329   ASSERT (is_mode (fd, O_TEXT));
330   ASSERT (is_mode (fd + 2, O_TEXT));
331   ASSERT (close (fd + 2) == 0);
332
333   /* Test F_GETFD.  */
334   errno = 0;
335   ASSERT (fcntl (-1, F_GETFD) == -1);
336   ASSERT (errno == EBADF);
337   errno = 0;
338   ASSERT (fcntl (fd + 1, F_GETFD) == -1);
339   ASSERT (errno == EBADF);
340   errno = 0;
341   ASSERT (fcntl (10000000, F_GETFD) == -1);
342   ASSERT (errno == EBADF);
343   {
344     int result = fcntl (fd, F_GETFD);
345     ASSERT (0 <= result);
346     ASSERT ((result & FD_CLOEXEC) == FD_CLOEXEC);
347     ASSERT (dup (fd) == fd + 1);
348     result = fcntl (fd + 1, F_GETFD);
349     ASSERT (0 <= result);
350     ASSERT ((result & FD_CLOEXEC) == 0);
351     ASSERT (close (fd + 1) == 0);
352   }
353
354   /* Cleanup.  */
355   ASSERT (close (fd) == 0);
356   ASSERT (unlink (file) == 0);
357
358   return 0;
359 }