xref: /onnv-gate/usr/src/lib/gss_mechs/mech_krb5/profile/prof_file.c (revision 7934:6aeeafc994de)
11914Scasper /*
2*7934SMark.Phalan@Sun.COM  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
31914Scasper  * Use is subject to license terms.
41914Scasper  */
50Sstevel@tonic-gate /*
60Sstevel@tonic-gate  * prof_file.c ---- routines that manipulate an individual profile file.
70Sstevel@tonic-gate  */
80Sstevel@tonic-gate 
90Sstevel@tonic-gate #include <autoconf.h>
10781Sgtb #include "prof_int.h"
110Sstevel@tonic-gate 
120Sstevel@tonic-gate #include <stdio.h>
130Sstevel@tonic-gate #ifdef HAVE_STDLIB_H
140Sstevel@tonic-gate #include <stdlib.h>
150Sstevel@tonic-gate #endif
160Sstevel@tonic-gate #ifdef HAVE_UNISTD_H
170Sstevel@tonic-gate #include <unistd.h>
180Sstevel@tonic-gate #endif
190Sstevel@tonic-gate #include <string.h>
20781Sgtb #include <stddef.h>
210Sstevel@tonic-gate 
220Sstevel@tonic-gate #include <sys/types.h>
230Sstevel@tonic-gate #include <sys/stat.h>
240Sstevel@tonic-gate #include <errno.h>
250Sstevel@tonic-gate 
26781Sgtb #ifdef HAVE_PWD_H
27781Sgtb #include <pwd.h>
28781Sgtb #endif
290Sstevel@tonic-gate 
30781Sgtb #if defined(_WIN32)
310Sstevel@tonic-gate #include <io.h>
320Sstevel@tonic-gate #define HAVE_STAT
330Sstevel@tonic-gate #define stat _stat
340Sstevel@tonic-gate #endif
350Sstevel@tonic-gate 
36781Sgtb #include "k5-platform.h"
37781Sgtb 
38781Sgtb struct global_shared_profile_data {
39781Sgtb 	/* This is the head of the global list of shared trees */
40781Sgtb 	prf_data_t trees;
41781Sgtb 	/* Lock for above list.  */
42781Sgtb 	k5_mutex_t mutex;
43781Sgtb };
44781Sgtb #define g_shared_trees		(krb5int_profile_shared_data.trees)
45781Sgtb #define g_shared_trees_mutex	(krb5int_profile_shared_data.mutex)
46781Sgtb 
47781Sgtb static struct global_shared_profile_data krb5int_profile_shared_data = {
48781Sgtb     0,
49781Sgtb     K5_MUTEX_PARTIAL_INITIALIZER
50781Sgtb };
51781Sgtb 
52781Sgtb MAKE_INIT_FUNCTION(profile_library_initializer);
53781Sgtb MAKE_FINI_FUNCTION(profile_library_finalizer);
540Sstevel@tonic-gate 
profile_library_initializer(void)55781Sgtb int profile_library_initializer(void)
56781Sgtb {
57*7934SMark.Phalan@Sun.COM #ifdef SHOW_INITFINI_FUNCS
58*7934SMark.Phalan@Sun.COM     printf("profile_library_initializer\n");
59*7934SMark.Phalan@Sun.COM #endif
60781Sgtb #if !USE_BUNDLE_ERROR_STRINGS
61781Sgtb     add_error_table(&et_prof_error_table);
62781Sgtb #endif
63781Sgtb     return k5_mutex_finish_init(&g_shared_trees_mutex);
64781Sgtb }
profile_library_finalizer(void)65781Sgtb void profile_library_finalizer(void)
66781Sgtb {
67*7934SMark.Phalan@Sun.COM     if (! INITIALIZER_RAN(profile_library_initializer) || PROGRAM_EXITING()) {
68*7934SMark.Phalan@Sun.COM #ifdef SHOW_INITFINI_FUNCS
69*7934SMark.Phalan@Sun.COM 	printf("profile_library_finalizer: skipping\n");
70*7934SMark.Phalan@Sun.COM #endif
71781Sgtb 	return;
72*7934SMark.Phalan@Sun.COM     }
73*7934SMark.Phalan@Sun.COM #ifdef SHOW_INITFINI_FUNCS
74*7934SMark.Phalan@Sun.COM     printf("profile_library_finalizer\n");
75*7934SMark.Phalan@Sun.COM #endif
76781Sgtb     k5_mutex_destroy(&g_shared_trees_mutex);
77781Sgtb #if !USE_BUNDLE_ERROR_STRINGS
78781Sgtb     remove_error_table(&et_prof_error_table);
79781Sgtb #endif
80781Sgtb }
81781Sgtb 
82781Sgtb static void profile_free_file_data(prf_data_t);
83781Sgtb 
84781Sgtb #if 0
85781Sgtb 
86781Sgtb #define scan_shared_trees_locked()				\
87781Sgtb 	{							\
88781Sgtb 	    prf_data_t d;					\
89781Sgtb 	    k5_mutex_assert_locked(&g_shared_trees_mutex);	\
90781Sgtb 	    for (d = g_shared_trees; d; d = d->next) {		\
91781Sgtb 		assert(d->magic == PROF_MAGIC_FILE_DATA);	\
92781Sgtb 		assert((d->flags & PROFILE_FILE_SHARED) != 0);	\
93781Sgtb 		assert(d->filespec[0] != 0);			\
94781Sgtb 		assert(d->fslen <= 1000); /* XXX */		\
95781Sgtb 		assert(d->filespec[d->fslen] == 0);		\
96781Sgtb 		assert(d->fslen = strlen(d->filespec));		\
97*7934SMark.Phalan@Sun.COM 		assert(d->root != NULL);			\
98781Sgtb 	    }							\
99781Sgtb 	}
100781Sgtb 
101781Sgtb #define scan_shared_trees_unlocked()			\
102781Sgtb 	{						\
103781Sgtb 	    int r;					\
104781Sgtb 	    r = k5_mutex_lock(&g_shared_trees_mutex);	\
105781Sgtb 	    assert (r == 0);				\
106781Sgtb 	    scan_shared_trees_locked();			\
107781Sgtb 	    k5_mutex_unlock(&g_shared_trees_mutex);	\
108781Sgtb 	}
109781Sgtb 
110781Sgtb #else
111781Sgtb 
112781Sgtb #define scan_shared_trees_locked()	{ ; }
113781Sgtb #define scan_shared_trees_unlocked()	{ ; }
1140Sstevel@tonic-gate 
1150Sstevel@tonic-gate #endif
1160Sstevel@tonic-gate 
rw_access(const_profile_filespec_t filespec)117781Sgtb static int rw_access(const_profile_filespec_t filespec)
1180Sstevel@tonic-gate {
1190Sstevel@tonic-gate #ifdef HAVE_ACCESS
1200Sstevel@tonic-gate 	if (access(filespec, W_OK) == 0)
1210Sstevel@tonic-gate 		return 1;
1220Sstevel@tonic-gate 	else
1230Sstevel@tonic-gate 		return 0;
1240Sstevel@tonic-gate #else
1250Sstevel@tonic-gate 	/*
1260Sstevel@tonic-gate 	 * We're on a substandard OS that doesn't support access.  So
1270Sstevel@tonic-gate 	 * we kludge a test using stdio routines, and hope fopen
1280Sstevel@tonic-gate 	 * checks the r/w permissions.
1290Sstevel@tonic-gate 	 */
1300Sstevel@tonic-gate 	FILE	*f;
131*7934SMark.Phalan@Sun.COM 	/* Solaris Kerberos */
1321914Scasper 	f = fopen(filespec, "r+F");
133781Sgtb 	if (f) {
134781Sgtb 		fclose(f);
135781Sgtb 		return 1;
136781Sgtb 	}
137781Sgtb 	return 0;
138781Sgtb #endif
139781Sgtb }
140781Sgtb 
r_access(const_profile_filespec_t filespec)141781Sgtb static int r_access(const_profile_filespec_t filespec)
142781Sgtb {
143781Sgtb #ifdef HAVE_ACCESS
144781Sgtb 	if (access(filespec, R_OK) == 0)
145781Sgtb 		return 1;
146781Sgtb 	else
147781Sgtb 		return 0;
1480Sstevel@tonic-gate #else
149781Sgtb 	/*
150781Sgtb 	 * We're on a substandard OS that doesn't support access.  So
151781Sgtb 	 * we kludge a test using stdio routines, and hope fopen
152781Sgtb 	 * checks the r/w permissions.
153781Sgtb 	 */
154781Sgtb 	FILE	*f;
155781Sgtb 
156*7934SMark.Phalan@Sun.COM 	/* Solaris Kerberos */
1571914Scasper 	f = fopen(filespec, "rF");
1580Sstevel@tonic-gate 	if (f) {
1590Sstevel@tonic-gate 		fclose(f);
1600Sstevel@tonic-gate 		return 1;
1610Sstevel@tonic-gate 	}
1620Sstevel@tonic-gate 	return 0;
1630Sstevel@tonic-gate #endif
1640Sstevel@tonic-gate }
1650Sstevel@tonic-gate 
166781Sgtb prf_data_t
profile_make_prf_data(const char * filename)167781Sgtb profile_make_prf_data(const char *filename)
168781Sgtb {
169781Sgtb     prf_data_t d;
170781Sgtb     size_t len, flen, slen;
171781Sgtb     char *fcopy;
172781Sgtb 
173781Sgtb     flen = strlen(filename);
174781Sgtb     slen = offsetof(struct _prf_data_t, filespec);
175781Sgtb     len = slen + flen + 1;
176781Sgtb     if (len < sizeof(struct _prf_data_t))
177781Sgtb 	len = sizeof(struct _prf_data_t);
178781Sgtb     d = malloc(len);
179781Sgtb     if (d == NULL)
180781Sgtb 	return NULL;
181781Sgtb     memset(d, 0, len);
182781Sgtb     fcopy = (char *) d + slen;
183781Sgtb     assert(fcopy == d->filespec);
184781Sgtb     strcpy(fcopy, filename);
185781Sgtb     d->refcount = 1;
186781Sgtb     d->comment = NULL;
187781Sgtb     d->magic = PROF_MAGIC_FILE_DATA;
188781Sgtb     d->root = NULL;
189781Sgtb     d->next = NULL;
190781Sgtb     d->fslen = flen;
191781Sgtb     return d;
192781Sgtb }
193781Sgtb 
profile_open_file(const_profile_filespec_t filespec,prf_file_t * ret_prof)194781Sgtb errcode_t profile_open_file(const_profile_filespec_t filespec,
195781Sgtb 			    prf_file_t *ret_prof)
1960Sstevel@tonic-gate {
1970Sstevel@tonic-gate 	prf_file_t	prf;
1980Sstevel@tonic-gate 	errcode_t	retval;
1990Sstevel@tonic-gate 	char		*home_env = 0;
200781Sgtb 	unsigned int	len;
201781Sgtb 	prf_data_t	data;
202781Sgtb 	char		*expanded_filename;
203781Sgtb 
204781Sgtb 	retval = CALL_INIT_FUNCTION(profile_library_initializer);
205781Sgtb 	if (retval)
206781Sgtb 		return retval;
207781Sgtb 
208781Sgtb 	scan_shared_trees_unlocked();
2090Sstevel@tonic-gate 
210*7934SMark.Phalan@Sun.COM 	prf = malloc(sizeof(struct _prf_file_t));
2110Sstevel@tonic-gate 	if (!prf)
2120Sstevel@tonic-gate 		return ENOMEM;
2130Sstevel@tonic-gate 	memset(prf, 0, sizeof(struct _prf_file_t));
214781Sgtb 	prf->magic = PROF_MAGIC_FILE;
215781Sgtb 
2160Sstevel@tonic-gate 	len = strlen(filespec)+1;
2170Sstevel@tonic-gate 	if (filespec[0] == '~' && filespec[1] == '/') {
2180Sstevel@tonic-gate 		home_env = getenv("HOME");
219781Sgtb #ifdef HAVE_PWD_H
220781Sgtb 		if (home_env == NULL) {
221781Sgtb 		    uid_t uid;
222*7934SMark.Phalan@Sun.COM 		    struct passwd *pw, pwx;
223781Sgtb 		    char pwbuf[BUFSIZ];
224781Sgtb 
225781Sgtb 		    uid = getuid();
226*7934SMark.Phalan@Sun.COM 		    if (!k5_getpwuid_r(uid, &pwx, pwbuf, sizeof(pwbuf), &pw)
227*7934SMark.Phalan@Sun.COM 			&& pw != NULL && pw->pw_dir[0] != 0)
228781Sgtb 			home_env = pw->pw_dir;
229781Sgtb 		}
230781Sgtb #endif
2310Sstevel@tonic-gate 		if (home_env)
2320Sstevel@tonic-gate 			len += strlen(home_env);
2330Sstevel@tonic-gate 	}
234781Sgtb 	expanded_filename = malloc(len);
235781Sgtb 	if (expanded_filename == 0)
236781Sgtb 	    return errno;
2370Sstevel@tonic-gate 	if (home_env) {
238781Sgtb 	    strcpy(expanded_filename, home_env);
239781Sgtb 	    strcat(expanded_filename, filespec+1);
2400Sstevel@tonic-gate 	} else
241781Sgtb 	    memcpy(expanded_filename, filespec, len);
242781Sgtb 
243781Sgtb 	retval = k5_mutex_lock(&g_shared_trees_mutex);
244781Sgtb 	if (retval) {
245781Sgtb 	    free(expanded_filename);
246781Sgtb 	    free(prf);
247781Sgtb 	    scan_shared_trees_unlocked();
248781Sgtb 	    return retval;
249781Sgtb 	}
250781Sgtb 	scan_shared_trees_locked();
251781Sgtb 	for (data = g_shared_trees; data; data = data->next) {
252781Sgtb 	    if (!strcmp(data->filespec, expanded_filename)
253781Sgtb 		/* Check that current uid has read access.  */
254781Sgtb 		&& r_access(data->filespec))
255781Sgtb 		break;
256781Sgtb 	}
257781Sgtb 	if (data) {
258781Sgtb 	    data->refcount++;
259781Sgtb 	    (void) k5_mutex_unlock(&g_shared_trees_mutex);
260*7934SMark.Phalan@Sun.COM 	    retval = profile_update_file_data(data);
261781Sgtb 	    free(expanded_filename);
262781Sgtb 	    prf->data = data;
263781Sgtb 	    *ret_prof = prf;
264781Sgtb 	    scan_shared_trees_unlocked();
265781Sgtb 	    return retval;
266781Sgtb 	}
267781Sgtb 	(void) k5_mutex_unlock(&g_shared_trees_mutex);
268781Sgtb 	data = profile_make_prf_data(expanded_filename);
269781Sgtb 	if (data == NULL) {
270781Sgtb 	    free(prf);
271781Sgtb 	    free(expanded_filename);
272781Sgtb 	    return ENOMEM;
273781Sgtb 	}
274781Sgtb 	free(expanded_filename);
275781Sgtb 	prf->data = data;
276781Sgtb 
277781Sgtb 	retval = k5_mutex_init(&data->lock);
278781Sgtb 	if (retval) {
279781Sgtb 	    free(data);
280781Sgtb 	    free(prf);
281781Sgtb 	    return retval;
282781Sgtb 	}
2830Sstevel@tonic-gate 
2840Sstevel@tonic-gate 	retval = profile_update_file(prf);
2850Sstevel@tonic-gate 	if (retval) {
2860Sstevel@tonic-gate 		profile_close_file(prf);
2870Sstevel@tonic-gate 		return retval;
2880Sstevel@tonic-gate 	}
2890Sstevel@tonic-gate 
290781Sgtb 	retval = k5_mutex_lock(&g_shared_trees_mutex);
291781Sgtb 	if (retval) {
292781Sgtb 	    profile_close_file(prf);
293781Sgtb 	    scan_shared_trees_unlocked();
294781Sgtb 	    return retval;
295781Sgtb 	}
296781Sgtb 	scan_shared_trees_locked();
297781Sgtb 	data->flags |= PROFILE_FILE_SHARED;
298781Sgtb 	data->next = g_shared_trees;
299781Sgtb 	g_shared_trees = data;
300781Sgtb 	scan_shared_trees_locked();
301781Sgtb 	(void) k5_mutex_unlock(&g_shared_trees_mutex);
302781Sgtb 
3030Sstevel@tonic-gate 	*ret_prof = prf;
3040Sstevel@tonic-gate 	return 0;
3050Sstevel@tonic-gate }
3060Sstevel@tonic-gate 
profile_update_file_data(prf_data_t data)307781Sgtb errcode_t profile_update_file_data(prf_data_t data)
3080Sstevel@tonic-gate {
3090Sstevel@tonic-gate 	errcode_t retval;
3100Sstevel@tonic-gate #ifdef HAVE_STAT
3110Sstevel@tonic-gate 	struct stat st;
312*7934SMark.Phalan@Sun.COM 	unsigned long frac;
313781Sgtb 	time_t now;
314781Sgtb #endif
3150Sstevel@tonic-gate 	FILE *f;
3160Sstevel@tonic-gate 
317781Sgtb 	retval = k5_mutex_lock(&data->lock);
318781Sgtb 	if (retval)
319781Sgtb 	    return retval;
320781Sgtb 
3210Sstevel@tonic-gate #ifdef HAVE_STAT
322781Sgtb 	now = time(0);
323*7934SMark.Phalan@Sun.COM 	if (now == data->last_stat && data->root != NULL) {
324781Sgtb 	    k5_mutex_unlock(&data->lock);
325781Sgtb 	    return 0;
326781Sgtb 	}
327781Sgtb 	if (stat(data->filespec, &st)) {
328781Sgtb 	    retval = errno;
329781Sgtb 	    k5_mutex_unlock(&data->lock);
330781Sgtb 	    return retval;
3310Sstevel@tonic-gate 	}
332781Sgtb 	data->last_stat = now;
333*7934SMark.Phalan@Sun.COM #if defined HAVE_STRUCT_STAT_ST_MTIMENSEC
334*7934SMark.Phalan@Sun.COM 	frac = st.st_mtimensec;
335*7934SMark.Phalan@Sun.COM #elif defined HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
336*7934SMark.Phalan@Sun.COM 	frac = st.st_mtimespec.tv_nsec;
337*7934SMark.Phalan@Sun.COM #elif defined HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
338*7934SMark.Phalan@Sun.COM 	frac = st.st_mtim.tv_nsec;
339*7934SMark.Phalan@Sun.COM #else
340*7934SMark.Phalan@Sun.COM 	frac = 0;
341781Sgtb #endif
342*7934SMark.Phalan@Sun.COM 	if (st.st_mtime == data->timestamp
343*7934SMark.Phalan@Sun.COM 	    && frac == data->frac_ts
344*7934SMark.Phalan@Sun.COM 	    && data->root != NULL) {
345781Sgtb 	    k5_mutex_unlock(&data->lock);
346781Sgtb 	    return 0;
347781Sgtb 	}
348781Sgtb 	if (data->root) {
349781Sgtb 		profile_free_node(data->root);
350781Sgtb 		data->root = 0;
351781Sgtb 	}
352781Sgtb 	if (data->comment) {
353781Sgtb 		free(data->comment);
354781Sgtb 		data->comment = 0;
3550Sstevel@tonic-gate 	}
3560Sstevel@tonic-gate #else
3570Sstevel@tonic-gate 	/*
3580Sstevel@tonic-gate 	 * If we don't have the stat() call, assume that our in-core
3590Sstevel@tonic-gate 	 * memory image is correct.  That is, we won't reread the
3600Sstevel@tonic-gate 	 * profile file if it changes.
3610Sstevel@tonic-gate 	 */
362781Sgtb 	if (data->root) {
363781Sgtb 	    k5_mutex_unlock(&data->lock);
364781Sgtb 	    return 0;
365781Sgtb 	}
3660Sstevel@tonic-gate #endif
3670Sstevel@tonic-gate 	errno = 0;
368*7934SMark.Phalan@Sun.COM 	/* Solaris Kerberos */
3691914Scasper 	f = fopen(data->filespec, "rF");
3700Sstevel@tonic-gate 	if (f == NULL) {
3710Sstevel@tonic-gate 		retval = errno;
372781Sgtb 		k5_mutex_unlock(&data->lock);
3730Sstevel@tonic-gate 		if (retval == 0)
3740Sstevel@tonic-gate 			retval = ENOENT;
3750Sstevel@tonic-gate 		return retval;
3760Sstevel@tonic-gate 	}
377781Sgtb 	data->upd_serial++;
378781Sgtb 	data->flags &= PROFILE_FILE_SHARED;
379781Sgtb 	if (rw_access(data->filespec))
380781Sgtb 		data->flags |= PROFILE_FILE_RW;
381781Sgtb 	retval = profile_parse_file(f, &data->root);
3820Sstevel@tonic-gate 	fclose(f);
383781Sgtb 	if (retval) {
384781Sgtb 	    k5_mutex_unlock(&data->lock);
385781Sgtb 	    return retval;
386781Sgtb 	}
387*7934SMark.Phalan@Sun.COM 	assert(data->root != NULL);
3880Sstevel@tonic-gate #ifdef HAVE_STAT
389781Sgtb 	data->timestamp = st.st_mtime;
390*7934SMark.Phalan@Sun.COM 	data->frac_ts = frac;
3910Sstevel@tonic-gate #endif
392781Sgtb 	k5_mutex_unlock(&data->lock);
3930Sstevel@tonic-gate 	return 0;
3940Sstevel@tonic-gate }
3950Sstevel@tonic-gate 
396781Sgtb static int
make_hard_link(const char * oldpath,const char * newpath)397781Sgtb make_hard_link(const char *oldpath, const char *newpath)
3980Sstevel@tonic-gate {
399781Sgtb #ifdef _WIN32
400781Sgtb     return -1;
401781Sgtb #else
402781Sgtb     return link(oldpath, newpath);
403781Sgtb #endif
4040Sstevel@tonic-gate }
4050Sstevel@tonic-gate 
write_data_to_file(prf_data_t data,const char * outfile,int can_create)406781Sgtb static errcode_t write_data_to_file(prf_data_t data, const char *outfile,
407781Sgtb 				    int can_create)
4080Sstevel@tonic-gate {
4090Sstevel@tonic-gate 	FILE		*f;
4100Sstevel@tonic-gate 	profile_filespec_t new_file;
4110Sstevel@tonic-gate 	profile_filespec_t old_file;
4120Sstevel@tonic-gate 	errcode_t	retval = 0;
4130Sstevel@tonic-gate 
4140Sstevel@tonic-gate 	retval = ENOMEM;
4150Sstevel@tonic-gate 
4160Sstevel@tonic-gate 	new_file = old_file = 0;
417*7934SMark.Phalan@Sun.COM 	new_file = malloc(strlen(outfile) + 5);
4180Sstevel@tonic-gate 	if (!new_file)
4190Sstevel@tonic-gate 		goto errout;
420*7934SMark.Phalan@Sun.COM 	old_file = malloc(strlen(outfile) + 5);
4210Sstevel@tonic-gate 	if (!old_file)
4220Sstevel@tonic-gate 		goto errout;
4230Sstevel@tonic-gate 
424781Sgtb 	sprintf(new_file, "%s.$$$", outfile);
425781Sgtb 	sprintf(old_file, "%s.bak", outfile);
4260Sstevel@tonic-gate 
4270Sstevel@tonic-gate 	errno = 0;
4280Sstevel@tonic-gate 
429*7934SMark.Phalan@Sun.COM 	/* Solaris Kerberos */
4301914Scasper 	f = fopen(new_file, "wF");
4310Sstevel@tonic-gate 	if (!f) {
4320Sstevel@tonic-gate 		retval = errno;
4330Sstevel@tonic-gate 		if (retval == 0)
4340Sstevel@tonic-gate 			retval = PROF_FAIL_OPEN;
4350Sstevel@tonic-gate 		goto errout;
4360Sstevel@tonic-gate 	}
4370Sstevel@tonic-gate 
438781Sgtb 	profile_write_tree_file(data->root, f);
4390Sstevel@tonic-gate 	if (fclose(f) != 0) {
4400Sstevel@tonic-gate 		retval = errno;
4410Sstevel@tonic-gate 		goto errout;
4420Sstevel@tonic-gate 	}
4430Sstevel@tonic-gate 
4440Sstevel@tonic-gate 	unlink(old_file);
445781Sgtb 	if (make_hard_link(outfile, old_file) == 0) {
446781Sgtb 	    /* Okay, got the hard link.  Yay.  Now we've got our
447781Sgtb 	       backup version, so just put the new version in
448781Sgtb 	       place.  */
449781Sgtb 	    if (rename(new_file, outfile)) {
450781Sgtb 		/* Weird, the rename didn't work.  But the old version
451781Sgtb 		   should still be in place, so no special cleanup is
452781Sgtb 		   needed.  */
453781Sgtb 		retval = errno;
454781Sgtb 		goto errout;
455781Sgtb 	    }
456781Sgtb 	} else if (errno == ENOENT && can_create) {
457781Sgtb 	    if (rename(new_file, outfile)) {
4580Sstevel@tonic-gate 		retval = errno;
4590Sstevel@tonic-gate 		goto errout;
460781Sgtb 	    }
461781Sgtb 	} else {
462781Sgtb 	    /* Couldn't make the hard link, so there's going to be a
463781Sgtb 	       small window where data->filespec does not refer to
464781Sgtb 	       either version.  */
465781Sgtb #ifndef _WIN32
466781Sgtb 	    sync();
467781Sgtb #endif
468781Sgtb 	    if (rename(outfile, old_file)) {
4690Sstevel@tonic-gate 		retval = errno;
4700Sstevel@tonic-gate 		goto errout;
471781Sgtb 	    }
472781Sgtb 	    if (rename(new_file, outfile)) {
473781Sgtb 		retval = errno;
474781Sgtb 		rename(old_file, outfile); /* back out... */
475781Sgtb 		goto errout;
476781Sgtb 	    }
4770Sstevel@tonic-gate 	}
4780Sstevel@tonic-gate 
479781Sgtb 	data->flags = 0;
480781Sgtb 	if (rw_access(outfile))
481781Sgtb 		data->flags |= PROFILE_FILE_RW;
482781Sgtb 	retval = 0;
4830Sstevel@tonic-gate 
4840Sstevel@tonic-gate errout:
4850Sstevel@tonic-gate 	if (new_file)
4860Sstevel@tonic-gate 		free(new_file);
4870Sstevel@tonic-gate 	if (old_file)
4880Sstevel@tonic-gate 		free(old_file);
489781Sgtb 	return retval;
490781Sgtb }
491781Sgtb 
profile_flush_file_data_to_buffer(prf_data_t data,char ** bufp)492781Sgtb errcode_t profile_flush_file_data_to_buffer (prf_data_t data, char **bufp)
493781Sgtb {
494781Sgtb 	errcode_t	retval;
495781Sgtb 	retval = k5_mutex_lock(&data->lock);
496781Sgtb 	if (retval)
497781Sgtb 		return retval;
498781Sgtb 	retval = profile_write_tree_to_buffer(data->root, bufp);
499781Sgtb 	k5_mutex_unlock(&data->lock);
5000Sstevel@tonic-gate 	return retval;
5010Sstevel@tonic-gate }
5020Sstevel@tonic-gate 
profile_flush_file_data(prf_data_t data)503781Sgtb errcode_t profile_flush_file_data(prf_data_t data)
504781Sgtb {
505781Sgtb 	errcode_t	retval = 0;
506781Sgtb 
507781Sgtb 	if (!data || data->magic != PROF_MAGIC_FILE_DATA)
508781Sgtb 		return PROF_MAGIC_FILE_DATA;
509781Sgtb 
510781Sgtb 	retval = k5_mutex_lock(&data->lock);
511781Sgtb 	if (retval)
512781Sgtb 	    return retval;
513781Sgtb 
514781Sgtb 	if ((data->flags & PROFILE_FILE_DIRTY) == 0) {
515781Sgtb 	    k5_mutex_unlock(&data->lock);
516781Sgtb 	    return 0;
517781Sgtb 	}
518781Sgtb 
519781Sgtb 	retval = write_data_to_file(data, data->filespec, 0);
520781Sgtb 	k5_mutex_unlock(&data->lock);
521781Sgtb 	return retval;
522781Sgtb }
523781Sgtb 
profile_flush_file_data_to_file(prf_data_t data,const char * outfile)524781Sgtb errcode_t profile_flush_file_data_to_file(prf_data_t data, const char *outfile)
525781Sgtb {
526781Sgtb     errcode_t retval = 0;
527781Sgtb 
528781Sgtb     if (!data || data->magic != PROF_MAGIC_FILE_DATA)
529781Sgtb 	return PROF_MAGIC_FILE_DATA;
530781Sgtb 
531781Sgtb     retval = k5_mutex_lock(&data->lock);
532781Sgtb     if (retval)
533781Sgtb 	return retval;
534781Sgtb     retval = write_data_to_file(data, outfile, 1);
535781Sgtb     k5_mutex_unlock(&data->lock);
536781Sgtb     return retval;
537781Sgtb }
538781Sgtb 
5390Sstevel@tonic-gate 
540781Sgtb 
profile_dereference_data(prf_data_t data)541781Sgtb void profile_dereference_data(prf_data_t data)
542781Sgtb {
543781Sgtb     int err;
544781Sgtb     err = k5_mutex_lock(&g_shared_trees_mutex);
545781Sgtb     if (err)
546781Sgtb 	return;
547781Sgtb     profile_dereference_data_locked(data);
548781Sgtb     (void) k5_mutex_unlock(&g_shared_trees_mutex);
549781Sgtb }
profile_dereference_data_locked(prf_data_t data)550781Sgtb void profile_dereference_data_locked(prf_data_t data)
5510Sstevel@tonic-gate {
552*7934SMark.Phalan@Sun.COM     scan_shared_trees_locked();
553781Sgtb     data->refcount--;
554781Sgtb     if (data->refcount == 0)
555781Sgtb 	profile_free_file_data(data);
556*7934SMark.Phalan@Sun.COM     scan_shared_trees_locked();
557781Sgtb }
5580Sstevel@tonic-gate 
profile_lock_global()559781Sgtb int profile_lock_global()
560781Sgtb {
561781Sgtb     return k5_mutex_lock(&g_shared_trees_mutex);
562781Sgtb }
profile_unlock_global()563781Sgtb int profile_unlock_global()
564781Sgtb {
565781Sgtb     return k5_mutex_unlock(&g_shared_trees_mutex);
566781Sgtb }
567781Sgtb 
profile_free_file(prf_file_t prf)568781Sgtb void profile_free_file(prf_file_t prf)
569781Sgtb {
570781Sgtb     profile_dereference_data(prf->data);
571781Sgtb     free(prf);
5720Sstevel@tonic-gate }
5730Sstevel@tonic-gate 
574781Sgtb /* Call with mutex locked!  */
profile_free_file_data(prf_data_t data)575781Sgtb static void profile_free_file_data(prf_data_t data)
576781Sgtb {
577781Sgtb     scan_shared_trees_locked();
578781Sgtb     if (data->flags & PROFILE_FILE_SHARED) {
579781Sgtb 	/* Remove from linked list.  */
580781Sgtb 	if (g_shared_trees == data)
581781Sgtb 	    g_shared_trees = data->next;
582781Sgtb 	else {
583781Sgtb 	    prf_data_t prev, next;
584781Sgtb 	    prev = g_shared_trees;
585781Sgtb 	    next = prev->next;
586781Sgtb 	    while (next) {
587781Sgtb 		if (next == data) {
588781Sgtb 		    prev->next = next->next;
589781Sgtb 		    break;
590781Sgtb 		}
591781Sgtb 		prev = next;
592781Sgtb 		next = next->next;
593781Sgtb 	    }
594781Sgtb 	}
595781Sgtb     }
596781Sgtb     if (data->root)
597781Sgtb 	profile_free_node(data->root);
598781Sgtb     if (data->comment)
599781Sgtb 	free(data->comment);
600781Sgtb     data->magic = 0;
601781Sgtb     k5_mutex_destroy(&data->lock);
602781Sgtb     free(data);
603781Sgtb     scan_shared_trees_locked();
604781Sgtb }
605781Sgtb 
profile_close_file(prf_file_t prf)606781Sgtb errcode_t profile_close_file(prf_file_t prf)
6070Sstevel@tonic-gate {
6080Sstevel@tonic-gate 	errcode_t	retval;
6090Sstevel@tonic-gate 
6100Sstevel@tonic-gate 	retval = profile_flush_file(prf);
6110Sstevel@tonic-gate 	if (retval)
6120Sstevel@tonic-gate 		return retval;
6130Sstevel@tonic-gate 	profile_free_file(prf);
6140Sstevel@tonic-gate 	return 0;
6150Sstevel@tonic-gate }
616