/ libpkg / pkgbase.c
pkgbase.c
  1  /*-
  2   * Copyright (c) 1998 John D. Polstra
  3   * Copyright (c) 2012 Matthew Seaman <matthew@FreeBSD.org>
  4   * Copyright (c) 2024 The FreeBSD Foundation
  5   *
  6   * This software was developed in part by Isaac Freund <ifreund@freebsdfoundation.org>
  7   * under sponsorship from the FreeBSD Foundation.
  8   *
  9   * SPDX-License-Identifier: BSD-2-Clause
 10   */
 11  
 12  #include <assert.h>
 13  #include <ctype.h>
 14  #include <dirent.h>
 15  #include <errno.h>
 16  #include <string.h>
 17  
 18  #include "pkg.h"
 19  #include "pkghash.h"
 20  #include "private/event.h"
 21  #include "private/pkg.h"
 22  #include "private/pkgdb.h"
 23  #include "private/utils.h"
 24  #include "xmalloc.h"
 25  
 26  struct pkgbase {
 27  	charv_t system_shlibs;
 28  	/*
 29  	 * unused yet but will be in the future when we will start using
 30  	 * provides/requires in pkgbase
 31  	 */
 32  	charv_t provides;
 33  	bool ignore_compat32;
 34  };
 35  
 36  static int
 37  scan_dir_for_shlibs(charv_t *shlib_list, const char *dir,
 38      enum pkg_shlib_flags flags)
 39  {
 40  	DIR *dirp= opendir(dir);
 41  	if (dirp == NULL) {
 42  		if (errno == ENOENT) {
 43  			return (EPKG_OK);
 44  		}
 45  		pkg_errno("Failed to open '%s' to scan for shared libraries", dir);
 46  		return (EPKG_FATAL);
 47  	}
 48  
 49  	struct dirent *dp;
 50  	size_t cnt = 0;
 51  	while ((dp = readdir(dirp)) != NULL) {
 52  		/* Only regular files and sym-links. On some
 53  		   filesystems d_type is not set, on these the d_type
 54  		   field will be DT_UNKNOWN. */
 55  		if (dp->d_type != DT_REG && dp->d_type != DT_LNK &&
 56  		    dp->d_type != DT_UNKNOWN)
 57  			continue;
 58  
 59  		int len = strlen(dp->d_name);
 60  		/* Name can't be shorter than "libx.so" */
 61  		if (len < 7 || strncmp(dp->d_name, "lib", 3) != 0)
 62  			continue;
 63  
 64  		const char *vers = dp->d_name + len;
 65  		while (vers > dp->d_name &&
 66  		       (isdigit(*(vers-1)) || *(vers-1) == '.'))
 67  			vers--;
 68  		if (vers == dp->d_name + len) {
 69  			if (strncmp(vers - 3, ".so", 3) != 0)
 70  				continue;
 71  		} else if (vers < dp->d_name + 3 ||
 72  		    strncmp(vers - 3, ".so.", 4) != 0)
 73  			continue;
 74  
 75  		/* We have a valid shared library name. */
 76  		char *full = pkg_shlib_name_with_flags(dp->d_name, flags);
 77  		vec_push(shlib_list, full);
 78  		cnt++;
 79  	}
 80  	if (cnt == 0)
 81  		errno = ENOENT;
 82  
 83  	closedir(dirp);
 84  
 85  	return (EPKG_OK);
 86  }
 87  
 88  static const struct {
 89  	const char *dir;
 90  	enum pkg_shlib_flags flags;
 91  } system_shlib_table[] = {
 92  	{"/lib", PKG_SHLIB_FLAGS_NONE },
 93  	{"/usr/lib", PKG_SHLIB_FLAGS_NONE },
 94  	{"/usr/lib32", PKG_SHLIB_FLAGS_COMPAT_32 },
 95  };
 96  
 97  int
 98  scan_system_shlibs(charv_t *system_shlibs, const char *rootdir)
 99  {
100  	int r = EPKG_OK;
101  	for (size_t i = 0; i < NELEM(system_shlib_table); i++) {
102  		char *dir;
103  		if (rootdir != NULL) {
104  			xasprintf(&dir, "%s%s", rootdir, system_shlib_table[i].dir);
105  		} else {
106  			dir = xstrdup(system_shlib_table[i].dir);
107  		}
108  		errno = 0;
109  		int ret = scan_dir_for_shlibs(system_shlibs, dir, system_shlib_table[i].flags);
110  		free(dir);
111  		if (errno == ENOENT)
112  			r = EPKG_NOCOMPAT32;
113  		if (ret != EPKG_OK) {
114  			return (ret);
115  		}
116  	}
117  	if (system_shlibs->d)
118  		qsort(system_shlibs->d, system_shlibs->len, sizeof(char *), char_cmp);
119  
120  	return (r);
121  }
122  
123  struct pkgbase *
124  pkgbase_new(struct pkgdb *db)
125  {
126  	struct pkgbase *pb = xcalloc(1, sizeof(*pb));
127  
128  	if (!pkgdb_file_exists(db, "/usr/bin/uname"))
129  		if (scan_system_shlibs(&pb->system_shlibs, ctx.pkg_rootdir) == EPKG_NOCOMPAT32)
130  			pb->ignore_compat32 = true;
131  
132  	return (pb);
133  }
134  
135  void
136  pkgbase_free(struct pkgbase *pb)
137  {
138  	if (pb == NULL)
139  		return;
140  	vec_free_and_free(&pb->system_shlibs, free);
141  	vec_free_and_free(&pb->provides, free);
142  	free(pb);
143  }
144  
145  bool
146  pkgbase_provide_shlib(struct pkgbase *pb, const char *shlib)
147  {
148  	if (pb->ignore_compat32 && str_ends_with(shlib, ":32"))
149  		return (true);
150  	return (charv_search(&pb->system_shlibs, shlib) != NULL);
151  }
152  
153  bool
154  pkgbase_provide(struct pkgbase *pb, const char *provide)
155  {
156  	return (charv_search(&pb->provides, provide) != NULL);
157  }