Actual source code: apple_fdir.h


  2: /*
  3:    Obtained from https://opensource.apple.com/source/Libc/Libc-1353.41.1/stdio/FreeBSD/mktemp.c.auto.html

  5:    The only line changed is mkdirat() to mkdir() because mkdirat() fails under valgrind
  6: */
  7: #include <sys/cdefs.h>

  9: #include <assert.h>
 10: #include <sys/param.h>
 11: #include <sys/stat.h>
 12: #include <fcntl.h>
 13: #include <errno.h>
 14: #include <stdio.h>
 15: #include <stdlib.h>
 16: #include <string.h>
 17: #include <ctype.h>
 18: #include <unistd.h>

 20: #define ALLOWED_MKOSTEMP_FLAGS (O_APPEND | O_SHLOCK | O_EXLOCK | O_CLOEXEC)

 22: char *_mktemp(char *);

 24: typedef enum {
 25:   FTPP_DONE,
 26:   FTPP_TRY_NEXT,
 27:   FTPP_ERROR
 28: } find_temp_path_progress_t;

 30: /* A contract for actions that find_temp_path performs for every path from
 31:  * the template.
 32:  *
 33:  * If the desired path was found, set result and return FTPP_DONE.
 34:  * If an IO/FS error occurred, set errno and return FTPP_ERROR.
 35:  * Otherwise return FTPP_TRY_NEXT.
 36:  */
 37: typedef find_temp_path_progress_t (*find_temp_path_action_t)(int dfd, char *path, void *ctx, void *result);

 39: static int find_temp_path(int dfd, char *path, int slen, int stat_base_dir, find_temp_path_action_t action, void *action_ctx, void *action_result);

 41: static find_temp_path_progress_t _mkdtemp_action(int dfd, char *path, void *ctx __unused, void *result __unused)
 42: {
 43:   if (mkdir(path, 0700) == 0) return FTPP_DONE;
 44:   return (errno == EEXIST) ? FTPP_TRY_NEXT : FTPP_ERROR; // errno is set already
 45: }

 47: static const char padchar[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";

 49: static int find_temp_path(int dfd, char *path, int slen, int stat_base_dir, find_temp_path_action_t action, void *action_ctx, void *action_result)
 50: {
 51:   char       *start, *trv, *suffp, *carryp;
 52:   const char *pad;
 53:   struct stat sbuf;
 54:   int         rval;
 55:   uint32_t    rand;
 56:   char        carrybuf[MAXPATHLEN];

 58:   if (slen < 0) {
 59:     errno = EINVAL;
 60:     return (0);
 61:   }

 63:   for (trv = path; *trv != '\0'; ++trv)
 64:     ;
 65:   if (trv - path >= MAXPATHLEN) {
 66:     errno = ENAMETOOLONG;
 67:     return (0);
 68:   }
 69:   trv -= slen;
 70:   suffp = trv;
 71:   --trv;
 72:   if (trv < path || NULL != strchr(suffp, '/')) {
 73:     errno = EINVAL;
 74:     return (0);
 75:   }

 77:   /* Fill space with random characters */
 78:   while (trv >= path && *trv == 'X') {
 79:     rand   = arc4random_uniform(sizeof(padchar) - 1);
 80:     *trv-- = padchar[rand];
 81:   }
 82:   start = trv + 1;

 84:   /* save first combination of random characters */
 85:   memcpy(carrybuf, start, suffp - start);

 87:   /*
 88:          * check the target directory.
 89:          */
 90:   if (stat_base_dir) {
 91:     for (; trv > path; --trv) {
 92:       if (*trv == '/') {
 93:         *trv = '\0';
 94:         rval = fstatat(dfd, path, &sbuf, 0);
 95:         *trv = '/';
 96:         if (rval != 0) return (0);
 97:         if (!S_ISDIR(sbuf.st_mode)) {
 98:           errno = ENOTDIR;
 99:           return (0);
100:         }
101:         break;
102:       }
103:     }
104:   }

106:   for (;;) {
107:     switch (action(dfd, path, action_ctx, action_result)) {
108:     case FTPP_DONE:
109:       return (1);
110:     case FTPP_ERROR:
111:       return (0); // errno must be set by the action
112:     default:;     // FTPP_TRY_NEXT, fall-through
113:     }

115:     /* If we have a collision, cycle through the space of filenames */
116:     for (trv = start, carryp = carrybuf;;) {
117:       /* have we tried all possible permutations? */
118:       if (trv == suffp) {
119:         /* yes - exit with EEXIST */
120:         errno = EEXIST;
121:         return (0);
122:       }
123:       pad = strchr(padchar, *trv);
124:       if (pad == NULL) {
125:         /* this should never happen */
126:         errno = EIO;
127:         return (0);
128:       }
129:       /* increment character */
130:       *trv = (*++pad == '\0') ? padchar[0] : *pad;
131:       /* carry to next position? */
132:       if (*trv == *carryp) {
133:         /* increment position and loop */
134:         ++trv;
135:         ++carryp;
136:       } else {
137:         /* try with new name */
138:         break;
139:       }
140:     }
141:   }
142:   /*NOTREACHED*/
143: }

145: char *mkdtemp(char *path)
146: {
147:   return (find_temp_path(AT_FDCWD, path, 0, 1, _mkdtemp_action, NULL, NULL) ? path : (char *)NULL);
148: }