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