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