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) strcpy(_alter, crle->c_objdir);
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 obj->o_flags |= RTC_OBJ_ALTER;
114
115 /*
116 * If we're overriding an existing alternative with the real path, free
117 * up any previous alternative.
118 */
119 if (obj->o_alter) {
120 crle->c_strsize -= strlen(alter) + 1;
121 fmt = MSG_INTL(MSG_DIA_ALTUPDATE);
122 } else
123 fmt = MSG_INTL(MSG_DIA_ALTCREATE);
124
125 /*
126 * Allocate the new alternative and update the string table size.
127 */
128 altsz = strlen(alter) + 1;
129 if ((obj->o_alter = malloc(altsz)) == NULL)
130 return (0);
131 (void) strcpy(obj->o_alter, alter);
132
133 crle->c_strsize += altsz;
134
135 if (crle->c_flags & CRLE_VERBOSE)
136 (void) printf(fmt, alter, obj->o_path);
137
138 return (1);
139 }
140
141
142 /*
143 * Establish an inode hash entry, this is unique for each dev hash table, and
144 * establishes the unique object descriptor.
145 */
146 static Hash_ent *
enterino(Crle_desc * crle,const char * name,struct stat * status,Half flags)147 enterino(Crle_desc *crle, const char *name, struct stat *status, Half flags)
148 {
149 Hash_ent *ent;
150 Hash_obj *obj;
151 Hash_tbl *tbl;
152 Aliste idx;
153 Addr ino = (Addr)status->st_ino;
154 ulong_t dev = status->st_dev;
155 Lword info;
156 int found = 0;
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 (APLIST_TRAVERSE(crle->c_inotbls, idx, tbl)) {
172 if (tbl->t_ident == dev) {
173 found = 1;
174 break;
175 }
176 }
177 if (found == 0) {
178 if ((tbl = make_hash(crle->c_inobkts, HASH_INT, dev)) == NULL)
179 return (NULL);
180 if (aplist_append(&crle->c_inotbls, tbl, AL_CNT_CRLE) == NULL)
181 return (NULL);
182 }
183
184 /*
185 * Reuse or add this new object to the inode hash table.
186 */
187 if ((ent = get_hash(tbl, ino, 0,
188 (HASH_FND_ENT | HASH_ADD_ENT))) == NULL)
189 return (NULL);
190
191 /*
192 * If an object descriptor doesn't yet exist create one.
193 */
194 if ((obj = ent->e_obj) == NULL) {
195 if ((obj = calloc(sizeof (Hash_obj), 1)) == NULL)
196 return (NULL);
197 obj->o_tbl = tbl;
198 obj->o_flags = flags;
199 obj->o_info = info;
200
201 /*
202 * Reallocate the objects name, as it might have been composed
203 * and passed to us on the stack.
204 */
205 if ((obj->o_path = strdup(name)) == NULL)
206 return (NULL);
207
208 /*
209 * Assign this object to the original ino hash entry.
210 */
211 ent->e_obj = obj;
212 }
213 return (ent);
214 }
215
216 /*
217 * Basic directory entry, establishes entry information, updated global counts
218 * and provides any diagnostics.
219 */
220 static int
_enterdir(Crle_desc * crle,const char * dir,Hash_ent * ent,Hash_obj * obj)221 _enterdir(Crle_desc *crle, const char *dir, Hash_ent *ent, Hash_obj *obj)
222 {
223 size_t size = strlen(dir) + 1;
224 char *ndir;
225
226 /*
227 * Establish this hash entries key (which is the directory name itself),
228 * assign the next available directory number, and its object.
229 */
230 if ((ndir = malloc(size)) == NULL)
231 return (0);
232 (void) strcpy(ndir, dir);
233
234 ent->e_key = (Addr)ndir;
235 ent->e_id = crle->c_dirnum++;
236 ent->e_obj = obj;
237
238 /*
239 * Update string table information. We add a dummy filename for each
240 * real directory so as to have a null terminated file table array for
241 * this directory.
242 */
243 crle->c_strsize += size;
244 crle->c_hashstrnum++;
245 crle->c_filenum++;
246
247 /*
248 * Provide any diagnostics.
249 */
250 if (crle->c_flags & CRLE_VERBOSE) {
251 const char *fmt;
252
253 if (obj->o_flags & RTC_OBJ_NOEXIST)
254 fmt = MSG_INTL(MSG_DIA_NOEXIST);
255 else
256 fmt = MSG_INTL(MSG_DIA_DIR);
257
258 (void) printf(fmt, ent->e_id, dir);
259 }
260 return (1);
261 }
262
263 /*
264 * Establish a string hash entry for a directory.
265 */
266 static Hash_ent *
enterdir(Crle_desc * crle,const char * odir,Half flags,struct stat * status)267 enterdir(Crle_desc *crle, const char *odir, Half flags, struct stat *status)
268 {
269 Hash_tbl *stbl = crle->c_strtbl;
270 Hash_ent *ent;
271 Hash_obj *obj;
272 char rdir[PATH_MAX], *ndir;
273
274 /*
275 * Establish the directories real name, this is the name that will be
276 * recorded in the object identifier.
277 */
278 if (realpath(odir, rdir) == NULL)
279 return (NULL);
280
281 if (strcmp(odir, rdir))
282 ndir = rdir;
283 else
284 ndir = (char *)odir;
285
286 /*
287 * If we're not dealing with an all-entries directory (i.e., we're
288 * recording this directory because of its explicitly specified
289 * filename) leave off any filename specific attributes.
290 */
291 if ((flags & RTC_OBJ_ALLENTS) == 0)
292 flags &= ~(RTC_OBJ_ALTER | RTC_OBJ_DUMP | RTC_OBJ_GROUP);
293 flags |= RTC_OBJ_DIRENT;
294
295 /*
296 * Establish a inode table entry, and the objects unique descriptor.
297 */
298 if ((ent = enterino(crle, ndir, status, flags)) == NULL)
299 return (NULL);
300 obj = ent->e_obj;
301
302 /*
303 * Create a string table entry for the real directory.
304 */
305 if ((ent = get_hash(stbl, (Addr)ndir, 0,
306 (HASH_FND_ENT | HASH_ADD_ENT))) == NULL)
307 return (NULL);
308
309 /*
310 * If this is a new entry reassign the directory name and assign a
311 * unique directory id.
312 */
313 if (ent->e_id == 0) {
314 if (_enterdir(crle, ndir, ent, obj) == 0)
315 return (NULL);
316 }
317
318 /*
319 * If the directory name supplied is different than the real name we've
320 * just entered, continue to create an entry for it.
321 */
322 if (ndir == odir)
323 return (ent);
324
325 /*
326 * Create a string table entry for this real directory.
327 */
328 if ((ent = get_hash(stbl, (Addr)odir, 0,
329 (HASH_FND_ENT | HASH_ADD_ENT))) == NULL)
330 return (NULL);
331
332 /*
333 * If this is a new entry reassign the directory name and assign a
334 * unique directory id.
335 */
336 if (ent->e_id == 0) {
337 if (_enterdir(crle, odir, ent, obj) == 0)
338 return (NULL);
339 }
340
341 return (ent);
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 *
enternoexistdir(Crle_desc * crle,const char * dir)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))) == NULL)
358 return (NULL);
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)) == NULL)
368 return (NULL);
369 obj->o_flags = (RTC_OBJ_NOEXIST | RTC_OBJ_DIRENT);
370
371 if (_enterdir(crle, dir, ent, obj) == 0)
372 return (NULL);
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
_enterfile(Crle_desc * crle,const char * file,int off,Hash_ent * fent,Hash_ent * rent,Hash_ent * dent,Hash_obj * obj)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)) == NULL)
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 *
enternoexistfile(Crle_desc * crle,const char * path,const char * file,Hash_ent * dent)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))) == NULL)
458 return (NULL);
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)) == NULL)
466 return (NULL);
467 obj->o_flags = RTC_OBJ_NOEXIST;
468
469 if (_enterfile(crle, path, 0, rent, 0, dent, obj) == 0)
470 return (NULL);
471 }
472 obj = rent->e_obj;
473 if ((obj->o_path = strdup(path)) == NULL)
474 return (NULL);
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))) == NULL)
489 return (NULL);
490
491 if (ent->e_id == 0) {
492 if (_enterfile(crle, file, off, ent, rent, dent, obj) == 0)
493 return (NULL);
494 }
495 return (ent);
496 }
497
498
499 /*
500 * Establish a string hash entry for a file.
501 */
502 static Hash_ent *
enterfile(Crle_desc * crle,const char * opath,const char * ofile,Half flags,Hash_ent * odent,struct stat * status)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) == NULL)
518 return (NULL);
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)) == NULL)
542 return (NULL);
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)) == NULL)
553 return (NULL);
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))) == NULL)
561 return (NULL);
562 if (rent->e_id == 0) {
563 if (_enterfile(crle, npath, 0, rent, 0, ndent, obj) == 0)
564 return (NULL);
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))) == NULL)
590 return (NULL);
591 if (ent->e_id == 0) {
592 if (_enterfile(crle, nfile, off, ent, rent, ndent, obj) == 0)
593 return (NULL);
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)) == NULL)
608 return (NULL);
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))) == NULL)
616 return (NULL);
617 if (rent->e_id == 0) {
618 if (_enterfile(crle, opath, 0, rent, 0, odent, obj) == 0)
619 return (NULL);
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))) == NULL)
635 return (NULL);
636 if (ent->e_id == 0) {
637 if (_enterfile(crle, ofile, off, ent, rent, odent, obj) == 0)
638 return (NULL);
639 }
640
641 return (ent);
642 }
643
644 /*
645 * Add a file to configuration information.
646 */
647 static int
inspect_file(Crle_desc * crle,const char * path,const char * file,Half flags,Hash_ent * dent,struct stat * status,int error)648 inspect_file(Crle_desc *crle, const char *path, const char *file, Half flags,
649 Hash_ent *dent, struct stat *status, int error)
650 {
651 Hash_ent *ent;
652 Hash_obj *obj;
653 int fd;
654 Elf *elf;
655 GElf_Ehdr ehdr;
656 GElf_Xword dyflags = 0;
657 Aliste idx;
658 Hash_tbl *tbl;
659 Addr ino = (Addr)status->st_ino;
660
661 /*
662 * Determine whether this file (inode) has already been processed.
663 */
664 for (APLIST_TRAVERSE(crle->c_inotbls, idx, tbl)) {
665 if (tbl->t_ident != status->st_dev)
666 continue;
667
668 if ((ent = get_hash(tbl, ino, 0, HASH_FND_ENT)) == NULL)
669 break;
670
671 /*
672 * This files inode object does exist, make sure it has a file
673 * entry for this directory.
674 */
675 if ((ent = enterfile(crle, path, file, flags, dent,
676 status)) == NULL)
677 return (error);
678 obj = ent->e_obj;
679
680 /*
681 * If an alternative has been asked for, and one has not yet
682 * been established, create one.
683 */
684 if ((flags & RTC_OBJ_ALTER) &&
685 ((obj->o_flags & RTC_OBJ_NOALTER) == 0)) {
686 if (enteralt(crle, path, file, flags, obj) == 0)
687 return (error);
688 }
689 return (0);
690 }
691
692 /*
693 * This is a new file, determine if it's a valid ELF file.
694 */
695 if ((fd = open(path, O_RDONLY, 0)) == -1) {
696 if (error) {
697 int err = errno;
698 (void) fprintf(stderr, MSG_INTL(MSG_SYS_OPEN),
699 crle->c_name, path, strerror(err));
700 }
701 return (error);
702 }
703
704 /*
705 * Obtain an ELF descriptor and determine if we have a shared object.
706 */
707 if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL) {
708 if (error)
709 (void) fprintf(stderr, MSG_INTL(MSG_ELF_BEGIN),
710 crle->c_name, path, elf_errmsg(-1));
711 (void) close(fd);
712 return (error);
713 }
714 if ((elf_kind(elf) != ELF_K_ELF) ||
715 (gelf_getehdr(elf, &ehdr) == NULL) ||
716 (!((ehdr.e_type == ET_EXEC) || (ehdr.e_type == ET_DYN))) ||
717 (!((ehdr.e_ident[EI_CLASS] == M_CLASS) ||
718 (ehdr.e_machine == M_MACH)))) {
719 if (error)
720 (void) fprintf(stderr, MSG_INTL(MSG_ELF_TYPE),
721 crle->c_name, path);
722 (void) close(fd);
723 (void) elf_end(elf);
724 return (error);
725 }
726
727 (void) close(fd);
728
729 /*
730 * If we're generating alternative objects find this objects DT_FLAGS
731 * to insure it isn't marked as non-dumpable (libdl.so.1 falls into
732 * this category).
733 */
734 if (flags & RTC_OBJ_DUMP)
735 dyflags = _gelf_getdyndtflags_1(elf);
736
737 /*
738 * Dynamic executables can be examined to determine their dependencies,
739 * dldump(3dl) their dependencies, and may even be dldump(3dl)'ed
740 * themselves.
741 *
742 * If we come across an executable while searching a directory
743 * (error == 0) it is ignored.
744 */
745 if (ehdr.e_type == ET_EXEC) {
746 if (error == 0) {
747 (void) elf_end(elf);
748 return (0);
749 }
750
751 /*
752 * If we're not dumping the application itself, or we've not
753 * asked to gather its dependencies then its rather useless.
754 */
755 if ((flags & (RTC_OBJ_GROUP | RTC_OBJ_DUMP)) == 0) {
756 (void) fprintf(stderr, MSG_INTL(MSG_GEN_INVFILE),
757 crle->c_name, path);
758 (void) elf_end(elf);
759 return (error);
760 }
761
762 /*
763 * If we're dumping the application under RTLD_REL_EXEC then the
764 * configuration file becomes specific to this application, so
765 * make sure we haven't been here before.
766 */
767 if (crle->c_app && (flags & RTC_OBJ_DUMP) &&
768 (crle->c_dlflags & RTLD_REL_EXEC)) {
769 (void) fprintf(stderr, MSG_INTL(MSG_ARG_MODE),
770 crle->c_name, crle->c_app, path);
771 (void) elf_end(elf);
772 return (error);
773 }
774 }
775
776 /*
777 * Enter the file in the string hash table.
778 */
779 if ((ent = enterfile(crle, path, file, flags, dent, status)) == NULL) {
780 (void) elf_end(elf);
781 return (error);
782 }
783 obj = ent->e_obj;
784
785 if (flags & RTC_OBJ_ALTER) {
786 /*
787 * If this object is marked as non-dumpable make sure we don't
788 * create a dldump(3dl) alternative. A user requested
789 * alternative is acceptable.
790 */
791 if ((flags & RTC_OBJ_DUMP) && (dyflags & DF_1_NODUMP)) {
792 obj->o_flags |= RTC_OBJ_NOALTER;
793 obj->o_flags &= ~(RTC_OBJ_ALTER | RTC_OBJ_DUMP);
794 } else {
795 if (enteralt(crle, path, file, flags, obj) == 0) {
796 (void) elf_end(elf);
797 return (error);
798 }
799 }
800 }
801
802 /*
803 * Executables are recorded in the configuration file either to allow
804 * for the configuration files update, or may indicate that the
805 * configuration file is specific to their use.
806 */
807 if (ehdr.e_type == ET_EXEC) {
808 obj->o_flags |= RTC_OBJ_EXEC;
809
810 if ((flags & RTC_OBJ_DUMP) &&
811 (crle->c_dlflags & RTLD_REL_EXEC)) {
812 /*
813 * Get the reallocated pathname rather than using the
814 * original (the original might be from an existing
815 * configuration file being updated, in which case the
816 * pointer will be unmapped before we get to use it).
817 */
818 ent = get_hash(crle->c_strtbl, (Addr)path, 0,
819 HASH_FND_ENT);
820
821 obj->o_flags |= RTC_OBJ_APP;
822 crle->c_app = (char *)ent->e_key;
823 }
824 }
825
826 /*
827 * If we've been asked to process this object as a group determine its
828 * dependencies.
829 */
830 if (flags & RTC_OBJ_GROUP) {
831 if (depend(crle, path, flags, &ehdr)) {
832 (void) elf_end(elf);
833 return (error);
834 }
835 }
836
837 (void) elf_end(elf);
838 return (0);
839 }
840
841 /*
842 * Add a directory to configuration information.
843 */
844 static int
inspect_dir(Crle_desc * crle,const char * name,Half flags,struct stat * status)845 inspect_dir(Crle_desc *crle, const char *name, Half flags, struct stat *status)
846 {
847 Hash_tbl *stbl = crle->c_strtbl;
848 DIR *dir;
849 struct dirent *dirent;
850 Hash_ent *ent;
851 int error = 0;
852 struct stat _status;
853 char path[PATH_MAX], * dst;
854 const char *src;
855
856 /*
857 * Determine whether we've already visited this directory to process
858 * all its entries.
859 */
860 if ((ent = get_hash(stbl, (Addr)name, 0, HASH_FND_ENT)) != NULL) {
861 if (ent->e_obj->o_flags & RTC_OBJ_ALLENTS)
862 return (0);
863 } else {
864 /*
865 * Create a directory hash entry.
866 */
867 if ((ent = enterdir(crle, name, (flags | RTC_OBJ_ALLENTS),
868 status)) == NULL)
869 return (1);
870 }
871 ent->e_obj->o_flags |= RTC_OBJ_ALLENTS;
872
873 /*
874 * Establish the pathname buffer.
875 */
876 for (dst = path, dst--, src = name; *src; src++)
877 *++dst = *src;
878 if (*dst++ != '/')
879 *dst++ = '/';
880
881 /*
882 * Access the directory in preparation for reading its entries.
883 */
884 if ((dir = opendir(name)) == NULL)
885 return (1);
886
887 /*
888 * Read each entry from the directory looking for ELF files.
889 */
890 while ((dirent = readdir(dir)) != NULL) {
891 const char *file = dirent->d_name;
892 char *_dst;
893
894 /*
895 * Ignore "." and ".." entries.
896 */
897 if ((file[0] == '.') && ((file[1] == '\0') ||
898 ((file[1] == '.') && (file[2] == '\0'))))
899 continue;
900
901 /*
902 * Complete full pathname, and reassign file to the new path.
903 */
904 for (_dst = dst, src = file, file = dst; *src; _dst++, src++)
905 *_dst = *src;
906 *_dst = '\0';
907
908 if (stat(path, &_status) == -1)
909 continue;
910
911 if ((_status.st_mode & S_IFMT) != S_IFREG)
912 continue;
913
914 if (inspect_file(crle, path, file, flags, ent, &_status, 0)) {
915 error = 1;
916 break;
917 }
918 }
919 return (error);
920 }
921
922 /*
923 * Inspect a file/dir name. A stat(name) results in the following actions:
924 *
925 * The name doesn't exist:
926 * The name is assummed to be a non-existent directory and a directory
927 * cache entry is created to indicate this.
928 *
929 * The name is a directory:
930 * The directory is searched for appropriate files.
931 *
932 * The name is a file:
933 * The file is processed and added to the cache if appropriate.
934 */
935 int
inspect(Crle_desc * crle,const char * name,Half flags)936 inspect(Crle_desc *crle, const char *name, Half flags)
937 {
938 Hash_ent *ent;
939 const char *file, *dir;
940 struct stat status;
941 char _name[PATH_MAX], _dir[PATH_MAX];
942 Half nflags = flags & ~RTC_OBJ_CMDLINE;
943 int noexist;
944
945 /*
946 * If this is the first time through here establish a string table
947 * cache.
948 */
949 if (crle->c_dirnum == 0) {
950 if ((crle->c_strtbl = make_hash(crle->c_strbkts,
951 HASH_STR, 0)) == NULL)
952 return (1);
953 crle->c_dirnum = 1;
954 }
955
956 if (crle->c_flags & CRLE_VERBOSE)
957 (void) printf(MSG_INTL(MSG_DIA_INSPECT), name);
958
959 /*
960 * Determine whether the name exists.
961 */
962 if ((noexist = stat(name, &status)) != 0) {
963 if (errno != ENOENT) {
964 int err = errno;
965 (void) fprintf(stderr, MSG_INTL(MSG_SYS_STAT),
966 crle->c_name, name, strerror(err));
967 return (1);
968 } else {
969 /*
970 * If we've been asked to create an alternative object
971 * assume the object is a file and create a valid
972 * alternative entry. This allows the creation of
973 * alternatives for files that might not yet be
974 * installed.
975 *
976 * Otherwise we have no idea whether the name specified
977 * is a file or directory, so we assume a directory and
978 * establish an object descriptor to mark this as
979 * non-existent. This allows us to mark things like
980 * platform specific directories as non-existent.
981 */
982 if ((flags & (RTC_OBJ_DUMP | RTC_OBJ_ALTER)) !=
983 RTC_OBJ_ALTER) {
984 if ((ent = enternoexistdir(crle, name)) == NULL)
985 return (1);
986 ent->e_flags |= flags;
987 return (0);
988 }
989 }
990 }
991
992 /*
993 * Determine whether we're dealing with a directory or a file.
994 */
995 if ((noexist == 0) && ((status.st_mode & S_IFMT) == S_IFDIR)) {
996 /*
997 * Process the directory name to collect its shared objects into
998 * the configuration file.
999 */
1000 if (inspect_dir(crle, name, nflags, &status))
1001 return (1);
1002
1003 ent = get_hash(crle->c_strtbl, (Addr)name, 0, HASH_FND_ENT);
1004 ent->e_flags |= flags;
1005 return (0);
1006 }
1007
1008 /*
1009 * If this isn't a regular file we might as well bail now. Note that
1010 * even if it is, we might still reject the file if it's not ELF later
1011 * in inspect_file().
1012 */
1013 if ((noexist == 0) && ((status.st_mode & S_IFMT) != S_IFREG)) {
1014 (void) fprintf(stderr, MSG_INTL(MSG_GEN_INVFILE), crle->c_name,
1015 name);
1016 return (1);
1017 }
1018
1019 /*
1020 * Break the pathname into directory and filename components.
1021 */
1022 if ((file = strrchr(name, '/')) == NULL) {
1023 dir = MSG_ORIG(MSG_DIR_DOT);
1024 (void) strcpy(_name, MSG_ORIG(MSG_PTH_DOT));
1025 (void) strcpy(&_name[MSG_PTH_DOT_SIZE], name);
1026 name = (const char *)_name;
1027 file = (const char *)&_name[MSG_PTH_DOT_SIZE];
1028 } else {
1029 size_t off = file - name;
1030
1031 if (file == name)
1032 dir = MSG_ORIG(MSG_DIR_ROOT);
1033 else {
1034 (void) strncpy(_dir, name, off);
1035 _dir[off] = '\0';
1036 dir = (const char *)_dir;
1037 }
1038 file++;
1039 }
1040
1041 /*
1042 * Determine whether we've already visited this directory and if not
1043 * create it.
1044 */
1045 if ((ent = get_hash(crle->c_strtbl,
1046 (Addr)dir, 0, HASH_FND_ENT)) == NULL) {
1047 struct stat _status;
1048
1049 if (stat(dir, &_status) != 0) {
1050 if (errno != ENOENT) {
1051 int err = errno;
1052 (void) fprintf(stderr, MSG_INTL(MSG_SYS_STAT),
1053 crle->c_name, name, strerror(err));
1054 return (1);
1055 } else {
1056 /*
1057 * Note that this directory will be tagged as
1058 * having an alternative - not that the
1059 * directory does, but supposedly it contains
1060 * a file that does.
1061 */
1062 if ((ent = enternoexistdir(crle, dir)) == NULL)
1063 return (1);
1064 ent->e_flags |= nflags;
1065 }
1066 } else {
1067 if ((ent = enterdir(crle, dir, nflags,
1068 &_status)) == NULL)
1069 return (1);
1070 }
1071 }
1072
1073 /*
1074 * Regardless of whether we've already processed this file (say from
1075 * an RTC_OBJ_ALLENTS which we could determine from the above), continue
1076 * to inspect the file. It may require alternatives or something that
1077 * hadn't be specified from the directory entry.
1078 */
1079 if (noexist) {
1080 if ((ent = enternoexistfile(crle, name, file, ent)) == NULL)
1081 return (1);
1082 ent->e_flags |= nflags;
1083 if (enteralt(crle, name, file, flags, ent->e_obj) == 0)
1084 return (1);
1085 } else {
1086 if (inspect_file(crle, name, file, nflags, ent, &status, 1))
1087 return (1);
1088 }
1089
1090 /*
1091 * Make sure to propagate any RTC_OBJ_CMDLINE flag.
1092 */
1093 if (ent = get_hash(crle->c_strtbl, (Addr)name, 0, HASH_FND_ENT))
1094 ent->e_flags |= (flags & RTC_OBJ_CMDLINE);
1095
1096 return (0);
1097 }
1098