Blame lib/tempname.c

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