From patchwork Tue Jan 30 22:00:51 2024 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Lennox Ho X-Patchwork-Id: 1893137 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@legolas.ozlabs.org Authentication-Results: legolas.ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20230601 header.b=CmlPGsKd; dkim-atps=neutral Authentication-Results: legolas.ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org (client-ip=8.43.85.97; helo=server2.sourceware.org; envelope-from=gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org; receiver=patchwork.ozlabs.org) Received: from server2.sourceware.org (server2.sourceware.org [8.43.85.97]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (secp384r1) server-digest SHA384) (No client certificate requested) by legolas.ozlabs.org (Postfix) with ESMTPS id 4TPfJY330Vz23g7 for ; Wed, 31 Jan 2024 09:01:32 +1100 (AEDT) Received: from server2.sourceware.org (localhost [IPv6:::1]) by sourceware.org (Postfix) with ESMTP id 06D4A3857731 for ; Tue, 30 Jan 2024 22:01:30 +0000 (GMT) X-Original-To: gcc-patches@gcc.gnu.org Delivered-To: gcc-patches@gcc.gnu.org Received: from mail-ua1-x935.google.com (mail-ua1-x935.google.com [IPv6:2607:f8b0:4864:20::935]) by sourceware.org (Postfix) with ESMTPS id 2F13A38582B7 for ; Tue, 30 Jan 2024 22:01:03 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.2 sourceware.org 2F13A38582B7 Authentication-Results: sourceware.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: sourceware.org; spf=pass smtp.mailfrom=gmail.com ARC-Filter: OpenARC Filter v1.0.0 sourceware.org 2F13A38582B7 Authentication-Results: server2.sourceware.org; arc=none smtp.remote-ip=2607:f8b0:4864:20::935 ARC-Seal: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1706652067; cv=none; b=QA3J7REsdcpslhHBGO1dZK1pdXQcl5JFf4hEQVHqx6rV1sGtfXLZHlscQOK1Sfn3EafgEBboPP5u4Uxj1WqdTsv8EBOb4RR4I7dBzhFzcNMUcWR/4GX5r+znPYX4zHq/NJEAgPsemPAztIhJvRs1V/RBz9WGF85/YLzlmn1ST6s= ARC-Message-Signature: i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1706652067; c=relaxed/simple; bh=j3nzWozEhCR7q2dthSxpYMxQ7CmA/wrQ6VIKOCwi5/M=; h=DKIM-Signature:MIME-Version:From:Date:Message-ID:Subject:To; b=V5GM0c0Hj40m3xYaCb5GLZOQQzkl96JWTJgjZ1okMPzPez+W97XJ9RnrtwzjPLbq0EuLyINDYdZlapbaavysKNUHQ92sVD1Bd9jo2xYFXsBSEvW9AaQLL2GXfH5uxL5vC1O/+QGb1k/8K3DEV+cFQtOmKVs9EIMkPXghXG9+ysE= ARC-Authentication-Results: i=1; server2.sourceware.org Received: by mail-ua1-x935.google.com with SMTP id a1e0cc1a2514c-7d2e16b552dso2136149241.1 for ; Tue, 30 Jan 2024 14:01:03 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1706652062; x=1707256862; darn=gcc.gnu.org; h=to:subject:message-id:date:from:mime-version:from:to:cc:subject :date:message-id:reply-to; bh=K4uZ3g+baTcS1CpWCvRys4XIF2h3xCdLz6u6s48tGJo=; b=CmlPGsKdYThj+Cq0EfdcCTjXZJKcfArtlhdZuLt6xe7PMDJ0FRnTZZdovj4IF69nVF /HtKxGsTDISrwWka3MvDHZOaSwA97mcaJ/Iqsn4lZyh17zyjcpKTfhFIAuBKBgakY7DE kHHtDiabavTLq3cX4hSnz+UXO3rrXINqwjYoq23XASZDsAGt+mRxuzVcNVv6NhXqSLHS BSg8X76QsgvFKOEz38aPnnX2syYDOz73a1qou0+tD5wdvdC3F5NwD516uh9bTo818k/K 1CP/8Z/TGX55lMnx5osVr9c4ya7LNPXmtftvHpBEZLPmAPIY6rXHs0Hx3s/lWihbLaqG d6xw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1706652062; x=1707256862; h=to:subject:message-id:date:from:mime-version:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=K4uZ3g+baTcS1CpWCvRys4XIF2h3xCdLz6u6s48tGJo=; b=iZjC0p+Aw0KHlb1JBaFrn1RH+qfDckBgMxH1xZORCwMcdqIz0h63WO0C843PLVcJL+ 1Wo8drAOq+jBiS+fw7ZpeoWV0FfcP3QearVn5eO56mZP3VsABkmeY/IYDB9WXaxVpYgD IwWp9y86cEeBXGEvtp78Jv6QDirGwN4Gos3ENGNkHycgbHiYqT1JxiV7X18x1AE+2FjO LxIpguX2blLT2KI8+1VdMlakGlfeE0MgNrvR10LXdpI3tfs4v03uxS3mVW/Y9Wv7qnbf vWN4vMRauVS/WmnqOBFzmLuQqgqczpxwxpWvvUJ5I3LlGnRRDIB5gx0T8uy4ljZAyOud aCcg== X-Gm-Message-State: AOJu0YyIyl2N3meG0sBgIalFcdMEJQTBTfgAu3NUFxoI9Em6by4LqbJf EoXae9OXkMA8zX3M9DTp8YS/KZCQntb2L/i6bHinKA9emDQJY9V3HewiPRtocpr/x+1LjErd+oE LoI3M6u7d9HdrmVUBAvwZ17X6cqLIxwt3B4s= X-Google-Smtp-Source: AGHT+IEPoBjXRvudTH3PcSFJwRbeg0XC7b8csFqG2kytuPUZqdBnxNCt0OVACC5FQlWolxCEXjSvbNq5i99i56JQibQ= X-Received: by 2002:a05:6102:9d5:b0:46b:3bcd:e38b with SMTP id g21-20020a05610209d500b0046b3bcde38bmr6288142vsi.33.1706652062261; Tue, 30 Jan 2024 14:01:02 -0800 (PST) MIME-Version: 1.0 From: Lennox Ho Date: Tue, 30 Jan 2024 17:00:51 -0500 Message-ID: Subject: [PATCH] libstdc++: Fix fs::hard_link_count behaviour on MinGW [PR113663] To: gcc-patches@gcc.gnu.org X-Spam-Status: No, score=-8.8 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, FREEMAIL_FROM, GIT_PATCH_0, KAM_SHORT, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE, URI_TRY_3LD autolearn=ham autolearn_force=no version=3.4.6 X-Spam-Checker-Version: SpamAssassin 3.4.6 (2021-04-09) on server2.sourceware.org X-BeenThere: gcc-patches@gcc.gnu.org X-Mailman-Version: 2.1.30 Precedence: list List-Id: Gcc-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: gcc-patches-bounces+incoming=patchwork.ozlabs.org@gcc.gnu.org std::filesystem::hard_link_count() always returns 1 on mingw-w64ucrt-11.0.1-r3 on Windows 10 19045 hard_link_count() queries _wstat64() on MinGW-w64 The MSFT documentation claims _wstat64() will always return 1 *non*-NTFS volumes https://learn.microsoft.com/en-us/previous-versions/visualstudio/visual-studio-2013/14h5k7ff(v=vs.120) My tests suggest that is not always true - hard_link_count()/_wstat64() still returns 1 on NTFS. GetFileInformationByHandle does return the correct result of 2. Please see the PR for a minimal repro. This patch changes the Windows implementation to always call GetFileInformationByHandle. Ran the libstdc++-v3/testsuite on x86_64-pc-linux-gnu and x86_64-pc-cygwin. Performed manual testing on x86_64-w64-mingw32. This is my first GCC contribution attempt. Please call out any omissions or mistakes. Thanks. PR target/113663 libstdc++-v3/ChangeLog: * src/c++17/fs_ops.cc (fs::equivalent): Moved helper class auto_handle to anonymous namespace as auto_win_file_handle. (fs::hard_link_count): Changed Windows implementation to use information provided by GetFileInformationByHandle which is more reliable. * testsuite/27_io/filesystem/operations/hard_link_count.cc: New test. Signed-off-by: "Lennox" Shou Hao Ho --- libstdc++-v3/src/c++17/fs_ops.cc | 59 ++++++++++++------- .../filesystem/operations/hard_link_count.cc | 54 +++++++++++++++++ 2 files changed, 91 insertions(+), 22 deletions(-) create mode 100644 libstdc++-v3/testsuite/27_io/filesystem/operations/hard_link_count.cc diff --git a/libstdc++-v3/src/c++17/fs_ops.cc b/libstdc++-v3/src/c++17/fs_ops.cc index 61df19753ef..eec3d86590f 100644 --- a/libstdc++-v3/src/c++17/fs_ops.cc +++ b/libstdc++-v3/src/c++17/fs_ops.cc @@ -822,6 +822,31 @@ fs::equivalent(const path& p1, const path& p2) return result; } +#if _GLIBCXX_FILESYSTEM_IS_WINDOWS +namespace +{ + struct auto_win_file_handle { + explicit auto_win_file_handle(const fs::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_win_file_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; + }; +} +#endif + bool fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept { @@ -858,27 +883,8 @@ fs::equivalent(const path& p1, const path& p2, error_code& ec) noexcept 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); + auto_win_file_handle h1(p1); + auto_win_file_handle h2(p2); if (!h1 || !h2) { if (!h1 && !h2) @@ -982,7 +988,16 @@ fs::hard_link_count(const path& p) std::uintmax_t fs::hard_link_count(const path& p, error_code& ec) noexcept { -#ifdef _GLIBCXX_HAVE_SYS_STAT_H +#if _GLIBCXX_FILESYSTEM_IS_WINDOWS + auto_win_file_handle h(p); + if (h && h.get_info()) + { + return static_cast(h.info.nNumberOfLinks); + } + + ec = __last_system_error(); + return static_cast(-1); +#elif defined(_GLIBCXX_HAVE_SYS_STAT_H) return do_stat(p, ec, std::mem_fn(&stat_type::st_nlink), static_cast(-1)); #else diff --git a/libstdc++-v3/testsuite/27_io/filesystem/operations/hard_link_count.cc b/libstdc++-v3/testsuite/27_io/filesystem/operations/hard_link_count.cc new file mode 100644 index 00000000000..5acba96aa82 --- /dev/null +++ b/libstdc++-v3/testsuite/27_io/filesystem/operations/hard_link_count.cc @@ -0,0 +1,54 @@ +// Copyright (C) 2019-2024 Free Software Foundation, Inc. +// +// This file is part of the GNU ISO C++ Library. This library is free +// software; you can redistribute it and/or modify it under the +// terms of the GNU General Public License as published by the +// Free Software Foundation; either version 3, or (at your option) +// any later version. + +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License along +// with this library; see the file COPYING3. If not see +// . + +// { dg-do run { target c++17 } } +// { dg-require-filesystem-ts "" } + +#include +#include +#include + +namespace fs = std::filesystem; + +void test01() +{ + // PR libstdc++/113663 + + fs::path p1 = __gnu_test::nonexistent_path(); + VERIFY( !fs::exists(p1) ); + + __gnu_test::scoped_file f1(p1); + VERIFY( fs::exists(p1) ); + + VERIFY( fs::hard_link_count(p1) == 1 ); + + fs::path p2 = __gnu_test::nonexistent_path(); + VERIFY( !fs::exists(p2) ); + + fs::create_hard_link(p1, p2); + __gnu_test::scoped_file f2(p2, __gnu_test::scoped_file::adopt_file); + VERIFY( fs::exists(p2) ); + + VERIFY( fs::hard_link_count(p1) == 2 ); + VERIFY( fs::hard_link_count(p2) == 2 ); +} + +int +main() +{ + test01(); +}