Age Owner Branch data TLA Line data Source code
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 : : */
56 : : int
5681 tgl@sss.pgh.pa.us 57 :CBC 564 : pg_mkdir_p(char *path, int omode)
58 : : {
59 : : mode_t numask,
60 : : oumask;
61 : : int last,
62 : : retval;
63 : : char *p;
64 : :
65 : 564 : retval = 0;
66 : 564 : 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 : 564 : oumask = umask(0);
103 : 564 : numask = oumask & ~(S_IWUSR | S_IXUSR);
104 : 564 : (void) umask(numask);
105 : :
106 [ + - ]: 564 : if (p[0] == '/') /* Skip leading '/'. */
107 : 564 : ++p;
108 [ + + ]: 83221 : for (last = 0; !last; ++p)
109 : : {
110 [ + + ]: 82657 : if (p[0] == '\0')
111 : 564 : last = 1;
112 [ + + ]: 82093 : else if (p[0] != '/')
113 : 75976 : continue;
114 : 6681 : *p = '\0';
115 [ + + - + ]: 6681 : if (!last && p[1] == '\0')
5681 tgl@sss.pgh.pa.us 116 :UBC 0 : last = 1;
117 : :
5681 tgl@sss.pgh.pa.us 118 [ + + ]:CBC 6681 : if (last)
119 : 564 : (void) umask(oumask);
120 : :
11 121 [ + + + + ]: 6681 : 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 : 5970 : int save_errno = errno;
129 : : struct stat sb;
130 : :
131 [ + - + - ]: 11940 : if (save_errno != EEXIST ||
132 : 5970 : stat(path, &sb) != 0 ||
133 [ - + ]: 5970 : !S_ISDIR(sb.st_mode))
134 : : {
135 : : /* Don't let stat replace mkdir's errno */
11 tgl@sss.pgh.pa.us 136 :UBC 0 : errno = save_errno;
5681 137 : 0 : retval = -1;
138 : 0 : 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 : :
5681 tgl@sss.pgh.pa.us 161 [ + + ]:CBC 6681 : if (!last)
162 : 6117 : *p = '/';
163 : : }
164 : :
165 : : /* ensure we restored umask */
166 : 564 : (void) umask(oumask);
167 : :
168 : 564 : return retval;
169 : : }
|