xref: /titanic_41/usr/src/cmd/sgs/crle/common/inspect.c (revision 45916cd2fec6e79bca5dee0421bd39e3c2910d1e)
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