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 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 #pragma ident "%Z%%M% %I% %E% SMI" 26 27 /* 28 * Routines to add file and directory entries into the internal configuration 29 * information. This information is maintained in a number of hash tables which 30 * after completion of input file processing will be processed and written to 31 * the output configuration file. 32 * 33 * Each hash table is defined via a Hash_tbl structure. These are organized: 34 * 35 * c_strtbl contains a hash entry for every file, directory, pathname and 36 * alternative path (dldump(3dl) image) processed. 37 * c_strsize and c_objnum maintain the size and count of the 38 * strings added to this table and are used to size the output 39 * configuration file. 40 * 41 * c_inotbls contains a list of inode hash tables. Each element of the list 42 * identifies a unique device. Thus, for each file processed its 43 * st_dev and st_ino are used to assign its entry to the correct 44 * hash table. 45 * 46 * Each directory processed is assigned a unique id (c_dirnum) 47 * which insures each file also becomes uniquely identified. 48 * 49 * All file and directory additions come through the inspect() entry point. 50 */ 51 52 #include <sys/types.h> 53 #include <sys/stat.h> 54 #include <fcntl.h> 55 #include <dirent.h> 56 #include <_libelf.h> 57 #include <errno.h> 58 #include <stdio.h> 59 #include <string.h> 60 #include <unistd.h> 61 #include <limits.h> 62 #include "machdep.h" 63 #include "sgs.h" 64 #include "rtc.h" 65 #include "_crle.h" 66 #include "msg.h" 67 68 /* 69 * Add an alternative pathname for an object. Although a configuration file 70 * may contain several pathnames that resolve to the same real file, there can 71 * only be one real file. Consequently, there can only be one alternative. 72 * For multiple pathnames that resolve to the same real file, multiple alter- 73 * natives may be specified. Always take the alternative for the real file 74 * over any others. 75 */ 76 static int 77 enteralt(Crle_desc * crle, const char *path, const char *file, Half flags, 78 Hash_obj * obj) 79 { 80 const char *fmt; 81 char alter[PATH_MAX]; 82 size_t altsz; 83 84 if (obj->o_alter) { 85 /* 86 * If an alternative has already been captured, only override 87 * it if the specified file is the real file. 88 */ 89 if (strcmp(path, obj->o_path)) 90 return (1); 91 } 92 93 /* 94 * Create an alternative pathname from the file and object destination 95 * directory. If we're dumping an alternative don't allow it to 96 * override the original. 97 */ 98 if (flags & RTC_OBJ_DUMP) { 99 char _alter[PATH_MAX]; 100 101 (void) strcpy(_alter, crle->c_objdir); 102 (void) realpath(_alter, _alter); 103 (void) snprintf(alter, PATH_MAX, MSG_ORIG(MSG_FMT_PATH), 104 _alter, file); 105 if (strcmp(alter, obj->o_path) == 0) { 106 (void) printf(MSG_INTL(MSG_ARG_ALT), crle->c_name, 107 obj->o_path); 108 return (0); 109 } 110 obj->o_flags |= RTC_OBJ_DUMP; 111 } else 112 (void) snprintf(alter, PATH_MAX, MSG_ORIG(MSG_FMT_PATH), 113 crle->c_objdir, file); 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 * Allocate the new alternative and update the string table size. 128 */ 129 altsz = strlen(alter) + 1; 130 if ((obj->o_alter = malloc(altsz)) == 0) 131 return (0); 132 (void) strcpy(obj->o_alter, alter); 133 134 crle->c_strsize += altsz; 135 136 if (crle->c_flags & CRLE_VERBOSE) 137 (void) printf(fmt, alter, obj->o_path); 138 139 return (1); 140 } 141 142 143 /* 144 * Establish an inode hash entry, this is unique for each dev hash table, and 145 * establishes the unique object descriptor. 146 */ 147 static Hash_ent * 148 enterino(Crle_desc * crle, const char *name, struct stat *status, Half flags) 149 { 150 Hash_ent * ent; 151 Hash_obj * obj; 152 Hash_tbl * tbl; 153 Listnode * lnp = 0; 154 Addr ino = (Addr)status->st_ino; 155 ulong_t dev = status->st_dev; 156 Lword info; 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 (LIST_TRAVERSE(&crle->c_inotbls, lnp, tbl)) { 172 if (tbl->t_ident == dev) 173 break; 174 } 175 if (lnp == 0) { 176 if ((tbl = make_hash(crle->c_inobkts, HASH_INT, dev)) == 0) 177 return (0); 178 if (list_append(&crle->c_inotbls, tbl) == 0) 179 return (0); 180 } 181 182 /* 183 * Reuse or add this new object to the inode hash table. 184 */ 185 if ((ent = get_hash(tbl, ino, 0, (HASH_FND_ENT | HASH_ADD_ENT))) == 0) 186 return (0); 187 188 /* 189 * If an object descriptor doesn't yet exist create one. 190 */ 191 if ((obj = ent->e_obj) == 0) { 192 if ((obj = calloc(sizeof (Hash_obj), 1)) == 0) 193 return (0); 194 obj->o_tbl = tbl; 195 obj->o_flags = flags; 196 obj->o_info = info; 197 198 /* 199 * Reallocate the objects name, as it might have been composed 200 * and passed to us on the stack. 201 */ 202 if ((obj->o_path = strdup(name)) == 0) 203 return (0); 204 205 /* 206 * Assign this object to the original ino hash entry. 207 */ 208 ent->e_obj = obj; 209 } 210 return (ent); 211 } 212 213 214 /* 215 * Basic directory entry, establishes entry information, updated global counts 216 * and provides any diagnostics. 217 */ 218 static int 219 _enterdir(Crle_desc * crle, const char *dir, Hash_ent * ent, Hash_obj * obj) 220 { 221 size_t size = strlen(dir) + 1; 222 char *ndir; 223 224 /* 225 * Establish this hash entries key (which is the directory name itself), 226 * assign the next available directory number, and its object. 227 */ 228 if ((ndir = malloc(size)) == 0) 229 return (0); 230 (void) strcpy(ndir, dir); 231 232 ent->e_key = (Addr)ndir; 233 ent->e_id = crle->c_dirnum++; 234 ent->e_obj = obj; 235 236 /* 237 * Update string table information. We add a dummy filename for each 238 * real directory so as to have a null terminated file table array for 239 * this directory. 240 */ 241 crle->c_strsize += size; 242 crle->c_hashstrnum++; 243 crle->c_filenum++; 244 245 /* 246 * Provide any diagnostics. 247 */ 248 if (crle->c_flags & CRLE_VERBOSE) { 249 const char *fmt; 250 251 if (obj->o_flags & RTC_OBJ_NOEXIST) 252 fmt = MSG_INTL(MSG_DIA_NOEXIST); 253 else 254 fmt = MSG_INTL(MSG_DIA_DIR); 255 256 (void) printf(fmt, ent->e_id, dir); 257 } 258 return (1); 259 } 260 261 262 /* 263 * Establish a string hash entry for a directory. 264 */ 265 static Hash_ent * 266 enterdir(Crle_desc * crle, const char *odir, Half flags, struct stat *status) 267 { 268 Hash_tbl * stbl = crle->c_strtbl; 269 Hash_ent * ent; 270 Hash_obj * obj; 271 char rdir[PATH_MAX], * ndir; 272 273 /* 274 * Establish the directories real name, this is the name that will be 275 * recorded in the object identifier. 276 */ 277 if (realpath(odir, rdir) == 0) 278 return (0); 279 280 if (strcmp(odir, rdir)) 281 ndir = rdir; 282 else 283 ndir = (char *)odir; 284 285 /* 286 * If we're not dealing with an all-entries directory (i.e., we're 287 * recording this directory because of its explicitly specified 288 * filename) leave off any filename specific attributes. 289 */ 290 if ((flags & RTC_OBJ_ALLENTS) == 0) 291 flags &= ~(RTC_OBJ_ALTER | RTC_OBJ_DUMP | RTC_OBJ_GROUP); 292 flags |= RTC_OBJ_DIRENT; 293 294 /* 295 * Establish a inode table entry, and the objects unique descriptor. 296 */ 297 if ((ent = enterino(crle, ndir, status, flags)) == 0) 298 return (0); 299 obj = ent->e_obj; 300 301 /* 302 * Create a string table entry for the real directory. 303 */ 304 if ((ent = get_hash(stbl, (Addr)ndir, 0, 305 (HASH_FND_ENT | HASH_ADD_ENT))) == 0) 306 return (0); 307 308 /* 309 * If this is a new entry reassign the directory name and assign a 310 * unique directory id. 311 */ 312 if (ent->e_id == 0) { 313 if (_enterdir(crle, ndir, ent, obj) == 0) 314 return (0); 315 } 316 317 /* 318 * If the directory name supplied is different than the real name we've 319 * just entered, continue to create an entry for it. 320 */ 321 if (ndir == odir) 322 return (ent); 323 324 /* 325 * Create a string table entry for this real directory. 326 */ 327 if ((ent = get_hash(stbl, (Addr)odir, 0, 328 (HASH_FND_ENT | HASH_ADD_ENT))) == 0) 329 return (0); 330 331 /* 332 * If this is a new entry reassign the directory name and assign a 333 * unique directory id. 334 */ 335 if (ent->e_id == 0) { 336 if (_enterdir(crle, odir, ent, obj) == 0) 337 return (0); 338 } 339 340 return (ent); 341 } 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))) == 0) 358 return (0); 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)) == 0) 368 return (0); 369 obj->o_flags = (RTC_OBJ_NOEXIST | RTC_OBJ_DIRENT); 370 371 if (_enterdir(crle, dir, ent, obj) == 0) 372 return (0); 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)) == 0) 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))) == 0) 458 return (0); 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)) == 0) 466 return (0); 467 obj->o_flags = RTC_OBJ_NOEXIST; 468 469 if (_enterfile(crle, path, 0, rent, 0, dent, obj) == 0) 470 return (0); 471 } 472 obj = rent->e_obj; 473 if ((obj->o_path = strdup(path)) == 0) 474 return (0); 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))) == 0) 489 return (0); 490 491 if (ent->e_id == 0) { 492 if (_enterfile(crle, file, off, ent, rent, dent, obj) == 0) 493 return (0); 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) == 0) 518 return (0); 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)) == 0) 542 return (0); 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)) == 0) 553 return (0); 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))) == 0) 561 return (0); 562 if (rent->e_id == 0) { 563 if (_enterfile(crle, npath, 0, rent, 0, ndent, obj) == 0) 564 return (0); 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))) == 0) 590 return (0); 591 if (ent->e_id == 0) { 592 if (_enterfile(crle, nfile, off, ent, rent, ndent, obj) == 0) 593 return (0); 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)) == 0) 608 return (0); 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))) == 0) 616 return (0); 617 if (rent->e_id == 0) { 618 if (_enterfile(crle, opath, 0, rent, 0, odent, obj) == 0) 619 return (0); 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))) == 0) 635 return (0); 636 if (ent->e_id == 0) { 637 if (_enterfile(crle, ofile, off, ent, rent, odent, obj) == 0) 638 return (0); 639 } 640 641 return (ent); 642 } 643 644 645 /* 646 * Add a file to configuration information. 647 */ 648 static int 649 inspect_file(Crle_desc * crle, const char *path, const char *file, Half flags, 650 Hash_ent * dent, struct stat *status, int error) 651 { 652 Hash_ent * ent; 653 Hash_obj * obj; 654 int fd; 655 Elf * elf; 656 GElf_Ehdr ehdr; 657 GElf_Xword dyflags = 0; 658 Listnode * lnp; 659 Hash_tbl * tbl; 660 Addr ino = (Addr)status->st_ino; 661 662 /* 663 * Determine whether this file (inode) has already been processed. 664 */ 665 for (LIST_TRAVERSE(&crle->c_inotbls, lnp, tbl)) { 666 if (tbl->t_ident != status->st_dev) 667 continue; 668 669 if ((ent = get_hash(tbl, ino, 0, HASH_FND_ENT)) == 0) 670 break; 671 672 /* 673 * This files inode object does exist, make sure it has a file 674 * entry for this directory. 675 */ 676 if ((ent = enterfile(crle, path, file, flags, dent, 677 status)) == 0) 678 return (error); 679 obj = ent->e_obj; 680 681 /* 682 * If an alternative has been asked for, and one has not yet 683 * been established, create one. 684 */ 685 if ((flags & RTC_OBJ_ALTER) && 686 ((obj->o_flags & RTC_OBJ_NOALTER) == 0)) { 687 if (enteralt(crle, path, file, flags, obj) == 0) 688 return (error); 689 } 690 return (0); 691 } 692 693 /* 694 * This is a new file, determine if it's a valid ELF file. 695 */ 696 if ((fd = open(path, O_RDONLY, 0)) == -1) { 697 if (error) { 698 int err = errno; 699 (void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN), 700 crle->c_name, path, strerror(err)); 701 } 702 return (error); 703 } 704 705 /* 706 * Obtain an ELF descriptor and determine if we have a shared object. 707 */ 708 if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) { 709 if (error) 710 (void) fprintf(stderr, MSG_INTL(MSG_ELF_BEGIN), 711 crle->c_name, path, elf_errmsg(-1)); 712 (void) close(fd); 713 return (error); 714 } 715 if ((elf_kind(elf) != ELF_K_ELF) || 716 (gelf_getehdr(elf, &ehdr) == NULL) || 717 (!((ehdr.e_type == ET_EXEC) || (ehdr.e_type == ET_DYN))) || 718 (!((ehdr.e_ident[EI_CLASS] == M_CLASS) || 719 (ehdr.e_machine == M_MACH)))) { 720 if (error) 721 (void) fprintf(stderr, MSG_INTL(MSG_ELF_TYPE), 722 crle->c_name, path); 723 (void) close(fd); 724 (void) elf_end(elf); 725 return (error); 726 } 727 728 (void) close(fd); 729 730 /* 731 * If we're generating alternative objects find this objects DT_FLAGS 732 * to insure it isn't marked as non-dumpable (libdl.so.1 falls into 733 * this category). 734 */ 735 if (flags & RTC_OBJ_DUMP) 736 dyflags = _gelf_getdyndtflags_1(elf); 737 738 /* 739 * Dynamic executables can be examined to determine their dependencies, 740 * dldump(3dl) their dependencies, and may even be dldump(3dl)'ed 741 * themselves. 742 * 743 * If we come across an executable while searching a directory 744 * (error == 0) it is ignored. 745 */ 746 if (ehdr.e_type == ET_EXEC) { 747 if (error == 0) { 748 (void) elf_end(elf); 749 return (0); 750 } 751 752 /* 753 * If we're not dumping the application itself, or we've not 754 * asked to gather its dependencies then its rather useless. 755 */ 756 if ((flags & (RTC_OBJ_GROUP | RTC_OBJ_DUMP)) == 0) { 757 (void) fprintf(stderr, MSG_INTL(MSG_GEN_INVFILE), 758 crle->c_name, path); 759 (void) elf_end(elf); 760 return (error); 761 } 762 763 /* 764 * If we're dumping the application under RTLD_REL_EXEC then the 765 * configuration file becomes specific to this application, so 766 * make sure we haven't been here before. 767 */ 768 if (crle->c_app && (flags & RTC_OBJ_DUMP) && 769 (crle->c_dlflags & RTLD_REL_EXEC)) { 770 (void) fprintf(stderr, MSG_INTL(MSG_ARG_MODE), 771 crle->c_name, crle->c_app, path); 772 (void) elf_end(elf); 773 return (error); 774 } 775 } 776 777 /* 778 * Enter the file in the string hash table. 779 */ 780 if ((ent = enterfile(crle, path, file, flags, dent, status)) == 0) { 781 (void) elf_end(elf); 782 return (error); 783 } 784 obj = ent->e_obj; 785 786 if (flags & RTC_OBJ_ALTER) { 787 /* 788 * If this object is marked as non-dumpable make sure we don't 789 * create a dldump(3dl) alternative. A user requested 790 * alternative is acceptable. 791 */ 792 if ((flags & RTC_OBJ_DUMP) && (dyflags & DF_1_NODUMP)) { 793 obj->o_flags |= RTC_OBJ_NOALTER; 794 obj->o_flags &= ~(RTC_OBJ_ALTER | RTC_OBJ_DUMP); 795 } else { 796 if (enteralt(crle, path, file, flags, obj) == 0) { 797 (void) elf_end(elf); 798 return (error); 799 } 800 } 801 } 802 803 /* 804 * Executables are recorded in the configuration file either to allow 805 * for the configuration files update, or may indicate that the 806 * configuration file is specific to their use. 807 */ 808 if (ehdr.e_type == ET_EXEC) { 809 obj->o_flags |= RTC_OBJ_EXEC; 810 811 if ((flags & RTC_OBJ_DUMP) && 812 (crle->c_dlflags & RTLD_REL_EXEC)) { 813 /* 814 * Get the reallocated pathname rather than using the 815 * original (the original might be from an existing 816 * configuration file being updated, in which case the 817 * pointer will be unmapped before we get to use it). 818 */ 819 ent = get_hash(crle->c_strtbl, (Addr)path, 0, 820 HASH_FND_ENT); 821 822 obj->o_flags |= RTC_OBJ_APP; 823 crle->c_app = (char *)ent->e_key; 824 } 825 } 826 827 /* 828 * If we've been asked to process this object as a group determine its 829 * dependencies. 830 */ 831 if (flags & RTC_OBJ_GROUP) { 832 if (depend(crle, path, flags, &ehdr)) { 833 (void) elf_end(elf); 834 return (error); 835 } 836 } 837 838 (void) elf_end(elf); 839 return (0); 840 } 841 842 843 /* 844 * Add a directory to configuration information. 845 */ 846 static int 847 inspect_dir(Crle_desc * crle, const char *name, Half flags, struct stat *status) 848 { 849 Hash_tbl * stbl = crle->c_strtbl; 850 DIR * dir; 851 struct dirent *dirent; 852 Hash_ent * ent; 853 int error = 0; 854 struct stat _status; 855 char path[PATH_MAX], * dst; 856 const char *src; 857 858 /* 859 * Determine whether we've already visited this directory to process 860 * all its entries. 861 */ 862 if ((ent = get_hash(stbl, (Addr)name, 0, HASH_FND_ENT)) != 0) { 863 if (ent->e_obj->o_flags & RTC_OBJ_ALLENTS) 864 return (0); 865 } else { 866 /* 867 * Create a directory hash entry. 868 */ 869 if ((ent = enterdir(crle, name, (flags | RTC_OBJ_ALLENTS), 870 status)) == 0) 871 return (1); 872 } 873 ent->e_obj->o_flags |= RTC_OBJ_ALLENTS; 874 875 /* 876 * Establish the pathname buffer. 877 */ 878 for (dst = path, dst--, src = name; *src; src++) 879 *++dst = *src; 880 if (*dst++ != '/') 881 *dst++ = '/'; 882 883 /* 884 * Access the directory in preparation for reading its entries. 885 */ 886 if ((dir = opendir(name)) == 0) 887 return (1); 888 889 /* 890 * Read each entry from the directory looking for ELF files. 891 */ 892 while ((dirent = readdir(dir)) != NULL) { 893 const char *file = dirent->d_name; 894 char *_dst; 895 896 /* 897 * Ignore "." and ".." entries. 898 */ 899 if ((file[0] == '.') && ((file[1] == '\0') || 900 ((file[1] == '.') && (file[2] == '\0')))) 901 continue; 902 903 /* 904 * Complete full pathname, and reassign file to the new path. 905 */ 906 for (_dst = dst, src = file, file = dst; *src; _dst++, src++) 907 *_dst = *src; 908 *_dst = '\0'; 909 910 if (stat(path, &_status) == -1) 911 continue; 912 913 if ((_status.st_mode & S_IFMT) != S_IFREG) 914 continue; 915 916 if (inspect_file(crle, path, file, flags, ent, &_status, 0)) { 917 error = 1; 918 break; 919 } 920 } 921 return (error); 922 } 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)) == 0) 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)) == 0) 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, '/')) == 0) { 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, (Addr)dir, 0, HASH_FND_ENT)) == 0) { 1049 struct stat _status; 1050 1051 if (stat(dir, &_status) != 0) { 1052 if (errno != ENOENT) { 1053 int err = errno; 1054 (void) fprintf(stderr, MSG_INTL(MSG_SYS_STAT), 1055 crle->c_name, name, strerror(err)); 1056 return (1); 1057 } else { 1058 /* 1059 * Note that this directory will be tagged as 1060 * having an alternative - not that the 1061 * directory does, but supposedly it contains 1062 * a file that does. 1063 */ 1064 if ((ent = enternoexistdir(crle, dir)) == 0) 1065 return (1); 1066 ent->e_flags |= nflags; 1067 } 1068 } else { 1069 if ((ent = enterdir(crle, dir, nflags, &_status)) == 0) 1070 return (1); 1071 } 1072 } 1073 1074 /* 1075 * Regardless of whether we've already processed this file (say from 1076 * an RTC_OBJ_ALLENTS which we could determine from the above), continue 1077 * to inspect the file. It may require alternatives or something that 1078 * hadn't be specified from the directory entry. 1079 */ 1080 if (noexist) { 1081 if ((ent = enternoexistfile(crle, name, file, ent)) == 0) 1082 return (1); 1083 ent->e_flags |= nflags; 1084 if (enteralt(crle, name, file, flags, ent->e_obj) == 0) 1085 return (1); 1086 } else { 1087 if (inspect_file(crle, name, file, nflags, ent, &status, 1)) 1088 return (1); 1089 } 1090 1091 /* 1092 * Make sure to propagate any RTC_OBJ_CMDLINE flag. 1093 */ 1094 if (ent = get_hash(crle->c_strtbl, (Addr)name, 0, HASH_FND_ENT)) 1095 ent->e_flags |= (flags & RTC_OBJ_CMDLINE); 1096 1097 return (0); 1098 } 1099