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