1004388ebScasper /*
2*159d09a2SMark Phalan  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
3004388ebScasper  * Use is subject to license terms.
4004388ebScasper  */
57c478bd9Sstevel@tonic-gate /*
67c478bd9Sstevel@tonic-gate  * prof_file.c ---- routines that manipulate an individual profile file.
77c478bd9Sstevel@tonic-gate  */
87c478bd9Sstevel@tonic-gate 
97c478bd9Sstevel@tonic-gate #include <autoconf.h>
10505d05c7Sgtb #include "prof_int.h"
117c478bd9Sstevel@tonic-gate 
127c478bd9Sstevel@tonic-gate #include <stdio.h>
137c478bd9Sstevel@tonic-gate #ifdef HAVE_STDLIB_H
147c478bd9Sstevel@tonic-gate #include <stdlib.h>
157c478bd9Sstevel@tonic-gate #endif
167c478bd9Sstevel@tonic-gate #ifdef HAVE_UNISTD_H
177c478bd9Sstevel@tonic-gate #include <unistd.h>
187c478bd9Sstevel@tonic-gate #endif
197c478bd9Sstevel@tonic-gate #include <string.h>
20505d05c7Sgtb #include <stddef.h>
217c478bd9Sstevel@tonic-gate 
227c478bd9Sstevel@tonic-gate #include <sys/types.h>
237c478bd9Sstevel@tonic-gate #include <sys/stat.h>
247c478bd9Sstevel@tonic-gate #include <errno.h>
257c478bd9Sstevel@tonic-gate 
26505d05c7Sgtb #ifdef HAVE_PWD_H
27505d05c7Sgtb #include <pwd.h>
28505d05c7Sgtb #endif
297c478bd9Sstevel@tonic-gate 
30505d05c7Sgtb #if defined(_WIN32)
317c478bd9Sstevel@tonic-gate #include <io.h>
327c478bd9Sstevel@tonic-gate #define HAVE_STAT
337c478bd9Sstevel@tonic-gate #define stat _stat
347c478bd9Sstevel@tonic-gate #endif
357c478bd9Sstevel@tonic-gate 
36505d05c7Sgtb #include "k5-platform.h"
37505d05c7Sgtb 
38505d05c7Sgtb struct global_shared_profile_data {
39505d05c7Sgtb 	/* This is the head of the global list of shared trees */
40505d05c7Sgtb 	prf_data_t trees;
41505d05c7Sgtb 	/* Lock for above list.  */
42505d05c7Sgtb 	k5_mutex_t mutex;
43505d05c7Sgtb };
44505d05c7Sgtb #define g_shared_trees		(krb5int_profile_shared_data.trees)
45505d05c7Sgtb #define g_shared_trees_mutex	(krb5int_profile_shared_data.mutex)
46505d05c7Sgtb 
47505d05c7Sgtb static struct global_shared_profile_data krb5int_profile_shared_data = {
48505d05c7Sgtb     0,
49505d05c7Sgtb     K5_MUTEX_PARTIAL_INITIALIZER
50505d05c7Sgtb };
51505d05c7Sgtb 
52505d05c7Sgtb MAKE_INIT_FUNCTION(profile_library_initializer);
53505d05c7Sgtb MAKE_FINI_FUNCTION(profile_library_finalizer);
54505d05c7Sgtb 
55505d05c7Sgtb int profile_library_initializer(void)
56505d05c7Sgtb {
57*159d09a2SMark Phalan #ifdef SHOW_INITFINI_FUNCS
58*159d09a2SMark Phalan     printf("profile_library_initializer\n");
59*159d09a2SMark Phalan #endif
60505d05c7Sgtb #if !USE_BUNDLE_ERROR_STRINGS
61505d05c7Sgtb     add_error_table(&et_prof_error_table);
62505d05c7Sgtb #endif
63505d05c7Sgtb     return k5_mutex_finish_init(&g_shared_trees_mutex);
64505d05c7Sgtb }
65505d05c7Sgtb void profile_library_finalizer(void)
66505d05c7Sgtb {
67*159d09a2SMark Phalan     if (! INITIALIZER_RAN(profile_library_initializer) || PROGRAM_EXITING()) {
68*159d09a2SMark Phalan #ifdef SHOW_INITFINI_FUNCS
69*159d09a2SMark Phalan 	printf("profile_library_finalizer: skipping\n");
70*159d09a2SMark Phalan #endif
71505d05c7Sgtb 	return;
72*159d09a2SMark Phalan     }
73*159d09a2SMark Phalan #ifdef SHOW_INITFINI_FUNCS
74*159d09a2SMark Phalan     printf("profile_library_finalizer\n");
75*159d09a2SMark Phalan #endif
76505d05c7Sgtb     k5_mutex_destroy(&g_shared_trees_mutex);
77505d05c7Sgtb #if !USE_BUNDLE_ERROR_STRINGS
78505d05c7Sgtb     remove_error_table(&et_prof_error_table);
79505d05c7Sgtb #endif
80505d05c7Sgtb }
81505d05c7Sgtb 
82505d05c7Sgtb static void profile_free_file_data(prf_data_t);
83505d05c7Sgtb 
84505d05c7Sgtb #if 0
85505d05c7Sgtb 
86505d05c7Sgtb #define scan_shared_trees_locked()				\
87505d05c7Sgtb 	{							\
88505d05c7Sgtb 	    prf_data_t d;					\
89505d05c7Sgtb 	    k5_mutex_assert_locked(&g_shared_trees_mutex);	\
90505d05c7Sgtb 	    for (d = g_shared_trees; d; d = d->next) {		\
91505d05c7Sgtb 		assert(d->magic == PROF_MAGIC_FILE_DATA);	\
92505d05c7Sgtb 		assert((d->flags & PROFILE_FILE_SHARED) != 0);	\
93505d05c7Sgtb 		assert(d->filespec[0] != 0);			\
94505d05c7Sgtb 		assert(d->fslen <= 1000); /* XXX */		\
95505d05c7Sgtb 		assert(d->filespec[d->fslen] == 0);		\
96505d05c7Sgtb 		assert(d->fslen = strlen(d->filespec));		\
97*159d09a2SMark Phalan 		assert(d->root != NULL);			\
98505d05c7Sgtb 	    }							\
99505d05c7Sgtb 	}
100505d05c7Sgtb 
101505d05c7Sgtb #define scan_shared_trees_unlocked()			\
102505d05c7Sgtb 	{						\
103505d05c7Sgtb 	    int r;					\
104505d05c7Sgtb 	    r = k5_mutex_lock(&g_shared_trees_mutex);	\
105505d05c7Sgtb 	    assert (r == 0);				\
106505d05c7Sgtb 	    scan_shared_trees_locked();			\
107505d05c7Sgtb 	    k5_mutex_unlock(&g_shared_trees_mutex);	\
108505d05c7Sgtb 	}
109505d05c7Sgtb 
110505d05c7Sgtb #else
1117c478bd9Sstevel@tonic-gate 
112505d05c7Sgtb #define scan_shared_trees_locked()	{ ; }
113505d05c7Sgtb #define scan_shared_trees_unlocked()	{ ; }
1147c478bd9Sstevel@tonic-gate 
1157c478bd9Sstevel@tonic-gate #endif
1167c478bd9Sstevel@tonic-gate 
117505d05c7Sgtb static int rw_access(const_profile_filespec_t filespec)
1187c478bd9Sstevel@tonic-gate {
1197c478bd9Sstevel@tonic-gate #ifdef HAVE_ACCESS
1207c478bd9Sstevel@tonic-gate 	if (access(filespec, W_OK) == 0)
1217c478bd9Sstevel@tonic-gate 		return 1;
1227c478bd9Sstevel@tonic-gate 	else
1237c478bd9Sstevel@tonic-gate 		return 0;
1247c478bd9Sstevel@tonic-gate #else
1257c478bd9Sstevel@tonic-gate 	/*
1267c478bd9Sstevel@tonic-gate 	 * We're on a substandard OS that doesn't support access.  So
1277c478bd9Sstevel@tonic-gate 	 * we kludge a test using stdio routines, and hope fopen
1287c478bd9Sstevel@tonic-gate 	 * checks the r/w permissions.
1297c478bd9Sstevel@tonic-gate 	 */
1307c478bd9Sstevel@tonic-gate 	FILE	*f;
131*159d09a2SMark Phalan 	/* Solaris Kerberos */
132004388ebScasper 	f = fopen(filespec, "r+F");
133505d05c7Sgtb 	if (f) {
134505d05c7Sgtb 		fclose(f);
135505d05c7Sgtb 		return 1;
136505d05c7Sgtb 	}
137505d05c7Sgtb 	return 0;
1387c478bd9Sstevel@tonic-gate #endif
139505d05c7Sgtb }
140505d05c7Sgtb 
141505d05c7Sgtb static int r_access(const_profile_filespec_t filespec)
142505d05c7Sgtb {
143505d05c7Sgtb #ifdef HAVE_ACCESS
144505d05c7Sgtb 	if (access(filespec, R_OK) == 0)
145505d05c7Sgtb 		return 1;
146505d05c7Sgtb 	else
147505d05c7Sgtb 		return 0;
148505d05c7Sgtb #else
149505d05c7Sgtb 	/*
150505d05c7Sgtb 	 * We're on a substandard OS that doesn't support access.  So
151505d05c7Sgtb 	 * we kludge a test using stdio routines, and hope fopen
152505d05c7Sgtb 	 * checks the r/w permissions.
153505d05c7Sgtb 	 */
154505d05c7Sgtb 	FILE	*f;
155505d05c7Sgtb 
156*159d09a2SMark Phalan 	/* Solaris Kerberos */
157004388ebScasper 	f = fopen(filespec, "rF");
1587c478bd9Sstevel@tonic-gate 	if (f) {
1597c478bd9Sstevel@tonic-gate 		fclose(f);
1607c478bd9Sstevel@tonic-gate 		return 1;
1617c478bd9Sstevel@tonic-gate 	}
1627c478bd9Sstevel@tonic-gate 	return 0;
1637c478bd9Sstevel@tonic-gate #endif
1647c478bd9Sstevel@tonic-gate }
1657c478bd9Sstevel@tonic-gate 
166505d05c7Sgtb prf_data_t
167505d05c7Sgtb profile_make_prf_data(const char *filename)
168505d05c7Sgtb {
169505d05c7Sgtb     prf_data_t d;
170505d05c7Sgtb     size_t len, flen, slen;
171505d05c7Sgtb     char *fcopy;
172505d05c7Sgtb 
173505d05c7Sgtb     flen = strlen(filename);
174505d05c7Sgtb     slen = offsetof(struct _prf_data_t, filespec);
175505d05c7Sgtb     len = slen + flen + 1;
176505d05c7Sgtb     if (len < sizeof(struct _prf_data_t))
177505d05c7Sgtb 	len = sizeof(struct _prf_data_t);
178505d05c7Sgtb     d = malloc(len);
179505d05c7Sgtb     if (d == NULL)
180505d05c7Sgtb 	return NULL;
181505d05c7Sgtb     memset(d, 0, len);
182505d05c7Sgtb     fcopy = (char *) d + slen;
183505d05c7Sgtb     assert(fcopy == d->filespec);
184505d05c7Sgtb     strcpy(fcopy, filename);
185505d05c7Sgtb     d->refcount = 1;
186505d05c7Sgtb     d->comment = NULL;
187505d05c7Sgtb     d->magic = PROF_MAGIC_FILE_DATA;
188505d05c7Sgtb     d->root = NULL;
189505d05c7Sgtb     d->next = NULL;
190505d05c7Sgtb     d->fslen = flen;
191505d05c7Sgtb     return d;
192505d05c7Sgtb }
193505d05c7Sgtb 
194505d05c7Sgtb errcode_t profile_open_file(const_profile_filespec_t filespec,
195505d05c7Sgtb 			    prf_file_t *ret_prof)
1967c478bd9Sstevel@tonic-gate {
1977c478bd9Sstevel@tonic-gate 	prf_file_t	prf;
1987c478bd9Sstevel@tonic-gate 	errcode_t	retval;
1997c478bd9Sstevel@tonic-gate 	char		*home_env = 0;
200505d05c7Sgtb 	unsigned int	len;
201505d05c7Sgtb 	prf_data_t	data;
202505d05c7Sgtb 	char		*expanded_filename;
203505d05c7Sgtb 
204505d05c7Sgtb 	retval = CALL_INIT_FUNCTION(profile_library_initializer);
205505d05c7Sgtb 	if (retval)
206505d05c7Sgtb 		return retval;
207505d05c7Sgtb 
208505d05c7Sgtb 	scan_shared_trees_unlocked();
2097c478bd9Sstevel@tonic-gate 
210*159d09a2SMark Phalan 	prf = malloc(sizeof(struct _prf_file_t));
2117c478bd9Sstevel@tonic-gate 	if (!prf)
2127c478bd9Sstevel@tonic-gate 		return ENOMEM;
2137c478bd9Sstevel@tonic-gate 	memset(prf, 0, sizeof(struct _prf_file_t));
214505d05c7Sgtb 	prf->magic = PROF_MAGIC_FILE;
215505d05c7Sgtb 
2167c478bd9Sstevel@tonic-gate 	len = strlen(filespec)+1;
2177c478bd9Sstevel@tonic-gate 	if (filespec[0] == '~' && filespec[1] == '/') {
2187c478bd9Sstevel@tonic-gate 		home_env = getenv("HOME");
219505d05c7Sgtb #ifdef HAVE_PWD_H
220505d05c7Sgtb 		if (home_env == NULL) {
221505d05c7Sgtb 		    uid_t uid;
222*159d09a2SMark Phalan 		    struct passwd *pw, pwx;
223505d05c7Sgtb 		    char pwbuf[BUFSIZ];
224505d05c7Sgtb 
225505d05c7Sgtb 		    uid = getuid();
226*159d09a2SMark Phalan 		    if (!k5_getpwuid_r(uid, &pwx, pwbuf, sizeof(pwbuf), &pw)
227*159d09a2SMark Phalan 			&& pw != NULL && pw->pw_dir[0] != 0)
228505d05c7Sgtb 			home_env = pw->pw_dir;
229505d05c7Sgtb 		}
230505d05c7Sgtb #endif
2317c478bd9Sstevel@tonic-gate 		if (home_env)
2327c478bd9Sstevel@tonic-gate 			len += strlen(home_env);
2337c478bd9Sstevel@tonic-gate 	}
234505d05c7Sgtb 	expanded_filename = malloc(len);
235505d05c7Sgtb 	if (expanded_filename == 0)
236505d05c7Sgtb 	    return errno;
2377c478bd9Sstevel@tonic-gate 	if (home_env) {
238505d05c7Sgtb 	    strcpy(expanded_filename, home_env);
239505d05c7Sgtb 	    strcat(expanded_filename, filespec+1);
2407c478bd9Sstevel@tonic-gate 	} else
241505d05c7Sgtb 	    memcpy(expanded_filename, filespec, len);
242505d05c7Sgtb 
243505d05c7Sgtb 	retval = k5_mutex_lock(&g_shared_trees_mutex);
244505d05c7Sgtb 	if (retval) {
245505d05c7Sgtb 	    free(expanded_filename);
246505d05c7Sgtb 	    free(prf);
247505d05c7Sgtb 	    scan_shared_trees_unlocked();
248505d05c7Sgtb 	    return retval;
249505d05c7Sgtb 	}
250505d05c7Sgtb 	scan_shared_trees_locked();
251505d05c7Sgtb 	for (data = g_shared_trees; data; data = data->next) {
252505d05c7Sgtb 	    if (!strcmp(data->filespec, expanded_filename)
253505d05c7Sgtb 		/* Check that current uid has read access.  */
254505d05c7Sgtb 		&& r_access(data->filespec))
255505d05c7Sgtb 		break;
256505d05c7Sgtb 	}
257505d05c7Sgtb 	if (data) {
258505d05c7Sgtb 	    data->refcount++;
259505d05c7Sgtb 	    (void) k5_mutex_unlock(&g_shared_trees_mutex);
260*159d09a2SMark Phalan 	    retval = profile_update_file_data(data);
261505d05c7Sgtb 	    free(expanded_filename);
262505d05c7Sgtb 	    prf->data = data;
263505d05c7Sgtb 	    *ret_prof = prf;
264505d05c7Sgtb 	    scan_shared_trees_unlocked();
265505d05c7Sgtb 	    return retval;
266505d05c7Sgtb 	}
267505d05c7Sgtb 	(void) k5_mutex_unlock(&g_shared_trees_mutex);
268505d05c7Sgtb 	data = profile_make_prf_data(expanded_filename);
269505d05c7Sgtb 	if (data == NULL) {
270505d05c7Sgtb 	    free(prf);
271505d05c7Sgtb 	    free(expanded_filename);
272505d05c7Sgtb 	    return ENOMEM;
273505d05c7Sgtb 	}
274505d05c7Sgtb 	free(expanded_filename);
275505d05c7Sgtb 	prf->data = data;
276505d05c7Sgtb 
277505d05c7Sgtb 	retval = k5_mutex_init(&data->lock);
278505d05c7Sgtb 	if (retval) {
279505d05c7Sgtb 	    free(data);
280505d05c7Sgtb 	    free(prf);
281505d05c7Sgtb 	    return retval;
282505d05c7Sgtb 	}
2837c478bd9Sstevel@tonic-gate 
2847c478bd9Sstevel@tonic-gate 	retval = profile_update_file(prf);
2857c478bd9Sstevel@tonic-gate 	if (retval) {
2867c478bd9Sstevel@tonic-gate 		profile_close_file(prf);
2877c478bd9Sstevel@tonic-gate 		return retval;
2887c478bd9Sstevel@tonic-gate 	}
2897c478bd9Sstevel@tonic-gate 
290505d05c7Sgtb 	retval = k5_mutex_lock(&g_shared_trees_mutex);
291505d05c7Sgtb 	if (retval) {
292505d05c7Sgtb 	    profile_close_file(prf);
293505d05c7Sgtb 	    scan_shared_trees_unlocked();
294505d05c7Sgtb 	    return retval;
295505d05c7Sgtb 	}
296505d05c7Sgtb 	scan_shared_trees_locked();
297505d05c7Sgtb 	data->flags |= PROFILE_FILE_SHARED;
298505d05c7Sgtb 	data->next = g_shared_trees;
299505d05c7Sgtb 	g_shared_trees = data;
300505d05c7Sgtb 	scan_shared_trees_locked();
301505d05c7Sgtb 	(void) k5_mutex_unlock(&g_shared_trees_mutex);
302505d05c7Sgtb 
3037c478bd9Sstevel@tonic-gate 	*ret_prof = prf;
3047c478bd9Sstevel@tonic-gate 	return 0;
3057c478bd9Sstevel@tonic-gate }
3067c478bd9Sstevel@tonic-gate 
307505d05c7Sgtb errcode_t profile_update_file_data(prf_data_t data)
3087c478bd9Sstevel@tonic-gate {
3097c478bd9Sstevel@tonic-gate 	errcode_t retval;
3107c478bd9Sstevel@tonic-gate #ifdef HAVE_STAT
3117c478bd9Sstevel@tonic-gate 	struct stat st;
312*159d09a2SMark Phalan 	unsigned long frac;
313505d05c7Sgtb 	time_t now;
3147c478bd9Sstevel@tonic-gate #endif
3157c478bd9Sstevel@tonic-gate 	FILE *f;
3167c478bd9Sstevel@tonic-gate 
317505d05c7Sgtb 	retval = k5_mutex_lock(&data->lock);
318505d05c7Sgtb 	if (retval)
319505d05c7Sgtb 	    return retval;
320505d05c7Sgtb 
3217c478bd9Sstevel@tonic-gate #ifdef HAVE_STAT
322505d05c7Sgtb 	now = time(0);
323*159d09a2SMark Phalan 	if (now == data->last_stat && data->root != NULL) {
324505d05c7Sgtb 	    k5_mutex_unlock(&data->lock);
325505d05c7Sgtb 	    return 0;
3267c478bd9Sstevel@tonic-gate 	}
327505d05c7Sgtb 	if (stat(data->filespec, &st)) {
328505d05c7Sgtb 	    retval = errno;
329505d05c7Sgtb 	    k5_mutex_unlock(&data->lock);
330505d05c7Sgtb 	    return retval;
331505d05c7Sgtb 	}
332505d05c7Sgtb 	data->last_stat = now;
333*159d09a2SMark Phalan #if defined HAVE_STRUCT_STAT_ST_MTIMENSEC
334*159d09a2SMark Phalan 	frac = st.st_mtimensec;
335*159d09a2SMark Phalan #elif defined HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
336*159d09a2SMark Phalan 	frac = st.st_mtimespec.tv_nsec;
337*159d09a2SMark Phalan #elif defined HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
338*159d09a2SMark Phalan 	frac = st.st_mtim.tv_nsec;
339*159d09a2SMark Phalan #else
340*159d09a2SMark Phalan 	frac = 0;
341505d05c7Sgtb #endif
342*159d09a2SMark Phalan 	if (st.st_mtime == data->timestamp
343*159d09a2SMark Phalan 	    && frac == data->frac_ts
344*159d09a2SMark Phalan 	    && data->root != NULL) {
345505d05c7Sgtb 	    k5_mutex_unlock(&data->lock);
346505d05c7Sgtb 	    return 0;
347505d05c7Sgtb 	}
348505d05c7Sgtb 	if (data->root) {
349505d05c7Sgtb 		profile_free_node(data->root);
350505d05c7Sgtb 		data->root = 0;
351505d05c7Sgtb 	}
352505d05c7Sgtb 	if (data->comment) {
353505d05c7Sgtb 		free(data->comment);
354505d05c7Sgtb 		data->comment = 0;
3557c478bd9Sstevel@tonic-gate 	}
3567c478bd9Sstevel@tonic-gate #else
3577c478bd9Sstevel@tonic-gate 	/*
3587c478bd9Sstevel@tonic-gate 	 * If we don't have the stat() call, assume that our in-core
3597c478bd9Sstevel@tonic-gate 	 * memory image is correct.  That is, we won't reread the
3607c478bd9Sstevel@tonic-gate 	 * profile file if it changes.
3617c478bd9Sstevel@tonic-gate 	 */
362505d05c7Sgtb 	if (data->root) {
363505d05c7Sgtb 	    k5_mutex_unlock(&data->lock);
364505d05c7Sgtb 	    return 0;
365505d05c7Sgtb 	}
3667c478bd9Sstevel@tonic-gate #endif
3677c478bd9Sstevel@tonic-gate 	errno = 0;
368*159d09a2SMark Phalan 	/* Solaris Kerberos */
369004388ebScasper 	f = fopen(data->filespec, "rF");
3707c478bd9Sstevel@tonic-gate 	if (f == NULL) {
3717c478bd9Sstevel@tonic-gate 		retval = errno;
372505d05c7Sgtb 		k5_mutex_unlock(&data->lock);
3737c478bd9Sstevel@tonic-gate 		if (retval == 0)
3747c478bd9Sstevel@tonic-gate 			retval = ENOENT;
3757c478bd9Sstevel@tonic-gate 		return retval;
3767c478bd9Sstevel@tonic-gate 	}
377505d05c7Sgtb 	data->upd_serial++;
378505d05c7Sgtb 	data->flags &= PROFILE_FILE_SHARED;
379505d05c7Sgtb 	if (rw_access(data->filespec))
380505d05c7Sgtb 		data->flags |= PROFILE_FILE_RW;
381505d05c7Sgtb 	retval = profile_parse_file(f, &data->root);
3827c478bd9Sstevel@tonic-gate 	fclose(f);
383505d05c7Sgtb 	if (retval) {
384505d05c7Sgtb 	    k5_mutex_unlock(&data->lock);
385505d05c7Sgtb 	    return retval;
386505d05c7Sgtb 	}
387*159d09a2SMark Phalan 	assert(data->root != NULL);
3887c478bd9Sstevel@tonic-gate #ifdef HAVE_STAT
389505d05c7Sgtb 	data->timestamp = st.st_mtime;
390*159d09a2SMark Phalan 	data->frac_ts = frac;
3917c478bd9Sstevel@tonic-gate #endif
392505d05c7Sgtb 	k5_mutex_unlock(&data->lock);
3937c478bd9Sstevel@tonic-gate 	return 0;
3947c478bd9Sstevel@tonic-gate }
3957c478bd9Sstevel@tonic-gate 
396505d05c7Sgtb static int
397505d05c7Sgtb make_hard_link(const char *oldpath, const char *newpath)
3987c478bd9Sstevel@tonic-gate {
399505d05c7Sgtb #ifdef _WIN32
400505d05c7Sgtb     return -1;
401505d05c7Sgtb #else
402505d05c7Sgtb     return link(oldpath, newpath);
4037c478bd9Sstevel@tonic-gate #endif
404505d05c7Sgtb }
4057c478bd9Sstevel@tonic-gate 
406505d05c7Sgtb static errcode_t write_data_to_file(prf_data_t data, const char *outfile,
407505d05c7Sgtb 				    int can_create)
4087c478bd9Sstevel@tonic-gate {
4097c478bd9Sstevel@tonic-gate 	FILE		*f;
4107c478bd9Sstevel@tonic-gate 	profile_filespec_t new_file;
4117c478bd9Sstevel@tonic-gate 	profile_filespec_t old_file;
4127c478bd9Sstevel@tonic-gate 	errcode_t	retval = 0;
4137c478bd9Sstevel@tonic-gate 
4147c478bd9Sstevel@tonic-gate 	retval = ENOMEM;
4157c478bd9Sstevel@tonic-gate 
4167c478bd9Sstevel@tonic-gate 	new_file = old_file = 0;
417*159d09a2SMark Phalan 	new_file = malloc(strlen(outfile) + 5);
4187c478bd9Sstevel@tonic-gate 	if (!new_file)
4197c478bd9Sstevel@tonic-gate 		goto errout;
420*159d09a2SMark Phalan 	old_file = malloc(strlen(outfile) + 5);
4217c478bd9Sstevel@tonic-gate 	if (!old_file)
4227c478bd9Sstevel@tonic-gate 		goto errout;
4237c478bd9Sstevel@tonic-gate 
424505d05c7Sgtb 	sprintf(new_file, "%s.$$$", outfile);
425505d05c7Sgtb 	sprintf(old_file, "%s.bak", outfile);
4267c478bd9Sstevel@tonic-gate 
4277c478bd9Sstevel@tonic-gate 	errno = 0;
4287c478bd9Sstevel@tonic-gate 
429*159d09a2SMark Phalan 	/* Solaris Kerberos */
430004388ebScasper 	f = fopen(new_file, "wF");
4317c478bd9Sstevel@tonic-gate 	if (!f) {
4327c478bd9Sstevel@tonic-gate 		retval = errno;
4337c478bd9Sstevel@tonic-gate 		if (retval == 0)
4347c478bd9Sstevel@tonic-gate 			retval = PROF_FAIL_OPEN;
4357c478bd9Sstevel@tonic-gate 		goto errout;
4367c478bd9Sstevel@tonic-gate 	}
4377c478bd9Sstevel@tonic-gate 
438505d05c7Sgtb 	profile_write_tree_file(data->root, f);
4397c478bd9Sstevel@tonic-gate 	if (fclose(f) != 0) {
4407c478bd9Sstevel@tonic-gate 		retval = errno;
4417c478bd9Sstevel@tonic-gate 		goto errout;
4427c478bd9Sstevel@tonic-gate 	}
4437c478bd9Sstevel@tonic-gate 
4447c478bd9Sstevel@tonic-gate 	unlink(old_file);
445505d05c7Sgtb 	if (make_hard_link(outfile, old_file) == 0) {
446505d05c7Sgtb 	    /* Okay, got the hard link.  Yay.  Now we've got our
447505d05c7Sgtb 	       backup version, so just put the new version in
448505d05c7Sgtb 	       place.  */
449505d05c7Sgtb 	    if (rename(new_file, outfile)) {
450505d05c7Sgtb 		/* Weird, the rename didn't work.  But the old version
451505d05c7Sgtb 		   should still be in place, so no special cleanup is
452505d05c7Sgtb 		   needed.  */
4537c478bd9Sstevel@tonic-gate 		retval = errno;
4547c478bd9Sstevel@tonic-gate 		goto errout;
455505d05c7Sgtb 	    }
456505d05c7Sgtb 	} else if (errno == ENOENT && can_create) {
457505d05c7Sgtb 	    if (rename(new_file, outfile)) {
4587c478bd9Sstevel@tonic-gate 		retval = errno;
4597c478bd9Sstevel@tonic-gate 		goto errout;
460505d05c7Sgtb 	    }
461505d05c7Sgtb 	} else {
462505d05c7Sgtb 	    /* Couldn't make the hard link, so there's going to be a
463505d05c7Sgtb 	       small window where data->filespec does not refer to
464505d05c7Sgtb 	       either version.  */
465505d05c7Sgtb #ifndef _WIN32
466505d05c7Sgtb 	    sync();
4677c478bd9Sstevel@tonic-gate #endif
468505d05c7Sgtb 	    if (rename(outfile, old_file)) {
469505d05c7Sgtb 		retval = errno;
470505d05c7Sgtb 		goto errout;
471505d05c7Sgtb 	    }
472505d05c7Sgtb 	    if (rename(new_file, outfile)) {
473505d05c7Sgtb 		retval = errno;
474505d05c7Sgtb 		rename(old_file, outfile); /* back out... */
475505d05c7Sgtb 		goto errout;
476505d05c7Sgtb 	    }
477505d05c7Sgtb 	}
4787c478bd9Sstevel@tonic-gate 
479505d05c7Sgtb 	data->flags = 0;
480505d05c7Sgtb 	if (rw_access(outfile))
481505d05c7Sgtb 		data->flags |= PROFILE_FILE_RW;
4827c478bd9Sstevel@tonic-gate 	retval = 0;
483505d05c7Sgtb 
4847c478bd9Sstevel@tonic-gate errout:
4857c478bd9Sstevel@tonic-gate 	if (new_file)
4867c478bd9Sstevel@tonic-gate 		free(new_file);
4877c478bd9Sstevel@tonic-gate 	if (old_file)
4887c478bd9Sstevel@tonic-gate 		free(old_file);
4897c478bd9Sstevel@tonic-gate 	return retval;
4907c478bd9Sstevel@tonic-gate }
4917c478bd9Sstevel@tonic-gate 
492505d05c7Sgtb errcode_t profile_flush_file_data_to_buffer (prf_data_t data, char **bufp)
493505d05c7Sgtb {
494505d05c7Sgtb 	errcode_t	retval;
495505d05c7Sgtb 	retval = k5_mutex_lock(&data->lock);
496505d05c7Sgtb 	if (retval)
497505d05c7Sgtb 		return retval;
498505d05c7Sgtb 	retval = profile_write_tree_to_buffer(data->root, bufp);
499505d05c7Sgtb 	k5_mutex_unlock(&data->lock);
500505d05c7Sgtb 	return retval;
501505d05c7Sgtb }
5027c478bd9Sstevel@tonic-gate 
503505d05c7Sgtb errcode_t profile_flush_file_data(prf_data_t data)
5047c478bd9Sstevel@tonic-gate {
505505d05c7Sgtb 	errcode_t	retval = 0;
506505d05c7Sgtb 
507505d05c7Sgtb 	if (!data || data->magic != PROF_MAGIC_FILE_DATA)
508505d05c7Sgtb 		return PROF_MAGIC_FILE_DATA;
509505d05c7Sgtb 
510505d05c7Sgtb 	retval = k5_mutex_lock(&data->lock);
511505d05c7Sgtb 	if (retval)
512505d05c7Sgtb 	    return retval;
513505d05c7Sgtb 
514505d05c7Sgtb 	if ((data->flags & PROFILE_FILE_DIRTY) == 0) {
515505d05c7Sgtb 	    k5_mutex_unlock(&data->lock);
516505d05c7Sgtb 	    return 0;
517505d05c7Sgtb 	}
518505d05c7Sgtb 
519505d05c7Sgtb 	retval = write_data_to_file(data, data->filespec, 0);
520505d05c7Sgtb 	k5_mutex_unlock(&data->lock);
521505d05c7Sgtb 	return retval;
522505d05c7Sgtb }
523505d05c7Sgtb 
524505d05c7Sgtb errcode_t profile_flush_file_data_to_file(prf_data_t data, const char *outfile)
525505d05c7Sgtb {
526505d05c7Sgtb     errcode_t retval = 0;
527505d05c7Sgtb 
528505d05c7Sgtb     if (!data || data->magic != PROF_MAGIC_FILE_DATA)
529505d05c7Sgtb 	return PROF_MAGIC_FILE_DATA;
530505d05c7Sgtb 
531505d05c7Sgtb     retval = k5_mutex_lock(&data->lock);
532505d05c7Sgtb     if (retval)
533505d05c7Sgtb 	return retval;
534505d05c7Sgtb     retval = write_data_to_file(data, outfile, 1);
535505d05c7Sgtb     k5_mutex_unlock(&data->lock);
536505d05c7Sgtb     return retval;
537505d05c7Sgtb }
5387c478bd9Sstevel@tonic-gate 
539505d05c7Sgtb 
540505d05c7Sgtb 
541505d05c7Sgtb void profile_dereference_data(prf_data_t data)
542505d05c7Sgtb {
543505d05c7Sgtb     int err;
544505d05c7Sgtb     err = k5_mutex_lock(&g_shared_trees_mutex);
545505d05c7Sgtb     if (err)
5467c478bd9Sstevel@tonic-gate 	return;
547505d05c7Sgtb     profile_dereference_data_locked(data);
548505d05c7Sgtb     (void) k5_mutex_unlock(&g_shared_trees_mutex);
549505d05c7Sgtb }
550505d05c7Sgtb void profile_dereference_data_locked(prf_data_t data)
551505d05c7Sgtb {
552*159d09a2SMark Phalan     scan_shared_trees_locked();
553505d05c7Sgtb     data->refcount--;
554505d05c7Sgtb     if (data->refcount == 0)
555505d05c7Sgtb 	profile_free_file_data(data);
556*159d09a2SMark Phalan     scan_shared_trees_locked();
557505d05c7Sgtb }
558505d05c7Sgtb 
559505d05c7Sgtb int profile_lock_global()
560505d05c7Sgtb {
561505d05c7Sgtb     return k5_mutex_lock(&g_shared_trees_mutex);
562505d05c7Sgtb }
563505d05c7Sgtb int profile_unlock_global()
564505d05c7Sgtb {
565505d05c7Sgtb     return k5_mutex_unlock(&g_shared_trees_mutex);
5667c478bd9Sstevel@tonic-gate }
5677c478bd9Sstevel@tonic-gate 
568505d05c7Sgtb void profile_free_file(prf_file_t prf)
569505d05c7Sgtb {
570505d05c7Sgtb     profile_dereference_data(prf->data);
571505d05c7Sgtb     free(prf);
572505d05c7Sgtb }
573505d05c7Sgtb 
574505d05c7Sgtb /* Call with mutex locked!  */
575505d05c7Sgtb static void profile_free_file_data(prf_data_t data)
576505d05c7Sgtb {
577505d05c7Sgtb     scan_shared_trees_locked();
578505d05c7Sgtb     if (data->flags & PROFILE_FILE_SHARED) {
579505d05c7Sgtb 	/* Remove from linked list.  */
580505d05c7Sgtb 	if (g_shared_trees == data)
581505d05c7Sgtb 	    g_shared_trees = data->next;
582505d05c7Sgtb 	else {
583505d05c7Sgtb 	    prf_data_t prev, next;
584505d05c7Sgtb 	    prev = g_shared_trees;
585505d05c7Sgtb 	    next = prev->next;
586505d05c7Sgtb 	    while (next) {
587505d05c7Sgtb 		if (next == data) {
588505d05c7Sgtb 		    prev->next = next->next;
589505d05c7Sgtb 		    break;
590505d05c7Sgtb 		}
591505d05c7Sgtb 		prev = next;
592505d05c7Sgtb 		next = next->next;
593505d05c7Sgtb 	    }
594505d05c7Sgtb 	}
595505d05c7Sgtb     }
596505d05c7Sgtb     if (data->root)
597505d05c7Sgtb 	profile_free_node(data->root);
598505d05c7Sgtb     if (data->comment)
599505d05c7Sgtb 	free(data->comment);
600505d05c7Sgtb     data->magic = 0;
601505d05c7Sgtb     k5_mutex_destroy(&data->lock);
602505d05c7Sgtb     free(data);
603505d05c7Sgtb     scan_shared_trees_locked();
604505d05c7Sgtb }
605505d05c7Sgtb 
606505d05c7Sgtb errcode_t profile_close_file(prf_file_t prf)
6077c478bd9Sstevel@tonic-gate {
6087c478bd9Sstevel@tonic-gate 	errcode_t	retval;
6097c478bd9Sstevel@tonic-gate 
6107c478bd9Sstevel@tonic-gate 	retval = profile_flush_file(prf);
6117c478bd9Sstevel@tonic-gate 	if (retval)
6127c478bd9Sstevel@tonic-gate 		return retval;
6137c478bd9Sstevel@tonic-gate 	profile_free_file(prf);
6147c478bd9Sstevel@tonic-gate 	return 0;
6157c478bd9Sstevel@tonic-gate }
616