root/maint/gnulib/lib/fdopendir.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

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

   1 /* provide a replacement fdopendir function
   2    Copyright (C) 2004-2021 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 # include "openat.h"
  29 # include "openat-priv.h"
  30 # include "save-cwd.h"
  31 
  32 # if GNULIB_DIRENT_SAFER
  33 #  include "dirent--.h"
  34 # endif
  35 
  36 # ifndef REPLACE_FCHDIR
  37 #  define REPLACE_FCHDIR 0
  38 # endif
  39 
  40 static DIR *fdopendir_with_dup (int, int, struct saved_cwd const *);
  41 static DIR *fd_clone_opendir (int, struct saved_cwd const *);
  42 
  43 /* Replacement for POSIX fdopendir.
  44 
  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.
  50 
  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.
  56 
  57    Otherwise, this function works just like POSIX fdopendir.
  58 
  59    W A R N I N G:
  60 
  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.  */
  65 # ifdef __KLIBC__
  66 #  include <InnoTekLIBC/backend.h>
  67 
  68 DIR *
  69 fdopendir (int fd)
     /* [previous][next][first][last][top][bottom][index][help] */
  70 {
  71   char path[_MAX_PATH];
  72   DIR *dirp;
  73 
  74   /* Get a path from fd */
  75   if (__libc_Back_ioFHToPath (fd, path, sizeof (path)))
  76     return NULL;
  77 
  78   dirp = opendir (path);
  79   if (!dirp)
  80     return NULL;
  81 
  82   /* Unregister fd registered by opendir() */
  83   _gl_unregister_dirp_fd (dirfd (dirp));
  84 
  85   /* Register our fd */
  86   if (_gl_register_dirp_fd (fd, dirp))
  87     {
  88       int saved_errno = errno;
  89 
  90       closedir (dirp);
  91 
  92       errno = saved_errno;
  93 
  94       dirp = NULL;
  95     }
  96 
  97   return dirp;
  98 }
  99 # else
 100 DIR *
 101 fdopendir (int fd)
     /* [previous][next][first][last][top][bottom][index][help] */
 102 {
 103   DIR *dir = fdopendir_with_dup (fd, -1, NULL);
 104 
 105   if (! REPLACE_FCHDIR && ! dir)
 106     {
 107       int saved_errno = errno;
 108       if (EXPECTED_ERRNO (saved_errno))
 109         {
 110           struct saved_cwd cwd;
 111           if (save_cwd (&cwd) != 0)
 112             openat_save_fail (errno);
 113           dir = fdopendir_with_dup (fd, -1, &cwd);
 114           saved_errno = errno;
 115           free_cwd (&cwd);
 116           errno = saved_errno;
 117         }
 118     }
 119 
 120   return dir;
 121 }
 122 # endif
 123 
 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.
 131 
 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).  */
 135 static DIR *
 136 fdopendir_with_dup (int fd, int older_dupfd, struct saved_cwd const *cwd)
     /* [previous][next][first][last][top][bottom][index][help] */
 137 {
 138   int dupfd = dup (fd);
 139   if (dupfd < 0 && errno == EMFILE)
 140     dupfd = older_dupfd;
 141   if (dupfd < 0)
 142     return NULL;
 143   else
 144     {
 145       DIR *dir;
 146       int saved_errno;
 147       if (dupfd < fd - 1 && dupfd != older_dupfd)
 148         {
 149           dir = fdopendir_with_dup (fd, dupfd, cwd);
 150           saved_errno = errno;
 151         }
 152       else
 153         {
 154           close (fd);
 155           dir = fd_clone_opendir (dupfd, cwd);
 156           saved_errno = errno;
 157           if (! dir)
 158             {
 159               int fd1 = dup (dupfd);
 160               if (fd1 != fd)
 161                 openat_save_fail (fd1 < 0 ? errno : EBADF);
 162             }
 163         }
 164 
 165       if (dupfd != older_dupfd)
 166         close (dupfd);
 167       errno = saved_errno;
 168       return dir;
 169     }
 170 }
 171 
 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.  */
 175 static DIR *
 176 fd_clone_opendir (int fd, struct saved_cwd const *cwd)
     /* [previous][next][first][last][top][bottom][index][help] */
 177 {
 178   if (REPLACE_FCHDIR || ! cwd)
 179     {
 180       DIR *dir = NULL;
 181       int saved_errno = EOPNOTSUPP;
 182       char buf[OPENAT_BUFFER_SIZE];
 183       char *proc_file = openat_proc_name (buf, fd, ".");
 184       if (proc_file)
 185         {
 186           dir = opendir (proc_file);
 187           saved_errno = errno;
 188           if (proc_file != buf)
 189             free (proc_file);
 190         }
 191 # if REPLACE_FCHDIR
 192       if (! dir && EXPECTED_ERRNO (saved_errno))
 193         {
 194           char const *name = _gl_directory_name (fd);
 195           DIR *dp = name ? opendir (name) : NULL;
 196 
 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)
 202             dup (fd);
 203 
 204           return dp;
 205         }
 206 # endif
 207       errno = saved_errno;
 208       return dir;
 209     }
 210   else
 211     {
 212       if (fchdir (fd) != 0)
 213         return NULL;
 214       else
 215         {
 216           DIR *dir = opendir (".");
 217           int saved_errno = errno;
 218           if (restore_cwd (cwd) != 0)
 219             openat_restore_fail (errno);
 220           errno = saved_errno;
 221           return dir;
 222         }
 223     }
 224 }
 225 
 226 #else /* HAVE_FDOPENDIR */
 227 
 228 # include <errno.h>
 229 # include <sys/stat.h>
 230 
 231 # undef fdopendir
 232 
 233 /* Like fdopendir, but work around GNU/Hurd bug by validating FD.  */
 234 
 235 DIR *
 236 rpl_fdopendir (int fd)
     /* [previous][next][first][last][top][bottom][index][help] */
 237 {
 238   struct stat st;
 239   if (fstat (fd, &st))
 240     return NULL;
 241   if (!S_ISDIR (st.st_mode))
 242     {
 243       errno = ENOTDIR;
 244       return NULL;
 245     }
 246   return fdopendir (fd);
 247 }
 248 
 249 #endif /* HAVE_FDOPENDIR */

/* [previous][next][first][last][top][bottom][index][help] */