From ab26c8e02b20edc13ce9216c92a2141dc7ca9f90 Mon Sep 17 00:00:00 2001
From: Andrew Dunstan <andrew@dunslane.net>
Date: Thu, 18 Jun 2026 11:34:33 -0400
Subject: [PATCH v2] Make pg_mkdir_p tolerant of a concurrent directory
 creation

pg_mkdir_p creates each missing path component with a stat() followed
by mkdir().  If the stat() reports the component as absent but another
process creates it in the window before this process's mkdir(), mkdir()
fails with EEXIST and pg_mkdir_p treated that as a hard error -- unlike
"mkdir -p", which is meant to be idempotent and race-tolerant.

This shows up when several processes concurrently create paths that
share an ancestor directory: for example, parallel initdb runs whose
data directories live under a common temporary directory.  One process
wins the race to create the shared ancestor and the others fail with

    could not create directory "...": File exists

On Windows, stat() opens a file handle and participates in share-mode
locking, which means it can transiently fail on a directory another
process is concurrently creating.  Use GetFileAttributes() instead: it
requests only FILE_READ_ATTRIBUTES and is exempt from share-mode
denial, so it reliably sees a concurrently-created directory.  Use the
same probe for the post-mkdir() fallback check.

On other platforms, after a failing mkdir() with EEXIST, the error is
genuine only if the path is still not a directory.  Save errno before
the stat() probe so that mkdir()'s error is not clobbered if we have
to report failure.
---
 src/port/pgmkdirp.c | 55 +++++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 53 insertions(+), 2 deletions(-)

diff --git a/src/port/pgmkdirp.c b/src/port/pgmkdirp.c
index 7d7cea4dd0e..f4c481515d1 100644
--- a/src/port/pgmkdirp.c
+++ b/src/port/pgmkdirp.c
@@ -56,7 +56,9 @@
 int
 pg_mkdir_p(char *path, int omode)
 {
+#ifndef WIN32
 	struct stat sb;
+#endif
 	mode_t		numask,
 				oumask;
 	int			last,
@@ -119,6 +121,42 @@ pg_mkdir_p(char *path, int omode)
 		if (last)
 			(void) umask(oumask);
 
+#ifdef WIN32
+		{
+			/*
+			 * On Windows, stat() opens a handle and can transiently fail on a
+			 * directory another process is concurrently creating.  Probe with
+			 * a path-based attribute query instead: it requests only
+			 * FILE_READ_ATTRIBUTES and is exempt from share-mode denial, so it
+			 * reliably sees a concurrently-created directory.
+			 */
+			DWORD		attr = GetFileAttributes(path);
+
+			if (attr != INVALID_FILE_ATTRIBUTES)
+			{
+				/* pre-existing entry */
+				if (!(attr & FILE_ATTRIBUTE_DIRECTORY))
+				{
+					errno = last ? EEXIST : ENOTDIR;
+					retval = -1;
+					break;
+				}
+				/* already a directory: nothing to do, fall through */
+			}
+			else if (mkdir(path, last ? omode : S_IRWXU | S_IRWXG | S_IRWXO) < 0)
+			{
+				/* tolerate a concurrent creation; re-probe the same way */
+				attr = GetFileAttributes(path);
+				if (errno != EEXIST ||
+					attr == INVALID_FILE_ATTRIBUTES ||
+					!(attr & FILE_ATTRIBUTE_DIRECTORY))
+				{
+					retval = -1;
+					break;
+				}
+			}
+		}
+#else
 		/* check for pre-existing directory */
 		if (stat(path, &sb) == 0)
 		{
@@ -134,9 +172,22 @@ pg_mkdir_p(char *path, int omode)
 		}
 		else if (mkdir(path, last ? omode : S_IRWXU | S_IRWXG | S_IRWXO) < 0)
 		{
-			retval = -1;
-			break;
+			/*
+			 * Tolerate a concurrent creation: another process may have
+			 * created the directory in the window between the stat() above
+			 * and this mkdir().  Save errno before the stat() probe so that
+			 * mkdir()'s error is not clobbered if we have to report failure.
+			 */
+			int			save_errno = errno;
+
+			if (save_errno != EEXIST || stat(path, &sb) != 0 || !S_ISDIR(sb.st_mode))
+			{
+				errno = save_errno;
+				retval = -1;
+				break;
+			}
 		}
+#endif
 		if (!last)
 			*p = '/';
 	}
-- 
2.43.0

