/
githubmirror
/
postgres
Обзор
Документация
Войти
/
githubmirror
/
postgres
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
src/port/pgmkdirp.c
169 строк
5 KB
Tom Lane
Make pg_mkdir_p() tolerant of a concurrent directory creation.
19 июн 2026, 19:52
19 июн 2026, 19:52
f25a07b
Код
Авторство
О чём код?
/* * This is adapted from FreeBSD's src/bin/mkdir/mkdir.c, which bears * the following copyright notice: * * Copyright (c) 1983, 1992, 1993 * The Regents of the University of California. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #include "c.h" #include <sys/stat.h> /* * pg_mkdir_p --- create a directory and, if necessary, parent directories * * This is equivalent to "mkdir -p" except we don't complain if the target * directory already exists. * * We assume the path is in canonical form, i.e., uses / as the separator. * * omode is the file permissions bits for the target directory. Note that any * parent directories that have to be created get permissions according to the * prevailing umask, but with u+wx forced on to ensure we can create there. * (We declare omode as int, not mode_t, to minimize dependencies for port.h.) * * Returns 0 on success, -1 (with errno set) on failure. * * Note that on failure, the path arg has been modified to show the particular * directory level we had problems with. */ int pg_mkdir_p(char *path, int omode) { mode_t numask, oumask; int last, retval; char *p; retval = 0; p = path; #ifdef WIN32 /* skip network and drive specifiers for win32 */ if (strlen(p) >= 2) { if (p[0] == '/' && p[1] == '/') { /* network drive */ p = strchr(p + 2, '/'); if (p == NULL) { errno = EINVAL; return -1; } } else if (p[1] == ':' && ((p[0] >= 'a' && p[0] <= 'z') || (p[0] >= 'A' && p[0] <= 'Z'))) { /* local drive */ p += 2; } } #endif /* * POSIX 1003.2: For each dir operand that does not name an existing * directory, effects equivalent to those caused by the following command * shall occur: * * mkdir -p -m $(umask -S),u+wx $(dirname dir) && mkdir [-m mode] dir * * We change the user's umask and then restore it, instead of doing * chmod's. Note we assume umask() can't change errno. */ oumask = umask(0); numask = oumask & ~(S_IWUSR | S_IXUSR); (void) umask(numask); if (p[0] == '/') /* Skip leading '/'. */ ++p; for (last = 0; !last; ++p) { if (p[0] == '\0') last = 1; else if (p[0] != '/') continue; *p = '\0'; if (!last && p[1] == '\0') last = 1; if (last) (void) umask(oumask); if (mkdir(path, last ? omode : S_IRWXU | S_IRWXG | S_IRWXO) < 0) { /* * 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)) { /* 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 */ } if (!last) *p = '/'; } /* ensure we restored umask */ (void) umask(oumask); return retval; }