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