rpm 5.3.12
tools/rpmcache.c
Go to the documentation of this file.
00001 
00005 #include "system.h"
00006 const char *__progname;
00007 
00008 #include <fnmatch.h>
00009 #include <fts.h>
00010 
00011 #include <rpmio.h>
00012 #include <rpmiotypes.h>
00013 #include <poptIO.h>
00014 
00015 #include <rpmtypes.h>
00016 #include <rpmtag.h>
00017 #include <rpmdb.h>
00018 
00019 #include "rpmps.h"
00020 
00021 #include "misc.h"       /* XXX rpmMkdirPath */
00022 
00023 #define _RPMGI_INTERNAL
00024 #include <rpmgi.h>
00025 
00026 #include <rpmcli.h>
00027 
00028 #include "debug.h"
00029 
00030 static int _debug = 0;
00031 
00032 /* XXX should be flag in ts */
00033 static int noCache = -1;
00034 
00035 static ARGV_t ftsSet;
00036 
00037 const char * bhpath;
00038 int bhpathlen = 0;
00039 int bhlvl = -1;
00040 
00041 struct ftsglob_s {
00042     const char ** patterns;
00043     int fnflags;
00044 };
00045 
00046 static struct ftsglob_s * bhglobs;
00047 static int nbhglobs = 5;
00048 
00049 static int indent = 2;
00050 
00051 typedef struct Item_s {
00052     const char * path;
00053     uint32_t size;
00054     uint32_t mtime;
00055     rpmds this;
00056     Header h;
00057 } * Item;
00058 
00059 static Item * items = NULL;
00060 static int nitems = 0;
00061 
00062 static inline Item freeItem(Item item) {
00063     if (item != NULL) {
00064         item->path = _free(item->path);
00065         (void)rpmdsFree(item->this);
00066         item->this = NULL;
00067         (void)headerFree(item->h);
00068         item->h = NULL;
00069         item = _free(item);
00070     }
00071     return NULL;
00072 }
00073 
00074 static inline Item newItem(void) {
00075     Item item = xcalloc(1, sizeof(*item));
00076     return item;
00077 }
00078 
00079 static int cmpItem(const void * a, const void * b) {
00080     Item aitem = *(Item *)a;
00081     Item bitem = *(Item *)b;
00082     int rc = strcmp(rpmdsN(aitem->this), rpmdsN(bitem->this));
00083     return rc;
00084 }
00085 
00086 static void freeItems(void) {
00087     int i;
00088     for (i = 0; i < nitems; i++)
00089         items[i] = freeItem(items[i]);
00090     items = _free(items);
00091     nitems = 0;
00092 }
00093 
00094 static int ftsCachePrint(/*@unused@*/ rpmts ts, FILE * fp)
00095 {
00096     int rc = 0;
00097     int i;
00098 
00099     if (fp == NULL) fp = stdout;
00100     for (i = 0; i < nitems; i++) {
00101         Item ip;
00102 
00103         ip = items[i];
00104         if (ip == NULL) {
00105             rc = 1;
00106             break;
00107         }
00108 
00109         fprintf(fp, "%s\n", ip->path);
00110     }
00111     return rc;
00112 }
00113 
00114 static int ftsCacheUpdate(rpmts ts)
00115 {
00116     HE_t he = memset(alloca(sizeof(*he)), 0, sizeof(*he));
00117     uint32_t tid = rpmtsGetTid(ts);
00118     rpmmi mi;
00119     unsigned char * md5;
00120     int rc = 0;
00121     int xx;
00122     int i;
00123 
00124     rc = rpmtsCloseDB(ts);
00125     rc = rpmDefineMacro(NULL, "_dbpath %{_cache_dbpath}", RMIL_CMDLINE);
00126     rc = rpmtsOpenDB(ts, O_RDWR);
00127     if (rc != 0)
00128         return rc;
00129 
00130     for (i = 0; i < nitems; i++) {
00131         Item ip;
00132 
00133         ip = items[i];
00134         if (ip == NULL) {
00135             rc = 1;
00136             break;
00137         }
00138 
00139         /* --- Check that identical package is not already cached. */
00140         he->tag = RPMTAG_SIGMD5;
00141         xx = headerGet(ip->h, he, 0);
00142         md5 = he->p.ui8p;
00143         if (!xx || md5 == NULL) {
00144             md5 = _free(md5);
00145             rc = 1;
00146             break;
00147         }
00148         mi = rpmtsInitIterator(ts, RPMTAG_SIGMD5, md5, 16);
00149         md5 = _free(md5);
00150         rc = rpmmiCount(mi);
00151         mi = rpmmiFree(mi);
00152         if (rc) {
00153             rc = 0;
00154             continue;
00155         }
00156 
00157         /* --- Add cache tags to new cache header. */
00158         he->tag = RPMTAG_CACHECTIME;
00159         he->t = RPM_UINT32_TYPE;
00160         he->p.ui32p = &tid;
00161         he->c = 1;
00162         he->append = 1;
00163         rc = headerPut(ip->h, he, 0);
00164         he->append = 0;
00165         if (rc != 1) break;
00166 
00167         he->tag = RPMTAG_CACHEPKGPATH;
00168         he->t = RPM_STRING_ARRAY_TYPE;
00169         he->p.argv = &ip->path;
00170         he->c = 1;
00171         he->append = 1;
00172         rc = headerPut(ip->h, he, 0);
00173         he->append = 0;
00174         if (rc != 1) break;
00175 
00176         he->tag = RPMTAG_CACHEPKGSIZE;
00177         he->t = RPM_UINT32_TYPE;
00178         he->p.ui32p = &ip->size;
00179         he->c = 1;
00180         he->append = 1;
00181         rc = headerPut(ip->h, he, 0);
00182         he->append = 0;
00183         if (rc != 1) break;
00184 
00185         he->tag = RPMTAG_CACHEPKGMTIME;
00186         he->t = RPM_UINT32_TYPE;
00187         he->p.ui32p = &ip->mtime;
00188         he->c = 1;
00189         he->append = 1;
00190         rc = headerPut(ip->h, he, 0);
00191         he->append = 0;
00192         if (rc != 1) break;
00193 
00194         /* --- Add new cache header to database. */
00195         if (!(rpmtsVSFlags(ts) & RPMVSF_NOHDRCHK))
00196             rc = rpmdbAdd(rpmtsGetRdb(ts), tid, ip->h, ts);
00197         else
00198             rc = rpmdbAdd(rpmtsGetRdb(ts), tid, ip->h, NULL);
00199         if (rc) break;
00200 
00201     }
00202     xx = rpmtsCloseDB(ts);
00203     return rc;
00204 }
00205 
00206 static rpmRC cacheStashLatest(rpmgi gi, Header h)
00207 {
00208     FTSENT * fts = gi->fts;
00209     rpmds add = NULL;
00210     struct stat sb, * st;
00211     int ec = -1;        /* assume not found */
00212     int i = 0;
00213     int xx;
00214 
00215     rpmlog(RPMLOG_DEBUG, "============== %s\n", fts->fts_accpath);
00216 
00217     /* XXX DIEDIEDIE: check platform compatibility. */
00218 
00219     add = rpmdsThis(h, RPMTAG_REQUIRENAME, (RPMSENSE_EQUAL|RPMSENSE_LESS));
00220 
00221     if (items != NULL && nitems > 0) {
00222         Item needle = memset(alloca(sizeof(*needle)), 0, sizeof(*needle));
00223         Item * found, * fneedle = &needle;
00224         
00225         needle->this = add;
00226 
00227         found = bsearch(fneedle, items, nitems, sizeof(*found), cmpItem);
00228 
00229         /* Rewind to the first item with same name. */
00230         while (found > items && cmpItem(found-1, fneedle) == 0)
00231             found--;
00232 
00233         /* Check that all saved items are newer than this item. */
00234         if (found != NULL)
00235         while (found < (items + nitems) && cmpItem(found, fneedle) == 0) {
00236             ec = rpmdsCompare(needle->this, (*found)->this);
00237             if (ec == 0) {
00238                 found++;
00239                 continue;
00240             }
00241             i = found - items;
00242             break;
00243         }
00244     }
00245 
00246     /*
00247      * At this point, ec is
00248      *  -1      no item with the same name has been seen.
00249      *  0       item exists, but already saved item EVR is newer.
00250      *  1       item exists, but already saved item EVR is same/older.
00251      */
00252     if (ec == 0) {
00253         goto exit;
00254     } else if (ec == 1) {
00255         items[i] = freeItem(items[i]);
00256     } else {
00257         i = nitems++;
00258         items = xrealloc(items, nitems * sizeof(*items));
00259     }
00260 
00261     items[i] = newItem();
00262     items[i]->path = xstrdup(fts->fts_path);
00263     st = fts->fts_statp;
00264     if (st == NULL || ((long)st & 0xffff0000) == 0L) {
00265         st = &sb;
00266         memset(st, 0, sizeof(*st));
00267         xx = Stat(fts->fts_accpath, &sb);
00268     }
00269 
00270     if (st != NULL) {
00271         items[i]->size = st->st_size;
00272         items[i]->mtime = st->st_mtime;
00273     }
00274     st = NULL;
00275     items[i]->this = rpmdsThis(h, RPMTAG_PROVIDENAME, RPMSENSE_EQUAL);
00276     items[i]->h = headerLink(h);
00277 
00278     if (nitems > 1)
00279         qsort(items, nitems, sizeof(*items), cmpItem);
00280 
00281 #if 0
00282     fprintf(stderr, "\t%*s [%d] %s\n",
00283                 indent * (fts->fts_level < 0 ? 0 : fts->fts_level), "",
00284                 i, fts->fts_name);
00285 #endif
00286 
00287 exit:
00288     (void)rpmdsFree(add);
00289     add = NULL;
00290     return (ec ? RPMRC_NOTFOUND : RPMRC_OK);
00291 }
00292 
00293 static const char * ftsInfoStrings[] = {
00294     "UNKNOWN",
00295     "D",
00296     "DC",
00297     "DEFAULT",
00298     "DNR",
00299     "DOT",
00300     "DP",
00301     "ERR",
00302     "F",
00303     "INIT",
00304     "NS",
00305     "NSOK",
00306     "SL",
00307     "SLNONE",
00308     "W",
00309 };
00310 
00311 static const char * ftsInfoStr(int fts_info) {
00312     if (!(fts_info >= 1 && fts_info <= 14))
00313         fts_info = 0;
00314     return ftsInfoStrings[ fts_info ];
00315 }
00316 
00317 static rpmRC cacheWalkPathFilter(rpmgi gi)
00318 {
00319     FTS * ftsp = gi->ftsp;
00320     FTSENT * fts = gi->fts;
00321     struct ftsglob_s * bhg;
00322     const char ** patterns;
00323     const char * pattern;
00324     const char * s;
00325     int lvl;
00326     int xx;
00327 
00328     switch (fts->fts_info) {
00329     case FTS_D:         /* preorder directory */
00330         if (fts->fts_pathlen < bhpathlen)
00331             break;
00332 
00333         /* Grab the level of the beehive top directory. */
00334         if (bhlvl < 0) {
00335             if (fts->fts_pathlen == bhpathlen && !strcmp(fts->fts_path, bhpath))
00336                 bhlvl = fts->fts_level;
00337             else
00338                 break;
00339         }
00340         lvl = fts->fts_level - bhlvl;
00341 
00342         if (lvl < 0)
00343             break;
00344 
00345 #if 0
00346         if (_debug)
00347             fprintf(stderr, "FTS_%s\t%*s %s\n", ftsInfoStr(fts->fts_info),
00348                 indent * (fts->fts_level < 0 ? 0 : fts->fts_level), "",
00349                 fts->fts_name);
00350 #endif
00351 
00352         /* Full path glob expression check. */
00353         bhg = bhglobs;
00354 
00355         if ((patterns = bhg->patterns) != NULL)
00356         while ((pattern = *patterns++) != NULL) {
00357             if (*pattern == '/')
00358                 xx = fnmatch(pattern, fts->fts_path, bhg->fnflags);
00359             else
00360                 xx = fnmatch(pattern, fts->fts_name, bhg->fnflags);
00361             if (xx == 0)
00362                 break;
00363         }
00364 
00365         /* Level specific glob expression check(s). */
00366         if (lvl == 0 || lvl >= nbhglobs)
00367             break;
00368         bhg += lvl;
00369 
00370         if ((patterns = bhg->patterns) != NULL)
00371         while ((pattern = *patterns++) != NULL) {
00372             if (*pattern == '/')
00373                 xx = fnmatch(pattern, fts->fts_path, bhg->fnflags);
00374             else
00375                 xx = fnmatch(pattern, fts->fts_name, bhg->fnflags);
00376             if (xx == 0)
00377                 break;
00378             else
00379                 xx = Fts_set(ftsp, fts, FTS_SKIP);
00380         }
00381 
00382         break;
00383     case FTS_DP:        /* postorder directory */
00384 #if 0
00385         if (_debug)
00386             fprintf(stderr, "FTS_%s\t%*s %s\n", ftsInfoStr(fts->fts_info),
00387                 indent * (fts->fts_level < 0 ? 0 : fts->fts_level), "",
00388                 fts->fts_name);
00389 #endif
00390         break;
00391     case FTS_F:         /* regular file */
00392 #if 0
00393         if (_debug)
00394             fprintf(stderr, "FTS_%s\t%*s %s\n", ftsInfoStr(fts->fts_info),
00395                 indent * (fts->fts_level < 0 ? 0 : fts->fts_level), "",
00396                 fts->fts_name);
00397 #endif
00398         if (fts->fts_level >= 0) {
00399             /* Ignore source packages. */
00400             if (!strcmp(fts->fts_parent->fts_name, "SRPMS")) {
00401                 xx = Fts_set(ftsp, fts->fts_parent, FTS_SKIP);
00402                 break;
00403             }
00404         }
00405 
00406         /* Ignore all but *.rpm files. */
00407         s = fts->fts_name + fts->fts_namelen + 1 - sizeof(".rpm");
00408         if (strcmp(s, ".rpm"))
00409             break;
00410 
00411         break;
00412     case FTS_NS:        /* stat(2) failed */
00413     case FTS_DNR:       /* unreadable directory */
00414     case FTS_ERR:       /* error; errno is set */
00415         if (_debug)
00416             fprintf(stderr, "FTS_%s\t%*s %s\n", ftsInfoStr(fts->fts_info),
00417                 indent * (fts->fts_level < 0 ? 0 : fts->fts_level), "",
00418                 fts->fts_name);
00419         break;
00420     case FTS_DC:        /* directory that causes cycles */
00421     case FTS_DEFAULT:   /* none of the above */
00422     case FTS_DOT:       /* dot or dot-dot */
00423     case FTS_INIT:      /* initialized only */
00424     case FTS_NSOK:      /* no stat(2) requested */
00425     case FTS_SL:        /* symbolic link */
00426     case FTS_SLNONE:    /* symbolic link without target */
00427     case FTS_W:         /* whiteout object */
00428     default:
00429         if (_debug)
00430             fprintf(stderr, "FTS_%s\t%*s %s\n", ftsInfoStr(fts->fts_info),
00431                 indent * (fts->fts_level < 0 ? 0 : fts->fts_level), "",
00432                 fts->fts_name);
00433         break;
00434     }
00435 
00436     return RPMRC_OK;
00437 }
00438 
00444 static void initGlobs(/*@unused@*/ rpmts ts, const char ** argv)
00445 {
00446     char buf[BUFSIZ];
00447     int i;
00448 
00449     buf[0] = '\0';
00450     if (argv != NULL && * argv != NULL) {
00451         const char * arg;
00452         int single = (Glob_pattern_p(argv[0], 0) && argv[1] == NULL);
00453         char * t;
00454 
00455         t = buf;
00456         if (!single)
00457             t = stpcpy(t, "@(");
00458         while ((arg = *argv++) != NULL) {
00459             t = stpcpy(t, arg);
00460             *t++ = '|';
00461         }
00462         t[-1] = (char)(single ? '\0' : ')');
00463         *t = '\0';
00464     }
00465 
00466     bhpath = rpmExpand("%{_bhpath}", NULL);
00467     bhpathlen = strlen(bhpath);
00468 
00469     ftsSet = xcalloc(2, sizeof(*ftsSet));
00470     ftsSet[0] = rpmExpand("%{_bhpath}", NULL);
00471 
00472     nbhglobs = 5;
00473     bhglobs = xcalloc(nbhglobs, sizeof(*bhglobs));
00474     for (i = 0; i < nbhglobs; i++) {
00475         const char * pattern;
00476         const char * macro;
00477 
00478         switch (i) {
00479         case 0:
00480             macro = "%{_bhpath}";
00481             break;
00482         case 1:
00483             macro = "%{_bhcoll}";
00484             break;
00485         case 2:
00486             macro = (buf[0] == '\0' ? "%{_bhN}" : buf);
00487             break;
00488         case 3:
00489             macro = "%{_bhVR}";
00490             break;
00491         case 4:
00492             macro = "%{_bhA}";
00493             break;
00494         default:
00495             macro = NULL;
00496             break;
00497         }
00498         bhglobs[i].patterns = xcalloc(2, sizeof(*bhglobs[i].patterns));
00499         if (macro == NULL)
00500             continue;
00501         pattern = rpmExpand(macro, NULL);
00502         if (pattern == NULL || *pattern == '\0') {
00503             pattern = _free(pattern);
00504             continue;
00505         }
00506         bhglobs[i].patterns[0] = pattern;
00507         bhglobs[i].fnflags = (FNM_PATHNAME | FNM_PERIOD | FNM_EXTMATCH);
00508         if (bhglobs[i].patterns[0] != NULL)
00509             rpmlog(RPMLOG_DEBUG, "\t%d \"%s\"\n",
00510                 i, bhglobs[i].patterns[0]);
00511     }
00512 }
00513 
00514 static void freeGlobs(void)
00515 {
00516     int i;
00517     for (i = 0; i < nbhglobs; i++) {
00518         bhglobs[i].patterns[0] = _free(bhglobs[i].patterns[0]);
00519         bhglobs[i].patterns = _free(bhglobs[i].patterns);
00520     }
00521     bhglobs = _free(bhglobs);
00522     ftsSet[0] = _free(ftsSet[0]);
00523     ftsSet = _free(ftsSet);
00524 }
00525 
00526 static rpmVSFlags vsflags = 0;
00527 
00528 static struct poptOption optionsTable[] = {
00529  { "nolegacy", '\0', POPT_BIT_SET,      &vsflags, RPMVSF_NEEDPAYLOAD,
00530         N_("don't verify header+payload signature"), NULL },
00531 
00532  { "cache", '\0', POPT_ARG_VAL,   &noCache, 0,
00533         N_("update cache database"), NULL },
00534  { "nocache", '\0', POPT_ARG_VAL,   &noCache, -1,
00535         N_("don't update cache database, only print package paths"), NULL },
00536 
00537  { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmioFtsPoptTable, 0,
00538         N_("File tree walk options:"),
00539         NULL },
00540 
00541  { NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmcliAllPoptTable, 0,
00542         N_("Common options for all rpm modes and executables:"),
00543         NULL },
00544 
00545     POPT_AUTOALIAS
00546     POPT_AUTOHELP
00547     POPT_TABLEEND
00548 };
00549 
00550 int
00551 main(int argc, char *argv[])
00552 {
00553     poptContext optCon = rpmcliInit(argc, argv, optionsTable);
00554     rpmts ts = NULL;
00555     rpmgi gi = NULL;
00556     const char * s;
00557     int ec = 1;
00558     rpmRC rpmrc;
00559     int xx;
00560 
00561     if (optCon == NULL)
00562         exit(EXIT_FAILURE);
00563 
00564     /* Configure the path to cache database, creating if necessary. */
00565     s = rpmExpand("%{?_cache_dbpath}", NULL);
00566     if (!(s && *s))
00567         rpmrc = RPMRC_FAIL;
00568     else
00569         rpmrc = rpmMkdirPath(s, "cache_dbpath");
00570     if (rpmrc == RPMRC_OK && Access(s, W_OK))
00571         rpmrc = RPMRC_FAIL;
00572     s = _free(s);
00573     if (rpmrc != RPMRC_OK) {
00574         fprintf(stderr, _("%s: %%{_cache_dbpath} macro is mis-configured.\n"),
00575                 __progname);
00576         exit(EXIT_FAILURE);
00577     }
00578 
00579     ts = rpmtsCreate();
00580 
00581     if (rpmcliQueryFlags & VERIFY_DIGEST)
00582         vsflags |= _RPMVSF_NODIGESTS;
00583     if (rpmcliQueryFlags & VERIFY_SIGNATURE)
00584         vsflags |= _RPMVSF_NOSIGNATURES;
00585     if (rpmcliQueryFlags & VERIFY_HDRCHK)
00586         vsflags |= RPMVSF_NOHDRCHK;
00587     (void) rpmtsSetVSFlags(ts, vsflags);
00588 
00589     {   uint32_t tid = (uint32_t) time(NULL);
00590         (void) rpmtsSetTid(ts, tid);
00591     }
00592 
00593     initGlobs(ts, poptGetArgs(optCon));
00594 
00595     gi = rpmgiNew(ts, RPMDBI_FTSWALK, NULL, 0);
00596 
00597     if (rpmioFtsOpts == 0)
00598         rpmioFtsOpts = (FTS_COMFOLLOW | FTS_LOGICAL | FTS_NOSTAT);
00599 
00600     if (noCache)
00601         rpmioFtsOpts |= FTS_NOSTAT;
00602     else
00603         rpmioFtsOpts &= ~FTS_NOSTAT;
00604 
00605     xx = rpmgiSetArgs(gi, ftsSet, rpmioFtsOpts, giFlags);
00606 
00607     gi->walkPathFilter = cacheWalkPathFilter;
00608     gi->stash = cacheStashLatest;
00609     while ((rpmrc = rpmgiNext(gi)) == RPMRC_OK)
00610         {};
00611 
00612     if (noCache)
00613         ec = ftsCachePrint(ts, stdout);
00614     else
00615         ec = ftsCacheUpdate(ts);
00616     if (ec) {
00617         fprintf(stderr, _("%s: cache operation failed: ec %d.\n"),
00618                 __progname, ec);
00619     }
00620 
00621     freeItems();
00622     freeGlobs();
00623 
00624     gi = rpmgiFree(gi);
00625     (void)rpmtsFree(ts); 
00626     ts = NULL;
00627     optCon = rpmcliFini(optCon);
00628 
00629     return ec;
00630 }