1 // Class filesystem::directory_entry etc. -*- C++ -*-
3 // Copyright (C) 2014-2025 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 // Cygwin needs this for secure_getenv
30 # define _GNU_SOURCE 1
33 #include <bits/largefile-config.h>
34 #include <experimental/filesystem>
36 #ifndef _GLIBCXX_HAVE_DIRENT_H
37 # error "the <dirent.h> header is needed to build the Filesystem TS"
44 #define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM \
45 namespace experimental { namespace filesystem {
46 #define _GLIBCXX_END_NAMESPACE_FILESYSTEM } }
47 #include "dir-common.h"
49 namespace fs
= std::experimental::filesystem
;
50 namespace posix
= std::filesystem::__gnu_posix
;
52 struct fs::_Dir
: std::filesystem::_Dir_base
54 _Dir(const fs::path
& p
, bool skip_permission_denied
, bool nofollow
,
56 : _Dir_base(p
.c_str(), skip_permission_denied
, nofollow
, ec
)
62 _Dir(_Dir_base
&& d
, const path
& p
) : _Dir_base(std::move(d
)), path(p
) { }
64 _Dir(_Dir
&&) = default;
66 // Returns false when the end of the directory entries is reached.
67 // Reports errors by setting ec.
68 bool advance(bool skip_permission_denied
, error_code
& ec
) noexcept
70 if (const auto entp
= _Dir_base::advance(skip_permission_denied
, ec
))
72 entry
= fs::directory_entry
{path
/ entp
->d_name
};
73 type
= get_file_type(*entp
);
80 type
= file_type::none
;
85 bool advance(error_code
& ec
) noexcept
{ return advance(false, ec
); }
87 // Returns false when the end of the directory entries is reached.
88 // Reports errors by throwing.
89 bool advance(bool skip_permission_denied
= false)
92 const bool ok
= advance(skip_permission_denied
, ec
);
94 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
95 "directory iterator cannot advance", ec
));
99 bool should_recurse(bool follow_symlink
, error_code
& ec
) const
101 file_type type
= this->type
;
102 if (type
== file_type::none
|| type
== file_type::unknown
)
104 type
= entry
.symlink_status(ec
).type();
109 if (type
== file_type::directory
)
111 if (type
== file_type::symlink
)
112 return follow_symlink
&& is_directory(entry
.status(ec
));
116 // Return a pathname for the current directory entry, as an _At_path.
118 current() const noexcept
120 const fs::path
& p
= entry
.path();
121 #if _GLIBCXX_HAVE_DIRFD
122 auto len
= std::prev(p
.end())->native().size();
123 return {::dirfd(this->dirp
), p
.c_str(), p
.native().size() - len
};
129 // Create a new _Dir for the directory this->entry.path().
131 open_subdir(bool skip_permission_denied
, bool nofollow
,
132 error_code
& ec
) noexcept
134 _Dir_base
d(current(), skip_permission_denied
, nofollow
, ec
);
135 return _Dir(std::move(d
), entry
.path());
139 directory_entry entry
;
140 file_type type
= file_type::none
;
145 template<typename Bitmask
>
147 is_set(Bitmask obj
, Bitmask bits
)
149 return (obj
& bits
) != Bitmask::none
;
153 fs::directory_iterator::
154 directory_iterator(const path
& p
, directory_options options
, error_code
* ecptr
)
156 // Do not report an error for permission denied errors.
157 const bool skip_permission_denied
158 = is_set(options
, directory_options::skip_permission_denied
);
161 _Dir
dir(p
, skip_permission_denied
, /*nofollow*/false, ec
);
165 auto sp
= std::make_shared
<fs::_Dir
>(std::move(dir
));
166 if (sp
->advance(skip_permission_denied
, ec
))
172 _GLIBCXX_THROW_OR_ABORT(fs::filesystem_error(
173 "directory iterator cannot open directory", p
, ec
));
176 const fs::directory_entry
&
177 fs::directory_iterator::operator*() const
180 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
181 "non-dereferenceable directory iterator",
182 std::make_error_code(errc::invalid_argument
)));
183 return _M_dir
->entry
;
186 fs::directory_iterator
&
187 fs::directory_iterator::operator++()
190 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
191 "cannot advance non-dereferenceable directory iterator",
192 std::make_error_code(errc::invalid_argument
)));
193 if (!_M_dir
->advance())
198 fs::directory_iterator
&
199 fs::directory_iterator::increment(error_code
& ec
) noexcept
203 ec
= std::make_error_code(errc::invalid_argument
);
206 if (!_M_dir
->advance(ec
))
211 struct fs::recursive_directory_iterator::_Dir_stack
: std::stack
<_Dir
>
213 _Dir_stack(_Dir
&& dir
)
215 this->push(std::move(dir
));
218 void clear() { c
.clear(); }
221 fs::recursive_directory_iterator::
222 recursive_directory_iterator(const path
& p
, directory_options options
,
224 : _M_options(options
), _M_pending(true)
226 // Do not report an error for permission denied errors.
227 const bool skip_permission_denied
228 = is_set(options
, directory_options::skip_permission_denied
);
231 _Dir
dir(p
, skip_permission_denied
, /*nofollow*/false, ec
);
235 auto sp
= std::__make_shared
<_Dir_stack
>(std::move(dir
));
236 if (ecptr
? sp
->top().advance(skip_permission_denied
, *ecptr
)
237 : sp
->top().advance(skip_permission_denied
))
245 _GLIBCXX_THROW_OR_ABORT(fs::filesystem_error(
246 "recursive directory iterator cannot open directory", p
, ec
));
249 fs::recursive_directory_iterator::~recursive_directory_iterator() = default;
252 fs::recursive_directory_iterator::depth() const
254 return int(_M_dirs
->size()) - 1;
257 const fs::directory_entry
&
258 fs::recursive_directory_iterator::operator*() const
260 return _M_dirs
->top().entry
;
263 fs::recursive_directory_iterator
&
264 fs::recursive_directory_iterator::
265 operator=(const recursive_directory_iterator
& other
) noexcept
= default;
267 fs::recursive_directory_iterator
&
268 fs::recursive_directory_iterator::
269 operator=(recursive_directory_iterator
&& other
) noexcept
= default;
271 fs::recursive_directory_iterator
&
272 fs::recursive_directory_iterator::operator++()
277 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
278 "cannot increment recursive directory iterator", ec
));
282 fs::recursive_directory_iterator
&
283 fs::recursive_directory_iterator::increment(error_code
& ec
) noexcept
287 ec
= std::make_error_code(errc::invalid_argument
);
292 = is_set(_M_options
, directory_options::follow_directory_symlink
);
293 const bool skip_permission_denied
294 = is_set(_M_options
, directory_options::skip_permission_denied
);
296 auto& top
= _M_dirs
->top();
298 if (std::exchange(_M_pending
, true) && top
.should_recurse(follow
, ec
))
300 _Dir dir
= top
.open_subdir(skip_permission_denied
, !follow
, ec
);
307 _M_dirs
->push(std::move(dir
));
310 while (!_M_dirs
->top().advance(skip_permission_denied
, ec
) && !ec
)
313 if (_M_dirs
->empty())
327 fs::recursive_directory_iterator::pop(error_code
& ec
)
331 ec
= std::make_error_code(errc::invalid_argument
);
335 const bool skip_permission_denied
336 = is_set(_M_options
, directory_options::skip_permission_denied
);
340 if (_M_dirs
->empty())
346 } while (!_M_dirs
->top().advance(skip_permission_denied
, ec
) && !ec
);
353 fs::recursive_directory_iterator::pop()
355 [[maybe_unused
]] const bool dereferenceable
= _M_dirs
!= nullptr;
359 _GLIBCXX_THROW_OR_ABORT(filesystem_error(dereferenceable
360 ? "recursive directory iterator cannot pop"
361 : "non-dereferenceable recursive directory iterator cannot pop",