1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1985-2010 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 * 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 = (uintptr_t)tmp.pid * ((uintptr_t)time(NiL) ^ (((uintptr_t)(&attempt)) >> 3) ^ (((uintptr_t)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