diff --git a/lib/transaction_check_shlibs.c b/lib/transaction_check_shlibs.c index 7495fda0..6f27dc5a 100644 --- a/lib/transaction_check_shlibs.c +++ b/lib/transaction_check_shlibs.c @@ -1,5 +1,6 @@ /*- * Copyright (c) 2014-2020 Juan Romero Pardines. + * Copyright (c) 2025-2026 Duncan Overbruck . * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -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; }; @@ -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)); @@ -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) { @@ -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; } @@ -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; } @@ -174,6 +196,7 @@ 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; @@ -181,27 +204,33 @@ check_shlibs(struct shlib_ctx *ctx, xbps_array_t pkgs) 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(); } } @@ -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 */ @@ -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(); } } @@ -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; diff --git a/tests/xbps/libxbps/shell/update_shlibs_test.sh b/tests/xbps/libxbps/shell/update_shlibs_test.sh index f3f953b8..cf108dd9 100644 --- a/tests/xbps/libxbps/shell/update_shlibs_test.sh +++ b/tests/xbps/libxbps/shell/update_shlibs_test.sh @@ -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 @@ -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 }