PR libstdc++/88881 adjust filesystem::status and tests for mingw semantics
authorJonathan Wakely <jwakely@redhat.com>
Thu, 17 Jan 2019 15:31:59 +0000 (15:31 +0000)
committerJonathan Wakely <redi@gcc.gnu.org>
Thu, 17 Jan 2019 15:31:59 +0000 (15:31 +0000)
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.

From-SVN: r268034

libstdc++-v3/ChangeLog
libstdc++-v3/src/c++17/fs_ops.cc
libstdc++-v3/testsuite/27_io/filesystem/operations/create_directories.cc
libstdc++-v3/testsuite/27_io/filesystem/operations/temp_directory_path.cc
libstdc++-v3/testsuite/experimental/filesystem/operations/create_directories.cc

index 2ed0254..62d5db8 100644 (file)
@@ -1,3 +1,20 @@
+2019-01-17  Jonathan Wakely  <jwakely@redhat.com>
+
+       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.
+
 2019-01-16  Jonathan Wakely  <jwakely@redhat.com>
 
        * config/abi/pre/gnu.ver (GLIBCXX_3.4.26): Add exports for fstream
index 7ece478..5f8be5b 100644 (file)
@@ -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());
index 9ad5ef0..c4411df 100644 (file)
@@ -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
   }
 }
 
index b3ae66d..2c8112f 100644 (file)
@@ -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) );
index 5b3e378..b6909b6 100644 (file)
@@ -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 );
   }
 }