Add macro %isu_package to generate ISU Package
[platform/upstream/rpm.git] / lib / verify.c
index 17022eb..3dcec10 100644 (file)
@@ -5,50 +5,64 @@
 
 #include "system.h"
 
-#include <rpmcli.h>
+#include <errno.h>
+#if WITH_CAP
+#include <sys/capability.h>
+#endif
+#if WITH_ACL
+#include <acl/libacl.h>
+#endif
 
-#include "psm.h"
-#include "rpmfi.h"
-#include "rpmts.h"
+#include <rpm/rpmcli.h>
+#include <rpm/header.h>
+#include <rpm/rpmlog.h>
+#include <rpm/rpmfi.h>
+#include <rpm/rpmts.h>
+#include <rpm/rpmdb.h>
+#include <rpm/rpmfileutil.h>
+
+#include "lib/misc.h"
+#include "lib/rpmchroot.h"
+#include "lib/rpmte_internal.h"        /* rpmteProcess() */
+#include "lib/rpmug.h"
 
-#include "legacy.h"    /* XXX domd5() */
-#include "ugid.h"      /* uidToUname(), gnameToGid */
 #include "debug.h"
 
 #define S_ISDEV(m) (S_ISBLK((m)) || S_ISCHR((m)))
 
-extern int _rpmds_unspecified_epoch_noise;
-
+/* If cap_compare() (Linux extension) not available, do it the hard way */
+#if WITH_CAP && !defined(HAVE_CAP_COMPARE)
+static int cap_compare(cap_t acap, cap_t bcap)
+{
+    int rc = 0;
+    size_t asize = cap_size(acap);
+    size_t bsize = cap_size(bcap);
+
+    if (asize != bsize) {
+       rc = 1;
+    } else {
+       char *abuf = xcalloc(asize, sizeof(*abuf));
+       char *bbuf = xcalloc(bsize, sizeof(*bbuf));
+       cap_copy_ext(abuf, acap, asize);
+       cap_copy_ext(bbuf, bcap, bsize);
+       rc = memcmp(abuf, bbuf, asize);
+       free(abuf);
+       free(bbuf);
+    }
+    return rc;
+}
+#endif
+       
 int rpmVerifyFile(const rpmts ts, const rpmfi fi,
                rpmVerifyAttrs * res, rpmVerifyAttrs omitMask)
 {
-    unsigned short fmode = rpmfiFMode(fi);
+    rpm_mode_t fmode = rpmfiFMode(fi);
     rpmfileAttrs fileAttrs = rpmfiFFlags(fi);
     rpmVerifyAttrs flags = rpmfiVFlags(fi);
     const char * fn = rpmfiFN(fi);
-    const char * rootDir = rpmtsRootDir(ts);
     struct stat sb;
     int rc;
 
-    /* Prepend the path to root (if specified). */
-    if (rootDir && *rootDir != '\0'
-     && !(rootDir[0] == '/' && rootDir[1] == '\0'))
-    {
-       int nb = strlen(fn) + strlen(rootDir) + 1;
-       char * tb = alloca(nb);
-       char * t;
-
-       t = tb;
-       *t = '\0';
-       t = stpcpy(t, rootDir);
-       while (t > tb && t[-1] == '/') {
-           --t;
-           *t = '\0';
-       }
-       t = stpcpy(t, fn);
-       fn = tb;
-    }
-
     *res = RPMVERIFY_NONE;
 
     /*
@@ -56,84 +70,84 @@ int rpmVerifyFile(const rpmts ts, const rpmfi fi,
      */
     switch (rpmfiFState(fi)) {
     case RPMFILE_STATE_NETSHARED:
-    case RPMFILE_STATE_REPLACED:
     case RPMFILE_STATE_NOTINSTALLED:
-    case RPMFILE_STATE_WRONGCOLOR:
        return 0;
        break;
+    case RPMFILE_STATE_REPLACED:
+       /* For replaced files we can only verify if it exists at all */
+       flags = RPMVERIFY_LSTATFAIL;
+       break;
+    case RPMFILE_STATE_WRONGCOLOR:
+       /*
+        * Files with wrong color are supposed to share some attributes
+        * with the actually installed file - verify what we can.
+        */
+       flags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE |
+                  RPMVERIFY_MTIME | RPMVERIFY_RDEV);
+       break;
     case RPMFILE_STATE_NORMAL:
+    /* File from a non-installed package, try to verify nevertheless */
+    case RPMFILE_STATE_MISSING:
        break;
     }
 
-    if (fn == NULL || Lstat(fn, &sb) != 0) {
+    if (fn == NULL || lstat(fn, &sb) != 0) {
        *res |= RPMVERIFY_LSTATFAIL;
        return 1;
     }
 
-    /*
-     * Not all attributes of non-regular files can be verified.
-     */
-    if (S_ISDIR(sb.st_mode))
-       flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | 
-                       RPMVERIFY_LINKTO);
-    else if (S_ISLNK(sb.st_mode)) {
-       flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME |
-               RPMVERIFY_MODE);
-#if CHOWN_FOLLOWS_SYMLINK
-           flags &= ~(RPMVERIFY_USER | RPMVERIFY_GROUP);
-#endif
-    }
-    else if (S_ISFIFO(sb.st_mode))
-       flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | 
-                       RPMVERIFY_LINKTO);
-    else if (S_ISCHR(sb.st_mode))
-       flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | 
-                       RPMVERIFY_LINKTO);
-    else if (S_ISBLK(sb.st_mode))
-       flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | 
-                       RPMVERIFY_LINKTO);
-    else 
+    /* Links have no mode, other types have no linkto */
+    if (S_ISLNK(sb.st_mode))
+       flags &= ~(RPMVERIFY_MODE);
+    else
        flags &= ~(RPMVERIFY_LINKTO);
 
-    /*
-     * Content checks of %ghost files are meaningless.
-     */
+    /* Not all attributes of non-regular files can be verified */
+    if (!S_ISREG(sb.st_mode))
+       flags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE |
+                  RPMVERIFY_MTIME | RPMVERIFY_CAPS);
+
+    /* Content checks of %ghost files are meaningless. */
     if (fileAttrs & RPMFILE_GHOST)
-       flags &= ~(RPMVERIFY_MD5 | RPMVERIFY_FILESIZE | RPMVERIFY_MTIME | 
-                       RPMVERIFY_LINKTO);
+       flags &= ~(RPMVERIFY_FILEDIGEST | RPMVERIFY_FILESIZE |
+                  RPMVERIFY_MTIME | RPMVERIFY_LINKTO);
 
-    /*
-     * Don't verify any features in omitMask.
-     */
+    /* Don't verify any features in omitMask. */
     flags &= ~(omitMask | RPMVERIFY_FAILURES);
 
 
-    if (flags & RPMVERIFY_MD5) {
-       unsigned char md5sum[16];
-       size_t fsize;
+    if (flags & RPMVERIFY_FILEDIGEST) {
+       const unsigned char *digest; 
+       int algo;
+       size_t diglen;
 
        /* XXX If --nomd5, then prelinked library sizes are not corrected. */
-       rc = domd5(fn, md5sum, 0, &fsize);
-       sb.st_size = fsize;
-       if (rc)
-           *res |= (RPMVERIFY_READFAIL|RPMVERIFY_MD5);
-       else {
-           const unsigned char * MD5 = rpmfiMD5(fi);
-           if (MD5 == NULL || memcmp(md5sum, MD5, sizeof(md5sum)))
-               *res |= RPMVERIFY_MD5;
-       }
+       if ((digest = rpmfiFDigest(fi, &algo, &diglen))) {
+           unsigned char fdigest[diglen];
+           rpm_loff_t fsize;
+
+           rc = rpmDoDigest(algo, fn, 0, fdigest, &fsize);
+           sb.st_size = fsize;
+           if (rc) {
+               *res |= (RPMVERIFY_READFAIL|RPMVERIFY_FILEDIGEST);
+           } else if (memcmp(fdigest, digest, diglen)) {
+               *res |= RPMVERIFY_FILEDIGEST;
+           }
+       } else {
+           *res |= RPMVERIFY_FILEDIGEST;
+       } 
     } 
 
     if (flags & RPMVERIFY_LINKTO) {
        char linkto[1024+1];
        int size = 0;
 
-       if ((size = Readlink(fn, linkto, sizeof(linkto)-1)) == -1)
+       if ((size = readlink(fn, linkto, sizeof(linkto)-1)) == -1)
            *res |= (RPMVERIFY_READLINKFAIL|RPMVERIFY_LINKTO);
        else {
            const char * flink = rpmfiFLink(fi);
            linkto[size] = '\0';
-           if (flink == NULL || strcmp(linkto, flink))
+           if (flink == NULL || !rstreq(linkto, flink))
                *res |= RPMVERIFY_LINKTO;
        }
     } 
@@ -144,14 +158,14 @@ int rpmVerifyFile(const rpmts ts, const rpmfi fi,
     } 
 
     if (flags & RPMVERIFY_MODE) {
-       unsigned short metamode = fmode;
-       unsigned short filemode;
+       rpm_mode_t metamode = fmode;
+       rpm_mode_t filemode;
 
        /*
-        * Platforms (like AIX) where sizeof(unsigned short) != sizeof(mode_t)
-        * need the (unsigned short) cast here. 
+        * Platforms (like AIX) where sizeof(rpm_mode_t) != sizeof(mode_t)
+        * need the (rpm_mode_t) cast here. 
         */
-       filemode = (unsigned short)sb.st_mode;
+       filemode = (rpm_mode_t)sb.st_mode;
 
        /*
         * Comparing the type of %ghost files is meaningless, but perms are OK.
@@ -163,6 +177,20 @@ int rpmVerifyFile(const rpmts ts, const rpmfi fi,
 
        if (metamode != filemode)
            *res |= RPMVERIFY_MODE;
+
+#if WITH_ACL
+       /*
+        * For now, any non-default acl's on a file is a difference as rpm
+        * cannot have set them.
+        */
+       acl_t facl = acl_get_file(fn, ACL_TYPE_ACCESS);
+       if (facl) {
+           if (acl_equiv_mode(facl, NULL) == 1) {
+               *res |= RPMVERIFY_MODE;
+           }
+           acl_free(facl);
+       }
+#endif 
     }
 
     if (flags & RPMVERIFY_RDEV) {
@@ -171,29 +199,52 @@ int rpmVerifyFile(const rpmts ts, const rpmfi fi,
        {
            *res |= RPMVERIFY_RDEV;
        } else if (S_ISDEV(fmode) && S_ISDEV(sb.st_mode)) {
-           uint_16 st_rdev = (sb.st_rdev & 0xffff);
-           uint_16 frdev = (rpmfiFRdev(fi) & 0xffff);
+           rpm_rdev_t st_rdev = (sb.st_rdev & 0xffff);
+           rpm_rdev_t frdev = (rpmfiFRdev(fi) & 0xffff);
            if (st_rdev != frdev)
                *res |= RPMVERIFY_RDEV;
        } 
     }
 
-    if (flags & RPMVERIFY_MTIME) {
-       if (sb.st_mtime != rpmfiFMtime(fi))
-           *res |= RPMVERIFY_MTIME;
+#if WITH_CAP
+    if (flags & RPMVERIFY_CAPS) {
+       /*
+        * Empty capability set ("=") is not exactly the same as no
+        * capabilities at all but suffices for now... 
+        */
+       cap_t cap, fcap;
+       cap = cap_from_text(rpmfiFCaps(fi));
+       if (!cap) {
+           cap = cap_from_text("=");
+       }
+       fcap = cap_get_file(fn);
+       if (!fcap) {
+           fcap = cap_from_text("=");
+       }
+       
+       if (cap_compare(cap, fcap) != 0)
+           *res |= RPMVERIFY_CAPS;
+
+       cap_free(fcap);
+       cap_free(cap);
+    }
+#endif
+
+    if ((flags & RPMVERIFY_MTIME) && (sb.st_mtime != rpmfiFMtime(fi))) {
+       *res |= RPMVERIFY_MTIME;
     }
 
     if (flags & RPMVERIFY_USER) {
-       const char * name = uidToUname(sb.st_uid);
+       const char * name = rpmugUname(sb.st_uid);
        const char * fuser = rpmfiFUser(fi);
-       if (name == NULL || fuser == NULL || strcmp(name, fuser))
+       if (name == NULL || fuser == NULL || !rstreq(name, fuser))
            *res |= RPMVERIFY_USER;
     }
 
     if (flags & RPMVERIFY_GROUP) {
-       const char * name = gidToGname(sb.st_gid);
+       const char * name = rpmugGname(sb.st_gid);
        const char * fgroup = rpmfiFGroup(fi);
-       if (name == NULL || fgroup == NULL || strcmp(name, fgroup))
+       if (name == NULL || fgroup == NULL || !rstreq(name, fgroup))
            *res |= RPMVERIFY_GROUP;
     }
 
@@ -202,181 +253,185 @@ int rpmVerifyFile(const rpmts ts, const rpmfi fi,
 
 /**
  * Return exit code from running verify script from header.
- * @todo malloc/free/refcount handling is fishy here.
- * @param qva          parsed query/verify options
  * @param ts           transaction set
- * @param fi           file info set
- * @param scriptFd      file handle to use for stderr (or NULL)
+ * @param h            header
  * @return              0 on success
  */
-static int rpmVerifyScript(QVA_t qva, rpmts ts,
-               rpmfi fi, FD_t scriptFd)
+static int rpmVerifyScript(rpmts ts, Header h)
 {
-    rpmpsm psm = rpmpsmNew(ts, NULL, fi);
     int rc = 0;
 
-    if (psm == NULL)   /* XXX can't happen */
-       return rc;
-
-    if (scriptFd != NULL)
-       rpmtsSetScriptFd(psm->ts, scriptFd);
+    if (headerIsEntry(h, RPMTAG_VERIFYSCRIPT)) {
+       /* fake up a erasure transaction element */
+       rpmte p = rpmteNew(ts, h, TR_REMOVED, NULL, NULL);
 
-    psm->stepName = "verify";
-    psm->scriptTag = RPMTAG_VERIFYSCRIPT;
-    psm->progTag = RPMTAG_VERIFYSCRIPTPROG;
-    rc = rpmpsmStage(psm, PSM_SCRIPT);
+       if (p != NULL) {
+           rpmteSetHeader(p, h);
 
-    if (scriptFd != NULL)
-       rpmtsSetScriptFd(psm->ts, NULL);
+           rc = (rpmpsmRun(ts, p, PKG_VERIFY) != RPMRC_OK);
 
-    psm = rpmpsmFree(psm);
+           /* clean up our fake transaction bits */
+           rpmteFree(p);
+       } else {
+           rc = RPMRC_FAIL;
+       }
+    }
 
     return rc;
 }
 
+#define unknown "?"
+#define        _verify(_RPMVERIFY_F, _C, _pad) \
+       ((verifyResult & _RPMVERIFY_F) ? _C : _pad)
+#define        _verifylink(_RPMVERIFY_F, _C, _pad)     \
+       ((verifyResult & RPMVERIFY_READLINKFAIL) ? unknown : \
+        (verifyResult & _RPMVERIFY_F) ? _C : _pad)
+#define        _verifyfile(_RPMVERIFY_F, _C, _pad)     \
+       ((verifyResult & RPMVERIFY_READFAIL) ? unknown : \
+        (verifyResult & _RPMVERIFY_F) ? _C : _pad)
+char * rpmVerifyString(uint32_t verifyResult, const char *pad)
+{
+    char *fmt = NULL;
+    rasprintf(&fmt, "%s%s%s%s%s%s%s%s%s",
+               _verify(RPMVERIFY_FILESIZE, "S", pad),
+               _verify(RPMVERIFY_MODE, "M", pad),
+               _verifyfile(RPMVERIFY_FILEDIGEST, "5", pad),
+               _verify(RPMVERIFY_RDEV, "D", pad),
+               _verifylink(RPMVERIFY_LINKTO, "L", pad),
+               _verify(RPMVERIFY_USER, "U", pad),
+               _verify(RPMVERIFY_GROUP, "G", pad),
+               _verify(RPMVERIFY_MTIME, "T", pad),
+               _verify(RPMVERIFY_CAPS, "P", pad));
+               
+    return fmt;
+}
+#undef _verifyfile
+#undef _verifylink
+#undef _verify
+#undef aok
+#undef unknown
+
+char * rpmFFlagsString(uint32_t fflags, const char *pad)
+{
+    char *fmt = NULL;
+    rasprintf(&fmt, "%s%s%s%s%s%s%s%s",
+               (fflags & RPMFILE_DOC) ? "d" : pad,
+               (fflags & RPMFILE_CONFIG) ? "c" : pad,
+               (fflags & RPMFILE_SPECFILE) ? "s" : pad,
+               (fflags & RPMFILE_MISSINGOK) ? "m" : pad,
+               (fflags & RPMFILE_NOREPLACE) ? "n" : pad,
+               (fflags & RPMFILE_GHOST) ? "g" : pad,
+               (fflags & RPMFILE_LICENSE) ? "l" : pad,
+               (fflags & RPMFILE_README) ? "r" : pad);
+    return fmt;
+}
+
 /**
  * Check file info from header against what's actually installed.
- * @param qva          parsed query/verify options
  * @param ts           transaction set
- * @param fi           file info set
+ * @param h            header to verify
+ * @param omitMask     bits to disable verify checks
+ * @param ghosts       should ghosts be verified?
  * @return             0 no problems, 1 problems found
  */
-static int verifyHeader(QVA_t qva, const rpmts ts, rpmfi fi)
+static int verifyHeader(rpmts ts, Header h, rpmVerifyAttrs omitMask, int ghosts)
 {
     rpmVerifyAttrs verifyResult = 0;
-    /* FIX: union? */
-    rpmVerifyAttrs omitMask = ((qva->qva_flags & VERIFY_ATTRS) ^ VERIFY_ATTRS);
     int ec = 0;                /* assume no problems */
-    char * t, * te;
-    char buf[BUFSIZ];
-    int i;
-
-    te = t = buf;
-    *te = '\0';
-
-    fi = rpmfiLink(fi, "verifyHeader");
-    fi = rpmfiInit(fi, 0);
-    if (fi != NULL)    /* XXX lclint */
-    while ((i = rpmfiNext(fi)) >= 0) {
-       rpmfileAttrs fileAttrs;
-       int rc;
+    rpmfi fi = rpmfiNew(ts, h, RPMTAG_BASENAMES, RPMFI_FLAGS_VERIFY);
+
+    if (fi == NULL)
+       return 1;
 
-       fileAttrs = rpmfiFFlags(fi);
+    rpmfiInit(fi, 0);
+    while (rpmfiNext(fi) >= 0) {
+       rpmfileAttrs fileAttrs = rpmfiFFlags(fi);
+       char *buf = NULL, *attrFormat;
+       char ac;
+       int rc;
 
        /* If not verifying %ghost, skip ghost files. */
-       if (!(qva->qva_fflags & RPMFILE_GHOST)
-       && (fileAttrs & RPMFILE_GHOST))
+       if ((fileAttrs & RPMFILE_GHOST) && !ghosts)
            continue;
 
        rc = rpmVerifyFile(ts, fi, &verifyResult, omitMask);
+
+       /* Filter out timestamp differences of shared files */
+       if (rc == 0 && (verifyResult & RPMVERIFY_MTIME)) {
+           rpmdbMatchIterator mi;
+           mi = rpmtsInitIterator(ts, RPMDBI_BASENAMES, rpmfiFN(fi), 0);
+           if (rpmdbGetIteratorCount(mi) > 1) 
+               verifyResult &= ~RPMVERIFY_MTIME;
+           rpmdbFreeIterator(mi);
+       }
+
+       attrFormat = rpmFFlagsString(fileAttrs, "");
+       ac = rstreq(attrFormat, "") ? ' ' : attrFormat[0];
        if (rc) {
            if (!(fileAttrs & (RPMFILE_MISSINGOK|RPMFILE_GHOST)) || rpmIsVerbose()) {
-               sprintf(te, _("missing   %c %s"),
-                       ((fileAttrs & RPMFILE_CONFIG)   ? 'c' :
-                        (fileAttrs & RPMFILE_DOC)      ? 'd' :
-                        (fileAttrs & RPMFILE_GHOST)    ? 'g' :
-                        (fileAttrs & RPMFILE_LICENSE)  ? 'l' :
-                        (fileAttrs & RPMFILE_PUBKEY)   ? 'P' :
-                        (fileAttrs & RPMFILE_README)   ? 'r' : ' '), 
-                       rpmfiFN(fi));
-               te += strlen(te);
+               rasprintf(&buf, _("missing   %c %s"), ac, rpmfiFN(fi));
                if ((verifyResult & RPMVERIFY_LSTATFAIL) != 0 &&
                    errno != ENOENT) {
-                   sprintf(te, " (%s)", strerror(errno));
-                   te += strlen(te);
+                   char *app;
+                   rasprintf(&app, " (%s)", strerror(errno));
+                   rstrcat(&buf, app);
+                   free(app);
                }
                ec = rc;
            }
        } else if (verifyResult || rpmIsVerbose()) {
-           const char * size, * MD5, * link, * mtime, * mode;
-           const char * group, * user, * rdev;
-           static const char *const aok = ".";
-           static const char *const unknown = "?";
-
-           ec = 1;
+           char *verifyFormat = rpmVerifyString(verifyResult, ".");
+           rasprintf(&buf, "%s  %c %s", verifyFormat, ac, rpmfiFN(fi));
+           free(verifyFormat);
 
-#define        _verify(_RPMVERIFY_F, _C)       \
-       ((verifyResult & _RPMVERIFY_F) ? _C : aok)
-#define        _verifylink(_RPMVERIFY_F, _C)   \
-       ((verifyResult & RPMVERIFY_READLINKFAIL) ? unknown : \
-        (verifyResult & _RPMVERIFY_F) ? _C : aok)
-#define        _verifyfile(_RPMVERIFY_F, _C)   \
-       ((verifyResult & RPMVERIFY_READFAIL) ? unknown : \
-        (verifyResult & _RPMVERIFY_F) ? _C : aok)
-       
-           MD5 = _verifyfile(RPMVERIFY_MD5, "5");
-           size = _verify(RPMVERIFY_FILESIZE, "S");
-           link = _verifylink(RPMVERIFY_LINKTO, "L");
-           mtime = _verify(RPMVERIFY_MTIME, "T");
-           rdev = _verify(RPMVERIFY_RDEV, "D");
-           user = _verify(RPMVERIFY_USER, "U");
-           group = _verify(RPMVERIFY_GROUP, "G");
-           mode = _verify(RPMVERIFY_MODE, "M");
-
-#undef _verifyfile
-#undef _verifylink
-#undef _verify
-
-           sprintf(te, "%s%s%s%s%s%s%s%s %c %s",
-                       size, mode, MD5, rdev, link, user, group, mtime,
-                       ((fileAttrs & RPMFILE_CONFIG)   ? 'c' :
-                        (fileAttrs & RPMFILE_DOC)      ? 'd' :
-                        (fileAttrs & RPMFILE_GHOST)    ? 'g' :
-                        (fileAttrs & RPMFILE_LICENSE)  ? 'l' :
-                        (fileAttrs & RPMFILE_PUBKEY)   ? 'P' :
-                        (fileAttrs & RPMFILE_README)   ? 'r' : ' '), 
-                       rpmfiFN(fi));
-           te += strlen(te);
+           if (verifyResult) ec = 1;
        }
+       free(attrFormat);
 
-       if (te > t) {
-           *te++ = '\n';
-           *te = '\0';
-           rpmlog(RPMLOG_NOTICE, "%s", t);
-           te = t = buf;
-           *t = '\0';
+       if (buf) {
+           rpmlog(RPMLOG_NOTICE, "%s\n", buf);
+           buf = _free(buf);
        }
     }
-    fi = rpmfiUnlink(fi, "verifyHeader");
+    rpmfiFree(fi);
        
     return ec;
 }
 
 /**
  * Check installed package dependencies for problems.
- * @param qva          parsed query/verify options
  * @param ts           transaction set
  * @param h            header
  * @return             number of problems found (0 for no problems)
  */
-static int verifyDependencies(QVA_t qva, rpmts ts,
-               Header h)
+static int verifyDependencies(rpmts ts, Header h)
 {
     rpmps ps;
-    rpmpsi psi;
-    int rc = 0;                /* assume no problems */
-    int xx;
+    rpmte te;
+    int rc;
 
     rpmtsEmpty(ts);
     (void) rpmtsAddInstallElement(ts, h, NULL, 0, NULL);
 
-    xx = rpmtsCheck(ts);
-    ps = rpmtsProblems(ts);
-
-    psi = rpmpsInitIterator(ps);
-    if (rpmpsNumProblems(ps) > 0) {
-       char *nevra = hGetNEVRA(h, NULL);
-       rpmlog(RPMLOG_NOTICE, "Unsatisfied dependencies for %s:\n", nevra);
-       free(nevra);
-       while (rpmpsNextIterator(psi) >= 0) {
-           rpmProblem p = rpmpsGetProblem(psi);
-           rpmlog(RPMLOG_NOTICE, "\t%s\n", rpmProblemString(p));
-           rc++;       
+    (void) rpmtsCheck(ts);
+    te = rpmtsElement(ts, 0);
+    ps = rpmteProblems(te);
+    rc = rpmpsNumProblems(ps);
+
+    if (rc > 0) {
+       rpmlog(RPMLOG_NOTICE, _("Unsatisfied dependencies for %s:\n"),
+              rpmteNEVRA(te));
+       rpmpsi psi = rpmpsInitIterator(ps);
+       rpmProblem p;
+
+       while ((p = rpmpsiNext(psi)) != NULL) {
+           char * ps = rpmProblemString(p);
+           rpmlog(RPMLOG_NOTICE, "\t%s\n", ps);
+           free(ps);
        }
+       rpmpsFreeIterator(psi);
     }
-    psi = rpmpsFreeIterator(psi);
-    ps = rpmpsFree(ps);
-
+    rpmpsFree(ps);
     rpmtsEmpty(ts);
 
     return rc;
@@ -384,68 +439,72 @@ static int verifyDependencies(QVA_t qva, rpmts ts,
 
 int showVerifyPackage(QVA_t qva, rpmts ts, Header h)
 {
-    int scareMem = 1;  /* XXX only rpmVerifyScript needs now */
-    rpmfi fi;
+    rpmVerifyAttrs omitMask = ((qva->qva_flags & VERIFY_ATTRS) ^ VERIFY_ATTRS);
+    int ghosts = (qva->qva_fflags & RPMFILE_GHOST);
     int ec = 0;
     int rc;
 
-    fi = rpmfiNew(ts, h, RPMTAG_BASENAMES, scareMem);
-    if (fi != NULL) {
-
-       if (qva->qva_flags & VERIFY_DEPS) {
-           int save_noise = _rpmds_unspecified_epoch_noise;
-           if (rpmIsVerbose())
-               _rpmds_unspecified_epoch_noise = 1;
-           if ((rc = verifyDependencies(qva, ts, h)) != 0)
-               ec = rc;
-           _rpmds_unspecified_epoch_noise = save_noise;
-       }
-       if (qva->qva_flags & VERIFY_FILES) {
-           if ((rc = verifyHeader(qva, ts, fi)) != 0)
-               ec = rc;
-       }
-       if ((qva->qva_flags & VERIFY_SCRIPT)
-        && headerIsEntry(h, RPMTAG_VERIFYSCRIPT))
-       {
-           FD_t fdo = fdDup(STDOUT_FILENO);
-           if ((rc = rpmVerifyScript(qva, ts, fi, fdo)) != 0)
-               ec = rc;
-           if (fdo != NULL)
-               rc = Fclose(fdo);
-       }
-
-       fi = rpmfiFree(fi);
+    if (qva->qva_flags & VERIFY_DEPS) {
+       if ((rc = verifyDependencies(ts, h)) != 0)
+           ec = rc;
+    }
+    if (qva->qva_flags & VERIFY_FILES) {
+       if ((rc = verifyHeader(ts, h, omitMask, ghosts)) != 0)
+           ec = rc;
+    }
+    if (qva->qva_flags & VERIFY_SCRIPT) {
+       if ((rc = rpmVerifyScript(ts, h)) != 0)
+           ec = rc;
     }
 
     return ec;
 }
 
-int rpmcliVerify(rpmts ts, QVA_t qva, const char ** argv)
+int rpmcliVerify(rpmts ts, QVA_t qva, char * const * argv)
 {
     rpmVSFlags vsflags, ovsflags;
     int ec = 0;
+    FD_t scriptFd = fdDup(STDOUT_FILENO);
+
+    /* 
+     * Open the DB + indices explicitly before possible chroot,
+     * otherwises BDB is going to be unhappy...
+     */
+    rpmtsOpenDB(ts, O_RDONLY);
+    rpmdbOpenAll(rpmtsGetRdb(ts));
+    if (rpmChrootSet(rpmtsRootDir(ts)) || rpmChrootIn()) {
+       ec = 1;
+       goto exit;
+    }
 
     if (qva->qva_showPackage == NULL)
         qva->qva_showPackage = showVerifyPackage;
 
-    /* XXX verify flags are inverted from query. */
     vsflags = rpmExpandNumeric("%{?_vsflags_verify}");
-    if (!(qva->qva_flags & VERIFY_DIGEST))
+    if (rpmcliQueryFlags & VERIFY_DIGEST)
        vsflags |= _RPMVSF_NODIGESTS;
-    if (!(qva->qva_flags & VERIFY_SIGNATURE))
+    if (rpmcliQueryFlags & VERIFY_SIGNATURE)
        vsflags |= _RPMVSF_NOSIGNATURES;
-    if (!(qva->qva_flags & VERIFY_HDRCHK))
+    if (rpmcliQueryFlags & VERIFY_HDRCHK)
        vsflags |= RPMVSF_NOHDRCHK;
     vsflags &= ~RPMVSF_NEEDPAYLOAD;
 
+    rpmtsSetScriptFd(ts, scriptFd);
     ovsflags = rpmtsSetVSFlags(ts, vsflags);
     ec = rpmcliArgIter(ts, qva, argv);
-    vsflags = rpmtsSetVSFlags(ts, ovsflags);
+    rpmtsSetVSFlags(ts, ovsflags);
+    rpmtsSetScriptFd(ts, NULL);
 
     if (qva->qva_showPackage == showVerifyPackage)
         qva->qva_showPackage = NULL;
 
     rpmtsEmpty(ts);
 
+    if (rpmChrootOut() || rpmChrootSet(NULL))
+       ec = 1;
+
+exit:
+    Fclose(scriptFd);
+
     return ec;
 }