From bb24bd82d4414cd54e37b7fdcd9cff8ed599d71f Mon Sep 17 00:00:00 2001 From: Greg Burd Date: Sun, 20 Sep 2026 12:21:51 -0400 Subject: [PATCH] Map ERROR_UNTRUSTED_MOUNT_POINT to ENOENT on Windows Recent Windows versions can fail path traversal with ERROR_UNTRUSTED_MOUNT_POINT (448, "The path cannot be traversed because it contains an untrusted mount point") when a reparse point is considered untrusted. Since PostgreSQL emulates symbolic links with junction points, this is reported for paths under pg_tblspc, and because the code was not in win32error.c's translation table it fell through to the catch-all EINVAL. That produced confusing failures such as ERROR: could not stat directory "pg_tblspc/16388/PG_19_202609165/5": Invalid argument ERROR: could not create directory "pg_tblspc/...": Invalid argument and made 13 TAP tests fail on a Windows 11 (build 26200) aarch64 animal, including recovery/014_unlogged_reinit, recovery/018_wal_optimize, test_misc/002_tablespace, initdb/001_initdb, pg_basebackup/010_pg_basebackup, pg_verifybackup/003_corruption and 008_untar, pg_combinebackup/002_compare_backups, pg_checksums/002_actions, pg_waldump/001_basic, pgbench/001_pgbench_with_server, scripts/090_reindexdb and worker_spi/002_worker_terminate. Map it to ENOENT, consistent with the existing treatment of the other "this path cannot be resolved" conditions ERROR_INVALID_NAME and ERROR_CANT_RESOLVE_FILENAME (the latter added by 387803d81d6 for broken junction points). Provide a fallback definition of the constant so that we continue to build against older SDK and MinGW headers. --- src/port/win32error.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/port/win32error.c b/src/port/win32error.c index 11d854c7370..6a4ab80a8d4 100644 --- a/src/port/win32error.c +++ b/src/port/win32error.c @@ -17,6 +17,14 @@ #include "postgres_fe.h" #endif +/* + * ERROR_UNTRUSTED_MOUNT_POINT was added in newer Windows SDKs; define it here + * so that we still build against older SDK or MinGW headers that lack it. + */ +#ifndef ERROR_UNTRUSTED_MOUNT_POINT +#define ERROR_UNTRUSTED_MOUNT_POINT 448L +#endif + static const struct { DWORD winerr; @@ -170,6 +178,21 @@ static const struct }, { ERROR_CANT_RESOLVE_FILENAME, ENOENT + }, + { + /* + * ERROR_UNTRUSTED_MOUNT_POINT ("The path cannot be traversed because + * it contains an untrusted mount point") is reported by recent + * Windows versions when a reparse point is considered untrusted, for + * example a junction created by a non-administrative user that points + * outside of that user's scope. PostgreSQL uses junction points to + * emulate symbolic links for tablespaces, so this can be reported for + * paths under pg_tblspc. Map it like the other "this path cannot be + * resolved" errors above, so that callers using + * errcode_for_file_access() report a sensible condition instead of the + * EINVAL that an unmapped code falls back to. + */ + ERROR_UNTRUSTED_MOUNT_POINT, ENOENT } }; -- 2.54.0