From 12a033e0b2485740d74985bed0882f87a5ad05ad Mon Sep 17 00:00:00 2001
From: Ning Yu <nyu@pivotal.io>
Date: Tue, 23 Jul 2019 11:21:19 +0800
Subject: [PATCH v1 1/4] Fix race condition in pg_mkdir_p()

pg_mkdir_p() is used by initdb and pg_basebackup to create a directory
as well as its parent directories.  The logic used to be like below:

    if (stat(path) < 0)
    {
        if (mkdir(path) < 0)
            retval = -1;
    }

It first checks for the existence of the path, if path does not
pre-exist then it creates the directory.  However if two processes try
to create path concurrently, then one possible execution order is as
below:

    A: stat(path) returns -1, so decide to create it;
    B: stat(path) returns -1, so decide to create it;
    B: mkdir(path) returns 0, successfully created path;
    A: mkdir(path) returns -1, fail to create path as it already exist;

This race condition could be fixed by swapping the order of stat() and
mkdir(), this is also what the "mkdir -p" command does.

Co-authored-by: Paul Guo <pguo@pivotal.io>
Co-authored-by: Ning Yu <nyu@pivotal.io>
---
 src/port/pgmkdirp.c | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/src/port/pgmkdirp.c b/src/port/pgmkdirp.c
index d943559760..e0cc06260b 100644
--- a/src/port/pgmkdirp.c
+++ b/src/port/pgmkdirp.c
@@ -119,11 +119,17 @@ 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))
+			/* path does not pre-exist and is successfully created */
+		}
+		else if (errno == EEXIST)
+		{
+			/* path exists, double check it is a dir */
+
+			if (stat(path, &sb) < 0 || !S_ISDIR(sb.st_mode))
 			{
+				/* path is not a dir at all */
 				if (last)
 					errno = EEXIST;
 				else
@@ -131,8 +137,10 @@ pg_mkdir_p(char *path, int omode)
 				retval = -1;
 				break;
 			}
+
+			/* now we know that path does is a dir */
 		}
-		else if (mkdir(path, last ? omode : S_IRWXU | S_IRWXG | S_IRWXO) < 0)
+		else
 		{
 			retval = -1;
 			break;
-- 
2.20.1

