1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1985-2011 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 <gsf@research.att.com>                  *
18 *                  David Korn <dgk@research.att.com>                   *
19 *                   Phong Vo <kpv@research.att.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>)==3
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  * if pfx contains trailing X's then it is a mktemp(3) template
48  * otherwise only first 5 chars of pfx are used
49  * if fdp!=0 then the path is opened O_EXCL and *fdp is the open fd
50  * malloc'd space returned by successful pathtemp() calls
51  * must be freed by the caller
52  *
53  * generated names are pseudo-randomized to avoid both
54  * collisions and predictions (same alg in sfio/sftmp.c)
55  *
56  * / as first pfx char provides tmp file generation control
57  * 0 returned for unknown ops
58  *
59  *	/cycle		dir specifies TMPPATH cycle control
60  *		automatic	(default) cycled with each tmp file
61  *		manual		cycled by application with dir=(nil)
62  *		(nil)		cycle TMPPATH
63  *	/prefix		dir specifies the default prefix (default ast)
64  *	/private	private file/dir modes
65  *	/public		public file/dir modes
66  *	/seed		dir specifies pseudo-random generator seed
67  *			0 or "0" to re-initialize
68  *	/TMPPATH	dir overrides the env value
69  *	/TMPDIR		dir overrides the env value
70  */
71 
72 #include <ast.h>
73 #include <ls.h>
74 #include <tv.h>
75 #include <tm.h>
76 
77 #define ATTEMPT		10
78 
79 #define TMP_ENV		"TMPDIR"
80 #define TMP_PATH_ENV	"TMPPATH"
81 #define TMP1		"/tmp"
82 #define TMP2		"/usr/tmp"
83 
84 #define VALID(d)	(*(d)&&!eaccess(d,W_OK|X_OK))
85 
86 static struct
87 {
88 	mode_t		mode;
89 	char**		vec;
90 	char**		dir;
91 	uint32_t	key;
92 	uint32_t	rng;
93 	pid_t		pid;
94 	int		manual;
95 	int		seed;
96 	char*		pfx;
97 	char*		tmpdir;
98 	char*		tmppath;
99 } tmp = { S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH };
100 
101 char*
pathtemp(char * buf,size_t len,const char * dir,const char * pfx,int * fdp)102 pathtemp(char* buf, size_t len, const char* dir, const char* pfx, int* fdp)
103 {
104 	register char*		d;
105 	register char*		b;
106 	register char*		s;
107 	register char*		x;
108 	uint32_t		key;
109 	int			m;
110 	int			n;
111 	int			l;
112 	int			r;
113 	int			z;
114 	int			attempt;
115 	Tv_t			tv;
116 	char			keybuf[16];
117 
118 	if (pfx && *pfx == '/')
119 	{
120 		pfx++;
121 		if (streq(pfx, "cycle"))
122 		{
123 			if (!dir)
124 			{
125 				tmp.manual = 1;
126 				if (tmp.dir && !*tmp.dir++)
127 					tmp.dir = tmp.vec;
128 			}
129 			else
130 				tmp.manual = streq(dir, "manual");
131 			return (char*)pfx;
132 		}
133 		else if (streq(pfx, "prefix"))
134 		{
135 			if (tmp.pfx)
136 				free(tmp.pfx);
137 			tmp.pfx = dir ? strdup(dir) : (char*)0;
138 			return (char*)pfx;
139 		}
140 		else if (streq(pfx, "private"))
141 		{
142 			tmp.mode = S_IRUSR|S_IWUSR;
143 			return (char*)pfx;
144 		}
145 		else if (streq(pfx, "public"))
146 		{
147 			tmp.mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
148 			return (char*)pfx;
149 		}
150 		else if (streq(pfx, "seed"))
151 		{
152 			tmp.key = (tmp.seed = (tmp.rng = dir ? (uint32_t)strtoul(dir, NiL, 0) : (uint32_t)1) != 0)? (uint32_t)0x63c63cd9L : 0;
153 			return (char*)pfx;
154 		}
155 		else if (streq(pfx, TMP_ENV))
156 		{
157 			if (tmp.vec)
158 			{
159 				free(tmp.vec);
160 				tmp.vec = 0;
161 			}
162 			if (tmp.tmpdir)
163 				free(tmp.tmpdir);
164 			tmp.tmpdir = dir ? strdup(dir) : (char*)0;
165 			return (char*)pfx;
166 		}
167 		else if (streq(pfx, TMP_PATH_ENV))
168 		{
169 			if (tmp.vec)
170 			{
171 				free(tmp.vec);
172 				tmp.vec = 0;
173 			}
174 			if (tmp.tmppath)
175 				free(tmp.tmppath);
176 			tmp.tmppath = dir ? strdup(dir) : (char*)0;
177 			return (char*)pfx;
178 		}
179 		return 0;
180 	}
181 	if (tmp.seed)
182 		tv.tv_nsec = 0;
183 	else
184 		tvgettime(&tv);
185 	if (!(d = (char*)dir) || *d && eaccess(d, W_OK|X_OK))
186 	{
187 		if (!tmp.vec)
188 		{
189 			if ((x = tmp.tmppath) || (x = getenv(TMP_PATH_ENV)))
190 			{
191 				n = 2;
192 				s = x;
193 				while (s = strchr(s, ':'))
194 				{
195 					s++;
196 					n++;
197 				}
198 				if (!(tmp.vec = newof(0, char*, n, strlen(x) + 1)))
199 					return 0;
200 				tmp.dir = tmp.vec;
201 				x = strcpy((char*)(tmp.dir + n), x);
202 				*tmp.dir++ = x;
203 				while (x = strchr(x, ':'))
204 				{
205 					*x++ = 0;
206 					if (!VALID(*(tmp.dir - 1)))
207 						tmp.dir--;
208 					*tmp.dir++ = x;
209 				}
210 				if (!VALID(*(tmp.dir - 1)))
211 					tmp.dir--;
212 				*tmp.dir = 0;
213 			}
214 			else
215 			{
216 				if (((d = tmp.tmpdir) || (d = getenv(TMP_ENV))) && !VALID(d))
217 					d = 0;
218 				if (!(tmp.vec = newof(0, char*, 2, d ? (strlen(d) + 1) : 0)))
219 					return 0;
220 				if (d)
221 					*tmp.vec = strcpy((char*)(tmp.vec + 2), d);
222 			}
223 			tmp.dir = tmp.vec;
224 		}
225 		if (!(d = *tmp.dir++))
226 		{
227 			tmp.dir = tmp.vec;
228 			d = *tmp.dir++;
229 		}
230 		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))
231 			return 0;
232 	}
233 	if (!len)
234 		len = PATH_MAX;
235 	len--;
236 	if (!(b = buf) && !(b = newof(0, char, len, 1)))
237 		return 0;
238 	z = 0;
239 	if (!pfx && !(pfx = tmp.pfx))
240 		pfx = "ast";
241 	m = strlen(pfx);
242 	if (buf && dir && (buf == (char*)dir && (buf + strlen(buf) + 1) == (char*)pfx || buf == (char*)pfx && !*dir) && !strcmp((char*)pfx + m + 1, "XXXXX"))
243 	{
244 		d = (char*)dir;
245 		len = m += strlen(d) + 8;
246 		l = 3;
247 		r = 3;
248 	}
249 	else if (*pfx && pfx[m - 1] == 'X')
250 	{
251 		for (l = m; l && pfx[l - 1] == 'X'; l--);
252 		r = m - l;
253 		m = l;
254 		l = r / 2;
255 		r -= l;
256 	}
257 	else if (strchr(pfx, '.'))
258 	{
259 		m = 5;
260 		l = 3;
261 		r = 3;
262 	}
263 	else
264 	{
265 		z = '.';
266 		m = 5;
267 		l = 2;
268 		r = 3;
269 	}
270 	x = b + len;
271 	s = b;
272 	if (d)
273 	{
274 		while (s < x && (n = *d++))
275 			*s++ = n;
276 		if (s < x && s > b && *(s - 1) != '/')
277 			*s++ = '/';
278 	}
279 	if ((x - s) > m)
280 		x = s + m;
281 	while (s < x && (n = *pfx++))
282 	{
283 		if (n == '/' || n == '\\' || n == z)
284 			n = '_';
285 		*s++ = n;
286 	}
287 	*s = 0;
288 	len -= (s - b);
289 	for (attempt = 0; attempt < ATTEMPT; attempt++)
290 	{
291 		if (!tmp.rng || !tmp.seed && (attempt || tmp.pid != getpid()))
292 		{
293 			register int	r;
294 
295 			/*
296 			 * get a quasi-random coefficient
297 			 */
298 
299 			tmp.pid = getpid();
300 			tmp.rng = (uint32_t)tmp.pid * ((uint32_t)time(NiL) ^ (((uint32_t)integralof(&attempt)) >> 3) ^ (((uint32_t)integralof(tmp.dir)) >> 3));
301 			if (!tmp.key)
302 				tmp.key = (tmp.rng >> 16) | ((tmp.rng & 0xffff) << 16);
303 			tmp.rng ^= tmp.key;
304 
305 			/*
306 			 * Knuth vol.2, page.16, Thm.A
307 			 */
308 
309 			if ((r = (tmp.rng - 1) & 03))
310 				tmp.rng += 4 - r;
311 		}
312 
313 		/*
314 		 * generate a pseudo-random name
315 		 */
316 
317 		key = tmp.rng * tmp.key + tv.tv_nsec;
318 		if (!tmp.seed)
319 			tvgettime(&tv);
320 		tmp.key = tmp.rng * key + tv.tv_nsec;
321 		sfsprintf(keybuf, sizeof(keybuf), "%07.7.32I*u%07.7.32I*u", sizeof(key), key, sizeof(tmp.key), tmp.key);
322 		sfsprintf(s, len, "%-.*s%s%-.*s", l, keybuf, z ? "." : "", r, keybuf + sizeof(keybuf) / 2);
323 		if (fdp)
324 		{
325 			if ((n = open(b, O_CREAT|O_RDWR|O_EXCL|O_TEMPORARY, tmp.mode)) >= 0)
326 			{
327 				*fdp = n;
328 				return b;
329 			}
330 		}
331 		else if (access(b, F_OK))
332 			return b;
333 	}
334 	if (!buf)
335 		free(b);
336 	return 0;
337 }
338