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