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