From: Jonathan Wakely Date: Mon, 12 Dec 2016 15:00:43 +0000 (+0000) Subject: Backport all Filesystem library fixes from trunk X-Git-Tag: releases/gcc-5.5.0~649 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=068a429b13a5fc74e0577d2a8f169f4a249a2206;p=thirdparty%2Fgcc.git 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. From-SVN: r243557 --- diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 7a0782825c42..c967a8f6a025 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,93 @@ +2016-12-12 Jonathan Wakely + + 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. + 2016-12-07 Ville Voutilainen Backport from mainline diff --git a/libstdc++-v3/include/experimental/fs_dir.h b/libstdc++-v3/include/experimental/fs_dir.h index 7c5f8e8ab21f..870b42b836ad 100644 --- a/libstdc++-v3/include/experimental/fs_dir.h +++ b/libstdc++-v3/include/experimental/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/fs_fwd.h b/libstdc++-v3/include/experimental/fs_fwd.h index ade86c35b978..da13903c8cc4 100644 --- a/libstdc++-v3/include/experimental/fs_fwd.h +++ b/libstdc++-v3/include/experimental/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/fs_ops.h b/libstdc++-v3/include/experimental/fs_ops.h index 91b890203fe8..727c84fc69e1 100644 --- a/libstdc++-v3/include/experimental/fs_ops.h +++ b/libstdc++-v3/include/experimental/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/fs_path.h b/libstdc++-v3/include/experimental/fs_path.h index 176918a394b1..d430ab3d9b85 100644 --- a/libstdc++-v3/include/experimental/fs_path.h +++ b/libstdc++-v3/include/experimental/fs_path.h @@ -44,6 +44,9 @@ #include #include #include +#if __cplusplus >= 201402L +# include +#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> + 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 + static __is_encoded_char<_CharT> + __is_path_src(const __basic_string_view<_CharT, _Traits>&, int); +#endif + template 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 + static const _CharT* + _S_range_begin(const __basic_string_view<_CharT, _Traits>& __str) + { return __str.data(); } + + template + static const _CharT* + _S_range_end(const __basic_string_view<_CharT, _Traits>& __str) + { return __str.data() + __str.size(); } +#endif + template())), 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> 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 _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 __x); +#endif template _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 __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::type>:: + _S_convert(__first, __last); } template @@ -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::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 : std::true_type { using value_type = char32_t; }; + template + struct path::__is_encoded_char : __is_encoded_char<_Tp> { }; + struct path::_Cmpt : path { _Cmpt(string_type __s, _Type __t, size_t __pos) @@ -721,6 +764,14 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 return *this; } + 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) { @@ -751,6 +802,16 @@ _GLIBCXX_BEGIN_NAMESPACE_CXX11 return *this; } +#if __cplusplus >= 201402L + inline path& + path::operator+=(__basic_string_view __x) + { + _M_pathname.append(__x.data(), __x.size()); + _M_split_cmpts(); + return *this; + } +#endif + template 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 __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 63c10e716dbf..503441a46904 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(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 2e1a851ff6ae..fea744ba2f2c 100644 --- a/libstdc++-v3/src/filesystem/ops.cc +++ b/libstdc++-v3/src/filesystem/ops.cc @@ -28,7 +28,9 @@ #include #include +#include #include +#include #include #include #include @@ -48,9 +50,6 @@ #endif #ifdef _GLIBCXX_USE_SENDFILE # include -#else -# include -# include #endif #if _GLIBCXX_HAVE_UTIME_H # include @@ -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,38 +444,47 @@ namespace } #ifdef _GLIBCXX_USE_SENDFILE - const auto n = ::sendfile(out.fd, in.fd, nullptr, from_st->st_size); - if (n != 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)) { - ec.assign(errno, std::generic_category()); - return false; +#endif + __gnu_cxx::stdio_filebuf sbin(in.fd, std::ios::in); + __gnu_cxx::stdio_filebuf 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 (!out.close() || !in.close()) + if (n != from_st->st_size) { ec.assign(errno, std::generic_category()); return false; } -#else - __gnu_cxx::stdio_filebuf sbin(in.fd, std::ios::in); - __gnu_cxx::stdio_filebuf 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()) + if (!out.close() || !in.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(d); #if _GLIBCXX_USE_UTIMENSAT auto ns = chrono::duration_cast(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(prms), 0)) + const int flag = (nofollow && is_symlink(st)) ? AT_SYMLINK_NOFOLLOW : 0; + if (::fchmodat(AT_FDCWD, p.c_str(), static_cast(prms), flag)) + err = errno; #else - if (::chmod(p.c_str(), static_cast(prms))) + if (nofollow && is_symlink(st)) + ec = std::make_error_code(std::errc::operation_not_supported); + else if (::chmod(p.c_str(), static_cast(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 60d91e2eb1bc..5d1b62e81625 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 000000000000..d0c50b7923b9 --- /dev/null +++ b/libstdc++-v3/testsuite/experimental/filesystem/iterators/pop.cc @@ -0,0 +1,107 @@ +// 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 -std=gnu++11" } +// { dg-require-filesystem-ts "" } + +#include +#include +#include + +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 ae98bee66c18..fbfb9a674e2f 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 ef28677e5b3f..1b8569549bf1 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 -#include #include #include @@ -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 cdb791113519..bcbdd1256cfb 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 000000000000..2addee2ad84a --- /dev/null +++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/create_symlink.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 +// . + +// { dg-options "-lstdc++fs -std=gnu++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/equivalent.cc b/libstdc++-v3/testsuite/experimental/filesystem/operations/equivalent.cc new file mode 100644 index 000000000000..9195c2cfe1cf --- /dev/null +++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/equivalent.cc @@ -0,0 +1,73 @@ +// 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 -std=gnu++11" } +// { dg-require-filesystem-ts "" } + +#include +#include +#include + +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 dba4a6f5a55b..5358fac4241c 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 000000000000..8c84edd7b002 --- /dev/null +++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/is_empty.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 +// . + +// { dg-options "-lstdc++fs -std=gnu++11" } +// { dg-require-filesystem-ts "" } + +#include +#include +#include + +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 000000000000..29282996bd3a --- /dev/null +++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/last_write_time.cc @@ -0,0 +1,155 @@ +// 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 -std=gnu++11" } +// { dg-require-filesystem-ts "" } + +// 15.25 Permissions [fs.op.last_write_time] + +#include +#include +#include + +#ifdef _GLIBCXX_HAVE_FCNTL_H +# include +#endif +#if _GLIBCXX_HAVE_UTIME_H +# include +#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(end_of_time).count(); + if (last_second > std::numeric_limits::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::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::max() - 1; + times.actime = std::numeric_limits::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 e4148609c2bf..45f1142b4f60 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 -#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,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 000000000000..c8cf7e710d6f --- /dev/null +++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/read_symlink.cc @@ -0,0 +1,50 @@ +// 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 -std=gnu++11" } +// { dg-require-filesystem-ts "" } + +#include +#include +#include + +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 000000000000..f684206f9006 --- /dev/null +++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/remove_all.cc @@ -0,0 +1,91 @@ +// 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 -std=gnu++11" } +// { dg-require-filesystem-ts "" } + +#include +#include +#include + +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 bd9b6adf3e0b..78e91a15f4e1 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 2e8922913cf3..659c4dbc48df 100644 --- a/libstdc++-v3/testsuite/experimental/filesystem/path/construct/range.cc +++ b/libstdc++-v3/testsuite/experimental/filesystem/path/construct/range.cc @@ -1,4 +1,4 @@ -// { dg-options "-std=gnu++11 -lstdc++fs" } +// { dg-options "-lstdc++fs -std=gnu++11" } // { dg-require-filesystem-ts "" } // Copyright (C) 2014-2015 Free Software Foundation, Inc. @@ -23,6 +23,7 @@ #include #include #include +#include using std::experimental::filesystem::path; using __gnu_test::compare_paths; @@ -53,6 +54,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 + r1((char*)s.c_str(), (char*)s.c_str() + s.size()); + path p9(r1.begin(), r1.end()); + compare_paths(p1, p9); + + test_container + 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 + r3(s.c_str(), s.c_str() + s.size()); + path p11(r3.begin(), r3.end()); + compare_paths(p1, p11); + + test_container + 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 + 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 + 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 + r7(ws.c_str(), ws.c_str() + ws.size()); + path p15(r7.begin(), r7.end()); + compare_paths(p1, p15); + + test_container + 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 000000000000..c247813db3a4 --- /dev/null +++ b/libstdc++-v3/testsuite/experimental/filesystem/path/construct/string_view.cc @@ -0,0 +1,55 @@ +// { dg-options "-lstdc++fs -std=gnu++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 +// . + +// 8.4.1 path constructors [path.construct] + +#include +#include +#include +#include + +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 ddcb2b7e3959..8dc2c08971d6 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 -#include +#include #include #include #include @@ -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 ); @@ -91,5 +90,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