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