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