Blob Blame History Raw
From bce17e42f2301a88574d757740627480a38d86aa Mon Sep 17 00:00:00 2001
From: Michal Domonkos <mdomonko@redhat.com>
Date: Fri, 26 Jul 2024 10:44:04 +0200
Subject: [PATCH] Fix root relocation regression

When relocating the root directory, make sure we insert the new path's
dirname to dirNames[] even if the root itself is owned by the package.

This appears to have been the intention from the first version (largely
untouched since) of this code as we allow the root to pass through the
first checks (by setting len to 0 in that case) as well as the second
for loop where we do the relocations.

This allows fsm to properly create and remove the relocated directory
since we're now using fd-based calls (#1919) and the parent directory
needs to be opened first.

No need to do string comparison here, the empty basename signals that
we're processing the root directory, so just use that.

Building a relocatable package that owns the root directory seems to be
a handy way to create user-installable packages (see RHEL-28967) and it
happened to work before with the path-based calls so this technically
was a regression.  Add a test that emulates this use case.

Backported from commits:
31c14ba6610568c2d634647fed1fb57221178da9
308ac60677732e9979b9ce11e5a3085906da1901

Fixes: RHEL-28967
---
 lib/relocation.c | 14 ++++++++------
 1 file changed, 8 insertions(+), 6 deletions(-)

diff --git a/lib/relocation.c b/lib/relocation.c
index 3ba4cfeab..8c35bc1a7 100644
--- a/lib/relocation.c
+++ b/lib/relocation.c
@@ -123,7 +123,7 @@ void rpmRelocateFileList(rpmRelocation *relocations, int numRelocations,
     char ** baseNames;
     char ** dirNames;
     uint32_t * dirIndexes;
-    rpm_count_t fileCount, dirCount;
+    rpm_count_t fileCount, dirCount, dirCountOrig;
     int nrelocated = 0;
     int fileAlloced = 0;
     char * fn = NULL;
@@ -162,7 +162,7 @@ void rpmRelocateFileList(rpmRelocation *relocations, int numRelocations,
     baseNames = bnames.data;
     dirIndexes = dindexes.data;
     fileCount = rpmtdCount(&bnames);
-    dirCount = rpmtdCount(&dnames);
+    dirCount = dirCountOrig = rpmtdCount(&dnames);
     /* XXX TODO: use rpmtdDup() instead */
     dirNames = dnames.data = duparray(dnames.data, dirCount);
     dnames.flags |= RPMTD_PTR_ALLOCED;
@@ -179,8 +179,9 @@ void rpmRelocateFileList(rpmRelocation *relocations, int numRelocations,
 	rpmFileTypes ft;
 	int fnlen;
 
+	size_t baselen = strlen(baseNames[i]);
 	size_t len = maxlen +
-		strlen(dirNames[dirIndexes[i]]) + strlen(baseNames[i]) + 1;
+		strlen(dirNames[dirIndexes[i]]) + baselen + 1;
 	if (len >= fileAlloced) {
 	    fileAlloced = len * 2;
 	    fn = xrealloc(fn, fileAlloced);
@@ -242,8 +243,9 @@ assert(fn != NULL);		/* XXX can't happen */
 	    continue;
 	}
 
-	/* Relocation on full paths only, please. */
-	if (fnlen != len) continue;
+	/* Relocation on '/' and full paths only, please. */
+	if (baselen && fnlen != len)
+	    continue;
 
 	rpmlog(RPMLOG_DEBUG, "relocating %s to %s\n",
 	       fn, relocations[j].newPath);
@@ -294,7 +296,7 @@ assert(fn != NULL);		/* XXX can't happen */
     }
 
     /* Finish off by relocating directories. */
-    for (i = dirCount - 1; i >= 0; i--) {
+    for (i = dirCountOrig - 1; i >= 0; i--) {
 	for (j = numRelocations - 1; j >= 0; j--) {
 
 	    if (relocations[j].oldPath == NULL) /* XXX can't happen */
-- 
2.45.2