rpm 5.3.7

rpmdb/rpmdb.c

Go to the documentation of this file.
00001 
00005 #include "system.h"
00006 
00007 #include <sys/file.h>
00008 
00009 #include <rpmiotypes.h>
00010 #include <rpmlog.h>
00011 #include <rpmpgp.h>
00012 #include <rpmurl.h>
00013 #include <rpmhash.h>            /* hashFunctionString */
00014 #define _MIRE_INTERNAL
00015 #include <rpmmacro.h>
00016 #include <rpmsq.h>
00017 #include <rpmsx.h>
00018 #include <argv.h>
00019 
00020 #define _RPMBF_INTERNAL
00021 #include <rpmbf.h>
00022 
00023 #include <rpmtypes.h>
00024 #define _RPMTAG_INTERNAL
00025 #include "header_internal.h"    /* XXX for HEADERFLAG_MAPPED */
00026 
00027 #define _RPMDB_INTERNAL
00028 #include "rpmdb.h"
00029 #include "pkgio.h"
00030 #include "fprint.h"
00031 #include "legacy.h"
00032 
00033 #include "debug.h"
00034 
00035 #if defined(__LCLINT__)
00036 #define UINT32_T        u_int32_t
00037 #else
00038 #define UINT32_T        rpmuint32_t
00039 #endif
00040 
00041 /* XXX retrofit the *BSD typedef for the deprived. */
00042 #if defined(__QNXNTO__)
00043 typedef rpmuint32_t     u_int32_t;
00044 #endif
00045 
00046 /*@access dbiIndexSet@*/
00047 /*@access dbiIndexItem@*/
00048 /*@access miRE@*/
00049 /*@access Header@*/             /* XXX compared with NULL */
00050 /*@access rpmmi@*/
00051 /*@access rpmts@*/              /* XXX compared with NULL */
00052 
00053 /*@unchecked@*/
00054 int _rpmdb_debug = 0;
00055 
00056 /*@unchecked@*/
00057 int _rpmmi_debug = 0;
00058 
00059 #define _DBI_FLAGS      0
00060 #define _DBI_PERMS      0644
00061 #define _DBI_MAJOR      -1
00062 
00069 static size_t dbiTagToDbix(rpmdb db, rpmTag tag)
00070         /*@*/
00071 {
00072     size_t dbix;
00073 
00074     if (db->db_tags != NULL)
00075     for (dbix = 0; dbix < db->db_ndbi; dbix++) {
00076         if (tag != db->db_tags[dbix].tag)
00077             continue;
00078         return dbix;
00079     }
00080     return 0xffffffff;
00081 }
00082 
00086 /*@-exportheader@*/
00087 static void dbiTagsInit(/*@null@*/ tagStore_t * dbiTagsP,
00088                 /*@null@*/ size_t * dbiNTagsP)
00089         /*@globals rpmGlobalMacroContext, h_errno, internalState @*/
00090         /*@modifies *dbiTagsP, *dbiNTagsP, rpmGlobalMacroContext, internalState @*/
00091 {
00092 /*@observer@*/
00093     static const char * const _dbiTagStr_default =
00094         "Packages:Name:Basenames:Group:Requirename:Providename:Conflictname:Triggername:Dirnames:Requireversion:Provideversion:Installtid:Sigmd5:Sha1header:Filedigests:Depends:Pubkeys";
00095     tagStore_t dbiTags = NULL;
00096     size_t dbiNTags = 0;
00097     char * dbiTagStr = NULL;
00098     char * o, * oe;
00099     rpmTag tag;
00100     size_t dbix;
00101     int bingo;
00102 
00103     dbiTagStr = rpmExpand("%{?_dbi_tags}", NULL);
00104     if (!(dbiTagStr && *dbiTagStr)) {
00105         dbiTagStr = _free(dbiTagStr);
00106         dbiTagStr = xstrdup(_dbiTagStr_default);
00107     }
00108 
00109 #ifdef  NOISY
00110 if (_rpmdb_debug)
00111 fprintf(stderr, "--> %s(%p, %p) dbiTagStr %s\n", __FUNCTION__, dbiTagsP, dbiNTagsP, dbiTagStr);
00112 #endif
00113     /* Always allocate package index */
00114     dbiTags = xcalloc(1, sizeof(*dbiTags));
00115     dbiTags[dbiNTags].str = xstrdup("Packages");
00116     dbiTags[dbiNTags].tag = RPMDBI_PACKAGES;
00117     dbiTags[dbiNTags].iob = NULL;
00118     dbiNTags++;
00119 
00120     for (o = dbiTagStr; o && *o; o = oe) {
00121         while (*o && xisspace((int)*o))
00122             o++;
00123         if (*o == '\0')
00124             break;
00125         for (oe = o; oe && *oe; oe++) {
00126             if (xisspace((int)*oe))
00127                 /*@innerbreak@*/ break;
00128             if (oe[0] == ':' && !(oe[1] == '/' && oe[2] == '/'))
00129                 /*@innerbreak@*/ break;
00130         }
00131         if (oe && *oe)
00132             *oe++ = '\0';
00133         tag = tagValue(o);
00134 
00135         bingo = 0;
00136         if (dbiTags != NULL)
00137         for (dbix = 0; dbix < dbiNTags; dbix++) {
00138             if (tag == dbiTags[dbix].tag) {
00139                 bingo = 1;
00140                 /*@innerbreak@*/ break;
00141             }
00142         }
00143         if (bingo)
00144             continue;
00145 
00146         dbiTags = xrealloc(dbiTags, (dbiNTags + 1) * sizeof(*dbiTags));
00147         dbiTags[dbiNTags].str = xstrdup(o);
00148         dbiTags[dbiNTags].tag = tag;
00149         dbiTags[dbiNTags].iob = NULL;
00150 #ifdef  NOISY
00151 if (_rpmdb_debug) {
00152 fprintf(stderr, "\t%u %s(", (unsigned)dbiNTags, o);
00153 if (tag & 0x40000000)
00154     fprintf(stderr, "0x%x)\n", tag);
00155 else
00156     fprintf(stderr, "%d)\n", tag);
00157 }
00158 #endif
00159         dbiNTags++;
00160     }
00161 
00162     if (dbiNTagsP != NULL)
00163         *dbiNTagsP = dbiNTags;
00164     if (dbiTagsP != NULL)
00165         *dbiTagsP = dbiTags;
00166     else
00167         dbiTags = tagStoreFree(dbiTags, dbiNTags);
00168     dbiTagStr = _free(dbiTagStr);
00169 }
00170 /*@=exportheader@*/
00171 
00172 /*@-redecl@*/
00173 #define DB1vec          NULL
00174 #define DB2vec          NULL
00175 
00176 #if defined(WITH_DB)
00177 /*@-exportheadervar -declundef @*/
00178 /*@observer@*/ /*@unchecked@*/
00179 extern struct _dbiVec db3vec;
00180 /*@=exportheadervar =declundef @*/
00181 #define DB3vec          &db3vec
00182 /*@=redecl@*/
00183 #else
00184 #define DB3vec          NULL
00185 #endif
00186 
00187 #ifdef HAVE_SQLITE3_H
00188 #define SQLITE_HACK
00189 /*@-exportheadervar -declundef @*/
00190 /*@observer@*/ /*@unchecked@*/
00191 extern struct _dbiVec sqlitevec;
00192 /*@=exportheadervar =declundef @*/
00193 #define SQLITEvec       &sqlitevec
00194 /*@=redecl@*/
00195 #else
00196 #define SQLITEvec       NULL
00197 #endif
00198 
00199 /*@-nullassign@*/
00200 /*@observer@*/ /*@unchecked@*/
00201 static struct _dbiVec *mydbvecs[] = {
00202     DB1vec, DB1vec, DB2vec, DB3vec, SQLITEvec, NULL
00203 };
00204 /*@=nullassign@*/
00205 
00206 static inline int checkfd(const char * devnull, int fdno, int flags)
00207         /*@*/
00208 {
00209     struct stat sb;
00210     int ret = 0;
00211 
00212     if (fstat(fdno, &sb) == -1 && errno == EBADF)
00213         ret = (open(devnull, flags) == fdno) ? 1 : 2;
00214     return ret;
00215 }
00216 
00217 dbiIndex dbiOpen(rpmdb db, rpmTag tag, /*@unused@*/ unsigned int flags)
00218 {
00219     static int _oneshot = 0;
00220     size_t dbix;
00221     dbiIndex dbi = NULL;
00222     int _dbapi;
00223     int rc = 0;
00224 
00225     /* Insure that stdin/stdout/stderr are open, lest stderr end up in rpmdb. */
00226    if (!_oneshot) {
00227         static const char _devnull[] = "/dev/null";
00228 /*@-noeffect@*/
00229 #if defined(STDIN_FILENO)
00230         (void) checkfd(_devnull, STDIN_FILENO, O_RDONLY);
00231 #endif
00232 #if defined(STDOUT_FILENO)
00233         (void) checkfd(_devnull, STDOUT_FILENO, O_WRONLY);
00234 #endif
00235 #if defined(STDERR_FILENO)
00236         (void) checkfd(_devnull, STDERR_FILENO, O_WRONLY);
00237 #endif
00238 /*@=noeffect@*/
00239         _oneshot++;
00240    }
00241 
00242 assert(db != NULL);                                     /* XXX sanity */
00243 assert(db->_dbi != NULL);                               /* XXX sanity */
00244 
00245     /* Is this index configured? */
00246     dbix = dbiTagToDbix(db, tag);
00247     if (dbix >= db->db_ndbi)
00248         goto exit;
00249 
00250     /* Is this index already open ? */
00251     if ((dbi = db->_dbi[dbix]) != NULL)
00252         goto exit;
00253 
00254     _dbapi = db->db_api;
00255 assert(_dbapi == 3 || _dbapi == 4);
00256 assert(mydbvecs[_dbapi] != NULL);
00257 
00258     rc = (*mydbvecs[_dbapi]->open) (db, tag, &dbi);
00259     if (rc) {
00260         static uint8_t _printed[128];
00261         if (!_printed[dbix & 0x1f]++)
00262             rpmlog(RPMLOG_ERR,
00263                 _("cannot open %s(%u) index: %s(%d)\n\tDB: %s\n"),
00264                 tagName(tag), tag,
00265                 (rc > 0 ? strerror(rc) : ""), rc,
00266                 ((mydbvecs[_dbapi]->dbv_version != NULL)
00267                 ? mydbvecs[_dbapi]->dbv_version : "unknown"));
00268         dbi = db3Free(dbi);
00269         goto exit;
00270     }
00271     db->_dbi[dbix] = dbi;
00272 
00273 exit:
00274 
00275 /*@-modfilesys@*/
00276 if (_rpmdb_debug)
00277 fprintf(stderr, "<== dbiOpen(%p, %s(%u), 0x%x) dbi %p = %p[%u:%u]\n", db, tagName(tag), tag, flags, dbi, db->_dbi, (unsigned)dbix, (unsigned)db->db_ndbi);
00278 /*@=modfilesys@*/
00279 
00280 /*@-compdef -nullstate@*/ /* FIX: db->_dbi may be NULL */
00281     return dbi;
00282 /*@=compdef =nullstate@*/
00283 }
00284 
00285 /*@-redef@*/
00286 union _dbswap {
00287     uint64_t ul;
00288     uint32_t ui;
00289     uint16_t us;
00290     uint8_t uc[8];
00291 };
00292 /*@=redef@*/
00293 
00294 /*@unchecked@*/
00295 static union _dbswap _endian = { .ui = 0x11223344 };
00296 
00297 static inline uint64_t _ntoh_ul(uint64_t ul)
00298         /*@*/
00299 {
00300     union _dbswap _a;
00301     _a.ul = ul;
00302     if (_endian.uc[0] == 0x44) {
00303         uint8_t _b, *_c = _a.uc; \
00304         _b = _c[7]; _c[7] = _c[0]; _c[0] = _b; \
00305         _b = _c[6]; _c[6] = _c[1]; _c[1] = _b; \
00306         _b = _c[5]; _c[5] = _c[2]; _c[2] = _b; \
00307         _b = _c[4]; _c[4] = _c[3]; _c[3] = _b; \
00308     }
00309     return _a.ul;
00310 }
00311 static inline uint64_t _hton_ul(uint64_t ul)
00312         /*@*/
00313 {
00314     return _ntoh_ul(ul);
00315 }
00316 
00317 static inline uint32_t _ntoh_ui(uint32_t ui)
00318         /*@*/
00319 {
00320     union _dbswap _a;
00321     _a.ui = ui;
00322     if (_endian.uc[0] == 0x44) {
00323         uint8_t _b, *_c = _a.uc; \
00324         _b = _c[3]; _c[3] = _c[0]; _c[0] = _b; \
00325         _b = _c[2]; _c[2] = _c[1]; _c[1] = _b; \
00326     }
00327     return _a.ui;
00328 }
00329 static inline uint32_t _hton_ui(uint32_t ui)
00330         /*@*/
00331 {
00332     return _ntoh_ui(ui);
00333 }
00334 
00335 static inline uint16_t _ntoh_us(uint16_t us)
00336         /*@*/
00337 {
00338     union _dbswap _a;
00339     _a.us = us;
00340     if (_endian.uc[0] == 0x44) {
00341         uint8_t _b, *_c = _a.uc; \
00342         _b = _c[1]; _c[1] = _c[0]; _c[0] = _b; \
00343     }
00344     return _a.us;
00345 }
00346 static inline uint16_t _hton_us(uint16_t us)
00347         /*@*/
00348 {
00349     return _ntoh_us(us);
00350 }
00351 
00352 typedef struct _setSwap_s {
00353     union _dbswap hdr;
00354     union _dbswap tag;
00355     uint32_t fp;
00356 } * setSwap;
00357 
00358 /* XXX assumes hdrNum is first int in dbiIndexItem */
00359 static int hdrNumCmp(const void * one, const void * two)
00360         /*@*/
00361 {
00362     const int * a = one, * b = two;
00363     return (*a - *b);
00364 }
00365 
00375 static int dbiAppendSet(dbiIndexSet set, const void * recs,
00376         int nrecs, size_t recsize, int sortset)
00377         /*@modifies *set @*/
00378 {
00379     const char * rptr = recs;
00380     size_t rlen = (recsize < sizeof(*(set->recs)))
00381                 ? recsize : sizeof(*(set->recs));
00382 
00383     if (set == NULL || recs == NULL || nrecs <= 0 || recsize == 0)
00384         return 1;
00385 
00386     set->recs = xrealloc(set->recs,
00387                         (set->count + nrecs) * sizeof(*(set->recs)));
00388 
00389     memset(set->recs + set->count, 0, nrecs * sizeof(*(set->recs)));
00390 
00391     while (nrecs-- > 0) {
00392         /*@-mayaliasunique@*/
00393         memcpy(set->recs + set->count, rptr, rlen);
00394         /*@=mayaliasunique@*/
00395         rptr += recsize;
00396         set->count++;
00397     }
00398 
00399     if (sortset && set->count > 1)
00400         qsort(set->recs, set->count, sizeof(*(set->recs)), hdrNumCmp);
00401 
00402     return 0;
00403 }
00404 
00405 /* XXX transaction.c */
00406 unsigned int dbiIndexSetCount(dbiIndexSet set) {
00407     return set->count;
00408 }
00409 
00410 /* XXX transaction.c */
00411 uint32_t dbiIndexRecordOffset(dbiIndexSet set, unsigned int recno) {
00412     return set->recs[recno].hdrNum;
00413 }
00414 
00415 /* XXX transaction.c */
00416 uint32_t dbiIndexRecordFileNumber(dbiIndexSet set, unsigned int recno) {
00417     return set->recs[recno].tagNum;
00418 }
00419 
00420 /* XXX transaction.c */
00421 dbiIndexSet dbiFreeIndexSet(dbiIndexSet set) {
00422     if (set) {
00423         set->recs = _free(set->recs);
00424         set = _free(set);
00425     }
00426     return set;
00427 }
00428 
00429 struct rpmmi_s {
00430     struct rpmioItem_s _item;   
00431 /*@dependent@*/ /*@null@*/
00432     rpmmi               mi_next;
00433 /*@refcounted@*/
00434     rpmdb               mi_db;
00435     rpmTag              mi_rpmtag;
00436     dbiIndexSet         mi_set;
00437     DBC *               mi_dbc;
00438     unsigned int        mi_count;
00439     uint32_t            mi_setx;
00440     void *              mi_keyp;
00441     const char *        mi_primary;
00442     size_t              mi_keylen;
00443 /*@refcounted@*/ /*@null@*/
00444     Header              mi_h;
00445     int                 mi_sorted;
00446     int                 mi_cflags;
00447     int                 mi_modified;
00448     uint32_t            mi_prevoffset;  /* header instance (big endian) */
00449     uint32_t            mi_offset;      /* header instance (big endian) */
00450     uint32_t            mi_bntag;       /* base name tag (native endian) */
00451 /*@refcounted@*/ /*@null@*/
00452     rpmbf               mi_bf;          /* Iterator instance Bloom filter. */
00453     int                 mi_nre;
00454 /*@only@*/ /*@null@*/
00455     miRE                mi_re;
00456 
00457 };
00458 
00459 /*@unchecked@*/
00460 static rpmdb rpmdbRock;
00461 
00462 /*@unchecked@*/ /*@exposed@*/ /*@null@*/
00463 static rpmmi rpmmiRock;
00464 
00465 int rpmdbCheckTerminate(int terminate)
00466         /*@globals rpmdbRock, rpmmiRock @*/
00467         /*@modifies rpmdbRock, rpmmiRock @*/
00468 {
00469     sigset_t newMask, oldMask;
00470     static int terminating = 0;
00471 
00472     if (terminating) return 1;
00473 
00474     (void) sigfillset(&newMask);                /* block all signals */
00475     (void) sigprocmask(SIG_BLOCK, &newMask, &oldMask);
00476 
00477     if (sigismember(&rpmsqCaught, SIGINT)
00478      || sigismember(&rpmsqCaught, SIGQUIT)
00479      || sigismember(&rpmsqCaught, SIGHUP)
00480      || sigismember(&rpmsqCaught, SIGTERM)
00481      || sigismember(&rpmsqCaught, SIGPIPE)
00482 #ifdef  NOTYET          /* XXX todo++ */
00483      || sigismember(&rpmsqCaught, SIGXCPU)
00484      || sigismember(&rpmsqCaught, SIGXFSZ)
00485 #endif
00486      || terminate)
00487         terminating = 1;
00488 
00489     if (terminating) {
00490         rpmdb db;
00491         rpmmi mi;
00492 
00493         while ((mi = rpmmiRock) != NULL) {
00494 /*@i@*/     rpmmiRock = mi->mi_next;
00495             mi->mi_next = NULL;
00496 /*@i@*/     mi = rpmmiFree(mi);
00497         }
00498 
00499 /*@-newreftrans@*/
00500         while ((db = rpmdbRock) != NULL) {
00501 /*@i@*/     rpmdbRock = db->db_next;
00502             db->db_next = NULL;
00503             (void) rpmdbClose(db);
00504         }
00505 /*@=newreftrans@*/
00506     }
00507 
00508     (void) sigprocmask(SIG_SETMASK, &oldMask, NULL);
00509     return terminating;
00510 }
00511 
00512 int rpmdbCheckSignals(void)
00513 {
00514 
00515     if (rpmdbCheckTerminate(0)) {
00516 /*@-abstract@*/ /* sigset_t is abstract type */
00517         rpmlog(RPMLOG_DEBUG, D_("Exiting on signal(0x%lx) ...\n"), *((unsigned long *)&rpmsqCaught));
00518 /*@=abstract@*/
00519         exit(EXIT_FAILURE);
00520     }
00521     return 0;
00522 }
00523 
00530 static int blockSignals(/*@unused@*/ rpmdb db, /*@out@*/ sigset_t * oldMask)
00531         /*@globals fileSystem @*/
00532         /*@modifies *oldMask, fileSystem @*/
00533 {
00534     sigset_t newMask;
00535 
00536     (void) sigfillset(&newMask);                /* block all signals */
00537     (void) sigprocmask(SIG_BLOCK, &newMask, oldMask);
00538     (void) sigdelset(&newMask, SIGINT);
00539     (void) sigdelset(&newMask, SIGQUIT);
00540     (void) sigdelset(&newMask, SIGHUP);
00541     (void) sigdelset(&newMask, SIGTERM);
00542     (void) sigdelset(&newMask, SIGPIPE);
00543     return sigprocmask(SIG_BLOCK, &newMask, NULL);
00544 }
00545 
00552 /*@mayexit@*/
00553 static int unblockSignals(/*@unused@*/ rpmdb db, sigset_t * oldMask)
00554         /*@globals fileSystem, internalState @*/
00555         /*@modifies fileSystem, internalState @*/
00556 {
00557     (void) rpmdbCheckSignals();
00558     return sigprocmask(SIG_SETMASK, oldMask, NULL);
00559 }
00560 
00568 static inline /*@null@*/ const char * queryHeader(Header h, const char * qfmt)
00569         /*@globals headerCompoundFormats, fileSystem, internalState @*/
00570         /*@modifies h, fileSystem, internalState @*/
00571 {
00572     const char * errstr = "(unkown error)";
00573     const char * str;
00574 
00575 /*@-modobserver@*/
00576     str = headerSprintf(h, qfmt, NULL, headerCompoundFormats, &errstr);
00577 /*@=modobserver@*/
00578     if (str == NULL)
00579         rpmlog(RPMLOG_ERR, _("incorrect format: \"%s\": %s\n"), qfmt, errstr);
00580     return str;
00581 }
00582 
00590 static int rpmdbExportInfo(/*@unused@*/ rpmdb db, Header h, int adding)
00591         /*@globals headerCompoundFormats, rpmGlobalMacroContext, h_errno,
00592                 fileSystem, internalState @*/
00593         /*@modifies h, rpmGlobalMacroContext,
00594                 fileSystem, internalState @*/
00595 {
00596     static int oneshot;
00597     HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
00598     const char * fn = NULL;
00599     int xx;
00600 
00601     {   const char * fnfmt = rpmGetPath("%{?_hrmib_path}", NULL);
00602         if (fnfmt && *fnfmt)
00603             fn = queryHeader(h, fnfmt);
00604         fnfmt = _free(fnfmt);
00605     }
00606 
00607     if (fn == NULL)
00608         goto exit;
00609 
00610     /* Lazily create the directory in chroot's if configured. */
00611     if (!oneshot) {
00612         char * _fn = xstrdup(fn);
00613         char * dn = dirname(_fn);
00614         mode_t _mode = 0755;
00615         uid_t _uid = 0;
00616         gid_t _gid = 0;
00617         /* If not a directory, then disable, else don't retry. */
00618         errno = 0;
00619         oneshot = (rpmioMkpath(dn, _mode, _uid, _gid) ? -1 : 1);
00620         _fn = _free(_fn);
00621     }
00622     /* If directory is AWOL, don't bother exporting info. */
00623     if (oneshot < 0)
00624         goto exit;
00625 
00626     if (adding) {
00627         FD_t fd = Fopen(fn, "w.fdio");
00628 
00629         if (fd != NULL) {
00630             xx = Fclose(fd);
00631             fd = NULL;
00632             he->tag = RPMTAG_INSTALLTID;
00633             if (headerGet(h, he, 0)) {
00634                 struct utimbuf stamp;
00635                 stamp.actime = he->p.ui32p[0];
00636                 stamp.modtime = he->p.ui32p[0];
00637                 if (!Utime(fn, &stamp))
00638                     rpmlog(RPMLOG_DEBUG, "  +++ %s\n", fn);
00639             }
00640             he->p.ptr = _free(he->p.ptr);
00641         }
00642     } else {
00643         if (!Unlink(fn))
00644             rpmlog(RPMLOG_DEBUG, "  --- %s\n", fn);
00645     }
00646 
00647 exit:
00648     fn = _free(fn);
00649     return 0;
00650 }
00651 
00652 /*@unchecked@*/ /*@only@*/ /*@null@*/
00653 rpmioPool _rpmdbPool;
00654 
00655 static rpmdb rpmdbGetPool(/*@null@*/ rpmioPool pool)
00656         /*@globals _rpmdbPool, fileSystem @*/
00657         /*@modifies pool, _rpmdbPool, fileSystem @*/
00658 {
00659     rpmdb db;
00660 
00661     if (_rpmdbPool == NULL) {
00662         _rpmdbPool = rpmioNewPool("db", sizeof(*db), -1, _rpmdb_debug,
00663                         NULL, NULL, NULL);
00664         pool = _rpmdbPool;
00665     }
00666     db = (rpmdb) rpmioGetPool(pool, sizeof(*db));
00667     memset(((char *)db)+sizeof(db->_item), 0, sizeof(*db)-sizeof(db->_item));
00668     return db;
00669 }
00670 
00671 int rpmdbOpenAll(rpmdb db)
00672 {
00673     int rc = 0;
00674 
00675     if (db == NULL) return -2;
00676 
00677   if (db->db_tags != NULL && db->_dbi != NULL) {
00678     size_t dbix;
00679     for (dbix = 0; dbix < db->db_ndbi; dbix++) {
00680         if ((int)db->db_tags[dbix].tag < 0)
00681             continue;
00682         if (db->_dbi[dbix] != NULL)
00683             continue;
00684         switch (db->db_tags[dbix].tag) {
00685         case RPMDBI_AVAILABLE:
00686         case RPMDBI_ADDED:
00687         case RPMDBI_REMOVED:
00688         case RPMDBI_DEPENDS:
00689         case RPMDBI_BTREE:
00690         case RPMDBI_HASH:
00691         case RPMDBI_QUEUE:
00692         case RPMDBI_RECNO:
00693             continue;
00694             /*@notreached@*/ /*@switchbreak@*/ break;
00695         default:
00696             /*@switchbreak@*/ break;
00697         }
00698         (void) dbiOpen(db, db->db_tags[dbix].tag, db->db_flags);
00699     }
00700   }
00701     return rc;
00702 }
00703 
00704 int rpmdbBlockDBI(rpmdb db, int tag)
00705 {
00706     rpmTag tagn = (rpmTag)(tag >= 0 ? tag : -tag);
00707     size_t dbix;
00708 
00709     if (db == NULL || db->_dbi == NULL)
00710         return 0;
00711 
00712     if (db->db_tags != NULL)
00713     for (dbix = 0; dbix < db->db_ndbi; dbix++) {
00714         if (db->db_tags[dbix].tag != tagn)
00715             continue;
00716         db->db_tags[dbix].tag = tag;
00717         return 0;
00718     }
00719     return 0;
00720 }
00721 
00722 int rpmdbCloseDBI(rpmdb db, int tag)
00723 {
00724     size_t dbix;
00725     int rc = 0;
00726 
00727     if (db == NULL || db->_dbi == NULL)
00728         return 0;
00729 
00730     if (db->db_tags != NULL)
00731     for (dbix = 0; dbix < db->db_ndbi; dbix++) {
00732         if (db->db_tags[dbix].tag != (rpmTag)tag)
00733             continue;
00734         if (db->_dbi[dbix] != NULL) {
00735             int xx;
00736             /*@-unqualifiedtrans@*/             /* FIX: double indirection. */
00737             xx = dbiClose(db->_dbi[dbix], 0);
00738             if (xx && rc == 0) rc = xx;
00739             db->_dbi[dbix] = NULL;
00740             /*@=unqualifiedtrans@*/
00741         }
00742         break;
00743     }
00744     return rc;
00745 }
00746 
00747 /* XXX query.c, rpminstall.c, verify.c */
00748 /*@-incondefs@*/
00749 int rpmdbClose(rpmdb db)
00750         /*@globals rpmdbRock @*/
00751         /*@modifies rpmdbRock @*/
00752 {
00753     static const char msg[] = "rpmdbClose";
00754     rpmdb * prev, next;
00755     size_t dbix;
00756     int rc = 0;
00757 
00758     if (db == NULL)
00759         return rc;
00760 
00761     yarnPossess(db->_item.use);
00762 /*@-modfilesys@*/
00763 if (_rpmdb_debug)
00764 fprintf(stderr, "--> db %p -- %ld %s at %s:%u\n", db, yarnPeekLock(db->_item.use), msg, __FILE__, __LINE__);
00765 
00766     /*@-usereleased@*/
00767     if (yarnPeekLock(db->_item.use) <= 1L) {
00768 
00769         if (db->_dbi)
00770         for (dbix = db->db_ndbi; dbix;) {
00771             int xx;
00772             dbix--;
00773             if (db->_dbi[dbix] == NULL)
00774                 continue;
00775             /*@-unqualifiedtrans@*/             /* FIX: double indirection. */
00776             xx = dbiClose(db->_dbi[dbix], 0);
00777             if (xx && rc == 0) rc = xx;
00778             db->_dbi[dbix] = NULL;
00779             /*@=unqualifiedtrans@*/
00780         }
00781         db->db_errpfx = _free(db->db_errpfx);
00782         db->db_root = _free(db->db_root);
00783         db->db_home = _free(db->db_home);
00784         db->db_tags = tagStoreFree(db->db_tags, db->db_ndbi);
00785         db->_dbi = _free(db->_dbi);
00786         db->db_ndbi = 0;
00787 
00788 /*@-newreftrans@*/
00789         prev = &rpmdbRock;
00790         while ((next = *prev) != NULL && next != db)
00791             prev = &next->db_next;
00792         if (next) {
00793 /*@i@*/     *prev = next->db_next;
00794             next->db_next = NULL;
00795         }
00796 /*@=newreftrans@*/
00797 
00798         if (rpmdbRock == NULL && rpmmiRock == NULL) {
00799             /* Last close uninstalls special signal handling. */
00800             (void) rpmsqEnable(-SIGHUP, NULL);
00801             (void) rpmsqEnable(-SIGINT, NULL);
00802             (void) rpmsqEnable(-SIGTERM,        NULL);
00803             (void) rpmsqEnable(-SIGQUIT,        NULL);
00804             (void) rpmsqEnable(-SIGPIPE,        NULL);
00805             /* Pending signals strike here. */
00806             (void) rpmdbCheckSignals();
00807         }
00808 
00809     /*@=usereleased@*/
00810         db = (rpmdb)rpmioPutPool((rpmioItem)db);
00811     } else
00812         yarnTwist(db->_item.use, BY, -1);
00813 
00814     return rc;
00815 }
00816 /*@=incondefs@*/
00817 
00823 static const char * rpmdbURIPath(const char *uri)
00824         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
00825         /*@modifies rpmGlobalMacroContext, fileSystem, internalState @*/
00826 {
00827     const char * s = rpmGetPath(uri, NULL);
00828     ARGV_t av = NULL;
00829     int xx = argvSplit(&av, s, ":");
00830     const char * fn = NULL;
00831     /* XXX av contains a colon separated path split, use the 1st path. */
00832     urltype ut = urlPath(av[0], &fn);
00833     
00834 xx = xx;
00835 
00836     switch (ut) {
00837     case URL_IS_PATH:
00838     case URL_IS_UNKNOWN:
00839         fn = xstrdup(av[0]);
00840         break;
00841     case URL_IS_HTTPS:
00842     case URL_IS_HTTP:
00843     case URL_IS_FTP:
00844     case URL_IS_HKP:
00845     case URL_IS_DASH:
00846     default:
00847         /* HACK: strip the URI prefix for these schemes. */
00848         fn = rpmGetPath(fn, NULL);
00849         break;
00850     }
00851 
00852     /* Convert relative to absolute paths. */
00853     if (ut != URL_IS_PATH)      /* XXX permit file:///... URI's */
00854     if (fn && *fn && *fn != '/') {
00855         char dn[PATH_MAX];
00856         char *t;
00857         dn[0] = '\0';
00858         if ((t = Realpath(".", dn)) != NULL) {
00859             t += strlen(dn);
00860             if (t > dn && t[-1] != '/')
00861                 *t++ = '/';
00862             t = stpncpy(t, fn, (sizeof(dn) - (t - dn)));
00863             *t = '\0';
00864             fn = _free(fn);
00865             fn = rpmGetPath(dn, NULL);
00866         }
00867     }
00868 
00869     av = argvFree(av);
00870     s = _free(s);
00871 assert(fn != NULL);
00872     return fn;
00873 }
00874 
00875 #define _DB_ROOT        "/"
00876 #define _DB_HOME        "%{?_dbpath}"
00877 #define _DB_FLAGS       0
00878 #define _DB_MODE        0
00879 #define _DB_PERMS       0644
00880 
00881 #define _DB_MAJOR       3
00882 #define _DB_ERRPFX      "rpmdb"
00883 
00884 /*@-exportheader -globs -mods @*/
00885 /*@only@*/ /*@null@*/
00886 rpmdb rpmdbNew(/*@kept@*/ /*@null@*/ const char * root,
00887                 /*@kept@*/ /*@null@*/ const char * home,
00888                 int mode, mode_t perms, int flags)
00889         /*@*/
00890 {
00891     rpmdb db = rpmdbGetPool(_rpmdbPool);
00892     const char * epfx = _DB_ERRPFX;
00893 
00894 /*@-modfilesys@*/ /*@-nullpass@*/
00895 if (_rpmdb_debug)
00896 fprintf(stderr, "==> rpmdbNew(%s, %s, 0x%x, 0%o, 0x%x) db %p\n", root, home, mode, perms, flags, db);
00897 /*@=modfilesys@*/ /*@=nullpass@*/
00898 
00899     if (!(perms & 0600)) perms = 0644;  /* XXX sanity */
00900 
00901     db->db_root = rpmdbURIPath( (root && *root ? root : _DB_ROOT) );
00902     db->db_home = rpmdbURIPath( (home && *home ? home : _DB_HOME) );
00903 
00904     if (!(db->db_home && db->db_home[0] && db->db_home[0] != '%')) {
00905         rpmlog(RPMLOG_ERR, _("no dbpath has been set\n"));
00906         db->db_root = _free(db->db_root);
00907         db->db_home = _free(db->db_home);
00908         db = (rpmdb) rpmioPutPool((rpmioItem)db);
00909         /*@-globstate@*/ return NULL; /*@=globstate@*/
00910     }
00911 
00912     db->db_flags = (flags >= 0) ? flags : _DB_FLAGS;
00913     db->db_mode = (mode >= 0) ? mode : _DB_MODE;
00914     db->db_perms = (perms > 0)  ? perms : _DB_PERMS;
00915     db->db_api = _DB_MAJOR;
00916     db->db_errpfx = rpmExpand( (epfx && *epfx ? epfx : _DB_ERRPFX), NULL);
00917 
00918     db->db_remove_env = 0;
00919     db->db_chrootDone = 0;
00920     db->db_maxkey = 0;
00921     db->db_errcall = NULL;
00922     db->db_errfile = NULL;
00923     db->db_malloc = NULL;
00924     db->db_realloc = NULL;
00925     db->db_free = NULL;
00926     db->db_export = rpmdbExportInfo;
00927     db->db_h = NULL;
00928 
00929     db->db_next = NULL;
00930     db->db_opens = 0;
00931 
00932     db->db_dbenv = NULL;
00933     db->db_txn = NULL;
00934     db->db_logc = NULL;
00935     db->db_mpf = NULL;
00936 
00937     dbiTagsInit(&db->db_tags, &db->db_ndbi);
00938     db->_dbi = xcalloc(db->db_ndbi, sizeof(*db->_dbi));
00939 
00940     memset(&db->db_getops, 0, sizeof(db->db_getops));
00941     memset(&db->db_putops, 0, sizeof(db->db_putops));
00942     memset(&db->db_delops, 0, sizeof(db->db_delops));
00943 
00944     /*@-globstate@*/
00945     return rpmdbLink(db, __FUNCTION__);
00946     /*@=globstate@*/
00947 }
00948 /*@=exportheader =globs =mods @*/
00949 
00950 static int rpmdbOpenDatabase(/*@null@*/ const char * prefix,
00951                 /*@null@*/ const char * dbpath,
00952                 int _dbapi, /*@null@*/ /*@out@*/ rpmdb *dbp,
00953                 int mode, mode_t perms, int flags)
00954         /*@globals rpmdbRock, rpmGlobalMacroContext, h_errno,
00955                 fileSystem, internalState @*/
00956         /*@modifies rpmdbRock, *dbp, rpmGlobalMacroContext,
00957                 fileSystem, internalState @*/
00958 {
00959     rpmdb db;
00960     int rc;
00961     int xx;
00962 
00963     /* Insure that _dbapi has one of -1, 1, 2, or 3 */
00964     if (_dbapi < -1 || _dbapi > 4)
00965         _dbapi = -1;
00966     if (_dbapi == 0)
00967         _dbapi = 1;
00968 
00969     if (dbp)
00970         *dbp = NULL;
00971     if (mode & O_WRONLY) 
00972         return 1;
00973 
00974     db = rpmdbNew(prefix, dbpath, mode, perms, flags);
00975     if (db == NULL)
00976         return 1;
00977 
00978     if (rpmdbRock == NULL && rpmmiRock == NULL) {
00979         /* First open installs special signal handling. */
00980         (void) rpmsqEnable(SIGHUP,      NULL);
00981         (void) rpmsqEnable(SIGINT,      NULL);
00982         (void) rpmsqEnable(SIGTERM,     NULL);
00983         (void) rpmsqEnable(SIGQUIT,     NULL);
00984         (void) rpmsqEnable(SIGPIPE,     NULL);
00985     }
00986 
00987 /*@-assignexpose -newreftrans@*/
00988 /*@i@*/ db->db_next = rpmdbRock;
00989         rpmdbRock = db;
00990 /*@=assignexpose =newreftrans@*/
00991 
00992     db->db_api = _dbapi;
00993 
00994     {   size_t dbix;
00995 
00996         rc = 0;
00997         if (db->db_tags != NULL)
00998         for (dbix = 0; rc == 0 && dbix < db->db_ndbi; dbix++) {
00999             tagStore_t dbiTag = db->db_tags + dbix;
01000             rpmTag tag = dbiTag->tag;
01001             dbiIndex dbi;
01002 
01003             /* Filter out temporary databases */
01004             switch (tag) {
01005             case RPMDBI_AVAILABLE:
01006             case RPMDBI_ADDED:
01007             case RPMDBI_REMOVED:
01008             case RPMDBI_DEPENDS:
01009                 continue;
01010                 /*@notreached@*/ /*@switchbreak@*/ break;
01011             default:
01012                 /*@switchbreak@*/ break;
01013             }
01014 
01015             dbi = dbiOpen(db, tag, 0);
01016             if (dbi == NULL) {
01017                 rc = -2;
01018                 break;
01019             }
01020 
01021             switch (tag) {
01022             case RPMDBI_PACKAGES:
01023                 if (dbi == NULL) rc |= 1;
01024 #if 0
01025                 /* XXX open only Packages, indices created on the fly. */
01026                 if (db->db_api == 3)
01027 #endif
01028                     goto exit;
01029                 /*@notreached@*/ /*@switchbreak@*/ break;
01030             case RPMTAG_NAME:
01031                 if (dbi == NULL) rc |= 1;
01032                 /*@switchbreak@*/ break;
01033             default:
01034                 /*@switchbreak@*/ break;
01035             }
01036         }
01037     }
01038 
01039 exit:
01040     if (rc || dbp == NULL)
01041         xx = rpmdbClose(db);
01042     else {
01043 /*@-assignexpose -newreftrans@*/
01044 /*@i@*/ *dbp = db;
01045 /*@=assignexpose =newreftrans@*/
01046     }
01047 
01048     return rc;
01049 }
01050 
01051 /* XXX python/rpmmodule.c */
01052 int rpmdbOpen (const char * prefix, rpmdb *dbp, int mode, mode_t perms)
01053 {
01054     int _dbapi = rpmExpandNumeric("%{?_dbapi}");
01055     return rpmdbOpenDatabase(prefix, NULL, _dbapi, dbp, mode, perms, 0);
01056 }
01057 
01058 int rpmdbCount(rpmdb db, rpmTag tag, const void * keyp, size_t keylen)
01059 {
01060     unsigned int count = 0;
01061     DBC * dbcursor = NULL;
01062     DBT k = DBT_INIT;
01063     DBT v = DBT_INIT;
01064     dbiIndex dbi;
01065     int rc;
01066     int xx;
01067 
01068     if (db == NULL || keyp == NULL)
01069         return 0;
01070 
01071     dbi = dbiOpen(db, tag, 0);
01072     if (dbi == NULL)
01073         return 0;
01074 
01075     if (keylen == 0)
01076         keylen = strlen(keyp);
01077 
01078 /*@-temptrans@*/
01079     k.data = (void *) keyp;
01080 /*@=temptrans@*/
01081     k.size = (UINT32_T) keylen;
01082 
01083     xx = dbiCopen(dbi, dbiTxnid(dbi), &dbcursor, 0);
01084     rc = dbiGet(dbi, dbcursor, &k, &v, DB_SET);
01085     switch (rc) {
01086     case 0:
01087         rc = dbiCount(dbi, dbcursor, &count, 0);
01088         if (rc != 0)
01089             rc = -1;
01090         else
01091             rc = count;
01092         break;
01093     case DB_NOTFOUND:
01094         rc = 0;
01095         break;
01096     default:
01097         rpmlog(RPMLOG_ERR, _("error(%d) getting records from %s index\n"),
01098                 rc, tagName(dbi->dbi_rpmtag));
01099         rc = -1;
01100         break;
01101     }
01102     xx = dbiCclose(dbi, dbcursor, 0);
01103     dbcursor = NULL;
01104     return rc;
01105 }
01106 
01107 /* XXX python/upgrade.c, install.c, uninstall.c */
01108 int rpmdbCountPackages(rpmdb db, const char * N)
01109 {
01110     return rpmdbCount(db, RPMTAG_NAME, N, strlen(N));
01111 }
01112 
01113 /* Return pointer to first RE character (or NUL terminator) */
01114 static const char * stemEnd(const char * s)
01115         /*@*/
01116 {
01117     int c;
01118 
01119     while ((c = (int)*s)) {
01120         switch (c) {
01121         case '.':
01122         case '^':
01123         case '$':
01124         case '?':
01125         case '*':
01126         case '+':
01127         case '|':
01128         case '[':
01129         case '(':
01130         case '{':
01131         case '\0':
01132             goto exit;
01133             /*@notreached@*/ /*@switchbreak@*/ break;
01134         case '\\':
01135             s++;
01136             if (*s == '\0') goto exit;
01137             /*@fallthrough@*/
01138         default:
01139             /*@switchbreak@*/ break;
01140         }
01141         s++;
01142     }
01143 exit:
01144     return s;
01145 }
01146 
01147 /*@only@*/
01148 static const char * _str2PCREpat(/*@null@*/ const char *_pre, const char *s,
01149                 /*@null@*/ const char *_post)
01150         /*@*/
01151 {
01152     static const char _REchars[] = "^.*(|)[]+?{}$";
01153     size_t nt = 0;
01154     const char * se;
01155     char * t;
01156     char * te;
01157 
01158     /* Find the PCRE pattern length, including escapes. */
01159     for (se = s; *se != '\0'; se++, nt++)
01160         if (strchr(_REchars, *se)) nt++;
01161     nt += strlen(_pre) + strlen(_post);
01162 
01163     /* Build the PCRE pattern, escaping characters as needed. */
01164     te = t = xmalloc(nt + 1);
01165     te = stpcpy(te, _pre);
01166     for (se = s; *se != '\0'; *te++ = *se++)
01167         if (strchr(_REchars, *se)) *te++ = '\\';
01168     te = stpcpy(te, _post);
01169     *te = '\0';
01170 
01171 /*@-dependenttrans@*/
01172     return t;
01173 /*@=dependenttrans@*/
01174 }
01175 
01186 static int dbiMireKeys(rpmdb db, rpmTag tag, rpmMireMode mode,
01187                 /*@null@*/ const char * pat,
01188                 /*@null@*/ dbiIndexSet * matches,
01189                 /*@null@*/ const char *** argvp)
01190         /*@globals internalState @*/
01191         /*@modifies *matches, *argvp, internalState @*/
01192 {
01193     DBC * dbcursor = NULL;
01194     DBT k = DBT_INIT;
01195     DBT p = DBT_INIT;
01196     DBT v = DBT_INIT;
01197     dbiIndex dbi;
01198     miRE mire = NULL;
01199     uint32_t _flags = DB_NEXT;
01200     ARGV_t av = NULL;
01201     dbiIndexSet set = NULL;
01202     const char * b = NULL;
01203     size_t nb = 0;
01204     int ret = 1;                /* assume error */
01205     int rc;
01206     int xx;
01207 
01208     dbi = dbiOpen(db, tag, 0);
01209     if (dbi == NULL)
01210         goto exit;
01211 
01212 if (_rpmmi_debug || dbi->dbi_debug)
01213 fprintf(stderr, "--> %s(%p, %s(%u), %d, \"%s\", %p, %p)\n", __FUNCTION__, db, tagName(tag), (unsigned)tag, mode, pat, matches, argvp);
01214 
01215     if (pat) {
01216 
01217         mire = mireNew(mode, 0);
01218         xx = mireRegcomp(mire, pat);
01219 
01220         /* Initialize the secondary retrieval key. */
01221         switch (mode) {
01222         default:
01223 assert(0);                      /* XXX sanity */
01224             /*@notreached@*/ break;
01225         case RPMMIRE_GLOB:
01226             break;
01227         case RPMMIRE_REGEX:
01228         case RPMMIRE_PCRE:
01229             if (*pat == '^') pat++;
01230 
01231             /* If partial match on stem won't help, just iterate. */
01232             nb = stemEnd(pat) - pat;
01233             if (nb == 0) {
01234                 k.doff = 0;
01235                 goto doit;
01236             }
01237 
01238             /* Remove the escapes in the stem. */
01239             {   char *be;
01240                 b = be = xmalloc(nb + 1);
01241                 while (nb--) {
01242                     if ((*be = *pat++) != '\\')
01243                         be++;
01244                 }
01245                 *be = '\0';
01246             }
01247             nb = strlen(b);
01248 
01249             /* Set stem length for partial match retrieve. */
01250             k.flags = DB_DBT_PARTIAL;
01251             k.dlen = nb;
01252             k.size = nb;
01253             k.data = (void *) b;
01254             _flags = DB_SET_RANGE;
01255             break;
01256         case RPMMIRE_STRCMP:
01257             k.size = (UINT32_T) strlen(pat);
01258             k.data = (void *) pat;
01259             _flags = DB_SET;
01260             break;
01261         }
01262     }
01263 
01264 doit:
01265     p.flags |= DB_DBT_PARTIAL;
01266     v.flags |= DB_DBT_PARTIAL;
01267 
01268     xx = dbiCopen(dbi, dbiTxnid(dbi), &dbcursor, 0);
01269 
01270     /* Iterate over matches, collecting primary/secondary keys. */
01271     while ((rc = dbiPget(dbi, dbcursor, &k, &p, &v, _flags)) == 0) {
01272         uint32_t hdrNum;
01273         const char * s;
01274         size_t ns;
01275 
01276         if (_flags == DB_SET) _flags = DB_NEXT_DUP;
01277         if (b != NULL && nb > 0) {
01278 
01279             /* Exit if the stem doesn't match. */
01280             if (k.size < nb || memcmp(b, k.data, nb))
01281                 break;
01282 
01283             /* Retrieve the full record after DB_SET_RANGE. */
01284             if (_flags == DB_SET_RANGE) {
01285                 memset (&k, 0, sizeof(k));
01286                 xx = dbiPget(dbi, dbcursor, &k, &p, &v, DB_CURRENT);
01287                 _flags = DB_NEXT;
01288             }
01289         }
01290 
01291         /* Get the secondary key. */
01292         s = (const char * ) k.data;
01293         ns = k.size;
01294 
01295         /* Skip if not matched. */
01296         if (mire && mireRegexec(mire, s, ns) < 0)
01297             continue;
01298             
01299         /* Get a native endian copy of the primary package key. */
01300         memcpy(&hdrNum, p.data, sizeof(hdrNum));
01301         hdrNum = _ntoh_ui(hdrNum);
01302 
01303         /* Collect primary keys. */
01304         if (matches) {
01305             if (set == NULL)
01306                 set = xcalloc(1, sizeof(*set));
01307             /* XXX TODO: sort/uniqify set? */
01308             (void) dbiAppendSet(set, &hdrNum, 1, sizeof(hdrNum), 0);
01309         }
01310 
01311         /* Collect secondary keys. */
01312         if (argvp) {
01313             char * a = memcpy(xmalloc(ns+1), s, ns);
01314             a[ns] = '\0';
01315             xx = argvAdd(&av, a);
01316             a = _free(a);
01317         }
01318     }
01319 
01320     xx = dbiCclose(dbi, dbcursor, 0);
01321     dbcursor = NULL;
01322 
01323     switch (rc) {
01324     case 0:
01325     case DB_NOTFOUND:
01326         ret = 0;
01327         break;
01328     default:
01329         rpmlog(RPMLOG_ERR, _("error(%d) getting keys from %s index\n"),
01330                 rc, tagName(dbi->dbi_rpmtag));
01331         break;
01332     }
01333 
01334 exit:
01335     if (ret == 0) {
01336         if (matches) {
01337             /* XXX TODO: sort/uniqify set? */
01338             *matches = set;
01339             set = NULL;
01340         }
01341         if (argvp)
01342             xx = argvAppend(argvp, av);
01343     }
01344     set = dbiFreeIndexSet(set);
01345     av = argvFree(av);
01346     b = _free(b);
01347     mire = mireFree(mire);
01348 if (_rpmmi_debug || dbi->dbi_debug)
01349 fprintf(stderr, "<-- %s(%p, %s(%u), %d, %p, %p, %p) rc %d %p[%u]\n", __FUNCTION__, db, tagName(tag), (unsigned)tag, mode, pat, matches, argvp, ret, (matches && *matches ? (*matches)->recs : NULL), (matches && *matches ? (*matches)->count : 0));
01350     return ret;
01351 }
01352 
01353 int rpmdbMireApply(rpmdb db, rpmTag tag, rpmMireMode mode, const char * pat,
01354                 const char *** argvp)
01355 {
01356     int rc = dbiMireKeys(db, tag, mode, pat, NULL, argvp);
01357 if (_rpmmi_debug)
01358 fprintf(stderr, "<-- %s(%p, %s(%u), %d, \"%s\", %p) rc %d\n", __FUNCTION__, db, tagName(tag), (unsigned)tag, mode, pat, argvp, rc);
01359     return rc;
01360 }
01361 
01362 int rpmmiGrowBasename(rpmmi mi, const char * bn)
01363 {
01364     rpmTag _tag = RPMTAG_BASENAMES;
01365     rpmMireMode _mode = RPMMIRE_STRCMP;
01366     dbiIndexSet set = NULL;
01367     unsigned int i;
01368     int rc = 1;         /* assume error */
01369 
01370     if (mi == NULL || mi->mi_db == NULL || bn == NULL || *bn == '\0')
01371         goto exit;
01372 
01373 #ifdef  NOTYET
01374 assert(mi->mi_rpmtag == _tag);
01375 #endif
01376     /* Retrieve set of headers that contain the base name. */
01377     rc = dbiMireKeys(mi->mi_db, _tag, _mode, bn, &set, NULL);
01378     if (rc == 0 && set != NULL) {
01379         rpmuint32_t tagNum = hashFunctionString(0, bn, 0);
01380         /* Set tagNum to the hash of the basename. */
01381         for (i = 0; i < set->count; i++)
01382             set->recs[i].tagNum = tagNum;
01383         if (mi->mi_set == NULL)
01384             mi->mi_set = xcalloc(1, sizeof(*mi->mi_set));
01385         (void) dbiAppendSet(mi->mi_set, set->recs, set->count, sizeof(*set->recs), 0);
01386     }
01387     rc = 0;
01388 
01389 exit:
01390 if (_rpmmi_debug)
01391 fprintf(stderr, "<-- %s(%p, \"%s\")\trc %d set %p %p[%u]\n", __FUNCTION__, mi, bn, rc, set, (set ? set->recs : NULL), (unsigned)(set ? set->count : 0));
01392     set = dbiFreeIndexSet(set);
01393     return rc;
01394 }
01395 
01403 static rpmRC dbiFindMatches(dbiIndex dbi,
01404                 const char * pat, /*@out@*/ dbiIndexSet * matches)
01405         /*@*/
01406 {
01407     const char * s = pat;
01408     size_t ns = (s ? strlen(s) : 0);
01409     DBC * dbcursor = NULL;
01410     rpmRC rc = RPMRC_NOTFOUND;
01411     int ret;
01412     int xx;
01413 
01414     if (ns == 0) goto exit;
01415 
01416     xx = dbiCopen(dbi, dbiTxnid(dbi), &dbcursor, 0);
01417 
01418     /* Add ^...$ *RE anchors. Escape pattern characters. */
01419     {   rpmTag tag = dbi->dbi_rpmtag;
01420         rpmMireMode mode = RPMMIRE_PCRE;
01421         static const char _post_NVRA[] = "(-[^-]+-[^-]+\\.[^.]+|-[^-]+\\.[^.]+|\\.[^.]+|)$";
01422         const char * _pat;
01423 
01424         switch (tag) {
01425         default:
01426             mode = RPMMIRE_PCRE;
01427             _pat = _str2PCREpat("^", s, ".*$");
01428             break;
01429         case RPMTAG_NVRA:
01430             mode = RPMMIRE_PCRE;
01431             _pat = (s[0] == '^' || s[ns-1] == '$')
01432                 ? xstrdup(s)
01433                 : _str2PCREpat("^", s, _post_NVRA);
01434             break;
01435         case RPMTAG_FILEPATHS:
01436             if (s[0] == '^' || s[ns-1] == '$')
01437                 mode = RPMMIRE_PCRE;
01438             else
01439 #ifdef NOTYET
01440             if (s[0] == '/' && Glob_pattern_p(s, 1))
01441                 mode = RPMMIRE_GLOB;
01442             else
01443 #endif
01444                 mode = RPMMIRE_STRCMP;
01445             _pat = xstrdup(s);
01446             break;
01447         }
01448 
01449         ret = dbiMireKeys(dbi->dbi_rpmdb, tag, mode, _pat, matches, NULL);
01450 
01451         _pat = _free(_pat);
01452     }
01453 
01454     switch (ret) {
01455     case 0:             rc = RPMRC_OK;          break;
01456     case DB_NOTFOUND:   rc = RPMRC_NOTFOUND;    break;
01457     default:            rc = RPMRC_FAIL;
01458         rpmlog(RPMLOG_ERR, _("error(%d) getting records from %s index\n"),
01459                 ret, tagName(dbi->dbi_rpmtag));
01460         break;
01461     }
01462 
01463     xx = dbiCclose(dbi, dbcursor, 0);
01464     dbcursor = NULL;
01465 
01466 exit:
01467 /*@-unqualifiedtrans@*/ /* FIX: double indirection */
01468     if (rc != RPMRC_OK && matches && *matches)
01469         *matches = dbiFreeIndexSet(*matches);
01470 /*@=unqualifiedtrans@*/
01471     return rc;
01472 }
01473 
01474 void * dbiStatsAccumulator(dbiIndex dbi, int opx)
01475 {
01476     void * sw = NULL;
01477     switch (opx) {
01478     case 14:    /* RPMTS_OP_DBGET */
01479         sw = &dbi->dbi_rpmdb->db_getops;
01480         break;
01481     case 15:    /* RPMTS_OP_DBPUT */
01482         sw = &dbi->dbi_rpmdb->db_putops;
01483         break;
01484     default:    /* XXX wrong, but let's not return NULL. */
01485     case 16:    /* RPMTS_OP_DBDEL */
01486         sw = &dbi->dbi_rpmdb->db_delops;
01487         break;
01488     }
01489     return sw;
01490 }
01491 
01500 static int miFreeHeader(rpmmi mi, dbiIndex dbi)
01501         /*@globals fileSystem, internalState @*/
01502         /*@modifies mi, dbi, fileSystem, internalState @*/
01503 {
01504     int rc = 0;
01505 
01506     if (mi == NULL || mi->mi_h == NULL)
01507         return 0;
01508 
01509     if (dbi && mi->mi_dbc && mi->mi_modified && mi->mi_prevoffset) {
01510         DBT k = DBT_INIT;
01511         DBT v = DBT_INIT;
01512         int xx;
01513 
01514 /*@i@*/ k.data = (void *) &mi->mi_prevoffset;
01515         k.size = (UINT32_T) sizeof(mi->mi_prevoffset);
01516         {   size_t len = 0;
01517             v.data = headerUnload(mi->mi_h, &len);
01518             v.size = (UINT32_T) len;
01519         }
01520 
01521         if (v.data != NULL) {
01522             sigset_t signalMask;
01523             (void) blockSignals(dbi->dbi_rpmdb, &signalMask);
01524             rc = dbiPut(dbi, mi->mi_dbc, &k, &v, DB_KEYLAST);
01525             if (rc) {
01526                 rpmlog(RPMLOG_ERR,
01527                         _("error(%d) storing record h#%u into %s\n"),
01528                         rc, (unsigned)_ntoh_ui(mi->mi_prevoffset),
01529                         tagName(dbi->dbi_rpmtag));
01530             }
01531             xx = dbiSync(dbi, 0);
01532             (void) unblockSignals(dbi->dbi_rpmdb, &signalMask);
01533         }
01534         v.data = _free(v.data); /* headerUnload */
01535         v.size = 0;
01536     }
01537 
01538     (void)headerFree(mi->mi_h);
01539     mi->mi_h = NULL;
01540 
01541 /*@-nullstate@*/
01542     return rc;
01543 /*@=nullstate@*/
01544 }
01545 
01546 static void rpmmiFini(void * _mi)
01547         /*@globals rpmmiRock @*/
01548         /*@modifies _mi, rpmmiRock @*/
01549 {
01550     rpmmi mi = _mi;
01551     rpmmi * prev, next;
01552     dbiIndex dbi;
01553     int xx;
01554 
01555     prev = &rpmmiRock;
01556     while ((next = *prev) != NULL && next != mi)
01557         prev = &next->mi_next;
01558     if (next) {
01559 /*@i@*/ *prev = next->mi_next;
01560         next->mi_next = NULL;
01561     }
01562 
01563     /* XXX NOTFOUND exits traverse here w mi->mi_db == NULL. b0rked imho. */
01564     if (mi->mi_db) {
01565         dbi = dbiOpen(mi->mi_db, RPMDBI_PACKAGES, 0);
01566 assert(dbi != NULL);                            /* XXX sanity */
01567 
01568         xx = miFreeHeader(mi, dbi);
01569 
01570         if (mi->mi_dbc)
01571             xx = dbiCclose(dbi, mi->mi_dbc, 0);
01572         mi->mi_dbc = NULL;
01573         /* XXX rpmdbUnlink will not do.
01574          * NB: must be called after rpmmiRock cleanup.
01575          */
01576         (void) rpmdbClose(mi->mi_db);
01577         mi->mi_db = NULL;
01578     }
01579 
01580     (void) mireFreeAll(mi->mi_re, mi->mi_nre);
01581     mi->mi_re = NULL;
01582 
01583     (void) rpmbfFree(mi->mi_bf);
01584     mi->mi_bf = NULL;
01585     mi->mi_set = dbiFreeIndexSet(mi->mi_set);
01586 
01587     mi->mi_keyp = _free(mi->mi_keyp);
01588     mi->mi_keylen = 0;
01589     mi->mi_primary = _free(mi->mi_primary);
01590 
01591     /* XXX this needs to be done elsewhere, not within destructor. */
01592     (void) rpmdbCheckSignals();
01593 }
01594 
01595 /*@unchecked@*/ /*@only@*/ /*@null@*/
01596 rpmioPool _rpmmiPool;
01597 
01598 static rpmmi rpmmiGetPool(/*@null@*/ rpmioPool pool)
01599         /*@globals _rpmdbPool, fileSystem @*/
01600         /*@modifies pool, _rpmdbPool, fileSystem @*/
01601 {
01602     rpmmi mi;
01603 
01604     if (_rpmmiPool == NULL) {
01605         _rpmmiPool = rpmioNewPool("mi", sizeof(*mi), -1, _rpmmi_debug,
01606                         NULL, NULL, rpmmiFini);
01607         pool = _rpmmiPool;
01608     }
01609     mi = (rpmmi) rpmioGetPool(pool, sizeof(*mi));
01610     memset(((char *)mi)+sizeof(mi->_item), 0, sizeof(*mi)-sizeof(mi->_item));
01611     return mi;
01612 }
01613 
01614 uint32_t rpmmiInstance(rpmmi mi)
01615 {
01616     /* Get a native endian copy of the primary package key. */
01617     uint32_t rc = _ntoh_ui(mi ? mi->mi_offset : 0);
01618 if (_rpmmi_debug)
01619 fprintf(stderr, "<-- %s(%p) rc %u\n", __FUNCTION__, mi, (unsigned)rc);
01620     return rc;
01621 }
01622 
01623 uint32_t rpmmiBNTag(rpmmi mi) {
01624     uint32_t rc = (mi ? mi->mi_bntag : 0);
01625 if (_rpmmi_debug)
01626 fprintf(stderr, "<-- %s(%p) rc %u\n", __FUNCTION__, mi, (unsigned)rc);
01627     return rc;
01628 }
01629 
01630 unsigned int rpmmiCount(rpmmi mi)
01631 {
01632     unsigned int rc;
01633 
01634     /* XXX Secondary db associated with Packages needs cursor record count */
01635     if (mi && mi->mi_primary && mi->mi_dbc == NULL) {
01636         dbiIndex dbi = dbiOpen(mi->mi_db, mi->mi_rpmtag, 0);
01637         DBT k = DBT_INIT;
01638         DBT v = DBT_INIT;
01639         int xx;
01640 assert(dbi != NULL);    /* XXX dbiCopen doesn't handle dbi == NULL */
01641         xx = dbiCopen(dbi, dbiTxnid(dbi), &mi->mi_dbc, mi->mi_cflags);
01642         k.data = mi->mi_keyp;
01643         k.size = (u_int32_t)mi->mi_keylen;
01644 if (k.data && k.size == 0) k.size = (UINT32_T) strlen((char *)k.data);
01645 if (k.data && k.size == 0) k.size++;    /* XXX "/" fixup. */
01646         if (!dbiGet(dbi, mi->mi_dbc, &k, &v, DB_SET))
01647             xx = dbiCount(dbi, mi->mi_dbc, &mi->mi_count, 0);
01648     }
01649 
01650     rc = (mi ? mi->mi_count : 0);
01651 
01652 if (_rpmmi_debug)
01653 fprintf(stderr, "<-- %s(%p) rc %u\n", __FUNCTION__, mi, (unsigned)rc);
01654     return rc;
01655 }
01656 
01663 static int mireCmp(const void * a, const void * b)
01664 {
01665 /*@-castexpose @*/
01666     const miRE mireA = (const miRE) a;
01667     const miRE mireB = (const miRE) b;
01668 /*@=castexpose @*/
01669     return (mireA->tag - mireB->tag);
01670 }
01671 
01679 static /*@only@*/ char * mireDup(rpmTag tag, rpmMireMode *modep,
01680                         const char * pattern)
01681         /*@modifies *modep @*/
01682         /*@requires maxSet(modep) >= 0 @*/
01683 {
01684     const char * s;
01685     char * pat;
01686     char * t;
01687     int brackets;
01688     size_t nb;
01689     int c;
01690 
01691     switch (*modep) {
01692     default:
01693     case RPMMIRE_DEFAULT:
01694         if (tag == RPMTAG_DIRNAMES || tag == RPMTAG_BASENAMES
01695          || tag == RPMTAG_FILEPATHS)
01696         {
01697             *modep = RPMMIRE_GLOB;
01698             pat = xstrdup(pattern);
01699             break;
01700         }
01701 
01702         nb = strlen(pattern) + sizeof("^$");
01703 
01704         /* Find no. of bytes needed for pattern. */
01705         /* periods and plusses are escaped, splats become '.*' */
01706         c = (int) '\0';
01707         brackets = 0;
01708         for (s = pattern; *s != '\0'; s++) {
01709             switch (*s) {
01710             case '.':
01711             case '+':
01712             case '*':
01713                 if (!brackets) nb++;
01714                 /*@switchbreak@*/ break;
01715             case '\\':
01716                 s++;
01717                 /*@switchbreak@*/ break;
01718             case '[':
01719                 brackets = 1;
01720                 /*@switchbreak@*/ break;
01721             case ']':
01722                 if (c != (int) '[') brackets = 0;
01723                 /*@switchbreak@*/ break;
01724             }
01725             c = (int) *s;
01726         }
01727 
01728         pat = t = xmalloc(nb);
01729 
01730         if (pattern[0] != '^') *t++ = '^';
01731 
01732         /* Copy pattern, escaping periods, prefixing splats with period. */
01733         c = (int) '\0';
01734         brackets = 0;
01735         for (s = pattern; *s != '\0'; s++, t++) {
01736             switch (*s) {
01737             case '.':
01738             case '+':
01739                 if (!brackets) *t++ = '\\';
01740                 /*@switchbreak@*/ break;
01741             case '*':
01742                 if (!brackets) *t++ = '.';
01743                 /*@switchbreak@*/ break;
01744             case '\\':
01745                 *t++ = *s++;
01746                 /*@switchbreak@*/ break;
01747             case '[':
01748                 brackets = 1;
01749                 /*@switchbreak@*/ break;
01750             case ']':
01751                 if (c != (int) '[') brackets = 0;
01752                 /*@switchbreak@*/ break;
01753             }
01754             *t = *s;
01755             c = (int) *t;
01756         }
01757 
01758         if (s > pattern && s[-1] != '$') *t++ = '$';
01759         *t = '\0';
01760         *modep = RPMMIRE_REGEX;
01761         break;
01762     case RPMMIRE_STRCMP:
01763     case RPMMIRE_REGEX:
01764     case RPMMIRE_GLOB:
01765         pat = xstrdup(pattern);
01766         break;
01767     }
01768 
01769     return pat;
01770 }
01771 
01772 int rpmmiAddPattern(rpmmi mi, rpmTag tag,
01773                 rpmMireMode mode, const char * pattern)
01774 {
01775     static rpmMireMode defmode = (rpmMireMode)-1;
01776     miRE nmire = NULL;
01777     miRE mire = NULL;
01778     const char * allpat = NULL;
01779     int notmatch = 0;
01780     int rc = 0;
01781 
01782     if (defmode == (rpmMireMode)-1) {
01783         const char *t = rpmExpand("%{?_query_selector_match}", NULL);
01784 
01785         if (*t == '\0' || !strcmp(t, "default"))
01786             defmode = RPMMIRE_DEFAULT;
01787         else if (!strcmp(t, "strcmp"))
01788             defmode = RPMMIRE_STRCMP;
01789         else if (!strcmp(t, "regex"))
01790             defmode = RPMMIRE_REGEX;
01791         else if (!strcmp(t, "glob"))
01792             defmode = RPMMIRE_GLOB;
01793         else
01794             defmode = RPMMIRE_DEFAULT;
01795         t = _free(t);
01796      }
01797 
01798     if (mi == NULL || pattern == NULL)
01799         return rc;
01800 
01801     /* Leading '!' inverts pattern match sense, like "grep -v". */
01802     if (*pattern == '!') {
01803         notmatch = 1;
01804         pattern++;
01805     }
01806 
01807     nmire = mireNew(mode, tag);
01808 assert(nmire != NULL);
01809     allpat = mireDup(nmire->tag, &nmire->mode, pattern);
01810 
01811     if (nmire->mode == RPMMIRE_DEFAULT)
01812         nmire->mode = defmode;
01813 
01814     rc = mireRegcomp(nmire, allpat);
01815     if (rc)
01816         goto exit;
01817 
01818     if (mi->mi_re == NULL) {
01819         mi->mi_re = mireGetPool(_mirePool);
01820         mire = mireLink(mi->mi_re);
01821     } else {
01822         void *use =  mi->mi_re->_item.use;
01823         void *pool = mi->mi_re->_item.pool;
01824         mi->mi_re = xrealloc(mi->mi_re, (mi->mi_nre + 1) * sizeof(*mi->mi_re));
01825 if (_mire_debug)
01826 fprintf(stderr, "    mire %p[%u] realloc\n", mi->mi_re, mi->mi_nre+1);
01827         mire = mi->mi_re + mi->mi_nre;
01828         memset(mire, 0, sizeof(*mire));
01829         /* XXX ensure no segfault, copy the use/pool from 1st item. */
01830 /*@-assignexpose@*/
01831         mire->_item.use = use;
01832         mire->_item.pool = pool;
01833 /*@=assignexpose@*/
01834     }
01835     mi->mi_nre++;
01836     
01837     mire->mode = nmire->mode;
01838     mire->pattern = nmire->pattern;     nmire->pattern = NULL;
01839     mire->preg = nmire->preg;           nmire->preg = NULL;
01840     mire->cflags = nmire->cflags;
01841     mire->eflags = nmire->eflags;
01842     mire->fnflags = nmire->fnflags;
01843     mire->tag = nmire->tag;
01844     mire->notmatch = notmatch;
01845     /* XXX todo: permit PCRE patterns to be used. */
01846     mire->offsets = NULL;
01847     mire->noffsets = 0;
01848 
01849     if (mi->mi_nre > 1)
01850         qsort(mi->mi_re, mi->mi_nre, sizeof(*mi->mi_re), mireCmp);
01851 
01852 exit:
01853 if (_rpmmi_debug)
01854 fprintf(stderr, "<-- %s(%p, %u(%s), %u, \"%s\") rc %d mi_re %p[%u]\n", __FUNCTION__, mi, (unsigned)tag, tagName(tag), (unsigned)mode, pattern, rc, (mi ? mi->mi_re: NULL), (unsigned)(mi ? mi->mi_nre : 0));
01855     allpat = _free(allpat);
01856     nmire = mireFree(nmire);
01857     return rc;
01858 }
01859 
01865 static inline unsigned char nibble(char c)
01866         /*@*/
01867 {
01868     if (c >= '0' && c <= '9')
01869         return (unsigned char)(c - '0');
01870     if (c >= 'A' && c <= 'F')
01871         return (unsigned char)((int)(c - 'A') + 10);
01872     if (c >= 'a' && c <= 'f')
01873         return (unsigned char)((int)(c - 'a') + 10);
01874     return '\0';
01875 }
01876 
01883 /*@only@*/
01884 static char * bin2hex(const void *data, size_t size)
01885         /*@*/
01886 {
01887     static char hex[] = "0123456789abcdef";
01888     const char * s = data;
01889     char * t, * val;
01890     val = t = xmalloc(size * 2 + 1);
01891     while (size-- > 0) {
01892         unsigned i;
01893         i = (unsigned) *s++;
01894         *t++ = hex[ (i >> 4) & 0xf ];
01895         *t++ = hex[ (i     ) & 0xf ];
01896     }
01897     *t = '\0';
01898 
01899     return val;
01900 }
01901 
01907 /*@-onlytrans@*/        /* XXX miRE array, not refcounted. */
01908 static int mireSkip (const rpmmi mi)
01909         /*@globals internalState @*/
01910         /*@modifies mi->mi_re, internalState @*/
01911 {
01912     HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
01913     char numbuf[32];
01914     miRE mire;
01915     int ntags = 0;
01916     int nmatches = 0;
01917     int i;
01918     int rc;
01919 
01920     if (mi->mi_h == NULL)       /* XXX can't happen */
01921         return 1;
01922 
01923     /*
01924      * Apply tag tests, implicitly "||" for multiple patterns/values of a
01925      * single tag, implicitly "&&" between multiple tag patterns.
01926      */
01927     if ((mire = mi->mi_re) == NULL)
01928         return 0;
01929 
01930     for (i = 0; i < mi->mi_nre; i++, mire++) {
01931         int anymatch;
01932 
01933         he->tag = mire->tag;
01934 
01935         if (!headerGet(mi->mi_h, he, 0)) {
01936             if (he->tag != RPMTAG_EPOCH) {
01937                 ntags++;
01938                 continue;
01939             }
01940             he->t = RPM_UINT32_TYPE;
01941             he->p.ui32p = xcalloc(1, sizeof(*he->p.ui32p));
01942             he->c = 1;
01943         }
01944 
01945         anymatch = 0;           /* no matches yet */
01946         while (1) {
01947             unsigned j;
01948             switch (he->t) {
01949             case RPM_UINT8_TYPE:
01950                 for (j = 0; j < (unsigned) he->c; j++) {
01951                     sprintf(numbuf, "%u", (unsigned) he->p.ui8p[j]);
01952                     rc = mireRegexec(mire, numbuf, 0);
01953                     if ((rc >= 0 && !mire->notmatch) || (rc < 0 && mire->notmatch))
01954                         anymatch++;
01955                 }
01956                 /*@switchbreak@*/ break;
01957             case RPM_UINT16_TYPE:
01958                 for (j = 0; j < (unsigned) he->c; j++) {
01959                     sprintf(numbuf, "%u", (unsigned) he->p.ui16p[j]);
01960                     rc = mireRegexec(mire, numbuf, 0);
01961                     if ((rc >= 0 && !mire->notmatch) || (rc < 0 && mire->notmatch))
01962                         anymatch++;
01963                 }
01964                 /*@switchbreak@*/ break;
01965             case RPM_UINT32_TYPE:
01966                 for (j = 0; j < (unsigned) he->c; j++) {
01967                     sprintf(numbuf, "%u", (unsigned) he->p.ui32p[j]);
01968                     rc = mireRegexec(mire, numbuf, 0);
01969                     if ((rc >= 0 && !mire->notmatch) || (rc < 0 && mire->notmatch))
01970                         anymatch++;
01971                 }
01972                 /*@switchbreak@*/ break;
01973             case RPM_UINT64_TYPE:
01974 /*@-duplicatequals@*/
01975                 for (j = 0; j < (unsigned) he->c; j++) {
01976                     sprintf(numbuf, "%llu", (unsigned long long)he->p.ui64p[j]);
01977                     rc = mireRegexec(mire, numbuf, 0);
01978                     if ((rc >= 0 && !mire->notmatch) || (rc < 0 && mire->notmatch))
01979                         anymatch++;
01980                 }
01981 /*@=duplicatequals@*/
01982                 /*@switchbreak@*/ break;
01983             case RPM_STRING_TYPE:
01984                 rc = mireRegexec(mire, he->p.str, 0);
01985                 if ((rc >= 0 && !mire->notmatch) || (rc < 0 && mire->notmatch))
01986                     anymatch++;
01987                 /*@switchbreak@*/ break;
01988             case RPM_STRING_ARRAY_TYPE:
01989                 for (j = 0; j < (unsigned) he->c; j++) {
01990                     rc = mireRegexec(mire, he->p.argv[j], 0);
01991                     if ((rc >= 0 && !mire->notmatch) || (rc < 0 && mire->notmatch)) {
01992                         anymatch++;
01993                         /*@innerbreak@*/ break;
01994                     }
01995                 }
01996                 /*@switchbreak@*/ break;
01997             case RPM_BIN_TYPE:
01998             {   const char * s;
01999 assert(he->p.ptr != NULL);
02000                 s = bin2hex(he->p.ptr, he->c);
02001                 rc = mireRegexec(mire, s, 0);
02002                 if ((rc >= 0 && !mire->notmatch) || (rc < 0 && mire->notmatch))
02003                     anymatch++;
02004                 s = _free(s);
02005             }   /*@switchbreak@*/ break;
02006             case RPM_I18NSTRING_TYPE:
02007             default:
02008                 /*@switchbreak@*/ break;
02009             }
02010             if ((i+1) < mi->mi_nre && mire[0].tag == mire[1].tag) {
02011                 i++;
02012                 mire++;
02013                 /*@innercontinue@*/ continue;
02014             }
02015             /*@innerbreak@*/ break;
02016         }
02017 
02018         he->p.ptr = _free(he->p.ptr);
02019 
02020         if (anymatch)
02021             nmatches++;
02022         ntags++;
02023     }
02024 
02025     return (ntags > 0 && ntags == nmatches ? 0 : 1);
02026 }
02027 /*@=onlytrans@*/
02028 
02029 int rpmmiSetRewrite(rpmmi mi, int rewrite)
02030 {
02031     int rc;
02032     if (mi == NULL)
02033         return 0;
02034     rc = (mi->mi_cflags & DB_WRITECURSOR) ? 1 : 0;
02035     if (rewrite)
02036         mi->mi_cflags |= DB_WRITECURSOR;
02037     else
02038         mi->mi_cflags &= ~DB_WRITECURSOR;
02039     return rc;
02040 }
02041 
02042 int rpmmiSetModified(rpmmi mi, int modified)
02043 {
02044     int rc;
02045     if (mi == NULL)
02046         return 0;
02047     rc = mi->mi_modified;
02048     mi->mi_modified = modified;
02049     return rc;
02050 }
02051 
02052 /*@unchecked@*/
02053 static int _rpmmi_usermem = 1;
02054 
02055 static int rpmmiGet(dbiIndex dbi, DBC * dbcursor, DBT * kp, DBT * pk, DBT * vp,
02056                 unsigned int flags)
02057         /*@globals internalState @*/
02058         /*@modifies dbi, dbcursor, *kp, *pk, *vp, internalState @*/
02059 {
02060     int map;
02061     int rc;
02062 
02063     switch (dbi->dbi_rpmdb->db_api) {
02064     default:    map = 0;                break;
02065     case 3:     map = _rpmmi_usermem;   break;  /* Berkeley DB */
02066     }
02067 
02068     if (map) {
02069         static const int _prot = PROT_READ | PROT_WRITE;
02070         static const int _flags = MAP_PRIVATE| MAP_ANONYMOUS;
02071         static const int _fdno = -1;
02072         static const off_t _off = 0;
02073 
02074         memset(vp, 0, sizeof(*vp));
02075         vp->flags |= DB_DBT_USERMEM;
02076         rc = dbiGet(dbi, dbcursor, kp, vp, flags);
02077         if (rc == DB_BUFFER_SMALL) {
02078             size_t uhlen = vp->size;
02079             void * uh = mmap(NULL, uhlen, _prot, _flags, _fdno, _off);
02080             if (uh == NULL || uh == (void *)-1)
02081                 fprintf(stderr,
02082                     "==> mmap(%p[%u], 0x%x, 0x%x, %d, 0x%x) error(%d): %s\n",
02083                     NULL, (unsigned)uhlen, _prot, _flags, _fdno, (unsigned)_off,
02084                     errno, strerror(errno));
02085 
02086             vp->ulen = (u_int32_t)uhlen;
02087             vp->data = uh;
02088             if (dbi->dbi_primary && pk)
02089                 rc = dbiPget(dbi, dbcursor, kp, pk, vp, flags);
02090             else
02091                 rc = dbiGet(dbi, dbcursor, kp, vp, flags);
02092             if (rc == 0) {
02093                 if (mprotect(uh, uhlen, PROT_READ) != 0)
02094                     fprintf(stderr, "==> mprotect(%p[%u],0x%x) error(%d): %s\n",
02095                         uh, (unsigned)uhlen, PROT_READ,
02096                         errno, strerror(errno));
02097             } else {
02098                 if (munmap(uh, uhlen) != 0)
02099                     fprintf(stderr, "==> munmap(%p[%u]) error(%d): %s\n",
02100                         uh, (unsigned)uhlen, errno, strerror(errno));
02101             }
02102         }
02103     } else
02104         rc = dbiGet(dbi, dbcursor, kp, vp, flags);
02105 if (_rpmmi_debug || dbi->dbi_debug)
02106 fprintf(stderr, "<-- %s(%p(%s),%p,%p,%p,0x%x) rc %d\n", __FUNCTION__, dbi, tagName(dbi->dbi_rpmtag), dbcursor, kp, vp, flags, rc);
02107 
02108     return rc;
02109 }
02110 
02111 Header rpmmiNext(rpmmi mi)
02112 {
02113     dbiIndex dbi;
02114     DBT k = DBT_INIT;
02115     DBT p = DBT_INIT;
02116     DBT v = DBT_INIT;
02117     void * uh;
02118     size_t uhlen;
02119 rpmTag tag;
02120 unsigned int _flags;
02121     int map;
02122     int rc;
02123     int xx;
02124 
02125     if (mi == NULL)
02126         return NULL;
02127 
02128     /* Find the tag to open. */
02129     tag = (mi->mi_set == NULL && mi->mi_primary != NULL
02130                 ? mi->mi_rpmtag : RPMDBI_PACKAGES);
02131     dbi = dbiOpen(mi->mi_db, tag, 0);
02132     if (dbi == NULL)
02133         return NULL;
02134 
02135     switch (dbi->dbi_rpmdb->db_api) {
02136     default:    map = 0;                break;
02137     case 3:     map = _rpmmi_usermem;   break;  /* Berkeley DB */
02138     }
02139 
02140 if (_rpmmi_debug || dbi->dbi_debug)
02141 fprintf(stderr, "--> %s(%p) dbi %p(%s)\n", __FUNCTION__, mi, dbi, tagName(tag));
02142 
02143     /*
02144      * Cursors are per-iterator, not per-dbi, so get a cursor for the
02145      * iterator on 1st call. If the iteration is to rewrite headers, and the
02146      * CDB model is used for the database, then the cursor needs to
02147      * marked with DB_WRITECURSOR as well.
02148      */
02149     if (mi->mi_dbc == NULL) {
02150         xx = dbiCopen(dbi, dbiTxnid(dbi), &mi->mi_dbc, mi->mi_cflags);
02151         k.data = mi->mi_keyp;
02152         k.size = (u_int32_t)mi->mi_keylen;
02153 if (k.data && k.size == 0) k.size = (UINT32_T) strlen((char *)k.data);
02154 if (k.data && k.size == 0) k.size++;    /* XXX "/" fixup. */
02155         _flags = DB_SET;
02156     } else
02157         _flags = (mi->mi_setx ? DB_NEXT_DUP : DB_SET);
02158 
02159 next:
02160     if (mi->mi_set) {
02161         /* The set of header instances is known in advance. */
02162         if (!(mi->mi_setx < mi->mi_set->count))
02163             return NULL;
02164         mi->mi_offset = _hton_ui(dbiIndexRecordOffset(mi->mi_set, mi->mi_setx));
02165         mi->mi_bntag = dbiIndexRecordFileNumber(mi->mi_set, mi->mi_setx);
02166         mi->mi_setx++;
02167 
02168         /* If next header is identical, return it now. */
02169         if (mi->mi_offset == mi->mi_prevoffset && mi->mi_h != NULL)
02170             return mi->mi_h;
02171 
02172         /* Should this header be skipped? */
02173         if (mi->mi_bf != NULL
02174          && rpmbfChk(mi->mi_bf, &mi->mi_offset, sizeof(mi->mi_offset)) > 0)
02175             goto next;
02176 
02177         /* Fetch header by offset. */
02178         k.data = &mi->mi_offset;
02179         k.size = (UINT32_T)sizeof(mi->mi_offset);
02180         rc = rpmmiGet(dbi, mi->mi_dbc, &k, NULL, &v, DB_SET);
02181     }
02182     else if (dbi->dbi_primary) {
02183         rc = rpmmiGet(dbi, mi->mi_dbc, &k, &p, &v, _flags);
02184         switch (rc) {
02185         default:
02186 assert(0);
02187             /*@notreached@*/ break;
02188         case DB_NOTFOUND:
02189             return NULL;
02190             /*@notreached@*/ break;
02191         case 0:
02192             mi->mi_setx++;
02193 assert((size_t)p.size == sizeof(mi->mi_offset));
02194             memcpy(&mi->mi_offset, p.data, sizeof(mi->mi_offset));
02195             /* If next header is identical, return it now. */
02196             if (mi->mi_offset == mi->mi_prevoffset && mi->mi_h != NULL)
02197                 return mi->mi_h;
02198             break;
02199         }
02200         _flags = DB_NEXT_DUP;
02201     }
02202     else {
02203         /* Iterating Packages database. */
02204 assert(mi->mi_rpmtag == RPMDBI_PACKAGES);
02205 
02206         /* Fetch header with DB_NEXT. */
02207         /* Instance 0 is the largest header instance in legacy databases,
02208          * and must be skipped. */
02209         do {
02210             rc = rpmmiGet(dbi, mi->mi_dbc, &k, NULL, &v, DB_NEXT);
02211             if (rc == 0) {
02212 assert((size_t)k.size == sizeof(mi->mi_offset));
02213                 memcpy(&mi->mi_offset, k.data, sizeof(mi->mi_offset));
02214             }
02215         } while (rc == 0 && mi->mi_offset == 0);
02216     }
02217 
02218     /* Did the header blob load correctly? */
02219     if (rc)
02220         return NULL;
02221 
02222     /* Should this header be skipped? */
02223     if (mi->mi_set == NULL && mi->mi_bf != NULL
02224      && rpmbfChk(mi->mi_bf, &mi->mi_offset, sizeof(mi->mi_offset)) > 0)
02225         goto next;
02226 
02227     uh = v.data;
02228     uhlen = v.size;
02229     if (uh == NULL)
02230         return NULL;
02231 
02232     /* Rewrite current header (if necessary) and unlink. */
02233     xx = miFreeHeader(mi, dbi);
02234 
02235     if (map) {
02236 /*@-onlytrans@*/
02237         mi->mi_h = headerLoad(uh);
02238 /*@=onlytrans@*/
02239         if (mi->mi_h) {
02240             mi->mi_h->flags |= HEADERFLAG_MAPPED;
02241             mi->mi_h->flags |= HEADERFLAG_RDONLY;
02242         }
02243     } else
02244         mi->mi_h = headerCopyLoad(uh);
02245 
02246     if (mi->mi_h == NULL) {
02247         rpmlog(RPMLOG_ERR,
02248                 _("rpmdb: header #%u cannot be loaded -- skipping.\n"),
02249                 (unsigned)_ntoh_ui(mi->mi_offset));
02250         /* damaged header should not be reused */
02251         if (mi->mi_h) {
02252             (void)headerFree(mi->mi_h);
02253             mi->mi_h = NULL;
02254         }
02255         /* TODO: skip more mi_set records */
02256         goto next;
02257     }
02258 
02259     /* Skip this header if iterator selector (if any) doesn't match. */
02260     if (mireSkip(mi))
02261         goto next;
02262 
02263     /* Mark header with its instance number. */
02264     {   char origin[32];
02265         uint32_t hdrNum = _ntoh_ui(mi->mi_offset);
02266         sprintf(origin, "rpmdb (h#%u)", (unsigned)hdrNum);
02267         (void) headerSetOrigin(mi->mi_h, origin);
02268         (void) headerSetInstance(mi->mi_h, hdrNum);
02269     }
02270 
02271     mi->mi_prevoffset = mi->mi_offset;
02272     mi->mi_modified = 0;
02273 
02274 /*@-compdef -retalias -retexpose -usereleased @*/
02275     return mi->mi_h;
02276 /*@=compdef =retalias =retexpose =usereleased @*/
02277 }
02278 
02279 int rpmmiSort(rpmmi mi)
02280 {
02281     int rc = 0;
02282 
02283     if (mi && mi->mi_set && mi->mi_set->recs && mi->mi_set->count > 0) {
02284     /*
02285      * mergesort is much (~10x with lots of identical basenames) faster
02286      * than pure quicksort, but glibc uses msort_with_tmp() on stack.
02287      */
02288     if (mi->mi_set->count > 1) {
02289 #if defined(__GLIBC__)
02290             qsort(mi->mi_set->recs, mi->mi_set->count,
02291                 sizeof(*mi->mi_set->recs), hdrNumCmp);
02292 #else
02293             rpm_mergesort(mi->mi_set->recs, mi->mi_set->count,
02294                 sizeof(*mi->mi_set->recs), hdrNumCmp);
02295 #endif
02296         }
02297         mi->mi_sorted = 1;
02298 #ifdef  NOTNOW
02299     {   struct _dbiIndexItem * rec;
02300         int i;
02301         for (i = 0, rec = mi->mi_set->recs; i < mi->mi_set->count; i++, rec++) {
02302             fprintf(stderr, "\t%p[%u] =  %p: %u %u %u\n", mi->mi_set->recs,
02303                         i, rec, rec->hdrNum, rec->tagNum, rec->fpNum);
02304         }
02305     }
02306 #endif
02307     }
02308     return rc;
02309 }
02310 
02311 /* XXX TODO: a Bloom Filter on removed packages created once, not each time. */
02312 int rpmmiPrune(rpmmi mi, uint32_t * hdrNums, int nHdrNums, int sorted)
02313 {
02314     int rc = (mi == NULL || hdrNums == NULL || nHdrNums <= 0);
02315 
02316     if (!rc) {
02317         int i;
02318         if (mi->mi_bf == NULL) {
02319             static size_t nRemoves = 2 * 8192;  /* XXX population estimate */
02320             static double e = 1.0e-4;
02321             size_t m = 0;
02322             size_t k = 0;
02323             rpmbfParams(nRemoves, e, &m, &k);
02324             mi->mi_bf = rpmbfNew(m, k, 0);
02325         }
02326         for (i = 0; i < nHdrNums; i++) {
02327             uint32_t mi_offset = _hton_ui(hdrNums[i]);
02328             int xx = rpmbfAdd(mi->mi_bf, &mi_offset, sizeof(mi_offset));
02329 assert(xx == 0);
02330         }
02331     }
02332 
02333 if (_rpmmi_debug)
02334 fprintf(stderr, "<-- %s(%p, %p[%u], %d) rc %d h# %u\n", __FUNCTION__, mi, hdrNums, (unsigned)nHdrNums, sorted, rc, (unsigned) (hdrNums ? hdrNums[0] : 0));
02335     return rc;
02336 }
02337 
02338 int rpmmiGrow(rpmmi mi, const uint32_t * hdrNums, int nHdrNums)
02339 {
02340     int rc = (mi == NULL || hdrNums == NULL || nHdrNums <= 0);
02341 
02342     if (!rc) {
02343         if (mi->mi_set == NULL)
02344             mi->mi_set = xcalloc(1, sizeof(*mi->mi_set));
02345         (void) dbiAppendSet(mi->mi_set, hdrNums, nHdrNums, sizeof(*hdrNums), 0);
02346     }
02347 
02348 if (_rpmmi_debug)
02349 fprintf(stderr, "<-- %s(%p, %p[%u]) rc %d h# %u\n", __FUNCTION__, mi, hdrNums, (unsigned)nHdrNums, rc, (unsigned) (hdrNums ? hdrNums[0] : 0));
02350     return rc;
02351 }
02352 
02353 /*@-dependenttrans -exposetrans -globstate @*/
02354 rpmmi rpmmiInit(rpmdb db, rpmTag tag, const void * keyp, size_t keylen)
02355 {
02356     HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
02357     rpmmi mi = NULL;
02358     dbiIndexSet set = NULL;
02359     dbiIndex dbi = NULL;
02360     int usePatterns = 0;
02361 
02362     if (db == NULL)
02363         goto exit;
02364 
02365     (void) rpmdbCheckSignals();
02366 
02367     /* XXX Control for whether patterns are permitted. */
02368     switch (tag) {
02369     default:    break;
02370     case 2:     /* XXX HACK to remove RPMDBI_LABEL from RPM. */
02371         /* XXX rpmlog message warning RPMDBI is deprecated? */
02372         tag = RPMTAG_NVRA;
02373         /*@fallthrough@*/
02374     case RPMTAG_NVRA:
02375 #ifdef  NOTYET          /* XXX JS unit tests break. */
02376     case RPMTAG_NAME:
02377 #endif
02378 #ifdef  RPM_VENDOR_MANDRIVA_XXX /* XXX rpm -qf /non/existent breaks */
02379     case RPMTAG_PROVIDENAME:
02380 #endif
02381     case RPMTAG_VERSION:
02382     case RPMTAG_RELEASE:
02383     case RPMTAG_ARCH:
02384     case RPMTAG_OS:
02385     case RPMTAG_GROUP:
02386         usePatterns = 1;
02387         break;
02388 #ifndef NOTYET          /* XXX can't quite do this yet */
02389     /* XXX HACK to remove the existing complexity of RPMTAG_BASENAMES */
02390     case RPMTAG_BASENAMES:
02391         if (keyp == NULL)       /* XXX rpmdbFindFpList & grow are speshul */
02392             break;
02393         tag = RPMTAG_FILEPATHS;
02394         /*@fallthrough@*/
02395 #endif
02396     case RPMTAG_FILEPATHS:
02397     case RPMTAG_DIRNAMES:
02398         usePatterns = 1;
02399         break;
02400     }
02401 
02402     dbi = dbiOpen(db, tag, 0);
02403 #ifdef  NOTYET  /* XXX non-configured tag indices force NULL return */
02404 assert(dbi != NULL);                                    /* XXX sanity */
02405 #else
02406     if (dbi == NULL)
02407         goto exit;
02408 #endif
02409 
02410     mi = rpmmiGetPool(_rpmmiPool);
02411     (void)rpmioLinkPoolItem((rpmioItem)mi, __FUNCTION__, __FILE__, __LINE__);
02412 
02413 if (_rpmmi_debug || (dbi && dbi->dbi_debug))
02414 fprintf(stderr, "--> %s(%p, %s, %p[%u]=\"%s\") dbi %p mi %p\n", __FUNCTION__, db, tagName(tag), keyp, (unsigned)keylen, (keylen == 0 || ((const char *)keyp)[keylen] == '\0' ? (const char *)keyp : "???"), dbi, mi);
02415 
02416     /* Chain cursors for teardown on abnormal exit. */
02417     mi->mi_next = rpmmiRock;
02418     rpmmiRock = mi;
02419 
02420     if (tag == RPMDBI_PACKAGES && keyp == NULL) {
02421         /* Special case #1: sequentially iterate Packages database. */
02422         assert(keylen == 0);
02423         /* This should be the only case when (set == NULL). */
02424     }
02425     else if (tag == RPMDBI_PACKAGES) {
02426         /* Special case #2: will fetch header instance. */
02427         uint32_t hdrNum;
02428 assert(keylen == sizeof(hdrNum));
02429         memcpy(&hdrNum, keyp, sizeof(hdrNum));
02430         /* The set has only one element, which is hdrNum. */
02431         set = xcalloc(1, sizeof(*set));
02432         set->count = 1;
02433         set->recs = xcalloc(1, sizeof(set->recs[0]));
02434         set->recs[0].hdrNum = hdrNum;
02435     }
02436     else if (keyp == NULL) {
02437         /* XXX Special case #3: empty iterator with rpmmiGrow() */
02438         assert(keylen == 0);
02439     }
02440     else if (usePatterns) {
02441         /* XXX Special case #4: gather primary keys with patterns. */
02442         rpmRC rc;
02443 
02444         rc = dbiFindMatches(dbi, keyp, &set);
02445 
02446         if ((rc  && rc != RPMRC_NOTFOUND) || set == NULL || set->count < 1) { /* error or empty set */
02447             set = dbiFreeIndexSet(set);
02448             rpmmiRock = mi->mi_next;
02449             mi->mi_next = NULL;
02450             mi = (rpmmi)rpmioFreePoolItem((rpmioItem)mi, __FUNCTION__, __FILE__, __LINE__);
02451             return NULL;
02452         }
02453     }
02454     else if (dbi && dbi->dbi_primary != NULL) {
02455         /* XXX Special case #5: secondary ndex associated with primary table. */
02456     }
02457     else {
02458         /* Common case: retrieve join keys. */
02459 assert(0);
02460     }
02461 
02462 /*@-assignexpose@*/
02463     mi->mi_db = rpmdbLink(db, __FUNCTION__);
02464 /*@=assignexpose@*/
02465     mi->mi_rpmtag = tag;
02466 
02467     mi->mi_dbc = NULL;
02468     mi->mi_set = set;
02469     mi->mi_setx = 0;
02470     mi->mi_count = (set ? set->count : 0);
02471 
02472     mi->mi_primary = (dbi && dbi->dbi_primary
02473                 ? xstrdup(dbi->dbi_primary) : NULL);
02474 
02475     /* Coerce/swab integer keys. Save key ind keylen in the iterator. */
02476     switch (tagType(tag) & 0xffff) {
02477     case RPM_UINT8_TYPE:
02478 assert(keylen == sizeof(he->p.ui8p[0]));
02479         mi->mi_keylen = sizeof(he->p.ui32p[0]); /* XXX coerce to uint32_t */
02480         mi->mi_keyp = he->p.ui32p = xmalloc(mi->mi_keylen);
02481         he->p.ui32p[0] = 0;
02482         memcpy(&he->p.ui8p[3], keyp, keylen);
02483         break;
02484     case RPM_UINT16_TYPE:
02485 assert(keylen == sizeof(he->p.ui16p[0]));
02486         mi->mi_keylen = sizeof(he->p.ui32p[0]); /* XXX coerce to uint32_t */
02487         mi->mi_keyp = he->p.ui32p = xmalloc(mi->mi_keylen);
02488         he->p.ui32p[0] = 0;
02489         memcpy(&he->p.ui16p[1], keyp, keylen);
02490         he->p.ui16p[1] = _hton_us(he->p.ui16p[1]);
02491         break;
02492 #if !defined(__LCLINT__)        /* LCL: buggy */
02493     case RPM_UINT32_TYPE:
02494 assert(keylen == sizeof(he->p.ui32p[0]));
02495         mi->mi_keylen = keylen;
02496 /*@-mayaliasunique@*/
02497         mi->mi_keyp = memcpy((he->p.ui32p = xmalloc(keylen)), keyp, keylen);
02498 /*@=mayaliasunique@*/
02499         he->p.ui32p[0] = _hton_ui(he->p.ui32p[0]);
02500         break;
02501     case RPM_UINT64_TYPE:
02502 assert(keylen == sizeof(he->p.ui64p[0]));
02503         mi->mi_keylen = keylen;
02504 /*@-mayaliasunique@*/
02505         mi->mi_keyp = memcpy((he->p.ui64p = xmalloc(keylen)), keyp, keylen);
02506 /*@=mayaliasunique@*/
02507         {   uint32_t _tmp = he->p.ui32p[0];
02508             he->p.ui32p[0] = _hton_ui(he->p.ui32p[1]);
02509             he->p.ui32p[1] = _hton_ui(_tmp);
02510         }
02511         break;
02512 #endif  /* !defined(__LCLINT__) */
02513     case RPM_BIN_TYPE:
02514     case RPM_I18NSTRING_TYPE:       /* XXX never occurs */
02515     case RPM_STRING_TYPE:
02516     case RPM_STRING_ARRAY_TYPE:
02517     default:
02518         mi->mi_keylen = keylen;
02519         if (keyp)
02520             mi->mi_keyp = keylen > 0
02521                 ? memcpy(xmalloc(keylen), keyp, keylen) : xstrdup(keyp) ;
02522         else
02523             mi->mi_keyp = NULL;
02524         break;
02525     }
02526     he->p.ptr = NULL;
02527 
02528     mi->mi_h = NULL;
02529     mi->mi_sorted = 0;
02530     mi->mi_cflags = 0;
02531     mi->mi_modified = 0;
02532     mi->mi_prevoffset = 0;
02533     mi->mi_offset = 0;
02534     mi->mi_nre = 0;
02535     mi->mi_re = NULL;
02536 
02537 exit:
02538     return mi;
02539 }
02540 /*@=dependenttrans =exposetrans =globstate @*/
02541 
02542 /* XXX psm.c */
02543 int rpmdbRemove(rpmdb db, /*@unused@*/ int rid, uint32_t hdrNum,
02544                 /*@unused@*/ rpmts ts)
02545 {
02546     HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
02547     Header h = NULL;
02548     sigset_t signalMask;
02549     dbiIndex dbi;
02550     size_t dbix;
02551     int rc = RPMRC_FAIL;                /* XXX RPMRC */
02552     int xx;
02553 
02554     if (db == NULL)
02555         return RPMRC_OK;                /* XXX RPMRC */
02556 
02557     /* Retrieve header for use by associated secondary index callbacks. */
02558     {   rpmmi mi;
02559         mi = rpmmiInit(db, RPMDBI_PACKAGES, &hdrNum, sizeof(hdrNum));
02560         h = rpmmiNext(mi);
02561         if (h)
02562             h = headerLink(h);
02563         mi = rpmmiFree(mi);
02564     }
02565 
02566     if (h == NULL) {
02567         rpmlog(RPMLOG_ERR, _("%s: cannot read header at 0x%x\n"),
02568               "rpmdbRemove", (unsigned)hdrNum);
02569         return RPMRC_FAIL;              /* XXX RPMRC */
02570     }
02571 
02572     he->tag = RPMTAG_NVRA;
02573     xx = headerGet(h, he, 0);
02574     rpmlog(RPMLOG_DEBUG, "  --- h#%8u %s\n", (unsigned)hdrNum, he->p.str);
02575     he->p.ptr = _free(he->p.ptr);
02576 
02577     (void) blockSignals(db, &signalMask);
02578 
02579     dbix = db->db_ndbi - 1;
02580     if (db->db_tags != NULL)
02581     do {
02582         tagStore_t dbiTag = db->db_tags + dbix;
02583         DBC * dbcursor;
02584         DBT k;
02585         DBT v;
02586         uint32_t ui;
02587 
02588         dbi = NULL;
02589         dbcursor = NULL;
02590         (void) memset(&k, 0, sizeof(k));
02591         (void) memset(&v, 0, sizeof(v));
02592         (void) memset(he, 0, sizeof(*he));
02593         he->tag = dbiTag->tag;
02594 
02595         switch (he->tag) {
02596         default:
02597             /* Don't bother if tag is not present. */
02598             if (!headerGet(h, he, 0))
02599                 /*@switchbreak@*/ break;
02600 
02601             dbi = dbiOpen(db, he->tag, 0);
02602             if (dbi == NULL)    goto exit;
02603 
02604             he->p.ptr = _free(he->p.ptr);
02605             /*@switchbreak@*/ break;
02606         case RPMDBI_AVAILABLE:  /* Filter out temporary databases */
02607         case RPMDBI_ADDED:
02608         case RPMDBI_REMOVED:
02609         case RPMDBI_DEPENDS:
02610         case RPMDBI_SEQNO:
02611             /*@switchbreak@*/ break;
02612         case RPMDBI_PACKAGES:
02613             if (db->db_export != NULL)
02614                 xx = db->db_export(db, h, 0);
02615 
02616             ui = _hton_ui(hdrNum);
02617             k.data = &ui;
02618             k.size = (UINT32_T) sizeof(ui);
02619 
02620             /* New h ref for use by associated secondary index callbacks. */
02621             db->db_h = headerLink(h);
02622 
02623             dbi = dbiOpen(db, he->tag, 0);
02624             if (dbi == NULL)    goto exit;
02625 
02626             rc = dbiCopen(dbi, dbiTxnid(dbi), &dbcursor, DB_WRITECURSOR);
02627             rc = dbiGet(dbi, dbcursor, &k, &v, DB_SET);
02628             if (!rc)
02629                 rc = dbiDel(dbi, dbcursor, &k, &v, 0);
02630             xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
02631 
02632             /* Unreference db_h used by associated secondary index callbacks. */
02633             (void) headerFree(db->db_h);
02634             db->db_h = NULL;
02635 
02636             if (!dbi->dbi_no_dbsync)
02637                 xx = dbiSync(dbi, 0);
02638 
02639             /*@switchbreak@*/ break;
02640         }
02641     } while (dbix-- > 0);
02642 
02643     /* Unreference header used by associated secondary index callbacks. */
02644     (void) headerFree(h);
02645     h = NULL;
02646     rc = RPMRC_OK;              /* XXX RPMRC */
02647 
02648 exit:
02649     (void) unblockSignals(db, &signalMask);
02650     return rc;
02651 }
02652 
02653 /* XXX install.c */
02654 int rpmdbAdd(rpmdb db, int iid, Header h, /*@unused@*/ rpmts ts)
02655 {
02656     HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
02657     sigset_t signalMask;
02658     dbiIndex dbi;
02659     size_t dbix;
02660     uint32_t hdrNum = headerGetInstance(h);
02661     int rc = RPMRC_FAIL;                /* XXX RPMRC */
02662     int xx;
02663 
02664     if (db == NULL)
02665         return RPMRC_OK;                /* XXX RPMRC */
02666 
02667 if (_rpmdb_debug)
02668 fprintf(stderr, "--> %s(%p, %u, %p, %p) h# %u\n", __FUNCTION__, db, (unsigned)iid, h, ts, (unsigned)hdrNum);
02669 
02670 assert(headerIsEntry(h, RPMTAG_REMOVETID) == 0);        /* XXX sanity */
02671 
02672     /* Add the install transaction id. */
02673     if (iid != 0 && iid != -1) {
02674         rpmuint32_t tid[2];
02675         tid[0] = iid;
02676         tid[1] = 0;
02677         he->tag = RPMTAG_INSTALLTID;
02678         he->t = RPM_UINT32_TYPE;
02679         he->p.ui32p = tid;
02680         he->c = 2;
02681         if (!headerIsEntry(h, he->tag))
02682 /*@-compmempass@*/
02683            xx = headerPut(h, he, 0);
02684 /*@=compmempass@*/
02685     }
02686 
02687 /* XXX pubkeys used to set RPMTAG_PACKAGECOLOR here. */
02688 assert(headerIsEntry(h, RPMTAG_PACKAGECOLOR) != 0);     /* XXX sanity */
02689 
02690     (void) blockSignals(db, &signalMask);
02691 
02692     /* Assign a primary Packages key for new Header's. */
02693     if (hdrNum == 0) {
02694         int64_t seqno = 0;
02695 
02696         dbi = dbiOpen(db, RPMDBI_SEQNO, 0);
02697         if (dbi == NULL) goto exit;
02698 
02699         if ((xx = dbiSeqno(dbi, &seqno, 0)) == 0) {
02700             hdrNum = seqno;
02701             (void) headerSetInstance(h, hdrNum);
02702         } else
02703             goto exit;
02704     }
02705 
02706 /* XXX ensure that the header instance is set persistently. */
02707 if (hdrNum == 0) {
02708 assert(hdrNum > 0);
02709 assert(hdrNum == headerGetInstance(h));
02710 }
02711 
02712     dbi = dbiOpen(db, RPMDBI_PACKAGES, 0);
02713     if (dbi == NULL) goto exit;
02714 
02715     dbix = db->db_ndbi - 1;
02716     if (db->db_tags != NULL)
02717     do {
02718         tagStore_t dbiTag = db->db_tags + dbix;
02719         DBC * dbcursor;
02720         DBT k;
02721         DBT v;
02722         uint32_t ui;
02723 
02724         dbi = NULL;
02725         dbcursor = NULL;
02726         (void) memset(&k, 0, sizeof(k));
02727         (void) memset(&v, 0, sizeof(v));
02728         (void) memset(he, 0, sizeof(*he));
02729         he->tag = dbiTag->tag;
02730 
02731         switch (he->tag) {
02732         default:
02733             /* Don't bother if tag is not present. */
02734             if (!headerGet(h, he, 0))
02735                 /*@switchbreak@*/ break;
02736 
02737             dbi = dbiOpen(db, he->tag, 0);
02738             if (dbi == NULL) goto exit;
02739 
02740             he->p.ptr = _free(he->p.ptr);
02741             /*@switchbreak@*/ break;
02742         case RPMDBI_AVAILABLE:  /* Filter out temporary databases */
02743         case RPMDBI_ADDED:
02744         case RPMDBI_REMOVED:
02745         case RPMDBI_DEPENDS:
02746         case RPMDBI_SEQNO:
02747             /*@switchbreak@*/ break;
02748         case RPMDBI_PACKAGES:
02749             if (db->db_export != NULL)
02750                 xx = db->db_export(db, h, 1);
02751 
02752             ui = _hton_ui(hdrNum);
02753             k.data = (void *) &ui;
02754             k.size = (UINT32_T) sizeof(ui);
02755 
02756             {   size_t len = 0;
02757                 v.data = headerUnload(h, &len);
02758 assert(v.data != NULL);
02759                 v.size = (UINT32_T) len;
02760             }
02761 
02762             /* New h ref for use by associated secondary index callbacks. */
02763             db->db_h = headerLink(h);
02764 
02765             dbi = dbiOpen(db, he->tag, 0);
02766             if (dbi == NULL) goto exit;
02767 
02768             xx = dbiCopen(dbi, dbiTxnid(dbi), &dbcursor, DB_WRITECURSOR);
02769             xx = dbiPut(dbi, dbcursor, &k, &v, DB_KEYLAST);
02770             xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
02771 
02772             /* Unreference db_h used by associated secondary index callbacks. */
02773             (void) headerFree(db->db_h);
02774             db->db_h = NULL;
02775 
02776             if (!dbi->dbi_no_dbsync)
02777                 xx = dbiSync(dbi, 0);
02778 
02779             v.data = _free(v.data); /* headerUnload */
02780             v.size = 0;
02781             /*@switchbreak@*/ break;
02782         }
02783 
02784     } while (dbix-- > 0);
02785     rc = RPMRC_OK;                      /* XXX RPMRC */
02786 
02787 exit:
02788     (void) unblockSignals(db, &signalMask);
02789     return rc;
02790 }