Update gnulib to current upstream master
[deliverable/binutils-gdb.git] / gdb / gnulib / import / openat.c
1 /* provide a replacement openat function
2 Copyright (C) 2004-2018 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 Jim Meyering */
18
19 /* If the user's config.h happens to include <fcntl.h>, let it include only
20 the system's <fcntl.h> here, so that orig_openat doesn't recurse to
21 rpl_openat. */
22 #define __need_system_fcntl_h
23 #include <config.h>
24
25 /* Get the original definition of open. It might be defined as a macro. */
26 #include <fcntl.h>
27 #include <sys/types.h>
28 #undef __need_system_fcntl_h
29
30 #if HAVE_OPENAT
31 static int
32 orig_openat (int fd, char const *filename, int flags, mode_t mode)
33 {
34 return openat (fd, filename, flags, mode);
35 }
36 #endif
37
38 /* Write "fcntl.h" here, not <fcntl.h>, otherwise OSF/1 5.1 DTK cc eliminates
39 this include because of the preliminary #include <fcntl.h> above. */
40 #include "fcntl.h"
41
42 #include "openat.h"
43
44 #include "cloexec.h"
45
46 #include <stdarg.h>
47 #include <stdbool.h>
48 #include <stddef.h>
49 #include <string.h>
50 #include <sys/stat.h>
51 #include <errno.h>
52
53 #if HAVE_OPENAT
54
55 /* Like openat, but support O_CLOEXEC and work around Solaris 9 bugs
56 with trailing slash. */
57 int
58 rpl_openat (int dfd, char const *filename, int flags, ...)
59 {
60 /* 0 = unknown, 1 = yes, -1 = no. */
61 #if GNULIB_defined_O_CLOEXEC
62 int have_cloexec = -1;
63 #else
64 static int have_cloexec;
65 #endif
66
67 mode_t mode;
68 int fd;
69
70 mode = 0;
71 if (flags & O_CREAT)
72 {
73 va_list arg;
74 va_start (arg, flags);
75
76 /* We have to use PROMOTED_MODE_T instead of mode_t, otherwise GCC 4
77 creates crashing code when 'mode_t' is smaller than 'int'. */
78 mode = va_arg (arg, PROMOTED_MODE_T);
79
80 va_end (arg);
81 }
82
83 # if OPEN_TRAILING_SLASH_BUG
84 /* If the filename ends in a slash and one of O_CREAT, O_WRONLY, O_RDWR
85 is specified, then fail.
86 Rationale: POSIX <http://www.opengroup.org/susv3/basedefs/xbd_chap04.html>
87 says that
88 "A pathname that contains at least one non-slash character and that
89 ends with one or more trailing slashes shall be resolved as if a
90 single dot character ( '.' ) were appended to the pathname."
91 and
92 "The special filename dot shall refer to the directory specified by
93 its predecessor."
94 If the named file already exists as a directory, then
95 - if O_CREAT is specified, open() must fail because of the semantics
96 of O_CREAT,
97 - if O_WRONLY or O_RDWR is specified, open() must fail because POSIX
98 <http://www.opengroup.org/susv3/functions/open.html> says that it
99 fails with errno = EISDIR in this case.
100 If the named file does not exist or does not name a directory, then
101 - if O_CREAT is specified, open() must fail since open() cannot create
102 directories,
103 - if O_WRONLY or O_RDWR is specified, open() must fail because the
104 file does not contain a '.' directory. */
105 if (flags & (O_CREAT | O_WRONLY | O_RDWR))
106 {
107 size_t len = strlen (filename);
108 if (len > 0 && filename[len - 1] == '/')
109 {
110 errno = EISDIR;
111 return -1;
112 }
113 }
114 # endif
115
116 fd = orig_openat (dfd, filename,
117 flags & ~(have_cloexec <= 0 ? O_CLOEXEC : 0), mode);
118
119 if (flags & O_CLOEXEC)
120 {
121 if (! have_cloexec)
122 {
123 if (0 <= fd)
124 have_cloexec = 1;
125 else if (errno == EINVAL)
126 {
127 fd = orig_openat (dfd, filename, flags & ~O_CLOEXEC, mode);
128 have_cloexec = -1;
129 }
130 }
131 if (have_cloexec < 0 && 0 <= fd)
132 set_cloexec_flag (fd, true);
133 }
134
135
136 # if OPEN_TRAILING_SLASH_BUG
137 /* If the filename ends in a slash and fd does not refer to a directory,
138 then fail.
139 Rationale: POSIX <http://www.opengroup.org/susv3/basedefs/xbd_chap04.html>
140 says that
141 "A pathname that contains at least one non-slash character and that
142 ends with one or more trailing slashes shall be resolved as if a
143 single dot character ( '.' ) were appended to the pathname."
144 and
145 "The special filename dot shall refer to the directory specified by
146 its predecessor."
147 If the named file without the slash is not a directory, open() must fail
148 with ENOTDIR. */
149 if (fd >= 0)
150 {
151 /* We know len is positive, since open did not fail with ENOENT. */
152 size_t len = strlen (filename);
153 if (filename[len - 1] == '/')
154 {
155 struct stat statbuf;
156
157 if (fstat (fd, &statbuf) >= 0 && !S_ISDIR (statbuf.st_mode))
158 {
159 close (fd);
160 errno = ENOTDIR;
161 return -1;
162 }
163 }
164 }
165 # endif
166
167 return fd;
168 }
169
170 #else /* !HAVE_OPENAT */
171
172 # include "dosname.h" /* solely for definition of IS_ABSOLUTE_FILE_NAME */
173 # include "openat-priv.h"
174 # include "save-cwd.h"
175
176 /* Replacement for Solaris' openat function.
177 <https://www.google.com/search?q=openat+site:docs.oracle.com>
178 First, try to simulate it via open ("/proc/self/fd/FD/FILE").
179 Failing that, simulate it by doing save_cwd/fchdir/open/restore_cwd.
180 If either the save_cwd or the restore_cwd fails (relatively unlikely),
181 then give a diagnostic and exit nonzero.
182 Otherwise, upon failure, set errno and return -1, as openat does.
183 Upon successful completion, return a file descriptor. */
184 int
185 openat (int fd, char const *file, int flags, ...)
186 {
187 mode_t mode = 0;
188
189 if (flags & O_CREAT)
190 {
191 va_list arg;
192 va_start (arg, flags);
193
194 /* We have to use PROMOTED_MODE_T instead of mode_t, otherwise GCC 4
195 creates crashing code when 'mode_t' is smaller than 'int'. */
196 mode = va_arg (arg, PROMOTED_MODE_T);
197
198 va_end (arg);
199 }
200
201 return openat_permissive (fd, file, flags, mode, NULL);
202 }
203
204 /* Like openat (FD, FILE, FLAGS, MODE), but if CWD_ERRNO is
205 nonnull, set *CWD_ERRNO to an errno value if unable to save
206 or restore the initial working directory. This is needed only
207 the first time remove.c's remove_dir opens a command-line
208 directory argument.
209
210 If a previous attempt to restore the current working directory
211 failed, then we must not even try to access a '.'-relative name.
212 It is the caller's responsibility not to call this function
213 in that case. */
214
215 int
216 openat_permissive (int fd, char const *file, int flags, mode_t mode,
217 int *cwd_errno)
218 {
219 struct saved_cwd saved_cwd;
220 int saved_errno;
221 int err;
222 bool save_ok;
223
224 if (fd == AT_FDCWD || IS_ABSOLUTE_FILE_NAME (file))
225 return open (file, flags, mode);
226
227 {
228 char buf[OPENAT_BUFFER_SIZE];
229 char *proc_file = openat_proc_name (buf, fd, file);
230 if (proc_file)
231 {
232 int open_result = open (proc_file, flags, mode);
233 int open_errno = errno;
234 if (proc_file != buf)
235 free (proc_file);
236 /* If the syscall succeeds, or if it fails with an unexpected
237 errno value, then return right away. Otherwise, fall through
238 and resort to using save_cwd/restore_cwd. */
239 if (0 <= open_result || ! EXPECTED_ERRNO (open_errno))
240 {
241 errno = open_errno;
242 return open_result;
243 }
244 }
245 }
246
247 save_ok = (save_cwd (&saved_cwd) == 0);
248 if (! save_ok)
249 {
250 if (! cwd_errno)
251 openat_save_fail (errno);
252 *cwd_errno = errno;
253 }
254 if (0 <= fd && fd == saved_cwd.desc)
255 {
256 /* If saving the working directory collides with the user's
257 requested fd, then the user's fd must have been closed to
258 begin with. */
259 free_cwd (&saved_cwd);
260 errno = EBADF;
261 return -1;
262 }
263
264 err = fchdir (fd);
265 saved_errno = errno;
266
267 if (! err)
268 {
269 err = open (file, flags, mode);
270 saved_errno = errno;
271 if (save_ok && restore_cwd (&saved_cwd) != 0)
272 {
273 if (! cwd_errno)
274 {
275 /* Don't write a message to just-created fd 2. */
276 saved_errno = errno;
277 if (err == STDERR_FILENO)
278 close (err);
279 openat_restore_fail (saved_errno);
280 }
281 *cwd_errno = errno;
282 }
283 }
284
285 free_cwd (&saved_cwd);
286 errno = saved_errno;
287 return err;
288 }
289
290 /* Return true if our openat implementation must resort to
291 using save_cwd and restore_cwd. */
292 bool
293 openat_needs_fchdir (void)
294 {
295 bool needs_fchdir = true;
296 int fd = open ("/", O_SEARCH);
297
298 if (0 <= fd)
299 {
300 char buf[OPENAT_BUFFER_SIZE];
301 char *proc_file = openat_proc_name (buf, fd, ".");
302 if (proc_file)
303 {
304 needs_fchdir = false;
305 if (proc_file != buf)
306 free (proc_file);
307 }
308 close (fd);
309 }
310
311 return needs_fchdir;
312 }
313
314 #endif /* !HAVE_OPENAT */
This page took 0.035237 seconds and 4 git commands to generate.