Blame lib/tempname.c

Packit 33f14e
/* tempname.c - generate the name of a temporary file.
Packit 33f14e
Packit 33f14e
   Copyright (C) 1991-2003, 2005-2007, 2009-2017 Free Software Foundation, Inc.
Packit 33f14e
Packit 33f14e
   This program is free software: you can redistribute it and/or modify
Packit 33f14e
   it under the terms of the GNU General Public License as published by
Packit 33f14e
   the Free Software Foundation; either version 3 of the License, or
Packit 33f14e
   (at your option) any later version.
Packit 33f14e
Packit 33f14e
   This program is distributed in the hope that it will be useful,
Packit 33f14e
   but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 33f14e
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
Packit 33f14e
   GNU General Public License for more details.
Packit 33f14e
Packit 33f14e
   You should have received a copy of the GNU General Public License
Packit 33f14e
   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
Packit 33f14e
Packit 33f14e
/* Extracted from glibc sysdeps/posix/tempname.c.  See also tmpdir.c.  */
Packit 33f14e
Packit 33f14e
#if !_LIBC
Packit 33f14e
# include <config.h>
Packit 33f14e
# include "tempname.h"
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
#include <sys/types.h>
Packit 33f14e
#include <assert.h>
Packit 33f14e
Packit 33f14e
#include <errno.h>
Packit 33f14e
#ifndef __set_errno
Packit 33f14e
# define __set_errno(Val) errno = (Val)
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
#include <stdio.h>
Packit 33f14e
#ifndef P_tmpdir
Packit 33f14e
# define P_tmpdir "/tmp"
Packit 33f14e
#endif
Packit 33f14e
#ifndef TMP_MAX
Packit 33f14e
# define TMP_MAX 238328
Packit 33f14e
#endif
Packit 33f14e
#ifndef __GT_FILE
Packit 33f14e
# define __GT_FILE      0
Packit 33f14e
# define __GT_DIR       1
Packit 33f14e
# define __GT_NOCREATE  2
Packit 33f14e
#endif
Packit 33f14e
#if !_LIBC && (GT_FILE != __GT_FILE || GT_DIR != __GT_DIR       \
Packit 33f14e
               || GT_NOCREATE != __GT_NOCREATE)
Packit 33f14e
# error report this to bug-gnulib@gnu.org
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
#include <stddef.h>
Packit 33f14e
#include <stdlib.h>
Packit 33f14e
#include <string.h>
Packit 33f14e
Packit 33f14e
#include <fcntl.h>
Packit 33f14e
#include <sys/time.h>
Packit 33f14e
#include <stdint.h>
Packit 33f14e
#include <unistd.h>
Packit 33f14e
Packit 33f14e
#include <sys/stat.h>
Packit 33f14e
Packit 33f14e
#if _LIBC
Packit 33f14e
# define struct_stat64 struct stat64
Packit 33f14e
#else
Packit 33f14e
# define struct_stat64 struct stat
Packit 33f14e
# define __try_tempname try_tempname
Packit 33f14e
# define __gen_tempname gen_tempname
Packit 33f14e
# define __getpid getpid
Packit 33f14e
# define __gettimeofday gettimeofday
Packit 33f14e
# define __mkdir mkdir
Packit 33f14e
# define __open open
Packit 33f14e
# define __lxstat64(version, file, buf) lstat (file, buf)
Packit 33f14e
# define __secure_getenv secure_getenv
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
#ifdef _LIBC
Packit 33f14e
# include <hp-timing.h>
Packit 33f14e
# if HP_TIMING_AVAIL
Packit 33f14e
#  define RANDOM_BITS(Var) \
Packit 33f14e
  if (__builtin_expect (value == UINT64_C (0), 0))                            \
Packit 33f14e
    {                                                                         \
Packit 33f14e
      /* If this is the first time this function is used initialize           \
Packit 33f14e
         the variable we accumulate the value in to some somewhat             \
Packit 33f14e
         random value.  If we'd not do this programs at startup time          \
Packit 33f14e
         might have a reduced set of possible names, at least on slow         \
Packit 33f14e
         machines.  */                                                        \
Packit 33f14e
      struct timeval tv;                                                      \
Packit 33f14e
      __gettimeofday (&tv, NULL);                                             \
Packit 33f14e
      value = ((uint64_t) tv.tv_usec << 16) ^ tv.tv_sec;                      \
Packit 33f14e
    }                                                                         \
Packit 33f14e
  HP_TIMING_NOW (Var)
Packit 33f14e
# endif
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
/* Use the widest available unsigned type if uint64_t is not
Packit 33f14e
   available.  The algorithm below extracts a number less than 62**6
Packit 33f14e
   (approximately 2**35.725) from uint64_t, so ancient hosts where
Packit 33f14e
   uintmax_t is only 32 bits lose about 3.725 bits of randomness,
Packit 33f14e
   which is better than not having mkstemp at all.  */
Packit 33f14e
#if !defined UINT64_MAX && !defined uint64_t
Packit 33f14e
# define uint64_t uintmax_t
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
#if _LIBC
Packit 33f14e
/* Return nonzero if DIR is an existent directory.  */
Packit 33f14e
static int
Packit 33f14e
direxists (const char *dir)
Packit 33f14e
{
Packit 33f14e
  struct_stat64 buf;
Packit 33f14e
  return __xstat64 (_STAT_VER, dir, &buf) == 0 && S_ISDIR (buf.st_mode);
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
/* Path search algorithm, for tmpnam, tmpfile, etc.  If DIR is
Packit 33f14e
   non-null and exists, uses it; otherwise uses the first of $TMPDIR,
Packit 33f14e
   P_tmpdir, /tmp that exists.  Copies into TMPL a template suitable
Packit 33f14e
   for use with mk[s]temp.  Will fail (-1) if DIR is non-null and
Packit 33f14e
   doesn't exist, none of the searched dirs exists, or there's not
Packit 33f14e
   enough space in TMPL. */
Packit 33f14e
int
Packit 33f14e
__path_search (char *tmpl, size_t tmpl_len, const char *dir, const char *pfx,
Packit 33f14e
               int try_tmpdir)
Packit 33f14e
{
Packit 33f14e
  const char *d;
Packit 33f14e
  size_t dlen, plen;
Packit 33f14e
Packit 33f14e
  if (!pfx || !pfx[0])
Packit 33f14e
    {
Packit 33f14e
      pfx = "file";
Packit 33f14e
      plen = 4;
Packit 33f14e
    }
Packit 33f14e
  else
Packit 33f14e
    {
Packit 33f14e
      plen = strlen (pfx);
Packit 33f14e
      if (plen > 5)
Packit 33f14e
        plen = 5;
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
  if (try_tmpdir)
Packit 33f14e
    {
Packit 33f14e
      d = __secure_getenv ("TMPDIR");
Packit 33f14e
      if (d != NULL && direxists (d))
Packit 33f14e
        dir = d;
Packit 33f14e
      else if (dir != NULL && direxists (dir))
Packit 33f14e
        /* nothing */ ;
Packit 33f14e
      else
Packit 33f14e
        dir = NULL;
Packit 33f14e
    }
Packit 33f14e
  if (dir == NULL)
Packit 33f14e
    {
Packit 33f14e
      if (direxists (P_tmpdir))
Packit 33f14e
        dir = P_tmpdir;
Packit 33f14e
      else if (strcmp (P_tmpdir, "/tmp") != 0 && direxists ("/tmp"))
Packit 33f14e
        dir = "/tmp";
Packit 33f14e
      else
Packit 33f14e
        {
Packit 33f14e
          __set_errno (ENOENT);
Packit 33f14e
          return -1;
Packit 33f14e
        }
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
  dlen = strlen (dir);
Packit 33f14e
  while (dlen > 1 && dir[dlen - 1] == '/')
Packit 33f14e
    dlen--;                     /* remove trailing slashes */
Packit 33f14e
Packit 33f14e
  /* check we have room for "${dir}/${pfx}XXXXXX\0" */
Packit 33f14e
  if (tmpl_len < dlen + 1 + plen + 6 + 1)
Packit 33f14e
    {
Packit 33f14e
      __set_errno (EINVAL);
Packit 33f14e
      return -1;
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
  sprintf (tmpl, "%.*s/%.*sXXXXXX", (int) dlen, dir, (int) plen, pfx);
Packit 33f14e
  return 0;
Packit 33f14e
}
Packit 33f14e
#endif /* _LIBC */
Packit 33f14e
Packit 33f14e
/* These are the characters used in temporary file names.  */
Packit 33f14e
static const char letters[] =
Packit 33f14e
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
Packit 33f14e
Packit 33f14e
int
Packit 33f14e
__try_tempname (char *tmpl, int suffixlen, void *args,
Packit 33f14e
                int (*tryfunc) (char *, void *))
Packit 33f14e
{
Packit 33f14e
  int len;
Packit 33f14e
  char *XXXXXX;
Packit 33f14e
  static uint64_t value;
Packit 33f14e
  uint64_t random_time_bits;
Packit 33f14e
  unsigned int count;
Packit 33f14e
  int fd = -1;
Packit 33f14e
  int save_errno = errno;
Packit 33f14e
Packit 33f14e
  /* A lower bound on the number of temporary files to attempt to
Packit 33f14e
     generate.  The maximum total number of temporary file names that
Packit 33f14e
     can exist for a given template is 62**6.  It should never be
Packit 33f14e
     necessary to try all of these combinations.  Instead if a reasonable
Packit 33f14e
     number of names is tried (we define reasonable as 62**3) fail to
Packit 33f14e
     give the system administrator the chance to remove the problems.  */
Packit 33f14e
#define ATTEMPTS_MIN (62 * 62 * 62)
Packit 33f14e
Packit 33f14e
  /* The number of times to attempt to generate a temporary file.  To
Packit 33f14e
     conform to POSIX, this must be no smaller than TMP_MAX.  */
Packit 33f14e
#if ATTEMPTS_MIN < TMP_MAX
Packit 33f14e
  unsigned int attempts = TMP_MAX;
Packit 33f14e
#else
Packit 33f14e
  unsigned int attempts = ATTEMPTS_MIN;
Packit 33f14e
#endif
Packit 33f14e
Packit 33f14e
  len = strlen (tmpl);
Packit 33f14e
  if (len < 6 + suffixlen || memcmp (&tmpl[len - 6 - suffixlen], "XXXXXX", 6))
Packit 33f14e
    {
Packit 33f14e
      __set_errno (EINVAL);
Packit 33f14e
      return -1;
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
  /* This is where the Xs start.  */
Packit 33f14e
  XXXXXX = &tmpl[len - 6 - suffixlen];
Packit 33f14e
Packit 33f14e
  /* Get some more or less random data.  */
Packit 33f14e
#ifdef RANDOM_BITS
Packit 33f14e
  RANDOM_BITS (random_time_bits);
Packit 33f14e
#else
Packit 33f14e
  {
Packit 33f14e
    struct timeval tv;
Packit 33f14e
    __gettimeofday (&tv, NULL);
Packit 33f14e
    random_time_bits = ((uint64_t) tv.tv_usec << 16) ^ tv.tv_sec;
Packit 33f14e
  }
Packit 33f14e
#endif
Packit 33f14e
  value += random_time_bits ^ __getpid ();
Packit 33f14e
Packit 33f14e
  for (count = 0; count < attempts; value += 7777, ++count)
Packit 33f14e
    {
Packit 33f14e
      uint64_t v = value;
Packit 33f14e
Packit 33f14e
      /* Fill in the random bits.  */
Packit 33f14e
      XXXXXX[0] = letters[v % 62];
Packit 33f14e
      v /= 62;
Packit 33f14e
      XXXXXX[1] = letters[v % 62];
Packit 33f14e
      v /= 62;
Packit 33f14e
      XXXXXX[2] = letters[v % 62];
Packit 33f14e
      v /= 62;
Packit 33f14e
      XXXXXX[3] = letters[v % 62];
Packit 33f14e
      v /= 62;
Packit 33f14e
      XXXXXX[4] = letters[v % 62];
Packit 33f14e
      v /= 62;
Packit 33f14e
      XXXXXX[5] = letters[v % 62];
Packit 33f14e
Packit 33f14e
      fd = tryfunc (tmpl, args);
Packit 33f14e
      if (fd >= 0)
Packit 33f14e
        {
Packit 33f14e
          __set_errno (save_errno);
Packit 33f14e
          return fd;
Packit 33f14e
        }
Packit 33f14e
      else if (errno != EEXIST)
Packit 33f14e
        return -1;
Packit 33f14e
    }
Packit 33f14e
Packit 33f14e
  /* We got out of the loop because we ran out of combinations to try.  */
Packit 33f14e
  __set_errno (EEXIST);
Packit 33f14e
  return -1;
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
static int
Packit 33f14e
try_file (char *tmpl, void *flags)
Packit 33f14e
{
Packit 33f14e
  int *openflags = flags;
Packit 33f14e
  return __open (tmpl,
Packit 33f14e
                 (*openflags & ~O_ACCMODE)
Packit 33f14e
                 | O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
static int
Packit 33f14e
try_dir (char *tmpl, void *flags _GL_UNUSED)
Packit 33f14e
{
Packit 33f14e
  return __mkdir (tmpl, S_IRUSR | S_IWUSR | S_IXUSR);
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
static int
Packit 33f14e
try_nocreate (char *tmpl, void *flags _GL_UNUSED)
Packit 33f14e
{
Packit 33f14e
  struct_stat64 st;
Packit 33f14e
Packit 33f14e
  if (__lxstat64 (_STAT_VER, tmpl, &st) == 0)
Packit 33f14e
    __set_errno (EEXIST);
Packit 33f14e
  return errno == ENOENT ? 0 : -1;
Packit 33f14e
}
Packit 33f14e
Packit 33f14e
/* Generate a temporary file name based on TMPL.  TMPL must match the
Packit 33f14e
   rules for mk[s]temp (i.e. end in "XXXXXX", possibly with a suffix).
Packit 33f14e
   The name constructed does not exist at the time of the call to
Packit 33f14e
   __gen_tempname.  TMPL is overwritten with the result.
Packit 33f14e
Packit 33f14e
   KIND may be one of:
Packit 33f14e
   __GT_NOCREATE:       simply verify that the name does not exist
Packit 33f14e
                        at the time of the call.
Packit 33f14e
   __GT_FILE:           create the file using open(O_CREAT|O_EXCL)
Packit 33f14e
                        and return a read-write fd.  The file is mode 0600.
Packit 33f14e
   __GT_DIR:            create a directory, which will be mode 0700.
Packit 33f14e
Packit 33f14e
   We use a clever algorithm to get hard-to-predict names. */
Packit 33f14e
int
Packit 33f14e
__gen_tempname (char *tmpl, int suffixlen, int flags, int kind)
Packit 33f14e
{
Packit 33f14e
  int (*tryfunc) (char *, void *);
Packit 33f14e
Packit 33f14e
  switch (kind)
Packit 33f14e
    {
Packit 33f14e
    case __GT_FILE:
Packit 33f14e
      tryfunc = try_file;
Packit 33f14e
      break;
Packit 33f14e
Packit 33f14e
    case __GT_DIR:
Packit 33f14e
      tryfunc = try_dir;
Packit 33f14e
      break;
Packit 33f14e
Packit 33f14e
    case __GT_NOCREATE:
Packit 33f14e
      tryfunc = try_nocreate;
Packit 33f14e
      break;
Packit 33f14e
Packit 33f14e
    default:
Packit 33f14e
      assert (! "invalid KIND in __gen_tempname");
Packit 33f14e
      abort ();
Packit 33f14e
    }
Packit 33f14e
  return __try_tempname (tmpl, suffixlen, &flags, tryfunc);
Packit 33f14e
}