Commit | Line | Data |
---|---|---|
9c9d63b1 | 1 | /* Copyright (C) 1991-2021 Free Software Foundation, Inc. |
698be2d8 | 2 | This file is part of the GNU C Library. |
2d8adcbd | 3 | |
698be2d8 CB |
4 | The GNU C Library is free software; you can redistribute it and/or |
5 | modify it under the terms of the GNU General Public | |
6 | License as published by the Free Software Foundation; either | |
7 | version 3 of the License, or (at your option) any later version. | |
2d8adcbd | 8 | |
698be2d8 | 9 | The GNU C Library is distributed in the hope that it will be useful, |
2d8adcbd | 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
698be2d8 CB |
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | General Public License for more details. | |
2d8adcbd | 13 | |
698be2d8 CB |
14 | You should have received a copy of the GNU General Public |
15 | License along with the GNU C Library; if not, see | |
16 | <https://www.gnu.org/licenses/>. */ | |
2d8adcbd MM |
17 | |
18 | #if !_LIBC | |
698be2d8 | 19 | # include <libc-config.h> |
2d8adcbd MM |
20 | # include "tempname.h" |
21 | #endif | |
22 | ||
23 | #include <sys/types.h> | |
24 | #include <assert.h> | |
25 | ||
26 | #include <errno.h> | |
2d8adcbd MM |
27 | |
28 | #include <stdio.h> | |
29 | #ifndef P_tmpdir | |
30 | # define P_tmpdir "/tmp" | |
31 | #endif | |
32 | #ifndef TMP_MAX | |
33 | # define TMP_MAX 238328 | |
34 | #endif | |
35 | #ifndef __GT_FILE | |
36 | # define __GT_FILE 0 | |
37 | # define __GT_DIR 1 | |
38 | # define __GT_NOCREATE 2 | |
39 | #endif | |
40 | #if !_LIBC && (GT_FILE != __GT_FILE || GT_DIR != __GT_DIR \ | |
41 | || GT_NOCREATE != __GT_NOCREATE) | |
42 | # error report this to bug-gnulib@gnu.org | |
43 | #endif | |
44 | ||
45 | #include <stddef.h> | |
46 | #include <stdlib.h> | |
47 | #include <string.h> | |
48 | ||
49 | #include <fcntl.h> | |
9c9d63b1 | 50 | #include <stdalign.h> |
2d8adcbd | 51 | #include <stdint.h> |
698be2d8 | 52 | #include <sys/random.h> |
2d8adcbd | 53 | #include <sys/stat.h> |
9c9d63b1 | 54 | #include <time.h> |
2d8adcbd MM |
55 | |
56 | #if _LIBC | |
57 | # define struct_stat64 struct stat64 | |
698be2d8 | 58 | # define __secure_getenv __libc_secure_getenv |
2d8adcbd MM |
59 | #else |
60 | # define struct_stat64 struct stat | |
2d8adcbd | 61 | # define __gen_tempname gen_tempname |
2d8adcbd MM |
62 | # define __mkdir mkdir |
63 | # define __open open | |
64 | # define __lxstat64(version, file, buf) lstat (file, buf) | |
9c9d63b1 PM |
65 | # define __getrandom getrandom |
66 | # define __clock_gettime64 clock_gettime | |
67 | # define __timespec64 timespec | |
2d8adcbd MM |
68 | #endif |
69 | ||
698be2d8 | 70 | /* Use getrandom if it works, falling back on a 64-bit linear |
9c9d63b1 PM |
71 | congruential generator that starts with Var's value |
72 | mixed in with a clock's low-order bits if available. */ | |
698be2d8 | 73 | typedef uint_fast64_t random_value; |
9c9d63b1 PM |
74 | #define RANDOM_VALUE_MAX UINT_FAST64_MAX |
75 | #define BASE_62_DIGITS 10 /* 62**10 < UINT_FAST64_MAX */ | |
76 | #define BASE_62_POWER (62LL * 62 * 62 * 62 * 62 * 62 * 62 * 62 * 62 * 62) | |
77 | ||
78 | static random_value | |
79 | random_bits (random_value var) | |
80 | { | |
81 | random_value r; | |
82 | if (__getrandom (&r, sizeof r, 0) == sizeof r) | |
83 | return r; | |
84 | #if _LIBC || (defined CLOCK_MONOTONIC && HAVE_CLOCK_GETTIME) | |
85 | /* Add entropy if getrandom is not supported. */ | |
86 | struct __timespec64 tv; | |
87 | __clock_gettime64 (CLOCK_MONOTONIC, &tv); | |
88 | var ^= tv.tv_nsec; | |
2d8adcbd | 89 | #endif |
9c9d63b1 PM |
90 | return 2862933555777941757 * var + 3037000493; |
91 | } | |
2d8adcbd MM |
92 | |
93 | #if _LIBC | |
94 | /* Return nonzero if DIR is an existent directory. */ | |
95 | static int | |
96 | direxists (const char *dir) | |
97 | { | |
98 | struct_stat64 buf; | |
99 | return __xstat64 (_STAT_VER, dir, &buf) == 0 && S_ISDIR (buf.st_mode); | |
100 | } | |
101 | ||
102 | /* Path search algorithm, for tmpnam, tmpfile, etc. If DIR is | |
103 | non-null and exists, uses it; otherwise uses the first of $TMPDIR, | |
104 | P_tmpdir, /tmp that exists. Copies into TMPL a template suitable | |
105 | for use with mk[s]temp. Will fail (-1) if DIR is non-null and | |
106 | doesn't exist, none of the searched dirs exists, or there's not | |
107 | enough space in TMPL. */ | |
108 | int | |
109 | __path_search (char *tmpl, size_t tmpl_len, const char *dir, const char *pfx, | |
110 | int try_tmpdir) | |
111 | { | |
112 | const char *d; | |
113 | size_t dlen, plen; | |
114 | ||
115 | if (!pfx || !pfx[0]) | |
116 | { | |
117 | pfx = "file"; | |
118 | plen = 4; | |
119 | } | |
120 | else | |
121 | { | |
122 | plen = strlen (pfx); | |
123 | if (plen > 5) | |
124 | plen = 5; | |
125 | } | |
126 | ||
127 | if (try_tmpdir) | |
128 | { | |
129 | d = __secure_getenv ("TMPDIR"); | |
130 | if (d != NULL && direxists (d)) | |
131 | dir = d; | |
132 | else if (dir != NULL && direxists (dir)) | |
133 | /* nothing */ ; | |
134 | else | |
135 | dir = NULL; | |
136 | } | |
137 | if (dir == NULL) | |
138 | { | |
139 | if (direxists (P_tmpdir)) | |
140 | dir = P_tmpdir; | |
141 | else if (strcmp (P_tmpdir, "/tmp") != 0 && direxists ("/tmp")) | |
142 | dir = "/tmp"; | |
143 | else | |
144 | { | |
145 | __set_errno (ENOENT); | |
146 | return -1; | |
147 | } | |
148 | } | |
149 | ||
150 | dlen = strlen (dir); | |
151 | while (dlen > 1 && dir[dlen - 1] == '/') | |
152 | dlen--; /* remove trailing slashes */ | |
153 | ||
154 | /* check we have room for "${dir}/${pfx}XXXXXX\0" */ | |
155 | if (tmpl_len < dlen + 1 + plen + 6 + 1) | |
156 | { | |
157 | __set_errno (EINVAL); | |
158 | return -1; | |
159 | } | |
160 | ||
161 | sprintf (tmpl, "%.*s/%.*sXXXXXX", (int) dlen, dir, (int) plen, pfx); | |
162 | return 0; | |
163 | } | |
164 | #endif /* _LIBC */ | |
165 | ||
698be2d8 CB |
166 | #if _LIBC |
167 | static int try_tempname_len (char *, int, void *, int (*) (char *, void *), | |
168 | size_t); | |
169 | #endif | |
170 | ||
171 | static int | |
172 | try_file (char *tmpl, void *flags) | |
173 | { | |
174 | int *openflags = flags; | |
175 | return __open (tmpl, | |
176 | (*openflags & ~O_ACCMODE) | |
177 | | O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR); | |
178 | } | |
179 | ||
180 | static int | |
181 | try_dir (char *tmpl, void *flags _GL_UNUSED) | |
182 | { | |
183 | return __mkdir (tmpl, S_IRUSR | S_IWUSR | S_IXUSR); | |
184 | } | |
185 | ||
186 | static int | |
187 | try_nocreate (char *tmpl, void *flags _GL_UNUSED) | |
188 | { | |
189 | struct_stat64 st; | |
190 | ||
191 | if (__lxstat64 (_STAT_VER, tmpl, &st) == 0 || errno == EOVERFLOW) | |
192 | __set_errno (EEXIST); | |
193 | return errno == ENOENT ? 0 : -1; | |
194 | } | |
195 | ||
2d8adcbd MM |
196 | /* These are the characters used in temporary file names. */ |
197 | static const char letters[] = | |
198 | "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; | |
199 | ||
698be2d8 CB |
200 | /* Generate a temporary file name based on TMPL. TMPL must match the |
201 | rules for mk[s]temp (i.e., end in at least X_SUFFIX_LEN "X"s, | |
202 | possibly with a suffix). | |
203 | The name constructed does not exist at the time of the call to | |
204 | this function. TMPL is overwritten with the result. | |
205 | ||
206 | KIND may be one of: | |
207 | __GT_NOCREATE: simply verify that the name does not exist | |
208 | at the time of the call. | |
209 | __GT_FILE: create the file using open(O_CREAT|O_EXCL) | |
210 | and return a read-write fd. The file is mode 0600. | |
211 | __GT_DIR: create a directory, which will be mode 0700. | |
212 | ||
213 | We use a clever algorithm to get hard-to-predict names. */ | |
214 | #ifdef _LIBC | |
215 | static | |
216 | #endif | |
2d8adcbd | 217 | int |
698be2d8 CB |
218 | gen_tempname_len (char *tmpl, int suffixlen, int flags, int kind, |
219 | size_t x_suffix_len) | |
2d8adcbd | 220 | { |
698be2d8 CB |
221 | static int (*const tryfunc[]) (char *, void *) = |
222 | { | |
223 | [__GT_FILE] = try_file, | |
224 | [__GT_DIR] = try_dir, | |
225 | [__GT_NOCREATE] = try_nocreate | |
226 | }; | |
227 | return try_tempname_len (tmpl, suffixlen, &flags, tryfunc[kind], | |
228 | x_suffix_len); | |
229 | } | |
230 | ||
231 | #ifdef _LIBC | |
232 | static | |
233 | #endif | |
234 | int | |
235 | try_tempname_len (char *tmpl, int suffixlen, void *args, | |
236 | int (*tryfunc) (char *, void *), size_t x_suffix_len) | |
237 | { | |
238 | size_t len; | |
2d8adcbd | 239 | char *XXXXXX; |
2d8adcbd MM |
240 | unsigned int count; |
241 | int fd = -1; | |
242 | int save_errno = errno; | |
243 | ||
244 | /* A lower bound on the number of temporary files to attempt to | |
245 | generate. The maximum total number of temporary file names that | |
246 | can exist for a given template is 62**6. It should never be | |
247 | necessary to try all of these combinations. Instead if a reasonable | |
248 | number of names is tried (we define reasonable as 62**3) fail to | |
698be2d8 CB |
249 | give the system administrator the chance to remove the problems. |
250 | This value requires that X_SUFFIX_LEN be at least 3. */ | |
2d8adcbd MM |
251 | #define ATTEMPTS_MIN (62 * 62 * 62) |
252 | ||
253 | /* The number of times to attempt to generate a temporary file. To | |
254 | conform to POSIX, this must be no smaller than TMP_MAX. */ | |
255 | #if ATTEMPTS_MIN < TMP_MAX | |
256 | unsigned int attempts = TMP_MAX; | |
257 | #else | |
258 | unsigned int attempts = ATTEMPTS_MIN; | |
259 | #endif | |
260 | ||
9c9d63b1 PM |
261 | /* A random variable. The initial value is used only the for fallback path |
262 | on 'random_bits' on 'getrandom' failure. Its initial value tries to use | |
263 | some entropy from the ASLR and ignore possible bits from the stack | |
264 | alignment. */ | |
265 | random_value v = ((uintptr_t) &v) / alignof (max_align_t); | |
698be2d8 CB |
266 | |
267 | /* How many random base-62 digits can currently be extracted from V. */ | |
268 | int vdigits = 0; | |
269 | ||
270 | /* Least unfair value for V. If V is less than this, V can generate | |
271 | BASE_62_DIGITS digits fairly. Otherwise it might be biased. */ | |
272 | random_value const unfair_min | |
273 | = RANDOM_VALUE_MAX - RANDOM_VALUE_MAX % BASE_62_POWER; | |
274 | ||
2d8adcbd | 275 | len = strlen (tmpl); |
698be2d8 CB |
276 | if (len < x_suffix_len + suffixlen |
277 | || strspn (&tmpl[len - x_suffix_len - suffixlen], "X") < x_suffix_len) | |
2d8adcbd MM |
278 | { |
279 | __set_errno (EINVAL); | |
280 | return -1; | |
281 | } | |
282 | ||
283 | /* This is where the Xs start. */ | |
698be2d8 | 284 | XXXXXX = &tmpl[len - x_suffix_len - suffixlen]; |
2d8adcbd | 285 | |
698be2d8 | 286 | for (count = 0; count < attempts; ++count) |
2d8adcbd | 287 | { |
698be2d8 CB |
288 | for (size_t i = 0; i < x_suffix_len; i++) |
289 | { | |
290 | if (vdigits == 0) | |
291 | { | |
292 | do | |
9c9d63b1 | 293 | v = random_bits (v); |
698be2d8 CB |
294 | while (unfair_min <= v); |
295 | ||
296 | vdigits = BASE_62_DIGITS; | |
297 | } | |
298 | ||
299 | XXXXXX[i] = letters[v % 62]; | |
300 | v /= 62; | |
301 | vdigits--; | |
302 | } | |
2d8adcbd MM |
303 | |
304 | fd = tryfunc (tmpl, args); | |
305 | if (fd >= 0) | |
306 | { | |
307 | __set_errno (save_errno); | |
308 | return fd; | |
309 | } | |
310 | else if (errno != EEXIST) | |
311 | return -1; | |
312 | } | |
313 | ||
314 | /* We got out of the loop because we ran out of combinations to try. */ | |
315 | __set_errno (EEXIST); | |
316 | return -1; | |
317 | } | |
318 | ||
698be2d8 CB |
319 | int |
320 | __gen_tempname (char *tmpl, int suffixlen, int flags, int kind) | |
2d8adcbd | 321 | { |
698be2d8 | 322 | return gen_tempname_len (tmpl, suffixlen, flags, kind, 6); |
2d8adcbd MM |
323 | } |
324 | ||
698be2d8 | 325 | #if !_LIBC |
2d8adcbd | 326 | int |
698be2d8 CB |
327 | try_tempname (char *tmpl, int suffixlen, void *args, |
328 | int (*tryfunc) (char *, void *)) | |
2d8adcbd | 329 | { |
698be2d8 | 330 | return try_tempname_len (tmpl, suffixlen, args, tryfunc, 6); |
2d8adcbd | 331 | } |
698be2d8 | 332 | #endif |