2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
6 * prof_file.c ---- routines that manipulate an individual profile file.
22 #include <sys/types.h>
36 #include "k5-platform.h"
38 struct global_shared_profile_data
{
39 /* This is the head of the global list of shared trees */
41 /* Lock for above list. */
44 #define g_shared_trees (krb5int_profile_shared_data.trees)
45 #define g_shared_trees_mutex (krb5int_profile_shared_data.mutex)
47 static struct global_shared_profile_data krb5int_profile_shared_data
= {
49 K5_MUTEX_PARTIAL_INITIALIZER
52 MAKE_INIT_FUNCTION(profile_library_initializer
);
53 MAKE_FINI_FUNCTION(profile_library_finalizer
);
55 int profile_library_initializer(void)
57 #ifdef SHOW_INITFINI_FUNCS
58 printf("profile_library_initializer\n");
60 #if !USE_BUNDLE_ERROR_STRINGS
61 add_error_table(&et_prof_error_table
);
63 return k5_mutex_finish_init(&g_shared_trees_mutex
);
65 void profile_library_finalizer(void)
67 if (! INITIALIZER_RAN(profile_library_initializer
) || PROGRAM_EXITING()) {
68 #ifdef SHOW_INITFINI_FUNCS
69 printf("profile_library_finalizer: skipping\n");
73 #ifdef SHOW_INITFINI_FUNCS
74 printf("profile_library_finalizer\n");
76 k5_mutex_destroy(&g_shared_trees_mutex
);
77 #if !USE_BUNDLE_ERROR_STRINGS
78 remove_error_table(&et_prof_error_table
);
82 static void profile_free_file_data(prf_data_t
);
86 #define scan_shared_trees_locked() \
89 k5_mutex_assert_locked(&g_shared_trees_mutex); \
90 for (d = g_shared_trees; d; d = d->next) { \
91 assert(d->magic == PROF_MAGIC_FILE_DATA); \
92 assert((d->flags & PROFILE_FILE_SHARED) != 0); \
93 assert(d->filespec[0] != 0); \
94 assert(d->fslen <= 1000); /* XXX */ \
95 assert(d->filespec[d->fslen] == 0); \
96 assert(d->fslen = strlen(d->filespec)); \
97 assert(d->root != NULL); \
101 #define scan_shared_trees_unlocked() \
104 r = k5_mutex_lock(&g_shared_trees_mutex); \
106 scan_shared_trees_locked(); \
107 k5_mutex_unlock(&g_shared_trees_mutex); \
112 #define scan_shared_trees_locked() { ; }
113 #define scan_shared_trees_unlocked() { ; }
117 static int rw_access(const_profile_filespec_t filespec
)
120 if (access(filespec
, W_OK
) == 0)
126 * We're on a substandard OS that doesn't support access. So
127 * we kludge a test using stdio routines, and hope fopen
128 * checks the r/w permissions.
131 /* Solaris Kerberos */
132 f
= fopen(filespec
, "r+F");
141 static int r_access(const_profile_filespec_t filespec
)
144 if (access(filespec
, R_OK
) == 0)
150 * We're on a substandard OS that doesn't support access. So
151 * we kludge a test using stdio routines, and hope fopen
152 * checks the r/w permissions.
156 /* Solaris Kerberos */
157 f
= fopen(filespec
, "rF");
167 profile_make_prf_data(const char *filename
)
170 size_t len
, flen
, slen
;
173 flen
= strlen(filename
);
174 slen
= offsetof(struct _prf_data_t
, filespec
);
175 len
= slen
+ flen
+ 1;
176 if (len
< sizeof(struct _prf_data_t
))
177 len
= sizeof(struct _prf_data_t
);
182 fcopy
= (char *) d
+ slen
;
183 assert(fcopy
== d
->filespec
);
184 strcpy(fcopy
, filename
);
187 d
->magic
= PROF_MAGIC_FILE_DATA
;
194 errcode_t
profile_open_file(const_profile_filespec_t filespec
,
195 prf_file_t
*ret_prof
)
202 char *expanded_filename
;
204 retval
= CALL_INIT_FUNCTION(profile_library_initializer
);
208 scan_shared_trees_unlocked();
210 prf
= malloc(sizeof(struct _prf_file_t
));
213 memset(prf
, 0, sizeof(struct _prf_file_t
));
214 prf
->magic
= PROF_MAGIC_FILE
;
216 len
= strlen(filespec
)+1;
217 if (filespec
[0] == '~' && filespec
[1] == '/') {
218 home_env
= getenv("HOME");
220 if (home_env
== NULL
) {
222 struct passwd
*pw
, pwx
;
226 if (!k5_getpwuid_r(uid
, &pwx
, pwbuf
, sizeof(pwbuf
), &pw
)
227 && pw
!= NULL
&& pw
->pw_dir
[0] != 0)
228 home_env
= pw
->pw_dir
;
232 len
+= strlen(home_env
);
234 expanded_filename
= malloc(len
);
235 if (expanded_filename
== 0)
238 strcpy(expanded_filename
, home_env
);
239 strcat(expanded_filename
, filespec
+1);
241 memcpy(expanded_filename
, filespec
, len
);
243 retval
= k5_mutex_lock(&g_shared_trees_mutex
);
245 free(expanded_filename
);
247 scan_shared_trees_unlocked();
250 scan_shared_trees_locked();
251 for (data
= g_shared_trees
; data
; data
= data
->next
) {
252 if (!strcmp(data
->filespec
, expanded_filename
)
253 /* Check that current uid has read access. */
254 && r_access(data
->filespec
))
259 (void) k5_mutex_unlock(&g_shared_trees_mutex
);
260 retval
= profile_update_file_data(data
);
261 free(expanded_filename
);
264 scan_shared_trees_unlocked();
267 (void) k5_mutex_unlock(&g_shared_trees_mutex
);
268 data
= profile_make_prf_data(expanded_filename
);
271 free(expanded_filename
);
274 free(expanded_filename
);
277 retval
= k5_mutex_init(&data
->lock
);
284 retval
= profile_update_file(prf
);
286 profile_close_file(prf
);
290 retval
= k5_mutex_lock(&g_shared_trees_mutex
);
292 profile_close_file(prf
);
293 scan_shared_trees_unlocked();
296 scan_shared_trees_locked();
297 data
->flags
|= PROFILE_FILE_SHARED
;
298 data
->next
= g_shared_trees
;
299 g_shared_trees
= data
;
300 scan_shared_trees_locked();
301 (void) k5_mutex_unlock(&g_shared_trees_mutex
);
307 errcode_t
profile_update_file_data(prf_data_t data
)
317 retval
= k5_mutex_lock(&data
->lock
);
323 if (now
== data
->last_stat
&& data
->root
!= NULL
) {
324 k5_mutex_unlock(&data
->lock
);
327 if (stat(data
->filespec
, &st
)) {
329 k5_mutex_unlock(&data
->lock
);
332 data
->last_stat
= now
;
333 #if defined HAVE_STRUCT_STAT_ST_MTIMENSEC
334 frac
= st
.st_mtimensec
;
335 #elif defined HAVE_STRUCT_STAT_ST_MTIMESPEC_TV_NSEC
336 frac
= st
.st_mtimespec
.tv_nsec
;
337 #elif defined HAVE_STRUCT_STAT_ST_MTIM_TV_NSEC
338 frac
= st
.st_mtim
.tv_nsec
;
342 if (st
.st_mtime
== data
->timestamp
343 && frac
== data
->frac_ts
344 && data
->root
!= NULL
) {
345 k5_mutex_unlock(&data
->lock
);
349 profile_free_node(data
->root
);
358 * If we don't have the stat() call, assume that our in-core
359 * memory image is correct. That is, we won't reread the
360 * profile file if it changes.
363 k5_mutex_unlock(&data
->lock
);
368 /* Solaris Kerberos */
369 f
= fopen(data
->filespec
, "rF");
372 k5_mutex_unlock(&data
->lock
);
378 data
->flags
&= PROFILE_FILE_SHARED
;
379 if (rw_access(data
->filespec
))
380 data
->flags
|= PROFILE_FILE_RW
;
381 retval
= profile_parse_file(f
, &data
->root
);
384 k5_mutex_unlock(&data
->lock
);
387 assert(data
->root
!= NULL
);
389 data
->timestamp
= st
.st_mtime
;
390 data
->frac_ts
= frac
;
392 k5_mutex_unlock(&data
->lock
);
397 make_hard_link(const char *oldpath
, const char *newpath
)
402 return link(oldpath
, newpath
);
406 static errcode_t
write_data_to_file(prf_data_t data
, const char *outfile
,
410 profile_filespec_t new_file
;
411 profile_filespec_t old_file
;
412 errcode_t retval
= 0;
416 new_file
= old_file
= 0;
417 new_file
= malloc(strlen(outfile
) + 5);
420 old_file
= malloc(strlen(outfile
) + 5);
424 sprintf(new_file
, "%s.$$$", outfile
);
425 sprintf(old_file
, "%s.bak", outfile
);
429 /* Solaris Kerberos */
430 f
= fopen(new_file
, "wF");
434 retval
= PROF_FAIL_OPEN
;
438 profile_write_tree_file(data
->root
, f
);
439 if (fclose(f
) != 0) {
445 if (make_hard_link(outfile
, old_file
) == 0) {
446 /* Okay, got the hard link. Yay. Now we've got our
447 backup version, so just put the new version in
449 if (rename(new_file
, outfile
)) {
450 /* Weird, the rename didn't work. But the old version
451 should still be in place, so no special cleanup is
456 } else if (errno
== ENOENT
&& can_create
) {
457 if (rename(new_file
, outfile
)) {
462 /* Couldn't make the hard link, so there's going to be a
463 small window where data->filespec does not refer to
468 if (rename(outfile
, old_file
)) {
472 if (rename(new_file
, outfile
)) {
474 rename(old_file
, outfile
); /* back out... */
480 if (rw_access(outfile
))
481 data
->flags
|= PROFILE_FILE_RW
;
492 errcode_t
profile_flush_file_data_to_buffer (prf_data_t data
, char **bufp
)
495 retval
= k5_mutex_lock(&data
->lock
);
498 retval
= profile_write_tree_to_buffer(data
->root
, bufp
);
499 k5_mutex_unlock(&data
->lock
);
503 errcode_t
profile_flush_file_data(prf_data_t data
)
505 errcode_t retval
= 0;
507 if (!data
|| data
->magic
!= PROF_MAGIC_FILE_DATA
)
508 return PROF_MAGIC_FILE_DATA
;
510 retval
= k5_mutex_lock(&data
->lock
);
514 if ((data
->flags
& PROFILE_FILE_DIRTY
) == 0) {
515 k5_mutex_unlock(&data
->lock
);
519 retval
= write_data_to_file(data
, data
->filespec
, 0);
520 k5_mutex_unlock(&data
->lock
);
524 errcode_t
profile_flush_file_data_to_file(prf_data_t data
, const char *outfile
)
526 errcode_t retval
= 0;
528 if (!data
|| data
->magic
!= PROF_MAGIC_FILE_DATA
)
529 return PROF_MAGIC_FILE_DATA
;
531 retval
= k5_mutex_lock(&data
->lock
);
534 retval
= write_data_to_file(data
, outfile
, 1);
535 k5_mutex_unlock(&data
->lock
);
541 void profile_dereference_data(prf_data_t data
)
544 err
= k5_mutex_lock(&g_shared_trees_mutex
);
547 profile_dereference_data_locked(data
);
548 (void) k5_mutex_unlock(&g_shared_trees_mutex
);
550 void profile_dereference_data_locked(prf_data_t data
)
552 scan_shared_trees_locked();
554 if (data
->refcount
== 0)
555 profile_free_file_data(data
);
556 scan_shared_trees_locked();
559 int profile_lock_global()
561 return k5_mutex_lock(&g_shared_trees_mutex
);
563 int profile_unlock_global()
565 return k5_mutex_unlock(&g_shared_trees_mutex
);
568 void profile_free_file(prf_file_t prf
)
570 profile_dereference_data(prf
->data
);
574 /* Call with mutex locked! */
575 static void profile_free_file_data(prf_data_t data
)
577 scan_shared_trees_locked();
578 if (data
->flags
& PROFILE_FILE_SHARED
) {
579 /* Remove from linked list. */
580 if (g_shared_trees
== data
)
581 g_shared_trees
= data
->next
;
583 prf_data_t prev
, next
;
584 prev
= g_shared_trees
;
588 prev
->next
= next
->next
;
597 profile_free_node(data
->root
);
601 k5_mutex_destroy(&data
->lock
);
603 scan_shared_trees_locked();
606 errcode_t
profile_close_file(prf_file_t prf
)
610 retval
= profile_flush_file(prf
);
613 profile_free_file(prf
);