Accepting request 1245646 from Base:System

- make the rpm package not depend on libarchive

OBS-URL: https://build.opensuse.org/request/show/1245646
OBS-URL: https://build.opensuse.org/package/show/openSUSE:Factory/rpm?expand=0&rev=319
This commit is contained in:
Ana Guerrero 2025-02-17 19:53:29 +00:00 committed by Git OBS Bridge
commit 70c5694e7e
7 changed files with 691 additions and 149 deletions

View File

@ -1,122 +0,0 @@
From fc04a1bde1941d2c61a9e33e55c5c492327674ba Mon Sep 17 00:00:00 2001
From: Jan Zerebecki <jan.suse@zerebecki.de>
Date: Thu, 15 Feb 2024 09:57:35 +0100
Subject: [PATCH 1/3] Add option to set mtime of files in rpms
to SOURCE_DATE_EPOCH.
For backwards compatibility the option clamp / limit the maximum mtime
is retained.
Setting it ouright avoids problems with an incorrectly older clock. It
also avoids problems with build scrips that incorrectly change file
mtimes when SOURCE_DATE_EPOCH_MTIME is in use.
mtimes are required to increase with new versions and releases
of an rpm with the same name, as rsync without --checksum and similar
tools would get confused if the content changes without newer mtime.
If SOURCE_DATE_EPOCH_MTIME is set use it instead for file modification time
stamps. It is supposed to be newer. This can be used if we might want to
compare if the file content remains the same when a build dependency
changes while a build script embeds SOURCE_DATE_EPOCH in the file
content.
This can be used to support automatic rebuilds. Normally automatic
rebuilds work, but together with reproducible builds an undesirable
situation may occur. If a build e.g. embeds SOURCE_DATE_EPOCH in the
output, then the output changes every time such a rebuild happens, which
can be very often. This is to be avoided as updating packages without
necessity is too expensive.
---
build/files.c | 33 ++++++++++++++++++++++++++++-----
docs/manual/buildprocess.md | 5 +++--
2 files changed, 31 insertions(+), 7 deletions(-)
diff --git a/build/files.c b/build/files.c
index c403c806e..cec7999ca 100644
--- a/build/files.c
+++ b/build/files.c
@@ -1033,14 +1033,34 @@ static void genCpioListAndHeader(FileList fl, Package pkg, int isSrc)
rpm_loff_t totalFileSize = 0;
Header h = pkg->header; /* just a shortcut */
int override_date = 0;
+ int set_mtime = 0;
time_t source_date_epoch = 0;
char *srcdate = getenv("SOURCE_DATE_EPOCH");
+ char *msrcdate = getenv("SOURCE_DATE_EPOCH_MTIME");
- /* Limit the maximum date to SOURCE_DATE_EPOCH if defined
- * similar to the tar --clamp-mtime option
+ /* If SOURCE_DATE_EPOCH_MTIME is set use it for file modification time
+ * stamps, it is supposed to be newer. This can be used if we might want to
+ * compare if the file content remains the same when a build dependency
+ * changes while a build script embeds SOURCE_DATE_EPOCH in the file
+ * content. mtimes are required to increase with new versions and releases
+ * of an rpm with the same name, as rsync without --checksum and similar
+ * tools would get confused if the content changes without newer mtime. */
+ if (msrcdate != NULL) {
+ srcdate = msrcdate;
+ }
+
+ /* Set the file mtime to SOURCE_DATE_EPOCH it if requested to make the
+ * resulting rpm reproducible.
* https://reproducible-builds.org/specs/source-date-epoch/
+ *
+ * For backwards compatibility clamp / limit the maximum mtime if requested
+ * similar the tar --clamp-mtime option. Setting it ouright avoids problems
+ * with an incorrectly older clock. It also avoids problems with build
+ * scrips that incorrectly change file mtimes when SOURCE_DATE_EPOCH_MTIME
+ * is in use.
*/
- if (srcdate && rpmExpandNumeric("%{?clamp_mtime_to_source_date_epoch}")) {
+ if (srcdate && (rpmExpandNumeric("%{?clamp_mtime_to_source_date_epoch}")
+ || rpmExpandNumeric("%{?set_mtime_to_source_date_epoch}"))) {
char *endptr;
errno = 0;
source_date_epoch = strtol(srcdate, &endptr, 10);
@@ -1049,6 +1069,9 @@ static void genCpioListAndHeader(FileList fl, Package pkg, int isSrc)
fl->processingFailed = 1;
}
override_date = 1;
+ if (rpmExpandNumeric("%{?set_mtime_to_source_date_epoch}")) {
+ set_mtime = 1;
+ }
}
/*
@@ -1191,8 +1214,8 @@ static void genCpioListAndHeader(FileList fl, Package pkg, int isSrc)
totalFileSize += flp->fl_size;
}
}
-
- if (override_date && flp->fl_mtime > source_date_epoch) {
+
+ if (override_date && (flp->fl_mtime > source_date_epoch || set_mtime)) {
flp->fl_mtime = source_date_epoch;
}
/*
diff --git a/docs/manual/buildprocess.md b/docs/manual/buildprocess.md
index 1ceb47a7e..64cd35626 100644
--- a/docs/manual/buildprocess.md
+++ b/docs/manual/buildprocess.md
@@ -94,13 +94,14 @@ Macro name | Description
`%_build_pkgcheck` | Progam to run on each generated binary package
`%_build_pkcheck_set` | Program to run on the generated binary package set
-### Reproducability
+### Reproducibility
Macro name | Description
--------------------------------------|-----------
`%source_date_epoch_from_changelog` | Set `SOURCE_DATE_EPOCH` from latest `%changelog` entry
`%use_source_date_epoch_as_buildtime` | Set package BuildTime to `SOURCE_DATE_EPOCH`
-`%clamp_mtime_to_source_date_epoch` | Ensure file timestamps are not newer than `SOURCE_DATE_EPOCH`
+`%set_mtime_to_source_date_epoch` | Set file modification timestamps to `SOURCE_DATE_EPOCH_MTIME` or as fallback to `SOURCE_DATE_EPOCH`
+`%clamp_mtime_to_source_date_epoch` | You should use the above instead, it is for backwards compatibility only. Ensure file timestamps are not newer than `SOURCE_DATE_EPOCH`
### Vendor defaults
--
2.30.2

12
buildsys.diff Normal file
View File

@ -0,0 +1,12 @@
--- build/parseSpec.c.orig 2025-02-13 13:20:21.075462279 +0000
+++ build/parseSpec.c 2025-02-13 13:15:42.447942795 +0000
@@ -1429,7 +1429,8 @@ static rpmRC parseSpecParts(rpmSpec spec
/* rpmGlob returns files sorted */
if (rpmGlob(pattern, &argc, &argv) == 0) {
for (int i = 0; i < argc; i++) {
- rpmlog(RPMLOG_NOTICE, "Reading %s\n", argv[i]);
+ if (stage != PARSE_BUILDSYS)
+ rpmlog(RPMLOG_NOTICE, "Reading %s\n", argv[i]);
pushOFI(spec, argv[i]);
snprintf(spec->fileStack->readBuf, spec->fileStack->readBufLen,
"# Spec part read from %s\n\n", argv[i]);

View File

@ -1,16 +1,6 @@
--- macros.in.orig 2024-12-16 12:48:44.110837972 +0000
+++ macros.in 2024-12-16 12:52:32.014378635 +0000
@@ -118,6 +118,9 @@
# The directory where sources/patches will be unpacked and built.
%_builddir %{_topdir}/BUILD
+# The build root where built files will be installed into
+%buildroot %{_builddir}/%{NAME}-%{VERSION}-build/BUILDROOT
+
# The interpreter used for build scriptlets.
%_buildshell /bin/sh
@@ -163,6 +166,7 @@
--- macros.in.orig 2025-02-12 13:23:21.868124201 +0000
+++ macros.in 2025-02-12 13:23:58.436059109 +0000
@@ -163,6 +163,7 @@
%{?_unique_debug_names:--unique-debug-suffix "-%{VERSION}-%{RELEASE}.%{_arch}"} \\\
%{?_unique_debug_srcs:--unique-debug-src-base "%{name}-%{VERSION}-%{RELEASE}.%{_arch}"} \\\
%{?_find_debuginfo_dwz_opts} \\\
@ -18,7 +8,7 @@
%{?_find_debuginfo_opts} \\\
%{?_debugsource_packages:-S debugsourcefiles.list} \\\
"%{builddir}/%{?buildsubdir}"\
@@ -216,7 +220,8 @@ Supplements: (%{name} = %{version}-%{r
@@ -216,7 +217,8 @@ Supplements: (%{name} = %{version}-%{r
%files langpack-%{1}\
%{nil}
@ -28,7 +18,7 @@
%_defaultlicensedir %{_datadir}/licenses
# Following macros for filtering auto deps must not be used in spec files.
@@ -275,7 +280,8 @@ Supplements: (%{name} = %{version}-%{r
@@ -275,7 +277,8 @@ Supplements: (%{name} = %{version}-%{r
%_tmppath %{_var}/tmp
# Path to top of build area.
@ -38,7 +28,7 @@
#==============================================================================
# ---- Optional rpmrc macros.
@@ -366,7 +372,7 @@ Supplements: (%{name} = %{version}-%{r
@@ -366,7 +369,7 @@ Supplements: (%{name} = %{version}-%{r
# "w.ufdio" uncompressed
#
#%_source_payload w9.gzdio
@ -47,7 +37,7 @@
# Algorithm to use for generating file checksum digests on build.
# If not specified or 0, MD5 is used.
@@ -476,6 +482,19 @@ Supplements: (%{name} = %{version}-%{r
@@ -476,6 +479,19 @@ Supplements: (%{name} = %{version}-%{r
#
#%_include_minidebuginfo 1
@ -67,7 +57,7 @@
#
# Include a .gdb_index section in the .debug files.
# Requires _enable_debug_packages and gdb-add-index installed.
@@ -508,39 +527,39 @@ Supplements: (%{name} = %{version}-%{r
@@ -508,39 +524,39 @@ Supplements: (%{name} = %{version}-%{r
# Same as for "separate" but if the __debug_package global is set then
# the -debuginfo package will have a compatibility link for the main
# ELF /usr/lib/debug/.build-id/xx/yyy -> /usr/lib/.build-id/xx/yyy
@ -114,7 +104,7 @@
#
# Use internal dependency generator rather than external helpers?
@@ -559,6 +578,7 @@ Supplements: (%{name} = %{version}-%{r
@@ -559,6 +575,7 @@ Supplements: (%{name} = %{version}-%{r
%__find_requires %{_rpmconfigdir}/find-requires
#%__find_conflicts ???
#%__find_obsoletes ???
@ -122,7 +112,7 @@
#
# Path to file attribute classifications for automatic dependency
@@ -980,7 +1000,7 @@ Supplements: (%{name} = %{version}-%{r
@@ -980,7 +997,7 @@ Supplements: (%{name} = %{version}-%{r
%_build_vendor %{_host_vendor}
%_build_os %{_host_os}
%_host @host@
@ -131,7 +121,7 @@
%_host_cpu @host_cpu@
%_host_vendor @host_vendor@
%_host_os @host_os@
@@ -1105,11 +1125,13 @@ Supplements: (%{name} = %{version}-%{r
@@ -1105,11 +1122,13 @@ Supplements: (%{name} = %{version}-%{r
#------------------------------------------------------------------------------
# arch macro for all supported 32-bit ARM processors

34
mtime_policy_set.diff Normal file
View File

@ -0,0 +1,34 @@
--- build/files.c.orig 2025-02-12 13:27:08.131721537 +0000
+++ build/files.c 2025-02-12 13:32:28.371151422 +0000
@@ -1049,10 +1049,10 @@ static void genCpioListAndHeader(FileLis
}
}
- if (!strcmp(mtime_policy_str, "clamp_to_buildtime")) {
+ if (!strcmp(mtime_policy_str, "clamp_to_buildtime") || !strcmp(mtime_policy_str, "set_to_buildtime")) {
mtime_clamp = spec->buildTime;
- override_date = 1;
- } else if (!strcmp(mtime_policy_str, "clamp_to_source_date_epoch")) {
+ override_date = mtime_policy_str[0] == 's' ? 2 : 1;
+ } else if (!strcmp(mtime_policy_str, "clamp_to_source_date_epoch") || !strcmp(mtime_policy_str, "set_to_source_date_epoch")) {
/* Limit the maximum date to SOURCE_DATE_EPOCH if defined
* similar to the tar --clamp-mtime option
* https://reproducible-builds.org/specs/source-date-epoch/
@@ -1065,7 +1065,7 @@ static void genCpioListAndHeader(FileLis
rpmlog(RPMLOG_ERR, _("unable to parse %s=%s\n"), "SOURCE_DATE_EPOCH", srcdate);
fl->processingFailed = 1;
}
- override_date = 1;
+ override_date = mtime_policy_str[0] == 's' ? 2 : 1;
}
} else if (*mtime_policy_str) {
rpmlog(RPMLOG_WARNING,
@@ -1214,7 +1214,7 @@ static void genCpioListAndHeader(FileLis
}
}
- if (override_date && flp->fl_mtime > mtime_clamp) {
+ if (override_date && (flp->fl_mtime > mtime_clamp || override_date == 2)) {
flp->fl_mtime = mtime_clamp;
}
/*

View File

@ -1,3 +1,20 @@
-------------------------------------------------------------------
Wed Feb 12 13:36:45 CET 2025 - mls@suse.de
- make the rpm package not depend on libarchive
* move the rpmuncompress tool to rpm-build
* rewrite rpm2archive to not use libarchive for cpio/tar writing
* new patch: rpm2archive.diff
- revert buildroot macro setting that did more harm than good
- add set_to_buildtime and set_to_source_date_epoch mtime policy
support
* new patch: mtime_policy_set.diff
- drop unused 0001-Add-option-to-set-mtime-of-files-in-rpms.patch
patch
- do not output debug messages in rpmspec -q if a buildsystem is
used
* new patch: buildsys.diff
-------------------------------------------------------------------
Mon Feb 3 13:13:27 CET 2025 - mls@suse.de

View File

@ -116,7 +116,6 @@ Patch135: selinux_transactional_update.patch
Patch136: rpmsort_reverse.diff
Patch138: canongnu.diff
Patch139: cmake_python_version.diff
Patch140: 0001-Add-option-to-set-mtime-of-files-in-rpms.patch
Patch141: 0002-log-build-time-if-it-is-set-from-SOURCE_DATE_EPOCH.patch
Patch142: 0003-Error-out-on-a-missing-changelog-date.patch
Patch150: unshare.diff
@ -124,6 +123,9 @@ Patch151: buildroot-symlink.diff
Patch152: debugpackage.diff
Patch153: nextfiles.diff
Patch154: undefbuildroot.diff
Patch155: rpm2archive.diff
Patch156: mtime_policy_set.diff
Patch157: buildsys.diff
Patch6464: auto-config-update-aarch64-ppc64le.diff
BuildRoot: %{_tmppath}/%{name}-%{version}-build
#
@ -241,11 +243,8 @@ rm -rf sqlite
%patch -P 122 -P 123
%patch -P 131 -P 133 -P 134 -P 135 -P 136 -P 138
%patch -P 139
%if 0
%patch -P 140
%endif
%patch -P 141 -P 142
%patch -P 150 -P 151 -P 152 -P 153 -P 154
%patch -P 150 -P 151 -P 152 -P 153 -P 154 -P 155 -P 156 -P 157
%ifarch aarch64 ppc64le riscv64 loongarch64
%patch -P 6464
@ -450,7 +449,6 @@ fi
/usr/lib/rpm/rpmpopt-*
/usr/lib/rpm/rpmrc
/usr/lib/rpm/rpmsort
/usr/lib/rpm/rpmuncompress
/usr/lib/rpm/rpmdump
/usr/lib/rpm/suse
/usr/lib/rpm/tgpg
@ -483,6 +481,7 @@ fi
/usr/lib/rpm/rpm_macros_provides.sh
/usr/lib/rpm/elfdeps
/usr/lib/rpm/rpmdeps
/usr/lib/rpm/rpmuncompress
/usr/bin/rpmspec
/usr/lib/rpm/brp-*
/usr/lib/rpm/check-*

612
rpm2archive.diff Normal file
View File

@ -0,0 +1,612 @@
--- tools/CMakeLists.txt.orig 2025-02-13 09:56:00.257085875 +0000
+++ tools/CMakeLists.txt 2025-02-13 09:56:11.433064235 +0000
@@ -40,11 +40,8 @@ if (READLINE_FOUND)
target_link_libraries(rpmlua PRIVATE PkgConfig::READLINE)
endif()
-if (WITH_ARCHIVE)
- add_executable(rpm2archive rpm2archive.c)
- target_link_libraries(rpm2archive PRIVATE PkgConfig::LIBARCHIVE)
- install(TARGETS rpm2archive)
-endif()
+add_executable(rpm2archive rpm2archive.c)
+install(TARGETS rpm2archive)
# Everything links to these
get_property(executables DIRECTORY PROPERTY BUILDSYSTEM_TARGETS)
@@ -60,12 +57,10 @@ foreach(cmd rpmverify rpmquery)
)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/${cmd} TYPE BIN)
endforeach()
-if (WITH_ARCHIVE)
- add_custom_target(rpm2cpio ALL COMMAND
- ${CMAKE_COMMAND} -E create_symlink rpm2archive rpm2cpio
- )
- install(FILES ${CMAKE_CURRENT_BINARY_DIR}/rpm2cpio TYPE BIN)
-endif()
+add_custom_target(rpm2cpio ALL COMMAND
+ ${CMAKE_COMMAND} -E create_symlink rpm2archive rpm2cpio
+ )
+install(FILES ${CMAKE_CURRENT_BINARY_DIR}/rpm2cpio TYPE BIN)
if (WITH_CXX)
set (cxx_sources
--- tools/rpm2archive.c.orig 2024-10-07 09:35:46.000000000 +0000
+++ tools/rpm2archive.c 2025-02-13 11:50:46.533098005 +0000
@@ -2,6 +2,14 @@
#include "system.h"
+#if defined(MAJOR_IN_MKDEV)
+#include <sys/mkdev.h>
+#elif defined(MAJOR_IN_SYSMACROS)
+#include <sys/sysmacros.h>
+#else
+#include <sys/types.h> /* already included from system.h */
+#endif
+
#include <rpm/rpmlib.h> /* rpmReadPackageFile .. */
#include <rpm/rpmfi.h>
#include <rpm/rpmstring.h>
@@ -12,8 +20,11 @@
#include <popt.h>
+#if 0
#include <archive.h>
#include <archive_entry.h>
+#endif
+
#include <unistd.h>
#include <errno.h>
#include <libgen.h>
@@ -36,6 +47,8 @@ static struct poptOption optionsTable[]
POPT_TABLEEND
};
+#if 0
+
static void fill_archive_entry(struct archive_entry * entry, rpmfi fi,
char **hardlink)
{
@@ -282,6 +295,540 @@ static int process_package(rpmts ts, con
return rc;
}
+#else
+
+static int do_fwrite(FD_t fdo, const void *p, size_t l)
+{
+ if (Fwrite(p, l, 1, fdo) != l) {
+ fprintf(stderr, "Error writing archive: %s\n", Fstrerror(fdo));
+ return RPMRC_FAIL;
+ }
+ return RPMRC_OK;
+}
+
+static int do_fwrite_content(FD_t fdo, char * buf, rpmfi fi)
+{
+ rpm_loff_t left = rpmfiFSize(fi);
+ size_t len, read;
+
+ while (left) {
+ len = (left > BUFSIZE ? BUFSIZE : left);
+ read = rpmfiArchiveRead(fi, buf, len);
+ if (read != len) {
+ fprintf(stderr, "Error reading file from rpm payload\n");
+ break;
+ }
+ if (do_fwrite(fdo, buf, len)) {
+ fprintf(stderr, "Error writing archive: %s\n", Fstrerror(fdo));
+ break;
+ }
+ left -= len;
+ }
+ return (left > 0);
+}
+
+/* cpio support */
+
+static inline void write_cpio_entry_num(unsigned char *p, unsigned long val)
+{
+ char space[64];
+ sprintf(space, "%8.8lx", val);
+ memcpy(p, space, 8);
+}
+
+static int write_cpio_entry(FD_t fdo, rpmfi fi, const char *filename, struct stat *st, const char *flink, const char *hlink, char *buf)
+{
+ unsigned char cpioh[110];
+ memcpy(cpioh, "070701", 6);
+ if (!fi) {
+ memset(cpioh + 6, '0', sizeof(cpioh) - 6);
+ write_cpio_entry_num(cpioh + 38, 1);
+ write_cpio_entry_num(cpioh + 94, 11);
+ if (do_fwrite(fdo, cpioh, sizeof(cpioh)))
+ return RPMRC_FAIL;
+ if (do_fwrite(fdo, "TRAILER!!!\0\0\0", 11 + 3))
+ return RPMRC_FAIL;
+ return RPMRC_OK;
+ }
+ if (st->st_size > UINT32_MAX) {
+ fprintf(stderr, "Warning: file too large for format, skipping: %s\n", filename);
+ return RPMRC_OK;
+ }
+ size_t fnl = strlen(filename);
+ write_cpio_entry_num(cpioh + 6, st->st_ino);
+ write_cpio_entry_num(cpioh + 14, st->st_mode);
+ write_cpio_entry_num(cpioh + 22, st->st_uid);
+ write_cpio_entry_num(cpioh + 30, st->st_gid);
+ write_cpio_entry_num(cpioh + 38, st->st_nlink);
+ write_cpio_entry_num(cpioh + 46, st->st_mtime);
+ write_cpio_entry_num(cpioh + 54, st->st_size);
+ write_cpio_entry_num(cpioh + 62, major(st->st_dev));
+ write_cpio_entry_num(cpioh + 70, minor(st->st_dev));
+ write_cpio_entry_num(cpioh + 78, major(st->st_rdev));
+ write_cpio_entry_num(cpioh + 86, minor(st->st_rdev));
+ write_cpio_entry_num(cpioh + 94, fnl + 1);
+ write_cpio_entry_num(cpioh + 102, 0);
+ if (do_fwrite(fdo, cpioh, sizeof(cpioh)))
+ return RPMRC_FAIL;
+ if (do_fwrite(fdo, filename, fnl + 1))
+ return RPMRC_FAIL;
+ fnl = (110 + fnl + 1) & 3;
+ if (fnl && do_fwrite(fdo, "\0\0\0", 4 - fnl))
+ return RPMRC_FAIL;
+ if (S_ISLNK(st->st_mode)) {
+ if (st->st_size != strlen(flink))
+ return RPMRC_FAIL;
+ if (do_fwrite(fdo, flink, st->st_size))
+ return RPMRC_FAIL;
+ } else if (S_ISREG(st->st_mode)) {
+ if (st->st_size && do_fwrite_content(fdo, buf, fi))
+ return RPMRC_FAIL;
+ } else {
+ return RPMRC_OK;
+ }
+ fnl = (st->st_size) & 3;
+ if (fnl && do_fwrite(fdo, "\0\0\0", 4 - fnl))
+ return RPMRC_FAIL;
+ return RPMRC_OK;
+}
+
+/* pax support */
+
+static void add_pax_attrib(char **paxbuf, const char *pax, const char *val)
+{
+ size_t ten, len = 1 + strlen(pax) + 1 + strlen(val) + 1;
+ for (ten = 1; ten <= len; ten *= 10)
+ len++;
+ if (*paxbuf)
+ *paxbuf = realloc(*paxbuf, strlen(*paxbuf) + len + 1);
+ else {
+ *paxbuf = xmalloc(len + 1);
+ **paxbuf = 0;
+ }
+ sprintf(*paxbuf + strlen(*paxbuf), "%llu %s=%s\n", (unsigned long long)len, pax, val);
+}
+
+static void set_pax_entry_num_base256(unsigned char *p, unsigned long long val, int size)
+{
+ /* use base-256 encoding */
+ unsigned char *pe = p + size;
+ for (; pe > p; val >>= 8)
+ *pe-- = (unsigned char)(val & 255);
+ *p |= 0x80;
+}
+
+static inline void set_pax_entry_num(unsigned char *p, unsigned long long val, int size, char *pax, char **paxbuf)
+{
+ char space[64];
+ int sz = size == 12 ? size - 1 : size - 2;
+ if (paxbuf && val >= (unsigned long long)1 << (sz * 3)) {
+ /* add pax header */
+ sprintf(space, "%llu", val);
+ add_pax_attrib(paxbuf, pax, space);
+ }
+ if (val >= (unsigned long long)1 << (size * 3)) {
+ set_pax_entry_num_base256(p, val, size);
+ return;
+ }
+ sprintf(space, "%0*llo ", sz, val);
+ memcpy(p, space, size);
+}
+
+static int pax_is_ascii(const char *val)
+{
+ for (; *val; val++)
+ if (*(const unsigned char *)val >= 0x80)
+ return 0;
+ return 1;
+}
+
+static inline void set_pax_entry_str(unsigned char *p, const char *val, int size, char *pax, char **paxbuf)
+{
+ size_t l = strlen(val);
+ if (paxbuf && (l > size || !pax_is_ascii(val)))
+ add_pax_attrib(paxbuf, pax, val);
+ memcpy(p, val, l < size ? l : size);
+}
+
+static void set_pax_path_mangle(unsigned char *paxh, const char *filename, const char *insert)
+{
+ size_t l = strlen(filename);
+ size_t ilen = insert ? strlen(insert) + 1 : 0;
+ const char *p, *p2, *bn;
+ int isdir = 0;
+ /* strip trailing '/' and '/.' components */
+ while (l && (filename[l - 1] == '/' || (filename[l - 1] == '.' && l > 1 && filename[l - 2] == '/'))) {
+ l--;
+ isdir = 1;
+ }
+ if (ilen) {
+ isdir = 0; /* no trailing slash for a PaxHeader */
+ if (l == 0) {
+ filename = "/rootdir";
+ l = 8;
+ } else if (l == 1 && filename[0] == '.') {
+ filename = "currentdir";
+ l = 10;
+ } else if (l == 2 && filename[0] == '.' && filename[1] == '.') {
+ filename = "parrentdir";
+ l = 10;
+ }
+ }
+ /* find the basename */
+ bn = filename + l;
+ while (bn > filename && bn[-1] != '/')
+ bn--;
+ /* truncate basename (we use 99 like libarchive so we can add a '/' if the prefix is empty) */
+ l -= bn - filename;
+ if (l > 99 - (ilen + isdir))
+ l = 99 - (ilen + isdir);
+ /* calculate prefix */
+ if (bn - filename <= 100 - (l + ilen + isdir)) {
+ p = filename; /* no need for a prefix */
+ } else {
+ p = bn - filename > 155 ? filename + 155 : bn;
+ while (p > filename && *p != '/')
+ p--;
+ /* move as much of the prefix into name as possible */
+ if (p > filename && bn - p < 99 - (l + ilen + isdir)) {
+ p2 = strchr(bn - (99 - (l + ilen + isdir)), '/');
+ if (p2 && p2 < p)
+ p = p2;
+ }
+ }
+ /* copy the prefix */
+ if (p != filename) {
+ memcpy(paxh + 345, filename, p - filename);
+ p++; /* skip the '/' */
+ }
+ /* copy rest of the dir */
+ p2 = p + (99 - (l + ilen + isdir)) > bn ? bn : p + (99 - (l + ilen + isdir));
+ while (p2 > p && *p2 != '/')
+ p2--;
+ if (p2 < bn && *p2 == '/')
+ p2++; /* always fits as we used 99 as size limit above */
+ memcpy(paxh, p, p2 - p);
+ /* copy the insert */
+ if (ilen) {
+ memcpy(paxh + (p2 - p), insert, ilen);
+ paxh[p2 - p + ilen - 1] = '/';
+ }
+ /* copy the basename */
+ memcpy(paxh + (p2 - p) + ilen, bn, l);
+ if (isdir)
+ paxh[p2 - p + ilen + l] = '/';
+}
+
+static int set_pax_path(unsigned char *paxh, const char *filename)
+{
+ size_t l = strlen(filename);
+ if (l <= 100) {
+ memcpy(paxh, filename, l);
+ return 0;
+ }
+ const char *p = strchr(filename + l - 100 - 1, '/');
+ if (p == filename)
+ p = strchr(filename + 1, '/');
+ if (p && p[1] && p - filename <= 155) {
+ memcpy(paxh, p + 1, l - (p + 1 - filename));
+ memcpy(paxh + 345, filename, p - filename);
+ return 0;
+ }
+ set_pax_path_mangle(paxh, filename, NULL);
+ return 1;
+}
+
+static int write_pax_entry_pax(FD_t fdo, rpmfi fi, const char *filename, struct stat *st, char *paxbuf);
+
+static int write_pax_entry(FD_t fdo, rpmfi fi, const char *filename, struct stat *st, const char *flink, const char *hlink, char *buf)
+{
+ unsigned char paxh[512];
+ int tartype = -1;
+ rpm_loff_t size = 0;
+
+ memset(paxh, 0, sizeof(paxh));
+ if (!fi) {
+ if (do_fwrite(fdo, paxh, sizeof(paxh)))
+ return RPMRC_FAIL;
+ if (do_fwrite(fdo, paxh, sizeof(paxh)))
+ return RPMRC_FAIL;
+ return RPMRC_OK;
+ }
+ if (filename == NULL && flink)
+ tartype = 'x';
+ else if (S_ISREG(st->st_mode))
+ tartype = st->st_nlink > 1 && !rpmfiArchiveHasContent(fi) ? '1' : '0';
+ else if (S_ISLNK(st->st_mode))
+ tartype = '2';
+ else if (S_ISCHR(st->st_mode))
+ tartype = '3';
+ else if (S_ISBLK(st->st_mode))
+ tartype = '4';
+ else if (S_ISDIR(st->st_mode))
+ tartype = '5';
+ else if (S_ISFIFO(st->st_mode))
+ tartype = '6';
+ if (tartype == -1) {
+ fprintf(stderr, "Warning: unsupported file type, skipping: %s\n", filename);
+ return RPMRC_OK;
+ }
+ if (tartype == '5') {
+ size_t l = strlen(filename);
+ if (!l || filename[l - 1] != '/') {
+ char *dirfilename = rstrscat(NULL, filename, "/", NULL);
+ int r = write_pax_entry(fdo, fi, dirfilename, st, flink, hlink, buf);
+ _free(dirfilename);
+ return r;
+ }
+ }
+ if (tartype == '0' || tartype == '1')
+ size = rpmfiFSize(fi);
+ else if (tartype == 'x')
+ size = (rpm_loff_t)strlen(buf);
+
+ /* fill entry header */
+ char *paxbuf = NULL;
+ char **paxbufp = tartype == 'x' ? NULL : &paxbuf;
+ if (tartype == 'x') {
+ set_pax_path_mangle(paxh, flink, "PaxHeader");
+ } else {
+ if (set_pax_path(paxh, filename) || !pax_is_ascii(filename))
+ add_pax_attrib(paxbufp, "path", filename);
+ }
+ set_pax_entry_num(paxh + 100, st->st_mode & 07777, 8, NULL, NULL);
+ set_pax_entry_num(paxh + 108, st->st_uid, 8, "uid", paxbufp);
+ set_pax_entry_num(paxh + 116, st->st_gid, 8, "gid", paxbufp);
+ set_pax_entry_num(paxh + 124, size, 12, "size", paxbufp);
+ set_pax_entry_num(paxh + 136, st->st_mtime, 12, "mtime", paxbufp);
+ memset(paxh + 148, ' ', 8);
+ paxh[156] = tartype;
+ if (tartype == '1' || tartype == '2')
+ set_pax_entry_str(paxh + 157, tartype == '1' ? hlink : flink, 100, "linkpath", paxbufp);
+ memcpy(paxh + 257, "ustar\00000", 8);
+ set_pax_entry_str(paxh + 265, rpmfiFUser(fi), 32, "user", paxbufp);
+ set_pax_entry_str(paxh + 297, rpmfiFGroup(fi), 32, "group", paxbufp);
+ set_pax_entry_num(paxh + 329, major(st->st_rdev), 8, "SCHILY.devmajor", paxbufp);
+ set_pax_entry_num(paxh + 337, minor(st->st_rdev), 8, "SCHILY.devminor", paxbufp);
+ int i, checksum = 0;
+ for (i = 0; i < 512; i++)
+ checksum += paxh[i];
+ set_pax_entry_num(paxh + 148, checksum, 8, NULL, NULL);
+ paxh[148 + 6] = 0;
+ paxh[148 + 7] = ' ';
+ /* write pax header if we need it */
+ if (paxbuf) {
+ int r = write_pax_entry_pax(fdo, fi, filename, st, paxbuf);
+ free(paxbuf);
+ if (r)
+ return RPMRC_FAIL;
+ }
+ /* write entry header */
+ if (do_fwrite(fdo, paxh, 512))
+ return RPMRC_FAIL;
+ if (tartype != '0' && tartype != 'x')
+ return RPMRC_OK; /* no content for those types */
+ /* write content */
+ if (tartype == '0' && size && do_fwrite_content(fdo, buf, fi))
+ return RPMRC_FAIL;
+ if (tartype == 'x' && size && do_fwrite(fdo, buf, size))
+ return RPMRC_FAIL;
+ /* write padding */
+ size &= 511;
+ if (size) {
+ memset(paxh, 0, sizeof(paxh));
+ if (do_fwrite(fdo, paxh, 512 - size))
+ return RPMRC_FAIL;
+ }
+ return RPMRC_OK;
+}
+
+static int write_pax_entry_pax(FD_t fdo, rpmfi fi, const char *filename, struct stat *st, char *paxbuf)
+{
+ /* tweak stat data and filename */
+ struct stat paxst = *st;
+ paxst.st_size = strlen(paxbuf);
+ paxst.st_mode = paxst.st_mode & 0777;
+ if (paxst.st_uid >= (1 << 18))
+ paxst.st_uid = (1 << 18) - 1;
+ if (paxst.st_gid >= (1 << 18))
+ paxst.st_gid = (1 << 18) - 1;
+ if (paxst.st_mtime < 0)
+ paxst.st_mtime = 0;
+ if ((unsigned long long)paxst.st_mtime >= 1ULL << 33)
+ paxst.st_mtime = (time_t)((1ULL << 33) - 1);
+ return write_pax_entry(fdo, fi, NULL, &paxst, filename, NULL, paxbuf);
+}
+
+static int process_package(rpmts ts, const char * filename)
+{
+ FD_t fdi;
+ FD_t gzdi;
+ FD_t fdo;
+ Header h;
+ int rc = 0;
+ char * rpmio_flags = NULL;
+ int iscpio = 0;
+
+ if (!strcmp(filename, "-")) {
+ if(isatty(STDIN_FILENO)) {
+ fprintf(stderr, "Error: missing input RPM package\n");
+ exit(EXIT_FAILURE);
+ }
+ fdi = fdDup(STDIN_FILENO);
+ } else {
+ fdi = Fopen(filename, "r.ufdio");
+ }
+
+ if (Ferror(fdi)) {
+ fprintf(stderr, "rpm2archive: %s: %s\n",
+ filename, Fstrerror(fdi));
+ exit(EXIT_FAILURE);
+ }
+
+ rc = rpmReadPackageFile(ts, fdi, "rpm2cpio", &h);
+
+ switch (rc) {
+ case RPMRC_OK:
+ case RPMRC_NOKEY:
+ case RPMRC_NOTTRUSTED:
+ break;
+ case RPMRC_NOTFOUND:
+ fprintf(stderr, _("argument is not an RPM package\n"));
+ exit(EXIT_FAILURE);
+ break;
+ case RPMRC_FAIL:
+ default:
+ fprintf(stderr, _("error reading header from package\n"));
+ exit(EXIT_FAILURE);
+ break;
+ }
+
+
+ /* Retrieve payload size and compression type. */
+ { const char *compr = headerGetString(h, RPMTAG_PAYLOADCOMPRESSOR);
+ rpmio_flags = rstrscat(NULL, "r.", compr ? compr : "gzip", NULL);
+ }
+
+ gzdi = Fdopen(fdi, rpmio_flags); /* XXX gzdi == fdi */
+ free(rpmio_flags);
+
+ if (gzdi == NULL) {
+ fprintf(stderr, _("cannot re-open payload: %s\n"), Fstrerror(gzdi));
+ exit(EXIT_FAILURE);
+ }
+
+ if (rstreq(format, "pax")) {
+ iscpio = 0;
+ } else if (rstreq(format, "cpio")) {
+ iscpio = 1;
+ } else {
+ fprintf(stderr, "Error: Format %s is not supported\n", format);
+ exit(EXIT_FAILURE);
+ }
+
+ if (!isatty(STDOUT_FILENO)) {
+ fdo = fdDup(STDOUT_FILENO);
+ } else {
+ if (!strcmp(filename, "-")) {
+ fprintf(stderr, "Error: refusing to output archive data to a terminal.\n");
+ exit(EXIT_FAILURE);
+ }
+ char * outname;
+ if (urlIsURL(filename)) {
+ const char * fname = strrchr(filename, '/');
+ if (fname != NULL) {
+ fname++;
+ } else {
+ fname = filename;
+ }
+ outname = rstrscat(NULL, fname, NULL);
+ } else {
+ outname = rstrscat(NULL, filename, NULL);
+ }
+ if (compress) {
+ outname = rstrscat(&outname, ".tgz", NULL);
+ } else {
+ outname = rstrscat(&outname, ".tar", NULL);
+ }
+ fdo = Fopen(outname, "w.ufdio");
+ if (!fdo) {
+ fprintf(stderr, "Error: Can't open output file: %s\n", outname);
+ exit(EXIT_FAILURE);
+ }
+ _free(outname);
+ }
+ if (compress && fdo)
+ fdo = Fdopen(fdo, "w.gzdio");
+ if (!fdo) {
+ fprintf(stderr, "Error: Can't setup output file\n");
+ exit(EXIT_FAILURE);
+ }
+
+ char * buf = (char *)xmalloc(BUFSIZE);
+ char * hardlink = NULL;
+
+ rpmfiles files = rpmfilesNew(NULL, h, 0, RPMFI_KEEPHEADER);
+ rpmfi fi = rpmfiNewArchiveReader(gzdi, files, iscpio ? RPMFI_ITER_READ_ARCHIVE : RPMFI_ITER_READ_ARCHIVE_CONTENT_FIRST);
+
+ while ((rc = rpmfiNext(fi)) >= 0) {
+ struct stat st;
+ const char *dn, *flink;
+ char *filename;
+ if (rpmfiStat(fi, 0, &st)) {
+ break;
+ }
+ dn = rpmfiDN(fi);
+ if (!strcmp(dn, "")) dn = "/";
+ filename = rstrscat(NULL, ".", dn, rpmfiBN(fi), NULL);
+ flink = S_ISLNK(st.st_mode) ? rpmfiFLink(fi) : NULL;
+ if (st.st_nlink > 1 && !iscpio) {
+ if (rpmfiArchiveHasContent(fi)) {
+ /* hardlink sizes are special, see rpmfiStat() */
+ _free(hardlink);
+ hardlink = xstrdup(filename);
+ }
+ }
+ if (iscpio)
+ rc = write_cpio_entry(fdo, fi, filename, &st, flink, st.st_nlink > 1 ? hardlink : NULL, buf);
+ else
+ rc = write_pax_entry(fdo, fi, filename, &st, flink, st.st_nlink > 1 ? hardlink : NULL, buf);
+ _free(filename);
+ if (rc == RPMRC_FAIL)
+ break;
+ }
+ /* End of iteration is not an error, everything else is */
+ if (rc == RPMERR_ITER_END) {
+ rc = 0;
+ } else {
+ rc = 1;
+ }
+
+ /* write trailer */
+ if (!rc) {
+ if (iscpio)
+ rc = write_cpio_entry(fdo, NULL, NULL, NULL, NULL, NULL, buf);
+ else
+ rc = write_pax_entry(fdo, NULL, NULL, NULL, NULL, NULL, buf);
+ rc = rc == RPMRC_FAIL ? 1 : 0;
+ }
+
+ if (Fclose(fdo) && !rc) {
+ fprintf(stderr, "Error writing archive\n");
+ rc = 1;
+ }
+
+ _free(hardlink);
+
+ Fclose(gzdi); /* XXX gzdi == fdi */
+ buf = _free(buf);
+ rpmfilesFree(files);
+ rpmfiFree(fi);
+ headerFree(h);
+ return rc;
+}
+#endif
+
+
int main(int argc, char *argv[])
{
int rc = 0;