630 lines
14 KiB
C
630 lines
14 KiB
C
/** \ingroup rpmio
|
|
* \file lib/rpmgi.c
|
|
*/
|
|
#include "system.h"
|
|
|
|
#include "rpmlib.h"
|
|
#include "rpmte.h" /* XXX rpmElementType */
|
|
|
|
#include "rpmgi_internal.h"
|
|
|
|
#include "rpmdb.h"
|
|
#include "rpmmacro.h" /* XXX rpmExpand */
|
|
#include "manifest.h"
|
|
#include "rpmerr.h"
|
|
|
|
#include "debug.h"
|
|
|
|
|
|
int _rpmgi_debug = 0;
|
|
|
|
rpmgiFlags giFlags = RPMGI_NONE;
|
|
|
|
static int indent = 2;
|
|
|
|
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 ];
|
|
}
|
|
|
|
/**
|
|
* Open a file after macro expanding path.
|
|
* @todo There are two error messages printed on header, then manifest failures.
|
|
* @param path file path
|
|
* @param fmode open mode
|
|
* @return file handle
|
|
*/
|
|
static FD_t rpmgiOpen(const char * path, const char * fmode)
|
|
{
|
|
const char * fn = rpmExpand(path, NULL);
|
|
FD_t fd = Fopen(fn, fmode);
|
|
|
|
if (fd == NULL || Ferror(fd)) {
|
|
rpmlog(RPMERR_OPEN, _("open of %s failed: %s\n"), fn, Fstrerror(fd));
|
|
if (fd != NULL) (void) Fclose(fd);
|
|
fd = NULL;
|
|
}
|
|
fn = _free(fn);
|
|
|
|
return fd;
|
|
}
|
|
|
|
/**
|
|
* Load manifest into iterator arg list.
|
|
* @param gi generalized iterator
|
|
* @param path file path
|
|
* @return RPMRC_OK on success
|
|
*/
|
|
static rpmRC rpmgiLoadManifest(rpmgi gi, const char * path)
|
|
{
|
|
FD_t fd = rpmgiOpen(path, "r.ufdio");
|
|
rpmRC rpmrc = RPMRC_FAIL;
|
|
|
|
if (fd != NULL) {
|
|
rpmrc = rpmReadPackageManifest(fd, &gi->argc, &gi->argv);
|
|
(void) Fclose(fd);
|
|
}
|
|
return rpmrc;
|
|
}
|
|
|
|
/**
|
|
* Return header from package.
|
|
* @param gi generalized iterator
|
|
* @param path file path
|
|
* @return header (NULL on failure)
|
|
*/
|
|
static Header rpmgiReadHeader(rpmgi gi, const char * path)
|
|
{
|
|
FD_t fd = rpmgiOpen(path, "r.ufdio");
|
|
Header h = NULL;
|
|
|
|
if (fd != NULL) {
|
|
/* XXX what if path needs expansion? */
|
|
rpmRC rpmrc = rpmReadPackageFile(gi->ts, fd, path, &h);
|
|
|
|
(void) Fclose(fd);
|
|
|
|
switch (rpmrc) {
|
|
case RPMRC_NOTFOUND:
|
|
/* XXX Read a package manifest. Restart ftswalk on success. */
|
|
case RPMRC_FAIL:
|
|
default:
|
|
h = headerFree(h);
|
|
break;
|
|
case RPMRC_NOTTRUSTED:
|
|
case RPMRC_NOKEY:
|
|
case RPMRC_OK:
|
|
break;
|
|
}
|
|
}
|
|
|
|
return h;
|
|
}
|
|
|
|
/**
|
|
* Read next header from package, lazily expanding manifests as found.
|
|
* @todo An empty file read as manifest truncates argv returning RPMRC_NOTFOUND.
|
|
* @todo Errors, e.g. non-existent path in manifest, will terminate iteration.
|
|
* @todo Chained manifests lose an arg someplace.
|
|
* @param gi generalized iterator
|
|
* @return RPMRC_OK on success
|
|
*/
|
|
static rpmRC rpmgiLoadReadHeader(rpmgi gi)
|
|
{
|
|
rpmRC rpmrc = RPMRC_NOTFOUND;
|
|
Header h = NULL;
|
|
|
|
if (gi->argv != NULL && gi->argv[gi->i] != NULL)
|
|
do {
|
|
const char * fn; /* XXX gi->hdrPath? */
|
|
|
|
fn = gi->argv[gi->i];
|
|
if (!(gi->flags & RPMGI_NOHEADER)) {
|
|
h = rpmgiReadHeader(gi, fn);
|
|
if (h != NULL)
|
|
rpmrc = RPMRC_OK;
|
|
} else
|
|
rpmrc = RPMRC_OK;
|
|
|
|
if (rpmrc == RPMRC_OK || gi->flags & RPMGI_NOMANIFEST)
|
|
break;
|
|
if (errno == ENOENT) {
|
|
break;
|
|
}
|
|
|
|
/* Not a header, so try for a manifest. */
|
|
gi->argv[gi->i] = NULL; /* Mark the insertion point */
|
|
rpmrc = rpmgiLoadManifest(gi, fn);
|
|
if (rpmrc != RPMRC_OK) {
|
|
gi->argv[gi->i] = fn; /* Manifest failed, restore fn */
|
|
break;
|
|
}
|
|
fn = _free(fn);
|
|
rpmrc = RPMRC_NOTFOUND;
|
|
} while (1);
|
|
|
|
if (rpmrc == RPMRC_OK && h != NULL)
|
|
gi->h = headerLink(h);
|
|
h = headerFree(h);
|
|
|
|
return rpmrc;
|
|
}
|
|
|
|
/**
|
|
* Filter file tree walk path.
|
|
* @param gi generalized iterator
|
|
* @return RPMRC_OK on success
|
|
*/
|
|
static rpmRC rpmgiWalkPathFilter(rpmgi gi)
|
|
{
|
|
FTSENT * fts = gi->fts;
|
|
rpmRC rpmrc = RPMRC_NOTFOUND;
|
|
const char * s;
|
|
|
|
if (_rpmgi_debug < 0)
|
|
rpmlog(RPMLOG_DEBUG, "FTS_%s\t%*s %s%s\n", ftsInfoStr(fts->fts_info),
|
|
indent * (fts->fts_level < 0 ? 0 : fts->fts_level), "",
|
|
fts->fts_name,
|
|
((fts->fts_info == FTS_D || fts->fts_info == FTS_DP) ? "/" : ""));
|
|
|
|
switch (fts->fts_info) {
|
|
case FTS_D: /* preorder directory */
|
|
break;
|
|
case FTS_DP: /* postorder directory */
|
|
break;
|
|
case FTS_F: /* regular file */
|
|
/* Ignore all but *.rpm files. */
|
|
s = fts->fts_name + fts->fts_namelen + 1 - sizeof(".rpm");
|
|
if (strcmp(s, ".rpm"))
|
|
break;
|
|
rpmrc = RPMRC_OK;
|
|
break;
|
|
case FTS_NS: /* stat(2) failed */
|
|
case FTS_DNR: /* unreadable directory */
|
|
case FTS_ERR: /* error; errno is set */
|
|
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:
|
|
break;
|
|
}
|
|
return rpmrc;
|
|
}
|
|
|
|
/**
|
|
* Read header from next package, lazily walking file tree.
|
|
* @param gi generalized iterator
|
|
* @return RPMRC_OK on success
|
|
*/
|
|
static rpmRC rpmgiWalkReadHeader(rpmgi gi)
|
|
{
|
|
rpmRC rpmrc = RPMRC_NOTFOUND;
|
|
|
|
if (gi->ftsp != NULL)
|
|
while ((gi->fts = Fts_read(gi->ftsp)) != NULL) {
|
|
rpmrc = rpmgiWalkPathFilter(gi);
|
|
if (rpmrc == RPMRC_OK)
|
|
break;
|
|
}
|
|
|
|
if (rpmrc == RPMRC_OK) {
|
|
Header h = NULL;
|
|
if (!(gi->flags & RPMGI_NOHEADER)) {
|
|
/* XXX rpmrc = rpmgiLoadReadHeader(gi); */
|
|
if (gi->fts != NULL) /* XXX can't happen */
|
|
h = rpmgiReadHeader(gi, gi->fts->fts_path);
|
|
}
|
|
if (h != NULL)
|
|
gi->h = headerLink(h);
|
|
h = headerFree(h);
|
|
}
|
|
|
|
return rpmrc;
|
|
}
|
|
|
|
/**
|
|
* Append globbed arg list to iterator.
|
|
* @param gi generalized iterator
|
|
* @param argv arg list to be globbed (or NULL)
|
|
* @returns RPMRC_OK on success
|
|
*/
|
|
static rpmRC rpmgiGlobArgv(rpmgi gi, ARGV_t argv)
|
|
{
|
|
const char * arg;
|
|
rpmRC rpmrc = RPMRC_OK;
|
|
int ac = 0;
|
|
int xx;
|
|
|
|
/* XXX Expand globs only if requested or for gi specific tags */
|
|
if ((gi->flags & RPMGI_NOGLOB)
|
|
|| !(gi->tag == RPMDBI_HDLIST || gi->tag == RPMDBI_ARGLIST || gi->tag == RPMDBI_FTSWALK))
|
|
{
|
|
if (argv != NULL) {
|
|
while (argv[ac] != NULL)
|
|
ac++;
|
|
/* XXX argv is not NULL */
|
|
xx = argvAppend(&gi->argv, argv);
|
|
}
|
|
gi->argc = ac;
|
|
return rpmrc;
|
|
}
|
|
|
|
if (argv != NULL)
|
|
while ((arg = *argv++) != NULL) {
|
|
ARGV_t av = NULL;
|
|
|
|
xx = rpmGlob(arg, &ac, &av);
|
|
xx = argvAppend(&gi->argv, av);
|
|
gi->argc += ac;
|
|
av = argvFree(av);
|
|
ac = 0;
|
|
}
|
|
return rpmrc;
|
|
}
|
|
|
|
/**
|
|
* Return rpmdb match iterator with filters (if any) set.
|
|
* @param gi generalized iterator
|
|
* @returns RPMRC_OK on success
|
|
*/
|
|
static rpmRC rpmgiInitFilter(rpmgi gi)
|
|
{
|
|
rpmRC rpmrc = RPMRC_OK;
|
|
ARGV_t av;
|
|
int res = 0;
|
|
|
|
gi->mi = rpmtsInitIterator(gi->ts, gi->tag, gi->keyp, gi->keylen);
|
|
|
|
if (_rpmgi_debug < 0)
|
|
fprintf(stderr, "*** gi %p\tmi %p\n", gi, gi->mi);
|
|
|
|
if (gi->argv != NULL)
|
|
for (av = (const char **) gi->argv; *av != NULL; av++) {
|
|
int tag = RPMTAG_NAME;
|
|
const char * pat;
|
|
char * a, * ae;
|
|
|
|
pat = a = xstrdup(*av);
|
|
tag = RPMTAG_NAME;
|
|
|
|
/* Parse for "tag=pattern" args. */
|
|
if ((ae = strchr(a, '=')) != NULL) {
|
|
*ae++ = '\0';
|
|
tag = rpmTagGetValue(a);
|
|
if (tag < 0) {
|
|
rpmlog(RPMERR_QUERYINFO, _("unknown tag: \"%s\"\n"), a);
|
|
res = 1;
|
|
}
|
|
pat = ae;
|
|
}
|
|
|
|
if (!res) {
|
|
if (_rpmgi_debug < 0)
|
|
fprintf(stderr, "\tav %p[%ld]: \"%s\" -> %s ~= \"%s\"\n", gi->argv, (av - gi->argv), *av, rpmTagGetName(tag), pat);
|
|
res = rpmdbSetIteratorRE(gi->mi, tag, RPMMIRE_DEFAULT, pat);
|
|
}
|
|
a = _free(a);
|
|
|
|
if (res == 0)
|
|
continue;
|
|
|
|
gi->mi = rpmdbFreeIterator(gi->mi); /* XXX odd side effect? */
|
|
rpmrc = RPMRC_FAIL;
|
|
break;
|
|
}
|
|
|
|
return rpmrc;
|
|
}
|
|
|
|
rpmgi XrpmgiUnlink(rpmgi gi, const char * msg, const char * fn, unsigned ln)
|
|
{
|
|
if (gi == NULL) return NULL;
|
|
|
|
if (_rpmgi_debug && msg != NULL)
|
|
fprintf(stderr, "--> gi %p -- %d %s at %s:%u\n", gi, gi->nrefs, msg, fn, ln);
|
|
|
|
gi->nrefs--;
|
|
return NULL;
|
|
}
|
|
|
|
rpmgi XrpmgiLink(rpmgi gi, const char * msg, const char * fn, unsigned ln)
|
|
{
|
|
if (gi == NULL) return NULL;
|
|
gi->nrefs++;
|
|
|
|
if (_rpmgi_debug && msg != NULL)
|
|
fprintf(stderr, "--> gi %p ++ %d %s at %s:%u\n", gi, gi->nrefs, msg, fn, ln);
|
|
|
|
return gi;
|
|
}
|
|
|
|
rpmgi rpmgiFree(rpmgi gi)
|
|
{
|
|
if (gi == NULL)
|
|
return NULL;
|
|
|
|
if (gi->nrefs > 1)
|
|
return rpmgiUnlink(gi, __FUNCTION__);
|
|
|
|
(void) rpmgiUnlink(gi, __FUNCTION__);
|
|
|
|
|
|
gi->hdrPath = _free(gi->hdrPath);
|
|
gi->h = headerFree(gi->h);
|
|
|
|
gi->argv = argvFree(gi->argv);
|
|
|
|
if (gi->ftsp != NULL) {
|
|
int xx;
|
|
xx = Fts_close(gi->ftsp);
|
|
gi->ftsp = NULL;
|
|
gi->fts = NULL;
|
|
}
|
|
if (gi->fd != NULL) {
|
|
(void) Fclose(gi->fd);
|
|
gi->fd = NULL;
|
|
}
|
|
gi->tsi = rpmtsiFree(gi->tsi);
|
|
gi->mi = rpmdbFreeIterator(gi->mi);
|
|
gi->ts = rpmtsFree(gi->ts);
|
|
|
|
memset(gi, 0, sizeof(*gi)); /* XXX trash and burn */
|
|
gi = _free(gi);
|
|
return NULL;
|
|
}
|
|
|
|
rpmgi rpmgiNew(rpmts ts, int tag, const void * keyp, size_t keylen)
|
|
{
|
|
rpmgi gi = xcalloc(1, sizeof(*gi));
|
|
|
|
if (gi == NULL)
|
|
return NULL;
|
|
|
|
gi->ts = rpmtsLink(ts, __FUNCTION__);
|
|
gi->tag = tag;
|
|
gi->keyp = keyp;
|
|
gi->keylen = keylen;
|
|
|
|
gi->flags = 0;
|
|
gi->active = 0;
|
|
gi->i = -1;
|
|
gi->hdrPath = NULL;
|
|
gi->h = NULL;
|
|
|
|
gi->tsi = NULL;
|
|
gi->mi = NULL;
|
|
gi->fd = NULL;
|
|
gi->argv = xcalloc(1, sizeof(*gi->argv));
|
|
gi->argc = 0;
|
|
gi->ftsOpts = 0;
|
|
gi->ftsp = NULL;
|
|
gi->fts = NULL;
|
|
|
|
gi = rpmgiLink(gi, __FUNCTION__);
|
|
|
|
return gi;
|
|
}
|
|
|
|
rpmRC rpmgiNext(rpmgi gi)
|
|
{
|
|
char hnum[32];
|
|
rpmRC rpmrc = RPMRC_NOTFOUND;
|
|
int xx;
|
|
|
|
if (gi == NULL)
|
|
return rpmrc;
|
|
|
|
/* Free header from previous iteration. */
|
|
gi->h = headerFree(gi->h);
|
|
gi->hdrPath = _free(gi->hdrPath);
|
|
hnum[0] = '\0';
|
|
|
|
if (++gi->i >= 0)
|
|
switch (gi->tag) {
|
|
default:
|
|
case RPMDBI_PACKAGES:
|
|
if (!gi->active) {
|
|
rpmrc = rpmgiInitFilter(gi);
|
|
if (rpmrc != RPMRC_OK) {
|
|
gi->mi = rpmdbFreeIterator(gi->mi); /* XXX unnecessary */
|
|
goto enditer;
|
|
}
|
|
rpmrc = RPMRC_NOTFOUND; /* XXX hack */
|
|
gi->active = 1;
|
|
}
|
|
if (gi->mi != NULL) { /* XXX unnecessary */
|
|
Header h = rpmdbNextIterator(gi->mi);
|
|
if (h != NULL) {
|
|
if (!(gi->flags & RPMGI_NOHEADER))
|
|
gi->h = headerLink(h);
|
|
sprintf(hnum, "%u", rpmdbGetIteratorOffset(gi->mi));
|
|
gi->hdrPath = rpmExpand("rpmdb h# ", hnum, NULL);
|
|
rpmrc = RPMRC_OK;
|
|
/* XXX header reference held by iterator, so no headerFree */
|
|
}
|
|
}
|
|
if (rpmrc != RPMRC_OK) {
|
|
gi->mi = rpmdbFreeIterator(gi->mi);
|
|
goto enditer;
|
|
}
|
|
break;
|
|
case RPMDBI_ADDED:
|
|
{ rpmte p;
|
|
|
|
if (!gi->active) {
|
|
gi->tsi = rpmtsiInit(gi->ts);
|
|
gi->active = 1;
|
|
}
|
|
if ((p = rpmtsiNext(gi->tsi, TR_ADDED)) != NULL) {
|
|
Header h = rpmteHeader(p);
|
|
if (h != NULL)
|
|
if (!(gi->flags & RPMGI_NOHEADER)) {
|
|
gi->h = headerLink(h);
|
|
sprintf(hnum, "%u", (unsigned)gi->i);
|
|
gi->hdrPath = rpmExpand("added h# ", hnum, NULL);
|
|
rpmrc = RPMRC_OK;
|
|
h = headerFree(h);
|
|
}
|
|
}
|
|
if (rpmrc != RPMRC_OK) {
|
|
gi->tsi = rpmtsiFree(gi->tsi);
|
|
goto enditer;
|
|
}
|
|
} break;
|
|
case RPMDBI_HDLIST:
|
|
if (!gi->active) {
|
|
const char * path = "/usr/share/comps/%{_arch}/hdlist";
|
|
gi->fd = rpmgiOpen(path, "r.ufdio");
|
|
gi->active = 1;
|
|
}
|
|
if (gi->fd != NULL) {
|
|
Header h = headerRead(gi->fd, HEADER_MAGIC_YES);
|
|
if (h != NULL) {
|
|
if (!(gi->flags & RPMGI_NOHEADER))
|
|
gi->h = headerLink(h);
|
|
sprintf(hnum, "%u", (unsigned)gi->i);
|
|
gi->hdrPath = rpmExpand("hdlist h# ", hnum, NULL);
|
|
rpmrc = RPMRC_OK;
|
|
h = headerFree(h);
|
|
}
|
|
}
|
|
if (rpmrc != RPMRC_OK) {
|
|
if (gi->fd != NULL) (void) Fclose(gi->fd);
|
|
gi->fd = NULL;
|
|
goto enditer;
|
|
}
|
|
break;
|
|
case RPMDBI_ARGLIST:
|
|
/* XXX gi->active initialize? */
|
|
if (_rpmgi_debug < 0)
|
|
fprintf(stderr, "*** gi %p\t%p[%d]: %s\n", gi, gi->argv, gi->i, gi->argv[gi->i]);
|
|
/* Read next header, lazily expanding manifests as found. */
|
|
rpmrc = rpmgiLoadReadHeader(gi);
|
|
|
|
if (rpmrc != RPMRC_OK) /* XXX check this */
|
|
goto enditer;
|
|
|
|
gi->hdrPath = xstrdup(gi->argv[gi->i]);
|
|
break;
|
|
case RPMDBI_FTSWALK:
|
|
if (gi->argv == NULL) /* HACK */
|
|
goto enditer;
|
|
|
|
if (!gi->active) {
|
|
gi->ftsp = Fts_open((char *const *)gi->argv, gi->ftsOpts, NULL);
|
|
/* XXX NULL with open(2)/malloc(3) errno set */
|
|
gi->active = 1;
|
|
}
|
|
|
|
/* Read next header, lazily walking file tree. */
|
|
rpmrc = rpmgiWalkReadHeader(gi);
|
|
|
|
if (rpmrc != RPMRC_OK) {
|
|
xx = Fts_close(gi->ftsp);
|
|
gi->ftsp = NULL;
|
|
goto enditer;
|
|
}
|
|
|
|
if (gi->fts != NULL)
|
|
gi->hdrPath = xstrdup(gi->fts->fts_path);
|
|
break;
|
|
}
|
|
|
|
if ((gi->flags & RPMGI_TSADD) && gi->h != NULL) {
|
|
/* XXX rpmgi hack: Save header in transaction element. */
|
|
xx = rpmtsAddInstallElement(gi->ts, gi->h, (fnpyKey)gi->hdrPath, 2, NULL);
|
|
}
|
|
|
|
return rpmrc;
|
|
|
|
enditer:
|
|
if (gi->flags & RPMGI_TSORDER) {
|
|
rpmts ts = gi->ts;
|
|
rpmps ps;
|
|
|
|
/* XXX installed database needs close here. */
|
|
xx = rpmtsCloseDB(ts);
|
|
xx = rpmtsSetDBMode(ts, -1); /* XXX disable lazy opens */
|
|
|
|
xx = rpmtsCheck(ts);
|
|
|
|
/* XXX query/verify will need the glop added to a buffer instead. */
|
|
ps = rpmtsProblems(ts);
|
|
if (rpmpsNumProblems(ps) > 0) {
|
|
/* XXX rpminstall will need RPMLOG_ERR */
|
|
rpmlog(RPMLOG_INFO, _("Failed dependencies:\n"));
|
|
if (rpmIsVerbose())
|
|
rpmpsPrint(NULL, ps);
|
|
|
|
|
|
if (!(rpmtsFlags(ts) & RPMTRANS_FLAG_NOSUGGEST))
|
|
rpmtsPrintSuggests(ts);
|
|
|
|
}
|
|
ps = rpmpsFree(ps);
|
|
rpmtsCleanProblems(ts);
|
|
|
|
xx = rpmtsOrder(ts);
|
|
|
|
gi->tag = RPMDBI_ADDED; /* XXX hackery */
|
|
gi->flags &= ~(RPMGI_TSADD|RPMGI_TSORDER);
|
|
|
|
}
|
|
|
|
gi->h = headerFree(gi->h);
|
|
gi->hdrPath = _free(gi->hdrPath);
|
|
gi->i = -1;
|
|
gi->active = 0;
|
|
return rpmrc;
|
|
}
|
|
|
|
const char * rpmgiHdrPath(rpmgi gi)
|
|
{
|
|
return (gi != NULL ? gi->hdrPath : NULL);
|
|
}
|
|
|
|
Header rpmgiHeader(rpmgi gi)
|
|
{
|
|
return (gi != NULL ? gi->h : NULL);
|
|
}
|
|
|
|
rpmts rpmgiTs(rpmgi gi)
|
|
{
|
|
return (gi != NULL ? gi->ts : NULL);
|
|
}
|
|
|
|
rpmRC rpmgiSetArgs(rpmgi gi, ARGV_t argv, int ftsOpts, rpmgiFlags flags)
|
|
{
|
|
gi->ftsOpts = ftsOpts;
|
|
gi->flags = flags;
|
|
return rpmgiGlobArgv(gi, argv);
|
|
}
|
|
|