#include "system.h"
#include <netdb.h>
+#include <errno.h>
#include <rpm/rpmtypes.h>
#include <rpm/rpmstring.h>
#include "lib/header_internal.h"
#include "lib/misc.h" /* tag function proto */
+#include <errno.h>
#include "debug.h"
/** \ingroup header
return NULL;
}
-static Header headerCreate(void *blob, int32_t indexLen)
+static Header headerCreate(void *blob, unsigned int pvlen, int32_t indexLen)
{
Header h = xcalloc(1, sizeof(*h));
- h->blob = blob;
if (blob) {
+ h->blob = (pvlen > 0) ? memcpy(xmalloc(pvlen), blob, pvlen) : blob;
h->indexAlloced = indexLen + 1;
h->indexUsed = indexLen;
} else {
Header headerNew(void)
{
- return headerCreate(NULL, 0);
+ return headerCreate(NULL, 0, 0);
}
int headerVerifyInfo(int il, int dl, const void * pev, void * iv, int negate)
return i;
if (hdrchkAlign(info->type, info->offset))
return i;
- if (!negate && hdrchkRange(dl, info->offset))
+ if (hdrchkRange(dl, info->offset))
return i;
if (hdrchkData(info->count))
return i;
}
}
+static inline unsigned int alignDiff(rpm_tagtype_t type, unsigned int alignsize)
+{
+ int typesize = typeSizes[type];
+
+ if (typesize > 1) {
+ unsigned int diff = typesize - (alignsize % typesize);
+ if (diff != typesize)
+ return diff;
+ }
+ return 0;
+}
+
unsigned headerSizeof(Header h, int magicp)
{
indexEntry entry;
size += 2 * sizeof(int32_t); /* count of index entries */
for (i = 0, entry = h->index; i < h->indexUsed; i++, entry++) {
- rpm_tagtype_t type;
-
/* Regions go in as is ... */
if (ENTRY_IS_REGION(entry)) {
size += entry->length;
continue;
/* Alignment */
- type = entry->info.type;
- if (typeSizes[type] > 1) {
- unsigned diff = typeSizes[type] - (size % typeSizes[type]);
- if (diff != typeSizes[type]) {
- size += diff;
- }
- }
+ size += alignDiff(entry->info.type, size);
size += sizeof(struct entryInfo_s) + entry->length;
}
return size;
}
+/*
+ * Header string (array) size calculation, bounded if end is non-NULL.
+ * Return length (including \0 termination) on success, -1 on error.
+ */
+static inline int strtaglen(const char *str, rpm_count_t c, const char *end)
+{
+ const char *start = str;
+ const char *s;
+
+ if (end) {
+ while ((s = memchr(start, '\0', end-start))) {
+ if (--c == 0 || s > end)
+ break;
+ start = s + 1;
+ }
+ } else {
+ while ((s = strchr(start, '\0'))) {
+ if (--c == 0)
+ break;
+ start = s + 1;
+ }
+ }
+ return (c > 0) ? -1 : (s - str + 1);
+}
+
/**
* Return length of entry data.
* @param type entry data type
static int dataLength(rpm_tagtype_t type, rpm_constdata_t p, rpm_count_t count,
int onDisk, rpm_constdata_t pend)
{
- const unsigned char * s = p;
- const unsigned char * se = pend;
+ const char * s = p;
+ const char * se = pend;
int length = 0;
switch (type) {
case RPM_STRING_TYPE:
if (count != 1)
return -1;
- while (*s++) {
- if (se && s > se)
- return -1;
- length++;
- }
- length++; /* count nul terminator too. */
+ length = strtaglen(s, 1, se);
break;
case RPM_STRING_ARRAY_TYPE:
/* Compute sum of length of all strings, including nul terminators */
if (onDisk) {
- while (count--) {
- length++; /* count nul terminator too */
- while (*s++) {
- if (se && s > se)
- return -1;
- length++;
- }
- }
+ length = strtaglen(s, count, se);
} else {
const char ** av = (const char **)p;
while (count--) {
* @param dataStart header data start
* @param dataEnd header data end
* @param regionid region offset
+ * @param fast use offsets for data sizes if possible
* @return no. bytes of data in region, -1 on error
*/
static int regionSwab(indexEntry entry, int il, int dl,
entryInfo pe,
unsigned char * dataStart,
const unsigned char * dataEnd,
- int regionid)
+ int regionid, int fast)
{
+ if ((entry != NULL && regionid >= 0) || (entry == NULL && regionid != 0))
+ return -1;
+
for (; il > 0; il--, pe++) {
struct indexEntry_s ie;
- rpm_tagtype_t type;
ie.info.tag = ntohl(pe->tag);
ie.info.type = ntohl(pe->type);
if (dataEnd && (unsigned char *)ie.data >= dataEnd)
return -1;
- ie.length = dataLength(ie.info.type, ie.data, ie.info.count, 1, dataEnd);
+ if (fast && il > 1) {
+ ie.length = ntohl(pe[1].offset) - ie.info.offset;
+ } else {
+ ie.length = dataLength(ie.info.type, ie.data, ie.info.count,
+ 1, dataEnd);
+ }
if (ie.length < 0 || hdrchkData(ie.length))
return -1;
}
/* Alignment */
- type = ie.info.type;
- if (typeSizes[type] > 1) {
- unsigned diff = typeSizes[type] - (dl % typeSizes[type]);
- if (diff != typeSizes[type]) {
- dl += diff;
- }
- }
+ dl += alignDiff(ie.info.type, dl);
/* Perform endian conversions */
switch (ntohl(pe->type)) {
return dl;
}
-/** \ingroup header
- * doHeaderUnload.
- * @param h header
- * @retval *lengthPtr no. bytes in unloaded header blob
- * @return unloaded header blob (NULL on error)
- */
-static void * doHeaderUnload(Header h, size_t * lengthPtr)
+void * headerExport(Header h, unsigned int *bsize)
{
int32_t * ei = NULL;
entryInfo pe;
char * dataStart;
char * te;
- unsigned len;
+ unsigned len, diff;
int32_t il = 0;
int32_t dl = 0;
indexEntry entry;
- rpm_tagtype_t type;
int i;
int drlen, ndribbles;
continue;
/* Alignment */
- type = entry->info.type;
- if (typeSizes[type] > 1) {
- unsigned diff = typeSizes[type] - (dl % typeSizes[type]);
- if (diff != typeSizes[type]) {
- drlen += diff;
- dl += diff;
- }
+ diff = alignDiff(entry->info.type, dl);
+ if (diff) {
+ drlen += diff;
+ dl += diff;
}
ndribbles++;
continue;
/* Alignment */
- type = entry->info.type;
- if (typeSizes[type] > 1) {
- unsigned diff = typeSizes[type] - (dl % typeSizes[type]);
- if (diff != typeSizes[type]) {
- dl += diff;
- }
- }
+ dl += alignDiff(entry->info.type, dl);
il++;
dl += entry->length;
unsigned char *t;
int count;
int rdlen;
+ unsigned int diff;
if (entry->data == NULL || entry->length <= 0)
continue;
ril++;
rdlen += entry->info.count;
- count = regionSwab(NULL, ril, 0, pe, t, NULL, 0);
+ count = regionSwab(NULL, ril, 0, pe, t, NULL, 0, 0);
if (count != rdlen)
goto errxit;
}
te += entry->info.count + drlen;
- count = regionSwab(NULL, ril, 0, pe, t, NULL, 0);
+ count = regionSwab(NULL, ril, 0, pe, t, NULL, 0, 0);
if (count != (rdlen + entry->info.count + drlen))
goto errxit;
}
continue;
/* Alignment */
- type = entry->info.type;
- if (typeSizes[type] > 1) {
- unsigned diff;
- diff = typeSizes[type] - ((te - dataStart) % typeSizes[type]);
- if (diff != typeSizes[type]) {
- memset(te, 0, diff);
- te += diff;
- }
+ diff = alignDiff(entry->info.type, (te - dataStart));
+ if (diff) {
+ memset(te, 0, diff);
+ te += diff;
}
pe->offset = htonl(te - dataStart);
if ((((char *)ei)+len) != te)
goto errxit;
- if (lengthPtr)
- *lengthPtr = len;
+ if (bsize)
+ *bsize = len;
headerSort(h);
void * headerUnload(Header h)
{
- return doHeaderUnload(h, NULL);
+ return headerExport(h, NULL);
}
/**
return 0;
}
-Header headerLoad(void * uh)
+Header headerImport(void * blob, unsigned int bsize, headerImportFlags flags)
{
- int32_t * ei = (int32_t *) uh;
+ const int32_t * ei = (int32_t *) blob;
int32_t il = ntohl(ei[0]); /* index length */
int32_t dl = ntohl(ei[1]); /* data length */
- size_t pvlen = sizeof(il) + sizeof(dl) +
- (il * sizeof(struct entryInfo_s)) + dl;
- void * pv = uh;
+ unsigned int pvlen = sizeof(il) + sizeof(dl) +
+ (il * sizeof(struct entryInfo_s)) + dl;;
Header h = NULL;
entryInfo pe;
unsigned char * dataStart;
unsigned char * dataEnd;
indexEntry entry;
int rdlen;
+ int fast = (flags & HEADERIMPORT_FAST);
/* Sanity checks on header intro. */
- if (hdrchkTags(il) || hdrchkData(dl))
+ if (bsize && bsize != pvlen)
goto errxit;
+ if (hdrchkTags(il) || hdrchkData(dl) || pvlen >= headerMaxbytes)
+ goto errxit;
+
+ h = headerCreate(blob, (flags & HEADERIMPORT_COPY) ? pvlen : 0, il);
- ei = (int32_t *) pv;
+ ei = h->blob; /* In case we had to copy */
pe = (entryInfo) &ei[2];
dataStart = (unsigned char *) (pe + il);
dataEnd = dataStart + dl;
- h = headerCreate(uh, il);
-
entry = h->index;
if (!(htonl(pe->tag) < RPMTAG_HEADERI18NTABLE)) {
h->flags |= HEADERFLAG_LEGACY;
entry->data = pe;
entry->length = pvlen - sizeof(il) - sizeof(dl);
- rdlen = regionSwab(entry+1, il, 0, pe, dataStart, dataEnd, entry->info.offset);
+ rdlen = regionSwab(entry+1, il, 0, pe,
+ dataStart, dataEnd, entry->info.offset, fast);
if (rdlen != dl)
goto errxit;
entry->rdlen = rdlen;
entry->info.type = htonl(pe->type);
entry->info.count = htonl(pe->count);
+ entry->info.tag = htonl(pe->tag);
- if (hdrchkType(entry->info.type))
+ if (!ENTRY_IS_REGION(entry))
+ goto errxit;
+ if (entry->info.type != REGION_TAG_TYPE)
goto errxit;
- if (hdrchkTags(entry->info.count))
+ if (entry->info.count != REGION_TAG_COUNT)
goto errxit;
{ int off = ntohl(pe->offset);
- if (hdrchkData(off))
- goto errxit;
if (off) {
size_t nb = REGION_TAG_COUNT;
int32_t stei[nb];
+ if (hdrchkRange(dl, (off + nb)))
+ goto errxit;
/* XXX Hmm, why the copy? */
memcpy(&stei, dataStart + off, nb);
rdl = -ntohl(stei[2]); /* negative offset */
ril = rdl/sizeof(*pe);
if (hdrchkTags(ril) || hdrchkData(rdl))
goto errxit;
- entry->info.tag = htonl(pe->tag);
} else {
ril = il;
rdl = (ril * sizeof(struct entryInfo_s));
entry->data = pe;
entry->length = pvlen - sizeof(il) - sizeof(dl);
- rdlen = regionSwab(entry+1, ril-1, 0, pe+1, dataStart, dataEnd, entry->info.offset);
+ rdlen = regionSwab(entry+1, ril-1, 0, pe+1,
+ dataStart, dataEnd, entry->info.offset, fast);
if (rdlen < 0)
goto errxit;
entry->rdlen = rdlen;
indexEntry newEntry = entry + ril;
int ne = (h->indexUsed - ril);
int rid = entry->info.offset+1;
- int rc;
/* Load dribble entries from region. */
- rc = regionSwab(newEntry, ne, 0, pe+ril, dataStart, dataEnd, rid);
- if (rc < 0)
+ rdlen = regionSwab(newEntry, ne, rdlen, pe+ril,
+ dataStart, dataEnd, rid, fast);
+ if (rdlen < 0)
goto errxit;
- rdlen += rc;
{ indexEntry firstEntry = newEntry;
int save = h->indexUsed;
h->indexUsed += ne;
}
}
+
+ rdlen += REGION_TAG_COUNT;
+
+ if (rdlen != dl)
+ goto errxit;
}
h->flags &= ~HEADERFLAG_SORTED;
errxit:
if (h) {
+ if (flags & HEADERIMPORT_COPY)
+ free(h->blob);
free(h->index);
free(h);
}
Header headerReload(Header h, rpmTagVal tag)
{
Header nh;
- void * uh = doHeaderUnload(h, NULL);
+ unsigned int uc = 0;
+ void * uh = headerExport(h, &uc);
h = headerFree(h);
if (uh == NULL)
return NULL;
- nh = headerLoad(uh);
+ nh = headerImport(uh, uc, 0);
if (nh == NULL) {
uh = _free(uh);
return NULL;
return nh;
}
-Header headerCopyLoad(const void * uh)
+Header headerLoad(void * uh)
{
- int32_t * ei = (int32_t *) uh;
- int32_t il = ntohl(ei[0]); /* index length */
- int32_t dl = ntohl(ei[1]); /* data length */
- size_t pvlen = sizeof(il) + sizeof(dl) +
- (il * sizeof(struct entryInfo_s)) + dl;
- Header h = NULL;
+ return headerImport(uh, 0, 0);
+}
- /* Sanity checks on header intro. */
- if (!(hdrchkTags(il) || hdrchkData(dl)) && pvlen < headerMaxbytes) {
- void * nuh = memcpy(xmalloc(pvlen), uh, pvlen);
- if ((h = headerLoad(nuh)) == NULL)
- free(nuh);
- }
- return h;
+Header headerCopyLoad(const void * uh)
+{
+ /* Discards const but that's ok as we'll take a copy */
+ return headerImport((void *)uh, 0, HEADERIMPORT_COPY);
}
Header headerRead(FD_t fd, int magicp)
int32_t il;
int32_t dl;
Header h = NULL;
- size_t len;
+ unsigned int len, blen;
if (magicp == HEADER_MAGIC_YES) {
int32_t magic;
- if (Fread(block, 1, 4*sizeof(*block), fd) != 4*sizeof(*block))
+ if (Freadall(fd, block, 4*sizeof(*block)) != 4*sizeof(*block))
goto exit;
magic = block[0];
il = ntohl(block[2]);
dl = ntohl(block[3]);
} else {
- if (Fread(block, 1, 2*sizeof(*block), fd) != 2*sizeof(*block))
+ if (Freadall(fd, block, 2*sizeof(*block)) != 2*sizeof(*block))
goto exit;
il = ntohl(block[0]);
dl = ntohl(block[1]);
}
- len = sizeof(il) + sizeof(dl) + (il * sizeof(struct entryInfo_s)) + dl;
+ blen = (il * sizeof(struct entryInfo_s)) + dl;
+ len = sizeof(il) + sizeof(dl) + blen;
/* Sanity checks on header intro. */
if (hdrchkTags(il) || hdrchkData(dl) || len > headerMaxbytes)
ei = xmalloc(len);
ei[0] = htonl(il);
ei[1] = htonl(dl);
- len -= sizeof(il) + sizeof(dl);
- if (Fread((char *)&ei[2], 1, len, fd) != len)
+ if (Freadall(fd, (char *)&ei[2], blen) != blen)
goto exit;
- h = headerLoad(ei);
+ h = headerImport(ei, len, 0);
exit:
if (h == NULL && ei != NULL) {
int headerWrite(FD_t fd, Header h, int magicp)
{
ssize_t nb;
- size_t length;
+ unsigned int length;
void * uh;
- uh = doHeaderUnload(h, &length);
+ uh = headerExport(h, &length);
if (uh == NULL)
return 1;
switch (magicp) {
dataStart = (unsigned char *) memcpy(pe + ril, dataStart, rdl);
- rc = regionSwab(NULL, ril, 0, pe, dataStart, dataStart + rdl, 0);
+ rc = regionSwab(NULL, ril, 0, pe, dataStart, dataStart + rdl, 0, 0);
/* don't return data on failure */
if (rc < 0) {
td->data = _free(td->data);
}
}
+ if (rc == 0)
+ td->flags |= RPMTD_INVALID;
+
/* XXX 1 on success */
return ((rc == 1) ? 1 : 0);
}
{
indexEntry entry;
rpm_data_t data;
- int length;
+ int length = 0;
/* Count must always be >= 1 for headerAddEntry. */
if (td->count <= 0)
if (hdrchkData(td->count))
return 0;
- length = 0;
data = grabData(td->type, td->data, td->count, &length);
- if (data == NULL || length <= 0)
+ if (data == NULL)
return 0;
/* Allocate more index space if necessary */
indexEntry entry;
rpm_data_t oldData;
rpm_data_t data;
- int length;
+ int length = 0;
/* First find the tag */
entry = findEntry(h, td->tag, td->type);
if (!entry)
return 0;
- length = 0;
data = grabData(td->type, td->data, td->count, &length);
- if (data == NULL || length <= 0)
+ if (data == NULL)
return 0;
/* make sure entry points to the first occurence of this tag */
h->instance = instance;
}
+#define RETRY_ERROR(_err) \
+ ((_err) == EINTR || (_err) == EAGAIN || (_err) == EWOULDBLOCK)
+
+ssize_t Freadall(FD_t fd, void * buf, ssize_t size)
+{
+ ssize_t total = 0;
+ ssize_t nb = 0;
+ char * bufp = buf;
+
+ while (total < size) {
+ nb = Fread(bufp, 1, size - total, fd);
+
+ if (nb == 0 || (nb < 0 && !RETRY_ERROR(errno))) {
+ total = nb;
+ break;
+ }
+
+ if (nb > 0) {
+ bufp += nb;
+ total += nb;
+ }
+ }
+
+ return total;
+}
+