xref: /netbsd-src/external/gpl3/gcc.old/dist/libstdc++-v3/src/filesystem/std-dir.cc (revision cef8759bd76c1b621f8eab8faa6f208faabc2e15)
1*cef8759bSmrg // Class filesystem::directory_entry etc. -*- C++ -*-
2*cef8759bSmrg 
3*cef8759bSmrg // Copyright (C) 2014-2018 Free Software Foundation, Inc.
4*cef8759bSmrg //
5*cef8759bSmrg // This file is part of the GNU ISO C++ Library.  This library is free
6*cef8759bSmrg // software; you can redistribute it and/or modify it under the
7*cef8759bSmrg // terms of the GNU General Public License as published by the
8*cef8759bSmrg // Free Software Foundation; either version 3, or (at your option)
9*cef8759bSmrg // any later version.
10*cef8759bSmrg 
11*cef8759bSmrg // This library is distributed in the hope that it will be useful,
12*cef8759bSmrg // but WITHOUT ANY WARRANTY; without even the implied warranty of
13*cef8759bSmrg // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14*cef8759bSmrg // GNU General Public License for more details.
15*cef8759bSmrg 
16*cef8759bSmrg // Under Section 7 of GPL version 3, you are granted additional
17*cef8759bSmrg // permissions described in the GCC Runtime Library Exception, version
18*cef8759bSmrg // 3.1, as published by the Free Software Foundation.
19*cef8759bSmrg 
20*cef8759bSmrg // You should have received a copy of the GNU General Public License and
21*cef8759bSmrg // a copy of the GCC Runtime Library Exception along with this program;
22*cef8759bSmrg // see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
23*cef8759bSmrg // <http://www.gnu.org/licenses/>.
24*cef8759bSmrg 
25*cef8759bSmrg #ifndef _GLIBCXX_USE_CXX11_ABI
26*cef8759bSmrg # define _GLIBCXX_USE_CXX11_ABI 1
27*cef8759bSmrg #endif
28*cef8759bSmrg 
29*cef8759bSmrg #include <bits/largefile-config.h>
30*cef8759bSmrg #include <filesystem>
31*cef8759bSmrg #include <experimental/filesystem>
32*cef8759bSmrg #include <utility>
33*cef8759bSmrg #include <stack>
34*cef8759bSmrg #include <string.h>
35*cef8759bSmrg #include <errno.h>
36*cef8759bSmrg #define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM namespace filesystem {
37*cef8759bSmrg #define _GLIBCXX_END_NAMESPACE_FILESYSTEM }
38*cef8759bSmrg #include "dir-common.h"
39*cef8759bSmrg 
40*cef8759bSmrg namespace fs = std::filesystem;
41*cef8759bSmrg 
42*cef8759bSmrg struct fs::_Dir : _Dir_base
43*cef8759bSmrg {
_Dirfs::_Dir44*cef8759bSmrg   _Dir(const fs::path& p, bool skip_permission_denied, error_code& ec)
45*cef8759bSmrg   : _Dir_base(p.c_str(), skip_permission_denied, ec)
46*cef8759bSmrg   {
47*cef8759bSmrg     if (!ec)
48*cef8759bSmrg       path = p;
49*cef8759bSmrg   }
50*cef8759bSmrg 
_Dirfs::_Dir51*cef8759bSmrg   _Dir(DIR* dirp, const path& p) : _Dir_base(dirp), path(p) { }
52*cef8759bSmrg 
53*cef8759bSmrg   _Dir(_Dir&&) = default;
54*cef8759bSmrg 
55*cef8759bSmrg   // Returns false when the end of the directory entries is reached.
56*cef8759bSmrg   // Reports errors by setting ec.
advancefs::_Dir57*cef8759bSmrg   bool advance(bool skip_permission_denied, error_code& ec) noexcept
58*cef8759bSmrg   {
59*cef8759bSmrg     if (const auto entp = _Dir_base::advance(skip_permission_denied, ec))
60*cef8759bSmrg       {
61*cef8759bSmrg 	file_type type = file_type::none;
62*cef8759bSmrg #ifdef _GLIBCXX_HAVE_STRUCT_DIRENT_D_TYPE
63*cef8759bSmrg 	// Even if the OS supports dirent::d_type the filesystem might not:
64*cef8759bSmrg 	if (entp->d_type != DT_UNKNOWN)
65*cef8759bSmrg 	  type = get_file_type(*entp);
66*cef8759bSmrg #endif
67*cef8759bSmrg 	entry = fs::directory_entry{path / entp->d_name, type};
68*cef8759bSmrg 	return true;
69*cef8759bSmrg       }
70*cef8759bSmrg     else if (!ec)
71*cef8759bSmrg       {
72*cef8759bSmrg 	// reached the end
73*cef8759bSmrg 	entry = {};
74*cef8759bSmrg       }
75*cef8759bSmrg     return false;
76*cef8759bSmrg   }
77*cef8759bSmrg 
advancefs::_Dir78*cef8759bSmrg   bool advance(error_code& ec) noexcept { return advance(false, ec); }
79*cef8759bSmrg 
80*cef8759bSmrg   // Returns false when the end of the directory entries is reached.
81*cef8759bSmrg   // Reports errors by throwing.
advancefs::_Dir82*cef8759bSmrg   bool advance(bool skip_permission_denied = false)
83*cef8759bSmrg   {
84*cef8759bSmrg     error_code ec;
85*cef8759bSmrg     const bool ok = advance(skip_permission_denied, ec);
86*cef8759bSmrg     if (ec)
87*cef8759bSmrg       _GLIBCXX_THROW_OR_ABORT(filesystem_error(
88*cef8759bSmrg 	      "directory iterator cannot advance", ec));
89*cef8759bSmrg     return ok;
90*cef8759bSmrg   }
91*cef8759bSmrg 
should_recursefs::_Dir92*cef8759bSmrg   bool should_recurse(bool follow_symlink, error_code& ec) const
93*cef8759bSmrg   {
94*cef8759bSmrg     file_type type = entry._M_type;
95*cef8759bSmrg     if (type == file_type::none || type == file_type::unknown)
96*cef8759bSmrg     {
97*cef8759bSmrg       type = entry.symlink_status(ec).type();
98*cef8759bSmrg       if (ec)
99*cef8759bSmrg 	return false;
100*cef8759bSmrg     }
101*cef8759bSmrg 
102*cef8759bSmrg     if (type == file_type::directory)
103*cef8759bSmrg       return true;
104*cef8759bSmrg     if (type == file_type::symlink)
105*cef8759bSmrg       return follow_symlink && is_directory(entry.status(ec));
106*cef8759bSmrg     return false;
107*cef8759bSmrg   }
108*cef8759bSmrg 
109*cef8759bSmrg   fs::path		path;
110*cef8759bSmrg   directory_entry	entry;
111*cef8759bSmrg };
112*cef8759bSmrg 
113*cef8759bSmrg namespace
114*cef8759bSmrg {
115*cef8759bSmrg   template<typename Bitmask>
116*cef8759bSmrg     inline bool
is_set(Bitmask obj,Bitmask bits)117*cef8759bSmrg     is_set(Bitmask obj, Bitmask bits)
118*cef8759bSmrg     {
119*cef8759bSmrg       return (obj & bits) != Bitmask::none;
120*cef8759bSmrg     }
121*cef8759bSmrg }
122*cef8759bSmrg 
123*cef8759bSmrg fs::directory_iterator::
directory_iterator(const path & p,directory_options options,error_code * ecptr)124*cef8759bSmrg directory_iterator(const path& p, directory_options options, error_code* ecptr)
125*cef8759bSmrg {
126*cef8759bSmrg   const bool skip_permission_denied
127*cef8759bSmrg     = is_set(options, directory_options::skip_permission_denied);
128*cef8759bSmrg 
129*cef8759bSmrg   error_code ec;
130*cef8759bSmrg   _Dir dir(p, skip_permission_denied, ec);
131*cef8759bSmrg 
132*cef8759bSmrg   if (dir.dirp)
133*cef8759bSmrg     {
134*cef8759bSmrg       auto sp = std::make_shared<fs::_Dir>(std::move(dir));
135*cef8759bSmrg       if (sp->advance(skip_permission_denied, ec))
136*cef8759bSmrg 	_M_dir.swap(sp);
137*cef8759bSmrg     }
138*cef8759bSmrg   if (ecptr)
139*cef8759bSmrg     *ecptr = ec;
140*cef8759bSmrg   else if (ec)
141*cef8759bSmrg     _GLIBCXX_THROW_OR_ABORT(fs::filesystem_error(
142*cef8759bSmrg 	  "directory iterator cannot open directory", p, ec));
143*cef8759bSmrg }
144*cef8759bSmrg 
145*cef8759bSmrg const fs::directory_entry&
operator *() const146*cef8759bSmrg fs::directory_iterator::operator*() const
147*cef8759bSmrg {
148*cef8759bSmrg   if (!_M_dir)
149*cef8759bSmrg     _GLIBCXX_THROW_OR_ABORT(filesystem_error(
150*cef8759bSmrg 	  "non-dereferenceable directory iterator",
151*cef8759bSmrg 	  std::make_error_code(errc::invalid_argument)));
152*cef8759bSmrg   return _M_dir->entry;
153*cef8759bSmrg }
154*cef8759bSmrg 
155*cef8759bSmrg fs::directory_iterator&
operator ++()156*cef8759bSmrg fs::directory_iterator::operator++()
157*cef8759bSmrg {
158*cef8759bSmrg   if (!_M_dir)
159*cef8759bSmrg     _GLIBCXX_THROW_OR_ABORT(filesystem_error(
160*cef8759bSmrg 	  "cannot advance non-dereferenceable directory iterator",
161*cef8759bSmrg 	  std::make_error_code(errc::invalid_argument)));
162*cef8759bSmrg   if (!_M_dir->advance())
163*cef8759bSmrg     _M_dir.reset();
164*cef8759bSmrg   return *this;
165*cef8759bSmrg }
166*cef8759bSmrg 
167*cef8759bSmrg fs::directory_iterator&
increment(error_code & ec)168*cef8759bSmrg fs::directory_iterator::increment(error_code& ec)
169*cef8759bSmrg {
170*cef8759bSmrg   if (!_M_dir)
171*cef8759bSmrg     {
172*cef8759bSmrg       ec = std::make_error_code(errc::invalid_argument);
173*cef8759bSmrg       return *this;
174*cef8759bSmrg     }
175*cef8759bSmrg   if (!_M_dir->advance(ec))
176*cef8759bSmrg     _M_dir.reset();
177*cef8759bSmrg   return *this;
178*cef8759bSmrg }
179*cef8759bSmrg 
180*cef8759bSmrg struct fs::recursive_directory_iterator::_Dir_stack : std::stack<_Dir>
181*cef8759bSmrg {
clearfs::recursive_directory_iterator::_Dir_stack182*cef8759bSmrg   void clear() { c.clear(); }
183*cef8759bSmrg };
184*cef8759bSmrg 
185*cef8759bSmrg fs::recursive_directory_iterator::
recursive_directory_iterator(const path & p,directory_options options,error_code * ecptr)186*cef8759bSmrg recursive_directory_iterator(const path& p, directory_options options,
187*cef8759bSmrg                              error_code* ecptr)
188*cef8759bSmrg : _M_options(options), _M_pending(true)
189*cef8759bSmrg {
190*cef8759bSmrg   if (DIR* dirp = ::opendir(p.c_str()))
191*cef8759bSmrg     {
192*cef8759bSmrg       if (ecptr)
193*cef8759bSmrg 	ecptr->clear();
194*cef8759bSmrg       auto sp = std::make_shared<_Dir_stack>();
195*cef8759bSmrg       sp->push(_Dir{ dirp, p });
196*cef8759bSmrg       if (ecptr ? sp->top().advance(*ecptr) : sp->top().advance())
197*cef8759bSmrg 	_M_dirs.swap(sp);
198*cef8759bSmrg     }
199*cef8759bSmrg   else
200*cef8759bSmrg     {
201*cef8759bSmrg       const int err = errno;
202*cef8759bSmrg       if (err == EACCES
203*cef8759bSmrg 	  && is_set(options, fs::directory_options::skip_permission_denied))
204*cef8759bSmrg 	{
205*cef8759bSmrg 	  if (ecptr)
206*cef8759bSmrg 	    ecptr->clear();
207*cef8759bSmrg 	  return;
208*cef8759bSmrg 	}
209*cef8759bSmrg 
210*cef8759bSmrg       if (!ecptr)
211*cef8759bSmrg 	_GLIBCXX_THROW_OR_ABORT(filesystem_error(
212*cef8759bSmrg 	      "recursive directory iterator cannot open directory", p,
213*cef8759bSmrg 	      std::error_code(err, std::generic_category())));
214*cef8759bSmrg 
215*cef8759bSmrg       ecptr->assign(err, std::generic_category());
216*cef8759bSmrg     }
217*cef8759bSmrg }
218*cef8759bSmrg 
219*cef8759bSmrg fs::recursive_directory_iterator::~recursive_directory_iterator() = default;
220*cef8759bSmrg 
221*cef8759bSmrg int
depth() const222*cef8759bSmrg fs::recursive_directory_iterator::depth() const
223*cef8759bSmrg {
224*cef8759bSmrg   return int(_M_dirs->size()) - 1;
225*cef8759bSmrg }
226*cef8759bSmrg 
227*cef8759bSmrg const fs::directory_entry&
operator *() const228*cef8759bSmrg fs::recursive_directory_iterator::operator*() const
229*cef8759bSmrg {
230*cef8759bSmrg   return _M_dirs->top().entry;
231*cef8759bSmrg }
232*cef8759bSmrg 
233*cef8759bSmrg fs::recursive_directory_iterator&
234*cef8759bSmrg fs::recursive_directory_iterator::
235*cef8759bSmrg operator=(const recursive_directory_iterator& other) noexcept = default;
236*cef8759bSmrg 
237*cef8759bSmrg fs::recursive_directory_iterator&
238*cef8759bSmrg fs::recursive_directory_iterator::
239*cef8759bSmrg operator=(recursive_directory_iterator&& other) noexcept = default;
240*cef8759bSmrg 
241*cef8759bSmrg fs::recursive_directory_iterator&
operator ++()242*cef8759bSmrg fs::recursive_directory_iterator::operator++()
243*cef8759bSmrg {
244*cef8759bSmrg   error_code ec;
245*cef8759bSmrg   increment(ec);
246*cef8759bSmrg   if (ec)
247*cef8759bSmrg     _GLIBCXX_THROW_OR_ABORT(filesystem_error(
248*cef8759bSmrg 	  "cannot increment recursive directory iterator", ec));
249*cef8759bSmrg   return *this;
250*cef8759bSmrg }
251*cef8759bSmrg 
252*cef8759bSmrg fs::recursive_directory_iterator&
increment(error_code & ec)253*cef8759bSmrg fs::recursive_directory_iterator::increment(error_code& ec)
254*cef8759bSmrg {
255*cef8759bSmrg   if (!_M_dirs)
256*cef8759bSmrg     {
257*cef8759bSmrg       ec = std::make_error_code(errc::invalid_argument);
258*cef8759bSmrg       return *this;
259*cef8759bSmrg     }
260*cef8759bSmrg 
261*cef8759bSmrg   const bool follow
262*cef8759bSmrg     = is_set(_M_options, directory_options::follow_directory_symlink);
263*cef8759bSmrg   const bool skip_permission_denied
264*cef8759bSmrg     = is_set(_M_options, directory_options::skip_permission_denied);
265*cef8759bSmrg 
266*cef8759bSmrg   auto& top = _M_dirs->top();
267*cef8759bSmrg 
268*cef8759bSmrg   if (std::exchange(_M_pending, true) && top.should_recurse(follow, ec))
269*cef8759bSmrg     {
270*cef8759bSmrg       _Dir dir(top.entry.path(), skip_permission_denied, ec);
271*cef8759bSmrg       if (ec)
272*cef8759bSmrg 	{
273*cef8759bSmrg 	  _M_dirs.reset();
274*cef8759bSmrg 	  return *this;
275*cef8759bSmrg 	}
276*cef8759bSmrg       if (dir.dirp)
277*cef8759bSmrg 	  _M_dirs->push(std::move(dir));
278*cef8759bSmrg     }
279*cef8759bSmrg 
280*cef8759bSmrg   while (!_M_dirs->top().advance(skip_permission_denied, ec) && !ec)
281*cef8759bSmrg     {
282*cef8759bSmrg       _M_dirs->pop();
283*cef8759bSmrg       if (_M_dirs->empty())
284*cef8759bSmrg 	{
285*cef8759bSmrg 	  _M_dirs.reset();
286*cef8759bSmrg 	  return *this;
287*cef8759bSmrg 	}
288*cef8759bSmrg     }
289*cef8759bSmrg   return *this;
290*cef8759bSmrg }
291*cef8759bSmrg 
292*cef8759bSmrg void
pop(error_code & ec)293*cef8759bSmrg fs::recursive_directory_iterator::pop(error_code& ec)
294*cef8759bSmrg {
295*cef8759bSmrg   if (!_M_dirs)
296*cef8759bSmrg     {
297*cef8759bSmrg       ec = std::make_error_code(errc::invalid_argument);
298*cef8759bSmrg       return;
299*cef8759bSmrg     }
300*cef8759bSmrg 
301*cef8759bSmrg   const bool skip_permission_denied
302*cef8759bSmrg     = is_set(_M_options, directory_options::skip_permission_denied);
303*cef8759bSmrg 
304*cef8759bSmrg   do {
305*cef8759bSmrg     _M_dirs->pop();
306*cef8759bSmrg     if (_M_dirs->empty())
307*cef8759bSmrg       {
308*cef8759bSmrg 	_M_dirs.reset();
309*cef8759bSmrg 	ec.clear();
310*cef8759bSmrg 	return;
311*cef8759bSmrg       }
312*cef8759bSmrg   } while (!_M_dirs->top().advance(skip_permission_denied, ec));
313*cef8759bSmrg }
314*cef8759bSmrg 
315*cef8759bSmrg void
pop()316*cef8759bSmrg fs::recursive_directory_iterator::pop()
317*cef8759bSmrg {
318*cef8759bSmrg   error_code ec;
319*cef8759bSmrg   pop(ec);
320*cef8759bSmrg   if (ec)
321*cef8759bSmrg     _GLIBCXX_THROW_OR_ABORT(filesystem_error(_M_dirs
322*cef8759bSmrg 	  ? "recursive directory iterator cannot pop"
323*cef8759bSmrg 	  : "non-dereferenceable recursive directory iterator cannot pop",
324*cef8759bSmrg 	  ec));
325*cef8759bSmrg }
326