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