-/* Copyright (C) 1991, 92, 93, 94, 95, 96, 97 Free Software Foundation, Inc.
+/* Copyright (C) 1991-2016 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.
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2.1 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.
+ Lesser 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. */
+ You should have received a copy of the GNU Lesser General Public
+ License along with the GNU C Library; if not, see
+ <http://www.gnu.org/licenses/>. */
+
+#if !_LIBC
+# include <config.h>
+# include "tempname.h"
+#endif
+
+#include <sys/types.h>
+#include <assert.h>
#include <errno.h>
-#include <stddef.h>
+#ifndef __set_errno
+# define __set_errno(Val) errno = (Val)
+#endif
+
#include <stdio.h>
+#ifndef P_tmpdir
+# define P_tmpdir "/tmp"
+#endif
+#ifndef TMP_MAX
+# define TMP_MAX 238328
+#endif
+#ifndef __GT_FILE
+# define __GT_FILE 0
+# define __GT_DIR 1
+# define __GT_NOCREATE 2
+#endif
+#if !_LIBC && (GT_FILE != __GT_FILE || GT_DIR != __GT_DIR \
+ || GT_NOCREATE != __GT_NOCREATE)
+# error report this to bug-gnulib@gnu.org
+#endif
+
+#include <stddef.h>
#include <stdlib.h>
#include <string.h>
-#include <sys/types.h>
-#include <sys/stat.h>
+
#include <fcntl.h>
+#include <sys/time.h>
+#include <stdint.h>
#include <unistd.h>
-#ifdef USE_IN_LIBIO
-# include "libioP.h"
-# include <libio.h>
+#include <sys/stat.h>
+
+#if _LIBC
+# define struct_stat64 struct stat64
+# define __secure_getenv __libc_secure_getenv
+#else
+# define struct_stat64 struct stat
+# define __gen_tempname gen_tempname
+# define __getpid getpid
+# define __gettimeofday gettimeofday
+# define __mkdir mkdir
+# define __open open
+# define __lxstat64(version, file, buf) lstat (file, buf)
+# define __secure_getenv secure_getenv
+#endif
+
+#ifdef _LIBC
+# include <hp-timing.h>
+# if HP_TIMING_AVAIL
+# define RANDOM_BITS(Var) \
+ if (__glibc_unlikely (value == UINT64_C (0))) \
+ { \
+ /* If this is the first time this function is used initialize \
+ the variable we accumulate the value in to some somewhat \
+ random value. If we'd not do this programs at startup time \
+ might have a reduced set of possible names, at least on slow \
+ machines. */ \
+ struct timeval tv; \
+ __gettimeofday (&tv, NULL); \
+ value = ((uint64_t) tv.tv_usec << 16) ^ tv.tv_sec; \
+ } \
+ HP_TIMING_NOW (Var)
+# endif
#endif
+/* Use the widest available unsigned type if uint64_t is not
+ available. The algorithm below extracts a number less than 62**6
+ (approximately 2**35.725) from uint64_t, so ancient hosts where
+ uintmax_t is only 32 bits lose about 3.725 bits of randomness,
+ which is better than not having mkstemp at all. */
+#if !defined UINT64_MAX && !defined uint64_t
+# define uint64_t uintmax_t
+#endif
+
+#if _LIBC
/* Return nonzero if DIR is an existent directory. */
static int
-diraccess (const char *dir)
+direxists (const char *dir)
{
- struct stat buf;
- return __stat (dir, &buf) == 0 && S_ISDIR (buf.st_mode);
+ struct_stat64 buf;
+ return __xstat64 (_STAT_VER, dir, &buf) == 0 && S_ISDIR (buf.st_mode);
}
-/* Return nonzero if FILE exists. */
-static int
-exists (const char *file)
+/* 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)
{
- /* We can stat the file even if we can't read its data. */
- struct stat st;
- int save = errno;
- if (__stat (file, &st) == 0)
- return 1;
+ const char *d;
+ size_t dlen, plen;
+
+ if (!pfx || !pfx[0])
+ {
+ pfx = "file";
+ plen = 4;
+ }
else
{
- /* We report that the file exists if stat failed for a reason other
- than nonexistence. In this case, it may or may not exist, and we
- don't know; but reporting that it does exist will never cause any
- trouble, while reporting that it doesn't exist when it does would
- violate the interface of __stdio_gen_tempname. */
- int exists = errno != ENOENT;
- __set_errno (save);
- return exists;
+ plen = strlen (pfx);
+ if (plen > 5)
+ plen = 5;
}
-}
-
-/* These are the characters used in temporary filenames. */
-static const char letters[] =
- "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
-
-/* Generate a temporary filename and return it (in a static buffer). If
- STREAMPTR is not NULL, open a stream "w+b" on the file and set
- *STREAMPTR to it. If DIR_SEARCH is nonzero, DIR and PFX are used as
- described for tempnam. If not, a temporary filename in P_tmpdir with no
- special prefix is generated. If LENPTR is not NULL, *LENPTR is set the
- to length (including the terminating '\0') of the resultant filename,
- which is returned. This goes through a cyclic pattern of all possible
- filenames consisting of five decimal digits of the current pid and three
- of the characters in `letters'. Data for tempnam and tmpnam is kept
- separate, but when tempnam is using P_tmpdir and no prefix (i.e, it is
- identical to tmpnam), the same data is used. Each potential filename is
- tested for an already-existing file of the same name, and no name of an
- existing file will be returned. When the cycle reaches its end
- (12345ZZZ), NULL is returned. */
-char *
-__stdio_gen_tempname (char *buf, size_t bufsize, const char *dir,
- const char *pfx, int dir_search, size_t *lenptr,
- FILE **streamptr)
-{
- int saverrno = errno;
- static const char tmpdir[] = P_tmpdir;
- static size_t indices[2];
- size_t *idx;
-#if 0
- static pid_t oldpid = (pid_t) 0;
-#endif
- pid_t pid = __getpid();
- register size_t len, plen, dlen;
- int wrapped;
-
- if (dir_search)
+ if (try_tmpdir)
+ {
+ d = __secure_getenv ("TMPDIR");
+ if (d != NULL && direxists (d))
+ dir = d;
+ else if (dir != NULL && direxists (dir))
+ /* nothing */ ;
+ else
+ dir = NULL;
+ }
+ if (dir == NULL)
{
- register const char *d = __secure_getenv ("TMPDIR");
- if (d != NULL && !diraccess (d))
- d = NULL;
- if (d == NULL && dir != NULL && diraccess (dir))
- d = dir;
- if (d == NULL && diraccess (tmpdir))
- d = tmpdir;
- if (d == NULL && diraccess ("/tmp"))
- d = "/tmp";
- if (d == NULL)
+ if (direxists (P_tmpdir))
+ dir = P_tmpdir;
+ else if (strcmp (P_tmpdir, "/tmp") != 0 && direxists ("/tmp"))
+ dir = "/tmp";
+ else
{
__set_errno (ENOENT);
- return NULL;
+ return -1;
}
- dir = d;
}
- else
- dir = tmpdir;
dlen = strlen (dir);
-
- /* Remove trailing slashes from the directory name. */
while (dlen > 1 && dir[dlen - 1] == '/')
- --dlen;
+ dlen--; /* remove trailing slashes */
- if (pfx != NULL && *pfx != '\0')
+ /* check we have room for "${dir}/${pfx}XXXXXX\0" */
+ if (tmpl_len < dlen + 1 + plen + 6 + 1)
{
- plen = strlen (pfx);
- if (plen > 5)
- plen = 5;
+ __set_errno (EINVAL);
+ return -1;
}
- else
- plen = 0;
- if (dir != tmpdir && !strcmp (dir, tmpdir))
- dir = tmpdir;
- idx = &indices[(plen == 0 && dir == tmpdir) ? 1 : 0];
+ sprintf (tmpl, "%.*s/%.*sXXXXXX", (int) dlen, dir, (int) plen, pfx);
+ return 0;
+}
+#endif /* _LIBC */
-#if 0
- /* XXX Is this ever useful??? At least when using a thread package
- which uses different PIDs for the threads it is not helpful. */
- if (pid != oldpid)
- {
- oldpid = pid;
- indices[0] = indices[1] = 0;
- }
-#endif
+/* These are the characters used in temporary file names. */
+static const char letters[] =
+"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
- wrapped = 0; /* We have not yet wrapped around the index counter. */
- len = dlen + 1 + plen + 5 + 3;
- while (1)
- {
- size_t i;
+/* Generate a temporary file name based on TMPL. TMPL must match the
+ rules for mk[s]temp (i.e. end in "XXXXXX", possibly with a suffix).
+ The name constructed does not exist at the time of the call to
+ __gen_tempname. TMPL is overwritten with the result.
- if (*idx >= ((sizeof (letters) - 1) * (sizeof (letters) - 1) *
- (sizeof (letters) - 1)))
- {
- if (wrapped)
- /* We really wrapped around this call. Can't believe it
- but nevertheless stop the endless loop. */
- break;
+ KIND may be one of:
+ __GT_NOCREATE: simply verify that the name does not exist
+ at the time of the call.
+ __GT_FILE: create the file using open(O_CREAT|O_EXCL)
+ and return a read-write fd. The file is mode 0600.
+ __GT_DIR: create a directory, which will be mode 0700.
- indices[0] = indices[1] = 0;
- wrapped = 1;
- }
+ We use a clever algorithm to get hard-to-predict names. */
+int
+__gen_tempname (char *tmpl, int suffixlen, int flags, int kind)
+{
+ int len;
+ char *XXXXXX;
+ static uint64_t value;
+ uint64_t random_time_bits;
+ unsigned int count;
+ int fd = -1;
+ int save_errno = errno;
+ struct_stat64 st;
- i = (*idx)++;
+ /* A lower bound on the number of temporary files to attempt to
+ generate. The maximum total number of temporary file names that
+ can exist for a given template is 62**6. It should never be
+ necessary to try all of these combinations. Instead if a reasonable
+ number of names is tried (we define reasonable as 62**3) fail to
+ give the system administrator the chance to remove the problems. */
+#define ATTEMPTS_MIN (62 * 62 * 62)
- /* Construct a file name and see if it already exists.
+ /* The number of times to attempt to generate a temporary file. To
+ conform to POSIX, this must be no smaller than TMP_MAX. */
+#if ATTEMPTS_MIN < TMP_MAX
+ unsigned int attempts = TMP_MAX;
+#else
+ unsigned int attempts = ATTEMPTS_MIN;
+#endif
- We use a single counter in *IDX to cycle each of three
- character positions through each of 62 possible letters. */
+ len = strlen (tmpl);
+ if (len < 6 + suffixlen || memcmp (&tmpl[len - 6 - suffixlen], "XXXXXX", 6))
+ {
+ __set_errno (EINVAL);
+ return -1;
+ }
- if (__snprintf (buf, bufsize, "%.*s/%.*s%.5d%c%c%c",
- (int) dlen, dir, (int) plen,
- pfx, pid % 100000,
- letters[i % (sizeof (letters) - 1)],
- letters[(i / (sizeof (letters) - 1))
- % (sizeof (letters) - 1)],
- letters[(i / ((sizeof (letters) - 1) *
- (sizeof (letters) - 1)))
- % (sizeof (letters) - 1)]
- ) != (int) len)
- return NULL;
+ /* This is where the Xs start. */
+ XXXXXX = &tmpl[len - 6 - suffixlen];
- if (streamptr != NULL)
- {
- /* Try to create the file atomically. */
- int fd = __open (buf, O_RDWR|O_CREAT|O_EXCL, 0666);
- if (fd >= 0)
- {
- /* We got a new file that did not previously exist.
- Create a stream for it. */
-#ifdef USE_IN_LIBIO
- int save;
- struct locked_FILE
- {
- struct _IO_FILE_plus fp;
-#ifdef _IO_MTSAFE_IO
- _IO_lock_t lock;
+ /* Get some more or less random data. */
+#ifdef RANDOM_BITS
+ RANDOM_BITS (random_time_bits);
+#else
+ {
+ struct timeval tv;
+ __gettimeofday (&tv, NULL);
+ random_time_bits = ((uint64_t) tv.tv_usec << 16) ^ tv.tv_sec;
+ }
#endif
- } *new_f;
- struct _IO_FILE_plus *fp;
+ value += random_time_bits ^ __getpid ();
- new_f = (struct locked_FILE *)
- malloc (sizeof (struct locked_FILE));
- if (new_f == NULL)
- {
- /* We lost trying to create a stream (out of memory?).
- Nothing to do but remove the file, close the descriptor,
- and return failure. */
- save = errno;
- lose:
- (void) remove (buf);
- (void) __close (fd);
- __set_errno (save);
- return NULL;
- }
- fp = &new_f->fp;
-#ifdef _IO_MTSAFE_IO
- fp->file._lock = &new_f->lock;
-#endif
- _IO_init (&fp->file, 0);
- _IO_JUMPS (&fp->file) = &_IO_file_jumps;
- _IO_file_init (&fp->file);
-# if !_IO_UNIFIED_JUMPTABLES
- fp->vtable = NULL;
-# endif
- if (_IO_file_attach (&fp->file, fd) == NULL)
- {
- save = errno;
- free (fp);
- goto lose;
- }
- fp->file._flags &= ~_IO_DELETE_DONT_CLOSE;
+ for (count = 0; count < attempts; value += 7777, ++count)
+ {
+ uint64_t v = value;
- *streamptr = (FILE *) fp;
-#else
- *streamptr = __newstream ();
- if (*streamptr == NULL)
+ /* Fill in the random bits. */
+ XXXXXX[0] = letters[v % 62];
+ v /= 62;
+ XXXXXX[1] = letters[v % 62];
+ v /= 62;
+ XXXXXX[2] = letters[v % 62];
+ v /= 62;
+ XXXXXX[3] = letters[v % 62];
+ v /= 62;
+ XXXXXX[4] = letters[v % 62];
+ v /= 62;
+ XXXXXX[5] = letters[v % 62];
+
+ switch (kind)
+ {
+ case __GT_FILE:
+ fd = __open (tmpl,
+ (flags & ~O_ACCMODE)
+ | O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
+ break;
+
+ case __GT_DIR:
+ fd = __mkdir (tmpl, S_IRUSR | S_IWUSR | S_IXUSR);
+ break;
+
+ case __GT_NOCREATE:
+ /* This case is backward from the other three. __gen_tempname
+ succeeds if __xstat fails because the name does not exist.
+ Note the continue to bypass the common logic at the bottom
+ of the loop. */
+ if (__lxstat64 (_STAT_VER, tmpl, &st) < 0)
+ {
+ if (errno == ENOENT)
{
- /* We lost trying to create a stream (out of memory?).
- Nothing to do but remove the file, close the descriptor,
- and return failure. */
- const int save = errno;
- (void) remove (buf);
- (void) __close (fd);
- __set_errno (save);
- return NULL;
+ __set_errno (save_errno);
+ return 0;
}
- (*streamptr)->__cookie = (__ptr_t) (long int) fd;
- (*streamptr)->__mode.__write = 1;
- (*streamptr)->__mode.__read = 1;
- (*streamptr)->__mode.__binary = 1;
-#endif
+ else
+ /* Give up now. */
+ return -1;
}
-#if defined EMFILE || defined ENFILE || defined EINTR
- else if (0
-# ifdef EMFILE
- || errno == EMFILE
-# endif
-# ifdef ENFILE
- || errno == ENFILE
-# endif
-# ifdef EINTR
- || errno == EINTR
-# endif
- )
- /* We cannot open anymore files since all descriptors are
- used or because we got a signal. */
- return NULL;
-#endif
- else
- continue;
- }
- else if (exists (buf))
- continue;
-
- /* If the file already existed we have continued the loop above,
- so we only get here when we have a winning name to return. */
+ continue;
- __set_errno (saverrno);
+ default:
+ assert (! "invalid KIND in __gen_tempname");
+ abort ();
+ }
- if (lenptr != NULL)
- *lenptr = len + 1;
- return buf;
+ if (fd >= 0)
+ {
+ __set_errno (save_errno);
+ return fd;
+ }
+ else if (errno != EEXIST)
+ return -1;
}
/* We got out of the loop because we ran out of combinations to try. */
- __set_errno (EEXIST); /* ? */
- return NULL;
+ __set_errno (EEXIST);
+ return -1;
}