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