1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1985-2013 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                 Eclipse Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *          http://www.eclipse.org/org/documents/epl-v10.html           *
11 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *               Glenn Fowler <glenn.s.fowler@gmail.com>                *
18 *                    David Korn <dgkorn@gmail.com>                     *
19 *                     Phong Vo <phongvo@gmail.com>                     *
20 *                                                                      *
21 ***********************************************************************/
22 #pragma prototyped
23 /*
24  * AT&T Research
25  *
26  * generate a temp file / name
27  *
28  *	[<dir>/][<pfx>]<bas>.<suf>
29  *
30  * length(<pfx>)<=5
31  * length(<bas>)==chars-to-make-BASE-length-name
32  * length(<suf>)==3
33  *
34  *	pathtmp(a,b,c,d)	pathtemp(a,L_tmpnam,b,c,0)
35  *	tmpfile()		char*p=pathtemp(0,0,0,"tf",&sp);
36  *				remove(p);
37  *				free(p)
38  *	tmpnam(0)		static char p[L_tmpnam];
39  *				pathtemp(p,sizeof(p),0,"tn",0)
40  *	tmpnam(p)		pathtemp(p,L_tmpnam,0,"tn",0)
41  *	tempnam(d,p)		pathtemp(0,d,p,0)
42  *	mktemp(p)		pathtemp(0,0,p,0)
43  *
44  * if buf==0 then space is malloc'd
45  * buf size is size
46  * dir and pfx may be 0
47  * / as pfx trailing char creates a directory instead of a file
48  * if pfx contains trailing X's then it is a mktemp(3) template
49  * otherwise only first 5 chars of pfx are used
50  * if fdp!=0 then the path is opened O_EXCL and *fdp is the open fd
51  * malloc'd space returned by successful pathtemp() calls
52  * must be freed by the caller
53  *
54  * generated names are pseudo-randomized to avoid both
55  * collisions and predictions (same alg in sfio/sftmp.c)
56  *
57  * / as first pfx char provides tmp file generation control
58  * 0 returned for unknown ops
59  *
60  *	/cycle		dir specifies TMPPATH cycle control
61  *		automatic	(default) cycled with each tmp file
62  *		manual		cycled by application with dir=(nil)
63  *		(nil)		cycle TMPPATH
64  *	/check		dir==0 disables access() check when fdp==0
65  *	/prefix		dir specifies the default prefix (default ast)
66  *	/private	private file/dir modes
67  *	/public		public file/dir modes
68  *	/seed		dir specifies pseudo-random generator seed
69  *			0 or "0" to re-initialize
70  *	/TMPPATH	dir overrides the env value
71  *	/TMPDIR		dir overrides the env value
72  */
73 
74 #include <ast.h>
75 #include <ls.h>
76 #include <tv.h>
77 #include <tm.h>
78 #include <error.h>
79 
80 #define ATTEMPT		16
81 
82 #define BASE		14
83 #define SUFF		3
84 
85 #define TMP_ENV		"TMPDIR"
86 #define TMP_PATH_ENV	"TMPPATH"
87 #define TMP1		"/tmp"
88 #define TMP2		"/usr/tmp"
89 
90 #define VALID(d)	(*(d)&&!eaccess(d,W_OK|X_OK))
91 
92 static struct Tmp_s
93 {
94 	mode_t		mode;
95 	char**		vec;
96 	char**		dir;
97 	uintmax_t	key;
98 	uintmax_t	rng;
99 	pid_t		pid;
100 	int		manual;
101 	int		nocheck;
102 	int		seed;
103 	char*		pfx;
104 	char*		tmpdir;
105 	char*		tmppath;
106 } tmp = { S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH };
107 
108 char*
pathtemp(char * buf,size_t len,const char * dir,const char * pfx,int * fdp)109 pathtemp(char* buf, size_t len, const char* dir, const char* pfx, int* fdp)
110 {
111 	register char*		d;
112 	register char*		b;
113 	register char*		s;
114 	register char*		x;
115 	uintmax_t		key;
116 	int			m;
117 	int			n;
118 	int			l;
119 	int			r;
120 	int			z;
121 	int			attempt;
122 	int			directory;
123 	Tv_t			tv;
124 	char			keybuf[16];
125 
126 	if (pfx && pfx[0] == '/' && pfx[1])
127 	{
128 		pfx++;
129 		if (dir && !*dir)
130 			dir = 0;
131 		if (streq(pfx, "check"))
132 		{
133 			tmp.nocheck = !dir;
134 			return (char*)pfx;
135 		}
136 		else if (streq(pfx, "cycle"))
137 		{
138 			if (!dir)
139 			{
140 				tmp.manual = 1;
141 				if (tmp.dir && !*tmp.dir++)
142 					tmp.dir = tmp.vec;
143 			}
144 			else
145 				tmp.manual = streq(dir, "manual");
146 			return (char*)pfx;
147 		}
148 		else if (streq(pfx, "prefix"))
149 		{
150 			if (tmp.pfx)
151 				free(tmp.pfx);
152 			tmp.pfx = dir ? strdup(dir) : (char*)0;
153 			return (char*)pfx;
154 		}
155 		else if (streq(pfx, "private"))
156 		{
157 			tmp.mode = S_IRUSR|S_IWUSR;
158 			return (char*)pfx;
159 		}
160 		else if (streq(pfx, "public"))
161 		{
162 			tmp.mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
163 			return (char*)pfx;
164 		}
165 		else if (streq(pfx, "seed"))
166 		{
167 			tmp.key = (tmp.seed = (tmp.rng = dir ? (uintmax_t)strtoul(dir, NiL, 0) : (uintmax_t)1) != 0)? (uintmax_t)0x63c63cd9L : 0;
168 			return (char*)pfx;
169 		}
170 		else if (streq(pfx, TMP_ENV))
171 		{
172 			if (tmp.vec)
173 			{
174 				free(tmp.vec);
175 				tmp.vec = 0;
176 			}
177 			if (tmp.tmpdir)
178 				free(tmp.tmpdir);
179 			tmp.tmpdir = dir ? strdup(dir) : (char*)0;
180 			return (char*)pfx;
181 		}
182 		else if (streq(pfx, TMP_PATH_ENV))
183 		{
184 			if (tmp.vec)
185 			{
186 				free(tmp.vec);
187 				tmp.vec = 0;
188 			}
189 			if (tmp.tmppath)
190 				free(tmp.tmppath);
191 			tmp.tmppath = dir ? strdup(dir) : (char*)0;
192 			return (char*)pfx;
193 		}
194 		return 0;
195 	}
196 	if (tmp.seed)
197 		tv.tv_nsec = 0;
198 	else
199 		tvgettime(&tv);
200 	if (!(d = (char*)dir) || *d && eaccess(d, W_OK|X_OK))
201 	{
202 		if (!tmp.vec)
203 		{
204 			if ((x = tmp.tmppath) || (x = getenv(TMP_PATH_ENV)))
205 			{
206 				n = 2;
207 				s = x;
208 				while (s = strchr(s, ':'))
209 				{
210 					s++;
211 					n++;
212 				}
213 				if (!(tmp.vec = newof(0, char*, n, strlen(x) + 1)))
214 					return 0;
215 				tmp.dir = tmp.vec;
216 				x = strcpy((char*)(tmp.dir + n), x);
217 				*tmp.dir++ = x;
218 				while (x = strchr(x, ':'))
219 				{
220 					*x++ = 0;
221 					if (!VALID(*(tmp.dir - 1)))
222 						tmp.dir--;
223 					*tmp.dir++ = x;
224 				}
225 				if (!VALID(*(tmp.dir - 1)))
226 					tmp.dir--;
227 				*tmp.dir = 0;
228 			}
229 			else
230 			{
231 				if (((d = tmp.tmpdir) || (d = getenv(TMP_ENV))) && !VALID(d))
232 					d = 0;
233 				if (!(tmp.vec = newof(0, char*, 2, d ? (strlen(d) + 1) : 0)))
234 					return 0;
235 				if (d)
236 					*tmp.vec = strcpy((char*)(tmp.vec + 2), d);
237 			}
238 			tmp.dir = tmp.vec;
239 		}
240 		if (!(d = *tmp.dir++))
241 		{
242 			tmp.dir = tmp.vec;
243 			d = *tmp.dir++;
244 		}
245 		if (!d && (!*(d = astconf("TMP", NiL, NiL)) || eaccess(d, W_OK|X_OK)) && eaccess(d = TMP1, W_OK|X_OK) && eaccess(d = TMP2, W_OK|X_OK))
246 			return 0;
247 	}
248 	if (!len)
249 		len = PATH_MAX;
250 	len--;
251 	if (!(b = buf) && !(b = newof(0, char, len, 1)))
252 		return 0;
253 	z = 0;
254 	if (!pfx && !(pfx = tmp.pfx))
255 		pfx = "ast";
256 	m = strlen(pfx);
257 	if (directory = pfx[m - 1] == '/')
258 		m--;
259 	if (buf && dir && (buf == (char*)dir && (buf + strlen(buf) + 1) == (char*)pfx || buf == (char*)pfx && !*dir) && !strcmp((char*)pfx + m + 1, "XXXXX"))
260 	{
261 		d = (char*)dir;
262 		len = m += strlen(d) + 8;
263 		l = 3;
264 		r = 3;
265 	}
266 	else if (*pfx && pfx[m - 1] == 'X')
267 	{
268 		for (l = m; l && pfx[l - 1] == 'X'; l--);
269 		r = m - l;
270 		m = l;
271 		l = r / 2;
272 		r -= l;
273 	}
274 	else if ((x = strchr(pfx, '.')) && (x - pfx) < 5)
275 	{
276 		if (m > 5)
277 			m = 5;
278 		l = BASE - SUFF - m;
279 		r = SUFF;
280 	}
281 	else
282 	{
283 		if (m > 5)
284 			m = 5;
285 		l = BASE - SUFF - m - 1;
286 		r = SUFF;
287 		z = '.';
288 	}
289 	x = b + len;
290 	s = b;
291 	if (d)
292 	{
293 		while (s < x && (n = *d++))
294 			*s++ = n;
295 		if (s < x && s > b && *(s - 1) != '/')
296 			*s++ = '/';
297 	}
298 	if ((x - s) > m)
299 		x = s + m;
300 	while (s < x && (n = *pfx++))
301 	{
302 		if (n == '/' || n == '\\' || n == z)
303 			n = '_';
304 		*s++ = n;
305 	}
306 	*s = 0;
307 	len -= (s - b);
308 	for (attempt = 0; attempt < ATTEMPT; attempt++)
309 	{
310 		if (!tmp.rng || !tmp.seed && (attempt || tmp.pid != getpid()))
311 		{
312 			register int	r;
313 
314 			/*
315 			 * get a quasi-random coefficient
316 			 */
317 
318 			tmp.pid = getpid();
319 			tmp.rng = (uintmax_t)tmp.pid * ((uintmax_t)time(NiL) ^ (((uintmax_t)integralof(&attempt)) >> 3) ^ (((uintmax_t)integralof(tmp.dir)) >> 3));
320 			if (!tmp.key)
321 				tmp.key = (tmp.rng >> (sizeof(tmp.rng) * 4)) | ((tmp.rng & 0xffff) << (sizeof(tmp.rng) * 4));
322 			tmp.rng ^= tmp.key;
323 
324 			/*
325 			 * Knuth vol.2, page.16, Thm.A
326 			 */
327 
328 			if ((r = (tmp.rng - 1) & 03))
329 				tmp.rng += 4 - r;
330 		}
331 
332 		/*
333 		 * generate a pseudo-random name
334 		 */
335 
336 		key = tmp.rng * tmp.key + tv.tv_nsec;
337 		if (!tmp.seed)
338 			tvgettime(&tv);
339 		tmp.key = tmp.rng * key + tv.tv_nsec;
340 		sfsprintf(keybuf, sizeof(keybuf), "%07.7.62I*u%07.7.62I*u", sizeof(key), key, sizeof(tmp.key), tmp.key);
341 		sfsprintf(s, len, "%-.*s%s%-.*s", l, keybuf, z ? "." : "", r, keybuf + sizeof(keybuf) / 2);
342 		if (fdp)
343 		{
344 			if (directory)
345 			{
346 				if (!mkdir(b, tmp.mode|S_IXUSR))
347 				{
348 					if ((n = open(b, O_SEARCH)) >= 0)
349 					{
350 						*fdp = n;
351 						return b;
352 					}
353 					rmdir(b);
354 				}
355 			}
356 			else if ((n = open(b, O_CREAT|O_RDWR|O_EXCL|O_TEMPORARY, tmp.mode)) >= 0)
357 			{
358 				*fdp = n;
359 				return b;
360 			}
361 			switch (errno)
362 			{
363 			case EACCES:
364 				if (access(b, F_OK))
365 					goto nope;
366 				break;
367 			case ENOTDIR:
368 			case EROFS:
369 				goto nope;
370 			}
371 		}
372 		else if (tmp.nocheck)
373 			return b;
374 		else if (access(b, F_OK))
375 			switch (errno)
376 			{
377 			case ENOENT:
378 				return b;
379 			}
380 	}
381  nope:
382 	if (!buf)
383 		free(b);
384 	return 0;
385 }
386