rpmdb/rpmdb.c

Go to the documentation of this file.
00001 
00005 #include "system.h"
00006 
00007 #define _USE_COPY_LOAD  /* XXX don't use DB_DBT_MALLOC (yet) */
00008 
00009 #include <sys/file.h>
00010 
00011 #include <rpmio.h>
00012 #include <rpmpgp.h>
00013 #include <rpmurl.h>
00014 #include <rpmmacro.h>
00015 #include <rpmsq.h>
00016 
00017 #define _RPMEVR_INTERNAL        /* XXX isInstallPrereq */
00018 #include <rpmevr.h>
00019 
00020 #define _RPMDB_INTERNAL
00021 #define _MIRE_INTERNAL
00022 #include "rpmdb.h"
00023 #include "fprint.h"
00024 #include "legacy.h"
00025 #include "header_internal.h"    /* XXX for HEADERFLAG_ALLOCATED */
00026 #include "debug.h"
00027 
00028 /*@access dbiIndexSet@*/
00029 /*@access dbiIndexItem@*/
00030 /*@access rpmts@*/              /* XXX compared with NULL */
00031 /*@access Header@*/             /* XXX compared with NULL */
00032 /*@access rpmdbMatchIterator@*/
00033 
00034 /*@unchecked@*/
00035 int _rpmdb_debug = 0;
00036 
00037 /*@unchecked@*/
00038 int _rsegfault = 0;
00039 
00040 /*@unchecked@*/
00041 int _wsegfault = 0;
00042 
00043 /*@unchecked@*/
00044 static int _rebuildinprogress = 0;
00045 /*@unchecked@*/
00046 static int _db_filter_dups = 0;
00047 
00048 
00049 /* Use a path uniquifier in the upper 16 bits of tagNum? */
00050 /* XXX Note: one cannot just choose a value, rpmdb tagNum's need fixing too */
00051 #define _DB_TAGGED_FILE_INDICES 1
00052 /*@unchecked@*/
00053 static int _db_tagged_file_indices = _DB_TAGGED_FILE_INDICES;
00054 
00055 #define _DBI_FLAGS      0
00056 #define _DBI_PERMS      0644
00057 #define _DBI_MAJOR      -1
00058 
00059 /* Bit mask macros. */
00060 /*@-exporttype@*/
00061 typedef unsigned int __pbm_bits;
00062 /*@=exporttype@*/
00063 #define __PBM_NBITS             (8 * sizeof (__pbm_bits))
00064 #define __PBM_IX(d)             ((d) / __PBM_NBITS)
00065 #define __PBM_MASK(d)           ((__pbm_bits) 1 << (((unsigned)(d)) % __PBM_NBITS))
00066 /*@-exporttype@*/
00067 typedef struct {
00068     __pbm_bits bits[1];
00069 } pbm_set;
00070 /*@=exporttype@*/
00071 #define __PBM_BITS(set) ((set)->bits)
00072 
00073 #define PBM_FREE(s)     _free(s);
00074 #define PBM_SET(d, s)   (__PBM_BITS (s)[__PBM_IX (d)] |= __PBM_MASK (d))
00075 #define PBM_CLR(d, s)   (__PBM_BITS (s)[__PBM_IX (d)] &= ~__PBM_MASK (d))
00076 #define PBM_ISSET(d, s) ((__PBM_BITS (s)[__PBM_IX (d)] & __PBM_MASK (d)) != 0)
00077 
00078 #define PBM_ALLOC(d)    xcalloc(__PBM_IX (d) + 1, sizeof(__pbm_bits))
00079 
00086 /*@unused@*/
00087 static inline pbm_set * PBM_REALLOC(pbm_set ** sp, int * odp, int nd)
00088         /*@modifies *sp, *odp @*/
00089 {
00090     int i, nb;
00091 
00092 /*@-bounds -sizeoftype@*/
00093     if (nd > (*odp)) {
00094         nd *= 2;
00095         nb = __PBM_IX(nd) + 1;
00096 /*@-unqualifiedtrans@*/
00097         *sp = xrealloc(*sp, nb * sizeof(__pbm_bits));
00098 /*@=unqualifiedtrans@*/
00099         for (i = __PBM_IX(*odp) + 1; i < nb; i++)
00100             __PBM_BITS(*sp)[i] = 0;
00101         *odp = nd;
00102     }
00103 /*@=bounds =sizeoftype@*/
00104 /*@-compdef -retalias -usereleased@*/
00105     return *sp;
00106 /*@=compdef =retalias =usereleased@*/
00107 }
00108 
00114 static inline unsigned char nibble(char c)
00115         /*@*/
00116 {
00117     if (c >= '0' && c <= '9')
00118         return (c - '0');
00119     if (c >= 'A' && c <= 'F')
00120         return (c - 'A') + 10;
00121     if (c >= 'a' && c <= 'f')
00122         return (c - 'a') + 10;
00123     return 0;
00124 }
00125 
00126 #ifdef  DYING
00127 
00133 static int printable(const void * ptr, size_t len)      /*@*/
00134 {
00135     const char * s = ptr;
00136     int i;
00137     for (i = 0; i < len; i++, s++)
00138         if (!(*s >= ' ' && *s <= '~')) return 0;
00139     return 1;
00140 }
00141 #endif
00142 
00149 static int dbiTagToDbix(rpmdb db, int rpmtag)
00150         /*@*/
00151 {
00152     int dbix;
00153 
00154     if (db->db_tagn != NULL)
00155     for (dbix = 0; dbix < db->db_ndbi; dbix++) {
00156 /*@-boundsread@*/
00157         if (rpmtag != db->db_tagn[dbix])
00158             continue;
00159         return dbix;
00160 /*@=boundsread@*/
00161     }
00162     return -1;
00163 }
00164 
00168 /*@-exportheader@*/
00169 static void dbiTagsInit(/*@null@*/int ** dbiTagsP, /*@null@*/int * dbiTagsMaxP)
00170         /*@globals rpmGlobalMacroContext, h_errno @*/
00171         /*@modifies dbiTagsP, dbiTagsMaxP, rpmGlobalMacroContext @*/
00172 {
00173 /*@observer@*/
00174     static const char * const _dbiTagStr_default =
00175         "Packages:Name:Basenames:Group:Requirename:Providename:Conflictname:Triggername:Dirnames:Requireversion:Provideversion:Installtid:Sigmd5:Sha1header:Filemd5s:Depends:Pubkeys";
00176     int * dbiTags = NULL;
00177     int dbiTagsMax = 0;
00178     char * dbiTagStr = NULL;
00179     char * o, * oe;
00180     int dbix, rpmtag, bingo;
00181 
00182     dbiTagStr = rpmExpand("%{?_dbi_tags}", NULL);
00183     if (!(dbiTagStr && *dbiTagStr)) {
00184         dbiTagStr = _free(dbiTagStr);
00185         dbiTagStr = xstrdup(_dbiTagStr_default);
00186     }
00187 
00188     /* Always allocate package index */
00189     dbiTags = xcalloc(1, sizeof(*dbiTags));
00190     dbiTags[dbiTagsMax++] = RPMDBI_PACKAGES;
00191 
00192     for (o = dbiTagStr; o && *o; o = oe) {
00193         while (*o && xisspace(*o))
00194             o++;
00195         if (*o == '\0')
00196             break;
00197         for (oe = o; oe && *oe; oe++) {
00198             if (xisspace(*oe))
00199                 /*@innerbreak@*/ break;
00200             if (oe[0] == ':' && !(oe[1] == '/' && oe[2] == '/'))
00201                 /*@innerbreak@*/ break;
00202         }
00203         if (oe && *oe)
00204             *oe++ = '\0';
00205         rpmtag = tagValue(o);
00206         if (rpmtag < 0) {
00207             rpmMessage(RPMMESS_WARNING,
00208                 _("dbiTagsInit: unrecognized tag name: \"%s\" ignored\n"), o);
00209             continue;
00210         }
00211 
00212         bingo = 0;
00213         if (dbiTags != NULL)
00214         for (dbix = 0; dbix < dbiTagsMax; dbix++) {
00215 /*@-boundsread@*/
00216             if (rpmtag == dbiTags[dbix]) {
00217                 bingo = 1;
00218                 /*@innerbreak@*/ break;
00219             }
00220 /*@=boundsread@*/
00221         }
00222         if (bingo)
00223             continue;
00224 
00225         dbiTags = xrealloc(dbiTags, (dbiTagsMax + 1) * sizeof(*dbiTags)); /* XXX memory leak */
00226         dbiTags[dbiTagsMax++] = rpmtag;
00227     }
00228 
00229     if (dbiTagsMaxP != NULL)
00230         *dbiTagsMaxP = dbiTagsMax;
00231 /*@-branchstate@*/
00232     if (dbiTagsP != NULL)
00233         *dbiTagsP = dbiTags;
00234     else
00235         dbiTags = _free(dbiTags);
00236 /*@=branchstate@*/
00237     dbiTagStr = _free(dbiTagStr);
00238 }
00239 /*@=exportheader@*/
00240 
00241 /*@-redecl@*/
00242 #define DB1vec          NULL
00243 #define DB2vec          NULL
00244 
00245 #ifdef HAVE_DB3_DB_H
00246 /*@-exportheadervar -declundef @*/
00247 /*@observer@*/ /*@unchecked@*/
00248 extern struct _dbiVec db3vec;
00249 /*@=exportheadervar =declundef @*/
00250 #define DB3vec          &db3vec
00251 /*@=redecl@*/
00252 #else
00253 #define DB3vec          NULL
00254 #endif
00255 
00256 #ifdef HAVE_SQLITE3_H
00257 /*@-exportheadervar -declundef @*/
00258 /*@observer@*/ /*@unchecked@*/
00259 extern struct _dbiVec sqlitevec;
00260 /*@=exportheadervar =declundef @*/
00261 #define SQLITEvec       &sqlitevec
00262 /*@=redecl@*/
00263 #else
00264 #define SQLITEvec       NULL
00265 #endif
00266 
00267 /*@-nullassign@*/
00268 /*@observer@*/ /*@unchecked@*/
00269 static struct _dbiVec *mydbvecs[] = {
00270     DB1vec, DB1vec, DB2vec, DB3vec, SQLITEvec, NULL
00271 };
00272 /*@=nullassign@*/
00273 
00274 dbiIndex dbiOpen(rpmdb db, rpmTag rpmtag, /*@unused@*/ unsigned int flags)
00275 {
00276     int dbix;
00277     dbiIndex dbi = NULL;
00278     int _dbapi, _dbapi_rebuild, _dbapi_wanted;
00279     int rc = 0;
00280 
00281 /*@-modfilesys@*/
00282 if (_rpmdb_debug)
00283 fprintf(stderr, "==> %s(%p, %s, 0x%x)\n", __FUNCTION__, db, tagName(rpmtag), flags);
00284 /*@=modfilesys@*/
00285 
00286     if (db == NULL)
00287         return NULL;
00288 
00289     dbix = dbiTagToDbix(db, rpmtag);
00290     if (dbix < 0 || dbix >= db->db_ndbi)
00291         return NULL;
00292 
00293     /* Is this index already open ? */
00294 /*@-compdef@*/ /* FIX: db->_dbi may be NULL */
00295     if (db->_dbi != NULL && (dbi = db->_dbi[dbix]) != NULL)
00296         return dbi;
00297 /*@=compdef@*/
00298 
00299     _dbapi_rebuild = rpmExpandNumeric("%{_dbapi_rebuild}");
00300     if (_dbapi_rebuild < 1 || _dbapi_rebuild > 4)
00301         _dbapi_rebuild = 4;
00302 /*    _dbapi_wanted = (_rebuildinprogress ? -1 : db->db_api); */
00303     _dbapi_wanted = (_rebuildinprogress ? _dbapi_rebuild : db->db_api);
00304 
00305     switch (_dbapi_wanted) {
00306     default:
00307         _dbapi = _dbapi_wanted;
00308         if (_dbapi < 0 || _dbapi >= 5 || mydbvecs[_dbapi] == NULL) {
00309             rpmMessage(RPMMESS_DEBUG, "dbiOpen: _dbiapi failed\n");
00310             return NULL;
00311         }
00312         errno = 0;
00313         dbi = NULL;
00314         rc = (*mydbvecs[_dbapi]->open) (db, rpmtag, &dbi);
00315         if (rc) {
00316             static int _printed[32];
00317             if (!_printed[dbix & 0x1f]++)
00318                 rpmError(RPMERR_DBOPEN,
00319                         _("cannot open %s index using db%d - %s (%d)\n"),
00320                         tagName(rpmtag), _dbapi,
00321                         (rc > 0 ? strerror(rc) : ""), rc);
00322             _dbapi = -1;
00323         }
00324         break;
00325     case -1:
00326         _dbapi = 5;
00327         while (_dbapi-- > 1) {
00328             if (mydbvecs[_dbapi] == NULL)
00329                 continue;
00330             errno = 0;
00331             dbi = NULL;
00332             rc = (*mydbvecs[_dbapi]->open) (db, rpmtag, &dbi);
00333             if (rc == 0 && dbi)
00334                 /*@loopbreak@*/ break;
00335         }
00336         if (_dbapi <= 0) {
00337             static int _printed[32];
00338             if (!_printed[dbix & 0x1f]++)
00339                 rpmError(RPMERR_DBOPEN, _("cannot open %s index\n"),
00340                         tagName(rpmtag));
00341             rc = 1;
00342             goto exit;
00343         }
00344         if (db->db_api == -1 && _dbapi > 0)
00345             db->db_api = _dbapi;
00346         break;
00347     }
00348 
00349 /* We don't ever _REQUIRE_ conversion... */
00350 #define SQLITE_HACK
00351 #ifdef  SQLITE_HACK_XXX
00352     /* Require conversion. */
00353     if (rc && _dbapi_wanted >= 0 && _dbapi != _dbapi_wanted && _dbapi_wanted == _dbapi_rebuild) {
00354         rc = (_rebuildinprogress ? 0 : 1);
00355         goto exit;
00356     }
00357 
00358     /* Suggest possible configuration */
00359     if (_dbapi_wanted >= 0 && _dbapi != _dbapi_wanted) {
00360         rc = 1;
00361         goto exit;
00362     }
00363 
00364     /* Suggest possible configuration */
00365     if (_dbapi_wanted < 0 && _dbapi != _dbapi_rebuild) {
00366         rc = (_rebuildinprogress ? 0 : 1);
00367         goto exit;
00368     }
00369 #endif
00370 
00371 exit:
00372     if (dbi != NULL && rc == 0) {
00373         if (db->_dbi != NULL)
00374             db->_dbi[dbix] = dbi;
00375 /*@-sizeoftype@*/
00376         if (rpmtag == RPMDBI_PACKAGES && db->db_bits == NULL) {
00377             db->db_nbits = 1024;
00378             if (!dbiStat(dbi, DB_FAST_STAT)) {
00379                 DB_HASH_STAT * hash = (DB_HASH_STAT *)dbi->dbi_stats;
00380                 if (hash)
00381                     db->db_nbits += hash->hash_nkeys;
00382             }
00383             db->db_bits = PBM_ALLOC(db->db_nbits);
00384         }
00385 /*@=sizeoftype@*/
00386     }
00387 #ifdef HAVE_DB3_DB_H
00388       else
00389         dbi = db3Free(dbi);
00390 #endif
00391 
00392 /*@-compdef -nullstate@*/ /* FIX: db->_dbi may be NULL */
00393     return dbi;
00394 /*@=compdef =nullstate@*/
00395 }
00396 
00403 static dbiIndexItem dbiIndexNewItem(unsigned int hdrNum, unsigned int tagNum)
00404         /*@*/
00405 {
00406     dbiIndexItem rec = xcalloc(1, sizeof(*rec));
00407     rec->hdrNum = hdrNum;
00408     rec->tagNum = tagNum;
00409     return rec;
00410 }
00411 
00412 union _dbswap {
00413     uint32_t ui;
00414     unsigned char uc[4];
00415 };
00416 
00417 #define _DBSWAP(_a) \
00418 /*@-bounds@*/ \
00419   { unsigned char _b, *_c = (_a).uc; \
00420     _b = _c[3]; _c[3] = _c[0]; _c[0] = _b; \
00421     _b = _c[2]; _c[2] = _c[1]; _c[1] = _b; \
00422 /*@=bounds@*/ \
00423   }
00424 
00432 static int dbt2set(dbiIndex dbi, DBT * data, /*@out@*/ dbiIndexSet * setp)
00433         /*@modifies dbi, *setp @*/
00434 {
00435     int _dbbyteswapped;
00436     const char * sdbir;
00437     dbiIndexSet set;
00438     int i;
00439 
00440     if (dbi == NULL || data == NULL || setp == NULL)
00441         return -1;
00442     _dbbyteswapped = dbiByteSwapped(dbi);
00443 
00444     if ((sdbir = data->data) == NULL) {
00445         *setp = NULL;
00446         return 0;
00447     }
00448 
00449     set = xmalloc(sizeof(*set));
00450     set->count = data->size / dbi->dbi_jlen;
00451     set->recs = xmalloc(set->count * sizeof(*(set->recs)));
00452 
00453 /*@-bounds -sizeoftype @*/
00454     switch (dbi->dbi_jlen) {
00455     default:
00456     case 2*sizeof(int_32):
00457         for (i = 0; i < set->count; i++) {
00458             union _dbswap hdrNum, tagNum;
00459 
00460             memcpy(&hdrNum.ui, sdbir, sizeof(hdrNum.ui));
00461             sdbir += sizeof(hdrNum.ui);
00462             memcpy(&tagNum.ui, sdbir, sizeof(tagNum.ui));
00463             sdbir += sizeof(tagNum.ui);
00464             if (_dbbyteswapped) {
00465                 _DBSWAP(hdrNum);
00466                 _DBSWAP(tagNum);
00467             }
00468             set->recs[i].hdrNum = hdrNum.ui;
00469             set->recs[i].tagNum = tagNum.ui;
00470             set->recs[i].fpNum = 0;
00471         }
00472         break;
00473     case 1*sizeof(int_32):
00474         for (i = 0; i < set->count; i++) {
00475             union _dbswap hdrNum;
00476 
00477             memcpy(&hdrNum.ui, sdbir, sizeof(hdrNum.ui));
00478             sdbir += sizeof(hdrNum.ui);
00479             if (_dbbyteswapped) {
00480                 _DBSWAP(hdrNum);
00481             }
00482             set->recs[i].hdrNum = hdrNum.ui;
00483             set->recs[i].tagNum = 0;
00484             set->recs[i].fpNum = 0;
00485         }
00486         break;
00487     }
00488     *setp = set;
00489 /*@=bounds =sizeoftype @*/
00490 /*@-compdef@*/
00491     return 0;
00492 /*@=compdef@*/
00493 }
00494 
00502 static int set2dbt(dbiIndex dbi, DBT * data, dbiIndexSet set)
00503         /*@modifies dbi, *data @*/
00504 {
00505     int _dbbyteswapped;
00506     char * tdbir;
00507     int i;
00508 
00509     if (dbi == NULL || data == NULL || set == NULL)
00510         return -1;
00511     _dbbyteswapped = dbiByteSwapped(dbi);
00512 
00513     data->size = set->count * (dbi->dbi_jlen);
00514     if (data->size == 0) {
00515         data->data = NULL;
00516         return 0;
00517     }
00518     tdbir = data->data = xmalloc(data->size);
00519 
00520 /*@-bounds -sizeoftype@*/
00521     switch (dbi->dbi_jlen) {
00522     default:
00523     case 2*sizeof(int_32):
00524         for (i = 0; i < set->count; i++) {
00525             union _dbswap hdrNum, tagNum;
00526 
00527             memset(&hdrNum, 0, sizeof(hdrNum));
00528             memset(&tagNum, 0, sizeof(tagNum));
00529             hdrNum.ui = set->recs[i].hdrNum;
00530             tagNum.ui = set->recs[i].tagNum;
00531             if (_dbbyteswapped) {
00532                 _DBSWAP(hdrNum);
00533                 _DBSWAP(tagNum);
00534             }
00535             memcpy(tdbir, &hdrNum.ui, sizeof(hdrNum.ui));
00536             tdbir += sizeof(hdrNum.ui);
00537             memcpy(tdbir, &tagNum.ui, sizeof(tagNum.ui));
00538             tdbir += sizeof(tagNum.ui);
00539         }
00540         break;
00541     case 1*sizeof(int_32):
00542         for (i = 0; i < set->count; i++) {
00543             union _dbswap hdrNum;
00544 
00545             memset(&hdrNum, 0, sizeof(hdrNum));
00546             hdrNum.ui = set->recs[i].hdrNum;
00547             if (_dbbyteswapped) {
00548                 _DBSWAP(hdrNum);
00549             }
00550             memcpy(tdbir, &hdrNum.ui, sizeof(hdrNum.ui));
00551             tdbir += sizeof(hdrNum.ui);
00552         }
00553         break;
00554     }
00555 /*@=bounds =sizeoftype@*/
00556 
00557 /*@-compdef@*/
00558     return 0;
00559 /*@=compdef@*/
00560 }
00561 
00562 /* XXX assumes hdrNum is first int in dbiIndexItem */
00563 static int hdrNumCmp(const void * one, const void * two)
00564         /*@*/
00565 {
00566     const int * a = one, * b = two;
00567     return (*a - *b);
00568 }
00569 
00579 static int dbiAppendSet(dbiIndexSet set, const void * recs,
00580         int nrecs, size_t recsize, int sortset)
00581         /*@modifies *set @*/
00582 {
00583     const char * rptr = recs;
00584     size_t rlen = (recsize < sizeof(*(set->recs)))
00585                 ? recsize : sizeof(*(set->recs));
00586 
00587     if (set == NULL || recs == NULL || nrecs <= 0 || recsize == 0)
00588         return 1;
00589 
00590     set->recs = xrealloc(set->recs,
00591                         (set->count + nrecs) * sizeof(*(set->recs)));
00592 
00593     memset(set->recs + set->count, 0, nrecs * sizeof(*(set->recs)));
00594 
00595     while (nrecs-- > 0) {
00596         /*@-mayaliasunique@*/
00597         memcpy(set->recs + set->count, rptr, rlen);
00598         /*@=mayaliasunique@*/
00599         rptr += recsize;
00600         set->count++;
00601     }
00602 
00603     if (sortset && set->count > 1)
00604         qsort(set->recs, set->count, sizeof(*(set->recs)), hdrNumCmp);
00605 
00606     return 0;
00607 }
00608 
00618 static int dbiPruneSet(dbiIndexSet set, void * recs, int nrecs,
00619                 size_t recsize, int sorted)
00620         /*@modifies set, recs @*/
00621 {
00622     int from;
00623     int to = 0;
00624     int num = set->count;
00625     int numCopied = 0;
00626 
00627 assert(set->count > 0);
00628     if (nrecs > 1 && !sorted)
00629         qsort(recs, nrecs, recsize, hdrNumCmp);
00630 
00631     for (from = 0; from < num; from++) {
00632         if (bsearch(&set->recs[from], recs, nrecs, recsize, hdrNumCmp)) {
00633             set->count--;
00634             continue;
00635         }
00636         if (from != to)
00637             set->recs[to] = set->recs[from]; /* structure assignment */
00638         to++;
00639         numCopied++;
00640     }
00641     return (numCopied == num);
00642 }
00643 
00644 /* XXX transaction.c */
00645 unsigned int dbiIndexSetCount(dbiIndexSet set) {
00646     return set->count;
00647 }
00648 
00649 /* XXX transaction.c */
00650 unsigned int dbiIndexRecordOffset(dbiIndexSet set, int recno) {
00651     return set->recs[recno].hdrNum;
00652 }
00653 
00654 /* XXX transaction.c */
00655 unsigned int dbiIndexRecordFileNumber(dbiIndexSet set, int recno) {
00656     return set->recs[recno].tagNum;
00657 }
00658 
00659 /* XXX transaction.c */
00660 dbiIndexSet dbiFreeIndexSet(dbiIndexSet set) {
00661     if (set) {
00662         set->recs = _free(set->recs);
00663         set = _free(set);
00664     }
00665     return set;
00666 }
00667 
00668 struct _rpmdbMatchIterator {
00669 /*@dependent@*/ /*@null@*/
00670     rpmdbMatchIterator  mi_next;
00671 /*@only@*/
00672     const void *        mi_keyp;
00673     size_t              mi_keylen;
00674 /*@refcounted@*/
00675     rpmdb               mi_db;
00676     rpmTag              mi_rpmtag;
00677     dbiIndexSet         mi_set;
00678     DBC *               mi_dbc;
00679     DBT                 mi_key;
00680     DBT                 mi_data;
00681     int                 mi_setx;
00682 /*@refcounted@*/ /*@null@*/
00683     Header              mi_h;
00684     int                 mi_sorted;
00685     int                 mi_cflags;
00686     int                 mi_modified;
00687     unsigned int        mi_prevoffset;  /* header instance (native endian) */
00688     unsigned int        mi_offset;      /* header instance (native endian) */
00689     unsigned int        mi_filenum;     /* tag element (native endian) */
00690     int                 mi_nre;
00691 /*@only@*/ /*@null@*/
00692     miRE                mi_re;
00693 /*@null@*/
00694     rpmts               mi_ts;
00695 /*@null@*/
00696     rpmRC (*mi_hdrchk) (rpmts ts, const void * uh, size_t uc, const char ** msg)
00697         /*@modifies ts, *msg @*/;
00698 
00699 };
00700 
00701 /*@unchecked@*/
00702 static rpmdb rpmdbRock;
00703 
00704 /*@unchecked@*/ /*@exposed@*/ /*@null@*/
00705 static rpmdbMatchIterator rpmmiRock;
00706 
00707 int rpmdbCheckSignals(void)
00708         /*@globals rpmdbRock, rpmmiRock @*/
00709         /*@modifies rpmdbRock, rpmmiRock @*/
00710 {
00711     sigset_t newMask, oldMask;
00712     static int terminate = 0;
00713 
00714     if (terminate) return 0;
00715 
00716     (void) sigfillset(&newMask);                /* block all signals */
00717     (void) sigprocmask(SIG_BLOCK, &newMask, &oldMask);
00718 
00719     if (sigismember(&rpmsqCaught, SIGINT)
00720      || sigismember(&rpmsqCaught, SIGQUIT)
00721      || sigismember(&rpmsqCaught, SIGHUP)
00722      || sigismember(&rpmsqCaught, SIGTERM)
00723      || sigismember(&rpmsqCaught, SIGPIPE))
00724         terminate = 1;
00725 
00726     if (terminate) {
00727         rpmdb db;
00728         rpmdbMatchIterator mi;
00729 
00730 /*@-abstract@*/ /* sigset_t is abstract type */
00731         rpmMessage(RPMMESS_DEBUG, "Exiting on signal(0x%lx) ...\n", *((unsigned long *)&rpmsqCaught));
00732 /*@=abstract@*/
00733 
00734 /*@-branchstate@*/
00735         while ((mi = rpmmiRock) != NULL) {
00736 /*@i@*/     rpmmiRock = mi->mi_next;
00737             mi->mi_next = NULL;
00738 /*@i@*/     mi = rpmdbFreeIterator(mi);
00739         }
00740 /*@=branchstate@*/
00741 
00742 /*@-newreftrans@*/
00743         while ((db = rpmdbRock) != NULL) {
00744 /*@i@*/     rpmdbRock = db->db_next;
00745             db->db_next = NULL;
00746             (void) rpmdbClose(db);
00747         }
00748 /*@=newreftrans@*/
00749         exit(EXIT_FAILURE);
00750     }
00751     return sigprocmask(SIG_SETMASK, &oldMask, NULL);
00752 }
00753 
00760 static int blockSignals(/*@unused@*/ rpmdb db, /*@out@*/ sigset_t * oldMask)
00761         /*@globals fileSystem @*/
00762         /*@modifies *oldMask, fileSystem @*/
00763 {
00764     sigset_t newMask;
00765 
00766     (void) sigfillset(&newMask);                /* block all signals */
00767     (void) sigprocmask(SIG_BLOCK, &newMask, oldMask);
00768     (void) sigdelset(&newMask, SIGINT);
00769     (void) sigdelset(&newMask, SIGQUIT);
00770     (void) sigdelset(&newMask, SIGHUP);
00771     (void) sigdelset(&newMask, SIGTERM);
00772     (void) sigdelset(&newMask, SIGPIPE);
00773     return sigprocmask(SIG_BLOCK, &newMask, NULL);
00774 }
00775 
00782 /*@mayexit@*/
00783 static int unblockSignals(/*@unused@*/ rpmdb db, sigset_t * oldMask)
00784         /*@globals rpmdbRock, fileSystem, internalState @*/
00785         /*@modifies rpmdbRock, fileSystem, internalState @*/
00786 {
00787     (void) rpmdbCheckSignals();
00788     return sigprocmask(SIG_SETMASK, oldMask, NULL);
00789 }
00790 
00798 static inline /*@null@*/ const char * queryHeader(Header h, const char * qfmt)
00799         /*@globals headerDefaultFormats @*/
00800 {
00801     static const struct headerSprintfExtension_s * hdrfmts = headerDefaultFormats;
00802     const char * errstr = "(unkown error)";
00803     const char * str;
00804 
00805 /*@-modobserver@*/
00806     str = headerSprintf(h, qfmt, rpmTagTable, hdrfmts, &errstr);
00807 /*@=modobserver@*/
00808     if (str == NULL)
00809         rpmError(RPMERR_QFMT, _("incorrect format: \"%s\": %s\n"), qfmt, errstr);
00810     return str;
00811 }
00812 
00820 static int rpmdbExportInfo(/*@unused@*/ rpmdb db, Header h, int adding)
00821         /*@globals headerDefaultFormats, rpmGlobalMacroContext, h_errno,
00822                 fileSystem, internalState @*/
00823         /*@modifies rpmGlobalMacroContext,
00824                 fileSystem, internalState @*/
00825 {
00826     const char * fn = NULL;
00827     int xx;
00828 
00829 /*@-branchstate@*/
00830     {   const char * fnfmt = rpmGetPath("%{?_hrmib_path}", NULL);
00831         if (fnfmt && *fnfmt)
00832             fn = queryHeader(h, fnfmt);
00833         fnfmt = _free(fnfmt);
00834     }
00835 /*@=branchstate@*/
00836 
00837     if (fn == NULL)
00838         goto exit;
00839 
00840     if (adding) {
00841         FD_t fd = Fopen(fn, "w");
00842         int_32 *iidp;
00843 
00844         if (fd != NULL) {
00845             xx = Fclose(fd);
00846             fd = NULL;
00847             if (headerGetEntry(h, RPMTAG_INSTALLTIME, NULL, (void **)&iidp, NULL)) {
00848                 struct utimbuf stamp;
00849                 stamp.actime = *iidp;
00850                 stamp.modtime = *iidp;
00851                 if (!Utime(fn, &stamp))
00852                     rpmMessage(RPMMESS_DEBUG, "  +++ %s\n", fn);
00853             }
00854         }
00855     } else {
00856         if (!Unlink(fn))
00857             rpmMessage(RPMMESS_DEBUG, "  --- %s\n", fn);
00858     }
00859 
00860 exit:
00861     fn = _free(fn);
00862     return 0;
00863 }
00864 
00865 #define _DB_ROOT        "/"
00866 #define _DB_HOME        "%{?_dbpath}"
00867 #define _DB_FLAGS       0
00868 #define _DB_MODE        0
00869 #define _DB_PERMS       0644
00870 
00871 #define _DB_MAJOR       -1
00872 #define _DB_ERRPFX      "rpmdb"
00873 
00874 /*@-fullinitblock@*/
00875 /*@observer@*/ /*@unchecked@*/
00876 static struct rpmdb_s dbTemplate = {
00877     _DB_ROOT,   _DB_HOME, _DB_FLAGS, _DB_MODE, _DB_PERMS,
00878     _DB_MAJOR,  _DB_ERRPFX
00879 };
00880 /*@=fullinitblock@*/
00881 
00882 int rpmdbOpenAll(rpmdb db)
00883 {
00884     int dbix;
00885     int rc = 0;
00886 
00887     if (db == NULL) return -2;
00888 
00889     if (db->db_tagn != NULL && db->_dbi != NULL)
00890     for (dbix = 0; dbix < db->db_ndbi; dbix++) {
00891         if (db->db_tagn[dbix] < 0)
00892             continue;
00893         if (db->_dbi[dbix] != NULL)
00894             continue;
00895         switch ((db->db_tagn[dbix])) {
00896         case RPMDBI_AVAILABLE:
00897         case RPMDBI_ADDED:
00898         case RPMDBI_REMOVED:
00899         case RPMDBI_DEPENDS:
00900             continue;
00901             /*@notreached@*/ /*@switchbreak@*/ break;
00902         default:
00903             /*@switchbreak@*/ break;
00904         }
00905         (void) dbiOpen(db, db->db_tagn[dbix], db->db_flags);
00906     }
00907     return rc;
00908 }
00909 
00910 int rpmdbBlockDBI(rpmdb db, int rpmtag)
00911 {
00912     int tagn = (rpmtag >= 0 ? rpmtag : -rpmtag);
00913     int dbix;
00914 
00915     if (db == NULL || db->_dbi == NULL)
00916         return 0;
00917 
00918     if (db->db_tagn != NULL)
00919     for (dbix = 0; dbix < db->db_ndbi; dbix++) {
00920         if (db->db_tagn[dbix] != tagn)
00921             continue;
00922         db->db_tagn[dbix] = rpmtag;
00923         return 0;
00924     }
00925     return 0;
00926 }
00927 
00928 int rpmdbCloseDBI(rpmdb db, int rpmtag)
00929 {
00930     int dbix;
00931     int rc = 0;
00932 
00933     if (db == NULL || db->_dbi == NULL)
00934         return 0;
00935 
00936     if (db->db_tagn != NULL)
00937     for (dbix = 0; dbix < db->db_ndbi; dbix++) {
00938         if (db->db_tagn[dbix] != rpmtag)
00939             continue;
00940 /*@-boundswrite@*/
00941         if (db->_dbi[dbix] != NULL) {
00942             int xx;
00943             /*@-unqualifiedtrans@*/             /* FIX: double indirection. */
00944             xx = dbiClose(db->_dbi[dbix], 0);
00945             if (xx && rc == 0) rc = xx;
00946             db->_dbi[dbix] = NULL;
00947             /*@=unqualifiedtrans@*/
00948         }
00949 /*@=boundswrite@*/
00950         break;
00951     }
00952     return rc;
00953 }
00954 
00955 /* XXX query.c, rpminstall.c, verify.c */
00956 /*@-incondefs@*/
00957 int rpmdbClose(rpmdb db)
00958         /*@globals rpmdbRock @*/
00959         /*@modifies rpmdbRock @*/
00960 {
00961     rpmdb * prev, next;
00962     int dbix;
00963     int rc = 0;
00964 
00965     if (db == NULL)
00966         goto exit;
00967 
00968     (void) rpmdbUnlink(db, "rpmdbClose");
00969 
00970     /*@-usereleased@*/
00971     if (db->nrefs > 0)
00972         goto exit;
00973 
00974     if (db->_dbi)
00975     for (dbix = db->db_ndbi; --dbix >= 0; ) {
00976         int xx;
00977         if (db->_dbi[dbix] == NULL)
00978             continue;
00979         /*@-unqualifiedtrans@*/         /* FIX: double indirection. */
00980         xx = dbiClose(db->_dbi[dbix], 0);
00981         if (xx && rc == 0) rc = xx;
00982         db->_dbi[dbix] = NULL;
00983         /*@=unqualifiedtrans@*/
00984     }
00985     db->db_errpfx = _free(db->db_errpfx);
00986     db->db_root = _free(db->db_root);
00987     db->db_home = _free(db->db_home);
00988     db->db_bits = PBM_FREE(db->db_bits);
00989     db->db_tagn = _free(db->db_tagn);
00990     db->_dbi = _free(db->_dbi);
00991     db->db_ndbi = 0;
00992 
00993 /*@-newreftrans@*/
00994     prev = &rpmdbRock;
00995     while ((next = *prev) != NULL && next != db)
00996         prev = &next->db_next;
00997     if (next) {
00998 /*@i@*/ *prev = next->db_next;
00999         next->db_next = NULL;
01000     }
01001 /*@=newreftrans@*/
01002 
01003     /*@-refcounttrans@*/ db = _free(db); /*@=refcounttrans@*/
01004     /*@=usereleased@*/
01005 
01006 exit:
01007     (void) rpmsqEnable(-SIGHUP, NULL);
01008     (void) rpmsqEnable(-SIGINT, NULL);
01009     (void) rpmsqEnable(-SIGTERM,NULL);
01010     (void) rpmsqEnable(-SIGQUIT,NULL);
01011     (void) rpmsqEnable(-SIGPIPE,NULL);
01012     return rc;
01013 }
01014 /*@=incondefs@*/
01015 
01016 int rpmdbSync(rpmdb db)
01017 {
01018     int dbix;
01019     int rc = 0;
01020 
01021     if (db == NULL) return 0;
01022     if (db->_dbi != NULL)
01023     for (dbix = 0; dbix < db->db_ndbi; dbix++) {
01024         int xx;
01025         if (db->_dbi[dbix] == NULL)
01026             continue;
01027         if (db->_dbi[dbix]->dbi_no_dbsync)
01028             continue;
01029         xx = dbiSync(db->_dbi[dbix], 0);
01030         if (xx && rc == 0) rc = xx;
01031     }
01032     return rc;
01033 }
01034 
01040 static const char * rpmdbURIPath(const char *uri)
01041         /*@globals rpmGlobalMacroContext, h_errno @*/
01042         /*@modifies rpmGlobalMacroContext @*/
01043 {
01044     const char * s = rpmGetPath(uri, NULL);
01045     const char * fn = NULL;
01046     urltype ut = urlPath(s, &fn);
01047 
01048 /*@-branchstate@*/
01049     switch (ut) {
01050     case URL_IS_PATH:
01051     case URL_IS_UNKNOWN:
01052         fn = s;
01053         s = NULL;
01054         break;
01055     case URL_IS_HTTPS:
01056     case URL_IS_HTTP:
01057     case URL_IS_FTP:
01058     case URL_IS_HKP:
01059     case URL_IS_DASH:
01060     default:
01061         /* HACK: strip the URI prefix for these schemes. */
01062         fn = rpmGetPath(fn, NULL);
01063         break;
01064     }
01065 /*@=branchstate@*/
01066 
01067     /* Convert relative to absolute paths. */
01068     if (ut != URL_IS_PATH)      /* XXX permit file:///... URI's */
01069     if (fn && *fn && *fn != '/') {
01070         char dn[PATH_MAX];
01071         char *t;
01072         dn[0] = '\0';
01073         if ((t = realpath(".", dn)) != NULL) {
01074             t += strlen(dn);
01075             if (t > dn && t[-1] != '/')
01076                 *t++ = '/';
01077             t = stpncpy(t, fn, (sizeof(dn) - (t - dn)));
01078             *t = '\0';
01079             fn = _free(fn);
01080             fn = rpmGetPath(dn, NULL);
01081         }
01082     }
01083 
01084     s = _free(s);
01085 assert(fn != NULL);
01086     return fn;
01087 }
01088 
01089 /*@-exportheader@*/
01090 /*@-mods@*/     /* FIX: dbTemplate structure assignment */
01091 /*@only@*/ /*@null@*/
01092 rpmdb rpmdbNew(/*@kept@*/ /*@null@*/ const char * root,
01093                 /*@kept@*/ /*@null@*/ const char * home,
01094                 int mode, int perms, int flags)
01095         /*@globals _db_filter_dups, rpmGlobalMacroContext, h_errno @*/
01096         /*@modifies _db_filter_dups, rpmGlobalMacroContext @*/
01097 {
01098     rpmdb db = xcalloc(sizeof(*db), 1);
01099     const char * epfx = _DB_ERRPFX;
01100     static int oneshot = 0;
01101 
01102 /*@-modfilesys@*/ /*@-nullpass@*/
01103 if (_rpmdb_debug)
01104 fprintf(stderr, "==> %s(%s, %s, 0x%x, 0%o, 0x%x) db %p\n", __FUNCTION__, root, home, mode, perms, flags, db);
01105 /*@=modfilesys@*/ /*@=nullpass@*/
01106 
01107     if (!oneshot) {
01108         _db_filter_dups = rpmExpandNumeric("%{_filterdbdups}");
01109         oneshot = 1;
01110     }
01111 
01112 /*@-boundswrite@*/
01113     /*@-assignexpose@*/
01114     *db = dbTemplate;   /* structure assignment */
01115     /*@=assignexpose@*/
01116 /*@=boundswrite@*/
01117 
01118     db->_dbi = NULL;
01119 
01120     if (!(perms & 0600)) perms = 0644;  /* XXX sanity */
01121 
01122     if (mode >= 0)      db->db_mode = mode;
01123     if (perms >= 0)     db->db_perms = perms;
01124     if (flags >= 0)     db->db_flags = flags;
01125 
01126     db->db_root = rpmdbURIPath( (root && *root ? root : _DB_ROOT) );
01127     db->db_home = rpmdbURIPath( (home && *home ? home : _DB_HOME) );
01128 
01129     if (!(db->db_home && db->db_home[0])) {
01130         rpmError(RPMERR_DBOPEN, _("no dbpath has been set\n"));
01131         db->db_root = _free(db->db_root);
01132         db->db_home = _free(db->db_home);
01133         db = _free(db);
01134         /*@-globstate@*/ return NULL; /*@=globstate@*/
01135     }
01136 
01137     /* XXX if default "/var/lib/rpm" path, manage %{_hrmib_path} entries too. */
01138     {   const char * dbpath = rpmGetPath("%{?_dbpath}", NULL);
01139         const char * rootpath = NULL;
01140         const char * homepath = NULL;
01141 
01142         (void) urlPath(db->db_root, &rootpath);
01143         (void) urlPath(db->db_home, &homepath);
01144 #define _VARLIBRPM      "/var/lib/rpm"
01145         if (!strcmp(rootpath, "/")
01146          && !strncmp(homepath, _VARLIBRPM, sizeof(_VARLIBRPM)-1))
01147             db->db_export = rpmdbExportInfo;
01148         dbpath = _free(dbpath);
01149 #undef  _VARLIBRPM
01150     }
01151 
01152     db->db_errpfx = rpmExpand( (epfx && *epfx ? epfx : _DB_ERRPFX), NULL);
01153     db->db_remove_env = 0;
01154     db->db_filter_dups = _db_filter_dups;
01155     dbiTagsInit(&db->db_tagn, &db->db_ndbi);
01156     db->_dbi = xcalloc(db->db_ndbi, sizeof(*db->_dbi));
01157     db->nrefs = 0;
01158     /*@-globstate@*/
01159     return rpmdbLink(db, "rpmdbCreate");
01160     /*@=globstate@*/
01161 }
01162 /*@=mods@*/
01163 /*@=exportheader@*/
01164 
01165 /*@-exportheader@*/
01166 int rpmdbOpenDatabase(/*@null@*/ const char * prefix,
01167                 /*@null@*/ const char * dbpath,
01168                 int _dbapi, /*@null@*/ /*@out@*/ rpmdb *dbp,
01169                 int mode, int perms, int flags)
01170         /*@globals rpmdbRock, rpmGlobalMacroContext, h_errno,
01171                 fileSystem, internalState @*/
01172         /*@modifies rpmdbRock, *dbp, rpmGlobalMacroContext,
01173                 fileSystem, internalState @*/
01174         /*@requires maxSet(dbp) >= 0 @*/
01175 {
01176     rpmdb db;
01177     int rc, xx;
01178     int justCheck = flags & RPMDB_FLAG_JUSTCHECK;
01179     int minimal = flags & RPMDB_FLAG_MINIMAL;
01180 
01181     /* Insure that _dbapi has one of -1, 1, 2, or 3 */
01182     if (_dbapi < -1 || _dbapi > 4)
01183         _dbapi = -1;
01184     if (_dbapi == 0)
01185         _dbapi = 1;
01186 
01187     if (dbp)
01188         *dbp = NULL;
01189     if (mode & O_WRONLY) 
01190         return 1;
01191 
01192     db = rpmdbNew(prefix, dbpath, mode, perms, flags);
01193     if (db == NULL)
01194         return 1;
01195 
01196     (void) rpmsqEnable(SIGHUP,  NULL);
01197     (void) rpmsqEnable(SIGINT,  NULL);
01198     (void) rpmsqEnable(SIGTERM, NULL);
01199     (void) rpmsqEnable(SIGQUIT, NULL);
01200     (void) rpmsqEnable(SIGPIPE, NULL);
01201 
01202     db->db_api = _dbapi;
01203 
01204     {   int dbix;
01205 
01206         rc = 0;
01207         if (db->db_tagn != NULL)
01208         for (dbix = 0; rc == 0 && dbix < db->db_ndbi; dbix++) {
01209             dbiIndex dbi;
01210             int rpmtag;
01211 
01212             /* Filter out temporary databases */
01213             switch ((rpmtag = db->db_tagn[dbix])) {
01214             case RPMDBI_AVAILABLE:
01215             case RPMDBI_ADDED:
01216             case RPMDBI_REMOVED:
01217             case RPMDBI_DEPENDS:
01218                 continue;
01219                 /*@notreached@*/ /*@switchbreak@*/ break;
01220             default:
01221                 /*@switchbreak@*/ break;
01222             }
01223 
01224             dbi = dbiOpen(db, rpmtag, 0);
01225             if (dbi == NULL) {
01226                 rc = -2;
01227                 break;
01228             }
01229 
01230             switch (rpmtag) {
01231             case RPMDBI_PACKAGES:
01232                 if (dbi == NULL) rc |= 1;
01233 #if 0
01234                 /* XXX open only Packages, indices created on the fly. */
01235                 if (db->db_api == 3)
01236 #endif
01237                     goto exit;
01238                 /*@notreached@*/ /*@switchbreak@*/ break;
01239             case RPMTAG_NAME:
01240                 if (dbi == NULL) rc |= 1;
01241                 if (minimal)
01242                     goto exit;
01243                 /*@switchbreak@*/ break;
01244             default:
01245                 /*@switchbreak@*/ break;
01246             }
01247         }
01248     }
01249 
01250 exit:
01251     if (rc || justCheck || dbp == NULL)
01252         xx = rpmdbClose(db);
01253     else {
01254 /*@-assignexpose -newreftrans@*/
01255 /*@i@*/ db->db_next = rpmdbRock;
01256         rpmdbRock = db;
01257 /*@i@*/ *dbp = db;
01258 /*@=assignexpose =newreftrans@*/
01259     }
01260 
01261     return rc;
01262 }
01263 /*@=exportheader@*/
01264 
01265 rpmdb XrpmdbUnlink(rpmdb db, const char * msg, const char * fn, unsigned ln)
01266 {
01267 /*@-modfilesys@*/
01268 if (_rpmdb_debug)
01269 fprintf(stderr, "--> db %p -- %d %s at %s:%u\n", db, db->nrefs, msg, fn, ln);
01270 /*@=modfilesys@*/
01271     db->nrefs--;
01272     return NULL;
01273 }
01274 
01275 rpmdb XrpmdbLink(rpmdb db, const char * msg, const char * fn, unsigned ln)
01276 {
01277     db->nrefs++;
01278 /*@-modfilesys@*/
01279 if (_rpmdb_debug)
01280 fprintf(stderr, "--> db %p ++ %d %s at %s:%u\n", db, db->nrefs, msg, fn, ln);
01281 /*@=modfilesys@*/
01282     /*@-refcounttrans@*/ return db; /*@=refcounttrans@*/
01283 }
01284 
01285 /* XXX python/rpmmodule.c */
01286 int rpmdbOpen (const char * prefix, rpmdb *dbp, int mode, int perms)
01287 {
01288     int _dbapi = rpmExpandNumeric("%{_dbapi}");
01289 /*@-boundswrite@*/
01290     return rpmdbOpenDatabase(prefix, NULL, _dbapi, dbp, mode, perms, 0);
01291 /*@=boundswrite@*/
01292 }
01293 
01294 int rpmdbInit (const char * prefix, int perms)
01295 {
01296     rpmdb db = NULL;
01297     int _dbapi = rpmExpandNumeric("%{_dbapi}");
01298     int rc;
01299 
01300 /*@-boundswrite@*/
01301     rc = rpmdbOpenDatabase(prefix, NULL, _dbapi, &db, (O_CREAT | O_RDWR),
01302                 perms, RPMDB_FLAG_JUSTCHECK);
01303 /*@=boundswrite@*/
01304     if (db != NULL) {
01305         int xx;
01306         xx = rpmdbOpenAll(db);
01307         if (xx && rc == 0) rc = xx;
01308         xx = rpmdbClose(db);
01309         if (xx && rc == 0) rc = xx;
01310         db = NULL;
01311     }
01312     return rc;
01313 }
01314 
01315 int rpmdbVerifyAllDBI(rpmdb db)
01316 {
01317     int rc = 0;
01318 
01319     if (db != NULL) {
01320         int dbix;
01321         int xx;
01322         rc = rpmdbOpenAll(db);
01323 
01324         if (db->_dbi != NULL)
01325         for (dbix = db->db_ndbi; --dbix >= 0; ) {
01326             if (db->_dbi[dbix] == NULL)
01327                 continue;
01328             /*@-unqualifiedtrans@*/             /* FIX: double indirection. */
01329             xx = dbiVerify(db->_dbi[dbix], 0);
01330             if (xx && rc == 0) rc = xx;
01331             db->_dbi[dbix] = NULL;
01332             /*@=unqualifiedtrans@*/
01333         }
01334 
01335         /*@-nullstate@*/        /* FIX: db->_dbi[] may be NULL. */
01336         xx = rpmdbClose(db);
01337         /*@=nullstate@*/
01338         if (xx && rc == 0) rc = xx;
01339         db = NULL;
01340     }
01341     return rc;
01342 }
01343 
01344 int rpmdbVerify(const char * prefix)
01345 {
01346     rpmdb db = NULL;
01347     int _dbapi = rpmExpandNumeric("%{_dbapi}");
01348     int rc = rpmdbOpenDatabase(prefix, NULL, _dbapi, &db, O_RDONLY, 0644, 0);
01349 
01350     if (!rc && db != NULL)
01351         rc = rpmdbVerifyAllDBI(db);
01352     return rc;
01353 }
01354 
01360 static inline unsigned taghash(const char *s)
01361 {
01362     unsigned int r = 0;
01363     int c;
01364     while ((c = *(const unsigned char *)s++) != 0) {
01365         /* XXX Excluding the '/' character may cause hash collisions. */
01366         if (c != '/')
01367             r += (r << 3) + c;
01368     }
01369     return ((r & 0x7fff) | 0x8000) << 16;
01370 }
01371 
01381 static int rpmdbFindByFile(rpmdb db, /*@null@*/ const char * filespec,
01382                 DBT * key, DBT * data, /*@out@*/ dbiIndexSet * matches)
01383         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
01384         /*@modifies db, *key, *data, *matches, rpmGlobalMacroContext,
01385                 fileSystem, internalState @*/
01386         /*@requires maxSet(matches) >= 0 @*/
01387 {
01388     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
01389     HFD_t hfd = headerFreeData;
01390     const char * dirName;
01391     const char * baseName;
01392     rpmTagType bnt, dnt;
01393     fingerPrintCache fpc;
01394     fingerPrint fp1;
01395     dbiIndex dbi = NULL;
01396     DBC * dbcursor;
01397     dbiIndexSet allMatches = NULL;
01398     dbiIndexItem rec = NULL;
01399     int i;
01400     int rc;
01401     int xx;
01402 
01403     *matches = NULL;
01404     if (filespec == NULL) return -2;
01405 
01406     /*@-branchstate@*/
01407     if ((baseName = strrchr(filespec, '/')) != NULL) {
01408         char * t;
01409         size_t len;
01410 
01411         len = baseName - filespec + 1;
01412 /*@-boundswrite@*/
01413         t = strncpy(alloca(len + 1), filespec, len);
01414         t[len] = '\0';
01415 /*@=boundswrite@*/
01416         dirName = t;
01417         baseName++;
01418     } else {
01419         dirName = "";
01420         baseName = filespec;
01421     }
01422     /*@=branchstate@*/
01423     if (baseName == NULL)
01424         return -2;
01425 
01426     fpc = fpCacheCreate(20);
01427     fp1 = fpLookup(fpc, dirName, baseName, 1);
01428 
01429     dbi = dbiOpen(db, RPMTAG_BASENAMES, 0);
01430 /*@-branchstate@*/
01431     if (dbi != NULL) {
01432         dbcursor = NULL;
01433         xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
01434 
01435 /*@-temptrans@*/
01436 key->data = (void *) baseName;
01437 /*@=temptrans@*/
01438 key->size = strlen(baseName);
01439 if (key->size == 0) key->size++;        /* XXX "/" fixup. */
01440 
01441         rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
01442         if (rc > 0) {
01443             rpmError(RPMERR_DBGETINDEX,
01444                 _("error(%d) getting \"%s\" records from %s index\n"),
01445                 rc, key->data, tagName(dbi->dbi_rpmtag));
01446         }
01447 
01448 if (rc == 0)
01449 (void) dbt2set(dbi, data, &allMatches);
01450 
01451         /* strip off directory tags */
01452         if (_db_tagged_file_indices && allMatches != NULL)
01453         for (i = 0; i < allMatches->count; i++) {
01454             if (allMatches->recs[i].tagNum & 0x80000000)
01455                 allMatches->recs[i].tagNum &= 0x0000ffff;
01456         }
01457 
01458         xx = dbiCclose(dbi, dbcursor, 0);
01459         dbcursor = NULL;
01460     } else
01461         rc = -2;
01462 /*@=branchstate@*/
01463 
01464     if (rc) {
01465         allMatches = dbiFreeIndexSet(allMatches);
01466         fpc = fpCacheFree(fpc);
01467         return rc;
01468     }
01469 
01470     *matches = xcalloc(1, sizeof(**matches));
01471     rec = dbiIndexNewItem(0, 0);
01472     i = 0;
01473     if (allMatches != NULL)
01474     while (i < allMatches->count) {
01475         const char ** baseNames, ** dirNames;
01476         int_32 * dirIndexes;
01477         unsigned int offset = dbiIndexRecordOffset(allMatches, i);
01478         unsigned int prevoff;
01479         Header h;
01480 
01481         {   rpmdbMatchIterator mi;
01482             mi = rpmdbInitIterator(db, RPMDBI_PACKAGES, &offset, sizeof(offset));
01483             h = rpmdbNextIterator(mi);
01484             if (h)
01485                 h = headerLink(h);
01486             mi = rpmdbFreeIterator(mi);
01487         }
01488 
01489         if (h == NULL) {
01490             i++;
01491             continue;
01492         }
01493 
01494         xx = hge(h, RPMTAG_BASENAMES, &bnt, (void **) &baseNames, NULL);
01495         xx = hge(h, RPMTAG_DIRNAMES, &dnt, (void **) &dirNames, NULL);
01496         xx = hge(h, RPMTAG_DIRINDEXES, NULL, (void **) &dirIndexes, NULL);
01497 
01498         do {
01499             fingerPrint fp2;
01500             int num = dbiIndexRecordFileNumber(allMatches, i);
01501 
01502             fp2 = fpLookup(fpc, dirNames[dirIndexes[num]], baseNames[num], 1);
01503             /*@-nullpass@*/
01504             if (FP_EQUAL(fp1, fp2)) {
01505             /*@=nullpass@*/
01506                 rec->hdrNum = dbiIndexRecordOffset(allMatches, i);
01507                 rec->tagNum = dbiIndexRecordFileNumber(allMatches, i);
01508                 xx = dbiAppendSet(*matches, rec, 1, sizeof(*rec), 0);
01509             }
01510 
01511             prevoff = offset;
01512             i++;
01513             if (i < allMatches->count)
01514                 offset = dbiIndexRecordOffset(allMatches, i);
01515         } while (i < allMatches->count && offset == prevoff);
01516 
01517         baseNames = hfd(baseNames, bnt);
01518         dirNames = hfd(dirNames, dnt);
01519         h = headerFree(h);
01520     }
01521 
01522     rec = _free(rec);
01523     allMatches = dbiFreeIndexSet(allMatches);
01524 
01525     fpc = fpCacheFree(fpc);
01526 
01527     if ((*matches)->count == 0) {
01528         *matches = dbiFreeIndexSet(*matches);
01529         return 1;
01530     }
01531 
01532     return 0;
01533 }
01534 
01535 /* XXX python/upgrade.c, install.c, uninstall.c */
01536 int rpmdbCountPackages(rpmdb db, const char * name)
01537 {
01538 DBC * dbcursor = NULL;
01539 DBT * key = alloca(sizeof(*key));
01540 DBT * data = alloca(sizeof(*data));
01541     dbiIndex dbi;
01542     int rc;
01543     int xx;
01544 
01545     if (db == NULL)
01546         return 0;
01547 
01548 memset(key, 0, sizeof(*key));
01549 memset(data, 0, sizeof(*data));
01550 
01551     dbi = dbiOpen(db, RPMTAG_NAME, 0);
01552     if (dbi == NULL)
01553         return 0;
01554 
01555 /*@-temptrans@*/
01556 key->data = (void *) name;
01557 /*@=temptrans@*/
01558 key->size = strlen(name);
01559 
01560     xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
01561     rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
01562 #ifndef SQLITE_HACK
01563     xx = dbiCclose(dbi, dbcursor, 0);
01564     dbcursor = NULL;
01565 #endif
01566 
01567     if (rc == 0) {              /* success */
01568         dbiIndexSet matches;
01569         /*@-nullpass@*/ /* FIX: matches might be NULL */
01570         matches = NULL;
01571         (void) dbt2set(dbi, data, &matches);
01572         if (matches) {
01573             rc = dbiIndexSetCount(matches);
01574             matches = dbiFreeIndexSet(matches);
01575         }
01576         /*@=nullpass@*/
01577     } else
01578     if (rc == DB_NOTFOUND) {    /* not found */
01579         rc = 0;
01580     } else {                    /* error */
01581         rpmError(RPMERR_DBGETINDEX,
01582                 _("error(%d) getting \"%s\" records from %s index\n"),
01583                 rc, key->data, tagName(dbi->dbi_rpmtag));
01584         rc = -1;
01585     }
01586 
01587 #ifdef  SQLITE_HACK
01588     xx = dbiCclose(dbi, dbcursor, 0);
01589     dbcursor = NULL;
01590 #endif
01591 
01592     return rc;
01593 }
01594 
01607 static rpmRC dbiFindMatches(dbiIndex dbi, DBC * dbcursor,
01608                 DBT * key, DBT * data,
01609                 const char * name,
01610                 /*@null@*/ const char * version,
01611                 /*@null@*/ const char * release,
01612                 /*@out@*/ dbiIndexSet * matches)
01613         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
01614         /*@modifies dbi, *dbcursor, *key, *data, *matches,
01615                 rpmGlobalMacroContext, fileSystem, internalState @*/
01616         /*@requires maxSet(matches) >= 0 @*/
01617 {
01618     int gotMatches = 0;
01619     int rc;
01620     int i;
01621 
01622 /*@-temptrans@*/
01623 key->data = (void *) name;
01624 /*@=temptrans@*/
01625 key->size = strlen(name);
01626 
01627     rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
01628 
01629     if (rc == 0) {              /* success */
01630         (void) dbt2set(dbi, data, matches);
01631         if (version == NULL && release == NULL)
01632             return RPMRC_OK;
01633     } else
01634     if (rc == DB_NOTFOUND) {    /* not found */
01635         return RPMRC_NOTFOUND;
01636     } else {                    /* error */
01637         rpmError(RPMERR_DBGETINDEX,
01638                 _("error(%d) getting \"%s\" records from %s index\n"),
01639                 rc, key->data, tagName(dbi->dbi_rpmtag));
01640         return RPMRC_FAIL;
01641     }
01642 
01643     /* Make sure the version and release match. */
01644     /*@-branchstate@*/
01645     for (i = 0; i < dbiIndexSetCount(*matches); i++) {
01646         unsigned int recoff = dbiIndexRecordOffset(*matches, i);
01647         rpmdbMatchIterator mi;
01648         Header h;
01649 
01650         if (recoff == 0)
01651             continue;
01652 
01653         mi = rpmdbInitIterator(dbi->dbi_rpmdb,
01654                         RPMDBI_PACKAGES, &recoff, sizeof(recoff));
01655 
01656         /* Set iterator selectors for version/release if available. */
01657         if (version &&
01658             rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_DEFAULT, version))
01659         {
01660             rc = RPMRC_FAIL;
01661             goto exit;
01662         }
01663         if (release &&
01664             rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT, release))
01665         {
01666             rc = RPMRC_FAIL;
01667             goto exit;
01668         }
01669 
01670         h = rpmdbNextIterator(mi);
01671 /*@-boundswrite@*/
01672         if (h)
01673             (*matches)->recs[gotMatches++] = (*matches)->recs[i];
01674         else
01675             (*matches)->recs[i].hdrNum = 0;
01676 /*@=boundswrite@*/
01677         mi = rpmdbFreeIterator(mi);
01678     }
01679     /*@=branchstate@*/
01680 
01681     if (gotMatches) {
01682         (*matches)->count = gotMatches;
01683         rc = RPMRC_OK;
01684     } else
01685         rc = RPMRC_NOTFOUND;
01686 
01687 exit:
01688 /*@-unqualifiedtrans@*/ /* FIX: double indirection */
01689     if (rc && matches && *matches)
01690         *matches = dbiFreeIndexSet(*matches);
01691 /*@=unqualifiedtrans@*/
01692     return rc;
01693 }
01694 
01707 static rpmRC dbiFindByLabel(dbiIndex dbi, DBC * dbcursor, DBT * key, DBT * data,
01708                 /*@null@*/ const char * arg, /*@out@*/ dbiIndexSet * matches)
01709         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
01710         /*@modifies dbi, *dbcursor, *key, *data, *matches,
01711                 rpmGlobalMacroContext, fileSystem, internalState @*/
01712         /*@requires maxSet(matches) >= 0 @*/
01713 {
01714     const char * release;
01715     char * localarg;
01716     char * s;
01717     char c;
01718     int brackets;
01719     rpmRC rc;
01720  
01721     if (arg == NULL || strlen(arg) == 0) return RPMRC_NOTFOUND;
01722 
01723     /* did they give us just a name? */
01724     rc = dbiFindMatches(dbi, dbcursor, key, data, arg, NULL, NULL, matches);
01725     if (rc != RPMRC_NOTFOUND) return rc;
01726 
01727     /*@-unqualifiedtrans@*/ /* FIX: double indirection */
01728     *matches = dbiFreeIndexSet(*matches);
01729     /*@=unqualifiedtrans@*/
01730 
01731     /* maybe a name and a release */
01732     localarg = alloca(strlen(arg) + 1);
01733     s = stpcpy(localarg, arg);
01734 
01735     c = '\0';
01736     brackets = 0;
01737     for (s -= 1; s > localarg; s--) {
01738         switch (*s) {
01739         case '[':
01740             brackets = 1;
01741             /*@switchbreak@*/ break;
01742         case ']':
01743             if (c != '[') brackets = 0;
01744             /*@switchbreak@*/ break;
01745         }
01746         c = *s;
01747         if (!brackets && *s == '-')
01748             break;
01749     }
01750 
01751     /*@-nullstate@*/    /* FIX: *matches may be NULL. */
01752     if (s == localarg) return RPMRC_NOTFOUND;
01753 
01754 /*@-boundswrite@*/
01755     *s = '\0';
01756 /*@=boundswrite@*/
01757     rc = dbiFindMatches(dbi, dbcursor, key, data, localarg, s + 1, NULL, matches);
01758     /*@=nullstate@*/
01759     if (rc != RPMRC_NOTFOUND) return rc;
01760 
01761     /*@-unqualifiedtrans@*/ /* FIX: double indirection */
01762     *matches = dbiFreeIndexSet(*matches);
01763     /*@=unqualifiedtrans@*/
01764     
01765     /* how about name-version-release? */
01766 
01767     release = s + 1;
01768 
01769     c = '\0';
01770     brackets = 0;
01771     for (; s > localarg; s--) {
01772         switch (*s) {
01773         case '[':
01774             brackets = 1;
01775             /*@switchbreak@*/ break;
01776         case ']':
01777             if (c != '[') brackets = 0;
01778             /*@switchbreak@*/ break;
01779         }
01780         c = *s;
01781         if (!brackets && *s == '-')
01782             break;
01783     }
01784 
01785     if (s == localarg) return RPMRC_NOTFOUND;
01786 
01787 /*@-boundswrite@*/
01788     *s = '\0';
01789 /*@=boundswrite@*/
01790     /*@-nullstate@*/    /* FIX: *matches may be NULL. */
01791     return dbiFindMatches(dbi, dbcursor, key, data, localarg, s + 1, release, matches);
01792     /*@=nullstate@*/
01793 }
01794 
01795 void * dbiStatsAccumulator(dbiIndex dbi, int opx)
01796 {
01797     void * sw = NULL;
01798     switch (opx) {
01799     case 14:    /* RPMTS_OP_DBGET */
01800         sw = &dbi->dbi_rpmdb->db_getops;
01801         break;
01802     case 15:    /* RPMTS_OP_DBPUT */
01803         sw = &dbi->dbi_rpmdb->db_putops;
01804         break;
01805     default:    /* XXX wrong, but let's not return NULL. */
01806     case 16:    /* RPMTS_OP_DBDEL */
01807         sw = &dbi->dbi_rpmdb->db_delops;
01808         break;
01809     }
01810     return sw;
01811 }
01812 
01821 static int miFreeHeader(rpmdbMatchIterator mi, dbiIndex dbi)
01822         /*@globals fileSystem, internalState @*/
01823         /*@modifies mi, dbi, fileSystem, internalState @*/
01824 {
01825     int rc = 0;
01826 
01827     if (mi == NULL || mi->mi_h == NULL)
01828         return 0;
01829 
01830     if (dbi && mi->mi_dbc && mi->mi_modified && mi->mi_prevoffset) {
01831         DBT * key = &mi->mi_key;
01832         DBT * data = &mi->mi_data;
01833         sigset_t signalMask;
01834         rpmRC rpmrc = RPMRC_NOTFOUND;
01835         int xx;
01836 
01837 /*@i@*/ key->data = (void *) &mi->mi_prevoffset;
01838         key->size = sizeof(mi->mi_prevoffset);
01839         data->data = headerUnload(mi->mi_h);
01840         data->size = headerSizeof(mi->mi_h, HEADER_MAGIC_NO);
01841 
01842         /* Check header digest/signature on blob export (if requested). */
01843         if (mi->mi_hdrchk && mi->mi_ts) {
01844             const char * msg = NULL;
01845             int lvl;
01846 
01847             rpmrc = (*mi->mi_hdrchk) (mi->mi_ts, data->data, data->size, &msg);
01848             lvl = (rpmrc == RPMRC_FAIL ? RPMMESS_ERROR : RPMMESS_DEBUG);
01849             rpmMessage(lvl, "%s h#%8u %s",
01850                 (rpmrc == RPMRC_FAIL ? _("miFreeHeader: skipping") : "write"),
01851                         mi->mi_prevoffset, (msg ? msg : "\n"));
01852             msg = _free(msg);
01853         }
01854 
01855         if (data->data != NULL && rpmrc != RPMRC_FAIL) {
01856             (void) blockSignals(dbi->dbi_rpmdb, &signalMask);
01857             rc = dbiPut(dbi, mi->mi_dbc, key, data, DB_KEYLAST);
01858             if (rc) {
01859                 rpmError(RPMERR_DBPUTINDEX,
01860                         _("error(%d) storing record #%d into %s\n"),
01861                         rc, mi->mi_prevoffset, tagName(dbi->dbi_rpmtag));
01862             }
01863             xx = dbiSync(dbi, 0);
01864             (void) unblockSignals(dbi->dbi_rpmdb, &signalMask);
01865         }
01866         data->data = _free(data->data);
01867         data->size = 0;
01868     }
01869 
01870     mi->mi_h = headerFree(mi->mi_h);
01871 
01872 /*@-nullstate@*/
01873     return rc;
01874 /*@=nullstate@*/
01875 }
01876 
01877 rpmdbMatchIterator rpmdbFreeIterator(rpmdbMatchIterator mi)
01878         /*@globals rpmmiRock @*/
01879         /*@modifies rpmmiRock @*/
01880 {
01881     rpmdbMatchIterator * prev, next;
01882     dbiIndex dbi;
01883     int xx;
01884     int i;
01885 
01886     if (mi == NULL)
01887         return NULL;
01888 
01889     prev = &rpmmiRock;
01890     while ((next = *prev) != NULL && next != mi)
01891         prev = &next->mi_next;
01892     if (next) {
01893 /*@i@*/ *prev = next->mi_next;
01894         next->mi_next = NULL;
01895     }
01896 
01897     dbi = dbiOpen(mi->mi_db, RPMDBI_PACKAGES, 0);
01898     if (dbi == NULL)    /* XXX can't happen */
01899         return NULL;
01900 
01901     xx = miFreeHeader(mi, dbi);
01902 
01903     if (mi->mi_dbc)
01904         xx = dbiCclose(dbi, mi->mi_dbc, 0);
01905     mi->mi_dbc = NULL;
01906 
01907     if (mi->mi_re != NULL)
01908     for (i = 0; i < mi->mi_nre; i++)
01909         xx = mireClean(mi->mi_re + i);
01910     mi->mi_re = _free(mi->mi_re);
01911 
01912     mi->mi_set = dbiFreeIndexSet(mi->mi_set);
01913     mi->mi_keyp = _free(mi->mi_keyp);
01914     mi->mi_db = rpmdbUnlink(mi->mi_db, "matchIterator");
01915 
01916     mi = _free(mi);
01917 
01918     (void) rpmdbCheckSignals();
01919 
01920     return mi;
01921 }
01922 
01923 unsigned int rpmdbGetIteratorOffset(rpmdbMatchIterator mi) {
01924     return (mi ? mi->mi_offset : 0);
01925 }
01926 
01927 unsigned int rpmdbGetIteratorFileNum(rpmdbMatchIterator mi) {
01928     return (mi ? mi->mi_filenum : 0);
01929 }
01930 
01931 int rpmdbGetIteratorCount(rpmdbMatchIterator mi) {
01932     return (mi && mi->mi_set ?  mi->mi_set->count : 0);
01933 }
01934 
01941 static int mireCmp(const void * a, const void * b)
01942 {
01943     const miRE mireA = (const miRE) a;
01944     const miRE mireB = (const miRE) b;
01945     return (mireA->tag - mireB->tag);
01946 }
01947 
01955 static /*@only@*/ char * mireDup(rpmTag tag, rpmMireMode *modep,
01956                         const char * pattern)
01957         /*@modifies *modep @*/
01958         /*@requires maxSet(modep) >= 0 @*/
01959 {
01960     const char * s;
01961     char * pat;
01962     char * t;
01963     int brackets;
01964     size_t nb;
01965     int c;
01966 
01967 /*@-boundswrite@*/
01968     switch (*modep) {
01969     default:
01970     case RPMMIRE_DEFAULT:
01971         if (tag == RPMTAG_DIRNAMES || tag == RPMTAG_BASENAMES) {
01972             *modep = RPMMIRE_GLOB;
01973             pat = xstrdup(pattern);
01974             break;
01975         }
01976 
01977         nb = strlen(pattern) + sizeof("^$");
01978 
01979         /* Find no. of bytes needed for pattern. */
01980         /* periods and plusses are escaped, splats become '.*' */
01981         c = '\0';
01982         brackets = 0;
01983         for (s = pattern; *s != '\0'; s++) {
01984             switch (*s) {
01985             case '.':
01986             case '+':
01987             case '*':
01988                 if (!brackets) nb++;
01989                 /*@switchbreak@*/ break;
01990             case '\\':
01991                 s++;
01992                 /*@switchbreak@*/ break;
01993             case '[':
01994                 brackets = 1;
01995                 /*@switchbreak@*/ break;
01996             case ']':
01997                 if (c != '[') brackets = 0;
01998                 /*@switchbreak@*/ break;
01999             }
02000             c = *s;
02001         }
02002 
02003         pat = t = xmalloc(nb);
02004 
02005         if (pattern[0] != '^') *t++ = '^';
02006 
02007         /* Copy pattern, escaping periods, prefixing splats with period. */
02008         c = '\0';
02009         brackets = 0;
02010         for (s = pattern; *s != '\0'; s++, t++) {
02011             switch (*s) {
02012             case '.':
02013             case '+':
02014                 if (!brackets) *t++ = '\\';
02015                 /*@switchbreak@*/ break;
02016             case '*':
02017                 if (!brackets) *t++ = '.';
02018                 /*@switchbreak@*/ break;
02019             case '\\':
02020                 *t++ = *s++;
02021                 /*@switchbreak@*/ break;
02022             case '[':
02023                 brackets = 1;
02024                 /*@switchbreak@*/ break;
02025             case ']':
02026                 if (c != '[') brackets = 0;
02027                 /*@switchbreak@*/ break;
02028             }
02029             c = *t = *s;
02030         }
02031 
02032         if (s > pattern && s[-1] != '$') *t++ = '$';
02033         *t = '\0';
02034         *modep = RPMMIRE_REGEX;
02035         break;
02036     case RPMMIRE_STRCMP:
02037     case RPMMIRE_REGEX:
02038     case RPMMIRE_GLOB:
02039         pat = xstrdup(pattern);
02040         break;
02041     }
02042 /*@-boundswrite@*/
02043 
02044     return pat;
02045 }
02046 
02047 int rpmdbSetIteratorRE(rpmdbMatchIterator mi, rpmTag tag,
02048                 rpmMireMode mode, const char * pattern)
02049 {
02050     static rpmMireMode defmode = (rpmMireMode)-1;
02051     miRE nmire = NULL;
02052     miRE mire = NULL;
02053     const char * allpat = NULL;
02054     int notmatch = 0;
02055     int rc = 0;
02056 
02057 /*@-boundsread@*/
02058     if (defmode == (rpmMireMode)-1) {
02059         const char *t = rpmExpand("%{?_query_selector_match}", NULL);
02060 
02061         if (*t == '\0' || !strcmp(t, "default"))
02062             defmode = RPMMIRE_DEFAULT;
02063         else if (!strcmp(t, "strcmp"))
02064             defmode = RPMMIRE_STRCMP;
02065         else if (!strcmp(t, "regex"))
02066             defmode = RPMMIRE_REGEX;
02067         else if (!strcmp(t, "glob"))
02068             defmode = RPMMIRE_GLOB;
02069         else
02070             defmode = RPMMIRE_DEFAULT;
02071         t = _free(t);
02072      }
02073 
02074     if (mi == NULL || pattern == NULL)
02075         return rc;
02076 
02077     /* Leading '!' inverts pattern match sense, like "grep -v". */
02078     if (*pattern == '!') {
02079         notmatch = 1;
02080         pattern++;
02081     }
02082 /*@=boundsread@*/
02083 
02084     nmire = mireNew(mode, tag);
02085 /*@-boundswrite@*/
02086     allpat = mireDup(nmire->tag, &nmire->mode, pattern);
02087 /*@=boundswrite@*/
02088 
02089     if (nmire->mode == RPMMIRE_DEFAULT)
02090         nmire->mode = defmode;
02091 
02092     rc = mireRegcomp(nmire, allpat);
02093     if (rc)
02094         goto exit;
02095 
02096     mi->mi_re = xrealloc(mi->mi_re, (mi->mi_nre + 1) * sizeof(*mi->mi_re));
02097     mire = mi->mi_re + mi->mi_nre;
02098     mi->mi_nre++;
02099     
02100     mire->mode = nmire->mode;
02101     mire->pattern = nmire->pattern;     nmire->pattern = NULL;
02102     mire->preg = nmire->preg;           nmire->preg = NULL;
02103     mire->cflags = nmire->cflags;
02104     mire->eflags = nmire->eflags;
02105     mire->fnflags = nmire->fnflags;
02106     mire->tag = nmire->tag;
02107     mire->notmatch = notmatch;
02108 
02109 /*@-boundsread@*/
02110     if (mi->mi_nre > 1)
02111         qsort(mi->mi_re, mi->mi_nre, sizeof(*mi->mi_re), mireCmp);
02112 /*@=boundsread@*/
02113 
02114 exit:
02115     allpat = _free(allpat);
02116     nmire = mireFree(nmire);
02117     return rc;
02118 }
02119 
02125 static int mireSkip (const rpmdbMatchIterator mi)
02126         /*@modifies mi->mi_re @*/
02127 {
02128     HGE_t hge = (HGE_t) headerGetEntryMinMemory;
02129     HFD_t hfd = (HFD_t) headerFreeData;
02130     union {
02131         void * ptr;
02132         const char ** argv;
02133         const char * str;
02134         int_32 * i32p;
02135         int_16 * i16p;
02136         int_8 * i8p;
02137     } u;
02138     char numbuf[32];
02139     rpmTagType t;
02140     int_32 c;
02141     miRE mire;
02142     static int_32 zero = 0;
02143     int ntags = 0;
02144     int nmatches = 0;
02145     int i, j;
02146     int rc;
02147 
02148     if (mi->mi_h == NULL)       /* XXX can't happen */
02149         return 1;
02150 
02151     /*
02152      * Apply tag tests, implicitly "||" for multiple patterns/values of a
02153      * single tag, implicitly "&&" between multiple tag patterns.
02154      */
02155 /*@-boundsread@*/
02156     if ((mire = mi->mi_re) == NULL)
02157         return 0;
02158 
02159     for (i = 0; i < mi->mi_nre; i++, mire++) {
02160         int anymatch;
02161 
02162         if (!hge(mi->mi_h, mire->tag, &t, (void **)&u, &c)) {
02163             if (mire->tag != RPMTAG_EPOCH)
02164                 continue;
02165             t = RPM_INT32_TYPE;
02166 /*@-immediatetrans@*/
02167             u.i32p = &zero;
02168 /*@=immediatetrans@*/
02169             c = 1;
02170         }
02171 
02172         anymatch = 0;           /* no matches yet */
02173         while (1) {
02174             switch (t) {
02175             case RPM_CHAR_TYPE:
02176             case RPM_INT8_TYPE:
02177                 sprintf(numbuf, "%d", (int) *u.i8p);
02178                 rc = mireRegexec(mire, numbuf);
02179                 if ((!rc && !mire->notmatch) || (rc && mire->notmatch))
02180                     anymatch++;
02181                 /*@switchbreak@*/ break;
02182             case RPM_INT16_TYPE:
02183                 sprintf(numbuf, "%d", (int) *u.i16p);
02184                 rc = mireRegexec(mire, numbuf);
02185                 if ((!rc && !mire->notmatch) || (rc && mire->notmatch))
02186                     anymatch++;
02187                 /*@switchbreak@*/ break;
02188             case RPM_INT32_TYPE:
02189                 sprintf(numbuf, "%d", (int) *u.i32p);
02190                 rc = mireRegexec(mire, numbuf);
02191                 if ((!rc && !mire->notmatch) || (rc && mire->notmatch))
02192                     anymatch++;
02193                 /*@switchbreak@*/ break;
02194             case RPM_STRING_TYPE:
02195                 rc = mireRegexec(mire, u.str);
02196                 if ((!rc && !mire->notmatch) || (rc && mire->notmatch))
02197                     anymatch++;
02198                 /*@switchbreak@*/ break;
02199             case RPM_I18NSTRING_TYPE:
02200             case RPM_STRING_ARRAY_TYPE:
02201                 for (j = 0; j < c; j++) {
02202                     rc = mireRegexec(mire, u.argv[j]);
02203                     if ((!rc && !mire->notmatch) || (rc && mire->notmatch)) {
02204                         anymatch++;
02205                         /*@innerbreak@*/ break;
02206                     }
02207                 }
02208                 /*@switchbreak@*/ break;
02209             case RPM_NULL_TYPE:
02210             case RPM_BIN_TYPE:
02211             default:
02212                 /*@switchbreak@*/ break;
02213             }
02214             if ((i+1) < mi->mi_nre && mire[0].tag == mire[1].tag) {
02215                 i++;
02216                 mire++;
02217                 /*@innercontinue@*/ continue;
02218             }
02219             /*@innerbreak@*/ break;
02220         }
02221 /*@=boundsread@*/
02222 
02223         u.ptr = hfd(u.ptr, t);
02224 
02225         ntags++;
02226         if (anymatch)
02227             nmatches++;
02228     }
02229 
02230     return (ntags > 0 && ntags == nmatches ? 0 : 1);
02231 }
02232 
02233 int rpmdbSetIteratorRewrite(rpmdbMatchIterator mi, int rewrite)
02234 {
02235     int rc;
02236     if (mi == NULL)
02237         return 0;
02238     rc = (mi->mi_cflags & DB_WRITECURSOR) ? 1 : 0;
02239     if (rewrite)
02240         mi->mi_cflags |= DB_WRITECURSOR;
02241     else
02242         mi->mi_cflags &= ~DB_WRITECURSOR;
02243     return rc;
02244 }
02245 
02246 int rpmdbSetIteratorModified(rpmdbMatchIterator mi, int modified)
02247 {
02248     int rc;
02249     if (mi == NULL)
02250         return 0;
02251     rc = mi->mi_modified;
02252     mi->mi_modified = modified;
02253     return rc;
02254 }
02255 
02256 int rpmdbSetHdrChk(rpmdbMatchIterator mi, rpmts ts,
02257         rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, const char ** msg))
02258 {
02259     int rc = 0;
02260     if (mi == NULL)
02261         return 0;
02262 /*@-assignexpose -newreftrans @*/ /* XXX forward linkage prevents rpmtsLink */
02263 /*@i@*/ mi->mi_ts = ts;
02264     mi->mi_hdrchk = hdrchk;
02265 /*@=assignexpose =newreftrans @*/
02266     return rc;
02267 }
02268 
02269 
02270 /*@-nullstate@*/ /* FIX: mi->mi_key.data may be NULL */
02271 Header rpmdbNextIterator(rpmdbMatchIterator mi)
02272 {
02273     dbiIndex dbi;
02274     void * uh;
02275     size_t uhlen;
02276     DBT * key;
02277     DBT * data;
02278     void * keyp;
02279     size_t keylen;
02280     int rc;
02281     int xx;
02282 
02283     if (mi == NULL)
02284         return NULL;
02285 
02286     dbi = dbiOpen(mi->mi_db, RPMDBI_PACKAGES, 0);
02287     if (dbi == NULL)
02288         return NULL;
02289 
02290     /*
02291      * Cursors are per-iterator, not per-dbi, so get a cursor for the
02292      * iterator on 1st call. If the iteration is to rewrite headers, and the
02293      * CDB model is used for the database, then the cursor needs to
02294      * marked with DB_WRITECURSOR as well.
02295      */
02296     if (mi->mi_dbc == NULL)
02297         xx = dbiCopen(dbi, dbi->dbi_txnid, &mi->mi_dbc, mi->mi_cflags);
02298 
02299 /*@-boundswrite@*/
02300     key = &mi->mi_key;
02301     memset(key, 0, sizeof(*key));
02302     data = &mi->mi_data;
02303     memset(data, 0, sizeof(*data));
02304 /*@=boundswrite@*/
02305 
02306 top:
02307     uh = NULL;
02308     uhlen = 0;
02309 
02310     do {
02311 union _dbswap mi_offset;
02312 
02313         /*@-branchstate -compmempass @*/
02314         if (mi->mi_set) {
02315             if (!(mi->mi_setx < mi->mi_set->count))
02316                 return NULL;
02317             mi->mi_offset = dbiIndexRecordOffset(mi->mi_set, mi->mi_setx);
02318             mi->mi_filenum = dbiIndexRecordFileNumber(mi->mi_set, mi->mi_setx);
02319 mi_offset.ui = mi->mi_offset;
02320 if (dbiByteSwapped(dbi) == 1)
02321     _DBSWAP(mi_offset);
02322             keyp = &mi_offset;
02323             keylen = sizeof(mi_offset.ui);
02324         } else {
02325 
02326             key->data = keyp = (void *)mi->mi_keyp;
02327             key->size = keylen = mi->mi_keylen;
02328             data->data = uh;
02329             data->size = uhlen;
02330 #if !defined(_USE_COPY_LOAD)
02331             data->flags |= DB_DBT_MALLOC;
02332 #endif
02333             rc = dbiGet(dbi, mi->mi_dbc, key, data,
02334                         (key->data == NULL ? DB_NEXT : DB_SET));
02335             data->flags = 0;
02336             keyp = key->data;
02337             keylen = key->size;
02338             uh = data->data;
02339             uhlen = data->size;
02340 
02341             /*
02342              * If we got the next key, save the header instance number.
02343              *
02344              * For db3 Packages, instance 0 (i.e. mi->mi_setx == 0) is the
02345              * largest header instance in the database, and should be
02346              * skipped.
02347              */
02348 /*@-boundswrite@*/
02349             if (keyp && mi->mi_setx && rc == 0) {
02350                 memcpy(&mi_offset, keyp, sizeof(mi_offset.ui));
02351 if (dbiByteSwapped(dbi) == 1)
02352     _DBSWAP(mi_offset);
02353                 mi->mi_offset = mi_offset.ui;
02354             }
02355 /*@=boundswrite@*/
02356 
02357             /* Terminate on error or end of keys */
02358             if (rc || (mi->mi_setx && mi->mi_offset == 0))
02359                 return NULL;
02360         }
02361         /*@=branchstate =compmempass @*/
02362         mi->mi_setx++;
02363     } while (mi->mi_offset == 0);
02364 
02365     /* If next header is identical, return it now. */
02366 /*@-compdef -refcounttrans -retalias -retexpose -usereleased @*/
02367     if (mi->mi_prevoffset && mi->mi_offset == mi->mi_prevoffset)
02368         return mi->mi_h;
02369 /*@=compdef =refcounttrans =retalias =retexpose =usereleased @*/
02370 
02371     /* Retrieve next header blob for index iterator. */
02372     /*@-branchstate -compmempass -immediatetrans @*/
02373     if (uh == NULL) {
02374         key->data = keyp;
02375         key->size = keylen;
02376 #if !defined(_USE_COPY_LOAD)
02377         data->flags |= DB_DBT_MALLOC;
02378 #endif
02379         rc = dbiGet(dbi, mi->mi_dbc, key, data, DB_SET);
02380         data->flags = 0;
02381         keyp = key->data;
02382         keylen = key->size;
02383         uh = data->data;
02384         uhlen = data->size;
02385         if (rc)
02386             return NULL;
02387     }
02388     /*@=branchstate =compmempass =immediatetrans @*/
02389 
02390     /* Rewrite current header (if necessary) and unlink. */
02391     xx = miFreeHeader(mi, dbi);
02392 
02393     /* Is this the end of the iteration? */
02394     if (uh == NULL)
02395         return NULL;
02396 
02397     /* Check header digest/signature once (if requested). */
02398 /*@-boundsread -branchstate -sizeoftype @*/
02399     if (mi->mi_hdrchk && mi->mi_ts) {
02400         rpmRC rpmrc = RPMRC_NOTFOUND;
02401 
02402         /* Don't bother re-checking a previously read header. */
02403         if (mi->mi_db->db_bits) {
02404             pbm_set * set;
02405 
02406             set = PBM_REALLOC((pbm_set **)&mi->mi_db->db_bits,
02407                         &mi->mi_db->db_nbits, mi->mi_offset);
02408             if (PBM_ISSET(mi->mi_offset, set))
02409                 rpmrc = RPMRC_OK;
02410         }
02411 
02412         /* If blob is unchecked, check blob import consistency now. */
02413         if (rpmrc != RPMRC_OK) {
02414             const char * msg = NULL;
02415             int lvl;
02416 
02417             rpmrc = (*mi->mi_hdrchk) (mi->mi_ts, uh, uhlen, &msg);
02418             lvl = (rpmrc == RPMRC_FAIL ? RPMMESS_ERROR : RPMMESS_DEBUG);
02419             rpmMessage(lvl, "%s h#%8u %s",
02420                 (rpmrc == RPMRC_FAIL ? _("rpmdbNextIterator: skipping") : " read"),
02421                         mi->mi_offset, (msg ? msg : "\n"));
02422             msg = _free(msg);
02423 
02424             /* Mark header checked. */
02425             if (mi->mi_db && mi->mi_db->db_bits && rpmrc == RPMRC_OK) {
02426                 pbm_set * set;
02427 
02428                 set = PBM_REALLOC((pbm_set **)&mi->mi_db->db_bits,
02429                         &mi->mi_db->db_nbits, mi->mi_offset);
02430                 PBM_SET(mi->mi_offset, set);
02431             }
02432 
02433             /* Skip damaged and inconsistent headers. */
02434             if (rpmrc == RPMRC_FAIL)
02435                 goto top;
02436         }
02437     }
02438 /*@=boundsread =branchstate =sizeoftype @*/
02439 
02440     /* Did the header blob load correctly? */
02441 #if !defined(_USE_COPY_LOAD)
02442 /*@-onlytrans@*/
02443     mi->mi_h = headerLoad(uh);
02444 /*@=onlytrans@*/
02445     if (mi->mi_h)
02446         mi->mi_h->flags |= HEADERFLAG_ALLOCATED;
02447 #else
02448     mi->mi_h = headerCopyLoad(uh);
02449 #endif
02450     if (mi->mi_h == NULL || !headerIsEntry(mi->mi_h, RPMTAG_NAME)) {
02451         rpmError(RPMERR_BADHEADER,
02452                 _("rpmdb: damaged header #%u retrieved -- skipping.\n"),
02453                 mi->mi_offset);
02454         goto top;
02455     }
02456 
02457     /*
02458      * Skip this header if iterator selector (if any) doesn't match.
02459      */
02460     if (mireSkip(mi)) {
02461         /* XXX hack, can't restart with Packages locked on single instance. */
02462         if (mi->mi_set || mi->mi_keyp == NULL)
02463             goto top;
02464         return NULL;
02465     }
02466 
02467     /* Mark header with its instance number. */
02468     {   char origin[32];
02469         sprintf(origin, "rpmdb (h#%u)", mi->mi_offset);
02470         (void) headerSetOrigin(mi->mi_h, origin);
02471         (void) headerSetInstance(mi->mi_h, mi->mi_offset);
02472     }
02473 
02474     mi->mi_prevoffset = mi->mi_offset;
02475     mi->mi_modified = 0;
02476 
02477 /*@-compdef -retalias -retexpose -usereleased @*/
02478     return mi->mi_h;
02479 /*@=compdef =retalias =retexpose =usereleased @*/
02480 }
02481 /*@=nullstate@*/
02482 
02483 static void rpmdbSortIterator(/*@null@*/ rpmdbMatchIterator mi)
02484         /*@modifies mi @*/
02485 {
02486     if (mi && mi->mi_set && mi->mi_set->recs && mi->mi_set->count > 0) {
02487     /*
02488      * mergesort is much (~10x with lots of identical basenames) faster
02489      * than pure quicksort, but glibc uses msort_with_tmp() on stack.
02490      */
02491 #if defined(__GLIBC__)
02492 /*@-boundsread@*/
02493         qsort(mi->mi_set->recs, mi->mi_set->count,
02494                 sizeof(*mi->mi_set->recs), hdrNumCmp);
02495 /*@=boundsread@*/
02496 #else
02497         mergesort(mi->mi_set->recs, mi->mi_set->count,
02498                 sizeof(*mi->mi_set->recs), hdrNumCmp);
02499 #endif
02500         mi->mi_sorted = 1;
02501     }
02502 }
02503 
02504 /*@-bounds@*/ /* LCL: segfault */
02505 static int rpmdbGrowIterator(/*@null@*/ rpmdbMatchIterator mi, int fpNum,
02506                 unsigned int exclude, unsigned int tag)
02507         /*@globals rpmGlobalMacroContext, h_errno, fileSystem, internalState @*/
02508         /*@modifies mi, rpmGlobalMacroContext, fileSystem, internalState @*/
02509 {
02510     DBC * dbcursor;
02511     DBT * key;
02512     DBT * data;
02513     dbiIndex dbi = NULL;
02514     dbiIndexSet set;
02515     int rc;
02516     int xx;
02517     int i, j;
02518 
02519     if (mi == NULL)
02520         return 1;
02521 
02522     dbcursor = mi->mi_dbc;
02523     key = &mi->mi_key;
02524     data = &mi->mi_data;
02525     if (key->data == NULL)
02526         return 1;
02527 
02528     dbi = dbiOpen(mi->mi_db, mi->mi_rpmtag, 0);
02529     if (dbi == NULL)
02530         return 1;
02531 
02532     xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
02533     rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
02534 #ifndef SQLITE_HACK
02535     xx = dbiCclose(dbi, dbcursor, 0);
02536     dbcursor = NULL;
02537 #endif
02538 
02539     if (rc) {                   /* error/not found */
02540         if (rc != DB_NOTFOUND)
02541             rpmError(RPMERR_DBGETINDEX,
02542                 _("error(%d) getting \"%s\" records from %s index\n"),
02543                 rc, key->data, tagName(dbi->dbi_rpmtag));
02544 #ifdef  SQLITE_HACK
02545         xx = dbiCclose(dbi, dbcursor, 0);
02546         dbcursor = NULL;
02547 #endif
02548         return rc;
02549     }
02550 
02551     set = NULL;
02552     (void) dbt2set(dbi, data, &set);
02553 
02554     /* prune the set against exclude and tag */
02555     for (i = j = 0; i < set->count; i++) {
02556         if (exclude && set->recs[i].hdrNum == exclude)
02557             continue;
02558         if (_db_tagged_file_indices && set->recs[i].tagNum & 0x80000000) {
02559             /* tagged entry */
02560             if ((set->recs[i].tagNum & 0xffff0000) != tag)
02561                 continue;
02562             set->recs[i].tagNum &= 0x0000ffff;
02563         }
02564         if (i > j)
02565             set->recs[j] = set->recs[i];
02566         j++;
02567     }
02568     if (j == 0) {
02569 #ifdef  SQLITE_HACK
02570         xx = dbiCclose(dbi, dbcursor, 0);
02571         dbcursor = NULL;
02572 #endif
02573         set = dbiFreeIndexSet(set);
02574         return DB_NOTFOUND;
02575     }
02576     set->count = j;
02577 
02578     for (i = 0; i < set->count; i++)
02579         set->recs[i].fpNum = fpNum;
02580 
02581 #ifdef  SQLITE_HACK
02582     xx = dbiCclose(dbi, dbcursor, 0);
02583     dbcursor = NULL;
02584 #endif
02585 
02586 /*@-branchstate@*/
02587     if (mi->mi_set == NULL) {
02588         mi->mi_set = set;
02589     } else {
02590 #if 0
02591 fprintf(stderr, "+++ %d = %d + %d\t\"%s\"\n", (mi->mi_set->count + set->count), mi->mi_set->count, set->count, ((char *)key->data));
02592 #endif
02593         mi->mi_set->recs = xrealloc(mi->mi_set->recs,
02594                 (mi->mi_set->count + set->count) * sizeof(*(mi->mi_set->recs)));
02595         memcpy(mi->mi_set->recs + mi->mi_set->count, set->recs,
02596                 set->count * sizeof(*(mi->mi_set->recs)));
02597         mi->mi_set->count += set->count;
02598         set = dbiFreeIndexSet(set);
02599     }
02600 /*@=branchstate@*/
02601 
02602     return rc;
02603 }
02604 /*@=bounds@*/
02605 
02606 int rpmdbPruneIterator(rpmdbMatchIterator mi, int * hdrNums,
02607         int nHdrNums, int sorted)
02608 {
02609     if (mi == NULL || hdrNums == NULL || nHdrNums <= 0)
02610         return 1;
02611 
02612     if (mi->mi_set)
02613         (void) dbiPruneSet(mi->mi_set, hdrNums, nHdrNums, sizeof(*hdrNums), sorted);
02614     return 0;
02615 }
02616 
02617 int rpmdbAppendIterator(rpmdbMatchIterator mi, const int * hdrNums, int nHdrNums)
02618 {
02619     if (mi == NULL || hdrNums == NULL || nHdrNums <= 0)
02620         return 1;
02621 
02622     if (mi->mi_set == NULL)
02623         mi->mi_set = xcalloc(1, sizeof(*mi->mi_set));
02624     (void) dbiAppendSet(mi->mi_set, hdrNums, nHdrNums, sizeof(*hdrNums), 0);
02625     return 0;
02626 }
02627 
02628 rpmdbMatchIterator rpmdbInitIterator(rpmdb db, rpmTag rpmtag,
02629                 const void * keyp, size_t keylen)
02630         /*@globals rpmmiRock @*/
02631         /*@modifies rpmmiRock @*/
02632 {
02633     rpmdbMatchIterator mi;
02634     DBT * key;
02635     DBT * data;
02636     dbiIndexSet set = NULL;
02637     dbiIndex dbi;
02638     const void * mi_keyp = NULL;
02639     int isLabel = 0;
02640 
02641     if (db == NULL)
02642         return NULL;
02643 
02644     (void) rpmdbCheckSignals();
02645 
02646     /* XXX HACK to remove rpmdbFindByLabel/findMatches from the API */
02647     if (rpmtag == RPMDBI_LABEL) {
02648         rpmtag = RPMTAG_NAME;
02649         isLabel = 1;
02650     }
02651 
02652     dbi = dbiOpen(db, rpmtag, 0);
02653     if (dbi == NULL)
02654         return NULL;
02655 
02656     /* Chain cursors for teardown on abnormal exit. */
02657     mi = xcalloc(1, sizeof(*mi));
02658     mi->mi_next = rpmmiRock;
02659     rpmmiRock = mi;
02660 
02661     key = &mi->mi_key;
02662     data = &mi->mi_data;
02663 
02664     /*
02665      * Handle label and file name special cases.
02666      * Otherwise, retrieve join keys for secondary lookup.
02667      */
02668 /*@-branchstate@*/
02669     if (rpmtag != RPMDBI_PACKAGES && keyp) {
02670         DBC * dbcursor = NULL;
02671         int rc;
02672         int xx;
02673 
02674         if (isLabel) {
02675             xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
02676             rc = dbiFindByLabel(dbi, dbcursor, key, data, keyp, &set);
02677             xx = dbiCclose(dbi, dbcursor, 0);
02678             dbcursor = NULL;
02679         } else if (rpmtag == RPMTAG_BASENAMES) {
02680             rc = rpmdbFindByFile(db, keyp, key, data, &set);
02681         } else {
02682             xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, 0);
02683 
02684 /*@-temptrans@*/
02685 key->data = (void *) keyp;
02686 /*@=temptrans@*/
02687 key->size = keylen;
02688 if (key->data && key->size == 0) key->size = strlen((char *)key->data);
02689 if (key->data && key->size == 0) key->size++;   /* XXX "/" fixup. */
02690 
02691 /*@-nullstate@*/
02692             rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
02693 /*@=nullstate@*/
02694             if (rc > 0) {
02695                 rpmError(RPMERR_DBGETINDEX,
02696                         _("error(%d) getting \"%s\" records from %s index\n"),
02697                         rc, (key->data ? key->data : "???"), tagName(dbi->dbi_rpmtag));
02698             }
02699 
02700             /* Join keys need to be native endian internally. */
02701             if (rc == 0)
02702                 (void) dbt2set(dbi, data, &set);
02703 
02704             xx = dbiCclose(dbi, dbcursor, 0);
02705             dbcursor = NULL;
02706         }
02707         if (rc) {       /* error/not found */
02708             set = dbiFreeIndexSet(set);
02709             rpmmiRock = mi->mi_next;
02710             mi->mi_next = NULL;
02711             mi = _free(mi);
02712             return NULL;
02713         }
02714     }
02715 /*@=branchstate@*/
02716 
02717     /* Copy the retrieval key, byte swapping header instance if necessary. */
02718     if (keyp) {
02719         switch (rpmtag) {
02720         case RPMDBI_PACKAGES:
02721           { union _dbswap *k;
02722 
02723 assert(keylen == sizeof(k->ui));                /* xxx programmer error */
02724             k = xmalloc(sizeof(*k));
02725             memcpy(k, keyp, keylen);
02726             if (dbiByteSwapped(dbi) == 1)
02727                 _DBSWAP(*k);
02728             mi_keyp = k;
02729           } break;
02730         default:
02731           { char * k;
02732             if (keylen == 0)
02733                 keylen = strlen(keyp);
02734             k = xmalloc(keylen + 1);
02735 /*@-boundsread@*/
02736             memcpy(k, keyp, keylen);
02737 /*@=boundsread@*/
02738             k[keylen] = '\0';   /* XXX assumes strings */
02739             mi_keyp = k;
02740           } break;
02741         }
02742     }
02743 
02744     mi->mi_keyp = mi_keyp;
02745     mi->mi_keylen = keylen;
02746 
02747     mi->mi_db = rpmdbLink(db, "matchIterator");
02748     mi->mi_rpmtag = rpmtag;
02749 
02750     mi->mi_dbc = NULL;
02751     mi->mi_set = set;
02752     mi->mi_setx = 0;
02753     mi->mi_h = NULL;
02754     mi->mi_sorted = 0;
02755     mi->mi_cflags = 0;
02756     mi->mi_modified = 0;
02757     mi->mi_prevoffset = 0;
02758     mi->mi_offset = 0;
02759     mi->mi_filenum = 0;
02760     mi->mi_nre = 0;
02761     mi->mi_re = NULL;
02762 
02763     mi->mi_ts = NULL;
02764     mi->mi_hdrchk = NULL;
02765 
02766 /*@i@*/ return mi;
02767 }
02768 
02769 /* XXX psm.c */
02770 int rpmdbRemove(rpmdb db, /*@unused@*/ int rid, unsigned int hdrNum,
02771                 /*@unused@*/ rpmts ts,
02772                 /*@unused@*/ rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, const char ** msg))
02773 {
02774 DBC * dbcursor = NULL;
02775 DBT * key = alloca(sizeof(*key));
02776 DBT * data = alloca(sizeof(*data));
02777 union _dbswap mi_offset;
02778     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
02779     HFD_t hfd = headerFreeData;
02780     Header h;
02781     sigset_t signalMask;
02782     int ret = 0;
02783     int rc = 0;
02784 
02785     if (db == NULL)
02786         return 0;
02787 
02788 memset(key, 0, sizeof(*key));
02789 memset(data, 0, sizeof(*data));
02790 
02791     {   rpmdbMatchIterator mi;
02792         mi = rpmdbInitIterator(db, RPMDBI_PACKAGES, &hdrNum, sizeof(hdrNum));
02793         h = rpmdbNextIterator(mi);
02794         if (h)
02795             h = headerLink(h);
02796         mi = rpmdbFreeIterator(mi);
02797     }
02798 
02799     if (h == NULL) {
02800         rpmError(RPMERR_DBCORRUPT, _("%s: cannot read header at 0x%x\n"),
02801               "rpmdbRemove", hdrNum);
02802         return 1;
02803     }
02804 
02805 #ifdef  DYING
02806     /* Add remove transaction id to header. */
02807     if (rid != 0 && rid != -1) {
02808         int_32 tid = rid;
02809         (void) headerAddEntry(h, RPMTAG_REMOVETID, RPM_INT32_TYPE, &tid, 1);
02810     }
02811 #endif
02812 
02813     {   const char *n, *v, *r;
02814         (void) headerNVR(h, &n, &v, &r);
02815         rpmMessage(RPMMESS_DEBUG, "  --- h#%8u %s-%s-%s\n", hdrNum, n, v, r);
02816     }
02817 
02818     (void) blockSignals(db, &signalMask);
02819 
02820         /*@-nullpass -nullptrarith -nullderef @*/ /* FIX: rpmvals heartburn */
02821     {   int dbix;
02822         dbiIndexItem rec = dbiIndexNewItem(hdrNum, 0);
02823 
02824         if (db->db_tagn != NULL)
02825         for (dbix = 0; dbix < db->db_ndbi; dbix++) {
02826             dbiIndex dbi;
02827             const char *av[1];
02828             const char ** rpmvals = NULL;
02829             byte * bin = NULL;
02830             rpmTagType rpmtype = 0;
02831             int rpmcnt = 0;
02832             int rpmtag;
02833             int xx;
02834             int i, j;
02835 
02836             dbi = NULL;
02837 /*@-boundsread@*/
02838             rpmtag = db->db_tagn[dbix];
02839 /*@=boundsread@*/
02840 
02841             /*@-branchstate@*/
02842             switch (rpmtag) {
02843             /* Filter out temporary databases */
02844             case RPMDBI_AVAILABLE:
02845             case RPMDBI_ADDED:
02846             case RPMDBI_REMOVED:
02847             case RPMDBI_DEPENDS:
02848                 continue;
02849                 /*@notreached@*/ /*@switchbreak@*/ break;
02850             case RPMDBI_PACKAGES:
02851                 if (db->db_export != NULL)
02852                     xx = db->db_export(db, h, 0);
02853                 dbi = dbiOpen(db, rpmtag, 0);
02854                 if (dbi == NULL)        /* XXX shouldn't happen */
02855                     continue;
02856               
02857 /*@-immediatetrans@*/
02858 mi_offset.ui = hdrNum;
02859 if (dbiByteSwapped(dbi) == 1)
02860     _DBSWAP(mi_offset);
02861                 key->data = &mi_offset;
02862 /*@=immediatetrans@*/
02863                 key->size = sizeof(mi_offset.ui);
02864 
02865                 rc = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
02866                 rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
02867                 if (rc) {
02868                     rpmError(RPMERR_DBGETINDEX,
02869                         _("error(%d) setting header #%d record for %s removal\n"),
02870                         rc, hdrNum, tagName(dbi->dbi_rpmtag));
02871                 } else
02872                     rc = dbiDel(dbi, dbcursor, key, data, 0);
02873                 xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
02874                 dbcursor = NULL;
02875                 if (!dbi->dbi_no_dbsync)
02876                     xx = dbiSync(dbi, 0);
02877                 continue;
02878                 /*@notreached@*/ /*@switchbreak@*/ break;
02879             }
02880             /*@=branchstate@*/
02881         
02882             if (!hge(h, rpmtag, &rpmtype, (void **) &rpmvals, &rpmcnt))
02883                 continue;
02884 
02885           dbi = dbiOpen(db, rpmtag, 0);
02886           if (dbi != NULL) {
02887             int printed;
02888 
02889             if (rpmtype == RPM_STRING_TYPE) {
02890                 /* XXX force uniform headerGetEntry return */
02891                 av[0] = (const char *) rpmvals;
02892                 rpmvals = av;
02893                 rpmcnt = 1;
02894             }
02895 
02896             printed = 0;
02897             xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
02898 /*@-branchstate@*/
02899             for (i = 0; i < rpmcnt; i++) {
02900                 dbiIndexSet set;
02901                 int stringvalued;
02902 
02903                 bin = _free(bin);
02904                 switch (dbi->dbi_rpmtag) {
02905                 case RPMTAG_FILEDIGESTS:
02906                     /* Filter out empty file digests. */
02907                     if (!(rpmvals[i] && *rpmvals[i] != '\0'))
02908                         /*@innercontinue@*/ continue;
02909                     /*@switchbreak@*/ break;
02910                 default:
02911                     /*@switchbreak@*/ break;
02912                 }
02913 
02914                 /* Identify value pointer and length. */
02915                 stringvalued = 0;
02916                 switch (rpmtype) {
02917 /*@-sizeoftype@*/
02918                 case RPM_CHAR_TYPE:
02919                 case RPM_INT8_TYPE:
02920                     key->size = sizeof(RPM_CHAR_TYPE);
02921                     key->data = rpmvals + i;
02922                     /*@switchbreak@*/ break;
02923                 case RPM_INT16_TYPE:
02924                     key->size = sizeof(int_16);
02925                     key->data = rpmvals + i;
02926                     /*@switchbreak@*/ break;
02927                 case RPM_INT32_TYPE:
02928                     key->size = sizeof(int_32);
02929                     key->data = rpmvals + i;
02930                     /*@switchbreak@*/ break;
02931 /*@=sizeoftype@*/
02932                 case RPM_BIN_TYPE:
02933                     key->size = rpmcnt;
02934                     key->data = rpmvals;
02935                     rpmcnt = 1;         /* XXX break out of loop. */
02936                     /*@switchbreak@*/ break;
02937                 case RPM_STRING_TYPE:
02938                 case RPM_I18NSTRING_TYPE:
02939                     rpmcnt = 1;         /* XXX break out of loop. */
02940                     /*@fallthrough@*/
02941                 case RPM_STRING_ARRAY_TYPE:
02942                     /* Convert from hex to binary. */
02943 /*@-boundsread@*/
02944                     if (dbi->dbi_rpmtag == RPMTAG_FILEDIGESTS) {
02945                         const char * s = rpmvals[i];
02946                         size_t dlen = strlen(s);
02947                         byte * t;
02948 assert((dlen & 1) == 0);
02949                         dlen /= 2;
02950                         bin = t = xcalloc(1, dlen);
02951                         for (j = 0; j < dlen; j++, t++, s += 2)
02952                             *t = (nibble(s[0]) << 4) | nibble(s[1]);
02953                         key->data = bin;
02954                         key->size = dlen;
02955                         /*@switchbreak@*/ break;
02956                     }
02957                     /* Extract the pubkey id from the base64 blob. */
02958                     if (dbi->dbi_rpmtag == RPMTAG_PUBKEYS) {
02959                         int nbin;
02960                         bin = xcalloc(1, 32);
02961                         nbin = pgpExtractPubkeyFingerprint(rpmvals[i], bin);
02962                         if (nbin <= 0)
02963                             /*@innercontinue@*/ continue;
02964                         key->data = bin;
02965                         key->size = nbin;
02966                         /*@switchbreak@*/ break;
02967                     }
02968 /*@=boundsread@*/
02969                     /*@fallthrough@*/
02970                 default:
02971 /*@i@*/             key->data = (void *) rpmvals[i];
02972                     key->size = strlen(rpmvals[i]);
02973                     stringvalued = 1;
02974                     /*@switchbreak@*/ break;
02975                 }
02976 
02977                 if (!printed) {
02978                     if (rpmcnt == 1 && stringvalued) {
02979                         rpmMessage(RPMMESS_DEBUG,
02980                                 _("removing \"%s\" from %s index.\n"),
02981                                 (char *)key->data, tagName(dbi->dbi_rpmtag));
02982                     } else {
02983                         rpmMessage(RPMMESS_DEBUG,
02984                                 _("removing %d entries from %s index.\n"),
02985                                 rpmcnt, tagName(dbi->dbi_rpmtag));
02986                     }
02987                     printed++;
02988                 }
02989 
02990                 /* XXX
02991                  * This is almost right, but, if there are duplicate tag
02992                  * values, there will be duplicate attempts to remove
02993                  * the header instance. It's faster to just ignore errors
02994                  * than to do things correctly.
02995                  */
02996 
02997 /* XXX with duplicates, an accurate data value and DB_GET_BOTH is needed. */
02998 
02999                 set = NULL;
03000 
03001 if (key->size == 0) key->size = strlen((char *)key->data);
03002 if (key->size == 0) key->size++;        /* XXX "/" fixup. */
03003  
03004 /*@-compmempass@*/
03005                 rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
03006                 if (rc == 0) {                  /* success */
03007                     (void) dbt2set(dbi, data, &set);
03008                 } else if (rc == DB_NOTFOUND) { /* not found */
03009                     /*@innercontinue@*/ continue;
03010                 } else {                        /* error */
03011                     rpmError(RPMERR_DBGETINDEX,
03012                         _("error(%d) setting \"%s\" records from %s index\n"),
03013                         rc, key->data, tagName(dbi->dbi_rpmtag));
03014                     ret += 1;
03015                     /*@innercontinue@*/ continue;
03016                 }
03017 /*@=compmempass@*/
03018 
03019                 rc = dbiPruneSet(set, rec, 1, sizeof(*rec), 1);
03020 
03021                 /* If nothing was pruned, then don't bother updating. */
03022                 if (rc) {
03023                     set = dbiFreeIndexSet(set);
03024                     /*@innercontinue@*/ continue;
03025                 }
03026 
03027 /*@-compmempass@*/
03028                 if (set->count > 0) {
03029                     (void) set2dbt(dbi, data, set);
03030                     rc = dbiPut(dbi, dbcursor, key, data, DB_KEYLAST);
03031                     if (rc) {
03032                         rpmError(RPMERR_DBPUTINDEX,
03033                                 _("error(%d) storing record \"%s\" into %s\n"),
03034                                 rc, key->data, tagName(dbi->dbi_rpmtag));
03035                         ret += 1;
03036                     }
03037                     data->data = _free(data->data);
03038                     data->size = 0;
03039                 } else {
03040                     rc = dbiDel(dbi, dbcursor, key, data, 0);
03041                     if (rc) {
03042                         rpmError(RPMERR_DBPUTINDEX,
03043                                 _("error(%d) removing record \"%s\" from %s\n"),
03044                                 rc, key->data, tagName(dbi->dbi_rpmtag));
03045                         ret += 1;
03046                     }
03047                 }
03048 /*@=compmempass@*/
03049                 set = dbiFreeIndexSet(set);
03050             }
03051 /*@=branchstate@*/
03052 
03053             xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
03054             dbcursor = NULL;
03055 
03056             if (!dbi->dbi_no_dbsync)
03057                 xx = dbiSync(dbi, 0);
03058           }
03059 
03060             if (rpmtype != RPM_BIN_TYPE)        /* XXX WTFO? HACK ALERT */
03061                 rpmvals = hfd(rpmvals, rpmtype);
03062             rpmtype = 0;
03063             rpmcnt = 0;
03064             bin = _free(bin);
03065         }
03066 
03067         rec = _free(rec);
03068     }
03069     /*@=nullpass =nullptrarith =nullderef @*/
03070 
03071     (void) unblockSignals(db, &signalMask);
03072 
03073     h = headerFree(h);
03074 
03075     /* XXX return ret; */
03076     return 0;
03077 }
03078 
03079 /* XXX install.c */
03080 int rpmdbAdd(rpmdb db, int iid, Header h,
03081                 /*@unused@*/ rpmts ts,
03082                 /*@unused@*/ rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, const char ** msg))
03083 {
03084 DBC * dbcursor = NULL;
03085 DBT * key = alloca(sizeof(*key));
03086 DBT * data = alloca(sizeof(*data));
03087     HGE_t hge = (HGE_t) headerGetEntryMinMemory;
03088     HAE_t hae = (HAE_t) headerAddEntry;
03089     HFD_t hfd = headerFreeData;
03090     sigset_t signalMask;
03091     const char ** baseNames;
03092     rpmTagType bnt;
03093     const char ** dirNames;
03094     int_32 * dirIndexes;
03095     rpmTagType dit, dnt;
03096     int count = 0;
03097     dbiIndex dbi;
03098     int dbix;
03099     union _dbswap mi_offset;
03100     unsigned int hdrNum = 0;
03101     int ret = 0;
03102     int rc;
03103     int xx;
03104 
03105     /* Initialize the header instance */
03106     (void) headerSetInstance(h, 0);
03107 
03108     if (db == NULL)
03109         return 0;
03110 
03111 memset(key, 0, sizeof(*key));
03112 memset(data, 0, sizeof(*data));
03113 
03114 #ifdef  NOTYET  /* XXX headerRemoveEntry() broken on dribbles. */
03115     xx = headerRemoveEntry(h, RPMTAG_REMOVETID);
03116 #endif
03117     if (iid != 0 && iid != -1) {
03118         int_32 tid = iid;
03119         if (!headerIsEntry(h, RPMTAG_INSTALLTID))
03120            xx = hae(h, RPMTAG_INSTALLTID, RPM_INT32_TYPE, &tid, 1);
03121     }
03122 
03123     /* Add the package color if not present. */
03124     if (!headerIsEntry(h, RPMTAG_PACKAGECOLOR)) {
03125         uint32_t hcolor = hGetColor(h);
03126         xx = hae(h, RPMTAG_PACKAGECOLOR, RPM_INT32_TYPE, &hcolor, 1);
03127     }
03128 
03129     /*
03130      * If old style filename tags is requested, the basenames need to be
03131      * retrieved early, and the header needs to be converted before
03132      * being written to the package header database.
03133      */
03134 
03135     xx = hge(h, RPMTAG_BASENAMES, &bnt, (void **) &baseNames, &count);
03136     xx = hge(h, RPMTAG_DIRINDEXES, &dit, (void **) &dirIndexes, NULL);
03137     xx = hge(h, RPMTAG_DIRNAMES, &dnt, (void **) &dirNames, NULL);
03138 
03139     (void) blockSignals(db, &signalMask);
03140 
03141     {
03142         unsigned int firstkey = 0;
03143         void * keyp = &firstkey;
03144         size_t keylen = sizeof(firstkey);
03145         void * datap = NULL;
03146         size_t datalen = 0;
03147 
03148       dbi = dbiOpen(db, RPMDBI_PACKAGES, 0);
03149       /*@-branchstate@*/
03150       if (dbi != NULL) {
03151 
03152         /* XXX db0: hack to pass sizeof header to fadAlloc */
03153         datap = h;
03154         datalen = headerSizeof(h, HEADER_MAGIC_NO);
03155 
03156         xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
03157 
03158         /* Retrieve join key for next header instance. */
03159 
03160 /*@-compmempass@*/
03161         key->data = keyp;
03162         key->size = keylen;
03163 /*@i@*/ data->data = datap;
03164         data->size = datalen;
03165         ret = dbiGet(dbi, dbcursor, key, data, DB_SET);
03166         keyp = key->data;
03167         keylen = key->size;
03168         datap = data->data;
03169         datalen = data->size;
03170 /*@=compmempass@*/
03171 
03172 /*@-bounds@*/
03173         hdrNum = 0;
03174         if (ret == 0 && datap) {
03175             memcpy(&mi_offset, datap, sizeof(mi_offset.ui));
03176             if (dbiByteSwapped(dbi) == 1)
03177                 _DBSWAP(mi_offset);
03178             hdrNum = mi_offset.ui;
03179         }
03180         ++hdrNum;
03181         mi_offset.ui = hdrNum;
03182         if (dbiByteSwapped(dbi) == 1)
03183             _DBSWAP(mi_offset);
03184         if (ret == 0 && datap) {
03185             memcpy(datap, &mi_offset, sizeof(mi_offset.ui));
03186         } else {
03187             datap = &mi_offset;
03188             datalen = sizeof(mi_offset.ui);
03189         }
03190 /*@=bounds@*/
03191 
03192         key->data = keyp;
03193         key->size = keylen;
03194 /*@-kepttrans@*/
03195         data->data = datap;
03196 /*@=kepttrans@*/
03197         data->size = datalen;
03198 
03199 /*@-compmempass@*/
03200         ret = dbiPut(dbi, dbcursor, key, data, DB_KEYLAST);
03201 /*@=compmempass@*/
03202         xx = dbiSync(dbi, 0);
03203 
03204         xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
03205         dbcursor = NULL;
03206       }
03207       /*@=branchstate@*/
03208 
03209     }
03210 
03211     if (ret) {
03212         rpmError(RPMERR_DBCORRUPT,
03213                 _("error(%d) allocating new package instance\n"), ret);
03214         goto exit;
03215     }
03216 
03217     /* Now update the indexes */
03218 
03219     if (hdrNum)
03220     {   
03221         dbiIndexItem rec = dbiIndexNewItem(hdrNum, 0);
03222 
03223         /* Save the header instance. */
03224         (void) headerSetInstance(h, hdrNum);
03225         
03226         if (db->db_tagn != NULL)
03227         for (dbix = 0; dbix < db->db_ndbi; dbix++) {
03228             const char *av[1];
03229             const char **rpmvals = NULL;
03230             byte * bin = NULL;
03231             rpmTagType rpmtype = 0;
03232             int rpmcnt = 0;
03233             int rpmtag;
03234             int_32 * requireFlags;
03235             rpmRC rpmrc;
03236             int i, j;
03237 
03238             rpmrc = RPMRC_NOTFOUND;
03239             dbi = NULL;
03240             requireFlags = NULL;
03241 /*@-boundsread@*/
03242             rpmtag = db->db_tagn[dbix];
03243 /*@=boundsread@*/
03244 
03245             switch (rpmtag) {
03246             /* Filter out temporary databases */
03247             case RPMDBI_AVAILABLE:
03248             case RPMDBI_ADDED:
03249             case RPMDBI_REMOVED:
03250             case RPMDBI_DEPENDS:
03251                 continue;
03252                 /*@notreached@*/ /*@switchbreak@*/ break;
03253             case RPMDBI_PACKAGES:
03254                 if (db->db_export != NULL)
03255                     xx = db->db_export(db, h, 1);
03256                 dbi = dbiOpen(db, rpmtag, 0);
03257                 if (dbi == NULL)        /* XXX shouldn't happen */
03258                     continue;
03259                 xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
03260 
03261 mi_offset.ui = hdrNum;
03262 if (dbiByteSwapped(dbi) == 1)
03263     _DBSWAP(mi_offset);
03264 /*@-immediatetrans@*/
03265 key->data = (void *) &mi_offset;
03266 /*@=immediatetrans@*/
03267 key->size = sizeof(mi_offset.ui);
03268 data->data = headerUnload(h);
03269 data->size = headerSizeof(h, HEADER_MAGIC_NO);
03270 
03271                 /* Check header digest/signature on blob export. */
03272                 if (hdrchk && ts) {
03273                     const char * msg = NULL;
03274                     int lvl;
03275 
03276                     rpmrc = (*hdrchk) (ts, data->data, data->size, &msg);
03277                     lvl = (rpmrc == RPMRC_FAIL ? RPMMESS_ERROR : RPMMESS_DEBUG);
03278                     rpmMessage(lvl, "%s h#%8u %s",
03279                         (rpmrc == RPMRC_FAIL ? _("rpmdbAdd: skipping") : "  +++"),
03280                                 hdrNum, (msg ? msg : "\n"));
03281                     msg = _free(msg);
03282                 }
03283 
03284                 if (data->data != NULL && rpmrc != RPMRC_FAIL) {
03285 /*@-compmempass@*/
03286                     xx = dbiPut(dbi, dbcursor, key, data, DB_KEYLAST);
03287 /*@=compmempass@*/
03288                     xx = dbiSync(dbi, 0);
03289                 }
03290 data->data = _free(data->data);
03291 data->size = 0;
03292                 xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
03293                 dbcursor = NULL;
03294                 if (!dbi->dbi_no_dbsync)
03295                     xx = dbiSync(dbi, 0);
03296                 continue;
03297                 /*@notreached@*/ /*@switchbreak@*/ break;
03298             case RPMTAG_BASENAMES:      /* XXX preserve legacy behavior */
03299                 rpmtype = bnt;
03300                 rpmvals = baseNames;
03301                 rpmcnt = count;
03302                 /*@switchbreak@*/ break;
03303             case RPMTAG_REQUIRENAME:
03304                 xx = hge(h, rpmtag, &rpmtype, (void **)&rpmvals, &rpmcnt);
03305                 xx = hge(h, RPMTAG_REQUIREFLAGS, NULL, (void **)&requireFlags, NULL);
03306                 /*@switchbreak@*/ break;
03307             default:
03308                 xx = hge(h, rpmtag, &rpmtype, (void **)&rpmvals, &rpmcnt);
03309                 /*@switchbreak@*/ break;
03310             }
03311 
03312             /*@-branchstate@*/
03313             if (rpmcnt <= 0) {
03314                 if (rpmtag != RPMTAG_GROUP)
03315                     continue;
03316 
03317                 /* XXX preserve legacy behavior */
03318                 rpmtype = RPM_STRING_TYPE;
03319                 rpmvals = (const char **) "Unknown";
03320                 rpmcnt = 1;
03321             }
03322             /*@=branchstate@*/
03323 
03324           dbi = dbiOpen(db, rpmtag, 0);
03325           if (dbi != NULL) {
03326             int printed;
03327 
03328             if (rpmtype == RPM_STRING_TYPE) {
03329                 /* XXX force uniform headerGetEntry return */
03330                 /*@-observertrans@*/
03331                 av[0] = (const char *) rpmvals;
03332                 /*@=observertrans@*/
03333                 rpmvals = av;
03334                 rpmcnt = 1;
03335             }
03336 
03337             printed = 0;
03338             xx = dbiCopen(dbi, dbi->dbi_txnid, &dbcursor, DB_WRITECURSOR);
03339 
03340 /*@-branchstate@*/
03341             for (i = 0; i < rpmcnt; i++) {
03342                 dbiIndexSet set;
03343                 int stringvalued;
03344 
03345                 bin = _free(bin);
03346                 /*
03347                  * Include the tagNum in all indices. rpm-3.0.4 and earlier
03348                  * included the tagNum only for files.
03349                  */
03350                 rec->tagNum = i;
03351                 switch (dbi->dbi_rpmtag) {
03352                 case RPMTAG_BASENAMES:
03353                     /* tag index entry with directory hash */
03354                     if (_db_tagged_file_indices && i < 0x010000)
03355                         rec->tagNum |= taghash(dirNames[dirIndexes[i]]);
03356                     /*@switchbreak@*/ break;
03357                 case RPMTAG_PUBKEYS:
03358                     /*@switchbreak@*/ break;
03359                 case RPMTAG_FILEMD5S:
03360                     /* Filter out empty MD5 strings. */
03361                     if (!(rpmvals[i] && *rpmvals[i] != '\0'))
03362                         /*@innercontinue@*/ continue;
03363                     /*@switchbreak@*/ break;
03364                 case RPMTAG_REQUIRENAME:
03365                     /* Filter out install prerequisites. */
03366                     if (requireFlags && isInstallPreReq(requireFlags[i]))
03367                         /*@innercontinue@*/ continue;
03368                     /*@switchbreak@*/ break;
03369                 case RPMTAG_TRIGGERNAME:
03370                     if (i) {    /* don't add duplicates */
03371 /*@-boundsread@*/
03372                         for (j = 0; j < i; j++) {
03373                             if (!strcmp(rpmvals[i], rpmvals[j]))
03374                                 /*@innerbreak@*/ break;
03375                         }
03376 /*@=boundsread@*/
03377                         if (j < i)
03378                             /*@innercontinue@*/ continue;
03379                     }
03380                     /*@switchbreak@*/ break;
03381                 default:
03382                     /*@switchbreak@*/ break;
03383                 }
03384 
03385                 /* Identify value pointer and length. */
03386                 stringvalued = 0;
03387                 switch (rpmtype) {
03388 /*@-sizeoftype@*/
03389                 case RPM_CHAR_TYPE:
03390                 case RPM_INT8_TYPE:
03391                     key->size = sizeof(int_8);
03392 /*@i@*/             key->data = rpmvals + i;
03393                     /*@switchbreak@*/ break;
03394                 case RPM_INT16_TYPE:
03395                     key->size = sizeof(int_16);
03396 /*@i@*/             key->data = rpmvals + i;
03397                     /*@switchbreak@*/ break;
03398                 case RPM_INT32_TYPE:
03399                     key->size = sizeof(int_32);
03400 /*@i@*/             key->data = rpmvals + i;
03401                     /*@switchbreak@*/ break;
03402 /*@=sizeoftype@*/
03403                 case RPM_BIN_TYPE:
03404                     key->size = rpmcnt;
03405 /*@i@*/             key->data = rpmvals;
03406                     rpmcnt = 1;         /* XXX break out of loop. */
03407                     /*@switchbreak@*/ break;
03408                 case RPM_STRING_TYPE:
03409                 case RPM_I18NSTRING_TYPE:
03410                     rpmcnt = 1;         /* XXX break out of loop. */
03411                     /*@fallthrough@*/
03412                 case RPM_STRING_ARRAY_TYPE:
03413                     /* Convert from hex to binary. */
03414 /*@-boundsread@*/
03415                     if (dbi->dbi_rpmtag == RPMTAG_FILEDIGESTS) {
03416                         const char * s = rpmvals[i];
03417                         size_t dlen = strlen(s);
03418                         byte * t;
03419 assert((dlen & 1) == 0);
03420                         dlen /= 2;
03421                         bin = t = xcalloc(1, dlen);
03422                         for (j = 0; j < dlen; j++, t++, s += 2)
03423                             *t = (nibble(s[0]) << 4) | nibble(s[1]);
03424                         key->data = bin;
03425                         key->size = dlen;
03426                         /*@switchbreak@*/ break;
03427                     }
03428                     /* Extract the pubkey id from the base64 blob. */
03429                     if (dbi->dbi_rpmtag == RPMTAG_PUBKEYS) {
03430                         int nbin;
03431                         bin = xcalloc(1, 32);
03432                         nbin = pgpExtractPubkeyFingerprint(rpmvals[i], bin);
03433                         if (nbin <= 0)
03434                             /*@innercontinue@*/ continue;
03435                         key->data = bin;
03436                         key->size = nbin;
03437                         /*@switchbreak@*/ break;
03438                     }
03439 /*@=boundsread@*/
03440                     /*@fallthrough@*/
03441                 default:
03442 /*@i@*/             key->data = (void *) rpmvals[i];
03443                     key->size = strlen(rpmvals[i]);
03444                     stringvalued = 1;
03445                     /*@switchbreak@*/ break;
03446                 }
03447 
03448                 if (!printed) {
03449                     if (rpmcnt == 1 && stringvalued) {
03450                         rpmMessage(RPMMESS_DEBUG,
03451                                 _("adding \"%s\" to %s index.\n"),
03452                                 (char *)key->data, tagName(dbi->dbi_rpmtag));
03453                     } else {
03454                         rpmMessage(RPMMESS_DEBUG,
03455                                 _("adding %d entries to %s index.\n"),
03456                                 rpmcnt, tagName(dbi->dbi_rpmtag));
03457                     }
03458                     printed++;
03459                 }
03460 
03461 /* XXX with duplicates, an accurate data value and DB_GET_BOTH is needed. */
03462 
03463                 set = NULL;
03464 
03465 if (key->size == 0) key->size = strlen((char *)key->data);
03466 if (key->size == 0) key->size++;        /* XXX "/" fixup. */
03467 
03468 /*@-compmempass@*/
03469                 rc = dbiGet(dbi, dbcursor, key, data, DB_SET);
03470                 if (rc == 0) {                  /* success */
03471                 /* With duplicates, cursor is positioned, discard the record. */
03472                     if (!dbi->dbi_permit_dups)
03473                         (void) dbt2set(dbi, data, &set);
03474                 } else if (rc != DB_NOTFOUND) { /* error */
03475                     rpmError(RPMERR_DBGETINDEX,
03476                         _("error(%d) getting \"%s\" records from %s index\n"),
03477                         rc, key->data, tagName(dbi->dbi_rpmtag));
03478                     ret += 1;
03479                     /*@innercontinue@*/ continue;
03480                 }
03481 /*@=compmempass@*/
03482 
03483                 if (set == NULL)                /* not found or duplicate */
03484                     set = xcalloc(1, sizeof(*set));
03485 
03486                 (void) dbiAppendSet(set, rec, 1, sizeof(*rec), 0);
03487 
03488 /*@-compmempass@*/
03489                 (void) set2dbt(dbi, data, set);
03490                 rc = dbiPut(dbi, dbcursor, key, data, DB_KEYLAST);
03491 /*@=compmempass@*/
03492 
03493                 if (rc) {
03494                     rpmError(RPMERR_DBPUTINDEX,
03495                                 _("error(%d) storing record %s into %s\n"),
03496                                 rc, key->data, tagName(dbi->dbi_rpmtag));
03497                     ret += 1;
03498                 }
03499 /*@-unqualifiedtrans@*/
03500                 data->data = _free(data->data);
03501 /*@=unqualifiedtrans@*/
03502                 data->size = 0;
03503                 set = dbiFreeIndexSet(set);
03504             }
03505 /*@=branchstate@*/
03506 
03507             xx = dbiCclose(dbi, dbcursor, DB_WRITECURSOR);
03508             dbcursor = NULL;
03509 
03510             if (!dbi->dbi_no_dbsync)
03511                 xx = dbiSync(dbi, 0);
03512           }
03513 
03514         /*@-observertrans@*/
03515             if (rpmtype != RPM_BIN_TYPE)        /* XXX WTFO? HACK ALERT */
03516                 rpmvals = hfd(rpmvals, rpmtype);
03517         /*@=observertrans@*/
03518             rpmtype = 0;
03519             rpmcnt = 0;
03520             bin = _free(bin);
03521         }
03522         /*@=nullpass =nullptrarith =nullderef @*/
03523 
03524         rec = _free(rec);
03525     }
03526 
03527 exit:
03528     (void) unblockSignals(db, &signalMask);
03529     dirIndexes = hfd(dirIndexes, dit);
03530     dirNames = hfd(dirNames, dnt);
03531 
03532     return ret;
03533 }
03534 
03535 /* XXX transaction.c */
03536 /*@-compmempass@*/
03537 int rpmdbFindFpList(rpmdb db, fingerPrint * fpList, dbiIndexSet * matchList, 
03538                     int numItems, unsigned int exclude)
03539 {
03540 DBT * key;
03541 DBT * data;
03542     HGE_t hge = (HGE_t)headerGetEntryMinMemory;
03543     HFD_t hfd = headerFreeData;
03544     rpmdbMatchIterator mi;
03545     fingerPrintCache fpc;
03546     Header h;
03547     int i, xx;
03548 
03549     if (db == NULL) return 0;
03550 
03551     mi = rpmdbInitIterator(db, RPMTAG_BASENAMES, NULL, 0);
03552 assert(mi);     /* XXX will never happen. */
03553     if (mi == NULL)
03554         return 2;
03555 
03556 key = &mi->mi_key;
03557 data = &mi->mi_data;
03558 
03559     /* Gather all installed headers with matching basename's. */
03560     for (i = 0; i < numItems; i++) {
03561            unsigned int tag;
03562 
03563 /*@-boundswrite@*/
03564         matchList[i] = xcalloc(1, sizeof(*(matchList[i])));
03565 /*@=boundswrite@*/
03566 
03567 /*@-boundsread -dependenttrans@*/
03568 key->data = (void *) fpList[i].baseName;
03569 /*@=boundsread =dependenttrans@*/
03570 key->size = strlen((char *)key->data);
03571 if (key->size == 0) key->size++;        /* XXX "/" fixup. */
03572 
03573         tag = (_db_tagged_file_indices ? taghash(fpList[i].entry->dirName) : 0);
03574         xx = rpmdbGrowIterator(mi, i, exclude, tag);
03575 
03576     }
03577 
03578     if ((i = rpmdbGetIteratorCount(mi)) == 0) {
03579         mi = rpmdbFreeIterator(mi);
03580         return 0;
03581     }
03582     fpc = fpCacheCreate(i);
03583 
03584     rpmdbSortIterator(mi);
03585     /* iterator is now sorted by (recnum, filenum) */
03586 
03587     /* For all installed headers with matching basename's ... */
03588     if (mi != NULL)
03589     while ((h = rpmdbNextIterator(mi)) != NULL) {
03590         const char ** dirNames;
03591         const char ** baseNames;
03592         const char ** fullBaseNames;
03593         rpmTagType bnt, dnt;
03594         uint_32 * dirIndexes;
03595         uint_32 * fullDirIndexes;
03596         fingerPrint * fps;
03597         dbiIndexItem im;
03598         int start;
03599         int num;
03600         int end;
03601 
03602         start = mi->mi_setx - 1;
03603         im = mi->mi_set->recs + start;
03604 
03605         /* Find the end of the set of matched basename's in this package. */
03606 /*@-boundsread@*/
03607         for (end = start + 1; end < mi->mi_set->count; end++) {
03608             if (im->hdrNum != mi->mi_set->recs[end].hdrNum)
03609                 /*@innerbreak@*/ break;
03610         }
03611 /*@=boundsread@*/
03612         num = end - start;
03613 
03614         /* Compute fingerprints for this installed header's matches */
03615         xx = hge(h, RPMTAG_BASENAMES, &bnt, (void **) &fullBaseNames, NULL);
03616         xx = hge(h, RPMTAG_DIRNAMES, &dnt, (void **) &dirNames, NULL);
03617         xx = hge(h, RPMTAG_DIRINDEXES, NULL, (void **) &fullDirIndexes, NULL);
03618 
03619         baseNames = xcalloc(num, sizeof(*baseNames));
03620         dirIndexes = xcalloc(num, sizeof(*dirIndexes));
03621 /*@-bounds@*/
03622         for (i = 0; i < num; i++) {
03623             baseNames[i] = fullBaseNames[im[i].tagNum];
03624             dirIndexes[i] = fullDirIndexes[im[i].tagNum];
03625         }
03626 /*@=bounds@*/
03627 
03628         fps = xcalloc(num, sizeof(*fps));
03629         fpLookupList(fpc, dirNames, baseNames, dirIndexes, num, fps);
03630 
03631         /* Add db (recnum,filenum) to list for fingerprint matches. */
03632 /*@-boundsread@*/
03633         for (i = 0; i < num; i++, im++) {
03634             /*@-nullpass@*/ /* FIX: fpList[].subDir may be NULL */
03635             if (!FP_EQUAL(fps[i], fpList[im->fpNum]))
03636                 /*@innercontinue@*/ continue;
03637             /*@=nullpass@*/
03638             xx = dbiAppendSet(matchList[im->fpNum], im, 1, sizeof(*im), 0);
03639         }
03640 /*@=boundsread@*/
03641 
03642         fps = _free(fps);
03643         dirNames = hfd(dirNames, dnt);
03644         fullBaseNames = hfd(fullBaseNames, bnt);
03645         baseNames = _free(baseNames);
03646         dirIndexes = _free(dirIndexes);
03647 
03648         mi->mi_setx = end;
03649     }
03650 
03651     mi = rpmdbFreeIterator(mi);
03652 
03653     fpc = fpCacheFree(fpc);
03654 
03655     return 0;
03656 
03657 }
03658 /*@=compmempass@*/
03659 
03665 static int rpmioFileExists(const char * urlfn)
03666         /*@globals h_errno, fileSystem, internalState @*/
03667         /*@modifies fileSystem, internalState @*/
03668 {
03669     const char *fn;
03670     int urltype = urlPath(urlfn, &fn);
03671     struct stat buf;
03672 
03673     /*@-branchstate@*/
03674     if (*fn == '\0') fn = "/";
03675     /*@=branchstate@*/
03676     switch (urltype) {
03677     case URL_IS_HTTPS:  /* XXX WRONG WRONG WRONG */
03678     case URL_IS_HTTP:   /* XXX WRONG WRONG WRONG */
03679     case URL_IS_FTP:    /* XXX WRONG WRONG WRONG */
03680     case URL_IS_HKP:    /* XXX WRONG WRONG WRONG */
03681     case URL_IS_PATH:
03682     case URL_IS_UNKNOWN:
03683         if (Stat(fn, &buf)) {
03684             switch(errno) {
03685             case ENOENT:
03686             case EINVAL:
03687                 return 0;
03688             }
03689         }
03690         break;
03691     case URL_IS_DASH:
03692     default:
03693         return 0;
03694         /*@notreached@*/ break;
03695     }
03696 
03697     return 1;
03698 }
03699 
03700 static int rpmdbRemoveDatabase(const char * prefix,
03701                 const char * dbpath, int _dbapi,
03702                 const int * dbiTags, int dbiTagsMax)
03703         /*@globals h_errno, fileSystem, internalState @*/
03704         /*@modifies fileSystem, internalState @*/
03705 { 
03706     int i;
03707     char * filename;
03708     int xx;
03709 
03710     i = strlen(dbpath);
03711     /*@-bounds -branchstate@*/
03712     if (dbpath[i - 1] != '/') {
03713         filename = alloca(i);
03714         strcpy(filename, dbpath);
03715         filename[i] = '/';
03716         filename[i + 1] = '\0';
03717         dbpath = filename;
03718     }
03719     /*@=bounds =branchstate@*/
03720     
03721     filename = alloca(strlen(prefix) + strlen(dbpath) + 40);
03722 
03723     switch (_dbapi) {
03724     case 4:
03725         /*@fallthrough@*/
03726     case 3:
03727         if (dbiTags != NULL)
03728         for (i = 0; i < dbiTagsMax; i++) {
03729 /*@-boundsread@*/
03730             const char * base = tagName(dbiTags[i]);
03731 /*@=boundsread@*/
03732             sprintf(filename, "%s/%s/%s", prefix, dbpath, base);
03733             (void)rpmCleanPath(filename);
03734             if (!rpmioFileExists(filename))
03735                 continue;
03736             xx = unlink(filename);
03737         }
03738         for (i = 0; i < 16; i++) {
03739             sprintf(filename, "%s/%s/__db.%03d", prefix, dbpath, i);
03740             (void)rpmCleanPath(filename);
03741             if (!rpmioFileExists(filename))
03742                 continue;
03743             xx = unlink(filename);
03744         }
03745         break;
03746     case 2:
03747     case 1:
03748     case 0:
03749         break;
03750     }
03751 
03752     sprintf(filename, "%s/%s", prefix, dbpath);
03753     (void)rpmCleanPath(filename);
03754     xx = rmdir(filename);
03755 
03756     return 0;
03757 }
03758 
03759 static int rpmdbMoveDatabase(const char * prefix,
03760                 const char * olddbpath, int _olddbapi,
03761                 const char * newdbpath, /*@unused@*/ int _newdbapi,
03762                 const int * dbiTags, int dbiTagsMax)
03763         /*@globals h_errno, fileSystem, internalState @*/
03764         /*@modifies fileSystem, internalState @*/
03765 {
03766     int i;
03767     char * ofilename, * nfilename;
03768     struct stat * nst = alloca(sizeof(*nst));
03769     int rc = 0;
03770     int xx;
03771  
03772     i = strlen(olddbpath);
03773     /*@-branchstate@*/
03774     if (olddbpath[i - 1] != '/') {
03775         ofilename = alloca(i + 2);
03776         strcpy(ofilename, olddbpath);
03777         ofilename[i] = '/';
03778         ofilename[i + 1] = '\0';
03779         olddbpath = ofilename;
03780     }
03781     /*@=branchstate@*/
03782     
03783     i = strlen(newdbpath);
03784     /*@-branchstate@*/
03785     if (newdbpath[i - 1] != '/') {
03786         nfilename = alloca(i + 2);
03787         strcpy(nfilename, newdbpath);
03788         nfilename[i] = '/';
03789         nfilename[i + 1] = '\0';
03790         newdbpath = nfilename;
03791     }
03792     /*@=branchstate@*/
03793     
03794     ofilename = alloca(strlen(prefix) + strlen(olddbpath) + 40);
03795     nfilename = alloca(strlen(prefix) + strlen(newdbpath) + 40);
03796 
03797     switch (_olddbapi) {
03798     case 4:
03799         /* Fall through */
03800     case 3:
03801         if (dbiTags != NULL)
03802         for (i = 0; i < dbiTagsMax; i++) {
03803             const char * base;
03804             int rpmtag;
03805 
03806             /* Filter out temporary databases */
03807             switch ((rpmtag = dbiTags[i])) {
03808             case RPMDBI_AVAILABLE:
03809             case RPMDBI_ADDED:
03810             case RPMDBI_REMOVED:
03811             case RPMDBI_DEPENDS:
03812                 continue;
03813                 /*@notreached@*/ /*@switchbreak@*/ break;
03814             default:
03815                 /*@switchbreak@*/ break;
03816             }
03817 
03818             base = tagName(rpmtag);
03819             sprintf(ofilename, "%s/%s/%s", prefix, olddbpath, base);
03820             (void)rpmCleanPath(ofilename);
03821             if (!rpmioFileExists(ofilename))
03822                 continue;
03823             sprintf(nfilename, "%s/%s/%s", prefix, newdbpath, base);
03824             (void)rpmCleanPath(nfilename);
03825 
03826             /*
03827              * Get uid/gid/mode/mtime. If old doesn't exist, use new.
03828              * XXX Yes, the variable names are backwards.
03829              */
03830             if (stat(nfilename, nst) < 0)
03831                 if (stat(ofilename, nst) < 0)
03832                     continue;
03833 
03834             if ((xx = rename(ofilename, nfilename)) != 0) {
03835                 rc = 1;
03836                 continue;
03837             }
03838             xx = chown(nfilename, nst->st_uid, nst->st_gid);
03839             xx = chmod(nfilename, (nst->st_mode & 07777));
03840             {   struct utimbuf stamp;
03841                 stamp.actime = nst->st_atime;
03842                 stamp.modtime = nst->st_mtime;
03843                 xx = utime(nfilename, &stamp);
03844             }
03845         }
03846         for (i = 0; i < 16; i++) {
03847             sprintf(ofilename, "%s/%s/__db.%03d", prefix, olddbpath, i);
03848             (void)rpmCleanPath(ofilename);
03849             if (rpmioFileExists(ofilename))
03850                 xx = unlink(ofilename);
03851             sprintf(nfilename, "%s/%s/__db.%03d", prefix, newdbpath, i);
03852             (void)rpmCleanPath(nfilename);
03853             if (rpmioFileExists(nfilename))
03854                 xx = unlink(nfilename);
03855         }
03856         break;
03857     case 2:
03858     case 1:
03859     case 0:
03860         break;
03861     }
03862 #ifdef  SQLITE_HACK_XXX
03863     if (rc || _olddbapi == _newdbapi)
03864         return rc;
03865 
03866     rc = rpmdbRemoveDatabase(prefix, newdbpath, _newdbapi, dbiTags, dbiTagsMax);
03867 
03868 
03869     /* Remove /etc/rpm/macros.db1 configuration file if db3 rebuilt. */
03870     if (rc == 0 && _newdbapi == 1 && _olddbapi == 3) {
03871         const char * mdb1 = "/etc/rpm/macros.db1";
03872         struct stat st;
03873         if (!stat(mdb1, &st) && S_ISREG(st.st_mode) && !unlink(mdb1))
03874             rpmMessage(RPMMESS_DEBUG,
03875                 _("removing %s after successful db3 rebuild.\n"), mdb1);
03876     }
03877 #endif
03878     return rc;
03879 }
03880 
03881 int rpmdbRebuild(const char * prefix, rpmts ts,
03882                 rpmRC (*hdrchk) (rpmts ts, const void *uh, size_t uc, const char ** msg))
03883         /*@globals _rebuildinprogress @*/
03884         /*@modifies _rebuildinprogress @*/
03885 {
03886     rpmdb olddb;
03887     const char * dbpath = NULL;
03888     const char * rootdbpath = NULL;
03889     rpmdb newdb;
03890     const char * newdbpath = NULL;
03891     const char * newrootdbpath = NULL;
03892     const char * tfn;
03893     int nocleanup = 1;
03894     int failed = 0;
03895     int removedir = 0;
03896     int rc = 0, xx;
03897     int _dbapi;
03898     int _dbapi_rebuild;
03899     int * dbiTags = NULL;
03900     int dbiTagsMax = 0;
03901 
03902     _dbapi = rpmExpandNumeric("%{_dbapi}");
03903     _dbapi_rebuild = rpmExpandNumeric("%{_dbapi_rebuild}");
03904 
03905     dbiTagsInit(&dbiTags, &dbiTagsMax);
03906 
03907     /*@-nullpass@*/
03908     tfn = rpmGetPath("%{?_dbpath}", NULL);
03909     /*@=nullpass@*/
03910 /*@-boundsread@*/
03911     if (!(tfn && tfn[0] != '\0'))
03912 /*@=boundsread@*/
03913     {
03914         rpmMessage(RPMMESS_DEBUG, _("no dbpath has been set"));
03915         rc = 1;
03916         goto exit;
03917     }
03918     /* Add --root prefix iff --dbpath is not a URL. */
03919     switch (urlPath(tfn, NULL)) {
03920         default:
03921             prefix = xstrdup("");
03922             break;
03923         case URL_IS_UNKNOWN:
03924             prefix = rpmGetPath((prefix ? prefix : "/"), NULL);
03925             break;
03926     }
03927 
03928     dbpath = rootdbpath = rpmGetPath(prefix, tfn, NULL);
03929     if (!(prefix[0] == '/' && prefix[1] == '\0'))
03930         dbpath += strlen(prefix);
03931     tfn = _free(tfn);
03932 
03933     /*@-nullpass@*/
03934     tfn = rpmGetPath("%{?_dbpath_rebuild}", NULL);
03935     /*@=nullpass@*/
03936 /*@-boundsread@*/
03937     if (!(tfn && tfn[0] != '\0' && strcmp(tfn, dbpath)))
03938 /*@=boundsread@*/
03939     {
03940         char pidbuf[20];
03941         char *t;
03942         sprintf(pidbuf, "rebuilddb.%d", (int) getpid());
03943         t = xmalloc(strlen(dbpath) + strlen(pidbuf) + 1);
03944 /*@-boundswrite@*/
03945         (void)stpcpy(stpcpy(t, dbpath), pidbuf);
03946 /*@=boundswrite@*/
03947         tfn = _free(tfn);
03948         tfn = t;
03949         nocleanup = 0;
03950     }
03951     newdbpath = newrootdbpath = rpmGetPath(prefix, tfn, NULL);
03952     if (!(prefix[0] == '/' && prefix[1] == '\0'))
03953         newdbpath += strlen(prefix);
03954     tfn = _free(tfn);
03955 
03956     rpmMessage(RPMMESS_DEBUG, _("rebuilding database %s into %s\n"),
03957         rootdbpath, newrootdbpath);
03958 
03959     if (!Access(newrootdbpath, F_OK)) {
03960         rpmError(RPMERR_MKDIR, _("temporary database %s already exists\n"),
03961               newrootdbpath);
03962         rc = 1;
03963         goto exit;
03964     }
03965 
03966     rpmMessage(RPMMESS_DEBUG, _("creating directory %s\n"), newrootdbpath);
03967     if (Mkdir(newrootdbpath, 0755)) {
03968         rpmError(RPMERR_MKDIR, _("creating directory %s: %s\n"),
03969               newrootdbpath, strerror(errno));
03970         rc = 1;
03971         goto exit;
03972     }
03973     removedir = 1;
03974 
03975     _rebuildinprogress = 0;
03976 
03977     rpmMessage(RPMMESS_DEBUG, _("opening old database with dbapi %d\n"),
03978                 _dbapi);
03979 /*@-boundswrite@*/
03980     if (rpmdbOpenDatabase(prefix, dbpath, _dbapi, &olddb, O_RDONLY, 0644, 
03981                      RPMDB_FLAG_MINIMAL)) {
03982         rc = 1;
03983         goto exit;
03984     }
03985 /*@=boundswrite@*/
03986     _dbapi = olddb->db_api;
03987     _rebuildinprogress = 1;
03988     rpmMessage(RPMMESS_DEBUG, _("opening new database with dbapi %d\n"),
03989                 _dbapi_rebuild);
03990     (void) rpmDefineMacro(NULL, "_rpmdb_rebuild %{nil}", -1);
03991 /*@-boundswrite@*/
03992     if (rpmdbOpenDatabase(prefix, newdbpath, _dbapi_rebuild, &newdb, O_RDWR | O_CREAT, 0644, 0)) {
03993         rc = 1;
03994         goto exit;
03995     }
03996 /*@=boundswrite@*/
03997 
03998     _rebuildinprogress = 0;
03999 
04000     _dbapi_rebuild = newdb->db_api;
04001     
04002     {   Header h = NULL;
04003         rpmdbMatchIterator mi;
04004 #define _RECNUM rpmdbGetIteratorOffset(mi)
04005 
04006         mi = rpmdbInitIterator(olddb, RPMDBI_PACKAGES, NULL, 0);
04007         if (ts && hdrchk)
04008             (void) rpmdbSetHdrChk(mi, ts, hdrchk);
04009 
04010         while ((h = rpmdbNextIterator(mi)) != NULL) {
04011 
04012             /* let's sanity check this record a bit, otherwise just skip it */
04013             if (!(headerIsEntry(h, RPMTAG_NAME) &&
04014                 headerIsEntry(h, RPMTAG_VERSION) &&
04015                 headerIsEntry(h, RPMTAG_RELEASE) &&
04016                 headerIsEntry(h, RPMTAG_BUILDTIME)))
04017             {
04018                 rpmError(RPMERR_INTERNAL,
04019                         _("header #%u in the database is bad -- skipping.\n"),
04020                         _RECNUM);
04021                 continue;
04022             }
04023 
04024             /* Filter duplicate entries ? (bug in pre rpm-3.0.4) */
04025             if (_db_filter_dups || newdb->db_filter_dups) {
04026                 const char * name, * version, * release;
04027                 int skip = 0;
04028 
04029                 (void) headerNVR(h, &name, &version, &release);
04030 
04031                 /*@-shadow@*/
04032                 {   rpmdbMatchIterator mi;
04033                     mi = rpmdbInitIterator(newdb, RPMTAG_NAME, name, 0);
04034                     (void) rpmdbSetIteratorRE(mi, RPMTAG_VERSION,
04035                                 RPMMIRE_DEFAULT, version);
04036                     (void) rpmdbSetIteratorRE(mi, RPMTAG_RELEASE,
04037                                 RPMMIRE_DEFAULT, release);
04038                     while (rpmdbNextIterator(mi)) {
04039                         skip = 1;
04040                         /*@innerbreak@*/ break;
04041                     }
04042                     mi = rpmdbFreeIterator(mi);
04043                 }
04044                 /*@=shadow@*/
04045 
04046                 if (skip)
04047                     continue;
04048             }
04049 
04050             /* Deleted entries are eliminated in legacy headers by copy. */
04051             {   Header nh = (headerIsEntry(h, RPMTAG_HEADERIMAGE)
04052                                 ? headerCopy(h) : NULL);
04053                 rc = rpmdbAdd(newdb, -1, (nh ? nh : h), ts, hdrchk);
04054                 nh = headerFree(nh);
04055             }
04056 
04057             if (rc) {
04058                 rpmError(RPMERR_INTERNAL,
04059                         _("cannot add record originally at %u\n"), _RECNUM);
04060                 failed = 1;
04061                 break;
04062             }
04063         }
04064 
04065         mi = rpmdbFreeIterator(mi);
04066 
04067     }
04068 
04069     xx = rpmdbClose(olddb);
04070     xx = rpmdbClose(newdb);
04071 
04072     if (failed) {
04073         rpmMessage(RPMMESS_NORMAL, _("failed to rebuild database: original database "
04074                 "remains in place\n"));
04075 
04076         xx = rpmdbRemoveDatabase(prefix, newdbpath, _dbapi_rebuild,
04077                         dbiTags, dbiTagsMax);
04078         rc = 1;
04079         goto exit;
04080     } else if (!nocleanup) {
04081         xx = rpmdbMoveDatabase(prefix, newdbpath, _dbapi_rebuild, dbpath, _dbapi,
04082                         dbiTags, dbiTagsMax);
04083 
04084         if (xx) {
04085             rpmMessage(RPMMESS_ERROR, _("failed to replace old database with new "
04086                         "database!\n"));
04087             rpmMessage(RPMMESS_ERROR, _("replace files in %s with files from %s "
04088                         "to recover"), dbpath, newdbpath);
04089             rc = 1;
04090             goto exit;
04091         }
04092     }
04093     rc = 0;
04094 
04095 exit:
04096     if (removedir && !(rc == 0 && nocleanup)) {
04097         rpmMessage(RPMMESS_DEBUG, _("removing directory %s\n"), newrootdbpath);
04098         if (Rmdir(newrootdbpath))
04099             rpmMessage(RPMMESS_ERROR, _("failed to remove directory %s: %s\n"),
04100                         newrootdbpath, strerror(errno));
04101     }
04102     newrootdbpath = _free(newrootdbpath);
04103     rootdbpath = _free(rootdbpath);
04104     dbiTags = _free(dbiTags);
04105     prefix = _free(prefix);
04106 
04107     return rc;
04108 }

Generated on Mon Aug 3 15:21:48 2009 for rpm by  doxygen 1.5.1