Blame lib/backend/ndb/rpmxdb.c

2ff057
#define _GNU_SOURCE
2ff057
2ff057
#include "system.h"
2ff057
2ff057
#include <rpm/rpmlog.h>
2ff057
2ff057
#include <sys/types.h>
2ff057
#include <sys/stat.h>
2ff057
#include <sys/file.h>
2ff057
#include <fcntl.h>
2ff057
#include <stdio.h>
2ff057
#include <time.h>
2ff057
#include <unistd.h>
2ff057
#include <string.h>
2ff057
#include <stdlib.h>
2ff057
#include <sys/mman.h>
2ff057
#include <endian.h>
2ff057
#include <libgen.h>
2ff057
2ff057
#include "rpmxdb.h"
2ff057
2ff057
#define RPMRC_OK 0
2ff057
#define RPMRC_NOTFOUND 1
2ff057
#define RPMRC_FAIL 2
2ff057
2ff057
typedef struct rpmxdb_s {
2ff057
    rpmpkgdb pkgdb;             /* master database */
2ff057
    char *filename;
2ff057
    int fd;
2ff057
    int flags;
2ff057
    int mode;
2ff057
    int rdonly;
2ff057
    unsigned int pagesize;
2ff057
    unsigned int generation;
2ff057
    unsigned int slotnpages;
2ff057
    unsigned int usergeneration;
2ff057
2ff057
    unsigned char *mapped;
2ff057
    unsigned int mappedlen;
2ff057
2ff057
    struct xdb_slot {
2ff057
	unsigned int slotno;
2ff057
	unsigned int blobtag;
2ff057
	unsigned int subtag;
2ff057
	unsigned char *mapped;
2ff057
	int mapflags;
2ff057
	unsigned int startpage;
2ff057
	unsigned int pagecnt;
2ff057
	void (*mapcallback)(rpmxdb xdb, void *data, void *newaddr, size_t newsize);
2ff057
	void *mapcallbackdata;
2ff057
	unsigned int next;
2ff057
	unsigned int prev;
2ff057
    } *slots;
2ff057
    unsigned int nslots;
2ff057
    unsigned int firstfree;
2ff057
    unsigned int usedblobpages;
2ff057
    unsigned int systempagesize;
2ff057
    int dofsync;
2ff057
} *rpmxdb;
2ff057
2ff057
2ff057
static inline void h2le(unsigned int x, unsigned char *p)
2ff057
{
2ff057
    p[0] = x;
2ff057
    p[1] = x >> 8;
2ff057
    p[2] = x >> 16;
2ff057
    p[3] = x >> 24;
2ff057
}
2ff057
2ff057
/* aligned versions */
2ff057
static inline unsigned int le2ha(unsigned char *p)
2ff057
{
2ff057
    unsigned int x = *(unsigned int *)p;
2ff057
    return le32toh(x);
2ff057
}
2ff057
2ff057
static inline void h2lea(unsigned int x, unsigned char *p)
2ff057
{
2ff057
    *(unsigned int *)p = htole32(x);
2ff057
}
2ff057
2ff057
2ff057
#define XDB_MAGIC     ('R' | 'p' << 8 | 'm' << 16 | 'X' << 24)
2ff057
#define XDB_VERSION	0
2ff057
2ff057
#define XDB_OFFSET_MAGIC	0
2ff057
#define XDB_OFFSET_VERSION	4
2ff057
#define XDB_OFFSET_GENERATION	8
2ff057
#define XDB_OFFSET_SLOTNPAGES	12
2ff057
#define XDB_OFFSET_PAGESIZE	16
2ff057
#define XDB_OFFSET_USERGENERATION	20
2ff057
2ff057
/* must be multiple of SLOT_SIZE */
2ff057
#define XDB_HEADER_SIZE		32
2ff057
2ff057
#define SLOT_MAGIC     ('S' | 'l' << 8 | 'o' << 16)
2ff057
2ff057
#define SLOT_SIZE 16
2ff057
#define SLOT_START (XDB_HEADER_SIZE / SLOT_SIZE)
2ff057
2ff057
static void rpmxdbUnmap(rpmxdb xdb)
2ff057
{
2ff057
    munmap(xdb->mapped, xdb->mappedlen);
2ff057
    xdb->mapped = 0;
2ff057
    xdb->mappedlen = 0;
2ff057
}
2ff057
2ff057
/* slot mapping functions */
2ff057
static int mapslot(rpmxdb xdb, struct xdb_slot *slot)
2ff057
{
2ff057
    void *mapped;
2ff057
    size_t off, size, shift;
2ff057
2ff057
    if (slot->mapped)
2ff057
	return RPMRC_FAIL;
2ff057
    size = slot->pagecnt * xdb->pagesize;
2ff057
    off = slot->startpage * xdb->pagesize;
2ff057
    shift = 0;
2ff057
    if (xdb->pagesize != xdb->systempagesize) {
2ff057
	shift = off & (xdb->systempagesize - 1);
2ff057
	off -= shift;
2ff057
	size += shift;
2ff057
	size = (size + xdb->systempagesize - 1) & ~(xdb->systempagesize - 1);
2ff057
    }
2ff057
    mapped = mmap(0, size, slot->mapflags, MAP_SHARED, xdb->fd, off);
2ff057
    if (mapped == MAP_FAILED)
2ff057
	return RPMRC_FAIL;
2ff057
    slot->mapped = (unsigned char *)mapped + shift;
2ff057
    return RPMRC_OK;
2ff057
}
2ff057
2ff057
static void unmapslot(rpmxdb xdb, struct xdb_slot *slot)
2ff057
{
2ff057
    size_t size;
2ff057
    unsigned char *mapped = slot->mapped;
2ff057
    if (!mapped)
2ff057
	return;
2ff057
    size = slot->pagecnt * xdb->pagesize;
2ff057
    if (xdb->pagesize != xdb->systempagesize) {
2ff057
	size_t off = slot->startpage * xdb->pagesize;
2ff057
	size_t shift = off & (xdb->systempagesize - 1);
2ff057
	mapped -= shift;
2ff057
	size += shift;
2ff057
	size = (size + xdb->systempagesize - 1) & ~(xdb->systempagesize - 1);
2ff057
    }
2ff057
    munmap(mapped, size);
2ff057
    slot->mapped = 0;
2ff057
}
2ff057
2ff057
static int remapslot(rpmxdb xdb, struct xdb_slot *slot, unsigned int newpagecnt)
2ff057
{
2ff057
    void *mapped;
2ff057
    size_t off, oldsize, newsize, shift;
2ff057
    oldsize = slot->pagecnt * xdb->pagesize;
2ff057
    newsize = newpagecnt * xdb->pagesize;
2ff057
    off = slot->startpage * xdb->pagesize;
2ff057
    shift = 0;
2ff057
    if (xdb->pagesize != xdb->systempagesize) {
2ff057
	off = slot->startpage * xdb->pagesize;
2ff057
	shift = off & (xdb->systempagesize - 1);
2ff057
	off -= shift;
2ff057
	oldsize += shift;
2ff057
	oldsize = (oldsize + xdb->systempagesize - 1) & ~(xdb->systempagesize - 1);
2ff057
	newsize += shift;
2ff057
	newsize = (newsize + xdb->systempagesize - 1) & ~(xdb->systempagesize - 1);
2ff057
    }
2ff057
    if (slot->mapped)
2ff057
	mapped = mremap(slot->mapped - shift, oldsize, newsize, MREMAP_MAYMOVE);
2ff057
    else
2ff057
	mapped = mmap(0, newsize, slot->mapflags, MAP_SHARED, xdb->fd, off);
2ff057
    if (mapped == MAP_FAILED)
2ff057
	return RPMRC_FAIL;
2ff057
    slot->mapped = (unsigned char *)mapped + shift;
2ff057
    slot->pagecnt = newpagecnt;
2ff057
    return RPMRC_OK;
2ff057
}
2ff057
2ff057
2ff057
static int usedslots_cmp(const void *a, const void *b)
2ff057
{
2ff057
    struct xdb_slot *sa = *(struct xdb_slot **)a;
2ff057
    struct xdb_slot *sb = *(struct xdb_slot **)b;
2ff057
    if (sa->startpage == sb->startpage) {
2ff057
      return sa->pagecnt > sb->pagecnt ? 1 : sa->pagecnt < sb->pagecnt ? -1 : 0;
2ff057
    }
2ff057
    return sa->startpage > sb->startpage ? 1 : -1;
2ff057
}
2ff057
2ff057
static int rpmxdbReadHeader(rpmxdb xdb)
2ff057
{
2ff057
    struct xdb_slot *slot;
2ff057
    unsigned int header[XDB_HEADER_SIZE / sizeof(unsigned int)];
2ff057
    unsigned int slotnpages, pagesize, generation, usergeneration, version;
2ff057
    unsigned int page, *lastfreep;
2ff057
    unsigned char *pageptr;
2ff057
    struct xdb_slot *slots, **usedslots, *lastslot;
2ff057
    unsigned int nslots;
2ff057
    unsigned int usedblobpages;
2ff057
    int i, nused, slotno;
2ff057
    struct stat stb;
2ff057
    size_t mapsize;
2ff057
2ff057
    if (xdb->mapped) {
2ff057
	if (le2ha(xdb->mapped + XDB_OFFSET_GENERATION) == xdb->generation) {
2ff057
	    return RPMRC_OK;
2ff057
	}
2ff057
	rpmxdbUnmap(xdb);
2ff057
    }
2ff057
    if (fstat(xdb->fd, &stb)) {
2ff057
        return RPMRC_FAIL;
2ff057
    }
2ff057
    if (pread(xdb->fd, header, sizeof(header), 0) != sizeof(header)) {
2ff057
	return RPMRC_FAIL;
2ff057
    }
2ff057
    if (le2ha((unsigned char *)header + XDB_OFFSET_MAGIC) != XDB_MAGIC)
2ff057
	return RPMRC_FAIL;
2ff057
    version = le2ha((unsigned char *)header + XDB_OFFSET_VERSION);
2ff057
    if (version != XDB_VERSION) {
2ff057
	rpmlog(RPMLOG_ERR, _("rpmxdb: Version mismatch. Expected version: %u. "
2ff057
	    "Found version: %u\n"), XDB_VERSION, version);
2ff057
	return RPMRC_FAIL;
2ff057
    }
2ff057
2ff057
    generation = le2ha((unsigned char *)header + XDB_OFFSET_GENERATION);
2ff057
    slotnpages = le2ha((unsigned char *)header + XDB_OFFSET_SLOTNPAGES);
2ff057
    pagesize = le2ha((unsigned char *)header + XDB_OFFSET_PAGESIZE);
2ff057
    usergeneration = le2ha((unsigned char *)header + XDB_OFFSET_USERGENERATION);
2ff057
    if (!slotnpages || !pagesize || stb.st_size % pagesize != 0)
2ff057
	return RPMRC_FAIL;
2ff057
    xdb->pagesize = pagesize;
2ff057
2ff057
    /* round up */
2ff057
    mapsize = slotnpages * pagesize;
2ff057
    mapsize = (mapsize + xdb->systempagesize - 1) & ~(xdb->systempagesize - 1);
2ff057
    xdb->mapped = mmap(0, mapsize, xdb->rdonly ? PROT_READ : PROT_READ | PROT_WRITE, MAP_SHARED, xdb->fd, 0);
2ff057
    if ((void *)xdb->mapped == MAP_FAILED) {
2ff057
	xdb->mapped = 0;
2ff057
	return RPMRC_FAIL;
2ff057
    }
2ff057
    xdb->mappedlen = mapsize;
2ff057
2ff057
    /* read in all slots */
2ff057
    xdb->firstfree = 0;
2ff057
    nslots = slotnpages * (pagesize / SLOT_SIZE) - SLOT_START + 1;
2ff057
    slots = xcalloc(nslots + 1, sizeof(struct xdb_slot));
2ff057
    usedslots = xcalloc(nslots + 1, sizeof(int));
2ff057
    nused = 0;
2ff057
    slotno = 1;
2ff057
    slot = slots + 1;
2ff057
    usedblobpages = 0;
2ff057
    lastfreep = &xdb->firstfree;
2ff057
    for (page = 0, pageptr = xdb->mapped; page < slotnpages; page++, pageptr += pagesize) {
2ff057
	unsigned int o;
2ff057
	for (o = page ? 0 : SLOT_START * SLOT_SIZE; o < pagesize; o += SLOT_SIZE, slotno++, slot++) {
2ff057
	    unsigned char *pp = pageptr + o;
2ff057
	    slot->slotno = slotno;
2ff057
	    slot->subtag = le2ha(pp);
2ff057
	    if ((slot->subtag & 0x00ffffff) != SLOT_MAGIC) {
2ff057
		free(slots);
2ff057
		free(usedslots);
2ff057
		rpmxdbUnmap(xdb);
2ff057
		return RPMRC_FAIL;
2ff057
	    }
2ff057
	    slot->subtag = (slot->subtag >> 24) & 255;
2ff057
	    slot->blobtag = le2ha(pp + 4);
2ff057
	    slot->startpage = le2ha(pp + 8);
2ff057
	    slot->pagecnt = le2ha(pp + 12);
2ff057
	    if (slot->pagecnt == 0 && slot->startpage)	/* empty but used slot? */
2ff057
		slot->startpage = slotnpages;
2ff057
	    if (!slot->startpage) {
2ff057
		*lastfreep = slotno;
2ff057
		lastfreep = &slot->next;
2ff057
	    } else {
2ff057
		usedslots[nused++] = slot;
2ff057
		usedblobpages += slot->pagecnt;
2ff057
	    }
2ff057
	}
2ff057
    }
2ff057
    if (nused > 1) {
2ff057
	qsort(usedslots, nused, sizeof(*usedslots), usedslots_cmp);
2ff057
    }
2ff057
    /* now chain em */
2ff057
    slots[0].pagecnt = slotnpages;
2ff057
    lastslot = slots;
2ff057
    for (i = 0; i < nused; i++, lastslot = slot) {
2ff057
	slot = usedslots[i];
2ff057
	if (lastslot->startpage + lastslot->pagecnt > slot->startpage) {
2ff057
	    free(slots);
2ff057
	    free(usedslots);
2ff057
	    rpmxdbUnmap(xdb);
2ff057
	    return RPMRC_FAIL;
2ff057
	}
2ff057
	lastslot->next = slot->slotno;
2ff057
	slot->prev = lastslot->slotno;
2ff057
    }
2ff057
    lastslot->next = nslots;
2ff057
    slots[nslots].slotno = nslots;
2ff057
    slots[nslots].prev = lastslot->slotno;
2ff057
    slots[nslots].startpage = stb.st_size / pagesize;
2ff057
    free(usedslots);
2ff057
    /* now sync with the old slot data */
2ff057
    if (xdb->slots) {
2ff057
	for (i = 1, slot = xdb->slots + i; i < xdb->nslots; i++, slot++) {
2ff057
	    if (slot->startpage && (slot->mapped || slot->mapcallback)) {
2ff057
		struct xdb_slot *nslot;
2ff057
		if (i >= nslots || !slots[i].startpage || slots[i].blobtag != slot->blobtag || slots[i].subtag != slot->subtag) {
2ff057
		    /* slot is gone */
2ff057
		    if (slot->mapped) {
2ff057
			unmapslot(xdb, slot);
2ff057
			slot->mapcallback(xdb, slot->mapcallbackdata, 0, 0);
2ff057
		    }
2ff057
		    continue;
2ff057
		}
2ff057
		nslot = slots + i;
2ff057
		if (slot->mapcallback) {
2ff057
		    nslot->mapflags = slot->mapflags;
2ff057
		    nslot->mapcallback = slot->mapcallback;
2ff057
		    nslot->mapcallbackdata = slot->mapcallbackdata;
2ff057
		}
2ff057
		if (slot->startpage != nslot->startpage || slot->pagecnt != nslot->pagecnt) {
2ff057
		    /* slot moved or was resized */
2ff057
		    if (slot->mapped)
2ff057
			unmapslot(xdb, slot);
2ff057
		    if (nslot->mapcallback) {
2ff057
			if (nslot->pagecnt) {
2ff057
			    mapslot(xdb, nslot);
2ff057
			    nslot->mapcallback(xdb, nslot->mapcallbackdata, nslot->mapped, nslot->mapped ? nslot->pagecnt * xdb->pagesize : 0);
2ff057
			} else {
2ff057
			    nslot->mapcallback(xdb, nslot->mapcallbackdata, 0, 0);
2ff057
			}
2ff057
		    }
2ff057
		}
2ff057
	    }
2ff057
	}
2ff057
	free(xdb->slots);
2ff057
    }
2ff057
    xdb->slots = slots;
2ff057
    xdb->nslots = nslots;
2ff057
    xdb->generation = generation;
2ff057
    xdb->slotnpages = slotnpages;
2ff057
    xdb->usergeneration = usergeneration;
2ff057
    xdb->usedblobpages = usedblobpages;
2ff057
    return RPMRC_OK;
2ff057
}
2ff057
2ff057
static int rpmxdbWriteHeader(rpmxdb xdb)
2ff057
{
2ff057
    if (!xdb->mapped)
2ff057
	return RPMRC_FAIL;
2ff057
    h2lea(XDB_MAGIC, xdb->mapped + XDB_OFFSET_MAGIC);
2ff057
    h2lea(XDB_VERSION, xdb->mapped + XDB_OFFSET_VERSION);
2ff057
    h2lea(xdb->generation, xdb->mapped + XDB_OFFSET_GENERATION);
2ff057
    h2lea(xdb->slotnpages, xdb->mapped + XDB_OFFSET_SLOTNPAGES);
2ff057
    h2lea(xdb->pagesize, xdb->mapped + XDB_OFFSET_PAGESIZE);
2ff057
    h2lea(xdb->usergeneration, xdb->mapped + XDB_OFFSET_USERGENERATION);
2ff057
    return RPMRC_OK;
2ff057
}
2ff057
2ff057
static void rpmxdbUpdateSlot(rpmxdb xdb, struct xdb_slot *slot)
2ff057
{
2ff057
    unsigned char *pp = xdb->mapped + (SLOT_START - 1 + slot->slotno) * SLOT_SIZE;
2ff057
    h2lea(SLOT_MAGIC | (slot->subtag << 24), pp);
2ff057
    h2lea(slot->blobtag, pp + 4);
2ff057
    if (slot->pagecnt || !slot->startpage)
2ff057
	h2lea(slot->startpage, pp + 8);
2ff057
    else
2ff057
	h2lea(1, pp + 8);	/* "empty but used" blobs always start at 1 */
2ff057
    h2lea(slot->pagecnt, pp + 12);
2ff057
    xdb->generation++;
2ff057
    h2lea(xdb->generation, xdb->mapped + XDB_OFFSET_GENERATION);
2ff057
}
2ff057
2ff057
static int rpmxdbWriteEmptyPages(rpmxdb xdb, unsigned int pageno, unsigned int count)
2ff057
{
2ff057
    unsigned char *page;
2ff057
    if (!count)
2ff057
	return RPMRC_OK;
2ff057
    page = xmalloc(xdb->pagesize);
2ff057
    memset(page, 0, xdb->pagesize);
2ff057
    for (; count; count--, pageno++) {
2ff057
	if (pwrite(xdb->fd, page, xdb->pagesize, pageno * xdb->pagesize) != xdb->pagesize) {
2ff057
	    free(page);
2ff057
	    return RPMRC_FAIL;
2ff057
	}
2ff057
    }
2ff057
    free(page);
2ff057
    return RPMRC_OK;
2ff057
}
2ff057
2ff057
static int rpmxdbWriteEmptySlotpage(rpmxdb xdb, int pageno)
2ff057
{
2ff057
    unsigned char *page;
2ff057
    int i, spp;
2ff057
    page = xmalloc(xdb->pagesize);
2ff057
    memset(page, 0, xdb->pagesize);
2ff057
    spp = xdb->pagesize / SLOT_SIZE;	/* slots per page */
2ff057
    for (i = pageno ? 0 : SLOT_START; i < spp; i++)
2ff057
        h2le(SLOT_MAGIC, page + i * SLOT_SIZE);
2ff057
    if (!pageno) {
2ff057
	/* only used when called from InitInternal */
2ff057
	if (xdb->mapped) {
2ff057
	    free(page);
2ff057
	    return RPMRC_FAIL;
2ff057
	}
2ff057
	xdb->mapped = page;
2ff057
	rpmxdbWriteHeader(xdb);
2ff057
	xdb->mapped = 0;
2ff057
    }
2ff057
    if (pwrite(xdb->fd, page, xdb->pagesize, pageno * xdb->pagesize) != xdb->pagesize) {
2ff057
	free(page);
2ff057
	return RPMRC_FAIL;
2ff057
    }
2ff057
    free(page);
2ff057
    return RPMRC_OK;
2ff057
}
2ff057
2ff057
static int rpmxdbInitInternal(rpmxdb xdb)
2ff057
{
2ff057
    struct stat stb;
2ff057
    if (fstat(xdb->fd, &stb)) {
2ff057
        return RPMRC_FAIL;
2ff057
    }
2ff057
    if (stb.st_size == 0) {
2ff057
        xdb->slotnpages = 1;
2ff057
        xdb->generation++;
2ff057
	xdb->pagesize = sysconf(_SC_PAGE_SIZE);
2ff057
        if (rpmxdbWriteEmptySlotpage(xdb, 0)) {
2ff057
            return RPMRC_FAIL;
2ff057
        }
2ff057
    }
2ff057
    return RPMRC_OK;
2ff057
}
2ff057
2ff057
/* we use the master pdb for locking */
2ff057
static int rpmxdbLockOnly(rpmxdb xdb, int excl)
2ff057
{
2ff057
    if (excl && xdb->rdonly)
2ff057
        return RPMRC_FAIL;
2ff057
    return rpmpkgLock(xdb->pkgdb, excl);
2ff057
}
2ff057
2ff057
/* this is the same as rpmxdbLockReadHeader. It does the
2ff057
 * ReadHeader to sync the mappings if xdb moved some blobs.
2ff057
 */
2ff057
int rpmxdbLock(rpmxdb xdb, int excl)
2ff057
{
2ff057
    if (rpmxdbLockOnly(xdb, excl))
2ff057
	return RPMRC_FAIL;
2ff057
    if (rpmxdbReadHeader(xdb)) {
2ff057
	rpmxdbUnlock(xdb, excl);
2ff057
        return RPMRC_FAIL;
2ff057
    }
2ff057
    return RPMRC_OK;
2ff057
}
2ff057
2ff057
int rpmxdbUnlock(rpmxdb xdb, int excl)
2ff057
{
2ff057
    return rpmpkgUnlock(xdb->pkgdb, excl);
2ff057
}
2ff057
2ff057
static int rpmxdbLockReadHeader(rpmxdb xdb, int excl)
2ff057
{
2ff057
    if (rpmxdbLockOnly(xdb, excl))
2ff057
	return RPMRC_FAIL;
2ff057
    if (rpmxdbReadHeader(xdb)) {
2ff057
	rpmxdbUnlock(xdb, excl);
2ff057
        return RPMRC_FAIL;
2ff057
    }
2ff057
    return RPMRC_OK;
2ff057
}
2ff057
2ff057
static int rpmxdbInit(rpmxdb xdb)
2ff057
{
2ff057
    int rc;
2ff057
2ff057
    if (rpmxdbLockOnly(xdb, 1))
2ff057
        return RPMRC_FAIL;
2ff057
    rc = rpmxdbInitInternal(xdb);
2ff057
    rpmxdbUnlock(xdb, 1);
2ff057
    return rc;
2ff057
}
2ff057
2ff057
int rpmxdbOpen(rpmxdb *xdbp, rpmpkgdb pkgdb, const char *filename, int flags, int mode)
2ff057
{
2ff057
    struct stat stb;
2ff057
    rpmxdb xdb;
2ff057
2ff057
    *xdbp = 0;
2ff057
    xdb = xcalloc(1, sizeof(*xdb));
2ff057
    xdb->pkgdb = pkgdb;
2ff057
    xdb->filename = xstrdup(filename);
2ff057
    xdb->systempagesize = sysconf(_SC_PAGE_SIZE);
2ff057
    if ((flags & (O_RDONLY|O_RDWR)) == O_RDONLY)
2ff057
	xdb->rdonly = 1;
2ff057
    if ((xdb->fd = open(filename, flags, mode)) == -1) {
2ff057
	free(xdb->filename);
2ff057
	free(xdb);
2ff057
	return RPMRC_FAIL;
2ff057
    }
2ff057
    if (flags & O_CREAT) {
2ff057
	char *filenameCopy;
2ff057
	DIR *pdir;
2ff057
2ff057
	filenameCopy = xstrdup(xdb->filename);
2ff057
2ff057
	if ((pdir = opendir(dirname(filenameCopy))) == NULL) {
2ff057
	    free(filenameCopy);
2ff057
	    close(xdb->fd);
2ff057
	    free(xdb->filename);
2ff057
	    free(xdb);
2ff057
	    return RPMRC_FAIL;
2ff057
	}
2ff057
2ff057
	if (fsync(dirfd(pdir)) == -1) {
2ff057
	    closedir(pdir);
2ff057
	    free(filenameCopy);
2ff057
	    close(xdb->fd);
2ff057
	    free(xdb->filename);
2ff057
	    free(xdb);
2ff057
	    return RPMRC_FAIL;
2ff057
	}
2ff057
	closedir(pdir);
2ff057
	free(filenameCopy);
2ff057
    }
2ff057
    if (fstat(xdb->fd, &stb)) {
2ff057
	close(xdb->fd);
2ff057
	free(xdb->filename);
2ff057
	free(xdb);
2ff057
	return RPMRC_FAIL;
2ff057
    }
2ff057
    if (stb.st_size == 0) {
2ff057
	if (rpmxdbInit(xdb)) {
2ff057
	    close(xdb->fd);
2ff057
	    free(xdb->filename);
2ff057
	    free(xdb);
2ff057
	    return RPMRC_FAIL;
2ff057
	}
2ff057
    }
2ff057
    xdb->flags = flags;
2ff057
    xdb->mode = mode;
2ff057
    xdb->dofsync = 1;
2ff057
    *xdbp = xdb;
2ff057
    return RPMRC_OK;
2ff057
}
2ff057
2ff057
void rpmxdbClose(rpmxdb xdb)
2ff057
{
2ff057
    struct xdb_slot *slot;
2ff057
    int i;
2ff057
2ff057
    for (i = 1, slot = xdb->slots + 1; i < xdb->nslots; i++, slot++) {
2ff057
	if (slot->mapped) {
2ff057
	    unmapslot(xdb, slot);
2ff057
	    slot->mapcallback(xdb, slot->mapcallbackdata, 0, 0);
2ff057
	}
2ff057
    }
2ff057
    if (xdb->slots)
2ff057
	free(xdb->slots);
2ff057
    if (xdb->fd >= 0)
2ff057
	close(xdb->fd);
2ff057
    if (xdb->filename)
2ff057
	free(xdb->filename);
2ff057
    free(xdb);
2ff057
}
2ff057
2ff057
/* moves the blob to a given new location (possibly resizeing) */
2ff057
static int moveblobto(rpmxdb xdb, struct xdb_slot *oldslot, struct xdb_slot *afterslot, unsigned int newpagecnt)
2ff057
{
2ff057
    struct xdb_slot *nextslot;
2ff057
    unsigned int newstartpage, oldpagecnt;
2ff057
    unsigned int tocopy;
2ff057
    int didmap;
2ff057
2ff057
    newstartpage = afterslot->startpage + afterslot->pagecnt;
2ff057
    nextslot = xdb->slots + afterslot->next;
2ff057
2ff057
    /* make sure there's enough room */
2ff057
    if (newpagecnt > nextslot->startpage - newstartpage)
2ff057
	return RPMRC_FAIL;
2ff057
2ff057
#if 0
2ff057
    printf("moveblobto %d %d %d %d, afterslot %d\n", oldslot->startpage, oldslot->pagecnt, newstartpage, newpagecnt, afterslot->slotno);
2ff057
#endif
2ff057
    /* map old content */
2ff057
    didmap = 0;
2ff057
    oldpagecnt = oldslot->pagecnt;
2ff057
    if (!oldslot->mapped && oldpagecnt) {
2ff057
	if (mapslot(xdb, oldslot))
2ff057
	    return RPMRC_FAIL;
2ff057
        didmap = 1;
2ff057
    }
2ff057
2ff057
    /* copy content */
2ff057
    tocopy = newpagecnt > oldpagecnt ? oldpagecnt : newpagecnt;
2ff057
    if (tocopy && pwrite(xdb->fd, oldslot->mapped, tocopy * xdb->pagesize, newstartpage * xdb->pagesize) != tocopy * xdb->pagesize) {
2ff057
	if (didmap)
2ff057
	    unmapslot(xdb, oldslot);
2ff057
	return RPMRC_FAIL;
2ff057
    }
2ff057
    /* zero out new pages */
2ff057
    if (newpagecnt > oldpagecnt) {
2ff057
	if (rpmxdbWriteEmptyPages(xdb, newstartpage + oldpagecnt, newpagecnt - oldpagecnt)) {
2ff057
	    if (didmap)
2ff057
		unmapslot(xdb, oldslot);
2ff057
	    return RPMRC_FAIL;
2ff057
	}
2ff057
    }
2ff057
2ff057
    if (oldslot->mapped)
2ff057
	unmapslot(xdb, oldslot);
2ff057
2ff057
    /* set new offset and position */
2ff057
    oldslot->startpage = newstartpage;
2ff057
    oldslot->pagecnt = newpagecnt;
2ff057
    rpmxdbUpdateSlot(xdb, oldslot);
2ff057
    xdb->usedblobpages -= oldpagecnt;
2ff057
    xdb->usedblobpages += newpagecnt;
2ff057
2ff057
    if (afterslot != oldslot && nextslot != oldslot) {
2ff057
	/* remove from old chain */
2ff057
	xdb->slots[oldslot->prev].next = oldslot->next;
2ff057
	xdb->slots[oldslot->next].prev = oldslot->prev;
2ff057
2ff057
	/* chain into new position, between lastslot and nextslot */
2ff057
	oldslot->prev = afterslot->slotno;
2ff057
	afterslot->next = oldslot->slotno;
2ff057
2ff057
	oldslot->next = nextslot->slotno;
2ff057
	nextslot->prev = oldslot->slotno;
2ff057
    }
2ff057
2ff057
    /* map again (if needed) */
2ff057
    if (oldslot->mapcallback) {
2ff057
	if (newpagecnt) {
2ff057
	    if (mapslot(xdb, oldslot))
2ff057
		oldslot->mapped = 0;	/* XXX: HELP, what can we do here? */
2ff057
	}
2ff057
	oldslot->mapcallback(xdb, oldslot->mapcallbackdata, oldslot->mapped, oldslot->mapped ? oldslot->pagecnt * xdb->pagesize : 0);
2ff057
    }
2ff057
    return RPMRC_OK;
2ff057
}
2ff057
2ff057
/* moves the blob to a new location (possibly resizeing) */
2ff057
static int moveblob(rpmxdb xdb, struct xdb_slot *oldslot, unsigned int newpagecnt)
2ff057
{
2ff057
    struct xdb_slot *slot, *lastslot;
2ff057
    unsigned int nslots;
2ff057
    unsigned int freecnt;
2ff057
    int i;
2ff057
2ff057
    nslots = xdb->nslots;
2ff057
    freecnt = 0;
2ff057
    lastslot = xdb->slots;
2ff057
    for (i = xdb->slots[0].next; ; lastslot = slot, i = slot->next) {
2ff057
	slot = xdb->slots + i;
2ff057
	freecnt = slot->startpage - (lastslot->startpage + lastslot->pagecnt);
2ff057
	if (freecnt >= newpagecnt)
2ff057
	    break;
2ff057
	if (i == nslots)
2ff057
	    break;
2ff057
    }
2ff057
    if (i == nslots && newpagecnt > freecnt) {
2ff057
	/* need to grow the file */
2ff057
	if (rpmxdbWriteEmptyPages(xdb, slot->startpage, newpagecnt - freecnt)) {
2ff057
	    return RPMRC_FAIL;
2ff057
	}
2ff057
	slot->startpage += newpagecnt - freecnt;
2ff057
    }
2ff057
    return moveblobto(xdb, oldslot, lastslot, newpagecnt);
2ff057
}
2ff057
2ff057
/* move the two blobs at the end of our file to the free area after the provided slot */
2ff057
static int moveblobstofront(rpmxdb xdb, struct xdb_slot *afterslot)
2ff057
{
2ff057
    struct xdb_slot *slot1, *slot2;
2ff057
    unsigned int freestart = afterslot->startpage + afterslot->pagecnt;
2ff057
    unsigned int freecount = xdb->slots[afterslot->next].startpage - freestart;
2ff057
2ff057
    slot1 = xdb->slots + xdb->slots[xdb->nslots].prev;
2ff057
    if (slot1 == xdb->slots)
2ff057
	slot1 = slot2 = 0;
2ff057
    else {
2ff057
	slot2 = xdb->slots + slot1->prev;
2ff057
	if (slot2 == xdb->slots)
2ff057
	    slot2 = 0;
2ff057
    }
2ff057
    if (slot1->pagecnt < slot2->pagecnt) {
2ff057
	struct xdb_slot *tmp = slot1;
2ff057
	slot1 = slot2;
2ff057
	slot2 = tmp;
2ff057
    }
2ff057
    if (slot1 && slot1->pagecnt && slot1->pagecnt <= freecount && slot1->startpage > freestart) {
2ff057
	if (moveblobto(xdb, slot1, afterslot, slot1->pagecnt))
2ff057
	    return RPMRC_FAIL;
2ff057
	freestart += slot1->pagecnt;
2ff057
	freecount -= slot1->pagecnt;
2ff057
	afterslot = slot1;
2ff057
    }
2ff057
    if (slot2 && slot2->pagecnt && slot2->pagecnt <= freecount && slot2->startpage > freestart) {
2ff057
	if (moveblobto(xdb, slot2, afterslot, slot2->pagecnt))
2ff057
	    return RPMRC_FAIL;
2ff057
    }
2ff057
    return RPMRC_OK;
2ff057
}
2ff057
2ff057
/* add a single page containing empty slots */
2ff057
static int addslotpage(rpmxdb xdb)
2ff057
{
2ff057
    unsigned char *newaddr;
2ff057
    struct xdb_slot *slot;
2ff057
    int i, spp, nslots;
2ff057
    size_t newmappedlen;
2ff057
2ff057
    if (xdb->firstfree)
2ff057
	return RPMRC_FAIL;
2ff057
2ff057
    /* move first blob if needed */
2ff057
    nslots = xdb->nslots;
2ff057
    for (i = xdb->slots[0].next; i != nslots; i = slot->next) {
2ff057
	slot = xdb->slots + i;
2ff057
	if (slot->pagecnt)
2ff057
	    break;
2ff057
    }
2ff057
    if (i != nslots && slot->pagecnt && slot->startpage == xdb->slotnpages) {
2ff057
	/* the blob at this slot is in the way. move it. */
2ff057
	if (moveblob(xdb, slot, slot->pagecnt))
2ff057
	    return RPMRC_FAIL;
2ff057
    }
2ff057
2ff057
    spp = xdb->pagesize / SLOT_SIZE;	/* slots per page */
2ff057
    slot = xrealloc(xdb->slots, (nslots + 1 + spp) * sizeof(*slot));
2ff057
    xdb->slots = slot;
2ff057
2ff057
    if (rpmxdbWriteEmptySlotpage(xdb, xdb->slotnpages)) {
2ff057
	return RPMRC_FAIL;
2ff057
    }
2ff057
    /* remap slots */
2ff057
    newmappedlen = xdb->slotnpages * xdb->pagesize + xdb->pagesize;
2ff057
    newmappedlen = (newmappedlen + xdb->systempagesize - 1) & ~(xdb->systempagesize - 1);
2ff057
    newaddr = mremap(xdb->mapped, xdb->mappedlen, newmappedlen, MREMAP_MAYMOVE);
2ff057
    if (newaddr == MAP_FAILED)
2ff057
	return RPMRC_FAIL;
2ff057
    xdb->mapped = newaddr;
2ff057
    xdb->mappedlen = newmappedlen;
2ff057
2ff057
    /* update the header */
2ff057
    xdb->slotnpages++;
2ff057
    xdb->generation++;
2ff057
    rpmxdbWriteHeader(xdb);
2ff057
2ff057
    /* fixup empty but used slots */
2ff057
    for (i = xdb->slots[0].next; i != nslots; i = slot->next) {
2ff057
	slot = xdb->slots + i;
2ff057
	if (slot->startpage >= xdb->slotnpages)
2ff057
	    break;
2ff057
	slot->startpage = xdb->slotnpages;
2ff057
	if (slot->pagecnt)
2ff057
	    abort();
2ff057
    }
2ff057
2ff057
    /* move tail element to the new end */
2ff057
    slot = xdb->slots + nslots + spp;
2ff057
    *slot = xdb->slots[nslots];
2ff057
    slot->slotno = nslots + spp;
2ff057
    xdb->slots[slot->prev].next = slot->slotno;
2ff057
    xdb->nslots += spp;
2ff057
2ff057
    /* add new free slots to the firstfree chain */
2ff057
    memset(xdb->slots + nslots, 0, sizeof(*slot) * spp);
2ff057
    for (i = 0; i < spp - 1; i++) {
2ff057
	xdb->slots[nslots + i].slotno = nslots + i;
2ff057
	xdb->slots[nslots + i].next = i + 1;
2ff057
    }
2ff057
    xdb->slots[nslots + i].slotno = nslots + i;
2ff057
    xdb->firstfree = nslots;
2ff057
    return RPMRC_OK;
2ff057
}
2ff057
2ff057
static int createblob(rpmxdb xdb, unsigned int *idp, unsigned int blobtag, unsigned int subtag)
2ff057
{
2ff057
    struct xdb_slot *slot;
2ff057
    unsigned int id;
2ff057
2ff057
    if (subtag > 255)
2ff057
	return RPMRC_FAIL;
2ff057
    if (!xdb->firstfree) {
2ff057
	if (addslotpage(xdb))
2ff057
	    return RPMRC_FAIL;
2ff057
    }
2ff057
    id = xdb->firstfree;
2ff057
    slot = xdb->slots + xdb->firstfree;
2ff057
    xdb->firstfree = slot->next;
2ff057
2ff057
    slot->mapped = 0;
2ff057
    slot->blobtag = blobtag;
2ff057
    slot->subtag = subtag;
2ff057
    slot->startpage = xdb->slotnpages;
2ff057
    slot->pagecnt = 0;
2ff057
    rpmxdbUpdateSlot(xdb, slot);
2ff057
    /* enqueue */
2ff057
    slot->prev = 0;
2ff057
    slot->next = xdb->slots[0].next;
2ff057
    xdb->slots[slot->next].prev = id;
2ff057
    xdb->slots[0].next = id;
2ff057
#if 0
2ff057
    printf("createblob #%d %d/%d\n", id, blobtag, subtag);
2ff057
#endif
2ff057
    if (slot->slotno != id)
2ff057
	abort();
2ff057
    if (slot->mapped)
2ff057
	abort();
2ff057
    *idp = id;
2ff057
    return RPMRC_OK;
2ff057
}
2ff057
2ff057
int rpmxdbLookupBlob(rpmxdb xdb, unsigned int *idp, unsigned int blobtag, unsigned int subtag, int flags)
2ff057
{
2ff057
    struct xdb_slot *slot;
2ff057
    unsigned int i, nslots;
2ff057
    if (rpmxdbLockReadHeader(xdb, flags ? 1 : 0))
2ff057
        return RPMRC_FAIL;
2ff057
    nslots = xdb->nslots;
2ff057
    slot = 0;
2ff057
    for (i = xdb->slots[0].next; i != nslots; i = slot->next) {
2ff057
	slot = xdb->slots + i;
2ff057
	if (slot->blobtag == blobtag && slot->subtag == subtag)
2ff057
	    break;
2ff057
    }
2ff057
    if (i == nslots)
2ff057
	i = 0;
2ff057
    if (i && (flags & O_TRUNC) != 0) {
2ff057
	if (rpmxdbResizeBlob(xdb, i, 0)) {
2ff057
	    rpmxdbUnlock(xdb, flags ? 1 : 0);
2ff057
	    return RPMRC_FAIL;
2ff057
	}
2ff057
    }
2ff057
    if (!i && (flags & O_CREAT) != 0) {
2ff057
	if (createblob(xdb, &i, blobtag, subtag)) {
2ff057
	    rpmxdbUnlock(xdb, flags ? 1 : 0);
2ff057
	    return RPMRC_FAIL;
2ff057
	}
2ff057
    }
2ff057
    *idp = i;
2ff057
    rpmxdbUnlock(xdb, flags ? 1 : 0);
2ff057
    return i ? RPMRC_OK : RPMRC_NOTFOUND;
2ff057
}
2ff057
2ff057
int rpmxdbDelBlob(rpmxdb xdb, unsigned int id)
2ff057
{
2ff057
    struct xdb_slot *slot;
2ff057
    if (!id)
2ff057
	return RPMRC_FAIL;
2ff057
    if (rpmxdbLockReadHeader(xdb, 1))
2ff057
        return RPMRC_FAIL;
2ff057
    if (id >= xdb->nslots) {
2ff057
	rpmxdbUnlock(xdb, 1);
2ff057
        return RPMRC_FAIL;
2ff057
    }
2ff057
    slot = xdb->slots + id;
2ff057
    if (!slot->startpage) {
2ff057
	rpmxdbUnlock(xdb, 1);
2ff057
        return RPMRC_OK;
2ff057
    }
2ff057
    if (slot->mapped) {
2ff057
	unmapslot(xdb, slot);
2ff057
	slot->mapcallback(xdb, slot->mapcallbackdata, 0, 0);
2ff057
    }
2ff057
    /* remove from old chain */
2ff057
    xdb->slots[slot->prev].next = slot->next;
2ff057
    xdb->slots[slot->next].prev = slot->prev;
2ff057
    xdb->usedblobpages -= slot->pagecnt;
2ff057
2ff057
    if (xdb->usedblobpages * 2 < xdb->slots[xdb->nslots].startpage && (slot->startpage + slot->pagecnt) * 2 < xdb->slots[xdb->nslots].startpage) {
2ff057
	/* freed in first half of pages, move last two blobs if we can */
2ff057
	moveblobstofront(xdb, xdb->slots + slot->prev);
2ff057
    }
2ff057
2ff057
    /* zero slot */
2ff057
    memset(slot, 0, sizeof(*slot));
2ff057
    slot->slotno = id;
2ff057
    rpmxdbUpdateSlot(xdb, slot);
2ff057
2ff057
    /* enqueue into free chain */
2ff057
    slot->next = xdb->firstfree;
2ff057
    xdb->firstfree = slot->slotno;
2ff057
2ff057
    /* check if we should truncate the file */
2ff057
    slot = xdb->slots + xdb->slots[xdb->nslots].prev;
2ff057
    if (slot->startpage + slot->pagecnt < xdb->slots[xdb->nslots].startpage / 4 * 3) {
2ff057
	unsigned int newend = slot->startpage + slot->pagecnt;
2ff057
	if (!ftruncate(xdb->fd, newend * xdb->pagesize))
2ff057
	    xdb->slots[xdb->nslots].startpage = newend;
2ff057
    }
2ff057
2ff057
    rpmxdbUnlock(xdb, 1);
2ff057
    return RPMRC_OK;
2ff057
}
2ff057
2ff057
int rpmxdbResizeBlob(rpmxdb xdb, unsigned int id, size_t newsize)
2ff057
{
2ff057
    struct xdb_slot *slot;
2ff057
    unsigned int oldpagecnt, newpagecnt;
2ff057
    if (!id)
2ff057
	return RPMRC_FAIL;
2ff057
    if (rpmxdbLockReadHeader(xdb, 1))
2ff057
        return RPMRC_FAIL;
2ff057
    if (id >= xdb->nslots) {
2ff057
	rpmxdbUnlock(xdb, 1);
2ff057
        return RPMRC_FAIL;
2ff057
    }
2ff057
    slot = xdb->slots + id;
2ff057
    if (!slot->startpage) {
2ff057
	rpmxdbUnlock(xdb, 1);
2ff057
        return RPMRC_FAIL;
2ff057
    }
2ff057
    oldpagecnt = slot->pagecnt;
2ff057
    newpagecnt = (newsize + xdb->pagesize - 1) / xdb->pagesize;
2ff057
    if (oldpagecnt && newpagecnt && newpagecnt <= oldpagecnt) {
2ff057
	/* reducing size. zero to end of page */
2ff057
	unsigned int pg = newsize & (xdb->pagesize - 1);
2ff057
	if (pg) {
2ff057
	    if (slot->mapped) {
2ff057
		memset(slot->mapped + pg, 0, xdb->pagesize - pg);
2ff057
	    } else {
2ff057
		char *empty = xcalloc(1, xdb->pagesize - pg);
2ff057
                if (pwrite(xdb->fd, empty, xdb->pagesize - pg, (slot->startpage + newpagecnt - 1) * xdb->pagesize + pg ) != xdb->pagesize - pg) {
2ff057
		    free(empty);
2ff057
		    rpmxdbUnlock(xdb, 1);
2ff057
		    return RPMRC_FAIL;
2ff057
		}
2ff057
		free(empty);
2ff057
	    }
2ff057
	}
2ff057
    }
2ff057
    if (newpagecnt == oldpagecnt) {
2ff057
	/* no size change */
2ff057
	rpmxdbUnlock(xdb, 1);
2ff057
        return RPMRC_OK;
2ff057
    }
2ff057
    if (!newpagecnt) {
2ff057
	/* special case: zero size blob, no longer mapped */
2ff057
	if (slot->mapped)
2ff057
	    unmapslot(xdb, slot);
2ff057
	slot->pagecnt = 0;
2ff057
	slot->startpage = xdb->slotnpages;
2ff057
	/* remove from old chain */
2ff057
	xdb->slots[slot->prev].next = slot->next;
2ff057
	xdb->slots[slot->next].prev = slot->prev;
2ff057
	/* enqueue into head */
2ff057
	slot->prev = 0;
2ff057
	slot->next = xdb->slots[0].next;
2ff057
	xdb->slots[slot->next].prev = slot->slotno;
2ff057
	xdb->slots[0].next = slot->slotno;
2ff057
	rpmxdbUpdateSlot(xdb, slot);
2ff057
	xdb->usedblobpages -= oldpagecnt;
2ff057
	if (slot->mapcallback)
2ff057
	    slot->mapcallback(xdb, slot->mapcallbackdata, 0, 0);
2ff057
    } else if (newpagecnt <= xdb->slots[slot->next].startpage - slot->startpage) {
2ff057
	/* can do it inplace */
2ff057
	if (newpagecnt > oldpagecnt) {
2ff057
	    /* zero new pages */
2ff057
	    if (rpmxdbWriteEmptyPages(xdb, slot->startpage + oldpagecnt, newpagecnt - oldpagecnt)) {
2ff057
		rpmxdbUnlock(xdb, 1);
2ff057
		return RPMRC_FAIL;
2ff057
	    }
2ff057
	}
2ff057
	if (slot->mapcallback) {
2ff057
	    if (remapslot(xdb, slot, newpagecnt)) {
2ff057
		rpmxdbUnlock(xdb, 1);
2ff057
		return RPMRC_FAIL;
2ff057
	    }
2ff057
	} else {
2ff057
	    if (slot->mapped)
2ff057
		unmapslot(xdb, slot);
2ff057
	    slot->pagecnt = newpagecnt;
2ff057
	}
2ff057
	rpmxdbUpdateSlot(xdb, slot);
2ff057
	xdb->usedblobpages -= oldpagecnt;
2ff057
	xdb->usedblobpages += newpagecnt;
2ff057
	if (slot->mapcallback)
2ff057
	    slot->mapcallback(xdb, slot->mapcallbackdata, slot->mapped, slot->pagecnt * xdb->pagesize);
2ff057
    } else {
2ff057
	/* need to relocate to a new page area */
2ff057
	if (moveblob(xdb, slot, newpagecnt)) {
2ff057
	    rpmxdbUnlock(xdb, 1);
2ff057
	    return RPMRC_FAIL;
2ff057
	}
2ff057
    }
2ff057
    rpmxdbUnlock(xdb, 1);
2ff057
    return RPMRC_OK;
2ff057
}
2ff057
2ff057
int rpmxdbMapBlob(rpmxdb xdb, unsigned int id, int flags, void (*mapcallback)(rpmxdb xdb, void *data, void *newaddr, size_t newsize), void *mapcallbackdata)
2ff057
{
2ff057
    struct xdb_slot *slot;
2ff057
    if (!id || !mapcallback)
2ff057
	return RPMRC_FAIL;
2ff057
    if ((flags & (O_RDONLY|O_RDWR)) == O_RDWR && xdb->rdonly)
2ff057
	return RPMRC_FAIL;
2ff057
    if (rpmxdbLockReadHeader(xdb, 0))
2ff057
        return RPMRC_FAIL;
2ff057
    if (id >= xdb->nslots) {
2ff057
	rpmxdbUnlock(xdb, 0);
2ff057
        return RPMRC_FAIL;
2ff057
    }
2ff057
    slot = xdb->slots + id;
2ff057
    if (!slot->startpage || slot->mapped) {
2ff057
	rpmxdbUnlock(xdb, 0);
2ff057
        return RPMRC_FAIL;
2ff057
    }
2ff057
    slot->mapflags = (flags & (O_RDONLY|O_RDWR)) == O_RDWR ? PROT_READ | PROT_WRITE : PROT_READ;
2ff057
    if (slot->pagecnt) {
2ff057
	if (mapslot(xdb, slot)) {
2ff057
	    slot->mapflags = 0;
2ff057
	    rpmxdbUnlock(xdb, 0);
2ff057
	    return RPMRC_FAIL;
2ff057
	}
2ff057
    }
2ff057
    slot->mapcallback = mapcallback;
2ff057
    slot->mapcallbackdata = mapcallbackdata;
2ff057
    mapcallback(xdb, mapcallbackdata, slot->mapped, slot->mapped ? slot->pagecnt * xdb->pagesize : 0);
2ff057
    rpmxdbUnlock(xdb, 0);
2ff057
    return RPMRC_OK;
2ff057
}
2ff057
2ff057
int rpmxdbUnmapBlob(rpmxdb xdb, unsigned int id)
2ff057
{
2ff057
    struct xdb_slot *slot;
2ff057
    if (!id)
2ff057
	return RPMRC_OK;
2ff057
    if (rpmxdbLockReadHeader(xdb, 0))
2ff057
        return RPMRC_FAIL;
2ff057
    if (id >= xdb->nslots) {
2ff057
	rpmxdbUnlock(xdb, 0);
2ff057
	return RPMRC_FAIL;
2ff057
    }
2ff057
    slot = xdb->slots + id;
2ff057
    if (slot->mapped) {
2ff057
	unmapslot(xdb, slot);
2ff057
	slot->mapcallback(xdb, slot->mapcallbackdata, 0, 0);
2ff057
    }
2ff057
    slot->mapcallback = 0;
2ff057
    slot->mapcallbackdata = 0;
2ff057
    slot->mapflags = 0;
2ff057
    rpmxdbUnlock(xdb, 0);
2ff057
    return RPMRC_OK;
2ff057
}
2ff057
2ff057
int rpmxdbRenameBlob(rpmxdb xdb, unsigned int *idp, unsigned int blobtag, unsigned int subtag)
2ff057
{
2ff057
    struct xdb_slot *slot;
2ff057
    unsigned int otherid;
2ff057
    unsigned int id = *idp;
2ff057
    int rc;
2ff057
2ff057
    if (!id || subtag > 255)
2ff057
	return RPMRC_FAIL;
2ff057
    if (rpmxdbLockReadHeader(xdb, 1))
2ff057
        return RPMRC_FAIL;
2ff057
    if (id >= xdb->nslots) {
2ff057
	rpmxdbUnlock(xdb, 1);
2ff057
        return RPMRC_FAIL;
2ff057
    }
2ff057
    slot = xdb->slots + id;
2ff057
#if 0
2ff057
    printf("rpmxdbRenameBlob #%d %d/%d -> %d/%d\n", id, slot->blobtag, slot->subtag, blobtag, subtag);
2ff057
#endif
2ff057
    if (!slot->startpage) {
2ff057
	rpmxdbUnlock(xdb, 1);
2ff057
	return RPMRC_FAIL;
2ff057
    }
2ff057
    if (slot->blobtag == blobtag && slot->subtag == subtag) {
2ff057
	rpmxdbUnlock(xdb, 1);
2ff057
	return RPMRC_OK;
2ff057
    }
2ff057
    rc = rpmxdbLookupBlob(xdb, &otherid, blobtag, subtag, 0);
2ff057
    if (rc == RPMRC_NOTFOUND)
2ff057
	otherid = 0;
2ff057
    else if (rc) {
2ff057
	rpmxdbUnlock(xdb, 1);
2ff057
	return RPMRC_FAIL;
2ff057
    }
2ff057
    if (otherid) {
2ff057
#if 0
2ff057
	printf("(replacing #%d)\n", otherid);
2ff057
#endif
2ff057
	if (rpmxdbDelBlob(xdb, otherid)) {
2ff057
	    rpmxdbUnlock(xdb, 1);
2ff057
	    return RPMRC_FAIL;
2ff057
	}
2ff057
	/* get otherid back from free chain */
2ff057
	if (xdb->firstfree != otherid)
2ff057
	    return RPMRC_FAIL;
2ff057
	xdb->firstfree = xdb->slots[otherid].next;
2ff057
2ff057
	slot->blobtag = blobtag;
2ff057
	slot->subtag = subtag;
2ff057
	xdb->slots[otherid] = *slot;
2ff057
	/* fixup ids */
2ff057
	xdb->slots[otherid].slotno = otherid;
2ff057
	xdb->slots[slot->prev].next = otherid;
2ff057
	xdb->slots[slot->next].prev = otherid;
2ff057
	/* write */
2ff057
	rpmxdbUpdateSlot(xdb, xdb->slots + otherid);
2ff057
	memset(slot, 0, sizeof(*slot));
2ff057
	slot->slotno = id;
2ff057
	rpmxdbUpdateSlot(xdb, slot);
2ff057
	slot->next = xdb->firstfree;
2ff057
	xdb->firstfree = slot->slotno;
2ff057
	*idp = otherid;
2ff057
    } else {
2ff057
	slot = xdb->slots + id;
2ff057
	slot->blobtag = blobtag;
2ff057
	slot->subtag = subtag;
2ff057
	rpmxdbUpdateSlot(xdb, slot);
2ff057
    }
2ff057
    rpmxdbUnlock(xdb, 1);
2ff057
    return RPMRC_OK;
2ff057
}
2ff057
2ff057
void rpmxdbSetFsync(rpmxdb xdb, int dofsync)
2ff057
{
2ff057
    xdb->dofsync = dofsync;
2ff057
}
2ff057
2ff057
int rpmxdbIsRdonly(rpmxdb xdb)
2ff057
{
2ff057
    return xdb->rdonly;
2ff057
}
2ff057
2ff057
int rpmxdbSetUserGeneration(rpmxdb xdb, unsigned int usergeneration)
2ff057
{
2ff057
    if (rpmxdbLockReadHeader(xdb, 1))
2ff057
        return RPMRC_FAIL;
2ff057
    /* sync before the update */
2ff057
    if (xdb->dofsync && fsync(xdb->fd)) {
2ff057
	rpmxdbUnlock(xdb, 1);
2ff057
	return RPMRC_FAIL;
2ff057
    }
2ff057
    xdb->usergeneration = usergeneration;
2ff057
    xdb->generation++;
2ff057
    rpmxdbWriteHeader(xdb);
2ff057
    rpmxdbUnlock(xdb, 1);
2ff057
    return RPMRC_OK;
2ff057
}
2ff057
2ff057
int rpmxdbGetUserGeneration(rpmxdb xdb, unsigned int *usergenerationp)
2ff057
{
2ff057
    if (rpmxdbLockReadHeader(xdb, 0))
2ff057
        return RPMRC_FAIL;
2ff057
    *usergenerationp = xdb->usergeneration;
2ff057
    rpmxdbUnlock(xdb, 0);
2ff057
    return RPMRC_OK;
2ff057
}
2ff057
2ff057
int rpmxdbStats(rpmxdb xdb)
2ff057
{
2ff057
    struct xdb_slot *slot;
2ff057
    unsigned int i, nslots;
2ff057
2ff057
    if (rpmxdbLockReadHeader(xdb, 0))
2ff057
        return RPMRC_FAIL;
2ff057
    nslots = xdb->nslots;
2ff057
    printf("--- XDB Stats\n");
2ff057
    printf("Filename: %s\n", xdb->filename);
2ff057
    printf("Generation: %d\n", xdb->generation);
2ff057
    printf("Slot pages: %d\n", xdb->slotnpages);
2ff057
    printf("Blob pages: %d\n", xdb->usedblobpages);
2ff057
    printf("Free pages: %d\n", xdb->slots[nslots].startpage - xdb->usedblobpages - xdb->slotnpages);
2ff057
    printf("Pagesize: %d / %d\n", xdb->pagesize, xdb->systempagesize);
2ff057
    for (i = 1, slot = xdb->slots + i; i < nslots; i++, slot++) {
2ff057
	if (!slot->startpage)
2ff057
	    continue;
2ff057
	printf("%2d: tag %d/%d, startpage %d, pagecnt %d%s\n", i, slot->blobtag, slot->subtag, slot->startpage, slot->pagecnt, slot->mapcallbackdata ? ", mapped" : "");
2ff057
    }
2ff057
#if 0
2ff057
    printf("Again in offset order:\n");
2ff057
    for (i = xdb->slots[0].next; i != nslots; i = slot->next) {
2ff057
	slot = xdb->slots + i;
2ff057
	printf("%2d: tag %d/%d, startpage %d, pagecnt %d%s\n", i, slot->blobtag, slot->subtag, slot->startpage, slot->pagecnt, slot->mapcallbackdata ? ", mapped" : "");
2ff057
    }
2ff057
#endif
2ff057
#if 0
2ff057
    printf("Free chain:\n");
2ff057
    for (i = xdb->firstfree; i; i = slot->next) {
2ff057
	slot = xdb->slots + i;
2ff057
	printf("%2d [%2d]: tag %d/%d, startpage %d, pagecnt %d%s\n", i, slot->slotno, slot->blobtag, slot->subtag, slot->startpage, slot->pagecnt, slot->mapcallbackdata ? ", mapped" : "");
2ff057
    }
2ff057
#endif
2ff057
    rpmxdbUnlock(xdb, 0);
2ff057
    return RPMRC_OK;
2ff057
}
2ff057