rpm/tools/rpmcache.c

607 lines
13 KiB
C

/**
* \file tools/rpmcache.c
*/
#include "system.h"
#include <fnmatch.h>
#include <fts.h>
#include <rpmcli.h>
#include "rpmps.h"
#include "rpmdb.h"
#include "rpmds.h"
#include "rpmts.h"
#include "debug.h"
static int _debug = 0;
/* XXX should be flag in ts */
static int noCache = 0;
static char ** ftsSet;
const char * bhpath;
int bhpathlen = 0;
int bhlvl = -1;
struct ftsglob_s {
const char ** patterns;
int fnflags;
};
static struct ftsglob_s * bhglobs;
static int nbhglobs = 5;
static int indent = 2;
typedef struct Item_s {
const char * path;
int_32 size;
int_32 mtime;
rpmds this;
Header h;
} * Item;
static Item * items = NULL;
static int nitems = 0;
static inline Item freeItem(Item item) {
if (item != NULL) {
item->path = _free(item->path);
item->this = rpmdsFree(item->this);
item->h = headerFree(item->h);
item = _free(item);
}
return NULL;
}
static inline Item newItem(void) {
Item item = xcalloc(1, sizeof(*item));
return item;
}
static int cmpItem(const void * a, const void * b) {
Item aitem = *(Item *)a;
Item bitem = *(Item *)b;
int rc = strcmp(rpmdsN(aitem->this), rpmdsN(bitem->this));
return rc;
}
static void freeItems(void) {
int i;
for (i = 0; i < nitems; i++)
items[i] = freeItem(items[i]);
items = _free(items);
nitems = 0;
}
static int ftsCachePrint(/*@unused@*/ rpmts ts, FILE * fp)
{
int rc = 0;
int i;
if (fp == NULL) fp = stdout;
for (i = 0; i < nitems; i++) {
Item ip;
ip = items[i];
if (ip == NULL) {
rc = 1;
break;
}
fprintf(fp, "%s\n", ip->path);
}
return rc;
}
static int ftsCacheUpdate(rpmts ts)
{
HGE_t hge = (HGE_t)headerGetEntryMinMemory;
int_32 tid = rpmtsGetTid(ts);
rpmdbMatchIterator mi;
unsigned char * md5;
int rc = 0;
int i;
rc = rpmtsCloseDB(ts);
rc = rpmDefineMacro(NULL, "_dbpath %{_cache_dbpath}", RMIL_CMDLINE);
rc = rpmtsOpenDB(ts, O_RDWR);
if (rc != 0)
return rc;
for (i = 0; i < nitems; i++) {
Item ip;
ip = items[i];
if (ip == NULL) {
rc = 1;
break;
}
/* --- Check that identical package is not already cached. */
if (!hge(ip->h, RPMTAG_SIGMD5, NULL, (void **) &md5, NULL)
|| md5 == NULL)
{
rc = 1;
break;
}
mi = rpmtsInitIterator(ts, RPMTAG_SIGMD5, md5, 16);
rc = rpmdbGetIteratorCount(mi);
mi = rpmdbFreeIterator(mi);
if (rc) {
rc = 0;
continue;
}
/* --- Add cache tags to new cache header. */
rc = headerAddOrAppendEntry(ip->h, RPMTAG_CACHECTIME,
RPM_INT32_TYPE, &tid, 1);
if (rc != 1) break;
rc = headerAddOrAppendEntry(ip->h, RPMTAG_CACHEPKGPATH,
RPM_STRING_ARRAY_TYPE, &ip->path, 1);
if (rc != 1) break;
rc = headerAddOrAppendEntry(ip->h, RPMTAG_CACHEPKGSIZE,
RPM_INT32_TYPE, &ip->size, 1);
if (rc != 1) break;
rc = headerAddOrAppendEntry(ip->h, RPMTAG_CACHEPKGMTIME,
RPM_INT32_TYPE, &ip->mtime, 1);
if (rc != 1) break;
/* --- Add new cache header to database. */
rc = rpmdbAdd(rpmtsGetRdb(ts), tid, ip->h, NULL, NULL);
if (rc) break;
}
return rc;
}
/**
*/
static int archOkay(/*@null@*/ const char * pkgArch)
/*@*/
{
if (pkgArch == NULL) return 0;
return (rpmMachineScore(RPM_MACHTABLE_INSTARCH, pkgArch) ? 1 : 0);
}
/**
*/
static int osOkay(/*@null@*/ const char * pkgOs)
/*@*/
{
if (pkgOs == NULL) return 0;
return (rpmMachineScore(RPM_MACHTABLE_INSTOS, pkgOs) ? 1 : 0);
}
static int ftsStashLatest(FTSENT * fts, rpmts ts)
{
Header h = NULL;
rpmds add = NULL;
const char * arch;
const char * os;
struct stat sb, * st;
int ec = -1; /* assume not found */
int i = 0;
rpmMessage(RPMMESS_DEBUG, "============== %s\n", fts->fts_accpath);
/* Read header from file. */
{ FD_t fd = Fopen(fts->fts_accpath, "r");
rpmRC rpmrc;
int xx;
if (fd == NULL || Ferror(fd)) {
if (fd) xx = Fclose(fd);
goto exit;
}
rpmrc = rpmReadPackageFile(ts, fd, fts->fts_path, &h);
xx = Fclose(fd);
if (rpmrc != RPMRC_OK || h == NULL)
goto exit;
}
if (!headerGetEntry(h, RPMTAG_ARCH, NULL, (void **) &arch, NULL)
|| !headerGetEntry(h, RPMTAG_OS, NULL, (void **) &os, NULL))
goto exit;
/* Make sure arch and os match this platform. */
if (!archOkay(arch) || !osOkay(os)) {
ec = 0;
goto exit;
}
add = rpmdsThis(h, RPMTAG_REQUIRENAME, (RPMSENSE_EQUAL|RPMSENSE_LESS));
if (items != NULL && nitems > 0) {
Item needle = memset(alloca(sizeof(*needle)), 0, sizeof(*needle));
Item * found, * fneedle = &needle;
needle->this = add;
found = bsearch(fneedle, items, nitems, sizeof(*found), cmpItem);
/* Rewind to the first item with same name. */
while (found > items && cmpItem(found-1, fneedle) == 0)
found--;
/* Check that all saved items are newer than this item. */
if (found != NULL)
while (found < (items + nitems) && cmpItem(found, fneedle) == 0) {
ec = rpmdsCompare(needle->this, (*found)->this);
if (ec == 0) {
found++;
continue;
}
i = found - items;
break;
}
}
/*
* At this point, ec is
* -1 no item with the same name has been seen.
* 0 item exists, but already saved item EVR is newer.
* 1 item exists, but already saved item EVR is same/older.
*/
if (ec == 0) {
goto exit;
} else if (ec == 1) {
items[i] = freeItem(items[i]);
} else {
i = nitems++;
items = xrealloc(items, nitems * sizeof(*items));
}
items[i] = newItem();
items[i]->path = xstrdup(fts->fts_path);
st = fts->fts_statp;
if (st == NULL && Stat(fts->fts_accpath, &sb) == 0)
st = &sb;
if (st != NULL) {
items[i]->size = st->st_size;
items[i]->mtime = st->st_mtime;
}
st = NULL;
items[i]->this = rpmdsThis(h, RPMTAG_PROVIDENAME, RPMSENSE_EQUAL);
items[i]->h = headerLink(h);
if (nitems > 1)
qsort(items, nitems, sizeof(*items), cmpItem);
#if 0
fprintf(stderr, "\t%*s [%d] %s\n",
indent * (fts->fts_level < 0 ? 0 : fts->fts_level), "",
i, fts->fts_name);
#endif
exit:
h = headerFree(h);
add = rpmdsFree(add);
return ec;
}
static const char * ftsInfoStrings[] = {
"UNKNOWN",
"D",
"DC",
"DEFAULT",
"DNR",
"DOT",
"DP",
"ERR",
"F",
"INIT",
"NS",
"NSOK",
"SL",
"SLNONE",
"W",
};
static const char * ftsInfoStr(int fts_info) {
if (!(fts_info >= 1 && fts_info <= 14))
fts_info = 0;
return ftsInfoStrings[ fts_info ];
}
static int ftsPrint(FTS * ftsp, FTSENT * fts, rpmts ts)
{
struct ftsglob_s * bhg;
const char ** patterns;
const char * pattern;
const char * s;
int lvl;
int xx;
switch (fts->fts_info) {
case FTS_D: /* preorder directory */
if (fts->fts_pathlen < bhpathlen)
break;
/* Grab the level of the beehive top directory. */
if (bhlvl < 0) {
if (fts->fts_pathlen == bhpathlen && !strcmp(fts->fts_path, bhpath))
bhlvl = fts->fts_level;
else
break;
}
lvl = fts->fts_level - bhlvl;
if (lvl < 0)
break;
#if 0
if (_debug)
fprintf(stderr, "FTS_%s\t%*s %s\n", ftsInfoStr(fts->fts_info),
indent * (fts->fts_level < 0 ? 0 : fts->fts_level), "",
fts->fts_name);
#endif
/* Full path glob expression check. */
bhg = bhglobs;
if ((patterns = bhg->patterns) != NULL)
while ((pattern = *patterns++) != NULL) {
if (*pattern == '/')
xx = fnmatch(pattern, fts->fts_path, bhg->fnflags);
else
xx = fnmatch(pattern, fts->fts_name, bhg->fnflags);
if (xx == 0)
break;
}
/* Level specific glob expression check(s). */
if (lvl == 0 || lvl >= nbhglobs)
break;
bhg += lvl;
if ((patterns = bhg->patterns) != NULL)
while ((pattern = *patterns++) != NULL) {
if (*pattern == '/')
xx = fnmatch(pattern, fts->fts_path, bhg->fnflags);
else
xx = fnmatch(pattern, fts->fts_name, bhg->fnflags);
if (xx == 0)
break;
else
xx = Fts_set(ftsp, fts, FTS_SKIP);
}
break;
case FTS_DP: /* postorder directory */
#if 0
if (_debug)
fprintf(stderr, "FTS_%s\t%*s %s\n", ftsInfoStr(fts->fts_info),
indent * (fts->fts_level < 0 ? 0 : fts->fts_level), "",
fts->fts_name);
#endif
break;
case FTS_F: /* regular file */
#if 0
if (_debug)
fprintf(stderr, "FTS_%s\t%*s %s\n", ftsInfoStr(fts->fts_info),
indent * (fts->fts_level < 0 ? 0 : fts->fts_level), "",
fts->fts_name);
#endif
if (fts->fts_level >= 0) {
/* Ignore source packages. */
if (!strcmp(fts->fts_parent->fts_name, "SRPMS")) {
xx = Fts_set(ftsp, fts->fts_parent, FTS_SKIP);
break;
}
}
/* Ignore all but *.rpm files. */
s = fts->fts_name + fts->fts_namelen + 1 - sizeof(".rpm");
if (strcmp(s, ".rpm"))
break;
xx = ftsStashLatest(fts, ts);
break;
case FTS_NS: /* stat(2) failed */
case FTS_DNR: /* unreadable directory */
case FTS_ERR: /* error; errno is set */
if (_debug)
fprintf(stderr, "FTS_%s\t%*s %s\n", ftsInfoStr(fts->fts_info),
indent * (fts->fts_level < 0 ? 0 : fts->fts_level), "",
fts->fts_name);
break;
case FTS_DC: /* directory that causes cycles */
case FTS_DEFAULT: /* none of the above */
case FTS_DOT: /* dot or dot-dot */
case FTS_INIT: /* initialized only */
case FTS_NSOK: /* no stat(2) requested */
case FTS_SL: /* symbolic link */
case FTS_SLNONE: /* symbolic link without target */
case FTS_W: /* whiteout object */
default:
if (_debug)
fprintf(stderr, "FTS_%s\t%*s %s\n", ftsInfoStr(fts->fts_info),
indent * (fts->fts_level < 0 ? 0 : fts->fts_level), "",
fts->fts_name);
break;
}
return 0;
}
/**
* Initialize fts and glob structures.
* @param ts transaction set
* @param argv package names to match
*/
static void initGlobs(/*@unused@*/ rpmts ts, const char ** argv)
{
char buf[BUFSIZ];
int i;
buf[0] = '\0';
if (argv != NULL && * argv != NULL) {
const char * arg;
int single = (Glob_pattern_p(argv[0], 0) && argv[1] == NULL);
char * t;
t = buf;
if (!single)
t = stpcpy(t, "@(");
while ((arg = *argv++) != NULL) {
t = stpcpy(t, arg);
*t++ = '|';
}
t[-1] = (single ? '\0' : ')');
*t = '\0';
}
bhpath = rpmExpand("%{_bhpath}", NULL);
bhpathlen = strlen(bhpath);
ftsSet = xcalloc(2, sizeof(*ftsSet));
ftsSet[0] = rpmExpand("%{_bhpath}", NULL);
nbhglobs = 5;
bhglobs = xcalloc(nbhglobs, sizeof(*bhglobs));
for (i = 0; i < nbhglobs; i++) {
const char * pattern;
const char * macro;
switch (i) {
case 0:
macro = "%{_bhpath}";
break;
case 1:
macro = "%{_bhcoll}";
break;
case 2:
macro = (buf[0] == '\0' ? "%{_bhN}" : buf);
break;
case 3:
macro = "%{_bhVR}";
break;
case 4:
macro = "%{_bhA}";
break;
default:
macro = NULL;
break;
}
bhglobs[i].patterns = xcalloc(2, sizeof(*bhglobs[i].patterns));
if (macro == NULL)
continue;
pattern = rpmExpand(macro, NULL);
if (pattern == NULL || *pattern == '\0') {
pattern = _free(pattern);
continue;
}
bhglobs[i].patterns[0] = pattern;
bhglobs[i].fnflags = (FNM_PATHNAME | FNM_PERIOD | FNM_EXTMATCH);
if (bhglobs[i].patterns[0] != NULL)
rpmMessage(RPMMESS_DEBUG, "\t%d \"%s\"\n",
i, bhglobs[i].patterns[0]);
}
}
static rpmVSFlags vsflags = 0;
static struct poptOption optionsTable[] = {
{ "nolegacy", '\0', POPT_BIT_SET, &vsflags, RPMVSF_NEEDPAYLOAD,
N_("don't verify header+payload signature"), NULL },
{ "nocache", '\0', POPT_ARG_VAL, &noCache, -1,
N_("don't update cache database, only print package paths"), NULL },
{ NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmcliFtsPoptTable, 0,
N_("File tree walk options:"),
NULL },
{ NULL, '\0', POPT_ARG_INCLUDE_TABLE, rpmcliAllPoptTable, 0,
N_("Common options for all rpm modes and executables:"),
NULL },
POPT_AUTOALIAS
POPT_AUTOHELP
POPT_TABLEEND
};
int
main(int argc, char *const argv[])
{
rpmts ts = NULL;
poptContext optCon;
const char * s;
FTS * ftsp;
FTSENT * fts;
int ec = 1;
rpmRC rpmrc;
int xx;
optCon = rpmcliInit(argc, argv, optionsTable);
if (optCon == NULL)
exit(EXIT_FAILURE);
/* Configure the path to cache database, creating if necessary. */
s = rpmExpand("%{?_cache_dbpath}", NULL);
if (!(s && *s))
rpmrc = RPMRC_FAIL;
else
rpmrc = rpmMkdirPath(s, "cache_dbpath");
s = _free(s);
if (rpmrc != RPMRC_OK) {
fprintf(stderr, _("%s: %%{_cache_dbpath} macro is mis-configured.\n"),
__progname);
exit(EXIT_FAILURE);
}
ts = rpmtsCreate();
if (rpmcliQueryFlags & VERIFY_DIGEST)
vsflags |= _RPMVSF_NODIGESTS;
if (rpmcliQueryFlags & VERIFY_SIGNATURE)
vsflags |= _RPMVSF_NOSIGNATURES;
if (rpmcliQueryFlags & VERIFY_HDRCHK)
vsflags |= RPMVSF_NOHDRCHK;
(void) rpmtsSetVSFlags(ts, vsflags);
{ int_32 tid = (int_32) time(NULL);
(void) rpmtsSetTid(ts, tid);
}
initGlobs(ts, poptGetArgs(optCon));
if (ftsOpts == 0)
ftsOpts = (FTS_COMFOLLOW | FTS_LOGICAL | FTS_NOSTAT);
if (noCache)
ftsOpts |= FTS_NOSTAT;
else
ftsOpts &= ~FTS_NOSTAT;
/* Walk file tree, filter paths, save matched items. */
ftsp = Fts_open(ftsSet, ftsOpts, NULL);
while((fts = Fts_read(ftsp)) != NULL) {
xx = ftsPrint(ftsp, fts, ts);
}
xx = Fts_close(ftsp);
if (noCache)
ec = ftsCachePrint(ts, stdout);
else
ec = ftsCacheUpdate(ts);
if (ec) {
fprintf(stderr, _("%s: cache operation failed: ec %d.\n"),
__progname, ec);
}
freeItems();
ts = rpmtsFree(ts);
optCon = rpmcliFini(optCon);
return ec;
}