xref: /titanic_44/usr/src/lib/libast/common/sfio/sftmp.c (revision 3e14f97f673e8a630f076077de35afdd43dc1587)
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