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 _GNU_SOURCE
131 #include <stdlib.h>
132 #include <sys/types.h>
133 #include <fcntl.h>
134 #include <sys/stat.h>
135 #include <errno.h>
136 #include <stdio.h>
137 #include <ctype.h>
138 #include <unistd.h>
139 
140 static int
_gettemp(char * path,register int * doopen,int domkdir,size_t suffixlen,int flags)141 _gettemp (
142        char *path,
143        register int *doopen,
144        int domkdir,
145        size_t suffixlen,
146        int flags)
147 {
148   register char *start, *trv;
149   char *end;
150 #ifdef __USE_INTERNAL_STAT64
151   struct stat64 sbuf;
152 #else
153   struct stat sbuf;
154 #endif
155   unsigned int pid;
156 
157   pid = getpid ();
158   for (trv = path; *trv; ++trv)		/* extra X's get set to 0's */
159     continue;
160   if (trv - path < (ptrdiff_t) suffixlen)
161     {
162       _REENT_ERRNO(ptr) = EINVAL;
163       return 0;
164     }
165   trv -= suffixlen;
166   end = trv;
167   while (path < trv && *--trv == 'X')
168     {
169       *trv = (pid % 10) + '0';
170       pid /= 10;
171     }
172   if (end - trv < 6)
173     {
174       _REENT_ERRNO(ptr) = EINVAL;
175       return 0;
176     }
177 
178   /*
179    * Check the target directory; if you have six X's and it
180    * doesn't exist this runs for a *very* long time.
181    */
182 
183   for (start = trv + 1;; --trv)
184     {
185       if (trv <= path)
186 	break;
187       if (*trv == '/')
188 	{
189 	  *trv = '\0';
190 #ifdef __USE_INTERNAL_STAT64
191 	  if (stat64 (path, &sbuf))
192 #else
193 	  if (stat (path, &sbuf))
194 #endif
195 	    return (0);
196 	  if (!(sbuf.st_mode & S_IFDIR))
197 	    {
198 	      _REENT_ERRNO(ptr) = ENOTDIR;
199 	      return (0);
200 	    }
201 	  *trv = '/';
202 	  break;
203 	}
204     }
205 
206   for (;;)
207     {
208 #if !defined _ELIX_LEVEL || _ELIX_LEVEL >= 4
209       if (domkdir)
210 	{
211 #ifdef HAVE_MKDIR
212 	  if (mkdir ( path, 0700) == 0)
213 	    return 1;
214 	  if (_REENT_ERRNO(ptr) != EEXIST)
215 	    return 0;
216 #else /* !HAVE_MKDIR */
217 	  _REENT_ERRNO(ptr) = ENOSYS;
218 	  return 0;
219 #endif /* !HAVE_MKDIR */
220 	}
221       else
222 #endif /* _ELIX_LEVEL */
223       if (doopen)
224 	{
225 	  if ((*doopen = open (path, O_CREAT | O_EXCL | O_RDWR | flags,
226 				  0600)) >= 0)
227 	    return 1;
228 	  if (_REENT_ERRNO(ptr) != EEXIST)
229 	    return 0;
230 	}
231 #ifdef __USE_INTERNAL_STAT64
232       else if (stat64 (path, &sbuf))
233 #else
234       else if (stat (path, &sbuf))
235 #endif
236 	return (_REENT_ERRNO(ptr) == ENOENT ? 1 : 0);
237 
238       /* tricky little algorithm for backward compatibility */
239       for (trv = start;;)
240 	{
241 	  if (trv == end)
242 	    return 0;
243 	  if (*trv == 'z')
244 	    *trv++ = 'a';
245 	  else
246 	    {
247 	      /* Safe, since it only encounters 7-bit characters.  */
248 	      if (isdigit ((unsigned char) *trv))
249 		*trv = 'a';
250 	      else
251 		++ * trv;
252 	      break;
253 	    }
254 	}
255     }
256   /*NOTREACHED*/
257 }
258 
259 #ifndef O_BINARY
260 # define O_BINARY 0
261 #endif
262 
263 int
mkstemp(char * path)264 mkstemp (
265        char *path)
266 {
267   int fd;
268 
269   return (_gettemp (path, &fd, 0, 0, O_BINARY) ? fd : -1);
270 }
271 
272 #if !defined _ELIX_LEVEL || _ELIX_LEVEL >= 4
273 char *
mkdtemp(char * path)274 mkdtemp (
275        char *path)
276 {
277   return (_gettemp (path, (int *) NULL, 1, 0, 0) ? path : NULL);
278 }
279 
280 int
mkstemps(char * path,int len)281 mkstemps (
282        char *path,
283        int len)
284 {
285   int fd;
286 
287   return (_gettemp (path, &fd, 0, len, O_BINARY) ? fd : -1);
288 }
289 
290 int
mkostemp(char * path,int flags)291 mkostemp (
292        char *path,
293        int flags)
294 {
295   int fd;
296 
297   return (_gettemp (path, &fd, 0, 0, flags & ~O_ACCMODE) ? fd : -1);
298 }
299 
300 int
mkostemps(char * path,int len,int flags)301 mkostemps (
302        char *path,
303        int len,
304        int flags)
305 {
306   int fd;
307 
308   return (_gettemp (path, &fd, 0, len, flags & ~O_ACCMODE) ? fd : -1);
309 }
310 #endif /* _ELIX_LEVEL */
311 
312 char *
mktemp(char * path)313 mktemp (
314        char *path)
315 {
316   return (_gettemp (path, (int *) NULL, 0, 0, 0) ? path : (char *) NULL);
317 }
318