xref: /freebsd/contrib/llvm-project/libcxx/include/__filesystem/directory_entry.h (revision 8ddb146abcdf061be9f2c0db7e391697dafad85c)
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