public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] PR libstdc++/88881 adjust filesystem::status and tests for mingw semantics
@ 2019-01-17 15:32 Jonathan Wakely
  0 siblings, 0 replies; only message in thread
From: Jonathan Wakely @ 2019-01-17 15:32 UTC (permalink / raw)
  To: libstdc++, gcc-patches

[-- Attachment #1: Type: text/plain, Size: 1145 bytes --]

On Windows stat("foo/bar/../.") will resolve to "foo" even if that is a
non-directory and "foo/bar" does not exist. This is the expected
behaviour and consistent with boost::filesystem, so don't try to correct
it. The only unwanted behaviour is that stat("baz/") fails due to a
mingw bug (fixed in mingw-w64 v6.0.0) so add a workaround.

	PR libstdc++/88881
	* src/c++17/fs_ops.cc (canonical(const path&, error_code&))
	[_GLIBCXX_FILESYSTEM_IS_WINDOWS]: Normalize path, to match behaviour
	of filesystem::exists.
	(create_directories(const path&, error_code&)): Add assertions.
	(status(const path&, error_code&)) [_GLIBCXX_FILESYSTEM_IS_WINDOWS]:
	Add workaround for bug in _wstat for paths with trailing slash.
	* testsuite/27_io/filesystem/operations/create_directories.cc: Adjust
	for expected behaviour on mingw.
	* testsuite/experimental/filesystem/operations/create_directories.cc:
	Likewise.
	* testsuite/27_io/filesystem/operations/temp_directory_path.cc: Use
	"TMP" instead of "TMPDIR" and clean environment before each test. Do
	not test permissions on mingw targets.

Tested x86_64-linux and x86_64-w64-mingw32, committed to trunk.


[-- Attachment #2: patch.txt --]
[-- Type: text/plain, Size: 7798 bytes --]

commit 812d6bac4fff9ad466d524f911e985cf37a47f4b
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Thu Jan 17 11:56:58 2019 +0000

    PR libstdc++/88881 adjust filesystem::status and tests for mingw semantics
    
    On Windows stat("foo/bar/../.") will resolve to "foo" even if that is a
    non-directory and "foo/bar" does not exist. This is the expected
    behaviour and consistent with boost::filesystem, so don't try to correct
    it. The only unwanted behaviour is that stat("baz/") fails due to a
    mingw bug (fixed in mingw-w64 v6.0.0) so add a workaround.
    
            PR libstdc++/88881
            * src/c++17/fs_ops.cc (canonical(const path&, error_code&))
            [_GLIBCXX_FILESYSTEM_IS_WINDOWS]: Normalize path, to match behaviour
            of filesystem::exists.
            (create_directories(const path&, error_code&)): Add assertions.
            (status(const path&, error_code&)) [_GLIBCXX_FILESYSTEM_IS_WINDOWS]:
            Add workaround for bug in _wstat for paths with trailing slash.
            * testsuite/27_io/filesystem/operations/create_directories.cc: Adjust
            for expected behaviour on mingw.
            * testsuite/experimental/filesystem/operations/create_directories.cc:
            Likewise.
            * testsuite/27_io/filesystem/operations/temp_directory_path.cc: Use
            "TMP" instead of "TMPDIR" and clean environment before each test. Do
            not test permissions on mingw targets.

diff --git a/libstdc++-v3/src/c++17/fs_ops.cc b/libstdc++-v3/src/c++17/fs_ops.cc
index 7ece478b62a..5f8be5b7848 100644
--- a/libstdc++-v3/src/c++17/fs_ops.cc
+++ b/libstdc++-v3/src/c++17/fs_ops.cc
@@ -144,7 +144,11 @@ fs::path
 fs::canonical(const path& p, error_code& ec)
 {
   path result;
+#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
+  const path pa = absolute(p.lexically_normal(), ec);
+#else
   const path pa = absolute(p, ec);
+#endif
   if (ec)
     return result;
 
@@ -483,6 +487,9 @@ fs::create_directories(const path& p, error_code& ec)
       return false;
     }
 
+  __glibcxx_assert(st.type() == file_type::not_found);
+  // !exists(p) so there must be at least one non-existent component in p.
+
   std::stack<path> missing;
   path pp = p;
 
@@ -526,6 +533,8 @@ fs::create_directories(const path& p, error_code& ec)
     }
   while (st.type() == file_type::not_found);
 
+  __glibcxx_assert(!missing.empty());
+
   bool created;
   do
     {
@@ -1318,8 +1327,35 @@ fs::file_status
 fs::status(const fs::path& p, error_code& ec) noexcept
 {
   file_status status;
+  auto str = p.c_str();
+
+#if _GLIBCXX_FILESYSTEM_IS_WINDOWS
+#if ! defined __MINGW64_VERSION_MAJOR || __MINGW64_VERSION_MAJOR < 6
+  // stat() fails if there's a trailing slash (PR 88881)
+  path p2;
+  if (p.has_relative_path())
+    {
+      wstring_view s = p.native();
+      const auto len = s.find_last_not_of(L"/\\") + wstring_view::size_type(1);
+      if (len != 0 && len != s.length())
+	{
+	  __try
+	    {
+	      p2.assign(s.substr(0, len));
+	    }
+	  __catch(const bad_alloc&)
+	    {
+	      ec = std::make_error_code(std::errc::not_enough_memory);
+	      return status;
+	    }
+	  str = p2.c_str();
+	}
+    }
+#endif
+#endif
+
   stat_type st;
-  if (posix::stat(p.c_str(), &st))
+  if (posix::stat(str, &st))
     {
       int err = errno;
       ec.assign(err, std::generic_category());
diff --git a/libstdc++-v3/testsuite/27_io/filesystem/operations/create_directories.cc b/libstdc++-v3/testsuite/27_io/filesystem/operations/create_directories.cc
index 9ad5ef09f4a..c4411dfc1e7 100644
--- a/libstdc++-v3/testsuite/27_io/filesystem/operations/create_directories.cc
+++ b/libstdc++-v3/testsuite/27_io/filesystem/operations/create_directories.cc
@@ -70,12 +70,20 @@ test01()
   b = fs::create_directories( p/"./d4/../d5", ec );
   VERIFY( !ec );
   VERIFY( b );
+#if defined(__MINGW32__) || defined(__MINGW64__)
+  // create_directories("./d4/..") is a no-op, does not create "d4"
+#else
   VERIFY( is_directory(p/"d4") );
+#endif
   VERIFY( is_directory(p/"d5") );
   VERIFY( is_directory(p/"./d4/../d5") );
 
   std::uintmax_t count = remove_all(p, ec);
+#if defined(__MINGW32__) || defined(__MINGW64__)
+  VERIFY( count == 5 );
+#else
   VERIFY( count == 6 );
+#endif
 }
 
 void
@@ -92,9 +100,11 @@ test02()
     result = create_directories(file.path, ec);
     VERIFY( !result );
     VERIFY( ec == std::errc::not_a_directory );
+    ec.clear();
     result = create_directories(file.path / "foo", ec);
     VERIFY( !result );
     VERIFY( ec == std::errc::not_a_directory );
+    ec.clear();
   }
 
   create_directories(p);
@@ -105,9 +115,18 @@ test02()
     result = create_directories(file.path, ec);
     VERIFY( !result );
     VERIFY( ec == std::errc::not_a_directory );
+    ec.clear();
     result = create_directories(file.path/"../bar", ec);
+#if defined(__MINGW32__) || defined(__MINGW64__)
+    VERIFY( result );
+    VERIFY( !ec );
+    VERIFY( is_directory(dir.path/"bar") );
+    remove(dir.path/"bar");
+#else
     VERIFY( !result );
     VERIFY( ec == std::errc::not_a_directory );
+    VERIFY( !is_directory(dir.path/"bar") );
+#endif
   }
 }
 
diff --git a/libstdc++-v3/testsuite/27_io/filesystem/operations/temp_directory_path.cc b/libstdc++-v3/testsuite/27_io/filesystem/operations/temp_directory_path.cc
index b3ae66d7d64..2c8112f6742 100644
--- a/libstdc++-v3/testsuite/27_io/filesystem/operations/temp_directory_path.cc
+++ b/libstdc++-v3/testsuite/27_io/filesystem/operations/temp_directory_path.cc
@@ -75,7 +75,7 @@ test02()
 {
   clean_env();
 
-  if (!set_env("TMPDIR", __gnu_test::nonexistent_path().string()))
+  if (!set_env("TMP", __gnu_test::nonexistent_path().string()))
     return; // just give up
 
   std::error_code ec;
@@ -95,6 +95,13 @@ test02()
 void
 test03()
 {
+#if defined(__MINGW32__) || defined(__MINGW64__)
+  // No permissions support
+  return;
+#endif
+
+  clean_env();
+
   auto p = __gnu_test::nonexistent_path();
   create_directories(p/"tmp");
   permissions(p, fs::perms::none);
@@ -119,8 +126,10 @@ test03()
 void
 test04()
 {
+  clean_env();
+
   __gnu_test::scoped_file f;
-  set_env("TMPDIR", f.path.string());
+  set_env("TMP", f.path.string());
   std::error_code ec;
   auto r = fs::temp_directory_path(ec);
   VERIFY( ec == std::make_error_code(std::errc::not_a_directory) );
diff --git a/libstdc++-v3/testsuite/experimental/filesystem/operations/create_directories.cc b/libstdc++-v3/testsuite/experimental/filesystem/operations/create_directories.cc
index 5b3e3783af5..b6909b630d4 100644
--- a/libstdc++-v3/testsuite/experimental/filesystem/operations/create_directories.cc
+++ b/libstdc++-v3/testsuite/experimental/filesystem/operations/create_directories.cc
@@ -63,12 +63,20 @@ test01()
   b = fs::create_directories( p/"./d4/../d5", ec );
   VERIFY( !ec );
   VERIFY( b );
+#if defined(__MINGW32__) || defined(__MINGW64__)
+  // create_directories("./d4/..") is a no-op, does not create "d4"
+#else
   VERIFY( is_directory(p/"d4") );
+#endif
   VERIFY( is_directory(p/"d5") );
   VERIFY( is_directory(p/"./d4/../d5") );
 
   std::uintmax_t count = remove_all(p, ec);
+#if defined(__MINGW32__) || defined(__MINGW64__)
+  VERIFY( count == 5 );
+#else
   VERIFY( count == 6 );
+#endif
 }
 
 void
@@ -87,8 +95,8 @@ test02()
     VERIFY( ec == std::errc::not_a_directory );
     result = create_directories(file.path / "foo", ec);
     VERIFY( !result );
-    __builtin_printf("%d\n", ec.value());
-    VERIFY( ec == std::errc::not_a_directory );
+    VERIFY( ec );
+    ec.clear();
   }
 
   create_directories(p);
@@ -101,7 +109,7 @@ test02()
     VERIFY( ec == std::errc::not_a_directory );
     result = create_directories(file.path/"../bar", ec);
     VERIFY( !result );
-    VERIFY( ec == std::errc::not_a_directory );
+    VERIFY( ec );
   }
 }
 

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2019-01-17 15:32 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-01-17 15:32 [PATCH] PR libstdc++/88881 adjust filesystem::status and tests for mingw semantics Jonathan Wakely

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).