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
enteralt(Crle_desc * crle,const char * path,const char * file,Half flags,Hash_obj * obj)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 *
enterino(Crle_desc * crle,const char * name,struct stat * status,Half flags)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
_enterdir(Crle_desc * crle,const char * dir,Hash_ent * ent,Hash_obj * obj)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 *
enterdir(Crle_desc * crle,const char * odir,Half flags,struct stat * status)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 *
enternoexistdir(Crle_desc * crle,const char * dir)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
_enterfile(Crle_desc * crle,const char * file,int off,Hash_ent * fent,Hash_ent * rent,Hash_ent * dent,Hash_obj * obj)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 *
enternoexistfile(Crle_desc * crle,const char * path,const char * file,Hash_ent * dent)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 *
enterfile(Crle_desc * crle,const char * opath,const char * ofile,Half flags,Hash_ent * odent,struct stat * status)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
inspect_file(Crle_desc * crle,const char * path,const char * file,Half flags,Hash_ent * dent,struct stat * status,int error)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
inspect_dir(Crle_desc * crle,const char * name,Half flags,struct stat * status)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
inspect(Crle_desc * crle,const char * name,Half flags)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