+2016-12-12 Jonathan Wakely <jwakely@redhat.com>
+
+ 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 <ville.voutilainen@gmail.com>
Backport from mainline
}
void pop();
+ void pop(error_code&);
void disable_recursion_pending() { _M_pending = false; }
unknown = 0xFFFF,
add_perms = 0x10000,
remove_perms = 0x20000,
- resolve_symlinks = 0x40000
+ symlink_nofollow = 0x40000
};
constexpr perms
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; }
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;
#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
_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
* @{
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&, ...);
_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>
__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)
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>&
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>&
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
_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>
_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
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)
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)
{
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)
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(); }
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)
std::error_code(err, std::generic_category())));
ec->assign(err, std::generic_category());
- return {nullptr, p};
+ return {};
}
inline fs::file_type
"directory iterator cannot advance",
std::error_code(err, std::generic_category())));
ec->assign(err, std::generic_category());
- return true;
+ return false;
}
else
{
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&
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));
}
}
{
_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));
}
}
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));
}
#include <experimental/filesystem>
#include <functional>
+#include <ostream>
#include <stack>
+#include <ext/stdio_filebuf.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#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>
#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();
}
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
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);
}
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;
};
#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))
}
#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<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 (!out.close() || !in.close())
+ if (n != from_st->st_size)
{
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())
+ if (!out.close() || !in.close())
{
ec.assign(errno, std::generic_category());
return false;
}
-#endif
ec.clear();
return true;
+#endif
}
}
#endif
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))
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))
{
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
{
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;
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
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
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());
}
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;
_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);
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();
}
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);
#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;
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
{
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
}
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);
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);
// 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);
}
--- /dev/null
+// 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 -std=gnu++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();
+}
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);
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);
// 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 );
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);
}
// libstdc++/71004
const fs::recursive_directory_iterator it;
- VERIFY( it == fs::recursive_directory_iterator() );
+ VERIFY( it == end(it) );
}
void
// 15.3 Copy [fs.op.copy]
#include <experimental/filesystem>
-#include <fstream>
#include <testsuite_fs.h>
#include <testsuite_hooks.h>
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.
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 );
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.
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.
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
VERIFY( !ec );
VERIFY( exists(to) );
VERIFY( file_size(to) == file_size(from) );
+
+ remove(from);
+ remove(to);
}
int
--- /dev/null
+// 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 -std=gnu++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();
+}
--- /dev/null
+// 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 -std=gnu++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();
+}
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
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
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
test01();
test02();
test03();
+ test04();
}
--- /dev/null
+// 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 -std=gnu++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();
+ 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();
+}
--- /dev/null
+// 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 -std=gnu++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();
+}
// 15.26 Permissions [fs.op.permissions]
#include <experimental/filesystem>
-#include <fstream>
#include <testsuite_fs.h>
#include <testsuite_hooks.h>
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 );
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();
}
--- /dev/null
+// 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 -std=gnu++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();
+}
--- /dev/null
+// 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 -std=gnu++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();
+}
std::error_code ec;
fs::path p1 = fs::temp_directory_path(ec);
+ VERIFY( !ec );
VERIFY( exists(p1) );
fs::path p2 = fs::temp_directory_path();
std::error_code ec;
fs::path p = fs::temp_directory_path(ec);
VERIFY( ec );
+ VERIFY( p == fs::path() );
std::error_code ec2;
try {
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();
}
-// { 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.
#include <experimental/filesystem>
#include <string>
#include <testsuite_fs.h>
+#include <testsuite_iterators.h>
using std::experimental::filesystem::path;
using __gnu_test::compare_paths;
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
}
}
--- /dev/null
+// { 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
+// <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();
+}
#define _TESTSUITE_FS_H 1
#include <experimental/filesystem>
-#include <iostream>
+#include <fstream>
#include <string>
#include <cstdio>
#include <stdlib.h>
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 );
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