1*004388ebScasper /*
2*004388ebScasper  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
3*004388ebScasper  * Use is subject to license terms.
4*004388ebScasper  */
57c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
67c478bd9Sstevel@tonic-gate /*
77c478bd9Sstevel@tonic-gate  * prof_file.c ---- routines that manipulate an individual profile file.
87c478bd9Sstevel@tonic-gate  */
97c478bd9Sstevel@tonic-gate 
107c478bd9Sstevel@tonic-gate #include <autoconf.h>
11505d05c7Sgtb #include "prof_int.h"
127c478bd9Sstevel@tonic-gate 
137c478bd9Sstevel@tonic-gate #include <stdio.h>
147c478bd9Sstevel@tonic-gate #ifdef HAVE_STDLIB_H
157c478bd9Sstevel@tonic-gate #include <stdlib.h>
167c478bd9Sstevel@tonic-gate #endif
177c478bd9Sstevel@tonic-gate #ifdef HAVE_UNISTD_H
187c478bd9Sstevel@tonic-gate #include <unistd.h>
197c478bd9Sstevel@tonic-gate #endif
207c478bd9Sstevel@tonic-gate #include <string.h>
21505d05c7Sgtb #include <stddef.h>
227c478bd9Sstevel@tonic-gate 
237c478bd9Sstevel@tonic-gate #include <sys/types.h>
247c478bd9Sstevel@tonic-gate #include <sys/stat.h>
257c478bd9Sstevel@tonic-gate #include <errno.h>
267c478bd9Sstevel@tonic-gate 
27505d05c7Sgtb #ifdef HAVE_PWD_H
28505d05c7Sgtb #include <pwd.h>
29505d05c7Sgtb #endif
307c478bd9Sstevel@tonic-gate 
31505d05c7Sgtb #if defined(_WIN32)
327c478bd9Sstevel@tonic-gate #include <io.h>
337c478bd9Sstevel@tonic-gate #define HAVE_STAT
347c478bd9Sstevel@tonic-gate #define stat _stat
357c478bd9Sstevel@tonic-gate #endif
367c478bd9Sstevel@tonic-gate 
37505d05c7Sgtb #include "k5-platform.h"
38505d05c7Sgtb 
39505d05c7Sgtb struct global_shared_profile_data {
40505d05c7Sgtb 	/* This is the head of the global list of shared trees */
41505d05c7Sgtb 	prf_data_t trees;
42505d05c7Sgtb 	/* Lock for above list.  */
43505d05c7Sgtb 	k5_mutex_t mutex;
44505d05c7Sgtb };
45505d05c7Sgtb #define g_shared_trees		(krb5int_profile_shared_data.trees)
46505d05c7Sgtb #define g_shared_trees_mutex	(krb5int_profile_shared_data.mutex)
47505d05c7Sgtb 
48505d05c7Sgtb static struct global_shared_profile_data krb5int_profile_shared_data = {
49505d05c7Sgtb     0,
50505d05c7Sgtb     K5_MUTEX_PARTIAL_INITIALIZER
51505d05c7Sgtb };
52505d05c7Sgtb 
53505d05c7Sgtb MAKE_INIT_FUNCTION(profile_library_initializer);
54505d05c7Sgtb MAKE_FINI_FUNCTION(profile_library_finalizer);
55505d05c7Sgtb 
56505d05c7Sgtb int profile_library_initializer(void)
57505d05c7Sgtb {
58505d05c7Sgtb #if !USE_BUNDLE_ERROR_STRINGS
59505d05c7Sgtb     add_error_table(&et_prof_error_table);
60505d05c7Sgtb #endif
61505d05c7Sgtb     return k5_mutex_finish_init(&g_shared_trees_mutex);
62505d05c7Sgtb }
63505d05c7Sgtb void profile_library_finalizer(void)
64505d05c7Sgtb {
65505d05c7Sgtb     if (! INITIALIZER_RAN(profile_library_initializer) || PROGRAM_EXITING())
66505d05c7Sgtb 	return;
67505d05c7Sgtb     k5_mutex_destroy(&g_shared_trees_mutex);
68505d05c7Sgtb #if !USE_BUNDLE_ERROR_STRINGS
69505d05c7Sgtb     remove_error_table(&et_prof_error_table);
70505d05c7Sgtb #endif
71505d05c7Sgtb }
72505d05c7Sgtb 
73505d05c7Sgtb static void profile_free_file_data(prf_data_t);
74505d05c7Sgtb 
75505d05c7Sgtb #if 0
76505d05c7Sgtb 
77505d05c7Sgtb #define scan_shared_trees_locked()				\
78505d05c7Sgtb 	{							\
79505d05c7Sgtb 	    prf_data_t d;					\
80505d05c7Sgtb 	    k5_mutex_assert_locked(&g_shared_trees_mutex);	\
81505d05c7Sgtb 	    for (d = g_shared_trees; d; d = d->next) {		\
82505d05c7Sgtb 		assert(d->magic == PROF_MAGIC_FILE_DATA);	\
83505d05c7Sgtb 		assert((d->flags & PROFILE_FILE_SHARED) != 0);	\
84505d05c7Sgtb 		assert(d->filespec[0] != 0);			\
85505d05c7Sgtb 		assert(d->fslen <= 1000); /* XXX */		\
86505d05c7Sgtb 		assert(d->filespec[d->fslen] == 0);		\
87505d05c7Sgtb 		assert(d->fslen = strlen(d->filespec));		\
88505d05c7Sgtb 	    }							\
89505d05c7Sgtb 	}
90505d05c7Sgtb 
91505d05c7Sgtb #define scan_shared_trees_unlocked()			\
92505d05c7Sgtb 	{						\
93505d05c7Sgtb 	    int r;					\
94505d05c7Sgtb 	    r = k5_mutex_lock(&g_shared_trees_mutex);	\
95505d05c7Sgtb 	    assert (r == 0);				\
96505d05c7Sgtb 	    scan_shared_trees_locked();			\
97505d05c7Sgtb 	    k5_mutex_unlock(&g_shared_trees_mutex);	\
98505d05c7Sgtb 	}
99505d05c7Sgtb 
100505d05c7Sgtb #else
1017c478bd9Sstevel@tonic-gate 
102505d05c7Sgtb #define scan_shared_trees_locked()	{ ; }
103505d05c7Sgtb #define scan_shared_trees_unlocked()	{ ; }
1047c478bd9Sstevel@tonic-gate 
1057c478bd9Sstevel@tonic-gate #endif
1067c478bd9Sstevel@tonic-gate 
107505d05c7Sgtb static int rw_access(const_profile_filespec_t filespec)
1087c478bd9Sstevel@tonic-gate {
1097c478bd9Sstevel@tonic-gate #ifdef HAVE_ACCESS
1107c478bd9Sstevel@tonic-gate 	if (access(filespec, W_OK) == 0)
1117c478bd9Sstevel@tonic-gate 		return 1;
1127c478bd9Sstevel@tonic-gate 	else
1137c478bd9Sstevel@tonic-gate 		return 0;
1147c478bd9Sstevel@tonic-gate #else
1157c478bd9Sstevel@tonic-gate 	/*
1167c478bd9Sstevel@tonic-gate 	 * We're on a substandard OS that doesn't support access.  So
1177c478bd9Sstevel@tonic-gate 	 * we kludge a test using stdio routines, and hope fopen
1187c478bd9Sstevel@tonic-gate 	 * checks the r/w permissions.
1197c478bd9Sstevel@tonic-gate 	 */
1207c478bd9Sstevel@tonic-gate 	FILE	*f;
1217c478bd9Sstevel@tonic-gate 
122*004388ebScasper 	f = fopen(filespec, "r+F");
123505d05c7Sgtb 	if (f) {
124505d05c7Sgtb 		fclose(f);
125505d05c7Sgtb 		return 1;
126505d05c7Sgtb 	}
127505d05c7Sgtb 	return 0;
1287c478bd9Sstevel@tonic-gate #endif
129505d05c7Sgtb }
130505d05c7Sgtb 
131505d05c7Sgtb static int r_access(const_profile_filespec_t filespec)
132505d05c7Sgtb {
133505d05c7Sgtb #ifdef HAVE_ACCESS
134505d05c7Sgtb 	if (access(filespec, R_OK) == 0)
135505d05c7Sgtb 		return 1;
136505d05c7Sgtb 	else
137505d05c7Sgtb 		return 0;
138505d05c7Sgtb #else
139505d05c7Sgtb 	/*
140505d05c7Sgtb 	 * We're on a substandard OS that doesn't support access.  So
141505d05c7Sgtb 	 * we kludge a test using stdio routines, and hope fopen
142505d05c7Sgtb 	 * checks the r/w permissions.
143505d05c7Sgtb 	 */
144505d05c7Sgtb 	FILE	*f;
145505d05c7Sgtb 
146*004388ebScasper 	f = fopen(filespec, "rF");
1477c478bd9Sstevel@tonic-gate 	if (f) {
1487c478bd9Sstevel@tonic-gate 		fclose(f);
1497c478bd9Sstevel@tonic-gate 		return 1;
1507c478bd9Sstevel@tonic-gate 	}
1517c478bd9Sstevel@tonic-gate 	return 0;
1527c478bd9Sstevel@tonic-gate #endif
1537c478bd9Sstevel@tonic-gate }
1547c478bd9Sstevel@tonic-gate 
155505d05c7Sgtb prf_data_t
156505d05c7Sgtb profile_make_prf_data(const char *filename)
157505d05c7Sgtb {
158505d05c7Sgtb     prf_data_t d;
159505d05c7Sgtb     size_t len, flen, slen;
160505d05c7Sgtb     char *fcopy;
161505d05c7Sgtb 
162505d05c7Sgtb     flen = strlen(filename);
163505d05c7Sgtb     slen = offsetof(struct _prf_data_t, filespec);
164505d05c7Sgtb     len = slen + flen + 1;
165505d05c7Sgtb     if (len < sizeof(struct _prf_data_t))
166505d05c7Sgtb 	len = sizeof(struct _prf_data_t);
167505d05c7Sgtb     d = malloc(len);
168505d05c7Sgtb     if (d == NULL)
169505d05c7Sgtb 	return NULL;
170505d05c7Sgtb     memset(d, 0, len);
171505d05c7Sgtb     fcopy = (char *) d + slen;
172505d05c7Sgtb     assert(fcopy == d->filespec);
173505d05c7Sgtb     strcpy(fcopy, filename);
174505d05c7Sgtb     d->refcount = 1;
175505d05c7Sgtb     d->comment = NULL;
176505d05c7Sgtb     d->magic = PROF_MAGIC_FILE_DATA;
177505d05c7Sgtb     d->root = NULL;
178505d05c7Sgtb     d->next = NULL;
179505d05c7Sgtb     d->fslen = flen;
180505d05c7Sgtb     return d;
181505d05c7Sgtb }
182505d05c7Sgtb 
183505d05c7Sgtb errcode_t profile_open_file(const_profile_filespec_t filespec,
184505d05c7Sgtb 			    prf_file_t *ret_prof)
1857c478bd9Sstevel@tonic-gate {
1867c478bd9Sstevel@tonic-gate 	prf_file_t	prf;
1877c478bd9Sstevel@tonic-gate 	errcode_t	retval;
1887c478bd9Sstevel@tonic-gate 	char		*home_env = 0;
189505d05c7Sgtb 	unsigned int	len;
190505d05c7Sgtb 	prf_data_t	data;
191505d05c7Sgtb 	char		*expanded_filename;
192505d05c7Sgtb 
193505d05c7Sgtb 	retval = CALL_INIT_FUNCTION(profile_library_initializer);
194505d05c7Sgtb 	if (retval)
195505d05c7Sgtb 		return retval;
196505d05c7Sgtb 
197505d05c7Sgtb 	scan_shared_trees_unlocked();
1987c478bd9Sstevel@tonic-gate 
1997c478bd9Sstevel@tonic-gate 	prf = (prf_file_t) malloc(sizeof(struct _prf_file_t));
2007c478bd9Sstevel@tonic-gate 	if (!prf)
2017c478bd9Sstevel@tonic-gate 		return ENOMEM;
2027c478bd9Sstevel@tonic-gate 	memset(prf, 0, sizeof(struct _prf_file_t));
203505d05c7Sgtb 	prf->magic = PROF_MAGIC_FILE;
204505d05c7Sgtb 
2057c478bd9Sstevel@tonic-gate 	len = strlen(filespec)+1;
2067c478bd9Sstevel@tonic-gate 	if (filespec[0] == '~' && filespec[1] == '/') {
2077c478bd9Sstevel@tonic-gate 		home_env = getenv("HOME");
208505d05c7Sgtb #ifdef HAVE_PWD_H
209505d05c7Sgtb 		if (home_env == NULL) {
210505d05c7Sgtb 		    uid_t uid;
211505d05c7Sgtb 		    struct passwd *pw;
212505d05c7Sgtb #ifdef HAVE_GETPWUID_R
213505d05c7Sgtb 		    struct passwd pwx;
214505d05c7Sgtb 		    char pwbuf[BUFSIZ];
215505d05c7Sgtb #endif
216505d05c7Sgtb 
217505d05c7Sgtb 		    uid = getuid();
218505d05c7Sgtb #ifndef HAVE_GETPWUID_R
219505d05c7Sgtb 		    pw = getpwuid(uid);
220505d05c7Sgtb #elif defined(GETPWUID_R_4_ARGS)
221505d05c7Sgtb 		    /* earlier POSIX drafts */
222505d05c7Sgtb 		    pw = getpwuid_r(uid, &pwx, pwbuf, sizeof(pwbuf));
223505d05c7Sgtb #else
224505d05c7Sgtb 		    /* POSIX */
225505d05c7Sgtb 		    if (getpwuid_r(uid, &pwx, pwbuf, sizeof(pwbuf), &pw) != 0)
226505d05c7Sgtb 			/* Probably already null, but let's make sure.  */
227505d05c7Sgtb 			pw = NULL;
228505d05c7Sgtb #endif /* getpwuid variants */
229505d05c7Sgtb 		    if (pw != NULL && pw->pw_dir[0] != 0)
230505d05c7Sgtb 			home_env = pw->pw_dir;
231505d05c7Sgtb 		}
232505d05c7Sgtb #endif
2337c478bd9Sstevel@tonic-gate 		if (home_env)
2347c478bd9Sstevel@tonic-gate 			len += strlen(home_env);
2357c478bd9Sstevel@tonic-gate 	}
236505d05c7Sgtb 	expanded_filename = malloc(len);
237505d05c7Sgtb 	if (expanded_filename == 0)
238505d05c7Sgtb 	    return errno;
2397c478bd9Sstevel@tonic-gate 	if (home_env) {
240505d05c7Sgtb 	    strcpy(expanded_filename, home_env);
241505d05c7Sgtb 	    strcat(expanded_filename, filespec+1);
2427c478bd9Sstevel@tonic-gate 	} else
243505d05c7Sgtb 	    memcpy(expanded_filename, filespec, len);
244505d05c7Sgtb 
245505d05c7Sgtb 	retval = k5_mutex_lock(&g_shared_trees_mutex);
246505d05c7Sgtb 	if (retval) {
247505d05c7Sgtb 	    free(expanded_filename);
248505d05c7Sgtb 	    free(prf);
249505d05c7Sgtb 	    scan_shared_trees_unlocked();
250505d05c7Sgtb 	    return retval;
251505d05c7Sgtb 	}
252505d05c7Sgtb 	scan_shared_trees_locked();
253505d05c7Sgtb 	for (data = g_shared_trees; data; data = data->next) {
254505d05c7Sgtb 	    if (!strcmp(data->filespec, expanded_filename)
255505d05c7Sgtb 		/* Check that current uid has read access.  */
256505d05c7Sgtb 		&& r_access(data->filespec))
257505d05c7Sgtb 		break;
258505d05c7Sgtb 	}
259505d05c7Sgtb 	if (data) {
260505d05c7Sgtb 	    retval = profile_update_file_data(data);
261505d05c7Sgtb 	    data->refcount++;
262505d05c7Sgtb 	    (void) k5_mutex_unlock(&g_shared_trees_mutex);
263505d05c7Sgtb 	    free(expanded_filename);
264505d05c7Sgtb 	    prf->data = data;
265505d05c7Sgtb 	    *ret_prof = prf;
266505d05c7Sgtb 	    scan_shared_trees_unlocked();
267505d05c7Sgtb 	    return retval;
268505d05c7Sgtb 	}
269505d05c7Sgtb 	(void) k5_mutex_unlock(&g_shared_trees_mutex);
270505d05c7Sgtb 	data = profile_make_prf_data(expanded_filename);
271505d05c7Sgtb 	if (data == NULL) {
272505d05c7Sgtb 	    free(prf);
273505d05c7Sgtb 	    free(expanded_filename);
274505d05c7Sgtb 	    return ENOMEM;
275505d05c7Sgtb 	}
276505d05c7Sgtb 	free(expanded_filename);
277505d05c7Sgtb 	prf->data = data;
278505d05c7Sgtb 
279505d05c7Sgtb 	retval = k5_mutex_init(&data->lock);
280505d05c7Sgtb 	if (retval) {
281505d05c7Sgtb 	    free(data);
282505d05c7Sgtb 	    free(prf);
283505d05c7Sgtb 	    return retval;
284505d05c7Sgtb 	}
2857c478bd9Sstevel@tonic-gate 
2867c478bd9Sstevel@tonic-gate 	retval = profile_update_file(prf);
2877c478bd9Sstevel@tonic-gate 	if (retval) {
2887c478bd9Sstevel@tonic-gate 		profile_close_file(prf);
2897c478bd9Sstevel@tonic-gate 		return retval;
2907c478bd9Sstevel@tonic-gate 	}
2917c478bd9Sstevel@tonic-gate 
292505d05c7Sgtb 	retval = k5_mutex_lock(&g_shared_trees_mutex);
293505d05c7Sgtb 	if (retval) {
294505d05c7Sgtb 	    profile_close_file(prf);
295505d05c7Sgtb 	    scan_shared_trees_unlocked();
296505d05c7Sgtb 	    return retval;
297505d05c7Sgtb 	}
298505d05c7Sgtb 	scan_shared_trees_locked();
299505d05c7Sgtb 	data->flags |= PROFILE_FILE_SHARED;
300505d05c7Sgtb 	data->next = g_shared_trees;
301505d05c7Sgtb 	g_shared_trees = data;
302505d05c7Sgtb 	scan_shared_trees_locked();
303505d05c7Sgtb 	(void) k5_mutex_unlock(&g_shared_trees_mutex);
304505d05c7Sgtb 
3057c478bd9Sstevel@tonic-gate 	*ret_prof = prf;
3067c478bd9Sstevel@tonic-gate 	return 0;
3077c478bd9Sstevel@tonic-gate }
3087c478bd9Sstevel@tonic-gate 
309505d05c7Sgtb errcode_t profile_update_file_data(prf_data_t data)
3107c478bd9Sstevel@tonic-gate {
3117c478bd9Sstevel@tonic-gate 	errcode_t retval;
3127c478bd9Sstevel@tonic-gate #ifdef HAVE_STAT
3137c478bd9Sstevel@tonic-gate 	struct stat st;
314505d05c7Sgtb #ifdef STAT_ONCE_PER_SECOND
315505d05c7Sgtb 	time_t now;
316505d05c7Sgtb #endif
3177c478bd9Sstevel@tonic-gate #endif
3187c478bd9Sstevel@tonic-gate 	FILE *f;
3197c478bd9Sstevel@tonic-gate 
320505d05c7Sgtb 	retval = k5_mutex_lock(&data->lock);
321505d05c7Sgtb 	if (retval)
322505d05c7Sgtb 	    return retval;
323505d05c7Sgtb 
3247c478bd9Sstevel@tonic-gate #ifdef HAVE_STAT
325505d05c7Sgtb #ifdef STAT_ONCE_PER_SECOND
326505d05c7Sgtb 	now = time(0);
327505d05c7Sgtb 	if (now == data->last_stat) {
328505d05c7Sgtb 	    k5_mutex_unlock(&data->lock);
329505d05c7Sgtb 	    return 0;
3307c478bd9Sstevel@tonic-gate 	}
331505d05c7Sgtb #endif
332505d05c7Sgtb 	if (stat(data->filespec, &st)) {
333505d05c7Sgtb 	    retval = errno;
334505d05c7Sgtb 	    k5_mutex_unlock(&data->lock);
335505d05c7Sgtb 	    return retval;
336505d05c7Sgtb 	}
337505d05c7Sgtb #ifdef STAT_ONCE_PER_SECOND
338505d05c7Sgtb 	data->last_stat = now;
339505d05c7Sgtb #endif
340505d05c7Sgtb 	if (st.st_mtime == data->timestamp) {
341505d05c7Sgtb 	    k5_mutex_unlock(&data->lock);
342505d05c7Sgtb 	    return 0;
343505d05c7Sgtb 	}
344505d05c7Sgtb 	if (data->root) {
345505d05c7Sgtb 		profile_free_node(data->root);
346505d05c7Sgtb 		data->root = 0;
347505d05c7Sgtb 	}
348505d05c7Sgtb 	if (data->comment) {
349505d05c7Sgtb 		free(data->comment);
350505d05c7Sgtb 		data->comment = 0;
3517c478bd9Sstevel@tonic-gate 	}
3527c478bd9Sstevel@tonic-gate #else
3537c478bd9Sstevel@tonic-gate 	/*
3547c478bd9Sstevel@tonic-gate 	 * If we don't have the stat() call, assume that our in-core
3557c478bd9Sstevel@tonic-gate 	 * memory image is correct.  That is, we won't reread the
3567c478bd9Sstevel@tonic-gate 	 * profile file if it changes.
3577c478bd9Sstevel@tonic-gate 	 */
358505d05c7Sgtb 	if (data->root) {
359505d05c7Sgtb 	    k5_mutex_unlock(&data->lock);
360505d05c7Sgtb 	    return 0;
361505d05c7Sgtb 	}
3627c478bd9Sstevel@tonic-gate #endif
3637c478bd9Sstevel@tonic-gate 	errno = 0;
364*004388ebScasper 	f = fopen(data->filespec, "rF");
3657c478bd9Sstevel@tonic-gate 	if (f == NULL) {
3667c478bd9Sstevel@tonic-gate 		retval = errno;
367505d05c7Sgtb 		k5_mutex_unlock(&data->lock);
3687c478bd9Sstevel@tonic-gate 		if (retval == 0)
3697c478bd9Sstevel@tonic-gate 			retval = ENOENT;
3707c478bd9Sstevel@tonic-gate 		return retval;
3717c478bd9Sstevel@tonic-gate 	}
372505d05c7Sgtb 	data->upd_serial++;
373505d05c7Sgtb 	data->flags &= PROFILE_FILE_SHARED;
374505d05c7Sgtb 	if (rw_access(data->filespec))
375505d05c7Sgtb 		data->flags |= PROFILE_FILE_RW;
376505d05c7Sgtb 	retval = profile_parse_file(f, &data->root);
3777c478bd9Sstevel@tonic-gate 	fclose(f);
378505d05c7Sgtb 	if (retval) {
379505d05c7Sgtb 	    k5_mutex_unlock(&data->lock);
380505d05c7Sgtb 	    return retval;
381505d05c7Sgtb 	}
3827c478bd9Sstevel@tonic-gate #ifdef HAVE_STAT
383505d05c7Sgtb 	data->timestamp = st.st_mtime;
3847c478bd9Sstevel@tonic-gate #endif
385505d05c7Sgtb 	k5_mutex_unlock(&data->lock);
3867c478bd9Sstevel@tonic-gate 	return 0;
3877c478bd9Sstevel@tonic-gate }
3887c478bd9Sstevel@tonic-gate 
389505d05c7Sgtb static int
390505d05c7Sgtb make_hard_link(const char *oldpath, const char *newpath)
3917c478bd9Sstevel@tonic-gate {
392505d05c7Sgtb #ifdef _WIN32
393505d05c7Sgtb     return -1;
394505d05c7Sgtb #else
395505d05c7Sgtb     return link(oldpath, newpath);
3967c478bd9Sstevel@tonic-gate #endif
397505d05c7Sgtb }
3987c478bd9Sstevel@tonic-gate 
399505d05c7Sgtb static errcode_t write_data_to_file(prf_data_t data, const char *outfile,
400505d05c7Sgtb 				    int can_create)
4017c478bd9Sstevel@tonic-gate {
4027c478bd9Sstevel@tonic-gate 	FILE		*f;
4037c478bd9Sstevel@tonic-gate 	profile_filespec_t new_file;
4047c478bd9Sstevel@tonic-gate 	profile_filespec_t old_file;
4057c478bd9Sstevel@tonic-gate 	errcode_t	retval = 0;
4067c478bd9Sstevel@tonic-gate 
4077c478bd9Sstevel@tonic-gate 	retval = ENOMEM;
4087c478bd9Sstevel@tonic-gate 
4097c478bd9Sstevel@tonic-gate 	new_file = old_file = 0;
410505d05c7Sgtb 	new_file = (char *) malloc(strlen(outfile) + 5);
4117c478bd9Sstevel@tonic-gate 	if (!new_file)
4127c478bd9Sstevel@tonic-gate 		goto errout;
413505d05c7Sgtb 	old_file = (char *) malloc(strlen(outfile) + 5);
4147c478bd9Sstevel@tonic-gate 	if (!old_file)
4157c478bd9Sstevel@tonic-gate 		goto errout;
4167c478bd9Sstevel@tonic-gate 
417505d05c7Sgtb 	sprintf(new_file, "%s.$$$", outfile);
418505d05c7Sgtb 	sprintf(old_file, "%s.bak", outfile);
4197c478bd9Sstevel@tonic-gate 
4207c478bd9Sstevel@tonic-gate 	errno = 0;
4217c478bd9Sstevel@tonic-gate 
422*004388ebScasper 	f = fopen(new_file, "wF");
4237c478bd9Sstevel@tonic-gate 	if (!f) {
4247c478bd9Sstevel@tonic-gate 		retval = errno;
4257c478bd9Sstevel@tonic-gate 		if (retval == 0)
4267c478bd9Sstevel@tonic-gate 			retval = PROF_FAIL_OPEN;
4277c478bd9Sstevel@tonic-gate 		goto errout;
4287c478bd9Sstevel@tonic-gate 	}
4297c478bd9Sstevel@tonic-gate 
430505d05c7Sgtb 	profile_write_tree_file(data->root, f);
4317c478bd9Sstevel@tonic-gate 	if (fclose(f) != 0) {
4327c478bd9Sstevel@tonic-gate 		retval = errno;
4337c478bd9Sstevel@tonic-gate 		goto errout;
4347c478bd9Sstevel@tonic-gate 	}
4357c478bd9Sstevel@tonic-gate 
4367c478bd9Sstevel@tonic-gate 	unlink(old_file);
437505d05c7Sgtb 	if (make_hard_link(outfile, old_file) == 0) {
438505d05c7Sgtb 	    /* Okay, got the hard link.  Yay.  Now we've got our
439505d05c7Sgtb 	       backup version, so just put the new version in
440505d05c7Sgtb 	       place.  */
441505d05c7Sgtb 	    if (rename(new_file, outfile)) {
442505d05c7Sgtb 		/* Weird, the rename didn't work.  But the old version
443505d05c7Sgtb 		   should still be in place, so no special cleanup is
444505d05c7Sgtb 		   needed.  */
4457c478bd9Sstevel@tonic-gate 		retval = errno;
4467c478bd9Sstevel@tonic-gate 		goto errout;
447505d05c7Sgtb 	    }
448505d05c7Sgtb 	} else if (errno == ENOENT && can_create) {
449505d05c7Sgtb 	    if (rename(new_file, outfile)) {
4507c478bd9Sstevel@tonic-gate 		retval = errno;
4517c478bd9Sstevel@tonic-gate 		goto errout;
452505d05c7Sgtb 	    }
453505d05c7Sgtb 	} else {
454505d05c7Sgtb 	    /* Couldn't make the hard link, so there's going to be a
455505d05c7Sgtb 	       small window where data->filespec does not refer to
456505d05c7Sgtb 	       either version.  */
457505d05c7Sgtb #ifndef _WIN32
458505d05c7Sgtb 	    sync();
4597c478bd9Sstevel@tonic-gate #endif
460505d05c7Sgtb 	    if (rename(outfile, old_file)) {
461505d05c7Sgtb 		retval = errno;
462505d05c7Sgtb 		goto errout;
463505d05c7Sgtb 	    }
464505d05c7Sgtb 	    if (rename(new_file, outfile)) {
465505d05c7Sgtb 		retval = errno;
466505d05c7Sgtb 		rename(old_file, outfile); /* back out... */
467505d05c7Sgtb 		goto errout;
468505d05c7Sgtb 	    }
469505d05c7Sgtb 	}
4707c478bd9Sstevel@tonic-gate 
471505d05c7Sgtb 	data->flags = 0;
472505d05c7Sgtb 	if (rw_access(outfile))
473505d05c7Sgtb 		data->flags |= PROFILE_FILE_RW;
4747c478bd9Sstevel@tonic-gate 	retval = 0;
475505d05c7Sgtb 
4767c478bd9Sstevel@tonic-gate errout:
4777c478bd9Sstevel@tonic-gate 	if (new_file)
4787c478bd9Sstevel@tonic-gate 		free(new_file);
4797c478bd9Sstevel@tonic-gate 	if (old_file)
4807c478bd9Sstevel@tonic-gate 		free(old_file);
4817c478bd9Sstevel@tonic-gate 	return retval;
4827c478bd9Sstevel@tonic-gate }
4837c478bd9Sstevel@tonic-gate 
484505d05c7Sgtb errcode_t profile_flush_file_data_to_buffer (prf_data_t data, char **bufp)
485505d05c7Sgtb {
486505d05c7Sgtb 	errcode_t	retval;
487505d05c7Sgtb 	retval = k5_mutex_lock(&data->lock);
488505d05c7Sgtb 	if (retval)
489505d05c7Sgtb 		return retval;
490505d05c7Sgtb 	retval = profile_write_tree_to_buffer(data->root, bufp);
491505d05c7Sgtb 	k5_mutex_unlock(&data->lock);
492505d05c7Sgtb 	return retval;
493505d05c7Sgtb }
4947c478bd9Sstevel@tonic-gate 
495505d05c7Sgtb errcode_t profile_flush_file_data(prf_data_t data)
4967c478bd9Sstevel@tonic-gate {
497505d05c7Sgtb 	errcode_t	retval = 0;
498505d05c7Sgtb 
499505d05c7Sgtb 	if (!data || data->magic != PROF_MAGIC_FILE_DATA)
500505d05c7Sgtb 		return PROF_MAGIC_FILE_DATA;
501505d05c7Sgtb 
502505d05c7Sgtb 	retval = k5_mutex_lock(&data->lock);
503505d05c7Sgtb 	if (retval)
504505d05c7Sgtb 	    return retval;
505505d05c7Sgtb 
506505d05c7Sgtb 	if ((data->flags & PROFILE_FILE_DIRTY) == 0) {
507505d05c7Sgtb 	    k5_mutex_unlock(&data->lock);
508505d05c7Sgtb 	    return 0;
509505d05c7Sgtb 	}
510505d05c7Sgtb 
511505d05c7Sgtb 	retval = write_data_to_file(data, data->filespec, 0);
512505d05c7Sgtb 	k5_mutex_unlock(&data->lock);
513505d05c7Sgtb 	return retval;
514505d05c7Sgtb }
515505d05c7Sgtb 
516505d05c7Sgtb errcode_t profile_flush_file_data_to_file(prf_data_t data, const char *outfile)
517505d05c7Sgtb {
518505d05c7Sgtb     errcode_t retval = 0;
519505d05c7Sgtb 
520505d05c7Sgtb     if (!data || data->magic != PROF_MAGIC_FILE_DATA)
521505d05c7Sgtb 	return PROF_MAGIC_FILE_DATA;
522505d05c7Sgtb 
523505d05c7Sgtb     retval = k5_mutex_lock(&data->lock);
524505d05c7Sgtb     if (retval)
525505d05c7Sgtb 	return retval;
526505d05c7Sgtb     retval = write_data_to_file(data, outfile, 1);
527505d05c7Sgtb     k5_mutex_unlock(&data->lock);
528505d05c7Sgtb     return retval;
529505d05c7Sgtb }
5307c478bd9Sstevel@tonic-gate 
531505d05c7Sgtb 
532505d05c7Sgtb 
533505d05c7Sgtb void profile_dereference_data(prf_data_t data)
534505d05c7Sgtb {
535505d05c7Sgtb     int err;
536505d05c7Sgtb     scan_shared_trees_unlocked();
537505d05c7Sgtb     err = k5_mutex_lock(&g_shared_trees_mutex);
538505d05c7Sgtb     if (err)
5397c478bd9Sstevel@tonic-gate 	return;
540505d05c7Sgtb     profile_dereference_data_locked(data);
541505d05c7Sgtb     (void) k5_mutex_unlock(&g_shared_trees_mutex);
542505d05c7Sgtb     scan_shared_trees_unlocked();
543505d05c7Sgtb }
544505d05c7Sgtb void profile_dereference_data_locked(prf_data_t data)
545505d05c7Sgtb {
546505d05c7Sgtb     data->refcount--;
547505d05c7Sgtb     if (data->refcount == 0)
548505d05c7Sgtb 	profile_free_file_data(data);
549505d05c7Sgtb }
550505d05c7Sgtb 
551505d05c7Sgtb int profile_lock_global()
552505d05c7Sgtb {
553505d05c7Sgtb     return k5_mutex_lock(&g_shared_trees_mutex);
554505d05c7Sgtb }
555505d05c7Sgtb int profile_unlock_global()
556505d05c7Sgtb {
557505d05c7Sgtb     return k5_mutex_unlock(&g_shared_trees_mutex);
5587c478bd9Sstevel@tonic-gate }
5597c478bd9Sstevel@tonic-gate 
560505d05c7Sgtb void profile_free_file(prf_file_t prf)
561505d05c7Sgtb {
562505d05c7Sgtb     profile_dereference_data(prf->data);
563505d05c7Sgtb     free(prf);
564505d05c7Sgtb }
565505d05c7Sgtb 
566505d05c7Sgtb /* Call with mutex locked!  */
567505d05c7Sgtb static void profile_free_file_data(prf_data_t data)
568505d05c7Sgtb {
569505d05c7Sgtb     scan_shared_trees_locked();
570505d05c7Sgtb     if (data->flags & PROFILE_FILE_SHARED) {
571505d05c7Sgtb 	/* Remove from linked list.  */
572505d05c7Sgtb 	if (g_shared_trees == data)
573505d05c7Sgtb 	    g_shared_trees = data->next;
574505d05c7Sgtb 	else {
575505d05c7Sgtb 	    prf_data_t prev, next;
576505d05c7Sgtb 	    prev = g_shared_trees;
577505d05c7Sgtb 	    next = prev->next;
578505d05c7Sgtb 	    while (next) {
579505d05c7Sgtb 		if (next == data) {
580505d05c7Sgtb 		    prev->next = next->next;
581505d05c7Sgtb 		    break;
582505d05c7Sgtb 		}
583505d05c7Sgtb 		prev = next;
584505d05c7Sgtb 		next = next->next;
585505d05c7Sgtb 	    }
586505d05c7Sgtb 	}
587505d05c7Sgtb     }
588505d05c7Sgtb     if (data->root)
589505d05c7Sgtb 	profile_free_node(data->root);
590505d05c7Sgtb     if (data->comment)
591505d05c7Sgtb 	free(data->comment);
592505d05c7Sgtb     data->magic = 0;
593505d05c7Sgtb     k5_mutex_destroy(&data->lock);
594505d05c7Sgtb     free(data);
595505d05c7Sgtb     scan_shared_trees_locked();
596505d05c7Sgtb }
597505d05c7Sgtb 
598505d05c7Sgtb errcode_t profile_close_file(prf_file_t prf)
5997c478bd9Sstevel@tonic-gate {
6007c478bd9Sstevel@tonic-gate 	errcode_t	retval;
6017c478bd9Sstevel@tonic-gate 
6027c478bd9Sstevel@tonic-gate 	retval = profile_flush_file(prf);
6037c478bd9Sstevel@tonic-gate 	if (retval)
6047c478bd9Sstevel@tonic-gate 		return retval;
6057c478bd9Sstevel@tonic-gate 	profile_free_file(prf);
6067c478bd9Sstevel@tonic-gate 	return 0;
6077c478bd9Sstevel@tonic-gate }
608