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 */ 8 9 #include <autoconf.h> 10 #include "prof_int.h" 11 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> 21 22 #include <sys/types.h> 23 #include <sys/stat.h> 24 #include <errno.h> 25 26 #ifdef HAVE_PWD_H 27 #include <pwd.h> 28 #endif 29 30 #if defined(_WIN32) 31 #include <io.h> 32 #define HAVE_STAT 33 #define stat _stat 34 #endif 35 36 #include "k5-platform.h" 37 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; 43 }; 44 #define g_shared_trees (krb5int_profile_shared_data.trees) 45 #define g_shared_trees_mutex (krb5int_profile_shared_data.mutex) 46 47 static struct global_shared_profile_data krb5int_profile_shared_data = { 48 0, 49 K5_MUTEX_PARTIAL_INITIALIZER 50 }; 51 52 MAKE_INIT_FUNCTION(profile_library_initializer); 53 MAKE_FINI_FUNCTION(profile_library_finalizer); 54 55 int profile_library_initializer(void) 56 { 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); 64 } 65 void profile_library_finalizer(void) 66 { 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; 72 } 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 80 } 81 82 static void profile_free_file_data(prf_data_t); 83 84 #if 0 85 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 } \ 99 } 100 101 #define scan_shared_trees_unlocked() \ 102 { \ 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); \ 108 } 109 110 #else 111 112 #define scan_shared_trees_locked() { ; } 113 #define scan_shared_trees_unlocked() { ; } 114 115 #endif 116 117 static int rw_access(const_profile_filespec_t filespec) 118 { 119 #ifdef HAVE_ACCESS 120 if (access(filespec, W_OK) == 0) 121 return 1; 122 else 123 return 0; 124 #else 125 /* 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. 129 */ 130 FILE *f; 131 /* Solaris Kerberos */ 132 f = fopen(filespec, "r+F"); 133 if (f) { 134 fclose(f); 135 return 1; 136 } 137 return 0; 138 #endif 139 } 140 141 static int r_access(const_profile_filespec_t filespec) 142 { 143 #ifdef HAVE_ACCESS 144 if (access(filespec, R_OK) == 0) 145 return 1; 146 else 147 return 0; 148 #else 149 /* 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. 153 */ 154 FILE *f; 155 156 /* Solaris Kerberos */ 157 f = fopen(filespec, "rF"); 158 if (f) { 159 fclose(f); 160 return 1; 161 } 162 return 0; 163 #endif 164 } 165 166 prf_data_t 167 profile_make_prf_data(const char *filename) 168 { 169 prf_data_t d; 170 size_t len, flen, slen; 171 char *fcopy; 172 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; 192 } 193 194 errcode_t profile_open_file(const_profile_filespec_t filespec, 195 prf_file_t *ret_prof) 196 { 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; 203 204 retval = CALL_INIT_FUNCTION(profile_library_initializer); 205 if (retval) 206 return retval; 207 208 scan_shared_trees_unlocked(); 209 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; 215 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]; 224 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; 229 } 230 #endif 231 if (home_env) 232 len += strlen(home_env); 233 } 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); 242 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; 249 } 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; 256 } 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; 266 } 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; 273 } 274 free(expanded_filename); 275 prf->data = data; 276 277 retval = k5_mutex_init(&data->lock); 278 if (retval) { 279 free(data); 280 free(prf); 281 return retval; 282 } 283 284 retval = profile_update_file(prf); 285 if (retval) { 286 profile_close_file(prf); 287 return retval; 288 } 289 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; 295 } 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); 302 303 *ret_prof = prf; 304 return 0; 305 } 306 307 errcode_t profile_update_file_data(prf_data_t data) 308 { 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; 316 317 retval = k5_mutex_lock(&data->lock); 318 if (retval) 319 return retval; 320 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; 326 } 327 if (stat(data->filespec, &st)) { 328 retval = errno; 329 k5_mutex_unlock(&data->lock); 330 return retval; 331 } 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; 347 } 348 if (data->root) { 349 profile_free_node(data->root); 350 data->root = 0; 351 } 352 if (data->comment) { 353 free(data->comment); 354 data->comment = 0; 355 } 356 #else 357 /* 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. 361 */ 362 if (data->root) { 363 k5_mutex_unlock(&data->lock); 364 return 0; 365 } 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; 376 } 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; 386 } 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; 394 } 395 396 static int 397 make_hard_link(const char *oldpath, const char *newpath) 398 { 399 #ifdef _WIN32 400 return -1; 401 #else 402 return link(oldpath, newpath); 403 #endif 404 } 405 406 static errcode_t write_data_to_file(prf_data_t data, const char *outfile, 407 int can_create) 408 { 409 FILE *f; 410 profile_filespec_t new_file; 411 profile_filespec_t old_file; 412 errcode_t retval = 0; 413 414 retval = ENOMEM; 415 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; 423 424 sprintf(new_file, "%s.$$$", outfile); 425 sprintf(old_file, "%s.bak", outfile); 426 427 errno = 0; 428 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; 436 } 437 438 profile_write_tree_file(data->root, f); 439 if (fclose(f) != 0) { 440 retval = errno; 441 goto errout; 442 } 443 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; 455 } 456 } else if (errno == ENOENT && can_create) { 457 if (rename(new_file, outfile)) { 458 retval = errno; 459 goto errout; 460 } 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; 471 } 472 if (rename(new_file, outfile)) { 473 retval = errno; 474 rename(old_file, outfile); /* back out... */ 475 goto errout; 476 } 477 } 478 479 data->flags = 0; 480 if (rw_access(outfile)) 481 data->flags |= PROFILE_FILE_RW; 482 retval = 0; 483 484 errout: 485 if (new_file) 486 free(new_file); 487 if (old_file) 488 free(old_file); 489 return retval; 490 } 491 492 errcode_t profile_flush_file_data_to_buffer (prf_data_t data, char **bufp) 493 { 494 errcode_t retval; 495 retval = k5_mutex_lock(&data->lock); 496 if (retval) 497 return retval; 498 retval = profile_write_tree_to_buffer(data->root, bufp); 499 k5_mutex_unlock(&data->lock); 500 return retval; 501 } 502 503 errcode_t profile_flush_file_data(prf_data_t data) 504 { 505 errcode_t retval = 0; 506 507 if (!data || data->magic != PROF_MAGIC_FILE_DATA) 508 return PROF_MAGIC_FILE_DATA; 509 510 retval = k5_mutex_lock(&data->lock); 511 if (retval) 512 return retval; 513 514 if ((data->flags & PROFILE_FILE_DIRTY) == 0) { 515 k5_mutex_unlock(&data->lock); 516 return 0; 517 } 518 519 retval = write_data_to_file(data, data->filespec, 0); 520 k5_mutex_unlock(&data->lock); 521 return retval; 522 } 523 524 errcode_t profile_flush_file_data_to_file(prf_data_t data, const char *outfile) 525 { 526 errcode_t retval = 0; 527 528 if (!data || data->magic != PROF_MAGIC_FILE_DATA) 529 return PROF_MAGIC_FILE_DATA; 530 531 retval = k5_mutex_lock(&data->lock); 532 if (retval) 533 return retval; 534 retval = write_data_to_file(data, outfile, 1); 535 k5_mutex_unlock(&data->lock); 536 return retval; 537 } 538 539 540 541 void profile_dereference_data(prf_data_t data) 542 { 543 int err; 544 err = k5_mutex_lock(&g_shared_trees_mutex); 545 if (err) 546 return; 547 profile_dereference_data_locked(data); 548 (void) k5_mutex_unlock(&g_shared_trees_mutex); 549 } 550 void profile_dereference_data_locked(prf_data_t data) 551 { 552 scan_shared_trees_locked(); 553 data->refcount--; 554 if (data->refcount == 0) 555 profile_free_file_data(data); 556 scan_shared_trees_locked(); 557 } 558 559 int profile_lock_global() 560 { 561 return k5_mutex_lock(&g_shared_trees_mutex); 562 } 563 int profile_unlock_global() 564 { 565 return k5_mutex_unlock(&g_shared_trees_mutex); 566 } 567 568 void profile_free_file(prf_file_t prf) 569 { 570 profile_dereference_data(prf->data); 571 free(prf); 572 } 573 574 /* Call with mutex locked! */ 575 static void profile_free_file_data(prf_data_t data) 576 { 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; 582 else { 583 prf_data_t prev, next; 584 prev = g_shared_trees; 585 next = prev->next; 586 while (next) { 587 if (next == data) { 588 prev->next = next->next; 589 break; 590 } 591 prev = next; 592 next = next->next; 593 } 594 } 595 } 596 if (data->root) 597 profile_free_node(data->root); 598 if (data->comment) 599 free(data->comment); 600 data->magic = 0; 601 k5_mutex_destroy(&data->lock); 602 free(data); 603 scan_shared_trees_locked(); 604 } 605 606 errcode_t profile_close_file(prf_file_t prf) 607 { 608 errcode_t retval; 609 610 retval = profile_flush_file(prf); 611 if (retval) 612 return retval; 613 profile_free_file(prf); 614 return 0; 615 } 616