lh | 9ed821d | 2023-04-07 01:36:19 -0700 | [diff] [blame^] | 1 | /* 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. */ |
| 49 | static 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. */ |
| 61 | int 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. */ |
| 124 | static const char letters[] = |
| 125 | "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; |
| 126 | #define NUM_LETTERS (62) |
| 127 | |
| 128 | static 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 | |
| 143 | static 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 | */ |
| 181 | int 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) { |
| 239 | restore_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 | } |