1// Filesystem directory iterator utilities -*- C++ -*- 2 3// Copyright (C) 2014-2020 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 <string.h> // strcmp 29#include <errno.h> 30#if _GLIBCXX_FILESYSTEM_IS_WINDOWS 31#include <wchar.h> // wcscmp 32#endif 33#ifdef _GLIBCXX_HAVE_DIRENT_H 34# ifdef _GLIBCXX_HAVE_SYS_TYPES_H 35# include <sys/types.h> 36# endif 37# include <dirent.h> 38#endif 39 40namespace std _GLIBCXX_VISIBILITY(default) 41{ 42_GLIBCXX_BEGIN_NAMESPACE_VERSION 43namespace filesystem 44{ 45namespace __gnu_posix 46{ 47#if _GLIBCXX_FILESYSTEM_IS_WINDOWS 48// Adapt the Windows _wxxx functions to look like POSIX xxx, but for wchar_t*. 49using char_type = wchar_t; 50using DIR = ::_WDIR; 51using dirent = _wdirent; 52inline DIR* opendir(const wchar_t* path) { return ::_wopendir(path); } 53inline dirent* readdir(DIR* dir) { return ::_wreaddir(dir); } 54inline int closedir(DIR* dir) { return ::_wclosedir(dir); } 55#elif defined _GLIBCXX_HAVE_DIRENT_H 56using char_type = char; 57using DIR = ::DIR; 58typedef struct ::dirent dirent; 59using ::opendir; 60using ::readdir; 61using ::closedir; 62#else 63using char_type = char; 64struct dirent { const char* d_name; }; 65struct DIR { }; 66inline DIR* opendir(const char*) { return nullptr; } 67inline dirent* readdir(DIR*) { return nullptr; } 68inline int closedir(DIR*) { return -1; } 69#endif 70} // namespace __gnu_posix 71 72namespace posix = __gnu_posix; 73 74struct _Dir_base 75{ 76 _Dir_base(posix::DIR* dirp = nullptr) : dirp(dirp) { } 77 78 // If no error occurs then dirp is non-null, 79 // otherwise null (whether error ignored or not). 80 _Dir_base(const posix::char_type* pathname, bool skip_permission_denied, 81 error_code& ec) noexcept 82 : dirp(posix::opendir(pathname)) 83 { 84 if (dirp) 85 ec.clear(); 86 else 87 { 88 const int err = errno; 89 if (err == EACCES && skip_permission_denied) 90 ec.clear(); 91 else 92 ec.assign(err, std::generic_category()); 93 } 94 } 95 96 _Dir_base(_Dir_base&& d) : dirp(std::exchange(d.dirp, nullptr)) { } 97 98 _Dir_base& operator=(_Dir_base&&) = delete; 99 100 ~_Dir_base() { if (dirp) posix::closedir(dirp); } 101 102 const posix::dirent* 103 advance(bool skip_permission_denied, error_code& ec) noexcept 104 { 105 ec.clear(); 106 107 int err = std::exchange(errno, 0); 108 const posix::dirent* entp = posix::readdir(dirp); 109 // std::swap cannot be used with Bionic's errno 110 err = std::exchange(errno, err); 111 112 if (entp) 113 { 114 // skip past dot and dot-dot 115 if (is_dot_or_dotdot(entp->d_name)) 116 return advance(skip_permission_denied, ec); 117 return entp; 118 } 119 else if (err) 120 { 121 if (err == EACCES && skip_permission_denied) 122 return nullptr; 123 ec.assign(err, std::generic_category()); 124 return nullptr; 125 } 126 else 127 { 128 // reached the end 129 return nullptr; 130 } 131 } 132 133 static bool is_dot_or_dotdot(const char* s) noexcept 134 { return !strcmp(s, ".") || !strcmp(s, ".."); } 135 136#if _GLIBCXX_FILESYSTEM_IS_WINDOWS 137 static bool is_dot_or_dotdot(const wchar_t* s) noexcept 138 { return !wcscmp(s, L".") || !wcscmp(s, L".."); } 139#endif 140 141 posix::DIR* dirp; 142}; 143 144inline bool 145is_permission_denied_error(int e) 146{ 147 if (e == EACCES) 148 return true; 149#ifdef __APPLE__ 150 if (e == EPERM) // See PR 99533 151 return true; 152#endif 153 return false; 154} 155 156} // namespace filesystem 157 158// BEGIN/END macros must be defined before including this file. 159_GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM 160 161inline file_type 162get_file_type(const std::filesystem::__gnu_posix::dirent& d [[gnu::unused]]) 163{ 164#ifdef _GLIBCXX_HAVE_STRUCT_DIRENT_D_TYPE 165 switch (d.d_type) 166 { 167 case DT_BLK: 168 return file_type::block; 169 case DT_CHR: 170 return file_type::character; 171 case DT_DIR: 172 return file_type::directory; 173 case DT_FIFO: 174 return file_type::fifo; 175 case DT_LNK: 176 return file_type::symlink; 177 case DT_REG: 178 return file_type::regular; 179 case DT_SOCK: 180 return file_type::socket; 181 case DT_UNKNOWN: 182 return file_type::unknown; 183 default: 184 return file_type::none; 185 } 186#else 187 return file_type::none; 188#endif 189} 190_GLIBCXX_END_NAMESPACE_FILESYSTEM 191 192_GLIBCXX_END_NAMESPACE_VERSION 193} // namespace std 194 195#endif // _GLIBCXX_DIR_COMMON_H 196