csomh / source-git / rpm

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