Bug 88881 - std::filesystem::status gives bad results on mingw32
Summary: std::filesystem::status gives bad results on mingw32
Status: RESOLVED FIXED
Alias: None
Product: gcc
Classification: Unclassified
Component: libstdc++ (show other bugs)
Version: 9.0
: P3 normal
Target Milestone: 9.5
Assignee: Not yet assigned to anyone
URL:
Keywords:
: 98035 (view as bug list)
Depends on:
Blocks:
 
Reported: 2019-01-16 15:22 UTC by Jonathan Wakely
Modified: 2022-07-22 07:07 UTC (History)
2 users (show)

See Also:
Host:
Target: *-*-mingw*
Build:
Known to work:
Known to fail:
Last reconfirmed: 2019-01-16 00:00:00


Attachments

Note You need to log in before you can comment on or make changes to this bug.
Description Jonathan Wakely 2019-01-16 15:22:09 UTC
// { dg-options "-std=gnu++17" }
#include <filesystem>
#include <iostream>

void print_status(std::filesystem::path p)
{
  std::cout << p << " exists? " << std::boolalpha << exists(p) << '\n';
}

int main()
{
  print_status("./");
  print_status("./nonexistent-path/..");
  print_status("./nonexistent-path/../");
}

This prints:

"./" exists? false
"./nonexistent-path/.." exists? true
"./nonexistent-path/../" exists? false


It seems that mingw's stat() fails for a directory with a trailing slash.

It also implicitly resolves .. components, without consideration for whether the previous component exists or not (which seems consistent with DOS commands, e.g. "cd .\kgjhfjfj\.." is equivalent to "cd .", but is not how stat is supposed to work).

I'll report these to mingw32-w64 but will need to add workarounds to std::filesystem.
Comment 1 Jonathan Wakely 2019-01-16 16:39:58 UTC
The trailing slash problem is apparently fixed in mingw-w64:
https://sourceforge.net/p/mingw-w64/bugs/643/
Comment 2 Jonathan Wakely 2019-01-16 16:57:21 UTC
But that fix looks wrong, it means "file/" will resolve to "file" and that's wrong a for a non-directory, because "file/" should fail.

Testcase demonstrating the mingw bugs:

#include <sys/stat.h>
#include <stdio.h>

void print(const char* s, int i, int expected)
{
  printf("%-26s %2d %s\n", s, i, i == expected ? "" : " FAIL");
}

void f1()
{
  struct stat st;
  int i;
  i = stat(".\\", &st);
  print("stat(\".\\\")", i, 0);
  i = stat(".\\nonesuch\\..", &st);
  print("stat(\".\\nonesuch\\..\")", i, -1);

  // Trailing slash after a non-directory should fail:
  i = stat("a.exe\\", &st);
  print("stat(\"a.exe\\\")", i, -1);
  // "/." after a non-directory is also wrong:
  i = stat("a.exe\\.", &st);
  print("stat(\"a.exe\\.\")", i, -1);
  // And resolving "/dir/.." after a non-directory is also wrong:
  i = stat("a.exe\\nonesuch\\..", &st);
  print("stat(\"a.exe\\nonesuch\\..\")", i, -1);
}

void f2()
{
  struct _stat st;
  int i;
  i = _stat(".\\", &st);
  print("_stat(\".\\\")", i, 0);
  i = _stat(".\\nonesuch\\..", &st);
  print("_stat(\".\\nonesuch\\..\")", i, -1);
}

void f3()
{
  struct _stat st;
  int i;
  i = _wstat(L".\\", &st);
  print("_wstat(L\".\\\")", i, 0);
  i = _wstat(L".\\nonesuch\\..", &st);
  print("_wstat(L\".\\nonesuch\\..\")", i, -1);
}

int main()
{
  f1();
  f2();
  f3();
}


Output for mingw-w64 5.0

stat(".\")                  0 
stat(".\nonesuch\..")       0  FAIL
stat("a.exe\")              0  FAIL
stat("a.exe\.")             0  FAIL
stat("a.exe\nonesuch\..")   0  FAIL
_stat(".\")                -1  FAIL
_stat(".\nonesuch\..")      0  FAIL
_wstat(L".\")              -1  FAIL
_wstat(L".\nonesuch\..")    0  FAIL
Comment 3 Jonathan Wakely 2019-01-16 17:15:11 UTC
The "nonexistent-path/.." part is reported as https://sourceforge.net/p/mingw-w64/bugs/782/
Comment 4 Jonathan Wakely 2019-01-16 22:40:59 UTC
This bug is the cause of:

FAIL: experimental/filesystem/operations/canonical.cc execution test

The trailing slashes in the paths cause the _wstat calls to fail.
Comment 5 Jonathan Wakely 2019-01-16 23:40:06 UTC
(In reply to Jonathan Wakely from comment #4)
> This bug is the cause of:
> 
> FAIL: experimental/filesystem/operations/canonical.cc execution test

And also:

FAIL: experimental/filesystem/operations/temp_directory_path.cc execution test

because GetTempPathW always returns a directory with a trailing slash.
Comment 6 Jonathan Wakely 2019-01-17 11:39:32 UTC
(In reply to Jonathan Wakely from comment #3)
> The "nonexistent-path/.." part is reported as
> https://sourceforge.net/p/mingw-w64/bugs/782/

And is apparently a feature not a bug. That's just how paths work on Windows. Go figure.

So the remaining problem is the trailing slash, which is fixed in mingw-w64 v6.0.0, and I have a workaround for.
Comment 7 Jonathan Wakely 2019-01-17 15:32:37 UTC
Author: redi
Date: Thu Jan 17 15:31:59 2019
New Revision: 268034

URL: https://gcc.gnu.org/viewcvs?rev=268034&root=gcc&view=rev
Log:
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.

Modified:
    trunk/libstdc++-v3/ChangeLog
    trunk/libstdc++-v3/src/c++17/fs_ops.cc
    trunk/libstdc++-v3/testsuite/27_io/filesystem/operations/create_directories.cc
    trunk/libstdc++-v3/testsuite/27_io/filesystem/operations/temp_directory_path.cc
    trunk/libstdc++-v3/testsuite/experimental/filesystem/operations/create_directories.cc
Comment 8 Jonathan Wakely 2019-01-17 15:34:32 UTC
fixed on trunk
Comment 9 Jonathan Wakely 2019-05-29 22:01:24 UTC
Author: redi
Date: Wed May 29 22:00:53 2019
New Revision: 271755

URL: https://gcc.gnu.org/viewcvs?rev=271755&root=gcc&view=rev
Log:
PR libstdc++/88881 fix filesystem::symlink_status for Windows

The fix for PR 88881 only added a workaround to filesystem::status, but
filesystem::symlink_status is also affected by the _wstat bug and needs
the same workaround.

The recent change to optimize path::parent_path() means that the
workaround can be simplified to just use parent_path().

	PR libstdc++/88881
	* src/c++17/fs_ops.cc [_GLIBCXX_FILESYSTEM_IS_WINDOWS]
	(status(const path&, error_code&)): Use parent_path() to remove
	trailing slash.
	(symlink_status(const path&, error_code&)): Duplicate workaround for
	bug in _wstat for paths with trailing slash.
	* testsuite/27_io/filesystem/operations/remove_all.cc: Check path
	with trailing slash.
	* testsuite/27_io/filesystem/operations/status.cc: Likewise.
	* testsuite/27_io/filesystem/operations/symlink_status.cc: Likewise.

Modified:
    trunk/libstdc++-v3/ChangeLog
    trunk/libstdc++-v3/src/c++17/fs_ops.cc
    trunk/libstdc++-v3/testsuite/27_io/filesystem/operations/remove_all.cc
    trunk/libstdc++-v3/testsuite/27_io/filesystem/operations/status.cc
    trunk/libstdc++-v3/testsuite/27_io/filesystem/operations/symlink_status.cc
Comment 10 Jonathan Wakely 2019-06-14 18:11:54 UTC
Author: redi
Date: Fri Jun 14 18:11:22 2019
New Revision: 272304

URL: https://gcc.gnu.org/viewcvs?rev=272304&root=gcc&view=rev
Log:
PR libstdc++/88881 fix filesystem::symlink_status for Windows

The fix for PR 88881 only added a workaround to filesystem::status, but
filesystem::symlink_status is also affected by the _wstat bug and needs
the same workaround.

The recent change to optimize path::parent_path() means that the
workaround can be simplified to just use parent_path().

Backport from mainline
2019-05-29  Jonathan Wakely  <jwakely@redhat.com>

	PR libstdc++/88881
	* src/c++17/fs_ops.cc [_GLIBCXX_FILESYSTEM_IS_WINDOWS]
	(status(const path&, error_code&)): Use parent_path() to remove
	trailing slash.
	(symlink_status(const path&, error_code&)): Duplicate workaround for
	bug in _wstat for paths with trailing slash.
	* testsuite/27_io/filesystem/operations/remove_all.cc: Check path
	with trailing slash.
	* testsuite/27_io/filesystem/operations/status.cc: Likewise.
	* testsuite/27_io/filesystem/operations/symlink_status.cc: Likewise.

Modified:
    branches/gcc-9-branch/libstdc++-v3/ChangeLog
    branches/gcc-9-branch/libstdc++-v3/src/c++17/fs_ops.cc
    branches/gcc-9-branch/libstdc++-v3/testsuite/27_io/filesystem/operations/remove_all.cc
    branches/gcc-9-branch/libstdc++-v3/testsuite/27_io/filesystem/operations/status.cc
    branches/gcc-9-branch/libstdc++-v3/testsuite/27_io/filesystem/operations/symlink_status.cc
Comment 11 Jonathan Wakely 2019-06-14 18:13:05 UTC
std::filesystem::symlink_status (and functions that use it) also fixed for 9.2
Comment 12 Jonathan Wakely 2020-11-27 13:55:36 UTC
*** Bug 98035 has been marked as a duplicate of this bug. ***
Comment 13 Jonathan Wakely 2020-11-27 14:00:46 UTC
My fix for the trailing slash does:

#if _GLIBCXX_FILESYSTEM_IS_WINDOWS
#if ! defined __MINGW64_VERSION_MAJOR || __MINGW64_VERSION_MAJOR < 6
  // stat() fails if there's a trailing slash (PR 88881)

I'm seeing status("./") fail with mingw64 6.0.0 so apparently the workaround is still needed.
Comment 14 Jonathan Wakely 2020-11-27 14:32:26 UTC
It looks like _stat and _wstat were fixed, but not _wstat64 which is what we use.
Comment 15 GCC Commits 2021-02-10 16:57:36 UTC
The master branch has been updated by Jonathan Wakely <redi@gcc.gnu.org>:

https://gcc.gnu.org/g:3df5b249b3c81e95cdcb293a388155ae5b168f9e

commit r11-7174-g3df5b249b3c81e95cdcb293a388155ae5b168f9e
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Wed Feb 10 16:51:34 2021 +0000

    libstdc++: Re-enable workaround for _wstat64 bug [PR 88881]
    
    This wasn't fixed upstream for mingw-w64 so we still need the
    workaround.
    
    libstdc++-v3/ChangeLog:
    
            PR libstdc++/88881
            * src/c++17/fs_ops.cc (fs::status): Re-enable workaround.
Comment 16 GCC Commits 2021-02-12 15:50:02 UTC
The master branch has been updated by Jonathan Wakely <redi@gcc.gnu.org>:

https://gcc.gnu.org/g:b7210405ed8eb5fd723b2c99960dcc5f0aec89b4

commit r11-7222-gb7210405ed8eb5fd723b2c99960dcc5f0aec89b4
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Wed Feb 10 16:51:34 2021 +0000

    libstdc++: Re-enable workaround for _wstat64 bug, again [PR 88881]
    
    I forgot that the workaround is present in both filesystem::status and
    filesystem::symlink_status. This restores it in the latter.
    
    libstdc++-v3/ChangeLog:
    
            PR libstdc++/88881
            * src/c++17/fs_ops.cc (fs::symlink_status): Re-enable workaround.
Comment 17 GCC Commits 2021-03-29 20:02:25 UTC
The releases/gcc-10 branch has been updated by Jonathan Wakely <redi@gcc.gnu.org>:

https://gcc.gnu.org/g:8455158044503a25ebb044428be2ddc83d2b0473

commit r10-9582-g8455158044503a25ebb044428be2ddc83d2b0473
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Wed Feb 10 16:51:34 2021 +0000

    libstdc++: Re-enable workaround for _wstat64 bug [PR 88881]
    
    This wasn't fixed upstream for mingw-w64 so we still need the
    workaround.
    
    libstdc++-v3/ChangeLog:
    
            PR libstdc++/88881
            * src/c++17/fs_ops.cc (fs::status): Re-enable workaround.
    
    (cherry picked from commit 3df5b249b3c81e95cdcb293a388155ae5b168f9e)
Comment 18 GCC Commits 2021-03-29 20:02:30 UTC
The releases/gcc-10 branch has been updated by Jonathan Wakely <redi@gcc.gnu.org>:

https://gcc.gnu.org/g:bad3463f257019946d8b17d6956ca058aef88eae

commit r10-9583-gbad3463f257019946d8b17d6956ca058aef88eae
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Wed Feb 10 16:51:34 2021 +0000

    libstdc++: Re-enable workaround for _wstat64 bug, again [PR 88881]
    
    I forgot that the workaround is present in both filesystem::status and
    filesystem::symlink_status. This restores it in the latter.
    
    libstdc++-v3/ChangeLog:
    
            PR libstdc++/88881
            * src/c++17/fs_ops.cc (fs::symlink_status): Re-enable workaround.
    
    (cherry picked from commit b7210405ed8eb5fd723b2c99960dcc5f0aec89b4)
Comment 19 Jonathan Wakely 2021-03-29 20:05:20 UTC
Re-fixed for 10.3 as well
Comment 20 Richard Biener 2021-06-01 08:12:48 UTC
GCC 9.4 is being released, retargeting bugs to GCC 9.5.
Comment 21 GCC Commits 2021-08-02 15:58:38 UTC
The releases/gcc-9 branch has been updated by Jonathan Wakely <redi@gcc.gnu.org>:

https://gcc.gnu.org/g:1a96bfe35fec4c425d6f5fbb9c539386e39ff3e4

commit r9-9656-g1a96bfe35fec4c425d6f5fbb9c539386e39ff3e4
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Wed Feb 10 16:51:34 2021 +0000

    libstdc++: Re-enable workaround for _wstat64 bug [PR 88881]
    
    This wasn't fixed upstream for mingw-w64 so we still need the
    workaround.
    
    libstdc++-v3/ChangeLog:
    
            PR libstdc++/88881
            * src/c++17/fs_ops.cc (fs::status): Re-enable workaround.
    
    (cherry picked from commit 3df5b249b3c81e95cdcb293a388155ae5b168f9e)
Comment 22 GCC Commits 2021-08-02 15:58:43 UTC
The releases/gcc-9 branch has been updated by Jonathan Wakely <redi@gcc.gnu.org>:

https://gcc.gnu.org/g:4178e8c049e4ef366335b661197e53b3efbed877

commit r9-9657-g4178e8c049e4ef366335b661197e53b3efbed877
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Wed Feb 10 16:51:34 2021 +0000

    libstdc++: Re-enable workaround for _wstat64 bug, again [PR 88881]
    
    I forgot that the workaround is present in both filesystem::status and
    filesystem::symlink_status. This restores it in the latter.
    
    libstdc++-v3/ChangeLog:
    
            PR libstdc++/88881
            * src/c++17/fs_ops.cc (fs::symlink_status): Re-enable workaround.
    
    (cherry picked from commit b7210405ed8eb5fd723b2c99960dcc5f0aec89b4)
Comment 23 Jonathan Wakely 2021-08-02 15:58:59 UTC
Fixed for 9.5 too.
Comment 24 GCC Commits 2022-06-30 13:16:53 UTC
The master branch has been updated by Jonathan Wakely <redi@gcc.gnu.org>:

https://gcc.gnu.org/g:6c96b14a19a9e6c365eacc59868a866b99f9786d

commit r13-1365-g6c96b14a19a9e6c365eacc59868a866b99f9786d
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Tue Jun 28 15:56:30 2022 +0100

    libstdc++: Fix experimental::filesystem::status on Windows [PR88881]
    
    Although the Filesystem TS isn't properly supported on Windows (unlike
    the C++17 Filesystem lib), most tests do pass. Two of the failures are
    due to PR 88881 which was only fixed for std::filesystem not the TS.
    This applies the fix to the TS implementation too.
    
    libstdc++-v3/ChangeLog:
    
            PR libstdc++/88881
            * src/filesystem/ops.cc (has_trailing_slash): New helper
            function.
            (fs::status): Strip trailing slashes.
            (fs::symlink_status): Likewise.
            * testsuite/experimental/filesystem/operations/temp_directory_path.cc:
            Clean the environment before each test and use TMP instead of
            TMPDIR so the test passes on Windows.
Comment 25 GCC Commits 2022-07-21 11:16:29 UTC
The releases/gcc-12 branch has been updated by Jonathan Wakely <redi@gcc.gnu.org>:

https://gcc.gnu.org/g:f3ff78e3db0fc18127dac4fe3eaf113d0c5ddd01

commit r12-8590-gf3ff78e3db0fc18127dac4fe3eaf113d0c5ddd01
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Tue Jun 28 15:56:30 2022 +0100

    libstdc++: Fix experimental::filesystem::status on Windows [PR88881]
    
    Although the Filesystem TS isn't properly supported on Windows (unlike
    the C++17 Filesystem lib), most tests do pass. Two of the failures are
    due to PR 88881 which was only fixed for std::filesystem not the TS.
    This applies the fix to the TS implementation too.
    
    libstdc++-v3/ChangeLog:
    
            PR libstdc++/88881
            * src/filesystem/ops.cc (has_trailing_slash): New helper
            function.
            (fs::status): Strip trailing slashes.
            (fs::symlink_status): Likewise.
            * testsuite/experimental/filesystem/operations/temp_directory_path.cc:
            Clean the environment before each test and use TMP instead of
            TMPDIR so the test passes on Windows.
    
    (cherry picked from commit 6c96b14a19a9e6c365eacc59868a866b99f9786d)
Comment 26 GCC Commits 2022-07-22 07:07:36 UTC
The releases/gcc-11 branch has been updated by Jonathan Wakely <redi@gcc.gnu.org>:

https://gcc.gnu.org/g:66e876a91b5d5c1a0c5067f2d8f1d531da3a81f8

commit r11-10161-g66e876a91b5d5c1a0c5067f2d8f1d531da3a81f8
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Tue Jun 28 15:56:30 2022 +0100

    libstdc++: Fix experimental::filesystem::status on Windows [PR88881]
    
    Although the Filesystem TS isn't properly supported on Windows (unlike
    the C++17 Filesystem lib), most tests do pass. Two of the failures are
    due to PR 88881 which was only fixed for std::filesystem not the TS.
    This applies the fix to the TS implementation too.
    
    libstdc++-v3/ChangeLog:
    
            PR libstdc++/88881
            * src/filesystem/ops.cc (has_trailing_slash): New helper
            function.
            (fs::status): Strip trailing slashes.
            (fs::symlink_status): Likewise.
            * testsuite/experimental/filesystem/operations/temp_directory_path.cc:
            Clean the environment before each test and use TMP instead of
            TMPDIR so the test passes on Windows.
    
    (cherry picked from commit 6c96b14a19a9e6c365eacc59868a866b99f9786d)