2 * ========================================================================
3 * Copyright 2013-2022 Eduardo Chappa
4 * Copyright 2006-2008 University of Washington
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
10 * http://www.apache.org/licenses/LICENSE-2.0
12 * ========================================================================
21 #include "canaccess.h"
23 #include "../charconv/utf8.h"
24 #include "../charconv/filesys.h"
31 #define ACCESSIBLE (WRITE_ACCESS)
36 #define ACCESSIBLE (WRITE_ACCESS|EXECUTE_ACCESS)
45 char *was_nonexistent_tmp_name(char *, size_t, char *);
50 * This routine is derived from BSD4.3 code,
51 * Copyright (c) 1987 Regents of the University of California.
52 * All rights reserved.
54 #if defined(LIBC_SCCS) && !defined(lint)
55 static char sccsid
[] = "@(#)mktemp.c 5.7 (Berkeley) 6/27/88";
56 #endif /* LIBC_SCCS and not lint */
59 was_nonexistent_tmp_name(char *as
, size_t aslen
, char *ext
)
61 register char *start
, *trv
;
64 static unsigned n
= 0;
68 static unsigned long r
= 0;
71 r
= (unsigned)getpid() + time((time_t *)0);
73 for(i
= 5; i
> 0; i
--)
74 r
= 1664525 * r
+ 1013904223;
76 pid
= ((unsigned)getpid() * 100) + n
++;
80 /* extra X's get set to 0's */
81 for(trv
= as
; *trv
; ++trv
)
85 * We should probably make the name random instead of having it
89 *trv
= (pid
% 10) + '0';
93 /* add the extension, enough room guaranteed by caller */
95 strncat(as
, ".", aslen
-strlen(as
)-1);
97 strncat(as
, ext
, aslen
-strlen(as
)-1);
102 * Check for write permission on target directory; if you have
103 * six X's and you can't write the directory, this will run for
104 * a *very* long time.
106 for(start
= ++trv
; trv
> as
&& *trv
!= PATH_SEP
[0]; --trv
)
109 if(*trv
== PATH_SEP
[0]){
113 if((trv
- as
== 2) && isalpha(as
[0]) && as
[1] == ':')
117 if(our_stat(as
==trv
? PATH_SEP
: as
, &sbuf
) || !(sbuf
.st_mode
& S_IFDIR
))
118 return((char *)NULL
);
126 if(our_stat(as
==trv
? PATH_SEP
: as
, &sbuf
) || !(sbuf
.st_mode
& S_IFDIR
))
127 return((char *)NULL
);
133 else if (our_stat(".", &sbuf
) == -1)
134 return((char *)NULL
);
138 * Check with lstat to be sure we don't have
139 * a symlink. If lstat fails and no such file, then we
140 * have a winner. Otherwise, lstat shouldn't fail.
141 * If lstat succeeds, then skip it because it exists.
144 if(our_lstat(as
, &sbuf
)){ /* lstat failed */
145 if(errno
== ENOENT
){ /* no such file, success */
146 #endif /* !_WINDOWS */
148 * Create the file so that the
149 * evil ones don't have a chance to put something there
150 * that they can read or write before we create it
153 f
= O_CREAT
|O_EXCL
|O_WRONLY
|O_BINARY
;
155 if((fd
=our_open(as
, f
, 0600)) >= 0 && close(fd
) == 0)
157 else if(++tries
> 3) /* open failed unexpectedly */
158 return((char *)NULL
);
161 else /* failed for unknown reason */
162 return((char *)NULL
);
164 #endif /* !_WINDOWS */
168 return((char *)NULL
);
171 * Change the digits from the initial values into
172 * lower case letters and try again.
177 if(isdigit((unsigned char)*trv
))
191 * This routine is derived from BSD4.3 code,
192 * Copyright (c) 1988 Regents of the University of California.
193 * All rights reserved.
195 #if defined(LIBC_SCCS) && !defined(lint)
196 static char sccsid
[] = "@(#)tmpnam.c 4.5 (Berkeley) 6/27/88";
197 #endif /* LIBC_SCCS and not lint */
198 /*----------------------------------------------------------------------
199 Return a unique file name in a given directory. This is not quite
200 the same as the usual tempnam() function, though it is similar.
201 We want it to use the TMPDIR/TMP/TEMP environment variable only if dir
202 is NULL, instead of using it regardless if it is set.
203 We also want it to be safer than tempnam().
204 If we return a filename, we are saying that the file did not exist
205 at the time this function was called (and it wasn't a symlink pointing
206 to a file that didn't exist, either).
207 If dir is NULL this is a temp file in a public directory. In that
208 case we create the file with permission 0600 before returning.
210 Args: dir -- The directory to create the name in
211 prefix -- Prefix of the name
213 Result: Malloc'd string equal to new name is returned. It must be free'd
214 by the caller. Returns the string on success and NULL on failure.
217 temp_nam(char *dir
, char *prefix
)
219 return(temp_nam_ext(dir
, prefix
, NULL
));
223 /*----------------------------------------------------------------------
225 Like temp_nam but create a unique name with an extension.
227 Result: Malloc'd string equal to new name is returned. It must be free'd
228 by the caller. Returns the string on success and NULL on failure.
231 temp_nam_ext(char *dir
, char *prefix
, char *ext
)
240 if(!(name
= (char *)malloc(MAXPATH
* sizeof(char))))
241 return((char *)NULL
);
243 if(!dir
&& (f
= getenv("TMPDIR")) && !our_stat(f
, &buf
) &&
244 (buf
.st_mode
&S_IFMT
) == S_IFDIR
&&
245 !can_access(f
, ACCESSIBLE
)){
246 strncpy(name
, f
, MAXPATH
-1);
247 name
[MAXPATH
-1] = '\0';
251 if(!dir
&& (f
= getenv("TMP")) && !our_stat(f
, &buf
) &&
252 (buf
.st_mode
&S_IFMT
) == S_IFDIR
&&
253 !can_access(f
, ACCESSIBLE
)){
254 strncpy(name
, f
, MAXPATH
-1);
255 name
[MAXPATH
-1] = '\0';
259 if(!dir
&& (f
= getenv("TEMP")) && !our_stat(f
, &buf
) &&
260 (buf
.st_mode
&S_IFMT
) == S_IFDIR
&&
261 !can_access(f
, ACCESSIBLE
)){
262 strncpy(name
, f
, MAXPATH
-1);
263 name
[MAXPATH
-1] = '\0';
268 strncpy(name
, dir
, MAXPATH
-1);
269 name
[MAXPATH
-1] = '\0';
272 if(!*dir
|| (isalpha(*dir
) && *(dir
+1) == ':' && !*(dir
+2))){
273 strncat(name
, PATH_SEP
, MAXPATH
-strlen(name
)-1);
274 name
[MAXPATH
-1] = '\0';
278 if(!our_stat(name
, &buf
)
279 && (buf
.st_mode
&S_IFMT
) == S_IFDIR
280 && !can_access(name
, ACCESSIBLE
)){
281 strncpy(name
, dir
, MAXPATH
-1);
282 name
[MAXPATH
-1] = '\0';
289 #define P_tmpdir "\\tmp"
291 #define P_tmpdir "/usr/tmp"
295 if(!our_stat(P_tmpdir
, &buf
) &&
296 (buf
.st_mode
&S_IFMT
) == S_IFDIR
&&
297 !can_access(P_tmpdir
, ACCESSIBLE
)){
298 strncpy(name
, P_tmpdir
, MAXPATH
-1);
299 name
[MAXPATH
-1] = '\0';
304 if(!our_stat("/tmp", &buf
) &&
305 (buf
.st_mode
&S_IFMT
) == S_IFDIR
&&
306 !can_access("/tmp", ACCESSIBLE
)){
307 strncpy(name
, "/tmp", MAXPATH
-1);
308 name
[MAXPATH
-1] = '\0';
314 return((char *)NULL
);
318 if(name
[0] && *((f
= &name
[l
=strlen(name
)]) - 1) != PATH_SEP
[0] && l
+1 < MAXPATH
){
324 if(prefix
&& (ll
= strlen(prefix
)) && l
+ll
< MAXPATH
){
325 strncpy(f
, prefix
, MAXPATH
-(f
-name
));
326 name
[MAXPATH
-1] = '\0';
331 if(l
+5+(ext
[0] ? strlen(ext
)+1 : 0) < MAXPATH
){
332 strncpy(f
, "XXXXX", MAXPATH
-(f
-name
));
333 name
[MAXPATH
-1] = '\0';
337 return((char *)NULL
);
340 return(was_nonexistent_tmp_name(name
, MAXPATH
, ext
));