rpm/lib/header.c

907 lines
23 KiB
C

/* RPM - Copyright (C) 1995 Red Hat Software
*
* header.c - routines for managing rpm headers
*/
/* Data written to file descriptors is in network byte order. */
/* Data read from file descriptors is expected to be in */
/* network byte order and is converted on the fly to host order. */
#include <stdlib.h>
#include <asm/byteorder.h>
#include <ctype.h>
#include <malloc.h>
#include <string.h>
#include <unistd.h>
#include "header.h"
#include "rpmlib.h" /* necessary only for dumpHeader() */
#define INDEX_MALLOC_SIZE 8
#define DATA_MALLOC_SIZE 1024
struct headerToken {
struct indexEntry *index;
int entries_malloced;
int entries_used;
char *data;
int data_malloced;
int data_used;
int fully_sorted; /* This means the index and the data! */
};
struct indexEntry {
int_32 tag;
int_32 type;
int_32 offset; /* Offset from beginning of data segment */
int_32 count;
};
static int indexSort(const void *ap, const void *bp);
static struct indexEntry *findEntry(Header h, int_32 tag);
static void *dataHostToNetwork(Header h);
static void *dataNetworkToHost(Header h);
/********************************************************************/
/* */
/* Header iteration and copying */
/* */
/********************************************************************/
struct headerIteratorS {
Header h;
int next_index;
};
HeaderIterator initIterator(Header h)
{
HeaderIterator hi = malloc(sizeof(struct headerIteratorS));
hi->h = h;
hi->next_index = 0;
return hi;
}
void freeIterator(HeaderIterator iter)
{
free(iter);
}
int nextIterator(HeaderIterator iter,
int_32 *tag, int_32 *type, void **p, int_32 *c)
{
struct headerToken *h = iter->h;
struct indexEntry *index = h->index;
int x = h->entries_used;
int slot = iter->next_index;
char **spp;
char *sp;
if (slot == h->entries_used) {
return 0;
}
iter->next_index++;
*tag = index[slot].tag;
*type = index[slot].type;
*c = index[slot].count;
/* Now look it up */
switch (*type) {
case INT64_TYPE:
case INT32_TYPE:
case INT16_TYPE:
case INT8_TYPE:
case BIN_TYPE:
case CHAR_TYPE:
*p = h->data + index[slot].offset;
break;
case STRING_TYPE:
if (*c == 1) {
/* Special case -- just return a pointer to the string */
*p = h->data + index[slot].offset;
break;
}
/* Fall through to STRING_ARRAY_TYPE */
case STRING_ARRAY_TYPE:
/* Correction! */
*type = STRING_ARRAY_TYPE;
/* Otherwise, build up an array of char* to return */
x = index[slot].count;
*p = malloc(x * sizeof(char *));
spp = (char **) *p;
sp = h->data + index[slot].offset;
while (x--) {
*spp++ = sp;
sp = strchr(sp, 0);
sp++;
}
break;
default:
fprintf(stderr, "Data type %d not supprted\n", (int) *type);
exit(1);
}
return 1;
}
static int indexSort(const void *ap, const void *bp)
{
int_32 a, b;
a = ((struct indexEntry *)ap)->tag;
b = ((struct indexEntry *)bp)->tag;
if (a > b) {
return 1;
} else if (a < b) {
return -1;
} else {
return 0;
}
}
Header copyHeader(Header h)
{
int_32 tag, type, count;
void *ptr;
HeaderIterator headerIter;
Header res = newHeader();
/* Sort the index */
qsort(h->index, h->entries_used, sizeof(struct indexEntry), indexSort);
headerIter = initIterator(h);
/* The result here is that the data is also sorted */
while (nextIterator(headerIter, &tag, &type, &ptr, &count)) {
addEntry(res, tag, type, ptr, count);
}
res->fully_sorted = 1;
return res;
}
/********************************************************************/
/* */
/* Reading and writing headers */
/* */
/********************************************************************/
void writeHeader(int fd, Header h)
{
int_32 l;
struct indexEntry *p;
struct indexEntry *copyIndex;
int c;
void *converted_data;
/* This magic actually sorts the data */
h = copyHeader(h);
/* We must write using network byte order! */
/* First write out the length of the index (count of index entries) */
l = htonl(h->entries_used);
write(fd, &l, sizeof(l));
/* Then write the length of the data (number of bytes) */
l = htonl(h->data_used);
write(fd, &l, sizeof(l));
/* Make a copy of the index for htonl() */
copyIndex = malloc(sizeof(struct indexEntry) * h->entries_used);
memcpy(copyIndex, h->index, sizeof(struct indexEntry) * h->entries_used);
/* Convert the index */
c = h->entries_used;
p = copyIndex;
while (c--) {
p->tag = htonl(p->tag);
p->type = htonl(p->type);
p->offset = htonl(p->offset);
p->count = htonl(p->count);
p++;
}
/* Write the index */
write(fd, copyIndex, sizeof(struct indexEntry) * h->entries_used);
free(copyIndex);
/* Finally convert and write the data */
converted_data = dataHostToNetwork(h);
write(fd, converted_data, h->data_used);
free(converted_data);
free(h);
}
static void *dataHostToNetwork(Header h)
{
char *data, *p;
struct indexEntry *index = h->index;
int entries = h->entries_used;
int count;
data = malloc(h->data_used);
memcpy(data, h->data, h->data_used);
while (entries--) {
p = data + index->offset;
count = index->count;
switch (index->type) {
case INT64_TYPE:
while (count--) {
*((int_64 *)p) = htonl(*((int_64 *)p));
p += sizeof(int_64);
}
break;
case INT32_TYPE:
while (count--) {
*((int_32 *)p) = htonl(*((int_32 *)p));
p += sizeof(int_32);
}
break;
case INT16_TYPE:
while (count--) {
*((int_16 *)p) = htons(*((int_16 *)p));
p += sizeof(int_16);
}
break;
case INT8_TYPE:
case BIN_TYPE:
case CHAR_TYPE:
case STRING_TYPE:
case STRING_ARRAY_TYPE:
/* No conversion necessary */
break;
default:
fprintf(stderr, "Data type %d not supprted\n", (int) index->type);
exit(1);
}
index++;
}
return data;
}
Header readHeader(int fd)
{
int_32 il, dl;
struct indexEntry *p;
int c;
void *converted_data;
struct headerToken *h = (struct headerToken *)
malloc(sizeof(struct headerToken));
/* First read the index length (count of index entries) */
read(fd, &il, sizeof(il));
il = ntohl(il);
/* Then read the data length (number of bytes) */
read(fd, &dl, sizeof(dl));
dl = ntohl(dl);
/* Next read the index */
h->index = malloc(il * sizeof(struct indexEntry));
h->entries_malloced = il;
h->entries_used = il;
read(fd, h->index, sizeof(struct indexEntry) * il);
/* Convert the index */
c = h->entries_used;
p = h->index;
while (c--) {
p->tag = ntohl(p->tag);
p->type = ntohl(p->type);
p->offset = ntohl(p->offset);
p->count = ntohl(p->count);
p++;
}
/* Finally, read the data */
h->data = malloc(dl);
h->data_malloced = dl;
h->data_used = dl;
read(fd, h->data, dl);
/* and convert it */
converted_data = dataNetworkToHost(h);
free(h->data);
h->data = converted_data;
h->fully_sorted = 1;
return h;
}
static void *dataNetworkToHost(Header h)
{
char *data, *p;
struct indexEntry *index = h->index;
int entries = h->entries_used;
int count;
data = malloc(h->data_used);
memcpy(data, h->data, h->data_used);
while (entries--) {
p = data + index->offset;
count = index->count;
switch (index->type) {
case INT64_TYPE:
while (count--) {
*((int_64 *)p) = ntohl(*((int_64 *)p));
p += sizeof(int_64);
}
break;
case INT32_TYPE:
while (count--) {
*((int_32 *)p) = ntohl(*((int_32 *)p));
p += sizeof(int_32);
}
break;
case INT16_TYPE:
while (count--) {
*((int_16 *)p) = ntohs(*((int_16 *)p));
p += sizeof(int_16);
}
break;
case INT8_TYPE:
case BIN_TYPE:
case CHAR_TYPE:
case STRING_TYPE:
case STRING_ARRAY_TYPE:
/* No conversion necessary */
break;
default:
fprintf(stderr, "Data type %d not supprted\n", (int) index->type);
exit(1);
}
index++;
}
return data;
}
/********************************************************************/
/* */
/* Header loading and unloading */
/* */
/********************************************************************/
Header loadHeader(void *pv)
{
int_32 il, dl; /* index length, data length */
char *p = pv;
struct headerToken *h = malloc(sizeof(struct headerToken));
il = *((int_32 *) p);
p += sizeof(int_32);
dl = *((int_32 *) p);
p += sizeof(int_32);
h->entries_malloced = il;
h->entries_used = il;
h->index = malloc(il * sizeof(struct indexEntry));
memcpy(h->index, p, il * sizeof(struct indexEntry));
p += il * sizeof(struct indexEntry);
h->data_malloced = dl;
h->data_used = dl;
h->data = malloc(dl);
memcpy(h->data, p, dl);
/* This assumes you only loadHeader() something you unloadHeader()-ed */
h->fully_sorted = 1;
return h;
}
void *unloadHeader(Header h)
{
void *p;
int_32 *pi;
char * chptr;
/* This magic actually sorts the data */
h = copyHeader(h);
pi = p = malloc(2 * sizeof(int_32) +
h->entries_used * sizeof(struct indexEntry) +
h->data_used);
*pi++ = h->entries_used;
*pi++ = h->data_used;
chptr = (char *) pi;
memcpy(chptr, h->index, h->entries_used * sizeof(struct indexEntry));
chptr += h->entries_used * sizeof(struct indexEntry);
memcpy(chptr, h->data, h->data_used);
freeHeader(h);
return p;
}
/********************************************************************/
/* */
/* Header dumping */
/* */
/********************************************************************/
void dumpHeader(Header h, FILE * f, int flags)
{
int i, c, ct;
struct indexEntry *p;
char *dp;
char ch;
char *type, *tag;
/* First write out the length of the index (count of index entries) */
fprintf(f, "Entry count: %d\n", h->entries_used);
/* And the length of the data (number of bytes) */
fprintf(f, "Data count : %d\n", h->data_used);
/* Now write the index */
p = h->index;
fprintf(f, "\n CT TAG TYPE "
"OFSET COUNT\n");
for (i = 0; i < h->entries_used; i++) {
switch (p->type) {
case NULL_TYPE: type = "NULL_TYPE"; break;
case CHAR_TYPE: type = "CHAR_TYPE"; break;
case BIN_TYPE: type = "BIN_TYPE"; break;
case INT8_TYPE: type = "INT8_TYPE"; break;
case INT16_TYPE: type = "INT16_TYPE"; break;
case INT32_TYPE: type = "INT32_TYPE"; break;
case INT64_TYPE: type = "INT64_TYPE"; break;
case STRING_TYPE: type = "STRING_TYPE"; break;
case STRING_ARRAY_TYPE: type = "STRING_ARRAY_TYPE"; break;
default: type = "(unknown)"; break;
}
switch (p->tag) {
case RPMTAG_NAME: tag = "RPMTAG_NAME"; break;
case RPMTAG_VERSION: tag = "RPMTAG_VERSION"; break;
case RPMTAG_RELEASE: tag = "RPMTAG_RELEASE"; break;
case RPMTAG_SERIAL: tag = "RPMTAG_SERIAL"; break;
case RPMTAG_SUMMARY: tag = "RPMTAG_SUMMARY"; break;
case RPMTAG_DESCRIPTION: tag = "RPMTAG_DESCRIPTION"; break;
case RPMTAG_BUILDTIME: tag = "RPMTAG_BUILDTIME"; break;
case RPMTAG_BUILDHOST: tag = "RPMTAG_BUILDHOST"; break;
case RPMTAG_INSTALLTIME: tag = "RPMTAG_INSTALLTIME"; break;
case RPMTAG_SIZE: tag = "RPMTAG_SIZE"; break;
case RPMTAG_DISTRIBUTION: tag = "RPMTAG_DISTRIBUTION"; break;
case RPMTAG_VENDOR: tag = "RPMTAG_VENDOR"; break;
case RPMTAG_GIF: tag = "RPMTAG_GIF"; break;
case RPMTAG_XPM: tag = "RPMTAG_XPM"; break;
case RPMTAG_COPYRIGHT: tag = "RPMTAG_COPYRIGHT"; break;
case RPMTAG_PACKAGER: tag = "RPMTAG_PACKAGER"; break;
case RPMTAG_GROUP: tag = "RPMTAG_GROUP"; break;
case RPMTAG_CHANGELOG: tag = "RPMTAG_CHANGELOG"; break;
case RPMTAG_SOURCE: tag = "RPMTAG_SOURCE"; break;
case RPMTAG_PATCH: tag = "RPMTAG_PATCH"; break;
case RPMTAG_URL: tag = "RPMTAG_URL"; break;
case RPMTAG_OS: tag = "RPMTAG_OS"; break;
case RPMTAG_ARCH: tag = "RPMTAG_ARCH"; break;
case RPMTAG_PREIN: tag = "RPMTAG_PREIN"; break;
case RPMTAG_POSTIN: tag = "RPMTAG_POSTIN"; break;
case RPMTAG_PREUN: tag = "RPMTAG_PREUN"; break;
case RPMTAG_POSTUN: tag = "RPMTAG_POSTUN"; break;
case RPMTAG_FILENAMES: tag = "RPMTAG_FILES"; break;
case RPMTAG_FILESIZES: tag = "RPMTAG_SIZES"; break;
case RPMTAG_FILESTATES: tag = "RPMTAG_FILESTATES"; break;
case RPMTAG_FILEMODES: tag = "RPMTAG_FILEMODES"; break;
case RPMTAG_FILEUIDS: tag = "RPMTAG_FILEUIDS"; break;
case RPMTAG_FILEGIDS: tag = "RPMTAG_FILEGIDS"; break;
case RPMTAG_FILERDEVS: tag = "RPMTAG_FILERDEVS"; break;
case RPMTAG_FILEMTIMES: tag = "RPMTAG_FILEMTIMES"; break;
case RPMTAG_FILEMD5S: tag = "RPMTAG_FILEMD5S"; break;
case RPMTAG_FILELINKTOS: tag = "RPMTAG_FILELINKTOS"; break;
case RPMTAG_FILEFLAGS: tag = "RPMTAG_FILEFLAGS"; break;
case RPMTAG_FILEUSERNAME: tag = "RPMTAG_FILEUSERNAME"; break;
case RPMTAG_FILEGROUPNAME: tag = "RPMTAG_FILEGROUPNAME"; break;
case RPMTAG_EXCLUDE: tag = "RPMTAG_EXCLUDE"; break;
case RPMTAG_EXCLUSIVE: tag = "RPMTAG_EXCLUSIVE"; break;
case RPMTAG_ICON: tag = "RPMTAG_ICON"; break;
case RPMTAG_SOURCERPM: tag = "RPMTAG_SOURCERPM"; break;
default: tag = "(unknown)"; break;
}
fprintf(f, "Entry : %.3d %-20s %-18s 0x%.8x %.8d\n", i, tag, type,
(uint_32) p->offset, (uint_32) p->count);
if (flags & DUMP_INLINE) {
/* Print the data inline */
dp = h->data + p->offset;
c = p->count;
ct = 0;
switch (p->type) {
case INT32_TYPE:
while (c--) {
fprintf(f, " Data: %.3d 0x%.8x (%d)\n", ct++,
(uint_32) *((int_32 *) dp),
(uint_32) *((int_32 *) dp));
dp += sizeof(int_32);
}
break;
case INT16_TYPE:
while (c--) {
fprintf(f, " Data: %.3d 0x%.4x (%d)\n", ct++,
(short int) *((int_16 *) dp),
(short int) *((int_16 *) dp));
dp += sizeof(int_16);
}
break;
case INT8_TYPE:
while (c--) {
fprintf(f, " Data: %.3d 0x%.2x (%d)\n", ct++,
(char) *((int_8 *) dp),
(char) *((int_8 *) dp));
dp += sizeof(int_8);
}
break;
case BIN_TYPE:
break;
case CHAR_TYPE:
while (c--) {
ch = (char) *((char *) dp);
fprintf(f, " Data: %.3d 0x%2x %c (%d)\n", ct++,
ch,
(isprint(ch) ? ch : ' '),
(char) *((char *) dp));
dp += sizeof(char);
}
break;
case STRING_TYPE:
case STRING_ARRAY_TYPE:
while (c--) {
fprintf(f, " Data: %.3d %s\n", ct++, (char *) dp);
dp = strchr(dp, 0);
dp++;
}
break;
default:
fprintf(stderr, "Data type %d not supprted\n", (int) p->type);
exit(1);
}
}
p++;
}
}
/********************************************************************/
/* */
/* Entry lookup */
/* */
/********************************************************************/
static int tagCompare(const void *key, const void *member)
{
if (*((int_32 *)key) > ((struct indexEntry *)member)->tag) {
return 1;
} else if (*((int_32 *)key) < ((struct indexEntry *)member)->tag) {
return -1;
} else {
return 0;
}
}
static struct indexEntry *findEntry(Header h, int_32 tag)
{
struct indexEntry *index = h->index;
int x = h->entries_used;
if (h->fully_sorted) {
return bsearch(&tag, index, x, sizeof(struct indexEntry), tagCompare);
} else {
while (x && (tag != index->tag)) {
index++;
x--;
}
return (x ? index : NULL);
}
}
int isEntry(Header h, int_32 tag)
{
return (findEntry(h, tag) ? 1 : 0);
}
int getEntry(Header h, int_32 tag, int_32 * type, void **p, int_32 * c)
{
struct indexEntry *index;
char **spp;
char *sp;
int x;
/* First find the tag */
index = findEntry(h, tag);
if (! index) {
*p = NULL;
return 0;
}
if (type) {
*type = (int) index->type;
}
if (c) {
*c = index->count;
}
/* Now look it up */
switch (index->type) {
case INT64_TYPE:
case INT32_TYPE:
case INT16_TYPE:
case INT8_TYPE:
case BIN_TYPE:
case CHAR_TYPE:
*p = h->data + index->offset;
break;
case STRING_TYPE:
if (index->count == 1) {
/* Special case -- just return a pointer to the string */
*p = h->data + index->offset;
break;
}
/* Fall through to STRING_ARRAY_TYPE */
case STRING_ARRAY_TYPE:
/* Correction! */
if (type) {
*type = STRING_ARRAY_TYPE;
}
/* Otherwise, build up an array of char* to return */
x = index->count;
*p = malloc(x * sizeof(char *));
spp = (char **) *p;
sp = h->data + index->offset;
while (x--) {
*spp++ = sp;
sp = strchr(sp, 0);
sp++;
}
break;
default:
fprintf(stderr, "Data type %d not supprted\n",
(int) index->type);
exit(1);
}
return 1;
}
/********************************************************************/
/* */
/* Header creation and deletion */
/* */
/********************************************************************/
Header newHeader()
{
struct headerToken *h = (struct headerToken *)
malloc(sizeof(struct headerToken));
h->data = malloc(DATA_MALLOC_SIZE);
h->data_malloced = DATA_MALLOC_SIZE;
h->data_used = 0;
h->index = malloc(INDEX_MALLOC_SIZE * sizeof(struct indexEntry));
h->entries_malloced = INDEX_MALLOC_SIZE;
h->entries_used = 0;
h->fully_sorted = 0;
return (Header) h;
}
void freeHeader(Header h)
{
free(h->index);
free(h->data);
free(h);
}
unsigned int sizeofHeader(Header h)
{
unsigned int size;
/* Do some real magic to determine the ON-DISK size */
h = copyHeader(h);
size = sizeof(int_32); /* count of index entries */
size += sizeof(int_32); /* length of data */
size += sizeof(struct indexEntry) * h->entries_used;
size += h->data_used;
freeHeader(h);
return size;
}
/********************************************************************/
/* */
/* Adding and modifying entries */
/* */
/********************************************************************/
int addEntry(Header h, int_32 tag, int_32 type, void *p, int_32 c)
{
struct indexEntry *entry;
void *ptr;
char **spp;
char *sp;
int i, length;
int pad;
if (c <= 0) {
fprintf(stderr, "Bad count for addEntry(): %d\n", (int) c);
exit(1);
}
/* Allocate more index space if necessary */
if (h->entries_used == h->entries_malloced) {
h->entries_malloced += INDEX_MALLOC_SIZE;
h->index = realloc(h->index,
h->entries_malloced * sizeof(struct indexEntry));
}
/* Fill in the index */
i = h->entries_used++;
entry = &((h->index)[i]);
entry->tag = tag;
entry->type = type;
entry->count = c;
/* Compute length of data to add */
pad = 0;
switch (type) {
case INT64_TYPE:
length = sizeof(int_64) * c;
pad = 8;
break;
case INT32_TYPE:
length = sizeof(int_32) * c;
pad = 4;
break;
case INT16_TYPE:
length = sizeof(int_16) * c;
pad = 2;
break;
case INT8_TYPE:
length = sizeof(int_8) * c;
break;
case BIN_TYPE:
case CHAR_TYPE:
length = sizeof(char) * c;
break;
case STRING_TYPE:
if (c == 1) {
/* Special case -- p is just the string */
length = strlen(p) + 1;
break;
}
/* Otherwise fall through to STRING_ARRAY_TYPE */
/* This should not be allowed */
fprintf(stderr, "addEntry() STRING_TYPE count must be 0.\n");
exit(1);
case STRING_ARRAY_TYPE:
/* This is like STRING_TYPE, except it's *always* an array */
/* Compute sum of length of all strings, including null terminators */
i = c;
spp = p;
length = 0;
while (i--) {
/* add one for null termination */
length += strlen(*spp++) + 1;
}
break;
default:
fprintf(stderr, "Data type %d not supprted\n", (int) type);
exit(1);
}
if (pad) {
pad = (pad - (h->data_used % pad)) % pad;
}
/* Allocate more data space if necessary */
while ((length + pad + h->data_used) > h->data_malloced) {
h->data_malloced += DATA_MALLOC_SIZE;
h->data = realloc(h->data, h->data_malloced);
}
/* Fill in the data */
entry->offset = h->data_used + pad;
ptr = h->data + h->data_used + pad;
switch (type) {
case INT32_TYPE:
case INT16_TYPE:
case INT8_TYPE:
case BIN_TYPE:
case CHAR_TYPE:
memcpy(ptr, p, length);
break;
case STRING_TYPE:
if (c == 1) {
/* Special case -- p is just the string */
strcpy(ptr, p);
break;
}
/* Fall through to STRING_ARRAY_TYPE */
/* This should not be allowed */
fprintf(stderr, "addEntry() internal error!.\n");
exit(1);
case STRING_ARRAY_TYPE:
/* Otherwise, p is char** */
i = c;
spp = p;
sp = (char *) ptr;
while (i--) {
strcpy(sp, *spp);
sp += strlen(*spp++) + 1;
}
break;
default:
fprintf(stderr, "Data type %d not supprted\n", (int) type);
exit(1);
}
h->data_used += length + pad;
h->fully_sorted = 0;
return 1;
}
int modifyEntry(Header h, int_32 tag, int_32 type, void *p, int_32 c)
{
struct indexEntry *index;
/* First find the tag */
index = findEntry(h, tag);
if (! index) {
return 0;
}
if (type != index->type) {
return 0;
}
if (c != 1) {
return 0;
}
if (index->count != 1) {
return 0;
}
switch (index->type) {
case INT64_TYPE:
*((int_64 *)(h->data + index->offset)) = *((int_64 *)p);
break;
case INT32_TYPE:
*((int_32 *)(h->data + index->offset)) = *((int_32 *)p);
break;
case INT16_TYPE:
*((int_16 *)(h->data + index->offset)) = *((int_16 *)p);
break;
case INT8_TYPE:
*((int_8 *)(h->data + index->offset)) = *((int_8 *)p);
break;
case BIN_TYPE:
case CHAR_TYPE:
*((char *)(h->data + index->offset)) = *((char *)p);
break;
default:
return 0;
}
return 1;
}