]> git.ipfire.org Git - thirdparty/gcc.git/blame - libstdc++-v3/src/c++17/fs_dir.cc
rs6000: More factoring of overload processing
[thirdparty/gcc.git] / libstdc++-v3 / src / c++17 / fs_dir.cc
CommitLineData
641cb5a6
JW
1// Class filesystem::directory_entry etc. -*- C++ -*-
2
7adcbafe 3// Copyright (C) 2014-2022 Free Software Foundation, Inc.
641cb5a6
JW
4//
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)
9// any later version.
10
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.
15
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.
19
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/>.
24
25#ifndef _GLIBCXX_USE_CXX11_ABI
26# define _GLIBCXX_USE_CXX11_ABI 1
27#endif
28
2fc11587 29#include <bits/largefile-config.h>
641cb5a6 30#include <filesystem>
641cb5a6
JW
31#include <utility>
32#include <stack>
33#include <string.h>
34#include <errno.h>
35#define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM namespace filesystem {
36#define _GLIBCXX_END_NAMESPACE_FILESYSTEM }
de4db54f 37#include "../filesystem/dir-common.h"
641cb5a6
JW
38
39namespace fs = std::filesystem;
9534a5e6 40namespace posix = std::filesystem::__gnu_posix;
641cb5a6 41
da29d2a3
JW
42template class std::__shared_ptr<fs::_Dir>;
43template class std::__shared_ptr<fs::recursive_directory_iterator::_Dir_stack>;
44
641cb5a6
JW
45struct fs::_Dir : _Dir_base
46{
c8bd4dc8
JW
47 _Dir(const fs::path& p, bool skip_permission_denied, bool nofollow,
48 error_code& ec)
49 : _Dir_base(p.c_str(), skip_permission_denied, nofollow, ec)
641cb5a6
JW
50 {
51 if (!ec)
52 path = p;
53 }
54
9534a5e6 55 _Dir(posix::DIR* dirp, const path& p) : _Dir_base(dirp), path(p) { }
641cb5a6
JW
56
57 _Dir(_Dir&&) = default;
58
59 // Returns false when the end of the directory entries is reached.
60 // Reports errors by setting ec.
61 bool advance(bool skip_permission_denied, error_code& ec) noexcept
62 {
63 if (const auto entp = _Dir_base::advance(skip_permission_denied, ec))
64 {
8d531548
JW
65 auto name = path;
66 name /= entp->d_name;
5ed5c0da
JW
67 file_type type = file_type::none;
68#ifdef _GLIBCXX_HAVE_STRUCT_DIRENT_D_TYPE
69 // Even if the OS supports dirent::d_type the filesystem might not:
70 if (entp->d_type != DT_UNKNOWN)
71 type = get_file_type(*entp);
72#endif
73 entry = fs::directory_entry{std::move(name), type};
641cb5a6
JW
74 return true;
75 }
76 else if (!ec)
77 {
78 // reached the end
79 entry = {};
80 }
81 return false;
82 }
83
84 bool advance(error_code& ec) noexcept { return advance(false, ec); }
85
86 // Returns false when the end of the directory entries is reached.
87 // Reports errors by throwing.
88 bool advance(bool skip_permission_denied = false)
89 {
90 error_code ec;
91 const bool ok = advance(skip_permission_denied, ec);
92 if (ec)
93 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
94 "directory iterator cannot advance", ec));
95 return ok;
96 }
97
98 bool should_recurse(bool follow_symlink, error_code& ec) const
99 {
100 file_type type = entry._M_type;
5ed5c0da 101 if (type == file_type::none)
641cb5a6
JW
102 {
103 type = entry.symlink_status(ec).type();
104 if (ec)
105 return false;
106 }
107
108 if (type == file_type::directory)
109 return true;
110 if (type == file_type::symlink)
111 return follow_symlink && is_directory(entry.status(ec));
112 return false;
113 }
114
115 fs::path path;
116 directory_entry entry;
117};
118
119namespace
120{
121 template<typename Bitmask>
122 inline bool
123 is_set(Bitmask obj, Bitmask bits)
124 {
125 return (obj & bits) != Bitmask::none;
126 }
127}
128
129fs::directory_iterator::
130directory_iterator(const path& p, directory_options options, error_code* ecptr)
131{
c8bd4dc8 132 // Do not report an error for permission denied errors.
641cb5a6
JW
133 const bool skip_permission_denied
134 = is_set(options, directory_options::skip_permission_denied);
c8bd4dc8
JW
135 // Do not allow opening a symlink (used by filesystem::remove_all)
136 const bool nofollow
137 = is_set(options, __directory_iterator_nofollow);
641cb5a6
JW
138
139 error_code ec;
c8bd4dc8 140 _Dir dir(p, skip_permission_denied, nofollow, ec);
641cb5a6
JW
141
142 if (dir.dirp)
143 {
da29d2a3 144 auto sp = std::__make_shared<fs::_Dir>(std::move(dir));
641cb5a6
JW
145 if (sp->advance(skip_permission_denied, ec))
146 _M_dir.swap(sp);
147 }
148 if (ecptr)
149 *ecptr = ec;
150 else if (ec)
151 _GLIBCXX_THROW_OR_ABORT(fs::filesystem_error(
152 "directory iterator cannot open directory", p, ec));
153}
154
155const fs::directory_entry&
044b0434 156fs::directory_iterator::operator*() const noexcept
641cb5a6 157{
641cb5a6
JW
158 return _M_dir->entry;
159}
160
161fs::directory_iterator&
162fs::directory_iterator::operator++()
163{
164 if (!_M_dir)
165 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
166 "cannot advance non-dereferenceable directory iterator",
167 std::make_error_code(errc::invalid_argument)));
168 if (!_M_dir->advance())
169 _M_dir.reset();
170 return *this;
171}
172
173fs::directory_iterator&
29453a9f 174fs::directory_iterator::increment(error_code& ec)
641cb5a6
JW
175{
176 if (!_M_dir)
177 {
178 ec = std::make_error_code(errc::invalid_argument);
179 return *this;
180 }
181 if (!_M_dir->advance(ec))
182 _M_dir.reset();
183 return *this;
184}
185
186struct fs::recursive_directory_iterator::_Dir_stack : std::stack<_Dir>
187{
c7dde4a9
JW
188 _Dir_stack(directory_options opts, posix::DIR* dirp, const path& p)
189 : options(opts), pending(true)
190 {
191 this->emplace(dirp, p);
192 }
193
194 const directory_options options;
195 bool pending;
196
641cb5a6
JW
197 void clear() { c.clear(); }
198};
199
200fs::recursive_directory_iterator::
201recursive_directory_iterator(const path& p, directory_options options,
202 error_code* ecptr)
641cb5a6 203{
9534a5e6 204 if (posix::DIR* dirp = posix::opendir(p.c_str()))
641cb5a6
JW
205 {
206 if (ecptr)
207 ecptr->clear();
c7dde4a9 208 auto sp = std::__make_shared<_Dir_stack>(options, dirp, p);
641cb5a6
JW
209 if (ecptr ? sp->top().advance(*ecptr) : sp->top().advance())
210 _M_dirs.swap(sp);
211 }
212 else
213 {
214 const int err = errno;
8cfb3873 215 if (fs::is_permission_denied_error(err)
641cb5a6
JW
216 && is_set(options, fs::directory_options::skip_permission_denied))
217 {
218 if (ecptr)
219 ecptr->clear();
220 return;
221 }
222
223 if (!ecptr)
224 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
225 "recursive directory iterator cannot open directory", p,
226 std::error_code(err, std::generic_category())));
227
228 ecptr->assign(err, std::generic_category());
229 }
230}
231
232fs::recursive_directory_iterator::~recursive_directory_iterator() = default;
233
c7dde4a9
JW
234fs::directory_options
235fs::recursive_directory_iterator::options() const noexcept
236{
237 return _M_dirs->options;
238}
239
641cb5a6 240int
c7dde4a9 241fs::recursive_directory_iterator::depth() const noexcept
641cb5a6
JW
242{
243 return int(_M_dirs->size()) - 1;
244}
245
c7dde4a9
JW
246bool
247fs::recursive_directory_iterator::recursion_pending() const noexcept
248{
249 return _M_dirs->pending;
250}
251
641cb5a6 252const fs::directory_entry&
c7dde4a9 253fs::recursive_directory_iterator::operator*() const noexcept
641cb5a6
JW
254{
255 return _M_dirs->top().entry;
256}
257
258fs::recursive_directory_iterator&
259fs::recursive_directory_iterator::
260operator=(const recursive_directory_iterator& other) noexcept = default;
261
262fs::recursive_directory_iterator&
263fs::recursive_directory_iterator::
264operator=(recursive_directory_iterator&& other) noexcept = default;
265
266fs::recursive_directory_iterator&
267fs::recursive_directory_iterator::operator++()
268{
269 error_code ec;
270 increment(ec);
271 if (ec)
272 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
273 "cannot increment recursive directory iterator", ec));
274 return *this;
275}
276
277fs::recursive_directory_iterator&
29453a9f 278fs::recursive_directory_iterator::increment(error_code& ec)
641cb5a6
JW
279{
280 if (!_M_dirs)
281 {
282 ec = std::make_error_code(errc::invalid_argument);
283 return *this;
284 }
285
286 const bool follow
c7dde4a9 287 = is_set(_M_dirs->options, directory_options::follow_directory_symlink);
641cb5a6 288 const bool skip_permission_denied
c7dde4a9 289 = is_set(_M_dirs->options, directory_options::skip_permission_denied);
641cb5a6
JW
290
291 auto& top = _M_dirs->top();
292
c7dde4a9 293 if (std::exchange(_M_dirs->pending, true) && top.should_recurse(follow, ec))
641cb5a6 294 {
c8bd4dc8 295 _Dir dir(top.entry.path(), skip_permission_denied, !follow, ec);
641cb5a6
JW
296 if (ec)
297 {
298 _M_dirs.reset();
299 return *this;
300 }
301 if (dir.dirp)
302 _M_dirs->push(std::move(dir));
303 }
304
305 while (!_M_dirs->top().advance(skip_permission_denied, ec) && !ec)
306 {
307 _M_dirs->pop();
308 if (_M_dirs->empty())
309 {
310 _M_dirs.reset();
311 return *this;
312 }
313 }
ec09a533
JW
314
315 if (ec)
316 _M_dirs.reset();
317
641cb5a6
JW
318 return *this;
319}
320
321void
322fs::recursive_directory_iterator::pop(error_code& ec)
323{
324 if (!_M_dirs)
325 {
326 ec = std::make_error_code(errc::invalid_argument);
327 return;
328 }
329
330 const bool skip_permission_denied
c7dde4a9 331 = is_set(_M_dirs->options, directory_options::skip_permission_denied);
641cb5a6
JW
332
333 do {
334 _M_dirs->pop();
335 if (_M_dirs->empty())
336 {
337 _M_dirs.reset();
338 ec.clear();
339 return;
340 }
ec09a533
JW
341 } while (!_M_dirs->top().advance(skip_permission_denied, ec) && !ec);
342
343 if (ec)
344 _M_dirs.reset();
641cb5a6
JW
345}
346
347void
348fs::recursive_directory_iterator::pop()
349{
2905e1af 350 [[maybe_unused]] const bool dereferenceable = _M_dirs != nullptr;
641cb5a6
JW
351 error_code ec;
352 pop(ec);
353 if (ec)
ec09a533 354 _GLIBCXX_THROW_OR_ABORT(filesystem_error(dereferenceable
641cb5a6
JW
355 ? "recursive directory iterator cannot pop"
356 : "non-dereferenceable recursive directory iterator cannot pop",
357 ec));
358}
c7dde4a9
JW
359
360void
361fs::recursive_directory_iterator::disable_recursion_pending() noexcept
362{
363 _M_dirs->pending = false;
364}