inspect.c revision 1698:0cf90a9f4e74
1168404Spjd/*
2168404Spjd * CDDL HEADER START
3168404Spjd *
4168404Spjd * The contents of this file are subject to the terms of the
5185029Spjd * Common Development and Distribution License (the "License").
6185029Spjd * You may not use this file except in compliance with the License.
7168404Spjd *
8168404Spjd * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9168404Spjd * or http://www.opensolaris.org/os/licensing.
10168404Spjd * See the License for the specific language governing permissions
11168404Spjd * and limitations under the License.
12168404Spjd *
13168404Spjd * When distributing Covered Code, include this CDDL HEADER in each
14168404Spjd * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15168404Spjd * If applicable, add the following below this CDDL HEADER, with the
16168404Spjd * fields enclosed by brackets "[]" replaced with your own identifying
17168404Spjd * information: Portions Copyright [yyyy] [name of copyright owner]
18168404Spjd *
19168404Spjd * CDDL HEADER END
20168404Spjd */
21168404Spjd/*
22185029Spjd *	Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
23168404Spjd *	Use is subject to license terms.
24168404Spjd */
25168404Spjd#pragma ident	"%Z%%M%	%I%	%E% SMI"
26185029Spjd
27185029Spjd/*
28168404Spjd * Routines to add file and directory entries into the internal configuration
29168404Spjd * information.  This information is maintained in a number of hash tables which
30168404Spjd * after completion of input file processing will be processed and written to
31168404Spjd * the output configuration file.
32168404Spjd *
33168404Spjd * Each hash table is defined via a Hash_tbl structure.  These are organized:
34168404Spjd *
35168404Spjd *  c_strtbl	contains a hash entry for every file, directory, pathname and
36185029Spjd *		alternative path (dldump(3dl) image) processed.
37168404Spjd *		c_strsize and c_objnum maintain the size and count of the
38168404Spjd *		strings added to this table and are used to size the output
39168404Spjd *		configuration file.
40168404Spjd *
41168404Spjd *  c_inotbls	contains a list of inode hash tables.  Each element of the list
42168404Spjd *		identifies a unique device.  Thus, for each file processed its
43168404Spjd *		st_dev and st_ino are used to assign its entry to the correct
44185029Spjd *		hash table.
45168404Spjd *
46185029Spjd *		Each directory processed is assigned a unique id (c_dirnum)
47185029Spjd *		which insures each file also becomes uniquely identified.
48185029Spjd *
49185029Spjd * All file and directory additions come through the inspect() entry point.
50185029Spjd */
51185029Spjd
52185029Spjd#include	<sys/types.h>
53168404Spjd#include	<sys/stat.h>
54168404Spjd#include	<fcntl.h>
55168404Spjd#include	<dirent.h>
56168404Spjd#include	<_libelf.h>
57185029Spjd#include	<errno.h>
58168404Spjd#include	<stdio.h>
59168404Spjd#include	<string.h>
60168404Spjd#include	<unistd.h>
61185029Spjd#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 */
76static int
77enteralt(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 */
147static Hash_ent *
148enterino(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 */
218static 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 */
265static Hash_ent *
266enterdir(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 */
348static Hash_ent *
349enternoexistdir(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 */
382static 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 */
445static Hash_ent *
446enternoexistfile(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 */
502static Hash_ent *
503enterfile(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 */
648static int
649inspect_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] == crle->c_class) ||
719	    (ehdr.e_machine == crle->c_machine)))) {
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 */
846static int
847inspect_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 */
938int
939inspect(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