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