PostgreSQL Source Code git master
Loading...
Searching...
No Matches
pgmkdirp.c
Go to the documentation of this file.
1/*
2 * This is adapted from FreeBSD's src/bin/mkdir/mkdir.c, which bears
3 * the following copyright notice:
4 *
5 * Copyright (c) 1983, 1992, 1993
6 * The Regents of the University of California. All rights reserved.
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 * 4. Neither the name of the University nor the names of its contributors
17 * may be used to endorse or promote products derived from this software
18 * without specific prior written permission.
19 *
20 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30 * SUCH DAMAGE.
31 */
32
33#include "c.h"
34
35#include <sys/stat.h>
36
37
38/*
39 * pg_mkdir_p --- create a directory and, if necessary, parent directories
40 *
41 * This is equivalent to "mkdir -p" except we don't complain if the target
42 * directory already exists.
43 *
44 * We assume the path is in canonical form, i.e., uses / as the separator.
45 *
46 * omode is the file permissions bits for the target directory. Note that any
47 * parent directories that have to be created get permissions according to the
48 * prevailing umask, but with u+wx forced on to ensure we can create there.
49 * (We declare omode as int, not mode_t, to minimize dependencies for port.h.)
50 *
51 * Returns 0 on success, -1 (with errno set) on failure.
52 *
53 * Note that on failure, the path arg has been modified to show the particular
54 * directory level we had problems with.
55 */
56int
57pg_mkdir_p(char *path, int omode)
58{
60 oumask;
61 int last,
62 retval;
63 char *p;
64
65 retval = 0;
66 p = path;
67
68#ifdef WIN32
69 /* skip network and drive specifiers for win32 */
70 if (strlen(p) >= 2)
71 {
72 if (p[0] == '/' && p[1] == '/')
73 {
74 /* network drive */
75 p = strchr(p + 2, '/');
76 if (p == NULL)
77 {
78 errno = EINVAL;
79 return -1;
80 }
81 }
82 else if (p[1] == ':' &&
83 ((p[0] >= 'a' && p[0] <= 'z') ||
84 (p[0] >= 'A' && p[0] <= 'Z')))
85 {
86 /* local drive */
87 p += 2;
88 }
89 }
90#endif
91
92 /*
93 * POSIX 1003.2: For each dir operand that does not name an existing
94 * directory, effects equivalent to those caused by the following command
95 * shall occur:
96 *
97 * mkdir -p -m $(umask -S),u+wx $(dirname dir) && mkdir [-m mode] dir
98 *
99 * We change the user's umask and then restore it, instead of doing
100 * chmod's. Note we assume umask() can't change errno.
101 */
102 oumask = umask(0);
103 numask = oumask & ~(S_IWUSR | S_IXUSR);
104 (void) umask(numask);
105
106 if (p[0] == '/') /* Skip leading '/'. */
107 ++p;
108 for (last = 0; !last; ++p)
109 {
110 if (p[0] == '\0')
111 last = 1;
112 else if (p[0] != '/')
113 continue;
114 *p = '\0';
115 if (!last && p[1] == '\0')
116 last = 1;
117
118 if (last)
119 (void) umask(oumask);
120
121 if (mkdir(path, last ? omode : S_IRWXU | S_IRWXG | S_IRWXO) < 0)
122 {
123 /*
124 * If we got EEXIST because there's already a directory there,
125 * don't complain.
126 */
127#ifndef WIN32
128 int save_errno = errno;
129 struct stat sb;
130
131 if (save_errno != EEXIST ||
132 stat(path, &sb) != 0 ||
133 !S_ISDIR(sb.st_mode))
134 {
135 /* Don't let stat replace mkdir's errno */
137 retval = -1;
138 break;
139 }
140#else /* WIN32 */
141 /*
142 * On Windows, stat() opens a handle and can transiently fail on a
143 * directory another process is concurrently creating. Probe with
144 * a path-based attribute query instead: it requests only
145 * FILE_READ_ATTRIBUTES and is exempt from share-mode denial, so
146 * it reliably sees a concurrently-created directory. We assume
147 * GetFileAttributes() won't change errno.
148 */
149 DWORD attr = GetFileAttributes(path);
150
151 if (errno != EEXIST ||
152 attr == INVALID_FILE_ATTRIBUTES ||
153 !(attr & FILE_ATTRIBUTE_DIRECTORY))
154 {
155 retval = -1;
156 break;
157 }
158#endif /* WIN32 */
159 }
160
161 if (!last)
162 *p = '/';
163 }
164
165 /* ensure we restored umask */
166 (void) umask(oumask);
167
168 return retval;
169}
int pg_mkdir_p(char *path, int omode)
Definition pgmkdirp.c:57
static int fb(int x)
#define EINVAL
Definition private.h:69
#define stat
Definition win32_port.h:74
#define S_IRWXG
Definition win32_port.h:300
#define S_IRWXO
Definition win32_port.h:312
#define S_ISDIR(m)
Definition win32_port.h:315
#define mkdir(a, b)
Definition win32_port.h:80
#define S_IWUSR
Definition win32_port.h:282
#define S_IXUSR
Definition win32_port.h:285
#define S_IRWXU
Definition win32_port.h:288