Thanks for reporting that. I installed into the TZDB development repository the 
first attached patch, which has the code you suggested and with fancier 
commentary. A couple of questions, if you have the time:

* Does the 'symlink' system call have the same problem on this MS-Windows 
platform? Should zic worry about them as well? (But if so, why didn't zic 
complain to your users about symlink?)

* I guess the glitch occurred because the PostgreSQL copy of zic.c links to the 
compatibility shim in postgresql/src/port/win32link.c. If other code links to 
that shim too, perhaps this should also be fixed there? Something like the 
second attached patch, which I have not compiled or tested as I don't use 
MS-Windows.
From 75c0c97b7fc4f41cd732e058bf4a6c8fad159bd6 Mon Sep 17 00:00:00 2001
From: Paul Eggert <[email protected]>
Date: Thu, 30 Jul 2026 10:41:54 -0700
Subject: [PROPOSED] Port to MS-Windows misreport of no hard links

Problem reported by Tom Lane in:
https://lists.iana.org/hyperkitty/list/[email protected]/thread/O5Q2I3GLZIF6GEFZ3MDWRZKG4ENPJVFQ/
* NEWS: Mention this.
* zic.c (dolink) [HAVE_LINK]: Treat EINVAL like ENOTSUP here, too.
---
 NEWS  | 3 +++
 zic.c | 5 +++++
 2 files changed, 8 insertions(+)

diff --git a/NEWS b/NEWS
index 69b94344..2c4b0d50 100644
--- a/NEWS
+++ b/NEWS
@@ -48,6 +48,9 @@ Unreleased, experimental changes
     zic now rejects Link targets that would have invalid names.
     (Thanks to Darren Carreras.)
 
+    zic now ports to MS-Windows file system drivers that misreport
+    lack of support for hard links.  (Thanks to Tom Lane.)
+
 
 Release 2026c - 2026-07-08 10:23:58 -0700
 
diff --git a/zic.c b/zic.c
index 424dcf07..f6d2ecc0 100644
--- a/zic.c
+++ b/zic.c
@@ -1781,6 +1781,11 @@ dolink(char const *target, char const *linkname, bool staysymlink)
 	      break;
 	    }
 	    link_errno = errno;
+	    /* When hard links are not supported, some MS-Windows file system
+	       drivers fail with EINVAL, contrary to the intent of
+	       MS-FSA 42.0 (2025) section 2.1.5.15.7.  */
+	    if (link_errno == EINVAL)
+	      link_errno = ENOTSUP;
 	  }
 #endif
 	  if (link_errno == EXDEV || link_errno == ENOTSUP)
-- 
2.53.0

diff --git a/src/port/win32error.c b/src/port/win32error.c
index 11d854c7370..4dc61804968 100644
--- a/src/port/win32error.c
+++ b/src/port/win32error.c
@@ -102,6 +102,9 @@ static const struct
 	{
 		ERROR_INVALID_PARAMETER, EINVAL
 	},
+	{
+		ERROR_NOT_SUPPORTED, ENOTSUP
+	},
 	{
 		ERROR_NO_PROC_SLOTS, EAGAIN
 	},
diff --git a/src/port/win32link.c b/src/port/win32link.c
index e7c8623810f..9e99d04354e 100644
--- a/src/port/win32link.c
+++ b/src/port/win32link.c
@@ -23,7 +23,10 @@ link(const char *src, const char *dst)
 	 */
 	if (CreateHardLinkA(dst, src, NULL) == 0)
 	{
-		_dosmaperr(GetLastError());
+		DWORD err = GetLastError();
+		if (err == ERROR_INVALID_PARAMETER)
+			err = ERROR_NOT_SUPPORTED;
+		_dosmaperr(err);
 		return -1;
 	}
 	else

Reply via email to