Imported Upstream version 4.14.1
[platform/upstream/rpm.git] / lib / backend / ndb / rpmpkg.c
1 #include "system.h"
2
3 #include <rpm/rpmlog.h>
4
5 #include <sys/types.h>
6 #include <sys/stat.h>
7 #include <sys/file.h>
8 #include <fcntl.h>
9 #include <stdio.h>
10 #include <time.h>
11 #include <unistd.h>
12 #include <string.h>
13 #include <stdlib.h>
14 #include <libgen.h>
15
16 #include "rpmpkg.h"
17
18 #define RPMRC_FAIL 2
19 #define RPMRC_NOTFOUND 1
20 #define RPMRC_OK 0
21
22 #ifdef RPMPKG_LZO
23 static int rpmpkgLZOCompress(unsigned char **blobp, unsigned int *bloblp);
24 static int rpmpkgLZODecompress(unsigned char **blobp, unsigned int *bloblp);
25 #endif
26
27 static int rpmpkgVerifyblob(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned int blkoff, unsigned int blkcnt);
28
29 typedef struct pkgslot_s {
30     unsigned int pkgidx;
31     unsigned int blkoff;
32     unsigned int blkcnt;
33     unsigned int slotno;
34 } pkgslot;
35
36 typedef struct rpmpkgdb_s {
37     int fd;                     /* our file descriptor */
38     int flags;
39     int mode;
40
41     int rdonly;
42
43     unsigned int locked_shared;
44     unsigned int locked_excl;
45
46     int header_ok;              /* header data (e.g. generation) is valid */
47     unsigned int generation;
48     unsigned int slotnpages;
49     unsigned int nextpkgidx;
50
51     struct pkgslot_s *slots;
52     unsigned int aslots;        /* allocated slots */
53     unsigned int nslots;        /* used slots */
54
55     unsigned int *slothash;
56     unsigned int nslothash;
57
58     unsigned int freeslot;      /* first free slot */
59     int slotorder;
60
61     char *filename;
62     unsigned int fileblks;      /* file size in blks */
63     int dofsync;
64 } * rpmpkgdb;
65
66 #define SLOTORDER_UNORDERED     0
67 #define SLOTORDER_BLKOFF        1
68
69
70 static inline unsigned int le2h(unsigned char *p) 
71 {
72     return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24; 
73 }
74
75 static inline void h2le(unsigned int x, unsigned char *p) 
76 {
77     p[0] = x;
78     p[1] = x >> 8;  
79     p[2] = x >> 16; 
80     p[3] = x >> 24; 
81 }
82
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)
86 {
87     unsigned int s1 = adler & 0xffff;
88     unsigned int s2 = (adler >> 16) & 0xffff;
89     int n;
90
91     for (; len >= 5552; len -= 5552) {
92         for (n = 0; n < 5552; n++) {
93             s1 += *buf++;
94             s2 += s1; 
95         }
96         s1 %= 65521;
97         s2 %= 65521;
98     }   
99     for (n = 0; n < len; n++) {
100         s1 += *buf++;
101         s2 += s1; 
102     }   
103     return ((s2 % 65521) << 16) + (s1 % 65521);
104 }
105
106 /*** Header management ***/
107
108 #define PKGDB_MAGIC     ('R' | 'p' << 8 | 'm' << 16 | 'P' << 24)
109 #define PKGDB_VERSION           0
110
111 /* must be a multiple of SLOT_SIZE! */
112 #define PKGDB_HEADER_SIZE       32
113
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
119
120 static int rpmpkgReadHeader(rpmpkgdb pkgdb)
121 {
122     unsigned int generation, slotnpages, nextpkgidx, version;
123     unsigned char header[PKGDB_HEADER_SIZE];
124
125     /* if we always head the write lock then our data matches */
126     if (pkgdb->header_ok)
127         return RPMRC_OK;
128     if (pread(pkgdb->fd, header, PKGDB_HEADER_SIZE, 0) != PKGDB_HEADER_SIZE) {
129         return RPMRC_FAIL;
130     }
131     if (le2h(header + PKGDB_OFFSET_MAGIC) != PKGDB_MAGIC) {
132         return RPMRC_FAIL;
133     }
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);
138         return RPMRC_FAIL;
139     }
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)) {
145         free(pkgdb->slots);
146         pkgdb->slots = 0;
147         if (pkgdb->slothash) {
148             free(pkgdb->slothash);
149             pkgdb->slothash = 0;
150         }
151     }
152     pkgdb->generation = generation;
153     pkgdb->slotnpages = slotnpages;
154     pkgdb->nextpkgidx = nextpkgidx;
155     pkgdb->header_ok = 1;
156     return RPMRC_OK;
157 }
158
159 static int rpmpkgWriteHeader(rpmpkgdb pkgdb)
160 {
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)) {
169         return RPMRC_FAIL;
170     }
171     if (pkgdb->dofsync && fsync(pkgdb->fd))
172         return RPMRC_FAIL;      /* write error */
173     return RPMRC_OK;
174 }
175
176 /*** Slot management ***/
177
178 #define SLOT_MAGIC      ('S' | 'l' << 8 | 'o' << 16 | 't' << 24)
179
180 #define SLOT_SIZE 16
181 #define BLK_SIZE  16
182 #define PAGE_SIZE 4096
183
184 /* the first slots (i.e. 32 bytes) are used for the header */
185 #define SLOT_START (PKGDB_HEADER_SIZE / SLOT_SIZE)
186
187 static inline unsigned int hashpkgidx(unsigned int h)
188 {
189     h *= 0x5bd1e995;
190     h ^= h >> 16;
191     return h;
192 }
193
194 static int rpmpkgHashSlots(rpmpkgdb pkgdb)
195 {
196     unsigned int nslots, num;
197     unsigned int *hash;
198     unsigned int h, hh, hmask;
199     int i;
200     pkgslot *slot;
201
202     pkgdb->nslothash = 0;
203     num = pkgdb->nslots;
204     while (num & (num - 1))
205         num = num & (num - 1);
206     num *= 4;
207     hash = pkgdb->slothash;
208     if (!hash || pkgdb->nslothash != num) {
209         free(pkgdb->slothash);
210         hash = pkgdb->slothash = calloc(num, sizeof(unsigned int));
211         if (!hash)
212             return RPMRC_FAIL;
213         pkgdb->nslothash = num;
214     } else {
215         memset(hash, 0, num * sizeof(unsigned int));
216     }
217     hmask = num - 1;
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)
221             ;
222         hash[h] = i + 1;
223     }
224     pkgdb->slothash = hash;
225     pkgdb->nslothash = num;
226     return RPMRC_OK;
227 }
228
229 static int rpmpkgReadSlots(rpmpkgdb pkgdb)
230 {
231     unsigned int slotnpages = pkgdb->slotnpages;
232     struct stat stb;
233     unsigned char pagebuf[PAGE_SIZE];
234     unsigned int page;
235     unsigned int i, minblkoff, fileblks, slotno, freeslot, o;
236     pkgslot *slot;
237
238     /* free old slot data */
239     if (pkgdb->slots) {
240         free(pkgdb->slots);
241         pkgdb->slots = 0;
242     }
243     if (pkgdb->slothash) {
244         free(pkgdb->slothash);
245         pkgdb->slothash = 0;
246     }
247     pkgdb->nslots = 0;
248     pkgdb->freeslot = 0;
249
250     /* calculate current database size in blks */
251     if (fstat(pkgdb->fd, &stb))
252         return RPMRC_FAIL;
253     if (stb.st_size % BLK_SIZE)
254         return RPMRC_FAIL;      /* hmm */
255     fileblks = stb.st_size / BLK_SIZE;
256
257     /* read (and somewhat verify) all slots */
258     pkgdb->aslots = slotnpages * (PAGE_SIZE / SLOT_SIZE);
259     pkgdb->slots = calloc(pkgdb->aslots, sizeof(*pkgdb->slots));
260     if (!pkgdb->slots) {
261         return RPMRC_FAIL;
262     }
263     i = 0;
264     slot = pkgdb->slots;
265     minblkoff = slotnpages * (PAGE_SIZE / BLK_SIZE);
266     slotno = SLOT_START;
267     freeslot = 0;
268     for (page = 0; page < slotnpages; page++) {
269         if (pread(pkgdb->fd, pagebuf, PAGE_SIZE, page * PAGE_SIZE) != PAGE_SIZE)
270             return RPMRC_FAIL;
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) {
275                 return RPMRC_FAIL;
276             }
277             blkoff = le2h(pp + 8);
278             if (!blkoff) {
279                 if (!freeslot)
280                     freeslot = slotno;
281                 continue;
282             }
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 */
293             i++;
294             slot++;
295         }
296     }
297     pkgdb->nslots = i;
298     pkgdb->slotorder = SLOTORDER_UNORDERED;     /* XXX: always order? */
299     pkgdb->fileblks = fileblks;
300     pkgdb->freeslot = freeslot;
301     if (rpmpkgHashSlots(pkgdb)) {
302         free(pkgdb->slots);
303         pkgdb->slots = 0;
304         return RPMRC_FAIL;
305     }
306     return RPMRC_OK;
307 }
308
309 static int orderslots_blkoff_cmp(const void *a, const void *b)
310 {
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;
314 }
315
316 static void rpmpkgOrderSlots(rpmpkgdb pkgdb, int slotorder)
317 {
318     if (pkgdb->slotorder == slotorder)
319         return;
320     if (slotorder == SLOTORDER_BLKOFF) {
321         if (pkgdb->nslots > 1)
322             qsort(pkgdb->slots, pkgdb->nslots, sizeof(*pkgdb->slots), orderslots_blkoff_cmp);
323     }
324     pkgdb->slotorder = slotorder;
325     rpmpkgHashSlots(pkgdb);
326 }
327
328 static inline pkgslot *rpmpkgFindSlot(rpmpkgdb pkgdb, unsigned int pkgidx)
329 {
330     unsigned int i, h,  hh, hmask = pkgdb->nslothash - 1;
331     unsigned int *hash = pkgdb->slothash;
332
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);
336     return 0;
337 }
338
339 static int rpmpkgFindEmptyOffset(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned int blkcnt, unsigned *blkoffp, pkgslot **oldslotp, int dontprepend)
340 {
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;
346
347     if (pkgdb->slotorder != SLOTORDER_BLKOFF)
348         rpmpkgOrderSlots(pkgdb, SLOTORDER_BLKOFF);
349
350     if (dontprepend && nslots) {
351         lastblkend = pkgdb->slots[0].blkoff;
352     }
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! */
357         }
358         if (slot->pkgidx == pkgidx) {
359             if (oldslot) {
360                 return RPMRC_FAIL;      /* eek, two slots with our pkgid ! */
361             }
362             oldslot = slot;
363         }
364         freecnt = slot->blkoff - lastblkend;
365         if (freecnt >= blkcnt) {
366             if (!bestblkoff || bestfreecnt > freecnt) {
367                 bestblkoff = lastblkend;
368                 bestfreecnt = freecnt;
369             }
370         }
371         lastblkend = slot->blkoff + slot->blkcnt;
372     }
373     if (!bestblkoff) {
374         bestblkoff = lastblkend;        /* append to end */
375     }
376     *oldslotp = oldslot;
377     *blkoffp = bestblkoff;
378     return RPMRC_OK;
379 }
380
381 static int rpmpkgNeighbourCheck(rpmpkgdb pkgdb, unsigned int blkoff, unsigned int blkcnt, unsigned int *newblkcnt)
382 {
383     unsigned int i, nslots = pkgdb->nslots;
384     unsigned int lastblkend = pkgdb->slotnpages * (PAGE_SIZE / BLK_SIZE);
385     pkgslot *slot, *left = 0, *right = 0;
386
387     if (pkgdb->slotorder != SLOTORDER_BLKOFF)
388         rpmpkgOrderSlots(pkgdb, SLOTORDER_BLKOFF);
389     if (blkoff < lastblkend)
390         return RPMRC_FAIL;
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)
395             left = slot;
396         if (!right && slot->blkoff >= blkoff)
397             right = slot;
398         lastblkend = slot->blkoff + slot->blkcnt;
399     }
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))
403         return RPMRC_FAIL;
404     if (right && right->blkoff < blkoff + blkcnt)
405         return RPMRC_FAIL;
406     /* check if neighbour blobs are in good shape */
407     if (left && rpmpkgVerifyblob(pkgdb, left->pkgidx, left->blkoff, left->blkcnt) != RPMRC_OK)
408         return RPMRC_FAIL;
409     if (right && rpmpkgVerifyblob(pkgdb, right->pkgidx, right->blkoff, right->blkcnt) != RPMRC_OK)
410         return RPMRC_FAIL;
411     *newblkcnt = right ? right->blkoff - blkoff : blkcnt;
412     /* bounds are intect. free area. */
413     return RPMRC_OK;
414 }
415
416 static int rpmpkgWriteslot(rpmpkgdb pkgdb, unsigned int slotno, unsigned int pkgidx, unsigned int blkoff, unsigned int blkcnt)
417 {
418     unsigned char buf[SLOT_SIZE];
419     /* sanity */
420     if (slotno < SLOT_START)
421         return RPMRC_FAIL;
422     if (blkoff && slotno == pkgdb->freeslot)
423         pkgdb->freeslot = 0;
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)) {
429         return RPMRC_FAIL;
430     }
431     pkgdb->generation++;
432     if (rpmpkgWriteHeader(pkgdb)) {
433         return RPMRC_FAIL;
434     }
435    return RPMRC_OK;
436 }
437
438 static int rpmpkgWriteEmptySlotpage(rpmpkgdb pkgdb, int pageno)
439 {
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) {
446         return RPMRC_FAIL;
447     }
448     if (pkgdb->dofsync && fsync(pkgdb->fd)) {
449         return RPMRC_FAIL;      /* write error */
450     }
451     return RPMRC_OK;
452 }
453
454 /*** Blk primitives ***/
455
456 static int rpmpkgZeroBlks(rpmpkgdb pkgdb, unsigned int blkoff, unsigned int blkcnt)
457 {
458     unsigned char buf[65536];
459     unsigned int towrite;
460     off_t fileoff;
461
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 */
468         }
469         fileoff += chunk;
470         towrite -= chunk;
471     }
472     if (blkoff + blkcnt > pkgdb->fileblks)
473         pkgdb->fileblks = blkoff + blkcnt;
474     return RPMRC_OK;
475 }
476
477 static int rpmpkgValidateZeroCheck(rpmpkgdb pkgdb, unsigned int blkoff, unsigned int blkcnt)
478 {
479     unsigned long long buf[(65536 / sizeof(unsigned long long)) + 1];
480     off_t fileoff;
481     off_t tocheck;
482     int i;
483
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;
488     tocheck *= BLK_SIZE;
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++)
493             if (buf[i])
494                 return RPMRC_FAIL;      /* not empty */
495         fileoff += 65536;
496         tocheck -= 65536;
497     }
498     if (tocheck) {
499         int cnt = (int)tocheck / sizeof(unsigned long long);
500         buf[cnt++] = 0;
501         if (pread(pkgdb->fd, (void *)buf, tocheck, fileoff) != tocheck)
502             return RPMRC_FAIL;          /* read error */
503         for (i = 0; i < cnt; i++)
504             if (buf[i])
505                 return RPMRC_FAIL;      /* not empty */
506     }
507     return RPMRC_OK;
508 }
509
510 static int rpmpkgValidateZero(rpmpkgdb pkgdb, unsigned int blkoff, unsigned int blkcnt)
511 {
512     if (rpmpkgValidateZeroCheck(pkgdb, blkoff, blkcnt) == RPMRC_OK)
513         return 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)
517         return RPMRC_FAIL;
518     if (rpmpkgZeroBlks(pkgdb, blkoff, blkcnt) != RPMRC_OK)
519         return RPMRC_FAIL;
520     return RPMRC_OK;
521 }
522
523
524 /*** Blob primitives ***/
525
526 /* head: magic + pkgidx + timestamp + bloblen */
527 /* tail: adler32 + bloblen + magic */
528
529 #define BLOBHEAD_MAGIC  ('B' | 'l' << 8 | 'b' << 16 | 'S' << 24)
530 #define BLOBTAIL_MAGIC  ('B' | 'l' << 8 | 'b' << 16 | 'E' << 24)
531
532 #define BLOBHEAD_SIZE   (4 + 4 + 4 + 4)
533 #define BLOBTAIL_SIZE   (4 + 4 + 4)
534
535 static int rpmpkgReadBlob(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned int blkoff, unsigned int blkcnt, unsigned char *blob, unsigned int *bloblp, unsigned int *tstampp)
536 {
537     unsigned char buf[BLOBHEAD_SIZE > BLOBTAIL_SIZE ? BLOBHEAD_SIZE : BLOBTAIL_SIZE];
538     unsigned int bloblen, toread, tstamp;
539     off_t fileoff;
540     unsigned int adl;
541     int verifyadler = bloblp ? 0 : 1;
542
543     /* sanity */
544     if (blkcnt <  (BLOBHEAD_SIZE + BLOBTAIL_SIZE + BLK_SIZE - 1) / BLK_SIZE)
545         return RPMRC_FAIL;      /* blkcnt too small */
546     /* read header */
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 */
558     adl = ADLER32_INIT;
559     if (verifyadler)
560         adl = update_adler32(adl, buf, BLOBHEAD_SIZE);
561     /* read in 64K chunks */
562     fileoff += BLOBHEAD_SIZE;
563     toread = blkcnt * BLK_SIZE - BLOBHEAD_SIZE;
564     if (!bloblp)
565         toread -= BLOBTAIL_SIZE;
566     while (toread) {
567         unsigned int chunk = toread > 65536 ? 65536 : toread;
568         if (pread(pkgdb->fd, blob, chunk, fileoff) != chunk) {
569             return RPMRC_FAIL;  /* read error */
570         }
571         if (verifyadler) {
572             if (!bloblp)
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);
576         }
577         if (bloblp)
578             blob += chunk;
579         toread -= chunk;
580         fileoff += chunk;
581     }
582     /* read trailer */
583     if (bloblp) {
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 */
587     }
588     if (verifyadler && le2h(buf) != adl) {
589         return RPMRC_FAIL;      /* bad blob, adler32 mismatch */
590     }
591     if (le2h(buf + 4) != bloblen) {
592         return RPMRC_FAIL;      /* bad blob, bloblen mismatch */
593     }
594     if (le2h(buf + 8) != BLOBTAIL_MAGIC) {
595         return RPMRC_FAIL;      /* bad blob */
596     }
597     if (bloblp)
598         *bloblp = bloblen;
599     if (tstampp)
600         *tstampp = tstamp;
601     return RPMRC_OK;
602 }
603
604 static int rpmpkgVerifyblob(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned int blkoff, unsigned int blkcnt)
605 {
606     unsigned char buf[65536];
607     return rpmpkgReadBlob(pkgdb, pkgidx, blkoff, blkcnt, buf, 0, 0);
608 }
609
610 static int rpmpkgWriteBlob(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned int blkoff, unsigned int blkcnt, unsigned char *blob, unsigned int blobl, unsigned int now)
611 {
612     unsigned char buf[(BLOBHEAD_SIZE > BLOBTAIL_SIZE ? BLOBHEAD_SIZE : BLOBTAIL_SIZE) + BLK_SIZE];
613     unsigned int towrite, pad;
614     unsigned int adl;
615     off_t fileoff;
616
617     /* sanity */
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);
625     h2le(now, buf + 8);
626     h2le(blobl, buf + 12);
627     if (pwrite(pkgdb->fd, buf, BLOBHEAD_SIZE, fileoff) != BLOBHEAD_SIZE) {
628         return RPMRC_FAIL;      /* write error */
629     }
630     adl = ADLER32_INIT;
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 */
638         }
639         adl = update_adler32(adl, blob, chunk);
640         blob += chunk;
641         towrite -= chunk;
642         fileoff += chunk;
643     }
644     /* pad if needed */
645     pad = blkcnt * BLK_SIZE - (BLOBHEAD_SIZE + blobl + BLOBTAIL_SIZE);
646     if (pad) {
647         memset(buf + (sizeof(buf) - BLOBTAIL_SIZE) - pad, 0, pad);
648         adl = update_adler32(adl, buf + (sizeof(buf) - BLOBTAIL_SIZE) - pad, pad);
649     }
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 */
655     }
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 */
661     }
662     return RPMRC_OK;
663 }
664
665 static int rpmpkgDelBlob(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned int blkoff, unsigned int blkcnt)
666 {
667     if (rpmpkgVerifyblob(pkgdb, pkgidx, blkoff, blkcnt))
668         return RPMRC_FAIL;
669     if (rpmpkgZeroBlks(pkgdb, blkoff, blkcnt))
670         return RPMRC_FAIL;
671     if (pkgdb->dofsync && fsync(pkgdb->fd))
672         return RPMRC_FAIL;      /* write error */
673     return RPMRC_OK;
674 }
675
676
677 static int rpmpkgMoveBlob(rpmpkgdb pkgdb, pkgslot *slot, unsigned int newblkoff)
678 {
679     unsigned int pkgidx = slot->pkgidx;
680     unsigned int blkoff = slot->blkoff;
681     unsigned int blkcnt = slot->blkcnt;
682     unsigned char *blob;
683     unsigned int tstamp, blobl;
684
685     blob = malloc((size_t)blkcnt * BLK_SIZE);
686     if (rpmpkgReadBlob(pkgdb, pkgidx, blkoff, blkcnt, blob, &blobl, &tstamp)) {
687         free(blob);
688         return RPMRC_FAIL;
689     }
690     if (rpmpkgWriteBlob(pkgdb, pkgidx, newblkoff, blkcnt, blob, blobl, tstamp)) {
691         free(blob);
692         return RPMRC_FAIL;
693     }
694     free(blob);
695     if (rpmpkgWriteslot(pkgdb, slot->slotno, pkgidx, newblkoff, blkcnt)) {
696         return RPMRC_FAIL;
697     }
698     if (rpmpkgDelBlob(pkgdb, pkgidx, blkoff, blkcnt)) {
699         return RPMRC_FAIL;
700     }
701     slot->blkoff = newblkoff;
702     pkgdb->slotorder = SLOTORDER_UNORDERED;
703     return RPMRC_OK;
704 }
705
706 static int rpmpkgAddSlotPage(rpmpkgdb pkgdb)
707 {
708     unsigned int cutoff;
709     if (pkgdb->slotorder != SLOTORDER_BLKOFF)
710         rpmpkgOrderSlots(pkgdb, SLOTORDER_BLKOFF);
711     cutoff = (pkgdb->slotnpages + 1) * (PAGE_SIZE / BLK_SIZE);
712
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;
717
718         oldslot = 0;
719         if (rpmpkgFindEmptyOffset(pkgdb, slot->pkgidx, slot->blkcnt, &newblkoff, &oldslot, 1)) {
720             return RPMRC_FAIL;
721         }
722         if (!oldslot || oldslot != slot) {
723             return RPMRC_FAIL;
724         }
725         if (rpmpkgMoveBlob(pkgdb, slot, newblkoff)) {
726             return RPMRC_FAIL;
727         }
728         rpmpkgOrderSlots(pkgdb, SLOTORDER_BLKOFF);
729     }
730
731     /* make sure our new page is empty */
732     if (rpmpkgValidateZero(pkgdb, pkgdb->slotnpages * (PAGE_SIZE / BLK_SIZE), PAGE_SIZE / BLK_SIZE)) {
733         return RPMRC_FAIL;
734     }
735     if (rpmpkgWriteEmptySlotpage(pkgdb, pkgdb->slotnpages)) {
736         return RPMRC_FAIL;
737     }
738
739     /* announce free page */
740     pkgdb->freeslot = pkgdb->slotnpages * (PAGE_SIZE / SLOT_SIZE);
741     pkgdb->slotnpages++;
742     pkgdb->generation++;
743     if (rpmpkgWriteHeader(pkgdb)) {
744         return RPMRC_FAIL;
745     }
746     return RPMRC_OK;
747 }
748
749 static int rpmpkgGetLock(rpmpkgdb pkgdb, int type)
750 {
751     if (!pkgdb->fd)
752         return RPMRC_FAIL;
753     if (flock(pkgdb->fd, type))
754         return RPMRC_FAIL;
755     return RPMRC_OK;
756 }
757
758 int rpmpkgLock(rpmpkgdb pkgdb, int excl)
759 {
760     unsigned int *lockcntp = excl ? &pkgdb->locked_excl : &pkgdb->locked_shared;
761     if (*lockcntp > 0 || (!excl && pkgdb->locked_excl)) {
762         (*lockcntp)++;
763         return RPMRC_OK;
764     }
765     pkgdb->header_ok = 0;
766     if (rpmpkgGetLock(pkgdb, excl ? LOCK_EX : LOCK_SH)) {
767         return RPMRC_FAIL;
768     }
769     (*lockcntp)++;
770     return RPMRC_OK;
771 }
772
773 static int rpmpkgLockInternal(rpmpkgdb pkgdb, int excl)
774 {
775     if (excl && pkgdb->rdonly)
776         return RPMRC_FAIL;
777
778     return  rpmpkgLock(pkgdb, excl);
779 }
780
781 int rpmpkgUnlock(rpmpkgdb pkgdb, int excl)
782 {
783     unsigned int *lockcntp = excl ? &pkgdb->locked_excl : &pkgdb->locked_shared;
784     if (*lockcntp == 0) {
785         return RPMRC_FAIL;
786     }
787     if (*lockcntp > 1 || (!excl && pkgdb->locked_excl)) {
788         (*lockcntp)--;
789         return RPMRC_OK;
790     }
791     if (excl && pkgdb->locked_shared) {
792         /* excl -> shared switch */
793         if (rpmpkgGetLock(pkgdb, LOCK_SH)) {
794             return RPMRC_FAIL;
795         }
796         (*lockcntp)--;
797         return RPMRC_OK;
798     }
799     flock(pkgdb->fd, LOCK_UN);
800     (*lockcntp)--;
801     pkgdb->header_ok = 0;
802     return RPMRC_OK;
803 }
804
805 static int rpmpkgLockReadHeader(rpmpkgdb pkgdb, int excl)
806 {
807     if (rpmpkgLockInternal(pkgdb, excl))
808         return RPMRC_FAIL;
809     if (rpmpkgReadHeader(pkgdb)) {
810         rpmpkgUnlock(pkgdb, excl);
811         return RPMRC_FAIL;
812     }
813     return RPMRC_OK;
814 }
815
816 static int rpmpkgInitInternal(rpmpkgdb pkgdb)
817 {
818     struct stat stb;
819     if (fstat(pkgdb->fd, &stb)) {
820         return RPMRC_FAIL;
821     }
822     if (stb.st_size == 0) {
823         if (rpmpkgWriteEmptySlotpage(pkgdb, 0)) {
824             return RPMRC_FAIL;
825         }
826         pkgdb->slotnpages = 1;
827         if (!pkgdb->nextpkgidx)
828             pkgdb->nextpkgidx = 1;
829         pkgdb->generation++;
830         if (rpmpkgWriteHeader(pkgdb)) {
831             return RPMRC_FAIL;
832         }
833     }
834     return RPMRC_OK;
835 }
836
837 static int rpmpkgInit(rpmpkgdb pkgdb)
838 {
839     int rc;
840     
841     if (rpmpkgLockInternal(pkgdb, 1))
842         return RPMRC_FAIL;
843     rc = rpmpkgInitInternal(pkgdb);
844     rpmpkgUnlock(pkgdb, 1);
845     return rc;
846 }
847
848 int rpmpkgOpen(rpmpkgdb *pkgdbp, const char *filename, int flags, int mode)
849 {
850     struct stat stb;
851     rpmpkgdb pkgdb;
852
853     *pkgdbp = 0;
854     pkgdb = calloc(1, sizeof(*pkgdb));
855     pkgdb->filename = strdup(filename);
856     if (!pkgdb->filename) {
857         free(pkgdb);
858         return RPMRC_FAIL;
859     }
860     if ((flags & (O_RDONLY|O_RDWR)) == O_RDONLY)
861         pkgdb->rdonly = 1;
862     if ((pkgdb->fd = open(filename, flags, mode)) == -1) {
863         free(pkgdb->filename);
864         free(pkgdb);
865         return RPMRC_FAIL;
866     }
867     if (flags & O_CREAT) {
868         char *filenameCopy;
869         DIR *pdir;
870
871         if ((filenameCopy = strdup(pkgdb->filename)) == NULL) {
872             close(pkgdb->fd);
873             free(pkgdb->filename);
874             free(pkgdb);
875             return RPMRC_FAIL;
876         }
877
878         if ((pdir = opendir(dirname(filenameCopy))) == NULL) {
879             free(filenameCopy);
880             close(pkgdb->fd);
881             free(pkgdb->filename);
882             free(pkgdb);
883             return RPMRC_FAIL;
884         }
885
886         if (fsync(dirfd(pdir)) == -1) {
887             closedir(pdir);
888             free(filenameCopy);
889             close(pkgdb->fd);
890             free(pkgdb->filename);
891             free(pkgdb);
892             return RPMRC_FAIL;
893         }
894         closedir(pdir);
895         free(filenameCopy);
896
897     }
898     if (fstat(pkgdb->fd, &stb)) {
899         close(pkgdb->fd);
900         free(pkgdb->filename);
901         free(pkgdb);
902         return RPMRC_FAIL;
903     }
904     if (stb.st_size == 0) {
905         if (rpmpkgInit(pkgdb)) {
906             close(pkgdb->fd);
907             free(pkgdb->filename);
908             free(pkgdb);
909             return RPMRC_FAIL;
910         }
911     }
912     pkgdb->flags = flags;
913     pkgdb->mode = mode;
914     pkgdb->dofsync = 1;
915     *pkgdbp = pkgdb;
916     return RPMRC_OK;
917 }
918
919 void rpmpkgClose(rpmpkgdb pkgdb)
920 {
921     if (pkgdb->fd >= 0) {
922         close(pkgdb->fd);
923         pkgdb->fd = -1;
924     }
925     if (pkgdb->slots)
926         free(pkgdb->slots);
927     pkgdb->slots = 0;
928     if (pkgdb->slothash)
929         free(pkgdb->slothash);
930     pkgdb->slothash = 0;
931     free(pkgdb->filename);
932     free(pkgdb);
933 }
934
935 void rpmpkgSetFsync(rpmpkgdb pkgdb, int dofsync)
936 {
937     pkgdb->dofsync = dofsync;
938 }
939
940
941 static int rpmpkgGetInternal(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned char **blobp, unsigned int *bloblp)
942 {
943     pkgslot *slot;
944     unsigned char *blob;
945
946     if (!pkgdb->slots && rpmpkgReadSlots(pkgdb)) {
947         return RPMRC_FAIL;
948     }
949     slot = rpmpkgFindSlot(pkgdb, pkgidx);
950     if (!slot) {
951         return RPMRC_NOTFOUND;
952     }
953     blob = malloc((size_t)slot->blkcnt * BLK_SIZE);
954     if (rpmpkgReadBlob(pkgdb, pkgidx, slot->blkoff, slot->blkcnt, blob, bloblp, (unsigned int *)0)) {
955         free(blob);
956         return RPMRC_FAIL;
957     }
958     *blobp = blob;
959     return RPMRC_OK;
960 }
961
962 static int rpmpkgPutInternal(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned char *blob, unsigned int blobl)
963 {
964     unsigned int blkcnt, blkoff, slotno;
965     pkgslot *oldslot;
966
967     /* we always read all slots when writing, just in case */
968     if (rpmpkgReadSlots(pkgdb)) {
969         return RPMRC_FAIL;
970     }
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)) {
974         return RPMRC_FAIL;
975     }
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)) {
979             return RPMRC_FAIL;
980         }
981         /* redo rpmpkgFindEmptyOffset to get another free area */
982         if (rpmpkgFindEmptyOffset(pkgdb, pkgidx, blkcnt, &blkoff, &oldslot, 0)) {
983             return RPMRC_FAIL;
984         }
985     }
986     /* make sure that we don't overwrite data */
987     if (rpmpkgValidateZero(pkgdb, blkoff, blkcnt)) {
988         return RPMRC_FAIL;
989     }
990     /* write new blob */
991     if (rpmpkgWriteBlob(pkgdb, pkgidx, blkoff, blkcnt, blob, blobl, (unsigned int)time(0))) {
992         return RPMRC_FAIL;
993     }
994     /* write slot */
995     slotno = oldslot ? oldslot->slotno : pkgdb->freeslot;
996     if (!slotno) {
997         return RPMRC_FAIL;
998     }
999     if (rpmpkgWriteslot(pkgdb, slotno, pkgidx, blkoff, blkcnt)) {
1000         free(pkgdb->slots);
1001         pkgdb->slots = 0;
1002         return RPMRC_FAIL;
1003     }
1004     /* erase old blob */
1005     if (oldslot && oldslot->blkoff) {
1006         if (rpmpkgDelBlob(pkgdb, pkgidx, oldslot->blkoff, oldslot->blkcnt)) {
1007             free(pkgdb->slots);
1008             pkgdb->slots = 0;
1009             return RPMRC_FAIL;
1010         }
1011     }
1012     if (oldslot) {
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;
1017     } else {
1018         free(pkgdb->slots);
1019         pkgdb->slots = 0;
1020     }
1021     return RPMRC_OK;
1022 }
1023
1024 static int rpmpkgDelInternal(rpmpkgdb pkgdb, unsigned int pkgidx)
1025 {
1026     pkgslot *slot;
1027     unsigned int blkoff, blkcnt;
1028
1029     /* we always read all slots when writing, just in case */
1030     if (rpmpkgReadSlots(pkgdb)) {
1031         return RPMRC_FAIL;
1032     }
1033     rpmpkgOrderSlots(pkgdb, SLOTORDER_BLKOFF);
1034     slot = rpmpkgFindSlot(pkgdb, pkgidx);
1035     if (!slot) {
1036         return RPMRC_OK;
1037     }
1038     if (rpmpkgWriteslot(pkgdb, slot->slotno, 0, 0, 0)) {
1039         return RPMRC_FAIL;
1040     }
1041     if (rpmpkgDelBlob(pkgdb, pkgidx, slot->blkoff, slot->blkcnt)) {
1042         return RPMRC_FAIL;
1043     }
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 */
1046         int i;
1047         if (slot == pkgdb->slots) {
1048             blkoff = pkgdb->slotnpages * (PAGE_SIZE / BLK_SIZE);
1049         } else {
1050             blkoff = slot[-1].blkoff + slot[-1].blkcnt;
1051         }
1052         if (slot < pkgdb->slots + pkgdb->nslots - 1) {
1053             blkcnt = slot[1].blkoff - blkoff;
1054         } else {
1055             blkcnt = slot->blkoff + slot->blkcnt - blkoff;
1056         }
1057         slot->blkoff = 0;
1058         slot->blkcnt = 0;
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)
1064                 slot -= 2;
1065             if (!slot->blkoff || slot->blkoff < blkoff)
1066                 continue;
1067             if (slot->blkoff < pkgdb->fileblks / 2)
1068                 continue;
1069             if (slot->blkcnt > blkcnt)
1070                 continue;
1071             rpmpkgMoveBlob(pkgdb, slot, blkoff);
1072             blkoff += slot->blkcnt;
1073             blkcnt -= slot->blkcnt;
1074         }
1075         rpmpkgOrderSlots(pkgdb, SLOTORDER_BLKOFF);
1076     } else {
1077         slot->blkoff = 0;
1078         slot->blkcnt = 0;
1079     }
1080     /* check if we can truncate the file */
1081     slot = pkgdb->slots + pkgdb->nslots - 1;
1082     if (!slot->blkoff && pkgdb->nslots > 1) {
1083         slot--;
1084     }
1085     if (slot->blkoff)
1086         blkoff = slot->blkoff + slot->blkcnt;
1087     else
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;
1094             }
1095         }
1096     }
1097     free(pkgdb->slots);
1098     pkgdb->slots = 0;
1099     return RPMRC_OK;
1100 }
1101
1102 static int rpmpkgListInternal(rpmpkgdb pkgdb, unsigned int **pkgidxlistp, unsigned int *npkgidxlistp)
1103 {
1104     unsigned int i, nslots, *pkgidxlist;
1105     pkgslot *slot;
1106
1107     if (!pkgdb->slots && rpmpkgReadSlots(pkgdb)) {
1108         return RPMRC_FAIL;
1109     }
1110     if (!pkgidxlistp) {
1111         *npkgidxlistp = pkgdb->nslots;
1112         return RPMRC_OK;
1113     }
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;
1119     }
1120     *pkgidxlistp = pkgidxlist;
1121     *npkgidxlistp = nslots;
1122     return RPMRC_OK;
1123 }
1124
1125 int rpmpkgGet(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned char **blobp, unsigned int *bloblp)
1126 {
1127     int rc;
1128
1129     *blobp = 0;
1130     *bloblp = 0;
1131     if (!pkgidx)
1132         return RPMRC_FAIL;
1133     if (rpmpkgLockReadHeader(pkgdb, 0))
1134         return RPMRC_FAIL;
1135     rc = rpmpkgGetInternal(pkgdb, pkgidx, blobp, bloblp);
1136     rpmpkgUnlock(pkgdb, 0);
1137 #ifdef RPMPKG_LZO
1138     if (!rc)
1139         rc = rpmpkgLZODecompress(blobp, bloblp);
1140 #endif
1141     return rc;
1142 }
1143
1144 int rpmpkgPut(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned char *blob, unsigned int blobl)
1145 {
1146     int rc;
1147
1148     if (!pkgidx) {
1149         return RPMRC_FAIL;
1150     }
1151     if (rpmpkgLockReadHeader(pkgdb, 1))
1152         return RPMRC_FAIL;
1153 #ifdef RPMPKG_LZO
1154     if (rpmpkgLZOCompress(&blob, &blobl)) {
1155         rpmpkgUnlock(pkgdb, 1);
1156         return RPMRC_FAIL;
1157     }
1158 #endif
1159     rc = rpmpkgPutInternal(pkgdb, pkgidx, blob, blobl);
1160 #ifdef RPMPKG_LZO
1161     free(blob);
1162 #endif
1163     rpmpkgUnlock(pkgdb, 1);
1164     return rc;
1165 }
1166
1167 int rpmpkgDel(rpmpkgdb pkgdb, unsigned int pkgidx)
1168 {
1169     int rc;
1170
1171     if (!pkgidx) {
1172         return RPMRC_FAIL;
1173     }
1174     if (rpmpkgLockReadHeader(pkgdb, 1))
1175         return RPMRC_FAIL;
1176     rc = rpmpkgDelInternal(pkgdb, pkgidx);
1177     rpmpkgUnlock(pkgdb, 1);
1178     return rc;
1179 }
1180
1181 int rpmpkgList(rpmpkgdb pkgdb, unsigned int **pkgidxlistp, unsigned int *npkgidxlistp)
1182 {
1183     int rc;
1184     if (pkgidxlistp)
1185         *pkgidxlistp = 0;
1186     *npkgidxlistp = 0;
1187     if (rpmpkgLockReadHeader(pkgdb, 0))
1188         return RPMRC_FAIL;
1189     rc = rpmpkgListInternal(pkgdb, pkgidxlistp, npkgidxlistp);
1190     rpmpkgUnlock(pkgdb, 0);
1191     return rc;
1192 }
1193
1194 int rpmpkgNextPkgIdx(rpmpkgdb pkgdb, unsigned int *pkgidxp)
1195 {
1196     if (rpmpkgLockReadHeader(pkgdb, 1))
1197         return RPMRC_FAIL;
1198     *pkgidxp = pkgdb->nextpkgidx++;
1199     if (rpmpkgWriteHeader(pkgdb)) {
1200         rpmpkgUnlock(pkgdb, 1);
1201         return RPMRC_FAIL;
1202     }
1203     /* no fsync needed. also no need to increase the generation count,
1204      * as the header is always read in */
1205     rpmpkgUnlock(pkgdb, 1);
1206     return RPMRC_OK;
1207 }
1208
1209 int rpmpkgGeneration(rpmpkgdb pkgdb, unsigned int *generationp)
1210 {
1211     if (rpmpkgLockReadHeader(pkgdb, 0))
1212         return RPMRC_FAIL;
1213     *generationp = pkgdb->generation;
1214     rpmpkgUnlock(pkgdb, 0);
1215     return RPMRC_OK;
1216 }
1217
1218 int rpmpkgStats(rpmpkgdb pkgdb)
1219 {
1220     unsigned int usedblks = 0;
1221     int i;
1222
1223     if (rpmpkgLockReadHeader(pkgdb, 0))
1224         return RPMRC_FAIL;
1225     if (rpmpkgReadSlots(pkgdb)) {
1226         rpmpkgUnlock(pkgdb, 0);
1227         return RPMRC_FAIL;
1228     }
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);
1240     return RPMRC_OK;
1241 }
1242
1243 #ifdef RPMPKG_LZO
1244
1245 #include "lzo/lzoconf.h"
1246 #include "lzo/lzo1x.h"
1247
1248 #define BLOBLZO_MAGIC   ('L' | 'Z' << 8 | 'O' << 16 | 'B' << 24)
1249
1250 static int rpmpkgLZOCompress(unsigned char **blobp, unsigned int *bloblp)
1251 {
1252     unsigned char *blob = *blobp;
1253     unsigned int blobl = *bloblp;
1254     unsigned char *lzoblob, *workmem;
1255     unsigned int lzoblobl;
1256     lzo_uint blobl2;
1257
1258     if (lzo_init() != LZO_E_OK) {
1259         return RPMRC_FAIL;
1260     }
1261     workmem = malloc(LZO1X_1_MEM_COMPRESS);
1262     if (!workmem) {
1263         return RPMRC_FAIL;
1264     }
1265     lzoblobl = 4 + 4 + blobl + blobl / 16 + 64 + 3;
1266     lzoblob = malloc(lzoblobl);
1267     if (!lzoblob) {
1268         free(workmem);
1269         return RPMRC_FAIL;
1270     }
1271     h2le(BLOBLZO_MAGIC, lzoblob);
1272     h2le(blobl, lzoblob + 4);
1273     if (lzo1x_1_compress(blob, blobl, lzoblob + 8, &blobl2, workmem) != LZO_E_OK) {
1274         free(workmem);
1275         free(lzoblob);
1276         return RPMRC_FAIL;
1277     }
1278     free(workmem);
1279     *blobp = lzoblob;
1280     *bloblp = 8 + blobl2;
1281     return RPMRC_OK;
1282 }
1283
1284 static int rpmpkgLZODecompress(unsigned char **blobp, unsigned int *bloblp)
1285 {
1286     unsigned char *lzoblob = *blobp;
1287     unsigned int lzoblobl = *bloblp;
1288     unsigned char *blob;
1289     unsigned int blobl;
1290     lzo_uint blobl2;
1291
1292     if (!lzoblob || lzoblobl < 8)
1293         return RPMRC_FAIL;
1294     if (le2h(lzoblob) != BLOBLZO_MAGIC)
1295         return RPMRC_FAIL;
1296     if (lzo_init() != LZO_E_OK)
1297         return RPMRC_FAIL;
1298     blobl = le2h(lzoblob + 4);
1299     blob = malloc(blobl ? blobl : 1);
1300     if (!blob)
1301         return RPMRC_FAIL;
1302     if (lzo1x_decompress(lzoblob + 8, lzoblobl - 8, blob, &blobl2, 0) != LZO_E_OK || blobl2 != blobl) {
1303         free(blob);
1304         return RPMRC_FAIL;
1305     }
1306     free(lzoblob);
1307     *blobp = blob;
1308     *bloblp = blobl;
1309     return RPMRC_OK;
1310 }
1311
1312 #endif