1 /* provide a replacement fdopendir function
2 Copyright (C) 2004-2022 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 <https://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 # ifndef REPLACE_FCHDIR
37 # define REPLACE_FCHDIR 0
40 static DIR *fdopendir_with_dup (int, int, struct saved_cwd
const *);
41 static DIR *fd_clone_opendir (int, struct saved_cwd
const *);
43 /* Replacement for POSIX fdopendir.
45 First, try to simulate it via opendir ("/proc/self/fd/..."). Failing
46 that, simulate it by using fchdir metadata, or by doing
47 save_cwd/fchdir/opendir(".")/restore_cwd.
48 If either the save_cwd or the restore_cwd fails (relatively unlikely),
49 then give a diagnostic and exit nonzero.
51 If successful, the resulting stream is based on FD in
52 implementations where streams are based on file descriptors and in
53 applications where no other thread or signal handler allocates or
54 frees file descriptors. In other cases, consult dirfd on the result
55 to find out whether FD is still being used.
57 Otherwise, this function works just like POSIX fdopendir.
61 Unlike other fd-related functions, this one places constraints on FD.
62 If this function returns successfully, FD is under control of the
63 dirent.h system, and the caller should not close or modify the state of
64 FD other than by the dirent.h functions. */
66 # include <InnoTekLIBC/backend.h>
74 /* Get a path from fd */
75 if (__libc_Back_ioFHToPath (fd
, path
, sizeof (path
)))
78 dirp
= opendir (path
);
82 /* Unregister fd registered by opendir() */
83 _gl_unregister_dirp_fd (dirfd (dirp
));
86 if (_gl_register_dirp_fd (fd
, dirp
))
88 int saved_errno
= errno
;
103 DIR *dir
= fdopendir_with_dup (fd
, -1, NULL
);
105 if (! REPLACE_FCHDIR
&& ! dir
)
107 int saved_errno
= errno
;
108 if (EXPECTED_ERRNO (saved_errno
))
110 struct saved_cwd cwd
;
111 if (save_cwd (&cwd
) != 0)
112 openat_save_fail (errno
);
113 dir
= fdopendir_with_dup (fd
, -1, &cwd
);
124 /* Like fdopendir, except that if OLDER_DUPFD is not -1, it is known
125 to be a dup of FD which is less than FD - 1 and which will be
126 closed by the caller and not otherwise used by the caller. This
127 function makes sure that FD is closed and all file descriptors less
128 than FD are open, and then calls fd_clone_opendir on a dup of FD.
129 That way, barring race conditions, fd_clone_opendir returns a
130 stream whose file descriptor is FD.
132 If REPLACE_FCHDIR or CWD is null, use opendir ("/proc/self/fd/...",
133 falling back on fchdir metadata. Otherwise, CWD is a saved version
134 of the working directory; use fchdir/opendir(".")/restore_cwd(CWD). */
136 fdopendir_with_dup (int fd
, int older_dupfd
, struct saved_cwd
const *cwd
)
138 int dupfd
= dup (fd
);
139 if (dupfd
< 0 && errno
== EMFILE
)
147 if (dupfd
< fd
- 1 && dupfd
!= older_dupfd
)
149 dir
= fdopendir_with_dup (fd
, dupfd
, cwd
);
155 dir
= fd_clone_opendir (dupfd
, cwd
);
159 int fd1
= dup (dupfd
);
161 openat_save_fail (fd1
< 0 ? errno
: EBADF
);
165 if (dupfd
!= older_dupfd
)
172 /* Like fdopendir, except the result controls a clone of FD. It is
173 the caller's responsibility both to close FD and (if the result is
174 not null) to closedir the result. */
176 fd_clone_opendir (int fd
, struct saved_cwd
const *cwd
)
178 if (REPLACE_FCHDIR
|| ! cwd
)
181 int saved_errno
= EOPNOTSUPP
;
182 char buf
[OPENAT_BUFFER_SIZE
];
183 char *proc_file
= openat_proc_name (buf
, fd
, ".");
186 dir
= opendir (proc_file
);
188 if (proc_file
!= buf
)
192 if (! dir
&& EXPECTED_ERRNO (saved_errno
))
194 char const *name
= _gl_directory_name (fd
);
195 DIR *dp
= name
? opendir (name
) : NULL
;
197 /* The caller has done an elaborate dance to arrange for opendir to
198 consume just the right file descriptor. If dirfd returns -1,
199 though, we're on a system like mingw where opendir does not
200 consume a file descriptor. Consume it via 'dup' instead. */
201 if (dp
&& dirfd (dp
) < 0)
212 if (fchdir (fd
) != 0)
216 DIR *dir
= opendir (".");
217 int saved_errno
= errno
;
218 if (restore_cwd (cwd
) != 0)
219 openat_restore_fail (errno
);
226 #else /* HAVE_FDOPENDIR */
229 # include <sys/stat.h>
233 /* Like fdopendir, but work around GNU/Hurd bug by validating FD. */
236 rpl_fdopendir (int fd
)
241 if (!S_ISDIR (st
.st_mode
))
246 return fdopendir (fd
);
249 #endif /* HAVE_FDOPENDIR */