1 // -*- C++ -*- 2 //===----------------------------------------------------------------------===// 3 // 4 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 5 // See https://llvm.org/LICENSE.txt for license information. 6 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 7 // 8 //===----------------------------------------------------------------------===// 9 10 #ifndef _LIBCPP___FILESYSTEM_DIRECTORY_ENTRY_H 11 #define _LIBCPP___FILESYSTEM_DIRECTORY_ENTRY_H 12 13 #include <__availability> 14 #include <__config> 15 #include <__errc> 16 #include <__filesystem/file_status.h> 17 #include <__filesystem/file_time_type.h> 18 #include <__filesystem/file_type.h> 19 #include <__filesystem/filesystem_error.h> 20 #include <__filesystem/operations.h> 21 #include <__filesystem/path.h> 22 #include <__filesystem/perms.h> 23 #include <chrono> 24 #include <cstdint> 25 #include <cstdlib> 26 #include <iosfwd> 27 #include <system_error> 28 29 _LIBCPP_PUSH_MACROS 30 #include <__undef_macros> 31 32 #ifndef _LIBCPP_CXX03_LANG 33 34 _LIBCPP_BEGIN_NAMESPACE_FILESYSTEM 35 36 _LIBCPP_AVAILABILITY_FILESYSTEM_PUSH 37 38 39 class directory_entry { 40 typedef _VSTD_FS::path _Path; 41 42 public: 43 // constructors and destructors 44 directory_entry() noexcept = default; 45 directory_entry(directory_entry const&) = default; 46 directory_entry(directory_entry&&) noexcept = default; 47 48 _LIBCPP_INLINE_VISIBILITY 49 explicit directory_entry(_Path const& __p) : __p_(__p) { 50 error_code __ec; 51 __refresh(&__ec); 52 } 53 54 _LIBCPP_INLINE_VISIBILITY 55 directory_entry(_Path const& __p, error_code& __ec) : __p_(__p) { 56 __refresh(&__ec); 57 } 58 59 ~directory_entry() {} 60 61 directory_entry& operator=(directory_entry const&) = default; 62 directory_entry& operator=(directory_entry&&) noexcept = default; 63 64 _LIBCPP_INLINE_VISIBILITY 65 void assign(_Path const& __p) { 66 __p_ = __p; 67 error_code __ec; 68 __refresh(&__ec); 69 } 70 71 _LIBCPP_INLINE_VISIBILITY 72 void assign(_Path const& __p, error_code& __ec) { 73 __p_ = __p; 74 __refresh(&__ec); 75 } 76 77 _LIBCPP_INLINE_VISIBILITY 78 void replace_filename(_Path const& __p) { 79 __p_.replace_filename(__p); 80 error_code __ec; 81 __refresh(&__ec); 82 } 83 84 _LIBCPP_INLINE_VISIBILITY 85 void replace_filename(_Path const& __p, error_code& __ec) { 86 __p_ = __p_.parent_path() / __p; 87 __refresh(&__ec); 88 } 89 90 _LIBCPP_INLINE_VISIBILITY 91 void refresh() { __refresh(); } 92 93 _LIBCPP_INLINE_VISIBILITY 94 void refresh(error_code& __ec) noexcept { __refresh(&__ec); } 95 96 _LIBCPP_INLINE_VISIBILITY 97 _Path const& path() const noexcept { return __p_; } 98 99 _LIBCPP_INLINE_VISIBILITY 100 operator const _Path&() const noexcept { return __p_; } 101 102 _LIBCPP_INLINE_VISIBILITY 103 bool exists() const { return _VSTD_FS::exists(file_status{__get_ft()}); } 104 105 _LIBCPP_INLINE_VISIBILITY 106 bool exists(error_code& __ec) const noexcept { 107 return _VSTD_FS::exists(file_status{__get_ft(&__ec)}); 108 } 109 110 _LIBCPP_INLINE_VISIBILITY 111 bool is_block_file() const { return __get_ft() == file_type::block; } 112 113 _LIBCPP_INLINE_VISIBILITY 114 bool is_block_file(error_code& __ec) const noexcept { 115 return __get_ft(&__ec) == file_type::block; 116 } 117 118 _LIBCPP_INLINE_VISIBILITY 119 bool is_character_file() const { return __get_ft() == file_type::character; } 120 121 _LIBCPP_INLINE_VISIBILITY 122 bool is_character_file(error_code& __ec) const noexcept { 123 return __get_ft(&__ec) == file_type::character; 124 } 125 126 _LIBCPP_INLINE_VISIBILITY 127 bool is_directory() const { return __get_ft() == file_type::directory; } 128 129 _LIBCPP_INLINE_VISIBILITY 130 bool is_directory(error_code& __ec) const noexcept { 131 return __get_ft(&__ec) == file_type::directory; 132 } 133 134 _LIBCPP_INLINE_VISIBILITY 135 bool is_fifo() const { return __get_ft() == file_type::fifo; } 136 137 _LIBCPP_INLINE_VISIBILITY 138 bool is_fifo(error_code& __ec) const noexcept { 139 return __get_ft(&__ec) == file_type::fifo; 140 } 141 142 _LIBCPP_INLINE_VISIBILITY 143 bool is_other() const { return _VSTD_FS::is_other(file_status{__get_ft()}); } 144 145 _LIBCPP_INLINE_VISIBILITY 146 bool is_other(error_code& __ec) const noexcept { 147 return _VSTD_FS::is_other(file_status{__get_ft(&__ec)}); 148 } 149 150 _LIBCPP_INLINE_VISIBILITY 151 bool is_regular_file() const { return __get_ft() == file_type::regular; } 152 153 _LIBCPP_INLINE_VISIBILITY 154 bool is_regular_file(error_code& __ec) const noexcept { 155 return __get_ft(&__ec) == file_type::regular; 156 } 157 158 _LIBCPP_INLINE_VISIBILITY 159 bool is_socket() const { return __get_ft() == file_type::socket; } 160 161 _LIBCPP_INLINE_VISIBILITY 162 bool is_socket(error_code& __ec) const noexcept { 163 return __get_ft(&__ec) == file_type::socket; 164 } 165 166 _LIBCPP_INLINE_VISIBILITY 167 bool is_symlink() const { return __get_sym_ft() == file_type::symlink; } 168 169 _LIBCPP_INLINE_VISIBILITY 170 bool is_symlink(error_code& __ec) const noexcept { 171 return __get_sym_ft(&__ec) == file_type::symlink; 172 } 173 _LIBCPP_INLINE_VISIBILITY 174 uintmax_t file_size() const { return __get_size(); } 175 176 _LIBCPP_INLINE_VISIBILITY 177 uintmax_t file_size(error_code& __ec) const noexcept { 178 return __get_size(&__ec); 179 } 180 181 _LIBCPP_INLINE_VISIBILITY 182 uintmax_t hard_link_count() const { return __get_nlink(); } 183 184 _LIBCPP_INLINE_VISIBILITY 185 uintmax_t hard_link_count(error_code& __ec) const noexcept { 186 return __get_nlink(&__ec); 187 } 188 189 _LIBCPP_INLINE_VISIBILITY 190 file_time_type last_write_time() const { return __get_write_time(); } 191 192 _LIBCPP_INLINE_VISIBILITY 193 file_time_type last_write_time(error_code& __ec) const noexcept { 194 return __get_write_time(&__ec); 195 } 196 197 _LIBCPP_INLINE_VISIBILITY 198 file_status status() const { return __get_status(); } 199 200 _LIBCPP_INLINE_VISIBILITY 201 file_status status(error_code& __ec) const noexcept { 202 return __get_status(&__ec); 203 } 204 205 _LIBCPP_INLINE_VISIBILITY 206 file_status symlink_status() const { return __get_symlink_status(); } 207 208 _LIBCPP_INLINE_VISIBILITY 209 file_status symlink_status(error_code& __ec) const noexcept { 210 return __get_symlink_status(&__ec); 211 } 212 213 _LIBCPP_INLINE_VISIBILITY 214 bool operator<(directory_entry const& __rhs) const noexcept { 215 return __p_ < __rhs.__p_; 216 } 217 218 _LIBCPP_INLINE_VISIBILITY 219 bool operator==(directory_entry const& __rhs) const noexcept { 220 return __p_ == __rhs.__p_; 221 } 222 223 _LIBCPP_INLINE_VISIBILITY 224 bool operator!=(directory_entry const& __rhs) const noexcept { 225 return __p_ != __rhs.__p_; 226 } 227 228 _LIBCPP_INLINE_VISIBILITY 229 bool operator<=(directory_entry const& __rhs) const noexcept { 230 return __p_ <= __rhs.__p_; 231 } 232 233 _LIBCPP_INLINE_VISIBILITY 234 bool operator>(directory_entry const& __rhs) const noexcept { 235 return __p_ > __rhs.__p_; 236 } 237 238 _LIBCPP_INLINE_VISIBILITY 239 bool operator>=(directory_entry const& __rhs) const noexcept { 240 return __p_ >= __rhs.__p_; 241 } 242 243 template <class _CharT, class _Traits> 244 _LIBCPP_INLINE_VISIBILITY 245 friend basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const directory_entry& __d) { 246 return __os << __d.path(); 247 } 248 249 private: 250 friend class directory_iterator; 251 friend class recursive_directory_iterator; 252 friend class _LIBCPP_HIDDEN __dir_stream; 253 254 enum _CacheType : unsigned char { 255 _Empty, 256 _IterSymlink, 257 _IterNonSymlink, 258 _RefreshSymlink, 259 _RefreshSymlinkUnresolved, 260 _RefreshNonSymlink 261 }; 262 263 struct __cached_data { 264 uintmax_t __size_; 265 uintmax_t __nlink_; 266 file_time_type __write_time_; 267 perms __sym_perms_; 268 perms __non_sym_perms_; 269 file_type __type_; 270 _CacheType __cache_type_; 271 272 _LIBCPP_INLINE_VISIBILITY 273 __cached_data() noexcept { __reset(); } 274 275 _LIBCPP_INLINE_VISIBILITY 276 void __reset() { 277 __cache_type_ = _Empty; 278 __type_ = file_type::none; 279 __sym_perms_ = __non_sym_perms_ = perms::unknown; 280 __size_ = __nlink_ = uintmax_t(-1); 281 __write_time_ = file_time_type::min(); 282 } 283 }; 284 285 _LIBCPP_INLINE_VISIBILITY 286 static __cached_data __create_iter_result(file_type __ft) { 287 __cached_data __data; 288 __data.__type_ = __ft; 289 __data.__cache_type_ = [&]() { 290 switch (__ft) { 291 case file_type::none: 292 return _Empty; 293 case file_type::symlink: 294 return _IterSymlink; 295 default: 296 return _IterNonSymlink; 297 } 298 }(); 299 return __data; 300 } 301 302 _LIBCPP_INLINE_VISIBILITY 303 void __assign_iter_entry(_Path&& __p, __cached_data __dt) { 304 __p_ = _VSTD::move(__p); 305 __data_ = __dt; 306 } 307 308 _LIBCPP_FUNC_VIS 309 error_code __do_refresh() noexcept; 310 311 _LIBCPP_INLINE_VISIBILITY 312 static bool __is_dne_error(error_code const& __ec) { 313 if (!__ec) 314 return true; 315 switch (static_cast<errc>(__ec.value())) { 316 case errc::no_such_file_or_directory: 317 case errc::not_a_directory: 318 return true; 319 default: 320 return false; 321 } 322 } 323 324 _LIBCPP_INLINE_VISIBILITY 325 void __handle_error(const char* __msg, error_code* __dest_ec, 326 error_code const& __ec, bool __allow_dne = false) const { 327 if (__dest_ec) { 328 *__dest_ec = __ec; 329 return; 330 } 331 if (__ec && (!__allow_dne || !__is_dne_error(__ec))) 332 __throw_filesystem_error(__msg, __p_, __ec); 333 } 334 335 _LIBCPP_INLINE_VISIBILITY 336 void __refresh(error_code* __ec = nullptr) { 337 __handle_error("in directory_entry::refresh", __ec, __do_refresh(), 338 /*allow_dne*/ true); 339 } 340 341 _LIBCPP_INLINE_VISIBILITY 342 file_type __get_sym_ft(error_code* __ec = nullptr) const { 343 switch (__data_.__cache_type_) { 344 case _Empty: 345 return __symlink_status(__p_, __ec).type(); 346 case _IterSymlink: 347 case _RefreshSymlink: 348 case _RefreshSymlinkUnresolved: 349 if (__ec) 350 __ec->clear(); 351 return file_type::symlink; 352 case _IterNonSymlink: 353 case _RefreshNonSymlink: 354 file_status __st(__data_.__type_); 355 if (__ec && !_VSTD_FS::exists(__st)) 356 *__ec = make_error_code(errc::no_such_file_or_directory); 357 else if (__ec) 358 __ec->clear(); 359 return __data_.__type_; 360 } 361 _LIBCPP_UNREACHABLE(); 362 } 363 364 _LIBCPP_INLINE_VISIBILITY 365 file_type __get_ft(error_code* __ec = nullptr) const { 366 switch (__data_.__cache_type_) { 367 case _Empty: 368 case _IterSymlink: 369 case _RefreshSymlinkUnresolved: 370 return __status(__p_, __ec).type(); 371 case _IterNonSymlink: 372 case _RefreshNonSymlink: 373 case _RefreshSymlink: { 374 file_status __st(__data_.__type_); 375 if (__ec && !_VSTD_FS::exists(__st)) 376 *__ec = make_error_code(errc::no_such_file_or_directory); 377 else if (__ec) 378 __ec->clear(); 379 return __data_.__type_; 380 } 381 } 382 _LIBCPP_UNREACHABLE(); 383 } 384 385 _LIBCPP_INLINE_VISIBILITY 386 file_status __get_status(error_code* __ec = nullptr) const { 387 switch (__data_.__cache_type_) { 388 case _Empty: 389 case _IterNonSymlink: 390 case _IterSymlink: 391 case _RefreshSymlinkUnresolved: 392 return __status(__p_, __ec); 393 case _RefreshNonSymlink: 394 case _RefreshSymlink: 395 return file_status(__get_ft(__ec), __data_.__non_sym_perms_); 396 } 397 _LIBCPP_UNREACHABLE(); 398 } 399 400 _LIBCPP_INLINE_VISIBILITY 401 file_status __get_symlink_status(error_code* __ec = nullptr) const { 402 switch (__data_.__cache_type_) { 403 case _Empty: 404 case _IterNonSymlink: 405 case _IterSymlink: 406 return __symlink_status(__p_, __ec); 407 case _RefreshNonSymlink: 408 return file_status(__get_sym_ft(__ec), __data_.__non_sym_perms_); 409 case _RefreshSymlink: 410 case _RefreshSymlinkUnresolved: 411 return file_status(__get_sym_ft(__ec), __data_.__sym_perms_); 412 } 413 _LIBCPP_UNREACHABLE(); 414 } 415 416 _LIBCPP_INLINE_VISIBILITY 417 uintmax_t __get_size(error_code* __ec = nullptr) const { 418 switch (__data_.__cache_type_) { 419 case _Empty: 420 case _IterNonSymlink: 421 case _IterSymlink: 422 case _RefreshSymlinkUnresolved: 423 return _VSTD_FS::__file_size(__p_, __ec); 424 case _RefreshSymlink: 425 case _RefreshNonSymlink: { 426 error_code __m_ec; 427 file_status __st(__get_ft(&__m_ec)); 428 __handle_error("in directory_entry::file_size", __ec, __m_ec); 429 if (_VSTD_FS::exists(__st) && !_VSTD_FS::is_regular_file(__st)) { 430 errc __err_kind = _VSTD_FS::is_directory(__st) ? errc::is_a_directory 431 : errc::not_supported; 432 __handle_error("in directory_entry::file_size", __ec, 433 make_error_code(__err_kind)); 434 } 435 return __data_.__size_; 436 } 437 } 438 _LIBCPP_UNREACHABLE(); 439 } 440 441 _LIBCPP_INLINE_VISIBILITY 442 uintmax_t __get_nlink(error_code* __ec = nullptr) const { 443 switch (__data_.__cache_type_) { 444 case _Empty: 445 case _IterNonSymlink: 446 case _IterSymlink: 447 case _RefreshSymlinkUnresolved: 448 return _VSTD_FS::__hard_link_count(__p_, __ec); 449 case _RefreshSymlink: 450 case _RefreshNonSymlink: { 451 error_code __m_ec; 452 (void)__get_ft(&__m_ec); 453 __handle_error("in directory_entry::hard_link_count", __ec, __m_ec); 454 return __data_.__nlink_; 455 } 456 } 457 _LIBCPP_UNREACHABLE(); 458 } 459 460 _LIBCPP_INLINE_VISIBILITY 461 file_time_type __get_write_time(error_code* __ec = nullptr) const { 462 switch (__data_.__cache_type_) { 463 case _Empty: 464 case _IterNonSymlink: 465 case _IterSymlink: 466 case _RefreshSymlinkUnresolved: 467 return _VSTD_FS::__last_write_time(__p_, __ec); 468 case _RefreshSymlink: 469 case _RefreshNonSymlink: { 470 error_code __m_ec; 471 file_status __st(__get_ft(&__m_ec)); 472 __handle_error("in directory_entry::last_write_time", __ec, __m_ec); 473 if (_VSTD_FS::exists(__st) && 474 __data_.__write_time_ == file_time_type::min()) 475 __handle_error("in directory_entry::last_write_time", __ec, 476 make_error_code(errc::value_too_large)); 477 return __data_.__write_time_; 478 } 479 } 480 _LIBCPP_UNREACHABLE(); 481 } 482 483 private: 484 _Path __p_; 485 __cached_data __data_; 486 }; 487 488 class __dir_element_proxy { 489 public: 490 inline _LIBCPP_INLINE_VISIBILITY directory_entry operator*() { 491 return _VSTD::move(__elem_); 492 } 493 494 private: 495 friend class directory_iterator; 496 friend class recursive_directory_iterator; 497 explicit __dir_element_proxy(directory_entry const& __e) : __elem_(__e) {} 498 __dir_element_proxy(__dir_element_proxy&& __o) 499 : __elem_(_VSTD::move(__o.__elem_)) {} 500 directory_entry __elem_; 501 }; 502 503 _LIBCPP_AVAILABILITY_FILESYSTEM_POP 504 505 _LIBCPP_END_NAMESPACE_FILESYSTEM 506 507 #endif // _LIBCPP_CXX03_LANG 508 509 _LIBCPP_POP_MACROS 510 511 #endif // _LIBCPP___FILESYSTEM_DIRECTORY_ENTRY_H 512