3 #include <rpm/rpmlog.h>
19 #define RPMRC_NOTFOUND 1
23 static int rpmpkgLZOCompress(unsigned char **blobp, unsigned int *bloblp);
24 static int rpmpkgLZODecompress(unsigned char **blobp, unsigned int *bloblp);
27 static int rpmpkgVerifyblob(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned int blkoff, unsigned int blkcnt);
29 typedef struct pkgslot_s {
36 typedef struct rpmpkgdb_s {
37 int fd; /* our file descriptor */
43 unsigned int locked_shared;
44 unsigned int locked_excl;
46 int header_ok; /* header data (e.g. generation) is valid */
47 unsigned int generation;
48 unsigned int slotnpages;
49 unsigned int nextpkgidx;
51 struct pkgslot_s *slots;
52 unsigned int aslots; /* allocated slots */
53 unsigned int nslots; /* used slots */
55 unsigned int *slothash;
56 unsigned int nslothash;
58 unsigned int freeslot; /* first free slot */
62 unsigned int fileblks; /* file size in blks */
66 #define SLOTORDER_UNORDERED 0
67 #define SLOTORDER_BLKOFF 1
70 static inline unsigned int le2h(unsigned char *p)
72 return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24;
75 static inline void h2le(unsigned int x, unsigned char *p)
83 /* adler 32 algorithm taken from RFC 1950 */
84 #define ADLER32_INIT 1
85 static unsigned int update_adler32(unsigned int adler, unsigned char *buf, unsigned int len)
87 unsigned int s1 = adler & 0xffff;
88 unsigned int s2 = (adler >> 16) & 0xffff;
91 for (; len >= 5552; len -= 5552) {
92 for (n = 0; n < 5552; n++) {
99 for (n = 0; n < len; n++) {
103 return ((s2 % 65521) << 16) + (s1 % 65521);
106 /*** Header management ***/
108 #define PKGDB_MAGIC ('R' | 'p' << 8 | 'm' << 16 | 'P' << 24)
109 #define PKGDB_VERSION 0
111 /* must be a multiple of SLOT_SIZE! */
112 #define PKGDB_HEADER_SIZE 32
114 #define PKGDB_OFFSET_MAGIC 0
115 #define PKGDB_OFFSET_VERSION 4
116 #define PKGDB_OFFSET_GENERATION 8
117 #define PKGDB_OFFSET_SLOTNPAGES 12
118 #define PKGDB_OFFSET_NEXTPKGIDX 16
120 static int rpmpkgReadHeader(rpmpkgdb pkgdb)
122 unsigned int generation, slotnpages, nextpkgidx, version;
123 unsigned char header[PKGDB_HEADER_SIZE];
125 /* if we always head the write lock then our data matches */
126 if (pkgdb->header_ok)
128 if (pread(pkgdb->fd, header, PKGDB_HEADER_SIZE, 0) != PKGDB_HEADER_SIZE) {
131 if (le2h(header + PKGDB_OFFSET_MAGIC) != PKGDB_MAGIC) {
134 version = le2h(header + PKGDB_OFFSET_VERSION);
135 if (version != PKGDB_VERSION) {
136 rpmlog(RPMLOG_ERR, _("rpmpkg: Version mismatch. Expected version: %u. "
137 "Found version: %u\n"), PKGDB_VERSION, version);
140 generation = le2h(header + PKGDB_OFFSET_GENERATION);
141 slotnpages = le2h(header + PKGDB_OFFSET_SLOTNPAGES);
142 nextpkgidx = le2h(header + PKGDB_OFFSET_NEXTPKGIDX);
143 /* free slots if our internal data no longer matches */
144 if (pkgdb->slots && (pkgdb->generation != generation || pkgdb->slotnpages != slotnpages)) {
147 if (pkgdb->slothash) {
148 free(pkgdb->slothash);
152 pkgdb->generation = generation;
153 pkgdb->slotnpages = slotnpages;
154 pkgdb->nextpkgidx = nextpkgidx;
155 pkgdb->header_ok = 1;
159 static int rpmpkgWriteHeader(rpmpkgdb pkgdb)
161 unsigned char header[PKGDB_HEADER_SIZE];
162 memset(header, 0, sizeof(header));
163 h2le(PKGDB_MAGIC, header + PKGDB_OFFSET_MAGIC);
164 h2le(PKGDB_VERSION, header + PKGDB_OFFSET_VERSION);
165 h2le(pkgdb->generation, header + PKGDB_OFFSET_GENERATION);
166 h2le(pkgdb->slotnpages, header + PKGDB_OFFSET_SLOTNPAGES);
167 h2le(pkgdb->nextpkgidx, header + PKGDB_OFFSET_NEXTPKGIDX);
168 if (pwrite(pkgdb->fd, header, sizeof(header), 0) != sizeof(header)) {
171 if (pkgdb->dofsync && fsync(pkgdb->fd))
172 return RPMRC_FAIL; /* write error */
176 /*** Slot management ***/
178 #define SLOT_MAGIC ('S' | 'l' << 8 | 'o' << 16 | 't' << 24)
182 #define PAGE_SIZE 4096
184 /* the first slots (i.e. 32 bytes) are used for the header */
185 #define SLOT_START (PKGDB_HEADER_SIZE / SLOT_SIZE)
187 static inline unsigned int hashpkgidx(unsigned int h)
194 static int rpmpkgHashSlots(rpmpkgdb pkgdb)
196 unsigned int nslots, num;
198 unsigned int h, hh, hmask;
202 pkgdb->nslothash = 0;
204 while (num & (num - 1))
205 num = num & (num - 1);
207 hash = pkgdb->slothash;
208 if (!hash || pkgdb->nslothash != num) {
209 free(pkgdb->slothash);
210 hash = pkgdb->slothash = calloc(num, sizeof(unsigned int));
213 pkgdb->nslothash = num;
215 memset(hash, 0, num * sizeof(unsigned int));
218 nslots = pkgdb->nslots;
219 for (i = 0, slot = pkgdb->slots; i < nslots; i++, slot++) {
220 for (h = hashpkgidx(slot->pkgidx) & hmask, hh = 7; hash[h] != 0; h = (h + hh++) & hmask)
224 pkgdb->slothash = hash;
225 pkgdb->nslothash = num;
229 static int rpmpkgReadSlots(rpmpkgdb pkgdb)
231 unsigned int slotnpages = pkgdb->slotnpages;
233 unsigned char pagebuf[PAGE_SIZE];
235 unsigned int i, minblkoff, fileblks, slotno, freeslot, o;
238 /* free old slot data */
243 if (pkgdb->slothash) {
244 free(pkgdb->slothash);
250 /* calculate current database size in blks */
251 if (fstat(pkgdb->fd, &stb))
253 if (stb.st_size % BLK_SIZE)
254 return RPMRC_FAIL; /* hmm */
255 fileblks = stb.st_size / BLK_SIZE;
257 /* read (and somewhat verify) all slots */
258 pkgdb->aslots = slotnpages * (PAGE_SIZE / SLOT_SIZE);
259 pkgdb->slots = calloc(pkgdb->aslots, sizeof(*pkgdb->slots));
265 minblkoff = slotnpages * (PAGE_SIZE / BLK_SIZE);
268 for (page = 0; page < slotnpages; page++) {
269 if (pread(pkgdb->fd, pagebuf, PAGE_SIZE, page * PAGE_SIZE) != PAGE_SIZE)
271 for (o = page ? 0 : SLOT_START * SLOT_SIZE; o < PAGE_SIZE; o += SLOT_SIZE, slotno++) {
272 unsigned char *pp = pagebuf + o;
273 unsigned int blkoff, blkcnt, pkgidx;
274 if (le2h(pp) != SLOT_MAGIC) {
277 blkoff = le2h(pp + 8);
283 pkgidx = le2h(pp + 4);
284 blkcnt = le2h(pp + 12);
285 slot->pkgidx = pkgidx;
286 slot->blkoff = blkoff;
287 slot->blkcnt = blkcnt;
288 slot->slotno = slotno;
289 if (slot->blkoff + slot->blkcnt > fileblks)
290 return RPMRC_FAIL; /* truncated database */
291 if (!slot->pkgidx || !slot->blkcnt || slot->blkoff < minblkoff)
292 return RPMRC_FAIL; /* bad entry */
298 pkgdb->slotorder = SLOTORDER_UNORDERED; /* XXX: always order? */
299 pkgdb->fileblks = fileblks;
300 pkgdb->freeslot = freeslot;
301 if (rpmpkgHashSlots(pkgdb)) {
309 static int orderslots_blkoff_cmp(const void *a, const void *b)
311 unsigned int blkoffa = ((const pkgslot *)a)->blkoff;
312 unsigned int blkoffb = ((const pkgslot *)b)->blkoff;
313 return blkoffa > blkoffb ? 1 : blkoffa < blkoffb ? -1 : 0;
316 static void rpmpkgOrderSlots(rpmpkgdb pkgdb, int slotorder)
318 if (pkgdb->slotorder == slotorder)
320 if (slotorder == SLOTORDER_BLKOFF) {
321 if (pkgdb->nslots > 1)
322 qsort(pkgdb->slots, pkgdb->nslots, sizeof(*pkgdb->slots), orderslots_blkoff_cmp);
324 pkgdb->slotorder = slotorder;
325 rpmpkgHashSlots(pkgdb);
328 static inline pkgslot *rpmpkgFindSlot(rpmpkgdb pkgdb, unsigned int pkgidx)
330 unsigned int i, h, hh, hmask = pkgdb->nslothash - 1;
331 unsigned int *hash = pkgdb->slothash;
333 for (h = hashpkgidx(pkgidx) & hmask, hh = 7; (i = hash[h]) != 0; h = (h + hh++) & hmask)
334 if (pkgdb->slots[i - 1].pkgidx == pkgidx)
335 return pkgdb->slots + (i - 1);
339 static int rpmpkgFindEmptyOffset(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned int blkcnt, unsigned *blkoffp, pkgslot **oldslotp, int dontprepend)
341 unsigned int i, nslots = pkgdb->nslots;
342 unsigned int bestblkoff = 0;
343 unsigned int freecnt, bestfreecnt = 0;
344 unsigned int lastblkend = pkgdb->slotnpages * (PAGE_SIZE / BLK_SIZE);
345 pkgslot *slot, *oldslot = 0;
347 if (pkgdb->slotorder != SLOTORDER_BLKOFF)
348 rpmpkgOrderSlots(pkgdb, SLOTORDER_BLKOFF);
350 if (dontprepend && nslots) {
351 lastblkend = pkgdb->slots[0].blkoff;
353 /* best fit strategy */
354 for (i = 0, slot = pkgdb->slots; i < nslots; i++, slot++) {
355 if (slot->blkoff < lastblkend) {
356 return RPMRC_FAIL; /* eek, slots overlap! */
358 if (slot->pkgidx == pkgidx) {
360 return RPMRC_FAIL; /* eek, two slots with our pkgid ! */
364 freecnt = slot->blkoff - lastblkend;
365 if (freecnt >= blkcnt) {
366 if (!bestblkoff || bestfreecnt > freecnt) {
367 bestblkoff = lastblkend;
368 bestfreecnt = freecnt;
371 lastblkend = slot->blkoff + slot->blkcnt;
374 bestblkoff = lastblkend; /* append to end */
377 *blkoffp = bestblkoff;
381 static int rpmpkgNeighbourCheck(rpmpkgdb pkgdb, unsigned int blkoff, unsigned int blkcnt, unsigned int *newblkcnt)
383 unsigned int i, nslots = pkgdb->nslots;
384 unsigned int lastblkend = pkgdb->slotnpages * (PAGE_SIZE / BLK_SIZE);
385 pkgslot *slot, *left = 0, *right = 0;
387 if (pkgdb->slotorder != SLOTORDER_BLKOFF)
388 rpmpkgOrderSlots(pkgdb, SLOTORDER_BLKOFF);
389 if (blkoff < lastblkend)
391 for (i = 0, slot = pkgdb->slots; i < nslots; i++, slot++) {
392 if (slot->blkoff < lastblkend)
393 return RPMRC_FAIL; /* eek, slots overlap! */
394 if (slot->blkoff < blkoff)
396 if (!right && slot->blkoff >= blkoff)
398 lastblkend = slot->blkoff + slot->blkcnt;
400 if (left && left->blkoff + left->blkcnt != blkoff)
401 return RPMRC_FAIL; /* must always start right after the block */
402 if (!left && blkoff != pkgdb->slotnpages * (PAGE_SIZE / BLK_SIZE))
404 if (right && right->blkoff < blkoff + blkcnt)
406 /* check if neighbour blobs are in good shape */
407 if (left && rpmpkgVerifyblob(pkgdb, left->pkgidx, left->blkoff, left->blkcnt) != RPMRC_OK)
409 if (right && rpmpkgVerifyblob(pkgdb, right->pkgidx, right->blkoff, right->blkcnt) != RPMRC_OK)
411 *newblkcnt = right ? right->blkoff - blkoff : blkcnt;
412 /* bounds are intect. free area. */
416 static int rpmpkgWriteslot(rpmpkgdb pkgdb, unsigned int slotno, unsigned int pkgidx, unsigned int blkoff, unsigned int blkcnt)
418 unsigned char buf[SLOT_SIZE];
420 if (slotno < SLOT_START)
422 if (blkoff && slotno == pkgdb->freeslot)
424 h2le(SLOT_MAGIC, buf);
425 h2le(pkgidx, buf + 4);
426 h2le(blkoff, buf + 8);
427 h2le(blkcnt, buf + 12);
428 if (pwrite(pkgdb->fd, buf, sizeof(buf), slotno * SLOT_SIZE) != sizeof(buf)) {
432 if (rpmpkgWriteHeader(pkgdb)) {
438 static int rpmpkgWriteEmptySlotpage(rpmpkgdb pkgdb, int pageno)
440 unsigned char page[PAGE_SIZE];
441 int i, off = pageno ? 0 : SLOT_START * SLOT_SIZE;
442 memset(page, 0, sizeof(page));
443 for (i = 0; i < PAGE_SIZE / SLOT_SIZE; i++)
444 h2le(SLOT_MAGIC, page + i * SLOT_SIZE);
445 if (pwrite(pkgdb->fd, page, PAGE_SIZE - off, pageno * PAGE_SIZE + off) != PAGE_SIZE - off) {
448 if (pkgdb->dofsync && fsync(pkgdb->fd)) {
449 return RPMRC_FAIL; /* write error */
454 /*** Blk primitives ***/
456 static int rpmpkgZeroBlks(rpmpkgdb pkgdb, unsigned int blkoff, unsigned int blkcnt)
458 unsigned char buf[65536];
459 unsigned int towrite;
462 memset(buf, 0, sizeof(buf));
463 fileoff = (off_t)blkoff * BLK_SIZE;
464 for (towrite = blkcnt * BLK_SIZE; towrite; ) {
465 unsigned int chunk = towrite > 65536 ? 65536 : towrite;
466 if (pwrite(pkgdb->fd, buf, chunk, fileoff) != chunk) {
467 return RPMRC_FAIL; /* write error */
472 if (blkoff + blkcnt > pkgdb->fileblks)
473 pkgdb->fileblks = blkoff + blkcnt;
477 static int rpmpkgValidateZeroCheck(rpmpkgdb pkgdb, unsigned int blkoff, unsigned int blkcnt)
479 unsigned long long buf[(65536 / sizeof(unsigned long long)) + 1];
484 if (blkoff > pkgdb->fileblks)
485 return RPMRC_FAIL; /* huh? */
486 fileoff = (off_t)blkoff * BLK_SIZE;
487 tocheck = blkoff + blkcnt > pkgdb->fileblks ? pkgdb->fileblks - blkoff : blkcnt;
489 while (tocheck >= 65536) {
490 if (pread(pkgdb->fd, (void *)buf, 65536, fileoff) != 65536)
491 return RPMRC_FAIL; /* read error */
492 for (i = 0; i < 65536 / sizeof(unsigned long long); i++)
494 return RPMRC_FAIL; /* not empty */
499 int cnt = (int)tocheck / sizeof(unsigned long long);
501 if (pread(pkgdb->fd, (void *)buf, tocheck, fileoff) != tocheck)
502 return RPMRC_FAIL; /* read error */
503 for (i = 0; i < cnt; i++)
505 return RPMRC_FAIL; /* not empty */
510 static int rpmpkgValidateZero(rpmpkgdb pkgdb, unsigned int blkoff, unsigned int blkcnt)
512 if (rpmpkgValidateZeroCheck(pkgdb, blkoff, blkcnt) == RPMRC_OK)
514 rpmlog(RPMLOG_WARNING, _("rpmpkg: detected non-zero blob, trying auto repair\n"));
515 /* auto-repair interrupted transactions */
516 if (rpmpkgNeighbourCheck(pkgdb, blkoff, blkcnt, &blkcnt) != RPMRC_OK)
518 if (rpmpkgZeroBlks(pkgdb, blkoff, blkcnt) != RPMRC_OK)
524 /*** Blob primitives ***/
526 /* head: magic + pkgidx + timestamp + bloblen */
527 /* tail: adler32 + bloblen + magic */
529 #define BLOBHEAD_MAGIC ('B' | 'l' << 8 | 'b' << 16 | 'S' << 24)
530 #define BLOBTAIL_MAGIC ('B' | 'l' << 8 | 'b' << 16 | 'E' << 24)
532 #define BLOBHEAD_SIZE (4 + 4 + 4 + 4)
533 #define BLOBTAIL_SIZE (4 + 4 + 4)
535 static int rpmpkgReadBlob(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned int blkoff, unsigned int blkcnt, unsigned char *blob, unsigned int *bloblp, unsigned int *tstampp)
537 unsigned char buf[BLOBHEAD_SIZE > BLOBTAIL_SIZE ? BLOBHEAD_SIZE : BLOBTAIL_SIZE];
538 unsigned int bloblen, toread, tstamp;
541 int verifyadler = bloblp ? 0 : 1;
544 if (blkcnt < (BLOBHEAD_SIZE + BLOBTAIL_SIZE + BLK_SIZE - 1) / BLK_SIZE)
545 return RPMRC_FAIL; /* blkcnt too small */
547 fileoff = (off_t)blkoff * BLK_SIZE;
548 if (pread(pkgdb->fd, buf, BLOBHEAD_SIZE, fileoff) != BLOBHEAD_SIZE)
549 return RPMRC_FAIL; /* read error */
550 if (le2h(buf) != BLOBHEAD_MAGIC)
551 return RPMRC_FAIL; /* bad blob */
552 if (le2h(buf + 4) != pkgidx)
553 return RPMRC_FAIL; /* bad blob */
554 tstamp = le2h(buf + 8);
555 bloblen = le2h(buf + 12);
556 if (blkcnt != (BLOBHEAD_SIZE + bloblen + BLOBTAIL_SIZE + BLK_SIZE - 1) / BLK_SIZE)
557 return RPMRC_FAIL; /* bad blob */
560 adl = update_adler32(adl, buf, BLOBHEAD_SIZE);
561 /* read in 64K chunks */
562 fileoff += BLOBHEAD_SIZE;
563 toread = blkcnt * BLK_SIZE - BLOBHEAD_SIZE;
565 toread -= BLOBTAIL_SIZE;
567 unsigned int chunk = toread > 65536 ? 65536 : toread;
568 if (pread(pkgdb->fd, blob, chunk, fileoff) != chunk) {
569 return RPMRC_FAIL; /* read error */
573 adl = update_adler32(adl, blob, chunk);
574 else if (toread > BLOBTAIL_SIZE)
575 adl = update_adler32(adl, blob, toread - BLOBTAIL_SIZE > chunk ? chunk : toread - BLOBTAIL_SIZE);
584 memcpy(buf, blob - BLOBTAIL_SIZE, BLOBTAIL_SIZE);
585 } else if (pread(pkgdb->fd, buf, BLOBTAIL_SIZE, fileoff) != BLOBTAIL_SIZE) {
586 return RPMRC_FAIL; /* read error */
588 if (verifyadler && le2h(buf) != adl) {
589 return RPMRC_FAIL; /* bad blob, adler32 mismatch */
591 if (le2h(buf + 4) != bloblen) {
592 return RPMRC_FAIL; /* bad blob, bloblen mismatch */
594 if (le2h(buf + 8) != BLOBTAIL_MAGIC) {
595 return RPMRC_FAIL; /* bad blob */
604 static int rpmpkgVerifyblob(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned int blkoff, unsigned int blkcnt)
606 unsigned char buf[65536];
607 return rpmpkgReadBlob(pkgdb, pkgidx, blkoff, blkcnt, buf, 0, 0);
610 static int rpmpkgWriteBlob(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned int blkoff, unsigned int blkcnt, unsigned char *blob, unsigned int blobl, unsigned int now)
612 unsigned char buf[(BLOBHEAD_SIZE > BLOBTAIL_SIZE ? BLOBHEAD_SIZE : BLOBTAIL_SIZE) + BLK_SIZE];
613 unsigned int towrite, pad;
618 if (blkcnt < (BLOBHEAD_SIZE + BLOBTAIL_SIZE + BLK_SIZE - 1) / BLK_SIZE)
619 return RPMRC_FAIL; /* blkcnt too small */
620 if (blkcnt != (BLOBHEAD_SIZE + blobl + BLOBTAIL_SIZE + BLK_SIZE - 1) / BLK_SIZE)
621 return RPMRC_FAIL; /* blkcnt mismatch */
622 fileoff = (off_t)blkoff * BLK_SIZE;
623 h2le(BLOBHEAD_MAGIC, buf);
624 h2le(pkgidx, buf + 4);
626 h2le(blobl, buf + 12);
627 if (pwrite(pkgdb->fd, buf, BLOBHEAD_SIZE, fileoff) != BLOBHEAD_SIZE) {
628 return RPMRC_FAIL; /* write error */
631 adl = update_adler32(adl, buf, BLOBHEAD_SIZE);
632 /* write in 64K chunks */
633 fileoff += BLOBHEAD_SIZE;
634 for (towrite = blobl; towrite;) {
635 unsigned int chunk = towrite > 65536 ? 65536 : towrite;
636 if (pwrite(pkgdb->fd, blob, chunk, fileoff) != chunk) {
637 return RPMRC_FAIL; /* write error */
639 adl = update_adler32(adl, blob, chunk);
645 pad = blkcnt * BLK_SIZE - (BLOBHEAD_SIZE + blobl + BLOBTAIL_SIZE);
647 memset(buf + (sizeof(buf) - BLOBTAIL_SIZE) - pad, 0, pad);
648 adl = update_adler32(adl, buf + (sizeof(buf) - BLOBTAIL_SIZE) - pad, pad);
650 h2le(adl, buf + (sizeof(buf) - BLOBTAIL_SIZE));
651 h2le(blobl, buf + (sizeof(buf) - BLOBTAIL_SIZE) + 4);
652 h2le(BLOBTAIL_MAGIC, buf + (sizeof(buf) - BLOBTAIL_SIZE) + 8);
653 if (pwrite(pkgdb->fd, buf + (sizeof(buf) - BLOBTAIL_SIZE) - pad, pad + BLOBTAIL_SIZE, fileoff) != pad + BLOBTAIL_SIZE) {
654 return RPMRC_FAIL; /* write error */
656 /* update file length */
657 if (blkoff + blkcnt > pkgdb->fileblks)
658 pkgdb->fileblks = blkoff + blkcnt;
659 if (pkgdb->dofsync && fsync(pkgdb->fd)) {
660 return RPMRC_FAIL; /* write error */
665 static int rpmpkgDelBlob(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned int blkoff, unsigned int blkcnt)
667 if (rpmpkgVerifyblob(pkgdb, pkgidx, blkoff, blkcnt))
669 if (rpmpkgZeroBlks(pkgdb, blkoff, blkcnt))
671 if (pkgdb->dofsync && fsync(pkgdb->fd))
672 return RPMRC_FAIL; /* write error */
677 static int rpmpkgMoveBlob(rpmpkgdb pkgdb, pkgslot *slot, unsigned int newblkoff)
679 unsigned int pkgidx = slot->pkgidx;
680 unsigned int blkoff = slot->blkoff;
681 unsigned int blkcnt = slot->blkcnt;
683 unsigned int tstamp, blobl;
685 blob = malloc((size_t)blkcnt * BLK_SIZE);
686 if (rpmpkgReadBlob(pkgdb, pkgidx, blkoff, blkcnt, blob, &blobl, &tstamp)) {
690 if (rpmpkgWriteBlob(pkgdb, pkgidx, newblkoff, blkcnt, blob, blobl, tstamp)) {
695 if (rpmpkgWriteslot(pkgdb, slot->slotno, pkgidx, newblkoff, blkcnt)) {
698 if (rpmpkgDelBlob(pkgdb, pkgidx, blkoff, blkcnt)) {
701 slot->blkoff = newblkoff;
702 pkgdb->slotorder = SLOTORDER_UNORDERED;
706 static int rpmpkgAddSlotPage(rpmpkgdb pkgdb)
709 if (pkgdb->slotorder != SLOTORDER_BLKOFF)
710 rpmpkgOrderSlots(pkgdb, SLOTORDER_BLKOFF);
711 cutoff = (pkgdb->slotnpages + 1) * (PAGE_SIZE / BLK_SIZE);
713 /* now move every blob before cutoff */
714 while (pkgdb->nslots && pkgdb->slots[0].blkoff < cutoff) {
715 unsigned int newblkoff;
716 pkgslot *slot = pkgdb->slots, *oldslot;
719 if (rpmpkgFindEmptyOffset(pkgdb, slot->pkgidx, slot->blkcnt, &newblkoff, &oldslot, 1)) {
722 if (!oldslot || oldslot != slot) {
725 if (rpmpkgMoveBlob(pkgdb, slot, newblkoff)) {
728 rpmpkgOrderSlots(pkgdb, SLOTORDER_BLKOFF);
731 /* make sure our new page is empty */
732 if (rpmpkgValidateZero(pkgdb, pkgdb->slotnpages * (PAGE_SIZE / BLK_SIZE), PAGE_SIZE / BLK_SIZE)) {
735 if (rpmpkgWriteEmptySlotpage(pkgdb, pkgdb->slotnpages)) {
739 /* announce free page */
740 pkgdb->freeslot = pkgdb->slotnpages * (PAGE_SIZE / SLOT_SIZE);
743 if (rpmpkgWriteHeader(pkgdb)) {
749 static int rpmpkgGetLock(rpmpkgdb pkgdb, int type)
753 if (flock(pkgdb->fd, type))
758 int rpmpkgLock(rpmpkgdb pkgdb, int excl)
760 unsigned int *lockcntp = excl ? &pkgdb->locked_excl : &pkgdb->locked_shared;
761 if (*lockcntp > 0 || (!excl && pkgdb->locked_excl)) {
765 pkgdb->header_ok = 0;
766 if (rpmpkgGetLock(pkgdb, excl ? LOCK_EX : LOCK_SH)) {
773 static int rpmpkgLockInternal(rpmpkgdb pkgdb, int excl)
775 if (excl && pkgdb->rdonly)
778 return rpmpkgLock(pkgdb, excl);
781 int rpmpkgUnlock(rpmpkgdb pkgdb, int excl)
783 unsigned int *lockcntp = excl ? &pkgdb->locked_excl : &pkgdb->locked_shared;
784 if (*lockcntp == 0) {
787 if (*lockcntp > 1 || (!excl && pkgdb->locked_excl)) {
791 if (excl && pkgdb->locked_shared) {
792 /* excl -> shared switch */
793 if (rpmpkgGetLock(pkgdb, LOCK_SH)) {
799 flock(pkgdb->fd, LOCK_UN);
801 pkgdb->header_ok = 0;
805 static int rpmpkgLockReadHeader(rpmpkgdb pkgdb, int excl)
807 if (rpmpkgLockInternal(pkgdb, excl))
809 if (rpmpkgReadHeader(pkgdb)) {
810 rpmpkgUnlock(pkgdb, excl);
816 static int rpmpkgInitInternal(rpmpkgdb pkgdb)
819 if (fstat(pkgdb->fd, &stb)) {
822 if (stb.st_size == 0) {
823 if (rpmpkgWriteEmptySlotpage(pkgdb, 0)) {
826 pkgdb->slotnpages = 1;
827 if (!pkgdb->nextpkgidx)
828 pkgdb->nextpkgidx = 1;
830 if (rpmpkgWriteHeader(pkgdb)) {
837 static int rpmpkgInit(rpmpkgdb pkgdb)
841 if (rpmpkgLockInternal(pkgdb, 1))
843 rc = rpmpkgInitInternal(pkgdb);
844 rpmpkgUnlock(pkgdb, 1);
848 int rpmpkgOpen(rpmpkgdb *pkgdbp, const char *filename, int flags, int mode)
854 pkgdb = calloc(1, sizeof(*pkgdb));
855 pkgdb->filename = strdup(filename);
856 if (!pkgdb->filename) {
860 if ((flags & (O_RDONLY|O_RDWR)) == O_RDONLY)
862 if ((pkgdb->fd = open(filename, flags, mode)) == -1) {
863 free(pkgdb->filename);
867 if (flags & O_CREAT) {
871 if ((filenameCopy = strdup(pkgdb->filename)) == NULL) {
873 free(pkgdb->filename);
878 if ((pdir = opendir(dirname(filenameCopy))) == NULL) {
881 free(pkgdb->filename);
886 if (fsync(dirfd(pdir)) == -1) {
890 free(pkgdb->filename);
898 if (fstat(pkgdb->fd, &stb)) {
900 free(pkgdb->filename);
904 if (stb.st_size == 0) {
905 if (rpmpkgInit(pkgdb)) {
907 free(pkgdb->filename);
912 pkgdb->flags = flags;
919 void rpmpkgClose(rpmpkgdb pkgdb)
921 if (pkgdb->fd >= 0) {
929 free(pkgdb->slothash);
931 free(pkgdb->filename);
935 void rpmpkgSetFsync(rpmpkgdb pkgdb, int dofsync)
937 pkgdb->dofsync = dofsync;
941 static int rpmpkgGetInternal(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned char **blobp, unsigned int *bloblp)
946 if (!pkgdb->slots && rpmpkgReadSlots(pkgdb)) {
949 slot = rpmpkgFindSlot(pkgdb, pkgidx);
951 return RPMRC_NOTFOUND;
953 blob = malloc((size_t)slot->blkcnt * BLK_SIZE);
954 if (rpmpkgReadBlob(pkgdb, pkgidx, slot->blkoff, slot->blkcnt, blob, bloblp, (unsigned int *)0)) {
962 static int rpmpkgPutInternal(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned char *blob, unsigned int blobl)
964 unsigned int blkcnt, blkoff, slotno;
967 /* we always read all slots when writing, just in case */
968 if (rpmpkgReadSlots(pkgdb)) {
971 blkcnt = (BLOBHEAD_SIZE + blobl + BLOBTAIL_SIZE + BLK_SIZE - 1) / BLK_SIZE;
972 /* find a nice place for the blob */
973 if (rpmpkgFindEmptyOffset(pkgdb, pkgidx, blkcnt, &blkoff, &oldslot, 0)) {
976 /* create new slot page if we don't have a free slot and can't reuse an old one */
977 if (!oldslot && !pkgdb->freeslot) {
978 if (rpmpkgAddSlotPage(pkgdb)) {
981 /* redo rpmpkgFindEmptyOffset to get another free area */
982 if (rpmpkgFindEmptyOffset(pkgdb, pkgidx, blkcnt, &blkoff, &oldslot, 0)) {
986 /* make sure that we don't overwrite data */
987 if (rpmpkgValidateZero(pkgdb, blkoff, blkcnt)) {
991 if (rpmpkgWriteBlob(pkgdb, pkgidx, blkoff, blkcnt, blob, blobl, (unsigned int)time(0))) {
995 slotno = oldslot ? oldslot->slotno : pkgdb->freeslot;
999 if (rpmpkgWriteslot(pkgdb, slotno, pkgidx, blkoff, blkcnt)) {
1004 /* erase old blob */
1005 if (oldslot && oldslot->blkoff) {
1006 if (rpmpkgDelBlob(pkgdb, pkgidx, oldslot->blkoff, oldslot->blkcnt)) {
1013 /* just update the slot, no need to free the slot data */
1014 oldslot->blkoff = blkoff;
1015 oldslot->blkcnt = blkcnt;
1016 pkgdb->slotorder = SLOTORDER_UNORDERED;
1024 static int rpmpkgDelInternal(rpmpkgdb pkgdb, unsigned int pkgidx)
1027 unsigned int blkoff, blkcnt;
1029 /* we always read all slots when writing, just in case */
1030 if (rpmpkgReadSlots(pkgdb)) {
1033 rpmpkgOrderSlots(pkgdb, SLOTORDER_BLKOFF);
1034 slot = rpmpkgFindSlot(pkgdb, pkgidx);
1038 if (rpmpkgWriteslot(pkgdb, slot->slotno, 0, 0, 0)) {
1041 if (rpmpkgDelBlob(pkgdb, pkgidx, slot->blkoff, slot->blkcnt)) {
1044 if (pkgdb->nslots > 1 && slot->blkoff < pkgdb->fileblks / 2) {
1045 /* we freed a blob in the first half of our data. do some extra work */
1047 if (slot == pkgdb->slots) {
1048 blkoff = pkgdb->slotnpages * (PAGE_SIZE / BLK_SIZE);
1050 blkoff = slot[-1].blkoff + slot[-1].blkcnt;
1052 if (slot < pkgdb->slots + pkgdb->nslots - 1) {
1053 blkcnt = slot[1].blkoff - blkoff;
1055 blkcnt = slot->blkoff + slot->blkcnt - blkoff;
1059 slot = pkgdb->slots + pkgdb->nslots - 2;
1060 if (slot->blkcnt < slot[1].blkcnt)
1061 slot++; /* bigger slot first */
1062 for (i = 0; i < 2; i++, slot++) {
1063 if (slot == pkgdb->slots + pkgdb->nslots)
1065 if (!slot->blkoff || slot->blkoff < blkoff)
1067 if (slot->blkoff < pkgdb->fileblks / 2)
1069 if (slot->blkcnt > blkcnt)
1071 rpmpkgMoveBlob(pkgdb, slot, blkoff);
1072 blkoff += slot->blkcnt;
1073 blkcnt -= slot->blkcnt;
1075 rpmpkgOrderSlots(pkgdb, SLOTORDER_BLKOFF);
1080 /* check if we can truncate the file */
1081 slot = pkgdb->slots + pkgdb->nslots - 1;
1082 if (!slot->blkoff && pkgdb->nslots > 1) {
1086 blkoff = slot->blkoff + slot->blkcnt;
1088 blkoff = pkgdb->slotnpages * (PAGE_SIZE / BLK_SIZE);
1089 if (blkoff < pkgdb->fileblks / 4 * 3) {
1090 /* truncate the file */
1091 if (!rpmpkgValidateZero(pkgdb, blkoff, pkgdb->fileblks - blkoff)) {
1092 if (!ftruncate(pkgdb->fd, blkoff * BLK_SIZE)) {
1093 pkgdb->fileblks = blkoff;
1102 static int rpmpkgListInternal(rpmpkgdb pkgdb, unsigned int **pkgidxlistp, unsigned int *npkgidxlistp)
1104 unsigned int i, nslots, *pkgidxlist;
1107 if (!pkgdb->slots && rpmpkgReadSlots(pkgdb)) {
1111 *npkgidxlistp = pkgdb->nslots;
1114 rpmpkgOrderSlots(pkgdb, SLOTORDER_BLKOFF);
1115 nslots = pkgdb->nslots;
1116 pkgidxlist = calloc(nslots + 1, sizeof(unsigned int));
1117 for (i = 0, slot = pkgdb->slots; i < nslots; i++, slot++) {
1118 pkgidxlist[i] = slot->pkgidx;
1120 *pkgidxlistp = pkgidxlist;
1121 *npkgidxlistp = nslots;
1125 int rpmpkgGet(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned char **blobp, unsigned int *bloblp)
1133 if (rpmpkgLockReadHeader(pkgdb, 0))
1135 rc = rpmpkgGetInternal(pkgdb, pkgidx, blobp, bloblp);
1136 rpmpkgUnlock(pkgdb, 0);
1139 rc = rpmpkgLZODecompress(blobp, bloblp);
1144 int rpmpkgPut(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned char *blob, unsigned int blobl)
1151 if (rpmpkgLockReadHeader(pkgdb, 1))
1154 if (rpmpkgLZOCompress(&blob, &blobl)) {
1155 rpmpkgUnlock(pkgdb, 1);
1159 rc = rpmpkgPutInternal(pkgdb, pkgidx, blob, blobl);
1163 rpmpkgUnlock(pkgdb, 1);
1167 int rpmpkgDel(rpmpkgdb pkgdb, unsigned int pkgidx)
1174 if (rpmpkgLockReadHeader(pkgdb, 1))
1176 rc = rpmpkgDelInternal(pkgdb, pkgidx);
1177 rpmpkgUnlock(pkgdb, 1);
1181 int rpmpkgList(rpmpkgdb pkgdb, unsigned int **pkgidxlistp, unsigned int *npkgidxlistp)
1187 if (rpmpkgLockReadHeader(pkgdb, 0))
1189 rc = rpmpkgListInternal(pkgdb, pkgidxlistp, npkgidxlistp);
1190 rpmpkgUnlock(pkgdb, 0);
1194 int rpmpkgNextPkgIdx(rpmpkgdb pkgdb, unsigned int *pkgidxp)
1196 if (rpmpkgLockReadHeader(pkgdb, 1))
1198 *pkgidxp = pkgdb->nextpkgidx++;
1199 if (rpmpkgWriteHeader(pkgdb)) {
1200 rpmpkgUnlock(pkgdb, 1);
1203 /* no fsync needed. also no need to increase the generation count,
1204 * as the header is always read in */
1205 rpmpkgUnlock(pkgdb, 1);
1209 int rpmpkgGeneration(rpmpkgdb pkgdb, unsigned int *generationp)
1211 if (rpmpkgLockReadHeader(pkgdb, 0))
1213 *generationp = pkgdb->generation;
1214 rpmpkgUnlock(pkgdb, 0);
1218 int rpmpkgStats(rpmpkgdb pkgdb)
1220 unsigned int usedblks = 0;
1223 if (rpmpkgLockReadHeader(pkgdb, 0))
1225 if (rpmpkgReadSlots(pkgdb)) {
1226 rpmpkgUnlock(pkgdb, 0);
1229 for (i = 0; i < pkgdb->nslots; i++)
1230 usedblks += pkgdb->slots[i].blkcnt;
1231 printf("--- Package DB Stats\n");
1232 printf("Filename: %s\n", pkgdb->filename);
1233 printf("Generation: %d\n", pkgdb->generation);
1234 printf("Slot pages: %d\n", pkgdb->slotnpages);
1235 printf("Used slots: %d\n", pkgdb->nslots);
1236 printf("Free slots: %d\n", pkgdb->slotnpages * (PAGE_SIZE / SLOT_SIZE) - pkgdb->nslots);
1237 printf("Blob area size: %d\n", (pkgdb->fileblks - pkgdb->slotnpages * (PAGE_SIZE / BLK_SIZE)) * BLK_SIZE);
1238 printf("Blob area used: %d\n", usedblks * BLK_SIZE);
1239 rpmpkgUnlock(pkgdb, 0);
1245 #include "lzo/lzoconf.h"
1246 #include "lzo/lzo1x.h"
1248 #define BLOBLZO_MAGIC ('L' | 'Z' << 8 | 'O' << 16 | 'B' << 24)
1250 static int rpmpkgLZOCompress(unsigned char **blobp, unsigned int *bloblp)
1252 unsigned char *blob = *blobp;
1253 unsigned int blobl = *bloblp;
1254 unsigned char *lzoblob, *workmem;
1255 unsigned int lzoblobl;
1258 if (lzo_init() != LZO_E_OK) {
1261 workmem = malloc(LZO1X_1_MEM_COMPRESS);
1265 lzoblobl = 4 + 4 + blobl + blobl / 16 + 64 + 3;
1266 lzoblob = malloc(lzoblobl);
1271 h2le(BLOBLZO_MAGIC, lzoblob);
1272 h2le(blobl, lzoblob + 4);
1273 if (lzo1x_1_compress(blob, blobl, lzoblob + 8, &blobl2, workmem) != LZO_E_OK) {
1280 *bloblp = 8 + blobl2;
1284 static int rpmpkgLZODecompress(unsigned char **blobp, unsigned int *bloblp)
1286 unsigned char *lzoblob = *blobp;
1287 unsigned int lzoblobl = *bloblp;
1288 unsigned char *blob;
1292 if (!lzoblob || lzoblobl < 8)
1294 if (le2h(lzoblob) != BLOBLZO_MAGIC)
1296 if (lzo_init() != LZO_E_OK)
1298 blobl = le2h(lzoblob + 4);
1299 blob = malloc(blobl ? blobl : 1);
1302 if (lzo1x_decompress(lzoblob + 8, lzoblobl - 8, blob, &blobl2, 0) != LZO_E_OK || blobl2 != blobl) {