1 #define _GNU_SOURCE
2 
3 #include "system.h"
4 
5 #include <rpm/rpmlog.h>
6 
7 #include <sys/types.h>
8 #include <sys/stat.h>
9 #include <sys/file.h>
10 #include <fcntl.h>
11 #include <stdio.h>
12 #include <time.h>
13 #include <unistd.h>
14 #include <string.h>
15 #include <stdlib.h>
16 #include <sys/mman.h>
17 #include <endian.h>
18 #include <libgen.h>
19 #include <dirent.h>
20 
21 #include "rpmxdb.h"
22 
23 #define RPMRC_OK 0
24 #define RPMRC_NOTFOUND 1
25 #define RPMRC_FAIL 2
26 
27 typedef struct rpmxdb_s {
28     rpmpkgdb pkgdb;             /* master database */
29     char *filename;
30     int fd;
31     int flags;
32     int mode;
33     int rdonly;
34     unsigned int pagesize;
35     unsigned int generation;
36     unsigned int slotnpages;
37     unsigned int usergeneration;
38 
39     unsigned char *mapped;
40     int mappedrw;
41     unsigned int mappedlen;
42 
43     struct xdb_slot {
44 	unsigned int slotno;
45 	unsigned int blobtag;
46 	unsigned int subtag;
47 	unsigned char *mapped;
48 	int mapflags;
49 	unsigned int startpage;
50 	unsigned int pagecnt;
51 	void (*mapcallback)(rpmxdb xdb, void *data, void *newaddr, size_t newsize);
52 	void *mapcallbackdata;
53 	unsigned int next;
54 	unsigned int prev;
55     } *slots;
56     unsigned int nslots;
57     unsigned int firstfree;
58     unsigned int usedblobpages;
59     unsigned int systempagesize;
60     int dofsync;
61     unsigned int locked_excl;
62 } *rpmxdb;
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 /* aligned versions */
le2ha(unsigned char * p)74 static inline unsigned int le2ha(unsigned char *p)
75 {
76     unsigned int x = *(unsigned int *)p;
77     return le32toh(x);
78 }
79 
h2lea(unsigned int x,unsigned char * p)80 static inline void h2lea(unsigned int x, unsigned char *p)
81 {
82     *(unsigned int *)p = htole32(x);
83 }
84 
85 
86 #define XDB_MAGIC     ('R' | 'p' << 8 | 'm' << 16 | 'X' << 24)
87 #define XDB_VERSION	0
88 
89 #define XDB_OFFSET_MAGIC	0
90 #define XDB_OFFSET_VERSION	4
91 #define XDB_OFFSET_GENERATION	8
92 #define XDB_OFFSET_SLOTNPAGES	12
93 #define XDB_OFFSET_PAGESIZE	16
94 #define XDB_OFFSET_USERGENERATION	20
95 
96 /* must be multiple of SLOT_SIZE */
97 #define XDB_HEADER_SIZE		32
98 
99 #define SLOT_MAGIC     ('S' | 'l' << 8 | 'o' << 16)
100 
101 #define SLOT_SIZE		16
102 #define SLOT_START (XDB_HEADER_SIZE / SLOT_SIZE)
103 
104 
105 /* low level map/remap a file into memory */
mapmem(void * oldaddr,size_t oldsize,size_t newsize,int prot,int fd,off_t offset)106 static void *mapmem(void *oldaddr, size_t oldsize, size_t newsize, int prot, int fd, off_t offset)
107 {
108     if (oldaddr) {
109 #if HAVE_MREMAP
110 	return mremap(oldaddr, oldsize, newsize, MREMAP_MAYMOVE);
111 #else
112 	void *mapped = mmap(0, newsize, prot, MAP_SHARED, fd, offset);
113 	if (mapped != MAP_FAILED)
114 	    munmap(oldaddr, oldsize);
115 	return mapped;
116 #endif
117     }
118     return mmap(0, newsize, prot, MAP_SHARED, fd, offset);
119 }
120 
121 /* unmap a mapped region */
unmapmem(void * addr,size_t size)122 static void unmapmem(void *addr, size_t size)
123 {
124   munmap(addr, size);
125 }
126 
127 #define ROUNDTOSYSTEMPAGE(xdb, size) (((size) + (xdb->systempagesize - 1)) & ~(xdb->systempagesize - 1))
128 
129 /* xdb header mapping functions */
mapheader(rpmxdb xdb,unsigned int slotnpages,int rw)130 static int mapheader(rpmxdb xdb, unsigned int slotnpages, int rw)
131 {
132     unsigned char *mapped;
133     size_t mappedlen = slotnpages * xdb->pagesize;
134     int mapflags = rw ? PROT_READ | PROT_WRITE : PROT_READ;
135 
136     mappedlen = ROUNDTOSYSTEMPAGE(xdb, mappedlen);
137     mapped = mapmem(xdb->mapped, xdb->mappedlen, mappedlen, mapflags, xdb->fd, 0);
138     if ((void *)mapped == MAP_FAILED)
139 	return RPMRC_FAIL;
140     xdb->mapped = mapped;
141     xdb->mappedlen = mappedlen;
142     xdb->mappedrw = rw;
143     return RPMRC_OK;
144 }
145 
unmapheader(rpmxdb xdb)146 static void unmapheader(rpmxdb xdb)
147 {
148     unmapmem(xdb->mapped, xdb->mappedlen);
149     xdb->mapped = 0;
150     xdb->mappedlen = 0;
151 }
152 
153 /* slot mapping functions */
mapslot(rpmxdb xdb,struct xdb_slot * slot)154 static int mapslot(rpmxdb xdb, struct xdb_slot *slot)
155 {
156     void *mapped;
157     size_t off, size, shift;
158 
159     if (slot->mapped)
160 	return RPMRC_FAIL;
161     size = slot->pagecnt * xdb->pagesize;
162     off = slot->startpage * xdb->pagesize;
163     shift = 0;
164     if (xdb->pagesize != xdb->systempagesize) {
165 	shift = off & (xdb->systempagesize - 1);
166 	off -= shift;
167 	size += shift;
168 	size = ROUNDTOSYSTEMPAGE(xdb, size);
169     }
170     mapped = mapmem(0, 0, size, slot->mapflags, xdb->fd, off);
171     if (mapped == MAP_FAILED)
172 	return RPMRC_FAIL;
173     slot->mapped = (unsigned char *)mapped + shift;
174     return RPMRC_OK;
175 }
176 
unmapslot(rpmxdb xdb,struct xdb_slot * slot)177 static void unmapslot(rpmxdb xdb, struct xdb_slot *slot)
178 {
179     unsigned char *mapped = slot->mapped;
180     size_t size;
181     if (!mapped)
182 	return;
183     size = slot->pagecnt * xdb->pagesize;
184     if (xdb->pagesize != xdb->systempagesize) {
185 	size_t off = slot->startpage * xdb->pagesize;
186 	size_t shift = off & (xdb->systempagesize - 1);
187 	size += shift;
188 	size = ROUNDTOSYSTEMPAGE(xdb, size);
189 	mapped -= shift;
190     }
191     unmapmem(mapped, size);
192     slot->mapped = 0;
193 }
194 
remapslot(rpmxdb xdb,struct xdb_slot * slot,unsigned int newpagecnt)195 static int remapslot(rpmxdb xdb, struct xdb_slot *slot, unsigned int newpagecnt)
196 {
197     unsigned char *mapped = slot->mapped;
198     size_t off, oldsize, newsize, shift;
199 
200     oldsize = slot->pagecnt * xdb->pagesize;
201     newsize = newpagecnt * xdb->pagesize;
202     off = slot->startpage * xdb->pagesize;
203     shift = 0;
204     if (xdb->pagesize != xdb->systempagesize) {
205 	shift = off & (xdb->systempagesize - 1);
206 	off -= shift;
207 	oldsize += shift;
208 	newsize += shift;
209 	oldsize = ROUNDTOSYSTEMPAGE(xdb, oldsize);
210 	newsize = ROUNDTOSYSTEMPAGE(xdb, newsize);
211     }
212     mapped = mapmem(mapped ? mapped - shift : 0, oldsize, newsize, slot->mapflags, xdb->fd, off);
213     if ((void *)mapped == MAP_FAILED)
214 	return RPMRC_FAIL;
215     slot->mapped = (unsigned char *)mapped + shift;
216     slot->pagecnt = newpagecnt;
217     return RPMRC_OK;
218 }
219 
220 
usedslots_cmp(const void * a,const void * b)221 static int usedslots_cmp(const void *a, const void *b)
222 {
223     struct xdb_slot *sa = *(struct xdb_slot **)a;
224     struct xdb_slot *sb = *(struct xdb_slot **)b;
225     if (sa->startpage == sb->startpage) {
226       return sa->pagecnt > sb->pagecnt ? 1 : sa->pagecnt < sb->pagecnt ? -1 : 0;
227     }
228     return sa->startpage > sb->startpage ? 1 : -1;
229 }
230 
rpmxdbReadHeaderRaw(rpmxdb xdb,unsigned int * generationp,unsigned int * slotnpagesp,unsigned int * pagesizep,unsigned int * usergenerationp)231 static int rpmxdbReadHeaderRaw(rpmxdb xdb, unsigned int *generationp, unsigned int *slotnpagesp, unsigned int *pagesizep, unsigned int *usergenerationp)
232 {
233     unsigned int header[XDB_HEADER_SIZE / sizeof(unsigned int)];
234     unsigned int version;
235     if (pread(xdb->fd, header, sizeof(header), 0) != sizeof(header))
236 	return RPMRC_FAIL;
237     if (le2ha((unsigned char *)header + XDB_OFFSET_MAGIC) != XDB_MAGIC)
238 	return RPMRC_FAIL;
239     version = le2ha((unsigned char *)header + XDB_OFFSET_VERSION);
240     if (version != XDB_VERSION) {
241 	rpmlog(RPMLOG_ERR, _("rpmxdb: Version mismatch. Expected version: %u. "
242 	    "Found version: %u\n"), XDB_VERSION, version);
243 	return RPMRC_FAIL;
244     }
245     *generationp = le2ha((unsigned char *)header + XDB_OFFSET_GENERATION);
246     *slotnpagesp = le2ha((unsigned char *)header + XDB_OFFSET_SLOTNPAGES);
247     *pagesizep = le2ha((unsigned char *)header + XDB_OFFSET_PAGESIZE);
248     *usergenerationp = le2ha((unsigned char *)header + XDB_OFFSET_USERGENERATION);
249     if (!*slotnpagesp || !*pagesizep)
250 	return RPMRC_FAIL;
251     return RPMRC_OK;
252 }
253 
rpmxdbReadHeader(rpmxdb xdb,int rw)254 static int rpmxdbReadHeader(rpmxdb xdb, int rw)
255 {
256     struct xdb_slot *slot;
257     unsigned int slotnpages, pagesize, generation, usergeneration;
258     unsigned int page, *lastfreep;
259     unsigned char *pageptr;
260     struct xdb_slot *slots, **usedslots, *lastslot;
261     unsigned int nslots;
262     unsigned int usedblobpages;
263     int i, nused, slotno;
264     struct stat stb;
265 
266     if (xdb->mapped) {
267 	if (le2ha(xdb->mapped + XDB_OFFSET_GENERATION) == xdb->generation) {
268 	    if (rw && !xdb->mappedrw) {
269 		unmapheader(xdb);
270 		if (mapheader(xdb, xdb->slotnpages, rw))
271 		    return RPMRC_FAIL;
272 	    }
273 	    return RPMRC_OK;
274 	}
275 	unmapheader(xdb);
276     }
277     if (fstat(xdb->fd, &stb)) {
278         return RPMRC_FAIL;
279     }
280     if (rpmxdbReadHeaderRaw(xdb, &generation, &slotnpages, &pagesize, &usergeneration))
281 	return RPMRC_FAIL;
282     if (stb.st_size % pagesize != 0)
283 	return RPMRC_FAIL;
284     xdb->pagesize = pagesize;
285 
286     if (mapheader(xdb, slotnpages, rw))
287 	return RPMRC_FAIL;
288 
289     /* read in all slots */
290     xdb->firstfree = 0;
291     nslots = slotnpages * (pagesize / SLOT_SIZE) - SLOT_START + 1;
292     slots = xcalloc(nslots + 1, sizeof(struct xdb_slot));
293     usedslots = xcalloc(nslots + 1, sizeof(struct xdb_slot *));
294     nused = 0;
295     slotno = 1;
296     slot = slots + 1;
297     usedblobpages = 0;
298     lastfreep = &xdb->firstfree;
299     for (page = 0, pageptr = xdb->mapped; page < slotnpages; page++, pageptr += pagesize) {
300 	unsigned int o;
301 	for (o = page ? 0 : SLOT_START * SLOT_SIZE; o < pagesize; o += SLOT_SIZE, slotno++, slot++) {
302 	    unsigned char *pp = pageptr + o;
303 	    slot->slotno = slotno;
304 	    slot->subtag = le2ha(pp);
305 	    if ((slot->subtag & 0x00ffffff) != SLOT_MAGIC) {
306 		free(slots);
307 		free(usedslots);
308 		unmapheader(xdb);
309 		return RPMRC_FAIL;
310 	    }
311 	    slot->subtag = (slot->subtag >> 24) & 255;
312 	    slot->blobtag = le2ha(pp + 4);
313 	    slot->startpage = le2ha(pp + 8);
314 	    slot->pagecnt = le2ha(pp + 12);
315 	    if (slot->pagecnt == 0 && slot->startpage)	/* empty but used slot? */
316 		slot->startpage = slotnpages;
317 	    if (!slot->startpage) {
318 		*lastfreep = slotno;
319 		lastfreep = &slot->next;
320 	    } else {
321 		usedslots[nused++] = slot;
322 		usedblobpages += slot->pagecnt;
323 	    }
324 	}
325     }
326     if (nused > 1) {
327 	qsort(usedslots, nused, sizeof(*usedslots), usedslots_cmp);
328     }
329     /* now chain em */
330     slots[0].pagecnt = slotnpages;
331     lastslot = slots;
332     for (i = 0; i < nused; i++, lastslot = slot) {
333 	slot = usedslots[i];
334 	if (lastslot->startpage + lastslot->pagecnt > slot->startpage) {
335 	    free(slots);
336 	    free(usedslots);
337 	    unmapheader(xdb);
338 	    return RPMRC_FAIL;
339 	}
340 	lastslot->next = slot->slotno;
341 	slot->prev = lastslot->slotno;
342     }
343     lastslot->next = nslots;
344     slots[nslots].slotno = nslots;
345     slots[nslots].prev = lastslot->slotno;
346     slots[nslots].startpage = stb.st_size / pagesize;
347     free(usedslots);
348     /* now sync with the old slot data */
349     if (xdb->slots) {
350 	for (i = 1, slot = xdb->slots + i; i < xdb->nslots; i++, slot++) {
351 	    if (slot->startpage && (slot->mapped || slot->mapcallback)) {
352 		struct xdb_slot *nslot;
353 		if (i >= nslots || !slots[i].startpage || slots[i].blobtag != slot->blobtag || slots[i].subtag != slot->subtag) {
354 		    /* slot is gone */
355 		    if (slot->mapped) {
356 			unmapslot(xdb, slot);
357 			slot->mapcallback(xdb, slot->mapcallbackdata, 0, 0);
358 		    }
359 		    continue;
360 		}
361 		nslot = slots + i;
362 		if (slot->mapcallback) {
363 		    nslot->mapflags = slot->mapflags;
364 		    nslot->mapcallback = slot->mapcallback;
365 		    nslot->mapcallbackdata = slot->mapcallbackdata;
366 		}
367 		if (slot->startpage != nslot->startpage || slot->pagecnt != nslot->pagecnt) {
368 		    /* slot moved or was resized */
369 		    if (slot->mapped)
370 			unmapslot(xdb, slot);
371 		    if (nslot->mapcallback) {
372 			if (nslot->pagecnt) {
373 			    mapslot(xdb, nslot);
374 			    nslot->mapcallback(xdb, nslot->mapcallbackdata, nslot->mapped, nslot->mapped ? nslot->pagecnt * xdb->pagesize : 0);
375 			} else {
376 			    nslot->mapcallback(xdb, nslot->mapcallbackdata, 0, 0);
377 			}
378 		    }
379 		} else {
380 		    nslot->mapped = slot->mapped;
381 		}
382 	    }
383 	}
384 	free(xdb->slots);
385     }
386     xdb->slots = slots;
387     xdb->nslots = nslots;
388     xdb->generation = generation;
389     xdb->slotnpages = slotnpages;
390     xdb->usergeneration = usergeneration;
391     xdb->usedblobpages = usedblobpages;
392     return RPMRC_OK;
393 }
394 
rpmxdbWriteHeader(rpmxdb xdb)395 static void rpmxdbWriteHeader(rpmxdb xdb)
396 {
397     h2lea(XDB_MAGIC, xdb->mapped + XDB_OFFSET_MAGIC);
398     h2lea(XDB_VERSION, xdb->mapped + XDB_OFFSET_VERSION);
399     h2lea(xdb->generation, xdb->mapped + XDB_OFFSET_GENERATION);
400     h2lea(xdb->slotnpages, xdb->mapped + XDB_OFFSET_SLOTNPAGES);
401     h2lea(xdb->pagesize, xdb->mapped + XDB_OFFSET_PAGESIZE);
402     h2lea(xdb->usergeneration, xdb->mapped + XDB_OFFSET_USERGENERATION);
403 }
404 
rpmxdbUpdateSlot(rpmxdb xdb,struct xdb_slot * slot)405 static void rpmxdbUpdateSlot(rpmxdb xdb, struct xdb_slot *slot)
406 {
407     unsigned char *pp = xdb->mapped + (SLOT_START - 1 + slot->slotno) * SLOT_SIZE;
408     h2lea(SLOT_MAGIC | (slot->subtag << 24), pp);
409     h2lea(slot->blobtag, pp + 4);
410     if (slot->pagecnt || !slot->startpage)
411 	h2lea(slot->startpage, pp + 8);
412     else
413 	h2lea(1, pp + 8);	/* "empty but used" blobs always start at 1 */
414     h2lea(slot->pagecnt, pp + 12);
415     xdb->generation++;
416     h2lea(xdb->generation, xdb->mapped + XDB_OFFSET_GENERATION);
417 }
418 
rpmxdbWriteEmptyPages(rpmxdb xdb,unsigned int pageno,unsigned int count)419 static int rpmxdbWriteEmptyPages(rpmxdb xdb, unsigned int pageno, unsigned int count)
420 {
421     unsigned char *page;
422     if (!count)
423 	return RPMRC_OK;
424     page = xmalloc(xdb->pagesize);
425     memset(page, 0, xdb->pagesize);
426     for (; count; count--, pageno++) {
427 	if (pwrite(xdb->fd, page, xdb->pagesize, pageno * xdb->pagesize) != xdb->pagesize) {
428 	    free(page);
429 	    return RPMRC_FAIL;
430 	}
431     }
432     free(page);
433     return RPMRC_OK;
434 }
435 
rpmxdbWriteEmptySlotpage(rpmxdb xdb,int pageno)436 static int rpmxdbWriteEmptySlotpage(rpmxdb xdb, int pageno)
437 {
438     unsigned char *page;
439     int i, spp;
440     page = xmalloc(xdb->pagesize);
441     memset(page, 0, xdb->pagesize);
442     spp = xdb->pagesize / SLOT_SIZE;	/* slots per page */
443     for (i = pageno ? 0 : SLOT_START; i < spp; i++)
444         h2le(SLOT_MAGIC, page + i * SLOT_SIZE);
445     if (!pageno) {
446 	/* only used when called from InitInternal */
447 	if (xdb->mapped) {
448 	    free(page);
449 	    return RPMRC_FAIL;
450 	}
451 	xdb->mapped = page;
452 	rpmxdbWriteHeader(xdb);
453 	xdb->mapped = 0;
454     }
455     if (pwrite(xdb->fd, page, xdb->pagesize, pageno * xdb->pagesize) != xdb->pagesize) {
456 	free(page);
457 	return RPMRC_FAIL;
458     }
459     free(page);
460     return RPMRC_OK;
461 }
462 
rpmxdbInitInternal(rpmxdb xdb)463 static int rpmxdbInitInternal(rpmxdb xdb)
464 {
465     struct stat stb;
466     if (fstat(xdb->fd, &stb)) {
467         return RPMRC_FAIL;
468     }
469     if (stb.st_size == 0) {
470         xdb->slotnpages = 1;
471         xdb->generation++;
472 	xdb->pagesize = sysconf(_SC_PAGE_SIZE);
473         if (rpmxdbWriteEmptySlotpage(xdb, 0)) {
474             return RPMRC_FAIL;
475         }
476     }
477     return RPMRC_OK;
478 }
479 
480 /* we use the master pdb for locking */
rpmxdbLockOnly(rpmxdb xdb,int excl)481 static int rpmxdbLockOnly(rpmxdb xdb, int excl)
482 {
483     int rc;
484     if (excl && xdb->rdonly)
485         return RPMRC_FAIL;
486     rc = rpmpkgLock(xdb->pkgdb, excl);
487     if (!rc && excl)
488 	xdb->locked_excl++;
489     return rc;
490 }
491 
492 /* This is similar to rpmxdbLockReadHeader. It does the
493  * ReadHeader to sync the mappings if xdb moved some blobs.
494  * Note that we just ask for rad-only access in the
495  * rpmxdbReadHeader call.
496  */
rpmxdbLock(rpmxdb xdb,int excl)497 int rpmxdbLock(rpmxdb xdb, int excl)
498 {
499     if (rpmxdbLockOnly(xdb, excl))
500 	return RPMRC_FAIL;
501     if (rpmxdbReadHeader(xdb, 0)) {
502 	rpmxdbUnlock(xdb, excl);
503         return RPMRC_FAIL;
504     }
505     return RPMRC_OK;
506 }
507 
rpmxdbUnlock(rpmxdb xdb,int excl)508 int rpmxdbUnlock(rpmxdb xdb, int excl)
509 {
510     if (excl && xdb->locked_excl) {
511 	xdb->locked_excl--;
512 	if (!xdb->locked_excl && xdb->mapped && xdb->mappedrw) {
513 	    unmapheader(xdb);
514 	    mapheader(xdb, xdb->slotnpages, 0);
515 	}
516     }
517     return rpmpkgUnlock(xdb->pkgdb, excl);
518 }
519 
520 /* Like rpmxdbLock, but map the header rw if excl is set.
521  * This is what the functions in this module use, whereas
522  * rpmidx uses rpmxdbLock.
523  */
rpmxdbLockReadHeader(rpmxdb xdb,int excl)524 static int rpmxdbLockReadHeader(rpmxdb xdb, int excl)
525 {
526     if (rpmxdbLockOnly(xdb, excl))
527 	return RPMRC_FAIL;
528     if (rpmxdbReadHeader(xdb, excl)) {
529 	rpmxdbUnlock(xdb, excl);
530         return RPMRC_FAIL;
531     }
532     return RPMRC_OK;
533 }
534 
rpmxdbInit(rpmxdb xdb)535 static int rpmxdbInit(rpmxdb xdb)
536 {
537     int rc;
538 
539     if (rpmxdbLockOnly(xdb, 1))
540         return RPMRC_FAIL;
541     rc = rpmxdbInitInternal(xdb);
542     rpmxdbUnlock(xdb, 1);
543     return rc;
544 }
545 
rpmxdbFsyncDir(const char * filename)546 static int rpmxdbFsyncDir(const char *filename)
547 {
548     int rc = RPMRC_OK;
549     DIR *pdir;
550     char *filenameCopy = xstrdup(filename);
551 
552     if ((pdir = opendir(dirname(filenameCopy))) == NULL) {
553 	free(filenameCopy);
554 	return RPMRC_FAIL;
555     }
556     if (fsync(dirfd(pdir)) == -1)
557 	rc = RPMRC_FAIL;
558     closedir(pdir);
559     free(filenameCopy);
560     return rc;
561 }
562 
rpmxdbOpen(rpmxdb * xdbp,rpmpkgdb pkgdb,const char * filename,int flags,int mode)563 int rpmxdbOpen(rpmxdb *xdbp, rpmpkgdb pkgdb, const char *filename, int flags, int mode)
564 {
565     struct stat stb;
566     rpmxdb xdb;
567 
568     *xdbp = 0;
569     xdb = xcalloc(1, sizeof(*xdb));
570     xdb->pkgdb = pkgdb;
571     xdb->filename = xstrdup(filename);
572     xdb->systempagesize = sysconf(_SC_PAGE_SIZE);
573     if ((flags & (O_RDONLY|O_RDWR)) == O_RDONLY)
574 	xdb->rdonly = 1;
575     if ((xdb->fd = open(filename, flags, mode)) == -1) {
576 	free(xdb->filename);
577 	free(xdb);
578 	return RPMRC_FAIL;
579     }
580     if (fstat(xdb->fd, &stb)) {
581 	close(xdb->fd);
582 	free(xdb->filename);
583 	free(xdb);
584 	return RPMRC_FAIL;
585     }
586     if (stb.st_size == 0) {
587 	/* created new database */
588 	if (rpmxdbFsyncDir(xdb->filename)) {
589 	    close(xdb->fd);
590 	    free(xdb->filename);
591 	    free(xdb);
592 	    return RPMRC_FAIL;
593 	}
594 	if (rpmxdbInit(xdb)) {
595 	    close(xdb->fd);
596 	    free(xdb->filename);
597 	    free(xdb);
598 	    return RPMRC_FAIL;
599 	}
600     }
601     xdb->flags = flags;
602     xdb->mode = mode;
603     xdb->dofsync = 1;
604     *xdbp = xdb;
605     return RPMRC_OK;
606 }
607 
rpmxdbClose(rpmxdb xdb)608 void rpmxdbClose(rpmxdb xdb)
609 {
610     struct xdb_slot *slot;
611     int i;
612 
613     for (i = 1, slot = xdb->slots + 1; i < xdb->nslots; i++, slot++) {
614 	if (slot->mapped) {
615 	    unmapslot(xdb, slot);
616 	    slot->mapcallback(xdb, slot->mapcallbackdata, 0, 0);
617 	}
618     }
619     if (xdb->slots)
620 	free(xdb->slots);
621     if (xdb->mapped)
622 	unmapheader(xdb);
623     if (xdb->fd >= 0)
624 	close(xdb->fd);
625     if (xdb->filename)
626 	free(xdb->filename);
627     free(xdb);
628 }
629 
630 /* moves the blob to a given new location (possibly resizeing) */
moveblobto(rpmxdb xdb,struct xdb_slot * oldslot,struct xdb_slot * afterslot,unsigned int newpagecnt)631 static int moveblobto(rpmxdb xdb, struct xdb_slot *oldslot, struct xdb_slot *afterslot, unsigned int newpagecnt)
632 {
633     struct xdb_slot *nextslot;
634     unsigned int newstartpage, oldpagecnt;
635     unsigned int tocopy;
636     int didmap;
637 
638     newstartpage = afterslot->startpage + afterslot->pagecnt;
639     nextslot = xdb->slots + afterslot->next;
640 
641     /* make sure there's enough room */
642     if (newpagecnt > nextslot->startpage - newstartpage)
643 	return RPMRC_FAIL;
644 
645 #if 0
646     printf("moveblobto %d %d %d %d, afterslot %d\n", oldslot->startpage, oldslot->pagecnt, newstartpage, newpagecnt, afterslot->slotno);
647 #endif
648     /* map old content */
649     didmap = 0;
650     oldpagecnt = oldslot->pagecnt;
651     if (!oldslot->mapped && oldpagecnt) {
652 	if (!oldslot->mapcallback)
653 	    oldslot->mapflags = PROT_READ;
654 	if (mapslot(xdb, oldslot))
655 	    return RPMRC_FAIL;
656         didmap = 1;
657     }
658 
659     /* copy content */
660     tocopy = newpagecnt > oldpagecnt ? oldpagecnt : newpagecnt;
661     if (tocopy && pwrite(xdb->fd, oldslot->mapped, tocopy * xdb->pagesize, newstartpage * xdb->pagesize) != tocopy * xdb->pagesize) {
662 	if (didmap)
663 	    unmapslot(xdb, oldslot);
664 	return RPMRC_FAIL;
665     }
666     /* zero out new pages */
667     if (newpagecnt > oldpagecnt) {
668 	if (rpmxdbWriteEmptyPages(xdb, newstartpage + oldpagecnt, newpagecnt - oldpagecnt)) {
669 	    if (didmap)
670 		unmapslot(xdb, oldslot);
671 	    return RPMRC_FAIL;
672 	}
673     }
674 
675     if (oldslot->mapped)
676 	unmapslot(xdb, oldslot);
677 
678     /* set new offset and position */
679     oldslot->startpage = newstartpage;
680     oldslot->pagecnt = newpagecnt;
681     rpmxdbUpdateSlot(xdb, oldslot);
682     xdb->usedblobpages -= oldpagecnt;
683     xdb->usedblobpages += newpagecnt;
684 
685     if (afterslot != oldslot && nextslot != oldslot) {
686 	/* remove from old chain */
687 	xdb->slots[oldslot->prev].next = oldslot->next;
688 	xdb->slots[oldslot->next].prev = oldslot->prev;
689 
690 	/* chain into new position, between lastslot and nextslot */
691 	oldslot->prev = afterslot->slotno;
692 	afterslot->next = oldslot->slotno;
693 
694 	oldslot->next = nextslot->slotno;
695 	nextslot->prev = oldslot->slotno;
696     }
697 
698     /* map again (if needed) */
699     if (oldslot->mapcallback) {
700 	if (newpagecnt) {
701 	    if (mapslot(xdb, oldslot))
702 		oldslot->mapped = 0;	/* XXX: HELP, what can we do here? */
703 	}
704 	oldslot->mapcallback(xdb, oldslot->mapcallbackdata, oldslot->mapped, oldslot->mapped ? oldslot->pagecnt * xdb->pagesize : 0);
705     }
706     return RPMRC_OK;
707 }
708 
709 /* moves the blob to a new location (possibly resizeing) */
moveblob(rpmxdb xdb,struct xdb_slot * oldslot,unsigned int newpagecnt)710 static int moveblob(rpmxdb xdb, struct xdb_slot *oldslot, unsigned int newpagecnt)
711 {
712     struct xdb_slot *slot, *lastslot;
713     unsigned int nslots;
714     unsigned int freecnt;
715     int i;
716 
717     nslots = xdb->nslots;
718     freecnt = 0;
719     lastslot = xdb->slots;
720     for (i = xdb->slots[0].next; ; lastslot = slot, i = slot->next) {
721 	slot = xdb->slots + i;
722 	freecnt = slot->startpage - (lastslot->startpage + lastslot->pagecnt);
723 	if (freecnt >= newpagecnt)
724 	    break;
725 	if (i == nslots)
726 	    break;
727     }
728     if (i == nslots && newpagecnt > freecnt) {
729 	/* need to grow the file */
730 	if (rpmxdbWriteEmptyPages(xdb, slot->startpage, newpagecnt - freecnt)) {
731 	    return RPMRC_FAIL;
732 	}
733 	slot->startpage += newpagecnt - freecnt;
734     }
735     return moveblobto(xdb, oldslot, lastslot, newpagecnt);
736 }
737 
738 /* move the two blobs at the end of our file to the free area after the provided slot */
moveblobstofront(rpmxdb xdb,struct xdb_slot * afterslot)739 static int moveblobstofront(rpmxdb xdb, struct xdb_slot *afterslot)
740 {
741     struct xdb_slot *slot1, *slot2;
742     unsigned int freestart = afterslot->startpage + afterslot->pagecnt;
743     unsigned int freecount = xdb->slots[afterslot->next].startpage - freestart;
744 
745     slot1 = xdb->slots + xdb->slots[xdb->nslots].prev;
746     if (slot1 == xdb->slots)
747 	slot1 = slot2 = 0;
748     else {
749 	slot2 = xdb->slots + slot1->prev;
750 	if (slot2 == xdb->slots)
751 	    slot2 = 0;
752     }
753     if (slot1->pagecnt < slot2->pagecnt) {
754 	struct xdb_slot *tmp = slot1;
755 	slot1 = slot2;
756 	slot2 = tmp;
757     }
758     if (slot1 && slot1->pagecnt && slot1->pagecnt <= freecount && slot1->startpage > freestart) {
759 	if (moveblobto(xdb, slot1, afterslot, slot1->pagecnt))
760 	    return RPMRC_FAIL;
761 	freestart += slot1->pagecnt;
762 	freecount -= slot1->pagecnt;
763 	afterslot = slot1;
764     }
765     if (slot2 && slot2->pagecnt && slot2->pagecnt <= freecount && slot2->startpage > freestart) {
766 	if (moveblobto(xdb, slot2, afterslot, slot2->pagecnt))
767 	    return RPMRC_FAIL;
768     }
769     return RPMRC_OK;
770 }
771 
772 /* add a single page containing empty slots */
addslotpage(rpmxdb xdb)773 static int addslotpage(rpmxdb xdb)
774 {
775     struct xdb_slot *slot;
776     int i, spp, nslots;
777 
778     if (xdb->firstfree)
779 	return RPMRC_FAIL;
780 
781     /* move first blob if needed */
782     nslots = xdb->nslots;
783     for (i = xdb->slots[0].next; i != nslots; i = slot->next) {
784 	slot = xdb->slots + i;
785 	if (slot->pagecnt)
786 	    break;
787     }
788     if (i != nslots && slot->pagecnt && slot->startpage == xdb->slotnpages) {
789 	/* the blob at this slot is in the way. move it. */
790 	if (moveblob(xdb, slot, slot->pagecnt))
791 	    return RPMRC_FAIL;
792     }
793 
794     spp = xdb->pagesize / SLOT_SIZE;	/* slots per page */
795     slot = xrealloc(xdb->slots, (nslots + 1 + spp) * sizeof(*slot));
796     xdb->slots = slot;
797 
798     if (rpmxdbWriteEmptySlotpage(xdb, xdb->slotnpages))
799 	return RPMRC_FAIL;
800 
801     /* remap the header */
802     if (mapheader(xdb, xdb->slotnpages + 1, xdb->mappedrw))
803 	return RPMRC_FAIL;
804 
805     /* update the header */
806     xdb->slotnpages++;
807     xdb->generation++;
808     rpmxdbWriteHeader(xdb);
809 
810     /* fixup empty but used slots */
811     for (i = xdb->slots[0].next; i != nslots; i = slot->next) {
812 	slot = xdb->slots + i;
813 	if (slot->startpage >= xdb->slotnpages)
814 	    break;
815 	slot->startpage = xdb->slotnpages;
816 	if (slot->pagecnt)
817 	    abort();
818     }
819 
820     /* move tail element to the new end */
821     slot = xdb->slots + nslots + spp;
822     *slot = xdb->slots[nslots];
823     slot->slotno = nslots + spp;
824     xdb->slots[slot->prev].next = slot->slotno;
825 
826     /* we have a new slotpage */
827     xdb->nslots += spp;
828     xdb->slots[0].pagecnt++;
829 
830     /* add new free slots to the firstfree chain */
831     memset(xdb->slots + nslots, 0, sizeof(*slot) * spp);
832     for (i = 0; i < spp - 1; i++) {
833 	xdb->slots[nslots + i].slotno = nslots + i;
834 	xdb->slots[nslots + i].next = nslots + i + 1;
835     }
836     xdb->slots[nslots + i].slotno = nslots + i;
837     xdb->firstfree = nslots;
838     return RPMRC_OK;
839 }
840 
createblob(rpmxdb xdb,unsigned int * idp,unsigned int blobtag,unsigned int subtag)841 static int createblob(rpmxdb xdb, unsigned int *idp, unsigned int blobtag, unsigned int subtag)
842 {
843     struct xdb_slot *slot;
844     unsigned int id;
845 
846     if (subtag > 255)
847 	return RPMRC_FAIL;
848     if (!xdb->firstfree) {
849 	if (addslotpage(xdb))
850 	    return RPMRC_FAIL;
851     }
852     id = xdb->firstfree;
853     slot = xdb->slots + xdb->firstfree;
854     xdb->firstfree = slot->next;
855 
856     slot->mapped = 0;
857     slot->blobtag = blobtag;
858     slot->subtag = subtag;
859     slot->startpage = xdb->slotnpages;
860     slot->pagecnt = 0;
861     rpmxdbUpdateSlot(xdb, slot);
862     /* enqueue */
863     slot->prev = 0;
864     slot->next = xdb->slots[0].next;
865     xdb->slots[slot->next].prev = id;
866     xdb->slots[0].next = id;
867 #if 0
868     printf("createblob #%d %d/%d\n", id, blobtag, subtag);
869 #endif
870     if (slot->slotno != id)
871 	abort();
872     if (slot->mapped)
873 	abort();
874     *idp = id;
875     return RPMRC_OK;
876 }
877 
rpmxdbLookupBlob(rpmxdb xdb,unsigned int * idp,unsigned int blobtag,unsigned int subtag,int flags)878 int rpmxdbLookupBlob(rpmxdb xdb, unsigned int *idp, unsigned int blobtag, unsigned int subtag, int flags)
879 {
880     struct xdb_slot *slot;
881     unsigned int i, nslots;
882     int excl = flags ? 1 : 0;
883     if (rpmxdbLockReadHeader(xdb, excl))
884         return RPMRC_FAIL;
885     nslots = xdb->nslots;
886     slot = 0;
887     for (i = xdb->slots[0].next; i != nslots; i = slot->next) {
888 	slot = xdb->slots + i;
889 	if (slot->blobtag == blobtag && slot->subtag == subtag)
890 	    break;
891     }
892     if (i == nslots)
893 	i = 0;
894     if (i && (flags & O_TRUNC) != 0) {
895 	if (rpmxdbResizeBlob(xdb, i, 0)) {
896 	    rpmxdbUnlock(xdb, excl);
897 	    return RPMRC_FAIL;
898 	}
899     }
900     if (!i && (flags & O_CREAT) != 0) {
901 	if (createblob(xdb, &i, blobtag, subtag)) {
902 	    rpmxdbUnlock(xdb, excl);
903 	    return RPMRC_FAIL;
904 	}
905     }
906     *idp = i;
907     rpmxdbUnlock(xdb, excl);
908     return i ? RPMRC_OK : RPMRC_NOTFOUND;
909 }
910 
rpmxdbDelBlob(rpmxdb xdb,unsigned int id)911 int rpmxdbDelBlob(rpmxdb xdb, unsigned int id)
912 {
913     struct xdb_slot *slot;
914     if (!id)
915 	return RPMRC_FAIL;
916     if (rpmxdbLockReadHeader(xdb, 1))
917         return RPMRC_FAIL;
918     if (id >= xdb->nslots) {
919 	rpmxdbUnlock(xdb, 1);
920         return RPMRC_FAIL;
921     }
922     slot = xdb->slots + id;
923     if (!slot->startpage) {
924 	rpmxdbUnlock(xdb, 1);
925         return RPMRC_OK;
926     }
927     if (slot->mapped) {
928 	unmapslot(xdb, slot);
929 	slot->mapcallback(xdb, slot->mapcallbackdata, 0, 0);
930     }
931     /* remove from old chain */
932     xdb->slots[slot->prev].next = slot->next;
933     xdb->slots[slot->next].prev = slot->prev;
934     xdb->usedblobpages -= slot->pagecnt;
935 
936     if (xdb->usedblobpages * 2 < xdb->slots[xdb->nslots].startpage && (slot->startpage + slot->pagecnt) * 2 < xdb->slots[xdb->nslots].startpage) {
937 	/* freed in first half of pages, move last two blobs if we can */
938 	moveblobstofront(xdb, xdb->slots + slot->prev);
939     }
940 
941     /* zero slot */
942     memset(slot, 0, sizeof(*slot));
943     slot->slotno = id;
944     rpmxdbUpdateSlot(xdb, slot);
945 
946     /* enqueue into free chain */
947     slot->next = xdb->firstfree;
948     xdb->firstfree = slot->slotno;
949 
950     /* check if we should truncate the file */
951     slot = xdb->slots + xdb->slots[xdb->nslots].prev;
952     if (slot->startpage + slot->pagecnt < xdb->slots[xdb->nslots].startpage / 4 * 3) {
953 	unsigned int newend = slot->startpage + slot->pagecnt;
954 	if (!ftruncate(xdb->fd, newend * xdb->pagesize))
955 	    xdb->slots[xdb->nslots].startpage = newend;
956     }
957 
958     rpmxdbUnlock(xdb, 1);
959     return RPMRC_OK;
960 }
961 
rpmxdbDelAllBlobs(rpmxdb xdb)962 int rpmxdbDelAllBlobs(rpmxdb xdb)
963 {
964     unsigned int slotnpages, pagesize, generation, usergeneration;
965     if (rpmxdbLockOnly(xdb, 1))
966 	return RPMRC_FAIL;
967     /* unmap all blobs */
968     if (xdb->slots) {
969 	int i;
970 	struct xdb_slot *slot;
971 	for (i = 1, slot = xdb->slots + i; i < xdb->nslots; i++, slot++) {
972 	    if (slot->startpage && slot->mapped) {
973 		unmapslot(xdb, slot);
974 		slot->mapcallback(xdb, slot->mapcallbackdata, 0, 0);
975 	    }
976 	}
977 	free(xdb->slots);
978 	xdb->slots = 0;
979     }
980     if (xdb->mapped)
981 	unmapheader(xdb);
982     if (rpmxdbReadHeaderRaw(xdb, &generation, &slotnpages, &pagesize, &usergeneration)) {
983 	rpmxdbUnlock(xdb, 1);
984 	return RPMRC_FAIL;
985     }
986     xdb->generation = generation + 1;
987     xdb->slotnpages = 1;
988     xdb->pagesize = pagesize;
989     xdb->usergeneration = usergeneration;
990     if (rpmxdbWriteEmptySlotpage(xdb, 0)) {
991 	rpmxdbUnlock(xdb, 1);
992 	return RPMRC_FAIL;
993     }
994     if (ftruncate(xdb->fd, xdb->pagesize)) {
995 	/* ftruncate failed, but that is not a problem */
996     }
997     rpmxdbUnlock(xdb, 1);
998     return RPMRC_OK;
999 }
1000 
rpmxdbResizeBlob(rpmxdb xdb,unsigned int id,size_t newsize)1001 int rpmxdbResizeBlob(rpmxdb xdb, unsigned int id, size_t newsize)
1002 {
1003     struct xdb_slot *slot;
1004     unsigned int oldpagecnt, newpagecnt;
1005     if (!id)
1006 	return RPMRC_FAIL;
1007     if (rpmxdbLockReadHeader(xdb, 1))
1008         return RPMRC_FAIL;
1009     if (id >= xdb->nslots) {
1010 	rpmxdbUnlock(xdb, 1);
1011         return RPMRC_FAIL;
1012     }
1013     slot = xdb->slots + id;
1014     if (!slot->startpage) {
1015 	rpmxdbUnlock(xdb, 1);
1016         return RPMRC_FAIL;
1017     }
1018     oldpagecnt = slot->pagecnt;
1019     newpagecnt = (newsize + xdb->pagesize - 1) / xdb->pagesize;
1020     if (oldpagecnt && newpagecnt && newpagecnt <= oldpagecnt) {
1021 	/* reducing size. zero to end of page */
1022 	unsigned int pg = newsize & (xdb->pagesize - 1);
1023 	if (pg) {
1024 	    if (slot->mapped) {
1025 		memset(slot->mapped + pg, 0, xdb->pagesize - pg);
1026 	    } else {
1027 		char *empty = xcalloc(1, xdb->pagesize - pg);
1028                 if (pwrite(xdb->fd, empty, xdb->pagesize - pg, (slot->startpage + newpagecnt - 1) * xdb->pagesize + pg ) != xdb->pagesize - pg) {
1029 		    free(empty);
1030 		    rpmxdbUnlock(xdb, 1);
1031 		    return RPMRC_FAIL;
1032 		}
1033 		free(empty);
1034 	    }
1035 	}
1036     }
1037     if (newpagecnt == oldpagecnt) {
1038 	/* no size change */
1039 	rpmxdbUnlock(xdb, 1);
1040         return RPMRC_OK;
1041     }
1042     if (!newpagecnt) {
1043 	/* special case: zero size blob, no longer mapped */
1044 	if (slot->mapped)
1045 	    unmapslot(xdb, slot);
1046 	slot->pagecnt = 0;
1047 	slot->startpage = xdb->slotnpages;
1048 	/* remove from old chain */
1049 	xdb->slots[slot->prev].next = slot->next;
1050 	xdb->slots[slot->next].prev = slot->prev;
1051 	/* enqueue into head */
1052 	slot->prev = 0;
1053 	slot->next = xdb->slots[0].next;
1054 	xdb->slots[slot->next].prev = slot->slotno;
1055 	xdb->slots[0].next = slot->slotno;
1056 	rpmxdbUpdateSlot(xdb, slot);
1057 	xdb->usedblobpages -= oldpagecnt;
1058 	if (slot->mapcallback)
1059 	    slot->mapcallback(xdb, slot->mapcallbackdata, 0, 0);
1060     } else if (newpagecnt <= xdb->slots[slot->next].startpage - slot->startpage) {
1061 	/* can do it inplace */
1062 	if (newpagecnt > oldpagecnt) {
1063 	    /* zero new pages */
1064 	    if (rpmxdbWriteEmptyPages(xdb, slot->startpage + oldpagecnt, newpagecnt - oldpagecnt)) {
1065 		rpmxdbUnlock(xdb, 1);
1066 		return RPMRC_FAIL;
1067 	    }
1068 	}
1069 	if (slot->mapcallback) {
1070 	    if (remapslot(xdb, slot, newpagecnt)) {
1071 		rpmxdbUnlock(xdb, 1);
1072 		return RPMRC_FAIL;
1073 	    }
1074 	} else {
1075 	    if (slot->mapped)
1076 		unmapslot(xdb, slot);
1077 	    slot->pagecnt = newpagecnt;
1078 	}
1079 	rpmxdbUpdateSlot(xdb, slot);
1080 	xdb->usedblobpages -= oldpagecnt;
1081 	xdb->usedblobpages += newpagecnt;
1082 	if (slot->mapcallback)
1083 	    slot->mapcallback(xdb, slot->mapcallbackdata, slot->mapped, slot->pagecnt * xdb->pagesize);
1084     } else {
1085 	/* need to relocate to a new page area */
1086 	if (moveblob(xdb, slot, newpagecnt)) {
1087 	    rpmxdbUnlock(xdb, 1);
1088 	    return RPMRC_FAIL;
1089 	}
1090     }
1091     rpmxdbUnlock(xdb, 1);
1092     return RPMRC_OK;
1093 }
1094 
rpmxdbMapBlob(rpmxdb xdb,unsigned int id,int flags,void (* mapcallback)(rpmxdb xdb,void * data,void * newaddr,size_t newsize),void * mapcallbackdata)1095 int rpmxdbMapBlob(rpmxdb xdb, unsigned int id, int flags, void (*mapcallback)(rpmxdb xdb, void *data, void *newaddr, size_t newsize), void *mapcallbackdata)
1096 {
1097     struct xdb_slot *slot;
1098     if (!id || !mapcallback)
1099 	return RPMRC_FAIL;
1100     if ((flags & (O_RDONLY|O_RDWR)) == O_RDWR && xdb->rdonly)
1101 	return RPMRC_FAIL;
1102     if (rpmxdbLockReadHeader(xdb, 0))
1103         return RPMRC_FAIL;
1104     if (id >= xdb->nslots) {
1105 	rpmxdbUnlock(xdb, 0);
1106         return RPMRC_FAIL;
1107     }
1108     slot = xdb->slots + id;
1109     if (!slot->startpage || slot->mapped) {
1110 	rpmxdbUnlock(xdb, 0);
1111         return RPMRC_FAIL;
1112     }
1113     slot->mapflags = (flags & (O_RDONLY|O_RDWR)) == O_RDWR ? PROT_READ | PROT_WRITE : PROT_READ;
1114     if (slot->pagecnt) {
1115 	if (mapslot(xdb, slot)) {
1116 	    slot->mapflags = 0;
1117 	    rpmxdbUnlock(xdb, 0);
1118 	    return RPMRC_FAIL;
1119 	}
1120     }
1121     slot->mapcallback = mapcallback;
1122     slot->mapcallbackdata = mapcallbackdata;
1123     mapcallback(xdb, mapcallbackdata, slot->mapped, slot->mapped ? slot->pagecnt * xdb->pagesize : 0);
1124     rpmxdbUnlock(xdb, 0);
1125     return RPMRC_OK;
1126 }
1127 
rpmxdbUnmapBlob(rpmxdb xdb,unsigned int id)1128 int rpmxdbUnmapBlob(rpmxdb xdb, unsigned int id)
1129 {
1130     struct xdb_slot *slot;
1131     if (!id)
1132 	return RPMRC_OK;
1133     if (rpmxdbLockReadHeader(xdb, 0))
1134         return RPMRC_FAIL;
1135     if (id >= xdb->nslots) {
1136 	rpmxdbUnlock(xdb, 0);
1137 	return RPMRC_FAIL;
1138     }
1139     slot = xdb->slots + id;
1140     if (slot->mapped) {
1141 	unmapslot(xdb, slot);
1142 	slot->mapcallback(xdb, slot->mapcallbackdata, 0, 0);
1143     }
1144     slot->mapcallback = 0;
1145     slot->mapcallbackdata = 0;
1146     slot->mapflags = 0;
1147     rpmxdbUnlock(xdb, 0);
1148     return RPMRC_OK;
1149 }
1150 
rpmxdbRenameBlob(rpmxdb xdb,unsigned int * idp,unsigned int blobtag,unsigned int subtag)1151 int rpmxdbRenameBlob(rpmxdb xdb, unsigned int *idp, unsigned int blobtag, unsigned int subtag)
1152 {
1153     struct xdb_slot *slot;
1154     unsigned int otherid;
1155     unsigned int id = *idp;
1156     int rc;
1157 
1158     if (!id || subtag > 255)
1159 	return RPMRC_FAIL;
1160     if (rpmxdbLockReadHeader(xdb, 1))
1161         return RPMRC_FAIL;
1162     if (id >= xdb->nslots) {
1163 	rpmxdbUnlock(xdb, 1);
1164         return RPMRC_FAIL;
1165     }
1166     slot = xdb->slots + id;
1167 #if 0
1168     printf("rpmxdbRenameBlob #%d %d/%d -> %d/%d\n", id, slot->blobtag, slot->subtag, blobtag, subtag);
1169 #endif
1170     if (!slot->startpage) {
1171 	rpmxdbUnlock(xdb, 1);
1172 	return RPMRC_FAIL;
1173     }
1174     if (slot->blobtag == blobtag && slot->subtag == subtag) {
1175 	rpmxdbUnlock(xdb, 1);
1176 	return RPMRC_OK;
1177     }
1178     rc = rpmxdbLookupBlob(xdb, &otherid, blobtag, subtag, 0);
1179     if (rc == RPMRC_NOTFOUND)
1180 	otherid = 0;
1181     else if (rc) {
1182 	rpmxdbUnlock(xdb, 1);
1183 	return RPMRC_FAIL;
1184     }
1185     if (otherid) {
1186 #if 0
1187 	printf("(replacing #%d)\n", otherid);
1188 #endif
1189 	if (rpmxdbDelBlob(xdb, otherid)) {
1190 	    rpmxdbUnlock(xdb, 1);
1191 	    return RPMRC_FAIL;
1192 	}
1193 	/* get otherid back from free chain */
1194 	if (xdb->firstfree != otherid)
1195 	    return RPMRC_FAIL;
1196 	xdb->firstfree = xdb->slots[otherid].next;
1197 
1198 	slot->blobtag = blobtag;
1199 	slot->subtag = subtag;
1200 	xdb->slots[otherid] = *slot;
1201 	/* fixup ids */
1202 	xdb->slots[otherid].slotno = otherid;
1203 	xdb->slots[slot->prev].next = otherid;
1204 	xdb->slots[slot->next].prev = otherid;
1205 	/* write */
1206 	rpmxdbUpdateSlot(xdb, xdb->slots + otherid);
1207 	memset(slot, 0, sizeof(*slot));
1208 	slot->slotno = id;
1209 	rpmxdbUpdateSlot(xdb, slot);
1210 	slot->next = xdb->firstfree;
1211 	xdb->firstfree = slot->slotno;
1212 	*idp = otherid;
1213     } else {
1214 	slot = xdb->slots + id;
1215 	slot->blobtag = blobtag;
1216 	slot->subtag = subtag;
1217 	rpmxdbUpdateSlot(xdb, slot);
1218     }
1219     rpmxdbUnlock(xdb, 1);
1220     return RPMRC_OK;
1221 }
1222 
rpmxdbSetFsync(rpmxdb xdb,int dofsync)1223 void rpmxdbSetFsync(rpmxdb xdb, int dofsync)
1224 {
1225     xdb->dofsync = dofsync;
1226 }
1227 
rpmxdbIsRdonly(rpmxdb xdb)1228 int rpmxdbIsRdonly(rpmxdb xdb)
1229 {
1230     return xdb->rdonly;
1231 }
1232 
rpmxdbPagesize(rpmxdb xdb)1233 unsigned int rpmxdbPagesize(rpmxdb xdb)
1234 {
1235     return xdb->pagesize;
1236 }
1237 
rpmxdbFsync(rpmxdb xdb)1238 static int rpmxdbFsync(rpmxdb xdb)
1239 {
1240 #ifdef HAVE_FDATASYNC
1241     return fdatasync(xdb->fd);
1242 #else
1243     return fsync(xdb->fd);
1244 #endif
1245 }
1246 
rpmxdbSetUserGeneration(rpmxdb xdb,unsigned int usergeneration)1247 int rpmxdbSetUserGeneration(rpmxdb xdb, unsigned int usergeneration)
1248 {
1249     if (rpmxdbLockReadHeader(xdb, 1))
1250         return RPMRC_FAIL;
1251     /* sync before the update */
1252     if (xdb->dofsync && rpmxdbFsync(xdb)) {
1253 	rpmxdbUnlock(xdb, 1);
1254 	return RPMRC_FAIL;
1255     }
1256     xdb->usergeneration = usergeneration;
1257     xdb->generation++;
1258     rpmxdbWriteHeader(xdb);
1259     rpmxdbUnlock(xdb, 1);
1260     return RPMRC_OK;
1261 }
1262 
rpmxdbGetUserGeneration(rpmxdb xdb,unsigned int * usergenerationp)1263 int rpmxdbGetUserGeneration(rpmxdb xdb, unsigned int *usergenerationp)
1264 {
1265     if (rpmxdbLockReadHeader(xdb, 0))
1266         return RPMRC_FAIL;
1267     *usergenerationp = xdb->usergeneration;
1268     rpmxdbUnlock(xdb, 0);
1269     return RPMRC_OK;
1270 }
1271 
rpmxdbStats(rpmxdb xdb)1272 int rpmxdbStats(rpmxdb xdb)
1273 {
1274     struct xdb_slot *slot;
1275     unsigned int i, nslots;
1276 
1277     if (rpmxdbLockReadHeader(xdb, 0))
1278         return RPMRC_FAIL;
1279     nslots = xdb->nslots;
1280     printf("--- XDB Stats\n");
1281     printf("Filename: %s\n", xdb->filename);
1282     printf("Generation: %d\n", xdb->generation);
1283     printf("Slot pages: %d\n", xdb->slotnpages);
1284     printf("Blob pages: %d\n", xdb->usedblobpages);
1285     printf("Free pages: %d\n", xdb->slots[nslots].startpage - xdb->usedblobpages - xdb->slotnpages);
1286     printf("Pagesize: %d / %d\n", xdb->pagesize, xdb->systempagesize);
1287     for (i = 1, slot = xdb->slots + i; i < nslots; i++, slot++) {
1288 	if (!slot->startpage)
1289 	    continue;
1290 	printf("%2d: tag %d/%d, startpage %d, pagecnt %d%s\n", i, slot->blobtag, slot->subtag, slot->startpage, slot->pagecnt, slot->mapcallbackdata ? ", mapped" : "");
1291     }
1292 #if 0
1293     printf("Again in offset order:\n");
1294     for (i = xdb->slots[0].next; i != nslots; i = slot->next) {
1295 	slot = xdb->slots + i;
1296 	printf("%2d: tag %d/%d, startpage %d, pagecnt %d%s\n", i, slot->blobtag, slot->subtag, slot->startpage, slot->pagecnt, slot->mapcallbackdata ? ", mapped" : "");
1297     }
1298 #endif
1299 #if 0
1300     printf("Free chain:\n");
1301     for (i = xdb->firstfree; i; i = slot->next) {
1302 	slot = xdb->slots + i;
1303 	printf("%2d [%2d]: tag %d/%d, startpage %d, pagecnt %d%s\n", i, slot->slotno, slot->blobtag, slot->subtag, slot->startpage, slot->pagecnt, slot->mapcallbackdata ? ", mapped" : "");
1304     }
1305 #endif
1306     rpmxdbUnlock(xdb, 0);
1307     return RPMRC_OK;
1308 }
1309 
1310