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