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