]>
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
);