PR libstdc++/83279 handle sendfile not copying entire file
authorJonathan Wakely <jwakely@redhat.com>
Thu, 14 Dec 2017 21:49:03 +0000 (21:49 +0000)
committerJonathan Wakely <redi@gcc.gnu.org>
Thu, 14 Dec 2017 21:49:03 +0000 (21:49 +0000)
PR libstdc++/83279
* src/filesystem/std-ops.cc (do_copy_file): Handle sendfile not
copying entire file.

From-SVN: r255666

libstdc++-v3/ChangeLog
libstdc++-v3/src/filesystem/std-ops.cc

index 9f2eff1..1b4c6e2 100644 (file)
@@ -1,5 +1,9 @@
 2017-12-14  Jonathan Wakely  <jwakely@redhat.com>
 
+       PR libstdc++/83279
+       * src/filesystem/std-ops.cc (do_copy_file): Handle sendfile not
+       copying entire file.
+
        PR libstdc++/68519
        * include/std/condition_variable (condition_variable::wait_for):
        Convert duration to native clock's duration before addition.
index fa5e19a..a15857c 100644 (file)
@@ -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)
     {
+      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;
+    }
+  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;
-      if (from_st->st_size && !(std::ostream(&sbout) << &sbin))
+
+  __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;
        }
-      if (!sbout.close() || !sbin.close())
+    }
+  else if (n > 0)
+    {
+      auto p = sbout.pubseekoff(n, std::ios_base::beg, std::ios_base::out);
+      if (p == errpos)
        {
-         ec.assign(errno, std::generic_category());
+         ec = std::make_error_code(std::errc::io_error);
          return false;
        }
-
-      ec.clear();
-      return true;
-
-#ifdef _GLIBCXX_USE_SENDFILE
     }
-  if (n != from_st->st_size)
+
+  if (count && !(std::ostream(&sbout) << &sbin))
     {
-      ec.assign(errno, std::generic_category());
+      ec = std::make_error_code(std::errc::io_error);
       return false;
     }
-  if (!out.close() || !in.close())
+  if (!sbout.close() || !sbin.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