1 /*-
2 * SPDX-License-Identifier: BSD-3-Clause
3 *
4 * Copyright (c) 1987, 1993
5 * The Regents of the University of California. All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 * may be used to endorse or promote products derived from this software
17 * without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32 #if defined(LIBC_SCCS) && !defined(lint)
33 static char sccsid[] = "@(#)mktemp.c 8.1 (Berkeley) 6/4/93";
34 #endif /* LIBC_SCCS and not lint */
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 char *_mktemp(char *);
48
49 static int _gettemp(int, char *, int *, int, int, int);
50
51 static const unsigned char padchar[] =
52 "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
53
54 int
mkostempsat(int dfd,char * path,int slen,int oflags)55 mkostempsat(int dfd, char *path, int slen, int oflags)
56 {
57 int fd;
58
59 return (_gettemp(dfd, path, &fd, 0, slen, oflags) ? fd : -1);
60 }
61
62 int
mkostemps(char * path,int slen,int oflags)63 mkostemps(char *path, int slen, int oflags)
64 {
65 int fd;
66
67 return (_gettemp(AT_FDCWD, path, &fd, 0, slen, oflags) ? fd : -1);
68 }
69
70 int
mkstemps(char * path,int slen)71 mkstemps(char *path, int slen)
72 {
73 int fd;
74
75 return (_gettemp(AT_FDCWD, path, &fd, 0, slen, 0) ? fd : -1);
76 }
77
78 int
mkostemp(char * path,int oflags)79 mkostemp(char *path, int oflags)
80 {
81 int fd;
82
83 return (_gettemp(AT_FDCWD, path, &fd, 0, 0, oflags) ? fd : -1);
84 }
85
86 int
mkstemp(char * path)87 mkstemp(char *path)
88 {
89 int fd;
90
91 return (_gettemp(AT_FDCWD, path, &fd, 0, 0, 0) ? fd : -1);
92 }
93
94 char *
mkdtemp(char * path)95 mkdtemp(char *path)
96 {
97 return (_gettemp(AT_FDCWD, path, (int *)NULL, 1, 0, 0) ? path : (char *)NULL);
98 }
99
100 char *
_mktemp(char * path)101 _mktemp(char *path)
102 {
103 return (_gettemp(AT_FDCWD, path, (int *)NULL, 0, 0, 0) ? path : (char *)NULL);
104 }
105
106 __warn_references(mktemp,
107 "warning: mktemp() possibly used unsafely; consider using mkstemp()");
108
109 char *
mktemp(char * path)110 mktemp(char *path)
111 {
112 return (_mktemp(path));
113 }
114
115 static int
_gettemp(int dfd,char * path,int * doopen,int domkdir,int slen,int oflags)116 _gettemp(int dfd, char *path, int *doopen, int domkdir, int slen, int oflags)
117 {
118 char *start, *trv, *suffp, *carryp;
119 char *pad;
120 struct stat sbuf;
121 uint32_t rand;
122 char carrybuf[MAXPATHLEN];
123 int saved;
124
125 if ((doopen != NULL && domkdir) || slen < 0 ||
126 (oflags & ~(O_APPEND | O_DIRECT | O_SHLOCK | O_EXLOCK | O_SYNC |
127 O_CLOEXEC)) != 0) {
128 errno = EINVAL;
129 return (0);
130 }
131
132 trv = path + strlen(path);
133 if (trv - path >= MAXPATHLEN) {
134 errno = ENAMETOOLONG;
135 return (0);
136 }
137 trv -= slen;
138 suffp = trv;
139 --trv;
140 if (trv < path || NULL != strchr(suffp, '/')) {
141 errno = EINVAL;
142 return (0);
143 }
144
145 /* Fill space with random characters */
146 while (trv >= path && *trv == 'X') {
147 rand = arc4random_uniform(sizeof(padchar) - 1);
148 *trv-- = padchar[rand];
149 }
150 start = trv + 1;
151
152 saved = 0;
153 oflags |= O_CREAT | O_EXCL | O_RDWR;
154 for (;;) {
155 if (doopen) {
156 *doopen = _openat(dfd, path, oflags, 0600);
157 if (*doopen >= 0)
158 return (1);
159 if (errno != EEXIST)
160 return (0);
161 } else if (domkdir) {
162 if (mkdir(path, 0700) == 0)
163 return (1);
164 if (errno != EEXIST)
165 return (0);
166 } else if (lstat(path, &sbuf))
167 return (errno == ENOENT);
168
169 /* save first combination of random characters */
170 if (!saved) {
171 memcpy(carrybuf, start, suffp - start);
172 saved = 1;
173 }
174
175 /* If we have a collision, cycle through the space of filenames */
176 for (trv = start, carryp = carrybuf;;) {
177 /* have we tried all possible permutations? */
178 if (trv == suffp)
179 return (0); /* yes - exit with EEXIST */
180 pad = strchr(padchar, *trv);
181 if (pad == NULL) {
182 /* this should never happen */
183 errno = EIO;
184 return (0);
185 }
186 /* increment character */
187 *trv = (*++pad == '\0') ? padchar[0] : *pad;
188 /* carry to next position? */
189 if (*trv == *carryp) {
190 /* increment position and loop */
191 ++trv;
192 ++carryp;
193 } else {
194 /* try with new name */
195 break;
196 }
197 }
198 }
199 /*NOTREACHED*/
200 }
201