diff --git a/bin/xbps-rindex/Makefile b/bin/xbps-rindex/Makefile index 6dc200fb4..bdacd6724 100644 --- a/bin/xbps-rindex/Makefile +++ b/bin/xbps-rindex/Makefile @@ -2,7 +2,7 @@ TOPDIR = ../.. -include $(TOPDIR)/config.mk BIN = xbps-rindex -OBJS = main.o index-add.o index-clean.o remove-obsoletes.o repoflush.o sign.o +OBJS = main.o index-add.o index-clean.o index-remove.o remove-obsoletes.o repoflush.o sign.o include $(TOPDIR)/mk/prog.mk diff --git a/bin/xbps-rindex/defs.h b/bin/xbps-rindex/defs.h index 6368d056e..fc74f140f 100644 --- a/bin/xbps-rindex/defs.h +++ b/bin/xbps-rindex/defs.h @@ -71,6 +71,9 @@ int index_clean(struct xbps_handle *, const char *, bool, const char *); /* From remove-obsoletes.c */ int remove_obsoletes(struct xbps_handle *, const char *); +/* From index-remove.c */ +int index_remove(struct xbps_handle *, int, int, char **, const char *); + /* From sign.c */ int sign_repo(struct xbps_handle *, const char *, const char *, const char *, const char *); diff --git a/bin/xbps-rindex/index-remove.c b/bin/xbps-rindex/index-remove.c new file mode 100644 index 000000000..a427760d0 --- /dev/null +++ b/bin/xbps-rindex/index-remove.c @@ -0,0 +1,157 @@ +/*- + * Copyright (c) 2012-2015 Juan Romero Pardines. + * Copyright (c) 2019-2020 Piotr Wójcik + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include "defs.h" + +static xbps_dictionary_t idx; + +int +index_remove(struct xbps_handle *xhp, int args, int argmax, char **argv, const char *compression) +{ + struct xbps_repo *repo = NULL; + char *tmprepodir = NULL; + const char *repodir = NULL; + char *rlockfname = NULL; + int rv = 0, rlockfd = -1; + + assert(argv); + + if ((tmprepodir = strdup(argv[args])) == NULL) + return ENOMEM; + repodir = dirname(tmprepodir); + if (!xbps_repo_lock(xhp, repodir, &rlockfd, &rlockfname)) { + rv = errno; + fprintf(stderr, "%s: cannot lock repository: %s\n", + _XBPS_RINDEX, strerror(rv)); + goto earlyout; + } + repo = xbps_repo_public_open(xhp, repodir); + if (repo == NULL) { + rv = errno; + fprintf(stderr, "%s: cannot read repository %s data: %s\n", + _XBPS_RINDEX, repodir, strerror(errno)); + goto earlyout; + } + if (repo->idx == NULL) { + fprintf(stderr, "%s: incomplete repository data file!\n", _XBPS_RINDEX); + rv = EINVAL; + goto earlyout; + } + idx = xbps_dictionary_copy_mutable(repo->idx); + + for (int i = args; i < argmax; i++) { + xbps_dictionary_t curpkgd = NULL; + const char *pkg = argv[i], *opkgver = NULL; + char *pkgver = NULL, *arch = NULL; + char pkgname[XBPS_NAME_SIZE]; + + /* + * Take package properties from passed path. + */ + assert(pkg); + pkgver = xbps_binpkg_pkgver(pkg); + if (!xbps_pkg_name(pkgname, sizeof(pkgname), pkgver)) { + fprintf(stderr, "%s: argument %s doesn't look like path to binary package\n", _XBPS_RINDEX, pkg); + free(pkgver); + goto out; + } + arch = xbps_binpkg_arch(pkg); + if (!xbps_pkg_arch_match(xhp, arch, NULL)) { + fprintf(stderr, "%s: ignoring %s, unmatched arch (%s)\n", _XBPS_RINDEX, pkgver, arch); + goto again; + } + + /* + * Check if this package exists already in the index + */ + curpkgd = xbps_dictionary_get(idx, pkgname); + if (!curpkgd) { + if (errno == ENOENT) { + xbps_dbg_printf(xhp, "Package %s isn't indexed in %s, skipping.\n", + pkgname, repodir); + goto again; + } + rv = errno; + fprintf(stderr, "%s: cannot read metadata of %s from repodata\n", _XBPS_RINDEX, pkgname); + free(arch); + free(pkgver); + goto out; + } + + + /* + * Unindex. + */ + xbps_dictionary_get_cstring_nocopy(curpkgd, "pkgver", &opkgver); + if (opkgver) { + printf("index: unindexing %s\n", opkgver); + } else { + printf("index: unindexing some version of %s\n", pkgname); + } + xbps_dictionary_remove(idx, pkgname); + +again: + free(arch); + free(pkgver); + } + + /* + * Generate repository data files. + */ + if (!xbps_dictionary_equals(idx, repo->idx)) { + if (!repodata_flush(xhp, repodir, "repodata", idx, repo->idxmeta, compression)) { + rv = errno; + fprintf(stderr, "%s: failed to write repodata: %s\n", + _XBPS_RINDEX, strerror(errno)); + goto out; + } + } + printf("index: %u packages in index.\n", xbps_dictionary_count(idx)); + + +out: + xbps_object_release(idx); + +earlyout: + if (repo) + xbps_repo_close(repo); + xbps_repo_unlock(rlockfd, rlockfname); + free(tmprepodir); + + return rv; +} diff --git a/bin/xbps-rindex/main.c b/bin/xbps-rindex/main.c index a796094c1..0fb0f864a 100644 --- a/bin/xbps-rindex/main.c +++ b/bin/xbps-rindex/main.c @@ -49,6 +49,7 @@ usage(bool fail) "MODE\n" " -a, --add ... Add package(s) to repository index\n" " -c, --clean Clean repository index\n" + " -R --remove ... Removes package(s) from repository\n" " -r, --remove-obsoletes Removes obsolete packages from repository\n" " -s, --sign Initialize repository metadata signature\n" " -S, --sign-pkg ... Sign binary package archive\n"); @@ -58,13 +59,14 @@ usage(bool fail) int main(int argc, char **argv) { - const char *shortopts = "acdfhrsCSVv"; + const char *shortopts = "acdfhRrsCSVv"; struct option longopts[] = { { "add", no_argument, NULL, 'a' }, { "clean", no_argument, NULL, 'c' }, { "debug", no_argument, NULL, 'd' }, { "force", no_argument, NULL, 'f' }, { "help", no_argument, NULL, 'h' }, + { "remove", no_argument, NULL, 'R' }, { "remove-obsoletes", no_argument, NULL, 'r' }, { "version", no_argument, NULL, 'V' }, { "verbose", no_argument, NULL, 'v' }, @@ -79,12 +81,12 @@ main(int argc, char **argv) struct xbps_handle xh; const char *compression = NULL; const char *privkey = NULL, *signedby = NULL; - int rv, c, flags = 0; - bool add_mode, clean_mode, rm_mode, sign_mode, sign_pkg_mode, force, - hashcheck; + int rv, c, flags = 0, modes_count = 0; + bool add_mode, clean_mode, obsoletes_mode, remove_mode, sign_mode, sign_pkg_mode, + force, hashcheck; - add_mode = clean_mode = rm_mode = sign_mode = sign_pkg_mode = force = - hashcheck = false; + add_mode = clean_mode = obsoletes_mode = remove_mode = sign_mode = sign_pkg_mode = + force = hashcheck = false; while ((c = getopt_long(argc, argv, shortopts, longopts, NULL)) != -1) { switch (c) { @@ -99,9 +101,11 @@ main(int argc, char **argv) break; case 'a': add_mode = true; + modes_count++; break; case 'c': clean_mode = true; + modes_count++; break; case 'd': flags |= XBPS_FLAG_DEBUG; @@ -112,17 +116,24 @@ main(int argc, char **argv) case 'h': usage(false); /* NOTREACHED */ + case 'R': + remove_mode = true; + modes_count++; + break; case 'r': - rm_mode = true; + obsoletes_mode = true; + modes_count++; break; case 's': sign_mode = true; + modes_count++; break; case 'C': hashcheck = true; break; case 'S': sign_pkg_mode = true; + modes_count++; break; case 'v': flags |= XBPS_FLAG_VERBOSE; @@ -136,17 +147,12 @@ main(int argc, char **argv) /* NOTREACHED */ } } - if ((argc == optind) || - (!add_mode && !clean_mode && !rm_mode && !sign_mode && !sign_pkg_mode)) { + if ((argc == optind) || (modes_count == 0)) { usage(true); /* NOTREACHED */ - } else if ((add_mode && (clean_mode || rm_mode || sign_mode || sign_pkg_mode)) || - (clean_mode && (add_mode || rm_mode || sign_mode || sign_pkg_mode)) || - (rm_mode && (add_mode || clean_mode || sign_mode || sign_pkg_mode)) || - (sign_mode && (add_mode || clean_mode || rm_mode || sign_pkg_mode)) || - (sign_pkg_mode && (add_mode || clean_mode || rm_mode || sign_mode))) { + } else if (modes_count > 1) { fprintf(stderr, "Only one mode can be specified: add, clean, " - "remove-obsoletes, sign or sign-pkg.\n"); + "remove, remove-obsoletes, sign or sign-pkg.\n"); exit(EXIT_FAILURE); } @@ -163,8 +169,10 @@ main(int argc, char **argv) rv = index_add(&xh, optind, argc, argv, force, compression); else if (clean_mode) rv = index_clean(&xh, argv[optind], hashcheck, compression); - else if (rm_mode) + else if (obsoletes_mode) rv = remove_obsoletes(&xh, argv[optind]); + else if (remove_mode) + rv = index_remove(&xh, optind, argc, argv, compression); else if (sign_mode) rv = sign_repo(&xh, argv[optind], privkey, signedby, compression); else if (sign_pkg_mode) diff --git a/bin/xbps-rindex/xbps-rindex.1 b/bin/xbps-rindex/xbps-rindex.1 index affb0cf93..9912861b8 100644 --- a/bin/xbps-rindex/xbps-rindex.1 +++ b/bin/xbps-rindex/xbps-rindex.1 @@ -1,4 +1,4 @@ -.Dd February 21, 2020 +.Dd April 14, 2020 .Dt XBPS-RINDEX 1 .Sh NAME .Nm xbps-rindex @@ -56,6 +56,10 @@ Absolute path to the local repository is expected. .It Sy -c, --clean Ar /path/to/repository Removes obsolete entries found in the local repository. Absolute path to the local repository is expected. +.It Sy -R, --remove Ar /path/to/repository/binpkg.xbps ... +Removes specified packages from repository. +Absolute paths to existing binpkgs are expected. +Binpkg is not read and does not need to exist on disk. .It Sy -r, --remove-obsoletes Ar /path/to/repository Removes obsolete packages from .Ar repository . diff --git a/data/_xbps b/data/_xbps index 6a7687b7b..5f0d6a13a 100644 --- a/data/_xbps +++ b/data/_xbps @@ -208,6 +208,7 @@ _xbps_rindex() { - '(mode)' \ {-a,--add}'[Add package to repository index]' \ {-c,--clean}'[Clean repository index]' \ + {-R,--remove}'[Remove packages from repository index]' \ {-r,--remove-obsoletes}'[Removes obsolete packages from repository]' \ {-s,--sign}'[Sign repository index]' \ {-S,--sign-pkg}'[Sign binary package archive]' \ diff --git a/tests/xbps/xbps-rindex/Kyuafile b/tests/xbps/xbps-rindex/Kyuafile index 149df6f4b..15b87e552 100644 --- a/tests/xbps/xbps-rindex/Kyuafile +++ b/tests/xbps/xbps-rindex/Kyuafile @@ -4,3 +4,4 @@ test_suite("xbps-rindex") atf_test_program{name="add_test"} atf_test_program{name="clean_test"} atf_test_program{name="remove_test"} +atf_test_program{name="unindex_test"} diff --git a/tests/xbps/xbps-rindex/Makefile b/tests/xbps/xbps-rindex/Makefile index ca28e3ad3..477200729 100644 --- a/tests/xbps/xbps-rindex/Makefile +++ b/tests/xbps/xbps-rindex/Makefile @@ -1,7 +1,7 @@ TOPDIR = ../../.. -include $(TOPDIR)/config.mk -TESTSHELL = add_test clean_test remove_test +TESTSHELL = add_test clean_test remove_test unindex_test TESTSSUBDIR = xbps/xbps-rindex EXTRA_FILES = Kyuafile diff --git a/tests/xbps/xbps-rindex/unindex_test.sh b/tests/xbps/xbps-rindex/unindex_test.sh new file mode 100644 index 000000000..bfca0ac3b --- /dev/null +++ b/tests/xbps/xbps-rindex/unindex_test.sh @@ -0,0 +1,126 @@ +#! /usr/bin/env atf-sh +# Test that xbps-rindex(1) -R (remove mode) works as expected. + +atf_test_case remove_same + +remove_same_head() { + atf_set "descr" "xbps-rindex(1) -R: test removing indexed version" +} + +remove_same_body() { + mkdir -p some_repo pkg_A pkg_B pkg_C + cd some_repo + xbps-create -A noarch -n foo-a-1.0_1 -s "foo pkg a" ../pkg_A + atf_check_equal $? 0 + xbps-create -A noarch -n foo-b-1.0_1 -s "foo pkg b" ../pkg_B + atf_check_equal $? 0 + xbps-create -A noarch -n foo-c-1.0_1 -s "foo pkg c" ../pkg_C + atf_check_equal $? 0 + xbps-rindex -d -a $PWD/*.xbps + atf_check_equal $? 0 + cd .. + result="$(xbps-query -r root -C empty.conf --repository=some_repo -s '')" + expected="[-] foo-a-1.0_1 foo pkg a +[-] foo-b-1.0_1 foo pkg b +[-] foo-c-1.0_1 foo pkg c" + rv=0 + if [ "$result" != "$expected" ]; then + echo "result: $result" + echo "expected: $expected" + rv=1 + fi + atf_check_equal $rv 0 + xbps-rindex -R $PWD/some_repo/foo-a-1.0_1.noarch.xbps $PWD/some_repo/foo-c-1.0_1.noarch.xbps + atf_check_equal $? 0 + result="$(xbps-query -r root -C empty.conf --repository=some_repo -s '')" + expected="[-] foo-b-1.0_1 foo pkg b" + rv=0 + if [ "$result" != "$expected" ]; then + echo "result: $result" + echo "expected: $expected" + rv=1 + fi + atf_check_equal $rv 0 +} + +atf_test_case remove_newer + +remove_newer_head() { + atf_set "descr" "xbps-rindex(1) -R: test removing newer version" +} + +remove_newer_body() { + mkdir -p some_repo pkg_A-2 pkg_A-1.0 + cd some_repo + xbps-create -A noarch -n foo-a-2_1 -s "foo pkg a" ../pkg_A-2 + atf_check_equal $? 0 + xbps-rindex -d -a $PWD/*.xbps + atf_check_equal $? 0 + cd .. + result="$(xbps-query -r root -C empty.conf --repository=some_repo -s '')" + expected="[-] foo-a-2_1 foo pkg a" + rv=0 + if [ "$result" != "$expected" ]; then + echo "result: $result" + echo "expected: $expected" + rv=1 + fi + atf_check_equal $rv 0 + cd some_repo + xbps-create -A noarch -n foo-a-1.0_1 -s "foo pkg a" ../pkg_A-1.0 + atf_check_equal $? 0 + cd .. + xbps-rindex -R $PWD/some_repo/foo-a-1.0_1.noarch.xbps + atf_check_equal $? 0 + result="$(xbps-query -r root -C empty.conf --repository=some_repo -s '')" + expected="" + rv=0 + if [ "$result" != "$expected" ]; then + echo "result: $result" + echo "expected: $expected" + rv=1 + fi + atf_check_equal $rv 0 +} + + +atf_test_case remove_not_indexed + +remove_not_indexed_head() { + atf_set "descr" "xbps-rindex(1) -R: force remove test" +} + +remove_not_indexed_body() { + mkdir -p some_repo pkg_A + cd some_repo + xbps-create -A noarch -n foo-a-1.0_1 -s "foo pkg a" ../pkg_A + atf_check_equal $? 0 + xbps-rindex -d -a $PWD/*.xbps + atf_check_equal $? 0 + cd .. + result="$(xbps-query -r root -C empty.conf --repository=some_repo -s '')" + expected="[-] foo-a-1.0_1 foo pkg a" + rv=0 + if [ "$result" != "$expected" ]; then + echo "result: $result" + echo "expected: $expected" + rv=1 + fi + atf_check_equal $rv 0 + xbps-rindex -R $PWD/some_repo/removed-earlier-1_1.noarch.xbps + atf_check_equal $? 0 + result="$(xbps-query -r root -C empty.conf --repository=some_repo -s '')" + rv=0 + if [ "$result" != "$expected" ]; then + echo "result: $result" + echo "expected: $expected" + rv=1 + fi + atf_check_equal $rv 0 +} + +atf_init_test_cases() { + atf_add_test_case remove_same + atf_add_test_case remove_newer + atf_add_test_case remove_not_indexed +}