This implements some DR resolutions for the filesystem lib.
Tested x86_64-linux, committed to trunk.
commit 03db1baaa50ea8d97b4442fffaae4e68a03eebad Author: Jonathan Wakely <jwak...@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 <jwak...@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 <jwak...@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());