From ea2e7da5d17da15c0ed1d165993d03c49a1a02be Mon Sep 17 00:00:00 2001 From: chantra Date: Feb 01 2022 23:52:28 +0000 Subject: Re-introduce PR1470 after rebasing on top of 1534. https://github.com/chantra/rpm/compare/master...cow.diff --- diff --git a/SOURCES/1470.patch b/SOURCES/1470.patch deleted file mode 100644 index 569aabc..0000000 --- a/SOURCES/1470.patch +++ /dev/null @@ -1,2659 +0,0 @@ -From 82b454d2ff43b39e1a3b38fded0c2e5caffcd336 Mon Sep 17 00:00:00 2001 -From: Matthew Almond -Date: Fri, 8 Nov 2019 09:29:43 -0800 -Subject: [PATCH 1/4] RPM with Copy on Write - -This is part of https://fedoraproject.org/wiki/Changes/RPMCoW - -The majority of changes are in two new programs: - -= rpm2extents - -Modeled as a 'stream processor'. It reads a regular .rpm file on stdin, -and produces a modified .rpm file on stdout. The lead, signature and -headers are preserved 1:1 to allow all the normal metadata inspection, -signature verification to work as expected. Only the 'payload' is -modified. - -The primary motivation for this tool is to re-organize the payload as a -sequence of raw file extents (hence the name). The files are organized -by their digest identity instead of path/filename. If any digest is -repeated, then the file is skipped/de-duped. Only regular files are -represented. All other entries like directories, symlinks, devices are -fully described in the headers and are omitted. - -The files are padded so they start on `sysconf(_SC_PAGESIZE)` boundries -to permit 'reflink' syscalls to work in the `reflink` plugin. - -At the end of the file is a footer with 3 sections: - -1. List of calculated digests of the input stream. This is used in - `librepo` because the file *written* is a derivative, and not the - same as the repo metadata describes. `rpm2extents` takes one or more - positional arguments that described which digest algorithms are - desired. This is often just `SHA256`. This program is only measuring - and recording the digest - it does not express an opinion on whether - the file is correct. Due to the API on most compression libraries - directly reading the source file, the whole file digest is measured - using a subprocess and pipes. I don't love it, but it works. -2. Sorted List of file content digests + offset pairs. This is used in - the plugin with a trivial binary search to locate the start of file - content. The size is not needed because it's part of normal headers. -3. (offset of 1., offset of 2., 8 byte MAGIC value) triple - -= reflink plugin - -Looks for the 8 byte magic value at the end of the rpm file. If present -it alters the `RPMTAG_PAYLOADFORMAT` in memory to `clon`, and reads in -the digest-> offset table. - -`rpmPackageFilesInstall()` in `fsm.c` is -modified to alter the enumeration strategy from -`rpmfiNewArchiveReader()` to `rpmfilesIter()` if not `cpio`. This is -needed because there is no cpio to enumerate. In the same function, if -`rpmpluginsCallFsmFilePre()` returns `RPMRC_PLUGIN_CONTENTS` then -`fsmMkfile()` is skipped as it is assumed the plugin did the work. - -The majority of the work is in `reflink_fsm_file_pre()` - the per file -hook for RPM plugins. If the file enumerated in -`rpmPackageFilesInstall()` is a regular file, this function will look up -the offset in the digest->offset table and will try to reflink it, then -fall back to a regular copy. If reflinking does work: we will have -reflinked a whole number of pages, so we truncate the file to the -expected size. Therefore installing most files does involve two writes: -the reflink of the full size, then a fork/copy on write for the last -page worth. - -If the file passed to `reflink_fsm_file_pre()` is anything other than a -regular file, it return `RPMRC_OK` so the normal mechanics of -`rpmPackageFilesInstall()` are used. That handles directories, symlinks -and other non file types. - -= New API for internal use - -1. `rpmReadPackageRaw()` is used within `rpm2extents` to read all the - headers without trying to validate signatures. This eliminates the - runtime dependency on rpmdb. -2. `rpmteFd()` exposes the Fd behind the rpmte, so plugins can interact - with the rpm itself. -3. `RPMRC_PLUGIN_CONTENTS` in `rpmRC_e` for use in - `rpmpluginsCallFsmFilePre()` specifically. -4. `pgpStringVal()` is used to help parse the command line in - `rpm2extents` - the positional arguments are strings, and this - converts the values back to the values in the table. - -Nothing has been removed, and none of the changes are intended to be -used externally, so I don't think a soname bump is warranted here. ---- - Makefile.am | 6 +- - lib/depends.c | 2 + - lib/fsm.c | 50 ++++- - lib/package.c | 40 ++++ - lib/rpmlib.h | 9 + - lib/rpmplugins.c | 21 +- - lib/rpmte.c | 5 + - lib/rpmte.h | 2 + - lib/rpmtypes.h | 3 +- - macros.in | 1 + - plugins/Makefile.am | 4 + - plugins/reflink.c | 340 +++++++++++++++++++++++++++++ - rpm2extents.c | 519 ++++++++++++++++++++++++++++++++++++++++++++ - rpmio/rpmpgp.c | 10 + - rpmio/rpmpgp.h | 9 + - 15 files changed, 1006 insertions(+), 15 deletions(-) - create mode 100644 plugins/reflink.c - create mode 100644 rpm2extents.c - -diff --git a/Makefile.am b/Makefile.am -index aca2f8996..17c1e0e8a 100644 ---- a/Makefile.am -+++ b/Makefile.am -@@ -105,7 +105,7 @@ pkginclude_HEADERS += build/rpmfc.h - pkginclude_HEADERS += build/rpmspec.h - - --bin_PROGRAMS = rpm rpm2cpio rpmbuild rpmdb rpmkeys rpmsign rpmspec -+bin_PROGRAMS = rpm rpm2cpio rpmbuild rpmdb rpmkeys rpmsign rpmspec rpm2extents - if WITH_ARCHIVE - bin_PROGRAMS += rpm2archive - endif -@@ -159,6 +159,10 @@ rpm2cpio_SOURCES = rpm2cpio.c debug.h system.h - rpm2cpio_LDADD = lib/librpm.la rpmio/librpmio.la - rpm2cpio_LDADD += @WITH_POPT_LIB@ - -+rpm2extents_SOURCES = rpm2extents.c debug.h system.h -+rpm2extents_LDADD = lib/librpm.la rpmio/librpmio.la -+rpm2extents_LDADD += @WITH_POPT_LIB@ -+ - rpm2archive_SOURCES = rpm2archive.c debug.h system.h - rpm2archive_LDADD = lib/librpm.la rpmio/librpmio.la - rpm2archive_LDADD += @WITH_POPT_LIB@ @WITH_ARCHIVE_LIB@ -diff --git a/lib/depends.c b/lib/depends.c -index 28a4a784d..9e0489bcc 100644 ---- a/lib/depends.c -+++ b/lib/depends.c -@@ -80,6 +80,8 @@ static rpmRC headerCheckPayloadFormat(Header h) { - */ - if (!payloadfmt) return rc; - -+ if (rstreq(payloadfmt, "clon")) return rc; -+ - if (!rstreq(payloadfmt, "cpio")) { - char *nevra = headerGetAsString(h, RPMTAG_NEVRA); - if (payloadfmt && rstreq(payloadfmt, "drpm")) { -diff --git a/lib/fsm.c b/lib/fsm.c -index 35dcda081..03a716474 100644 ---- a/lib/fsm.c -+++ b/lib/fsm.c -@@ -7,6 +7,7 @@ - - #include - #include -+#include - #if WITH_CAP - #include - #endif -@@ -18,6 +19,7 @@ - - #include "rpmio/rpmio_internal.h" /* fdInit/FiniDigest */ - #include "lib/fsm.h" -+#include "lib/rpmlib.h" - #include "lib/rpmte_internal.h" /* XXX rpmfs */ - #include "lib/rpmplugins.h" /* rpm plugins hooks */ - #include "lib/rpmug.h" -@@ -835,7 +837,7 @@ 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; - rpmfs fs = rpmteGetFileStates(te); - rpmPlugins plugins = rpmtsPlugins(ts); - struct stat sb; -@@ -850,10 +852,21 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, - char *tid = NULL; - const char *suffix; - char *fpath = NULL; -+ Header h = rpmteHeader(te); -+ const char *payloadfmt = headerGetString(h, RPMTAG_PAYLOADFORMAT); -+ bool cpio = true; - -- if (fi == NULL) { -- rc = RPMERR_BAD_MAGIC; -- goto exit; -+ if (payloadfmt && rstreq(payloadfmt, "clon")) { -+ cpio = false; -+ } -+ if (cpio) { -+ fi = rpmfiNewArchiveReader(payload, files, RPMFI_ITER_READ_ARCHIVE); -+ if (fi == NULL) { -+ rc = RPMERR_BAD_MAGIC; -+ goto exit; -+ } -+ } else { -+ fi = rpmfilesIter(files, RPMFI_ITER_FWD); - } - - /* transaction id used for temporary path suffix while installing */ -@@ -893,10 +906,20 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, - /* Run fsm file pre hook for all plugins */ - rc = rpmpluginsCallFsmFilePre(plugins, fi, fpath, - sb.st_mode, action); -- if (rc) { -- skip = 1; -- } else { -+ skip = skip || rpmfiFFlags(fi) & RPMFILE_GHOST; -+ bool plugin_contents = false; -+ switch (rc) { -+ case RPMRC_OK: - setFileState(fs, rpmfiFX(fi)); -+ break; -+ case RPMRC_PLUGIN_CONTENTS: -+ plugin_contents = true; -+ // reduce reads on cpio to this value. Could be zero if -+ // this is from a hard link. -+ rc = RPMRC_OK; -+ break; -+ default: -+ skip = 1; - } - - if (!skip) { -@@ -926,8 +949,12 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, - - if (S_ISREG(sb.st_mode)) { - if (rc == RPMERR_ENOENT) { -- rc = fsmMkfile(fi, fpath, files, psm, nodigest, -- &setmeta, &firsthardlink, &firstlinkfile); -+ if (plugin_contents) { -+ rc = RPMRC_OK; -+ } else { -+ rc = fsmMkfile(fi, fpath, files, psm, nodigest, -+ &setmeta, &firsthardlink, &firstlinkfile); -+ } - } - } else if (S_ISDIR(sb.st_mode)) { - if (rc == RPMERR_ENOENT) { -@@ -1011,7 +1038,10 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, - exit: - - /* No need to bother with close errors on read */ -- rpmfiArchiveClose(fi); -+ if (cpio) { -+ rpmfiArchiveClose(fi); -+ } -+ h = headerFree(h); - rpmfiFree(fi); - Fclose(payload); - free(tid); -diff --git a/lib/package.c b/lib/package.c -index 93a06ebfe..b0c1c2857 100644 ---- a/lib/package.c -+++ b/lib/package.c -@@ -407,5 +407,45 @@ rpmRC rpmReadPackageFile(rpmts ts, FD_t fd, const char * fn, Header * hdrp) - return rc; - } - -+rpmRC rpmReadPackageRaw(FD_t fd, Header * sigp, Header * hdrp) -+{ -+ char *msg = NULL; -+ hdrblob sigblob = hdrblobCreate(); -+ hdrblob blob = hdrblobCreate(); -+ Header h = NULL; -+ Header sigh = NULL; -+ -+ rpmRC rc = rpmLeadRead(fd, &msg); -+ if (rc != RPMRC_OK) -+ goto exit; -+ -+ rc = hdrblobRead(fd, 1, 0, RPMTAG_HEADERSIGNATURES, sigblob, &msg); -+ if (rc != RPMRC_OK) -+ goto exit; -+ -+ rc = hdrblobRead(fd, 1, 1, RPMTAG_HEADERIMMUTABLE, blob, &msg); -+ if (rc != RPMRC_OK) -+ goto exit; -+ -+ rc = hdrblobImport(sigblob, 0, &sigh, &msg); -+ if (rc) -+ goto exit; - -+ rc = hdrblobImport(blob, 0, &h, &msg); -+ if (rc) -+ goto exit; - -+ *sigp = headerLink(sigh); -+ *hdrp = headerLink(h); -+ -+exit: -+ if (rc != RPMRC_OK && msg) -+ rpmlog(RPMLOG_ERR, "%s: %s\n", Fdescr(fd), msg); -+ hdrblobFree(sigblob); -+ hdrblobFree(blob); -+ headerFree(sigh); -+ headerFree(h); -+ free(msg); -+ -+ return rc; -+} -diff --git a/lib/rpmlib.h b/lib/rpmlib.h -index 72ee724e8..4f1a24d14 100644 ---- a/lib/rpmlib.h -+++ b/lib/rpmlib.h -@@ -156,6 +156,15 @@ rpmRC rpmReadHeader(rpmts ts, FD_t fd, Header *hdrp, char ** msg); - rpmRC rpmReadPackageFile(rpmts ts, FD_t fd, - const char * fn, Header * hdrp); - -+/** \ingroup header -+ * Return package signature, header from file handle, no verification. -+ * @param fd file handle -+ * @param[out] sigp address of header (or NULL) -+ * @param[out] hdrp address of header (or NULL) -+ * @return RPMRC_OK on success -+ */ -+rpmRC rpmReadPackageRaw(FD_t fd, Header * sigp, Header * hdrp); -+ - /** \ingroup rpmtrans - * Install source package. - * @param ts transaction set -diff --git a/lib/rpmplugins.c b/lib/rpmplugins.c -index 62d75c4cf..c5084d398 100644 ---- a/lib/rpmplugins.c -+++ b/lib/rpmplugins.c -@@ -356,13 +356,28 @@ rpmRC rpmpluginsCallFsmFilePre(rpmPlugins plugins, rpmfi fi, const char *path, - plugin_fsm_file_pre_func hookFunc; - int i; - rpmRC rc = RPMRC_OK; -+ rpmRC hook_rc; - - for (i = 0; i < plugins->count; i++) { - rpmPlugin plugin = plugins->plugins[i]; - RPMPLUGINS_SET_HOOK_FUNC(fsm_file_pre); -- if (hookFunc && hookFunc(plugin, fi, path, file_mode, op) == RPMRC_FAIL) { -- rpmlog(RPMLOG_ERR, "Plugin %s: hook fsm_file_pre failed\n", plugin->name); -- rc = RPMRC_FAIL; -+ if (hookFunc) { -+ hook_rc = hookFunc(plugin, fi, path, file_mode, op); -+ if (hook_rc == RPMRC_FAIL) { -+ rpmlog(RPMLOG_ERR, "Plugin %s: hook fsm_file_pre failed\n", plugin->name); -+ rc = RPMRC_FAIL; -+ } else if (hook_rc == RPMRC_PLUGIN_CONTENTS && rc != RPMRC_FAIL) { -+ if (rc == RPMRC_PLUGIN_CONTENTS) { -+ /* -+ Another plugin already said it'd handle contents. It's undefined how -+ these would combine, so treat this as a failure condition. -+ */ -+ rc = RPMRC_FAIL; -+ } else { -+ /* Plugin will handle content */ -+ rc = RPMRC_PLUGIN_CONTENTS; -+ } -+ } - } - } - -diff --git a/lib/rpmte.c b/lib/rpmte.c -index fe9782953..ba06c2985 100644 ---- a/lib/rpmte.c -+++ b/lib/rpmte.c -@@ -422,6 +422,11 @@ FD_t rpmteSetFd(rpmte te, FD_t fd) - return NULL; - } - -+FD_t rpmteFd(rpmte te) -+{ -+ return (te != NULL ? te->fd : NULL); -+} -+ - fnpyKey rpmteKey(rpmte te) - { - return (te != NULL ? te->key : NULL); -diff --git a/lib/rpmte.h b/lib/rpmte.h -index 81acf7a19..6fc0a9f91 100644 ---- a/lib/rpmte.h -+++ b/lib/rpmte.h -@@ -209,6 +209,8 @@ const char * rpmteNEVR(rpmte te); - */ - const char * rpmteNEVRA(rpmte te); - -+FD_t rpmteFd(rpmte te); -+ - /** \ingroup rpmte - * Retrieve key from transaction element. - * @param te transaction element -diff --git a/lib/rpmtypes.h b/lib/rpmtypes.h -index e8e69b506..af2611e9e 100644 ---- a/lib/rpmtypes.h -+++ b/lib/rpmtypes.h -@@ -106,7 +106,8 @@ typedef enum rpmRC_e { - RPMRC_NOTFOUND = 1, /*!< Generic not found code. */ - RPMRC_FAIL = 2, /*!< Generic failure code. */ - RPMRC_NOTTRUSTED = 3, /*!< Signature is OK, but key is not trusted. */ -- RPMRC_NOKEY = 4 /*!< Public key is unavailable. */ -+ RPMRC_NOKEY = 4, /*!< Public key is unavailable. */ -+ RPMRC_PLUGIN_CONTENTS = 5 /*!< fsm_file_pre plugin is handling content */ - } rpmRC; - - #ifdef __cplusplus -diff --git a/macros.in b/macros.in -index 5778a1f58..15a28c2d2 100644 ---- a/macros.in -+++ b/macros.in -@@ -1175,6 +1175,7 @@ package or when debugging this package.\ - - # Transaction plugin macros - %__plugindir %{_libdir}/rpm-plugins -+%__transaction_reflink %{__plugindir}/reflink.so - %__transaction_systemd_inhibit %{__plugindir}/systemd_inhibit.so - %__transaction_selinux %{__plugindir}/selinux.so - %__transaction_syslog %{__plugindir}/syslog.so -diff --git a/plugins/Makefile.am b/plugins/Makefile.am -index 963d53db4..a21401966 100644 ---- a/plugins/Makefile.am -+++ b/plugins/Makefile.am -@@ -33,6 +33,10 @@ prioreset_la_SOURCES = prioreset.c - prioreset_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmio.la - plugins_LTLIBRARIES += prioreset.la - -+reflink_la_SOURCES = reflink.c -+reflink_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmio.la -+plugins_LTLIBRARIES += reflink.la -+ - syslog_la_SOURCES = syslog.c - syslog_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmio.la - plugins_LTLIBRARIES += syslog.la -diff --git a/plugins/reflink.c b/plugins/reflink.c -new file mode 100644 -index 000000000..d7f19acd9 ---- /dev/null -+++ b/plugins/reflink.c -@@ -0,0 +1,340 @@ -+#include "system.h" -+ -+#include -+#include -+#include -+#include -+#include -+#include -+#if defined(__linux__) -+#include /* For FICLONE */ -+#endif -+ -+#include -+#include "lib/rpmlib.h" -+#include "lib/rpmplugin.h" -+#include "lib/rpmte_internal.h" -+#include -+#include "rpmio/rpmio_internal.h" -+ -+ -+#include "debug.h" -+ -+#include -+ -+/* use hash table to remember inode -> ix (for rpmfilesFN(ix)) lookups */ -+#undef HASHTYPE -+#undef HTKEYTYPE -+#undef HTDATATYPE -+#define HASHTYPE inodeIndexHash -+#define HTKEYTYPE rpm_ino_t -+#define HTDATATYPE int -+#include "lib/rpmhash.H" -+#include "lib/rpmhash.C" -+ -+/* -+We use this in find to indicate a key wasn't found. This is an unrecoverable -+error, but we can at least show a decent error. 0 is never a valid offset -+because it's the offset of the start of the file. -+*/ -+#define NOT_FOUND 0 -+ -+#define BUFFER_SIZE (1024 * 128) -+ -+/* magic value at end of file (64 bits) that indicates this is a transcoded rpm */ -+#define MAGIC 3472329499408095051 -+ -+struct reflink_state_s { -+ /* Stuff that's used across rpms */ -+ long fundamental_block_size; -+ char *buffer; -+ -+ /* stuff that's used/updated per psm */ -+ uint32_t keys, keysize; -+ -+ // table for current rpm, keys * (keysize + sizeof(rpm_loff_t)) -+ unsigned char *table; -+ FD_t fd; -+ rpmfiles files; -+ inodeIndexHash inodeIndexes; -+}; -+ -+typedef struct reflink_state_s * reflink_state; -+ -+static int inodeCmp(rpm_ino_t a, rpm_ino_t b) -+{ -+ return (a != b); -+} -+ -+static unsigned int inodeId(rpm_ino_t a) -+{ -+ /* rpm_ino_t is uint32_t so maps safely to unsigned int */ -+ return (unsigned int)a; -+} -+ -+static rpmRC reflink_init(rpmPlugin plugin, rpmts ts) { -+ reflink_state state = rcalloc(1, sizeof(struct reflink_state_s)); -+ -+ /* -+ IOCTL-FICLONERANGE(2): ...Disk filesystems generally require the offset and -+ length arguments to be aligned to the fundamental block size. -+ -+ The value of "fundamental block size" is directly related to the system's -+ page size, so we should use that. -+ */ -+ state->fundamental_block_size = sysconf(_SC_PAGESIZE); -+ state->buffer = rcalloc(1, BUFFER_SIZE); -+ rpmPluginSetData(plugin, state); -+ -+ return RPMRC_OK; -+} -+ -+static void reflink_cleanup(rpmPlugin plugin) { -+ reflink_state state = rpmPluginGetData(plugin); -+ free(state->buffer); -+ free(state); -+} -+ -+static rpmRC reflink_psm_pre(rpmPlugin plugin, rpmte te) { -+ reflink_state state = rpmPluginGetData(plugin); -+ state->fd = rpmteFd(te); -+ if (state->fd == 0) { -+ rpmlog(RPMLOG_DEBUG, _("reflink: fd = 0, no install\n")); -+ return RPMRC_OK; -+ } -+ rpm_loff_t current = Ftell(state->fd); -+ uint64_t magic; -+ if (Fseek(state->fd, -(sizeof(magic)), SEEK_END) < 0) { -+ rpmlog(RPMLOG_ERR, _("reflink: failed to seek for magic\n")); -+ if (Fseek(state->fd, current, SEEK_SET) < 0) { -+ /* yes this gets a bit repetitive */ -+ rpmlog(RPMLOG_ERR, _("reflink: unable to seek back to original location\n")); -+ } -+ return RPMRC_FAIL; -+ } -+ size_t len = sizeof(magic); -+ if (Fread(&magic, len, 1, state->fd) != len) { -+ rpmlog(RPMLOG_ERR, _("reflink: unable to read magic\n")); -+ if (Fseek(state->fd, current, SEEK_SET) < 0) { -+ rpmlog(RPMLOG_ERR, _("reflink: unable to seek back to original location\n")); -+ } -+ return RPMRC_FAIL; -+ } -+ if (magic != MAGIC) { -+ rpmlog(RPMLOG_DEBUG, _("reflink: not transcoded\n")); -+ if (Fseek(state->fd, current, SEEK_SET) < 0) { -+ rpmlog(RPMLOG_ERR, _("reflink: unable to seek back to original location\n")); -+ return RPMRC_FAIL; -+ } -+ return RPMRC_OK; -+ } -+ rpmlog(RPMLOG_DEBUG, _("reflink: *is* transcoded\n")); -+ Header h = rpmteHeader(te); -+ -+ /* replace/add header that main fsm.c can read */ -+ headerDel(h, RPMTAG_PAYLOADFORMAT); -+ headerPutString(h, RPMTAG_PAYLOADFORMAT, "clon"); -+ headerFree(h); -+ state->files = rpmteFiles(te); -+ /* tail of file contains offset_table, offset_checksums -+ then magic -+ */ -+ if (Fseek(state->fd, -(sizeof(rpm_loff_t) * 2 + sizeof(magic)), SEEK_END) < 0) { -+ rpmlog(RPMLOG_ERR, _("reflink: failed to seek for tail %p\n"), state->fd); -+ return RPMRC_FAIL; -+ } -+ rpm_loff_t table_start; -+ len = sizeof(table_start); -+ if (Fread(&table_start, len, 1, state->fd) != len) { -+ rpmlog(RPMLOG_ERR, _("reflink: unable to read table_start\n")); -+ return RPMRC_FAIL; -+ } -+ if (Fseek(state->fd, table_start, SEEK_SET) < 0) { -+ rpmlog(RPMLOG_ERR, _("reflink: unable to seek to table_start\n")); -+ return RPMRC_FAIL; -+ } -+ len = sizeof(state->keys); -+ if (Fread(&state->keys, len, 1, state->fd) != len) { -+ rpmlog(RPMLOG_ERR, _("reflink: unable to read number of keys\n")); -+ return RPMRC_FAIL; -+ } -+ len = sizeof(state->keysize); -+ if (Fread(&state->keysize, len, 1, state->fd) != len) { -+ rpmlog(RPMLOG_ERR, _("reflink: unable to read keysize\n")); -+ return RPMRC_FAIL; -+ } -+ rpmlog(RPMLOG_DEBUG, _("reflink: table_start=0x%lx, keys=%d, keysize=%d\n"), table_start, state->keys, state->keysize); -+ // now get digest table if there is a reason to have one. -+ if (state->keys == 0 || state->keysize == 0) { -+ // no files (or no digests(!)) -+ state->table = NULL; -+ } else { -+ int table_size = state->keys * (state->keysize + sizeof(rpm_loff_t)); -+ state->table = rcalloc(1, table_size); -+ if (Fread(state->table, table_size, 1, state->fd) != table_size) { -+ rpmlog(RPMLOG_ERR, _("reflink: unable to read table\n")); -+ return RPMRC_FAIL; -+ } -+ state->inodeIndexes = inodeIndexHashCreate(state->keys, inodeId, inodeCmp, NULL, NULL); -+ } -+ -+ // seek back to original location -+ // might not be needed if we seek to offset immediately -+ if (Fseek(state->fd, current, SEEK_SET) < 0) { -+ rpmlog(RPMLOG_ERR, _("reflink: unable to seek back to original location\n")); -+ return RPMRC_FAIL; -+ } -+ return RPMRC_OK; -+} -+ -+static rpmRC reflink_psm_post(rpmPlugin plugin, rpmte te, int res) -+{ -+ reflink_state state = rpmPluginGetData(plugin); -+ state->files = rpmfilesFree(state->files); -+ if (state->table) { -+ free(state->table); -+ state->table = NULL; -+ } -+ if (state->inodeIndexes) { -+ inodeIndexHashFree(state->inodeIndexes); -+ state->inodeIndexes = NULL; -+ } -+ return RPMRC_OK; -+} -+ -+ -+// have a prototype, warnings system -+rpm_loff_t find(const unsigned char *digest, reflink_state state); -+ -+rpm_loff_t find(const unsigned char *digest, reflink_state state) { -+# if defined(__GNUC__) -+ /* GCC nested function because bsearch's comparison function can't access -+ state-keysize otherwise -+ */ -+ int cmpdigest(const void *k1, const void *k2) { -+ rpmlog(RPMLOG_DEBUG, _("reflink: cmpdigest k1=%p k2=%p\n"), k1, k2); -+ return memcmp(k1, k2, state->keysize); -+ } -+# endif -+ rpmlog(RPMLOG_DEBUG, _("reflink: bsearch(key=%p, base=%p, nmemb=%d, size=%lu)\n"), digest, state->table, state->keys, state->keysize + sizeof(rpm_loff_t)); -+ char *entry = bsearch(digest, state->table, state->keys, state->keysize + sizeof(rpm_loff_t), cmpdigest); -+ if (entry == NULL) { -+ return NOT_FOUND; -+ } -+ rpm_loff_t offset = *(rpm_loff_t *)(entry + state->keysize); -+ return offset; -+} -+ -+static rpmRC reflink_fsm_file_pre(rpmPlugin plugin, rpmfi fi, const char* path, mode_t file_mode, rpmFsmOp op) -+{ -+ struct file_clone_range fcr; -+ rpm_loff_t size; -+ int dst, rc; -+ int *hlix; -+ -+ reflink_state state = rpmPluginGetData(plugin); -+ if (state->table == NULL) { -+ // no table means rpm is not in reflink format, so leave. Now. -+ return RPMRC_OK; -+ } -+ if (op == FA_TOUCH) { -+ // we're not overwriting an existing file -+ return RPMRC_OK; -+ } -+ fcr.dest_offset = 0; -+ if (S_ISREG(file_mode) && !(rpmfiFFlags(fi) & RPMFILE_GHOST)) { -+ rpm_ino_t inode = rpmfiFInode(fi); -+ /* check for hard link entry in table. GetEntry overwrites hlix with the address of the first match */ -+ if (inodeIndexHashGetEntry(state->inodeIndexes, inode, &hlix, NULL, NULL)) { -+ // entry is in table, use hard link -+ char *fn = rpmfilesFN(state->files, hlix[0]); -+ if (link(fn, path) != 0) { -+ rpmlog(RPMLOG_ERR, _("reflink: Unable to hard link %s -> %s due to %s\n"), fn, path, strerror(errno)); -+ free(fn); -+ return RPMRC_FAIL; -+ } -+ free(fn); -+ return RPMRC_PLUGIN_CONTENTS; -+ } -+ /* if we didn't hard link, then we'll track this inode as being created soon */ -+ if (rpmfiFNlink(fi) > 1) { -+ /* minor optimization: only store files with more than one link */ -+ inodeIndexHashAddEntry(state->inodeIndexes, inode, rpmfiFX(fi)); -+ } -+ /* derived from wfd_open in fsm.c */ -+ mode_t old_umask = umask(0577); -+ dst = open(path, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR); -+ umask(old_umask); -+ if (dst == -1) { -+ rpmlog(RPMLOG_ERR, _("reflink: Unable to open %s for writing due to %s, flags = %x\n"), path, strerror(errno), rpmfiFFlags(fi)); -+ return RPMRC_FAIL; -+ } -+ size = rpmfiFSize(fi); -+ if (size > 0) { -+ /* round src_length down to fundamental_block_size multiple */ -+ fcr.src_length = size / state->fundamental_block_size * state->fundamental_block_size; -+ if ((size % state->fundamental_block_size) > 0) { -+ /* round up to next fundamental_block_size. We expect the data in the rpm to be similarly padded */ -+ fcr.src_length += state->fundamental_block_size; -+ } -+ fcr.src_fd = Fileno(state->fd); -+ if (fcr.src_fd == -1) { -+ close(dst); -+ rpmlog(RPMLOG_ERR, _("reflink: src fd lookup failed\n")); -+ return RPMRC_FAIL; -+ } -+ fcr.src_offset = find(rpmfiFDigest(fi, NULL, NULL), state); -+ if (fcr.src_offset == NOT_FOUND) { -+ close(dst); -+ rpmlog(RPMLOG_ERR, _("reflink: digest not found\n")); -+ return RPMRC_FAIL; -+ } -+ rpmlog(RPMLOG_DEBUG, _("reflink: Reflinking %lu bytes at %lu to %s orig size=%lu, file=%ld\n"), fcr.src_length, fcr.src_offset, path, size, fcr.src_fd); -+ rc = ioctl(dst, FICLONERANGE, &fcr); -+ if (rc) { -+ rpmlog(RPMLOG_WARNING, _("reflink: falling back to copying bits for %s due to %d, %d = %s\n"), path, rc, errno, strerror(errno)); -+ if (Fseek(state->fd, fcr.src_offset, SEEK_SET) < 0) { -+ close(dst); -+ rpmlog(RPMLOG_ERR, _("reflink: unable to seek on copying bits\n")); -+ return RPMRC_FAIL; -+ } -+ rpm_loff_t left = size; -+ size_t len, read, written; -+ while (left) { -+ len = (left > BUFFER_SIZE ? BUFFER_SIZE : left); -+ read = Fread(state->buffer, len, 1, state->fd); -+ if (read != len) { -+ close(dst); -+ rpmlog(RPMLOG_ERR, _("reflink: short read on copying bits\n")); -+ return RPMRC_FAIL; -+ } -+ written = write(dst, state->buffer, len); -+ if (read != written) { -+ close(dst); -+ rpmlog(RPMLOG_ERR, _("reflink: short write on copying bits\n")); -+ return RPMRC_FAIL; -+ } -+ left -= len; -+ } -+ } else { -+ /* reflink worked, so truncate */ -+ rc = ftruncate(dst, size); -+ if (rc) { -+ rpmlog(RPMLOG_ERR, _("reflink: Unable to truncate %s to %ld due to %s\n"), path, size, strerror(errno)); -+ return RPMRC_FAIL; -+ } -+ } -+ } -+ close(dst); -+ return RPMRC_PLUGIN_CONTENTS; -+ } -+ return RPMRC_OK; -+} -+ -+struct rpmPluginHooks_s reflink_hooks = { -+ .init = reflink_init, -+ .cleanup = reflink_cleanup, -+ .psm_pre = reflink_psm_pre, -+ .psm_post = reflink_psm_post, -+ .fsm_file_pre = reflink_fsm_file_pre, -+}; -diff --git a/rpm2extents.c b/rpm2extents.c -new file mode 100644 -index 000000000..5662b86a6 ---- /dev/null -+++ b/rpm2extents.c -@@ -0,0 +1,519 @@ -+/* rpm2extents: convert payload to inline extents */ -+ -+#include "system.h" -+ -+#include /* rpmReadPackageFile .. */ -+#include -+#include -+#include -+#include -+ -+#include -+#include "lib/rpmlead.h" -+#include "lib/signature.h" -+#include "lib/header_internal.h" -+#include "rpmio/rpmio_internal.h" -+ -+#include -+#include -+#include -+#include -+#include -+#include -+ -+#include "debug.h" -+ -+/* hash of void * (pointers) to file digests to offsets within output. -+ The length of the key depends on what the FILEDIGESTALGO is. -+ */ -+#undef HASHTYPE -+#undef HTKEYTYPE -+#undef HTDATATYPE -+#define HASHTYPE digestSet -+#define HTKEYTYPE const unsigned char * -+#include "lib/rpmhash.H" -+#include "lib/rpmhash.C" -+ -+/* magic value at end of file (64 bits) that indicates this is a transcoded rpm */ -+#define MAGIC 3472329499408095051 -+ -+struct digestoffset { -+ const unsigned char * digest; -+ rpm_loff_t pos; -+}; -+ -+rpm_loff_t pad_to(rpm_loff_t pos, rpm_loff_t unit); -+ -+rpm_loff_t pad_to(rpm_loff_t pos, rpm_loff_t unit) -+{ -+ return (unit - (pos % unit)) % unit; -+} -+ -+static int digestor( -+ FD_t fdi, -+ FD_t fdo, -+ FD_t validationo, -+ uint8_t algos[], -+ uint32_t algos_len -+) -+{ -+ ssize_t fdilength; -+ const char *filedigest, *algo_name; -+ size_t filedigest_len, len; -+ uint32_t algo_name_len, algo_digest_len; -+ int algo; -+ rpmRC rc = RPMRC_FAIL; -+ -+ for (algo = 0; algo < algos_len; algo++) -+ { -+ fdInitDigest(fdi, algos[algo], 0); -+ } -+ fdilength = ufdCopy(fdi, fdo); -+ if (fdilength == -1) -+ { -+ fprintf(stderr, _("digest cat failed\n")); -+ goto exit; -+ } -+ -+ len = sizeof(fdilength); -+ if (Fwrite(&fdilength, len, 1, validationo) != len) -+ { -+ fprintf(stderr, _("Unable to write input length %zd\n"), fdilength); -+ goto exit; -+ } -+ len = sizeof(algos_len); -+ if (Fwrite(&algos_len, len, 1, validationo) != len) -+ { -+ fprintf(stderr, _("Unable to write number of validation digests\n")); -+ goto exit; -+ } -+ for (algo = 0; algo < algos_len; algo++) -+ { -+ fdFiniDigest(fdi, algos[algo], (void **)&filedigest, &filedigest_len, 0); -+ -+ algo_name = pgpValString(PGPVAL_HASHALGO, algos[algo]); -+ algo_name_len = (uint32_t)strlen(algo_name); -+ algo_digest_len = (uint32_t)filedigest_len; -+ -+ len = sizeof(algo_name_len); -+ if (Fwrite(&algo_name_len, len, 1, validationo) != len) -+ { -+ fprintf( -+ stderr, -+ _("Unable to write validation algo name length\n") -+ ); -+ goto exit; -+ } -+ len = sizeof(algo_digest_len); -+ if (Fwrite(&algo_digest_len, len, 1, validationo) != len) -+ { -+ fprintf( -+ stderr, -+ _("Unable to write number of bytes for validation digest\n") -+ ); -+ goto exit; -+ } -+ if (Fwrite(algo_name, algo_name_len, 1, validationo) != algo_name_len) -+ { -+ fprintf(stderr, _("Unable to write validation algo name\n")); -+ goto exit; -+ } -+ if ( -+ Fwrite( -+ filedigest, -+ algo_digest_len, -+ 1, -+ validationo -+ ) != algo_digest_len -+ ) -+ { -+ fprintf( -+ stderr, -+ _("Unable to write validation digest value %u, %zu\n"), -+ algo_digest_len, -+ filedigest_len -+ ); -+ goto exit; -+ } -+ } -+ rc = RPMRC_OK; -+exit: -+ return rc; -+} -+ -+static rpmRC process_package(FD_t fdi, FD_t validationi) -+{ -+ uint32_t diglen; -+ /* GNU C extension: can use diglen from outer context */ -+ int digestSetCmp(const unsigned char * a, const unsigned char * b) -+ { -+ return memcmp(a, b, diglen); -+ } -+ -+ unsigned int digestSetHash(const unsigned char * digest) -+ { -+ /* assumes sizeof(unsigned int) < diglen */ -+ return *(unsigned int *)digest; -+ } -+ -+ int digestoffsetCmp(const void * a, const void * b) -+ { -+ return digestSetCmp( -+ ((struct digestoffset *)a)->digest, -+ ((struct digestoffset *)b)->digest -+ ); -+ } -+ -+ FD_t fdo; -+ FD_t gzdi; -+ Header h, sigh; -+ long fundamental_block_size = sysconf(_SC_PAGESIZE); -+ rpmRC rc = RPMRC_OK; -+ rpm_mode_t mode; -+ char *rpmio_flags = NULL, *zeros; -+ const unsigned char *digest; -+ rpm_loff_t pos, size, pad, validation_pos; -+ uint32_t offset_ix = 0; -+ size_t len; -+ int next = 0; -+ -+ fdo = fdDup(STDOUT_FILENO); -+ -+ if (rpmReadPackageRaw(fdi, &sigh, &h)) -+ { -+ fprintf(stderr, _("Error reading package\n")); -+ exit(EXIT_FAILURE); -+ } -+ -+ if (rpmLeadWrite(fdo, h)) -+ { -+ fprintf( -+ stderr, -+ _("Unable to write package lead: %s\n"), -+ Fstrerror(fdo) -+ ); -+ exit(EXIT_FAILURE); -+ } -+ -+ if (rpmWriteSignature(fdo, sigh)) -+ { -+ fprintf(stderr, _("Unable to write signature: %s\n"), Fstrerror(fdo)); -+ exit(EXIT_FAILURE); -+ } -+ -+ if (headerWrite(fdo, h, HEADER_MAGIC_YES)) -+ { -+ fprintf(stderr, _("Unable to write headers: %s\n"), Fstrerror(fdo)); -+ exit(EXIT_FAILURE); -+ } -+ -+ /* Retrieve payload size and compression type. */ -+ { const char *compr = headerGetString(h, RPMTAG_PAYLOADCOMPRESSOR); -+ rpmio_flags = rstrscat(NULL, "r.", compr ? compr : "gzip", NULL); -+ } -+ -+ gzdi = Fdopen(fdi, rpmio_flags); /* XXX gzdi == fdi */ -+ free(rpmio_flags); -+ -+ if (gzdi == NULL) -+ { -+ fprintf(stderr, _("cannot re-open payload: %s\n"), Fstrerror(gzdi)); -+ exit(EXIT_FAILURE); -+ } -+ -+ rpmfiles files = rpmfilesNew(NULL, h, 0, RPMFI_KEEPHEADER); -+ rpmfi fi = rpmfiNewArchiveReader( -+ gzdi, -+ files, -+ RPMFI_ITER_READ_ARCHIVE_CONTENT_FIRST -+ ); -+ -+ /* this is encoded in the file format, so needs to be fixed size (for -+ now?) -+ */ -+ diglen = (uint32_t)rpmDigestLength(rpmfiDigestAlgo(fi)); -+ digestSet ds = digestSetCreate( -+ rpmfiFC(fi), -+ digestSetHash, -+ digestSetCmp, -+ NULL -+ ); -+ struct digestoffset offsets[rpmfiFC(fi)]; -+ pos = RPMLEAD_SIZE + headerSizeof(sigh, HEADER_MAGIC_YES); -+ -+ /* main headers are aligned to 8 byte boundry */ -+ pos += pad_to(pos, 8); -+ pos += headerSizeof(h, HEADER_MAGIC_YES); -+ -+ zeros = xcalloc(fundamental_block_size, 1); -+ -+ while (next >= 0) -+ { -+ next = rpmfiNext(fi); -+ if (next == RPMERR_ITER_END) -+ { -+ rc = RPMRC_OK; -+ break; -+ } -+ mode = rpmfiFMode(fi); -+ if (!S_ISREG(mode) || !rpmfiArchiveHasContent(fi)) -+ { -+ /* not a regular file, or the archive doesn't contain any content for -+ this entry -+ */ -+ continue; -+ } -+ digest = rpmfiFDigest(fi, NULL, NULL); -+ if (digestSetGetEntry(ds, digest, NULL)) -+ { -+ /* This specific digest has already been included, so skip it */ -+ continue; -+ } -+ pad = pad_to(pos, fundamental_block_size); -+ if (Fwrite(zeros, sizeof(char), pad, fdo) != pad) -+ { -+ fprintf(stderr, _("Unable to write padding\n")); -+ rc = RPMRC_FAIL; -+ goto exit; -+ } -+ /* round up to next fundamental_block_size */ -+ pos += pad; -+ digestSetAddEntry(ds, digest); -+ offsets[offset_ix].digest = digest; -+ offsets[offset_ix].pos = pos; -+ offset_ix++; -+ size = rpmfiFSize(fi); -+ rc = rpmfiArchiveReadToFile(fi, fdo, 0); -+ if (rc != RPMRC_OK) -+ { -+ fprintf(stderr, _("rpmfiArchiveReadToFile failed with %d\n"), rc); -+ goto exit; -+ } -+ pos += size; -+ } -+ Fclose(gzdi); /* XXX gzdi == fdi */ -+ -+ qsort( -+ offsets, -+ (size_t)offset_ix, -+ sizeof(struct digestoffset), -+ digestoffsetCmp -+ ); -+ -+ len = sizeof(offset_ix); -+ if (Fwrite(&offset_ix, len, 1, fdo) != len) -+ { -+ fprintf(stderr, _("Unable to write length of table\n")); -+ rc = RPMRC_FAIL; -+ goto exit; -+ } -+ len = sizeof(diglen); -+ if (Fwrite(&diglen, len, 1, fdo) != len) -+ { -+ fprintf(stderr, _("Unable to write length of digest\n")); -+ rc = RPMRC_FAIL; -+ goto exit; -+ } -+ len = sizeof(rpm_loff_t); -+ for (int x = 0; x < offset_ix; x++) -+ { -+ if (Fwrite(offsets[x].digest, diglen, 1, fdo) != diglen) -+ { -+ fprintf(stderr, _("Unable to write digest\n")); -+ rc = RPMRC_FAIL; -+ goto exit; -+ } -+ if (Fwrite(&offsets[x].pos, len, 1, fdo) != len) -+ { -+ fprintf(stderr, _("Unable to write offset\n")); -+ rc = RPMRC_FAIL; -+ goto exit; -+ } -+ } -+ validation_pos = ( -+ pos + sizeof(offset_ix) + sizeof(diglen) + -+ offset_ix * (diglen + sizeof(rpm_loff_t)) -+ ); -+ -+ ssize_t validation_len = ufdCopy(validationi, fdo); -+ if (validation_len == -1) -+ { -+ fprintf(stderr, _("digest table ufdCopy failed\n")); -+ rc = RPMRC_FAIL; -+ goto exit; -+ } -+ /* add more padding so the last file can be cloned. It doesn't matter that -+ the table and validation etc are in this space. In fact, it's pretty -+ efficient if it is -+ */ -+ -+ pad = pad_to( -+ ( -+ validation_pos + validation_len + 2 * sizeof(rpm_loff_t) + -+ sizeof(uint64_t) -+ ), -+ fundamental_block_size -+ ); -+ if (Fwrite(zeros, sizeof(char), pad, fdo) != pad) -+ { -+ fprintf(stderr, _("Unable to write final padding\n")); -+ rc = RPMRC_FAIL; -+ goto exit; -+ } -+ zeros = _free(zeros); -+ if (Fwrite(&pos, len, 1, fdo) != len) -+ { -+ fprintf(stderr, _("Unable to write offset of digest table\n")); -+ rc = RPMRC_FAIL; -+ goto exit; -+ } -+ if (Fwrite(&validation_pos, len, 1, fdo) != len) -+ { -+ fprintf(stderr, _("Unable to write offset of validation table\n")); -+ rc = RPMRC_FAIL; -+ goto exit; -+ } -+ uint64_t magic = MAGIC; -+ len = sizeof(magic); -+ if (Fwrite(&magic, len, 1, fdo) != len) -+ { -+ fprintf(stderr, _("Unable to write magic\n")); -+ rc = RPMRC_FAIL; -+ goto exit; -+ } -+ -+exit: -+ rpmfilesFree(files); -+ rpmfiFree(fi); -+ headerFree(h); -+ return rc; -+} -+ -+int main(int argc, char *argv[]) -+{ -+ rpmRC rc; -+ int cprc = 0; -+ uint8_t algos[argc - 1]; -+ int mainpipefd[2]; -+ int metapipefd[2]; -+ pid_t cpid, w; -+ int wstatus; -+ -+ xsetprogname(argv[0]); /* Portability call -- see system.h */ -+ rpmReadConfigFiles(NULL, NULL); -+ -+ if (argc > 1 && (rstreq(argv[1], "-h") || rstreq(argv[1], "--help"))) -+ { -+ fprintf(stderr, _("Usage: %s [DIGESTALGO]...\n"), argv[0]); -+ exit(EXIT_FAILURE); -+ } -+ -+ if (argc == 1) -+ { -+ fprintf( -+ stderr, -+ _("Need at least one DIGESTALGO parameter, e.g. 'SHA256'\n") -+ ); -+ exit(EXIT_FAILURE); -+ } -+ -+ for (int x = 0; x < (argc - 1); x++) -+ { -+ if (pgpStringVal(PGPVAL_HASHALGO, argv[x + 1], &algos[x]) != 0) -+ { -+ fprintf( -+ stderr, -+ _("Unable to resolve '%s' as a digest algorithm, exiting\n"), -+ argv[x + 1] -+ ); -+ exit(EXIT_FAILURE); -+ } -+ } -+ -+ -+ if (pipe(mainpipefd) == -1) -+ { -+ fprintf(stderr, _("Main pipe failure\n")); -+ exit(EXIT_FAILURE); -+ } -+ if (pipe(metapipefd) == -1) -+ { -+ fprintf(stderr, _("Meta pipe failure\n")); -+ exit(EXIT_FAILURE); -+ } -+ cpid = fork(); -+ if (cpid == 0) -+ { -+ /* child: digestor */ -+ close(mainpipefd[0]); -+ close(metapipefd[0]); -+ FD_t fdi = fdDup(STDIN_FILENO); -+ FD_t fdo = fdDup(mainpipefd[1]); -+ FD_t validationo = fdDup(metapipefd[1]); -+ rc = digestor(fdi, fdo, validationo, algos, argc - 1); -+ Fclose(validationo); -+ Fclose(fdo); -+ Fclose(fdi); -+ } else { -+ /* parent: main program */ -+ close(mainpipefd[1]); -+ close(metapipefd[1]); -+ FD_t fdi = fdDup(mainpipefd[0]); -+ FD_t validationi = fdDup(metapipefd[0]); -+ rc = process_package(fdi, validationi); -+ Fclose(validationi); -+ /* fdi is normally closed through the stacked file gzdi in the function. */ -+ /* wait for child process (digestor for stdin) to complete. */ -+ if (rc != RPMRC_OK) -+ { -+ if (kill(cpid, SIGTERM) != 0) -+ { -+ fprintf( -+ stderr, -+ _("Failed to kill digest process when main process failed: %s\n"), -+ strerror(errno) -+ ); -+ } -+ } -+ w = waitpid(cpid, &wstatus, 0); -+ if (w == -1) -+ { -+ fprintf(stderr, _("waitpid failed\n")); -+ cprc = EXIT_FAILURE; -+ } else if (WIFEXITED(wstatus)) -+ { -+ cprc = WEXITSTATUS(wstatus); -+ if (cprc != 0) -+ { -+ fprintf( -+ stderr, -+ _("Digest process non-zero exit code %d\n"), -+ cprc -+ ); -+ } -+ } else if (WIFSIGNALED(wstatus)) -+ { -+ fprintf( -+ stderr, -+ _("Digest process was terminated with a signal: %d\n"), -+ WTERMSIG(wstatus) -+ ); -+ cprc = EXIT_FAILURE; -+ } else -+ { -+ /* don't think this can happen, but covering all bases */ -+ fprintf(stderr, _("Unhandled circumstance in waitpid\n")); -+ cprc = EXIT_FAILURE; -+ } -+ if (cprc != EXIT_SUCCESS) -+ { -+ rc = RPMRC_FAIL; -+ } -+ } -+ if (rc != RPMRC_OK) -+ { -+ /* translate rpmRC into generic failure return code. */ -+ return EXIT_FAILURE; -+ } -+ return EXIT_SUCCESS; -+} -diff --git a/rpmio/rpmpgp.c b/rpmio/rpmpgp.c -index d1d2e7de3..2b58de167 100644 ---- a/rpmio/rpmpgp.c -+++ b/rpmio/rpmpgp.c -@@ -298,6 +298,16 @@ int pgpValTok(pgpValTbl vs, const char * s, const char * se) - return vs->val; - } - -+int pgpStringVal(pgpValType type, const char *str, uint8_t *val) -+{ -+ pgpValTbl tbl = pgpValTable(type); -+ if (tbl == NULL) return -1; -+ int v = pgpValTok(tbl, str, str + strlen(str)); -+ if (v == -1) return -1; -+ *val = (uint8_t)v; -+ return 0; -+} -+ - /** \ingroup rpmpgp - * Decode length from 1, 2, or 5 octet body length encoding, used in - * new format packet headers and V4 signature subpackets. -diff --git a/rpmio/rpmpgp.h b/rpmio/rpmpgp.h -index 1614750d6..4626a2efc 100644 ---- a/rpmio/rpmpgp.h -+++ b/rpmio/rpmpgp.h -@@ -973,6 +973,15 @@ typedef rpmFlags rpmDigestFlags; - */ - const char * pgpValString(pgpValType type, uint8_t val); - -+/** \ingroup rpmpgp -+ * Return OpenPGP value for a string. -+ * @param type type of value -+ * @param str string to lookup -+ * @param[out] val byte value associated with string -+ * @return 0 on success else -1 -+ */ -+int pgpStringVal(pgpValType type, const char *str, uint8_t *val); -+ - /** \ingroup rpmpgp - * Return (native-endian) integer from big-endian representation. - * @param s pointer to big-endian integer - -From 9de362d128634768543e1999763fb1371313c40d Mon Sep 17 00:00:00 2001 -From: Matthew Almond -Date: Sun, 31 Jan 2021 12:30:33 -0800 -Subject: [PATCH 2/4] Remove use of bool type for consistency - ---- - lib/fsm.c | 9 ++++----- - 1 file changed, 4 insertions(+), 5 deletions(-) - -diff --git a/lib/fsm.c b/lib/fsm.c -index 03a716474..7966ac2f1 100644 ---- a/lib/fsm.c -+++ b/lib/fsm.c -@@ -7,7 +7,6 @@ - - #include - #include --#include - #if WITH_CAP - #include - #endif -@@ -854,10 +853,10 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, - char *fpath = NULL; - Header h = rpmteHeader(te); - const char *payloadfmt = headerGetString(h, RPMTAG_PAYLOADFORMAT); -- bool cpio = true; -+ int cpio = 1; - - if (payloadfmt && rstreq(payloadfmt, "clon")) { -- cpio = false; -+ cpio = 0; - } - if (cpio) { - fi = rpmfiNewArchiveReader(payload, files, RPMFI_ITER_READ_ARCHIVE); -@@ -907,13 +906,13 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, - rc = rpmpluginsCallFsmFilePre(plugins, fi, fpath, - sb.st_mode, action); - skip = skip || rpmfiFFlags(fi) & RPMFILE_GHOST; -- bool plugin_contents = false; -+ int plugin_contents = 0; - switch (rc) { - case RPMRC_OK: - setFileState(fs, rpmfiFX(fi)); - break; - case RPMRC_PLUGIN_CONTENTS: -- plugin_contents = true; -+ plugin_contents = 1; - // reduce reads on cpio to this value. Could be zero if - // this is from a hard link. - rc = RPMRC_OK; - -From 91f7284e961cdbecdfec5beedbce03ee2f0fbd85 Mon Sep 17 00:00:00 2001 -From: Matthew Almond -Date: Sun, 31 Jan 2021 13:51:16 -0800 -Subject: [PATCH 3/4] Match formatting/style of existing code - -The existing code contains some variability in formatting. I'm not sure -if { is meant to be on the end of the line, or on a new line, but I've -standardized on the former. - -The indentation is intended to match the existing convention: 4 column -indent, but 8 column wide tab characters. This is easy to follow/use in -vim, but is surprisingly difficult to get right in vscode. I am doing -this reformat here and now, and future changes will be after this. - -I'm keen to fold the patches together, but for now, I'm trying to keep -the history of #1470 linear so everyone can follow along. ---- - lib/rpmplugins.c | 6 +- - plugins/reflink.c | 407 ++++++++++++++++++--------------- - rpm2extents.c | 562 ++++++++++++++++++++-------------------------- - 3 files changed, 462 insertions(+), 513 deletions(-) - -diff --git a/lib/rpmplugins.c b/lib/rpmplugins.c -index c5084d398..3da3097af 100644 ---- a/lib/rpmplugins.c -+++ b/lib/rpmplugins.c -@@ -368,9 +368,9 @@ rpmRC rpmpluginsCallFsmFilePre(rpmPlugins plugins, rpmfi fi, const char *path, - rc = RPMRC_FAIL; - } else if (hook_rc == RPMRC_PLUGIN_CONTENTS && rc != RPMRC_FAIL) { - if (rc == RPMRC_PLUGIN_CONTENTS) { -- /* -- Another plugin already said it'd handle contents. It's undefined how -- these would combine, so treat this as a failure condition. -+ /* Another plugin already said it'd handle contents. It's -+ * undefined how these would combine, so treat this as a -+ * failure condition. - */ - rc = RPMRC_FAIL; - } else { -diff --git a/plugins/reflink.c b/plugins/reflink.c -index d7f19acd9..9eaa87094 100644 ---- a/plugins/reflink.c -+++ b/plugins/reflink.c -@@ -32,31 +32,32 @@ - #include "lib/rpmhash.H" - #include "lib/rpmhash.C" - --/* --We use this in find to indicate a key wasn't found. This is an unrecoverable --error, but we can at least show a decent error. 0 is never a valid offset --because it's the offset of the start of the file. --*/ -+/* We use this in find to indicate a key wasn't found. This is an -+ * unrecoverable error, but we can at least show a decent error. 0 is never a -+ * valid offset because it's the offset of the start of the file. -+ */ - #define NOT_FOUND 0 - - #define BUFFER_SIZE (1024 * 128) - --/* magic value at end of file (64 bits) that indicates this is a transcoded rpm */ -+/* magic value at end of file (64 bits) that indicates this is a transcoded -+ * rpm. -+ */ - #define MAGIC 3472329499408095051 - - struct reflink_state_s { -- /* Stuff that's used across rpms */ -- long fundamental_block_size; -- char *buffer; -+ /* Stuff that's used across rpms */ -+ long fundamental_block_size; -+ char *buffer; - -- /* stuff that's used/updated per psm */ -- uint32_t keys, keysize; -+ /* stuff that's used/updated per psm */ -+ uint32_t keys, keysize; - -- // table for current rpm, keys * (keysize + sizeof(rpm_loff_t)) -- unsigned char *table; -- FD_t fd; -- rpmfiles files; -- inodeIndexHash inodeIndexes; -+ /* table for current rpm, keys * (keysize + sizeof(rpm_loff_t)) */ -+ unsigned char *table; -+ FD_t fd; -+ rpmfiles files; -+ inodeIndexHash inodeIndexes; - }; - - typedef struct reflink_state_s * reflink_state; -@@ -73,60 +74,62 @@ static unsigned int inodeId(rpm_ino_t a) - } - - static rpmRC reflink_init(rpmPlugin plugin, rpmts ts) { -- reflink_state state = rcalloc(1, sizeof(struct reflink_state_s)); -+ reflink_state state = rcalloc(1, sizeof(struct reflink_state_s)); - -- /* -- IOCTL-FICLONERANGE(2): ...Disk filesystems generally require the offset and -- length arguments to be aligned to the fundamental block size. -+ /* IOCTL-FICLONERANGE(2): ...Disk filesystems generally require the offset -+ * and length arguments to be aligned to the fundamental block size. -+ * -+ * The value of "fundamental block size" is directly related to the -+ * system's page size, so we should use that. -+ */ -+ state->fundamental_block_size = sysconf(_SC_PAGESIZE); -+ state->buffer = rcalloc(1, BUFFER_SIZE); -+ rpmPluginSetData(plugin, state); - -- The value of "fundamental block size" is directly related to the system's -- page size, so we should use that. -- */ -- state->fundamental_block_size = sysconf(_SC_PAGESIZE); -- state->buffer = rcalloc(1, BUFFER_SIZE); -- rpmPluginSetData(plugin, state); -- -- return RPMRC_OK; -+ return RPMRC_OK; - } - - static void reflink_cleanup(rpmPlugin plugin) { -- reflink_state state = rpmPluginGetData(plugin); -- free(state->buffer); -- free(state); -+ reflink_state state = rpmPluginGetData(plugin); -+ free(state->buffer); -+ free(state); - } - - static rpmRC reflink_psm_pre(rpmPlugin plugin, rpmte te) { - reflink_state state = rpmPluginGetData(plugin); - state->fd = rpmteFd(te); - if (state->fd == 0) { -- rpmlog(RPMLOG_DEBUG, _("reflink: fd = 0, no install\n")); -- return RPMRC_OK; -+ rpmlog(RPMLOG_DEBUG, _("reflink: fd = 0, no install\n")); -+ return RPMRC_OK; - } - rpm_loff_t current = Ftell(state->fd); - uint64_t magic; - if (Fseek(state->fd, -(sizeof(magic)), SEEK_END) < 0) { -- rpmlog(RPMLOG_ERR, _("reflink: failed to seek for magic\n")); -- if (Fseek(state->fd, current, SEEK_SET) < 0) { -- /* yes this gets a bit repetitive */ -- rpmlog(RPMLOG_ERR, _("reflink: unable to seek back to original location\n")); -- } -- return RPMRC_FAIL; -+ rpmlog(RPMLOG_ERR, _("reflink: failed to seek for magic\n")); -+ if (Fseek(state->fd, current, SEEK_SET) < 0) { -+ /* yes this gets a bit repetitive */ -+ rpmlog(RPMLOG_ERR, -+ _("reflink: unable to seek back to original location\n")); -+ } -+ return RPMRC_FAIL; - } - size_t len = sizeof(magic); - if (Fread(&magic, len, 1, state->fd) != len) { -- rpmlog(RPMLOG_ERR, _("reflink: unable to read magic\n")); -- if (Fseek(state->fd, current, SEEK_SET) < 0) { -- rpmlog(RPMLOG_ERR, _("reflink: unable to seek back to original location\n")); -- } -- return RPMRC_FAIL; -+ rpmlog(RPMLOG_ERR, _("reflink: unable to read magic\n")); -+ if (Fseek(state->fd, current, SEEK_SET) < 0) { -+ rpmlog(RPMLOG_ERR, -+ _("reflink: unable to seek back to original location\n")); -+ } -+ return RPMRC_FAIL; - } - if (magic != MAGIC) { -- rpmlog(RPMLOG_DEBUG, _("reflink: not transcoded\n")); -- if (Fseek(state->fd, current, SEEK_SET) < 0) { -- rpmlog(RPMLOG_ERR, _("reflink: unable to seek back to original location\n")); -- return RPMRC_FAIL; -- } -- return RPMRC_OK; -+ rpmlog(RPMLOG_DEBUG, _("reflink: not transcoded\n")); -+ if (Fseek(state->fd, current, SEEK_SET) < 0) { -+ rpmlog(RPMLOG_ERR, -+ _("reflink: unable to seek back to original location\n")); -+ return RPMRC_FAIL; -+ } -+ return RPMRC_OK; - } - rpmlog(RPMLOG_DEBUG, _("reflink: *is* transcoded\n")); - Header h = rpmteHeader(te); -@@ -136,53 +139,60 @@ static rpmRC reflink_psm_pre(rpmPlugin plugin, rpmte te) { - headerPutString(h, RPMTAG_PAYLOADFORMAT, "clon"); - headerFree(h); - state->files = rpmteFiles(te); -- /* tail of file contains offset_table, offset_checksums -- then magic -- */ -+ /* tail of file contains offset_table, offset_checksums then magic */ - if (Fseek(state->fd, -(sizeof(rpm_loff_t) * 2 + sizeof(magic)), SEEK_END) < 0) { -- rpmlog(RPMLOG_ERR, _("reflink: failed to seek for tail %p\n"), state->fd); -- return RPMRC_FAIL; -+ rpmlog(RPMLOG_ERR, _("reflink: failed to seek for tail %p\n"), -+ state->fd); -+ return RPMRC_FAIL; - } - rpm_loff_t table_start; - len = sizeof(table_start); - if (Fread(&table_start, len, 1, state->fd) != len) { -- rpmlog(RPMLOG_ERR, _("reflink: unable to read table_start\n")); -- return RPMRC_FAIL; -+ rpmlog(RPMLOG_ERR, _("reflink: unable to read table_start\n")); -+ return RPMRC_FAIL; - } - if (Fseek(state->fd, table_start, SEEK_SET) < 0) { -- rpmlog(RPMLOG_ERR, _("reflink: unable to seek to table_start\n")); -- return RPMRC_FAIL; -+ rpmlog(RPMLOG_ERR, _("reflink: unable to seek to table_start\n")); -+ return RPMRC_FAIL; - } - len = sizeof(state->keys); - if (Fread(&state->keys, len, 1, state->fd) != len) { -- rpmlog(RPMLOG_ERR, _("reflink: unable to read number of keys\n")); -- return RPMRC_FAIL; -+ rpmlog(RPMLOG_ERR, _("reflink: unable to read number of keys\n")); -+ return RPMRC_FAIL; - } - len = sizeof(state->keysize); - if (Fread(&state->keysize, len, 1, state->fd) != len) { -- rpmlog(RPMLOG_ERR, _("reflink: unable to read keysize\n")); -- return RPMRC_FAIL; -+ rpmlog(RPMLOG_ERR, _("reflink: unable to read keysize\n")); -+ return RPMRC_FAIL; - } -- rpmlog(RPMLOG_DEBUG, _("reflink: table_start=0x%lx, keys=%d, keysize=%d\n"), table_start, state->keys, state->keysize); -- // now get digest table if there is a reason to have one. -+ rpmlog( -+ RPMLOG_DEBUG, -+ _("reflink: table_start=0x%lx, keys=%d, keysize=%d\n"), -+ table_start, state->keys, state->keysize -+ ); -+ /* now get digest table if there is a reason to have one. */ - if (state->keys == 0 || state->keysize == 0) { -- // no files (or no digests(!)) -- state->table = NULL; -+ /* no files (or no digests(!)) */ -+ state->table = NULL; - } else { -- int table_size = state->keys * (state->keysize + sizeof(rpm_loff_t)); -- state->table = rcalloc(1, table_size); -- if (Fread(state->table, table_size, 1, state->fd) != table_size) { -- rpmlog(RPMLOG_ERR, _("reflink: unable to read table\n")); -- return RPMRC_FAIL; -- } -- state->inodeIndexes = inodeIndexHashCreate(state->keys, inodeId, inodeCmp, NULL, NULL); -+ int table_size = state->keys * (state->keysize + sizeof(rpm_loff_t)); -+ state->table = rcalloc(1, table_size); -+ if (Fread(state->table, table_size, 1, state->fd) != table_size) { -+ rpmlog(RPMLOG_ERR, _("reflink: unable to read table\n")); -+ return RPMRC_FAIL; -+ } -+ state->inodeIndexes = inodeIndexHashCreate( -+ state->keys, inodeId, inodeCmp, NULL, NULL -+ ); - } - -- // seek back to original location -- // might not be needed if we seek to offset immediately -+ /* Seek back to original location. -+ * Might not be needed if we seek to offset immediately -+ */ - if (Fseek(state->fd, current, SEEK_SET) < 0) { -- rpmlog(RPMLOG_ERR, _("reflink: unable to seek back to original location\n")); -- return RPMRC_FAIL; -+ rpmlog(RPMLOG_ERR, -+ _("reflink: unable to seek back to original location\n")); -+ return RPMRC_FAIL; - } - return RPMRC_OK; - } -@@ -192,40 +202,45 @@ static rpmRC reflink_psm_post(rpmPlugin plugin, rpmte te, int res) - reflink_state state = rpmPluginGetData(plugin); - state->files = rpmfilesFree(state->files); - if (state->table) { -- free(state->table); -- state->table = NULL; -+ free(state->table); -+ state->table = NULL; - } - if (state->inodeIndexes) { -- inodeIndexHashFree(state->inodeIndexes); -- state->inodeIndexes = NULL; -+ inodeIndexHashFree(state->inodeIndexes); -+ state->inodeIndexes = NULL; - } - return RPMRC_OK; - } - - --// have a prototype, warnings system -+/* have a prototype, warnings system */ - rpm_loff_t find(const unsigned char *digest, reflink_state state); - - rpm_loff_t find(const unsigned char *digest, reflink_state state) { - # if defined(__GNUC__) -- /* GCC nested function because bsearch's comparison function can't access -- state-keysize otherwise -- */ -- int cmpdigest(const void *k1, const void *k2) { -- rpmlog(RPMLOG_DEBUG, _("reflink: cmpdigest k1=%p k2=%p\n"), k1, k2); -- return memcmp(k1, k2, state->keysize); -- } -+ /* GCC nested function because bsearch's comparison function can't access -+ * state-keysize otherwise -+ */ -+ int cmpdigest(const void *k1, const void *k2) { -+ rpmlog(RPMLOG_DEBUG, _("reflink: cmpdigest k1=%p k2=%p\n"), k1, k2); -+ return memcmp(k1, k2, state->keysize); -+ } - # endif -- rpmlog(RPMLOG_DEBUG, _("reflink: bsearch(key=%p, base=%p, nmemb=%d, size=%lu)\n"), digest, state->table, state->keys, state->keysize + sizeof(rpm_loff_t)); -- char *entry = bsearch(digest, state->table, state->keys, state->keysize + sizeof(rpm_loff_t), cmpdigest); -- if (entry == NULL) { -- return NOT_FOUND; -- } -- rpm_loff_t offset = *(rpm_loff_t *)(entry + state->keysize); -- return offset; -+ rpmlog(RPMLOG_DEBUG, -+ _("reflink: bsearch(key=%p, base=%p, nmemb=%d, size=%lu)\n"), -+ digest, state->table, state->keys, -+ state->keysize + sizeof(rpm_loff_t)); -+ char *entry = bsearch(digest, state->table, state->keys, -+ state->keysize + sizeof(rpm_loff_t), cmpdigest); -+ if (entry == NULL) { -+ return NOT_FOUND; -+ } -+ rpm_loff_t offset = *(rpm_loff_t *)(entry + state->keysize); -+ return offset; - } - --static rpmRC reflink_fsm_file_pre(rpmPlugin plugin, rpmfi fi, const char* path, mode_t file_mode, rpmFsmOp op) -+static rpmRC reflink_fsm_file_pre(rpmPlugin plugin, rpmfi fi, const char* path, -+ mode_t file_mode, rpmFsmOp op) - { - struct file_clone_range fcr; - rpm_loff_t size; -@@ -234,99 +249,119 @@ static rpmRC reflink_fsm_file_pre(rpmPlugin plugin, rpmfi fi, const char* path, - - reflink_state state = rpmPluginGetData(plugin); - if (state->table == NULL) { -- // no table means rpm is not in reflink format, so leave. Now. -- return RPMRC_OK; -+ /* no table means rpm is not in reflink format, so leave. Now. */ -+ return RPMRC_OK; - } - if (op == FA_TOUCH) { -- // we're not overwriting an existing file -- return RPMRC_OK; -+ /* we're not overwriting an existing file. */ -+ return RPMRC_OK; - } - fcr.dest_offset = 0; - if (S_ISREG(file_mode) && !(rpmfiFFlags(fi) & RPMFILE_GHOST)) { -- rpm_ino_t inode = rpmfiFInode(fi); -- /* check for hard link entry in table. GetEntry overwrites hlix with the address of the first match */ -- if (inodeIndexHashGetEntry(state->inodeIndexes, inode, &hlix, NULL, NULL)) { -- // entry is in table, use hard link -- char *fn = rpmfilesFN(state->files, hlix[0]); -- if (link(fn, path) != 0) { -- rpmlog(RPMLOG_ERR, _("reflink: Unable to hard link %s -> %s due to %s\n"), fn, path, strerror(errno)); -- free(fn); -- return RPMRC_FAIL; -- } -- free(fn); -- return RPMRC_PLUGIN_CONTENTS; -- } -- /* if we didn't hard link, then we'll track this inode as being created soon */ -- if (rpmfiFNlink(fi) > 1) { -- /* minor optimization: only store files with more than one link */ -- inodeIndexHashAddEntry(state->inodeIndexes, inode, rpmfiFX(fi)); -- } -- /* derived from wfd_open in fsm.c */ -- mode_t old_umask = umask(0577); -- dst = open(path, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR); -- umask(old_umask); -- if (dst == -1) { -- rpmlog(RPMLOG_ERR, _("reflink: Unable to open %s for writing due to %s, flags = %x\n"), path, strerror(errno), rpmfiFFlags(fi)); -- return RPMRC_FAIL; -- } -- size = rpmfiFSize(fi); -- if (size > 0) { -- /* round src_length down to fundamental_block_size multiple */ -- fcr.src_length = size / state->fundamental_block_size * state->fundamental_block_size; -- if ((size % state->fundamental_block_size) > 0) { -- /* round up to next fundamental_block_size. We expect the data in the rpm to be similarly padded */ -- fcr.src_length += state->fundamental_block_size; -- } -- fcr.src_fd = Fileno(state->fd); -- if (fcr.src_fd == -1) { -- close(dst); -- rpmlog(RPMLOG_ERR, _("reflink: src fd lookup failed\n")); -- return RPMRC_FAIL; -- } -- fcr.src_offset = find(rpmfiFDigest(fi, NULL, NULL), state); -- if (fcr.src_offset == NOT_FOUND) { -- close(dst); -- rpmlog(RPMLOG_ERR, _("reflink: digest not found\n")); -- return RPMRC_FAIL; -- } -- rpmlog(RPMLOG_DEBUG, _("reflink: Reflinking %lu bytes at %lu to %s orig size=%lu, file=%ld\n"), fcr.src_length, fcr.src_offset, path, size, fcr.src_fd); -- rc = ioctl(dst, FICLONERANGE, &fcr); -- if (rc) { -- rpmlog(RPMLOG_WARNING, _("reflink: falling back to copying bits for %s due to %d, %d = %s\n"), path, rc, errno, strerror(errno)); -- if (Fseek(state->fd, fcr.src_offset, SEEK_SET) < 0) { -- close(dst); -- rpmlog(RPMLOG_ERR, _("reflink: unable to seek on copying bits\n")); -- return RPMRC_FAIL; -- } -- rpm_loff_t left = size; -- size_t len, read, written; -- while (left) { -- len = (left > BUFFER_SIZE ? BUFFER_SIZE : left); -- read = Fread(state->buffer, len, 1, state->fd); -- if (read != len) { -- close(dst); -- rpmlog(RPMLOG_ERR, _("reflink: short read on copying bits\n")); -- return RPMRC_FAIL; -- } -- written = write(dst, state->buffer, len); -- if (read != written) { -- close(dst); -- rpmlog(RPMLOG_ERR, _("reflink: short write on copying bits\n")); -- return RPMRC_FAIL; -- } -- left -= len; -- } -- } else { -- /* reflink worked, so truncate */ -- rc = ftruncate(dst, size); -- if (rc) { -- rpmlog(RPMLOG_ERR, _("reflink: Unable to truncate %s to %ld due to %s\n"), path, size, strerror(errno)); -- return RPMRC_FAIL; -- } -- } -- } -- close(dst); -- return RPMRC_PLUGIN_CONTENTS; -+ rpm_ino_t inode = rpmfiFInode(fi); -+ /* check for hard link entry in table. GetEntry overwrites hlix with -+ * the address of the first match. -+ */ -+ if (inodeIndexHashGetEntry(state->inodeIndexes, inode, &hlix, NULL, -+ NULL)) { -+ /* entry is in table, use hard link */ -+ char *fn = rpmfilesFN(state->files, hlix[0]); -+ if (link(fn, path) != 0) { -+ rpmlog(RPMLOG_ERR, -+ _("reflink: Unable to hard link %s -> %s due to %s\n"), -+ fn, path, strerror(errno)); -+ free(fn); -+ return RPMRC_FAIL; -+ } -+ free(fn); -+ return RPMRC_PLUGIN_CONTENTS; -+ } -+ /* if we didn't hard link, then we'll track this inode as being -+ * created soon -+ */ -+ if (rpmfiFNlink(fi) > 1) { -+ /* minor optimization: only store files with more than one link */ -+ inodeIndexHashAddEntry(state->inodeIndexes, inode, rpmfiFX(fi)); -+ } -+ /* derived from wfd_open in fsm.c */ -+ mode_t old_umask = umask(0577); -+ dst = open(path, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR); -+ umask(old_umask); -+ if (dst == -1) { -+ rpmlog(RPMLOG_ERR, -+ _("reflink: Unable to open %s for writing due to %s, flags = %x\n"), -+ path, strerror(errno), rpmfiFFlags(fi)); -+ return RPMRC_FAIL; -+ } -+ size = rpmfiFSize(fi); -+ if (size > 0) { -+ /* round src_length down to fundamental_block_size multiple */ -+ fcr.src_length = size / state->fundamental_block_size * state->fundamental_block_size; -+ if ((size % state->fundamental_block_size) > 0) { -+ /* round up to next fundamental_block_size. We expect the data -+ * in the rpm to be similarly padded. -+ */ -+ fcr.src_length += state->fundamental_block_size; -+ } -+ fcr.src_fd = Fileno(state->fd); -+ if (fcr.src_fd == -1) { -+ close(dst); -+ rpmlog(RPMLOG_ERR, _("reflink: src fd lookup failed\n")); -+ return RPMRC_FAIL; -+ } -+ fcr.src_offset = find(rpmfiFDigest(fi, NULL, NULL), state); -+ if (fcr.src_offset == NOT_FOUND) { -+ close(dst); -+ rpmlog(RPMLOG_ERR, _("reflink: digest not found\n")); -+ return RPMRC_FAIL; -+ } -+ rpmlog(RPMLOG_DEBUG, -+ _("reflink: Reflinking %lu bytes at %lu to %s orig size=%lu, file=%ld\n"), -+ fcr.src_length, fcr.src_offset, path, size, fcr.src_fd); -+ rc = ioctl(dst, FICLONERANGE, &fcr); -+ if (rc) { -+ rpmlog(RPMLOG_WARNING, -+ _("reflink: falling back to copying bits for %s due to %d, %d = %s\n"), -+ path, rc, errno, strerror(errno)); -+ if (Fseek(state->fd, fcr.src_offset, SEEK_SET) < 0) { -+ close(dst); -+ rpmlog(RPMLOG_ERR, -+ _("reflink: unable to seek on copying bits\n")); -+ return RPMRC_FAIL; -+ } -+ rpm_loff_t left = size; -+ size_t len, read, written; -+ while (left) { -+ len = (left > BUFFER_SIZE ? BUFFER_SIZE : left); -+ read = Fread(state->buffer, len, 1, state->fd); -+ if (read != len) { -+ close(dst); -+ rpmlog(RPMLOG_ERR, -+ _("reflink: short read on copying bits\n")); -+ return RPMRC_FAIL; -+ } -+ written = write(dst, state->buffer, len); -+ if (read != written) { -+ close(dst); -+ rpmlog(RPMLOG_ERR, -+ _("reflink: short write on copying bits\n")); -+ return RPMRC_FAIL; -+ } -+ left -= len; -+ } -+ } else { -+ /* reflink worked, so truncate */ -+ rc = ftruncate(dst, size); -+ if (rc) { -+ rpmlog(RPMLOG_ERR, -+ _("reflink: Unable to truncate %s to %ld due to %s\n"), -+ path, size, strerror(errno)); -+ return RPMRC_FAIL; -+ } -+ } -+ } -+ close(dst); -+ return RPMRC_PLUGIN_CONTENTS; - } - return RPMRC_OK; - } -diff --git a/rpm2extents.c b/rpm2extents.c -index 5662b86a6..c111be0a2 100644 ---- a/rpm2extents.c -+++ b/rpm2extents.c -@@ -24,7 +24,7 @@ - #include "debug.h" - - /* hash of void * (pointers) to file digests to offsets within output. -- The length of the key depends on what the FILEDIGESTALGO is. -+ * The length of the key depends on what the FILEDIGESTALGO is. - */ - #undef HASHTYPE - #undef HTKEYTYPE -@@ -34,7 +34,9 @@ - #include "lib/rpmhash.H" - #include "lib/rpmhash.C" - --/* magic value at end of file (64 bits) that indicates this is a transcoded rpm */ -+/* magic value at end of file (64 bits) that indicates this is a transcoded -+ * rpm. -+ */ - #define MAGIC 3472329499408095051 - - struct digestoffset { -@@ -64,77 +66,54 @@ static int digestor( - int algo; - rpmRC rc = RPMRC_FAIL; - -- for (algo = 0; algo < algos_len; algo++) -- { -- fdInitDigest(fdi, algos[algo], 0); -+ for (algo = 0; algo < algos_len; algo++) { -+ fdInitDigest(fdi, algos[algo], 0); - } - fdilength = ufdCopy(fdi, fdo); -- if (fdilength == -1) -- { -- fprintf(stderr, _("digest cat failed\n")); -- goto exit; -+ if (fdilength == -1) { -+ fprintf(stderr, _("digest cat failed\n")); -+ goto exit; - } - - len = sizeof(fdilength); -- if (Fwrite(&fdilength, len, 1, validationo) != len) -- { -- fprintf(stderr, _("Unable to write input length %zd\n"), fdilength); -- goto exit; -+ if (Fwrite(&fdilength, len, 1, validationo) != len) { -+ fprintf(stderr, _("Unable to write input length %zd\n"), fdilength); -+ goto exit; - } - len = sizeof(algos_len); -- if (Fwrite(&algos_len, len, 1, validationo) != len) -- { -- fprintf(stderr, _("Unable to write number of validation digests\n")); -- goto exit; -+ if (Fwrite(&algos_len, len, 1, validationo) != len) { -+ fprintf(stderr, _("Unable to write number of validation digests\n")); -+ goto exit; - } -- for (algo = 0; algo < algos_len; algo++) -- { -- fdFiniDigest(fdi, algos[algo], (void **)&filedigest, &filedigest_len, 0); -- -- algo_name = pgpValString(PGPVAL_HASHALGO, algos[algo]); -- algo_name_len = (uint32_t)strlen(algo_name); -- algo_digest_len = (uint32_t)filedigest_len; -- -- len = sizeof(algo_name_len); -- if (Fwrite(&algo_name_len, len, 1, validationo) != len) -- { -- fprintf( -- stderr, -- _("Unable to write validation algo name length\n") -- ); -- goto exit; -- } -- len = sizeof(algo_digest_len); -- if (Fwrite(&algo_digest_len, len, 1, validationo) != len) -- { -- fprintf( -- stderr, -- _("Unable to write number of bytes for validation digest\n") -- ); -- goto exit; -- } -- if (Fwrite(algo_name, algo_name_len, 1, validationo) != algo_name_len) -- { -- fprintf(stderr, _("Unable to write validation algo name\n")); -- goto exit; -- } -- if ( -- Fwrite( -- filedigest, -- algo_digest_len, -- 1, -- validationo -- ) != algo_digest_len -- ) -- { -- fprintf( -- stderr, -- _("Unable to write validation digest value %u, %zu\n"), -- algo_digest_len, -- filedigest_len -- ); -- goto exit; -- } -+ for (algo = 0; algo < algos_len; algo++) { -+ fdFiniDigest(fdi, algos[algo], (void **)&filedigest, &filedigest_len, 0); -+ -+ algo_name = pgpValString(PGPVAL_HASHALGO, algos[algo]); -+ algo_name_len = (uint32_t)strlen(algo_name); -+ algo_digest_len = (uint32_t)filedigest_len; -+ -+ len = sizeof(algo_name_len); -+ if (Fwrite(&algo_name_len, len, 1, validationo) != len) { -+ fprintf(stderr, -+ _("Unable to write validation algo name length\n")); -+ goto exit; -+ } -+ len = sizeof(algo_digest_len); -+ if (Fwrite(&algo_digest_len, len, 1, validationo) != len) { -+ fprintf(stderr, -+ _("Unable to write number of bytes for validation digest\n")); -+ goto exit; -+ } -+ if (Fwrite(algo_name, algo_name_len, 1, validationo) != algo_name_len) { -+ fprintf(stderr, _("Unable to write validation algo name\n")); -+ goto exit; -+ } -+ if (Fwrite(filedigest, algo_digest_len, 1, validationo ) != algo_digest_len) { -+ fprintf(stderr, -+ _("Unable to write validation digest value %u, %zu\n"), -+ algo_digest_len, filedigest_len); -+ goto exit; -+ } - } - rc = RPMRC_OK; - exit: -@@ -145,23 +124,20 @@ static rpmRC process_package(FD_t fdi, FD_t validationi) - { - uint32_t diglen; - /* GNU C extension: can use diglen from outer context */ -- int digestSetCmp(const unsigned char * a, const unsigned char * b) -- { -- return memcmp(a, b, diglen); -+ int digestSetCmp(const unsigned char * a, const unsigned char * b) { -+ return memcmp(a, b, diglen); - } - -- unsigned int digestSetHash(const unsigned char * digest) -- { -+ unsigned int digestSetHash(const unsigned char * digest) { - /* assumes sizeof(unsigned int) < diglen */ - return *(unsigned int *)digest; - } - -- int digestoffsetCmp(const void * a, const void * b) -- { -- return digestSetCmp( -- ((struct digestoffset *)a)->digest, -- ((struct digestoffset *)b)->digest -- ); -+ int digestoffsetCmp(const void * a, const void * b) { -+ return digestSetCmp( -+ ((struct digestoffset *)a)->digest, -+ ((struct digestoffset *)b)->digest -+ ); - } - - FD_t fdo; -@@ -179,65 +155,52 @@ static rpmRC process_package(FD_t fdi, FD_t validationi) - - fdo = fdDup(STDOUT_FILENO); - -- if (rpmReadPackageRaw(fdi, &sigh, &h)) -- { -- fprintf(stderr, _("Error reading package\n")); -- exit(EXIT_FAILURE); -+ if (rpmReadPackageRaw(fdi, &sigh, &h)) { -+ fprintf(stderr, _("Error reading package\n")); -+ exit(EXIT_FAILURE); - } - - if (rpmLeadWrite(fdo, h)) - { -- fprintf( -- stderr, -- _("Unable to write package lead: %s\n"), -- Fstrerror(fdo) -- ); -- exit(EXIT_FAILURE); -+ fprintf(stderr, _("Unable to write package lead: %s\n"), -+ Fstrerror(fdo)); -+ exit(EXIT_FAILURE); - } - -- if (rpmWriteSignature(fdo, sigh)) -- { -- fprintf(stderr, _("Unable to write signature: %s\n"), Fstrerror(fdo)); -- exit(EXIT_FAILURE); -+ if (rpmWriteSignature(fdo, sigh)) { -+ fprintf(stderr, _("Unable to write signature: %s\n"), Fstrerror(fdo)); -+ exit(EXIT_FAILURE); - } - -- if (headerWrite(fdo, h, HEADER_MAGIC_YES)) -- { -- fprintf(stderr, _("Unable to write headers: %s\n"), Fstrerror(fdo)); -- exit(EXIT_FAILURE); -+ if (headerWrite(fdo, h, HEADER_MAGIC_YES)) { -+ fprintf(stderr, _("Unable to write headers: %s\n"), Fstrerror(fdo)); -+ exit(EXIT_FAILURE); - } - - /* Retrieve payload size and compression type. */ -- { const char *compr = headerGetString(h, RPMTAG_PAYLOADCOMPRESSOR); -- rpmio_flags = rstrscat(NULL, "r.", compr ? compr : "gzip", NULL); -+ { -+ const char *compr = headerGetString(h, RPMTAG_PAYLOADCOMPRESSOR); -+ rpmio_flags = rstrscat(NULL, "r.", compr ? compr : "gzip", NULL); - } - - gzdi = Fdopen(fdi, rpmio_flags); /* XXX gzdi == fdi */ - free(rpmio_flags); - -- if (gzdi == NULL) -- { -- fprintf(stderr, _("cannot re-open payload: %s\n"), Fstrerror(gzdi)); -- exit(EXIT_FAILURE); -+ if (gzdi == NULL) { -+ fprintf(stderr, _("cannot re-open payload: %s\n"), Fstrerror(gzdi)); -+ exit(EXIT_FAILURE); - } - - rpmfiles files = rpmfilesNew(NULL, h, 0, RPMFI_KEEPHEADER); -- rpmfi fi = rpmfiNewArchiveReader( -- gzdi, -- files, -- RPMFI_ITER_READ_ARCHIVE_CONTENT_FIRST -- ); -+ rpmfi fi = rpmfiNewArchiveReader(gzdi, files, -+ RPMFI_ITER_READ_ARCHIVE_CONTENT_FIRST); - - /* this is encoded in the file format, so needs to be fixed size (for -- now?) -- */ -+ * now?) -+ */ - diglen = (uint32_t)rpmDigestLength(rpmfiDigestAlgo(fi)); -- digestSet ds = digestSetCreate( -- rpmfiFC(fi), -- digestSetHash, -- digestSetCmp, -- NULL -- ); -+ digestSet ds = digestSetCreate(rpmfiFC(fi), digestSetHash, digestSetCmp, -+ NULL); - struct digestoffset offsets[rpmfiFC(fi)]; - pos = RPMLEAD_SIZE + headerSizeof(sigh, HEADER_MAGIC_YES); - -@@ -247,139 +210,114 @@ static rpmRC process_package(FD_t fdi, FD_t validationi) - - zeros = xcalloc(fundamental_block_size, 1); - -- while (next >= 0) -- { -- next = rpmfiNext(fi); -- if (next == RPMERR_ITER_END) -- { -- rc = RPMRC_OK; -- break; -- } -- mode = rpmfiFMode(fi); -- if (!S_ISREG(mode) || !rpmfiArchiveHasContent(fi)) -- { -- /* not a regular file, or the archive doesn't contain any content for -- this entry -- */ -- continue; -- } -- digest = rpmfiFDigest(fi, NULL, NULL); -- if (digestSetGetEntry(ds, digest, NULL)) -- { -- /* This specific digest has already been included, so skip it */ -- continue; -- } -- pad = pad_to(pos, fundamental_block_size); -- if (Fwrite(zeros, sizeof(char), pad, fdo) != pad) -- { -- fprintf(stderr, _("Unable to write padding\n")); -- rc = RPMRC_FAIL; -- goto exit; -- } -- /* round up to next fundamental_block_size */ -- pos += pad; -- digestSetAddEntry(ds, digest); -- offsets[offset_ix].digest = digest; -- offsets[offset_ix].pos = pos; -- offset_ix++; -- size = rpmfiFSize(fi); -- rc = rpmfiArchiveReadToFile(fi, fdo, 0); -- if (rc != RPMRC_OK) -- { -- fprintf(stderr, _("rpmfiArchiveReadToFile failed with %d\n"), rc); -- goto exit; -- } -- pos += size; -+ while (next >= 0) { -+ next = rpmfiNext(fi); -+ if (next == RPMERR_ITER_END) { -+ rc = RPMRC_OK; -+ break; -+ } -+ mode = rpmfiFMode(fi); -+ if (!S_ISREG(mode) || !rpmfiArchiveHasContent(fi)) { -+ /* not a regular file, or the archive doesn't contain any content -+ * for this entry. -+ */ -+ continue; -+ } -+ digest = rpmfiFDigest(fi, NULL, NULL); -+ if (digestSetGetEntry(ds, digest, NULL)) { -+ /* This specific digest has already been included, so skip it. */ -+ continue; -+ } -+ pad = pad_to(pos, fundamental_block_size); -+ if (Fwrite(zeros, sizeof(char), pad, fdo) != pad) { -+ fprintf(stderr, _("Unable to write padding\n")); -+ rc = RPMRC_FAIL; -+ goto exit; -+ } -+ /* round up to next fundamental_block_size */ -+ pos += pad; -+ digestSetAddEntry(ds, digest); -+ offsets[offset_ix].digest = digest; -+ offsets[offset_ix].pos = pos; -+ offset_ix++; -+ size = rpmfiFSize(fi); -+ rc = rpmfiArchiveReadToFile(fi, fdo, 0); -+ if (rc != RPMRC_OK) { -+ fprintf(stderr, _("rpmfiArchiveReadToFile failed with %d\n"), rc); -+ goto exit; -+ } -+ pos += size; - } - Fclose(gzdi); /* XXX gzdi == fdi */ - -- qsort( -- offsets, -- (size_t)offset_ix, -- sizeof(struct digestoffset), -- digestoffsetCmp -- ); -+ qsort(offsets, (size_t)offset_ix, sizeof(struct digestoffset), -+ digestoffsetCmp); - - len = sizeof(offset_ix); -- if (Fwrite(&offset_ix, len, 1, fdo) != len) -- { -- fprintf(stderr, _("Unable to write length of table\n")); -- rc = RPMRC_FAIL; -- goto exit; -+ if (Fwrite(&offset_ix, len, 1, fdo) != len) { -+ fprintf(stderr, _("Unable to write length of table\n")); -+ rc = RPMRC_FAIL; -+ goto exit; - } - len = sizeof(diglen); -- if (Fwrite(&diglen, len, 1, fdo) != len) -- { -- fprintf(stderr, _("Unable to write length of digest\n")); -- rc = RPMRC_FAIL; -- goto exit; -+ if (Fwrite(&diglen, len, 1, fdo) != len) { -+ fprintf(stderr, _("Unable to write length of digest\n")); -+ rc = RPMRC_FAIL; -+ goto exit; - } - len = sizeof(rpm_loff_t); -- for (int x = 0; x < offset_ix; x++) -- { -- if (Fwrite(offsets[x].digest, diglen, 1, fdo) != diglen) -- { -- fprintf(stderr, _("Unable to write digest\n")); -- rc = RPMRC_FAIL; -- goto exit; -- } -- if (Fwrite(&offsets[x].pos, len, 1, fdo) != len) -- { -- fprintf(stderr, _("Unable to write offset\n")); -- rc = RPMRC_FAIL; -- goto exit; -- } -+ for (int x = 0; x < offset_ix; x++) { -+ if (Fwrite(offsets[x].digest, diglen, 1, fdo) != diglen) { -+ fprintf(stderr, _("Unable to write digest\n")); -+ rc = RPMRC_FAIL; -+ goto exit; -+ } -+ if (Fwrite(&offsets[x].pos, len, 1, fdo) != len) { -+ fprintf(stderr, _("Unable to write offset\n")); -+ rc = RPMRC_FAIL; -+ goto exit; -+ } - } - validation_pos = ( -- pos + sizeof(offset_ix) + sizeof(diglen) + -- offset_ix * (diglen + sizeof(rpm_loff_t)) -+ pos + sizeof(offset_ix) + sizeof(diglen) + -+ offset_ix * (diglen + sizeof(rpm_loff_t)) - ); - - ssize_t validation_len = ufdCopy(validationi, fdo); -- if (validation_len == -1) -- { -- fprintf(stderr, _("digest table ufdCopy failed\n")); -- rc = RPMRC_FAIL; -- goto exit; -+ if (validation_len == -1) { -+ fprintf(stderr, _("digest table ufdCopy failed\n")); -+ rc = RPMRC_FAIL; -+ goto exit; - } - /* add more padding so the last file can be cloned. It doesn't matter that -- the table and validation etc are in this space. In fact, it's pretty -- efficient if it is -+ * the table and validation etc are in this space. In fact, it's pretty -+ * efficient if it is. - */ - -- pad = pad_to( -- ( -- validation_pos + validation_len + 2 * sizeof(rpm_loff_t) + -- sizeof(uint64_t) -- ), -- fundamental_block_size -- ); -- if (Fwrite(zeros, sizeof(char), pad, fdo) != pad) -- { -- fprintf(stderr, _("Unable to write final padding\n")); -- rc = RPMRC_FAIL; -- goto exit; -+ pad = pad_to((validation_pos + validation_len + 2 * sizeof(rpm_loff_t) + -+ sizeof(uint64_t)), fundamental_block_size); -+ if (Fwrite(zeros, sizeof(char), pad, fdo) != pad) { -+ fprintf(stderr, _("Unable to write final padding\n")); -+ rc = RPMRC_FAIL; -+ goto exit; - } - zeros = _free(zeros); -- if (Fwrite(&pos, len, 1, fdo) != len) -- { -- fprintf(stderr, _("Unable to write offset of digest table\n")); -- rc = RPMRC_FAIL; -- goto exit; -+ if (Fwrite(&pos, len, 1, fdo) != len) { -+ fprintf(stderr, _("Unable to write offset of digest table\n")); -+ rc = RPMRC_FAIL; -+ goto exit; - } -- if (Fwrite(&validation_pos, len, 1, fdo) != len) -- { -- fprintf(stderr, _("Unable to write offset of validation table\n")); -- rc = RPMRC_FAIL; -- goto exit; -+ if (Fwrite(&validation_pos, len, 1, fdo) != len) { -+ fprintf(stderr, _("Unable to write offset of validation table\n")); -+ rc = RPMRC_FAIL; -+ goto exit; - } - uint64_t magic = MAGIC; - len = sizeof(magic); -- if (Fwrite(&magic, len, 1, fdo) != len) -- { -- fprintf(stderr, _("Unable to write magic\n")); -- rc = RPMRC_FAIL; -- goto exit; -+ if (Fwrite(&magic, len, 1, fdo) != len) { -+ fprintf(stderr, _("Unable to write magic\n")); -+ rc = RPMRC_FAIL; -+ goto exit; - } - - exit: -@@ -389,8 +327,7 @@ static rpmRC process_package(FD_t fdi, FD_t validationi) - return rc; - } - --int main(int argc, char *argv[]) --{ -+int main(int argc, char *argv[]) { - rpmRC rc; - int cprc = 0; - uint8_t algos[argc - 1]; -@@ -402,118 +339,95 @@ int main(int argc, char *argv[]) - xsetprogname(argv[0]); /* Portability call -- see system.h */ - rpmReadConfigFiles(NULL, NULL); - -- if (argc > 1 && (rstreq(argv[1], "-h") || rstreq(argv[1], "--help"))) -- { -- fprintf(stderr, _("Usage: %s [DIGESTALGO]...\n"), argv[0]); -- exit(EXIT_FAILURE); -+ if (argc > 1 && (rstreq(argv[1], "-h") || rstreq(argv[1], "--help"))) { -+ fprintf(stderr, _("Usage: %s [DIGESTALGO]...\n"), argv[0]); -+ exit(EXIT_FAILURE); - } - -- if (argc == 1) -- { -- fprintf( -- stderr, -- _("Need at least one DIGESTALGO parameter, e.g. 'SHA256'\n") -- ); -- exit(EXIT_FAILURE); -+ if (argc == 1) { -+ fprintf(stderr, -+ _("Need at least one DIGESTALGO parameter, e.g. 'SHA256'\n")); -+ exit(EXIT_FAILURE); - } - -- for (int x = 0; x < (argc - 1); x++) -- { -- if (pgpStringVal(PGPVAL_HASHALGO, argv[x + 1], &algos[x]) != 0) -- { -- fprintf( -- stderr, -- _("Unable to resolve '%s' as a digest algorithm, exiting\n"), -- argv[x + 1] -- ); -- exit(EXIT_FAILURE); -- } -+ for (int x = 0; x < (argc - 1); x++) { -+ if (pgpStringVal(PGPVAL_HASHALGO, argv[x + 1], &algos[x]) != 0) -+ { -+ fprintf(stderr, -+ _("Unable to resolve '%s' as a digest algorithm, exiting\n"), -+ argv[x + 1]); -+ exit(EXIT_FAILURE); -+ } - } - - -- if (pipe(mainpipefd) == -1) -- { -- fprintf(stderr, _("Main pipe failure\n")); -- exit(EXIT_FAILURE); -+ if (pipe(mainpipefd) == -1) { -+ fprintf(stderr, _("Main pipe failure\n")); -+ exit(EXIT_FAILURE); - } -- if (pipe(metapipefd) == -1) -- { -- fprintf(stderr, _("Meta pipe failure\n")); -- exit(EXIT_FAILURE); -+ if (pipe(metapipefd) == -1) { -+ fprintf(stderr, _("Meta pipe failure\n")); -+ exit(EXIT_FAILURE); - } - cpid = fork(); -- if (cpid == 0) -- { -- /* child: digestor */ -- close(mainpipefd[0]); -- close(metapipefd[0]); -- FD_t fdi = fdDup(STDIN_FILENO); -- FD_t fdo = fdDup(mainpipefd[1]); -- FD_t validationo = fdDup(metapipefd[1]); -- rc = digestor(fdi, fdo, validationo, algos, argc - 1); -- Fclose(validationo); -- Fclose(fdo); -- Fclose(fdi); -+ if (cpid == 0) { -+ /* child: digestor */ -+ close(mainpipefd[0]); -+ close(metapipefd[0]); -+ FD_t fdi = fdDup(STDIN_FILENO); -+ FD_t fdo = fdDup(mainpipefd[1]); -+ FD_t validationo = fdDup(metapipefd[1]); -+ rc = digestor(fdi, fdo, validationo, algos, argc - 1); -+ Fclose(validationo); -+ Fclose(fdo); -+ Fclose(fdi); - } else { -- /* parent: main program */ -- close(mainpipefd[1]); -- close(metapipefd[1]); -- FD_t fdi = fdDup(mainpipefd[0]); -- FD_t validationi = fdDup(metapipefd[0]); -- rc = process_package(fdi, validationi); -- Fclose(validationi); -- /* fdi is normally closed through the stacked file gzdi in the function. */ -- /* wait for child process (digestor for stdin) to complete. */ -- if (rc != RPMRC_OK) -- { -- if (kill(cpid, SIGTERM) != 0) -- { -- fprintf( -- stderr, -- _("Failed to kill digest process when main process failed: %s\n"), -- strerror(errno) -- ); -- } -- } -- w = waitpid(cpid, &wstatus, 0); -- if (w == -1) -- { -- fprintf(stderr, _("waitpid failed\n")); -- cprc = EXIT_FAILURE; -- } else if (WIFEXITED(wstatus)) -- { -- cprc = WEXITSTATUS(wstatus); -- if (cprc != 0) -- { -- fprintf( -- stderr, -- _("Digest process non-zero exit code %d\n"), -- cprc -- ); -- } -- } else if (WIFSIGNALED(wstatus)) -- { -- fprintf( -- stderr, -- _("Digest process was terminated with a signal: %d\n"), -- WTERMSIG(wstatus) -- ); -- cprc = EXIT_FAILURE; -- } else -- { -- /* don't think this can happen, but covering all bases */ -- fprintf(stderr, _("Unhandled circumstance in waitpid\n")); -- cprc = EXIT_FAILURE; -- } -- if (cprc != EXIT_SUCCESS) -- { -- rc = RPMRC_FAIL; -- } -+ /* parent: main program */ -+ close(mainpipefd[1]); -+ close(metapipefd[1]); -+ FD_t fdi = fdDup(mainpipefd[0]); -+ FD_t validationi = fdDup(metapipefd[0]); -+ rc = process_package(fdi, validationi); -+ Fclose(validationi); -+ /* fdi is normally closed through the stacked file gzdi in the -+ * function. -+ * Wait for child process (digestor for stdin) to complete. -+ */ -+ if (rc != RPMRC_OK) { -+ if (kill(cpid, SIGTERM) != 0) { -+ fprintf(stderr, -+ _("Failed to kill digest process when main process failed: %s\n"), -+ strerror(errno)); -+ } -+ } -+ w = waitpid(cpid, &wstatus, 0); -+ if (w == -1) { -+ fprintf(stderr, _("waitpid failed\n")); -+ cprc = EXIT_FAILURE; -+ } else if (WIFEXITED(wstatus)) { -+ cprc = WEXITSTATUS(wstatus); -+ if (cprc != 0) { -+ fprintf(stderr, -+ _("Digest process non-zero exit code %d\n"), -+ cprc); -+ } -+ } else if (WIFSIGNALED(wstatus)) { -+ fprintf(stderr, -+ _("Digest process was terminated with a signal: %d\n"), -+ WTERMSIG(wstatus)); -+ cprc = EXIT_FAILURE; -+ } else { -+ /* Don't think this can happen, but covering all bases */ -+ fprintf(stderr, _("Unhandled circumstance in waitpid\n")); -+ cprc = EXIT_FAILURE; -+ } -+ if (cprc != EXIT_SUCCESS) { -+ rc = RPMRC_FAIL; -+ } - } -- if (rc != RPMRC_OK) -- { -- /* translate rpmRC into generic failure return code. */ -- return EXIT_FAILURE; -+ if (rc != RPMRC_OK) { -+ /* translate rpmRC into generic failure return code. */ -+ return EXIT_FAILURE; - } - return EXIT_SUCCESS; - } - -From 19694b76508d83bc83201441ff2c2721d45c4d1d Mon Sep 17 00:00:00 2001 -From: Matthew Almond -Date: Sun, 31 Jan 2021 15:24:25 -0800 -Subject: [PATCH 4/4] Fix printf formatting in reflink.c - -There were some mismatches on field "sizes". This should eliminate the -error messages. ---- - plugins/reflink.c | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/plugins/reflink.c b/plugins/reflink.c -index 9eaa87094..513887604 100644 ---- a/plugins/reflink.c -+++ b/plugins/reflink.c -@@ -316,7 +316,7 @@ static rpmRC reflink_fsm_file_pre(rpmPlugin plugin, rpmfi fi, const char* path, - return RPMRC_FAIL; - } - rpmlog(RPMLOG_DEBUG, -- _("reflink: Reflinking %lu bytes at %lu to %s orig size=%lu, file=%ld\n"), -+ _("reflink: Reflinking %llu bytes at %llu to %s orig size=%ld, file=%lld\n"), - fcr.src_length, fcr.src_offset, path, size, fcr.src_fd); - rc = ioctl(dst, FICLONERANGE, &fcr); - if (rc) { diff --git a/SOURCES/master...cow.diff b/SOURCES/master...cow.diff new file mode 100644 index 0000000..0e4160c --- /dev/null +++ b/SOURCES/master...cow.diff @@ -0,0 +1,1176 @@ +diff --git a/Makefile.am b/Makefile.am +index e5c75d7b4..288668819 100644 +--- a/Makefile.am ++++ b/Makefile.am +@@ -99,7 +99,7 @@ pkginclude_HEADERS += build/rpmfc.h + pkginclude_HEADERS += build/rpmspec.h + + +-bin_PROGRAMS = rpm rpm2cpio rpmbuild rpmdb rpmkeys rpmsign rpmspec ++bin_PROGRAMS = rpm rpm2cpio rpmbuild rpmdb rpmkeys rpmsign rpmspec rpm2extents + if WITH_ARCHIVE + bin_PROGRAMS += rpm2archive + endif +@@ -154,6 +154,10 @@ rpm2cpio_SOURCES = rpm2cpio.c debug.h system.h + rpm2cpio_LDADD = lib/librpm.la rpmio/librpmio.la + rpm2cpio_LDADD += @WITH_POPT_LIB@ + ++rpm2extents_SOURCES = rpm2extents.c debug.h system.h ++rpm2extents_LDADD = lib/librpm.la rpmio/librpmio.la ++rpm2extents_LDADD += @WITH_POPT_LIB@ ++ + rpm2archive_SOURCES = rpm2archive.c debug.h system.h + rpm2archive_LDADD = lib/librpm.la rpmio/librpmio.la + rpm2archive_LDADD += @WITH_POPT_LIB@ @WITH_ARCHIVE_LIB@ +diff --git a/lib/depends.c b/lib/depends.c +index 30234df3d..8998afcd3 100644 +--- a/lib/depends.c ++++ b/lib/depends.c +@@ -81,6 +81,8 @@ static rpmRC headerCheckPayloadFormat(Header h) { + */ + if (!payloadfmt) return rc; + ++ if (rstreq(payloadfmt, "clon")) return rc; ++ + if (!rstreq(payloadfmt, "cpio")) { + char *nevra = headerGetAsString(h, RPMTAG_NEVRA); + if (payloadfmt && rstreq(payloadfmt, "drpm")) { +diff --git a/lib/fsm.c b/lib/fsm.c +index 935a0a5c6..feda3750c 100644 +--- a/lib/fsm.c ++++ b/lib/fsm.c +@@ -19,6 +19,7 @@ + + #include "rpmio/rpmio_internal.h" /* fdInit/FiniDigest */ + #include "lib/fsm.h" ++#include "lib/rpmlib.h" + #include "lib/rpmte_internal.h" /* XXX rpmfs */ + #include "lib/rpmplugins.h" /* rpm plugins hooks */ + #include "lib/rpmug.h" +@@ -52,6 +53,7 @@ struct filedata_s { + int stage; + int setmeta; + int skip; ++ int plugin_contents; + rpmFileAction action; + const char *suffix; + char *fpath; +@@ -891,6 +893,14 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + struct filedata_s *fdata = xcalloc(fc, sizeof(*fdata)); + struct filedata_s *firstlink = NULL; + ++ Header h = rpmteHeader(te); ++ const char *payloadfmt = headerGetString(h, RPMTAG_PAYLOADFORMAT); ++ int cpio = 1; ++ ++ if (payloadfmt && rstreq(payloadfmt, "clon")) { ++ cpio = 0; ++ } ++ + /* transaction id used for temporary path suffix while installing */ + rasprintf(&tid, ";%08x", (unsigned)rpmtsGetTid(ts)); + +@@ -911,12 +921,23 @@ 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); + + /* Run fsm file pre hook for all plugins */ + rc = rpmpluginsCallFsmFilePre(plugins, fi, fp->fpath, + fp->sb.st_mode, fp->action); ++ fp->plugin_contents = 0; ++ switch (rc) { ++ case RPMRC_OK: ++ setFileState(fs, fx); ++ break; ++ case RPMRC_PLUGIN_CONTENTS: ++ fp->plugin_contents = 1; ++ // reduce reads on cpio to this value. Could be zero if ++ // this is from a hard link. ++ rc = RPMRC_OK; ++ break; ++ } + fp->stage = FILE_PRE; + } + fi = rpmfiFree(fi); +@@ -924,10 +945,14 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + if (rc) + goto exit; + +- fi = rpmfiNewArchiveReader(payload, files, RPMFI_ITER_READ_ARCHIVE); +- if (fi == NULL) { +- rc = RPMERR_BAD_MAGIC; +- goto exit; ++ if (cpio) { ++ fi = rpmfiNewArchiveReader(payload, files, RPMFI_ITER_READ_ARCHIVE); ++ if (fi == NULL) { ++ rc = RPMERR_BAD_MAGIC; ++ goto exit; ++ } ++ } else { ++ fi = rpmfilesIter(files, RPMFI_ITER_FWD); + } + + /* Detect and create directories not explicitly in package. */ +@@ -969,8 +994,12 @@ 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, +- &firstlink, &firstlinkfile); ++ if(fp->plugin_contents) { ++ rc = RPMRC_OK; ++ }else { ++ rc = fsmMkfile(fi, fp, files, psm, nodigest, ++ &firstlink, &firstlinkfile); ++ } + } + } else if (S_ISDIR(fp->sb.st_mode)) { + if (rc == RPMERR_ENOENT) { +@@ -1078,6 +1107,7 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, + rpmswAdd(rpmtsOp(ts, RPMTS_OP_DIGEST), fdOp(payload, FDSTAT_DIGEST)); + + exit: ++ h = headerFree(h); + fi = rpmfiFree(fi); + Fclose(payload); + free(tid); +diff --git a/lib/package.c b/lib/package.c +index 281275029..90bd0d8a7 100644 +--- a/lib/package.c ++++ b/lib/package.c +@@ -404,5 +404,45 @@ rpmRC rpmReadPackageFile(rpmts ts, FD_t fd, const char * fn, Header * hdrp) + return rc; + } + ++rpmRC rpmReadPackageRaw(FD_t fd, Header * sigp, Header * hdrp) ++{ ++ char *msg = NULL; ++ hdrblob sigblob = hdrblobCreate(); ++ hdrblob blob = hdrblobCreate(); ++ Header h = NULL; ++ Header sigh = NULL; ++ ++ rpmRC rc = rpmLeadRead(fd, &msg); ++ if (rc != RPMRC_OK) ++ goto exit; ++ ++ rc = hdrblobRead(fd, 1, 0, RPMTAG_HEADERSIGNATURES, sigblob, &msg); ++ if (rc != RPMRC_OK) ++ goto exit; ++ ++ rc = hdrblobRead(fd, 1, 1, RPMTAG_HEADERIMMUTABLE, blob, &msg); ++ if (rc != RPMRC_OK) ++ goto exit; ++ ++ rc = hdrblobImport(sigblob, 0, &sigh, &msg); ++ if (rc) ++ goto exit; + ++ rc = hdrblobImport(blob, 0, &h, &msg); ++ if (rc) ++ goto exit; + ++ *sigp = headerLink(sigh); ++ *hdrp = headerLink(h); ++ ++exit: ++ if (rc != RPMRC_OK && msg) ++ rpmlog(RPMLOG_ERR, "%s: %s\n", Fdescr(fd), msg); ++ hdrblobFree(sigblob); ++ hdrblobFree(blob); ++ headerFree(sigh); ++ headerFree(h); ++ free(msg); ++ ++ return rc; ++} +diff --git a/lib/rpmlib.h b/lib/rpmlib.h +index 0879d04e5..a09ba0daf 100644 +--- a/lib/rpmlib.h ++++ b/lib/rpmlib.h +@@ -155,6 +155,15 @@ rpmRC rpmReadHeader(rpmts ts, FD_t fd, Header *hdrp, char ** msg); + rpmRC rpmReadPackageFile(rpmts ts, FD_t fd, + const char * fn, Header * hdrp); + ++/** \ingroup header ++ * Return package signature, header from file handle, no verification. ++ * @param fd file handle ++ * @param[out] sigp address of header (or NULL) ++ * @param[out] hdrp address of header (or NULL) ++ * @return RPMRC_OK on success ++ */ ++rpmRC rpmReadPackageRaw(FD_t fd, Header * sigp, Header * hdrp); ++ + /** \ingroup rpmtrans + * Install source package. + * @param ts transaction set +diff --git a/lib/rpmplugins.c b/lib/rpmplugins.c +index 62d75c4cf..3da3097af 100644 +--- a/lib/rpmplugins.c ++++ b/lib/rpmplugins.c +@@ -356,13 +356,28 @@ rpmRC rpmpluginsCallFsmFilePre(rpmPlugins plugins, rpmfi fi, const char *path, + plugin_fsm_file_pre_func hookFunc; + int i; + rpmRC rc = RPMRC_OK; ++ rpmRC hook_rc; + + for (i = 0; i < plugins->count; i++) { + rpmPlugin plugin = plugins->plugins[i]; + RPMPLUGINS_SET_HOOK_FUNC(fsm_file_pre); +- if (hookFunc && hookFunc(plugin, fi, path, file_mode, op) == RPMRC_FAIL) { +- rpmlog(RPMLOG_ERR, "Plugin %s: hook fsm_file_pre failed\n", plugin->name); +- rc = RPMRC_FAIL; ++ if (hookFunc) { ++ hook_rc = hookFunc(plugin, fi, path, file_mode, op); ++ if (hook_rc == RPMRC_FAIL) { ++ rpmlog(RPMLOG_ERR, "Plugin %s: hook fsm_file_pre failed\n", plugin->name); ++ rc = RPMRC_FAIL; ++ } else if (hook_rc == RPMRC_PLUGIN_CONTENTS && rc != RPMRC_FAIL) { ++ if (rc == RPMRC_PLUGIN_CONTENTS) { ++ /* Another plugin already said it'd handle contents. It's ++ * undefined how these would combine, so treat this as a ++ * failure condition. ++ */ ++ rc = RPMRC_FAIL; ++ } else { ++ /* Plugin will handle content */ ++ rc = RPMRC_PLUGIN_CONTENTS; ++ } ++ } + } + } + +diff --git a/lib/rpmte.c b/lib/rpmte.c +index 3663604e7..d43dc41ad 100644 +--- a/lib/rpmte.c ++++ b/lib/rpmte.c +@@ -423,6 +423,11 @@ FD_t rpmteSetFd(rpmte te, FD_t fd) + return NULL; + } + ++FD_t rpmteFd(rpmte te) ++{ ++ return (te != NULL ? te->fd : NULL); ++} ++ + fnpyKey rpmteKey(rpmte te) + { + return (te != NULL ? te->key : NULL); +diff --git a/lib/rpmte.h b/lib/rpmte.h +index 81acf7a19..6fc0a9f91 100644 +--- a/lib/rpmte.h ++++ b/lib/rpmte.h +@@ -209,6 +209,8 @@ const char * rpmteNEVR(rpmte te); + */ + const char * rpmteNEVRA(rpmte te); + ++FD_t rpmteFd(rpmte te); ++ + /** \ingroup rpmte + * Retrieve key from transaction element. + * @param te transaction element +diff --git a/lib/rpmtypes.h b/lib/rpmtypes.h +index e8e69b506..af2611e9e 100644 +--- a/lib/rpmtypes.h ++++ b/lib/rpmtypes.h +@@ -106,7 +106,8 @@ typedef enum rpmRC_e { + RPMRC_NOTFOUND = 1, /*!< Generic not found code. */ + RPMRC_FAIL = 2, /*!< Generic failure code. */ + RPMRC_NOTTRUSTED = 3, /*!< Signature is OK, but key is not trusted. */ +- RPMRC_NOKEY = 4 /*!< Public key is unavailable. */ ++ RPMRC_NOKEY = 4, /*!< Public key is unavailable. */ ++ RPMRC_PLUGIN_CONTENTS = 5 /*!< fsm_file_pre plugin is handling content */ + } rpmRC; + + #ifdef __cplusplus +diff --git a/macros.in b/macros.in +index e90cefa9a..363252b0f 100644 +--- a/macros.in ++++ b/macros.in +@@ -1143,6 +1143,7 @@ package or when debugging this package.\ + + # Transaction plugin macros + %__plugindir %{_libdir}/rpm-plugins ++%__transaction_reflink %{__plugindir}/reflink.so + %__transaction_systemd_inhibit %{__plugindir}/systemd_inhibit.so + %__transaction_selinux %{__plugindir}/selinux.so + %__transaction_syslog %{__plugindir}/syslog.so +diff --git a/plugins/Makefile.am b/plugins/Makefile.am +index 3a929d0ce..ad0d3bce7 100644 +--- a/plugins/Makefile.am ++++ b/plugins/Makefile.am +@@ -42,6 +42,10 @@ prioreset_la_SOURCES = prioreset.c + prioreset_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmio.la + plugins_LTLIBRARIES += prioreset.la + ++reflink_la_SOURCES = reflink.c ++reflink_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmio.la ++plugins_LTLIBRARIES += reflink.la ++ + syslog_la_SOURCES = syslog.c + syslog_la_LIBADD = $(top_builddir)/lib/librpm.la $(top_builddir)/rpmio/librpmio.la + plugins_LTLIBRARIES += syslog.la +diff --git a/plugins/reflink.c b/plugins/reflink.c +new file mode 100644 +index 000000000..513887604 +--- /dev/null ++++ b/plugins/reflink.c +@@ -0,0 +1,375 @@ ++#include "system.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#if defined(__linux__) ++#include /* For FICLONE */ ++#endif ++ ++#include ++#include "lib/rpmlib.h" ++#include "lib/rpmplugin.h" ++#include "lib/rpmte_internal.h" ++#include ++#include "rpmio/rpmio_internal.h" ++ ++ ++#include "debug.h" ++ ++#include ++ ++/* use hash table to remember inode -> ix (for rpmfilesFN(ix)) lookups */ ++#undef HASHTYPE ++#undef HTKEYTYPE ++#undef HTDATATYPE ++#define HASHTYPE inodeIndexHash ++#define HTKEYTYPE rpm_ino_t ++#define HTDATATYPE int ++#include "lib/rpmhash.H" ++#include "lib/rpmhash.C" ++ ++/* We use this in find to indicate a key wasn't found. This is an ++ * unrecoverable error, but we can at least show a decent error. 0 is never a ++ * valid offset because it's the offset of the start of the file. ++ */ ++#define NOT_FOUND 0 ++ ++#define BUFFER_SIZE (1024 * 128) ++ ++/* magic value at end of file (64 bits) that indicates this is a transcoded ++ * rpm. ++ */ ++#define MAGIC 3472329499408095051 ++ ++struct reflink_state_s { ++ /* Stuff that's used across rpms */ ++ long fundamental_block_size; ++ char *buffer; ++ ++ /* stuff that's used/updated per psm */ ++ uint32_t keys, keysize; ++ ++ /* table for current rpm, keys * (keysize + sizeof(rpm_loff_t)) */ ++ unsigned char *table; ++ FD_t fd; ++ rpmfiles files; ++ inodeIndexHash inodeIndexes; ++}; ++ ++typedef struct reflink_state_s * reflink_state; ++ ++static int inodeCmp(rpm_ino_t a, rpm_ino_t b) ++{ ++ return (a != b); ++} ++ ++static unsigned int inodeId(rpm_ino_t a) ++{ ++ /* rpm_ino_t is uint32_t so maps safely to unsigned int */ ++ return (unsigned int)a; ++} ++ ++static rpmRC reflink_init(rpmPlugin plugin, rpmts ts) { ++ reflink_state state = rcalloc(1, sizeof(struct reflink_state_s)); ++ ++ /* IOCTL-FICLONERANGE(2): ...Disk filesystems generally require the offset ++ * and length arguments to be aligned to the fundamental block size. ++ * ++ * The value of "fundamental block size" is directly related to the ++ * system's page size, so we should use that. ++ */ ++ state->fundamental_block_size = sysconf(_SC_PAGESIZE); ++ state->buffer = rcalloc(1, BUFFER_SIZE); ++ rpmPluginSetData(plugin, state); ++ ++ return RPMRC_OK; ++} ++ ++static void reflink_cleanup(rpmPlugin plugin) { ++ reflink_state state = rpmPluginGetData(plugin); ++ free(state->buffer); ++ free(state); ++} ++ ++static rpmRC reflink_psm_pre(rpmPlugin plugin, rpmte te) { ++ reflink_state state = rpmPluginGetData(plugin); ++ state->fd = rpmteFd(te); ++ if (state->fd == 0) { ++ rpmlog(RPMLOG_DEBUG, _("reflink: fd = 0, no install\n")); ++ return RPMRC_OK; ++ } ++ rpm_loff_t current = Ftell(state->fd); ++ uint64_t magic; ++ if (Fseek(state->fd, -(sizeof(magic)), SEEK_END) < 0) { ++ rpmlog(RPMLOG_ERR, _("reflink: failed to seek for magic\n")); ++ if (Fseek(state->fd, current, SEEK_SET) < 0) { ++ /* yes this gets a bit repetitive */ ++ rpmlog(RPMLOG_ERR, ++ _("reflink: unable to seek back to original location\n")); ++ } ++ return RPMRC_FAIL; ++ } ++ size_t len = sizeof(magic); ++ if (Fread(&magic, len, 1, state->fd) != len) { ++ rpmlog(RPMLOG_ERR, _("reflink: unable to read magic\n")); ++ if (Fseek(state->fd, current, SEEK_SET) < 0) { ++ rpmlog(RPMLOG_ERR, ++ _("reflink: unable to seek back to original location\n")); ++ } ++ return RPMRC_FAIL; ++ } ++ if (magic != MAGIC) { ++ rpmlog(RPMLOG_DEBUG, _("reflink: not transcoded\n")); ++ if (Fseek(state->fd, current, SEEK_SET) < 0) { ++ rpmlog(RPMLOG_ERR, ++ _("reflink: unable to seek back to original location\n")); ++ return RPMRC_FAIL; ++ } ++ return RPMRC_OK; ++ } ++ rpmlog(RPMLOG_DEBUG, _("reflink: *is* transcoded\n")); ++ Header h = rpmteHeader(te); ++ ++ /* replace/add header that main fsm.c can read */ ++ headerDel(h, RPMTAG_PAYLOADFORMAT); ++ headerPutString(h, RPMTAG_PAYLOADFORMAT, "clon"); ++ headerFree(h); ++ state->files = rpmteFiles(te); ++ /* tail of file contains offset_table, offset_checksums then magic */ ++ if (Fseek(state->fd, -(sizeof(rpm_loff_t) * 2 + sizeof(magic)), SEEK_END) < 0) { ++ rpmlog(RPMLOG_ERR, _("reflink: failed to seek for tail %p\n"), ++ state->fd); ++ return RPMRC_FAIL; ++ } ++ rpm_loff_t table_start; ++ len = sizeof(table_start); ++ if (Fread(&table_start, len, 1, state->fd) != len) { ++ rpmlog(RPMLOG_ERR, _("reflink: unable to read table_start\n")); ++ return RPMRC_FAIL; ++ } ++ if (Fseek(state->fd, table_start, SEEK_SET) < 0) { ++ rpmlog(RPMLOG_ERR, _("reflink: unable to seek to table_start\n")); ++ return RPMRC_FAIL; ++ } ++ len = sizeof(state->keys); ++ if (Fread(&state->keys, len, 1, state->fd) != len) { ++ rpmlog(RPMLOG_ERR, _("reflink: unable to read number of keys\n")); ++ return RPMRC_FAIL; ++ } ++ len = sizeof(state->keysize); ++ if (Fread(&state->keysize, len, 1, state->fd) != len) { ++ rpmlog(RPMLOG_ERR, _("reflink: unable to read keysize\n")); ++ return RPMRC_FAIL; ++ } ++ rpmlog( ++ RPMLOG_DEBUG, ++ _("reflink: table_start=0x%lx, keys=%d, keysize=%d\n"), ++ table_start, state->keys, state->keysize ++ ); ++ /* now get digest table if there is a reason to have one. */ ++ if (state->keys == 0 || state->keysize == 0) { ++ /* no files (or no digests(!)) */ ++ state->table = NULL; ++ } else { ++ int table_size = state->keys * (state->keysize + sizeof(rpm_loff_t)); ++ state->table = rcalloc(1, table_size); ++ if (Fread(state->table, table_size, 1, state->fd) != table_size) { ++ rpmlog(RPMLOG_ERR, _("reflink: unable to read table\n")); ++ return RPMRC_FAIL; ++ } ++ state->inodeIndexes = inodeIndexHashCreate( ++ state->keys, inodeId, inodeCmp, NULL, NULL ++ ); ++ } ++ ++ /* Seek back to original location. ++ * Might not be needed if we seek to offset immediately ++ */ ++ if (Fseek(state->fd, current, SEEK_SET) < 0) { ++ rpmlog(RPMLOG_ERR, ++ _("reflink: unable to seek back to original location\n")); ++ return RPMRC_FAIL; ++ } ++ return RPMRC_OK; ++} ++ ++static rpmRC reflink_psm_post(rpmPlugin plugin, rpmte te, int res) ++{ ++ reflink_state state = rpmPluginGetData(plugin); ++ state->files = rpmfilesFree(state->files); ++ if (state->table) { ++ free(state->table); ++ state->table = NULL; ++ } ++ if (state->inodeIndexes) { ++ inodeIndexHashFree(state->inodeIndexes); ++ state->inodeIndexes = NULL; ++ } ++ return RPMRC_OK; ++} ++ ++ ++/* have a prototype, warnings system */ ++rpm_loff_t find(const unsigned char *digest, reflink_state state); ++ ++rpm_loff_t find(const unsigned char *digest, reflink_state state) { ++# if defined(__GNUC__) ++ /* GCC nested function because bsearch's comparison function can't access ++ * state-keysize otherwise ++ */ ++ int cmpdigest(const void *k1, const void *k2) { ++ rpmlog(RPMLOG_DEBUG, _("reflink: cmpdigest k1=%p k2=%p\n"), k1, k2); ++ return memcmp(k1, k2, state->keysize); ++ } ++# endif ++ rpmlog(RPMLOG_DEBUG, ++ _("reflink: bsearch(key=%p, base=%p, nmemb=%d, size=%lu)\n"), ++ digest, state->table, state->keys, ++ state->keysize + sizeof(rpm_loff_t)); ++ char *entry = bsearch(digest, state->table, state->keys, ++ state->keysize + sizeof(rpm_loff_t), cmpdigest); ++ if (entry == NULL) { ++ return NOT_FOUND; ++ } ++ rpm_loff_t offset = *(rpm_loff_t *)(entry + state->keysize); ++ return offset; ++} ++ ++static rpmRC reflink_fsm_file_pre(rpmPlugin plugin, rpmfi fi, const char* path, ++ mode_t file_mode, rpmFsmOp op) ++{ ++ struct file_clone_range fcr; ++ rpm_loff_t size; ++ int dst, rc; ++ int *hlix; ++ ++ reflink_state state = rpmPluginGetData(plugin); ++ if (state->table == NULL) { ++ /* no table means rpm is not in reflink format, so leave. Now. */ ++ return RPMRC_OK; ++ } ++ if (op == FA_TOUCH) { ++ /* we're not overwriting an existing file. */ ++ return RPMRC_OK; ++ } ++ fcr.dest_offset = 0; ++ if (S_ISREG(file_mode) && !(rpmfiFFlags(fi) & RPMFILE_GHOST)) { ++ rpm_ino_t inode = rpmfiFInode(fi); ++ /* check for hard link entry in table. GetEntry overwrites hlix with ++ * the address of the first match. ++ */ ++ if (inodeIndexHashGetEntry(state->inodeIndexes, inode, &hlix, NULL, ++ NULL)) { ++ /* entry is in table, use hard link */ ++ char *fn = rpmfilesFN(state->files, hlix[0]); ++ if (link(fn, path) != 0) { ++ rpmlog(RPMLOG_ERR, ++ _("reflink: Unable to hard link %s -> %s due to %s\n"), ++ fn, path, strerror(errno)); ++ free(fn); ++ return RPMRC_FAIL; ++ } ++ free(fn); ++ return RPMRC_PLUGIN_CONTENTS; ++ } ++ /* if we didn't hard link, then we'll track this inode as being ++ * created soon ++ */ ++ if (rpmfiFNlink(fi) > 1) { ++ /* minor optimization: only store files with more than one link */ ++ inodeIndexHashAddEntry(state->inodeIndexes, inode, rpmfiFX(fi)); ++ } ++ /* derived from wfd_open in fsm.c */ ++ mode_t old_umask = umask(0577); ++ dst = open(path, O_WRONLY | O_CREAT | O_EXCL, S_IRUSR); ++ umask(old_umask); ++ if (dst == -1) { ++ rpmlog(RPMLOG_ERR, ++ _("reflink: Unable to open %s for writing due to %s, flags = %x\n"), ++ path, strerror(errno), rpmfiFFlags(fi)); ++ return RPMRC_FAIL; ++ } ++ size = rpmfiFSize(fi); ++ if (size > 0) { ++ /* round src_length down to fundamental_block_size multiple */ ++ fcr.src_length = size / state->fundamental_block_size * state->fundamental_block_size; ++ if ((size % state->fundamental_block_size) > 0) { ++ /* round up to next fundamental_block_size. We expect the data ++ * in the rpm to be similarly padded. ++ */ ++ fcr.src_length += state->fundamental_block_size; ++ } ++ fcr.src_fd = Fileno(state->fd); ++ if (fcr.src_fd == -1) { ++ close(dst); ++ rpmlog(RPMLOG_ERR, _("reflink: src fd lookup failed\n")); ++ return RPMRC_FAIL; ++ } ++ fcr.src_offset = find(rpmfiFDigest(fi, NULL, NULL), state); ++ if (fcr.src_offset == NOT_FOUND) { ++ close(dst); ++ rpmlog(RPMLOG_ERR, _("reflink: digest not found\n")); ++ return RPMRC_FAIL; ++ } ++ rpmlog(RPMLOG_DEBUG, ++ _("reflink: Reflinking %llu bytes at %llu to %s orig size=%ld, file=%lld\n"), ++ fcr.src_length, fcr.src_offset, path, size, fcr.src_fd); ++ rc = ioctl(dst, FICLONERANGE, &fcr); ++ if (rc) { ++ rpmlog(RPMLOG_WARNING, ++ _("reflink: falling back to copying bits for %s due to %d, %d = %s\n"), ++ path, rc, errno, strerror(errno)); ++ if (Fseek(state->fd, fcr.src_offset, SEEK_SET) < 0) { ++ close(dst); ++ rpmlog(RPMLOG_ERR, ++ _("reflink: unable to seek on copying bits\n")); ++ return RPMRC_FAIL; ++ } ++ rpm_loff_t left = size; ++ size_t len, read, written; ++ while (left) { ++ len = (left > BUFFER_SIZE ? BUFFER_SIZE : left); ++ read = Fread(state->buffer, len, 1, state->fd); ++ if (read != len) { ++ close(dst); ++ rpmlog(RPMLOG_ERR, ++ _("reflink: short read on copying bits\n")); ++ return RPMRC_FAIL; ++ } ++ written = write(dst, state->buffer, len); ++ if (read != written) { ++ close(dst); ++ rpmlog(RPMLOG_ERR, ++ _("reflink: short write on copying bits\n")); ++ return RPMRC_FAIL; ++ } ++ left -= len; ++ } ++ } else { ++ /* reflink worked, so truncate */ ++ rc = ftruncate(dst, size); ++ if (rc) { ++ rpmlog(RPMLOG_ERR, ++ _("reflink: Unable to truncate %s to %ld due to %s\n"), ++ path, size, strerror(errno)); ++ return RPMRC_FAIL; ++ } ++ } ++ } ++ close(dst); ++ return RPMRC_PLUGIN_CONTENTS; ++ } ++ return RPMRC_OK; ++} ++ ++struct rpmPluginHooks_s reflink_hooks = { ++ .init = reflink_init, ++ .cleanup = reflink_cleanup, ++ .psm_pre = reflink_psm_pre, ++ .psm_post = reflink_psm_post, ++ .fsm_file_pre = reflink_fsm_file_pre, ++}; +diff --git a/rpm2extents.c b/rpm2extents.c +new file mode 100644 +index 000000000..c111be0a2 +--- /dev/null ++++ b/rpm2extents.c +@@ -0,0 +1,433 @@ ++/* rpm2extents: convert payload to inline extents */ ++ ++#include "system.h" ++ ++#include /* rpmReadPackageFile .. */ ++#include ++#include ++#include ++#include ++ ++#include ++#include "lib/rpmlead.h" ++#include "lib/signature.h" ++#include "lib/header_internal.h" ++#include "rpmio/rpmio_internal.h" ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "debug.h" ++ ++/* hash of void * (pointers) to file digests to offsets within output. ++ * The length of the key depends on what the FILEDIGESTALGO is. ++ */ ++#undef HASHTYPE ++#undef HTKEYTYPE ++#undef HTDATATYPE ++#define HASHTYPE digestSet ++#define HTKEYTYPE const unsigned char * ++#include "lib/rpmhash.H" ++#include "lib/rpmhash.C" ++ ++/* magic value at end of file (64 bits) that indicates this is a transcoded ++ * rpm. ++ */ ++#define MAGIC 3472329499408095051 ++ ++struct digestoffset { ++ const unsigned char * digest; ++ rpm_loff_t pos; ++}; ++ ++rpm_loff_t pad_to(rpm_loff_t pos, rpm_loff_t unit); ++ ++rpm_loff_t pad_to(rpm_loff_t pos, rpm_loff_t unit) ++{ ++ return (unit - (pos % unit)) % unit; ++} ++ ++static int digestor( ++ FD_t fdi, ++ FD_t fdo, ++ FD_t validationo, ++ uint8_t algos[], ++ uint32_t algos_len ++) ++{ ++ ssize_t fdilength; ++ const char *filedigest, *algo_name; ++ size_t filedigest_len, len; ++ uint32_t algo_name_len, algo_digest_len; ++ int algo; ++ rpmRC rc = RPMRC_FAIL; ++ ++ for (algo = 0; algo < algos_len; algo++) { ++ fdInitDigest(fdi, algos[algo], 0); ++ } ++ fdilength = ufdCopy(fdi, fdo); ++ if (fdilength == -1) { ++ fprintf(stderr, _("digest cat failed\n")); ++ goto exit; ++ } ++ ++ len = sizeof(fdilength); ++ if (Fwrite(&fdilength, len, 1, validationo) != len) { ++ fprintf(stderr, _("Unable to write input length %zd\n"), fdilength); ++ goto exit; ++ } ++ len = sizeof(algos_len); ++ if (Fwrite(&algos_len, len, 1, validationo) != len) { ++ fprintf(stderr, _("Unable to write number of validation digests\n")); ++ goto exit; ++ } ++ for (algo = 0; algo < algos_len; algo++) { ++ fdFiniDigest(fdi, algos[algo], (void **)&filedigest, &filedigest_len, 0); ++ ++ algo_name = pgpValString(PGPVAL_HASHALGO, algos[algo]); ++ algo_name_len = (uint32_t)strlen(algo_name); ++ algo_digest_len = (uint32_t)filedigest_len; ++ ++ len = sizeof(algo_name_len); ++ if (Fwrite(&algo_name_len, len, 1, validationo) != len) { ++ fprintf(stderr, ++ _("Unable to write validation algo name length\n")); ++ goto exit; ++ } ++ len = sizeof(algo_digest_len); ++ if (Fwrite(&algo_digest_len, len, 1, validationo) != len) { ++ fprintf(stderr, ++ _("Unable to write number of bytes for validation digest\n")); ++ goto exit; ++ } ++ if (Fwrite(algo_name, algo_name_len, 1, validationo) != algo_name_len) { ++ fprintf(stderr, _("Unable to write validation algo name\n")); ++ goto exit; ++ } ++ if (Fwrite(filedigest, algo_digest_len, 1, validationo ) != algo_digest_len) { ++ fprintf(stderr, ++ _("Unable to write validation digest value %u, %zu\n"), ++ algo_digest_len, filedigest_len); ++ goto exit; ++ } ++ } ++ rc = RPMRC_OK; ++exit: ++ return rc; ++} ++ ++static rpmRC process_package(FD_t fdi, FD_t validationi) ++{ ++ uint32_t diglen; ++ /* GNU C extension: can use diglen from outer context */ ++ int digestSetCmp(const unsigned char * a, const unsigned char * b) { ++ return memcmp(a, b, diglen); ++ } ++ ++ unsigned int digestSetHash(const unsigned char * digest) { ++ /* assumes sizeof(unsigned int) < diglen */ ++ return *(unsigned int *)digest; ++ } ++ ++ int digestoffsetCmp(const void * a, const void * b) { ++ return digestSetCmp( ++ ((struct digestoffset *)a)->digest, ++ ((struct digestoffset *)b)->digest ++ ); ++ } ++ ++ FD_t fdo; ++ FD_t gzdi; ++ Header h, sigh; ++ long fundamental_block_size = sysconf(_SC_PAGESIZE); ++ rpmRC rc = RPMRC_OK; ++ rpm_mode_t mode; ++ char *rpmio_flags = NULL, *zeros; ++ const unsigned char *digest; ++ rpm_loff_t pos, size, pad, validation_pos; ++ uint32_t offset_ix = 0; ++ size_t len; ++ int next = 0; ++ ++ fdo = fdDup(STDOUT_FILENO); ++ ++ if (rpmReadPackageRaw(fdi, &sigh, &h)) { ++ fprintf(stderr, _("Error reading package\n")); ++ exit(EXIT_FAILURE); ++ } ++ ++ if (rpmLeadWrite(fdo, h)) ++ { ++ fprintf(stderr, _("Unable to write package lead: %s\n"), ++ Fstrerror(fdo)); ++ exit(EXIT_FAILURE); ++ } ++ ++ if (rpmWriteSignature(fdo, sigh)) { ++ fprintf(stderr, _("Unable to write signature: %s\n"), Fstrerror(fdo)); ++ exit(EXIT_FAILURE); ++ } ++ ++ if (headerWrite(fdo, h, HEADER_MAGIC_YES)) { ++ fprintf(stderr, _("Unable to write headers: %s\n"), Fstrerror(fdo)); ++ exit(EXIT_FAILURE); ++ } ++ ++ /* Retrieve payload size and compression type. */ ++ { ++ const char *compr = headerGetString(h, RPMTAG_PAYLOADCOMPRESSOR); ++ rpmio_flags = rstrscat(NULL, "r.", compr ? compr : "gzip", NULL); ++ } ++ ++ gzdi = Fdopen(fdi, rpmio_flags); /* XXX gzdi == fdi */ ++ free(rpmio_flags); ++ ++ if (gzdi == NULL) { ++ fprintf(stderr, _("cannot re-open payload: %s\n"), Fstrerror(gzdi)); ++ exit(EXIT_FAILURE); ++ } ++ ++ rpmfiles files = rpmfilesNew(NULL, h, 0, RPMFI_KEEPHEADER); ++ rpmfi fi = rpmfiNewArchiveReader(gzdi, files, ++ RPMFI_ITER_READ_ARCHIVE_CONTENT_FIRST); ++ ++ /* this is encoded in the file format, so needs to be fixed size (for ++ * now?) ++ */ ++ diglen = (uint32_t)rpmDigestLength(rpmfiDigestAlgo(fi)); ++ digestSet ds = digestSetCreate(rpmfiFC(fi), digestSetHash, digestSetCmp, ++ NULL); ++ struct digestoffset offsets[rpmfiFC(fi)]; ++ pos = RPMLEAD_SIZE + headerSizeof(sigh, HEADER_MAGIC_YES); ++ ++ /* main headers are aligned to 8 byte boundry */ ++ pos += pad_to(pos, 8); ++ pos += headerSizeof(h, HEADER_MAGIC_YES); ++ ++ zeros = xcalloc(fundamental_block_size, 1); ++ ++ while (next >= 0) { ++ next = rpmfiNext(fi); ++ if (next == RPMERR_ITER_END) { ++ rc = RPMRC_OK; ++ break; ++ } ++ mode = rpmfiFMode(fi); ++ if (!S_ISREG(mode) || !rpmfiArchiveHasContent(fi)) { ++ /* not a regular file, or the archive doesn't contain any content ++ * for this entry. ++ */ ++ continue; ++ } ++ digest = rpmfiFDigest(fi, NULL, NULL); ++ if (digestSetGetEntry(ds, digest, NULL)) { ++ /* This specific digest has already been included, so skip it. */ ++ continue; ++ } ++ pad = pad_to(pos, fundamental_block_size); ++ if (Fwrite(zeros, sizeof(char), pad, fdo) != pad) { ++ fprintf(stderr, _("Unable to write padding\n")); ++ rc = RPMRC_FAIL; ++ goto exit; ++ } ++ /* round up to next fundamental_block_size */ ++ pos += pad; ++ digestSetAddEntry(ds, digest); ++ offsets[offset_ix].digest = digest; ++ offsets[offset_ix].pos = pos; ++ offset_ix++; ++ size = rpmfiFSize(fi); ++ rc = rpmfiArchiveReadToFile(fi, fdo, 0); ++ if (rc != RPMRC_OK) { ++ fprintf(stderr, _("rpmfiArchiveReadToFile failed with %d\n"), rc); ++ goto exit; ++ } ++ pos += size; ++ } ++ Fclose(gzdi); /* XXX gzdi == fdi */ ++ ++ qsort(offsets, (size_t)offset_ix, sizeof(struct digestoffset), ++ digestoffsetCmp); ++ ++ len = sizeof(offset_ix); ++ if (Fwrite(&offset_ix, len, 1, fdo) != len) { ++ fprintf(stderr, _("Unable to write length of table\n")); ++ rc = RPMRC_FAIL; ++ goto exit; ++ } ++ len = sizeof(diglen); ++ if (Fwrite(&diglen, len, 1, fdo) != len) { ++ fprintf(stderr, _("Unable to write length of digest\n")); ++ rc = RPMRC_FAIL; ++ goto exit; ++ } ++ len = sizeof(rpm_loff_t); ++ for (int x = 0; x < offset_ix; x++) { ++ if (Fwrite(offsets[x].digest, diglen, 1, fdo) != diglen) { ++ fprintf(stderr, _("Unable to write digest\n")); ++ rc = RPMRC_FAIL; ++ goto exit; ++ } ++ if (Fwrite(&offsets[x].pos, len, 1, fdo) != len) { ++ fprintf(stderr, _("Unable to write offset\n")); ++ rc = RPMRC_FAIL; ++ goto exit; ++ } ++ } ++ validation_pos = ( ++ pos + sizeof(offset_ix) + sizeof(diglen) + ++ offset_ix * (diglen + sizeof(rpm_loff_t)) ++ ); ++ ++ ssize_t validation_len = ufdCopy(validationi, fdo); ++ if (validation_len == -1) { ++ fprintf(stderr, _("digest table ufdCopy failed\n")); ++ rc = RPMRC_FAIL; ++ goto exit; ++ } ++ /* add more padding so the last file can be cloned. It doesn't matter that ++ * the table and validation etc are in this space. In fact, it's pretty ++ * efficient if it is. ++ */ ++ ++ pad = pad_to((validation_pos + validation_len + 2 * sizeof(rpm_loff_t) + ++ sizeof(uint64_t)), fundamental_block_size); ++ if (Fwrite(zeros, sizeof(char), pad, fdo) != pad) { ++ fprintf(stderr, _("Unable to write final padding\n")); ++ rc = RPMRC_FAIL; ++ goto exit; ++ } ++ zeros = _free(zeros); ++ if (Fwrite(&pos, len, 1, fdo) != len) { ++ fprintf(stderr, _("Unable to write offset of digest table\n")); ++ rc = RPMRC_FAIL; ++ goto exit; ++ } ++ if (Fwrite(&validation_pos, len, 1, fdo) != len) { ++ fprintf(stderr, _("Unable to write offset of validation table\n")); ++ rc = RPMRC_FAIL; ++ goto exit; ++ } ++ uint64_t magic = MAGIC; ++ len = sizeof(magic); ++ if (Fwrite(&magic, len, 1, fdo) != len) { ++ fprintf(stderr, _("Unable to write magic\n")); ++ rc = RPMRC_FAIL; ++ goto exit; ++ } ++ ++exit: ++ rpmfilesFree(files); ++ rpmfiFree(fi); ++ headerFree(h); ++ return rc; ++} ++ ++int main(int argc, char *argv[]) { ++ rpmRC rc; ++ int cprc = 0; ++ uint8_t algos[argc - 1]; ++ int mainpipefd[2]; ++ int metapipefd[2]; ++ pid_t cpid, w; ++ int wstatus; ++ ++ xsetprogname(argv[0]); /* Portability call -- see system.h */ ++ rpmReadConfigFiles(NULL, NULL); ++ ++ if (argc > 1 && (rstreq(argv[1], "-h") || rstreq(argv[1], "--help"))) { ++ fprintf(stderr, _("Usage: %s [DIGESTALGO]...\n"), argv[0]); ++ exit(EXIT_FAILURE); ++ } ++ ++ if (argc == 1) { ++ fprintf(stderr, ++ _("Need at least one DIGESTALGO parameter, e.g. 'SHA256'\n")); ++ exit(EXIT_FAILURE); ++ } ++ ++ for (int x = 0; x < (argc - 1); x++) { ++ if (pgpStringVal(PGPVAL_HASHALGO, argv[x + 1], &algos[x]) != 0) ++ { ++ fprintf(stderr, ++ _("Unable to resolve '%s' as a digest algorithm, exiting\n"), ++ argv[x + 1]); ++ exit(EXIT_FAILURE); ++ } ++ } ++ ++ ++ if (pipe(mainpipefd) == -1) { ++ fprintf(stderr, _("Main pipe failure\n")); ++ exit(EXIT_FAILURE); ++ } ++ if (pipe(metapipefd) == -1) { ++ fprintf(stderr, _("Meta pipe failure\n")); ++ exit(EXIT_FAILURE); ++ } ++ cpid = fork(); ++ if (cpid == 0) { ++ /* child: digestor */ ++ close(mainpipefd[0]); ++ close(metapipefd[0]); ++ FD_t fdi = fdDup(STDIN_FILENO); ++ FD_t fdo = fdDup(mainpipefd[1]); ++ FD_t validationo = fdDup(metapipefd[1]); ++ rc = digestor(fdi, fdo, validationo, algos, argc - 1); ++ Fclose(validationo); ++ Fclose(fdo); ++ Fclose(fdi); ++ } else { ++ /* parent: main program */ ++ close(mainpipefd[1]); ++ close(metapipefd[1]); ++ FD_t fdi = fdDup(mainpipefd[0]); ++ FD_t validationi = fdDup(metapipefd[0]); ++ rc = process_package(fdi, validationi); ++ Fclose(validationi); ++ /* fdi is normally closed through the stacked file gzdi in the ++ * function. ++ * Wait for child process (digestor for stdin) to complete. ++ */ ++ if (rc != RPMRC_OK) { ++ if (kill(cpid, SIGTERM) != 0) { ++ fprintf(stderr, ++ _("Failed to kill digest process when main process failed: %s\n"), ++ strerror(errno)); ++ } ++ } ++ w = waitpid(cpid, &wstatus, 0); ++ if (w == -1) { ++ fprintf(stderr, _("waitpid failed\n")); ++ cprc = EXIT_FAILURE; ++ } else if (WIFEXITED(wstatus)) { ++ cprc = WEXITSTATUS(wstatus); ++ if (cprc != 0) { ++ fprintf(stderr, ++ _("Digest process non-zero exit code %d\n"), ++ cprc); ++ } ++ } else if (WIFSIGNALED(wstatus)) { ++ fprintf(stderr, ++ _("Digest process was terminated with a signal: %d\n"), ++ WTERMSIG(wstatus)); ++ cprc = EXIT_FAILURE; ++ } else { ++ /* Don't think this can happen, but covering all bases */ ++ fprintf(stderr, _("Unhandled circumstance in waitpid\n")); ++ cprc = EXIT_FAILURE; ++ } ++ if (cprc != EXIT_SUCCESS) { ++ rc = RPMRC_FAIL; ++ } ++ } ++ if (rc != RPMRC_OK) { ++ /* translate rpmRC into generic failure return code. */ ++ return EXIT_FAILURE; ++ } ++ return EXIT_SUCCESS; ++} +diff --git a/rpmio/rpmpgp.c b/rpmio/rpmpgp.c +index 015c15a5c..7b972b4a6 100644 +--- a/rpmio/rpmpgp.c ++++ b/rpmio/rpmpgp.c +@@ -283,6 +283,16 @@ int pgpValTok(pgpValTbl vs, const char * s, const char * se) + return vs->val; + } + ++int pgpStringVal(pgpValType type, const char *str, uint8_t *val) ++{ ++ pgpValTbl tbl = pgpValTable(type); ++ if (tbl == NULL) return -1; ++ int v = pgpValTok(tbl, str, str + strlen(str)); ++ if (v == -1) return -1; ++ *val = (uint8_t)v; ++ return 0; ++} ++ + /** \ingroup rpmpgp + * Decode length from 1, 2, or 5 octet body length encoding, used in + * new format packet headers and V4 signature subpackets. +diff --git a/rpmio/rpmpgp.h b/rpmio/rpmpgp.h +index c53e29b01..2b57318ba 100644 +--- a/rpmio/rpmpgp.h ++++ b/rpmio/rpmpgp.h +@@ -973,6 +973,15 @@ typedef rpmFlags rpmDigestFlags; + */ + const char * pgpValString(pgpValType type, uint8_t val); + ++/** \ingroup rpmpgp ++ * Return OpenPGP value for a string. ++ * @param type type of value ++ * @param str string to lookup ++ * @param[out] val byte value associated with string ++ * @return 0 on success else -1 ++ */ ++int pgpStringVal(pgpValType type, const char *str, uint8_t *val); ++ + /** \ingroup rpmpgp + * Return (native-endian) integer from big-endian representation. + * @param s pointer to big-endian integer diff --git a/SOURCES/measure.patch b/SOURCES/measure.patch index dab1596..b0c580f 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 db24eb0..7685441 100644 --- a/SPECS/rpm.spec +++ b/SPECS/rpm.spec @@ -193,10 +193,10 @@ Patch1983: 0034-rpmsign-Adopting-PKCS11-opaque-keys-support-in-libfsverity-for-f %endif 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) +Patch9990: https://github.com/rpm-software-management/rpm/pull/1381.patch +Provides: rpm(pr1381) +Patch9991: https://github.com/chantra/rpm/compare/master...cow.diff +Provides: rpm(pr1470) Patch9999: measure.patch # Partially GPL/LGPL dual-licensed and some bits with BSD @@ -490,6 +490,13 @@ 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 @@ -715,6 +722,10 @@ 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