diff --git a/rpm-4.19.x-add-autosetup-C.patch b/rpm-4.19.x-add-autosetup-C.patch new file mode 100644 index 0000000..e07211d --- /dev/null +++ b/rpm-4.19.x-add-autosetup-C.patch @@ -0,0 +1,1164 @@ +From 93dceb0dc1e05a6e71e966c27814364eb77346d2 Mon Sep 17 00:00:00 2001 +From: Matteo Croce +Date: Tue, 16 Jan 2024 17:26:16 -0500 +Subject: [PATCH 01/14] add build directory auto path to %autosetup + +Add a `-C` flag to %autosetup and %setup which ensures that the sources +will be extracted in the root of the build directory. +It works by inspecting the archive and stripping the first path entry +if the archive has a top level directory alone in the root. + +The archive inspection and path stripping is implemented in rpmuncompress +which now has a new `-C` flag: + + $ ~/rpm/usr/lib/rpm/rpmuncompress -x -v -C source-1.0.0 source-singleroot.tar.gz + mkdir 'source-1.0.0' ; /usr/bin/gzip -dc 'source-singleroot.tar.gz' | /usr/bin/tar -xvvof - -C 'source-1.0.0' --strip-components=1 + -rw-r--r-- root/root 32 2024-01-16 19:13 source-xxxxxxxxxx/file1 + -rw-r--r-- root/root 33886 2024-01-16 19:13 source-xxxxxxxxxx/file2 + drwxr-xr-x root/root 0 2024-01-16 19:13 source-xxxxxxxxxx/dir1/ + -r--r--r-- root/root 210 2024-01-16 19:13 source-xxxxxxxxxx/dir1/file3 + + $ find source-1.0.0 -ls + 92341 0 drwxr-xr-x 1 teknoraver teknoraver 28 Jan 16 19:16 source-1.0.0 + 92342 4 -rw-r--r-- 1 teknoraver teknoraver 32 Jan 16 19:13 source-1.0.0/file1 + 92343 36 -rw-r--r-- 1 teknoraver teknoraver 33886 Jan 16 19:13 source-1.0.0/file2 + 92344 0 drwxr-xr-x 1 teknoraver teknoraver 10 Jan 16 19:13 source-1.0.0/dir1 + 92345 4 -r--r--r-- 1 teknoraver teknoraver 210 Jan 16 19:13 source-1.0.0/dir1/file3 + + $ ~/rpm/usr/lib/rpm/rpmuncompress -x -v -C source-2.0.0 source-noroot.tar.gz + mkdir 'source-2.0.0' ; /usr/bin/gzip -dc 'source-noroot.tar.gz' | /usr/bin/tar -xvvof - -C 'source-2.0.0' + drwxr-xr-x root/root 0 2024-01-16 19:13 dir1/ + -r--r--r-- root/root 210 2024-01-16 19:13 dir1/file3 + -rw-r--r-- root/root 32 2024-01-16 19:13 file1 + -rw-r--r-- root/root 33886 2024-01-16 19:13 file2 + + $ find source-2.0.0 -ls + 92346 0 drwxr-xr-x 1 teknoraver teknoraver 28 Jan 16 19:17 source-2.0.0 + 92347 0 drwxr-xr-x 1 teknoraver teknoraver 10 Jan 16 19:13 source-2.0.0/dir1 + 92348 4 -r--r--r-- 1 teknoraver teknoraver 210 Jan 16 19:13 source-2.0.0/dir1/file3 + 92349 4 -rw-r--r-- 1 teknoraver teknoraver 32 Jan 16 19:13 source-2.0.0/file1 + 92350 36 -rw-r--r-- 1 teknoraver teknoraver 33886 Jan 16 19:13 source-2.0.0/file2 + +And it's exposed to %autosetup + + $ rpmbuild -bp test.spec + Executing(%prep): /bin/sh -e /var/tmp/rpm-tmp.NL55sI + + umask 022 + + cd /home/teknoraver/rpmbuild/BUILD + + cd /home/teknoraver/rpmbuild/BUILD + + rm -rf netperf-2.7.1 + + /usr/lib/rpm/rpmuncompress -x -C netperf-2.7.1 /home/teknoraver/rpmbuild/SOURCES/netperf-3bc455b.tar.gz + + STATUS=0 + + '[' 0 -ne 0 ']' + + cd netperf-2.7.1 + + rm -rf /home/teknoraver/rpmbuild/BUILD/netperf-2.7.1-SPECPARTS + + /usr/bin/mkdir -p /home/teknoraver/rpmbuild/BUILD/netperf-2.7.1-SPECPARTS + + /usr/bin/chmod -Rf a+rX,u+w,g-w,o-w . + + RPM_EC=0 + ++ jobs -p + + exit 0 + +(backported from commit 33853c73cf6af5ff3108fe5c59a0a7a1614bed99) +--- + CMakeLists.txt | 5 +-- + build/parsePrep.c | 13 +++++--- + docs/manual/spec.md | 3 ++ + macros.in | 4 +-- + tools/CMakeLists.txt | 1 + + tools/rpmuncompress.c | 74 +++++++++++++++++++++++++++++++++++++++++-- + 6 files changed, 87 insertions(+), 13 deletions(-) + +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 6dbf179f3..7b9fbf56f 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -205,6 +205,7 @@ pkg_check_modules(ZSTD IMPORTED_TARGET libzstd>=1.3.8) + pkg_check_modules(LIBELF IMPORTED_TARGET libelf) + pkg_check_modules(LIBDW IMPORTED_TARGET libdw) + pkg_check_modules(LIBLZMA IMPORTED_TARGET liblzma>=5.2.0) ++pkg_check_modules(LIBARCHIVE REQUIRED IMPORTED_TARGET libarchive) + + # Lua module does not ship an IMPORTED target, define our own + add_library(LUA::LUA INTERFACE IMPORTED) +@@ -265,10 +266,6 @@ if (WITH_SELINUX) + pkg_check_modules(SELINUX REQUIRED IMPORTED_TARGET libselinux) + endif() + +-if (WITH_ARCHIVE) +- pkg_check_modules(LIBARCHIVE REQUIRED IMPORTED_TARGET libarchive) +-endif() +- + if (WITH_FSVERITY) + pkg_check_modules(FSVERITY REQUIRED IMPORTED_TARGET libfsverity) + endif() +diff --git a/build/parsePrep.c b/build/parsePrep.c +index 07d9a6923..022a5be7d 100644 +--- a/build/parsePrep.c ++++ b/build/parsePrep.c +@@ -124,7 +124,7 @@ exit: + * @param quietly should -vv be omitted from tar? + * @return expanded %setup macro (NULL on error) + */ +-static char *doUntar(rpmSpec spec, uint32_t c, int quietly) ++static char *doUntar(rpmSpec spec, uint32_t c, int quietly, int autoPath) + { + char *buf = NULL; + struct Source *sp; +@@ -135,7 +135,9 @@ static char *doUntar(rpmSpec spec, uint32_t c, int quietly) + } + + buf = rpmExpand("%{__rpmuncompress} -x ", +- quietly ? "" : "-v ","%{shescape:", sp->path, "}", NULL); ++ quietly ? "" : "-v ", ++ autoPath ? "-C %{buildsubdir} " : "", ++ "%{shescape:", sp->path, "}", NULL); + rstrcat(&buf, + "\nSTATUS=$?\n" + "if [ $STATUS -ne 0 ]; then\n" +@@ -165,12 +167,13 @@ static int doSetupMacro(rpmSpec spec, const char *line) + rpmRC rc = RPMRC_FAIL; + uint32_t num; + int leaveDirs = 0, skipDefaultAction = 0; +- int createDir = 0, quietly = 0; ++ int createDir = 0, quietly = 0, autoPath = 0; + char * dirName = NULL; + struct poptOption optionsTable[] = { + { NULL, 'a', POPT_ARG_STRING, NULL, 'a', NULL, NULL}, + { NULL, 'b', POPT_ARG_STRING, NULL, 'b', NULL, NULL}, + { NULL, 'c', 0, &createDir, 0, NULL, NULL}, ++ { NULL, 'C', 0, &autoPath, 0, NULL, NULL}, + { NULL, 'D', 0, &leaveDirs, 0, NULL, NULL}, + { NULL, 'n', POPT_ARG_STRING, &dirName, 0, NULL, NULL}, + { NULL, 'T', 0, &skipDefaultAction, 0, NULL, NULL}, +@@ -202,7 +205,7 @@ static int doSetupMacro(rpmSpec spec, const char *line) + goto exit; + } + +- { char *chptr = doUntar(spec, num, quietly); ++ { char *chptr = doUntar(spec, num, quietly, 0); + if (chptr == NULL) + goto exit; + +@@ -259,7 +262,7 @@ static int doSetupMacro(rpmSpec spec, const char *line) + + /* do the default action */ + if (!skipDefaultAction) { +- char *chptr = doUntar(spec, 0, quietly); ++ char *chptr = doUntar(spec, 0, quietly, autoPath); + if (!chptr) + goto exit; + appendBuf(spec, chptr, 1); +diff --git a/docs/manual/spec.md b/docs/manual/spec.md +index 098099875..3ace79e56 100644 +--- a/docs/manual/spec.md ++++ b/docs/manual/spec.md +@@ -489,6 +489,9 @@ can just create the directory. It accepts a number of options: + -a N unpack source N after changing to the build directory + -b N unpack source N before changing to the build directory + -c create the build directory (and change to it) before unpacking ++-C Create the build directory and ensure the archive contents ++ are unpacked there, stripping the top level directory in the archive ++ if it exists + -D do not delete the build directory prior to unpacking (used + when more than one source is to be unpacked with `-a` or `-b`) + -n DIR set the name of build directory (default is `%{name}-%{version}`) +diff --git a/macros.in b/macros.in +index ef413a358..19be79909 100644 +--- a/macros.in ++++ b/macros.in +@@ -1338,8 +1338,8 @@ end + # usage of git repository and per-patch commits. + # -N Disable automatic patch application + # -p Use -p for patch application +-%autosetup(a:b:cDn:TvNS:p:)\ +-%setup %{-a} %{-b} %{-c} %{-D} %{-n} %{-T} %{!-v:-q}\ ++%autosetup(a:b:cCDn:TvNS:p:)\ ++%setup %{-a} %{-b} %{-c} %{-C} %{-D} %{-n} %{-T} %{!-v:-q}\ + %{-S:%global __scm %{-S*}}\ + %{expand:%__scm_setup_%{__scm} %{!-v:-q}}\ + %{!-N:%autopatch %{-v} %{-p:-p%{-p*}}} +diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt +index 5be4cf2d0..ea452e738 100644 +--- a/tools/CMakeLists.txt ++++ b/tools/CMakeLists.txt +@@ -19,6 +19,7 @@ target_link_libraries(rpmlua PRIVATE LUA::LUA) + target_link_libraries(rpmbuild PRIVATE librpmbuild) + target_link_libraries(rpmspec PRIVATE librpmbuild) + target_link_libraries(rpmdeps PRIVATE librpmbuild) ++target_link_libraries(rpmuncompress PRIVATE PkgConfig::LIBARCHIVE) + + if (HAVE_STRCHRNUL) + add_executable(rpmsort rpmsort.c) +diff --git a/tools/rpmuncompress.c b/tools/rpmuncompress.c +index e13cc6a66..c4832332c 100644 +--- a/tools/rpmuncompress.c ++++ b/tools/rpmuncompress.c +@@ -6,6 +6,9 @@ + #include + #include + ++#include ++#include ++ + #include + #include + +@@ -14,6 +17,7 @@ + static int verbose = 0; + static int extract = 0; + static int dryrun = 0; ++static char *dstpath = NULL; + + static struct poptOption optionsTable[] = { + { "extract", 'x', POPT_ARG_VAL, &extract, 1, +@@ -22,6 +26,8 @@ static struct poptOption optionsTable[] = { + N_("provide more detailed output"), NULL }, + { "dry-run", 'n', POPT_ARG_VAL, &dryrun, 1, + N_("only print what would be done"), NULL }, ++ { "path", 'C', POPT_ARG_STRING, &dstpath, 0, ++ N_("extract into a specific path"), NULL }, + + POPT_AUTOALIAS + POPT_AUTOHELP +@@ -78,16 +84,78 @@ static char *doUncompress(const char *fn) + return cmd; + } + ++/** ++ * Detect if an archive has a single top level entry, and it's a directory. ++ * ++ * @param path path of the archive ++ * @return 1 if archive as only a directory as top level entry, ++ * 0 if it contains multiple top level entries or a single file ++ * -1 on archive error ++ */ ++static int singleRoot(const char *path) ++{ ++ struct archive *a; ++ struct archive_entry *entry; ++ int r, ret = -1, rootLen; ++ char *rootName = NULL; ++ ++ a = archive_read_new(); ++ archive_read_support_filter_all(a); ++ archive_read_support_format_all(a); ++ r = archive_read_open_filename(a, path, 10240); ++ if (r != ARCHIVE_OK) { ++ goto afree; ++ } ++ if (archive_read_next_header(a, &entry) != ARCHIVE_OK) { ++ goto afree; ++ } ++ rootName = xstrdup(archive_entry_pathname(entry)); ++ rootLen = strlen(rootName); ++ if (archive_entry_filetype(entry) != AE_IFDIR) { ++ /* Root entry is not a directory */ ++ ret = 0; ++ goto afree; ++ } ++ while (archive_read_next_header(a, &entry) == ARCHIVE_OK) { ++ if (strncmp(rootName, archive_entry_pathname(entry), rootLen)) { ++ /* multiple top level entries */ ++ ret = 0; ++ goto afree; ++ } ++ } ++ ret = 1; ++ ++afree: ++ free(rootName); ++ r = archive_read_free(a); ++ if (r != ARCHIVE_OK) ++ ret = -1; ++ ++ return ret; ++} ++ + static char *doUntar(const char *fn) + { + const struct archiveType_s *at = NULL; + char *buf = NULL; + char *tar = NULL; + const char *taropts = verbose ? "-xvvof" : "-xof"; ++ char *mkdir = NULL; ++ char *stripcd = NULL; + + if ((at = getArchiver(fn)) == NULL) + goto exit; + ++ if (dstpath) { ++ int sr = singleRoot(fn); ++ ++ /* the trick is simple, if the archive has multiple entries, ++ * just extract it into the specified destination path, otherwise ++ * strip the first path entry and extract in the destination path ++ */ ++ rasprintf(&mkdir, "mkdir '%s' ; ", dstpath); ++ rasprintf(&stripcd, " -C '%s' %s", dstpath, sr ? "--strip-components=1" : ""); ++ } + tar = rpmGetPath("%{__tar}", NULL); + if (at->compressed != COMPRESSED_NOT) { + char *zipper = NULL; +@@ -96,7 +164,7 @@ static char *doUntar(const char *fn) + zipper = rpmExpand(at->cmd, " ", at->unpack, " ", + verbose ? "" : at->quiet, NULL); + if (needtar) { +- rasprintf(&buf, "%s '%s' | %s %s -", zipper, fn, tar, taropts); ++ rasprintf(&buf, "%s %s '%s' | %s %s - %s", mkdir ?: "", zipper, fn, tar, taropts, stripcd ?: ""); + } else if (at->compressed == COMPRESSED_GEM) { + char *tmp = xstrdup(fn); + const char *bn = basename(tmp); +@@ -119,11 +187,13 @@ static char *doUntar(const char *fn) + } + free(zipper); + } else { +- rasprintf(&buf, "%s %s '%s'", tar, taropts, fn); ++ rasprintf(&buf, "%s %s %s '%s' %s", mkdir ?: "", tar, taropts, fn, stripcd ?: ""); + } + + exit: + free(tar); ++ free(mkdir); ++ free(stripcd); + return buf; + } + +-- +2.54.0 + + +From 8ee83a3174f4956baad3feb164d68d1b48e629b6 Mon Sep 17 00:00:00 2001 +From: Florian Festi +Date: Mon, 17 Jun 2024 15:03:28 +0200 +Subject: [PATCH 02/14] Pass TZ=UTC to zip in rpmuncompress + +The ZIP format has no notion of time zone, so timestamps are only +meaningful if it is known what time zone they were created in. Pass UTC +to prevent time stamps to depend on local time zone setting and make +builds from sources in zip files (more) reproducible. + +The other archive formats (7zip using UTC, Ruby gems using tar and tar +itself) don't have this issue. Everything else are stream compressors +that don't deal with file meta data. + +Tested manually with Fedora's xz-java-1.9.zip as mentioned in the issue. + +Resolves: #2955 +(cherry picked from commit b847608cc22236ae19af51dc3faef16398df4110) +--- + tools/rpmuncompress.c | 31 +++++++++++++++++-------------- + 1 file changed, 17 insertions(+), 14 deletions(-) + +diff --git a/tools/rpmuncompress.c b/tools/rpmuncompress.c +index c4832332c..4c9e46aad 100644 +--- a/tools/rpmuncompress.c ++++ b/tools/rpmuncompress.c +@@ -40,19 +40,20 @@ struct archiveType_s { + const char *cmd; + const char *unpack; + const char *quiet; ++ int setTZ; + } archiveTypes[] = { +- { COMPRESSED_NOT, 0, "%{__cat}" , "", "" }, +- { COMPRESSED_OTHER, 0, "%{__gzip}", "-dc", "" }, +- { COMPRESSED_BZIP2, 0, "%{__bzip2}", "-dc", "" }, +- { COMPRESSED_ZIP, 1, "%{__unzip}", "", "-qq" }, +- { COMPRESSED_LZMA, 0, "%{__xz}", "-dc", "" }, +- { COMPRESSED_XZ, 0, "%{__xz}", "-dc", "" }, +- { COMPRESSED_LZIP, 0, "%{__lzip}", "-dc", "" }, +- { COMPRESSED_LRZIP, 0, "%{__lrzip}", "-dqo-", "" }, +- { COMPRESSED_7ZIP, 1, "%{__7zip}", "x", "" }, +- { COMPRESSED_ZSTD, 0, "%{__zstd}", "-dc", "" }, +- { COMPRESSED_GEM, 1, "%{__gem}", "unpack", "" }, +- { -1, 0, NULL, NULL, NULL }, ++ { COMPRESSED_NOT, 0, "%{__cat}" , "", "", 0 }, ++ { COMPRESSED_OTHER, 0, "%{__gzip}", "-dc", "", 0 }, ++ { COMPRESSED_BZIP2, 0, "%{__bzip2}", "-dc", "", 0 }, ++ { COMPRESSED_ZIP, 1, "%{__unzip}", "", "-qq", 1 }, ++ { COMPRESSED_LZMA, 0, "%{__xz}", "-dc", "", 0 }, ++ { COMPRESSED_XZ, 0, "%{__xz}", "-dc", "", 0 }, ++ { COMPRESSED_LZIP, 0, "%{__lzip}", "-dc", "", 0 }, ++ { COMPRESSED_LRZIP, 0, "%{__lrzip}", "-dqo-", "", 0 }, ++ { COMPRESSED_7ZIP, 1, "%{__7zip}", "x", "", 0 }, ++ { COMPRESSED_ZSTD, 0, "%{__zstd}", "-dc", "", 0 }, ++ { COMPRESSED_GEM, 1, "%{__gem}", "unpack", "", 0 }, ++ { -1, 0, NULL, NULL, NULL, 0 }, + }; + + static const struct archiveType_s *getArchiver(const char *fn) +@@ -77,7 +78,8 @@ static char *doUncompress(const char *fn) + char *cmd = NULL; + const struct archiveType_s *at = getArchiver(fn); + if (at) { +- cmd = rpmExpand(at->cmd, " ", at->unpack, NULL); ++ cmd = rpmExpand(at->setTZ ? "TZ=UTC " : "", ++ at->cmd, " ", at->unpack, NULL); + /* path must not be expanded */ + cmd = rstrscat(&cmd, " ", fn, NULL); + } +@@ -161,7 +163,8 @@ static char *doUntar(const char *fn) + char *zipper = NULL; + int needtar = (at->extractable == 0); + +- zipper = rpmExpand(at->cmd, " ", at->unpack, " ", ++ zipper = rpmExpand(at->setTZ ? "TZ=UTC " : "", ++ at->cmd, " ", at->unpack, " ", + verbose ? "" : at->quiet, NULL); + if (needtar) { + rasprintf(&buf, "%s %s '%s' | %s %s - %s", mkdir ?: "", zipper, fn, tar, taropts, stripcd ?: ""); +-- +2.54.0 + + +From c4f56c0898b470811034f62099f70be733d58b36 Mon Sep 17 00:00:00 2001 +From: Florian Festi +Date: Mon, 17 Jun 2024 18:19:45 +0200 +Subject: [PATCH 03/14] Free cmd for --dry-run too + +Prevent memory leak and the memory sanatizer failing. + +(cherry picked from commit e8a252dca9f0731dc5f24d91447a6906363ff9ec) +--- + tools/rpmuncompress.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/tools/rpmuncompress.c b/tools/rpmuncompress.c +index 4c9e46aad..e8232acbc 100644 +--- a/tools/rpmuncompress.c ++++ b/tools/rpmuncompress.c +@@ -234,10 +234,10 @@ int main(int argc, char *argv[]) + if (WIFEXITED(status) && WEXITSTATUS(status) == 0) + ec = EXIT_SUCCESS; + } +- free(cmd); + } + + exit: ++ free(cmd); + rpmcliFini(optCon); + return ec; + } +-- +2.54.0 + + +From b94d7b57aaa80d7bb171da846215f306b3d78c3d Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Tue, 27 Aug 2024 13:27:20 +0300 +Subject: [PATCH 04/14] Work around unowned directories in rpmuncompress -C + +Some archives have a directory prefix but are missing the leading +directory entry, work around it by just comparing the leading directory +components (if any) across the archive. + +Add tests for unowned directories with rpmuncompress -C + +GNU tar seems to always place the root directory node in there, but +clearly not all implementations do. One of them being Python tarfile +as of Python 3.12.5, so the test-tarballs created from the pre-existing +source-singleroot.tar.gz content with: + + import tarfile + + tar = tarfile.open("source-singleroot-unowned1.tar.gz", "w:gz") + for name in ["source-strip/file1", "source-strip/file2"]: + tar.add(name) + tar.close() + + tar = tarfile.open("source-singleroot-unowned2.tar.gz", "w:gz") + for name in ["source-strip/dir1", "source-strip/file1", + "source-strip/file2"]: + tar.add(name) + tar.close() + +Fixes: #3250 +(cherry picked from commit 671fc8e5d6633c14c9621903a23c0040acb43f65) +--- + tools/rpmuncompress.c | 13 ++++++++----- + 1 file changed, 8 insertions(+), 5 deletions(-) + +diff --git a/tools/rpmuncompress.c b/tools/rpmuncompress.c +index e8232acbc..2cf988dac 100644 +--- a/tools/rpmuncompress.c ++++ b/tools/rpmuncompress.c +@@ -112,15 +112,18 @@ static int singleRoot(const char *path) + goto afree; + } + rootName = xstrdup(archive_entry_pathname(entry)); +- rootLen = strlen(rootName); +- if (archive_entry_filetype(entry) != AE_IFDIR) { +- /* Root entry is not a directory */ ++ char *sep = strchr(rootName, '/'); ++ if (sep == NULL) { ++ /* No directories in the pathname */ + ret = 0; + goto afree; + } ++ ++ /* Do all entries in the archive start with the same lead directory? */ ++ rootLen = sep - rootName + 1; + while (archive_read_next_header(a, &entry) == ARCHIVE_OK) { +- if (strncmp(rootName, archive_entry_pathname(entry), rootLen)) { +- /* multiple top level entries */ ++ const char *p = archive_entry_pathname(entry); ++ if (strncmp(rootName, p, rootLen)) { + ret = 0; + goto afree; + } +-- +2.54.0 + + +From b754b7baa2ab07cb4b987b6c4d160a12197e320e Mon Sep 17 00:00:00 2001 +From: Florian Festi +Date: Thu, 29 Aug 2024 12:29:05 +0200 +Subject: [PATCH 05/14] Replace gcc only ? : operator usage + +(cherry picked from commit c90733d96e93f3544853821787190fbb01472b15) +--- + tools/rpmuncompress.c | 23 +++++++++++++---------- + 1 file changed, 13 insertions(+), 10 deletions(-) + +diff --git a/tools/rpmuncompress.c b/tools/rpmuncompress.c +index 2cf988dac..a3b7464d2 100644 +--- a/tools/rpmuncompress.c ++++ b/tools/rpmuncompress.c +@@ -152,14 +152,17 @@ static char *doUntar(const char *fn) + goto exit; + + if (dstpath) { +- int sr = singleRoot(fn); +- +- /* the trick is simple, if the archive has multiple entries, +- * just extract it into the specified destination path, otherwise +- * strip the first path entry and extract in the destination path +- */ +- rasprintf(&mkdir, "mkdir '%s' ; ", dstpath); +- rasprintf(&stripcd, " -C '%s' %s", dstpath, sr ? "--strip-components=1" : ""); ++ int sr = singleRoot(fn); ++ ++ /* the trick is simple, if the archive has multiple entries, ++ * just extract it into the specified destination path, otherwise ++ * strip the first path entry and extract in the destination path ++ */ ++ rasprintf(&mkdir, "mkdir '%s' ; ", dstpath); ++ rasprintf(&stripcd, " -C '%s' %s", dstpath, sr ? "--strip-components=1" : ""); ++ } else { ++ mkdir = xstrdup(""); ++ stripcd = xstrdup(""); + } + tar = rpmGetPath("%{__tar}", NULL); + if (at->compressed != COMPRESSED_NOT) { +@@ -170,7 +173,7 @@ static char *doUntar(const char *fn) + at->cmd, " ", at->unpack, " ", + verbose ? "" : at->quiet, NULL); + if (needtar) { +- rasprintf(&buf, "%s %s '%s' | %s %s - %s", mkdir ?: "", zipper, fn, tar, taropts, stripcd ?: ""); ++ rasprintf(&buf, "%s %s '%s' | %s %s - %s", mkdir, zipper, fn, tar, taropts, stripcd); + } else if (at->compressed == COMPRESSED_GEM) { + char *tmp = xstrdup(fn); + const char *bn = basename(tmp); +@@ -193,7 +196,7 @@ static char *doUntar(const char *fn) + } + free(zipper); + } else { +- rasprintf(&buf, "%s %s %s '%s' %s", mkdir ?: "", tar, taropts, fn, stripcd ?: ""); ++ rasprintf(&buf, "%s %s %s '%s' %s", mkdir, tar, taropts, fn, stripcd); + } + + exit: +-- +2.54.0 + + +From dc2a2d06006b3f55dfd4e80bcd8f8e801ff0c0c9 Mon Sep 17 00:00:00 2001 +From: Florian Festi +Date: Thu, 29 Aug 2024 15:40:18 +0200 +Subject: [PATCH 06/14] Return unique top directory iff it exists + +Used in the next commit. + +(cherry picked from commit 7b46a0d0c336ee865821de53cdcfff09752669ed) +--- + tools/rpmuncompress.c | 18 +++++++++++------- + 1 file changed, 11 insertions(+), 7 deletions(-) + +diff --git a/tools/rpmuncompress.c b/tools/rpmuncompress.c +index a3b7464d2..88956d173 100644 +--- a/tools/rpmuncompress.c ++++ b/tools/rpmuncompress.c +@@ -90,11 +90,11 @@ static char *doUncompress(const char *fn) + * Detect if an archive has a single top level entry, and it's a directory. + * + * @param path path of the archive +- * @return 1 if archive as only a directory as top level entry, +- * 0 if it contains multiple top level entries or a single file +- * -1 on archive error ++ * @return only top level directory (if any), ++ * NULL if it contains multiple top level entries or a single file ++ * or on archive error + */ +-static int singleRoot(const char *path) ++static char * singleRoot(const char *path) + { + struct archive *a; + struct archive_entry *entry; +@@ -128,15 +128,18 @@ static int singleRoot(const char *path) + goto afree; + } + } ++ *sep = '\0'; + ret = 1; + + afree: +- free(rootName); + r = archive_read_free(a); + if (r != ARCHIVE_OK) + ret = -1; + +- return ret; ++ if (ret != 1) ++ rootName = _free(rootName); ++ ++ return rootName; + } + + static char *doUntar(const char *fn) +@@ -152,7 +155,7 @@ static char *doUntar(const char *fn) + goto exit; + + if (dstpath) { +- int sr = singleRoot(fn); ++ char * sr = singleRoot(fn); + + /* the trick is simple, if the archive has multiple entries, + * just extract it into the specified destination path, otherwise +@@ -160,6 +163,7 @@ static char *doUntar(const char *fn) + */ + rasprintf(&mkdir, "mkdir '%s' ; ", dstpath); + rasprintf(&stripcd, " -C '%s' %s", dstpath, sr ? "--strip-components=1" : ""); ++ free(sr); + } else { + mkdir = xstrdup(""); + stripcd = xstrdup(""); +-- +2.54.0 + + +From 5847a366c9172eaabd2743119c8f8b94b0b3e9ef Mon Sep 17 00:00:00 2001 +From: Florian Festi +Date: Thu, 29 Aug 2024 15:11:51 +0200 +Subject: [PATCH 07/14] rpmuncompress: Support -C for zip and 7zip archives + +As both don't support an equivalent to tars --strip-components=1 we move +the files out of top directory ourselves with shell commands. + +Support for Ruby gems is still missing - iff at all possible or +desirable. + +Resolves: #3249 +(backported from commit abf057616d8f73cc0540520121ea3c4cad5d8cdd) +--- + tools/rpmuncompress.c | 58 +++++++++++++++++++++++++++++-------------- + 1 file changed, 40 insertions(+), 18 deletions(-) + +diff --git a/tools/rpmuncompress.c b/tools/rpmuncompress.c +index 88956d173..692ca322f 100644 +--- a/tools/rpmuncompress.c ++++ b/tools/rpmuncompress.c +@@ -40,19 +40,20 @@ struct archiveType_s { + const char *cmd; + const char *unpack; + const char *quiet; ++ const char *dest; + int setTZ; + } archiveTypes[] = { +- { COMPRESSED_NOT, 0, "%{__cat}" , "", "", 0 }, +- { COMPRESSED_OTHER, 0, "%{__gzip}", "-dc", "", 0 }, +- { COMPRESSED_BZIP2, 0, "%{__bzip2}", "-dc", "", 0 }, +- { COMPRESSED_ZIP, 1, "%{__unzip}", "", "-qq", 1 }, +- { COMPRESSED_LZMA, 0, "%{__xz}", "-dc", "", 0 }, +- { COMPRESSED_XZ, 0, "%{__xz}", "-dc", "", 0 }, +- { COMPRESSED_LZIP, 0, "%{__lzip}", "-dc", "", 0 }, +- { COMPRESSED_LRZIP, 0, "%{__lrzip}", "-dqo-", "", 0 }, +- { COMPRESSED_7ZIP, 1, "%{__7zip}", "x", "", 0 }, +- { COMPRESSED_ZSTD, 0, "%{__zstd}", "-dc", "", 0 }, +- { COMPRESSED_GEM, 1, "%{__gem}", "unpack", "", 0 }, ++ { COMPRESSED_NOT, 0, "%{__cat}" , "", "", "", 0 }, ++ { COMPRESSED_OTHER, 0, "%{__gzip}", "-dc", "", "", 0 }, ++ { COMPRESSED_BZIP2, 0, "%{__bzip2}", "-dc", "", "", 0 }, ++ { COMPRESSED_ZIP, 1, "%{__unzip}", "", "-qq", "-d", 1 }, ++ { COMPRESSED_LZMA, 0, "%{__xz}", "-dc", "", "", 0 }, ++ { COMPRESSED_XZ, 0, "%{__xz}", "-dc", "", "", 0 }, ++ { COMPRESSED_LZIP, 0, "%{__lzip}", "-dc", "", "", 0 }, ++ { COMPRESSED_LRZIP, 0, "%{__lrzip}", "-dqo-", "", "", 0 }, ++ { COMPRESSED_7ZIP, 1, "%{__7zip}", "x", "-bso0 -bsp0", "-o", 0 }, ++ { COMPRESSED_ZSTD, 0, "%{__zstd}", "-dc", "", "", 0 }, ++ { COMPRESSED_GEM, 1, "%{__gem}", "unpack", "", "--target=", 0 }, + { -1, 0, NULL, NULL, NULL, 0 }, + }; + +@@ -154,15 +155,37 @@ static char *doUntar(const char *fn) + if ((at = getArchiver(fn)) == NULL) + goto exit; + ++ int needtar = (at->extractable == 0); ++ + if (dstpath) { + char * sr = singleRoot(fn); + +- /* the trick is simple, if the archive has multiple entries, +- * just extract it into the specified destination path, otherwise +- * strip the first path entry and extract in the destination path ++ /* if the archive has multiple entries, just extract it into the ++ * specified destination path, otherwise also strip the first path ++ * entry + */ +- rasprintf(&mkdir, "mkdir '%s' ; ", dstpath); +- rasprintf(&stripcd, " -C '%s' %s", dstpath, sr ? "--strip-components=1" : ""); ++ if (needtar) { ++ rasprintf(&mkdir, "mkdir -p '%s' ; ", dstpath); ++ rasprintf(&stripcd, " -C '%s' %s", dstpath, sr ? "--strip-components=1" : ""); ++ } else { ++ if (sr) { ++ rasprintf(&mkdir, "mkdir -p '%s' ; tmp=`mktemp -d -p'%s'` ; ", ++ dstpath, dstpath); ++ char * moveup; ++ /* Extract into temp directory to avoid collisions */ ++ /* then move files in top dir two levels up */ ++ rasprintf( ++ &moveup, ++ " && " ++ "(shopt -s dotglob; mv \"$tmp\"/'%s'/* '%s') && " ++ "rmdir \"$tmp\"/'%s' \"$tmp\" ", sr, dstpath, sr); ++ rasprintf(&stripcd, "%s\"$tmp\" %s", at->dest, moveup); ++ free(moveup); ++ } else { ++ rasprintf(&mkdir, "mkdir -p '%s' ; ", dstpath); ++ rasprintf(&stripcd, "%s'%s'", at->dest, dstpath); ++ } ++ } + free(sr); + } else { + mkdir = xstrdup(""); +@@ -171,7 +194,6 @@ static char *doUntar(const char *fn) + tar = rpmGetPath("%{__tar}", NULL); + if (at->compressed != COMPRESSED_NOT) { + char *zipper = NULL; +- int needtar = (at->extractable == 0); + + zipper = rpmExpand(at->setTZ ? "TZ=UTC " : "", + at->cmd, " ", at->unpack, " ", +@@ -196,7 +218,7 @@ static char *doUntar(const char *fn) + free(gem); + free(tmp); + } else { +- rasprintf(&buf, "%s '%s'", zipper, fn); ++ rasprintf(&buf, "%s%s '%s' %s", mkdir, zipper, fn, stripcd); + } + free(zipper); + } else { +-- +2.54.0 + + +From 127d4a2947005688e645b9e9545b178efd5c0e6c Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Mon, 2 Sep 2024 10:51:57 +0300 +Subject: [PATCH 08/14] Fix rpmuncompress going interactive on re-extraction of + zip / 7zip archives + +Testing this is a PITA compared to the issue itself: we can't use the +existing -C tests because that fails due to mv, and to avoid -C we +need to control where it runs, and to do that we need --chdir which +causes extra warnings from brap so we need to ignore stderr which +we would not want to do here, really. + +Fixes: #2779 +(cherry picked from commit fa3ec7e3d73a93911bb8d3d1ac87c2d64eeda680) +--- + tools/rpmuncompress.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/tools/rpmuncompress.c b/tools/rpmuncompress.c +index 692ca322f..30bd74a2a 100644 +--- a/tools/rpmuncompress.c ++++ b/tools/rpmuncompress.c +@@ -46,12 +46,12 @@ struct archiveType_s { + { COMPRESSED_NOT, 0, "%{__cat}" , "", "", "", 0 }, + { COMPRESSED_OTHER, 0, "%{__gzip}", "-dc", "", "", 0 }, + { COMPRESSED_BZIP2, 0, "%{__bzip2}", "-dc", "", "", 0 }, +- { COMPRESSED_ZIP, 1, "%{__unzip}", "", "-qq", "-d", 1 }, ++ { COMPRESSED_ZIP, 1, "%{__unzip}", "-u", "-qq", "-d", 1 }, + { COMPRESSED_LZMA, 0, "%{__xz}", "-dc", "", "", 0 }, + { COMPRESSED_XZ, 0, "%{__xz}", "-dc", "", "", 0 }, + { COMPRESSED_LZIP, 0, "%{__lzip}", "-dc", "", "", 0 }, + { COMPRESSED_LRZIP, 0, "%{__lrzip}", "-dqo-", "", "", 0 }, +- { COMPRESSED_7ZIP, 1, "%{__7zip}", "x", "-bso0 -bsp0", "-o", 0 }, ++ { COMPRESSED_7ZIP, 1, "%{__7zip}", "x -y", "-bso0 -bsp0", "-o", 0 }, + { COMPRESSED_ZSTD, 0, "%{__zstd}", "-dc", "", "", 0 }, + { COMPRESSED_GEM, 1, "%{__gem}", "unpack", "", "--target=", 0 }, + { -1, 0, NULL, NULL, NULL, 0 }, +-- +2.54.0 + + +From b28d1c4b8cafc45498b93a324cb8dd52ab2a753b Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Thu, 3 Oct 2024 12:51:05 +0300 +Subject: [PATCH 09/14] Fix up rpmuncompress not being compiled as C++ + +This one lone source got overlooked in the initial C++ enablement +in commit 37cdcc29269eb5cd603c69be07f83f113ce479cd. C++ doesn't like +jumping over variable declarations, so just move the declarations +early to minimally fix the build. + +Nicely goes to show the danger of such hacks - you can always miss +something. On the library side things are likely to blow up in +linkage but you never know really. + +(backported from commit 826339518a5ee43498ab0fe047a87540a980232e) +--- + tools/rpmuncompress.c | 6 ++++-- + 1 file changed, 4 insertions(+), 2 deletions(-) + +diff --git a/tools/rpmuncompress.c b/tools/rpmuncompress.c +index 30bd74a2a..932ae3a3d 100644 +--- a/tools/rpmuncompress.c ++++ b/tools/rpmuncompress.c +@@ -101,6 +101,7 @@ static char * singleRoot(const char *path) + struct archive_entry *entry; + int r, ret = -1, rootLen; + char *rootName = NULL; ++ char *sep = NULL; + + a = archive_read_new(); + archive_read_support_filter_all(a); +@@ -113,7 +114,7 @@ static char * singleRoot(const char *path) + goto afree; + } + rootName = xstrdup(archive_entry_pathname(entry)); +- char *sep = strchr(rootName, '/'); ++ sep = strchr(rootName, '/'); + if (sep == NULL) { + /* No directories in the pathname */ + ret = 0; +@@ -151,11 +152,12 @@ static char *doUntar(const char *fn) + const char *taropts = verbose ? "-xvvof" : "-xof"; + char *mkdir = NULL; + char *stripcd = NULL; ++ int needtar = 0; + + if ((at = getArchiver(fn)) == NULL) + goto exit; + +- int needtar = (at->extractable == 0); ++ needtar = (at->extractable == 0); + + if (dstpath) { + char * sr = singleRoot(fn); +-- +2.54.0 + + +From 0988bb56aa86c3fb0719e45910621068830f916b Mon Sep 17 00:00:00 2001 +From: Michal Domonkos +Date: Tue, 9 Jun 2026 15:57:08 +0200 +Subject: [PATCH 10/14] Refactor singleRoot() + +Locate the slash first in the returned const string and only duplicate +it when actually needed. This will save us an extra string copy in the +next commits. + +No functional change. + +(cherry picked from commit 24744c59d359cc51fc644ddf2707c4ef1a639cf6) +--- + tools/rpmuncompress.c | 18 ++++++++++++------ + 1 file changed, 12 insertions(+), 6 deletions(-) + +diff --git a/tools/rpmuncompress.c b/tools/rpmuncompress.c +index 932ae3a3d..68d1481f6 100644 +--- a/tools/rpmuncompress.c ++++ b/tools/rpmuncompress.c +@@ -100,8 +100,9 @@ static char * singleRoot(const char *path) + struct archive *a; + struct archive_entry *entry; + int r, ret = -1, rootLen; ++ const char *p = NULL; ++ const char *sep = NULL; + char *rootName = NULL; +- char *sep = NULL; + + a = archive_read_new(); + archive_read_support_filter_all(a); +@@ -113,24 +114,29 @@ static char * singleRoot(const char *path) + if (archive_read_next_header(a, &entry) != ARCHIVE_OK) { + goto afree; + } +- rootName = xstrdup(archive_entry_pathname(entry)); +- sep = strchr(rootName, '/'); ++ ++ /* Extract the lead directory from the first entry */ ++ p = archive_entry_pathname(entry); ++ sep = strchr(p, '/'); + if (sep == NULL) { + /* No directories in the pathname */ + ret = 0; + goto afree; ++ } else { ++ rootName = xstrdup(p); ++ rootLen = sep - p + 1; + } + + /* Do all entries in the archive start with the same lead directory? */ +- rootLen = sep - rootName + 1; + while (archive_read_next_header(a, &entry) == ARCHIVE_OK) { +- const char *p = archive_entry_pathname(entry); ++ p = archive_entry_pathname(entry); + if (strncmp(rootName, p, rootLen)) { + ret = 0; + goto afree; + } + } +- *sep = '\0'; ++ ++ rootName[rootLen - 1] = '\0'; + ret = 1; + + afree: +-- +2.54.0 + + +From 3a441228720f71bcc132e535c36913be9cc69322 Mon Sep 17 00:00:00 2001 +From: Michal Domonkos +Date: Tue, 9 Jun 2026 16:34:25 +0200 +Subject: [PATCH 11/14] Invert conditional in singleRoot() + +No functional change, just makes the next commit simpler. + +(cherry picked from commit 94cfcceff77d730f1c3432fdb572fc175a3ffdf6) +--- + tools/rpmuncompress.c | 8 ++++---- + 1 file changed, 4 insertions(+), 4 deletions(-) + +diff --git a/tools/rpmuncompress.c b/tools/rpmuncompress.c +index 68d1481f6..eef2f4f0e 100644 +--- a/tools/rpmuncompress.c ++++ b/tools/rpmuncompress.c +@@ -118,13 +118,13 @@ static char * singleRoot(const char *path) + /* Extract the lead directory from the first entry */ + p = archive_entry_pathname(entry); + sep = strchr(p, '/'); +- if (sep == NULL) { ++ if (sep) { ++ rootName = xstrdup(p); ++ rootLen = sep - p + 1; ++ } else { + /* No directories in the pathname */ + ret = 0; + goto afree; +- } else { +- rootName = xstrdup(p); +- rootLen = sep - p + 1; + } + + /* Do all entries in the archive start with the same lead directory? */ +-- +2.54.0 + + +From 18cc9e175ac6807148144099acea22147da3dddf Mon Sep 17 00:00:00 2001 +From: Michal Domonkos +Date: Tue, 9 Jun 2026 16:20:59 +0200 +Subject: [PATCH 12/14] Handle singleroot archives without trailing slash + +While some tar implementations (like GNU tar) do include a trailing +slash in directory entries, others (like ptar(1) written in Perl) do +not. Handle the latter in the singleroot detection logic as well. + +Add a test archive generated with ptar(1) that has the following layout: + + source-strip + source-strip/file2 + source-strip/file1 + source-strip/dir1 + source-strip/dir1/file3 + +Fixes: #4182 +(cherry picked from commit 27ff4d457ff66673f66b0933234e5ed56932641f) +--- + tools/rpmuncompress.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/tools/rpmuncompress.c b/tools/rpmuncompress.c +index eef2f4f0e..6aff47757 100644 +--- a/tools/rpmuncompress.c ++++ b/tools/rpmuncompress.c +@@ -121,6 +121,9 @@ static char * singleRoot(const char *path) + if (sep) { + rootName = xstrdup(p); + rootLen = sep - p + 1; ++ } else if (archive_entry_filetype(entry) == AE_IFDIR) { ++ rootName = rstrscat(NULL, p, "/", NULL); ++ rootLen = strlen(rootName); + } else { + /* No directories in the pathname */ + ret = 0; +-- +2.54.0 + + +From ccdd46de999955b28ff5ae54b738137a8e14a8c7 Mon Sep 17 00:00:00 2001 +From: Michal Domonkos +Date: Fri, 12 Jun 2026 13:13:22 +0200 +Subject: [PATCH 13/14] Prevent command injection in rpmuncompress(1) + +When extracting a singleroot archive in -C mode with an archiver that +can uncompress and extract in one go (such as zip), we will include the +top-level directory name verbatim in the shell command that does our own +variant of tar's --strip-components=1. This allows an attacker to craft +such an archive where the top-level directory name contains arbitrary +shell code enclosed in single quotes, which rpmuncompress will happily +execute when merely extracting it. + +That is nasty since one does not expect a utility like rpmuncompress to +*execute* anything from the archive when extracting it. This is now even +more relevant with commit a698d1b6b18e430806c54755af56d47ac401e430 which +exposed rpmuncompress for general use by installing it into $PATH. + +Fix by simply *not* passing the top-level directory name, as returned by +singleRoot(), to the shell, ever. Luckily, we don't really need to since +we know that the temp directory will only have a single top-level entry, +and therefore can use a glob to just let the shell expand to it. + +This relies on our singleroot detection logic *and* the archiver having +a common understanding of what a singleroot archive is, of course, which +may not be ideal. What could theoretically happen is that we consider an +archive singleroot but the archiver, for some reason, extracts multiple +top-level entries, or the single entry will have a different name. This +could result in the destination directory to have an unexpected layout +once we do the move. That said, this is all just speculation, and even +if true, does not seem to be a security flaw unlike the one being fixed. + +An alternative fix would be sanitizing the directory name before passing +it to the shell (like the suggested patch in CVE-2026-44604) but that is +too brittle, error prone and just unnecessarily complicated. + +Ideally, we would just not shell out at all, and use the libarchive API +to do the whole dance, which we may in the future (see #4236). But for +now, this minimal fix should do the job. + +Add also a test archive created with the following Python 3 code adapted +from the CVE: + + import zipfile + name = "evil'$(touch beenhere)'" + with zipfile.ZipFile("source-singleroot-evil.zip", "w") as z: + z.writestr(f"{name}/README.txt", "x") + +Fixes: CVE-2026-44604 +(cherry picked from commit 0693ce7674c423f382b148763a29f7faa0aacb41) +--- + tools/rpmuncompress.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/tools/rpmuncompress.c b/tools/rpmuncompress.c +index 6aff47757..3652cc6bb 100644 +--- a/tools/rpmuncompress.c ++++ b/tools/rpmuncompress.c +@@ -188,8 +188,8 @@ static char *doUntar(const char *fn) + rasprintf( + &moveup, + " && " +- "(shopt -s dotglob; mv \"$tmp\"/'%s'/* '%s') && " +- "rmdir \"$tmp\"/'%s' \"$tmp\" ", sr, dstpath, sr); ++ "(shopt -s dotglob; mv \"$tmp\"/*/* '%s') && " ++ "rmdir \"$tmp\"/* \"$tmp\" ", dstpath); + rasprintf(&stripcd, "%s\"$tmp\" %s", at->dest, moveup); + free(moveup); + } else { +-- +2.54.0 + + +From 6ee2186a8b246d7cd8efff6c449c509669206760 Mon Sep 17 00:00:00 2001 +From: Michal Domonkos +Date: Fri, 12 Jun 2026 13:16:39 +0200 +Subject: [PATCH 14/14] Revert "Return unique top directory iff it exists" + +Now that we no longer need the singleroot name in the caller, go back to +just returning an integer. This also prevents an accidental inclusion of +the name in a shell command in the future (see previous commit). + +Adjust and simplify the singleRoot() description comment while at it. + +This reverts commit 7b46a0d0c336ee865821de53cdcfff09752669ed. + +(cherry picked from commit 3a9145cddff47e6324a9fff3c9a6ae036d7a7db5) +--- + tools/rpmuncompress.c | 18 +++++++----------- + 1 file changed, 7 insertions(+), 11 deletions(-) + +diff --git a/tools/rpmuncompress.c b/tools/rpmuncompress.c +index 3652cc6bb..7233e4bb9 100644 +--- a/tools/rpmuncompress.c ++++ b/tools/rpmuncompress.c +@@ -91,11 +91,11 @@ static char *doUncompress(const char *fn) + * Detect if an archive has a single top level entry, and it's a directory. + * + * @param path path of the archive +- * @return only top level directory (if any), +- * NULL if it contains multiple top level entries or a single file +- * or on archive error ++ * @return 1 if the archive has a single top level, directory entry, ++ * 0 otherwise, ++ * -1 on archive error + */ +-static char * singleRoot(const char *path) ++static int singleRoot(const char *path) + { + struct archive *a; + struct archive_entry *entry; +@@ -139,18 +139,15 @@ static char * singleRoot(const char *path) + } + } + +- rootName[rootLen - 1] = '\0'; + ret = 1; + + afree: ++ free(rootName); + r = archive_read_free(a); + if (r != ARCHIVE_OK) + ret = -1; + +- if (ret != 1) +- rootName = _free(rootName); +- +- return rootName; ++ return ret; + } + + static char *doUntar(const char *fn) +@@ -169,7 +166,7 @@ static char *doUntar(const char *fn) + needtar = (at->extractable == 0); + + if (dstpath) { +- char * sr = singleRoot(fn); ++ int sr = singleRoot(fn); + + /* if the archive has multiple entries, just extract it into the + * specified destination path, otherwise also strip the first path +@@ -197,7 +194,6 @@ static char *doUntar(const char *fn) + rasprintf(&stripcd, "%s'%s'", at->dest, dstpath); + } + } +- free(sr); + } else { + mkdir = xstrdup(""); + stripcd = xstrdup(""); +-- +2.54.0 + diff --git a/rpm.spec b/rpm.spec index b6583a2..b49dafd 100644 --- a/rpm.spec +++ b/rpm.spec @@ -27,7 +27,7 @@ %global rpmver 4.19.1.1 #global snapver rc1 -%global baserelease 23 +%global baserelease 24 %global sover 10 %global srcver %{rpmver}%{?snapver:-%{snapver}} @@ -175,6 +175,8 @@ rpm-4.19.x-multisig-verify-fixes.patch rpm-4.19.x-nsswitch-enable.patch 0001-Fix-empty-password-field-in-passwd-group-causing-ent.patch +rpm-4.19.x-add-autosetup-C.patch + # These are not yet upstream rpm-4.7.1-geode-i686.patch @@ -664,6 +666,9 @@ fi %doc %{_defaultdocdir}/rpm/API/ %changelog +* Thu Jun 18 2026 Michal Domonkos - 4.19.1.1-24 +- Add support for %%autosetup -C (RHEL-141269) + * Thu Feb 05 2026 Michal Domonkos - 4.19.1.1-23 - Fix key import API to return NOTTRUSTED for disabled algorithms (RHEL-112394)