hppa: Modify TLS patterns to provide both 32 and 64-bit support.
[official-gcc.git] / libstdc++-v3 / src / c++17 / fs_dir.cc
blobd882beb1074bc82278e4c58ed8af610a909ee91e
1 // Class filesystem::directory_entry etc. -*- C++ -*-
3 // Copyright (C) 2014-2023 Free Software Foundation, Inc.
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.
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
27 #endif
29 #include <bits/largefile-config.h>
30 #include <filesystem>
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 }
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, bool nofollow,
48 [[maybe_unused]] bool filename_only, error_code& ec)
49 : _Dir_base(p.c_str(), skip_permission_denied, nofollow, ec)
51 #if _GLIBCXX_HAVE_DIRFD && _GLIBCXX_HAVE_OPENAT && _GLIBCXX_HAVE_UNLINKAT
52 if (filename_only)
53 return; // Do not store path p when we aren't going to use it.
54 #endif
56 if (!ec)
57 path = p;
60 _Dir(_Dir_base&& d, const path& p) : _Dir_base(std::move(d)), path(p) { }
62 _Dir(_Dir&&) = default;
64 // Returns false when the end of the directory entries is reached.
65 // Reports errors by setting ec.
66 bool advance(bool skip_permission_denied, error_code& ec) noexcept
68 if (const auto entp = _Dir_base::advance(skip_permission_denied, ec))
70 auto name = path;
71 name /= entp->d_name;
72 file_type type = file_type::none;
73 #ifdef _GLIBCXX_HAVE_STRUCT_DIRENT_D_TYPE
74 // Even if the OS supports dirent::d_type the filesystem might not:
75 if (entp->d_type != DT_UNKNOWN)
76 type = get_file_type(*entp);
77 #endif
78 entry = fs::directory_entry{std::move(name), type};
79 return true;
81 else if (!ec)
83 // reached the end
84 entry = {};
86 return false;
89 bool advance(error_code& ec) noexcept { return advance(false, ec); }
91 // Returns false when the end of the directory entries is reached.
92 // Reports errors by throwing.
93 bool advance(bool skip_permission_denied = false)
95 error_code ec;
96 const bool ok = advance(skip_permission_denied, ec);
97 if (ec)
98 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
99 "directory iterator cannot advance", ec));
100 return ok;
103 bool should_recurse(bool follow_symlink, error_code& ec) const
105 file_type type = entry._M_type;
106 if (type == file_type::none)
108 type = entry.symlink_status(ec).type();
109 if (ec)
110 return false;
113 if (type == file_type::directory)
114 return true;
115 if (type == file_type::symlink)
116 return follow_symlink && is_directory(entry.status(ec));
117 return false;
120 // Return a pathname for the current directory entry, as an _At_path.
121 _Dir_base::_At_path
122 current() const noexcept
124 const fs::path& p = entry.path();
125 #if _GLIBCXX_HAVE_DIRFD
126 if (!p.empty()) [[__likely__]]
128 auto len = std::prev(p.end())->native().size();
129 return {::dirfd(this->dirp), p.c_str(), p.native().size() - len};
131 #endif
132 return p.c_str();
135 // Create a new _Dir for the directory this->entry.path().
136 _Dir
137 open_subdir(bool skip_permission_denied, bool nofollow,
138 error_code& ec) const noexcept
140 _Dir_base d(current(), skip_permission_denied, nofollow, ec);
141 // If this->path is empty, the new _Dir should have an empty path too.
142 const fs::path& p = this->path.empty() ? this->path : this->entry.path();
143 return _Dir(std::move(d), p);
146 bool
147 do_unlink(bool is_directory, error_code& ec) const noexcept
149 #if _GLIBCXX_HAVE_UNLINKAT
150 const auto atp = current();
151 if (::unlinkat(atp.dir(), atp.path_at_dir(),
152 is_directory ? AT_REMOVEDIR : 0) == -1)
154 ec.assign(errno, std::generic_category());
155 return false;
157 else
159 ec.clear();
160 return true;
162 #else
163 return fs::remove(entry.path(), ec);
164 #endif
167 // Remove the non-directory that this->entry refers to.
168 bool
169 unlink(error_code& ec) const noexcept
170 { return do_unlink(/* is_directory*/ false, ec); }
172 // Remove the directory that this->entry refers to.
173 bool
174 rmdir(error_code& ec) const noexcept
175 { return do_unlink(/* is_directory*/ true, ec); }
177 fs::path path; // Empty if only using unlinkat with file descr.
178 directory_entry entry;
181 namespace
183 template<typename Bitmask>
184 inline bool
185 is_set(Bitmask obj, Bitmask bits)
187 return (obj & bits) != Bitmask::none;
190 // Non-standard directory option flags, currently only for internal use:
192 // Do not allow directory iterator to open a symlink.
193 // This might seem redundant given directory_options::follow_directory_symlink
194 // but that is only checked for recursing into sub-directories, and we need
195 // something that controls the initial opendir() call in the constructor.
196 constexpr fs::directory_options __directory_iterator_nofollow{64};
197 // Do not store full paths in std::filesystem::recursive_directory_iterator.
198 // When fs::remove_all uses recursive_directory_iterator::__erase and unlinkat
199 // is available in libc, we do not need the parent directory's path, only the
200 // filenames of the directory entries (and a file descriptor for the parent).
201 // This flag avoids allocating memory for full paths that won't be needed.
202 constexpr fs::directory_options __directory_iterator_filename_only{128};
205 fs::directory_iterator::
206 directory_iterator(const path& p, directory_options options, error_code* ecptr)
208 // Do not report an error for permission denied errors.
209 const bool skip_permission_denied
210 = is_set(options, directory_options::skip_permission_denied);
211 // Do not allow opening a symlink.
212 const bool nofollow = is_set(options, __directory_iterator_nofollow);
214 error_code ec;
215 _Dir dir(p, skip_permission_denied, nofollow, /*filename only*/false, ec);
217 if (dir.dirp)
219 auto sp = std::__make_shared<fs::_Dir>(std::move(dir));
220 if (sp->advance(skip_permission_denied, ec))
221 _M_dir.swap(sp);
223 if (ecptr)
224 *ecptr = ec;
225 else if (ec)
226 _GLIBCXX_THROW_OR_ABORT(fs::filesystem_error(
227 "directory iterator cannot open directory", p, ec));
230 const fs::directory_entry&
231 fs::directory_iterator::operator*() const noexcept
233 return _M_dir->entry;
236 fs::directory_iterator&
237 fs::directory_iterator::operator++()
239 if (!_M_dir)
240 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
241 "cannot advance non-dereferenceable directory iterator",
242 std::make_error_code(errc::invalid_argument)));
243 if (!_M_dir->advance())
244 _M_dir.reset();
245 return *this;
248 fs::directory_iterator&
249 fs::directory_iterator::increment(error_code& ec)
251 if (!_M_dir)
253 ec = std::make_error_code(errc::invalid_argument);
254 return *this;
256 if (!_M_dir->advance(ec))
257 _M_dir.reset();
258 return *this;
261 struct fs::recursive_directory_iterator::_Dir_stack : std::stack<_Dir>
263 _Dir_stack(directory_options opts, _Dir&& dir)
264 : options(opts), pending(true)
266 this->push(std::move(dir));
269 path::string_type orig;
270 const directory_options options;
271 bool pending;
273 void clear() { c.clear(); }
275 path current_path() const
277 path p;
278 if (top().path.empty())
280 // Reconstruct path that failed from dir stack.
281 p = orig;
282 for (auto& d : this->c)
283 p /= d.entry.path();
285 else
286 p = top().entry.path();
287 return p;
291 fs::recursive_directory_iterator::
292 recursive_directory_iterator(const path& p, directory_options options,
293 error_code* ecptr)
295 // Do not report an error for permission denied errors.
296 const bool skip_permission_denied
297 = is_set(options, directory_options::skip_permission_denied);
298 // Do not allow opening a symlink as the starting directory.
299 const bool nofollow = is_set(options, __directory_iterator_nofollow);
300 // Prefer to store only filenames (not full paths) in directory_entry values.
301 const bool filename_only
302 = is_set(options, __directory_iterator_filename_only);
304 error_code ec;
305 _Dir dir(p, skip_permission_denied, nofollow, filename_only, ec);
307 if (dir.dirp)
309 auto sp = std::__make_shared<_Dir_stack>(options, std::move(dir));
310 if (ecptr ? sp->top().advance(skip_permission_denied, *ecptr)
311 : sp->top().advance(skip_permission_denied))
313 _M_dirs.swap(sp);
314 if (filename_only) // Need to save original path for error reporting.
315 _M_dirs->orig = p.native();
318 else if (ecptr)
319 *ecptr = ec;
320 else if (ec)
321 _GLIBCXX_THROW_OR_ABORT(fs::filesystem_error(
322 "recursive directory iterator cannot open directory", p, ec));
325 fs::recursive_directory_iterator::~recursive_directory_iterator() = default;
327 fs::directory_options
328 fs::recursive_directory_iterator::options() const noexcept
330 return _M_dirs->options;
334 fs::recursive_directory_iterator::depth() const noexcept
336 return int(_M_dirs->size()) - 1;
339 bool
340 fs::recursive_directory_iterator::recursion_pending() const noexcept
342 return _M_dirs->pending;
345 const fs::directory_entry&
346 fs::recursive_directory_iterator::operator*() const noexcept
348 return _M_dirs->top().entry;
351 fs::recursive_directory_iterator&
352 fs::recursive_directory_iterator::
353 operator=(const recursive_directory_iterator& other) noexcept = default;
355 fs::recursive_directory_iterator&
356 fs::recursive_directory_iterator::
357 operator=(recursive_directory_iterator&& other) noexcept = default;
359 fs::recursive_directory_iterator&
360 fs::recursive_directory_iterator::operator++()
362 error_code ec;
363 increment(ec);
364 if (ec)
365 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
366 "cannot increment recursive directory iterator", ec));
367 return *this;
370 fs::recursive_directory_iterator&
371 fs::recursive_directory_iterator::increment(error_code& ec)
373 if (!_M_dirs)
375 ec = std::make_error_code(errc::invalid_argument);
376 return *this;
379 const bool follow
380 = is_set(_M_dirs->options, directory_options::follow_directory_symlink);
381 const bool skip_permission_denied
382 = is_set(_M_dirs->options, directory_options::skip_permission_denied);
384 auto& top = _M_dirs->top();
386 if (std::exchange(_M_dirs->pending, true) && top.should_recurse(follow, ec))
388 _Dir dir = top.open_subdir(skip_permission_denied, !follow, ec);
389 if (ec)
391 _M_dirs.reset();
392 return *this;
394 if (dir.dirp)
395 _M_dirs->push(std::move(dir));
398 while (!_M_dirs->top().advance(skip_permission_denied, ec) && !ec)
400 _M_dirs->pop();
401 if (_M_dirs->empty())
403 _M_dirs.reset();
404 return *this;
408 if (ec)
409 _M_dirs.reset();
411 return *this;
414 void
415 fs::recursive_directory_iterator::pop(error_code& ec)
417 if (!_M_dirs)
419 ec = std::make_error_code(errc::invalid_argument);
420 return;
423 const bool skip_permission_denied
424 = is_set(_M_dirs->options, directory_options::skip_permission_denied);
426 do {
427 _M_dirs->pop();
428 if (_M_dirs->empty())
430 _M_dirs.reset();
431 ec.clear();
432 return;
434 } while (!_M_dirs->top().advance(skip_permission_denied, ec) && !ec);
436 if (ec)
437 _M_dirs.reset();
440 void
441 fs::recursive_directory_iterator::pop()
443 [[maybe_unused]] const bool dereferenceable = _M_dirs != nullptr;
444 error_code ec;
445 pop(ec);
446 if (ec)
447 _GLIBCXX_THROW_OR_ABORT(filesystem_error(dereferenceable
448 ? "recursive directory iterator cannot pop"
449 : "non-dereferenceable recursive directory iterator cannot pop",
450 ec));
453 void
454 fs::recursive_directory_iterator::disable_recursion_pending() noexcept
456 _M_dirs->pending = false;
459 // Used to implement filesystem::remove_all.
460 fs::recursive_directory_iterator&
461 fs::recursive_directory_iterator::__erase(error_code* ecptr)
463 error_code ec;
464 if (!_M_dirs)
466 ec = std::make_error_code(errc::invalid_argument);
467 return *this;
470 // We never want to skip permission denied when removing files.
471 const bool skip_permission_denied = false;
472 // We never want to follow directory symlinks when removing files.
473 const bool nofollow = true;
475 // Loop until we find something we can remove.
476 while (!ec)
478 auto& top = _M_dirs->top();
480 #if _GLIBCXX_FILESYSTEM_IS_WINDOWS
481 // _Dir::unlink uses fs::remove which uses std::system_category() for
482 // Windows errror codes, so we can't just check for EPERM and EISDIR.
483 // Use directory_entry::refresh() here to check if we have a directory.
484 // This can be a TOCTTOU race, but we don't have openat or unlinkat to
485 // solve that on Windows, and generally don't support symlinks anyway.
486 if (top.entry._M_type == file_type::none)
487 top.entry.refresh();
488 #endif
490 if (top.entry._M_type == file_type::directory)
492 _Dir dir = top.open_subdir(skip_permission_denied, nofollow, ec);
493 if (!ec)
495 __glibcxx_assert(dir.dirp != nullptr);
496 if (dir.advance(skip_permission_denied, ec))
498 // Non-empty directory, recurse into it.
499 _M_dirs->push(std::move(dir));
500 continue;
502 if (!ec)
504 // Directory is empty so we can remove it.
505 if (top.rmdir(ec))
506 break; // Success
510 else if (top.unlink(ec))
511 break; // Success
512 #if ! _GLIBCXX_FILESYSTEM_IS_WINDOWS
513 else if (top.entry._M_type == file_type::none)
515 // We did not have a cached type, so it's possible that top.entry
516 // is actually a directory, and that's why the unlink above failed.
517 #ifdef EPERM
518 // POSIX.1-2017 says unlink on a directory returns EPERM,
519 // but LSB allows EISDIR too. Some targets don't even define EPERM.
520 if (ec.value() == EPERM || ec.value() == EISDIR)
521 #else
522 if (ec.value() == EISDIR)
523 #endif
525 // Retry, treating it as a directory.
526 top.entry._M_type = file_type::directory;
527 ec.clear();
528 continue;
531 #endif
534 if (!ec)
536 // We successfully removed the current entry, so advance to the next one.
537 if (_M_dirs->top().advance(skip_permission_denied, ec))
538 return *this;
539 else if (!ec)
541 // Reached the end of the current directory.
542 _M_dirs->pop();
543 if (_M_dirs->empty())
544 _M_dirs.reset();
545 return *this;
549 // Reset _M_dirs to empty.
550 auto dirs = std::move(_M_dirs);
552 // Need to report an error
553 if (ecptr)
554 *ecptr = ec;
555 else
556 _GLIBCXX_THROW_OR_ABORT(fs::filesystem_error("cannot remove all",
557 dirs->orig,
558 dirs->current_path(),
559 ec));
561 return *this;