]> git.ipfire.org Git - thirdparty/gcc.git/blame - libstdc++-v3/src/c++17/fs_dir.cc
Make filesystem::path safe for self assignment
[thirdparty/gcc.git] / libstdc++-v3 / src / c++17 / fs_dir.cc
CommitLineData
641cb5a6
JW
1// Class filesystem::directory_entry etc. -*- C++ -*-
2
a5544970 3// Copyright (C) 2014-2019 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
29#include <filesystem>
641cb5a6
JW
30#include <utility>
31#include <stack>
32#include <string.h>
33#include <errno.h>
34#define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM namespace filesystem {
35#define _GLIBCXX_END_NAMESPACE_FILESYSTEM }
de4db54f 36#include "../filesystem/dir-common.h"
641cb5a6
JW
37
38namespace fs = std::filesystem;
9534a5e6 39namespace posix = std::filesystem::__gnu_posix;
641cb5a6 40
da29d2a3
JW
41template class std::__shared_ptr<fs::_Dir>;
42template class std::__shared_ptr<fs::recursive_directory_iterator::_Dir_stack>;
43
641cb5a6
JW
44struct fs::_Dir : _Dir_base
45{
46 _Dir(const fs::path& p, bool skip_permission_denied, error_code& ec)
47 : _Dir_base(p.c_str(), skip_permission_denied, ec)
48 {
49 if (!ec)
50 path = p;
51 }
52
9534a5e6 53 _Dir(posix::DIR* dirp, const path& p) : _Dir_base(dirp), path(p) { }
641cb5a6
JW
54
55 _Dir(_Dir&&) = default;
56
57 // Returns false when the end of the directory entries is reached.
58 // Reports errors by setting ec.
59 bool advance(bool skip_permission_denied, error_code& ec) noexcept
60 {
61 if (const auto entp = _Dir_base::advance(skip_permission_denied, ec))
62 {
8d531548
JW
63 auto name = path;
64 name /= entp->d_name;
fb601354 65 entry = fs::directory_entry{std::move(name), get_file_type(*entp)};
641cb5a6
JW
66 return true;
67 }
68 else if (!ec)
69 {
70 // reached the end
71 entry = {};
72 }
73 return false;
74 }
75
76 bool advance(error_code& ec) noexcept { return advance(false, ec); }
77
78 // Returns false when the end of the directory entries is reached.
79 // Reports errors by throwing.
80 bool advance(bool skip_permission_denied = false)
81 {
82 error_code ec;
83 const bool ok = advance(skip_permission_denied, ec);
84 if (ec)
85 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
86 "directory iterator cannot advance", ec));
87 return ok;
88 }
89
90 bool should_recurse(bool follow_symlink, error_code& ec) const
91 {
92 file_type type = entry._M_type;
93 if (type == file_type::none || type == file_type::unknown)
94 {
95 type = entry.symlink_status(ec).type();
96 if (ec)
97 return false;
98 }
99
100 if (type == file_type::directory)
101 return true;
102 if (type == file_type::symlink)
103 return follow_symlink && is_directory(entry.status(ec));
104 return false;
105 }
106
107 fs::path path;
108 directory_entry entry;
109};
110
111namespace
112{
113 template<typename Bitmask>
114 inline bool
115 is_set(Bitmask obj, Bitmask bits)
116 {
117 return (obj & bits) != Bitmask::none;
118 }
119}
120
121fs::directory_iterator::
122directory_iterator(const path& p, directory_options options, error_code* ecptr)
123{
124 const bool skip_permission_denied
125 = is_set(options, directory_options::skip_permission_denied);
126
127 error_code ec;
128 _Dir dir(p, skip_permission_denied, ec);
129
130 if (dir.dirp)
131 {
da29d2a3 132 auto sp = std::__make_shared<fs::_Dir>(std::move(dir));
641cb5a6
JW
133 if (sp->advance(skip_permission_denied, ec))
134 _M_dir.swap(sp);
135 }
136 if (ecptr)
137 *ecptr = ec;
138 else if (ec)
139 _GLIBCXX_THROW_OR_ABORT(fs::filesystem_error(
140 "directory iterator cannot open directory", p, ec));
141}
142
143const fs::directory_entry&
144fs::directory_iterator::operator*() const
145{
146 if (!_M_dir)
147 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
148 "non-dereferenceable directory iterator",
149 std::make_error_code(errc::invalid_argument)));
150 return _M_dir->entry;
151}
152
153fs::directory_iterator&
154fs::directory_iterator::operator++()
155{
156 if (!_M_dir)
157 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
158 "cannot advance non-dereferenceable directory iterator",
159 std::make_error_code(errc::invalid_argument)));
160 if (!_M_dir->advance())
161 _M_dir.reset();
162 return *this;
163}
164
165fs::directory_iterator&
29453a9f 166fs::directory_iterator::increment(error_code& ec)
641cb5a6
JW
167{
168 if (!_M_dir)
169 {
170 ec = std::make_error_code(errc::invalid_argument);
171 return *this;
172 }
173 if (!_M_dir->advance(ec))
174 _M_dir.reset();
175 return *this;
176}
177
178struct fs::recursive_directory_iterator::_Dir_stack : std::stack<_Dir>
179{
180 void clear() { c.clear(); }
181};
182
183fs::recursive_directory_iterator::
184recursive_directory_iterator(const path& p, directory_options options,
185 error_code* ecptr)
186: _M_options(options), _M_pending(true)
187{
9534a5e6 188 if (posix::DIR* dirp = posix::opendir(p.c_str()))
641cb5a6
JW
189 {
190 if (ecptr)
191 ecptr->clear();
da29d2a3 192 auto sp = std::__make_shared<_Dir_stack>();
641cb5a6
JW
193 sp->push(_Dir{ dirp, p });
194 if (ecptr ? sp->top().advance(*ecptr) : sp->top().advance())
195 _M_dirs.swap(sp);
196 }
197 else
198 {
199 const int err = errno;
200 if (err == EACCES
201 && is_set(options, fs::directory_options::skip_permission_denied))
202 {
203 if (ecptr)
204 ecptr->clear();
205 return;
206 }
207
208 if (!ecptr)
209 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
210 "recursive directory iterator cannot open directory", p,
211 std::error_code(err, std::generic_category())));
212
213 ecptr->assign(err, std::generic_category());
214 }
215}
216
217fs::recursive_directory_iterator::~recursive_directory_iterator() = default;
218
219int
220fs::recursive_directory_iterator::depth() const
221{
222 return int(_M_dirs->size()) - 1;
223}
224
225const fs::directory_entry&
226fs::recursive_directory_iterator::operator*() const
227{
228 return _M_dirs->top().entry;
229}
230
231fs::recursive_directory_iterator&
232fs::recursive_directory_iterator::
233operator=(const recursive_directory_iterator& other) noexcept = default;
234
235fs::recursive_directory_iterator&
236fs::recursive_directory_iterator::
237operator=(recursive_directory_iterator&& other) noexcept = default;
238
239fs::recursive_directory_iterator&
240fs::recursive_directory_iterator::operator++()
241{
242 error_code ec;
243 increment(ec);
244 if (ec)
245 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
246 "cannot increment recursive directory iterator", ec));
247 return *this;
248}
249
250fs::recursive_directory_iterator&
29453a9f 251fs::recursive_directory_iterator::increment(error_code& ec)
641cb5a6
JW
252{
253 if (!_M_dirs)
254 {
255 ec = std::make_error_code(errc::invalid_argument);
256 return *this;
257 }
258
259 const bool follow
260 = is_set(_M_options, directory_options::follow_directory_symlink);
261 const bool skip_permission_denied
262 = is_set(_M_options, directory_options::skip_permission_denied);
263
264 auto& top = _M_dirs->top();
265
266 if (std::exchange(_M_pending, true) && top.should_recurse(follow, ec))
267 {
268 _Dir dir(top.entry.path(), skip_permission_denied, ec);
269 if (ec)
270 {
271 _M_dirs.reset();
272 return *this;
273 }
274 if (dir.dirp)
275 _M_dirs->push(std::move(dir));
276 }
277
278 while (!_M_dirs->top().advance(skip_permission_denied, ec) && !ec)
279 {
280 _M_dirs->pop();
281 if (_M_dirs->empty())
282 {
283 _M_dirs.reset();
284 return *this;
285 }
286 }
287 return *this;
288}
289
290void
291fs::recursive_directory_iterator::pop(error_code& ec)
292{
293 if (!_M_dirs)
294 {
295 ec = std::make_error_code(errc::invalid_argument);
296 return;
297 }
298
299 const bool skip_permission_denied
300 = is_set(_M_options, directory_options::skip_permission_denied);
301
302 do {
303 _M_dirs->pop();
304 if (_M_dirs->empty())
305 {
306 _M_dirs.reset();
307 ec.clear();
308 return;
309 }
310 } while (!_M_dirs->top().advance(skip_permission_denied, ec));
311}
312
313void
314fs::recursive_directory_iterator::pop()
315{
316 error_code ec;
317 pop(ec);
318 if (ec)
319 _GLIBCXX_THROW_OR_ABORT(filesystem_error(_M_dirs
320 ? "recursive directory iterator cannot pop"
321 : "non-dereferenceable recursive directory iterator cannot pop",
322 ec));
323}