b295de5893
Recent changes left downgradeLzmaLevel dysfunctional (because it does not recognize 'T'). In my view, the only advantage of XZ over LZMA is that XZ can can split input into blocks and compress them in parallel (resulting in a speed-up). Other advantages, such as a checksum, are immaterial for our purpose (because the package manager must verify the integrity of a package beforehand). Therefore, downgradeLzmaLevel will also downgrade XZ->LZMA automatically, for payloads smaller than 5*dictSize (the default blockSize being 3*dictSize, so that there are at least two blocks, the second block not too small).
995 lines
26 KiB
C
995 lines
26 KiB
C
/** \ingroup rpmbuild
|
|
* \file build/pack.c
|
|
* Assemble components of an RPM package.
|
|
*/
|
|
|
|
#include "system.h"
|
|
|
|
#include "rpmio_internal.h"
|
|
#include "rpmbuild.h"
|
|
#include "buildio.h"
|
|
|
|
#include "misc.h"
|
|
#include "signature.h"
|
|
#include "rpmlead.h"
|
|
#include "debug.h"
|
|
|
|
/*@access StringBuf @*/ /* compared with NULL */
|
|
/*@access TFI_t @*/ /* compared with NULL */
|
|
/*@access Header @*/ /* compared with NULL */
|
|
/*@access FD_t @*/ /* compared with NULL */
|
|
/*@access CSA_t @*/
|
|
|
|
/**
|
|
*/
|
|
static inline int genSourceRpmName(Spec spec)
|
|
/*@modifies spec->sourceRpmName @*/
|
|
{
|
|
if (spec->sourceRpmName == NULL) {
|
|
const char *name, *version, *release;
|
|
char fileName[BUFSIZ];
|
|
|
|
(void) headerNVRD(spec->packages->header, &name, &version, &release, NULL);
|
|
sprintf(fileName, "%s-%s-%s.%ssrc.rpm", name, version, release,
|
|
spec->noSource ? "no" : "");
|
|
spec->sourceRpmName = xstrdup(fileName);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @todo Create transaction set *much* earlier.
|
|
*/
|
|
static int cpio_doio(FD_t fdo, /*@unused@*/ Header h, CSA_t csa,
|
|
const char * fmode)
|
|
/*@globals rpmGlobalMacroContext,
|
|
fileSystem@*/
|
|
/*@modifies fdo, csa, rpmGlobalMacroContext, fileSystem @*/
|
|
{
|
|
const char * rootDir = "/";
|
|
rpmdb rpmdb = NULL;
|
|
rpmTransactionSet ts = rpmtransCreateSet(rpmdb, rootDir);
|
|
TFI_t fi = csa->cpioList;
|
|
const char *failedFile = NULL;
|
|
FD_t cfd;
|
|
int rc, ec;
|
|
|
|
{
|
|
/*@-nullpass@*/
|
|
(void) Fflush(fdo);
|
|
cfd = Fdopen(fdDup(Fileno(fdo)), fmode);
|
|
/*@=nullpass@*/
|
|
}
|
|
if (cfd == NULL)
|
|
return 1;
|
|
|
|
rc = fsmSetup(fi->fsm, FSM_PKGBUILD, ts, fi, cfd,
|
|
&csa->cpioArchiveSize, &failedFile);
|
|
(void) Fclose(cfd);
|
|
ec = fsmTeardown(fi->fsm);
|
|
if (!rc) rc = ec;
|
|
|
|
if (rc) {
|
|
if (failedFile)
|
|
rpmError(RPMERR_CPIO, _("create archive failed on file %s: %s\n"),
|
|
failedFile, cpioStrerror(rc));
|
|
else
|
|
rpmError(RPMERR_CPIO, _("create archive failed: %s\n"),
|
|
cpioStrerror(rc));
|
|
rc = 1;
|
|
}
|
|
|
|
failedFile = _free(failedFile);
|
|
ts = rpmtransFree(ts);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
*/
|
|
static int cpio_copy(FD_t fdo, CSA_t csa)
|
|
/*@globals fileSystem@*/
|
|
/*@modifies fdo, csa, fileSystem @*/
|
|
{
|
|
char buf[BUFSIZ];
|
|
size_t nb;
|
|
|
|
while((nb = Fread(buf, sizeof(buf[0]), sizeof(buf), csa->cpioFdIn)) > 0) {
|
|
if (Fwrite(buf, sizeof(buf[0]), nb, fdo) != nb) {
|
|
rpmError(RPMERR_CPIO, _("cpio_copy write failed: %s\n"),
|
|
Fstrerror(fdo));
|
|
return 1;
|
|
}
|
|
csa->cpioArchiveSize += nb;
|
|
}
|
|
if (Ferror(csa->cpioFdIn)) {
|
|
rpmError(RPMERR_CPIO, _("cpio_copy read failed: %s\n"),
|
|
Fstrerror(csa->cpioFdIn));
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
*/
|
|
static /*@only@*/ /*@null@*/ StringBuf addFileToTagAux(Spec spec,
|
|
const char * file, /*@only@*/ StringBuf sb)
|
|
/*@globals rpmGlobalMacroContext,
|
|
fileSystem@*/
|
|
/*@modifies rpmGlobalMacroContext, fileSystem @*/
|
|
{
|
|
char buf[BUFSIZ];
|
|
const char * fn = buf;
|
|
FILE * f;
|
|
FD_t fd;
|
|
|
|
/* XXX use rpmGenPath(rootdir, "%{_buildir}/%{_buildsubdir}/", file) */
|
|
fn = rpmGetPath("%{_builddir}/", spec->buildSubdir, "/", file, NULL);
|
|
|
|
fd = Fopen(fn, "r.ufdio");
|
|
if (fn != buf) fn = _free(fn);
|
|
if (fd == NULL || Ferror(fd)) {
|
|
sb = freeStringBuf(sb);
|
|
return NULL;
|
|
}
|
|
/*@-type@*/ /* FIX: cast? */
|
|
if ((f = fdGetFp(fd)) != NULL)
|
|
/*@=type@*/
|
|
while (fgets(buf, sizeof(buf), f)) {
|
|
/* XXX display fn in error msg */
|
|
if (expandMacros(spec, spec->macros, buf, sizeof(buf))) {
|
|
rpmError(RPMERR_BADSPEC, _("line: %s\n"), buf);
|
|
sb = freeStringBuf(sb);
|
|
break;
|
|
}
|
|
appendStringBuf(sb, buf);
|
|
}
|
|
(void) Fclose(fd);
|
|
|
|
return sb;
|
|
}
|
|
|
|
/**
|
|
*/
|
|
static int addFileToTag(Spec spec, const char * file, Header h, int tag)
|
|
/*@globals rpmGlobalMacroContext,
|
|
fileSystem@*/
|
|
/*@modifies h, rpmGlobalMacroContext, fileSystem @*/
|
|
{
|
|
HGE_t hge = (HGE_t)headerGetEntryMinMemory;
|
|
StringBuf sb = newStringBuf();
|
|
char *s;
|
|
|
|
if (hge(h, tag, NULL, (void **)&s, NULL)) {
|
|
appendLineStringBuf(sb, s);
|
|
(void) headerRemoveEntry(h, tag);
|
|
}
|
|
|
|
if ((sb = addFileToTagAux(spec, file, sb)) == NULL)
|
|
return 1;
|
|
|
|
(void) headerAddEntry(h, tag, RPM_STRING_TYPE, getStringBuf(sb), 1);
|
|
|
|
sb = freeStringBuf(sb);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
*/
|
|
static int addFileToArrayTag(Spec spec, const char *file, Header h, int tag)
|
|
/*@globals rpmGlobalMacroContext,
|
|
fileSystem@*/
|
|
/*@modifies h, rpmGlobalMacroContext, fileSystem @*/
|
|
{
|
|
StringBuf sb = newStringBuf();
|
|
char *s;
|
|
|
|
if ((sb = addFileToTagAux(spec, file, sb)) == NULL)
|
|
return 1;
|
|
|
|
s = getStringBuf(sb);
|
|
(void) headerAddOrAppendEntry(h, tag, RPM_STRING_ARRAY_TYPE, &s, 1);
|
|
|
|
sb = freeStringBuf(sb);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
*/
|
|
static int processScriptFiles(Spec spec, Package pkg)
|
|
/*@globals rpmGlobalMacroContext,
|
|
fileSystem@*/
|
|
/*@modifies pkg->header, rpmGlobalMacroContext, fileSystem @*/
|
|
{
|
|
struct TriggerFileEntry *p;
|
|
|
|
if (pkg->preInFile) {
|
|
if (addFileToTag(spec, pkg->preInFile, pkg->header, RPMTAG_PREIN)) {
|
|
rpmError(RPMERR_BADFILENAME,
|
|
_("Could not open PreIn file: %s\n"), pkg->preInFile);
|
|
return RPMERR_BADFILENAME;
|
|
}
|
|
}
|
|
if (pkg->preUnFile) {
|
|
if (addFileToTag(spec, pkg->preUnFile, pkg->header, RPMTAG_PREUN)) {
|
|
rpmError(RPMERR_BADFILENAME,
|
|
_("Could not open PreUn file: %s\n"), pkg->preUnFile);
|
|
return RPMERR_BADFILENAME;
|
|
}
|
|
}
|
|
if (pkg->postInFile) {
|
|
if (addFileToTag(spec, pkg->postInFile, pkg->header, RPMTAG_POSTIN)) {
|
|
rpmError(RPMERR_BADFILENAME,
|
|
_("Could not open PostIn file: %s\n"), pkg->postInFile);
|
|
return RPMERR_BADFILENAME;
|
|
}
|
|
}
|
|
if (pkg->postUnFile) {
|
|
if (addFileToTag(spec, pkg->postUnFile, pkg->header, RPMTAG_POSTUN)) {
|
|
rpmError(RPMERR_BADFILENAME,
|
|
_("Could not open PostUn file: %s\n"), pkg->postUnFile);
|
|
return RPMERR_BADFILENAME;
|
|
}
|
|
}
|
|
if (pkg->verifyFile) {
|
|
if (addFileToTag(spec, pkg->verifyFile, pkg->header,
|
|
RPMTAG_VERIFYSCRIPT)) {
|
|
rpmError(RPMERR_BADFILENAME,
|
|
_("Could not open VerifyScript file: %s\n"), pkg->verifyFile);
|
|
return RPMERR_BADFILENAME;
|
|
}
|
|
}
|
|
|
|
for (p = pkg->triggerFiles; p != NULL; p = p->next) {
|
|
(void) headerAddOrAppendEntry(pkg->header, RPMTAG_TRIGGERSCRIPTPROG,
|
|
RPM_STRING_ARRAY_TYPE, &(p->prog), 1);
|
|
if (p->script) {
|
|
(void) headerAddOrAppendEntry(pkg->header, RPMTAG_TRIGGERSCRIPTS,
|
|
RPM_STRING_ARRAY_TYPE, &(p->script), 1);
|
|
} else if (p->fileName) {
|
|
if (addFileToArrayTag(spec, p->fileName, pkg->header,
|
|
RPMTAG_TRIGGERSCRIPTS)) {
|
|
rpmError(RPMERR_BADFILENAME,
|
|
_("Could not open Trigger script file: %s\n"),
|
|
p->fileName);
|
|
return RPMERR_BADFILENAME;
|
|
}
|
|
} else {
|
|
/* This is dumb. When the header supports NULL string */
|
|
/* this will go away. */
|
|
char *bull = "";
|
|
(void) headerAddOrAppendEntry(pkg->header, RPMTAG_TRIGGERSCRIPTS,
|
|
RPM_STRING_ARRAY_TYPE, &bull, 1);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int readRPM(const char *fileName, Spec *specp, struct rpmlead *lead,
|
|
Header *sigs, CSA_t csa)
|
|
{
|
|
FD_t fdi;
|
|
Spec spec;
|
|
rpmRC rc;
|
|
|
|
fdi = (fileName != NULL)
|
|
? Fopen(fileName, "r.ufdio")
|
|
: fdDup(STDIN_FILENO);
|
|
|
|
if (fdi == NULL || Ferror(fdi)) {
|
|
rpmError(RPMERR_BADMAGIC, _("readRPM: open %s: %s\n"),
|
|
(fileName ? fileName : "<stdin>"),
|
|
Fstrerror(fdi));
|
|
if (fdi) (void) Fclose(fdi);
|
|
return RPMERR_BADMAGIC;
|
|
}
|
|
|
|
/* Get copy of lead */
|
|
/*@-sizeoftype@*/
|
|
if ((rc = Fread(lead, sizeof(char), sizeof(*lead), fdi)) != sizeof(*lead)) {
|
|
rpmError(RPMERR_BADMAGIC, _("readRPM: read %s: %s\n"),
|
|
(fileName ? fileName : "<stdin>"),
|
|
Fstrerror(fdi));
|
|
return RPMERR_BADMAGIC;
|
|
}
|
|
/*@=sizeoftype@*/
|
|
|
|
/* XXX FIXME: EPIPE on <stdin> */
|
|
if (Fseek(fdi, 0, SEEK_SET) == -1) {
|
|
rpmError(RPMERR_FSEEK, _("%s: Fseek failed: %s\n"),
|
|
(fileName ? fileName : "<stdin>"), Fstrerror(fdi));
|
|
return RPMERR_FSEEK;
|
|
}
|
|
|
|
/* Reallocate build data structures */
|
|
spec = newSpec();
|
|
spec->packages = newPackage(spec);
|
|
|
|
/* XXX the header just allocated will be allocated again */
|
|
spec->packages->header = headerFree(spec->packages->header);
|
|
|
|
/* Read the rpm lead, signatures, and header */
|
|
rc = rpmReadPackageInfo(fdi, sigs, &spec->packages->header);
|
|
switch (rc) {
|
|
case RPMRC_BADMAGIC:
|
|
rpmError(RPMERR_BADMAGIC, _("readRPM: %s is not an RPM package\n"),
|
|
(fileName ? fileName : "<stdin>"));
|
|
return RPMERR_BADMAGIC;
|
|
case RPMRC_OK:
|
|
break;
|
|
case RPMRC_FAIL:
|
|
case RPMRC_BADSIZE:
|
|
case RPMRC_SHORTREAD:
|
|
default:
|
|
rpmError(RPMERR_BADMAGIC, _("readRPM: reading header from %s\n"),
|
|
(fileName ? fileName : "<stdin>"));
|
|
return RPMERR_BADMAGIC;
|
|
/*@notreached@*/ break;
|
|
}
|
|
|
|
/*@-branchstate@*/
|
|
if (specp)
|
|
*specp = spec;
|
|
else
|
|
spec = freeSpec(spec);
|
|
/*@=branchstate@*/
|
|
|
|
if (csa != NULL)
|
|
csa->cpioFdIn = fdi;
|
|
else
|
|
(void) Fclose(fdi);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#if 0
|
|
/*@unchecked@*/
|
|
static unsigned char header_magic[8] = {
|
|
0x8e, 0xad, 0xe8, 0x01, 0x00, 0x00, 0x00, 0x00
|
|
};
|
|
#endif
|
|
|
|
#define RPMPKGVERSION_MIN 30004
|
|
#define RPMPKGVERSION_MAX 40003
|
|
/*@unchecked@*/
|
|
static int rpmpkg_version = -1;
|
|
|
|
static int rpmLeadVersion(void)
|
|
/*@globals rpmpkg_version, rpmGlobalMacroContext @*/
|
|
/*@modifies rpmpkg_version, rpmGlobalMacroContext @*/
|
|
{
|
|
int rpmlead_version;
|
|
|
|
/* Intitialize packaging version from macro configuration. */
|
|
if (rpmpkg_version < 0) {
|
|
rpmpkg_version = rpmExpandNumeric("%{?_package_version}");
|
|
if (rpmpkg_version < RPMPKGVERSION_MIN)
|
|
rpmpkg_version = RPMPKGVERSION_MIN;
|
|
if (rpmpkg_version > RPMPKGVERSION_MAX)
|
|
rpmpkg_version = RPMPKGVERSION_MAX;
|
|
}
|
|
|
|
rpmlead_version = rpmpkg_version / 10000;
|
|
if (rpmlead_version < 3 || rpmlead_version > 4)
|
|
rpmlead_version = 3;
|
|
return rpmlead_version;
|
|
}
|
|
|
|
// Estimate the uncompressed size of cpio archive before it is actually written.
|
|
static uint64_t calcArchiveSize(TFI_t fi)
|
|
{
|
|
uint64_t size = 124; // cpio trailer
|
|
for (int i = 0; i < fi->fc; i++) {
|
|
if (fi->actions[i] == FA_SKIP) // %ghost
|
|
continue;
|
|
size += 110; // cpio header
|
|
size += strlen(fi->apath[i]) + 1;
|
|
size = (size + 3) & ~(uint64_t)3;
|
|
if (!S_ISREG(fi->fsts[i].st_mode) && !S_ISLNK(fi->fsts[i].st_mode))
|
|
continue;
|
|
if (fi->fsts[i].st_nlink > 1) {
|
|
// Only the first hardlink occurrence counts.
|
|
int found = 0;
|
|
for (int j = i - 1; j >= 0; j--) {
|
|
if (fi->actions[j] == FA_SKIP)
|
|
continue;
|
|
if (fi->fsts[i].st_dev != fi->fsts[j].st_dev)
|
|
continue;
|
|
if (fi->fsts[i].st_ino != fi->fsts[j].st_ino)
|
|
continue;
|
|
found = 1;
|
|
break;
|
|
}
|
|
if (found)
|
|
continue;
|
|
}
|
|
// Valid for symlinks, target not null-terminated.
|
|
size += fi->fsts[i].st_size;
|
|
size = (size + 3) & ~(uint64_t)3;
|
|
}
|
|
return size;
|
|
}
|
|
|
|
// LZMA compressions levels 6-9 are equivalent, except for the dictionary size,
|
|
// which is 8-64M. For smaller inputs, levels 7-9 are downgraded automatically.
|
|
static void downgradeLzmaLevel(char *mode, uint64_t archiveSize)
|
|
{
|
|
if (!(mode[1] >= '0' && mode[1] <= '9'))
|
|
return;
|
|
char *p = &mode[2];
|
|
if (*p == 'T') {
|
|
do
|
|
p++;
|
|
while (*p >= '0' && *p <= '9');
|
|
}
|
|
if (!(p[0] == '.' && (p[1] == 'l' || p[1] == 'x') && p[2] == 'z'))
|
|
return;
|
|
#define S(m) ((m << 20) + (m << 10))
|
|
// For small payloads, downgrade XZ->LZMA automatically. XZ only makes
|
|
// sense in multi-threaded mode (to speed up compression). The default
|
|
// block size in multi-threaded mode is three times the dictionary size.
|
|
// There has to be at least two blocks, the second block not too small,
|
|
// so we require at least five times the dictionary size.
|
|
#define T(m) ((m << 20) * 5)
|
|
#define ForceLzma memcpy(&mode[2], ".lzdio", 7)
|
|
switch (mode[1]) {
|
|
case '9':
|
|
if (archiveSize < T(64)) ForceLzma;
|
|
if (archiveSize > S(32)) break;
|
|
mode[1] = '8';
|
|
/*@fallthrough@*/
|
|
case '8':
|
|
if (archiveSize < T(32)) ForceLzma;
|
|
if (archiveSize > S(16)) break;
|
|
mode[1] = '7';
|
|
/*@fallthrough@*/
|
|
case '7':
|
|
if (archiveSize < T(16)) ForceLzma;
|
|
if (archiveSize > S(8)) break;
|
|
mode[1] = '6';
|
|
/*@fallthrough@*/
|
|
case '6':
|
|
case '5':
|
|
if (archiveSize < T(8)) ForceLzma;
|
|
break;
|
|
case '4':
|
|
case '3':
|
|
if (archiveSize < T(4)) ForceLzma;
|
|
break;
|
|
case '2':
|
|
if (archiveSize < T(2)) ForceLzma;
|
|
break;
|
|
case '1':
|
|
if (archiveSize < T(1)) ForceLzma;
|
|
break;
|
|
case 0:
|
|
// Dictionary size is 256K, but block size is 1M.
|
|
if (archiveSize < (7<<18)) ForceLzma;
|
|
}
|
|
#undef S
|
|
#undef T
|
|
}
|
|
|
|
int writeRPM(Header *hdrp, const char *fileName, int type,
|
|
CSA_t csa, char *passPhrase, const char **cookie)
|
|
{
|
|
FD_t fd = NULL;
|
|
FD_t ifd = NULL;
|
|
int count, sigtype;
|
|
const char * sigtarget;
|
|
const char * sha1 = NULL;
|
|
const char *s;
|
|
char buf[BUFSIZ];
|
|
Header h;
|
|
Header sig = NULL;
|
|
int rc = 0;
|
|
|
|
uint64_t archiveSize = calcArchiveSize(csa->cpioList);
|
|
if (archiveSize >= UINT32_MAX) { // sic, UINT32_MAX proper is kind of special
|
|
rpmError(RPMERR_FWRITE, "cpio archive too big - %uM\n",
|
|
(unsigned)(archiveSize>>20));
|
|
return RPMERR_FWRITE;
|
|
}
|
|
|
|
/* Transfer header reference form *hdrp to h. */
|
|
h = headerLink(*hdrp);
|
|
*hdrp = headerFree(*hdrp);
|
|
|
|
if (Fileno(csa->cpioFdIn) < 0) {
|
|
csa->cpioArchiveSize = 0;
|
|
/* Add a bogus archive size to the Header */
|
|
(void) headerAddEntry(h, RPMTAG_ARCHIVESIZE, RPM_INT32_TYPE,
|
|
&csa->cpioArchiveSize, 1);
|
|
}
|
|
|
|
/* Binary packages now have explicit Provides: name = version-release. */
|
|
if (type == RPMLEAD_BINARY)
|
|
providePackageNVR(h);
|
|
|
|
char *rpmio_flags = NULL;
|
|
const char *N, *dash;
|
|
|
|
/* Save payload information */
|
|
/*@-branchstate@*/
|
|
switch(type) {
|
|
case RPMLEAD_SOURCE:
|
|
rpmio_flags = rpmExpand("%{?_source_payload}", NULL);
|
|
break;
|
|
case RPMLEAD_BINARY:
|
|
headerName(h, &N);
|
|
dash = strrchr(N, '-');
|
|
if (dash && strcmp(dash, "-debuginfo") == 0)
|
|
rpmio_flags = rpmExpand("%{?_debuginfo_payload}", NULL);
|
|
if (!(rpmio_flags && *rpmio_flags))
|
|
rpmio_flags = rpmExpand("%{?_binary_payload}", NULL);
|
|
break;
|
|
}
|
|
/*@=branchstate@*/
|
|
if (!(rpmio_flags && *rpmio_flags == 'w')) {
|
|
rpmio_flags = _free(rpmio_flags);
|
|
rpmio_flags = xstrdup("w9.gzdio");
|
|
}
|
|
downgradeLzmaLevel(rpmio_flags, archiveSize);
|
|
s = strchr(rpmio_flags, '.');
|
|
if (s) {
|
|
(void) headerAddEntry(h, RPMTAG_PAYLOADFORMAT, RPM_STRING_TYPE, "cpio", 1);
|
|
if (s[1] == 'g' && s[2] == 'z')
|
|
(void) headerAddEntry(h, RPMTAG_PAYLOADCOMPRESSOR, RPM_STRING_TYPE,
|
|
"gzip", 1);
|
|
#ifdef HAVE_BZLIB_H
|
|
if (s[1] == 'b' && s[2] == 'z') {
|
|
(void) headerAddEntry(h, RPMTAG_PAYLOADCOMPRESSOR, RPM_STRING_TYPE,
|
|
"bzip2", 1);
|
|
(void) rpmlibNeedsFeature(h, "PayloadIsBzip2", NULL);
|
|
}
|
|
#endif
|
|
if (s[1] == 'l' && s[2] == 'z') {
|
|
(void) headerAddEntry(h, RPMTAG_PAYLOADCOMPRESSOR, RPM_STRING_TYPE,
|
|
"lzma", 1);
|
|
(void) rpmlibNeedsFeature(h, "PayloadIsLzma", NULL);
|
|
}
|
|
if (s[1] == 'x' && s[2] == 'z') {
|
|
(void) headerAddEntry(h, RPMTAG_PAYLOADCOMPRESSOR, RPM_STRING_TYPE,
|
|
"xz", 1);
|
|
(void) rpmlibNeedsFeature(h, "PayloadIsXz", NULL);
|
|
}
|
|
strcpy(buf, rpmio_flags);
|
|
buf[s - rpmio_flags] = '\0';
|
|
(void) headerAddEntry(h, RPMTAG_PAYLOADFLAGS, RPM_STRING_TYPE, buf+1, 1);
|
|
}
|
|
|
|
/* Create and add the cookie */
|
|
if (cookie) {
|
|
if (!headerGetEntry(h, RPMTAG_BUILDHOST, NULL, (void **)&s, NULL))
|
|
s = buildHost();
|
|
sprintf(buf, "%s %d", s, (int) (*getBuildTime()));
|
|
*cookie = xstrdup(buf);
|
|
(void) headerAddEntry(h, RPMTAG_COOKIE, RPM_STRING_TYPE, *cookie, 1);
|
|
}
|
|
|
|
/* Reallocate the header into one contiguous region. */
|
|
h = headerReload(h, RPMTAG_HEADERIMMUTABLE);
|
|
if (h == NULL) { /* XXX can't happen */
|
|
rc = RPMERR_RELOAD;
|
|
rpmError(RPMERR_RELOAD, _("Unable to create immutable header region.\n"));
|
|
goto exit;
|
|
}
|
|
/* Re-reference reallocated header. */
|
|
*hdrp = headerLink(h);
|
|
|
|
/*
|
|
* Write the header+archive into a temp file so that the size of
|
|
* archive (after compression) can be added to the header.
|
|
*/
|
|
if (makeTempFile(NULL, &sigtarget, &fd)) {
|
|
rc = RPMERR_CREATE;
|
|
rpmError(RPMERR_CREATE, _("Unable to open temp file.\n"));
|
|
goto exit;
|
|
}
|
|
|
|
if (headerWrite(fd, h, HEADER_MAGIC_YES)) {
|
|
rc = RPMERR_NOSPACE;
|
|
rpmError(RPMERR_NOSPACE, _("Unable to write temp header\n"));
|
|
} else { /* Write the archive and get the size */
|
|
if (csa->cpioList != NULL) {
|
|
rc = cpio_doio(fd, h, csa, rpmio_flags);
|
|
} else if (Fileno(csa->cpioFdIn) >= 0) {
|
|
rc = cpio_copy(fd, csa);
|
|
} else {
|
|
rc = RPMERR_BADARG;
|
|
rpmError(RPMERR_BADARG, _("Bad CSA data\n"));
|
|
}
|
|
}
|
|
rpmio_flags = _free(rpmio_flags);
|
|
|
|
if (rc)
|
|
goto exit;
|
|
|
|
/*
|
|
* Set the actual archive size, and rewrite the header.
|
|
* This used to be done using headerModifyEntry(), but now that headers
|
|
* have regions, the value is scribbled directly into the header data
|
|
* area. Some new scheme for adding the final archive size will have
|
|
* to be devised if headerGetEntryMinMemory() ever changes to return
|
|
* a pointer to memory not in the region, probably by appending
|
|
* the archive size to the header region rather than including the
|
|
* archive size within the header region.
|
|
*/
|
|
if (Fileno(csa->cpioFdIn) < 0) {
|
|
if (archiveSize != csa->cpioArchiveSize) {
|
|
rpmError(RPMERR_FWRITE, "wrong archive size: %" PRIu64 " -> %u\n",
|
|
archiveSize, csa->cpioArchiveSize);
|
|
rc = RPMERR_FWRITE;
|
|
goto exit;
|
|
}
|
|
HGE_t hge = (HGE_t)headerGetEntryMinMemory;
|
|
uint32_t *sizep;
|
|
if (hge(h, RPMTAG_ARCHIVESIZE, NULL, (void *)&sizep, NULL))
|
|
*sizep = csa->cpioArchiveSize;
|
|
else {
|
|
rpmError(RPMERR_FWRITE, "cannot add RPMTAG_ARCHIVESIZE\n");
|
|
rc = RPMERR_FWRITE;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
(void) Fflush(fd);
|
|
if (Fseek(fd, 0L, SEEK_SET) == -1) {
|
|
rc = RPMERR_FSEEK;
|
|
rpmError(RPMERR_FSEEK, _("%s: Fseek failed: %s\n"),
|
|
sigtarget, Fstrerror(fd));
|
|
}
|
|
|
|
fdInitDigest(fd, PGPHASHALGO_SHA1, 0);
|
|
if (headerWrite(fd, h, HEADER_MAGIC_YES)) {
|
|
rc = RPMERR_NOSPACE;
|
|
rpmError(RPMERR_NOSPACE, _("Unable to write final header\n"));
|
|
}
|
|
(void) Fflush(fd);
|
|
fdFiniDigest(fd, PGPHASHALGO_SHA1, (void **)&sha1, NULL, 1);
|
|
|
|
(void) Fclose(fd);
|
|
fd = NULL;
|
|
(void) Unlink(fileName);
|
|
|
|
if (rc)
|
|
goto exit;
|
|
|
|
/* Generate the signature */
|
|
(void) fflush(stdout);
|
|
sig = rpmNewSignature();
|
|
(void) rpmAddSignature(sig, sigtarget, RPMSIGTAG_SIZE, passPhrase);
|
|
(void) rpmAddSignature(sig, sigtarget, RPMSIGTAG_MD5, passPhrase);
|
|
if ((sigtype = rpmLookupSignatureType(RPMLOOKUPSIG_QUERY)) > 0) {
|
|
rpmMessage(RPMMESS_NORMAL, _("Generating signature: %d\n"), sigtype);
|
|
(void) rpmAddSignature(sig, sigtarget, sigtype, passPhrase);
|
|
}
|
|
|
|
if (sha1) {
|
|
(void) headerAddEntry(sig, RPMTAG_SHA1HEADER, RPM_STRING_TYPE, sha1, 1);
|
|
sha1 = _free(sha1);
|
|
}
|
|
|
|
/* Reallocate the signature into one contiguous region. */
|
|
sig = headerReload(sig, RPMTAG_HEADERSIGNATURES);
|
|
if (sig == NULL) { /* XXX can't happen */
|
|
rc = RPMERR_RELOAD;
|
|
rpmError(RPMERR_RELOAD, _("Unable to reload signature header.\n"));
|
|
goto exit;
|
|
}
|
|
|
|
/* Open the output file */
|
|
fd = Fopen(fileName, "w.ufdio");
|
|
if (fd == NULL || Ferror(fd)) {
|
|
rc = RPMERR_CREATE;
|
|
rpmError(RPMERR_CREATE, _("Could not open %s: %s\n"),
|
|
fileName, Fstrerror(fd));
|
|
goto exit;
|
|
}
|
|
|
|
/* Write the lead section into the package. */
|
|
{ int archnum = -1;
|
|
int osnum = -1;
|
|
struct rpmlead lead;
|
|
|
|
if (Fileno(csa->cpioFdIn) < 0) {
|
|
#ifndef DYING
|
|
rpmGetArchInfo(NULL, &archnum);
|
|
rpmGetOsInfo(NULL, &osnum);
|
|
#endif
|
|
} else if (csa->lead != NULL) {
|
|
archnum = csa->lead->archnum;
|
|
osnum = csa->lead->osnum;
|
|
}
|
|
|
|
memset(&lead, 0, sizeof(lead));
|
|
lead.major = rpmLeadVersion();
|
|
lead.minor = 0;
|
|
lead.type = type;
|
|
lead.archnum = archnum;
|
|
lead.osnum = osnum;
|
|
lead.signature_type = RPMSIGTYPE_HEADERSIG;
|
|
|
|
{ const char *name, *version, *release;
|
|
(void) headerNVR(h, &name, &version, &release);
|
|
sprintf(buf, "%s-%s-%s", name, version, release);
|
|
strncpy(lead.name, buf, sizeof(lead.name));
|
|
}
|
|
|
|
if (writeLead(fd, &lead)) {
|
|
rc = RPMERR_NOSPACE;
|
|
rpmError(RPMERR_NOSPACE, _("Unable to write package: %s\n"),
|
|
Fstrerror(fd));
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
/* Write the signature section into the package. */
|
|
rc = rpmWriteSignature(fd, sig);
|
|
if (rc)
|
|
goto exit;
|
|
|
|
/* Append the header and archive */
|
|
ifd = Fopen(sigtarget, "r.ufdio");
|
|
if (ifd == NULL || Ferror(ifd)) {
|
|
rc = RPMERR_READ;
|
|
rpmError(RPMERR_READ, _("Unable to open sigtarget %s: %s\n"),
|
|
sigtarget, Fstrerror(ifd));
|
|
goto exit;
|
|
}
|
|
|
|
/* Add signatures to header, and write header into the package. */
|
|
{ Header nh = headerRead(ifd, HEADER_MAGIC_YES);
|
|
|
|
if (nh == NULL) {
|
|
rc = RPMERR_READ;
|
|
rpmError(RPMERR_READ, _("Unable to read header from %s: %s\n"),
|
|
sigtarget, Fstrerror(ifd));
|
|
goto exit;
|
|
}
|
|
|
|
#ifdef NOTYET
|
|
(void) headerMergeLegacySigs(nh, sig);
|
|
#endif
|
|
|
|
rc = headerWrite(fd, nh, HEADER_MAGIC_YES);
|
|
nh = headerFree(nh);
|
|
|
|
if (rc) {
|
|
rc = RPMERR_NOSPACE;
|
|
rpmError(RPMERR_NOSPACE, _("Unable to write header to %s: %s\n"),
|
|
fileName, Fstrerror(fd));
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
/* Write the payload into the package. */
|
|
while ((count = Fread(buf, sizeof(buf[0]), sizeof(buf), ifd)) > 0) {
|
|
if (count == -1) {
|
|
rc = RPMERR_READ;
|
|
rpmError(RPMERR_READ, _("Unable to read payload from %s: %s\n"),
|
|
sigtarget, Fstrerror(ifd));
|
|
goto exit;
|
|
}
|
|
if (Fwrite(buf, sizeof(buf[0]), count, fd) != count) {
|
|
rc = RPMERR_NOSPACE;
|
|
rpmError(RPMERR_NOSPACE, _("Unable to write payload to %s: %s\n"),
|
|
fileName, Fstrerror(fd));
|
|
goto exit;
|
|
}
|
|
}
|
|
rc = 0;
|
|
|
|
exit:
|
|
sha1 = _free(sha1);
|
|
h = headerFree(h);
|
|
sig = rpmFreeSignature(sig);
|
|
if (ifd) {
|
|
(void) Fclose(ifd);
|
|
ifd = NULL;
|
|
}
|
|
if (fd) {
|
|
(void) Fclose(fd);
|
|
fd = NULL;
|
|
}
|
|
if (sigtarget) {
|
|
(void) Unlink(sigtarget);
|
|
sigtarget = _free(sigtarget);
|
|
}
|
|
|
|
if (rc == 0)
|
|
rpmMessage(RPMMESS_NORMAL, _("Wrote: %s\n"), fileName);
|
|
else
|
|
(void) Unlink(fileName);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*@unchecked@*/
|
|
static int_32 copyTags[] = {
|
|
RPMTAG_CHANGELOGTIME,
|
|
RPMTAG_CHANGELOGNAME,
|
|
RPMTAG_CHANGELOGTEXT,
|
|
0
|
|
};
|
|
|
|
int packageBinaries(Spec spec)
|
|
{
|
|
struct cpioSourceArchive_s csabuf;
|
|
CSA_t csa = &csabuf;
|
|
int rc;
|
|
const char *errorString;
|
|
Package pkg;
|
|
|
|
for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) {
|
|
const char *fn;
|
|
|
|
if (pkg->fileList == NULL)
|
|
continue;
|
|
|
|
if ((rc = processScriptFiles(spec, pkg)))
|
|
return rc;
|
|
|
|
if (spec->cookie) {
|
|
(void) headerAddEntry(pkg->header, RPMTAG_COOKIE,
|
|
RPM_STRING_TYPE, spec->cookie, 1);
|
|
}
|
|
|
|
/* Copy changelog from src rpm */
|
|
headerCopyTags(spec->packages->header, pkg->header, copyTags);
|
|
|
|
(void) headerAddEntry(pkg->header, RPMTAG_RPMVERSION,
|
|
RPM_STRING_TYPE, VERSION, 1);
|
|
if (!headerIsEntry(pkg->header, RPMTAG_BUILDHOST))
|
|
(void) headerAddEntry(pkg->header, RPMTAG_BUILDHOST,
|
|
RPM_STRING_TYPE, buildHost(), 1);
|
|
(void) headerAddEntry(pkg->header, RPMTAG_BUILDTIME,
|
|
RPM_INT32_TYPE, getBuildTime(), 1);
|
|
|
|
providePackageNVR(pkg->header);
|
|
|
|
{ const char * optflags = rpmExpand("%{?optflags}", NULL);
|
|
(void) headerAddEntry(pkg->header, RPMTAG_OPTFLAGS, RPM_STRING_TYPE,
|
|
optflags, 1);
|
|
optflags = _free(optflags);
|
|
}
|
|
|
|
(void) genSourceRpmName(spec);
|
|
(void) headerAddEntry(pkg->header, RPMTAG_SOURCERPM, RPM_STRING_TYPE,
|
|
spec->sourceRpmName, 1);
|
|
|
|
{ const char *binFormat = rpmGetPath("%{_rpmfilename}", NULL);
|
|
char *binRpm, *binDir;
|
|
binRpm = headerSprintf(pkg->header, binFormat, rpmTagTable,
|
|
rpmHeaderFormats, &errorString);
|
|
binFormat = _free(binFormat);
|
|
if (binRpm == NULL) {
|
|
const char *name;
|
|
(void) headerName(pkg->header, &name);
|
|
rpmError(RPMERR_BADFILENAME, _("Could not generate output "
|
|
"filename for package %s: %s\n"), name, errorString);
|
|
return RPMERR_BADFILENAME;
|
|
}
|
|
fn = rpmGetPath("%{_rpmdir}/", binRpm, NULL);
|
|
if ((binDir = strchr(binRpm, '/')) != NULL) {
|
|
struct stat st;
|
|
const char *dn;
|
|
*binDir = '\0';
|
|
dn = rpmGetPath("%{_rpmdir}/", binRpm, NULL);
|
|
if (Stat(dn, &st) < 0) {
|
|
switch(errno) {
|
|
case ENOENT:
|
|
if (MkdirP(dn, 0755) == 0)
|
|
/*@switchbreak@*/ break;
|
|
/*@fallthrough@*/
|
|
default:
|
|
rpmError(RPMERR_BADFILENAME,_("cannot create %s: %s\n"),
|
|
dn, strerror(errno));
|
|
/*@switchbreak@*/ break;
|
|
}
|
|
}
|
|
dn = _free(dn);
|
|
}
|
|
binRpm = _free(binRpm);
|
|
}
|
|
|
|
memset(csa, 0, sizeof(*csa));
|
|
csa->cpioArchiveSize = 0;
|
|
/*@-type@*/ /* LCL: function typedefs */
|
|
csa->cpioFdIn = fdNew("init (packageBinaries)");
|
|
/*@-assignexpose -newreftrans@*/
|
|
/*@i@*/ csa->cpioList = pkg->cpioList;
|
|
/*@=assignexpose =newreftrans@*/
|
|
|
|
rc = writeRPM(&pkg->header, fn, RPMLEAD_BINARY,
|
|
csa, spec->passPhrase, NULL);
|
|
csa->cpioFdIn = fdFree(csa->cpioFdIn, "init (packageBinaries)");
|
|
/*@=type@*/
|
|
fn = _free(fn);
|
|
if (rc)
|
|
return rc;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int packageSources(Spec spec)
|
|
{
|
|
struct cpioSourceArchive_s csabuf;
|
|
CSA_t csa = &csabuf;
|
|
int rc;
|
|
time_t buildtime;
|
|
int_32 buildtime32;
|
|
int_32 *override_buildtime = NULL;
|
|
|
|
const char *env_buildtime = getenv("SOURCE_DATE_EPOCH");
|
|
|
|
/* Add some cruft */
|
|
(void) headerAddEntry(spec->sourceHeader, RPMTAG_RPMVERSION,
|
|
RPM_STRING_TYPE, VERSION, 1);
|
|
if (!headerIsEntry(spec->sourceHeader, RPMTAG_BUILDHOST))
|
|
(void) headerAddEntry(spec->sourceHeader, RPMTAG_BUILDHOST,
|
|
RPM_STRING_TYPE, buildHost(), 1);
|
|
|
|
if (env_buildtime && *env_buildtime) {
|
|
char *endptr;
|
|
errno = 0;
|
|
buildtime = strtol(env_buildtime, &endptr, 10);
|
|
if (env_buildtime == endptr || *endptr
|
|
|| ((buildtime == LONG_MIN || buildtime == LONG_MAX)
|
|
&& errno != 0)) {
|
|
rpmlog(RPMLOG_ERR, _("unable to parse %s=%s\n"),
|
|
"SOURCE_DATE_EPOCH", env_buildtime);
|
|
} else {
|
|
buildtime32 = (typeof(buildtime32)) buildtime;
|
|
override_buildtime = &buildtime32;
|
|
}
|
|
}
|
|
|
|
(void) headerAddEntry(spec->sourceHeader, RPMTAG_BUILDTIME,
|
|
RPM_INT32_TYPE, override_buildtime ?: getBuildTime() , 1);
|
|
|
|
(void) genSourceRpmName(spec);
|
|
|
|
spec->cookie = _free(spec->cookie);
|
|
|
|
/* XXX this should be %_srpmdir */
|
|
{
|
|
const char *fn = rpmGetPath("%{_srcrpmdir}/", spec->sourceRpmName, NULL);
|
|
{
|
|
const char *dn = rpmGetPath("%{_srcrpmdir}/", NULL);
|
|
struct stat st;
|
|
|
|
if (Stat(dn, &st) < 0) {
|
|
switch(errno) {
|
|
case ENOENT:
|
|
if (MkdirP(dn, 0755) == 0)
|
|
/*@switchbreak@*/ break;
|
|
/*@fallthrough@*/
|
|
default:
|
|
rpmError(RPMERR_BADFILENAME,_("cannot create %s: %s\n"),
|
|
dn, strerror(errno));
|
|
/*@switchbreak@*/ break;
|
|
}
|
|
}
|
|
dn = _free(dn);
|
|
}
|
|
memset(csa, 0, sizeof(*csa));
|
|
csa->cpioArchiveSize = 0;
|
|
/*@-type@*/ /* LCL: function typedefs */
|
|
csa->cpioFdIn = fdNew("init (packageSources)");
|
|
/*@-assignexpose -newreftrans@*/
|
|
/*@i@*/ csa->cpioList = spec->sourceCpioList;
|
|
/*@=assignexpose =newreftrans@*/
|
|
|
|
rc = writeRPM(&spec->sourceHeader, fn, RPMLEAD_SOURCE,
|
|
csa, spec->passPhrase, &(spec->cookie));
|
|
csa->cpioFdIn = fdFree(csa->cpioFdIn, "init (packageSources)");
|
|
/*@=type@*/
|
|
fn = _free(fn);
|
|
}
|
|
return rc;
|
|
}
|