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