#include "system.h" #include #include #include #include #include #include #include #include #include #include #include #include "rpmpkg.h" #define RPMRC_FAIL 2 #define RPMRC_NOTFOUND 1 #define RPMRC_OK 0 #ifdef RPMPKG_LZO static int rpmpkgLZOCompress(unsigned char **blobp, unsigned int *bloblp); static int rpmpkgLZODecompress(unsigned char **blobp, unsigned int *bloblp); #endif static int rpmpkgVerifyblob(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned int blkoff, unsigned int blkcnt); typedef struct pkgslot_s { unsigned int pkgidx; unsigned int blkoff; unsigned int blkcnt; unsigned int slotno; } pkgslot; typedef struct rpmpkgdb_s { int fd; /* our file descriptor */ int flags; int mode; int rdonly; unsigned int locked_shared; unsigned int locked_excl; int header_ok; /* header data (e.g. generation) is valid */ unsigned int generation; unsigned int slotnpages; unsigned int nextpkgidx; struct pkgslot_s *slots; unsigned int aslots; /* allocated slots */ unsigned int nslots; /* used slots */ unsigned int *slothash; unsigned int nslothash; unsigned int freeslot; /* first free slot */ int slotorder; char *filename; unsigned int fileblks; /* file size in blks */ int dofsync; } * rpmpkgdb; #define SLOTORDER_UNORDERED 0 #define SLOTORDER_BLKOFF 1 static inline unsigned int le2h(unsigned char *p) { return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24; } static inline void h2le(unsigned int x, unsigned char *p) { p[0] = x; p[1] = x >> 8; p[2] = x >> 16; p[3] = x >> 24; } /* adler 32 algorithm taken from RFC 1950 */ #define ADLER32_INIT 1 static unsigned int update_adler32(unsigned int adler, unsigned char *buf, unsigned int len) { unsigned int s1 = adler & 0xffff; unsigned int s2 = (adler >> 16) & 0xffff; int n; for (; len >= 5552; len -= 5552) { for (n = 0; n < 5552; n++) { s1 += *buf++; s2 += s1; } s1 %= 65521; s2 %= 65521; } for (n = 0; n < len; n++) { s1 += *buf++; s2 += s1; } return ((s2 % 65521) << 16) + (s1 % 65521); } /*** Header management ***/ #define PKGDB_MAGIC ('R' | 'p' << 8 | 'm' << 16 | 'P' << 24) #define PKGDB_VERSION 0 /* must be a multiple of SLOT_SIZE! */ #define PKGDB_HEADER_SIZE 32 #define PKGDB_OFFSET_MAGIC 0 #define PKGDB_OFFSET_VERSION 4 #define PKGDB_OFFSET_GENERATION 8 #define PKGDB_OFFSET_SLOTNPAGES 12 #define PKGDB_OFFSET_NEXTPKGIDX 16 static int rpmpkgReadHeader(rpmpkgdb pkgdb) { unsigned int generation, slotnpages, nextpkgidx, version; unsigned char header[PKGDB_HEADER_SIZE]; /* if we always head the write lock then our data matches */ if (pkgdb->header_ok) return RPMRC_OK; if (pread(pkgdb->fd, header, PKGDB_HEADER_SIZE, 0) != PKGDB_HEADER_SIZE) { return RPMRC_FAIL; } if (le2h(header + PKGDB_OFFSET_MAGIC) != PKGDB_MAGIC) { return RPMRC_FAIL; } version = le2h(header + PKGDB_OFFSET_VERSION); if (version != PKGDB_VERSION) { rpmlog(RPMLOG_ERR, _("rpmpkg: Version mismatch. Expected version: %u. " "Found version: %u\n"), PKGDB_VERSION, version); return RPMRC_FAIL; } generation = le2h(header + PKGDB_OFFSET_GENERATION); slotnpages = le2h(header + PKGDB_OFFSET_SLOTNPAGES); nextpkgidx = le2h(header + PKGDB_OFFSET_NEXTPKGIDX); /* free slots if our internal data no longer matches */ if (pkgdb->slots && (pkgdb->generation != generation || pkgdb->slotnpages != slotnpages)) { free(pkgdb->slots); pkgdb->slots = 0; if (pkgdb->slothash) { free(pkgdb->slothash); pkgdb->slothash = 0; } } pkgdb->generation = generation; pkgdb->slotnpages = slotnpages; pkgdb->nextpkgidx = nextpkgidx; pkgdb->header_ok = 1; return RPMRC_OK; } static int rpmpkgWriteHeader(rpmpkgdb pkgdb) { unsigned char header[PKGDB_HEADER_SIZE]; memset(header, 0, sizeof(header)); h2le(PKGDB_MAGIC, header + PKGDB_OFFSET_MAGIC); h2le(PKGDB_VERSION, header + PKGDB_OFFSET_VERSION); h2le(pkgdb->generation, header + PKGDB_OFFSET_GENERATION); h2le(pkgdb->slotnpages, header + PKGDB_OFFSET_SLOTNPAGES); h2le(pkgdb->nextpkgidx, header + PKGDB_OFFSET_NEXTPKGIDX); if (pwrite(pkgdb->fd, header, sizeof(header), 0) != sizeof(header)) { return RPMRC_FAIL; } if (pkgdb->dofsync && fsync(pkgdb->fd)) return RPMRC_FAIL; /* write error */ return RPMRC_OK; } /*** Slot management ***/ #define SLOT_MAGIC ('S' | 'l' << 8 | 'o' << 16 | 't' << 24) #define SLOT_SIZE 16 #define BLK_SIZE 16 #define PAGE_SIZE 4096 /* the first slots (i.e. 32 bytes) are used for the header */ #define SLOT_START (PKGDB_HEADER_SIZE / SLOT_SIZE) static inline unsigned int hashpkgidx(unsigned int h) { h *= 0x5bd1e995; h ^= h >> 16; return h; } static int rpmpkgHashSlots(rpmpkgdb pkgdb) { unsigned int nslots, num; unsigned int *hash; unsigned int h, hh, hmask; int i; pkgslot *slot; pkgdb->nslothash = 0; num = pkgdb->nslots; while (num & (num - 1)) num = num & (num - 1); num *= 4; hash = pkgdb->slothash; if (!hash || pkgdb->nslothash != num) { free(pkgdb->slothash); hash = pkgdb->slothash = xcalloc(num, sizeof(unsigned int)); pkgdb->nslothash = num; } else { memset(hash, 0, num * sizeof(unsigned int)); } hmask = num - 1; nslots = pkgdb->nslots; for (i = 0, slot = pkgdb->slots; i < nslots; i++, slot++) { for (h = hashpkgidx(slot->pkgidx) & hmask, hh = 7; hash[h] != 0; h = (h + hh++) & hmask) ; hash[h] = i + 1; } pkgdb->slothash = hash; pkgdb->nslothash = num; return RPMRC_OK; } static int rpmpkgReadSlots(rpmpkgdb pkgdb) { unsigned int slotnpages = pkgdb->slotnpages; struct stat stb; unsigned char pagebuf[PAGE_SIZE]; unsigned int page; unsigned int i, minblkoff, fileblks, slotno, freeslot, o; pkgslot *slot; /* free old slot data */ if (pkgdb->slots) { free(pkgdb->slots); pkgdb->slots = 0; } if (pkgdb->slothash) { free(pkgdb->slothash); pkgdb->slothash = 0; } pkgdb->nslots = 0; pkgdb->freeslot = 0; /* calculate current database size in blks */ if (fstat(pkgdb->fd, &stb)) return RPMRC_FAIL; if (stb.st_size % BLK_SIZE) return RPMRC_FAIL; /* hmm */ fileblks = stb.st_size / BLK_SIZE; /* read (and somewhat verify) all slots */ pkgdb->aslots = slotnpages * (PAGE_SIZE / SLOT_SIZE); pkgdb->slots = xcalloc(pkgdb->aslots, sizeof(*pkgdb->slots)); i = 0; slot = pkgdb->slots; minblkoff = slotnpages * (PAGE_SIZE / BLK_SIZE); slotno = SLOT_START; freeslot = 0; for (page = 0; page < slotnpages; page++) { if (pread(pkgdb->fd, pagebuf, PAGE_SIZE, page * PAGE_SIZE) != PAGE_SIZE) return RPMRC_FAIL; for (o = page ? 0 : SLOT_START * SLOT_SIZE; o < PAGE_SIZE; o += SLOT_SIZE, slotno++) { unsigned char *pp = pagebuf + o; unsigned int blkoff, blkcnt, pkgidx; if (le2h(pp) != SLOT_MAGIC) { return RPMRC_FAIL; } blkoff = le2h(pp + 8); if (!blkoff) { if (!freeslot) freeslot = slotno; continue; } pkgidx = le2h(pp + 4); blkcnt = le2h(pp + 12); slot->pkgidx = pkgidx; slot->blkoff = blkoff; slot->blkcnt = blkcnt; slot->slotno = slotno; if (slot->blkoff + slot->blkcnt > fileblks) return RPMRC_FAIL; /* truncated database */ if (!slot->pkgidx || !slot->blkcnt || slot->blkoff < minblkoff) return RPMRC_FAIL; /* bad entry */ i++; slot++; } } pkgdb->nslots = i; pkgdb->slotorder = SLOTORDER_UNORDERED; /* XXX: always order? */ pkgdb->fileblks = fileblks; pkgdb->freeslot = freeslot; if (rpmpkgHashSlots(pkgdb)) { free(pkgdb->slots); pkgdb->slots = 0; return RPMRC_FAIL; } return RPMRC_OK; } static int orderslots_blkoff_cmp(const void *a, const void *b) { unsigned int blkoffa = ((const pkgslot *)a)->blkoff; unsigned int blkoffb = ((const pkgslot *)b)->blkoff; return blkoffa > blkoffb ? 1 : blkoffa < blkoffb ? -1 : 0; } static void rpmpkgOrderSlots(rpmpkgdb pkgdb, int slotorder) { if (pkgdb->slotorder == slotorder) return; if (slotorder == SLOTORDER_BLKOFF) { if (pkgdb->nslots > 1) qsort(pkgdb->slots, pkgdb->nslots, sizeof(*pkgdb->slots), orderslots_blkoff_cmp); } pkgdb->slotorder = slotorder; rpmpkgHashSlots(pkgdb); } static inline pkgslot *rpmpkgFindSlot(rpmpkgdb pkgdb, unsigned int pkgidx) { unsigned int i, h, hh, hmask = pkgdb->nslothash - 1; unsigned int *hash = pkgdb->slothash; for (h = hashpkgidx(pkgidx) & hmask, hh = 7; (i = hash[h]) != 0; h = (h + hh++) & hmask) if (pkgdb->slots[i - 1].pkgidx == pkgidx) return pkgdb->slots + (i - 1); return 0; } static int rpmpkgFindEmptyOffset(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned int blkcnt, unsigned *blkoffp, pkgslot **oldslotp, int dontprepend) { unsigned int i, nslots = pkgdb->nslots; unsigned int bestblkoff = 0; unsigned int freecnt, bestfreecnt = 0; unsigned int lastblkend = pkgdb->slotnpages * (PAGE_SIZE / BLK_SIZE); pkgslot *slot, *oldslot = 0; if (pkgdb->slotorder != SLOTORDER_BLKOFF) rpmpkgOrderSlots(pkgdb, SLOTORDER_BLKOFF); if (dontprepend && nslots) { lastblkend = pkgdb->slots[0].blkoff; } /* best fit strategy */ for (i = 0, slot = pkgdb->slots; i < nslots; i++, slot++) { if (slot->blkoff < lastblkend) { return RPMRC_FAIL; /* eek, slots overlap! */ } if (slot->pkgidx == pkgidx) { if (oldslot) { return RPMRC_FAIL; /* eek, two slots with our pkgid ! */ } oldslot = slot; } freecnt = slot->blkoff - lastblkend; if (freecnt >= blkcnt) { if (!bestblkoff || bestfreecnt > freecnt) { bestblkoff = lastblkend; bestfreecnt = freecnt; } } lastblkend = slot->blkoff + slot->blkcnt; } if (!bestblkoff) { bestblkoff = lastblkend; /* append to end */ } *oldslotp = oldslot; *blkoffp = bestblkoff; return RPMRC_OK; } static int rpmpkgNeighbourCheck(rpmpkgdb pkgdb, unsigned int blkoff, unsigned int blkcnt, unsigned int *newblkcnt) { unsigned int i, nslots = pkgdb->nslots; unsigned int lastblkend = pkgdb->slotnpages * (PAGE_SIZE / BLK_SIZE); pkgslot *slot, *left = 0, *right = 0; if (pkgdb->slotorder != SLOTORDER_BLKOFF) rpmpkgOrderSlots(pkgdb, SLOTORDER_BLKOFF); if (blkoff < lastblkend) return RPMRC_FAIL; for (i = 0, slot = pkgdb->slots; i < nslots; i++, slot++) { if (slot->blkoff < lastblkend) return RPMRC_FAIL; /* eek, slots overlap! */ if (slot->blkoff < blkoff) left = slot; if (!right && slot->blkoff >= blkoff) right = slot; lastblkend = slot->blkoff + slot->blkcnt; } if (left && left->blkoff + left->blkcnt != blkoff) return RPMRC_FAIL; /* must always start right after the block */ if (!left && blkoff != pkgdb->slotnpages * (PAGE_SIZE / BLK_SIZE)) return RPMRC_FAIL; if (right && right->blkoff < blkoff + blkcnt) return RPMRC_FAIL; /* check if neighbour blobs are in good shape */ if (left && rpmpkgVerifyblob(pkgdb, left->pkgidx, left->blkoff, left->blkcnt) != RPMRC_OK) return RPMRC_FAIL; if (right && rpmpkgVerifyblob(pkgdb, right->pkgidx, right->blkoff, right->blkcnt) != RPMRC_OK) return RPMRC_FAIL; *newblkcnt = right ? right->blkoff - blkoff : blkcnt; /* bounds are intect. free area. */ return RPMRC_OK; } static int rpmpkgWriteslot(rpmpkgdb pkgdb, unsigned int slotno, unsigned int pkgidx, unsigned int blkoff, unsigned int blkcnt) { unsigned char buf[SLOT_SIZE]; /* sanity */ if (slotno < SLOT_START) return RPMRC_FAIL; if (blkoff && slotno == pkgdb->freeslot) pkgdb->freeslot = 0; h2le(SLOT_MAGIC, buf); h2le(pkgidx, buf + 4); h2le(blkoff, buf + 8); h2le(blkcnt, buf + 12); if (pwrite(pkgdb->fd, buf, sizeof(buf), slotno * SLOT_SIZE) != sizeof(buf)) { return RPMRC_FAIL; } pkgdb->generation++; if (rpmpkgWriteHeader(pkgdb)) { return RPMRC_FAIL; } return RPMRC_OK; } static int rpmpkgWriteEmptySlotpage(rpmpkgdb pkgdb, int pageno) { unsigned char page[PAGE_SIZE]; int i, off = pageno ? 0 : SLOT_START * SLOT_SIZE; memset(page, 0, sizeof(page)); for (i = 0; i < PAGE_SIZE / SLOT_SIZE; i++) h2le(SLOT_MAGIC, page + i * SLOT_SIZE); if (pwrite(pkgdb->fd, page, PAGE_SIZE - off, pageno * PAGE_SIZE + off) != PAGE_SIZE - off) { return RPMRC_FAIL; } if (pkgdb->dofsync && fsync(pkgdb->fd)) { return RPMRC_FAIL; /* write error */ } return RPMRC_OK; } /*** Blk primitives ***/ static int rpmpkgZeroBlks(rpmpkgdb pkgdb, unsigned int blkoff, unsigned int blkcnt) { unsigned char buf[65536]; unsigned int towrite; off_t fileoff; memset(buf, 0, sizeof(buf)); fileoff = (off_t)blkoff * BLK_SIZE; for (towrite = blkcnt * BLK_SIZE; towrite; ) { unsigned int chunk = towrite > 65536 ? 65536 : towrite; if (pwrite(pkgdb->fd, buf, chunk, fileoff) != chunk) { return RPMRC_FAIL; /* write error */ } fileoff += chunk; towrite -= chunk; } if (blkoff + blkcnt > pkgdb->fileblks) pkgdb->fileblks = blkoff + blkcnt; return RPMRC_OK; } static int rpmpkgValidateZeroCheck(rpmpkgdb pkgdb, unsigned int blkoff, unsigned int blkcnt) { unsigned long long buf[(65536 / sizeof(unsigned long long)) + 1]; off_t fileoff; off_t tocheck; int i; if (blkoff > pkgdb->fileblks) return RPMRC_FAIL; /* huh? */ fileoff = (off_t)blkoff * BLK_SIZE; tocheck = blkoff + blkcnt > pkgdb->fileblks ? pkgdb->fileblks - blkoff : blkcnt; tocheck *= BLK_SIZE; while (tocheck >= 65536) { if (pread(pkgdb->fd, (void *)buf, 65536, fileoff) != 65536) return RPMRC_FAIL; /* read error */ for (i = 0; i < 65536 / sizeof(unsigned long long); i++) if (buf[i]) return RPMRC_FAIL; /* not empty */ fileoff += 65536; tocheck -= 65536; } if (tocheck) { int cnt = (int)tocheck / sizeof(unsigned long long); buf[cnt++] = 0; if (pread(pkgdb->fd, (void *)buf, tocheck, fileoff) != tocheck) return RPMRC_FAIL; /* read error */ for (i = 0; i < cnt; i++) if (buf[i]) return RPMRC_FAIL; /* not empty */ } return RPMRC_OK; } static int rpmpkgValidateZero(rpmpkgdb pkgdb, unsigned int blkoff, unsigned int blkcnt) { if (rpmpkgValidateZeroCheck(pkgdb, blkoff, blkcnt) == RPMRC_OK) return RPMRC_OK; rpmlog(RPMLOG_WARNING, _("rpmpkg: detected non-zero blob, trying auto repair\n")); /* auto-repair interrupted transactions */ if (rpmpkgNeighbourCheck(pkgdb, blkoff, blkcnt, &blkcnt) != RPMRC_OK) return RPMRC_FAIL; if (rpmpkgZeroBlks(pkgdb, blkoff, blkcnt) != RPMRC_OK) return RPMRC_FAIL; return RPMRC_OK; } /*** Blob primitives ***/ /* head: magic + pkgidx + timestamp + bloblen */ /* tail: adler32 + bloblen + magic */ #define BLOBHEAD_MAGIC ('B' | 'l' << 8 | 'b' << 16 | 'S' << 24) #define BLOBTAIL_MAGIC ('B' | 'l' << 8 | 'b' << 16 | 'E' << 24) #define BLOBHEAD_SIZE (4 + 4 + 4 + 4) #define BLOBTAIL_SIZE (4 + 4 + 4) static int rpmpkgReadBlob(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned int blkoff, unsigned int blkcnt, unsigned char *blob, unsigned int *bloblp, unsigned int *tstampp) { unsigned char buf[BLOBHEAD_SIZE > BLOBTAIL_SIZE ? BLOBHEAD_SIZE : BLOBTAIL_SIZE]; unsigned int bloblen, toread, tstamp; off_t fileoff; unsigned int adl; int verifyadler = bloblp ? 0 : 1; /* sanity */ if (blkcnt < (BLOBHEAD_SIZE + BLOBTAIL_SIZE + BLK_SIZE - 1) / BLK_SIZE) return RPMRC_FAIL; /* blkcnt too small */ /* read header */ fileoff = (off_t)blkoff * BLK_SIZE; if (pread(pkgdb->fd, buf, BLOBHEAD_SIZE, fileoff) != BLOBHEAD_SIZE) return RPMRC_FAIL; /* read error */ if (le2h(buf) != BLOBHEAD_MAGIC) return RPMRC_FAIL; /* bad blob */ if (le2h(buf + 4) != pkgidx) return RPMRC_FAIL; /* bad blob */ tstamp = le2h(buf + 8); bloblen = le2h(buf + 12); if (blkcnt != (BLOBHEAD_SIZE + bloblen + BLOBTAIL_SIZE + BLK_SIZE - 1) / BLK_SIZE) return RPMRC_FAIL; /* bad blob */ adl = ADLER32_INIT; if (verifyadler) adl = update_adler32(adl, buf, BLOBHEAD_SIZE); /* read in 64K chunks */ fileoff += BLOBHEAD_SIZE; toread = blkcnt * BLK_SIZE - BLOBHEAD_SIZE; if (!bloblp) toread -= BLOBTAIL_SIZE; while (toread) { unsigned int chunk = toread > 65536 ? 65536 : toread; if (pread(pkgdb->fd, blob, chunk, fileoff) != chunk) { return RPMRC_FAIL; /* read error */ } if (verifyadler) { if (!bloblp) adl = update_adler32(adl, blob, chunk); else if (toread > BLOBTAIL_SIZE) adl = update_adler32(adl, blob, toread - BLOBTAIL_SIZE > chunk ? chunk : toread - BLOBTAIL_SIZE); } if (bloblp) blob += chunk; toread -= chunk; fileoff += chunk; } /* read trailer */ if (bloblp) { memcpy(buf, blob - BLOBTAIL_SIZE, BLOBTAIL_SIZE); } else if (pread(pkgdb->fd, buf, BLOBTAIL_SIZE, fileoff) != BLOBTAIL_SIZE) { return RPMRC_FAIL; /* read error */ } if (verifyadler && le2h(buf) != adl) { return RPMRC_FAIL; /* bad blob, adler32 mismatch */ } if (le2h(buf + 4) != bloblen) { return RPMRC_FAIL; /* bad blob, bloblen mismatch */ } if (le2h(buf + 8) != BLOBTAIL_MAGIC) { return RPMRC_FAIL; /* bad blob */ } if (bloblp) *bloblp = bloblen; if (tstampp) *tstampp = tstamp; return RPMRC_OK; } static int rpmpkgVerifyblob(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned int blkoff, unsigned int blkcnt) { unsigned char buf[65536]; return rpmpkgReadBlob(pkgdb, pkgidx, blkoff, blkcnt, buf, 0, 0); } static int rpmpkgWriteBlob(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned int blkoff, unsigned int blkcnt, unsigned char *blob, unsigned int blobl, unsigned int now) { unsigned char buf[(BLOBHEAD_SIZE > BLOBTAIL_SIZE ? BLOBHEAD_SIZE : BLOBTAIL_SIZE) + BLK_SIZE]; unsigned int towrite, pad; unsigned int adl; off_t fileoff; /* sanity */ if (blkcnt < (BLOBHEAD_SIZE + BLOBTAIL_SIZE + BLK_SIZE - 1) / BLK_SIZE) return RPMRC_FAIL; /* blkcnt too small */ if (blkcnt != (BLOBHEAD_SIZE + blobl + BLOBTAIL_SIZE + BLK_SIZE - 1) / BLK_SIZE) return RPMRC_FAIL; /* blkcnt mismatch */ fileoff = (off_t)blkoff * BLK_SIZE; h2le(BLOBHEAD_MAGIC, buf); h2le(pkgidx, buf + 4); h2le(now, buf + 8); h2le(blobl, buf + 12); if (pwrite(pkgdb->fd, buf, BLOBHEAD_SIZE, fileoff) != BLOBHEAD_SIZE) { return RPMRC_FAIL; /* write error */ } adl = ADLER32_INIT; adl = update_adler32(adl, buf, BLOBHEAD_SIZE); /* write in 64K chunks */ fileoff += BLOBHEAD_SIZE; for (towrite = blobl; towrite;) { unsigned int chunk = towrite > 65536 ? 65536 : towrite; if (pwrite(pkgdb->fd, blob, chunk, fileoff) != chunk) { return RPMRC_FAIL; /* write error */ } adl = update_adler32(adl, blob, chunk); blob += chunk; towrite -= chunk; fileoff += chunk; } /* pad if needed */ pad = blkcnt * BLK_SIZE - (BLOBHEAD_SIZE + blobl + BLOBTAIL_SIZE); if (pad) { memset(buf + (sizeof(buf) - BLOBTAIL_SIZE) - pad, 0, pad); adl = update_adler32(adl, buf + (sizeof(buf) - BLOBTAIL_SIZE) - pad, pad); } h2le(adl, buf + (sizeof(buf) - BLOBTAIL_SIZE)); h2le(blobl, buf + (sizeof(buf) - BLOBTAIL_SIZE) + 4); h2le(BLOBTAIL_MAGIC, buf + (sizeof(buf) - BLOBTAIL_SIZE) + 8); if (pwrite(pkgdb->fd, buf + (sizeof(buf) - BLOBTAIL_SIZE) - pad, pad + BLOBTAIL_SIZE, fileoff) != pad + BLOBTAIL_SIZE) { return RPMRC_FAIL; /* write error */ } /* update file length */ if (blkoff + blkcnt > pkgdb->fileblks) pkgdb->fileblks = blkoff + blkcnt; if (pkgdb->dofsync && fsync(pkgdb->fd)) { return RPMRC_FAIL; /* write error */ } return RPMRC_OK; } static int rpmpkgDelBlob(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned int blkoff, unsigned int blkcnt) { if (rpmpkgVerifyblob(pkgdb, pkgidx, blkoff, blkcnt)) return RPMRC_FAIL; if (rpmpkgZeroBlks(pkgdb, blkoff, blkcnt)) return RPMRC_FAIL; if (pkgdb->dofsync && fsync(pkgdb->fd)) return RPMRC_FAIL; /* write error */ return RPMRC_OK; } static int rpmpkgMoveBlob(rpmpkgdb pkgdb, pkgslot *slot, unsigned int newblkoff) { unsigned int pkgidx = slot->pkgidx; unsigned int blkoff = slot->blkoff; unsigned int blkcnt = slot->blkcnt; unsigned char *blob; unsigned int tstamp, blobl; blob = xmalloc((size_t)blkcnt * BLK_SIZE); if (rpmpkgReadBlob(pkgdb, pkgidx, blkoff, blkcnt, blob, &blobl, &tstamp)) { free(blob); return RPMRC_FAIL; } if (rpmpkgWriteBlob(pkgdb, pkgidx, newblkoff, blkcnt, blob, blobl, tstamp)) { free(blob); return RPMRC_FAIL; } free(blob); if (rpmpkgWriteslot(pkgdb, slot->slotno, pkgidx, newblkoff, blkcnt)) { return RPMRC_FAIL; } if (rpmpkgDelBlob(pkgdb, pkgidx, blkoff, blkcnt)) { return RPMRC_FAIL; } slot->blkoff = newblkoff; pkgdb->slotorder = SLOTORDER_UNORDERED; return RPMRC_OK; } static int rpmpkgAddSlotPage(rpmpkgdb pkgdb) { unsigned int cutoff; if (pkgdb->slotorder != SLOTORDER_BLKOFF) rpmpkgOrderSlots(pkgdb, SLOTORDER_BLKOFF); cutoff = (pkgdb->slotnpages + 1) * (PAGE_SIZE / BLK_SIZE); /* now move every blob before cutoff */ while (pkgdb->nslots && pkgdb->slots[0].blkoff < cutoff) { unsigned int newblkoff; pkgslot *slot = pkgdb->slots, *oldslot; oldslot = 0; if (rpmpkgFindEmptyOffset(pkgdb, slot->pkgidx, slot->blkcnt, &newblkoff, &oldslot, 1)) { return RPMRC_FAIL; } if (!oldslot || oldslot != slot) { return RPMRC_FAIL; } if (rpmpkgMoveBlob(pkgdb, slot, newblkoff)) { return RPMRC_FAIL; } rpmpkgOrderSlots(pkgdb, SLOTORDER_BLKOFF); } /* make sure our new page is empty */ if (rpmpkgValidateZero(pkgdb, pkgdb->slotnpages * (PAGE_SIZE / BLK_SIZE), PAGE_SIZE / BLK_SIZE)) { return RPMRC_FAIL; } if (rpmpkgWriteEmptySlotpage(pkgdb, pkgdb->slotnpages)) { return RPMRC_FAIL; } /* announce free page */ pkgdb->freeslot = pkgdb->slotnpages * (PAGE_SIZE / SLOT_SIZE); pkgdb->slotnpages++; pkgdb->generation++; if (rpmpkgWriteHeader(pkgdb)) { return RPMRC_FAIL; } return RPMRC_OK; } static int rpmpkgGetLock(rpmpkgdb pkgdb, int type) { if (!pkgdb->fd) return RPMRC_FAIL; if (flock(pkgdb->fd, type)) return RPMRC_FAIL; return RPMRC_OK; } int rpmpkgLock(rpmpkgdb pkgdb, int excl) { unsigned int *lockcntp = excl ? &pkgdb->locked_excl : &pkgdb->locked_shared; if (*lockcntp > 0 || (!excl && pkgdb->locked_excl)) { (*lockcntp)++; return RPMRC_OK; } pkgdb->header_ok = 0; if (rpmpkgGetLock(pkgdb, excl ? LOCK_EX : LOCK_SH)) { return RPMRC_FAIL; } (*lockcntp)++; return RPMRC_OK; } static int rpmpkgLockInternal(rpmpkgdb pkgdb, int excl) { if (excl && pkgdb->rdonly) return RPMRC_FAIL; return rpmpkgLock(pkgdb, excl); } int rpmpkgUnlock(rpmpkgdb pkgdb, int excl) { unsigned int *lockcntp = excl ? &pkgdb->locked_excl : &pkgdb->locked_shared; if (*lockcntp == 0) { return RPMRC_FAIL; } if (*lockcntp > 1 || (!excl && pkgdb->locked_excl)) { (*lockcntp)--; return RPMRC_OK; } if (excl && pkgdb->locked_shared) { /* excl -> shared switch */ if (rpmpkgGetLock(pkgdb, LOCK_SH)) { return RPMRC_FAIL; } (*lockcntp)--; return RPMRC_OK; } flock(pkgdb->fd, LOCK_UN); (*lockcntp)--; pkgdb->header_ok = 0; return RPMRC_OK; } static int rpmpkgLockReadHeader(rpmpkgdb pkgdb, int excl) { if (rpmpkgLockInternal(pkgdb, excl)) return RPMRC_FAIL; if (rpmpkgReadHeader(pkgdb)) { rpmpkgUnlock(pkgdb, excl); return RPMRC_FAIL; } return RPMRC_OK; } static int rpmpkgInitInternal(rpmpkgdb pkgdb) { struct stat stb; if (fstat(pkgdb->fd, &stb)) { return RPMRC_FAIL; } if (stb.st_size == 0) { if (rpmpkgWriteEmptySlotpage(pkgdb, 0)) { return RPMRC_FAIL; } pkgdb->slotnpages = 1; if (!pkgdb->nextpkgidx) pkgdb->nextpkgidx = 1; pkgdb->generation++; if (rpmpkgWriteHeader(pkgdb)) { return RPMRC_FAIL; } } return RPMRC_OK; } static int rpmpkgInit(rpmpkgdb pkgdb) { int rc; if (rpmpkgLockInternal(pkgdb, 1)) return RPMRC_FAIL; rc = rpmpkgInitInternal(pkgdb); rpmpkgUnlock(pkgdb, 1); return rc; } int rpmpkgOpen(rpmpkgdb *pkgdbp, const char *filename, int flags, int mode) { struct stat stb; rpmpkgdb pkgdb; *pkgdbp = 0; pkgdb = xcalloc(1, sizeof(*pkgdb)); pkgdb->filename = xstrdup(filename); if ((flags & (O_RDONLY|O_RDWR)) == O_RDONLY) pkgdb->rdonly = 1; if ((pkgdb->fd = open(filename, flags, mode)) == -1) { free(pkgdb->filename); free(pkgdb); return RPMRC_FAIL; } if (flags & O_CREAT) { char *filenameCopy; DIR *pdir; filenameCopy = xstrdup(pkgdb->filename); if ((pdir = opendir(dirname(filenameCopy))) == NULL) { free(filenameCopy); close(pkgdb->fd); free(pkgdb->filename); free(pkgdb); return RPMRC_FAIL; } if (fsync(dirfd(pdir)) == -1) { closedir(pdir); free(filenameCopy); close(pkgdb->fd); free(pkgdb->filename); free(pkgdb); return RPMRC_FAIL; } closedir(pdir); free(filenameCopy); } if (fstat(pkgdb->fd, &stb)) { close(pkgdb->fd); free(pkgdb->filename); free(pkgdb); return RPMRC_FAIL; } if (stb.st_size == 0) { if (rpmpkgInit(pkgdb)) { close(pkgdb->fd); free(pkgdb->filename); free(pkgdb); return RPMRC_FAIL; } } pkgdb->flags = flags; pkgdb->mode = mode; pkgdb->dofsync = 1; *pkgdbp = pkgdb; return RPMRC_OK; } void rpmpkgClose(rpmpkgdb pkgdb) { if (pkgdb->fd >= 0) { close(pkgdb->fd); pkgdb->fd = -1; } if (pkgdb->slots) free(pkgdb->slots); pkgdb->slots = 0; if (pkgdb->slothash) free(pkgdb->slothash); pkgdb->slothash = 0; free(pkgdb->filename); free(pkgdb); } void rpmpkgSetFsync(rpmpkgdb pkgdb, int dofsync) { pkgdb->dofsync = dofsync; } static int rpmpkgGetInternal(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned char **blobp, unsigned int *bloblp) { pkgslot *slot; unsigned char *blob; if (!pkgdb->slots && rpmpkgReadSlots(pkgdb)) { return RPMRC_FAIL; } slot = rpmpkgFindSlot(pkgdb, pkgidx); if (!slot) { return RPMRC_NOTFOUND; } blob = xmalloc((size_t)slot->blkcnt * BLK_SIZE); if (rpmpkgReadBlob(pkgdb, pkgidx, slot->blkoff, slot->blkcnt, blob, bloblp, (unsigned int *)0)) { free(blob); return RPMRC_FAIL; } *blobp = blob; return RPMRC_OK; } static int rpmpkgPutInternal(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned char *blob, unsigned int blobl) { unsigned int blkcnt, blkoff, slotno; pkgslot *oldslot; /* we always read all slots when writing, just in case */ if (rpmpkgReadSlots(pkgdb)) { return RPMRC_FAIL; } blkcnt = (BLOBHEAD_SIZE + blobl + BLOBTAIL_SIZE + BLK_SIZE - 1) / BLK_SIZE; /* find a nice place for the blob */ if (rpmpkgFindEmptyOffset(pkgdb, pkgidx, blkcnt, &blkoff, &oldslot, 0)) { return RPMRC_FAIL; } /* create new slot page if we don't have a free slot and can't reuse an old one */ if (!oldslot && !pkgdb->freeslot) { if (rpmpkgAddSlotPage(pkgdb)) { return RPMRC_FAIL; } /* redo rpmpkgFindEmptyOffset to get another free area */ if (rpmpkgFindEmptyOffset(pkgdb, pkgidx, blkcnt, &blkoff, &oldslot, 0)) { return RPMRC_FAIL; } } /* make sure that we don't overwrite data */ if (rpmpkgValidateZero(pkgdb, blkoff, blkcnt)) { return RPMRC_FAIL; } /* write new blob */ if (rpmpkgWriteBlob(pkgdb, pkgidx, blkoff, blkcnt, blob, blobl, (unsigned int)time(0))) { return RPMRC_FAIL; } /* write slot */ slotno = oldslot ? oldslot->slotno : pkgdb->freeslot; if (!slotno) { return RPMRC_FAIL; } if (rpmpkgWriteslot(pkgdb, slotno, pkgidx, blkoff, blkcnt)) { free(pkgdb->slots); pkgdb->slots = 0; return RPMRC_FAIL; } /* erase old blob */ if (oldslot && oldslot->blkoff) { if (rpmpkgDelBlob(pkgdb, pkgidx, oldslot->blkoff, oldslot->blkcnt)) { free(pkgdb->slots); pkgdb->slots = 0; return RPMRC_FAIL; } } if (oldslot) { /* just update the slot, no need to free the slot data */ oldslot->blkoff = blkoff; oldslot->blkcnt = blkcnt; pkgdb->slotorder = SLOTORDER_UNORDERED; } else { free(pkgdb->slots); pkgdb->slots = 0; } return RPMRC_OK; } static int rpmpkgDelInternal(rpmpkgdb pkgdb, unsigned int pkgidx) { pkgslot *slot; unsigned int blkoff, blkcnt; /* we always read all slots when writing, just in case */ if (rpmpkgReadSlots(pkgdb)) { return RPMRC_FAIL; } rpmpkgOrderSlots(pkgdb, SLOTORDER_BLKOFF); slot = rpmpkgFindSlot(pkgdb, pkgidx); if (!slot) { return RPMRC_OK; } if (rpmpkgWriteslot(pkgdb, slot->slotno, 0, 0, 0)) { return RPMRC_FAIL; } if (rpmpkgDelBlob(pkgdb, pkgidx, slot->blkoff, slot->blkcnt)) { return RPMRC_FAIL; } if (pkgdb->nslots > 1 && slot->blkoff < pkgdb->fileblks / 2) { /* we freed a blob in the first half of our data. do some extra work */ int i; if (slot == pkgdb->slots) { blkoff = pkgdb->slotnpages * (PAGE_SIZE / BLK_SIZE); } else { blkoff = slot[-1].blkoff + slot[-1].blkcnt; } if (slot < pkgdb->slots + pkgdb->nslots - 1) { blkcnt = slot[1].blkoff - blkoff; } else { blkcnt = slot->blkoff + slot->blkcnt - blkoff; } slot->blkoff = 0; slot->blkcnt = 0; slot = pkgdb->slots + pkgdb->nslots - 2; if (slot->blkcnt < slot[1].blkcnt) slot++; /* bigger slot first */ for (i = 0; i < 2; i++, slot++) { if (slot == pkgdb->slots + pkgdb->nslots) slot -= 2; if (!slot->blkoff || slot->blkoff < blkoff) continue; if (slot->blkoff < pkgdb->fileblks / 2) continue; if (slot->blkcnt > blkcnt) continue; rpmpkgMoveBlob(pkgdb, slot, blkoff); blkoff += slot->blkcnt; blkcnt -= slot->blkcnt; } rpmpkgOrderSlots(pkgdb, SLOTORDER_BLKOFF); } else { slot->blkoff = 0; slot->blkcnt = 0; } /* check if we can truncate the file */ slot = pkgdb->slots + pkgdb->nslots - 1; if (!slot->blkoff && pkgdb->nslots > 1) { slot--; } if (slot->blkoff) blkoff = slot->blkoff + slot->blkcnt; else blkoff = pkgdb->slotnpages * (PAGE_SIZE / BLK_SIZE); if (blkoff < pkgdb->fileblks / 4 * 3) { /* truncate the file */ if (!rpmpkgValidateZero(pkgdb, blkoff, pkgdb->fileblks - blkoff)) { if (!ftruncate(pkgdb->fd, blkoff * BLK_SIZE)) { pkgdb->fileblks = blkoff; } } } free(pkgdb->slots); pkgdb->slots = 0; return RPMRC_OK; } static int rpmpkgListInternal(rpmpkgdb pkgdb, unsigned int **pkgidxlistp, unsigned int *npkgidxlistp) { unsigned int i, nslots, *pkgidxlist; pkgslot *slot; if (!pkgdb->slots && rpmpkgReadSlots(pkgdb)) { return RPMRC_FAIL; } if (!pkgidxlistp) { *npkgidxlistp = pkgdb->nslots; return RPMRC_OK; } rpmpkgOrderSlots(pkgdb, SLOTORDER_BLKOFF); nslots = pkgdb->nslots; pkgidxlist = xcalloc(nslots + 1, sizeof(unsigned int)); for (i = 0, slot = pkgdb->slots; i < nslots; i++, slot++) { pkgidxlist[i] = slot->pkgidx; } *pkgidxlistp = pkgidxlist; *npkgidxlistp = nslots; return RPMRC_OK; } int rpmpkgGet(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned char **blobp, unsigned int *bloblp) { int rc; *blobp = 0; *bloblp = 0; if (!pkgidx) return RPMRC_FAIL; if (rpmpkgLockReadHeader(pkgdb, 0)) return RPMRC_FAIL; rc = rpmpkgGetInternal(pkgdb, pkgidx, blobp, bloblp); rpmpkgUnlock(pkgdb, 0); #ifdef RPMPKG_LZO if (!rc) rc = rpmpkgLZODecompress(blobp, bloblp); #endif return rc; } int rpmpkgPut(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned char *blob, unsigned int blobl) { int rc; if (!pkgidx) { return RPMRC_FAIL; } if (rpmpkgLockReadHeader(pkgdb, 1)) return RPMRC_FAIL; #ifdef RPMPKG_LZO if (rpmpkgLZOCompress(&blob, &blobl)) { rpmpkgUnlock(pkgdb, 1); return RPMRC_FAIL; } #endif rc = rpmpkgPutInternal(pkgdb, pkgidx, blob, blobl); #ifdef RPMPKG_LZO free(blob); #endif rpmpkgUnlock(pkgdb, 1); return rc; } int rpmpkgDel(rpmpkgdb pkgdb, unsigned int pkgidx) { int rc; if (!pkgidx) { return RPMRC_FAIL; } if (rpmpkgLockReadHeader(pkgdb, 1)) return RPMRC_FAIL; rc = rpmpkgDelInternal(pkgdb, pkgidx); rpmpkgUnlock(pkgdb, 1); return rc; } int rpmpkgList(rpmpkgdb pkgdb, unsigned int **pkgidxlistp, unsigned int *npkgidxlistp) { int rc; if (pkgidxlistp) *pkgidxlistp = 0; *npkgidxlistp = 0; if (rpmpkgLockReadHeader(pkgdb, 0)) return RPMRC_FAIL; rc = rpmpkgListInternal(pkgdb, pkgidxlistp, npkgidxlistp); rpmpkgUnlock(pkgdb, 0); return rc; } int rpmpkgNextPkgIdx(rpmpkgdb pkgdb, unsigned int *pkgidxp) { if (rpmpkgLockReadHeader(pkgdb, 1)) return RPMRC_FAIL; *pkgidxp = pkgdb->nextpkgidx++; if (rpmpkgWriteHeader(pkgdb)) { rpmpkgUnlock(pkgdb, 1); return RPMRC_FAIL; } /* no fsync needed. also no need to increase the generation count, * as the header is always read in */ rpmpkgUnlock(pkgdb, 1); return RPMRC_OK; } int rpmpkgGeneration(rpmpkgdb pkgdb, unsigned int *generationp) { if (rpmpkgLockReadHeader(pkgdb, 0)) return RPMRC_FAIL; *generationp = pkgdb->generation; rpmpkgUnlock(pkgdb, 0); return RPMRC_OK; } int rpmpkgStats(rpmpkgdb pkgdb) { unsigned int usedblks = 0; int i; if (rpmpkgLockReadHeader(pkgdb, 0)) return RPMRC_FAIL; if (rpmpkgReadSlots(pkgdb)) { rpmpkgUnlock(pkgdb, 0); return RPMRC_FAIL; } for (i = 0; i < pkgdb->nslots; i++) usedblks += pkgdb->slots[i].blkcnt; printf("--- Package DB Stats\n"); printf("Filename: %s\n", pkgdb->filename); printf("Generation: %d\n", pkgdb->generation); printf("Slot pages: %d\n", pkgdb->slotnpages); printf("Used slots: %d\n", pkgdb->nslots); printf("Free slots: %d\n", pkgdb->slotnpages * (PAGE_SIZE / SLOT_SIZE) - pkgdb->nslots); printf("Blob area size: %d\n", (pkgdb->fileblks - pkgdb->slotnpages * (PAGE_SIZE / BLK_SIZE)) * BLK_SIZE); printf("Blob area used: %d\n", usedblks * BLK_SIZE); rpmpkgUnlock(pkgdb, 0); return RPMRC_OK; } #ifdef RPMPKG_LZO #include "lzo/lzoconf.h" #include "lzo/lzo1x.h" #define BLOBLZO_MAGIC ('L' | 'Z' << 8 | 'O' << 16 | 'B' << 24) static int rpmpkgLZOCompress(unsigned char **blobp, unsigned int *bloblp) { unsigned char *blob = *blobp; unsigned int blobl = *bloblp; unsigned char *lzoblob, *workmem; unsigned int lzoblobl; lzo_uint blobl2; if (lzo_init() != LZO_E_OK) { return RPMRC_FAIL; } workmem = xmalloc(LZO1X_1_MEM_COMPRESS); lzoblobl = 4 + 4 + blobl + blobl / 16 + 64 + 3; lzoblob = xmalloc(lzoblobl); h2le(BLOBLZO_MAGIC, lzoblob); h2le(blobl, lzoblob + 4); if (lzo1x_1_compress(blob, blobl, lzoblob + 8, &blobl2, workmem) != LZO_E_OK) { free(workmem); free(lzoblob); return RPMRC_FAIL; } free(workmem); *blobp = lzoblob; *bloblp = 8 + blobl2; return RPMRC_OK; } static int rpmpkgLZODecompress(unsigned char **blobp, unsigned int *bloblp) { unsigned char *lzoblob = *blobp; unsigned int lzoblobl = *bloblp; unsigned char *blob; unsigned int blobl; lzo_uint blobl2; if (!lzoblob || lzoblobl < 8) return RPMRC_FAIL; if (le2h(lzoblob) != BLOBLZO_MAGIC) return RPMRC_FAIL; if (lzo_init() != LZO_E_OK) return RPMRC_FAIL; blobl = le2h(lzoblob + 4); blob = xmalloc(blobl ? blobl : 1); if (lzo1x_decompress(lzoblob + 8, lzoblobl - 8, blob, &blobl2, 0) != LZO_E_OK || blobl2 != blobl) { free(blob); return RPMRC_FAIL; } free(lzoblob); *blobp = blob; *bloblp = blobl; return RPMRC_OK; } #endif