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
This commit is contained in:
Jonathan Wakely 2019-01-17 15:31:59 +00:00 committed by Jonathan Wakely
parent a9e48eaada
commit dd0f7ba273
5 changed files with 95 additions and 6 deletions

View 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

View 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());

View 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
}
}

View 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) );

View 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 );
}
}