1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1985-2012 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 <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
_tmprmfile(Sfio_t * f,int type,Void_t * val,Sfdisc_t * disc)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
_rmfiles(void)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
_rmtmp(Sfio_t * f,char * file)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
_sfgetpath(char * path)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
_tmpfd(Sfio_t * f)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
_tmpexcept(Sfio_t * f,int type,Void_t * val,Sfdisc_t * disc)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 Sfnotify_f notify = _Sfnotify;
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 /* try to create the temp file */
307 SFCLEAR(&newf,NIL(Vtmutex_t*));
308 newf.flags = SF_STATIC;
309 newf.mode = SF_AVAIL;
310
311 if((fd = _tmpfd(f)) < 0 )
312 return -1;
313
314 /* make sure that the notify function won't be called here since
315 we are only interested in creating the file, not the stream */
316 _Sfnotify = 0;
317 sf = sfnew(&newf,NIL(Void_t*),(size_t)SF_UNBOUND,fd,SF_READ|SF_WRITE);
318 _Sfnotify = notify;
319 if(!sf)
320 return -1;
321
322 if(newf.mutex) /* don't need a mutex for this stream */
323 { (void)vtmtxclrlock(newf.mutex);
324 (void)vtmtxclose(newf.mutex);
325 newf.mutex = NIL(Vtmutex_t*);
326 }
327
328 /* make sure that new stream has the same mode */
329 if((m = f->flags&(SF_READ|SF_WRITE)) != (SF_READ|SF_WRITE))
330 sfset(sf, ((~m)&(SF_READ|SF_WRITE)), 0);
331 sfset(sf, (f->mode&(SF_READ|SF_WRITE)), 1);
332
333 /* now remake the old stream into the new image */
334 memcpy((Void_t*)(&savf), (Void_t*)f, sizeof(Sfio_t));
335 memcpy((Void_t*)f, (Void_t*)sf, sizeof(Sfio_t));
336 f->push = savf.push;
337 f->pool = savf.pool;
338 f->rsrv = savf.rsrv;
339 f->proc = savf.proc;
340 f->mutex = savf.mutex;
341 f->stdio = savf.stdio;
342
343 /* remove the SF_STATIC bit if it was only set above in making newf */
344 if(!(savf.flags&SF_STATIC) )
345 f->flags &= ~SF_STATIC;
346
347 if(savf.data)
348 { SFSTRSIZE(&savf);
349 if(!(savf.flags&SF_MALLOC) )
350 (void)sfsetbuf(f,(Void_t*)savf.data,savf.size);
351 if(savf.extent > 0)
352 (void)sfwrite(f,(Void_t*)savf.data,(size_t)savf.extent);
353 (void)sfseek(f,(Sfoff_t)(savf.next - savf.data),SEEK_SET);
354 if((savf.flags&SF_MALLOC) )
355 free((Void_t*)savf.data);
356 }
357
358 /* announce change of status */
359 f->disc = NIL(Sfdisc_t*);
360 if(_Sfnotify)
361 (*_Sfnotify)(f, SF_SETFD, (void*)((long)f->file));
362
363 /* erase all traces of newf */
364 newf.data = newf.endb = newf.endr = newf.endw = NIL(uchar*);
365 newf.file = -1;
366 _Sfnotify = 0;
367 sfclose(&newf);
368 _Sfnotify = notify;
369
370 return 1;
371 }
372
373 #if __STD_C
sftmp(size_t s)374 Sfio_t* sftmp(size_t s)
375 #else
376 Sfio_t* sftmp(s)
377 size_t s;
378 #endif
379 {
380 Sfio_t *f;
381 int rv;
382 Sfnotify_f notify = _Sfnotify;
383 static Sfdisc_t Tmpdisc =
384 { NIL(Sfread_f), NIL(Sfwrite_f), NIL(Sfseek_f), _tmpexcept,
385 #if _tmp_rmfail
386 &Rmdisc
387 #else
388 NIL(Sfdisc_t*)
389 #endif
390 };
391
392 /* start with a memory resident stream */
393 _Sfnotify = 0; /* local computation so no notification */
394 f = sfnew(NIL(Sfio_t*),NIL(char*),s,-1,SF_STRING|SF_READ|SF_WRITE);
395 _Sfnotify = notify;
396 if(!f)
397 return NIL(Sfio_t*);
398
399 if(s != (size_t)SF_UNBOUND) /* set up a discipline for out-of-bound, etc. */
400 f->disc = &Tmpdisc;
401
402 if(s == 0) /* make the file now */
403 { _Sfnotify = 0; /* local computation so no notification */
404 rv = _tmpexcept(f,SF_DPOP,NIL(Void_t*),f->disc);
405 _Sfnotify = notify;
406 if(rv < 0)
407 { sfclose(f);
408 return NIL(Sfio_t*);
409 }
410 }
411
412 if(_Sfnotify)
413 (*_Sfnotify)(f, SF_NEW, (void*)((long)f->file));
414
415 return f;
416 }
417