/* -*- buffer-read-only: t -*- vi: set ro: */
/* DO NOT EDIT! GENERATED AUTOMATICALLY! */
/* provide a replacement fdopendir function
- Copyright (C) 2004-2010 Free Software Foundation, Inc.
+ Copyright (C) 2004-2014 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
# include "dirent--.h"
# endif
-/* Replacement for Solaris' function by the same name.
- <http://www.google.com/search?q=fdopendir+site:docs.sun.com>
- First, try to simulate it via opendir ("/proc/self/fd/FD"). Failing
+# ifndef REPLACE_FCHDIR
+# define REPLACE_FCHDIR 0
+# endif
+
+static DIR *fdopendir_with_dup (int, int, struct saved_cwd const *);
+static DIR *fd_clone_opendir (int, struct saved_cwd const *);
+
+/* Replacement for POSIX fdopendir.
+
+ First, try to simulate it via opendir ("/proc/self/fd/..."). Failing
that, simulate it by using fchdir metadata, or by doing
save_cwd/fchdir/opendir(".")/restore_cwd.
If either the save_cwd or the restore_cwd fails (relatively unlikely),
then give a diagnostic and exit nonzero.
- Otherwise, this function works just like Solaris' fdopendir.
+
+ If successful, the resulting stream is based on FD in
+ implementations where streams are based on file descriptors and in
+ applications where no other thread or signal handler allocates or
+ frees file descriptors. In other cases, consult dirfd on the result
+ to find out whether FD is still being used.
+
+ Otherwise, this function works just like POSIX fdopendir.
W A R N I N G:
- Unlike other fd-related functions, this one effectively consumes
- its FD parameter. The caller should not close or otherwise
- manipulate FD if this function returns successfully. Also, this
- implementation does not guarantee that dirfd(fdopendir(n))==n;
- the open directory stream may use a clone of FD, or have no
- associated fd at all. */
+
+ Unlike other fd-related functions, this one places constraints on FD.
+ If this function returns successfully, FD is under control of the
+ dirent.h system, and the caller should not close or modify the state of
+ FD other than by the dirent.h functions. */
DIR *
fdopendir (int fd)
{
- int saved_errno;
- DIR *dir;
+ DIR *dir = fdopendir_with_dup (fd, -1, NULL);
- char buf[OPENAT_BUFFER_SIZE];
- char *proc_file = openat_proc_name (buf, fd, ".");
- if (proc_file)
+ if (! REPLACE_FCHDIR && ! dir)
{
- dir = opendir (proc_file);
- saved_errno = errno;
+ int saved_errno = errno;
+ if (EXPECTED_ERRNO (saved_errno))
+ {
+ struct saved_cwd cwd;
+ if (save_cwd (&cwd) != 0)
+ openat_save_fail (errno);
+ dir = fdopendir_with_dup (fd, -1, &cwd);
+ saved_errno = errno;
+ free_cwd (&cwd);
+ errno = saved_errno;
+ }
}
+
+ return dir;
+}
+
+/* Like fdopendir, except that if OLDER_DUPFD is not -1, it is known
+ to be a dup of FD which is less than FD - 1 and which will be
+ closed by the caller and not otherwise used by the caller. This
+ function makes sure that FD is closed and all file descriptors less
+ than FD are open, and then calls fd_clone_opendir on a dup of FD.
+ That way, barring race conditions, fd_clone_opendir returns a
+ stream whose file descriptor is FD.
+
+ If REPLACE_CHDIR or CWD is null, use opendir ("/proc/self/fd/...",
+ falling back on fchdir metadata. Otherwise, CWD is a saved version
+ of the working directory; use fchdir/opendir(".")/restore_cwd(CWD). */
+static DIR *
+fdopendir_with_dup (int fd, int older_dupfd, struct saved_cwd const *cwd)
+{
+ int dupfd = dup (fd);
+ if (dupfd < 0 && errno == EMFILE)
+ dupfd = older_dupfd;
+ if (dupfd < 0)
+ return NULL;
else
{
- dir = NULL;
- saved_errno = EOPNOTSUPP;
+ DIR *dir;
+ int saved_errno;
+ if (dupfd < fd - 1 && dupfd != older_dupfd)
+ {
+ dir = fdopendir_with_dup (fd, dupfd, cwd);
+ saved_errno = errno;
+ }
+ else
+ {
+ close (fd);
+ dir = fd_clone_opendir (dupfd, cwd);
+ saved_errno = errno;
+ if (! dir)
+ {
+ int fd1 = dup (dupfd);
+ if (fd1 != fd)
+ openat_save_fail (fd1 < 0 ? errno : EBADF);
+ }
+ }
+
+ if (dupfd != older_dupfd)
+ close (dupfd);
+ errno = saved_errno;
+ return dir;
}
+}
- /* If the syscall fails with an expected errno value, resort to
- save_cwd/restore_cwd. */
- if (! dir && EXPECTED_ERRNO (saved_errno))
+/* Like fdopendir, except the result controls a clone of FD. It is
+ the caller's responsibility both to close FD and (if the result is
+ not null) to closedir the result. */
+static DIR *
+fd_clone_opendir (int fd, struct saved_cwd const *cwd)
+{
+ if (REPLACE_FCHDIR || ! cwd)
{
-# if REPLACE_FCHDIR
- const char *name = _gl_directory_name (fd);
- if (name)
- dir = opendir (name);
- saved_errno = errno;
-# else /* !REPLACE_FCHDIR */
- struct saved_cwd saved_cwd;
- if (save_cwd (&saved_cwd) != 0)
- openat_save_fail (errno);
-
- if (fchdir (fd) != 0)
+ DIR *dir = NULL;
+ int saved_errno = EOPNOTSUPP;
+ char buf[OPENAT_BUFFER_SIZE];
+ char *proc_file = openat_proc_name (buf, fd, ".");
+ if (proc_file)
{
- dir = NULL;
+ dir = opendir (proc_file);
saved_errno = errno;
+ if (proc_file != buf)
+ free (proc_file);
+ }
+# if REPLACE_FCHDIR
+ if (! dir && EXPECTED_ERRNO (saved_errno))
+ {
+ char const *name = _gl_directory_name (fd);
+ return (name ? opendir (name) : NULL);
}
+# endif
+ errno = saved_errno;
+ return dir;
+ }
+ else
+ {
+ if (fchdir (fd) != 0)
+ return NULL;
else
{
- dir = opendir (".");
- saved_errno = errno;
-
- if (restore_cwd (&saved_cwd) != 0)
+ DIR *dir = opendir (".");
+ int saved_errno = errno;
+ if (restore_cwd (cwd) != 0)
openat_restore_fail (errno);
+ errno = saved_errno;
+ return dir;
}
-
- free_cwd (&saved_cwd);
-# endif /* !REPLACE_FCHDIR */
}
-
- if (dir)
- close (fd);
- if (proc_file != buf)
- free (proc_file);
- errno = saved_errno;
- return dir;
}
#else /* HAVE_FDOPENDIR */