blob: 28c0098f50cc6f8e5d5d2ed7f5191a297c0252b7 [file] [log] [blame]
lh9ed821d2023-04-07 01:36:19 -07001/* Copyright (C) 1991,92,93,94,95,96,97,98,99 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3
4 The GNU C Library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public License as
6 published by the Free Software Foundation; either version 2 of the
7 License, or (at your option) any later version.
8
9 The GNU C Library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public
15 License along with the GNU C Library; see the file COPYING.LIB. If not,
16 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 Boston, MA 02111-1307, USA. */
18
19/* March 11, 2002 Manuel Novoa III
20 *
21 * Modify code to remove dependency on libgcc long long arith support funcs.
22 */
23
24/* June 6, 2004 Erik Andersen
25 *
26 * Don't use brain damaged getpid() based randomness.
27 */
28
29/* April 15, 2005 Mike Frysinger
30 *
31 * Use brain damaged getpid() if real random fails.
32 */
33
34#include <stddef.h>
35#include <stdint.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#include <errno.h>
40#include <fcntl.h>
41#include <unistd.h>
42#include <assert.h>
43#include <sys/types.h>
44#include <sys/stat.h>
45#include <sys/time.h>
46#include "tempname.h"
47
48/* Return nonzero if DIR is an existent directory. */
49static int direxists (const char *dir)
50{
51 struct stat buf;
52 return stat(dir, &buf) == 0 && S_ISDIR (buf.st_mode);
53}
54
55/* Path search algorithm, for tmpnam, tmpfile, etc. If DIR is
56 non-null and exists, uses it; otherwise uses the first of $TMPDIR,
57 P_tmpdir, /tmp that exists. Copies into TMPL a template suitable
58 for use with mk[s]temp. Will fail (-1) if DIR is non-null and
59 doesn't exist, none of the searched dirs exists, or there's not
60 enough space in TMPL. */
61int attribute_hidden ___path_search (char *tmpl, size_t tmpl_len, const char *dir,
62 const char *pfx /*, int try_tmpdir*/)
63{
64 /*const char *d; */
65 /* dir and pfx lengths should always fit into an int,
66 so don't bother using size_t here. Especially since
67 the printf func requires an int for precision (%*s). */
68 int dlen, plen;
69
70 if (!pfx || !pfx[0])
71 {
72 pfx = "file";
73 plen = 4;
74 }
75 else
76 {
77 plen = strlen (pfx);
78 if (plen > 5)
79 plen = 5;
80 }
81
82 /* Disable support for $TMPDIR */
83#if 0
84 if (try_tmpdir)
85 {
86 d = __secure_getenv ("TMPDIR");
87 if (d != NULL && direxists (d))
88 dir = d;
89 else if (dir != NULL && direxists (dir))
90 /* nothing */ ;
91 else
92 dir = NULL;
93 }
94#endif
95 if (dir == NULL)
96 {
97 if (direxists (P_tmpdir))
98 dir = P_tmpdir;
99 else if (strcmp (P_tmpdir, "/tmp") != 0 && direxists ("/tmp"))
100 dir = "/tmp";
101 else
102 {
103 __set_errno (ENOENT);
104 return -1;
105 }
106 }
107
108 dlen = strlen (dir);
109 while (dlen > 1 && dir[dlen - 1] == '/')
110 dlen--; /* remove trailing slashes */
111
112 /* check we have room for "${dir}/${pfx}XXXXXX\0" */
113 if (tmpl_len < (size_t)dlen + 1 + plen + 6 + 1)
114 {
115 __set_errno (EINVAL);
116 return -1;
117 }
118
119 sprintf (tmpl, "%.*s/%.*sXXXXXX", dlen, dir, plen, pfx);
120 return 0;
121}
122
123/* These are the characters used in temporary filenames. */
124static const char letters[] =
125"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
126#define NUM_LETTERS (62)
127
128static unsigned int fillrand(unsigned char *buf, unsigned int len)
129{
130 int fd;
131 unsigned int result = -1;
132 fd = open("/dev/urandom", O_RDONLY);
133 if (fd < 0) {
134 fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
135 }
136 if (fd >= 0) {
137 result = read(fd, buf, len);
138 close(fd);
139 }
140 return result;
141}
142
143static void brain_damaged_fillrand(unsigned char *buf, unsigned int len)
144{
145 unsigned int i, k;
146 struct timeval tv;
147 uint32_t high, low, rh;
148 static uint64_t value;
149 gettimeofday(&tv, NULL);
150 value += ((uint64_t) tv.tv_usec << 16) ^ tv.tv_sec ^ getpid();
151 low = value & UINT32_MAX;
152 high = value >> 32;
153 for (i = 0; i < len; ++i) {
154 rh = high % NUM_LETTERS;
155 high /= NUM_LETTERS;
156#define L ((UINT32_MAX % NUM_LETTERS + 1) % NUM_LETTERS)
157 k = (low % NUM_LETTERS) + (L * rh);
158#undef L
159#define H ((UINT32_MAX / NUM_LETTERS) + ((UINT32_MAX % NUM_LETTERS + 1) / NUM_LETTERS))
160 low = (low / NUM_LETTERS) + (H * rh) + (k / NUM_LETTERS);
161#undef H
162 k %= NUM_LETTERS;
163 buf[i] = letters[k];
164 }
165}
166
167/* Generate a temporary file name based on TMPL. TMPL must match the
168 rules for mk[s]temp (i.e. end in "XXXXXX"). The name constructed
169 does not exist at the time of the call to __gen_tempname. TMPL is
170 overwritten with the result.
171
172 KIND may be one of:
173 __GT_NOCREATE: simply verify that the name does not exist
174 at the time of the call. mode argument is ignored.
175 __GT_FILE: create the file using open(O_CREAT|O_EXCL)
176 and return a read-write fd with given mode.
177 __GT_BIGFILE: same as __GT_FILE but use open64().
178 __GT_DIR: create a directory with given mode.
179
180*/
181int attribute_hidden __gen_tempname (char *tmpl, int kind, mode_t mode)
182{
183 char *XXXXXX;
184 unsigned int i;
185 int fd, save_errno = errno;
186 unsigned char randomness[6];
187 size_t len;
188
189 len = strlen (tmpl);
190 /* This is where the Xs start. */
191 XXXXXX = tmpl + len - 6;
192 if (len < 6 || strcmp (XXXXXX, "XXXXXX"))
193 {
194 __set_errno (EINVAL);
195 return -1;
196 }
197
198 for (i = 0; i < TMP_MAX; ++i) {
199 unsigned char j;
200 /* Get some random data. */
201 if (fillrand(randomness, sizeof(randomness)) != sizeof(randomness)) {
202 /* if random device nodes failed us, lets use the braindamaged ver */
203 brain_damaged_fillrand(randomness, sizeof(randomness));
204 }
205 for (j = 0; j < sizeof(randomness); ++j)
206 XXXXXX[j] = letters[randomness[j] % NUM_LETTERS];
207
208 switch (kind) {
209 case __GT_NOCREATE:
210 {
211 struct stat st;
212 if (stat (tmpl, &st) < 0) {
213 if (errno == ENOENT) {
214 fd = 0;
215 goto restore_and_ret;
216 } else
217 /* Give up now. */
218 return -1;
219 } else
220 fd = 0;
221 }
222 case __GT_FILE:
223 fd = open (tmpl, O_RDWR | O_CREAT | O_EXCL, mode);
224 break;
225#if defined __UCLIBC_HAS_LFS__
226 case __GT_BIGFILE:
227 fd = open64 (tmpl, O_RDWR | O_CREAT | O_EXCL, mode);
228 break;
229#endif
230 case __GT_DIR:
231 fd = mkdir (tmpl, mode);
232 break;
233 default:
234 fd = -1;
235 assert (! "invalid KIND in __gen_tempname");
236 }
237
238 if (fd >= 0) {
239restore_and_ret:
240 __set_errno (save_errno);
241 return fd;
242 }
243 else if (errno != EEXIST)
244 /* Any other error will apply also to other names we might
245 try, and there are 2^32 or so of them, so give up now. */
246 return -1;
247 }
248
249 /* We got out of the loop because we ran out of combinations to try. */
250 __set_errno (EEXIST);
251 return -1;
252}