1 // Class filesystem::directory_entry etc. -*- C++ -*-
3 // Copyright (C) 2014-2022 Free Software Foundation, Inc.
5 // This file is part of the GNU ISO C++ Library. This library is free
6 // software; you can redistribute it and/or modify it under the
7 // terms of the GNU General Public License as published by the
8 // Free Software Foundation; either version 3, or (at your option)
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // Under Section 7 of GPL version 3, you are granted additional
17 // permissions described in the GCC Runtime Library Exception, version
18 // 3.1, as published by the Free Software Foundation.
20 // You should have received a copy of the GNU General Public License and
21 // a copy of the GCC Runtime Library Exception along with this program;
22 // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23 // <http://www.gnu.org/licenses/>.
25 #ifndef _GLIBCXX_USE_CXX11_ABI
26 # define _GLIBCXX_USE_CXX11_ABI 1
29 #include <bits/largefile-config.h>
35 #define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM namespace filesystem {
36 #define _GLIBCXX_END_NAMESPACE_FILESYSTEM }
37 #include "../filesystem/dir-common.h"
39 namespace fs
= std::filesystem
;
40 namespace posix
= std::filesystem::__gnu_posix
;
42 template class std::__shared_ptr
<fs::_Dir
>;
43 template class std::__shared_ptr
<fs::recursive_directory_iterator::_Dir_stack
>;
45 struct fs::_Dir
: _Dir_base
47 _Dir(const fs::path
& p
, bool skip_permission_denied
, error_code
& ec
)
48 : _Dir_base(p
.c_str(), skip_permission_denied
, ec
)
54 _Dir(posix::DIR* dirp
, const path
& p
) : _Dir_base(dirp
), path(p
) { }
56 _Dir(_Dir
&&) = default;
58 // Returns false when the end of the directory entries is reached.
59 // Reports errors by setting ec.
60 bool advance(bool skip_permission_denied
, error_code
& ec
) noexcept
62 if (const auto entp
= _Dir_base::advance(skip_permission_denied
, ec
))
66 file_type type
= file_type::none
;
67 #ifdef _GLIBCXX_HAVE_STRUCT_DIRENT_D_TYPE
68 // Even if the OS supports dirent::d_type the filesystem might not:
69 if (entp
->d_type
!= DT_UNKNOWN
)
70 type
= get_file_type(*entp
);
72 entry
= fs::directory_entry
{std::move(name
), type
};
83 bool advance(error_code
& ec
) noexcept
{ return advance(false, ec
); }
85 // Returns false when the end of the directory entries is reached.
86 // Reports errors by throwing.
87 bool advance(bool skip_permission_denied
= false)
90 const bool ok
= advance(skip_permission_denied
, ec
);
92 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
93 "directory iterator cannot advance", ec
));
97 bool should_recurse(bool follow_symlink
, error_code
& ec
) const
99 file_type type
= entry
._M_type
;
100 if (type
== file_type::none
)
102 type
= entry
.symlink_status(ec
).type();
107 if (type
== file_type::directory
)
109 if (type
== file_type::symlink
)
110 return follow_symlink
&& is_directory(entry
.status(ec
));
115 directory_entry entry
;
120 template<typename Bitmask
>
122 is_set(Bitmask obj
, Bitmask bits
)
124 return (obj
& bits
) != Bitmask::none
;
128 fs::directory_iterator::
129 directory_iterator(const path
& p
, directory_options options
, error_code
* ecptr
)
131 const bool skip_permission_denied
132 = is_set(options
, directory_options::skip_permission_denied
);
135 _Dir
dir(p
, skip_permission_denied
, ec
);
139 auto sp
= std::__make_shared
<fs::_Dir
>(std::move(dir
));
140 if (sp
->advance(skip_permission_denied
, ec
))
146 _GLIBCXX_THROW_OR_ABORT(fs::filesystem_error(
147 "directory iterator cannot open directory", p
, ec
));
150 const fs::directory_entry
&
151 fs::directory_iterator::operator*() const noexcept
153 return _M_dir
->entry
;
156 fs::directory_iterator
&
157 fs::directory_iterator::operator++()
160 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
161 "cannot advance non-dereferenceable directory iterator",
162 std::make_error_code(errc::invalid_argument
)));
163 if (!_M_dir
->advance())
168 fs::directory_iterator
&
169 fs::directory_iterator::increment(error_code
& ec
)
173 ec
= std::make_error_code(errc::invalid_argument
);
176 if (!_M_dir
->advance(ec
))
181 struct fs::recursive_directory_iterator::_Dir_stack
: std::stack
<_Dir
>
183 _Dir_stack(directory_options opts
, posix::DIR* dirp
, const path
& p
)
184 : options(opts
), pending(true)
186 this->emplace(dirp
, p
);
189 const directory_options options
;
192 void clear() { c
.clear(); }
195 fs::recursive_directory_iterator::
196 recursive_directory_iterator(const path
& p
, directory_options options
,
199 if (posix::DIR* dirp
= posix::opendir(p
.c_str()))
203 auto sp
= std::__make_shared
<_Dir_stack
>(options
, dirp
, p
);
204 if (ecptr
? sp
->top().advance(*ecptr
) : sp
->top().advance())
209 const int err
= errno
;
210 if (fs::is_permission_denied_error(err
)
211 && is_set(options
, fs::directory_options::skip_permission_denied
))
219 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
220 "recursive directory iterator cannot open directory", p
,
221 std::error_code(err
, std::generic_category())));
223 ecptr
->assign(err
, std::generic_category());
227 fs::recursive_directory_iterator::~recursive_directory_iterator() = default;
229 fs::directory_options
230 fs::recursive_directory_iterator::options() const noexcept
232 return _M_dirs
->options
;
236 fs::recursive_directory_iterator::depth() const noexcept
238 return int(_M_dirs
->size()) - 1;
242 fs::recursive_directory_iterator::recursion_pending() const noexcept
244 return _M_dirs
->pending
;
247 const fs::directory_entry
&
248 fs::recursive_directory_iterator::operator*() const noexcept
250 return _M_dirs
->top().entry
;
253 fs::recursive_directory_iterator
&
254 fs::recursive_directory_iterator::
255 operator=(const recursive_directory_iterator
& other
) noexcept
= default;
257 fs::recursive_directory_iterator
&
258 fs::recursive_directory_iterator::
259 operator=(recursive_directory_iterator
&& other
) noexcept
= default;
261 fs::recursive_directory_iterator
&
262 fs::recursive_directory_iterator::operator++()
267 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
268 "cannot increment recursive directory iterator", ec
));
272 fs::recursive_directory_iterator
&
273 fs::recursive_directory_iterator::increment(error_code
& ec
)
277 ec
= std::make_error_code(errc::invalid_argument
);
282 = is_set(_M_dirs
->options
, directory_options::follow_directory_symlink
);
283 const bool skip_permission_denied
284 = is_set(_M_dirs
->options
, directory_options::skip_permission_denied
);
286 auto& top
= _M_dirs
->top();
288 if (std::exchange(_M_dirs
->pending
, true) && top
.should_recurse(follow
, ec
))
290 _Dir
dir(top
.entry
.path(), skip_permission_denied
, ec
);
297 _M_dirs
->push(std::move(dir
));
300 while (!_M_dirs
->top().advance(skip_permission_denied
, ec
) && !ec
)
303 if (_M_dirs
->empty())
313 fs::recursive_directory_iterator::pop(error_code
& ec
)
317 ec
= std::make_error_code(errc::invalid_argument
);
321 const bool skip_permission_denied
322 = is_set(_M_dirs
->options
, directory_options::skip_permission_denied
);
326 if (_M_dirs
->empty())
332 } while (!_M_dirs
->top().advance(skip_permission_denied
, ec
));
336 fs::recursive_directory_iterator::pop()
341 _GLIBCXX_THROW_OR_ABORT(filesystem_error(_M_dirs
342 ? "recursive directory iterator cannot pop"
343 : "non-dereferenceable recursive directory iterator cannot pop",
348 fs::recursive_directory_iterator::disable_recursion_pending() noexcept
350 _M_dirs
->pending
= false;