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