Blame src/lib/libast/sfio/sftmp.c

Packit Service a8c26c
/***********************************************************************
Packit Service a8c26c
*                                                                      *
Packit Service a8c26c
*               This software is part of the ast package               *
Packit Service a8c26c
*          Copyright (c) 1985-2012 AT&T Intellectual Property          *
Packit Service a8c26c
*                      and is licensed under the                       *
Packit Service a8c26c
*                 Eclipse Public License, Version 1.0                  *
Packit Service a8c26c
*                    by AT&T Intellectual Property                     *
Packit Service a8c26c
*                                                                      *
Packit Service a8c26c
*                A copy of the License is available at                 *
Packit Service a8c26c
*          http://www.eclipse.org/org/documents/epl-v10.html           *
Packit Service a8c26c
*         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
Packit Service a8c26c
*                                                                      *
Packit Service a8c26c
*              Information and Software Systems Research               *
Packit Service a8c26c
*                            AT&T Research                             *
Packit Service a8c26c
*                           Florham Park NJ                            *
Packit Service a8c26c
*                                                                      *
Packit Service a8c26c
*                 Glenn Fowler <gsf@research.att.com>                  *
Packit Service a8c26c
*                  David Korn <dgk@research.att.com>                   *
Packit Service a8c26c
*                   Phong Vo <kpv@research.att.com>                    *
Packit Service a8c26c
*                                                                      *
Packit Service a8c26c
***********************************************************************/
Packit Service a8c26c
#include	"sfhdr.h"
Packit Service a8c26c
Packit Service a8c26c
/*	Create a temporary stream for read/write.
Packit Service a8c26c
**	The stream is originally created as a memory-resident stream.
Packit Service a8c26c
**	When this memory is exceeded, a real temp file will be created.
Packit Service a8c26c
**	The temp file creation sequence is somewhat convoluted so that
Packit Service a8c26c
**	pool/stack/discipline will work correctly.
Packit Service a8c26c
**
Packit Service a8c26c
**	Written by David Korn and Kiem-Phong Vo.
Packit Service a8c26c
*/
Packit Service a8c26c
Packit Service a8c26c
#if _tmp_rmfail	
Packit Service a8c26c
Packit Service a8c26c
/* File not removable while there is an open file descriptor.
Packit Service a8c26c
** To ensure that temp files are properly removed, we need:
Packit Service a8c26c
** 1. A discipline to remove a file when the corresponding stream is closed.
Packit Service a8c26c
**    Care must be taken to close the file descriptor before removing the
Packit Service a8c26c
**    file because systems such as NT do not allow file removal while
Packit Service a8c26c
**    there is an open file handle.
Packit Service a8c26c
** 2. An atexit() function is set up to close temp files when process exits.
Packit Service a8c26c
** 3. On systems with O_TEMPORARY (e.g., NT), this is used to further ensure
Packit Service a8c26c
**    that temp files will be removed after the last handle is closed.
Packit Service a8c26c
*/
Packit Service a8c26c
Packit Service a8c26c
typedef struct _file_s		File_t;
Packit Service a8c26c
struct _file_s
Packit Service a8c26c
{	File_t*	next;		/* link list		*/
Packit Service a8c26c
	Sfio_t*	f;		/* associated stream	*/
Packit Service a8c26c
	char	name[1];	/* temp file name	*/
Packit Service a8c26c
};
Packit Service a8c26c
Packit Service a8c26c
static File_t*	File;		/* list pf temp files	*/
Packit Service a8c26c
Packit Service a8c26c
#if __STD_C
Packit Service a8c26c
static int _tmprmfile(Sfio_t* f, int type, Void_t* val, Sfdisc_t* disc)
Packit Service a8c26c
#else
Packit Service a8c26c
static int _tmprmfile(f, type, val, disc)
Packit Service a8c26c
Sfio_t*		f;
Packit Service a8c26c
int		type;
Packit Service a8c26c
Void_t*		val;
Packit Service a8c26c
Sfdisc_t*	disc;
Packit Service a8c26c
#endif
Packit Service a8c26c
{
Packit Service a8c26c
	reg File_t	*ff, *last;
Packit Service a8c26c
Packit Service a8c26c
	NOTUSED(val);
Packit Service a8c26c
Packit Service a8c26c
	if(type == SF_DPOP)	/* don't allow this to pop */
Packit Service a8c26c
		return -1;
Packit Service a8c26c
Packit Service a8c26c
	if(type == SF_CLOSING)
Packit Service a8c26c
	{
Packit Service a8c26c
		(void)vtmtxlock(_Sfmutex);
Packit Service a8c26c
		for(last = NIL(File_t*), ff = File; ff; last = ff, ff = ff->next)
Packit Service a8c26c
			if(ff->f == f)
Packit Service a8c26c
				break;
Packit Service a8c26c
		if(ff)
Packit Service a8c26c
		{	if(!last)
Packit Service a8c26c
				File = ff->next;
Packit Service a8c26c
			else	last->next = ff->next;
Packit Service a8c26c
Packit Service a8c26c
			if(_Sfnotify)
Packit Service a8c26c
				(*_Sfnotify)(f,SF_CLOSING,f->file);
Packit Service a8c26c
			CLOSE(f->file);
Packit Service a8c26c
			f->file = -1;
Packit Service a8c26c
			while(sysremovef(ff->name) < 0 && errno == EINTR)
Packit Service a8c26c
				errno = 0;
Packit Service a8c26c
Packit Service a8c26c
			free((Void_t*)ff);
Packit Service a8c26c
		}
Packit Service a8c26c
		(void)vtmtxunlock(_Sfmutex);
Packit Service a8c26c
	}
Packit Service a8c26c
Packit Service a8c26c
	return 0;
Packit Service a8c26c
}
Packit Service a8c26c
Packit Service a8c26c
#if __STD_C
Packit Service a8c26c
static void _rmfiles(void)
Packit Service a8c26c
#else
Packit Service a8c26c
static void _rmfiles()
Packit Service a8c26c
#endif
Packit Service a8c26c
{	reg File_t	*ff, *next;
Packit Service a8c26c
Packit Service a8c26c
	(void)vtmtxlock(_Sfmutex);
Packit Service a8c26c
	for(ff = File; ff; ff = next)
Packit Service a8c26c
	{	next = ff->next;
Packit Service a8c26c
		_tmprmfile(ff->f, SF_CLOSING, NIL(Void_t*), ff->f->disc);
Packit Service a8c26c
	}
Packit Service a8c26c
	(void)vtmtxunlock(_Sfmutex);
Packit Service a8c26c
}
Packit Service a8c26c
Packit Service a8c26c
static Sfdisc_t	Rmdisc =
Packit Service a8c26c
	{ NIL(Sfread_f), NIL(Sfwrite_f), NIL(Sfseek_f), _tmprmfile, NIL(Sfdisc_t*) };
Packit Service a8c26c
Packit Service a8c26c
#endif /*_tmp_rmfail*/
Packit Service a8c26c
Packit Service a8c26c
#if __STD_C
Packit Service a8c26c
static int _rmtmp(Sfio_t* f, char* file)
Packit Service a8c26c
#else
Packit Service a8c26c
static int _rmtmp(f, file)
Packit Service a8c26c
Sfio_t*	f;
Packit Service a8c26c
char*	file;
Packit Service a8c26c
#endif
Packit Service a8c26c
{
Packit Service a8c26c
#if _tmp_rmfail	/* remove only when stream is closed */
Packit Service a8c26c
	reg File_t*	ff;
Packit Service a8c26c
Packit Service a8c26c
	if(!File)
Packit Service a8c26c
		atexit(_rmfiles);
Packit Service a8c26c
Packit Service a8c26c
	if(!(ff = (File_t*)malloc(sizeof(File_t)+strlen(file))) )
Packit Service a8c26c
		return -1;
Packit Service a8c26c
	(void)vtmtxlock(_Sfmutex);
Packit Service a8c26c
	ff->f = f;
Packit Service a8c26c
	strcpy(ff->name,file);
Packit Service a8c26c
	ff->next = File;
Packit Service a8c26c
	File = ff;
Packit Service a8c26c
	(void)vtmtxunlock(_Sfmutex);
Packit Service a8c26c
Packit Service a8c26c
#else	/* can remove now */
Packit Service a8c26c
	while(sysremovef(file) < 0 && errno == EINTR)
Packit Service a8c26c
		errno = 0;
Packit Service a8c26c
#endif
Packit Service a8c26c
Packit Service a8c26c
	return 0;
Packit Service a8c26c
}
Packit Service a8c26c
Packit Service a8c26c
#if !_PACKAGE_ast
Packit Service a8c26c
#define		TMPDFLT		"/tmp"
Packit Service a8c26c
static char	**Tmppath, **Tmpcur;
Packit Service a8c26c
Packit Service a8c26c
#if __STD_C
Packit Service a8c26c
char** _sfgetpath(char* path)
Packit Service a8c26c
#else
Packit Service a8c26c
char** _sfgetpath(path)
Packit Service a8c26c
char*	path;
Packit Service a8c26c
#endif
Packit Service a8c26c
{	reg char	*p, **dirs;
Packit Service a8c26c
	reg int		n;
Packit Service a8c26c
Packit Service a8c26c
	if(!(path = getenv(path)) )
Packit Service a8c26c
		return NIL(char**);
Packit Service a8c26c
Packit Service a8c26c
	for(p = path, n = 0;;)	/* count number of directories */
Packit Service a8c26c
	{	while(*p == ':')
Packit Service a8c26c
			++p;
Packit Service a8c26c
		if(*p == 0)
Packit Service a8c26c
			break;
Packit Service a8c26c
		n += 1;
Packit Service a8c26c
		while(*p && *p != ':')	/* skip dir name */
Packit Service a8c26c
			++p;
Packit Service a8c26c
	}
Packit Service a8c26c
	if(n == 0 || !(dirs = (char**)malloc((n+1)*sizeof(char*))) )
Packit Service a8c26c
		return NIL(char**);
Packit Service a8c26c
	if(!(p = (char*)malloc(strlen(path)+1)) )
Packit Service a8c26c
	{	free(dirs);
Packit Service a8c26c
		return NIL(char**);
Packit Service a8c26c
	}
Packit Service a8c26c
	strcpy(p,path);
Packit Service a8c26c
	for(n = 0;; ++n)
Packit Service a8c26c
	{	while(*p == ':')
Packit Service a8c26c
			++p;
Packit Service a8c26c
		if(*p == 0)
Packit Service a8c26c
			break;
Packit Service a8c26c
		dirs[n] = p;
Packit Service a8c26c
		while(*p && *p != ':')
Packit Service a8c26c
			++p;
Packit Service a8c26c
		if(*p == ':')
Packit Service a8c26c
			*p++ = 0;
Packit Service a8c26c
	}
Packit Service a8c26c
	dirs[n] = NIL(char*);
Packit Service a8c26c
Packit Service a8c26c
	return dirs;
Packit Service a8c26c
}
Packit Service a8c26c
Packit Service a8c26c
#endif /*!_PACKAGE_ast*/
Packit Service a8c26c
Packit Service a8c26c
#if __STD_C
Packit Service a8c26c
static int _tmpfd(Sfio_t* f)
Packit Service a8c26c
#else
Packit Service a8c26c
static int _tmpfd(f)
Packit Service a8c26c
Sfio_t*	f;
Packit Service a8c26c
#endif
Packit Service a8c26c
{
Packit Service a8c26c
	reg char*	file;
Packit Service a8c26c
	int		fd;
Packit Service a8c26c
Packit Service a8c26c
#if _PACKAGE_ast
Packit Service a8c26c
	if(!(file = pathtemp(NiL,PATH_MAX,NiL,"sf",&fd)))
Packit Service a8c26c
		return -1;
Packit Service a8c26c
	_rmtmp(f, file);
Packit Service a8c26c
	free(file);
Packit Service a8c26c
#else
Packit Service a8c26c
	int		t;
Packit Service a8c26c
Packit Service a8c26c
	/* set up path of dirs to create temp files */
Packit Service a8c26c
	if(!Tmppath && !(Tmppath = _sfgetpath("TMPPATH")) )
Packit Service a8c26c
	{	if(!(Tmppath = (char**)malloc(2*sizeof(char*))) )
Packit Service a8c26c
			return -1;
Packit Service a8c26c
		if(!(file = getenv("TMPDIR")) )
Packit Service a8c26c
			file = TMPDFLT;
Packit Service a8c26c
		if(!(Tmppath[0] = (char*)malloc(strlen(file)+1)) )
Packit Service a8c26c
		{	free(Tmppath);
Packit Service a8c26c
			Tmppath = NIL(char**);
Packit Service a8c26c
			return -1;
Packit Service a8c26c
		}
Packit Service a8c26c
		strcpy(Tmppath[0],file);
Packit Service a8c26c
		Tmppath[1] = NIL(char*);
Packit Service a8c26c
	}
Packit Service a8c26c
Packit Service a8c26c
	/* set current directory to create this temp file */
Packit Service a8c26c
	if(Tmpcur)
Packit Service a8c26c
		Tmpcur += 1;
Packit Service a8c26c
	if(!Tmpcur || !Tmpcur[0])
Packit Service a8c26c
		Tmpcur = Tmppath;
Packit Service a8c26c
Packit Service a8c26c
	fd = -1;
Packit Service a8c26c
	for(t = 0; t < 10; ++t)
Packit Service a8c26c
	{	/* compute a random name */
Packit Service a8c26c
		static ulong	Key, A;
Packit Service a8c26c
		if(A == 0 || t > 0)	/* get a quasi-random coefficient */
Packit Service a8c26c
		{	reg int	r;
Packit Service a8c26c
			A = (ulong)time(NIL(time_t*)) ^ (((ulong)(&t)) >> 3);
Packit Service a8c26c
			if(Key == 0)
Packit Service a8c26c
				Key = (A >> 16) | ((A&0xffff)<<16);
Packit Service a8c26c
			A ^= Key;
Packit Service a8c26c
			if((r = (A-1) & 03) != 0) /* Knuth vol.2, page.16, Thm.A */
Packit Service a8c26c
				A += 4-r;
Packit Service a8c26c
		}
Packit Service a8c26c
Packit Service a8c26c
		Key = A*Key + 987654321;
Packit Service a8c26c
		file = sfprints("%s/sf%3.3.32lu.%3.3.32lu",
Packit Service a8c26c
				Tmpcur[0], (Key>>15)&0x7fff, Key&0x7fff);
Packit Service a8c26c
		if(!file)
Packit Service a8c26c
			return -1;
Packit Service a8c26c
#if _has_oflags
Packit Service a8c26c
		if((fd = sysopenf(file,O_RDWR|O_CREAT|O_EXCL|O_TEMPORARY,SF_CREATMODE)) >= 0)
Packit Service a8c26c
			break;
Packit Service a8c26c
#else
Packit Service a8c26c
		if((fd = sysopenf(file,O_RDONLY)) >= 0)
Packit Service a8c26c
		{	/* file already exists */
Packit Service a8c26c
			CLOSE(fd);
Packit Service a8c26c
			fd = -1;
Packit Service a8c26c
		}
Packit Service a8c26c
		else if((fd = syscreatf(file,SF_CREATMODE)) >= 0)
Packit Service a8c26c
		{	/* reopen for read and write */
Packit Service a8c26c
			CLOSE(fd);
Packit Service a8c26c
			if((fd = sysopenf(file,O_RDWR)) >= 0)
Packit Service a8c26c
				break;
Packit Service a8c26c
Packit Service a8c26c
			/* don't know what happened but must remove file */
Packit Service a8c26c
			while(sysremovef(file) < 0 && errno == EINTR)
Packit Service a8c26c
				errno = 0;
Packit Service a8c26c
		}
Packit Service a8c26c
#endif /* _has_oflags */
Packit Service a8c26c
	}
Packit Service a8c26c
	if(fd >= 0)
Packit Service a8c26c
		_rmtmp(f, file);
Packit Service a8c26c
#endif /* _PACKAGE_ast */
Packit Service a8c26c
	return fd;
Packit Service a8c26c
}
Packit Service a8c26c
Packit Service a8c26c
#if __STD_C
Packit Service a8c26c
static int _tmpexcept(Sfio_t* f, int type, Void_t* val, Sfdisc_t* disc)
Packit Service a8c26c
#else
Packit Service a8c26c
static int _tmpexcept(f,type,val,disc)
Packit Service a8c26c
Sfio_t*		f;
Packit Service a8c26c
int		type;
Packit Service a8c26c
Void_t*		val;
Packit Service a8c26c
Sfdisc_t*	disc;
Packit Service a8c26c
#endif
Packit Service a8c26c
{
Packit Service a8c26c
	reg int		fd, m;
Packit Service a8c26c
	reg Sfio_t*	sf;
Packit Service a8c26c
	Sfio_t		newf, savf;
Packit Service a8c26c
	Sfnotify_f	notify = _Sfnotify;
Packit Service a8c26c
Packit Service a8c26c
	NOTUSED(val);
Packit Service a8c26c
Packit Service a8c26c
	/* the discipline needs to change only under the following exceptions */
Packit Service a8c26c
	if(type != SF_WRITE && type != SF_SEEK &&
Packit Service a8c26c
	   type != SF_DPUSH && type != SF_DPOP && type != SF_DBUFFER)
Packit Service a8c26c
		return 0;
Packit Service a8c26c
Packit Service a8c26c
	/* try to create the temp file */
Packit Service a8c26c
	SFCLEAR(&newf,NIL(Vtmutex_t*));
Packit Service a8c26c
	newf.flags = SF_STATIC;
Packit Service a8c26c
	newf.mode = SF_AVAIL;
Packit Service a8c26c
Packit Service a8c26c
	if((fd = _tmpfd(f)) < 0 )
Packit Service a8c26c
		return -1;
Packit Service a8c26c
Packit Service a8c26c
	/* make sure that the notify function won't be called here since
Packit Service a8c26c
	   we are only interested in creating the file, not the stream */
Packit Service a8c26c
	_Sfnotify = 0;
Packit Service a8c26c
	sf = sfnew(&newf,NIL(Void_t*),(size_t)SF_UNBOUND,fd,SF_READ|SF_WRITE);
Packit Service a8c26c
	_Sfnotify = notify;
Packit Service a8c26c
	if(!sf)
Packit Service a8c26c
		return -1;
Packit Service a8c26c
Packit Service a8c26c
	if(newf.mutex) /* don't need a mutex for this stream */
Packit Service a8c26c
	{	(void)vtmtxclrlock(newf.mutex);
Packit Service a8c26c
		(void)vtmtxclose(newf.mutex);
Packit Service a8c26c
		newf.mutex = NIL(Vtmutex_t*);
Packit Service a8c26c
	}
Packit Service a8c26c
Packit Service a8c26c
	/* make sure that new stream has the same mode */
Packit Service a8c26c
	if((m = f->flags&(SF_READ|SF_WRITE)) != (SF_READ|SF_WRITE))
Packit Service a8c26c
		sfset(sf, ((~m)&(SF_READ|SF_WRITE)), 0);
Packit Service a8c26c
	sfset(sf, (f->mode&(SF_READ|SF_WRITE)), 1);
Packit Service a8c26c
Packit Service a8c26c
	/* now remake the old stream into the new image */
Packit Service a8c26c
	memcpy((Void_t*)(&savf), (Void_t*)f, sizeof(Sfio_t));
Packit Service a8c26c
	memcpy((Void_t*)f, (Void_t*)sf, sizeof(Sfio_t));
Packit Service a8c26c
	f->push = savf.push;
Packit Service a8c26c
	f->pool = savf.pool;
Packit Service a8c26c
	f->rsrv = savf.rsrv;
Packit Service a8c26c
	f->proc = savf.proc;
Packit Service a8c26c
	f->mutex = savf.mutex;
Packit Service a8c26c
	f->stdio = savf.stdio;
Packit Service a8c26c
Packit Service a8c26c
	/* remove the SF_STATIC bit if it was only set above in making newf */
Packit Service a8c26c
	if(!(savf.flags&SF_STATIC) )
Packit Service a8c26c
		f->flags &= ~SF_STATIC;
Packit Service a8c26c
Packit Service a8c26c
	if(savf.data)
Packit Service a8c26c
	{	SFSTRSIZE(&savf);
Packit Service a8c26c
		if(!(savf.flags&SF_MALLOC) )
Packit Service a8c26c
			(void)sfsetbuf(f,(Void_t*)savf.data,savf.size);
Packit Service a8c26c
		if(savf.extent > 0)
Packit Service a8c26c
			(void)sfwrite(f,(Void_t*)savf.data,(size_t)savf.extent);
Packit Service a8c26c
		(void)sfseek(f,(Sfoff_t)(savf.next - savf.data),SEEK_SET);
Packit Service a8c26c
		if((savf.flags&SF_MALLOC) )
Packit Service a8c26c
			free((Void_t*)savf.data);
Packit Service a8c26c
	}
Packit Service a8c26c
Packit Service a8c26c
	/* announce change of status */
Packit Service a8c26c
	f->disc = NIL(Sfdisc_t*);
Packit Service a8c26c
	if(_Sfnotify)
Packit Service a8c26c
		(*_Sfnotify)(f, SF_SETFD, (void*)((long)f->file));
Packit Service a8c26c
Packit Service a8c26c
	/* erase all traces of newf */
Packit Service a8c26c
	newf.data = newf.endb = newf.endr = newf.endw = NIL(uchar*);
Packit Service a8c26c
	newf.file = -1;
Packit Service a8c26c
	_Sfnotify = 0;
Packit Service a8c26c
	sfclose(&newf);
Packit Service a8c26c
	_Sfnotify = notify;
Packit Service a8c26c
Packit Service a8c26c
	return 1;
Packit Service a8c26c
}
Packit Service a8c26c
Packit Service a8c26c
#if __STD_C
Packit Service a8c26c
Sfio_t* sftmp(size_t s)
Packit Service a8c26c
#else
Packit Service a8c26c
Sfio_t* sftmp(s)
Packit Service a8c26c
size_t	s;
Packit Service a8c26c
#endif
Packit Service a8c26c
{
Packit Service a8c26c
	Sfio_t		*f;
Packit Service a8c26c
	int		rv;
Packit Service a8c26c
	Sfnotify_f	notify = _Sfnotify;
Packit Service a8c26c
	static Sfdisc_t	Tmpdisc = 
Packit Service a8c26c
			{ NIL(Sfread_f), NIL(Sfwrite_f), NIL(Sfseek_f), _tmpexcept,
Packit Service a8c26c
#if _tmp_rmfail	
Packit Service a8c26c
			  &Rmdisc
Packit Service a8c26c
#else
Packit Service a8c26c
			NIL(Sfdisc_t*)
Packit Service a8c26c
#endif
Packit Service a8c26c
			};
Packit Service a8c26c
Packit Service a8c26c
	/* start with a memory resident stream */
Packit Service a8c26c
	_Sfnotify = 0; /* local computation so no notification */
Packit Service a8c26c
	f = sfnew(NIL(Sfio_t*),NIL(char*),s,-1,SF_STRING|SF_READ|SF_WRITE);
Packit Service a8c26c
	_Sfnotify = notify;
Packit Service a8c26c
	if(!f)
Packit Service a8c26c
		return NIL(Sfio_t*);
Packit Service a8c26c
Packit Service a8c26c
	if(s != (size_t)SF_UNBOUND)	/* set up a discipline for out-of-bound, etc. */
Packit Service a8c26c
		f->disc = &Tmpdisc;
Packit Service a8c26c
Packit Service a8c26c
	if(s == 0) /* make the file now */
Packit Service a8c26c
	{	_Sfnotify = 0; /* local computation so no notification */
Packit Service a8c26c
		rv =  _tmpexcept(f,SF_DPOP,NIL(Void_t*),f->disc);
Packit Service a8c26c
		_Sfnotify = notify;
Packit Service a8c26c
		if(rv < 0)
Packit Service a8c26c
		{	sfclose(f);
Packit Service a8c26c
			return NIL(Sfio_t*);
Packit Service a8c26c
		}
Packit Service a8c26c
	}
Packit Service a8c26c
Packit Service a8c26c
	if(_Sfnotify)
Packit Service a8c26c
		(*_Sfnotify)(f, SF_NEW, (void*)((long)f->file));
Packit Service a8c26c
Packit Service a8c26c
	return f;
Packit Service a8c26c
}