2 * Copyright (c) 1987, 1993
3 * The Regents of the University of California. All rights reserved.
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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
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>
35 #include "namespace.h"
36 #include <sys/param.h>
45 #include "un-namespace.h"
47 #define ALLOWED_MKOSTEMP_FLAGS (O_APPEND | O_SHLOCK | O_EXLOCK | O_CLOEXEC)
49 char *_mktemp(char *);
52 FTPP_DONE
, FTPP_TRY_NEXT
, FTPP_ERROR
53 } find_temp_path_progress_t
;
55 /* A contract for actions that find_temp_path performs for every path from
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.
62 typedef find_temp_path_progress_t (*find_temp_path_action_t
)(
63 char *path
, void *ctx
, void *result
);
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
);
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
);
77 static const char padchar
[] =
78 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
81 mkostemps(char *path
, int slen
, int oflags
)
84 if (oflags
& ~ALLOWED_MKOSTEMP_FLAGS
) {
88 return (find_temp_path(path
, slen
, TRUE
, _mkostemps_action
, &oflags
, &fd
) ? fd
: -1);
92 mkstemps(char *path
, int slen
)
96 return (find_temp_path(path
, slen
, TRUE
, _mkostemps_action
, NULL
, &fd
) ? fd
: -1);
100 mkostemp(char *path
, int oflags
)
103 if (oflags
& ~ALLOWED_MKOSTEMP_FLAGS
) {
107 return (find_temp_path(path
, 0, TRUE
, _mkostemps_action
, &oflags
, &fd
) ? fd
: -1);
115 return (find_temp_path(path
, 0, TRUE
, _mkostemps_action
, NULL
, &fd
) ? fd
: -1);
121 return (find_temp_path(path
, 0, TRUE
, _mkdtemp_action
, NULL
, NULL
) ?
122 path
: (char *)NULL
);
128 return (find_temp_path(path
, 0, FALSE
, _mktemp_action
, NULL
, NULL
) ?
129 path
: (char *)NULL
);
132 __warn_references(mktemp
,
133 "warning: mktemp() possibly used unsafely; consider using mkstemp()");
138 return (_mktemp(path
));
142 mkstemp_dprotected_np(char *path
, int class, int dpflags
)
145 int ctx
[2] = { class, dpflags
};
147 return (find_temp_path(path
, 0, TRUE
, _mkstemp_dprotected_np_action
, &ctx
, &fd
) ? fd
: -1);
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.
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
)
158 char *start
, *trv
, *suffp
, *carryp
;
163 char carrybuf
[MAXPATHLEN
];
170 for (trv
= path
; *trv
!= '\0'; ++trv
)
172 if (trv
- path
>= MAXPATHLEN
) {
173 errno
= ENAMETOOLONG
;
179 if (trv
< path
|| NULL
!= strchr(suffp
, '/')) {
184 /* Fill space with random characters */
185 while (trv
>= path
&& *trv
== 'X') {
186 rand
= arc4random_uniform(sizeof(padchar
) - 1);
187 *trv
-- = padchar
[rand
];
191 /* save first combination of random characters */
192 memcpy(carrybuf
, start
, suffp
- start
);
195 * check the target directory.
198 for (; trv
> path
; --trv
) {
201 rval
= stat(path
, &sbuf
);
205 if (!S_ISDIR(sbuf
.st_mode
)) {
215 switch (action(path
, action_ctx
, action_result
)) {
219 return (0); // errno must be set by the action
221 ; // FTPP_TRY_NEXT, fall-through
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? */
228 /* yes - exit with EEXIST */
232 pad
= strchr(padchar
, *trv
);
234 /* this should never happen */
238 /* increment character */
239 *trv
= (*++pad
== '\0') ? padchar
[0] : *pad
;
240 /* carry to next position? */
241 if (*trv
== *carryp
) {
242 /* increment position and loop */
246 /* try with new name */
254 static find_temp_path_progress_t
255 _mkostemps_action(char *path
, void *ctx
, void *result
)
257 int oflags
= (ctx
!= NULL
) ? *((int *) ctx
) : 0;
258 int fd
= _open(path
, O_CREAT
|O_EXCL
|O_RDWR
|oflags
, 0600);
260 *((int *) result
) = fd
;
263 return (errno
== EEXIST
) ?
265 FTPP_ERROR
; // errno is set already
268 static find_temp_path_progress_t
269 _mktemp_action(char *path
, void *ctx __unused
, void *result __unused
)
272 if (lstat(path
, &sbuf
)) {
274 return (errno
== ENOENT
) ?
275 FTPP_DONE
: // path is vacant, done
276 FTPP_ERROR
; // errno is set already
278 return FTPP_TRY_NEXT
;
281 static find_temp_path_progress_t
282 _mkdtemp_action(char *path
, void *ctx __unused
, void *result __unused
)
284 if (mkdir(path
, 0700) == 0)
286 return (errno
== EEXIST
) ?
288 FTPP_ERROR
; // errno is set already
291 static find_temp_path_progress_t
292 _mkstemp_dprotected_np_action(char *path
, void *ctx
, void *result
)
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);
298 *((int *) result
) = fd
;
301 return (errno
== EEXIST
) ?
303 FTPP_ERROR
; // errno is set already