607 lines
13 KiB
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;
|
|
}
|