]>
git.saurik.com Git - apple/libc.git/blob - util/mkpath_np.c
2 * Copyright (c) 2011 Apple Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
32 _mkpath(int dfd
, const char *path
, mode_t omode
, const char ** firstdir
)
34 char *apath
= NULL
, *opath
= NULL
, *s
, *sn
, *sl
;
35 unsigned int depth
= 0;
36 mode_t chmod_mode
= 0;
38 int old_errno
= errno
;
41 /* Try the trivial case first. */
42 if (0 == mkdirat(dfd
, path
, omode
)) {
44 *firstdir
= strdup(path
);
49 /* Anything other than an ENOENT, EEXIST, or EISDIR indicates an
50 * error that we need to send back to the caller. ENOENT indicates
51 * that we need to try a lower level.
57 if (fstatat(dfd
, path
, &sbuf
, 0) == 0) {
58 if (S_ISDIR(sbuf
.st_mode
)) {
67 case EISDIR
: /* <rdar://problem/10288022> */
81 sl
= s
= apath
+ strlen(apath
) - 1;
84 /* Strip off trailing /., see <rdar://problem/14351794> */
85 if (s
- 1 > apath
&& *s
== '.' && *(s
- 1) == '/')
87 /* Strip off trailing /, see <rdar://problem/11592386> */
88 if (s
> apath
&& *s
== '/')
93 path
= opath
= strdup(apath
);
100 /* Retry the trivial case after having stripped of trailing /. <rdar://problem/14351794> */
101 if (0 == mkdirat(dfd
, path
, omode
)) {
103 *firstdir
= strdup(path
);
109 /* Increase our depth and try making that directory */
110 s
= strrchr(apath
, '/');
112 /* We should never hit this under normal circumstances,
113 * but it can occur due to really unfortunate timing
121 if (0 == mkdirat(dfd
, apath
, S_IRWXU
| S_IRWXG
| S_IRWXO
)) {
122 /* Found our starting point */
125 * For each dir operand that does not name an existing
126 * directory, effects equivalent to those cased by the
127 * following command shall occcur:
129 * mkdir -p -m $(umask -S),u+wx $(dirname dir) &&
130 * mkdir [-m mode] dir
134 if (-1 == fstatat(dfd
, apath
, &dirstat
, 0)) {
135 /* Really unfortunate timing ... */
140 if ((dirstat
.st_mode
& (S_IWUSR
| S_IXUSR
)) != (S_IWUSR
| S_IXUSR
)) {
141 chmod_mode
= dirstat
.st_mode
| S_IWUSR
| S_IXUSR
;
142 if (-1 == fchmodat(dfd
, apath
, chmod_mode
, 0)) {
143 /* Really unfortunate timing ... */
150 *firstdir
= strdup(apath
);
153 } else if (errno
== EEXIST
) {
154 /* Some other process won the race in creating this directory
155 * before we did. We will use this as our starting point.
156 * See: <rdar://problem/10279893>
158 if (fstatat(dfd
, apath
, &sbuf
, 0) == 0 &&
159 S_ISDIR(sbuf
.st_mode
)) {
162 *firstdir
= strdup(apath
);
169 } else if (errno
!= ENOENT
) {
176 /* Decrease our depth and make that directory */
177 s
= strrchr(apath
, '\0');
181 if (-1 == mkdirat(dfd
, apath
, S_IRWXU
| S_IRWXG
| S_IRWXO
)) {
182 /* This handles "." and ".." added to the new section of path */
190 if (-1 == fchmodat(dfd
, apath
, chmod_mode
, 0)) {
191 /* Really unfortunate timing ... */
198 if (-1 == mkdirat(dfd
, path
, omode
)) {
200 if (errno
== EEXIST
&&
201 fstatat(dfd
, path
, &sbuf
, 0) == 0 &&
202 !S_ISDIR(sbuf
.st_mode
)) {
215 /* This extended version of mkpath_np is provided to help NSFileManager
216 * maintain binary compatibility. If firstdir is not NULL, *firstdir will be
217 * set to the path of the first created directory, and it is the caller's
218 * responsibility to free the returned string. This SPI is subject to removal
219 * once NSFileManager no longer has a need for it, and use in new code is
220 * highly discouraged.
222 * See: <rdar://problem/9888987>
226 _mkpath_np(const char *path
, mode_t omode
, const char ** firstdir
) {
227 return _mkpath(AT_FDCWD
, path
, omode
, firstdir
);
230 int mkpath_np(const char *path
, mode_t omode
) {
231 return _mkpath(AT_FDCWD
, path
, omode
, NULL
);
234 int mkpathat_np(int dfd
, const char *path
, mode_t omode
) {
235 return _mkpath(dfd
, path
, omode
, NULL
);