XBPS Library API 20250713
The X Binary Package System
transaction_check_shlibs.c
1/*-
2 * Copyright (c) 2014-2020 Juan Romero Pardines.
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
18 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <stdbool.h>
29#include <errno.h>
30
31#include "uthash.h"
32
33#include "xbps.h"
34#include "xbps/xbps_array.h"
35#include "xbps/xbps_dictionary.h"
36#include "xbps_api_impl.h"
37
38/*
39 * Verify shlib-{provides,requires} for packages in transaction.
40 * This will catch cases where a package update would break its reverse
41 * dependencies due to an incompatible SONAME bump:
42 *
43 * - foo-1.0 is installed and provides the 'libfoo.so.0' soname.
44 * - foo-2.0 provides the 'libfoo.so.1' soname.
45 * - baz-1.0 requires 'libfoo.so.0'.
46 * - foo is updated to 2.0, hence baz-1.0 is now broken.
47 *
48 * Abort transaction if such case is found.
49 */
50
51struct shlib_entry {
52 const char *name;
53 UT_hash_handle hh;
54};
55
56struct shlib_ctx {
57 struct xbps_handle *xhp;
58 struct shlib_entry *entries;
59 xbps_dictionary_t seen;
60 xbps_array_t missing;
61};
62
63static struct shlib_entry *
64shlib_entry_find(struct shlib_entry *head, const char *name)
65{
66 struct shlib_entry *res = NULL;
67 HASH_FIND_STR(head, name, res);
68 return res;
69}
70
71static struct shlib_entry *
72shlib_entry_get(struct shlib_ctx *ctx, const char *name)
73{
74 struct shlib_entry *res = shlib_entry_find(ctx->entries, name);
75 if (res)
76 return res;
77 res = calloc(1, sizeof(*res));
78 if (!res) {
79 xbps_error_printf("out of memory\n");
80 errno = ENOMEM;
81 return NULL;
82 }
83 res->name = name;
84 HASH_ADD_STR(ctx->entries, name, res);
85 return res;
86}
87
88static int
89collect_shlib_array(struct shlib_ctx *ctx, xbps_array_t array)
90{
91 for (unsigned int i = 0; i < xbps_array_count(array); i++) {
92 struct shlib_entry *entry;
93 const char *shlib = NULL;
94 if (!xbps_array_get_cstring_nocopy(array, i, &shlib))
95 return -EINVAL;
96 entry = shlib_entry_get(ctx, shlib);
97 if (!entry)
98 return -errno;
99 }
100 return 0;
101}
102
103static int
104collect_shlibs(struct shlib_ctx *ctx, xbps_array_t pkgs)
105{
106 xbps_object_t obj;
107 xbps_object_iterator_t iter;
108 xbps_bool_t placeholder;
109
110 // can't set null values to xbps_dictionary so just use one boolean
111 placeholder = xbps_bool_create(true);
112 if (!placeholder) {
113 xbps_error_printf("out of memory\n");
114 return -ENOMEM;
115 }
116
117 ctx->seen = xbps_dictionary_create();
118 if (!ctx->seen) {
119 xbps_error_printf("out of memory\n");
120 return -ENOMEM;
121 }
122
123 for (unsigned int i = 0; i < xbps_array_count(pkgs); i++) {
124 const char *pkgname;
125 xbps_dictionary_t pkgd = xbps_array_get(pkgs, i);
126 xbps_array_t array;
127
128 if (xbps_transaction_pkg_type(pkgd) == XBPS_TRANS_HOLD)
129 continue;
130 if (!xbps_dictionary_get_cstring_nocopy(pkgd, "pkgname", &pkgname)) {
131 xbps_error_printf("invalid package: missing `pkgname` property\n");
132 return -EINVAL;
133 }
134 if (!xbps_dictionary_set(ctx->seen, pkgname, placeholder)) {
135 xbps_error_printf("out of memory\n");
136 return -ENOMEM;
137 }
138
139 if (xbps_transaction_pkg_type(pkgd) == XBPS_TRANS_REMOVE)
140 continue;
141
142 array = xbps_dictionary_get(pkgd, "shlib-provides");
143 if (array) {
144 int r = collect_shlib_array(ctx, array);
145 if (r < 0)
146 return r;
147 }
148 }
149
150 iter = xbps_dictionary_iterator(ctx->xhp->pkgdb);
151 if (!iter) {
152 xbps_error_printf("out of memory\n");
153 return -ENOMEM;
154 }
155
156 while ((obj = xbps_object_iterator_next(iter))) {
157 xbps_array_t array;
158 xbps_dictionary_t pkgd;
159 const char *pkgname = NULL;
160
161 pkgname = xbps_dictionary_keysym_cstring_nocopy(obj);
162 /* ignore internal objs */
163 if (strncmp(pkgname, "_XBPS_", 6) == 0)
164 continue;
165
166 pkgd = xbps_dictionary_get_keysym(ctx->xhp->pkgdb, obj);
167
168 if (xbps_dictionary_get(ctx->seen, pkgname))
169 continue;
170
171 array = xbps_dictionary_get(pkgd, "shlib-provides");
172 if (array) {
173 int r = collect_shlib_array(ctx, array);
174 if (r < 0)
175 return r;
176 }
177 }
178
179 xbps_object_iterator_release(iter);
180 return 0;
181}
182
183static int
184check_shlibs(struct shlib_ctx *ctx, xbps_array_t pkgs)
185{
186 xbps_object_iterator_t iter;
187 xbps_object_t obj;
188
189 for (unsigned int i = 0; i < xbps_array_count(pkgs); i++) {
190 xbps_array_t array;
191 xbps_dictionary_t pkgd = xbps_array_get(pkgs, i);
193
194 if (ttype == XBPS_TRANS_HOLD || ttype == XBPS_TRANS_REMOVE)
195 continue;
196
197 array = xbps_dictionary_get(pkgd, "shlib-requires");
198 if (!array)
199 continue;
200 for (unsigned int j = 0; j < xbps_array_count(array); j++) {
201 const char *pkgver = NULL;
202 const char *shlib = NULL;
203 char *missing;
204 if (!xbps_array_get_cstring_nocopy(array, j, &shlib))
205 return -EINVAL;
206 if (shlib_entry_find(ctx->entries, shlib))
207 continue;
208 if (!xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver))
209 return -EINVAL;
210 missing = xbps_xasprintf(
211 "%s: broken, unresolvable shlib `%s'",
212 pkgver, shlib);
213 if (!xbps_array_add_cstring_nocopy(ctx->missing, missing)) {
214 xbps_error_printf("out of memory\n");
215 return -ENOMEM;
216 }
217 }
218 }
219
220 iter = xbps_dictionary_iterator(ctx->xhp->pkgdb);
221 if (!iter) {
222 xbps_error_printf("out of memory\n");
223 return -ENOMEM;
224 }
225
226 while ((obj = xbps_object_iterator_next(iter))) {
227 xbps_array_t array;
228 xbps_dictionary_t pkgd;
229 const char *pkgname = NULL;
230
231 pkgname = xbps_dictionary_keysym_cstring_nocopy(obj);
232 /* ignore internal objs */
233 if (strncmp(pkgname, "_XBPS_", 6) == 0)
234 continue;
235
236 pkgd = xbps_dictionary_get_keysym(ctx->xhp->pkgdb, obj);
237
238 if (xbps_dictionary_get(ctx->seen, pkgname))
239 continue;
240
241 array = xbps_dictionary_get(pkgd, "shlib-requires");
242 if (!array)
243 continue;
244 for (unsigned int i = 0; i < xbps_array_count(array); i++) {
245 const char *pkgver = NULL;
246 const char *shlib = NULL;
247 char *missing;
248 if (!xbps_array_get_cstring_nocopy(array, i, &shlib))
249 return -EINVAL;
250 if (shlib_entry_find(ctx->entries, shlib))
251 continue;
252 if (!xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver))
253 return -EINVAL;
254 missing = xbps_xasprintf(
255 "%s: broken, unresolvable shlib `%s'", pkgver,
256 shlib);
257 if (!xbps_array_add_cstring_nocopy(ctx->missing, missing)) {
258 xbps_error_printf("out of memory\n");
259 return -ENOMEM;
260 }
261 }
262 }
263
264 xbps_object_iterator_release(iter);
265 return 0;
266}
267
268bool HIDDEN
269xbps_transaction_check_shlibs(struct xbps_handle *xhp, xbps_array_t pkgs)
270{
271 struct shlib_entry *entry, *tmp;
272 struct shlib_ctx ctx = { .xhp = xhp };
273 int r;
274
275 ctx.missing = xbps_dictionary_get(xhp->transd, "missing_shlibs");
276
277 r = collect_shlibs(&ctx, pkgs);
278 if (r < 0)
279 goto err;
280
281 r = check_shlibs(&ctx, pkgs);
282 if (r < 0)
283 goto err;
284
285 if (xbps_array_count(ctx.missing) == 0)
286 xbps_dictionary_remove(xhp->transd, "missing_shlibs");
287
288 r = 0;
289err:
290 HASH_ITER(hh, ctx.entries, entry, tmp) {
291 HASH_DEL(ctx.entries, entry);
292 free(entry);
293 }
294 if (ctx.seen)
295 xbps_object_release(ctx.seen);
296 return r == 0;
297}
xbps_dictionary_t pkgdb
Definition xbps.h:590
xbps_dictionary_t transd
Definition xbps.h:597
Generic XBPS structure handler for initialization.
Definition xbps.h:560
void void xbps_error_printf(const char *fmt,...)
Prints error messages to stderr.
Definition log.c:98
xbps_trans_type_t xbps_transaction_pkg_type(xbps_dictionary_t pkg_repod)
xbps_trans_type_t
Definition xbps.h:1390
char * xbps_xasprintf(const char *fmt,...) __attribute__((format(printf