XBPS Library API 20260225
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) {
80 return NULL;
81 }
82 res->name = name;
83 HASH_ADD_STR(ctx->entries, name, res);
84 return res;
85}
86
87static int
88collect_shlib_array(struct shlib_ctx *ctx, xbps_array_t array)
89{
90 for (unsigned int i = 0; i < xbps_array_count(array); i++) {
91 struct shlib_entry *entry;
92 const char *shlib = NULL;
93 if (!xbps_array_get_cstring_nocopy(array, i, &shlib))
94 return -EINVAL;
95 entry = shlib_entry_get(ctx, shlib);
96 if (!entry)
97 return -errno;
98 }
99 return 0;
100}
101
102static int
103collect_shlibs(struct shlib_ctx *ctx, xbps_array_t pkgs)
104{
105 xbps_object_t obj;
106 xbps_object_iterator_t iter;
107 xbps_bool_t placeholder;
108
109 // can't set null values to xbps_dictionary so just use one boolean
110 placeholder = xbps_bool_create(true);
111 if (!placeholder)
112 return xbps_error_oom();
113
114 ctx->seen = xbps_dictionary_create();
115 if (!ctx->seen)
116 return xbps_error_oom();
117
118 for (unsigned int i = 0; i < xbps_array_count(pkgs); i++) {
119 const char *pkgname;
120 xbps_dictionary_t pkgd = xbps_array_get(pkgs, i);
121 xbps_array_t array;
122
123 if (xbps_transaction_pkg_type(pkgd) == XBPS_TRANS_HOLD)
124 continue;
125 if (!xbps_dictionary_get_cstring_nocopy(pkgd, "pkgname", &pkgname)) {
126 xbps_error_printf("invalid package: missing `pkgname` property\n");
127 return -EINVAL;
128 }
129 if (!xbps_dictionary_set(ctx->seen, pkgname, placeholder))
130 return xbps_error_oom();
131
132 if (xbps_transaction_pkg_type(pkgd) == XBPS_TRANS_REMOVE)
133 continue;
134
135 array = xbps_dictionary_get(pkgd, "shlib-provides");
136 if (array) {
137 int r = collect_shlib_array(ctx, array);
138 if (r < 0)
139 return r;
140 }
141 }
142
143 iter = xbps_dictionary_iterator(ctx->xhp->pkgdb);
144 if (!iter)
145 return xbps_error_oom();
146
147 while ((obj = xbps_object_iterator_next(iter))) {
148 xbps_array_t array;
149 xbps_dictionary_t pkgd;
150 const char *pkgname = NULL;
151
152 pkgname = xbps_dictionary_keysym_cstring_nocopy(obj);
153 /* ignore internal objs */
154 if (strncmp(pkgname, "_XBPS_", 6) == 0)
155 continue;
156
157 pkgd = xbps_dictionary_get_keysym(ctx->xhp->pkgdb, obj);
158
159 if (xbps_dictionary_get(ctx->seen, pkgname))
160 continue;
161
162 array = xbps_dictionary_get(pkgd, "shlib-provides");
163 if (array) {
164 int r = collect_shlib_array(ctx, array);
165 if (r < 0)
166 return r;
167 }
168 }
169
170 xbps_object_iterator_release(iter);
171 return 0;
172}
173
174static int
175check_shlibs(struct shlib_ctx *ctx, xbps_array_t pkgs)
176{
177 xbps_object_iterator_t iter;
178 xbps_object_t obj;
179
180 for (unsigned int i = 0; i < xbps_array_count(pkgs); i++) {
181 xbps_array_t array;
182 xbps_dictionary_t pkgd = xbps_array_get(pkgs, i);
184
185 if (ttype == XBPS_TRANS_HOLD || ttype == XBPS_TRANS_REMOVE)
186 continue;
187
188 array = xbps_dictionary_get(pkgd, "shlib-requires");
189 if (!array)
190 continue;
191 for (unsigned int j = 0; j < xbps_array_count(array); j++) {
192 const char *pkgver = NULL;
193 const char *shlib = NULL;
194 char *missing;
195 if (!xbps_array_get_cstring_nocopy(array, j, &shlib))
196 return -EINVAL;
197 if (shlib_entry_find(ctx->entries, shlib))
198 continue;
199 if (!xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver))
200 return -EINVAL;
201 missing = xbps_xasprintf(
202 "%s: broken, unresolvable shlib `%s'",
203 pkgver, shlib);
204 if (!xbps_array_add_cstring_nocopy(ctx->missing, missing))
205 return xbps_error_oom();
206 }
207 }
208
209 iter = xbps_dictionary_iterator(ctx->xhp->pkgdb);
210 if (!iter)
211 return xbps_error_oom();
212
213 while ((obj = xbps_object_iterator_next(iter))) {
214 xbps_array_t array;
215 xbps_dictionary_t pkgd;
216 const char *pkgname = NULL;
217
218 pkgname = xbps_dictionary_keysym_cstring_nocopy(obj);
219 /* ignore internal objs */
220 if (strncmp(pkgname, "_XBPS_", 6) == 0)
221 continue;
222
223 pkgd = xbps_dictionary_get_keysym(ctx->xhp->pkgdb, obj);
224
225 if (xbps_dictionary_get(ctx->seen, pkgname))
226 continue;
227
228 array = xbps_dictionary_get(pkgd, "shlib-requires");
229 if (!array)
230 continue;
231 for (unsigned int i = 0; i < xbps_array_count(array); i++) {
232 const char *pkgver = NULL;
233 const char *shlib = NULL;
234 char *missing;
235 if (!xbps_array_get_cstring_nocopy(array, i, &shlib))
236 return -EINVAL;
237 if (shlib_entry_find(ctx->entries, shlib))
238 continue;
239 if (!xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver))
240 return -EINVAL;
241 missing = xbps_xasprintf(
242 "%s: broken, unresolvable shlib `%s'", pkgver,
243 shlib);
244 if (!xbps_array_add_cstring_nocopy(ctx->missing, missing))
245 return xbps_error_oom();
246 }
247 }
248
249 xbps_object_iterator_release(iter);
250 return 0;
251}
252
253bool HIDDEN
254xbps_transaction_check_shlibs(struct xbps_handle *xhp, xbps_array_t pkgs)
255{
256 struct shlib_entry *entry, *tmp;
257 struct shlib_ctx ctx = { .xhp = xhp };
258 int r;
259
260 ctx.missing = xbps_dictionary_get(xhp->transd, "missing_shlibs");
261
262 r = collect_shlibs(&ctx, pkgs);
263 if (r < 0)
264 goto err;
265
266 r = check_shlibs(&ctx, pkgs);
267 if (r < 0)
268 goto err;
269
270 if (xbps_array_count(ctx.missing) == 0)
271 xbps_dictionary_remove(xhp->transd, "missing_shlibs");
272
273 r = 0;
274err:
275 HASH_ITER(hh, ctx.entries, entry, tmp) {
276 HASH_DEL(ctx.entries, entry);
277 free(entry);
278 }
279 if (ctx.seen)
280 xbps_object_release(ctx.seen);
281 return r == 0;
282}
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
#define xbps_error_oom()
Log out of memory condition.
Definition xbps.h:776
void xbps_error_printf(const char *fmt,...)
Prints error messages to stderr.
Definition log.c:93
xbps_trans_type_t xbps_transaction_pkg_type(xbps_dictionary_t pkg_repod)
xbps_trans_type_t
Definition xbps.h:1400
char * xbps_xasprintf(const char *fmt,...) __attribute__((format(printf