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 _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 _LIBCPP_INLINE_VISIBILITY 244 bool operator>=(directory_entry const& __rhs) const noexcept { 245 return __p_ >= __rhs.__p_; 246 } 247 248 template <class _CharT, class _Traits> 249 _LIBCPP_INLINE_VISIBILITY 250 friend basic_ostream<_CharT, _Traits>& operator<<(basic_ostream<_CharT, _Traits>& __os, const directory_entry& __d) { 251 return __os << __d.path(); 252 } 253 254 private: 255 friend class directory_iterator; 256 friend class recursive_directory_iterator; 257 friend class _LIBCPP_HIDDEN __dir_stream; 258 259 enum _CacheType : unsigned char { 260 _Empty, 261 _IterSymlink, 262 _IterNonSymlink, 263 _RefreshSymlink, 264 _RefreshSymlinkUnresolved, 265 _RefreshNonSymlink 266 }; 267 268 struct __cached_data { 269 uintmax_t __size_; 270 uintmax_t __nlink_; 271 file_time_type __write_time_; 272 perms __sym_perms_; 273 perms __non_sym_perms_; 274 file_type __type_; 275 _CacheType __cache_type_; 276 277 _LIBCPP_INLINE_VISIBILITY 278 __cached_data() noexcept { __reset(); } 279 280 _LIBCPP_INLINE_VISIBILITY 281 void __reset() { 282 __cache_type_ = _Empty; 283 __type_ = file_type::none; 284 __sym_perms_ = __non_sym_perms_ = perms::unknown; 285 __size_ = __nlink_ = uintmax_t(-1); 286 __write_time_ = file_time_type::min(); 287 } 288 }; 289 290 _LIBCPP_INLINE_VISIBILITY 291 static __cached_data __create_iter_result(file_type __ft) { 292 __cached_data __data; 293 __data.__type_ = __ft; 294 __data.__cache_type_ = [&]() { 295 switch (__ft) { 296 case file_type::none: 297 return _Empty; 298 case file_type::symlink: 299 return _IterSymlink; 300 default: 301 return _IterNonSymlink; 302 } 303 }(); 304 return __data; 305 } 306 307 _LIBCPP_INLINE_VISIBILITY 308 void __assign_iter_entry(_Path&& __p, __cached_data __dt) { 309 __p_ = _VSTD::move(__p); 310 __data_ = __dt; 311 } 312 313 _LIBCPP_FUNC_VIS 314 error_code __do_refresh() noexcept; 315 316 _LIBCPP_INLINE_VISIBILITY 317 static bool __is_dne_error(error_code const& __ec) { 318 if (!__ec) 319 return true; 320 switch (static_cast<errc>(__ec.value())) { 321 case errc::no_such_file_or_directory: 322 case errc::not_a_directory: 323 return true; 324 default: 325 return false; 326 } 327 } 328 329 _LIBCPP_INLINE_VISIBILITY 330 void __handle_error(const char* __msg, error_code* __dest_ec, 331 error_code const& __ec, bool __allow_dne = false) const { 332 if (__dest_ec) { 333 *__dest_ec = __ec; 334 return; 335 } 336 if (__ec && (!__allow_dne || !__is_dne_error(__ec))) 337 __throw_filesystem_error(__msg, __p_, __ec); 338 } 339 340 _LIBCPP_INLINE_VISIBILITY 341 void __refresh(error_code* __ec = nullptr) { 342 __handle_error("in directory_entry::refresh", __ec, __do_refresh(), 343 /*allow_dne*/ true); 344 } 345 346 _LIBCPP_INLINE_VISIBILITY 347 file_type __get_sym_ft(error_code* __ec = nullptr) const { 348 switch (__data_.__cache_type_) { 349 case _Empty: 350 return __symlink_status(__p_, __ec).type(); 351 case _IterSymlink: 352 case _RefreshSymlink: 353 case _RefreshSymlinkUnresolved: 354 if (__ec) 355 __ec->clear(); 356 return file_type::symlink; 357 case _IterNonSymlink: 358 case _RefreshNonSymlink: 359 file_status __st(__data_.__type_); 360 if (__ec && !_VSTD_FS::exists(__st)) 361 *__ec = make_error_code(errc::no_such_file_or_directory); 362 else if (__ec) 363 __ec->clear(); 364 return __data_.__type_; 365 } 366 __libcpp_unreachable(); 367 } 368 369 _LIBCPP_INLINE_VISIBILITY 370 file_type __get_ft(error_code* __ec = nullptr) const { 371 switch (__data_.__cache_type_) { 372 case _Empty: 373 case _IterSymlink: 374 case _RefreshSymlinkUnresolved: 375 return __status(__p_, __ec).type(); 376 case _IterNonSymlink: 377 case _RefreshNonSymlink: 378 case _RefreshSymlink: { 379 file_status __st(__data_.__type_); 380 if (__ec && !_VSTD_FS::exists(__st)) 381 *__ec = make_error_code(errc::no_such_file_or_directory); 382 else if (__ec) 383 __ec->clear(); 384 return __data_.__type_; 385 } 386 } 387 __libcpp_unreachable(); 388 } 389 390 _LIBCPP_INLINE_VISIBILITY 391 file_status __get_status(error_code* __ec = nullptr) const { 392 switch (__data_.__cache_type_) { 393 case _Empty: 394 case _IterNonSymlink: 395 case _IterSymlink: 396 case _RefreshSymlinkUnresolved: 397 return __status(__p_, __ec); 398 case _RefreshNonSymlink: 399 case _RefreshSymlink: 400 return file_status(__get_ft(__ec), __data_.__non_sym_perms_); 401 } 402 __libcpp_unreachable(); 403 } 404 405 _LIBCPP_INLINE_VISIBILITY 406 file_status __get_symlink_status(error_code* __ec = nullptr) const { 407 switch (__data_.__cache_type_) { 408 case _Empty: 409 case _IterNonSymlink: 410 case _IterSymlink: 411 return __symlink_status(__p_, __ec); 412 case _RefreshNonSymlink: 413 return file_status(__get_sym_ft(__ec), __data_.__non_sym_perms_); 414 case _RefreshSymlink: 415 case _RefreshSymlinkUnresolved: 416 return file_status(__get_sym_ft(__ec), __data_.__sym_perms_); 417 } 418 __libcpp_unreachable(); 419 } 420 421 _LIBCPP_INLINE_VISIBILITY 422 uintmax_t __get_size(error_code* __ec = nullptr) const { 423 switch (__data_.__cache_type_) { 424 case _Empty: 425 case _IterNonSymlink: 426 case _IterSymlink: 427 case _RefreshSymlinkUnresolved: 428 return _VSTD_FS::__file_size(__p_, __ec); 429 case _RefreshSymlink: 430 case _RefreshNonSymlink: { 431 error_code __m_ec; 432 file_status __st(__get_ft(&__m_ec)); 433 __handle_error("in directory_entry::file_size", __ec, __m_ec); 434 if (_VSTD_FS::exists(__st) && !_VSTD_FS::is_regular_file(__st)) { 435 errc __err_kind = _VSTD_FS::is_directory(__st) ? errc::is_a_directory 436 : errc::not_supported; 437 __handle_error("in directory_entry::file_size", __ec, 438 make_error_code(__err_kind)); 439 } 440 return __data_.__size_; 441 } 442 } 443 __libcpp_unreachable(); 444 } 445 446 _LIBCPP_INLINE_VISIBILITY 447 uintmax_t __get_nlink(error_code* __ec = nullptr) const { 448 switch (__data_.__cache_type_) { 449 case _Empty: 450 case _IterNonSymlink: 451 case _IterSymlink: 452 case _RefreshSymlinkUnresolved: 453 return _VSTD_FS::__hard_link_count(__p_, __ec); 454 case _RefreshSymlink: 455 case _RefreshNonSymlink: { 456 error_code __m_ec; 457 (void)__get_ft(&__m_ec); 458 __handle_error("in directory_entry::hard_link_count", __ec, __m_ec); 459 return __data_.__nlink_; 460 } 461 } 462 __libcpp_unreachable(); 463 } 464 465 _LIBCPP_INLINE_VISIBILITY 466 file_time_type __get_write_time(error_code* __ec = nullptr) const { 467 switch (__data_.__cache_type_) { 468 case _Empty: 469 case _IterNonSymlink: 470 case _IterSymlink: 471 case _RefreshSymlinkUnresolved: 472 return _VSTD_FS::__last_write_time(__p_, __ec); 473 case _RefreshSymlink: 474 case _RefreshNonSymlink: { 475 error_code __m_ec; 476 file_status __st(__get_ft(&__m_ec)); 477 __handle_error("in directory_entry::last_write_time", __ec, __m_ec); 478 if (_VSTD_FS::exists(__st) && 479 __data_.__write_time_ == file_time_type::min()) 480 __handle_error("in directory_entry::last_write_time", __ec, 481 make_error_code(errc::value_too_large)); 482 return __data_.__write_time_; 483 } 484 } 485 __libcpp_unreachable(); 486 } 487 488 private: 489 _Path __p_; 490 __cached_data __data_; 491 }; 492 493 class __dir_element_proxy { 494 public: 495 inline _LIBCPP_INLINE_VISIBILITY directory_entry operator*() { 496 return _VSTD::move(__elem_); 497 } 498 499 private: 500 friend class directory_iterator; 501 friend class recursive_directory_iterator; 502 explicit __dir_element_proxy(directory_entry const& __e) : __elem_(__e) {} 503 __dir_element_proxy(__dir_element_proxy&& __o) 504 : __elem_(_VSTD::move(__o.__elem_)) {} 505 directory_entry __elem_; 506 }; 507 508 _LIBCPP_AVAILABILITY_FILESYSTEM_POP 509 510 _LIBCPP_END_NAMESPACE_FILESYSTEM 511 512 #endif // _LIBCPP_CXX03_LANG 513 514 _LIBCPP_POP_MACROS 515 516 #endif // _LIBCPP___FILESYSTEM_DIRECTORY_ENTRY_H 517