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"
37 #include <sys/param.h>
46 #include "un-namespace.h"
48 #define ALLOWED_MKOSTEMP_FLAGS (O_APPEND | O_SHLOCK | O_EXLOCK | O_CLOEXEC)
50 char *_mktemp(char *);
53 FTPP_DONE
, FTPP_TRY_NEXT
, FTPP_ERROR
54 } find_temp_path_progress_t
;
56 /* A contract for actions that find_temp_path performs for every path from
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.
63 typedef find_temp_path_progress_t (*find_temp_path_action_t
)(
64 int dfd
, char *path
, void *ctx
, void *result
);
66 static 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
);
69 static find_temp_path_progress_t
_mkostemps_action(
70 int dfd
, char *path
, void *ctx
, void *result
);
71 static find_temp_path_progress_t
_mktemp_action(
72 int dfd
, char *path
, void *ctx
, void *result
);
73 static find_temp_path_progress_t
_mkdtemp_action(
74 int dfd
, char *path
, void *ctx
, void *result
);
75 static find_temp_path_progress_t
_mkstemp_dprotected_np_action(
76 int dfd
, char *path
, void *ctx
, void *result
);
78 static const char padchar
[] =
79 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
82 mkostemps(char *path
, int slen
, int oflags
)
85 if (oflags
& ~ALLOWED_MKOSTEMP_FLAGS
) {
89 return (find_temp_path(AT_FDCWD
, path
, slen
, TRUE
, _mkostemps_action
, &oflags
, &fd
) ? fd
: -1);
93 mkostempsat_np(int dfd
, char *path
, int slen
, int oflags
)
96 if (oflags
& ~ALLOWED_MKOSTEMP_FLAGS
) {
100 return (find_temp_path(dfd
, path
, slen
, TRUE
, _mkostemps_action
, &oflags
, &fd
) ? fd
: -1);
104 mkstemps(char *path
, int slen
)
108 return (find_temp_path(AT_FDCWD
, path
, slen
, TRUE
, _mkostemps_action
, NULL
, &fd
) ? fd
: -1);
112 mkstempsat_np(int dfd
, char *path
, int slen
)
116 return (find_temp_path(dfd
, path
, slen
, TRUE
, _mkostemps_action
, NULL
, &fd
) ? fd
: -1);
120 mkostemp(char *path
, int oflags
)
123 if (oflags
& ~ALLOWED_MKOSTEMP_FLAGS
) {
127 return (find_temp_path(AT_FDCWD
, path
, 0, TRUE
, _mkostemps_action
, &oflags
, &fd
) ? fd
: -1);
135 return (find_temp_path(AT_FDCWD
, path
, 0, TRUE
, _mkostemps_action
, NULL
, &fd
) ? fd
: -1);
141 return (find_temp_path(AT_FDCWD
, path
, 0, TRUE
, _mkdtemp_action
, NULL
, NULL
) ?
142 path
: (char *)NULL
);
146 mkdtempat_np(int dfd
, char *path
)
148 return (find_temp_path(dfd
, path
, 0, TRUE
, _mkdtemp_action
, NULL
, NULL
) ?
149 path
: (char *)NULL
);
155 return (find_temp_path(AT_FDCWD
, path
, 0, FALSE
, _mktemp_action
, NULL
, NULL
) ?
156 path
: (char *)NULL
);
159 __warn_references(mktemp
,
160 "warning: mktemp() possibly used unsafely; consider using mkstemp()");
165 return (_mktemp(path
));
169 mkstemp_dprotected_np(char *path
, int class, int dpflags
)
172 int ctx
[2] = { class, dpflags
};
174 return (find_temp_path(AT_FDCWD
, path
, 0, TRUE
, _mkstemp_dprotected_np_action
, &ctx
, &fd
) ? fd
: -1);
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.
182 find_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
)
185 char *start
, *trv
, *suffp
, *carryp
;
190 char carrybuf
[MAXPATHLEN
];
197 for (trv
= path
; *trv
!= '\0'; ++trv
)
199 if (trv
- path
>= MAXPATHLEN
) {
200 errno
= ENAMETOOLONG
;
206 if (trv
< path
|| NULL
!= strchr(suffp
, '/')) {
211 /* Fill space with random characters */
212 while (trv
>= path
&& *trv
== 'X') {
213 rand
= arc4random_uniform(sizeof(padchar
) - 1);
214 *trv
-- = padchar
[rand
];
218 /* save first combination of random characters */
219 memcpy(carrybuf
, start
, suffp
- start
);
222 * check the target directory.
225 for (; trv
> path
; --trv
) {
228 rval
= fstatat(dfd
, path
, &sbuf
, 0);
232 if (!S_ISDIR(sbuf
.st_mode
)) {
242 switch (action(dfd
, path
, action_ctx
, action_result
)) {
246 return (0); // errno must be set by the action
248 ; // FTPP_TRY_NEXT, fall-through
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? */
255 /* yes - exit with EEXIST */
259 pad
= strchr(padchar
, *trv
);
261 /* this should never happen */
265 /* increment character */
266 *trv
= (*++pad
== '\0') ? padchar
[0] : *pad
;
267 /* carry to next position? */
268 if (*trv
== *carryp
) {
269 /* increment position and loop */
273 /* try with new name */
281 static find_temp_path_progress_t
282 _mkostemps_action(int dfd
, char *path
, void *ctx
, void *result
)
284 int oflags
= (ctx
!= NULL
) ? *((int *) ctx
) : 0;
285 int fd
= openat(dfd
, path
, O_CREAT
|O_EXCL
|O_RDWR
|oflags
, 0600);
287 *((int *) result
) = fd
;
290 return (errno
== EEXIST
) ?
292 FTPP_ERROR
; // errno is set already
295 static find_temp_path_progress_t
296 _mktemp_action(int dfd
, char *path
, void *ctx __unused
, void *result __unused
)
299 if (fstatat(dfd
, path
, &sbuf
, AT_SYMLINK_NOFOLLOW
)) {
301 return (errno
== ENOENT
) ?
302 FTPP_DONE
: // path is vacant, done
303 FTPP_ERROR
; // errno is set already
305 return FTPP_TRY_NEXT
;
308 static find_temp_path_progress_t
309 _mkdtemp_action(int dfd
, char *path
, void *ctx __unused
, void *result __unused
)
311 if (mkdirat(dfd
, path
, 0700) == 0)
313 return (errno
== EEXIST
) ?
315 FTPP_ERROR
; // errno is set already
318 static find_temp_path_progress_t
319 _mkstemp_dprotected_np_action(int dfd
, char *path
, void *ctx
, void *result
)
321 assert(dfd
== AT_FDCWD
);
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);
327 *((int *) result
) = fd
;
330 return (errno
== EEXIST
) ?
332 FTPP_ERROR
; // errno is set already