import less(1)
[unleashed/tickless.git] / usr / src / lib / gss_mechs / mech_krb5 / profile / prof_file.c
blob378739755ffd7130f7b30e4b6df61e30f59df7db
1 /*
2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
4 */
5 /*
6 * prof_file.c ---- routines that manipulate an individual profile file.
7 */
9 #include <autoconf.h>
10 #include "prof_int.h"
12 #include <stdio.h>
13 #ifdef HAVE_STDLIB_H
14 #include <stdlib.h>
15 #endif
16 #ifdef HAVE_UNISTD_H
17 #include <unistd.h>
18 #endif
19 #include <string.h>
20 #include <stddef.h>
22 #include <sys/types.h>
23 #include <sys/stat.h>
24 #include <errno.h>
26 #ifdef HAVE_PWD_H
27 #include <pwd.h>
28 #endif
30 #if defined(_WIN32)
31 #include <io.h>
32 #define HAVE_STAT
33 #define stat _stat
34 #endif
36 #include "k5-platform.h"
38 struct global_shared_profile_data {
39 /* This is the head of the global list of shared trees */
40 prf_data_t trees;
41 /* Lock for above list. */
42 k5_mutex_t mutex;
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");
59 #endif
60 #if !USE_BUNDLE_ERROR_STRINGS
61 add_error_table(&et_prof_error_table);
62 #endif
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");
70 #endif
71 return;
73 #ifdef SHOW_INITFINI_FUNCS
74 printf("profile_library_finalizer\n");
75 #endif
76 k5_mutex_destroy(&g_shared_trees_mutex);
77 #if !USE_BUNDLE_ERROR_STRINGS
78 remove_error_table(&et_prof_error_table);
79 #endif
82 static void profile_free_file_data(prf_data_t);
84 #if 0
86 #define scan_shared_trees_locked() \
87 { \
88 prf_data_t d; \
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); \
98 } \
101 #define scan_shared_trees_unlocked() \
103 int r; \
104 r = k5_mutex_lock(&g_shared_trees_mutex); \
105 assert (r == 0); \
106 scan_shared_trees_locked(); \
107 k5_mutex_unlock(&g_shared_trees_mutex); \
110 #else
112 #define scan_shared_trees_locked() { ; }
113 #define scan_shared_trees_unlocked() { ; }
115 #endif
117 static int rw_access(const_profile_filespec_t filespec)
119 #ifdef HAVE_ACCESS
120 if (access(filespec, W_OK) == 0)
121 return 1;
122 else
123 return 0;
124 #else
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.
130 FILE *f;
131 /* Solaris Kerberos */
132 f = fopen(filespec, "r+F");
133 if (f) {
134 fclose(f);
135 return 1;
137 return 0;
138 #endif
141 static int r_access(const_profile_filespec_t filespec)
143 #ifdef HAVE_ACCESS
144 if (access(filespec, R_OK) == 0)
145 return 1;
146 else
147 return 0;
148 #else
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.
154 FILE *f;
156 /* Solaris Kerberos */
157 f = fopen(filespec, "rF");
158 if (f) {
159 fclose(f);
160 return 1;
162 return 0;
163 #endif
166 prf_data_t
167 profile_make_prf_data(const char *filename)
169 prf_data_t d;
170 size_t len, flen, slen;
171 char *fcopy;
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);
178 d = malloc(len);
179 if (d == NULL)
180 return NULL;
181 memset(d, 0, len);
182 fcopy = (char *) d + slen;
183 assert(fcopy == d->filespec);
184 strcpy(fcopy, filename);
185 d->refcount = 1;
186 d->comment = NULL;
187 d->magic = PROF_MAGIC_FILE_DATA;
188 d->root = NULL;
189 d->next = NULL;
190 d->fslen = flen;
191 return d;
194 errcode_t profile_open_file(const_profile_filespec_t filespec,
195 prf_file_t *ret_prof)
197 prf_file_t prf;
198 errcode_t retval;
199 char *home_env = 0;
200 unsigned int len;
201 prf_data_t data;
202 char *expanded_filename;
204 retval = CALL_INIT_FUNCTION(profile_library_initializer);
205 if (retval)
206 return retval;
208 scan_shared_trees_unlocked();
210 prf = malloc(sizeof(struct _prf_file_t));
211 if (!prf)
212 return ENOMEM;
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");
219 #ifdef HAVE_PWD_H
220 if (home_env == NULL) {
221 uid_t uid;
222 struct passwd *pw, pwx;
223 char pwbuf[BUFSIZ];
225 uid = getuid();
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;
230 #endif
231 if (home_env)
232 len += strlen(home_env);
234 expanded_filename = malloc(len);
235 if (expanded_filename == 0)
236 return errno;
237 if (home_env) {
238 strcpy(expanded_filename, home_env);
239 strcat(expanded_filename, filespec+1);
240 } else
241 memcpy(expanded_filename, filespec, len);
243 retval = k5_mutex_lock(&g_shared_trees_mutex);
244 if (retval) {
245 free(expanded_filename);
246 free(prf);
247 scan_shared_trees_unlocked();
248 return retval;
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))
255 break;
257 if (data) {
258 data->refcount++;
259 (void) k5_mutex_unlock(&g_shared_trees_mutex);
260 retval = profile_update_file_data(data);
261 free(expanded_filename);
262 prf->data = data;
263 *ret_prof = prf;
264 scan_shared_trees_unlocked();
265 return retval;
267 (void) k5_mutex_unlock(&g_shared_trees_mutex);
268 data = profile_make_prf_data(expanded_filename);
269 if (data == NULL) {
270 free(prf);
271 free(expanded_filename);
272 return ENOMEM;
274 free(expanded_filename);
275 prf->data = data;
277 retval = k5_mutex_init(&data->lock);
278 if (retval) {
279 free(data);
280 free(prf);
281 return retval;
284 retval = profile_update_file(prf);
285 if (retval) {
286 profile_close_file(prf);
287 return retval;
290 retval = k5_mutex_lock(&g_shared_trees_mutex);
291 if (retval) {
292 profile_close_file(prf);
293 scan_shared_trees_unlocked();
294 return retval;
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);
303 *ret_prof = prf;
304 return 0;
307 errcode_t profile_update_file_data(prf_data_t data)
309 errcode_t retval;
310 #ifdef HAVE_STAT
311 struct stat st;
312 unsigned long frac;
313 time_t now;
314 #endif
315 FILE *f;
317 retval = k5_mutex_lock(&data->lock);
318 if (retval)
319 return retval;
321 #ifdef HAVE_STAT
322 now = time(0);
323 if (now == data->last_stat && data->root != NULL) {
324 k5_mutex_unlock(&data->lock);
325 return 0;
327 if (stat(data->filespec, &st)) {
328 retval = errno;
329 k5_mutex_unlock(&data->lock);
330 return retval;
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;
339 #else
340 frac = 0;
341 #endif
342 if (st.st_mtime == data->timestamp
343 && frac == data->frac_ts
344 && data->root != NULL) {
345 k5_mutex_unlock(&data->lock);
346 return 0;
348 if (data->root) {
349 profile_free_node(data->root);
350 data->root = 0;
352 if (data->comment) {
353 free(data->comment);
354 data->comment = 0;
356 #else
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.
362 if (data->root) {
363 k5_mutex_unlock(&data->lock);
364 return 0;
366 #endif
367 errno = 0;
368 /* Solaris Kerberos */
369 f = fopen(data->filespec, "rF");
370 if (f == NULL) {
371 retval = errno;
372 k5_mutex_unlock(&data->lock);
373 if (retval == 0)
374 retval = ENOENT;
375 return retval;
377 data->upd_serial++;
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);
382 fclose(f);
383 if (retval) {
384 k5_mutex_unlock(&data->lock);
385 return retval;
387 assert(data->root != NULL);
388 #ifdef HAVE_STAT
389 data->timestamp = st.st_mtime;
390 data->frac_ts = frac;
391 #endif
392 k5_mutex_unlock(&data->lock);
393 return 0;
396 static int
397 make_hard_link(const char *oldpath, const char *newpath)
399 #ifdef _WIN32
400 return -1;
401 #else
402 return link(oldpath, newpath);
403 #endif
406 static errcode_t write_data_to_file(prf_data_t data, const char *outfile,
407 int can_create)
409 FILE *f;
410 profile_filespec_t new_file;
411 profile_filespec_t old_file;
412 errcode_t retval = 0;
414 retval = ENOMEM;
416 new_file = old_file = 0;
417 new_file = malloc(strlen(outfile) + 5);
418 if (!new_file)
419 goto errout;
420 old_file = malloc(strlen(outfile) + 5);
421 if (!old_file)
422 goto errout;
424 sprintf(new_file, "%s.$$$", outfile);
425 sprintf(old_file, "%s.bak", outfile);
427 errno = 0;
429 /* Solaris Kerberos */
430 f = fopen(new_file, "wF");
431 if (!f) {
432 retval = errno;
433 if (retval == 0)
434 retval = PROF_FAIL_OPEN;
435 goto errout;
438 profile_write_tree_file(data->root, f);
439 if (fclose(f) != 0) {
440 retval = errno;
441 goto errout;
444 unlink(old_file);
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
448 place. */
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
452 needed. */
453 retval = errno;
454 goto errout;
456 } else if (errno == ENOENT && can_create) {
457 if (rename(new_file, outfile)) {
458 retval = errno;
459 goto errout;
461 } else {
462 /* Couldn't make the hard link, so there's going to be a
463 small window where data->filespec does not refer to
464 either version. */
465 #ifndef _WIN32
466 sync();
467 #endif
468 if (rename(outfile, old_file)) {
469 retval = errno;
470 goto errout;
472 if (rename(new_file, outfile)) {
473 retval = errno;
474 rename(old_file, outfile); /* back out... */
475 goto errout;
479 data->flags = 0;
480 if (rw_access(outfile))
481 data->flags |= PROFILE_FILE_RW;
482 retval = 0;
484 errout:
485 free(new_file);
486 free(old_file);
487 return retval;
490 errcode_t profile_flush_file_data_to_buffer (prf_data_t data, char **bufp)
492 errcode_t retval;
493 retval = k5_mutex_lock(&data->lock);
494 if (retval)
495 return retval;
496 retval = profile_write_tree_to_buffer(data->root, bufp);
497 k5_mutex_unlock(&data->lock);
498 return retval;
501 errcode_t profile_flush_file_data(prf_data_t data)
503 errcode_t retval = 0;
505 if (!data || data->magic != PROF_MAGIC_FILE_DATA)
506 return PROF_MAGIC_FILE_DATA;
508 retval = k5_mutex_lock(&data->lock);
509 if (retval)
510 return retval;
512 if ((data->flags & PROFILE_FILE_DIRTY) == 0) {
513 k5_mutex_unlock(&data->lock);
514 return 0;
517 retval = write_data_to_file(data, data->filespec, 0);
518 k5_mutex_unlock(&data->lock);
519 return retval;
522 errcode_t profile_flush_file_data_to_file(prf_data_t data, const char *outfile)
524 errcode_t retval = 0;
526 if (!data || data->magic != PROF_MAGIC_FILE_DATA)
527 return PROF_MAGIC_FILE_DATA;
529 retval = k5_mutex_lock(&data->lock);
530 if (retval)
531 return retval;
532 retval = write_data_to_file(data, outfile, 1);
533 k5_mutex_unlock(&data->lock);
534 return retval;
539 void profile_dereference_data(prf_data_t data)
541 int err;
542 err = k5_mutex_lock(&g_shared_trees_mutex);
543 if (err)
544 return;
545 profile_dereference_data_locked(data);
546 (void) k5_mutex_unlock(&g_shared_trees_mutex);
548 void profile_dereference_data_locked(prf_data_t data)
550 scan_shared_trees_locked();
551 data->refcount--;
552 if (data->refcount == 0)
553 profile_free_file_data(data);
554 scan_shared_trees_locked();
557 int profile_lock_global()
559 return k5_mutex_lock(&g_shared_trees_mutex);
561 int profile_unlock_global()
563 return k5_mutex_unlock(&g_shared_trees_mutex);
566 void profile_free_file(prf_file_t prf)
568 profile_dereference_data(prf->data);
569 free(prf);
572 /* Call with mutex locked! */
573 static void profile_free_file_data(prf_data_t data)
575 scan_shared_trees_locked();
576 if (data->flags & PROFILE_FILE_SHARED) {
577 /* Remove from linked list. */
578 if (g_shared_trees == data)
579 g_shared_trees = data->next;
580 else {
581 prf_data_t prev, next;
582 prev = g_shared_trees;
583 next = prev->next;
584 while (next) {
585 if (next == data) {
586 prev->next = next->next;
587 break;
589 prev = next;
590 next = next->next;
594 if (data->root)
595 profile_free_node(data->root);
596 free(data->comment);
597 data->magic = 0;
598 k5_mutex_destroy(&data->lock);
599 free(data);
600 scan_shared_trees_locked();
603 errcode_t profile_close_file(prf_file_t prf)
605 errcode_t retval;
607 retval = profile_flush_file(prf);
608 if (retval)
609 return retval;
610 profile_free_file(prf);
611 return 0;