1 /*
2 * Copyright (c) 1987 Regents of the University of California.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms are permitted
6 * provided that: (1) source distributions retain this entire copyright
7 * notice and comment, and (2) distributions including binaries display
8 * the following acknowledgement: ``This product includes software
9 * developed by the University of California, Berkeley and its contributors''
10 * in the documentation or other materials provided with the distribution.
11 * Neither the name of the University nor the names of its
12 * contributors may be used to endorse or promote products derived
13 * from this software without specific prior written permission.
14 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
17 */
18 /* This is file MKTEMP.C */
19 /* This file may have been modified by DJ Delorie (Jan 1991). If so,
20 ** these modifications are Copyright (C) 1991 DJ Delorie.
21 */
22
23 /*
24 FUNCTION
25 <<mktemp>>, <<mkstemp>>, <<mkostemp>>, <<mkstemps>>,
26 <<mkostemps>>---generate unused file name
27 <<mkdtemp>>---generate unused directory
28
29 INDEX
30 mktemp
31 INDEX
32 mkdtemp
33 INDEX
34 mkstemp
35 INDEX
36 mkstemps
37 INDEX
38 mkostemp
39 INDEX
40 mkostemps
41 INDEX
42 _mktemp_r
43 INDEX
44 _mkdtemp_r
45 INDEX
46 _mkstemp_r
47 INDEX
48 _mkstemps_r
49 INDEX
50 _mkostemp_r
51 INDEX
52 _mkostemps_r
53
54 SYNOPSIS
55 #include <stdlib.h>
56 char *mktemp(char *<[path]>);
57 char *mkdtemp(char *<[path]>);
58 int mkstemp(char *<[path]>);
59 int mkstemps(char *<[path]>, int <[suffixlen]>);
60 int mkostemp(char *<[path]>, int <[flags]>);
61 int mkostemps(char *<[path]>, int <[suffixlen]>, int <[flags]>);
62
63 char *mktemp( char *<[path]>);
64 char *mkdtemp( char *<[path]>);
65 int *mkstemp( char *<[path]>);
66 int *mkstemps( char *<[path]>, int <[len]>);
67 int *mkostemp( char *<[path]>,
68 int <[flags]>);
69 int *mkostemps( char *<[path]>, int <[len]>,
70 int <[flags]>);
71
72 DESCRIPTION
73 <<mktemp>>, <<mkstemp>>, and <<mkstemps>> attempt to generate a file name
74 that is not yet in use for any existing file. <<mkstemp>> and <<mkstemps>>
75 create the file and open it for reading and writing; <<mktemp>> simply
76 generates the file name (making <<mktemp>> a security risk). <<mkostemp>>
77 and <<mkostemps>> allow the addition of other <<open>> flags, such
78 as <<O_CLOEXEC>>, <<O_APPEND>>, or <<O_SYNC>>. On platforms with a
79 separate text mode, <<mkstemp>> forces <<O_BINARY>>, while <<mkostemp>>
80 allows the choice between <<O_BINARY>>, <<O_TEXT>>, or 0 for default.
81 <<mkdtemp>> attempts to create a directory instead of a file, with a
82 permissions mask of 0700.
83
84 You supply a simple pattern for the generated file name, as the string
85 at <[path]>. The pattern should be a valid filename (including path
86 information if you wish) ending with at least six `<<X>>'
87 characters. The generated filename will match the leading part of the
88 name you supply, with the trailing `<<X>>' characters replaced by some
89 combination of digits and letters. With <<mkstemps>>, the `<<X>>'
90 characters end <[suffixlen]> bytes before the end of the string.
91
92 The alternate functions <<_mktemp_r>>, <<_mkdtemp_r>>, <<_mkstemp_r>>,
93 <<_mkostemp_r>>, <<_mkostemps_r>>, and <<_mkstemps_r>> are reentrant
94 versions. The extra argument <[reent]> is a pointer to a reentrancy
95 structure.
96
97 RETURNS
98 <<mktemp>> returns the pointer <[path]> to the modified string
99 representing an unused filename, unless it could not generate one, or
100 the pattern you provided is not suitable for a filename; in that case,
101 it returns <<NULL>>. Be aware that there is an inherent race between
102 generating the name and attempting to create a file by that name;
103 you are advised to use <<O_EXCL|O_CREAT>>.
104
105 <<mkdtemp>> returns the pointer <[path]> to the modified string if the
106 directory was created, otherwise it returns <<NULL>>.
107
108 <<mkstemp>>, <<mkstemps>>, <<mkostemp>>, and <<mkostemps>> return a file
109 descriptor to the newly created file, unless it could not generate an
110 unused filename, or the pattern you provided is not suitable for a
111 filename; in that case, it returns <<-1>>.
112
113 NOTES
114 Never use <<mktemp>>. The generated filenames are easy to guess and
115 there's a race between the test if the file exists and the creation
116 of the file. In combination this makes <<mktemp>> prone to attacks
117 and using it is a security risk. Whenever possible use <<mkstemp>>
118 instead. It doesn't suffer the race condition.
119
120 PORTABILITY
121 ANSI C does not require either <<mktemp>> or <<mkstemp>>; the System
122 V Interface Definition requires <<mktemp>> as of Issue 2. POSIX 2001
123 requires <<mkstemp>>, and POSIX 2008 requires <<mkdtemp>> while
124 deprecating <<mktemp>>. <<mkstemps>>, <<mkostemp>>, and <<mkostemps>>
125 are not standardized.
126
127 Supporting OS subroutines required: <<getpid>>, <<mkdir>>, <<open>>, <<stat>>.
128 */
129
130 #define _DEFAULT_SOURCE
131 #include <_ansi.h>
132 #include <stdlib.h>
133 #include <sys/types.h>
134 #include <fcntl.h>
135 #include <sys/stat.h>
136 #include <errno.h>
137 #include <stdio.h>
138 #include <ctype.h>
139 #include <unistd.h>
140
141 static int
_gettemp(char * path,register int * doopen,int domkdir,size_t suffixlen,int flags)142 _gettemp (
143 char *path,
144 register int *doopen,
145 int domkdir,
146 size_t suffixlen,
147 int flags)
148 {
149 register char *start, *trv;
150 char *end;
151 #ifdef __USE_INTERNAL_STAT64
152 struct stat64 sbuf;
153 #else
154 struct stat sbuf;
155 #endif
156 unsigned int pid;
157
158 pid = getpid ();
159 for (trv = path; *trv; ++trv) /* extra X's get set to 0's */
160 continue;
161 if (trv - path < (ptrdiff_t) suffixlen)
162 {
163 _REENT_ERRNO(ptr) = EINVAL;
164 return 0;
165 }
166 trv -= suffixlen;
167 end = trv;
168 while (path < trv && *--trv == 'X')
169 {
170 *trv = (pid % 10) + '0';
171 pid /= 10;
172 }
173 if (end - trv < 6)
174 {
175 _REENT_ERRNO(ptr) = EINVAL;
176 return 0;
177 }
178
179 /*
180 * Check the target directory; if you have six X's and it
181 * doesn't exist this runs for a *very* long time.
182 */
183
184 for (start = trv + 1;; --trv)
185 {
186 if (trv <= path)
187 break;
188 if (*trv == '/')
189 {
190 *trv = '\0';
191 #ifdef __USE_INTERNAL_STAT64
192 if (stat64 (path, &sbuf))
193 #else
194 if (stat (path, &sbuf))
195 #endif
196 return (0);
197 if (!(sbuf.st_mode & S_IFDIR))
198 {
199 _REENT_ERRNO(ptr) = ENOTDIR;
200 return (0);
201 }
202 *trv = '/';
203 break;
204 }
205 }
206
207 for (;;)
208 {
209 #if !defined _ELIX_LEVEL || _ELIX_LEVEL >= 4
210 if (domkdir)
211 {
212 #ifdef HAVE_MKDIR
213 if (mkdir ( path, 0700) == 0)
214 return 1;
215 if (_REENT_ERRNO(ptr) != EEXIST)
216 return 0;
217 #else /* !HAVE_MKDIR */
218 _REENT_ERRNO(ptr) = ENOSYS;
219 return 0;
220 #endif /* !HAVE_MKDIR */
221 }
222 else
223 #endif /* _ELIX_LEVEL */
224 if (doopen)
225 {
226 if ((*doopen = open (path, O_CREAT | O_EXCL | O_RDWR | flags,
227 0600)) >= 0)
228 return 1;
229 if (_REENT_ERRNO(ptr) != EEXIST)
230 return 0;
231 }
232 #ifdef __USE_INTERNAL_STAT64
233 else if (stat64 (path, &sbuf))
234 #else
235 else if (stat (path, &sbuf))
236 #endif
237 return (_REENT_ERRNO(ptr) == ENOENT ? 1 : 0);
238
239 /* tricky little algorithm for backward compatibility */
240 for (trv = start;;)
241 {
242 if (trv == end)
243 return 0;
244 if (*trv == 'z')
245 *trv++ = 'a';
246 else
247 {
248 /* Safe, since it only encounters 7-bit characters. */
249 if (isdigit ((unsigned char) *trv))
250 *trv = 'a';
251 else
252 ++ * trv;
253 break;
254 }
255 }
256 }
257 /*NOTREACHED*/
258 }
259
260 #ifndef O_BINARY
261 # define O_BINARY 0
262 #endif
263
264 int
mkstemp(char * path)265 mkstemp (
266 char *path)
267 {
268 int fd;
269
270 return (_gettemp (path, &fd, 0, 0, O_BINARY) ? fd : -1);
271 }
272
273 #if !defined _ELIX_LEVEL || _ELIX_LEVEL >= 4
274 char *
mkdtemp(char * path)275 mkdtemp (
276 char *path)
277 {
278 return (_gettemp (path, (int *) NULL, 1, 0, 0) ? path : NULL);
279 }
280
281 int
mkstemps(char * path,int len)282 mkstemps (
283 char *path,
284 int len)
285 {
286 int fd;
287
288 return (_gettemp (path, &fd, 0, len, O_BINARY) ? fd : -1);
289 }
290
291 int
mkostemp(char * path,int flags)292 mkostemp (
293 char *path,
294 int flags)
295 {
296 int fd;
297
298 return (_gettemp (path, &fd, 0, 0, flags & ~O_ACCMODE) ? fd : -1);
299 }
300
301 int
mkostemps(char * path,int len,int flags)302 mkostemps (
303 char *path,
304 int len,
305 int flags)
306 {
307 int fd;
308
309 return (_gettemp (path, &fd, 0, len, flags & ~O_ACCMODE) ? fd : -1);
310 }
311 #endif /* _ELIX_LEVEL */
312
313 char *
mktemp(char * path)314 mktemp (
315 char *path)
316 {
317 return (_gettemp (path, (int *) NULL, 0, 0, 0) ? path : (char *) NULL);
318 }
319