1/*	$NetBSD: gettemp.c,v 1.22 2024/01/20 14:52:49 christos Exp $	*/
2
3/*
4 * Copyright (c) 1987, 1993
5 *	The Regents of the University of California.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. Neither the name of the University nor the names of its contributors
16 *    may be used to endorse or promote products derived from this software
17 *    without specific prior written permission.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29 * SUCH DAMAGE.
30 */
31
32#include "gettemp.h"
33
34#if !HAVE_NBTOOL_CONFIG_H || !HAVE_MKSTEMP || !HAVE_MKDTEMP
35
36#include <sys/cdefs.h>
37#if defined(LIBC_SCCS) && !defined(lint)
38#if 0
39static char sccsid[] = "@(#)mktemp.c	8.1 (Berkeley) 6/4/93";
40#else
41__RCSID("$NetBSD: gettemp.c,v 1.22 2024/01/20 14:52:49 christos Exp $");
42#endif
43#endif /* LIBC_SCCS and not lint */
44
45#include <sys/param.h>
46#include <sys/stat.h>
47#include <fcntl.h>
48#include <string.h>
49
50static const unsigned char padchar[] =
51"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
52
53int
54GETTEMP(char *path, int *doopen, int domkdir, int slen, int oflags)
55{
56	char *start, *trv, *suffp, *carryp;
57	const char *pad;
58	struct stat sbuf;
59	int rval;
60	uint32_t r;
61	char carrybuf[MAXPATHLEN];
62
63	_DIAGASSERT(path != NULL);
64	/* doopen may be NULL */
65	if ((doopen != NULL && domkdir) || slen < 0 ||
66	    (oflags & ~(O_APPEND | O_DIRECT | O_SHLOCK | O_EXLOCK | O_SYNC |
67	    O_CLOEXEC)) != 0) {
68		errno = EINVAL;
69		return 0;
70	}
71
72	for (trv = path; *trv != '\0'; ++trv)
73		continue;
74
75	if (trv - path >= MAXPATHLEN) {
76		errno = ENAMETOOLONG;
77		return 0;
78	}
79	trv -= slen;
80	suffp = trv;
81	--trv;
82	if (trv < path || NULL != strchr(suffp, '/')) {
83		errno = EINVAL;
84		return 0;
85	}
86
87	/* Fill space with random characters */
88	while (trv >= path && *trv == 'X') {
89		r = arc4random_uniform((unsigned int)(sizeof(padchar) - 1));
90		*trv-- = padchar[r];
91	}
92	start = trv + 1;
93
94	/* save first combination of random characters */
95	memcpy(carrybuf, start, (size_t)(suffp - start));
96
97	/*
98	 * check the target directory.
99	 */
100	if (doopen != NULL || domkdir) {
101		for (; trv > path; --trv) {
102			if (*trv == '/') {
103				*trv = '\0';
104				rval = stat(path, &sbuf);
105				*trv = '/';
106				if (rval != 0)
107					return 0;
108				if (!S_ISDIR(sbuf.st_mode)) {
109					errno = ENOTDIR;
110					return 0;
111				}
112				break;
113			}
114		}
115	}
116
117	for (;;) {
118		if (doopen) {
119			if ((*doopen = open(path, O_CREAT|O_EXCL|O_RDWR|oflags,
120			    0600)) != -1)
121				return 1;
122			if (errno != EEXIST)
123				return 0;
124		} else if (domkdir) {
125			if (mkdir(path, 0700) != -1)
126				return 1;
127			if (errno != EEXIST)
128				return 0;
129		} else if (lstat(path, &sbuf))
130			return errno == ENOENT;
131
132		/*
133		 * If we have a collision,
134		 * cycle through the space of filenames
135		 */
136		for (trv = start, carryp = carrybuf;;) {
137			/* have we tried all possible permutations? */
138			if (trv == suffp)
139				return 0; /* yes - exit with EEXIST */
140			pad = strchr((const char *)padchar, *trv);
141			if (pad == NULL) {
142				/* this should never happen */
143				errno = EIO;
144				return 0;
145			}
146			/* increment character */
147			*trv = (*++pad == '\0') ? padchar[0] : *pad;
148			/* carry to next position? */
149			if (*trv == *carryp) {
150				/* increment position and loop */
151				++trv;
152				++carryp;
153			} else {
154				/* try with new name */
155				break;
156			}
157		}
158	}
159	/*NOTREACHED*/
160}
161
162#endif /* !HAVE_NBTOOL_CONFIG_H || !HAVE_MKSTEMP || !HAVE_MKDTEMP */
163