Main Page   Modules   Compound List   File List   Compound Members   File Members   Related Pages  

lib/rpmdb.c

Go to the documentation of this file.
00001 
00005 #include "system.h"
00006 
00007 static int _debug = 0;
00008 #define INLINE
00009 
00010 #include <sys/file.h>
00011 #include <signal.h>
00012 #include <sys/signal.h>
00013 
00014 #include <rpmlib.h>
00015 #include <rpmmacro.h>   /* XXX for rpmGetPath/rpmGenPath */
00016 
00017 #include "rpmdb.h"
00018 #include "fprint.h"
00019 #include "misc.h"
00020 #include "debug.h"
00021 
00022 /*@access dbiIndexSet@*/
00023 /*@access dbiIndexItem@*/
00024 /*@access Header@*/             /* XXX compared with NULL */
00025 /*@access rpmdbMatchIterator@*/
00026 
00027 extern int _noDirTokens;
00028 static int _rebuildinprogress = 0;
00029 static int _db_filter_dups = 0;
00030 
00031 int _filterDbDups = 0;  /* Filter duplicate entries ? (bug in pre rpm-3.0.4) */
00032 
00033 #define _DBI_FLAGS      0
00034 #define _DBI_PERMS      0644
00035 #define _DBI_MAJOR      -1
00036 
00037 static int dbiTagsMax = 0;
00038 /*@only@*/ static int *dbiTags = NULL;
00039 
00045 static int dbiTagToDbix(int rpmtag)
00046 {
00047     int dbix;
00048 
00049     if (!(dbiTagsMax > 0 && dbiTags))
00050         return -1;
00051     for (dbix = 0; dbix < dbiTagsMax; dbix++) {
00052         if (rpmtag == dbiTags[dbix])
00053             return dbix;
00054     }
00055     return -1;
00056 }
00057 
00061 static void dbiTagsInit(void)
00062 {
00063     static const char * _dbiTagStr_default =
00064         "Packages:Name:Basenames:Group:Requirename:Providename:Conflictname:Triggername";
00065     char * dbiTagStr;
00066     char * o, * oe;
00067     int rpmtag;
00068 
00069     dbiTagStr = rpmExpand("%{_dbi_tags}", NULL);
00070     if (!(dbiTagStr && *dbiTagStr && *dbiTagStr != '%')) {
00071         free((void *)dbiTagStr);
00072         dbiTagStr = xstrdup(_dbiTagStr_default);
00073     }
00074 
00075     if (dbiTagsMax || dbiTags) {
00076         free(dbiTags);
00077         dbiTags = NULL;
00078         dbiTagsMax = 0;
00079     }
00080 
00081     /* Always allocate package index */
00082     dbiTagsMax = 1;
00083     dbiTags = xcalloc(1, dbiTagsMax * sizeof(*dbiTags));
00084 
00085     for (o = dbiTagStr; o && *o; o = oe) {
00086         while (*o && isspace(*o))
00087             o++;
00088         if (*o == '\0')
00089             break;
00090         for (oe = o; oe && *oe; oe++) {
00091             if (isspace(*oe))
00092                 break;
00093             if (oe[0] == ':' && !(oe[1] == '/' && oe[2] == '/'))
00094                 break;
00095         }
00096         if (oe && *oe)
00097             *oe++ = '\0';
00098         rpmtag = tagValue(o);
00099         if (rpmtag < 0) {
00100 
00101             fprintf(stderr, _("dbiTagsInit: unrecognized tag name: \"%s\" ignored\n"), o);
00102             continue;
00103         }
00104         if (dbiTagToDbix(rpmtag) >= 0)
00105             continue;
00106 
00107         dbiTags = xrealloc(dbiTags, (dbiTagsMax + 1) * sizeof(*dbiTags)); /* XXX memory leak */
00108         dbiTags[dbiTagsMax++] = rpmtag;
00109     }
00110 
00111     free(dbiTagStr);
00112 }
00113 
00114 #if USE_DB1
00115 extern struct _dbiVec db1vec;
00116 #define DB1vec          &db1vec
00117 #else
00118 #define DB1vec          NULL
00119 #endif
00120 
00121 #if USE_DB2
00122 extern struct _dbiVec db2vec;
00123 #define DB2vec          &db2vec
00124 #else
00125 #define DB2vec          NULL
00126 #endif
00127 
00128 #if USE_DB3
00129 extern struct _dbiVec db3vec;
00130 #define DB3vec          &db3vec
00131 #else
00132 #define DB3vec          NULL
00133 #endif
00134 
00135 static struct _dbiVec *mydbvecs[] = {
00136     DB1vec, DB1vec, DB2vec, DB3vec, NULL
00137 };
00138 
00139 INLINE int dbiSync(dbiIndex dbi, unsigned int flags) {
00140 if (_debug < 0 || dbi->dbi_debug)
00141 fprintf(stderr, "    Sync %s\n", tagName(dbi->dbi_rpmtag));
00142     return (*dbi->dbi_vec->sync) (dbi, flags);
00143 }
00144 
00145 INLINE int dbiByteSwapped(dbiIndex dbi) {
00146     return (*dbi->dbi_vec->byteswapped) (dbi);
00147 }
00148 
00149 INLINE int XdbiCopen(dbiIndex dbi, /*@out@*/ DBC ** dbcp, unsigned int flags,
00150         const char * f, unsigned int l)
00151 {
00152 if (_debug < 0 || dbi->dbi_debug)
00153 fprintf(stderr, "+++ RMW %s (%s:%u)\n", tagName(dbi->dbi_rpmtag), f, l);
00154     return (*dbi->dbi_vec->copen) (dbi, dbcp, flags);
00155 }
00156 
00157 INLINE int XdbiCclose(dbiIndex dbi, /*@only@*/ DBC * dbcursor, unsigned int flags,
00158         const char * f, unsigned int l)
00159 {
00160 if (_debug < 0 || dbi->dbi_debug)
00161 fprintf(stderr, "--- RMW %s (%s:%u)\n", tagName(dbi->dbi_rpmtag), f, l);
00162     return (*dbi->dbi_vec->cclose) (dbi, dbcursor, flags);
00163 }
00164 
00165 INLINE int dbiDel(dbiIndex dbi, DBC * dbcursor, const void * keyp, size_t keylen, unsigned int flags)
00166 {
00167     int NULkey;
00168     int rc;
00169 
00170     /* XXX make sure that keylen is correct for "" lookup */
00171     NULkey = (keyp && *((char *)keyp) == '\0' && keylen == 0);
00172     if (NULkey) keylen++;
00173     rc = (*dbi->dbi_vec->cdel) (dbi, dbcursor, keyp, keylen, flags);
00174     if (NULkey) keylen--;
00175 
00176 if (_debug < 0 || dbi->dbi_debug)
00177 fprintf(stderr, "    Del %s key (%p,%ld) %s rc %d\n", tagName(dbi->dbi_rpmtag), keyp, (long)keylen, (dbi->dbi_rpmtag != RPMDBI_PACKAGES ? (char *)keyp : ""), rc);
00178 
00179     return rc;
00180 }
00181 
00182 INLINE int dbiGet(dbiIndex dbi, DBC * dbcursor, void ** keypp, size_t * keylenp,
00183         void ** datapp, size_t * datalenp, unsigned int flags)
00184 {
00185     int NULkey;
00186     int rc;
00187 
00188     /* XXX make sure that keylen is correct for "" lookup */
00189     NULkey = (keypp && *keypp && *((char *)(*keypp)) == '\0' && keylenp && *keylenp == 0);
00190     if (NULkey) (*keylenp)++;
00191     rc = (*dbi->dbi_vec->cget) (dbi, dbcursor, keypp, keylenp, datapp, datalenp, flags);
00192     if (NULkey) (*keylenp)--;
00193 
00194 if (_debug < 0 || dbi->dbi_debug) {
00195 char keyval[32];
00196 int dataval = 0xdeadbeef;
00197 if (dbi->dbi_rpmtag == RPMDBI_PACKAGES && keypp && *keypp && keylenp && *keylenp >= sizeof(keyval)) {
00198     int keyint;
00199     memcpy(&keyint, *keypp, sizeof(keyint));
00200     sprintf(keyval, "%d", keyint);
00201 } else keyval[0] = '\0';
00202 if (rc == 0 && datapp && *datapp && datalenp && *datalenp >= sizeof(dataval))
00203     memcpy(&dataval, *datapp, sizeof(dataval));
00204 fprintf(stderr, "    Get %s key (%p,%ld) data (%p,%ld) \"%s\" %x rc %d\n",
00205     tagName(dbi->dbi_rpmtag), *keypp, (long)*keylenp, *datapp, (long)*datalenp,
00206     (dbi->dbi_rpmtag != RPMDBI_PACKAGES ? (char *)*keypp : keyval), dataval, rc);
00207 }
00208     return rc;
00209 }
00210 
00211 INLINE int dbiPut(dbiIndex dbi, DBC * dbcursor, const void * keyp, size_t keylen,
00212         const void * datap, size_t datalen, unsigned int flags)
00213 {
00214     int NULkey;
00215     int rc;
00216 
00217     /* XXX make sure that keylen is correct for "" lookup */
00218     NULkey = (keyp && *((char *)keyp) == '\0' && keylen == 0);
00219     if (NULkey) keylen++;
00220     rc = (*dbi->dbi_vec->cput) (dbi, dbcursor, keyp, keylen, datap, datalen, flags);
00221     if (NULkey) keylen--;
00222 
00223 if (_debug < 0 || dbi->dbi_debug) {
00224 int dataval = 0xdeadbeef;
00225 if (datap) memcpy(&dataval, datap, sizeof(dataval));
00226 fprintf(stderr, "    Put %s key (%p,%ld) data (%p,%ld) \"%s\" %x rc %d\n", tagName(dbi->dbi_rpmtag), keyp, (long)keylen, datap, (long)datalen, (dbi->dbi_rpmtag != RPMDBI_PACKAGES ? (char *)keyp : ""), dataval, rc);
00227 }
00228 
00229     return rc;
00230 }
00231 
00232 INLINE int dbiClose(dbiIndex dbi, unsigned int flags) {
00233 if (_debug < 0 || dbi->dbi_debug)
00234 fprintf(stderr, "    %s Close\n", tagName(dbi->dbi_rpmtag));
00235     return (*dbi->dbi_vec->close) (dbi, flags);
00236 }
00237 
00238 dbiIndex dbiOpen(rpmdb rpmdb, int rpmtag, /*@unused@*/ unsigned int flags)
00239 {
00240     int dbix;
00241     dbiIndex dbi = NULL;
00242     int _dbapi, _dbapi_rebuild, _dbapi_wanted;
00243     int rc = 0;
00244 
00245     dbix = dbiTagToDbix(rpmtag);
00246     if (dbix < 0 || dbix >= dbiTagsMax)
00247         return NULL;
00248 
00249     /* Is this index already open ? */
00250     if ((dbi = rpmdb->_dbi[dbix]) != NULL)
00251         return dbi;
00252 
00253     _dbapi_rebuild = rpmExpandNumeric("%{_dbapi_rebuild}");
00254     if (_dbapi_rebuild < 1 || _dbapi_rebuild > 3)
00255         _dbapi_rebuild = 3;
00256     _dbapi_wanted = (_rebuildinprogress ? -1 : rpmdb->db_api);
00257 
00258     switch (_dbapi_wanted) {
00259     default:
00260         _dbapi = _dbapi_wanted;
00261         if (_dbapi < 0 || _dbapi >= 4 || mydbvecs[_dbapi] == NULL) {
00262             return NULL;
00263         }
00264         errno = 0;
00265         dbi = NULL;
00266         rc = (*mydbvecs[_dbapi]->open) (rpmdb, rpmtag, &dbi);
00267         if (rc) {
00268             static int _printed[32];
00269             if (!_printed[dbix & 0x1f]++)
00270                 rpmError(RPMERR_DBOPEN,
00271                         _("cannot open %s index using db%d - %s (%d)\n"),
00272                         tagName(rpmtag), _dbapi,
00273                         (rc > 0 ? strerror(rc) : ""), rc);
00274             _dbapi = -1;
00275         }
00276         break;
00277     case -1:
00278         _dbapi = 4;
00279         while (_dbapi-- > 1) {
00280             if (mydbvecs[_dbapi] == NULL)
00281                 continue;
00282             errno = 0;
00283             dbi = NULL;
00284             rc = (*mydbvecs[_dbapi]->open) (rpmdb, rpmtag, &dbi);
00285             if (rc == 0 && dbi)
00286                 break;
00287         }
00288         if (_dbapi <= 0) {
00289             static int _printed[32];
00290             if (!_printed[dbix & 0x1f]++)
00291                 rpmError(RPMERR_DBOPEN, _("cannot open %s index\n"),
00292                         tagName(rpmtag));
00293             rc = 1;
00294             goto exit;
00295         }
00296         if (rpmdb->db_api == -1 && _dbapi > 0)
00297             rpmdb->db_api = _dbapi;
00298         break;
00299     }
00300 
00301     /* Require conversion. */
00302     if (rc && _dbapi_wanted >= 0 && _dbapi != _dbapi_wanted && _dbapi_wanted == _dbapi_rebuild) {
00303         rc = (_rebuildinprogress ? 0 : 1);
00304         goto exit;
00305     }
00306 
00307     /* Suggest possible configuration */
00308     if (_dbapi_wanted >= 0 && _dbapi != _dbapi_wanted) {
00309         rc = 1;
00310         goto exit;
00311     }
00312 
00313     /* Suggest possible configuration */
00314     if (_dbapi_wanted < 0 && _dbapi != _dbapi_rebuild) {
00315         rc = (_rebuildinprogress ? 0 : 1);
00316         goto exit;
00317     }
00318 
00319 exit:
00320     if (rc == 0 && dbi) {
00321         rpmdb->_dbi[dbix] = dbi;
00322     } else if (dbi) {
00323         db3Free(dbi);
00324         dbi = NULL;
00325     }
00326 
00327     return dbi;
00328 }
00329 
00336 static INLINE dbiIndexItem dbiIndexNewItem(unsigned int hdrNum, unsigned int tagNum) {
00337     dbiIndexItem rec = xcalloc(1, sizeof(*rec));
00338     rec->hdrNum = hdrNum;
00339     rec->tagNum = tagNum;
00340     return rec;
00341 }
00342 
00343 union _dbswap {
00344     unsigned int ui;
00345     unsigned char uc[4];
00346 };
00347 
00348 #define _DBSWAP(_a) \
00349   { unsigned char _b, *_c = (_a).uc; \
00350     _b = _c[3]; _c[3] = _c[0]; _c[0] = _b; \
00351     _b = _c[2]; _c[2] = _c[1]; _c[1] = _b; \
00352   }
00353 
00362 static int dbiSearch(dbiIndex dbi, DBC * dbcursor, const char * keyp, size_t keylen,
00363                 dbiIndexSet * setp)
00364 {
00365     void * datap;
00366     size_t datalen;
00367     int rc;
00368 
00369     if (setp) *setp = NULL;
00370     if (keylen == 0) keylen = strlen(keyp);
00371 
00372     rc = dbiGet(dbi, dbcursor, (void **)&keyp, &keylen, &datap, &datalen, 0);
00373 
00374     if (rc > 0) {
00375         rpmError(RPMERR_DBGETINDEX,
00376                 _("error(%d) getting \"%s\" records from %s index\n"),
00377                 rc, keyp, tagName(dbi->dbi_rpmtag));
00378     } else
00379     if (rc == 0 && setp) {
00380         int _dbbyteswapped = dbiByteSwapped(dbi);
00381         const char * sdbir = datap;
00382         dbiIndexSet set;
00383         int i;
00384 
00385         set = xmalloc(sizeof(*set));
00386 
00387         /* Convert to database internal format */
00388         switch (dbi->dbi_jlen) {
00389         default:
00390         case 2*sizeof(int_32):
00391             set->count = datalen / (2*sizeof(int_32));
00392             set->recs = xmalloc(set->count * sizeof(*(set->recs)));
00393             for (i = 0; i < set->count; i++) {
00394                 union _dbswap hdrNum, tagNum;
00395 
00396                 memcpy(&hdrNum.ui, sdbir, sizeof(hdrNum.ui));
00397                 sdbir += sizeof(hdrNum.ui);
00398                 memcpy(&tagNum.ui, sdbir, sizeof(tagNum.ui));
00399                 sdbir += sizeof(tagNum.ui);
00400                 if (_dbbyteswapped) {
00401                     _DBSWAP(hdrNum);
00402                     _DBSWAP(tagNum);
00403                 }
00404                 set->recs[i].hdrNum = hdrNum.ui;
00405                 set->recs[i].tagNum = tagNum.ui;
00406                 set->recs[i].fpNum = 0;
00407                 set->recs[i].dbNum = 0;
00408             }
00409             break;
00410         case 1*sizeof(int_32):
00411             set->count = datalen / (1*sizeof(int_32));
00412             set->recs = xmalloc(set->count * sizeof(*(set->recs)));
00413             for (i = 0; i < set->count; i++) {
00414                 union _dbswap hdrNum;
00415 
00416                 memcpy(&hdrNum.ui, sdbir, sizeof(hdrNum.ui));
00417                 sdbir += sizeof(hdrNum.ui);
00418                 if (_dbbyteswapped) {
00419                     _DBSWAP(hdrNum);
00420                 }
00421                 set->recs[i].hdrNum = hdrNum.ui;
00422                 set->recs[i].tagNum = 0;
00423                 set->recs[i].fpNum = 0;
00424                 set->recs[i].dbNum = 0;
00425             }
00426             break;
00427         }
00428         *setp = set;
00429     }
00430     return rc;
00431 }
00432 
00440 /*@-compmempass@*/
00441 static int dbiUpdateIndex(dbiIndex dbi, DBC * dbcursor, const char * keyp, dbiIndexSet set)
00442 {
00443     size_t keylen = strlen(keyp);
00444     void * datap;
00445     size_t datalen;
00446     int rc;
00447 
00448     if (set->count) {
00449         char * tdbir;
00450         int i;
00451         int _dbbyteswapped = dbiByteSwapped(dbi);
00452 
00453         /* Convert to database internal format */
00454 
00455         switch (dbi->dbi_jlen) {
00456         default:
00457         case 2*sizeof(int_32):
00458             datalen = set->count * (2 * sizeof(int_32));
00459             datap = tdbir = alloca(datalen);
00460             for (i = 0; i < set->count; i++) {
00461                 union _dbswap hdrNum, tagNum;
00462 
00463                 hdrNum.ui = set->recs[i].hdrNum;
00464                 tagNum.ui = set->recs[i].tagNum;
00465                 if (_dbbyteswapped) {
00466                     _DBSWAP(hdrNum);
00467                     _DBSWAP(tagNum);
00468                 }
00469                 memcpy(tdbir, &hdrNum.ui, sizeof(hdrNum.ui));
00470                 tdbir += sizeof(hdrNum.ui);
00471                 memcpy(tdbir, &tagNum.ui, sizeof(tagNum.ui));
00472                 tdbir += sizeof(tagNum.ui);
00473             }
00474             break;
00475         case 1*sizeof(int_32):
00476             datalen = set->count * (1 * sizeof(int_32));
00477             datap = tdbir = alloca(datalen);
00478             for (i = 0; i < set->count; i++) {
00479                 union _dbswap hdrNum;
00480 
00481                 hdrNum.ui = set->recs[i].hdrNum;
00482                 if (_dbbyteswapped) {
00483                     _DBSWAP(hdrNum);
00484                 }
00485                 memcpy(tdbir, &hdrNum.ui, sizeof(hdrNum.ui));
00486                 tdbir += sizeof(hdrNum.ui);
00487             }
00488             break;
00489         }
00490 
00491         rc = dbiPut(dbi, dbcursor, keyp, keylen, datap, datalen, 0);
00492 
00493         if (rc) {
00494             rpmError(RPMERR_DBPUTINDEX,
00495                 _("error(%d) storing record %s into %s\n"),
00496                 rc, keyp, tagName(dbi->dbi_rpmtag));
00497         }
00498 
00499     } else {
00500 
00501         rc = dbiDel(dbi, dbcursor, keyp, keylen, 0);
00502 
00503         if (rc) {
00504             rpmError(RPMERR_DBPUTINDEX,
00505                 _("error(%d) removing record %s from %s\n"),
00506                 rc, keyp, tagName(dbi->dbi_rpmtag));
00507         }
00508 
00509     }
00510 
00511     return rc;
00512 }
00513 /*@=compmempass@*/
00514 
00515 /* XXX assumes hdrNum is first int in dbiIndexItem */
00516 static int hdrNumCmp(const void * one, const void * two) {
00517     const int * a = one, * b = two;
00518     return (*a - *b);
00519 }
00520 
00530 static INLINE int dbiAppendSet(dbiIndexSet set, const void * recs,
00531         int nrecs, size_t recsize, int sortset)
00532 {
00533     const char * rptr = recs;
00534     size_t rlen = (recsize < sizeof(*(set->recs)))
00535                 ? recsize : sizeof(*(set->recs));
00536 
00537     if (set == NULL || recs == NULL || nrecs <= 0 || recsize <= 0)
00538         return 1;
00539 
00540     set->recs = (set->count == 0)
00541         ? xmalloc(nrecs * sizeof(*(set->recs)))
00542         : xrealloc(set->recs, (set->count + nrecs) * sizeof(*(set->recs)));
00543 
00544     memset(set->recs + set->count, 0, nrecs * sizeof(*(set->recs)));
00545 
00546     while (nrecs-- > 0) {
00547         memcpy(set->recs + set->count, rptr, rlen);
00548         rptr += recsize;
00549         set->count++;
00550     }
00551 
00552     if (set->count > 1 && sortset)
00553         qsort(set->recs, set->count, sizeof(*(set->recs)), hdrNumCmp);
00554 
00555     return 0;
00556 }
00557 
00567 static INLINE int dbiPruneSet(dbiIndexSet set, void * recs, int nrecs,
00568         size_t recsize, int sorted)
00569 {
00570     int from;
00571     int to = 0;
00572     int num = set->count;
00573     int numCopied = 0;
00574 
00575     if (nrecs > 1 && !sorted)
00576         qsort(recs, nrecs, recsize, hdrNumCmp);
00577 
00578     for (from = 0; from < num; from++) {
00579         if (bsearch(&set->recs[from], recs, nrecs, recsize, hdrNumCmp)) {
00580             set->count--;
00581             continue;
00582         }
00583         if (from != to)
00584             set->recs[to] = set->recs[from]; /* structure assignment */
00585         to++;
00586         numCopied++;
00587     }
00588 
00589     return (numCopied == num);
00590 }
00591 
00592 /* XXX transaction.c */
00593 unsigned int dbiIndexSetCount(dbiIndexSet set) {
00594     return set->count;
00595 }
00596 
00597 /* XXX transaction.c */
00598 unsigned int dbiIndexRecordOffset(dbiIndexSet set, int recno) {
00599     return set->recs[recno].hdrNum;
00600 }
00601 
00602 /* XXX transaction.c */
00603 unsigned int dbiIndexRecordFileNumber(dbiIndexSet set, int recno) {
00604     return set->recs[recno].tagNum;
00605 }
00606 
00607 /* XXX transaction.c */
00608 void dbiFreeIndexSet(dbiIndexSet set) {
00609     if (set) {
00610         if (set->recs) free(set->recs);
00611         free(set);
00612     }
00613 }
00614 
00618 static void blockSignals(rpmdb rpmdb, /*@out@*/ sigset_t * oldMask)
00619 {
00620     sigset_t newMask;
00621 
00622     /* XXX HACK (disabled) permit ^C aborts for now ... */
00623     if (!(rpmdb && rpmdb->db_api == 4)) {
00624         sigfillset(&newMask);           /* block all signals */
00625         sigprocmask(SIG_BLOCK, &newMask, oldMask);
00626     }
00627 }
00628 
00632 static void unblockSignals(rpmdb rpmdb, sigset_t * oldMask)
00633 {
00634     /* XXX HACK (disabled) permit ^C aborts for now ... */
00635     if (!(rpmdb && rpmdb->db_api == 4)) {
00636         sigprocmask(SIG_SETMASK, oldMask, NULL);
00637     }
00638 }
00639 
00640 #define _DB_ROOT        "/"
00641 #define _DB_HOME        "%{_dbpath}"
00642 #define _DB_FLAGS       0
00643 #define _DB_MODE        0
00644 #define _DB_PERMS       0644
00645 
00646 #define _DB_MAJOR       -1
00647 #define _DB_REMOVE_ENV  0
00648 #define _DB_FILTER_DUPS 0
00649 #define _DB_ERRPFX      "rpmdb"
00650 
00651 static struct rpmdb_s dbTemplate = {
00652     _DB_ROOT,   _DB_HOME, _DB_FLAGS, _DB_MODE, _DB_PERMS,
00653     _DB_MAJOR,  _DB_REMOVE_ENV, _DB_FILTER_DUPS, _DB_ERRPFX
00654 };
00655 
00656 int rpmdbOpenAll (rpmdb rpmdb)
00657 {
00658     int dbix;
00659 
00660     for (dbix = 0; dbix < dbiTagsMax; dbix++) {
00661         if (rpmdb->_dbi[dbix] != NULL)
00662             continue;
00663         (void) dbiOpen(rpmdb, dbiTags[dbix], rpmdb->db_flags);
00664     }
00665     return 0;
00666 }
00667 
00668 /* XXX query.c, rpminstall.c, verify.c */
00669 int rpmdbClose (rpmdb rpmdb)
00670 {
00671     int dbix;
00672 
00673     for (dbix = rpmdb->db_ndbi; --dbix >= 0; ) {
00674         if (rpmdb->_dbi[dbix] == NULL)
00675             continue;
00676         dbiClose(rpmdb->_dbi[dbix], 0);
00677         rpmdb->_dbi[dbix] = NULL;
00678     }
00679     if (rpmdb->db_errpfx) {
00680         free((void *)rpmdb->db_errpfx);
00681         rpmdb->db_errpfx = NULL;
00682     }
00683     if (rpmdb->db_root) {
00684         free((void *)rpmdb->db_root);
00685         rpmdb->db_root = NULL;
00686     }
00687     if (rpmdb->db_home) {
00688         free((void *)rpmdb->db_home);
00689         rpmdb->db_home = NULL;
00690     }
00691     if (rpmdb->_dbi) {
00692         free((void *)rpmdb->_dbi);
00693         rpmdb->_dbi = NULL;
00694     }
00695     free(rpmdb);
00696     return 0;
00697 }
00698 
00699 int rpmdbSync(rpmdb rpmdb)
00700 {
00701     int dbix;
00702 
00703     for (dbix = 0; dbix < rpmdb->db_ndbi; dbix++) {
00704         int xx;
00705         if (rpmdb->_dbi[dbix] == NULL)
00706             continue;
00707         xx = dbiSync(rpmdb->_dbi[dbix], 0);
00708     }
00709     return 0;
00710 }
00711 
00712 static /*@only@*/ rpmdb newRpmdb(const char * root, const char * home,
00713                 int mode, int perms, int flags)
00714 {
00715     rpmdb rpmdb = xcalloc(sizeof(*rpmdb), 1);
00716     static int _initialized = 0;
00717 
00718     if (!_initialized) {
00719         _filterDbDups = rpmExpandNumeric("%{_filterdbdups}");
00720         _initialized = 1;
00721     }
00722 
00723     *rpmdb = dbTemplate;        /* structure assignment */
00724 
00725     if (!(perms & 0600)) perms = 0644;  /* XXX sanity */
00726 
00727     if (root)
00728         rpmdb->db_root = (*root ? root : _DB_ROOT);
00729     if (home)
00730         rpmdb->db_home = (*home ? home : _DB_HOME);
00731     if (mode >= 0)      rpmdb->db_mode = mode;
00732     if (perms >= 0)     rpmdb->db_perms = perms;
00733     if (flags >= 0)     rpmdb->db_flags = flags;
00734 
00735     if (rpmdb->db_root)
00736         rpmdb->db_root = rpmGetPath(rpmdb->db_root, NULL);
00737     if (rpmdb->db_home) {
00738         rpmdb->db_home = rpmGetPath(rpmdb->db_home, NULL);
00739         if (!(rpmdb->db_home && rpmdb->db_home[0] != '%')) {
00740             rpmError(RPMERR_DBOPEN, _("no dbpath has been set\n"));
00741            goto errxit;
00742         }
00743     }
00744     if (rpmdb->db_errpfx)
00745         rpmdb->db_errpfx = xstrdup(rpmdb->db_errpfx);
00746     rpmdb->db_remove_env = 0;
00747     rpmdb->db_filter_dups = _filterDbDups;
00748     rpmdb->db_ndbi = dbiTagsMax;
00749     rpmdb->_dbi = xcalloc(rpmdb->db_ndbi, sizeof(*rpmdb->_dbi));
00750     return rpmdb;
00751 
00752 errxit:
00753     if (rpmdb)
00754         rpmdbClose(rpmdb);
00755     return NULL;
00756 }
00757 
00758 static int openDatabase(const char * prefix, const char * dbpath, int _dbapi,
00759         /*@out@*/ rpmdb *dbp, int mode, int perms, int flags)
00760 {
00761     rpmdb rpmdb;
00762     int rc;
00763     static int _initialized = 0;
00764     int justCheck = flags & RPMDB_FLAG_JUSTCHECK;
00765     int minimal = flags & RPMDB_FLAG_MINIMAL;
00766 
00767     if (!_initialized || dbiTagsMax == 0) {
00768         dbiTagsInit();
00769         _initialized++;
00770     }
00771 
00772     /* Insure that _dbapi has one of -1, 1, 2, or 3 */
00773     if (_dbapi < -1 || _dbapi > 3)
00774         _dbapi = -1;
00775     if (_dbapi == 0)
00776         _dbapi = 1;
00777 
00778     if (dbp)
00779         *dbp = NULL;
00780     if (mode & O_WRONLY) 
00781         return 1;
00782 
00783     rpmdb = newRpmdb(prefix, dbpath, mode, perms, flags);
00784     rpmdb->db_api = _dbapi;
00785 
00786     {   int dbix;
00787 
00788         rc = 0;
00789         for (dbix = 0; rc == 0 && dbix < dbiTagsMax; dbix++) {
00790             dbiIndex dbi;
00791             int rpmtag;
00792 
00793             /* Filter out temporary databases */
00794             switch ((rpmtag = dbiTags[dbix])) {
00795             case RPMDBI_AVAILABLE:
00796             case RPMDBI_ADDED:
00797             case RPMDBI_REMOVED:
00798             case RPMDBI_DEPENDS:
00799                 continue;
00800                 /*@notreached@*/ break;
00801             default:
00802                 break;
00803             }
00804 
00805             dbi = dbiOpen(rpmdb, rpmtag, 0);
00806 
00807             switch (rpmtag) {
00808             case RPMDBI_PACKAGES:
00809                 if (dbi == NULL) rc |= 1;
00810 #if 0
00811                 if (rpmdb->db_api == 3)
00812 #endif
00813                     goto exit;
00814                 break;
00815             case RPMTAG_NAME:
00816                 if (dbi == NULL) rc |= 1;
00817                 if (minimal)
00818                     goto exit;
00819                 break;
00820             case RPMTAG_BASENAMES:
00821             {   void * keyp = NULL;
00822                 DBC * dbcursor;
00823                 int xx;
00824 
00825     /* We used to store the fileindexes as complete paths, rather then
00826        plain basenames. Let's see which version we are... */
00827     /*
00828      * XXX FIXME: db->fileindex can be NULL under pathological (e.g. mixed
00829      * XXX db1/db2 linkage) conditions.
00830      */
00831                 if (justCheck)
00832                     break;
00833                 dbcursor = NULL;
00834                 xx = dbiCopen(dbi, &dbcursor, 0);
00835                 xx = dbiGet(dbi, dbcursor, &keyp, NULL, NULL, NULL, 0);
00836                 if (xx == 0) {
00837                     const char * akey = keyp;
00838                     if (strchr(akey, '/')) {
00839                         rpmError(RPMERR_OLDDB, _("old format database is present; "
00840                                 "use --rebuilddb to generate a new format database\n"));
00841                         rc |= 1;
00842                     }
00843                 }
00844                 xx = dbiCclose(dbi, dbcursor, 0);
00845                 dbcursor = NULL;
00846             }   break;
00847             default:
00848                 break;
00849             }
00850         }
00851     }
00852 
00853 exit:
00854     if (rc || justCheck || dbp == NULL)
00855         rpmdbClose(rpmdb);
00856     else
00857         *dbp = rpmdb;
00858 
00859     return rc;
00860 }
00861 
00862 /* XXX python/rpmmodule.c */
00863 int rpmdbOpen (const char * prefix, rpmdb *dbp, int mode, int perms)
00864 {
00865     int _dbapi = rpmExpandNumeric("%{_dbapi}");
00866     return openDatabase(prefix, NULL, _dbapi, dbp, mode, perms, 0);
00867 }
00868 
00869 int rpmdbInit (const char * prefix, int perms)
00870 {
00871     rpmdb rpmdb = NULL;
00872     int _dbapi = rpmExpandNumeric("%{_dbapi}");
00873     int rc;
00874 
00875     rc = openDatabase(prefix, NULL, _dbapi, &rpmdb, (O_CREAT | O_RDWR),
00876                 perms, RPMDB_FLAG_JUSTCHECK);
00877     if (rpmdb) {
00878         rpmdbOpenAll(rpmdb);
00879         rpmdbClose(rpmdb);
00880         rpmdb = NULL;
00881     }
00882     return rc;
00883 }
00884 
00885 static int rpmdbFindByFile(rpmdb rpmdb, const char * filespec,
00886                         /*@out@*/ dbiIndexSet * matches)
00887 {
00888     const char * dirName;
00889     const char * baseName;
00890     fingerPrintCache fpc;
00891     fingerPrint fp1;
00892     dbiIndex dbi = NULL;
00893     DBC * dbcursor;
00894     dbiIndexSet allMatches = NULL;
00895     dbiIndexItem rec = NULL;
00896     int i;
00897     int rc;
00898     int xx;
00899 
00900     *matches = NULL;
00901     if ((baseName = strrchr(filespec, '/')) != NULL) {
00902         char * t;
00903         size_t len;
00904 
00905         len = baseName - filespec + 1;
00906         t = strncpy(alloca(len + 1), filespec, len);
00907         t[len] = '\0';
00908         dirName = t;
00909         baseName++;
00910     } else {
00911         dirName = "";
00912         baseName = filespec;
00913     }
00914 
00915     fpc = fpCacheCreate(20);
00916     fp1 = fpLookup(fpc, dirName, baseName, 1);
00917 
00918     dbi = dbiOpen(rpmdb, RPMTAG_BASENAMES, 0);
00919     dbcursor = NULL;
00920     xx = dbiCopen(dbi, &dbcursor, 0);
00921     rc = dbiSearch(dbi, dbcursor, baseName, 0, &allMatches);
00922     xx = dbiCclose(dbi, dbcursor, 0);
00923     dbcursor = NULL;
00924     if (rc) {
00925         dbiFreeIndexSet(allMatches);
00926         allMatches = NULL;
00927         fpCacheFree(fpc);
00928         return rc;
00929     }
00930 
00931     *matches = xcalloc(1, sizeof(**matches));
00932     rec = dbiIndexNewItem(0, 0);
00933     i = 0;
00934     while (i < allMatches->count) {
00935         const char ** baseNames, ** dirNames;
00936         int_32 * dirIndexes;
00937         unsigned int offset = dbiIndexRecordOffset(allMatches, i);
00938         unsigned int prevoff;
00939         Header h;
00940 
00941         {   rpmdbMatchIterator mi;
00942             mi = rpmdbInitIterator(rpmdb, RPMDBI_PACKAGES, &offset, sizeof(offset));
00943             h = rpmdbNextIterator(mi);
00944             if (h)
00945                 h = headerLink(h);
00946             rpmdbFreeIterator(mi);
00947         }
00948 
00949         if (h == NULL) {
00950             i++;
00951             continue;
00952         }
00953 
00954         headerGetEntryMinMemory(h, RPMTAG_BASENAMES, NULL, 
00955                                 (const void **) &baseNames, NULL);
00956         headerGetEntryMinMemory(h, RPMTAG_DIRNAMES, NULL, 
00957                                 (const void **) &dirNames, NULL);
00958         headerGetEntryMinMemory(h, RPMTAG_DIRINDEXES, NULL, 
00959                                 (const void **) &dirIndexes, NULL);
00960 
00961         do {
00962             fingerPrint fp2;
00963             int num = dbiIndexRecordFileNumber(allMatches, i);
00964 
00965             fp2 = fpLookup(fpc, dirNames[dirIndexes[num]], baseNames[num], 1);
00966             if (FP_EQUAL(fp1, fp2)) {
00967                 rec->hdrNum = dbiIndexRecordOffset(allMatches, i);
00968                 rec->tagNum = dbiIndexRecordFileNumber(allMatches, i);
00969                 dbiAppendSet(*matches, rec, 1, sizeof(*rec), 0);
00970             }
00971 
00972             prevoff = offset;
00973             i++;
00974             offset = dbiIndexRecordOffset(allMatches, i);
00975         } while (i < allMatches->count && 
00976                 (i == 0 || offset == prevoff));
00977 
00978         free(baseNames);
00979         free(dirNames);
00980         headerFree(h);
00981     }
00982 
00983     if (rec) {
00984         free(rec);
00985         rec = NULL;
00986     }
00987     if (allMatches) {
00988         dbiFreeIndexSet(allMatches);
00989         allMatches = NULL;
00990     }
00991 
00992     fpCacheFree(fpc);
00993 
00994     if ((*matches)->count == 0) {
00995         dbiFreeIndexSet(*matches);
00996         *matches = NULL; 
00997         return 1;
00998     }
00999 
01000     return 0;
01001 }
01002 
01003 /* XXX python/upgrade.c, install.c, uninstall.c */
01004 int rpmdbCountPackages(rpmdb rpmdb, const char * name)
01005 {
01006     dbiIndex dbi;
01007     dbiIndexSet matches = NULL;
01008     int rc = -1;
01009     int xx;
01010 
01011     dbi = dbiOpen(rpmdb, RPMTAG_NAME, 0);
01012     if (dbi) {
01013         DBC * dbcursor = NULL;
01014         xx = dbiCopen(dbi, &dbcursor, 0);
01015         rc = dbiSearch(dbi, dbcursor, name, 0, &matches);
01016         xx = dbiCclose(dbi, dbcursor, 0);
01017         dbcursor = NULL;
01018     }
01019 
01020     if (rc == 0)        /* success */
01021         rc = dbiIndexSetCount(matches);
01022     else if (rc > 0)    /* error */
01023         rpmError(RPMERR_DBCORRUPT, _("error(%d) counting packages\n"), rc);
01024     else                /* not found */
01025         rc = 0;
01026 
01027     if (matches)
01028         dbiFreeIndexSet(matches);
01029 
01030     return rc;
01031 }
01032 
01033 /* XXX transaction.c */
01034 /* 0 found matches */
01035 /* 1 no matches */
01036 /* 2 error */
01037 static int dbiFindMatches(dbiIndex dbi, DBC * dbcursor,
01038         const char * name, const char * version, const char * release,
01039         /*@out@*/ dbiIndexSet * matches)
01040 {
01041     int gotMatches;
01042     int rc;
01043     int i;
01044 
01045     rc = dbiSearch(dbi, dbcursor, name, 0, matches);
01046 
01047     if (rc != 0) {
01048         rc = ((rc == -1) ? 2 : 1);
01049         goto exit;
01050     }
01051 
01052     if (!version && !release) {
01053         rc = 0;
01054         goto exit;
01055     }
01056 
01057     gotMatches = 0;
01058 
01059     /* make sure the version and releases match */
01060     for (i = 0; i < dbiIndexSetCount(*matches); i++) {
01061         unsigned int recoff = dbiIndexRecordOffset(*matches, i);
01062         int goodRelease, goodVersion;
01063         const char * pkgVersion;
01064         const char * pkgRelease;
01065         Header h;
01066 
01067         if (recoff == 0)
01068             continue;
01069 
01070     {   rpmdbMatchIterator mi;
01071         mi = rpmdbInitIterator(dbi->dbi_rpmdb, RPMDBI_PACKAGES, &recoff, sizeof(recoff));
01072         h = rpmdbNextIterator(mi);
01073         if (h)
01074             h = headerLink(h);
01075         rpmdbFreeIterator(mi);
01076     }
01077 
01078         if (h == NULL) {
01079             rpmError(RPMERR_DBCORRUPT, _("%s: cannot read header at 0x%x\n"),
01080                 "findMatches", recoff);
01081             rc = 2;
01082             goto exit;
01083         }
01084 
01085         headerNVR(h, NULL, &pkgVersion, &pkgRelease);
01086             
01087         goodRelease = goodVersion = 1;
01088 
01089         if (release && strcmp(release, pkgRelease)) goodRelease = 0;
01090         if (version && strcmp(version, pkgVersion)) goodVersion = 0;
01091 
01092         if (goodRelease && goodVersion) {
01093             /* structure assignment */
01094             (*matches)->recs[gotMatches++] = (*matches)->recs[i];
01095         } else 
01096             (*matches)->recs[i].hdrNum = 0;
01097 
01098         headerFree(h);
01099     }
01100 
01101     if (gotMatches) {
01102         (*matches)->count = gotMatches;
01103         rc = 0;
01104     } else {
01105         rc = 1;
01106     }
01107 
01108 exit:
01109     if (rc && matches && *matches) {
01110         dbiFreeIndexSet(*matches);
01111         *matches = NULL;
01112     }
01113     return rc;
01114 }
01115 
01116 /* 0 found matches */
01117 /* 1 no matches */
01118 /* 2 error */
01119 static int dbiFindByLabel(dbiIndex dbi, DBC * dbcursor, const char * arg, dbiIndexSet * matches)
01120 {
01121     char * localarg, * chptr;
01122     char * release;
01123     int rc;
01124  
01125     if (!strlen(arg)) return 1;
01126 
01127     /* did they give us just a name? */
01128     rc = dbiFindMatches(dbi, dbcursor, arg, NULL, NULL, matches);
01129     if (rc != 1) return rc;
01130     if (*matches) {
01131         dbiFreeIndexSet(*matches);
01132         *matches = NULL;
01133     }
01134 
01135     /* maybe a name and a release */
01136     localarg = alloca(strlen(arg) + 1);
01137     strcpy(localarg, arg);
01138 
01139     chptr = (localarg + strlen(localarg)) - 1;
01140     while (chptr > localarg && *chptr != '-') chptr--;
01141     if (chptr == localarg) return 1;
01142 
01143     *chptr = '\0';
01144     rc = dbiFindMatches(dbi, dbcursor, localarg, chptr + 1, NULL, matches);
01145     if (rc != 1) return rc;
01146     if (*matches) dbiFreeIndexSet(*matches);
01147     
01148     /* how about name-version-release? */
01149 
01150     release = chptr + 1;
01151     while (chptr > localarg && *chptr != '-') chptr--;
01152     if (chptr == localarg) return 1;
01153 
01154     *chptr = '\0';
01155     return dbiFindMatches(dbi, dbcursor, localarg, chptr + 1, release, matches);
01156 }
01157 
01167 static int dbiUpdateRecord(dbiIndex dbi, DBC * dbcursor, int offset, Header h)
01168 {
01169     sigset_t signalMask;
01170     void * uh;
01171     size_t uhlen;
01172     int rc;
01173     int xx;
01174 
01175     if (_noDirTokens)
01176         expandFilelist(h);
01177 
01178     uhlen = headerSizeof(h, HEADER_MAGIC_NO);
01179     uh = headerUnload(h);
01180     blockSignals(dbi->dbi_rpmdb, &signalMask);
01181     rc = dbiPut(dbi, dbcursor, &offset, sizeof(offset), uh, uhlen, 0);
01182     xx = dbiSync(dbi, 0);
01183     unblockSignals(dbi->dbi_rpmdb, &signalMask);
01184     free(uh);
01185 
01186     return rc;
01187 }
01188 
01189 struct _rpmdbMatchIterator {
01190     const void *        mi_keyp;
01191     size_t              mi_keylen;
01192     rpmdb               mi_rpmdb;
01193     int                 mi_rpmtag;
01194     dbiIndexSet         mi_set;
01195     DBC *               mi_dbc;
01196     int                 mi_setx;
01197     Header              mi_h;
01198     int                 mi_sorted;
01199     int                 mi_modified;
01200     unsigned int        mi_prevoffset;
01201     unsigned int        mi_offset;
01202     unsigned int        mi_filenum;
01203     unsigned int        mi_fpnum;
01204     unsigned int        mi_dbnum;
01205     const char *        mi_version;
01206     const char *        mi_release;
01207 };
01208 
01209 void rpmdbFreeIterator(rpmdbMatchIterator mi)
01210 {
01211     dbiIndex dbi = NULL;
01212     int xx;
01213 
01214     if (mi == NULL)
01215         return;
01216 
01217     dbi = dbiOpen(mi->mi_rpmdb, RPMDBI_PACKAGES, 0);
01218     if (mi->mi_h) {
01219         if (mi->mi_modified && mi->mi_prevoffset) {
01220             DBC * dbcursor = NULL;
01221             xx = dbiCopen(dbi, &dbcursor, 0);
01222             dbiUpdateRecord(dbi, dbcursor, mi->mi_prevoffset, mi->mi_h);
01223             xx = dbiCclose(dbi, dbcursor, 0);
01224             dbcursor = NULL;
01225         }
01226         headerFree(mi->mi_h);
01227         mi->mi_h = NULL;
01228     }
01229     if (dbi->dbi_rmw) {
01230         xx = dbiCclose(dbi, dbi->dbi_rmw, 0);
01231         dbi->dbi_rmw = NULL;
01232     }
01233 
01234     if (mi->mi_release) {
01235         free((void *)mi->mi_release);
01236         mi->mi_release = NULL;
01237     }
01238     if (mi->mi_version) {
01239         free((void *)mi->mi_version);
01240         mi->mi_version = NULL;
01241     }
01242     if (mi->mi_dbc) {
01243         int xx = dbiCclose(dbi, mi->mi_dbc, 1);
01244         mi->mi_dbc = NULL;
01245     }
01246     if (mi->mi_set) {
01247         dbiFreeIndexSet(mi->mi_set);
01248         mi->mi_set = NULL;
01249     }
01250     if (mi->mi_keyp) {
01251         free((void *)mi->mi_keyp);
01252         mi->mi_keyp = NULL;
01253     }
01254     free(mi);
01255 }
01256 
01257 rpmdb rpmdbGetIteratorRpmDB(rpmdbMatchIterator mi) {
01258     if (mi == NULL)
01259         return 0;
01260     return mi->mi_rpmdb;
01261 }
01262 
01263 unsigned int rpmdbGetIteratorOffset(rpmdbMatchIterator mi) {
01264     if (mi == NULL)
01265         return 0;
01266     return mi->mi_offset;
01267 }
01268 
01269 unsigned int rpmdbGetIteratorFileNum(rpmdbMatchIterator mi) {
01270     if (mi == NULL)
01271         return 0;
01272     return mi->mi_filenum;
01273 }
01274 
01275 int rpmdbGetIteratorCount(rpmdbMatchIterator mi) {
01276     if (!(mi && mi->mi_set))
01277         return 0;       /* XXX W2DO? */
01278     return mi->mi_set->count;
01279 }
01280 
01281 void rpmdbSetIteratorRelease(rpmdbMatchIterator mi, const char * release) {
01282     if (mi == NULL)
01283         return;
01284     if (mi->mi_release) {
01285         free((void *)mi->mi_release);
01286         mi->mi_release = NULL;
01287     }
01288     mi->mi_release = (release ? xstrdup(release) : NULL);
01289 }
01290 
01291 void rpmdbSetIteratorVersion(rpmdbMatchIterator mi, const char * version) {
01292     if (mi == NULL)
01293         return;
01294     if (mi->mi_version) {
01295         free((void *)mi->mi_version);
01296         mi->mi_version = NULL;
01297     }
01298     mi->mi_version = (version ? xstrdup(version) : NULL);
01299 }
01300 
01301 int rpmdbSetIteratorModified(rpmdbMatchIterator mi, int modified) {
01302     int rc;
01303     if (mi == NULL)
01304         return 0;
01305     rc = mi->mi_modified;
01306     mi->mi_modified = modified;
01307     return rc;
01308 }
01309 
01310 Header XrpmdbNextIterator(rpmdbMatchIterator mi, const char * f, unsigned l)
01311 {
01312     dbiIndex dbi;
01313     void * uh = NULL;
01314     size_t uhlen = 0;
01315     void * keyp;
01316     size_t keylen;
01317     int rc;
01318     int xx;
01319 
01320     if (mi == NULL)
01321         return NULL;
01322 
01323     dbi = dbiOpen(mi->mi_rpmdb, RPMDBI_PACKAGES, 0);
01324     if (dbi == NULL)
01325         return NULL;
01326     /* XXX cursors need to be per-iterator, not per-dbi. Get a cursor now. */
01327     if (mi->mi_dbc == NULL) {
01328         xx = XdbiCopen(dbi, &mi->mi_dbc, 1, f, l);
01329     }
01330     dbi->dbi_lastoffset = mi->mi_prevoffset;
01331 
01332 top:
01333     /* XXX skip over instances with 0 join key */
01334     do {
01335         if (mi->mi_set) {
01336             if (!(mi->mi_setx < mi->mi_set->count))
01337                 return NULL;
01338             mi->mi_offset = dbiIndexRecordOffset(mi->mi_set, mi->mi_setx);
01339             mi->mi_filenum = dbiIndexRecordFileNumber(mi->mi_set, mi->mi_setx);
01340             keyp = &mi->mi_offset;
01341             keylen = sizeof(mi->mi_offset);
01342         } else {
01343             keyp = (void *)mi->mi_keyp;         /* XXX FIXME const */
01344             keylen = mi->mi_keylen;
01345 
01346             rc = dbiGet(dbi, mi->mi_dbc, &keyp, &keylen, &uh, &uhlen, 0);
01347 
01348             /*
01349              * If we got the next key, save the header instance number.
01350              * For db1 Packages (db1->dbi_lastoffset != 0), always copy.
01351              * For db3 Packages, instance 0 (i.e. mi->mi_setx == 0) is the
01352              * largest header instance in the database, and should be
01353              * skipped.
01354              */
01355             if (rc == 0 && keyp && (dbi->dbi_lastoffset || mi->mi_setx))
01356                 memcpy(&mi->mi_offset, keyp, sizeof(mi->mi_offset));
01357 
01358             /* Terminate on error or end of keys */
01359             if (rc || (mi->mi_setx && mi->mi_offset == 0))
01360                 return NULL;
01361         }
01362         mi->mi_setx++;
01363     } while (mi->mi_offset == 0);
01364 
01365     if (mi->mi_prevoffset && mi->mi_offset == mi->mi_prevoffset)
01366         goto exit;
01367 
01368     /* Retrieve next header */
01369     if (uh == NULL) {
01370         rc = dbiGet(dbi, mi->mi_dbc, &keyp, &keylen, &uh, &uhlen, 0);
01371         if (rc)
01372             return NULL;
01373     }
01374 
01375     /* Free current header */
01376     if (mi->mi_h) {
01377         if (mi->mi_modified && mi->mi_prevoffset)
01378             dbiUpdateRecord(dbi, mi->mi_dbc, mi->mi_prevoffset, mi->mi_h);
01379         headerFree(mi->mi_h);
01380         mi->mi_h = NULL;
01381     }
01382 
01383     mi->mi_h = headerCopyLoad(uh);
01384     /* XXX db1 with hybrid, simulated db interface on falloc.c needs free. */
01385     if (dbi->dbi_api <= 1) free(uh);
01386 
01387     if (mi->mi_release) {
01388         const char *release;
01389         headerNVR(mi->mi_h, NULL, NULL, &release);
01390         if (strcmp(mi->mi_release, release))
01391             goto top;
01392     }
01393 
01394     if (mi->mi_version) {
01395         const char *version;
01396         headerNVR(mi->mi_h, NULL, &version, NULL);
01397         if (strcmp(mi->mi_version, version))
01398             goto top;
01399     }
01400 
01401     mi->mi_prevoffset = mi->mi_offset;
01402     mi->mi_modified = 0;
01403 
01404 exit:
01405 #ifdef  NOTNOW
01406     if (mi->mi_h) {
01407         const char *n, *v, *r;
01408         headerNVR(mi->mi_h, &n, &v, &r);
01409         rpmMessage(RPMMESS_DEBUG, "%s-%s-%s at 0x%x, h %p\n", n, v, r,
01410                 mi->mi_offset, mi->mi_h);
01411     }
01412 #endif
01413     return mi->mi_h;
01414 }
01415 
01416 static void rpmdbSortIterator(rpmdbMatchIterator mi) {
01417     if (mi && mi->mi_set && mi->mi_set->recs && mi->mi_set->count > 0) {
01418         qsort(mi->mi_set->recs, mi->mi_set->count, sizeof(*mi->mi_set->recs),
01419                 hdrNumCmp);
01420         mi->mi_sorted = 1;
01421     }
01422 }
01423 
01424 static int rpmdbGrowIterator(rpmdbMatchIterator mi,
01425         const void * keyp, size_t keylen, int fpNum)
01426 {
01427     dbiIndex dbi = NULL;
01428     DBC * dbcursor = NULL;
01429     dbiIndexSet set = NULL;
01430     int rc;
01431     int xx;
01432 
01433     if (!(mi && keyp))
01434         return 1;
01435 
01436     dbi = dbiOpen(mi->mi_rpmdb, mi->mi_rpmtag, 0);
01437     if (dbi == NULL)
01438         return 1;
01439 
01440     if (keylen == 0)
01441         keylen = strlen(keyp);
01442 
01443     xx = dbiCopen(dbi, &dbcursor, 0);
01444     rc = dbiSearch(dbi, dbcursor, keyp, keylen, &set);
01445     xx = dbiCclose(dbi, dbcursor, 0);
01446     dbcursor = NULL;
01447 
01448     if (rc == 0) {      /* success */
01449         int i;
01450         for (i = 0; i < set->count; i++)
01451             set->recs[i].fpNum = fpNum;
01452 
01453         if (mi->mi_set == NULL) {
01454             mi->mi_set = set;
01455             set = NULL;
01456         } else {
01457             mi->mi_set->recs = xrealloc(mi->mi_set->recs,
01458                 (mi->mi_set->count + set->count) * sizeof(*(mi->mi_set->recs)));
01459             memcpy(mi->mi_set->recs + mi->mi_set->count, set->recs,
01460                 set->count * sizeof(*(mi->mi_set->recs)));
01461             mi->mi_set->count += set->count;
01462         }
01463     }
01464 
01465     if (set)
01466         dbiFreeIndexSet(set);
01467     return rc;
01468 }
01469 
01470 int rpmdbPruneIterator(rpmdbMatchIterator mi, int * hdrNums,
01471         int nHdrNums, int sorted)
01472 {
01473     if (mi == NULL || hdrNums == NULL || nHdrNums <= 0)
01474         return 1;
01475 
01476     if (mi->mi_set)
01477         dbiPruneSet(mi->mi_set, hdrNums, nHdrNums, sizeof(*hdrNums), sorted);
01478     return 0;
01479 }
01480 
01481 int rpmdbAppendIterator(rpmdbMatchIterator mi, int * hdrNums, int nHdrNums)
01482 {
01483     if (mi == NULL || hdrNums == NULL || nHdrNums <= 0)
01484         return 1;
01485 
01486     if (mi->mi_set == NULL)
01487         mi->mi_set = xcalloc(1, sizeof(*mi->mi_set));
01488     dbiAppendSet(mi->mi_set, hdrNums, nHdrNums, sizeof(*hdrNums), 0);
01489     return 0;
01490 }
01491 
01492 rpmdbMatchIterator rpmdbInitIterator(rpmdb rpmdb, int rpmtag,
01493         const void * keyp, size_t keylen)
01494 {
01495     rpmdbMatchIterator mi = NULL;
01496     dbiIndexSet set = NULL;
01497     dbiIndex dbi;
01498     int isLabel = 0;
01499 
01500     /* XXX HACK to remove rpmdbFindByLabel/findMatches from the API */
01501     switch (rpmtag) {
01502     case RPMDBI_LABEL:
01503         rpmtag = RPMTAG_NAME;
01504         isLabel = 1;
01505         break;
01506     }
01507 
01508     dbi = dbiOpen(rpmdb, rpmtag, 0);
01509     if (dbi == NULL)
01510         return NULL;
01511 
01512 #if 0
01513     assert(dbi->dbi_rmw == NULL);       /* db3: avoid "lost" cursors */
01514     assert(dbi->dbi_lastoffset == 0);   /* db0: avoid "lost" cursors */
01515 #else
01516 if (dbi->dbi_rmw)
01517 fprintf(stderr, "*** RMW %s %p\n", tagName(rpmtag), dbi->dbi_rmw);
01518 #endif
01519 
01520     dbi->dbi_lastoffset = 0;            /* db0: rewind to beginning */
01521 
01522     if (rpmtag != RPMDBI_PACKAGES && keyp) {
01523         DBC * dbcursor = NULL;
01524         int rc;
01525         int xx;
01526 
01527         if (isLabel) {
01528             /* XXX HACK to get rpmdbFindByLabel out of the API */
01529             xx = dbiCopen(dbi, &dbcursor, 0);
01530             rc = dbiFindByLabel(dbi, dbcursor, keyp, &set);
01531             xx = dbiCclose(dbi, dbcursor, 0);
01532             dbcursor = NULL;
01533         } else if (rpmtag == RPMTAG_BASENAMES) {
01534             rc = rpmdbFindByFile(rpmdb, keyp, &set);
01535         } else {
01536             xx = dbiCopen(dbi, &dbcursor, 0);
01537             rc = dbiSearch(dbi, dbcursor, keyp, keylen, &set);
01538             xx = dbiCclose(dbi, dbcursor, 0);
01539             dbcursor = NULL;
01540         }
01541         if (rc) {       /* error/not found */
01542             if (set)
01543                 dbiFreeIndexSet(set);
01544             return NULL;
01545         }
01546     }
01547 
01548     if (keyp) {
01549         char * k;
01550 
01551         if (rpmtag != RPMDBI_PACKAGES && keylen == 0)
01552             keylen = strlen(keyp);
01553         k = xmalloc(keylen + 1);
01554         memcpy(k, keyp, keylen);
01555         k[keylen] = '\0';       /* XXX for strings */
01556         keyp = k;
01557     }
01558 
01559     mi = xcalloc(sizeof(*mi), 1);
01560     mi->mi_keyp = keyp;
01561     mi->mi_keylen = keylen;
01562 
01563     mi->mi_rpmdb = rpmdb;
01564     mi->mi_rpmtag = rpmtag;
01565 
01566     mi->mi_dbc = NULL;
01567     mi->mi_set = set;
01568     mi->mi_setx = 0;
01569     mi->mi_h = NULL;
01570     mi->mi_sorted = 0;
01571     mi->mi_modified = 0;
01572     mi->mi_prevoffset = 0;
01573     mi->mi_offset = 0;
01574     mi->mi_filenum = 0;
01575     mi->mi_fpnum = 0;
01576     mi->mi_dbnum = 0;
01577     mi->mi_version = NULL;
01578     mi->mi_release = NULL;
01579     return mi;
01580 }
01581 
01582 static INLINE int removeIndexEntry(dbiIndex dbi, DBC * dbcursor, const char * keyp,
01583                 dbiIndexItem rec)
01584 {
01585     dbiIndexSet set = NULL;
01586     int rc;
01587     
01588     rc = dbiSearch(dbi, dbcursor, keyp, 0, &set);
01589 
01590     if (rc < 0)                 /* not found */
01591         rc = 0;
01592     else if (rc > 0)            /* error */
01593         rc = 1;         /* error message already generated from dbindex.c */
01594     else {                      /* success */
01595         if (!dbiPruneSet(set, rec, 1, sizeof(*rec), 1) &&
01596             dbiUpdateIndex(dbi, dbcursor, keyp, set))
01597             rc = 1;
01598     }
01599 
01600     if (set) {
01601         dbiFreeIndexSet(set);
01602         set = NULL;
01603     }
01604 
01605     return rc;
01606 }
01607 
01608 /* XXX install.c uninstall.c */
01609 int rpmdbRemove(rpmdb rpmdb, int rid, unsigned int hdrNum)
01610 {
01611     Header h;
01612     sigset_t signalMask;
01613 
01614     {   rpmdbMatchIterator mi;
01615         mi = rpmdbInitIterator(rpmdb, RPMDBI_PACKAGES, &hdrNum, sizeof(hdrNum));
01616         h = rpmdbNextIterator(mi);
01617         if (h)
01618             h = headerLink(h);
01619         rpmdbFreeIterator(mi);
01620     }
01621 
01622     if (h == NULL) {
01623         rpmError(RPMERR_DBCORRUPT, _("%s: cannot read header at 0x%x\n"),
01624               "rpmdbRemove", hdrNum);
01625         return 1;
01626     }
01627 
01628     /* Add remove transaction id to header. */
01629     if (rid > 0) {
01630         int_32 tid = rid;
01631         headerAddEntry(h, RPMTAG_REMOVETID, RPM_INT32_TYPE, &tid, 1);
01632     }
01633 
01634     {   const char *n, *v, *r;
01635         headerNVR(h, &n, &v, &r);
01636         rpmMessage(RPMMESS_DEBUG, "  --- %10d %s-%s-%s\n", hdrNum, n, v, r);
01637     }
01638 
01639     blockSignals(rpmdb, &signalMask);
01640 
01641     {   int dbix;
01642         dbiIndexItem rec = dbiIndexNewItem(hdrNum, 0);
01643 
01644         for (dbix = 0; dbix < dbiTagsMax; dbix++) {
01645             dbiIndex dbi;
01646             DBC * dbcursor = NULL;
01647             const char *av[1];
01648             const char ** rpmvals = NULL;
01649             int rpmtype = 0;
01650             int rpmcnt = 0;
01651             int rpmtag;
01652             int xx;
01653             int i;
01654 
01655             dbi = NULL;
01656             rpmtag = dbiTags[dbix];
01657 
01658             switch (rpmtag) {
01659             /* Filter out temporary databases */
01660             case RPMDBI_AVAILABLE:
01661             case RPMDBI_ADDED:
01662             case RPMDBI_REMOVED:
01663             case RPMDBI_DEPENDS:
01664                 continue;
01665                 /*@notreached@*/ break;
01666             case RPMDBI_PACKAGES:
01667                 dbi = dbiOpen(rpmdb, rpmtag, 0);
01668                 xx = dbiCopen(dbi, &dbcursor, 0);
01669                 xx = dbiDel(dbi, dbcursor, &hdrNum, sizeof(hdrNum), 0);
01670                 xx = dbiCclose(dbi, dbcursor, 0);
01671                 dbcursor = NULL;
01672                 /* XXX HACK sync is on the bt with multiple db access */
01673                 if (!dbi->dbi_no_dbsync)
01674                     xx = dbiSync(dbi, 0);
01675                 continue;
01676                 /*@notreached@*/ break;
01677             }
01678         
01679             if (!headerGetEntry(h, rpmtag, &rpmtype,
01680                         (void **) &rpmvals, &rpmcnt))
01681                 continue;
01682 
01683             dbi = dbiOpen(rpmdb, rpmtag, 0);
01684             xx = dbiCopen(dbi, &dbcursor, 0);
01685 
01686             if (rpmtype == RPM_STRING_TYPE) {
01687 
01688                 rpmMessage(RPMMESS_DEBUG, _("removing \"%s\" from %s index.\n"), 
01689                         (const char *)rpmvals, tagName(dbi->dbi_rpmtag));
01690 
01691                 /* XXX force uniform headerGetEntry return */
01692                 av[0] = (const char *) rpmvals;
01693                 rpmvals = av;
01694                 rpmcnt = 1;
01695             } else {
01696 
01697                 rpmMessage(RPMMESS_DEBUG, _("removing %d entries from %s index.\n"), 
01698                         rpmcnt, tagName(dbi->dbi_rpmtag));
01699 
01700             }
01701 
01702             for (i = 0; i < rpmcnt; i++) {
01703                 /*
01704                  * This is almost right, but, if there are duplicate tag
01705                  * values, there will be duplicate attempts to remove
01706                  * the header instance. It's easier to just ignore errors
01707                  * than to do things correctly.
01708                  */
01709                 xx = removeIndexEntry(dbi, dbcursor, rpmvals[i], rec);
01710             }
01711 
01712             xx = dbiCclose(dbi, dbcursor, 0);
01713             dbcursor = NULL;
01714 
01715             /* XXX HACK sync is on the bt with multiple db access */
01716             if (!dbi->dbi_no_dbsync)
01717                 xx = dbiSync(dbi, 0);
01718 
01719             headerFreeData(rpmvals, rpmtype);
01720             rpmvals = NULL;
01721             rpmtype = 0;
01722             rpmcnt = 0;
01723         }
01724 
01725         if (rec) {
01726             free(rec);
01727             rec = NULL;
01728         }
01729     }
01730 
01731     unblockSignals(rpmdb, &signalMask);
01732 
01733     headerFree(h);
01734 
01735     return 0;
01736 }
01737 
01738 static INLINE int addIndexEntry(dbiIndex dbi, DBC * dbcursor, const char *index, dbiIndexItem rec)
01739 {
01740     dbiIndexSet set = NULL;
01741     int rc;
01742 
01743     rc = dbiSearch(dbi, dbcursor, index, 0, &set);
01744 
01745     if (rc > 0) {
01746         rc = 1;                 /* error */
01747     } else {
01748         if (rc < 0) {           /* not found */
01749             rc = 0;
01750             set = xcalloc(1, sizeof(*set));
01751         }
01752         dbiAppendSet(set, rec, 1, sizeof(*rec), 0);
01753         if (dbiUpdateIndex(dbi, dbcursor, index, set))
01754             rc = 1;
01755     }
01756 
01757     if (set) {
01758         dbiFreeIndexSet(set);
01759         set = NULL;
01760     }
01761 
01762     return 0;
01763 }
01764 
01765 /* XXX install.c */
01766 int rpmdbAdd(rpmdb rpmdb, int iid, Header h)
01767 {
01768     sigset_t signalMask;
01769     const char ** baseNames;
01770     int count = 0;
01771     int type;
01772     dbiIndex dbi;
01773     int dbix;
01774     unsigned int hdrNum;
01775     int rc = 0;
01776     int xx;
01777 
01778     if (iid > 0) {
01779         int_32 tid = iid;
01780         headerRemoveEntry(h, RPMTAG_REMOVETID);
01781         headerAddEntry(h, RPMTAG_INSTALLTID, RPM_INT32_TYPE, &tid, 1);
01782     }
01783 
01784     /*
01785      * If old style filename tags is requested, the basenames need to be
01786      * retrieved early, and the header needs to be converted before
01787      * being written to the package header database.
01788      */
01789 
01790     headerGetEntry(h, RPMTAG_BASENAMES, &type, (void **) &baseNames, &count);
01791 
01792     if (_noDirTokens)
01793         expandFilelist(h);
01794 
01795     blockSignals(rpmdb, &signalMask);
01796 
01797     {
01798         unsigned int firstkey = 0;
01799         DBC * dbcursor = NULL;
01800         void * keyp = &firstkey;
01801         size_t keylen = sizeof(firstkey);
01802         void * datap = NULL;
01803         size_t datalen = 0;
01804 
01805         dbi = dbiOpen(rpmdb, RPMDBI_PACKAGES, 0);
01806 
01807         /* XXX db0: hack to pass sizeof header to fadAlloc */
01808         datap = h;
01809         datalen = headerSizeof(h, HEADER_MAGIC_NO);
01810 
01811         xx = dbiCopen(dbi, &dbcursor, 0);
01812 
01813         /* Retrieve join key for next header instance. */
01814 
01815         rc = dbiGet(dbi, dbcursor, &keyp, &keylen, &datap, &datalen, 0);
01816 
01817         hdrNum = 0;
01818         if (rc == 0 && datap)
01819             memcpy(&hdrNum, datap, sizeof(hdrNum));
01820         ++hdrNum;
01821         if (rc == 0 && datap) {
01822             memcpy(datap, &hdrNum, sizeof(hdrNum));
01823         } else {
01824             datap = &hdrNum;
01825             datalen = sizeof(hdrNum);
01826         }
01827 
01828         rc = dbiPut(dbi, dbcursor, keyp, keylen, datap, datalen, 0);
01829         xx = dbiSync(dbi, 0);
01830 
01831         xx = dbiCclose(dbi, dbcursor, 0);
01832         dbcursor = NULL;
01833 
01834     }
01835 
01836     if (rc) {
01837         rpmError(RPMERR_DBCORRUPT,
01838                 _("error(%d) allocating new package instance\n"), rc);
01839         goto exit;
01840     }
01841 
01842     /* Now update the indexes */
01843 
01844     {   dbiIndexItem rec = dbiIndexNewItem(hdrNum, 0);
01845 
01846         for (dbix = 0; dbix < dbiTagsMax; dbix++) {
01847             DBC * dbcursor = NULL;
01848             const char *av[1];
01849             const char **rpmvals = NULL;
01850             int rpmtype = 0;
01851             int rpmcnt = 0;
01852             int rpmtag;
01853             int_32 * requireFlags;
01854             int i, j;
01855 
01856             dbi = NULL;
01857             requireFlags = NULL;
01858             rpmtag = dbiTags[dbix];
01859 
01860             switch (rpmtag) {
01861             /* Filter out temporary databases */
01862             case RPMDBI_AVAILABLE:
01863             case RPMDBI_ADDED:
01864             case RPMDBI_REMOVED:
01865             case RPMDBI_DEPENDS:
01866                 continue;
01867                 /*@notreached@*/ break;
01868             case RPMDBI_PACKAGES:
01869                 dbi = dbiOpen(rpmdb, rpmtag, 0);
01870                 xx = dbiCopen(dbi, &dbcursor, 0);
01871                 xx = dbiUpdateRecord(dbi, dbcursor, hdrNum, h);
01872                 xx = dbiCclose(dbi, dbcursor, 0);
01873                 dbcursor = NULL;
01874                 if (!dbi->dbi_no_dbsync)
01875                     xx = dbiSync(dbi, 0);
01876                 {   const char *n, *v, *r;
01877                     headerNVR(h, &n, &v, &r);
01878                     rpmMessage(RPMMESS_DEBUG, "  +++ %10d %s-%s-%s\n", hdrNum, n, v, r);
01879                 }
01880                 continue;
01881                 /*@notreached@*/ break;
01882             /* XXX preserve legacy behavior */
01883             case RPMTAG_BASENAMES:
01884                 rpmtype = type;
01885                 rpmvals = baseNames;
01886                 rpmcnt = count;
01887                 break;
01888             case RPMTAG_REQUIRENAME:
01889                 headerGetEntry(h, rpmtag, &rpmtype, (void **)&rpmvals, &rpmcnt);
01890                 headerGetEntry(h, RPMTAG_REQUIREFLAGS, NULL,
01891                         (void **)&requireFlags, NULL);
01892                 break;
01893             default:
01894                 headerGetEntry(h, rpmtag, &rpmtype, (void **)&rpmvals, &rpmcnt);
01895                 break;
01896             }
01897 
01898             if (rpmcnt <= 0) {
01899                 if (rpmtag != RPMTAG_GROUP)
01900                     continue;
01901 
01902                 /* XXX preserve legacy behavior */
01903                 rpmtype = RPM_STRING_TYPE;
01904                 rpmvals = (const char **) "Unknown";
01905                 rpmcnt = 1;
01906             }
01907 
01908             dbi = dbiOpen(rpmdb, rpmtag, 0);
01909 
01910             xx = dbiCopen(dbi, &dbcursor, 0);
01911             if (rpmtype == RPM_STRING_TYPE) {
01912                 rpmMessage(RPMMESS_DEBUG, _("adding \"%s\" to %s index.\n"), 
01913                         (const char *)rpmvals, tagName(dbi->dbi_rpmtag));
01914 
01915                 /* XXX force uniform headerGetEntry return */
01916                 av[0] = (const char *) rpmvals;
01917                 rpmvals = av;
01918                 rpmcnt = 1;
01919             } else {
01920 
01921                 rpmMessage(RPMMESS_DEBUG, _("adding %d entries to %s index.\n"), 
01922                         rpmcnt, tagName(dbi->dbi_rpmtag));
01923 
01924             }
01925 
01926             for (i = 0; i < rpmcnt; i++) {
01927 
01928                 /*
01929                  * Include the tagNum in all indices. rpm-3.0.4 and earlier
01930                  * included the tagNum only for files.
01931                  */
01932                 switch (dbi->dbi_rpmtag) {
01933                 case RPMTAG_REQUIRENAME:
01934                     /* Filter out install prerequisites. */
01935                     if (requireFlags && isInstallPreReq(requireFlags[i]))
01936                         continue;
01937                     rec->tagNum = i;
01938                     break;
01939                 case RPMTAG_TRIGGERNAME:
01940                     if (i) {    /* don't add duplicates */
01941                         for (j = 0; j < i; j++) {
01942                             if (!strcmp(rpmvals[i], rpmvals[j]))
01943                                 break;
01944                         }
01945                         if (j < i)
01946                             continue;
01947                     }
01948                     rec->tagNum = i;
01949                     break;
01950                 default:
01951                     rec->tagNum = i;
01952                     break;
01953                 }
01954 
01955                 rc += addIndexEntry(dbi, dbcursor, rpmvals[i], rec);
01956             }
01957             xx = dbiCclose(dbi, dbcursor, 0);
01958             dbcursor = NULL;
01959 
01960             /* XXX HACK sync is on the bt with multiple db access */
01961             if (!dbi->dbi_no_dbsync)
01962                 xx = dbiSync(dbi, 0);
01963 
01964             headerFreeData(rpmvals, rpmtype);
01965             rpmvals = NULL;
01966             rpmtype = 0;
01967             rpmcnt = 0;
01968         }
01969 
01970         if (rec) {
01971             free(rec);
01972             rec = NULL;
01973         }
01974     }
01975 
01976 exit:
01977     unblockSignals(rpmdb, &signalMask);
01978 
01979     return rc;
01980 }
01981 
01982 /* XXX transaction.c */
01983 int rpmdbFindFpList(rpmdb rpmdb, fingerPrint * fpList, dbiIndexSet * matchList, 
01984                     int numItems)
01985 {
01986     rpmdbMatchIterator mi;
01987     fingerPrintCache fpc;
01988     Header h;
01989     int i;
01990 
01991     mi = rpmdbInitIterator(rpmdb, RPMTAG_BASENAMES, NULL, 0);
01992 
01993     /* Gather all matches from the database */
01994     for (i = 0; i < numItems; i++) {
01995         rpmdbGrowIterator(mi, fpList[i].baseName, 0, i);
01996         matchList[i] = xcalloc(1, sizeof(*(matchList[i])));
01997     }
01998 
01999     if ((i = rpmdbGetIteratorCount(mi)) == 0) {
02000         rpmdbFreeIterator(mi);
02001         return 0;
02002     }
02003     fpc = fpCacheCreate(i);
02004 
02005     rpmdbSortIterator(mi);
02006     /* iterator is now sorted by (recnum, filenum) */
02007 
02008     /* For each set of files matched in a package ... */
02009     while ((h = rpmdbNextIterator(mi)) != NULL) {
02010         const char ** dirNames;
02011         const char ** baseNames;
02012         const char ** fullBaseNames;
02013         int_32 * dirIndexes;
02014         int_32 * fullDirIndexes;
02015         fingerPrint * fps;
02016         dbiIndexItem im;
02017         int start;
02018         int num;
02019         int end;
02020 
02021         start = mi->mi_setx - 1;
02022         im = mi->mi_set->recs + start;
02023 
02024         /* Find the end of the set of matched files in this package. */
02025         for (end = start + 1; end < mi->mi_set->count; end++) {
02026             if (im->hdrNum != mi->mi_set->recs[end].hdrNum)
02027                 break;
02028         }
02029         num = end - start;
02030 
02031         /* Compute fingerprints for this header's matches */
02032         headerGetEntryMinMemory(h, RPMTAG_BASENAMES, NULL, 
02033                             (const void **) &fullBaseNames, NULL);
02034         headerGetEntryMinMemory(h, RPMTAG_DIRNAMES, NULL, 
02035                             (const void **) &dirNames, NULL);
02036         headerGetEntryMinMemory(h, RPMTAG_DIRINDEXES, NULL, 
02037                             (const void **) &fullDirIndexes, NULL);
02038 
02039         baseNames = xcalloc(num, sizeof(*baseNames));
02040         dirIndexes = xcalloc(num, sizeof(*dirIndexes));
02041         for (i = 0; i < num; i++) {
02042             baseNames[i] = fullBaseNames[im[i].tagNum];
02043             dirIndexes[i] = fullDirIndexes[im[i].tagNum];
02044         }
02045 
02046         fps = xcalloc(num, sizeof(*fps));
02047         fpLookupList(fpc, dirNames, baseNames, dirIndexes, num, fps);
02048 
02049         /* Add db (recnum,filenum) to list for fingerprint matches. */
02050         for (i = 0; i < num; i++, im++) {
02051             if (FP_EQUAL(fps[i], fpList[im->fpNum]))
02052                 dbiAppendSet(matchList[im->fpNum], im, 1, sizeof(*im), 0);
02053         }
02054 
02055         free(fps);
02056         free(dirNames);
02057         free(fullBaseNames);
02058         free(baseNames);
02059         free(dirIndexes);
02060 
02061         mi->mi_setx = end;
02062     }
02063 
02064     rpmdbFreeIterator(mi);
02065 
02066     fpCacheFree(fpc);
02067 
02068     return 0;
02069 
02070 }
02071 
02072 char * db1basename (int rpmtag) {
02073     char * base = NULL;
02074     switch (rpmtag) {
02075     case RPMDBI_PACKAGES:       base = "packages.rpm";          break;
02076     case RPMTAG_NAME:           base = "nameindex.rpm";         break;
02077     case RPMTAG_BASENAMES:      base = "fileindex.rpm";         break;
02078     case RPMTAG_GROUP:          base = "groupindex.rpm";        break;
02079     case RPMTAG_REQUIRENAME:    base = "requiredby.rpm";        break;
02080     case RPMTAG_PROVIDENAME:    base = "providesindex.rpm";     break;
02081     case RPMTAG_CONFLICTNAME:   base = "conflictsindex.rpm";    break;
02082     case RPMTAG_TRIGGERNAME:    base = "triggerindex.rpm";      break;
02083     default:
02084       { const char * tn = tagName(rpmtag);
02085         base = alloca( strlen(tn) + sizeof(".idx") + 1 );
02086         (void) stpcpy( stpcpy(base, tn), ".idx");
02087       } break;
02088     }
02089     return xstrdup(base);
02090 }
02091 
02092 static int rpmdbRemoveDatabase(const char * rootdir,
02093         const char * dbpath, int _dbapi)
02094 { 
02095     int i;
02096     char * filename;
02097     int xx;
02098 
02099     i = strlen(dbpath);
02100     if (dbpath[i - 1] != '/') {
02101         filename = alloca(i);
02102         strcpy(filename, dbpath);
02103         filename[i] = '/';
02104         filename[i + 1] = '\0';
02105         dbpath = filename;
02106     }
02107     
02108     filename = alloca(strlen(rootdir) + strlen(dbpath) + 40);
02109 
02110     switch (_dbapi) {
02111     case 3:
02112         for (i = 0; i < dbiTagsMax; i++) {
02113             const char * base = tagName(dbiTags[i]);
02114             sprintf(filename, "%s/%s/%s", rootdir, dbpath, base);
02115             (void)rpmCleanPath(filename);
02116             xx = unlink(filename);
02117         }
02118         for (i = 0; i < 16; i++) {
02119             sprintf(filename, "%s/%s/__db.%03d", rootdir, dbpath, i);
02120             (void)rpmCleanPath(filename);
02121             xx = unlink(filename);
02122         }
02123         break;
02124     case 2:
02125     case 1:
02126     case 0:
02127         for (i = 0; i < dbiTagsMax; i++) {
02128             const char * base = db1basename(dbiTags[i]);
02129             sprintf(filename, "%s/%s/%s", rootdir, dbpath, base);
02130             (void)rpmCleanPath(filename);
02131             xx = unlink(filename);
02132             free((void *)base);
02133         }
02134         break;
02135     }
02136 
02137     sprintf(filename, "%s/%s", rootdir, dbpath);
02138     (void)rpmCleanPath(filename);
02139     xx = rmdir(filename);
02140 
02141     return 0;
02142 }
02143 
02144 static int rpmdbMoveDatabase(const char * rootdir,
02145         const char * olddbpath, int _olddbapi,
02146         const char * newdbpath, int _newdbapi)
02147 {
02148     int i;
02149     char * ofilename, * nfilename;
02150     int rc = 0;
02151     int xx;
02152  
02153     i = strlen(olddbpath);
02154     if (olddbpath[i - 1] != '/') {
02155         ofilename = alloca(i + 2);
02156         strcpy(ofilename, olddbpath);
02157         ofilename[i] = '/';
02158         ofilename[i + 1] = '\0';
02159         olddbpath = ofilename;
02160     }
02161     
02162     i = strlen(newdbpath);
02163     if (newdbpath[i - 1] != '/') {
02164         nfilename = alloca(i + 2);
02165         strcpy(nfilename, newdbpath);
02166         nfilename[i] = '/';
02167         nfilename[i + 1] = '\0';
02168         newdbpath = nfilename;
02169     }
02170     
02171     ofilename = alloca(strlen(rootdir) + strlen(olddbpath) + 40);
02172     nfilename = alloca(strlen(rootdir) + strlen(newdbpath) + 40);
02173 
02174     switch (_olddbapi) {
02175     case 3:
02176         for (i = 0; i < dbiTagsMax; i++) {
02177             const char * base;
02178             int rpmtag;
02179 
02180             /* Filter out temporary databases */
02181             switch ((rpmtag = dbiTags[i])) {
02182             case RPMDBI_AVAILABLE:
02183             case RPMDBI_ADDED:
02184             case RPMDBI_REMOVED:
02185             case RPMDBI_DEPENDS:
02186                 continue;
02187                 /*@notreached@*/ break;
02188             default:
02189                 break;
02190             }
02191 
02192             base = tagName(rpmtag);
02193             sprintf(ofilename, "%s/%s/%s", rootdir, olddbpath, base);
02194             (void)rpmCleanPath(ofilename);
02195             if (!rpmfileexists(ofilename))
02196                 continue;
02197             sprintf(nfilename, "%s/%s/%s", rootdir, newdbpath, base);
02198             (void)rpmCleanPath(nfilename);
02199             if ((xx = Rename(ofilename, nfilename)) != 0)
02200                 rc = 1;
02201         }
02202         for (i = 0; i < 16; i++) {
02203             sprintf(ofilename, "%s/%s/__db.%03d", rootdir, olddbpath, i);
02204             (void)rpmCleanPath(ofilename);
02205             if (!rpmfileexists(ofilename))
02206                 continue;
02207             sprintf(nfilename, "%s/%s/__db.%03d", rootdir, newdbpath, i);
02208             (void)rpmCleanPath(nfilename);
02209             if ((xx = Rename(ofilename, nfilename)) != 0)
02210                 rc = 1;
02211         }
02212         break;
02213     case 2:
02214     case 1:
02215     case 0:
02216         for (i = 0; i < dbiTagsMax; i++) {
02217             const char * base;
02218             int rpmtag;
02219 
02220             /* Filter out temporary databases */
02221             switch ((rpmtag = dbiTags[i])) {
02222             case RPMDBI_AVAILABLE:
02223             case RPMDBI_ADDED:
02224             case RPMDBI_REMOVED:
02225             case RPMDBI_DEPENDS:
02226                 continue;
02227                 /*@notreached@*/ break;
02228             default:
02229                 break;
02230             }
02231 
02232             base = db1basename(rpmtag);
02233             sprintf(ofilename, "%s/%s/%s", rootdir, olddbpath, base);
02234             (void)rpmCleanPath(ofilename);
02235             if (!rpmfileexists(ofilename))
02236                 continue;
02237             sprintf(nfilename, "%s/%s/%s", rootdir, newdbpath, base);
02238             (void)rpmCleanPath(nfilename);
02239             if ((xx = Rename(ofilename, nfilename)) != 0)
02240                 rc = 1;
02241             free((void *)base);
02242         }
02243         break;
02244     }
02245     if (rc || _olddbapi == _newdbapi)
02246         return rc;
02247 
02248     rc = rpmdbRemoveDatabase(rootdir, newdbpath, _newdbapi);
02249 
02250 
02251     /* Remove /etc/rpm/macros.db1 configuration file if db3 rebuilt. */
02252     if (rc == 0 && _newdbapi == 1 && _olddbapi == 3) {
02253         const char * mdb1 = "/etc/rpm/macros.db1";
02254         struct stat st;
02255         if (!stat(mdb1, &st) && S_ISREG(st.st_mode) && !unlink(mdb1))
02256             rpmMessage(RPMMESS_DEBUG,
02257                 _("removing %s after successful db3 rebuild.\n"), mdb1);
02258     }
02259     return rc;
02260 }
02261 
02262 int rpmdbRebuild(const char * rootdir)
02263 {
02264     rpmdb olddb;
02265     const char * dbpath = NULL;
02266     const char * rootdbpath = NULL;
02267     rpmdb newdb;
02268     const char * newdbpath = NULL;
02269     const char * newrootdbpath = NULL;
02270     const char * tfn;
02271     int nocleanup = 1;
02272     int failed = 0;
02273     int removedir = 0;
02274     int rc = 0;
02275     int _dbapi;
02276     int _dbapi_rebuild;
02277 
02278     _dbapi = rpmExpandNumeric("%{_dbapi}");
02279     _dbapi_rebuild = rpmExpandNumeric("%{_dbapi_rebuild}");
02280 
02281     tfn = rpmGetPath("%{_dbpath}", NULL);
02282     if (!(tfn && tfn[0] != '%')) {
02283         rpmMessage(RPMMESS_DEBUG, _("no dbpath has been set"));
02284         rc = 1;
02285         goto exit;
02286     }
02287     dbpath = rootdbpath = rpmGetPath(rootdir, tfn, NULL);
02288     if (!(rootdir[0] == '/' && rootdir[1] == '\0'))
02289         dbpath += strlen(rootdir);
02290     free((void *)tfn);
02291 
02292     tfn = rpmGetPath("%{_dbpath_rebuild}", NULL);
02293     if (!(tfn && tfn[0] != '%' && strcmp(tfn, dbpath))) {
02294         char pidbuf[20];
02295         char *t;
02296         sprintf(pidbuf, "rebuilddb.%d", (int) getpid());
02297         t = xmalloc(strlen(dbpath) + strlen(pidbuf) + 1);
02298         (void)stpcpy(stpcpy(t, dbpath), pidbuf);
02299         if (tfn) free((void *)tfn);
02300         tfn = t;
02301         nocleanup = 0;
02302     }
02303     newdbpath = newrootdbpath = rpmGetPath(rootdir, tfn, NULL);
02304     if (!(rootdir[0] == '/' && rootdir[1] == '\0'))
02305         newdbpath += strlen(rootdir);
02306     free((void *)tfn);
02307 
02308     rpmMessage(RPMMESS_DEBUG, _("rebuilding database %s into %s\n"),
02309         rootdbpath, newrootdbpath);
02310 
02311     if (!access(newrootdbpath, F_OK)) {
02312         rpmError(RPMERR_MKDIR, _("temporary database %s already exists\n"),
02313               newrootdbpath);
02314         rc = 1;
02315         goto exit;
02316     }
02317 
02318     rpmMessage(RPMMESS_DEBUG, _("creating directory %s\n"), newrootdbpath);
02319     if (Mkdir(newrootdbpath, 0755)) {
02320         rpmError(RPMERR_MKDIR, _("creating directory %s: %s\n"),
02321               newrootdbpath, strerror(errno));
02322         rc = 1;
02323         goto exit;
02324     }
02325     removedir = 1;
02326 
02327     rpmMessage(RPMMESS_DEBUG, _("opening old database with dbapi %d\n"),
02328                 _dbapi);
02329     _rebuildinprogress = 1;
02330     if (openDatabase(rootdir, dbpath, _dbapi, &olddb, O_RDONLY, 0644, 
02331                      RPMDB_FLAG_MINIMAL)) {
02332         rc = 1;
02333         goto exit;
02334     }
02335     _dbapi = olddb->db_api;
02336     _rebuildinprogress = 0;
02337 
02338     rpmMessage(RPMMESS_DEBUG, _("opening new database with dbapi %d\n"),
02339                 _dbapi_rebuild);
02340     if (openDatabase(rootdir, newdbpath, _dbapi_rebuild, &newdb, O_RDWR | O_CREAT, 0644, 0)) {
02341         rc = 1;
02342         goto exit;
02343     }
02344     _dbapi_rebuild = newdb->db_api;
02345     
02346     {   Header h = NULL;
02347         rpmdbMatchIterator mi;
02348 #define _RECNUM rpmdbGetIteratorOffset(mi)
02349 
02350         /* RPMDBI_PACKAGES */
02351         mi = rpmdbInitIterator(olddb, RPMDBI_PACKAGES, NULL, 0);
02352         while ((h = rpmdbNextIterator(mi)) != NULL) {
02353 
02354             /* let's sanity check this record a bit, otherwise just skip it */
02355             if (!(headerIsEntry(h, RPMTAG_NAME) &&
02356                 headerIsEntry(h, RPMTAG_VERSION) &&
02357                 headerIsEntry(h, RPMTAG_RELEASE) &&
02358                 headerIsEntry(h, RPMTAG_BUILDTIME)))
02359             {
02360                 rpmError(RPMERR_INTERNAL,
02361                         _("record number %d in database is bad -- skipping.\n"),
02362                         _RECNUM);
02363                 continue;
02364             }
02365 
02366             /* Filter duplicate entries ? (bug in pre rpm-3.0.4) */
02367             if (_db_filter_dups || newdb->db_filter_dups) {
02368                 const char * name, * version, * release;
02369                 int skip = 0;
02370 
02371                 headerNVR(h, &name, &version, &release);
02372 
02373                 {   rpmdbMatchIterator mi;
02374                     mi = rpmdbInitIterator(newdb, RPMTAG_NAME, name, 0);
02375                     rpmdbSetIteratorVersion(mi, version);
02376                     rpmdbSetIteratorRelease(mi, release);
02377                     while (rpmdbNextIterator(mi)) {
02378                         skip = 1;
02379                         break;
02380                     }
02381                     rpmdbFreeIterator(mi);
02382                 }
02383 
02384                 if (skip)
02385                     continue;
02386             }
02387 
02388             /* Deleted entries are eliminated in legacy headers by copy. */
02389             {   Header nh = (headerIsEntry(h, RPMTAG_HEADERIMAGE)
02390                                 ? headerCopy(h) : NULL);
02391                 rc = rpmdbAdd(newdb, -1, (nh ? nh : h));
02392                 if (nh)
02393                     headerFree(nh);
02394             }
02395 
02396             if (rc) {
02397                 rpmError(RPMERR_INTERNAL,
02398                         _("cannot add record originally at %d\n"), _RECNUM);
02399                 failed = 1;
02400                 break;
02401             }
02402         }
02403 
02404         rpmdbFreeIterator(mi);
02405 
02406     }
02407 
02408     if (!nocleanup) {
02409         olddb->db_remove_env = 1;
02410         newdb->db_remove_env = 1;
02411     }
02412     rpmdbClose(olddb);
02413     rpmdbClose(newdb);
02414 
02415     if (failed) {
02416         rpmMessage(RPMMESS_NORMAL, _("failed to rebuild database: original database "
02417                 "remains in place\n"));
02418 
02419         rpmdbRemoveDatabase(rootdir, newdbpath, _dbapi_rebuild);
02420         rc = 1;
02421         goto exit;
02422     } else if (!nocleanup) {
02423         if (rpmdbMoveDatabase(rootdir, newdbpath, _dbapi_rebuild, dbpath, _dbapi)) {
02424             rpmMessage(RPMMESS_ERROR, _("failed to replace old database with new "
02425                         "database!\n"));
02426             rpmMessage(RPMMESS_ERROR, _("replace files in %s with files from %s "
02427                         "to recover"), dbpath, newdbpath);
02428             rc = 1;
02429             goto exit;
02430         }
02431     }
02432     rc = 0;
02433 
02434 exit:
02435     if (removedir && !(rc == 0 && nocleanup)) {
02436         rpmMessage(RPMMESS_DEBUG, _("removing directory %s\n"), newrootdbpath);
02437         if (Rmdir(newrootdbpath))
02438             rpmMessage(RPMMESS_ERROR, _("failed to remove directory %s: %s\n"),
02439                         newrootdbpath, strerror(errno));
02440     }
02441     if (newrootdbpath)  free((void *)newrootdbpath);
02442     if (rootdbpath)     free((void *)rootdbpath);
02443 
02444     return rc;
02445 }

Generated at Mon May 21 08:53:40 2001 for rpm by doxygen1.2.6 written by Dimitri van Heesch, © 1997-2001