xref: /titanic_50/usr/src/lib/libast/common/path/pathtemp.c (revision bf545727d6506df2bed889b1689857eeb24fe524)
1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 1985-2008 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                  Common Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *            http://www.opensource.org/licenses/cpl1.0.txt             *
11 *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
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  *
43  * if buf==0 then space is malloc'd
44  * buf size is size
45  * dir and pfx may be 0
46  * only first 5 chars of pfx are used
47  * if fdp!=0 then the path is opened O_EXCL and *fdp is the open fd
48  * malloc'd space returned by successful pathtemp() calls
49  * must be freed by the caller
50  *
51  * generated names are pseudo-randomized to avoid both
52  * collisions and predictions (same alg in sfio/sftmp.c)
53  *
54  * / as first pfx char provides tmp file generation control
55  * 0 returned for unknown ops
56  *
57  *	/cycle		dir specifies TMPPATH cycle control
58  *		automatic	(default) cycled with each tmp file
59  *		manual		cycled by application with dir=(nil)
60  *		(nil)		cycle TMPPATH
61  *	/prefix		dir specifies the default prefix (default ast)
62  *	/TMPPATH	dir overrides the env value
63  *	/TMPDIR		dir overrides the env value
64  */
65 
66 #include <ast.h>
67 #include <ls.h>
68 #include <tm.h>
69 
70 #define ATTEMPT		10
71 
72 #define TMP_ENV		"TMPDIR"
73 #define TMP_PATH_ENV	"TMPPATH"
74 #define TMP1		"/tmp"
75 #define TMP2		"/usr/tmp"
76 
77 #define VALID(d)	(*(d)&&!eaccess(d,W_OK|X_OK))
78 
79 static struct
80 {
81 	mode_t		mode;
82 	char**		vec;
83 	char**		dir;
84 	unsigned long	key;
85 	unsigned long	rng;
86 	pid_t		pid;
87 	int		manual;
88 	char*		pfx;
89 	char*		tmpdir;
90 	char*		tmppath;
91 } tmp = { S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH };
92 
93 char*
94 pathtemp(char* buf, size_t len, const char* dir, const char* pfx, int* fdp)
95 {
96 	register char*		d;
97 	register char*		b;
98 	register char*		s;
99 	register char*		x;
100 	char*			fmt;
101 	int			m;
102 	int			n;
103 	int			z;
104 	int			attempt;
105 
106 	if (pfx && *pfx == '/')
107 	{
108 		pfx++;
109 		if (streq(pfx, "cycle"))
110 		{
111 			if (!dir)
112 			{
113 				tmp.manual = 1;
114 				if (tmp.dir && !*tmp.dir++)
115 					tmp.dir = tmp.vec;
116 			}
117 			else
118 				tmp.manual = streq(dir, "manual");
119 			return (char*)pfx;
120 		}
121 		else if (streq(pfx, "prefix"))
122 		{
123 			if (tmp.pfx)
124 				free(tmp.pfx);
125 			tmp.pfx = dir ? strdup(dir) : (char*)0;
126 			return (char*)pfx;
127 		}
128 		else if (streq(pfx, "private"))
129 			tmp.mode = S_IRUSR|S_IWUSR;
130 		else if (streq(pfx, "public"))
131 			tmp.mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH;
132 		else if (streq(pfx, TMP_ENV))
133 		{
134 			if (tmp.vec)
135 			{
136 				free(tmp.vec);
137 				tmp.vec = 0;
138 			}
139 			if (tmp.tmpdir)
140 				free(tmp.tmpdir);
141 			tmp.tmpdir = dir ? strdup(dir) : (char*)0;
142 			return (char*)pfx;
143 		}
144 		else if (streq(pfx, TMP_PATH_ENV))
145 		{
146 			if (tmp.vec)
147 			{
148 				free(tmp.vec);
149 				tmp.vec = 0;
150 			}
151 			if (tmp.tmppath)
152 				free(tmp.tmppath);
153 			tmp.tmppath = dir ? strdup(dir) : (char*)0;
154 			return (char*)pfx;
155 		}
156 		return 0;
157 	}
158 	if (!(d = (char*)dir) || *d && eaccess(d, W_OK|X_OK))
159 	{
160 		if (!tmp.vec)
161 		{
162 			if ((x = tmp.tmppath) || (x = getenv(TMP_PATH_ENV)))
163 			{
164 				n = 2;
165 				s = x;
166 				while (s = strchr(s, ':'))
167 				{
168 					s++;
169 					n++;
170 				}
171 				if (!(tmp.vec = newof(0, char*, n, strlen(x) + 1)))
172 					return 0;
173 				tmp.dir = tmp.vec;
174 				x = strcpy((char*)(tmp.dir + n), x);
175 				*tmp.dir++ = x;
176 				while (x = strchr(x, ':'))
177 				{
178 					*x++ = 0;
179 					if (!VALID(*(tmp.dir - 1)))
180 						tmp.dir--;
181 					*tmp.dir++ = x;
182 				}
183 				if (!VALID(*(tmp.dir - 1)))
184 					tmp.dir--;
185 				*tmp.dir = 0;
186 			}
187 			else
188 			{
189 				if (((d = tmp.tmpdir) || (d = getenv(TMP_ENV))) && !VALID(d))
190 					d = 0;
191 				if (!(tmp.vec = newof(0, char*, 2, d ? (strlen(d) + 1) : 0)))
192 					return 0;
193 				if (d)
194 					*tmp.vec = strcpy((char*)(tmp.vec + 2), d);
195 			}
196 			tmp.dir = tmp.vec;
197 		}
198 		if (!(d = *tmp.dir++))
199 		{
200 			tmp.dir = tmp.vec;
201 			d = *tmp.dir++;
202 		}
203 		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))
204 			return 0;
205 	}
206 	if (!len)
207 		len = PATH_MAX;
208 	len--;
209 	if (!(b = buf) && !(b = newof(0, char, len, 1)))
210 		return 0;
211 	if (buf && dir && pfx && (buf == (char*)dir && (buf + strlen(buf) + 1) == (char*)pfx || buf == (char*)pfx && !*dir) && !strcmp((char*)pfx + strlen(pfx) + 1, "XXXXX"))
212 	{
213 		z = 0;
214 		d = (char*)dir;
215 		len = m = strlen(d) + strlen(pfx) + 8;
216 		fmt = "%03.3.32lu%03.3.32lu";
217 	}
218 	else
219 	{
220 		z = '.';
221 		m = 5;
222 		fmt = "%02.2.32lu.%03.3.32lu";
223 	}
224 	x = b + len;
225 	s = b;
226 	if (d)
227 	{
228 		while (s < x && (n = *d++))
229 			*s++ = n;
230 		if (s < x && s > b && *(s - 1) != '/')
231 			*s++ = '/';
232 	}
233 	if (!pfx && !(pfx = tmp.pfx))
234 		pfx = "ast";
235 	if ((x - s) > m)
236 		x = s + m;
237 	while (s < x && (n = *pfx++))
238 	{
239 		if (n == '/' || n == '\\' || n == z)
240 			n = '_';
241 		*s++ = n;
242 	}
243 	*s = 0;
244 	len -= (s - b);
245 	for (attempt = 0; attempt < ATTEMPT; attempt++)
246 	{
247 		if (!tmp.rng || attempt || tmp.pid != getpid())
248 		{
249 			register int	r;
250 
251 			/*
252 			 * get a quasi-random coefficient
253 			 */
254 
255 			tmp.pid = getpid();
256 			tmp.rng = (unsigned long)tmp.pid * ((unsigned long)time(NiL) ^ (((unsigned long)(&attempt)) >> 3) ^ (((unsigned long)tmp.dir) >> 3));
257 			if (!tmp.key)
258 				tmp.key = (tmp.rng >> 16) | ((tmp.rng & 0xffff) << 16);
259 			tmp.rng ^= tmp.key;
260 
261 			/*
262 			 * Knuth vol.2, page.16, Thm.A
263 			 */
264 
265 			if ((r = (tmp.rng - 1) & 03))
266 				tmp.rng += 4 - r;
267 		}
268 
269 		/*
270 		 * generate a pseudo-random name
271 		 */
272 
273 		tmp.key = tmp.rng * tmp.key + 987654321L;
274 		sfsprintf(s, len, fmt, (tmp.key >> 15) & 0x7fff, tmp.key & 0x7fff);
275 		if (fdp)
276 		{
277 			if ((n = open(b, O_CREAT|O_RDWR|O_EXCL|O_TEMPORARY, tmp.mode)) >= 0)
278 			{
279 				*fdp = n;
280 				return b;
281 			}
282 		}
283 		else if (access(b, F_OK))
284 			return b;
285 	}
286 	if (!buf)
287 		free(b);
288 	return 0;
289 }
290