From 12a033e0b2485740d74985bed0882f87a5ad05ad Mon Sep 17 00:00:00 2001 From: Ning Yu 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 Co-authored-by: Ning Yu --- 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