]> git.saurik.com Git - apple/libc.git/blame_incremental - stdio/FreeBSD/mktemp.c
Libc-1353.11.2.tar.gz
[apple/libc.git] / stdio / FreeBSD / mktemp.c
... / ...
CommitLineData
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)
31static 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 <assert.h>
37#include <sys/param.h>
38#include <sys/stat.h>
39#include <fcntl.h>
40#include <errno.h>
41#include <stdio.h>
42#include <stdlib.h>
43#include <string.h>
44#include <ctype.h>
45#include <unistd.h>
46#include "un-namespace.h"
47
48#define ALLOWED_MKOSTEMP_FLAGS (O_APPEND | O_SHLOCK | O_EXLOCK | O_CLOEXEC)
49
50char *_mktemp(char *);
51
52typedef enum {
53 FTPP_DONE, FTPP_TRY_NEXT, FTPP_ERROR
54} find_temp_path_progress_t;
55
56/* A contract for actions that find_temp_path performs for every path from
57 * the template.
58 *
59 * If the desired path was found, set result and return FTPP_DONE.
60 * If an IO/FS error ocurred, set errno and return FTPP_ERROR.
61 * Otherwise return FTPP_TRY_NEXT.
62 */
63typedef find_temp_path_progress_t (*find_temp_path_action_t)(
64 int dfd, char *path, void *ctx, void *result);
65
66static int find_temp_path(int dfd, char *path, int slen, bool stat_base_dir,
67 find_temp_path_action_t action, void *action_ctx, void *action_result);
68
69static find_temp_path_progress_t _mkostemps_action(
70 int dfd, char *path, void *ctx, void *result);
71static find_temp_path_progress_t _mktemp_action(
72 int dfd, char *path, void *ctx, void *result);
73static find_temp_path_progress_t _mkdtemp_action(
74 int dfd, char *path, void *ctx, void *result);
75static find_temp_path_progress_t _mkstemp_dprotected_np_action(
76 int dfd, char *path, void *ctx, void *result);
77
78static const char padchar[] =
79"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
80
81int
82mkostemps(char *path, int slen, int oflags)
83{
84 int fd;
85 if (oflags & ~ALLOWED_MKOSTEMP_FLAGS) {
86 errno = EINVAL;
87 return -1;
88 }
89 return (find_temp_path(AT_FDCWD, path, slen, TRUE, _mkostemps_action, &oflags, &fd) ? fd : -1);
90}
91
92int
93mkostempsat_np(int dfd, char *path, int slen, int oflags)
94{
95 int fd;
96 if (oflags & ~ALLOWED_MKOSTEMP_FLAGS) {
97 errno = EINVAL;
98 return -1;
99 }
100 return (find_temp_path(dfd, path, slen, TRUE, _mkostemps_action, &oflags, &fd) ? fd : -1);
101}
102
103int
104mkstemps(char *path, int slen)
105{
106 int fd;
107
108 return (find_temp_path(AT_FDCWD, path, slen, TRUE, _mkostemps_action, NULL, &fd) ? fd : -1);
109}
110
111int
112mkstempsat_np(int dfd, char *path, int slen)
113{
114 int fd;
115
116 return (find_temp_path(dfd, path, slen, TRUE, _mkostemps_action, NULL, &fd) ? fd : -1);
117}
118
119int
120mkostemp(char *path, int oflags)
121{
122 int fd;
123 if (oflags & ~ALLOWED_MKOSTEMP_FLAGS) {
124 errno = EINVAL;
125 return -1;
126 }
127 return (find_temp_path(AT_FDCWD, path, 0, TRUE, _mkostemps_action, &oflags, &fd) ? fd : -1);
128}
129
130int
131mkstemp(char *path)
132{
133 int fd;
134
135 return (find_temp_path(AT_FDCWD, path, 0, TRUE, _mkostemps_action, NULL, &fd) ? fd : -1);
136}
137
138char *
139mkdtemp(char *path)
140{
141 return (find_temp_path(AT_FDCWD, path, 0, TRUE, _mkdtemp_action, NULL, NULL) ?
142 path : (char *)NULL);
143}
144
145char *
146mkdtempat_np(int dfd, char *path)
147{
148 return (find_temp_path(dfd, path, 0, TRUE, _mkdtemp_action, NULL, NULL) ?
149 path : (char *)NULL);
150}
151
152char *
153_mktemp(char *path)
154{
155 return (find_temp_path(AT_FDCWD, path, 0, FALSE, _mktemp_action, NULL, NULL) ?
156 path : (char *)NULL);
157}
158
159__warn_references(mktemp,
160 "warning: mktemp() possibly used unsafely; consider using mkstemp()");
161
162char *
163mktemp(char *path)
164{
165 return (_mktemp(path));
166}
167
168int
169mkstemp_dprotected_np(char *path, int class, int dpflags)
170{
171 int fd;
172 int ctx[2] = { class, dpflags };
173
174 return (find_temp_path(AT_FDCWD, path, 0, TRUE, _mkstemp_dprotected_np_action, &ctx, &fd) ? fd : -1);
175}
176
177/* For every path matching a given template, invoke an action. Depending on
178 * the progress reported by action, stops or tries the next path.
179 * Returns 1 if succeeds, 0 and sets errno if fails.
180 */
181static int
182find_temp_path(int dfd, char *path, int slen, bool stat_base_dir,
183 find_temp_path_action_t action, void *action_ctx, void *action_result)
184{
185 char *start, *trv, *suffp, *carryp;
186 char *pad;
187 struct stat sbuf;
188 int rval;
189 uint32_t rand;
190 char carrybuf[MAXPATHLEN];
191
192 if (slen < 0) {
193 errno = EINVAL;
194 return (0);
195 }
196
197 for (trv = path; *trv != '\0'; ++trv)
198 ;
199 if (trv - path >= MAXPATHLEN) {
200 errno = ENAMETOOLONG;
201 return (0);
202 }
203 trv -= slen;
204 suffp = trv;
205 --trv;
206 if (trv < path || NULL != strchr(suffp, '/')) {
207 errno = EINVAL;
208 return (0);
209 }
210
211 /* Fill space with random characters */
212 while (trv >= path && *trv == 'X') {
213 rand = arc4random_uniform(sizeof(padchar) - 1);
214 *trv-- = padchar[rand];
215 }
216 start = trv + 1;
217
218 /* save first combination of random characters */
219 memcpy(carrybuf, start, suffp - start);
220
221 /*
222 * check the target directory.
223 */
224 if (stat_base_dir) {
225 for (; trv > path; --trv) {
226 if (*trv == '/') {
227 *trv = '\0';
228 rval = fstatat(dfd, path, &sbuf, 0);
229 *trv = '/';
230 if (rval != 0)
231 return (0);
232 if (!S_ISDIR(sbuf.st_mode)) {
233 errno = ENOTDIR;
234 return (0);
235 }
236 break;
237 }
238 }
239 }
240
241 for (;;) {
242 switch (action(dfd, path, action_ctx, action_result)) {
243 case FTPP_DONE:
244 return (1);
245 case FTPP_ERROR:
246 return (0); // errno must be set by the action
247 default:
248 ; // FTPP_TRY_NEXT, fall-through
249 }
250
251 /* If we have a collision, cycle through the space of filenames */
252 for (trv = start, carryp = carrybuf;;) {
253 /* have we tried all possible permutations? */
254 if (trv == suffp) {
255 /* yes - exit with EEXIST */
256 errno = EEXIST;
257 return (0);
258 }
259 pad = strchr(padchar, *trv);
260 if (pad == NULL) {
261 /* this should never happen */
262 errno = EIO;
263 return (0);
264 }
265 /* increment character */
266 *trv = (*++pad == '\0') ? padchar[0] : *pad;
267 /* carry to next position? */
268 if (*trv == *carryp) {
269 /* increment position and loop */
270 ++trv;
271 ++carryp;
272 } else {
273 /* try with new name */
274 break;
275 }
276 }
277 }
278 /*NOTREACHED*/
279}
280
281static find_temp_path_progress_t
282_mkostemps_action(int dfd, char *path, void *ctx, void *result)
283{
284 int oflags = (ctx != NULL) ? *((int *) ctx) : 0;
285 int fd = openat(dfd, path, O_CREAT|O_EXCL|O_RDWR|oflags, 0600);
286 if (fd >= 0) {
287 *((int *) result) = fd;
288 return FTPP_DONE;
289 }
290 return (errno == EEXIST) ?
291 FTPP_TRY_NEXT :
292 FTPP_ERROR; // errno is set already
293}
294
295static find_temp_path_progress_t
296_mktemp_action(int dfd, char *path, void *ctx __unused, void *result __unused)
297{
298 struct stat sbuf;
299 if (fstatat(dfd, path, &sbuf, AT_SYMLINK_NOFOLLOW)) {
300 // stat failed
301 return (errno == ENOENT) ?
302 FTPP_DONE : // path is vacant, done
303 FTPP_ERROR; // errno is set already
304 }
305 return FTPP_TRY_NEXT;
306}
307
308static find_temp_path_progress_t
309_mkdtemp_action(int dfd, char *path, void *ctx __unused, void *result __unused)
310{
311 if (mkdirat(dfd, path, 0700) == 0)
312 return FTPP_DONE;
313 return (errno == EEXIST) ?
314 FTPP_TRY_NEXT :
315 FTPP_ERROR; // errno is set already
316}
317
318static find_temp_path_progress_t
319_mkstemp_dprotected_np_action(int dfd, char *path, void *ctx, void *result)
320{
321 assert(dfd == AT_FDCWD);
322
323 int class = ((int *) ctx)[0];
324 int dpflags = ((int *) ctx)[1];
325 int fd = open_dprotected_np(path, O_CREAT|O_EXCL|O_RDWR, class, dpflags, 0600);
326 if (fd >= 0) {
327 *((int *) result) = fd;
328 return FTPP_DONE;
329 }
330 return (errno == EEXIST) ?
331 FTPP_TRY_NEXT :
332 FTPP_ERROR; // errno is set already
333}
334
335