xref: /onnv-gate/usr/src/lib/libast/common/path/pathtemp.c (revision 12068:08a39a083754)
14887Schin /***********************************************************************
24887Schin *                                                                      *
34887Schin *               This software is part of the ast package               *
4*12068SRoger.Faulkner@Oracle.COM *          Copyright (c) 1985-2010 AT&T Intellectual Property          *
54887Schin *                      and is licensed under the                       *
64887Schin *                  Common Public License, Version 1.0                  *
78462SApril.Chin@Sun.COM *                    by AT&T Intellectual Property                     *
84887Schin *                                                                      *
94887Schin *                A copy of the License is available at                 *
104887Schin *            http://www.opensource.org/licenses/cpl1.0.txt             *
114887Schin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
124887Schin *                                                                      *
134887Schin *              Information and Software Systems Research               *
144887Schin *                            AT&T Research                             *
154887Schin *                           Florham Park NJ                            *
164887Schin *                                                                      *
174887Schin *                 Glenn Fowler <gsf@research.att.com>                  *
184887Schin *                  David Korn <dgk@research.att.com>                   *
194887Schin *                   Phong Vo <kpv@research.att.com>                    *
204887Schin *                                                                      *
214887Schin ***********************************************************************/
224887Schin #pragma prototyped
234887Schin /*
244887Schin  * AT&T Research
254887Schin  *
264887Schin  * generate a temp file / name
274887Schin  *
284887Schin  *	[<dir>/][<pfx>]<bas>.<suf>
294887Schin  *
304887Schin  * length(<pfx>)<=5
314887Schin  * length(<bas>)==3
324887Schin  * length(<suf>)==3
334887Schin  *
344887Schin  *	pathtmp(a,b,c,d)	pathtemp(a,L_tmpnam,b,c,0)
354887Schin  *	tmpfile()		char*p=pathtemp(0,0,0,"tf",&sp);
364887Schin  *				remove(p);
374887Schin  *				free(p)
384887Schin  *	tmpnam(0)		static char p[L_tmpnam];
394887Schin  *				pathtemp(p,sizeof(p),0,"tn",0)
404887Schin  *	tmpnam(p)		pathtemp(p,L_tmpnam,0,"tn",0)
414887Schin  *	tempnam(d,p)		pathtemp(0,d,p,0)
42*12068SRoger.Faulkner@Oracle.COM  *	mktemp(p)		pathtemp(0,0,p,0)
434887Schin  *
444887Schin  * if buf==0 then space is malloc'd
454887Schin  * buf size is size
464887Schin  * dir and pfx may be 0
47*12068SRoger.Faulkner@Oracle.COM  * if pfx contains trailing X's then it is a mktemp(3) template
48*12068SRoger.Faulkner@Oracle.COM  * otherwise only first 5 chars of pfx are used
494887Schin  * if fdp!=0 then the path is opened O_EXCL and *fdp is the open fd
504887Schin  * malloc'd space returned by successful pathtemp() calls
514887Schin  * must be freed by the caller
524887Schin  *
534887Schin  * generated names are pseudo-randomized to avoid both
544887Schin  * collisions and predictions (same alg in sfio/sftmp.c)
554887Schin  *
564887Schin  * / as first pfx char provides tmp file generation control
574887Schin  * 0 returned for unknown ops
584887Schin  *
594887Schin  *	/cycle		dir specifies TMPPATH cycle control
604887Schin  *		automatic	(default) cycled with each tmp file
614887Schin  *		manual		cycled by application with dir=(nil)
624887Schin  *		(nil)		cycle TMPPATH
634887Schin  *	/prefix		dir specifies the default prefix (default ast)
64*12068SRoger.Faulkner@Oracle.COM  *	/private	private file/dir modes
65*12068SRoger.Faulkner@Oracle.COM  *	/public		public file/dir modes
66*12068SRoger.Faulkner@Oracle.COM  *	/seed		dir specifies pseudo-random generator seed
67*12068SRoger.Faulkner@Oracle.COM  *			0 or "0" to re-initialize
684887Schin  *	/TMPPATH	dir overrides the env value
694887Schin  *	/TMPDIR		dir overrides the env value
704887Schin  */
714887Schin 
724887Schin #include <ast.h>
734887Schin #include <ls.h>
74*12068SRoger.Faulkner@Oracle.COM #include <tv.h>
754887Schin #include <tm.h>
764887Schin 
774887Schin #define ATTEMPT		10
784887Schin 
794887Schin #define TMP_ENV		"TMPDIR"
804887Schin #define TMP_PATH_ENV	"TMPPATH"
814887Schin #define TMP1		"/tmp"
824887Schin #define TMP2		"/usr/tmp"
834887Schin 
844887Schin #define VALID(d)	(*(d)&&!eaccess(d,W_OK|X_OK))
854887Schin 
864887Schin static struct
874887Schin {
884887Schin 	mode_t		mode;
894887Schin 	char**		vec;
904887Schin 	char**		dir;
91*12068SRoger.Faulkner@Oracle.COM 	uint32_t	key;
92*12068SRoger.Faulkner@Oracle.COM 	uint32_t	rng;
934887Schin 	pid_t		pid;
944887Schin 	int		manual;
95*12068SRoger.Faulkner@Oracle.COM 	int		seed;
964887Schin 	char*		pfx;
974887Schin 	char*		tmpdir;
984887Schin 	char*		tmppath;
994887Schin } tmp = { S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH };
1004887Schin 
1014887Schin char*
pathtemp(char * buf,size_t len,const char * dir,const char * pfx,int * fdp)1024887Schin pathtemp(char* buf, size_t len, const char* dir, const char* pfx, int* fdp)
1034887Schin {
1044887Schin 	register char*		d;
1054887Schin 	register char*		b;
1064887Schin 	register char*		s;
1074887Schin 	register char*		x;
108*12068SRoger.Faulkner@Oracle.COM 	uint32_t		key;
1094887Schin 	int			m;
1104887Schin 	int			n;
111*12068SRoger.Faulkner@Oracle.COM 	int			l;
112*12068SRoger.Faulkner@Oracle.COM 	int			r;
1134887Schin 	int			z;
1144887Schin 	int			attempt;
115*12068SRoger.Faulkner@Oracle.COM 	Tv_t			tv;
116*12068SRoger.Faulkner@Oracle.COM 	char			keybuf[16];
1174887Schin 
1184887Schin 	if (pfx && *pfx == '/')
1194887Schin 	{
1204887Schin 		pfx++;
1214887Schin 		if (streq(pfx, "cycle"))
1224887Schin 		{
1234887Schin 			if (!dir)
1244887Schin 			{
1254887Schin 				tmp.manual = 1;
1264887Schin 				if (tmp.dir && !*tmp.dir++)
1274887Schin 					tmp.dir = tmp.vec;
1284887Schin 			}
1294887Schin 			else
1304887Schin 				tmp.manual = streq(dir, "manual");
1314887Schin 			return (char*)pfx;
1324887Schin 		}
1334887Schin 		else if (streq(pfx, "prefix"))
1344887Schin 		{
1354887Schin 			if (tmp.pfx)
1364887Schin 				free(tmp.pfx);
1374887Schin 			tmp.pfx = dir ? strdup(dir) : (char*)0;
1384887Schin 			return (char*)pfx;
1394887Schin 		}
1404887Schin 		else if (streq(pfx, "private"))
141*12068SRoger.Faulkner@Oracle.COM 		{
1424887Schin 			tmp.mode = S_IRUSR|S_IWUSR;
143*12068SRoger.Faulkner@Oracle.COM 			return (char*)pfx;
144*12068SRoger.Faulkner@Oracle.COM 		}
1454887Schin 		else if (streq(pfx, "public"))
146*12068SRoger.Faulkner@Oracle.COM 		{
1474887Schin 			tmp.mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
148*12068SRoger.Faulkner@Oracle.COM 			return (char*)pfx;
149*12068SRoger.Faulkner@Oracle.COM 		}
150*12068SRoger.Faulkner@Oracle.COM 		else if (streq(pfx, "seed"))
151*12068SRoger.Faulkner@Oracle.COM 		{
152*12068SRoger.Faulkner@Oracle.COM 			tmp.key = (tmp.seed = (tmp.rng = dir ? (uint32_t)strtoul(dir, NiL, 0) : (uint32_t)1) != 0)? (uint32_t)0x63c63cd9L : 0;
153*12068SRoger.Faulkner@Oracle.COM 			return (char*)pfx;
154*12068SRoger.Faulkner@Oracle.COM 		}
1554887Schin 		else if (streq(pfx, TMP_ENV))
1564887Schin 		{
1574887Schin 			if (tmp.vec)
1584887Schin 			{
1594887Schin 				free(tmp.vec);
1604887Schin 				tmp.vec = 0;
1614887Schin 			}
1624887Schin 			if (tmp.tmpdir)
1634887Schin 				free(tmp.tmpdir);
1644887Schin 			tmp.tmpdir = dir ? strdup(dir) : (char*)0;
1654887Schin 			return (char*)pfx;
1664887Schin 		}
1674887Schin 		else if (streq(pfx, TMP_PATH_ENV))
1684887Schin 		{
1694887Schin 			if (tmp.vec)
1704887Schin 			{
1714887Schin 				free(tmp.vec);
1724887Schin 				tmp.vec = 0;
1734887Schin 			}
1744887Schin 			if (tmp.tmppath)
1754887Schin 				free(tmp.tmppath);
1764887Schin 			tmp.tmppath = dir ? strdup(dir) : (char*)0;
1774887Schin 			return (char*)pfx;
1784887Schin 		}
1794887Schin 		return 0;
1804887Schin 	}
181*12068SRoger.Faulkner@Oracle.COM 	if (tmp.seed)
182*12068SRoger.Faulkner@Oracle.COM 		tv.tv_nsec = 0;
183*12068SRoger.Faulkner@Oracle.COM 	else
184*12068SRoger.Faulkner@Oracle.COM 		tvgettime(&tv);
1854887Schin 	if (!(d = (char*)dir) || *d && eaccess(d, W_OK|X_OK))
1864887Schin 	{
1874887Schin 		if (!tmp.vec)
1884887Schin 		{
1894887Schin 			if ((x = tmp.tmppath) || (x = getenv(TMP_PATH_ENV)))
1904887Schin 			{
1914887Schin 				n = 2;
1924887Schin 				s = x;
1934887Schin 				while (s = strchr(s, ':'))
1944887Schin 				{
1954887Schin 					s++;
1964887Schin 					n++;
1974887Schin 				}
1984887Schin 				if (!(tmp.vec = newof(0, char*, n, strlen(x) + 1)))
1994887Schin 					return 0;
2004887Schin 				tmp.dir = tmp.vec;
2014887Schin 				x = strcpy((char*)(tmp.dir + n), x);
2024887Schin 				*tmp.dir++ = x;
2034887Schin 				while (x = strchr(x, ':'))
2044887Schin 				{
2054887Schin 					*x++ = 0;
2064887Schin 					if (!VALID(*(tmp.dir - 1)))
2074887Schin 						tmp.dir--;
2084887Schin 					*tmp.dir++ = x;
2094887Schin 				}
2104887Schin 				if (!VALID(*(tmp.dir - 1)))
2114887Schin 					tmp.dir--;
2124887Schin 				*tmp.dir = 0;
2134887Schin 			}
2144887Schin 			else
2154887Schin 			{
2164887Schin 				if (((d = tmp.tmpdir) || (d = getenv(TMP_ENV))) && !VALID(d))
2174887Schin 					d = 0;
2184887Schin 				if (!(tmp.vec = newof(0, char*, 2, d ? (strlen(d) + 1) : 0)))
2194887Schin 					return 0;
2204887Schin 				if (d)
2214887Schin 					*tmp.vec = strcpy((char*)(tmp.vec + 2), d);
2224887Schin 			}
2234887Schin 			tmp.dir = tmp.vec;
2244887Schin 		}
2254887Schin 		if (!(d = *tmp.dir++))
2264887Schin 		{
2274887Schin 			tmp.dir = tmp.vec;
2284887Schin 			d = *tmp.dir++;
2294887Schin 		}
2304887Schin 		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))
2314887Schin 			return 0;
2324887Schin 	}
2334887Schin 	if (!len)
2344887Schin 		len = PATH_MAX;
2354887Schin 	len--;
2364887Schin 	if (!(b = buf) && !(b = newof(0, char, len, 1)))
2374887Schin 		return 0;
238*12068SRoger.Faulkner@Oracle.COM 	z = 0;
239*12068SRoger.Faulkner@Oracle.COM 	if (!pfx && !(pfx = tmp.pfx))
240*12068SRoger.Faulkner@Oracle.COM 		pfx = "ast";
241*12068SRoger.Faulkner@Oracle.COM 	m = strlen(pfx);
242*12068SRoger.Faulkner@Oracle.COM 	if (buf && dir && (buf == (char*)dir && (buf + strlen(buf) + 1) == (char*)pfx || buf == (char*)pfx && !*dir) && !strcmp((char*)pfx + m + 1, "XXXXX"))
2434887Schin 	{
2444887Schin 		d = (char*)dir;
245*12068SRoger.Faulkner@Oracle.COM 		len = m += strlen(d) + 8;
246*12068SRoger.Faulkner@Oracle.COM 		l = 3;
247*12068SRoger.Faulkner@Oracle.COM 		r = 3;
248*12068SRoger.Faulkner@Oracle.COM 	}
249*12068SRoger.Faulkner@Oracle.COM 	else if (*pfx && pfx[m - 1] == 'X')
250*12068SRoger.Faulkner@Oracle.COM 	{
251*12068SRoger.Faulkner@Oracle.COM 		for (l = m; l && pfx[l - 1] == 'X'; l--);
252*12068SRoger.Faulkner@Oracle.COM 		r = m - l;
253*12068SRoger.Faulkner@Oracle.COM 		m = l;
254*12068SRoger.Faulkner@Oracle.COM 		l = r / 2;
255*12068SRoger.Faulkner@Oracle.COM 		r -= l;
256*12068SRoger.Faulkner@Oracle.COM 	}
257*12068SRoger.Faulkner@Oracle.COM 	else if (strchr(pfx, '.'))
258*12068SRoger.Faulkner@Oracle.COM 	{
259*12068SRoger.Faulkner@Oracle.COM 		m = 5;
260*12068SRoger.Faulkner@Oracle.COM 		l = 3;
261*12068SRoger.Faulkner@Oracle.COM 		r = 3;
2624887Schin 	}
2634887Schin 	else
2644887Schin 	{
2654887Schin 		z = '.';
2664887Schin 		m = 5;
267*12068SRoger.Faulkner@Oracle.COM 		l = 2;
268*12068SRoger.Faulkner@Oracle.COM 		r = 3;
2694887Schin 	}
2704887Schin 	x = b + len;
2714887Schin 	s = b;
2724887Schin 	if (d)
2734887Schin 	{
2744887Schin 		while (s < x && (n = *d++))
2754887Schin 			*s++ = n;
2764887Schin 		if (s < x && s > b && *(s - 1) != '/')
2774887Schin 			*s++ = '/';
2784887Schin 	}
2794887Schin 	if ((x - s) > m)
2804887Schin 		x = s + m;
2814887Schin 	while (s < x && (n = *pfx++))
2824887Schin 	{
2834887Schin 		if (n == '/' || n == '\\' || n == z)
2844887Schin 			n = '_';
2854887Schin 		*s++ = n;
2864887Schin 	}
2874887Schin 	*s = 0;
2884887Schin 	len -= (s - b);
2894887Schin 	for (attempt = 0; attempt < ATTEMPT; attempt++)
2904887Schin 	{
291*12068SRoger.Faulkner@Oracle.COM 		if (!tmp.rng || !tmp.seed && (attempt || tmp.pid != getpid()))
2924887Schin 		{
2934887Schin 			register int	r;
2944887Schin 
2954887Schin 			/*
2964887Schin 			 * get a quasi-random coefficient
2974887Schin 			 */
2984887Schin 
2994887Schin 			tmp.pid = getpid();
300*12068SRoger.Faulkner@Oracle.COM 			tmp.rng = (uintptr_t)tmp.pid * ((uintptr_t)time(NiL) ^ (((uintptr_t)(&attempt)) >> 3) ^ (((uintptr_t)tmp.dir) >> 3));
3014887Schin 			if (!tmp.key)
3024887Schin 				tmp.key = (tmp.rng >> 16) | ((tmp.rng & 0xffff) << 16);
3034887Schin 			tmp.rng ^= tmp.key;
3044887Schin 
3054887Schin 			/*
3064887Schin 			 * Knuth vol.2, page.16, Thm.A
3074887Schin 			 */
3084887Schin 
3094887Schin 			if ((r = (tmp.rng - 1) & 03))
3104887Schin 				tmp.rng += 4 - r;
3114887Schin 		}
3124887Schin 
3134887Schin 		/*
3144887Schin 		 * generate a pseudo-random name
3154887Schin 		 */
3164887Schin 
317*12068SRoger.Faulkner@Oracle.COM 		key = tmp.rng * tmp.key + tv.tv_nsec;
318*12068SRoger.Faulkner@Oracle.COM 		if (!tmp.seed)
319*12068SRoger.Faulkner@Oracle.COM 			tvgettime(&tv);
320*12068SRoger.Faulkner@Oracle.COM 		tmp.key = tmp.rng * key + tv.tv_nsec;
321*12068SRoger.Faulkner@Oracle.COM 		sfsprintf(keybuf, sizeof(keybuf), "%07.7.32I*u%07.7.32I*u", sizeof(key), key, sizeof(tmp.key), tmp.key);
322*12068SRoger.Faulkner@Oracle.COM 		sfsprintf(s, len, "%-.*s%s%-.*s", l, keybuf, z ? "." : "", r, keybuf + sizeof(keybuf) / 2);
3234887Schin 		if (fdp)
3244887Schin 		{
3254887Schin 			if ((n = open(b, O_CREAT|O_RDWR|O_EXCL|O_TEMPORARY, tmp.mode)) >= 0)
3264887Schin 			{
3274887Schin 				*fdp = n;
3284887Schin 				return b;
3294887Schin 			}
3304887Schin 		}
3314887Schin 		else if (access(b, F_OK))
3324887Schin 			return b;
3334887Schin 	}
3344887Schin 	if (!buf)
3354887Schin 		free(b);
3364887Schin 	return 0;
3374887Schin }
338