diff mbox

Three patches for std::experimental::filesystem

Message ID 20161021170105.GY2922@redhat.com
State New
Headers show

Commit Message

Jonathan Wakely Oct. 21, 2016, 5:01 p.m. UTC
This implements some DR resolutions for the filesystem lib.

Tested x86_64-linux, committed to trunk.
diff mbox

Patch

commit 03db1baaa50ea8d97b4442fffaae4e68a03eebad
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Thu Oct 20 19:26:47 2016 +0100

    LWG2720 implement filesystem::perms::symlink_nofollow
    
    	* include/experimental/bits/fs_fwd.h (perms::resolve_symlinks):
    	Replace with symlink_nofollow (LWG 2720).
    	* src/filesystem/ops.cc (permissions(const path&, perms, error_code&)):
    	Handle symlink_nofollow.
    	* testsuite/experimental/filesystem/operations/create_symlink.cc: New
    	test.
    	* testsuite/experimental/filesystem/operations/permissions.cc: Test
    	overload taking error_code.

diff --git a/libstdc++-v3/include/experimental/bits/fs_fwd.h b/libstdc++-v3/include/experimental/bits/fs_fwd.h
index 1c08b19..fb8521a 100644
--- a/libstdc++-v3/include/experimental/bits/fs_fwd.h
+++ b/libstdc++-v3/include/experimental/bits/fs_fwd.h
@@ -162,7 +162,7 @@  _GLIBCXX_END_NAMESPACE_CXX11
       unknown		=  0xFFFF,
       add_perms		= 0x10000,
       remove_perms	= 0x20000,
-      resolve_symlinks	= 0x40000
+      symlink_nofollow	= 0x40000
   };
 
   constexpr perms
diff --git a/libstdc++-v3/src/filesystem/ops.cc b/libstdc++-v3/src/filesystem/ops.cc
index 6b38584..68343a9 100644
--- a/libstdc++-v3/src/filesystem/ops.cc
+++ b/libstdc++-v3/src/filesystem/ops.cc
@@ -1101,6 +1101,7 @@  void fs::permissions(const path& p, perms prms, error_code& ec) noexcept
 {
   const bool add = is_set(prms, perms::add_perms);
   const bool remove = is_set(prms, perms::remove_perms);
+  const bool nofollow = is_set(prms, perms::symlink_nofollow);
   if (add && remove)
     {
       ec = std::make_error_code(std::errc::invalid_argument);
@@ -1111,7 +1112,7 @@  void fs::permissions(const path& p, perms prms, error_code& ec) noexcept
 
   if (add || remove)
     {
-      auto st = status(p, ec);
+      auto st = nofollow ? symlink_status(p, ec) : status(p, ec);
       if (ec)
 	return;
       auto curr = st.permissions();
@@ -1122,9 +1123,12 @@  void fs::permissions(const path& p, perms prms, error_code& ec) noexcept
     }
 
 #if _GLIBCXX_USE_FCHMODAT
-  if (::fchmodat(AT_FDCWD, p.c_str(), static_cast<mode_t>(prms), 0))
+  const int flag = nofollow ? AT_SYMLINK_NOFOLLOW : 0;
+  if (::fchmodat(AT_FDCWD, p.c_str(), static_cast<mode_t>(prms), flag))
 #else
-  if (::chmod(p.c_str(), static_cast<mode_t>(prms)))
+  if (nofollow)
+    ec = std::make_error_code(std::errc::operation_not_supported);
+  else if (::chmod(p.c_str(), static_cast<mode_t>(prms)))
 #endif
     ec.assign(errno, std::generic_category());
   else
diff --git a/libstdc++-v3/testsuite/experimental/filesystem/operations/create_symlink.cc b/libstdc++-v3/testsuite/experimental/filesystem/operations/create_symlink.cc
new file mode 100644
index 0000000..7297259
--- /dev/null
+++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/create_symlink.cc
@@ -0,0 +1,93 @@ 
+// Copyright (C) 2016 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
+// terms of the GNU General Public License as published by the
+// Free Software Foundation; either version 3, or (at your option)
+// any later version.
+
+// This library is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+
+// You should have received a copy of the GNU General Public License along
+// with this library; see the file COPYING3.  If not see
+// <http://www.gnu.org/licenses/>.
+
+// { dg-options "-lstdc++fs" }
+// { dg-do run { target c++11 } }
+// { dg-require-filesystem-ts "" }
+
+#include <experimental/filesystem>
+#include <testsuite_hooks.h>
+#include <testsuite_fs.h>
+
+namespace fs = std::experimental::filesystem;
+
+void
+test01()
+{
+  std::error_code ec, ec2;
+  __gnu_test::scoped_file f;
+  auto tgt = f.path;
+
+  // Test empty path.
+  fs::path p;
+  create_symlink(tgt, p, ec );
+  VERIFY( ec );
+  try
+  {
+    create_symlink(tgt, p);
+  }
+  catch (const std::experimental::filesystem::filesystem_error& ex)
+  {
+    ec2 = ex.code();
+    VERIFY( ex.path1() == tgt );
+    VERIFY( ex.path2() == p );
+  }
+  VERIFY( ec2 == ec );
+}
+
+void
+test02()
+{
+  std::error_code ec, ec2;
+  __gnu_test::scoped_file f;
+  auto tgt = f.path;
+
+  // Test non-existent path
+  auto p = __gnu_test::nonexistent_path();
+  VERIFY( !exists(p) );
+
+  create_symlink(tgt, p, ec); // create the symlink once
+  VERIFY( !ec );
+  VERIFY( exists(p) );
+  VERIFY( is_symlink(p) );
+  remove(p);
+  create_symlink(tgt, p); // create the symlink again
+  VERIFY( exists(p) );
+  VERIFY( is_symlink(p) );
+
+  create_symlink(tgt, p, ec); // Try to create existing symlink
+  VERIFY( ec );
+  try
+  {
+    create_symlink(tgt, p);
+  }
+  catch (const std::experimental::filesystem::filesystem_error& ex)
+  {
+    ec2 = ex.code();
+    VERIFY( ex.path1() == tgt );
+    VERIFY( ex.path2() == p );
+  }
+  VERIFY( ec2 == ec );
+
+  remove(p);
+}
+
+int
+main()
+{
+  test01();
+}
diff --git a/libstdc++-v3/testsuite/experimental/filesystem/operations/permissions.cc b/libstdc++-v3/testsuite/experimental/filesystem/operations/permissions.cc
index 07e8366..839cfef 100644
--- a/libstdc++-v3/testsuite/experimental/filesystem/operations/permissions.cc
+++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/permissions.cc
@@ -22,7 +22,6 @@ 
 // 15.26 Permissions [fs.op.permissions]
 
 #include <experimental/filesystem>
-#include <fstream>
 #include <testsuite_fs.h>
 #include <testsuite_hooks.h>
 
@@ -32,7 +31,8 @@  test01()
   using perms = std::experimental::filesystem::perms;
 
   auto p = __gnu_test::nonexistent_path();
-  std::ofstream{p.native()};
+
+  __gnu_test::scoped_file f(p);
   VERIFY( exists(p) );
   permissions(p, perms::owner_all);
   VERIFY( status(p).permissions() == perms::owner_all );
@@ -40,6 +40,83 @@  test01()
   VERIFY( status(p).permissions() == (perms::owner_all | perms::group_read) );
   permissions(p, perms::group_read | perms::remove_perms);
   VERIFY( status(p).permissions() == perms::owner_all );
+}
+
+void
+test02()
+{
+  using perms = std::experimental::filesystem::perms;
+
+  auto p = __gnu_test::nonexistent_path();
+
+  std::error_code ec;
+  permissions(p, perms::owner_all, ec);
+  VERIFY( ec );
+
+  __gnu_test::scoped_file f(p);
+  VERIFY( exists(p) );
+
+  ec = std::make_error_code(std::errc::invalid_argument);
+  permissions(p, perms::owner_all, ec);
+  VERIFY( !ec );
+  VERIFY( status(p).permissions() == perms::owner_all );
+  permissions(p, perms::group_read | perms::add_perms, ec);
+  VERIFY( !ec );
+  VERIFY( status(p).permissions() == (perms::owner_all | perms::group_read) );
+  permissions(p, perms::group_read | perms::remove_perms, ec);
+  VERIFY( !ec );
+  VERIFY( status(p).permissions() == perms::owner_all );
+}
+
+void
+test03()
+{
+  using perms = std::experimental::filesystem::perms;
+
+  __gnu_test::scoped_file f;
+  VERIFY( exists(f.path) );
+
+  auto p = __gnu_test::nonexistent_path();
+  create_symlink(f.path, p);
+
+  std::error_code ec, ec2;
+  permissions(p, perms::owner_all | perms::symlink_nofollow, ec);
+  try
+  {
+    permissions(p, perms::owner_all | perms::symlink_nofollow);
+  }
+  catch (const std::experimental::filesystem::filesystem_error& ex)
+  {
+    ec2 = ex.code();
+    VERIFY( ex.path1() == p );
+  }
+  // Both calls should succeed, or both should fail with same error:
+  VERIFY( ec == ec2 );
+
+  remove(p);
+}
+
+void
+test04()
+{
+  using perms = std::experimental::filesystem::perms;
+
+  auto p = __gnu_test::nonexistent_path();
+  create_symlink(__gnu_test::nonexistent_path(), p);
+
+  std::error_code ec, ec2;
+  permissions(p, perms::owner_all, ec);
+  VERIFY( ec );
+  try
+  {
+    permissions(p, perms::owner_all);
+  }
+  catch (const std::experimental::filesystem::filesystem_error& ex)
+  {
+    ec2 = ex.code();
+    VERIFY( ex.path1() == p );
+  }
+  VERIFY( ec == ec2 );
 
   remove(p);
 }
@@ -48,4 +125,7 @@  int
 main()
 {
   test01();
+  test02();
+  test03();
+  test04();
 }

commit 77389ec047bffaa2a546d16840df4f89ecc04570
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Thu Oct 20 19:16:23 2016 +0100

    LWG2725 Fix error reporting for filesystem::exists
    
    	* include/experimental/bits/fs_ops.h
    	(exists(const path&, error_code&)): Clear error if status is known
    	(LWG 2725).
    	(status(const path&, error_code&)): Handle EOVERFLOW.
    	* testsuite/experimental/filesystem/operations/exists.cc: Test
    	overload taking an error_code.

diff --git a/libstdc++-v3/include/experimental/bits/fs_ops.h b/libstdc++-v3/include/experimental/bits/fs_ops.h
index 8506b09..62a9826 100644
--- a/libstdc++-v3/include/experimental/bits/fs_ops.h
+++ b/libstdc++-v3/include/experimental/bits/fs_ops.h
@@ -112,6 +112,12 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
   void current_path(const path& __p);
   void current_path(const path& __p, error_code& __ec) noexcept;
 
+  bool
+  equivalent(const path& __p1, const path& __p2);
+
+  bool
+  equivalent(const path& __p1, const path& __p2, error_code& __ec) noexcept;
+
   inline bool
   exists(file_status __s) noexcept
   { return status_known(__s) && __s.type() != file_type::not_found; }
@@ -122,13 +128,12 @@  _GLIBCXX_BEGIN_NAMESPACE_VERSION
 
   inline bool
   exists(const path& __p, error_code& __ec) noexcept
-  { return exists(status(__p, __ec)); }
-
-  bool
-  equivalent(const path& __p1, const path& __p2);
-
-  bool
-  equivalent(const path& __p1, const path& __p2, error_code& __ec) noexcept;
+  {
+    auto __s = status(__p, __ec);
+    if (status_known(__s))
+      __ec.clear();
+    return exists(__s);
+  }
 
   uintmax_t file_size(const path& __p);
   uintmax_t file_size(const path& __p, error_code& __ec) noexcept;
diff --git a/libstdc++-v3/src/filesystem/ops.cc b/libstdc++-v3/src/filesystem/ops.cc
index 659cfbb..6b38584 100644
--- a/libstdc++-v3/src/filesystem/ops.cc
+++ b/libstdc++-v3/src/filesystem/ops.cc
@@ -1297,7 +1297,7 @@  fs::space(const path& p, error_code& ec) noexcept
 
 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
 fs::file_status
-fs::status(const fs::path& p, std::error_code& ec) noexcept
+fs::status(const fs::path& p, error_code& ec) noexcept
 {
   file_status status;
   stat_type st;
@@ -1307,6 +1307,10 @@  fs::status(const fs::path& p, std::error_code& ec) noexcept
       ec.assign(err, std::generic_category());
       if (is_not_found_errno(err))
 	status.type(file_type::not_found);
+#ifdef EOVERFLOW
+      else if (err == EOVERFLOW)
+	status.type(file_type::unknown);
+#endif
     }
   else
     {
diff --git a/libstdc++-v3/testsuite/experimental/filesystem/operations/exists.cc b/libstdc++-v3/testsuite/experimental/filesystem/operations/exists.cc
index 0eaa671..7dbc8e9 100644
--- a/libstdc++-v3/testsuite/experimental/filesystem/operations/exists.cc
+++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/exists.cc
@@ -33,6 +33,18 @@  test01()
   VERIFY( exists(path{"."}) );
   VERIFY( exists(path{".."}) );
   VERIFY( exists(std::experimental::filesystem::current_path()) );
+
+  std::error_code ec = std::make_error_code(std::errc::invalid_argument);
+  VERIFY( exists(path{"/"}, ec) );
+  VERIFY( !ec );
+  VERIFY( exists(path{"/."}, ec) );
+  VERIFY( !ec );
+  VERIFY( exists(path{"."}, ec) );
+  VERIFY( !ec );
+  VERIFY( exists(path{".."}, ec) );
+  VERIFY( !ec );
+  VERIFY( exists(std::experimental::filesystem::current_path(), ec) );
+  VERIFY( !ec );
 }
 
 void
@@ -40,6 +52,10 @@  test02()
 {
   path rel = __gnu_test::nonexistent_path();
   VERIFY( !exists(rel) );
+
+  std::error_code ec = std::make_error_code(std::errc::invalid_argument);
+  VERIFY( !exists(rel, ec) );
+  VERIFY( !ec ); // DR 2725
 }
 
 void
@@ -47,6 +63,38 @@  test03()
 {
   path abs = absolute(__gnu_test::nonexistent_path());
   VERIFY( !exists(abs) );
+
+  std::error_code ec = std::make_error_code(std::errc::invalid_argument);
+  VERIFY( !exists(abs, ec) );
+  VERIFY( !ec ); // DR 2725
+}
+
+void
+test04()
+{
+  using perms = std::experimental::filesystem::perms;
+  path p = __gnu_test::nonexistent_path();
+  create_directory(p);
+  permissions(p, perms::all | perms::remove_perms);
+
+  auto unr = p / "unreachable";
+  std::error_code ec;
+  VERIFY( !exists(unr, ec) );
+  VERIFY( ec == std::errc::permission_denied );
+  ec.clear();
+  try
+  {
+    exists(unr);
+  }
+  catch(const std::experimental::filesystem::filesystem_error& ex)
+  {
+    ec = ex.code();
+    VERIFY( ex.path1() == unr );
+  }
+  VERIFY( ec == std::errc::permission_denied );
+
+  permissions(p, perms::owner_all);
+  remove(p);
 }
 
 int
@@ -55,4 +103,5 @@  main()
   test01();
   test02();
   test03();
+  test04();
 }

commit 9f5d09ce67a7a846440df5b571aa17435af7b7cc
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Thu Oct 20 19:05:06 2016 +0100

    LWG2707 init filesystem::path from string_type&&
    
    	* include/experimental/bits/fs_path.h (path::path(string_type&&))
    	(path::operator=(string&&), path::assign(string_type&&)): Define
    	construction and assignment from string_type rvalues (LWG 2707).

diff --git a/libstdc++-v3/include/experimental/bits/fs_path.h b/libstdc++-v3/include/experimental/bits/fs_path.h
index dab8ef0..4d7291f 100644
--- a/libstdc++-v3/include/experimental/bits/fs_path.h
+++ b/libstdc++-v3/include/experimental/bits/fs_path.h
@@ -159,6 +159,10 @@  _GLIBCXX_BEGIN_NAMESPACE_CXX11
       __p.clear();
     }
 
+    path(string_type&& __source)
+    : _M_pathname(std::move(__source))
+    { _M_split_cmpts(); }
+
     template<typename _Source,
 	     typename _Require = _Path<_Source>>
       path(_Source const& __source)
@@ -193,6 +197,8 @@  _GLIBCXX_BEGIN_NAMESPACE_CXX11
 
     path& operator=(const path& __p) = default;
     path& operator=(path&& __p) noexcept;
+    path& operator=(string_type&& __source);
+    path& assign(string_type&& __source);
 
     template<typename _Source>
       _Path<_Source>&
@@ -722,6 +728,14 @@  _GLIBCXX_BEGIN_NAMESPACE_CXX11
   }
 
   inline path&
+  path::operator=(string_type&& __source)
+  { return *this = path(std::move(__source)); }
+
+  inline path&
+  path::assign(string_type&& __source)
+  { return *this = path(std::move(__source)); }
+
+  inline path&
   path::operator+=(const path& __p)
   {
     return operator+=(__p.native());