(root)/
gcc-13.2.0/
libstdc++-v3/
src/
filesystem/
dir-common.h
       1  // Filesystem directory iterator utilities -*- C++ -*-
       2  
       3  // Copyright (C) 2014-2023 Free Software Foundation, Inc.
       4  //
       5  // This file is part of the GNU ISO C++ Library.  This library is free
       6  // software; you can redistribute it and/or modify it under the
       7  // terms of the GNU General Public License as published by the
       8  // Free Software Foundation; either version 3, or (at your option)
       9  // any later version.
      10  
      11  // This library is distributed in the hope that it will be useful,
      12  // but WITHOUT ANY WARRANTY; without even the implied warranty of
      13  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14  // GNU General Public License for more details.
      15  
      16  // Under Section 7 of GPL version 3, you are granted additional
      17  // permissions described in the GCC Runtime Library Exception, version
      18  // 3.1, as published by the Free Software Foundation.
      19  
      20  // You should have received a copy of the GNU General Public License and
      21  // a copy of the GCC Runtime Library Exception along with this program;
      22  // see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
      23  // <http://www.gnu.org/licenses/>.
      24  
      25  #ifndef _GLIBCXX_DIR_COMMON_H
      26  #define _GLIBCXX_DIR_COMMON_H 1
      27  
      28  #include <stdint.h>  // uint32_t
      29  #include <string.h>  // strcmp
      30  #include <errno.h>
      31  #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
      32  #include <wchar.h>  // wcscmp
      33  #endif
      34  #ifdef _GLIBCXX_HAVE_DIRENT_H
      35  # ifdef _GLIBCXX_HAVE_SYS_TYPES_H
      36  #  include <sys/types.h>
      37  # endif
      38  # include <dirent.h> // opendir, readdir, fdopendir, dirfd
      39  # ifdef _GLIBCXX_HAVE_FCNTL_H
      40  #  include <fcntl.h>  // open, openat, fcntl, AT_FDCWD, O_NOFOLLOW etc.
      41  #  include <unistd.h> // close, unlinkat
      42  # endif
      43  #endif
      44  
      45  namespace std _GLIBCXX_VISIBILITY(default)
      46  {
      47  _GLIBCXX_BEGIN_NAMESPACE_VERSION
      48  namespace filesystem
      49  {
      50  namespace __gnu_posix
      51  {
      52  #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
      53  // Adapt the Windows _wxxx functions to look like POSIX xxx, but for wchar_t*.
      54  using char_type = wchar_t;
      55  using DIR = ::_WDIR;
      56  using dirent = _wdirent;
      57  inline DIR* opendir(const wchar_t* path) { return ::_wopendir(path); }
      58  inline dirent* readdir(DIR* dir) { return ::_wreaddir(dir); }
      59  inline int closedir(DIR* dir) { return ::_wclosedir(dir); }
      60  #elif defined _GLIBCXX_HAVE_DIRENT_H
      61  using char_type = char;
      62  using DIR = ::DIR;
      63  typedef struct ::dirent dirent;
      64  using ::opendir;
      65  using ::readdir;
      66  using ::closedir;
      67  #else
      68  using char_type = char;
      69  struct dirent { const char* d_name; };
      70  struct DIR { };
      71  inline DIR* opendir(const char*) { return nullptr; }
      72  inline dirent* readdir(DIR*) { return nullptr; }
      73  inline int closedir(DIR*) { return -1; }
      74  #undef _GLIBCXX_HAVE_DIRFD
      75  #undef _GLIBCXX_HAVE_UNLINKAT
      76  #endif
      77  } // namespace __gnu_posix
      78  
      79  namespace posix = __gnu_posix;
      80  
      81  inline bool
      82  is_permission_denied_error(int e)
      83  {
      84    if (e == EACCES)
      85      return true;
      86  #ifdef __APPLE__
      87    if (e == EPERM) // See PR 99533
      88      return true;
      89  #endif
      90    return false;
      91  }
      92  
      93  struct _Dir_base
      94  {
      95    // As well as the full pathname (including the directory iterator's path)
      96    // this type contains a file descriptor for a directory and a second pathname
      97    // relative to that directory. The file descriptor and relative pathname
      98    // can be used with POSIX openat and unlinkat.
      99    struct _At_path
     100    {
     101      // No file descriptor given, so interpret the pathname relative to the CWD.
     102      _At_path(const posix::char_type* p) noexcept
     103      : pathname(p), dir_fd(fdcwd()), offset(0)
     104      { }
     105  
     106      _At_path(int fd, const posix::char_type* p, size_t offset) noexcept
     107      : pathname(p), dir_fd(fd), offset(offset)
     108      { }
     109  
     110      const posix::char_type*
     111      path() const noexcept { return pathname; }
     112  
     113      int
     114      dir() const noexcept { return dir_fd; }
     115  
     116      const posix::char_type*
     117      path_at_dir() const noexcept { return pathname + offset; }
     118  
     119    private:
     120      const posix::char_type* pathname; // Full path relative to CWD.
     121      int dir_fd; // A directory descriptor (either the parent dir, or AT_FDCWD).
     122      uint32_t offset; // Offset into pathname for the part relative to dir_fd.
     123  
     124      // Special value representing the current working directory.
     125      // Not a valid file descriptor for an open directory stream.
     126      static constexpr int
     127      fdcwd() noexcept
     128      {
     129  #ifdef AT_FDCWD
     130        return AT_FDCWD;
     131  #else
     132        return -1; // Use invalid fd if AT_FDCWD isn't supported.
     133  #endif
     134      }
     135    };
     136  
     137    // If no error occurs then dirp is non-null,
     138    // otherwise null (even if a permission denied error is ignored).
     139    _Dir_base(const _At_path& atp,
     140  	    bool skip_permission_denied, bool nofollow,
     141  	    error_code& ec) noexcept
     142    : dirp(_Dir_base::openat(atp, nofollow))
     143    {
     144      if (dirp)
     145        ec.clear();
     146      else if (is_permission_denied_error(errno) && skip_permission_denied)
     147        ec.clear();
     148      else
     149        ec.assign(errno, std::generic_category());
     150    }
     151  
     152    _Dir_base(_Dir_base&& d) : dirp(std::exchange(d.dirp, nullptr)) { }
     153  
     154    _Dir_base& operator=(_Dir_base&&) = delete;
     155  
     156    ~_Dir_base() { if (dirp) posix::closedir(dirp); }
     157  
     158    const posix::dirent*
     159    advance(bool skip_permission_denied, error_code& ec) noexcept
     160    {
     161      ec.clear();
     162  
     163      int err = std::exchange(errno, 0);
     164      const posix::dirent* entp = posix::readdir(dirp);
     165      // std::swap cannot be used with Bionic's errno
     166      err = std::exchange(errno, err);
     167  
     168      if (entp)
     169        {
     170  	// skip past dot and dot-dot
     171  	if (is_dot_or_dotdot(entp->d_name))
     172  	  return advance(skip_permission_denied, ec);
     173  	return entp;
     174        }
     175      else if (err)
     176        {
     177  	if (err == EACCES && skip_permission_denied)
     178  	  return nullptr;
     179  	ec.assign(err, std::generic_category());
     180  	return nullptr;
     181        }
     182      else
     183        {
     184  	// reached the end
     185  	return nullptr;
     186        }
     187    }
     188  
     189    static bool is_dot_or_dotdot(const char* s) noexcept
     190    { return !strcmp(s, ".") || !strcmp(s, ".."); }
     191  
     192  #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
     193    static bool is_dot_or_dotdot(const wchar_t* s) noexcept
     194    { return !wcscmp(s, L".") || !wcscmp(s, L".."); }
     195  #endif
     196  
     197    // Set the close-on-exec flag if not already done via O_CLOEXEC.
     198    static bool
     199    set_close_on_exec([[maybe_unused]] int fd)
     200    {
     201  #if ! defined O_CLOEXEC && defined FD_CLOEXEC
     202      int flags = ::fcntl(fd, F_GETFD);
     203      if (flags == -1 || ::fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
     204        return false;
     205  #endif
     206      return true;
     207    }
     208  
     209    static posix::DIR*
     210    openat(const _At_path& atp, bool nofollow)
     211    {
     212  #if _GLIBCXX_HAVE_FDOPENDIR && defined O_RDONLY && defined O_DIRECTORY \
     213      && ! _GLIBCXX_FILESYSTEM_IS_WINDOWS
     214  
     215      // Any file descriptor we open here should be closed on exec.
     216  #ifdef O_CLOEXEC
     217      constexpr int close_on_exec = O_CLOEXEC;
     218  #else
     219      constexpr int close_on_exec = 0;
     220  #endif
     221  
     222      int flags = O_RDONLY | O_DIRECTORY | close_on_exec;
     223  
     224      // Directory iterators are vulnerable to race conditions unless O_NOFOLLOW
     225      // is supported, because a directory could be replaced with a symlink after
     226      // checking is_directory(symlink_status(f)). O_NOFOLLOW avoids the race.
     227  #ifdef O_NOFOLLOW
     228      if (nofollow)
     229        flags |= O_NOFOLLOW;
     230  #else
     231      nofollow = false;
     232  #endif
     233  
     234      int fd;
     235  
     236  #if _GLIBCXX_HAVE_OPENAT
     237      fd = ::openat(atp.dir(), atp.path_at_dir(), flags);
     238  #else
     239      // If we cannot use openat, there's no benefit to using posix::open unless
     240      // we will use O_NOFOLLOW, so just use the simpler posix::opendir.
     241      if (!nofollow)
     242        return posix::opendir(atp.path());
     243  
     244      fd = ::open(atp.path(), flags);
     245  #endif
     246  
     247      if (fd == -1)
     248        return nullptr;
     249      if (set_close_on_exec(fd))
     250        if (::DIR* dirp = ::fdopendir(fd))
     251  	return dirp;
     252      int err = errno;
     253      ::close(fd);
     254      errno = err;
     255      return nullptr;
     256  #else
     257      return posix::opendir(atp.path());
     258  #endif
     259    }
     260  
     261    posix::DIR*	dirp;
     262  };
     263  
     264  } // namespace filesystem
     265  
     266  // BEGIN/END macros must be defined before including this file.
     267  _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM
     268  
     269  inline file_type
     270  get_file_type(const std::filesystem::__gnu_posix::dirent& d [[gnu::unused]])
     271  {
     272  #ifdef _GLIBCXX_HAVE_STRUCT_DIRENT_D_TYPE
     273    switch (d.d_type)
     274    {
     275    case DT_BLK:
     276      return file_type::block;
     277    case DT_CHR:
     278      return file_type::character;
     279    case DT_DIR:
     280      return file_type::directory;
     281    case DT_FIFO:
     282      return file_type::fifo;
     283    case DT_LNK:
     284      return file_type::symlink;
     285    case DT_REG:
     286      return file_type::regular;
     287    case DT_SOCK:
     288      return file_type::socket;
     289    case DT_UNKNOWN:
     290      return file_type::unknown;
     291    default:
     292      return file_type::none;
     293    }
     294  #else
     295    return file_type::none;
     296  #endif
     297  }
     298  
     299  _GLIBCXX_END_NAMESPACE_FILESYSTEM
     300  
     301  _GLIBCXX_END_NAMESPACE_VERSION
     302  } // namespace std
     303  
     304  #endif // _GLIBCXX_DIR_COMMON_H