]> git.cworth.org Git - tar/blob - gnu/fdopendir.c
Imported Upstream version 1.24
[tar] / gnu / fdopendir.c
1 /* -*- buffer-read-only: t -*- vi: set ro: */
2 /* DO NOT EDIT! GENERATED AUTOMATICALLY! */
3 /* provide a replacement fdopendir 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 <dirent.h>
24
25 #include <stdlib.h>
26 #include <unistd.h>
27
28 #if !HAVE_FDOPENDIR
29
30 # include "openat.h"
31 # include "openat-priv.h"
32 # include "save-cwd.h"
33
34 # if GNULIB_DIRENT_SAFER
35 #  include "dirent--.h"
36 # endif
37
38 static DIR *fdopendir_with_dup (int, int);
39 static DIR *fd_clone_opendir (int);
40
41 /* Replacement for POSIX fdopendir.
42
43    First, try to simulate it via opendir ("/proc/self/fd/FD").  Failing
44    that, simulate it by using fchdir metadata, or by doing
45    save_cwd/fchdir/opendir(".")/restore_cwd.
46    If either the save_cwd or the restore_cwd fails (relatively unlikely),
47    then give a diagnostic and exit nonzero.
48
49    If successful, the resulting stream is based on FD in
50    implementations where streams are based on file descriptors and in
51    applications where no other thread or signal handler allocates or
52    frees file descriptors.  In other cases, consult dirfd on the result
53    to find out whether FD is still being used.
54
55    Otherwise, this function works just like POSIX fdopendir.
56
57    W A R N I N G:
58
59    Unlike other fd-related functions, this one places constraints on FD.
60    If this function returns successfully, FD is under control of the
61    dirent.h system, and the caller should not close or modify the state of
62    FD other than by the dirent.h functions.  */
63 DIR *
64 fdopendir (int fd)
65 {
66   return fdopendir_with_dup (fd, -1);
67 }
68
69 /* Like fdopendir, except that if OLDER_DUPFD is not -1, it is known
70    to be a dup of FD which is less than FD - 1 and which will be
71    closed by the caller and not otherwise used by the caller.  This
72    function makes sure that FD is closed and all file descriptors less
73    than FD are open, and then calls fd_clone_opendir on a dup of FD.
74    That way, barring race conditions, fd_clone_opendir returns a
75    stream whose file descriptor is FD.  */
76 static DIR *
77 fdopendir_with_dup (int fd, int older_dupfd)
78 {
79   int dupfd = dup (fd);
80   if (dupfd < 0 && errno == EMFILE)
81     dupfd = older_dupfd;
82   if (dupfd < 0)
83     return NULL;
84   else
85     {
86       DIR *dir;
87       int saved_errno;
88       if (dupfd < fd - 1 && dupfd != older_dupfd)
89         {
90           dir = fdopendir_with_dup (fd, dupfd);
91           saved_errno = errno;
92         }
93       else
94         {
95           close (fd);
96           dir = fd_clone_opendir (dupfd);
97           saved_errno = errno;
98           if (! dir)
99             {
100               int fd1 = dup (dupfd);
101               if (fd1 != fd)
102                 openat_save_fail (fd1 < 0 ? errno : EBADF);
103             }
104         }
105
106       if (dupfd != older_dupfd)
107         close (dupfd);
108       errno = saved_errno;
109       return dir;
110     }
111 }
112
113 /* Like fdopendir, except the result controls a clone of FD.  It is
114    the caller's responsibility both to close FD and (if the result is
115    not null) to closedir the result.  */
116 static DIR *
117 fd_clone_opendir (int fd)
118 {
119   int saved_errno;
120   DIR *dir;
121
122   char buf[OPENAT_BUFFER_SIZE];
123   char *proc_file = openat_proc_name (buf, fd, ".");
124   if (proc_file)
125     {
126       dir = opendir (proc_file);
127       saved_errno = errno;
128     }
129   else
130     {
131       dir = NULL;
132       saved_errno = EOPNOTSUPP;
133     }
134
135   /* If the syscall fails with an expected errno value, resort to
136      save_cwd/restore_cwd.  */
137   if (! dir && EXPECTED_ERRNO (saved_errno))
138     {
139 # if REPLACE_FCHDIR
140       const char *name = _gl_directory_name (fd);
141       if (name)
142         dir = opendir (name);
143       saved_errno = errno;
144 # else /* !REPLACE_FCHDIR */
145
146       /* Occupy the destination FD slot, so that save_cwd cannot hijack it.  */
147       int fd_reserve = dup (fd);
148       if (fd_reserve < 0)
149         {
150           saved_errno = errno;
151           dir = NULL;
152           goto fail;
153         }
154
155       struct saved_cwd saved_cwd;
156       if (save_cwd (&saved_cwd) != 0)
157         openat_save_fail (errno);
158
159       /* Liberate the target file descriptor, so that opendir uses it.  */
160       close (fd_reserve);
161
162       if (fchdir (fd) != 0)
163         {
164           dir = NULL;
165           saved_errno = errno;
166         }
167       else
168         {
169           dir = opendir (".");
170           saved_errno = errno;
171
172           if (restore_cwd (&saved_cwd) != 0)
173             openat_restore_fail (errno);
174         }
175
176       free_cwd (&saved_cwd);
177 # endif /* !REPLACE_FCHDIR */
178     }
179
180  fail:
181   if (proc_file != buf)
182     free (proc_file);
183   errno = saved_errno;
184   return dir;
185 }
186
187 #else /* HAVE_FDOPENDIR */
188
189 # include <errno.h>
190 # include <sys/stat.h>
191
192 # undef fdopendir
193
194 /* Like fdopendir, but work around GNU/Hurd bug by validating FD.  */
195
196 DIR *
197 rpl_fdopendir (int fd)
198 {
199   struct stat st;
200   if (fstat (fd, &st))
201     return NULL;
202   if (!S_ISDIR (st.st_mode))
203     {
204       errno = ENOTDIR;
205       return NULL;
206     }
207   return fdopendir (fd);
208 }
209
210 #endif /* HAVE_FDOPENDIR */