Add support for patch rpms. Maybe not needed that much any more, as delta rpms are more efficient and do not need so much evil rpm patchery. rh#103205 Index: lib/depends.c =================================================================== --- lib/depends.c.orig +++ lib/depends.c @@ -159,6 +159,7 @@ int rpmtsAddInstallElement(rpmts ts, Hea const char * os; rpmds oldChk, newChk; rpmds obsoletes; + rpmds patches; alKey pkgKey; /* addedPackages key */ int xx; int ec = 0; @@ -387,6 +388,40 @@ addheader: } obsoletes = rpmdsFree(obsoletes); + patches = rpmdsLink(rpmteDS(p, RPMTAG_PATCHESNAME), "Patches"); + patches = rpmdsInit(patches); + if (patches != NULL) + while (rpmdsNext(patches) >= 0) { + const char * Name; + + if ((Name = rpmdsN(patches)) == NULL) + continue; /* XXX can't happen */ + + /* Ignore colored patches not in our rainbow. */ + dscolor = rpmdsColor(patches); + if (tscolor && dscolor && !(tscolor & dscolor)) + continue; + + mi = rpmtsInitIterator(ts, RPMTAG_NAME, Name, 0); + + xx = rpmdbPruneIterator(mi, + ts->removedPackages, ts->numRemovedPackages, 1); + + while((oh = rpmdbNextIterator(mi)) != NULL) { + /* Ignore colored packages not in our rainbow. */ + ohcolor = hGetColor(oh); + if (tscolor && hcolor && ohcolor && !(hcolor & ohcolor)) + /*@innercontinue@*/ continue; + if (rpmdsEVR(patches) == NULL + || rpmdsNVRMatchesDep(oh, patches, _rpmds_nopromote)) { + if (rpmVersionCompare(h, oh)) + xx = removePackage(ts, oh, rpmdbGetIteratorOffset(mi), pkgKey); + } + } + mi = rpmdbFreeIterator(mi); + } + patches = rpmdsFree(patches); + ec = 0; exit: @@ -644,6 +679,57 @@ exit: return rc; } +static int checkPatchDeps(rpmts ts, rpmte p, int reportprobs) +{ + const char * Name; + Header h; + rpmds patches; + rpmds this; + rpmdbMatchIterator mi; + + patches = rpmdsInit(rpmteDS(p, RPMTAG_PATCHESNAME)); + if (!patches) + return 0; + this = rpmteDS(p, RPMTAG_NAME); + + mi = rpmtsInitIterator(ts, RPMTAG_NAME, rpmdsN(this), 0); + while ((h = rpmdbNextIterator(mi)) != NULL) { + if (rpmdsNVRMatchesDep(h, this, _rpmds_nopromote)) { + rpmdsNotify(this, _("(patch refresh)"), 0); + p->hPatched = headerLink(h); + p->isPatchRefresh = 1; + mi = rpmdbFreeIterator(mi); + return 0; + } + } + mi = rpmdbFreeIterator(mi); + + while (rpmdsNext(patches) >= 0) { + if ((Name = rpmdsN(patches)) == NULL) + return 1; /* XXX can't happen */ + mi = rpmtsInitIterator(ts, RPMTAG_NAME, Name, 0); + while ((h = rpmdbNextIterator(mi)) != NULL) { + if (rpmdsNVRMatchesDep(h, patches, _rpmds_nopromote)) { + rpmdsNotify(patches, _("(db package)"), 0); + p->hPatched = headerLink(h); + p->isPatchRefresh = 0; + mi = rpmdbFreeIterator(mi); + return 0; + } + } + mi = rpmdbFreeIterator(mi); + } + + rpmdsNotify(patches, NULL, 1); + if (reportprobs) { + patches = rpmdsInit(patches); + rpmdsNext(patches); + rpmdsProblem(ts->probs, rpmteNEVR(p), patches, NULL, 1); + } + return 0; +} + + /** * Check added requires/conflicts against against installed+added packages. * @param ts transaction set @@ -1727,6 +1813,7 @@ int rpmtsCheck(rpmts ts) rpmteDS(p, RPMTAG_CONFLICTNAME), NULL, tscolor, 1); + rc |= checkPatchDeps(ts, p, 1); if (rc) goto exit; @@ -1824,3 +1911,22 @@ exit: /*@=branchstate@*/ return rc; } + +void rpmtsPatchCheck(rpmts ts) +{ + int closeatexit = 0; + rpmtsi pi = NULL; rpmte p; + + if (rpmtsGetRdb(ts) == NULL && ts->dbmode != -1) { + if ((rpmtsOpenDB(ts, ts->dbmode)) != 0) + return; + closeatexit = 1; + } + pi = rpmtsiInit(ts); + while ((p = rpmtsiNext(pi, TR_ADDED)) != NULL) + if (p->key) /* key is filename for install, zero for verify */ + (void)checkPatchDeps(ts, p, 0); + pi = rpmtsiFree(pi); + if (closeatexit) + (void)rpmtsCloseDB(ts); +} Index: lib/formats.c =================================================================== --- lib/formats.c.orig +++ lib/formats.c @@ -232,6 +232,8 @@ static /*@only@*/ char * fflagsFormat(in strcat(buf, "l"); if (anint & RPMFILE_README) strcat(buf, "r"); + if (anint & RPMFILE_UNPATCHED) + strcat(buf, "u"); /*@=boundswrite@*/ val = xmalloc(5 + padding); Index: lib/fsm.c =================================================================== --- lib/fsm.c.orig +++ lib/fsm.c @@ -707,7 +707,7 @@ assert(rpmteType(fi->te) == TR_ADDED); break; case FA_BACKUP: - if (!(fsm->fflags & RPMFILE_GHOST)) /* XXX Don't if %ghost file. */ + if (!(fsm->fflags & (RPMFILE_GHOST|RPMFILE_UNPATCHED))) /* XXX Don't if %ghost file. */ switch (rpmteType(fi->te)) { case TR_ADDED: fsm->osuffix = SUFFIX_RPMORIG; @@ -720,13 +720,13 @@ assert(rpmteType(fi->te) == TR_ADDED); case FA_ALTNAME: assert(rpmteType(fi->te) == TR_ADDED); - if (!(fsm->fflags & RPMFILE_GHOST)) /* XXX Don't if %ghost file. */ + if (!(fsm->fflags & (RPMFILE_GHOST|RPMFILE_UNPATCHED))) /* XXX Don't if %ghost file. */ fsm->nsuffix = SUFFIX_RPMNEW; break; case FA_SAVE: assert(rpmteType(fi->te) == TR_ADDED); - if (!(fsm->fflags & RPMFILE_GHOST)) /* XXX Don't if %ghost file. */ + if (!(fsm->fflags & (RPMFILE_GHOST|RPMFILE_UNPATCHED))) /* XXX Don't if %ghost file. */ fsm->osuffix = SUFFIX_RPMSAVE; break; case FA_ERASE: @@ -1740,7 +1740,7 @@ int fsmStage(FSM_t fsm, fileStage stage) } if (fsm->goal == FSM_PKGBUILD) { - if (fsm->fflags & RPMFILE_GHOST) /* XXX Don't if %ghost file. */ + if (fsm->fflags & (RPMFILE_GHOST|RPMFILE_UNPATCHED)) /* XXX Don't if %ghost file. */ break; if (!S_ISDIR(st->st_mode) && st->st_nlink > 1) { struct hardLink_s * li, * prev; Index: lib/poptQV.c =================================================================== --- lib/poptQV.c.orig +++ lib/poptQV.c @@ -171,6 +171,7 @@ static void queryArgCallback(poptContext case 'l': qva->qva_flags |= QUERY_FOR_LIST; break; case 's': qva->qva_flags |= QUERY_FOR_STATE | QUERY_FOR_LIST; break; + case 'P': qva->qva_flags |= QUERY_FOR_PATCHES; break; case POPT_DUMP: qva->qva_flags |= QUERY_FOR_DUMPFILES | QUERY_FOR_LIST; break; @@ -278,6 +279,8 @@ struct poptOption rpmQueryPoptTable[] = N_("skip %%readme files"), NULL }, #endif + { "patches", 'P', 0, 0, 'P', + N_("list patches or patched files "), NULL }, { "qf", '\0', POPT_ARG_STRING | POPT_ARGFLAG_DOC_HIDDEN, 0, POPT_QUERYFORMAT, NULL, NULL }, { "queryformat", '\0', POPT_ARG_STRING, 0, POPT_QUERYFORMAT, Index: lib/query.c =================================================================== --- lib/query.c.orig +++ lib/query.c @@ -225,6 +225,10 @@ int showQueryPackage(QVA_t qva, rpmts ts if ((qva->qva_fflags & RPMFILE_GHOST) && (fflags & RPMFILE_GHOST)) continue; + /* If querying patches, skip unpatched files. */ + if ((qva->qva_flags & QUERY_FOR_PATCHES) && (fflags & RPMFILE_UNPATCHED)) + continue; + /*@-boundswrite@*/ if (!rpmIsVerbose() && prefix) te = stpcpy(te, prefix); @@ -362,6 +366,21 @@ void rpmDisplayQueryTags(FILE * fp) } } +static int isPatch(Header h) +{ + int i, requiresCount = 0; + const char ** requires; + + if (!headerGetEntry(h, RPMTAG_REQUIRENAME, NULL, (void **) &requires, &requiresCount)) + return 0; + for (i = 0; i < requiresCount; i++) + if (!strcmp("rpmlib(PatchRPMs)", requires[i])) + break; + if (requiresCount) + free(requires); + return i < requiresCount; +} + static int rpmgiShowMatches(QVA_t qva, rpmts ts) /*@globals rpmGlobalMacroContext, h_errno, internalState @*/ /*@modifies qva, rpmGlobalMacroContext, h_errno, internalState @*/ @@ -376,6 +395,8 @@ static int rpmgiShowMatches(QVA_t qva, r h = rpmgiHeader(gi); if (h == NULL) /* XXX perhaps stricter break instead? */ continue; + if ((qva->qva_flags & QUERY_FOR_PATCHES) != 0 && !isPatch(h)) + continue; if ((rc = qva->qva_showPackage(qva, ts, h)) != 0) ec = rc; if (qva->qva_source == RPMQV_DBOFFSET) @@ -391,6 +412,8 @@ int rpmcliShowMatches(QVA_t qva, rpmts t while ((h = rpmdbNextIterator(qva->qva_mi)) != NULL) { int rc; + if ((qva->qva_flags & QUERY_FOR_PATCHES) != 0 && !isPatch(h)) + continue; if ((rc = qva->qva_showPackage(qva, ts, h)) != 0) ec = rc; if (qva->qva_source == RPMQV_DBOFFSET) @@ -685,7 +708,17 @@ int rpmcliArgIter(rpmts ts, QVA_t qva, A switch (qva->qva_source) { case RPMQV_ALL: - qva->qva_gi = rpmgiNew(ts, RPMDBI_PACKAGES, NULL, 0); + if ((!argv || !*argv) && (qva->qva_flags & QUERY_FOR_PATCHES) != 0) { + qva->qva_gi = rpmgiNew(ts, RPMTAG_REQUIRENAME, "rpmlib(PatchRPMs)", 0); + qva->qva_gi->mi = rpmtsInitIterator(qva->qva_gi->ts, qva->qva_gi->tag, qva->qva_gi->keyp, qva->qva_gi->keylen); + if (qva->qva_gi->mi == NULL) { + rpmError(RPMERR_QUERYINFO, _("no patch-rpm installed\n")); + break; + } + qva->qva_gi->mi = rpmdbFreeIterator(qva->qva_gi->mi); + } else { + qva->qva_gi = rpmgiNew(ts, RPMDBI_PACKAGES, NULL, 0); + } qva->qva_rc = rpmgiSetArgs(qva->qva_gi, argv, ftsOpts, RPMGI_NONE); if (qva->qva_gi != NULL && (qva->qva_gi->flags & RPMGI_TSADD)) /* Load the ts with headers. */ Index: lib/rpmcli.h =================================================================== --- lib/rpmcli.h.orig +++ lib/rpmcli.h @@ -165,7 +165,7 @@ typedef enum rpmQueryFlags_e { QUERY_SCRIPT = (1 << 18), /*!< verify: from --noscripts */ QUERY_DIGEST = (1 << 19), /*!< verify: from --nodigest */ QUERY_SIGNATURE = (1 << 20), /*!< verify: from --nosignature */ - QUERY_PATCHES = (1 << 21), /*!< verify: from --nopatches */ + QUERY_FOR_PATCHES = (1 << 21), /*!< verify: from --patches */ QUERY_HDRCHK = (1 << 22), /*!< verify: from --nohdrchk */ /*@=enummemuse@*/ QUERY_FOR_LIST = (1 << 23), /*!< query: from --list */ Index: lib/rpmds.c =================================================================== --- lib/rpmds.c.orig +++ lib/rpmds.c @@ -87,6 +87,10 @@ fprintf(stderr, "*** ds %p\t%s[%d]\n", d tagEVR = RPMTAG_TRIGGERVERSION; tagF = RPMTAG_TRIGGERFLAGS; } else + if (ds->tagN == RPMTAG_PATCHESNAME) { + tagEVR = RPMTAG_PATCHESVERSION; + tagF = RPMTAG_PATCHESFLAGS; + } else return NULL; /*@-branchstate@*/ @@ -325,6 +329,11 @@ rpmds rpmdsNew(Header h, rpmTag tagN, in tagEVR = RPMTAG_ENHANCESVERSION; tagF = RPMTAG_ENHANCESFLAGS; } else + if (tagN == RPMTAG_PATCHESNAME) { + Type = "patches"; + tagEVR = RPMTAG_PATCHESVERSION; + tagF = RPMTAG_PATCHESFLAGS; + } else goto exit; /*@-branchstate@*/ @@ -1127,14 +1136,28 @@ void rpmdsProblem(rpmps ps, const char * if (DNEVR == NULL) DNEVR = "? ?N? ?OP? ?EVR?"; /*@=branchstate@*/ - rpmMessage(RPMMESS_DEBUG, _("package %s has unsatisfied %s: %s\n"), - pkgNEVR, ds->Type, DNEVR+2); - switch ((unsigned)DNEVR[0]) { case 'C': type = RPMPROB_CONFLICT; break; default: case 'R': type = RPMPROB_REQUIRES; break; } + if (DNEVR[0] == 'p') { + const char *d; + char *dn; + rpmds pds = rpmdsInit(ds); + dn = xstrdup("p "); + while (rpmdsNext(pds) >= 0) { + d = rpmdsDNEVR(ds) + 2; + dn = xrealloc(dn, strlen(dn) + strlen(d) + 4); + if (dn[2]) + strcat(dn, " | "); + strcat(dn, d); + } + DNEVR = (const char *)dn; + } + + rpmMessage(RPMMESS_DEBUG, _("package %s has unsatisfied %s: %s\n"), + pkgNEVR, ds->Type, DNEVR+2); key = (suggestedKeys ? suggestedKeys[0] : NULL); rpmpsAppend(ps, type, pkgNEVR, key, NULL, NULL, DNEVR, adding); Index: lib/rpminstall.c =================================================================== --- lib/rpminstall.c.orig +++ lib/rpminstall.c @@ -692,6 +692,11 @@ maybe_manifest: /*@=branchstate@*/ } ps = rpmpsFree(ps); + } else if (eiu->numRPMS) { + /* needed in rpmtsOrder */ + rpmalMakeIndex(ts->addedPackages); + /* need patch references */ + rpmtsPatchCheck(ts); } if (eiu->numRPMS && !(ia->installInterfaceFlags & INSTALL_NOORDER)) { @@ -797,7 +802,7 @@ int rpmErase(rpmts ts, struct rpmInstall { int notifyFlags; notifyFlags = ia->eraseInterfaceFlags | (rpmIsVerbose() ? INSTALL_LABEL : 0 ); xx = rpmtsSetNotifyCallback(ts, - rpmShowProgress, (void *) ((long)notifyFlags) + rpmShowProgress, (void *) ((long)notifyFlags)) } #endif Index: lib/rpmlibprov.c =================================================================== --- lib/rpmlibprov.c.orig +++ lib/rpmlibprov.c @@ -33,6 +33,9 @@ static struct rpmlibProvides_s rpmlibPro { "rpmlib(PayloadIsBzip2)", "3.0.5-1", (RPMSENSE_RPMLIB|RPMSENSE_EQUAL), N_("package payload can be compressed using bzip2.") }, + { "rpmlib(PatchRPMs)", "3.0.6-1", + (RPMSENSE_RPMLIB|RPMSENSE_EQUAL), + N_("understand rpms that replace a subset of files.") }, { "rpmlib(PayloadFilesHavePrefix)", "4.0-1", (RPMSENSE_RPMLIB|RPMSENSE_EQUAL), N_("package payload file(s) have \"./\" prefix.") }, Index: lib/rpmte.c =================================================================== --- lib/rpmte.c.orig +++ lib/rpmte.c @@ -64,6 +64,7 @@ static void delTE(rpmte p) p->NEVRA = _free(p->NEVRA); p->h = headerFree(p->h); + p->hPatched = headerFree(p->hPatched); /*@-boundswrite@*/ memset(p, 0, sizeof(*p)); /* XXX trash and burn */ @@ -183,6 +184,9 @@ static void addTE(rpmts ts, rpmte p, Hea p->requires = rpmdsNew(h, RPMTAG_REQUIRENAME, scareMem); p->conflicts = rpmdsNew(h, RPMTAG_CONFLICTNAME, scareMem); p->obsoletes = rpmdsNew(h, RPMTAG_OBSOLETENAME, scareMem); + p->patches = rpmdsNew(h, RPMTAG_PATCHESNAME, scareMem | 2); + p->hPatched = NULL; + p->isPatchRefresh = 0; savep = rpmtsSetRelocateElement(ts, p); p->fi = rpmfiNew(ts, h, RPMTAG_BASENAMES, scareMem); @@ -520,6 +524,9 @@ rpmds rpmteDS(rpmte te, rpmTag tag) if (tag == RPMTAG_OBSOLETENAME) return te->obsoletes; else + if (tag == RPMTAG_PATCHESNAME) + return te->patches; + else return NULL; /*@=compdef =refcounttrans =retalias =retexpose =usereleased @*/ } Index: lib/rpmte.h =================================================================== --- lib/rpmte.h.orig +++ lib/rpmte.h @@ -115,6 +115,9 @@ struct rpmte_s { int autorelocatex; /*!< (TR_ADDED) Auto relocation entry index. */ /*@refcounted@*/ /*@null@*/ FD_t fd; /*!< (TR_ADDED) Payload file descriptor. */ + rpmds patches; /*!< Patches: dependencies. */ + Header hPatched; /*!< (TR_ADDED) Header of package we patch */ + int isPatchRefresh; /*!< (TR_ADDED) is a patch refresh */ /*@-fielduse@*/ /* LCL: confused by union? */ union { Index: lib/transaction.c =================================================================== --- lib/transaction.c.orig +++ lib/transaction.c @@ -198,6 +198,11 @@ static int handleInstInstalledFiles(cons int rConflicts; rConflicts = reportConflicts; + if (rConflicts && p->hPatched && p->isPatchRefresh) { + /* If same package (patch refresh) turn off conflicts */ + /* Handling of unpatched files not worth the trouble */ + rConflicts = 0; + } /* Resolve file conflicts to prefer Elf64 (if not forced). */ if (tscolor != 0 && FColor != 0 && FColor != oFColor) { @@ -972,6 +977,176 @@ rpmfi rpmtsiFi(const rpmtsi tsi) /*@=compdef =refcounttrans =usereleased @*/ } +static int_32 *dupint32(int_32 *old, int cnt) +{ + int i; + int_32 *new = xmalloc(cnt * sizeof(int_32)); + for (i = 0; i < cnt; i++) + new[i] = old[i]; + return new; +} + +static void patchUnpatchedFiles(Header oldh, Header h, int isRefresh) +{ + int fileCount, oldfileCount, i, j, oldidx, oldidxj; + const char ** baseNames, ** dirNames; + int_32 * dirIndexes; + int_32 * fileMtimes; + int_32 * fileSizes; + int_32 * fileFlags; + char ** fileMd5s; + const char ** oldbaseNames, ** olddirNames; + int_32 * olddirIndexes; + int_32 * oldfileMtimes; + int_32 * oldfileSizes; + int_32 * oldfileFlags; + char ** oldfileMd5s; + const char * name, * version, * release; + char * evr; + int_32 sense; + int_32 *epochp; + int save = 0; + char epoch[20]; + const char ** oldpatches, **oldpatchesEVR = NULL; + int_32 * oldpatchesFlags; + int oldpatchesCount; + + if (!oldh) { + headerRemoveEntry(h, RPMTAG_PATCHESNAME); + headerRemoveEntry(h, RPMTAG_PATCHESFLAGS); + headerRemoveEntry(h, RPMTAG_PATCHESVERSION); +#if 1 + name = "(none)"; + sense = 0; + evr = ""; + headerAddEntry(h, RPMTAG_PATCHESNAME, RPM_STRING_ARRAY_TYPE, &name, 1); + headerAddEntry(h, RPMTAG_PATCHESFLAGS, RPM_INT32_TYPE, &sense, 1); + headerAddEntry(h, RPMTAG_PATCHESVERSION, RPM_STRING_ARRAY_TYPE, &evr, 1); +#endif + return; + } + if (!headerGetEntry(h, RPMTAG_BASENAMES, NULL, + (void **) &baseNames, &fileCount)) + return; + headerGetEntry(h, RPMTAG_DIRNAMES, NULL, + (void **) &dirNames, NULL); + headerGetEntry(h, RPMTAG_DIRINDEXES, NULL, + (void **) &dirIndexes, NULL); + headerGetEntry(h, RPMTAG_FILESIZES, NULL, + (void **) &fileSizes, NULL); + headerGetEntry(h, RPMTAG_FILEMD5S, NULL, + (void **) &fileMd5s, NULL); + headerGetEntry(h, RPMTAG_FILEMTIMES, NULL, + (void **) &fileMtimes, NULL); + headerGetEntry(h, RPMTAG_FILEFLAGS, NULL, + (void **) &fileFlags, NULL); + + if (!headerGetEntry(oldh, RPMTAG_BASENAMES, NULL, + (void **) &oldbaseNames, &oldfileCount)) + return; + headerGetEntry(oldh, RPMTAG_DIRNAMES, NULL, + (void **) &olddirNames, NULL); + headerGetEntry(oldh, RPMTAG_DIRINDEXES, NULL, + (void **) &olddirIndexes, NULL); + headerGetEntry(oldh, RPMTAG_FILESIZES, NULL, + (void **) &oldfileSizes, NULL); + headerGetEntry(oldh, RPMTAG_FILEMD5S, NULL, + (void **) &oldfileMd5s, NULL); + headerGetEntry(oldh, RPMTAG_FILEMTIMES, NULL, + (void **) &oldfileMtimes, NULL); + headerGetEntry(oldh, RPMTAG_FILEFLAGS, NULL, + (void **) &oldfileFlags, NULL); + + oldidx = -1; + oldidxj = 0; + for (i = 0; i < fileCount; i++) { + if (!(fileFlags[i] & RPMFILE_UNPATCHED)) + continue; + if (dirIndexes[i] != oldidx) { + for (j = 0; j < oldfileCount; j++) + if (strcmp(dirNames[dirIndexes[i]], olddirNames[olddirIndexes[j]]) == 0) + break; + if (j == oldfileCount) { + while (i + 1 < fileCount && dirIndexes[i] == dirIndexes[i + 1]) + i++; + continue; + } + oldidx = olddirIndexes[j]; + oldidxj = j; + } + for (j = oldidxj; j < oldfileCount; j++) + if (olddirIndexes[j] == oldidx && !strcmp(baseNames[i], oldbaseNames[j])) { + if (!save) { + /* duplicate fileSizes, fileMtimes, fileFlags + * so we can modify them */ + fileSizes = dupint32(fileSizes, fileCount); + fileMtimes = dupint32(fileMtimes, fileCount); + fileFlags = dupint32(fileFlags, fileCount); + } + fileSizes[i] = oldfileSizes[j]; + fileMtimes[i] = oldfileMtimes[j]; + fileMd5s[i] = oldfileMd5s[j]; + fileFlags[i] = oldfileFlags[j] | RPMFILE_UNPATCHED; + save = 1; + break; + } + } + if (save) { + headerModifyEntry(h, RPMTAG_FILESIZES, RPM_INT32_TYPE, + (void *) fileSizes, fileCount); + headerModifyEntry(h, RPMTAG_FILEMD5S, RPM_STRING_ARRAY_TYPE, + (void *) fileMd5s, fileCount); + headerModifyEntry(h, RPMTAG_FILEMTIMES, RPM_INT32_TYPE, + (void *) fileMtimes, fileCount); + headerModifyEntry(h, RPMTAG_FILEFLAGS, RPM_INT32_TYPE, + (void *) fileFlags, fileCount); + free(fileSizes); + free(fileMtimes); + free(fileFlags); + } + free(baseNames); + free(dirNames); + free(fileMd5s); + free(oldbaseNames); + free(olddirNames); + free(oldfileMd5s); + + if (isRefresh) { + /* same patch installed, this is just a refresh operation */ + headerRemoveEntry(h, RPMTAG_PATCHESNAME); + headerRemoveEntry(h, RPMTAG_PATCHESFLAGS); + headerRemoveEntry(h, RPMTAG_PATCHESVERSION); + if (headerGetEntry(oldh, RPMTAG_PATCHESNAME, NULL, (void **) &oldpatches, &oldpatchesCount) && oldpatchesCount) { + headerGetEntry(oldh, RPMTAG_PATCHESFLAGS, NULL, (void **) &oldpatchesFlags, &oldpatchesCount); + headerGetEntry(oldh, RPMTAG_PATCHESVERSION, NULL, (void **) &oldpatchesEVR, &oldpatchesCount); + headerAddEntry(h, RPMTAG_PATCHESNAME, RPM_STRING_ARRAY_TYPE, oldpatches, oldpatchesCount); + headerAddEntry(h, RPMTAG_PATCHESFLAGS, RPM_INT32_TYPE, oldpatchesFlags, oldpatchesCount); + headerAddEntry(h, RPMTAG_PATCHESVERSION, RPM_STRING_ARRAY_TYPE, oldpatchesEVR, oldpatchesCount); + free(oldpatches); + free(oldpatchesEVR); + } + return; + } + headerNVR(oldh, &name, &version, &release); + *epoch = 0; + if (headerGetEntry(h, RPMTAG_EPOCH, NULL, (void **) &epochp, NULL)) + sprintf(epoch, "%d:", *epochp); + evr = xmalloc(strlen(epoch) + strlen(version) + strlen(release) + 2); + strcpy(evr, epoch); + strcat(evr, version); + strcat(evr, "-"); + strcat(evr, release); + sense = RPMSENSE_EQUAL; + headerRemoveEntry(h, RPMTAG_PATCHESNAME); + headerRemoveEntry(h, RPMTAG_PATCHESFLAGS); + headerRemoveEntry(h, RPMTAG_PATCHESVERSION); + headerAddEntry(h, RPMTAG_PATCHESNAME, RPM_STRING_ARRAY_TYPE, &name, 1); + headerAddEntry(h, RPMTAG_PATCHESFLAGS, RPM_INT32_TYPE, &sense, 1); + headerAddEntry(h, RPMTAG_PATCHESVERSION, RPM_STRING_ARRAY_TYPE, &evr, 1); + free(evr); +} + + /** * This is not a generalized function to be called from outside * librpm. It is called internally by rpmtsRun() to rollback @@ -2137,6 +2312,8 @@ assert(psm != NULL); } psm->fi = rpmfiLink(p->fi, NULL); + if (p->hPatched || rpmteDS(p, RPMTAG_PATCHESNAME)) + patchUnpatchedFiles(p->hPatched, p->fi->h, p->isPatchRefresh); /*@-nullstate@*/ /* FIX: psm->fi may be NULL */ if (rpmpsmStage(psm, PSM_PKGINSTALL)) { ourrc++; Index: doc/rpm.8 =================================================================== --- doc/rpm.8.orig +++ doc/rpm.8 @@ -68,7 +68,8 @@ rpm \- RPM Package Manager [\fB\fIPACKAGE_NAME\fB\fR] [\fB-a,--all\fR] [\fB-f,--file \fIFILE\fB\fR] - [\fB-g,--group \fIGROUP\fB\fR] {\fB-p,--package \fIPACKAGE_FILE\fB\fR] + [\fB-g,--group \fIGROUP\fB\fR] [\fB-p,--package \fIPACKAGE_FILE\fB\fR] + [\fB-P,--patches\fR] [\fB--fileid \fIMD5\fB\fR] [\fB--hdrid \fISHA1\fB\fR] [\fB--pkgid \fIMD5\fB\fR] [\fB--tid \fITID\fB\fR] [\fB--querybynumber \fIHDRNUM\fB\fR] [\fB--triggeredby \fIPACKAGE_NAME\fB\fR] [\fB--whatprovides \fICAPABILITY\fB\fR] [\fB--whatrequires \fICAPABILITY\fB\fR] @@ -77,7 +78,8 @@ rpm \- RPM Package Manager .PP - [\fB--changelog\fR] [\fB-c,--configfiles\fR] [\fB-d,--docfiles\fR] [\fB--dump\fR] + [\fB--basedon\fR] [\fB--changelog\fR] [\fB-c,--configfiles\fR] + [\fB-d,--docfiles\fR] [\fB--dump\fR] [\fB--filesbypkg\fR] [\fB-i,--info\fR] [\fB--last\fR] [\fB-l,--list\fR] [\fB--provides\fR] [\fB--qf,--queryformat \fIQUERYFMT\fB\fR] [\fB-R,--requires\fR] [\fB--scripts\fR] [\fB-s,--state\fR] @@ -547,6 +549,10 @@ that will be expanded to paths that are the package manifest as additional \fIPACKAGE_FILE\fR arguments to the query. .TP +\fB-P, --patches\fP +Limit the selected packages to patch-rpms. As a side effect, limit +the file list to patched files. +.TP \fB--pkgid \fIMD5\fB\fR Query package that contains a given package identifier, i.e. the \fIMD5\fR digest of the combined header and @@ -581,6 +587,10 @@ Query all packages that requires \fICAPA .SS "PACKAGE QUERY OPTIONS:" .PP .TP +\fB--basedon\fR +Show what packages a patch-rpm is based on. A patch-rpm can only be +installed if one of the packages it is based on is installed. +.TP \fB--changelog\fR Display change information for the package. .TP @@ -618,7 +628,8 @@ Orders the package listing by install ti packages are at the top. .TP \fB-l, --list\fR -List files in package. +List files in package. If the \fB\-P\fP option is also given, only +patched files are shown. .TP \fB--provides\fR List capabilities this package provides. Index: rpmpopt.in =================================================================== --- rpmpopt.in.orig +++ rpmpopt.in @@ -76,6 +76,10 @@ rpm alias --supplements --qf \ "[%|ENHANCESFLAGS:depflag_strong?{%{ENHANCESNAME} %{ENHANCESFLAGS:depflags} %{ENHANCESVERSION}\n}|]" \ --POPTdesc=$"list capabilities this package supplements" +rpm alias --basedon --qf \ + "[%{PATCHESNAME} %{PATCHESFLAGS:depflags} %{PATCHESVERSION}\n]" \ + --POPTdesc=$"list packages this patch-rpm is based on" + rpm alias --info --qf 'Name : %-27{NAME} Relocations: %|PREFIXES?{[%{PREFIXES} ]}:{(not relocatable)}|\n\ Version : %-27{VERSION} Vendor: %{VENDOR}\n\ Release : %-27{RELEASE} Build Date: %{BUILDTIME:date}\n\ @@ -373,6 +377,10 @@ rpmq alias --supplements --qf \ "[%|ENHANCESFLAGS:depflag_strong?{%{ENHANCESNAME} %{ENHANCESFLAGS:depflags} %{ENHANCESVERSION}\n}|]" \ --POPTdesc=$"list capabilities this package supplements" +rpmq alias --basedon --qf \ + "[%{PATCHESNAME} %{PATCHESFLAGS:depflags} %{PATCHESVERSION}\n]" \ + --POPTdesc=$"list packages this patch-rpm is based on" + rpmq alias --info --qf 'Name : %-27{NAME} Relocations: %|PREFIXES?{[%{PREFIXES} ]}:{(not relocatable)}|\n\ Version : %-27{VERSION} Vendor: %{VENDOR}\n\ Release : %-27{RELEASE} Build Date: %{BUILDTIME:date}\n\ @@ -488,6 +496,10 @@ rpmquery alias --supplements --qf \ "[%|ENHANCESFLAGS:depflag_strong?{%{ENHANCESNAME} %{ENHANCESFLAGS:depflags} %{ENHANCESVERSION}\n}|]" \ --POPTdesc=$"list capabilities this package supplements" +rpmquery alias --basedon --qf \ + "[%{PATCHESNAME} %{PATCHESFLAGS:depflags} %{PATCHESVERSION}\n]" \ + --POPTdesc=$"list packages this patch-rpm is based on" + rpmquery alias --info --qf 'Name : %-27{NAME} Relocations: %|PREFIXES?{[%{PREFIXES} ]}:{(not relocatable)}|\n\ Version : %-27{VERSION} Vendor: %{VENDOR}\n\ Release : %-27{RELEASE} Build Date: %{BUILDTIME:date}\n\