Re: fix pg_mkdir_p to tolerate concurrent directory creation - Mailing list pgsql-hackers

From Tom Lane
Subject Re: fix pg_mkdir_p to tolerate concurrent directory creation
Date
Msg-id 1528024.1781881678@sss.pgh.pa.us
Whole thread
Responses Re: fix pg_mkdir_p to tolerate concurrent directory creation
List pgsql-hackers
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

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 = '/';
     }

pgsql-hackers by date:

Previous
From: Tom Lane
Date:
Subject: Re: fix pg_mkdir_p to tolerate concurrent directory creation
Next
From: Dmitry Dolgov
Date:
Subject: Re: File locks for data directory lockfile in the context of Linux namespaces