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: }