5 #include <rpm/rpmlog.h>
23 #define RPMRC_NOTFOUND 1
26 typedef struct rpmxdb_s {
27 rpmpkgdb pkgdb; /* master database */
33 unsigned int pagesize;
34 unsigned int generation;
35 unsigned int slotnpages;
36 unsigned int usergeneration;
38 unsigned char *mapped;
39 unsigned int mappedlen;
45 unsigned char *mapped;
47 unsigned int startpage;
49 void (*mapcallback)(rpmxdb xdb, void *data, void *newaddr, size_t newsize);
50 void *mapcallbackdata;
55 unsigned int firstfree;
56 unsigned int usedblobpages;
57 unsigned int systempagesize;
62 static inline void h2le(unsigned int x, unsigned char *p)
70 /* aligned versions */
71 static inline unsigned int le2ha(unsigned char *p)
73 unsigned int x = *(unsigned int *)p;
77 static inline void h2lea(unsigned int x, unsigned char *p)
79 *(unsigned int *)p = htole32(x);
83 #define XDB_MAGIC ('R' | 'p' << 8 | 'm' << 16 | 'X' << 24)
86 #define XDB_OFFSET_MAGIC 0
87 #define XDB_OFFSET_VERSION 4
88 #define XDB_OFFSET_GENERATION 8
89 #define XDB_OFFSET_SLOTNPAGES 12
90 #define XDB_OFFSET_PAGESIZE 16
91 #define XDB_OFFSET_USERGENERATION 20
93 /* must be multiple of SLOT_SIZE */
94 #define XDB_HEADER_SIZE 32
96 #define SLOT_MAGIC ('S' | 'l' << 8 | 'o' << 16)
99 #define SLOT_START (XDB_HEADER_SIZE / SLOT_SIZE)
101 static void rpmxdbUnmap(rpmxdb xdb)
103 munmap(xdb->mapped, xdb->mappedlen);
108 /* slot mapping functions */
109 static int mapslot(rpmxdb xdb, struct xdb_slot *slot)
112 size_t off, size, shift;
116 size = slot->pagecnt * xdb->pagesize;
117 off = slot->startpage * xdb->pagesize;
119 if (xdb->pagesize != xdb->systempagesize) {
120 shift = off & (xdb->systempagesize - 1);
123 size = (size + xdb->systempagesize - 1) & ~(xdb->systempagesize - 1);
125 mapped = mmap(0, size, slot->mapflags, MAP_SHARED, xdb->fd, off);
126 if (mapped == MAP_FAILED)
128 slot->mapped = (unsigned char *)mapped + shift;
132 static void unmapslot(rpmxdb xdb, struct xdb_slot *slot)
135 unsigned char *mapped = slot->mapped;
138 size = slot->pagecnt * xdb->pagesize;
139 if (xdb->pagesize != xdb->systempagesize) {
140 size_t off = slot->startpage * xdb->pagesize;
141 size_t shift = off & (xdb->systempagesize - 1);
144 size = (size + xdb->systempagesize - 1) & ~(xdb->systempagesize - 1);
146 munmap(mapped, size);
150 static int remapslot(rpmxdb xdb, struct xdb_slot *slot, unsigned int newpagecnt)
153 size_t off, oldsize, newsize, shift;
154 oldsize = slot->pagecnt * xdb->pagesize;
155 newsize = newpagecnt * xdb->pagesize;
156 off = slot->startpage * xdb->pagesize;
158 if (xdb->pagesize != xdb->systempagesize) {
159 off = slot->startpage * xdb->pagesize;
160 shift = off & (xdb->systempagesize - 1);
163 oldsize = (oldsize + xdb->systempagesize - 1) & ~(xdb->systempagesize - 1);
165 newsize = (newsize + xdb->systempagesize - 1) & ~(xdb->systempagesize - 1);
168 mapped = mremap(slot->mapped - shift, oldsize, newsize, MREMAP_MAYMOVE);
170 mapped = mmap(0, newsize, slot->mapflags, MAP_SHARED, xdb->fd, off);
171 if (mapped == MAP_FAILED)
173 slot->mapped = (unsigned char *)mapped + shift;
174 slot->pagecnt = newpagecnt;
179 static int usedslots_cmp(const void *a, const void *b)
181 struct xdb_slot *sa = *(struct xdb_slot **)a;
182 struct xdb_slot *sb = *(struct xdb_slot **)b;
183 if (sa->startpage == sb->startpage) {
184 return sa->pagecnt > sb->pagecnt ? 1 : sa->pagecnt < sb->pagecnt ? -1 : 0;
186 return sa->startpage > sb->startpage ? 1 : -1;
189 static int rpmxdbReadHeader(rpmxdb xdb)
191 struct xdb_slot *slot;
192 unsigned int header[XDB_HEADER_SIZE / sizeof(unsigned int)];
193 unsigned int slotnpages, pagesize, generation, usergeneration, version;
194 unsigned int page, *lastfreep;
195 unsigned char *pageptr;
196 struct xdb_slot *slots, **usedslots, *lastslot;
198 unsigned int usedblobpages;
199 int i, nused, slotno;
204 if (le2ha(xdb->mapped + XDB_OFFSET_GENERATION) == xdb->generation) {
209 if (fstat(xdb->fd, &stb)) {
212 if (pread(xdb->fd, header, sizeof(header), 0) != sizeof(header)) {
215 if (le2ha((unsigned char *)header + XDB_OFFSET_MAGIC) != XDB_MAGIC)
217 version = le2ha((unsigned char *)header + XDB_OFFSET_VERSION);
218 if (version != XDB_VERSION) {
219 rpmlog(RPMLOG_ERR, _("rpmxdb: Version mismatch. Expected version: %u. "
220 "Found version: %u\n"), XDB_VERSION, version);
224 generation = le2ha((unsigned char *)header + XDB_OFFSET_GENERATION);
225 slotnpages = le2ha((unsigned char *)header + XDB_OFFSET_SLOTNPAGES);
226 pagesize = le2ha((unsigned char *)header + XDB_OFFSET_PAGESIZE);
227 usergeneration = le2ha((unsigned char *)header + XDB_OFFSET_USERGENERATION);
228 if (!slotnpages || !pagesize || stb.st_size % pagesize != 0)
230 xdb->pagesize = pagesize;
233 mapsize = slotnpages * pagesize;
234 mapsize = (mapsize + xdb->systempagesize - 1) & ~(xdb->systempagesize - 1);
235 xdb->mapped = mmap(0, mapsize, xdb->rdonly ? PROT_READ : PROT_READ | PROT_WRITE, MAP_SHARED, xdb->fd, 0);
236 if ((void *)xdb->mapped == MAP_FAILED) {
240 xdb->mappedlen = mapsize;
242 /* read in all slots */
244 nslots = slotnpages * (pagesize / SLOT_SIZE) - SLOT_START + 1;
245 slots = calloc(nslots + 1, sizeof(struct xdb_slot));
250 usedslots = calloc(nslots + 1, sizeof(int));
260 lastfreep = &xdb->firstfree;
261 for (page = 0, pageptr = xdb->mapped; page < slotnpages; page++, pageptr += pagesize) {
263 for (o = page ? 0 : SLOT_START * SLOT_SIZE; o < pagesize; o += SLOT_SIZE, slotno++, slot++) {
264 unsigned char *pp = pageptr + o;
265 slot->slotno = slotno;
266 slot->subtag = le2ha(pp);
267 if ((slot->subtag & 0x00ffffff) != SLOT_MAGIC) {
273 slot->subtag = (slot->subtag >> 24) & 255;
274 slot->blobtag = le2ha(pp + 4);
275 slot->startpage = le2ha(pp + 8);
276 slot->pagecnt = le2ha(pp + 12);
277 if (slot->pagecnt == 0 && slot->startpage) /* empty but used slot? */
278 slot->startpage = slotnpages;
279 if (!slot->startpage) {
281 lastfreep = &slot->next;
283 usedslots[nused++] = slot;
284 usedblobpages += slot->pagecnt;
289 qsort(usedslots, nused, sizeof(*usedslots), usedslots_cmp);
292 slots[0].pagecnt = slotnpages;
294 for (i = 0; i < nused; i++, lastslot = slot) {
296 if (lastslot->startpage + lastslot->pagecnt > slot->startpage) {
302 lastslot->next = slot->slotno;
303 slot->prev = lastslot->slotno;
305 lastslot->next = nslots;
306 slots[nslots].slotno = nslots;
307 slots[nslots].prev = lastslot->slotno;
308 slots[nslots].startpage = stb.st_size / pagesize;
310 /* now sync with the old slot data */
312 for (i = 1, slot = xdb->slots + i; i < xdb->nslots; i++, slot++) {
313 if (slot->startpage && (slot->mapped || slot->mapcallback)) {
314 struct xdb_slot *nslot;
315 if (i >= nslots || !slots[i].startpage || slots[i].blobtag != slot->blobtag || slots[i].subtag != slot->subtag) {
318 unmapslot(xdb, slot);
319 slot->mapcallback(xdb, slot->mapcallbackdata, 0, 0);
324 if (slot->mapcallback) {
325 nslot->mapflags = slot->mapflags;
326 nslot->mapcallback = slot->mapcallback;
327 nslot->mapcallbackdata = slot->mapcallbackdata;
329 if (slot->startpage != nslot->startpage || slot->pagecnt != nslot->pagecnt) {
330 /* slot moved or was resized */
332 unmapslot(xdb, slot);
333 if (nslot->mapcallback) {
334 if (nslot->pagecnt) {
336 nslot->mapcallback(xdb, nslot->mapcallbackdata, nslot->mapped, nslot->mapped ? nslot->pagecnt * xdb->pagesize : 0);
338 nslot->mapcallback(xdb, nslot->mapcallbackdata, 0, 0);
347 xdb->nslots = nslots;
348 xdb->generation = generation;
349 xdb->slotnpages = slotnpages;
350 xdb->usergeneration = usergeneration;
351 xdb->usedblobpages = usedblobpages;
355 static int rpmxdbWriteHeader(rpmxdb xdb)
359 h2lea(XDB_MAGIC, xdb->mapped + XDB_OFFSET_MAGIC);
360 h2lea(XDB_VERSION, xdb->mapped + XDB_OFFSET_VERSION);
361 h2lea(xdb->generation, xdb->mapped + XDB_OFFSET_GENERATION);
362 h2lea(xdb->slotnpages, xdb->mapped + XDB_OFFSET_SLOTNPAGES);
363 h2lea(xdb->pagesize, xdb->mapped + XDB_OFFSET_PAGESIZE);
364 h2lea(xdb->usergeneration, xdb->mapped + XDB_OFFSET_USERGENERATION);
368 static void rpmxdbUpdateSlot(rpmxdb xdb, struct xdb_slot *slot)
370 unsigned char *pp = xdb->mapped + (SLOT_START - 1 + slot->slotno) * SLOT_SIZE;
371 h2lea(SLOT_MAGIC | (slot->subtag << 24), pp);
372 h2lea(slot->blobtag, pp + 4);
373 if (slot->pagecnt || !slot->startpage)
374 h2lea(slot->startpage, pp + 8);
376 h2lea(1, pp + 8); /* "empty but used" blobs always start at 1 */
377 h2lea(slot->pagecnt, pp + 12);
379 h2lea(xdb->generation, xdb->mapped + XDB_OFFSET_GENERATION);
382 static int rpmxdbWriteEmptyPages(rpmxdb xdb, unsigned int pageno, unsigned int count)
387 page = malloc(xdb->pagesize);
390 memset(page, 0, xdb->pagesize);
391 for (; count; count--, pageno++) {
392 if (pwrite(xdb->fd, page, xdb->pagesize, pageno * xdb->pagesize) != xdb->pagesize) {
401 static int rpmxdbWriteEmptySlotpage(rpmxdb xdb, int pageno)
405 page = malloc(xdb->pagesize);
408 memset(page, 0, xdb->pagesize);
409 spp = xdb->pagesize / SLOT_SIZE; /* slots per page */
410 for (i = pageno ? 0 : SLOT_START; i < spp; i++)
411 h2le(SLOT_MAGIC, page + i * SLOT_SIZE);
413 /* only used when called from InitInternal */
419 rpmxdbWriteHeader(xdb);
422 if (pwrite(xdb->fd, page, xdb->pagesize, pageno * xdb->pagesize) != xdb->pagesize) {
430 static int rpmxdbInitInternal(rpmxdb xdb)
433 if (fstat(xdb->fd, &stb)) {
436 if (stb.st_size == 0) {
439 xdb->pagesize = sysconf(_SC_PAGE_SIZE);
440 if (rpmxdbWriteEmptySlotpage(xdb, 0)) {
447 /* we use the master pdb for locking */
448 static int rpmxdbLockOnly(rpmxdb xdb, int excl)
450 if (excl && xdb->rdonly)
452 return rpmpkgLock(xdb->pkgdb, excl);
455 /* this is the same as rpmxdbLockReadHeader. It does the
456 * ReadHeader to sync the mappings if xdb moved some blobs.
458 int rpmxdbLock(rpmxdb xdb, int excl)
460 if (rpmxdbLockOnly(xdb, excl))
462 if (rpmxdbReadHeader(xdb)) {
463 rpmxdbUnlock(xdb, excl);
469 int rpmxdbUnlock(rpmxdb xdb, int excl)
471 return rpmpkgUnlock(xdb->pkgdb, excl);
474 static int rpmxdbLockReadHeader(rpmxdb xdb, int excl)
476 if (rpmxdbLockOnly(xdb, excl))
478 if (rpmxdbReadHeader(xdb)) {
479 rpmxdbUnlock(xdb, excl);
485 static int rpmxdbInit(rpmxdb xdb)
489 if (rpmxdbLockOnly(xdb, 1))
491 rc = rpmxdbInitInternal(xdb);
492 rpmxdbUnlock(xdb, 1);
496 int rpmxdbOpen(rpmxdb *xdbp, rpmpkgdb pkgdb, const char *filename, int flags, int mode)
502 xdb = calloc(1, sizeof(*xdb));
504 xdb->filename = strdup(filename);
505 xdb->systempagesize = sysconf(_SC_PAGE_SIZE);
506 if (!xdb->filename) {
510 if ((flags & (O_RDONLY|O_RDWR)) == O_RDONLY)
512 if ((xdb->fd = open(filename, flags, mode)) == -1) {
517 if (flags & O_CREAT) {
521 if ((filenameCopy = strdup(xdb->filename)) == NULL) {
528 if ((pdir = opendir(dirname(filenameCopy))) == NULL) {
536 if (fsync(dirfd(pdir)) == -1) {
547 if (fstat(xdb->fd, &stb)) {
553 if (stb.st_size == 0) {
554 if (rpmxdbInit(xdb)) {
568 void rpmxdbClose(rpmxdb xdb)
570 struct xdb_slot *slot;
573 for (i = 1, slot = xdb->slots + 1; i < xdb->nslots; i++, slot++) {
575 unmapslot(xdb, slot);
576 slot->mapcallback(xdb, slot->mapcallbackdata, 0, 0);
588 /* moves the blob to a given new location (possibly resizeing) */
589 static int moveblobto(rpmxdb xdb, struct xdb_slot *oldslot, struct xdb_slot *afterslot, unsigned int newpagecnt)
591 struct xdb_slot *nextslot;
592 unsigned int newstartpage, oldpagecnt;
596 newstartpage = afterslot->startpage + afterslot->pagecnt;
597 nextslot = xdb->slots + afterslot->next;
599 /* make sure there's enough room */
600 if (newpagecnt > nextslot->startpage - newstartpage)
604 printf("moveblobto %d %d %d %d, afterslot %d\n", oldslot->startpage, oldslot->pagecnt, newstartpage, newpagecnt, afterslot->slotno);
606 /* map old content */
608 oldpagecnt = oldslot->pagecnt;
609 if (!oldslot->mapped && oldpagecnt) {
610 if (mapslot(xdb, oldslot))
616 tocopy = newpagecnt > oldpagecnt ? oldpagecnt : newpagecnt;
617 if (tocopy && pwrite(xdb->fd, oldslot->mapped, tocopy * xdb->pagesize, newstartpage * xdb->pagesize) != tocopy * xdb->pagesize) {
619 unmapslot(xdb, oldslot);
622 /* zero out new pages */
623 if (newpagecnt > oldpagecnt) {
624 if (rpmxdbWriteEmptyPages(xdb, newstartpage + oldpagecnt, newpagecnt - oldpagecnt)) {
626 unmapslot(xdb, oldslot);
632 unmapslot(xdb, oldslot);
634 /* set new offset and position */
635 oldslot->startpage = newstartpage;
636 oldslot->pagecnt = newpagecnt;
637 rpmxdbUpdateSlot(xdb, oldslot);
638 xdb->usedblobpages -= oldpagecnt;
639 xdb->usedblobpages += newpagecnt;
641 if (afterslot != oldslot && nextslot != oldslot) {
642 /* remove from old chain */
643 xdb->slots[oldslot->prev].next = oldslot->next;
644 xdb->slots[oldslot->next].prev = oldslot->prev;
646 /* chain into new position, between lastslot and nextslot */
647 oldslot->prev = afterslot->slotno;
648 afterslot->next = oldslot->slotno;
650 oldslot->next = nextslot->slotno;
651 nextslot->prev = oldslot->slotno;
654 /* map again (if needed) */
655 if (oldslot->mapcallback) {
657 if (mapslot(xdb, oldslot))
658 oldslot->mapped = 0; /* XXX: HELP, what can we do here? */
660 oldslot->mapcallback(xdb, oldslot->mapcallbackdata, oldslot->mapped, oldslot->mapped ? oldslot->pagecnt * xdb->pagesize : 0);
665 /* moves the blob to a new location (possibly resizeing) */
666 static int moveblob(rpmxdb xdb, struct xdb_slot *oldslot, unsigned int newpagecnt)
668 struct xdb_slot *slot, *lastslot;
670 unsigned int freecnt;
673 nslots = xdb->nslots;
675 lastslot = xdb->slots;
676 for (i = xdb->slots[0].next; ; lastslot = slot, i = slot->next) {
677 slot = xdb->slots + i;
678 freecnt = slot->startpage - (lastslot->startpage + lastslot->pagecnt);
679 if (freecnt >= newpagecnt)
684 if (i == nslots && newpagecnt > freecnt) {
685 /* need to grow the file */
686 if (rpmxdbWriteEmptyPages(xdb, slot->startpage, newpagecnt - freecnt)) {
689 slot->startpage += newpagecnt - freecnt;
691 return moveblobto(xdb, oldslot, lastslot, newpagecnt);
694 /* move the two blobs at the end of our file to the free area after the provided slot */
695 static int moveblobstofront(rpmxdb xdb, struct xdb_slot *afterslot)
697 struct xdb_slot *slot1, *slot2;
698 unsigned int freestart = afterslot->startpage + afterslot->pagecnt;
699 unsigned int freecount = xdb->slots[afterslot->next].startpage - freestart;
701 slot1 = xdb->slots + xdb->slots[xdb->nslots].prev;
702 if (slot1 == xdb->slots)
705 slot2 = xdb->slots + slot1->prev;
706 if (slot2 == xdb->slots)
709 if (slot1->pagecnt < slot2->pagecnt) {
710 struct xdb_slot *tmp = slot1;
714 if (slot1 && slot1->pagecnt && slot1->pagecnt <= freecount && slot1->startpage > freestart) {
715 if (moveblobto(xdb, slot1, afterslot, slot1->pagecnt))
717 freestart += slot1->pagecnt;
718 freecount -= slot1->pagecnt;
721 if (slot2 && slot2->pagecnt && slot2->pagecnt <= freecount && slot2->startpage > freestart) {
722 if (moveblobto(xdb, slot2, afterslot, slot2->pagecnt))
728 /* add a single page containing empty slots */
729 static int addslotpage(rpmxdb xdb)
731 unsigned char *newaddr;
732 struct xdb_slot *slot;
739 /* move first blob if needed */
740 nslots = xdb->nslots;
741 for (i = xdb->slots[0].next; i != nslots; i = slot->next) {
742 slot = xdb->slots + i;
746 if (i != nslots && slot->pagecnt && slot->startpage == xdb->slotnpages) {
747 /* the blob at this slot is in the way. move it. */
748 if (moveblob(xdb, slot, slot->pagecnt))
752 spp = xdb->pagesize / SLOT_SIZE; /* slots per page */
753 slot = realloc(xdb->slots, (nslots + 1 + spp) * sizeof(*slot));
758 if (rpmxdbWriteEmptySlotpage(xdb, xdb->slotnpages)) {
762 newmappedlen = xdb->slotnpages * xdb->pagesize + xdb->pagesize;
763 newmappedlen = (newmappedlen + xdb->systempagesize - 1) & ~(xdb->systempagesize - 1);
764 newaddr = mremap(xdb->mapped, xdb->mappedlen, newmappedlen, MREMAP_MAYMOVE);
765 if (newaddr == MAP_FAILED)
767 xdb->mapped = newaddr;
768 xdb->mappedlen = newmappedlen;
770 /* update the header */
773 rpmxdbWriteHeader(xdb);
775 /* fixup empty but used slots */
776 for (i = xdb->slots[0].next; i != nslots; i = slot->next) {
777 slot = xdb->slots + i;
778 if (slot->startpage >= xdb->slotnpages)
780 slot->startpage = xdb->slotnpages;
785 /* move tail element to the new end */
786 slot = xdb->slots + nslots + spp;
787 *slot = xdb->slots[nslots];
788 slot->slotno = nslots + spp;
789 xdb->slots[slot->prev].next = slot->slotno;
792 /* add new free slots to the firstfree chain */
793 memset(xdb->slots + nslots, 0, sizeof(*slot) * spp);
794 for (i = 0; i < spp - 1; i++) {
795 xdb->slots[nslots + i].slotno = nslots + i;
796 xdb->slots[nslots + i].next = i + 1;
798 xdb->slots[nslots + i].slotno = nslots + i;
799 xdb->firstfree = nslots;
803 static int createblob(rpmxdb xdb, unsigned int *idp, unsigned int blobtag, unsigned int subtag)
805 struct xdb_slot *slot;
810 if (!xdb->firstfree) {
811 if (addslotpage(xdb))
815 slot = xdb->slots + xdb->firstfree;
816 xdb->firstfree = slot->next;
819 slot->blobtag = blobtag;
820 slot->subtag = subtag;
821 slot->startpage = xdb->slotnpages;
823 rpmxdbUpdateSlot(xdb, slot);
826 slot->next = xdb->slots[0].next;
827 xdb->slots[slot->next].prev = id;
828 xdb->slots[0].next = id;
830 printf("createblob #%d %d/%d\n", id, blobtag, subtag);
832 if (slot->slotno != id)
840 int rpmxdbLookupBlob(rpmxdb xdb, unsigned int *idp, unsigned int blobtag, unsigned int subtag, int flags)
842 struct xdb_slot *slot;
843 unsigned int i, nslots;
844 if (rpmxdbLockReadHeader(xdb, flags ? 1 : 0))
846 nslots = xdb->nslots;
848 for (i = xdb->slots[0].next; i != nslots; i = slot->next) {
849 slot = xdb->slots + i;
850 if (slot->blobtag == blobtag && slot->subtag == subtag)
855 if (i && (flags & O_TRUNC) != 0) {
856 if (rpmxdbResizeBlob(xdb, i, 0)) {
857 rpmxdbUnlock(xdb, flags ? 1 : 0);
861 if (!i && (flags & O_CREAT) != 0) {
862 if (createblob(xdb, &i, blobtag, subtag)) {
863 rpmxdbUnlock(xdb, flags ? 1 : 0);
868 rpmxdbUnlock(xdb, flags ? 1 : 0);
869 return i ? RPMRC_OK : RPMRC_NOTFOUND;
872 int rpmxdbDelBlob(rpmxdb xdb, unsigned int id)
874 struct xdb_slot *slot;
877 if (rpmxdbLockReadHeader(xdb, 1))
879 if (id >= xdb->nslots) {
880 rpmxdbUnlock(xdb, 1);
883 slot = xdb->slots + id;
884 if (!slot->startpage) {
885 rpmxdbUnlock(xdb, 1);
889 unmapslot(xdb, slot);
890 slot->mapcallback(xdb, slot->mapcallbackdata, 0, 0);
892 /* remove from old chain */
893 xdb->slots[slot->prev].next = slot->next;
894 xdb->slots[slot->next].prev = slot->prev;
895 xdb->usedblobpages -= slot->pagecnt;
897 if (xdb->usedblobpages * 2 < xdb->slots[xdb->nslots].startpage && (slot->startpage + slot->pagecnt) * 2 < xdb->slots[xdb->nslots].startpage) {
898 /* freed in first half of pages, move last two blobs if we can */
899 moveblobstofront(xdb, xdb->slots + slot->prev);
903 memset(slot, 0, sizeof(*slot));
905 rpmxdbUpdateSlot(xdb, slot);
907 /* enqueue into free chain */
908 slot->next = xdb->firstfree;
909 xdb->firstfree = slot->slotno;
911 /* check if we should truncate the file */
912 slot = xdb->slots + xdb->slots[xdb->nslots].prev;
913 if (slot->startpage + slot->pagecnt < xdb->slots[xdb->nslots].startpage / 4 * 3) {
914 unsigned int newend = slot->startpage + slot->pagecnt;
915 if (!ftruncate(xdb->fd, newend * xdb->pagesize))
916 xdb->slots[xdb->nslots].startpage = newend;
919 rpmxdbUnlock(xdb, 1);
923 int rpmxdbResizeBlob(rpmxdb xdb, unsigned int id, size_t newsize)
925 struct xdb_slot *slot;
926 unsigned int oldpagecnt, newpagecnt;
929 if (rpmxdbLockReadHeader(xdb, 1))
931 if (id >= xdb->nslots) {
932 rpmxdbUnlock(xdb, 1);
935 slot = xdb->slots + id;
936 if (!slot->startpage) {
937 rpmxdbUnlock(xdb, 1);
940 oldpagecnt = slot->pagecnt;
941 newpagecnt = (newsize + xdb->pagesize - 1) / xdb->pagesize;
942 if (oldpagecnt && newpagecnt && newpagecnt <= oldpagecnt) {
943 /* reducing size. zero to end of page */
944 unsigned int pg = newsize & (xdb->pagesize - 1);
947 memset(slot->mapped + pg, 0, xdb->pagesize - pg);
949 char *empty = calloc(1, xdb->pagesize - pg);
951 rpmxdbUnlock(xdb, 1);
954 if (pwrite(xdb->fd, empty, xdb->pagesize - pg, (slot->startpage + newpagecnt - 1) * xdb->pagesize + pg ) != xdb->pagesize - pg) {
956 rpmxdbUnlock(xdb, 1);
963 if (newpagecnt == oldpagecnt) {
965 rpmxdbUnlock(xdb, 1);
969 /* special case: zero size blob, no longer mapped */
971 unmapslot(xdb, slot);
973 slot->startpage = xdb->slotnpages;
974 /* remove from old chain */
975 xdb->slots[slot->prev].next = slot->next;
976 xdb->slots[slot->next].prev = slot->prev;
977 /* enqueue into head */
979 slot->next = xdb->slots[0].next;
980 xdb->slots[slot->next].prev = slot->slotno;
981 xdb->slots[0].next = slot->slotno;
982 rpmxdbUpdateSlot(xdb, slot);
983 xdb->usedblobpages -= oldpagecnt;
984 if (slot->mapcallback)
985 slot->mapcallback(xdb, slot->mapcallbackdata, 0, 0);
986 } else if (newpagecnt <= xdb->slots[slot->next].startpage - slot->startpage) {
987 /* can do it inplace */
988 if (newpagecnt > oldpagecnt) {
990 if (rpmxdbWriteEmptyPages(xdb, slot->startpage + oldpagecnt, newpagecnt - oldpagecnt)) {
991 rpmxdbUnlock(xdb, 1);
995 if (slot->mapcallback) {
996 if (remapslot(xdb, slot, newpagecnt)) {
997 rpmxdbUnlock(xdb, 1);
1002 unmapslot(xdb, slot);
1003 slot->pagecnt = newpagecnt;
1005 rpmxdbUpdateSlot(xdb, slot);
1006 xdb->usedblobpages -= oldpagecnt;
1007 xdb->usedblobpages += newpagecnt;
1008 if (slot->mapcallback)
1009 slot->mapcallback(xdb, slot->mapcallbackdata, slot->mapped, slot->pagecnt * xdb->pagesize);
1011 /* need to relocate to a new page area */
1012 if (moveblob(xdb, slot, newpagecnt)) {
1013 rpmxdbUnlock(xdb, 1);
1017 rpmxdbUnlock(xdb, 1);
1021 int rpmxdbMapBlob(rpmxdb xdb, unsigned int id, int flags, void (*mapcallback)(rpmxdb xdb, void *data, void *newaddr, size_t newsize), void *mapcallbackdata)
1023 struct xdb_slot *slot;
1024 if (!id || !mapcallback)
1026 if ((flags & (O_RDONLY|O_RDWR)) == O_RDWR && xdb->rdonly)
1028 if (rpmxdbLockReadHeader(xdb, 0))
1030 if (id >= xdb->nslots) {
1031 rpmxdbUnlock(xdb, 0);
1034 slot = xdb->slots + id;
1035 if (!slot->startpage || slot->mapped) {
1036 rpmxdbUnlock(xdb, 0);
1039 slot->mapflags = (flags & (O_RDONLY|O_RDWR)) == O_RDWR ? PROT_READ | PROT_WRITE : PROT_READ;
1040 if (slot->pagecnt) {
1041 if (mapslot(xdb, slot)) {
1043 rpmxdbUnlock(xdb, 0);
1047 slot->mapcallback = mapcallback;
1048 slot->mapcallbackdata = mapcallbackdata;
1049 mapcallback(xdb, mapcallbackdata, slot->mapped, slot->mapped ? slot->pagecnt * xdb->pagesize : 0);
1050 rpmxdbUnlock(xdb, 0);
1054 int rpmxdbUnmapBlob(rpmxdb xdb, unsigned int id)
1056 struct xdb_slot *slot;
1059 if (rpmxdbLockReadHeader(xdb, 0))
1061 if (id >= xdb->nslots) {
1062 rpmxdbUnlock(xdb, 0);
1065 slot = xdb->slots + id;
1067 unmapslot(xdb, slot);
1068 slot->mapcallback(xdb, slot->mapcallbackdata, 0, 0);
1070 slot->mapcallback = 0;
1071 slot->mapcallbackdata = 0;
1073 rpmxdbUnlock(xdb, 0);
1077 int rpmxdbRenameBlob(rpmxdb xdb, unsigned int *idp, unsigned int blobtag, unsigned int subtag)
1079 struct xdb_slot *slot;
1080 unsigned int otherid;
1081 unsigned int id = *idp;
1084 if (!id || subtag > 255)
1086 if (rpmxdbLockReadHeader(xdb, 1))
1088 if (id >= xdb->nslots) {
1089 rpmxdbUnlock(xdb, 1);
1092 slot = xdb->slots + id;
1094 printf("rpmxdbRenameBlob #%d %d/%d -> %d/%d\n", id, slot->blobtag, slot->subtag, blobtag, subtag);
1096 if (!slot->startpage) {
1097 rpmxdbUnlock(xdb, 1);
1100 if (slot->blobtag == blobtag && slot->subtag == subtag) {
1101 rpmxdbUnlock(xdb, 1);
1104 rc = rpmxdbLookupBlob(xdb, &otherid, blobtag, subtag, 0);
1105 if (rc == RPMRC_NOTFOUND)
1108 rpmxdbUnlock(xdb, 1);
1113 printf("(replacing #%d)\n", otherid);
1115 if (rpmxdbDelBlob(xdb, otherid)) {
1116 rpmxdbUnlock(xdb, 1);
1119 /* get otherid back from free chain */
1120 if (xdb->firstfree != otherid)
1122 xdb->firstfree = xdb->slots[otherid].next;
1124 slot->blobtag = blobtag;
1125 slot->subtag = subtag;
1126 xdb->slots[otherid] = *slot;
1128 xdb->slots[otherid].slotno = otherid;
1129 xdb->slots[slot->prev].next = otherid;
1130 xdb->slots[slot->next].prev = otherid;
1132 rpmxdbUpdateSlot(xdb, xdb->slots + otherid);
1133 memset(slot, 0, sizeof(*slot));
1135 rpmxdbUpdateSlot(xdb, slot);
1136 slot->next = xdb->firstfree;
1137 xdb->firstfree = slot->slotno;
1140 slot = xdb->slots + id;
1141 slot->blobtag = blobtag;
1142 slot->subtag = subtag;
1143 rpmxdbUpdateSlot(xdb, slot);
1145 rpmxdbUnlock(xdb, 1);
1149 void rpmxdbSetFsync(rpmxdb xdb, int dofsync)
1151 xdb->dofsync = dofsync;
1154 int rpmxdbIsRdonly(rpmxdb xdb)
1159 int rpmxdbSetUserGeneration(rpmxdb xdb, unsigned int usergeneration)
1161 if (rpmxdbLockReadHeader(xdb, 1))
1163 /* sync before the update */
1164 if (xdb->dofsync && fsync(xdb->fd)) {
1165 rpmxdbUnlock(xdb, 1);
1168 xdb->usergeneration = usergeneration;
1170 rpmxdbWriteHeader(xdb);
1171 rpmxdbUnlock(xdb, 1);
1175 int rpmxdbGetUserGeneration(rpmxdb xdb, unsigned int *usergenerationp)
1177 if (rpmxdbLockReadHeader(xdb, 0))
1179 *usergenerationp = xdb->usergeneration;
1180 rpmxdbUnlock(xdb, 0);
1184 int rpmxdbStats(rpmxdb xdb)
1186 struct xdb_slot *slot;
1187 unsigned int i, nslots;
1189 if (rpmxdbLockReadHeader(xdb, 0))
1191 nslots = xdb->nslots;
1192 printf("--- XDB Stats\n");
1193 printf("Filename: %s\n", xdb->filename);
1194 printf("Generation: %d\n", xdb->generation);
1195 printf("Slot pages: %d\n", xdb->slotnpages);
1196 printf("Blob pages: %d\n", xdb->usedblobpages);
1197 printf("Free pages: %d\n", xdb->slots[nslots].startpage - xdb->usedblobpages - xdb->slotnpages);
1198 printf("Pagesize: %d / %d\n", xdb->pagesize, xdb->systempagesize);
1199 for (i = 1, slot = xdb->slots + i; i < nslots; i++, slot++) {
1200 if (!slot->startpage)
1202 printf("%2d: tag %d/%d, startpage %d, pagecnt %d%s\n", i, slot->blobtag, slot->subtag, slot->startpage, slot->pagecnt, slot->mapcallbackdata ? ", mapped" : "");
1205 printf("Again in offset order:\n");
1206 for (i = xdb->slots[0].next; i != nslots; i = slot->next) {
1207 slot = xdb->slots + i;
1208 printf("%2d: tag %d/%d, startpage %d, pagecnt %d%s\n", i, slot->blobtag, slot->subtag, slot->startpage, slot->pagecnt, slot->mapcallbackdata ? ", mapped" : "");
1212 printf("Free chain:\n");
1213 for (i = xdb->firstfree; i; i = slot->next) {
1214 slot = xdb->slots + i;
1215 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" : "");
1218 rpmxdbUnlock(xdb, 0);