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>>, <<mkstemps>>,
26 <<mkostemps>>---generate unused file name
27 
28 INDEX
29 	mktemp
30 INDEX
31 	mkstemp
32 INDEX
33 	mkstemps
34 INDEX
35 	mkostemps
36 
37 SYNOPSIS
38 	#include <stdlib.h>
39 	char *mktemp(char *<[path]>);
40 	int mkstemp(char *<[path]>);
41         int mkstemps(char *<[path]>, int suffixlen);
42         int mkostemps(char *<[path]>, int suffixlen, int flags);
43 
44 DESCRIPTION
45 <<mktemp>>, <<mkstemp>>, <<mkstemps>>, and <<mkostemps>> attempt to
46 generate a file name that is not yet in use for any existing file.
47 <<mkstemp>>, <<mkstemps>> and <mkostemps>> create the file and open it
48 for reading and writing; <<mktemp>> simply generates the file name
49 (making <<mktemp>> a security risk). <<mkostemps>> allow the addition
50 of other <<open>> flags, such as <<O_CLOEXEC>>, <<O_APPEND>>, or
51 <<O_SYNC>>.  On platforms with a separate text mode, <<mkstemp>>
52 forces <<O_BINARY>>, while <<mkostemp>> allows the choice between
53 <<O_BINARY>>, <<O_TEXT>>, or 0 for default.
54 
55 You supply a simple pattern for the generated file name, as the string
56 at <[path]>.  The pattern should be a valid filename (including path
57 information if you wish) ending with at least six `<<X>>' characters.
58 The generated filename will match the leading part of the name you
59 supply, with the trailing `<<X>>' characters replaced by some
60 combination of digits and letters.  With <<mkstemps>> and
61 <mkostemps>>, the `<<X>>' characters end <[suffixlen]> bytes before
62 the end of the string.
63 
64 RETURNS
65 <<mktemp>> returns the pointer <[path]> to the modified string
66 representing an unused filename, unless it could not generate one, or
67 the pattern you provided is not suitable for a filename; in that case,
68 it returns <<NULL>>.  Be aware that there is an inherent race between
69 generating the name and attempting to create a file by that name;
70 you are advised to use <<O_EXCL|O_CREAT>>.
71 
72 <<mkstemp>>, <<mkstemps>> and <<mkostemps>> return a file descriptor
73 to the newly created file, unless it could not generate an unused
74 filename, or the pattern you provided is not suitable for a filename;
75 in that case, it returns <<-1>>.
76 
77 NOTES
78 Never use <<mktemp>>.  The generated filenames are easy to guess and
79 there's a race between the test if the file exists and the creation
80 of the file.  In combination this makes <<mktemp>> prone to attacks
81 and using it is a security risk.  Whenever possible use <<mkstemp>>
82 instead.  It doesn't suffer the race condition.
83 
84 PORTABILITY
85 ANSI C does not require either <<mktemp>> or <<mkstemp>>; the System V
86 Interface Definition requires <<mktemp>> as of Issue 2.  POSIX 2001
87 requires <<mkstemp>> while deprecating <<mktemp>>.  <<mkstemps>>, and
88 <<mkostemps>> are not standardized.
89 
90 Supporting OS subroutines required: <<open>>
91 */
92 
93 #include "stdio_private.h"
94 
95 /* Our names are six characters from [0-9a-z] */
96 #define NUM_NAMES       (36UL * 36UL * 36UL * 36UL * 36UL * 36UL)
97 
98 /* Attempts */
99 #define NUM_ATTEMPT     (36UL * 36UL * 36UL)
100 
101 static int
_gettemp(char * path,int suffixlen,int * doopen,int flags)102 _gettemp (char *path,
103           int suffixlen,
104           int *doopen,
105           int flags)
106 {
107   char *start, *trv;
108   char *end;
109   uint32_t attempt;
110 
111   end = path + strlen(path) - suffixlen;
112   trv = end;
113 
114   /* Replace 'X' with 'a' */
115   while (path < trv && *--trv == 'X')
116     *trv = 'a';
117 
118   /* Make sure we got six Xs */
119   if (end - trv < 6)
120     {
121       errno = EINVAL;
122       return 0;
123     }
124 
125   start = trv + 1;
126 
127   for (attempt = 0; attempt < NUM_ATTEMPT; attempt++)
128     {
129       /* Generate a random filename index */
130       long filename = random() % NUM_NAMES;
131       size_t i;
132 
133       /* Convert the random index into characters */
134       for (i = 0; i < 6; i++)
135         {
136           char c = filename % 36;
137           filename /= 36;
138 
139           if (c < 10)
140             c += '0';
141           else
142             c += 'a' - 10;
143 
144           start[i] = c;
145         }
146 
147       /*
148        * Use open to check if the file exists to avoid depending on
149        * stat or access. Don't rely on O_EXCL working, although if it
150        * doesn't, this introduces a race condition
151        */
152       int fd = open(path, O_RDONLY);
153       if (fd < 0) {
154         if (errno != EACCES) {
155           if (errno != ENOENT)
156             return 0;
157           if (doopen)
158           {
159             fd = open (path, flags | O_CREAT | O_EXCL | O_RDWR,
160                        0600);
161             if (fd >= 0) {
162               *doopen = fd;
163               return 1;
164             }
165             if (errno != EEXIST)
166               return 0;
167           } else {
168             return 1;
169           }
170         }
171       } else
172         close(fd);
173     }
174   return 0;
175 }
176 
177 int
mkstemp(char * template)178 mkstemp (char *template)
179 {
180   int fd;
181 
182   return (_gettemp (template, 0, &fd, 0) ? fd : -1);
183 }
184 
185 char *
mktemp(char * template)186 mktemp (char *template)
187 {
188   return (_gettemp (template, 0, (int *) NULL, 0) ? template : (char *) NULL);
189 }
190 
191 #ifndef O_BINARY
192 #define O_BINARY 0
193 #endif
194 #ifndef O_TEXT
195 #define O_TEXT 0
196 #endif
197 
198 int
mkstemps(char * template,int suffixlen)199 mkstemps(char *template, int suffixlen)
200 {
201   int fd;
202 
203   return (_gettemp (template, suffixlen, &fd, O_BINARY) ? fd : -1);
204 }
205 
206 int
mkostemps(char * template,int suffixlen,int flags)207 mkostemps(char *template, int suffixlen, int flags)
208 {
209   int fd;
210 
211   flags &= (O_APPEND | O_CLOEXEC | O_SYNC | O_BINARY | O_TEXT);
212   return (_gettemp (template, suffixlen, &fd, flags) ? fd : -1);
213 }
214