This syncs the std::experimental::filesystem code on gcc-6-branch to match what's on trunk, except that the C++17 std::string_view type doesn't exist on the branch, so the filesystem::path type only supports experimental::string_view.
I'm doing it as one big patch, rather than backporting 26 individual patches. This implements the following LWG DRs: 2681 2682 2683 2706 2707 2712 2720 2723 Backport from mainline PR libstdc++/70975 PR libstdc++/71337 PR libstdc++/78111 * include/experimental/bits/fs_dir.h (recursive_directory_iterator): Overload pop (LWG 2706). * include/experimental/bits/fs_fwd.h (perms::resolve_symlinks): Replace with symlink_nofollow (LWG 2720). * include/experimental/bits/fs_ops.h (exists(const path&, error_code&)): Clear error if status is known (LWG 2725). * include/experimental/bits/fs_path.h (__is_path_src) (_S_range_begin, _S_range_end): Overload to treat string_view as a Source object. (path::operator+=, path::compare): Overload for basic_string_view. (path::path(string_type&&)) (path::operator=(string&&), path::assign(string_type&&)): Define construction and assignment from string_type rvalues (LWG 2707). (path::_S_convert<_Iter>(_Iter, _Iter)): Remove cv-qualifiers from iterator's value_type. (path::_S_convert<_Iter>(_Iter __first, __null_terminated)): Likewise. Do not use operation not supported by input iterators. (path::__is_path_iter_src): Add partial specialization for const encoded character types. * src/filesystem/dir.cc (open_dir): Return same value for errors whether ignored or not. (_Dir::advance(error_code*, directory_options)): Return false on error. (directory_iterator(const path&, directory_options, error_code*)): Create end iterator on error (LWG 2723). (recursive_directory_iterator(const path&, directory_options, error_code*)): Likewise. (recursive_directory_iterator::increment): Reset state on error. (recursive_directory_iterator::pop): Define new overload. * src/filesystem/ops.cc (canonical): Set error for non-existent path. (file_time): Take error_code parameter and check for overflow. (close_fd): Remove. (do_copy_file): Report an error if source or destination is not a regular file (LWG 2712). Pass error_code in file_time calls. Just use close(3) instead of close_fd, to prevent retrying on error. Check if _GLIBCXX_USE_FCHMODAT is defined. [_GLIBCXX_USE_SENDFILE]: Fallback to read/write operations in case sendfile fails with ENOSYS or EINVAL. Pass non-null pointer to sendfile for offset argument. (copy): Update comment to refer to LWG 2681. Implement 2682 and 2683 resolutions. (equivalent): Fix error handling and result when only one file exists. (is_empty): Fix error handling. (last_write_time(const path&, error_code&)): Pass error_code in file_time calls. (last_write_time(const path&, file_time_type, error_code&)): Handle negative times correctly. (permissions(const path&, perms, error_code&)): Handle symlink_nofollow. (read_symlink): Add missing ec.clear(). (status(const path&, error_code&)): Handle EOVERFLOW. (temp_directory_path): Pass error_code argument to other filesystem operations. * testsuite/experimental/filesystem/iterators/directory_iterator.cc: Update expected behaviour on error. * testsuite/experimental/filesystem/iterators/pop.cc: New. * testsuite/experimental/filesystem/iterators/ recursive_directory_iterator.cc: Update expected behaviour on error. * testsuite/experimental/filesystem/operations/copy.cc: Update expected behaviour for copying directories with create_symlinks. Verify that error_code arguments are cleared if there's no error. Remove files created by tests. Test copying directories. * testsuite/experimental/filesystem/operations/copy_file.cc: Remove files created by tests. * testsuite/experimental/filesystem/operations/create_symlink.cc: New. * testsuite/experimental/filesystem/operations/equivalent.cc: New. * testsuite/experimental/filesystem/operations/exists.cc: Test overload taking an error_code. * testsuite/experimental/filesystem/operations/is_empty.cc: New. * testsuite/experimental/filesystem/operations/last_write_time.cc: New. * testsuite/experimental/filesystem/operations/permissions.cc: Test overload taking error_code. Test symlink_nofollow on non-symlinks. * testsuite/experimental/filesystem/operations/read_symlink.cc: New. * testsuite/experimental/filesystem/operations/remove_all.cc: New. * testsuite/experimental/filesystem/operations/temp_directory_path.cc: Add testcase for inaccessible directory. * testsuite/experimental/filesystem/path/construct/range.cc: Test construction from input iterators with const value types. * testsuite/experimental/filesystem/path/construct/string_view.cc: New. * testsuite/util/testsuite_fs.h (scoped_file): Define RAII helper. Tested x86_64-linux, committed to gcc-6-branch. I'll backport most of this to gcc-5-branch too, I think the gcc-6 patch should apply cleanly, but I'll need to adjust the tests to not use { dg-do action { target c++11 } } as that's only on trunk and gcc-6-branch.
commit 20a4577cefafe57b19990b50f37a4042837d672b Author: Jonathan Wakely <jwak...@redhat.com> Date: Mon Dec 12 13:35:06 2016 +0000 Backport all Filesystem library fixes from trunk This implements the following LWG DRs: 2681 2682 2683 2706 2707 2712 2720 2723 Backport from mainline PR libstdc++/70975 PR libstdc++/71337 PR libstdc++/78111 * include/experimental/bits/fs_dir.h (recursive_directory_iterator): Overload pop (LWG 2706). * include/experimental/bits/fs_fwd.h (perms::resolve_symlinks): Replace with symlink_nofollow (LWG 2720). * include/experimental/bits/fs_ops.h (exists(const path&, error_code&)): Clear error if status is known (LWG 2725). * include/experimental/bits/fs_path.h (__is_path_src) (_S_range_begin, _S_range_end): Overload to treat string_view as a Source object. (path::operator+=, path::compare): Overload for basic_string_view. (path::path(string_type&&)) (path::operator=(string&&), path::assign(string_type&&)): Define construction and assignment from string_type rvalues (LWG 2707). (path::_S_convert<_Iter>(_Iter, _Iter)): Remove cv-qualifiers from iterator's value_type. (path::_S_convert<_Iter>(_Iter __first, __null_terminated)): Likewise. Do not use operation not supported by input iterators. (path::__is_path_iter_src): Add partial specialization for const encoded character types. * src/filesystem/dir.cc (open_dir): Return same value for errors whether ignored or not. (_Dir::advance(error_code*, directory_options)): Return false on error. (directory_iterator(const path&, directory_options, error_code*)): Create end iterator on error (LWG 2723). (recursive_directory_iterator(const path&, directory_options, error_code*)): Likewise. (recursive_directory_iterator::increment): Reset state on error. (recursive_directory_iterator::pop): Define new overload. * src/filesystem/ops.cc (canonical): Set error for non-existent path. (file_time): Take error_code parameter and check for overflow. (close_fd): Remove. (do_copy_file): Report an error if source or destination is not a regular file (LWG 2712). Pass error_code in file_time calls. Just use close(3) instead of close_fd, to prevent retrying on error. Check if _GLIBCXX_USE_FCHMODAT is defined. [_GLIBCXX_USE_SENDFILE]: Fallback to read/write operations in case sendfile fails with ENOSYS or EINVAL. Pass non-null pointer to sendfile for offset argument. (copy): Update comment to refer to LWG 2681. Implement 2682 and 2683 resolutions. (equivalent): Fix error handling and result when only one file exists. (is_empty): Fix error handling. (last_write_time(const path&, error_code&)): Pass error_code in file_time calls. (last_write_time(const path&, file_time_type, error_code&)): Handle negative times correctly. (permissions(const path&, perms, error_code&)): Handle symlink_nofollow. (read_symlink): Add missing ec.clear(). (status(const path&, error_code&)): Handle EOVERFLOW. (temp_directory_path): Pass error_code argument to other filesystem operations. * testsuite/experimental/filesystem/iterators/directory_iterator.cc: Update expected behaviour on error. * testsuite/experimental/filesystem/iterators/pop.cc: New. * testsuite/experimental/filesystem/iterators/ recursive_directory_iterator.cc: Update expected behaviour on error. * testsuite/experimental/filesystem/operations/copy.cc: Update expected behaviour for copying directories with create_symlinks. Verify that error_code arguments are cleared if there's no error. Remove files created by tests. Test copying directories. * testsuite/experimental/filesystem/operations/copy_file.cc: Remove files created by tests. * testsuite/experimental/filesystem/operations/create_symlink.cc: New. * testsuite/experimental/filesystem/operations/equivalent.cc: New. * testsuite/experimental/filesystem/operations/exists.cc: Test overload taking an error_code. * testsuite/experimental/filesystem/operations/is_empty.cc: New. * testsuite/experimental/filesystem/operations/last_write_time.cc: New. * testsuite/experimental/filesystem/operations/permissions.cc: Test overload taking error_code. Test symlink_nofollow on non-symlinks. * testsuite/experimental/filesystem/operations/read_symlink.cc: New. * testsuite/experimental/filesystem/operations/remove_all.cc: New. * testsuite/experimental/filesystem/operations/temp_directory_path.cc: Add testcase for inaccessible directory. * testsuite/experimental/filesystem/path/construct/range.cc: Test construction from input iterators with const value types. * testsuite/experimental/filesystem/path/construct/string_view.cc: New. * testsuite/util/testsuite_fs.h (scoped_file): Define RAII helper. diff --git a/libstdc++-v3/include/experimental/bits/fs_dir.h b/libstdc++-v3/include/experimental/bits/fs_dir.h index 70a95eb..818e7ff 100644 --- a/libstdc++-v3/include/experimental/bits/fs_dir.h +++ b/libstdc++-v3/include/experimental/bits/fs_dir.h @@ -312,6 +312,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 } void pop(); + void pop(error_code&); void disable_recursion_pending() { _M_pending = false; } diff --git a/libstdc++-v3/include/experimental/bits/fs_fwd.h b/libstdc++-v3/include/experimental/bits/fs_fwd.h index e7f2eb0..45cd0ce 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/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/include/experimental/bits/fs_path.h b/libstdc++-v3/include/experimental/bits/fs_path.h index dab8ef0..153d345 100644 --- a/libstdc++-v3/include/experimental/bits/fs_path.h +++ b/libstdc++-v3/include/experimental/bits/fs_path.h @@ -44,6 +44,9 @@ #include <bits/stl_algobase.h> #include <bits/quoted_string.h> #include <bits/locale_conv.h> +#if __cplusplus >= 201402L +# include <experimental/string_view> +#endif #if defined(_WIN32) && !defined(__CYGWIN__) # define _GLIBCXX_FILESYSTEM_IS_WINDOWS 1 @@ -61,6 +64,12 @@ inline namespace v1 _GLIBCXX_BEGIN_NAMESPACE_VERSION _GLIBCXX_BEGIN_NAMESPACE_CXX11 +#if __cplusplus >= 201402L + template<typename _CharT, typename _Traits = std::char_traits<_CharT>> + using __basic_string_view + = std::experimental::basic_string_view<_CharT, _Traits>; +#endif + /** * @ingroup filesystem * @{ @@ -87,6 +96,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 static __is_encoded_char<_CharT> __is_path_src(const basic_string<_CharT, _Traits, _Alloc>&, int); +#if __cplusplus >= 201402L + template<typename _CharT, typename _Traits> + static __is_encoded_char<_CharT> + __is_path_src(const __basic_string_view<_CharT, _Traits>&, int); +#endif + template<typename _Unknown> static std::false_type __is_path_src(const _Unknown&, ...); @@ -130,6 +145,18 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 _S_range_end(const basic_string<_CharT, _Traits, _Alloc>& __str) { return __str.data() + __str.size(); } +#if __cplusplus >= 201402L + template<typename _CharT, typename _Traits> + static const _CharT* + _S_range_begin(const __basic_string_view<_CharT, _Traits>& __str) + { return __str.data(); } + + template<typename _CharT, typename _Traits> + static const _CharT* + _S_range_end(const __basic_string_view<_CharT, _Traits>& __str) + { return __str.data() + __str.size(); } +#endif + template<typename _Tp, typename _Iter = decltype(_S_range_begin(std::declval<_Tp>())), typename _Val = typename std::iterator_traits<_Iter>::value_type> @@ -159,6 +186,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 +224,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>& @@ -237,6 +270,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 path& operator+=(const string_type& __x); path& operator+=(const value_type* __x); path& operator+=(value_type __x); +#if __cplusplus >= 201402L + path& operator+=(__basic_string_view<value_type> __x); +#endif template<typename _Source> _Path<_Source>& @@ -305,6 +341,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 int compare(const path& __p) const noexcept; int compare(const string_type& __s) const; int compare(const value_type* __s) const; +#if __cplusplus >= 201402L + int compare(const __basic_string_view<value_type> __s) const; +#endif // decomposition @@ -379,7 +418,8 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 _S_convert(_Iter __first, _Iter __last) { using __value_type = typename std::iterator_traits<_Iter>::value_type; - return _Cvt<__value_type>::_S_convert(__first, __last); + return _Cvt<typename remove_cv<__value_type>::type>:: + _S_convert(__first, __last); } template<typename _InputIterator> @@ -387,10 +427,10 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 _S_convert(_InputIterator __src, __null_terminated) { using _Tp = typename std::iterator_traits<_InputIterator>::value_type; - std::basic_string<_Tp> __tmp; - while (*__src != _Tp{}) - __tmp.push_back(*__src++); - return _S_convert(__tmp.data(), __tmp.data() + __tmp.size()); + std::basic_string<typename remove_cv<_Tp>::type> __tmp; + for (; *__src != _Tp{}; ++__src) + __tmp.push_back(*__src); + return _S_convert(__tmp.c_str(), __tmp.c_str() + __tmp.size()); } static string_type @@ -565,6 +605,9 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 struct path::__is_encoded_char<char32_t> : std::true_type { using value_type = char32_t; }; + template<typename _Tp> + struct path::__is_encoded_char<const _Tp> : __is_encoded_char<_Tp> { }; + struct path::_Cmpt : path { _Cmpt(string_type __s, _Type __t, size_t __pos) @@ -722,6 +765,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()); @@ -751,6 +802,16 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 return *this; } +#if __cplusplus >= 201402L + inline path& + path::operator+=(__basic_string_view<value_type> __x) + { + _M_pathname.append(__x.data(), __x.size()); + _M_split_cmpts(); + return *this; + } +#endif + template<typename _CharT> inline path::_Path<_CharT*, _CharT*>& path::operator+=(_CharT __x) @@ -892,6 +953,12 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 inline int path::compare(const value_type* __s) const { return compare(path(__s)); } +#if __cplusplus >= 201402L + inline int + path::compare(__basic_string_view<value_type> __s) const + { return compare(path(__s)); } +#endif + inline path path::filename() const { return empty() ? path() : *--end(); } diff --git a/libstdc++-v3/src/filesystem/dir.cc b/libstdc++-v3/src/filesystem/dir.cc index 6ff12d0..9a63c4a 100644 --- a/libstdc++-v3/src/filesystem/dir.cc +++ b/libstdc++-v3/src/filesystem/dir.cc @@ -79,8 +79,7 @@ namespace return (obj & bits) != Bitmask::none; } - // Returns {dirp, p} on success, {nullptr, p} on error. - // If an ignored EACCES error occurs returns {}. + // Returns {dirp, p} on success, {} on error (whether ignored or not). inline fs::_Dir open_dir(const fs::path& p, fs::directory_options options, std::error_code* ec) @@ -102,7 +101,7 @@ namespace std::error_code(err, std::generic_category()))); ec->assign(err, std::generic_category()); - return {nullptr, p}; + return {}; } inline fs::file_type @@ -169,7 +168,7 @@ fs::_Dir::advance(error_code* ec, directory_options options) "directory iterator cannot advance", std::error_code(err, std::generic_category()))); ec->assign(err, std::generic_category()); - return true; + return false; } else { @@ -191,12 +190,6 @@ directory_iterator(const path& p, directory_options options, error_code* ec) if (sp->advance(ec, options)) _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)); - } } const fs::directory_entry& @@ -270,10 +263,6 @@ recursive_directory_iterator(const path& p, directory_options options, 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)); } } @@ -354,7 +343,10 @@ fs::recursive_directory_iterator::increment(error_code& ec) noexcept { _Dir dir = open_dir(top.entry.path(), _M_options, &ec); if (ec) - return *this; + { + _M_dirs.reset(); + return *this; + } if (dir.dirp) _M_dirs->push(std::move(dir)); } @@ -372,19 +364,33 @@ 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; + } 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(&ec, _M_options)); +} + +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)); } diff --git a/libstdc++-v3/src/filesystem/ops.cc b/libstdc++-v3/src/filesystem/ops.cc index 9fb5b639..0dcb1b4 100644 --- a/libstdc++-v3/src/filesystem/ops.cc +++ b/libstdc++-v3/src/filesystem/ops.cc @@ -28,7 +28,9 @@ #include <experimental/filesystem> #include <functional> +#include <ostream> #include <stack> +#include <ext/stdio_filebuf.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> @@ -48,9 +50,6 @@ #endif #ifdef _GLIBCXX_USE_SENDFILE # include <sys/sendfile.h> -#else -# include <ext/stdio_filebuf.h> -# include <ostream> #endif #if _GLIBCXX_HAVE_UTIME_H # include <utime.h> @@ -143,7 +142,11 @@ fs::canonical(const path& p, const path& base, error_code& ec) #endif if (!exists(pa, ec)) - return result; + { + if (!ec) + ec = make_error_code(std::errc::no_such_file_or_directory); + return result; + } // else: we know there are (currently) no unresolvable symlink loops result = pa.root_path(); @@ -289,27 +292,24 @@ namespace } inline fs::file_time_type - file_time(const stat_type& st) noexcept + file_time(const stat_type& st, std::error_code& ec) noexcept { using namespace std::chrono; - return fs::file_time_type{ #ifdef _GLIBCXX_USE_ST_MTIM - seconds{st.st_mtim.tv_sec} + nanoseconds{st.st_mtim.tv_nsec} + time_t s = st.st_mtim.tv_sec; + nanoseconds ns{st.st_mtim.tv_nsec}; #else - seconds{st.st_mtime} + time_t s = st.st_mtime; + nanoseconds ns{}; #endif - }; - } - // Returns true if the file descriptor was successfully closed, - // otherwise returns false and the reason will be in errno. - inline bool - close_fd(int fd) - { - while (::close(fd)) - if (errno != EINTR) - return false; - return true; + if (s >= (nanoseconds::max().count() / 1e9)) + { + ec = std::make_error_code(std::errc::value_too_large); // EOVERFLOW + return fs::file_time_type::min(); + } + ec.clear(); + return fs::file_time_type{seconds{s} + ns}; } bool @@ -354,13 +354,25 @@ namespace from_st = &st2; } f = make_file_status(*from_st); + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 2712. copy_file() has a number of unspecified error conditions + if (!is_regular_file(f)) + { + ec = std::make_error_code(std::errc::not_supported); + return false; + } using opts = fs::copy_options; if (exists(t)) { - if (!is_other(t) && !is_other(f) - && to_st->st_dev == from_st->st_dev + if (!is_regular_file(t)) + { + ec = std::make_error_code(std::errc::not_supported); + return false; + } + + if (to_st->st_dev == from_st->st_dev && to_st->st_ino == from_st->st_ino) { ec = std::make_error_code(std::errc::file_exists); @@ -374,22 +386,27 @@ namespace } else if (is_set(option, opts::update_existing)) { - if (file_time(*from_st) <= file_time(*to_st)) - { - ec.clear(); - return false; - } + const auto from_mtime = file_time(*from_st, ec); + if (ec) + return false; + if ((from_mtime <= file_time(*to_st, ec)) || ec) + return false; } else if (!is_set(option, opts::overwrite_existing)) { ec = std::make_error_code(std::errc::file_exists); return false; } + else if (!is_regular_file(t)) + { + ec = std::make_error_code(std::errc::not_supported); + return false; + } } struct CloseFD { - ~CloseFD() { if (fd != -1) close_fd(fd); } - bool close() { return close_fd(std::exchange(fd, -1)); } + ~CloseFD() { if (fd != -1) ::close(fd); } + bool close() { return ::close(std::exchange(fd, -1)) == 0; } int fd; }; @@ -416,7 +433,7 @@ namespace #ifdef _GLIBCXX_USE_FCHMOD if (::fchmod(out.fd, from_st->st_mode)) -#elif _GLIBCXX_USE_FCHMODAT +#elif defined _GLIBCXX_USE_FCHMODAT if (::fchmodat(AT_FDCWD, to.c_str(), from_st->st_mode, 0)) #else if (::chmod(to.c_str(), from_st->st_mode)) @@ -427,7 +444,33 @@ namespace } #ifdef _GLIBCXX_USE_SENDFILE - const auto n = ::sendfile(out.fd, in.fd, nullptr, from_st->st_size); + off_t offset = 0; + const auto n = ::sendfile(out.fd, in.fd, &offset, from_st->st_size); + if (n < 0 && (errno == ENOSYS || errno == EINVAL)) + { +#endif + __gnu_cxx::stdio_filebuf<char> sbin(in.fd, std::ios::in); + __gnu_cxx::stdio_filebuf<char> sbout(out.fd, std::ios::out); + if (sbin.is_open()) + in.fd = -1; + if (sbout.is_open()) + out.fd = -1; + if (from_st->st_size && !(std::ostream(&sbout) << &sbin)) + { + ec = std::make_error_code(std::errc::io_error); + return false; + } + if (!sbout.close() || !sbin.close()) + { + ec.assign(errno, std::generic_category()); + return false; + } + + ec.clear(); + return true; + +#ifdef _GLIBCXX_USE_SENDFILE + } if (n != from_st->st_size) { ec.assign(errno, std::generic_category()); @@ -438,27 +481,10 @@ namespace ec.assign(errno, std::generic_category()); return false; } -#else - __gnu_cxx::stdio_filebuf<char> sbin(in.fd, std::ios::in); - __gnu_cxx::stdio_filebuf<char> sbout(out.fd, std::ios::out); - if (sbin.is_open()) - in.fd = -1; - if (sbout.is_open()) - out.fd = -1; - if (from_st->st_size && !(std::ostream(&sbout) << &sbin)) - { - ec = std::make_error_code(std::errc::io_error); - return false; - } - if (!sbout.close() || !sbin.close()) - { - ec.assign(errno, std::generic_category()); - return false; - } -#endif ec.clear(); return true; +#endif } } #endif @@ -474,7 +500,8 @@ fs::copy(const path& from, const path& to, copy_options options, file_status f, t; stat_type from_st, to_st; - // N4099 doesn't check copy_symlinks here, but I think that's a defect. + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 2681. filesystem::copy() cannot copy symlinks if (use_lstat || copy_symlinks ? ::lstat(from.c_str(), &from_st) : ::stat(from.c_str(), &from_st)) @@ -541,6 +568,10 @@ fs::copy(const path& from, const path& to, copy_options options, do_copy_file(from, to, options, &from_st, ptr, ec); } } + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 2682. filesystem::copy() won't create a symlink to a directory + else if (is_directory(f) && create_symlinks) + ec = std::make_error_code(errc::is_a_directory); else if (is_directory(f) && (is_set(options, copy_options::recursive) || options == copy_options::none)) { @@ -553,7 +584,10 @@ fs::copy(const path& from, const path& to, copy_options options, for (const directory_entry& x : directory_iterator(from)) copy(x.path(), to/x.path().filename(), options, ec); } - // "Otherwise no effects." (should ec.clear() be called?) + // _GLIBCXX_RESOLVE_LIB_DEFECTS + // 2683. filesystem::copy() says "no effects" + else + ec.clear(); } bool @@ -890,7 +924,7 @@ fs::equivalent(const path& p1, const path& p2) { error_code ec; auto result = equivalent(p1, p2, ec); - if (ec.value()) + if (ec) _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check file equivalence", p1, p2, ec)); return result; @@ -900,25 +934,42 @@ bool fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept { #ifdef _GLIBCXX_HAVE_SYS_STAT_H + int err = 0; + file_status s1, s2; stat_type st1, st2; - if (::stat(p1.c_str(), &st1) == 0 && ::stat(p2.c_str(), &st2) == 0) + if (::stat(p1.c_str(), &st1) == 0) + s1 = make_file_status(st1); + else if (is_not_found_errno(errno)) + s1.type(file_type::not_found); + else + err = errno; + + if (::stat(p2.c_str(), &st2) == 0) + s2 = make_file_status(st2); + else if (is_not_found_errno(errno)) + s2.type(file_type::not_found); + else + err = errno; + + if (exists(s1) && exists(s2)) { - file_status s1 = make_file_status(st1); - file_status s2 = make_file_status(st2); if (is_other(s1) && is_other(s2)) { ec = std::make_error_code(std::errc::not_supported); return false; } ec.clear(); + if (is_other(s1) || is_other(s2)) + return false; return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino; } - else if (is_not_found_errno(errno)) - { - ec = std::make_error_code(std::errc::no_such_file_or_directory); - return false; - } - ec.assign(errno, std::generic_category()); + else if (!exists(s1) && !exists(s2)) + ec = std::make_error_code(std::errc::no_such_file_or_directory); + else if (err) + ec.assign(err, std::generic_category()); + else + ec.clear(); + return false; #else ec = std::make_error_code(std::errc::not_supported); #endif @@ -1000,20 +1051,24 @@ fs::hard_link_count(const path& p, error_code& ec) noexcept bool fs::is_empty(const path& p) { - return fs::is_directory(status(p)) - ? fs::directory_iterator(p) == fs::directory_iterator() - : fs::file_size(p) == 0; + error_code ec; + bool e = is_empty(p, ec); + if (ec) + _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot check if file is empty", + p, ec)); + return e; } bool fs::is_empty(const path& p, error_code& ec) noexcept { auto s = status(p, ec); - if (ec.value()) + if (ec) return false; - return fs::is_directory(s) + bool empty = fs::is_directory(s) ? fs::directory_iterator(p, ec) == fs::directory_iterator() : fs::file_size(p, ec) == 0; + return ec ? false : empty; } fs::file_time_type @@ -1029,7 +1084,7 @@ fs::last_write_time(const path& p) fs::file_time_type fs::last_write_time(const path& p, error_code& ec) noexcept { - return do_stat(p, ec, [](const auto& st) { return file_time(st); }, + return do_stat(p, ec, [&ec](const auto& st) { return file_time(st, ec); }, file_time_type::min()); } @@ -1050,6 +1105,11 @@ fs::last_write_time(const path& p __attribute__((__unused__)), auto s = chrono::duration_cast<chrono::seconds>(d); #if _GLIBCXX_USE_UTIMENSAT auto ns = chrono::duration_cast<chrono::nanoseconds>(d - s); + if (ns < ns.zero()) // tv_nsec must be non-negative and less than 10e9. + { + --s; + ns += chrono::seconds(1); + } struct ::timespec ts[2]; ts[0].tv_sec = 0; ts[0].tv_nsec = UTIME_OMIT; @@ -1082,10 +1142,12 @@ fs::permissions(const path& p, perms prms) _GLIBCXX_THROW_OR_ABORT(filesystem_error("cannot set permissions", p, ec)); } -void fs::permissions(const path& p, perms prms, error_code& ec) noexcept +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); @@ -1094,24 +1156,33 @@ void fs::permissions(const path& p, perms prms, error_code& ec) noexcept prms &= perms::mask; - if (add || remove) + file_status st; + if (add || remove || nofollow) { - auto st = status(p, ec); + st = nofollow ? symlink_status(p, ec) : status(p, ec); if (ec) return; auto curr = st.permissions(); if (add) prms |= curr; - else + else if (remove) prms = curr & ~prms; } + int err = 0; #if _GLIBCXX_USE_FCHMODAT - if (::fchmodat(AT_FDCWD, p.c_str(), static_cast<mode_t>(prms), 0)) + const int flag = (nofollow && is_symlink(st)) ? AT_SYMLINK_NOFOLLOW : 0; + if (::fchmodat(AT_FDCWD, p.c_str(), static_cast<mode_t>(prms), flag)) + err = errno; #else - if (::chmod(p.c_str(), static_cast<mode_t>(prms))) + if (nofollow && is_symlink(st)) + ec = std::make_error_code(std::errc::operation_not_supported); + else if (::chmod(p.c_str(), static_cast<mode_t>(prms))) + err = errno; #endif - ec.assign(errno, std::generic_category()); + + if (err) + ec.assign(err, std::generic_category()); else ec.clear(); } @@ -1142,6 +1213,7 @@ fs::path fs::read_symlink(const path& p, error_code& ec) ec.assign(errno, std::generic_category()); return {}; } + ec.clear(); return path{buf.data(), buf.data()+len}; #else ec = std::make_error_code(std::errc::not_supported); @@ -1282,7 +1354,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; @@ -1292,6 +1364,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 { @@ -1390,12 +1466,17 @@ fs::path fs::temp_directory_path(error_code& ec) for (auto e = env; tmpdir == nullptr && *e != nullptr; ++e) tmpdir = ::getenv(*e); path p = tmpdir ? tmpdir : "/tmp"; - if (exists(p) && is_directory(p)) + auto st = status(p, ec); + if (!ec) { - ec.clear(); - return p; + if (is_directory(st)) + { + ec.clear(); + return p; + } + else + ec = std::make_error_code(std::errc::not_a_directory); } - ec = std::make_error_code(std::errc::not_a_directory); return {}; #endif } diff --git a/libstdc++-v3/testsuite/experimental/filesystem/iterators/directory_iterator.cc b/libstdc++-v3/testsuite/experimental/filesystem/iterators/directory_iterator.cc index ffd97ce..6b8e3fc 100644 --- a/libstdc++-v3/testsuite/experimental/filesystem/iterators/directory_iterator.cc +++ b/libstdc++-v3/testsuite/experimental/filesystem/iterators/directory_iterator.cc @@ -34,14 +34,14 @@ test01() const auto p = __gnu_test::nonexistent_path(); fs::directory_iterator iter(p, ec); VERIFY( ec ); - VERIFY( iter != fs::directory_iterator() ); + VERIFY( iter == end(iter) ); // Test empty directory. create_directory(p, fs::current_path(), ec); VERIFY( !ec ); iter = fs::directory_iterator(p, ec); VERIFY( !ec ); - VERIFY( iter == fs::directory_iterator() ); + VERIFY( iter == end(iter) ); // Test non-empty directory. create_directory_symlink(p, p / "l", ec); @@ -51,20 +51,20 @@ test01() VERIFY( iter != fs::directory_iterator() ); VERIFY( iter->path() == p/"l" ); ++iter; - VERIFY( iter == fs::directory_iterator() ); + VERIFY( iter == end(iter) ); // Test inaccessible directory. permissions(p, fs::perms::none, ec); VERIFY( !ec ); iter = fs::directory_iterator(p, ec); VERIFY( ec ); - VERIFY( iter != fs::directory_iterator() ); + VERIFY( iter == end(iter) ); // Test inaccessible directory, skipping permission denied. const auto opts = fs::directory_options::skip_permission_denied; iter = fs::directory_iterator(p, opts, ec); VERIFY( !ec ); - VERIFY( iter == fs::directory_iterator() ); + VERIFY( iter == end(iter) ); permissions(p, fs::perms::owner_all, ec); remove_all(p, ec); @@ -84,12 +84,12 @@ test02() // Test post-increment (libstdc++/71005) auto iter = fs::directory_iterator(p, ec); VERIFY( !ec ); - VERIFY( iter != fs::directory_iterator() ); + VERIFY( iter != end(iter) ); const auto entry1 = *iter; const auto entry2 = *iter++; VERIFY( entry1 == entry2 ); VERIFY( entry1.path() == p/"l" ); - VERIFY( iter == fs::directory_iterator() ); + VERIFY( iter == end(iter) ); remove_all(p, ec); } diff --git a/libstdc++-v3/testsuite/experimental/filesystem/iterators/pop.cc b/libstdc++-v3/testsuite/experimental/filesystem/iterators/pop.cc new file mode 100644 index 0000000..9306c03 --- /dev/null +++ b/libstdc++-v3/testsuite/experimental/filesystem/iterators/pop.cc @@ -0,0 +1,108 @@ +// 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; + fs::recursive_directory_iterator dir; + dir.pop(ec); // This is undefined, but our implementation + VERIFY( ec ); // checks and returns an error. + VERIFY( dir == end(dir) ); + + std::error_code ec2; + try + { + dir.pop(); + } + catch (const fs::filesystem_error& ex) + { + ec2 = ex.code(); + } + VERIFY( ec2 == ec ); +} + +void +test02() +{ + std::error_code ec = make_error_code(std::errc::interrupted); + const auto p = __gnu_test::nonexistent_path(); + create_directories(p / "d1/d2/d3"); + for (int i = 0; i < 3; ++i) + { + fs::recursive_directory_iterator dir(p); + std::advance(dir, i); + VERIFY( dir.depth() == i ); + dir.pop(ec); + VERIFY( !ec ); + VERIFY( dir == end(dir) ); + + dir = fs::recursive_directory_iterator(p); + std::advance(dir, i); + VERIFY( dir.depth() == i ); + dir.pop(); + VERIFY( dir == end(dir) ); + } + remove_all(p, ec); +} + +void +test03() +{ + std::error_code ec = make_error_code(std::errc::interrupted); + const auto p = __gnu_test::nonexistent_path(); + create_directories(p / "d1/d2/d3"); + create_directories(p / "d1/d2/e3"); + create_directories(p / "d1/e2/d3"); + for (int i = 0; i < 3; ++i) + { + fs::recursive_directory_iterator dir(p); + std::advance(dir, i); + int expected_depth = i; + VERIFY( dir.depth() == expected_depth ); + dir.pop(ec); + VERIFY( !ec ); + if (dir != end(dir)) + VERIFY( dir.depth() == (expected_depth - 1) ); + + dir = fs::recursive_directory_iterator(p); + std::advance(dir, i); + VERIFY( dir.depth() == i ); + dir.pop(); + if (dir != end(dir)) + VERIFY( dir.depth() == (i -1) ); + } + remove_all(p, ec); +} + +int +main() +{ + test01(); + test02(); + test03(); +} diff --git a/libstdc++-v3/testsuite/experimental/filesystem/iterators/recursive_directory_iterator.cc b/libstdc++-v3/testsuite/experimental/filesystem/iterators/recursive_directory_iterator.cc index b44ff3f..9e94c47 100644 --- a/libstdc++-v3/testsuite/experimental/filesystem/iterators/recursive_directory_iterator.cc +++ b/libstdc++-v3/testsuite/experimental/filesystem/iterators/recursive_directory_iterator.cc @@ -34,39 +34,39 @@ test01() const auto p = __gnu_test::nonexistent_path(); fs::recursive_directory_iterator iter(p, ec); VERIFY( ec ); - VERIFY( iter != fs::recursive_directory_iterator() ); + VERIFY( iter == end(iter) ); // Test empty directory. create_directory(p, fs::current_path(), ec); VERIFY( !ec ); iter = fs::recursive_directory_iterator(p, ec); VERIFY( !ec ); - VERIFY( iter == fs::recursive_directory_iterator() ); + VERIFY( iter == end(iter) ); // Test non-empty directory. create_directories(p / "d1/d2"); VERIFY( !ec ); iter = fs::recursive_directory_iterator(p, ec); VERIFY( !ec ); - VERIFY( iter != fs::recursive_directory_iterator() ); + VERIFY( iter != end(iter) ); VERIFY( iter->path() == p/"d1" ); ++iter; VERIFY( iter->path() == p/"d1/d2" ); ++iter; - VERIFY( iter == fs::recursive_directory_iterator() ); + VERIFY( iter == end(iter) ); // Test inaccessible directory. permissions(p, fs::perms::none, ec); VERIFY( !ec ); iter = fs::recursive_directory_iterator(p, ec); VERIFY( ec ); - VERIFY( iter != fs::recursive_directory_iterator() ); + VERIFY( iter == end(iter) ); // Test inaccessible directory, skipping permission denied. const auto opts = fs::directory_options::skip_permission_denied; iter = fs::recursive_directory_iterator(p, opts, ec); VERIFY( !ec ); - VERIFY( iter == fs::recursive_directory_iterator() ); + VERIFY( iter == end(iter) ); // Test inaccessible sub-directory. permissions(p, fs::perms::owner_all, ec); @@ -75,23 +75,24 @@ test01() VERIFY( !ec ); iter = fs::recursive_directory_iterator(p, ec); VERIFY( !ec ); - VERIFY( iter != fs::recursive_directory_iterator() ); + VERIFY( iter != end(iter) ); VERIFY( iter->path() == p/"d1" ); ++iter; // should recurse into d1 VERIFY( iter->path() == p/"d1/d2" ); iter.increment(ec); // should fail to recurse into p/d1/d2 VERIFY( ec ); + VERIFY( iter == end(iter) ); // Test inaccessible sub-directory, skipping permission denied. iter = fs::recursive_directory_iterator(p, opts, ec); VERIFY( !ec ); - VERIFY( iter != fs::recursive_directory_iterator() ); + VERIFY( iter != end(iter) ); VERIFY( iter->path() == p/"d1" ); ++iter; // should recurse into d1 VERIFY( iter->path() == p/"d1/d2" ); iter.increment(ec); // should fail to recurse into p/d1/d2, so skip it VERIFY( !ec ); - VERIFY( iter == fs::recursive_directory_iterator() ); + VERIFY( iter == end(iter) ); permissions(p/"d1/d2", fs::perms::owner_all, ec); remove_all(p, ec); @@ -110,7 +111,7 @@ test02() // Test post-increment (libstdc++/71005) auto iter = fs::recursive_directory_iterator(p, ec); VERIFY( !ec ); - VERIFY( iter != fs::recursive_directory_iterator() ); + VERIFY( iter != end(iter) ); const auto entry1 = *iter; const auto entry2 = *iter++; VERIFY( entry1 == entry2 ); @@ -119,7 +120,7 @@ test02() const auto entry4 = *iter++; VERIFY( entry3 == entry4 ); VERIFY( entry3.path() == p/"d1/d2" ); - VERIFY( iter == fs::recursive_directory_iterator() ); + VERIFY( iter == end(iter) ); remove_all(p, ec); } @@ -150,7 +151,7 @@ test04() // libstdc++/71004 const fs::recursive_directory_iterator it; - VERIFY( it == fs::recursive_directory_iterator() ); + VERIFY( it == end(it) ); } void diff --git a/libstdc++-v3/testsuite/experimental/filesystem/operations/copy.cc b/libstdc++-v3/testsuite/experimental/filesystem/operations/copy.cc index a5f6a3e..e53e75a 100644 --- a/libstdc++-v3/testsuite/experimental/filesystem/operations/copy.cc +++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/copy.cc @@ -21,7 +21,6 @@ // 15.3 Copy [fs.op.copy] #include <experimental/filesystem> -#include <fstream> #include <testsuite_fs.h> #include <testsuite_hooks.h> @@ -44,14 +43,25 @@ test01() fs::copy(".", ".", fs::copy_options::none, ec); VERIFY( ec ); - std::ofstream{p.native()}; + __gnu_test::scoped_file f(p); VERIFY( fs::is_directory(".") ); VERIFY( fs::is_regular_file(p) ); ec.clear(); fs::copy(".", p, fs::copy_options::none, ec); VERIFY( ec ); - remove(p, ec); + auto to = __gnu_test::nonexistent_path(); + ec.clear(); + auto opts = fs::copy_options::create_symlinks; + fs::copy("/", to, opts, ec); + VERIFY( ec == std::make_error_code(std::errc::is_a_directory) ); + VERIFY( !exists(to) ); + + ec.clear(); + opts != fs::copy_options::recursive; + fs::copy("/", to, opts, ec); + VERIFY( ec == std::make_error_code(std::errc::is_a_directory) ); + VERIFY( !exists(to) ); } // Test is_symlink(f) case. @@ -62,29 +72,35 @@ test02() auto from = __gnu_test::nonexistent_path(); auto to = __gnu_test::nonexistent_path(); - std::error_code ec; + std::error_code ec, bad = std::make_error_code(std::errc::invalid_argument); + ec = bad; fs::create_symlink(".", from, ec); VERIFY( !ec ); VERIFY( fs::exists(from) ); + ec = bad; fs::copy(from, to, fs::copy_options::skip_symlinks, ec); VERIFY( !ec ); VERIFY( !fs::exists(to) ); + ec = bad; fs::copy(from, to, fs::copy_options::skip_symlinks, ec); VERIFY( !ec ); VERIFY( !fs::exists(to) ); + ec = bad; fs::copy(from, to, fs::copy_options::skip_symlinks|fs::copy_options::copy_symlinks, ec); VERIFY( !ec ); VERIFY( !fs::exists(to) ); + ec = bad; fs::copy(from, to, fs::copy_options::copy_symlinks, ec); VERIFY( !ec ); VERIFY( fs::exists(to) ); + VERIFY( is_symlink(to) ); fs::copy(from, to, fs::copy_options::copy_symlinks, ec); VERIFY( ec ); @@ -117,6 +133,9 @@ test03() fs::copy(from, to); VERIFY( fs::exists(to) ); VERIFY( fs::file_size(to) == fs::file_size(from) ); + + remove(from); + remove(to); } // Test is_directory(f) case. @@ -129,6 +148,37 @@ test04() auto to = __gnu_test::nonexistent_path(); std::error_code ec; + create_directories(from/"a/b/c"); + + { + __gnu_test::scoped_file f(to); + copy(from, to, ec); + VERIFY( ec ); + } + + __gnu_test::scoped_file f1(from/"a/f1"); + std::ofstream{f1.path} << "file one"; + __gnu_test::scoped_file f2(from/"a/b/f2"); + std::ofstream{f2.path} << "file two"; + + copy(from, to, ec); + VERIFY( !ec ); + VERIFY( exists(to) && is_empty(to) ); + remove(to); + + copy(from, to, fs::copy_options::recursive, ec); + VERIFY( !ec ); + VERIFY( exists(to) && !is_empty(to) ); + VERIFY( is_regular_file(to/"a/f1") && !is_empty(to/"a/f1") ); + VERIFY( file_size(from/"a/f1") == file_size(to/"a/f1") ); + VERIFY( is_regular_file(to/"a/b/f2") && !is_empty(to/"a/b/f2") ); + VERIFY( file_size(from/"a/b/f2") == file_size(to/"a/b/f2") ); + VERIFY( is_directory(to/"a/b/c") && is_empty(to/"a/b/c") ); + + f1.path.clear(); + f2.path.clear(); + remove_all(from, ec); + remove_all(to, ec); } // Test no-op cases. @@ -138,10 +188,10 @@ test05() bool test __attribute__((unused)) = false; auto to = __gnu_test::nonexistent_path(); - std::error_code ec; + std::error_code ec = std::make_error_code(std::errc::invalid_argument); - fs::copy("/", to, fs::copy_options::create_symlinks, ec); - VERIFY( !ec ); + fs::copy("/", to, fs::copy_options::copy_symlinks, ec); + VERIFY( !ec ); // Previous value should be cleared (LWG 2683) } int diff --git a/libstdc++-v3/testsuite/experimental/filesystem/operations/copy_file.cc b/libstdc++-v3/testsuite/experimental/filesystem/operations/copy_file.cc index cdb7911..bcbdd12 100644 --- a/libstdc++-v3/testsuite/experimental/filesystem/operations/copy_file.cc +++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/copy_file.cc @@ -73,6 +73,9 @@ test01() VERIFY( !ec ); VERIFY( exists(to) ); VERIFY( file_size(to) == file_size(from) ); + + remove(from); + remove(to); } int 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/equivalent.cc b/libstdc++-v3/testsuite/experimental/filesystem/operations/equivalent.cc new file mode 100644 index 0000000..77ed054 --- /dev/null +++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/equivalent.cc @@ -0,0 +1,74 @@ +// 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_fs.h> +#include <testsuite_hooks.h> + +namespace fs = std::experimental::filesystem; + + +void +test01() +{ + auto p1 = __gnu_test::nonexistent_path(); + auto p2 = __gnu_test::nonexistent_path(); + std::error_code ec; + bool result; + + result = equivalent(p1, p2, ec); + VERIFY( ec ); + VERIFY( !result ); + const auto bad_ec = ec; + + __gnu_test::scoped_file f1(p1); + result = equivalent(p1, p2, ec); + VERIFY( !ec ); + VERIFY( !result ); + + __gnu_test::scoped_file f2(p2); + ec = bad_ec; + result = equivalent(p1, p2, ec); + VERIFY( !ec ); + VERIFY( !result ); + + auto p3 = __gnu_test::nonexistent_path(); + create_hard_link(p1, p3, ec); + if (ec) + return; // hard links not supported + __gnu_test::scoped_file f3(p3, __gnu_test::scoped_file::adopt_file); + + ec = bad_ec; + result = equivalent(p1, p3, ec); + VERIFY( !ec ); + VERIFY( result ); + + ec = bad_ec; + result = equivalent(p2, p3, ec); + VERIFY( !ec ); + VERIFY( !result ); +} + +int +main() +{ + test01(); +} diff --git a/libstdc++-v3/testsuite/experimental/filesystem/operations/exists.cc b/libstdc++-v3/testsuite/experimental/filesystem/operations/exists.cc index 633095d..39f45b1 100644 --- a/libstdc++-v3/testsuite/experimental/filesystem/operations/exists.cc +++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/exists.cc @@ -34,6 +34,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 @@ -43,6 +55,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 @@ -52,6 +68,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 @@ -60,4 +108,5 @@ main() test01(); test02(); test03(); + test04(); } diff --git a/libstdc++-v3/testsuite/experimental/filesystem/operations/is_empty.cc b/libstdc++-v3/testsuite/experimental/filesystem/operations/is_empty.cc new file mode 100644 index 0000000..d35967a --- /dev/null +++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/is_empty.cc @@ -0,0 +1,109 @@ +// 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 } }E +// { dg-require-filesystem-ts "" } + +#include <experimental/filesystem> +#include <testsuite_hooks.h> +#include <testsuite_fs.h> + +namespace fs = std::experimental::filesystem; + +void +test01() +{ + auto p = __gnu_test::nonexistent_path(); + create_directory(p); + permissions(p, fs::perms::none); + std::error_code ec, ec2; + + bool result = fs::is_empty(p, ec); + VERIFY( ec == std::make_error_code(std::errc::permission_denied) ); + VERIFY( !result ); + + try { + fs::is_empty(p); + } catch (const fs::filesystem_error& e) { + ec2 = e.code(); + } + VERIFY( ec2 == ec ); + + result = fs::is_empty(p/"f", ec); + VERIFY( ec == std::make_error_code(std::errc::permission_denied) ); + VERIFY( !result ); + + try { + fs::is_empty(p/"f"); + } catch (const fs::filesystem_error& e) { + ec2 = e.code(); + } + VERIFY( ec2 == ec ); + + permissions(p, fs::perms::owner_all, ec); + remove_all(p, ec); +} + +void +test02() +{ + auto p = __gnu_test::nonexistent_path(); + create_directory(p); + std::error_code ec, bad_ec = make_error_code(std::errc::invalid_argument); + bool empty; + + ec = bad_ec; + empty = is_empty(p, ec); + VERIFY( !ec ); + VERIFY( empty ); + empty = is_empty(p); + VERIFY( empty ); + + __gnu_test::scoped_file f(p/"f"); + ec = bad_ec; + empty = is_empty(f.path, ec); + VERIFY( !ec ); + VERIFY( empty ); + empty = is_empty(f.path); + VERIFY( empty ); + + std::ofstream{f.path.native()} << "data"; + ec = bad_ec; + empty = is_empty(p, ec); + VERIFY( !ec ); + VERIFY( !empty ); + empty = is_empty(p); + VERIFY( !empty ); + + ec = bad_ec; + empty = is_empty(p, ec); + VERIFY( !ec ); + VERIFY( !empty ); + empty = is_empty(p); + VERIFY( !empty ); + + f.path.clear(); + remove_all(p, ec); +} + +int +main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/experimental/filesystem/operations/last_write_time.cc b/libstdc++-v3/testsuite/experimental/filesystem/operations/last_write_time.cc new file mode 100644 index 0000000..a60a25f --- /dev/null +++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/last_write_time.cc @@ -0,0 +1,156 @@ +// 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 "" } + +// 15.25 Permissions [fs.op.last_write_time] + +#include <experimental/filesystem> +#include <testsuite_fs.h> +#include <testsuite_hooks.h> + +#ifdef _GLIBCXX_HAVE_FCNTL_H +# include <fcntl.h> +#endif +#if _GLIBCXX_HAVE_UTIME_H +# include <utime.h> +#endif + +using time_type = std::experimental::filesystem::file_time_type; + +void +test01() +{ + // read times + + auto p = __gnu_test::nonexistent_path(); + std::error_code ec; + time_type mtime = last_write_time(p, ec); + VERIFY( ec ); + VERIFY( ec == std::make_error_code(std::errc::no_such_file_or_directory) ); +#if __cpp_exceptions + bool caught = false; + try { + mtime = last_write_time(p); + } catch (std::system_error const& e) { + caught = true; + ec = e.code(); + } + VERIFY( caught ); + VERIFY( ec ); + VERIFY( ec == std::make_error_code(std::errc::no_such_file_or_directory) ); +#endif + + __gnu_test::scoped_file file(p); + VERIFY( exists(p) ); + mtime = last_write_time(p, ec); + VERIFY( !ec ); + VERIFY( mtime <= time_type::clock::now() ); + VERIFY( mtime == last_write_time(p) ); + + auto end_of_time = time_type::duration::max(); + auto last_second + = std::chrono::duration_cast<std::chrono::seconds>(end_of_time).count(); + if (last_second > std::numeric_limits<std::time_t>::max()) + return; // can't test overflow + +#if _GLIBCXX_USE_UTIMENSAT + struct ::timespec ts[2]; + ts[0].tv_sec = 0; + ts[0].tv_nsec = UTIME_NOW; + ts[1].tv_sec = std::numeric_limits<std::time_t>::max() - 1; + ts[1].tv_nsec = 0; + VERIFY( !::utimensat(AT_FDCWD, p.c_str(), ts, 0) ); +#elif _GLIBCXX_HAVE_UTIME_H + ::utimbuf times; + times.modtime = std::numeric_limits<std::time_t>::max() - 1; + times.actime = std::numeric_limits<std::time_t>::max() - 1; + VERIFY( !::utime(p.c_str(), ×) ); +#else + return; +#endif + + mtime = last_write_time(p, ec); + VERIFY( ec ); + VERIFY( ec == std::make_error_code(std::errc::value_too_large) ); + VERIFY( mtime == time_type::min() ); + +#if __cpp_exceptions + caught = false; + try { + mtime = last_write_time(p); + } catch (std::system_error const& e) { + caught = true; + ec = e.code(); + } + VERIFY( caught ); + VERIFY( ec ); + VERIFY( ec == std::make_error_code(std::errc::value_too_large) ); +#endif +} + +bool approx_equal(time_type file_time, time_type expected) +{ + auto delta = expected - file_time; + if (delta < delta.zero()) + delta = -delta; + return delta < std::chrono::seconds(1); +} + +void +test02() +{ + // write times + + __gnu_test::scoped_file f; + std::error_code ec; + time_type time; + + time = last_write_time(f.path); + last_write_time(f.path, time, ec); + VERIFY( !ec ); + VERIFY( approx_equal(last_write_time(f.path), time) ); + + time -= std::chrono::milliseconds(1000 * 60 * 10 + 15); + last_write_time(f.path, time, ec); + VERIFY( !ec ); + VERIFY( approx_equal(last_write_time(f.path), time) ); + + time += std::chrono::milliseconds(1000 * 60 * 20 + 15); + last_write_time(f.path, time, ec); + VERIFY( !ec ); + VERIFY( approx_equal(last_write_time(f.path), time) ); + + time = time_type(); + last_write_time(f.path, time, ec); + VERIFY( !ec ); + VERIFY( approx_equal(last_write_time(f.path), time) ); + + time -= std::chrono::milliseconds(1000 * 60 * 10 + 15); + last_write_time(f.path, time, ec); + VERIFY( !ec ); + VERIFY( approx_equal(last_write_time(f.path), time) ); +} + +int +main() +{ + test01(); + test02(); +} diff --git a/libstdc++-v3/testsuite/experimental/filesystem/operations/permissions.cc b/libstdc++-v3/testsuite/experimental/filesystem/operations/permissions.cc index e414860..45f1142 100644 --- a/libstdc++-v3/testsuite/experimental/filesystem/operations/permissions.cc +++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/permissions.cc @@ -21,7 +21,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,12 +40,113 @@ 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); +} + +void +test05() +{ + using perms = std::experimental::filesystem::perms; + std::error_code ec; + + __gnu_test::scoped_file f; + auto p = perms::owner_write; + + // symlink_nofollow should not give an error for non-symlinks + permissions(f.path, p|perms::symlink_nofollow, ec); + VERIFY( !ec ); + auto st = status(f.path); + VERIFY( st.permissions() == p ); + p |= perms::owner_read; + permissions(f.path, p|perms::symlink_nofollow, ec); + st = status(f.path); + VERIFY( st.permissions() == p ); +} + int main() { test01(); + test02(); + test03(); + test04(); + test05(); } diff --git a/libstdc++-v3/testsuite/experimental/filesystem/operations/read_symlink.cc b/libstdc++-v3/testsuite/experimental/filesystem/operations/read_symlink.cc new file mode 100644 index 0000000..c67b318 --- /dev/null +++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/read_symlink.cc @@ -0,0 +1,51 @@ +// 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() +{ + auto p = __gnu_test::nonexistent_path(); + std::error_code ec; + + read_symlink(p, ec); + VERIFY( ec ); + + fs::path tgt = "."; + create_symlink(tgt, p); + + auto result = read_symlink(p, ec); + VERIFY( !ec ); + VERIFY( result == tgt ); + + remove(p); +} + +int +main() +{ + test01(); +} diff --git a/libstdc++-v3/testsuite/experimental/filesystem/operations/remove_all.cc b/libstdc++-v3/testsuite/experimental/filesystem/operations/remove_all.cc new file mode 100644 index 0000000..57d15af --- /dev/null +++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/remove_all.cc @@ -0,0 +1,92 @@ +// 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; + std::uintmax_t n; + + n = fs::remove_all("", ec); + VERIFY( ec ); + VERIFY( n == std::uintmax_t(-1) ); + + auto p = __gnu_test::nonexistent_path(); + ec.clear(); + n = remove_all(p, ec); + VERIFY( ec ); + VERIFY( n == std::uintmax_t(-1) ); + + const auto bad_ec = ec; + auto link = __gnu_test::nonexistent_path(); + create_symlink(p, link); // dangling symlink + ec = bad_ec; + n = remove_all(link, ec); + VERIFY( !ec ); + VERIFY( n == 1 ); + VERIFY( !exists(symlink_status(link)) ); // DR 2721 + + __gnu_test::scoped_file f(p); + create_symlink(p, link); + ec = bad_ec; + n = remove_all(link, ec); + VERIFY( !ec ); + VERIFY( n == 1 ); + VERIFY( !exists(symlink_status(link)) ); // The symlink is removed, but + VERIFY( exists(p) ); // its target is not. + + auto dir = __gnu_test::nonexistent_path(); + create_directories(dir/"a/b/c"); + ec = bad_ec; + n = remove_all(dir/"a", ec); + VERIFY( !ec ); + VERIFY( n == 3 ); + VERIFY( exists(dir) ); + VERIFY( !exists(dir/"a") ); + + create_directories(dir/"a/b/c"); + __gnu_test::scoped_file a1(dir/"a/1"); + __gnu_test::scoped_file a2(dir/"a/2"); + __gnu_test::scoped_file b1(dir/"a/b/1"); + __gnu_test::scoped_file b2(dir/"a/b/2"); + ec = bad_ec; + n = remove_all(dir, ec); + VERIFY( !ec ); + VERIFY( n == 8 ); + VERIFY( !exists(dir) ); + + a1.path.clear(); + a2.path.clear(); + b1.path.clear(); + b2.path.clear(); +} + +int +main() +{ + test01(); +} diff --git a/libstdc++-v3/testsuite/experimental/filesystem/operations/temp_directory_path.cc b/libstdc++-v3/testsuite/experimental/filesystem/operations/temp_directory_path.cc index 1ae363b..23d4b9c 100644 --- a/libstdc++-v3/testsuite/experimental/filesystem/operations/temp_directory_path.cc +++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/temp_directory_path.cc @@ -46,6 +46,7 @@ test01() std::error_code ec; fs::path p1 = fs::temp_directory_path(ec); + VERIFY( !ec ); VERIFY( exists(p1) ); fs::path p2 = fs::temp_directory_path(); @@ -65,6 +66,7 @@ test02() std::error_code ec; fs::path p = fs::temp_directory_path(ec); VERIFY( ec ); + VERIFY( p == fs::path() ); std::error_code ec2; try { @@ -75,10 +77,54 @@ test02() VERIFY( ec2 == ec ); } +void +test03() +{ + auto p = __gnu_test::nonexistent_path(); + create_directories(p/"tmp"); + permissions(p, fs::perms::none); + setenv("TMPDIR", (p/"tmp").c_str(), 1); + std::error_code ec; + auto r = fs::temp_directory_path(ec); // libstdc++/PR71337 + VERIFY( ec == std::make_error_code(std::errc::permission_denied) ); + VERIFY( r == fs::path() ); + + std::error_code ec2; + try { + fs::temp_directory_path(); + } catch (const fs::filesystem_error& e) { + ec2 = e.code(); + } + VERIFY( ec2 == ec ); + + permissions(p, fs::perms::owner_all, ec); + remove_all(p, ec); +} + +void +test04() +{ + __gnu_test::scoped_file f; + setenv("TMPDIR", f.path.c_str(), 1); + std::error_code ec; + auto r = fs::temp_directory_path(ec); + VERIFY( ec == std::make_error_code(std::errc::not_a_directory) ); + VERIFY( r == fs::path() ); + + std::error_code ec2; + try { + fs::temp_directory_path(); + } catch (const fs::filesystem_error& e) { + ec2 = e.code(); + } + VERIFY( ec2 == ec ); +} int main() { test01(); test02(); + test03(); + test04(); } diff --git a/libstdc++-v3/testsuite/experimental/filesystem/path/construct/range.cc b/libstdc++-v3/testsuite/experimental/filesystem/path/construct/range.cc index 90eaf30..9e51e0a 100644 --- a/libstdc++-v3/testsuite/experimental/filesystem/path/construct/range.cc +++ b/libstdc++-v3/testsuite/experimental/filesystem/path/construct/range.cc @@ -1,4 +1,5 @@ -// { dg-options "-std=gnu++11 -lstdc++fs" } +// { dg-options "-lstdc++fs" } +// { dg-do run { target c++11 } } // { dg-require-filesystem-ts "" } // Copyright (C) 2014-2016 Free Software Foundation, Inc. @@ -23,6 +24,7 @@ #include <experimental/filesystem> #include <string> #include <testsuite_fs.h> +#include <testsuite_iterators.h> using std::experimental::filesystem::path; using __gnu_test::compare_paths; @@ -53,6 +55,53 @@ test01() compare_paths(p1, p7); compare_paths(p1, p8); #endif + + using __gnu_test::test_container; + using __gnu_test::input_iterator_wrapper; + // Test with input iterators and const value_types + + test_container<char, input_iterator_wrapper> + r1((char*)s.c_str(), (char*)s.c_str() + s.size()); + path p9(r1.begin(), r1.end()); + compare_paths(p1, p9); + + test_container<char, input_iterator_wrapper> + r2((char*)s.c_str(), (char*)s.c_str() + s.size() + 1); // includes null-terminator + path p10(r2.begin()); + compare_paths(p1, p10); + + test_container<const char, input_iterator_wrapper> + r3(s.c_str(), s.c_str() + s.size()); + path p11(r3.begin(), r3.end()); + compare_paths(p1, p11); + + test_container<const char, input_iterator_wrapper> + r4(s.c_str(), s.c_str() + s.size() + 1); // includes null-terminator + path p12(r4.begin()); + compare_paths(p1, p12); + +#if _GLIBCXX_USE_WCHAR_T + // Test with input iterators and const value_types + test_container<wchar_t, input_iterator_wrapper> + r5((wchar_t*)ws.c_str(), (wchar_t*)ws.c_str() + ws.size()); + path p13(r5.begin(), r5.end()); + compare_paths(p1, p13); + + test_container<wchar_t, input_iterator_wrapper> + r6((wchar_t*)ws.c_str(), (wchar_t*)ws.c_str() + ws.size() + 1); // includes null-terminator + path p14(r6.begin()); + compare_paths(p1, p14); + + test_container<const wchar_t, input_iterator_wrapper> + r7(ws.c_str(), ws.c_str() + ws.size()); + path p15(r7.begin(), r7.end()); + compare_paths(p1, p15); + + test_container<const wchar_t, input_iterator_wrapper> + r8(ws.c_str(), ws.c_str() + ws.size() + 1); // includes null-terminator + path p16(r8.begin()); + compare_paths(p1, p16); +#endif } } diff --git a/libstdc++-v3/testsuite/experimental/filesystem/path/construct/string_view.cc b/libstdc++-v3/testsuite/experimental/filesystem/path/construct/string_view.cc new file mode 100644 index 0000000..43bc115 --- /dev/null +++ b/libstdc++-v3/testsuite/experimental/filesystem/path/construct/string_view.cc @@ -0,0 +1,56 @@ +// { dg-options "-lstdc++fs" } +// { dg-do run { target c++14 } } +// { dg-require-filesystem-ts "" } + +// 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/>. + +// 8.4.1 path constructors [path.construct] + +#include <experimental/filesystem> +#include <experimental/string_view> +#include <string> +#include <testsuite_fs.h> + +using std::experimental::filesystem::path; +using __gnu_test::compare_paths; + +void +test01() +{ + for (std::string s : __gnu_test::test_paths) + { + path p1 = s; + std::experimental::string_view sv(s); + path p2 = sv; + compare_paths(p1, p2); + +#if _GLIBCXX_USE_WCHAR_T + std::wstring ws(s.begin(), s.end()); + path p3 = ws; + std::experimental::wstring_view wsv(ws); + path p4 = wsv; + compare_paths(p1, p4); +#endif + } +} + +int +main() +{ + test01(); +} diff --git a/libstdc++-v3/testsuite/util/testsuite_fs.h b/libstdc++-v3/testsuite/util/testsuite_fs.h index f1e0bfc..7d9b20c 100644 --- a/libstdc++-v3/testsuite/util/testsuite_fs.h +++ b/libstdc++-v3/testsuite/util/testsuite_fs.h @@ -23,7 +23,7 @@ #define _TESTSUITE_FS_H 1 #include <experimental/filesystem> -#include <iostream> +#include <fstream> #include <string> #include <cstdio> #include <stdlib.h> @@ -40,7 +40,6 @@ namespace __gnu_test compare_paths(const std::experimental::filesystem::path& p1, const std::experimental::filesystem::path& p2) { - // std::cout << "Comparing " << p1 << " and " << p2 << std::endl; PATH_CHK( p1, p2, string ); PATH_CHK( p1, p2, empty ); PATH_CHK( p1, p2, has_root_path ); @@ -95,5 +94,23 @@ namespace __gnu_test return p; } + // RAII helper to remove a file on scope exit. + struct scoped_file + { + using path_type = std::experimental::filesystem::path; + + enum adopt_file_t { adopt_file }; + + explicit + scoped_file(const path_type& p = nonexistent_path()) : path(p) + { std::ofstream{p.native()}; } + + scoped_file(path_type p, adopt_file_t) : path(p) { } + + ~scoped_file() { if (!path.empty()) remove(path); } + + path_type path; + }; + } // namespace __gnu_test #endif