From dd0f7ba27314978e77aab5d8da1eb6070805ee4c Mon Sep 17 00:00:00 2001 From: Jonathan Wakely Date: Thu, 17 Jan 2019 15:31:59 +0000 Subject: [PATCH] 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. From-SVN: r268034 --- libstdc++-v3/ChangeLog | 17 +++++++++ libstdc++-v3/src/c++17/fs_ops.cc | 38 ++++++++++++++++++- .../operations/create_directories.cc | 19 ++++++++++ .../operations/temp_directory_path.cc | 13 ++++++- .../operations/create_directories.cc | 14 +++++-- 5 files changed, 95 insertions(+), 6 deletions(-) diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 2ed025442562..62d5db8b4e82 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,20 @@ +2019-01-17 Jonathan Wakely + + 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 * config/abi/pre/gnu.ver (GLIBCXX_3.4.26): Add exports for fstream diff --git a/libstdc++-v3/src/c++17/fs_ops.cc b/libstdc++-v3/src/c++17/fs_ops.cc index 7ece478b62a7..5f8be5b78481 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 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 9ad5ef09f4a8..c4411dfc1e7c 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 b3ae66d7d640..2c8112f67424 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 5b3e3783af51..b6909b630d46 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 ); } }