xref: /onnv-gate/usr/src/lib/libast/common/sfio/sftmp.c (revision 12068:08a39a083754)
14887Schin /***********************************************************************
24887Schin *                                                                      *
34887Schin *               This software is part of the ast package               *
4*12068SRoger.Faulkner@Oracle.COM *          Copyright (c) 1985-2010 AT&T Intellectual Property          *
54887Schin *                      and is licensed under the                       *
64887Schin *                  Common Public License, Version 1.0                  *
78462SApril.Chin@Sun.COM *                    by AT&T Intellectual Property                     *
84887Schin *                                                                      *
94887Schin *                A copy of the License is available at                 *
104887Schin *            http://www.opensource.org/licenses/cpl1.0.txt             *
114887Schin *         (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9)         *
124887Schin *                                                                      *
134887Schin *              Information and Software Systems Research               *
144887Schin *                            AT&T Research                             *
154887Schin *                           Florham Park NJ                            *
164887Schin *                                                                      *
174887Schin *                 Glenn Fowler <gsf@research.att.com>                  *
184887Schin *                  David Korn <dgk@research.att.com>                   *
194887Schin *                   Phong Vo <kpv@research.att.com>                    *
204887Schin *                                                                      *
214887Schin ***********************************************************************/
224887Schin #include	"sfhdr.h"
234887Schin 
244887Schin /*	Create a temporary stream for read/write.
254887Schin **	The stream is originally created as a memory-resident stream.
264887Schin **	When this memory is exceeded, a real temp file will be created.
274887Schin **	The temp file creation sequence is somewhat convoluted so that
284887Schin **	pool/stack/discipline will work correctly.
294887Schin **
304887Schin **	Written by David Korn and Kiem-Phong Vo.
314887Schin */
324887Schin 
334887Schin #if _tmp_rmfail
344887Schin 
354887Schin /* File not removable while there is an open file descriptor.
364887Schin ** To ensure that temp files are properly removed, we need:
374887Schin ** 1. A discipline to remove a file when the corresponding stream is closed.
384887Schin **    Care must be taken to close the file descriptor before removing the
394887Schin **    file because systems such as NT do not allow file removal while
404887Schin **    there is an open file handle.
414887Schin ** 2. An atexit() function is set up to close temp files when process exits.
424887Schin ** 3. On systems with O_TEMPORARY (e.g., NT), this is used to further ensure
434887Schin **    that temp files will be removed after the last handle is closed.
444887Schin */
454887Schin 
464887Schin typedef struct _file_s		File_t;
474887Schin struct _file_s
484887Schin {	File_t*	next;		/* link list		*/
494887Schin 	Sfio_t*	f;		/* associated stream	*/
504887Schin 	char	name[1];	/* temp file name	*/
514887Schin };
524887Schin 
534887Schin static File_t*	File;		/* list pf temp files	*/
544887Schin 
554887Schin #if __STD_C
_tmprmfile(Sfio_t * f,int type,Void_t * val,Sfdisc_t * disc)564887Schin static int _tmprmfile(Sfio_t* f, int type, Void_t* val, Sfdisc_t* disc)
574887Schin #else
584887Schin static int _tmprmfile(f, type, val, disc)
594887Schin Sfio_t*		f;
604887Schin int		type;
614887Schin Void_t*		val;
624887Schin Sfdisc_t*	disc;
634887Schin #endif
644887Schin {
654887Schin 	reg File_t	*ff, *last;
664887Schin 
674887Schin 	NOTUSED(val);
684887Schin 
694887Schin 	if(type == SF_DPOP)	/* don't allow this to pop */
704887Schin 		return -1;
714887Schin 
724887Schin 	if(type == SF_CLOSING)
734887Schin 	{
744887Schin 		(void)vtmtxlock(_Sfmutex);
754887Schin 		for(last = NIL(File_t*), ff = File; ff; last = ff, ff = ff->next)
764887Schin 			if(ff->f == f)
774887Schin 				break;
784887Schin 		if(ff)
794887Schin 		{	if(!last)
804887Schin 				File = ff->next;
814887Schin 			else	last->next = ff->next;
824887Schin 
834887Schin 			if(_Sfnotify)
844887Schin 				(*_Sfnotify)(f,SF_CLOSING,f->file);
854887Schin 			CLOSE(f->file);
864887Schin 			f->file = -1;
874887Schin 			while(sysremovef(ff->name) < 0 && errno == EINTR)
884887Schin 				errno = 0;
894887Schin 
904887Schin 			free((Void_t*)ff);
914887Schin 		}
924887Schin 		(void)vtmtxunlock(_Sfmutex);
934887Schin 	}
944887Schin 
954887Schin 	return 0;
964887Schin }
974887Schin 
984887Schin #if __STD_C
_rmfiles(void)994887Schin static void _rmfiles(void)
1004887Schin #else
1014887Schin static void _rmfiles()
1024887Schin #endif
1034887Schin {	reg File_t	*ff, *next;
1044887Schin 
1054887Schin 	(void)vtmtxlock(_Sfmutex);
1064887Schin 	for(ff = File; ff; ff = next)
1074887Schin 	{	next = ff->next;
1084887Schin 		_tmprmfile(ff->f, SF_CLOSING, NIL(Void_t*), ff->f->disc);
1094887Schin 	}
1104887Schin 	(void)vtmtxunlock(_Sfmutex);
1114887Schin }
1124887Schin 
1134887Schin static Sfdisc_t	Rmdisc =
1144887Schin 	{ NIL(Sfread_f), NIL(Sfwrite_f), NIL(Sfseek_f), _tmprmfile, NIL(Sfdisc_t*) };
1154887Schin 
1164887Schin #endif /*_tmp_rmfail*/
1174887Schin 
1184887Schin #if __STD_C
_rmtmp(Sfio_t * f,char * file)1194887Schin static int _rmtmp(Sfio_t* f, char* file)
1204887Schin #else
1214887Schin static int _rmtmp(f, file)
1224887Schin Sfio_t*	f;
1234887Schin char*	file;
1244887Schin #endif
1254887Schin {
1264887Schin #if _tmp_rmfail	/* remove only when stream is closed */
1274887Schin 	reg File_t*	ff;
1284887Schin 
1294887Schin 	if(!File)
1304887Schin 		atexit(_rmfiles);
1314887Schin 
1324887Schin 	if(!(ff = (File_t*)malloc(sizeof(File_t)+strlen(file))) )
1334887Schin 		return -1;
1344887Schin 	(void)vtmtxlock(_Sfmutex);
1354887Schin 	ff->f = f;
1364887Schin 	strcpy(ff->name,file);
1374887Schin 	ff->next = File;
1384887Schin 	File = ff;
1394887Schin 	(void)vtmtxunlock(_Sfmutex);
1404887Schin 
1414887Schin #else	/* can remove now */
1424887Schin 	while(sysremovef(file) < 0 && errno == EINTR)
1434887Schin 		errno = 0;
1444887Schin #endif
1454887Schin 
1464887Schin 	return 0;
1474887Schin }
1484887Schin 
1494887Schin #if !_PACKAGE_ast
1504887Schin #define		TMPDFLT		"/tmp"
1514887Schin static char	**Tmppath, **Tmpcur;
1524887Schin 
1534887Schin #if __STD_C
_sfgetpath(char * path)1544887Schin char** _sfgetpath(char* path)
1554887Schin #else
1564887Schin char** _sfgetpath(path)
1574887Schin char*	path;
1584887Schin #endif
1594887Schin {	reg char	*p, **dirs;
1604887Schin 	reg int		n;
1614887Schin 
1624887Schin 	if(!(path = getenv(path)) )
1634887Schin 		return NIL(char**);
1644887Schin 
1654887Schin 	for(p = path, n = 0;;)	/* count number of directories */
1664887Schin 	{	while(*p == ':')
1674887Schin 			++p;
1684887Schin 		if(*p == 0)
1694887Schin 			break;
1704887Schin 		n += 1;
1714887Schin 		while(*p && *p != ':')	/* skip dir name */
1724887Schin 			++p;
1734887Schin 	}
1744887Schin 	if(n == 0 || !(dirs = (char**)malloc((n+1)*sizeof(char*))) )
1754887Schin 		return NIL(char**);
1764887Schin 	if(!(p = (char*)malloc(strlen(path)+1)) )
1774887Schin 	{	free(dirs);
1784887Schin 		return NIL(char**);
1794887Schin 	}
1804887Schin 	strcpy(p,path);
1814887Schin 	for(n = 0;; ++n)
1824887Schin 	{	while(*p == ':')
1834887Schin 			++p;
1844887Schin 		if(*p == 0)
1854887Schin 			break;
1864887Schin 		dirs[n] = p;
1874887Schin 		while(*p && *p != ':')
1884887Schin 			++p;
1894887Schin 		if(*p == ':')
1904887Schin 			*p++ = 0;
1914887Schin 	}
1924887Schin 	dirs[n] = NIL(char*);
1934887Schin 
1944887Schin 	return dirs;
1954887Schin }
1964887Schin 
1974887Schin #endif /*!_PACKAGE_ast*/
1984887Schin 
1994887Schin #if __STD_C
_tmpfd(Sfio_t * f)2004887Schin static int _tmpfd(Sfio_t* f)
2014887Schin #else
2024887Schin static int _tmpfd(f)
2034887Schin Sfio_t*	f;
2044887Schin #endif
2054887Schin {
2064887Schin 	reg char*	file;
2074887Schin 	int		fd;
2084887Schin 
2094887Schin #if _PACKAGE_ast
2104887Schin 	if(!(file = pathtemp(NiL,PATH_MAX,NiL,"sf",&fd)))
2114887Schin 		return -1;
2124887Schin 	_rmtmp(f, file);
2134887Schin 	free(file);
2144887Schin #else
2154887Schin 	int		t;
2164887Schin 
2174887Schin 	/* set up path of dirs to create temp files */
2184887Schin 	if(!Tmppath && !(Tmppath = _sfgetpath("TMPPATH")) )
2194887Schin 	{	if(!(Tmppath = (char**)malloc(2*sizeof(char*))) )
2204887Schin 			return -1;
2214887Schin 		if(!(file = getenv("TMPDIR")) )
2224887Schin 			file = TMPDFLT;
2234887Schin 		if(!(Tmppath[0] = (char*)malloc(strlen(file)+1)) )
2244887Schin 		{	free(Tmppath);
2254887Schin 			Tmppath = NIL(char**);
2264887Schin 			return -1;
2274887Schin 		}
2284887Schin 		strcpy(Tmppath[0],file);
2294887Schin 		Tmppath[1] = NIL(char*);
2304887Schin 	}
2314887Schin 
2324887Schin 	/* set current directory to create this temp file */
2334887Schin 	if(Tmpcur)
2344887Schin 		Tmpcur += 1;
2354887Schin 	if(!Tmpcur || !Tmpcur[0])
2364887Schin 		Tmpcur = Tmppath;
2374887Schin 
2384887Schin 	fd = -1;
2394887Schin 	for(t = 0; t < 10; ++t)
2404887Schin 	{	/* compute a random name */
2414887Schin 		static ulong	Key, A;
2424887Schin 		if(A == 0 || t > 0)	/* get a quasi-random coefficient */
2434887Schin 		{	reg int	r;
2444887Schin 			A = (ulong)time(NIL(time_t*)) ^ (((ulong)(&t)) >> 3);
2454887Schin 			if(Key == 0)
2464887Schin 				Key = (A >> 16) | ((A&0xffff)<<16);
2474887Schin 			A ^= Key;
2484887Schin 			if((r = (A-1) & 03) != 0) /* Knuth vol.2, page.16, Thm.A */
2494887Schin 				A += 4-r;
2504887Schin 		}
2514887Schin 
2524887Schin 		Key = A*Key + 987654321;
2534887Schin 		file = sfprints("%s/sf%3.3.32lu.%3.3.32lu",
2544887Schin 				Tmpcur[0], (Key>>15)&0x7fff, Key&0x7fff);
2554887Schin 		if(!file)
2564887Schin 			return -1;
2574887Schin #if _has_oflags
2584887Schin 		if((fd = sysopenf(file,O_RDWR|O_CREAT|O_EXCL|O_TEMPORARY,SF_CREATMODE)) >= 0)
2594887Schin 			break;
2604887Schin #else
2614887Schin 		if((fd = sysopenf(file,O_RDONLY)) >= 0)
2624887Schin 		{	/* file already exists */
2634887Schin 			CLOSE(fd);
2644887Schin 			fd = -1;
2654887Schin 		}
2664887Schin 		else if((fd = syscreatf(file,SF_CREATMODE)) >= 0)
2674887Schin 		{	/* reopen for read and write */
2684887Schin 			CLOSE(fd);
2694887Schin 			if((fd = sysopenf(file,O_RDWR)) >= 0)
2704887Schin 				break;
2714887Schin 
2724887Schin 			/* don't know what happened but must remove file */
2734887Schin 			while(sysremovef(file) < 0 && errno == EINTR)
2744887Schin 				errno = 0;
2754887Schin 		}
2764887Schin #endif /* _has_oflags */
2774887Schin 	}
2784887Schin 	if(fd >= 0)
2794887Schin 		_rmtmp(f, file);
2804887Schin #endif /* _PACKAGE_ast */
2814887Schin 	return fd;
2824887Schin }
2834887Schin 
2844887Schin #if __STD_C
_tmpexcept(Sfio_t * f,int type,Void_t * val,Sfdisc_t * disc)2854887Schin static int _tmpexcept(Sfio_t* f, int type, Void_t* val, Sfdisc_t* disc)
2864887Schin #else
2874887Schin static int _tmpexcept(f,type,val,disc)
2884887Schin Sfio_t*		f;
2894887Schin int		type;
2904887Schin Void_t*		val;
2914887Schin Sfdisc_t*	disc;
2924887Schin #endif
2934887Schin {
2944887Schin 	reg int		fd, m;
2954887Schin 	reg Sfio_t*	sf;
2964887Schin 	Sfio_t		newf, savf;
2978462SApril.Chin@Sun.COM 	void		(*notifyf)_ARG_((Sfio_t*, int, void*));
2984887Schin 
2994887Schin 	NOTUSED(val);
3004887Schin 
3014887Schin 	/* the discipline needs to change only under the following exceptions */
3024887Schin 	if(type != SF_WRITE && type != SF_SEEK &&
3034887Schin 	   type != SF_DPUSH && type != SF_DPOP && type != SF_DBUFFER)
3044887Schin 		return 0;
3054887Schin 
3064887Schin 	/* notify function */
3074887Schin 	notifyf = _Sfnotify;
3084887Schin 
3094887Schin 	/* try to create the temp file */
3104887Schin 	SFCLEAR(&newf,NIL(Vtmutex_t*));
3114887Schin 	newf.flags = SF_STATIC;
3124887Schin 	newf.mode = SF_AVAIL;
3134887Schin 
3144887Schin 	if((fd = _tmpfd(f)) < 0 )
3154887Schin 		return -1;
3164887Schin 
3174887Schin 	/* make sure that the notify function won't be called here since
3184887Schin 	   we are only interested in creating the file, not the stream */
3194887Schin 	_Sfnotify = 0;
3204887Schin 	sf = sfnew(&newf,NIL(Void_t*),(size_t)SF_UNBOUND,fd,SF_READ|SF_WRITE);
3214887Schin 	_Sfnotify = notifyf;
3224887Schin 	if(!sf)
3234887Schin 		return -1;
3244887Schin 
3254887Schin 	if(newf.mutex) /* don't need a mutex for this stream */
3264887Schin 	{	(void)vtmtxclrlock(newf.mutex);
3274887Schin 		(void)vtmtxclose(newf.mutex);
3284887Schin 		newf.mutex = NIL(Vtmutex_t*);
3294887Schin 	}
3304887Schin 
3314887Schin 	/* make sure that new stream has the same mode */
3324887Schin 	if((m = f->flags&(SF_READ|SF_WRITE)) != (SF_READ|SF_WRITE))
3334887Schin 		sfset(sf, ((~m)&(SF_READ|SF_WRITE)), 0);
3344887Schin 	sfset(sf, (f->mode&(SF_READ|SF_WRITE)), 1);
3354887Schin 
3364887Schin 	/* now remake the old stream into the new image */
3374887Schin 	memcpy((Void_t*)(&savf), (Void_t*)f, sizeof(Sfio_t));
3384887Schin 	memcpy((Void_t*)f, (Void_t*)sf, sizeof(Sfio_t));
3394887Schin 	f->push = savf.push;
3404887Schin 	f->pool = savf.pool;
3414887Schin 	f->rsrv = savf.rsrv;
3424887Schin 	f->proc = savf.proc;
3434887Schin 	f->mutex = savf.mutex;
3444887Schin 	f->stdio = savf.stdio;
3454887Schin 
3464887Schin 	if(savf.data)
3474887Schin 	{	SFSTRSIZE(&savf);
3484887Schin 		if(!(savf.flags&SF_MALLOC) )
3494887Schin 			(void)sfsetbuf(f,(Void_t*)savf.data,savf.size);
3504887Schin 		if(savf.extent > 0)
3514887Schin 			(void)sfwrite(f,(Void_t*)savf.data,(size_t)savf.extent);
3524887Schin 		(void)sfseek(f,(Sfoff_t)(savf.next - savf.data),SEEK_SET);
3534887Schin 		if((savf.flags&SF_MALLOC) )
3544887Schin 			free((Void_t*)savf.data);
3554887Schin 	}
3564887Schin 
3574887Schin 	/* announce change of status */
3584887Schin 	if(notifyf)
3598462SApril.Chin@Sun.COM 		(*notifyf)(f, SF_NEW, (void*)((long)f->file));
3604887Schin 
3614887Schin 	f->disc = disc->disc;
3624887Schin 
3634887Schin 	/* erase all traces of newf */
3644887Schin 	newf.data = newf.endb = newf.endr = newf.endw = NIL(uchar*);
3654887Schin 	newf.file = -1;
3664887Schin 	sfclose(&newf);
3674887Schin 
3684887Schin 	return 1;
3694887Schin }
3704887Schin 
3714887Schin #if __STD_C
sftmp(size_t s)3728462SApril.Chin@Sun.COM Sfio_t* sftmp(size_t s)
3734887Schin #else
3744887Schin Sfio_t* sftmp(s)
3758462SApril.Chin@Sun.COM size_t	s;
3764887Schin #endif
3774887Schin {
3788462SApril.Chin@Sun.COM 	Sfio_t*		f;
3794887Schin 	static Sfdisc_t	Tmpdisc =
3804887Schin 			{ NIL(Sfread_f), NIL(Sfwrite_f), NIL(Sfseek_f), _tmpexcept,
3814887Schin #if _tmp_rmfail
3824887Schin 			  &Rmdisc
3834887Schin #else
3844887Schin 			NIL(Sfdisc_t*)
3854887Schin #endif
3864887Schin 			};
3874887Schin 
3884887Schin 	/* start with a memory resident stream */
3894887Schin 	if(!(f = sfnew(NIL(Sfio_t*),NIL(char*),s,-1,SF_STRING|SF_READ|SF_WRITE)) )
3904887Schin 		return NIL(Sfio_t*);
3914887Schin 
3924887Schin 	if(s != (size_t)SF_UNBOUND)	/* set up a discipline for out-of-bound, etc. */
3934887Schin 		f->disc = &Tmpdisc;
3944887Schin 
3954887Schin 	/* make the file now */
3964887Schin 	if(s == 0 && _tmpexcept(f,SF_DPOP,NIL(Void_t*),f->disc) < 0)
3974887Schin 	{	sfclose(f);
3984887Schin 		return NIL(Sfio_t*);
3994887Schin 	}
4004887Schin 
4014887Schin 	return f;
4024887Schin }
403