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