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