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