diff --git a/MODULE.bazel.lock b/MODULE.bazel.lock index 81efe96..ef2cebb 100644 --- a/MODULE.bazel.lock +++ b/MODULE.bazel.lock @@ -235,11 +235,8 @@ "https://bcr.bazel.build/modules/xz/5.4.5.bcr.7/source.json": "8f42ea24ea4bf0d34e9fca53ee39de4fd4aeb93459a777600975ba18857fd2a9", "https://bcr.bazel.build/modules/yq.bzl/0.1.1/MODULE.bazel": "9039681f9bcb8958ee2c87ffc74bdafba9f4369096a2b5634b88abc0eaefa072", "https://bcr.bazel.build/modules/yq.bzl/0.1.1/source.json": "2d2bad780a9f2b9195a4a370314d2c17ae95eaa745cefc2e12fbc49759b15aa3", - "https://bcr.bazel.build/modules/zlib/1.2.11/MODULE.bazel": "07b389abc85fdbca459b69e2ec656ae5622873af3f845e1c9d80fe179f3effa0", - "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.3/MODULE.bazel": "af322bc08976524477c79d1e45e241b6efbeb918c497e8840b8ab116802dda79", - "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/MODULE.bazel": "eec517b5bbe5492629466e11dae908d043364302283de25581e3eb944326c4ca", - "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.5/source.json": "22bc55c47af97246cfc093d0acf683a7869377de362b5d1c552c2c2e16b7a806", - "https://bcr.bazel.build/modules/zlib/1.3.1/MODULE.bazel": "751c9940dcfe869f5f7274e1295422a34623555916eb98c174c1e945594bf198", + "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.8/MODULE.bazel": "772c674bb78a0342b8caf32ab5c25085c493ca4ff08398208dcbe4375fe9f776", + "https://bcr.bazel.build/modules/zlib/1.3.1.bcr.8/source.json": "cf377d76800dfc3d3b71e9dd4a8c53a62837cbce37cc4f25e6207b15fc1e8f2b", "https://bcr.bazel.build/modules/zstd/1.5.7.bcr.1/MODULE.bazel": "c5977176dd8555be7a9d598512ae0cae11831259c06f00a5e5e0037d5db4e3f5", "https://bcr.bazel.build/modules/zstd/1.5.7.bcr.1/source.json": "aa95e0b5aac9d80195b9047d223beaf26b1948be55d49f0e803e180e9ccc6e75" }, diff --git a/pkgutil.c b/pkgutil.c index 74026c3..ec8d5f3 100644 --- a/pkgutil.c +++ b/pkgutil.c @@ -77,6 +77,8 @@ static void usage(FILE *out) { static char *strip_components_path(const char *path, int strip); static int apply_strip_components(struct archive_entry *e, int strip); +static int path_component_count(const char *path); +static char *normalize_rel_path(const char *path); static int match_excluded_with_prefix(struct archive *match, struct archive_entry *e, const char *prefix); @@ -302,23 +304,31 @@ static void extract_nested_archive_from_stream(struct astream *in, fail_archive(a, "read nested header"); } + const char *p = archive_entry_pathname(e); + char *rel = normalize_rel_path(p); + archive_entry_set_pathname(e, rel); + if (match != NULL) { r = match_excluded_with_prefix(match, e, prefix); if (r != 0) { archive_read_data_skip(a); + free(rel); continue; } } if (apply_strip_components(e, strip_components)) { archive_read_data_skip(a); + free(rel); continue; } r = archive_read_extract2(a, e, disk); if (r != ARCHIVE_OK) { + free(rel); fail_archive(a, "extract nested entry"); } + free(rel); } archive_write_free(disk); @@ -418,6 +428,34 @@ static int apply_strip_components(struct archive_entry *e, int strip) { return (0); } +static int path_component_count(const char *path) { + int count = 0; + int in_component = 0; + + if (path == NULL) { + return (0); + } + + for (const char *p = path; *p != '\0'; p++) { + switch (*p) { + case '/': +#if defined(_WIN32) && !defined(__CYGWIN__) + case '\\': +#endif + in_component = 0; + break; + default: + if (!in_component) { + count++; + in_component = 1; + } + break; + } + } + + return (count); +} + static int match_excluded_with_prefix(struct archive *match, struct archive_entry *e, const char *prefix) { @@ -682,19 +720,24 @@ int main(int argc, char **argv) { } } int is_nested = should_be_treated_as_nested_archive(rel); - if (apply_strip_components(e, strip_components)) { - archive_read_data_skip(xar); - free(rel); - continue; - } - free(rel); - rel = strdup(archive_entry_pathname(e)); - if (rel == NULL) { - fail_errno("strdup"); - } - if (do_expand_full && is_nested) { - mkdirs_for_path(rel); + char *nested_outdir = strip_components_path(rel, strip_components); + int nested_strip = strip_components; + int rel_components = path_component_count(rel); + + if (nested_outdir == NULL) { + nested_outdir = strdup("."); + if (nested_outdir == NULL) { + fail_errno("strdup"); + } + } + if (nested_strip > rel_components) { + nested_strip -= rel_components; + } else { + nested_strip = 0; + } + + mkdirs_for_path(nested_outdir); { struct astream in = { @@ -706,11 +749,17 @@ int main(int argc, char **argv) { .eof = 0, }; - extract_nested_archive_from_stream(&in, rel, flags, match, - strip_components, rel); + extract_nested_archive_from_stream(&in, nested_outdir, flags, match, + nested_strip, rel); } + free(nested_outdir); free(rel); } else { + if (apply_strip_components(e, strip_components)) { + archive_read_data_skip(xar); + free(rel); + continue; + } r = archive_read_extract2(xar, e, disk); if (r != ARCHIVE_OK) { free(rel); diff --git a/tests/BUILD.bazel b/tests/BUILD.bazel index 6902fbe..85cc5e4 100644 --- a/tests/BUILD.bazel +++ b/tests/BUILD.bazel @@ -44,6 +44,26 @@ run_binary( tags = ["manual"], ) +run_binary( + name = "pkgutil_component_expand_full_strip_6_action", + tool = "//:pkgutil", + srcs = [ + "@component_pkg//file", + ], + args = [ + "--exclude", + "Payload/Library/Developer/CommandLineTools/SDKs/MacOSX15.5.sdk/System/Library/Frameworks/Ruby.framework/Versions/2.6/Headers/ruby/ruby", + "--expand-full", + "--strip-components", + "6", + "$(location @component_pkg//file)", + "$@", + ], + out_dirs = ["pkgutil-component-expand-full-strip-6"], + testonly = True, + tags = ["manual"], +) + run_binary( name = "pkgutil_product_expand_action", tool = "//:pkgutil", @@ -136,6 +156,19 @@ exec_test( ], ) +exec_test( + native_test, + name = "pkgutil_component_expand_full_strip_6_test", + src = ":test", + args = [ + "-e", + "$(location :pkgutil_component_expand_full_strip_6_action)/usr/bin/cups-config", + ], + data = [ + ":pkgutil_component_expand_full_strip_6_action", + ], +) + exec_test( native_test, name = "pkgutil_product_expand_test",