10Sstevel@tonic-gate /*
20Sstevel@tonic-gate * CDDL HEADER START
30Sstevel@tonic-gate *
40Sstevel@tonic-gate * The contents of this file are subject to the terms of the
51475Scasper * Common Development and Distribution License (the "License").
61475Scasper * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate *
80Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate * See the License for the specific language governing permissions
110Sstevel@tonic-gate * and limitations under the License.
120Sstevel@tonic-gate *
130Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate *
190Sstevel@tonic-gate * CDDL HEADER END
200Sstevel@tonic-gate */
216812Sraf
220Sstevel@tonic-gate /*
23*13093SRoger.Faulkner@Oracle.COM * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
240Sstevel@tonic-gate */
250Sstevel@tonic-gate
260Sstevel@tonic-gate /* Copyright (c) 1988 AT&T */
270Sstevel@tonic-gate /* All Rights Reserved */
280Sstevel@tonic-gate
290Sstevel@tonic-gate /*
300Sstevel@tonic-gate * mktemp() expects a string with up to six trailing 'X's.
310Sstevel@tonic-gate * These will be overlaid with letters, digits and symbols from
320Sstevel@tonic-gate * the portable filename character set. If every combination thus
330Sstevel@tonic-gate * inserted leads to an existing file name, the string is shortened
340Sstevel@tonic-gate * to length zero and a pointer to a null string is returned.
350Sstevel@tonic-gate *
360Sstevel@tonic-gate * The guarantee made by mktime() to the caller is that the
370Sstevel@tonic-gate * generated file name string will not match the string
380Sstevel@tonic-gate * produced by any other concurrent process using mktemp().
390Sstevel@tonic-gate * To guarantee uniqueness across the process-id space,
400Sstevel@tonic-gate * the process-id of the caller is encoded into the string.
410Sstevel@tonic-gate * To allow repeated calls within the same process to generate
420Sstevel@tonic-gate * different strings on each call, a sequence number is encoded
430Sstevel@tonic-gate * into the string along with process-id.
440Sstevel@tonic-gate *
450Sstevel@tonic-gate * The encoding is performed using radix-64 (6 bits per character),
460Sstevel@tonic-gate * with 64 characters taken from the portable file name character set.
470Sstevel@tonic-gate * This allows the six X's to be a representation of a 36-bit integer
480Sstevel@tonic-gate * composed of bit fields:
490Sstevel@tonic-gate * ( pid | seq )
500Sstevel@tonic-gate * where the process-id occupies the high-order bits and the sequence
510Sstevel@tonic-gate * number occupies the low-order bits. The size of the pid field is
520Sstevel@tonic-gate * not fixed at the traditional 15 bits (MAXPID = 30000); the system
530Sstevel@tonic-gate * now allows a larger process-id space and MAXPID is obtained from
540Sstevel@tonic-gate * the system with a call to sysconf(_SC_MAXPID).
550Sstevel@tonic-gate *
560Sstevel@tonic-gate * mktime() should fail if fewer than six X's are presented to it.
570Sstevel@tonic-gate * However, this has been traditionally accepted and is preserved
580Sstevel@tonic-gate * in the present code. The consequence is that the 36-bit integer
590Sstevel@tonic-gate * is reduced to a (6*N)-bit integer, where N is the number of X's.
600Sstevel@tonic-gate * mktime() fails immediately if the resulting integer is not large
610Sstevel@tonic-gate * enough to contain MAXPID.
620Sstevel@tonic-gate *
630Sstevel@tonic-gate * In an attempt to confuse and thwart hackers, the starting
640Sstevel@tonic-gate * sequence number is randomized using the current time.
650Sstevel@tonic-gate */
660Sstevel@tonic-gate
676812Sraf #pragma weak _mktemp = mktemp
686812Sraf
690Sstevel@tonic-gate #define XCNT 6
700Sstevel@tonic-gate
716812Sraf #include "lint.h"
720Sstevel@tonic-gate #include "mtlib.h"
730Sstevel@tonic-gate #include <sys/types.h>
740Sstevel@tonic-gate #include <string.h>
750Sstevel@tonic-gate #include <unistd.h>
760Sstevel@tonic-gate #include <thread.h>
770Sstevel@tonic-gate #include <synch.h>
780Sstevel@tonic-gate #include <sys/stat.h>
790Sstevel@tonic-gate #include <errno.h>
800Sstevel@tonic-gate #include <sys/time.h>
810Sstevel@tonic-gate #include <stdlib.h>
820Sstevel@tonic-gate #include <stdio.h>
830Sstevel@tonic-gate #include <sys/param.h>
840Sstevel@tonic-gate
850Sstevel@tonic-gate /*
860Sstevel@tonic-gate * 64-bit digits, must be from the POSIX "portable file name character set".
870Sstevel@tonic-gate */
880Sstevel@tonic-gate static char
890Sstevel@tonic-gate chars[64] = {
900Sstevel@tonic-gate 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
910Sstevel@tonic-gate 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
920Sstevel@tonic-gate 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
930Sstevel@tonic-gate 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
940Sstevel@tonic-gate '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '.', '_',
950Sstevel@tonic-gate };
960Sstevel@tonic-gate
970Sstevel@tonic-gate char *
libc_mktemps(char * as,int slen)981475Scasper libc_mktemps(char *as, int slen)
990Sstevel@tonic-gate {
1000Sstevel@tonic-gate /* statics are protected by this static mutex */
1010Sstevel@tonic-gate static mutex_t mktemp_lock = DEFAULTMUTEX;
1020Sstevel@tonic-gate static int pidshift = 0;
1030Sstevel@tonic-gate static int previous_try = 0;
1040Sstevel@tonic-gate static pid_t previous_pid = 0;
1050Sstevel@tonic-gate static int previous_xcnt = XCNT;
1060Sstevel@tonic-gate
1070Sstevel@tonic-gate pid_t pid;
1080Sstevel@tonic-gate int try;
1090Sstevel@tonic-gate int tryshift;
1100Sstevel@tonic-gate int max_try;
1110Sstevel@tonic-gate char *s;
1120Sstevel@tonic-gate char *first_x;
1130Sstevel@tonic-gate int len;
1140Sstevel@tonic-gate uint_t xcnt;
1150Sstevel@tonic-gate struct stat64 buf;
1160Sstevel@tonic-gate
1170Sstevel@tonic-gate if (as == NULL || *as == '\0') /* If the string passed is null then */
1180Sstevel@tonic-gate return (as); /* a pointer to a null string is returned. */
1190Sstevel@tonic-gate
1200Sstevel@tonic-gate lmutex_lock(&mktemp_lock);
1210Sstevel@tonic-gate
1220Sstevel@tonic-gate pid = getpid();
1230Sstevel@tonic-gate if (pid != previous_pid) { /* first time or first after fork() */
1240Sstevel@tonic-gate /*
1250Sstevel@tonic-gate * Randomize the starting sequence number in
1260Sstevel@tonic-gate * an attempt to confuse and thwart hackers.
1270Sstevel@tonic-gate * Use the low 12 bits of the time in milliseconds.
1280Sstevel@tonic-gate */
1290Sstevel@tonic-gate struct timeval tm;
1300Sstevel@tonic-gate
1310Sstevel@tonic-gate (void) gettimeofday(&tm, NULL);
1320Sstevel@tonic-gate previous_try = (tm.tv_sec * 1000 + tm.tv_usec / 1000) & 0xfff;
1330Sstevel@tonic-gate previous_pid = pid;
1340Sstevel@tonic-gate previous_xcnt = XCNT;
1350Sstevel@tonic-gate }
1360Sstevel@tonic-gate
1370Sstevel@tonic-gate /* for all possible values of pid, 0 <= pid < (1 << pidshift) */
1380Sstevel@tonic-gate if (pidshift == 0) /* one-time initialization */
139*13093SRoger.Faulkner@Oracle.COM pidshift = fls((uint_t)MAXPID); /* high bit number */
1400Sstevel@tonic-gate
1410Sstevel@tonic-gate /* count the X's */
1420Sstevel@tonic-gate xcnt = 0;
1430Sstevel@tonic-gate len = (int)strlen(as);
1441475Scasper if (slen >= len || slen < 0)
1451475Scasper goto fail;
1461475Scasper len -= slen;
1470Sstevel@tonic-gate s = as + (len - 1);
1480Sstevel@tonic-gate while ((len != 0) && (xcnt < XCNT) && (*s == 'X')) {
1490Sstevel@tonic-gate xcnt++;
1500Sstevel@tonic-gate len--;
1510Sstevel@tonic-gate --s;
1520Sstevel@tonic-gate }
1530Sstevel@tonic-gate first_x = s + 1; /* Remember pointer to the first X */
1540Sstevel@tonic-gate
1550Sstevel@tonic-gate /* fail if we don't have enough X's to represent MAXPID */
1560Sstevel@tonic-gate if ((tryshift = xcnt * 6 - pidshift) < 0) {
1570Sstevel@tonic-gate /*
1580Sstevel@tonic-gate * Some broken programs call mktemp() repeatedly,
1590Sstevel@tonic-gate * passing the same string without reinserting the X's.
1600Sstevel@tonic-gate * Check to see if this is such a call by testing
1610Sstevel@tonic-gate * the trailing characters of the string for a
1620Sstevel@tonic-gate * match with the process-id.
1630Sstevel@tonic-gate */
1640Sstevel@tonic-gate uint64_t xpid = 0; /* reconstructed pid */
1650Sstevel@tonic-gate
1660Sstevel@tonic-gate s = as + len;
1670Sstevel@tonic-gate for (xcnt = previous_xcnt; xcnt && s > as; xcnt--) {
1680Sstevel@tonic-gate int c;
1690Sstevel@tonic-gate int i;
1700Sstevel@tonic-gate
1710Sstevel@tonic-gate c = *--s;
1720Sstevel@tonic-gate for (i = 0; i < 64; i++)
1730Sstevel@tonic-gate if (c == chars[i])
1740Sstevel@tonic-gate break;
1750Sstevel@tonic-gate if (i == 64)
1760Sstevel@tonic-gate goto fail;
1770Sstevel@tonic-gate xpid = xpid * 64 + i;
1780Sstevel@tonic-gate }
1790Sstevel@tonic-gate xpid >>= (previous_xcnt * 6 - pidshift);
1800Sstevel@tonic-gate xpid &= ((1 << pidshift) - 1);
1810Sstevel@tonic-gate
1820Sstevel@tonic-gate if (xpid == pid &&
1830Sstevel@tonic-gate lstat64(as, &buf) == -1 && errno == ENOENT) {
1840Sstevel@tonic-gate lmutex_unlock(&mktemp_lock);
1850Sstevel@tonic-gate return (as);
1860Sstevel@tonic-gate }
1870Sstevel@tonic-gate
1880Sstevel@tonic-gate goto fail;
1890Sstevel@tonic-gate }
1900Sstevel@tonic-gate
1910Sstevel@tonic-gate /* we can try sequence numbers in the range 0 <= try < max_try */
1920Sstevel@tonic-gate max_try = 1 << tryshift;
1930Sstevel@tonic-gate if (previous_try >= max_try)
1940Sstevel@tonic-gate previous_try = 0;
1950Sstevel@tonic-gate
1960Sstevel@tonic-gate try = previous_try;
1970Sstevel@tonic-gate for (;;) {
1980Sstevel@tonic-gate /* num is up to a 36-bit integer ... */
1990Sstevel@tonic-gate uint64_t num = ((uint64_t)pid << tryshift) + (uint64_t)try;
2000Sstevel@tonic-gate int i;
2010Sstevel@tonic-gate
2020Sstevel@tonic-gate /* ... which we represent backwards in base 64 */
2030Sstevel@tonic-gate for (i = 0, s = first_x; i < xcnt; i++) {
2040Sstevel@tonic-gate *s++ = chars[num & 077];
2050Sstevel@tonic-gate num >>= 6;
2060Sstevel@tonic-gate }
2070Sstevel@tonic-gate
2080Sstevel@tonic-gate if (lstat64(as, &buf) == -1) {
2090Sstevel@tonic-gate if (errno != ENOENT)
2100Sstevel@tonic-gate break; /* unrecoverable error */
2110Sstevel@tonic-gate /* remember where we left off for the next call */
2120Sstevel@tonic-gate previous_try = try + 1;
2130Sstevel@tonic-gate previous_xcnt = xcnt;
2140Sstevel@tonic-gate lmutex_unlock(&mktemp_lock);
2150Sstevel@tonic-gate return (as);
2160Sstevel@tonic-gate }
2170Sstevel@tonic-gate
2180Sstevel@tonic-gate if (++try == max_try)
2190Sstevel@tonic-gate try = 0;
2200Sstevel@tonic-gate if (try == previous_try)
2210Sstevel@tonic-gate break;
2220Sstevel@tonic-gate }
2230Sstevel@tonic-gate
2240Sstevel@tonic-gate fail:
2250Sstevel@tonic-gate lmutex_unlock(&mktemp_lock);
2260Sstevel@tonic-gate *as = '\0';
2270Sstevel@tonic-gate return (as);
2280Sstevel@tonic-gate }
2291475Scasper
2301475Scasper char *
mktemp(char * template)2311475Scasper mktemp(char *template)
2321475Scasper {
2331475Scasper return (libc_mktemps(template, 0));
2341475Scasper }
235