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(1, sizeof (Hash_obj))) == 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(1, sizeof (Hash_obj))) == 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(1, sizeof (Hash_obj))) == 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 nfile = strrchr(npath, '/'); 526 if (nfile != NULL) 527 nfile++; 528 else 529 nfile = npath; 530 531 /* 532 * Determine if the real pathname has a different directory to 533 * the original passed to us. 534 */ 535 size = nfile - npath; 536 if (strncmp(opath, npath, size)) { 537 char _npath[PATH_MAX]; 538 struct stat _status; 539 540 (void) strncpy(_npath, npath, size); 541 _npath[size - 1] = '\0'; 542 543 (void) stat(_npath, &_status); 544 if ((ndent = enterdir(crle, _npath, flags, 545 &_status)) == NULL) 546 return (NULL); 547 } 548 } else { 549 npath = (char *)opath; 550 nfile = (char *)ofile; 551 } 552 553 /* 554 * Establish an inode table entry, and the objects unique descriptor. 555 */ 556 if ((ent = enterino(crle, npath, status, flags)) == NULL) 557 return (NULL); 558 obj = ent->e_obj; 559 560 /* 561 * Create a string table entry for the full filename. 562 */ 563 if ((rent = get_hash(stbl, (Addr)npath, 0, 564 (HASH_FND_ENT | HASH_ADD_ENT))) == NULL) 565 return (NULL); 566 if (rent->e_id == 0) { 567 if (_enterfile(crle, npath, 0, rent, 0, ndent, obj) == 0) 568 return (NULL); 569 } 570 571 /* 572 * Identify this entry and its directory as real paths. If dldump(3dl) 573 * processing is required this flag is checked, as we only need to dump 574 * the real pathname. Many other objects may point to the same 575 * alternative, but only one needs to be dumped. In addition, during 576 * ld.so.1 validation, only this directory and file need be checked. 577 */ 578 rent->e_flags |= RTC_OBJ_REALPTH; 579 ndent->e_flags |= RTC_OBJ_REALPTH; 580 581 /* 582 * Express the filename in terms of the full pathname. By reusing the 583 * name within the full filename we can reduce the overall string table 584 * size in the output configuration file. 585 */ 586 off = nfile - npath; 587 nfile = (char *)rent->e_key + off; 588 589 /* 590 * Create a entry for the individual file within this directory. 591 */ 592 if ((ent = get_hash(stbl, (Addr)nfile, ndent->e_id, 593 (HASH_FND_ENT | HASH_ADD_ENT))) == NULL) 594 return (NULL); 595 if (ent->e_id == 0) { 596 if (_enterfile(crle, nfile, off, ent, rent, ndent, obj) == 0) 597 return (NULL); 598 } 599 600 /* 601 * If the original path name is not equivalent to the real path name, 602 * then we had an alias (typically it's a symlink). Add the path name 603 * to the string hash table and reference the object data structure. 604 */ 605 if (nfile == ofile) 606 return (ent); 607 608 /* 609 * Establish an inode table entry, and the objects unique descriptor. 610 */ 611 if ((ent = enterino(crle, opath, status, 0)) == NULL) 612 return (NULL); 613 obj = ent->e_obj; 614 615 /* 616 * Create a string table entry for the full filename. 617 */ 618 if ((rent = get_hash(stbl, (Addr)opath, 0, 619 (HASH_FND_ENT | HASH_ADD_ENT))) == NULL) 620 return (NULL); 621 if (rent->e_id == 0) { 622 if (_enterfile(crle, opath, 0, rent, 0, odent, obj) == 0) 623 return (NULL); 624 } 625 626 /* 627 * Express the filename in terms of the full pathname. By reusing the 628 * name within the full filename we can reduce the overall string table 629 * size in the output configuration file. 630 */ 631 off = ofile - opath; 632 ofile = (char *)rent->e_key + off; 633 634 /* 635 * Create a entry for the individual file within this directory. 636 */ 637 if ((ent = get_hash(stbl, (Addr)ofile, odent->e_id, 638 (HASH_FND_ENT | HASH_ADD_ENT))) == NULL) 639 return (NULL); 640 if (ent->e_id == 0) { 641 if (_enterfile(crle, ofile, off, ent, rent, odent, obj) == 0) 642 return (NULL); 643 } 644 645 return (ent); 646 } 647 648 /* 649 * Add a file to configuration information. 650 */ 651 static int 652 inspect_file(Crle_desc *crle, const char *path, const char *file, Half flags, 653 Hash_ent *dent, struct stat *status, int error) 654 { 655 Hash_ent *ent; 656 Hash_obj *obj; 657 int fd; 658 Elf *elf; 659 GElf_Ehdr ehdr; 660 GElf_Xword dyflags = 0; 661 Aliste idx; 662 Hash_tbl *tbl; 663 Addr ino = (Addr)status->st_ino; 664 665 /* 666 * Determine whether this file (inode) has already been processed. 667 */ 668 for (APLIST_TRAVERSE(crle->c_inotbls, idx, tbl)) { 669 if (tbl->t_ident != status->st_dev) 670 continue; 671 672 if ((ent = get_hash(tbl, ino, 0, HASH_FND_ENT)) == NULL) 673 break; 674 675 /* 676 * This files inode object does exist, make sure it has a file 677 * entry for this directory. 678 */ 679 if ((ent = enterfile(crle, path, file, flags, dent, 680 status)) == NULL) 681 return (error); 682 obj = ent->e_obj; 683 684 /* 685 * If an alternative has been asked for, and one has not yet 686 * been established, create one. 687 */ 688 if ((flags & RTC_OBJ_ALTER) && 689 ((obj->o_flags & RTC_OBJ_NOALTER) == 0)) { 690 if (enteralt(crle, path, file, flags, obj) == 0) 691 return (error); 692 } 693 return (0); 694 } 695 696 /* 697 * This is a new file, determine if it's a valid ELF file. 698 */ 699 if ((fd = open(path, O_RDONLY, 0)) == -1) { 700 if (error) { 701 int err = errno; 702 (void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN), 703 crle->c_name, path, strerror(err)); 704 } 705 return (error); 706 } 707 708 /* 709 * Obtain an ELF descriptor and determine if we have a shared object. 710 */ 711 if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { 712 if (error) 713 (void) fprintf(stderr, MSG_INTL(MSG_ELF_BEGIN), 714 crle->c_name, path, elf_errmsg(-1)); 715 (void) close(fd); 716 return (error); 717 } 718 if ((elf_kind(elf) != ELF_K_ELF) || 719 (gelf_getehdr(elf, &ehdr) == NULL) || 720 (!((ehdr.e_type == ET_EXEC) || (ehdr.e_type == ET_DYN))) || 721 (!((ehdr.e_ident[EI_CLASS] == M_CLASS) || 722 (ehdr.e_machine == M_MACH)))) { 723 if (error) 724 (void) fprintf(stderr, MSG_INTL(MSG_ELF_TYPE), 725 crle->c_name, path); 726 (void) close(fd); 727 (void) elf_end(elf); 728 return (error); 729 } 730 731 (void) close(fd); 732 733 /* 734 * If we're generating alternative objects find this objects DT_FLAGS 735 * to insure it isn't marked as non-dumpable (libdl.so.1 falls into 736 * this category). 737 */ 738 if (flags & RTC_OBJ_DUMP) 739 dyflags = _gelf_getdyndtflags_1(elf); 740 741 /* 742 * Dynamic executables can be examined to determine their dependencies, 743 * dldump(3dl) their dependencies, and may even be dldump(3dl)'ed 744 * themselves. 745 * 746 * If we come across an executable while searching a directory 747 * (error == 0) it is ignored. 748 */ 749 if (ehdr.e_type == ET_EXEC) { 750 if (error == 0) { 751 (void) elf_end(elf); 752 return (0); 753 } 754 755 /* 756 * If we're not dumping the application itself, or we've not 757 * asked to gather its dependencies then its rather useless. 758 */ 759 if ((flags & (RTC_OBJ_GROUP | RTC_OBJ_DUMP)) == 0) { 760 (void) fprintf(stderr, MSG_INTL(MSG_GEN_INVFILE), 761 crle->c_name, path); 762 (void) elf_end(elf); 763 return (error); 764 } 765 766 /* 767 * If we're dumping the application under RTLD_REL_EXEC then the 768 * configuration file becomes specific to this application, so 769 * make sure we haven't been here before. 770 */ 771 if (crle->c_app && (flags & RTC_OBJ_DUMP) && 772 (crle->c_dlflags & RTLD_REL_EXEC)) { 773 (void) fprintf(stderr, MSG_INTL(MSG_ARG_MODE), 774 crle->c_name, crle->c_app, path); 775 (void) elf_end(elf); 776 return (error); 777 } 778 } 779 780 /* 781 * Enter the file in the string hash table. 782 */ 783 if ((ent = enterfile(crle, path, file, flags, dent, status)) == NULL) { 784 (void) elf_end(elf); 785 return (error); 786 } 787 obj = ent->e_obj; 788 789 if (flags & RTC_OBJ_ALTER) { 790 /* 791 * If this object is marked as non-dumpable make sure we don't 792 * create a dldump(3dl) alternative. A user requested 793 * alternative is acceptable. 794 */ 795 if ((flags & RTC_OBJ_DUMP) && (dyflags & DF_1_NODUMP)) { 796 obj->o_flags |= RTC_OBJ_NOALTER; 797 obj->o_flags &= ~(RTC_OBJ_ALTER | RTC_OBJ_DUMP); 798 } else { 799 if (enteralt(crle, path, file, flags, obj) == 0) { 800 (void) elf_end(elf); 801 return (error); 802 } 803 } 804 } 805 806 /* 807 * Executables are recorded in the configuration file either to allow 808 * for the configuration files update, or may indicate that the 809 * configuration file is specific to their use. 810 */ 811 if (ehdr.e_type == ET_EXEC) { 812 obj->o_flags |= RTC_OBJ_EXEC; 813 814 if ((flags & RTC_OBJ_DUMP) && 815 (crle->c_dlflags & RTLD_REL_EXEC)) { 816 /* 817 * Get the reallocated pathname rather than using the 818 * original (the original might be from an existing 819 * configuration file being updated, in which case the 820 * pointer will be unmapped before we get to use it). 821 */ 822 ent = get_hash(crle->c_strtbl, (Addr)path, 0, 823 HASH_FND_ENT); 824 825 obj->o_flags |= RTC_OBJ_APP; 826 crle->c_app = (char *)ent->e_key; 827 } 828 } 829 830 /* 831 * If we've been asked to process this object as a group determine its 832 * dependencies. 833 */ 834 if (flags & RTC_OBJ_GROUP) { 835 if (depend(crle, path, flags, &ehdr)) { 836 (void) elf_end(elf); 837 return (error); 838 } 839 } 840 841 (void) elf_end(elf); 842 return (0); 843 } 844 845 /* 846 * Add a directory to configuration information. 847 */ 848 static int 849 inspect_dir(Crle_desc *crle, const char *name, Half flags, struct stat *status) 850 { 851 Hash_tbl *stbl = crle->c_strtbl; 852 DIR *dir; 853 struct dirent *dirent; 854 Hash_ent *ent; 855 int error = 0; 856 struct stat _status; 857 char path[PATH_MAX], * dst; 858 const char *src; 859 860 /* 861 * Determine whether we've already visited this directory to process 862 * all its entries. 863 */ 864 if ((ent = get_hash(stbl, (Addr)name, 0, HASH_FND_ENT)) != NULL) { 865 if (ent->e_obj->o_flags & RTC_OBJ_ALLENTS) 866 return (0); 867 } else { 868 /* 869 * Create a directory hash entry. 870 */ 871 if ((ent = enterdir(crle, name, (flags | RTC_OBJ_ALLENTS), 872 status)) == NULL) 873 return (1); 874 } 875 ent->e_obj->o_flags |= RTC_OBJ_ALLENTS; 876 877 /* 878 * Establish the pathname buffer. 879 */ 880 for (dst = path, dst--, src = name; *src; src++) 881 *++dst = *src; 882 if (*dst++ != '/') 883 *dst++ = '/'; 884 885 /* 886 * Access the directory in preparation for reading its entries. 887 */ 888 if ((dir = opendir(name)) == NULL) 889 return (1); 890 891 /* 892 * Read each entry from the directory looking for ELF files. 893 */ 894 while ((dirent = readdir(dir)) != NULL) { 895 const char *file = dirent->d_name; 896 char *_dst; 897 898 /* 899 * Ignore "." and ".." entries. 900 */ 901 if ((file[0] == '.') && ((file[1] == '\0') || 902 ((file[1] == '.') && (file[2] == '\0')))) 903 continue; 904 905 /* 906 * Complete full pathname, and reassign file to the new path. 907 */ 908 for (_dst = dst, src = file, file = dst; *src; _dst++, src++) 909 *_dst = *src; 910 *_dst = '\0'; 911 912 if (stat(path, &_status) == -1) 913 continue; 914 915 if ((_status.st_mode & S_IFMT) != S_IFREG) 916 continue; 917 918 if (inspect_file(crle, path, file, flags, ent, &_status, 0)) { 919 error = 1; 920 break; 921 } 922 } 923 return (error); 924 } 925 926 /* 927 * Inspect a file/dir name. A stat(name) results in the following actions: 928 * 929 * The name doesn't exist: 930 * The name is assummed to be a non-existent directory and a directory 931 * cache entry is created to indicate this. 932 * 933 * The name is a directory: 934 * The directory is searched for appropriate files. 935 * 936 * The name is a file: 937 * The file is processed and added to the cache if appropriate. 938 */ 939 int 940 inspect(Crle_desc *crle, const char *name, Half flags) 941 { 942 Hash_ent *ent; 943 const char *file, *dir; 944 struct stat status; 945 char _name[PATH_MAX], _dir[PATH_MAX]; 946 Half nflags = flags & ~RTC_OBJ_CMDLINE; 947 int noexist; 948 949 /* 950 * If this is the first time through here establish a string table 951 * cache. 952 */ 953 if (crle->c_dirnum == 0) { 954 if ((crle->c_strtbl = make_hash(crle->c_strbkts, 955 HASH_STR, 0)) == NULL) 956 return (1); 957 crle->c_dirnum = 1; 958 } 959 960 if (crle->c_flags & CRLE_VERBOSE) 961 (void) printf(MSG_INTL(MSG_DIA_INSPECT), name); 962 963 /* 964 * Determine whether the name exists. 965 */ 966 if ((noexist = stat(name, &status)) != 0) { 967 if (errno != ENOENT) { 968 int err = errno; 969 (void) fprintf(stderr, MSG_INTL(MSG_SYS_STAT), 970 crle->c_name, name, strerror(err)); 971 return (1); 972 } else { 973 /* 974 * If we've been asked to create an alternative object 975 * assume the object is a file and create a valid 976 * alternative entry. This allows the creation of 977 * alternatives for files that might not yet be 978 * installed. 979 * 980 * Otherwise we have no idea whether the name specified 981 * is a file or directory, so we assume a directory and 982 * establish an object descriptor to mark this as 983 * non-existent. This allows us to mark things like 984 * platform specific directories as non-existent. 985 */ 986 if ((flags & (RTC_OBJ_DUMP | RTC_OBJ_ALTER)) != 987 RTC_OBJ_ALTER) { 988 if ((ent = enternoexistdir(crle, name)) == NULL) 989 return (1); 990 ent->e_flags |= flags; 991 return (0); 992 } 993 } 994 } 995 996 /* 997 * Determine whether we're dealing with a directory or a file. 998 */ 999 if ((noexist == 0) && ((status.st_mode & S_IFMT) == S_IFDIR)) { 1000 /* 1001 * Process the directory name to collect its shared objects into 1002 * the configuration file. 1003 */ 1004 if (inspect_dir(crle, name, nflags, &status)) 1005 return (1); 1006 1007 ent = get_hash(crle->c_strtbl, (Addr)name, 0, HASH_FND_ENT); 1008 ent->e_flags |= flags; 1009 return (0); 1010 } 1011 1012 /* 1013 * If this isn't a regular file we might as well bail now. Note that 1014 * even if it is, we might still reject the file if it's not ELF later 1015 * in inspect_file(). 1016 */ 1017 if ((noexist == 0) && ((status.st_mode & S_IFMT) != S_IFREG)) { 1018 (void) fprintf(stderr, MSG_INTL(MSG_GEN_INVFILE), crle->c_name, 1019 name); 1020 return (1); 1021 } 1022 1023 /* 1024 * Break the pathname into directory and filename components. 1025 */ 1026 if ((file = strrchr(name, '/')) == NULL) { 1027 dir = MSG_ORIG(MSG_DIR_DOT); 1028 (void) strcpy(_name, MSG_ORIG(MSG_PTH_DOT)); 1029 (void) strcpy(&_name[MSG_PTH_DOT_SIZE], name); 1030 name = (const char *)_name; 1031 file = (const char *)&_name[MSG_PTH_DOT_SIZE]; 1032 } else { 1033 size_t off = file - name; 1034 1035 if (file == name) { 1036 dir = MSG_ORIG(MSG_DIR_ROOT); 1037 } else { 1038 (void) strncpy(_dir, name, off); 1039 _dir[off] = '\0'; 1040 dir = (const char *)_dir; 1041 } 1042 file++; 1043 } 1044 1045 /* 1046 * Determine whether we've already visited this directory and if not 1047 * create it. 1048 */ 1049 if ((ent = get_hash(crle->c_strtbl, 1050 (Addr)dir, 0, HASH_FND_ENT)) == NULL) { 1051 struct stat _status; 1052 1053 if (stat(dir, &_status) != 0) { 1054 if (errno != ENOENT) { 1055 int err = errno; 1056 (void) fprintf(stderr, MSG_INTL(MSG_SYS_STAT), 1057 crle->c_name, name, strerror(err)); 1058 return (1); 1059 } else { 1060 /* 1061 * Note that this directory will be tagged as 1062 * having an alternative - not that the 1063 * directory does, but supposedly it contains 1064 * a file that does. 1065 */ 1066 if ((ent = enternoexistdir(crle, dir)) == NULL) 1067 return (1); 1068 ent->e_flags |= nflags; 1069 } 1070 } else { 1071 if ((ent = enterdir(crle, dir, nflags, 1072 &_status)) == NULL) 1073 return (1); 1074 } 1075 } 1076 1077 /* 1078 * Regardless of whether we've already processed this file (say from 1079 * an RTC_OBJ_ALLENTS which we could determine from the above), continue 1080 * to inspect the file. It may require alternatives or something that 1081 * hadn't be specified from the directory entry. 1082 */ 1083 if (noexist) { 1084 if ((ent = enternoexistfile(crle, name, file, ent)) == NULL) 1085 return (1); 1086 ent->e_flags |= nflags; 1087 if (enteralt(crle, name, file, flags, ent->e_obj) == 0) 1088 return (1); 1089 } else { 1090 if (inspect_file(crle, name, file, nflags, ent, &status, 1)) 1091 return (1); 1092 } 1093 1094 /* 1095 * Make sure to propagate any RTC_OBJ_CMDLINE flag. 1096 */ 1097 ent = get_hash(crle->c_strtbl, (Addr)name, 0, HASH_FND_ENT); 1098 if (ent != NULL) 1099 ent->e_flags |= (flags & RTC_OBJ_CMDLINE); 1100 1101 return (0); 1102 } 1103