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