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 <unistd.h>
11 #include <string.h>
12 #include <stdlib.h>
13 #include <libgen.h>
14 #include <dirent.h>
15 
16 #include "rpmpkg.h"
17 
18 #define RPMRC_FAIL 2
19 #define RPMRC_NOTFOUND 1
20 #define RPMRC_OK 0
21 
22 
23 static int rpmpkgVerifyblob(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned int blkoff, unsigned int blkcnt);
24 
25 typedef struct pkgslot_s {
26     unsigned int pkgidx;
27     unsigned int blkoff;
28     unsigned int blkcnt;
29     unsigned int slotno;
30 } pkgslot;
31 
32 typedef struct rpmpkgdb_s {
33     int fd;			/* our file descriptor */
34 
35     int rdonly;
36 
37     unsigned int locked_shared;
38     unsigned int locked_excl;
39 
40     int header_ok;		/* header data (e.g. generation) is valid */
41     unsigned int generation;
42     unsigned int slotnpages;
43     unsigned int nextpkgidx;
44 
45     struct pkgslot_s *slots;
46     unsigned int nslots;	/* used slots */
47 
48     unsigned int *slothash;
49     unsigned int nslothash;
50 
51     unsigned int freeslot;	/* first free slot */
52     int ordered;		/* slots are ordered by the blk offsets */
53 
54     char *filename;
55     unsigned int fileblks;	/* file size in blks */
56     int dofsync;
57 } * rpmpkgdb;
58 
59 
le2h(unsigned char * p)60 static inline unsigned int le2h(unsigned char *p)
61 {
62     return p[0] | p[1] << 8 | p[2] << 16 | p[3] << 24;
63 }
64 
h2le(unsigned int x,unsigned char * p)65 static inline void h2le(unsigned int x, unsigned char *p)
66 {
67     p[0] = x;
68     p[1] = x >> 8;
69     p[2] = x >> 16;
70     p[3] = x >> 24;
71 }
72 
73 /* adler 32 algorithm taken from RFC 1950 */
74 #define ADLER32_INIT 1
update_adler32(unsigned int adler,unsigned char * buf,unsigned int len)75 static unsigned int update_adler32(unsigned int adler, unsigned char *buf, unsigned int len)
76 {
77     unsigned int s1 = adler & 0xffff;
78     unsigned int s2 = (adler >> 16) & 0xffff;
79     int n;
80 
81     for (; len >= 5552; len -= 5552) {
82         for (n = 0; n < 5552; n++) {
83             s1 += *buf++;
84             s2 += s1;
85         }
86         s1 %= 65521;
87         s2 %= 65521;
88     }
89     for (n = 0; n < len; n++) {
90         s1 += *buf++;
91         s2 += s1;
92     }
93     return ((s2 % 65521) << 16) + (s1 % 65521);
94 }
95 
96 /*** Header management ***/
97 
98 #define PKGDB_MAGIC	('R' | 'p' << 8 | 'm' << 16 | 'P' << 24)
99 #define PKGDB_VERSION		0
100 
101 /* must be a multiple of SLOT_SIZE! */
102 #define PKGDB_HEADER_SIZE	32
103 
104 #define PKGDB_OFFSET_MAGIC	0
105 #define PKGDB_OFFSET_VERSION	4
106 #define PKGDB_OFFSET_GENERATION	8
107 #define PKGDB_OFFSET_SLOTNPAGES 12
108 #define PKGDB_OFFSET_NEXTPKGIDX 16
109 
rpmpkgReadHeader(rpmpkgdb pkgdb)110 static int rpmpkgReadHeader(rpmpkgdb pkgdb)
111 {
112     unsigned int generation, slotnpages, nextpkgidx, version;
113     unsigned char header[PKGDB_HEADER_SIZE];
114 
115     /* if we always head the write lock then our data matches */
116     if (pkgdb->header_ok)
117 	return RPMRC_OK;
118     if (pread(pkgdb->fd, header, PKGDB_HEADER_SIZE, 0) != PKGDB_HEADER_SIZE) {
119 	return RPMRC_FAIL;
120     }
121     if (le2h(header + PKGDB_OFFSET_MAGIC) != PKGDB_MAGIC) {
122 	return RPMRC_FAIL;
123     }
124     version = le2h(header + PKGDB_OFFSET_VERSION);
125     if (version != PKGDB_VERSION) {
126 	rpmlog(RPMLOG_ERR, _("rpmpkg: Version mismatch. Expected version: %u. "
127 	    "Found version: %u\n"), PKGDB_VERSION, version);
128 	return RPMRC_FAIL;
129     }
130     generation = le2h(header + PKGDB_OFFSET_GENERATION);
131     slotnpages = le2h(header + PKGDB_OFFSET_SLOTNPAGES);
132     nextpkgidx = le2h(header + PKGDB_OFFSET_NEXTPKGIDX);
133     /* free slots if our internal data no longer matches */
134     if (pkgdb->slots && (pkgdb->generation != generation || pkgdb->slotnpages != slotnpages)) {
135 	free(pkgdb->slots);
136 	pkgdb->slots = 0;
137     }
138     pkgdb->generation = generation;
139     pkgdb->slotnpages = slotnpages;
140     pkgdb->nextpkgidx = nextpkgidx;
141     pkgdb->header_ok = 1;
142     return RPMRC_OK;
143 }
144 
rpmpkgFsync(rpmpkgdb pkgdb)145 static int rpmpkgFsync(rpmpkgdb pkgdb)
146 {
147 #ifdef HAVE_FDATASYNC
148     return fdatasync(pkgdb->fd);
149 #else
150     return fsync(pkgdb->fd);
151 #endif
152 }
153 
rpmpkgWriteHeader(rpmpkgdb pkgdb)154 static int rpmpkgWriteHeader(rpmpkgdb pkgdb)
155 {
156     unsigned char header[PKGDB_HEADER_SIZE];
157     memset(header, 0, sizeof(header));
158     h2le(PKGDB_MAGIC, header + PKGDB_OFFSET_MAGIC);
159     h2le(PKGDB_VERSION, header + PKGDB_OFFSET_VERSION);
160     h2le(pkgdb->generation, header + PKGDB_OFFSET_GENERATION);
161     h2le(pkgdb->slotnpages, header + PKGDB_OFFSET_SLOTNPAGES);
162     h2le(pkgdb->nextpkgidx, header + PKGDB_OFFSET_NEXTPKGIDX);
163     if (pwrite(pkgdb->fd, header, sizeof(header), 0) != sizeof(header)) {
164 	return RPMRC_FAIL;
165     }
166     if (pkgdb->dofsync && rpmpkgFsync(pkgdb))
167 	return RPMRC_FAIL;	/* write error */
168     return RPMRC_OK;
169 }
170 
171 /*** Slot management ***/
172 
173 #define SLOT_MAGIC	('S' | 'l' << 8 | 'o' << 16 | 't' << 24)
174 
175 #define SLOT_SIZE 16
176 #define BLK_SIZE  16
177 #define PAGE_SIZE 4096
178 
179 /* the first slots (i.e. 32 bytes) are used for the header */
180 #define SLOT_START (PKGDB_HEADER_SIZE / SLOT_SIZE)
181 
hashpkgidx(unsigned int h)182 static inline unsigned int hashpkgidx(unsigned int h)
183 {
184     h *= 0x5bd1e995;
185     h ^= h >> 16;
186     return h;
187 }
188 
189 /* (re-)create a hash mapping pkgidx numbers to slots */
rpmpkgHashSlots(rpmpkgdb pkgdb)190 static int rpmpkgHashSlots(rpmpkgdb pkgdb)
191 {
192     unsigned int nslots, num;
193     unsigned int *hash;
194     unsigned int h, hh, hmask;
195     int i;
196     pkgslot *slot;
197 
198     num = pkgdb->nslots + 32;
199     while (num & (num - 1))
200 	num = num & (num - 1);
201     num *= 4;
202     hash = pkgdb->slothash;
203     if (!hash || pkgdb->nslothash != num) {
204 	if (hash)
205 	    free(hash);
206 	hash = pkgdb->slothash = xcalloc(num, sizeof(unsigned int));
207 	pkgdb->nslothash = num;
208     } else {
209 	memset(hash, 0, num * sizeof(unsigned int));
210     }
211     hmask = num - 1;
212     nslots = pkgdb->nslots;
213     for (i = 0, slot = pkgdb->slots; i < nslots; i++, slot++) {
214 	for (h = hashpkgidx(slot->pkgidx) & hmask, hh = 7; hash[h] != 0; h = (h + hh++) & hmask)
215 	    ;
216 	hash[h] = i + 1;
217     }
218     return RPMRC_OK;
219 }
220 
rpmpkgReadSlots(rpmpkgdb pkgdb)221 static int rpmpkgReadSlots(rpmpkgdb pkgdb)
222 {
223     unsigned int slotnpages = pkgdb->slotnpages;
224     struct stat stb;
225     unsigned char pagebuf[PAGE_SIZE];
226     unsigned int page;
227     unsigned int i, minblkoff, fileblks, slotno, freeslot, o;
228     pkgslot *slot;
229 
230     /* free old slot data */
231     if (pkgdb->slots) {
232 	free(pkgdb->slots);
233 	pkgdb->slots = 0;
234     }
235     pkgdb->nslots = 0;
236     pkgdb->freeslot = 0;
237 
238     /* calculate current database size in blks */
239     if (fstat(pkgdb->fd, &stb))
240 	return RPMRC_FAIL;
241     if (stb.st_size % BLK_SIZE)
242 	return RPMRC_FAIL;	/* hmm */
243     fileblks = stb.st_size / BLK_SIZE;
244 
245     /* read (and somewhat verify) all slots */
246     pkgdb->slots = xcalloc(slotnpages * (PAGE_SIZE / SLOT_SIZE), sizeof(*pkgdb->slots));
247     i = 0;
248     slot = pkgdb->slots;
249     minblkoff = slotnpages * (PAGE_SIZE / BLK_SIZE);
250     slotno = SLOT_START;
251     freeslot = 0;
252     for (page = 0; page < slotnpages; page++) {
253 	if (pread(pkgdb->fd, pagebuf, PAGE_SIZE, page * PAGE_SIZE) != PAGE_SIZE)
254 	    return RPMRC_FAIL;
255 	for (o = page ? 0 : SLOT_START * SLOT_SIZE; o < PAGE_SIZE; o += SLOT_SIZE, slotno++) {
256 	    unsigned char *pp = pagebuf + o;
257 	    unsigned int blkoff, blkcnt, pkgidx;
258 	    if (le2h(pp) != SLOT_MAGIC) {
259 		return RPMRC_FAIL;
260 	    }
261 	    blkoff = le2h(pp + 8);
262 	    if (!blkoff) {
263 		if (!freeslot)
264 		    freeslot = slotno;
265 		continue;
266 	    }
267 	    pkgidx = le2h(pp + 4);
268 	    blkcnt = le2h(pp + 12);
269 	    slot->pkgidx = pkgidx;
270 	    slot->blkoff = blkoff;
271 	    slot->blkcnt = blkcnt;
272 	    slot->slotno = slotno;
273 	    if (slot->blkoff + slot->blkcnt > fileblks)
274 		return RPMRC_FAIL;	/* truncated database */
275 	    if (!slot->pkgidx || !slot->blkcnt || slot->blkoff < minblkoff)
276 		return RPMRC_FAIL;	/* bad entry */
277 	    i++;
278 	    slot++;
279 	}
280     }
281     pkgdb->nslots = i;
282     pkgdb->ordered = 0;
283     pkgdb->fileblks = fileblks;
284     pkgdb->freeslot = freeslot;
285     if (rpmpkgHashSlots(pkgdb)) {
286 	free(pkgdb->slots);
287 	pkgdb->slots = 0;
288 	return RPMRC_FAIL;
289     }
290     return RPMRC_OK;
291 }
292 
orderslots_blkoff_cmp(const void * a,const void * b)293 static int orderslots_blkoff_cmp(const void *a, const void *b)
294 {
295     unsigned int blkoffa = ((const pkgslot *)a)->blkoff;
296     unsigned int blkoffb = ((const pkgslot *)b)->blkoff;
297     return blkoffa > blkoffb ? 1 : blkoffa < blkoffb ? -1 : 0;
298 }
299 
rpmpkgOrderSlots(rpmpkgdb pkgdb)300 static void rpmpkgOrderSlots(rpmpkgdb pkgdb)
301 {
302     if (pkgdb->ordered)
303 	return;
304     if (pkgdb->nslots > 1)
305 	qsort(pkgdb->slots, pkgdb->nslots, sizeof(*pkgdb->slots), orderslots_blkoff_cmp);
306     pkgdb->ordered = 1;
307     rpmpkgHashSlots(pkgdb);
308 }
309 
rpmpkgFindSlot(rpmpkgdb pkgdb,unsigned int pkgidx)310 static inline pkgslot *rpmpkgFindSlot(rpmpkgdb pkgdb, unsigned int pkgidx)
311 {
312     unsigned int i, h,  hh, hmask = pkgdb->nslothash - 1;
313     unsigned int *hash = pkgdb->slothash;
314 
315     for (h = hashpkgidx(pkgidx) & hmask, hh = 7; (i = hash[h]) != 0; h = (h + hh++) & hmask)
316 	if (pkgdb->slots[i - 1].pkgidx == pkgidx)
317 	    return pkgdb->slots + (i - 1);
318     return 0;
319 }
320 
321 /* Find an empty space for blkcnt blocks. If dontprepend is true, ignore
322    the space between the slot area and the first blob */
rpmpkgFindEmptyOffset(rpmpkgdb pkgdb,unsigned int pkgidx,unsigned int blkcnt,unsigned * blkoffp,pkgslot ** oldslotp,int dontprepend)323 static int rpmpkgFindEmptyOffset(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned int blkcnt, unsigned *blkoffp, pkgslot **oldslotp, int dontprepend)
324 {
325     unsigned int i, nslots = pkgdb->nslots;
326     unsigned int bestblkoff = 0;
327     unsigned int freecnt, bestfreecnt = 0;
328     unsigned int lastblkend = pkgdb->slotnpages * (PAGE_SIZE / BLK_SIZE);
329     pkgslot *slot, *oldslot = 0;
330 
331     if (!pkgdb->ordered)
332 	rpmpkgOrderSlots(pkgdb);
333 
334     if (dontprepend && nslots) {
335 	lastblkend = pkgdb->slots[0].blkoff;
336     }
337     /* best fit strategy */
338     for (i = 0, slot = pkgdb->slots; i < nslots; i++, slot++) {
339 	if (slot->blkoff < lastblkend) {
340 	    return RPMRC_FAIL;		/* eek, slots overlap! */
341 	}
342 	if (slot->pkgidx == pkgidx) {
343 	    if (oldslot) {
344 		return RPMRC_FAIL;	/* eek, two slots with our pkgid ! */
345 	    }
346 	    oldslot = slot;
347 	}
348 	freecnt = slot->blkoff - lastblkend;
349 	if (freecnt >= blkcnt) {
350 	    if (!bestblkoff || bestfreecnt > freecnt) {
351 		bestblkoff = lastblkend;
352 		bestfreecnt = freecnt;
353 	    }
354 	}
355 	lastblkend = slot->blkoff + slot->blkcnt;
356     }
357     if (!bestblkoff) {
358 	bestblkoff = lastblkend;	/* append to end */
359     }
360     *oldslotp = oldslot;
361     *blkoffp = bestblkoff;
362     return RPMRC_OK;
363 }
364 
365 /* verify the blobs to the left and right of a free area */
rpmpkgNeighbourCheck(rpmpkgdb pkgdb,unsigned int blkoff,unsigned int blkcnt,unsigned int * newblkcnt)366 static int rpmpkgNeighbourCheck(rpmpkgdb pkgdb, unsigned int blkoff, unsigned int blkcnt, unsigned int *newblkcnt)
367 {
368     unsigned int i, nslots = pkgdb->nslots;
369     unsigned int lastblkend = pkgdb->slotnpages * (PAGE_SIZE / BLK_SIZE);
370     pkgslot *slot, *left = 0, *right = 0;
371 
372     if (!pkgdb->ordered)
373 	rpmpkgOrderSlots(pkgdb);
374     if (blkoff < lastblkend)
375 	return RPMRC_FAIL;
376     for (i = 0, slot = pkgdb->slots; i < nslots; i++, slot++) {
377 	if (slot->blkoff < lastblkend)
378 	    return RPMRC_FAIL;		/* eek, slots overlap! */
379 	if (slot->blkoff < blkoff)
380 	    left = slot;
381 	if (!right && slot->blkoff >= blkoff)
382 	    right = slot;
383 	lastblkend = slot->blkoff + slot->blkcnt;
384     }
385     if (left && left->blkoff + left->blkcnt != blkoff)
386 	return RPMRC_FAIL;	/* must always start right after the block */
387     if (!left && blkoff != pkgdb->slotnpages * (PAGE_SIZE / BLK_SIZE))
388 	return RPMRC_FAIL;
389     if (right && right->blkoff < blkoff + blkcnt)
390 	return RPMRC_FAIL;
391     /* check if neighbour blobs are in good shape */
392     if (left && rpmpkgVerifyblob(pkgdb, left->pkgidx, left->blkoff, left->blkcnt) != RPMRC_OK)
393 	return RPMRC_FAIL;
394     if (right && rpmpkgVerifyblob(pkgdb, right->pkgidx, right->blkoff, right->blkcnt) != RPMRC_OK)
395 	return RPMRC_FAIL;
396     *newblkcnt = right ? right->blkoff - blkoff : blkcnt;
397     /* bounds are intact. ok to zero area. */
398     return RPMRC_OK;
399 }
400 
rpmpkgWriteslot(rpmpkgdb pkgdb,unsigned int slotno,unsigned int pkgidx,unsigned int blkoff,unsigned int blkcnt)401 static int rpmpkgWriteslot(rpmpkgdb pkgdb, unsigned int slotno, unsigned int pkgidx, unsigned int blkoff, unsigned int blkcnt)
402 {
403     unsigned char buf[SLOT_SIZE];
404     /* sanity */
405     if (slotno < SLOT_START)
406 	return RPMRC_FAIL;
407     if (blkoff && slotno == pkgdb->freeslot)
408 	pkgdb->freeslot = 0;
409     h2le(SLOT_MAGIC, buf);
410     h2le(pkgidx, buf + 4);
411     h2le(blkoff, buf + 8);
412     h2le(blkcnt, buf + 12);
413     if (pwrite(pkgdb->fd, buf, sizeof(buf), slotno * SLOT_SIZE) != sizeof(buf)) {
414 	return RPMRC_FAIL;
415     }
416     pkgdb->generation++;
417     /* rpmpkgFsync() is done by rpmpkgWriteHeader() */
418     if (rpmpkgWriteHeader(pkgdb)) {
419 	return RPMRC_FAIL;
420     }
421    return RPMRC_OK;
422 }
423 
rpmpkgWriteEmptySlotpage(rpmpkgdb pkgdb,int pageno)424 static int rpmpkgWriteEmptySlotpage(rpmpkgdb pkgdb, int pageno)
425 {
426     unsigned char page[PAGE_SIZE];
427     int i, off = pageno ? 0 : SLOT_START * SLOT_SIZE;
428     memset(page, 0, sizeof(page));
429     for (i = 0; i < PAGE_SIZE / SLOT_SIZE; i++)
430         h2le(SLOT_MAGIC, page + i * SLOT_SIZE);
431     if (pwrite(pkgdb->fd, page, PAGE_SIZE - off, pageno * PAGE_SIZE + off) != PAGE_SIZE - off) {
432 	return RPMRC_FAIL;
433     }
434     if (pkgdb->dofsync && rpmpkgFsync(pkgdb)) {
435 	return RPMRC_FAIL;	/* write error */
436     }
437     return RPMRC_OK;
438 }
439 
440 /*** Blk primitives ***/
441 
rpmpkgZeroBlks(rpmpkgdb pkgdb,unsigned int blkoff,unsigned int blkcnt)442 static int rpmpkgZeroBlks(rpmpkgdb pkgdb, unsigned int blkoff, unsigned int blkcnt)
443 {
444     unsigned char buf[65536];
445     unsigned int towrite;
446     off_t fileoff;
447 
448     memset(buf, 0, sizeof(buf));
449     fileoff = (off_t)blkoff * BLK_SIZE;
450     for (towrite = blkcnt * BLK_SIZE; towrite; ) {
451 	unsigned int chunk = towrite > 65536 ? 65536 : towrite;
452 	if (pwrite(pkgdb->fd, buf, chunk, fileoff) != chunk) {
453 	    return RPMRC_FAIL;	/* write error */
454 	}
455 	fileoff += chunk;
456 	towrite -= chunk;
457     }
458     if (blkoff + blkcnt > pkgdb->fileblks)
459 	pkgdb->fileblks = blkoff + blkcnt;
460     return RPMRC_OK;
461 }
462 
rpmpkgValidateZeroCheck(rpmpkgdb pkgdb,unsigned int blkoff,unsigned int blkcnt)463 static int rpmpkgValidateZeroCheck(rpmpkgdb pkgdb, unsigned int blkoff, unsigned int blkcnt)
464 {
465     unsigned long long buf[(65536 / sizeof(unsigned long long)) + 1];
466     off_t fileoff;
467     off_t tocheck;
468     int i;
469 
470     if (blkoff > pkgdb->fileblks)
471 	return RPMRC_FAIL;		/* huh? */
472     fileoff = (off_t)blkoff * BLK_SIZE;
473     tocheck = blkoff + blkcnt > pkgdb->fileblks ? pkgdb->fileblks - blkoff : blkcnt;
474     tocheck *= BLK_SIZE;
475     while (tocheck >= 65536) {
476         if (pread(pkgdb->fd, (void *)buf, 65536, fileoff) != 65536)
477 	    return RPMRC_FAIL;		/* read error */
478 	for (i = 0; i < 65536 / sizeof(unsigned long long); i++)
479 	    if (buf[i])
480 		return RPMRC_FAIL;	/* not empty */
481 	fileoff += 65536;
482 	tocheck -= 65536;
483     }
484     if (tocheck) {
485 	int cnt = (int)tocheck / sizeof(unsigned long long);
486 	buf[cnt++] = 0;
487         if (pread(pkgdb->fd, (void *)buf, tocheck, fileoff) != tocheck)
488 	    return RPMRC_FAIL;		/* read error */
489 	for (i = 0; i < cnt; i++)
490 	    if (buf[i])
491 		return RPMRC_FAIL;	/* not empty */
492     }
493     return RPMRC_OK;
494 }
495 
rpmpkgValidateZero(rpmpkgdb pkgdb,unsigned int blkoff,unsigned int blkcnt)496 static int rpmpkgValidateZero(rpmpkgdb pkgdb, unsigned int blkoff, unsigned int blkcnt)
497 {
498     if (rpmpkgValidateZeroCheck(pkgdb, blkoff, blkcnt) == RPMRC_OK)
499 	return RPMRC_OK;
500     rpmlog(RPMLOG_WARNING, _("rpmpkg: detected non-zero blob, trying auto repair\n"));
501     /* auto-repair interrupted transactions */
502     if (rpmpkgNeighbourCheck(pkgdb, blkoff, blkcnt, &blkcnt) != RPMRC_OK)
503 	return RPMRC_FAIL;
504     if (rpmpkgZeroBlks(pkgdb, blkoff, blkcnt) != RPMRC_OK)
505 	return RPMRC_FAIL;
506     return RPMRC_OK;
507 }
508 
509 
510 /*** Blob primitives ***/
511 
512 /* head: magic + pkgidx + generation + bloblen */
513 /* tail: adler32 + bloblen + magic */
514 
515 #define BLOBHEAD_MAGIC	('B' | 'l' << 8 | 'b' << 16 | 'S' << 24)
516 #define BLOBTAIL_MAGIC	('B' | 'l' << 8 | 'b' << 16 | 'E' << 24)
517 
518 #define BLOBHEAD_SIZE	(4 + 4 + 4 + 4)
519 #define BLOBTAIL_SIZE	(4 + 4 + 4)
520 
rpmpkgReadBlob(rpmpkgdb pkgdb,unsigned int pkgidx,unsigned int blkoff,unsigned int blkcnt,unsigned char * blob,unsigned int * bloblp,unsigned int * generationp)521 static int rpmpkgReadBlob(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned int blkoff, unsigned int blkcnt, unsigned char *blob, unsigned int *bloblp, unsigned int *generationp)
522 {
523     unsigned char buf[BLOBHEAD_SIZE > BLOBTAIL_SIZE ? BLOBHEAD_SIZE : BLOBTAIL_SIZE];
524     unsigned int bloblen, toread, generation;
525     off_t fileoff;
526     unsigned int adl;
527     int verifyadler = bloblp ? 0 : 1;
528 
529     /* sanity */
530     if (blkcnt <  (BLOBHEAD_SIZE + BLOBTAIL_SIZE + BLK_SIZE - 1) / BLK_SIZE)
531 	return RPMRC_FAIL;	/* blkcnt too small */
532     /* read header */
533     fileoff = (off_t)blkoff * BLK_SIZE;
534     if (pread(pkgdb->fd, buf, BLOBHEAD_SIZE, fileoff) != BLOBHEAD_SIZE)
535 	return RPMRC_FAIL;	/* read error */
536     if (le2h(buf) != BLOBHEAD_MAGIC)
537 	return RPMRC_FAIL;	/* bad blob */
538     if (le2h(buf + 4) != pkgidx)
539 	return RPMRC_FAIL;	/* bad blob */
540     generation = le2h(buf + 8);
541     bloblen = le2h(buf + 12);
542     if (blkcnt != (BLOBHEAD_SIZE + bloblen + BLOBTAIL_SIZE + BLK_SIZE - 1) / BLK_SIZE)
543 	return RPMRC_FAIL;	/* bad blob */
544     adl = ADLER32_INIT;
545     if (verifyadler)
546 	adl = update_adler32(adl, buf, BLOBHEAD_SIZE);
547     /* read in 64K chunks */
548     fileoff += BLOBHEAD_SIZE;
549     toread = blkcnt * BLK_SIZE - BLOBHEAD_SIZE;
550     if (!bloblp)
551 	toread -= BLOBTAIL_SIZE;
552     while (toread) {
553 	unsigned int chunk = toread > 65536 ? 65536 : toread;
554         if (pread(pkgdb->fd, blob, chunk, fileoff) != chunk) {
555 	    return RPMRC_FAIL;	/* read error */
556 	}
557 	if (verifyadler) {
558 	    if (!bloblp)
559 		adl = update_adler32(adl, blob, chunk);
560 	    else if (toread > BLOBTAIL_SIZE)
561 		adl = update_adler32(adl, blob, toread - BLOBTAIL_SIZE > chunk ? chunk : toread - BLOBTAIL_SIZE);
562 	}
563 	if (bloblp)
564 	    blob += chunk;
565 	toread -= chunk;
566 	fileoff += chunk;
567     }
568     /* read trailer */
569     if (bloblp) {
570 	memcpy(buf, blob - BLOBTAIL_SIZE, BLOBTAIL_SIZE);
571     } else if (pread(pkgdb->fd, buf, BLOBTAIL_SIZE, fileoff) != BLOBTAIL_SIZE) {
572 	return RPMRC_FAIL;	/* read error */
573     }
574     if (verifyadler && le2h(buf) != adl) {
575 	return RPMRC_FAIL;	/* bad blob, adler32 mismatch */
576     }
577     if (le2h(buf + 4) != bloblen) {
578 	return RPMRC_FAIL;	/* bad blob, bloblen mismatch */
579     }
580     if (le2h(buf + 8) != BLOBTAIL_MAGIC) {
581 	return RPMRC_FAIL;	/* bad blob */
582     }
583     if (bloblp)
584 	*bloblp = bloblen;
585     if (generationp)
586 	*generationp = generation;
587     return RPMRC_OK;
588 }
589 
rpmpkgVerifyblob(rpmpkgdb pkgdb,unsigned int pkgidx,unsigned int blkoff,unsigned int blkcnt)590 static int rpmpkgVerifyblob(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned int blkoff, unsigned int blkcnt)
591 {
592     unsigned char buf[65536];
593     return rpmpkgReadBlob(pkgdb, pkgidx, blkoff, blkcnt, buf, 0, 0);
594 }
595 
rpmpkgWriteBlob(rpmpkgdb pkgdb,unsigned int pkgidx,unsigned int blkoff,unsigned int blkcnt,unsigned char * blob,unsigned int blobl,unsigned int now)596 static int rpmpkgWriteBlob(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned int blkoff, unsigned int blkcnt, unsigned char *blob, unsigned int blobl, unsigned int now)
597 {
598     unsigned char buf[(BLOBHEAD_SIZE > BLOBTAIL_SIZE ? BLOBHEAD_SIZE : BLOBTAIL_SIZE) + BLK_SIZE];
599     unsigned int towrite, pad;
600     unsigned int adl;
601     off_t fileoff;
602 
603     /* sanity */
604     if (blkcnt <  (BLOBHEAD_SIZE + BLOBTAIL_SIZE + BLK_SIZE - 1) / BLK_SIZE)
605 	return RPMRC_FAIL;	/* blkcnt too small */
606     if (blkcnt != (BLOBHEAD_SIZE + blobl + BLOBTAIL_SIZE + BLK_SIZE - 1) / BLK_SIZE)
607 	return RPMRC_FAIL;	/* blkcnt mismatch */
608     fileoff = (off_t)blkoff * BLK_SIZE;
609     h2le(BLOBHEAD_MAGIC, buf);
610     h2le(pkgidx, buf + 4);
611     h2le(now, buf + 8);
612     h2le(blobl, buf + 12);
613     if (pwrite(pkgdb->fd, buf, BLOBHEAD_SIZE, fileoff) != BLOBHEAD_SIZE) {
614 	return RPMRC_FAIL;	/* write error */
615     }
616     adl = ADLER32_INIT;
617     adl = update_adler32(adl, buf, BLOBHEAD_SIZE);
618     /* write in 64K chunks */
619     fileoff += BLOBHEAD_SIZE;
620     for (towrite = blobl; towrite;) {
621 	unsigned int chunk = towrite > 65536 ? 65536 : towrite;
622 	if (pwrite(pkgdb->fd, blob, chunk, fileoff) != chunk) {
623 	    return RPMRC_FAIL;	/* write error */
624 	}
625 	adl = update_adler32(adl, blob, chunk);
626 	blob += chunk;
627 	towrite -= chunk;
628 	fileoff += chunk;
629     }
630     /* pad if needed */
631     pad = blkcnt * BLK_SIZE - (BLOBHEAD_SIZE + blobl + BLOBTAIL_SIZE);
632     if (pad) {
633 	memset(buf + (sizeof(buf) - BLOBTAIL_SIZE) - pad, 0, pad);
634 	adl = update_adler32(adl, buf + (sizeof(buf) - BLOBTAIL_SIZE) - pad, pad);
635     }
636     h2le(adl, buf + (sizeof(buf) - BLOBTAIL_SIZE));
637     h2le(blobl, buf + (sizeof(buf) - BLOBTAIL_SIZE) + 4);
638     h2le(BLOBTAIL_MAGIC, buf + (sizeof(buf) - BLOBTAIL_SIZE) + 8);
639     if (pwrite(pkgdb->fd, buf + (sizeof(buf) - BLOBTAIL_SIZE) - pad, pad + BLOBTAIL_SIZE, fileoff) != pad + BLOBTAIL_SIZE) {
640 	return RPMRC_FAIL;	/* write error */
641     }
642     /* update file length */
643     if (blkoff + blkcnt > pkgdb->fileblks)
644 	pkgdb->fileblks = blkoff + blkcnt;
645     if (pkgdb->dofsync && rpmpkgFsync(pkgdb)) {
646 	return RPMRC_FAIL;	/* write error */
647     }
648     return RPMRC_OK;
649 }
650 
rpmpkgDelBlob(rpmpkgdb pkgdb,unsigned int pkgidx,unsigned int blkoff,unsigned int blkcnt)651 static int rpmpkgDelBlob(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned int blkoff, unsigned int blkcnt)
652 {
653     if (rpmpkgVerifyblob(pkgdb, pkgidx, blkoff, blkcnt))
654 	return RPMRC_FAIL;
655     if (rpmpkgZeroBlks(pkgdb, blkoff, blkcnt))
656 	return RPMRC_FAIL;
657     if (pkgdb->dofsync && rpmpkgFsync(pkgdb))
658 	return RPMRC_FAIL;	/* write error */
659     return RPMRC_OK;
660 }
661 
662 
rpmpkgMoveBlob(rpmpkgdb pkgdb,pkgslot * slot,unsigned int newblkoff)663 static int rpmpkgMoveBlob(rpmpkgdb pkgdb, pkgslot *slot, unsigned int newblkoff)
664 {
665     unsigned int pkgidx = slot->pkgidx;
666     unsigned int blkoff = slot->blkoff;
667     unsigned int blkcnt = slot->blkcnt;
668     unsigned char *blob;
669     unsigned int generation, blobl;
670 
671     blob = xmalloc((size_t)blkcnt * BLK_SIZE);
672     if (rpmpkgReadBlob(pkgdb, pkgidx, blkoff, blkcnt, blob, &blobl, &generation)) {
673 	free(blob);
674 	return RPMRC_FAIL;
675     }
676     if (rpmpkgWriteBlob(pkgdb, pkgidx, newblkoff, blkcnt, blob, blobl, generation)) {
677 	free(blob);
678 	return RPMRC_FAIL;
679     }
680     free(blob);
681     if (rpmpkgWriteslot(pkgdb, slot->slotno, pkgidx, newblkoff, blkcnt)) {
682 	return RPMRC_FAIL;
683     }
684     if (rpmpkgDelBlob(pkgdb, pkgidx, blkoff, blkcnt)) {
685 	return RPMRC_FAIL;
686     }
687     slot->blkoff = newblkoff;
688     pkgdb->ordered = 0;
689     return RPMRC_OK;
690 }
691 
rpmpkgAddSlotPage(rpmpkgdb pkgdb)692 static int rpmpkgAddSlotPage(rpmpkgdb pkgdb)
693 {
694     unsigned int cutoff;
695     if (!pkgdb->ordered)
696 	rpmpkgOrderSlots(pkgdb);
697     cutoff = (pkgdb->slotnpages + 1) * (PAGE_SIZE / BLK_SIZE);
698 
699     /* now move every blob before cutoff */
700     while (pkgdb->nslots && pkgdb->slots[0].blkoff < cutoff) {
701 	unsigned int newblkoff;
702         pkgslot *slot = pkgdb->slots, *oldslot;
703 
704 	oldslot = 0;
705 	if (rpmpkgFindEmptyOffset(pkgdb, slot->pkgidx, slot->blkcnt, &newblkoff, &oldslot, 1)) {
706 	    return RPMRC_FAIL;
707 	}
708 	if (!oldslot || oldslot != slot) {
709 	    return RPMRC_FAIL;
710 	}
711 	if (rpmpkgMoveBlob(pkgdb, slot, newblkoff)) {
712 	    return RPMRC_FAIL;
713 	}
714 	rpmpkgOrderSlots(pkgdb);
715     }
716 
717     /* make sure our new page is empty */
718     if (rpmpkgValidateZero(pkgdb, pkgdb->slotnpages * (PAGE_SIZE / BLK_SIZE), PAGE_SIZE / BLK_SIZE)) {
719 	return RPMRC_FAIL;
720     }
721     if (rpmpkgWriteEmptySlotpage(pkgdb, pkgdb->slotnpages)) {
722 	return RPMRC_FAIL;
723     }
724 
725     /* announce free page */
726     pkgdb->freeslot = pkgdb->slotnpages * (PAGE_SIZE / SLOT_SIZE);
727     pkgdb->slotnpages++;
728     pkgdb->generation++;
729     if (rpmpkgWriteHeader(pkgdb)) {
730 	return RPMRC_FAIL;
731     }
732     return RPMRC_OK;
733 }
734 
rpmpkgGetLock(rpmpkgdb pkgdb,int type)735 static int rpmpkgGetLock(rpmpkgdb pkgdb, int type)
736 {
737     if (!pkgdb->fd)
738 	return RPMRC_FAIL;
739     if (flock(pkgdb->fd, type))
740 	return RPMRC_FAIL;
741     return RPMRC_OK;
742 }
743 
rpmpkgLock(rpmpkgdb pkgdb,int excl)744 int rpmpkgLock(rpmpkgdb pkgdb, int excl)
745 {
746     unsigned int *lockcntp = excl ? &pkgdb->locked_excl : &pkgdb->locked_shared;
747     if (*lockcntp > 0 || (!excl && pkgdb->locked_excl)) {
748 	(*lockcntp)++;
749 	return RPMRC_OK;
750     }
751     pkgdb->header_ok = 0;
752     if (rpmpkgGetLock(pkgdb, excl ? LOCK_EX : LOCK_SH)) {
753 	return RPMRC_FAIL;
754     }
755     (*lockcntp)++;
756     return RPMRC_OK;
757 }
758 
rpmpkgLockInternal(rpmpkgdb pkgdb,int excl)759 static int rpmpkgLockInternal(rpmpkgdb pkgdb, int excl)
760 {
761     if (excl && pkgdb->rdonly)
762 	return RPMRC_FAIL;
763 
764     return  rpmpkgLock(pkgdb, excl);
765 }
766 
rpmpkgUnlock(rpmpkgdb pkgdb,int excl)767 int rpmpkgUnlock(rpmpkgdb pkgdb, int excl)
768 {
769     unsigned int *lockcntp = excl ? &pkgdb->locked_excl : &pkgdb->locked_shared;
770     if (*lockcntp == 0) {
771 	return RPMRC_FAIL;
772     }
773     if (*lockcntp > 1 || (!excl && pkgdb->locked_excl)) {
774 	(*lockcntp)--;
775 	return RPMRC_OK;
776     }
777     if (excl && pkgdb->locked_shared) {
778 	/* excl -> shared switch */
779 	if (rpmpkgGetLock(pkgdb, LOCK_SH)) {
780 	    return RPMRC_FAIL;
781 	}
782 	(*lockcntp)--;
783 	return RPMRC_OK;
784     }
785     flock(pkgdb->fd, LOCK_UN);
786     (*lockcntp)--;
787     pkgdb->header_ok = 0;
788     return RPMRC_OK;
789 }
790 
rpmpkgLockReadHeader(rpmpkgdb pkgdb,int excl)791 static int rpmpkgLockReadHeader(rpmpkgdb pkgdb, int excl)
792 {
793     if (rpmpkgLockInternal(pkgdb, excl))
794 	return RPMRC_FAIL;
795     if (rpmpkgReadHeader(pkgdb)) {
796 	rpmpkgUnlock(pkgdb, excl);
797 	return RPMRC_FAIL;
798     }
799     return RPMRC_OK;
800 }
801 
rpmpkgInitInternal(rpmpkgdb pkgdb)802 static int rpmpkgInitInternal(rpmpkgdb pkgdb)
803 {
804     struct stat stb;
805     if (fstat(pkgdb->fd, &stb)) {
806 	return RPMRC_FAIL;
807     }
808     if (stb.st_size == 0) {
809 	if (rpmpkgWriteEmptySlotpage(pkgdb, 0)) {
810 	    return RPMRC_FAIL;
811 	}
812 	pkgdb->slotnpages = 1;
813 	if (!pkgdb->nextpkgidx)
814 	    pkgdb->nextpkgidx = 1;
815 	pkgdb->generation++;
816 	if (rpmpkgWriteHeader(pkgdb)) {
817 	    return RPMRC_FAIL;
818 	}
819     }
820     return RPMRC_OK;
821 }
822 
rpmpkgInit(rpmpkgdb pkgdb)823 static int rpmpkgInit(rpmpkgdb pkgdb)
824 {
825     int rc;
826 
827     if (rpmpkgLockInternal(pkgdb, 1))
828 	return RPMRC_FAIL;
829     rc = rpmpkgInitInternal(pkgdb);
830     rpmpkgUnlock(pkgdb, 1);
831     return rc;
832 }
833 
rpmpkgFsyncDir(const char * filename)834 static int rpmpkgFsyncDir(const char *filename)
835 {
836     int rc = RPMRC_OK;
837     DIR *pdir;
838     char *filenameCopy = xstrdup(filename);
839 
840     if ((pdir = opendir(dirname(filenameCopy))) == NULL) {
841 	free(filenameCopy);
842 	return RPMRC_FAIL;
843     }
844     if (fsync(dirfd(pdir)) == -1)
845 	rc = RPMRC_FAIL;
846     closedir(pdir);
847     free(filenameCopy);
848     return rc;
849 }
850 
rpmpkgOpen(rpmpkgdb * pkgdbp,const char * filename,int flags,int mode)851 int rpmpkgOpen(rpmpkgdb *pkgdbp, const char *filename, int flags, int mode)
852 {
853     struct stat stb;
854     rpmpkgdb pkgdb;
855 
856     *pkgdbp = 0;
857     pkgdb = xcalloc(1, sizeof(*pkgdb));
858     pkgdb->filename = xstrdup(filename);
859     if ((flags & (O_RDONLY|O_RDWR)) == O_RDONLY)
860 	pkgdb->rdonly = 1;
861     if ((pkgdb->fd = open(filename, flags, mode)) == -1) {
862 	free(pkgdb->filename);
863 	free(pkgdb);
864         return RPMRC_FAIL;
865     }
866     if (fstat(pkgdb->fd, &stb)) {
867 	close(pkgdb->fd);
868 	free(pkgdb->filename);
869 	free(pkgdb);
870         return RPMRC_FAIL;
871     }
872     if (stb.st_size == 0) {
873 	/* created new database */
874 	if (rpmpkgFsyncDir(pkgdb->filename)) {
875 	    close(pkgdb->fd);
876 	    free(pkgdb->filename);
877 	    free(pkgdb);
878 	    return RPMRC_FAIL;
879 	}
880 	if (rpmpkgInit(pkgdb)) {
881 	    close(pkgdb->fd);
882 	    free(pkgdb->filename);
883 	    free(pkgdb);
884 	    return RPMRC_FAIL;
885 	}
886     }
887     pkgdb->dofsync = 1;
888     *pkgdbp = pkgdb;
889     return RPMRC_OK;
890 }
891 
rpmpkgClose(rpmpkgdb pkgdb)892 void rpmpkgClose(rpmpkgdb pkgdb)
893 {
894     if (pkgdb->fd >= 0) {
895 	close(pkgdb->fd);
896 	pkgdb->fd = -1;
897     }
898     if (pkgdb->slots)
899 	free(pkgdb->slots);
900     pkgdb->slots = 0;
901     if (pkgdb->slothash)
902 	free(pkgdb->slothash);
903     pkgdb->slothash = 0;
904     free(pkgdb->filename);
905     free(pkgdb);
906 }
907 
908 
salvage_latest_blob(unsigned int * salvaged,unsigned int cnt)909 static unsigned int salvage_latest_blob(unsigned int *salvaged, unsigned int cnt)
910 {
911     unsigned int i, max = 0, maxi = 0;
912     for (i = 0; i < cnt - 1; i++) {
913 	if (salvaged[i * 4 + 7] - salvaged[i * 4 + 3] > max) {
914 	    max = salvaged[i * 4 + 7] - salvaged[i * 4 + 3];
915 	    maxi = i;
916 	}
917     }
918     if ((unsigned int)(salvaged[3] - salvaged[i * 4 + 3]) > max)
919 	maxi = cnt - 1;
920     return maxi;
921 }
922 
salvage_cmp(const void * av,const void * bv)923 static int salvage_cmp(const void *av, const void *bv)
924 {
925     const unsigned int *a = av, *b = bv;
926     if (a[0] != b[0])
927 	return a[0] > b[0] ? 1 : -1;
928     if (a[3] != b[3])
929 	return a[3] > b[3] ? 1 : -1;
930     if (a[1] != b[1])
931 	return a[1] > b[1] ? 1 : -1;
932     return 0;
933 }
934 
935 #define SALVAGE_PAGESIZE 4096
936 #define SALVAGE_BLKCHUNK (SALVAGE_PAGESIZE / BLK_SIZE)
937 
rpmpkgSalvage(rpmpkgdb * pkgdbp,const char * filename)938 int rpmpkgSalvage(rpmpkgdb *pkgdbp, const char *filename)
939 {
940     struct stat stb;
941     rpmpkgdb pkgdb;
942     unsigned int blk, iblk, blkskip;
943     unsigned int blkoff, blkcnt, pkgidx, generation, bloblen;
944     unsigned int nfound, nslots, i, j;
945     unsigned char page[SALVAGE_PAGESIZE];
946     unsigned int *salvaged;
947 
948     *pkgdbp = 0;
949     pkgdb = xcalloc(1, sizeof(*pkgdb));
950     pkgdb->filename = xstrdup(filename);
951     pkgdb->rdonly = 1;
952     if ((pkgdb->fd = open(filename, O_RDONLY)) == -1) {
953 	rpmpkgClose(pkgdb);
954 	return RPMRC_FAIL;
955     }
956     if (rpmpkgGetLock(pkgdb, LOCK_SH)) {
957 	rpmpkgClose(pkgdb);
958 	return RPMRC_FAIL;
959     }
960     pkgdb->locked_shared++;
961     if (fstat(pkgdb->fd, &stb)) {
962 	rpmpkgClose(pkgdb);
963 	return RPMRC_FAIL;
964     }
965     if (stb.st_size < BLK_SIZE) {
966 	rpmpkgClose(pkgdb);
967 	return RPMRC_FAIL;
968     }
969     pkgdb->fileblks = stb.st_size / BLK_SIZE;
970     blkskip = 1;
971     nfound = 0;
972     salvaged = xmalloc(64 * (4 * sizeof(unsigned int)));
973     for (blk = 0; blk < pkgdb->fileblks; blk += SALVAGE_BLKCHUNK) {
974 	unsigned int size;
975 	unsigned char *bp = page;
976 	if (pkgdb->fileblks - blk > SALVAGE_BLKCHUNK)
977 	    size = SALVAGE_PAGESIZE;
978 	else
979 	    size = (pkgdb->fileblks - blk) * BLK_SIZE;
980 	if (pread(pkgdb->fd, page, size, (off_t)blk * BLK_SIZE) != size)
981 	    continue;
982 	if (size != SALVAGE_PAGESIZE)
983 	    memset(page + size, 0, SALVAGE_PAGESIZE - size);
984 	if (blkskip) {
985 	    memset(page, 0, blkskip * BLK_SIZE);
986 	    blkskip = 0;
987 	}
988 	for (iblk = 0; iblk < SALVAGE_BLKCHUNK; iblk++, bp += BLK_SIZE) {
989 	    if (le2h(bp) != BLOBHEAD_MAGIC)
990 		continue;
991 	    pkgidx = le2h(bp + 4);
992 	    if (!pkgidx)
993 		continue;
994 	    generation = le2h(bp + 8);
995 	    bloblen = le2h(bp + 12);
996 	    blkoff = blk + iblk;
997 	    blkcnt = (BLOBHEAD_SIZE + bloblen + BLOBTAIL_SIZE + BLK_SIZE - 1) / BLK_SIZE;
998 	    if (blkoff + blkcnt > pkgdb->fileblks)
999 		continue;
1000 	    if (rpmpkgVerifyblob(pkgdb, pkgidx, blkoff, blkcnt))
1001 		continue;
1002 	    /* found a valid blob, add to salvaged list */
1003 	    if (nfound && (nfound & 63) == 0)
1004 		salvaged = xrealloc(salvaged, (nfound + 64) * (4 * sizeof(unsigned int)));
1005 	    salvaged[nfound * 4 + 0] = pkgidx;
1006 	    salvaged[nfound * 4 + 1] = blkoff;
1007 	    salvaged[nfound * 4 + 2] = blkcnt;
1008 	    salvaged[nfound * 4 + 3] = generation;
1009 	    nfound++;
1010 	    /* advance to after the blob! */
1011 	    blkoff += blkcnt;
1012 	    if (blkoff >= blk + SALVAGE_BLKCHUNK) {
1013 		blkskip = blkoff % SALVAGE_BLKCHUNK;
1014 		blk = blkoff - blkskip - SALVAGE_BLKCHUNK;
1015 		break;
1016 	    } else {
1017 		iblk = blkoff - blk - 1;
1018 		bp = page + iblk * BLK_SIZE;
1019 	    }
1020 	}
1021     }
1022     /* now strip duplicate entries */
1023     nslots = 0;
1024     if (nfound > 1) {
1025 	qsort(salvaged, nfound, sizeof(unsigned int) * 4, salvage_cmp);
1026 	for (i = 0; i < nfound; i++) {
1027 	    pkgidx = salvaged[i * 4];
1028 	    for (j = i + 1; j < nfound; j++)
1029 		if (salvaged[j * 4] != pkgidx)
1030 		    break;
1031 	    if (j != i + 1)
1032 		i += salvage_latest_blob(salvaged + i * 4, j - i);
1033 	    if (i != nslots)
1034 		memcpy(salvaged + nslots * 4, salvaged + i * 4, sizeof(unsigned int) * 4);
1035 	    nslots++;
1036 	    i = j - 1;
1037 	}
1038     }
1039     pkgdb->slots = xcalloc(nslots + 1, sizeof(*pkgdb->slots));
1040     for (i = 0; i < nslots; i++) {
1041 	pkgdb->slots[i].pkgidx = salvaged[i * 4 + 0];
1042 	pkgdb->slots[i].blkoff = salvaged[i * 4 + 1];
1043 	pkgdb->slots[i].blkcnt = salvaged[i * 4 + 2];
1044 	pkgdb->slots[i].slotno = 0;
1045     }
1046     free(salvaged);
1047     pkgdb->header_ok = 1;
1048     pkgdb->nslots = nslots;
1049     pkgdb->ordered = 0;
1050     rpmpkgOrderSlots(pkgdb);
1051     if (rpmpkgHashSlots(pkgdb)) {
1052 	rpmpkgClose(pkgdb);
1053 	return RPMRC_FAIL;
1054     }
1055     *pkgdbp = pkgdb;
1056     return RPMRC_OK;
1057 }
1058 
rpmpkgSetFsync(rpmpkgdb pkgdb,int dofsync)1059 void rpmpkgSetFsync(rpmpkgdb pkgdb, int dofsync)
1060 {
1061     pkgdb->dofsync = dofsync;
1062 }
1063 
1064 
rpmpkgGetInternal(rpmpkgdb pkgdb,unsigned int pkgidx,unsigned char ** blobp,unsigned int * bloblp)1065 static int rpmpkgGetInternal(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned char **blobp, unsigned int *bloblp)
1066 {
1067     pkgslot *slot;
1068     unsigned char *blob;
1069 
1070     if (!pkgdb->slots && rpmpkgReadSlots(pkgdb)) {
1071 	return RPMRC_FAIL;
1072     }
1073     slot = rpmpkgFindSlot(pkgdb, pkgidx);
1074     if (!slot) {
1075 	return RPMRC_NOTFOUND;
1076     }
1077     blob = xmalloc((size_t)slot->blkcnt * BLK_SIZE);
1078     if (rpmpkgReadBlob(pkgdb, pkgidx, slot->blkoff, slot->blkcnt, blob, bloblp, (unsigned int *)0)) {
1079 	free(blob);
1080 	return RPMRC_FAIL;
1081     }
1082     *blobp = blob;
1083     return RPMRC_OK;
1084 }
1085 
rpmpkgPutInternal(rpmpkgdb pkgdb,unsigned int pkgidx,unsigned char * blob,unsigned int blobl)1086 static int rpmpkgPutInternal(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned char *blob, unsigned int blobl)
1087 {
1088     unsigned int blkcnt, blkoff, slotno;
1089     pkgslot *oldslot;
1090 
1091     /* we always read all slots when writing, just in case */
1092     /* this also will set pkgdb->freeslot */
1093     if (rpmpkgReadSlots(pkgdb)) {
1094 	return RPMRC_FAIL;
1095     }
1096     blkcnt = (BLOBHEAD_SIZE + blobl + BLOBTAIL_SIZE + BLK_SIZE - 1) / BLK_SIZE;
1097     /* find a nice place for the blob */
1098     if (rpmpkgFindEmptyOffset(pkgdb, pkgidx, blkcnt, &blkoff, &oldslot, 0)) {
1099 	return RPMRC_FAIL;
1100     }
1101     /* create new slot page if we don't have a free slot and can't reuse an old one */
1102     if (!oldslot && !pkgdb->freeslot) {
1103 	if (rpmpkgAddSlotPage(pkgdb)) {
1104 	    return RPMRC_FAIL;
1105 	}
1106 	/* redo rpmpkgFindEmptyOffset to get another free area */
1107 	if (rpmpkgFindEmptyOffset(pkgdb, pkgidx, blkcnt, &blkoff, &oldslot, 0)) {
1108 	    return RPMRC_FAIL;
1109 	}
1110     }
1111     /* make sure that we don't overwrite data */
1112     if (rpmpkgValidateZero(pkgdb, blkoff, blkcnt)) {
1113 	return RPMRC_FAIL;
1114     }
1115     /* write new blob */
1116     if (rpmpkgWriteBlob(pkgdb, pkgidx, blkoff, blkcnt, blob, blobl, pkgdb->generation)) {
1117 	return RPMRC_FAIL;
1118     }
1119     /* write slot */
1120     slotno = oldslot ? oldslot->slotno : pkgdb->freeslot;
1121     if (!slotno) {
1122 	return RPMRC_FAIL;
1123     }
1124     if (rpmpkgWriteslot(pkgdb, slotno, pkgidx, blkoff, blkcnt)) {
1125 	free(pkgdb->slots);
1126 	pkgdb->slots = 0;
1127 	return RPMRC_FAIL;
1128     }
1129     /* erase old blob */
1130     if (oldslot && oldslot->blkoff) {
1131 	if (rpmpkgDelBlob(pkgdb, pkgidx, oldslot->blkoff, oldslot->blkcnt)) {
1132 	    free(pkgdb->slots);
1133 	    pkgdb->slots = 0;
1134 	    return RPMRC_FAIL;
1135 	}
1136     }
1137     if (oldslot) {
1138 	/* just update the slot, no need to free the slot data */
1139 	oldslot->blkoff = blkoff;
1140 	oldslot->blkcnt = blkcnt;
1141 	pkgdb->ordered = 0;
1142     } else {
1143 	free(pkgdb->slots);
1144 	pkgdb->slots = 0;
1145     }
1146     return RPMRC_OK;
1147 }
1148 
rpmpkgDelInternal(rpmpkgdb pkgdb,unsigned int pkgidx)1149 static int rpmpkgDelInternal(rpmpkgdb pkgdb, unsigned int pkgidx)
1150 {
1151     pkgslot *slot;
1152     unsigned int blkoff, blkcnt;
1153 
1154     /* we always read all slots when writing, just in case */
1155     if (rpmpkgReadSlots(pkgdb)) {
1156 	return RPMRC_FAIL;
1157     }
1158     rpmpkgOrderSlots(pkgdb);
1159     slot = rpmpkgFindSlot(pkgdb, pkgidx);
1160     if (!slot) {
1161 	return RPMRC_OK;
1162     }
1163     if (rpmpkgWriteslot(pkgdb, slot->slotno, 0, 0, 0)) {
1164 	return RPMRC_FAIL;
1165     }
1166     if (rpmpkgDelBlob(pkgdb, pkgidx, slot->blkoff, slot->blkcnt)) {
1167 	return RPMRC_FAIL;
1168     }
1169     if (pkgdb->nslots > 1 && slot->blkoff < pkgdb->fileblks / 2) {
1170 	/* we freed a blob in the first half of our data. do some extra work */
1171 	int i;
1172 	if (slot == pkgdb->slots) {
1173 	    blkoff = pkgdb->slotnpages * (PAGE_SIZE / BLK_SIZE);
1174 	} else {
1175 	    blkoff = slot[-1].blkoff + slot[-1].blkcnt;
1176 	}
1177 	if (slot < pkgdb->slots + pkgdb->nslots - 1) {
1178 	    blkcnt = slot[1].blkoff - blkoff;
1179 	} else {
1180 	    blkcnt = slot->blkoff + slot->blkcnt - blkoff;
1181 	}
1182 	slot->blkoff = 0;
1183 	slot->blkcnt = 0;
1184 	/* try to move the last two slots, the bigger one first */
1185 	slot = pkgdb->slots + pkgdb->nslots - 2;
1186 	if (slot->blkcnt < slot[1].blkcnt)
1187 	    slot++;	/* bigger slot first */
1188 	for (i = 0; i < 2; i++, slot++) {
1189 	    if (slot == pkgdb->slots + pkgdb->nslots)
1190 		slot -= 2;
1191 	    if (!slot->blkoff || slot->blkoff < blkoff)
1192 		continue;
1193 	    if (slot->blkoff < pkgdb->fileblks / 2)
1194 		continue;
1195 	    if (slot->blkcnt > blkcnt)
1196 		continue;
1197 	    rpmpkgMoveBlob(pkgdb, slot, blkoff);
1198 	    blkoff += slot->blkcnt;
1199 	    blkcnt -= slot->blkcnt;
1200 	}
1201 	rpmpkgOrderSlots(pkgdb);
1202     } else {
1203 	slot->blkoff = 0;
1204 	slot->blkcnt = 0;
1205     }
1206     /* check if we can truncate the file */
1207     slot = pkgdb->slots + pkgdb->nslots - 1;
1208     if (!slot->blkoff && pkgdb->nslots > 1) {
1209 	slot--;
1210     }
1211     if (slot->blkoff)
1212 	blkoff = slot->blkoff + slot->blkcnt;
1213     else
1214 	blkoff = pkgdb->slotnpages * (PAGE_SIZE / BLK_SIZE);
1215     if (blkoff < pkgdb->fileblks / 4 * 3) {
1216 	/* truncate the file */
1217 	if (!rpmpkgValidateZero(pkgdb, blkoff, pkgdb->fileblks - blkoff)) {
1218 	    if (!ftruncate(pkgdb->fd, blkoff * BLK_SIZE)) {
1219 		pkgdb->fileblks = blkoff;
1220 	    }
1221 	}
1222     }
1223     free(pkgdb->slots);
1224     pkgdb->slots = 0;
1225     return RPMRC_OK;
1226 }
1227 
rpmpkgListInternal(rpmpkgdb pkgdb,unsigned int ** pkgidxlistp,unsigned int * npkgidxlistp)1228 static int rpmpkgListInternal(rpmpkgdb pkgdb, unsigned int **pkgidxlistp, unsigned int *npkgidxlistp)
1229 {
1230     unsigned int i, nslots, *pkgidxlist;
1231     pkgslot *slot;
1232 
1233     if (!pkgdb->slots && rpmpkgReadSlots(pkgdb)) {
1234 	return RPMRC_FAIL;
1235     }
1236     if (!pkgidxlistp) {
1237 	*npkgidxlistp = pkgdb->nslots;
1238 	return RPMRC_OK;
1239     }
1240     rpmpkgOrderSlots(pkgdb);
1241     nslots = pkgdb->nslots;
1242     pkgidxlist = xcalloc(nslots + 1, sizeof(unsigned int));
1243     for (i = 0, slot = pkgdb->slots; i < nslots; i++, slot++) {
1244 	pkgidxlist[i] = slot->pkgidx;
1245     }
1246     *pkgidxlistp = pkgidxlist;
1247     *npkgidxlistp = nslots;
1248     return RPMRC_OK;
1249 }
1250 
rpmpkgVerifyInternal(rpmpkgdb pkgdb)1251 static int rpmpkgVerifyInternal(rpmpkgdb pkgdb)
1252 {
1253     unsigned int i, nslots;
1254     pkgslot *slot;
1255 
1256     if (rpmpkgReadSlots(pkgdb))
1257 	return RPMRC_FAIL;
1258     rpmpkgOrderSlots(pkgdb);
1259     nslots = pkgdb->nslots;
1260     for (i = 0, slot = pkgdb->slots; i < nslots; i++, slot++) {
1261 	if (rpmpkgVerifyblob(pkgdb, slot->pkgidx, slot->blkoff, slot->blkcnt))
1262 	    return RPMRC_FAIL;
1263     }
1264     return RPMRC_OK;
1265 }
1266 
rpmpkgGet(rpmpkgdb pkgdb,unsigned int pkgidx,unsigned char ** blobp,unsigned int * bloblp)1267 int rpmpkgGet(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned char **blobp, unsigned int *bloblp)
1268 {
1269     int rc;
1270 
1271     *blobp = 0;
1272     *bloblp = 0;
1273     if (!pkgidx)
1274 	return RPMRC_FAIL;
1275     if (rpmpkgLockReadHeader(pkgdb, 0))
1276 	return RPMRC_FAIL;
1277     rc = rpmpkgGetInternal(pkgdb, pkgidx, blobp, bloblp);
1278     rpmpkgUnlock(pkgdb, 0);
1279     return rc;
1280 }
1281 
rpmpkgPut(rpmpkgdb pkgdb,unsigned int pkgidx,unsigned char * blob,unsigned int blobl)1282 int rpmpkgPut(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned char *blob, unsigned int blobl)
1283 {
1284     int rc;
1285 
1286     if (!pkgidx) {
1287 	return RPMRC_FAIL;
1288     }
1289     if (rpmpkgLockReadHeader(pkgdb, 1))
1290 	return RPMRC_FAIL;
1291     rc = rpmpkgPutInternal(pkgdb, pkgidx, blob, blobl);
1292     rpmpkgUnlock(pkgdb, 1);
1293     return rc;
1294 }
1295 
rpmpkgDel(rpmpkgdb pkgdb,unsigned int pkgidx)1296 int rpmpkgDel(rpmpkgdb pkgdb, unsigned int pkgidx)
1297 {
1298     int rc;
1299 
1300     if (!pkgidx) {
1301 	return RPMRC_FAIL;
1302     }
1303     if (rpmpkgLockReadHeader(pkgdb, 1))
1304 	return RPMRC_FAIL;
1305     rc = rpmpkgDelInternal(pkgdb, pkgidx);
1306     rpmpkgUnlock(pkgdb, 1);
1307     return rc;
1308 }
1309 
rpmpkgList(rpmpkgdb pkgdb,unsigned int ** pkgidxlistp,unsigned int * npkgidxlistp)1310 int rpmpkgList(rpmpkgdb pkgdb, unsigned int **pkgidxlistp, unsigned int *npkgidxlistp)
1311 {
1312     int rc;
1313     if (pkgidxlistp)
1314 	*pkgidxlistp = 0;
1315     *npkgidxlistp = 0;
1316     if (rpmpkgLockReadHeader(pkgdb, 0))
1317 	return RPMRC_FAIL;
1318     rc = rpmpkgListInternal(pkgdb, pkgidxlistp, npkgidxlistp);
1319     rpmpkgUnlock(pkgdb, 0);
1320     return rc;
1321 }
1322 
rpmpkgVerify(rpmpkgdb pkgdb)1323 int rpmpkgVerify(rpmpkgdb pkgdb)
1324 {
1325     int rc;
1326     if (rpmpkgLockReadHeader(pkgdb, 0))
1327 	return RPMRC_FAIL;
1328     rc = rpmpkgVerifyInternal(pkgdb);
1329     rpmpkgUnlock(pkgdb, 0);
1330     return rc;
1331 }
1332 
rpmpkgNextPkgIdx(rpmpkgdb pkgdb,unsigned int * pkgidxp)1333 int rpmpkgNextPkgIdx(rpmpkgdb pkgdb, unsigned int *pkgidxp)
1334 {
1335     if (rpmpkgLockReadHeader(pkgdb, 1) || !pkgdb->nextpkgidx)
1336 	return RPMRC_FAIL;
1337     *pkgidxp = pkgdb->nextpkgidx++;
1338     if (rpmpkgWriteHeader(pkgdb)) {
1339 	rpmpkgUnlock(pkgdb, 1);
1340 	return RPMRC_FAIL;
1341     }
1342     /* no fsync needed. also no need to increase the generation count,
1343      * as the header is always read in */
1344     rpmpkgUnlock(pkgdb, 1);
1345     return RPMRC_OK;
1346 }
1347 
rpmpkgGeneration(rpmpkgdb pkgdb,unsigned int * generationp)1348 int rpmpkgGeneration(rpmpkgdb pkgdb, unsigned int *generationp)
1349 {
1350     if (rpmpkgLockReadHeader(pkgdb, 0))
1351 	return RPMRC_FAIL;
1352     *generationp = pkgdb->generation;
1353     rpmpkgUnlock(pkgdb, 0);
1354     return RPMRC_OK;
1355 }
1356 
rpmpkgStats(rpmpkgdb pkgdb)1357 int rpmpkgStats(rpmpkgdb pkgdb)
1358 {
1359     unsigned int usedblks = 0;
1360     int i;
1361 
1362     if (rpmpkgLockReadHeader(pkgdb, 0))
1363 	return RPMRC_FAIL;
1364     if (rpmpkgReadSlots(pkgdb)) {
1365 	rpmpkgUnlock(pkgdb, 0);
1366 	return RPMRC_FAIL;
1367     }
1368     for (i = 0; i < pkgdb->nslots; i++)
1369 	usedblks += pkgdb->slots[i].blkcnt;
1370     printf("--- Package DB Stats\n");
1371     printf("Filename: %s\n", pkgdb->filename);
1372     printf("Generation: %d\n", pkgdb->generation);
1373     printf("Slot pages: %d\n", pkgdb->slotnpages);
1374     printf("Used slots: %d\n", pkgdb->nslots);
1375     printf("Free slots: %d\n", pkgdb->slotnpages * (PAGE_SIZE / SLOT_SIZE) - pkgdb->nslots);
1376     printf("Blob area size: %d\n", (pkgdb->fileblks - pkgdb->slotnpages * (PAGE_SIZE / BLK_SIZE)) * BLK_SIZE);
1377     printf("Blob area used: %d\n", usedblks * BLK_SIZE);
1378     rpmpkgUnlock(pkgdb, 0);
1379     return RPMRC_OK;
1380 }
1381 
1382