mktemp.c revision 1475:0c7070c5774f
1221465Snwhitehorn/*
2221465Snwhitehorn * CDDL HEADER START
3221465Snwhitehorn *
4221465Snwhitehorn * The contents of this file are subject to the terms of the
5221465Snwhitehorn * Common Development and Distribution License (the "License").
6221465Snwhitehorn * You may not use this file except in compliance with the License.
7221465Snwhitehorn *
8221465Snwhitehorn * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9221465Snwhitehorn * or http://www.opensolaris.org/os/licensing.
10221465Snwhitehorn * See the License for the specific language governing permissions
11221465Snwhitehorn * and limitations under the License.
12221465Snwhitehorn *
13221465Snwhitehorn * When distributing Covered Code, include this CDDL HEADER in each
14221465Snwhitehorn * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15221465Snwhitehorn * If applicable, add the following below this CDDL HEADER, with the
16221465Snwhitehorn * fields enclosed by brackets "[]" replaced with your own identifying
17221465Snwhitehorn * information: Portions Copyright [yyyy] [name of copyright owner]
18221465Snwhitehorn *
19221465Snwhitehorn * CDDL HEADER END
20221465Snwhitehorn */
21221465Snwhitehorn/*
22221465Snwhitehorn * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23221465Snwhitehorn * Use is subject to license terms.
24 */
25
26#pragma ident	"%Z%%M%	%I%	%E% SMI"
27
28/*	Copyright (c) 1988 AT&T	*/
29/*	  All Rights Reserved  	*/
30
31
32/*
33 *	mktemp() expects a string with up to six trailing 'X's.
34 *	These will be overlaid with letters, digits and symbols from
35 *	the portable filename character set.  If every combination thus
36 *	inserted leads to an existing file name, the string is shortened
37 *	to length zero and a pointer to a null string is returned.
38 *
39 *	The guarantee made by mktime() to the caller is that the
40 *	generated file name string will not match the string
41 *	produced by any other concurrent process using mktemp().
42 *	To guarantee uniqueness across the process-id space,
43 *	the process-id of the caller is encoded into the string.
44 *	To allow repeated calls within the same process to generate
45 *	different strings on each call, a sequence number is encoded
46 *	into the string along with process-id.
47 *
48 *	The encoding is performed using radix-64 (6 bits per character),
49 *	with 64 characters taken from the portable file name character set.
50 *	This allows the six X's to be a representation of a 36-bit integer
51 *	composed of bit fields:
52 *		( pid | seq )
53 *	where the process-id occupies the high-order bits and the sequence
54 *	number occupies the low-order bits.  The size of the pid field is
55 *	not fixed at the traditional 15 bits (MAXPID = 30000); the system
56 *	now allows a larger process-id space and MAXPID is obtained from
57 *	the system with a call to sysconf(_SC_MAXPID).
58 *
59 *	mktime() should fail if fewer than six X's are presented to it.
60 *	However, this has been traditionally accepted and is preserved
61 *	in the present code.  The consequence is that the 36-bit integer
62 *	is reduced to a (6*N)-bit integer, where N is the number of X's.
63 *	mktime() fails immediately if the resulting integer is not large
64 *	enough to contain MAXPID.
65 *
66 *	In an attempt to confuse and thwart hackers, the starting
67 *	sequence number is randomized using the current time.
68 */
69
70#define	XCNT  6
71#pragma weak mktemp = _mktemp
72
73#include "synonyms.h"
74#include "mtlib.h"
75#include <sys/types.h>
76#include <string.h>
77#include <unistd.h>
78#include <thread.h>
79#include <synch.h>
80#include <sys/stat.h>
81#include <errno.h>
82#include <sys/time.h>
83#include <stdlib.h>
84#include <stdio.h>
85#include <sys/param.h>
86
87/*
88 * 64-bit digits, must be from the POSIX "portable file name character set".
89 */
90static char
91chars[64] = {
92	'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
93	'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
94	'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
95	'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
96	'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '_',
97};
98
99/*
100 * Find highest one bit set.
101 * Returns bit number of highest bit that is set.
102 * Low order bit is number 0, high order bit is number 31.
103 */
104static int
105highbit(uint_t i)
106{
107	int h = 0;
108
109	if (i & 0xffff0000)
110		h += 16, i >>= 16;
111	if (i & 0xff00)
112		h += 8, i >>= 8;
113	if (i & 0xf0)
114		h += 4, i >>= 4;
115	if (i & 0xc)
116		h += 2, i >>= 2;
117	if (i & 0x2)
118		h += 1;
119	return (h);
120}
121
122char *
123libc_mktemps(char *as, int slen)
124{
125	/* statics are protected by this static mutex */
126	static mutex_t	mktemp_lock = DEFAULTMUTEX;
127	static int	pidshift = 0;
128	static int	previous_try = 0;
129	static pid_t	previous_pid = 0;
130	static int	previous_xcnt = XCNT;
131
132	pid_t		pid;
133	int		try;
134	int		tryshift;
135	int		max_try;
136	char		*s;
137	char		*first_x;
138	int		len;
139	uint_t		xcnt;
140	struct stat64	buf;
141
142	if (as == NULL || *as == '\0')	/* If the string passed is null then */
143		return (as);	/* a pointer to a null string is returned. */
144
145	lmutex_lock(&mktemp_lock);
146
147	pid = getpid();
148	if (pid != previous_pid) {	/* first time or first after fork() */
149		/*
150		 * Randomize the starting sequence number in
151		 * an attempt to confuse and thwart hackers.
152		 * Use the low 12 bits of the time in milliseconds.
153		 */
154		struct timeval tm;
155
156		(void) gettimeofday(&tm, NULL);
157		previous_try = (tm.tv_sec * 1000 + tm.tv_usec / 1000) & 0xfff;
158		previous_pid = pid;
159		previous_xcnt = XCNT;
160	}
161
162	/* for all possible values of pid, 0 <= pid < (1 << pidshift) */
163	if (pidshift == 0)	/* one-time initialization */
164		pidshift = highbit((uint_t)MAXPID) + 1;
165
166	/* count the X's */
167	xcnt = 0;
168	len = (int)strlen(as);
169	if (slen >= len || slen < 0)
170		goto fail;
171	len -= slen;
172	s = as + (len - 1);
173	while ((len != 0) && (xcnt < XCNT) && (*s == 'X')) {
174		xcnt++;
175		len--;
176		--s;
177	}
178	first_x = s + 1;	/* Remember pointer to the first X */
179
180	/* fail if we don't have enough X's to represent MAXPID */
181	if ((tryshift = xcnt * 6 - pidshift) < 0) {
182		/*
183		 * Some broken programs call mktemp() repeatedly,
184		 * passing the same string without reinserting the X's.
185		 * Check to see if this is such a call by testing
186		 * the trailing characters of the string for a
187		 * match with the process-id.
188		 */
189		uint64_t xpid = 0;		/* reconstructed pid */
190
191		s = as + len;
192		for (xcnt = previous_xcnt; xcnt && s > as; xcnt--) {
193			int c;
194			int i;
195
196			c = *--s;
197			for (i = 0; i < 64; i++)
198				if (c == chars[i])
199					break;
200			if (i == 64)
201				goto fail;
202			xpid = xpid * 64 + i;
203		}
204		xpid >>= (previous_xcnt * 6 - pidshift);
205		xpid &= ((1 << pidshift) - 1);
206
207		if (xpid == pid &&
208		    lstat64(as, &buf) == -1 && errno == ENOENT) {
209			lmutex_unlock(&mktemp_lock);
210			return (as);
211		}
212
213		goto fail;
214	}
215
216	/* we can try sequence numbers in the range 0 <= try < max_try */
217	max_try = 1 << tryshift;
218	if (previous_try >= max_try)
219		previous_try = 0;
220
221	try = previous_try;
222	for (;;) {
223		/* num is up to a 36-bit integer ... */
224		uint64_t num = ((uint64_t)pid << tryshift) + (uint64_t)try;
225		int i;
226
227		/* ... which we represent backwards in base 64 */
228		for (i = 0, s = first_x; i < xcnt; i++) {
229			*s++ = chars[num & 077];
230			num >>= 6;
231		}
232
233		if (lstat64(as, &buf) == -1) {
234			if (errno != ENOENT)
235				break;		/* unrecoverable error */
236			/* remember where we left off for the next call */
237			previous_try = try + 1;
238			previous_xcnt = xcnt;
239			lmutex_unlock(&mktemp_lock);
240			return (as);
241		}
242
243		if (++try == max_try)
244			try = 0;
245		if (try == previous_try)
246			break;
247	}
248
249fail:
250	lmutex_unlock(&mktemp_lock);
251	*as = '\0';
252	return (as);
253}
254
255char *
256mktemp(char *template)
257{
258	return (libc_mktemps(template, 0));
259}
260