The PR is due to missing the third argument to open(3) but there are some other errors in the same function as well as some unimplemented features, and some bad error handling elsewhere.
The autoconf check for sendfile is intentionally conservative because some BSDs define a sendfile() function with different semantics (it can only copy a file to a socket, not another regular file). Tested powerpc64le-linux, committed to trunk.
commit 65987fba7d5bd477458b586bea557c572a39bf63 Author: Jonathan Wakely <jwak...@redhat.com> Date: Thu May 14 12:54:24 2015 +0100 PR libstdc++/66011 * acinclude.m4 (GLIBCXX_CHECK_FILESYSTEM_DEPS): Check for fchmod and sendfile. * config.h.in: Regenerate. * configure: Regenerate. * src/filesystem/ops.cc (do_copy_file): Fix arguments to open(). Do not return after copying contents. Use fchmod, fchmodat, and sendfile when available. (current_path, permissions, space): Use errno not return value. diff --git a/libstdc++-v3/acinclude.m4 b/libstdc++-v3/acinclude.m4 index b2b48cc..8340572 100644 --- a/libstdc++-v3/acinclude.m4 +++ b/libstdc++-v3/acinclude.m4 @@ -3939,6 +3939,19 @@ dnl fi AC_MSG_RESULT($glibcxx_cv_st_mtim) dnl + AC_MSG_CHECKING([for fchmod]) + AC_CACHE_VAL(glibcxx_cv_fchmod, [dnl + GCC_TRY_COMPILE_OR_LINK( + [#include <sys/stat.h>], + [fchmod(1, S_IWUSR);], + [glibcxx_cv_fchmod=yes], + [glibcxx_cv_fchmod=no]) + ]) + if test $glibcxx_cv_fchmod = yes; then + AC_DEFINE(_GLIBCXX_USE_FCHMOD, 1, [Define if fchmod is available in <sys/stat.h>.]) + fi + AC_MSG_RESULT($glibcxx_cv_fchmod) +dnl AC_MSG_CHECKING([for fchmodat]) AC_CACHE_VAL(glibcxx_cv_fchmodat, [dnl GCC_TRY_COMPILE_OR_LINK( @@ -3955,6 +3968,26 @@ dnl fi AC_MSG_RESULT($glibcxx_cv_fchmodat) dnl + AC_MSG_CHECKING([for sendfile that can copy files]) + AC_CACHE_VAL(glibcxx_cv_sendfile, [dnl + case "${target_os}" in + gnu* | linux* | solaris*) + GCC_TRY_COMPILE_OR_LINK( + [#include <sys/sendfile.h>], + [sendfile(1, 2, (off_t*)NULL, sizeof 1);], + [glibcxx_cv_sendfile=yes], + [glibcxx_cv_sendfile=no]) + ;; + *) + glibcxx_cv_sendfile=no + ;; + esac + ]) + if test $glibcxx_cv_sendfile = yes; then + AC_DEFINE(_GLIBCXX_USE_SENDFILE, 1, [Define if sendfile is available in <sys/stat.h>.]) + fi + AC_MSG_RESULT($glibcxx_cv_sendfile) +dnl CXXFLAGS="$ac_save_CXXFLAGS" AC_LANG_RESTORE ]) diff --git a/libstdc++-v3/src/filesystem/ops.cc b/libstdc++-v3/src/filesystem/ops.cc index aa1ab04..f24cc19 100644 --- a/libstdc++-v3/src/filesystem/ops.cc +++ b/libstdc++-v3/src/filesystem/ops.cc @@ -41,7 +41,7 @@ #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H # include <sys/statvfs.h> #endif -#ifdef _GLIBCXX_HAVE_GNU_SENDFILE +#ifdef _GLIBCXX_USE_SENDFILE # include <sys/sendfile.h> #else # include <ext/stdio_filebuf.h> @@ -241,6 +241,8 @@ namespace } f = make_file_status(*from_st); + using opts = fs::copy_options; + if (exists(t)) { if (!is_other(t) && !is_other(f) @@ -251,12 +253,12 @@ namespace return false; } - if (is_set(option, fs::copy_options::skip_existing)) + if (is_set(option, opts::skip_existing)) { ec.clear(); return false; } - else if (is_set(option, fs::copy_options::update_existing)) + else if (is_set(option, opts::update_existing)) { if (file_time(*from_st) <= file_time(*to_st)) { @@ -264,7 +266,7 @@ namespace return false; } } - else if (!is_set(option, fs::copy_options::overwrite_existing)) + else if (!is_set(option, opts::overwrite_existing)) { ec = std::make_error_code(std::errc::file_exists); return false; @@ -282,14 +284,22 @@ namespace ec.assign(errno, std::generic_category()); return false; } - CloseFD out = { ::open(to.c_str(), O_WRONLY|O_CREAT) }; + int oflag = O_WRONLY|O_CREAT; + if (is_set(option, opts::overwrite_existing|opts::update_existing)) + oflag |= O_TRUNC; + else + oflag |= O_EXCL; + CloseFD out = { ::open(to.c_str(), oflag, S_IWUSR) }; if (out.fd == -1) { - ec.assign(errno, std::generic_category()); + if (errno == EEXIST && is_set(option, opts::skip_existing)) + ec.clear(); + else + ec.assign(errno, std::generic_category()); return false; } -#ifdef _GLIBCXX_HAVE_GNU_SENDFILE +#ifdef _GLIBCXX_USE_SENDFILE auto n = ::sendfile(out.fd, in.fd, nullptr, from_st->st_size); if (n != from_st->st_size) { @@ -299,20 +309,17 @@ namespace #else __gnu_cxx::stdio_filebuf<char> sbin(in.fd, std::ios::in); __gnu_cxx::stdio_filebuf<char> sbout(out.fd, std::ios::out); - if (std::ostream(&sbout) << &sbin) - { - ec.clear(); - return true; - } - else + if ( !(std::ostream(&sbout) << &sbin) ) { ec = std::make_error_code(std::errc::io_error); return false; } #endif -#ifdef _GLIBCXX_HAVE_FCHMOD +#ifdef _GLIBCXX_USE_FCHMOD if (::fchmod(out.fd, from_st->st_mode)) +#elif _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)) #endif @@ -320,6 +327,7 @@ namespace ec.assign(errno, std::generic_category()); return false; } + ec.clear(); return true; } } @@ -715,8 +723,8 @@ void fs::current_path(const path& p, error_code& ec) noexcept { #ifdef _GLIBCXX_HAVE_UNISTD_H - if (int err = ::chdir(p.c_str())) - ec.assign(err, std::generic_category()); + if (::chdir(p.c_str())) + ec.assign(errno, std::generic_category()); else ec.clear(); #else @@ -908,11 +916,11 @@ fs::permissions(const path& p, perms prms) void fs::permissions(const path& p, perms prms, error_code& ec) noexcept { #if _GLIBCXX_USE_FCHMODAT - if (int err = ::fchmodat(AT_FDCWD, p.c_str(), static_cast<mode_t>(prms), 0)) + if (::fchmodat(AT_FDCWD, p.c_str(), static_cast<mode_t>(prms), 0)) #else - if (int err = ::chmod(p.c_str(), static_cast<mode_t>(prms))) + if (::chmod(p.c_str(), static_cast<mode_t>(prms))) #endif - ec.assign(err, std::generic_category()); + ec.assign(errno, std::generic_category()); else ec.clear(); } @@ -1064,8 +1072,8 @@ fs::space(const path& p, error_code& ec) noexcept }; #ifdef _GLIBCXX_HAVE_SYS_STATVFS_H struct ::statvfs f; - if (int err = ::statvfs(p.c_str(), &f)) - ec.assign(err, std::generic_category()); + if (::statvfs(p.c_str(), &f)) + ec.assign(errno, std::generic_category()); else { info = space_info{