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