Fix filesystem::equivalent for mingw
authorJonathan Wakely <jwakely@redhat.com>
Thu, 17 Jan 2019 15:32:10 +0000 (15:32 +0000)
committerJonathan Wakely <redi@gcc.gnu.org>
Thu, 17 Jan 2019 15:32:10 +0000 (15:32 +0000)
* 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.

From-SVN: r268036

libstdc++-v3/ChangeLog
libstdc++-v3/src/c++17/fs_ops.cc

index 1be8354aeca54ea91383878aa8b4f4535adf45c7..3ea31688ae8d56d0ddbf8b8cdfc221f7e4a77f7b 100644 (file)
@@ -1,5 +1,10 @@
 2019-01-17  Jonathan Wakely  <jwakely@redhat.com>
 
+       * 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.
+
        PR libstdc++/88884
        * src/c++17/fs_ops.cc (absolute(const path&, error_code&)): Do nothing
        if the path is already absolute.
index c4b99fb36ce52e4c91a8934a8a8221fa25466375..3ff0ded1c66cde6a4615c73f05f5a7173044ebf4 100644 (file)
@@ -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);