/* Copyright (C) 1991,92,93,94,95,96,97,98,99 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with the GNU C Library; see the file COPYING.LIB. If not, see <http://www.gnu.org/licenses/>. */ /* March 11, 2002 Manuel Novoa III * * Modify code to remove dependency on libgcc long long arith support funcs. */ /* June 6, 2004 Erik Andersen * * Don't use brain damaged getpid() based randomness. */ /* April 15, 2005 Mike Frysinger * * Use brain damaged getpid() if real random fails. */ #include <stddef.h> #include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <fcntl.h> #include <unistd.h> #include <assert.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/time.h> #include "tempname.h" /* Return nonzero if DIR is an existent directory. */ static int direxists (const char *dir) { struct stat buf; return stat(dir, &buf) == 0 && S_ISDIR (buf.st_mode); } /* Path search algorithm, for tmpnam, tmpfile, etc. If DIR is non-null and exists, uses it; otherwise uses the first of $TMPDIR, P_tmpdir, /tmp that exists. Copies into TMPL a template suitable for use with mk[s]temp. Will fail (-1) if DIR is non-null and doesn't exist, none of the searched dirs exists, or there's not enough space in TMPL. */ int ___path_search (char *tmpl, size_t tmpl_len, const char *dir, const char *pfx /*, int try_tmpdir*/) { /*const char *d; */ /* dir and pfx lengths should always fit into an int, so don't bother using size_t here. Especially since the printf func requires an int for precision (%*s). */ int dlen, plen; if (!pfx || !pfx[0]) { pfx = "file"; plen = 4; } else { plen = strlen (pfx); if (plen > 5) plen = 5; } /* Disable support for $TMPDIR */ #if 0 if (try_tmpdir) { d = __secure_getenv ("TMPDIR"); if (d != NULL && direxists (d)) dir = d; else if (dir != NULL && direxists (dir)) /* nothing */ ; else dir = NULL; } #endif if (dir == NULL) { if (direxists (P_tmpdir)) dir = P_tmpdir; else if (strcmp (P_tmpdir, "/tmp") != 0 && direxists ("/tmp")) dir = "/tmp"; else { __set_errno (ENOENT); return -1; } } dlen = strlen (dir); while (dlen > 1 && dir[dlen - 1] == '/') dlen--; /* remove trailing slashes */ /* check we have room for "${dir}/${pfx}XXXXXX\0" */ if (tmpl_len < (size_t)dlen + 1 + plen + 6 + 1) { __set_errno (EINVAL); return -1; } sprintf (tmpl, "%.*s/%.*sXXXXXX", dlen, dir, plen, pfx); return 0; } /* These are the characters used in temporary filenames. */ static const char letters[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; #define NUM_LETTERS (62) static unsigned int fillrand(unsigned char *buf, unsigned int len) { int fd; unsigned int result = -1; fd = open("/dev/urandom", O_RDONLY); if (fd < 0) { fd = open("/dev/random", O_RDONLY | O_NONBLOCK); } if (fd >= 0) { result = read(fd, buf, len); close(fd); } return result; } static void brain_damaged_fillrand(unsigned char *buf, unsigned int len) { unsigned int i, k; struct timeval tv; uint32_t high, low, rh; static uint64_t value; gettimeofday(&tv, NULL); value += ((uint64_t) tv.tv_usec << 16) ^ tv.tv_sec ^ getpid(); low = value & UINT32_MAX; high = value >> 32; for (i = 0; i < len; ++i) { rh = high % NUM_LETTERS; high /= NUM_LETTERS; #define L ((UINT32_MAX % NUM_LETTERS + 1) % NUM_LETTERS) k = (low % NUM_LETTERS) + (L * rh); #undef L #define H ((UINT32_MAX / NUM_LETTERS) + ((UINT32_MAX % NUM_LETTERS + 1) / NUM_LETTERS)) low = (low / NUM_LETTERS) + (H * rh) + (k / NUM_LETTERS); #undef H k %= NUM_LETTERS; buf[i] = letters[k]; } } /* Generate a temporary file name based on TMPL. TMPL must match the rules for mk[s]temp (i.e. end in "XXXXXX"). The name constructed does not exist at the time of the call to __gen_tempname. TMPL is overwritten with the result. KIND may be one of: __GT_NOCREATE: simply verify that the name does not exist at the time of the call. mode argument is ignored. __GT_FILE: create the file using open(O_CREAT|O_EXCL) and return a read-write fd with given mode. __GT_BIGFILE: same as __GT_FILE but use open64(). __GT_DIR: create a directory with given mode. */ int attribute_hidden __gen_tempname (char *tmpl, int kind, mode_t mode) { char *XXXXXX; unsigned int i; int fd, save_errno = errno; unsigned char randomness[6]; size_t len; len = strlen (tmpl); /* This is where the Xs start. */ XXXXXX = tmpl + len - 6; if (len < 6 || strcmp (XXXXXX, "XXXXXX")) { __set_errno (EINVAL); return -1; } for (i = 0; i < TMP_MAX; ++i) { unsigned char j; /* Get some random data. */ if (fillrand(randomness, sizeof(randomness)) != sizeof(randomness)) { /* if random device nodes failed us, lets use the braindamaged ver */ brain_damaged_fillrand(randomness, sizeof(randomness)); } for (j = 0; j < sizeof(randomness); ++j) XXXXXX[j] = letters[randomness[j] % NUM_LETTERS]; switch (kind) { case __GT_NOCREATE: { struct stat st; if (stat (tmpl, &st) < 0) { if (errno == ENOENT) { fd = 0; goto restore_and_ret; } else /* Give up now. */ return -1; } else fd = 0; } case __GT_FILE: fd = open (tmpl, O_RDWR | O_CREAT | O_EXCL, mode); break; #if defined __UCLIBC_HAS_LFS__ case __GT_BIGFILE: fd = open64 (tmpl, O_RDWR | O_CREAT | O_EXCL, mode); break; #endif case __GT_DIR: fd = mkdir (tmpl, mode); break; default: fd = -1; assert (! "invalid KIND in __gen_tempname"); } if (fd >= 0) { restore_and_ret: __set_errno (save_errno); return fd; } else if (errno != EEXIST) /* Any other error will apply also to other names we might try, and there are 2^32 or so of them, so give up now. */ return -1; } /* We got out of the loop because we ran out of combinations to try. */ __set_errno (EEXIST); return -1; }