Back to index

tetex-bin  3.0
tempname.c
Go to the documentation of this file.
00001 /* tempname.c - generate the name of a temporary file.
00002 
00003    Copyright (C) 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
00004    2000, 2001, 2002, 2003 Free Software Foundation, Inc.
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2, or (at your option)
00009    any later version.
00010 
00011    This program is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014    GNU General Public License for more details.
00015 
00016    You should have received a copy of the GNU General Public License along
00017    with this program; if not, write to the Free Software Foundation,
00018    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
00019 
00020 #if HAVE_CONFIG_H
00021 # include <config.h>
00022 #endif
00023 
00024 #include <sys/types.h>
00025 #include <assert.h>
00026 
00027 #include <errno.h>
00028 #ifndef __set_errno
00029 # define __set_errno(Val) errno = (Val)
00030 #endif
00031 
00032 #include <stdio.h>
00033 #ifndef P_tmpdir
00034 # define P_tmpdir "/tmp"
00035 #endif
00036 #ifndef TMP_MAX
00037 # define TMP_MAX 238328
00038 #endif
00039 #ifndef __GT_FILE
00040 # define __GT_FILE   0
00041 # define __GT_BIGFILE       1
00042 # define __GT_DIR    2
00043 # define __GT_NOCREATE      3
00044 #endif
00045 
00046 #include <stddef.h>
00047 #include <stdlib.h>
00048 #include <string.h>
00049 
00050 #if HAVE_FCNTL_H || _LIBC
00051 # include <fcntl.h>
00052 #endif
00053 
00054 #if HAVE_SYS_TIME_H || _LIBC
00055 # include <sys/time.h>
00056 #endif
00057 
00058 #if HAVE_STDINT_H || _LIBC
00059 # include <stdint.h>
00060 #endif
00061 #if HAVE_INTTYPES_H
00062 # include <inttypes.h>
00063 #endif
00064 
00065 #if HAVE_UNISTD_H || _LIBC
00066 # include <unistd.h>
00067 #endif
00068 
00069 #include <sys/stat.h>
00070 #if STAT_MACROS_BROKEN
00071 # undef S_ISDIR
00072 #endif
00073 #if !defined S_ISDIR && defined S_IFDIR
00074 # define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
00075 #endif
00076 #if !S_IRUSR && S_IREAD
00077 # define S_IRUSR S_IREAD
00078 #endif
00079 #if !S_IRUSR
00080 # define S_IRUSR 00400
00081 #endif
00082 #if !S_IWUSR && S_IWRITE
00083 # define S_IWUSR S_IWRITE
00084 #endif
00085 #if !S_IWUSR
00086 # define S_IWUSR 00200
00087 #endif
00088 #if !S_IXUSR && S_IEXEC
00089 # define S_IXUSR S_IEXEC
00090 #endif
00091 #if !S_IXUSR
00092 # define S_IXUSR 00100
00093 #endif
00094 
00095 #if _LIBC
00096 # define struct_stat64 struct stat64
00097 #else
00098 # define struct_stat64 struct stat
00099 # define __getpid getpid
00100 # define __gettimeofday gettimeofday
00101 # define __mkdir mkdir
00102 # define __open open
00103 # define __open64 open
00104 # define __lxstat64(version, path, buf) lstat (path, buf)
00105 # define __xstat64(version, path, buf) stat (path, buf)
00106 #endif
00107 
00108 #if ! (HAVE___SECURE_GETENV || _LIBC)
00109 # define __secure_getenv getenv
00110 #endif
00111 
00112 #ifdef _LIBC
00113 # include <hp-timing.h>
00114 # if HP_TIMING_AVAIL
00115 #  define RANDOM_BITS(Var) \
00116   if (__builtin_expect (value == UINT64_C (0), 0))                          \
00117     {                                                                \
00118       /* If this is the first time this function is used initialize         \
00119         the variable we accumulate the value in to some somewhat            \
00120         random value.  If we'd not do this programs at startup time         \
00121         might have a reduced set of possible names, at least on slow        \
00122         machines.  */                                                       \
00123       struct timeval tv;                                             \
00124       __gettimeofday (&tv, NULL);                                    \
00125       value = ((uint64_t) tv.tv_usec << 16) ^ tv.tv_sec;                    \
00126     }                                                                \
00127   HP_TIMING_NOW (Var)
00128 # endif
00129 #endif
00130 
00131 /* Use the widest available unsigned type if uint64_t is not
00132    available.  The algorithm below extracts a number less than 62**6
00133    (approximately 2**35.725) from uint64_t, so ancient hosts where
00134    uintmax_t is only 32 bits lose about 3.725 bits of randomness,
00135    which is better than not having mkstemp at all.  */
00136 #if !defined UINT64_MAX && !defined uint64_t
00137 # define uint64_t uintmax_t
00138 #endif
00139 
00140 /* Return nonzero if DIR is an existent directory.  */
00141 static int
00142 direxists (const char *dir)
00143 {
00144   struct_stat64 buf;
00145   return __xstat64 (_STAT_VER, dir, &buf) == 0 && S_ISDIR (buf.st_mode);
00146 }
00147 
00148 /* Path search algorithm, for tmpnam, tmpfile, etc.  If DIR is
00149    non-null and exists, uses it; otherwise uses the first of $TMPDIR,
00150    P_tmpdir, /tmp that exists.  Copies into TMPL a template suitable
00151    for use with mk[s]temp.  Will fail (-1) if DIR is non-null and
00152    doesn't exist, none of the searched dirs exists, or there's not
00153    enough space in TMPL. */
00154 int
00155 __path_search (char *tmpl, size_t tmpl_len, const char *dir, const char *pfx,
00156               int try_tmpdir)
00157 {
00158   const char *d;
00159   size_t dlen, plen;
00160 
00161   if (!pfx || !pfx[0])
00162     {
00163       pfx = "file";
00164       plen = 4;
00165     }
00166   else
00167     {
00168       plen = strlen (pfx);
00169       if (plen > 5)
00170        plen = 5;
00171     }
00172 
00173   if (try_tmpdir)
00174     {
00175       d = __secure_getenv ("TMPDIR");
00176       if (d != NULL && direxists (d))
00177        dir = d;
00178       else if (dir != NULL && direxists (dir))
00179        /* nothing */ ;
00180       else
00181        dir = NULL;
00182     }
00183   if (dir == NULL)
00184     {
00185       if (direxists (P_tmpdir))
00186        dir = P_tmpdir;
00187       else if (strcmp (P_tmpdir, "/tmp") != 0 && direxists ("/tmp"))
00188        dir = "/tmp";
00189       else
00190        {
00191          __set_errno (ENOENT);
00192          return -1;
00193        }
00194     }
00195 
00196   dlen = strlen (dir);
00197   while (dlen > 1 && dir[dlen - 1] == '/')
00198     dlen--;                 /* remove trailing slashes */
00199 
00200   /* check we have room for "${dir}/${pfx}XXXXXX\0" */
00201   if (tmpl_len < dlen + 1 + plen + 6 + 1)
00202     {
00203       __set_errno (EINVAL);
00204       return -1;
00205     }
00206 
00207   sprintf (tmpl, "%.*s/%.*sXXXXXX", (int) dlen, dir, (int) plen, pfx);
00208   return 0;
00209 }
00210 
00211 /* These are the characters used in temporary filenames.  */
00212 static const char letters[] =
00213 "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
00214 
00215 /* Generate a temporary file name based on TMPL.  TMPL must match the
00216    rules for mk[s]temp (i.e. end in "XXXXXX").  The name constructed
00217    does not exist at the time of the call to __gen_tempname.  TMPL is
00218    overwritten with the result.
00219 
00220    KIND may be one of:
00221    __GT_NOCREATE:    simply verify that the name does not exist
00222                      at the time of the call.
00223    __GT_FILE:        create the file using open(O_CREAT|O_EXCL)
00224                      and return a read-write fd.  The file is mode 0600.
00225    __GT_BIGFILE:     same as __GT_FILE but use open64().
00226    __GT_DIR:         create a directory, which will be mode 0700.
00227 
00228    We use a clever algorithm to get hard-to-predict names. */
00229 int
00230 __gen_tempname (char *tmpl, int kind)
00231 {
00232   int len;
00233   char *XXXXXX;
00234   static uint64_t value;
00235   uint64_t random_time_bits;
00236   unsigned int count;
00237   int fd = -1;
00238   int save_errno = errno;
00239   struct_stat64 st;
00240 
00241   /* A lower bound on the number of temporary files to attempt to
00242      generate.  The maximum total number of temporary file names that
00243      can exist for a given template is 62**6.  It should never be
00244      necessary to try all these combinations.  Instead if a reasonable
00245      number of names is tried (we define reasonable as 62**3) fail to
00246      give the system administrator the chance to remove the problems.  */
00247   unsigned int attempts_min = 62 * 62 * 62;
00248 
00249   /* The number of times to attempt to generate a temporary file.  To
00250      conform to POSIX, this must be no smaller than TMP_MAX.  */
00251   unsigned int attempts = attempts_min < TMP_MAX ? TMP_MAX : attempts_min;
00252 
00253   len = strlen (tmpl);
00254   if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX"))
00255     {
00256       __set_errno (EINVAL);
00257       return -1;
00258     }
00259 
00260   /* This is where the Xs start.  */
00261   XXXXXX = &tmpl[len - 6];
00262 
00263   /* Get some more or less random data.  */
00264 #ifdef RANDOM_BITS
00265   RANDOM_BITS (random_time_bits);
00266 #else
00267 # if HAVE_GETTIMEOFDAY || _LIBC
00268   {
00269     struct timeval tv;
00270     __gettimeofday (&tv, NULL);
00271     random_time_bits = ((uint64_t) tv.tv_usec << 16) ^ tv.tv_sec;
00272   }
00273 # else
00274   random_time_bits = time (NULL);
00275 # endif
00276 #endif
00277   value += random_time_bits ^ __getpid ();
00278 
00279   for (count = 0; count < attempts; value += 7777, ++count)
00280     {
00281       uint64_t v = value;
00282 
00283       /* Fill in the random bits.  */
00284       XXXXXX[0] = letters[v % 62];
00285       v /= 62;
00286       XXXXXX[1] = letters[v % 62];
00287       v /= 62;
00288       XXXXXX[2] = letters[v % 62];
00289       v /= 62;
00290       XXXXXX[3] = letters[v % 62];
00291       v /= 62;
00292       XXXXXX[4] = letters[v % 62];
00293       v /= 62;
00294       XXXXXX[5] = letters[v % 62];
00295 
00296       switch (kind)
00297        {
00298        case __GT_FILE:
00299          fd = __open (tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
00300          break;
00301 
00302        case __GT_BIGFILE:
00303          fd = __open64 (tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
00304          break;
00305 
00306        case __GT_DIR:
00307          fd = __mkdir (tmpl, S_IRUSR | S_IWUSR | S_IXUSR);
00308          break;
00309 
00310        case __GT_NOCREATE:
00311          /* This case is backward from the other three.  __gen_tempname
00312             succeeds if __xstat fails because the name does not exist.
00313             Note the continue to bypass the common logic at the bottom
00314             of the loop.  */
00315          if (__lxstat64 (_STAT_VER, tmpl, &st) < 0)
00316            {
00317              if (errno == ENOENT)
00318               {
00319                 __set_errno (save_errno);
00320                 return 0;
00321               }
00322              else
00323               /* Give up now. */
00324               return -1;
00325            }
00326          continue;
00327 
00328        default:
00329          assert (! "invalid KIND in __gen_tempname");
00330        }
00331 
00332       if (fd >= 0)
00333        {
00334          __set_errno (save_errno);
00335          return fd;
00336        }
00337       else if (errno != EEXIST)
00338        return -1;
00339     }
00340 
00341   /* We got out of the loop because we ran out of combinations to try.  */
00342   __set_errno (EEXIST);
00343   return -1;
00344 }