1 /***********************************************************************
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 *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
13 * Information and Software Systems Research *
17 * Glenn Fowler <gsf@research.att.com> *
18 * David Korn <dgk@research.att.com> *
19 * Phong Vo <kpv@research.att.com> *
21 ***********************************************************************/
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.
30 ** Written by David Korn and Kiem-Phong Vo.
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.
46 typedef struct _file_s File_t
;
48 { File_t
* next
; /* link list */
49 Sfio_t
* f
; /* associated stream */
50 char name
[1]; /* temp file name */
53 static File_t
* File
; /* list pf temp files */
56 static int _tmprmfile(Sfio_t
* f
, int type
, Void_t
* val
, Sfdisc_t
* disc
)
58 static int _tmprmfile(f
, type
, val
, disc
)
65 reg File_t
*ff
, *last
;
69 if(type
== SF_DPOP
) /* don't allow this to pop */
72 if(type
== SF_CLOSING
)
74 (void)vtmtxlock(_Sfmutex
);
75 for(last
= NIL(File_t
*), ff
= File
; ff
; last
= ff
, ff
= ff
->next
)
81 else last
->next
= ff
->next
;
84 (*_Sfnotify
)(f
,SF_CLOSING
,f
->file
);
87 while(sysremovef(ff
->name
) < 0 && errno
== EINTR
)
92 (void)vtmtxunlock(_Sfmutex
);
99 static void _rmfiles(void)
101 static void _rmfiles()
103 { reg File_t
*ff
, *next
;
105 (void)vtmtxlock(_Sfmutex
);
106 for(ff
= File
; ff
; ff
= next
)
108 _tmprmfile(ff
->f
, SF_CLOSING
, NIL(Void_t
*), ff
->f
->disc
);
110 (void)vtmtxunlock(_Sfmutex
);
113 static Sfdisc_t Rmdisc
=
114 { NIL(Sfread_f
), NIL(Sfwrite_f
), NIL(Sfseek_f
), _tmprmfile
, NIL(Sfdisc_t
*) };
116 #endif /*_tmp_rmfail*/
119 static int _rmtmp(Sfio_t
* f
, char* file
)
121 static int _rmtmp(f
, file
)
126 #if _tmp_rmfail /* remove only when stream is closed */
132 if(!(ff
= (File_t
*)malloc(sizeof(File_t
)+strlen(file
))) )
134 (void)vtmtxlock(_Sfmutex
);
136 strcpy(ff
->name
,file
);
139 (void)vtmtxunlock(_Sfmutex
);
141 #else /* can remove now */
142 while(sysremovef(file
) < 0 && errno
== EINTR
)
150 #define TMPDFLT "/tmp"
151 static char **Tmppath
, **Tmpcur
;
154 char** _sfgetpath(char* path
)
156 char** _sfgetpath(path
)
159 { reg
char *p
, **dirs
;
162 if(!(path
= getenv(path
)) )
165 for(p
= path
, n
= 0;;) /* count number of directories */
171 while(*p
&& *p
!= ':') /* skip dir name */
174 if(n
== 0 || !(dirs
= (char**)malloc((n
+1)*sizeof(char*))) )
176 if(!(p
= (char*)malloc(strlen(path
)+1)) )
187 while(*p
&& *p
!= ':')
192 dirs
[n
] = NIL(char*);
197 #endif /*!_PACKAGE_ast*/
200 static int _tmpfd(Sfio_t
* f
)
210 if(!(file
= pathtemp(NiL
,PATH_MAX
,NiL
,"sf",&fd
)))
217 /* set up path of dirs to create temp files */
218 if(!Tmppath
&& !(Tmppath
= _sfgetpath("TMPPATH")) )
219 { if(!(Tmppath
= (char**)malloc(2*sizeof(char*))) )
221 if(!(file
= getenv("TMPDIR")) )
223 if(!(Tmppath
[0] = (char*)malloc(strlen(file
)+1)) )
225 Tmppath
= NIL(char**);
228 strcpy(Tmppath
[0],file
);
229 Tmppath
[1] = NIL(char*);
232 /* set current directory to create this temp file */
235 if(!Tmpcur
|| !Tmpcur
[0])
239 for(t
= 0; t
< 10; ++t
)
240 { /* compute a random name */
242 if(A
== 0 || t
> 0) /* get a quasi-random coefficient */
244 A
= (ulong
)time(NIL(time_t*)) ^ (((ulong
)(&t
)) >> 3);
246 Key
= (A
>> 16) | ((A
&0xffff)<<16);
248 if((r
= (A
-1) & 03) != 0) /* Knuth vol.2, page.16, Thm.A */
252 Key
= A
*Key
+ 987654321;
253 file
= sfprints("%s/sf%3.3.32lu.%3.3.32lu",
254 Tmpcur
[0], (Key
>>15)&0x7fff, Key
&0x7fff);
258 if((fd
= sysopenf(file
,O_RDWR
|O_CREAT
|O_EXCL
|O_TEMPORARY
,SF_CREATMODE
)) >= 0)
261 if((fd
= sysopenf(file
,O_RDONLY
)) >= 0)
262 { /* file already exists */
266 else if((fd
= syscreatf(file
,SF_CREATMODE
)) >= 0)
267 { /* reopen for read and write */
269 if((fd
= sysopenf(file
,O_RDWR
)) >= 0)
272 /* don't know what happened but must remove file */
273 while(sysremovef(file
) < 0 && errno
== EINTR
)
276 #endif /* _has_oflags */
280 #endif /* _PACKAGE_ast */
285 static int _tmpexcept(Sfio_t
* f
, int type
, Void_t
* val
, Sfdisc_t
* disc
)
287 static int _tmpexcept(f
,type
,val
,disc
)
297 void (*notifyf
)_ARG_((Sfio_t
*, int, void*));
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
)
306 /* notify function */
309 /* try to create the temp file */
310 SFCLEAR(&newf
,NIL(Vtmutex_t
*));
311 newf
.flags
= SF_STATIC
;
312 newf
.mode
= SF_AVAIL
;
314 if((fd
= _tmpfd(f
)) < 0 )
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 */
320 sf
= sfnew(&newf
,NIL(Void_t
*),(size_t)SF_UNBOUND
,fd
,SF_READ
|SF_WRITE
);
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
*);
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);
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
));
343 f
->mutex
= savf
.mutex
;
344 f
->stdio
= savf
.stdio
;
348 if(!(savf
.flags
&SF_MALLOC
) )
349 (void)sfsetbuf(f
,(Void_t
*)savf
.data
,savf
.size
);
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
);
357 /* announce change of status */
359 (*notifyf
)(f
, SF_NEW
, (void*)((long)f
->file
));
361 f
->disc
= disc
->disc
;
363 /* erase all traces of newf */
364 newf
.data
= newf
.endb
= newf
.endr
= newf
.endw
= NIL(uchar
*);
372 Sfio_t
* sftmp(size_t s
)
379 static Sfdisc_t Tmpdisc
=
380 { NIL(Sfread_f
), NIL(Sfwrite_f
), NIL(Sfseek_f
), _tmpexcept
,
388 /* start with a memory resident stream */
389 if(!(f
= sfnew(NIL(Sfio_t
*),NIL(char*),s
,-1,SF_STRING
|SF_READ
|SF_WRITE
)) )
392 if(s
!= (size_t)SF_UNBOUND
) /* set up a discipline for out-of-bound, etc. */
395 /* make the file now */
396 if(s
== 0 && _tmpexcept(f
,SF_DPOP
,NIL(Void_t
*),f
->disc
) < 0)