/** \ingroup rpmdb * \file lib/headerutil.c */ #include "system.h" #include #include #include #include #include "debug.h" int headerIsSource(Header h) { return (!headerIsEntry(h, RPMTAG_SOURCERPM)); } Header headerCopy(Header h) { Header nh = headerNew(); HeaderIterator hi; struct rpmtd_s td; hi = headerInitIterator(h); while (headerNext(hi, &td)) { if (rpmtdCount(&td) > 0) { (void) headerPut(nh, &td, HEADERPUT_DEFAULT); } rpmtdFreeData(&td); } headerFreeIterator(hi); return nh; } void headerCopyTags(Header headerFrom, Header headerTo, const rpmTagVal * tagstocopy) { const rpmTagVal * p; struct rpmtd_s td; if (headerFrom == headerTo) return; for (p = tagstocopy; *p != 0; p++) { if (headerIsEntry(headerTo, *p)) continue; if (!headerGet(headerFrom, *p, &td, (HEADERGET_MINMEM|HEADERGET_RAW))) continue; (void) headerPut(headerTo, &td, HEADERPUT_DEFAULT); rpmtdFreeData(&td); } } char * headerGetAsString(Header h, rpmTagVal tag) { char *res = NULL; struct rpmtd_s td; if (headerGet(h, tag, &td, HEADERGET_EXT)) { if (rpmtdCount(&td) == 1) { res = rpmtdFormat(&td, RPMTD_FORMAT_STRING, NULL); } rpmtdFreeData(&td); } return res; } const char * headerGetString(Header h, rpmTagVal tag) { const char *res = NULL; struct rpmtd_s td; if (headerGet(h, tag, &td, HEADERGET_MINMEM)) { if (rpmtdCount(&td) == 1) { res = rpmtdGetString(&td); } rpmtdFreeData(&td); } return res; } uint64_t headerGetNumber(Header h, rpmTagVal tag) { uint64_t res = 0; struct rpmtd_s td; if (headerGet(h, tag, &td, HEADERGET_EXT)) { if (rpmtdCount(&td) == 1) { res = rpmtdGetNumber(&td); } rpmtdFreeData(&td); } return res; } /* * Sanity check data types against tag table before putting. Assume * append on all array-types. */ static int headerPutType(Header h, rpmTagVal tag, rpmTagType reqtype, rpm_constdata_t data, rpm_count_t size) { struct rpmtd_s td; rpmTagType type = rpmTagGetTagType(tag); rpmTagReturnType retype = rpmTagGetReturnType(tag); headerPutFlags flags = HEADERPUT_APPEND; int valid = 1; /* Basic sanity checks: type must match and there must be data to put */ if (type != reqtype || size < 1 || data == NULL || h == NULL) { valid = 0; } /* * Non-array types can't be appended to. Binary types use size * for data length, for other non-array types size must be 1. */ if (retype != RPM_ARRAY_RETURN_TYPE) { flags = HEADERPUT_DEFAULT; if (type != RPM_BIN_TYPE && size != 1) { valid = 0; } } if (valid) { rpmtdReset(&td); td.tag = tag; td.type = type; td.data = (void *) data; td.count = size; valid = headerPut(h, &td, flags); } return valid; } int headerPutString(Header h, rpmTagVal tag, const char *val) { rpmTagType type = rpmTagGetTagType(tag); const void *sptr = NULL; /* string arrays expect char **, arrange that */ if (type == RPM_STRING_ARRAY_TYPE || type == RPM_I18NSTRING_TYPE) { sptr = &val; } else if (type == RPM_STRING_TYPE) { sptr = val; } else { return 0; } return headerPutType(h, tag, type, sptr, 1); } int headerPutStringArray(Header h, rpmTagVal tag, const char **array, rpm_count_t size) { return headerPutType(h, tag, RPM_STRING_ARRAY_TYPE, array, size); } int headerPutChar(Header h, rpmTagVal tag, const char *val, rpm_count_t size) { return headerPutType(h, tag, RPM_CHAR_TYPE, val, size); } int headerPutUint8(Header h, rpmTagVal tag, const uint8_t *val, rpm_count_t size) { return headerPutType(h, tag, RPM_INT8_TYPE, val, size); } int headerPutUint16(Header h, rpmTagVal tag, const uint16_t *val, rpm_count_t size) { return headerPutType(h, tag, RPM_INT16_TYPE, val, size); } int headerPutUint32(Header h, rpmTagVal tag, const uint32_t *val, rpm_count_t size) { return headerPutType(h, tag, RPM_INT32_TYPE, val, size); } int headerPutUint64(Header h, rpmTagVal tag, const uint64_t *val, rpm_count_t size) { return headerPutType(h, tag, RPM_INT64_TYPE, val, size); } int headerPutBin(Header h, rpmTagVal tag, const uint8_t *val, rpm_count_t size) { return headerPutType(h, tag, RPM_BIN_TYPE, val, size); } static int dncmp(const void * a, const void * b) { const char *const * first = a; const char *const * second = b; return strcmp(*first, *second); } static void compressFilelist(Header h) { struct rpmtd_s fileNames; char ** dirNames; const char ** baseNames; uint32_t * dirIndexes; rpm_count_t count, realCount = 0; int i; int dirIndex = -1; /* * This assumes the file list is already sorted, and begins with a * single '/'. That assumption isn't critical, but it makes things go * a bit faster. */ if (headerIsEntry(h, RPMTAG_DIRNAMES)) { headerDel(h, RPMTAG_OLDFILENAMES); return; /* Already converted. */ } if (!headerGet(h, RPMTAG_OLDFILENAMES, &fileNames, HEADERGET_MINMEM)) return; count = rpmtdCount(&fileNames); if (count < 1) return; dirNames = xmalloc(sizeof(*dirNames) * count); /* worst case */ baseNames = xmalloc(sizeof(*dirNames) * count); dirIndexes = xmalloc(sizeof(*dirIndexes) * count); /* HACK. Source RPM, so just do things differently */ { const char *fn = rpmtdGetString(&fileNames); if (fn && *fn != '/') { dirIndex = 0; dirNames[dirIndex] = xstrdup(""); while ((i = rpmtdNext(&fileNames)) >= 0) { dirIndexes[i] = dirIndex; baseNames[i] = rpmtdGetString(&fileNames); realCount++; } goto exit; } } /* * XXX EVIL HACK, FIXME: * This modifies (and then restores) a const string from rpmtd * through basename retrieved from strrchr() which silently * casts away const on return. */ while ((i = rpmtdNext(&fileNames)) >= 0) { char ** needle; char savechar; char * baseName; size_t len; char *filename = (char *) rpmtdGetString(&fileNames); /* HACK HACK */ if (filename == NULL) /* XXX can't happen */ continue; baseName = strrchr(filename, '/'); if (baseName == NULL) { baseName = filename; } else { baseName += 1; } len = baseName - filename; needle = dirNames; savechar = *baseName; *baseName = '\0'; if (dirIndex < 0 || (needle = bsearch(&filename, dirNames, dirIndex + 1, sizeof(dirNames[0]), dncmp)) == NULL) { char *s = xmalloc(len + 1); rstrlcpy(s, filename, len + 1); dirIndexes[realCount] = ++dirIndex; dirNames[dirIndex] = s; } else dirIndexes[realCount] = needle - dirNames; *baseName = savechar; baseNames[realCount] = baseName; realCount++; } exit: if (count > 0) { headerPutUint32(h, RPMTAG_DIRINDEXES, dirIndexes, realCount); headerPutStringArray(h, RPMTAG_BASENAMES, baseNames, realCount); headerPutStringArray(h, RPMTAG_DIRNAMES, (const char **) dirNames, dirIndex + 1); } rpmtdFreeData(&fileNames); for (i = 0; i <= dirIndex; i++) { free(dirNames[i]); } free(dirNames); free(baseNames); free(dirIndexes); headerDel(h, RPMTAG_OLDFILENAMES); } static void expandFilelist(Header h) { struct rpmtd_s filenames; if (!headerIsEntry(h, RPMTAG_OLDFILENAMES)) { (void) headerGet(h, RPMTAG_FILENAMES, &filenames, HEADERGET_EXT); if (rpmtdCount(&filenames) < 1) return; rpmtdSetTag(&filenames, RPMTAG_OLDFILENAMES); headerPut(h, &filenames, HEADERPUT_DEFAULT); rpmtdFreeData(&filenames); } (void) headerDel(h, RPMTAG_DIRNAMES); (void) headerDel(h, RPMTAG_BASENAMES); (void) headerDel(h, RPMTAG_DIRINDEXES); } /* * Up to rpm 3.0.4, packages implicitly provided their own name-version-release. * Retrofit an explicit "Provides: name = epoch:version-release. */ static void providePackageNVR(Header h) { const char *name = headerGetString(h, RPMTAG_NAME); char *pEVR = headerGetAsString(h, RPMTAG_EVR); rpmsenseFlags pFlags = RPMSENSE_EQUAL; int bingo = 1; struct rpmtd_s pnames; rpmds hds, nvrds; /* Generate provides for this package name-version-release. */ if (!(name && pEVR)) return; /* * Rpm prior to 3.0.3 does not have versioned provides. * If no provides at all are available, we can just add. */ if (!headerGet(h, RPMTAG_PROVIDENAME, &pnames, HEADERGET_MINMEM)) { goto exit; } /* * Otherwise, fill in entries on legacy packages. */ if (!headerIsEntry(h, RPMTAG_PROVIDEVERSION)) { while (rpmtdNext(&pnames) >= 0) { rpmsenseFlags fdummy = RPMSENSE_ANY; headerPutString(h, RPMTAG_PROVIDEVERSION, ""); headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &fdummy, 1); } goto exit; } /* see if we already have this provide */ hds = rpmdsNew(h, RPMTAG_PROVIDENAME, 0); nvrds = rpmdsSingle(RPMTAG_PROVIDENAME, name, pEVR, pFlags); if (rpmdsFind(hds, nvrds) >= 0) { bingo = 0; } rpmdsFree(hds); rpmdsFree(nvrds); exit: if (bingo) { headerPutString(h, RPMTAG_PROVIDENAME, name); headerPutString(h, RPMTAG_PROVIDEVERSION, pEVR); headerPutUint32(h, RPMTAG_PROVIDEFLAGS, &pFlags, 1); } rpmtdFreeData(&pnames); free(pEVR); } static void legacyRetrofit(Header h) { /* * The file list was moved to a more compressed format which not * only saves memory (nice), but gives fingerprinting a nice, fat * speed boost (very nice). Go ahead and convert old headers to * the new style (this is a noop for new headers). */ compressFilelist(h); /* Retrofit "Provide: name = EVR" for binary packages. */ if (!headerIsSource(h)) { providePackageNVR(h); } } int headerConvert(Header h, int op) { int rc = 1; if (h == NULL) return 0; switch (op) { case HEADERCONV_EXPANDFILELIST: expandFilelist(h); break; case HEADERCONV_COMPRESSFILELIST: compressFilelist(h); break; case HEADERCONV_RETROFIT_V3: legacyRetrofit(h); break; default: rc = 0; break; } return rc; };