LWG2720 implement filesystem::perms::symlink_nofollow
authorJonathan Wakely <jwakely@redhat.com>
Fri, 21 Oct 2016 17:01:05 +0000 (18:01 +0100)
committerJonathan Wakely <redi@gcc.gnu.org>
Fri, 21 Oct 2016 17:01:05 +0000 (18:01 +0100)
* 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.

From-SVN: r241418

libstdc++-v3/ChangeLog
libstdc++-v3/include/experimental/bits/fs_fwd.h
libstdc++-v3/src/filesystem/ops.cc
libstdc++-v3/testsuite/experimental/filesystem/operations/create_symlink.cc [new file with mode: 0644]
libstdc++-v3/testsuite/experimental/filesystem/operations/permissions.cc

index d4250ee529a023076b35ce81d3b1a50ab218c6fb..fd79decb4d8ac30d0491274d47557d095854a40a 100644 (file)
@@ -1,5 +1,14 @@
 2016-10-21  Jonathan Wakely  <jwakely@redhat.com>
 
+       * 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.
+
        * include/experimental/bits/fs_ops.h
        (exists(const path&, error_code&)): Clear error if status is known
        (LWG 2725).
index 1c08b197ddad75adac381e63f5b70e7e2ac72abb..fb8521a800190920e48aeba16df98c67bf29adaa 100644 (file)
@@ -162,7 +162,7 @@ _GLIBCXX_END_NAMESPACE_CXX11
       unknown          =  0xFFFF,
       add_perms                = 0x10000,
       remove_perms     = 0x20000,
-      resolve_symlinks = 0x40000
+      symlink_nofollow = 0x40000
   };
 
   constexpr perms
index 6b38584bff8267f1a49f447ef897df62bf0e62c1..68343a938249bb9a3acca59f296ede76d2fd955b 100644 (file)
@@ -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 (file)
index 0000000..7297259
--- /dev/null
@@ -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();
+}
index 07e83664a6746f7b204a9f849220fed5ac49f0e3..839cfefa8827bf400e288d5c6e299710822b3dfc 100644 (file)
@@ -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();
 }