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* 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