From: Patrick Ohly Date: Mon, 24 Oct 2016 10:54:48 +0000 (+0200) Subject: non-recursive extract and list X-Git-Tag: v3.4.0~75^2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7c5e94c9ec3a70ddaeec6c554149eb13db9cfe60;p=thirdparty%2Flibarchive.git non-recursive extract and list Sometimes it makes sense to extract or list a directory contained in an archive without also doing the same for the content of the directory, i.e. allowing -n (= --no-recursion) in combination with the x and t modes. bsdtar uses the match functionality in libarchive to track include matches. A new libarchive API call archive_match_set_inclusion_recursion() gets introduced to influence the matching behavior, with the default behavior as before. Non-recursive matching can be achieved by anchoring the path match at both start and end. Asking for a directory which itself isn't in the archive when in non-recursive mode is an error and handled by the existing mechanism for tracking unused inclusion entries. --- diff --git a/libarchive/archive.h b/libarchive/archive.h index 438ce4680..3633a5807 100644 --- a/libarchive/archive.h +++ b/libarchive/archive.h @@ -1095,6 +1095,8 @@ __LA_DECL int archive_match_excluded(struct archive *, */ __LA_DECL int archive_match_path_excluded(struct archive *, struct archive_entry *); +/* Control recursive inclusion of directory content when directory is included. Default on. */ +__LA_DECL int archive_match_set_inclusion_recursion(struct archive *, int); /* Add exclusion pathname pattern. */ __LA_DECL int archive_match_exclude_pattern(struct archive *, const char *); __LA_DECL int archive_match_exclude_pattern_w(struct archive *, diff --git a/libarchive/archive_match.c b/libarchive/archive_match.c index 027d9715d..04747b1f6 100644 --- a/libarchive/archive_match.c +++ b/libarchive/archive_match.c @@ -93,6 +93,9 @@ struct archive_match { /* exclusion/inclusion set flag. */ int setflag; + /* Recursively include directory content? */ + int recursive_include; + /* * Matching filename patterns. */ @@ -223,6 +226,7 @@ archive_match_new(void) return (NULL); a->archive.magic = ARCHIVE_MATCH_MAGIC; a->archive.state = ARCHIVE_STATE_NEW; + a->recursive_include = 1; match_list_init(&(a->inclusions)); match_list_init(&(a->exclusions)); __archive_rb_tree_init(&(a->exclusion_tree), &rb_ops_mbs); @@ -470,6 +474,28 @@ archive_match_path_excluded(struct archive *_a, #endif } +/* + * When recursive inclusion of directory content is enabled, + * an inclusion pattern that matches a directory will also + * include everything beneath that directory. Enabled by default. + * + * For compatibility with GNU tar, exclusion patterns always + * match if a subset of the full patch matches (i.e., they are + * are not rooted at the beginning of the path) and thus there + * is no corresponding non-recursive exclusion mode. + */ +int +archive_match_set_inclusion_recursion(struct archive *_a, int enabled) +{ + struct archive_match *a; + + archive_check_magic(_a, ARCHIVE_MATCH_MAGIC, + ARCHIVE_STATE_NEW, "archive_match_set_inclusion_recursion"); + a = (struct archive_match *)_a; + a->recursive_include = enabled; + return (ARCHIVE_OK); +} + /* * Utility functions to get statistic information for inclusion patterns. */ @@ -781,7 +807,10 @@ static int match_path_inclusion(struct archive_match *a, struct match *m, int mbs, const void *pn) { - int flag = PATHMATCH_NO_ANCHOR_END; + /* Recursive operation requires only a prefix match. */ + int flag = a->recursive_include ? + PATHMATCH_NO_ANCHOR_END : + 0; int r; if (mbs) { diff --git a/tar/bsdtar.1 b/tar/bsdtar.1 index 4c0fe818f..828405470 100644 --- a/tar/bsdtar.1 +++ b/tar/bsdtar.1 @@ -398,8 +398,7 @@ and the default behavior in c, r, and u modes or if .Nm is run in x mode as root. .It Fl n , Fl Fl norecurse , Fl Fl no-recursion -(c, r, u modes only) -Do not recursively archive the contents of directories. +Do not operate recursively on the content of directories. .It Fl Fl newer Ar date (c, r, u modes only) Only include files and directories newer than the specified date. diff --git a/tar/bsdtar.c b/tar/bsdtar.c index 280a0a160..b59963d0f 100644 --- a/tar/bsdtar.c +++ b/tar/bsdtar.c @@ -839,8 +839,6 @@ main(int argc, char **argv) break; } } - if (bsdtar->flags & OPTFLAG_NO_SUBDIRS) - only_mode(bsdtar, "-n", "cru"); if (bsdtar->flags & OPTFLAG_STDOUT) only_mode(bsdtar, "-O", "xt"); if (bsdtar->flags & OPTFLAG_UNLINK_FIRST) @@ -890,6 +888,16 @@ main(int argc, char **argv) only_mode(bsdtar, buff, "cru"); } + /* + * When creating an archive from a directory tree, the directory + * walking code will already avoid entering directories when + * recursive inclusion of directory content is disabled, therefore + * changing the matching behavior has no effect for creation modes. + * It is relevant for extraction or listing. + */ + archive_match_set_inclusion_recursion(bsdtar->matching, + !(bsdtar->flags & OPTFLAG_NO_SUBDIRS)); + /* Filename "-" implies stdio. */ if (strcmp(bsdtar->filename, "-") == 0) bsdtar->filename = NULL;