[PATCH] PR libstdc++/83279 handle sendfile not copying entire file

Jonathan Wakely jwakely@redhat.com
Thu Dec 14 21:53:00 GMT 2017


I failed to notice that the man page for sendfile(3) says it won't
copy more than 2GiB. This refactors the code to first try sendfile
(because it's fast if it works) and if that fails, or stops before the
end of the file, then use filebufs to copy what's left over.

I'm not adding a test because creating two files larger than 2G isn't
a good idea, but I've tested it locally.

	PR libstdc++/83279
	* src/filesystem/std-ops.cc (do_copy_file): Handle sendfile not
	copying entire file.

Tested x86_64-linux, committed to trunk. Backports to follow.

-------------- next part --------------
commit ae3ff01506b1bb3232be1063b3fc7df1c80994cb
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Thu Dec 14 21:29:29 2017 +0000

    PR libstdc++/83279 handle sendfile not copying entire file
    
            PR libstdc++/83279
            * src/filesystem/std-ops.cc (do_copy_file): Handle sendfile not
            copying entire file.

diff --git a/libstdc++-v3/src/filesystem/std-ops.cc b/libstdc++-v3/src/filesystem/std-ops.cc
index fa5e19a36ba..a15857c31bf 100644
--- a/libstdc++-v3/src/filesystem/std-ops.cc
+++ b/libstdc++-v3/src/filesystem/std-ops.cc
@@ -382,48 +382,71 @@ fs::do_copy_file(const char* from, const char* to,
       return false;
     }
 
+  ssize_t n = 0;
+  size_t count = from_st->st_size;
 #ifdef _GLIBCXX_USE_SENDFILE
-  off_t offset = 0;
-  const auto n = ::sendfile(out.fd, in.fd, &offset, from_st->st_size);
-  if (n < 0 && (errno == ENOSYS || errno == EINVAL))
+  n = ::sendfile(out.fd, in.fd, nullptr, count);
+  if (n < 0 && errno != ENOSYS && errno != EINVAL)
     {
-#endif // _GLIBCXX_USE_SENDFILE
-      __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;
+    }
+  if ((size_t)n == count)
+    {
+      if (!out.close() || !in.close())
 	{
 	  ec.assign(errno, std::generic_category());
 	  return false;
 	}
-
       ec.clear();
       return true;
-
-#ifdef _GLIBCXX_USE_SENDFILE
     }
-  if (n != from_st->st_size)
+  else if (n > 0)
+    count -= n;
+#endif // _GLIBCXX_USE_SENDFILE
+
+  __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;
+
+  const std::streampos errpos(std::streamoff(-1));
+
+  if (n < 0)
+    {
+      auto p1 = sbin.pubseekoff(0, std::ios_base::beg, std::ios_base::in);
+      auto p2 = sbout.pubseekoff(0, std::ios_base::beg, std::ios_base::out);
+      if (p1 == errpos || p2 == errpos)
+	{
+	  ec = std::make_error_code(std::errc::io_error);
+	  return false;
+	}
+    }
+  else if (n > 0)
+    {
+      auto p = sbout.pubseekoff(n, std::ios_base::beg, std::ios_base::out);
+      if (p == errpos)
+	{
+	  ec = std::make_error_code(std::errc::io_error);
+	  return false;
+	}
+    }
+
+  if (count && !(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;
     }
-  if (!out.close() || !in.close())
-    {
-      ec.assign(errno, std::generic_category());
-      return false;
-    }
-
   ec.clear();
   return true;
-#endif // _GLIBCXX_USE_SENDFILE
 }
 #endif // NEED_DO_COPY_FILE
 #endif // _GLIBCXX_HAVE_SYS_STAT_H


More information about the Libstdc++ mailing list