From 9a6bdcc06c9d35de18aa6ca180814cb39b078f88 Mon Sep 17 00:00:00 2001 From: xujing Date: Wed, 7 Sep 2022 20:55:21 +0800 Subject: [PATCH] rpm: fixCVE-2021-35937,CVE-2021-35938,CVE-2021-35939 --- backport-0001-CVE-2021-35938.patch | 43 +++ ...t-0001-CVE-2021-35939-CVE-2021-35937.patch | 285 +++++++++++++++ ...Add-diagnostics-to-archive-unpacking.patch | 47 +++ ...-helper-to-fsm-to-make-it-debuggable.patch | 52 +++ ...lback-on-directory-changes-during-rp.patch | 108 ++++++ backport-Bury-rpmio-FD-use-to-fsmUnpack.patch | 132 +++++++ ...up-file-unpack-iteration-logic-a-bit.patch | 70 ++++ ...ped-hardlink-with-content-case-with-.patch | 59 +++ ...-creation-steps-the-at-family-of-cal.patch | 192 ++++++++++ backport-Drop-unused-filename-variable.patch | 31 ++ ...-the-hardlink-metadata-setting-logic.patch | 70 ++++ ...ile-install-failures-more-gracefully.patch | 261 ++++++++++++++ ...k-tracking-with-a-file-state-pointer.patch | 96 +++++ ...ile-open-and-close-in-fsm-debuggable.patch | 148 ++++++++ ...etadata-setting-back-to-unpack-stage.patch | 72 ++++ ...stall-and-remove-around-a-common-str.patch | 340 ++++++++++++++++++ ...le-to-take-advantage-of-the-new-stat.patch | 112 ++++++ ...iptor-of-created-file-from-fsmMkfile.patch | 68 ++++ ...solidate-the-hardlink-handling-logic.patch | 135 +++++++ ...-to-FA_CREATE-if-the-file-went-away-.patch | 66 ++++ rpm.spec | 29 +- 21 files changed, 2415 insertions(+), 1 deletion(-) create mode 100644 backport-0001-CVE-2021-35938.patch create mode 100644 backport-0001-CVE-2021-35939-CVE-2021-35937.patch create mode 100644 backport-Add-diagnostics-to-archive-unpacking.patch create mode 100644 backport-Add-hardlink-helper-to-fsm-to-make-it-debuggable.patch create mode 100644 backport-Add-optional-callback-on-directory-changes-during-rp.patch create mode 100644 backport-Bury-rpmio-FD-use-to-fsmUnpack.patch create mode 100644 backport-Clean-up-file-unpack-iteration-logic-a-bit.patch create mode 100644 backport-Consolidate-skipped-hardlink-with-content-case-with-.patch create mode 100644 backport-Convert-the-file-creation-steps-the-at-family-of-cal.patch create mode 100644 backport-Drop-unused-filename-variable.patch create mode 100644 backport-Fix-sanitize-the-hardlink-metadata-setting-logic.patch create mode 100644 backport-Handle-file-install-failures-more-gracefully.patch create mode 100644 backport-Handle-hardlink-tracking-with-a-file-state-pointer.patch create mode 100644 backport-Make-file-open-and-close-in-fsm-debuggable.patch create mode 100644 backport-Move-file-metadata-setting-back-to-unpack-stage.patch create mode 100644 backport-Refactor-file-install-and-remove-around-a-common-str.patch create mode 100644 backport-Refactor-fsmMkfile-to-take-advantage-of-the-new-stat.patch create mode 100644 backport-Return-descriptor-of-created-file-from-fsmMkfile.patch create mode 100644 backport-Streamline-consolidate-the-hardlink-handling-logic.patch create mode 100644 backport-Upgrade-FA_TOUCH-to-FA_CREATE-if-the-file-went-away-.patch diff --git a/backport-0001-CVE-2021-35938.patch b/backport-0001-CVE-2021-35938.patch new file mode 100644 index 0000000..4da38fc --- /dev/null +++ b/backport-0001-CVE-2021-35938.patch @@ -0,0 +1,43 @@ +From 25a435e90844ea98fe5eb7bef22c1aecf3a9c033 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Mon, 14 Feb 2022 14:29:33 +0200 +Subject: [PATCH] Set file metadata via fd-based ops for everything but + symlinks + +Conflict:adapt context +Reference:https://github.com/rpm-software-management/rpm/commit/25a435e90844ea98fe5eb7bef22c1aecf3a9c033 + +Regular file ops are fd-based already, for the rest we need to open them +manually. Files with temporary suffix must never be followed, for +directories (and pre-existing FA_TOUCHed files) use the rpm symlink +"root or target owner allowed" rule wrt following. + +This mostly fixes CVE-2021-35938, but as we're not yet using dirfd-based +operatiosn for everything there are corner cases left undone. And then +there's the plugin API which needs updating for all this. +--- + lib/fsm.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/lib/fsm.c b/lib/fsm.c +index 913e9de2d..6f781c64d 100644 +--- a/lib/fsm.c ++++ b/lib/fsm.c +@@ -990,6 +990,14 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + rc = RPMERR_UNKNOWN_FILETYPE; + } + ++ if (!rc && fd == -1 && !S_ISLNK(fp->sb.st_mode)) { ++ /* Only follow safe symlinks, and never on temporary files */ ++ fd = fsmOpenat(di.dirfd, fp->fpath, ++ fp->suffix ? AT_SYMLINK_NOFOLLOW : 0); ++ if (fd < 0) ++ rc = RPMERR_OPEN_FAILED; ++ } ++ + setmeta: + if (!rc && fp->setmeta) { + rc = fsmSetmeta(fp->fpath, fi, plugins, fp->action, +-- +2.27.0 + diff --git a/backport-0001-CVE-2021-35939-CVE-2021-35937.patch b/backport-0001-CVE-2021-35939-CVE-2021-35937.patch new file mode 100644 index 0000000..2e1c7e5 --- /dev/null +++ b/backport-0001-CVE-2021-35939-CVE-2021-35937.patch @@ -0,0 +1,285 @@ +From 96ec957e281220f8e137a2d5eb23b83a6377d556 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Thu, 10 Feb 2022 14:32:43 +0200 +Subject: [PATCH] Validate intermediate symlinks during installation, + CVE-2021-35939 + +Conflict:adapt context; adapt configure.ac because c223d84fbf,f8b8e86ae3, +9fe75561f9 is not merged. +Reference:https://github.com/rpm-software-management/rpm/commit/96ec957e281220f8e137a2d5eb23b83a6377d556 + +Whenever directory changes during unpacking, walk the entire tree from +starting from / and validate any symlinks crossed, fail the install +on invalid links. + +This is the first of step of many towards securing our file operations +against local tamperers and besides plugging that one CVE, paves the way +for the next step by adding the necessary directory fd tracking. +This also bumps the rpm OS requirements to a whole new level by requiring +the *at() family of calls from POSIX-1.2008. + +This necessarily does a whole lot of huffing and puffing we previously +did not do. It should be possible to cache secure (ie root-owned) +directory structures to avoid validating everything a million times +but for now, just keeping things simple. +--- + INSTALL | 2 + + configure.ac | 3 +- + lib/fsm.c | 144 ++++++++++++++++++++++++++++++++++++++++++++++++--- + 3 files changed, 142 insertions(+), 7 deletions(-) + +diff --git a/INSTALL b/INSTALL +index 677ef88c4..961a160e0 100644 +--- a/INSTALL ++++ b/INSTALL +@@ -103,6 +103,8 @@ option to configure). For GCC, OpenMP 4.5 is fully supported since GCC 6.1, + of GPG, available from + http://www.gnupg.org/ + ++Rpm requires a POSIX.1-2008 level operating system. ++ + To compile RPM: + -------------- + +diff --git a/configure.ac b/configure.ac +index 3ee340726..0099e5f34 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -580,7 +580,8 @@ AC_CHECK_FUNCS([secure_getenv __secure_getenv]) + + AC_CHECK_FUNCS( + [mkstemp getcwd basename dirname realpath setenv unsetenv regcomp lchown \ +- utimes getline], ++ utimes getline \ ++ openat mkdirat fstatat ], + [], [AC_MSG_ERROR([function required by rpm])]) + + AC_LIBOBJ(fnmatch) +diff --git a/lib/fsm.c b/lib/fsm.c +index 911898347..b6b152ab1 100644 +--- a/lib/fsm.c ++++ b/lib/fsm.c +@@ -8,6 +8,7 @@ + + #include + #include ++#include + #if WITH_CAP + #include + #endif +@@ -20,6 +21,7 @@ + #include "rpmio/rpmio_internal.h" /* fdInit/FiniDigest */ + #include "lib/fsm.h" + #include "lib/rpmte_internal.h" /* XXX rpmfs */ ++#include "lib/rpmfi_internal.h" /* rpmfiSetOnChdir */ + #include "lib/rpmplugins.h" /* rpm plugins hooks */ + #include "lib/rpmug.h" + +@@ -406,17 +408,118 @@ static int fsmRmdir(const char *path) + return rc; + } + +-static int fsmMkdir(const char *path, mode_t mode) ++static int fsmMkdir(int dirfd, const char *path, mode_t mode) + { +- int rc = mkdir(path, (mode & 07777)); ++ int rc = mkdirat(dirfd, path, (mode & 07777)); + if (_fsm_debug) +- rpmlog(RPMLOG_DEBUG, " %8s (%s, 0%04o) %s\n", __func__, +- path, (unsigned)(mode & 07777), ++ rpmlog(RPMLOG_DEBUG, " %8s (%d %s, 0%04o) %s\n", __func__, ++ dirfd, path, (unsigned)(mode & 07777), + (rc < 0 ? strerror(errno) : "")); + if (rc < 0) rc = RPMERR_MKDIR_FAILED; + return rc; + } + ++static int fsmOpenat(int dirfd, const char *path, int flags) ++{ ++ struct stat lsb, sb; ++ int sflags = flags | O_NOFOLLOW; ++ int fd = openat(dirfd, path, sflags); ++ ++ /* ++ * Only ever follow symlinks by root or target owner. Since we can't ++ * open the symlink itself, the order matters: we stat the link *after* ++ * opening the target, and if the link ownership changed between the calls ++ * it could've only been the link owner or root. ++ */ ++ if (fd < 0 && errno == ELOOP && flags != sflags) { ++ int ffd = openat(dirfd, path, flags); ++ if (ffd >= 0 && fstatat(dirfd, path, &lsb, AT_SYMLINK_NOFOLLOW) == 0) { ++ if (fstat(ffd, &sb) == 0) { ++ if (lsb.st_uid == 0 || lsb.st_uid == sb.st_uid) { ++ fd = ffd; ++ } else { ++ close(ffd); ++ } ++ } ++ } ++ } ++ return fd; ++} ++ ++static int fsmDoMkDir(rpmPlugins plugins, int dirfd, const char *dn, ++ int owned, mode_t mode) ++{ ++ int rc; ++ rpmFsmOp op = (FA_CREATE); ++ if (!owned) ++ op |= FAF_UNOWNED; ++ ++ /* Run fsm file pre hook for all plugins */ ++ rc = rpmpluginsCallFsmFilePre(plugins, NULL, dn, mode, op); ++ ++ if (!rc) ++ rc = fsmMkdir(dirfd, dn, mode); ++ ++ if (!rc) { ++ rc = rpmpluginsCallFsmFilePrepare(plugins, NULL, dn, dn, mode, op); ++ } ++ ++ /* Run fsm file post hook for all plugins */ ++ rpmpluginsCallFsmFilePost(plugins, NULL, dn, mode, op, rc); ++ ++ if (!rc) { ++ rpmlog(RPMLOG_DEBUG, ++ "%s directory created with perms %04o\n", ++ dn, (unsigned)(mode & 07777)); ++ } ++ ++ return rc; ++} ++ ++static int ensureDir(rpmPlugins plugins, const char *p, int owned, int create) ++{ ++ char *path = xstrdup(p); ++ char *dp = path; ++ char *sp = NULL, *bn; ++ int oflags = O_RDONLY; ++ ++ int dirfd = fsmOpenat(-1, "/", oflags); ++ int fd = dirfd; /* special case of "/" */ ++ ++ while ((bn = strtok_r(dp, "/", &sp)) != NULL) { ++ struct stat sb; ++ fd = fsmOpenat(dirfd, bn, oflags); ++ ++ if (fd < 0 && errno == ENOENT && create) { ++ mode_t mode = S_IFDIR | (_dirPerms & 07777); ++ if (fsmDoMkDir(plugins, dirfd, bn, owned, mode) == 0) { ++ fd = fsmOpenat(dirfd, bn, oflags|O_NOFOLLOW); ++ } ++ } ++ ++ if (fd >= 0 && fstat(fd, &sb) == 0 && !S_ISDIR(sb.st_mode)) { ++ close(fd); ++ errno = ENOTDIR; ++ fd = -1; ++ } ++ ++ close(dirfd); ++ if (fd >= 0) { ++ dirfd = fd; ++ } else { ++ dirfd = -1; ++ rpmlog(RPMLOG_ERR, _("failed to open dir %s of %s: %s\n"), ++ bn, p, strerror(errno)); ++ break; ++ } ++ ++ dp = NULL; ++ } ++ ++ free(path); ++ return dirfd; ++} ++ + static int fsmMkfifo(const char *path, mode_t mode) + { + int rc = mkfifo(path, (mode & 07777)); +@@ -507,7 +610,7 @@ static int fsmMkdirs(rpmfiles files, rpmfs fs, rpmPlugins plugins) + rc = rpmpluginsCallFsmFilePre(plugins, NULL, dn, mode, op); + + if (!rc) +- rc = fsmMkdir(dn, mode); ++ rc = fsmMkdir(-1, dn, mode); + + if (!rc) { + rc = rpmpluginsCallFsmFilePrepare(plugins, NULL, dn, dn, +@@ -874,6 +977,21 @@ static void setFileState(rpmfs fs, int i) + } + } + ++struct diriter_s { ++ int dirfd; ++}; ++ ++static int onChdir(rpmfi fi, void *data) ++{ ++ struct diriter_s *di = data; ++ ++ if (di->dirfd >= 0) { ++ close(di->dirfd); ++ di->dirfd = -1; ++ } ++ return 0; ++} ++ + int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + rpmpsm psm, char ** failedFile) + { +@@ -890,6 +1008,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + char *tid = NULL; + struct filedata_s *fdata = xcalloc(fc, sizeof(*fdata)); + struct filedata_s *firstlink = NULL; ++ struct diriter_s di = { -1 }; + + /* transaction id used for temporary path suffix while installing */ + rasprintf(&tid, ";%08x", (unsigned)rpmtsGetTid(ts)); +@@ -932,6 +1051,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + rc = RPMERR_BAD_MAGIC; + goto exit; + } ++ rpmfiSetOnChdir(fi, onChdir, &di); + + /* Detect and create directories not explicitly in package. */ + if (!rc) +@@ -946,6 +1066,16 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + if (!fp->suffix) { + rc = fsmBackup(fi, fp->action); + } ++ ++ if (di.dirfd == -1) { ++ di.dirfd = ensureDir(plugins, rpmfiDN(fi), 0, ++ (fp->action == FA_CREATE)); ++ if (di.dirfd == -1) { ++ rc = RPMERR_OPEN_FAILED; ++ break; ++ } ++ } ++ + /* Assume file does't exist when tmp suffix is in use */ + if (!fp->suffix) { + rc = fsmVerify(fp->fpath, fi); +@@ -980,7 +1110,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + mode_t mode = fp->sb.st_mode; + mode &= ~07777; + mode |= 00700; +- rc = fsmMkdir(fp->fpath, mode); ++ rc = fsmMkdir(di.dirfd, fp->fpath, mode); + } + } else if (S_ISLNK(fp->sb.st_mode)) { + if (rc == RPMERR_ENOENT) { +@@ -1022,6 +1152,8 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + fp->stage = FILE_UNPACK; + } + fi = rpmfiFree(fi); ++ close(di.dirfd); ++ di.dirfd = -1; + + if (!rc && fx < 0 && fx != RPMERR_ITER_END) + rc = fx; +-- +2.27.0 + diff --git a/backport-Add-diagnostics-to-archive-unpacking.patch b/backport-Add-diagnostics-to-archive-unpacking.patch new file mode 100644 index 0000000..1a10ceb --- /dev/null +++ b/backport-Add-diagnostics-to-archive-unpacking.patch @@ -0,0 +1,47 @@ +From f9b90179b7c80a170969d9ab4c77c0a311635e3f Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Thu, 11 Feb 2021 14:18:37 +0200 +Subject: [PATCH] Add diagnostics to archive unpacking + +Conflict:NA +Reference:https://github.com/rpm-software-management/rpm/commit/f9b90179b7c80a170969d9ab4c77c0a311635e3f + +Move to a clearly defined helper function, add fsmdebug output. +--- + lib/fsm.c | 13 ++++++++++++- + 1 file changed, 12 insertions(+), 1 deletion(-) + +diff --git a/lib/fsm.c b/lib/fsm.c +index bd2481a50..c065bd55a 100644 +--- a/lib/fsm.c ++++ b/lib/fsm.c +@@ -297,6 +297,17 @@ static int fsmOpen(FD_t *wfdp, const char *dest) + return rc; + } + ++static int fsmUnpack(rpmfi fi, FD_t fd, rpmpsm psm, int nodigest) ++{ ++ int rc = rpmfiArchiveReadToFilePsm(fi, fd, nodigest, psm); ++ if (_fsm_debug) { ++ rpmlog(RPMLOG_DEBUG, " %8s (%s %lu bytes [%d]) %s\n", __func__, ++ rpmfiFN(fi), rpmfiFSize(fi), Fileno(fd), ++ (rc < 0 ? strerror(errno) : "")); ++ } ++ return rc; ++} ++ + static int fsmMkfile(rpmfi fi, struct filedata_s *fp, rpmfiles files, + rpmpsm psm, int nodigest, + struct filedata_s ** firstlink, FD_t *firstlinkfile) +@@ -324,7 +335,7 @@ static int fsmMkfile(rpmfi fi, struct filedata_s *fp, rpmfiles files, + /* If the file has content, unpack it */ + if (rpmfiArchiveHasContent(fi)) { + if (!rc) +- rc = rpmfiArchiveReadToFilePsm(fi, fd, nodigest, psm); ++ rc = fsmUnpack(fi, fd, psm, nodigest); + /* Last file of hardlink set, ensure metadata gets set */ + if (*firstlink) { + (*firstlink)->setmeta = 1; +-- +2.27.0 + diff --git a/backport-Add-hardlink-helper-to-fsm-to-make-it-debuggable.patch b/backport-Add-hardlink-helper-to-fsm-to-make-it-debuggable.patch new file mode 100644 index 0000000..47fb0a2 --- /dev/null +++ b/backport-Add-hardlink-helper-to-fsm-to-make-it-debuggable.patch @@ -0,0 +1,52 @@ +From bbd30984dcedd2de4762b96c92b62a32f9448b03 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Thu, 11 Feb 2021 09:54:41 +0200 +Subject: [PATCH] Add hardlink helper to fsm to make it debuggable + +Conflict:NA +Reference:https://github.com/rpm-software-management/rpm/commit/bbd30984dcedd2de4762b96c92b62a32f9448b03 + +--- + lib/fsm.c | 19 +++++++++++++++---- + 1 file changed, 15 insertions(+), 4 deletions(-) + +diff --git a/lib/fsm.c b/lib/fsm.c +index 6efd25bdd..f8038e09b 100644 +--- a/lib/fsm.c ++++ b/lib/fsm.c +@@ -211,6 +211,20 @@ const char * dnlNextIterator(DNLI_t dnli) + return dn; + } + ++static int fsmLink(const char *opath, const char *path) ++{ ++ int rc = link(opath, path); ++ ++ if (_fsm_debug) { ++ rpmlog(RPMLOG_DEBUG, " %8s (%s, %s) %s\n", __func__, ++ opath, path, (rc < 0 ? strerror(errno) : "")); ++ } ++ ++ if (rc < 0) ++ rc = RPMERR_LINK_FAILED; ++ return rc; ++} ++ + static int fsmSetFCaps(const char *path, const char *captxt) + { + int rc = 0; +@@ -304,10 +318,7 @@ static int fsmMkfile(rpmfi fi, struct filedata_s *fp, rpmfiles files, + rc = wfd_open(firstlinkfile, fp->fpath); + } else { + /* Create hard links for others */ +- rc = link((*firstlink)->fpath, fp->fpath); +- if (rc < 0) { +- rc = RPMERR_LINK_FAILED; +- } ++ rc = fsmLink((*firstlink)->fpath, fp->fpath); + } + } + /* Write normal files or fill the last hardlinked (already +-- +2.27.0 + diff --git a/backport-Add-optional-callback-on-directory-changes-during-rp.patch b/backport-Add-optional-callback-on-directory-changes-during-rp.patch new file mode 100644 index 0000000..c967fd3 --- /dev/null +++ b/backport-Add-optional-callback-on-directory-changes-during-rp.patch @@ -0,0 +1,108 @@ +From fb13f7fd9eff012cb7b9dbf94ac5381c69404055 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Wed, 9 Feb 2022 14:47:14 +0200 +Subject: [PATCH] Add optional callback on directory changes during rpmfi + iteration + +Conflict:adapt rpmfi.c because 318efbaec is not merged. +Reference:https://github.com/rpm-software-management/rpm/commit/fb13f7fd9eff012cb7b9dbf94ac5381c69404055 + +Internal only for now in case we need to fiddle with the API some more, +but no reason this couldn't be made public later. +--- + lib/rpmfi.c | 24 +++++++++++++++++++++++- + lib/rpmfi_internal.h | 17 +++++++++++++++++ + 2 files changed, 40 insertions(+), 1 deletion(-) + +diff --git a/lib/rpmfi.c b/lib/rpmfi.c +index 4673fbb85..e8e7d08bf 100644 +--- a/lib/rpmfi.c ++++ b/lib/rpmfi.c +@@ -55,6 +55,9 @@ struct rpmfi_s { + int intervalStart; /*!< Start of iterating interval. */ + int intervalEnd; /*!< End of iterating interval. */ + ++ rpmfiChdirCb onChdir; /*!< Callback for directory changes */ ++ void *onChdirData; /*!< Caller private callback data */ ++ + rpmfiles files; /*!< File info set */ + rpmcpio_t archive; /*!< Archive with payload */ + unsigned char * found; /*!< Bit field of files found in the archive */ +@@ -303,6 +306,17 @@ rpm_count_t rpmfiDC(rpmfi fi) + } + #endif + ++int rpmfiSetOnChdir(rpmfi fi, rpmfiChdirCb cb, void *data) ++{ ++ int rc = -1; ++ if (fi != NULL) { ++ fi->onChdir = cb; ++ fi->onChdirData = data; ++ rc = 0; ++ } ++ return rc; ++} ++ + int rpmfiFX(rpmfi fi) + { + return (fi != NULL ? fi->i : -1); +@@ -313,9 +327,17 @@ int rpmfiSetFX(rpmfi fi, int fx) + int i = -1; + + if (fi != NULL && fx >= 0 && fx < rpmfilesFC(fi->files)) { ++ int dx = fi->j; + i = fi->i; + fi->i = fx; + fi->j = rpmfilesDI(fi->files, fi->i); ++ i = fi->i; ++ ++ if (fi->j != dx && fi->onChdir) { ++ int chrc = fi->onChdir(fi, fi->onChdirData); ++ if (chrc < 0) ++ i = chrc; ++ } + } + return i; + } +@@ -1780,9 +1802,9 @@ static rpmfi initIter(rpmfiles files, int itype, int link) + if (files && itype>=0 && itype<=RPMFILEITERMAX) { + fi = xcalloc(1, sizeof(*fi)); + fi->i = -1; ++ fi->j = -1; + fi->files = link ? rpmfilesLink(files) : files; + fi->next = nextfuncs[itype]; +- fi->i = -1; + if (itype == RPMFI_ITER_BACK) { + fi->i = rpmfilesFC(fi->files); + } else if (itype >=RPMFI_ITER_READ_ARCHIVE +diff --git a/lib/rpmfi_internal.h b/lib/rpmfi_internal.h +index dccc6ccbe..37f1d45f5 100644 +--- a/lib/rpmfi_internal.h ++++ b/lib/rpmfi_internal.h +@@ -13,6 +13,23 @@ + extern "C" { + #endif + ++/** \ingroup rpmfi ++ * Callback on file iterator directory changes ++ * @param fi file info ++ * @param data caller private callback data ++ * @return 0 on success, < 0 on error (to stop iteration) ++ */ ++typedef int (*rpmfiChdirCb)(rpmfi fi, void *data); ++ ++/** \ingroup rpmfi ++ * Set a callback for directory changes during iteration. ++ * @param fi file info ++ * @param cb callback function ++ * @param data caller private callback data ++ * @return string pool handle (weak reference) ++ */ ++int rpmfiSetOnChdir(rpmfi fi, rpmfiChdirCb cb, void *data); ++ + /** \ingroup rpmfi + * Return file info set string pool handle + * @param fi file info +-- +2.27.0 + diff --git a/backport-Bury-rpmio-FD-use-to-fsmUnpack.patch b/backport-Bury-rpmio-FD-use-to-fsmUnpack.patch new file mode 100644 index 0000000..21f229e --- /dev/null +++ b/backport-Bury-rpmio-FD-use-to-fsmUnpack.patch @@ -0,0 +1,132 @@ +From bbc270d78fb361bd78eac9a9117070caeb537d4a Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Mon, 14 Feb 2022 12:35:58 +0200 +Subject: [PATCH] Bury rpmio FD use to fsmUnpack() + +Conflict:adapt context +Reference:https://github.com/rpm-software-management/rpm/commit/bbc270d78fb361bd78eac9a9117070caeb537d4a + +fsmUnpack() is the only place in FSM that needs to deal with rpmio FD +types, everywhere else they are nothing but a hindrance that need to +be converted to OS level descriptors for use. Better deal with OS +level descriptors to begin with. +--- + lib/fsm.c | 37 ++++++++++++++++--------------------- + 1 file changed, 16 insertions(+), 21 deletions(-) + +diff --git a/lib/fsm.c b/lib/fsm.c +index 13b114220..b019f5711 100644 +--- a/lib/fsm.c ++++ b/lib/fsm.c +@@ -110,14 +110,14 @@ static int fsmSetFCaps(const char *path, const char *captxt) + return rc; + } + +-static int fsmClose(FD_t *wfdp) ++static int fsmClose(int *wfdp) + { + int rc = 0; +- if (wfdp && *wfdp) { ++ if (wfdp && *wfdp >= 0) { + int myerrno = errno; + static int oneshot = 0; + static int flush_io = 0; +- int fdno = Fileno(*wfdp); ++ int fdno = *wfdp; + + if (!oneshot) { + flush_io = rpmExpandNumeric("%{?_flush_io}"); +@@ -126,61 +126,56 @@ static int fsmClose(FD_t *wfdp) + if (flush_io) { + fsync(fdno); + } +- if (Fclose(*wfdp)) ++ if (close(fdno)) + rc = RPMERR_CLOSE_FAILED; + + if (_fsm_debug) { + rpmlog(RPMLOG_DEBUG, " %8s ([%d]) %s\n", __func__, + fdno, (rc < 0 ? strerror(errno) : "")); + } +- *wfdp = NULL; ++ *wfdp = -1; + errno = myerrno; + } + return rc; + } + +-static int fsmOpen(FD_t *wfdp, int dirfd, const char *dest) ++static int fsmOpen(int *wfdp, int dirfd, const char *dest) + { + int rc = 0; + /* Create the file with 0200 permissions (write by owner). */ + int fd = openat(dirfd, dest, O_WRONLY|O_EXCL|O_CREAT, 0200); + +- if (fd >= 0) { +- *wfdp = fdDup(fd); +- close(fd); +- } +- +- if (fd < 0 || Ferror(*wfdp)) ++ if (fd < 0) + rc = RPMERR_OPEN_FAILED; + + if (_fsm_debug) { + rpmlog(RPMLOG_DEBUG, " %8s (%s [%d]) %s\n", __func__, +- dest, Fileno(*wfdp), (rc < 0 ? strerror(errno) : "")); ++ dest, fd, (rc < 0 ? strerror(errno) : "")); + } +- +- if (rc) +- fsmClose(wfdp); ++ *wfdp = fd; + + return rc; + } + +-static int fsmUnpack(rpmfi fi, FD_t fd, rpmpsm psm, int nodigest) ++static int fsmUnpack(rpmfi fi, int fdno, rpmpsm psm, int nodigest) + { ++ FD_t fd = fdDup(fdno); + int rc = rpmfiArchiveReadToFilePsm(fi, fd, nodigest, psm); + if (_fsm_debug) { + rpmlog(RPMLOG_DEBUG, " %8s (%s %lu bytes [%d]) %s\n", __func__, + rpmfiFN(fi), rpmfiFSize(fi), Fileno(fd), + (rc < 0 ? strerror(errno) : "")); + } ++ Fclose(fd); + return rc; + } + + static int fsmMkfile(int dirfd, rpmfi fi, struct filedata_s *fp, rpmfiles files, + rpmpsm psm, int nodigest, +- struct filedata_s ** firstlink, FD_t *firstlinkfile) ++ struct filedata_s ** firstlink, int *firstlinkfile) + { + int rc = 0; +- FD_t fd = NULL; ++ int fd = -1; + + if (*firstlink == NULL) { + /* First encounter, open file for writing */ +@@ -206,7 +201,7 @@ static int fsmMkfile(int dirfd, rpmfi fi, struct filedata_s *fp, rpmfiles files, + if (*firstlink) { + fp->setmeta = 1; + *firstlink = NULL; +- *firstlinkfile = NULL; ++ *firstlinkfile = -1; + } + } + +@@ -811,7 +806,7 @@ 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; +- FD_t firstlinkfile = NULL; ++ int firstlinkfile = -1; + char *tid = NULL; + struct filedata_s *fdata = xcalloc(fc, sizeof(*fdata)); + struct filedata_s *firstlink = NULL; +-- +2.27.0 + diff --git a/backport-Clean-up-file-unpack-iteration-logic-a-bit.patch b/backport-Clean-up-file-unpack-iteration-logic-a-bit.patch new file mode 100644 index 0000000..f7a76a5 --- /dev/null +++ b/backport-Clean-up-file-unpack-iteration-logic-a-bit.patch @@ -0,0 +1,70 @@ +From 073bcf7ff6d580e5bafee0ef762cf7ce71bc486d Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Mon, 8 Feb 2021 10:45:59 +0200 +Subject: [PATCH] Clean up file unpack iteration logic a bit + +Conflict:NA +Reference:https://github.com/rpm-software-management/rpm/commit/073bcf7ff6d580e5bafee0ef762cf7ce71bc486d + +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 35dcda081..7c291adb0 100644 +--- a/lib/fsm.c ++++ b/lib/fsm.c +@@ -841,6 +841,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; +@@ -862,17 +863,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; +@@ -896,7 +888,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + if (rc) { + skip = 1; + } else { +- setFileState(fs, rpmfiFX(fi)); ++ setFileState(fs, fx); + } + + if (!skip) { +@@ -1005,6 +997,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.27.0 + diff --git a/backport-Consolidate-skipped-hardlink-with-content-case-with-.patch b/backport-Consolidate-skipped-hardlink-with-content-case-with-.patch new file mode 100644 index 0000000..e9ab300 --- /dev/null +++ b/backport-Consolidate-skipped-hardlink-with-content-case-with-.patch @@ -0,0 +1,59 @@ +From cc22fc694d30a64862f0b16d137deaab5416382d Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Fri, 11 Feb 2022 13:05:45 +0200 +Subject: [PATCH] Consolidate skipped hardlink with content case with the + others + +Conflict:NA +Reference:https://github.com/rpm-software-management/rpm/commit/cc22fc694d30a64862f0b16d137deaab5416382d + +Handling this in a separate clause makes the logic much clearer and +(in theory at least) lets us handle hardlinks to any content, not +just regular files. +--- + lib/fsm.c | 20 ++++++++++---------- + 1 file changed, 10 insertions(+), 10 deletions(-) + +diff --git a/lib/fsm.c b/lib/fsm.c +index ec6ee2c36..82610c77d 100644 +--- a/lib/fsm.c ++++ b/lib/fsm.c +@@ -832,9 +832,18 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + while (!rc && (fx = rpmfiNext(fi)) >= 0) { + struct filedata_s *fp = &fdata[fx]; + ++ /* ++ * Tricksy case: this file is a being skipped, but it's part of ++ * a hardlinked set and has the actual content linked with it. ++ * Write the content to the first non-skipped file of the set ++ * instead. ++ */ ++ if (fp->skip && firstlink && rpmfiArchiveHasContent(fi)) ++ fp = firstlink; ++ + if (!fp->skip) { + /* Directories replacing something need early backup */ +- if (!fp->suffix) { ++ if (!fp->suffix && fp != firstlink) { + rc = fsmBackup(fi, fp->action); + } + +@@ -904,15 +913,6 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + if (!IS_DEV_LOG(fp->fpath)) + rc = RPMERR_UNKNOWN_FILETYPE; + } +- } else if (firstlink && rpmfiArchiveHasContent(fi)) { +- /* +- * Tricksy case: this file is a being skipped, but it's part of +- * a hardlinked set and has the actual content linked with it. +- * Write the content to the first non-skipped file of the set +- * instead. +- */ +- rc = fsmMkfile(fi, firstlink, files, psm, nodigest, +- &firstlink, &firstlinkfile); + } + + /* Notify on success. */ +-- +2.27.0 + diff --git a/backport-Convert-the-file-creation-steps-the-at-family-of-cal.patch b/backport-Convert-the-file-creation-steps-the-at-family-of-cal.patch new file mode 100644 index 0000000..59b2531 --- /dev/null +++ b/backport-Convert-the-file-creation-steps-the-at-family-of-cal.patch @@ -0,0 +1,192 @@ +From b599e28112ce5cee98b9ffa7bd96886ec5155e9c Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Fri, 11 Feb 2022 15:35:16 +0200 +Subject: [PATCH] Convert the file creation steps the *at() family of calls + +Conflict:adapt context +Reference:https://github.com/rpm-software-management/rpm/commit/b599e28112ce5cee98b9ffa7bd96886ec5155e9c + +Supposedly no functional changes here, we just need all these things +converted before we can swap over to relative paths. +--- + configure.ac | 2 +- + lib/fsm.c | 59 ++++++++++++++++++++++++++-------------------------- + 2 files changed, 31 insertions(+), 30 deletions(-) + +diff --git a/configure.ac b/configure.ac +index 0099e5f34..ac9003768 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -581,7 +581,7 @@ AC_CHECK_FUNCS([secure_getenv __secure_getenv]) + AC_CHECK_FUNCS( + [mkstemp getcwd basename dirname realpath setenv unsetenv regcomp lchown \ + utimes getline \ +- openat mkdirat fstatat ], ++ openat mkdirat fstatat linkat symlinkat mkfifoat mknodat ], + [], [AC_MSG_ERROR([function required by rpm])]) + + AC_LIBOBJ(fnmatch) +diff --git a/lib/fsm.c b/lib/fsm.c +index ae1bd3f48..8443954f2 100644 +--- a/lib/fsm.c ++++ b/lib/fsm.c +@@ -77,13 +77,13 @@ const char * dnlNextIterator(DNLI_t dnli) + return dn; + } + +-static int fsmLink(const char *opath, const char *path) ++static int fsmLink(int odirfd, const char *opath, int dirfd, const char *path) + { +- int rc = link(opath, path); ++ int rc = linkat(odirfd, opath, dirfd, path, 0); + + if (_fsm_debug) { +- rpmlog(RPMLOG_DEBUG, " %8s (%s, %s) %s\n", __func__, +- opath, path, (rc < 0 ? strerror(errno) : "")); ++ rpmlog(RPMLOG_DEBUG, " %8s (%d %s, %d %s) %s\n", __func__, ++ odirfd, opath, dirfd, path, (rc < 0 ? strerror(errno) : "")); + } + + if (rc < 0) +@@ -139,17 +139,18 @@ static int fsmClose(FD_t *wfdp) + return rc; + } + +-static int fsmOpen(FD_t *wfdp, const char *dest) ++static int fsmOpen(FD_t *wfdp, int dirfd, const char *dest) + { + int rc = 0; + /* Create the file with 0200 permissions (write by owner). */ +- { +- mode_t old_umask = umask(0577); +- *wfdp = Fopen(dest, "wx.ufdio"); +- umask(old_umask); ++ int fd = openat(dirfd, dest, O_WRONLY|O_EXCL|O_CREAT, 0200); ++ ++ if (fd >= 0) { ++ *wfdp = fdDup(fd); ++ close(fd); + } + +- if (Ferror(*wfdp)) ++ if (fd < 0 || Ferror(*wfdp)) + rc = RPMERR_OPEN_FAILED; + + if (_fsm_debug) { +@@ -174,7 +175,7 @@ static int fsmUnpack(rpmfi fi, FD_t fd, rpmpsm psm, int nodigest) + return rc; + } + +-static int fsmMkfile(rpmfi fi, struct filedata_s *fp, rpmfiles files, ++static int fsmMkfile(int dirfd, rpmfi fi, struct filedata_s *fp, rpmfiles files, + rpmpsm psm, int nodigest, + struct filedata_s ** firstlink, FD_t *firstlinkfile) + { +@@ -183,7 +184,7 @@ static int fsmMkfile(rpmfi fi, struct filedata_s *fp, rpmfiles files, + + if (*firstlink == NULL) { + /* First encounter, open file for writing */ +- rc = fsmOpen(&fd, fp->fpath); ++ rc = fsmOpen(&fd, dirfd, fp->fpath); + /* If it's a part of a hardlinked set, the content may come later */ + if (fp->sb.st_nlink > 1) { + *firstlink = fp; +@@ -192,7 +193,7 @@ static int fsmMkfile(rpmfi fi, struct filedata_s *fp, rpmfiles files, + } else { + /* Create hard links for others and avoid redundant metadata setting */ + if (*firstlink != fp) { +- rc = fsmLink((*firstlink)->fpath, fp->fpath); ++ rc = fsmLink(dirfd, (*firstlink)->fpath, dirfd, fp->fpath); + } + fd = *firstlinkfile; + } +@@ -382,13 +383,13 @@ static int ensureDir(rpmPlugins plugins, const char *p, int owned, int create) + return dirfd; + } + +-static int fsmMkfifo(const char *path, mode_t mode) ++static int fsmMkfifo(int dirfd, const char *path, mode_t mode) + { +- int rc = mkfifo(path, (mode & 07777)); ++ int rc = mkfifoat(dirfd, path, (mode & 07777)); + + if (_fsm_debug) { +- rpmlog(RPMLOG_DEBUG, " %8s (%s, 0%04o) %s\n", +- __func__, path, (unsigned)(mode & 07777), ++ rpmlog(RPMLOG_DEBUG, " %8s (%d %s, 0%04o) %s\n", ++ __func__, dirfd, path, (unsigned)(mode & 07777), + (rc < 0 ? strerror(errno) : "")); + } + +@@ -398,14 +399,14 @@ static int fsmMkfifo(const char *path, mode_t mode) + return rc; + } + +-static int fsmMknod(const char *path, mode_t mode, dev_t dev) ++static int fsmMknod(int dirfd, const char *path, mode_t mode, dev_t dev) + { + /* FIX: check S_IFIFO or dev != 0 */ +- int rc = mknod(path, (mode & ~07777), dev); ++ int rc = mknodat(dirfd, path, (mode & ~07777), dev); + + if (_fsm_debug) { +- rpmlog(RPMLOG_DEBUG, " %8s (%s, 0%o, 0x%x) %s\n", +- __func__, path, (unsigned)(mode & ~07777), ++ rpmlog(RPMLOG_DEBUG, " %8s (%d %s, 0%o, 0x%x) %s\n", ++ __func__, dirfd, path, (unsigned)(mode & ~07777), + (unsigned)dev, (rc < 0 ? strerror(errno) : "")); + } + +@@ -440,13 +441,13 @@ static void fsmDebug(const char *fpath, rpmFileAction action, + (fpath ? fpath : "")); + } + +-static int fsmSymlink(const char *opath, const char *path) ++static int fsmSymlink(const char *opath, int dirfd, const char *path) + { +- int rc = symlink(opath, path); ++ int rc = symlinkat(opath, dirfd, path); + + if (_fsm_debug) { +- rpmlog(RPMLOG_DEBUG, " %8s (%s, %s) %s\n", __func__, +- opath, path, (rc < 0 ? strerror(errno) : "")); ++ rpmlog(RPMLOG_DEBUG, " %8s (%s, %d %s) %s\n", __func__, ++ opath, dirfd, path, (rc < 0 ? strerror(errno) : "")); + } + + if (rc < 0) +@@ -884,7 +885,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, ++ rc = fsmMkfile(di.dirfd, fi, fp, files, psm, nodigest, + &firstlink, &firstlinkfile); + } + } else if (S_ISDIR(fp->sb.st_mode)) { +@@ -896,19 +897,19 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + } + } else if (S_ISLNK(fp->sb.st_mode)) { + if (rc == RPMERR_ENOENT) { +- rc = fsmSymlink(rpmfiFLink(fi), fp->fpath); ++ rc = fsmSymlink(rpmfiFLink(fi), di.dirfd, fp->fpath); + } + } 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(fp->fpath, 0000); ++ rc = fsmMkfifo(di.dirfd, fp->fpath, 0000); + } + } 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(fp->fpath, fp->sb.st_mode, fp->sb.st_rdev); ++ rc = fsmMknod(di.dirfd, fp->fpath, fp->sb.st_mode, fp->sb.st_rdev); + } + } else { + /* XXX Special case /dev/log, which shouldn't be packaged anyways */ +-- +2.27.0 + diff --git a/backport-Drop-unused-filename-variable.patch b/backport-Drop-unused-filename-variable.patch new file mode 100644 index 0000000..8caed2b --- /dev/null +++ b/backport-Drop-unused-filename-variable.patch @@ -0,0 +1,31 @@ +From 8a9147f7d182e1a5aa55ac019c32a4ba765fae62 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Wed, 10 Feb 2021 08:25:28 +0200 +Subject: [PATCH] Drop unused filename variable + +Conflict:NA +Reference:https://github.com/rpm-software-management/rpm/commit/8a9147f7d182e1a5aa55ac019c32a4ba765fae62 + +--- + lib/fsm.c | 2 -- + 1 file changed, 2 deletions(-) + +diff --git a/lib/fsm.c b/lib/fsm.c +index 7c291adb0..41b6267dd 100644 +--- a/lib/fsm.c ++++ b/lib/fsm.c +@@ -959,11 +959,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.27.0 + diff --git a/backport-Fix-sanitize-the-hardlink-metadata-setting-logic.patch b/backport-Fix-sanitize-the-hardlink-metadata-setting-logic.patch new file mode 100644 index 0000000..2216ddc --- /dev/null +++ b/backport-Fix-sanitize-the-hardlink-metadata-setting-logic.patch @@ -0,0 +1,70 @@ +From dce44771b2a3325b3dc1ebfe41027df9910a39fd Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Fri, 11 Feb 2022 13:18:11 +0200 +Subject: [PATCH] Fix + sanitize the hardlink metadata setting logic + +Conflict:adapt context +Reference:https://github.com/rpm-software-management/rpm/commit/dce44771b2a3325b3dc1ebfe41027df9910a39fd + +Fix the initial setmeta value to something meaningful: we will never +set metadata on skipped files, and hardlinks are handled with a special +logic during install. They'd need different kind of special logic on +FA_TOUCH so just play it safe and always apply metadata on those. + +Harlink metadata setting on install should happen on the *last* entry +of hardlinked set that gets installed (wrt various skip scenarios) +as otherwise creating those additional links affects the timestamp. +Note in particular the "last file of..." case in fsmMkfile() where we +the comment said just that, but set the metadata on the *first* file +which would then be NULL'ed away. + +This all gets current masked by the fact that we do the metadata setting on +a separate round, but that is about to change plus this makes the overall +logic clearer anyhow. +--- + lib/fsm.c | 8 +++++--- + 1 file changed, 5 insertions(+), 3 deletions(-) + +diff --git a/lib/fsm.c b/lib/fsm.c +index 82610c77d..d9cfe6fa9 100644 +--- a/lib/fsm.c ++++ b/lib/fsm.c +@@ -193,7 +193,6 @@ static int fsmMkfile(rpmfi fi, struct filedata_s *fp, rpmfiles files, + /* Create hard links for others and avoid redundant metadata setting */ + if (*firstlink != fp) { + rc = fsmLink((*firstlink)->fpath, fp->fpath); +- fp->setmeta = 0; + } + fd = *firstlinkfile; + } +@@ -204,7 +203,7 @@ static int fsmMkfile(rpmfi fi, struct filedata_s *fp, rpmfiles files, + rc = fsmUnpack(fi, fd, psm, nodigest); + /* Last file of hardlink set, ensure metadata gets set */ + if (*firstlink) { +- (*firstlink)->setmeta = 1; ++ fp->setmeta = 1; + *firstlink = NULL; + *firstlinkfile = NULL; + } +@@ -797,7 +796,6 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + else + 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 { +@@ -805,6 +803,10 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + /* Remap file perms, owner, and group. */ + rc = rpmfiStat(fi, 1, &fp->sb); + ++ /* Hardlinks are tricky and handled elsewhere for install */ ++ fp->setmeta = (fp->skip == 0) && ++ (fp->sb.st_nlink == 1 || fp->action == FA_TOUCH); ++ + setFileState(fs, fx); + fsmDebug(fp->fpath, fp->action, &fp->sb); + +-- +2.27.0 + diff --git a/backport-Handle-file-install-failures-more-gracefully.patch b/backport-Handle-file-install-failures-more-gracefully.patch new file mode 100644 index 0000000..173c274 --- /dev/null +++ b/backport-Handle-file-install-failures-more-gracefully.patch @@ -0,0 +1,261 @@ +From a82251b44ee2d2802ee8aea1b3d89f88beee4bad Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Wed, 10 Feb 2021 14:15:33 +0200 +Subject: [PATCH] Handle file install failures more gracefully + +Conflict:adapt context; delete testcode +Reference:https://github.com/rpm-software-management/rpm/commit/a82251b44ee2d2802ee8aea1b3d89f88beee4bad +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 ++++++++++++++++++++------------ + 1 files changed, 93 insertions(+), 54 deletions(-) + +diff --git a/lib/fsm.c b/lib/fsm.c +index f86383a98..6efd25bdd 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; +@@ -844,10 +854,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); +@@ -858,20 +867,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 (fp->action != FA_TOUCH) { +@@ -881,20 +887,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 */ +@@ -918,7 +936,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) { +@@ -954,12 +972,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 */ +@@ -969,47 +981,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++) +-- +2.27.0 + diff --git a/backport-Handle-hardlink-tracking-with-a-file-state-pointer.patch b/backport-Handle-hardlink-tracking-with-a-file-state-pointer.patch new file mode 100644 index 0000000..5d6d81b --- /dev/null +++ b/backport-Handle-hardlink-tracking-with-a-file-state-pointer.patch @@ -0,0 +1,96 @@ +From 1c23cff7296cec56379ee41077145c29d6841a12 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Wed, 10 Feb 2021 11:25:10 +0200 +Subject: [PATCH] Handle hardlink tracking with a file state pointer + +Conflict:NA +Reference:https://github.com/rpm-software-management/rpm/commit/1c23cff7296cec56379ee41077145c29d6841a12 + +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 094f5e2bb..f86383a98 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; + } +@@ -855,10 +853,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; +@@ -925,7 +923,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) { +@@ -962,13 +960,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.27.0 + diff --git a/backport-Make-file-open-and-close-in-fsm-debuggable.patch b/backport-Make-file-open-and-close-in-fsm-debuggable.patch new file mode 100644 index 0000000..66aed34 --- /dev/null +++ b/backport-Make-file-open-and-close-in-fsm-debuggable.patch @@ -0,0 +1,148 @@ +From 2efdc55e15b21a80dcc414aa95e433c3adf4516c Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Thu, 11 Feb 2021 10:36:28 +0200 +Subject: [PATCH] Make file open and close in fsm debuggable + +Conflict:adapt context +Reference:https://github.com/rpm-software-management/rpm/commit/2efdc55e15b21a80dcc414aa95e433c3adf4516c + +Add debugging facilities to file open and close routines in fsm, rename +wfd_open/close to fsmOpen/Close to bring the messages in line with +our other stuff. + +Slight reorganization needed for the error paths, in particular in +case of file close we haven't even been recording the close result +with can be critical when writing files. Add an error code for close +failure and return it from fsmClose(), but updating the calling code +to take this into account is out of scope in this commit. +--- + lib/fsm.c | 40 ++++++++++++++++++++++++++-------------- + lib/rpmarchive.h | 1 + + 2 files changed, 27 insertions(+), 14 deletions(-) + +diff --git a/lib/fsm.c b/lib/fsm.c +index f8038e09b..ffafa5d68 100644 +--- a/lib/fsm.c ++++ b/lib/fsm.c +@@ -244,27 +244,36 @@ static int fsmSetFCaps(const char *path, const char *captxt) + return rc; + } + +-static void wfd_close(FD_t *wfdp) ++static int fsmClose(FD_t *wfdp) + { ++ int rc = 0; + if (wfdp && *wfdp) { + int myerrno = errno; + static int oneshot = 0; + static int flush_io = 0; ++ int fdno = Fileno(*wfdp); ++ + if (!oneshot) { + flush_io = rpmExpandNumeric("%{?_flush_io}"); + oneshot = 1; + } + if (flush_io) { +- int fdno = Fileno(*wfdp); + fsync(fdno); + } +- Fclose(*wfdp); ++ if (Fclose(*wfdp)) ++ rc = RPMERR_CLOSE_FAILED; ++ ++ if (_fsm_debug) { ++ rpmlog(RPMLOG_DEBUG, " %8s ([%d]) %s\n", __func__, ++ fdno, (rc < 0 ? strerror(errno) : "")); ++ } + *wfdp = NULL; + errno = myerrno; + } ++ return rc; + } + +-static int wfd_open(FD_t *wfdp, const char *dest) ++static int fsmOpen(FD_t *wfdp, const char *dest) + { + int rc = 0; + /* Create the file with 0200 permissions (write by owner). */ +@@ -273,15 +282,18 @@ static int wfd_open(FD_t *wfdp, const char *dest) + *wfdp = Fopen(dest, "wx.ufdio"); + umask(old_umask); + } +- if (Ferror(*wfdp)) { ++ ++ if (Ferror(*wfdp)) + rc = RPMERR_OPEN_FAILED; +- goto exit; ++ ++ if (_fsm_debug) { ++ rpmlog(RPMLOG_DEBUG, " %8s (%s [%d]) %s\n", __func__, ++ dest, Fileno(*wfdp), (rc < 0 ? strerror(errno) : "")); + } + +- return 0; ++ if (rc) ++ fsmClose(wfdp); + +-exit: +- wfd_close(wfdp); + return rc; + } + +@@ -294,12 +306,12 @@ static int expandRegular(rpmfi fi, const char *dest, rpmpsm psm, int nodigest) + FD_t wfd = NULL; + int rc; + +- rc = wfd_open(&wfd, dest); ++ rc = fsmOpen(&wfd, dest); + if (rc != 0) + goto exit; + + rc = rpmfiArchiveReadToFilePsm(fi, wfd, nodigest, psm); +- wfd_close(&wfd); ++ fsmClose(&wfd); + exit: + return rc; + } +@@ -315,7 +327,7 @@ static int fsmMkfile(rpmfi fi, struct filedata_s *fp, rpmfiles files, + /* Create first hardlinked file empty */ + if (*firstlink == NULL) { + *firstlink = fp; +- rc = wfd_open(firstlinkfile, fp->fpath); ++ rc = fsmOpen(firstlinkfile, fp->fpath); + } else { + /* Create hard links for others */ + rc = fsmLink((*firstlink)->fpath, fp->fpath); +@@ -329,7 +341,7 @@ static int fsmMkfile(rpmfi fi, struct filedata_s *fp, rpmfiles files, + } else if (rpmfiArchiveHasContent(fi)) { + if (!rc) + rc = rpmfiArchiveReadToFilePsm(fi, *firstlinkfile, nodigest, psm); +- wfd_close(firstlinkfile); ++ fsmClose(firstlinkfile); + *firstlink = NULL; + } else { + fp->setmeta = 0; +@@ -988,7 +1000,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + /* 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); ++ fsmClose(&firstlinkfile); + firstlink = NULL; + } + +diff --git a/lib/rpmarchive.h b/lib/rpmarchive.h +index 2484b4d71..ec6366c86 100644 +--- a/lib/rpmarchive.h ++++ b/lib/rpmarchive.h +@@ -47,6 +47,7 @@ enum rpmfilesErrorCodes { + RPMERR_COPY_FAILED = -32785, + RPMERR_LSETFCON_FAILED = -32786, + RPMERR_SETCAP_FAILED = -32787, ++ RPMERR_CLOSE_FAILED = -32788, + }; + + #ifdef __cplusplus +-- +2.27.0 + diff --git a/backport-Move-file-metadata-setting-back-to-unpack-stage.patch b/backport-Move-file-metadata-setting-back-to-unpack-stage.patch new file mode 100644 index 0000000..1610b6f --- /dev/null +++ b/backport-Move-file-metadata-setting-back-to-unpack-stage.patch @@ -0,0 +1,72 @@ +From da79e3c3ae7da8719f0bf87a1a60e046597b8240 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Fri, 11 Feb 2022 13:28:25 +0200 +Subject: [PATCH] Move file metadata setting back to unpack stage + +Conflict:NA +Reference:https://github.com/rpm-software-management/rpm/commit/da79e3c3ae7da8719f0bf87a1a60e046597b8240 + +Commit a82251b44ee2d2802ee8aea1b3d89f88beee4bad moved metadata setting +to a separate step because there are potential benefits to doing so, but +the current downsides are worse: as long as we operate in potentially +untrusted directories, we'd need to somehow verify the content is what we +initially laid down to avoid possible privilege escalation from non-root +owned directories. + +This commit does not fix that vulnerability, only makes the window much +smaller and paves the way for the real fix(es) without introducing a +second round of directory tree validation chase to the picture. +--- + lib/fsm.c | 22 +++++++--------------- + 1 file changed, 7 insertions(+), 15 deletions(-) + +diff --git a/lib/fsm.c b/lib/fsm.c +index d9cfe6fa9..ae1bd3f48 100644 +--- a/lib/fsm.c ++++ b/lib/fsm.c +@@ -880,7 +880,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + + /* When touching we don't need any of this... */ + if (fp->action == FA_TOUCH) +- continue; ++ goto setmeta; + + if (S_ISREG(fp->sb.st_mode)) { + if (rc == RPMERR_ENOENT) { +@@ -915,6 +915,12 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + if (!IS_DEV_LOG(fp->fpath)) + rc = RPMERR_UNKNOWN_FILETYPE; + } ++ ++setmeta: ++ if (!rc && fp->setmeta) { ++ rc = fsmSetmeta(fp->fpath, fi, plugins, fp->action, ++ &fp->sb, nofcaps); ++ } + } + + /* Notify on success. */ +@@ -931,20 +937,6 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + if (!rc && fx < 0 && fx != RPMERR_ITER_END) + rc = fx; + +- /* 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); +- + /* If all went well, commit files to final destination */ + fi = rpmfilesIter(files, RPMFI_ITER_FWD); + while (!rc && (fx = rpmfiNext(fi)) >= 0) { +-- +2.27.0 + diff --git a/backport-Refactor-file-install-and-remove-around-a-common-str.patch b/backport-Refactor-file-install-and-remove-around-a-common-str.patch new file mode 100644 index 0000000..e292eb4 --- /dev/null +++ b/backport-Refactor-file-install-and-remove-around-a-common-str.patch @@ -0,0 +1,340 @@ +From 924cb31c53dcf60ff0cce650851dba354f383f15 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Wed, 10 Feb 2021 09:47:19 +0200 +Subject: [PATCH] Refactor file install and remove around a common struct + +Conflict:NA +Reference:https://github.com/rpm-software-management/rpm/commit/924cb31c53dcf60ff0cce650851dba354f383f15 + +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 c581a918a..9dba30560 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 +@@ -840,19 +848,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; +@@ -866,96 +871,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. */ +@@ -967,10 +975,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; + } +@@ -978,23 +986,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) +@@ -1010,7 +1017,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; + } +@@ -1022,29 +1031,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. +@@ -1069,20 +1080,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. */ +@@ -1090,10 +1101,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.27.0 + diff --git a/backport-Refactor-fsmMkfile-to-take-advantage-of-the-new-stat.patch b/backport-Refactor-fsmMkfile-to-take-advantage-of-the-new-stat.patch new file mode 100644 index 0000000..4aff8c6 --- /dev/null +++ b/backport-Refactor-fsmMkfile-to-take-advantage-of-the-new-stat.patch @@ -0,0 +1,112 @@ +From 210431f18338dfa2d579ea86776cd426a72bf971 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Wed, 10 Feb 2021 10:08:27 +0200 +Subject: [PATCH] Refactor fsmMkfile() to take advantage of the new state + struct + +Conflict:NA +Reference:https://github.com/rpm-software-management/rpm/commit/210431f18338dfa2d579ea86776cd426a72bf971 + +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 9dba30560..80ca234b1 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; +@@ -874,6 +875,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 { +@@ -900,8 +902,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); +@@ -927,8 +927,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) { +@@ -961,7 +961,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.27.0 + diff --git a/backport-Return-descriptor-of-created-file-from-fsmMkfile.patch b/backport-Return-descriptor-of-created-file-from-fsmMkfile.patch new file mode 100644 index 0000000..3d37445 --- /dev/null +++ b/backport-Return-descriptor-of-created-file-from-fsmMkfile.patch @@ -0,0 +1,68 @@ +From 693d828c035848585b500dfde6f4e58cfb8d4de4 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Mon, 14 Feb 2022 12:44:42 +0200 +Subject: [PATCH] Return descriptor of created file from fsmMkfile() + +Conflict:adapt context +Reference:https://github.com/rpm-software-management/rpm/commit/693d828c035848585b500dfde6f4e58cfb8d4de4 + +This will be needed for using fd-based metadata operations. +--- + lib/fsm.c | 13 ++++++++----- + 1 file changed, 8 insertions(+), 5 deletions(-) + +diff --git a/lib/fsm.c b/lib/fsm.c +index b019f5711..7c4796f74 100644 +--- a/lib/fsm.c ++++ b/lib/fsm.c +@@ -172,7 +172,8 @@ static int fsmUnpack(rpmfi fi, int fdno, rpmpsm psm, int nodigest) + + static int fsmMkfile(int dirfd, rpmfi fi, struct filedata_s *fp, rpmfiles files, + rpmpsm psm, int nodigest, +- struct filedata_s ** firstlink, int *firstlinkfile) ++ struct filedata_s ** firstlink, int *firstlinkfile, ++ int *fdp) + { + int rc = 0; + int fd = -1; +@@ -204,9 +205,7 @@ static int fsmMkfile(int dirfd, rpmfi fi, struct filedata_s *fp, rpmfiles files, + *firstlinkfile = -1; + } + } +- +- if (fd != *firstlinkfile) +- fsmClose(&fd); ++ *fdp = fd; + + return rc; + } +@@ -867,6 +866,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + fp = firstlink; + + if (!fp->skip) { ++ int fd = -1; + /* Directories replacing something need early backup */ + if (!fp->suffix && fp != firstlink) { + rc = fsmBackup(fi, fp->action); +@@ -910,7 +910,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + if (S_ISREG(fp->sb.st_mode)) { + if (rc == RPMERR_ENOENT) { + rc = fsmMkfile(di.dirfd, fi, fp, files, psm, nodigest, +- &firstlink, &firstlinkfile); ++ &firstlink, &firstlinkfile, &fd); + } + } else if (S_ISDIR(fp->sb.st_mode)) { + if (rc == RPMERR_ENOENT) { +@@ -946,6 +946,9 @@ setmeta: + rc = fsmSetmeta(fp->fpath, fi, plugins, fp->action, + &fp->sb, nofcaps); + } ++ ++ if (fd != firstlinkfile) ++ fsmClose(&fd); + } + + /* Notify on success. */ +-- +2.27.0 + diff --git a/backport-Streamline-consolidate-the-hardlink-handling-logic.patch b/backport-Streamline-consolidate-the-hardlink-handling-logic.patch new file mode 100644 index 0000000..9bda5e9 --- /dev/null +++ b/backport-Streamline-consolidate-the-hardlink-handling-logic.patch @@ -0,0 +1,135 @@ +From 505699eb9789543a1b7bcaf8bb654d59b9d2623a Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Thu, 11 Feb 2021 14:06:29 +0200 +Subject: [PATCH] Streamline + consolidate the hardlink handling logic + +Conflict:delete testcode +Reference:https://github.com/rpm-software-management/rpm/commit/505699eb9789543a1b7bcaf8bb654d59b9d2623a + +This is one tricksy piece of code, in particular wrt interactions with +skipped files. Streamline the logic to make it easier to follow, and +consolidate as much as possible inside fsmMkfile() so there's only +one place opening, closing and writing to files. Add bunch of tests +for partially skipped sets. + +This actually also fixes a case where metadata does not get properly +set on a partially skipped hardlink file set (the --excludepath=/foo/zzzz +case would fail prior to this) +--- + lib/fsm.c | 80 ++++++++++++++++++++++----------------------------- + 1 files changed, 35 insertions(+), 45 deletions(-) + +diff --git a/lib/fsm.c b/lib/fsm.c +index ffafa5d68..bd2481a50 100644 +--- a/lib/fsm.c ++++ b/lib/fsm.c +@@ -297,56 +297,45 @@ static int fsmOpen(FD_t *wfdp, const char *dest) + return rc; + } + +-/** \ingroup payload +- * Create file from payload stream. +- * @return 0 on success +- */ +-static int expandRegular(rpmfi fi, const char *dest, rpmpsm psm, int nodigest) +-{ +- FD_t wfd = NULL; +- int rc; +- +- rc = fsmOpen(&wfd, dest); +- if (rc != 0) +- goto exit; +- +- rc = rpmfiArchiveReadToFilePsm(fi, wfd, nodigest, psm); +- fsmClose(&wfd); +-exit: +- return rc; +-} +- + static int fsmMkfile(rpmfi fi, struct filedata_s *fp, rpmfiles files, + rpmpsm psm, int nodigest, + struct filedata_s ** firstlink, FD_t *firstlinkfile) + { + int rc = 0; +- int numHardlinks = rpmfiFNlink(fi); ++ FD_t fd = NULL; + +- if (numHardlinks > 1) { +- /* Create first hardlinked file empty */ +- if (*firstlink == NULL) { ++ if (*firstlink == NULL) { ++ /* First encounter, open file for writing */ ++ rc = fsmOpen(&fd, fp->fpath); ++ /* If it's a part of a hardlinked set, the content may come later */ ++ if (fp->sb.st_nlink > 1) { + *firstlink = fp; +- rc = fsmOpen(firstlinkfile, fp->fpath); +- } else { +- /* Create hard links for others */ ++ *firstlinkfile = fd; ++ } ++ } else { ++ /* Create hard links for others and avoid redundant metadata setting */ ++ if (*firstlink != fp) { + rc = fsmLink((*firstlink)->fpath, fp->fpath); ++ fp->setmeta = 0; + } ++ fd = *firstlinkfile; + } +- /* Write normal files or fill the last hardlinked (already +- existing) file with content */ +- if (numHardlinks<=1) { +- if (!rc) +- rc = expandRegular(fi, fp->fpath, psm, nodigest); +- } else if (rpmfiArchiveHasContent(fi)) { ++ ++ /* If the file has content, unpack it */ ++ if (rpmfiArchiveHasContent(fi)) { + if (!rc) +- rc = rpmfiArchiveReadToFilePsm(fi, *firstlinkfile, nodigest, psm); +- fsmClose(firstlinkfile); +- *firstlink = NULL; +- } else { +- fp->setmeta = 0; ++ rc = rpmfiArchiveReadToFilePsm(fi, fd, nodigest, psm); ++ /* Last file of hardlink set, ensure metadata gets set */ ++ if (*firstlink) { ++ (*firstlink)->setmeta = 1; ++ *firstlink = NULL; ++ *firstlinkfile = NULL; ++ } + } + ++ if (fd != *firstlinkfile) ++ fsmClose(&fd); ++ + return rc; + } + +@@ -994,14 +983,15 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + if (!IS_DEV_LOG(fp->fpath)) + rc = RPMERR_UNKNOWN_FILETYPE; + } +- +- } 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); +- fsmClose(&firstlinkfile); +- firstlink = NULL; ++ } else if (firstlink && rpmfiArchiveHasContent(fi)) { ++ /* ++ * Tricksy case: this file is a being skipped, but it's part of ++ * a hardlinked set and has the actual content linked with it. ++ * Write the content to the first non-skipped file of the set ++ * instead. ++ */ ++ rc = fsmMkfile(fi, firstlink, files, psm, nodigest, ++ &firstlink, &firstlinkfile); + } + + /* Notify on success. */ +-- +2.27.0 + diff --git a/backport-Upgrade-FA_TOUCH-to-FA_CREATE-if-the-file-went-away-.patch b/backport-Upgrade-FA_TOUCH-to-FA_CREATE-if-the-file-went-away-.patch new file mode 100644 index 0000000..c42d567 --- /dev/null +++ b/backport-Upgrade-FA_TOUCH-to-FA_CREATE-if-the-file-went-away-.patch @@ -0,0 +1,66 @@ +From 886c24cfc6c0fec90d8db1406a0e32c0e09e92c9 Mon Sep 17 00:00:00 2001 +From: Panu Matilainen +Date: Thu, 27 Aug 2020 10:31:07 +0300 +Subject: [PATCH] Upgrade FA_TOUCH to FA_CREATE if the file went away + (RhBug:1872141) + +Conflict:delete testcode +Reference:https://github.com/rpm-software-management/rpm/commit/886c24cfc6c0fec90d8db1406a0e32c0e09e92c9 + +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 +++++++++---- + 1 files changed, 11 insertions(+), 4 deletions(-) + +diff --git a/lib/fsm.c b/lib/fsm.c +index 5fb507993..08980d49b 100644 +--- a/lib/fsm.c ++++ b/lib/fsm.c +@@ -902,10 +902,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); +@@ -917,6 +913,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, +-- +2.27.0 + diff --git a/rpm.spec b/rpm.spec index ab79249..0538e61 100644 --- a/rpm.spec +++ b/rpm.spec @@ -1,6 +1,6 @@ Name: rpm Version: 4.15.1 -Release: 36 +Release: 37 Summary: RPM Package Manager License: GPLv2+ URL: http://www.rpm.org/ @@ -93,6 +93,27 @@ Patch79: backport-0003-CVE-2021-3521.patch Patch80: rpm-selinux-plugin-check-context-file-exist.patch Patch81: backport-Use-root-as-default-UID_0_USER-and-UID_0_GROUP.patch +Patch82: backport-Upgrade-FA_TOUCH-to-FA_CREATE-if-the-file-went-away-.patch +Patch83: backport-Clean-up-file-unpack-iteration-logic-a-bit.patch +Patch84: backport-Refactor-file-install-and-remove-around-a-common-str.patch +Patch85: backport-Refactor-fsmMkfile-to-take-advantage-of-the-new-stat.patch +Patch86: backport-Drop-unused-filename-variable.patch +Patch87: backport-Handle-hardlink-tracking-with-a-file-state-pointer.patch +Patch88: backport-Handle-file-install-failures-more-gracefully.patch +Patch89: backport-Add-hardlink-helper-to-fsm-to-make-it-debuggable.patch +Patch90: backport-Make-file-open-and-close-in-fsm-debuggable.patch +Patch91: backport-Streamline-consolidate-the-hardlink-handling-logic.patch +Patch92: backport-Add-diagnostics-to-archive-unpacking.patch +Patch93: backport-Add-optional-callback-on-directory-changes-during-rp.patch +Patch94: backport-0001-CVE-2021-35939-CVE-2021-35937.patch +Patch95: backport-Consolidate-skipped-hardlink-with-content-case-with-.patch +Patch96: backport-Fix-sanitize-the-hardlink-metadata-setting-logic.patch +Patch97: backport-Convert-the-file-creation-steps-the-at-family-of-cal.patch +Patch98: backport-Bury-rpmio-FD-use-to-fsmUnpack.patch +Patch99: backport-Move-file-metadata-setting-back-to-unpack-stage.patch +Patch100: backport-Return-descriptor-of-created-file-from-fsmMkfile.patch +Patch101: backport-0001-CVE-2021-35938.patch + BuildRequires: gcc autoconf automake libtool make gawk popt-devel openssl-devel readline-devel libdb-devel BuildRequires: zlib-devel libzstd-devel xz-devel bzip2-devel libarchive-devel ima-evm-utils-devel BuildRequires: dbus-devel fakechroot elfutils-devel elfutils-libelf-devel ima-evm-utils @@ -375,6 +396,12 @@ make check || (cat tests/rpmtests.log; exit 0) %{_mandir}/man1/gendiff.1* %changelog +* Wed Sep 7 2022 xujing - 4.15.1-37 +- Type:CVE +- ID:NA +- SUG:NA +- DESC:fix CVE-2021-35937,CVE-2021-35938,CVE-2021-35939 + * Tue Dec 14 2021 renhongxun - 4.15.1-36 - Type:bugfix - ID:NA