From 9ceacf27cb0c51f83f96a18e35ccc6fef71fc03d Mon Sep 17 00:00:00 2001 From: chantra Date: Feb 01 2022 23:29:24 +0000 Subject: Apply GH1534 and disable reflink until rebased --- diff --git a/SOURCES/1534.patch b/SOURCES/1534.patch new file mode 100644 index 0000000..d5acd42 --- /dev/null +++ b/SOURCES/1534.patch @@ -0,0 +1,1359 @@ +From 3a5d2ee0f56d049927c4dcdd03e8e275ac03e20d Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Mon, 8 Feb 2021 10:45:59 +0200 +Subject: [PATCH 01/10] Clean up file unpack iteration logic a bit + +Handle rpmfiNext() in the while-condition directly to make it more like +similar other constructs elsewhere, adjust for the end of iteration +code after the loop. Also take the file index from rpmfiNext() so +we don't need multiple calls to rpmfiFX() later. +--- + lib/fsm.c | 19 +++++++------------ + 1 file changed, 7 insertions(+), 12 deletions(-) + +diff --git a/lib/fsm.c b/lib/fsm.c +index 432bcbd90..f71b9bc1e 100644 +--- a/lib/fsm.c ++++ b/lib/fsm.c +@@ -865,6 +865,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + struct stat sb; + int saveerrno = errno; + int rc = 0; ++ int fx = -1; + int nodigest = (rpmtsFlags(ts) & RPMTRANS_FLAG_NOFILEDIGEST) ? 1 : 0; + int nofcaps = (rpmtsFlags(ts) & RPMTRANS_FLAG_NOCAPS) ? 1 : 0; + int firsthardlink = -1; +@@ -886,17 +887,8 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + /* Detect and create directories not explicitly in package. */ + rc = fsmMkdirs(files, fs, plugins); + +- while (!rc) { +- /* Read next payload header. */ +- rc = rpmfiNext(fi); +- +- if (rc < 0) { +- if (rc == RPMERR_ITER_END) +- rc = 0; +- break; +- } +- +- action = rpmfsGetAction(fs, rpmfiFX(fi)); ++ while (!rc && (fx = rpmfiNext(fi)) >= 0) { ++ action = rpmfsGetAction(fs, fx); + skip = XFA_SKIPPING(action); + if (action != FA_TOUCH) { + suffix = S_ISDIR(rpmfiFMode(fi)) ? NULL : tid; +@@ -920,7 +912,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + if (rc) { + skip = 1; + } else { +- setFileState(fs, rpmfiFX(fi)); ++ setFileState(fs, fx); + } + + if (!skip) { +@@ -1022,6 +1014,9 @@ touch: + fpath = _free(fpath); + } + ++ if (!rc && fx != RPMERR_ITER_END) ++ rc = fx; ++ + rpmswAdd(rpmtsOp(ts, RPMTS_OP_UNCOMPRESS), fdOp(payload, FDSTAT_READ)); + rpmswAdd(rpmtsOp(ts, RPMTS_OP_DIGEST), fdOp(payload, FDSTAT_DIGEST)); + +-- +2.34.1 + + +From 3096acbddc57eb2b65c96aad1ed3524ea9d0cead Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Wed, 10 Feb 2021 08:25:28 +0200 +Subject: [PATCH 02/10] Drop unused filename variable + +--- + lib/fsm.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/lib/fsm.c b/lib/fsm.c +index f71b9bc1e..8e6a6c08b 100644 +--- a/lib/fsm.c ++++ b/lib/fsm.c +@@ -976,11 +976,9 @@ touch: + /* On FA_TOUCH no hardlinks are created thus this is skipped. */ + /* we skip the hard linked file containing the content */ + /* write the content to the first used instead */ +- char *fn = rpmfilesFN(files, firsthardlink); + rc = rpmfiArchiveReadToFilePsm(fi, firstlinkfile, nodigest, psm); + wfd_close(&firstlinkfile); + firsthardlink = -1; +- free(fn); + } + + if (rc) { +-- +2.34.1 + + +From 9561315538f1fc4a5b207a43ac39805134e7153f Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Wed, 10 Feb 2021 09:57:17 +0200 +Subject: [PATCH 03/10] Don't update path info if rename failed on file commit + +--- + lib/fsm.c | 16 +++++++++------- + 1 file changed, 9 insertions(+), 7 deletions(-) + +diff --git a/lib/fsm.c b/lib/fsm.c +index 8e6a6c08b..138b55297 100644 +--- a/lib/fsm.c ++++ b/lib/fsm.c +@@ -797,14 +797,16 @@ static int fsmCommit(char **path, rpmfi fi, rpmFileAction action, const char *su + /* Rename temporary to final file name if needed. */ + if (dest != *path) { + rc = fsmRename(*path, dest); +- if (!rc && nsuffix) { +- char * opath = fsmFsPath(fi, NULL); +- rpmlog(RPMLOG_WARNING, _("%s created as %s\n"), +- opath, dest); +- free(opath); ++ if (!rc) { ++ if (nsuffix) { ++ char * opath = fsmFsPath(fi, NULL); ++ rpmlog(RPMLOG_WARNING, _("%s created as %s\n"), ++ opath, dest); ++ free(opath); ++ } ++ free(*path); ++ *path = dest; + } +- free(*path); +- *path = dest; + } + } + +-- +2.34.1 + + +From 73d090166a60c57484e4be40e46fcb07f026cbf8 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Thu, 27 Aug 2020 10:31:07 +0300 +Subject: [PATCH 04/10] Upgrade FA_TOUCH to FA_CREATE if the file went away + (RhBug:1872141) + +When %_minimize_writes is enabled, we determine unchanged files during +fingerprinting and only update their metadata (FA_TOUCH) instead of +always recreating from scratch (FA_CREATE) during install. However +package scriptlets (and administrators) can and will do arbitrary stuff +in the meanwhile, such as rm -f their own files in %pre, hoping to +get a fresh copy of contents no matter what. Or something. +Now, if the file was determined to not need changing by rpm, this will +just fail with chown & friends trying to touch non-existent file. +One can consider this a case of package shooting itself in the foot, but +when a package update fails or succeeds depending on %_minimize_writes this +to me suggests the feature is at fault as much as the package. + +Do fsmVerify() on all files to be FA_TOUCH'ed to detect files whose +type changed or were removed since fingerprinting. This still doesn't +ensure correctness if something tampers with the contents in the meanwhile, +(for that we'd need to run the file through the whole machinery again, +checksumming and all) but covers the most glaring cases. +--- + lib/fsm.c | 15 ++++++--- + tests/Makefile.am | 1 + + tests/data/SPECS/suicidal.spec | 19 ++++++++++++ + tests/rpmi.at | 56 ++++++++++++++++++++++++++++++++++ + 4 files changed, 87 insertions(+), 4 deletions(-) + create mode 100644 tests/data/SPECS/suicidal.spec + +diff --git a/lib/fsm.c b/lib/fsm.c +index 138b55297..8a4ecaf05 100644 +--- a/lib/fsm.c ++++ b/lib/fsm.c +@@ -920,10 +920,6 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + if (!skip) { + int setmeta = 1; + +- /* When touching we don't need any of this... */ +- if (action == FA_TOUCH) +- goto touch; +- + /* Directories replacing something need early backup */ + if (!suffix) { + rc = fsmBackup(fi, action); +@@ -935,6 +931,17 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + rc = RPMERR_ENOENT; + } + ++ /* See if the file was removed while our attention was elsewhere */ ++ if (rc == RPMERR_ENOENT && action == FA_TOUCH) { ++ rpmlog(RPMLOG_DEBUG, "file %s vanished unexpectedly\n", fpath); ++ action = FA_CREATE; ++ fsmDebug(fpath, action, &sb); ++ } ++ ++ /* When touching we don't need any of this... */ ++ if (action == FA_TOUCH) ++ goto touch; ++ + if (S_ISREG(sb.st_mode)) { + if (rc == RPMERR_ENOENT) { + rc = fsmMkfile(fi, fpath, files, psm, nodigest, +diff --git a/tests/Makefile.am b/tests/Makefile.am +index 7a6641d4f..d540e2a7b 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -59,6 +59,7 @@ EXTRA_DIST += data/SPECS/verifyscript.spec + EXTRA_DIST += data/SPECS/fakeshell.spec + EXTRA_DIST += data/SPECS/scripts.spec + EXTRA_DIST += data/SPECS/selfconflict.spec ++EXTRA_DIST += data/SPECS/suicidal.spec + EXTRA_DIST += data/SPECS/replacetest.spec + EXTRA_DIST += data/SPECS/triggers.spec + EXTRA_DIST += data/SPECS/filetriggers.spec +diff --git a/tests/data/SPECS/suicidal.spec b/tests/data/SPECS/suicidal.spec +new file mode 100644 +index 000000000..77d17d8c9 +--- /dev/null ++++ b/tests/data/SPECS/suicidal.spec +@@ -0,0 +1,19 @@ ++Name: suicidal ++Version: 1 ++Release: %{rel} ++License: GPL ++Group: Testing ++Summary: Testing suicidal package behavior ++BuildArch: noarch ++ ++%description ++ ++%build ++mkdir -p %{buildroot}/opt ++echo shoot > %{buildroot}/opt/foot ++ ++%pre -p ++os.remove('/opt/foot') ++ ++%files ++/opt/foot +diff --git a/tests/rpmi.at b/tests/rpmi.at +index e8d6e9b7a..71e17821b 100644 +--- a/tests/rpmi.at ++++ b/tests/rpmi.at +@@ -711,3 +711,59 @@ runroot rpm -e testdoc + []) + AT_CLEANUP + ++AT_SETUP([rpm -i --excludeartifacts]) ++AT_KEYWORDS([install]) ++RPMDB_INIT ++runroot rpmbuild --quiet -bb /data/SPECS/vattrtest.spec ++ ++AT_CHECK([ ++RPMDB_INIT ++runroot rpm -i --excludeartifacts /build/RPMS/noarch/vattrtest-1.0-1.noarch.rpm ++test -e ${RPMTEST}/opt/vattrtest/a && exit 1 ++runroot rpm -e vattrtest ++runroot rpm -i /build/RPMS/noarch/vattrtest-1.0-1.noarch.rpm ++test -e ${RPMTEST}/opt/vattrtest/a || exit 1 ++], ++[0], ++[], ++[]) ++AT_CLEANUP ++ ++AT_SETUP([rpm -U ]) ++AT_KEYWORDS([install]) ++RPMDB_INIT ++ ++for r in 1 2; do ++ runroot rpmbuild -bb --quiet \ ++ --define "rel ${r}" \ ++ /data/SPECS/suicidal.spec ++done ++ ++AT_CHECK([ ++RPMDB_INIT ++ ++for r in 1 2; do ++ runroot rpm -U \ ++ --define "_minimize_writes 0" \ ++ /build/RPMS/noarch/suicidal-1-${r}.noarch.rpm ++done ++runroot rpm -V --nouser --nogroup suicidal ++], ++[0], ++[], ++[]) ++ ++AT_CHECK([ ++RPMDB_INIT ++ ++for r in 1 2; do ++ runroot rpm -U \ ++ --define "_minimize_writes 1" \ ++ /build/RPMS/noarch/suicidal-1-${r}.noarch.rpm ++done ++runroot rpm -V --nouser --nogroup suicidal ++], ++[0], ++[], ++[]) ++AT_CLEANUP +-- +2.34.1 + + +From e78ea489eeed84cdee9f5cedcbbf35cfcf1a70b6 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Wed, 10 Feb 2021 09:47:19 +0200 +Subject: [PATCH 05/10] Refactor file install and remove around a common struct + +Collect the common state info into a struct shared by both file install +and remove, update code accordingly. The change looks much more drastic +than it is - it's just adding fp-> prefix to a lot of places. +While we're at it, remember the state data throughout the operation. + +No functional changes here, just paving way for the next steps which +will look clearer with these pre-requisites in place. +--- + lib/fsm.c | 158 +++++++++++++++++++++++++++++------------------------- + 1 file changed, 85 insertions(+), 73 deletions(-) + +diff --git a/lib/fsm.c b/lib/fsm.c +index 8a4ecaf05..ab15c2bf3 100644 +--- a/lib/fsm.c ++++ b/lib/fsm.c +@@ -38,6 +38,14 @@ static int strict_erasures = 0; + #define _dirPerms 0755 + #define _filePerms 0644 + ++struct filedata_s { ++ int skip; ++ rpmFileAction action; ++ const char *suffix; ++ char *fpath; ++ struct stat sb; ++}; ++ + /* + * XXX Forward declarations for previously exported functions to avoid moving + * things around needlessly +@@ -864,19 +872,16 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + rpmfi fi = rpmfiNewArchiveReader(payload, files, RPMFI_ITER_READ_ARCHIVE); + rpmfs fs = rpmteGetFileStates(te); + rpmPlugins plugins = rpmtsPlugins(ts); +- struct stat sb; + int saveerrno = errno; + int rc = 0; + int fx = -1; ++ int fc = rpmfilesFC(files); + int nodigest = (rpmtsFlags(ts) & RPMTRANS_FLAG_NOFILEDIGEST) ? 1 : 0; + int nofcaps = (rpmtsFlags(ts) & RPMTRANS_FLAG_NOCAPS) ? 1 : 0; + int firsthardlink = -1; + FD_t firstlinkfile = NULL; +- int skip; +- rpmFileAction action; + char *tid = NULL; +- const char *suffix; +- char *fpath = NULL; ++ struct filedata_s *fdata = xcalloc(fc, sizeof(*fdata)); + + if (fi == NULL) { + rc = RPMERR_BAD_MAGIC; +@@ -890,96 +895,99 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + rc = fsmMkdirs(files, fs, plugins); + + while (!rc && (fx = rpmfiNext(fi)) >= 0) { +- action = rpmfsGetAction(fs, fx); +- skip = XFA_SKIPPING(action); +- if (action != FA_TOUCH) { +- suffix = S_ISDIR(rpmfiFMode(fi)) ? NULL : tid; ++ struct filedata_s *fp = &fdata[fx]; ++ fp->action = rpmfsGetAction(fs, fx); ++ fp->skip = XFA_SKIPPING(fp->action); ++ if (fp->action != FA_TOUCH) { ++ fp->suffix = S_ISDIR(rpmfiFMode(fi)) ? NULL : tid; + } else { +- suffix = NULL; ++ fp->suffix = NULL; + } +- fpath = fsmFsPath(fi, suffix); ++ fp->fpath = fsmFsPath(fi, fp->suffix); + + /* Remap file perms, owner, and group. */ +- rc = rpmfiStat(fi, 1, &sb); ++ rc = rpmfiStat(fi, 1, &fp->sb); + +- fsmDebug(fpath, action, &sb); ++ fsmDebug(fp->fpath, fp->action, &fp->sb); + + /* Exit on error. */ + if (rc) + break; + + /* Run fsm file pre hook for all plugins */ +- rc = rpmpluginsCallFsmFilePre(plugins, fi, fpath, +- sb.st_mode, action); ++ rc = rpmpluginsCallFsmFilePre(plugins, fi, fp->fpath, ++ fp->sb.st_mode, fp->action); + if (rc) { +- skip = 1; ++ fp->skip = 1; + } else { + setFileState(fs, fx); + } + +- if (!skip) { ++ if (!fp->skip) { + int setmeta = 1; + + /* Directories replacing something need early backup */ +- if (!suffix) { +- rc = fsmBackup(fi, action); ++ if (!fp->suffix) { ++ rc = fsmBackup(fi, fp->action); + } + /* Assume file does't exist when tmp suffix is in use */ +- if (!suffix) { +- rc = fsmVerify(fpath, fi); ++ if (!fp->suffix) { ++ rc = fsmVerify(fp->fpath, fi); + } else { + rc = RPMERR_ENOENT; + } + + /* See if the file was removed while our attention was elsewhere */ +- if (rc == RPMERR_ENOENT && action == FA_TOUCH) { +- rpmlog(RPMLOG_DEBUG, "file %s vanished unexpectedly\n", fpath); +- action = FA_CREATE; +- fsmDebug(fpath, action, &sb); ++ if (rc == RPMERR_ENOENT && fp->action == FA_TOUCH) { ++ rpmlog(RPMLOG_DEBUG, "file %s vanished unexpectedly\n", ++ fp->fpath); ++ fp->action = FA_CREATE; ++ fsmDebug(fp->fpath, fp->action, &fp->sb); + } + + /* When touching we don't need any of this... */ +- if (action == FA_TOUCH) ++ if (fp->action == FA_TOUCH) + goto touch; + +- if (S_ISREG(sb.st_mode)) { ++ if (S_ISREG(fp->sb.st_mode)) { + if (rc == RPMERR_ENOENT) { +- rc = fsmMkfile(fi, fpath, files, psm, nodigest, ++ rc = fsmMkfile(fi, fp->fpath, files, psm, nodigest, + &setmeta, &firsthardlink, &firstlinkfile); + } +- } else if (S_ISDIR(sb.st_mode)) { ++ } else if (S_ISDIR(fp->sb.st_mode)) { + if (rc == RPMERR_ENOENT) { +- mode_t mode = sb.st_mode; ++ mode_t mode = fp->sb.st_mode; + mode &= ~07777; + mode |= 00700; +- rc = fsmMkdir(fpath, mode); ++ rc = fsmMkdir(fp->fpath, mode); + } +- } else if (S_ISLNK(sb.st_mode)) { ++ } else if (S_ISLNK(fp->sb.st_mode)) { + if (rc == RPMERR_ENOENT) { +- rc = fsmSymlink(rpmfiFLink(fi), fpath); ++ rc = fsmSymlink(rpmfiFLink(fi), fp->fpath); + } +- } else if (S_ISFIFO(sb.st_mode)) { ++ } else if (S_ISFIFO(fp->sb.st_mode)) { + /* This mimics cpio S_ISSOCK() behavior but probably isn't right */ + if (rc == RPMERR_ENOENT) { +- rc = fsmMkfifo(fpath, 0000); ++ rc = fsmMkfifo(fp->fpath, 0000); + } +- } else if (S_ISCHR(sb.st_mode) || +- S_ISBLK(sb.st_mode) || +- S_ISSOCK(sb.st_mode)) ++ } else if (S_ISCHR(fp->sb.st_mode) || ++ S_ISBLK(fp->sb.st_mode) || ++ S_ISSOCK(fp->sb.st_mode)) + { + if (rc == RPMERR_ENOENT) { +- rc = fsmMknod(fpath, sb.st_mode, sb.st_rdev); ++ rc = fsmMknod(fp->fpath, fp->sb.st_mode, fp->sb.st_rdev); + } + } else { + /* XXX Special case /dev/log, which shouldn't be packaged anyways */ +- if (!IS_DEV_LOG(fpath)) ++ if (!IS_DEV_LOG(fp->fpath)) + rc = RPMERR_UNKNOWN_FILETYPE; + } + + touch: + /* Set permissions, timestamps etc for non-hardlink entries */ + if (!rc && setmeta) { +- rc = fsmSetmeta(fpath, fi, plugins, action, &sb, nofcaps); ++ rc = fsmSetmeta(fp->fpath, fi, plugins, fp->action, ++ &fp->sb, nofcaps); + } + } else if (firsthardlink >= 0 && rpmfiArchiveHasContent(fi)) { + /* On FA_TOUCH no hardlinks are created thus this is skipped. */ +@@ -991,10 +999,10 @@ touch: + } + + if (rc) { +- if (!skip) { ++ if (!fp->skip) { + /* XXX only erase if temp fn w suffix is in use */ +- if (suffix) { +- (void) fsmRemove(fpath, sb.st_mode); ++ if (fp->suffix) { ++ (void) fsmRemove(fp->fpath, fp->sb.st_mode); + } + errno = saveerrno; + } +@@ -1002,23 +1010,22 @@ touch: + /* Notify on success. */ + rpmpsmNotify(psm, RPMCALLBACK_INST_PROGRESS, rpmfiArchiveTell(fi)); + +- if (!skip) { ++ if (!fp->skip) { + /* Backup file if needed. Directories are handled earlier */ +- if (suffix) +- rc = fsmBackup(fi, action); ++ if (fp->suffix) ++ rc = fsmBackup(fi, fp->action); + + if (!rc) +- rc = fsmCommit(&fpath, fi, action, suffix); ++ rc = fsmCommit(&fp->fpath, fi, fp->action, fp->suffix); + } + } + + if (rc) +- *failedFile = xstrdup(fpath); ++ *failedFile = xstrdup(fp->fpath); + + /* Run fsm file post hook for all plugins */ +- rpmpluginsCallFsmFilePost(plugins, fi, fpath, +- sb.st_mode, action, rc); +- fpath = _free(fpath); ++ rpmpluginsCallFsmFilePost(plugins, fi, fp->fpath, ++ fp->sb.st_mode, fp->action, rc); + } + + if (!rc && fx != RPMERR_ITER_END) +@@ -1034,7 +1041,9 @@ exit: + rpmfiFree(fi); + Fclose(payload); + free(tid); +- free(fpath); ++ for (int i = 0; i < fc; i++) ++ free(fdata[i].fpath); ++ free(fdata); + + return rc; + } +@@ -1046,29 +1055,31 @@ int rpmPackageFilesRemove(rpmts ts, rpmte te, rpmfiles files, + rpmfi fi = rpmfilesIter(files, RPMFI_ITER_BACK); + rpmfs fs = rpmteGetFileStates(te); + rpmPlugins plugins = rpmtsPlugins(ts); +- struct stat sb; ++ int fc = rpmfilesFC(files); ++ int fx = -1; ++ struct filedata_s *fdata = xcalloc(fc, sizeof(*fdata)); + int rc = 0; +- char *fpath = NULL; + +- while (!rc && rpmfiNext(fi) >= 0) { +- rpmFileAction action = rpmfsGetAction(fs, rpmfiFX(fi)); +- fpath = fsmFsPath(fi, NULL); +- rc = fsmStat(fpath, 1, &sb); ++ while (!rc && (fx = rpmfiNext(fi)) >= 0) { ++ struct filedata_s *fp = &fdata[fx]; ++ fp->action = rpmfsGetAction(fs, rpmfiFX(fi)); ++ fp->fpath = fsmFsPath(fi, NULL); ++ rc = fsmStat(fp->fpath, 1, &fp->sb); + +- fsmDebug(fpath, action, &sb); ++ fsmDebug(fp->fpath, fp->action, &fp->sb); + + /* Run fsm file pre hook for all plugins */ +- rc = rpmpluginsCallFsmFilePre(plugins, fi, fpath, +- sb.st_mode, action); ++ rc = rpmpluginsCallFsmFilePre(plugins, fi, fp->fpath, ++ fp->sb.st_mode, fp->action); + +- if (!XFA_SKIPPING(action)) +- rc = fsmBackup(fi, action); ++ if (!XFA_SKIPPING(fp->action)) ++ rc = fsmBackup(fi, fp->action); + + /* Remove erased files. */ +- if (action == FA_ERASE) { ++ if (fp->action == FA_ERASE) { + int missingok = (rpmfiFFlags(fi) & (RPMFILE_MISSINGOK | RPMFILE_GHOST)); + +- rc = fsmRemove(fpath, sb.st_mode); ++ rc = fsmRemove(fp->fpath, fp->sb.st_mode); + + /* + * Missing %ghost or %missingok entries are not errors. +@@ -1093,20 +1104,20 @@ int rpmPackageFilesRemove(rpmts ts, rpmte te, rpmfiles files, + if (rc) { + int lvl = strict_erasures ? RPMLOG_ERR : RPMLOG_WARNING; + rpmlog(lvl, _("%s %s: remove failed: %s\n"), +- S_ISDIR(sb.st_mode) ? _("directory") : _("file"), +- fpath, strerror(errno)); ++ S_ISDIR(fp->sb.st_mode) ? _("directory") : _("file"), ++ fp->fpath, strerror(errno)); + } + } + + /* Run fsm file post hook for all plugins */ +- rpmpluginsCallFsmFilePost(plugins, fi, fpath, +- sb.st_mode, action, rc); ++ rpmpluginsCallFsmFilePost(plugins, fi, fp->fpath, ++ fp->sb.st_mode, fp->action, rc); + + /* XXX Failure to remove is not (yet) cause for failure. */ + if (!strict_erasures) rc = 0; + + if (rc) +- *failedFile = xstrdup(fpath); ++ *failedFile = xstrdup(fp->fpath); + + if (rc == 0) { + /* Notify on success. */ +@@ -1114,10 +1125,11 @@ int rpmPackageFilesRemove(rpmts ts, rpmte te, rpmfiles files, + rpm_loff_t amount = rpmfiFC(fi) - rpmfiFX(fi); + rpmpsmNotify(psm, RPMCALLBACK_UNINST_PROGRESS, amount); + } +- fpath = _free(fpath); + } + +- free(fpath); ++ for (int i = 0; i < fc; i++) ++ free(fdata[i].fpath); ++ free(fdata); + rpmfiFree(fi); + + return rc; +-- +2.34.1 + + +From 215525131b0e4e015d83c25828700d7531fec6dd Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Wed, 10 Feb 2021 10:08:27 +0200 +Subject: [PATCH 06/10] Refactor fsmMkfile() to take advantage of the new state + struct + +Move setmeta into the struct too (we'll want this later anyhow), +and now we only need to pass the struct to fsmMkfile(). One less +argument to pass around, it has way too many still. + +No functional changes. +--- + lib/fsm.c | 22 +++++++++++----------- + 1 file changed, 11 insertions(+), 11 deletions(-) + +diff --git a/lib/fsm.c b/lib/fsm.c +index ab15c2bf3..6c873206c 100644 +--- a/lib/fsm.c ++++ b/lib/fsm.c +@@ -39,6 +39,7 @@ static int strict_erasures = 0; + #define _filePerms 0644 + + struct filedata_s { ++ int setmeta; + int skip; + rpmFileAction action; + const char *suffix; +@@ -279,8 +280,8 @@ exit: + return rc; + } + +-static int fsmMkfile(rpmfi fi, const char *dest, rpmfiles files, +- rpmpsm psm, int nodigest, int *setmeta, ++static int fsmMkfile(rpmfi fi, struct filedata_s *fp, rpmfiles files, ++ rpmpsm psm, int nodigest, + int * firsthardlink, FD_t *firstlinkfile) + { + int rc = 0; +@@ -290,11 +291,11 @@ static int fsmMkfile(rpmfi fi, const char *dest, rpmfiles files, + /* Create first hardlinked file empty */ + if (*firsthardlink < 0) { + *firsthardlink = rpmfiFX(fi); +- rc = wfd_open(firstlinkfile, dest); ++ rc = wfd_open(firstlinkfile, fp->fpath); + } else { + /* Create hard links for others */ + char *fn = rpmfilesFN(files, *firsthardlink); +- rc = link(fn, dest); ++ rc = link(fn, fp->fpath); + if (rc < 0) { + rc = RPMERR_LINK_FAILED; + } +@@ -305,14 +306,14 @@ static int fsmMkfile(rpmfi fi, const char *dest, rpmfiles files, + existing) file with content */ + if (numHardlinks<=1) { + if (!rc) +- rc = expandRegular(fi, dest, psm, nodigest); ++ rc = expandRegular(fi, fp->fpath, psm, nodigest); + } else if (rpmfiArchiveHasContent(fi)) { + if (!rc) + rc = rpmfiArchiveReadToFilePsm(fi, *firstlinkfile, nodigest, psm); + wfd_close(firstlinkfile); + *firsthardlink = -1; + } else { +- *setmeta = 0; ++ fp->setmeta = 0; + } + + return rc; +@@ -898,6 +899,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + struct filedata_s *fp = &fdata[fx]; + fp->action = rpmfsGetAction(fs, fx); + fp->skip = XFA_SKIPPING(fp->action); ++ fp->setmeta = 1; + if (fp->action != FA_TOUCH) { + fp->suffix = S_ISDIR(rpmfiFMode(fi)) ? NULL : tid; + } else { +@@ -924,8 +926,6 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + } + + if (!fp->skip) { +- int setmeta = 1; +- + /* Directories replacing something need early backup */ + if (!fp->suffix) { + rc = fsmBackup(fi, fp->action); +@@ -951,8 +951,8 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + + if (S_ISREG(fp->sb.st_mode)) { + if (rc == RPMERR_ENOENT) { +- rc = fsmMkfile(fi, fp->fpath, files, psm, nodigest, +- &setmeta, &firsthardlink, &firstlinkfile); ++ rc = fsmMkfile(fi, fp, files, psm, nodigest, ++ &firsthardlink, &firstlinkfile); + } + } else if (S_ISDIR(fp->sb.st_mode)) { + if (rc == RPMERR_ENOENT) { +@@ -985,7 +985,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + + touch: + /* Set permissions, timestamps etc for non-hardlink entries */ +- if (!rc && setmeta) { ++ if (!rc && fp->setmeta) { + rc = fsmSetmeta(fp->fpath, fi, plugins, fp->action, + &fp->sb, nofcaps); + } +-- +2.34.1 + + +From 062167c2532a066301b15a0e85a1ac6cb6c07472 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Wed, 10 Feb 2021 10:24:22 +0200 +Subject: [PATCH 07/10] Clarify file installation temporary suffix rule + +We only use a temporary suffix for regular files that we are actually +creating, skipped and touched files should not have it. Add XFA_CREATING() +macro to accomppany XFA_SKIPPING() to easily check whether the file +is being created or something else. + +No functional changes but makes the logic clearer. +--- + lib/fsm.c | 5 +---- + lib/rpmfiles.h | 3 +++ + 2 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/lib/fsm.c b/lib/fsm.c +index 6c873206c..e4842a200 100644 +--- a/lib/fsm.c ++++ b/lib/fsm.c +@@ -900,11 +900,8 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + fp->action = rpmfsGetAction(fs, fx); + fp->skip = XFA_SKIPPING(fp->action); + fp->setmeta = 1; +- if (fp->action != FA_TOUCH) { ++ if (XFA_CREATING(fp->action)) + fp->suffix = S_ISDIR(rpmfiFMode(fi)) ? NULL : tid; +- } else { +- fp->suffix = NULL; +- } + fp->fpath = fsmFsPath(fi, fp->suffix); + + /* Remap file perms, owner, and group. */ +diff --git a/lib/rpmfiles.h b/lib/rpmfiles.h +index 64b33281a..7f3f82662 100644 +--- a/lib/rpmfiles.h ++++ b/lib/rpmfiles.h +@@ -90,6 +90,9 @@ typedef enum rpmFileAction_e { + #define XFA_SKIPPING(_a) \ + ((_a) == FA_SKIP || (_a) == FA_SKIPNSTATE || (_a) == FA_SKIPNETSHARED || (_a) == FA_SKIPCOLOR) + ++#define XFA_CREATING(_a) \ ++ ((_a) == FA_CREATE || (_a) == FA_BACKUP || (_a) == FA_SAVE || (_a) == FA_ALTNAME) ++ + /** + * We pass these around as an array with a sentinel. + */ +-- +2.34.1 + + +From b3c6a3358dc4ec72fda0a229c22a5b79ac392848 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Wed, 10 Feb 2021 10:24:22 +0200 +Subject: [PATCH 08/10] Clarify file installation temporary suffix rule + +We only use a temporary suffix for regular files that we are actually +creating, skipped and touched files should not have it. Add XFA_CREATING() +macro to accomppany XFA_SKIPPING() to easily check whether the file +is being created or something else. + +No functional changes but makes the logic clearer. +--- + lib/fsm.c | 4 ++-- + 1 file changed, 2 insertions(+), 2 deletions(-) + +diff --git a/lib/fsm.c b/lib/fsm.c +index e4842a200..4d7c03895 100644 +--- a/lib/fsm.c ++++ b/lib/fsm.c +@@ -900,8 +900,8 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + fp->action = rpmfsGetAction(fs, fx); + fp->skip = XFA_SKIPPING(fp->action); + fp->setmeta = 1; +- if (XFA_CREATING(fp->action)) +- fp->suffix = S_ISDIR(rpmfiFMode(fi)) ? NULL : tid; ++ if (XFA_CREATING(fp->action) && !S_ISDIR(rpmfiFMode(fi))) ++ fp->suffix = tid; + fp->fpath = fsmFsPath(fi, fp->suffix); + + /* Remap file perms, owner, and group. */ +-- +2.34.1 + + +From 9097021b19c658ff55f7515878bae0592fd86377 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Wed, 10 Feb 2021 11:25:10 +0200 +Subject: [PATCH 09/10] Handle hardlink tracking with a file state pointer + +No functional changes, just makes it a little cleaner as firstlink now +points to the actual file data instead of a index number somewhere. +--- + lib/fsm.c | 20 +++++++++----------- + 1 file changed, 9 insertions(+), 11 deletions(-) + +diff --git a/lib/fsm.c b/lib/fsm.c +index 4d7c03895..cef17e669 100644 +--- a/lib/fsm.c ++++ b/lib/fsm.c +@@ -282,24 +282,22 @@ exit: + + static int fsmMkfile(rpmfi fi, struct filedata_s *fp, rpmfiles files, + rpmpsm psm, int nodigest, +- int * firsthardlink, FD_t *firstlinkfile) ++ struct filedata_s ** firstlink, FD_t *firstlinkfile) + { + int rc = 0; + int numHardlinks = rpmfiFNlink(fi); + + if (numHardlinks > 1) { + /* Create first hardlinked file empty */ +- if (*firsthardlink < 0) { +- *firsthardlink = rpmfiFX(fi); ++ if (*firstlink == NULL) { ++ *firstlink = fp; + rc = wfd_open(firstlinkfile, fp->fpath); + } else { + /* Create hard links for others */ +- char *fn = rpmfilesFN(files, *firsthardlink); +- rc = link(fn, fp->fpath); ++ rc = link((*firstlink)->fpath, fp->fpath); + if (rc < 0) { + rc = RPMERR_LINK_FAILED; + } +- free(fn); + } + } + /* Write normal files or fill the last hardlinked (already +@@ -311,7 +309,7 @@ static int fsmMkfile(rpmfi fi, struct filedata_s *fp, rpmfiles files, + if (!rc) + rc = rpmfiArchiveReadToFilePsm(fi, *firstlinkfile, nodigest, psm); + wfd_close(firstlinkfile); +- *firsthardlink = -1; ++ *firstlink = NULL; + } else { + fp->setmeta = 0; + } +@@ -879,10 +877,10 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + int fc = rpmfilesFC(files); + int nodigest = (rpmtsFlags(ts) & RPMTRANS_FLAG_NOFILEDIGEST) ? 1 : 0; + int nofcaps = (rpmtsFlags(ts) & RPMTRANS_FLAG_NOCAPS) ? 1 : 0; +- int firsthardlink = -1; + FD_t firstlinkfile = NULL; + char *tid = NULL; + struct filedata_s *fdata = xcalloc(fc, sizeof(*fdata)); ++ struct filedata_s *firstlink = NULL; + + if (fi == NULL) { + rc = RPMERR_BAD_MAGIC; +@@ -949,7 +947,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + if (S_ISREG(fp->sb.st_mode)) { + if (rc == RPMERR_ENOENT) { + rc = fsmMkfile(fi, fp, files, psm, nodigest, +- &firsthardlink, &firstlinkfile); ++ &firstlink, &firstlinkfile); + } + } else if (S_ISDIR(fp->sb.st_mode)) { + if (rc == RPMERR_ENOENT) { +@@ -986,13 +984,13 @@ touch: + rc = fsmSetmeta(fp->fpath, fi, plugins, fp->action, + &fp->sb, nofcaps); + } +- } else if (firsthardlink >= 0 && rpmfiArchiveHasContent(fi)) { ++ } else if (firstlink && rpmfiArchiveHasContent(fi)) { + /* On FA_TOUCH no hardlinks are created thus this is skipped. */ + /* we skip the hard linked file containing the content */ + /* write the content to the first used instead */ + rc = rpmfiArchiveReadToFilePsm(fi, firstlinkfile, nodigest, psm); + wfd_close(&firstlinkfile); +- firsthardlink = -1; ++ firstlink = NULL; + } + + if (rc) { +-- +2.34.1 + + +From 357afe98316cb643a0757163ddbc276a071f1a18 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Wed, 10 Feb 2021 14:15:33 +0200 +Subject: [PATCH 10/10] Handle file install failures more gracefully + +Run the file installation in multiple stages: +1) gather intel +2) unpack the archive to temporary files +3) set file metadatas +4) commit files to final location +5) mop up leftovers on failure + +This means we no longer leave behind a trail of untracked, potentially +harmful junk on installation failure. + +If commit step fails the package can still be left in an inconsistent stage, +this could be further improved by first backing up old files to temporary +location to allow undo on failure, but leaving that for some other day. +Also unowned directories will still be left behind. + +And yes, this is a somewhat scary change as it's the biggest ever change +to how rpm lays down files on install. Adopt the hardlink test spec +over to install tests and add some more tests for the new behavior. + +Fixes: #967 (+ multiple reports over the years) +--- + lib/fsm.c | 147 ++++++++++++++++++++------------ + tests/data/SPECS/hlinktest.spec | 4 + + tests/rpmbuild.at | 33 ------- + tests/rpmi.at | 92 ++++++++++++++++++++ + 4 files changed, 189 insertions(+), 87 deletions(-) + +diff --git a/lib/fsm.c b/lib/fsm.c +index cef17e669..82af6f39f 100644 +--- a/lib/fsm.c ++++ b/lib/fsm.c +@@ -38,7 +38,17 @@ static int strict_erasures = 0; + #define _dirPerms 0755 + #define _filePerms 0644 + ++enum filestage_e { ++ FILE_COMMIT = -1, ++ FILE_NONE = 0, ++ FILE_PRE = 1, ++ FILE_UNPACK = 2, ++ FILE_PREP = 3, ++ FILE_POST = 4, ++}; ++ + struct filedata_s { ++ int stage; + int setmeta; + int skip; + rpmFileAction action; +@@ -868,10 +878,9 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + rpmpsm psm, char ** failedFile) + { + FD_t payload = rpmtePayload(te); +- rpmfi fi = rpmfiNewArchiveReader(payload, files, RPMFI_ITER_READ_ARCHIVE); ++ rpmfi fi = NULL; + rpmfs fs = rpmteGetFileStates(te); + rpmPlugins plugins = rpmtsPlugins(ts); +- int saveerrno = errno; + int rc = 0; + int fx = -1; + int fc = rpmfilesFC(files); +@@ -882,20 +891,17 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + struct filedata_s *fdata = xcalloc(fc, sizeof(*fdata)); + struct filedata_s *firstlink = NULL; + +- if (fi == NULL) { +- rc = RPMERR_BAD_MAGIC; +- goto exit; +- } +- + /* transaction id used for temporary path suffix while installing */ + rasprintf(&tid, ";%08x", (unsigned)rpmtsGetTid(ts)); + +- /* Detect and create directories not explicitly in package. */ +- rc = fsmMkdirs(files, fs, plugins); +- ++ /* Collect state data for the whole operation */ ++ fi = rpmfilesIter(files, RPMFI_ITER_FWD); + while (!rc && (fx = rpmfiNext(fi)) >= 0) { + struct filedata_s *fp = &fdata[fx]; +- fp->action = rpmfsGetAction(fs, fx); ++ if (rpmfiFFlags(fi) & RPMFILE_GHOST) ++ fp->action = FA_SKIP; ++ else ++ fp->action = rpmfsGetAction(fs, fx); + fp->skip = XFA_SKIPPING(fp->action); + fp->setmeta = 1; + if (XFA_CREATING(fp->action) && !S_ISDIR(rpmfiFMode(fi))) +@@ -905,20 +911,32 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + /* Remap file perms, owner, and group. */ + rc = rpmfiStat(fi, 1, &fp->sb); + ++ setFileState(fs, fx); + fsmDebug(fp->fpath, fp->action, &fp->sb); + +- /* Exit on error. */ +- if (rc) +- break; +- + /* Run fsm file pre hook for all plugins */ + rc = rpmpluginsCallFsmFilePre(plugins, fi, fp->fpath, + fp->sb.st_mode, fp->action); +- if (rc) { +- fp->skip = 1; +- } else { +- setFileState(fs, fx); +- } ++ fp->stage = FILE_PRE; ++ } ++ fi = rpmfiFree(fi); ++ ++ if (rc) ++ goto exit; ++ ++ fi = rpmfiNewArchiveReader(payload, files, RPMFI_ITER_READ_ARCHIVE); ++ if (fi == NULL) { ++ rc = RPMERR_BAD_MAGIC; ++ goto exit; ++ } ++ ++ /* Detect and create directories not explicitly in package. */ ++ if (!rc) ++ rc = fsmMkdirs(files, fs, plugins); ++ ++ /* Process the payload */ ++ while (!rc && (fx = rpmfiNext(fi)) >= 0) { ++ struct filedata_s *fp = &fdata[fx]; + + if (!fp->skip) { + /* Directories replacing something need early backup */ +@@ -942,7 +960,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + + /* When touching we don't need any of this... */ + if (fp->action == FA_TOUCH) +- goto touch; ++ continue; + + if (S_ISREG(fp->sb.st_mode)) { + if (rc == RPMERR_ENOENT) { +@@ -978,12 +996,6 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + rc = RPMERR_UNKNOWN_FILETYPE; + } + +-touch: +- /* Set permissions, timestamps etc for non-hardlink entries */ +- if (!rc && fp->setmeta) { +- rc = fsmSetmeta(fp->fpath, fi, plugins, fp->action, +- &fp->sb, nofcaps); +- } + } else if (firstlink && rpmfiArchiveHasContent(fi)) { + /* On FA_TOUCH no hardlinks are created thus this is skipped. */ + /* we skip the hard linked file containing the content */ +@@ -993,47 +1005,74 @@ touch: + firstlink = NULL; + } + +- if (rc) { +- if (!fp->skip) { +- /* XXX only erase if temp fn w suffix is in use */ +- if (fp->suffix) { +- (void) fsmRemove(fp->fpath, fp->sb.st_mode); +- } +- errno = saveerrno; +- } +- } else { +- /* Notify on success. */ ++ /* Notify on success. */ ++ if (rc) ++ *failedFile = xstrdup(fp->fpath); ++ else + rpmpsmNotify(psm, RPMCALLBACK_INST_PROGRESS, rpmfiArchiveTell(fi)); ++ fp->stage = FILE_UNPACK; ++ } ++ fi = rpmfiFree(fi); + +- if (!fp->skip) { +- /* Backup file if needed. Directories are handled earlier */ +- if (fp->suffix) +- rc = fsmBackup(fi, fp->action); ++ if (!rc && fx < 0 && fx != RPMERR_ITER_END) ++ rc = fx; + +- if (!rc) +- rc = fsmCommit(&fp->fpath, fi, fp->action, fp->suffix); +- } ++ /* Set permissions, timestamps etc for non-hardlink entries */ ++ fi = rpmfilesIter(files, RPMFI_ITER_FWD); ++ while (!rc && (fx = rpmfiNext(fi)) >= 0) { ++ struct filedata_s *fp = &fdata[fx]; ++ if (!fp->skip && fp->setmeta) { ++ rc = fsmSetmeta(fp->fpath, fi, plugins, fp->action, ++ &fp->sb, nofcaps); + } +- + if (rc) + *failedFile = xstrdup(fp->fpath); ++ fp->stage = FILE_PREP; ++ } ++ fi = rpmfiFree(fi); + +- /* Run fsm file post hook for all plugins */ +- rpmpluginsCallFsmFilePost(plugins, fi, fp->fpath, +- fp->sb.st_mode, fp->action, rc); ++ /* If all went well, commit files to final destination */ ++ fi = rpmfilesIter(files, RPMFI_ITER_FWD); ++ while (!rc && (fx = rpmfiNext(fi)) >= 0) { ++ struct filedata_s *fp = &fdata[fx]; ++ ++ if (!fp->skip) { ++ /* Backup file if needed. Directories are handled earlier */ ++ if (!rc && fp->suffix) ++ rc = fsmBackup(fi, fp->action); ++ ++ if (!rc) ++ rc = fsmCommit(&fp->fpath, fi, fp->action, fp->suffix); ++ ++ if (!rc) ++ fp->stage = FILE_COMMIT; ++ else ++ *failedFile = xstrdup(fp->fpath); ++ } + } ++ fi = rpmfiFree(fi); + +- if (!rc && fx != RPMERR_ITER_END) +- rc = fx; ++ /* Walk backwards in case we need to erase */ ++ fi = rpmfilesIter(files, RPMFI_ITER_BACK); ++ while ((fx = rpmfiNext(fi)) >= 0) { ++ struct filedata_s *fp = &fdata[fx]; ++ /* Run fsm file post hook for all plugins for all processed files */ ++ if (fp->stage) { ++ rpmpluginsCallFsmFilePost(plugins, fi, fp->fpath, ++ fp->sb.st_mode, fp->action, rc); ++ } ++ ++ /* On failure, erase non-committed files */ ++ if (rc && fp->stage > FILE_NONE && !fp->skip) { ++ (void) fsmRemove(fp->fpath, fp->sb.st_mode); ++ } ++ } + + rpmswAdd(rpmtsOp(ts, RPMTS_OP_UNCOMPRESS), fdOp(payload, FDSTAT_READ)); + rpmswAdd(rpmtsOp(ts, RPMTS_OP_DIGEST), fdOp(payload, FDSTAT_DIGEST)); + + exit: +- +- /* No need to bother with close errors on read */ +- rpmfiArchiveClose(fi); +- rpmfiFree(fi); ++ fi = rpmfiFree(fi); + Fclose(payload); + free(tid); + for (int i = 0; i < fc; i++) +diff --git a/tests/data/SPECS/hlinktest.spec b/tests/data/SPECS/hlinktest.spec +index b3b8866fc..1f453524e 100644 +--- a/tests/data/SPECS/hlinktest.spec ++++ b/tests/data/SPECS/hlinktest.spec +@@ -1,5 +1,6 @@ + %bcond_with unpackaged_dirs + %bcond_with unpackaged_files ++%bcond_with owned_dir + + Summary: Testing hard link behavior + Name: hlinktest +@@ -38,4 +39,7 @@ touch $RPM_BUILD_ROOT/toot + + %files + %defattr(-,root,root) ++%if %{with owned_dir} ++%dir /foo ++%endif + /foo/* +diff --git a/tests/rpmbuild.at b/tests/rpmbuild.at +index 4294fd97c..c99fa7a63 100644 +--- a/tests/rpmbuild.at ++++ b/tests/rpmbuild.at +@@ -139,39 +139,6 @@ drwxrwxrwx zoot zoot /j/dir + []) + AT_CLEANUP + +-# ------------------------------ +-# hardlink tests +-AT_SETUP([rpmbuild hardlink]) +-AT_KEYWORDS([build]) +-AT_CHECK([ +-RPMDB_CLEAR +-RPMDB_INIT +-rm -rf ${TOPDIR} +- +-runroot rpmbuild \ +- -bb --quiet /data/SPECS/hlinktest.spec +- +-runroot rpm -i /build/RPMS/noarch/hlinktest-1.0-1.noarch.rpm +- +-runroot rpm -q --qf "[[%{filenlinks} %{filenames}\n]]%{longsize}\n" hlinktest +-runroot rpm -V --nouser --nogroup hlinktest +-ls -i "${RPMTEST}"/foo/hello* | awk {'print $1'} | sort -u | wc -l +- +-], +-[0], +-[2 /foo/aaaa +-1 /foo/copyllo +-4 /foo/hello +-4 /foo/hello-bar +-4 /foo/hello-foo +-4 /foo/hello-world +-2 /foo/zzzz +-87 +-1 +-], +-[]) +-AT_CLEANUP +- + AT_SETUP([rpmbuild unpackaged files]) + AT_KEYWORDS([build]) + AT_CHECK([ +diff --git a/tests/rpmi.at b/tests/rpmi.at +index 71e17821b..bf1606b6d 100644 +--- a/tests/rpmi.at ++++ b/tests/rpmi.at +@@ -767,3 +767,95 @@ runroot rpm -V --nouser --nogroup suicidal + [], + []) + AT_CLEANUP ++ ++# ------------------------------ ++# hardlink tests ++AT_SETUP([rpm -i hardlinks]) ++AT_KEYWORDS([build install]) ++RPMDB_INIT ++ ++# Need a reproducable test package ++runroot rpmbuild \ ++ --define "%optflags -O2 -g" \ ++ --define "%_target_platform noarch-linux" \ ++ --define "%_binary_payload w.ufdio" \ ++ --define "%_buildhost localhost" \ ++ --define "%use_source_date_epoch_as_buildtime 1" \ ++ --define "%source_date_epoch_from_changelog 1" \ ++ --define "%clamp_mtime_to_source_date_epoch 1" \ ++ --with owned_dir \ ++ -bb --quiet /data/SPECS/hlinktest.spec ++ ++pkg="/build/RPMS/noarch/hlinktest-1.0-1.noarch.rpm" ++ ++cp "${RPMTEST}/${pkg}" "${RPMTEST}/tmp/1.rpm" ++dd if=/dev/zero of="${RPMTEST}/tmp/1.rpm" \ ++ conv=notrunc bs=1 seek=8180 count=6 2> /dev/null ++ ++cp "${RPMTEST}/${pkg}" "${RPMTEST}/tmp/2.rpm" ++dd if=/dev/zero of="${RPMTEST}/tmp/2.rpm" \ ++ conv=notrunc bs=1 seek=8150 count=6 2> /dev/null ++ ++cp "${RPMTEST}/${pkg}" "${RPMTEST}/tmp/3.rpm" ++dd if=/dev/zero of="${RPMTEST}/tmp/3.rpm" \ ++ conv=notrunc bs=1 seek=8050 count=6 2> /dev/null ++ ++AT_CHECK([ ++RPMDB_INIT ++runroot rpm -i --noverify /tmp/1.rpm ++# test that nothing of the contents remains after failure ++test -d "${RPMTEST}/foo" ++], ++[1], ++[], ++[error: unpacking of archive failed: cpio: Archive file not in header ++error: hlinktest-1.0-1.noarch: install failed ++]) ++ ++AT_CHECK([ ++RPMDB_INIT ++runroot rpm -i --noverify /tmp/2.rpm ++# test that nothing of the contents remains after failure ++test -d "${RPMTEST}/foo" ++], ++[1], ++[], ++[error: unpacking of archive failed: cpio: Bad/unreadable header ++error: hlinktest-1.0-1.noarch: install failed ++]) ++ ++AT_CHECK([ ++RPMDB_INIT ++runroot rpm -i --noverify /tmp/3.rpm 2>&1| sed 's/;.*:/:/g' ++# test that nothing of the contents remains after failure ++test -d "${RPMTEST}/foo" ++], ++[1], ++[error: unpacking of archive failed on file /foo/hello-world: Digest mismatch ++error: hlinktest-1.0-1.noarch: install failed ++], ++[]) ++ ++AT_CHECK([ ++RPMDB_INIT ++runroot rpm -i /build/RPMS/noarch/hlinktest-1.0-1.noarch.rpm ++runroot rpm -q --qf "[[%{filenlinks} %{filenames}\n]]%{longsize}\n" hlinktest ++ls -i "${RPMTEST}"/foo/hello* | awk {'print $1'} | sort -u | wc -l ++runroot rpm -e hlinktest ++ ++], ++[0], ++[1 /foo ++2 /foo/aaaa ++1 /foo/copyllo ++4 /foo/hello ++4 /foo/hello-bar ++4 /foo/hello-foo ++4 /foo/hello-world ++2 /foo/zzzz ++87 ++1 ++], ++[]) ++AT_CLEANUP ++ +-- +2.34.1 + diff --git a/SOURCES/measure.patch b/SOURCES/measure.patch index b0c580f..dab1596 100644 --- a/SOURCES/measure.patch +++ b/SOURCES/measure.patch @@ -15,9 +15,9 @@ index 3cc8a3555..c8a087959 100644 --- a/macros.in +++ b/macros.in @@ -1173,6 +1173,7 @@ package or when debugging this package.\ + # Transaction plugin macros %__plugindir %{_libdir}/rpm-plugins - %__transaction_reflink %{__plugindir}/reflink.so +%__transaction_measure %{__plugindir}/measure.so %__transaction_systemd_inhibit %{__plugindir}/systemd_inhibit.so %__transaction_selinux %{__plugindir}/selinux.so diff --git a/SPECS/rpm.spec b/SPECS/rpm.spec index 6eedee8..db24eb0 100644 --- a/SPECS/rpm.spec +++ b/SPECS/rpm.spec @@ -192,10 +192,11 @@ Patch1982: 0033-Enable-fsverity-in-CI.patch Patch1983: 0034-rpmsign-Adopting-PKCS11-opaque-keys-support-in-libfsverity-for-fsverity-signatures.patch %endif -Patch9990: https://github.com/rpm-software-management/rpm/pull/1381.patch -Provides: rpm(pr1381) -Patch9991: https://github.com/rpm-software-management/rpm/pull/1470.patch -Provides: rpm(pr1470) +Patch9989: 1534.patch +#Patch9990: https://github.com/rpm-software-management/rpm/pull/1381.patch +#Provides: rpm(pr1381) +#Patch9991: https://github.com/rpm-software-management/rpm/pull/1470.patch +#Provides: rpm(pr1470) Patch9999: measure.patch # Partially GPL/LGPL dual-licensed and some bits with BSD @@ -489,13 +490,6 @@ Obsoletes: fapolicyd-dnf-plugin %description plugin-fapolicyd %{summary}. -%package plugin-reflink -Summary: Rpm plugin for reflink functionality -Requires: rpm-libs%{_isa} = %{version}-%{release} - -%description plugin-reflink -%{summary}. - %package plugin-measure Summary: Rpm plugin for measure Group: System Environment/Base @@ -721,10 +715,6 @@ make check || cat tests/rpmtests.log %{_libdir}/rpm-plugins/fapolicyd.so %{_mandir}/man8/rpm-plugin-fapolicyd.8* -%files plugin-reflink -%{_bindir}/rpm2extents -%{_libdir}/rpm-plugins/reflink.so - %files plugin-measure %{_libdir}/rpm-plugins/measure.so %endif # with plugins