[packages/rpm] Rel 6; multicolor fixes to addSelfErasures

arekm arekm at pld-linux.org
Mon Apr 13 09:47:21 CEST 2026


commit 839106b1348bef2f3e7f38e52424de080ea652a8
Author: Arkadiusz Miśkiewicz <arekm at maven.pl>
Date:   Mon Apr 13 09:45:54 2026 +0200

    Rel 6; multicolor fixes to addSelfErasures

 multicolor-addSelfErasures.patch | 201 +++++++++++++++++++++++++++++++++++++++
 rpm.spec                         |   5 +-
 2 files changed, 205 insertions(+), 1 deletion(-)
---
diff --git a/rpm.spec b/rpm.spec
index 8ddd418..2d8213b 100644
--- a/rpm.spec
+++ b/rpm.spec
@@ -36,7 +36,7 @@ Summary(ru.UTF-8):	Менеджер пакетов от RPM
 Summary(uk.UTF-8):	Менеджер пакетів від RPM
 Name:		rpm
 Version:	6.0.1
-Release:	5
+Release:	6
 Epoch:		1
 License:	GPL v2 / LGPL v2.1
 Group:		Base
@@ -62,6 +62,7 @@ Source14:	%{name}.noautoreqfiles
 Source16:	libtooldeps.sh
 Source17:	libtool.attr
 Patch0:		%{name}-popt-aliases.patch
+Patch1:		multicolor-addSelfErasures.patch
 Patch3:		%{name}-scripts-closefds.patch
 Patch4:		%{name}-dir-macros-relative.patch
 Patch6:		%{name}-debuginfo.patch
@@ -631,6 +632,8 @@ Dokumentacja API RPM-a oraz przewodniki w formacie HTML generowane ze
 %prep
 %setup -q %{!?with_sequoia:-a100} -n %{name}-%{version}%{?subver}
 %patch -P0 -p1
+%patch -P1 -p1
+
 %patch -P3 -p1
 %patch -P4 -p1
 %patch -P6 -p1
diff --git a/multicolor-addSelfErasures.patch b/multicolor-addSelfErasures.patch
new file mode 100644
index 0000000..1090e94
--- /dev/null
+++ b/multicolor-addSelfErasures.patch
@@ -0,0 +1,201 @@
+    fix: do not erase uncolored packages from different arch families on upgrade
+    
+    addSelfErasures() uses skipColor() to decide whether an installed
+    package should be erased when a same-name package is upgraded.
+    skipColor() only distinguishes packages when both have a non-zero
+    HEADERCOLOR (i.e. contain ELF objects).  Packages that carry no ELF
+    (typical for -devel sub-packages: headers, symlinks, pkg-config and
+    libtool files) all have HEADERCOLOR=0, so skipColor() lets the erasure
+    through regardless of architecture.
+    
+    This is harmless in a two-arch multilib world (i386 + x86_64) where
+    package managers always install both variants in a single transaction,
+    but breaks when a third arch family is present (e.g. x32, archcolor=4).
+    Installing xz-devel.i686 would silently erase the already-installed
+    xz-devel.x32 even though they install to different paths
+    (/usr/lib vs /usr/libx32) and are not interchangeable.
+    
+    When both the new and installed package are uncolored, fall back to
+    comparing the archcolor assigned to each architecture in rpmrc.  Skip
+    the erasure only when both archcolors are positive (non-zero) and
+    differ, so that:
+    
+     - i686 (ac=1) upgrading i586 (ac=1): same archcolor, erased (correct)
+     - x86_64 (ac=2) vs i686 (ac=1): different, kept (correct multilib)
+     - x32 (ac=4) vs i686 (ac=1): different, kept (correct)
+     - noarch (ac=0) -> arch migration: noarch ac is 0, not skipped (correct)
+    
+    Add regression tests for both two-way (i686 + x86_64, _transaction_color
+    3) and three-way (i686 + x86_64 + x32, _transaction_color 7) multilib.
+
+diff --git a/lib/depends.cc b/lib/depends.cc
+index 844a4bcf7..80ba7c0a3 100644
+--- a/lib/depends.cc
++++ b/lib/depends.cc
+@@ -164,11 +164,39 @@ static int addSelfErasures(rpmts ts, rpm_color_t tscolor, int op,
+     rpmdbMatchIterator mi = rpmtsInitIterator(ts, RPMDBI_NAME, rpmteN(p), 0);
+     int rc = 0;
+ 
++    /* Pre-compute the new package's arch and archcolor -- they are
++     * constant across all iterations and rpmGetArchColor() acquires
++     * a mutex internally, so avoid calling it in the loop. */
++    const char *parch = rpmteA(p);
++    int pac = parch ? rpmGetArchColor(parch) : -1;
++
+     while ((oh = rpmdbNextIterator(mi)) != NULL) {
+ 	/* Ignore colored packages not in our rainbow. */
+-	if (skipColor(tscolor, hcolor, headerGetNumber(oh, RPMTAG_HEADERCOLOR)))
++	rpm_color_t ocolor = headerGetNumber(oh, RPMTAG_HEADERCOLOR);
++	if (skipColor(tscolor, hcolor, ocolor))
+ 	    continue;
+ 
++	/* When both the new and installed package are uncolored (no ELF,
++	 * e.g. -devel sub-packages), skipColor cannot tell them apart.
++	 * Compare their architectures' assigned colors instead, so that
++	 * packages from genuinely different arch families (e.g. x32 vs
++	 * i686) are not accidentally erased by each other.  Skip only
++	 * when both archcolors are positive (non-zero) and differ --
++	 * archcolor 0 means noarch which is always replaceable.
++	 *
++	 * Note: checkAdded() and rpmtsTeIterator() use strict arch string
++	 * comparison, but that would be too strict here -- it would
++	 * prevent valid same-family upgrades (e.g. i586 -> i686) where
++	 * the arch strings differ but the archcolor is the same. */
++	if (tscolor && hcolor == 0 && ocolor == 0) {
++	    const char *oarch = headerGetString(oh, RPMTAG_ARCH);
++	    if (oarch) {
++		int oac = rpmGetArchColor(oarch);
++		if (pac > 0 && oac > 0 && pac != oac)
++		    continue;
++	    }
++	}
++
+ 	/* On reinstall, skip packages with differing NEVRA. */
+ 	if (op != RPMTE_UPGRADE) {
+ 	    char * ohNEVRA = headerGetAsString(oh, RPMTAG_NEVRA);
+diff --git a/tests/data/SPECS/colortest.spec b/tests/data/SPECS/colortest.spec
+new file mode 100644
+index 000000000..645bf05d0
+--- /dev/null
++++ b/tests/data/SPECS/colortest.spec
+@@ -0,0 +1,20 @@
++%{?!ver:%define ver 1.0}
++
++Name:		colortest
++Version:	%{ver}
++Release:	1
++Summary:	Testing uncolored multilib upgrade behavior
++
++Group:		Testing
++License:	GPL
++
++%description
++%{summary}
++
++%install
++mkdir -p %{buildroot}/opt/colortest/
++echo "%{_arch}" > %{buildroot}/opt/colortest/%{_arch}
++
++%files
++%defattr(-,root,root,-)
++/opt/colortest/
+diff --git a/tests/rpmi.at b/tests/rpmi.at
+index 691fc6393..899c5c3f4 100644
+--- a/tests/rpmi.at
++++ b/tests/rpmi.at
+@@ -2083,3 +2083,95 @@ runroot rpm -U --nodb --test --ignorearch --ignoreos --nodeps --nosignature \
+ [	installing package hello-2.0-1.x86_64 needs 36KB more space on the / filesystem
+ ])
+ RPMTEST_CLEANUP
++
++# ------------------------------
++# Uncolored (color=0) packages from different arch families must not
++# erase each other on upgrade.  This is the typical case for -devel
++# sub-packages that carry no ELF and thus have HEADERCOLOR=0.
++# Regression test for the addSelfErasures() archcolor check.
++#
++# _transaction_color is a bitmask of archcolors the system supports:
++#   1 = i386 family (32-bit ELF), 2 = x86_64 (64-bit ELF).
++#   3 (1|2) = standard x86_64 multilib as used by Fedora/RHEL.
++RPMTEST_SETUP_RW([rpm -U uncolored multilib upgrade])
++AT_KEYWORDS([install upgrade multilib color])
++
++runroot rpmbuild --quiet -bb --define "ver 1.0" --target i686 \
++	/data/SPECS/colortest.spec
++runroot rpmbuild --quiet -bb --define "ver 1.0" --target x86_64 \
++	/data/SPECS/colortest.spec
++
++# Install i686 variant first
++RPMTEST_CHECK([
++RPMDB_RESET
++runroot rpm -U --ignoreos --ignorearch --nodeps \
++	--define "_transaction_color 3" \
++	/build/RPMS/i686/colortest-1.0-1.i686.rpm
++runroot rpm -q colortest
++],
++[0],
++[colortest-1.0-1.i686
++],
++[])
++
++# Now upgrade with x86_64 variant -- both should coexist
++RPMTEST_CHECK([
++runroot rpm -U --ignoreos --ignorearch --nodeps \
++	--define "_transaction_color 3" \
++	/build/RPMS/x86_64/colortest-1.0-1.x86_64.rpm
++runroot rpm -q colortest | sort
++],
++[0],
++[colortest-1.0-1.i686
++colortest-1.0-1.x86_64
++],
++[])
++RPMTEST_CLEANUP
++
++# ------------------------------
++# Three-way multilib: systems that carry a third arch family (e.g. x32,
++# archcolor=4) use _transaction_color 7 (1|2|4).  Verify that all three
++# uncolored variants survive sequential upgrades.
++#
++# x32 is not in upstream rpmrc, so inject the necessary arch definitions
++# into the test environment to keep the test self-contained.
++RPMTEST_SETUP_RW([rpm -U uncolored three-way multilib upgrade])
++AT_KEYWORDS([install upgrade multilib color])
++
++cat << EOF > ${RPMTEST}/root/.config/rpm/rpmrc
++archcolor: x32 4
++arch_compat: x32: x32 noarch
++arch_canon: x32: x32 1
++buildarchtranslate: x32: x32
++buildarch_compat: x32: noarch
++EOF
++
++runroot rpmbuild --quiet -bb --define "ver 1.0" --target i686 \
++	/data/SPECS/colortest.spec
++runroot rpmbuild --quiet -bb --define "ver 1.0" --target x86_64 \
++	/data/SPECS/colortest.spec
++runroot rpmbuild --quiet -bb --define "ver 1.0" --target x32 \
++	/data/SPECS/colortest.spec
++
++RPMTEST_CHECK([
++RPMDB_RESET
++
++# Install all three sequentially -- none should erase the others
++runroot rpm -U --ignoreos --ignorearch --nodeps \
++	--define "_transaction_color 7" \
++	/build/RPMS/i686/colortest-1.0-1.i686.rpm
++runroot rpm -U --ignoreos --ignorearch --nodeps \
++	--define "_transaction_color 7" \
++	/build/RPMS/x86_64/colortest-1.0-1.x86_64.rpm
++runroot rpm -U --ignoreos --ignorearch --nodeps \
++	--define "_transaction_color 7" \
++	/build/RPMS/x32/colortest-1.0-1.x32.rpm
++runroot rpm -q colortest | sort
++],
++[0],
++[colortest-1.0-1.i686
++colortest-1.0-1.x32
++colortest-1.0-1.x86_64
++],
++[])
++RPMTEST_CLEANUP
================================================================

---- gitweb:

http://git.pld-linux.org/gitweb.cgi/packages/rpm.git/commitdiff/839106b1348bef2f3e7f38e52424de080ea652a8



More information about the pld-cvs-commit mailing list