get:
Show a patch.

patch:
Update a patch.

put:
Update a patch.

GET /api/patches/2197056/?format=api
HTTP 200 OK
Allow: GET, PUT, PATCH, HEAD, OPTIONS
Content-Type: application/json
Vary: Accept

{
    "id": 2197056,
    "url": "http://patchwork.ozlabs.org/api/patches/2197056/?format=api",
    "web_url": "http://patchwork.ozlabs.org/project/gcc/patch/20260216224136.71140-1-adam.wood@mines.sdsmt.edu/",
    "project": {
        "id": 17,
        "url": "http://patchwork.ozlabs.org/api/projects/17/?format=api",
        "name": "GNU Compiler Collection",
        "link_name": "gcc",
        "list_id": "gcc-patches.gcc.gnu.org",
        "list_email": "gcc-patches@gcc.gnu.org",
        "web_url": null,
        "scm_url": null,
        "webscm_url": null,
        "list_archive_url": "",
        "list_archive_url_format": "",
        "commit_url_format": ""
    },
    "msgid": "<20260216224136.71140-1-adam.wood@mines.sdsmt.edu>",
    "list_archive_url": null,
    "date": "2026-02-16T22:36:58",
    "name": "[v4] libstdc++: Add symlink support on Windows",
    "commit_ref": null,
    "pull_url": null,
    "state": "new",
    "archived": false,
    "hash": "ff5b22efe8917ebab67294374c92f2b9350be87c",
    "submitter": {
        "id": 91323,
        "url": "http://patchwork.ozlabs.org/api/people/91323/?format=api",
        "name": "Adam Wood",
        "email": "adam.wood@mines.sdsmt.edu"
    },
    "delegate": null,
    "mbox": "http://patchwork.ozlabs.org/project/gcc/patch/20260216224136.71140-1-adam.wood@mines.sdsmt.edu/mbox/",
    "series": [
        {
            "id": 492358,
            "url": "http://patchwork.ozlabs.org/api/series/492358/?format=api",
            "web_url": "http://patchwork.ozlabs.org/project/gcc/list/?series=492358",
            "date": "2026-02-16T22:36:58",
            "name": "[v4] libstdc++: Add symlink support on Windows",
            "version": 4,
            "mbox": "http://patchwork.ozlabs.org/series/492358/mbox/"
        }
    ],
    "comments": "http://patchwork.ozlabs.org/api/patches/2197056/comments/",
    "check": "pending",
    "checks": "http://patchwork.ozlabs.org/api/patches/2197056/checks/",
    "tags": {},
    "related": [],
    "headers": {
        "Return-Path": "<gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org>",
        "X-Original-To": [
            "incoming@patchwork.ozlabs.org",
            "gcc-patches@gcc.gnu.org"
        ],
        "Delivered-To": [
            "patchwork-incoming@legolas.ozlabs.org",
            "gcc-patches@gcc.gnu.org"
        ],
        "Authentication-Results": [
            "legolas.ozlabs.org;\n\tdkim=pass (1024-bit key;\n secure) header.d=mines.sdsmt.edu header.i=@mines.sdsmt.edu\n header.a=rsa-sha256 header.s=sdsmtmines header.b=Ceob9KZG;\n\tdkim-atps=neutral",
            "legolas.ozlabs.org;\n spf=pass (sender SPF authorized) smtp.mailfrom=gcc.gnu.org\n (client-ip=2620:52:6:3111::32; helo=vm01.sourceware.org;\n envelope-from=gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org;\n receiver=patchwork.ozlabs.org)",
            "sourceware.org;\n\tdkim=pass (1024-bit key,\n secure) header.d=mines.sdsmt.edu header.i=@mines.sdsmt.edu\n header.a=rsa-sha256 header.s=sdsmtmines header.b=Ceob9KZG",
            "sourceware.org; dmarc=pass (p=quarantine dis=none)\n header.from=mines.sdsmt.edu",
            "sourceware.org; spf=pass smtp.mailfrom=mines.sdsmt.edu",
            "server2.sourceware.org;\n arc=none smtp.remote-ip=209.85.222.176"
        ],
        "Received": [
            "from vm01.sourceware.org (vm01.sourceware.org\n [IPv6:2620:52:6:3111::32])\n\t(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)\n\t key-exchange x25519 server-signature ECDSA (secp384r1) server-digest SHA384)\n\t(No client certificate requested)\n\tby legolas.ozlabs.org (Postfix) with ESMTPS id 4fFHqg5hLBz1xpl\n\tfor <incoming@patchwork.ozlabs.org>; Tue, 17 Feb 2026 09:43:26 +1100 (AEDT)",
            "from vm01.sourceware.org (localhost [127.0.0.1])\n\tby sourceware.org (Postfix) with ESMTP id C482D4BAD167\n\tfor <incoming@patchwork.ozlabs.org>; Mon, 16 Feb 2026 22:43:22 +0000 (GMT)",
            "from mail-qk1-f176.google.com (mail-qk1-f176.google.com\n [209.85.222.176])\n by sourceware.org (Postfix) with ESMTPS id 7E3A34BA23D7\n for <gcc-patches@gcc.gnu.org>; Mon, 16 Feb 2026 22:42:03 +0000 (GMT)",
            "by mail-qk1-f176.google.com with SMTP id\n af79cd13be357-8cb48234b08so322984185a.1\n for <gcc-patches@gcc.gnu.org>; Mon, 16 Feb 2026 14:42:03 -0800 (PST)",
            "from gcc-test-vm (96-3-109-172-dynamic.midco.net. [96.3.109.172])\n by smtp.gmail.com with ESMTPSA id\n 6a1803df08f44-8971cdb5dbfsm165780646d6.41.2026.02.16.14.42.01\n (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256);\n Mon, 16 Feb 2026 14:42:02 -0800 (PST)"
        ],
        "DKIM-Filter": [
            "OpenDKIM Filter v2.11.0 sourceware.org C482D4BAD167",
            "OpenDKIM Filter v2.11.0 sourceware.org 7E3A34BA23D7"
        ],
        "DMARC-Filter": "OpenDMARC Filter v1.4.2 sourceware.org 7E3A34BA23D7",
        "ARC-Filter": "OpenARC Filter v1.0.0 sourceware.org 7E3A34BA23D7",
        "ARC-Seal": "i=1; a=rsa-sha256; d=sourceware.org; s=key; t=1771281723; cv=none;\n b=ZmUz9pZvgt1Aj5uquXPPuvRS/cBlnEqsr/cqzj/3hymOJLUElLNi5POohtInPRC25QOMr8nAMDTjKoK1Dv6nywGN2qQO31DsCsLRujpb2Tz12ILu1M69lnUusdYuuiTndvGgkYsjrS3bz/PmI5h4E3G39QVtvbgwJNjiGgjSiPM=",
        "ARC-Message-Signature": "i=1; a=rsa-sha256; d=sourceware.org; s=key;\n t=1771281723; c=relaxed/simple;\n bh=8aeUrZ6RE+qtn1VIDm1JFciacJWzuFgWsWE/BRAzkVk=;\n h=DKIM-Signature:From:To:Subject:Date:Message-ID:MIME-Version;\n b=c0j269f0s63l9/czFc5Nqwt3DwMMr4mHfzq/PkutvE4Y0416rUOHI7aimnTwh6TIPFb1umUGnjCMiJpD/h8oIje+Z1sSxCjlM+QzX7r5EzGPayIgc7HNHQyexWVzCO6Nf2pIK9FxVODUB9j5a75o9mub4FmGoKSepFYW0nsx15Y=",
        "ARC-Authentication-Results": "i=1; server2.sourceware.org",
        "DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=mines.sdsmt.edu; s=sdsmtmines; t=1771281723; x=1771886523;\n darn=gcc.gnu.org;\n h=content-transfer-encoding:mime-version:message-id:date:subject:cc\n :to:from:from:to:cc:subject:date:message-id:reply-to;\n bh=oJ6XPTmv2uANtVCyh8nqon9zXWD8NOEBqkQbQMtHX9k=;\n b=Ceob9KZGX1NruRQLbjpz0f0jWytPfFBj/6bOg+2CB0SXQMEOAtciG+zXxgGvwTe5cE\n S57xP1JFBRbNfESolNLUnlyLoF617CaNf39lAnxeqjtNeb34AmQiqSvp8uWdSvVcXQFG\n DJ1330xY2AVfNJ2SahSu0RuU5xveUh/ZGe7Gc=",
        "X-Google-DKIM-Signature": "v=1; a=rsa-sha256; c=relaxed/relaxed;\n d=1e100.net; s=20230601; t=1771281723; x=1771886523;\n h=content-transfer-encoding:mime-version:message-id:date:subject:cc\n :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date\n :message-id:reply-to;\n bh=oJ6XPTmv2uANtVCyh8nqon9zXWD8NOEBqkQbQMtHX9k=;\n b=fxVzP/oEfVYi017x6I57ivockjsKj1dtuSRsRmoiOW4AKMoPgxz39m4KHdVJ2Cw5MK\n 5EygyE3Vt9PYBEiZq054XtU/BCvPsZj+RsuU1yxiNiee/0dSpQWfR6BniepOM+FFbpBT\n JmUkCbdkRwc5lVQf9BFU5jk5+oveP0vzQ8FfbgALwwO1sRfoN9/Gc62+Q2Po1r0gOqgV\n 6noc9UoqtaYKvW12hgXr7bXAswsI5NlHLuI4tPOFruvGrmp426KTy+0FWxDtgdOkX8Eo\n bkLNZcDDHiJnG2KbFb4zT4Qt1DpH2Iz634FK4B1VEf7xmuu38LTk1bOdsypC66O7WFCd\n B/IA==",
        "X-Gm-Message-State": "AOJu0Yz0ppO+ckh9x0euMZ+meBkgKULEPM7nbjMJlAmKIV/7jyBD/vOT\n WakGN3VEs0bjKiRGgs8kblcdMDuBs9RDVJl9l4Sbl2a06jD0086oVMd2NZWA6Tr0ZzPOTR4f2FV\n H2dCpug==",
        "X-Gm-Gg": "AZuq6aLr56/W/kWqGSdRlUnYBLJVFGgfYh8HgULIvVG2iKBe+xPxuzZfG7aQJiuxm/Y\n XrUnysEK6/I8lqbl4W8oo66oOzy+BZAJ2NqCYsNC4yhLHV5+23vJ5icsem4IbvXw4FcFj2mlPI/\n Ly9jMKtzOuQCpqFDyWsxrFB4jBNB7RI1/85cfUprO7V+K3/8x+6kyIM+C5EUa7kuZmOVvbTc4tw\n b+SqcDNBTg7B0ew51QxziUeEXQvCvvBUclhZKSOy3GAsNsogqSD9BWOFa9+ERlw7k48wUUcMMxk\n SgkzvwslwcaVAxO0YUYwHfl+LRbsPFa4ekIiDSvBSVLGNm5LNfjG/DP0/PWWH3Ebw5q/cgIkwMF\n xPbrL3c53hlGjkufv7TAXhRsiXTHJPcxbzuQeZIbgMIE7KxCL58f3P3WOpoB/pRLRqsa30CdKf+\n dMV0NT2a2qHSybiLg+3KQ6gr3iPdumm1Vwew1UKpQST1EpYhblb9OBwFOmSRVr",
        "X-Received": "by 2002:a05:620a:4010:b0:8ca:4438:b91b with SMTP id\n af79cd13be357-8cb4c00d58bmr1037379685a.57.1771281722837;\n Mon, 16 Feb 2026 14:42:02 -0800 (PST)",
        "From": "Adam Wood <adam.wood@mines.sdsmt.edu>",
        "To": "libstdc++@gcc.gnu.org",
        "Cc": "gcc-patches@gcc.gnu.org,\n\tAdam Wood <adam.wood@mines.sdsmt.edu>",
        "Subject": "[PATCH v4] libstdc++: Add symlink support on Windows",
        "Date": "Mon, 16 Feb 2026 14:36:58 -0800",
        "Message-ID": "<20260216224136.71140-1-adam.wood@mines.sdsmt.edu>",
        "X-Mailer": "git-send-email 2.52.0",
        "MIME-Version": "1.0",
        "Content-Transfer-Encoding": "8bit",
        "X-BeenThere": "gcc-patches@gcc.gnu.org",
        "X-Mailman-Version": "2.1.30",
        "Precedence": "list",
        "List-Id": "Gcc-patches mailing list <gcc-patches.gcc.gnu.org>",
        "List-Unsubscribe": "<https://gcc.gnu.org/mailman/options/gcc-patches>,\n <mailto:gcc-patches-request@gcc.gnu.org?subject=unsubscribe>",
        "List-Archive": "<https://gcc.gnu.org/pipermail/gcc-patches/>",
        "List-Post": "<mailto:gcc-patches@gcc.gnu.org>",
        "List-Help": "<mailto:gcc-patches-request@gcc.gnu.org?subject=help>",
        "List-Subscribe": "<https://gcc.gnu.org/mailman/listinfo/gcc-patches>,\n <mailto:gcc-patches-request@gcc.gnu.org?subject=subscribe>",
        "Errors-To": "gcc-patches-bounces~incoming=patchwork.ozlabs.org@gcc.gnu.org"
    },
    "content": "Tested on x86_64-w64-mingw32 on Windows 11.\n\nThe changes in v4 make sure the new symlink functions are only defined/enabled\non versions of Windows with symlink support. \n\nBecause path resolution may be different between Windows and POSIX when a\ndotdot follows a symlink, I also made some edits to tests involving that\nspecific case. I removed my changes to fs::absolute and\nfs::canonical because I was misplaced and was trying to force Windows to\nact like POSIX in that specific case.\n\nFinally, I dealt with the issue that creating symlinks with the unprivileged\nflag returns an error in earlier versions of Windows by just trying again\nif ERROR_INVALID_PARAMETER occurs without the unpriviledged flag. This\nis how MSVC STL does it as well.\n\nThe patch does not support junctions or mount points.\n\nChanges in v4:\n  * Remove my changes to fs::absolute and fs::canonical.\n  * Set error code in windows_create_symlink if SYMBOLIC_LINK_FLAG_DIRECTORY\n    is not defined.\n  * Attempt symlink creation with SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE\n    and if that causes a ERROR_INVALID_PARAMETER try again without the\n    unprivileged flag.\n  * Don't define windows_read_symlink_handle if SYMBOLIC_LINK_FLAG_DIRECTORY\n    is not defined.\n  * Set error code in fs::read_symlink on Windows \n    if SYMBOLIC_LINK_FLAG_DIRECTORY is not defined.\n  * Return 0 unconditionally in __detail::__is_handle_symlink if\n    SYMBOLIC_LINK_FLAG_DIRECTORY is not defined.\n  * Use stat for lstat if SYMBOLIC_LINK_FLAG_DIRECTORY is not defined.\n  * Changed an ifdef NO_SYMLINKS to defined(NO_SYMLINKS) or\n    defined(_GLIBCXX_FILESYSTEM_IS_WINDOWS) in test03 for fs::canonical.\n  * Disabled, on Windows, some sections of test01 for fs::weakly_canonical\n    that tested a dotdot immediately after a symlink.\n\nChanges in v3:\n  * Include <ntdef.h> for REPARSE_DATA_BUFFER and removed\n    the manual definition of REPARSE_DATA_BUFFER.\n  * Replace path_starts_with_dotdot with path_find_dotdot_component\n    which returns an iterator to the first component that is \"..\"\n    or the end iterator.\n  * Wrap the inner fs::exists call inside a Windows-specific ifdef.\n  * Use PRREPARSE_DATA_BUFFER instead of REPARSE_DATA_BUFFER*.\n  * Move the helper functions for __gnu_posix::stat into a new\n    namespace called __detail.\n\nChanges in v2:\n  * Wrapped Windows specific code in an ifdef.\n\nlibstdc++-v3/Changelog:\n\n\t* src/c++17/fs_ops.cc:\n\tInclude <winioctl.h> for FSCTL_GET_REPARSE_POINT.\n\tInclude <ntdef.h> for REPARSE_DATA_BUFFER.\n\t(windows_create_symlink): New helper function for fs::create_symlink\n\tand fs::create_directory_symlink.\n\t(fs::create_directory_symlink): Call windows_create_symlink on Windows.\n\t(fs::create_symlink): Call windows_create_symlink on Windows.\n\t(auto_win_file_handle::auto_win_file_handle): Add follow_symlink\n\tparameter to control whether the handle should open the symlink\n\tor the target, with a default value of true.\n\t(windows_read_symlink_handle): New helper function\n\tfor fs::read_symlink.\n\t(fs::read_symlink): Call windows_read_symlink_handle on Windows.\n\t(fs::remove): Call RemoveDirectoryW only for directories, and\n\tDeleteFileW for regular files, but attempt both for symlinks.\n\t(fs::remove_all): Return immediately if path is empty.\n\tCheck if path points to a symlink, and if so, remove the\n\tsymlink using fs::remove.\n\t* src/filesystem/ops-common.h:\n\tDefine S_IFLNK and S_ISLNK.\n\tCreate helper namespace __detail.\n\t(__detail::__open_for_stat): New helper function for stat and lstat.\n\t(__detail::__is_handle_symlink): New helper function for\n\tstat, lstat, and fs::read_symlink.\n\t(__detail::__stat_windows): New helper function for stat and lstat.\n\t(__gnu_posix::stat, __gnu_posix::lstat): Use __stat_windows to properly\n\tfollow or not follow symlinks, and check if file is a symlink.\n\t* testsuite/27_io/filesystem/operations/canonical.cc (test03):\n\tUse fs::create_directory_symlink instead of fs::create_symlink.\n\tCheck if NO_SYMLINKS or _GLIBCXX_FILESYSTEM_IS_WINDOWS instead of just\n\tchecking NO_SYMLINKS when defining baz.\n\t* testsuite/27_io/filesystem/operations/copy.cc (test02):\n\tCreate a symlink to temporary file instead of \".\".\n\tUse fs::exists(symlink_status()) instead of fs::exists for symlinks.\n\t* testsuite/27_io/filesystem/operations/weakly_canonical.cc (test01):\n\tUse fs::create_directory_symlink instead of fs::create_symlink.\n\tWrap statements that test a dotdot after a symlink in an ifndef\n\t_GLIBCXX_FILESYSTEM_IS_WINDOWS.\n\t* testsuite/util/testsuite_fs.h: Do not define NO_SYMLINKS on Windows.\n---\n libstdc++-v3/src/c++17/fs_ops.cc              | 210 +++++++++++++++++-\n libstdc++-v3/src/filesystem/ops-common.h      |  89 +++++++-\n .../27_io/filesystem/operations/canonical.cc  |   4 +-\n .../27_io/filesystem/operations/copy.cc       |   7 +-\n .../filesystem/operations/weakly_canonical.cc |   6 +-\n libstdc++-v3/testsuite/util/testsuite_fs.h    |   2 +\n 6 files changed, 302 insertions(+), 16 deletions(-)",
    "diff": "diff --git a/libstdc++-v3/src/c++17/fs_ops.cc b/libstdc++-v3/src/c++17/fs_ops.cc\nindex 454962c7521..4d8153d3f64 100644\n--- a/libstdc++-v3/src/c++17/fs_ops.cc\n+++ b/libstdc++-v3/src/c++17/fs_ops.cc\n@@ -56,6 +56,8 @@\n #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS\n # define WIN32_LEAN_AND_MEAN\n # include <windows.h>\n+# include <winioctl.h> // FSCTL_GET_REPARSE_POINT\n+# include <ntdef.h> // REPARSE_DATA_BUFFER\n #endif\n \n #define _GLIBCXX_BEGIN_NAMESPACE_FILESYSTEM namespace filesystem {\n@@ -644,6 +646,52 @@ fs::create_directory(const path& p, const path& attributes,\n #endif\n }\n \n+#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS\n+namespace\n+{\n+  void\n+  windows_create_symlink(const fs::path& to, const fs::path& new_symlink,\n+\t\t\t const fs::file_type target_type,\n+\t\t\t std::error_code& ec) noexcept\n+  {\n+#ifdef SYMBOLIC_LINK_FLAG_DIRECTORY\n+    DWORD symlink_type = target_type == fs::file_type::directory\n+\t\t\t ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0;\n+    // Windows can't handle relative symlinks with non-preferred slashes.\n+    // Creating the symlink will succeed, but the symlink won't resolve\n+    // correctly in later operations.\n+    const fs::path* preferred_to = &to;\n+    fs::path to2;\n+    if (to.native().find(L'/') != std::string::npos)\n+      {\n+\t__try\n+\t  {\n+\t    to2 = to;\n+\t    to2.make_preferred();\n+\t    preferred_to = &to2;\n+\t  }\n+\t__catch (const std::bad_alloc&)\n+\t  {\n+\t    ec = std::make_error_code(std::errc::not_enough_memory);\n+\t    return;\n+\t  }\n+      }\n+    if (CreateSymbolicLinkW(new_symlink.c_str(), preferred_to->c_str(),\n+\t\t\t    symlink_type\n+\t\t\t    | SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE))\n+      ec.clear();\n+    else if (GetLastError() == ERROR_INVALID_PARAMETER\n+\t     && CreateSymbolicLinkW(new_symlink.c_str(), preferred_to->c_str(),\n+\t\t\t\t    symlink_type))\n+      ec.clear();\n+    else\n+      ec = std::__last_system_error();\n+#else\n+    ec = std::make_error_code(std::errc::function_not_supported);\n+#endif\n+  }\n+}\n+#endif\n \n void\n fs::create_directory_symlink(const path& to, const path& new_symlink)\n@@ -660,7 +708,7 @@ fs::create_directory_symlink(const path& to, const path& new_symlink,\n \t\t\t     error_code& ec) noexcept\n {\n #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS\n-  ec = std::make_error_code(std::errc::function_not_supported);\n+  windows_create_symlink(to, new_symlink, file_type::directory, ec);\n #else\n   create_symlink(to, new_symlink, ec);\n #endif\n@@ -715,6 +763,8 @@ fs::create_symlink(const path& to, const path& new_symlink,\n     ec.assign(errno, std::generic_category());\n   else\n     ec.clear();\n+#elif _GLIBCXX_FILESYSTEM_IS_WINDOWS\n+  windows_create_symlink(to, new_symlink, file_type::regular, ec);\n #else\n   ec = std::make_error_code(std::errc::function_not_supported);\n #endif\n@@ -829,10 +879,14 @@ namespace\n   struct auto_win_file_handle\n   {\n     explicit\n-    auto_win_file_handle(const wchar_t* p, std::error_code& ec) noexcept\n+    auto_win_file_handle(const wchar_t* p, std::error_code& ec,\n+\t\t\t const bool follow_symlink = true) noexcept\n     : handle(CreateFileW(p, 0,\n \t\t\t FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,\n-\t\t\t 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)),\n+\t\t\t 0, OPEN_EXISTING,\n+\t\t\t (FILE_FLAG_BACKUP_SEMANTICS\n+\t\t\t  | (follow_symlink ? 0 : FILE_FLAG_OPEN_REPARSE_POINT)),\n+\t\t\t 0)),\n       ec(ec)\n     {\n       if (handle == INVALID_HANDLE_VALUE)\n@@ -1193,6 +1247,74 @@ fs::proximate(const path& p, const path& base, error_code& ec)\n   return result;\n }\n \n+#if defined(_GLIBCXX_FILESYSTEM_IS_WINDOWS) \\\n+    && defined(SYMBOLIC_LINK_FLAG_DIRECTORY)\n+namespace\n+{\n+  void\n+  windows_read_symlink_handle(auto_win_file_handle& link_handle,\n+\t\t\t      std::error_code& ec,\n+\t\t\t      fs::path& result)\n+  {\n+    PREPARSE_DATA_BUFFER reparse_buffer = nullptr;\n+    std::unique_ptr<char[]> big_buffer;\n+\n+    // Allocate enough memory on the stack to get the reparse data\n+    // plus a 260 character path.  Should be sufficient in most cases.\n+    // Allocate an extra wchar_t to ensure we can manually null terminate.\n+    static constexpr size_t small_buffer_size = sizeof(REPARSE_DATA_BUFFER)\n+\t\t\t\t\t\t+ 260 * sizeof(wchar_t);\n+    char small_buffer[small_buffer_size + sizeof(wchar_t)];\n+    reparse_buffer = reinterpret_cast<PREPARSE_DATA_BUFFER>(&small_buffer[0]);\n+    long unsigned int bytes_returned, big_buffer_size;\n+\n+    // Attempt to get the reparse data with the buffer on the stack\n+    // before allocating the exact amount needed on the heap.\n+    bool got_reparse_data = DeviceIoControl(link_handle.handle,\n+\t\t\t\t\t    FSCTL_GET_REPARSE_POINT,\n+\t\t\t\t\t    nullptr, 0,\n+\t\t\t\t\t    &small_buffer, small_buffer_size,\n+\t\t\t\t\t    &bytes_returned, nullptr);\n+\n+    int last_error = GetLastError();\n+    if (!got_reparse_data && last_error == ERROR_MORE_DATA)\n+      {\n+\tbig_buffer_size = bytes_returned;\n+\tbig_buffer.reset(new char[big_buffer_size + sizeof(wchar_t)]);\n+\tgot_reparse_data = DeviceIoControl(link_handle.handle,\n+\t\t\t\t\t   FSCTL_GET_REPARSE_POINT,\n+\t\t\t\t\t   nullptr, 0,\n+\t\t\t\t\t   big_buffer.get(), big_buffer_size,\n+\t\t\t\t\t   &bytes_returned, nullptr);\n+\tif (!got_reparse_data)\n+\t  {\n+\t    ec = std::__last_system_error();\n+\t    return;\n+\t  }\n+\n+\treparse_buffer\n+\t  = reinterpret_cast<PREPARSE_DATA_BUFFER>(big_buffer.get());\n+\n+      }\n+    else\n+      {\n+\tif (!got_reparse_data)\n+\t  {\n+\t    ec = std::__last_system_error();\n+\t    return;\n+\t  }\n+      }\n+\n+    ec.clear();\n+    auto& symlink_buffer = reparse_buffer->SymbolicLinkReparseBuffer;\n+    wchar_t* target_name = &symlink_buffer.PathBuffer[0];\n+    target_name += symlink_buffer.PrintNameOffset / sizeof(wchar_t);\n+    target_name[symlink_buffer.PrintNameLength / sizeof(wchar_t)] = L'\\0';\n+    result = target_name;\n+  }\n+};\n+#endif // _GLIBCXX_FILESYSTEM_IS_WINDOWS\n+\n fs::path\n fs::read_symlink(const path& p)\n {\n@@ -1248,6 +1370,26 @@ fs::path fs::read_symlink(const path& p, error_code& ec)\n \tbufsz *= 2;\n     }\n   while (true);\n+#elif defined(_GLIBCXX_FILESYSTEM_IS_WINDOWS) \\\n+      && defined(SYMBOLIC_LINK_FLAG_DIRECTORY)\n+  auto_win_file_handle link_handle(p.c_str(), ec, false);\n+  if (!link_handle)\n+    return result;\n+\n+  int is_symlink = __detail::__is_handle_symlink(link_handle.handle);\n+  if (is_symlink == -1)\n+    {\n+      ec = __last_system_error();\n+      return result;\n+    }\n+\n+  if (!is_symlink)\n+    {\n+      ec.assign(EINVAL, std::generic_category());\n+      return result;\n+    }\n+\n+  windows_read_symlink_handle(link_handle, ec, result);\n #else\n   ec = std::make_error_code(std::errc::function_not_supported);\n #endif\n@@ -1291,8 +1433,14 @@ fs::remove(const path& p, error_code& ec) noexcept\n   auto st = symlink_status(p, ec);\n   if (exists(st))\n     {\n-      if ((is_directory(p, ec) && RemoveDirectoryW(p.c_str()))\n-\t  || DeleteFileW(p.c_str()))\n+      if ((is_directory(st) || is_symlink(st))\n+\t  && RemoveDirectoryW(p.c_str()))\n+\t{\n+\t  ec.clear();\n+\t  return true;\n+\t}\n+      else if ((is_regular_file(st) || is_symlink(st))\n+\t       && DeleteFileW(p.c_str()))\n \t{\n \t  ec.clear();\n \t  return true;\n@@ -1320,6 +1468,30 @@ std::uintmax_t\n fs::remove_all(const path& p)\n {\n   error_code ec;\n+#if _GLIBCXX_FILESYSTEM_IS_WINDOWS\n+  if (p.empty())\n+    return 0;\n+  // The current opendir implementation on Windows always follows an initial\n+  // symlink.  Therefore, if remove_all is called on a symlink,\n+  // the target is removed.  Call remove if we have a symlink.\n+  auto p_status = symlink_status(p, ec);\n+  if (!exists(p_status))\n+    {\n+      int err = ec.default_error_condition().value();\n+      bool not_found = !ec || is_not_found_errno(err);\n+      if (!not_found)\n+\t_GLIBCXX_THROW_OR_ABORT(filesystem_error(\"cannot remove all\",\n+\t\t\t\t\t\t p, ec));\n+      return 0;\n+    }\n+  if (is_symlink(p_status))\n+    {\n+      if (!remove(p, ec))\n+\t_GLIBCXX_THROW_OR_ABORT(filesystem_error(\"cannot remove all\",\n+\t\t\t\t\t\t p, ec));\n+      return 1;\n+    }\n+#endif\n   uintmax_t count = 0;\n   recursive_directory_iterator dir(p, directory_options{64|128}, ec);\n   switch (ec.value()) // N.B. assumes ec.category() == std::generic_category()\n@@ -1363,6 +1535,34 @@ fs::remove_all(const path& p)\n std::uintmax_t\n fs::remove_all(const path& p, error_code& ec)\n {\n+#if _GLIBCXX_FILESYSTEM_IS_WINDOWS\n+  if (p.empty())\n+    {\n+      ec.clear();\n+      return 0;\n+    }\n+  // The current opendir implementation on Windows always follows an initial\n+  // symlink.  Therefore, if remove_all is called on a symlink,\n+  // the target is removed.  Call remove if we have a symlink.\n+  auto p_status = symlink_status(p, ec);\n+  if (!exists(p_status))\n+    {\n+      int err = ec.default_error_condition().value();\n+      bool not_found = !ec || is_not_found_errno(err);\n+      if (not_found)\n+\t{\n+\t  ec.clear();\n+\t  return 0;\n+\t}\n+      return -1;\n+    }\n+  if (is_symlink(p_status))\n+    {\n+      if (remove(p, ec))\n+\treturn 1;\n+      return ec ? -1 : 0;\n+    }\n+#endif\n   uintmax_t count = 0;\n   recursive_directory_iterator dir(p, directory_options{64|128}, ec);\n   switch (ec.value()) // N.B. assumes ec.category() == std::generic_category()\ndiff --git a/libstdc++-v3/src/filesystem/ops-common.h b/libstdc++-v3/src/filesystem/ops-common.h\nindex 304d5896d02..5203a65443f 100644\n--- a/libstdc++-v3/src/filesystem/ops-common.h\n+++ b/libstdc++-v3/src/filesystem/ops-common.h\n@@ -105,6 +105,84 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION\n \n namespace filesystem\n {\n+#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS\n+namespace __detail\n+{\n+#define S_IFLNK 0xC000\n+#define\tS_ISLNK(m)\t(((m) & S_IFMT) == S_IFLNK)\n+\n+  using stat_type = struct ::__stat64;\n+\n+  inline HANDLE __open_for_stat(const wchar_t* path, bool following_symlinks)\n+  {\n+    constexpr auto share_flags\n+      = FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE;\n+    auto file_flags = FILE_FLAG_BACKUP_SEMANTICS;\n+    if (!following_symlinks)\n+      file_flags |= FILE_FLAG_OPEN_REPARSE_POINT;\n+    HANDLE handle;\n+    handle = CreateFileW(path, 0, share_flags, 0, OPEN_EXISTING, file_flags, 0);\n+\n+    if (handle == INVALID_HANDLE_VALUE)\n+      {\n+\t// CreateFileW does not set errno.\n+\tstd::error_condition generic_error\n+\t  = std::__last_system_error().default_error_condition();\n+\terrno = generic_error.value();\n+      }\n+\n+    return handle;\n+  }\n+\n+  // -1 error, 0 not a symlink, 1 a symlink\n+  inline int __is_handle_symlink(HANDLE handle)\n+  {\n+#ifdef SYMBOLIC_LINK_FLAG_DIRECTORY\n+    FILE_ATTRIBUTE_TAG_INFO type_info;\n+    if (!GetFileInformationByHandleEx(handle, FileAttributeTagInfo,\n+\t\t\t\t      &type_info, sizeof(type_info)))\n+      {\n+\terrno = std::__last_system_error().default_error_condition().value();\n+\treturn -1;\n+      }\n+    return type_info.FileAttributes & FILE_ATTRIBUTE_REPARSE_POINT\n+\t   && type_info.ReparseTag == IO_REPARSE_TAG_SYMLINK;\n+#else\n+    return 0;\n+#endif\n+  }\n+\n+  inline int __stat_windows(const wchar_t* path, stat_type* buffer,\n+\t\t\t    bool following_symlinks)\n+  {\n+    HANDLE handle = __open_for_stat(path, following_symlinks);\n+    if (handle == INVALID_HANDLE_VALUE)\n+      return -1;\n+    // Manually check for symlink, because _fstat does not.\n+    int is_symlink = __is_handle_symlink(handle);\n+    if (is_symlink == -1)\n+      {\n+\tCloseHandle(handle);\n+\treturn -1;\n+      }\n+    int fd = ::_open_osfhandle((intptr_t)handle, _O_RDONLY);\n+    if (fd == -1)\n+      {\n+\tCloseHandle(handle);\n+\treturn -1;\n+      }\n+    int stat_result = ::_fstat64(fd, buffer);\n+    if (is_symlink)\n+      {\n+\t// Clear the previous file type.\n+\tbuffer->st_mode &= ~S_IFMT;\n+\tbuffer->st_mode |= S_IFLNK;\n+      }\n+    ::_close(fd);\n+    return stat_result;\n+  }\n+}\n+#endif\n namespace __gnu_posix\n {\n #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS\n@@ -121,13 +199,14 @@ namespace __gnu_posix\n   using stat_type = struct ::__stat64;\n \n   inline int stat(const wchar_t* path, stat_type* buffer)\n-  { return ::_wstat64(path, buffer); }\n+  { return __detail::__stat_windows(path, buffer, true); }\n \n   inline int lstat(const wchar_t* path, stat_type* buffer)\n-  {\n-    // FIXME: symlinks not currently supported\n-    return stat(path, buffer);\n-  }\n+#ifdef SYMBOLIC_LINK_FLAG_DIRECTORY\n+  { return __detail::__stat_windows(path, buffer, false); }\n+#else\n+  { return stat(path, buffer); }\n+#endif\n \n   using ::mode_t;\n \ndiff --git a/libstdc++-v3/testsuite/27_io/filesystem/operations/canonical.cc b/libstdc++-v3/testsuite/27_io/filesystem/operations/canonical.cc\nindex a915b291c37..277fcc8b5ce 100644\n--- a/libstdc++-v3/testsuite/27_io/filesystem/operations/canonical.cc\n+++ b/libstdc++-v3/testsuite/27_io/filesystem/operations/canonical.cc\n@@ -111,14 +111,14 @@ test03()\n   fs::path foo = dir/\"foo\", bar = dir/\"bar\";\n   fs::create_directory(foo);\n   fs::create_directory(bar);\n-#ifdef NO_SYMLINKS\n+#if defined(NO_SYMLINKS) || defined(_GLIBCXX_FILESYSTEM_IS_WINDOWS)\n #if defined(__MINGW32__) || defined(__MINGW64__)\n   const fs::path baz = dir/\"foo\\\\\\\\..\\\\bar///\";\n #else\n   const fs::path baz = dir/\"foo//../bar///\";\n #endif\n #else\n-  fs::create_symlink(\"../bar\", foo/\"baz\");\n+  fs::create_directory_symlink(\"../bar\", foo/\"baz\");\n   const fs::path baz = dir/\"foo//./baz///\";\n #endif\n \ndiff --git a/libstdc++-v3/testsuite/27_io/filesystem/operations/copy.cc b/libstdc++-v3/testsuite/27_io/filesystem/operations/copy.cc\nindex 1ca4a44da59..03960fa90f2 100644\n--- a/libstdc++-v3/testsuite/27_io/filesystem/operations/copy.cc\n+++ b/libstdc++-v3/testsuite/27_io/filesystem/operations/copy.cc\n@@ -68,13 +68,14 @@ test02()\n {\n #ifndef NO_SYMLINKS\n   const std::error_code bad_ec = make_error_code(std::errc::invalid_argument);\n+  __gnu_test::scoped_file tmp_file;\n   auto from = __gnu_test::nonexistent_path();\n   std::error_code ec;\n \n   ec = bad_ec;\n-  fs::create_symlink(\".\", from, ec);\n+  fs::create_symlink(tmp_file.path, from, ec);\n   VERIFY( !ec );\n-  VERIFY( fs::exists(from) );\n+  VERIFY( fs::exists(symlink_status(from)) );\n \n   auto to = __gnu_test::nonexistent_path();\n   ec = bad_ec;\n@@ -97,7 +98,7 @@ test02()\n   ec = bad_ec;\n   fs::copy(from, to, fs::copy_options::copy_symlinks, ec);\n   VERIFY( !ec );\n-  VERIFY( fs::exists(to) );\n+  VERIFY( fs::exists(symlink_status(to)) );\n   VERIFY( is_symlink(to) );\n \n   ec.clear();\ndiff --git a/libstdc++-v3/testsuite/27_io/filesystem/operations/weakly_canonical.cc b/libstdc++-v3/testsuite/27_io/filesystem/operations/weakly_canonical.cc\nindex 6c187c73b79..dc93b4467fc 100644\n--- a/libstdc++-v3/testsuite/27_io/filesystem/operations/weakly_canonical.cc\n+++ b/libstdc++-v3/testsuite/27_io/filesystem/operations/weakly_canonical.cc\n@@ -40,19 +40,23 @@ test01()\n   fs::path p;\n \n #ifndef NO_SYMLINKS\n-  fs::create_symlink(\"../bar\", foo/\"bar\");\n+  fs::create_directory_symlink(\"../bar\", foo/\"bar\");\n \n+#ifndef _GLIBCXX_FILESYSTEM_IS_WINDOWS\n   p = fs::weakly_canonical(dir/\"foo//./bar///../biz/.\");\n   VERIFY( p == dirc/\"biz/\" );\n+#endif\n   p = fs::weakly_canonical(dir/\"foo/.//bar/././baz/.\");\n   VERIFY( p == dirc/\"bar/baz\" );\n   p = fs::weakly_canonical(fs::current_path()/dir/\"bar//../foo/bar/baz\");\n   VERIFY( p == dirc/\"bar/baz\" );\n \n+#ifndef _GLIBCXX_FILESYSTEM_IS_WINDOWS\n   ec = bad_ec;\n   p = fs::weakly_canonical(dir/\"foo//./bar///../biz/.\", ec);\n   VERIFY( !ec );\n   VERIFY( p == dirc/\"biz/\" );\n+#endif\n   ec = bad_ec;\n   p = fs::weakly_canonical(dir/\"foo/.//bar/././baz/.\", ec);\n   VERIFY( !ec );\ndiff --git a/libstdc++-v3/testsuite/util/testsuite_fs.h b/libstdc++-v3/testsuite/util/testsuite_fs.h\nindex 6940ae51aa6..aee56c3dbf7 100644\n--- a/libstdc++-v3/testsuite/util/testsuite_fs.h\n+++ b/libstdc++-v3/testsuite/util/testsuite_fs.h\n@@ -43,8 +43,10 @@ namespace test_fs = std::experimental::filesystem;\n #endif\n \n #ifndef _GLIBCXX_HAVE_SYMLINK\n+#ifndef _GLIBCXX_FILESYSTEM_IS_WINDOWS\n #define NO_SYMLINKS\n #endif\n+#endif\n \n #if !defined (_GLIBCXX_HAVE_SYS_STATVFS_H) \\\n   && !defined (_GLIBCXX_FILESYSTEM_IS_WINDOWS)\n",
    "prefixes": [
        "v4"
    ]
}