pg.ddx.io  pgsql-hackers@postgresql.org mailing list archive  
help / color / mirror / Atom feed
From: Tom Lane <tgl@sss.pgh.pa.us>
To: Álvaro Herrera <alvherre@kurilemu.de>
Cc: Andrew Dunstan <andrew@dunslane.net>
Cc: PostgreSQL Hackers <pgsql-hackers@lists.postgresql.org>
Subject: Re: fix pg_mkdir_p to tolerate concurrent directory creation
Date: Fri, 19 Jun 2026 11:07:58 -0400
Message-ID: <1528024.1781881678@sss.pgh.pa.us> (raw)
In-Reply-To: <ajVLauHFakqDsXsY@alvherre.pgsql>
References: <ajVLauHFakqDsXsY@alvherre.pgsql>

Actually ... given this change, why don't we drop the initial stat()
call?  Just try mkdir(), and move on if it succeeds.  If not, but
there's already a directory there, we're good.  That eliminates the
race condition without duplicating code.

			regards, tom lane

Attachments:

v3-0001-Make-pg_mkdir_p-tolerant-of-a-concurrent-director.patch (text/x-diff, 1.7K) download diff --git a/src/port/pgmkdirp.c b/src/port/pgmkdirp.c index 7d7cea4dd0e..3e6b06fce79 100644 --- a/src/port/pgmkdirp.c +++ b/src/port/pgmkdirp.c @@ -56,7 +56,6 @@ int pg_mkdir_p(char *path, int omode) { - struct stat sb; mode_t numask, oumask; int last, @@ -119,24 +118,46 @@ pg_mkdir_p(char *path, int omode) if (last) (void) umask(oumask); - /* check for pre-existing directory */ - if (stat(path, &sb) == 0) + if (mkdir(path, last ? omode : S_IRWXU | S_IRWXG | S_IRWXO) < 0) { - if (!S_ISDIR(sb.st_mode)) + /* + * If we got EEXIST because there's already a directory there, + * don't complain. + */ +#ifndef WIN32 + int save_errno = errno; + struct stat sb; + + if (save_errno != EEXIST || + stat(path, &sb) != 0 || + !S_ISDIR(sb.st_mode)) { - if (last) - errno = EEXIST; - else - errno = ENOTDIR; + /* Don't let stat replace mkdir's errno */ + errno = save_errno; retval = -1; break; } +#else /* 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. We assume + * GetFileAttributes() won't change errno. + */ + DWORD attr = GetFileAttributes(path); + + if (errno != EEXIST || + attr == INVALID_FILE_ATTRIBUTES || + !(attr & FILE_ATTRIBUTE_DIRECTORY)) + { + retval = -1; + break; + } +#endif /* WIN32 */ } - else if (mkdir(path, last ? omode : S_IRWXU | S_IRWXG | S_IRWXO) < 0) - { - retval = -1; - break; - } + if (!last) *p = '/'; }

view thread (11+ messages)  latest in thread

Message-ID: <1528024.1781881678@sss.pgh.pa.us>
Permalink:  ../1528024.1781881678@sss.pgh.pa.us/
Also on:    postgresql.org/message-id/1528024.1781881678@sss.pgh.pa.us

 ·  · 

reply

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Reply to all the recipients using the --to and --cc options:
  reply via email

  To: pgsql-hackers@postgresql.org
  Cc: tgl@sss.pgh.pa.us, alvherre@kurilemu.de, andrew@dunslane.net, pgsql-hackers@lists.postgresql.org
  Subject: Re: fix pg_mkdir_p to tolerate concurrent directory creation
  In-Reply-To: <1528024.1781881678@sss.pgh.pa.us>

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

This inbox is served by DDX for PostgreSQL; see mirroring instructions
for how to clone and mirror all data and code used for this inbox