1 /* THIS FILE HAS BEEN MODIFIED FROM THE ORIGINAL OPENBSD SOURCE */
2 /* Changes: Removed mktemp */
3 
4 /*	$OpenBSD: mktemp.c,v 1.19 2005/08/08 08:05:36 espie Exp $ */
5 /*
6  * Copyright (c) 1987, 1993
7  *	The Regents of the University of California.  All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted provided that the following conditions
11  * are met:
12  * 1. Redistributions of source code must retain the above copyright
13  *    notice, this list of conditions and the following disclaimer.
14  * 2. Redistributions in binary form must reproduce the above copyright
15  *    notice, this list of conditions and the following disclaimer in the
16  *    documentation and/or other materials provided with the distribution.
17  * 3. Neither the name of the University nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 /* OPENBSD ORIGINAL: lib/libc/stdio/mktemp.c */
35 
36 #include "includes.h"
37 
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 
41 #include <fcntl.h>
42 #include <ctype.h>
43 #include <errno.h>
44 #include <unistd.h>
45 
46 #if !defined(HAVE_MKDTEMP) || defined(HAVE_STRICT_MKSTEMP)
47 
48 static int _gettemp(char *, int *, int, int);
49 
50 int
51 mkstemps(char *path, int slen)
52 {
53 	int fd;
54 
55 	return (_gettemp(path, &fd, 0, slen) ? fd : -1);
56 }
57 
58 int
59 mkstemp(char *path)
60 {
61 	int fd;
62 
63 	return (_gettemp(path, &fd, 0, 0) ? fd : -1);
64 }
65 
66 char *
67 mkdtemp(char *path)
68 {
69 	return(_gettemp(path, (int *)NULL, 1, 0) ? path : (char *)NULL);
70 }
71 
72 static int
73 _gettemp(path, doopen, domkdir, slen)
74 	char *path;
75 	register int *doopen;
76 	int domkdir;
77 	int slen;
78 {
79 	register char *start, *trv, *suffp;
80 	struct stat sbuf;
81 	int rval;
82 	pid_t pid;
83 
84 	if (doopen && domkdir) {
85 		errno = EINVAL;
86 		return(0);
87 	}
88 
89 	for (trv = path; *trv; ++trv)
90 		;
91 	trv -= slen;
92 	suffp = trv;
93 	--trv;
94 	if (trv < path) {
95 		errno = EINVAL;
96 		return (0);
97 	}
98 	pid = getpid();
99 	while (trv >= path && *trv == 'X' && pid != 0) {
100 		*trv-- = (pid % 10) + '0';
101 		pid /= 10;
102 	}
103 	while (trv >= path && *trv == 'X') {
104 		char c;
105 
106 		pid = (arc4random() & 0xffff) % (26+26);
107 		if (pid < 26)
108 			c = pid + 'A';
109 		else
110 			c = (pid - 26) + 'a';
111 		*trv-- = c;
112 	}
113 	start = trv + 1;
114 
115 	/*
116 	 * check the target directory; if you have six X's and it
117 	 * doesn't exist this runs for a *very* long time.
118 	 */
119 	if (doopen || domkdir) {
120 		for (;; --trv) {
121 			if (trv <= path)
122 				break;
123 			if (*trv == '/') {
124 				*trv = '\0';
125 				rval = stat(path, &sbuf);
126 				*trv = '/';
127 				if (rval != 0)
128 					return(0);
129 				if (!S_ISDIR(sbuf.st_mode)) {
130 					errno = ENOTDIR;
131 					return(0);
132 				}
133 				break;
134 			}
135 		}
136 	}
137 
138 	for (;;) {
139 		if (doopen) {
140 			if ((*doopen =
141 			    open(path, O_CREAT|O_EXCL|O_RDWR, 0600)) >= 0)
142 				return(1);
143 			if (errno != EEXIST)
144 				return(0);
145 		} else if (domkdir) {
146 			if (mkdir(path, 0700) == 0)
147 				return(1);
148 			if (errno != EEXIST)
149 				return(0);
150 		} else if (lstat(path, &sbuf))
151 			return(errno == ENOENT ? 1 : 0);
152 
153 		/* tricky little algorithm for backward compatibility */
154 		for (trv = start;;) {
155 			if (!*trv)
156 				return (0);
157 			if (*trv == 'Z') {
158 				if (trv == suffp)
159 					return (0);
160 				*trv++ = 'a';
161 			} else {
162 				if (isdigit(*trv))
163 					*trv = 'a';
164 				else if (*trv == 'z')	/* inc from z to A */
165 					*trv = 'A';
166 				else {
167 					if (trv == suffp)
168 						return (0);
169 					++*trv;
170 				}
171 				break;
172 			}
173 		}
174 	}
175 	/*NOTREACHED*/
176 }
177 
178 #endif /* !defined(HAVE_MKDTEMP) || defined(HAVE_STRICT_MKSTEMP) */
179