1/*********************************************************************** 2* * 3* This software is part of the ast package * 4* Copyright (c) 1985-2011 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#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 46typedef struct _file_s File_t; 47struct _file_s 48{ File_t* next; /* link list */ 49 Sfio_t* f; /* associated stream */ 50 char name[1]; /* temp file name */ 51}; 52 53static File_t* File; /* list pf temp files */ 54 55#if __STD_C 56static int _tmprmfile(Sfio_t* f, int type, Void_t* val, Sfdisc_t* disc) 57#else 58static int _tmprmfile(f, type, val, disc) 59Sfio_t* f; 60int type; 61Void_t* val; 62Sfdisc_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 99static void _rmfiles(void) 100#else 101static 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 113static 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 119static int _rmtmp(Sfio_t* f, char* file) 120#else 121static int _rmtmp(f, file) 122Sfio_t* f; 123char* 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" 151static char **Tmppath, **Tmpcur; 152 153#if __STD_C 154char** _sfgetpath(char* path) 155#else 156char** _sfgetpath(path) 157char* 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 200static int _tmpfd(Sfio_t* f) 201#else 202static int _tmpfd(f) 203Sfio_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 285static int _tmpexcept(Sfio_t* f, int type, Void_t* val, Sfdisc_t* disc) 286#else 287static int _tmpexcept(f,type,val,disc) 288Sfio_t* f; 289int type; 290Void_t* val; 291Sfdisc_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, void*)); 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, (void*)((long)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 372Sfio_t* sftmp(size_t s) 373#else 374Sfio_t* sftmp(s) 375size_t s; 376#endif 377{ 378 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