Add noextract configuration option

Closes #208
Fixes #165
This commit is contained in:
Duncan Overbruck 2020-01-18 14:49:59 +01:00
parent ef9260a16e
commit 6794077efd
No known key found for this signature in database
GPG Key ID: 335C1D17EC3D6E35
10 changed files with 269 additions and 3 deletions

View File

@ -95,6 +95,9 @@ check_pkg_files(struct xbps_handle *xhp, const char *pkgname, void *arg)
while ((obj = xbps_object_iterator_next(iter))) { while ((obj = xbps_object_iterator_next(iter))) {
xbps_dictionary_get_cstring_nocopy(obj, "file", &file); xbps_dictionary_get_cstring_nocopy(obj, "file", &file);
/* skip noextract files */
if (xhp->noextract && xbps_patterns_match(xhp->noextract, file))
continue;
path = xbps_xasprintf("%s/%s", xhp->rootdir, file); path = xbps_xasprintf("%s/%s", xhp->rootdir, file);
xbps_dictionary_get_cstring_nocopy(obj, xbps_dictionary_get_cstring_nocopy(obj,
"sha256", &sha256); "sha256", &sha256);
@ -150,6 +153,9 @@ check_pkg_files(struct xbps_handle *xhp, const char *pkgname, void *arg)
while ((obj = xbps_object_iterator_next(iter))) { while ((obj = xbps_object_iterator_next(iter))) {
xbps_dictionary_get_cstring_nocopy(obj, "file", &file); xbps_dictionary_get_cstring_nocopy(obj, "file", &file);
/* skip noextract files */
if (xhp->noextract && xbps_patterns_match(xhp->noextract, file))
continue;
path = xbps_xasprintf("%s/%s", xhp->rootdir, file); path = xbps_xasprintf("%s/%s", xhp->rootdir, file);
if (access(path, R_OK) == -1) { if (access(path, R_OK) == -1) {
if (errno == ENOENT) { if (errno == ENOENT) {

View File

@ -66,6 +66,10 @@ check_pkg_symlinks(struct xbps_handle *xhp, const char *pkgname, void *arg)
if (!xbps_dictionary_get_cstring_nocopy(obj, "file", &file)) if (!xbps_dictionary_get_cstring_nocopy(obj, "file", &file))
continue; continue;
/* skip noextract files */
if (xhp->noextract && xbps_patterns_match(xhp->noextract, file))
continue;
if (!xbps_dictionary_get_cstring_nocopy(obj, "target", &tgt)) { if (!xbps_dictionary_get_cstring_nocopy(obj, "target", &tgt)) {
xbps_warn_printf("%s: `%s' symlink with " xbps_warn_printf("%s: `%s' symlink with "
"empty target object!\n", pkgname, file); "empty target object!\n", pkgname, file);

View File

@ -1,4 +1,4 @@
.Dd June 12, 2019 .Dd January 18, 2020
.Dt XBPS-D 5 .Dt XBPS-D 5
.Sh NAME .Sh NAME
.Nm xbps.d .Nm xbps.d
@ -60,6 +60,18 @@ If path starts with '/' it's an absolute path, otherwise it will be relative to
Declares a ignored package. Declares a ignored package.
If a package depends on an ignored package the dependency is always satisfied, If a package depends on an ignored package the dependency is always satisfied,
without installing the ignored package. without installing the ignored package.
.It Sy noextract=pattern
Skip extraction of matching files.
Patterns starting with a exclamation mark negate the previous match,
a single backslash can be used to escape the exclamation mark.
.Pp
In the following example all files matching the first pattern will not be extracted,
but files that also match the second pattern will still be extracted.
.Pp
.Bl -tag -compact -width noextract=!/usr/bin/foo
.It Sy noextract=/usr/bin/f*
.It Sy noextract=!/usr/bin/foo
.El
.It Sy include=path/file.conf .It Sy include=path/file.conf
Imports settings from the specified configuration file. Imports settings from the specified configuration file.
.Em NOTE .Em NOTE

View File

@ -50,7 +50,7 @@
* *
* This header documents the full API for the XBPS Library. * This header documents the full API for the XBPS Library.
*/ */
#define XBPS_API_VERSION "20191229" #define XBPS_API_VERSION "20200118"
#ifndef XBPS_VERSION #ifndef XBPS_VERSION
#define XBPS_VERSION "UNSET" #define XBPS_VERSION "UNSET"
@ -540,6 +540,7 @@ struct xbps_handle {
*/ */
xbps_array_t preserved_files; xbps_array_t preserved_files;
xbps_array_t ignored_pkgs; xbps_array_t ignored_pkgs;
xbps_array_t noextract;
/** /**
* @var repositories * @var repositories
* *
@ -2168,6 +2169,18 @@ char *xbps_sanitize_path(const char *src);
char *xbps_symlink_target(struct xbps_handle *xhp, const char *path, char *xbps_symlink_target(struct xbps_handle *xhp, const char *path,
const char *target); const char *target);
/**
* Returns true if any of the fnmatch patterns in \a patterns matches
* and is not negated by a later match.
*
* @param[in] patterns The patterns to match against.
* @param[in] path The path that is matched against the patterns.
*
* @return true if any pattern matches, false otherwise.
* The returned buffer must be free(3)d when it's no longer necessary.
*/
bool xbps_patterns_match(xbps_array_t patterns, const char *path);
/** /**
* Internalizes a plist file declared in \f and returns a proplib array. * Internalizes a plist file declared in \f and returns a proplib array.
* *

View File

@ -145,6 +145,19 @@ store_ignored_pkg(struct xbps_handle *xhp, const char *pkgname)
xbps_dbg_printf(xhp, "Added ignored package: %s\n", pkgname); xbps_dbg_printf(xhp, "Added ignored package: %s\n", pkgname);
} }
static void
store_noextract(struct xbps_handle *xhp, const char *value)
{
if (*value == '\0')
return;
if (xhp->noextract == NULL) {
xhp->noextract = xbps_array_create();
assert(xhp->noextract);
}
xbps_array_add_cstring(xhp->noextract, value);
xbps_dbg_printf(xhp, "Added noextract pattern: %s\n", value);
}
enum { enum {
KEY_ERROR = 0, KEY_ERROR = 0,
KEY_ARCHITECTURE, KEY_ARCHITECTURE,
@ -152,6 +165,7 @@ enum {
KEY_CACHEDIR, KEY_CACHEDIR,
KEY_IGNOREPKG, KEY_IGNOREPKG,
KEY_INCLUDE, KEY_INCLUDE,
KEY_NOEXTRACT,
KEY_PRESERVE, KEY_PRESERVE,
KEY_REPOSITORY, KEY_REPOSITORY,
KEY_ROOTDIR, KEY_ROOTDIR,
@ -169,6 +183,7 @@ static const struct key {
{ "cachedir", 8, KEY_CACHEDIR }, { "cachedir", 8, KEY_CACHEDIR },
{ "ignorepkg", 9, KEY_IGNOREPKG }, { "ignorepkg", 9, KEY_IGNOREPKG },
{ "include", 7, KEY_INCLUDE }, { "include", 7, KEY_INCLUDE },
{ "noextract", 9, KEY_NOEXTRACT },
{ "preserve", 8, KEY_PRESERVE }, { "preserve", 8, KEY_PRESERVE },
{ "repository", 10, KEY_REPOSITORY }, { "repository", 10, KEY_REPOSITORY },
{ "rootdir", 7, KEY_ROOTDIR }, { "rootdir", 7, KEY_ROOTDIR },
@ -353,6 +368,9 @@ parse_file(struct xbps_handle *xhp, const char *path, bool nested)
case KEY_IGNOREPKG: case KEY_IGNOREPKG:
store_ignored_pkg(xhp, val); store_ignored_pkg(xhp, val);
break; break;
case KEY_NOEXTRACT:
store_noextract(xhp, val);
break;
case KEY_INCLUDE: case KEY_INCLUDE:
/* Avoid double-nested parsing, only allow it once */ /* Avoid double-nested parsing, only allow it once */
if (nested) { if (nested) {

View File

@ -305,6 +305,15 @@ unpack_archive(struct xbps_handle *xhp,
xucd_stats = true; xucd_stats = true;
} }
} }
/*
* Skip files that match noextract patterns from configuration file.
*/
if (xhp->noextract && xbps_patterns_match(xhp->noextract, entry_pname+1)) {
xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FILE_PRESERVED, 0,
pkgver, "%s: file `%s' won't be extracted, "
"it matches a noextract pattern.", pkgver, entry_pname);
continue;
}
/* /*
* Always check that extracted file exists and hash * Always check that extracted file exists and hash
* doesn't match, in that case overwrite the file. * doesn't match, in that case overwrite the file.
@ -323,7 +332,7 @@ unpack_archive(struct xbps_handle *xhp,
"and must be preserved, skipping.\n", entry_pname); "and must be preserved, skipping.\n", entry_pname);
xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FILE_PRESERVED, 0, xbps_set_cb_state(xhp, XBPS_STATE_UNPACK_FILE_PRESERVED, 0,
pkgver, "%s: file `%s' won't be extracted, " pkgver, "%s: file `%s' won't be extracted, "
"it's preserved.\n", pkgver, entry_pname); "it's preserved.", pkgver, entry_pname);
continue; continue;
} }

View File

@ -648,3 +648,27 @@ xbps_symlink_target(struct xbps_handle *xhp, const char *path, const char *tgt)
return res; return res;
} }
bool
xbps_patterns_match(xbps_array_t patterns, const char *path)
{
bool match = false;
if (patterns == NULL)
return false;
for (unsigned int i = 0; i < xbps_array_count(patterns); i++) {
const char *pattern = NULL;
bool negate = false;
if (!xbps_array_get_cstring_nocopy(patterns, i, &pattern))
continue;
if (pattern == NULL)
continue;
if ((negate = *pattern == '!') || *pattern == '\\')
pattern++;
if (fnmatch(pattern, path, 0) == 0)
match = !negate;
}
return match;
}

View File

@ -26,3 +26,4 @@ atf_test_program{name="conflicts_test"}
atf_test_program{name="downgrade_hold_test"} atf_test_program{name="downgrade_hold_test"}
atf_test_program{name="ignore_test"} atf_test_program{name="ignore_test"}
atf_test_program{name="preserve_test"} atf_test_program{name="preserve_test"}
atf_test_program{name="noextract_files_test"}

View File

@ -9,6 +9,7 @@ TESTSHELL+= vpkg_test install_test preserve_files_test configure_test
TESTSHELL+= update_shlibs_test update_hold_test update_repolock_test TESTSHELL+= update_shlibs_test update_hold_test update_repolock_test
TESTSHELL+= cyclic_deps_test conflicts_test update_itself_test TESTSHELL+= cyclic_deps_test conflicts_test update_itself_test
TESTSHELL+= downgrade_hold_test ignore_test preserve_test TESTSHELL+= downgrade_hold_test ignore_test preserve_test
TESTSHELL+= noextract_files_test
EXTRA_FILES = Kyuafile EXTRA_FILES = Kyuafile
include $(TOPDIR)/mk/test.mk include $(TOPDIR)/mk/test.mk

View File

@ -0,0 +1,178 @@
#!/usr/bin/env atf-sh
atf_test_case tc1
tc1_head() {
atf_set "descr" "Tests for pkg install with noextract: match whole directory"
}
tc1_body() {
mkdir some_repo
mkdir -p pkg_A/usr/bin pkg_A/usr/lib
touch pkg_A/usr/bin/blah pkg_A/usr/bin/foo pkg_A/usr/lib/foo
cd some_repo
xbps-create -A noarch -n A-1.0_1 -s "A pkg" ../pkg_A
atf_check_equal $? 0
xbps-rindex -d -a $PWD/*.xbps
atf_check_equal $? 0
cd ..
mkdir -p root/xbps.d
echo "noextract=/usr/bin/*" > root/xbps.d/foo.conf
xbps-install -C xbps.d -r root --repository=$PWD/some_repo -yd A
atf_check_equal $? 0
rv=0
[ -e root/usr/lib/foo ] || rv=1
[ -e root/usr/bin/blah ] && rv=1
[ -e root/usr/bin/foo ] && rv=1
atf_check_equal $rv 0
xbps-pkgdb -C xbps.d -r root A
atf_check_equal $? 0
}
atf_test_case tc2
tc2_head() {
atf_set "descr" "Tests for pkg install with noextract: match certain file"
}
tc2_body() {
mkdir some_repo
mkdir -p pkg_A/usr/bin pkg_A/usr/lib
touch pkg_A/usr/bin/blah pkg_A/usr/bin/foo pkg_A/usr/lib/foo
cd some_repo
xbps-create -A noarch -n A-1.0_1 -s "A pkg" ../pkg_A
atf_check_equal $? 0
xbps-rindex -d -a $PWD/*.xbps
atf_check_equal $? 0
cd ..
mkdir -p root/xbps.d
echo "noextract=/usr/bin/f*" > root/xbps.d/foo.conf
xbps-install -C xbps.d -r root --repository=$PWD/some_repo -yd A
atf_check_equal $? 0
tree root
rv=0
[ -e root/usr/lib/foo ] || rv=1
[ -e root/usr/bin/blah ] || rv=1
[ -e root/usr/bin/foo ] && rv=1
atf_check_equal $rv 0
xbps-pkgdb -C xbps.d -r root A
atf_check_equal $? 0
}
atf_test_case tc3
tc3_head() {
atf_set "descr" "Tests for pkg install with noextract: negate pattern"
}
tc3_body() {
mkdir some_repo
mkdir -p pkg_A/usr/bin pkg_A/usr/lib
touch pkg_A/usr/bin/blah pkg_A/usr/bin/foo pkg_A/usr/lib/foo
cd some_repo
xbps-create -A noarch -n A-1.0_1 -s "A pkg" ../pkg_A
atf_check_equal $? 0
xbps-rindex -d -a $PWD/*.xbps
atf_check_equal $? 0
cd ..
mkdir -p root/xbps.d
echo "noextract=/usr/bin/*" > root/xbps.d/foo.conf
echo "noextract=!/usr/bin/blah" >> root/xbps.d/foo.conf
xbps-install -C xbps.d -r root --repository=$PWD/some_repo -yd A
atf_check_equal $? 0
tree root
rv=0
[ -e root/usr/lib/foo ] || rv=1
[ -e root/usr/bin/blah ] || rv=1
[ -e root/usr/bin/foo ] && rv=1
atf_check_equal $rv 0
xbps-pkgdb -C xbps.d -r root A
atf_check_equal $? 0
}
tc4_head() {
atf_set "descr" "Tests for pkg install with noextract: negate and match again"
}
tc4_body() {
mkdir some_repo
mkdir -p pkg_A/usr/bin pkg_A/usr/lib
touch pkg_A/usr/bin/blah pkg_A/usr/bin/foo pkg_A/usr/lib/foo
cd some_repo
xbps-create -A noarch -n A-1.0_1 -s "A pkg" ../pkg_A
atf_check_equal $? 0
xbps-rindex -d -a $PWD/*.xbps
atf_check_equal $? 0
cd ..
mkdir -p root/xbps.d
echo "noextract=/usr/bin/*" > root/xbps.d/foo.conf
echo "noextract=!/usr/bin/blah" >> root/xbps.d/foo.conf
echo "noextract=/usr/bin/bla*" >> root/xbps.d/foo.conf
xbps-install -C xbps.d -r root --repository=$PWD/some_repo -yd A
atf_check_equal $? 0
tree root
rv=0
[ -e root/usr/lib/foo ] || rv=1
[ -e root/usr/bin/blah ] && rv=1
[ -e root/usr/bin/foo ] && rv=1
atf_check_equal $rv 0
xbps-pkgdb -C xbps.d -r root A
atf_check_equal $? 0
}
atf_test_case tc5
tc5_head() {
atf_set "descr" "Tests for pkg install with noextract: match full path"
}
tc5_body() {
mkdir some_repo
mkdir -p pkg_A/usr/bin pkg_A/usr/lib
touch pkg_A/usr/bin/blah pkg_A/usr/bin/foo pkg_A/usr/lib/foo
cd some_repo
xbps-create -A noarch -n A-1.0_1 -s "A pkg" ../pkg_A
atf_check_equal $? 0
xbps-rindex -d -a $PWD/*.xbps
atf_check_equal $? 0
cd ..
mkdir -p root/xbps.d
echo "noextract=*foo" > root/xbps.d/foo.conf
xbps-install -C xbps.d -r root --repository=$PWD/some_repo -yd A
atf_check_equal $? 0
rv=0
[ -e root/usr/lib/foo ] && rv=1
[ -e root/usr/bin/foo ] && rv=1
[ -e root/usr/bin/blah ] || rv=2
atf_check_equal $rv 0
xbps-pkgdb -C xbps.d -r root A
atf_check_equal $? 0
}
atf_init_test_cases() {
atf_add_test_case tc1
atf_add_test_case tc2
atf_add_test_case tc3
atf_add_test_case tc4
atf_add_test_case tc5
}