1 /*
2 * This file and its contents are supplied under the terms of the
3 * Common Development and Distribution License ("CDDL"), version 1.0.
4 * You may only use this file in accordance with the terms of version
5 * 1.0 of the CDDL.
6 *
7 * A full copy of the text of the CDDL should have accompanied this
8 * source. A copy of the CDDL is also available via the Internet at
9 * http://www.illumos.org/license/CDDL.
10 */
11
12 /*
13 * Copyright 2020 Joyent, Inc.
14 * Copyright 2022 Jason King
15 */
16
17 #include <sys/debug.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <sys/avl.h>
21 #include <sys/fcntl.h>
22 #include <sys/sysmacros.h>
23 #include <dirent.h>
24 #include <err.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <gelf.h>
28 #include <libcustr.h>
29 #include <libelf.h>
30 #include <libgen.h>
31 #include <limits.h>
32 #include <stdbool.h>
33 #include <stddef.h>
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #include <unistd.h>
38
39 static inline bool
is_same(const struct stat * s1,const struct stat * s2)40 is_same(const struct stat *s1, const struct stat *s2)
41 {
42 if ((s1->st_ino == s2->st_ino) && (s1->st_dev == s2->st_dev))
43 return (true);
44 return (false);
45 }
46
47 typedef enum dir_flags {
48 DF_NONE = 0,
49 DF_IS_SELF = 1 << 0,
50 DF_IS_SYMLINK = 1 << 2,
51 } dir_flags_t;
52
53 typedef struct path {
54 custr_t *p_name;
55 size_t p_pfxidx;
56 } path_t;
57
58 typedef struct name {
59 char *n_name;
60 bool n_is_symlink;
61 } name_t;
62
63 typedef struct names {
64 name_t *ns_names;
65 uint_t ns_num;
66 uint_t ns_alloc;
67 } names_t;
68
69 typedef struct elfinfo {
70 int ei_class;
71 uint16_t ei_type;
72 bool ei_hasverdef;
73 } elfinfo_t;
74
75 typedef struct obj {
76 avl_node_t obj_avlid;
77 avl_node_t obj_avlname;
78 dev_t obj_dev;
79 ino_t obj_inode;
80 names_t obj_names;
81 elfinfo_t obj_elfinfo;
82 } obj_t;
83
84 static void path_init(path_t *, const char *, bool);
85 static size_t path_append(path_t *, const char *);
86 static const char *path_name(const path_t *);
87 static const char *path_fullpath(const path_t *);
88 static void path_pop(path_t *, size_t);
89
90 static bool maybe_obj(const char *, mode_t);
91 static bool get_elfinfo(const path_t *, int, elfinfo_t *);
92 static void add_name(obj_t *, const path_t *, bool);
93 static int cmp_id(const void *, const void *);
94 static int cmp_objname(const void *, const void *);
95 static int cmp_names(const void *, const void *);
96
97 static void process_dir(path_t *, int, const struct stat *, dir_flags_t);
98 static void process_file(path_t *, int, const struct stat *, bool);
99 static void process_arg(char *);
100
101 static void sort_names(void *, void *);
102 static void print_entry(void *, void *);
103
104 static void foreach_avl(avl_tree_t *, void (*)(void *, void *), void *);
105
106 static void nomem(void);
107 static char *xstrdup(const char *);
108 static void *xcalloc(size_t, size_t);
109
110 static avl_tree_t avl_byid;
111 static avl_tree_t avl_byname;
112
113 static const char *special_paths[] = {
114 "usr/bin/alias",
115 "usr/lib/isaexec",
116 };
117
118 static int rootfd = -1;
119
120 /* By default, we process aliases */
121 static bool do_alias = true;
122
123 /* By default, we treat certain well known names (e.g. isaexec) as special */
124 static bool do_special = true;
125
126 /* fast_mode, relpath, and so_only are all false by default */
127 static bool fast_mode;
128 static bool relpath;
129 static bool so_only;
130
131 static void __NORETURN
usage(const char * name)132 usage(const char *name)
133 {
134 (void) fprintf(stderr,
135 "Usage: %s [-afnrs] file | dir\n"
136 "\t[-a]\texpand symlink aliases\n"
137 "\t[-f]\tuse file name at mode to speed search\n"
138 "\t[-h]\tshow this help\n"
139 "\t[-n]\tdon\'t treat well known paths as special in sorting\n"
140 "\t[-r]\treport relative paths\n"
141 "\t[-s]\tonly remote shareable (ET_DYN) objects\n", name);
142 exit(EXIT_FAILURE);
143 }
144
145 int
main(int argc,char ** argv)146 main(int argc, char **argv)
147 {
148 int c;
149
150 if (elf_version(EV_CURRENT) == EV_NONE)
151 errx(EXIT_FAILURE, "elf library is out of date");
152
153 while ((c = getopt(argc, argv, "ahfnrs")) != -1) {
154 switch (c) {
155 case 'a':
156 do_alias = false;
157 break;
158 case 'f':
159 fast_mode = true;
160 break;
161 case 'n':
162 do_special = false;
163 break;
164 case 'r':
165 relpath = true;
166 break;
167 case 's':
168 so_only = true;
169 break;
170 case 'h':
171 usage(argv[0]);
172 case '?':
173 (void) fprintf(stderr, "Unknown option -%c\n", optopt);
174 usage(argv[0]);
175 }
176 }
177
178 if (optind == argc) {
179 (void) fprintf(stderr, "Missing file or dir parameter\n");
180 usage(argv[0]);
181 }
182
183 if (argv[optind][0] == '\0')
184 errx(EXIT_FAILURE, "Invalid file or dir value");
185
186 avl_create(&avl_byid, cmp_id, sizeof (obj_t),
187 offsetof(obj_t, obj_avlid));
188 avl_create(&avl_byname, cmp_objname, sizeof (obj_t),
189 offsetof(obj_t, obj_avlname));
190
191 process_arg(argv[optind]);
192 foreach_avl(&avl_byid, sort_names, &avl_byname);
193 foreach_avl(&avl_byname, print_entry, NULL);
194
195 return (EXIT_SUCCESS);
196 }
197
198 static void
process_arg(char * arg)199 process_arg(char *arg)
200 {
201 path_t path;
202 struct stat sb;
203 int fd;
204
205 if ((fd = open(arg, O_RDONLY)) == -1) {
206 err(EXIT_FAILURE, "could not open %s", arg);
207 }
208
209 if (fstat(fd, &sb) < 0) {
210 err(EXIT_FAILURE, "failed to stat %s", arg);
211 }
212
213 if (S_ISDIR(sb.st_mode)) {
214 path_init(&path, arg, relpath);
215 if (relpath) {
216 (void) printf("PREFIX %s\n", arg);
217 }
218
219 rootfd = fd;
220 /* process_dir() will close fd */
221 process_dir(&path, fd, &sb, DF_NONE);
222 return;
223 }
224
225 char *argcopy = xstrdup(arg);
226 char *dir = dirname(argcopy);
227
228 if (!S_ISREG(sb.st_mode)) {
229 err(EXIT_FAILURE, "not a file or directory: %s", arg);
230 }
231
232 #ifndef O_DIRECTORY
233 struct stat tsb;
234 if (stat(dir, &tsb) == -1) {
235 err(EXIT_FAILURE, "failed to stat %s", dir);
236 }
237 if (!S_ISDIR(tsb.st_mode)) {
238 errx(EXIT_FAILURE, "not a directory: %s", dir);
239 }
240 rootfd = open(dir, O_RDONLY);
241 #else
242 rootfd = open(dir, O_RDONLY|O_DIRECTORY);
243 #endif
244 if (rootfd < 0) {
245 err(EXIT_FAILURE, "%s", dir);
246 }
247
248 path_init(&path, dir, relpath);
249 if (relpath) {
250 (void) printf("PREFIX %s\n", dir);
251 }
252 free(argcopy);
253
254 process_file(&path, fd, &sb, DF_NONE);
255 }
256
257 /*
258 * Processing a directory has some subtleties. When we encounter a
259 * subdirectory that's a symlink, we want any files we encounter when
260 * we traverse it to be treated as aliases. We may also have a symlink that's
261 * a link back to ourself (e.g. 32 -> .). In this special case, we still want
262 * to reprocess the directory to generate alias names, but we use the
263 * DFLAG_SELF flag to avoid recursing forever.
264 *
265 * A limitation of this approach is that we cannot handle a loop over multiple
266 * levels (e.g. foo -> ../..). Nothing currently in illumos-gate creates any
267 * such symlinks in the proto area, so we've opted to avoid complicating
268 * processing further to handle scenarios that aren't realistically expected
269 * to occur.
270 *
271 * Note that dirfd is always closed upon return from process_dir().
272 */
273 static void
process_dir(path_t * p,int dirfd,const struct stat * dirsb,dir_flags_t dflags)274 process_dir(path_t *p, int dirfd, const struct stat *dirsb, dir_flags_t dflags)
275 {
276 DIR *d;
277 struct dirent *de;
278
279 d = fdopendir(dirfd);
280 if (d == NULL) {
281 warn("opendir(%s)", path_fullpath(p));
282 VERIFY0(close(dirfd));
283 return;
284 }
285
286 while ((de = readdir(d)) != NULL) {
287 struct stat sb;
288 int fd;
289 bool is_link = false;
290 size_t plen;
291
292 if (strcmp(de->d_name, ".") == 0 ||
293 strcmp(de->d_name, "..") == 0) {
294 continue;
295 }
296
297 plen = path_append(p, de->d_name);
298
299 if (fstatat(rootfd, path_name(p), &sb,
300 AT_SYMLINK_NOFOLLOW) < 0) {
301 warn("%s", path_fullpath(p));
302 path_pop(p, plen);
303 continue;
304 }
305
306 fd = openat(dirfd, de->d_name, O_RDONLY);
307 if (fd < 0) {
308 /*
309 * Symlinks in the proto area may point to a path
310 * that's not present (e.g. /dev/stdin -> fd/0).
311 * Silently skip such entries.
312 */
313 if (errno != ENOENT || !S_ISLNK(sb.st_mode)) {
314 warn("%s", path_fullpath(p));
315 }
316 path_pop(p, plen);
317 continue;
318 }
319
320 if (S_ISLNK(sb.st_mode)) {
321 is_link = true;
322
323 if (fstat(fd, &sb) < 0) {
324 warn("stat %s", path_fullpath(p));
325 path_pop(p, plen);
326 continue;
327 }
328 }
329
330 if (S_ISDIR(sb.st_mode)) {
331 if ((dflags & DF_IS_SELF) != 0) {
332 VERIFY0(close(fd));
333 path_pop(p, plen);
334 continue;
335 }
336
337 dir_flags_t newflags = dflags;
338
339 /* The recursive process_dir() call closes fd */
340 if (is_link)
341 newflags |= DF_IS_SYMLINK;
342 if (is_same(&sb, dirsb))
343 newflags |= DF_IS_SELF;
344 process_dir(p, fd, &sb, newflags);
345 } else if (S_ISREG(sb.st_mode)) {
346 if (!maybe_obj(de->d_name, sb.st_mode)) {
347 VERIFY0(close(fd));
348 path_pop(p, plen);
349 continue;
350 }
351
352 if ((dflags & (DF_IS_SELF | DF_IS_SYMLINK)) != 0)
353 is_link = true;
354
355 /* process_file() closes fd */
356 process_file(p, fd, &sb, is_link);
357 }
358
359 path_pop(p, plen);
360 }
361
362 /* Closes dirfd */
363 VERIFY0(closedir(d));
364 }
365
366 /* Note that fd is always closed upon return */
367 static void
process_file(path_t * p,int fd,const struct stat * sb,bool is_link)368 process_file(path_t *p, int fd, const struct stat *sb, bool is_link)
369 {
370 avl_index_t where = { 0 };
371 obj_t templ = {
372 .obj_dev = sb->st_dev,
373 .obj_inode = sb->st_ino,
374 };
375 obj_t *obj = avl_find(&avl_byid, &templ, &where);
376 elfinfo_t elfinfo = { 0 };
377
378 if (obj != NULL)
379 goto done;
380
381 if (!get_elfinfo(p, fd, &elfinfo)) {
382 VERIFY0(close(fd));
383 return;
384 }
385
386 obj = xcalloc(1, sizeof (*obj));
387 obj->obj_dev = sb->st_dev;
388 obj->obj_inode = sb->st_ino;
389 obj->obj_elfinfo = elfinfo;
390 avl_add(&avl_byid, obj);
391
392 done:
393 add_name(obj, p, is_link);
394 VERIFY0(close(fd));
395 }
396
397 static void
print_entry(void * a,void * arg __unused)398 print_entry(void *a, void *arg __unused)
399 {
400 obj_t *obj = a;
401 const char *objname = obj->obj_names.ns_names[0].n_name;
402 const char *bits = "";
403 const char *type = "";
404 const char *verdef = obj->obj_elfinfo.ei_hasverdef ?
405 "VERDEF" : "NOVERDEF";
406
407 switch (obj->obj_elfinfo.ei_class) {
408 case ELFCLASS32:
409 bits = "32";
410 break;
411 case ELFCLASS64:
412 bits = "64";
413 break;
414 default:
415 errx(EXIT_FAILURE, "unknown elfclass value %x for %s",
416 obj->obj_elfinfo.ei_class, objname);
417 }
418
419 switch (obj->obj_elfinfo.ei_type) {
420 case ET_REL:
421 type = "REL";
422 break;
423 case ET_DYN:
424 type = "DYN";
425 break;
426 case ET_EXEC:
427 type = "EXEC";
428 break;
429 default:
430 errx(EXIT_FAILURE, "unexpected elf type %x for %s",
431 obj->obj_elfinfo.ei_type, objname);
432 }
433
434 names_t *names = &obj->obj_names;
435
436 VERIFY3U(names->ns_num, >, 0);
437 VERIFY(!names->ns_names[0].n_is_symlink);
438
439 (void) printf("OBJECT %2s %-4s %-8s %s\n", bits, type, verdef,
440 objname);
441
442 for (uint_t i = 1; i < names->ns_num; i++) {
443 if (do_alias) {
444 (void) printf("%-23s %s\t%s\n",
445 "ALIAS", objname, names->ns_names[i].n_name);
446 } else {
447 (void) printf("OBJECT %2s %-4s %-8s %s\n", bits, type,
448 verdef, names->ns_names[i].n_name);
449 }
450 }
451 }
452
453 /*
454 * Returns true and eip populated if name was an elf object, otherwise
455 * returns false.
456 */
457 static bool
get_elfinfo(const path_t * p,int fd,elfinfo_t * eip)458 get_elfinfo(const path_t *p, int fd, elfinfo_t *eip)
459 {
460 Elf *elf = NULL;
461 Elf_Scn *scn = NULL;
462 GElf_Ehdr ehdr = { 0 };
463 int eval;
464
465 if ((elf = elf_begin(fd, ELF_C_READ, NULL)) == NULL)
466 goto fail_noend;
467
468 if ((eip->ei_class = gelf_getclass(elf)) == ELFCLASSNONE) {
469 VERIFY0(elf_end(elf));
470 return (false);
471 }
472
473 if (gelf_getehdr(elf, &ehdr) == NULL)
474 goto fail;
475
476 eip->ei_type = ehdr.e_type;
477 eip->ei_hasverdef = false;
478
479 while ((scn = elf_nextscn(elf, scn)) != NULL) {
480 Elf_Data *data = NULL;
481 GElf_Shdr shdr = { 0 };
482
483 if (gelf_getshdr(scn, &shdr) == NULL)
484 goto fail;
485
486 if (shdr.sh_type != SHT_DYNAMIC)
487 continue;
488
489 if ((data = elf_getdata(scn, NULL)) == NULL)
490 continue;
491
492 size_t nent = shdr.sh_size / shdr.sh_entsize;
493
494 for (size_t i = 0; i < nent; i++) {
495 GElf_Dyn dyn = { 0 };
496
497 if (gelf_getdyn(data, i, &dyn) == NULL) {
498 goto fail;
499 }
500
501 if (dyn.d_tag == DT_VERDEF) {
502 eip->ei_hasverdef = true;
503 break;
504 }
505 }
506 }
507
508 VERIFY0(elf_end(elf));
509 return (true);
510
511 fail:
512 VERIFY0(elf_end(elf));
513
514 fail_noend:
515 eval = elf_errno();
516
517 warnx("%s: %s", path_fullpath(p), elf_errmsg(eval));
518 return (false);
519 }
520
521 static bool
is_special(const char * name)522 is_special(const char *name)
523 {
524 for (uint_t i = 0; i < ARRAY_SIZE(special_paths); i++) {
525 if (strcmp(special_paths[i], name) == 0)
526 return (true);
527 }
528 return (false);
529 }
530
531 static void
sort_names(void * a,void * b)532 sort_names(void *a, void *b)
533 {
534 obj_t *obj = a;
535 avl_tree_t *name_avl = b;
536 names_t *names = &obj->obj_names;
537
538 /* We should always have at least one name */
539 VERIFY3U(names->ns_num, >, 0);
540
541 /* If there is only one, they get the prize and we're done */
542 if (names->ns_num == 1)
543 return;
544
545 name_t *first = NULL;
546
547 /*
548 * Find the first (in sorted order) pathname for this object
549 * that isn't a symlink.
550 */
551 for (uint_t i = 0; i < names->ns_num; i++) {
552 name_t *n = &names->ns_names[i];
553
554 if (n->n_is_symlink)
555 continue;
556
557 if (first == NULL) {
558 first = n;
559 continue;
560 }
561
562 /*
563 * If we're treating isaexec as special, we always
564 * want it to be the first entry. Otherwise, pick
565 * the 'lowest' sorted pathname.
566 */
567 if (do_special) {
568 if (is_special(n->n_name)) {
569 first = n;
570 break;
571 }
572
573 if (strcmp(n->n_name, first->n_name) < 0)
574 first = n;
575 }
576 }
577
578 /*
579 * If the first pathname (in sorted order) isn't the first
580 * name entry, we swap it into first place (while also updating
581 * the names AVL tree).
582 */
583 if (first != NULL && first != &names->ns_names[0]) {
584 name_t tmp = names->ns_names[0];
585
586 avl_remove(name_avl, obj);
587 (void) memcpy(&names->ns_names[0], first, sizeof (name_t));
588 (void) memcpy(first, &tmp, sizeof (name_t));
589 avl_add(name_avl, obj);
590 }
591
592 /*
593 * The remaining names (symlinks or not) are sorted by
594 * their pathname.
595 */
596 qsort(&names->ns_names[1], names->ns_num - 1, sizeof (name_t),
597 cmp_names);
598 }
599
600 /*
601 * We grow the names array by NAME_CHUNK entries every time we need to
602 * extend it.
603 */
604 #define NAME_CHUNK 4
605
606 static name_t *
name_new(names_t * names)607 name_new(names_t *names)
608 {
609 if (names->ns_num < names->ns_alloc)
610 return (&names->ns_names[names->ns_num++]);
611
612 name_t *newn = NULL;
613 uint_t newamt = names->ns_alloc + NAME_CHUNK;
614
615 /*
616 * Since we may be building on a machine that doesn't
617 * have reallocarray or the like, we avoid their use here.
618 * If we ever officially designate an illumos-gate build
619 * as the minimum required to build master that contains
620 * reallocarray and such, we can replace most of this logic
621 * with it.
622 *
623 * Also use xcalloc so we get the unused entries zeroed already.
624 * Not strictly necessary, but useful for debugging.
625 */
626 newn = xcalloc(newamt, sizeof (name_t));
627
628 /* Move over existing entries */
629 (void) memcpy(newn, names->ns_names, names->ns_num * sizeof (name_t));
630
631 free(names->ns_names);
632
633 names->ns_names = newn;
634 names->ns_alloc = newamt;
635 return (&names->ns_names[names->ns_num++]);
636 }
637
638 static void
add_name(obj_t * obj,const path_t * p,bool is_symlink)639 add_name(obj_t *obj, const path_t *p, bool is_symlink)
640 {
641 names_t *ns = &obj->obj_names;
642 const char *srcname = path_name(p);
643
644 /* We should never have duplicates */
645 for (size_t i = 0; i < ns->ns_num; i++)
646 VERIFY3S(strcmp(ns->ns_names[i].n_name, srcname), !=, 0);
647
648 name_t *n = name_new(ns);
649
650 n->n_name = xstrdup(srcname);
651 n->n_is_symlink = is_symlink;
652
653 if (is_symlink)
654 return;
655
656 /*
657 * If the name was not a symlink, first determine if there are
658 * existing (hard) links to this name already, and if so, which entry
659 * is the first one. Typically this will be the name we just added
660 * unless the file does have multiple hard links (e.g. isaexec).
661 */
662 uint_t nhlink = 0;
663 uint_t firsthlink = UINT_MAX;
664
665 for (uint_t i = 0; i < ns->ns_num; i++) {
666 if (ns->ns_names[i].n_is_symlink)
667 continue;
668 if (nhlink == 0)
669 firsthlink = i;
670 nhlink++;
671 }
672
673 if (nhlink > 1)
674 return;
675
676 /*
677 * Put the first non-symlink name as the very first
678 * entry.
679 */
680 VERIFY3U(firsthlink, !=, UINT_MAX);
681
682 if (firsthlink != 0) {
683 name_t tmp = {
684 .n_name = ns->ns_names[0].n_name,
685 .n_is_symlink = ns->ns_names[0].n_is_symlink,
686 };
687
688 (void) memcpy(&ns->ns_names[0], &ns->ns_names[firsthlink],
689 sizeof (name_t));
690 (void) memcpy(&ns->ns_names[firsthlink], &tmp, sizeof (name_t));
691 }
692
693 avl_add(&avl_byname, obj);
694 }
695
696 /*
697 * This is an arbitrary initial value -- basically we can nest 16 directories
698 * deep before having to grow p->p_idx (which seems like a nice power of 2).
699 */
700 #define PATH_INIT 16
701
702 static void
path_init(path_t * p,const char * name,bool relpath)703 path_init(path_t *p, const char *name, bool relpath)
704 {
705 (void) memset(p, '\0', sizeof (*p));
706
707 if (custr_alloc(&p->p_name) < 0) {
708 nomem();
709 }
710
711 if (name != NULL && custr_append(p->p_name, name) < 0)
712 nomem();
713
714 size_t len = custr_len(p->p_name);
715
716 /* Trim off any trailing /'s, but don't trim '/' to an empty path */
717 while (len > 1 && custr_cstr(p->p_name)[len - 1] == '/') {
718 VERIFY0(custr_rtrunc(p->p_name, 0));
719 len--;
720 }
721
722 p->p_pfxidx = relpath ? len + 1 : 0;
723 }
724
725 static size_t
path_append(path_t * p,const char * name)726 path_append(path_t *p, const char *name)
727 {
728 size_t clen = custr_len(p->p_name);
729
730 if (clen > 0)
731 VERIFY0(custr_appendc(p->p_name, '/'));
732 VERIFY0(custr_append(p->p_name, name));
733
734 return (clen);
735 }
736
737 static const char *
path_name(const path_t * p)738 path_name(const path_t *p)
739 {
740 return (custr_cstr(p->p_name) + p->p_pfxidx);
741 }
742
743 static const char *
path_fullpath(const path_t * p)744 path_fullpath(const path_t *p)
745 {
746 return (custr_cstr(p->p_name));
747 }
748
749 static void
path_pop(path_t * p,size_t idx)750 path_pop(path_t *p, size_t idx)
751 {
752 VERIFY0(custr_trunc(p->p_name, idx));
753 }
754
755 static int
cmp_id(const void * a,const void * b)756 cmp_id(const void *a, const void *b)
757 {
758 const obj_t *l = a;
759 const obj_t *r = b;
760
761 if (l->obj_dev < r->obj_dev)
762 return (-1);
763 if (l->obj_dev > r->obj_dev)
764 return (1);
765 if (l->obj_inode < r->obj_inode)
766 return (-1);
767 if (l->obj_inode > r->obj_inode)
768 return (1);
769 return (0);
770 }
771
772 static int
cmp_objname(const void * a,const void * b)773 cmp_objname(const void *a, const void *b)
774 {
775 const obj_t *l = a;
776 const obj_t *r = b;
777 const name_t *ln = &l->obj_names.ns_names[0];
778 const name_t *rn = &r->obj_names.ns_names[0];
779
780 return (cmp_names(ln, rn));
781 }
782
783 static int
cmp_names(const void * a,const void * b)784 cmp_names(const void *a, const void *b)
785 {
786 const name_t *l = a;
787 const name_t *r = b;
788 int cmp = strcmp(l->n_name, r->n_name);
789
790 if (cmp < 0)
791 return (-1);
792 if (cmp > 0)
793 return (1);
794 return (0);
795 }
796
797 /*
798 * Use the filename and permission bits to determine if this file could
799 * possibly be an executable.
800 */
801 static bool
maybe_obj(const char * name,mode_t mode)802 maybe_obj(const char *name, mode_t mode)
803 {
804 /* If not in fast mode, check everything, so always return true */
805 if (!fast_mode)
806 return (true);
807
808 size_t len = strlen(name);
809
810 /* If the file name ends in .so, we check */
811 if (len >= 3 && strcmp(&name[len - 3], ".so") == 0) {
812 return (true);
813 }
814
815 /* If the file name contains '.so', we check */
816 if (len >= 4 && strstr(name, ".so.") != NULL) {
817 return (true);
818 }
819
820 /* If the above checks fail, we assume it's not a shared library */
821 if (so_only)
822 return (false);
823
824 /*
825 * The original perl script used a -x test which only looked at
826 * file permissions. This may return slightly different results
827 * than using access(2) or faccessat(2).
828 */
829 if ((mode & (S_IXUSR|S_IXGRP|S_IXOTH)) == 0)
830 return (false);
831
832 return (true);
833 }
834
835 static void
foreach_avl(avl_tree_t * avl,void (* cb)(void *,void *),void * arg)836 foreach_avl(avl_tree_t *avl, void (*cb)(void *, void *), void *arg)
837 {
838 void *obj;
839
840 for (obj = avl_first(avl); obj != NULL; obj = AVL_NEXT(avl, obj))
841 cb(obj, arg);
842 }
843
844 static char *
xstrdup(const char * s)845 xstrdup(const char *s)
846 {
847 char *news = strdup(s);
848
849 if (news == NULL) {
850 nomem();
851 }
852 return (news);
853 }
854
855 static void *
xcalloc(size_t nelem,size_t elsize)856 xcalloc(size_t nelem, size_t elsize)
857 {
858 void *p = calloc(nelem, elsize);
859
860 if (p == NULL) {
861 nomem();
862 }
863
864 return (p);
865 }
866
867 #define NOMEM_MSG "out of memory\n"
868 static void __NORETURN
nomem(void)869 nomem(void)
870 {
871 /* Try to get the error out before aborting */
872 (void) write(STDERR_FILENO, NOMEM_MSG, sizeof (NOMEM_MSG));
873 abort();
874 }
875