diff --git a/Makefile.am b/Makefile.am index 288668819..96542c8c8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -185,7 +185,7 @@ bin_PROGRAMS += rpmgraph rpmgraph_SOURCES = tools/rpmgraph.c rpmgraph_LDADD = lib/librpm.la rpmio/librpmio.la @WITH_POPT_LIB@ -dist_bin_SCRIPTS = scripts/gendiff +dist_bin_SCRIPTS = scripts/gendiff scripts/rpm2extents_dump rpmconfig_DATA = rpmrc rpmrc: $(top_srcdir)/rpmrc.in diff --git a/lib/Makefile.am b/lib/Makefile.am index 5a1b6ca9b..2f1b3597f 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -40,7 +40,8 @@ librpm_la_SOURCES = \ rpmscript.h rpmscript.c \ rpmchroot.c rpmchroot.h \ rpmplugins.c rpmplugins.h rpmplugin.h rpmug.c rpmug.h \ - rpmtriggers.h rpmtriggers.c rpmvs.c rpmvs.h + rpmtriggers.h rpmtriggers.c rpmvs.c rpmvs.h \ + rpmextents.c rpmextents_internal.h librpm_la_LDFLAGS = -version-info $(rpm_version_info) diff --git a/lib/depends.c b/lib/depends.c index 8998afcd3..30234df3d 100644 --- a/lib/depends.c +++ b/lib/depends.c @@ -81,8 +81,6 @@ 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 feda3750c..6972602e0 100644 --- a/lib/fsm.c +++ b/lib/fsm.c @@ -19,7 +19,6 @@ #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" @@ -53,7 +52,6 @@ struct filedata_s { int stage; int setmeta; int skip; - int plugin_contents; rpmFileAction action; const char *suffix; char *fpath; @@ -893,14 +891,6 @@ 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)); @@ -921,23 +911,12 @@ 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); @@ -945,14 +924,24 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, if (rc) 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); + rpmRC plugin_rc = rpmpluginsCallFsmFileArchiveReader(plugins, payload, files, &fi); + switch(plugin_rc) { + case RPMRC_PLUGIN_CONTENTS: + if(fi == NULL) { + rc = RPMERR_BAD_MAGIC; + goto exit; + } + rc = RPMRC_OK; + break; + case RPMRC_OK: + fi = rpmfiNewArchiveReader(payload, files, RPMFI_ITER_READ_ARCHIVE); + if (fi == NULL) { + rc = RPMERR_BAD_MAGIC; + goto exit; + } + break; + default: + rc = RPMRC_FAIL; } /* Detect and create directories not explicitly in package. */ @@ -992,14 +981,15 @@ int rpmPackageFilesInstall(rpmts ts, rpmte te, rpmfiles files, if (fp->action == FA_TOUCH) continue; - if (S_ISREG(fp->sb.st_mode)) { + rpmRC plugin_rc = rpmpluginsCallFsmFileInstall(plugins, fi, fp->fpath, fp->sb.st_mode, fp->action); + if(!(plugin_rc == RPMRC_PLUGIN_CONTENTS || plugin_rc == RPMRC_OK)){ + rc = plugin_rc; + } else if(plugin_rc == RPMRC_PLUGIN_CONTENTS){ + rc = RPMRC_OK; + } else if (S_ISREG(fp->sb.st_mode)) { if (rc == RPMERR_ENOENT) { - if(fp->plugin_contents) { - rc = RPMRC_OK; - }else { - rc = fsmMkfile(fi, fp, files, psm, nodigest, - &firstlink, &firstlinkfile); - } + rc = fsmMkfile(fi, fp, files, psm, nodigest, + &firstlink, &firstlinkfile); } } else if (S_ISDIR(fp->sb.st_mode)) { if (rc == RPMERR_ENOENT) { @@ -1107,7 +1097,6 @@ 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/rpmchecksig.c b/lib/rpmchecksig.c index 40a3ab83f..c9fc3bbc9 100644 --- a/lib/rpmchecksig.c +++ b/lib/rpmchecksig.c @@ -20,6 +20,7 @@ #include "rpmio/rpmio_internal.h" /* fdSetBundle() */ #include "lib/rpmlead.h" #include "lib/header_internal.h" +#include "lib/rpmextents_internal.h" #include "lib/rpmvs.h" #include "debug.h" @@ -221,36 +222,24 @@ rpmRC rpmpkgRead(struct rpmvs_s *vs, FD_t fd, } static int rpmpkgVerifySigs(rpmKeyring keyring, int vfylevel, rpmVSFlags flags, - FD_t fd, const char *fn) + FD_t fd, rpmsinfoCb cb, void *cbdata) { char *msg = NULL; - struct vfydata_s vd = { .seen = 0, - .bad = 0, - .verbose = rpmIsVerbose(), - }; int rc; - struct rpmvs_s *vs = rpmvsCreate(vfylevel, flags, keyring); - rpmlog(RPMLOG_NOTICE, "%s:%s", fn, vd.verbose ? "\n" : ""); + + if(isTranscodedRpm(fd) == RPMRC_OK){ + return extentsVerifySigs(fd); + } + + struct rpmvs_s *vs = rpmvsCreate(vfylevel, flags, keyring); rc = rpmpkgRead(vs, fd, NULL, NULL, &msg); if (rc) goto exit; - rc = rpmvsVerify(vs, RPMSIG_VERIFIABLE_TYPE, vfyCb, &vd); - - if (!vd.verbose) { - if (vd.seen & RPMSIG_DIGEST_TYPE) { - rpmlog(RPMLOG_NOTICE, " %s", (vd.bad & RPMSIG_DIGEST_TYPE) ? - _("DIGESTS") : _("digests")); - } - if (vd.seen & RPMSIG_SIGNATURE_TYPE) { - rpmlog(RPMLOG_NOTICE, " %s", (vd.bad & RPMSIG_SIGNATURE_TYPE) ? - _("SIGNATURES") : _("signatures")); - } - rpmlog(RPMLOG_NOTICE, " %s\n", rc ? _("NOT OK") : _("OK")); - } + rc = rpmvsVerify(vs, RPMSIG_VERIFIABLE_TYPE, cb, cbdata); exit: if (rc && msg) @@ -260,15 +249,39 @@ static int rpmpkgVerifySigs(rpmKeyring keyring, int vfylevel, rpmVSFlags flags, return rc; } +static void rpmkgVerifySigsPreLogging(struct vfydata_s *vd, const char *fn){ + rpmlog(RPMLOG_NOTICE, "%s:%s", fn, vd->verbose ? "\n" : ""); +} + +static void rpmkgVerifySigsPostLogging(struct vfydata_s *vd, int rc){ + if (!vd->verbose) { + if (vd->seen & RPMSIG_DIGEST_TYPE) { + rpmlog(RPMLOG_NOTICE, " %s", (vd->bad & RPMSIG_DIGEST_TYPE) ? + _("DIGESTS") : _("digests")); + } + if (vd->seen & RPMSIG_SIGNATURE_TYPE) { + rpmlog(RPMLOG_NOTICE, " %s", (vd->bad & RPMSIG_SIGNATURE_TYPE) ? + _("SIGNATURES") : _("signatures")); + } + rpmlog(RPMLOG_NOTICE, " %s\n", rc ? _("NOT OK") : _("OK")); + } +} + /* Wrapper around rpmkVerifySigs to preserve API */ int rpmVerifySignatures(QVA_t qva, rpmts ts, FD_t fd, const char * fn) { int rc = 1; /* assume failure */ + struct vfydata_s vd = { .seen = 0, + .bad = 0, + .verbose = rpmIsVerbose(), + }; if (ts && qva && fd && fn) { rpmKeyring keyring = rpmtsGetKeyring(ts, 1); rpmVSFlags vsflags = rpmtsVfyFlags(ts); int vfylevel = rpmtsVfyLevel(ts); - rc = rpmpkgVerifySigs(keyring, vfylevel, vsflags, fd, fn); + rpmkgVerifySigsPreLogging(&vd, fn); + rc = rpmpkgVerifySigs(keyring, vfylevel, vsflags, fd, vfyCb, &vd); + rpmkgVerifySigsPostLogging(&vd, rc); rpmKeyringFree(keyring); } return rc; @@ -290,12 +303,22 @@ int rpmcliVerifySignatures(rpmts ts, ARGV_const_t argv) while ((arg = *argv++) != NULL) { FD_t fd = Fopen(arg, "r.ufdio"); + struct vfydata_s vd = { .seen = 0, + .bad = 0, + .verbose = rpmIsVerbose(), + }; if (fd == NULL || Ferror(fd)) { rpmlog(RPMLOG_ERR, _("%s: open failed: %s\n"), arg, Fstrerror(fd)); res++; - } else if (rpmpkgVerifySigs(keyring, vfylevel, vsflags, fd, arg)) { + } else { + rpmkgVerifySigsPreLogging(&vd, arg); + int rc = rpmpkgVerifySigs(keyring, vfylevel, vsflags, fd, + vfyCb, &vd); + rpmkgVerifySigsPostLogging(&vd, rc); + if (rc) { res++; + } } Fclose(fd); @@ -304,3 +327,53 @@ int rpmcliVerifySignatures(rpmts ts, ARGV_const_t argv) rpmKeyringFree(keyring); return res; } + +struct vfydatafd_s { + size_t len; + char msg[BUFSIZ]; +}; + + +static int vfyFDCb(struct rpmsinfo_s *sinfo, void *cbdata) +{ + struct vfydatafd_s *vd = cbdata; + char *vmsg, *msg; + size_t n; + size_t remainder = BUFSIZ - vd->len >= 0 ? BUFSIZ - vd->len : 0; + + vmsg = rpmsinfoMsg(sinfo); + rasprintf(&msg, " %s\n", vmsg); + n = rstrlcpy(vd->msg + vd->len, msg, remainder); + free(vmsg); + free(msg); + if(n <= remainder){ + vd->len += n; + } + return 1; +} + + +int rpmcliVerifySignaturesFD(rpmts ts, FD_t fdi, char **msg) +{ + rpmRC rc = RPMRC_FAIL; + rpmKeyring keyring = rpmtsGetKeyring(ts, 1); + rpmVSFlags vsflags = rpmtsVfyFlags(ts); + int vfylevel = rpmtsVfyLevel(ts); + struct vfydatafd_s vd = {.len = 0}; + + vsflags |= rpmcliVSFlags; + if (rpmcliVfyLevelMask) { + vfylevel &= ~rpmcliVfyLevelMask; + rpmtsSetVfyLevel(ts, vfylevel); + } + + if (!rpmpkgVerifySigs(keyring, vfylevel, vsflags, fdi, vfyFDCb, &vd)) { + rc = RPMRC_OK; + } + *msg = strdup(vd.msg); + rpmsqPoll(); + + rpmKeyringFree(keyring); + return rc; +} + diff --git a/lib/rpmcli.h b/lib/rpmcli.h index 906fe9951..7ff48b37a 100644 --- a/lib/rpmcli.h +++ b/lib/rpmcli.h @@ -411,6 +411,16 @@ int rpmcliImportPubkeys(rpmts ts, ARGV_const_t argv); */ int rpmcliVerifySignatures(rpmts ts, ARGV_const_t argv); + +/** \ingroup rpmcli + * Verify package signatures. + * @param ts transaction set + * @param fd a file descriptor to verify + * @param msg a string containing textual information about the verification, similar to rpmcliVerifySignatures output. + * @return 0 on success + */ +int rpmcliVerifySignaturesFD(rpmts ts, FD_t fd, char **msg); + #ifdef __cplusplus } #endif diff --git a/lib/rpmextents.c b/lib/rpmextents.c new file mode 100644 index 000000000..59ba427a4 --- /dev/null +++ b/lib/rpmextents.c @@ -0,0 +1,109 @@ + +#include "system.h" + +#include +#include +#include +#include + + +#include "lib/rpmextents_internal.h" + + +int extentsVerifySigs(FD_t fd){ + rpm_loff_t current; + int32_t rc; + size_t len; + uint64_t content_len; + char *content = NULL; + struct extents_footer_t footer; + + current = Ftell(fd); + + if(extentsFooterFromFD(fd, &footer) != RPMRC_OK) { + rc = -1; + goto exit; + } + if(Fseek(fd, footer.offsets.checksig_offset, SEEK_SET) < 0) { + rpmlog(RPMLOG_ERR, _("extentsVerifySigs: Failed to seek signature verification offset\n")); + rc = -1; + goto exit; + } + len = sizeof(rc); + if (Fread(&rc, len, 1, fd) != len) { + rpmlog(RPMLOG_ERR, _("extentsVerifySigs: Failed to read Signature Verification RC\n")); + rc = -1; + goto exit; + } + + len = sizeof(content_len); + if (Fread(&content_len, len, 1, fd) != len) { + rpmlog(RPMLOG_ERR, _("extentsVerifySigs: Failed to read signature content length\n")); + goto exit; + } + + content = rmalloc(content_len + 1); + if(content == NULL) { + rpmlog(RPMLOG_ERR, _("extentsVerifySigs: Failed to allocate memory to read signature content\n")); + goto exit; + } + content[content_len] = 0; + if (Fread(content, content_len, 1, fd) != content_len) { + rpmlog(RPMLOG_ERR, _("extentsVerifySigs: Failed to read signature content\n")); + goto exit; + } + + rpmlog(RPMLOG_NOTICE, "%s", content); +exit: + if(content){ + rfree(content); + } + if (Fseek(fd, current, SEEK_SET) < 0) { + rpmlog(RPMLOG_ERR, _("extentsVerifySigs: unable to seek back to original location\n")); + } + return rc; + +} + +rpmRC extentsFooterFromFD(FD_t fd, struct extents_footer_t *footer) { + + rpmRC rc = RPMRC_NOTFOUND; + rpm_loff_t current; + size_t len; + + // If the file is not seekable, we cannot detect whether or not it is transcoded. + if(Fseek(fd, 0, SEEK_CUR) < 0) { + return RPMRC_FAIL; + } + current = Ftell(fd); + + len = sizeof(struct extents_footer_t); + if(Fseek(fd, -len, SEEK_END) < 0) { + rpmlog(RPMLOG_ERR, _("isTranscodedRpm: failed to seek for footer: %s\n"), strerror(errno)); + rc = RPMRC_FAIL; + goto exit; + } + if (Fread(footer, len, 1, fd) != len) { + rpmlog(RPMLOG_ERR, _("isTranscodedRpm: unable to read footer\n")); + rc = RPMRC_FAIL; + goto exit; + } + if (footer->magic != EXTENTS_MAGIC) { + rc = RPMRC_NOTFOUND; + goto exit; + } + rc = RPMRC_OK; +exit: + if (Fseek(fd, current, SEEK_SET) < 0) { + rpmlog(RPMLOG_ERR, _("isTranscodedRpm: unable to seek back to original location\n")); + rc = RPMRC_FAIL; + } + return rc; +} + +rpmRC isTranscodedRpm(FD_t fd) { + struct extents_footer_t footer; + return extentsFooterFromFD(fd, &footer); +} + + diff --git a/lib/rpmextents_internal.h b/lib/rpmextents_internal.h new file mode 100644 index 000000000..380c08425 --- /dev/null +++ b/lib/rpmextents_internal.h @@ -0,0 +1,57 @@ +#ifndef _RPMEXTENTS_INTERNAL_H +#define _RPMEXTENTS_INTERNAL_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** \ingroup rpmextents + * RPM extents library + */ + +/* magic value at end of file (64 bits) that indicates this is a transcoded + * rpm. + */ +#define EXTENTS_MAGIC 3472329499408095051 + +typedef uint64_t extents_magic_t; + +struct __attribute__ ((__packed__)) extents_footer_offsets_t { + off64_t checksig_offset; + off64_t table_offset; + off64_t csum_offset; +}; + +struct __attribute__ ((__packed__)) extents_footer_t { + struct extents_footer_offsets_t offsets; + extents_magic_t magic; +}; + +/** \ingroup rpmextents + * Checks the results of the signature verification ran during transcoding. + * @param fd The FD_t of the transcoded RPM + * @return The number of checks that `rpmvsVerify` failed during transcoding. + */ +int extentsVerifySigs(FD_t fd); + +/** \ingroup rpmextents + * Read the RPM Extents footer from a file descriptor. + * @param fd The FD_t of the transcoded RPM + * @param[out] footer A pointer to an allocated extents_footer_t with a copy of the footer. + * @return RPMRC_OK on success, RPMRC_NOTFOUND if not a transcoded file, RPMRC_FAIL on any failure. + */ +rpmRC extentsFooterFromFD(FD_t fd, struct extents_footer_t *footer); + +/** \ingroup rpmextents + * Check if a RPM is a transcoded RPM + * @param fd The FD_t of the transcoded RPM + * return RPMRC_OK on success, RPMRC_NOTFOUND if not a transcoded file, RPMRC_FAIL on any failure. + */ +rpmRC isTranscodedRpm(FD_t fd); + +#ifdef __cplusplus +} +#endif +#endif /* _RPMEXTENTS_INTERNAL_H */ diff --git a/lib/rpmplugin.h b/lib/rpmplugin.h index fd81aec8d..6dbbcff35 100644 --- a/lib/rpmplugin.h +++ b/lib/rpmplugin.h @@ -60,6 +60,13 @@ typedef rpmRC (*plugin_fsm_file_prepare_func)(rpmPlugin plugin, rpmfi fi, const char* path, const char *dest, mode_t file_mode, rpmFsmOp op); +typedef rpmRC (*plugin_fsm_file_install_func)(rpmPlugin plugin, rpmfi fi, + const char* path, + mode_t file_mode, rpmFsmOp op); +typedef rpmRC (*plugin_fsm_file_archive_reader_func)(rpmPlugin plugin, + FD_t payload, + rpmfiles files, rpmfi *fi); + typedef struct rpmPluginHooks_s * rpmPluginHooks; struct rpmPluginHooks_s { @@ -80,6 +87,8 @@ struct rpmPluginHooks_s { plugin_fsm_file_pre_func fsm_file_pre; plugin_fsm_file_post_func fsm_file_post; plugin_fsm_file_prepare_func fsm_file_prepare; + plugin_fsm_file_install_func fsm_file_install; + plugin_fsm_file_archive_reader_func fsm_file_archive_reader; }; #ifdef __cplusplus diff --git a/lib/rpmplugins.c b/lib/rpmplugins.c index 3da3097af..901af1ac5 100644 --- a/lib/rpmplugins.c +++ b/lib/rpmplugins.c @@ -421,3 +421,74 @@ rpmRC rpmpluginsCallFsmFilePrepare(rpmPlugins plugins, rpmfi fi, return rc; } + +rpmRC rpmpluginsCallFsmFileInstall(rpmPlugins plugins, rpmfi fi, + const char *path, mode_t file_mode, + rpmFsmOp op) +{ + plugin_fsm_file_install_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_install); + if (hookFunc) { + hook_rc = hookFunc(plugin, fi, path, file_mode, op); + if (hook_rc == RPMRC_FAIL) { + rpmlog(RPMLOG_ERR, "Plugin %s: hook fsm_file_install 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; + } + } + } + } + + return rc; +} + +rpmRC rpmpluginsCallFsmFileArchiveReader(rpmPlugins plugins, FD_t payload, + rpmfiles files, rpmfi *fi) +{ + plugin_fsm_file_archive_reader_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_archive_reader); + if (hookFunc) { + hook_rc = hookFunc(plugin, payload, files, fi); + if (hook_rc == RPMRC_FAIL) { + rpmlog(RPMLOG_ERR, "Plugin %s: hook fsm_file_archive_reader 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; + } + } + } + } + + return rc; +} + + diff --git a/lib/rpmplugins.h b/lib/rpmplugins.h index 39762c376..88807c53c 100644 --- a/lib/rpmplugins.h +++ b/lib/rpmplugins.h @@ -167,6 +167,23 @@ rpmRC rpmpluginsCallFsmFilePrepare(rpmPlugins plugins, rpmfi fi, const char *path, const char *dest, mode_t mode, rpmFsmOp op); +/** \ingroup rpmplugins + * Call the fsm file install plugin hook + * @param plugins plugins structure + * @param fi file info iterator (or NULL) + * @param path file object path + * @param file_mode file object mode + * @param op file operation + associated flags + * @return RPMRC_OK on success, RPMRC_FAIL otherwise + */ +RPM_GNUC_INTERNAL +rpmRC rpmpluginsCallFsmFileInstall(rpmPlugins plugins, rpmfi fi, + const char* path, mode_t file_mode, + rpmFsmOp op); + +RPM_GNUC_INTERNAL +rpmRC rpmpluginsCallFsmFileArchiveReader(rpmPlugins plugins, FD_t payload, + rpmfiles files, rpmfi *fi); #ifdef __cplusplus } #endif diff --git a/plugins/reflink.c b/plugins/reflink.c index 513887604..4fc1d74d1 100644 --- a/plugins/reflink.c +++ b/plugins/reflink.c @@ -13,6 +13,7 @@ #include #include "lib/rpmlib.h" #include "lib/rpmplugin.h" +#include "lib/rpmextents_internal.h" #include "lib/rpmte_internal.h" #include #include "rpmio/rpmio_internal.h" @@ -28,7 +29,7 @@ #undef HTDATATYPE #define HASHTYPE inodeIndexHash #define HTKEYTYPE rpm_ino_t -#define HTDATATYPE int +#define HTDATATYPE const char * #include "lib/rpmhash.H" #include "lib/rpmhash.C" @@ -40,11 +41,6 @@ #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; @@ -58,6 +54,7 @@ struct reflink_state_s { FD_t fd; rpmfiles files; inodeIndexHash inodeIndexes; + int transcoded; }; typedef struct reflink_state_s * reflink_state; @@ -96,51 +93,35 @@ static void reflink_cleanup(rpmPlugin plugin) { } static rpmRC reflink_psm_pre(rpmPlugin plugin, rpmte te) { + rpmRC rc; + size_t len; + 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")); + rc = isTranscodedRpm(state->fd); + + switch(rc){ + // Fail to parse the file, fail the plugin. + case RPMRC_FAIL: return RPMRC_FAIL; - } - return RPMRC_OK; + // This is not a transcoded file, do nothing. + case RPMRC_NOTFOUND: + return RPMRC_OK; + default: + break; } rpmlog(RPMLOG_DEBUG, _("reflink: *is* transcoded\n")); - Header h = rpmteHeader(te); + state->transcoded = 1; - /* 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) { + if (Fseek(state->fd, -(sizeof(rpm_loff_t) * 2 + sizeof(extents_magic_t)), SEEK_END) < 0) { rpmlog(RPMLOG_ERR, _("reflink: failed to seek for tail %p\n"), state->fd); return RPMRC_FAIL; @@ -182,7 +163,7 @@ static rpmRC reflink_psm_pre(rpmPlugin plugin, rpmte te) { return RPMRC_FAIL; } state->inodeIndexes = inodeIndexHashCreate( - state->keys, inodeId, inodeCmp, NULL, NULL + state->keys, inodeId, inodeCmp, NULL, (inodeIndexHashFreeData)rfree ); } @@ -239,13 +220,13 @@ rpm_loff_t find(const unsigned char *digest, reflink_state state) { return offset; } -static rpmRC reflink_fsm_file_pre(rpmPlugin plugin, rpmfi fi, const char* path, +static rpmRC reflink_fsm_file_install(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; + const char **hl_target = NULL; reflink_state state = rpmPluginGetData(plugin); if (state->table == NULL) { @@ -262,18 +243,15 @@ static rpmRC reflink_fsm_file_pre(rpmPlugin plugin, rpmfi fi, const char* path, /* 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)) { + if (inodeIndexHashGetEntry(state->inodeIndexes, inode, &hl_target, + NULL, NULL)) { /* entry is in table, use hard link */ - char *fn = rpmfilesFN(state->files, hlix[0]); - if (link(fn, path) != 0) { + if (link(hl_target[0], path) != 0) { rpmlog(RPMLOG_ERR, _("reflink: Unable to hard link %s -> %s due to %s\n"), - fn, path, strerror(errno)); - free(fn); + hl_target[0], path, strerror(errno)); return RPMRC_FAIL; } - free(fn); return RPMRC_PLUGIN_CONTENTS; } /* if we didn't hard link, then we'll track this inode as being @@ -281,7 +259,7 @@ static rpmRC reflink_fsm_file_pre(rpmPlugin plugin, rpmfi fi, const char* path, */ if (rpmfiFNlink(fi) > 1) { /* minor optimization: only store files with more than one link */ - inodeIndexHashAddEntry(state->inodeIndexes, inode, rpmfiFX(fi)); + inodeIndexHashAddEntry(state->inodeIndexes, inode, rstrdup(path)); } /* derived from wfd_open in fsm.c */ mode_t old_umask = umask(0577); @@ -366,10 +344,21 @@ static rpmRC reflink_fsm_file_pre(rpmPlugin plugin, rpmfi fi, const char* path, return RPMRC_OK; } +static rpmRC reflink_fsm_file_archive_reader(rpmPlugin plugin, FD_t payload, + rpmfiles files, rpmfi *fi) { + reflink_state state = rpmPluginGetData(plugin); + if(state->transcoded) { + *fi = rpmfilesIter(files, RPMFI_ITER_FWD); + 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, + .fsm_file_install = reflink_fsm_file_install, + .fsm_file_archive_reader = reflink_fsm_file_archive_reader, }; diff --git a/rpm2extents.c b/rpm2extents.c index c111be0a2..7dd5128de 100644 --- a/rpm2extents.c +++ b/rpm2extents.c @@ -2,7 +2,9 @@ #include "system.h" +#include #include /* rpmReadPackageFile .. */ +#include #include #include #include @@ -10,8 +12,10 @@ #include #include "lib/rpmlead.h" +#include "lib/rpmts.h" #include "lib/signature.h" #include "lib/header_internal.h" +#include "lib/rpmextents_internal.h" #include "rpmio/rpmio_internal.h" #include @@ -34,11 +38,6 @@ #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; @@ -51,38 +50,49 @@ rpm_loff_t pad_to(rpm_loff_t pos, rpm_loff_t unit) return (unit - (pos % unit)) % unit; } -static int digestor( +static struct poptOption optionsTable[] = { + { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmcliAllPoptTable, 0, + N_("Common options for all rpm modes and executables:"), NULL }, + + POPT_AUTOALIAS + POPT_AUTOHELP + POPT_TABLEEND +}; + + +static void FDDigestInit(FD_t fdi, uint8_t algos[], uint32_t algos_len){ + int algo; + + for (algo = 0; algo < algos_len; algo++) { + fdInitDigest(fdi, algos[algo], 0); + } +} + +static int FDWriteDigests( FD_t fdi, FD_t fdo, - FD_t validationo, uint8_t algos[], - uint32_t algos_len -) + 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; - } + ssize_t fdilength = fdOp(fdi, FDSTAT_READ)->bytes; len = sizeof(fdilength); - if (Fwrite(&fdilength, len, 1, validationo) != len) { - fprintf(stderr, _("Unable to write input length %zd\n"), fdilength); + if (Fwrite(&fdilength, len, 1, fdo) != len) { + rpmlog(RPMLOG_ERR, _("Unable to write input length %zd: %d, %s\n"), + fdilength, errno, strerror(errno)); goto exit; } len = sizeof(algos_len); - if (Fwrite(&algos_len, len, 1, validationo) != len) { - fprintf(stderr, _("Unable to write number of validation digests\n")); + if (Fwrite(&algos_len, len, 1, fdo) != len) { + algo_digest_len = (uint32_t)filedigest_len; + rpmlog(RPMLOG_ERR, _("Unable to write number of digests: %d, %s\n"), + errno, strerror(errno)); goto exit; } for (algo = 0; algo < algos_len; algo++) { @@ -93,25 +103,29 @@ static int digestor( 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")); + if (Fwrite(&algo_name_len, len, 1, fdo) != len) { + rpmlog(RPMLOG_ERR, + _("Unable to write digest algo name length: %d, %s\n"), + errno, strerror(errno)); 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")); + if (Fwrite(&algo_digest_len, len, 1, fdo) != len) { + rpmlog(RPMLOG_ERR, + _("Unable to write number of bytes for digest: %d, %s\n"), + errno, strerror(errno)); goto exit; } - if (Fwrite(algo_name, algo_name_len, 1, validationo) != algo_name_len) { - fprintf(stderr, _("Unable to write validation algo name\n")); + if (Fwrite(algo_name, algo_name_len, 1, fdo) != algo_name_len) { + rpmlog(RPMLOG_ERR, _("Unable to write digest algo name: %d, %s\n"), + errno, strerror(errno)); 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); + if (Fwrite(filedigest, algo_digest_len, 1, fdo ) != algo_digest_len) { + rpmlog(RPMLOG_ERR, + _("Unable to write digest value %u, %zu: %d, %s\n"), + algo_digest_len, filedigest_len, + errno, strerror(errno)); goto exit; } } @@ -120,7 +134,80 @@ static int digestor( return rc; } -static rpmRC process_package(FD_t fdi, FD_t validationi) +static rpmRC FDWriteSignaturesValidation(FD_t fdo, int rpmvsrc, char *msg) { + size_t len; + rpmRC rc = RPMRC_FAIL; + + if(rpmvsrc){ + rpmlog(RPMLOG_WARNING, + _("Error verifying package signatures:\n%s\n"), msg); + } + + len = sizeof(rpmvsrc); + if (Fwrite(&rpmvsrc, len, 1, fdo) != len) { + rpmlog(RPMLOG_ERR, + _("Unable to write signature verification RC code %d: %d, %s\n"), + rpmvsrc, errno, strerror(errno)); + goto exit; + } + size_t content_len = msg ? strlen(msg) : 0; + len = sizeof(content_len); + if (Fwrite(&content_len, len, 1, fdo) != len) { + rpmlog(RPMLOG_ERR, + _("Unable to write signature verification output length %zd: %d, %s\n"), + content_len, errno, strerror(errno)); + goto exit; + } + if (Fwrite(msg, content_len, 1, fdo) != content_len) { + rpmlog(RPMLOG_ERR, + _("Unable to write signature verification output %s: %d, %s\n"), + msg, errno, strerror(errno)); + goto exit; + } + + rc = RPMRC_OK; +exit: + + return rc; +} + +static rpmRC validator(FD_t fdi, FD_t digesto, FD_t sigo, + uint8_t algos[], + uint32_t algos_len){ + int rpmvsrc; + rpmRC rc = RPMRC_FAIL; + char *msg = NULL; + rpmts ts = rpmtsCreate(); + + rpmtsSetRootDir(ts, rpmcliRootDir); + + FDDigestInit(fdi, algos, algos_len); + + rpmvsrc = rpmcliVerifySignaturesFD(ts, fdi, &msg); + + // Write result of digest computation + if(FDWriteDigests(fdi, digesto, algos, algos_len) != RPMRC_OK) { + rpmlog(RPMLOG_ERR, _("Failed to write digests: %d, %s\n"), + errno, strerror(errno)); + goto exit; + } + + // Write result of signature validation. + if(FDWriteSignaturesValidation(sigo, rpmvsrc, msg)) { + rpmlog(RPMLOG_ERR, + _("Failed to write signature verification result: %d, %s\n"), + errno, strerror(errno)); + goto exit; + } + rc = RPMRC_OK; +exit: + if(msg) { + free(msg); + } + return rc; +} + +static rpmRC process_package(FD_t fdi, FD_t digestori, FD_t validationi) { uint32_t diglen; /* GNU C extension: can use diglen from outer context */ @@ -148,7 +235,7 @@ static rpmRC process_package(FD_t fdi, FD_t validationi) rpm_mode_t mode; char *rpmio_flags = NULL, *zeros; const unsigned char *digest; - rpm_loff_t pos, size, pad, validation_pos; + rpm_loff_t pos, size, pad, digest_pos, validation_pos, digest_table_pos; uint32_t offset_ix = 0; size_t len; int next = 0; @@ -156,24 +243,24 @@ 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")); + rpmlog(RPMLOG_ERR, _("Error reading package\n")); exit(EXIT_FAILURE); } if (rpmLeadWrite(fdo, h)) { - fprintf(stderr, _("Unable to write package lead: %s\n"), + rpmlog(RPMLOG_ERR, _("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)); + rpmlog(RPMLOG_ERR, _("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)); + rpmlog(RPMLOG_ERR, _("Unable to write headers: %s\n"), Fstrerror(fdo)); exit(EXIT_FAILURE); } @@ -187,7 +274,7 @@ static rpmRC process_package(FD_t fdi, FD_t validationi) free(rpmio_flags); if (gzdi == NULL) { - fprintf(stderr, _("cannot re-open payload: %s\n"), Fstrerror(gzdi)); + rpmlog(RPMLOG_ERR, _("cannot re-open payload: %s\n"), Fstrerror(gzdi)); exit(EXIT_FAILURE); } @@ -230,7 +317,7 @@ static rpmRC process_package(FD_t fdi, FD_t validationi) } pad = pad_to(pos, fundamental_block_size); if (Fwrite(zeros, sizeof(char), pad, fdo) != pad) { - fprintf(stderr, _("Unable to write padding\n")); + rpmlog(RPMLOG_ERR, _("Unable to write padding\n")); rc = RPMRC_FAIL; goto exit; } @@ -243,7 +330,12 @@ static rpmRC process_package(FD_t fdi, FD_t validationi) size = rpmfiFSize(fi); rc = rpmfiArchiveReadToFile(fi, fdo, 0); if (rc != RPMRC_OK) { - fprintf(stderr, _("rpmfiArchiveReadToFile failed with %d\n"), rc); + char *errstr = rpmfileStrerror(rc); + rpmlog(RPMLOG_ERR, + _("rpmfiArchiveReadToFile failed while extracting "\ + "\"%s\" with RC %d: %s\n"), + rpmfiFN(fi), rc, errstr); + free(errstr); goto exit; } pos += size; @@ -253,42 +345,53 @@ static rpmRC process_package(FD_t fdi, FD_t validationi) qsort(offsets, (size_t)offset_ix, sizeof(struct digestoffset), digestoffsetCmp); + validation_pos = pos; + ssize_t validation_len = ufdCopy(validationi, fdo); + if (validation_len == -1) { + rpmlog(RPMLOG_ERR, _("validation output ufdCopy failed\n")); + rc = RPMRC_FAIL; + goto exit; + } + + digest_table_pos = validation_pos + validation_len; + len = sizeof(offset_ix); if (Fwrite(&offset_ix, len, 1, fdo) != len) { - fprintf(stderr, _("Unable to write length of table\n")); + rpmlog(RPMLOG_ERR, _("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")); + rpmlog(RPMLOG_ERR, _("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")); + rpmlog(RPMLOG_ERR, _("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")); + rpmlog(RPMLOG_ERR, _("Unable to write offset\n")); rc = RPMRC_FAIL; goto exit; } } - validation_pos = ( - pos + sizeof(offset_ix) + sizeof(diglen) + + digest_pos = ( + digest_table_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")); + ssize_t digest_len = ufdCopy(digestori, fdo); + if (digest_len == -1) { + rpmlog(RPMLOG_ERR, _("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. @@ -297,25 +400,15 @@ static rpmRC process_package(FD_t fdi, FD_t validationi) 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")); + rpmlog(RPMLOG_ERR, _("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")); + struct extents_footer_t footer = {.offsets = {validation_pos, digest_table_pos, digest_pos}, .magic = EXTENTS_MAGIC}; + len = sizeof(footer); + if (Fwrite(&footer, len, 1, fdo) != len) { + rpmlog(RPMLOG_ERR, _("Unable to write footer\n")); rc = RPMRC_FAIL; goto exit; } @@ -327,104 +420,202 @@ static rpmRC process_package(FD_t fdi, FD_t validationi) 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; +static off_t ufdTee(FD_t sfd, FD_t *fds, int len) +{ + char buf[BUFSIZ]; + ssize_t rdbytes, wrbytes; + off_t total = 0; + + while (1) { + rdbytes = Fread(buf, sizeof(buf[0]), sizeof(buf), sfd); + + if (rdbytes > 0) { + for(int i=0; i < len; i++) { + wrbytes = Fwrite(buf, sizeof(buf[0]), rdbytes, fds[i]); + if (wrbytes != rdbytes) { + rpmlog(RPMLOG_ERR, + _("Error wriing to FD %d: %s\n"), + i, Fstrerror(fds[i])); + total = -1; + break; + } + } + if(total == -1){ + break; + } + total += wrbytes; + } else { + if (rdbytes < 0) + total = -1; + break; + } + } - xsetprogname(argv[0]); /* Portability call -- see system.h */ - rpmReadConfigFiles(NULL, NULL); + return total; +} - if (argc > 1 && (rstreq(argv[1], "-h") || rstreq(argv[1], "--help"))) { - fprintf(stderr, _("Usage: %s [DIGESTALGO]...\n"), argv[0]); - exit(EXIT_FAILURE); - } +static rpmRC teeRpm(FD_t fdi, uint8_t algos[], uint32_t algos_len) { + rpmRC rc = RPMRC_FAIL; + off_t offt = -1; + // tee-ed stdin + int processorpipefd[2]; + int validatorpipefd[2]; + // metadata + int meta_digestpipefd[2]; + int meta_rpmsignpipefd[2]; + + pid_t cpids[2], w; + int wstatus; + FD_t fds[2]; - if (argc == 1) { - fprintf(stderr, - _("Need at least one DIGESTALGO parameter, e.g. 'SHA256'\n")); - exit(EXIT_FAILURE); + if (pipe(processorpipefd) == -1) { + rpmlog(RPMLOG_ERR, _("Processor pipe failure\n")); + return RPMRC_FAIL; } - 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(validatorpipefd) == -1) { + rpmlog(RPMLOG_ERR, _("Validator pipe failure\n")); + return RPMRC_FAIL; } - - if (pipe(mainpipefd) == -1) { - fprintf(stderr, _("Main pipe failure\n")); - exit(EXIT_FAILURE); + if (pipe(meta_digestpipefd) == -1) { + rpmlog(RPMLOG_ERR, _("Meta digest pipe failure\n")); + return RPMRC_FAIL; } - if (pipe(metapipefd) == -1) { - fprintf(stderr, _("Meta pipe failure\n")); - exit(EXIT_FAILURE); + + if (pipe(meta_rpmsignpipefd) == -1) { + rpmlog(RPMLOG_ERR, _("Meta rpm signature pipe failure\n")); + return RPMRC_FAIL; } - 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); + + cpids[0] = fork(); + if (cpids[0] == 0) { + /* child: validator */ + close(processorpipefd[0]); + close(processorpipefd[1]); + close(validatorpipefd[1]); + close(meta_digestpipefd[0]); + close(meta_rpmsignpipefd[0]); + FD_t fdi = fdDup(validatorpipefd[0]); + FD_t digesto = fdDup(meta_digestpipefd[1]); + FD_t sigo = fdDup(meta_rpmsignpipefd[1]); + close(meta_digestpipefd[1]); + close(meta_rpmsignpipefd[1]); + rc = validator(fdi, digesto, sigo, algos, algos_len); + if(rc != RPMRC_OK) { + rpmlog(RPMLOG_ERR, _("Validator failed with RC %d\n"), rc); + } Fclose(fdi); + Fclose(digesto); + Fclose(sigo); + if (rc != RPMRC_OK) { + exit(EXIT_FAILURE); + } + exit(EXIT_SUCCESS); } 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)); + cpids[1] = fork(); + if (cpids[1] == 0) { + /* child: process_package */ + close(validatorpipefd[0]); + close(validatorpipefd[1]); + close(processorpipefd[1]); + close(meta_digestpipefd[1]); + close(meta_rpmsignpipefd[1]); + FD_t fdi = fdDup(processorpipefd[0]); + close(processorpipefd[0]); + FD_t sigi = fdDup(meta_rpmsignpipefd[0]); + close(meta_rpmsignpipefd[0]); + FD_t digestori = fdDup(meta_digestpipefd[0]); + close(meta_digestpipefd[0]); + + rc = process_package(fdi, digestori, sigi); + if(rc != RPMRC_OK) { + rpmlog(RPMLOG_ERR, _("Package processor failed: %d\n"), rc); } - } - 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); + Fclose(digestori); + Fclose(sigi); + /* fdi is normally closed through the stacked file gzdi in the + * function + */ + + if (rc != RPMRC_OK) { + exit(EXIT_FAILURE); } - } else if (WIFSIGNALED(wstatus)) { - fprintf(stderr, - _("Digest process was terminated with a signal: %d\n"), - WTERMSIG(wstatus)); - cprc = EXIT_FAILURE; + exit(EXIT_SUCCESS); + + } else { - /* Don't think this can happen, but covering all bases */ - fprintf(stderr, _("Unhandled circumstance in waitpid\n")); - cprc = EXIT_FAILURE; + /* Actual parent. Read from fdi and write to both processes */ + close(processorpipefd[0]); + close(validatorpipefd[0]); + fds[0] = fdDup(processorpipefd[1]); + fds[1] = fdDup(validatorpipefd[1]); + close(validatorpipefd[1]); + close(processorpipefd[1]); + close(meta_digestpipefd[0]); + close(meta_digestpipefd[1]); + close(meta_rpmsignpipefd[0]); + close(meta_rpmsignpipefd[1]); + + rc = RPMRC_OK; + offt = ufdTee(fdi, fds, 2); + if(offt == -1){ + rpmlog(RPMLOG_ERR, _("Failed to tee RPM\n")); + rc = RPMRC_FAIL; + } + Fclose(fds[0]); + Fclose(fds[1]); + w = waitpid(cpids[0], &wstatus, 0); + if (w == -1) { + rpmlog(RPMLOG_ERR, _("waitpid cpids[0] failed\n")); + rc = RPMRC_FAIL; + } + w = waitpid(cpids[1], &wstatus, 0); + if (w == -1) { + rpmlog(RPMLOG_ERR, _("waitpid cpids[1] failed\n")); + rc = RPMRC_FAIL; + } } - if (cprc != EXIT_SUCCESS) { - rc = RPMRC_FAIL; + } + + return rc; +} + +int main(int argc, char *argv[]) { + rpmRC rc; + poptContext optCon = NULL; + const char **args = NULL; + int nb_algos = 0; + + xsetprogname(argv[0]); /* Portability call -- see system.h */ + rpmReadConfigFiles(NULL, NULL); + optCon = rpmcliInit(argc, argv, optionsTable); + poptSetOtherOptionHelp(optCon, "[OPTIONS]* "); + + if (poptPeekArg(optCon) == NULL) { + rpmlog(RPMLOG_ERR, + _("Need at least one DIGESTALGO parameter, e.g. 'SHA256'\n")); + poptPrintUsage(optCon, stderr, 0); + exit(EXIT_FAILURE); + } + + args = poptGetArgs(optCon); + + for (nb_algos=0; args[nb_algos]; nb_algos++); + uint8_t algos[nb_algos]; + for (int x = 0; x < nb_algos; x++) { + if (pgpStringVal(PGPVAL_HASHALGO, args[x], &algos[x]) != 0) + { + rpmlog(RPMLOG_ERR, + _("Unable to resolve '%s' as a digest algorithm, exiting\n"), + args[x]); + exit(EXIT_FAILURE); } } + + FD_t fdi = fdDup(STDIN_FILENO); + rc = teeRpm(fdi, algos, nb_algos); if (rc != RPMRC_OK) { /* translate rpmRC into generic failure return code. */ return EXIT_FAILURE; diff --git a/scripts/rpm2extents_dump b/scripts/rpm2extents_dump new file mode 100755 index 000000000..596a59a49 --- /dev/null +++ b/scripts/rpm2extents_dump @@ -0,0 +1,94 @@ +#!/usr/bin/env python3 + +import argparse +import binascii +import os +import struct +import sys + +MAGIC_SIZE = 8 +MAGIC_STR = b'KWTSH100' + +POS_SIZE = 8 + +def keep_position(func): + def wrapper(*args, **kwargs): + curr = args[0].tell() + res = func(*args, **kwargs) + f.seek(curr, os.SEEK_SET) + return res + return wrapper + +def read_validation_digest(f, validation_offset): + digests = [] + # validation + f.seek(validation_offset, os.SEEK_SET) + val_content_len, val_digests_num = struct.unpack('=QI', f.read(8+4)) + for i in range(val_digests_num): + algo_name_len, digest_len = struct.unpack('=II', f.read(8)) + algo_name, digest = struct.unpack(f'{algo_name_len}s{digest_len}s', f.read(algo_name_len+digest_len)) + digests.append((algo_name, binascii.hexlify(digest))) + return digests + + +def read_digests_table(f, digest_offset): + digests = [] + # validation + f.seek(digest_offset, os.SEEK_SET) + table_len, digest_len = struct.unpack('=II', f.read(8)) + + for i in range(table_len): + digest, pos = struct.unpack(f'{digest_len}sQ', f.read(digest_len + 8)) + digests.append((pos, binascii.hexlify(digest))) + return digests + +def read_signature_output(f, signature_offset): + f.seek(signature_offset, os.SEEK_SET) + signature_rc, signature_output_len = struct.unpack('=IQ', f.read(12)) + return signature_rc, f.read(signature_output_len) + +@keep_position +def parse_file(f): + digests = [] + pos_table_offset = f.seek(-8 - 3*POS_SIZE, os.SEEK_END) + signature_offset, digest_offset, validation_offset = struct.unpack('=QQQ', f.read(3*POS_SIZE)) + + validation_digests = read_validation_digest(f, validation_offset) + digests_table = read_digests_table(f, digest_offset) + signature_ouput = read_signature_output(f, signature_offset) + + return validation_digests, digests_table, signature_ouput + +@keep_position +def is_transcoded(f): + f.seek(-MAGIC_SIZE, os.SEEK_END) + magic = f.read(MAGIC_SIZE) + return magic == MAGIC_STR + +def arg_parse(): + parser = argparse.ArgumentParser() + parser.add_argument('--dump-signature', action='store_true') + parser.add_argument('--dump-file-digest-table', action='store_true') + parser.add_argument('--dump-digests', action='store_true') + parser.add_argument('file') + + return parser.parse_args() + +if __name__ == '__main__': + args = arg_parse() + f = open(args.file, 'rb') + if not is_transcoded(f): + sys.exit(1) + + validation_digests, digests_table, signature_output = parse_file(f) + if(args.dump_file_digest_table): + for digest in digests_table: + print(f"FileDigest {hex(digest[0])}: {digest[1]}") + + if(args.dump_digests): + for validation_digest in validation_digests: + print(f"HeaderDigest {validation_digest[0]} {validation_digest[1]}") + + if(args.dump_signature): + print(f"RPMSignOutput RC {signature_output[0]}\nRPMSignOutput Content {signature_output[1].decode()}") + diff --git a/tests/Makefile.am b/tests/Makefile.am index f78e17c3e..fc8a24a5e 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -36,6 +36,7 @@ TESTSUITE_AT += rpmsigdig.at TESTSUITE_AT += rpmio.at TESTSUITE_AT += rpmorder.at TESTSUITE_AT += rpmvfylevel.at +TESTSUITE_AT += rpm2extents.at EXTRA_DIST += $(TESTSUITE_AT) ## testsuite data diff --git a/tests/atlocal.in b/tests/atlocal.in index c3189d327..a110564e2 100644 --- a/tests/atlocal.in +++ b/tests/atlocal.in @@ -50,6 +50,19 @@ else CAP_DISABLED=true; fi +FSTYPE=$(stat -f -c %T /) +REFLINKABLE_FS=("xfs" "brtfs") + +REFLINK_DISABLED=true; +for item in "${REFLINKABLE_FS[@]}" +do + if test "${FSTYPE}" = "${item}" + then + REFLINK_DISABLED=false; + break + fi +done + function setup_env() { if [ -d testing ]; then @@ -82,6 +95,15 @@ function runroot() ) } +function runroot_plugins() +{ + setup_env + (unset RPM_CONFIGDIR RPM_POPTEXEC_PATH; cd ${RPMTEST} && \ + MAGIC="/magic/magic" FAKECHROOT_BASE="${RPMTEST}" fakechroot "$@" --define "_buildhost testhost" --define "_topdir /build" --nouserns + ) +} + + function runroot_other() { setup_env diff --git a/tests/rpm2extents.at b/tests/rpm2extents.at new file mode 100644 index 000000000..29165c1b9 --- /dev/null +++ b/tests/rpm2extents.at @@ -0,0 +1,141 @@ +# rpm2extents.at: Some very basic checks +# +# Copyright (C) 2022 Manu Bretelle +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +AT_BANNER([rpm2extents tests]) + +# ------------------------------ + +# check that transcoder write magic at the end +AT_SETUP([rpm2extents magic]) +AT_KEYWORDS([rpm2extents]) +AT_CHECK([runroot_other cat /data/RPMS/hello-2.0-1.x86_64.rpm | runroot_other rpm2extents SHA256 | tail -c8], +[0], +[KWTSH100], +[ignore]) +AT_CLEANUP + +# Check that transcoder writes checksig return code and content. +# +AT_SETUP([rpm2extents signature]) +AT_KEYWORDS([rpm2extents]) +AT_CHECK([ +RPMDB_INIT + +runroot_other cat /data/RPMS/hello-2.0-1.x86_64-signed.rpm | runroot_other rpm2extents SHA256 > /tmp/hello-2.0-1.x86_64-signed.rpm 2> /dev/null +rpm2extents_dump --dump-signature /tmp/hello-2.0-1.x86_64-signed.rpm +runroot rpmkeys --import /data/keys/rpm.org-rsa-2048-test.pub +runroot_other cat /data/RPMS/hello-2.0-1.x86_64-signed.rpm | runroot_other rpm2extents SHA256 > /tmp/hello-2.0-1.x86_64-signed.rpm +rpm2extents_dump --dump-signature /tmp/hello-2.0-1.x86_64-signed.rpm +], +[0], +[RPMSignOutput RC 2 +RPMSignOutput Content Header V4 RSA/SHA256 Signature, key ID 1964c5fc: NOKEY + Header SHA256 digest: OK + Header SHA1 digest: OK + Payload SHA256 digest: OK + V4 RSA/SHA256 Signature, key ID 1964c5fc: NOKEY + MD5 digest: OK + +RPMSignOutput RC 0 +RPMSignOutput Content Header V4 RSA/SHA256 Signature, key ID 1964c5fc: OK + Header SHA256 digest: OK + Header SHA1 digest: OK + Payload SHA256 digest: OK + V4 RSA/SHA256 Signature, key ID 1964c5fc: OK + MD5 digest: OK + +], +[]) +AT_CLEANUP + +AT_SETUP([rpm2extents signature verification]) +AT_KEYWORDS([rpm2extents]) +AT_CHECK([ +RPMDB_INIT + +runroot_other cat /data/RPMS/hello-2.0-1.x86_64-signed.rpm | runroot_other rpm2extents SHA256 > ${RPMTEST}/tmp/hello-2.0-1.x86_64-signed.rpm 2> /dev/null +runroot rpmkeys -Kv /tmp/hello-2.0-1.x86_64-signed.rpm; echo $? +runroot rpmkeys --import /data/keys/rpm.org-rsa-2048-test.pub +runroot_other cat /data/RPMS/hello-2.0-1.x86_64-signed.rpm | runroot_other rpm2extents SHA256 > ${RPMTEST}/tmp/hello-2.0-1.x86_64-signed.rpm +runroot rpmkeys -Kv /tmp/hello-2.0-1.x86_64-signed.rpm; echo $? +], +[0], +[/tmp/hello-2.0-1.x86_64-signed.rpm: + Header V4 RSA/SHA256 Signature, key ID 1964c5fc: NOKEY + Header SHA256 digest: OK + Header SHA1 digest: OK + Payload SHA256 digest: OK + V4 RSA/SHA256 Signature, key ID 1964c5fc: NOKEY + MD5 digest: OK +1 +/tmp/hello-2.0-1.x86_64-signed.rpm: + Header V4 RSA/SHA256 Signature, key ID 1964c5fc: OK + Header SHA256 digest: OK + Header SHA1 digest: OK + Payload SHA256 digest: OK + V4 RSA/SHA256 Signature, key ID 1964c5fc: OK + MD5 digest: OK +0 +], +[]) +AT_CLEANUP + +AT_SETUP([rpm2extents install package]) +AT_KEYWORDS([rpm2extents reflink]) +AT_SKIP_IF([$REFLINK_DISABLED]) +AT_CHECK([ +RPMDB_INIT + +runroot_other cat /data/RPMS/hello-2.0-1.x86_64.rpm | runroot_other rpm2extents SHA256 > ${RPMTEST}/tmp/hello-2.0-1.x86_64.rpm 2> /dev/null +runroot_plugins rpm -i --nodigest --nodeps --undefine=%__transaction_dbus_announce /tmp/hello-2.0-1.x86_64.rpm +], +[0], +[], +[]) +AT_CLEANUP + +AT_SETUP([reflink hardlink package]) +AT_KEYWORDS([reflink hardlink]) +AT_SKIP_IF([$REFLINK_DISABLED]) +AT_CHECK([ +RPMDB_INIT + +PKG=hlinktest-1.0-1.noarch.rpm +runroot_other cat /data/RPMS/${PKG} | runroot_other rpm2extents SHA256 > ${RPMTEST}/tmp/${PKG} 2> /dev/null +runroot_plugins rpm -i --nodigest --nodeps --undefine=%__transaction_dbus_announce /tmp/${PKG} +], +[0], +[], +[]) +AT_CLEANUP + +AT_SETUP([rpm2extents install package]) +AT_KEYWORDS([rpm2extents reflink]) +AT_SKIP_IF([$REFLINK_DISABLED]) +AT_CHECK([ +RPMDB_INIT + +runroot_other cat /data/RPMS/hello-2.0-1.x86_64.rpm | runroot_other rpm2extents SHA256 > ${RPMTEST}/tmp/hello-2.0-1.x86_64.rpm 2> /dev/null +runroot_plugins rpm -i --nodigest --nodeps --undefine=%__transaction_dbus_announce /tmp/hello-2.0-1.x86_64.rpm +], +[0], +[], +[]) +AT_CLEANUP + + diff --git a/tests/rpmtests.at b/tests/rpmtests.at index a1adab8e0..205fed6a3 100644 --- a/tests/rpmtests.at +++ b/tests/rpmtests.at @@ -21,3 +21,4 @@ m4_include([rpmreplace.at]) m4_include([rpmconfig.at]) m4_include([rpmconfig2.at]) m4_include([rpmconfig3.at]) +m4_include([rpm2extents.at])