Main Page   Modules   Data Structures   File List   Data Fields   Globals   Related Pages  

rpmdb/rpmdb.c

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

Generated on Tue Sep 17 15:56:44 2002 for rpm by doxygen1.2.14 written by Dimitri van Heesch, © 1997-2002