summaryrefslogtreecommitdiff
path: root/lib/rpmfi.c
diff options
context:
space:
mode:
authorKim Kibum <kb0929.kim@samsung.com>2012-05-21 17:49:08 +0900
committerKim Kibum <kb0929.kim@samsung.com>2012-05-21 17:49:08 +0900
commitdec48cfa66e17ba4a7e50c92cb24b913289feb12 (patch)
treee1f48cd5cabb40a1d604b36949ff072d01267cb5 /lib/rpmfi.c
parentb7a3bffb8e0341b7e4ef69def268bca3a7f279ff (diff)
downloadrpm-dec48cfa66e17ba4a7e50c92cb24b913289feb12.tar.gz
rpm-dec48cfa66e17ba4a7e50c92cb24b913289feb12.tar.bz2
rpm-dec48cfa66e17ba4a7e50c92cb24b913289feb12.zip
Upload Tizen:Base source
Diffstat (limited to 'lib/rpmfi.c')
-rw-r--r--lib/rpmfi.c1349
1 files changed, 1349 insertions, 0 deletions
diff --git a/lib/rpmfi.c b/lib/rpmfi.c
new file mode 100644
index 0000000..f8be4c9
--- /dev/null
+++ b/lib/rpmfi.c
@@ -0,0 +1,1349 @@
+/** \ingroup rpmdep
+ * \file lib/rpmfi.c
+ * Routines to handle file info tag sets.
+ */
+
+#include "system.h"
+
+#include <rpm/rpmlog.h>
+#include <rpm/rpmts.h>
+#include <rpm/rpmfileutil.h> /* XXX rpmDoDigest */
+#include <rpm/rpmstring.h>
+#include <rpm/rpmmacro.h> /* XXX rpmCleanPath */
+#include <rpm/rpmds.h>
+
+#include "lib/rpmfi_internal.h"
+#include "lib/rpmte_internal.h" /* relocations */
+#include "lib/cpio.h" /* XXX CPIO_FOO */
+#include "lib/fsm.h" /* XXX newFSM() */
+
+#include "debug.h"
+
+/*
+ * Simple and stupid string "cache."
+ * Store each unique string just once, retrieve by index value.
+ * For data where number of unique names is typically very low,
+ * the dumb linear lookup appears to be fast enough and hash table seems
+ * like an overkill.
+ */
+struct strcache_s {
+ char **uniq;
+ scidx_t num;
+};
+
+static struct strcache_s _ugcache = { NULL, 0 };
+static strcache ugcache = &_ugcache;
+static struct strcache_s _langcache = { NULL, 0 };
+static strcache langcache = &_langcache;
+
+static scidx_t strcachePut(strcache cache, const char *str)
+{
+ int found = 0;
+ scidx_t ret;
+
+ for (scidx_t i = 0; i < cache->num; i++) {
+ if (rstreq(str, cache->uniq[i])) {
+ ret = i;
+ found = 1;
+ break;
+ }
+ }
+ if (!found) {
+ /* blow up on index wraparound */
+ assert((scidx_t)(cache->num + 1) > cache->num);
+ cache->uniq = xrealloc(cache->uniq,
+ sizeof(cache->uniq) * (cache->num+1));
+ cache->uniq[cache->num] = xstrdup(str);
+ ret = cache->num;
+ cache->num++;
+ }
+ return ret;
+}
+
+static const char *strcacheGet(strcache cache, scidx_t idx)
+{
+ const char *name = NULL;
+ if (idx >= 0 && idx < cache->num && cache->uniq != NULL)
+ name = cache->uniq[idx];
+ return name;
+}
+
+static strcache strcacheNew(void)
+{
+ strcache cache = xcalloc(1, sizeof(*cache));
+ return cache;
+}
+
+static strcache strcacheFree(strcache cache)
+{
+ if (cache != NULL) {
+ for (scidx_t i = 0; i < cache->num; i++) {
+ free(cache->uniq[i]);
+ }
+ cache->uniq = _free(cache->uniq);
+ free(cache);
+ }
+ return NULL;
+}
+
+static rpmfi rpmfiUnlink(rpmfi fi)
+{
+ if (fi)
+ fi->nrefs--;
+ return NULL;
+}
+
+rpmfi rpmfiLink(rpmfi fi)
+{
+ if (fi)
+ fi->nrefs++;
+ return fi;
+}
+
+rpm_count_t rpmfiFC(rpmfi fi)
+{
+ return (fi != NULL ? fi->fc : 0);
+}
+
+rpm_count_t rpmfiDC(rpmfi fi)
+{
+ return (fi != NULL ? fi->dc : 0);
+}
+
+#ifdef NOTYET
+int rpmfiDI(rpmfi fi)
+{
+}
+#endif
+
+int rpmfiFX(rpmfi fi)
+{
+ return (fi != NULL ? fi->i : -1);
+}
+
+int rpmfiSetFX(rpmfi fi, int fx)
+{
+ int i = -1;
+
+ if (fi != NULL && fx >= 0 && fx < fi->fc) {
+ i = fi->i;
+ fi->i = fx;
+ fi->j = fi->dil[fi->i];
+ }
+ return i;
+}
+
+int rpmfiDX(rpmfi fi)
+{
+ return (fi != NULL ? fi->j : -1);
+}
+
+int rpmfiSetDX(rpmfi fi, int dx)
+{
+ int j = -1;
+
+ if (fi != NULL && dx >= 0 && dx < fi->dc) {
+ j = fi->j;
+ fi->j = dx;
+ }
+ return j;
+}
+
+int rpmfiDIIndex(rpmfi fi, int dx)
+{
+ int j = -1;
+ if (fi != NULL && dx >= 0 && dx < fi->fc) {
+ if (fi->dil != NULL)
+ j = fi->dil[dx];
+ }
+ return j;
+}
+
+const char * rpmfiBNIndex(rpmfi fi, int ix)
+{
+ const char * BN = NULL;
+
+ if (fi != NULL && ix >= 0 && ix < fi->fc) {
+ if (fi->bnl != NULL)
+ BN = fi->bnl[ix];
+ }
+ return BN;
+}
+
+const char * rpmfiDNIndex(rpmfi fi, int jx)
+{
+ const char * DN = NULL;
+
+ if (fi != NULL && jx >= 0 && jx < fi->dc) {
+ if (fi->dnl != NULL)
+ DN = fi->dnl[jx];
+ }
+ return DN;
+}
+
+const char * rpmfiFNIndex(rpmfi fi, int ix)
+{
+ const char * FN = "";
+
+ if (fi != NULL && ix >= 0 && ix < fi->fc) {
+ char * t;
+ if (fi->fn == NULL) {
+ size_t dnlmax = 0, bnlmax = 0, len;
+ for (int i = 0; i < fi->dc; i++) {
+ if ((len = strlen(fi->dnl[i])) > dnlmax)
+ dnlmax = len;
+ }
+ for (int i = 0; i < fi->fc; i++) {
+ if ((len = strlen(fi->bnl[i])) > bnlmax)
+ bnlmax = len;
+ }
+ fi->fn = xmalloc(dnlmax + bnlmax + 1);
+ }
+ FN = t = fi->fn;
+ *t = '\0';
+ t = stpcpy(t, fi->dnl[fi->dil[ix]]);
+ t = stpcpy(t, fi->bnl[ix]);
+ }
+ return FN;
+}
+
+rpmfileAttrs rpmfiFFlagsIndex(rpmfi fi, int ix)
+{
+ rpmfileAttrs FFlags = 0;
+
+ if (fi != NULL && ix >= 0 && ix < fi->fc) {
+ if (fi->fflags != NULL)
+ FFlags = fi->fflags[ix];
+ }
+ return FFlags;
+}
+
+rpmVerifyAttrs rpmfiVFlagsIndex(rpmfi fi, int ix)
+{
+ rpmVerifyAttrs VFlags = 0;
+
+ if (fi != NULL && ix >= 0 && ix < fi->fc) {
+ if (fi->vflags != NULL)
+ VFlags = fi->vflags[ix];
+ }
+ return VFlags;
+}
+
+rpm_mode_t rpmfiFModeIndex(rpmfi fi, int ix)
+{
+ rpm_mode_t fmode = 0;
+
+ if (fi != NULL && ix >= 0 && ix < fi->fc) {
+ if (fi->fmodes != NULL)
+ fmode = fi->fmodes[ix];
+ }
+ return fmode;
+}
+
+rpmfileState rpmfiFStateIndex(rpmfi fi, int ix)
+{
+ rpmfileState fstate = RPMFILE_STATE_MISSING;
+
+ if (fi != NULL && ix >= 0 && ix < fi->fc) {
+ if (fi->fstates != NULL)
+ fstate = fi->fstates[ix];
+ }
+ return fstate;
+}
+
+const unsigned char * rpmfiMD5(rpmfi fi)
+{
+ const unsigned char *digest;
+ int algo = 0;
+
+ digest = rpmfiFDigest(fi, &algo, NULL);
+ return (algo == PGPHASHALGO_MD5) ? digest : NULL;
+}
+
+int rpmfiDigestAlgo(rpmfi fi)
+{
+ return fi ? fi->digestalgo : 0;
+}
+
+const unsigned char * rpmfiFDigestIndex(rpmfi fi, int ix, int *algo, size_t *len)
+{
+ const unsigned char *digest = NULL;
+
+ if (fi != NULL && ix >= 0 && ix < fi->fc) {
+ size_t diglen = rpmDigestLength(fi->digestalgo);
+ if (fi->digests != NULL)
+ digest = fi->digests + (diglen * ix);
+ if (len)
+ *len = diglen;
+ if (algo)
+ *algo = fi->digestalgo;
+ }
+ return digest;
+}
+
+char * rpmfiFDigestHex(rpmfi fi, int *algo)
+{
+ size_t diglen = 0;
+ char *fdigest = NULL;
+ const unsigned char *digest = rpmfiFDigest(fi, algo, &diglen);
+ if (digest) {
+ fdigest = pgpHexStr(digest, diglen);
+ }
+ return fdigest;
+}
+
+const char * rpmfiFLinkIndex(rpmfi fi, int ix)
+{
+ const char * flink = NULL;
+
+ if (fi != NULL && ix >= 0 && ix < fi->fc) {
+ if (fi->flinks != NULL)
+ flink = strcacheGet(fi->flinkcache, fi->flinks[ix]);
+ }
+ return flink;
+}
+
+rpm_loff_t rpmfiFSizeIndex(rpmfi fi, int ix)
+{
+ rpm_loff_t fsize = 0;
+
+ if (fi != NULL && ix >= 0 && ix < fi->fc) {
+ if (fi->fsizes != NULL)
+ fsize = fi->fsizes[ix];
+ }
+ return fsize;
+}
+
+rpm_rdev_t rpmfiFRdevIndex(rpmfi fi, int ix)
+{
+ rpm_rdev_t frdev = 0;
+
+ if (fi != NULL && ix >= 0 && ix < fi->fc) {
+ if (fi->frdevs != NULL)
+ frdev = fi->frdevs[ix];
+ }
+ return frdev;
+}
+
+rpm_ino_t rpmfiFInodeIndex(rpmfi fi, int ix)
+{
+ rpm_ino_t finode = 0;
+
+ if (fi != NULL && ix >= 0 && ix < fi->fc) {
+ if (fi->finodes != NULL)
+ finode = fi->finodes[ix];
+ }
+ return finode;
+}
+
+rpm_color_t rpmfiColor(rpmfi fi)
+{
+ rpm_color_t color = 0;
+
+ if (fi != NULL && fi->fcolors != NULL) {
+ for (int i = 0; i < fi->fc; i++)
+ color |= fi->fcolors[i];
+ /* XXX ignore all but lsnibble for now. */
+ color &= 0xf;
+ }
+ return color;
+}
+
+rpm_color_t rpmfiFColorIndex(rpmfi fi, int ix)
+{
+ rpm_color_t fcolor = 0;
+
+ if (fi != NULL && ix >= 0 && ix < fi->fc) {
+ if (fi->fcolors != NULL)
+ /* XXX ignore all but lsnibble for now. */
+ fcolor = (fi->fcolors[ix] & 0x0f);
+ }
+ return fcolor;
+}
+
+const char * rpmfiFClassIndex(rpmfi fi, int ix)
+{
+ const char * fclass = NULL;
+ int cdictx;
+
+ if (fi != NULL && fi->fcdictx != NULL && ix >= 0 && ix < fi->fc) {
+ cdictx = fi->fcdictx[ix];
+ if (fi->cdict != NULL && cdictx >= 0 && cdictx < fi->ncdict)
+ fclass = fi->cdict[cdictx];
+ }
+ return fclass;
+}
+
+uint32_t rpmfiFDependsIndex(rpmfi fi, int ix, const uint32_t ** fddictp)
+{
+ int fddictx = -1;
+ int fddictn = 0;
+ const uint32_t * fddict = NULL;
+
+ if (fi != NULL && ix >= 0 && ix < fi->fc) {
+ if (fi->fddictn != NULL)
+ fddictn = fi->fddictn[ix];
+ if (fddictn > 0 && fi->fddictx != NULL)
+ fddictx = fi->fddictx[ix];
+ if (fi->ddict != NULL && fddictx >= 0 && (fddictx+fddictn) <= fi->nddict)
+ fddict = fi->ddict + fddictx;
+ }
+ if (fddictp)
+ *fddictp = fddict;
+ return fddictn;
+}
+
+uint32_t rpmfiFNlinkIndex(rpmfi fi, int ix)
+{
+ uint32_t nlink = 0;
+
+ if (fi != NULL && ix >= 0 && ix < fi->fc) {
+ /* XXX rpm-2.3.12 has not RPMTAG_FILEINODES */
+ if (fi->finodes && fi->frdevs) {
+ rpm_ino_t finode = fi->finodes[ix];
+ rpm_rdev_t frdev = fi->frdevs[ix];
+ int j;
+
+ for (j = 0; j < fi->fc; j++) {
+ if (fi->frdevs[j] == frdev && fi->finodes[j] == finode)
+ nlink++;
+ }
+ }
+ }
+ return nlink;
+}
+
+rpm_time_t rpmfiFMtimeIndex(rpmfi fi, int ix)
+{
+ rpm_time_t fmtime = 0;
+
+ if (fi != NULL && ix >= 0 && ix < fi->fc) {
+ if (fi->fmtimes != NULL)
+ fmtime = fi->fmtimes[ix];
+ }
+ return fmtime;
+}
+
+const char * rpmfiFUserIndex(rpmfi fi, int ix)
+{
+ const char * fuser = NULL;
+
+ if (fi != NULL && ix >= 0 && ix < fi->fc) {
+ if (fi->fuser != NULL)
+ fuser = strcacheGet(ugcache, fi->fuser[ix]);
+ }
+ return fuser;
+}
+
+const char * rpmfiFGroupIndex(rpmfi fi, int ix)
+{
+ const char * fgroup = NULL;
+
+ if (fi != NULL && ix >= 0 && ix < fi->fc) {
+ if (fi->fgroup != NULL)
+ fgroup = strcacheGet(ugcache, fi->fgroup[ix]);
+ }
+ return fgroup;
+}
+
+const char * rpmfiFCapsIndex(rpmfi fi, int ix)
+{
+ const char *fcaps = NULL;
+ if (fi != NULL && ix >= 0 && ix < fi->fc) {
+ fcaps = fi->fcapcache ? strcacheGet(fi->fcapcache, fi->fcaps[ix]) : "";
+ }
+ return fcaps;
+}
+
+const char * rpmfiFLangsIndex(rpmfi fi, int ix)
+{
+ const char *flangs = NULL;
+ if (fi != NULL && fi->flangs != NULL && ix >= 0 && ix < fi->fc) {
+ flangs = strcacheGet(langcache, fi->flangs[ix]);
+ }
+ return flangs;
+}
+
+struct fingerPrint_s *rpmfiFpsIndex(rpmfi fi, int ix)
+{
+ struct fingerPrint_s * fps = NULL;
+ if (fi != NULL && fi->fps != NULL && ix >= 0 && ix < fi->fc) {
+ fps = fi->fps + ix;
+ }
+ return fps;
+}
+
+int rpmfiNext(rpmfi fi)
+{
+ int i = -1;
+
+ if (fi != NULL && ++fi->i >= 0) {
+ if (fi->i < fi->fc) {
+ i = fi->i;
+ if (fi->dil != NULL)
+ fi->j = fi->dil[fi->i];
+ } else
+ fi->i = -1;
+ }
+
+ return i;
+}
+
+rpmfi rpmfiInit(rpmfi fi, int fx)
+{
+ if (fi != NULL) {
+ if (fx >= 0 && fx < fi->fc) {
+ fi->i = fx - 1;
+ fi->j = -1;
+ }
+ }
+
+ return fi;
+}
+
+int rpmfiNextD(rpmfi fi)
+{
+ int j = -1;
+
+ if (fi != NULL && ++fi->j >= 0) {
+ if (fi->j < fi->dc)
+ j = fi->j;
+ else
+ fi->j = -1;
+ }
+
+ return j;
+}
+
+rpmfi rpmfiInitD(rpmfi fi, int dx)
+{
+ if (fi != NULL) {
+ if (dx >= 0 && dx < fi->fc)
+ fi->j = dx - 1;
+ else
+ fi = NULL;
+ }
+
+ return fi;
+}
+
+/**
+ * Identify a file type.
+ * @param ft file type
+ * @return string to identify a file type
+ */
+static
+const char * ftstring (rpmFileTypes ft)
+{
+ switch (ft) {
+ case XDIR: return "directory";
+ case CDEV: return "char dev";
+ case BDEV: return "block dev";
+ case LINK: return "link";
+ case SOCK: return "sock";
+ case PIPE: return "fifo/pipe";
+ case REG: return "file";
+ default: return "unknown file type";
+ }
+}
+
+rpmFileTypes rpmfiWhatis(rpm_mode_t mode)
+{
+ if (S_ISDIR(mode)) return XDIR;
+ if (S_ISCHR(mode)) return CDEV;
+ if (S_ISBLK(mode)) return BDEV;
+ if (S_ISLNK(mode)) return LINK;
+ if (S_ISSOCK(mode)) return SOCK;
+ if (S_ISFIFO(mode)) return PIPE;
+ return REG;
+}
+
+int rpmfiCompare(const rpmfi afi, const rpmfi bfi)
+{
+ rpmFileTypes awhat = rpmfiWhatis(rpmfiFMode(afi));
+ rpmFileTypes bwhat = rpmfiWhatis(rpmfiFMode(bfi));
+
+ if ((rpmfiFFlags(afi) & RPMFILE_GHOST) ||
+ (rpmfiFFlags(bfi) & RPMFILE_GHOST)) return 0;
+
+ if (awhat != bwhat) return 1;
+
+ if (awhat == LINK) {
+ const char * alink = rpmfiFLink(afi);
+ const char * blink = rpmfiFLink(bfi);
+ if (alink == blink) return 0;
+ if (alink == NULL) return 1;
+ if (blink == NULL) return -1;
+ return strcmp(alink, blink);
+ } else if (awhat == REG) {
+ size_t adiglen, bdiglen;
+ int aalgo, balgo;
+ const unsigned char * adigest = rpmfiFDigest(afi, &aalgo, &adiglen);
+ const unsigned char * bdigest = rpmfiFDigest(bfi, &balgo, &bdiglen);
+ if (adigest == bdigest) return 0;
+ if (adigest == NULL) return 1;
+ if (bdigest == NULL) return -1;
+ /* can't meaningfully compare different hash types */
+ if (aalgo != balgo || adiglen != bdiglen) return -1;
+ return memcmp(adigest, bdigest, adiglen);
+ }
+
+ return 0;
+}
+
+rpmFileAction rpmfiDecideFate(const rpmfi ofi, rpmfi nfi, int skipMissing)
+{
+ const char * fn = rpmfiFN(nfi);
+ rpmfileAttrs newFlags = rpmfiFFlags(nfi);
+ char buffer[1024];
+ rpmFileTypes dbWhat, newWhat, diskWhat;
+ struct stat sb;
+ int save = (newFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_SAVE;
+
+ if (lstat(fn, &sb)) {
+ /*
+ * The file doesn't exist on the disk. Create it unless the new
+ * package has marked it as missingok, or allfiles is requested.
+ */
+ if (skipMissing && (newFlags & RPMFILE_MISSINGOK)) {
+ rpmlog(RPMLOG_DEBUG, "%s skipped due to missingok flag\n",
+ fn);
+ return FA_SKIP;
+ } else {
+ return FA_CREATE;
+ }
+ }
+
+ diskWhat = rpmfiWhatis((rpm_mode_t)sb.st_mode);
+ dbWhat = rpmfiWhatis(rpmfiFMode(ofi));
+ newWhat = rpmfiWhatis(rpmfiFMode(nfi));
+
+ /*
+ * RPM >= 2.3.10 shouldn't create config directories -- we'll ignore
+ * them in older packages as well.
+ */
+ if (newWhat == XDIR)
+ return FA_CREATE;
+
+ if (diskWhat != newWhat && dbWhat != REG && dbWhat != LINK)
+ return save;
+ else if (newWhat != dbWhat && diskWhat != dbWhat)
+ return save;
+ else if (dbWhat != newWhat)
+ return FA_CREATE;
+ else if (dbWhat != LINK && dbWhat != REG)
+ return FA_CREATE;
+
+ /*
+ * This order matters - we'd prefer to CREATE the file if at all
+ * possible in case something else (like the timestamp) has changed.
+ */
+ memset(buffer, 0, sizeof(buffer));
+ if (dbWhat == REG) {
+ int oalgo, nalgo;
+ size_t odiglen, ndiglen;
+ const unsigned char * odigest, * ndigest;
+ odigest = rpmfiFDigest(ofi, &oalgo, &odiglen);
+ if (diskWhat == REG) {
+ if (rpmDoDigest(oalgo, fn, 0,
+ (unsigned char *)buffer, NULL))
+ return FA_CREATE; /* assume file has been removed */
+ if (odigest && !memcmp(odigest, buffer, odiglen))
+ return FA_CREATE; /* unmodified config file, replace. */
+ }
+ ndigest = rpmfiFDigest(nfi, &nalgo, &ndiglen);
+ /* Can't compare different hash types, backup to avoid data loss */
+ if (oalgo != nalgo || odiglen != ndiglen)
+ return save;
+ if (odigest && ndigest && !memcmp(odigest, ndigest, odiglen))
+ return FA_SKIP; /* identical file, don't bother. */
+ } else /* dbWhat == LINK */ {
+ const char * oFLink, * nFLink;
+ oFLink = rpmfiFLink(ofi);
+ if (diskWhat == LINK) {
+ if (readlink(fn, buffer, sizeof(buffer) - 1) == -1)
+ return FA_CREATE; /* assume file has been removed */
+ if (oFLink && rstreq(oFLink, buffer))
+ return FA_CREATE; /* unmodified config file, replace. */
+ }
+ nFLink = rpmfiFLink(nfi);
+ if (oFLink && nFLink && rstreq(oFLink, nFLink))
+ return FA_SKIP; /* identical file, don't bother. */
+ }
+
+ /*
+ * The config file on the disk has been modified, but
+ * the ones in the two packages are different. It would
+ * be nice if RPM was smart enough to at least try and
+ * merge the difference ala CVS, but...
+ */
+ return save;
+}
+
+int rpmfiConfigConflict(const rpmfi fi)
+{
+ const char * fn = rpmfiFN(fi);
+ rpmfileAttrs flags = rpmfiFFlags(fi);
+ char buffer[1024];
+ rpmFileTypes newWhat, diskWhat;
+ struct stat sb;
+
+ if (!(flags & RPMFILE_CONFIG) || lstat(fn, &sb)) {
+ return 0;
+ }
+
+ diskWhat = rpmfiWhatis((rpm_mode_t)sb.st_mode);
+ newWhat = rpmfiWhatis(rpmfiFMode(fi));
+
+ if (newWhat != LINK && newWhat != REG)
+ return 1;
+
+ if (diskWhat != newWhat)
+ return 1;
+
+ memset(buffer, 0, sizeof(buffer));
+ if (newWhat == REG) {
+ int algo;
+ size_t diglen;
+ const unsigned char *ndigest = rpmfiFDigest(fi, &algo, &diglen);
+ if (rpmDoDigest(algo, fn, 0, (unsigned char *)buffer, NULL))
+ return 0; /* assume file has been removed */
+ if (ndigest && !memcmp(ndigest, buffer, diglen))
+ return 0; /* unmodified config file */
+ } else /* newWhat == LINK */ {
+ const char * nFLink;
+ if (readlink(fn, buffer, sizeof(buffer) - 1) == -1)
+ return 0; /* assume file has been removed */
+ nFLink = rpmfiFLink(fi);
+ if (nFLink && rstreq(nFLink, buffer))
+ return 0; /* unmodified config file */
+ }
+
+ return 1;
+}
+
+static char **duparray(char ** src, int size)
+{
+ char **dest = xmalloc((size+1) * sizeof(*dest));
+ for (int i = 0; i < size; i++) {
+ dest[i] = xstrdup(src[i]);
+ }
+ free(src);
+ return dest;
+}
+
+static int addPrefixes(Header h, rpmRelocation *relocations, int numRelocations)
+{
+ struct rpmtd_s validRelocs;
+ const char *validprefix;
+ const char ** actualRelocations;
+ int numActual = 0;
+
+ headerGet(h, RPMTAG_PREFIXES, &validRelocs, HEADERGET_MINMEM);
+ /*
+ * If no relocations are specified (usually the case), then return the
+ * original header. If there are prefixes, however, then INSTPREFIXES
+ * should be added for RPM_INSTALL_PREFIX environ variables in scriptlets,
+ * but, since relocateFileList() can be called more than once for
+ * the same header, don't bother if already present.
+ */
+ if (relocations == NULL || numRelocations == 0) {
+ if (rpmtdCount(&validRelocs) > 0) {
+ if (!headerIsEntry(h, RPMTAG_INSTPREFIXES)) {
+ rpmtdSetTag(&validRelocs, RPMTAG_INSTPREFIXES);
+ headerPut(h, &validRelocs, HEADERPUT_DEFAULT);
+ }
+ rpmtdFreeData(&validRelocs);
+ }
+ return 0;
+ }
+
+ actualRelocations = xmalloc(rpmtdCount(&validRelocs) * sizeof(*actualRelocations));
+ rpmtdInit(&validRelocs);
+ while ((validprefix = rpmtdNextString(&validRelocs))) {
+ int j;
+ for (j = 0; j < numRelocations; j++) {
+ if (relocations[j].oldPath == NULL || /* XXX can't happen */
+ !rstreq(validprefix, relocations[j].oldPath))
+ continue;
+ /* On install, a relocate to NULL means skip the path. */
+ if (relocations[j].newPath) {
+ actualRelocations[numActual] = relocations[j].newPath;
+ numActual++;
+ }
+ break;
+ }
+ if (j == numRelocations) {
+ actualRelocations[numActual] = validprefix;
+ numActual++;
+ }
+ }
+ rpmtdFreeData(&validRelocs);
+
+ if (numActual) {
+ headerPutStringArray(h, RPMTAG_INSTPREFIXES, actualRelocations, numActual);
+ }
+ actualRelocations = _free(actualRelocations);
+ return numActual;
+}
+
+static void saveRelocs(Header h, rpmtd bnames, rpmtd dnames, rpmtd dindexes)
+{
+ struct rpmtd_s td;
+ headerGet(h, RPMTAG_BASENAMES, &td, HEADERGET_MINMEM);
+ rpmtdSetTag(&td, RPMTAG_ORIGBASENAMES);
+ headerPut(h, &td, HEADERPUT_DEFAULT);
+ rpmtdFreeData(&td);
+
+ headerGet(h, RPMTAG_DIRNAMES, &td, HEADERGET_MINMEM);
+ rpmtdSetTag(&td, RPMTAG_ORIGDIRNAMES);
+ headerPut(h, &td, HEADERPUT_DEFAULT);
+ rpmtdFreeData(&td);
+
+ headerGet(h, RPMTAG_DIRINDEXES, &td, HEADERGET_MINMEM);
+ rpmtdSetTag(&td, RPMTAG_ORIGDIRINDEXES);
+ headerPut(h, &td, HEADERPUT_DEFAULT);
+ rpmtdFreeData(&td);
+
+ headerMod(h, bnames);
+ headerMod(h, dnames);
+ headerMod(h, dindexes);
+}
+
+void rpmRelocateFileList(rpmRelocation *relocations, int numRelocations,
+ rpmfs fs, Header h)
+{
+ static int _printed = 0;
+ char ** baseNames;
+ char ** dirNames;
+ uint32_t * dirIndexes;
+ rpm_count_t fileCount, dirCount;
+ int nrelocated = 0;
+ int fileAlloced = 0;
+ char * fn = NULL;
+ int haveRelocatedBase = 0;
+ size_t maxlen = 0;
+ int i, j;
+ struct rpmtd_s bnames, dnames, dindexes, fmodes;
+
+ addPrefixes(h, relocations, numRelocations);
+
+ if (!_printed) {
+ _printed = 1;
+ rpmlog(RPMLOG_DEBUG, "========== relocations\n");
+ for (i = 0; i < numRelocations; i++) {
+ if (relocations[i].oldPath == NULL) continue; /* XXX can't happen */
+ if (relocations[i].newPath == NULL)
+ rpmlog(RPMLOG_DEBUG, "%5d exclude %s\n",
+ i, relocations[i].oldPath);
+ else
+ rpmlog(RPMLOG_DEBUG, "%5d relocate %s -> %s\n",
+ i, relocations[i].oldPath, relocations[i].newPath);
+ }
+ }
+
+ for (i = 0; i < numRelocations; i++) {
+ if (relocations[i].newPath == NULL) continue;
+ size_t len = strlen(relocations[i].newPath);
+ if (len > maxlen) maxlen = len;
+ }
+
+ headerGet(h, RPMTAG_BASENAMES, &bnames, HEADERGET_MINMEM);
+ headerGet(h, RPMTAG_DIRINDEXES, &dindexes, HEADERGET_ALLOC);
+ headerGet(h, RPMTAG_DIRNAMES, &dnames, HEADERGET_MINMEM);
+ headerGet(h, RPMTAG_FILEMODES, &fmodes, HEADERGET_MINMEM);
+ /* TODO XXX ugh.. use rpmtd iterators & friends instead */
+ baseNames = bnames.data;
+ dirIndexes = dindexes.data;
+ fileCount = rpmtdCount(&bnames);
+ dirCount = rpmtdCount(&dnames);
+ /* XXX TODO: use rpmtdDup() instead */
+ dirNames = dnames.data = duparray(dnames.data, dirCount);
+ dnames.flags |= RPMTD_PTR_ALLOCED;
+
+ /*
+ * For all relocations, we go through sorted file/relocation lists
+ * backwards so that /usr/local relocations take precedence over /usr
+ * ones.
+ */
+
+ /* Relocate individual paths. */
+
+ for (i = fileCount - 1; i >= 0; i--) {
+ rpmFileTypes ft;
+ int fnlen;
+
+ size_t len = maxlen +
+ strlen(dirNames[dirIndexes[i]]) + strlen(baseNames[i]) + 1;
+ if (len >= fileAlloced) {
+ fileAlloced = len * 2;
+ fn = xrealloc(fn, fileAlloced);
+ }
+
+assert(fn != NULL); /* XXX can't happen */
+ *fn = '\0';
+ fnlen = stpcpy( stpcpy(fn, dirNames[dirIndexes[i]]), baseNames[i]) - fn;
+
+ /*
+ * See if this file path needs relocating.
+ */
+ /*
+ * XXX FIXME: Would a bsearch of the (already sorted)
+ * relocation list be a good idea?
+ */
+ for (j = numRelocations - 1; j >= 0; j--) {
+ if (relocations[j].oldPath == NULL) /* XXX can't happen */
+ continue;
+ len = !rstreq(relocations[j].oldPath, "/")
+ ? strlen(relocations[j].oldPath)
+ : 0;
+
+ if (fnlen < len)
+ continue;
+ /*
+ * Only subdirectories or complete file paths may be relocated. We
+ * don't check for '\0' as our directory names all end in '/'.
+ */
+ if (!(fn[len] == '/' || fnlen == len))
+ continue;
+
+ if (!rstreqn(relocations[j].oldPath, fn, len))
+ continue;
+ break;
+ }
+ if (j < 0) continue;
+
+ rpmtdSetIndex(&fmodes, i);
+ ft = rpmfiWhatis(rpmtdGetNumber(&fmodes));
+
+ /* On install, a relocate to NULL means skip the path. */
+ if (relocations[j].newPath == NULL) {
+ if (ft == XDIR) {
+ /* Start with the parent, looking for directory to exclude. */
+ for (j = dirIndexes[i]; j < dirCount; j++) {
+ len = strlen(dirNames[j]) - 1;
+ while (len > 0 && dirNames[j][len-1] == '/') len--;
+ if (fnlen != len)
+ continue;
+ if (!rstreqn(fn, dirNames[j], fnlen))
+ continue;
+ break;
+ }
+ }
+ rpmfsSetAction(fs, i, FA_SKIPNSTATE);
+ rpmlog(RPMLOG_DEBUG, "excluding %s %s\n",
+ ftstring(ft), fn);
+ continue;
+ }
+
+ /* Relocation on full paths only, please. */
+ if (fnlen != len) continue;
+
+ rpmlog(RPMLOG_DEBUG, "relocating %s to %s\n",
+ fn, relocations[j].newPath);
+ nrelocated++;
+
+ strcpy(fn, relocations[j].newPath);
+ { char * te = strrchr(fn, '/');
+ if (te) {
+ if (te > fn) te++; /* root is special */
+ fnlen = te - fn;
+ } else
+ te = fn + strlen(fn);
+ if (!rstreq(baseNames[i], te)) { /* basename changed too? */
+ if (!haveRelocatedBase) {
+ /* XXX TODO: use rpmtdDup() instead */
+ bnames.data = baseNames = duparray(baseNames, fileCount);
+ bnames.flags |= RPMTD_PTR_ALLOCED;
+ haveRelocatedBase = 1;
+ }
+ free(baseNames[i]);
+ baseNames[i] = xstrdup(te);
+ }
+ *te = '\0'; /* terminate new directory name */
+ }
+
+ /* Does this directory already exist in the directory list? */
+ for (j = 0; j < dirCount; j++) {
+ if (fnlen != strlen(dirNames[j]))
+ continue;
+ if (!rstreqn(fn, dirNames[j], fnlen))
+ continue;
+ break;
+ }
+
+ if (j < dirCount) {
+ dirIndexes[i] = j;
+ continue;
+ }
+
+ /* Creating new paths is a pita */
+ dirNames = dnames.data = xrealloc(dnames.data,
+ sizeof(*dirNames) * (dirCount + 1));
+
+ dirNames[dirCount] = xstrdup(fn);
+ dirIndexes[i] = dirCount;
+ dirCount++;
+ dnames.count++;
+ }
+
+ /* Finish off by relocating directories. */
+ for (i = dirCount - 1; i >= 0; i--) {
+ for (j = numRelocations - 1; j >= 0; j--) {
+
+ if (relocations[j].oldPath == NULL) /* XXX can't happen */
+ continue;
+ size_t len = !rstreq(relocations[j].oldPath, "/")
+ ? strlen(relocations[j].oldPath)
+ : 0;
+
+ if (len && !rstreqn(relocations[j].oldPath, dirNames[i], len))
+ continue;
+
+ /*
+ * Only subdirectories or complete file paths may be relocated. We
+ * don't check for '\0' as our directory names all end in '/'.
+ */
+ if (dirNames[i][len] != '/')
+ continue;
+
+ if (relocations[j].newPath) { /* Relocate the path */
+ char *t = NULL;
+ rstrscat(&t, relocations[j].newPath, (dirNames[i] + len), NULL);
+ /* Unfortunatly rpmCleanPath strips the trailing slash.. */
+ (void) rpmCleanPath(t);
+ rstrcat(&t, "/");
+
+ rpmlog(RPMLOG_DEBUG,
+ "relocating directory %s to %s\n", dirNames[i], t);
+ free(dirNames[i]);
+ dirNames[i] = t;
+ nrelocated++;
+ }
+ }
+ }
+
+ /* Save original filenames in header and replace (relocated) filenames. */
+ if (nrelocated) {
+ saveRelocs(h, &bnames, &dnames, &dindexes);
+ }
+
+ rpmtdFreeData(&bnames);
+ rpmtdFreeData(&dnames);
+ rpmtdFreeData(&dindexes);
+ rpmtdFreeData(&fmodes);
+ free(fn);
+}
+
+rpmfi rpmfiFree(rpmfi fi)
+{
+ if (fi == NULL) return NULL;
+
+ if (fi->nrefs > 1)
+ return rpmfiUnlink(fi);
+
+ if (fi->fc > 0) {
+ fi->bnl = _free(fi->bnl);
+ fi->dnl = _free(fi->dnl);
+
+ fi->flinkcache = strcacheFree(fi->flinkcache);
+ fi->flinks = _free(fi->flinks);
+ fi->flangs = _free(fi->flangs);
+ fi->digests = _free(fi->digests);
+ fi->fcapcache = strcacheFree(fi->fcapcache);
+ fi->fcaps = _free(fi->fcaps);
+
+ fi->cdict = _free(fi->cdict);
+
+ fi->fuser = _free(fi->fuser);
+ fi->fgroup = _free(fi->fgroup);
+
+ fi->fstates = _free(fi->fstates);
+ fi->fps = _free(fi->fps);
+
+ /* these point to header memory if KEEPHEADER is used, dont free */
+ if (!(fi->fiflags & RPMFI_KEEPHEADER) && fi->h == NULL) {
+ fi->fmtimes = _free(fi->fmtimes);
+ fi->fmodes = _free(fi->fmodes);
+ fi->fflags = _free(fi->fflags);
+ fi->vflags = _free(fi->vflags);
+ fi->fsizes = _free(fi->fsizes);
+ fi->frdevs = _free(fi->frdevs);
+ fi->finodes = _free(fi->finodes);
+ fi->dil = _free(fi->dil);
+
+ fi->fcolors = _free(fi->fcolors);
+ fi->fcdictx = _free(fi->fcdictx);
+ fi->ddict = _free(fi->ddict);
+ fi->fddictx = _free(fi->fddictx);
+ fi->fddictn = _free(fi->fddictn);
+
+ }
+ }
+
+ fi->fsm = freeFSM(fi->fsm);
+
+ fi->fn = _free(fi->fn);
+ fi->apath = _free(fi->apath);
+
+ fi->replacedSizes = _free(fi->replacedSizes);
+
+ fi->h = headerFree(fi->h);
+
+ (void) rpmfiUnlink(fi);
+ memset(fi, 0, sizeof(*fi)); /* XXX trash and burn */
+ fi = _free(fi);
+
+ return NULL;
+}
+
+/* Helper to push header tag data into a string cache */
+static scidx_t *cacheTag(strcache cache, Header h, rpmTag tag)
+{
+ scidx_t *idx = NULL;
+ struct rpmtd_s td;
+ if (headerGet(h, tag, &td, HEADERGET_MINMEM)) {
+ idx = xmalloc(sizeof(*idx) * rpmtdCount(&td));
+ int i = 0;
+ const char *str;
+ while ((str = rpmtdNextString(&td))) {
+ idx[i++] = strcachePut(cache, str);
+ }
+ rpmtdFreeData(&td);
+ }
+ return idx;
+}
+
+#define _hgfi(_h, _tag, _td, _flags, _data) \
+ if (headerGet((_h), (_tag), (_td), (_flags))) \
+ _data = (td.data)
+
+rpmfi rpmfiNew(const rpmts ts, Header h, rpmTagVal tagN, rpmfiFlags flags)
+{
+ rpmfi fi = NULL;
+ rpm_loff_t *asize = NULL;
+ unsigned char * t;
+ int isBuild, isSource;
+ struct rpmtd_s fdigests, digalgo;
+ struct rpmtd_s td;
+ headerGetFlags scareFlags = (flags & RPMFI_KEEPHEADER) ?
+ HEADERGET_MINMEM : HEADERGET_ALLOC;
+ headerGetFlags defFlags = HEADERGET_ALLOC;
+
+ fi = xcalloc(1, sizeof(*fi));
+ if (fi == NULL) /* XXX can't happen */
+ goto exit;
+
+ fi->magic = RPMFIMAGIC;
+ fi->i = -1;
+
+ fi->fiflags = flags;
+ fi->scareFlags = scareFlags;
+
+ if (headerGet(h, RPMTAG_LONGARCHIVESIZE, &td, HEADERGET_EXT)) {
+ asize = rpmtdGetUint64(&td);
+ }
+ /* 0 means unknown */
+ fi->archiveSize = asize ? *asize : 0;
+ rpmtdFreeData(&td);
+
+ /* Archive size is not set when this gets called from build */
+ isBuild = (asize == NULL);
+ isSource = headerIsSource(h);
+ if (isBuild) fi->fiflags |= RPMFI_ISBUILD;
+ if (isSource) fi->fiflags |= RPMFI_ISSOURCE;
+
+ _hgfi(h, RPMTAG_BASENAMES, &td, defFlags, fi->bnl);
+ fi->fc = rpmtdCount(&td);
+ if (fi->fc == 0) {
+ goto exit;
+ }
+
+ _hgfi(h, RPMTAG_DIRNAMES, &td, defFlags, fi->dnl);
+ fi->dc = rpmtdCount(&td);
+ _hgfi(h, RPMTAG_DIRINDEXES, &td, scareFlags, fi->dil);
+ if (!(flags & RPMFI_NOFILEMODES))
+ _hgfi(h, RPMTAG_FILEMODES, &td, scareFlags, fi->fmodes);
+ if (!(flags & RPMFI_NOFILEFLAGS))
+ _hgfi(h, RPMTAG_FILEFLAGS, &td, scareFlags, fi->fflags);
+ if (!(flags & RPMFI_NOFILEVERIFYFLAGS))
+ _hgfi(h, RPMTAG_FILEVERIFYFLAGS, &td, scareFlags, fi->vflags);
+ if (!(flags & RPMFI_NOFILESIZES))
+ _hgfi(h, RPMTAG_FILESIZES, &td, scareFlags, fi->fsizes);
+
+ if (!(flags & RPMFI_NOFILECOLORS))
+ _hgfi(h, RPMTAG_FILECOLORS, &td, scareFlags, fi->fcolors);
+
+ if (!(flags & RPMFI_NOFILECLASS)) {
+ _hgfi(h, RPMTAG_CLASSDICT, &td, scareFlags, fi->cdict);
+ fi->ncdict = rpmtdCount(&td);
+ _hgfi(h, RPMTAG_FILECLASS, &td, scareFlags, fi->fcdictx);
+ }
+ if (!(flags & RPMFI_NOFILEDEPS)) {
+ _hgfi(h, RPMTAG_DEPENDSDICT, &td, scareFlags, fi->ddict);
+ fi->nddict = rpmtdCount(&td);
+ _hgfi(h, RPMTAG_FILEDEPENDSX, &td, scareFlags, fi->fddictx);
+ _hgfi(h, RPMTAG_FILEDEPENDSN, &td, scareFlags, fi->fddictn);
+ }
+
+ if (!(flags & RPMFI_NOFILESTATES))
+ _hgfi(h, RPMTAG_FILESTATES, &td, defFlags, fi->fstates);
+
+ if (!(flags & RPMFI_NOFILECAPS) && headerIsEntry(h, RPMTAG_FILECAPS)) {
+ fi->fcapcache = strcacheNew();
+ fi->fcaps = cacheTag(fi->fcapcache, h, RPMTAG_FILECAPS);
+ }
+
+ if (!(flags & RPMFI_NOFILELINKTOS)) {
+ fi->flinkcache = strcacheNew();
+ fi->flinks = cacheTag(fi->flinkcache, h, RPMTAG_FILELINKTOS);
+ }
+ /* FILELANGS are only interesting when installing */
+ if ((headerGetInstance(h) == 0) && !(flags & RPMFI_NOFILELANGS))
+ fi->flangs = cacheTag(langcache, h, RPMTAG_FILELANGS);
+
+ /* See if the package has non-md5 file digests */
+ fi->digestalgo = PGPHASHALGO_MD5;
+ if (headerGet(h, RPMTAG_FILEDIGESTALGO, &digalgo, HEADERGET_MINMEM)) {
+ uint32_t *algo = rpmtdGetUint32(&digalgo);
+ /* Hmm, what to do with unknown digest algorithms? */
+ if (algo && rpmDigestLength(*algo) != 0) {
+ fi->digestalgo = *algo;
+ }
+ }
+
+ fi->digests = NULL;
+ /* grab hex digests from header and store in binary format */
+ if (!(flags & RPMFI_NOFILEDIGESTS) &&
+ headerGet(h, RPMTAG_FILEDIGESTS, &fdigests, HEADERGET_MINMEM)) {
+ const char *fdigest;
+ size_t diglen = rpmDigestLength(fi->digestalgo);
+ fi->digests = t = xmalloc(rpmtdCount(&fdigests) * diglen);
+
+ while ((fdigest = rpmtdNextString(&fdigests))) {
+ if (!(fdigest && *fdigest != '\0')) {
+ memset(t, 0, diglen);
+ t += diglen;
+ continue;
+ }
+ for (int j = 0; j < diglen; j++, t++, fdigest += 2)
+ *t = (rnibble(fdigest[0]) << 4) | rnibble(fdigest[1]);
+ }
+ rpmtdFreeData(&fdigests);
+ }
+
+ /* XXX TR_REMOVED doesn;t need fmtimes, frdevs, finodes */
+ if (!(flags & RPMFI_NOFILEMTIMES))
+ _hgfi(h, RPMTAG_FILEMTIMES, &td, scareFlags, fi->fmtimes);
+ if (!(flags & RPMFI_NOFILERDEVS))
+ _hgfi(h, RPMTAG_FILERDEVS, &td, scareFlags, fi->frdevs);
+ if (!(flags & RPMFI_NOFILEINODES))
+ _hgfi(h, RPMTAG_FILEINODES, &td, scareFlags, fi->finodes);
+
+ if (!(flags & RPMFI_NOFILEUSER))
+ fi->fuser = cacheTag(ugcache, h, RPMTAG_FILEUSERNAME);
+ if (!(flags & RPMFI_NOFILEGROUP))
+ fi->fgroup = cacheTag(ugcache, h, RPMTAG_FILEGROUPNAME);
+
+ /* lazily alloced from rpmfiFN() */
+ fi->fn = NULL;
+
+exit:
+
+ if (fi != NULL) {
+ fi->h = (fi->fiflags & RPMFI_KEEPHEADER) ? headerLink(h) : NULL;
+ }
+
+ /* FIX: rpmfi null annotations */
+ return rpmfiLink(fi);
+}
+
+void rpmfiSetFReplacedSize(rpmfi fi, rpm_loff_t newsize)
+{
+ if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
+ if (fi->replacedSizes == NULL) {
+ fi->replacedSizes = xcalloc(fi->fc, sizeof(*fi->replacedSizes));
+ }
+ /* XXX watch out, replacedSizes is not rpm_loff_t (yet) */
+ fi->replacedSizes[fi->i] = (rpm_off_t) newsize;
+ }
+}
+
+rpm_loff_t rpmfiFReplacedSize(rpmfi fi)
+{
+ rpm_loff_t rsize = 0;
+ if (fi != NULL && fi->i >= 0 && fi->i < fi->fc) {
+ if (fi->replacedSizes) {
+ rsize = fi->replacedSizes[fi->i];
+ }
+ }
+ return rsize;
+}
+
+void rpmfiFpLookup(rpmfi fi, fingerPrintCache fpc)
+{
+ if (fi->fc > 0 && fi->fps == NULL) {
+ fi->fps = xcalloc(fi->fc, sizeof(*fi->fps));
+ }
+ fpLookupList(fpc, fi->dnl, fi->bnl, fi->dil, fi->fc, fi->fps);
+}
+
+FSM_t rpmfiFSM(rpmfi fi)
+{
+ if (fi != NULL && fi->fsm == NULL) {
+ cpioMapFlags mapflags;
+ /* Figure out mapflags:
+ * - path, mode, uid and gid are used by everything
+ * - all binary packages get SBIT_CHECK set
+ * - if archive size is not known, we're only building this package,
+ * different rules apply
+ */
+ mapflags = CPIO_MAP_PATH | CPIO_MAP_MODE | CPIO_MAP_UID | CPIO_MAP_GID;
+ if (fi->fiflags & RPMFI_ISBUILD) {
+ mapflags |= CPIO_MAP_TYPE;
+ if (fi->fiflags & RPMFI_ISSOURCE) mapflags |= CPIO_FOLLOW_SYMLINKS;
+ } else {
+ if (!(fi->fiflags & RPMFI_ISSOURCE)) mapflags |= CPIO_SBIT_CHECK;
+ }
+ fi->fsm = newFSM(mapflags);
+ }
+ return (fi != NULL) ? fi->fsm : NULL;
+}
+
+/*
+ * Generate iterator accessors function wrappers, these do nothing but
+ * call the corresponding rpmfiFooIndex(fi, fi->[ij])
+ */
+
+#define RPMFI_ITERFUNC(TYPE, NAME, IXV) \
+ TYPE rpmfi ## NAME(rpmfi fi) { return rpmfi ## NAME ## Index(fi, fi ? fi->IXV : -1); }
+
+RPMFI_ITERFUNC(const char *, BN, i)
+RPMFI_ITERFUNC(const char *, DN, j)
+RPMFI_ITERFUNC(const char *, FN, i)
+RPMFI_ITERFUNC(const char *, FLink, i)
+RPMFI_ITERFUNC(const char *, FUser, i)
+RPMFI_ITERFUNC(const char *, FGroup, i)
+RPMFI_ITERFUNC(const char *, FCaps, i)
+RPMFI_ITERFUNC(const char *, FLangs, i)
+RPMFI_ITERFUNC(const char *, FClass, i)
+RPMFI_ITERFUNC(rpmfileState, FState, i)
+RPMFI_ITERFUNC(rpmfileAttrs, FFlags, i)
+RPMFI_ITERFUNC(rpmVerifyAttrs, VFlags, i)
+RPMFI_ITERFUNC(rpm_mode_t, FMode, i)
+RPMFI_ITERFUNC(rpm_rdev_t, FRdev, i)
+RPMFI_ITERFUNC(rpm_time_t, FMtime, i)
+RPMFI_ITERFUNC(rpm_ino_t, FInode, i)
+RPMFI_ITERFUNC(rpm_loff_t, FSize, i)
+RPMFI_ITERFUNC(rpm_color_t, FColor, i)
+RPMFI_ITERFUNC(uint32_t, FNlink, i)
+
+const unsigned char * rpmfiFDigest(rpmfi fi, int *algo, size_t *len)
+{
+ return rpmfiFDigestIndex(fi, fi ? fi->i : -1, algo, len);
+}
+
+uint32_t rpmfiFDepends(rpmfi fi, const uint32_t ** fddictp)
+{
+ return rpmfiFDependsIndex(fi, fi ? fi->i : -1, fddictp);
+}