"Fossies" - the Fresh Open Source Software Archive

Member "rpm-4.15.1/lib/backend/db3.c" (5 Sep 2019, 34481 Bytes) of package /linux/misc/rpm-4.15.1.tar.bz2:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "db3.c" see the Fossies "Dox" file reference documentation.

    1 /** \ingroup rpmdb
    2  * \file lib/db3.c
    3  */
    4 
    5 static int _debug = 1;  /* XXX if < 0 debugging, > 0 unusual error returns */
    6 
    7 #include "system.h"
    8 
    9 #include <errno.h>
   10 #include <sys/wait.h>
   11 #include <popt.h>
   12 #include <db.h>
   13 #include <signal.h>
   14 
   15 #include <rpm/rpmtypes.h>
   16 #include <rpm/rpmmacro.h>
   17 #include <rpm/rpmfileutil.h>
   18 #include <rpm/rpmlog.h>
   19 
   20 #include "lib/rpmdb_internal.h"
   21 
   22 #include "debug.h"
   23 
   24 static const char * _errpfx = "rpmdb";
   25 
   26 struct dbiCursor_s {
   27     dbiIndex dbi;
   28     const void *key;
   29     unsigned int keylen;
   30     int flags;
   31     DBC *cursor;
   32 };
   33 
   34 static struct dbiConfig_s staticdbicfg;
   35 static struct dbConfig_s staticcfg;
   36 
   37 /** \ingroup dbi
   38  */
   39 static const struct poptOption rdbOptions[] = {
   40  /* Environment options */
   41    
   42  { "cdb",   0,POPT_BIT_SET, &staticcfg.db_eflags, DB_INIT_CDB,
   43     NULL, NULL },
   44  { "lock",  0,POPT_BIT_SET, &staticcfg.db_eflags, DB_INIT_LOCK,
   45     NULL, NULL },
   46  { "log",   0,POPT_BIT_SET, &staticcfg.db_eflags, DB_INIT_LOG,
   47     NULL, NULL },
   48  { "txn",   0,POPT_BIT_SET, &staticcfg.db_eflags, DB_INIT_TXN,
   49     NULL, NULL },
   50  { "recover",   0,POPT_BIT_SET, &staticcfg.db_eflags, DB_RECOVER,
   51     NULL, NULL },
   52  { "recover_fatal", 0,POPT_BIT_SET, &staticcfg.db_eflags, DB_RECOVER_FATAL,
   53     NULL, NULL },
   54  { "lockdown",  0,POPT_BIT_SET, &staticcfg.db_eflags, DB_LOCKDOWN,
   55     NULL, NULL },
   56  { "private",   0,POPT_BIT_SET, &staticcfg.db_eflags, DB_PRIVATE,
   57     NULL, NULL },
   58 
   59  { "deadlock",  0,POPT_BIT_SET, &staticcfg.db_verbose, DB_VERB_DEADLOCK,
   60     NULL, NULL },
   61  { "recovery",  0,POPT_BIT_SET, &staticcfg.db_verbose, DB_VERB_RECOVERY,
   62     NULL, NULL },
   63  { "waitsfor",  0,POPT_BIT_SET, &staticcfg.db_verbose, DB_VERB_WAITSFOR,
   64     NULL, NULL },
   65  { "verbose",   0,POPT_ARG_VAL,     &staticcfg.db_verbose, -1,
   66     NULL, NULL },
   67 
   68  { "cachesize", 0,POPT_ARG_INT,     &staticcfg.db_cachesize, 0,
   69     NULL, NULL },
   70  { "mmapsize", 0,POPT_ARG_INT,      &staticcfg.db_mmapsize, 0,
   71     NULL, NULL },
   72  { "mp_mmapsize", 0,POPT_ARG_INT,   &staticcfg.db_mmapsize, 0,
   73     NULL, NULL },
   74  { "mp_size",   0,POPT_ARG_INT,     &staticcfg.db_cachesize, 0,
   75     NULL, NULL },
   76 
   77  { "nofsync",   0,POPT_ARG_NONE,    &staticcfg.db_no_fsync, 0,
   78     NULL, NULL },
   79 
   80  /* Per-dbi options */
   81  { "nommap",    0,POPT_BIT_SET,     &staticdbicfg.dbi_oflags, DB_NOMMAP,
   82     NULL, NULL },
   83 
   84  { "nodbsync",  0,POPT_ARG_NONE,    &staticdbicfg.dbi_no_dbsync, 0,
   85     NULL, NULL },
   86  { "lockdbfd",  0,POPT_ARG_NONE,    &staticdbicfg.dbi_lockdbfd, 0,
   87     NULL, NULL },
   88 
   89     POPT_TABLEEND
   90 };
   91 
   92 
   93 static int dbapi_err(rpmdb rdb, const char * msg, int error, int printit)
   94 {
   95     if (printit && error) {
   96     if (msg)
   97         rpmlog(RPMLOG_ERR, _("%s error(%d) from %s: %s\n"),
   98         rdb->db_descr, error, msg, db_strerror(error));
   99     else
  100         rpmlog(RPMLOG_ERR, _("%s error(%d): %s\n"),
  101         rdb->db_descr, error, db_strerror(error));
  102     }
  103     return error;
  104 }
  105 
  106 static int cvtdberr(dbiIndex dbi, const char * msg, int error, int printit)
  107 {
  108     return dbapi_err(dbi->dbi_rpmdb, msg, error, printit);
  109 }
  110 
  111 static void errlog(const DB_ENV * env, const char *errpfx, const char *msg)
  112 {
  113     rpmlog(RPMLOG_ERR, "%s: %s\n", errpfx, msg);
  114 }
  115 
  116 static void warnlog(const DB_ENV *env, const char *msg)
  117 {
  118     rpmlog(RPMLOG_WARNING, "%s: %s\n", _errpfx, msg);
  119 }
  120 
  121 static uint32_t db_envflags(DB * db)
  122 {
  123     DB_ENV * env = db->get_env(db);
  124     uint32_t eflags = 0;
  125     (void) env->get_open_flags(env, &eflags);
  126     return eflags;
  127 }
  128 
  129 /*
  130  * Try to acquire db environment open/close serialization lock.
  131  * Return the open, locked fd on success, -1 on failure.
  132  */
  133 static int serialize_env(const char *dbhome)
  134 {
  135     char *lock_path = rstrscat(NULL, dbhome, "/.dbenv.lock", NULL);
  136     mode_t oldmask = umask(022);
  137     int fd = open(lock_path, (O_RDWR|O_CREAT), 0644);
  138     umask(oldmask);
  139 
  140     if (fd >= 0) {
  141     int rc;
  142     struct flock info;
  143     memset(&info, 0, sizeof(info));
  144     info.l_type = F_WRLCK;
  145     info.l_whence = SEEK_SET;
  146     do {
  147         rc = fcntl(fd, F_SETLKW, &info);
  148     } while (rc == -1 && errno == EINTR);
  149         
  150     if (rc == -1) {
  151         close(fd);
  152         fd = -1;
  153     }
  154     }
  155 
  156     free(lock_path);
  157     return fd;
  158 }
  159 
  160 static int db_fini(rpmdb rdb, const char * dbhome)
  161 {
  162     DB_ENV * dbenv = rdb->db_dbenv;
  163     int rc;
  164     int lockfd = -1;
  165     uint32_t eflags = 0;
  166 
  167     if (dbenv == NULL)
  168     return 0;
  169 
  170     if (rdb->db_opens > 1) {
  171     rdb->db_opens--;
  172     return 0;
  173     }
  174 
  175     (void) dbenv->get_open_flags(dbenv, &eflags);
  176     if (!(eflags & DB_PRIVATE))
  177     lockfd = serialize_env(dbhome);
  178 
  179     rc = dbenv->close(dbenv, 0);
  180     rc = dbapi_err(rdb, "dbenv->close", rc, _debug);
  181 
  182     rpmlog(RPMLOG_DEBUG, "closed   db environment %s\n", dbhome);
  183 
  184     if (!(eflags & DB_PRIVATE) && rdb->db_remove_env) {
  185     int xx;
  186 
  187     xx = db_env_create(&dbenv, 0);
  188     xx = dbapi_err(rdb, "db_env_create", xx, _debug);
  189     xx = dbenv->remove(dbenv, dbhome, 0);
  190     /* filter out EBUSY as it just means somebody else gets to clean it */
  191     xx = dbapi_err(rdb, "dbenv->remove", xx, (xx == EBUSY ? 0 : _debug));
  192 
  193     rpmlog(RPMLOG_DEBUG, "removed  db environment %s\n", dbhome);
  194 
  195     }
  196 
  197     if (lockfd >= 0)
  198     close(lockfd);
  199 
  200     return rc;
  201 }
  202 
  203 static int fsync_disable(int fd)
  204 {
  205     return 0;
  206 }
  207 
  208 /*
  209  * dbenv->failchk() callback method for determining is the given pid/tid 
  210  * is alive. We only care about pid's though. 
  211  */ 
  212 static int isalive(DB_ENV *dbenv, pid_t pid, db_threadid_t tid, uint32_t flags)
  213 {
  214     int alive = 0;
  215 
  216     if (pid == getpid()) {
  217     alive = 1;
  218     } else if (kill(pid, 0) == 0) {
  219     alive = 1;
  220     /* only existing processes can fail with EPERM */
  221     } else if (errno == EPERM) {
  222         alive = 1;
  223     }
  224     
  225     return alive;
  226 }
  227 
  228 
  229 static void dbConfigure(rpmDbiTagVal rpmtag, struct dbConfig_s *cfg, struct dbiConfig_s  *dbicfg)
  230 {
  231     char *dbOpts;
  232 
  233     dbOpts = rpmExpand("%{_dbi_config_", rpmTagGetName(rpmtag), "}", NULL);
  234     
  235     if (!(dbOpts && *dbOpts && *dbOpts != '%')) {
  236     dbOpts = _free(dbOpts);
  237     dbOpts = rpmExpand("%{_dbi_config}", NULL);
  238     if (!(dbOpts && *dbOpts && *dbOpts != '%')) {
  239         dbOpts = _free(dbOpts);
  240     }
  241     }
  242 
  243     /* Parse the options for the database element(s). */
  244     if (dbOpts && *dbOpts && *dbOpts != '%') {
  245     char *o, *oe;
  246     char *p, *pe;
  247 
  248     memset(&staticdbicfg, 0, sizeof(staticdbicfg));
  249 /*=========*/
  250     for (o = dbOpts; o && *o; o = oe) {
  251         const struct poptOption *opt;
  252         const char * tok;
  253         unsigned int argInfo;
  254 
  255         /* Skip leading white space. */
  256         while (*o && risspace(*o))
  257         o++;
  258 
  259         /* Find and terminate next key=value pair. Save next start point. */
  260         for (oe = o; oe && *oe; oe++) {
  261         if (risspace(*oe))
  262             break;
  263         if (oe[0] == ':' && !(oe[1] == '/' && oe[2] == '/'))
  264             break;
  265         }
  266         if (oe && *oe)
  267         *oe++ = '\0';
  268         if (*o == '\0')
  269         continue;
  270 
  271         /* Separate key from value, save value start (if any). */
  272         for (pe = o; pe && *pe && *pe != '='; pe++)
  273         {};
  274         p = (pe ? *pe++ = '\0', pe : NULL);
  275 
  276         /* Skip over negation at start of token. */
  277         for (tok = o; *tok == '!'; tok++)
  278         {};
  279 
  280         /* Find key in option table. */
  281         for (opt = rdbOptions; opt->longName != NULL; opt++) {
  282         if (!rstreq(tok, opt->longName))
  283             continue;
  284         break;
  285         }
  286         if (opt->longName == NULL) {
  287         rpmlog(RPMLOG_ERR,
  288             _("unrecognized db option: \"%s\" ignored.\n"), o);
  289         continue;
  290         }
  291 
  292         /* Toggle the flags for negated tokens, if necessary. */
  293         argInfo = opt->argInfo;
  294         if (argInfo == POPT_BIT_SET && *o == '!' && ((tok - o) % 2))
  295         argInfo = POPT_BIT_CLR;
  296 
  297         /* Save value in template as appropriate. */
  298         switch (argInfo & POPT_ARG_MASK) {
  299 
  300         case POPT_ARG_NONE:
  301         (void) poptSaveInt((int *)opt->arg, argInfo, 1L);
  302         break;
  303         case POPT_ARG_VAL:
  304         (void) poptSaveInt((int *)opt->arg, argInfo, (long)opt->val);
  305             break;
  306         case POPT_ARG_STRING:
  307         {   char ** t = opt->arg;
  308         if (t) {
  309 /* FIX: opt->arg annotation in popt.h */
  310             *t = _free(*t);
  311             *t = xstrdup( (p ? p : "") );
  312         }
  313         }   break;
  314 
  315         case POPT_ARG_INT:
  316         case POPT_ARG_LONG:
  317           { long aLong = strtol(p, &pe, 0);
  318         if (pe) {
  319             if (!rstrncasecmp(pe, "Mb", 2))
  320             aLong *= 1024 * 1024;
  321             else if (!rstrncasecmp(pe, "Kb", 2))
  322             aLong *= 1024;
  323             else if (*pe != '\0') {
  324             rpmlog(RPMLOG_ERR,
  325                 _("%s has invalid numeric value, skipped\n"),
  326                 opt->longName);
  327             continue;
  328             }
  329         }
  330 
  331         if ((argInfo & POPT_ARG_MASK) == POPT_ARG_LONG) {
  332             if (aLong == LONG_MIN || aLong == LONG_MAX) {
  333             rpmlog(RPMLOG_ERR,
  334                 _("%s has too large or too small long value, skipped\n"),
  335                 opt->longName);
  336             continue;
  337             }
  338             (void) poptSaveLong((long *)opt->arg, argInfo, aLong);
  339             break;
  340         } else {
  341             if (aLong > INT_MAX || aLong < INT_MIN) {
  342             rpmlog(RPMLOG_ERR,
  343                 _("%s has too large or too small integer value, skipped\n"),
  344                 opt->longName);
  345             continue;
  346             }
  347             (void) poptSaveInt((int *)opt->arg, argInfo, aLong);
  348         }
  349           } break;
  350         default:
  351         break;
  352         }
  353     }
  354 /*=========*/
  355     }
  356 
  357     dbOpts = _free(dbOpts);
  358     if (cfg) {
  359     *cfg = staticcfg;   /* structure assignment */
  360     /* Throw in some defaults if configuration didn't set any */
  361     if (!cfg->db_mmapsize)
  362         cfg->db_mmapsize = 16 * 1024 * 1024;
  363     if (!cfg->db_cachesize)
  364         cfg->db_cachesize = 8 * 1024 * 1024;
  365     }
  366     if (dbicfg) {
  367     *dbicfg = staticdbicfg;
  368     }
  369 }
  370 
  371 static char * prDbiOpenFlags(int dbflags, int print_dbenv_flags)
  372 {
  373     ARGV_t flags = NULL;
  374     const struct poptOption *opt;
  375     char *buf;
  376 
  377     for (opt = rdbOptions; opt->longName != NULL; opt++) {
  378         if (opt->argInfo != POPT_BIT_SET)
  379             continue;
  380         if (print_dbenv_flags) {
  381             if (!(opt->arg == &staticcfg.db_eflags))
  382                 continue;
  383         } else {
  384             if (!(opt->arg == &staticdbicfg.dbi_oflags))
  385                 continue;
  386         }
  387         if ((dbflags & opt->val) != opt->val)
  388             continue;
  389         argvAdd(&flags, opt->longName);
  390         dbflags &= ~opt->val;
  391     }   
  392     if (dbflags) {
  393         char *df = NULL;
  394         rasprintf(&df, "0x%x", (unsigned)dbflags);
  395         argvAdd(&flags, df);
  396         free(df);
  397     }   
  398     buf = argvJoin(flags, ":");
  399     argvFree(flags);
  400             
  401     return buf ? buf : xstrdup("(none)");
  402 }
  403 
  404 static int db_init(rpmdb rdb, const char * dbhome)
  405 {
  406     DB_ENV *dbenv = NULL;
  407     int rc, xx;
  408     int retry_open = 2;
  409     int lockfd = -1;
  410     int rdonly = ((rdb->db_mode & O_ACCMODE) == O_RDONLY);
  411     struct dbConfig_s * cfg = &rdb->cfg;
  412     /* This is our setup, thou shall not have other setups before us */
  413     uint32_t eflags = (DB_CREATE|DB_INIT_MPOOL|DB_INIT_CDB);
  414 
  415     if (rdb->db_dbenv != NULL) {
  416     rdb->db_opens++;
  417     return 0;
  418     } else {
  419     /* On first call, set backend description to something... */
  420     free(rdb->db_descr);
  421     rasprintf(&rdb->db_descr, "db%u", DB_VERSION_MAJOR);
  422     }
  423 
  424     /*
  425      * Both verify and rebuild are rather special, if for different reasons:
  426      * On rebuild we dont want to be affected by eg paniced environment, and
  427      * CDB only slows things down there. Verify is a quirky beast unlike
  428      * anything else in BDB, and does not like shared env or CDB.
  429      */
  430     if (rdb->db_flags & (RPMDB_FLAG_VERIFYONLY|RPMDB_FLAG_REBUILD)) {
  431     eflags |= DB_PRIVATE;
  432     eflags &= ~DB_INIT_CDB;
  433     }
  434 
  435     rc = db_env_create(&dbenv, 0);
  436     rc = dbapi_err(rdb, "db_env_create", rc, _debug);
  437     if (dbenv == NULL || rc)
  438     goto errxit;
  439 
  440     dbenv->set_alloc(dbenv, rmalloc, rrealloc, NULL);
  441     dbenv->set_errcall(dbenv, NULL);
  442     dbenv->set_errpfx(dbenv, _errpfx);
  443     dbenv->set_msgcall(dbenv, warnlog);
  444 
  445     /* 
  446      * These enable automatic stale lock removal. 
  447      * thread_count 8 is some kind of "magic minimum" value...
  448      */
  449     dbenv->set_thread_count(dbenv, 8);
  450     dbenv->set_isalive(dbenv, isalive);
  451 
  452     dbenv->set_verbose(dbenv, DB_VERB_DEADLOCK,
  453         (cfg->db_verbose & DB_VERB_DEADLOCK));
  454     dbenv->set_verbose(dbenv, DB_VERB_RECOVERY,
  455         (cfg->db_verbose & DB_VERB_RECOVERY));
  456     dbenv->set_verbose(dbenv, DB_VERB_WAITSFOR,
  457         (cfg->db_verbose & DB_VERB_WAITSFOR));
  458 
  459     if (cfg->db_mmapsize) {
  460     xx = dbenv->set_mp_mmapsize(dbenv, cfg->db_mmapsize);
  461     xx = dbapi_err(rdb, "dbenv->set_mp_mmapsize", xx, _debug);
  462     }
  463 
  464     if (cfg->db_cachesize) {
  465     xx = dbenv->set_cachesize(dbenv, 0, cfg->db_cachesize, 0);
  466     xx = dbapi_err(rdb, "dbenv->set_cachesize", xx, _debug);
  467     }
  468 
  469     /*
  470      * Serialize shared environment open (and clock) via fcntl() lock.
  471      * Otherwise we can end up calling dbenv->failchk() while another
  472      * process is joining the environment, leading to transient
  473      * DB_RUNRECOVER errors. Also prevents races wrt removing the
  474      * environment (eg chrooted operation). Silently fall back to
  475      * private environment on failure to allow non-privileged queries
  476      * to "work", broken as it might be.
  477      */
  478     if (!(eflags & DB_PRIVATE)) {
  479     lockfd = serialize_env(dbhome);
  480     if (lockfd < 0 && rdonly) {
  481         eflags |= DB_PRIVATE;
  482         retry_open--;
  483         rpmlog(RPMLOG_DEBUG, "serialize failed, using private dbenv\n");
  484     }
  485     }
  486 
  487     /*
  488      * Actually open the environment. Fall back to private environment
  489      * if we dont have permission to join/create shared environment or
  490      * system doesn't support it..
  491      */
  492     while (retry_open) {
  493     char *fstr = prDbiOpenFlags(eflags, 1);
  494     rpmlog(RPMLOG_DEBUG, "opening  db environment %s %s\n", dbhome, fstr);
  495     free(fstr);
  496 
  497     rc = (dbenv->open)(dbenv, dbhome, eflags, rdb->db_perms);
  498     if (rc == EINVAL && errno == rc) {
  499         eflags |= DB_PRIVATE;
  500         retry_open--;
  501     } else if (rdonly && (rc == EACCES || rc == EROFS || rc == DB_VERSION_MISMATCH)) {
  502         eflags |= DB_PRIVATE;
  503         retry_open--;
  504     } else {
  505         retry_open = 0;
  506     }
  507     }
  508     rc = dbapi_err(rdb, "dbenv->open", rc, _debug);
  509     if (rc)
  510     goto errxit;
  511 
  512     dbenv->set_errcall(dbenv, errlog);
  513 
  514     /* stale lock removal */
  515     rc = dbenv->failchk(dbenv, 0);
  516     rc = dbapi_err(rdb, "dbenv->failchk", rc, _debug);
  517     if (rc)
  518     goto errxit;
  519 
  520     rdb->db_dbenv = dbenv;
  521     rdb->db_opens = 1;
  522 
  523     if (lockfd >= 0)
  524     close(lockfd);
  525     return 0;
  526 
  527 errxit:
  528     if (dbenv) {
  529     int xx;
  530     xx = dbenv->close(dbenv, 0);
  531     xx = dbapi_err(rdb, "dbenv->close", xx, _debug);
  532     }
  533     if (lockfd >= 0)
  534     close(lockfd);
  535     return rc;
  536 }
  537 
  538 static void db3_dbSetFSync(rpmdb rdb, int enable)
  539 {
  540 #ifdef HAVE_FDATASYNC
  541     db_env_set_func_fsync(enable ? fdatasync : fsync_disable);
  542 #else
  543     db_env_set_func_fsync(enable ? fsync : fsync_disable);
  544 #endif
  545 }
  546 
  547 static int db3_Ctrl(rpmdb rdb, dbCtrlOp ctrl)
  548 {
  549     return 0;
  550 }
  551 
  552 static int dbiSync(dbiIndex dbi, unsigned int flags)
  553 {
  554     DB * db = dbi->dbi_db;
  555     int rc = 0;
  556 
  557     if (db != NULL && !dbi->cfg.dbi_no_dbsync) {
  558     rc = db->sync(db, flags);
  559     rc = cvtdberr(dbi, "db->sync", rc, _debug);
  560     }
  561     return rc;
  562 }
  563 
  564 static dbiCursor db3_dbiCursorInit(dbiIndex dbi, unsigned int flags)
  565 {
  566     dbiCursor dbc = NULL;
  567     
  568     if (dbi && dbi->dbi_db) {
  569     DB * db = dbi->dbi_db;
  570     DBC * cursor;
  571     int cflags;
  572     int rc = 0;
  573     uint32_t eflags = db_envflags(db);
  574     
  575        /* DB_WRITECURSOR requires CDB and writable db */
  576     if ((flags & DBC_WRITE) &&
  577         (eflags & DB_INIT_CDB) && !(dbi->dbi_flags & DBI_RDONLY))
  578     {
  579         cflags = DB_WRITECURSOR;
  580     } else
  581         cflags = 0;
  582 
  583     /*
  584      * Check for stale locks which could block writes "forever".
  585      * XXX: Should we also do this on reads? Reads are less likely
  586      *      to get blocked so it seems excessive...
  587      * XXX: On DB_RUNRECOVER, we should abort everything. Now
  588      *      we'll just fail to open a cursor again and again and again.
  589      */
  590     if (cflags & DB_WRITECURSOR) {
  591         DB_ENV *dbenv = db->get_env(db);
  592         rc = dbenv->failchk(dbenv, 0);
  593         rc = cvtdberr(dbi, "dbenv->failchk", rc, _debug);
  594     }
  595 
  596     if (rc == 0) {
  597         rc = db->cursor(db, NULL, &cursor, cflags);
  598         rc = cvtdberr(dbi, "db->cursor", rc, _debug);
  599     }
  600 
  601     if (rc == 0) {
  602         dbc = xcalloc(1, sizeof(*dbc));
  603         dbc->cursor = cursor;
  604         dbc->dbi = dbi;
  605         dbc->flags = flags;
  606     }
  607     }
  608 
  609     return dbc;
  610 }
  611 
  612 static dbiCursor db3_dbiCursorFree(dbiIndex dbi, dbiCursor dbc)
  613 {
  614     if (dbc) {
  615     /* Automatically sync on write-cursor close */
  616     if (dbc->flags & DBC_WRITE)
  617         dbiSync(dbc->dbi, 0);
  618     DBC * cursor = dbc->cursor;
  619     int rc = cursor->c_close(cursor);
  620     cvtdberr(dbc->dbi, "dbcursor->c_close", rc, _debug);
  621     free(dbc);
  622     }
  623     return NULL;
  624 }
  625 
  626 static int dbiCursorPut(dbiCursor dbc, DBT * key, DBT * data, unsigned int flags)
  627 {
  628     int rc = EINVAL;
  629     int sane = (key->data != NULL && key->size > 0 &&
  630         data->data != NULL && data->size > 0);
  631 
  632     if (dbc && sane) {
  633     DBC * cursor = dbc->cursor;
  634     rpmdb rdb = dbc->dbi->dbi_rpmdb;
  635     rpmswEnter(&rdb->db_putops, (ssize_t) 0);
  636 
  637     rc = cursor->c_put(cursor, key, data, DB_KEYLAST);
  638     rc = cvtdberr(dbc->dbi, "dbcursor->c_put", rc, _debug);
  639 
  640     rpmswExit(&rdb->db_putops, (ssize_t) data->size);
  641     }
  642     return rc;
  643 }
  644 
  645 static int dbiCursorGet(dbiCursor dbc, DBT * key, DBT * data, unsigned int flags)
  646 {
  647     int rc = EINVAL;
  648     int sane = ((flags == DB_NEXT) || (key->data != NULL && key->size > 0));
  649 
  650     if (dbc && sane) {
  651     DBC * cursor = dbc->cursor;
  652     rpmdb rdb = dbc->dbi->dbi_rpmdb;
  653     int _printit;
  654     rpmswEnter(&rdb->db_getops, 0);
  655 
  656     /* XXX db4 does DB_FIRST on uninitialized cursor */
  657     rc = cursor->c_get(cursor, key, data, flags);
  658     /* XXX DB_NOTFOUND can be returned */
  659     _printit = (rc == DB_NOTFOUND ? 0 : _debug);
  660     rc = cvtdberr(dbc->dbi, "dbcursor->c_get", rc, _printit);
  661 
  662     /* Remember the last key fetched */
  663     if (rc == 0) {
  664         dbc->key = key->data;
  665         dbc->keylen = key->size;
  666     } else {
  667         dbc->key = NULL;
  668         dbc->keylen = 0;
  669     }
  670 
  671     rpmswExit(&rdb->db_getops, data->size);
  672     }
  673     return rc;
  674 }
  675 
  676 static int dbiCursorDel(dbiCursor dbc, DBT * key, DBT * data, unsigned int flags)
  677 {
  678     int rc = EINVAL;
  679     int sane = (key->data != NULL && key->size > 0);
  680 
  681     if (dbc && sane) {
  682     DBC * cursor = dbc->cursor;
  683     int _printit;
  684     rpmdb rdb = dbc->dbi->dbi_rpmdb;
  685     rpmswEnter(&rdb->db_delops, 0);
  686 
  687     /* XXX TODO: insure that cursor is positioned with duplicates */
  688     rc = cursor->c_get(cursor, key, data, DB_SET);
  689     /* XXX DB_NOTFOUND can be returned */
  690     _printit = (rc == DB_NOTFOUND ? 0 : _debug);
  691     rc = cvtdberr(dbc->dbi, "dbcursor->c_get", rc, _printit);
  692 
  693     if (rc == 0) {
  694         rc = cursor->c_del(cursor, flags);
  695         rc = cvtdberr(dbc->dbi, "dbcursor->c_del", rc, _debug);
  696     }
  697     rpmswExit(&rdb->db_delops, data->size);
  698     }
  699     return rc;
  700 }
  701 
  702 static int dbiByteSwapped(dbiIndex dbi)
  703 {
  704     DB * db = dbi->dbi_db;
  705     int rc = 0;
  706 
  707     if (dbi->dbi_byteswapped != -1)
  708     return dbi->dbi_byteswapped;
  709 
  710     if (db != NULL) {
  711     int isswapped = 0;
  712     rc = db->get_byteswapped(db, &isswapped);
  713     if (rc == 0)
  714         dbi->dbi_byteswapped = rc = isswapped;
  715     }
  716     return rc;
  717 }
  718 
  719 static int db3_dbiVerify(dbiIndex dbi, unsigned int flags)
  720 {
  721     int rc = 0;
  722 
  723     if (dbi && dbi->dbi_db) {
  724     DB * db = dbi->dbi_db;
  725     
  726     rc = db->verify(db, dbi->dbi_file, NULL, NULL, flags);
  727     rc = cvtdberr(dbi, "db->verify", rc, _debug);
  728 
  729     rpmlog(RPMLOG_DEBUG, "verified db index       %s\n", dbi->dbi_file);
  730 
  731     /* db->verify() destroys the handle, make sure nobody accesses it */
  732     dbi->dbi_db = NULL;
  733     }
  734     return rc;
  735 }
  736 
  737 static int db3_dbiClose(dbiIndex dbi, unsigned int flags)
  738 {
  739     rpmdb rdb = dbi->dbi_rpmdb;
  740     const char * dbhome = rpmdbHome(rdb);
  741     DB * db = dbi->dbi_db;
  742     int _printit;
  743     int rc = 0;
  744 
  745     if (db) {
  746     rc = db->close(db, flags);
  747     /* XXX ignore not found error messages. */
  748     _printit = (rc == ENOENT ? 0 : _debug);
  749     rc = cvtdberr(dbi, "db->close", rc, _printit);
  750     db = dbi->dbi_db = NULL;
  751 
  752     rpmlog(RPMLOG_DEBUG, "closed   db index       %s/%s\n",
  753         dbhome, dbi->dbi_file);
  754     }
  755 
  756     db_fini(rdb, dbhome ? dbhome : "");
  757 
  758     dbi->dbi_db = NULL;
  759 
  760     dbi = dbiFree(dbi);
  761 
  762     return rc;
  763 }
  764 
  765 /*
  766  * Lock a file using fcntl(2). Traditionally this is Packages,
  767  * the file used to store metadata of installed header(s),
  768  * as Packages is always opened, and should be opened first,
  769  * for any rpmdb access.
  770  *
  771  * If no DBENV is used, then access is protected with a
  772  * shared/exclusive locking scheme, as always.
  773  *
  774  * With a DBENV, the fcntl(2) lock is necessary only to keep
  775  * the riff-raff from playing where they don't belong, as
  776  * the DBENV should provide it's own locking scheme. So try to
  777  * acquire a lock, but permit failures, as some other
  778  * DBENV player may already have acquired the lock.
  779  *
  780  * With NPTL posix mutexes, revert to fcntl lock on non-functioning
  781  * glibc/kernel combinations.
  782  */
  783 static int dbiFlock(dbiIndex dbi, int mode)
  784 {
  785     int fdno = -1;
  786     int rc = 0;
  787     DB * db = dbi->dbi_db;
  788 
  789     if (!(db->fd(db, &fdno) == 0 && fdno >= 0)) {
  790     rc = 1;
  791     } else {
  792     const char *dbhome = rpmdbHome(dbi->dbi_rpmdb);
  793     struct flock l;
  794     memset(&l, 0, sizeof(l));
  795     l.l_whence = 0;
  796     l.l_start = 0;
  797     l.l_len = 0;
  798     l.l_type = (mode & O_ACCMODE) == O_RDONLY
  799             ? F_RDLCK : F_WRLCK;
  800     l.l_pid = 0;
  801 
  802     rc = fcntl(fdno, F_SETLK, (void *) &l);
  803     if (rc) {
  804         uint32_t eflags = db_envflags(db);
  805         /* Warning iff using non-private CDB locking. */
  806         rc = (((eflags & DB_INIT_CDB) && !(eflags & DB_PRIVATE)) ? 0 : 1);
  807         rpmlog( (rc ? RPMLOG_ERR : RPMLOG_WARNING),
  808             _("cannot get %s lock on %s/%s\n"),
  809             ((mode & O_ACCMODE) == O_RDONLY)
  810                 ? _("shared") : _("exclusive"),
  811             dbhome, dbi->dbi_file);
  812     } else {
  813         rpmlog(RPMLOG_DEBUG,
  814             "locked   db index       %s/%s\n",
  815             dbhome, dbi->dbi_file);
  816     }
  817     }
  818     return rc;
  819 }
  820 
  821 static int db3_dbiOpen(rpmdb rdb, rpmDbiTagVal rpmtag, dbiIndex * dbip, int flags)
  822 {
  823     const char *dbhome = rpmdbHome(rdb);
  824     dbiIndex dbi = NULL;
  825     int rc = 0;
  826     int retry_open;
  827     int verifyonly = (flags & RPMDB_FLAG_VERIFYONLY);
  828 
  829     DB * db = NULL;
  830     DBTYPE dbtype = DB_UNKNOWN;
  831     uint32_t oflags;
  832     static int _lockdbfd = 0;
  833 
  834     if (dbip)
  835     *dbip = NULL;
  836 
  837     if ((dbi = dbiNew(rdb, rpmtag)) == NULL)
  838     return 1;
  839 
  840     /*
  841      * Parse db configuration parameters.
  842      */
  843     dbConfigure(rpmtag, rdb->db_dbenv == NULL ? &rdb->cfg : NULL, &dbi->cfg);
  844 
  845     /*
  846      * Map open mode flags onto configured database/environment flags.
  847      */
  848     oflags = dbi->cfg.dbi_oflags;
  849     if ((rdb->db_mode & O_ACCMODE) == O_RDONLY)
  850     oflags |= DB_RDONLY;
  851 
  852     rc = db_init(rdb, dbhome);
  853 
  854     retry_open = (rc == 0) ? 2 : 0;
  855 
  856     while (retry_open) {
  857     rc = db_create(&db, rdb->db_dbenv, 0);
  858     rc = cvtdberr(dbi, "db_create", rc, _debug);
  859 
  860     /* For verify we only want the handle, not an open db */
  861     if (verifyonly)
  862         break;
  863 
  864     if (rc == 0 && db != NULL) {
  865         int _printit;
  866         char *dbfs = prDbiOpenFlags(oflags, 0);
  867         rpmlog(RPMLOG_DEBUG, "opening  db index       %s/%s %s mode=0x%x\n",
  868             dbhome, dbi->dbi_file, dbfs, rdb->db_mode);
  869         free(dbfs);
  870 
  871         rc = (db->open)(db, NULL, dbi->dbi_file, NULL,
  872                 dbtype, oflags, rdb->db_perms);
  873 
  874         /* Attempt to create if missing, discarding DB_RDONLY (!) */
  875         if (rc == ENOENT) {
  876         oflags |= DB_CREATE;
  877         oflags &= ~DB_RDONLY;
  878         dbtype = (rpmtag == RPMDBI_PACKAGES) ?  DB_HASH : DB_BTREE;
  879         retry_open--;
  880         } else {
  881         retry_open = 0;
  882         }
  883 
  884         /* XXX return rc == errno without printing */
  885         _printit = (rc > 0 ? 0 : _debug);
  886         rc = cvtdberr(dbi, "db->open", rc, _printit);
  887 
  888         /* Validate the index type is something we can support */
  889         if ((rc == 0) && (dbtype == DB_UNKNOWN)) {
  890         db->get_type(db, &dbtype);
  891         if (dbtype != DB_HASH && dbtype != DB_BTREE) {
  892             rpmlog(RPMLOG_ERR, _("invalid index type %x on %s/%s\n"),
  893                 dbtype, dbhome, dbi->dbi_file);
  894             rc = 1;
  895         }
  896         }
  897 
  898         if (rc != 0) {
  899         db->close(db, 0);
  900         db = NULL;
  901         }
  902     }
  903     }
  904 
  905     dbi->dbi_db = db;
  906 
  907     dbi->dbi_flags = 0;
  908     if (oflags & DB_CREATE)
  909     dbi->dbi_flags |= DBI_CREATED;
  910     if (oflags & DB_RDONLY)
  911     dbi->dbi_flags |= DBI_RDONLY;
  912 
  913     if (!verifyonly && rc == 0 && dbi->cfg.dbi_lockdbfd && _lockdbfd++ == 0) {
  914     rc = dbiFlock(dbi, rdb->db_mode);
  915     }
  916 
  917     if (rc == 0 && dbi->dbi_db != NULL && dbip != NULL) {
  918     *dbip = dbi;
  919     } else {
  920     (void) dbiClose(dbi, 0);
  921     }
  922 
  923     return rc;
  924 }
  925 
  926 /**
  927  * Convert retrieved data to index set.
  928  * @param dbi       index database handle
  929  * @param data      retrieved data
  930  * @retval setp     (malloc'ed) index set
  931  * @return      0 on success
  932  */
  933 static int dbt2set(dbiIndex dbi, DBT * data, dbiIndexSet * setp)
  934 {
  935     int _dbbyteswapped = dbiByteSwapped(dbi);
  936     const char * sdbir;
  937     dbiIndexSet set;
  938     unsigned int i;
  939 
  940     if (dbi == NULL || data == NULL || setp == NULL)
  941     return -1;
  942 
  943     if ((sdbir = data->data) == NULL) {
  944     *setp = NULL;
  945     return 0;
  946     }
  947 
  948     set = dbiIndexSetNew(data->size / (2 * sizeof(int32_t)));
  949     set->count = data->size / (2 * sizeof(int32_t));
  950 
  951     for (i = 0; i < set->count; i++) {
  952     union _dbswap hdrNum, tagNum;
  953 
  954     memcpy(&hdrNum.ui, sdbir, sizeof(hdrNum.ui));
  955     sdbir += sizeof(hdrNum.ui);
  956     memcpy(&tagNum.ui, sdbir, sizeof(tagNum.ui));
  957     sdbir += sizeof(tagNum.ui);
  958     if (_dbbyteswapped) {
  959         _DBSWAP(hdrNum);
  960         _DBSWAP(tagNum);
  961     }
  962     set->recs[i].hdrNum = hdrNum.ui;
  963     set->recs[i].tagNum = tagNum.ui;
  964     }
  965     *setp = set;
  966     return 0;
  967 }
  968 
  969 /**
  970  * Convert index set to database representation.
  971  * @param dbi       index database handle
  972  * @param data      retrieved data
  973  * @param set       index set
  974  * @return      0 on success
  975  */
  976 static int set2dbt(dbiIndex dbi, DBT * data, dbiIndexSet set)
  977 {
  978     int _dbbyteswapped = dbiByteSwapped(dbi);
  979     char * tdbir;
  980     unsigned int i;
  981 
  982     if (dbi == NULL || data == NULL || set == NULL)
  983     return -1;
  984 
  985     data->size = set->count * (2 * sizeof(int32_t));
  986     if (data->size == 0) {
  987     data->data = NULL;
  988     return 0;
  989     }
  990     tdbir = data->data = xmalloc(data->size);
  991 
  992     for (i = 0; i < set->count; i++) {
  993     union _dbswap hdrNum, tagNum;
  994 
  995     memset(&hdrNum, 0, sizeof(hdrNum));
  996     memset(&tagNum, 0, sizeof(tagNum));
  997     hdrNum.ui = set->recs[i].hdrNum;
  998     tagNum.ui = set->recs[i].tagNum;
  999     if (_dbbyteswapped) {
 1000         _DBSWAP(hdrNum);
 1001         _DBSWAP(tagNum);
 1002     }
 1003     memcpy(tdbir, &hdrNum.ui, sizeof(hdrNum.ui));
 1004     tdbir += sizeof(hdrNum.ui);
 1005     memcpy(tdbir, &tagNum.ui, sizeof(tagNum.ui));
 1006     tdbir += sizeof(tagNum.ui);
 1007     }
 1008     return 0;
 1009 }
 1010 
 1011 static rpmRC db3_idxdbGet(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen,
 1012               dbiIndexSet *set, int searchType)
 1013 {
 1014     rpmRC rc = RPMRC_FAIL; /* assume failure */
 1015     if (dbi != NULL && dbc != NULL && set != NULL) {
 1016     int cflags = DB_NEXT;
 1017     int dbrc;
 1018     DBT data, key;
 1019     memset(&data, 0, sizeof(data));
 1020     memset(&key, 0, sizeof(key));
 1021 
 1022     if (keyp) {
 1023         if (keylen == 0) {      /* XXX "/" fixup */ 
 1024         keyp = "";
 1025         keylen = 1;
 1026         }
 1027         key.data = (void *) keyp; /* discards const */
 1028         key.size = keylen;
 1029         cflags = searchType == DBC_PREFIX_SEARCH ? DB_SET_RANGE : DB_SET;
 1030     }
 1031 
 1032     for (;;) {
 1033         dbiIndexSet newset = NULL;
 1034         dbrc = dbiCursorGet(dbc, &key, &data, cflags);
 1035         if (dbrc != 0)
 1036         break;
 1037         if (searchType == DBC_PREFIX_SEARCH &&
 1038             (key.size < keylen || memcmp(key.data, keyp, keylen) != 0))
 1039         break;
 1040         dbt2set(dbi, &data, &newset);
 1041         if (*set == NULL) {
 1042         *set = newset;
 1043         } else {
 1044         dbiIndexSetAppendSet(*set, newset, 0);
 1045         dbiIndexSetFree(newset);
 1046         }
 1047         if (searchType != DBC_PREFIX_SEARCH)
 1048         break;
 1049         key.data = NULL;
 1050         key.size = 0;
 1051         cflags = DB_NEXT;
 1052     }
 1053 
 1054     /* fixup result status for prefix search */
 1055     if (searchType == DBC_PREFIX_SEARCH) {
 1056         if (dbrc == DB_NOTFOUND && *set != NULL && (*set)->count > 0)
 1057         dbrc = 0;
 1058         else if (dbrc == 0 && (*set == NULL || (*set)->count == 0))
 1059         dbrc = DB_NOTFOUND;
 1060     }
 1061 
 1062     if (dbrc == 0) {
 1063         rc = RPMRC_OK;
 1064     } else if (dbrc == DB_NOTFOUND) {
 1065         rc = RPMRC_NOTFOUND;
 1066     } else {
 1067         rpmlog(RPMLOG_ERR,
 1068            _("error(%d) getting \"%s\" records from %s index: %s\n"),
 1069            dbrc, keyp ? keyp : "???", dbiName(dbi), db_strerror(dbrc));
 1070     }
 1071     }
 1072     return rc;
 1073 }
 1074 
 1075 /* Update secondary index. NULL set deletes the key */
 1076 static rpmRC updateIndex(dbiCursor dbc, const char *keyp, unsigned int keylen,
 1077              dbiIndexSet set)
 1078 {
 1079     rpmRC rc = RPMRC_FAIL;
 1080 
 1081     if (dbc && keyp) {
 1082     dbiIndex dbi = dbc->dbi;
 1083     int dbrc;
 1084     DBT data, key;
 1085     memset(&key, 0, sizeof(data));
 1086     memset(&data, 0, sizeof(data));
 1087 
 1088     key.data = (void *) keyp; /* discards const */
 1089     key.size = keylen;
 1090 
 1091     if (set)
 1092         set2dbt(dbi, &data, set);
 1093 
 1094     if (dbiIndexSetCount(set) > 0) {
 1095         dbrc = dbiCursorPut(dbc, &key, &data, DB_KEYLAST);
 1096         if (dbrc) {
 1097         rpmlog(RPMLOG_ERR,
 1098                _("error(%d) storing record \"%s\" into %s\n"),
 1099                dbrc, (char*)key.data, dbiName(dbi));
 1100         }
 1101         free(data.data);
 1102     } else {
 1103         dbrc = dbiCursorDel(dbc, &key, &data, 0);
 1104         if (dbrc) {
 1105         rpmlog(RPMLOG_ERR,
 1106                _("error(%d) removing record \"%s\" from %s\n"),
 1107                dbrc, (char*)key.data, dbiName(dbi));
 1108         }
 1109     }
 1110 
 1111     if (dbrc == 0)
 1112         rc = RPMRC_OK;
 1113     }
 1114 
 1115     return rc;
 1116 }
 1117 
 1118 static rpmRC db3_idxdbPut(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen,
 1119            dbiIndexItem rec)
 1120 {
 1121     dbiIndexSet set = NULL;
 1122     rpmRC rc;
 1123 
 1124     if (keyp && keylen == 0) {      /* XXX "/" fixup */
 1125     keyp = "";
 1126     keylen++;
 1127     }
 1128     rc = idxdbGet(dbi, dbc, keyp, keylen, &set, DBC_NORMAL_SEARCH);
 1129 
 1130     /* Not found means a new key and is not an error. */
 1131     if (rc && rc != RPMRC_NOTFOUND)
 1132     return rc;
 1133 
 1134     if (set == NULL)
 1135     set = dbiIndexSetNew(1);
 1136     dbiIndexSetAppend(set, rec, 1, 0);
 1137 
 1138     rc = updateIndex(dbc, keyp, keylen, set);
 1139 
 1140     dbiIndexSetFree(set);
 1141     return rc;
 1142 }
 1143 
 1144 static rpmRC db3_idxdbDel(dbiIndex dbi, dbiCursor dbc, const char *keyp, size_t keylen,
 1145            dbiIndexItem rec)
 1146 {
 1147     dbiIndexSet set = NULL;
 1148     rpmRC rc;
 1149 
 1150     if (keyp && keylen == 0) {      /* XXX "/" fixup */
 1151     keyp = "";
 1152     keylen++;
 1153     }
 1154     rc = idxdbGet(dbi, dbc, keyp, keylen, &set, DBC_NORMAL_SEARCH);
 1155     if (rc)
 1156     return rc;
 1157 
 1158     if (dbiIndexSetPrune(set, rec, 1, 1)) {
 1159     /* Nothing was pruned. XXX: Can this actually happen? */
 1160     rc = RPMRC_OK;
 1161     } else {
 1162     /* If there's data left, update data. Otherwise delete the key. */
 1163     if (dbiIndexSetCount(set) > 0) {
 1164         rc = updateIndex(dbc, keyp, keylen, set);
 1165     } else {
 1166         rc = updateIndex(dbc, keyp, keylen, NULL);
 1167     }
 1168     };
 1169     dbiIndexSetFree(set);
 1170 
 1171     return rc;
 1172 }
 1173 
 1174 static const void * db3_idxdbKey(dbiIndex dbi, dbiCursor dbc, unsigned int *keylen)
 1175 {
 1176     const void *key = NULL;
 1177     if (dbc) {
 1178     key = dbc->key;
 1179     if (key && keylen)
 1180         *keylen = dbc->keylen;
 1181     }
 1182     return key;
 1183 }
 1184 
 1185 
 1186 /* Update primary Packages index. NULL hdr means remove */
 1187 static rpmRC updatePackages(dbiCursor dbc, unsigned int hdrNum, DBT *hdr)
 1188 {
 1189     union _dbswap mi_offset;
 1190     int rc = 0;
 1191     DBT key;
 1192 
 1193     if (dbc == NULL || hdrNum == 0)
 1194     return RPMRC_FAIL;
 1195 
 1196     memset(&key, 0, sizeof(key));
 1197 
 1198     mi_offset.ui = hdrNum;
 1199     if (dbiByteSwapped(dbc->dbi) == 1)
 1200     _DBSWAP(mi_offset);
 1201     key.data = (void *) &mi_offset;
 1202     key.size = sizeof(mi_offset.ui);
 1203 
 1204     if (hdr) {
 1205     rc = dbiCursorPut(dbc, &key, hdr, DB_KEYLAST);
 1206     if (rc) {
 1207         rpmlog(RPMLOG_ERR,
 1208            _("error(%d) adding header #%d record\n"), rc, hdrNum);
 1209     }
 1210     } else {
 1211     DBT data;
 1212 
 1213     memset(&data, 0, sizeof(data));
 1214     rc = dbiCursorGet(dbc, &key, &data, DB_SET);
 1215     if (rc) {
 1216         rpmlog(RPMLOG_ERR,
 1217            _("error(%d) removing header #%d record\n"), rc, hdrNum);
 1218     } else
 1219         rc = dbiCursorDel(dbc, &key, &data, 0);
 1220     }
 1221 
 1222     return rc == 0 ? RPMRC_OK : RPMRC_FAIL;
 1223 }
 1224 
 1225 /* Get current header instance number or try to allocate a new one */
 1226 static unsigned int pkgInstance(dbiIndex dbi, int alloc)
 1227 {
 1228     unsigned int hdrNum = 0;
 1229 
 1230     if (dbi != NULL && dbi->dbi_type == DBI_PRIMARY) {
 1231     dbiCursor dbc;
 1232     DBT key, data;
 1233     unsigned int firstkey = 0;
 1234     union _dbswap mi_offset;
 1235     int ret;
 1236 
 1237     memset(&key, 0, sizeof(key));
 1238     memset(&data, 0, sizeof(data));
 1239 
 1240     dbc = dbiCursorInit(dbi, alloc ? DBC_WRITE : 0);
 1241 
 1242     /* Key 0 holds the current largest instance, fetch it */
 1243     key.data = &firstkey;
 1244     key.size = sizeof(firstkey);
 1245     ret = dbiCursorGet(dbc, &key, &data, DB_SET);
 1246 
 1247     if (ret == 0 && data.data) {
 1248         memcpy(&mi_offset, data.data, sizeof(mi_offset.ui));
 1249         if (dbiByteSwapped(dbi) == 1)
 1250         _DBSWAP(mi_offset);
 1251         hdrNum = mi_offset.ui;
 1252     }
 1253 
 1254     if (alloc) {
 1255         /* Rather complicated "increment by one", bswapping as needed */
 1256         ++hdrNum;
 1257         mi_offset.ui = hdrNum;
 1258         if (dbiByteSwapped(dbi) == 1)
 1259         _DBSWAP(mi_offset);
 1260         if (ret == 0 && data.data) {
 1261         memcpy(data.data, &mi_offset, sizeof(mi_offset.ui));
 1262         } else {
 1263         data.data = &mi_offset;
 1264         data.size = sizeof(mi_offset.ui);
 1265         }
 1266 
 1267         /* Unless we manage to insert the new instance number, we failed */
 1268         ret = dbiCursorPut(dbc, &key, &data, DB_KEYLAST);
 1269         if (ret) {
 1270         hdrNum = 0;
 1271         rpmlog(RPMLOG_ERR,
 1272             _("error(%d) allocating new package instance\n"), ret);
 1273         }
 1274     }
 1275     dbiCursorFree(dbi, dbc);
 1276     }
 1277     
 1278     return hdrNum;
 1279 }
 1280 
 1281 static rpmRC db3_pkgdbPut(dbiIndex dbi, dbiCursor dbc,  unsigned int hdrNum,
 1282                unsigned char *hdrBlob, unsigned int hdrLen)
 1283 {
 1284     DBT hdr;
 1285     memset(&hdr, 0, sizeof(hdr));
 1286     hdr.data = hdrBlob;
 1287     hdr.size = hdrLen;
 1288     return updatePackages(dbc, hdrNum, &hdr);
 1289 }
 1290 
 1291 static rpmRC db3_pkgdbDel(dbiIndex dbi, dbiCursor dbc,  unsigned int hdrNum)
 1292 {
 1293     return updatePackages(dbc, hdrNum, NULL);
 1294 }
 1295 
 1296 static rpmRC db3_pkgdbGet(dbiIndex dbi, dbiCursor dbc, unsigned int hdrNum,
 1297          unsigned char **hdrBlob, unsigned int *hdrLen)
 1298 {
 1299     DBT key, data;
 1300     union _dbswap mi_offset;
 1301     int rc;
 1302 
 1303     if (dbc == NULL)
 1304     return RPMRC_FAIL;
 1305 
 1306     memset(&key, 0, sizeof(key));
 1307     memset(&data, 0, sizeof(data));
 1308 
 1309     if (hdrNum) {
 1310     mi_offset.ui = hdrNum;
 1311     if (dbiByteSwapped(dbc->dbi) == 1)
 1312         _DBSWAP(mi_offset);
 1313     key.data = (void *) &mi_offset;
 1314     key.size = sizeof(mi_offset.ui);
 1315     }
 1316 
 1317 #if !defined(_USE_COPY_LOAD)
 1318     data.flags |= DB_DBT_MALLOC;
 1319 #endif
 1320     rc = dbiCursorGet(dbc, &key, &data, hdrNum ? DB_SET : DB_NEXT);
 1321     if (rc == 0) {
 1322     if (hdrBlob)
 1323         *hdrBlob = data.data;
 1324     if (hdrLen)
 1325         *hdrLen = data.size;
 1326     return RPMRC_OK;
 1327     } else if (rc == DB_NOTFOUND)
 1328     return RPMRC_NOTFOUND;
 1329     else
 1330     return RPMRC_FAIL;
 1331 }
 1332 
 1333 static unsigned int db3_pkgdbKey(dbiIndex dbi, dbiCursor dbc)
 1334 {
 1335     union _dbswap mi_offset;
 1336 
 1337     if (dbc == NULL || dbc->key == NULL)
 1338     return 0;
 1339     memcpy(&mi_offset, dbc->key, sizeof(mi_offset.ui));
 1340     if (dbiByteSwapped(dbc->dbi) == 1)
 1341     _DBSWAP(mi_offset);
 1342     return mi_offset.ui;
 1343 }
 1344 
 1345 static rpmRC db3_pkgdbNew(dbiIndex dbi, dbiCursor dbc, unsigned int *hdrNum)
 1346 {
 1347     unsigned int num;
 1348     if (dbc == NULL)
 1349     return RPMRC_FAIL;
 1350     num = pkgInstance(dbc->dbi, 1);
 1351     if (!num)
 1352     return RPMRC_FAIL;
 1353     *hdrNum = num;
 1354     return RPMRC_OK;
 1355 }
 1356 
 1357 struct rpmdbOps_s db3_dbops = {
 1358     .open   = db3_dbiOpen,
 1359     .close  = db3_dbiClose,
 1360     .verify = db3_dbiVerify,
 1361 
 1362     .setFSync = db3_dbSetFSync,
 1363     .ctrl = db3_Ctrl,
 1364 
 1365     .cursorInit = db3_dbiCursorInit,
 1366     .cursorFree = db3_dbiCursorFree,
 1367 
 1368     .pkgdbGet = db3_pkgdbGet,
 1369     .pkgdbPut = db3_pkgdbPut,
 1370     .pkgdbDel = db3_pkgdbDel,
 1371     .pkgdbNew = db3_pkgdbNew,
 1372     .pkgdbKey = db3_pkgdbKey,
 1373 
 1374     .idxdbGet = db3_idxdbGet,
 1375     .idxdbPut = db3_idxdbPut,
 1376     .idxdbDel = db3_idxdbDel,
 1377     .idxdbKey = db3_idxdbKey
 1378 };
 1379