diff --git a/lib/backend/bdb_ro.c b/lib/backend/bdb_ro.c index 74569cb03d..0c132412fc 100644 --- a/lib/backend/bdb_ro.c +++ b/lib/backend/bdb_ro.c @@ -790,6 +790,7 @@ static unsigned int bdbro_pkgdbKey(dbiIndex dbi, dbiCursor dbc) struct rpmdbOps_s bdbro_dbops = { .name = "bdb_ro", .path = "Packages", + .readonly = 1, .open = bdbro_Open, .close = bdbro_Close, diff --git a/lib/backend/db3.c b/lib/backend/db3.c index 3e6d198ac3..5649fb8465 100644 --- a/lib/backend/db3.c +++ b/lib/backend/db3.c @@ -1231,7 +1231,7 @@ static rpmRC updatePackages(dbiCursor dbc, unsigned int hdrNum, DBT *hdr) } /* Get current header instance number or try to allocate a new one */ -static unsigned int pkgInstance(dbiIndex dbi, int alloc) +static unsigned int pkgInstance(dbiIndex dbi, int alloc, unsigned int hdrNumIn) { unsigned int hdrNum = 0; @@ -1259,6 +1259,11 @@ static unsigned int pkgInstance(dbiIndex dbi, int alloc) hdrNum = mi_offset.ui; } + if (alloc && hdrNumIn) { + alloc = hdrNum >= hdrNumIn ? 0 : 1; + hdrNum = alloc ? hdrNumIn - 1 : hdrNumIn; + } + if (alloc) { /* Rather complicated "increment by one", bswapping as needed */ ++hdrNum; @@ -1296,8 +1301,8 @@ static rpmRC db3_pkgdbPut(dbiIndex dbi, dbiCursor dbc, unsigned int *hdrNum, hdr.data = hdrBlob; hdr.size = hdrLen; - if (hnum == 0) - hnum = pkgInstance(dbi, 1); + if (hnum == 0 || (dbi->dbi_rpmdb->db_flags & RPMDB_FLAG_CONVERT) != 0) + hnum = pkgInstance(dbi, 1, hnum); if (hnum) rc = updatePackages(dbc, hnum, &hdr); diff --git a/lib/backend/dbi.c b/lib/backend/dbi.c index 7841440888..d8d262f001 100644 --- a/lib/backend/dbi.c +++ b/lib/backend/dbi.c @@ -57,45 +57,71 @@ dbDetectBackend(rpmdb rdb) const char *dbhome = rpmdbHome(rdb); char *db_backend = rpmExpand("%{?_db_backend}", NULL); char *path = NULL; - const struct rpmdbOps_s **ops; + const struct rpmdbOps_s **ops, *ops_config; - for (ops = backends; ops && *ops; ops++) { + rdb->db_ops = NULL; + + ops_config = NULL; + for (ops = backends; *ops; ops++) { if (rstreq(db_backend, (*ops)->name)) { - rdb->db_ops = *ops; + ops_config = *ops; break; } } - for (ops = backends; ops && *ops; ops++) { - int stop = 0; - if ((*ops)->path == NULL) - continue; - - path = rstrscat(NULL, dbhome, "/", (*ops)->path, NULL); - if (access(path, F_OK) == 0 && rdb->db_ops != *ops) { - rpmlog(RPMLOG_WARNING, - _("Found %s %s database while attempting %s backend: " - "using %s backend.\n"), - (*ops)->name, (*ops)->path, db_backend, (*ops)->name); - rdb->db_ops = *ops; - stop = 1; - } + /* if we have a configured backend, check it first */ + if (ops_config && ops_config->path) { + path = rstrscat(NULL, dbhome, "/", ops_config->path, NULL); + if (access(path, F_OK) == 0) + rdb->db_ops = ops_config; free(path); - if (stop) - break; } + /* if it did not match, check all available backends */ + if (rdb->db_ops == NULL) { + for (ops = backends; *ops; ops++) { + if ((*ops)->path == NULL || *ops == ops_config) + continue; + + path = rstrscat(NULL, dbhome, "/", (*ops)->path, NULL); + if (access(path, F_OK) == 0) { + rpmlog(RPMLOG_WARNING, + _("Found %s %s database while attempting %s backend: " + "using %s backend.\n"), + (*ops)->name, (*ops)->path, db_backend, (*ops)->name); + rdb->db_ops = *ops; + } + free(path); + if (rdb->db_ops != NULL) + break; + } + } + + /* if we did not find a match, use the configured backend */ + if (rdb->db_ops == NULL && ops_config) + rdb->db_ops = ops_config; + + /* if everything failed fall back to dummydb */ if (rdb->db_ops == NULL) { rdb->db_ops = &dummydb_dbops; rpmlog(RPMLOG_WARNING, "using dummy database, installs not possible\n"); } rdb->db_descr = rdb->db_ops->name; + rdb->db_ops_config = ops_config; if (db_backend) free(db_backend); } +int dbiNeedConversion(rpmdb rdb) +{ + if (!rdb->db_ops) + dbDetectBackend(rdb); + return rdb->db_ops->readonly && rdb->db_ops_config + && rdb->db_ops_config->path && !rdb->db_ops_config->readonly; +} + const char * dbiName(dbiIndex dbi) { return dbi->dbi_file; diff --git a/lib/backend/dbi.h b/lib/backend/dbi.h index c4bf387870..a9584d6e23 100644 --- a/lib/backend/dbi.h +++ b/lib/backend/dbi.h @@ -10,6 +10,7 @@ enum rpmdbFlags { RPMDB_FLAG_JUSTCHECK = (1 << 0), RPMDB_FLAG_REBUILD = (1 << 1), RPMDB_FLAG_VERIFYONLY = (1 << 2), + RPMDB_FLAG_CONVERT = (1 << 3), }; typedef enum dbCtrlOp_e { @@ -60,6 +61,7 @@ struct rpmdb_s { int db_buildindex; /*!< Index rebuild indicator */ const struct rpmdbOps_s * db_ops; /*!< backend ops */ + const struct rpmdbOps_s * db_ops_config; /*!< configured backend ops */ /* dbenv and related parameters */ void * db_dbenv; /*!< Backend private handle */ @@ -204,6 +206,14 @@ int dbiFlags(dbiIndex dbi); RPM_GNUC_INTERNAL const char * dbiName(dbiIndex dbi); +/** \ingroup dbi + * Check if the database needs to be converted to a different format + * @param db rpm database + * @return boolean + */ +RPM_GNUC_INTERNAL +int dbiNeedConversion(rpmdb rdb); + /** \ingroup dbi * Open a database cursor. * @param dbi index database handle @@ -248,6 +258,7 @@ const void * idxdbKey(dbiIndex dbi, dbiCursor dbc, unsigned int *keylen); struct rpmdbOps_s { const char *name; /* backend name */ const char *path; /* main database name */ + int readonly; /* cannot modify database */ int (*open)(rpmdb rdb, rpmDbiTagVal rpmtag, dbiIndex * dbip, int flags); int (*close)(dbiIndex dbi, unsigned int flags); diff --git a/lib/backend/lmdb.c b/lib/backend/lmdb.c index f5b94ce487..b51a6ed2e1 100644 --- a/lib/backend/lmdb.c +++ b/lib/backend/lmdb.c @@ -804,7 +804,7 @@ static rpmRC updatePackages(dbiCursor dbc, unsigned int hdrNum, MDB_val *hdr) } /* Get current header instance number or try to allocate a new one */ -static unsigned int pkgInstance(dbiCursor dbc, int alloc) +static unsigned int pkgInstance(dbiCursor dbc, int alloc, unsigned int hdrNumIn) { unsigned int hdrNum = 0; @@ -826,6 +826,11 @@ static unsigned int pkgInstance(dbiCursor dbc, int alloc) hdrNum = mi_offset.ui; } + if (alloc && hdrNumIn) { + alloc = hdrNum >= hdrNumIn ? 0 : 1; + hdrNum = alloc ? hdrNumIn - 1 : hdrNumIn; + } + if (alloc) { /* Rather complicated "increment by one", bswapping as needed */ ++hdrNum; @@ -856,8 +861,8 @@ static rpmRC lmdb_pkgdbPut(dbiIndex dbi, dbiCursor dbc, unsigned int *hdrNum, hdr.mv_data = hdrBlob; hdr.mv_size = hdrLen; - if (hnum == 0) - hnum = pkgInstance(dbc, 1); + if (hnum == 0 || (dbi->dbi_rpmdb->db_flags & RPMDB_FLAG_CONVERT) != 0) + hnum = pkgInstance(dbc, 1, hnum); if (hnum) rc = updatePackages(dbc, hnum, &hdr); diff --git a/lib/backend/ndb/rpmpkg.c b/lib/backend/ndb/rpmpkg.c index 922ae110bd..387e37cb56 100644 --- a/lib/backend/ndb/rpmpkg.c +++ b/lib/backend/ndb/rpmpkg.c @@ -968,11 +968,12 @@ static int rpmpkgPutInternal(rpmpkgdb pkgdb, unsigned int pkgidx, unsigned char if (rpmpkgWriteBlob(pkgdb, pkgidx, blkoff, blkcnt, blob, blobl, pkgdb->generation)) { return RPMRC_FAIL; } + /* update nextpkgidx if needed */ + if (pkgidx >= pkgdb->nextpkgidx) { + pkgdb->nextpkgidx = pkgidx + 1; + } /* write slot */ slotno = oldslot ? oldslot->slotno : pkgdb->freeslot; - if (!slotno) { - return RPMRC_FAIL; - } if (rpmpkgWriteslot(pkgdb, slotno, pkgidx, blkoff, blkcnt)) { free(pkgdb->slots); pkgdb->slots = 0; diff --git a/lib/rpmdb.c b/lib/rpmdb.c index 91543eb686..7c9b64b66d 100644 --- a/lib/rpmdb.c +++ b/lib/rpmdb.c @@ -505,8 +505,16 @@ static int openDatabase(const char * prefix, rpmsqActivate(1); } + /* Convert the database if needed */ + if (!db->db_pkgs && !justCheck && (mode & O_ACCMODE) == O_RDWR && dbiNeedConversion(db)) { + rpmlog(RPMLOG_WARNING, _("Converting database from %s to %s format\n"), db->db_ops->name, db->db_ops_config->name); + rc = rpmdbRebuild(prefix, NULL, NULL, RPMDB_REBUILD_FLAG_CONVERT); + db->db_ops = NULL; /* force re-detection of backend */ + } + /* Just the primary Packages database opened here */ - rc = pkgdbOpen(db, db->db_flags, NULL); + if (!rc) + rc = pkgdbOpen(db, db->db_flags, NULL); if (!db->db_descr) db->db_descr = "unknown db"; } @@ -2306,6 +2314,15 @@ int rpmdbAdd(rpmdb db, Header h) if (db == NULL) return 0; + if ((db->db_flags & RPMDB_FLAG_CONVERT) != 0) { + /* keep old instance numbers when converting */ + hdrNum = headerGetInstance(h); + if (hdrNum == 0) { + ret = -1; + goto exit; + } + } + hdrBlob = headerExport(h, &hdrLen); if (hdrBlob == NULL || hdrLen == 0) { ret = -1; @@ -2479,7 +2496,8 @@ static int rpmdbSetPermissions(char * src, char * dest) } int rpmdbRebuild(const char * prefix, rpmts ts, - rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, char ** msg)) + rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, char ** msg), + int rebuildflags) { rpmdb olddb; char * dbpath = NULL; @@ -2524,7 +2542,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_CONVERT ? + RPMDB_FLAG_CONVERT : 0))) { rc = 1; goto exit; } @@ -2557,6 +2577,7 @@ int rpmdbRebuild(const char * prefix, rpmts ts, /* Deleted entries are eliminated in legacy headers by copy. */ if (headerIsEntry(h, RPMTAG_HEADERIMAGE)) { Header nh = headerReload(headerCopy(h), RPMTAG_HEADERIMAGE); + headerSetInstance(nh, headerGetInstance(h)); rc = rpmdbAdd(newdb, nh); headerFree(nh); } else { diff --git a/lib/rpmdb_internal.h b/lib/rpmdb_internal.h index 5ad844c719..f38382d8df 100644 --- a/lib/rpmdb_internal.h +++ b/lib/rpmdb_internal.h @@ -23,6 +23,10 @@ extern "C" { #undef HTKEYTYPE #undef HTDATATYPE +enum rpmdbRebuildFlags_e { + RPMDB_REBUILD_FLAG_CONVERT = (1 << 0), +}; + /** \ingroup rpmdb * Reference a database instance. * @param db rpm database @@ -63,11 +67,13 @@ int rpmdbClose (rpmdb db); * @param prefix path to top of install tree * @param ts transaction set (or NULL) * @param (*hdrchk) headerCheck() vector (or NULL) + * @param rebuildflags flags * @return 0 on success */ RPM_GNUC_INTERNAL int rpmdbRebuild(const char * prefix, rpmts ts, - rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, char ** msg)); + rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, char ** msg), + int rebuildflags); /** \ingroup rpmdb * Verify database components. diff --git a/lib/rpmts.c b/lib/rpmts.c index 22c14d02c9..bec29f745e 100644 --- a/lib/rpmts.c +++ b/lib/rpmts.c @@ -143,9 +143,9 @@ int rpmtsRebuildDB(rpmts ts) txn = rpmtxnBegin(ts, RPMTXN_WRITE); if (txn) { if (!(ts->vsflags & RPMVSF_NOHDRCHK)) - rc = rpmdbRebuild(ts->rootDir, ts, headerCheck); + rc = rpmdbRebuild(ts->rootDir, ts, headerCheck, 0); else - rc = rpmdbRebuild(ts->rootDir, NULL, NULL); + rc = rpmdbRebuild(ts->rootDir, NULL, NULL, 0); rpmtxnEnd(txn); } return rc;