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