diff --git a/NEWS b/NEWS index d391f3a7..5812d934 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,8 @@ xbps-0.27 (???): + * libxbps: make sure that base symlinks in the root directory are never removed, + due to /usr switch in Void. This will also be harmless in other systems. + * The testsuite now contains more tests than ever and will be extended as new issues appear. diff --git a/lib/package_remove.c b/lib/package_remove.c index 0c91f61d..e8e29b01 100644 --- a/lib/package_remove.c +++ b/lib/package_remove.c @@ -33,6 +33,21 @@ #include "xbps_api_impl.h" +#ifndef __arraycount +# define __arraycount(a) (sizeof(a) / sizeof(*(a))) +#endif + +/* These are symlinks in Void and must not be removed */ +static const char *basesymlinks[] = { + "/bin", + "/sbin", + "/lib", + "/lib32", + "/lib64", + "/usr/lib64", + "/var/run", +}; + int HIDDEN xbps_remove_pkg_files(struct xbps_handle *xhp, xbps_dictionary_t dict, @@ -44,9 +59,10 @@ xbps_remove_pkg_files(struct xbps_handle *xhp, xbps_object_iterator_t iter; xbps_object_t obj; const char *file, *sha256, *curobj = NULL; - char *path = NULL, *pkgname = NULL; + char *path = NULL; char buf[PATH_MAX]; int rv = 0; + bool found; assert(xbps_object_type(dict) == XBPS_TYPE_DICTIONARY); assert(key != NULL); @@ -68,9 +84,6 @@ xbps_remove_pkg_files(struct xbps_handle *xhp, else if (strcmp(key, "dirs") == 0) curobj = "directory"; - pkgname = xbps_pkg_name(pkgver); - assert(pkgname); - while ((obj = xbps_object_iterator_next(iter))) { xbps_dictionary_get_cstring_nocopy(obj, "file", &file); path = xbps_xasprintf("%s/%s", xhp->rootdir, file); @@ -141,6 +154,22 @@ xbps_remove_pkg_files(struct xbps_handle *xhp, continue; } } + /* + * Make sure to not remove any symlink of root directory. + */ + found = false; + for (uint8_t i = 0; i < __arraycount(basesymlinks); i++) { + if (strcmp(file, basesymlinks[i]) == 0) { + found = true; + xbps_dbg_printf(xhp, "[remove] %s ignoring " + "%s removal\n", pkgver, file); + break; + } + } + if (found) { + free(path); + continue; + } /* * Remove the object if possible. */ @@ -158,7 +187,6 @@ xbps_remove_pkg_files(struct xbps_handle *xhp, free(path); } xbps_object_iterator_release(iter); - free(pkgname); return rv; } diff --git a/tests/xbps/libxbps/common/Kyuafile b/tests/xbps/libxbps/common/Kyuafile index f600ae48..cfefa124 100644 --- a/tests/xbps/libxbps/common/Kyuafile +++ b/tests/xbps/libxbps/common/Kyuafile @@ -12,5 +12,6 @@ atf_test_program{name="issue6_test"} atf_test_program{name="issue18_test"} atf_test_program{name="issue20_test"} atf_test_program{name="conf_files_test"} +atf_test_program{name="remove_test"} include('find_pkg_orphans/Kyuafile') include('pkgdb/Kyuafile') diff --git a/tests/xbps/libxbps/shell/Makefile b/tests/xbps/libxbps/shell/Makefile index 7e077fd0..c73a00d8 100644 --- a/tests/xbps/libxbps/shell/Makefile +++ b/tests/xbps/libxbps/shell/Makefile @@ -1,7 +1,7 @@ TOPDIR = ../../../.. -include $(TOPDIR)/config.mk -TESTSHELL = conf_files_test issue6_test issue18_test issue20_test +TESTSHELL = conf_files_test issue6_test issue18_test issue20_test remove_test include ../Makefile.inc include $(TOPDIR)/mk/test.mk diff --git a/tests/xbps/libxbps/shell/remove_test.sh b/tests/xbps/libxbps/shell/remove_test.sh new file mode 100644 index 00000000..1e8e4753 --- /dev/null +++ b/tests/xbps/libxbps/shell/remove_test.sh @@ -0,0 +1,37 @@ +#! /usr/bin/env atf-sh + +# 1st test: make sure that base symlinks on rootdir are not removed. +atf_test_case keep_base_symlinks + +keep_base_symlinks_head() { + atf_set "descr" "Tests for package removal: keep base symlinks test" +} + +keep_base_symlinks_body() { + mkdir -p root/usr/bin + ln -sfr root/usr/bin root/bin + + mkdir some_repo + mkdir -p pkg_A/usr/bin pkg_A/bin + touch -f pkg_A/usr/bin/foo + cd some_repo + xbps-create -A noarch -n foo-1.0_1 -s "foo pkg" ../pkg_A + atf_check_equal $? 0 + xbps-rindex -a *.xbps + atf_check_equal $? 0 + cd .. + xbps-install -r root --repository=some_repo -y foo + atf_check_equal $? 0 + xbps-remove -r root -y foo + atf_check_equal $? 0 + if [ -h root/bin ]; then + rv=0 + else + rv=1 + fi + atf_check_equal $rv 0 +} + +atf_init_test_cases() { + atf_add_test_case keep_base_symlinks +}