]> git.saurik.com Git - apple/libc.git/blob - stdio/FreeBSD/mktemp.c
Libc-1158.50.2.tar.gz
[apple/libc.git] / stdio / FreeBSD / mktemp.c
1 /*
2 * Copyright (c) 1987, 1993
3 * The Regents of the University of California. All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 * 3. Neither the name of the University nor the names of its contributors
14 * may be used to endorse or promote products derived from this software
15 * without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
18 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
21 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27 * SUCH DAMAGE.
28 */
29
30 #if defined(LIBC_SCCS) && !defined(lint)
31 static char sccsid[] = "@(#)mktemp.c 8.1 (Berkeley) 6/4/93";
32 #endif /* LIBC_SCCS and not lint */
33 #include <sys/cdefs.h>
34
35 #include "namespace.h"
36 #include <sys/param.h>
37 #include <sys/stat.h>
38 #include <fcntl.h>
39 #include <errno.h>
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <string.h>
43 #include <ctype.h>
44 #include <unistd.h>
45 #include "un-namespace.h"
46
47 #define ALLOWED_MKOSTEMP_FLAGS (O_APPEND | O_SHLOCK | O_EXLOCK | O_CLOEXEC)
48
49 char *_mktemp(char *);
50
51 typedef enum {
52 FTPP_DONE, FTPP_TRY_NEXT, FTPP_ERROR
53 } find_temp_path_progress_t;
54
55 /* A contract for actions that find_temp_path performs for every path from
56 * the template.
57 *
58 * If the desired path was found, set result and return FTPP_DONE.
59 * If an IO/FS error ocurred, set errno and return FTPP_ERROR.
60 * Otherwise return FTPP_TRY_NEXT.
61 */
62 typedef find_temp_path_progress_t (*find_temp_path_action_t)(
63 char *path, void *ctx, void *result);
64
65 static int find_temp_path(char *path, int slen, bool stat_base_dir,
66 find_temp_path_action_t action, void *action_ctx, void *action_result);
67
68 static find_temp_path_progress_t _mkostemps_action(
69 char *path, void *ctx, void *result);
70 static find_temp_path_progress_t _mktemp_action(
71 char *path, void *ctx, void *result);
72 static find_temp_path_progress_t _mkdtemp_action(
73 char *path, void *ctx, void *result);
74 static find_temp_path_progress_t _mkstemp_dprotected_np_action(
75 char *path, void *ctx, void *result);
76
77 static const char padchar[] =
78 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
79
80 int
81 mkostemps(char *path, int slen, int oflags)
82 {
83 int fd;
84 if (oflags & ~ALLOWED_MKOSTEMP_FLAGS) {
85 errno = EINVAL;
86 return -1;
87 }
88 return (find_temp_path(path, slen, TRUE, _mkostemps_action, &oflags, &fd) ? fd : -1);
89 }
90
91 int
92 mkstemps(char *path, int slen)
93 {
94 int fd;
95
96 return (find_temp_path(path, slen, TRUE, _mkostemps_action, NULL, &fd) ? fd : -1);
97 }
98
99 int
100 mkostemp(char *path, int oflags)
101 {
102 int fd;
103 if (oflags & ~ALLOWED_MKOSTEMP_FLAGS) {
104 errno = EINVAL;
105 return -1;
106 }
107 return (find_temp_path(path, 0, TRUE, _mkostemps_action, &oflags, &fd) ? fd : -1);
108 }
109
110 int
111 mkstemp(char *path)
112 {
113 int fd;
114
115 return (find_temp_path(path, 0, TRUE, _mkostemps_action, NULL, &fd) ? fd : -1);
116 }
117
118 char *
119 mkdtemp(char *path)
120 {
121 return (find_temp_path(path, 0, TRUE, _mkdtemp_action, NULL, NULL) ?
122 path : (char *)NULL);
123 }
124
125 char *
126 _mktemp(char *path)
127 {
128 return (find_temp_path(path, 0, FALSE, _mktemp_action, NULL, NULL) ?
129 path : (char *)NULL);
130 }
131
132 __warn_references(mktemp,
133 "warning: mktemp() possibly used unsafely; consider using mkstemp()");
134
135 char *
136 mktemp(char *path)
137 {
138 return (_mktemp(path));
139 }
140
141 int
142 mkstemp_dprotected_np(char *path, int class, int dpflags)
143 {
144 int fd;
145 int ctx[2] = { class, dpflags };
146
147 return (find_temp_path(path, 0, TRUE, _mkstemp_dprotected_np_action, &ctx, &fd) ? fd : -1);
148 }
149
150 /* For every path matching a given template, invoke an action. Depending on
151 * the progress reported by action, stops or tries the next path.
152 * Returns 1 if succeeds, 0 and sets errno if fails.
153 */
154 static int
155 find_temp_path(char *path, int slen, bool stat_base_dir,
156 find_temp_path_action_t action, void *action_ctx, void *action_result)
157 {
158 char *start, *trv, *suffp, *carryp;
159 char *pad;
160 struct stat sbuf;
161 int rval;
162 uint32_t rand;
163 char carrybuf[MAXPATHLEN];
164
165 if (slen < 0) {
166 errno = EINVAL;
167 return (0);
168 }
169
170 for (trv = path; *trv != '\0'; ++trv)
171 ;
172 if (trv - path >= MAXPATHLEN) {
173 errno = ENAMETOOLONG;
174 return (0);
175 }
176 trv -= slen;
177 suffp = trv;
178 --trv;
179 if (trv < path || NULL != strchr(suffp, '/')) {
180 errno = EINVAL;
181 return (0);
182 }
183
184 /* Fill space with random characters */
185 while (trv >= path && *trv == 'X') {
186 rand = arc4random_uniform(sizeof(padchar) - 1);
187 *trv-- = padchar[rand];
188 }
189 start = trv + 1;
190
191 /* save first combination of random characters */
192 memcpy(carrybuf, start, suffp - start);
193
194 /*
195 * check the target directory.
196 */
197 if (stat_base_dir) {
198 for (; trv > path; --trv) {
199 if (*trv == '/') {
200 *trv = '\0';
201 rval = stat(path, &sbuf);
202 *trv = '/';
203 if (rval != 0)
204 return (0);
205 if (!S_ISDIR(sbuf.st_mode)) {
206 errno = ENOTDIR;
207 return (0);
208 }
209 break;
210 }
211 }
212 }
213
214 for (;;) {
215 switch (action(path, action_ctx, action_result)) {
216 case FTPP_DONE:
217 return (1);
218 case FTPP_ERROR:
219 return (0); // errno must be set by the action
220 default:
221 ; // FTPP_TRY_NEXT, fall-through
222 }
223
224 /* If we have a collision, cycle through the space of filenames */
225 for (trv = start, carryp = carrybuf;;) {
226 /* have we tried all possible permutations? */
227 if (trv == suffp) {
228 /* yes - exit with EEXIST */
229 errno = EEXIST;
230 return (0);
231 }
232 pad = strchr(padchar, *trv);
233 if (pad == NULL) {
234 /* this should never happen */
235 errno = EIO;
236 return (0);
237 }
238 /* increment character */
239 *trv = (*++pad == '\0') ? padchar[0] : *pad;
240 /* carry to next position? */
241 if (*trv == *carryp) {
242 /* increment position and loop */
243 ++trv;
244 ++carryp;
245 } else {
246 /* try with new name */
247 break;
248 }
249 }
250 }
251 /*NOTREACHED*/
252 }
253
254 static find_temp_path_progress_t
255 _mkostemps_action(char *path, void *ctx, void *result)
256 {
257 int oflags = (ctx != NULL) ? *((int *) ctx) : 0;
258 int fd = _open(path, O_CREAT|O_EXCL|O_RDWR|oflags, 0600);
259 if (fd >= 0) {
260 *((int *) result) = fd;
261 return FTPP_DONE;
262 }
263 return (errno == EEXIST) ?
264 FTPP_TRY_NEXT :
265 FTPP_ERROR; // errno is set already
266 }
267
268 static find_temp_path_progress_t
269 _mktemp_action(char *path, void *ctx __unused, void *result __unused)
270 {
271 struct stat sbuf;
272 if (lstat(path, &sbuf)) {
273 // stat failed
274 return (errno == ENOENT) ?
275 FTPP_DONE : // path is vacant, done
276 FTPP_ERROR; // errno is set already
277 }
278 return FTPP_TRY_NEXT;
279 }
280
281 static find_temp_path_progress_t
282 _mkdtemp_action(char *path, void *ctx __unused, void *result __unused)
283 {
284 if (mkdir(path, 0700) == 0)
285 return FTPP_DONE;
286 return (errno == EEXIST) ?
287 FTPP_TRY_NEXT :
288 FTPP_ERROR; // errno is set already
289 }
290
291 static find_temp_path_progress_t
292 _mkstemp_dprotected_np_action(char *path, void *ctx, void *result)
293 {
294 int class = ((int *) ctx)[0];
295 int dpflags = ((int *) ctx)[1];
296 int fd = open_dprotected_np(path, O_CREAT|O_EXCL|O_RDWR, class, dpflags, 0600);
297 if (fd >= 0) {
298 *((int *) result) = fd;
299 return FTPP_DONE;
300 }
301 return (errno == EEXIST) ?
302 FTPP_TRY_NEXT :
303 FTPP_ERROR; // errno is set already
304 }
305
306