diff mbox

libstdc++/66011 Fix various filesystem operations

Message ID 20150514132820.GR30202@redhat.com
State New
Headers show

Commit Message

Jonathan Wakely May 14, 2015, 1:28 p.m. UTC
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.
diff mbox

Patch

commit 65987fba7d5bd477458b586bea557c572a39bf63
Author: Jonathan Wakely <jwakely@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{