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.
commit 812d6bac4fff9ad466d524f911e985cf37a47f4b Author: Jonathan Wakely <jwak...@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 ); } }