17c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
27c478bd9Sstevel@tonic-gate /*
37c478bd9Sstevel@tonic-gate  * prof_file.c ---- routines that manipulate an individual profile file.
47c478bd9Sstevel@tonic-gate  */
57c478bd9Sstevel@tonic-gate 
67c478bd9Sstevel@tonic-gate #include <autoconf.h>
7*505d05c7Sgtb #include "prof_int.h"
87c478bd9Sstevel@tonic-gate 
97c478bd9Sstevel@tonic-gate #include <stdio.h>
107c478bd9Sstevel@tonic-gate #ifdef HAVE_STDLIB_H
117c478bd9Sstevel@tonic-gate #include <stdlib.h>
127c478bd9Sstevel@tonic-gate #endif
137c478bd9Sstevel@tonic-gate #ifdef HAVE_UNISTD_H
147c478bd9Sstevel@tonic-gate #include <unistd.h>
157c478bd9Sstevel@tonic-gate #endif
167c478bd9Sstevel@tonic-gate #include <string.h>
17*505d05c7Sgtb #include <stddef.h>
187c478bd9Sstevel@tonic-gate 
197c478bd9Sstevel@tonic-gate #include <sys/types.h>
207c478bd9Sstevel@tonic-gate #include <sys/stat.h>
217c478bd9Sstevel@tonic-gate #include <errno.h>
227c478bd9Sstevel@tonic-gate 
23*505d05c7Sgtb #ifdef HAVE_PWD_H
24*505d05c7Sgtb #include <pwd.h>
25*505d05c7Sgtb #endif
267c478bd9Sstevel@tonic-gate 
27*505d05c7Sgtb #if defined(_WIN32)
287c478bd9Sstevel@tonic-gate #include <io.h>
297c478bd9Sstevel@tonic-gate #define HAVE_STAT
307c478bd9Sstevel@tonic-gate #define stat _stat
317c478bd9Sstevel@tonic-gate #endif
327c478bd9Sstevel@tonic-gate 
33*505d05c7Sgtb #include "k5-platform.h"
34*505d05c7Sgtb 
35*505d05c7Sgtb struct global_shared_profile_data {
36*505d05c7Sgtb 	/* This is the head of the global list of shared trees */
37*505d05c7Sgtb 	prf_data_t trees;
38*505d05c7Sgtb 	/* Lock for above list.  */
39*505d05c7Sgtb 	k5_mutex_t mutex;
40*505d05c7Sgtb };
41*505d05c7Sgtb #define g_shared_trees		(krb5int_profile_shared_data.trees)
42*505d05c7Sgtb #define g_shared_trees_mutex	(krb5int_profile_shared_data.mutex)
43*505d05c7Sgtb 
44*505d05c7Sgtb static struct global_shared_profile_data krb5int_profile_shared_data = {
45*505d05c7Sgtb     0,
46*505d05c7Sgtb     K5_MUTEX_PARTIAL_INITIALIZER
47*505d05c7Sgtb };
48*505d05c7Sgtb 
49*505d05c7Sgtb MAKE_INIT_FUNCTION(profile_library_initializer);
50*505d05c7Sgtb MAKE_FINI_FUNCTION(profile_library_finalizer);
51*505d05c7Sgtb 
52*505d05c7Sgtb int profile_library_initializer(void)
53*505d05c7Sgtb {
54*505d05c7Sgtb #if !USE_BUNDLE_ERROR_STRINGS
55*505d05c7Sgtb     add_error_table(&et_prof_error_table);
56*505d05c7Sgtb #endif
57*505d05c7Sgtb     return k5_mutex_finish_init(&g_shared_trees_mutex);
58*505d05c7Sgtb }
59*505d05c7Sgtb void profile_library_finalizer(void)
60*505d05c7Sgtb {
61*505d05c7Sgtb     if (! INITIALIZER_RAN(profile_library_initializer) || PROGRAM_EXITING())
62*505d05c7Sgtb 	return;
63*505d05c7Sgtb     k5_mutex_destroy(&g_shared_trees_mutex);
64*505d05c7Sgtb #if !USE_BUNDLE_ERROR_STRINGS
65*505d05c7Sgtb     remove_error_table(&et_prof_error_table);
66*505d05c7Sgtb #endif
67*505d05c7Sgtb }
68*505d05c7Sgtb 
69*505d05c7Sgtb static void profile_free_file_data(prf_data_t);
70*505d05c7Sgtb 
71*505d05c7Sgtb #if 0
72*505d05c7Sgtb 
73*505d05c7Sgtb #define scan_shared_trees_locked()				\
74*505d05c7Sgtb 	{							\
75*505d05c7Sgtb 	    prf_data_t d;					\
76*505d05c7Sgtb 	    k5_mutex_assert_locked(&g_shared_trees_mutex);	\
77*505d05c7Sgtb 	    for (d = g_shared_trees; d; d = d->next) {		\
78*505d05c7Sgtb 		assert(d->magic == PROF_MAGIC_FILE_DATA);	\
79*505d05c7Sgtb 		assert((d->flags & PROFILE_FILE_SHARED) != 0);	\
80*505d05c7Sgtb 		assert(d->filespec[0] != 0);			\
81*505d05c7Sgtb 		assert(d->fslen <= 1000); /* XXX */		\
82*505d05c7Sgtb 		assert(d->filespec[d->fslen] == 0);		\
83*505d05c7Sgtb 		assert(d->fslen = strlen(d->filespec));		\
84*505d05c7Sgtb 	    }							\
85*505d05c7Sgtb 	}
86*505d05c7Sgtb 
87*505d05c7Sgtb #define scan_shared_trees_unlocked()			\
88*505d05c7Sgtb 	{						\
89*505d05c7Sgtb 	    int r;					\
90*505d05c7Sgtb 	    r = k5_mutex_lock(&g_shared_trees_mutex);	\
91*505d05c7Sgtb 	    assert (r == 0);				\
92*505d05c7Sgtb 	    scan_shared_trees_locked();			\
93*505d05c7Sgtb 	    k5_mutex_unlock(&g_shared_trees_mutex);	\
94*505d05c7Sgtb 	}
95*505d05c7Sgtb 
96*505d05c7Sgtb #else
977c478bd9Sstevel@tonic-gate 
98*505d05c7Sgtb #define scan_shared_trees_locked()	{ ; }
99*505d05c7Sgtb #define scan_shared_trees_unlocked()	{ ; }
1007c478bd9Sstevel@tonic-gate 
1017c478bd9Sstevel@tonic-gate #endif
1027c478bd9Sstevel@tonic-gate 
103*505d05c7Sgtb static int rw_access(const_profile_filespec_t filespec)
1047c478bd9Sstevel@tonic-gate {
1057c478bd9Sstevel@tonic-gate #ifdef HAVE_ACCESS
1067c478bd9Sstevel@tonic-gate 	if (access(filespec, W_OK) == 0)
1077c478bd9Sstevel@tonic-gate 		return 1;
1087c478bd9Sstevel@tonic-gate 	else
1097c478bd9Sstevel@tonic-gate 		return 0;
1107c478bd9Sstevel@tonic-gate #else
1117c478bd9Sstevel@tonic-gate 	/*
1127c478bd9Sstevel@tonic-gate 	 * We're on a substandard OS that doesn't support access.  So
1137c478bd9Sstevel@tonic-gate 	 * we kludge a test using stdio routines, and hope fopen
1147c478bd9Sstevel@tonic-gate 	 * checks the r/w permissions.
1157c478bd9Sstevel@tonic-gate 	 */
1167c478bd9Sstevel@tonic-gate 	FILE	*f;
1177c478bd9Sstevel@tonic-gate 
1187c478bd9Sstevel@tonic-gate 	f = fopen(filespec, "r+");
119*505d05c7Sgtb 	if (f) {
120*505d05c7Sgtb 		fclose(f);
121*505d05c7Sgtb 		return 1;
122*505d05c7Sgtb 	}
123*505d05c7Sgtb 	return 0;
1247c478bd9Sstevel@tonic-gate #endif
125*505d05c7Sgtb }
126*505d05c7Sgtb 
127*505d05c7Sgtb static int r_access(const_profile_filespec_t filespec)
128*505d05c7Sgtb {
129*505d05c7Sgtb #ifdef HAVE_ACCESS
130*505d05c7Sgtb 	if (access(filespec, R_OK) == 0)
131*505d05c7Sgtb 		return 1;
132*505d05c7Sgtb 	else
133*505d05c7Sgtb 		return 0;
134*505d05c7Sgtb #else
135*505d05c7Sgtb 	/*
136*505d05c7Sgtb 	 * We're on a substandard OS that doesn't support access.  So
137*505d05c7Sgtb 	 * we kludge a test using stdio routines, and hope fopen
138*505d05c7Sgtb 	 * checks the r/w permissions.
139*505d05c7Sgtb 	 */
140*505d05c7Sgtb 	FILE	*f;
141*505d05c7Sgtb 
142*505d05c7Sgtb 	f = fopen(filespec, "r");
1437c478bd9Sstevel@tonic-gate 	if (f) {
1447c478bd9Sstevel@tonic-gate 		fclose(f);
1457c478bd9Sstevel@tonic-gate 		return 1;
1467c478bd9Sstevel@tonic-gate 	}
1477c478bd9Sstevel@tonic-gate 	return 0;
1487c478bd9Sstevel@tonic-gate #endif
1497c478bd9Sstevel@tonic-gate }
1507c478bd9Sstevel@tonic-gate 
151*505d05c7Sgtb prf_data_t
152*505d05c7Sgtb profile_make_prf_data(const char *filename)
153*505d05c7Sgtb {
154*505d05c7Sgtb     prf_data_t d;
155*505d05c7Sgtb     size_t len, flen, slen;
156*505d05c7Sgtb     char *fcopy;
157*505d05c7Sgtb 
158*505d05c7Sgtb     flen = strlen(filename);
159*505d05c7Sgtb     slen = offsetof(struct _prf_data_t, filespec);
160*505d05c7Sgtb     len = slen + flen + 1;
161*505d05c7Sgtb     if (len < sizeof(struct _prf_data_t))
162*505d05c7Sgtb 	len = sizeof(struct _prf_data_t);
163*505d05c7Sgtb     d = malloc(len);
164*505d05c7Sgtb     if (d == NULL)
165*505d05c7Sgtb 	return NULL;
166*505d05c7Sgtb     memset(d, 0, len);
167*505d05c7Sgtb     fcopy = (char *) d + slen;
168*505d05c7Sgtb     assert(fcopy == d->filespec);
169*505d05c7Sgtb     strcpy(fcopy, filename);
170*505d05c7Sgtb     d->refcount = 1;
171*505d05c7Sgtb     d->comment = NULL;
172*505d05c7Sgtb     d->magic = PROF_MAGIC_FILE_DATA;
173*505d05c7Sgtb     d->root = NULL;
174*505d05c7Sgtb     d->next = NULL;
175*505d05c7Sgtb     d->fslen = flen;
176*505d05c7Sgtb     return d;
177*505d05c7Sgtb }
178*505d05c7Sgtb 
179*505d05c7Sgtb errcode_t profile_open_file(const_profile_filespec_t filespec,
180*505d05c7Sgtb 			    prf_file_t *ret_prof)
1817c478bd9Sstevel@tonic-gate {
1827c478bd9Sstevel@tonic-gate 	prf_file_t	prf;
1837c478bd9Sstevel@tonic-gate 	errcode_t	retval;
1847c478bd9Sstevel@tonic-gate 	char		*home_env = 0;
185*505d05c7Sgtb 	unsigned int	len;
186*505d05c7Sgtb 	prf_data_t	data;
187*505d05c7Sgtb 	char		*expanded_filename;
188*505d05c7Sgtb 
189*505d05c7Sgtb 	retval = CALL_INIT_FUNCTION(profile_library_initializer);
190*505d05c7Sgtb 	if (retval)
191*505d05c7Sgtb 		return retval;
192*505d05c7Sgtb 
193*505d05c7Sgtb 	scan_shared_trees_unlocked();
1947c478bd9Sstevel@tonic-gate 
1957c478bd9Sstevel@tonic-gate 	prf = (prf_file_t) malloc(sizeof(struct _prf_file_t));
1967c478bd9Sstevel@tonic-gate 	if (!prf)
1977c478bd9Sstevel@tonic-gate 		return ENOMEM;
1987c478bd9Sstevel@tonic-gate 	memset(prf, 0, sizeof(struct _prf_file_t));
199*505d05c7Sgtb 	prf->magic = PROF_MAGIC_FILE;
200*505d05c7Sgtb 
2017c478bd9Sstevel@tonic-gate 	len = strlen(filespec)+1;
2027c478bd9Sstevel@tonic-gate 	if (filespec[0] == '~' && filespec[1] == '/') {
2037c478bd9Sstevel@tonic-gate 		home_env = getenv("HOME");
204*505d05c7Sgtb #ifdef HAVE_PWD_H
205*505d05c7Sgtb 		if (home_env == NULL) {
206*505d05c7Sgtb 		    uid_t uid;
207*505d05c7Sgtb 		    struct passwd *pw;
208*505d05c7Sgtb #ifdef HAVE_GETPWUID_R
209*505d05c7Sgtb 		    struct passwd pwx;
210*505d05c7Sgtb 		    char pwbuf[BUFSIZ];
211*505d05c7Sgtb #endif
212*505d05c7Sgtb 
213*505d05c7Sgtb 		    uid = getuid();
214*505d05c7Sgtb #ifndef HAVE_GETPWUID_R
215*505d05c7Sgtb 		    pw = getpwuid(uid);
216*505d05c7Sgtb #elif defined(GETPWUID_R_4_ARGS)
217*505d05c7Sgtb 		    /* earlier POSIX drafts */
218*505d05c7Sgtb 		    pw = getpwuid_r(uid, &pwx, pwbuf, sizeof(pwbuf));
219*505d05c7Sgtb #else
220*505d05c7Sgtb 		    /* POSIX */
221*505d05c7Sgtb 		    if (getpwuid_r(uid, &pwx, pwbuf, sizeof(pwbuf), &pw) != 0)
222*505d05c7Sgtb 			/* Probably already null, but let's make sure.  */
223*505d05c7Sgtb 			pw = NULL;
224*505d05c7Sgtb #endif /* getpwuid variants */
225*505d05c7Sgtb 		    if (pw != NULL && pw->pw_dir[0] != 0)
226*505d05c7Sgtb 			home_env = pw->pw_dir;
227*505d05c7Sgtb 		}
228*505d05c7Sgtb #endif
2297c478bd9Sstevel@tonic-gate 		if (home_env)
2307c478bd9Sstevel@tonic-gate 			len += strlen(home_env);
2317c478bd9Sstevel@tonic-gate 	}
232*505d05c7Sgtb 	expanded_filename = malloc(len);
233*505d05c7Sgtb 	if (expanded_filename == 0)
234*505d05c7Sgtb 	    return errno;
2357c478bd9Sstevel@tonic-gate 	if (home_env) {
236*505d05c7Sgtb 	    strcpy(expanded_filename, home_env);
237*505d05c7Sgtb 	    strcat(expanded_filename, filespec+1);
2387c478bd9Sstevel@tonic-gate 	} else
239*505d05c7Sgtb 	    memcpy(expanded_filename, filespec, len);
240*505d05c7Sgtb 
241*505d05c7Sgtb 	retval = k5_mutex_lock(&g_shared_trees_mutex);
242*505d05c7Sgtb 	if (retval) {
243*505d05c7Sgtb 	    free(expanded_filename);
244*505d05c7Sgtb 	    free(prf);
245*505d05c7Sgtb 	    scan_shared_trees_unlocked();
246*505d05c7Sgtb 	    return retval;
247*505d05c7Sgtb 	}
248*505d05c7Sgtb 	scan_shared_trees_locked();
249*505d05c7Sgtb 	for (data = g_shared_trees; data; data = data->next) {
250*505d05c7Sgtb 	    if (!strcmp(data->filespec, expanded_filename)
251*505d05c7Sgtb 		/* Check that current uid has read access.  */
252*505d05c7Sgtb 		&& r_access(data->filespec))
253*505d05c7Sgtb 		break;
254*505d05c7Sgtb 	}
255*505d05c7Sgtb 	if (data) {
256*505d05c7Sgtb 	    retval = profile_update_file_data(data);
257*505d05c7Sgtb 	    data->refcount++;
258*505d05c7Sgtb 	    (void) k5_mutex_unlock(&g_shared_trees_mutex);
259*505d05c7Sgtb 	    free(expanded_filename);
260*505d05c7Sgtb 	    prf->data = data;
261*505d05c7Sgtb 	    *ret_prof = prf;
262*505d05c7Sgtb 	    scan_shared_trees_unlocked();
263*505d05c7Sgtb 	    return retval;
264*505d05c7Sgtb 	}
265*505d05c7Sgtb 	(void) k5_mutex_unlock(&g_shared_trees_mutex);
266*505d05c7Sgtb 	data = profile_make_prf_data(expanded_filename);
267*505d05c7Sgtb 	if (data == NULL) {
268*505d05c7Sgtb 	    free(prf);
269*505d05c7Sgtb 	    free(expanded_filename);
270*505d05c7Sgtb 	    return ENOMEM;
271*505d05c7Sgtb 	}
272*505d05c7Sgtb 	free(expanded_filename);
273*505d05c7Sgtb 	prf->data = data;
274*505d05c7Sgtb 
275*505d05c7Sgtb 	retval = k5_mutex_init(&data->lock);
276*505d05c7Sgtb 	if (retval) {
277*505d05c7Sgtb 	    free(data);
278*505d05c7Sgtb 	    free(prf);
279*505d05c7Sgtb 	    return retval;
280*505d05c7Sgtb 	}
2817c478bd9Sstevel@tonic-gate 
2827c478bd9Sstevel@tonic-gate 	retval = profile_update_file(prf);
2837c478bd9Sstevel@tonic-gate 	if (retval) {
2847c478bd9Sstevel@tonic-gate 		profile_close_file(prf);
2857c478bd9Sstevel@tonic-gate 		return retval;
2867c478bd9Sstevel@tonic-gate 	}
2877c478bd9Sstevel@tonic-gate 
288*505d05c7Sgtb 	retval = k5_mutex_lock(&g_shared_trees_mutex);
289*505d05c7Sgtb 	if (retval) {
290*505d05c7Sgtb 	    profile_close_file(prf);
291*505d05c7Sgtb 	    scan_shared_trees_unlocked();
292*505d05c7Sgtb 	    return retval;
293*505d05c7Sgtb 	}
294*505d05c7Sgtb 	scan_shared_trees_locked();
295*505d05c7Sgtb 	data->flags |= PROFILE_FILE_SHARED;
296*505d05c7Sgtb 	data->next = g_shared_trees;
297*505d05c7Sgtb 	g_shared_trees = data;
298*505d05c7Sgtb 	scan_shared_trees_locked();
299*505d05c7Sgtb 	(void) k5_mutex_unlock(&g_shared_trees_mutex);
300*505d05c7Sgtb 
3017c478bd9Sstevel@tonic-gate 	*ret_prof = prf;
3027c478bd9Sstevel@tonic-gate 	return 0;
3037c478bd9Sstevel@tonic-gate }
3047c478bd9Sstevel@tonic-gate 
305*505d05c7Sgtb errcode_t profile_update_file_data(prf_data_t data)
3067c478bd9Sstevel@tonic-gate {
3077c478bd9Sstevel@tonic-gate 	errcode_t retval;
3087c478bd9Sstevel@tonic-gate #ifdef HAVE_STAT
3097c478bd9Sstevel@tonic-gate 	struct stat st;
310*505d05c7Sgtb #ifdef STAT_ONCE_PER_SECOND
311*505d05c7Sgtb 	time_t now;
312*505d05c7Sgtb #endif
3137c478bd9Sstevel@tonic-gate #endif
3147c478bd9Sstevel@tonic-gate 	FILE *f;
3157c478bd9Sstevel@tonic-gate 
316*505d05c7Sgtb 	retval = k5_mutex_lock(&data->lock);
317*505d05c7Sgtb 	if (retval)
318*505d05c7Sgtb 	    return retval;
319*505d05c7Sgtb 
3207c478bd9Sstevel@tonic-gate #ifdef HAVE_STAT
321*505d05c7Sgtb #ifdef STAT_ONCE_PER_SECOND
322*505d05c7Sgtb 	now = time(0);
323*505d05c7Sgtb 	if (now == data->last_stat) {
324*505d05c7Sgtb 	    k5_mutex_unlock(&data->lock);
325*505d05c7Sgtb 	    return 0;
3267c478bd9Sstevel@tonic-gate 	}
327*505d05c7Sgtb #endif
328*505d05c7Sgtb 	if (stat(data->filespec, &st)) {
329*505d05c7Sgtb 	    retval = errno;
330*505d05c7Sgtb 	    k5_mutex_unlock(&data->lock);
331*505d05c7Sgtb 	    return retval;
332*505d05c7Sgtb 	}
333*505d05c7Sgtb #ifdef STAT_ONCE_PER_SECOND
334*505d05c7Sgtb 	data->last_stat = now;
335*505d05c7Sgtb #endif
336*505d05c7Sgtb 	if (st.st_mtime == data->timestamp) {
337*505d05c7Sgtb 	    k5_mutex_unlock(&data->lock);
338*505d05c7Sgtb 	    return 0;
339*505d05c7Sgtb 	}
340*505d05c7Sgtb 	if (data->root) {
341*505d05c7Sgtb 		profile_free_node(data->root);
342*505d05c7Sgtb 		data->root = 0;
343*505d05c7Sgtb 	}
344*505d05c7Sgtb 	if (data->comment) {
345*505d05c7Sgtb 		free(data->comment);
346*505d05c7Sgtb 		data->comment = 0;
3477c478bd9Sstevel@tonic-gate 	}
3487c478bd9Sstevel@tonic-gate #else
3497c478bd9Sstevel@tonic-gate 	/*
3507c478bd9Sstevel@tonic-gate 	 * If we don't have the stat() call, assume that our in-core
3517c478bd9Sstevel@tonic-gate 	 * memory image is correct.  That is, we won't reread the
3527c478bd9Sstevel@tonic-gate 	 * profile file if it changes.
3537c478bd9Sstevel@tonic-gate 	 */
354*505d05c7Sgtb 	if (data->root) {
355*505d05c7Sgtb 	    k5_mutex_unlock(&data->lock);
356*505d05c7Sgtb 	    return 0;
357*505d05c7Sgtb 	}
3587c478bd9Sstevel@tonic-gate #endif
3597c478bd9Sstevel@tonic-gate 	errno = 0;
360*505d05c7Sgtb 	f = fopen(data->filespec, "r");
3617c478bd9Sstevel@tonic-gate 	if (f == NULL) {
3627c478bd9Sstevel@tonic-gate 		retval = errno;
363*505d05c7Sgtb 		k5_mutex_unlock(&data->lock);
3647c478bd9Sstevel@tonic-gate 		if (retval == 0)
3657c478bd9Sstevel@tonic-gate 			retval = ENOENT;
3667c478bd9Sstevel@tonic-gate 		return retval;
3677c478bd9Sstevel@tonic-gate 	}
368*505d05c7Sgtb 	data->upd_serial++;
369*505d05c7Sgtb 	data->flags &= PROFILE_FILE_SHARED;
370*505d05c7Sgtb 	if (rw_access(data->filespec))
371*505d05c7Sgtb 		data->flags |= PROFILE_FILE_RW;
372*505d05c7Sgtb 	retval = profile_parse_file(f, &data->root);
3737c478bd9Sstevel@tonic-gate 	fclose(f);
374*505d05c7Sgtb 	if (retval) {
375*505d05c7Sgtb 	    k5_mutex_unlock(&data->lock);
376*505d05c7Sgtb 	    return retval;
377*505d05c7Sgtb 	}
3787c478bd9Sstevel@tonic-gate #ifdef HAVE_STAT
379*505d05c7Sgtb 	data->timestamp = st.st_mtime;
3807c478bd9Sstevel@tonic-gate #endif
381*505d05c7Sgtb 	k5_mutex_unlock(&data->lock);
3827c478bd9Sstevel@tonic-gate 	return 0;
3837c478bd9Sstevel@tonic-gate }
3847c478bd9Sstevel@tonic-gate 
385*505d05c7Sgtb static int
386*505d05c7Sgtb make_hard_link(const char *oldpath, const char *newpath)
3877c478bd9Sstevel@tonic-gate {
388*505d05c7Sgtb #ifdef _WIN32
389*505d05c7Sgtb     return -1;
390*505d05c7Sgtb #else
391*505d05c7Sgtb     return link(oldpath, newpath);
3927c478bd9Sstevel@tonic-gate #endif
393*505d05c7Sgtb }
3947c478bd9Sstevel@tonic-gate 
395*505d05c7Sgtb static errcode_t write_data_to_file(prf_data_t data, const char *outfile,
396*505d05c7Sgtb 				    int can_create)
3977c478bd9Sstevel@tonic-gate {
3987c478bd9Sstevel@tonic-gate 	FILE		*f;
3997c478bd9Sstevel@tonic-gate 	profile_filespec_t new_file;
4007c478bd9Sstevel@tonic-gate 	profile_filespec_t old_file;
4017c478bd9Sstevel@tonic-gate 	errcode_t	retval = 0;
4027c478bd9Sstevel@tonic-gate 
4037c478bd9Sstevel@tonic-gate 	retval = ENOMEM;
4047c478bd9Sstevel@tonic-gate 
4057c478bd9Sstevel@tonic-gate 	new_file = old_file = 0;
406*505d05c7Sgtb 	new_file = (char *) malloc(strlen(outfile) + 5);
4077c478bd9Sstevel@tonic-gate 	if (!new_file)
4087c478bd9Sstevel@tonic-gate 		goto errout;
409*505d05c7Sgtb 	old_file = (char *) malloc(strlen(outfile) + 5);
4107c478bd9Sstevel@tonic-gate 	if (!old_file)
4117c478bd9Sstevel@tonic-gate 		goto errout;
4127c478bd9Sstevel@tonic-gate 
413*505d05c7Sgtb 	sprintf(new_file, "%s.$$$", outfile);
414*505d05c7Sgtb 	sprintf(old_file, "%s.bak", outfile);
4157c478bd9Sstevel@tonic-gate 
4167c478bd9Sstevel@tonic-gate 	errno = 0;
4177c478bd9Sstevel@tonic-gate 
4187c478bd9Sstevel@tonic-gate 	f = fopen(new_file, "w");
4197c478bd9Sstevel@tonic-gate 	if (!f) {
4207c478bd9Sstevel@tonic-gate 		retval = errno;
4217c478bd9Sstevel@tonic-gate 		if (retval == 0)
4227c478bd9Sstevel@tonic-gate 			retval = PROF_FAIL_OPEN;
4237c478bd9Sstevel@tonic-gate 		goto errout;
4247c478bd9Sstevel@tonic-gate 	}
4257c478bd9Sstevel@tonic-gate 
426*505d05c7Sgtb 	profile_write_tree_file(data->root, f);
4277c478bd9Sstevel@tonic-gate 	if (fclose(f) != 0) {
4287c478bd9Sstevel@tonic-gate 		retval = errno;
4297c478bd9Sstevel@tonic-gate 		goto errout;
4307c478bd9Sstevel@tonic-gate 	}
4317c478bd9Sstevel@tonic-gate 
4327c478bd9Sstevel@tonic-gate 	unlink(old_file);
433*505d05c7Sgtb 	if (make_hard_link(outfile, old_file) == 0) {
434*505d05c7Sgtb 	    /* Okay, got the hard link.  Yay.  Now we've got our
435*505d05c7Sgtb 	       backup version, so just put the new version in
436*505d05c7Sgtb 	       place.  */
437*505d05c7Sgtb 	    if (rename(new_file, outfile)) {
438*505d05c7Sgtb 		/* Weird, the rename didn't work.  But the old version
439*505d05c7Sgtb 		   should still be in place, so no special cleanup is
440*505d05c7Sgtb 		   needed.  */
4417c478bd9Sstevel@tonic-gate 		retval = errno;
4427c478bd9Sstevel@tonic-gate 		goto errout;
443*505d05c7Sgtb 	    }
444*505d05c7Sgtb 	} else if (errno == ENOENT && can_create) {
445*505d05c7Sgtb 	    if (rename(new_file, outfile)) {
4467c478bd9Sstevel@tonic-gate 		retval = errno;
4477c478bd9Sstevel@tonic-gate 		goto errout;
448*505d05c7Sgtb 	    }
449*505d05c7Sgtb 	} else {
450*505d05c7Sgtb 	    /* Couldn't make the hard link, so there's going to be a
451*505d05c7Sgtb 	       small window where data->filespec does not refer to
452*505d05c7Sgtb 	       either version.  */
453*505d05c7Sgtb #ifndef _WIN32
454*505d05c7Sgtb 	    sync();
4557c478bd9Sstevel@tonic-gate #endif
456*505d05c7Sgtb 	    if (rename(outfile, old_file)) {
457*505d05c7Sgtb 		retval = errno;
458*505d05c7Sgtb 		goto errout;
459*505d05c7Sgtb 	    }
460*505d05c7Sgtb 	    if (rename(new_file, outfile)) {
461*505d05c7Sgtb 		retval = errno;
462*505d05c7Sgtb 		rename(old_file, outfile); /* back out... */
463*505d05c7Sgtb 		goto errout;
464*505d05c7Sgtb 	    }
465*505d05c7Sgtb 	}
4667c478bd9Sstevel@tonic-gate 
467*505d05c7Sgtb 	data->flags = 0;
468*505d05c7Sgtb 	if (rw_access(outfile))
469*505d05c7Sgtb 		data->flags |= PROFILE_FILE_RW;
4707c478bd9Sstevel@tonic-gate 	retval = 0;
471*505d05c7Sgtb 
4727c478bd9Sstevel@tonic-gate errout:
4737c478bd9Sstevel@tonic-gate 	if (new_file)
4747c478bd9Sstevel@tonic-gate 		free(new_file);
4757c478bd9Sstevel@tonic-gate 	if (old_file)
4767c478bd9Sstevel@tonic-gate 		free(old_file);
4777c478bd9Sstevel@tonic-gate 	return retval;
4787c478bd9Sstevel@tonic-gate }
4797c478bd9Sstevel@tonic-gate 
480*505d05c7Sgtb errcode_t profile_flush_file_data_to_buffer (prf_data_t data, char **bufp)
481*505d05c7Sgtb {
482*505d05c7Sgtb 	errcode_t	retval;
483*505d05c7Sgtb 	retval = k5_mutex_lock(&data->lock);
484*505d05c7Sgtb 	if (retval)
485*505d05c7Sgtb 		return retval;
486*505d05c7Sgtb 	retval = profile_write_tree_to_buffer(data->root, bufp);
487*505d05c7Sgtb 	k5_mutex_unlock(&data->lock);
488*505d05c7Sgtb 	return retval;
489*505d05c7Sgtb }
4907c478bd9Sstevel@tonic-gate 
491*505d05c7Sgtb errcode_t profile_flush_file_data(prf_data_t data)
4927c478bd9Sstevel@tonic-gate {
493*505d05c7Sgtb 	errcode_t	retval = 0;
494*505d05c7Sgtb 
495*505d05c7Sgtb 	if (!data || data->magic != PROF_MAGIC_FILE_DATA)
496*505d05c7Sgtb 		return PROF_MAGIC_FILE_DATA;
497*505d05c7Sgtb 
498*505d05c7Sgtb 	retval = k5_mutex_lock(&data->lock);
499*505d05c7Sgtb 	if (retval)
500*505d05c7Sgtb 	    return retval;
501*505d05c7Sgtb 
502*505d05c7Sgtb 	if ((data->flags & PROFILE_FILE_DIRTY) == 0) {
503*505d05c7Sgtb 	    k5_mutex_unlock(&data->lock);
504*505d05c7Sgtb 	    return 0;
505*505d05c7Sgtb 	}
506*505d05c7Sgtb 
507*505d05c7Sgtb 	retval = write_data_to_file(data, data->filespec, 0);
508*505d05c7Sgtb 	k5_mutex_unlock(&data->lock);
509*505d05c7Sgtb 	return retval;
510*505d05c7Sgtb }
511*505d05c7Sgtb 
512*505d05c7Sgtb errcode_t profile_flush_file_data_to_file(prf_data_t data, const char *outfile)
513*505d05c7Sgtb {
514*505d05c7Sgtb     errcode_t retval = 0;
515*505d05c7Sgtb 
516*505d05c7Sgtb     if (!data || data->magic != PROF_MAGIC_FILE_DATA)
517*505d05c7Sgtb 	return PROF_MAGIC_FILE_DATA;
518*505d05c7Sgtb 
519*505d05c7Sgtb     retval = k5_mutex_lock(&data->lock);
520*505d05c7Sgtb     if (retval)
521*505d05c7Sgtb 	return retval;
522*505d05c7Sgtb     retval = write_data_to_file(data, outfile, 1);
523*505d05c7Sgtb     k5_mutex_unlock(&data->lock);
524*505d05c7Sgtb     return retval;
525*505d05c7Sgtb }
5267c478bd9Sstevel@tonic-gate 
527*505d05c7Sgtb 
528*505d05c7Sgtb 
529*505d05c7Sgtb void profile_dereference_data(prf_data_t data)
530*505d05c7Sgtb {
531*505d05c7Sgtb     int err;
532*505d05c7Sgtb     scan_shared_trees_unlocked();
533*505d05c7Sgtb     err = k5_mutex_lock(&g_shared_trees_mutex);
534*505d05c7Sgtb     if (err)
5357c478bd9Sstevel@tonic-gate 	return;
536*505d05c7Sgtb     profile_dereference_data_locked(data);
537*505d05c7Sgtb     (void) k5_mutex_unlock(&g_shared_trees_mutex);
538*505d05c7Sgtb     scan_shared_trees_unlocked();
539*505d05c7Sgtb }
540*505d05c7Sgtb void profile_dereference_data_locked(prf_data_t data)
541*505d05c7Sgtb {
542*505d05c7Sgtb     data->refcount--;
543*505d05c7Sgtb     if (data->refcount == 0)
544*505d05c7Sgtb 	profile_free_file_data(data);
545*505d05c7Sgtb }
546*505d05c7Sgtb 
547*505d05c7Sgtb int profile_lock_global()
548*505d05c7Sgtb {
549*505d05c7Sgtb     return k5_mutex_lock(&g_shared_trees_mutex);
550*505d05c7Sgtb }
551*505d05c7Sgtb int profile_unlock_global()
552*505d05c7Sgtb {
553*505d05c7Sgtb     return k5_mutex_unlock(&g_shared_trees_mutex);
5547c478bd9Sstevel@tonic-gate }
5557c478bd9Sstevel@tonic-gate 
556*505d05c7Sgtb void profile_free_file(prf_file_t prf)
557*505d05c7Sgtb {
558*505d05c7Sgtb     profile_dereference_data(prf->data);
559*505d05c7Sgtb     free(prf);
560*505d05c7Sgtb }
561*505d05c7Sgtb 
562*505d05c7Sgtb /* Call with mutex locked!  */
563*505d05c7Sgtb static void profile_free_file_data(prf_data_t data)
564*505d05c7Sgtb {
565*505d05c7Sgtb     scan_shared_trees_locked();
566*505d05c7Sgtb     if (data->flags & PROFILE_FILE_SHARED) {
567*505d05c7Sgtb 	/* Remove from linked list.  */
568*505d05c7Sgtb 	if (g_shared_trees == data)
569*505d05c7Sgtb 	    g_shared_trees = data->next;
570*505d05c7Sgtb 	else {
571*505d05c7Sgtb 	    prf_data_t prev, next;
572*505d05c7Sgtb 	    prev = g_shared_trees;
573*505d05c7Sgtb 	    next = prev->next;
574*505d05c7Sgtb 	    while (next) {
575*505d05c7Sgtb 		if (next == data) {
576*505d05c7Sgtb 		    prev->next = next->next;
577*505d05c7Sgtb 		    break;
578*505d05c7Sgtb 		}
579*505d05c7Sgtb 		prev = next;
580*505d05c7Sgtb 		next = next->next;
581*505d05c7Sgtb 	    }
582*505d05c7Sgtb 	}
583*505d05c7Sgtb     }
584*505d05c7Sgtb     if (data->root)
585*505d05c7Sgtb 	profile_free_node(data->root);
586*505d05c7Sgtb     if (data->comment)
587*505d05c7Sgtb 	free(data->comment);
588*505d05c7Sgtb     data->magic = 0;
589*505d05c7Sgtb     k5_mutex_destroy(&data->lock);
590*505d05c7Sgtb     free(data);
591*505d05c7Sgtb     scan_shared_trees_locked();
592*505d05c7Sgtb }
593*505d05c7Sgtb 
594*505d05c7Sgtb errcode_t profile_close_file(prf_file_t prf)
5957c478bd9Sstevel@tonic-gate {
5967c478bd9Sstevel@tonic-gate 	errcode_t	retval;
5977c478bd9Sstevel@tonic-gate 
5987c478bd9Sstevel@tonic-gate 	retval = profile_flush_file(prf);
5997c478bd9Sstevel@tonic-gate 	if (retval)
6007c478bd9Sstevel@tonic-gate 		return retval;
6017c478bd9Sstevel@tonic-gate 	profile_free_file(prf);
6027c478bd9Sstevel@tonic-gate 	return 0;
6037c478bd9Sstevel@tonic-gate }
604