1 /*
2  * tmpfile.c - functions to create and safely open temp files for the shell.
3  */
4 
5 /* Copyright (C) 2000-2020 Free Software Foundation, Inc.
6 
7    This file is part of GNU Bash, the Bourne Again SHell.
8 
9    Bash is free software: you can redistribute it and/or modify
10    it under the terms of the GNU General Public License as published by
11    the Free Software Foundation, either version 3 of the License, or
12    (at your option) any later version.
13 
14    Bash is distributed in the hope that it will be useful,
15    but WITHOUT ANY WARRANTY; without even the implied warranty of
16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17    GNU General Public License for more details.
18 
19    You should have received a copy of the GNU General Public License
20    along with Bash.  If not, see <http://www.gnu.org/licenses/>.
21 */
22 
23 #include <config.h>
24 
25 #include <bashtypes.h>
26 #include <posixstat.h>
27 #include <posixtime.h>
28 #include <filecntl.h>
29 
30 #if defined (HAVE_UNISTD_H)
31 #  include <unistd.h>
32 #endif
33 
34 #include <bashansi.h>
35 
36 #include <stdio.h>
37 #include <errno.h>
38 
39 #include <shell.h>
40 
41 #ifndef errno
42 extern int errno;
43 #endif
44 
45 #define BASEOPENFLAGS	(O_CREAT | O_TRUNC | O_EXCL | O_BINARY)
46 
47 #define DEFAULT_TMPDIR		"."	/* bogus default, should be changed */
48 #define DEFAULT_NAMEROOT	"shtmp"
49 
50 /* Use ANSI-C rand() interface if random(3) is not available */
51 #if !HAVE_RANDOM
52 #define random() rand()
53 #endif
54 
55 extern pid_t dollar_dollar_pid;
56 
57 static char *get_sys_tmpdir PARAMS((void));
58 static char *get_tmpdir PARAMS((int));
59 
60 static char *sys_tmpdir = (char *)NULL;
61 static int ntmpfiles;
62 static int tmpnamelen = -1;
63 static unsigned long filenum = 1L;
64 
65 static char *
get_sys_tmpdir()66 get_sys_tmpdir ()
67 {
68   if (sys_tmpdir)
69     return sys_tmpdir;
70 
71 #ifdef P_tmpdir
72   sys_tmpdir = P_tmpdir;
73   if (file_iswdir (sys_tmpdir))
74     return sys_tmpdir;
75 #endif
76 
77   sys_tmpdir = "/tmp";
78   if (file_iswdir (sys_tmpdir))
79     return sys_tmpdir;
80 
81   sys_tmpdir = "/var/tmp";
82   if (file_iswdir (sys_tmpdir))
83     return sys_tmpdir;
84 
85   sys_tmpdir = "/usr/tmp";
86   if (file_iswdir (sys_tmpdir))
87     return sys_tmpdir;
88 
89   sys_tmpdir = DEFAULT_TMPDIR;
90 
91   return sys_tmpdir;
92 }
93 
94 static char *
get_tmpdir(flags)95 get_tmpdir (flags)
96      int flags;
97 {
98   char *tdir;
99 
100   tdir = (flags & MT_USETMPDIR) ? get_string_value ("TMPDIR") : (char *)NULL;
101   if (tdir && (file_iswdir (tdir) == 0 || strlen (tdir) > PATH_MAX))
102     tdir = 0;
103 
104   if (tdir == 0)
105     tdir = get_sys_tmpdir ();
106 
107 #if defined (HAVE_PATHCONF) && defined (_PC_NAME_MAX)
108   if (tmpnamelen == -1)
109     tmpnamelen = pathconf (tdir, _PC_NAME_MAX);
110 #else
111   tmpnamelen = 0;
112 #endif
113 
114   return tdir;
115 }
116 
117 static void
sh_seedrand()118 sh_seedrand ()
119 {
120 #if HAVE_RANDOM
121   int d;
122   static int seeded = 0;
123   if (seeded == 0)
124     {
125       struct timeval tv;
126 
127       gettimeofday (&tv, NULL);
128       srandom (tv.tv_sec ^ tv.tv_usec ^ (getpid () << 16) ^ (uintptr_t)&d);
129       seeded = 1;
130     }
131 #endif
132 }
133 
134 char *
sh_mktmpname(nameroot,flags)135 sh_mktmpname (nameroot, flags)
136      char *nameroot;
137      int flags;
138 {
139   char *filename, *tdir, *lroot;
140   struct stat sb;
141   int r, tdlen;
142   static int seeded = 0;
143 
144   filename = (char *)xmalloc (PATH_MAX + 1);
145   tdir = get_tmpdir (flags);
146   tdlen = strlen (tdir);
147 
148   lroot = nameroot ? nameroot : DEFAULT_NAMEROOT;
149   if (nameroot == 0)
150     flags &= ~MT_TEMPLATE;
151 
152   if ((flags & MT_TEMPLATE) && strlen (nameroot) > PATH_MAX)
153     flags &= ~MT_TEMPLATE;
154 
155 #ifdef USE_MKTEMP
156   if (flags & MT_TEMPLATE)
157     strcpy (filename, nameroot);
158   else
159     sprintf (filename, "%s/%s.XXXXXX", tdir, lroot);
160   if (mktemp (filename) == 0)
161     {
162       free (filename);
163       filename = NULL;
164     }
165 #else  /* !USE_MKTEMP */
166   sh_seedrand ();
167   while (1)
168     {
169       filenum = (filenum << 1) ^
170 		(unsigned long) time ((time_t *)0) ^
171 		(unsigned long) dollar_dollar_pid ^
172 		(unsigned long) ((flags & MT_USERANDOM) ? random () : ntmpfiles++);
173       sprintf (filename, "%s/%s-%lu", tdir, lroot, filenum);
174       if (tmpnamelen > 0 && tmpnamelen < 32)
175 	filename[tdlen + 1 + tmpnamelen] = '\0';
176 #  ifdef HAVE_LSTAT
177       r = lstat (filename, &sb);
178 #  else
179       r = stat (filename, &sb);
180 #  endif
181       if (r < 0 && errno == ENOENT)
182 	break;
183     }
184 #endif /* !USE_MKTEMP */
185 
186   return filename;
187 }
188 
189 int
sh_mktmpfd(nameroot,flags,namep)190 sh_mktmpfd (nameroot, flags, namep)
191      char *nameroot;
192      int flags;
193      char **namep;
194 {
195   char *filename, *tdir, *lroot;
196   int fd, tdlen;
197 
198   filename = (char *)xmalloc (PATH_MAX + 1);
199   tdir = get_tmpdir (flags);
200   tdlen = strlen (tdir);
201 
202   lroot = nameroot ? nameroot : DEFAULT_NAMEROOT;
203   if (nameroot == 0)
204     flags &= ~MT_TEMPLATE;
205 
206   if ((flags & MT_TEMPLATE) && strlen (nameroot) > PATH_MAX)
207     flags &= ~MT_TEMPLATE;
208 
209 #ifdef USE_MKSTEMP
210   if (flags & MT_TEMPLATE)
211     strcpy (filename, nameroot);
212   else
213     sprintf (filename, "%s/%s.XXXXXX", tdir, lroot);
214   fd = mkstemp (filename);
215   if (fd < 0 || namep == 0)
216     {
217       free (filename);
218       filename = NULL;
219     }
220   if (namep)
221     *namep = filename;
222   return fd;
223 #else /* !USE_MKSTEMP */
224   sh_seedrand ();
225   do
226     {
227       filenum = (filenum << 1) ^
228 		(unsigned long) time ((time_t *)0) ^
229 		(unsigned long) dollar_dollar_pid ^
230 		(unsigned long) ((flags & MT_USERANDOM) ? random () : ntmpfiles++);
231       sprintf (filename, "%s/%s-%lu", tdir, lroot, filenum);
232       if (tmpnamelen > 0 && tmpnamelen < 32)
233 	filename[tdlen + 1 + tmpnamelen] = '\0';
234       fd = open (filename, BASEOPENFLAGS | ((flags & MT_READWRITE) ? O_RDWR : O_WRONLY), 0600);
235     }
236   while (fd < 0 && errno == EEXIST);
237 
238   if (namep)
239     *namep = filename;
240   else
241     free (filename);
242 
243   return fd;
244 #endif /* !USE_MKSTEMP */
245 }
246 
247 FILE *
sh_mktmpfp(nameroot,flags,namep)248 sh_mktmpfp (nameroot, flags, namep)
249      char *nameroot;
250      int flags;
251      char **namep;
252 {
253   int fd;
254   FILE *fp;
255 
256   fd = sh_mktmpfd (nameroot, flags, namep);
257   if (fd < 0)
258     return ((FILE *)NULL);
259   fp = fdopen (fd, (flags & MT_READWRITE) ? "w+" : "w");
260   if (fp == 0)
261     close (fd);
262   return fp;
263 }
264 
265 char *
sh_mktmpdir(nameroot,flags)266 sh_mktmpdir (nameroot, flags)
267      char *nameroot;
268      int flags;
269 {
270   char *filename, *tdir, *lroot, *dirname;
271   int fd, tdlen;
272 
273 #ifdef USE_MKDTEMP
274   filename = (char *)xmalloc (PATH_MAX + 1);
275   tdir = get_tmpdir (flags);
276   tdlen = strlen (tdir);
277 
278   lroot = nameroot ? nameroot : DEFAULT_NAMEROOT;
279   if (nameroot == 0)
280     flags &= ~MT_TEMPLATE;
281 
282   if ((flags & MT_TEMPLATE) && strlen (nameroot) > PATH_MAX)
283     flags &= ~MT_TEMPLATE;
284 
285   if (flags & MT_TEMPLATE)
286     strcpy (filename, nameroot);
287   else
288     sprintf (filename, "%s/%s.XXXXXX", tdir, lroot);
289   dirname = mkdtemp (filename);
290   if (dirname == 0)
291     {
292       free (filename);
293       filename = NULL;
294     }
295   return dirname;
296 #else /* !USE_MKDTEMP */
297   filename = (char *)NULL;
298   do
299     {
300       filename = sh_mktmpname (nameroot, flags);
301       fd = mkdir (filename, 0700);
302       if (fd == 0)
303 	break;
304       free (filename);
305       filename = (char *)NULL;
306     }
307   while (fd < 0 && errno == EEXIST);
308 
309   return (filename);
310 #endif /* !USE_MKDTEMP */
311 }
312