diff --git a/rpm-4.19.x-add-parkdb.patch b/rpm-4.19.x-add-parkdb.patch new file mode 100644 index 0000000..ae88c3f --- /dev/null +++ b/rpm-4.19.x-add-parkdb.patch @@ -0,0 +1,428 @@ +From 17a703eb3dfa7e2da4f441bd200d2548aef64025 Mon Sep 17 00:00:00 2001 +From: Michal Domonkos +Date: Tue, 2 Jun 2026 10:02:53 +0200 +Subject: [PATCH 1/2] Extract common database rebuild logic + +No functional change, just prepares the ground for the next commit. + +(backported from commit 1dcddf95b7d956f5d024f63a531ea58bc485efe5) +--- + lib/rpmts.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/lib/rpmts.c b/lib/rpmts.c +index 2f293e751..bc52a2f28 100644 +--- a/lib/rpmts.c ++++ b/lib/rpmts.c +@@ -137,12 +137,10 @@ int rpmtsSetDBMode(rpmts ts, int dbmode) + return rc; + } + +- +-int rpmtsRebuildDB(rpmts ts) ++static int rebuildDB(rpmts ts, int rebuildflags) + { + int rc = -1; + rpmtxn txn = NULL; +- int rebuildflags = 0; + + /* Cannot do this on a populated transaction set */ + if (rpmtsNElements(ts) > 0) +@@ -162,6 +160,11 @@ int rpmtsRebuildDB(rpmts ts) + return rc; + } + ++int rpmtsRebuildDB(rpmts ts) ++{ ++ return rebuildDB(ts, 0); ++} ++ + int rpmtsVerifyDB(rpmts ts) + { + int rc = -1; +-- +2.54.0 + + +From 845be24a9128d73d6307cec8692c5783a7671a61 Mon Sep 17 00:00:00 2001 +From: Michal Domonkos +Date: Wed, 3 Jun 2026 14:19:41 +0200 +Subject: [PATCH 2/2] Add support for database parking + +Prior to including the RPM database on read-only media (such as a base +image in rpm-ostree), it should undergo some housekeeping and cleanup. +Add a new "parking" operation to rpmdb(8) and the corresponding API to +do just that. + +This operation consists of a database rebuild coupled with an optional, +backend specific action triggered by passing a new flag RPMDB_FLAG_PARK +on database open. Since the action may take place on database close, let +the backend report a failure to park that way by handling the result in +rpmdbRebuild(). + +Implement such an action in the sqlite backend where it actually makes a +difference: Commit a8588f8e3970223f0b2fcc5d679dcb176f2e317b enabled WAL +mode but sqlite recommends that the database be converted to the default +DELETE journal mode prior to "burning" it onto read-only media [*]. Make +sure to do the conversion on database close since we may write to the +database during the rebuild, and that should always happen in WAL mode. + +As a nice side effect with the sqlite backend, parking the database will +automatically remove the *-wal and *-shm files from %_dbpath, making it +usable in reproducible images. Hint at that in the man page but make it +clear that this is not a guarantee (but rather an implementation detail +of sqlite). Make use of this property in the test, though. + +By parking, the database does not become read-only, and the parked state +does not stick when opening the database for writing (as we re-establish +WAL mode in such a case). This allows for the database to be immediately +writable inside a container running on top of a base image that includes +the database, without the user having to unpark it first. + +[*] https://sqlite.org/wal.html#read_only_databases + +Fixes: #2219 +(backported from commit 1825517392c1a04b1f3bc6a89d308fdda2927edd) +--- + docs/man/rpmdb.8.md | 13 +++++++++++-- + include/rpm/rpmts.h | 7 +++++++ + lib/backend/dbi.h | 1 + + lib/backend/sqlite.c | 17 +++++++++++++++-- + lib/rpmdb.c | 7 +++++-- + lib/rpmdb_internal.h | 1 + + lib/rpmts.c | 5 +++++ + python/rpmts-py.c | 15 +++++++++++++++ + tools/rpmdb.c | 6 ++++++ + 9 files changed, 66 insertions(+), 6 deletions(-) + +diff --git a/docs/man/rpmdb.8.md b/docs/man/rpmdb.8.md +index 8ad1ce3bb..7d308ca98 100644 +--- a/docs/man/rpmdb.8.md ++++ b/docs/man/rpmdb.8.md +@@ -12,7 +12,7 @@ rpmdb - RPM Database Tool + SYNOPSIS + ======== + +-**rpmdb** {**\--initdb\|\--rebuilddb**} ++**rpmdb** {**\--initdb\|\--rebuilddb\|\--parkdb**} + + **rpmdb** {**\--verifydb**} + +@@ -23,7 +23,7 @@ DESCRIPTION + + The general form of an rpmdb command is + +-**rpm** {**\--initdb\|\--rebuilddb**} \[**-v**\] \[**\--dbpath ++**rpm** {**\--initdb\|\--rebuilddb\|\--parkdb**} \[**-v**\] \[**\--dbpath + ***DIRECTORY*\] \[**\--root ***DIRECTORY*\] + + Use **\--initdb** to create a new database if one doesn\'t already exist +@@ -38,6 +38,15 @@ for transfporting to another host or database type. + **\--importdb** imports a database from a header-list format as created + by **\--exportdb**. + ++**\--parkdb** parks the database. This prepares and optimizes the database for ++inclusion on read-only media, such as immutable OS images. Write operations, ++such as RPM transactions, will unpark the database and continue normally, ++leaving the database unparked when finished. Thus, to unpark manually, use ++**\--rebuilddb**. Depending on the backend used, this operation may include the ++removal of auxiliary, backend-specific files from disk, such as the write-ahead ++log or shared memory index, and thus facilitate bit-for-bit reproducible OS ++images. ++ + SEE ALSO + ======== + +diff --git a/include/rpm/rpmts.h b/include/rpm/rpmts.h +index 5c168820f..d390f2ac4 100644 +--- a/include/rpm/rpmts.h ++++ b/include/rpm/rpmts.h +@@ -308,6 +308,13 @@ int rpmtsSetDBMode(rpmts ts, int dbmode); + */ + int rpmtsRebuildDB(rpmts ts); + ++/** \ingroup rpmts ++ * Park the database used by the transaction. ++ * @param ts transaction set ++ * @return 0 on success ++ */ ++int rpmtsParkDB(rpmts ts); ++ + /** \ingroup rpmts + * Verify the database used by the transaction. + * @param ts transaction set +diff --git a/lib/backend/dbi.h b/lib/backend/dbi.h +index 00ada038e..6e6226ff7 100644 +--- a/lib/backend/dbi.h ++++ b/lib/backend/dbi.h +@@ -13,6 +13,7 @@ enum rpmdbFlags { + RPMDB_FLAG_REBUILD = (1 << 1), + RPMDB_FLAG_VERIFYONLY = (1 << 2), + RPMDB_FLAG_SALVAGE = (1 << 3), ++ RPMDB_FLAG_PARK = (1 << 4), + }; + + typedef enum dbCtrlOp_e { +diff --git a/lib/backend/sqlite.c b/lib/backend/sqlite.c +index 76e9cae72..6cedcce16 100644 +--- a/lib/backend/sqlite.c ++++ b/lib/backend/sqlite.c +@@ -197,10 +197,23 @@ static int sqlite_fini(rpmdb rdb) + if (sqlite3_db_readonly(sdb, NULL) == 0) { + sqlexec(sdb, "PRAGMA optimize"); + sqlexec(sdb, "PRAGMA wal_checkpoint = TRUNCATE"); ++ ++ /* Park the database if requested */ ++ if ((rdb->db_flags & RPMDB_FLAG_PARK) != 0) { ++ int zero = 0; ++ /* Make sure any WAL and SHM files are cleaned up */ ++ sqlite3_file_control(sdb, NULL, SQLITE_FCNTL_PERSIST_WAL, ++ &zero); ++ rc = sqlexec(sdb, "PRAGMA journal_mode = DELETE"); ++ if (rc == 0) ++ rpmlog(RPMLOG_DEBUG, _("rpmdb sqlite parking done\n")); ++ else ++ rpmlog(RPMLOG_ERR, _("rpmdb sqlite parking failed\n")); ++ } + } + rdb->db_dbenv = NULL; + int xx = sqlite3_close(sdb); +- rc = (xx != SQLITE_OK); ++ rc += (xx != SQLITE_OK); + } + } + +@@ -329,7 +342,7 @@ static int sqlite_Close(dbiIndex dbi, unsigned int flags) + int rc = 0; + if (rdb->db_flags & RPMDB_FLAG_REBUILD) + rc = init_index(dbi, rpmTagGetValue(dbi->dbi_file)); +- sqlite_fini(dbi->dbi_rpmdb); ++ rc += sqlite_fini(dbi->dbi_rpmdb); + dbiFree(dbi); + return rc; + } +diff --git a/lib/rpmdb.c b/lib/rpmdb.c +index 117df46e1..39b2e723e 100644 +--- a/lib/rpmdb.c ++++ b/lib/rpmdb.c +@@ -2450,7 +2450,9 @@ int rpmdbRebuild(const char * prefix, rpmts ts, + goto exit; + } + if (openDatabase(prefix, newdbpath, &newdb, +- (O_RDWR | O_CREAT), 0644, RPMDB_FLAG_REBUILD)) { ++ (O_RDWR | O_CREAT), 0644, RPMDB_FLAG_REBUILD | ++ (rebuildflags & RPMDB_REBUILD_FLAG_PARK ? ++ RPMDB_FLAG_PARK : 0))) { + rc = 1; + goto exit; + } +@@ -2498,7 +2500,8 @@ int rpmdbRebuild(const char * prefix, rpmts ts, + + rpmdbClose(olddb); + dbCtrl(newdb, DB_CTRL_INDEXSYNC); +- rpmdbClose(newdb); ++ if (rpmdbClose(newdb)) ++ failed = 1; + + if (failed) { + rpmlog(RPMLOG_WARNING, +diff --git a/lib/rpmdb_internal.h b/lib/rpmdb_internal.h +index 0590013db..36e18b71a 100644 +--- a/lib/rpmdb_internal.h ++++ b/lib/rpmdb_internal.h +@@ -22,6 +22,7 @@ extern "C" { + + enum rpmdbRebuildFlags_e { + RPMDB_REBUILD_FLAG_SALVAGE = (1 << 0), ++ RPMDB_REBUILD_FLAG_PARK = (1 << 1), + }; + + /** \ingroup rpmdb +diff --git a/lib/rpmts.c b/lib/rpmts.c +index bc52a2f28..96dd31b0a 100644 +--- a/lib/rpmts.c ++++ b/lib/rpmts.c +@@ -165,6 +165,11 @@ int rpmtsRebuildDB(rpmts ts) + return rebuildDB(ts, 0); + } + ++int rpmtsParkDB(rpmts ts) ++{ ++ return rebuildDB(ts, RPMDB_REBUILD_FLAG_PARK); ++} ++ + int rpmtsVerifyDB(rpmts ts) + { + int rc = -1; +diff --git a/python/rpmts-py.c b/python/rpmts-py.c +index ae2eeea28..dc81d3489 100644 +--- a/python/rpmts-py.c ++++ b/python/rpmts-py.c +@@ -375,6 +375,18 @@ rpmts_RebuildDB(rpmtsObject * s) + return Py_BuildValue("i", rc); + } + ++static PyObject * ++rpmts_ParkDB(rpmtsObject * s) ++{ ++ int rc; ++ ++ Py_BEGIN_ALLOW_THREADS ++ rc = rpmtsParkDB(s->ts); ++ Py_END_ALLOW_THREADS ++ ++ return Py_BuildValue("i", rc); ++} ++ + static PyObject * + rpmts_VerifyDB(rpmtsObject * s) + { +@@ -799,6 +811,9 @@ Remove all elements from the transaction set\n" }, + {"rebuildDB", (PyCFunction) rpmts_RebuildDB, METH_NOARGS, + "ts.rebuildDB() -> None\n\ + - Rebuild the default transaction rpmdb.\n" }, ++ {"parkDB", (PyCFunction) rpmts_ParkDB, METH_NOARGS, ++"ts.parkDB() -> None\n\ ++- Park the default transaction rpmdb.\n" }, + {"verifyDB", (PyCFunction) rpmts_VerifyDB, METH_NOARGS, + "ts.verifyDB() -> None\n\ + - Verify the default transaction rpmdb.\n" }, +diff --git a/tools/rpmdb.c b/tools/rpmdb.c +index 36efff8af..3de81be26 100644 +--- a/tools/rpmdb.c ++++ b/tools/rpmdb.c +@@ -13,6 +13,7 @@ enum modes { + MODE_EXPORTDB = (1 << 3), + MODE_IMPORTDB = (1 << 4), + MODE_SALVAGEDB = (1 << 5), ++ MODE_PARKDB = (1 << 6), + }; + + static int mode = 0; +@@ -23,6 +24,8 @@ static struct poptOption dbOptsTable[] = { + { "rebuilddb", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), &mode, MODE_REBUILDDB, + N_("rebuild database inverted lists from installed package headers"), + NULL}, ++ { "parkdb", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), ++ &mode, MODE_PARKDB, N_("park database"), NULL}, + { "verifydb", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR), + &mode, MODE_VERIFYDB, N_("verify database"), NULL}, + { "salvagedb", '\0', (POPT_ARG_VAL|POPT_ARGFLAG_OR|POPT_ARGFLAG_DOC_HIDDEN), +@@ -117,6 +120,9 @@ int main(int argc, char *argv[]) + ec = rpmtsRebuildDB(ts); + rpmtsSetVSFlags(ts, ovsflags); + } break; ++ case MODE_PARKDB: ++ ec = rpmtsParkDB(ts); ++ break; + case MODE_VERIFYDB: + ec = rpmtsVerifyDB(ts); + break; +-- +2.54.0 + +diff -up rpm-4.19.1.1/docs/man/rpmdb.8.orig rpm-4.19.1.1/docs/man/rpmdb.8 +--- rpm-4.19.1.1/docs/man/rpmdb.8.orig 2026-06-18 09:27:25.469107665 +0200 ++++ rpm-4.19.1.1/docs/man/rpmdb.8 2026-06-18 09:27:53.907945350 +0200 +@@ -1,70 +1,60 @@ +-.\" Automatically generated by Pandoc 3.1.3 ++.\" Automatically generated by Pandoc 3.1.11.1 + .\" +-.\" Define V font for inline verbatim, using C font in formats +-.\" that render this, and otherwise B font. +-.ie "\f[CB]x\f[]"x" \{\ +-. ftr V B +-. ftr VI BI +-. ftr VB B +-. ftr VBI BI +-.\} +-.el \{\ +-. ftr V CR +-. ftr VI CI +-. ftr VB CB +-. ftr VBI CBI +-.\} + .TH "RPMDB" "8" "29 June 2010" "" "" +-.hy + .SH NAME +-.PP +-rpmdb - RPM Database Tool ++rpmdb \- RPM Database Tool + .SH SYNOPSIS ++\f[B]rpmdb\f[R] {\f[B]\-\-initdb|\-\-rebuilddb|\-\-parkdb\f[R]} + .PP +-\f[B]rpmdb\f[R] {\f[B]--initdb|--rebuilddb\f[R]} +-.PP +-\f[B]rpmdb\f[R] {\f[B]--verifydb\f[R]} ++\f[B]rpmdb\f[R] {\f[B]\-\-verifydb\f[R]} + .PP +-\f[B]rpmdb\f[R] {\f[B]--exportdb|--importdb\f[R]} ++\f[B]rpmdb\f[R] {\f[B]\-\-exportdb|\-\-importdb\f[R]} + .SH DESCRIPTION +-.PP + The general form of an rpmdb command is + .PP +-\f[B]rpm\f[R] {\f[B]--initdb|--rebuilddb\f[R]} [\f[B]-v\f[R]] +-[\f[B]--dbpath \f[R]\f[I]DIRECTORY\f[R]] [\f[B]--root +-\f[R]\f[I]DIRECTORY\f[R]] ++\f[B]rpm\f[R] {\f[B]\-\-initdb|\-\-rebuilddb|\-\-parkdb\f[R]} ++[\f[B]\-v\f[R]] [\f[B]\-\-dbpath \f[R]\f[I]DIRECTORY\f[R]] ++[\f[B]\-\-root \f[R]\f[I]DIRECTORY\f[R]] + .PP +-Use \f[B]--initdb\f[R] to create a new database if one doesn\[aq]t ++Use \f[B]\-\-initdb\f[R] to create a new database if one doesn\[aq]t + already exist (existing database is not overwritten), use +-\f[B]--rebuilddb\f[R] to rebuild the database indices from the installed +-package headers. ++\f[B]\-\-rebuilddb\f[R] to rebuild the database indices from the ++installed package headers. + .PP +-\f[B]--verifydb\f[R] performs a low-level integrity check on the ++\f[B]\-\-verifydb\f[R] performs a low\-level integrity check on the + database. + .PP +-\f[B]--exportdb\f[R] exports the database in header-list format, ++\f[B]\-\-exportdb\f[R] exports the database in header\-list format, + suitable for transfporting to another host or database type. + .PP +-\f[B]--importdb\f[R] imports a database from a header-list format as +-created by \f[B]--exportdb\f[R]. +-.SH SEE ALSO ++\f[B]\-\-importdb\f[R] imports a database from a header\-list format as ++created by \f[B]\-\-exportdb\f[R]. + .PP ++\f[B]\-\-parkdb\f[R] parks the database. ++This prepares and optimizes the database for inclusion on read\-only ++media, such as immutable OS images. ++Write operations, such as RPM transactions, will unpark the database and ++continue normally, leaving the database unparked when finished. ++Thus, to unpark manually, use \f[B]\-\-rebuilddb\f[R]. ++Depending on the backend used, this operation may include the removal of ++auxiliary, backend\-specific files from disk, such as the write\-ahead ++log or shared memory index, and thus facilitate bit\-for\-bit ++reproducible OS images. ++.SH SEE ALSO + \f[B]popt\f[R](3), \f[B]rpm\f[R](8), \f[B]rpmkeys\f[R](8), + \f[B]rpmsign\f[R](8), \f[B]rpm2cpio\f[R](8), \f[B]rpmbuild\f[R](8), + \f[B]rpmspec\f[R](8) + .PP +-\f[B]rpm --help\f[R] - as rpm supports customizing the options via popt +-aliases it\[aq]s impossible to guarantee that what\[aq]s described in +-the manual matches what\[aq]s available. ++\f[B]rpm \-\-help\f[R] \- as rpm supports customizing the options via ++popt aliases it\[aq]s impossible to guarantee that what\[aq]s described ++in the manual matches what\[aq]s available. + .PP + \f[B]http://www.rpm.org/ \f[R] + .SH AUTHORS + .IP +-.nf +-\f[C] ++.EX + Marc Ewing + Jeff Johnson + Erik Troan + Panu Matilainen +-\f[R] +-.fi ++.EE diff --git a/rpm.spec b/rpm.spec index b49dafd..1c3a235 100644 --- a/rpm.spec +++ b/rpm.spec @@ -176,6 +176,7 @@ rpm-4.19.x-nsswitch-enable.patch 0001-Fix-empty-password-field-in-passwd-group-causing-ent.patch rpm-4.19.x-add-autosetup-C.patch +rpm-4.19.x-add-parkdb.patch # These are not yet upstream rpm-4.7.1-geode-i686.patch @@ -668,6 +669,7 @@ fi %changelog * Thu Jun 18 2026 Michal Domonkos - 4.19.1.1-24 - Add support for %%autosetup -C (RHEL-141269) +- Add support for database parking (RHEL-126405) * Thu Feb 05 2026 Michal Domonkos - 4.19.1.1-23 - Fix key import API to return NOTTRUSTED for disabled algorithms (RHEL-112394)