diff mbox series

PR libstdc++/79283 fix filesystem::read_symlink for /proc

Message ID 20171025124707.GA26781@redhat.com
State New
Headers show
Series PR libstdc++/79283 fix filesystem::read_symlink for /proc | expand

Commit Message

Jonathan Wakely Oct. 25, 2017, 12:47 p.m. UTC
For some paths lstat returns st_size == 0, so use a buffer with
non-zero size and then grow it dynamically if it's not big enough.

	PR libstdc++/79283
	* src/filesystem/ops.cc (read_symlink): Handle st_size being zero.
	* src/filesystem/std-ops.cc (read_symlink): Likewise.
	(do_copy_file) [!NEED_DO_COPY_FILE]: Avoid multiple definitions.

Tested x86+64-linux, committed to trunk. I'll backport the
experimental::filesystem::read_symlink part to the branches.
commit ef737409818853af3a2b9a8b22984194d8c61de7
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Wed Oct 25 02:15:25 2017 +0100

    PR libstdc++/79283 fix filesystem::read_symlink for /proc
    
            PR libstdc++/79283
            * src/filesystem/ops.cc (read_symlink): Handle st_size being zero.
            * src/filesystem/std-ops.cc (read_symlink): Likewise.
            (do_copy_file) [!NEED_DO_COPY_FILE]: Avoid multiple definitions.
diff mbox series

Patch

diff --git a/libstdc++-v3/src/filesystem/ops.cc b/libstdc++-v3/src/filesystem/ops.cc
index 61d9c89e616..1ec8883fde9 100644
--- a/libstdc++-v3/src/filesystem/ops.cc
+++ b/libstdc++-v3/src/filesystem/ops.cc
@@ -962,26 +962,45 @@  fs::read_symlink(const path& p)
 
 fs::path fs::read_symlink(const path& p, error_code& ec)
 {
+  path result;
 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
   stat_type st;
   if (::lstat(p.c_str(), &st))
     {
       ec.assign(errno, std::generic_category());
-      return {};
+      return result;
     }
-  std::string buf(st.st_size, '\0');
-  ssize_t len = ::readlink(p.c_str(), &buf.front(), buf.size());
-  if (len == -1)
+  std::string buf(st.st_size ? st.st_size + 1 : 128, '\0');
+  do
     {
-      ec.assign(errno, std::generic_category());
-      return {};
+      ssize_t len = ::readlink(p.c_str(), buf.data(), buf.size());
+      if (len == -1)
+	{
+	  ec.assign(errno, std::generic_category());
+	  return result;
+	}
+      else if (len == (ssize_t)buf.size())
+	{
+	  if (buf.size() > 4096)
+	    {
+	      ec.assign(ENAMETOOLONG, std::generic_category());
+	      return result;
+	    }
+	  buf.resize(buf.size() * 2);
+	}
+      else
+	{
+	  buf.resize(len);
+	  result.assign(buf);
+	  ec.clear();
+	  break;
+	}
     }
-  ec.clear();
-  return path{buf.data(), buf.data()+len};
+  while (true);
 #else
   ec = std::make_error_code(std::errc::not_supported);
-  return {};
 #endif
+  return result;
 }
 
 
diff --git a/libstdc++-v3/src/filesystem/std-ops.cc b/libstdc++-v3/src/filesystem/std-ops.cc
index ff7acbfc1e7..947be7ef4c5 100644
--- a/libstdc++-v3/src/filesystem/std-ops.cc
+++ b/libstdc++-v3/src/filesystem/std-ops.cc
@@ -24,6 +24,7 @@ 
 
 #ifndef _GLIBCXX_USE_CXX11_ABI
 # define _GLIBCXX_USE_CXX11_ABI 1
+# define NEED_DO_COPY_FILE
 #endif
 
 #include <filesystem>
@@ -251,6 +252,7 @@  namespace std::filesystem
 }
 
 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
+#ifdef NEED_DO_COPY_FILE
 bool
 fs::do_copy_file(const char* from, const char* to,
 		 copy_options_existing_file options,
@@ -423,6 +425,7 @@  fs::do_copy_file(const char* from, const char* to,
   return true;
 #endif // _GLIBCXX_USE_SENDFILE
 }
+#endif // NEED_DO_COPY_FILE
 #endif // _GLIBCXX_HAVE_SYS_STAT_H
 
 void
@@ -1166,26 +1169,45 @@  fs::read_symlink(const path& p)
 
 fs::path fs::read_symlink(const path& p, error_code& ec)
 {
+  path result;
 #ifdef _GLIBCXX_HAVE_SYS_STAT_H
   stat_type st;
   if (::lstat(p.c_str(), &st))
     {
       ec.assign(errno, std::generic_category());
-      return {};
+      return result;
     }
-  std::string buf(st.st_size, '\0');
-  ssize_t len = ::readlink(p.c_str(), &buf.front(), buf.size());
-  if (len == -1)
+  std::string buf(st.st_size ? st.st_size + 1 : 128, '\0');
+  do
     {
-      ec.assign(errno, std::generic_category());
-      return {};
+      ssize_t len = ::readlink(p.c_str(), buf.data(), buf.size());
+      if (len == -1)
+	{
+	  ec.assign(errno, std::generic_category());
+	  return result;
+	}
+      else if (len == (ssize_t)buf.size())
+	{
+	  if (buf.size() > 4096)
+	    {
+	      ec.assign(ENAMETOOLONG, std::generic_category());
+	      return result;
+	    }
+	  buf.resize(buf.size() * 2);
+	}
+      else
+	{
+	  buf.resize(len);
+	  result.assign(buf);
+	  ec.clear();
+	  break;
+	}
     }
-  ec.clear();
-  return path{buf.data(), buf.data()+len};
+  while (true);
 #else
   ec = std::make_error_code(std::errc::not_supported);
-  return {};
 #endif
+  return result;
 }
 
 fs::path