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

Generated on Thu Nov 3 20:15:03 2005 for rpm by doxygen 1.3.5