| /* 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, | 
 |    write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | 
 |    Boston, MA 02111-1307, USA.  */ | 
 |  | 
 | /* 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 attribute_hidden ___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; | 
 | } |