]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - libstdc++-v3/src/filesystem/dir.cc
Update copyright years.
[thirdparty/gcc.git] / libstdc++-v3 / src / filesystem / dir.cc
index bce751c3fb1d843e6eb474dc93278c3e7fb49065..acc62986c68e5cb732d0632f86aeed5892ff0d82 100644 (file)
@@ -1,6 +1,6 @@
 // Class filesystem::directory_entry etc. -*- C++ -*-
 
-// Copyright (C) 2014-2015 Free Software Foundation, Inc.
+// Copyright (C) 2014-2021 Free Software Foundation, Inc.
 //
 // This file is part of the GNU ISO C++ Library.  This library is free
 // software; you can redistribute it and/or modify it under the
 // see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
 // <http://www.gnu.org/licenses/>.
 
+#ifndef _GLIBCXX_USE_CXX11_ABI
+# define _GLIBCXX_USE_CXX11_ABI 1
+#endif
+
+#include <bits/largefile-config.h>
 #include <experimental/filesystem>
+
+#ifndef _GLIBCXX_HAVE_DIRENT_H
+# error "the <dirent.h> header is needed to build the Filesystem TS"
+#endif
+
 #include <utility>
 #include <stack>
 #include <string.h>
 #include <errno.h>
-#ifdef _GLIBCXX_HAVE_DIRENT_H
-# ifdef _GLIBCXX_HAVE_SYS_TYPES_H
-#  include <sys/types.h>
-# endif
-# include <dirent.h>
-#else
-# error "the <dirent.h> header is needed to build the Filesystem TS"
-#endif
-
-#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
-# undef opendir
-# define opendir _wopendir
-#endif
+#define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM \
+  namespace experimental { namespace filesystem {
+#define _GLIBCXX_END_NAMESPACE_FILESYSTEM } }
+#include "dir-common.h"
 
 namespace fs = std::experimental::filesystem;
+namespace posix = std::filesystem::__gnu_posix;
 
-struct fs::_Dir
+struct fs::_Dir : std::filesystem::_Dir_base
 {
-  _Dir() : dirp(nullptr) { }
-
-  _Dir(DIR* dirp, const fs::path& path) : dirp(dirp), path(path) { }
-
-  _Dir(_Dir&& d)
-  : dirp(std::exchange(d.dirp, nullptr)), path(std::move(d.path)),
-    entry(std::move(d.entry)), type(d.type)
-  { }
-
-  _Dir& operator=(_Dir&&) = delete;
+  _Dir(const fs::path& p, bool skip_permission_denied, error_code& ec)
+  : _Dir_base(p.c_str(), skip_permission_denied, ec)
+  {
+    if (!ec)
+      path = p;
+  }
 
-  ~_Dir() { if (dirp) ::closedir(dirp); }
+  _Dir(posix::DIR* dirp, const path& p) : _Dir_base(dirp), path(p) { }
 
-  bool advance(std::error_code*, directory_options = directory_options::none);
+  _Dir(_Dir&&) = default;
 
-  DIR*                 dirp;
-  fs::path             path;
-  directory_entry      entry;
-  file_type            type = file_type::none;
-};
+  // Returns false when the end of the directory entries is reached.
+  // Reports errors by setting ec.
+  bool advance(bool skip_permission_denied, error_code& ec) noexcept
+  {
+    if (const auto entp = _Dir_base::advance(skip_permission_denied, ec))
+      {
+       entry = fs::directory_entry{path / entp->d_name};
+       type = get_file_type(*entp);
+       return true;
+      }
+    else if (!ec)
+      {
+       // reached the end
+       entry = {};
+       type = file_type::none;
+      }
+    return false;
+  }
 
-namespace
-{
-  template<typename Bitmask>
-    inline bool is_set(Bitmask obj, Bitmask bits)
-    {
-      return (obj & bits) != Bitmask::none;
-    }
+  bool advance(error_code& ec) noexcept { return advance(false, ec); }
 
-  // Returns {dirp, p} on success, {nullptr, p} on error.
-  // If an ignored EACCES error occurs returns {}.
-  fs::_Dir
-  open_dir(const fs::path& p, fs::directory_options options, std::error_code* ec)
+  // Returns false when the end of the directory entries is reached.
+  // Reports errors by throwing.
+  bool advance(bool skip_permission_denied = false)
   {
+    error_code ec;
+    const bool ok = advance(skip_permission_denied, ec);
     if (ec)
-      ec->clear();
-
-    if (DIR* dirp = ::opendir(p.c_str()))
-      return {dirp, p};
-
-    const int err = errno;
-    if (err == EACCES
-        && is_set(options, fs::directory_options::skip_permission_denied))
-      return {};
-
-    if (!ec)
-      _GLIBCXX_THROW_OR_ABORT(fs::filesystem_error(
-            "directory iterator cannot open directory", p,
-            std::error_code(err, std::generic_category())));
-
-    ec->assign(err, std::generic_category());
-    return {nullptr, p};
+      _GLIBCXX_THROW_OR_ABORT(filesystem_error(
+             "directory iterator cannot advance", ec));
+    return ok;
   }
 
-  inline fs::file_type
-  get_file_type(const dirent& d __attribute__((__unused__)))
+  bool should_recurse(bool follow_symlink, error_code& ec) const
   {
-#ifdef _GLIBCXX_HAVE_STRUCT_DIRENT_D_TYPE
-    switch (d.d_type)
+    file_type type = this->type;
+    if (type == file_type::none || type == file_type::unknown)
     {
-    case DT_BLK:
-      return fs::file_type::block;
-    case DT_CHR:
-      return fs::file_type::character;
-    case DT_DIR:
-      return fs::file_type::directory;
-    case DT_FIFO:
-      return fs::file_type::fifo;
-    case DT_LNK:
-      return fs::file_type::symlink;
-    case DT_REG:
-      return fs::file_type::regular;
-    case DT_SOCK:
-      return fs::file_type::socket;
-    case DT_UNKNOWN:
-      return fs::file_type::unknown;
-    default:
-      return fs::file_type::none;
+      type = entry.symlink_status(ec).type();
+      if (ec)
+       return false;
     }
-#else
-    return fs::file_type::none;
-#endif
-  }
 
-  int
-  native_readdir(DIR* dirp, ::dirent*& entryp)
-  {
-#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
-    if ((entryp = ::readdir(dirp)))
-      return 0;
-    return errno;
-#else
-    return ::readdir_r(dirp, entryp, &entryp);
-#endif
+    if (type == file_type::directory)
+      return true;
+    if (type == file_type::symlink)
+      return follow_symlink && is_directory(entry.status(ec));
+    return false;
   }
-}
 
-// Returns false when the end of the directory entries is reached.
-// Reports errors by setting ec or throwing.
-bool
-fs::_Dir::advance(error_code* ec, directory_options options)
-{
-  if (ec)
-    ec->clear();
-
-  ::dirent ent;
-  ::dirent* result = &ent;
-  if (int err = native_readdir(dirp, result))
-    {
-      if (err == EACCES
-        && is_set(options, directory_options::skip_permission_denied))
-       return false;
+  fs::path             path;
+  directory_entry      entry;
+  file_type            type = file_type::none;
+};
 
-      if (!ec)
-       _GLIBCXX_THROW_OR_ABORT(filesystem_error(
-             "directory iterator cannot advance",
-             std::error_code(err, std::generic_category())));
-      ec->assign(err, std::generic_category());
-      return true;
-    }
-  else if (result != nullptr)
-    {
-      // skip past dot and dot-dot
-      if (!strcmp(ent.d_name, ".") || !strcmp(ent.d_name, ".."))
-       return advance(ec, options);
-      entry = fs::directory_entry{path / ent.d_name};
-      type = get_file_type(ent);
-      return true;
-    }
-  else
+namespace
+{
+  template<typename Bitmask>
+    inline bool
+    is_set(Bitmask obj, Bitmask bits)
     {
-      // reached the end
-      entry = {};
-      type = fs::file_type::none;
-      return false;
+      return (obj & bits) != Bitmask::none;
     }
 }
 
 fs::directory_iterator::
-directory_iterator(const path& p, directory_options options, error_code* ec)
+directory_iterator(const path& p, directory_options options, error_code* ecptr)
 {
-  _Dir dir = open_dir(p, options, ec);
+  const bool skip_permission_denied
+    = is_set(options, directory_options::skip_permission_denied);
+
+  error_code ec;
+  _Dir dir(p, skip_permission_denied, ec);
 
   if (dir.dirp)
     {
       auto sp = std::make_shared<fs::_Dir>(std::move(dir));
-      if (sp->advance(ec, options))
+      if (sp->advance(skip_permission_denied, ec))
        _M_dir.swap(sp);
     }
-  else if (!dir.path.empty())
-    {
-      // An error occurred, we need a non-empty shared_ptr so that *this will
-      // not compare equal to the end iterator.
-      _M_dir.reset(static_cast<fs::_Dir*>(nullptr));
-    }
+  if (ecptr)
+    *ecptr = ec;
+  else if (ec)
+    _GLIBCXX_THROW_OR_ABORT(fs::filesystem_error(
+         "directory iterator cannot open directory", p, ec));
 }
 
 const fs::directory_entry&
@@ -219,7 +162,7 @@ fs::directory_iterator::operator++()
     _GLIBCXX_THROW_OR_ABORT(filesystem_error(
          "cannot advance non-dereferenceable directory iterator",
          std::make_error_code(errc::invalid_argument)));
-  if (!_M_dir->advance(nullptr))
+  if (!_M_dir->advance())
     _M_dir.reset();
   return *this;
 }
@@ -232,13 +175,11 @@ fs::directory_iterator::increment(error_code& ec) noexcept
       ec = std::make_error_code(errc::invalid_argument);
       return *this;
     }
-  if (!_M_dir->advance(&ec))
+  if (!_M_dir->advance(ec))
     _M_dir.reset();
   return *this;
 }
 
-using Dir_iter_pair = std::pair<fs::_Dir, fs::directory_iterator>;
-
 struct fs::recursive_directory_iterator::_Dir_stack : std::stack<_Dir>
 {
   void clear() { c.clear(); }
@@ -246,15 +187,17 @@ struct fs::recursive_directory_iterator::_Dir_stack : std::stack<_Dir>
 
 fs::recursive_directory_iterator::
 recursive_directory_iterator(const path& p, directory_options options,
-                             error_code* ec)
+                             error_code* ecptr)
 : _M_options(options), _M_pending(true)
 {
-  if (DIR* dirp = ::opendir(p.c_str()))
+  if (posix::DIR* dirp = posix::opendir(p.c_str()))
     {
-      _M_dirs = std::make_shared<_Dir_stack>();
-      _M_dirs->push(_Dir{ dirp, p });
-      if (!_M_dirs->top().advance(ec))
-       _M_dirs.reset();
+      if (ecptr)
+       ecptr->clear();
+      auto sp = std::make_shared<_Dir_stack>();
+      sp->push(_Dir{ dirp, p });
+      if (ecptr ? sp->top().advance(*ecptr) : sp->top().advance())
+       _M_dirs.swap(sp);
     }
   else
     {
@@ -262,21 +205,17 @@ recursive_directory_iterator(const path& p, directory_options options,
       if (err == EACCES
          && is_set(options, fs::directory_options::skip_permission_denied))
        {
-         if (ec)
-           ec->clear();
+         if (ecptr)
+           ecptr->clear();
          return;
        }
 
-      if (!ec)
+      if (!ecptr)
        _GLIBCXX_THROW_OR_ABORT(filesystem_error(
              "recursive directory iterator cannot open directory", p,
              std::error_code(err, std::generic_category())));
 
-      ec->assign(err, std::generic_category());
-
-      // An error occurred, we need a non-empty shared_ptr so that *this will
-      // not compare equal to the end iterator.
-      _M_dirs.reset(static_cast<_Dir_stack*>(nullptr));
+      ecptr->assign(err, std::generic_category());
     }
 }
 
@@ -313,35 +252,6 @@ fs::recursive_directory_iterator::operator++()
   return *this;
 }
 
-namespace
-{
-  bool
-  recurse(const fs::_Dir& d, fs::directory_options options, std::error_code& ec)
-  {
-    bool follow_symlink
-      = is_set(options, fs::directory_options::follow_directory_symlink);
-#ifdef _GLIBCXX_HAVE_STRUCT_DIRENT_D_TYPE
-    if (d.type == fs::file_type::directory)
-      return true;
-    if (d.type == fs::file_type::symlink && follow_symlink)
-      return d.entry.status().type() == fs::file_type::directory;
-    if (d.type != fs::file_type::none && d.type != fs::file_type::unknown)
-      return false;
-#endif
-    const fs::path& path = d.entry.path();
-    auto type = fs::symlink_status(path, ec).type();
-    if (ec.value())
-      return false;
-    if (type == fs::file_type::symlink)
-      {
-       if (!follow_symlink)
-         return false;
-       type = fs::status(path, ec).type();
-      }
-    return type == fs::file_type::directory;
-  }
-}
-
 fs::recursive_directory_iterator&
 fs::recursive_directory_iterator::increment(error_code& ec) noexcept
 {
@@ -351,18 +261,26 @@ fs::recursive_directory_iterator::increment(error_code& ec) noexcept
       return *this;
     }
 
+  const bool follow
+    = is_set(_M_options, directory_options::follow_directory_symlink);
+  const bool skip_permission_denied
+    = is_set(_M_options, directory_options::skip_permission_denied);
+
   auto& top = _M_dirs->top();
 
-  if (std::exchange(_M_pending, true) && recurse(top, _M_options, ec))
+  if (std::exchange(_M_pending, true) && top.should_recurse(follow, ec))
     {
-      _Dir dir = open_dir(top.entry.path(), _M_options, &ec);
+      _Dir dir(top.entry.path(), skip_permission_denied, ec);
       if (ec)
-       return *this;
+       {
+         _M_dirs.reset();
+         return *this;
+       }
       if (dir.dirp)
          _M_dirs->push(std::move(dir));
     }
 
-  while (!_M_dirs->top().advance(&ec, _M_options) && !ec)
+  while (!_M_dirs->top().advance(skip_permission_denied, ec) && !ec)
     {
       _M_dirs->pop();
       if (_M_dirs->empty())
@@ -375,19 +293,36 @@ fs::recursive_directory_iterator::increment(error_code& ec) noexcept
 }
 
 void
-fs::recursive_directory_iterator::pop()
+fs::recursive_directory_iterator::pop(error_code& ec)
 {
   if (!_M_dirs)
-    _GLIBCXX_THROW_OR_ABORT(filesystem_error(
-         "cannot pop non-dereferenceable recursive directory iterator",
-         std::make_error_code(errc::invalid_argument)));
+    {
+      ec = std::make_error_code(errc::invalid_argument);
+      return;
+    }
+
+  const bool skip_permission_denied
+    = is_set(_M_options, directory_options::skip_permission_denied);
 
   do {
     _M_dirs->pop();
     if (_M_dirs->empty())
       {
        _M_dirs.reset();
+       ec.clear();
        return;
       }
-  } while (!_M_dirs->top().advance(nullptr, _M_options));
+  } while (!_M_dirs->top().advance(skip_permission_denied, ec));
+}
+
+void
+fs::recursive_directory_iterator::pop()
+{
+  error_code ec;
+  pop(ec);
+  if (ec)
+    _GLIBCXX_THROW_OR_ABORT(filesystem_error(_M_dirs
+         ? "recursive directory iterator cannot pop"
+         : "non-dereferenceable recursive directory iterator cannot pop",
+         ec));
 }