From 49d43a334572199088ecc24ce6fc0e86c8699d2e Mon Sep 17 00:00:00 2001
From: Michael Schroeder <mls@suse.de>
Date: Fri, 10 Jan 2020 15:49:16 +0100
Subject: [PATCH] ndb: add a rpmxdbDelAllBlobs method

This will delete all blobs (i.e. index databases) from the
ndb master index database. We do this kind of low-level on purpose,
so that it even works if the index database is corrupt.

This will be used in the next commit which implements automatic
index regeneration if the index is out of sync.

URL:https://github.com/rpm-software-management/rpm/commit/49d43a334572199088ecc24ce6fc0e86c8699d2e
---
 lib/backend/ndb/rpmxdb.c | 81 +++++++++++++++++++++++++++++++++++++-----------
 lib/backend/ndb/rpmxdb.h |  1 +
 2 files changed, 64 insertions(+), 18 deletions(-)

diff --git a/lib/backend/ndb/rpmxdb.c b/lib/backend/ndb/rpmxdb.c
index 2f94491..3baafa6 100644
--- a/lib/backend/ndb/rpmxdb.c
+++ b/lib/backend/ndb/rpmxdb.c
@@ -224,11 +224,33 @@ static int usedslots_cmp(const void *a, const void *b)
     return sa->startpage > sb->startpage ? 1 : -1;
 }
 
+static int rpmxdbReadHeaderRaw(rpmxdb xdb, unsigned int *generationp, unsigned int *slotnpagesp, unsigned int *pagesizep, unsigned int *usergenerationp)
+{
+    unsigned int header[XDB_HEADER_SIZE / sizeof(unsigned int)];
+    unsigned int version;
+    if (pread(xdb->fd, header, sizeof(header), 0) != sizeof(header))
+	return RPMRC_FAIL;
+    if (le2ha((unsigned char *)header + XDB_OFFSET_MAGIC) != XDB_MAGIC)
+	return RPMRC_FAIL;
+    version = le2ha((unsigned char *)header + XDB_OFFSET_VERSION);
+    if (version != XDB_VERSION) {
+	rpmlog(RPMLOG_ERR, _("rpmxdb: Version mismatch. Expected version: %u. "
+	    "Found version: %u\n"), XDB_VERSION, version);
+	return RPMRC_FAIL;
+    }
+    *generationp = le2ha((unsigned char *)header + XDB_OFFSET_GENERATION);
+    *slotnpagesp = le2ha((unsigned char *)header + XDB_OFFSET_SLOTNPAGES);
+    *pagesizep = le2ha((unsigned char *)header + XDB_OFFSET_PAGESIZE);
+    *usergenerationp = le2ha((unsigned char *)header + XDB_OFFSET_USERGENERATION);
+    if (!*slotnpagesp || !*pagesizep)
+	return RPMRC_FAIL;
+    return RPMRC_OK;
+}
+
 static int rpmxdbReadHeader(rpmxdb xdb)
 {
     struct xdb_slot *slot;
-    unsigned int header[XDB_HEADER_SIZE / sizeof(unsigned int)];
-    unsigned int slotnpages, pagesize, generation, usergeneration, version;
+    unsigned int slotnpages, pagesize, generation, usergeneration;
     unsigned int page, *lastfreep;
     unsigned char *pageptr;
     struct xdb_slot *slots, **usedslots, *lastslot;
@@ -246,23 +268,9 @@ static int rpmxdbReadHeader(rpmxdb xdb)
     if (fstat(xdb->fd, &stb)) {
         return RPMRC_FAIL;
     }
-    if (pread(xdb->fd, header, sizeof(header), 0) != sizeof(header)) {
-	return RPMRC_FAIL;
-    }
-    if (le2ha((unsigned char *)header + XDB_OFFSET_MAGIC) != XDB_MAGIC)
+    if (rpmxdbReadHeaderRaw(xdb, &generation, &slotnpages, &pagesize, &usergeneration))
 	return RPMRC_FAIL;
-    version = le2ha((unsigned char *)header + XDB_OFFSET_VERSION);
-    if (version != XDB_VERSION) {
-	rpmlog(RPMLOG_ERR, _("rpmxdb: Version mismatch. Expected version: %u. "
-	    "Found version: %u\n"), XDB_VERSION, version);
-	return RPMRC_FAIL;
-    }
-
-    generation = le2ha((unsigned char *)header + XDB_OFFSET_GENERATION);
-    slotnpages = le2ha((unsigned char *)header + XDB_OFFSET_SLOTNPAGES);
-    pagesize = le2ha((unsigned char *)header + XDB_OFFSET_PAGESIZE);
-    usergeneration = le2ha((unsigned char *)header + XDB_OFFSET_USERGENERATION);
-    if (!slotnpages || !pagesize || stb.st_size % pagesize != 0)
+    if (stb.st_size % pagesize != 0)
 	return RPMRC_FAIL;
     xdb->pagesize = pagesize;
     xdb->mapflags = xdb->rdonly ? PROT_READ : PROT_READ | PROT_WRITE;
@@ -923,6 +931,43 @@ int rpmxdbDelBlob(rpmxdb xdb, unsigned int id)
     return RPMRC_OK;
 }
 
+int rpmxdbDelAllBlobs(rpmxdb xdb)
+{
+    unsigned int slotnpages, pagesize, generation, usergeneration;
+    if (rpmxdbLockOnly(xdb, 1))
+	return RPMRC_FAIL;
+    /* unmap all blobs */
+    if (xdb->slots) {
+	int i;
+	struct xdb_slot *slot;
+	for (i = 1, slot = xdb->slots + i; i < xdb->nslots; i++, slot++) {
+	    if (slot->startpage && slot->mapped) {
+		unmapslot(xdb, slot);
+		slot->mapcallback(xdb, slot->mapcallbackdata, 0, 0);
+	    }
+	}
+	free(xdb->slots);
+	xdb->slots = 0;
+    }
+    if (xdb->mapped)
+	unmapheader(xdb);
+    if (rpmxdbReadHeaderRaw(xdb, &generation, &slotnpages, &pagesize, &usergeneration)) {
+	rpmxdbUnlock(xdb, 1);
+	return RPMRC_FAIL;
+    }
+    xdb->generation = generation + 1;
+    xdb->slotnpages = 1;
+    xdb->pagesize = pagesize;
+    xdb->usergeneration = usergeneration;
+    if (rpmxdbWriteEmptySlotpage(xdb, 0)) {
+	rpmxdbUnlock(xdb, 1);
+	return RPMRC_FAIL;
+    }
+    ftruncate(xdb->fd, xdb->pagesize);
+    rpmxdbUnlock(xdb, 1);
+    return RPMRC_OK;
+}
+
 int rpmxdbResizeBlob(rpmxdb xdb, unsigned int id, size_t newsize)
 {
     struct xdb_slot *slot;
diff --git a/lib/backend/ndb/rpmxdb.h b/lib/backend/ndb/rpmxdb.h
index 88a3e61..ddf02c1 100644
--- a/lib/backend/ndb/rpmxdb.h
+++ b/lib/backend/ndb/rpmxdb.h
@@ -14,6 +14,7 @@ int rpmxdbUnlock(rpmxdb xdb, int excl);
 
 int rpmxdbLookupBlob(rpmxdb xdb, unsigned int *idp, unsigned int blobtag, unsigned int subtag, int flags);
 int rpmxdbDelBlob(rpmxdb xdb, unsigned int id) ;
+int rpmxdbDelAllBlobs(rpmxdb xdb);
 
 int rpmxdbMapBlob(rpmxdb xdb, unsigned int id, int flags, void (*mapcallback)(rpmxdb xdb, void *data, void *newaddr, size_t newsize), void *mapcallbackdata);
 int rpmxdbUnmapBlob(rpmxdb xdb, unsigned int id);
-- 
1.8.3.1

