/** \ingroup rpmbuild * \file build/pack.c * Assemble components of an RPM package. */ #include "system.h" #include #include #include #include /* RPMSIGTAG*, rpmReadPackageFile */ #include #include #include "rpmio/rpmio_internal.h" /* fdInitDigest, fdFiniDigest */ #include "lib/fsm.h" #include "lib/cpio.h" #include "lib/signature.h" #include "lib/rpmlead.h" #include "build/rpmbuild_internal.h" #include "build/rpmbuild_misc.h" #include "debug.h" typedef struct cpioSourceArchive_s { rpm_loff_t cpioArchiveSize; rpmfi cpioList; } * CSA_t; /** * @todo Create transaction set *much* earlier. */ static rpmRC cpio_doio(FD_t fdo, Header h, CSA_t csa, const char * fmodeMacro) { char *failedFile = NULL; FD_t cfd; int fsmrc; (void) Fflush(fdo); cfd = Fdopen(fdDup(Fileno(fdo)), fmodeMacro); if (cfd == NULL) return RPMRC_FAIL; fsmrc = rpmPackageFilesArchive(csa->cpioList, headerIsSource(h), cfd, &csa->cpioArchiveSize, &failedFile); if (fsmrc) { if (failedFile) rpmlog(RPMLOG_ERR, _("create archive failed on file %s: %s\n"), failedFile, rpmcpioStrerror(fsmrc)); else rpmlog(RPMLOG_ERR, _("create archive failed: %s\n"), rpmcpioStrerror(fsmrc)); } free(failedFile); Fclose(cfd); return (fsmrc == 0) ? RPMRC_OK : RPMRC_FAIL; } static rpmRC addFileToTag(rpmSpec spec, const char * file, Header h, rpmTagVal tag, int append) { StringBuf sb = NULL; char buf[BUFSIZ]; char * fn; FILE * f; rpmRC rc = RPMRC_FAIL; /* assume failure */ /* no script file is not an error */ if (file == NULL) return RPMRC_OK; fn = rpmGetPath("%{_builddir}/%{?buildsubdir:%{buildsubdir}/}", file, NULL); f = fopen(fn, "r"); if (f == NULL) { rpmlog(RPMLOG_ERR,_("Could not open %s file: %s\n"), rpmTagGetName(tag), file); goto exit; } sb = newStringBuf(); if (append) { const char *s = headerGetString(h, tag); if (s) { appendLineStringBuf(sb, s); headerDel(h, tag); } } while (fgets(buf, sizeof(buf), f)) { if (expandMacros(spec, spec->macros, buf, sizeof(buf))) { rpmlog(RPMLOG_ERR, _("%s: line: %s\n"), fn, buf); goto exit; } appendStringBuf(sb, buf); } headerPutString(h, tag, getStringBuf(sb)); rc = RPMRC_OK; exit: if (f) fclose(f); free(fn); freeStringBuf(sb); return rc; } static rpm_time_t * getBuildTime(void) { static rpm_time_t buildTime[1]; if (buildTime[0] == 0) buildTime[0] = (int32_t) time(NULL); return buildTime; } static const char * buildHost(void) { static char hostname[1024]; static int oneshot = 0; struct hostent *hbn; if (! oneshot) { (void) gethostname(hostname, sizeof(hostname)); hbn = gethostbyname(hostname); if (hbn) strcpy(hostname, hbn->h_name); else rpmlog(RPMLOG_WARNING, _("Could not canonicalize hostname: %s\n"), hostname); oneshot = 1; } return(hostname); } static rpmRC processScriptFiles(rpmSpec spec, Package pkg) { struct TriggerFileEntry *p; int addflags = 0; rpmRC rc = RPMRC_FAIL; Header h = pkg->header; if (addFileToTag(spec, pkg->preInFile, h, RPMTAG_PREIN, 1) || addFileToTag(spec, pkg->preUnFile, h, RPMTAG_PREUN, 1) || addFileToTag(spec, pkg->preTransFile, h, RPMTAG_PRETRANS, 1) || addFileToTag(spec, pkg->postInFile, h, RPMTAG_POSTIN, 1) || addFileToTag(spec, pkg->postUnFile, h, RPMTAG_POSTUN, 1) || addFileToTag(spec, pkg->postTransFile, h, RPMTAG_POSTTRANS, 1) || addFileToTag(spec, pkg->verifyFile, h, RPMTAG_VERIFYSCRIPT, 1)) { goto exit; } /* if any trigger has flags, we need to add flags entry for all of them */ for (p = pkg->triggerFiles; p != NULL; p = p->next) { if (p->flags) { addflags = 1; break; } } for (p = pkg->triggerFiles; p != NULL; p = p->next) { headerPutString(h, RPMTAG_TRIGGERSCRIPTPROG, p->prog); if (addflags) { headerPutUint32(h, RPMTAG_TRIGGERSCRIPTFLAGS, &p->flags, 1); } if (p->script) { headerPutString(h, RPMTAG_TRIGGERSCRIPTS, p->script); } else if (p->fileName) { if (addFileToTag(spec, p->fileName, h, RPMTAG_TRIGGERSCRIPTS, 0)) { goto exit; } } else { /* This is dumb. When the header supports NULL string */ /* this will go away. */ headerPutString(h, RPMTAG_TRIGGERSCRIPTS, ""); } } rc = RPMRC_OK; exit: return rc; } static rpmRC copyPayload(FD_t ifd, const char *ifn, FD_t ofd, const char *ofn) { char buf[BUFSIZ]; size_t nb; rpmRC rc = RPMRC_OK; while ((nb = Fread(buf, 1, sizeof(buf), ifd)) > 0) { if (Fwrite(buf, sizeof(buf[0]), nb, ofd) != nb) { rpmlog(RPMLOG_ERR, _("Unable to write payload to %s: %s\n"), ofn, Fstrerror(ofd)); rc = RPMRC_FAIL; break; } } if (nb < 0) { rpmlog(RPMLOG_ERR, _("Unable to read payload from %s: %s\n"), ifn, Fstrerror(ifd)); rc = RPMRC_FAIL; } return rc; } static int depContainsTilde(Header h, rpmTagVal tagEVR) { struct rpmtd_s evrs; const char *evr = NULL; if (headerGet(h, tagEVR, &evrs, HEADERGET_MINMEM)) { while ((evr = rpmtdNextString(&evrs)) != NULL) if (strchr(evr, '~')) break; rpmtdFreeData(&evrs); } return evr != NULL; } static rpmTagVal depevrtags[] = { RPMTAG_PROVIDEVERSION, RPMTAG_REQUIREVERSION, RPMTAG_OBSOLETEVERSION, RPMTAG_CONFLICTVERSION, RPMTAG_ORDERVERSION, RPMTAG_TRIGGERVERSION, RPMTAG_SUGGESTSVERSION, RPMTAG_ENHANCESVERSION, 0 }; static int haveTildeDep(Header h) { int i; for (i = 0; depevrtags[i] != 0; i++) if (depContainsTilde(h, depevrtags[i])) return 1; return 0; } static rpmRC writeRPM(Header *hdrp, unsigned char ** pkgidp, const char *fileName, CSA_t csa, char **cookie) { FD_t fd = NULL; FD_t ifd = NULL; char * sigtarget = NULL;; char * rpmio_flags = NULL; char * SHA1 = NULL; const char *s; Header h; Header sig = NULL; int xx; rpmRC rc = RPMRC_OK; struct rpmtd_s td; rpmTagVal sizetag; rpmTagVal payloadtag; /* Transfer header reference form *hdrp to h. */ h = headerLink(*hdrp); *hdrp = headerFree(*hdrp); if (pkgidp) *pkgidp = NULL; /* Save payload information */ if (headerIsSource(h)) rpmio_flags = rpmExpand("%{?_source_payload}", NULL); else rpmio_flags = rpmExpand("%{?_binary_payload}", NULL); /* If not configured or bogus, fall back to gz */ if (rpmio_flags[0] != 'w') { free(rpmio_flags); rpmio_flags = xstrdup("w9.gzdio"); } s = strchr(rpmio_flags, '.'); if (s) { char *buf = NULL; const char *compr = NULL; headerPutString(h, RPMTAG_PAYLOADFORMAT, "cpio"); if (rstreq(s+1, "ufdio")) { compr = NULL; } else if (rstreq(s+1, "gzdio")) { compr = "gzip"; #if HAVE_BZLIB_H } else if (rstreq(s+1, "bzdio")) { compr = "bzip2"; /* Add prereq on rpm version that understands bzip2 payloads */ (void) rpmlibNeedsFeature(h, "PayloadIsBzip2", "3.0.5-1"); #endif #if HAVE_LZMA_H } else if (rstreq(s+1, "xzdio")) { compr = "xz"; (void) rpmlibNeedsFeature(h, "PayloadIsXz", "5.2-1"); } else if (rstreq(s+1, "lzdio")) { compr = "lzma"; (void) rpmlibNeedsFeature(h, "PayloadIsLzma", "4.4.6-1"); #endif } else { rpmlog(RPMLOG_ERR, _("Unknown payload compression: %s\n"), rpmio_flags); rc = RPMRC_FAIL; goto exit; } if (compr) headerPutString(h, RPMTAG_PAYLOADCOMPRESSOR, compr); buf = xstrdup(rpmio_flags); buf[s - rpmio_flags] = '\0'; headerPutString(h, RPMTAG_PAYLOADFLAGS, buf+1); free(buf); } /* check if the package has a dependency with a '~' */ if (haveTildeDep(h)) (void) rpmlibNeedsFeature(h, "TildeInVersions", "4.10.0-1"); /* Create and add the cookie */ if (cookie) { rasprintf(cookie, "%s %d", buildHost(), (int) (*getBuildTime())); headerPutString(h, RPMTAG_COOKIE, *cookie); } /* Reallocate the header into one contiguous region. */ h = headerReload(h, RPMTAG_HEADERIMMUTABLE); if (h == NULL) { /* XXX can't happen */ rc = RPMRC_FAIL; rpmlog(RPMLOG_ERR, _("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. */ fd = rpmMkTempFile(NULL, &sigtarget); if (fd == NULL || Ferror(fd)) { rc = RPMRC_FAIL; rpmlog(RPMLOG_ERR, _("Unable to open temp file.\n")); goto exit; } fdInitDigest(fd, PGPHASHALGO_SHA1, 0); if (headerWrite(fd, h, HEADER_MAGIC_YES)) { rc = RPMRC_FAIL; rpmlog(RPMLOG_ERR, _("Unable to write temp header\n")); } else { /* Write the archive and get the size */ (void) Fflush(fd); fdFiniDigest(fd, PGPHASHALGO_SHA1, (void **)&SHA1, NULL, 1); if (csa->cpioList != NULL) { rc = cpio_doio(fd, h, csa, rpmio_flags); } else { rc = RPMRC_FAIL; rpmlog(RPMLOG_ERR, _("Bad CSA data\n")); } } if (rc != RPMRC_OK) goto exit; (void) Fclose(fd); fd = NULL; (void) unlink(fileName); /* Generate the signature */ (void) fflush(stdout); sig = rpmNewSignature(); /* * There should be rpmlib() dependency on this, but that doesn't * really do much good as these are signature tags that get read * way before dependency checking has a chance to figure out anything. * On the positive side, not inserting the 32bit tag at all means * older rpm will just bail out with error message on attempt to read * such a package. */ if (csa->cpioArchiveSize < UINT32_MAX) { sizetag = RPMSIGTAG_SIZE; payloadtag = RPMSIGTAG_PAYLOADSIZE; } else { sizetag = RPMSIGTAG_LONGSIZE; payloadtag = RPMSIGTAG_LONGARCHIVESIZE; } (void) rpmGenDigest(sig, sigtarget, sizetag); (void) rpmGenDigest(sig, sigtarget, RPMSIGTAG_MD5); if (SHA1) { /* XXX can't use rpmtdFromFoo() on RPMSIGTAG_* items */ rpmtdReset(&td); td.tag = RPMSIGTAG_SHA1; td.type = RPM_STRING_TYPE; td.data = SHA1; td.count = 1; headerPut(sig, &td, HEADERPUT_DEFAULT); SHA1 = _free(SHA1); } { /* XXX can't use headerPutType() on legacy RPMSIGTAG_* items */ rpmtdReset(&td); td.tag = payloadtag; td.count = 1; if (payloadtag == RPMSIGTAG_PAYLOADSIZE) { rpm_off_t asize = csa->cpioArchiveSize; td.type = RPM_INT32_TYPE; td.data = &asize; headerPut(sig, &td, HEADERPUT_DEFAULT); } else { rpm_loff_t asize = csa->cpioArchiveSize; td.type = RPM_INT64_TYPE; td.data = &asize; headerPut(sig, &td, HEADERPUT_DEFAULT); } } /* Reallocate the signature into one contiguous region. */ sig = headerReload(sig, RPMTAG_HEADERSIGNATURES); if (sig == NULL) { /* XXX can't happen */ rc = RPMRC_FAIL; rpmlog(RPMLOG_ERR, _("Unable to reload signature header.\n")); goto exit; } /* Open the output file */ fd = Fopen(fileName, "w.ufdio"); if (fd == NULL || Ferror(fd)) { rc = RPMRC_FAIL; rpmlog(RPMLOG_ERR, _("Could not open %s: %s\n"), fileName, Fstrerror(fd)); goto exit; } /* Write the lead section into the package. */ { rpmlead lead = rpmLeadFromHeader(h); rc = rpmLeadWrite(fd, lead); rpmLeadFree(lead); if (rc != RPMRC_OK) { rc = RPMRC_FAIL; rpmlog(RPMLOG_ERR, _("Unable to write package: %s\n"), Fstrerror(fd)); goto exit; } } /* Write the signature section into the package. */ if (rpmWriteSignature(fd, sig)) { rc = RPMRC_FAIL; goto exit; } /* Append the header and archive */ ifd = Fopen(sigtarget, "r.ufdio"); if (ifd == NULL || Ferror(ifd)) { rc = RPMRC_FAIL; rpmlog(RPMLOG_ERR, _("Unable to open sigtarget %s: %s\n"), sigtarget, Fstrerror(ifd)); goto exit; } /* Add signatures to header, and write header into the package. */ /* XXX header+payload digests/signatures might be checked again here. */ { Header nh = headerRead(ifd, HEADER_MAGIC_YES); if (nh == NULL) { rc = RPMRC_FAIL; rpmlog(RPMLOG_ERR, _("Unable to read header from %s: %s\n"), sigtarget, Fstrerror(ifd)); goto exit; } xx = headerWrite(fd, nh, HEADER_MAGIC_YES); headerFree(nh); if (xx) { rc = RPMRC_FAIL; rpmlog(RPMLOG_ERR, _("Unable to write header to %s: %s\n"), fileName, Fstrerror(fd)); goto exit; } } /* Write the payload into the package. */ rc = copyPayload(ifd, fileName, fd, sigtarget); exit: free(rpmio_flags); free(SHA1); headerFree(h); /* XXX Fish the pkgid out of the signature header. */ if (sig != NULL && pkgidp != NULL) { struct rpmtd_s md5tag; headerGet(sig, RPMSIGTAG_MD5, &md5tag, HEADERGET_DEFAULT); if (rpmtdType(&md5tag) == RPM_BIN_TYPE && md5tag.count == 16 && md5tag.data != NULL) { *pkgidp = md5tag.data; } } rpmFreeSignature(sig); Fclose(ifd); Fclose(fd); if (sigtarget) { (void) unlink(sigtarget); free(sigtarget); } if (rc == RPMRC_OK) rpmlog(RPMLOG_NOTICE, _("Wrote: %s\n"), fileName); else (void) unlink(fileName); return rc; } static const rpmTagVal copyTags[] = { RPMTAG_CHANGELOGTIME, RPMTAG_CHANGELOGNAME, RPMTAG_CHANGELOGTEXT, 0 }; static rpmRC checkPackages(char *pkgcheck) { int fail = rpmExpandNumeric("%{?_nonzero_exit_pkgcheck_terminate_build}"); int xx; rpmlog(RPMLOG_NOTICE, _("Executing \"%s\":\n"), pkgcheck); xx = system(pkgcheck); if (WEXITSTATUS(xx) == -1 || WEXITSTATUS(xx) == 127) { rpmlog(RPMLOG_ERR, _("Execution of \"%s\" failed.\n"), pkgcheck); if (fail) return RPMRC_NOTFOUND; } if (WEXITSTATUS(xx) != 0) { rpmlog(RPMLOG_ERR, _("Package check \"%s\" failed.\n"), pkgcheck); if (fail) return RPMRC_FAIL; } return RPMRC_OK; } static void trimChangelog(Header h) { static int oneshot; static int cuttime, minnum, maxnum; int * times; char ** names = 0, ** texts = 0; int i, keep, count = 0; if (!oneshot) { char *binarychangelogtrim = rpmExpand("%{?_binarychangelogtrim}", NULL); oneshot = 1; if (binarychangelogtrim && *binarychangelogtrim) { maxnum = atoi(binarychangelogtrim); binarychangelogtrim = strchr(binarychangelogtrim, ','); if (binarychangelogtrim) binarychangelogtrim++; } if (binarychangelogtrim && *binarychangelogtrim) { cuttime = atoi(binarychangelogtrim); binarychangelogtrim = strchr(binarychangelogtrim, ','); if (binarychangelogtrim) binarychangelogtrim++; } if (binarychangelogtrim && *binarychangelogtrim) { minnum = atoi(binarychangelogtrim); binarychangelogtrim = strchr(binarychangelogtrim, ','); } } if (!cuttime && !minnum && !maxnum) { return; } if (!headerGetEntry(h, RPMTAG_CHANGELOGTIME, NULL, (void **) ×, &count)) return; if ((!cuttime || count <= minnum) && (!maxnum || count <= maxnum)) { return; } keep = count; if (maxnum && keep > maxnum) keep = maxnum; if (cuttime) { for (i = 0; i < keep; i++) { if (i >= minnum && times[i] < cuttime) break; } keep = i; } if (keep >= count) return; headerGetEntry(h, RPMTAG_CHANGELOGNAME, NULL, (void **) &names, &count); headerGetEntry(h, RPMTAG_CHANGELOGTEXT, NULL, (void **) &texts, &count); headerModifyEntry(h, RPMTAG_CHANGELOGTIME, RPM_INT32_TYPE, times, keep); headerModifyEntry(h, RPMTAG_CHANGELOGNAME, RPM_STRING_ARRAY_TYPE, names, keep); headerModifyEntry(h, RPMTAG_CHANGELOGTEXT, RPM_STRING_ARRAY_TYPE, texts, keep); free(names); free(texts); } rpmRC packageBinaries(rpmSpec spec, const char *cookie, int cheating) { struct cpioSourceArchive_s csabuf; CSA_t csa = &csabuf; rpmRC rc; const char *errorString; Package pkg; char *pkglist = NULL; trimChangelog(spec->packages->header); for (pkg = spec->packages; pkg != NULL; pkg = pkg->next) { char *fn; if (pkg->fileList == NULL) continue; if ((rc = processScriptFiles(spec, pkg))) return rc; if (cookie) { headerPutString(pkg->header, RPMTAG_COOKIE, cookie); } /* Copy changelog from src rpm */ headerCopyTags(spec->packages->header, pkg->header, copyTags); headerPutString(pkg->header, RPMTAG_RPMVERSION, VERSION); headerPutString(pkg->header, RPMTAG_BUILDHOST, buildHost()); headerPutUint32(pkg->header, RPMTAG_BUILDTIME, getBuildTime(), 1); if (spec->sourcePkgId != NULL) { headerPutBin(pkg->header, RPMTAG_SOURCEPKGID, spec->sourcePkgId,16); } if (cheating) { (void) rpmlibNeedsFeature(pkg->header, "ShortCircuited", "4.9.0-1"); } { char *binFormat = rpmGetPath("%{_rpmfilename}", NULL); char *binRpm, *binDir; binRpm = headerFormat(pkg->header, binFormat, &errorString); free(binFormat); if (binRpm == NULL) { rpmlog(RPMLOG_ERR, _("Could not generate output " "filename for package %s: %s\n"), headerGetString(pkg->header, RPMTAG_NAME), errorString); return RPMRC_FAIL; } fn = rpmGetPath("%{_rpmdir}/", binRpm, NULL); if ((binDir = strchr(binRpm, '/')) != NULL) { struct stat st; char *dn; *binDir = '\0'; dn = rpmGetPath("%{_rpmdir}/", binRpm, NULL); if (stat(dn, &st) < 0) { switch(errno) { case ENOENT: if (mkdir(dn, 0755) == 0) break; default: rpmlog(RPMLOG_ERR,_("cannot create %s: %s\n"), dn, strerror(errno)); break; } } free(dn); } free(binRpm); } memset(csa, 0, sizeof(*csa)); csa->cpioArchiveSize = 0; csa->cpioList = rpmfiLink(pkg->cpioList); rc = writeRPM(&pkg->header, NULL, fn, csa, NULL); csa->cpioList = rpmfiFree(csa->cpioList); if (rc == RPMRC_OK) { /* Do check each written package if enabled */ char *pkgcheck = rpmExpand("%{?_build_pkgcheck} ", fn, NULL); if (pkgcheck[0] != ' ') { rc = checkPackages(pkgcheck); } free(pkgcheck); rstrcat(&pkglist, fn); rstrcat(&pkglist, " "); } free(fn); if (rc != RPMRC_OK) { pkglist = _free(pkglist); return rc; } } /* Now check the package set if enabled */ if (pkglist != NULL) { char *pkgcheck_set = rpmExpand("%{?_build_pkgcheck_set} ", pkglist, NULL); if (pkgcheck_set[0] != ' ') { /* run only if _build_pkgcheck_set is defined */ checkPackages(pkgcheck_set); } free(pkgcheck_set); pkglist = _free(pkglist); } return RPMRC_OK; } rpmRC packageSources(rpmSpec spec, char **cookie) { struct cpioSourceArchive_s csabuf; CSA_t csa = &csabuf; rpmRC rc; /* Add some cruft */ headerPutString(spec->sourceHeader, RPMTAG_RPMVERSION, VERSION); headerPutString(spec->sourceHeader, RPMTAG_BUILDHOST, buildHost()); headerPutUint32(spec->sourceHeader, RPMTAG_BUILDTIME, getBuildTime(), 1); /* XXX this should be %_srpmdir */ { char *fn = rpmGetPath("%{_srcrpmdir}/", spec->sourceRpmName,NULL); char *pkgcheck = rpmExpand("%{?_build_pkgcheck_srpm} ", fn, NULL); memset(csa, 0, sizeof(*csa)); csa->cpioArchiveSize = 0; csa->cpioList = rpmfiLink(spec->sourceCpioList); spec->sourcePkgId = NULL; rc = writeRPM(&spec->sourceHeader, &spec->sourcePkgId, fn, csa, cookie); /* Do check SRPM package if enabled */ if (rc == RPMRC_OK && pkgcheck[0] != ' ') { rc = checkPackages(pkgcheck); } rpmfiFree(csa->cpioList); free(pkgcheck); free(fn); } return rc; }