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