1 /* provide a replacement fdopendir function
2 Copyright (C) 2004-2010 Free Software Foundation, Inc.
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.
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.
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/>. */
17 /* written by Jim Meyering */
29 # include "openat-priv.h"
30 # include "save-cwd.h"
32 # if GNULIB_DIRENT_SAFER
33 # include "dirent--.h"
36 static DIR *fdopendir_with_dup (int, int);
37 static DIR *fd_clone_opendir (int);
39 /* Replacement for POSIX fdopendir.
41 First, try to simulate it via opendir ("/proc/self/fd/FD"). Failing
42 that, simulate it by using fchdir metadata, or by doing
43 save_cwd/fchdir/opendir(".")/restore_cwd.
44 If either the save_cwd or the restore_cwd fails (relatively unlikely),
45 then give a diagnostic and exit nonzero.
47 If successful, the resulting stream is based on FD in
48 implementations where streams are based on file descriptors and in
49 applications where no other thread or signal handler allocates or
50 frees file descriptors. In other cases, consult dirfd on the result
51 to find out whether FD is still being used.
53 Otherwise, this function works just like POSIX fdopendir.
57 Unlike other fd-related functions, this one places constraints on FD.
58 If this function returns successfully, FD is under control of the
59 dirent.h system, and the caller should not close or modify the state of
60 FD other than by the dirent.h functions. */
64 return fdopendir_with_dup (fd, -1);
67 /* Like fdopendir, except that if OLDER_DUPFD is not -1, it is known
68 to be a dup of FD which is less than FD - 1 and which will be
69 closed by the caller and not otherwise used by the caller. This
70 function makes sure that FD is closed and all file descriptors less
71 than FD are open, and then calls fd_clone_opendir on a dup of FD.
72 That way, barring race conditions, fd_clone_opendir returns a
73 stream whose file descriptor is FD. */
75 fdopendir_with_dup (int fd, int older_dupfd)
78 if (dupfd < 0 && errno == EMFILE)
86 if (dupfd < fd - 1 && dupfd != older_dupfd)
88 dir = fdopendir_with_dup (fd, dupfd);
94 dir = fd_clone_opendir (dupfd);
98 int fd1 = dup (dupfd);
100 openat_save_fail (fd1 < 0 ? errno : EBADF);
104 if (dupfd != older_dupfd)
111 /* Like fdopendir, except the result controls a clone of FD. It is
112 the caller's responsibility both to close FD and (if the result is
113 not null) to closedir the result. */
115 fd_clone_opendir (int fd)
120 char buf[OPENAT_BUFFER_SIZE];
121 char *proc_file = openat_proc_name (buf, fd, ".");
124 dir = opendir (proc_file);
130 saved_errno = EOPNOTSUPP;
133 /* If the syscall fails with an expected errno value, resort to
134 save_cwd/restore_cwd. */
135 if (! dir && EXPECTED_ERRNO (saved_errno))
138 const char *name = _gl_directory_name (fd);
140 dir = opendir (name);
142 # else /* !REPLACE_FCHDIR */
144 /* Occupy the destination FD slot, so that save_cwd cannot hijack it. */
145 struct saved_cwd saved_cwd;
146 int fd_reserve = dup (fd);
154 if (save_cwd (&saved_cwd) != 0)
155 openat_save_fail (errno);
157 /* Liberate the target file descriptor, so that opendir uses it. */
160 if (fchdir (fd) != 0)
170 if (restore_cwd (&saved_cwd) != 0)
171 openat_restore_fail (errno);
174 free_cwd (&saved_cwd);
175 # endif /* !REPLACE_FCHDIR */
179 if (proc_file != buf)
185 #else /* HAVE_FDOPENDIR */
188 # include <sys/stat.h>
192 /* Like fdopendir, but work around GNU/Hurd bug by validating FD. */
195 rpl_fdopendir (int fd)
200 if (!S_ISDIR (st.st_mode))
205 return fdopendir (fd);
208 #endif /* HAVE_FDOPENDIR */