1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1985-2007 AT&T Knowledge Ventures * 5 * and is licensed under the * 6 * Common Public License, Version 1.0 * 7 * by AT&T Knowledge Ventures * 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 #include "sfhdr.h" 23 24 /* Create a temporary stream for read/write. 25 ** The stream is originally created as a memory-resident stream. 26 ** When this memory is exceeded, a real temp file will be created. 27 ** The temp file creation sequence is somewhat convoluted so that 28 ** pool/stack/discipline will work correctly. 29 ** 30 ** Written by David Korn and Kiem-Phong Vo. 31 */ 32 33 #if _tmp_rmfail 34 35 /* File not removable while there is an open file descriptor. 36 ** To ensure that temp files are properly removed, we need: 37 ** 1. A discipline to remove a file when the corresponding stream is closed. 38 ** Care must be taken to close the file descriptor before removing the 39 ** file because systems such as NT do not allow file removal while 40 ** there is an open file handle. 41 ** 2. An atexit() function is set up to close temp files when process exits. 42 ** 3. On systems with O_TEMPORARY (e.g., NT), this is used to further ensure 43 ** that temp files will be removed after the last handle is closed. 44 */ 45 46 typedef struct _file_s File_t; 47 struct _file_s 48 { File_t* next; /* link list */ 49 Sfio_t* f; /* associated stream */ 50 char name[1]; /* temp file name */ 51 }; 52 53 static File_t* File; /* list pf temp files */ 54 55 #if __STD_C 56 static int _tmprmfile(Sfio_t* f, int type, Void_t* val, Sfdisc_t* disc) 57 #else 58 static int _tmprmfile(f, type, val, disc) 59 Sfio_t* f; 60 int type; 61 Void_t* val; 62 Sfdisc_t* disc; 63 #endif 64 { 65 reg File_t *ff, *last; 66 67 NOTUSED(val); 68 69 if(type == SF_DPOP) /* don't allow this to pop */ 70 return -1; 71 72 if(type == SF_CLOSING) 73 { 74 (void)vtmtxlock(_Sfmutex); 75 for(last = NIL(File_t*), ff = File; ff; last = ff, ff = ff->next) 76 if(ff->f == f) 77 break; 78 if(ff) 79 { if(!last) 80 File = ff->next; 81 else last->next = ff->next; 82 83 if(_Sfnotify) 84 (*_Sfnotify)(f,SF_CLOSING,f->file); 85 CLOSE(f->file); 86 f->file = -1; 87 while(sysremovef(ff->name) < 0 && errno == EINTR) 88 errno = 0; 89 90 free((Void_t*)ff); 91 } 92 (void)vtmtxunlock(_Sfmutex); 93 } 94 95 return 0; 96 } 97 98 #if __STD_C 99 static void _rmfiles(void) 100 #else 101 static void _rmfiles() 102 #endif 103 { reg File_t *ff, *next; 104 105 (void)vtmtxlock(_Sfmutex); 106 for(ff = File; ff; ff = next) 107 { next = ff->next; 108 _tmprmfile(ff->f, SF_CLOSING, NIL(Void_t*), ff->f->disc); 109 } 110 (void)vtmtxunlock(_Sfmutex); 111 } 112 113 static Sfdisc_t Rmdisc = 114 { NIL(Sfread_f), NIL(Sfwrite_f), NIL(Sfseek_f), _tmprmfile, NIL(Sfdisc_t*) }; 115 116 #endif /*_tmp_rmfail*/ 117 118 #if __STD_C 119 static int _rmtmp(Sfio_t* f, char* file) 120 #else 121 static int _rmtmp(f, file) 122 Sfio_t* f; 123 char* file; 124 #endif 125 { 126 #if _tmp_rmfail /* remove only when stream is closed */ 127 reg File_t* ff; 128 129 if(!File) 130 atexit(_rmfiles); 131 132 if(!(ff = (File_t*)malloc(sizeof(File_t)+strlen(file))) ) 133 return -1; 134 (void)vtmtxlock(_Sfmutex); 135 ff->f = f; 136 strcpy(ff->name,file); 137 ff->next = File; 138 File = ff; 139 (void)vtmtxunlock(_Sfmutex); 140 141 #else /* can remove now */ 142 while(sysremovef(file) < 0 && errno == EINTR) 143 errno = 0; 144 #endif 145 146 return 0; 147 } 148 149 #if !_PACKAGE_ast 150 #define TMPDFLT "/tmp" 151 static char **Tmppath, **Tmpcur; 152 153 #if __STD_C 154 char** _sfgetpath(char* path) 155 #else 156 char** _sfgetpath(path) 157 char* path; 158 #endif 159 { reg char *p, **dirs; 160 reg int n; 161 162 if(!(path = getenv(path)) ) 163 return NIL(char**); 164 165 for(p = path, n = 0;;) /* count number of directories */ 166 { while(*p == ':') 167 ++p; 168 if(*p == 0) 169 break; 170 n += 1; 171 while(*p && *p != ':') /* skip dir name */ 172 ++p; 173 } 174 if(n == 0 || !(dirs = (char**)malloc((n+1)*sizeof(char*))) ) 175 return NIL(char**); 176 if(!(p = (char*)malloc(strlen(path)+1)) ) 177 { free(dirs); 178 return NIL(char**); 179 } 180 strcpy(p,path); 181 for(n = 0;; ++n) 182 { while(*p == ':') 183 ++p; 184 if(*p == 0) 185 break; 186 dirs[n] = p; 187 while(*p && *p != ':') 188 ++p; 189 if(*p == ':') 190 *p++ = 0; 191 } 192 dirs[n] = NIL(char*); 193 194 return dirs; 195 } 196 197 #endif /*!_PACKAGE_ast*/ 198 199 #if __STD_C 200 static int _tmpfd(Sfio_t* f) 201 #else 202 static int _tmpfd(f) 203 Sfio_t* f; 204 #endif 205 { 206 reg char* file; 207 int fd; 208 209 #if _PACKAGE_ast 210 if(!(file = pathtemp(NiL,PATH_MAX,NiL,"sf",&fd))) 211 return -1; 212 _rmtmp(f, file); 213 free(file); 214 #else 215 int t; 216 217 /* set up path of dirs to create temp files */ 218 if(!Tmppath && !(Tmppath = _sfgetpath("TMPPATH")) ) 219 { if(!(Tmppath = (char**)malloc(2*sizeof(char*))) ) 220 return -1; 221 if(!(file = getenv("TMPDIR")) ) 222 file = TMPDFLT; 223 if(!(Tmppath[0] = (char*)malloc(strlen(file)+1)) ) 224 { free(Tmppath); 225 Tmppath = NIL(char**); 226 return -1; 227 } 228 strcpy(Tmppath[0],file); 229 Tmppath[1] = NIL(char*); 230 } 231 232 /* set current directory to create this temp file */ 233 if(Tmpcur) 234 Tmpcur += 1; 235 if(!Tmpcur || !Tmpcur[0]) 236 Tmpcur = Tmppath; 237 238 fd = -1; 239 for(t = 0; t < 10; ++t) 240 { /* compute a random name */ 241 static ulong Key, A; 242 if(A == 0 || t > 0) /* get a quasi-random coefficient */ 243 { reg int r; 244 A = (ulong)time(NIL(time_t*)) ^ (((ulong)(&t)) >> 3); 245 if(Key == 0) 246 Key = (A >> 16) | ((A&0xffff)<<16); 247 A ^= Key; 248 if((r = (A-1) & 03) != 0) /* Knuth vol.2, page.16, Thm.A */ 249 A += 4-r; 250 } 251 252 Key = A*Key + 987654321; 253 file = sfprints("%s/sf%3.3.32lu.%3.3.32lu", 254 Tmpcur[0], (Key>>15)&0x7fff, Key&0x7fff); 255 if(!file) 256 return -1; 257 #if _has_oflags 258 if((fd = sysopenf(file,O_RDWR|O_CREAT|O_EXCL|O_TEMPORARY,SF_CREATMODE)) >= 0) 259 break; 260 #else 261 if((fd = sysopenf(file,O_RDONLY)) >= 0) 262 { /* file already exists */ 263 CLOSE(fd); 264 fd = -1; 265 } 266 else if((fd = syscreatf(file,SF_CREATMODE)) >= 0) 267 { /* reopen for read and write */ 268 CLOSE(fd); 269 if((fd = sysopenf(file,O_RDWR)) >= 0) 270 break; 271 272 /* don't know what happened but must remove file */ 273 while(sysremovef(file) < 0 && errno == EINTR) 274 errno = 0; 275 } 276 #endif /* _has_oflags */ 277 } 278 if(fd >= 0) 279 _rmtmp(f, file); 280 #endif /* _PACKAGE_ast */ 281 return fd; 282 } 283 284 #if __STD_C 285 static int _tmpexcept(Sfio_t* f, int type, Void_t* val, Sfdisc_t* disc) 286 #else 287 static int _tmpexcept(f,type,val,disc) 288 Sfio_t* f; 289 int type; 290 Void_t* val; 291 Sfdisc_t* disc; 292 #endif 293 { 294 reg int fd, m; 295 reg Sfio_t* sf; 296 Sfio_t newf, savf; 297 void (*notifyf)_ARG_((Sfio_t*, int, int)); 298 299 NOTUSED(val); 300 301 /* the discipline needs to change only under the following exceptions */ 302 if(type != SF_WRITE && type != SF_SEEK && 303 type != SF_DPUSH && type != SF_DPOP && type != SF_DBUFFER) 304 return 0; 305 306 /* notify function */ 307 notifyf = _Sfnotify; 308 309 /* try to create the temp file */ 310 SFCLEAR(&newf,NIL(Vtmutex_t*)); 311 newf.flags = SF_STATIC; 312 newf.mode = SF_AVAIL; 313 314 if((fd = _tmpfd(f)) < 0 ) 315 return -1; 316 317 /* make sure that the notify function won't be called here since 318 we are only interested in creating the file, not the stream */ 319 _Sfnotify = 0; 320 sf = sfnew(&newf,NIL(Void_t*),(size_t)SF_UNBOUND,fd,SF_READ|SF_WRITE); 321 _Sfnotify = notifyf; 322 if(!sf) 323 return -1; 324 325 if(newf.mutex) /* don't need a mutex for this stream */ 326 { (void)vtmtxclrlock(newf.mutex); 327 (void)vtmtxclose(newf.mutex); 328 newf.mutex = NIL(Vtmutex_t*); 329 } 330 331 /* make sure that new stream has the same mode */ 332 if((m = f->flags&(SF_READ|SF_WRITE)) != (SF_READ|SF_WRITE)) 333 sfset(sf, ((~m)&(SF_READ|SF_WRITE)), 0); 334 sfset(sf, (f->mode&(SF_READ|SF_WRITE)), 1); 335 336 /* now remake the old stream into the new image */ 337 memcpy((Void_t*)(&savf), (Void_t*)f, sizeof(Sfio_t)); 338 memcpy((Void_t*)f, (Void_t*)sf, sizeof(Sfio_t)); 339 f->push = savf.push; 340 f->pool = savf.pool; 341 f->rsrv = savf.rsrv; 342 f->proc = savf.proc; 343 f->mutex = savf.mutex; 344 f->stdio = savf.stdio; 345 346 if(savf.data) 347 { SFSTRSIZE(&savf); 348 if(!(savf.flags&SF_MALLOC) ) 349 (void)sfsetbuf(f,(Void_t*)savf.data,savf.size); 350 if(savf.extent > 0) 351 (void)sfwrite(f,(Void_t*)savf.data,(size_t)savf.extent); 352 (void)sfseek(f,(Sfoff_t)(savf.next - savf.data),SEEK_SET); 353 if((savf.flags&SF_MALLOC) ) 354 free((Void_t*)savf.data); 355 } 356 357 /* announce change of status */ 358 if(notifyf) 359 (*notifyf)(f,SF_NEW,f->file); 360 361 f->disc = disc->disc; 362 363 /* erase all traces of newf */ 364 newf.data = newf.endb = newf.endr = newf.endw = NIL(uchar*); 365 newf.file = -1; 366 sfclose(&newf); 367 368 return 1; 369 } 370 371 #if __STD_C 372 Sfio_t* sftmp(reg size_t s) 373 #else 374 Sfio_t* sftmp(s) 375 reg size_t s; 376 #endif 377 { 378 reg Sfio_t* f; 379 static Sfdisc_t Tmpdisc = 380 { NIL(Sfread_f), NIL(Sfwrite_f), NIL(Sfseek_f), _tmpexcept, 381 #if _tmp_rmfail 382 &Rmdisc 383 #else 384 NIL(Sfdisc_t*) 385 #endif 386 }; 387 388 /* start with a memory resident stream */ 389 if(!(f = sfnew(NIL(Sfio_t*),NIL(char*),s,-1,SF_STRING|SF_READ|SF_WRITE)) ) 390 return NIL(Sfio_t*); 391 392 if(s != (size_t)SF_UNBOUND) /* set up a discipline for out-of-bound, etc. */ 393 f->disc = &Tmpdisc; 394 395 /* make the file now */ 396 if(s == 0 && _tmpexcept(f,SF_DPOP,NIL(Void_t*),f->disc) < 0) 397 { sfclose(f); 398 return NIL(Sfio_t*); 399 } 400 401 return f; 402 } 403