From 6489957449fec63ddf330330e9435b4ee0c388b0 Mon Sep 17 00:00:00 2001 From: Michael Schroeder Date: Mon, 3 Feb 2020 14:18:23 +0100 Subject: [PATCH] ndb: add a rpmpkgSalvage() method This can be used to recover as much data as possibly from a terminally broken database. It works by scanning the database file for entries that are not corrupt. URL:https://github.com/rpm-software-management/rpm/commit/6489957449fec63ddf330330e9435b4ee0c388b0 --- lib/backend/ndb/rpmpkg.c | 157 +++++++++++++++++++++++++++++++++++++++++++++-- lib/backend/ndb/rpmpkg.h | 1 + 2 files changed, 153 insertions(+), 5 deletions(-) diff --git a/lib/backend/ndb/rpmpkg.c b/lib/backend/ndb/rpmpkg.c index 922ae11..b9cd67c 100644 --- a/lib/backend/ndb/rpmpkg.c +++ b/lib/backend/ndb/rpmpkg.c @@ -30,8 +30,6 @@ typedef struct pkgslot_s { typedef struct rpmpkgdb_s { int fd; /* our file descriptor */ - int flags; - int mode; int rdonly; @@ -885,8 +883,6 @@ int rpmpkgOpen(rpmpkgdb *pkgdbp, const char *filename, int flags, int mode) return RPMRC_FAIL; } } - pkgdb->flags = flags; - pkgdb->mode = mode; pkgdb->dofsync = 1; *pkgdbp = pkgdb; return RPMRC_OK; @@ -908,6 +904,157 @@ void rpmpkgClose(rpmpkgdb pkgdb) free(pkgdb); } + +static unsigned int salvage_latest_blob(unsigned int *salvaged, unsigned int cnt) +{ + unsigned int i, max = 0, maxi = 0; + for (i = 0; i < cnt - 1; i++) { + if (salvaged[i * 4 + 7] - salvaged[i * 4 + 3] > max) { + max = salvaged[i * 4 + 7] - salvaged[i * 4 + 3]; + maxi = i; + } + } + if ((unsigned int)(salvaged[3] - salvaged[i * 4 + 3]) > max) + maxi = cnt - 1; + return maxi; +} + +static int salvage_cmp(const void *av, const void *bv) +{ + const unsigned int *a = av, *b = bv; + if (a[0] != b[0]) + return a[0] > b[0] ? 1 : -1; + if (a[3] != b[3]) + return a[3] > b[3] ? 1 : -1; + if (a[1] != b[1]) + return a[1] > b[1] ? 1 : -1; + return 0; +} + +#define SALVAGE_PAGESIZE 4096 +#define SALVAGE_BLKCHUNK (SALVAGE_PAGESIZE / BLK_SIZE) + +int rpmpkgSalvage(rpmpkgdb *pkgdbp, const char *filename) +{ + struct stat stb; + rpmpkgdb pkgdb; + unsigned int blk, iblk, blkskip; + unsigned int blkoff, blkcnt, pkgidx, generation, bloblen; + unsigned int nfound, nslots, i, j; + unsigned char page[SALVAGE_PAGESIZE]; + unsigned int *salvaged; + + *pkgdbp = 0; + pkgdb = xcalloc(1, sizeof(*pkgdb)); + pkgdb->filename = xstrdup(filename); + pkgdb->rdonly = 1; + if ((pkgdb->fd = open(filename, O_RDONLY)) == -1) { + rpmpkgClose(pkgdb); + return RPMRC_FAIL; + } + if (rpmpkgGetLock(pkgdb, LOCK_SH)) { + rpmpkgClose(pkgdb); + return RPMRC_FAIL; + } + pkgdb->locked_shared++; + if (fstat(pkgdb->fd, &stb)) { + rpmpkgClose(pkgdb); + return RPMRC_FAIL; + } + if (stb.st_size < BLK_SIZE) { + rpmpkgClose(pkgdb); + return RPMRC_FAIL; + } + pkgdb->fileblks = stb.st_size / BLK_SIZE; + blkskip = 1; + nfound = 0; + salvaged = xmalloc(64 * (4 * sizeof(unsigned int))); + for (blk = 0; blk < pkgdb->fileblks; blk += SALVAGE_BLKCHUNK) { + unsigned int size; + unsigned char *bp = page; + if (pkgdb->fileblks - blk > SALVAGE_BLKCHUNK) + size = SALVAGE_PAGESIZE; + else + size = (pkgdb->fileblks - blk) * BLK_SIZE; + if (pread(pkgdb->fd, page, size, (off_t)blk * BLK_SIZE) != size) + continue; + if (size != SALVAGE_PAGESIZE) + memset(page + size, 0, SALVAGE_PAGESIZE - size); + if (blkskip) { + memset(page, 0, blkskip * BLK_SIZE); + blkskip = 0; + } + for (iblk = 0; iblk < SALVAGE_BLKCHUNK; iblk++, bp += BLK_SIZE) { + if (le2h(bp) != BLOBHEAD_MAGIC) + continue; + pkgidx = le2h(bp + 4); + if (!pkgidx) + continue; + generation = le2h(bp + 8); + bloblen = le2h(bp + 12); + blkoff = blk + iblk; + blkcnt = (BLOBHEAD_SIZE + bloblen + BLOBTAIL_SIZE + BLK_SIZE - 1) / BLK_SIZE; + if (blkoff + blkcnt > pkgdb->fileblks) + continue; + if (rpmpkgVerifyblob(pkgdb, pkgidx, blkoff, blkcnt)) + continue; + /* found a valid blob, add to salvaged list */ + if (nfound && (nfound & 63) == 0) + salvaged = xrealloc(salvaged, (nfound + 64) * (4 * sizeof(unsigned int))); + salvaged[nfound * 4 + 0] = pkgidx; + salvaged[nfound * 4 + 1] = blkoff; + salvaged[nfound * 4 + 2] = blkcnt; + salvaged[nfound * 4 + 3] = generation; + nfound++; + /* advance to after the blob! */ + blkoff += blkcnt; + if (blkoff >= blk + SALVAGE_BLKCHUNK) { + blkskip = blkoff % SALVAGE_BLKCHUNK; + blk = blkoff - blkskip - SALVAGE_BLKCHUNK; + break; + } else { + iblk = blkoff - blk - 1; + bp = page + iblk * BLK_SIZE; + } + } + } + /* now strip duplicate entries */ + nslots = 0; + if (nfound > 1) { + qsort(salvaged, nfound, sizeof(unsigned int) * 4, salvage_cmp); + for (i = 0; i < nfound; i++) { + pkgidx = salvaged[i * 4]; + for (j = i + 1; j < nfound; j++) + if (salvaged[j * 4] != pkgidx) + break; + if (j != i + 1) + i += salvage_latest_blob(salvaged + i * 4, j - i); + if (i != nslots) + memcpy(salvaged + nslots * 4, salvaged + i * 4, sizeof(unsigned int) * 4); + nslots++; + i = j - 1; + } + } + pkgdb->slots = xcalloc(nslots + 1, sizeof(*pkgdb->slots)); + for (i = 0; i < nslots; i++) { + pkgdb->slots[i].pkgidx = salvaged[i * 4 + 0]; + pkgdb->slots[i].blkoff = salvaged[i * 4 + 1]; + pkgdb->slots[i].blkcnt = salvaged[i * 4 + 2]; + pkgdb->slots[i].slotno = 0; + } + free(salvaged); + pkgdb->header_ok = 1; + pkgdb->nslots = nslots; + pkgdb->ordered = 0; + rpmpkgOrderSlots(pkgdb); + if (rpmpkgHashSlots(pkgdb)) { + rpmpkgClose(pkgdb); + return RPMRC_FAIL; + } + *pkgdbp = pkgdb; + return RPMRC_OK; +} + void rpmpkgSetFsync(rpmpkgdb pkgdb, int dofsync) { pkgdb->dofsync = dofsync; @@ -1184,7 +1331,7 @@ int rpmpkgVerify(rpmpkgdb pkgdb) int rpmpkgNextPkgIdx(rpmpkgdb pkgdb, unsigned int *pkgidxp) { - if (rpmpkgLockReadHeader(pkgdb, 1)) + if (rpmpkgLockReadHeader(pkgdb, 1) || !pkgdb->nextpkgidx) return RPMRC_FAIL; *pkgidxp = pkgdb->nextpkgidx++; if (rpmpkgWriteHeader(pkgdb)) { diff --git a/lib/backend/ndb/rpmpkg.h b/lib/backend/ndb/rpmpkg.h index 72d05c4..6466818 100644 --- a/lib/backend/ndb/rpmpkg.h +++ b/lib/backend/ndb/rpmpkg.h @@ -2,6 +2,7 @@ struct rpmpkgdb_s; typedef struct rpmpkgdb_s *rpmpkgdb; int rpmpkgOpen(rpmpkgdb *pkgdbp, const char *filename, int flags, int mode); +int rpmpkgSalvage(rpmpkgdb *pkgdbp, const char *filename); void rpmpkgClose(rpmpkgdb pkgdbp); void rpmpkgSetFsync(rpmpkgdb pkgdbp, int dofsync); -- 1.8.3.1