rpm  5.4.4
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_DEPCACHE:
00689         case RPMDBI_BTREE:
00690         case RPMDBI_HASH:
00691         case RPMDBI_QUEUE:
00692         case RPMDBI_RECNO:
00693         case RPMDBI_HEAP:
00694             continue;
00695             /*@notreached@*/ /*@switchbreak@*/ break;
00696         default:
00697             /*@switchbreak@*/ break;
00698         }
00699         (void) dbiOpen(db, db->db_tags[dbix].tag, db->db_flags);
00700     }
00701   }
00702     return rc;
00703 }
00704 
00705 int rpmdbBlockDBI(rpmdb db, int tag)
00706 {
00707     rpmTag tagn = (rpmTag)(tag >= 0 ? tag : -tag);
00708     size_t dbix;
00709 
00710     if (db == NULL || db->_dbi == NULL)
00711         return 0;
00712 
00713     if (db->db_tags != NULL)
00714     for (dbix = 0; dbix < db->db_ndbi; dbix++) {
00715         if (db->db_tags[dbix].tag != tagn)
00716             continue;
00717         db->db_tags[dbix].tag = tag;
00718         return 0;
00719     }
00720     return 0;
00721 }
00722 
00723 int rpmdbCloseDBI(rpmdb db, int tag)
00724 {
00725     size_t dbix;
00726     int rc = 0;
00727 
00728     if (db == NULL || db->_dbi == NULL)
00729         return 0;
00730 
00731     if (db->db_tags != NULL)
00732     for (dbix = 0; dbix < db->db_ndbi; dbix++) {
00733         if (db->db_tags[dbix].tag != (rpmTag)tag)
00734             continue;
00735         if (db->_dbi[dbix] != NULL) {
00736             int xx;
00737             /*@-unqualifiedtrans@*/             /* FIX: double indirection. */
00738             xx = dbiClose(db->_dbi[dbix], 0);
00739             if (xx && rc == 0) rc = xx;
00740             db->_dbi[dbix] = NULL;
00741             /*@=unqualifiedtrans@*/
00742         }
00743         break;
00744     }
00745     return rc;
00746 }
00747 
00748 /* XXX query.c, rpminstall.c, verify.c */
00749 /*@-incondefs@*/
00750 int rpmdbClose(rpmdb db)
00751         /*@globals rpmdbRock @*/
00752         /*@modifies rpmdbRock @*/
00753 {
00754     static const char msg[] = "rpmdbClose";
00755     rpmdb * prev, next;
00756     size_t dbix;
00757     int rc = 0;
00758 
00759     if (db == NULL)
00760         return rc;
00761 
00762     yarnPossess(db->_item.use);
00763 /*@-modfilesys@*/
00764 if (_rpmdb_debug)
00765 fprintf(stderr, "--> db %p -- %ld %s at %s:%u\n", db, yarnPeekLock(db->_item.use), msg, __FILE__, __LINE__);
00766 
00767     /*@-usereleased@*/
00768     if (yarnPeekLock(db->_item.use) <= 1L) {
00769 
00770         if (db->_dbi)
00771         for (dbix = db->db_ndbi; dbix;) {
00772             int xx;
00773             dbix--;
00774             if (db->_dbi[dbix] == NULL)
00775                 continue;
00776             /*@-unqualifiedtrans@*/             /* FIX: double indirection. */
00777             xx = dbiClose(db->_dbi[dbix], 0);
00778             if (xx && rc == 0) rc = xx;
00779             db->_dbi[dbix] = NULL;
00780             /*@=unqualifiedtrans@*/
00781         }
00782         db->db_errpfx = _free(db->db_errpfx);
00783         db->db_root = _free(db->db_root);
00784         db->db_home = _free(db->db_home);
00785         db->db_tags = tagStoreFree(db->db_tags, db->db_ndbi);
00786         db->_dbi = _free(db->_dbi);
00787         db->db_ndbi = 0;
00788 
00789 /*@-newreftrans@*/
00790         prev = &rpmdbRock;
00791         while ((next = *prev) != NULL && next != db)
00792             prev = &next->db_next;
00793         if (next) {
00794 /*@i@*/     *prev = next->db_next;
00795             next->db_next = NULL;
00796         }
00797 /*@=newreftrans@*/
00798 
00799         if (rpmdbRock == NULL && rpmmiRock == NULL) {
00800             /* Last close uninstalls special signal handling. */
00801             (void) rpmsqEnable(-SIGHUP, NULL);
00802             (void) rpmsqEnable(-SIGINT, NULL);
00803             (void) rpmsqEnable(-SIGTERM,        NULL);
00804             (void) rpmsqEnable(-SIGQUIT,        NULL);
00805             (void) rpmsqEnable(-SIGPIPE,        NULL);
00806             /* Pending signals strike here. */
00807             (void) rpmdbCheckSignals();
00808         }
00809 
00810     /*@=usereleased@*/
00811         db = (rpmdb)rpmioPutPool((rpmioItem)db);
00812     } else
00813         yarnTwist(db->_item.use, BY, -1);
00814 
00815     return rc;
00816 }
00817 /*@=incondefs@*/
00818 
00824 static const char * rpmdbURIPath(const char *uri)
00825         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
00826         /*@modifies rpmGlobalMacroContext, fileSystem, internalState @*/
00827 {
00828     const char * s = rpmGetPath(uri, NULL);
00829     ARGV_t av = NULL;
00830     int xx = argvSplit(&av, s, ":");
00831     const char * fn = NULL;
00832     /* XXX av contains a colon separated path split, use the 1st path. */
00833     urltype ut = urlPath(av[0], &fn);
00834     
00835 xx = xx;
00836 
00837     switch (ut) {
00838     case URL_IS_PATH:
00839     case URL_IS_UNKNOWN:
00840         fn = xstrdup(av[0]);
00841         break;
00842     case URL_IS_DASH:
00843     case URL_IS_HKP:
00844     case URL_IS_FTP:
00845     case URL_IS_HTTP:
00846     case URL_IS_HTTPS:
00847     case URL_IS_MONGO:  /* XXX FIXME */
00848     default:
00849         /* HACK: strip the URI prefix for these schemes. */
00850         fn = rpmGetPath(fn, NULL);
00851         break;
00852     }
00853 
00854     /* Convert relative to absolute paths. */
00855     if (ut != URL_IS_PATH)      /* XXX permit file:///... URI's */
00856     if (fn && *fn && *fn != '/') {
00857         char dn[PATH_MAX];
00858         char *t;
00859         dn[0] = '\0';
00860         if ((t = Realpath(".", dn)) != NULL) {
00861             t += strlen(dn);
00862             if (t > dn && t[-1] != '/')
00863                 *t++ = '/';
00864             t = stpncpy(t, fn, (sizeof(dn) - (t - dn)));
00865             *t = '\0';
00866             fn = _free(fn);
00867             fn = rpmGetPath(dn, NULL);
00868         }
00869     }
00870 
00871     av = argvFree(av);
00872     s = _free(s);
00873 assert(fn != NULL);
00874     return fn;
00875 }
00876 
00877 #define _DB_ROOT        "/"
00878 #define _DB_HOME        "%{?_dbpath}"
00879 #define _DB_FLAGS       0
00880 #define _DB_MODE        0
00881 #define _DB_PERMS       0644
00882 
00883 #define _DB_MAJOR       3
00884 #define _DB_ERRPFX      "rpmdb"
00885 
00886 /*@-exportheader -globs -mods @*/
00887 /*@only@*/ /*@null@*/
00888 rpmdb rpmdbNew(/*@kept@*/ /*@null@*/ const char * root,
00889                 /*@kept@*/ /*@null@*/ const char * home,
00890                 int mode, mode_t perms, int flags)
00891         /*@*/
00892 {
00893     rpmdb db = rpmdbGetPool(_rpmdbPool);
00894     const char * epfx = _DB_ERRPFX;
00895 
00896 /*@-modfilesys@*/ /*@-nullpass@*/
00897 if (_rpmdb_debug)
00898 fprintf(stderr, "==> rpmdbNew(%s, %s, 0x%x, 0%o, 0x%x) db %p\n", root, home, mode, perms, flags, db);
00899 /*@=modfilesys@*/ /*@=nullpass@*/
00900 
00901     if (!(perms & 0600)) perms = 0644;  /* XXX sanity */
00902 
00903     db->db_root = rpmdbURIPath( (root && *root ? root : _DB_ROOT) );
00904     db->db_home = rpmdbURIPath( (home && *home ? home : _DB_HOME) );
00905 
00906     if (!(db->db_home && db->db_home[0] && db->db_home[0] != '%')) {
00907         rpmlog(RPMLOG_ERR, _("no dbpath has been set\n"));
00908         db->db_root = _free(db->db_root);
00909         db->db_home = _free(db->db_home);
00910         db = (rpmdb) rpmioPutPool((rpmioItem)db);
00911         /*@-globstate@*/ return NULL; /*@=globstate@*/
00912     }
00913 
00914     db->db_flags = (flags >= 0) ? flags : _DB_FLAGS;
00915     db->db_mode = (mode >= 0) ? mode : _DB_MODE;
00916     db->db_perms = (perms > 0)  ? perms : _DB_PERMS;
00917     db->db_api = _DB_MAJOR;
00918     db->db_errpfx = rpmExpand( (epfx && *epfx ? epfx : _DB_ERRPFX), NULL);
00919 
00920     db->db_remove_env = 0;
00921     db->db_chrootDone = 0;
00922     db->db_maxkey = 0;
00923     db->db_errcall = NULL;
00924     db->db_errfile = NULL;
00925     db->db_malloc = NULL;
00926     db->db_realloc = NULL;
00927     db->db_free = NULL;
00928     db->db_export = rpmdbExportInfo;
00929     db->db_h = NULL;
00930 
00931     db->db_next = NULL;
00932     db->db_opens = 0;
00933 
00934     db->db_dbenv = NULL;
00935     db->db_txn = NULL;
00936     db->db_logc = NULL;
00937     db->db_mpf = NULL;
00938 
00939     dbiTagsInit(&db->db_tags, &db->db_ndbi);
00940     db->_dbi = xcalloc(db->db_ndbi, sizeof(*db->_dbi));
00941 
00942     memset(&db->db_getops, 0, sizeof(db->db_getops));
00943     memset(&db->db_putops, 0, sizeof(db->db_putops));
00944     memset(&db->db_delops, 0, sizeof(db->db_delops));
00945 
00946     /*@-globstate@*/
00947     return rpmdbLink(db, __FUNCTION__);
00948     /*@=globstate@*/
00949 }
00950 /*@=exportheader =globs =mods @*/
00951 
00952 static int rpmdbOpenDatabase(/*@null@*/ const char * prefix,
00953                 /*@null@*/ const char * dbpath,
00954                 int _dbapi, /*@null@*/ /*@out@*/ rpmdb *dbp,
00955                 int mode, mode_t perms, int flags)
00956         /*@globals rpmdbRock, rpmGlobalMacroContext, h_errno,
00957                 fileSystem, internalState @*/
00958         /*@modifies rpmdbRock, *dbp, rpmGlobalMacroContext,
00959                 fileSystem, internalState @*/
00960 {
00961     rpmdb db;
00962     int rc;
00963     int xx;
00964 
00965     /* Insure that _dbapi has one of -1, 1, 2, or 3 */
00966     if (_dbapi < -1 || _dbapi > 4)
00967         _dbapi = -1;
00968     if (_dbapi == 0)
00969         _dbapi = 1;
00970 
00971     if (dbp)
00972         *dbp = NULL;
00973     if (mode & O_WRONLY) 
00974         return 1;
00975 
00976     db = rpmdbNew(prefix, dbpath, mode, perms, flags);
00977     if (db == NULL)
00978         return 1;
00979 
00980     if (rpmdbRock == NULL && rpmmiRock == NULL) {
00981         /* First open installs special signal handling. */
00982         (void) rpmsqEnable(SIGHUP,      NULL);
00983         (void) rpmsqEnable(SIGINT,      NULL);
00984         (void) rpmsqEnable(SIGTERM,     NULL);
00985         (void) rpmsqEnable(SIGQUIT,     NULL);
00986         (void) rpmsqEnable(SIGPIPE,     NULL);
00987     }
00988 
00989 /*@-assignexpose -newreftrans@*/
00990 /*@i@*/ db->db_next = rpmdbRock;
00991         rpmdbRock = db;
00992 /*@=assignexpose =newreftrans@*/
00993 
00994     db->db_api = _dbapi;
00995 
00996     {   size_t dbix;
00997 
00998         rc = 0;
00999         if (db->db_tags != NULL)
01000         for (dbix = 0; rc == 0 && dbix < db->db_ndbi; dbix++) {
01001             tagStore_t dbiTag = db->db_tags + dbix;
01002             rpmTag tag = dbiTag->tag;
01003             dbiIndex dbi;
01004 
01005             /* Filter out temporary databases */
01006             switch (tag) {
01007             case RPMDBI_AVAILABLE:
01008             case RPMDBI_ADDED:
01009             case RPMDBI_REMOVED:
01010             case RPMDBI_DEPCACHE:
01011                 continue;
01012                 /*@notreached@*/ /*@switchbreak@*/ break;
01013             default:
01014                 /*@switchbreak@*/ break;
01015             }
01016 
01017             dbi = dbiOpen(db, tag, 0);
01018             if (dbi == NULL) {
01019                 rc = -2;
01020                 break;
01021             }
01022 
01023             switch (tag) {
01024             case RPMDBI_PACKAGES:
01025                 if (dbi == NULL) rc |= 1;
01026 #if 0
01027                 /* XXX open only Packages, indices created on the fly. */
01028                 if (db->db_api == 3)
01029 #endif
01030                     goto exit;
01031                 /*@notreached@*/ /*@switchbreak@*/ break;
01032             case RPMTAG_NAME:
01033                 if (dbi == NULL) rc |= 1;
01034                 /*@switchbreak@*/ break;
01035             default:
01036                 /*@switchbreak@*/ break;
01037             }
01038         }
01039     }
01040 
01041 exit:
01042     if (rc || dbp == NULL)
01043         xx = rpmdbClose(db);
01044     else {
01045 /*@-assignexpose -newreftrans@*/
01046 /*@i@*/ *dbp = db;
01047 /*@=assignexpose =newreftrans@*/
01048     }
01049 
01050     return rc;
01051 }
01052 
01053 /* XXX python/rpmmodule.c */
01054 int rpmdbOpen (const char * prefix, rpmdb *dbp, int mode, mode_t perms)
01055 {
01056     int _dbapi = rpmExpandNumeric("%{?_dbapi}");
01057     return rpmdbOpenDatabase(prefix, NULL, _dbapi, dbp, mode, perms, 0);
01058 }
01059 
01060 int rpmdbCount(rpmdb db, rpmTag tag, const void * keyp, size_t keylen)
01061 {
01062     unsigned int count = 0;
01063     DBC * dbcursor = NULL;
01064     DBT k = DBT_INIT;
01065     DBT v = DBT_INIT;
01066     dbiIndex dbi;
01067     int rc;
01068     int xx;
01069 
01070     if (db == NULL || keyp == NULL)
01071         return 0;
01072 
01073     dbi = dbiOpen(db, tag, 0);
01074     if (dbi == NULL)
01075         return 0;
01076 
01077     if (keylen == 0)
01078         keylen = strlen(keyp);
01079 
01080 /*@-temptrans@*/
01081     k.data = (void *) keyp;
01082 /*@=temptrans@*/
01083     k.size = (UINT32_T) keylen;
01084 
01085     xx = dbiCopen(dbi, dbiTxnid(dbi), &dbcursor, 0);
01086     rc = dbiGet(dbi, dbcursor, &k, &v, DB_SET);
01087     switch (rc) {
01088     case 0:
01089         rc = dbiCount(dbi, dbcursor, &count, 0);
01090         if (rc != 0)
01091             rc = -1;
01092         else
01093             rc = count;
01094         break;
01095     case DB_NOTFOUND:
01096         rc = 0;
01097         break;
01098     default:
01099         rpmlog(RPMLOG_ERR, _("error(%d) getting records from %s index\n"),
01100                 rc, tagName(dbi->dbi_rpmtag));
01101         rc = -1;
01102         break;
01103     }
01104     xx = dbiCclose(dbi, dbcursor, 0);
01105     dbcursor = NULL;
01106     return rc;
01107 }
01108 
01109 /* XXX python/upgrade.c, install.c, uninstall.c */
01110 int rpmdbCountPackages(rpmdb db, const char * N)
01111 {
01112     return rpmdbCount(db, RPMTAG_NAME, N, strlen(N));
01113 }
01114 
01115 /* Return pointer to first RE character (or NUL terminator) */
01116 static const char * stemEnd(const char * s)
01117         /*@*/
01118 {
01119     int c;
01120 
01121     while ((c = (int)*s)) {
01122         switch (c) {
01123         case '.':
01124         case '^':
01125         case '$':
01126         case '?':
01127         case '*':
01128         case '+':
01129         case '|':
01130         case '[':
01131         case '(':
01132         case '{':
01133         case '\0':
01134             goto exit;
01135             /*@notreached@*/ /*@switchbreak@*/ break;
01136         case '\\':
01137             s++;
01138             if (*s == '\0') goto exit;
01139             /*@fallthrough@*/
01140         default:
01141             /*@switchbreak@*/ break;
01142         }
01143         s++;
01144     }
01145 exit:
01146     return s;
01147 }
01148 
01149 /*@only@*/
01150 static const char * _str2PCREpat(/*@null@*/ const char *_pre, const char *s,
01151                 /*@null@*/ const char *_post)
01152         /*@*/
01153 {
01154     static const char _REchars[] = "^.*(|)[]+?{}$";
01155     size_t nt = 0;
01156     const char * se;
01157     char * t;
01158     char * te;
01159 
01160     /* Find the PCRE pattern length, including escapes. */
01161     for (se = s; *se != '\0'; se++, nt++)
01162         if (strchr(_REchars, *se)) nt++;
01163     nt += strlen(_pre) + strlen(_post);
01164 
01165     /* Build the PCRE pattern, escaping characters as needed. */
01166     te = t = xmalloc(nt + 1);
01167     te = stpcpy(te, _pre);
01168     for (se = s; *se != '\0'; *te++ = *se++)
01169         if (strchr(_REchars, *se)) *te++ = '\\';
01170     te = stpcpy(te, _post);
01171     *te = '\0';
01172 
01173 /*@-dependenttrans@*/
01174     return t;
01175 /*@=dependenttrans@*/
01176 }
01177 
01188 static int dbiMireKeys(rpmdb db, rpmTag tag, rpmMireMode mode,
01189                 /*@null@*/ const char * pat,
01190                 /*@null@*/ dbiIndexSet * matches,
01191                 /*@null@*/ const char *** argvp)
01192         /*@globals internalState @*/
01193         /*@modifies *matches, *argvp, internalState @*/
01194 {
01195     DBC * dbcursor = NULL;
01196     DBT k = DBT_INIT;
01197     DBT p = DBT_INIT;
01198     DBT v = DBT_INIT;
01199     dbiIndex dbi;
01200     miRE mire = NULL;
01201     uint32_t _flags = DB_NEXT;
01202     ARGV_t av = NULL;
01203     dbiIndexSet set = NULL;
01204     const char * b = NULL;
01205     size_t nb = 0;
01206     int ret = 1;                /* assume error */
01207     int rc;
01208     int xx;
01209 
01210     dbi = dbiOpen(db, tag, 0);
01211     if (dbi == NULL)
01212         goto exit;
01213 
01214 if (_rpmmi_debug || dbi->dbi_debug)
01215 fprintf(stderr, "--> %s(%p, %s(%u), %d, \"%s\", %p, %p)\n", __FUNCTION__, db, tagName(tag), (unsigned)tag, mode, pat, matches, argvp);
01216 
01217     if (pat) {
01218 
01219         mire = mireNew(mode, 0);
01220         xx = mireRegcomp(mire, pat);
01221 
01222         /* Initialize the secondary retrieval key. */
01223         switch (mode) {
01224         default:
01225 assert(0);                      /* XXX sanity */
01226             /*@notreached@*/ break;
01227         case RPMMIRE_GLOB:
01228             break;
01229         case RPMMIRE_REGEX:
01230         case RPMMIRE_PCRE:
01231             if (*pat == '^') pat++;
01232 
01233             /* If partial match on stem won't help, just iterate. */
01234             nb = stemEnd(pat) - pat;
01235             if (nb == 0) {
01236                 k.doff = 0;
01237                 goto doit;
01238             }
01239 
01240             /* Remove the escapes in the stem. */
01241             {   char *be;
01242                 b = be = xmalloc(nb + 1);
01243                 while (nb--) {
01244                     if ((*be = *pat++) != '\\')
01245                         be++;
01246                 }
01247                 *be = '\0';
01248             }
01249             nb = strlen(b);
01250 
01251             /* Set stem length for partial match retrieve. */
01252             k.flags = DB_DBT_PARTIAL;
01253             k.dlen = nb;
01254             k.size = nb;
01255             k.data = (void *) b;
01256             _flags = DB_SET_RANGE;
01257             break;
01258         case RPMMIRE_STRCMP:
01259             k.size = (UINT32_T) strlen(pat);
01260             k.data = (void *) pat;
01261             _flags = DB_SET;
01262             break;
01263         }
01264     }
01265 
01266 doit:
01267     v.flags |= DB_DBT_PARTIAL;
01268 
01269     xx = dbiCopen(dbi, dbiTxnid(dbi), &dbcursor, 0);
01270 
01271     /* Iterate over matches, collecting primary/secondary keys. */
01272     while ((rc = dbiPget(dbi, dbcursor, &k, &p, &v, _flags)) == 0) {
01273         uint32_t hdrNum;
01274         const char * s;
01275         size_t ns;
01276 
01277         if (_flags == DB_SET) _flags = DB_NEXT_DUP;
01278         if (b != NULL && nb > 0) {
01279 
01280             /* Exit if the stem doesn't match. */
01281             if (k.size < nb || memcmp(b, k.data, nb))
01282                 break;
01283 
01284             /* Retrieve the full record after DB_SET_RANGE. */
01285             if (_flags == DB_SET_RANGE) {
01286                 memset (&k, 0, sizeof(k));
01287                 xx = dbiPget(dbi, dbcursor, &k, &p, &v, DB_CURRENT);
01288                 _flags = DB_NEXT;
01289             }
01290         }
01291 
01292         /* Get the secondary key. */
01293         s = (const char * ) k.data;
01294         ns = k.size;
01295 
01296         /* Skip if not matched. */
01297         if (mire && mireRegexec(mire, s, ns) < 0)
01298             continue;
01299             
01300         /* Get a native endian copy of the primary package key. */
01301         memcpy(&hdrNum, p.data, sizeof(hdrNum));
01302         hdrNum = _ntoh_ui(hdrNum);
01303 
01304         /* Collect primary keys. */
01305         if (matches) {
01306             if (set == NULL)
01307                 set = xcalloc(1, sizeof(*set));
01308             /* XXX TODO: sort/uniqify set? */
01309             (void) dbiAppendSet(set, &hdrNum, 1, sizeof(hdrNum), 0);
01310         }
01311 
01312         /* Collect secondary keys. */
01313         if (argvp) {
01314             char * a = memcpy(xmalloc(ns+1), s, ns);
01315             a[ns] = '\0';
01316             xx = argvAdd(&av, a);
01317             a = _free(a);
01318         }
01319     }
01320 
01321     xx = dbiCclose(dbi, dbcursor, 0);
01322     dbcursor = NULL;
01323 
01324     switch (rc) {
01325     case 0:
01326     case DB_NOTFOUND:
01327         ret = 0;
01328         break;
01329     default:
01330         rpmlog(RPMLOG_ERR, _("error(%d) getting keys from %s index\n"),
01331                 rc, tagName(dbi->dbi_rpmtag));
01332         break;
01333     }
01334 
01335 exit:
01336     if (ret == 0) {
01337         if (matches) {
01338             /* XXX TODO: sort/uniqify set? */
01339             *matches = set;
01340             set = NULL;
01341         }
01342         if (argvp)
01343             xx = argvAppend(argvp, av);
01344     }
01345     set = dbiFreeIndexSet(set);
01346     av = argvFree(av);
01347     b = _free(b);
01348     mire = mireFree(mire);
01349 if (_rpmmi_debug || dbi->dbi_debug)
01350 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));
01351     return ret;
01352 }
01353 
01354 int rpmdbMireApply(rpmdb db, rpmTag tag, rpmMireMode mode, const char * pat,
01355                 const char *** argvp)
01356 {
01357     int rc = dbiMireKeys(db, tag, mode, pat, NULL, argvp);
01358 if (_rpmmi_debug)
01359 fprintf(stderr, "<-- %s(%p, %s(%u), %d, \"%s\", %p) rc %d\n", __FUNCTION__, db, tagName(tag), (unsigned)tag, mode, pat, argvp, rc);
01360     return rc;
01361 }
01362 
01363 int rpmmiGrowBasename(rpmmi mi, const char * bn)
01364 {
01365     rpmTag _tag = RPMTAG_BASENAMES;
01366     rpmMireMode _mode = RPMMIRE_STRCMP;
01367     dbiIndexSet set = NULL;
01368     unsigned int i;
01369     int rc = 1;         /* assume error */
01370 
01371     if (mi == NULL || mi->mi_db == NULL || bn == NULL || *bn == '\0')
01372         goto exit;
01373 
01374 #ifdef  NOTYET
01375 assert(mi->mi_rpmtag == _tag);
01376 #endif
01377     /* Retrieve set of headers that contain the base name. */
01378     rc = dbiMireKeys(mi->mi_db, _tag, _mode, bn, &set, NULL);
01379     if (rc == 0 && set != NULL) {
01380         rpmuint32_t tagNum = hashFunctionString(0, bn, 0);
01381         /* Set tagNum to the hash of the basename. */
01382         for (i = 0; i < set->count; i++)
01383             set->recs[i].tagNum = tagNum;
01384         if (mi->mi_set == NULL)
01385             mi->mi_set = xcalloc(1, sizeof(*mi->mi_set));
01386         (void) dbiAppendSet(mi->mi_set, set->recs, set->count, sizeof(*set->recs), 0);
01387     }
01388     rc = 0;
01389 
01390 exit:
01391 if (_rpmmi_debug)
01392 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));
01393     set = dbiFreeIndexSet(set);
01394     return rc;
01395 }
01396 
01404 static rpmRC dbiFindMatches(dbiIndex dbi,
01405                 const char * pat, /*@out@*/ dbiIndexSet * matches)
01406         /*@*/
01407 {
01408     const char * s = pat;
01409     size_t ns = (s ? strlen(s) : 0);
01410     DBC * dbcursor = NULL;
01411     rpmRC rc = RPMRC_NOTFOUND;
01412     int ret;
01413     int xx;
01414 
01415     if (ns == 0) goto exit;
01416 
01417     xx = dbiCopen(dbi, dbiTxnid(dbi), &dbcursor, 0);
01418 
01419     /* Add ^...$ *RE anchors. Escape pattern characters. */
01420     {   rpmTag tag = dbi->dbi_rpmtag;
01421         rpmMireMode mode = RPMMIRE_PCRE;
01422         static const char _post_NVRA[] = "(-[^-]+-[^-]+\\.[^.]+|-[^-]+\\.[^.]+|\\.[^.]+|)$";
01423         const char * _pat;
01424 
01425         switch (tag) {
01426         default:
01427             mode = RPMMIRE_PCRE;
01428             _pat = _str2PCREpat("^", s, ".*$");
01429             break;
01430         case RPMTAG_NVRA:
01431             mode = RPMMIRE_PCRE;
01432             _pat = (s[0] == '^' || s[ns-1] == '$')
01433                 ? xstrdup(s)
01434                 : _str2PCREpat("^", s, _post_NVRA);
01435             break;
01436         case RPMTAG_FILEPATHS:
01437             if (s[0] == '^' || s[ns-1] == '$')
01438                 mode = RPMMIRE_PCRE;
01439             else
01440 #ifdef NOTYET
01441             if (s[0] == '/' && Glob_pattern_p(s, 1))
01442                 mode = RPMMIRE_GLOB;
01443             else
01444 #endif
01445                 mode = RPMMIRE_STRCMP;
01446             _pat = xstrdup(s);
01447             break;
01448         }
01449 
01450         ret = dbiMireKeys(dbi->dbi_rpmdb, tag, mode, _pat, matches, NULL);
01451 
01452         _pat = _free(_pat);
01453     }
01454 
01455     switch (ret) {
01456     case 0:             rc = RPMRC_OK;          break;
01457     case DB_NOTFOUND:   rc = RPMRC_NOTFOUND;    break;
01458     default:            rc = RPMRC_FAIL;
01459         rpmlog(RPMLOG_ERR, _("error(%d) getting records from %s index\n"),
01460                 ret, tagName(dbi->dbi_rpmtag));
01461         break;
01462     }
01463 
01464     xx = dbiCclose(dbi, dbcursor, 0);
01465     dbcursor = NULL;
01466 
01467 exit:
01468 /*@-unqualifiedtrans@*/ /* FIX: double indirection */
01469     if (rc != RPMRC_OK && matches && *matches)
01470         *matches = dbiFreeIndexSet(*matches);
01471 /*@=unqualifiedtrans@*/
01472     return rc;
01473 }
01474 
01475 void * dbiStatsAccumulator(dbiIndex dbi, int opx)
01476 {
01477     void * sw = NULL;
01478     switch (opx) {
01479     case 14:    /* RPMTS_OP_DBGET */
01480         sw = &dbi->dbi_rpmdb->db_getops;
01481         break;
01482     case 15:    /* RPMTS_OP_DBPUT */
01483         sw = &dbi->dbi_rpmdb->db_putops;
01484         break;
01485     default:    /* XXX wrong, but let's not return NULL. */
01486     case 16:    /* RPMTS_OP_DBDEL */
01487         sw = &dbi->dbi_rpmdb->db_delops;
01488         break;
01489     }
01490     return sw;
01491 }
01492 
01501 static int miFreeHeader(rpmmi mi, dbiIndex dbi)
01502         /*@globals fileSystem, internalState @*/
01503         /*@modifies mi, dbi, fileSystem, internalState @*/
01504 {
01505     int rc = 0;
01506 
01507     if (mi == NULL || mi->mi_h == NULL)
01508         return 0;
01509 
01510     if (dbi && mi->mi_dbc && mi->mi_modified && mi->mi_prevoffset) {
01511         DBT k = DBT_INIT;
01512         DBT v = DBT_INIT;
01513         int xx;
01514 
01515 /*@i@*/ k.data = (void *) &mi->mi_prevoffset;
01516         k.size = (UINT32_T) sizeof(mi->mi_prevoffset);
01517         {   size_t len = 0;
01518             v.data = headerUnload(mi->mi_h, &len);
01519             v.size = (UINT32_T) len;
01520         }
01521 
01522         if (v.data != NULL) {
01523             sigset_t signalMask;
01524             (void) blockSignals(dbi->dbi_rpmdb, &signalMask);
01525             rc = dbiPut(dbi, mi->mi_dbc, &k, &v, DB_KEYLAST);
01526             if (rc) {
01527                 rpmlog(RPMLOG_ERR,
01528                         _("error(%d) storing record h#%u into %s\n"),
01529                         rc, (unsigned)_ntoh_ui(mi->mi_prevoffset),
01530                         tagName(dbi->dbi_rpmtag));
01531             }
01532             xx = dbiSync(dbi, 0);
01533             (void) unblockSignals(dbi->dbi_rpmdb, &signalMask);
01534         }
01535         v.data = _free(v.data); /* headerUnload */
01536         v.size = 0;
01537     }
01538 
01539     (void)headerFree(mi->mi_h);
01540     mi->mi_h = NULL;
01541 
01542 /*@-nullstate@*/
01543     return rc;
01544 /*@=nullstate@*/
01545 }
01546 
01547 static void rpmmiFini(void * _mi)
01548         /*@globals rpmmiRock @*/
01549         /*@modifies _mi, rpmmiRock @*/
01550 {
01551     rpmmi mi = _mi;
01552     rpmmi * prev, next;
01553     dbiIndex dbi;
01554     int xx;
01555 
01556     prev = &rpmmiRock;
01557     while ((next = *prev) != NULL && next != mi)
01558         prev = &next->mi_next;
01559     if (next) {
01560 /*@i@*/ *prev = next->mi_next;
01561         next->mi_next = NULL;
01562     }
01563 
01564     /* XXX NOTFOUND exits traverse here w mi->mi_db == NULL. b0rked imho. */
01565     if (mi->mi_db) {
01566         dbi = dbiOpen(mi->mi_db, RPMDBI_PACKAGES, 0);
01567 assert(dbi != NULL);                            /* XXX sanity */
01568 
01569         xx = miFreeHeader(mi, dbi);
01570 
01571         if (mi->mi_dbc)
01572             xx = dbiCclose(dbi, mi->mi_dbc, 0);
01573         mi->mi_dbc = NULL;
01574         /* XXX rpmdbUnlink will not do.
01575          * NB: must be called after rpmmiRock cleanup.
01576          */
01577         (void) rpmdbClose(mi->mi_db);
01578         mi->mi_db = NULL;
01579     }
01580 
01581     (void) mireFreeAll(mi->mi_re, mi->mi_nre);
01582     mi->mi_re = NULL;
01583 
01584     (void) rpmbfFree(mi->mi_bf);
01585     mi->mi_bf = NULL;
01586     mi->mi_set = dbiFreeIndexSet(mi->mi_set);
01587 
01588     mi->mi_keyp = _free(mi->mi_keyp);
01589     mi->mi_keylen = 0;
01590     mi->mi_primary = _free(mi->mi_primary);
01591 
01592     /* XXX this needs to be done elsewhere, not within destructor. */
01593     (void) rpmdbCheckSignals();
01594 }
01595 
01596 /*@unchecked@*/ /*@only@*/ /*@null@*/
01597 rpmioPool _rpmmiPool;
01598 
01599 static rpmmi rpmmiGetPool(/*@null@*/ rpmioPool pool)
01600         /*@globals _rpmdbPool, fileSystem @*/
01601         /*@modifies pool, _rpmdbPool, fileSystem @*/
01602 {
01603     rpmmi mi;
01604 
01605     if (_rpmmiPool == NULL) {
01606         _rpmmiPool = rpmioNewPool("mi", sizeof(*mi), -1, _rpmmi_debug,
01607                         NULL, NULL, rpmmiFini);
01608         pool = _rpmmiPool;
01609     }
01610     mi = (rpmmi) rpmioGetPool(pool, sizeof(*mi));
01611     memset(((char *)mi)+sizeof(mi->_item), 0, sizeof(*mi)-sizeof(mi->_item));
01612     return mi;
01613 }
01614 
01615 uint32_t rpmmiInstance(rpmmi mi)
01616 {
01617     /* Get a native endian copy of the primary package key. */
01618     uint32_t rc = _ntoh_ui(mi ? mi->mi_offset : 0);
01619 if (_rpmmi_debug)
01620 fprintf(stderr, "<-- %s(%p) rc %u\n", __FUNCTION__, mi, (unsigned)rc);
01621     return rc;
01622 }
01623 
01624 uint32_t rpmmiBNTag(rpmmi mi) {
01625     uint32_t rc = (mi ? mi->mi_bntag : 0);
01626 if (_rpmmi_debug)
01627 fprintf(stderr, "<-- %s(%p) rc %u\n", __FUNCTION__, mi, (unsigned)rc);
01628     return rc;
01629 }
01630 
01631 unsigned int rpmmiCount(rpmmi mi)
01632 {
01633     unsigned int rc;
01634     int initDbc;
01635 
01636     /* XXX Secondary db associated with Packages needs cursor record count */
01637     if (mi && mi->mi_primary && ((initDbc = mi->mi_dbc == NULL) || mi->mi_count == 0)) {
01638         dbiIndex dbi = dbiOpen(mi->mi_db, mi->mi_rpmtag, 0);
01639         DBT k = DBT_INIT;
01640         DBT v = DBT_INIT;
01641         int xx;
01642         if(initDbc) {
01643 assert(dbi != NULL);    /* XXX dbiCopen doesn't handle dbi == NULL */
01644             xx = dbiCopen(dbi, dbiTxnid(dbi), &mi->mi_dbc, mi->mi_cflags);
01645         }
01646         k.data = mi->mi_keyp;
01647         k.size = (u_int32_t)mi->mi_keylen;
01648 if (k.data && k.size == 0) k.size = (UINT32_T) strlen((char *)k.data);
01649 if (k.data && k.size == 0) k.size++;    /* XXX "/" fixup. */
01650         if (!dbiGet(dbi, mi->mi_dbc, &k, &v, DB_SET))
01651             xx = dbiCount(dbi, mi->mi_dbc, &mi->mi_count, 0);
01652         if(initDbc)
01653             mi->mi_dbc = NULL;
01654     }
01655 
01656     rc = (mi ? mi->mi_count : 0);
01657 
01658 if (_rpmmi_debug)
01659 fprintf(stderr, "<-- %s(%p) rc %u\n", __FUNCTION__, mi, (unsigned)rc);
01660     return rc;
01661 }
01662 
01669 static int mireCmp(const void * a, const void * b)
01670 {
01671 /*@-castexpose @*/
01672     const miRE mireA = (const miRE) a;
01673     const miRE mireB = (const miRE) b;
01674 /*@=castexpose @*/
01675     return (mireA->tag - mireB->tag);
01676 }
01677 
01685 static /*@only@*/ char * mireDup(rpmTag tag, rpmMireMode *modep,
01686                         const char * pattern)
01687         /*@modifies *modep @*/
01688         /*@requires maxSet(modep) >= 0 @*/
01689 {
01690     const char * s;
01691     char * pat;
01692     char * t;
01693     int brackets;
01694     size_t nb;
01695     int c;
01696 
01697     switch (*modep) {
01698     default:
01699     case RPMMIRE_DEFAULT:
01700         if (tag == RPMTAG_DIRNAMES || tag == RPMTAG_BASENAMES
01701          || tag == RPMTAG_FILEPATHS)
01702         {
01703             *modep = RPMMIRE_GLOB;
01704             pat = xstrdup(pattern);
01705             break;
01706         }
01707 
01708         nb = strlen(pattern) + sizeof("^$");
01709 
01710         /* Find no. of bytes needed for pattern. */
01711         /* periods and plusses are escaped, splats become '.*' */
01712         c = (int) '\0';
01713         brackets = 0;
01714         for (s = pattern; *s != '\0'; s++) {
01715             switch (*s) {
01716             case '.':
01717             case '+':
01718             case '*':
01719                 if (!brackets) nb++;
01720                 /*@switchbreak@*/ break;
01721             case '\\':
01722                 s++;
01723                 /*@switchbreak@*/ break;
01724             case '[':
01725                 brackets = 1;
01726                 /*@switchbreak@*/ break;
01727             case ']':
01728                 if (c != (int) '[') brackets = 0;
01729                 /*@switchbreak@*/ break;
01730             }
01731             c = (int) *s;
01732         }
01733 
01734         pat = t = xmalloc(nb);
01735 
01736         if (pattern[0] != '^') *t++ = '^';
01737 
01738         /* Copy pattern, escaping periods, prefixing splats with period. */
01739         c = (int) '\0';
01740         brackets = 0;
01741         for (s = pattern; *s != '\0'; s++, t++) {
01742             switch (*s) {
01743             case '.':
01744             case '+':
01745                 if (!brackets) *t++ = '\\';
01746                 /*@switchbreak@*/ break;
01747             case '*':
01748                 if (!brackets) *t++ = '.';
01749                 /*@switchbreak@*/ break;
01750             case '\\':
01751                 *t++ = *s++;
01752                 /*@switchbreak@*/ break;
01753             case '[':
01754                 brackets = 1;
01755                 /*@switchbreak@*/ break;
01756             case ']':
01757                 if (c != (int) '[') brackets = 0;
01758                 /*@switchbreak@*/ break;
01759             }
01760             *t = *s;
01761             c = (int) *t;
01762         }
01763 
01764         if (s > pattern && s[-1] != '$') *t++ = '$';
01765         *t = '\0';
01766         *modep = RPMMIRE_REGEX;
01767         break;
01768     case RPMMIRE_STRCMP:
01769     case RPMMIRE_REGEX:
01770     case RPMMIRE_GLOB:
01771         pat = xstrdup(pattern);
01772         break;
01773     }
01774 
01775     return pat;
01776 }
01777 
01778 int rpmmiAddPattern(rpmmi mi, rpmTag tag,
01779                 rpmMireMode mode, const char * pattern)
01780 {
01781     static rpmMireMode defmode = (rpmMireMode)-1;
01782     miRE nmire = NULL;
01783     miRE mire = NULL;
01784     const char * allpat = NULL;
01785     int notmatch = 0;
01786     int rc = 0;
01787 
01788     if (defmode == (rpmMireMode)-1) {
01789         const char *t = rpmExpand("%{?_query_selector_match}", NULL);
01790 
01791         if (*t == '\0' || !strcmp(t, "default"))
01792             defmode = RPMMIRE_DEFAULT;
01793         else if (!strcmp(t, "strcmp"))
01794             defmode = RPMMIRE_STRCMP;
01795         else if (!strcmp(t, "regex"))
01796             defmode = RPMMIRE_REGEX;
01797         else if (!strcmp(t, "glob"))
01798             defmode = RPMMIRE_GLOB;
01799         else
01800             defmode = RPMMIRE_DEFAULT;
01801         t = _free(t);
01802      }
01803 
01804     if (mi == NULL || pattern == NULL)
01805         return rc;
01806 
01807     /* Leading '!' inverts pattern match sense, like "grep -v". */
01808     if (*pattern == '!') {
01809         notmatch = 1;
01810         pattern++;
01811     }
01812 
01813     nmire = mireNew(mode, tag);
01814 assert(nmire != NULL);
01815     allpat = mireDup(nmire->tag, &nmire->mode, pattern);
01816 
01817     if (nmire->mode == RPMMIRE_DEFAULT)
01818         nmire->mode = defmode;
01819 
01820     rc = mireRegcomp(nmire, allpat);
01821     if (rc)
01822         goto exit;
01823 
01824     if (mi->mi_re == NULL) {
01825         mi->mi_re = mireGetPool(_mirePool);
01826         mire = mireLink(mi->mi_re);
01827     } else {
01828         void *use =  mi->mi_re->_item.use;
01829         void *pool = mi->mi_re->_item.pool;
01830         mi->mi_re = xrealloc(mi->mi_re, (mi->mi_nre + 1) * sizeof(*mi->mi_re));
01831 if (_mire_debug)
01832 fprintf(stderr, "    mire %p[%u] realloc\n", mi->mi_re, mi->mi_nre+1);
01833         mire = mi->mi_re + mi->mi_nre;
01834         memset(mire, 0, sizeof(*mire));
01835         /* XXX ensure no segfault, copy the use/pool from 1st item. */
01836 /*@-assignexpose@*/
01837         mire->_item.use = use;
01838         mire->_item.pool = pool;
01839 /*@=assignexpose@*/
01840     }
01841     mi->mi_nre++;
01842     
01843     mire->mode = nmire->mode;
01844     mire->pattern = nmire->pattern;     nmire->pattern = NULL;
01845     mire->preg = nmire->preg;           nmire->preg = NULL;
01846     mire->cflags = nmire->cflags;
01847     mire->eflags = nmire->eflags;
01848     mire->fnflags = nmire->fnflags;
01849     mire->tag = nmire->tag;
01850     mire->notmatch = notmatch;
01851     /* XXX todo: permit PCRE patterns to be used. */
01852     mire->offsets = NULL;
01853     mire->noffsets = 0;
01854 
01855     if (mi->mi_nre > 1)
01856         qsort(mi->mi_re, mi->mi_nre, sizeof(*mi->mi_re), mireCmp);
01857 
01858 exit:
01859 if (_rpmmi_debug)
01860 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));
01861     allpat = _free(allpat);
01862     nmire = mireFree(nmire);
01863     return rc;
01864 }
01865 
01871 static inline unsigned char nibble(char c)
01872         /*@*/
01873 {
01874     if (c >= '0' && c <= '9')
01875         return (unsigned char)(c - '0');
01876     if (c >= 'A' && c <= 'F')
01877         return (unsigned char)((int)(c - 'A') + 10);
01878     if (c >= 'a' && c <= 'f')
01879         return (unsigned char)((int)(c - 'a') + 10);
01880     return '\0';
01881 }
01882 
01889 /*@only@*/
01890 static char * bin2hex(const void *data, size_t size)
01891         /*@*/
01892 {
01893     static char hex[] = "0123456789abcdef";
01894     const char * s = data;
01895     char * t, * val;
01896     val = t = xmalloc(size * 2 + 1);
01897     while (size-- > 0) {
01898         unsigned i;
01899         i = (unsigned) *s++;
01900         *t++ = hex[ (i >> 4) & 0xf ];
01901         *t++ = hex[ (i     ) & 0xf ];
01902     }
01903     *t = '\0';
01904 
01905     return val;
01906 }
01907 
01913 /*@-onlytrans@*/        /* XXX miRE array, not refcounted. */
01914 static int mireSkip (const rpmmi mi)
01915         /*@globals internalState @*/
01916         /*@modifies mi->mi_re, internalState @*/
01917 {
01918     HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
01919     char numbuf[32];
01920     miRE mire;
01921     int ntags = 0;
01922     int nmatches = 0;
01923     int i;
01924     int rc;
01925 
01926     if (mi->mi_h == NULL)       /* XXX can't happen */
01927         return 1;
01928 
01929     /*
01930      * Apply tag tests, implicitly "||" for multiple patterns/values of a
01931      * single tag, implicitly "&&" between multiple tag patterns.
01932      */
01933     if ((mire = mi->mi_re) == NULL)
01934         return 0;
01935 
01936     for (i = 0; i < mi->mi_nre; i++, mire++) {
01937         int anymatch;
01938 
01939         he->tag = mire->tag;
01940 
01941         if (!headerGet(mi->mi_h, he, 0)) {
01942             if (he->tag != RPMTAG_EPOCH) {
01943                 ntags++;
01944                 continue;
01945             }
01946             he->t = RPM_UINT32_TYPE;
01947             he->p.ui32p = xcalloc(1, sizeof(*he->p.ui32p));
01948             he->c = 1;
01949         }
01950 
01951         anymatch = 0;           /* no matches yet */
01952         while (1) {
01953             unsigned j;
01954             switch (he->t) {
01955             case RPM_UINT8_TYPE:
01956                 for (j = 0; j < (unsigned) he->c; j++) {
01957                     sprintf(numbuf, "%u", (unsigned) he->p.ui8p[j]);
01958                     rc = mireRegexec(mire, numbuf, 0);
01959                     if ((rc >= 0 && !mire->notmatch) || (rc < 0 && mire->notmatch))
01960                         anymatch++;
01961                 }
01962                 /*@switchbreak@*/ break;
01963             case RPM_UINT16_TYPE:
01964                 for (j = 0; j < (unsigned) he->c; j++) {
01965                     sprintf(numbuf, "%u", (unsigned) he->p.ui16p[j]);
01966                     rc = mireRegexec(mire, numbuf, 0);
01967                     if ((rc >= 0 && !mire->notmatch) || (rc < 0 && mire->notmatch))
01968                         anymatch++;
01969                 }
01970                 /*@switchbreak@*/ break;
01971             case RPM_UINT32_TYPE:
01972                 for (j = 0; j < (unsigned) he->c; j++) {
01973                     sprintf(numbuf, "%u", (unsigned) he->p.ui32p[j]);
01974                     rc = mireRegexec(mire, numbuf, 0);
01975                     if ((rc >= 0 && !mire->notmatch) || (rc < 0 && mire->notmatch))
01976                         anymatch++;
01977                 }
01978                 /*@switchbreak@*/ break;
01979             case RPM_UINT64_TYPE:
01980 /*@-duplicatequals@*/
01981                 for (j = 0; j < (unsigned) he->c; j++) {
01982                     sprintf(numbuf, "%llu", (unsigned long long)he->p.ui64p[j]);
01983                     rc = mireRegexec(mire, numbuf, 0);
01984                     if ((rc >= 0 && !mire->notmatch) || (rc < 0 && mire->notmatch))
01985                         anymatch++;
01986                 }
01987 /*@=duplicatequals@*/
01988                 /*@switchbreak@*/ break;
01989             case RPM_STRING_TYPE:
01990                 rc = mireRegexec(mire, he->p.str, 0);
01991                 if ((rc >= 0 && !mire->notmatch) || (rc < 0 && mire->notmatch))
01992                     anymatch++;
01993                 /*@switchbreak@*/ break;
01994             case RPM_STRING_ARRAY_TYPE:
01995                 for (j = 0; j < (unsigned) he->c; j++) {
01996                     rc = mireRegexec(mire, he->p.argv[j], 0);
01997                     if ((rc >= 0 && !mire->notmatch) || (rc < 0 && mire->notmatch)) {
01998                         anymatch++;
01999                         /*@innerbreak@*/ break;
02000                     }
02001                 }
02002                 /*@switchbreak@*/ break;
02003             case RPM_BIN_TYPE:
02004             {   const char * s;
02005 assert(he->p.ptr != NULL);
02006                 s = bin2hex(he->p.ptr, he->c);
02007                 rc = mireRegexec(mire, s, 0);
02008                 if ((rc >= 0 && !mire->notmatch) || (rc < 0 && mire->notmatch))
02009                     anymatch++;
02010                 s = _free(s);
02011             }   /*@switchbreak@*/ break;
02012             case RPM_I18NSTRING_TYPE:
02013             default:
02014                 /*@switchbreak@*/ break;
02015             }
02016             if ((i+1) < mi->mi_nre && mire[0].tag == mire[1].tag) {
02017                 i++;
02018                 mire++;
02019                 /*@innercontinue@*/ continue;
02020             }
02021             /*@innerbreak@*/ break;
02022         }
02023 
02024         he->p.ptr = _free(he->p.ptr);
02025 
02026         if (anymatch)
02027             nmatches++;
02028         ntags++;
02029     }
02030 
02031     return (ntags > 0 && ntags == nmatches ? 0 : 1);
02032 }
02033 /*@=onlytrans@*/
02034 
02035 int rpmmiSetRewrite(rpmmi mi, int rewrite)
02036 {
02037     int rc;
02038     if (mi == NULL)
02039         return 0;
02040     rc = (mi->mi_cflags & DB_WRITECURSOR) ? 1 : 0;
02041     if (rewrite)
02042         mi->mi_cflags |= DB_WRITECURSOR;
02043     else
02044         mi->mi_cflags &= ~DB_WRITECURSOR;
02045     return rc;
02046 }
02047 
02048 int rpmmiSetModified(rpmmi mi, int modified)
02049 {
02050     int rc;
02051     if (mi == NULL)
02052         return 0;
02053     rc = mi->mi_modified;
02054     mi->mi_modified = modified;
02055     return rc;
02056 }
02057 
02058 /*@unchecked@*/
02059 static int _rpmmi_usermem = 1;
02060 
02061 static int rpmmiGet(dbiIndex dbi, DBC * dbcursor, DBT * kp, DBT * pk, DBT * vp,
02062                 unsigned int flags)
02063         /*@globals internalState @*/
02064         /*@modifies dbi, dbcursor, *kp, *pk, *vp, internalState @*/
02065 {
02066     int map;
02067     int rc;
02068 
02069     switch (dbi->dbi_rpmdb->db_api) {
02070     default:    map = 0;                break;
02071     case 3:     map = _rpmmi_usermem;   break;  /* Berkeley DB */
02072     }
02073 
02074     if (map) {
02075         static const int _prot = PROT_READ | PROT_WRITE;
02076         static const int _flags = MAP_PRIVATE| MAP_ANONYMOUS;
02077         static const int _fdno = -1;
02078         static const off_t _off = 0;
02079 
02080         memset(vp, 0, sizeof(*vp));
02081         vp->flags |= DB_DBT_USERMEM;
02082         rc = dbiGet(dbi, dbcursor, kp, vp, flags);
02083         if (rc == DB_BUFFER_SMALL) {
02084             size_t uhlen = vp->size;
02085             void * uh = mmap(NULL, uhlen, _prot, _flags, _fdno, _off);
02086             if (uh == NULL || uh == (void *)-1)
02087                 fprintf(stderr,
02088                     "==> mmap(%p[%u], 0x%x, 0x%x, %d, 0x%x) error(%d): %s\n",
02089                     NULL, (unsigned)uhlen, _prot, _flags, _fdno, (unsigned)_off,
02090                     errno, strerror(errno));
02091 
02092             vp->ulen = (u_int32_t)uhlen;
02093             vp->data = uh;
02094             if (dbi->dbi_primary && pk)
02095                 rc = dbiPget(dbi, dbcursor, kp, pk, vp, flags);
02096             else
02097                 rc = dbiGet(dbi, dbcursor, kp, vp, flags);
02098             if (rc == 0) {
02099                 if (mprotect(uh, uhlen, PROT_READ) != 0)
02100                     fprintf(stderr, "==> mprotect(%p[%u],0x%x) error(%d): %s\n",
02101                         uh, (unsigned)uhlen, PROT_READ,
02102                         errno, strerror(errno));
02103             } else {
02104                 if (munmap(uh, uhlen) != 0)
02105                     fprintf(stderr, "==> munmap(%p[%u]) error(%d): %s\n",
02106                         uh, (unsigned)uhlen, errno, strerror(errno));
02107             }
02108         }
02109     } else
02110         rc = dbiGet(dbi, dbcursor, kp, vp, flags);
02111 if (_rpmmi_debug || dbi->dbi_debug)
02112 fprintf(stderr, "<-- %s(%p(%s),%p,%p,%p,0x%x) rc %d\n", __FUNCTION__, dbi, tagName(dbi->dbi_rpmtag), dbcursor, kp, vp, flags, rc);
02113 
02114     return rc;
02115 }
02116 
02117 Header rpmmiNext(rpmmi mi)
02118 {
02119     dbiIndex dbi;
02120     DBT k = DBT_INIT;
02121     DBT p = DBT_INIT;
02122     DBT v = DBT_INIT;
02123     void * uh;
02124     size_t uhlen;
02125 rpmTag tag;
02126 unsigned int _flags;
02127     int map;
02128     int rc;
02129     int xx;
02130 
02131     if (mi == NULL)
02132         return NULL;
02133 
02134     /* Find the tag to open. */
02135     tag = (mi->mi_set == NULL && mi->mi_primary != NULL
02136                 ? mi->mi_rpmtag : RPMDBI_PACKAGES);
02137     dbi = dbiOpen(mi->mi_db, tag, 0);
02138     if (dbi == NULL)
02139         return NULL;
02140 
02141     switch (dbi->dbi_rpmdb->db_api) {
02142     default:    map = 0;                break;
02143     case 3:     map = _rpmmi_usermem;   break;  /* Berkeley DB */
02144     }
02145 
02146 if (_rpmmi_debug || dbi->dbi_debug)
02147 fprintf(stderr, "--> %s(%p) dbi %p(%s)\n", __FUNCTION__, mi, dbi, tagName(tag));
02148 
02149     /*
02150      * Cursors are per-iterator, not per-dbi, so get a cursor for the
02151      * iterator on 1st call. If the iteration is to rewrite headers, and the
02152      * CDB model is used for the database, then the cursor needs to
02153      * marked with DB_WRITECURSOR as well.
02154      */
02155     if (mi->mi_dbc == NULL) {
02156         xx = dbiCopen(dbi, dbiTxnid(dbi), &mi->mi_dbc, mi->mi_cflags);
02157         k.data = mi->mi_keyp;
02158         k.size = (u_int32_t)mi->mi_keylen;
02159 if (k.data && k.size == 0) k.size = (UINT32_T) strlen((char *)k.data);
02160 if (k.data && k.size == 0) k.size++;    /* XXX "/" fixup. */
02161         _flags = DB_SET;
02162     } else
02163         _flags = (mi->mi_setx ? DB_NEXT_DUP : DB_SET);
02164 
02165 next:
02166     if (mi->mi_set) {
02167         /* The set of header instances is known in advance. */
02168         if (!(mi->mi_setx < mi->mi_set->count))
02169             return NULL;
02170         mi->mi_offset = _hton_ui(dbiIndexRecordOffset(mi->mi_set, mi->mi_setx));
02171         mi->mi_bntag = dbiIndexRecordFileNumber(mi->mi_set, mi->mi_setx);
02172         mi->mi_setx++;
02173 
02174         /* If next header is identical, return it now. */
02175         if (mi->mi_offset == mi->mi_prevoffset && mi->mi_h != NULL)
02176             return mi->mi_h;
02177 
02178         /* Should this header be skipped? */
02179         if (mi->mi_bf != NULL
02180          && rpmbfChk(mi->mi_bf, &mi->mi_offset, sizeof(mi->mi_offset)) > 0)
02181             goto next;
02182 
02183         /* Fetch header by offset. */
02184         k.data = &mi->mi_offset;
02185         k.size = (UINT32_T)sizeof(mi->mi_offset);
02186         rc = rpmmiGet(dbi, mi->mi_dbc, &k, NULL, &v, DB_SET);
02187     }
02188     else if (dbi->dbi_primary) {
02189         rc = rpmmiGet(dbi, mi->mi_dbc, &k, &p, &v, _flags);
02190         switch (rc) {
02191         default:
02192 assert(0);
02193             /*@notreached@*/ break;
02194         case DB_NOTFOUND:
02195             return NULL;
02196             /*@notreached@*/ break;
02197         case 0:
02198             mi->mi_setx++;
02199 assert((size_t)p.size == sizeof(mi->mi_offset));
02200             memcpy(&mi->mi_offset, p.data, sizeof(mi->mi_offset));
02201             /* If next header is identical, return it now. */
02202             if (mi->mi_offset == mi->mi_prevoffset && mi->mi_h != NULL)
02203                 return mi->mi_h;
02204             break;
02205         }
02206         _flags = DB_NEXT_DUP;
02207     }
02208     else {
02209         /* Iterating Packages database. */
02210 assert(mi->mi_rpmtag == RPMDBI_PACKAGES);
02211 
02212         /* Fetch header with DB_NEXT. */
02213         /* Instance 0 is the largest header instance in legacy databases,
02214          * and must be skipped. */
02215         do {
02216             rc = rpmmiGet(dbi, mi->mi_dbc, &k, NULL, &v, DB_NEXT);
02217             if (rc == 0) {
02218 assert((size_t)k.size == sizeof(mi->mi_offset));
02219                 memcpy(&mi->mi_offset, k.data, sizeof(mi->mi_offset));
02220             }
02221         } while (rc == 0 && mi->mi_offset == 0);
02222     }
02223 
02224     /* Did the header blob load correctly? */
02225     if (rc)
02226         return NULL;
02227 
02228     /* Should this header be skipped? */
02229     if (mi->mi_set == NULL && mi->mi_bf != NULL
02230      && rpmbfChk(mi->mi_bf, &mi->mi_offset, sizeof(mi->mi_offset)) > 0)
02231         goto next;
02232 
02233     uh = v.data;
02234     uhlen = v.size;
02235     if (uh == NULL)
02236         return NULL;
02237 
02238     /* Rewrite current header (if necessary) and unlink. */
02239     xx = miFreeHeader(mi, dbi);
02240 
02241     if (map) {
02242 /*@-onlytrans@*/
02243         mi->mi_h = headerLoad(uh);
02244 /*@=onlytrans@*/
02245         if (mi->mi_h) {
02246             mi->mi_h->flags |= HEADERFLAG_MAPPED;
02247             mi->mi_h->flags |= HEADERFLAG_RDONLY;
02248         }
02249     } else
02250         mi->mi_h = headerCopyLoad(uh);
02251 
02252     if (mi->mi_h == NULL) {
02253         rpmlog(RPMLOG_ERR,
02254                 _("rpmdb: header #%u cannot be loaded -- skipping.\n"),
02255                 (unsigned)_ntoh_ui(mi->mi_offset));
02256         /* damaged header should not be reused */
02257         if (mi->mi_h) {
02258             (void)headerFree(mi->mi_h);
02259             mi->mi_h = NULL;
02260         }
02261         /* TODO: skip more mi_set records */
02262         goto next;
02263     }
02264 
02265     /* Skip this header if iterator selector (if any) doesn't match. */
02266     if (mireSkip(mi))
02267         goto next;
02268 
02269     /* Mark header with its instance number. */
02270     {   char origin[32];
02271         uint32_t hdrNum = _ntoh_ui(mi->mi_offset);
02272         sprintf(origin, "rpmdb (h#%u)", (unsigned)hdrNum);
02273         (void) headerSetOrigin(mi->mi_h, origin);
02274         (void) headerSetInstance(mi->mi_h, hdrNum);
02275     }
02276 
02277     mi->mi_prevoffset = mi->mi_offset;
02278     mi->mi_modified = 0;
02279 
02280 /*@-compdef -retalias -retexpose -usereleased @*/
02281     return mi->mi_h;
02282 /*@=compdef =retalias =retexpose =usereleased @*/
02283 }
02284 
02285 int rpmmiSort(rpmmi mi)
02286 {
02287     int rc = 0;
02288 
02289     if (mi && mi->mi_set && mi->mi_set->recs && mi->mi_set->count > 0) {
02290     /*
02291      * mergesort is much (~10x with lots of identical basenames) faster
02292      * than pure quicksort, but glibc uses msort_with_tmp() on stack.
02293      */
02294     if (mi->mi_set->count > 1) {
02295 #if defined(__GLIBC__)
02296             qsort(mi->mi_set->recs, mi->mi_set->count,
02297                 sizeof(*mi->mi_set->recs), hdrNumCmp);
02298 #else
02299             rpm_mergesort(mi->mi_set->recs, mi->mi_set->count,
02300                 sizeof(*mi->mi_set->recs), hdrNumCmp);
02301 #endif
02302         }
02303         mi->mi_sorted = 1;
02304 #ifdef  NOTNOW
02305     {   struct _dbiIndexItem * rec;
02306         int i;
02307         for (i = 0, rec = mi->mi_set->recs; i < mi->mi_set->count; i++, rec++) {
02308             fprintf(stderr, "\t%p[%u] =  %p: %u %u %u\n", mi->mi_set->recs,
02309                         i, rec, rec->hdrNum, rec->tagNum, rec->fpNum);
02310         }
02311     }
02312 #endif
02313     }
02314     return rc;
02315 }
02316 
02317 /* XXX TODO: a Bloom Filter on removed packages created once, not each time. */
02318 int rpmmiPrune(rpmmi mi, uint32_t * hdrNums, int nHdrNums, int sorted)
02319 {
02320     int rc = (mi == NULL || hdrNums == NULL || nHdrNums <= 0);
02321 
02322     if (!rc) {
02323         int i;
02324         if (mi->mi_bf == NULL) {
02325             static size_t nRemoves = 2 * 8192;  /* XXX population estimate */
02326             static double e = 1.0e-4;
02327             size_t m = 0;
02328             size_t k = 0;
02329             rpmbfParams(nRemoves, e, &m, &k);
02330             mi->mi_bf = rpmbfNew(m, k, 0);
02331         }
02332         for (i = 0; i < nHdrNums; i++) {
02333             uint32_t mi_offset = _hton_ui(hdrNums[i]);
02334             int xx = rpmbfAdd(mi->mi_bf, &mi_offset, sizeof(mi_offset));
02335 assert(xx == 0);
02336         }
02337     }
02338 
02339 if (_rpmmi_debug)
02340 fprintf(stderr, "<-- %s(%p, %p[%u], %d) rc %d h# %u\n", __FUNCTION__, mi, hdrNums, (unsigned)nHdrNums, sorted, rc, (unsigned) (hdrNums ? hdrNums[0] : 0));
02341     return rc;
02342 }
02343 
02344 int rpmmiGrow(rpmmi mi, const uint32_t * hdrNums, int nHdrNums)
02345 {
02346     int rc = (mi == NULL || hdrNums == NULL || nHdrNums <= 0);
02347 
02348     if (!rc) {
02349         if (mi->mi_set == NULL)
02350             mi->mi_set = xcalloc(1, sizeof(*mi->mi_set));
02351         (void) dbiAppendSet(mi->mi_set, hdrNums, nHdrNums, sizeof(*hdrNums), 0);
02352     }
02353 
02354 if (_rpmmi_debug)
02355 fprintf(stderr, "<-- %s(%p, %p[%u]) rc %d h# %u\n", __FUNCTION__, mi, hdrNums, (unsigned)nHdrNums, rc, (unsigned) (hdrNums ? hdrNums[0] : 0));
02356     return rc;
02357 }
02358 
02359 /*@-dependenttrans -exposetrans -globstate @*/
02360 rpmmi rpmmiInit(rpmdb db, rpmTag tag, const void * keyp, size_t keylen)
02361 {
02362     HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
02363     rpmmi mi = NULL;
02364     dbiIndexSet set = NULL;
02365     dbiIndex dbi = NULL;
02366     int usePatterns = 0;
02367 
02368     if (db == NULL)
02369         goto exit;
02370 
02371     (void) rpmdbCheckSignals();
02372 
02373     /* XXX Control for whether patterns are permitted. */
02374     switch (tag) {
02375     default:    break;
02376     case 2:     /* XXX HACK to remove RPMDBI_LABEL from RPM. */
02377         /* XXX rpmlog message warning RPMDBI is deprecated? */
02378         tag = RPMTAG_NVRA;
02379         /*@fallthrough@*/
02380     case RPMTAG_NVRA:
02381 #ifdef  NOTYET          /* XXX JS unit tests break. */
02382     case RPMTAG_NAME:
02383 #endif
02384 #ifdef  RPM_VENDOR_MANDRIVA_XXX /* XXX rpm -qf /non/existent breaks */
02385     case RPMTAG_PROVIDENAME:
02386 #endif
02387     case RPMTAG_VERSION:
02388     case RPMTAG_RELEASE:
02389     case RPMTAG_ARCH:
02390     case RPMTAG_OS:
02391     case RPMTAG_GROUP:
02392         usePatterns = 1;
02393         break;
02394 #ifndef NOTYET          /* XXX can't quite do this yet */
02395     /* XXX HACK to remove the existing complexity of RPMTAG_BASENAMES */
02396     case RPMTAG_BASENAMES:
02397         if (keyp == NULL)       /* XXX rpmdbFindFpList & grow are speshul */
02398             break;
02399         tag = RPMTAG_FILEPATHS;
02400         /*@fallthrough@*/
02401 #endif
02402     case RPMTAG_FILEPATHS:
02403     case RPMTAG_DIRNAMES:
02404         usePatterns = 1;
02405         break;
02406     }
02407 
02408     dbi = dbiOpen(db, tag, 0);
02409 #ifdef  NOTYET  /* XXX non-configured tag indices force NULL return */
02410 assert(dbi != NULL);                                    /* XXX sanity */
02411 #else
02412     if (dbi == NULL)
02413         goto exit;
02414 #endif
02415 
02416     mi = rpmmiGetPool(_rpmmiPool);
02417     (void)rpmioLinkPoolItem((rpmioItem)mi, __FUNCTION__, __FILE__, __LINE__);
02418 
02419 if (_rpmmi_debug || (dbi && dbi->dbi_debug))
02420 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);
02421 
02422     /* Chain cursors for teardown on abnormal exit. */
02423     mi->mi_next = rpmmiRock;
02424     rpmmiRock = mi;
02425 
02426     if (tag == RPMDBI_PACKAGES && keyp == NULL) {
02427         /* Special case #1: sequentially iterate Packages database. */
02428         assert(keylen == 0);
02429         /* This should be the only case when (set == NULL). */
02430     }
02431     else if (tag == RPMDBI_PACKAGES) {
02432         /* Special case #2: will fetch header instance. */
02433         uint32_t hdrNum;
02434 assert(keylen == sizeof(hdrNum));
02435         memcpy(&hdrNum, keyp, sizeof(hdrNum));
02436         /* The set has only one element, which is hdrNum. */
02437         set = xcalloc(1, sizeof(*set));
02438         set->count = 1;
02439         set->recs = xcalloc(1, sizeof(set->recs[0]));
02440         set->recs[0].hdrNum = hdrNum;
02441     }
02442     else if (keyp == NULL) {
02443         /* XXX Special case #3: empty iterator with rpmmiGrow() */
02444         assert(keylen == 0);
02445     }
02446     else if (usePatterns) {
02447         /* XXX Special case #4: gather primary keys with patterns. */
02448         rpmRC rc;
02449 
02450         rc = dbiFindMatches(dbi, keyp, &set);
02451 #if defined(RPM_VENDOR_MANDRIVA)
02452         /*
02453          * Hack to workaround disttag/distepoch pattern matching issue to buy some
02454          * time to come up with better pattern fix..
02455          * One size should fit all now.. ;)
02456          *
02457          * This patch will try match NVR first, then for all matches returned,
02458          * it will match disttag, distepoch & arch individually.
02459          */
02460 
02461         /* We'll only try this if query fails */
02462         if(!rc && ((const char*)keyp)[0] != '^' && tag == RPMTAG_NVRA &&
02463                 (set == NULL || set->count < 1)) {
02464             size_t i;
02465             char *tmp = (char*)keyp;
02466 
02467             /* If pattern has less than three '-', it can't contain disttag, so
02468              * no point in trying */
02469             for (i = 0; (tmp = strchr(tmp, '-')); i++, tmp++);
02470             if (i >= 3) {
02471                 dbiIndex pdbi;
02472                 DBC *pdbc;
02473                 const char *origkeyp = keyp;
02474                 size_t klen = strlen(keyp)+1;
02475                 size_t size = 0;
02476                 int xx;
02477 
02478                 keyp = alloca(klen);
02479                 stpcpy((char*)keyp, origkeyp);
02480                 tmp = strrchr(keyp, '-');
02481                 *tmp = '\0';
02482                 rc = dbiFindMatches(dbi, keyp, &set);
02483 
02484                 pdbi = dbiOpen(db, RPMDBI_PACKAGES, 0);
02485                 xx = dbiCopen(pdbi, dbiTxnid(pdbi), &pdbc, 0);
02486 
02487                 for(i = 0; set && i < set->count; i++) {
02488                     DBT k = DBT_INIT;
02489                     DBT v = DBT_INIT;
02490                     Header h;
02491                     uint32_t offset = _hton_ui(set->recs[i].hdrNum);
02492                     rpmTag checkTags[] =
02493                     { RPMTAG_DISTTAG, RPMTAG_DISTEPOCH, RPMTAG_ARCH };
02494                     int j;
02495 
02496                     memset(&k, 0, sizeof(k));
02497                     memset(&v, 0, sizeof(v));
02498                     k.data = &offset;
02499                     k.size = sizeof(offset);
02500 
02501                     xx = dbiGet(dbi, pdbc, &k, &v, DB_SET);
02502                     h = headerLoad(v.data);
02503                     tmp = (char*)((size_t)keyp + strlen(keyp) + 1);
02504 
02505                     for (j = 0; j < (int)(sizeof(checkTags)/sizeof(checkTags[0])) &&
02506                             *tmp != '\0'; j++) {
02507                         he->tag = checkTags[j];
02508                         if(headerGet(h, he, 0)) {
02509                             size_t len = strlen(he->p.str);
02510 
02511                             if (he->tag == RPMTAG_ARCH && *tmp == '.')
02512                                 tmp++;
02513 
02514                             if(!strncmp(he->p.str, tmp, len))
02515                                 tmp += len;
02516                             _free(he->p.ptr);
02517                         }
02518                     }
02519                     if(j && *tmp  == '\0') {
02520                         set->recs[size].hdrNum = set->recs[i].hdrNum;
02521                         set->recs[size].tagNum = set->recs[i].tagNum;
02522                         size++;
02523                     }
02524 
02525                     h = headerFree(h);
02526                 }
02527                 if(set && set->count != size) {
02528                     set->count = size;
02529                     set->recs = realloc(set->recs, size * sizeof(*set->recs));
02530                 }
02531 
02532                 xx = dbiCclose(pdbi, pdbc, 0);
02533             }
02534         }
02535 #endif
02536 
02537         if ((rc  && rc != RPMRC_NOTFOUND) || set == NULL || set->count < 1) { /* error or empty set */
02538             set = dbiFreeIndexSet(set);
02539             rpmmiRock = mi->mi_next;
02540             mi->mi_next = NULL;
02541             mi = (rpmmi)rpmioFreePoolItem((rpmioItem)mi, __FUNCTION__, __FILE__, __LINE__);
02542             return NULL;
02543         }
02544     }
02545     else if (dbi && dbi->dbi_primary != NULL) {
02546         /* XXX Special case #5: secondary index associated w primary table. */
02547     }
02548     else {
02549         /* Common case: retrieve join keys. */
02550 assert(0);
02551     }
02552 
02553 /*@-assignexpose@*/
02554     mi->mi_db = rpmdbLink(db, __FUNCTION__);
02555 /*@=assignexpose@*/
02556     mi->mi_rpmtag = tag;
02557 
02558     mi->mi_dbc = NULL;
02559     mi->mi_set = set;
02560     mi->mi_setx = 0;
02561     mi->mi_count = (set ? set->count : 0);
02562 
02563     mi->mi_primary = (dbi && dbi->dbi_primary
02564                 ? xstrdup(dbi->dbi_primary) : NULL);
02565 
02566     /* Coerce/swab integer keys. Save key ind keylen in the iterator. */
02567     switch (tagType(tag) & 0xffff) {
02568     case RPM_UINT8_TYPE:
02569 assert(keylen == sizeof(he->p.ui8p[0]));
02570         mi->mi_keylen = sizeof(he->p.ui32p[0]); /* XXX coerce to uint32_t */
02571         mi->mi_keyp = he->p.ui32p = xmalloc(mi->mi_keylen);
02572         he->p.ui32p[0] = 0;
02573         memcpy(&he->p.ui8p[3], keyp, keylen);
02574         break;
02575     case RPM_UINT16_TYPE:
02576 assert(keylen == sizeof(he->p.ui16p[0]));
02577         mi->mi_keylen = sizeof(he->p.ui32p[0]); /* XXX coerce to uint32_t */
02578         mi->mi_keyp = he->p.ui32p = xmalloc(mi->mi_keylen);
02579         he->p.ui32p[0] = 0;
02580         memcpy(&he->p.ui16p[1], keyp, keylen);
02581         he->p.ui16p[1] = _hton_us(he->p.ui16p[1]);
02582         break;
02583 #if !defined(__LCLINT__)        /* LCL: buggy */
02584     case RPM_UINT32_TYPE:
02585 assert(keylen == sizeof(he->p.ui32p[0]));
02586         mi->mi_keylen = keylen;
02587 /*@-mayaliasunique@*/
02588         mi->mi_keyp = memcpy((he->p.ui32p = xmalloc(keylen)), keyp, keylen);
02589 /*@=mayaliasunique@*/
02590         he->p.ui32p[0] = _hton_ui(he->p.ui32p[0]);
02591         break;
02592     case RPM_UINT64_TYPE:
02593 assert(keylen == sizeof(he->p.ui64p[0]));
02594         mi->mi_keylen = keylen;
02595 /*@-mayaliasunique@*/
02596         mi->mi_keyp = memcpy((he->p.ui64p = xmalloc(keylen)), keyp, keylen);
02597 /*@=mayaliasunique@*/
02598         {   uint32_t _tmp = he->p.ui32p[0];
02599             he->p.ui32p[0] = _hton_ui(he->p.ui32p[1]);
02600             he->p.ui32p[1] = _hton_ui(_tmp);
02601         }
02602         break;
02603 #endif  /* !defined(__LCLINT__) */
02604     case RPM_BIN_TYPE:
02605     case RPM_I18NSTRING_TYPE:       /* XXX never occurs */
02606     case RPM_STRING_TYPE:
02607     case RPM_STRING_ARRAY_TYPE:
02608     default:
02609         mi->mi_keylen = keylen;
02610         if (keyp)
02611             mi->mi_keyp = keylen > 0
02612                 ? memcpy(xmalloc(keylen), keyp, keylen) : xstrdup(keyp) ;
02613         else
02614             mi->mi_keyp = NULL;
02615         break;
02616     }
02617     he->p.ptr = NULL;
02618 
02619     mi->mi_h = NULL;
02620     mi->mi_sorted = 0;
02621     mi->mi_cflags = 0;
02622     mi->mi_modified = 0;
02623     mi->mi_prevoffset = 0;
02624     mi->mi_offset = 0;
02625     mi->mi_nre = 0;
02626     mi->mi_re = NULL;
02627 
02628 exit:
02629     return mi;
02630 }
02631 /*@=dependenttrans =exposetrans =globstate @*/
02632 
02633 /* XXX psm.c */
02634 int rpmdbRemove(rpmdb db, /*@unused@*/ int rid, uint32_t hdrNum,
02635                 /*@unused@*/ rpmts ts)
02636 {
02637     HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
02638     Header h = NULL;
02639     sigset_t signalMask;
02640     dbiIndex dbi;
02641     size_t dbix;
02642     int rc = RPMRC_FAIL;                /* XXX RPMRC */
02643     int xx;
02644 
02645     if (db == NULL)
02646         return RPMRC_OK;                /* XXX RPMRC */
02647 
02648     /* Retrieve header for use by associated secondary index callbacks. */
02649     {   rpmmi mi;
02650         mi = rpmmiInit(db, RPMDBI_PACKAGES, &hdrNum, sizeof(hdrNum));
02651         h = rpmmiNext(mi);
02652         if (h)
02653             h = headerLink(h);
02654         mi = rpmmiFree(mi);
02655     }
02656 
02657     if (h == NULL) {
02658         rpmlog(RPMLOG_ERR, _("%s: cannot read header at 0x%x\n"),
02659               "rpmdbRemove", (unsigned)hdrNum);
02660         return RPMRC_FAIL;              /* XXX RPMRC */
02661     }
02662 
02663     he->tag = RPMTAG_NVRA;
02664     xx = headerGet(h, he, 0);
02665     rpmlog(RPMLOG_DEBUG, "  --- h#%8u %s\n", (unsigned)hdrNum, he->p.str);
02666     he->p.ptr = _free(he->p.ptr);
02667 
02668     (void) blockSignals(db, &signalMask);
02669 
02670     dbix = db->db_ndbi - 1;
02671     if (db->db_tags != NULL)
02672     do {
02673         tagStore_t dbiTag = db->db_tags + dbix;
02674         DBC * dbcursor;
02675         DBT k;
02676         DBT v;
02677         uint32_t ui;
02678 
02679         dbi = NULL;
02680         dbcursor = NULL;
02681         (void) memset(&k, 0, sizeof(k));
02682         (void) memset(&v, 0, sizeof(v));
02683         (void) memset(he, 0, sizeof(*he));
02684         he->tag = dbiTag->tag;
02685 
02686         switch (he->tag) {
02687         default:
02688             /* Don't bother if tag is not present. */
02689             if (!headerGet(h, he, 0))
02690                 /*@switchbreak@*/ break;
02691 
02692             dbi = dbiOpen(db, he->tag, 0);
02693             if (dbi == NULL)    goto exit;
02694 
02695             he->p.ptr = _free(he->p.ptr);
02696             /*@switchbreak@*/ break;
02697         case RPMDBI_AVAILABLE:  /* Filter out temporary databases */
02698         case RPMDBI_ADDED:
02699         case RPMDBI_REMOVED:
02700         case RPMDBI_DEPCACHE:
02701         case RPMDBI_SEQNO:
02702             /*@switchbreak@*/ break;
02703         case RPMDBI_PACKAGES:
02704             if (db->db_export != NULL)
02705                 xx = db->db_export(db, h, 0);
02706 
02707             ui = _hton_ui(hdrNum);
02708             k.data = &ui;
02709             k.size = (UINT32_T) sizeof(ui);
02710 
02711             /* New h ref for use by associated secondary index callbacks. */
02712             db->db_h = headerLink(h);
02713 
02714             dbi = dbiOpen(db, he->tag, 0);
02715             if (dbi == NULL)    goto exit;
02716 
02717             rc = dbiCopen(dbi, dbiTxnid(dbi), &dbcursor, DB_WRITECURSOR);
02718             rc = dbiGet(dbi, dbcursor, &k, &v, DB_SET);
02719             if (!rc)
02720                 rc = dbiDel(dbi, dbcursor, &k, &v, 0);
02721             xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
02722 
02723             /* Unreference db_h used by associated secondary index callbacks. */
02724             (void) headerFree(db->db_h);
02725             db->db_h = NULL;
02726 
02727             if (!dbi->dbi_no_dbsync)
02728                 xx = dbiSync(dbi, 0);
02729 
02730             /*@switchbreak@*/ break;
02731         }
02732     } while (dbix-- > 0);
02733 
02734     /* Unreference header used by associated secondary index callbacks. */
02735     (void) headerFree(h);
02736     h = NULL;
02737     rc = RPMRC_OK;              /* XXX RPMRC */
02738 
02739 exit:
02740     (void) unblockSignals(db, &signalMask);
02741     return rc;
02742 }
02743 
02744 /* XXX install.c */
02745 int rpmdbAdd(rpmdb db, int iid, Header h, /*@unused@*/ rpmts ts)
02746 {
02747     HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
02748     sigset_t signalMask;
02749     dbiIndex dbi;
02750     size_t dbix;
02751     uint32_t hdrNum = headerGetInstance(h);
02752     int rc = RPMRC_FAIL;                /* XXX RPMRC */
02753     int xx;
02754 
02755     if (db == NULL)
02756         return RPMRC_OK;                /* XXX RPMRC */
02757 
02758 if (_rpmdb_debug)
02759 fprintf(stderr, "--> %s(%p, %u, %p, %p) h# %u\n", __FUNCTION__, db, (unsigned)iid, h, ts, (unsigned)hdrNum);
02760 
02761 assert(headerIsEntry(h, RPMTAG_REMOVETID) == 0);        /* XXX sanity */
02762 
02763     /* Add the install transaction id. */
02764     if (iid != 0 && iid != -1) {
02765         rpmuint32_t tid[2];
02766         tid[0] = iid;
02767         tid[1] = 0;
02768         he->tag = RPMTAG_INSTALLTID;
02769         he->t = RPM_UINT32_TYPE;
02770         he->p.ui32p = tid;
02771         he->c = 2;
02772         if (!headerIsEntry(h, he->tag))
02773 /*@-compmempass@*/
02774            xx = headerPut(h, he, 0);
02775 /*@=compmempass@*/
02776     }
02777 
02778 /* XXX pubkeys used to set RPMTAG_PACKAGECOLOR here. */
02779 assert(headerIsEntry(h, RPMTAG_PACKAGECOLOR) != 0);     /* XXX sanity */
02780 
02781     (void) blockSignals(db, &signalMask);
02782 
02783     /* Assign a primary Packages key for new Header's. */
02784     if (hdrNum == 0) {
02785         int64_t seqno = 0;
02786 
02787         dbi = dbiOpen(db, RPMDBI_SEQNO, 0);
02788         if (dbi == NULL) goto exit;
02789 
02790         if ((xx = dbiSeqno(dbi, &seqno, 0)) == 0) {
02791             hdrNum = seqno;
02792             (void) headerSetInstance(h, hdrNum);
02793         } else
02794             goto exit;
02795     }
02796 
02797 /* XXX ensure that the header instance is set persistently. */
02798 if (hdrNum == 0) {
02799 assert(hdrNum > 0);
02800 assert(hdrNum == headerGetInstance(h));
02801 }
02802 
02803     dbi = dbiOpen(db, RPMDBI_PACKAGES, 0);
02804     if (dbi == NULL) goto exit;
02805 
02806     dbix = db->db_ndbi - 1;
02807     if (db->db_tags != NULL)
02808     do {
02809         tagStore_t dbiTag = db->db_tags + dbix;
02810         DBC * dbcursor;
02811         DBT k;
02812         DBT v;
02813         uint32_t ui;
02814 
02815         dbi = NULL;
02816         dbcursor = NULL;
02817         (void) memset(&k, 0, sizeof(k));
02818         (void) memset(&v, 0, sizeof(v));
02819         (void) memset(he, 0, sizeof(*he));
02820         he->tag = dbiTag->tag;
02821 
02822         switch (he->tag) {
02823         default:
02824             /* Don't bother if tag is not present. */
02825             if (!headerGet(h, he, 0))
02826                 /*@switchbreak@*/ break;
02827 
02828             dbi = dbiOpen(db, he->tag, 0);
02829             if (dbi == NULL) goto exit;
02830 
02831             he->p.ptr = _free(he->p.ptr);
02832             /*@switchbreak@*/ break;
02833         case RPMDBI_AVAILABLE:  /* Filter out temporary databases */
02834         case RPMDBI_ADDED:
02835         case RPMDBI_REMOVED:
02836         case RPMDBI_DEPCACHE:
02837         case RPMDBI_SEQNO:
02838             /*@switchbreak@*/ break;
02839         case RPMDBI_PACKAGES:
02840             if (db->db_export != NULL)
02841                 xx = db->db_export(db, h, 1);
02842 
02843             ui = _hton_ui(hdrNum);
02844             k.data = (void *) &ui;
02845             k.size = (UINT32_T) sizeof(ui);
02846 
02847             {   size_t len = 0;
02848                 v.data = headerUnload(h, &len);
02849 assert(v.data != NULL);
02850                 v.size = (UINT32_T) len;
02851             }
02852 
02853             /* New h ref for use by associated secondary index callbacks. */
02854             db->db_h = headerLink(h);
02855 
02856             dbi = dbiOpen(db, he->tag, 0);
02857             if (dbi == NULL) goto exit;
02858 
02859             xx = dbiCopen(dbi, dbiTxnid(dbi), &dbcursor, DB_WRITECURSOR);
02860             xx = dbiPut(dbi, dbcursor, &k, &v, DB_KEYLAST);
02861             xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
02862 
02863             /* Unreference db_h used by associated secondary index callbacks. */
02864             (void) headerFree(db->db_h);
02865             db->db_h = NULL;
02866 
02867             if (!dbi->dbi_no_dbsync)
02868                 xx = dbiSync(dbi, 0);
02869 
02870             v.data = _free(v.data); /* headerUnload */
02871             v.size = 0;
02872             /*@switchbreak@*/ break;
02873         }
02874 
02875     } while (dbix-- > 0);
02876     rc = RPMRC_OK;                      /* XXX RPMRC */
02877 
02878 exit:
02879     (void) unblockSignals(db, &signalMask);
02880     return rc;
02881 }