1 // Class filesystem::directory_entry etc. -*- C++ -*- 2 3 // Copyright (C) 2014-2018 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_USE_CXX11_ABI 26 # define _GLIBCXX_USE_CXX11_ABI 1 27 #endif 28 29 #include <bits/largefile-config.h> 30 #include <experimental/filesystem> 31 #include <utility> 32 #include <stack> 33 #include <string.h> 34 #include <errno.h> 35 #define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM \ 36 namespace experimental { namespace filesystem { 37 #define _GLIBCXX_END_NAMESPACE_FILESYSTEM } } 38 #include "dir-common.h" 39 40 namespace fs = std::experimental::filesystem; 41 42 struct fs::_Dir : std::filesystem::_Dir_base 43 { 44 _Dir(const fs::path& p, bool skip_permission_denied, error_code& ec) 45 : _Dir_base(p.c_str(), skip_permission_denied, ec) 46 { 47 if (!ec) 48 path = p; 49 } 50 51 _Dir(DIR* dirp, const path& p) : _Dir_base(dirp), path(p) { } 52 53 _Dir(_Dir&&) = default; 54 55 // Returns false when the end of the directory entries is reached. 56 // Reports errors by setting ec. 57 bool advance(bool skip_permission_denied, error_code& ec) noexcept 58 { 59 if (const auto entp = _Dir_base::advance(skip_permission_denied, ec)) 60 { 61 entry = fs::directory_entry{path / entp->d_name}; 62 type = get_file_type(*entp); 63 return true; 64 } 65 else if (!ec) 66 { 67 // reached the end 68 entry = {}; 69 type = file_type::none; 70 } 71 return false; 72 } 73 74 bool advance(error_code& ec) noexcept { return advance(false, ec); } 75 76 // Returns false when the end of the directory entries is reached. 77 // Reports errors by throwing. 78 bool advance(bool skip_permission_denied = false) 79 { 80 error_code ec; 81 const bool ok = advance(skip_permission_denied, ec); 82 if (ec) 83 _GLIBCXX_THROW_OR_ABORT(filesystem_error( 84 "directory iterator cannot advance", ec)); 85 return ok; 86 } 87 88 bool should_recurse(bool follow_symlink, error_code& ec) const 89 { 90 file_type type = this->type; 91 if (type == file_type::none || type == file_type::unknown) 92 { 93 type = entry.symlink_status(ec).type(); 94 if (ec) 95 return false; 96 } 97 98 if (type == file_type::directory) 99 return true; 100 if (type == file_type::symlink) 101 return follow_symlink && is_directory(entry.status(ec)); 102 return false; 103 } 104 105 fs::path path; 106 directory_entry entry; 107 file_type type = file_type::none; 108 }; 109 110 namespace 111 { 112 template<typename Bitmask> 113 inline bool 114 is_set(Bitmask obj, Bitmask bits) 115 { 116 return (obj & bits) != Bitmask::none; 117 } 118 } 119 120 fs::directory_iterator:: 121 directory_iterator(const path& p, directory_options options, error_code* ecptr) 122 { 123 const bool skip_permission_denied 124 = is_set(options, directory_options::skip_permission_denied); 125 126 error_code ec; 127 _Dir dir(p, skip_permission_denied, ec); 128 129 if (dir.dirp) 130 { 131 auto sp = std::make_shared<fs::_Dir>(std::move(dir)); 132 if (sp->advance(skip_permission_denied, ec)) 133 _M_dir.swap(sp); 134 } 135 if (ecptr) 136 *ecptr = ec; 137 else if (ec) 138 _GLIBCXX_THROW_OR_ABORT(fs::filesystem_error( 139 "directory iterator cannot open directory", p, ec)); 140 } 141 142 const fs::directory_entry& 143 fs::directory_iterator::operator*() const 144 { 145 if (!_M_dir) 146 _GLIBCXX_THROW_OR_ABORT(filesystem_error( 147 "non-dereferenceable directory iterator", 148 std::make_error_code(errc::invalid_argument))); 149 return _M_dir->entry; 150 } 151 152 fs::directory_iterator& 153 fs::directory_iterator::operator++() 154 { 155 if (!_M_dir) 156 _GLIBCXX_THROW_OR_ABORT(filesystem_error( 157 "cannot advance non-dereferenceable directory iterator", 158 std::make_error_code(errc::invalid_argument))); 159 if (!_M_dir->advance()) 160 _M_dir.reset(); 161 return *this; 162 } 163 164 fs::directory_iterator& 165 fs::directory_iterator::increment(error_code& ec) noexcept 166 { 167 if (!_M_dir) 168 { 169 ec = std::make_error_code(errc::invalid_argument); 170 return *this; 171 } 172 if (!_M_dir->advance(ec)) 173 _M_dir.reset(); 174 return *this; 175 } 176 177 struct fs::recursive_directory_iterator::_Dir_stack : std::stack<_Dir> 178 { 179 void clear() { c.clear(); } 180 }; 181 182 fs::recursive_directory_iterator:: 183 recursive_directory_iterator(const path& p, directory_options options, 184 error_code* ec) 185 : _M_options(options), _M_pending(true) 186 { 187 if (ec) 188 ec->clear(); 189 if (DIR* dirp = ::opendir(p.c_str())) 190 { 191 auto sp = std::make_shared<_Dir_stack>(); 192 sp->push(_Dir{ dirp, p }); 193 if (sp->top().advance(ec)) 194 _M_dirs.swap(sp); 195 } 196 else 197 { 198 const int err = errno; 199 if (err == EACCES 200 && is_set(options, fs::directory_options::skip_permission_denied)) 201 return; 202 203 if (!ec) 204 _GLIBCXX_THROW_OR_ABORT(filesystem_error( 205 "recursive directory iterator cannot open directory", p, 206 std::error_code(err, std::generic_category()))); 207 208 ec->assign(err, std::generic_category()); 209 } 210 } 211 212 fs::recursive_directory_iterator::~recursive_directory_iterator() = default; 213 214 int 215 fs::recursive_directory_iterator::depth() const 216 { 217 return int(_M_dirs->size()) - 1; 218 } 219 220 const fs::directory_entry& 221 fs::recursive_directory_iterator::operator*() const 222 { 223 return _M_dirs->top().entry; 224 } 225 226 fs::recursive_directory_iterator& 227 fs::recursive_directory_iterator:: 228 operator=(const recursive_directory_iterator& other) noexcept = default; 229 230 fs::recursive_directory_iterator& 231 fs::recursive_directory_iterator:: 232 operator=(recursive_directory_iterator&& other) noexcept = default; 233 234 fs::recursive_directory_iterator& 235 fs::recursive_directory_iterator::operator++() 236 { 237 error_code ec; 238 increment(ec); 239 if (ec.value()) 240 _GLIBCXX_THROW_OR_ABORT(filesystem_error( 241 "cannot increment recursive directory iterator", ec)); 242 return *this; 243 } 244 245 fs::recursive_directory_iterator& 246 fs::recursive_directory_iterator::increment(error_code& ec) noexcept 247 { 248 if (!_M_dirs) 249 { 250 ec = std::make_error_code(errc::invalid_argument); 251 return *this; 252 } 253 254 const bool follow 255 = is_set(_M_options, directory_options::follow_directory_symlink); 256 const bool skip_permission_denied 257 = is_set(_M_options, directory_options::skip_permission_denied); 258 259 auto& top = _M_dirs->top(); 260 261 if (std::exchange(_M_pending, true) && top.should_recurse(follow, ec)) 262 { 263 _Dir dir(top.entry.path(), skip_permission_denied, ec); 264 if (ec) 265 { 266 _M_dirs.reset(); 267 return *this; 268 } 269 if (dir.dirp) 270 _M_dirs->push(std::move(dir)); 271 } 272 273 while (!_M_dirs->top().advance(skip_permission_denied, ec) && !ec) 274 { 275 _M_dirs->pop(); 276 if (_M_dirs->empty()) 277 { 278 _M_dirs.reset(); 279 return *this; 280 } 281 } 282 return *this; 283 } 284 285 void 286 fs::recursive_directory_iterator::pop(error_code& ec) 287 { 288 if (!_M_dirs) 289 { 290 ec = std::make_error_code(errc::invalid_argument); 291 return; 292 } 293 294 const bool skip_permission_denied 295 = is_set(_M_options, directory_options::skip_permission_denied); 296 297 do { 298 _M_dirs->pop(); 299 if (_M_dirs->empty()) 300 { 301 _M_dirs.reset(); 302 ec.clear(); 303 return; 304 } 305 } while (!_M_dirs->top().advance(skip_permission_denied, ec)); 306 } 307 308 void 309 fs::recursive_directory_iterator::pop() 310 { 311 error_code ec; 312 pop(ec); 313 if (ec) 314 _GLIBCXX_THROW_OR_ABORT(filesystem_error(_M_dirs 315 ? "recursive directory iterator cannot pop" 316 : "non-dereferenceable recursive directory iterator cannot pop", 317 ec)); 318 } 319