From d17f7088fbe9b7d159021d0442d174925df7b20d Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Fri, 21 Oct 2016 18:01:05 +0100 Subject: [PATCH] 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. From-SVN: r241418 --- libstdc++-v3/ChangeLog | 9 ++ .../include/experimental/bits/fs_fwd.h | 2 +- libstdc++-v3/src/filesystem/ops.cc | 10 +- .../filesystem/operations/create_symlink.cc | 93 +++++++++++++++++++ .../filesystem/operations/permissions.cc | 84 ++++++++++++++++- 5 files changed, 192 insertions(+), 6 deletions(-) create mode 100644 libstdc++-v3/testsuite/experimental/filesystem/operations/create_symlink.cc diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index d4250ee529a..fd79decb4d8 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,5 +1,14 @@ 2016-10-21 Jonathan Wakely + * 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). diff --git a/libstdc++-v3/include/experimental/bits/fs_fwd.h b/libstdc++-v3/include/experimental/bits/fs_fwd.h index 1c08b197dda..fb8521a8001 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 6b38584bff8..68343a93824 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(prms), 0)) + const int flag = nofollow ? AT_SYMLINK_NOFOLLOW : 0; + if (::fchmodat(AT_FDCWD, p.c_str(), static_cast(prms), flag)) #else - if (::chmod(p.c_str(), static_cast(prms))) + if (nofollow) + ec = std::make_error_code(std::errc::operation_not_supported); + else if (::chmod(p.c_str(), static_cast(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 00000000000..7297259d8c0 --- /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 +// . + +// { dg-options "-lstdc++fs" } +// { dg-do run { target c++11 } } +// { dg-require-filesystem-ts "" } + +#include +#include +#include + +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 07e83664a67..839cfefa882 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 -#include #include #include @@ -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(); } -- 2.30.2