1/* tempname.c - generate the name of a temporary file.
2
3   Copyright (C) 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
4   2000, 2001, 2002, 2003, 2005 Free Software Foundation, Inc.
5
6   This program is free software; you can redistribute it and/or modify
7   it under the terms of the GNU General Public License as published by
8   the Free Software Foundation; either version 2, or (at your option)
9   any later version.
10
11   This program is distributed in the hope that it will be useful,
12   but WITHOUT ANY WARRANTY; without even the implied warranty of
13   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14   GNU General Public License for more details.
15
16   You should have received a copy of the GNU General Public License along
17   with this program; if not, write to the Free Software Foundation,
18   Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.  */
19#include <sys/cdefs.h>
20__RCSID("$NetBSD: tempname.c,v 1.2 2016/05/17 14:00:09 christos Exp $");
21
22
23#ifdef HAVE_CONFIG_H
24# include <config.h>
25#endif
26
27#include <sys/types.h>
28#include <assert.h>
29
30#include <errno.h>
31#ifndef __set_errno
32# define __set_errno(Val) errno = (Val)
33#endif
34
35#include <stdio.h>
36#ifndef P_tmpdir
37# define P_tmpdir "/tmp"
38#endif
39#ifndef TMP_MAX
40# define TMP_MAX 238328
41#endif
42#ifndef __GT_FILE
43# define __GT_FILE	0
44# define __GT_BIGFILE	1
45# define __GT_DIR	2
46# define __GT_NOCREATE	3
47#endif
48
49#include <stddef.h>
50#include <stdlib.h>
51#include <string.h>
52
53#include <fcntl.h>
54
55#if HAVE_SYS_TIME_H || _LIBC
56# include <sys/time.h>
57#endif
58
59#if HAVE_STDINT_H || _LIBC
60# include <stdint.h>
61#endif
62#if HAVE_INTTYPES_H
63# include <inttypes.h>
64#endif
65
66#if HAVE_UNISTD_H || _LIBC
67# include <unistd.h>
68#endif
69
70#include <sys/stat.h>
71
72#if _LIBC
73# define struct_stat64 struct stat64
74#else
75# include "stat-macros.h"
76# define struct_stat64 struct stat
77# define __getpid getpid
78# define __gettimeofday gettimeofday
79# define __mkdir mkdir
80# define __open open
81# define __open64 open
82# define __lxstat64(version, file, buf) lstat (file, buf)
83# define __xstat64(version, file, buf) stat (file, buf)
84#endif
85
86#if ! (HAVE___SECURE_GETENV || _LIBC)
87# define __secure_getenv getenv
88#endif
89
90#ifdef _LIBC
91# include <hp-timing.h>
92# if HP_TIMING_AVAIL
93#  define RANDOM_BITS(Var) \
94  if (__builtin_expect (value == UINT64_C (0), 0))			      \
95    {									      \
96      /* If this is the first time this function is used initialize	      \
97	 the variable we accumulate the value in to some somewhat	      \
98	 random value.  If we'd not do this programs at startup time	      \
99	 might have a reduced set of possible names, at least on slow	      \
100	 machines.  */							      \
101      struct timeval tv;						      \
102      __gettimeofday (&tv, NULL);					      \
103      value = ((uint64_t) tv.tv_usec << 16) ^ tv.tv_sec;		      \
104    }									      \
105  HP_TIMING_NOW (Var)
106# endif
107#endif
108
109/* Use the widest available unsigned type if uint64_t is not
110   available.  The algorithm below extracts a number less than 62**6
111   (approximately 2**35.725) from uint64_t, so ancient hosts where
112   uintmax_t is only 32 bits lose about 3.725 bits of randomness,
113   which is better than not having mkstemp at all.  */
114#if !defined UINT64_MAX && !defined uint64_t
115# define uint64_t uintmax_t
116#endif
117
118/* Return nonzero if DIR is an existent directory.  */
119static int
120direxists (const char *dir)
121{
122  struct_stat64 buf;
123  return __xstat64 (_STAT_VER, dir, &buf) == 0 && S_ISDIR (buf.st_mode);
124}
125
126/* Path search algorithm, for tmpnam, tmpfile, etc.  If DIR is
127   non-null and exists, uses it; otherwise uses the first of $TMPDIR,
128   P_tmpdir, /tmp that exists.  Copies into TMPL a template suitable
129   for use with mk[s]temp.  Will fail (-1) if DIR is non-null and
130   doesn't exist, none of the searched dirs exists, or there's not
131   enough space in TMPL. */
132int
133__path_search (char *tmpl, size_t tmpl_len, const char *dir, const char *pfx,
134	       int try_tmpdir)
135{
136  const char *d;
137  size_t dlen, plen;
138
139  if (!pfx || !pfx[0])
140    {
141      pfx = "file";
142      plen = 4;
143    }
144  else
145    {
146      plen = strlen (pfx);
147      if (plen > 5)
148	plen = 5;
149    }
150
151  if (try_tmpdir)
152    {
153      d = __secure_getenv ("TMPDIR");
154      if (d != NULL && direxists (d))
155	dir = d;
156      else if (dir != NULL && direxists (dir))
157	/* nothing */ ;
158      else
159	dir = NULL;
160    }
161  if (dir == NULL)
162    {
163      if (direxists (P_tmpdir))
164	dir = P_tmpdir;
165      else if (strcmp (P_tmpdir, "/tmp") != 0 && direxists ("/tmp"))
166	dir = "/tmp";
167      else
168	{
169	  __set_errno (ENOENT);
170	  return -1;
171	}
172    }
173
174  dlen = strlen (dir);
175  while (dlen > 1 && dir[dlen - 1] == '/')
176    dlen--;			/* remove trailing slashes */
177
178  /* check we have room for "${dir}/${pfx}XXXXXX\0" */
179  if (tmpl_len < dlen + 1 + plen + 6 + 1)
180    {
181      __set_errno (EINVAL);
182      return -1;
183    }
184
185  sprintf (tmpl, "%.*s/%.*sXXXXXX", (int) dlen, dir, (int) plen, pfx);
186  return 0;
187}
188
189/* These are the characters used in temporary file names.  */
190static const char letters[] =
191"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
192
193/* Generate a temporary file name based on TMPL.  TMPL must match the
194   rules for mk[s]temp (i.e. end in "XXXXXX").  The name constructed
195   does not exist at the time of the call to __gen_tempname.  TMPL is
196   overwritten with the result.
197
198   KIND may be one of:
199   __GT_NOCREATE:	simply verify that the name does not exist
200			at the time of the call.
201   __GT_FILE:		create the file using open(O_CREAT|O_EXCL)
202			and return a read-write fd.  The file is mode 0600.
203   __GT_BIGFILE:	same as __GT_FILE but use open64().
204   __GT_DIR:		create a directory, which will be mode 0700.
205
206   We use a clever algorithm to get hard-to-predict names. */
207int
208__gen_tempname (char *tmpl, int kind)
209{
210  int len;
211  char *XXXXXX;
212  static uint64_t value;
213  uint64_t random_time_bits;
214  unsigned int count;
215  int fd = -1;
216  int save_errno = errno;
217  struct_stat64 st;
218
219  /* A lower bound on the number of temporary files to attempt to
220     generate.  The maximum total number of temporary file names that
221     can exist for a given template is 62**6.  It should never be
222     necessary to try all these combinations.  Instead if a reasonable
223     number of names is tried (we define reasonable as 62**3) fail to
224     give the system administrator the chance to remove the problems.  */
225  unsigned int attempts_min = 62 * 62 * 62;
226
227  /* The number of times to attempt to generate a temporary file.  To
228     conform to POSIX, this must be no smaller than TMP_MAX.  */
229  unsigned int attempts = attempts_min < TMP_MAX ? TMP_MAX : attempts_min;
230
231  len = strlen (tmpl);
232  if (len < 6 || strcmp (&tmpl[len - 6], "XXXXXX"))
233    {
234      __set_errno (EINVAL);
235      return -1;
236    }
237
238  /* This is where the Xs start.  */
239  XXXXXX = &tmpl[len - 6];
240
241  /* Get some more or less random data.  */
242#ifdef RANDOM_BITS
243  RANDOM_BITS (random_time_bits);
244#else
245# if HAVE_GETTIMEOFDAY || _LIBC
246  {
247    struct timeval tv;
248    __gettimeofday (&tv, NULL);
249    random_time_bits = ((uint64_t) tv.tv_usec << 16) ^ tv.tv_sec;
250  }
251# else
252  random_time_bits = time (NULL);
253# endif
254#endif
255  value += random_time_bits ^ __getpid ();
256
257  for (count = 0; count < attempts; value += 7777, ++count)
258    {
259      uint64_t v = value;
260
261      /* Fill in the random bits.  */
262      XXXXXX[0] = letters[v % 62];
263      v /= 62;
264      XXXXXX[1] = letters[v % 62];
265      v /= 62;
266      XXXXXX[2] = letters[v % 62];
267      v /= 62;
268      XXXXXX[3] = letters[v % 62];
269      v /= 62;
270      XXXXXX[4] = letters[v % 62];
271      v /= 62;
272      XXXXXX[5] = letters[v % 62];
273
274      switch (kind)
275	{
276	case __GT_FILE:
277	  fd = __open (tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
278	  break;
279
280	case __GT_BIGFILE:
281	  fd = __open64 (tmpl, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
282	  break;
283
284	case __GT_DIR:
285	  fd = __mkdir (tmpl, S_IRUSR | S_IWUSR | S_IXUSR);
286	  break;
287
288	case __GT_NOCREATE:
289	  /* This case is backward from the other three.  __gen_tempname
290	     succeeds if __xstat fails because the name does not exist.
291	     Note the continue to bypass the common logic at the bottom
292	     of the loop.  */
293	  if (__lxstat64 (_STAT_VER, tmpl, &st) < 0)
294	    {
295	      if (errno == ENOENT)
296		{
297		  __set_errno (save_errno);
298		  return 0;
299		}
300	      else
301		/* Give up now. */
302		return -1;
303	    }
304	  continue;
305
306	default:
307	  assert (! "invalid KIND in __gen_tempname");
308	}
309
310      if (fd >= 0)
311	{
312	  __set_errno (save_errno);
313	  return fd;
314	}
315      else if (errno != EEXIST)
316	return -1;
317    }
318
319  /* We got out of the loop because we ran out of combinations to try.  */
320  __set_errno (EEXIST);
321  return -1;
322}
323