diff options
Diffstat (limited to 'rpmio')
-rw-r--r-- | rpmio/Makefile.am | 13 | ||||
-rw-r--r-- | rpmio/argv.c | 28 | ||||
-rw-r--r-- | rpmio/argv.h | 8 | ||||
-rw-r--r-- | rpmio/base64.c | 2 | ||||
-rw-r--r-- | rpmio/digest.c | 82 | ||||
-rw-r--r-- | rpmio/digest.h | 5 | ||||
-rw-r--r-- | rpmio/digest_beecrypt.c | 253 | ||||
-rw-r--r-- | rpmio/digest_nss.c | 62 | ||||
-rw-r--r-- | rpmio/digest_openssl.c | 839 | ||||
-rw-r--r-- | rpmio/macro.c | 1324 | ||||
-rw-r--r-- | rpmio/rpmbase64.h | 6 | ||||
-rw-r--r-- | rpmio/rpmfileutil.c | 70 | ||||
-rw-r--r-- | rpmio/rpmfileutil.h | 4 | ||||
-rw-r--r-- | rpmio/rpmglob.c | 66 | ||||
-rw-r--r-- | rpmio/rpmio.c | 1253 | ||||
-rw-r--r-- | rpmio/rpmio.h | 1 | ||||
-rw-r--r-- | rpmio/rpmio_internal.h | 8 | ||||
-rw-r--r-- | rpmio/rpmkeyring.c | 159 | ||||
-rw-r--r-- | rpmio/rpmkeyring.h | 17 | ||||
-rw-r--r-- | rpmio/rpmlog.c | 372 | ||||
-rw-r--r-- | rpmio/rpmlog.h | 56 | ||||
-rw-r--r-- | rpmio/rpmlua.c | 56 | ||||
-rw-r--r-- | rpmio/rpmlua.h | 2 | ||||
-rw-r--r-- | rpmio/rpmmacro.h | 34 | ||||
-rw-r--r-- | rpmio/rpmpgp.c | 370 | ||||
-rw-r--r-- | rpmio/rpmpgp.h | 80 | ||||
-rw-r--r-- | rpmio/rpmsq.c | 246 | ||||
-rw-r--r-- | rpmio/rpmsq.h | 50 | ||||
-rw-r--r-- | rpmio/rpmstrpool.c | 42 | ||||
-rw-r--r-- | rpmio/rpmstrpool.h | 13 | ||||
-rw-r--r-- | rpmio/rpmsw.h | 2 | ||||
-rw-r--r-- | rpmio/rpmurl.h | 2 | ||||
-rw-r--r-- | rpmio/rpmutil.h | 5 | ||||
-rw-r--r-- | rpmio/stubs.c | 18 |
34 files changed, 3686 insertions, 1862 deletions
diff --git a/rpmio/Makefile.am b/rpmio/Makefile.am index 7b27d6153..6024ae4e2 100644 --- a/rpmio/Makefile.am +++ b/rpmio/Makefile.am @@ -1,9 +1,14 @@ # Makefile for rpm library. +include $(top_srcdir)/rpm.am +AM_CFLAGS = @RPMCFLAGS@ + AM_CPPFLAGS = -I$(top_builddir) -I$(top_srcdir) -I$(top_builddir)/include/ AM_CPPFLAGS += @WITH_NSS_INCLUDE@ AM_CPPFLAGS += @WITH_BEECRYPT_INCLUDE@ +AM_CPPFLAGS += @WITH_OPENSSL_INCLUDE@ AM_CPPFLAGS += @WITH_POPT_INCLUDE@ +AM_CPPFLAGS += $(ZSTD_CFLAGS) AM_CPPFLAGS += -I$(top_srcdir)/misc AM_CPPFLAGS += -DRPMCONFIGDIR="\"@RPMCONFIGDIR@\"" AM_CPPFLAGS += -DLOCALSTATEDIR="\"$(localstatedir)\"" @@ -21,20 +26,26 @@ librpmio_la_SOURCES = \ if WITH_BEECRYPT librpmio_la_SOURCES += digest_beecrypt.c else +if WITH_OPENSSL +librpmio_la_SOURCES += digest_openssl.c +else librpmio_la_SOURCES += digest_nss.c endif +endif -librpmio_la_LDFLAGS = -version-info 4:0:1 +librpmio_la_LDFLAGS = -version-info $(rpm_version_info) librpmio_la_LIBADD = \ ../misc/libmisc.la \ @WITH_NSS_LIB@ \ @WITH_BEECRYPT_LIB@ \ + @WITH_OPENSSL_LIB@ \ @WITH_BZ2_LIB@ \ @WITH_ZLIB_LIB@ \ @WITH_LIBELF_LIB@ \ @WITH_POPT_LIB@ \ @WITH_LZMA_LIB@ \ + $(ZSTD_LIBS) \ -lpthread if WITH_INTERNAL_BEECRYPT diff --git a/rpmio/argv.c b/rpmio/argv.c index f061f03de..3508a9fc1 100644 --- a/rpmio/argv.c +++ b/rpmio/argv.c @@ -213,12 +213,32 @@ int argvSplit(ARGV_t * argvp, const char * str, const char * seps) char *argvJoin(ARGV_const_t argv, const char *sep) { + int argc = 0; + size_t argvlen = 0; char *dest = NULL; - char * const *arg; - for (arg = argv; arg && *arg; arg++) { - rstrscat(&dest, *arg, *(arg+1) ? sep : "", NULL); - } + if (argv) { + ARGV_const_t arg; + for (arg = argv; *arg; arg++) + argvlen += strlen(*arg); + argc = arg - argv; + } + + if (argc > 0) { + size_t seplen = (sep != NULL) ? strlen(sep) : 0; + char *p; + + dest = xmalloc(argvlen + (seplen * (argc - 1)) + 1); + + p = stpcpy(dest, argv[0]); + for (int i = 1; i < argc; i++) { + if (seplen) + p = stpcpy(p, sep); + p = stpcpy(p, argv[i]); + } + *p = '\0'; + } + return dest; } diff --git a/rpmio/argv.h b/rpmio/argv.h index d73a3c35f..602b1dc20 100644 --- a/rpmio/argv.h +++ b/rpmio/argv.h @@ -3,6 +3,8 @@ /** \ingroup rpmargv * \file rpmio/argv.h + * + * Argument Manipulation API. */ #include <stdio.h> @@ -149,7 +151,7 @@ typedef rpmFlags argvFlags; /** \ingroup rpmargv * Split a string into an argv array. * @param str string arg to split - * @param seps seperator characters + * @param seps separator characters * @param flags flags to control behavior * @return argv array */ @@ -159,7 +161,7 @@ ARGV_t argvSplitString(const char * str, const char * seps, argvFlags flags); * Split a string into an argv array. * @retval *argvp argv array * @param str string arg to split - * @param seps seperator characters + * @param seps separator characters * @return 0 always */ int argvSplit(ARGV_t * argvp, const char * str, const char * seps); @@ -167,7 +169,7 @@ int argvSplit(ARGV_t * argvp, const char * str, const char * seps); /** \ingroup rpmargv * Join an argv array into a string. * @param *argv argv array to join - * @param sep seperator string to use + * @param sep separator string to use * @return malloc'ed string */ char *argvJoin(ARGV_const_t argv, const char *sep); diff --git a/rpmio/base64.c b/rpmio/base64.c index 60e67d442..4424aabbd 100644 --- a/rpmio/base64.c +++ b/rpmio/base64.c @@ -104,7 +104,7 @@ static int base64_decode_value(unsigned char value_in) { static const int decoding[] = {62,-1,-1,-1,63,52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1,-1,0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1,-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51}; value_in -= 43; - if (value_in > sizeof(decoding)/sizeof(int)) + if (value_in >= sizeof(decoding)/sizeof(int)) return -1; return decoding[value_in]; } diff --git a/rpmio/digest.c b/rpmio/digest.c index c1f73b331..1f5e1667b 100644 --- a/rpmio/digest.c +++ b/rpmio/digest.c @@ -8,14 +8,28 @@ #include "debug.h" -#define DIGESTS_MAX 11 +#define DIGESTS_MAX 12 struct rpmDigestBundle_s { - int index_min; /*!< Smallest index of active digest */ int index_max; /*!< Largest index of active digest */ off_t nbytes; /*!< Length of total input data */ - DIGEST_CTX digests[DIGESTS_MAX]; /*!< Digest contexts indexed by algo */ + DIGEST_CTX digests[DIGESTS_MAX]; /*!< Digest contexts identified by id */ + int ids[DIGESTS_MAX]; /*!< Digest ID (arbitrary non-zero) */ }; +static int findID(rpmDigestBundle bundle, int id) +{ + int ix = -1; + if (bundle) { + for (int i = 0; i < DIGESTS_MAX; i++) { + if (bundle->ids[i] == id) { + ix = i; + break; + } + } + } + return ix; +} + rpmDigestBundle rpmDigestBundleNew(void) { rpmDigestBundle bundle = xcalloc(1, sizeof(*bundle)); @@ -25,7 +39,7 @@ rpmDigestBundle rpmDigestBundleNew(void) rpmDigestBundle rpmDigestBundleFree(rpmDigestBundle bundle) { if (bundle) { - for (int i = bundle->index_min; i <= bundle->index_max ; i++) { + for (int i = 0; i <= bundle->index_max ; i++) { if (bundle->digests[i] == NULL) continue; rpmDigestFinal(bundle->digests[i], NULL, NULL, 0); @@ -40,56 +54,58 @@ rpmDigestBundle rpmDigestBundleFree(rpmDigestBundle bundle) int rpmDigestBundleAdd(rpmDigestBundle bundle, int algo, rpmDigestFlags flags) { - DIGEST_CTX ctx = NULL; - if (bundle && algo > 0 && algo < DIGESTS_MAX) { - if (bundle->digests[algo] == NULL) { - ctx = rpmDigestInit(algo, flags); - if (ctx) { - bundle->digests[algo] = ctx; - if (algo < bundle->index_min) { - bundle->index_min = algo; - } - if (algo > bundle->index_max) { - bundle->index_max = algo; - } + return rpmDigestBundleAddID(bundle, algo, algo, flags); +} + +int rpmDigestBundleAddID(rpmDigestBundle bundle, int algo, int id, + rpmDigestFlags flags) +{ + int rc = -1; + if (id > 0 && findID(bundle, id) < 0) { + int ix = findID(bundle, 0); /* Find first free slot */ + if (ix >= 0) { + bundle->digests[ix] = rpmDigestInit(algo, flags); + if (bundle->digests[ix]) { + bundle->ids[ix]= id; + if (ix > bundle->index_max) + bundle->index_max = ix; + rc = 0; } } } - return (ctx != NULL); + return rc; } - int rpmDigestBundleUpdate(rpmDigestBundle bundle, const void *data, size_t len) { int rc = 0; if (bundle && data && len > 0) { - for (int i = bundle->index_min; i <= bundle->index_max; i++) { - DIGEST_CTX ctx = bundle->digests[i]; - if (ctx == NULL) - continue; - rc += rpmDigestUpdate(ctx, data, len); + for (int i = 0; i <= bundle->index_max; i++) { + if (bundle->ids[i] > 0) + rc += rpmDigestUpdate(bundle->digests[i], data, len); } bundle->nbytes += len; } return rc; } -int rpmDigestBundleFinal(rpmDigestBundle bundle, - int algo, void ** datap, size_t * lenp, int asAscii) +int rpmDigestBundleFinal(rpmDigestBundle bundle, int id, + void ** datap, size_t * lenp, int asAscii) { int rc = 0; - if (bundle && algo >= bundle->index_min && algo <= bundle->index_max) { - rc = rpmDigestFinal(bundle->digests[algo], datap, lenp, asAscii); - bundle->digests[algo] = NULL; + int ix = findID(bundle, id); + + if (ix >= 0) { + rc = rpmDigestFinal(bundle->digests[ix], datap, lenp, asAscii); + bundle->digests[ix] = NULL; + bundle->ids[ix] = 0; } return rc; } -DIGEST_CTX rpmDigestBundleDupCtx(rpmDigestBundle bundle, int algo) +DIGEST_CTX rpmDigestBundleDupCtx(rpmDigestBundle bundle, int id) { - DIGEST_CTX dup = NULL; - if (bundle && algo >= bundle->index_min && algo <= bundle->index_max) { - dup = rpmDigestDup(bundle->digests[algo]); - } + int ix = findID(bundle, id); + DIGEST_CTX dup = (ix >= 0) ? rpmDigestDup(bundle->digests[ix]) : NULL; return dup; } diff --git a/rpmio/digest.h b/rpmio/digest.h index f10f1c871..9e0cde3b9 100644 --- a/rpmio/digest.h +++ b/rpmio/digest.h @@ -5,8 +5,7 @@ typedef struct pgpDigAlg_s * pgpDigAlg; -typedef int (*setmpifunc)(pgpDigAlg digp, - int num, const uint8_t *p, const uint8_t *pend); +typedef int (*setmpifunc)(pgpDigAlg digp, int num, const uint8_t *p); typedef int (*verifyfunc)(pgpDigAlg pgpkey, pgpDigAlg pgpsig, uint8_t *hash, size_t hashlen, int hash_algo); typedef void (*freefunc)(pgpDigAlg digp); @@ -28,7 +27,7 @@ struct pgpDigParams_s { uint8_t tag; uint8_t version; /*!< version number. */ - pgpTime_t time; /*!< time that the key was created. */ + uint32_t time; /*!< key/signature creation time. */ uint8_t pubkey_algo; /*!< public key algorithm. */ uint8_t hash_algo; diff --git a/rpmio/digest_beecrypt.c b/rpmio/digest_beecrypt.c index 7ff706034..597027e25 100644 --- a/rpmio/digest_beecrypt.c +++ b/rpmio/digest_beecrypt.c @@ -1,17 +1,17 @@ #include "system.h" -#include <beecrypt.h> -#include <dsa.h> -#include <endianness.h> -#include <md5.h> -#include <mp.h> -#include <rsa.h> -#include <rsapk.h> -#include <sha1.h> +#include <beecrypt/beecrypt.h> +#include <beecrypt/dsa.h> +#include <beecrypt/endianness.h> +#include <beecrypt/md5.h> +#include <beecrypt/mp.h> +#include <beecrypt/rsa.h> +#include <beecrypt/rsapk.h> +#include <beecrypt/sha1.h> #if HAVE_BEECRYPT_API_H -#include <sha256.h> -#include <sha384.h> -#include <sha512.h> +#include <beecrypt/sha256.h> +#include <beecrypt/sha384.h> +#include <beecrypt/sha512.h> #endif #include <rpm/rpmpgp.h> @@ -198,66 +198,6 @@ int rpmDigestFinal(DIGEST_CTX ctx, void ** datap, size_t *lenp, int asAscii) return 0; } -/**************************** helpers ************************************/ - -static inline char * pgpHexCvt(char *t, const byte *s, int nbytes) -{ - static char hex[] = "0123456789abcdef"; - while (nbytes-- > 0) { - unsigned int i; - i = *s++; - *t++ = hex[ (i >> 4) & 0xf ]; - *t++ = hex[ (i ) & 0xf ]; - } - *t = '\0'; - return t; -} - -static const char * pgpMpiHex(const byte *p, const byte *pend) -{ - static char prbuf[2048]; - char *t = prbuf; - int nbytes = pgpMpiLen(p) - 2; - if (nbytes > 1024 || nbytes > pend - (p + 2)) - return NULL; - t = pgpHexCvt(t, p+2, nbytes); - return prbuf; -} - -static int pgpHexSet(int lbits, mpnumber * mpn, const byte * p, const byte * pend) -{ - unsigned int mbits = pgpMpiBits(p); - unsigned int nbits; - unsigned int nbytes; - char *t; - unsigned int ix; - - nbits = (lbits > mbits ? lbits : mbits); - nbytes = ((nbits + 7) >> 3); - t = xmalloc(2*nbytes+1); - ix = 2 * ((nbits - mbits) >> 3); - - if (ix > 0) memset(t, (int)'0', ix); - strcpy(t+ix, pgpMpiHex(p, pend)); - (void) mpnsethex(mpn, t); - t = _free(t); - return 0; -} - -static void pgpFreeSigRSADSA(pgpDigAlg sa) -{ - if (sa->data) - free(sa->data); - sa->data = 0; -} - -static void pgpFreeKeyRSADSA(pgpDigAlg sa) -{ - if (sa->data) - free(sa->data); - sa->data = 0; -} - /****************************** RSA **************************************/ @@ -267,45 +207,73 @@ struct pgpDigSigRSA_s { struct pgpDigKeyRSA_s { rsapk rsa_pk; + int nbytes; }; -static int pgpSetSigMpiRSA(pgpDigAlg pgpsig, int num, - const uint8_t *p, const uint8_t *pend) +static int pgpSetSigMpiRSA(pgpDigAlg pgpsig, int num, const uint8_t *p) { struct pgpDigSigRSA_s *sig = pgpsig->data; + int mlen = pgpMpiLen(p) - 2; int rc = 1; + if (!sig) + sig = pgpsig->data = xcalloc(1, sizeof(*sig)); + switch (num) { case 0: - sig = pgpsig->data = xcalloc(1, sizeof(*sig)); - (void) mpnsethex(&sig->c, pgpMpiHex(p, pend)); - rc = 0; + if (!mpnsetbin(&sig->c, p + 2, mlen)) + rc = 0; break; } return rc; } -static int pgpSetKeyMpiRSA(pgpDigAlg pgpkey, int num, - const uint8_t *p, const uint8_t *pend) +static int pgpSetKeyMpiRSA(pgpDigAlg pgpkey, int num, const uint8_t *p) { struct pgpDigKeyRSA_s *key = pgpkey->data; + int mlen = pgpMpiLen(p) - 2; int rc = 1; if (!key) key = pgpkey->data = xcalloc(1, sizeof(*key)); + switch (num) { case 0: - (void) mpbsethex(&key->rsa_pk.n, pgpMpiHex(p, pend)); - rc = 0; + key->nbytes = mlen; + if (!mpbsetbin(&key->rsa_pk.n, p + 2, mlen)) + rc = 0; break; case 1: - (void) mpnsethex(&key->rsa_pk.e, pgpMpiHex(p, pend)); - rc = 0; + if (!mpnsetbin(&key->rsa_pk.e, p + 2, mlen)) + rc = 0; break; } return rc; } +static int pkcs1pad(mpnumber *rsahm, int nbytes, const char *prefix, uint8_t *hash, size_t hashlen) +{ + int datalen = strlen(prefix) / 2 + hashlen; + byte *buf, *bp; + int rc = 1; + + if (nbytes < 4 + datalen) + return 1; + buf = xmalloc(nbytes); + memset(buf, 0xff, nbytes); + buf[0] = 0x00; + buf[1] = 0x01; + bp = buf + nbytes - datalen; + bp[-1] = 0; + for (; *prefix; prefix += 2) + *bp++ = (rnibble(prefix[0]) << 4) | rnibble(prefix[1]); + memcpy(bp, hash, hashlen); + if (!mpnsetbin(rsahm, buf, nbytes)) + rc = 0; + buf = _free(buf); + return rc; +} + static int pgpVerifySigRSA(pgpDigAlg pgpkey, pgpDigAlg pgpsig, uint8_t *hash, size_t hashlen, int hash_algo) { struct pgpDigKeyRSA_s *key = pgpkey->data; @@ -340,28 +308,10 @@ static int pgpVerifySigRSA(pgpDigAlg pgpkey, pgpDigAlg pgpsig, uint8_t *hash, si return 1; } - /* Generate RSA modulus parameter. */ - { unsigned int nbits = MP_WORDS_TO_BITS(sig->c.size); - unsigned int nb = (nbits + 7) >> 3; - byte *buf, *bp; - - if (nb < 3) - return 1; - buf = xmalloc(nb); - memset(buf, 0xff, nb); - buf[0] = 0x00; - buf[1] = 0x01; - bp = buf + nb - strlen(prefix)/2 - hashlen - 1; - if (bp < buf) - return 1; - *bp++ = 0; - for (; *prefix; prefix += 2) - *bp++ = (rnibble(prefix[0]) << 4) | rnibble(prefix[1]); - memcpy(bp, hash, hashlen); - mpnzero(&rsahm); - (void) mpnsetbin(&rsahm, buf, nb); - buf = _free(buf); - } + memset(&rsahm, 0, sizeof(rsahm)); + if (pkcs1pad(&rsahm, key->nbytes, prefix, hash, hashlen) != 0) + return 1; + #if HAVE_BEECRYPT_API_H rc = rsavrfy(&key->rsa_pk.n, &key->rsa_pk.e, &sig->c, &rsahm) == 1 ? 0 : 1; #else @@ -371,6 +321,25 @@ static int pgpVerifySigRSA(pgpDigAlg pgpkey, pgpDigAlg pgpsig, uint8_t *hash, si return rc; } +static void pgpFreeSigRSA(pgpDigAlg pgpsig) +{ + struct pgpDigSigRSA_s *sig = pgpsig->data; + if (sig) { + mpnfree(&sig->c); + pgpsig->data = _free(sig); + } +} + +static void pgpFreeKeyRSA(pgpDigAlg pgpkey) +{ + struct pgpDigKeyRSA_s *key = pgpkey->data; + if (key) { + mpbfree(&key->rsa_pk.n); + mpnfree(&key->rsa_pk.e); + pgpkey->data = _free(key); + } +} + /****************************** DSA **************************************/ @@ -384,30 +353,35 @@ struct pgpDigKeyDSA_s { mpbarrett q; mpnumber g; mpnumber y; + int qbytes; }; -static int pgpSetSigMpiDSA(pgpDigAlg pgpsig, int num, - const uint8_t *p, const uint8_t *pend) +static int pgpSetSigMpiDSA(pgpDigAlg pgpsig, int num, const uint8_t *p) { struct pgpDigSigDSA_s *sig = pgpsig->data; + int mlen = pgpMpiLen(p) - 2; int rc = 1; + if (!sig) + sig = pgpsig->data = xcalloc(1, sizeof(*sig)); + switch (num) { case 0: - sig = pgpsig->data = xcalloc(1, sizeof(*sig)); - rc = pgpHexSet(160, &sig->r, p, pend); + if (!mpnsetbin(&sig->r, p + 2, mlen)) + rc = 0; break; case 1: - rc = pgpHexSet(160, &sig->s, p, pend); + if (!mpnsetbin(&sig->s, p + 2, mlen)) + rc = 0; break; } return rc; } -static int pgpSetKeyMpiDSA(pgpDigAlg pgpkey, int num, - const uint8_t *p, const uint8_t *pend) +static int pgpSetKeyMpiDSA(pgpDigAlg pgpkey, int num, const uint8_t *p) { struct pgpDigKeyDSA_s *key = pgpkey->data; + int mlen = pgpMpiLen(p) - 2; int rc = 1; if (!key) @@ -415,20 +389,21 @@ static int pgpSetKeyMpiDSA(pgpDigAlg pgpkey, int num, switch (num) { case 0: - mpbsethex(&key->p, pgpMpiHex(p, pend)); - rc = 0; + if (!mpbsetbin(&key->p, p + 2, mlen)) + rc = 0; break; case 1: - mpbsethex(&key->q, pgpMpiHex(p, pend)); - rc = 0; + key->qbytes = mlen; + if (!mpbsetbin(&key->q, p + 2, mlen)) + rc = 0; break; case 2: - mpnsethex(&key->g, pgpMpiHex(p, pend)); - rc = 0; + if (!mpnsetbin(&key->g, p + 2, mlen)) + rc = 0; break; case 3: - mpnsethex(&key->y, pgpMpiHex(p, pend)); - rc = 0; + if (!mpnsetbin(&key->y, p + 2, mlen)) + rc = 0; break; } return rc; @@ -441,17 +416,41 @@ static int pgpVerifySigDSA(pgpDigAlg pgpkey, pgpDigAlg pgpsig, uint8_t *hash, si mpnumber hm; int rc = 1; - if (sig && key) { + if (sig && key && hashlen >= key->qbytes) { mpnzero(&hm); - mpnsetbin(&hm, hash, hashlen); + mpnsetbin(&hm, hash, key->qbytes); rc = dsavrfy(&key->p, &key->q, &key->g, &hm, &key->y, &sig->r, &sig->s) == 1 ? 0 : 1; mpnfree(&hm); } return rc; } -static int pgpSetMpiNULL(pgpDigAlg pgpkey, int num, - const uint8_t *p, const uint8_t *pend) +static void pgpFreeSigDSA(pgpDigAlg pgpsig) +{ + struct pgpDigSigDSA_s *sig = pgpsig->data; + if (sig) { + mpnfree(&sig->r); + mpnfree(&sig->s); + pgpsig->data = _free(sig); + } +} + +static void pgpFreeKeyDSA(pgpDigAlg pgpkey) +{ + struct pgpDigKeyDSA_s *key = pgpkey->data; + if (key) { + mpbfree(&key->p); + mpbfree(&key->q); + mpnfree(&key->g); + mpnfree(&key->y); + pgpkey->data = _free(key); + } +} + + +/****************************** NULL **************************************/ + +static int pgpSetMpiNULL(pgpDigAlg pgpkey, int num, const uint8_t *p) { return 1; } @@ -469,12 +468,12 @@ pgpDigAlg pgpPubkeyNew(int algo) switch (algo) { case PGPPUBKEYALGO_RSA: ka->setmpi = pgpSetKeyMpiRSA; - ka->free = pgpFreeKeyRSADSA; + ka->free = pgpFreeKeyRSA; ka->mpis = 2; break; case PGPPUBKEYALGO_DSA: ka->setmpi = pgpSetKeyMpiDSA; - ka->free = pgpFreeKeyRSADSA; + ka->free = pgpFreeKeyDSA; ka->mpis = 4; break; default: @@ -495,13 +494,13 @@ pgpDigAlg pgpSignatureNew(int algo) switch (algo) { case PGPPUBKEYALGO_RSA: sa->setmpi = pgpSetSigMpiRSA; - sa->free = pgpFreeSigRSADSA; + sa->free = pgpFreeSigRSA; sa->verify = pgpVerifySigRSA; sa->mpis = 1; break; case PGPPUBKEYALGO_DSA: sa->setmpi = pgpSetSigMpiDSA; - sa->free = pgpFreeSigRSADSA; + sa->free = pgpFreeSigDSA; sa->verify = pgpVerifySigDSA; sa->mpis = 2; break; diff --git a/rpmio/digest_nss.c b/rpmio/digest_nss.c index 8a948219d..992d9acf6 100644 --- a/rpmio/digest_nss.c +++ b/rpmio/digest_nss.c @@ -53,6 +53,9 @@ int rpmInitCrypto(void) * a private context if possible. */ if (!_crypto_initialized) { + /* NSPR sets SIGPIPE to ignore behind our back, save and restore */ + struct sigaction oact; + sigaction(SIGPIPE, NULL, &oact); #if HAVE_NSS_INITCONTEXT PRUint32 flags = (NSS_INIT_READONLY|NSS_INIT_NOCERTDB| NSS_INIT_NOMODDB|NSS_INIT_FORCEOPEN| @@ -62,10 +65,12 @@ int rpmInitCrypto(void) #else if (NSS_NoDB_Init(NULL) != SECSuccess) { #endif + rpmlog(RPMLOG_ERR, _("Failed to initialize NSS library\n")); rc = -1; } else { _crypto_initialized = 1; } + sigaction(SIGPIPE, &oact, NULL); } /* Register one post-fork handler per process */ @@ -223,8 +228,7 @@ static SECOidTag getHashAlg(unsigned int hashalgo) return SEC_OID_UNKNOWN; } -static int pgpMpiSet(unsigned int lbits, uint8_t *dest, - const uint8_t * p, const uint8_t * pend) +static int pgpMpiSet(unsigned int lbits, uint8_t *dest, const uint8_t * p) { unsigned int mbits = pgpMpiBits(p); unsigned int nbits; @@ -232,9 +236,6 @@ static int pgpMpiSet(unsigned int lbits, uint8_t *dest, uint8_t *t = dest; unsigned int ix; - if ((p + ((mbits+7) >> 3)) > pend) - return 1; - if (mbits > lbits) return 1; @@ -249,14 +250,10 @@ static int pgpMpiSet(unsigned int lbits, uint8_t *dest, return 0; } -static SECItem *pgpMpiItem(PRArenaPool *arena, SECItem *item, - const uint8_t *p, const uint8_t *pend) +static SECItem *pgpMpiItem(PRArenaPool *arena, SECItem *item, const uint8_t *p) { size_t nbytes = pgpMpiLen(p)-2; - if (p + nbytes + 2 > pend) - return NULL; - if (item == NULL) { if ((item=SECITEM_AllocItem(arena, item, nbytes)) == NULL) return item; @@ -312,25 +309,28 @@ static SECKEYPublicKey *pgpNewPublicKey(KeyType type) #define DSA1_Q_BITS DSA_Q_BITS #endif -static int pgpSetSigMpiDSA(pgpDigAlg pgpsig, int num, - const uint8_t *p, const uint8_t *pend) +static int pgpSetSigMpiDSA(pgpDigAlg pgpsig, int num, const uint8_t *p) { SECItem *sig = pgpsig->data; - int lbits = DSA1_Q_BITS; + unsigned int qbits = DSA1_Q_BITS; + unsigned int subprlen = DSA1_SUBPRIME_LEN; + unsigned int siglen = DSA1_SIGNATURE_LEN; int rc = 1; /* assume failure */ switch (num) { case 0: - sig = pgpsig->data = SECITEM_AllocItem(NULL, NULL, DSA1_SIGNATURE_LEN); + sig = pgpsig->data = SECITEM_AllocItem(NULL, NULL, siglen); if (sig) { - memset(sig->data, 0, DSA1_SIGNATURE_LEN); - rc = pgpMpiSet(lbits, sig->data, p, pend); + memset(sig->data, 0, siglen); + rc = pgpMpiSet(qbits, sig->data, p); } break; case 1: - if (sig && pgpMpiSet(lbits, sig->data+DSA1_SUBPRIME_LEN, p, pend) == 0) { + if (sig && pgpMpiSet(qbits, sig->data+subprlen, p) == 0) { SECItem *signew = SECITEM_AllocItem(NULL, NULL, 0); - if (signew && DSAU_EncodeDerSig(signew, sig) == SECSuccess) { + if (signew == NULL) + break; + if (DSAU_EncodeDerSigWithLen(signew, sig, siglen) == SECSuccess) { SECITEM_FreeItem(sig, PR_TRUE); pgpsig->data = signew; rc = 0; @@ -342,8 +342,7 @@ static int pgpSetSigMpiDSA(pgpDigAlg pgpsig, int num, return rc; } -static int pgpSetKeyMpiDSA(pgpDigAlg pgpkey, int num, - const uint8_t *p, const uint8_t *pend) +static int pgpSetKeyMpiDSA(pgpDigAlg pgpkey, int num, const uint8_t *p) { SECItem *mpi = NULL; SECKEYPublicKey *key = pgpkey->data; @@ -354,16 +353,16 @@ static int pgpSetKeyMpiDSA(pgpDigAlg pgpkey, int num, if (key) { switch (num) { case 0: - mpi = pgpMpiItem(key->arena, &key->u.dsa.params.prime, p, pend); + mpi = pgpMpiItem(key->arena, &key->u.dsa.params.prime, p); break; case 1: - mpi = pgpMpiItem(key->arena, &key->u.dsa.params.subPrime, p, pend); + mpi = pgpMpiItem(key->arena, &key->u.dsa.params.subPrime, p); break; case 2: - mpi = pgpMpiItem(key->arena, &key->u.dsa.params.base, p, pend); + mpi = pgpMpiItem(key->arena, &key->u.dsa.params.base, p); break; case 3: - mpi = pgpMpiItem(key->arena, &key->u.dsa.publicValue, p, pend); + mpi = pgpMpiItem(key->arena, &key->u.dsa.publicValue, p); break; } } @@ -388,21 +387,19 @@ static int pgpVerifySigDSA(pgpDigAlg pgpkey, pgpDigAlg pgpsig, return (rc != SECSuccess); } -static int pgpSetSigMpiRSA(pgpDigAlg pgpsig, int num, - const uint8_t *p, const uint8_t *pend) +static int pgpSetSigMpiRSA(pgpDigAlg pgpsig, int num, const uint8_t *p) { SECItem *sigitem = NULL; if (num == 0) { - sigitem = pgpMpiItem(NULL, pgpsig->data, p, pend); + sigitem = pgpMpiItem(NULL, pgpsig->data, p); if (sigitem) pgpsig->data = sigitem; } return (sigitem == NULL); } -static int pgpSetKeyMpiRSA(pgpDigAlg pgpkey, int num, - const uint8_t *p, const uint8_t *pend) +static int pgpSetKeyMpiRSA(pgpDigAlg pgpkey, int num, const uint8_t *p) { SECItem *kitem = NULL; SECKEYPublicKey *key = pgpkey->data; @@ -413,10 +410,10 @@ static int pgpSetKeyMpiRSA(pgpDigAlg pgpkey, int num, if (key) { switch (num) { case 0: - kitem = pgpMpiItem(key->arena, &key->u.rsa.modulus, p, pend); + kitem = pgpMpiItem(key->arena, &key->u.rsa.modulus, p); break; case 1: - kitem = pgpMpiItem(key->arena, &key->u.rsa.publicExponent, p, pend); + kitem = pgpMpiItem(key->arena, &key->u.rsa.publicExponent, p); break; } } @@ -471,8 +468,7 @@ static void pgpFreeKeyRSADSA(pgpDigAlg ka) ka->data = NULL; } -static int pgpSetMpiNULL(pgpDigAlg pgpkey, int num, - const uint8_t *p, const uint8_t *pend) +static int pgpSetMpiNULL(pgpDigAlg pgpkey, int num, const uint8_t *p) { return 1; } diff --git a/rpmio/digest_openssl.c b/rpmio/digest_openssl.c new file mode 100644 index 000000000..18e52a724 --- /dev/null +++ b/rpmio/digest_openssl.c @@ -0,0 +1,839 @@ +#include "system.h" + +#include <openssl/evp.h> +#include <openssl/rsa.h> +#include <openssl/dsa.h> +#include <rpm/rpmpgp.h> + +#include "rpmio/digest.h" + + +/* Compatibility functions for OpenSSL 1.0.2 */ + +#ifndef HAVE_EVP_MD_CTX_NEW +# define EVP_MD_CTX_new EVP_MD_CTX_create +# define EVP_MD_CTX_free EVP_MD_CTX_destroy +#endif + +#ifndef HAVE_RSA_SET0_KEY +int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d); +int RSA_set0_key(RSA *r, BIGNUM *n, BIGNUM *e, BIGNUM *d) +{ + if (!r) return 0; + + if (n) { + r->n = n; + } + + if (e) { + r->e = e; + } + + if (d) { + r->d = d; + } + + return 1; +} +#endif /* HAVE_RSA_SET0_KEY */ + +#ifndef HAVE_DSA_SET0_KEY +int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key); + +int DSA_set0_key(DSA *d, BIGNUM *pub_key, BIGNUM *priv_key) +{ + if (!d) return 0; + + if (pub_key) { + d->pub_key = pub_key; + } + + if (priv_key) { + d->priv_key = priv_key; + } + + return 1; +} +#endif /* HAVE_DSA_SET0_KEY */ + +#ifndef HAVE_DSA_SET0_PQG +int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g); + +int DSA_set0_pqg(DSA *d, BIGNUM *p, BIGNUM *q, BIGNUM *g) +{ + if (!d) return 0; + + if (p) { + d->p = p; + } + + if (q) { + d->q = q; + } + + if (g) { + d->g = g; + } + + return 1; +} +#endif /* HAVE_DSA_SET0_PQG */ + +#ifndef HAVE_DSA_SIG_SET0 +int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s); + +int DSA_SIG_set0(DSA_SIG *sig, BIGNUM *r, BIGNUM *s) +{ + if (!sig) return 0; + + if (r) { + sig->r = r; + } + + if (s) { + sig->s = s; + } + + return 1; +} +#endif /* HAVE_DSA_SIG_SET0 */ + +#ifndef HAVE_BN2BINPAD +static int BN_bn2binpad(const BIGNUM *a, unsigned char *to, int tolen) +{ + int i; + + i = BN_num_bytes(a); + if (tolen < i) + return -1; + + /* Add leading zeroes if necessary */ + if (tolen > i) { + memset(to, 0, tolen - i); + to += tolen - i; + } + + BN_bn2bin(a, to); + + return tolen; +} +#endif /* HAVE_BN2BINPAD */ + +struct DIGEST_CTX_s { + rpmDigestFlags flags; /*!< Bit(s) to control digest operation. */ + int algo; /*!< Used hash algorithm */ + + EVP_MD_CTX *md_ctx; /* Digest context (opaque) */ + +}; + +/**************************** init ************************************/ + +int rpmInitCrypto(void) { + return 0; +} + +int rpmFreeCrypto(void) { + return 0; +} + +/**************************** digest ************************************/ + +DIGEST_CTX rpmDigestDup(DIGEST_CTX octx) +{ + if (!octx) return NULL; + + DIGEST_CTX nctx = NULL; + nctx = xcalloc(1, sizeof(*nctx)); + + nctx->flags = octx->flags; + nctx->algo = octx->algo; + nctx->md_ctx = EVP_MD_CTX_new(); + if (!nctx->md_ctx) { + free(nctx); + return NULL; + } + + if (!EVP_MD_CTX_copy(nctx->md_ctx, octx->md_ctx)) { + free(nctx); + return NULL; + } + + return nctx; +} + +static const EVP_MD *getEVPMD(int hashalgo) +{ + switch (hashalgo) { + + case PGPHASHALGO_MD5: + return EVP_md5(); + + case PGPHASHALGO_SHA1: + return EVP_sha1(); + + case PGPHASHALGO_RIPEMD160: + return EVP_ripemd160(); + + case PGPHASHALGO_MD2: + return EVP_md2(); + + case PGPHASHALGO_SHA256: + return EVP_sha256(); + + case PGPHASHALGO_SHA384: + return EVP_sha384(); + + case PGPHASHALGO_SHA512: + return EVP_sha512(); + + case PGPHASHALGO_SHA224: + return EVP_sha224(); + + default: + return EVP_md_null(); + } +} + +size_t rpmDigestLength(int hashalgo) +{ + return EVP_MD_size(getEVPMD(hashalgo)); +} + +DIGEST_CTX rpmDigestInit(int hashalgo, rpmDigestFlags flags) +{ + DIGEST_CTX ctx = xcalloc(1, sizeof(*ctx)); + + ctx->md_ctx = EVP_MD_CTX_new(); + if (!ctx->md_ctx) { + free(ctx); + return NULL; + } + + const EVP_MD *md = getEVPMD(hashalgo); + if (md == EVP_md_null()) { + free(ctx->md_ctx); + free(ctx); + return NULL; + } + + ctx->algo = hashalgo; + ctx->flags = flags; + if (!EVP_DigestInit_ex(ctx->md_ctx, md, NULL)) { + free(ctx->md_ctx); + free(ctx); + return NULL; + } + + return ctx; +} + +int rpmDigestUpdate(DIGEST_CTX ctx, const void *data, size_t len) +{ + if (ctx == NULL) return -1; + + EVP_DigestUpdate(ctx->md_ctx, data, len); + + return 0; +} + +int rpmDigestFinal(DIGEST_CTX ctx, void ** datap, size_t *lenp, int asAscii) +{ + int ret; + unsigned char *digest = NULL; + unsigned int digestlen; + + if (ctx == NULL) return -1; + + digestlen = EVP_MD_CTX_size(ctx->md_ctx); + digest = xcalloc(digestlen, sizeof(*digest)); + + ret = EVP_DigestFinal_ex(ctx->md_ctx, digest, &digestlen); + if (ret != 1) goto done; + + if (!asAscii) { + /* Raw data requested */ + if (lenp) *lenp = digestlen; + if (datap) { + *datap = digest; + digest = NULL; + } + } + + else { + /* ASCII requested */ + if (lenp) *lenp = (2*digestlen) + 1; + if (datap) { + const uint8_t * s = (const uint8_t *) digest; + *datap = pgpHexStr(s, digestlen); + } + } + + ret = 1; + +done: + if (digest) { + /* Zero the digest, just in case it's sensitive */ + memset(digest, 0, digestlen); + free(digest); + } + + EVP_MD_CTX_free(ctx->md_ctx); + free(ctx); + + if (ret != 1) { + return -1; + } + + return 0; +} + + +/****************************** RSA **************************************/ + +/* Key */ + +struct pgpDigKeyRSA_s { + size_t nbytes; /* Size of modulus */ + + BIGNUM *n; /* Common Modulus */ + BIGNUM *e; /* Public Exponent */ + + EVP_PKEY *evp_pkey; /* Fully constructed key */ +}; + +static int constructRSASigningKey(struct pgpDigKeyRSA_s *key) +{ + if (key->evp_pkey) { + /* We've already constructed it, so just reuse it */ + return 1; + } + + /* Create the RSA key */ + RSA *rsa = RSA_new(); + if (!rsa) return 0; + + if (!RSA_set0_key(rsa, key->n, key->e, NULL)) { + RSA_free(rsa); + return 0; + } + + /* Create an EVP_PKEY container to abstract the key-type. */ + key->evp_pkey = EVP_PKEY_new(); + if (!key->evp_pkey) { + RSA_free(rsa); + return 0; + } + + /* Assign the RSA key to the EVP_PKEY structure. + This will take over memory management of the RSA key */ + if (!EVP_PKEY_assign_RSA(key->evp_pkey, rsa)) { + EVP_PKEY_free(key->evp_pkey); + key->evp_pkey = NULL; + RSA_free(rsa); + } + + return 1; +} + +static int pgpSetKeyMpiRSA(pgpDigAlg pgpkey, int num, const uint8_t *p) +{ + size_t mlen = pgpMpiLen(p) - 2; + struct pgpDigKeyRSA_s *key = pgpkey->data; + + if (!key) { + key = pgpkey->data = xcalloc(1, sizeof(*key)); + } + + switch (num) { + case 0: + /* Modulus */ + if (key->n) { + /* This should only ever happen once per key */ + return 1; + } + + key->nbytes = mlen; + /* Create a BIGNUM from the pointer. + Note: this assumes big-endian data as required by PGP */ + key->n = BN_bin2bn(p+2, mlen, NULL); + if (!key->n) return 1; + break; + + case 1: + /* Exponent */ + if (key->e) { + /* This should only ever happen once per key */ + return 1; + } + + /* Create a BIGNUM from the pointer. + Note: this assumes big-endian data as required by PGP */ + key->e = BN_bin2bn(p+2, mlen, NULL); + if (!key->e) return 1; + break; + } + + return 0; +} + +static void pgpFreeKeyRSA(pgpDigAlg pgpkey) +{ + struct pgpDigKeyRSA_s *key = pgpkey->data; + if (key) { + if (key->evp_pkey) { + EVP_PKEY_free(key->evp_pkey); + } else { + /* If key->evp_pkey was constructed, + * the memory management of these BNs + * are freed with it. */ + BN_clear_free(key->n); + BN_clear_free(key->e); + } + + free(key); + } +} + +/* Signature */ + +struct pgpDigSigRSA_s { + BIGNUM *bn; + size_t len; +}; + +static int pgpSetSigMpiRSA(pgpDigAlg pgpsig, int num, const uint8_t *p) +{ + BIGNUM *bn = NULL; + + int mlen = pgpMpiLen(p) - 2; + int rc = 1; + + struct pgpDigSigRSA_s *sig = pgpsig->data; + if (!sig) { + sig = xcalloc(1, sizeof(*sig)); + } + + switch (num) { + case 0: + if (sig->bn) { + /* This should only ever happen once per signature */ + return 1; + } + + bn = sig->bn = BN_new(); + if (!bn) return 1; + + /* Create a BIGNUM from the signature pointer. + Note: this assumes big-endian data as required + by the PGP multiprecision integer format + (RFC4880, Section 3.2) + This will be useful later, as we can + retrieve this value with appropriate + padding. */ + bn = BN_bin2bn(p+2, mlen, bn); + if (!bn) return 1; + + sig->bn = bn; + sig->len = mlen; + + pgpsig->data = sig; + rc = 0; + break; + } + return rc; +} + +static void pgpFreeSigRSA(pgpDigAlg pgpsig) +{ + struct pgpDigSigRSA_s *sig = pgpsig->data; + if (sig) { + BN_clear_free(sig->bn); + free(pgpsig->data); + } +} + +static int pgpVerifySigRSA(pgpDigAlg pgpkey, pgpDigAlg pgpsig, + uint8_t *hash, size_t hashlen, int hash_algo) +{ + int rc, ret; + EVP_PKEY_CTX *pkey_ctx = NULL; + struct pgpDigSigRSA_s *sig = pgpsig->data; + + void *padded_sig = NULL; + + struct pgpDigKeyRSA_s *key = pgpkey->data; + + if (!constructRSASigningKey(key)) { + rc = 1; + goto done; + } + + pkey_ctx = EVP_PKEY_CTX_new(key->evp_pkey, NULL); + if (!pkey_ctx) { + rc = 1; + goto done; + } + + ret = EVP_PKEY_verify_init(pkey_ctx); + if (ret < 0) { + rc = 1; + goto done; + } + + ret = EVP_PKEY_CTX_set_rsa_padding(pkey_ctx, RSA_PKCS1_PADDING); + if (ret < 0) { + rc = 1; + goto done; + } + + ret = EVP_PKEY_CTX_set_signature_md(pkey_ctx, getEVPMD(hash_algo)); + if (ret < 0) { + rc = 1; + goto done; + } + + int pkey_len = EVP_PKEY_size(key->evp_pkey); + padded_sig = xcalloc(1, pkey_len); + if (!BN_bn2binpad(sig->bn, padded_sig, pkey_len)) { + rc = 1; + goto done; + } + + ret = EVP_PKEY_verify(pkey_ctx, padded_sig, pkey_len, hash, hashlen); + if (ret == 1) + { + /* Success */ + rc = 0; + } + else + { + /* Failure */ + rc = 1; + } + +done: + EVP_PKEY_CTX_free(pkey_ctx); + free(padded_sig); + return rc; +} + +/****************************** DSA ***************************************/ +/* Key */ + +struct pgpDigKeyDSA_s { + BIGNUM *p; /* Prime */ + BIGNUM *q; /* Subprime */ + BIGNUM *g; /* Base */ + BIGNUM *y; /* Public Key */ + + DSA *dsa_key; /* Fully constructed key */ +}; + +static int constructDSASigningKey(struct pgpDigKeyDSA_s *key) +{ + int rc; + + if (key->dsa_key) { + /* We've already constructed it, so just reuse it */ + return 1; + } + + /* Create the DSA key */ + DSA *dsa = DSA_new(); + if (!dsa) return 0; + + if (!DSA_set0_pqg(dsa, key->p, key->q, key->g)) { + rc = 0; + goto done; + } + + if (!DSA_set0_key(dsa, key->y, NULL)) { + rc = 0; + goto done; + } + + key->dsa_key = dsa; + + rc = 1; +done: + if (rc == 0) { + DSA_free(dsa); + } + return rc; +} + + +static int pgpSetKeyMpiDSA(pgpDigAlg pgpkey, int num, const uint8_t *p) +{ + BIGNUM *bn; + size_t mlen = pgpMpiLen(p) - 2; + struct pgpDigKeyDSA_s *key = pgpkey->data; + + if (!key) { + key = pgpkey->data = xcalloc(1, sizeof(*key)); + } + + /* Create a BIGNUM from the key pointer. + Note: this assumes big-endian data as required + by the PGP multiprecision integer format + (RFC4880, Section 3.2) */ + bn = BN_bin2bn(p+2, mlen, NULL); + if (!bn) return 1; + + switch (num) { + case 0: + /* Prime */ + if (key->p) { + /* This should only ever happen once per key */ + return 1; + } + key->p = bn; + break; + + case 1: + /* Subprime */ + if (key->q) { + /* This should only ever happen once per key */ + return 1; + } + key->q = bn; + break; + case 2: + /* Base */ + if (key->g) { + /* This should only ever happen once per key */ + return 1; + } + key->g = bn; + break; + case 3: + /* Public */ + if (key->y) { + /* This should only ever happen once per key */ + return 1; + } + key->y = bn; + break; + } + + return 0; +} + +static void pgpFreeKeyDSA(pgpDigAlg pgpkey) +{ + struct pgpDigKeyDSA_s *key = pgpkey->data; + if (key) { + if (key->dsa_key) { + DSA_free(key->dsa_key); + } else { + /* If sig->dsa_key was constructed, + * the memory management of these BNs + * are freed with it. */ + BN_clear_free(key->p); + BN_clear_free(key->q); + BN_clear_free(key->g); + BN_clear_free(key->y); + } + free(key); + } +} + +/* Signature */ + +struct pgpDigSigDSA_s { + BIGNUM *r; + BIGNUM *s; + + DSA_SIG *dsa_sig; +}; + +static int constructDSASignature(struct pgpDigSigDSA_s *sig) +{ + int rc; + + if (sig->dsa_sig) { + /* We've already constructed it, so just reuse it */ + return 1; + } + + /* Create the DSA signature */ + DSA_SIG *dsa_sig = DSA_SIG_new(); + if (!dsa_sig) return 0; + + if (!DSA_SIG_set0(dsa_sig, sig->r, sig->s)) { + rc = 0; + goto done; + } + + sig->dsa_sig = dsa_sig; + + rc = 1; +done: + if (rc == 0) { + DSA_SIG_free(sig->dsa_sig); + } + return rc; +} + +static int pgpSetSigMpiDSA(pgpDigAlg pgpsig, int num, const uint8_t *p) +{ + BIGNUM *bn = NULL; + + int mlen = pgpMpiLen(p) - 2; + int rc = 1; + + struct pgpDigSigDSA_s *sig = pgpsig->data; + if (!sig) { + sig = xcalloc(1, sizeof(*sig)); + } + + /* Create a BIGNUM from the signature pointer. + Note: this assumes big-endian data as required + by the PGP multiprecision integer format + (RFC4880, Section 3.2) */ + bn = BN_bin2bn(p+2, mlen, NULL); + if (!bn) return 1; + + switch (num) { + case 0: + if (sig->r) { + /* This should only ever happen once per signature */ + BN_free(bn); + return 1; + } + sig->r = bn; + rc = 0; + break; + case 1: + if (sig->s) { + /* This should only ever happen once per signature */ + BN_free(bn); + return 1; + } + sig->s = bn; + rc = 0; + break; + } + + pgpsig->data = sig; + + return rc; +} + +static void pgpFreeSigDSA(pgpDigAlg pgpsig) +{ + struct pgpDigSigDSA_s *sig = pgpsig->data; + if (sig) { + if (sig->dsa_sig) { + DSA_SIG_free(sig->dsa_sig); + } else { + /* If sig->dsa_sig was constructed, + * the memory management of these BNs + * are freed with it. */ + BN_clear_free(sig->r); + BN_clear_free(sig->s); + } + free(pgpsig->data); + } +} + +static int pgpVerifySigDSA(pgpDigAlg pgpkey, pgpDigAlg pgpsig, + uint8_t *hash, size_t hashlen, int hash_algo) +{ + int rc, ret; + struct pgpDigSigDSA_s *sig = pgpsig->data; + + struct pgpDigKeyDSA_s *key = pgpkey->data; + + if (!constructDSASigningKey(key)) { + rc = 1; + goto done; + } + + if (!constructDSASignature(sig)) { + rc = 1; + goto done; + } + + ret = DSA_do_verify(hash, hashlen, sig->dsa_sig, key->dsa_key); + if (ret == 1) + { + /* Success */ + rc = 0; + } + else + { + /* Failure */ + rc = 1; + } + +done: + return rc; +} + +/****************************** NULL **************************************/ + +static int pgpSetMpiNULL(pgpDigAlg pgpkey, int num, const uint8_t *p) +{ + return 1; +} + +static int pgpVerifyNULL(pgpDigAlg pgpkey, pgpDigAlg pgpsig, + uint8_t *hash, size_t hashlen, int hash_algo) +{ + return 1; +} + +/****************************** PGP **************************************/ +pgpDigAlg pgpPubkeyNew(int algo) +{ + pgpDigAlg ka = xcalloc(1, sizeof(*ka));; + + switch (algo) { + case PGPPUBKEYALGO_RSA: + ka->setmpi = pgpSetKeyMpiRSA; + ka->free = pgpFreeKeyRSA; + ka->mpis = 2; + break; + case PGPPUBKEYALGO_DSA: + ka->setmpi = pgpSetKeyMpiDSA; + ka->free = pgpFreeKeyDSA; + ka->mpis = 4; + break; + default: + ka->setmpi = pgpSetMpiNULL; + ka->mpis = -1; + break; + } + + ka->verify = pgpVerifyNULL; /* keys can't be verified */ + + return ka; +} + +pgpDigAlg pgpSignatureNew(int algo) +{ + pgpDigAlg sa = xcalloc(1, sizeof(*sa)); + + switch (algo) { + case PGPPUBKEYALGO_RSA: + sa->setmpi = pgpSetSigMpiRSA; + sa->free = pgpFreeSigRSA; + sa->verify = pgpVerifySigRSA; + sa->mpis = 1; + break; + case PGPPUBKEYALGO_DSA: + sa->setmpi = pgpSetSigMpiDSA; + sa->free = pgpFreeSigDSA; + sa->verify = pgpVerifySigDSA; + sa->mpis = 2; + break; + default: + sa->setmpi = pgpSetMpiNULL; + sa->verify = pgpVerifyNULL; + sa->mpis = -1; + break; + } + return sa; +} diff --git a/rpmio/macro.c b/rpmio/macro.c index c1bb04334..7858b10b2 100644 --- a/rpmio/macro.c +++ b/rpmio/macro.c @@ -4,6 +4,8 @@ #include "system.h" #include <stdarg.h> +#include <pthread.h> +#include <errno.h> #ifdef HAVE_GETOPT_H #include <getopt.h> #else @@ -34,21 +36,31 @@ extern int optind; #include "debug.h" +enum macroFlags_e { + ME_NONE = 0, + ME_AUTO = (1 << 0), + ME_USED = (1 << 1), +}; + /*! The structure used to store a macro. */ struct rpmMacroEntry_s { struct rpmMacroEntry_s *prev;/*!< Macro entry stack. */ - char *name; /*!< Macro name. */ - char *opts; /*!< Macro parameters (a la getopt) */ - char *body; /*!< Macro body. */ - int used; /*!< No. of expansions. */ + const char *name; /*!< Macro name. */ + const char *opts; /*!< Macro parameters (a la getopt) */ + const char *body; /*!< Macro body. */ + int flags; /*!< Macro state bits. */ int level; /*!< Scoping level. */ + char arena[]; /*!< String arena. */ }; /*! The structure used to store the set of macros in a context. */ struct rpmMacroContext_s { - rpmMacroEntry *macroTable; /*!< Macro entry table for context. */ - int macrosAllocated;/*!< No. of allocated macros. */ - int firstFree; /*!< No. of macros. */ + rpmMacroEntry *tab; /*!< Macro entry table (array of pointers). */ + int n; /*!< No. of macros. */ + int depth; /*!< Depth tracking when recursing from Lua */ + int level; /*!< Scope level tracking when recursing from Lua */ + pthread_mutex_t lock; + pthread_mutexattr_t lockattr; }; @@ -58,6 +70,29 @@ rpmMacroContext rpmGlobalMacroContext = &rpmGlobalMacroContext_s; static struct rpmMacroContext_s rpmCLIMacroContext_s; rpmMacroContext rpmCLIMacroContext = &rpmCLIMacroContext_s; +/* + * The macro engine internals do not require recursive mutexes but Lua + * macro bindings which can get called from the internals use the external + * interfaces which do perform locking. Until that is fixed somehow + * we'll just have to settle for recursive mutexes. + * Unfortunately POSIX doesn't specify static initializers for recursive + * mutexes so we need to have a separate PTHREAD_ONCE initializer just + * to initialize the otherwise static macro context mutexes. Pooh. + */ +static pthread_once_t locksInitialized = PTHREAD_ONCE_INIT; + +static void initLocks(void) +{ + rpmMacroContext mcs[] = { rpmGlobalMacroContext, rpmCLIMacroContext, NULL }; + + for (rpmMacroContext *mcp = mcs; *mcp; mcp++) { + rpmMacroContext mc = *mcp; + pthread_mutexattr_init(&mc->lockattr); + pthread_mutexattr_settype(&mc->lockattr, PTHREAD_MUTEX_RECURSIVE); + pthread_mutex_init(&mc->lock, &mc->lockattr); + } +} + /** * Macro expansion state. */ @@ -66,12 +101,16 @@ typedef struct MacroBuf_s { size_t tpos; /*!< Current position in expansion buffer */ size_t nb; /*!< No. bytes remaining in expansion buffer. */ int depth; /*!< Current expansion depth. */ + int level; /*!< Current scoping level */ + int error; /*!< Errors encountered during expansion? */ int macro_trace; /*!< Pre-print macro to expand? */ int expand_trace; /*!< Post-print macro expansion? */ + int escape; /*!< Preserve '%%' during expansion? */ + int flags; /*!< Flags to control behavior */ rpmMacroContext mc; } * MacroBuf; -#define _MAX_MACRO_DEPTH 16 +#define _MAX_MACRO_DEPTH 64 static int max_macro_depth = _MAX_MACRO_DEPTH; #define _PRINT_MACRO_TRACE 0 @@ -80,110 +119,28 @@ static int print_macro_trace = _PRINT_MACRO_TRACE; #define _PRINT_EXPAND_TRACE 0 static int print_expand_trace = _PRINT_EXPAND_TRACE; -#define MACRO_CHUNK_SIZE 16 - /* forward ref */ static int expandMacro(MacroBuf mb, const char *src, size_t slen); +static void pushMacro(rpmMacroContext mc, + const char * n, const char * o, const char * b, int level, int flags); +static void popMacro(rpmMacroContext mc, const char * n); +static int loadMacroFile(rpmMacroContext mc, const char * fn); /* =============================================================== */ -/** - * Compare macro entries by name (qsort/bsearch). - * @param ap 1st macro entry - * @param bp 2nd macro entry - * @return result of comparison - */ -static int -compareMacroName(const void * ap, const void * bp) +static rpmMacroContext rpmmctxAcquire(rpmMacroContext mc) { - rpmMacroEntry ame = *((const rpmMacroEntry *)ap); - rpmMacroEntry bme = *((const rpmMacroEntry *)bp); - - if (ame == NULL && bme == NULL) - return 0; - if (ame == NULL) - return 1; - if (bme == NULL) - return -1; - return strcmp(ame->name, bme->name); + if (mc == NULL) + mc = rpmGlobalMacroContext; + pthread_once(&locksInitialized, initLocks); + pthread_mutex_lock(&mc->lock); + return mc; } -/** - * Enlarge macro table. - * @param mc macro context - */ -static void -expandMacroTable(rpmMacroContext mc) +static rpmMacroContext rpmmctxRelease(rpmMacroContext mc) { - if (mc->macroTable == NULL) { - mc->macrosAllocated = MACRO_CHUNK_SIZE; - mc->macroTable = (rpmMacroEntry *) - xmalloc(sizeof(*(mc->macroTable)) * mc->macrosAllocated); - mc->firstFree = 0; - } else { - mc->macrosAllocated += MACRO_CHUNK_SIZE; - mc->macroTable = (rpmMacroEntry *) - xrealloc(mc->macroTable, sizeof(*(mc->macroTable)) * - mc->macrosAllocated); - } - memset(&mc->macroTable[mc->firstFree], 0, MACRO_CHUNK_SIZE * sizeof(*(mc->macroTable))); -} - -/** - * Sort entries in macro table. - * @param mc macro context - */ -static void -sortMacroTable(rpmMacroContext mc) -{ - int i; - - if (mc == NULL || mc->macroTable == NULL) - return; - - qsort(mc->macroTable, mc->firstFree, sizeof(*(mc->macroTable)), - compareMacroName); - - /* Empty pointers are now at end of table. Reset first free index. */ - for (i = 0; i < mc->firstFree; i++) { - if (mc->macroTable[i] != NULL) - continue; - mc->firstFree = i; - break; - } -} - -void -rpmDumpMacroTable(rpmMacroContext mc, FILE * fp) -{ - int nempty = 0; - int nactive = 0; - - if (mc == NULL) mc = rpmGlobalMacroContext; - if (fp == NULL) fp = stderr; - - fprintf(fp, "========================\n"); - if (mc->macroTable != NULL) { - int i; - for (i = 0; i < mc->firstFree; i++) { - rpmMacroEntry me; - if ((me = mc->macroTable[i]) == NULL) { - /* XXX this should never happen */ - nempty++; - continue; - } - fprintf(fp, "%3d%c %s", me->level, - (me->used > 0 ? '=' : ':'), me->name); - if (me->opts && *me->opts) - fprintf(fp, "(%s)", me->opts); - if (me->body && *me->body) - fprintf(fp, "\t%s", me->body); - fprintf(fp, "\n"); - nactive++; - } - } - fprintf(fp, _("======================== active %d empty %d\n"), - nactive, nempty); + pthread_mutex_unlock(&mc->lock); + return NULL; } /** @@ -191,33 +148,41 @@ rpmDumpMacroTable(rpmMacroContext mc, FILE * fp) * @param mc macro context * @param name macro name * @param namelen no. of bytes + * @param pos found/insert position * @return address of slot in macro table with name (or NULL) */ static rpmMacroEntry * -findEntry(rpmMacroContext mc, const char * name, size_t namelen) +findEntry(rpmMacroContext mc, const char *name, size_t namelen, size_t *pos) { - rpmMacroEntry key, *ret; - struct rpmMacroEntry_s keybuf; - char namebuf[namelen+1]; - const char *mname = name; - - if (mc == NULL) mc = rpmGlobalMacroContext; - if (mc->macroTable == NULL || mc->firstFree == 0) - return NULL; - - if (namelen > 0) { - strncpy(namebuf, name, namelen); - namebuf[namelen] = '\0'; - mname = namebuf; + /* bsearch */ + int cmp = 1; + size_t l = 0; + size_t u = mc->n; + size_t i = 0; + while (l < u) { + i = (l + u) / 2; + rpmMacroEntry me = mc->tab[i]; + if (namelen == 0) + cmp = strcmp(me->name, name); + else { + cmp = strncmp(me->name, name, namelen); + /* longer me->name compares greater */ + if (cmp == 0) + cmp = (me->name[namelen] != '\0'); + } + if (cmp < 0) + l = i + 1; + else if (cmp > 0) + u = i; + else + break; } - - key = &keybuf; - memset(key, 0, sizeof(*key)); - key->name = (char *)mname; - ret = (rpmMacroEntry *) bsearch(&key, mc->macroTable, mc->firstFree, - sizeof(*(mc->macroTable)), compareMacroName); - /* XXX TODO: find 1st empty slot and return that */ - return ret; + + if (pos) + *pos = (cmp < 0) ? i + 1 : i; + if (cmp == 0) + return &mc->tab[i]; + return NULL; } /* =============================================================== */ @@ -226,7 +191,7 @@ findEntry(rpmMacroContext mc, const char * name, size_t namelen) * fgets(3) analogue that reads \ continuations. Last newline always trimmed. * @param buf input buffer * @param size inbut buffer size (bytes) - * @param fd file handle + * @param f file handle * @return buffer, or NULL on end-of-file */ static char * @@ -316,11 +281,9 @@ static void printMacro(MacroBuf mb, const char * s, const char * se) { const char *senl; - const char *ellipsis; - int choplen; if (s >= se) { /* XXX just in case */ - fprintf(stderr, _("%3d>%*s(empty)"), mb->depth, + fprintf(stderr, _("%3d>%*s(empty)\n"), mb->depth, (2 * mb->depth + 1), ""); return; } @@ -332,19 +295,11 @@ printMacro(MacroBuf mb, const char * s, const char * se) for (senl = se; *senl && !iseol(*senl); senl++) {}; - /* Limit trailing non-trace output */ - choplen = 61 - (2 * mb->depth); - if ((senl - s) > choplen) { - senl = s + choplen; - ellipsis = "..."; - } else - ellipsis = ""; - /* Substitute caret at end-of-macro position */ fprintf(stderr, "%3d>%*s%%%.*s^", mb->depth, (2 * mb->depth + 1), "", (int)(se - s), s); - if (se[1] != '\0' && (senl - (se+1)) > 0) - fprintf(stderr, "%-.*s%s", (int)(senl - (se+1)), se+1, ellipsis); + if (se[0] != '\0' && se[1] != '\0' && (senl - (se+1)) > 0) + fprintf(stderr, "%-.*s", (int)(senl - (se+1)), se+1); fprintf(stderr, "\n"); } @@ -357,9 +312,6 @@ printMacro(MacroBuf mb, const char * s, const char * se) static void printExpansion(MacroBuf mb, const char * t, const char * te) { - const char *ellipsis; - int choplen; - if (!(te > t)) { rpmlog(RPMLOG_DEBUG, _("%3d<%*s(empty)\n"), mb->depth, (2 * mb->depth + 1), ""); return; @@ -368,7 +320,6 @@ printExpansion(MacroBuf mb, const char * t, const char * te) /* Shorten output which contains newlines */ while (te > t && iseol(te[-1])) te--; - ellipsis = ""; if (mb->depth > 0) { const char *tenl; @@ -376,17 +327,11 @@ printExpansion(MacroBuf mb, const char * t, const char * te) while ((tenl = strchr(t, '\n')) && tenl < te) t = ++tenl; - /* Limit expand output */ - choplen = 61 - (2 * mb->depth); - if ((te - t) > choplen) { - te = t + choplen; - ellipsis = "..."; - } } rpmlog(RPMLOG_DEBUG,"%3d<%*s", mb->depth, (2 * mb->depth + 1), ""); if (te > t) - rpmlog(RPMLOG_DEBUG, "%.*s%s", (int)(te - t), t, ellipsis); + rpmlog(RPMLOG_DEBUG, "%.*s", (int)(te - t), t); rpmlog(RPMLOG_DEBUG, "\n"); } @@ -400,14 +345,14 @@ printExpansion(MacroBuf mb, const char * t, const char * te) #define COPYNAME(_ne, _s, _c) \ { SKIPBLANK(_s,_c); \ - while(((_c) = *(_s)) && (risalnum(_c) || (_c) == '_')) \ + while (((_c) = *(_s)) && (risalnum(_c) || (_c) == '_')) \ *(_ne)++ = *(_s)++; \ *(_ne) = '\0'; \ } #define COPYOPTS(_oe, _s, _c) \ { \ - while(((_c) = *(_s)) && (_c) != ')') \ + while (((_c) = *(_s)) && (_c) != ')') \ *(_oe)++ = *(_s)++; \ *(_oe) = '\0'; \ } @@ -424,15 +369,18 @@ static int expandThis(MacroBuf mb, const char * src, size_t slen, char **target) { struct MacroBuf_s umb; - int rc; /* Copy other state from "parent", but we want a buffer of our own */ umb = *mb; umb.buf = NULL; - rc = expandMacro(&umb, src, slen); + umb.error = 0; + /* In case of error, flag it in the "parent"... */ + if (expandMacro(&umb, src, slen)) + mb->error = 1; *target = umb.buf; - return rc; + /* ...but return code for this operation specifically */ + return umb.error; } static void mbAppend(MacroBuf mb, char c) @@ -446,6 +394,7 @@ static void mbAppend(MacroBuf mb, char c) mb->nb--; } +#ifdef WITH_LUA static void mbAppendStr(MacroBuf mb, const char *str) { size_t len = strlen(str); @@ -457,68 +406,66 @@ static void mbAppendStr(MacroBuf mb, const char *str) mb->tpos += len; mb->nb -= len; } +#endif + /** * Expand output of shell command into target buffer. * @param mb macro expansion state * @param cmd shell command * @param clen no. bytes in shell command - * @return result of expansion */ -static int +static void doShellEscape(MacroBuf mb, const char * cmd, size_t clen) { char *buf = NULL; FILE *shf; - int rc = 0; int c; - rpmlog(RPMLOG_WARNING, _("Refusing to run shell code: %s\n"), cmd); - mbAppendStr(mb, "UNEXPANDEDSHELLSCRIPT"); -#if 0 - rc = expandThis(mb, cmd, clen, &buf); - if (rc) + if (expandThis(mb, cmd, clen, &buf)) goto exit; if ((shf = popen(buf, "r")) == NULL) { - rc = 1; + mb->error = 1; goto exit; } - while((c = fgetc(shf)) != EOF) { + + size_t tpos = mb->tpos; + while ((c = fgetc(shf)) != EOF) { mbAppend(mb, c); } (void) pclose(shf); - /* XXX delete trailing \r \n */ - while (iseol(mb->buf[mb->tpos-1])) { - mb->buf[mb->tpos--] = '\0'; + /* Delete trailing \r \n */ + while (mb->tpos > tpos && iseol(mb->buf[mb->tpos-1])) { + mb->buf[--mb->tpos] = '\0'; mb->nb++; } exit: _free(buf); -#endif - return rc; } /** * Parse (and execute) new macro definition. * @param mb macro expansion state * @param se macro definition to parse + * @param slen length of se argument * @param level macro recursion level * @param expandbody should body be expanded? * @return address to continue parsing */ static const char * -doDefine(MacroBuf mb, const char * se, int level, int expandbody) +doDefine(MacroBuf mb, const char * se, size_t slen, int level, int expandbody) { const char *s = se; - size_t blen = MACROBUFSIZ; - char *buf = xmalloc(blen); + char *buf = xmalloc(slen + 3); /* Some leeway for termination issues... */ char *n = buf, *ne = n; char *o = NULL, *oe; char *b, *be, *ebody = NULL; int c; int oc = ')'; + const char *sbody; /* as-is body start */ + int rc = 1; /* assume failure */ /* Copy name */ COPYNAME(ne, s, c); @@ -527,13 +474,20 @@ doDefine(MacroBuf mb, const char * se, int level, int expandbody) oe = ne + 1; if (*s == '(') { s++; /* skip ( */ - o = oe; - COPYOPTS(oe, s, oc); - s++; /* skip ) */ + /* Options must be terminated with ')' */ + if (strchr(s, ')')) { + o = oe; + COPYOPTS(oe, s, oc); + s++; /* skip ) */ + } else { + rpmlog(RPMLOG_ERR, _("Macro %%%s has unterminated opts\n"), n); + goto exit; + } } /* Copy body, skipping over escaped newlines */ b = be = oe + 1; + sbody = s; SKIPBLANK(s, c); if (c == '{') { /* XXX permit silent {...} grouping */ if ((se = matchchar(s, c, '}')) == NULL) { @@ -594,14 +548,8 @@ doDefine(MacroBuf mb, const char * se, int level, int expandbody) /* Names must start with alphabetic or _ and be at least 3 chars */ if (!((c = *n) && (risalpha(c) || c == '_') && (ne - n) > 2)) { - rpmlog(RPMLOG_ERR, - _("Macro %%%s has illegal name (%%define)\n"), n); - goto exit; - } - - /* Options must be terminated with ')' */ - if (o && oc != ')') { - rpmlog(RPMLOG_ERR, _("Macro %%%s has unterminated opts\n"), n); + rpmlog(RPMLOG_ERR, _("Macro %%%s has illegal name (%s)\n"), + n, expandbody ? "%global": "%define"); goto exit; } @@ -610,6 +558,9 @@ doDefine(MacroBuf mb, const char * se, int level, int expandbody) goto exit; } + if (!isblank(*sbody) && !(*sbody == '\\' && iseol(sbody[1]))) + rpmlog(RPMLOG_WARNING, _("Macro %%%s needs whitespace before body\n"), n); + if (expandbody) { if (expandThis(mb, b, 0, &ebody)) { rpmlog(RPMLOG_ERR, _("Macro %%%s failed to expand\n"), n); @@ -618,9 +569,12 @@ doDefine(MacroBuf mb, const char * se, int level, int expandbody) b = ebody; } - addMacro(mb->mc, n, o, b, (level - 1)); + pushMacro(mb->mc, n, o, b, level, ME_NONE); + rc = 0; exit: + if (rc) + mb->error = 1; _free(buf); _free(ebody); return se; @@ -628,15 +582,16 @@ exit: /** * Parse (and execute) macro undefinition. - * @param mc macro context + * @param mb macro expansion state * @param se macro name to undefine + * @param slen length of se argument * @return address to continue parsing */ static const char * -doUndefine(rpmMacroContext mc, const char * se) +doUndefine(MacroBuf mb, const char * se, size_t slen) { const char *s = se; - char *buf = xmalloc(MACROBUFSIZ); + char *buf = xmalloc(slen + 1); char *n = buf, *ne = n; int c; @@ -649,12 +604,12 @@ doUndefine(rpmMacroContext mc, const char * se) /* Names must start with alphabetic or _ and be at least 3 chars */ if (!((c = *n) && (risalpha(c) || c == '_') && (ne - n) > 2)) { - rpmlog(RPMLOG_ERR, - _("Macro %%%s has illegal name (%%undefine)\n"), n); + rpmlog(RPMLOG_ERR, _("Macro %%%s has illegal name (%%undefine)\n"), n); + mb->error = 1; goto exit; } - delMacro(mc, n); + popMacro(mb->mc, n); exit: _free(buf); @@ -662,58 +617,6 @@ exit: } /** - * Push new macro definition onto macro entry stack. - * @param mep address of macro entry slot - * @param n macro name - * @param o macro parameters (NULL if none) - * @param b macro body (NULL becomes "") - * @param level macro recursion level - */ -static void -pushMacro(rpmMacroEntry * mep, - const char * n, const char * o, - const char * b, int level) -{ - rpmMacroEntry prev = (mep && *mep ? *mep : NULL); - rpmMacroEntry me = (rpmMacroEntry) xmalloc(sizeof(*me)); - - me->prev = prev; - me->name = (prev ? prev->name : xstrdup(n)); - me->opts = (o ? xstrdup(o) : NULL); - me->body = xstrdup(b ? b : ""); - me->used = 0; - me->level = level; - if (mep) - *mep = me; - else - me = _free(me); -} - -/** - * Pop macro definition from macro entry stack. - * @param mep address of macro entry slot - */ -static void -popMacro(rpmMacroEntry * mep) -{ - if (mep && *mep) { - rpmMacroEntry me = *mep; - - /* restore previous definition of the macro */ - *mep = me->prev; - - /* name is shared between entries, only free if last of its kind */ - if (me->prev == NULL) - free(me->name); - free(me->opts); - free(me->body); - - memset(me, 0, sizeof(*me)); /* trash and burn */ - free(me); - } -} - -/** * Free parsed arguments for parameterized macro. * @param mb macro expansion state */ @@ -721,41 +624,60 @@ static void freeArgs(MacroBuf mb) { rpmMacroContext mc = mb->mc; - int ndeleted = 0; - int i; - - if (mc == NULL || mc->macroTable == NULL) - return; /* Delete dynamic macro definitions */ - for (i = 0; i < mc->firstFree; i++) { - rpmMacroEntry *mep, me; - int skiptest = 0; - mep = &mc->macroTable[i]; - me = *mep; - - if (me == NULL) /* XXX this should never happen */ - continue; - if (me->level < mb->depth) + for (int i = 0; i < mc->n; i++) { + rpmMacroEntry me = mc->tab[i]; + assert(me); + if (me->level < mb->level) continue; - if (strlen(me->name) == 1 && strchr("#*0", *me->name)) { - if (*me->name == '*' && me->used > 0) - skiptest = 1; /* XXX skip test for %# %* %0 */ - } else if (!skiptest && me->used <= 0) { -#if NOTYET - rpmlog(RPMLOG_ERR, - _("Macro %%%s (%s) was not used below level %d\n"), - me->name, me->body, me->level); -#endif + /* Warn on defined but unused non-automatic, scoped macros */ + if (!(me->flags & (ME_AUTO|ME_USED))) { + rpmlog(RPMLOG_WARNING, + _("Macro %%%s defined but not used within scope\n"), + me->name); + /* Only whine once */ + me->flags |= ME_USED; } - popMacro(mep); - if (!(mep && *mep)) - ndeleted++; + + /* compensate if the slot is to go away */ + if (me->prev == NULL) + i--; + popMacro(mc, me->name); } + mb->level--; +} - /* If any deleted macros, sort macro table */ - if (ndeleted) - sortMacroTable(mc); +static void splitQuoted(ARGV_t *av, const char * str, const char * seps) +{ + const int qchar = 0x1f; /* ASCII unit separator */ + const char *s = str; + const char *start = str; + int quoted = 0; + + while (start != NULL) { + if (!quoted && strchr(seps, *s)) { + size_t slen = s - start; + /* quoted arguments are always kept, otherwise skip empty args */ + if (slen > 0) { + char *d, arg[slen + 1]; + const char *t; + for (d = arg, t = start; t - start < slen; t++) { + if (*t == qchar) + continue; + *d++ = *t; + } + arg[d - arg] = '\0'; + argvAdd(av, arg); + } + start = s + 1; + } + if (*s == qchar) + quoted = !quoted; + else if (*s == '\0') + start = NULL; + s++; + } } /** @@ -771,31 +693,41 @@ static const char * grabArgs(MacroBuf mb, const rpmMacroEntry me, const char * se, const char * lastc) { + const char *cont = NULL; const char *opts; char *args = NULL; ARGV_t argv = NULL; int argc = 0; int c; - /* Copy macro name as argv[0] */ - argvAdd(&argv, me->name); - addMacro(mb->mc, "0", NULL, me->name, mb->depth); - /* + * Prepare list of call arguments, starting with macro name as argv[0]. * Make a copy of se up to lastc string that we can pass to argvSplit(). * Append the results to main argv. */ - { ARGV_t av = NULL; - char *s = xcalloc((lastc-se)+1, sizeof(*s)); - memcpy(s, se, (lastc-se)); + argvAdd(&argv, me->name); + if (lastc) { + int oescape = mb->escape; + char *s = NULL; - argvSplit(&av, s, " \t"); - argvAppend(&argv, av); + /* Expand possible macros in arguments */ + mb->escape = 1; + expandThis(mb, se, lastc-se, &s); + mb->escape = oescape; - argvFree(av); + splitQuoted(&argv, s, " \t"); free(s); + + cont = ((*lastc == '\0' || *lastc == '\n') && *(lastc-1) != '\\') ? + lastc : lastc + 1; } + /* Bump call depth on entry before first macro define */ + mb->level++; + + /* Setup macro name as %0 */ + pushMacro(mb->mc, "0", NULL, me->name, mb->level, ME_AUTO); + /* * The macro %* analoguous to the shell's $* means "Pass all non-macro * parameters." Consequently, there needs to be a macro that means "Pass all @@ -805,7 +737,7 @@ grabArgs(MacroBuf mb, const rpmMacroEntry me, const char * se, * This is the (potential) justification for %{**} ... */ args = argvJoin(argv + 1, " "); - addMacro(mb->mc, "**", NULL, args, mb->depth); + pushMacro(mb->mc, "**", NULL, args, mb->level, ME_AUTO); free(args); /* @@ -822,12 +754,13 @@ grabArgs(MacroBuf mb, const rpmMacroEntry me, const char * se, argc = argvCount(argv); /* Define option macros. */ - while((c = getopt(argc, argv, opts)) != -1) + while ((c = getopt(argc, argv, opts)) != -1) { char *name = NULL, *body = NULL; if (c == '?' || strchr(opts, c) == NULL) { rpmlog(RPMLOG_ERR, _("Unknown option %c in %s(%s)\n"), (char)optopt, me->name, opts); + mb->error = 1; goto exit; } @@ -837,13 +770,13 @@ grabArgs(MacroBuf mb, const rpmMacroEntry me, const char * se, } else { rasprintf(&body, "-%c", c); } - addMacro(mb->mc, name, NULL, body, mb->depth); + pushMacro(mb->mc, name, NULL, body, mb->level, ME_AUTO); free(name); free(body); if (optarg) { rasprintf(&name, "-%c*", c); - addMacro(mb->mc, name, NULL, optarg, mb->depth); + pushMacro(mb->mc, name, NULL, optarg, mb->level, ME_AUTO); free(name); } } @@ -851,7 +784,7 @@ grabArgs(MacroBuf mb, const rpmMacroEntry me, const char * se, /* Add argument count (remaining non-option items) as macro. */ { char *ac = NULL; rasprintf(&ac, "%d", (argc - optind)); - addMacro(mb->mc, "#", NULL, ac, mb->depth); + pushMacro(mb->mc, "#", NULL, ac, mb->level, ME_AUTO); free(ac); } @@ -860,41 +793,70 @@ grabArgs(MacroBuf mb, const rpmMacroEntry me, const char * se, for (c = optind; c < argc; c++) { char *name = NULL; rasprintf(&name, "%d", (c - optind + 1)); - addMacro(mb->mc, name, NULL, argv[c], mb->depth); + pushMacro(mb->mc, name, NULL, argv[c], mb->level, ME_AUTO); free(name); } } /* Add concatenated unexpanded arguments as yet another macro. */ args = argvJoin(argv + optind, " "); - addMacro(mb->mc, "*", NULL, args ? args : "", mb->depth); + pushMacro(mb->mc, "*", NULL, args ? args : "", mb->level, ME_AUTO); free(args); exit: argvFree(argv); - return *lastc ? lastc + 1 : lastc; + return cont; } /** * Perform macro message output * @param mb macro expansion state * @param waserror use rpmlog()? - * @param msg message to ouput + * @param msg message to output * @param msglen no. of bytes in message */ static void -doOutput(MacroBuf mb, int waserror, const char * msg, size_t msglen) +doOutput(MacroBuf mb, int loglevel, const char * msg, size_t msglen) { char *buf = NULL; (void) expandThis(mb, msg, msglen, &buf); - if (waserror) - rpmlog(RPMLOG_ERR, "%s\n", buf); - else - fprintf(stderr, "%s", buf); + rpmlog(loglevel, "%s\n", buf); _free(buf); } +static void doLua(MacroBuf mb, const char * f, size_t fn, const char * g, size_t gn) +{ +#ifdef WITH_LUA + rpmlua lua = NULL; /* Global state. */ + char *scriptbuf = xmalloc(gn + 1); + char *printbuf; + rpmMacroContext mc = mb->mc; + int odepth = mc->depth; + int olevel = mc->level; + + if (g != NULL && gn > 0) + memcpy(scriptbuf, g, gn); + scriptbuf[gn] = '\0'; + rpmluaPushPrintBuffer(lua); + mc->depth = mb->depth; + mc->level = mb->level; + if (rpmluaRunScript(lua, scriptbuf, NULL) == -1) + mb->error = 1; + mc->depth = odepth; + mc->level = olevel; + printbuf = rpmluaPopPrintBuffer(lua); + if (printbuf) { + mbAppendStr(mb, printbuf); + free(printbuf); + } + free(scriptbuf); +#else + rpmlog(RPMLOG_ERR, _("<lua> scriptlet support not built in\n")); + mb->error = 1; +#endif +} + /** * Execute macro primitives. * @param mb macro expansion state @@ -911,8 +873,14 @@ doFoo(MacroBuf mb, int negate, const char * f, size_t fn, char *buf = NULL; char *b = NULL, *be; int c; + int verbose = (rpmIsVerbose() != 0); + int expand = (g != NULL && gn > 0); + + /* Don't expand %{verbose:...} argument on false condition */ + if (STREQ("verbose", f, fn) && (verbose == negate)) + expand = 0; - if (g != NULL) { + if (expand) { (void) expandThis(mb, g, gn, &buf); } else { buf = xmalloc(MACROBUFSIZ + fn + gn); @@ -927,16 +895,38 @@ doFoo(MacroBuf mb, int negate, const char * f, size_t fn, if ((b = strrchr(buf, '/')) != NULL) *b = '\0'; b = buf; + } else if (STREQ("shrink", f, fn)) { + /* + * shrink body by removing all leading and trailing whitespaces and + * reducing intermediate whitespaces to a single space character. + */ + size_t i = 0, j = 0; + size_t buflen = strlen(buf); + int was_space = 0; + while (i < buflen) { + if (risspace(buf[i])) { + was_space = 1; + i++; + continue; + } else if (was_space) { + was_space = 0; + if (j > 0) /* remove leading blanks at all */ + buf[j++] = ' '; + } + buf[j++] = buf[i++]; + } + buf[j] = '\0'; + b = buf; + } else if (STREQ("quote", f, fn)) { + char *quoted = NULL; + rasprintf("ed, "%c%s%c", 0x1f, buf, 0x1f); + free(buf); + b = buf = quoted; } else if (STREQ("suffix", f, fn)) { if ((b = strrchr(buf, '.')) != NULL) b++; - } else if (STREQ("expand", f, fn)) { + } else if (STREQ("expand", f, fn) || STREQ("verbose", f, fn)) { b = buf; - } else if (STREQ("verbose", f, fn)) { - if (negate) - b = (rpmIsVerbose() ? NULL : buf); - else - b = (rpmIsVerbose() ? buf : NULL); } else if (STREQ("url2path", f, fn) || STREQ("u2p", f, fn)) { (void)urlPath(buf, (const char **)&b); if (*b == '\0') b = "/"; @@ -948,7 +938,7 @@ doFoo(MacroBuf mb, int negate, const char * f, size_t fn, be++; *be++ = '\0'; (void) rpmFileIsCompressed(b, &compressed); - switch(compressed) { + switch (compressed) { default: case COMPRESSED_NOT: sprintf(be, "%%__cat %s", b); @@ -975,6 +965,9 @@ doFoo(MacroBuf mb, int negate, const char * f, size_t fn, case COMPRESSED_7ZIP: sprintf(be, "%%__7zip x %s", b); break; + case COMPRESSED_ZSTD: + sprintf(be, "%%__zstd -dc %s", b); + break; } b = be; } else if (STREQ("getenv", f, fn)) { @@ -1013,6 +1006,7 @@ doFoo(MacroBuf mb, int negate, const char * f, size_t fn, * The main macro recursion loop. * @param mb macro expansion state * @param src string to expand + * @param slen length of string buffer * @return 0 on success, 1 on failure */ static int @@ -1025,13 +1019,19 @@ expandMacro(MacroBuf mb, const char *src, size_t slen) const char *g, *ge; size_t fn, gn, tpos; int c; - int rc = 0; int negate; const char * lastc; int chkexist; char *source = NULL; + int store_macro_trace; + int store_expand_trace; - /* Handle non-terminated substrings by creating a terminated copy */ + /* + * Always make a (terminated) copy of the source string. + * Beware: avoiding the copy when src is known \0-terminated seems like + * an obvious opportunity for optimization, but doing that breaks + * a special case of macro undefining itself. + */ if (!slen) slen = strlen(src); source = xmalloc(slen + 1); @@ -1040,36 +1040,41 @@ expandMacro(MacroBuf mb, const char *src, size_t slen) s = source; if (mb->buf == NULL) { - size_t blen = MACROBUFSIZ + strlen(s); - mb->buf = xcalloc(blen + 1, sizeof(*mb->buf)); + size_t blen = MACROBUFSIZ + slen; + mb->buf = xmalloc(blen + 1); + mb->buf[0] = '\0'; mb->tpos = 0; mb->nb = blen; } tpos = mb->tpos; /* save expansion pointer for printExpand */ + store_macro_trace = mb->macro_trace; + store_expand_trace = mb->expand_trace; if (++mb->depth > max_macro_depth) { rpmlog(RPMLOG_ERR, _("Too many levels of recursion in macro expansion. It is likely caused by recursive macro declaration.\n")); mb->depth--; mb->expand_trace = 1; - _free(source); - return 1; + mb->error = 1; + goto exit; } - while (rc == 0 && (c = *s) != '\0') { + while (mb->error == 0 && (c = *s) != '\0') { s++; /* Copy text until next macro */ - switch(c) { + switch (c) { case '%': - if (*s) { /* Ensure not end-of-string. */ - if (*s != '%') - break; - s++; /* skip first % in %% */ - } + if (*s) { /* Ensure not end-of-string. */ + if (*s != '%') + break; + s++; /* skip first % in %% */ + if (mb->escape) + mbAppend(mb, c); + } default: - mbAppend(mb, c); - continue; - break; + mbAppend(mb, c); + continue; + break; } /* Expand next macro */ @@ -1082,189 +1087,183 @@ expandMacro(MacroBuf mb, const char *src, size_t slen) chkexist = 0; switch ((c = *s)) { default: /* %name substitution */ - while (strchr("!?", *s) != NULL) { - switch(*s++) { - case '!': - negate = ((negate + 1) % 2); - break; - case '?': - chkexist++; - break; - } - } - f = se = s; - if (*se == '-') - se++; - while((c = *se) && (risalnum(c) || c == '_')) - se++; - /* Recognize non-alnum macros too */ - switch (*se) { - case '*': - se++; - if (*se == '*') se++; - break; - case '#': - se++; + while (*s != '\0' && strchr("!?", *s) != NULL) { + switch (*s++) { + case '!': + negate = ((negate + 1) % 2); break; - default: + case '?': + chkexist++; break; } - fe = se; - /* For "%name " macros ... */ - if ((c = *fe) && isblank(c)) - if ((lastc = strchr(fe,'\n')) == NULL) - lastc = strchr(fe, '\0'); + } + f = se = s; + if (*se == '-') + se++; + while ((c = *se) && (risalnum(c) || c == '_')) + se++; + /* Recognize non-alnum macros too */ + switch (*se) { + case '*': + se++; + if (*se == '*') se++; break; + case '#': + se++; + break; + default: + break; + } + fe = se; + /* For "%name " macros ... */ + if ((c = *fe) && isblank(c)) + if ((lastc = strchr(fe,'\n')) == NULL) + lastc = strchr(fe, '\0'); + break; case '(': /* %(...) shell escape */ - if ((se = matchchar(s, c, ')')) == NULL) { - rpmlog(RPMLOG_ERR, - _("Unterminated %c: %s\n"), (char)c, s); - rc = 1; - continue; - } - if (mb->macro_trace) - printMacro(mb, s, se+1); + if ((se = matchchar(s, c, ')')) == NULL) { + rpmlog(RPMLOG_ERR, _("Unterminated %c: %s\n"), (char)c, s); + mb->error = 1; + continue; + } + if (mb->macro_trace) + printMacro(mb, s, se+1); - s++; /* skip ( */ - rc = doShellEscape(mb, s, (se - s)); - se++; /* skip ) */ + s++; /* skip ( */ + doShellEscape(mb, s, (se - s)); + se++; /* skip ) */ - s = se; - continue; - break; + s = se; + continue; + break; case '{': /* %{...}/%{...:...} substitution */ - if ((se = matchchar(s, c, '}')) == NULL) { - rpmlog(RPMLOG_ERR, - _("Unterminated %c: %s\n"), (char)c, s); - rc = 1; - continue; - } - f = s+1;/* skip { */ - se++; /* skip } */ - while (strchr("!?", *f) != NULL) { - switch(*f++) { - case '!': - negate = ((negate + 1) % 2); - break; - case '?': - chkexist++; - break; - } - } - for (fe = f; (c = *fe) && !strchr(" :}", c);) - fe++; - switch (c) { - case ':': - g = fe + 1; - ge = se - 1; - break; - case ' ': - lastc = se-1; - break; - default: - break; + if ((se = matchchar(s, c, '}')) == NULL) { + rpmlog(RPMLOG_ERR, _("Unterminated %c: %s\n"), (char)c, s); + mb->error = 1; + continue; + } + f = s+1;/* skip { */ + se++; /* skip } */ + while (strchr("!?", *f) != NULL) { + switch (*f++) { + case '!': + negate = ((negate + 1) % 2); + break; + case '?': + chkexist++; + break; } + } + for (fe = f; (c = *fe) && !strchr(" :}", c);) + fe++; + switch (c) { + case ':': + g = fe + 1; + ge = se - 1; + break; + case ' ': + lastc = se-1; break; + default: + break; + } + break; } /* XXX Everything below expects fe > f */ fn = (fe - f); gn = (ge - g); if ((fe - f) <= 0) { -/* XXX Process % in unknown context */ - c = '%'; /* XXX only need to save % */ - mbAppend(mb, c); + /* XXX Process % in unknown context */ + c = '%'; /* XXX only need to save % */ + mbAppend(mb, c); #if 0 - rpmlog(RPMLOG_ERR, - _("A %% is followed by an unparseable macro\n")); + rpmlog(RPMLOG_ERR, + _("A %% is followed by an unparseable macro\n")); #endif - s = se; - continue; + s = se; + continue; } if (mb->macro_trace) - printMacro(mb, s, se); + printMacro(mb, s, se); /* Expand builtin macros */ + if (STREQ("load", f, fn)) { + char *arg = NULL; + if (g && gn > 0 && expandThis(mb, g, gn, &arg) == 0) { + /* Print failure iff %{load:...} or %{!?load:...} */ + if (loadMacroFile(mb->mc, arg) && chkexist == negate) { + rpmlog(RPMLOG_ERR, _("failed to load macro file %s"), arg); + mb->error = 1; + } + } + free(arg); + s = se; + continue; + } if (STREQ("global", f, fn)) { - s = doDefine(mb, se, RMIL_GLOBAL, 1); - continue; + s = doDefine(mb, se, slen - (se - s), RMIL_GLOBAL, 1); + continue; } if (STREQ("define", f, fn)) { - s = doDefine(mb, se, mb->depth, 0); - continue; + s = doDefine(mb, se, slen - (se - s), mb->level, 0); + continue; } if (STREQ("undefine", f, fn)) { - s = doUndefine(mb->mc, se); - continue; + s = doUndefine(mb, se, slen - (se - s)); + continue; } if (STREQ("echo", f, fn) || - STREQ("warn", f, fn) || - STREQ("error", f, fn)) { - int waserror = 0; - if (STREQ("error", f, fn)) - waserror = 1; - if (g != NULL && g < ge) - doOutput(mb, waserror, g, gn); - else - doOutput(mb, waserror, f, fn); - s = se; - continue; + STREQ("warn", f, fn) || + STREQ("error", f, fn)) { + int loglevel = RPMLOG_NOTICE; /* assume echo */ + if (STREQ("error", f, fn)) { + loglevel = RPMLOG_ERR; + mb->error = 1; + } else if (STREQ("warn", f, fn)) { + loglevel = RPMLOG_WARNING; + } + if (g != NULL && g < ge) + doOutput(mb, loglevel, g, gn); + else + doOutput(mb, loglevel, "", 0); + s = se; + continue; } if (STREQ("trace", f, fn)) { - /* XXX TODO restore expand_trace/macro_trace to 0 on return */ - mb->expand_trace = mb->macro_trace = (negate ? 0 : mb->depth); - if (mb->depth == 1) { - print_macro_trace = mb->macro_trace; - print_expand_trace = mb->expand_trace; - } - s = se; - continue; + /* XXX TODO restore expand_trace/macro_trace to 0 on return */ + mb->expand_trace = mb->macro_trace = (negate ? 0 : mb->depth); + if (mb->depth == 1) { + print_macro_trace = mb->macro_trace; + print_expand_trace = mb->expand_trace; + } + s = se; + continue; } if (STREQ("dump", f, fn)) { - rpmDumpMacroTable(mb->mc, NULL); - while (iseol(*se)) - se++; - s = se; - continue; + rpmDumpMacroTable(mb->mc, NULL); + while (iseol(*se)) + se++; + s = se; + continue; } -#ifdef WITH_LUA -#define LUA_FAILURE_OUTPUT "1" if (STREQ("lua", f, fn)) { - rpmlua lua = NULL; /* Global state. */ - const char *ls = s+sizeof("{lua:")-1; - const char *lse = se-sizeof("}")+1; - char *scriptbuf = (char *)xmalloc((lse-ls)+1); - char *printbuf; - memcpy(scriptbuf, ls, lse-ls); - scriptbuf[lse-ls] = '\0'; - rpmluaPushPrintBuffer(lua); - if (rpmluaRunScript(lua, scriptbuf, NULL) == -1) { - printbuf = rpmluaPopPrintBuffer(lua); - rpmlog(RPMLOG_WARNING, _("lua: using fallback output '%s'\n"), LUA_FAILURE_OUTPUT); - printbuf = (char *)xcalloc(1, sizeof(LUA_FAILURE_OUTPUT)); - strcpy(printbuf, LUA_FAILURE_OUTPUT); - } else { - printbuf = rpmluaPopPrintBuffer(lua); - } - if (printbuf) { - mbAppendStr(mb, printbuf); - free(printbuf); - } - free(scriptbuf); - s = se; - continue; + doLua(mb, f, fn, g, gn); + s = se; + continue; } -#endif /* XXX necessary but clunky */ if (STREQ("basename", f, fn) || STREQ("dirname", f, fn) || + STREQ("shrink", f, fn) || STREQ("suffix", f, fn) || + STREQ("quote", f, fn) || STREQ("expand", f, fn) || STREQ("verbose", f, fn) || STREQ("uncompress", f, fn) || @@ -1274,110 +1273,116 @@ expandMacro(MacroBuf mb, const char *src, size_t slen) STREQ("getconfdir", f, fn) || STREQ("S", f, fn) || STREQ("P", f, fn) || - STREQ("F", f, fn)) { - /* FIX: verbose may be set */ - doFoo(mb, negate, f, fn, g, gn); - s = se; - continue; + STREQ("F", f, fn)) + { + /* FIX: verbose may be set */ + doFoo(mb, negate, f, fn, g, gn); + s = se; + continue; } /* Expand defined macros */ - mep = findEntry(mb->mc, f, fn); + mep = findEntry(mb->mc, f, fn, NULL); me = (mep ? *mep : NULL); + if (me) { + if ((me->flags & ME_AUTO) && mb->level > me->level) { + /* Ignore out-of-scope automatic macros */ + me = NULL; + } else { + /* If we looked up a macro, consider it used */ + me->flags |= ME_USED; + } + } + /* XXX Special processing for flags */ if (*f == '-') { - if (me) - me->used++; /* Mark macro as used */ - if ((me == NULL && !negate) || /* Without -f, skip %{-f...} */ + if ((me == NULL && !negate) || /* Without -f, skip %{-f...} */ (me != NULL && negate)) { /* With -f, skip %{!-f...} */ - s = se; - continue; - } + s = se; + continue; + } - if (g && g < ge) { /* Expand X in %{-f:X} */ - rc = expandMacro(mb, g, gn); - } else + if (g && g < ge) { /* Expand X in %{-f:X} */ + expandMacro(mb, g, gn); + } else if (me && me->body && *me->body) {/* Expand %{-f}/%{-f*} */ - rc = expandMacro(mb, me->body, 0); + expandMacro(mb, me->body, 0); } - s = se; - continue; + s = se; + continue; } /* XXX Special processing for macro existence */ if (chkexist) { - if ((me == NULL && !negate) || /* Without -f, skip %{?f...} */ + if ((me == NULL && !negate) || /* Without -f, skip %{?f...} */ (me != NULL && negate)) { /* With -f, skip %{!?f...} */ - s = se; - continue; - } - if (g && g < ge) { /* Expand X in %{?f:X} */ - rc = expandMacro(mb, g, gn); - } else - if (me && me->body && *me->body) { /* Expand %{?f}/%{?f*} */ - rc = expandMacro(mb, me->body, 0); - } s = se; continue; + } + if (g && g < ge) { /* Expand X in %{?f:X} */ + expandMacro(mb, g, gn); + } else + if (me && me->body && *me->body) { /* Expand %{?f}/%{?f*} */ + expandMacro(mb, me->body, 0); + } + s = se; + continue; } if (me == NULL) { /* leave unknown %... as is */ - /* XXX hack to permit non-overloaded %foo to be passed */ - c = '%'; /* XXX only need to save % */ - mbAppend(mb, c); - continue; + /* XXX hack to permit non-overloaded %foo to be passed */ + c = '%'; /* XXX only need to save % */ + mbAppend(mb, c); + continue; } /* Setup args for "%name " macros with opts */ if (me && me->opts != NULL) { - if (lastc != NULL) { - se = grabArgs(mb, me, fe, lastc); - } else { - addMacro(mb->mc, "**", NULL, "", mb->depth); - addMacro(mb->mc, "*", NULL, "", mb->depth); - addMacro(mb->mc, "#", NULL, "0", mb->depth); - addMacro(mb->mc, "0", NULL, me->name, mb->depth); - } + const char *xe = grabArgs(mb, me, fe, lastc); + if (xe != NULL) + se = xe; } /* Recursively expand body of macro */ if (me->body && *me->body) { - rc = expandMacro(mb, me->body, 0); - if (rc == 0) - me->used++; /* Mark macro as used */ + expandMacro(mb, me->body, 0); } /* Free args for "%name " macros with opts */ if (me->opts != NULL) - freeArgs(mb); + freeArgs(mb); s = se; } mb->buf[mb->tpos] = '\0'; mb->depth--; - if (rc != 0 || mb->expand_trace) + if (mb->error != 0 || mb->expand_trace) printExpansion(mb, mb->buf+tpos, mb->buf+mb->tpos); + mb->macro_trace = store_macro_trace; + mb->expand_trace = store_expand_trace; +exit: _free(source); - return rc; + return mb->error; } /* =============================================================== */ -static int doExpandMacros(rpmMacroContext mc, const char *src, char **target) +static int doExpandMacros(rpmMacroContext mc, const char *src, int flags, + char **target) { MacroBuf mb = xcalloc(1, sizeof(*mb)); int rc = 0; - if (mc == NULL) mc = rpmGlobalMacroContext; - mb->buf = NULL; - mb->depth = 0; + mb->depth = mc->depth; + mb->level = mc->level; mb->macro_trace = print_macro_trace; mb->expand_trace = print_expand_trace; mb->mc = mc; + mb->flags = flags; rc = expandMacro(mb, src, 0); @@ -1389,91 +1394,111 @@ static int doExpandMacros(rpmMacroContext mc, const char *src, char **target) return rc; } -int expandMacros(void * spec, rpmMacroContext mc, char * sbuf, size_t slen) +static void pushMacro(rpmMacroContext mc, + const char * n, const char * o, const char * b, int level, int flags) { - char *target = NULL; - int rc = doExpandMacros(mc, sbuf, &target); - rstrlcpy(sbuf, target, slen); - free(target); - return rc; -} - -void -addMacro(rpmMacroContext mc, - const char * n, const char * o, const char * b, int level) -{ - rpmMacroEntry * mep; - - if (mc == NULL) mc = rpmGlobalMacroContext; - - /* If new name, expand macro table */ - if ((mep = findEntry(mc, n, 0)) == NULL) { - if (mc->firstFree == mc->macrosAllocated) - expandMacroTable(mc); - if (mc->macroTable != NULL) - mep = mc->macroTable + mc->firstFree++; + /* new entry */ + rpmMacroEntry me; + /* pointer into me */ + char *p; + /* calculate sizes */ + size_t olen = o ? strlen(o) : 0; + size_t blen = b ? strlen(b) : 0; + size_t mesize = sizeof(*me) + blen + 1 + (olen ? olen + 1 : 0); + + size_t pos; + rpmMacroEntry *mep = findEntry(mc, n, 0, &pos); + if (mep) { + /* entry with shared name */ + me = xmalloc(mesize); + /* copy body */ + me->body = p = me->arena; + if (blen) + memcpy(p, b, blen + 1); + else + *p = '\0'; + p += blen + 1; + /* set name */ + me->name = (*mep)->name; } - - if (mep != NULL) { - /* Push macro over previous definition */ - pushMacro(mep, n, o, b, level); - - /* If new name, sort macro table */ - if ((*mep)->prev == NULL) - sortMacroTable(mc); + else { + /* extend macro table */ + const int delta = 256; + if (mc->n % delta == 0) + mc->tab = xrealloc(mc->tab, sizeof(me) * (mc->n + delta)); + /* shift pos+ entries to the right */ + memmove(mc->tab + pos + 1, mc->tab + pos, sizeof(me) * (mc->n - pos)); + mc->n++; + /* make slot */ + mc->tab[pos] = NULL; + mep = &mc->tab[pos]; + /* entry with new name */ + size_t nlen = strlen(n); + me = xmalloc(mesize + nlen + 1); + /* copy body */ + me->body = p = me->arena; + if (blen) + memcpy(p, b, blen + 1); + else + *p = '\0'; + p += blen + 1; + /* copy name */ + me->name = memcpy(p, n, nlen + 1); + p += nlen + 1; } + + /* copy options */ + if (olen) + me->opts = memcpy(p, o, olen + 1); + else + me->opts = o ? "" : NULL; + /* initialize */ + me->flags = flags; + me->flags &= ~(ME_USED); + me->level = level; + /* push over previous definition */ + me->prev = *mep; + *mep = me; } -void -delMacro(rpmMacroContext mc, const char * n) +static void popMacro(rpmMacroContext mc, const char * n) { - rpmMacroEntry * mep; - - if (mc == NULL) mc = rpmGlobalMacroContext; - /* If name exists, pop entry */ - if ((mep = findEntry(mc, n, 0)) != NULL) { - popMacro(mep); - /* If deleted name, sort macro table */ - if (!(mep && *mep)) - sortMacroTable(mc); + size_t pos; + rpmMacroEntry *mep = findEntry(mc, n, 0, &pos); + if (mep == NULL) + return; + /* parting entry */ + rpmMacroEntry me = *mep; + assert(me); + /* detach/pop definition */ + mc->tab[pos] = me->prev; + /* shrink macro table */ + if (me->prev == NULL) { + mc->n--; + /* move pos+ elements to the left */ + memmove(mc->tab + pos, mc->tab + pos + 1, sizeof(me) * (mc->n - pos)); + /* deallocate */ + if (mc->n == 0) + mc->tab = _free(mc->tab); } + /* comes in a single chunk */ + free(me); } -int -rpmDefineMacro(rpmMacroContext mc, const char * macro, int level) +static int defineMacro(rpmMacroContext mc, const char * macro, int level) { MacroBuf mb = xcalloc(1, sizeof(*mb)); + int rc; /* XXX just enough to get by */ - mb->mc = (mc ? mc : rpmGlobalMacroContext); - (void) doDefine(mb, macro, level, 0); + mb->mc = mc; + (void) doDefine(mb, macro, strlen(macro), level, 0); + rc = mb->error; _free(mb); - return 0; -} - -void -rpmLoadMacros(rpmMacroContext mc, int level) -{ - - if (mc == NULL || mc == rpmGlobalMacroContext) - return; - - if (mc->macroTable != NULL) { - int i; - for (i = 0; i < mc->firstFree; i++) { - rpmMacroEntry *mep, me; - mep = &mc->macroTable[i]; - me = *mep; - - if (me == NULL) /* XXX this should never happen */ - continue; - addMacro(NULL, me->name, me->opts, me->body, (level - 1)); - } - } + return rc; } -int -rpmLoadMacroFile(rpmMacroContext mc, const char * fn) +static int loadMacroFile(rpmMacroContext mc, const char * fn) { FILE *fd = fopen(fn, "r"); size_t blen = MACROBUFSIZ; @@ -1483,11 +1508,8 @@ rpmLoadMacroFile(rpmMacroContext mc, const char * fn) if (fd == NULL) goto exit; - /* XXX Assume new fangled macro expansion */ - max_macro_depth = 16; - buf[0] = '\0'; - while(rdcl(buf, blen, fd) != NULL) { + while (rdcl(buf, blen, fd) != NULL) { char c, *n; n = buf; @@ -1496,8 +1518,9 @@ rpmLoadMacroFile(rpmMacroContext mc, const char * fn) if (c != '%') continue; n++; /* skip % */ - rc = rpmDefineMacro(mc, n, RMIL_MACROFILES); + rc = defineMacro(mc, n, RMIL_MACROFILES); } + rc = fclose(fd); exit: @@ -1505,15 +1528,124 @@ exit: return rc; } +static void copyMacros(rpmMacroContext src, rpmMacroContext dst, int level) +{ + for (int i = 0; i < src->n; i++) { + rpmMacroEntry me = src->tab[i]; + assert(me); + pushMacro(dst, me->name, me->opts, me->body, level, me->flags); + } +} + +/* External interfaces */ + +int rpmExpandMacros(rpmMacroContext mc, const char * sbuf, char ** obuf, int flags) +{ + char *target = NULL; + int rc; + + mc = rpmmctxAcquire(mc); + rc = doExpandMacros(mc, sbuf, flags, &target); + rpmmctxRelease(mc); + + if (rc) { + free(target); + return -1; + } else { + *obuf = target; + return 1; + } +} + +void +rpmDumpMacroTable(rpmMacroContext mc, FILE * fp) +{ + mc = rpmmctxAcquire(mc); + if (fp == NULL) fp = stderr; + + fprintf(fp, "========================\n"); + for (int i = 0; i < mc->n; i++) { + rpmMacroEntry me = mc->tab[i]; + assert(me); + fprintf(fp, "%3d%c %s", me->level, + ((me->flags & ME_USED) ? '=' : ':'), me->name); + if (me->opts && *me->opts) + fprintf(fp, "(%s)", me->opts); + if (me->body && *me->body) + fprintf(fp, "\t%s", me->body); + fprintf(fp, "\n"); + } + fprintf(fp, _("======================== active %d empty %d\n"), + mc->n, 0); + rpmmctxRelease(mc); +} + +int rpmPushMacro(rpmMacroContext mc, + const char * n, const char * o, const char * b, int level) +{ + mc = rpmmctxAcquire(mc); + pushMacro(mc, n, o, b, level, ME_NONE); + rpmmctxRelease(mc); + return 0; +} + +int rpmPopMacro(rpmMacroContext mc, const char * n) +{ + mc = rpmmctxAcquire(mc); + popMacro(mc, n); + rpmmctxRelease(mc); + return 0; +} + +int +rpmDefineMacro(rpmMacroContext mc, const char * macro, int level) +{ + int rc; + mc = rpmmctxAcquire(mc); + rc = defineMacro(mc, macro, level); + rpmmctxRelease(mc); + return rc; +} + +void +rpmLoadMacros(rpmMacroContext mc, int level) +{ + rpmMacroContext gmc; + if (mc == NULL || mc == rpmGlobalMacroContext) + return; + + gmc = rpmmctxAcquire(NULL); + mc = rpmmctxAcquire(mc); + + copyMacros(mc, gmc, level); + + rpmmctxRelease(mc); + rpmmctxRelease(gmc); +} + +int +rpmLoadMacroFile(rpmMacroContext mc, const char * fn) +{ + int rc; + + mc = rpmmctxAcquire(mc); + rc = loadMacroFile(mc, fn); + rpmmctxRelease(mc); + + return rc; +} + void rpmInitMacros(rpmMacroContext mc, const char * macrofiles) { ARGV_t pattern, globs = NULL; + rpmMacroContext climc; if (macrofiles == NULL) return; argvSplit(&globs, macrofiles, ":"); + mc = rpmmctxAcquire(mc); for (pattern = globs; *pattern; pattern++) { ARGV_t path, files = NULL; @@ -1529,31 +1661,30 @@ rpmInitMacros(rpmMacroContext mc, const char * macrofiles) rpmFileHasSuffix(*path, ".rpmorig")) { continue; } - (void) rpmLoadMacroFile(mc, *path); + (void) loadMacroFile(mc, *path); } argvFree(files); } argvFree(globs); /* Reload cmdline macros */ - rpmLoadMacros(rpmCLIMacroContext, RMIL_CMDLINE); + climc = rpmmctxAcquire(rpmCLIMacroContext); + copyMacros(climc, mc, RMIL_CMDLINE); + rpmmctxRelease(climc); + + rpmmctxRelease(mc); } void rpmFreeMacros(rpmMacroContext mc) { - - if (mc == NULL) mc = rpmGlobalMacroContext; - - if (mc->macroTable != NULL) { - for (int i = 0; i < mc->firstFree; i++) { - while (mc->macroTable[i] != NULL) { - popMacro(&mc->macroTable[i]); - } - } - free(mc->macroTable); + mc = rpmmctxAcquire(mc); + while (mc->n > 0) { + /* remove from the end to avoid memmove */ + rpmMacroEntry me = mc->tab[mc->n - 1]; + popMacro(mc, me->name); } - memset(mc, 0, sizeof(*mc)); + rpmmctxRelease(mc); } char * @@ -1564,6 +1695,7 @@ rpmExpand(const char *arg, ...) char *pe; const char *s; va_list ap; + rpmMacroContext mc; if (arg == NULL) { ret = xstrdup(""); @@ -1584,7 +1716,9 @@ rpmExpand(const char *arg, ...) pe = stpcpy(pe, s); va_end(ap); - (void) doExpandMacros(NULL, buf, &ret); + mc = rpmmctxAcquire(NULL); + (void) doExpandMacros(mc, buf, 0, &ret); + rpmmctxRelease(mc); free(buf); exit: diff --git a/rpmio/rpmbase64.h b/rpmio/rpmbase64.h index 95ee9cd03..3858cc331 100644 --- a/rpmio/rpmbase64.h +++ b/rpmio/rpmbase64.h @@ -1,6 +1,12 @@ /* base64 encoder/decoder based on public domain implementation * by Chris Venter */ +/** \ingroup rpmio + * \file rpmio/rpmbase64.h + * + * Base64 encoding and decoding API + */ + #include <sys/types.h> #ifdef __cplusplus diff --git a/rpmio/rpmfileutil.c b/rpmio/rpmfileutil.c index 6cdfe2552..9e47ff8a5 100644 --- a/rpmio/rpmfileutil.c +++ b/rpmio/rpmfileutil.c @@ -19,6 +19,7 @@ #include <errno.h> #include <popt.h> #include <ctype.h> +#include <pthread.h> #include <rpm/rpmfileutil.h> #include <rpm/rpmurl.h> @@ -31,6 +32,7 @@ #include "debug.h" static const char *rpm_config_dir = NULL; +static pthread_once_t configDirSet = PTHREAD_ONCE_INIT; static int is_prelinked(int fdno) { @@ -103,28 +105,38 @@ static int open_dso(const char * path, pid_t * pidp, rpm_loff_t *fsizep) if (pidp != NULL && is_prelinked(fdno)) { int pipes[2]; pid_t pid; - int xx; - xx = close(fdno); + close(fdno); pipes[0] = pipes[1] = -1; - xx = pipe(pipes); - if (!(pid = fork())) { + if (pipe(pipes) < 0) + return -1; + + pid = fork(); + if (pid < 0) { + close(pipes[0]); + close(pipes[1]); + return -1; + } + + if (pid == 0) { ARGV_t av, lib; + int dfd; argvSplit(&av, cmd, " "); - xx = close(pipes[0]); - xx = dup2(pipes[1], STDOUT_FILENO); - xx = close(pipes[1]); - if ((lib = argvSearch(av, "library", NULL)) != NULL) { + close(pipes[0]); + dfd = dup2(pipes[1], STDOUT_FILENO); + close(pipes[1]); + if (dfd >= 0 && (lib = argvSearch(av, "library", NULL)) != NULL) { *lib = (char *) path; unsetenv("MALLOC_CHECK_"); - xx = execve(av[0], av+1, environ); + execve(av[0], av+1, environ); } - _exit(127); + _exit(127); /* not normally reached */ + } else { + *pidp = pid; + fdno = pipes[0]; + close(pipes[1]); } - *pidp = pid; - fdno = pipes[0]; - xx = close(pipes[1]); } return fdno; @@ -136,8 +148,8 @@ int rpmDoDigest(int algo, const char * fn,int asAscii, const char * path; urltype ut = urlPath(fn, &path); unsigned char * dig = NULL; - size_t diglen; - unsigned char buf[32*BUFSIZ]; + size_t diglen, buflen = 32 * BUFSIZ; + unsigned char *buf = xmalloc(buflen); FD_t fd; rpm_loff_t fsize = 0; pid_t pid = 0; @@ -150,7 +162,7 @@ int rpmDoDigest(int algo, const char * fn,int asAscii, goto exit; } - switch(ut) { + switch (ut) { case URL_IS_PATH: case URL_IS_UNKNOWN: case URL_IS_HTTPS: @@ -171,10 +183,10 @@ int rpmDoDigest(int algo, const char * fn,int asAscii, fdInitDigest(fd, algo, 0); fsize = 0; - while ((rc = Fread(buf, sizeof(buf[0]), sizeof(buf), fd)) > 0) + while ((rc = Fread(buf, sizeof(*buf), buflen, fd)) > 0) fsize += rc; fdFiniDigest(fd, algo, (void **)&dig, &diglen, asAscii); - if (Ferror(fd)) + if (dig == NULL || Ferror(fd)) rc = 1; (void) Fclose(fd); @@ -195,6 +207,7 @@ exit: if (!rc) memcpy(digest, dig, diglen); dig = _free(dig); + free(buf); return rc; } @@ -326,7 +339,8 @@ int rpmFileIsCompressed(const char * file, rpmCompressedMagic * compressed) rc = 0; - if ((magic[0] == 'B') && (magic[1] == 'Z')) { + if ((magic[0] == 'B') && (magic[1] == 'Z') && + (magic[2] == 'h')) { *compressed = COMPRESSED_BZIP2; } else if ((magic[0] == 'P') && (magic[1] == 'K') && (((magic[2] == 3) && (magic[3] == 4)) || @@ -337,6 +351,9 @@ int rpmFileIsCompressed(const char * file, rpmCompressedMagic * compressed) (magic[4] == 0x5a) && (magic[5] == 0x00)) { /* new style xz (lzma) with magic */ *compressed = COMPRESSED_XZ; + } else if ((magic[0] == 0x28) && (magic[1] == 0x85) && + (magic[2] == 0x2f) ) { + *compressed = COMPRESSED_ZSTD; } else if ((magic[0] == 'L') && (magic[1] == 'Z') && (magic[2] == 'I') && (magic[3] == 'P')) { *compressed = COMPRESSED_LZIP; @@ -356,6 +373,8 @@ int rpmFileIsCompressed(const char * file, rpmCompressedMagic * compressed) *compressed = COMPRESSED_7ZIP; } else if (rpmFileHasSuffix(file, ".lzma")) { *compressed = COMPRESSED_LZMA; + } else if (rpmFileHasSuffix(file, ".gem")) { + *compressed = COMPRESSED_GEM; } return rc; @@ -375,7 +394,7 @@ char *rpmCleanPath(char * path) s = t = te = path; while (*s != '\0') { /*fprintf(stderr, "*** got \"%.*s\"\trest \"%s\"\n", (t-path), path, s); */ - switch(*s) { + switch (*s) { case ':': /* handle url's */ if (s[1] == '/' && s[2] == '/') { *t++ = *s++; @@ -599,11 +618,14 @@ int rpmMkdirs(const char *root, const char *pathstr) return rc; } +static void setConfigDir(void) +{ + char *rpmenv = getenv("RPM_CONFIGDIR"); + rpm_config_dir = rpmenv ? xstrdup(rpmenv) : RPMCONFIGDIR; +} + const char *rpmConfigDir(void) { - if (rpm_config_dir == NULL) { - char *rpmenv = getenv("RPM_CONFIGDIR"); - rpm_config_dir = rpmenv ? xstrdup(rpmenv) : RPMCONFIGDIR; - } + pthread_once(&configDirSet, setConfigDir); return rpm_config_dir; } diff --git a/rpmio/rpmfileutil.h b/rpmio/rpmfileutil.h index 79fac8699..916f6b240 100644 --- a/rpmio/rpmfileutil.h +++ b/rpmio/rpmfileutil.h @@ -26,7 +26,9 @@ typedef enum rpmCompressedMagic_e { COMPRESSED_XZ = 5, /*!< xz can handle */ COMPRESSED_LZIP = 6, /*!< lzip can handle */ COMPRESSED_LRZIP = 7, /*!< lrzip can handle */ - COMPRESSED_7ZIP = 8 /*!< 7zip can handle */ + COMPRESSED_7ZIP = 8, /*!< 7zip can handle */ + COMPRESSED_GEM = 9, /*!< gem can handle */ + COMPRESSED_ZSTD = 10 /*!< zstd can handle */ } rpmCompressedMagic; /** \ingroup rpmfileutil diff --git a/rpmio/rpmglob.c b/rpmio/rpmglob.c index 4fc106daf..da5493d75 100644 --- a/rpmio/rpmglob.c +++ b/rpmio/rpmglob.c @@ -124,30 +124,15 @@ static inline const char *next_brace_sub(const char *begin) unsigned int depth = 0; const char *cp = begin; - while (1) { - if (depth == 0) { - if (*cp != ',' && *cp != '}' && *cp != '\0') { - if (*cp == '{') - ++depth; - ++cp; - continue; - } - } else { - while (*cp != '\0' && (*cp != '}' || depth > 0)) { - if (*cp == '}') - --depth; - ++cp; - } - if (*cp == '\0') - /* An incorrectly terminated brace expression. */ - return NULL; + while (*cp != '\0') { + if ((*cp == '}' && depth-- == 0) || (*cp == ',' && depth == 0)) + break; - continue; - } - break; + if (*cp++ == '{') + depth++; } - return cp; + return *cp != '\0' ? cp : NULL; } static int __glob_pattern_p(const char *pattern, int quote); @@ -352,7 +337,7 @@ glob(const char *pattern, int flags, user_name = dirname + 1; else { char *newp; - newp = (char *) alloca(end_name - dirname); + newp = (char *) alloca(end_name - dirname + 1); *((char *) mempcpy(newp, dirname + 1, end_name - dirname)) = '\0'; user_name = newp; @@ -649,7 +634,7 @@ static int prefix_array(const char *dirname, char **array, size_t n) static int __glob_pattern_p(const char *pattern, int quote) { register const char *p; - int open = 0; + int openBrackets = 0; for (p = pattern; *p != '\0'; ++p) switch (*p) { @@ -663,11 +648,11 @@ static int __glob_pattern_p(const char *pattern, int quote) break; case '[': - open = 1; + openBrackets = 1; break; case ']': - if (open) + if (openBrackets) return 1; break; } @@ -844,6 +829,8 @@ int rpmGlob(const char * patterns, int * argcPtr, ARGV_t * argvPtr) int i, j; int rc; + gflags |= GLOB_BRACE; + if (home != NULL && strlen(home) > 0) gflags |= GLOB_TILDE; @@ -966,5 +953,32 @@ exit: int rpmIsGlob(const char * pattern, int quote) { - return __glob_pattern_p(pattern, quote); + if (!__glob_pattern_p(pattern, quote)) { + + const char *begin; + const char *next; + const char *rest; + + begin = strchr(pattern, '{'); + if (begin == NULL) + return 0; + /* + * Find the first sub-pattern and at the same time find the + * rest after the closing brace. + */ + next = next_brace_sub(begin + 1); + if (next == NULL) + return 0; + + /* Now find the end of the whole brace expression. */ + rest = next; + while (*rest != '}') { + rest = next_brace_sub(rest + 1); + if (rest == NULL) + return 0; + } + /* Now we can be sure that brace expression is well-foermed. */ + } + + return 1; } diff --git a/rpmio/rpmio.c b/rpmio/rpmio.c index cd223e829..57df81a9d 100644 --- a/rpmio/rpmio.c +++ b/rpmio/rpmio.c @@ -5,6 +5,11 @@ #include "system.h" #include <stdarg.h> #include <errno.h> +#include <ctype.h> +#if defined(__linux__) +#include <sys/personality.h> +#endif +#include <sys/utsname.h> #include <rpm/rpmlog.h> #include <rpm/rpmmacro.h> @@ -16,11 +21,17 @@ #include "debug.h" -typedef struct _FDSTACK_s { +typedef struct FDSTACK_s * FDSTACK_t; + +struct FDSTACK_s { FDIO_t io; void * fp; int fdno; -} FDSTACK_t; + int syserrno; /* last system errno encountered */ + const char *errcookie; /* pointer to custom error string */ + + FDSTACK_t prev; +}; /** \ingroup rpmio * Cumulative statistics for a descriptor. @@ -38,13 +49,9 @@ struct _FD_s { #define RPMIO_DEBUG_IO 0x40000000 int magic; #define FDMAGIC 0x04463138 - int nfps; - FDSTACK_t fps[8]; + FDSTACK_t fps; int urlType; /* ufdio: */ - int syserrno; /* last system errno encountered */ - const char *errcookie; /* gzdio/bzdio/ufdio/xzdio: */ - char *descr; /* file name (or other description) */ FDSTAT_t stats; /* I/O statistics */ @@ -57,51 +64,37 @@ struct _FD_s { #define DBGIO(_f, _x) DBG((_f), RPMIO_DEBUG_IO, _x) -static FDIO_t fdGetIo(FD_t fd) -{ - return (fd != NULL) ? fd->fps[fd->nfps].io : NULL; -} - -static void fdSetIo(FD_t fd, FDIO_t io) -{ - if (fd) - fd->fps[fd->nfps].io = io; -} - -static void * fdGetFp(FD_t fd) -{ - return (fd != NULL) ? fd->fps[fd->nfps].fp : NULL; -} - -static void fdSetFp(FD_t fd, void * fp) +static FDSTACK_t fdGetFps(FD_t fd) { - if (fd) - fd->fps[fd->nfps].fp = fp; + return (fd != NULL) ? fd->fps : NULL; } static void fdSetFdno(FD_t fd, int fdno) { if (fd) - fd->fps[fd->nfps].fdno = fdno; + fd->fps->fdno = fdno; } static void fdPush(FD_t fd, FDIO_t io, void * fp, int fdno) { - if (fd == NULL || fd->nfps >= (sizeof(fd->fps)/sizeof(fd->fps[0]) - 1)) - return; - fd->nfps++; - fdSetIo(fd, io); - fdSetFp(fd, fp); - fdSetFdno(fd, fdno); + FDSTACK_t fps = xcalloc(1, sizeof(*fps)); + fps->io = io; + fps->fp = fp; + fps->fdno = fdno; + fps->prev = fd->fps; + + fd->fps = fps; + fdLink(fd); } -static void fdPop(FD_t fd) +static FDSTACK_t fdPop(FD_t fd) { - if (fd == NULL || fd->nfps < 0) return; - fdSetIo(fd, NULL); - fdSetFp(fd, NULL); - fdSetFdno(fd, -1); - fd->nfps--; + FDSTACK_t fps = fd->fps; + fd->fps = fps->prev; + free(fps); + fps = fd->fps; + fdFree(fd); + return fps; } void fdSetBundle(FD_t fd, rpmDigestBundle bundle) @@ -110,77 +103,67 @@ void fdSetBundle(FD_t fd, rpmDigestBundle bundle) fd->digests = bundle; } -rpmDigestBundle fdGetBundle(FD_t fd) -{ - return (fd != NULL) ? fd->digests : NULL; -} - -static void * iotFileno(FD_t fd, FDIO_t iot) +rpmDigestBundle fdGetBundle(FD_t fd, int create) { - void * rc = NULL; - - if (fd == NULL) - return NULL; - - for (int i = fd->nfps; i >= 0; i--) { - FDSTACK_t * fps = &fd->fps[i]; - if (fps->io != iot) - continue; - rc = fps->fp; - break; + rpmDigestBundle bundle = NULL; + if (fd) { + if (fd->digests == NULL && create) + fd->digests = rpmDigestBundleNew(); + bundle = fd->digests; } - - return rc; + return bundle; } /** \ingroup rpmio * \name RPMIO Vectors. */ -typedef ssize_t (*fdio_read_function_t) (FD_t fd, void *buf, size_t nbytes); -typedef ssize_t (*fdio_write_function_t) (FD_t fd, const void *buf, size_t nbytes); -typedef int (*fdio_seek_function_t) (FD_t fd, off_t pos, int whence); -typedef int (*fdio_close_function_t) (FD_t fd); -typedef FD_t (*fdio_ref_function_t) (FD_t fd); -typedef FD_t (*fdio_deref_function_t) (FD_t fd); -typedef FD_t (*fdio_new_function_t) (const char *descr); -typedef int (*fdio_fileno_function_t) (FD_t fd); +typedef ssize_t (*fdio_read_function_t) (FDSTACK_t fps, void *buf, size_t nbytes); +typedef ssize_t (*fdio_write_function_t) (FDSTACK_t fps, const void *buf, size_t nbytes); +typedef int (*fdio_seek_function_t) (FDSTACK_t fps, off_t pos, int whence); +typedef int (*fdio_close_function_t) (FDSTACK_t fps); typedef FD_t (*fdio_open_function_t) (const char * path, int flags, mode_t mode); -typedef FD_t (*fdio_fopen_function_t) (const char * path, const char * fmode); -typedef void * (*fdio_ffileno_function_t) (FD_t fd); -typedef int (*fdio_fflush_function_t) (FD_t fd); -typedef long (*fdio_ftell_function_t) (FD_t); +typedef FD_t (*fdio_fdopen_function_t) (FD_t fd, int fdno, const char * fmode); +typedef int (*fdio_fflush_function_t) (FDSTACK_t fps); +typedef off_t (*fdio_ftell_function_t) (FDSTACK_t fps); +typedef int (*fdio_ferror_function_t) (FDSTACK_t fps); +typedef const char * (*fdio_fstrerr_function_t)(FDSTACK_t fps); struct FDIO_s { + const char * ioname; + const char * name; fdio_read_function_t read; fdio_write_function_t write; fdio_seek_function_t seek; fdio_close_function_t close; - fdio_ref_function_t _fdref; - fdio_deref_function_t _fdderef; - fdio_new_function_t _fdnew; - fdio_fileno_function_t _fileno; - fdio_open_function_t _open; - fdio_fopen_function_t _fopen; - fdio_ffileno_function_t _ffileno; + fdio_fdopen_function_t _fdopen; fdio_fflush_function_t _fflush; fdio_ftell_function_t _ftell; + fdio_ferror_function_t _ferror; + fdio_fstrerr_function_t _fstrerr; }; /* forward refs */ static const FDIO_t fdio; static const FDIO_t ufdio; static const FDIO_t gzdio; +#if HAVE_BZLIB_H static const FDIO_t bzdio; +#endif +#ifdef HAVE_LZMA_H static const FDIO_t xzdio; static const FDIO_t lzdio; +#endif +#ifdef HAVE_ZSTD +static const FDIO_t zstdio; +#endif /** \ingroup rpmio * Update digest(s) attached to fd. */ static void fdUpdateDigests(FD_t fd, const void * buf, size_t buflen); -static FD_t fdNew(const char *descr); +static FD_t fdNew(int fdno, const char *descr); /** */ int _rpmio_debug = 0; @@ -191,38 +174,23 @@ static const char * fdbg(FD_t fd) { static char buf[BUFSIZ]; char *be = buf; - int i; buf[0] = '\0'; if (fd == NULL) return buf; *be++ = '\t'; - for (i = fd->nfps; i >= 0; i--) { - FDSTACK_t * fps = &fd->fps[i]; - if (i != fd->nfps) + for (FDSTACK_t fps = fd->fps; fps != NULL; fps = fps->prev) { + FDIO_t iot = fps->io; + if (fps != fd->fps) *be++ = ' '; *be++ = '|'; *be++ = ' '; - if (fps->io == fdio) { - sprintf(be, "FD %d fp %p", fps->fdno, fps->fp); - } else if (fps->io == ufdio) { - sprintf(be, "UFD %d fp %p", fps->fdno, fps->fp); - } else if (fps->io == gzdio) { - sprintf(be, "GZD %p fdno %d", fps->fp, fps->fdno); -#if HAVE_BZLIB_H - } else if (fps->io == bzdio) { - sprintf(be, "BZD %p fdno %d", fps->fp, fps->fdno); -#endif -#if HAVE_LZMA_H - } else if (fps->io == xzdio) { - sprintf(be, "XZD %p fdno %d", fps->fp, fps->fdno); - } else if (fps->io == lzdio) { - sprintf(be, "LZD %p fdno %d", fps->fp, fps->fdno); -#endif + /* plain fd io types dont have _fopen() method */ + if (iot->_fdopen == NULL) { + sprintf(be, "%s %d fp %p", iot->ioname, fps->fdno, fps->fp); } else { - sprintf(be, "??? io %p fp %p fdno %d ???", - fps->io, fps->fp, fps->fdno); + sprintf(be, "%s %p fp %d", iot->ioname, fps->fp, fps->fdno); } be += strlen(be); *be = '\0'; @@ -238,8 +206,10 @@ static void fdstat_enter(FD_t fd, fdOpX opx) static void fdstat_exit(FD_t fd, fdOpX opx, ssize_t rc) { - if (rc == -1) - fd->syserrno = errno; + if (rc == -1) { + FDSTACK_t fps = fdGetFps(fd); + fps->syserrno = errno; + } if (fd->stats != NULL) (void) rpmswExit(fdOp(fd, opx), rc); } @@ -291,21 +261,35 @@ FD_t fdDup(int fdno) if ((nfdno = dup(fdno)) < 0) return NULL; - fd = fdNew(NULL); - fdSetFdno(fd, nfdno); + fd = fdNew(nfdno, NULL); DBGIO(fd, (stderr, "==> fdDup(%d) fd %p %s\n", fdno, (fd ? fd : NULL), fdbg(fd))); return fd; } /* Regular fd doesn't have fflush() equivalent but its not an error either */ -static int fdFlush(FD_t fd) +static int fdFlush(FDSTACK_t fps) { return 0; } -static int fdFileno(FD_t fd) +static int fdError(FDSTACK_t fps) +{ + return fps->syserrno; +} + +static int zfdError(FDSTACK_t fps) { - return (fd != NULL) ? fd->fps[0].fdno : -2; + return (fps->syserrno || fps->errcookie != NULL) ? -1 : 0; +} + +static const char * fdStrerr(FDSTACK_t fps) +{ + return (fps->syserrno != 0) ? strerror(fps->syserrno) : ""; +} + +static const char * zfdStrerr(FDSTACK_t fps) +{ + return (fps->errcookie != NULL) ? fps->errcookie : ""; } const char * Fdescr(FD_t fd) @@ -315,7 +299,7 @@ const char * Fdescr(FD_t fd) /* Lazy lookup if description is not set (eg dupped fd) */ if (fd->descr == NULL) { - int fdno = fd->fps[fd->nfps].fdno; + int fdno = fd->fps->fdno; #if defined(__linux__) /* Grab the path from /proc if we can */ char *procpath = NULL; @@ -358,69 +342,55 @@ FD_t fdFree( FD_t fd) if (fd->digests) { fd->digests = rpmDigestBundleFree(fd->digests); } + free(fd->fps); free(fd->descr); free(fd); } return NULL; } -FD_t fdNew(const char *descr) +static FD_t fdNew(int fdno, const char *descr) { FD_t fd = xcalloc(1, sizeof(*fd)); - if (fd == NULL) /* XXX xmalloc never returns NULL */ - return NULL; fd->nrefs = 0; fd->flags = 0; fd->magic = FDMAGIC; fd->urlType = URL_IS_UNKNOWN; - - fd->nfps = 0; - memset(fd->fps, 0, sizeof(fd->fps)); - - fd->fps[0].io = fdio; - fd->fps[0].fp = NULL; - fd->fps[0].fdno = -1; - - fd->syserrno = 0; - fd->errcookie = NULL; fd->stats = xcalloc(1, sizeof(*fd->stats)); fd->digests = NULL; fd->descr = descr ? xstrdup(descr) : NULL; - return fdLink(fd); + fdPush(fd, fdio, NULL, fdno); + return fd; } -static ssize_t fdRead(FD_t fd, void * buf, size_t count) +static ssize_t fdRead(FDSTACK_t fps, void * buf, size_t count) { - return read(fdFileno(fd), buf, count); + return read(fps->fdno, buf, count); } -static ssize_t fdWrite(FD_t fd, const void * buf, size_t count) +static ssize_t fdWrite(FDSTACK_t fps, const void * buf, size_t count) { if (count == 0) return 0; - return write(fdFileno(fd), buf, count); + return write(fps->fdno, buf, count); } -static int fdSeek(FD_t fd, off_t pos, int whence) +static int fdSeek(FDSTACK_t fps, off_t pos, int whence) { - return lseek(fdFileno(fd), pos, whence); + return lseek(fps->fdno, pos, whence); } -static int fdClose(FD_t fd) +static int fdClose(FDSTACK_t fps) { - int fdno; + int fdno = fps->fdno; int rc; - if (fd == NULL) return -2; - fdno = fdFileno(fd); - - fdSetFdno(fd, -1); + fps->fdno = -1; rc = ((fdno >= 0) ? close(fdno) : -2); - fdFree(fd); return rc; } @@ -435,20 +405,20 @@ static FD_t fdOpen(const char *path, int flags, mode_t mode) (void) close(fdno); return NULL; } - fd = fdNew(path); - fdSetFdno(fd, fdno); + fd = fdNew(fdno, path); fd->flags = flags; return fd; } -static long fdTell(FD_t fd) +static off_t fdTell(FDSTACK_t fps) { - return lseek(Fileno(fd), 0, SEEK_CUR); + return lseek(fps->fdno, 0, SEEK_CUR); } static const struct FDIO_s fdio_s = { - fdRead, fdWrite, fdSeek, fdClose, fdLink, fdFree, fdNew, fdFileno, - fdOpen, NULL, fdGetFp, fdFlush, fdTell + "fdio", NULL, + fdRead, fdWrite, fdSeek, fdClose, + fdOpen, NULL, fdFlush, fdTell, fdError, fdStrerr, }; static const FDIO_t fdio = &fdio_s ; @@ -543,21 +513,17 @@ fprintf(stderr, "*** ufdOpen(%s,0x%x,0%o)\n", url, (unsigned)flags, (unsigned)mo break; } - if (fd == NULL) return NULL; - - fdSetIo(fd, ufdio); - fd->urlType = urlType; - - if (Fileno(fd) < 0) { - (void) fdClose(fd); - return NULL; + if (fd != NULL) { + fd->fps->io = ufdio; + fd->urlType = urlType; } return fd; } static const struct FDIO_s ufdio_s = { - fdRead, fdWrite, fdSeek, fdClose, fdLink, fdFree, fdNew, fdFileno, - ufdOpen, NULL, fdGetFp, fdFlush, fdTell + "ufdio", NULL, + fdRead, fdWrite, fdSeek, fdClose, + ufdOpen, NULL, fdFlush, fdTell, fdError, fdStrerr }; static const FDIO_t ufdio = &ufdio_s ; @@ -565,161 +531,104 @@ static const FDIO_t ufdio = &ufdio_s ; /* Support for GZIP library. */ #include <zlib.h> -static void * gzdFileno(FD_t fd) +static FD_t gzdFdopen(FD_t fd, int fdno, const char *fmode) { - return iotFileno(fd, gzdio); -} + gzFile gzfile = gzdopen(fdno, fmode); -static -FD_t gzdOpen(const char * path, const char * fmode) -{ - FD_t fd; - gzFile gzfile; - if ((gzfile = gzopen(path, fmode)) == NULL) + if (gzfile == NULL) return NULL; - fd = fdNew(path); - fdPop(fd); fdPush(fd, gzdio, gzfile, -1); - - return fdLink(fd); -} -static FD_t gzdFdopen(FD_t fd, const char *fmode) -{ - int fdno; - gzFile gzfile; - - if (fd == NULL || fmode == NULL) return NULL; - fdno = fdFileno(fd); fdSetFdno(fd, -1); /* XXX skip the fdio close */ - if (fdno < 0) return NULL; - gzfile = gzdopen(fdno, fmode); - if (gzfile == NULL) return NULL; - fdPush(fd, gzdio, gzfile, fdno); /* Push gzdio onto stack */ - - return fdLink(fd); + return fd; } -static int gzdFlush(FD_t fd) +static int gzdFlush(FDSTACK_t fps) { - gzFile gzfile; - gzfile = gzdFileno(fd); + gzFile gzfile = fps->fp; if (gzfile == NULL) return -2; return gzflush(gzfile, Z_SYNC_FLUSH); /* XXX W2DO? */ } -static ssize_t gzdRead(FD_t fd, void * buf, size_t count) +static void gzdSetError(FDSTACK_t fps) { - gzFile gzfile; + gzFile gzfile = fps->fp; + int zerror = 0; + fps->errcookie = gzerror(gzfile, &zerror); + if (zerror == Z_ERRNO) { + fps->syserrno = errno; + fps->errcookie = strerror(fps->syserrno); + } +} + +static ssize_t gzdRead(FDSTACK_t fps, void * buf, size_t count) +{ + gzFile gzfile = fps->fp; ssize_t rc; - gzfile = gzdFileno(fd); if (gzfile == NULL) return -2; /* XXX can't happen */ rc = gzread(gzfile, buf, count); - if (rc < 0) { - int zerror = 0; - fd->errcookie = gzerror(gzfile, &zerror); - if (zerror == Z_ERRNO) { - fd->syserrno = errno; - fd->errcookie = strerror(fd->syserrno); - } - } + if (rc < 0) + gzdSetError(fps); return rc; } -static ssize_t gzdWrite(FD_t fd, const void * buf, size_t count) +static ssize_t gzdWrite(FDSTACK_t fps, const void * buf, size_t count) { gzFile gzfile; ssize_t rc; - gzfile = gzdFileno(fd); + gzfile = fps->fp; if (gzfile == NULL) return -2; /* XXX can't happen */ rc = gzwrite(gzfile, (void *)buf, count); - if (rc < 0) { - int zerror = 0; - fd->errcookie = gzerror(gzfile, &zerror); - if (zerror == Z_ERRNO) { - fd->syserrno = errno; - fd->errcookie = strerror(fd->syserrno); - } - } + if (rc < 0) + gzdSetError(fps); return rc; } /* XXX zlib-1.0.4 has not */ -static int gzdSeek(FD_t fd, off_t pos, int whence) +static int gzdSeek(FDSTACK_t fps, off_t pos, int whence) { off_t p = pos; int rc; #if HAVE_GZSEEK - gzFile gzfile; - - if (fd == NULL) return -2; + gzFile gzfile = fps->fp; - gzfile = gzdFileno(fd); if (gzfile == NULL) return -2; /* XXX can't happen */ rc = gzseek(gzfile, p, whence); - if (rc < 0) { - int zerror = 0; - fd->errcookie = gzerror(gzfile, &zerror); - if (zerror == Z_ERRNO) { - fd->syserrno = errno; - fd->errcookie = strerror(fd->syserrno); - } - } + if (rc < 0) + gzdSetError(fps); #else rc = -2; #endif return rc; } -static int gzdClose(FD_t fd) +static int gzdClose(FDSTACK_t fps) { - gzFile gzfile; + gzFile gzfile = fps->fp; int rc; - gzfile = gzdFileno(fd); if (gzfile == NULL) return -2; /* XXX can't happen */ rc = gzclose(gzfile); - /* XXX TODO: preserve fd if errors */ - - if (fd) { - if (rc < 0) { - fd->errcookie = "gzclose error"; - if (rc == Z_ERRNO) { - fd->syserrno = errno; - fd->errcookie = strerror(fd->syserrno); - } - } - } - - if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "GZDIO", stderr); - if (rc == 0) - fdFree(fd); - return rc; + return (rc != 0) ? -1 : 0; } -static long gzdTell(FD_t fd) +static off_t gzdTell(FDSTACK_t fps) { off_t pos = -1; - gzFile gzfile = gzdFileno(fd); + gzFile gzfile = fps->fp; if (gzfile != NULL) { #if HAVE_GZSEEK pos = gztell(gzfile); - if (pos < 0) { - int zerror = 0; - fd->errcookie = gzerror(gzfile, &zerror); - if (zerror == Z_ERRNO) { - fd->syserrno = errno; - fd->errcookie = strerror(fd->syserrno); - } - } + if (pos < 0) + gzdSetError(fps); #else pos = -2; #endif @@ -727,8 +636,9 @@ static long gzdTell(FD_t fd) return pos; } static const struct FDIO_s gzdio_s = { - gzdRead, gzdWrite, gzdSeek, gzdClose, fdLink, fdFree, fdNew, fdFileno, - NULL, gzdOpen, gzdFileno, gzdFlush, gzdTell + "gzdio", "gzip", + gzdRead, gzdWrite, gzdSeek, gzdClose, + NULL, gzdFdopen, gzdFlush, gzdTell, zfdError, zfdStrerr }; static const FDIO_t gzdio = &gzdio_s ; @@ -738,133 +648,73 @@ static const FDIO_t gzdio = &gzdio_s ; #include <bzlib.h> -static void * bzdFileno(FD_t fd) +static FD_t bzdFdopen(FD_t fd, int fdno, const char * fmode) { - return iotFileno(fd, bzdio); -} + BZFILE *bzfile = BZ2_bzdopen(fdno, fmode); -static FD_t bzdOpen(const char * path, const char * mode) -{ - FD_t fd; - BZFILE *bzfile;; - if ((bzfile = BZ2_bzopen(path, mode)) == NULL) + if (bzfile == NULL) return NULL; - fd = fdNew(path); - fdPop(fd); fdPush(fd, bzdio, bzfile, -1); - return fdLink(fd); -} -static FD_t bzdFdopen(FD_t fd, const char * fmode) -{ - int fdno; - BZFILE *bzfile; - - if (fd == NULL || fmode == NULL) return NULL; - fdno = fdFileno(fd); fdSetFdno(fd, -1); /* XXX skip the fdio close */ - if (fdno < 0) return NULL; - bzfile = BZ2_bzdopen(fdno, fmode); - if (bzfile == NULL) return NULL; - fdPush(fd, bzdio, bzfile, fdno); /* Push bzdio onto stack */ - - return fdLink(fd); + return fd; } -static int bzdFlush(FD_t fd) +static int bzdFlush(FDSTACK_t fps) { - return BZ2_bzflush(bzdFileno(fd)); + return BZ2_bzflush(fps->fp); } -static ssize_t bzdRead(FD_t fd, void * buf, size_t count) +static ssize_t bzdRead(FDSTACK_t fps, void * buf, size_t count) { - BZFILE *bzfile; + BZFILE *bzfile = fps->fp; ssize_t rc = 0; - bzfile = bzdFileno(fd); if (bzfile) rc = BZ2_bzread(bzfile, buf, count); if (rc == -1) { int zerror = 0; - if (bzfile) - fd->errcookie = BZ2_bzerror(bzfile, &zerror); + if (bzfile) { + fps->errcookie = BZ2_bzerror(bzfile, &zerror); + } } return rc; } -static ssize_t bzdWrite(FD_t fd, const void * buf, size_t count) +static ssize_t bzdWrite(FDSTACK_t fps, const void * buf, size_t count) { - BZFILE *bzfile; + BZFILE *bzfile = fps->fp; ssize_t rc; - bzfile = bzdFileno(fd); rc = BZ2_bzwrite(bzfile, (void *)buf, count); if (rc == -1) { int zerror = 0; - fd->errcookie = BZ2_bzerror(bzfile, &zerror); + fps->errcookie = BZ2_bzerror(bzfile, &zerror); } return rc; } -static int bzdClose(FD_t fd) +static int bzdClose(FDSTACK_t fps) { - BZFILE *bzfile; - int rc; - - bzfile = bzdFileno(fd); + BZFILE *bzfile = fps->fp; if (bzfile == NULL) return -2; - /* FIX: check rc */ - BZ2_bzclose(bzfile); - rc = 0; /* XXX FIXME */ - - /* XXX TODO: preserve fd if errors */ - if (fd) { - if (rc == -1) { - int zerror = 0; - fd->errcookie = BZ2_bzerror(bzfile, &zerror); - } - } + /* bzclose() doesn't return errors */ + BZ2_bzclose(bzfile); - if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "BZDIO", stderr); - if (rc == 0) - fdFree(fd); - return rc; + return 0; } static const struct FDIO_s bzdio_s = { - bzdRead, bzdWrite, NULL, bzdClose, fdLink, fdFree, fdNew, fdFileno, - NULL, bzdOpen, bzdFileno, bzdFlush, NULL + "bzdio", "bzip2", + bzdRead, bzdWrite, NULL, bzdClose, + NULL, bzdFdopen, bzdFlush, NULL, zfdError, zfdStrerr }; static const FDIO_t bzdio = &bzdio_s ; #endif /* HAVE_BZLIB_H */ -static const char * getFdErrstr (FD_t fd) -{ - const char *errstr = NULL; - - if (fdGetIo(fd) == gzdio) { - errstr = fd->errcookie; - } else -#ifdef HAVE_BZLIB_H - if (fdGetIo(fd) == bzdio) { - errstr = fd->errcookie; - } else -#endif /* HAVE_BZLIB_H */ -#ifdef HAVE_LZMA_H - if (fdGetIo(fd) == xzdio || fdGetIo(fd) == lzdio) { - errstr = fd->errcookie; - } else -#endif /* HAVE_LZMA_H */ - { - errstr = (fd->syserrno ? strerror(fd->syserrno) : ""); - } - - return errstr; -} - /* =============================================================== */ /* Support for LZMA library. */ @@ -873,6 +723,10 @@ static const char * getFdErrstr (FD_t fd) #include <sys/types.h> #include <inttypes.h> #include <lzma.h> +/* Multithreading support in stable API since xz 5.2.0 */ +#if LZMA_VERSION >= 50020002 +#define HAVE_LZMA_MT +#endif #define kBufferSize (1 << 15) @@ -889,87 +743,138 @@ typedef struct lzfile { } LZFILE; -static LZFILE *lzopen_internal(const char *path, const char *mode, int fd, int xz) +static LZFILE *lzopen_internal(const char *mode, int fd, int xz) { - int level = 7; /* Use XZ's default compression level if unspecified */ + int level = LZMA_PRESET_DEFAULT; int encoding = 0; FILE *fp; LZFILE *lzfile; lzma_ret ret; lzma_stream init_strm = LZMA_STREAM_INIT; - + uint64_t mem_limit = rpmExpandNumeric("%{_xz_memlimit}"); +#ifdef HAVE_LZMA_MT + int threads = 0; +#endif for (; *mode; mode++) { if (*mode == 'w') encoding = 1; else if (*mode == 'r') encoding = 0; - else if (*mode >= '1' && *mode <= '9') + else if (*mode >= '0' && *mode <= '9') level = *mode - '0'; + else if (*mode == 'T') { + if (isdigit(*(mode+1))) { +#ifdef HAVE_LZMA_MT + threads = atoi(++mode); +#endif + /* skip past rest of digits in string that atoi() + * should've processed + * */ + while (isdigit(*++mode)); + } +#ifdef HAVE_LZMA_MT + else + threads = -1; +#endif + } } - if (fd != -1) - fp = fdopen(fd, encoding ? "w" : "r"); - else - fp = fopen(path, encoding ? "w" : "r"); + fp = fdopen(fd, encoding ? "w" : "r"); if (!fp) - return 0; + return NULL; lzfile = calloc(1, sizeof(*lzfile)); - if (!lzfile) { - fclose(fp); - return 0; - } - lzfile->file = fp; lzfile->encoding = encoding; lzfile->eof = 0; lzfile->strm = init_strm; if (encoding) { if (xz) { - ret = lzma_easy_encoder(&lzfile->strm, level, LZMA_CHECK_SHA256); +#ifdef HAVE_LZMA_MT + if (!threads) { +#endif + ret = lzma_easy_encoder(&lzfile->strm, level, LZMA_CHECK_SHA256); +#ifdef HAVE_LZMA_MT + } else { + if (threads == -1) + threads = sysconf(_SC_NPROCESSORS_ONLN); + lzma_mt mt_options = { + .flags = 0, + .threads = threads, + .block_size = 0, + .timeout = 0, + .preset = level, + .filters = NULL, + .check = LZMA_CHECK_SHA256 }; + +#if __WORDSIZE == 32 + /* In 32 bit environment, required memory easily exceeds memory address + * space limit if compressing using multiple threads. + * By setting a memory limit, liblzma will automatically adjust number + * of threads to avoid exceeding memory. + */ + if (threads > 1) { + struct utsname u; + uint32_t memlimit = (SIZE_MAX>>1) + (SIZE_MAX>>3); + uint64_t memory_usage; + /* While a 32 bit linux kernel will have an address limit of 3GiB + * for processes (which is why set the memory limit to 2.5GiB as a safety + * margin), 64 bit kernels will have a limit of 4GiB for 32 bit binaries. + * Therefore the memory limit should be higher if running on a 64 bit + * kernel, so we increase it to 3,5GiB. + */ + uname(&u); + if (strstr(u.machine, "64") || strstr(u.machine, "s390x") +#if defined(__linux__) + || ((personality(0xffffffff) & PER_MASK) == PER_LINUX32) +#endif + ) + memlimit += (SIZE_MAX>>2); + /* keep reducing the number of threads until memory usage gets below limit */ + while ((memory_usage = lzma_stream_encoder_mt_memusage(&mt_options)) > memlimit) { + /* number of threads shouldn't be able to hit zero with compression + * settings aailable to set through rpm... */ + assert(--mt_options.threads != 0); + } + lzma_memlimit_set(&lzfile->strm, memlimit); + + if (threads != (int)mt_options.threads) + rpmlog(RPMLOG_NOTICE, + "XZ: Adjusted the number of threads from %d to %d to not exceed the memory usage limit of %lu bytes", + threads, mt_options.threads, memlimit); + } +#endif + + ret = lzma_stream_encoder_mt(&lzfile->strm, &mt_options); + } +#endif } else { lzma_options_lzma options; lzma_lzma_preset(&options, level); ret = lzma_alone_encoder(&lzfile->strm, &options); } - } else { /* lzma_easy_decoder_memusage(level) is not ready yet, use hardcoded limit for now */ - ret = lzma_auto_decoder(&lzfile->strm, 100<<20, 0); + } else { /* lzma_easy_decoder_memusage(level) is not ready yet, use hardcoded limit for now */ + ret = lzma_auto_decoder(&lzfile->strm, mem_limit ? mem_limit : 100<<20, 0); } if (ret != LZMA_OK) { + switch (ret) { + case LZMA_MEM_ERROR: + rpmlog(RPMLOG_ERR, "liblzma: Memory allocation failed"); + break; + + case LZMA_DATA_ERROR: + rpmlog(RPMLOG_ERR, "liblzma: File size limits exceeded"); + break; + + default: + rpmlog(RPMLOG_ERR, "liblzma: <Unknown error (%d), possibly a bug", ret); + break; + } fclose(fp); free(lzfile); - return 0; + return NULL; } return lzfile; } -static LZFILE *xzopen(const char *path, const char *mode) -{ - return lzopen_internal(path, mode, -1, 1); -} - -static LZFILE *xzdopen(int fd, const char *mode) -{ - if (fd < 0) - return 0; - return lzopen_internal(0, mode, fd, 1); -} - -static LZFILE *lzopen(const char *path, const char *mode) -{ - return lzopen_internal(path, mode, -1, 0); -} - -static LZFILE *lzdopen(int fd, const char *mode) -{ - if (fd < 0) - return 0; - return lzopen_internal(0, mode, fd, 0); -} - -static int lzflush(LZFILE *lzfile) -{ - return fflush(lzfile->file); -} - static int lzclose(LZFILE *lzfile) { lzma_ret ret; @@ -1054,170 +959,363 @@ static ssize_t lzwrite(LZFILE *lzfile, void *buf, size_t len) } } -static void * lzdFileno(FD_t fd) +static FD_t xzdFdopen(FD_t fd, int fdno, const char * fmode) { - void * rc = NULL; - - if (fd == NULL) - return NULL; + LZFILE *lzfile = lzopen_internal(fmode, fdno, 1); - for (int i = fd->nfps; i >= 0; i--) { - FDSTACK_t * fps = &fd->fps[i]; - if (fps->io != xzdio && fps->io != lzdio) - continue; - rc = fps->fp; - break; - } - return rc; -} - -static FD_t xzdOpen(const char * path, const char * mode) -{ - FD_t fd; - LZFILE *lzfile; - if ((lzfile = xzopen(path, mode)) == NULL) + if (lzfile == NULL) return NULL; - fd = fdNew(path); - fdPop(fd); fdPush(fd, xzdio, lzfile, -1); - return fdLink(fd); -} - -static FD_t xzdFdopen(FD_t fd, const char * fmode) -{ - int fdno; - LZFILE *lzfile; - if (fd == NULL || fmode == NULL) return NULL; - fdno = fdFileno(fd); fdSetFdno(fd, -1); /* XXX skip the fdio close */ - if (fdno < 0) return NULL; - lzfile = xzdopen(fdno, fmode); - if (lzfile == NULL) return NULL; fdPush(fd, xzdio, lzfile, fdno); - return fdLink(fd); + return fd; } -static FD_t lzdOpen(const char * path, const char * mode) +static FD_t lzdFdopen(FD_t fd, int fdno, const char * fmode) { - FD_t fd; - LZFILE *lzfile; - if ((lzfile = lzopen(path, mode)) == NULL) - return NULL; - fd = fdNew(path); - fdPop(fd); fdPush(fd, xzdio, lzfile, -1); - return fdLink(fd); -} + LZFILE *lzfile = lzopen_internal(fmode, fdno, 0); -static FD_t lzdFdopen(FD_t fd, const char * fmode) -{ - int fdno; - LZFILE *lzfile; + if (lzfile == NULL) + return NULL; - if (fd == NULL || fmode == NULL) return NULL; - fdno = fdFileno(fd); fdSetFdno(fd, -1); /* XXX skip the fdio close */ - if (fdno < 0) return NULL; - lzfile = lzdopen(fdno, fmode); - if (lzfile == NULL) return NULL; - fdPush(fd, xzdio, lzfile, fdno); - return fdLink(fd); + fdPush(fd, lzdio, lzfile, fdno); + return fd; } -static int lzdFlush(FD_t fd) +static int lzdFlush(FDSTACK_t fps) { - return lzflush(lzdFileno(fd)); + LZFILE *lzfile = fps->fp; + return fflush(lzfile->file); } -static ssize_t lzdRead(FD_t fd, void * buf, size_t count) +static ssize_t lzdRead(FDSTACK_t fps, void * buf, size_t count) { - LZFILE *lzfile; + LZFILE *lzfile = fps->fp; ssize_t rc = 0; - lzfile = lzdFileno(fd); if (lzfile) rc = lzread(lzfile, buf, count); if (rc == -1) { - fd->errcookie = "Lzma: decoding error"; + fps->errcookie = "Lzma: decoding error"; } return rc; } -static ssize_t lzdWrite(FD_t fd, const void * buf, size_t count) +static ssize_t lzdWrite(FDSTACK_t fps, const void * buf, size_t count) { - LZFILE *lzfile; + LZFILE *lzfile = fps->fp; ssize_t rc = 0; - lzfile = lzdFileno(fd); - rc = lzwrite(lzfile, (void *)buf, count); if (rc < 0) { - fd->errcookie = "Lzma: encoding error"; + fps->errcookie = "Lzma: encoding error"; } return rc; } -static int lzdClose(FD_t fd) +static int lzdClose(FDSTACK_t fps) { - LZFILE *lzfile; + LZFILE *lzfile = fps->fp; int rc; - lzfile = lzdFileno(fd); - if (lzfile == NULL) return -2; rc = lzclose(lzfile); - /* XXX TODO: preserve fd if errors */ - - if (fd) { - if (rc == -1) { - fd->errcookie = "lzclose error"; - fd->syserrno = errno; - fd->errcookie = strerror(fd->syserrno); - } - } - - if (_rpmio_debug || rpmIsDebug()) fdstat_print(fd, "XZDIO", stderr); - if (rc == 0) - fdFree(fd); return rc; } static struct FDIO_s xzdio_s = { - lzdRead, lzdWrite, NULL, lzdClose, NULL, NULL, NULL, fdFileno, - NULL, xzdOpen, lzdFileno, lzdFlush, NULL + "xzdio", "xz", + lzdRead, lzdWrite, NULL, lzdClose, + NULL, xzdFdopen, lzdFlush, NULL, zfdError, zfdStrerr }; static const FDIO_t xzdio = &xzdio_s; static struct FDIO_s lzdio_s = { - lzdRead, lzdWrite, NULL, lzdClose, NULL, NULL, NULL, fdFileno, - NULL, lzdOpen, lzdFileno, lzdFlush, NULL + "lzdio", "lzma", + lzdRead, lzdWrite, NULL, lzdClose, + NULL, lzdFdopen, lzdFlush, NULL, zfdError, zfdStrerr }; static const FDIO_t lzdio = &lzdio_s; #endif /* HAVE_LZMA_H */ /* =============================================================== */ +/* Support for ZSTD library. */ +#ifdef HAVE_ZSTD -const char *Fstrerror(FD_t fd) +#include <zstd.h> + +typedef struct rpmzstd_s { + int flags; /*!< open flags. */ + int fdno; + int level; /*!< compression level */ + FILE * fp; + void * _stream; /*!< ZSTD_{C,D}Stream */ + size_t nb; + void * b; + ZSTD_inBuffer zib; /*!< ZSTD_inBuffer */ + ZSTD_outBuffer zob; /*!< ZSTD_outBuffer */ +} * rpmzstd; + +static rpmzstd rpmzstdNew(int fdno, const char *fmode) { - if (fd == NULL) - return (errno ? strerror(errno) : ""); - return getFdErrstr(fd); + int flags = 0; + int level = 3; + const char * s = fmode; + char stdio[32]; + char *t = stdio; + char *te = t + sizeof(stdio) - 2; + int c; + + switch ((c = *s++)) { + case 'a': + *t++ = (char)c; + flags &= ~O_ACCMODE; + flags |= O_WRONLY | O_CREAT | O_APPEND; + break; + case 'w': + *t++ = (char)c; + flags &= ~O_ACCMODE; + flags |= O_WRONLY | O_CREAT | O_TRUNC; + break; + case 'r': + *t++ = (char)c; + flags &= ~O_ACCMODE; + flags |= O_RDONLY; + break; + } + + while ((c = *s++) != 0) { + switch (c) { + case '.': + break; + case '+': + if (t < te) *t++ = c; + flags &= ~O_ACCMODE; + flags |= O_RDWR; + continue; + break; + default: + if (c >= (int)'0' && c <= (int)'9') { + level = strtol(s-1, (char **)&s, 10); + if (level < 1){ + level = 1; + rpmlog(RPMLOG_WARNING, "Invalid compression level for zstd. Using %i instead.\n", 1); + } + if (level > 19) { + level = 19; + rpmlog(RPMLOG_WARNING, "Invalid compression level for zstd. Using %i instead.\n", 19); + } + } + continue; + break; + } + break; + } + *t = '\0'; + + FILE * fp = fdopen(fdno, stdio); + if (fp == NULL) + return NULL; + + void * _stream = NULL; + size_t nb = 0; + + if ((flags & O_ACCMODE) == O_RDONLY) { /* decompressing */ + if ((_stream = (void *) ZSTD_createDStream()) == NULL + || ZSTD_isError(ZSTD_initDStream(_stream))) { + return NULL; + } + nb = ZSTD_DStreamInSize(); + } else { /* compressing */ + if ((_stream = (void *) ZSTD_createCStream()) == NULL + || ZSTD_isError(ZSTD_initCStream(_stream, level))) { + return NULL; + } + nb = ZSTD_CStreamOutSize(); + } + + rpmzstd zstd = (rpmzstd) xcalloc(1, sizeof(*zstd)); + zstd->flags = flags; + zstd->fdno = fdno; + zstd->level = level; + zstd->fp = fp; + zstd->_stream = _stream; + zstd->nb = nb; + zstd->b = xmalloc(nb); + + return zstd; } -#define FDIOVEC(_fd, _vec) \ - ((fdGetIo(_fd) && fdGetIo(_fd)->_vec) ? fdGetIo(_fd)->_vec : NULL) +static FD_t zstdFdopen(FD_t fd, int fdno, const char * fmode) +{ + rpmzstd zstd = rpmzstdNew(fdno, fmode); + + if (zstd == NULL) + return NULL; + + fdSetFdno(fd, -1); /* XXX skip the fdio close */ + fdPush(fd, zstdio, zstd, fdno); /* Push zstdio onto stack */ + return fd; +} + +static int zstdFlush(FDSTACK_t fps) +{ + rpmzstd zstd = (rpmzstd) fps->fp; +assert(zstd); + int rc = -1; + + if ((zstd->flags & O_ACCMODE) == O_RDONLY) { /* decompressing */ + rc = 0; + } else { /* compressing */ + /* close frame */ + zstd->zob.dst = zstd->b; + zstd->zob.size = zstd->nb; + zstd->zob.pos = 0; + int xx = ZSTD_flushStream(zstd->_stream, &zstd->zob); + if (ZSTD_isError(xx)) + fps->errcookie = ZSTD_getErrorName(xx); + else if (zstd->zob.pos != fwrite(zstd->b, 1, zstd->zob.pos, zstd->fp)) + fps->errcookie = "zstdFlush fwrite failed."; + else + rc = 0; + } + return rc; +} + +static ssize_t zstdRead(FDSTACK_t fps, void * buf, size_t count) +{ + rpmzstd zstd = (rpmzstd) fps->fp; +assert(zstd); + ZSTD_outBuffer zob = { buf, count, 0 }; + + while (zob.pos < zob.size) { + /* Re-fill compressed data buffer. */ + if (zstd->zib.pos >= zstd->zib.size) { + zstd->zib.size = fread(zstd->b, 1, zstd->nb, zstd->fp); + if (zstd->zib.size == 0) + break; /* EOF */ + zstd->zib.src = zstd->b; + zstd->zib.pos = 0; + } + + /* Decompress next chunk. */ + int xx = ZSTD_decompressStream(zstd->_stream, &zob, &zstd->zib); + if (ZSTD_isError(xx)) { + fps->errcookie = ZSTD_getErrorName(xx); + return -1; + } + } + return zob.pos; +} + +static ssize_t zstdWrite(FDSTACK_t fps, const void * buf, size_t count) +{ + rpmzstd zstd = (rpmzstd) fps->fp; +assert(zstd); + ZSTD_inBuffer zib = { buf, count, 0 }; + + while (zib.pos < zib.size) { + + /* Reset to beginning of compressed data buffer. */ + zstd->zob.dst = zstd->b; + zstd->zob.size = zstd->nb; + zstd->zob.pos = 0; + + /* Compress next chunk. */ + int xx = ZSTD_compressStream(zstd->_stream, &zstd->zob, &zib); + if (ZSTD_isError(xx)) { + fps->errcookie = ZSTD_getErrorName(xx); + return -1; + } + + /* Write compressed data buffer. */ + if (zstd->zob.pos > 0) { + size_t nw = fwrite(zstd->b, 1, zstd->zob.pos, zstd->fp); + if (nw != zstd->zob.pos) { + fps->errcookie = "zstdWrite fwrite failed."; + return -1; + } + } + } + return zib.pos; +} + +static int zstdClose(FDSTACK_t fps) +{ + rpmzstd zstd = (rpmzstd) fps->fp; +assert(zstd); + int rc = -2; + + if ((zstd->flags & O_ACCMODE) == O_RDONLY) { /* decompressing */ + rc = 0; + ZSTD_freeDStream(zstd->_stream); + } else { /* compressing */ + /* close frame */ + zstd->zob.dst = zstd->b; + zstd->zob.size = zstd->nb; + zstd->zob.pos = 0; + int xx = ZSTD_endStream(zstd->_stream, &zstd->zob); + if (ZSTD_isError(xx)) + fps->errcookie = ZSTD_getErrorName(xx); + else if (zstd->zob.pos != fwrite(zstd->b, 1, zstd->zob.pos, zstd->fp)) + fps->errcookie = "zstdClose fwrite failed."; + else + rc = 0; + ZSTD_freeCStream(zstd->_stream); + } + + if (zstd->fp && fileno(zstd->fp) > 2) + (void) fclose(zstd->fp); + + if (zstd->b) free(zstd->b); + free(zstd); + + return rc; +} + +static const struct FDIO_s zstdio_s = { + "zstdio", "zstd", + zstdRead, zstdWrite, NULL, zstdClose, + NULL, zstdFdopen, zstdFlush, NULL, zfdError, zfdStrerr +}; +static const FDIO_t zstdio = &zstdio_s ; + +#endif /* HAVE_ZSTD */ + +/* =============================================================== */ + +#define FDIOVEC(_fps, _vec) \ + ((_fps) && (_fps)->io) ? (_fps)->io->_vec : NULL + +const char *Fstrerror(FD_t fd) +{ + const char *err = ""; + + if (fd != NULL) { + FDSTACK_t fps = fdGetFps(fd); + fdio_fstrerr_function_t _fstrerr = FDIOVEC(fps, _fstrerr); + if (_fstrerr) + err = _fstrerr(fps); + } else if (errno){ + err = strerror(errno); + } + return err; +} ssize_t Fread(void *buf, size_t size, size_t nmemb, FD_t fd) { ssize_t rc = -1; if (fd != NULL) { - fdio_read_function_t _read = FDIOVEC(fd, read); + FDSTACK_t fps = fdGetFps(fd); + fdio_read_function_t _read = FDIOVEC(fps, read); fdstat_enter(fd, FDSTAT_READ); do { - rc = (_read ? (*_read) (fd, buf, size * nmemb) : -2); + rc = (_read ? (*_read) (fps, buf, size * nmemb) : -2); } while (rc == -1 && errno == EINTR); fdstat_exit(fd, FDSTAT_READ, rc); @@ -1236,11 +1334,12 @@ ssize_t Fwrite(const void *buf, size_t size, size_t nmemb, FD_t fd) ssize_t rc = -1; if (fd != NULL) { - fdio_write_function_t _write = FDIOVEC(fd, write); + FDSTACK_t fps = fdGetFps(fd); + fdio_write_function_t _write = FDIOVEC(fps, write); fdstat_enter(fd, FDSTAT_WRITE); do { - rc = (_write ? _write(fd, buf, size * nmemb) : -2); + rc = (_write ? _write(fps, buf, size * nmemb) : -2); } while (rc == -1 && errno == EINTR); fdstat_exit(fd, FDSTAT_WRITE, rc); @@ -1259,10 +1358,11 @@ int Fseek(FD_t fd, off_t offset, int whence) int rc = -1; if (fd != NULL) { - fdio_seek_function_t _seek = FDIOVEC(fd, seek); + FDSTACK_t fps = fdGetFps(fd); + fdio_seek_function_t _seek = FDIOVEC(fps, seek); fdstat_enter(fd, FDSTAT_SEEK); - rc = (_seek ? _seek(fd, offset, whence) : -2); + rc = (_seek ? _seek(fps, offset, whence) : -2); fdstat_exit(fd, FDSTAT_SEEK, rc); } @@ -1281,19 +1381,27 @@ int Fclose(FD_t fd) fd = fdLink(fd); fdstat_enter(fd, FDSTAT_CLOSE); - while (fd->nfps >= 0) { - fdio_close_function_t _close = FDIOVEC(fd, close); - rc = _close ? _close(fd) : -2; + for (FDSTACK_t fps = fd->fps; fps != NULL; fps = fdPop(fd)) { + if (fps->fdno >= 0) { + fdio_close_function_t _close = FDIOVEC(fps, close); + rc = _close ? _close(fps) : -2; + + if (ec == 0 && rc) + ec = rc; + } + + /* Debugging stats for compresed types */ + if ((_rpmio_debug || rpmIsDebug()) && fps->fdno == -1) + fdstat_print(fd, fps->io->ioname, stderr); - if (fd->nfps == 0) + /* Leave freeing the last one after stats */ + if (fps->prev == NULL) break; - if (ec == 0 && rc) - ec = rc; - fdPop(fd); } fdstat_exit(fd, FDSTAT_CLOSE, rc); DBGIO(fd, (stderr, "==>\tFclose(%p) rc %lx %s\n", (fd ? fd : NULL), (unsigned long)rc, fdbg(fd))); + fdPop(fd); fdFree(fd); return ec; @@ -1320,14 +1428,17 @@ static void cvtfmode (const char *m, switch (*m) { case 'a': + flags &= ~O_ACCMODE; flags |= O_WRONLY | O_CREAT | O_APPEND; if (--nstdio > 0) *stdio++ = *m; break; case 'w': + flags &= ~O_ACCMODE; flags |= O_WRONLY | O_CREAT | O_TRUNC; if (--nstdio > 0) *stdio++ = *m; break; case 'r': + flags &= ~O_ACCMODE; flags |= O_RDONLY; if (--nstdio > 0) *stdio++ = *m; break; @@ -1343,7 +1454,7 @@ static void cvtfmode (const char *m, case '.': break; case '+': - flags &= ~(O_RDONLY|O_WRONLY); + flags &= ~O_ACCMODE; flags |= O_RDWR; if (--nstdio > 0) *stdio++ = c; continue; @@ -1357,6 +1468,11 @@ static void cvtfmode (const char *m, if (--nstdio > 0) *stdio++ = c; continue; break; + case '?': + flags |= RPMIO_DEBUG_IO; + if (--nother > 0) *other++ = c; + continue; + break; default: if (--nother > 0) *other++ = c; continue; @@ -1372,17 +1488,49 @@ static void cvtfmode (const char *m, *f = flags; } +static FDIO_t findIOT(const char *name) +{ + static FDIO_t fdio_types[] = { + &fdio_s, + &ufdio_s, + &gzdio_s, +#if HAVE_BZLIB_H + &bzdio_s, +#endif +#if HAVE_LZMA_H + &xzdio_s, + &lzdio_s, +#endif +#ifdef HAVE_ZSTD + &zstdio_s, +#endif + NULL + }; + FDIO_t iot = NULL; + + for (FDIO_t *t = fdio_types; t && *t; t++) { + if (rstreq(name, (*t)->ioname) || + ((*t)->name && rstreq(name, (*t)->name))) { + iot = (*t); + break; + } + } + + return iot; +} + FD_t Fdopen(FD_t ofd, const char *fmode) { char stdio[20], other[20], zstdio[40]; const char *end = NULL; - FDIO_t iof = NULL; + FDIO_t iot = NULL; FD_t fd = ofd; + int fdno = Fileno(ofd); if (_rpmio_debug) fprintf(stderr, "*** Fdopen(%p,%s) %s\n", fd, fmode, fdbg(fd)); - if (fd == NULL || fmode == NULL) + if (fd == NULL || fmode == NULL || fdno < 0) return NULL; cvtfmode(fmode, stdio, sizeof(stdio), other, sizeof(other), &end, NULL); @@ -1396,37 +1544,16 @@ fprintf(stderr, "*** Fdopen(%p,%s) %s\n", fd, fmode, fdbg(fd)); return fd; if (end && *end) { - if (rstreq(end, "fdio")) { - iof = fdio; - } else if (rstreq(end, "gzdio") || rstreq(end, "gzip")) { - iof = gzdio; - fd = gzdFdopen(fd, zstdio); -#if HAVE_BZLIB_H - } else if (rstreq(end, "bzdio") || rstreq(end, "bzip2")) { - iof = bzdio; - fd = bzdFdopen(fd, zstdio); -#endif -#if HAVE_LZMA_H - } else if (rstreq(end, "xzdio") || rstreq(end, "xz")) { - iof = xzdio; - fd = xzdFdopen(fd, zstdio); - } else if (rstreq(end, "lzdio") || rstreq(end, "lzma")) { - iof = lzdio; - fd = lzdFdopen(fd, zstdio); -#endif - } else if (rstreq(end, "ufdio")) { - iof = ufdio; - } + iot = findIOT(end); } else if (other[0] != '\0') { for (end = other; *end && strchr("0123456789fh", *end); end++) {}; - if (*end == '\0') { - iof = gzdio; - fd = gzdFdopen(fd, zstdio); - } + if (*end == '\0') + iot = findIOT("gzdio"); } - if (iof == NULL) - return fd; + + if (iot && iot->_fdopen) + fd = iot->_fdopen(fd, fdno, zstdio); DBGIO(fd, (stderr, "==> Fdopen(%p,\"%s\") returns fd %p %s\n", ofd, fmode, (fd ? fd : NULL), fdbg(fd))); return fd; @@ -1438,7 +1565,7 @@ FD_t Fopen(const char *path, const char *fmode) const char *end = NULL; mode_t perms = 0666; int flags = 0; - FD_t fd; + FD_t fd = NULL; if (path == NULL || fmode == NULL) return NULL; @@ -1449,39 +1576,16 @@ FD_t Fopen(const char *path, const char *fmode) return NULL; if (end == NULL || rstreq(end, "fdio")) { -if (_rpmio_debug) -fprintf(stderr, "*** Fopen fdio path %s fmode %s\n", path, fmode); + if (_rpmio_debug) + fprintf(stderr, "*** Fopen fdio path %s fmode %s\n", path, fmode); fd = fdOpen(path, flags, perms); - if (fdFileno(fd) < 0) { - if (fd) (void) fdClose(fd); - return NULL; - } } else { - /* XXX gzdio and bzdio here too */ - - switch (urlIsURL(path)) { - case URL_IS_HTTPS: - case URL_IS_HTTP: - case URL_IS_HKP: - case URL_IS_PATH: - case URL_IS_DASH: - case URL_IS_FTP: - case URL_IS_UNKNOWN: -if (_rpmio_debug) -fprintf(stderr, "*** Fopen ufdio path %s fmode %s\n", path, fmode); - fd = ufdOpen(path, flags, perms); - if (fd == NULL || !(fdFileno(fd) >= 0)) - return fd; - break; - default: -if (_rpmio_debug) -fprintf(stderr, "*** Fopen WTFO path %s fmode %s\n", path, fmode); - return NULL; - break; - } - + if (_rpmio_debug) + fprintf(stderr, "*** Fopen ufdio path %s fmode %s\n", path, fmode); + fd = ufdOpen(path, flags, perms); } + /* Open compressed stream if necessary */ if (fd) fd = Fdopen(fd, fmode); @@ -1495,9 +1599,10 @@ int Fflush(FD_t fd) { int rc = -1; if (fd != NULL) { - fdio_fflush_function_t _fflush = FDIOVEC(fd, _fflush); + FDSTACK_t fps = fdGetFps(fd); + fdio_fflush_function_t _fflush = FDIOVEC(fps, _fflush); - rc = (_fflush ? _fflush(fd) : -2); + rc = (_fflush ? _fflush(fps) : -2); } return rc; } @@ -1506,42 +1611,25 @@ off_t Ftell(FD_t fd) { off_t pos = -1; if (fd != NULL) { - fdio_ftell_function_t _ftell = FDIOVEC(fd, _ftell); + FDSTACK_t fps = fdGetFps(fd); + fdio_ftell_function_t _ftell = FDIOVEC(fps, _ftell); - pos = (_ftell ? _ftell(fd) : -2); + pos = (_ftell ? _ftell(fps) : -2); } return pos; } int Ferror(FD_t fd) { - int i, rc = 0; + int rc = 0; if (fd == NULL) return -1; - for (i = fd->nfps; rc == 0 && i >= 0; i--) { - FDSTACK_t * fps = &fd->fps[i]; - int ec; - - if (fps->io == gzdio) { - ec = (fd->syserrno || fd->errcookie != NULL) ? -1 : 0; - i--; /* XXX fdio under gzdio always has fdno == -1 */ -#if HAVE_BZLIB_H - } else if (fps->io == bzdio) { - ec = (fd->syserrno || fd->errcookie != NULL) ? -1 : 0; - i--; /* XXX fdio under bzdio always has fdno == -1 */ -#endif -#if HAVE_LZMA_H - } else if (fps->io == xzdio || fps->io == lzdio) { - ec = (fd->syserrno || fd->errcookie != NULL) ? -1 : 0; - i--; /* XXX fdio under xzdio/lzdio always has fdno == -1 */ -#endif - } else { - /* XXX need to check ufdio/gzdio/bzdio/fdio errors correctly. */ - ec = (fdFileno(fd) < 0 ? -1 : 0); - } + for (FDSTACK_t fps = fd->fps; fps != NULL; fps = fps->prev) { + fdio_ferror_function_t _ferror = FDIOVEC(fps, _ferror); + rc = _ferror(fps); - if (rc == 0 && ec) - rc = ec; + if (rc) + break; } DBGIO(fd, (stderr, "==> Ferror(%p) rc %d %s\n", fd, rc, fdbg(fd))); return rc; @@ -1549,11 +1637,13 @@ DBGIO(fd, (stderr, "==> Ferror(%p) rc %d %s\n", fd, rc, fdbg(fd))); int Fileno(FD_t fd) { - int i, rc = -1; + int rc = -1; if (fd == NULL) return -1; - for (i = fd->nfps ; rc == -1 && i >= 0; i--) { - rc = fd->fps[i].fdno; + for (FDSTACK_t fps = fd->fps; fps != NULL; fps = fps->prev) { + rc = fps->fdno; + if (rc != -1) + break; } DBGIO(fd, (stderr, "==> Fileno(%p) rc %d %s\n", (fd ? fd : NULL), rc, fdbg(fd))); @@ -1627,11 +1717,16 @@ exit: void fdInitDigest(FD_t fd, int hashalgo, rpmDigestFlags flags) { + return fdInitDigestID(fd, hashalgo, hashalgo, flags); +} + +void fdInitDigestID(FD_t fd, int hashalgo, int id, rpmDigestFlags flags) +{ if (fd->digests == NULL) { fd->digests = rpmDigestBundleNew(); } fdstat_enter(fd, FDSTAT_DIGEST); - rpmDigestBundleAdd(fd->digests, hashalgo, flags); + rpmDigestBundleAddID(fd->digests, hashalgo, id, flags); fdstat_exit(fd, FDSTAT_DIGEST, (ssize_t) 0); } @@ -1644,14 +1739,22 @@ static void fdUpdateDigests(FD_t fd, const void * buf, size_t buflen) } } -void fdFiniDigest(FD_t fd, int hashalgo, +void fdFiniDigest(FD_t fd, int id, void ** datap, size_t * lenp, int asAscii) { if (fd && fd->digests) { fdstat_enter(fd, FDSTAT_DIGEST); - rpmDigestBundleFinal(fd->digests, hashalgo, datap, lenp, asAscii); + rpmDigestBundleFinal(fd->digests, id, datap, lenp, asAscii); fdstat_exit(fd, FDSTAT_DIGEST, (ssize_t) 0); } } +DIGEST_CTX fdDupDigest(FD_t fd, int id) +{ + DIGEST_CTX ctx = NULL; + if (fd && fd->digests) + ctx = rpmDigestBundleDupCtx(fd->digests, id); + + return ctx; +} diff --git a/rpmio/rpmio.h b/rpmio/rpmio.h index 8cebe16ca..9bd108649 100644 --- a/rpmio/rpmio.h +++ b/rpmio/rpmio.h @@ -4,6 +4,7 @@ /** \ingroup rpmio * \file rpmio/rpmio.h * + * RPM I/O API (Fd_t is RPM equivalent to libc's FILE) */ #include <sys/types.h> diff --git a/rpmio/rpmio_internal.h b/rpmio/rpmio_internal.h index 8c9f1a81a..fbed183b0 100644 --- a/rpmio/rpmio_internal.h +++ b/rpmio/rpmio_internal.h @@ -13,20 +13,24 @@ extern "C" { #endif void fdSetBundle(FD_t fd, rpmDigestBundle bundle); -rpmDigestBundle fdGetBundle(FD_t fd); +rpmDigestBundle fdGetBundle(FD_t fd, int create); /** \ingroup rpmio * Attach digest to fd. */ void fdInitDigest(FD_t fd, int hashalgo, rpmDigestFlags flags); +void fdInitDigestID(FD_t fd, int hashalgo, int id, rpmDigestFlags flags); + /** \ingroup rpmio */ -void fdFiniDigest(FD_t fd, int hashalgo, +void fdFiniDigest(FD_t fd, int id, void ** datap, size_t * lenp, int asAscii); +DIGEST_CTX fdDupDigest(FD_t fd, int id); + /** * Read an entire file into a buffer. * @param fn file name to read diff --git a/rpmio/rpmkeyring.c b/rpmio/rpmkeyring.c index 18fdfa01c..4e14de1e5 100644 --- a/rpmio/rpmkeyring.c +++ b/rpmio/rpmkeyring.c @@ -1,5 +1,7 @@ #include "system.h" +#include <pthread.h> + #include <rpm/rpmstring.h> #include <rpm/rpmpgp.h> #include <rpm/rpmfileutil.h> @@ -17,17 +19,16 @@ struct rpmPubkey_s { pgpKeyID_t keyid; pgpDigParams pgpkey; int nrefs; + pthread_rwlock_t lock; }; struct rpmKeyring_s { struct rpmPubkey_s **keys; size_t numkeys; int nrefs; + pthread_rwlock_t lock; }; -static rpmPubkey rpmPubkeyUnlink(rpmPubkey key); -static rpmKeyring rpmKeyringUnlink(rpmKeyring keyring); - static int keyidcmp(const void *k1, const void *k2) { const struct rpmPubkey_s *key1 = *(const struct rpmPubkey_s **) k1; @@ -41,27 +42,30 @@ rpmKeyring rpmKeyringNew(void) rpmKeyring keyring = xcalloc(1, sizeof(*keyring)); keyring->keys = NULL; keyring->numkeys = 0; - keyring->nrefs = 0; - return rpmKeyringLink(keyring); + keyring->nrefs = 1; + pthread_rwlock_init(&keyring->lock, NULL); + return keyring; } rpmKeyring rpmKeyringFree(rpmKeyring keyring) { - if (keyring == NULL) { + if (keyring == NULL) return NULL; - } - - if (keyring->nrefs > 1) { - return rpmKeyringUnlink(keyring); - } - if (keyring->keys) { - for (int i = 0; i < keyring->numkeys; i++) { - keyring->keys[i] = rpmPubkeyFree(keyring->keys[i]); + pthread_rwlock_wrlock(&keyring->lock); + if (--keyring->nrefs == 0) { + if (keyring->keys) { + for (int i = 0; i < keyring->numkeys; i++) { + keyring->keys[i] = rpmPubkeyFree(keyring->keys[i]); + } + free(keyring->keys); } - free(keyring->keys); + pthread_rwlock_unlock(&keyring->lock); + pthread_rwlock_destroy(&keyring->lock); + free(keyring); + } else { + pthread_rwlock_unlock(&keyring->lock); } - free(keyring); return NULL; } @@ -74,38 +78,36 @@ static rpmPubkey rpmKeyringFindKeyid(rpmKeyring keyring, rpmPubkey key) int rpmKeyringAddKey(rpmKeyring keyring, rpmPubkey key) { + int rc = 1; /* assume already seen key */ if (keyring == NULL || key == NULL) return -1; - /* check if we already have this key */ - if (rpmKeyringFindKeyid(keyring, key)) { - return 1; + /* check if we already have this key, but always wrlock for simplicity */ + pthread_rwlock_wrlock(&keyring->lock); + if (!rpmKeyringFindKeyid(keyring, key)) { + keyring->keys = xrealloc(keyring->keys, + (keyring->numkeys + 1) * sizeof(rpmPubkey)); + keyring->keys[keyring->numkeys] = rpmPubkeyLink(key); + keyring->numkeys++; + qsort(keyring->keys, keyring->numkeys, sizeof(*keyring->keys), + keyidcmp); + rc = 0; } - - keyring->keys = xrealloc(keyring->keys, (keyring->numkeys + 1) * sizeof(rpmPubkey)); - keyring->keys[keyring->numkeys] = rpmPubkeyLink(key); - keyring->numkeys++; - qsort(keyring->keys, keyring->numkeys, sizeof(*keyring->keys), keyidcmp); + pthread_rwlock_unlock(&keyring->lock); - return 0; + return rc; } rpmKeyring rpmKeyringLink(rpmKeyring keyring) { if (keyring) { + pthread_rwlock_wrlock(&keyring->lock); keyring->nrefs++; + pthread_rwlock_unlock(&keyring->lock); } return keyring; } -static rpmKeyring rpmKeyringUnlink(rpmKeyring keyring) -{ - if (keyring) { - keyring->nrefs--; - } - return NULL; -} - rpmPubkey rpmPubkeyRead(const char *filename) { uint8_t *pkt = NULL; @@ -131,7 +133,7 @@ rpmPubkey rpmPubkeyNew(const uint8_t *pkt, size_t pktlen) if (pkt == NULL || pktlen == 0) goto exit; - if (pgpPubkeyFingerprint(pkt, pktlen, keyid)) + if (pgpPubkeyKeyID(pkt, pktlen, keyid)) goto exit; if (pgpPrtParams(pkt, pktlen, PGPTAG_PUBLIC_KEY, &pgpkey)) @@ -141,12 +143,45 @@ rpmPubkey rpmPubkeyNew(const uint8_t *pkt, size_t pktlen) key->pkt = xmalloc(pktlen); key->pktlen = pktlen; key->pgpkey = pgpkey; - key->nrefs = 0; + key->nrefs = 1; memcpy(key->pkt, pkt, pktlen); memcpy(key->keyid, keyid, sizeof(keyid)); + pthread_rwlock_init(&key->lock, NULL); exit: - return rpmPubkeyLink(key); + return key; +} + +rpmPubkey *rpmGetSubkeys(rpmPubkey mainkey, int *count) +{ + rpmPubkey *subkeys = NULL; + pgpDigParams *pgpsubkeys = NULL; + int pgpsubkeysCount = 0; + int i; + + if (mainkey && !pgpPrtParamsSubkeys(mainkey->pkt, mainkey->pktlen, + mainkey->pgpkey, &pgpsubkeys, &pgpsubkeysCount)) { + + subkeys = xmalloc(pgpsubkeysCount * sizeof(*subkeys)); + + for (i = 0; i < pgpsubkeysCount; i++) { + rpmPubkey subkey = xcalloc(1, sizeof(*subkey)); + subkeys[i] = subkey; + + /* Packets with all subkeys already stored in main key */ + subkey->pkt = NULL; + subkey->pktlen = 0; + + subkey->pgpkey = pgpsubkeys[i]; + memcpy(subkey->keyid, pgpsubkeys[i]->signid, sizeof(subkey->keyid)); + subkey->nrefs = 1; + pthread_rwlock_init(&subkey->lock, NULL); + } + free(pgpsubkeys); + } + *count = pgpsubkeysCount; + + return subkeys; } rpmPubkey rpmPubkeyFree(rpmPubkey key) @@ -154,31 +189,29 @@ rpmPubkey rpmPubkeyFree(rpmPubkey key) if (key == NULL) return NULL; - if (key->nrefs > 1) - return rpmPubkeyUnlink(key); - - pgpDigParamsFree(key->pgpkey); - free(key->pkt); - free(key); + pthread_rwlock_wrlock(&key->lock); + if (--key->nrefs == 0) { + pgpDigParamsFree(key->pgpkey); + free(key->pkt); + pthread_rwlock_unlock(&key->lock); + pthread_rwlock_destroy(&key->lock); + free(key); + } else { + pthread_rwlock_unlock(&key->lock); + } return NULL; } rpmPubkey rpmPubkeyLink(rpmPubkey key) { if (key) { + pthread_rwlock_wrlock(&key->lock); key->nrefs++; + pthread_rwlock_unlock(&key->lock); } return key; } -static rpmPubkey rpmPubkeyUnlink(rpmPubkey key) -{ - if (key) { - key->nrefs--; - } - return NULL; -} - pgpDig rpmPubkeyDig(rpmPubkey key) { pgpDig dig = NULL; @@ -189,12 +222,15 @@ pgpDig rpmPubkeyDig(rpmPubkey key) return NULL; dig = pgpNewDig(); + + pthread_rwlock_rdlock(&key->lock); rc = pgpPrtPkts(key->pkt, key->pktlen, dig, 0); + pthread_rwlock_unlock(&key->lock); + if (rc == 0) { pgpDigParams pubp = pgpDigGetParams(dig, PGPTAG_PUBLIC_KEY); if (!pubp || !memcmp(pubp->signid, zeros, sizeof(pubp->signid)) || - !memcmp(pubp->time, zeros, sizeof(pubp->time)) || - pubp->userid == NULL) { + pubp->time == 0 || pubp->userid == NULL) { rc = -1; } } @@ -210,11 +246,23 @@ char * rpmPubkeyBase64(rpmPubkey key) char *enc = NULL; if (key) { + pthread_rwlock_rdlock(&key->lock); enc = rpmBase64Encode(key->pkt, key->pktlen, -1); + pthread_rwlock_unlock(&key->lock); } return enc; } +pgpDigParams rpmPubkeyPgpDigParams(rpmPubkey key) +{ + pgpDigParams params= NULL; + + if (key) { + params = key->pgpkey; + } + return params; +} + static rpmPubkey findbySig(rpmKeyring keyring, pgpDigParams sig) { rpmPubkey key = NULL; @@ -239,6 +287,8 @@ static rpmPubkey findbySig(rpmKeyring keyring, pgpDigParams sig) rpmRC rpmKeyringLookup(rpmKeyring keyring, pgpDig sig) { + pthread_rwlock_rdlock(&keyring->lock); + rpmRC res = RPMRC_NOKEY; pgpDigParams sigp = pgpDigGetParams(sig, PGPTAG_SIGNATURE); rpmPubkey key = findbySig(keyring, sigp); @@ -253,6 +303,7 @@ rpmRC rpmKeyringLookup(rpmKeyring keyring, pgpDig sig) res = RPMRC_OK; } + pthread_rwlock_unlock(&keyring->lock); return res; } @@ -261,6 +312,8 @@ rpmRC rpmKeyringVerifySig(rpmKeyring keyring, pgpDigParams sig, DIGEST_CTX ctx) rpmRC rc = RPMRC_FAIL; if (sig && ctx) { + pthread_rwlock_rdlock(&keyring->lock); + pgpDigParams pgpkey = NULL; rpmPubkey key = findbySig(keyring, sig); @@ -269,6 +322,8 @@ rpmRC rpmKeyringVerifySig(rpmKeyring keyring, pgpDigParams sig, DIGEST_CTX ctx) /* We call verify even if key not found for a signature sanity check */ rc = pgpVerifySignature(pgpkey, sig, ctx); + + pthread_rwlock_unlock(&keyring->lock); } return rc; diff --git a/rpmio/rpmkeyring.h b/rpmio/rpmkeyring.h index 9fcab5feb..8aeed8c6c 100644 --- a/rpmio/rpmkeyring.h +++ b/rpmio/rpmkeyring.h @@ -3,6 +3,8 @@ /** \ingroup rpmkeyring * \file rpmio/rpmkeyring.h + * + * RPM keyring API */ #include <rpm/rpmtypes.h> @@ -65,6 +67,14 @@ rpmKeyring rpmKeyringLink(rpmKeyring keyring); rpmPubkey rpmPubkeyNew(const uint8_t *pkt, size_t pktlen); /** \ingroup rpmkeyring + * Return array of subkeys belonging to maikey + * param mainkey main rpmPubkey + * param count count of returned subkeys + * @return an array of subkey's handles + */ +rpmPubkey *rpmGetSubkeys(rpmPubkey mainkey, int *count); + +/** \ingroup rpmkeyring * Create a new rpmPubkey from ASCII-armored pubkey file * @param filename Path to pubkey file * @return new pubkey handle @@ -99,6 +109,13 @@ pgpDig rpmPubkeyDig(rpmPubkey key); */ char * rpmPubkeyBase64(rpmPubkey key); +/** \ingroup rpmkeyring + * Return pgp params of key + * @param key Pubkey + * @return pgp params, NULL on error + */ +pgpDigParams rpmPubkeyPgpDigParams(rpmPubkey key); + #ifdef __cplusplus } #endif diff --git a/rpmio/rpmlog.c b/rpmio/rpmlog.c index 8023d5c44..223af32aa 100644 --- a/rpmio/rpmlog.c +++ b/rpmio/rpmlog.c @@ -5,11 +5,22 @@ #include "system.h" #include <stdarg.h> #include <stdlib.h> +#include <pthread.h> +#include <errno.h> #include <rpm/rpmlog.h> +#include <rpm/rpmmacro.h> #include "debug.h" -static int nrecs = 0; -static rpmlogRec recs = NULL; +typedef struct rpmlogCtx_s * rpmlogCtx; +struct rpmlogCtx_s { + pthread_rwlock_t lock; + unsigned mask; + int nrecs; + rpmlogRec recs; + rpmlogCallback cbfunc; + rpmlogCallbackData cbdata; + FILE *stdlog; +}; struct rpmlogRec_s { int code; /* unused */ @@ -17,64 +28,110 @@ struct rpmlogRec_s { char * message; /* log message string */ }; +/* Force log context acquisition through a function */ +static rpmlogCtx rpmlogCtxAcquire(int write) +{ + static struct rpmlogCtx_s _globalCtx = { PTHREAD_RWLOCK_INITIALIZER, + RPMLOG_UPTO(RPMLOG_NOTICE), + 0, NULL, NULL, NULL, NULL }; + rpmlogCtx ctx = &_globalCtx; + int xx; + + /* XXX Silently failing is bad, but we can't very well use log here... */ + if (write) + xx = pthread_rwlock_wrlock(&ctx->lock); + else + xx = pthread_rwlock_rdlock(&ctx->lock); + + return (xx == 0) ? ctx : NULL; +} + +/* Release log context */ +static rpmlogCtx rpmlogCtxRelease(rpmlogCtx ctx) +{ + if (ctx) + pthread_rwlock_unlock(&ctx->lock); + return NULL; +} + int rpmlogGetNrecs(void) { + rpmlogCtx ctx = rpmlogCtxAcquire(0); + int nrecs = -1; + if (ctx) + nrecs = ctx->nrecs; + rpmlogCtxRelease(ctx); return nrecs; } int rpmlogCode(void) { - if (recs != NULL && nrecs > 0) - return recs[nrecs-1].code; - return -1; -} + int code = -1; + rpmlogCtx ctx = rpmlogCtxAcquire(0); + + if (ctx && ctx->recs != NULL && ctx->nrecs > 0) + code = ctx->recs[ctx->nrecs-1].code; + rpmlogCtxRelease(ctx); + return code; +} const char * rpmlogMessage(void) { - if (recs != NULL && nrecs > 0) - return recs[nrecs-1].message; - return _("(no error)"); + const char *msg = _("(no error)"); + rpmlogCtx ctx = rpmlogCtxAcquire(0); + + if (ctx && ctx->recs != NULL && ctx->nrecs > 0) + msg = ctx->recs[ctx->nrecs-1].message; + + rpmlogCtxRelease(ctx); + return msg; } const char * rpmlogRecMessage(rpmlogRec rec) { - assert(rec != NULL); - return (rec->message); + return (rec != NULL) ? rec->message : NULL; } rpmlogLvl rpmlogRecPriority(rpmlogRec rec) { - assert(rec != NULL); - return (rec->pri); + return (rec != NULL) ? rec->pri : (rpmlogLvl)-1; } void rpmlogPrint(FILE *f) { - int i; + rpmlogCtx ctx = rpmlogCtxAcquire(0); + + if (ctx == NULL) + return; if (f == NULL) f = stderr; - if (recs) - for (i = 0; i < nrecs; i++) { - rpmlogRec rec = recs + i; + for (int i = 0; i < ctx->nrecs; i++) { + rpmlogRec rec = ctx->recs + i; if (rec->message && *rec->message) fprintf(f, " %s", rec->message); } + + rpmlogCtxRelease(ctx); } void rpmlogClose (void) { - int i; + rpmlogCtx ctx = rpmlogCtxAcquire(1); - if (recs) - for (i = 0; i < nrecs; i++) { - rpmlogRec rec = recs + i; + if (ctx == NULL) + return; + + for (int i = 0; i < ctx->nrecs; i++) { + rpmlogRec rec = ctx->recs + i; rec->message = _free(rec->message); } - recs = _free(recs); - nrecs = 0; + ctx->recs = _free(ctx->recs); + ctx->nrecs = 0; + + rpmlogCtxRelease(ctx); } void rpmlogOpen (const char *ident, int option, @@ -82,65 +139,51 @@ void rpmlogOpen (const char *ident, int option, { } -static unsigned rpmlogMask = RPMLOG_UPTO( RPMLOG_NOTICE ); - #ifdef NOTYET static unsigned rpmlogFacility = RPMLOG_USER; #endif int rpmlogSetMask (int mask) { - int omask = rpmlogMask; - if (mask) - rpmlogMask = mask; - return omask; -} + rpmlogCtx ctx = rpmlogCtxAcquire(mask ? 1 : 0); -static rpmlogCallback _rpmlogCallback = NULL; -static rpmlogCallbackData _rpmlogCallbackData = NULL; + int omask = -1; + if (ctx) { + omask = ctx->mask; + if (mask) + ctx->mask = mask; + } -rpmlogCallback rpmlogSetCallback(rpmlogCallback cb, rpmlogCallbackData data) -{ - rpmlogCallback ocb = _rpmlogCallback; - _rpmlogCallback = cb; - _rpmlogCallbackData = data; - return ocb; + rpmlogCtxRelease(ctx); + return omask; } -static FILE * _stdlog = NULL; - -static int rpmlogDefault(rpmlogRec rec) +rpmlogCallback rpmlogSetCallback(rpmlogCallback cb, rpmlogCallbackData data) { - FILE *msgout = (_stdlog ? _stdlog : stderr); + rpmlogCtx ctx = rpmlogCtxAcquire(1); - switch (rec->pri) { - case RPMLOG_INFO: - case RPMLOG_NOTICE: - msgout = (_stdlog ? _stdlog : stdout); - break; - case RPMLOG_EMERG: - case RPMLOG_ALERT: - case RPMLOG_CRIT: - case RPMLOG_ERR: - case RPMLOG_WARNING: - case RPMLOG_DEBUG: - default: - break; + rpmlogCallback ocb = NULL; + if (ctx) { + ocb = ctx->cbfunc; + ctx->cbfunc = cb; + ctx->cbdata = data; } - (void) fputs(rpmlogLevelPrefix(rec->pri), msgout); - - (void) fputs(rec->message, msgout); - (void) fflush(msgout); - - return (rec->pri <= RPMLOG_CRIT ? RPMLOG_EXIT : 0); + rpmlogCtxRelease(ctx); + return ocb; } - FILE * rpmlogSetFile(FILE * fp) { - FILE * ofp = _stdlog; - _stdlog = fp; + rpmlogCtx ctx = rpmlogCtxAcquire(1); + + FILE * ofp = NULL; + if (ctx) { + ofp = ctx->stdlog; + ctx->stdlog = fp; + } + + rpmlogCtxRelease(ctx); return ofp; } @@ -155,6 +198,38 @@ static const char * const rpmlogMsgPrefix[] = { "D: ", /*!< RPMLOG_DEBUG */ }; +#define ANSI_COLOR_BLACK "\x1b[30m" +#define ANSI_COLOR_RED "\x1b[31m" +#define ANSI_COLOR_GREEN "\x1b[32m" +#define ANSI_COLOR_YELLOW "\x1b[33m" +#define ANSI_COLOR_BLUE "\x1b[34m" +#define ANSI_COLOR_MAGENTA "\x1b[35m" +#define ANSI_COLOR_CYAN "\x1b[36m" +#define ANSI_COLOR_WHITE "\x1b[37m" + +#define ANSI_BRIGHT_BLACK "\x1b[30;1m" +#define ANSI_BRIGHT_RED "\x1b[31;1m" +#define ANSI_BRIGHT_GREEN "\x1b[32;1m" +#define ANSI_BRIGHT_YELLOW "\x1b[33;1m" +#define ANSI_BRIGHT_BLUE "\x1b[34;1m" +#define ANSI_BRIGHT_MAGENTA "\x1b[35;1m" +#define ANSI_BRIGHT_CYAN "\x1b[36;1m" +#define ANSI_BRIGHT_WHITE "\x1b[37;1m" + +#define ANSI_COLOR_BOLD "\x1b[1m" +#define ANSI_COLOR_RESET "\x1b[0m" + +static const char *rpmlogMsgPrefixColor[] = { + ANSI_BRIGHT_RED, /*!< RPMLOG_EMERG */ + ANSI_BRIGHT_RED, /*!< RPMLOG_ALERT */ + ANSI_BRIGHT_RED, /*!< RPMLOG_CRIT */ + ANSI_BRIGHT_RED, /*!< RPMLOG_ERR */ + ANSI_BRIGHT_MAGENTA,/*!< RPMLOG_WARNING */ + "", /*!< RPMLOG_NOTICE */ + "", /*!< RPMLOG_INFO */ + ANSI_BRIGHT_BLUE, /*!< RPMLOG_DEBUG */ +}; + const char * rpmlogLevelPrefix(rpmlogLvl pri) { const char * prefix = ""; @@ -163,46 +238,174 @@ const char * rpmlogLevelPrefix(rpmlogLvl pri) return prefix; } +static const char * rpmlogLevelColor(rpmlogLvl pri) +{ + return rpmlogMsgPrefixColor[pri&0x7]; +} + +enum { + COLOR_NO = 0, + COLOR_AUTO = 1, + COLOR_ALWAYS = 2, +}; + +static int getColorConfig(void) +{ + int rc = COLOR_NO; + char * color = rpmExpand("%{?_color_output}%{!?_color_output:auto}", NULL); + if (rstreq(color, "auto")) + rc = COLOR_AUTO; + else if (rstreq(color, "always")) + rc = COLOR_ALWAYS; + free(color); + return rc; +} + +static int rpmlogDefault(FILE *stdlog, rpmlogRec rec) +{ + static const char fubar[] = + "Error occurred during writing of a log message"; + FILE *msgout = (stdlog ? stdlog : stderr); + static __thread int color = -1; + const char * colorOn = NULL; + + if (color < 0) + color = getColorConfig(); + + if (color == COLOR_ALWAYS || + (color == COLOR_AUTO && isatty(fileno(msgout)))) + colorOn = rpmlogLevelColor(rec->pri); + + switch (rec->pri) { + case RPMLOG_INFO: + case RPMLOG_NOTICE: + msgout = (stdlog ? stdlog : stdout); + break; + case RPMLOG_EMERG: + case RPMLOG_ALERT: + case RPMLOG_CRIT: + case RPMLOG_ERR: + case RPMLOG_WARNING: + case RPMLOG_DEBUG: + if (colorOn && *colorOn) + if (fputs(rpmlogLevelColor(rec->pri), msgout) == EOF + && errno != EPIPE) + perror(fubar); + break; + default: + break; + } + + if (fputs(rpmlogLevelPrefix(rec->pri), msgout) == EOF && errno != EPIPE) + perror(fubar); + + switch (rec->pri) { + case RPMLOG_INFO: + case RPMLOG_NOTICE: + break; + case RPMLOG_EMERG: + case RPMLOG_ALERT: + case RPMLOG_CRIT: + case RPMLOG_ERR: + case RPMLOG_WARNING: + if (colorOn && *colorOn) { + if (fputs(ANSI_COLOR_RESET, msgout) == EOF && errno != EPIPE) + perror(fubar); + if (fputs(ANSI_COLOR_BOLD, msgout) == EOF && errno != EPIPE) + perror(fubar); + } + case RPMLOG_DEBUG: + default: + break; + } + + if (rec->message) + (void) fputs(rec->message, msgout); + + switch (rec->pri) { + case RPMLOG_INFO: + case RPMLOG_NOTICE: + break; + case RPMLOG_EMERG: + case RPMLOG_ALERT: + case RPMLOG_CRIT: + case RPMLOG_ERR: + case RPMLOG_WARNING: + case RPMLOG_DEBUG: + if (colorOn && *colorOn) + if (fputs(ANSI_COLOR_RESET, msgout) == EPIPE && errno != EPIPE) + perror(fubar); + break; + default: + break; + } + + (void) fflush(msgout); + + return (rec->pri <= RPMLOG_CRIT ? RPMLOG_EXIT : 0); +} + /* FIX: rpmlogMsgPrefix[] dependent, not unqualified */ /* FIX: rpmlogMsgPrefix[] may be NULL */ -static void dolog (struct rpmlogRec_s *rec) +static void dolog(struct rpmlogRec_s *rec, int saverec) { + static pthread_mutex_t serialize = PTHREAD_MUTEX_INITIALIZER; + int cbrc = RPMLOG_DEFAULT; int needexit = 0; + FILE *clog = NULL; + rpmlogCallbackData *cbdata = NULL; + rpmlogCallback cbfunc = NULL; + rpmlogCtx ctx = rpmlogCtxAcquire(saverec); + + if (ctx == NULL) + return; /* Save copy of all messages at warning (or below == "more important"). */ - if (rec->pri <= RPMLOG_WARNING) { - recs = xrealloc(recs, (nrecs+2) * sizeof(*recs)); - recs[nrecs].code = rec->code; - recs[nrecs].pri = rec->pri; - recs[nrecs].message = xstrdup(rec->message); - recs[nrecs+1].code = 0; - recs[nrecs+1].message = NULL; - ++nrecs; + if (saverec) { + ctx->recs = xrealloc(ctx->recs, (ctx->nrecs+2) * sizeof(*ctx->recs)); + ctx->recs[ctx->nrecs].code = rec->code; + ctx->recs[ctx->nrecs].pri = rec->pri; + ctx->recs[ctx->nrecs].message = xstrdup(rec->message); + ctx->recs[ctx->nrecs+1].code = 0; + ctx->recs[ctx->nrecs+1].message = NULL; + ctx->nrecs++; } - - if (_rpmlogCallback) { - cbrc = _rpmlogCallback(rec, _rpmlogCallbackData); - needexit += cbrc & RPMLOG_EXIT; - } - - if (cbrc & RPMLOG_DEFAULT) { - cbrc = rpmlogDefault(rec); - needexit += cbrc & RPMLOG_EXIT; + cbfunc = ctx->cbfunc; + cbdata = ctx->cbdata; + clog = ctx->stdlog; + + /* Free the context for callback and actual log output */ + ctx = rpmlogCtxRelease(ctx); + + /* Always serialize callback and output to avoid interleaved messages. */ + if (pthread_mutex_lock(&serialize) == 0) { + if (cbfunc) { + cbrc = cbfunc(rec, cbdata); + needexit += cbrc & RPMLOG_EXIT; + } + + if (cbrc & RPMLOG_DEFAULT) { + cbrc = rpmlogDefault(clog, rec); + needexit += cbrc & RPMLOG_EXIT; + } + pthread_mutex_unlock(&serialize); } if (needexit) exit(EXIT_FAILURE); + } void rpmlog (int code, const char *fmt, ...) { unsigned pri = RPMLOG_PRI(code); unsigned mask = RPMLOG_MASK(pri); + int saverec = (pri <= RPMLOG_WARNING); va_list ap; int n; - if ((mask & rpmlogMask) == 0) + if ((mask & rpmlogSetMask(0)) == 0) return; va_start(ap, fmt); @@ -222,9 +425,8 @@ void rpmlog (int code, const char *fmt, ...) rec.pri = pri; rec.message = msg; - dolog(&rec); + dolog(&rec, saverec); free(msg); } } - diff --git a/rpmio/rpmlog.h b/rpmio/rpmlog.h index 0766086f4..6b0d8f70a 100644 --- a/rpmio/rpmlog.h +++ b/rpmio/rpmlog.h @@ -42,33 +42,6 @@ typedef enum rpmlogLvl_e { #define RPMLOG_PRI(p) ((p) & RPMLOG_PRIMASK) #define RPMLOG_MAKEPRI(fac, pri) ((((unsigned)(fac)) << 3) | (pri)) -#ifdef RPMLOG_NAMES -#define _RPMLOG_NOPRI 0x10 /* the "no priority" priority */ - /* mark "facility" */ -#define _RPMLOG_MARK RPMLOG_MAKEPRI(RPMLOG_NFACILITIES, 0) -typedef struct _rpmcode { - const char *c_name; - int c_val; -} RPMCODE; - -RPMCODE rpmprioritynames[] = - { - { "alert", RPMLOG_ALERT }, - { "crit", RPMLOG_CRIT }, - { "debug", RPMLOG_DEBUG }, - { "emerg", RPMLOG_EMERG }, - { "err", RPMLOG_ERR }, - { "error", RPMLOG_ERR }, /* DEPRECATED */ - { "info", RPMLOG_INFO }, - { "none", _RPMLOG_NOPRI }, /* INTERNAL */ - { "notice", RPMLOG_NOTICE }, - { "panic", RPMLOG_EMERG }, /* DEPRECATED */ - { "warn", RPMLOG_WARNING }, /* DEPRECATED */ - { "warning",RPMLOG_WARNING }, - { NULL, -1 } - }; -#endif - /** \ingroup rpmlog * facility codes */ @@ -104,35 +77,6 @@ typedef enum rpmlogFac_e { #define RPMLOG_FAC(p) (((p) & RPMLOG_FACMASK) >> 3) -#ifdef RPMLOG_NAMES -RPMCODE facilitynames[] = - { - { "auth", RPMLOG_AUTH }, - { "authpriv",RPMLOG_AUTHPRIV }, - { "cron", RPMLOG_CRON }, - { "daemon", RPMLOG_DAEMON }, - { "ftp", RPMLOG_FTP }, - { "kern", RPMLOG_KERN }, - { "lpr", RPMLOG_LPR }, - { "mail", RPMLOG_MAIL }, - { "mark", _RPMLOG_MARK }, /* INTERNAL */ - { "news", RPMLOG_NEWS }, - { "security",RPMLOG_AUTH }, /* DEPRECATED */ - { "syslog", RPMLOG_SYSLOG }, - { "user", RPMLOG_USER }, - { "uucp", RPMLOG_UUCP }, - { "local0", RPMLOG_LOCAL0 }, - { "local1", RPMLOG_LOCAL1 }, - { "local2", RPMLOG_LOCAL2 }, - { "local3", RPMLOG_LOCAL3 }, - { "local4", RPMLOG_LOCAL4 }, - { "local5", RPMLOG_LOCAL5 }, - { "local6", RPMLOG_LOCAL6 }, - { "local7", RPMLOG_LOCAL7 }, - { NULL, -1 } - }; -#endif - /* * arguments to setlogmask. */ diff --git a/rpmio/rpmlua.c b/rpmio/rpmlua.c index 96f4dc1ca..c96fb6b67 100644 --- a/rpmio/rpmlua.c +++ b/rpmio/rpmlua.c @@ -60,6 +60,9 @@ struct rpmluapb_s { static rpmlua globalLuaState = NULL; +static char *(*nextFileFunc)(void *) = NULL; +static void *nextFileFuncParam = NULL; + static int luaopen_rpm(lua_State *L); static int rpm_print(lua_State *L); @@ -80,7 +83,7 @@ rpmlua rpmluaNew() {"posix", luaopen_posix}, {"rex", luaopen_rex}, {"rpm", luaopen_rpm}, - /*{"os", luaopen_rpm_os},*/ + {"os", luaopen_rpm_os}, {NULL, NULL}, }; @@ -94,12 +97,6 @@ rpmlua rpmluaNew() lua_call(L, 1, 0); lua_settop(L, 0); } - /* Disable os and io standard libraries */ - lua_pushnil (L); - lua_setglobal(L, "os"); - lua_pushnil (L); - lua_setglobal(L, "io"); - #ifndef LUA_GLOBALSINDEX lua_pushglobaltable(L); #endif @@ -218,6 +215,12 @@ static int pushvar(lua_State *L, rpmluavType type, void *value) return ret; } +void rpmluaSetNextFileFunc(char *(*func)(void *), void *funcParam) +{ + nextFileFunc = func; + nextFileFuncParam = funcParam; +} + void rpmluaSetVar(rpmlua _lua, rpmluav var) { INITSTATE(_lua, lua); @@ -532,13 +535,15 @@ int rpmluaRunScript(rpmlua _lua, const char *script, const char *name) int ret = 0; if (name == NULL) name = "<lua>"; + if (script == NULL) + script = ""; if (luaL_loadbuffer(L, script, strlen(script), name) != 0) { rpmlog(RPMLOG_ERR, _("invalid syntax in lua script: %s\n"), lua_tostring(L, -1)); lua_pop(L, 1); ret = -1; } else if (lua_pcall(L, 0, 0, 0) != 0) { - rpmlog(RPMLOG_WARNING, _("lua script failed: %s\n"), + rpmlog(RPMLOG_ERR, _("lua script failed: %s\n"), lua_tostring(L, -1)); lua_pop(L, 1); ret = -1; @@ -662,7 +667,9 @@ static int rpm_b64decode(lua_State *L) static int rpm_expand(lua_State *L) { const char *str = luaL_checkstring(L, 1); - char *val = rpmExpand(str, NULL); + char *val = NULL; + if (rpmExpandMacros(NULL, str, &val, 0) < 0) + return luaL_error(L, "error expanding macro"); lua_pushstring(L, val); free(val); return 1; @@ -671,16 +678,42 @@ static int rpm_expand(lua_State *L) static int rpm_define(lua_State *L) { const char *str = luaL_checkstring(L, 1); - (void) rpmDefineMacro(NULL, str, 0); + if (rpmDefineMacro(NULL, str, 0)) + return luaL_error(L, "error defining macro"); + return 0; +} + +static int rpm_undefine(lua_State *L) +{ + const char *str = luaL_checkstring(L, 1); + rpmPopMacro(NULL, str); return 0; } +static int rpm_load(lua_State *L) +{ + const char *str = luaL_checkstring(L, 1); + int rc = rpmLoadMacroFile(NULL, str); + lua_pushnumber(L, rc); + return 1; +} + static int rpm_interactive(lua_State *L) { _rpmluaInteractive(L); return 0; } +static int rpm_next_file(lua_State *L) +{ + if (nextFileFunc) + lua_pushstring(L, nextFileFunc(nextFileFuncParam)); + else + lua_pushstring(L, NULL); + + return 1; +} + typedef struct rpmluaHookData_s { lua_State *L; int funcRef; @@ -867,10 +900,13 @@ static const luaL_Reg rpmlib[] = { {"b64decode", rpm_b64decode}, {"expand", rpm_expand}, {"define", rpm_define}, + {"undefine", rpm_undefine}, + {"load", rpm_load}, {"register", rpm_register}, {"unregister", rpm_unregister}, {"call", rpm_call}, {"interactive", rpm_interactive}, + {"next_file", rpm_next_file}, {NULL, NULL} }; diff --git a/rpmio/rpmlua.h b/rpmio/rpmlua.h index 7298ed57d..935b892bc 100644 --- a/rpmio/rpmlua.h +++ b/rpmio/rpmlua.h @@ -62,6 +62,8 @@ void rpmluaSetData(rpmlua lua, const char *key, const void *data); char *rpmluaPopPrintBuffer(rpmlua lua); void rpmluaPushPrintBuffer(rpmlua lua); +void rpmluaSetNextFileFunc(char *(*func)(void *), void *funcParam); + void rpmluaGetVar(rpmlua lua, rpmluav var); void rpmluaSetVar(rpmlua lua, rpmluav var); void rpmluaDelVar(rpmlua lua, const char *key, ...); diff --git a/rpmio/rpmmacro.h b/rpmio/rpmmacro.h index 765c78c6d..a31beb76a 100644 --- a/rpmio/rpmmacro.h +++ b/rpmio/rpmmacro.h @@ -3,6 +3,8 @@ /** \ingroup rpmio * \file rpmio/rpmmacro.h + * + * Macro API */ #include <stdio.h> @@ -43,6 +45,10 @@ extern const char * macrofiles; #define RMIL_OLDSPEC -1 #define RMIL_GLOBAL 0 +/* Deprecated compatibility wrappers */ +#define addMacro(_mc, _n, _o, _b, _l) rpmPushMacro(_mc, _n, _o, _b, _l) +#define delMacro(_mc, _n) rpmPopMacro(_mc, _n) + /** \ingroup rpmmacro * Print macros to file stream. * @param mc macro context (NULL uses global context). @@ -53,37 +59,35 @@ void rpmDumpMacroTable (rpmMacroContext mc, /** \ingroup rpmmacro * Expand macro into buffer. - * @deprecated Use rpmExpand(). - * @todo Eliminate from API. - * @param spec cookie (unused) * @param mc macro context (NULL uses global context). - * @retval sbuf input macro to expand, output expansion - * @param slen size of buffer - * @return 0 on success + * @param sbuf input macro to expand + * @param obuf macro expansion (malloc'ed) + * @param flags flags (currently unused) + * @return negative on failure */ -int expandMacros (void * spec, rpmMacroContext mc, - char * sbuf, - size_t slen); +int rpmExpandMacros (rpmMacroContext mc, const char * sbuf, + char ** obuf, int flags); /** \ingroup rpmmacro - * Add macro to context. - * @deprecated Use rpmDefineMacro(). + * Push macro to context. * @param mc macro context (NULL uses global context). * @param n macro name - * @param o macro paramaters + * @param o macro parameters * @param b macro body * @param level macro recursion level (0 is entry API) + * @return 0 on success */ -void addMacro (rpmMacroContext mc, const char * n, +int rpmPushMacro (rpmMacroContext mc, const char * n, const char * o, const char * b, int level); /** \ingroup rpmmacro - * Delete macro from context. + * Pop macro from context. * @param mc macro context (NULL uses global context). * @param n macro name + * @return 0 on success */ -void delMacro (rpmMacroContext mc, const char * n); +int rpmPopMacro (rpmMacroContext mc, const char * n); /** \ingroup rpmmacro * Define macro in context. diff --git a/rpmio/rpmpgp.c b/rpmio/rpmpgp.c index e70cf706b..15cce2275 100644 --- a/rpmio/rpmpgp.c +++ b/rpmio/rpmpgp.c @@ -248,6 +248,19 @@ static void pgpPrtVal(const char * pre, pgpValTbl vs, uint8_t val) fprintf(stderr, "%s(%u)", pgpValStr(vs, val), (unsigned)val); } +static void pgpPrtTime(const char * pre, const uint8_t *p, size_t plen) +{ + if (!_print) return; + if (pre && *pre) + fprintf(stderr, "%s", pre); + if (plen == 4) { + time_t t = pgpGrab(p, plen); + fprintf(stderr, " %-24.24s(0x%08x)", ctime(&t), (unsigned)t); + } else { + pgpPrtHex("", p+1, plen-1); + } +} + /** \ingroup rpmpgp * Return hex formatted representation of a multiprecision integer. * @param p bytes @@ -384,15 +397,24 @@ unsigned int pgpCRC(const uint8_t *octets, size_t len) return crc & 0xffffff; } +static int pgpVersion(const uint8_t *h, size_t hlen, uint8_t *version) +{ + if (hlen < 1) + return -1; + + *version = h[0]; + return 0; +} + static int pgpPrtSubType(const uint8_t *h, size_t hlen, pgpSigType sigtype, pgpDigParams _digp) { const uint8_t *p = h; - size_t plen, i; + size_t plen = 0, i; while (hlen > 0) { i = pgpLen(p, hlen, &plen); - if (i == 0 || i + plen > hlen) + if (i == 0 || plen < 1 || i + plen > hlen) break; p += i; @@ -423,23 +445,22 @@ static int pgpPrtSubType(const uint8_t *h, size_t hlen, pgpSigType sigtype, if (!(_digp->saved & PGPDIG_SAVED_TIME) && (sigtype == PGPSIGTYPE_POSITIVE_CERT || sigtype == PGPSIGTYPE_BINARY || sigtype == PGPSIGTYPE_TEXT || sigtype == PGPSIGTYPE_STANDALONE)) { + if (plen-1 != sizeof(_digp->time)) + break; _digp->saved |= PGPDIG_SAVED_TIME; - memcpy(_digp->time, p+1, sizeof(_digp->time)); + _digp->time = pgpGrab(p+1, sizeof(_digp->time)); } case PGPSUBTYPE_SIG_EXPIRE_TIME: case PGPSUBTYPE_KEY_EXPIRE_TIME: - if ((plen - 1) == 4) { - time_t t = pgpGrab(p+1, plen-1); - if (_print) - fprintf(stderr, " %-24.24s(0x%08x)", ctime(&t), (unsigned)t); - } else - pgpPrtHex("", p+1, plen-1); + pgpPrtTime(" ", p+1, plen-1); break; case PGPSUBTYPE_ISSUER_KEYID: /* issuer key ID */ if (!(_digp->saved & PGPDIG_SAVED_ID) && (sigtype == PGPSIGTYPE_POSITIVE_CERT || sigtype == PGPSIGTYPE_BINARY || sigtype == PGPSIGTYPE_TEXT || sigtype == PGPSIGTYPE_STANDALONE)) { + if (plen-1 != sizeof(_digp->signid)) + break; _digp->saved |= PGPDIG_SAVED_ID; memcpy(_digp->signid, p+1, sizeof(_digp->signid)); } @@ -499,11 +520,15 @@ static int pgpPrtSigParams(pgpTag tag, uint8_t pubkey_algo, uint8_t sigtype, int i; pgpDigAlg sigalg = pgpSignatureNew(pubkey_algo); - for (i = 0; p < pend && i < sigalg->mpis; i++, p += pgpMpiLen(p)) { + for (i = 0; i < sigalg->mpis && p + 2 <= pend; i++) { + int mpil = pgpMpiLen(p); + if (p + mpil > pend) + break; if (sigtype == PGPSIGTYPE_BINARY || sigtype == PGPSIGTYPE_TEXT) { - if (sigalg->setmpi(sigalg, i, p, pend)) + if (sigalg->setmpi(sigalg, i, p)) break; } + p += mpil; } /* Does the size and number of MPI's match our expectations? */ @@ -519,18 +544,33 @@ static int pgpPrtSigParams(pgpTag tag, uint8_t pubkey_algo, uint8_t sigtype, return rc; } +static int pgpGet(const uint8_t *s, size_t nbytes, const uint8_t *send, + unsigned int *valp) +{ + int rc = -1; + + if (s + nbytes <= send) { + *valp = pgpGrab(s, nbytes); + rc = 0; + } + + return rc; +} + static int pgpPrtSig(pgpTag tag, const uint8_t *h, size_t hlen, pgpDigParams _digp) { - uint8_t version = h[0]; + uint8_t version = 0; uint8_t * p; - size_t plen; - int rc; + unsigned int plen; + int rc = 1; + + if (pgpVersion(h, hlen, &version)) + return rc; switch (version) { case 3: { pgpPktSigV3 v = (pgpPktSigV3)h; - time_t t; if (hlen <= sizeof(*v) || v->hashlen != 5) return 1; @@ -540,9 +580,7 @@ static int pgpPrtSig(pgpTag tag, const uint8_t *h, size_t hlen, pgpPrtVal(" ", pgpHashTbl, v->hash_algo); pgpPrtVal(" ", pgpSigTypeTbl, v->sigtype); pgpPrtNL(); - t = pgpGrab(v->time, sizeof(v->time)); - if (_print) - fprintf(stderr, " %-24.24s(0x%08x)", ctime(&t), (unsigned)t); + pgpPrtTime(" ", v->time, sizeof(v->time)); pgpPrtNL(); pgpPrtHex(" signer keyid", v->signid, sizeof(v->signid)); plen = pgpGrab(v->signhash16, sizeof(v->signhash16)); @@ -554,7 +592,7 @@ static int pgpPrtSig(pgpTag tag, const uint8_t *h, size_t hlen, _digp->hashlen = v->hashlen; _digp->sigtype = v->sigtype; _digp->hash = memcpy(xmalloc(v->hashlen), &v->sigtype, v->hashlen); - memcpy(_digp->time, v->time, sizeof(_digp->time)); + _digp->time = pgpGrab(v->time, sizeof(v->time)); memcpy(_digp->signid, v->signid, sizeof(_digp->signid)); _digp->pubkey_algo = v->pubkey_algo; _digp->hash_algo = v->hash_algo; @@ -577,7 +615,8 @@ static int pgpPrtSig(pgpTag tag, const uint8_t *h, size_t hlen, pgpPrtNL(); p = &v->hashlen[0]; - plen = pgpGrab(v->hashlen, sizeof(v->hashlen)); + if (pgpGet(v->hashlen, sizeof(v->hashlen), h + hlen, &plen)) + return 1; p += sizeof(v->hashlen); if ((p + plen) > (h + hlen)) @@ -591,7 +630,8 @@ static int pgpPrtSig(pgpTag tag, const uint8_t *h, size_t hlen, return 1; p += plen; - plen = pgpGrab(p,2); + if (pgpGet(p, 2, h + hlen, &plen)) + return 1; p += 2; if ((p + plen) > (h + hlen)) @@ -601,7 +641,8 @@ static int pgpPrtSig(pgpTag tag, const uint8_t *h, size_t hlen, return 1; p += plen; - plen = pgpGrab(p,2); + if (pgpGet(p, 2, h + hlen, &plen)) + return 1; pgpPrtHex(" signhash16", p, 2); pgpPrtNL(); @@ -620,6 +661,7 @@ static int pgpPrtSig(pgpTag tag, const uint8_t *h, size_t hlen, rc = pgpPrtSigParams(tag, v->pubkey_algo, v->sigtype, p, h, hlen, _digp); } break; default: + rpmlog(RPMLOG_WARNING, _("Unsupported version of key: V%d\n"), version); rc = 1; break; } @@ -650,9 +692,13 @@ static int pgpPrtPubkeyParams(uint8_t pubkey_algo, int i; pgpDigAlg keyalg = pgpPubkeyNew(pubkey_algo); - for (i = 0; p < pend && i < keyalg->mpis; i++, p += pgpMpiLen(p)) { - if (keyalg->setmpi(keyalg, i, p, pend)) + for (i = 0; i < keyalg->mpis && p + 2 <= pend; i++) { + int mpil = pgpMpiLen(p); + if (p + mpil > pend) + break; + if (keyalg->setmpi(keyalg, i, p)) break; + p += mpil; } /* Does the size and number of MPI's match our expectations? */ @@ -660,7 +706,9 @@ static int pgpPrtPubkeyParams(uint8_t pubkey_algo, rc = 0; /* We can't handle more than one key at a time */ - if (rc == 0 && keyp->alg == NULL && keyp->tag == PGPTAG_PUBLIC_KEY) + if (rc == 0 && keyp->alg == NULL && (keyp->tag == PGPTAG_PUBLIC_KEY || + keyp->tag == PGPTAG_PUBLIC_SUBKEY)) + keyp->alg = keyalg; else pgpDigAlgFree(keyalg); @@ -671,11 +719,13 @@ static int pgpPrtPubkeyParams(uint8_t pubkey_algo, static int pgpPrtKey(pgpTag tag, const uint8_t *h, size_t hlen, pgpDigParams _digp) { - uint8_t version = *h; + uint8_t version = 0; const uint8_t * p = NULL; - time_t t; int rc = 1; + if (pgpVersion(h, hlen, &version)) + return rc; + /* We only permit V4 keys, V3 keys are long long since deprecated */ switch (version) { case 4: @@ -684,14 +734,13 @@ static int pgpPrtKey(pgpTag tag, const uint8_t *h, size_t hlen, if (hlen > sizeof(*v)) { pgpPrtVal("V4 ", pgpTagTbl, tag); pgpPrtVal(" ", pgpPubkeyTbl, v->pubkey_algo); - t = pgpGrab(v->time, sizeof(v->time)); - if (_print) - fprintf(stderr, " %-24.24s(0x%08x)", ctime(&t), (unsigned)t); + pgpPrtTime(" ", v->time, sizeof(v->time)); pgpPrtNL(); - if (_digp->tag == tag) { + /* If _digp->hash is not NULL then signature is already loaded */ + if (_digp->hash == NULL) { _digp->version = v->version; - memcpy(_digp->time, v->time, sizeof(_digp->time)); + _digp->time = pgpGrab(v->time, sizeof(v->time)); _digp->pubkey_algo = v->pubkey_algo; } @@ -699,6 +748,8 @@ static int pgpPrtKey(pgpTag tag, const uint8_t *h, size_t hlen, rc = pgpPrtPubkeyParams(v->pubkey_algo, p, h, hlen, _digp); } } break; + default: + rpmlog(RPMLOG_WARNING, _("Unsupported version of key: V%d\n"), h[0]); } return rc; } @@ -716,14 +767,19 @@ static int pgpPrtUserID(pgpTag tag, const uint8_t *h, size_t hlen, return 0; } -static int getFingerprint(const uint8_t *h, size_t hlen, pgpKeyID_t keyid) +int pgpPubkeyFingerprint(const uint8_t *h, size_t hlen, + uint8_t **fp, size_t *fplen) { int rc = -1; /* assume failure */ const uint8_t *se; const uint8_t *pend = h + hlen; + uint8_t version = 0; + + if (pgpVersion(h, hlen, &version)) + return rc; /* We only permit V4 keys, V3 keys are long long since deprecated */ - switch (h[0]) { + switch (version) { case 4: { pgpPktKeyV4 v = (pgpPktKeyV4) (h); int mpis = -1; @@ -747,8 +803,8 @@ static int getFingerprint(const uint8_t *h, size_t hlen, pgpKeyID_t keyid) /* Does the size and number of MPI's match our expectations? */ if (se == pend && mpis == 0) { DIGEST_CTX ctx = rpmDigestInit(PGPHASHALGO_SHA1, RPMDIGEST_NONE); - uint8_t * d = NULL; - size_t dlen; + uint8_t *d = NULL; + size_t dlen = 0; int i = se - h; uint8_t in[3] = { 0x99, (i >> 8), i }; @@ -756,42 +812,42 @@ static int getFingerprint(const uint8_t *h, size_t hlen, pgpKeyID_t keyid) (void) rpmDigestUpdate(ctx, h, i); (void) rpmDigestFinal(ctx, (void **)&d, &dlen, 0); - if (d) { - memcpy(keyid, (d + (dlen-8)), 8); - free(d); + if (dlen == 20) { rc = 0; + *fp = d; + *fplen = dlen; + } else { + free(d); } } } break; + default: + rpmlog(RPMLOG_WARNING, _("Unsupported version of key: V%d\n"), version); + } + return rc; +} + +static int getKeyID(const uint8_t *h, size_t hlen, pgpKeyID_t keyid) +{ + uint8_t *fp = NULL; + size_t fplen = 0; + int rc = pgpPubkeyFingerprint(h, hlen, &fp, &fplen); + if (fp && fplen > 8) { + memcpy(keyid, (fp + (fplen-8)), 8); + free(fp); } return rc; } -int pgpPubkeyFingerprint(const uint8_t * pkt, size_t pktlen, pgpKeyID_t keyid) +int pgpPubkeyKeyID(const uint8_t * pkt, size_t pktlen, pgpKeyID_t keyid) { struct pgpPkt p; if (decodePkt(pkt, pktlen, &p)) return -1; - return getFingerprint(p.body, p.blen, keyid); -} - -int pgpExtractPubkeyFingerprint(const char * b64pkt, pgpKeyID_t keyid) -{ - uint8_t * pkt; - size_t pktlen; - int rc = -1; /* assume failure */ - - if (rpmBase64Decode(b64pkt, (void **)&pkt, &pktlen) == 0) { - if (pgpPubkeyFingerprint(pkt, pktlen, keyid) == 0) { - /* if there ever was a bizarre return code for success... */ - rc = 8; - } - free(pkt); - } - return rc; + return getKeyID(p.body, p.blen, keyid); } static int pgpPrtPkt(struct pgpPkt *p, pgpDigParams _digp) @@ -803,8 +859,8 @@ static int pgpPrtPkt(struct pgpPkt *p, pgpDigParams _digp) rc = pgpPrtSig(p->tag, p->body, p->blen, _digp); break; case PGPTAG_PUBLIC_KEY: - /* Get the public key fingerprint. */ - if (!getFingerprint(p->body, p->blen, _digp->signid)) + /* Get the public key Key ID. */ + if (!getKeyID(p->body, p->blen, _digp->signid)) _digp->saved |= PGPDIG_SAVED_ID; else memset(_digp->signid, 0, sizeof(_digp->signid)); @@ -813,12 +869,14 @@ static int pgpPrtPkt(struct pgpPkt *p, pgpDigParams _digp) case PGPTAG_USER_ID: rc = pgpPrtUserID(p->tag, p->body, p->blen, _digp); break; + case PGPTAG_RESERVED: + rc = -1; + break; case PGPTAG_COMMENT: case PGPTAG_COMMENT_OLD: case PGPTAG_PUBLIC_SUBKEY: case PGPTAG_SECRET_KEY: case PGPTAG_SECRET_SUBKEY: - case PGPTAG_RESERVED: case PGPTAG_PUBLIC_SESSION_KEY: case PGPTAG_SYMMETRIC_SESSION_KEY: case PGPTAG_COMPRESSED_DATA: @@ -903,6 +961,8 @@ int pgpDigParamsCmp(pgpDigParams p1, pgpDigParams p2) int rc = 1; /* assume different, eg if either is NULL */ if (p1 && p2) { /* XXX Should we compare something else too? */ + if (p1->tag != p2->tag) + goto exit; if (p1->hash_algo != p2->hash_algo) goto exit; if (p1->pubkey_algo != p2->pubkey_algo) @@ -913,6 +973,8 @@ int pgpDigParamsCmp(pgpDigParams p1, pgpDigParams p2) goto exit; if (memcmp(p1->signid, p2->signid, sizeof(p1->signid)) != 0) goto exit; + if (p1->userid && p2->userid && strcmp(p1->userid, p2->userid) != 0) + goto exit; /* Parameters match ... at least for our purposes */ rc = 0; @@ -937,36 +999,128 @@ unsigned int pgpDigParamsAlgo(pgpDigParams digp, unsigned int algotype) return algo; } +static pgpDigParams pgpDigParamsNew(uint8_t tag) +{ + pgpDigParams digp = xcalloc(1, sizeof(*digp)); + digp->tag = tag; + return digp; +} + +static int hashKey(DIGEST_CTX hash, const struct pgpPkt *pkt, int exptag) +{ + int rc = -1; + if (pkt->tag == exptag) { + uint8_t head[] = { + 0x99, + (pkt->blen >> 8), + (pkt->blen ), + }; + + rpmDigestUpdate(hash, head, 3); + rpmDigestUpdate(hash, pkt->body, pkt->blen); + rc = 0; + } + return rc; +} + +static int pgpVerifySelf(pgpDigParams key, pgpDigParams selfsig, + const struct pgpPkt *all, int i) +{ + int rc = -1; + DIGEST_CTX hash = NULL; + + switch (selfsig->sigtype) { + case PGPSIGTYPE_SUBKEY_BINDING: + hash = rpmDigestInit(selfsig->hash_algo, 0); + if (hash) { + rc = hashKey(hash, &all[0], PGPTAG_PUBLIC_KEY); + if (!rc) + rc = hashKey(hash, &all[i-1], PGPTAG_PUBLIC_SUBKEY); + } + break; + default: + /* ignore types we can't handle */ + rc = 0; + break; + } + + if (hash && rc == 0) + rc = pgpVerifySignature(key, selfsig, hash); + + rpmDigestFinal(hash, NULL, NULL, 0); + + return rc; +} + int pgpPrtParams(const uint8_t * pkts, size_t pktlen, unsigned int pkttype, pgpDigParams * ret) { const uint8_t *p = pkts; const uint8_t *pend = pkts + pktlen; pgpDigParams digp = NULL; - struct pgpPkt pkt; + pgpDigParams selfsig = NULL; + int i = 0; + int alloced = 16; /* plenty for normal cases */ + struct pgpPkt *all = xmalloc(alloced * sizeof(*all)); int rc = -1; /* assume failure */ + int expect = 0; + int prevtag = 0; while (p < pend) { - if (decodePkt(p, (pend - p), &pkt)) + struct pgpPkt *pkt = &all[i]; + if (decodePkt(p, (pend - p), pkt)) break; if (digp == NULL) { - if (pkttype && pkt.tag != pkttype) { + if (pkttype && pkt->tag != pkttype) { break; } else { - digp = xcalloc(1, sizeof(*digp)); - digp->tag = pkt.tag; + digp = pgpDigParamsNew(pkt->tag); } } - if (pgpPrtPkt(&pkt, digp)) + if (expect) { + if (pkt->tag != expect) + break; + selfsig = pgpDigParamsNew(pkt->tag); + } + + if (pgpPrtPkt(pkt, selfsig ? selfsig : digp)) break; - p += (pkt.body - pkt.head) + pkt.blen; + if (selfsig) { + /* subkeys must be followed by binding signature */ + if (prevtag == PGPTAG_PUBLIC_SUBKEY) { + if (selfsig->sigtype != PGPSIGTYPE_SUBKEY_BINDING) + break; + } + + int xx = pgpVerifySelf(digp, selfsig, all, i); + + selfsig = pgpDigParamsFree(selfsig); + if (xx) + break; + expect = 0; + } + + if (pkt->tag == PGPTAG_PUBLIC_SUBKEY) + expect = PGPTAG_SIGNATURE; + prevtag = pkt->tag; + + i++; + p += (pkt->body - pkt->head) + pkt->blen; + if (pkttype == PGPTAG_SIGNATURE) + break; + + if (alloced <= i) { + alloced *= 2; + all = xrealloc(all, alloced * sizeof(*all)); + } } - rc = (digp && (p == pend)) ? 0 : -1; + rc = (digp && (p == pend) && expect == 0) ? 0 : -1; + free(all); if (ret && rc == 0) { *ret = digp; } else { @@ -975,6 +1129,63 @@ int pgpPrtParams(const uint8_t * pkts, size_t pktlen, unsigned int pkttype, return rc; } +int pgpPrtParamsSubkeys(const uint8_t *pkts, size_t pktlen, + pgpDigParams mainkey, pgpDigParams **subkeys, + int *subkeysCount) +{ + const uint8_t *p = pkts; + const uint8_t *pend = pkts + pktlen; + pgpDigParams *digps = NULL; + int count = 0; + int alloced = 10; + struct pgpPkt pkt; + int rc, i; + + digps = xmalloc(alloced * sizeof(*digps)); + + while (p < pend) { + if (decodePkt(p, (pend - p), &pkt)) + break; + + p += (pkt.body - pkt.head) + pkt.blen; + + if (pkt.tag == PGPTAG_PUBLIC_SUBKEY) { + if (count == alloced) { + alloced <<= 1; + digps = xrealloc(digps, alloced * sizeof(*digps)); + } + + digps[count] = xcalloc(1, sizeof(**digps)); + digps[count]->tag = PGPTAG_PUBLIC_SUBKEY; + /* Copy UID from main key to subkey */ + digps[count]->userid = xstrdup(mainkey->userid); + + if (getKeyID(pkt.body, pkt.blen, digps[count]->signid)) { + pgpDigParamsFree(digps[count]); + continue; + } + + if (pgpPrtKey(pkt.tag, pkt.body, pkt.blen, digps[count])) { + pgpDigParamsFree(digps[count]); + continue; + } + count++; + } + } + rc = (p == pend) ? 0 : -1; + + if (rc == 0) { + *subkeys = xrealloc(digps, count * sizeof(*digps)); + *subkeysCount = count; + } else { + for (i = 0; i < count; i++) + pgpDigParamsFree(digps[i]); + free(digps); + } + + return rc; +} + int pgpPrtPkts(const uint8_t * pkts, size_t pktlen, pgpDig dig, int printing) { int rc; @@ -1230,6 +1441,29 @@ pgpArmor pgpParsePkts(const char *armor, uint8_t ** pkt, size_t * pktlen) return ec; } +int pgpPubKeyCertLen(const uint8_t *pkts, size_t pktslen, size_t *certlen) +{ + const uint8_t *p = pkts; + const uint8_t *pend = pkts + pktslen; + struct pgpPkt pkt; + + while (p < pend) { + if (decodePkt(p, (pend - p), &pkt)) + return -1; + + if (pkt.tag == PGPTAG_PUBLIC_KEY && pkts != p) { + *certlen = p - pkts; + return 0; + } + + p += (pkt.body - pkt.head) + pkt.blen; + } + + *certlen = pktslen; + + return 0; +} + char * pgpArmorWrap(int atype, const unsigned char * s, size_t ns) { char *buf = NULL, *val = NULL; diff --git a/rpmio/rpmpgp.h b/rpmio/rpmpgp.h index d45465636..d37a2e92c 100644 --- a/rpmio/rpmpgp.h +++ b/rpmio/rpmpgp.h @@ -244,14 +244,16 @@ typedef enum pgpCompressAlgo_e { 4 - Reserved for double-width SHA (experimental) 5 - MD2 "MD2" 6 - Reserved for TIGER/192 "TIGER192" - 7 - Reserved for HAVAL (5 pass, 160-bit) - "HAVAL-5-160" + 7 - Reserved for HAVAL (5 pass, 160-bit) "HAVAL-5-160" + 8 - SHA-256 "SHA256" + 9 - SHA-384 "SHA384" + 10 - SHA-512 "SHA512" + 11 - SHA-224 "SHA224" 100 to 110 - Private/Experimental algorithm. \endverbatim * * Implementations MUST implement SHA-1. Implementations SHOULD * implement MD5. - * @todo Add SHA256. */ typedef enum pgpHashAlgo_e { PGPHASHALGO_MD5 = 1, /*!< MD5 */ @@ -970,23 +972,23 @@ char * pgpHexStr(const uint8_t *p, size_t plen); /** \ingroup rpmpgp * Calculate OpenPGP public key fingerprint. - * @todo V3 non-RSA public keys not implemented. * @param pkt OpenPGP packet (i.e. PGPTAG_PUBLIC_KEY) * @param pktlen OpenPGP packet length (no. of bytes) - * @retval keyid public key fingerprint - * @return 0 on sucess, else -1 + * @retval fp public key fingerprint + * @retval fplen public key fingerprint length + * @return 0 on success, else -1 */ int pgpPubkeyFingerprint(const uint8_t * pkt, size_t pktlen, - pgpKeyID_t keyid); + uint8_t **fp, size_t *fplen); /** \ingroup rpmpgp -* Extract OpenPGP public key fingerprint from base64 encoded packet. -* @todo V3 non-RSA public keys not implemented. -* @param b64pkt base64 encoded openpgp packet -* @retval keyid public key fingerprint -* @return 8 (no. of bytes) on success, < 0 on error -*/ -int pgpExtractPubkeyFingerprint(const char * b64pkt, pgpKeyID_t keyid); + * Calculate OpenPGP public key Key ID + * @param pkt OpenPGP packet (i.e. PGPTAG_PUBLIC_KEY) + * @param pktlen OpenPGP packet length (no. of bytes) + * @retval keyid public key Key ID + * @return 0 on success, else -1 + */ +int pgpPubkeyKeyID(const uint8_t * pkt, size_t pktlen, pgpKeyID_t keyid); /** \ingroup rpmpgp * Parse a OpenPGP packet(s). @@ -1000,6 +1002,18 @@ int pgpPrtParams(const uint8_t *pkts, size_t pktlen, unsigned int pkttype, pgpDigParams * ret); /** \ingroup rpmpgp + * Parse subkey parameters from OpenPGP packet(s). + * @param pkts OpenPGP packet(s) + * @param pktlen OpenPGP packet(s) length (no. of bytes) + * @param mainkey parameters of main key + * @param subkeys array of subkey parameters (alloced) + * @param subkeysCount count of subkeys + * @return -1 on error, 0 on success + */ +int pgpPrtParamsSubkeys(const uint8_t *pkts, size_t pktlen, + pgpDigParams mainkey, pgpDigParams **subkeys, + int *subkeysCount); +/** \ingroup rpmpgp * Print/parse a OpenPGP packet(s). * @param pkts OpenPGP packet(s) * @param pktlen OpenPGP packet(s) length (no. of bytes) @@ -1028,6 +1042,20 @@ pgpArmor pgpReadPkts(const char * fn, uint8_t ** pkt, size_t * pktlen); pgpArmor pgpParsePkts(const char *armor, uint8_t ** pkt, size_t * pktlen); /** \ingroup rpmpgp + * Return a length of the first public key certificate in a buffer given + * by pkts that contains one or more certificates. A public key certificate + * consits of packets like Public key packet, User ID packet and so on. + * In a buffer every certificate starts with Public key packet and it ends + * with the start of the next certificate or with the end of the buffer. + * + * @param pkts pointer to a buffer with certificates + * @param pktslen length of the buffer with certificates + * @param certlen length of the first certificate in the buffer + * @return 0 on success + */ +int pgpPubKeyCertLen(const uint8_t *pkts, size_t pktslen, size_t *certlen); + +/** \ingroup rpmpgp * Wrap a OpenPGP packets in ascii armor for transport. * @param atype type of armor * @param s binary pkt data @@ -1187,7 +1215,8 @@ rpmDigestBundle rpmDigestBundleNew(void); rpmDigestBundle rpmDigestBundleFree(rpmDigestBundle bundle); /** \ingroup rpmpgp - * Add a new type of digest to a bundle. + * Add a new type of digest to a bundle. Same as calling + * rpmDigestBundleAddID() with algo == id value. * @param bundle digest bundle * @param algo type of digest * @param flags bit(s) to control digest operation @@ -1197,6 +1226,17 @@ int rpmDigestBundleAdd(rpmDigestBundle bundle, int algo, rpmDigestFlags flags); /** \ingroup rpmpgp + * Add a new type of digest to a bundle. + * @param bundle digest bundle + * @param algo type of digest + * @param id id of digest (arbitrary, must be > 0) + * @param flags bit(s) to control digest operation + * @return 0 on success + */ +int rpmDigestBundleAddID(rpmDigestBundle bundle, int algo, int id, + rpmDigestFlags flags); + +/** \ingroup rpmpgp * Update contexts within bundle with next plain text buffer. * @param bundle digest bundle * @param data next data buffer @@ -1209,22 +1249,22 @@ int rpmDigestBundleUpdate(rpmDigestBundle bundle, const void *data, size_t len); * Return digest from a bundle and destroy context, see rpmDigestFinal(). * * @param bundle digest bundle - * @param algo type of digest to return + * @param id id of digest to return * @retval datap address of returned digest * @retval lenp address of digest length * @param asAscii return digest as ascii string? * @return 0 on success */ -int rpmDigestBundleFinal(rpmDigestBundle bundle, - int algo, void ** datap, size_t * lenp, int asAscii); +int rpmDigestBundleFinal(rpmDigestBundle bundle, int id, + void ** datap, size_t * lenp, int asAscii); /** \ingroup rpmpgp * Duplicate a digest context from a bundle. * @param bundle digest bundle - * @param algo type of digest to dup + * @param id id of digest to dup * @return duplicated digest context */ -DIGEST_CTX rpmDigestBundleDupCtx(rpmDigestBundle bundle, int algo); +DIGEST_CTX rpmDigestBundleDupCtx(rpmDigestBundle bundle, int id); #ifdef __cplusplus } diff --git a/rpmio/rpmsq.c b/rpmio/rpmsq.c index bfd4db004..1bbdd0a6d 100644 --- a/rpmio/rpmsq.c +++ b/rpmio/rpmsq.c @@ -8,107 +8,221 @@ #include <sys/signal.h> #include <errno.h> #include <stdio.h> - -#define ADD_REF(__tbl) (__tbl)->active++ -#define SUB_REF(__tbl) --(__tbl)->active +#include <stdlib.h> +#include <string.h> #include <rpm/rpmsq.h> +#include <rpm/rpmlog.h> #include "debug.h" -static sigset_t rpmsqCaught; +static __thread int disableInterruptSafety; +static __thread sigset_t rpmsqCaught; +static __thread sigset_t rpmsqActive; typedef struct rpmsig_s * rpmsig; +static void rpmsqIgn(int signum, siginfo_t *info, void *context) +{ +} + +static void rpmsqTerm(int signum, siginfo_t *info, void *context) +{ + if (info->si_pid == 0) { + rpmlog(RPMLOG_DEBUG, + "exiting on signal %d (killed by death, eh?)\n", signum); + } else { + int lvl = (signum == SIGPIPE) ? RPMLOG_DEBUG : RPMLOG_WARNING; + rpmlog(lvl, + _("exiting on signal %d from pid %d\n"), signum, info->si_pid); + } + /* exit 128 + signum for compatibility with bash(1) */ + exit(128 + signum); +} + static struct rpmsig_s { int signum; + rpmsqAction_t defhandler; rpmsqAction_t handler; - int active; + siginfo_t siginfo; struct sigaction oact; } rpmsigTbl[] = { - { SIGINT, rpmsqAction }, -#define rpmsigTbl_sigint (&rpmsigTbl[0]) - { SIGQUIT, rpmsqAction }, -#define rpmsigTbl_sigquit (&rpmsigTbl[1]) - { SIGHUP, rpmsqAction }, -#define rpmsigTbl_sighup (&rpmsigTbl[3]) - { SIGTERM, rpmsqAction }, -#define rpmsigTbl_sigterm (&rpmsigTbl[4]) - { SIGPIPE, rpmsqAction }, -#define rpmsigTbl_sigpipe (&rpmsigTbl[5]) - { -1, NULL }, + { SIGINT, rpmsqTerm, NULL }, + { SIGQUIT, rpmsqTerm, NULL }, + { SIGHUP, rpmsqTerm, NULL }, + { SIGTERM, rpmsqTerm, NULL }, + { SIGPIPE, rpmsqTerm, NULL }, + { -1, NULL, NULL }, }; +static int rpmsigGet(int signum, struct rpmsig_s **sig) +{ + for (rpmsig tbl = rpmsigTbl; tbl->signum >= 0; tbl++) { + if (tbl->signum == signum) { + *sig = tbl; + return 1; + } + } + return 0; +} + int rpmsqIsCaught(int signum) { return sigismember(&rpmsqCaught, signum); } -#ifdef SA_SIGINFO -void rpmsqAction(int signum, siginfo_t * info, void * context) -#else -void rpmsqAction(int signum) -#endif +static void rpmsqHandler(int signum, siginfo_t * info, void * context) { int save = errno; - rpmsig tbl; - for (tbl = rpmsigTbl; tbl->signum >= 0; tbl++) { - if (tbl->signum != signum) - continue; - - (void) sigaddset(&rpmsqCaught, signum); - break; + if (sigismember(&rpmsqActive, signum)) { + if (!sigismember(&rpmsqCaught, signum)) { + rpmsig sig = NULL; + if (rpmsigGet(signum, &sig)) { + (void) sigaddset(&rpmsqCaught, signum); + memcpy(&sig->siginfo, info, sizeof(*info)); + } + } } + errno = save; } -int rpmsqEnable(int signum, rpmsqAction_t handler) +rpmsqAction_t rpmsqSetAction(int signum, rpmsqAction_t handler) { - int tblsignum = (signum >= 0 ? signum : -signum); - struct sigaction sa; - rpmsig tbl; - int ret = -1; + rpmsig sig = NULL; + rpmsqAction_t oh = RPMSQ_ERR; - for (tbl = rpmsigTbl; tbl->signum >= 0; tbl++) { - if (tblsignum != tbl->signum) - continue; + if (rpmsigGet(signum, &sig)) { + oh = sig->handler; + sig->handler = (handler == RPMSQ_IGN) ? rpmsqIgn : handler; + } + return oh; +} - if (signum >= 0) { /* Enable. */ - if (ADD_REF(tbl) <= 0) { - (void) sigdelset(&rpmsqCaught, tbl->signum); +int rpmsqActivate(int state) +{ + sigset_t newMask, oldMask; + + if (disableInterruptSafety) + return 0; + + (void) sigfillset(&newMask); + (void) pthread_sigmask(SIG_BLOCK, &newMask, &oldMask); + + if (state) { + struct sigaction sa; + for (rpmsig tbl = rpmsigTbl; tbl->signum >= 0; tbl++) { + sigdelset(&rpmsqCaught, tbl->signum); + memset(&tbl->siginfo, 0, sizeof(tbl->siginfo)); + + /* XXX Don't set a signal handler if already SIG_IGN */ + sigaction(tbl->signum, NULL, &tbl->oact); + if (tbl->oact.sa_handler == SIG_IGN) + continue; + + sigemptyset (&sa.sa_mask); + sa.sa_flags = SA_SIGINFO; + sa.sa_sigaction = rpmsqHandler; + if (sigaction(tbl->signum, &sa, &tbl->oact) == 0) + sigaddset(&rpmsqActive, tbl->signum); + } + } else { + for (rpmsig tbl = rpmsigTbl; tbl->signum >= 0; tbl++) { + if (!sigismember(&rpmsqActive, tbl->signum)) + continue; + if (sigaction(tbl->signum, &tbl->oact, NULL) == 0) { + sigdelset(&rpmsqActive, tbl->signum); + sigdelset(&rpmsqCaught, tbl->signum); + memset(&tbl->siginfo, 0, sizeof(tbl->siginfo)); + } + } + } + pthread_sigmask(SIG_SETMASK, &oldMask, NULL); + return 0; +} - /* XXX Don't set a signal handler if already SIG_IGN */ - (void) sigaction(tbl->signum, NULL, &tbl->oact); - if (tbl->oact.sa_handler == SIG_IGN) - continue; +int rpmsqPoll(void) +{ + sigset_t newMask, oldMask; + int n = 0; + + /* block all signals while processing the queue */ + (void) sigfillset(&newMask); + (void) pthread_sigmask(SIG_BLOCK, &newMask, &oldMask); + + for (rpmsig tbl = rpmsigTbl; tbl->signum >= 0; tbl++) { + /* honor blocked signals in polling too */ + if (sigismember(&oldMask, tbl->signum)) + continue; + if (sigismember(&rpmsqCaught, tbl->signum)) { + rpmsqAction_t handler = (tbl->handler != NULL) ? tbl->handler : + tbl->defhandler; + /* delete signal before running handler to prevent recursing */ + sigdelset(&rpmsqCaught, tbl->signum); + handler(tbl->signum, &tbl->siginfo, NULL); + memset(&tbl->siginfo, 0, sizeof(tbl->siginfo)); + n++; + } + } + pthread_sigmask(SIG_SETMASK, &oldMask, NULL); + return n; +} - (void) sigemptyset (&sa.sa_mask); -#ifdef SA_SIGINFO - sa.sa_flags = SA_SIGINFO; -#else - sa.sa_flags = 0; +int rpmsqBlock(int op) +{ + static __thread sigset_t oldMask; + static __thread int blocked = 0; + sigset_t newMask; + int ret = 0; + + if (op == SIG_BLOCK) { + blocked++; + if (blocked == 1) { + sigfillset(&newMask); + sigdelset(&newMask, SIGABRT); + sigdelset(&newMask, SIGBUS); + sigdelset(&newMask, SIGFPE); + sigdelset(&newMask, SIGILL); + sigdelset(&newMask, SIGSEGV); + sigdelset(&newMask, SIGTSTP); +#ifdef HWASAN_BUILD + sigdelset(&newMask, SIGTRAP); #endif - sa.sa_sigaction = (handler != NULL ? handler : tbl->handler); - if (sigaction(tbl->signum, &sa, &tbl->oact) < 0) { - SUB_REF(tbl); - break; - } - tbl->active = 1; /* XXX just in case */ - if (handler != NULL) - tbl->handler = handler; - } - } else { /* Disable. */ - if (SUB_REF(tbl) <= 0) { - if (sigaction(tbl->signum, &tbl->oact, NULL) < 0) - break; - tbl->active = 0; /* XXX just in case */ - tbl->handler = (handler != NULL ? handler : rpmsqAction); - } + ret = pthread_sigmask(SIG_BLOCK, &newMask, &oldMask); + } + } else if (op == SIG_UNBLOCK) { + blocked--; + if (blocked == 0) { + ret = pthread_sigmask(SIG_SETMASK, &oldMask, NULL); + rpmsqPoll(); + } else if (blocked < 0) { + blocked = 0; + ret = -1; } - ret = tbl->active; - break; } + return ret; } +/** \ingroup rpmio + * + * By default, librpm will trap various unix signals such as SIGINT and SIGTERM, + * in order to avoid process exit while locks are held or a transaction is being + * performed. However, there exist tools that operate on non-running roots (traditionally + * build systems such as mock), as well as deployment tools such as rpm-ostree. + * + * These tools are more robust against interruption - typically they + * will just throw away the partially constructed root. This function + * is designed for use by those tools, so an operator can happily + * press Control-C. + * + * It's recommended to call this once only at process startup if this + * behavior is desired (and to then avoid using librpm against "live" + * databases), because currently signal handlers will not be retroactively + * applied if a database is open. + */ +void rpmsqSetInterruptSafety(int on) +{ + disableInterruptSafety = !on; +} diff --git a/rpmio/rpmsq.h b/rpmio/rpmsq.h index 00a1a72cf..23206457c 100644 --- a/rpmio/rpmsq.h +++ b/rpmio/rpmsq.h @@ -4,6 +4,7 @@ /** \ingroup rpmio * \file rpmio/rpmsq.h * + * Signal Queue API */ #include <rpm/rpmsw.h> #include <signal.h> @@ -18,11 +19,14 @@ extern "C" { * @param info (siginfo_t) signal info * @param context signal context */ -#ifdef SA_SIGINFO typedef void (*rpmsqAction_t) (int signum, siginfo_t * info, void * context); -#else -typedef void (*rpmsqAction_t) (int signum); -#endif + +/** \ingroup rpmsq + * SIG_DFL, SIG_IGN and SIG_ERR counterparts + */ +#define RPMSQ_DFL ((rpmsqAction_t)0) +#define RPMSQ_IGN ((rpmsqAction_t)1) +#define RPMSQ_ERR ((rpmsqAction_t)-1) /** \ingroup rpmsq * Test if given signal has been caught (while signals blocked). @@ -33,24 +37,36 @@ typedef void (*rpmsqAction_t) (int signum); int rpmsqIsCaught(int signum); /** \ingroup rpmsq - * Default signal handler. + * Activate (or disable) the signal queue. + * @param state 1 to enable, 0 to disable + * @return 0 on success, negative on error + */ +int rpmsqActivate(int state); + +/** \ingroup rpmsq + * Set or delete a signal handler for a signal. * @param signum signal number - * @param info (siginfo_t) signal info - * @param context signal context + * @param handler signal handler or NULL to delete + * @return previous handler, RPMSQ_ERR on error */ -#ifdef SA_SIGINFO -void rpmsqAction(int signum, siginfo_t * info, void * context); -#else -void rpmsqAction(int signum); -#endif +rpmsqAction_t rpmsqSetAction(int signum, rpmsqAction_t handler); /** \ingroup rpmsq - * Enable or disable a signal handler. - * @param signum signal to enable (or disable if negative) - * @param handler sa_sigaction handler (or NULL to use rpmsqHandler()) - * @return no. of refs, -1 on error + * Block or unblock (almost) all signals. + * The operation is "reference counted" so the calls can be nested, + * and signals are only unblocked when the reference count falls to zero. + * @param op SIG_BLOCK/SIG_UNBLOCK + * @return 0 on success, -1 on error */ -int rpmsqEnable(int signum, rpmsqAction_t handler); +int rpmsqBlock(int op); + +/** \ingroup rpmsq + * Poll for caught signals, executing their handlers. + * @return no. active signals found + */ +int rpmsqPoll(void); + +void rpmsqSetInterruptSafety(int on); #ifdef __cplusplus } diff --git a/rpmio/rpmstrpool.c b/rpmio/rpmstrpool.c index 742afaa22..30a57eb10 100644 --- a/rpmio/rpmstrpool.c +++ b/rpmio/rpmstrpool.c @@ -27,7 +27,7 @@ struct poolHash_s { }; struct rpmstrPool_s { - char ** offs; /* pointers into data area */ + const char ** offs; /* pointers into data area */ rpmsid offs_size; /* largest offset index */; rpmsid offs_alloced; /* offsets allocation size */ @@ -35,6 +35,7 @@ struct rpmstrPool_s { size_t chunks_size; /* current chunk */ size_t chunks_allocated; /* allocated size of the chunks array */ size_t chunk_allocated; /* size of the current chunk */ + size_t chunk_used; /* usage of the current chunk */ poolHash hash; /* string -> sid hash table */ int frozen; /* are new id additions allowed? */ @@ -137,7 +138,7 @@ static void poolHashAddHEntry(rpmstrPool pool, const char * key, unsigned int ke { poolHash ht = pool->hash; - /* keep load factor inbetween 0.25 and 0.5 */ + /* keep load factor between 0.25 and 0.5 */ if (2*(ht->keyCount) > ht->numBuckets) { poolHashResize(pool, ht->numBuckets * 2); } @@ -219,7 +220,7 @@ static void rpmstrPoolRehash(rpmstrPool pool) pool->hash = poolHashFree(pool->hash); pool->hash = poolHashCreate(sizehint); - for (int i = 1; i < pool->offs_size; i++) + for (int i = 1; i <= pool->offs_size; i++) poolHashAddEntry(pool, rpmstrPoolStr(pool, i), i); } @@ -234,8 +235,8 @@ rpmstrPool rpmstrPoolCreate(void) pool->chunks = xcalloc(pool->chunks_allocated, sizeof(*pool->chunks)); pool->chunks_size = 1; pool->chunk_allocated = STRDATA_CHUNK; - pool->offs[1] = xcalloc(1, pool->chunk_allocated); - pool->chunks[pool->chunks_size] = pool->offs[1]; + pool->chunks[pool->chunks_size] = xcalloc(1, pool->chunk_allocated); + pool->offs[1] = pool->chunks[pool->chunks_size]; rpmstrPoolRehash(pool); pool->nrefs = 1; @@ -296,43 +297,40 @@ static rpmsid rpmstrPoolPut(rpmstrPool pool, const char *s, size_t slen, unsigne { char *t = NULL; size_t ssize = slen + 1; - size_t chunk_used; pool->offs_size += 1; - /* need one extra for end of string */ - /* and one extra to mark the end of the chunk */ - if (pool->offs_alloced <= pool->offs_size + 2) { + if (pool->offs_alloced <= pool->offs_size) { pool->offs_alloced += STROFFS_CHUNK; pool->offs = xrealloc(pool->offs, pool->offs_alloced * sizeof(*pool->offs)); } - chunk_used = pool->offs[pool->offs_size] - pool->chunks[pool->chunks_size]; - if (ssize + 1 > pool->chunk_allocated - chunk_used) { - /* check size of ->chunks */ + /* Do we need a new chunk to store the string? */ + if (ssize > pool->chunk_allocated - pool->chunk_used) { pool->chunks_size += 1; + /* Grow chunks array if needed */ if (pool->chunks_size >= pool->chunks_allocated) { pool->chunks_allocated += pool->chunks_allocated; pool->chunks = xrealloc(pool->chunks, pool->chunks_allocated * sizeof(*pool->chunks)); } - /* Check if string is bigger than chunks */ + /* Ensure the string fits in the new chunk we're about to allocate */ if (ssize > pool->chunk_allocated) { pool->chunk_allocated = 2 * ssize; } - /* Dummy entry for end of last string*/ - pool->offs_size += 1; - - pool->offs[pool->offs_size] = xcalloc(1, pool->chunk_allocated); - pool->chunks[pool->chunks_size] = pool->offs[pool->offs_size]; + pool->chunks[pool->chunks_size] = xcalloc(1, pool->chunk_allocated); + pool->chunk_used = 0; } - t = memcpy(pool->offs[pool->offs_size], s, slen); + /* Copy the string into current chunk, ensure termination */ + t = memcpy(pool->chunks[pool->chunks_size] + pool->chunk_used, s, slen); t[slen] = '\0'; - pool->offs[pool->offs_size+1] = t + ssize; + pool->chunk_used += ssize; + /* Actually add the string to the pool */ + pool->offs[pool->offs_size] = t; poolHashAddHEntry(pool, t, hash, pool->offs_size); return pool->offs_size; @@ -406,8 +404,8 @@ const char * rpmstrPoolStr(rpmstrPool pool, rpmsid sid) size_t rpmstrPoolStrlen(rpmstrPool pool, rpmsid sid) { size_t slen = 0; - if (pool && sid <= pool->offs_size) { - slen = pool->offs[sid+1] - pool->offs[sid] - 1; + if (pool && sid > 0 && sid <= pool->offs_size) { + slen = strlen(pool->offs[sid]); } return slen; } diff --git a/rpmio/rpmstrpool.h b/rpmio/rpmstrpool.h index 0db07bb40..11c946973 100644 --- a/rpmio/rpmstrpool.h +++ b/rpmio/rpmstrpool.h @@ -1,6 +1,13 @@ #ifndef _RPMSTRPOOL_H #define _RPMSTRPOOL_H +/** \ingroup rpmstrpool + * \file rpmio/rpmstrpool.h + * + * String pools manipulation helper functions + * + */ + #include <rpm/rpmtypes.h> #ifdef __cplusplus @@ -80,11 +87,11 @@ const char * rpmstrPoolStr(rpmstrPool pool, rpmsid sid); /** \ingroup rpmstrpool * Return length of a string by its pool id. The result is equal to - * calling strlen() on a string retrieved through rpmstrPoolStr() but - * runs in constant time regardless of the length of the string. + * calling strlen() on a string retrieved through rpmstrPoolStr(), but + * the pool might be able to optimize the calculation. * @param pool string pool * @param sid pool id of a string - * @return length of the string + * @return length of the string, 0 for invalid pool or id */ size_t rpmstrPoolStrlen(rpmstrPool pool, rpmsid sid); diff --git a/rpmio/rpmsw.h b/rpmio/rpmsw.h index 7c285e784..61d6fe317 100644 --- a/rpmio/rpmsw.h +++ b/rpmio/rpmsw.h @@ -3,6 +3,8 @@ /** \ingroup rpmio * \file rpmio/rpmsw.h + * + * Statistics API */ #include <unistd.h> diff --git a/rpmio/rpmurl.h b/rpmio/rpmurl.h index e820e95e7..588c95df1 100644 --- a/rpmio/rpmurl.h +++ b/rpmio/rpmurl.h @@ -3,6 +3,8 @@ /** \ingroup rpmio * \file rpmio/rpmurl.h + * + * A couple utils for URL Manipulation */ #ifdef __cplusplus diff --git a/rpmio/rpmutil.h b/rpmio/rpmutil.h index 68a21f980..9e8a25d0f 100644 --- a/rpmio/rpmutil.h +++ b/rpmio/rpmutil.h @@ -3,8 +3,9 @@ #include <unistd.h> -/* - * Miscellanous utility macros: +/** \file rpmio/rpmutil.h + * + * Miscellaneous utility macros: * - portability wrappers for various gcc extensions like __attribute__() * - ... * diff --git a/rpmio/stubs.c b/rpmio/stubs.c deleted file mode 100644 index 694306e47..000000000 --- a/rpmio/stubs.c +++ /dev/null @@ -1,18 +0,0 @@ -/** - * \file rpmio/stubs.c - */ - -/* XXX Portable shared libraries require rpmlib to contain these functions. */ - -#include "system.h" - -#if !defined(HAVE_STPCPY) -#include "misc/stpcpy.c" -#endif - -#if !defined(HAVE_STPNCPY) -#include "misc/stpncpy.c" -#endif - -#include "misc/fnmatch.h" -#include "misc/fnmatch.c" |