Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 52 additions & 17 deletions lib/transaction_check_shlibs.c
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/*-
* Copyright (c) 2014-2020 Juan Romero Pardines.
* Copyright (c) 2025-2026 Duncan Overbruck <mail@duncano.de>.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
Expand Down Expand Up @@ -56,6 +57,10 @@ struct shlib_entry {
struct shlib_ctx {
struct xbps_handle *xhp;
struct shlib_entry *entries;
// XXX: _32bit is a big hack because there is nothing allowing us
// to separate 32bit shlibs from normal shlibs, so we collect them
// in a separate hashmap based on the package name ending with `-32bit`.
struct shlib_entry *entries_32bit;
xbps_dictionary_t seen;
xbps_array_t missing;
};
Expand All @@ -69,9 +74,9 @@ shlib_entry_find(struct shlib_entry *head, const char *name)
}

static struct shlib_entry *
shlib_entry_get(struct shlib_ctx *ctx, const char *name)
shlib_entry_get(struct shlib_entry **head, const char *name)
{
struct shlib_entry *res = shlib_entry_find(ctx->entries, name);
struct shlib_entry *res = shlib_entry_find(*head, name);
if (res)
return res;
res = calloc(1, sizeof(*res));
Expand All @@ -80,25 +85,34 @@ shlib_entry_get(struct shlib_ctx *ctx, const char *name)
return NULL;
}
res->name = name;
HASH_ADD_STR(ctx->entries, name, res);
HASH_ADD_STR(*head, name, res);
return res;
}

static int
collect_shlib_array(struct shlib_ctx *ctx, xbps_array_t array)
collect_shlib_array(struct shlib_entry **head, xbps_array_t array)
{
for (unsigned int i = 0; i < xbps_array_count(array); i++) {
struct shlib_entry *entry;
const char *shlib = NULL;
if (!xbps_array_get_cstring_nocopy(array, i, &shlib))
return -EINVAL;
entry = shlib_entry_get(ctx, shlib);
entry = shlib_entry_get(head, shlib);
if (!entry)
return -errno;
}
return 0;
}

static bool
endswith(const char *s, const char *end, size_t len)
{
size_t slen = strlen(s);
if (slen <= len)
return false;
return strcmp(s + (slen - len), end) == 0;
}

static int
collect_shlibs(struct shlib_ctx *ctx, xbps_array_t pkgs)
{
Expand Down Expand Up @@ -134,7 +148,11 @@ collect_shlibs(struct shlib_ctx *ctx, xbps_array_t pkgs)

array = xbps_dictionary_get(pkgd, "shlib-provides");
if (array) {
int r = collect_shlib_array(ctx, array);
bool is32bit =
endswith(pkgname, "-32bit", sizeof("-32bit") - 1);
int r = collect_shlib_array(
!is32bit ? &ctx->entries : &ctx->entries_32bit,
array);
if (r < 0)
return r;
}
Expand All @@ -161,7 +179,11 @@ collect_shlibs(struct shlib_ctx *ctx, xbps_array_t pkgs)

array = xbps_dictionary_get(pkgd, "shlib-provides");
if (array) {
int r = collect_shlib_array(ctx, array);
bool is32bit =
endswith(pkgname, "-32bit", sizeof("-32bit") - 1);
int r = collect_shlib_array(
!is32bit ? &ctx->entries : &ctx->entries_32bit,
array);
if (r < 0)
return r;
}
Expand All @@ -174,34 +196,41 @@ collect_shlibs(struct shlib_ctx *ctx, xbps_array_t pkgs)
static int
check_shlibs(struct shlib_ctx *ctx, xbps_array_t pkgs)
{
char msg[1024];
xbps_object_iterator_t iter;
xbps_object_t obj;

for (unsigned int i = 0; i < xbps_array_count(pkgs); i++) {
xbps_array_t array;
xbps_dictionary_t pkgd = xbps_array_get(pkgs, i);
xbps_trans_type_t ttype = xbps_transaction_pkg_type(pkgd);
const char *pkgname = NULL;
bool is32bit;

if (ttype == XBPS_TRANS_HOLD || ttype == XBPS_TRANS_REMOVE)
continue;

if (!xbps_dictionary_get_cstring_nocopy(pkgd, "pkgname", &pkgname))
return -EINVAL;

is32bit = endswith(pkgname, "-32bit", sizeof("-32bit") - 1);

array = xbps_dictionary_get(pkgd, "shlib-requires");
if (!array)
continue;
for (unsigned int j = 0; j < xbps_array_count(array); j++) {
const char *pkgver = NULL;
const char *shlib = NULL;
char *missing;
if (!xbps_array_get_cstring_nocopy(array, j, &shlib))
return -EINVAL;
if (shlib_entry_find(ctx->entries, shlib))
if (shlib_entry_find(!is32bit ? ctx->entries : ctx->entries_32bit, shlib))
continue;
if (!xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver))
return -EINVAL;
missing = xbps_xasprintf(
"%s: broken, unresolvable shlib `%s'",
pkgver, shlib);
if (!xbps_array_add_cstring_nocopy(ctx->missing, missing))
snprintf(msg, sizeof(msg),
"%s: broken, unresolvable shlib `%s'", pkgver,
shlib);
if (!xbps_array_add_cstring_nocopy(ctx->missing, msg))
return xbps_error_oom();
}
}
Expand All @@ -214,6 +243,7 @@ check_shlibs(struct shlib_ctx *ctx, xbps_array_t pkgs)
xbps_array_t array;
xbps_dictionary_t pkgd;
const char *pkgname = NULL;
bool is32bit;

pkgname = xbps_dictionary_keysym_cstring_nocopy(obj);
/* ignore internal objs */
Expand All @@ -225,23 +255,24 @@ check_shlibs(struct shlib_ctx *ctx, xbps_array_t pkgs)
if (xbps_dictionary_get(ctx->seen, pkgname))
continue;

is32bit = endswith(pkgname, "-32bit", sizeof("-32bit") - 1);

array = xbps_dictionary_get(pkgd, "shlib-requires");
if (!array)
continue;
for (unsigned int i = 0; i < xbps_array_count(array); i++) {
const char *pkgver = NULL;
const char *shlib = NULL;
char *missing;
if (!xbps_array_get_cstring_nocopy(array, i, &shlib))
return -EINVAL;
if (shlib_entry_find(ctx->entries, shlib))
if (shlib_entry_find(!is32bit ? ctx->entries : ctx->entries_32bit, shlib))
continue;
if (!xbps_dictionary_get_cstring_nocopy(pkgd, "pkgver", &pkgver))
return -EINVAL;
missing = xbps_xasprintf(
snprintf(msg, sizeof(msg),
"%s: broken, unresolvable shlib `%s'", pkgver,
shlib);
if (!xbps_array_add_cstring_nocopy(ctx->missing, missing))
if (!xbps_array_add_cstring_nocopy(ctx->missing, msg))
return xbps_error_oom();
}
}
Expand Down Expand Up @@ -276,6 +307,10 @@ xbps_transaction_check_shlibs(struct xbps_handle *xhp, xbps_array_t pkgs)
HASH_DEL(ctx.entries, entry);
free(entry);
}
HASH_ITER(hh, ctx.entries_32bit, entry, tmp) {
HASH_DEL(ctx.entries_32bit, entry);
free(entry);
}
if (ctx.seen)
xbps_object_release(ctx.seen);
return r == 0;
Expand Down
70 changes: 70 additions & 0 deletions tests/xbps/libxbps/shell/update_shlibs_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -255,6 +255,74 @@ shlib_provides_replaces_body() {
atf_check_equal $? 2
}

atf_test_case shlib_32bit

shlib_32bit_head() {
atf_set "descr" "32bit shlib-provides does not satisfy non-32bit shlib-requires"
}

shlib_32bit_body() {
mkdir -p repo pkg_A pkg_B
cd repo
atf_check -o ignore -- xbps-create -A noarch -n A-1.0_1 -s "A pkg" --shlib-provides "libfoo.so.1" ../pkg_A
atf_check -o ignore -- xbps-create -A noarch -n A-32bit-1.0_1 -s "A pkg" --shlib-provides "libfoo.so.1" ../pkg_A
atf_check -o ignore -- xbps-create -A noarch -n B-1.0_1 -s "B-32bit pkg" --shlib-requires "libfoo.so.1" ../pkg_B
atf_check -o ignore -- xbps-create -A noarch -n B-32bit-1.0_1 -s "B-32bit pkg" --shlib-requires "libfoo.so.1" ../pkg_B
atf_check -o ignore -e ignore -- xbps-rindex -a $PWD/*.xbps
cd ..

atf_check \
-s exit:8 \
-e match:"B-32bit-1\.0_1: broken, unresolvable shlib \`libfoo\.so\.1'" \
-- xbps-install -R repo -r root -ny A B-32bit
atf_check \
-s exit:8 \
-e match:"B-1\.0_1: broken, unresolvable shlib \`libfoo\.so\.1'" \
-- xbps-install -R repo -r root -ny A-32bit B
}

atf_test_case shlib_bump_32bit

shlib_bump_32bit_head() {
atf_set "descr" "Tests for pkg updates: update pkg with 32bit soname bump"
}

shlib_bump_32bit_body() {
mkdir -p repo pkg_A pkg_B
cd repo
atf_check -o ignore -- xbps-create -A noarch -n A-1.0_1 -s "A pkg" --shlib-provides "libfoo.so.1" ../pkg_A
atf_check -o ignore -- xbps-create -A noarch -n A-32bit-1.0_1 -s "A-32bit pkg" --shlib-provides "libfoo.so.1" ../pkg_A
atf_check -o ignore -- xbps-create -A noarch -n B-1.0_1 -s "B pkg" --dependencies "A>=0" --shlib-requires "libfoo.so.1" ../pkg_B
atf_check -o ignore -- xbps-create -A noarch -n B-32bit-1.0_1 -s "B-32bit pkg" --dependencies "A-32bit>=0" --shlib-requires "libfoo.so.1" ../pkg_B
atf_check -o ignore -e ignore -- xbps-rindex -a $PWD/*.xbps
cd ..

atf_check -o ignore -e ignore -- xbps-install -C empty.conf -r root --repository=$PWD/repo -y B B-32bit

cd repo
atf_check -o ignore -- xbps-create -A noarch -n A-2.0_1 -s "A pkg" --shlib-provides "libfoo.so.2" ../pkg_A
atf_check -o ignore -e ignore -- xbps-rindex -a $PWD/*.xbps
cd ..

# returns ENOEXEC if there are unresolved shlibs
atf_check \
-s exit:8 \
-e match:"B-1\.0_1: broken, unresolvable shlib \`libfoo\.so\.1'" \
-- xbps-install -C empty.conf -r root --repository=$PWD/repo -yu A A-32bit

cd repo
atf_check -o ignore -- xbps-create -A noarch -n A-32bit-2.0_1 -s "A-32bit pkg" --shlib-provides "libfoo.so.2" ../pkg_A
atf_check -o ignore -- xbps-create -A noarch -n B-1.0_2 -s "B pkg" --dependencies "A>=0" --shlib-requires "libfoo.so.2" ../pkg_B
atf_check -o ignore -e ignore -- xbps-rindex -a $PWD/*.xbps
cd ..

# returns ENOEXEC if there are unresolved shlibs
atf_check \
-s exit:8 \
-e match:"B-32bit-1\.0_1: broken, unresolvable shlib \`libfoo\.so\.1'" \
-- xbps-install -C empty.conf -r root --repository=$PWD/repo -yu A A-32bit
}

atf_init_test_cases() {
atf_add_test_case shlib_bump
atf_add_test_case shlib_bump_incomplete_revdep_in_trans
Expand All @@ -263,4 +331,6 @@ atf_init_test_cases() {
atf_add_test_case shlib_bump_versioned
atf_add_test_case shlib_unknown_provider
atf_add_test_case shlib_provides_replaces
atf_add_test_case shlib_32bit
atf_add_test_case shlib_bump_32bit
}
Loading