From 2b0cab0ed63e77582b047f723b69a0e3dd647566 Mon Sep 17 00:00:00 2001
From: Roberto Sassu <roberto.sassu@huawei.com>
Date: Wed, 12 Aug 2020 18:23:42 +0200
Subject: [PATCH] Generate digest lists before calling genCpioListAndHeader()

Signed-off-by: luhuaxin <luhuaxin1@huawei.com>
---
 build/files.c | 182 ++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 147 insertions(+), 35 deletions(-)

diff --git a/build/files.c b/build/files.c
index d1e56c0..5358ead 100644
--- a/build/files.c
+++ b/build/files.c
@@ -1000,20 +1000,149 @@ static int seenHardLink(FileRecords files, FileListRec flp, rpm_ino_t *fileid)
  * @param pkg		(sub) package
  * @param isSrc		pass 1 for source packages 0 otherwise
  */
-static void genCpioListAndHeader(FileList fl, Package pkg, int isSrc)
+static void genDigestListInput(FileList fl, Package pkg, int isSrc)
 {
     FileListRec flp;
     char buf[BUFSIZ];
     char file_info[BUFSIZ];
     char file_digest[128 * 2 + 1];
+    int i;
+    uint32_t defaultalgo = PGPHASHALGO_MD5, digestalgo;
+    Header h = pkg->header; /* just a shortcut */
+
+    /*
+     * See if non-md5 file digest algorithm is requested. If not
+     * specified, quietly assume md5. Otherwise check if supported type.
+     */
+    digestalgo = rpmExpandNumeric(isSrc ? "%{_source_filedigest_algorithm}" :
+					  "%{_binary_filedigest_algorithm}");
+    if (digestalgo == 0) {
+	digestalgo = defaultalgo;
+    }
+
+    if (rpmDigestLength(digestalgo) == 0) {
+	rpmlog(RPMLOG_WARNING,
+	       _("Unknown file digest algorithm %u, falling back to MD5\n"),
+	       digestalgo);
+	digestalgo = defaultalgo;
+    }
+
+    /* Sort the big list */
+    if (fl->files.recs) {
+	qsort(fl->files.recs, fl->files.used,
+	      sizeof(*(fl->files.recs)), compareFileListRecs);
+    }
+
+    /* Generate the header. */
+    for (i = 0, flp = fl->files.recs; i < fl->files.used; i++, flp++) {
+	/* Merge duplicate entries. */
+	while (i < (fl->files.used - 1) &&
+	    rstreq(flp->cpioPath, flp[1].cpioPath)) {
+
+	    /* Two entries for the same file found, merge the entries. */
+	    /* Note that an %exclude is a duplication of a file reference */
+
+	    /* file flags */
+	    flp[1].flags |= flp->flags;
+
+	    if (!(flp[1].flags & RPMFILE_EXCLUDE))
+		rpmlog(RPMLOG_WARNING, _("File listed twice: %s\n"),
+			flp->cpioPath);
+
+	    /* file mode */
+	    if (S_ISDIR(flp->fl_mode)) {
+		if ((flp[1].specdFlags & (SPECD_DIRMODE | SPECD_DEFDIRMODE)) <
+		    (flp->specdFlags & (SPECD_DIRMODE | SPECD_DEFDIRMODE)))
+			flp[1].fl_mode = flp->fl_mode;
+	    } else {
+		if ((flp[1].specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE)) <
+		    (flp->specdFlags & (SPECD_FILEMODE | SPECD_DEFFILEMODE)))
+			flp[1].fl_mode = flp->fl_mode;
+	    }
+
+	    /* uid */
+	    if ((flp[1].specdFlags & (SPECD_UID | SPECD_DEFUID)) <
+		(flp->specdFlags & (SPECD_UID | SPECD_DEFUID)))
+	    {
+		flp[1].fl_uid = flp->fl_uid;
+		flp[1].uname = flp->uname;
+	    }
+
+	    /* gid */
+	    if ((flp[1].specdFlags & (SPECD_GID | SPECD_DEFGID)) <
+		(flp->specdFlags & (SPECD_GID | SPECD_DEFGID)))
+	    {
+		flp[1].fl_gid = flp->fl_gid;
+		flp[1].gname = flp->gname;
+	    }
+
+	    /* verify flags */
+	    if ((flp[1].specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY)) <
+		(flp->specdFlags & (SPECD_VERIFY | SPECD_DEFVERIFY)))
+		    flp[1].verifyFlags = flp->verifyFlags;
+
+	    /* XXX to-do: language */
+
+	    flp++; i++;
+	}
+
+	/* Skip files that were marked with %exclude. */
+	if (flp->flags & RPMFILE_EXCLUDE)
+	{
+	    argvAdd(&pkg->fileExcludeList, flp->cpioPath);
+	    continue;
+	}
+
+	buf[0] = '\0';
+	if (S_ISREG(flp->fl_mode) && !(flp->flags & RPMFILE_GHOST))
+	    (void) rpmDoDigest(digestalgo, flp->diskPath, 1,
+			       (unsigned char *)buf);
+	headerPutString(h, RPMTAG_FILEDIGESTS, buf);
+	snprintf(file_digest, sizeof(file_digest), "%s", buf);
+
+	if (check_fileList_bin_pkg && S_ISREG(flp->fl_mode) &&
+		!(flp->flags & RPMFILE_GHOST)) {
+	    appendStringBuf(check_fileList_bin_pkg, "path=");
+	    appendStringBuf(check_fileList_bin_pkg, flp->diskPath);
+	    snprintf(file_info, sizeof(file_info),
+		"|digestalgopgp=%d|digest=%s|mode=%d"
+		"|uname=%s|gname=%s|caps=%s\n",
+		digestalgo, file_digest, flp->fl_mode,
+		rpmstrPoolStr(fl->pool, flp->uname),
+		rpmstrPoolStr(fl->pool, flp->gname), flp->caps &&
+		strlen(flp->caps) ? flp->caps : "");
+	    appendStringBuf(check_fileList_bin_pkg, file_info);
+	}
+    }
+
+    if (genDigestList(pkg->header, fl, check_fileList_bin_pkg) > 0)
+	fl->processingFailed = 1;
+}
+
+/**
+ * Add file entries to header.
+ * @todo Should directories have %doc/%config attributes? (#14531)
+ * @todo Remove RPMTAG_OLDFILENAMES, add dirname/basename instead.
+ * @param fl		package file tree walk data
+ * @param pkg		(sub) package
+ * @param isSrc		pass 1 for source packages 0 otherwise
+ */
+static void genCpioListAndHeader(FileList fl, Package pkg, int isSrc)
+{
+    FileListRec flp;
+    char buf[BUFSIZ];
     int i, npaths = 0;
     int fail_on_dupes = rpmExpandNumeric("%{?_duplicate_files_terminate_build}") > 0;
     uint32_t defaultalgo = PGPHASHALGO_MD5, digestalgo;
     rpm_loff_t totalFileSize = 0;
     Header h = pkg->header; /* just a shortcut */
-    int processed = 0;
     time_t source_date_epoch = 0;
     char *srcdate = getenv("SOURCE_DATE_EPOCH");
+    struct rpmtd_s oldfiledigests;
+
+    headerGet(h, RPMTAG_FILEDIGESTS, &oldfiledigests, HEADERGET_ALLOC);
+    headerDel(h, RPMTAG_FILEDIGESTS);
+    rpmtdInit(&oldfiledigests);
 
     /* Limit the maximum date to SOURCE_DATE_EPOCH if defined
      * similar to the tar --clamp-mtime option
@@ -1080,9 +1209,8 @@ static void genCpioListAndHeader(FileList fl, Package pkg, int isSrc)
     
     pkg->dpaths = xmalloc((fl->files.used + 1) * sizeof(*pkg->dpaths));
 
-process_files:
     /* Generate the header. */
-    for (i = processed, flp = fl->files.recs + processed; i < fl->files.used; i++, flp++) {
+    for (i = 0, flp = fl->files.recs; i < fl->files.used; i++, flp++) {
 	rpm_ino_t fileid = flp - fl->files.recs;
 
  	/* Merge duplicate entries. */
@@ -1212,13 +1340,17 @@ process_files:
 	if (fl->haveCaps) {
 	    headerPutString(h, RPMTAG_FILECAPS, flp->caps);
 	}
-	
+
 	buf[0] = '\0';
-	if (S_ISREG(flp->fl_mode) && !(flp->flags & RPMFILE_GHOST))
-	    (void) rpmDoDigest(digestalgo, flp->diskPath, 1, 
-			       (unsigned char *)buf);
-	headerPutString(h, RPMTAG_FILEDIGESTS, buf);
-	snprintf(file_digest, sizeof(file_digest), "%s", buf);
+	if (strstr(flp->diskPath, DIGEST_LIST_DIR) || !oldfiledigests.count) {
+	    if (S_ISREG(flp->fl_mode) && !(flp->flags & RPMFILE_GHOST))
+	        (void) rpmDoDigest(digestalgo, flp->diskPath, 1,
+				   (unsigned char *)buf);
+	    headerPutString(h, RPMTAG_FILEDIGESTS, buf);
+	} else {
+	    headerPutString(h, RPMTAG_FILEDIGESTS,
+			    rpmtdNextString(&oldfiledigests));
+	}
 
 	buf[0] = '\0';
 	if (S_ISLNK(flp->fl_mode)) {
@@ -1259,31 +1391,6 @@ process_files:
 	flp->flags &= PARSEATTR_MASK;
 
 	headerPutUint32(h, RPMTAG_FILEFLAGS, &(flp->flags) ,1);
-
-	if (!processed && check_fileList_bin_pkg && S_ISREG(flp->fl_mode) &&
-		!(flp->flags & RPMFILE_GHOST)) {
-	    appendStringBuf(check_fileList_bin_pkg, "path=");
-	    appendStringBuf(check_fileList_bin_pkg, flp->diskPath);
-	    snprintf(file_info, sizeof(file_info),
-		"|digestalgopgp=%d|digest=%s|mode=%d"
-		"|uname=%s|gname=%s|caps=%s\n",
-		digestalgo, file_digest, flp->fl_mode,
-		rpmstrPoolStr(fl->pool, flp->uname),
-		rpmstrPoolStr(fl->pool, flp->gname), flp->caps &&
-		strlen(flp->caps) ? flp->caps : "");
-	    appendStringBuf(check_fileList_bin_pkg, file_info);
-	}
-    }
-
-    if (!processed) {
-	if (genDigestList(pkg->header, fl, check_fileList_bin_pkg) > 0) {
-	    fl->processingFailed = 1;
-	} else if (i < fl->files.used) {
-	    pkg->dpaths = xrealloc(pkg->dpaths,
-				   (fl->files.used + 1) * sizeof(*pkg->dpaths));
-	    processed = i;
-	    goto process_files;
-	}
     }
 
     pkg->dpaths[npaths] = NULL;
@@ -1324,6 +1431,7 @@ process_files:
 	/* Binary packages with dirNames cannot be installed by legacy rpm. */
 	(void) rpmlibNeedsFeature(pkg, "CompressedFileNames", "3.0.4-1");
     }
+    rpmtdFreeData(&oldfiledigests);
 }
 
 static FileRecords FileRecordsFree(FileRecords files)
@@ -2808,6 +2916,10 @@ static rpmRC processPackageFiles(rpmSpec spec, rpmBuildPkgFlags pkgFlags,
     if (checkHardLinks(&fl.files))
 	(void) rpmlibNeedsFeature(pkg, "PartialHardlinkSets", "4.0.4-1");
 
+    genDigestListInput(&fl, pkg, 0);
+    if (fl.processingFailed)
+	goto exit;
+
     genCpioListAndHeader(&fl, pkg, 0);
 
 exit:
-- 
2.33.0

