1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* 27 * Routines to add file and directory entries into the internal configuration 28 * information. This information is maintained in a number of hash tables which 29 * after completion of input file processing will be processed and written to 30 * the output configuration file. 31 * 32 * Each hash table is defined via a Hash_tbl structure. These are organized: 33 * 34 * c_strtbl contains a hash entry for every file, directory, pathname and 35 * alternative path (dldump(3dl) image) processed. 36 * c_strsize and c_objnum maintain the size and count of the 37 * strings added to this table and are used to size the output 38 * configuration file. 39 * 40 * c_inotbls contains a list of inode hash tables. Each element of the list 41 * identifies a unique device. Thus, for each file processed its 42 * st_dev and st_ino are used to assign its entry to the correct 43 * hash table. 44 * 45 * Each directory processed is assigned a unique id (c_dirnum) 46 * which insures each file also becomes uniquely identified. 47 * 48 * All file and directory additions come through the inspect() entry point. 49 */ 50 51 #include <sys/types.h> 52 #include <sys/stat.h> 53 #include <fcntl.h> 54 #include <dirent.h> 55 #include <_libelf.h> 56 #include <errno.h> 57 #include <stdio.h> 58 #include <string.h> 59 #include <unistd.h> 60 #include <limits.h> 61 #include "machdep.h" 62 #include "sgs.h" 63 #include "rtc.h" 64 #include "_crle.h" 65 #include "msg.h" 66 67 /* 68 * Add an alternative pathname for an object. Although a configuration file 69 * may contain several pathnames that resolve to the same real file, there can 70 * only be one real file. Consequently, there can only be one alternative. 71 * For multiple pathnames that resolve to the same real file, multiple alter- 72 * natives may be specified. Always take the alternative for the real file 73 * over any others. 74 */ 75 static int 76 enteralt(Crle_desc *crle, const char *path, const char *file, Half flags, 77 Hash_obj *obj) 78 { 79 const char *fmt; 80 char alter[PATH_MAX]; 81 size_t altsz; 82 83 if (obj->o_alter) { 84 /* 85 * If an alternative has already been captured, only override 86 * it if the specified file is the real file. 87 */ 88 if (strcmp(path, obj->o_path)) 89 return (1); 90 } 91 92 /* 93 * Create an alternative pathname from the file and object destination 94 * directory. If we're dumping an alternative don't allow it to 95 * override the original. 96 */ 97 if (flags & RTC_OBJ_DUMP) { 98 char _alter[PATH_MAX]; 99 100 (void) strlcpy(alter, crle->c_objdir, sizeof (alter)); 101 (void) realpath(alter, _alter); 102 (void) snprintf(alter, PATH_MAX, MSG_ORIG(MSG_FMT_PATH), 103 _alter, file); 104 if (strcmp(alter, obj->o_path) == 0) { 105 (void) printf(MSG_INTL(MSG_ARG_ALT), crle->c_name, 106 obj->o_path); 107 return (0); 108 } 109 obj->o_flags |= RTC_OBJ_DUMP; 110 } else { 111 (void) snprintf(alter, PATH_MAX, MSG_ORIG(MSG_FMT_PATH), 112 crle->c_objdir, file); 113 } 114 obj->o_flags |= RTC_OBJ_ALTER; 115 116 /* 117 * If we're overriding an existing alternative with the real path, free 118 * up any previous alternative. 119 */ 120 if (obj->o_alter) { 121 crle->c_strsize -= strlen(alter) + 1; 122 fmt = MSG_INTL(MSG_DIA_ALTUPDATE); 123 } else { 124 fmt = MSG_INTL(MSG_DIA_ALTCREATE); 125 } 126 127 /* 128 * Allocate the new alternative and update the string table size. 129 */ 130 altsz = strlen(alter) + 1; 131 if ((obj->o_alter = malloc(altsz)) == NULL) 132 return (0); 133 (void) strcpy(obj->o_alter, alter); 134 135 crle->c_strsize += altsz; 136 137 if (crle->c_flags & CRLE_VERBOSE) 138 (void) printf(fmt, alter, obj->o_path); 139 140 return (1); 141 } 142 143 144 /* 145 * Establish an inode hash entry, this is unique for each dev hash table, and 146 * establishes the unique object descriptor. 147 */ 148 static Hash_ent * 149 enterino(Crle_desc *crle, const char *name, struct stat *status, Half flags) 150 { 151 Hash_ent *ent; 152 Hash_obj *obj; 153 Hash_tbl *tbl; 154 Aliste idx; 155 Addr ino = (Addr)status->st_ino; 156 ulong_t dev = status->st_dev; 157 Lword info; 158 int found = 0; 159 160 /* 161 * For configuration file verification we retain information about the 162 * file or directory. 163 */ 164 if (flags & RTC_OBJ_DIRENT) 165 info = (Lword)status->st_mtime; 166 else 167 info = (Lword)status->st_size; 168 169 /* 170 * Determine the objects device number and establish a hash table for 171 * for this devices inodes. 172 */ 173 for (APLIST_TRAVERSE(crle->c_inotbls, idx, tbl)) { 174 if (tbl->t_ident == dev) { 175 found = 1; 176 break; 177 } 178 } 179 if (found == 0) { 180 if ((tbl = make_hash(crle->c_inobkts, HASH_INT, dev)) == NULL) 181 return (NULL); 182 if (aplist_append(&crle->c_inotbls, tbl, AL_CNT_CRLE) == NULL) 183 return (NULL); 184 } 185 186 /* 187 * Reuse or add this new object to the inode hash table. 188 */ 189 if ((ent = get_hash(tbl, ino, 0, 190 (HASH_FND_ENT | HASH_ADD_ENT))) == NULL) 191 return (NULL); 192 193 /* 194 * If an object descriptor doesn't yet exist create one. 195 */ 196 if ((obj = ent->e_obj) == NULL) { 197 if ((obj = calloc(sizeof (Hash_obj), 1)) == NULL) 198 return (NULL); 199 obj->o_tbl = tbl; 200 obj->o_flags = flags; 201 obj->o_info = info; 202 203 /* 204 * Reallocate the objects name, as it might have been composed 205 * and passed to us on the stack. 206 */ 207 if ((obj->o_path = strdup(name)) == NULL) 208 return (NULL); 209 210 /* 211 * Assign this object to the original ino hash entry. 212 */ 213 ent->e_obj = obj; 214 } 215 return (ent); 216 } 217 218 /* 219 * Basic directory entry, establishes entry information, updated global counts 220 * and provides any diagnostics. 221 */ 222 static int 223 _enterdir(Crle_desc *crle, const char *dir, Hash_ent *ent, Hash_obj *obj) 224 { 225 size_t size = strlen(dir) + 1; 226 char *ndir; 227 228 /* 229 * Establish this hash entries key (which is the directory name itself), 230 * assign the next available directory number, and its object. 231 */ 232 if ((ndir = malloc(size)) == NULL) 233 return (0); 234 (void) strcpy(ndir, dir); 235 236 ent->e_key = (Addr)ndir; 237 ent->e_id = crle->c_dirnum++; 238 ent->e_obj = obj; 239 240 /* 241 * Update string table information. We add a dummy filename for each 242 * real directory so as to have a null terminated file table array for 243 * this directory. 244 */ 245 crle->c_strsize += size; 246 crle->c_hashstrnum++; 247 crle->c_filenum++; 248 249 /* 250 * Provide any diagnostics. 251 */ 252 if (crle->c_flags & CRLE_VERBOSE) { 253 const char *fmt; 254 255 if (obj->o_flags & RTC_OBJ_NOEXIST) 256 fmt = MSG_INTL(MSG_DIA_NOEXIST); 257 else 258 fmt = MSG_INTL(MSG_DIA_DIR); 259 260 (void) printf(fmt, ent->e_id, dir); 261 } 262 return (1); 263 } 264 265 /* 266 * Establish a string hash entry for a directory. 267 */ 268 static Hash_ent * 269 enterdir(Crle_desc *crle, const char *odir, Half flags, struct stat *status) 270 { 271 Hash_tbl *stbl = crle->c_strtbl; 272 Hash_ent *ent; 273 Hash_obj *obj; 274 char rdir[PATH_MAX], *ndir; 275 276 /* 277 * Establish the directories real name, this is the name that will be 278 * recorded in the object identifier. 279 */ 280 if (realpath(odir, rdir) == NULL) 281 return (NULL); 282 283 if (strcmp(odir, rdir)) 284 ndir = rdir; 285 else 286 ndir = (char *)odir; 287 288 /* 289 * If we're not dealing with an all-entries directory (i.e., we're 290 * recording this directory because of its explicitly specified 291 * filename) leave off any filename specific attributes. 292 */ 293 if ((flags & RTC_OBJ_ALLENTS) == 0) 294 flags &= ~(RTC_OBJ_ALTER | RTC_OBJ_DUMP | RTC_OBJ_GROUP); 295 flags |= RTC_OBJ_DIRENT; 296 297 /* 298 * Establish a inode table entry, and the objects unique descriptor. 299 */ 300 if ((ent = enterino(crle, ndir, status, flags)) == NULL) 301 return (NULL); 302 obj = ent->e_obj; 303 304 /* 305 * Create a string table entry for the real directory. 306 */ 307 if ((ent = get_hash(stbl, (Addr)ndir, 0, 308 (HASH_FND_ENT | HASH_ADD_ENT))) == NULL) 309 return (NULL); 310 311 /* 312 * If this is a new entry reassign the directory name and assign a 313 * unique directory id. 314 */ 315 if (ent->e_id == 0) { 316 if (_enterdir(crle, ndir, ent, obj) == 0) 317 return (NULL); 318 } 319 320 /* 321 * If the directory name supplied is different than the real name we've 322 * just entered, continue to create an entry for it. 323 */ 324 if (ndir == odir) 325 return (ent); 326 327 /* 328 * Create a string table entry for this real directory. 329 */ 330 if ((ent = get_hash(stbl, (Addr)odir, 0, 331 (HASH_FND_ENT | HASH_ADD_ENT))) == NULL) 332 return (NULL); 333 334 /* 335 * If this is a new entry reassign the directory name and assign a 336 * unique directory id. 337 */ 338 if (ent->e_id == 0) { 339 if (_enterdir(crle, odir, ent, obj) == 0) 340 return (NULL); 341 } 342 343 return (ent); 344 } 345 346 /* 347 * Establish a non-existent directory entry. There is no inode entry created 348 * for this, just a directory and its associated object. 349 */ 350 static Hash_ent * 351 enternoexistdir(Crle_desc *crle, const char *dir) 352 { 353 Hash_ent *ent; 354 355 /* 356 * Reuse or add this new non-existent directory to the string table. 357 */ 358 if ((ent = get_hash(crle->c_strtbl, (Addr)dir, 0, 359 (HASH_FND_ENT | HASH_ADD_ENT))) == NULL) 360 return (NULL); 361 362 /* 363 * If this is a new entry, assign both the object and the directory 364 * entry information. 365 */ 366 if (ent->e_id == 0) { 367 Hash_obj * obj; 368 369 if ((obj = calloc(sizeof (Hash_obj), 1)) == NULL) 370 return (NULL); 371 obj->o_flags = (RTC_OBJ_NOEXIST | RTC_OBJ_DIRENT); 372 373 if (_enterdir(crle, dir, ent, obj) == 0) 374 return (NULL); 375 } 376 return (ent); 377 } 378 379 380 /* 381 * Basic file entry, establishes entry information, updated global counts 382 * and provides any diagnostics. 383 */ 384 static int 385 _enterfile(Crle_desc *crle, const char *file, int off, Hash_ent *fent, 386 Hash_ent *rent, Hash_ent *dent, Hash_obj *obj) 387 { 388 size_t size = strlen(file) + 1; 389 char *nfile; 390 391 /* 392 * If this is a full file name reallocate it, as it might have been 393 * composed and passed to us on the stack. Otherwise reuse the original 394 * directory name to satisfy the filename, here we record the offset of 395 * the file in the directory name so that we can reduce the string table 396 * in the final configuration file. 397 */ 398 if (off == 0) { 399 if ((nfile = malloc(size)) == NULL) 400 return (0); 401 (void) strcpy(nfile, file); 402 } else { 403 nfile = (char *)file; 404 } 405 406 fent->e_key = (Addr)nfile; 407 fent->e_off = off; 408 409 /* 410 * Assign directory and directory id, and any real (full) path 411 * association. 412 */ 413 fent->e_dir = dent; 414 fent->e_id = dent->e_id; 415 fent->e_path = rent; 416 417 /* 418 * Increment the file count for this directory. 419 */ 420 dent->e_cnt++; 421 422 /* 423 * Assign this object to the new string hash entry. 424 */ 425 fent->e_obj = obj; 426 427 /* 428 * Update string table information. 429 */ 430 crle->c_strsize += size; 431 crle->c_hashstrnum++; 432 crle->c_filenum++; 433 434 /* 435 * Provide any diagnostics. 436 */ 437 if (crle->c_flags & CRLE_VERBOSE) 438 (void) printf(MSG_INTL(MSG_DIA_FILE), fent->e_id, nfile); 439 440 return (1); 441 } 442 443 444 /* 445 * Establish a non-existent file entry. There is no inode entry created for 446 * this, just the files full and simple name, and its associated object. 447 */ 448 static Hash_ent * 449 enternoexistfile(Crle_desc *crle, const char *path, const char *file, 450 Hash_ent *dent) 451 { 452 Hash_ent *rent, *ent; 453 Hash_obj *obj; 454 int off; 455 456 /* 457 * Create a string table entry for the full filename. 458 */ 459 if ((rent = get_hash(crle->c_strtbl, (Addr)path, 0, 460 (HASH_FND_ENT | HASH_ADD_ENT))) == NULL) 461 return (NULL); 462 463 /* 464 * If this is a new entry, assign both the object and the full filename 465 * entry information. 466 */ 467 if (rent->e_id == 0) { 468 if ((obj = calloc(sizeof (Hash_obj), 1)) == NULL) 469 return (NULL); 470 obj->o_flags = RTC_OBJ_NOEXIST; 471 472 if (_enterfile(crle, path, 0, rent, 0, dent, obj) == 0) 473 return (NULL); 474 } 475 obj = rent->e_obj; 476 if ((obj->o_path = strdup(path)) == NULL) 477 return (NULL); 478 479 /* 480 * Express the filename in terms of the full pathname. By reusing the 481 * name within the full filename we can reduce the overall string table 482 * size in the output configuration file. 483 */ 484 off = file - path; 485 file = (char *)rent->e_key + off; 486 487 /* 488 * Create a entry for the individual file within this directory. 489 */ 490 if ((ent = get_hash(crle->c_strtbl, (Addr)file, dent->e_id, 491 (HASH_FND_ENT | HASH_ADD_ENT))) == NULL) 492 return (NULL); 493 494 if (ent->e_id == 0) { 495 if (_enterfile(crle, file, off, ent, rent, dent, obj) == 0) 496 return (NULL); 497 } 498 return (ent); 499 } 500 501 502 /* 503 * Establish a string hash entry for a file. 504 */ 505 static Hash_ent * 506 enterfile(Crle_desc *crle, const char *opath, const char *ofile, Half flags, 507 Hash_ent *odent, struct stat *status) 508 { 509 Hash_tbl *stbl = crle->c_strtbl; 510 Hash_ent *ent, *rent, *ndent = odent; 511 Hash_obj *obj; 512 size_t size; 513 char rpath[PATH_MAX], *npath, *nfile; 514 int off; 515 516 /* 517 * Establish the files real name, this is the name that will be 518 * recorded in the object identifier. 519 */ 520 if (realpath(opath, rpath) == NULL) 521 return (NULL); 522 523 if (strcmp(opath, rpath)) { 524 npath = rpath; 525 if (nfile = strrchr(npath, '/')) 526 nfile++; 527 else 528 nfile = npath; 529 530 /* 531 * Determine if the real pathname has a different directory to 532 * the original passed to us. 533 */ 534 size = nfile - npath; 535 if (strncmp(opath, npath, size)) { 536 char _npath[PATH_MAX]; 537 struct stat _status; 538 539 (void) strncpy(_npath, npath, size); 540 _npath[size - 1] = '\0'; 541 542 (void) stat(_npath, &_status); 543 if ((ndent = enterdir(crle, _npath, flags, 544 &_status)) == NULL) 545 return (NULL); 546 } 547 } else { 548 npath = (char *)opath; 549 nfile = (char *)ofile; 550 } 551 552 /* 553 * Establish an inode table entry, and the objects unique descriptor. 554 */ 555 if ((ent = enterino(crle, npath, status, flags)) == NULL) 556 return (NULL); 557 obj = ent->e_obj; 558 559 /* 560 * Create a string table entry for the full filename. 561 */ 562 if ((rent = get_hash(stbl, (Addr)npath, 0, 563 (HASH_FND_ENT | HASH_ADD_ENT))) == NULL) 564 return (NULL); 565 if (rent->e_id == 0) { 566 if (_enterfile(crle, npath, 0, rent, 0, ndent, obj) == 0) 567 return (NULL); 568 } 569 570 /* 571 * Identify this entry and its directory as real paths. If dldump(3dl) 572 * processing is required this flag is checked, as we only need to dump 573 * the real pathname. Many other objects may point to the same 574 * alternative, but only one needs to be dumped. In addition, during 575 * ld.so.1 validation, only this directory and file need be checked. 576 */ 577 rent->e_flags |= RTC_OBJ_REALPTH; 578 ndent->e_flags |= RTC_OBJ_REALPTH; 579 580 /* 581 * Express the filename in terms of the full pathname. By reusing the 582 * name within the full filename we can reduce the overall string table 583 * size in the output configuration file. 584 */ 585 off = nfile - npath; 586 nfile = (char *)rent->e_key + off; 587 588 /* 589 * Create a entry for the individual file within this directory. 590 */ 591 if ((ent = get_hash(stbl, (Addr)nfile, ndent->e_id, 592 (HASH_FND_ENT | HASH_ADD_ENT))) == NULL) 593 return (NULL); 594 if (ent->e_id == 0) { 595 if (_enterfile(crle, nfile, off, ent, rent, ndent, obj) == 0) 596 return (NULL); 597 } 598 599 /* 600 * If the original path name is not equivalent to the real path name, 601 * then we had an alias (typically it's a symlink). Add the path name 602 * to the string hash table and reference the object data structure. 603 */ 604 if (nfile == ofile) 605 return (ent); 606 607 /* 608 * Establish an inode table entry, and the objects unique descriptor. 609 */ 610 if ((ent = enterino(crle, opath, status, 0)) == NULL) 611 return (NULL); 612 obj = ent->e_obj; 613 614 /* 615 * Create a string table entry for the full filename. 616 */ 617 if ((rent = get_hash(stbl, (Addr)opath, 0, 618 (HASH_FND_ENT | HASH_ADD_ENT))) == NULL) 619 return (NULL); 620 if (rent->e_id == 0) { 621 if (_enterfile(crle, opath, 0, rent, 0, odent, obj) == 0) 622 return (NULL); 623 } 624 625 /* 626 * Express the filename in terms of the full pathname. By reusing the 627 * name within the full filename we can reduce the overall string table 628 * size in the output configuration file. 629 */ 630 off = ofile - opath; 631 ofile = (char *)rent->e_key + off; 632 633 /* 634 * Create a entry for the individual file within this directory. 635 */ 636 if ((ent = get_hash(stbl, (Addr)ofile, odent->e_id, 637 (HASH_FND_ENT | HASH_ADD_ENT))) == NULL) 638 return (NULL); 639 if (ent->e_id == 0) { 640 if (_enterfile(crle, ofile, off, ent, rent, odent, obj) == 0) 641 return (NULL); 642 } 643 644 return (ent); 645 } 646 647 /* 648 * Add a file to configuration information. 649 */ 650 static int 651 inspect_file(Crle_desc *crle, const char *path, const char *file, Half flags, 652 Hash_ent *dent, struct stat *status, int error) 653 { 654 Hash_ent *ent; 655 Hash_obj *obj; 656 int fd; 657 Elf *elf; 658 GElf_Ehdr ehdr; 659 GElf_Xword dyflags = 0; 660 Aliste idx; 661 Hash_tbl *tbl; 662 Addr ino = (Addr)status->st_ino; 663 664 /* 665 * Determine whether this file (inode) has already been processed. 666 */ 667 for (APLIST_TRAVERSE(crle->c_inotbls, idx, tbl)) { 668 if (tbl->t_ident != status->st_dev) 669 continue; 670 671 if ((ent = get_hash(tbl, ino, 0, HASH_FND_ENT)) == NULL) 672 break; 673 674 /* 675 * This files inode object does exist, make sure it has a file 676 * entry for this directory. 677 */ 678 if ((ent = enterfile(crle, path, file, flags, dent, 679 status)) == NULL) 680 return (error); 681 obj = ent->e_obj; 682 683 /* 684 * If an alternative has been asked for, and one has not yet 685 * been established, create one. 686 */ 687 if ((flags & RTC_OBJ_ALTER) && 688 ((obj->o_flags & RTC_OBJ_NOALTER) == 0)) { 689 if (enteralt(crle, path, file, flags, obj) == 0) 690 return (error); 691 } 692 return (0); 693 } 694 695 /* 696 * This is a new file, determine if it's a valid ELF file. 697 */ 698 if ((fd = open(path, O_RDONLY, 0)) == -1) { 699 if (error) { 700 int err = errno; 701 (void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN), 702 crle->c_name, path, strerror(err)); 703 } 704 return (error); 705 } 706 707 /* 708 * Obtain an ELF descriptor and determine if we have a shared object. 709 */ 710 if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { 711 if (error) 712 (void) fprintf(stderr, MSG_INTL(MSG_ELF_BEGIN), 713 crle->c_name, path, elf_errmsg(-1)); 714 (void) close(fd); 715 return (error); 716 } 717 if ((elf_kind(elf) != ELF_K_ELF) || 718 (gelf_getehdr(elf, &ehdr) == NULL) || 719 (!((ehdr.e_type == ET_EXEC) || (ehdr.e_type == ET_DYN))) || 720 (!((ehdr.e_ident[EI_CLASS] == M_CLASS) || 721 (ehdr.e_machine == M_MACH)))) { 722 if (error) 723 (void) fprintf(stderr, MSG_INTL(MSG_ELF_TYPE), 724 crle->c_name, path); 725 (void) close(fd); 726 (void) elf_end(elf); 727 return (error); 728 } 729 730 (void) close(fd); 731 732 /* 733 * If we're generating alternative objects find this objects DT_FLAGS 734 * to insure it isn't marked as non-dumpable (libdl.so.1 falls into 735 * this category). 736 */ 737 if (flags & RTC_OBJ_DUMP) 738 dyflags = _gelf_getdyndtflags_1(elf); 739 740 /* 741 * Dynamic executables can be examined to determine their dependencies, 742 * dldump(3dl) their dependencies, and may even be dldump(3dl)'ed 743 * themselves. 744 * 745 * If we come across an executable while searching a directory 746 * (error == 0) it is ignored. 747 */ 748 if (ehdr.e_type == ET_EXEC) { 749 if (error == 0) { 750 (void) elf_end(elf); 751 return (0); 752 } 753 754 /* 755 * If we're not dumping the application itself, or we've not 756 * asked to gather its dependencies then its rather useless. 757 */ 758 if ((flags & (RTC_OBJ_GROUP | RTC_OBJ_DUMP)) == 0) { 759 (void) fprintf(stderr, MSG_INTL(MSG_GEN_INVFILE), 760 crle->c_name, path); 761 (void) elf_end(elf); 762 return (error); 763 } 764 765 /* 766 * If we're dumping the application under RTLD_REL_EXEC then the 767 * configuration file becomes specific to this application, so 768 * make sure we haven't been here before. 769 */ 770 if (crle->c_app && (flags & RTC_OBJ_DUMP) && 771 (crle->c_dlflags & RTLD_REL_EXEC)) { 772 (void) fprintf(stderr, MSG_INTL(MSG_ARG_MODE), 773 crle->c_name, crle->c_app, path); 774 (void) elf_end(elf); 775 return (error); 776 } 777 } 778 779 /* 780 * Enter the file in the string hash table. 781 */ 782 if ((ent = enterfile(crle, path, file, flags, dent, status)) == NULL) { 783 (void) elf_end(elf); 784 return (error); 785 } 786 obj = ent->e_obj; 787 788 if (flags & RTC_OBJ_ALTER) { 789 /* 790 * If this object is marked as non-dumpable make sure we don't 791 * create a dldump(3dl) alternative. A user requested 792 * alternative is acceptable. 793 */ 794 if ((flags & RTC_OBJ_DUMP) && (dyflags & DF_1_NODUMP)) { 795 obj->o_flags |= RTC_OBJ_NOALTER; 796 obj->o_flags &= ~(RTC_OBJ_ALTER | RTC_OBJ_DUMP); 797 } else { 798 if (enteralt(crle, path, file, flags, obj) == 0) { 799 (void) elf_end(elf); 800 return (error); 801 } 802 } 803 } 804 805 /* 806 * Executables are recorded in the configuration file either to allow 807 * for the configuration files update, or may indicate that the 808 * configuration file is specific to their use. 809 */ 810 if (ehdr.e_type == ET_EXEC) { 811 obj->o_flags |= RTC_OBJ_EXEC; 812 813 if ((flags & RTC_OBJ_DUMP) && 814 (crle->c_dlflags & RTLD_REL_EXEC)) { 815 /* 816 * Get the reallocated pathname rather than using the 817 * original (the original might be from an existing 818 * configuration file being updated, in which case the 819 * pointer will be unmapped before we get to use it). 820 */ 821 ent = get_hash(crle->c_strtbl, (Addr)path, 0, 822 HASH_FND_ENT); 823 824 obj->o_flags |= RTC_OBJ_APP; 825 crle->c_app = (char *)ent->e_key; 826 } 827 } 828 829 /* 830 * If we've been asked to process this object as a group determine its 831 * dependencies. 832 */ 833 if (flags & RTC_OBJ_GROUP) { 834 if (depend(crle, path, flags, &ehdr)) { 835 (void) elf_end(elf); 836 return (error); 837 } 838 } 839 840 (void) elf_end(elf); 841 return (0); 842 } 843 844 /* 845 * Add a directory to configuration information. 846 */ 847 static int 848 inspect_dir(Crle_desc *crle, const char *name, Half flags, struct stat *status) 849 { 850 Hash_tbl *stbl = crle->c_strtbl; 851 DIR *dir; 852 struct dirent *dirent; 853 Hash_ent *ent; 854 int error = 0; 855 struct stat _status; 856 char path[PATH_MAX], * dst; 857 const char *src; 858 859 /* 860 * Determine whether we've already visited this directory to process 861 * all its entries. 862 */ 863 if ((ent = get_hash(stbl, (Addr)name, 0, HASH_FND_ENT)) != NULL) { 864 if (ent->e_obj->o_flags & RTC_OBJ_ALLENTS) 865 return (0); 866 } else { 867 /* 868 * Create a directory hash entry. 869 */ 870 if ((ent = enterdir(crle, name, (flags | RTC_OBJ_ALLENTS), 871 status)) == NULL) 872 return (1); 873 } 874 ent->e_obj->o_flags |= RTC_OBJ_ALLENTS; 875 876 /* 877 * Establish the pathname buffer. 878 */ 879 for (dst = path, dst--, src = name; *src; src++) 880 *++dst = *src; 881 if (*dst++ != '/') 882 *dst++ = '/'; 883 884 /* 885 * Access the directory in preparation for reading its entries. 886 */ 887 if ((dir = opendir(name)) == NULL) 888 return (1); 889 890 /* 891 * Read each entry from the directory looking for ELF files. 892 */ 893 while ((dirent = readdir(dir)) != NULL) { 894 const char *file = dirent->d_name; 895 char *_dst; 896 897 /* 898 * Ignore "." and ".." entries. 899 */ 900 if ((file[0] == '.') && ((file[1] == '\0') || 901 ((file[1] == '.') && (file[2] == '\0')))) 902 continue; 903 904 /* 905 * Complete full pathname, and reassign file to the new path. 906 */ 907 for (_dst = dst, src = file, file = dst; *src; _dst++, src++) 908 *_dst = *src; 909 *_dst = '\0'; 910 911 if (stat(path, &_status) == -1) 912 continue; 913 914 if ((_status.st_mode & S_IFMT) != S_IFREG) 915 continue; 916 917 if (inspect_file(crle, path, file, flags, ent, &_status, 0)) { 918 error = 1; 919 break; 920 } 921 } 922 return (error); 923 } 924 925 /* 926 * Inspect a file/dir name. A stat(name) results in the following actions: 927 * 928 * The name doesn't exist: 929 * The name is assummed to be a non-existent directory and a directory 930 * cache entry is created to indicate this. 931 * 932 * The name is a directory: 933 * The directory is searched for appropriate files. 934 * 935 * The name is a file: 936 * The file is processed and added to the cache if appropriate. 937 */ 938 int 939 inspect(Crle_desc *crle, const char *name, Half flags) 940 { 941 Hash_ent *ent; 942 const char *file, *dir; 943 struct stat status; 944 char _name[PATH_MAX], _dir[PATH_MAX]; 945 Half nflags = flags & ~RTC_OBJ_CMDLINE; 946 int noexist; 947 948 /* 949 * If this is the first time through here establish a string table 950 * cache. 951 */ 952 if (crle->c_dirnum == 0) { 953 if ((crle->c_strtbl = make_hash(crle->c_strbkts, 954 HASH_STR, 0)) == NULL) 955 return (1); 956 crle->c_dirnum = 1; 957 } 958 959 if (crle->c_flags & CRLE_VERBOSE) 960 (void) printf(MSG_INTL(MSG_DIA_INSPECT), name); 961 962 /* 963 * Determine whether the name exists. 964 */ 965 if ((noexist = stat(name, &status)) != 0) { 966 if (errno != ENOENT) { 967 int err = errno; 968 (void) fprintf(stderr, MSG_INTL(MSG_SYS_STAT), 969 crle->c_name, name, strerror(err)); 970 return (1); 971 } else { 972 /* 973 * If we've been asked to create an alternative object 974 * assume the object is a file and create a valid 975 * alternative entry. This allows the creation of 976 * alternatives for files that might not yet be 977 * installed. 978 * 979 * Otherwise we have no idea whether the name specified 980 * is a file or directory, so we assume a directory and 981 * establish an object descriptor to mark this as 982 * non-existent. This allows us to mark things like 983 * platform specific directories as non-existent. 984 */ 985 if ((flags & (RTC_OBJ_DUMP | RTC_OBJ_ALTER)) != 986 RTC_OBJ_ALTER) { 987 if ((ent = enternoexistdir(crle, name)) == NULL) 988 return (1); 989 ent->e_flags |= flags; 990 return (0); 991 } 992 } 993 } 994 995 /* 996 * Determine whether we're dealing with a directory or a file. 997 */ 998 if ((noexist == 0) && ((status.st_mode & S_IFMT) == S_IFDIR)) { 999 /* 1000 * Process the directory name to collect its shared objects into 1001 * the configuration file. 1002 */ 1003 if (inspect_dir(crle, name, nflags, &status)) 1004 return (1); 1005 1006 ent = get_hash(crle->c_strtbl, (Addr)name, 0, HASH_FND_ENT); 1007 ent->e_flags |= flags; 1008 return (0); 1009 } 1010 1011 /* 1012 * If this isn't a regular file we might as well bail now. Note that 1013 * even if it is, we might still reject the file if it's not ELF later 1014 * in inspect_file(). 1015 */ 1016 if ((noexist == 0) && ((status.st_mode & S_IFMT) != S_IFREG)) { 1017 (void) fprintf(stderr, MSG_INTL(MSG_GEN_INVFILE), crle->c_name, 1018 name); 1019 return (1); 1020 } 1021 1022 /* 1023 * Break the pathname into directory and filename components. 1024 */ 1025 if ((file = strrchr(name, '/')) == NULL) { 1026 dir = MSG_ORIG(MSG_DIR_DOT); 1027 (void) strcpy(_name, MSG_ORIG(MSG_PTH_DOT)); 1028 (void) strcpy(&_name[MSG_PTH_DOT_SIZE], name); 1029 name = (const char *)_name; 1030 file = (const char *)&_name[MSG_PTH_DOT_SIZE]; 1031 } else { 1032 size_t off = file - name; 1033 1034 if (file == name) { 1035 dir = MSG_ORIG(MSG_DIR_ROOT); 1036 } else { 1037 (void) strncpy(_dir, name, off); 1038 _dir[off] = '\0'; 1039 dir = (const char *)_dir; 1040 } 1041 file++; 1042 } 1043 1044 /* 1045 * Determine whether we've already visited this directory and if not 1046 * create it. 1047 */ 1048 if ((ent = get_hash(crle->c_strtbl, 1049 (Addr)dir, 0, HASH_FND_ENT)) == NULL) { 1050 struct stat _status; 1051 1052 if (stat(dir, &_status) != 0) { 1053 if (errno != ENOENT) { 1054 int err = errno; 1055 (void) fprintf(stderr, MSG_INTL(MSG_SYS_STAT), 1056 crle->c_name, name, strerror(err)); 1057 return (1); 1058 } else { 1059 /* 1060 * Note that this directory will be tagged as 1061 * having an alternative - not that the 1062 * directory does, but supposedly it contains 1063 * a file that does. 1064 */ 1065 if ((ent = enternoexistdir(crle, dir)) == NULL) 1066 return (1); 1067 ent->e_flags |= nflags; 1068 } 1069 } else { 1070 if ((ent = enterdir(crle, dir, nflags, 1071 &_status)) == NULL) 1072 return (1); 1073 } 1074 } 1075 1076 /* 1077 * Regardless of whether we've already processed this file (say from 1078 * an RTC_OBJ_ALLENTS which we could determine from the above), continue 1079 * to inspect the file. It may require alternatives or something that 1080 * hadn't be specified from the directory entry. 1081 */ 1082 if (noexist) { 1083 if ((ent = enternoexistfile(crle, name, file, ent)) == NULL) 1084 return (1); 1085 ent->e_flags |= nflags; 1086 if (enteralt(crle, name, file, flags, ent->e_obj) == 0) 1087 return (1); 1088 } else { 1089 if (inspect_file(crle, name, file, nflags, ent, &status, 1)) 1090 return (1); 1091 } 1092 1093 /* 1094 * Make sure to propagate any RTC_OBJ_CMDLINE flag. 1095 */ 1096 if (ent = get_hash(crle->c_strtbl, (Addr)name, 0, HASH_FND_ENT)) 1097 ent->e_flags |= (flags & RTC_OBJ_CMDLINE); 1098 1099 return (0); 1100 } 1101