Line data Source code
1 : /* Test duplicating file descriptors.
2 : Copyright (C) 2009-2020 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 <https://www.gnu.org/licenses/>. */
16 :
17 : /* Written by Eric Blake <ebb9@byu.net>, 2009. */
18 :
19 : #include <config.h>
20 :
21 : #include <unistd.h>
22 :
23 : #include "signature.h"
24 : SIGNATURE_CHECK (dup2, int, (int, int));
25 :
26 : #include <errno.h>
27 : #include <fcntl.h>
28 :
29 : #if HAVE_SYS_RESOURCE_H
30 : # include <sys/resource.h>
31 : #endif
32 :
33 : #include "binary-io.h"
34 :
35 : #if GNULIB_TEST_CLOEXEC
36 : # include "cloexec.h"
37 : #endif
38 :
39 : #if defined _WIN32 && ! defined __CYGWIN__
40 : /* Get declarations of the native Windows API functions. */
41 : # define WIN32_LEAN_AND_MEAN
42 : # include <windows.h>
43 : /* Get _get_osfhandle. */
44 : # if GNULIB_MSVC_NOTHROW
45 : # include "msvc-nothrow.h"
46 : # else
47 : # include <io.h>
48 : # endif
49 : #endif
50 :
51 : #include "macros.h"
52 :
53 : /* Return non-zero if FD is open. */
54 : static int
55 10 : is_open (int fd)
56 : {
57 : #if defined _WIN32 && ! defined __CYGWIN__
58 : /* On native Windows, the initial state of unassigned standard file
59 : descriptors is that they are open but point to an
60 : INVALID_HANDLE_VALUE, and there is no fcntl. */
61 : return (HANDLE) _get_osfhandle (fd) != INVALID_HANDLE_VALUE;
62 : #else
63 : # ifndef F_GETFL
64 : # error Please port fcntl to your platform
65 : # endif
66 10 : return 0 <= fcntl (fd, F_GETFL);
67 : #endif
68 : }
69 :
70 : #if GNULIB_TEST_CLOEXEC
71 : /* Return non-zero if FD is open and inheritable across exec/spawn. */
72 : static int
73 5 : is_inheritable (int fd)
74 : {
75 : # if defined _WIN32 && ! defined __CYGWIN__
76 : /* On native Windows, the initial state of unassigned standard file
77 : descriptors is that they are open but point to an
78 : INVALID_HANDLE_VALUE, and there is no fcntl. */
79 : HANDLE h = (HANDLE) _get_osfhandle (fd);
80 : DWORD flags;
81 : if (h == INVALID_HANDLE_VALUE || GetHandleInformation (h, &flags) == 0)
82 : return 0;
83 : return (flags & HANDLE_FLAG_INHERIT) != 0;
84 : # else
85 : # ifndef F_GETFD
86 : # error Please port fcntl to your platform
87 : # endif
88 5 : int i = fcntl (fd, F_GETFD);
89 5 : return 0 <= i && (i & FD_CLOEXEC) == 0;
90 : # endif
91 : }
92 : #endif /* GNULIB_TEST_CLOEXEC */
93 :
94 : #if !O_BINARY
95 : # define setmode(f,m) zero ()
96 10 : static int zero (void) { return 0; }
97 : #endif
98 :
99 : /* Return non-zero if FD is open in the given MODE, which is either
100 : O_TEXT or O_BINARY. */
101 : static int
102 4 : is_mode (int fd, int mode)
103 : {
104 4 : int value = setmode (fd, O_BINARY);
105 4 : setmode (fd, value);
106 4 : return mode == value;
107 : }
108 :
109 : int
110 1 : main (void)
111 : {
112 1 : const char *file = "test-dup2.tmp";
113 : char buffer[1];
114 1 : int bad_fd = getdtablesize ();
115 1 : int fd = open (file, O_CREAT | O_TRUNC | O_RDWR, 0600);
116 :
117 : /* Assume std descriptors were provided by invoker. */
118 1 : ASSERT (STDERR_FILENO < fd);
119 1 : ASSERT (is_open (fd));
120 : /* Ignore any other fd's leaked into this process. */
121 1 : close (fd + 1);
122 1 : close (fd + 2);
123 1 : ASSERT (!is_open (fd + 1));
124 1 : ASSERT (!is_open (fd + 2));
125 :
126 : /* Assigning to self must be a no-op. */
127 1 : ASSERT (dup2 (fd, fd) == fd);
128 1 : ASSERT (is_open (fd));
129 :
130 : /* The source must be valid. */
131 1 : errno = 0;
132 1 : ASSERT (dup2 (-1, fd) == -1);
133 1 : ASSERT (errno == EBADF);
134 1 : close (99);
135 1 : errno = 0;
136 1 : ASSERT (dup2 (99, fd) == -1);
137 1 : ASSERT (errno == EBADF);
138 1 : errno = 0;
139 1 : ASSERT (dup2 (AT_FDCWD, fd) == -1);
140 1 : ASSERT (errno == EBADF);
141 1 : ASSERT (is_open (fd));
142 :
143 : /* If the source is not open, then the destination is unaffected. */
144 1 : errno = 0;
145 1 : ASSERT (dup2 (fd + 1, fd + 1) == -1);
146 1 : ASSERT (errno == EBADF);
147 1 : ASSERT (!is_open (fd + 1));
148 1 : errno = 0;
149 1 : ASSERT (dup2 (fd + 1, fd) == -1);
150 1 : ASSERT (errno == EBADF);
151 1 : ASSERT (is_open (fd));
152 :
153 : /* The destination must be valid. */
154 1 : errno = 0;
155 1 : ASSERT (dup2 (fd, -2) == -1);
156 1 : ASSERT (errno == EBADF);
157 1 : if (bad_fd > 256)
158 : {
159 1 : ASSERT (dup2 (fd, 255) == 255);
160 1 : ASSERT (dup2 (fd, 256) == 256);
161 1 : ASSERT (close (255) == 0);
162 1 : ASSERT (close (256) == 0);
163 : }
164 1 : ASSERT (dup2 (fd, bad_fd - 1) == bad_fd - 1);
165 1 : ASSERT (close (bad_fd - 1) == 0);
166 1 : errno = 0;
167 1 : ASSERT (dup2 (fd, bad_fd) == -1);
168 1 : ASSERT (errno == EBADF);
169 :
170 : /* Using dup2 can skip fds. */
171 1 : ASSERT (dup2 (fd, fd + 2) == fd + 2);
172 1 : ASSERT (is_open (fd));
173 1 : ASSERT (!is_open (fd + 1));
174 1 : ASSERT (is_open (fd + 2));
175 :
176 : /* Verify that dup2 closes the previous occupant of a fd. */
177 1 : ASSERT (open ("/dev/null", O_WRONLY, 0600) == fd + 1);
178 1 : ASSERT (dup2 (fd + 1, fd) == fd);
179 1 : ASSERT (close (fd + 1) == 0);
180 1 : ASSERT (write (fd, "1", 1) == 1);
181 1 : ASSERT (dup2 (fd + 2, fd) == fd);
182 1 : ASSERT (lseek (fd, 0, SEEK_END) == 0);
183 1 : ASSERT (write (fd + 2, "2", 1) == 1);
184 1 : ASSERT (lseek (fd, 0, SEEK_SET) == 0);
185 1 : ASSERT (read (fd, buffer, 1) == 1);
186 1 : ASSERT (*buffer == '2');
187 :
188 : #if GNULIB_TEST_CLOEXEC
189 : /* Any new fd created by dup2 must not be cloexec. */
190 1 : ASSERT (close (fd + 2) == 0);
191 1 : ASSERT (dup_cloexec (fd) == fd + 1);
192 1 : ASSERT (!is_inheritable (fd + 1));
193 1 : ASSERT (dup2 (fd + 1, fd + 1) == fd + 1);
194 1 : ASSERT (!is_inheritable (fd + 1));
195 1 : ASSERT (dup2 (fd + 1, fd + 2) == fd + 2);
196 1 : ASSERT (!is_inheritable (fd + 1));
197 1 : ASSERT (is_inheritable (fd + 2));
198 1 : errno = 0;
199 1 : ASSERT (dup2 (fd + 1, -1) == -1);
200 1 : ASSERT (errno == EBADF);
201 1 : ASSERT (!is_inheritable (fd + 1));
202 : #endif
203 :
204 : /* On systems that distinguish between text and binary mode, dup2
205 : reuses the mode of the source. */
206 1 : setmode (fd, O_BINARY);
207 1 : ASSERT (is_mode (fd, O_BINARY));
208 1 : ASSERT (dup2 (fd, fd + 1) == fd + 1);
209 1 : ASSERT (is_mode (fd + 1, O_BINARY));
210 1 : setmode (fd, O_TEXT);
211 1 : ASSERT (is_mode (fd, O_TEXT));
212 1 : ASSERT (dup2 (fd, fd + 1) == fd + 1);
213 1 : ASSERT (is_mode (fd + 1, O_TEXT));
214 :
215 : /* Clean up. */
216 1 : ASSERT (close (fd + 2) == 0);
217 1 : ASSERT (close (fd + 1) == 0);
218 1 : ASSERT (close (fd) == 0);
219 1 : ASSERT (unlink (file) == 0);
220 :
221 1 : return 0;
222 : }
|