root/lib/fdopendir.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. fdopendir
  2. fdopendir
  3. fdopendir
  4. fdopendir_with_dup
  5. fd_clone_opendir
  6. rpl_fdopendir

     1 /* provide a replacement fdopendir function
     2    Copyright (C) 2004-2023 Free Software Foundation, Inc.
     3 
     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.
     8 
     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.
    13 
    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/>.  */
    16 
    17 /* written by Jim Meyering */
    18 
    19 #include <config.h>
    20 
    21 #include <dirent.h>
    22 
    23 #include <stdlib.h>
    24 #include <unistd.h>
    25 
    26 #if !HAVE_FDOPENDIR
    27 
    28 # if GNULIB_defined_DIR
    29 /* We are in control of the file descriptor of a DIR.  */
    30 
    31 #  include "dirent-private.h"
    32 
    33 #  if !REPLACE_FCHDIR
    34 #   error "unexpected configuration: GNULIB_defined_DIR but fchdir not replaced"
    35 #  endif
    36 
    37 DIR *
    38 fdopendir (int fd)
    39 {
    40   char const *name = _gl_directory_name (fd);
    41   DIR *dirp = name ? opendir (name) : NULL;
    42   if (dirp != NULL)
    43     dirp->fd_to_close = fd;
    44   return dirp;
    45 }
    46 
    47 # elif defined __KLIBC__
    48 
    49 #  include <InnoTekLIBC/backend.h>
    50 
    51 DIR *
    52 fdopendir (int fd)
    53 {
    54   char path[_MAX_PATH];
    55   DIR *dirp;
    56 
    57   /* Get a path from fd */
    58   if (__libc_Back_ioFHToPath (fd, path, sizeof (path)))
    59     return NULL;
    60 
    61   dirp = opendir (path);
    62   if (!dirp)
    63     return NULL;
    64 
    65   /* Unregister fd registered by opendir() */
    66   _gl_unregister_dirp_fd (dirfd (dirp));
    67 
    68   /* Register our fd */
    69   if (_gl_register_dirp_fd (fd, dirp))
    70     {
    71       int saved_errno = errno;
    72 
    73       closedir (dirp);
    74 
    75       errno = saved_errno;
    76 
    77       dirp = NULL;
    78     }
    79 
    80   return dirp;
    81 }
    82 
    83 # else
    84 /* We are not in control of the file descriptor of a DIR, and therefore have to
    85    play tricks with file descriptors before and after a call to opendir().  */
    86 
    87 #  include "openat.h"
    88 #  include "openat-priv.h"
    89 #  include "save-cwd.h"
    90 
    91 #  if GNULIB_DIRENT_SAFER
    92 #   include "dirent--.h"
    93 #  endif
    94 
    95 #  ifndef REPLACE_FCHDIR
    96 #   define REPLACE_FCHDIR 0
    97 #  endif
    98 
    99 static DIR *fdopendir_with_dup (int, int, struct saved_cwd const *);
   100 static DIR *fd_clone_opendir (int, struct saved_cwd const *);
   101 
   102 /* Replacement for POSIX fdopendir.
   103 
   104    First, try to simulate it via opendir ("/proc/self/fd/...").  Failing
   105    that, simulate it by using fchdir metadata, or by doing
   106    save_cwd/fchdir/opendir(".")/restore_cwd.
   107    If either the save_cwd or the restore_cwd fails (relatively unlikely),
   108    then give a diagnostic and exit nonzero.
   109 
   110    If successful, the resulting stream is based on FD in
   111    implementations where streams are based on file descriptors and in
   112    applications where no other thread or signal handler allocates or
   113    frees file descriptors.  In other cases, consult dirfd on the result
   114    to find out whether FD is still being used.
   115 
   116    Otherwise, this function works just like POSIX fdopendir.
   117 
   118    W A R N I N G:
   119 
   120    Unlike other fd-related functions, this one places constraints on FD.
   121    If this function returns successfully, FD is under control of the
   122    dirent.h system, and the caller should not close or modify the state of
   123    FD other than by the dirent.h functions.  */
   124 DIR *
   125 fdopendir (int fd)
   126 {
   127   DIR *dir = fdopendir_with_dup (fd, -1, NULL);
   128 
   129   if (! REPLACE_FCHDIR && ! dir)
   130     {
   131       int saved_errno = errno;
   132       if (EXPECTED_ERRNO (saved_errno))
   133         {
   134           struct saved_cwd cwd;
   135           if (save_cwd (&cwd) != 0)
   136             openat_save_fail (errno);
   137           dir = fdopendir_with_dup (fd, -1, &cwd);
   138           saved_errno = errno;
   139           free_cwd (&cwd);
   140           errno = saved_errno;
   141         }
   142     }
   143 
   144   return dir;
   145 }
   146 
   147 /* Like fdopendir, except that if OLDER_DUPFD is not -1, it is known
   148    to be a dup of FD which is less than FD - 1 and which will be
   149    closed by the caller and not otherwise used by the caller.  This
   150    function makes sure that FD is closed and all file descriptors less
   151    than FD are open, and then calls fd_clone_opendir on a dup of FD.
   152    That way, barring race conditions, fd_clone_opendir returns a
   153    stream whose file descriptor is FD.
   154 
   155    If REPLACE_FCHDIR or CWD is null, use opendir ("/proc/self/fd/...",
   156    falling back on fchdir metadata.  Otherwise, CWD is a saved version
   157    of the working directory; use fchdir/opendir(".")/restore_cwd(CWD).  */
   158 static DIR *
   159 fdopendir_with_dup (int fd, int older_dupfd, struct saved_cwd const *cwd)
   160 {
   161   int dupfd = dup (fd);
   162   if (dupfd < 0 && errno == EMFILE)
   163     dupfd = older_dupfd;
   164   if (dupfd < 0)
   165     return NULL;
   166   else
   167     {
   168       DIR *dir;
   169       int saved_errno;
   170       if (dupfd < fd - 1 && dupfd != older_dupfd)
   171         {
   172           dir = fdopendir_with_dup (fd, dupfd, cwd);
   173           saved_errno = errno;
   174         }
   175       else
   176         {
   177           close (fd);
   178           dir = fd_clone_opendir (dupfd, cwd);
   179           saved_errno = errno;
   180           if (! dir)
   181             {
   182               int fd1 = dup (dupfd);
   183               if (fd1 != fd)
   184                 openat_save_fail (fd1 < 0 ? errno : EBADF);
   185             }
   186         }
   187 
   188       if (dupfd != older_dupfd)
   189         close (dupfd);
   190       errno = saved_errno;
   191       return dir;
   192     }
   193 }
   194 
   195 /* Like fdopendir, except the result controls a clone of FD.  It is
   196    the caller's responsibility both to close FD and (if the result is
   197    not null) to closedir the result.  */
   198 static DIR *
   199 fd_clone_opendir (int fd, struct saved_cwd const *cwd)
   200 {
   201   if (REPLACE_FCHDIR || ! cwd)
   202     {
   203       DIR *dir = NULL;
   204       int saved_errno = EOPNOTSUPP;
   205       char buf[OPENAT_BUFFER_SIZE];
   206       char *proc_file = openat_proc_name (buf, fd, ".");
   207       if (proc_file)
   208         {
   209           dir = opendir (proc_file);
   210           saved_errno = errno;
   211           if (proc_file != buf)
   212             free (proc_file);
   213         }
   214 #  if REPLACE_FCHDIR
   215       if (! dir && EXPECTED_ERRNO (saved_errno))
   216         {
   217           char const *name = _gl_directory_name (fd);
   218           DIR *dp = name ? opendir (name) : NULL;
   219 
   220           /* The caller has done an elaborate dance to arrange for opendir to
   221              consume just the right file descriptor.  If dirfd returns -1,
   222              though, we're on a system like mingw where opendir does not
   223              consume a file descriptor.  Consume it via 'dup' instead.  */
   224           if (dp && dirfd (dp) < 0)
   225             dup (fd);
   226 
   227           return dp;
   228         }
   229 #  endif
   230       errno = saved_errno;
   231       return dir;
   232     }
   233   else
   234     {
   235       if (fchdir (fd) != 0)
   236         return NULL;
   237       else
   238         {
   239           DIR *dir = opendir (".");
   240           int saved_errno = errno;
   241           if (restore_cwd (cwd) != 0)
   242             openat_restore_fail (errno);
   243           errno = saved_errno;
   244           return dir;
   245         }
   246     }
   247 }
   248 
   249 # endif
   250 
   251 #else /* HAVE_FDOPENDIR */
   252 
   253 # include <errno.h>
   254 # include <sys/stat.h>
   255 
   256 # undef fdopendir
   257 
   258 /* Like fdopendir, but work around GNU/Hurd bug by validating FD.  */
   259 
   260 DIR *
   261 rpl_fdopendir (int fd)
   262 {
   263   struct stat st;
   264   if (fstat (fd, &st))
   265     return NULL;
   266   if (!S_ISDIR (st.st_mode))
   267     {
   268       errno = ENOTDIR;
   269       return NULL;
   270     }
   271   return fdopendir (fd);
   272 }
   273 
   274 #endif /* HAVE_FDOPENDIR */

/* [<][>][^][v][top][bottom][index][help] */