diff mbox series

Fix filesystem::equivalent for mingw

Message ID 20190117153250.GA27124@redhat.com
State New
Headers show
Series Fix filesystem::equivalent for mingw | expand

Commit Message

Jonathan Wakely Jan. 17, 2019, 3:32 p.m. UTC
* src/c++17/fs_ops.cc
	(equivalent(const path&, const path&, error_code&))
	[_GLIBCXX_FILESYSTEM_IS_WINDOWS]: Use GetFileInformationByHandle to
	compare files instead of relying on incomplete info returned by stat.

Tested x86_64-linux and x86_64-w64-mingw32, committed to trunk.
commit 6c860ebca6e0b486e9b95231e175fb4084ca3f0a
Author: Jonathan Wakely <jwakely@redhat.com>
Date:   Thu Jan 17 14:18:38 2019 +0000

    Fix filesystem::equivalent for mingw
    
            * src/c++17/fs_ops.cc
            (equivalent(const path&, const path&, error_code&))
            [_GLIBCXX_FILESYSTEM_IS_WINDOWS]: Use GetFileInformationByHandle to
            compare files instead of relying on incomplete info returned by stat.
diff mbox series

Patch

diff --git a/libstdc++-v3/src/c++17/fs_ops.cc b/libstdc++-v3/src/c++17/fs_ops.cc
index c4b99fb36ce..3ff0ded1c66 100644
--- a/libstdc++-v3/src/c++17/fs_ops.cc
+++ b/libstdc++-v3/src/c++17/fs_ops.cc
@@ -851,7 +851,49 @@  fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept
       ec.clear();
       if (is_other(s1) || is_other(s2))
 	return false;
+#if _GLIBCXX_FILESYSTEM_IS_WINDOWS
+      // st_ino is not set, so can't be used to distinguish files
+      if (st1.st_mode != st2.st_mode || st1.st_dev != st2.st_dev)
+	return false;
+
+      struct auto_handle {
+	explicit auto_handle(const path& p_)
+	: handle(CreateFileW(p_.c_str(), 0,
+	      FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
+	      0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0))
+	{ }
+
+	~auto_handle()
+	{ if (*this) CloseHandle(handle); }
+
+	explicit operator bool() const
+	{ return handle != INVALID_HANDLE_VALUE; }
+
+	bool get_info()
+	{ return GetFileInformationByHandle(handle, &info); }
+
+	HANDLE handle;
+	BY_HANDLE_FILE_INFORMATION info;
+      };
+      auto_handle h1(p1);
+      auto_handle h2(p2);
+      if (!h1 || !h2)
+	{
+	  if (!h1 && !h2)
+	    ec.assign((int)GetLastError(), generic_category());
+	  return false;
+	}
+      if (!h1.get_info() || !h2.get_info())
+	{
+	  ec.assign((int)GetLastError(), generic_category());
+	  return false;
+	}
+      return h1.info.dwVolumeSerialNumber == h2.info.dwVolumeSerialNumber
+	&& h1.info.nFileIndexHigh == h2.info.nFileIndexHigh
+	&& h1.info.nFileIndexLow == h2.info.nFileIndexLow;
+#else
       return st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino;
+#endif
     }
   else if (!exists(s1) && !exists(s2))
     ec = std::make_error_code(std::errc::no_such_file_or_directory);