1004388ebScasper /*
2159d09a2SMark 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>
32*55fea89dSDan Cross #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
profile_library_initializer(void)55505d05c7Sgtb int profile_library_initializer(void)
56505d05c7Sgtb {
57159d09a2SMark Phalan #ifdef SHOW_INITFINI_FUNCS
58159d09a2SMark Phalan printf("profile_library_initializer\n");
59159d09a2SMark 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 }
profile_library_finalizer(void)65505d05c7Sgtb void profile_library_finalizer(void)
66505d05c7Sgtb {
67159d09a2SMark Phalan if (! INITIALIZER_RAN(profile_library_initializer) || PROGRAM_EXITING()) {
68159d09a2SMark Phalan #ifdef SHOW_INITFINI_FUNCS
69159d09a2SMark Phalan printf("profile_library_finalizer: skipping\n");
70159d09a2SMark Phalan #endif
71505d05c7Sgtb return;
72159d09a2SMark Phalan }
73159d09a2SMark Phalan #ifdef SHOW_INITFINI_FUNCS
74159d09a2SMark Phalan printf("profile_library_finalizer\n");
75159d09a2SMark 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)); \
97159d09a2SMark 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
rw_access(const_profile_filespec_t filespec)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;
131159d09a2SMark 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
r_access(const_profile_filespec_t filespec)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
156159d09a2SMark 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
profile_make_prf_data(const char * filename)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
profile_open_file(const_profile_filespec_t filespec,prf_file_t * ret_prof)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
210159d09a2SMark 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;
222159d09a2SMark Phalan struct passwd *pw, pwx;
223505d05c7Sgtb char pwbuf[BUFSIZ];
224505d05c7Sgtb
225505d05c7Sgtb uid = getuid();
226159d09a2SMark Phalan if (!k5_getpwuid_r(uid, &pwx, pwbuf, sizeof(pwbuf), &pw)
227159d09a2SMark 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);
260159d09a2SMark 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
profile_update_file_data(prf_data_t data)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;
312159d09a2SMark 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);
323159d09a2SMark 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;
333159d09a2SMark Phalan #if defined HAVE_STRUCT_STAT_ST_MTIMENSEC
334159d09a2SMark Phalan frac = st.st_mtimensec;
335159d09a2SMark Phalan #elif defined HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
336159d09a2SMark Phalan frac = st.st_mtimespec.tv_nsec;
337159d09a2SMark Phalan #elif defined HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
338159d09a2SMark Phalan frac = st.st_mtim.tv_nsec;
339159d09a2SMark Phalan #else
340159d09a2SMark Phalan frac = 0;
341505d05c7Sgtb #endif
342159d09a2SMark Phalan if (st.st_mtime == data->timestamp
343159d09a2SMark Phalan && frac == data->frac_ts
344159d09a2SMark 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;
368159d09a2SMark 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 }
387159d09a2SMark Phalan assert(data->root != NULL);
3887c478bd9Sstevel@tonic-gate #ifdef HAVE_STAT
389505d05c7Sgtb data->timestamp = st.st_mtime;
390159d09a2SMark 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
make_hard_link(const char * oldpath,const char * newpath)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
write_data_to_file(prf_data_t data,const char * outfile,int can_create)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;
415*55fea89dSDan Cross
4167c478bd9Sstevel@tonic-gate new_file = old_file = 0;
417159d09a2SMark Phalan new_file = malloc(strlen(outfile) + 5);
4187c478bd9Sstevel@tonic-gate if (!new_file)
4197c478bd9Sstevel@tonic-gate goto errout;
420159d09a2SMark 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
429159d09a2SMark 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
profile_flush_file_data_to_buffer(prf_data_t data,char ** bufp)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
profile_flush_file_data(prf_data_t data)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;
513*55fea89dSDan Cross
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
profile_flush_file_data_to_file(prf_data_t data,const char * outfile)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
profile_dereference_data(prf_data_t data)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 }
profile_dereference_data_locked(prf_data_t data)550505d05c7Sgtb void profile_dereference_data_locked(prf_data_t data)
551505d05c7Sgtb {
552159d09a2SMark Phalan scan_shared_trees_locked();
553505d05c7Sgtb data->refcount--;
554505d05c7Sgtb if (data->refcount == 0)
555505d05c7Sgtb profile_free_file_data(data);
556159d09a2SMark Phalan scan_shared_trees_locked();
557505d05c7Sgtb }
558505d05c7Sgtb
profile_lock_global()559505d05c7Sgtb int profile_lock_global()
560505d05c7Sgtb {
561505d05c7Sgtb return k5_mutex_lock(&g_shared_trees_mutex);
562505d05c7Sgtb }
profile_unlock_global()563505d05c7Sgtb int profile_unlock_global()
564505d05c7Sgtb {
565505d05c7Sgtb return k5_mutex_unlock(&g_shared_trees_mutex);
5667c478bd9Sstevel@tonic-gate }
5677c478bd9Sstevel@tonic-gate
profile_free_file(prf_file_t prf)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! */
profile_free_file_data(prf_data_t data)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
profile_close_file(prf_file_t prf)606505d05c7Sgtb errcode_t profile_close_file(prf_file_t prf)
6077c478bd9Sstevel@tonic-gate {
6087c478bd9Sstevel@tonic-gate errcode_t retval;
609*55fea89dSDan Cross
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