rpm/backport-ndb-add-a-rpmpkgSalvage-method.patch
2021-01-12 20:52:15 +08:00

220 lines
6.5 KiB
Diff

From 6489957449fec63ddf330330e9435b4ee0c388b0 Mon Sep 17 00:00:00 2001
From: Michael Schroeder <mls@suse.de>
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