2474 lines
63 KiB
C
2474 lines
63 KiB
C
/** \ingroup rpmdep
|
|
* \file lib/rpmfi.c
|
|
* Routines to handle file info tag sets.
|
|
*/
|
|
|
|
#include "system.h"
|
|
|
|
#include <unordered_map>
|
|
#include <vector>
|
|
#include <memory>
|
|
|
|
#include <rpm/rpmlog.h>
|
|
#include <rpm/rpmts.h>
|
|
#include <rpm/rpmfileutil.h> /* XXX rpmDoDigest */
|
|
#include <rpm/rpmstring.h>
|
|
#include <rpm/rpmmacro.h> /* XXX rpmCleanPath */
|
|
#include <rpm/rpmds.h>
|
|
#include <rpm/rpmbase64.h>
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#include <inttypes.h>
|
|
|
|
#include "rpmfi_internal.h"
|
|
#include "rpmte_internal.h" /* relocations */
|
|
#include "cpio.h" /* XXX CPIO_FOO */
|
|
#include "fsm.h" /* rpmpsm stuff for now */
|
|
#include "rpmug.h"
|
|
#include "rpmio_internal.h" /* fdInit/FiniDigest */
|
|
|
|
#include "debug.h"
|
|
|
|
using hardlinks = std::vector<int>;
|
|
using nlinkHash = std::unordered_map<int,std::shared_ptr<hardlinks>>;
|
|
|
|
typedef int (*iterfunc)(rpmfi fi);
|
|
|
|
struct rpmfi_s {
|
|
int i; /*!< Current file index. */
|
|
int j; /*!< Current directory index. */
|
|
iterfunc next; /*!< Iterator function. */
|
|
char * fn; /*!< File name buffer. */
|
|
char * ofn; /*!< Original file name buffer. */
|
|
|
|
int intervalStart; /*!< Start of iterating interval. */
|
|
int intervalEnd; /*!< End of iterating interval. */
|
|
|
|
rpmfiChdirCb onChdir; /*!< Callback for directory changes */
|
|
void *onChdirData; /*!< Caller private callback data */
|
|
|
|
rpmfiles files; /*!< File info set */
|
|
rpmcpio_t archive; /*!< Archive with payload */
|
|
uint8_t * found; /*!< Bit field of files found in the archive */
|
|
int nrefs; /*!< Reference count */
|
|
};
|
|
|
|
struct rpmfn_s {
|
|
rpm_count_t dc; /*!< No. of directories. */
|
|
rpm_count_t fc; /*!< No. of files. */
|
|
|
|
rpmsid * bnid; /*!< Index to base name(s) (pool) */
|
|
rpmsid * dnid; /*!< Index to directory name(s) (pool) */
|
|
uint32_t * dil; /*!< Directory indice(s) (from header) */
|
|
};
|
|
|
|
typedef struct rpmfn_s * rpmfn;
|
|
|
|
/**
|
|
* A package filename set.
|
|
*/
|
|
struct rpmfiles_s {
|
|
Header h; /*!< Header for file info set (or NULL) */
|
|
rpmstrPool pool; /*!< String pool of this file info set */
|
|
|
|
struct rpmfn_s fndata; /*!< File name data */
|
|
struct rpmfn_s *ofndata; /*!< Original file name data */
|
|
|
|
rpmsid * flinks; /*!< Index to file link(s) (pool) */
|
|
|
|
rpm_flag_t * fflags; /*!< File flag(s) (from header) */
|
|
rpm_off_t * fsizes; /*!< File size(s) (from header) */
|
|
rpm_loff_t * lfsizes; /*!< File size(s) (from header) */
|
|
rpm_time_t * fmtimes; /*!< File modification time(s) (from header) */
|
|
rpm_mode_t * fmodes; /*!< File mode(s) (from header) */
|
|
rpm_rdev_t * frdevs; /*!< File rdev(s) (from header) */
|
|
rpm_ino_t * finodes; /*!< File inodes(s) (from header) */
|
|
|
|
rpmsid * fuser; /*!< Index to file owner(s) (misc pool) */
|
|
rpmsid * fgroup; /*!< Index to file group(s) (misc pool) */
|
|
rpmsid * flangs; /*!< Index to file lang(s) (misc pool) */
|
|
|
|
char * fstates; /*!< File state(s) (from header) */
|
|
|
|
rpm_color_t * fcolors; /*!< File color bits (header) */
|
|
char ** fcaps; /*!< File capability strings (header) */
|
|
|
|
char ** cdict; /*!< File class dictionary (header) */
|
|
rpm_count_t ncdict; /*!< No. of class entries. */
|
|
uint32_t * fcdictx; /*!< File class dictionary index (header) */
|
|
|
|
char ** mdict; /*!< File mime dictionary (header) */
|
|
rpm_count_t nmdict; /*!< No. of mime entries. */
|
|
uint32_t * fmdictx; /*!< File mime dictionary index (header) */
|
|
|
|
uint32_t * ddict; /*!< File depends dictionary (header) */
|
|
rpm_count_t nddict; /*!< No. of depends entries. */
|
|
uint32_t * fddictx; /*!< File depends dictionary start (header) */
|
|
uint32_t * fddictn; /*!< File depends dictionary count (header) */
|
|
rpm_flag_t * vflags; /*!< File verify flag(s) (from header) */
|
|
|
|
rpmfiFlags fiflags; /*!< file info set control flags */
|
|
|
|
struct fingerPrint * fps; /*!< File fingerprint(s). */
|
|
|
|
int digestalgo; /*!< File digest algorithm */
|
|
uint32_t *signatureoffs; /*!< File signature offsets */
|
|
int veritysiglength; /*!< Verity signature length */
|
|
uint16_t verityalgo; /*!< Verity algorithm */
|
|
unsigned char * digests; /*!< File digests in binary. */
|
|
unsigned char * signatures; /*!< File signatures in binary. */
|
|
unsigned char * veritysigs; /*!< Verity signatures in binary. */
|
|
|
|
nlinkHash *nlinks; /*!< Files connected by hardlinks */
|
|
rpm_loff_t * replacedSizes; /*!< (TR_ADDED) */
|
|
int magic;
|
|
int nrefs; /*!< Reference count. */
|
|
};
|
|
|
|
static int indexSane(rpmtd xd, rpmtd yd, rpmtd zd);
|
|
static int cmpPoolFn(rpmstrPool pool, rpmfn files, int ix, const char * fn);
|
|
|
|
static rpmfiles rpmfilesUnlink(rpmfiles fi)
|
|
{
|
|
if (fi)
|
|
fi->nrefs--;
|
|
return NULL;
|
|
}
|
|
|
|
rpmfiles rpmfilesLink(rpmfiles fi)
|
|
{
|
|
if (fi)
|
|
fi->nrefs++;
|
|
return fi;
|
|
}
|
|
|
|
static rpmfi rpmfiUnlink(rpmfi fi)
|
|
{
|
|
if (fi)
|
|
fi->nrefs--;
|
|
return NULL;
|
|
}
|
|
|
|
rpmfi rpmfiLink(rpmfi fi)
|
|
{
|
|
if (fi)
|
|
fi->nrefs++;
|
|
return fi;
|
|
}
|
|
|
|
/*
|
|
* Collect and validate file path data from header.
|
|
* Return the number of files found (could be none) or -1 on error.
|
|
*/
|
|
static int rpmfnInit(rpmfn fndata, rpmTagVal bntag, Header h, rpmstrPool pool)
|
|
{
|
|
struct rpmtd_s bn, dn, dx;
|
|
rpmTagVal dntag, ditag;
|
|
int rc = 0;
|
|
|
|
if (bntag == RPMTAG_BASENAMES) {
|
|
dntag = RPMTAG_DIRNAMES;
|
|
ditag = RPMTAG_DIRINDEXES;
|
|
} else if (bntag == RPMTAG_ORIGBASENAMES) {
|
|
dntag = RPMTAG_ORIGDIRNAMES;
|
|
ditag = RPMTAG_ORIGDIRINDEXES;
|
|
} else {
|
|
return -1;
|
|
}
|
|
|
|
/* Grab and validate file triplet data (if there is any) */
|
|
if (headerGet(h, bntag, &bn, HEADERGET_MINMEM)) {
|
|
headerGet(h, dntag, &dn, HEADERGET_MINMEM);
|
|
headerGet(h, ditag, &dx, HEADERGET_ALLOC);
|
|
|
|
if (indexSane(&bn, &dn, &dx)) {
|
|
/* Init the file triplet data */
|
|
fndata->fc = rpmtdCount(&bn);
|
|
fndata->dc = rpmtdCount(&dn);
|
|
fndata->bnid = rpmtdToPool(&bn, pool);
|
|
fndata->dnid = rpmtdToPool(&dn, pool);
|
|
/* Steal index data from the td (pooh...) */
|
|
fndata->dil = (uint32_t *)dx.data;
|
|
dx.data = NULL;
|
|
rc = fndata->fc;
|
|
} else {
|
|
memset(fndata, 0, sizeof(*fndata));
|
|
rc = -1;
|
|
}
|
|
rpmtdFreeData(&bn);
|
|
rpmtdFreeData(&dn);
|
|
rpmtdFreeData(&dx);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static void rpmfnClear(rpmfn fndata)
|
|
{
|
|
if (fndata) {
|
|
free(fndata->bnid);
|
|
free(fndata->dnid);
|
|
free(fndata->dil);
|
|
memset(fndata, 0, sizeof(*fndata));
|
|
}
|
|
}
|
|
|
|
static rpm_count_t rpmfnFC(rpmfn fndata)
|
|
{
|
|
return (fndata != NULL) ? fndata->fc :0;
|
|
}
|
|
|
|
static rpm_count_t rpmfnDC(rpmfn fndata)
|
|
{
|
|
return (fndata != NULL) ? fndata->dc : 0;
|
|
}
|
|
|
|
static int rpmfnDI(rpmfn fndata, int ix)
|
|
{
|
|
int j = -1;
|
|
if (ix >= 0 && ix < rpmfnFC(fndata)) {
|
|
if (fndata->dil != NULL)
|
|
j = fndata->dil[ix];
|
|
}
|
|
return j;
|
|
}
|
|
|
|
static rpmsid rpmfnBNId(rpmfn fndata, int ix)
|
|
{
|
|
rpmsid id = 0;
|
|
if (ix >= 0 && ix < rpmfnFC(fndata)) {
|
|
if (fndata->bnid != NULL)
|
|
id = fndata->bnid[ix];
|
|
}
|
|
return id;
|
|
}
|
|
|
|
static rpmsid rpmfnDNId(rpmfn fndata, int ix)
|
|
{
|
|
rpmsid id = 0;
|
|
if (ix >= 0 && ix < rpmfnDC(fndata)) {
|
|
if (fndata->dnid != NULL)
|
|
id = fndata->dnid[ix];
|
|
}
|
|
return id;
|
|
}
|
|
|
|
static const char * rpmfnBN(rpmstrPool pool, rpmfn fndata, int ix)
|
|
{
|
|
return rpmstrPoolStr(pool, rpmfnBNId(fndata, ix));
|
|
}
|
|
|
|
static const char * rpmfnDN(rpmstrPool pool, rpmfn fndata, int ix)
|
|
{
|
|
return rpmstrPoolStr(pool, rpmfnDNId(fndata, ix));
|
|
}
|
|
|
|
static char * rpmfnFN(rpmstrPool pool, rpmfn fndata, int ix)
|
|
{
|
|
char *fn = NULL;
|
|
if (ix >= 0 && ix < rpmfnFC(fndata)) {
|
|
fn = rstrscat(NULL, rpmfnDN(pool, fndata, rpmfnDI(fndata, ix)),
|
|
rpmfnBN(pool, fndata, ix), NULL);
|
|
}
|
|
return fn;
|
|
}
|
|
|
|
rpm_count_t rpmfilesFC(rpmfiles fi)
|
|
{
|
|
return (fi != NULL ? rpmfnFC(&fi->fndata) : 0);
|
|
}
|
|
|
|
rpm_count_t rpmfilesDC(rpmfiles fi)
|
|
{
|
|
return (fi != NULL ? rpmfnDC(&fi->fndata) : 0);
|
|
}
|
|
|
|
int rpmfilesDigestAlgo(rpmfiles fi)
|
|
{
|
|
return (fi != NULL) ? fi->digestalgo : 0;
|
|
}
|
|
|
|
rpm_count_t rpmfiFC(rpmfi fi)
|
|
{
|
|
return (fi != NULL ? rpmfilesFC(fi->files) : 0);
|
|
}
|
|
|
|
rpm_count_t rpmfiDC(rpmfi fi)
|
|
{
|
|
return (fi != NULL ? rpmfilesDC(fi->files) : 0);
|
|
}
|
|
|
|
int rpmfiSetOnChdir(rpmfi fi, rpmfiChdirCb cb, void *data)
|
|
{
|
|
int rc = -1;
|
|
if (fi != NULL) {
|
|
fi->onChdir = cb;
|
|
fi->onChdirData = data;
|
|
rc = 0;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
int rpmfiFX(rpmfi fi)
|
|
{
|
|
return (fi != NULL ? fi->i : -1);
|
|
}
|
|
|
|
int rpmfiSetFX(rpmfi fi, int fx)
|
|
{
|
|
int i = -1;
|
|
|
|
if (fi != NULL && fx >= 0 && fx < rpmfilesFC(fi->files)) {
|
|
int dx = fi->j;
|
|
fi->i = fx;
|
|
fi->j = rpmfilesDI(fi->files, fi->i);
|
|
i = fi->i;
|
|
|
|
if (fi->j != dx && fi->onChdir) {
|
|
int chrc = fi->onChdir(fi, fi->onChdirData);
|
|
if (chrc < 0)
|
|
i = chrc;
|
|
}
|
|
}
|
|
return i;
|
|
}
|
|
|
|
int rpmfiDX(rpmfi fi)
|
|
{
|
|
return (fi != NULL ? fi->j : -1);
|
|
}
|
|
|
|
int rpmfilesDI(rpmfiles fi, int ix)
|
|
{
|
|
return (fi != NULL) ? rpmfnDI(&fi->fndata, ix) : -1;
|
|
}
|
|
|
|
int rpmfilesODI(rpmfiles fi, int ix)
|
|
{
|
|
return (fi != NULL) ? rpmfnDI(fi->ofndata, ix) : -1;
|
|
}
|
|
|
|
rpmsid rpmfilesBNId(rpmfiles fi, int ix)
|
|
{
|
|
return (fi != NULL) ? rpmfnBNId(&fi->fndata, ix) : 0;
|
|
}
|
|
|
|
rpmsid rpmfilesOBNId(rpmfiles fi, int ix)
|
|
{
|
|
return (fi != NULL) ? rpmfnBNId(fi->ofndata, ix) : 0;
|
|
}
|
|
|
|
rpmsid rpmfilesDNId(rpmfiles fi, int jx)
|
|
{
|
|
return (fi != NULL) ? rpmfnDNId(&fi->fndata, jx) : 0;
|
|
}
|
|
|
|
rpmsid rpmfilesODNId(rpmfiles fi, int jx)
|
|
{
|
|
return (fi != NULL) ? rpmfnDNId(fi->ofndata, jx) : 0;
|
|
}
|
|
|
|
const char * rpmfilesBN(rpmfiles fi, int ix)
|
|
{
|
|
return (fi != NULL) ? rpmfnBN(fi->pool, &fi->fndata, ix) : NULL;
|
|
}
|
|
|
|
const char * rpmfilesOBN(rpmfiles fi, int ix)
|
|
{
|
|
return (fi != NULL) ? rpmstrPoolStr(fi->pool, rpmfilesOBNId(fi, ix)) : NULL;
|
|
}
|
|
|
|
const char * rpmfilesDN(rpmfiles fi, int jx)
|
|
{
|
|
return (fi != NULL) ? rpmfnDN(fi->pool, &fi->fndata, jx) : NULL;
|
|
}
|
|
|
|
const char * rpmfilesODN(rpmfiles fi, int jx)
|
|
{
|
|
return (fi != NULL) ? rpmstrPoolStr(fi->pool, rpmfilesODNId(fi, jx)) : NULL;
|
|
}
|
|
|
|
char * rpmfilesFN(rpmfiles fi, int ix)
|
|
{
|
|
return (fi != NULL) ? rpmfnFN(fi->pool, &fi->fndata, ix) : NULL;
|
|
}
|
|
|
|
char * rpmfilesOFN(rpmfiles fi, int ix)
|
|
{
|
|
return (fi != NULL) ? rpmfnFN(fi->pool, fi->ofndata, ix) : NULL;
|
|
}
|
|
|
|
/* Fn is expected to be relative path, convert directory to relative too */
|
|
static int cmpPoolFn(rpmstrPool pool, rpmfn files, int ix, const char * fn)
|
|
{
|
|
rpmsid dnid = rpmfnDNId(files, rpmfnDI(files, ix));
|
|
const char *dn = rpmstrPoolStr(pool, dnid);
|
|
const char *reldn = (dn[0] == '/') ? dn + 1 : dn;
|
|
size_t l = strlen(reldn);
|
|
int cmp = strncmp(reldn, fn, l);
|
|
if (cmp == 0)
|
|
cmp = strcmp(rpmfnBN(pool, files, ix), fn + l);
|
|
return cmp;
|
|
}
|
|
|
|
static int rpmfnFindFN(rpmstrPool pool, rpmfn files, const char * fn)
|
|
{
|
|
int fc = rpmfnFC(files);
|
|
|
|
/*
|
|
* Skip payload prefix, turn absolute paths into relative. This
|
|
* allows handling binary rpm payloads with and without ./ prefix and
|
|
* srpm payloads which only contain basenames.
|
|
*/
|
|
if (fn[0] == '.' && fn[1] == '/')
|
|
fn += 2;
|
|
if (fn[0] == '/')
|
|
fn += 1;
|
|
|
|
/* try binary search */
|
|
|
|
int lo = 0;
|
|
int hi = fc;
|
|
int mid, cmp;
|
|
|
|
while (hi > lo) {
|
|
mid = (hi + lo) / 2 ;
|
|
cmp = cmpPoolFn(pool, files, mid, fn);
|
|
if (cmp < 0) {
|
|
lo = mid+1;
|
|
} else if (cmp > 0) {
|
|
hi = mid;
|
|
} else {
|
|
return mid;
|
|
}
|
|
}
|
|
|
|
/* not found: try linear search */
|
|
for (int i=0; i < fc; i++) {
|
|
if (cmpPoolFn(pool, files, i, fn) == 0)
|
|
return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
int rpmfilesFindFN(rpmfiles files, const char * fn)
|
|
{
|
|
return (files && fn) ? rpmfnFindFN(files->pool, &files->fndata, fn) : -1;
|
|
}
|
|
|
|
int rpmfilesFindOFN(rpmfiles files, const char * fn)
|
|
{
|
|
return (files && fn) ? rpmfnFindFN(files->pool, files->ofndata, fn) : -1;
|
|
}
|
|
|
|
int rpmfiFindFN(rpmfi fi, const char * fn)
|
|
{
|
|
int ix = -1;
|
|
|
|
if (fi != NULL) {
|
|
ix = rpmfilesFindFN(fi->files, fn);
|
|
}
|
|
return ix;
|
|
}
|
|
|
|
int rpmfiFindOFN(rpmfi fi, const char * fn)
|
|
{
|
|
int ix = -1;
|
|
|
|
if (fi != NULL) {
|
|
ix = rpmfilesFindOFN(fi->files, fn);
|
|
}
|
|
return ix;
|
|
}
|
|
|
|
/*
|
|
* Dirnames are not sorted when separated from basenames, we need to assemble
|
|
* the whole path for search (binary or otherwise) purposes.
|
|
*/
|
|
static int cmpPfx(rpmfiles files, int ix, const char *pfx, size_t plen)
|
|
{
|
|
char *fn = rpmfilesFN(files, ix);
|
|
int rc = strncmp(pfx, fn, plen);
|
|
free(fn);
|
|
return rc;
|
|
}
|
|
|
|
rpmfileAttrs rpmfilesFFlags(rpmfiles fi, int ix)
|
|
{
|
|
rpmfileAttrs FFlags = 0;
|
|
|
|
if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
|
|
if (fi->fflags != NULL)
|
|
FFlags = fi->fflags[ix];
|
|
}
|
|
return FFlags;
|
|
}
|
|
|
|
rpmVerifyAttrs rpmfilesVFlags(rpmfiles fi, int ix)
|
|
{
|
|
rpmVerifyAttrs VFlags = 0;
|
|
|
|
if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
|
|
if (fi->vflags != NULL)
|
|
VFlags = fi->vflags[ix];
|
|
}
|
|
return VFlags;
|
|
}
|
|
|
|
rpm_mode_t rpmfilesFMode(rpmfiles fi, int ix)
|
|
{
|
|
rpm_mode_t fmode = 0;
|
|
|
|
if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
|
|
if (fi->fmodes != NULL)
|
|
fmode = fi->fmodes[ix];
|
|
}
|
|
return fmode;
|
|
}
|
|
|
|
rpmfileState rpmfilesFState(rpmfiles fi, int ix)
|
|
{
|
|
rpmfileState fstate = RPMFILE_STATE_MISSING;
|
|
|
|
if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
|
|
if (fi->fstates != NULL)
|
|
fstate = (rpmfileState)fi->fstates[ix];
|
|
}
|
|
return fstate;
|
|
}
|
|
|
|
int rpmfiDigestAlgo(rpmfi fi)
|
|
{
|
|
return fi ? rpmfilesDigestAlgo(fi->files) : 0;
|
|
}
|
|
|
|
const unsigned char * rpmfilesFDigest(rpmfiles fi, int ix, int *algo, size_t *len)
|
|
{
|
|
const unsigned char *digest = NULL;
|
|
|
|
if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
|
|
size_t diglen = rpmDigestLength(fi->digestalgo);
|
|
if (fi->digests != NULL)
|
|
digest = fi->digests + (diglen * ix);
|
|
if (len)
|
|
*len = diglen;
|
|
if (algo)
|
|
*algo = fi->digestalgo;
|
|
}
|
|
return digest;
|
|
}
|
|
|
|
char * rpmfiFDigestHex(rpmfi fi, int *algo)
|
|
{
|
|
size_t diglen = 0;
|
|
char *fdigest = NULL;
|
|
const unsigned char *digest = rpmfiFDigest(fi, algo, &diglen);
|
|
if (digest) {
|
|
fdigest = rpmhex(digest, diglen);
|
|
}
|
|
return fdigest;
|
|
}
|
|
|
|
const unsigned char * rpmfilesFSignature(rpmfiles fi, int ix, size_t *len)
|
|
{
|
|
const unsigned char *signature = NULL;
|
|
|
|
if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
|
|
size_t slen = 0;
|
|
if (fi->signatures != NULL && fi->signatureoffs != NULL) {
|
|
uint32_t off = fi->signatureoffs[ix];
|
|
slen = fi->signatureoffs[ix+1] - off;
|
|
if (slen > 0)
|
|
signature = fi->signatures + off;
|
|
}
|
|
if (len)
|
|
*len = slen;
|
|
}
|
|
return signature;
|
|
}
|
|
|
|
const unsigned char * rpmfilesVSignature(rpmfiles fi, int ix, size_t *len,
|
|
uint16_t *algo)
|
|
{
|
|
const unsigned char *vsignature = NULL;
|
|
|
|
if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
|
|
if (fi->veritysigs != NULL)
|
|
vsignature = fi->veritysigs + (fi->veritysiglength * ix);
|
|
if (len)
|
|
*len = fi->veritysiglength;
|
|
if (algo)
|
|
*algo = fi->verityalgo;
|
|
}
|
|
return vsignature;
|
|
}
|
|
|
|
const char * rpmfilesFLink(rpmfiles fi, int ix)
|
|
{
|
|
const char * flink = NULL;
|
|
|
|
if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
|
|
if (fi->flinks != NULL)
|
|
flink = rpmstrPoolStr(fi->pool, fi->flinks[ix]);
|
|
}
|
|
return flink;
|
|
}
|
|
|
|
rpm_loff_t rpmfilesFSize(rpmfiles fi, int ix)
|
|
{
|
|
rpm_loff_t fsize = 0;
|
|
|
|
if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
|
|
if (fi->fsizes != NULL)
|
|
fsize = fi->fsizes[ix];
|
|
else if (fi->lfsizes != NULL)
|
|
fsize = fi->lfsizes[ix];
|
|
}
|
|
return fsize;
|
|
}
|
|
|
|
rpm_rdev_t rpmfilesFRdev(rpmfiles fi, int ix)
|
|
{
|
|
rpm_rdev_t frdev = 0;
|
|
|
|
if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
|
|
if (fi->frdevs != NULL)
|
|
frdev = fi->frdevs[ix];
|
|
}
|
|
return frdev;
|
|
}
|
|
|
|
rpm_ino_t rpmfilesFInode(rpmfiles fi, int ix)
|
|
{
|
|
rpm_ino_t finode = 0;
|
|
|
|
if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
|
|
if (fi->finodes != NULL)
|
|
finode = fi->finodes[ix];
|
|
}
|
|
return finode;
|
|
}
|
|
|
|
rpm_color_t rpmfilesColor(rpmfiles files)
|
|
{
|
|
rpm_color_t color = 0;
|
|
|
|
if (files != NULL && files->fcolors != NULL) {
|
|
int fc = rpmfilesFC(files);
|
|
for (int i = 0; i < fc; i++)
|
|
color |= files->fcolors[i];
|
|
/* XXX ignore all but lsnibble for now. */
|
|
color &= 0xf;
|
|
}
|
|
return color;
|
|
}
|
|
|
|
rpm_color_t rpmfiColor(rpmfi fi)
|
|
{
|
|
return (fi != NULL) ? rpmfilesColor(fi->files) : 0;
|
|
}
|
|
|
|
rpm_color_t rpmfilesFColor(rpmfiles fi, int ix)
|
|
{
|
|
rpm_color_t fcolor = 0;
|
|
|
|
if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
|
|
if (fi->fcolors != NULL)
|
|
/* XXX ignore all but lsnibble for now. */
|
|
fcolor = (fi->fcolors[ix] & 0x0f);
|
|
}
|
|
return fcolor;
|
|
}
|
|
|
|
const char * rpmfilesFClass(rpmfiles fi, int ix)
|
|
{
|
|
const char * fclass = NULL;
|
|
int cdictx;
|
|
|
|
if (fi != NULL && fi->fcdictx != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
|
|
cdictx = fi->fcdictx[ix];
|
|
if (fi->cdict != NULL && cdictx >= 0 && cdictx < fi->ncdict)
|
|
fclass = fi->cdict[cdictx];
|
|
}
|
|
return fclass;
|
|
}
|
|
|
|
const char * rpmfilesFMime(rpmfiles fi, int ix)
|
|
{
|
|
const char * fmime = NULL;
|
|
int mdictx;
|
|
|
|
if (fi != NULL && fi->fmdictx != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
|
|
mdictx = fi->fmdictx[ix];
|
|
if (fi->mdict != NULL && mdictx >= 0 && mdictx < fi->nmdict)
|
|
fmime = fi->mdict[mdictx];
|
|
}
|
|
return fmime;
|
|
}
|
|
|
|
uint32_t rpmfilesFDepends(rpmfiles fi, int ix, const uint32_t ** fddictp)
|
|
{
|
|
int fddictx = -1;
|
|
int fddictn = 0;
|
|
const uint32_t * fddict = NULL;
|
|
|
|
if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
|
|
if (fi->fddictn != NULL)
|
|
fddictn = fi->fddictn[ix];
|
|
if (fddictn > 0 && fi->fddictx != NULL)
|
|
fddictx = fi->fddictx[ix];
|
|
if (fi->ddict != NULL && fddictx >= 0 && (fddictx+fddictn) <= fi->nddict)
|
|
fddict = fi->ddict + fddictx;
|
|
}
|
|
if (fddictp)
|
|
*fddictp = fddict;
|
|
return fddictn;
|
|
}
|
|
|
|
uint32_t rpmfilesFLinks(rpmfiles fi, int ix, const int ** files)
|
|
{
|
|
uint32_t nlink = 0;
|
|
|
|
if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
|
|
nlink = 1;
|
|
if (fi->nlinks) {
|
|
auto entry = fi->nlinks->find(ix);
|
|
if (entry != fi->nlinks->end()) {
|
|
nlink = entry->second->size();
|
|
if (files) {
|
|
*files = entry->second->data();
|
|
}
|
|
} else if (files){
|
|
*files = NULL;
|
|
}
|
|
}
|
|
}
|
|
return nlink;
|
|
}
|
|
|
|
uint32_t rpmfiFLinks(rpmfi fi, const int ** files)
|
|
{
|
|
return rpmfilesFLinks(fi ? fi->files : NULL, fi ? fi->i : -1, files);
|
|
}
|
|
|
|
uint32_t rpmfilesFNlink(rpmfiles fi, int ix)
|
|
{
|
|
return rpmfilesFLinks(fi, ix, NULL);
|
|
}
|
|
|
|
rpm_time_t rpmfilesFMtime(rpmfiles fi, int ix)
|
|
{
|
|
rpm_time_t fmtime = 0;
|
|
|
|
if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
|
|
if (fi->fmtimes != NULL)
|
|
fmtime = fi->fmtimes[ix];
|
|
}
|
|
return fmtime;
|
|
}
|
|
|
|
const char * rpmfilesFUser(rpmfiles fi, int ix)
|
|
{
|
|
const char * fuser = NULL;
|
|
|
|
if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
|
|
if (fi->fuser != NULL)
|
|
fuser = rpmstrPoolStr(fi->pool, fi->fuser[ix]);
|
|
}
|
|
return fuser;
|
|
}
|
|
|
|
const char * rpmfilesFGroup(rpmfiles fi, int ix)
|
|
{
|
|
const char * fgroup = NULL;
|
|
|
|
if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
|
|
if (fi->fgroup != NULL)
|
|
fgroup = rpmstrPoolStr(fi->pool, fi->fgroup[ix]);
|
|
}
|
|
return fgroup;
|
|
}
|
|
|
|
const char * rpmfilesFCaps(rpmfiles fi, int ix)
|
|
{
|
|
const char *fcaps = NULL;
|
|
if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
|
|
fcaps = fi->fcaps ? fi->fcaps[ix] : "";
|
|
}
|
|
return fcaps;
|
|
}
|
|
|
|
const char * rpmfilesFLangs(rpmfiles fi, int ix)
|
|
{
|
|
const char *flangs = NULL;
|
|
if (fi != NULL && fi->flangs != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
|
|
flangs = rpmstrPoolStr(fi->pool, fi->flangs[ix]);
|
|
}
|
|
return flangs;
|
|
}
|
|
|
|
int rpmfilesStat(rpmfiles fi, int ix, int flags, struct stat *sb)
|
|
{
|
|
int rc = -1;
|
|
if (fi && sb) {
|
|
/* XXX FIXME define proper flags with sane semantics... */
|
|
int warn = flags & 0x1;
|
|
const char *user = rpmfilesFUser(fi, ix);
|
|
const char *group = rpmfilesFGroup(fi, ix);
|
|
|
|
memset(sb, 0, sizeof(*sb));
|
|
sb->st_nlink = rpmfilesFLinks(fi, ix, NULL);
|
|
sb->st_ino = rpmfilesFInode(fi, ix);
|
|
sb->st_rdev = rpmfilesFRdev(fi, ix);
|
|
sb->st_mode = rpmfilesFMode(fi, ix);
|
|
sb->st_mtime = rpmfilesFMtime(fi, ix);
|
|
|
|
/* Only regular files and symlinks have a size */
|
|
if (S_ISREG(sb->st_mode) || S_ISLNK(sb->st_mode))
|
|
sb->st_size = rpmfilesFSize(fi, ix);
|
|
|
|
if (user && rpmugUid(user, &sb->st_uid)) {
|
|
if (warn)
|
|
rpmlog(RPMLOG_WARNING,
|
|
_("user %s does not exist - using %s\n"), user, UID_0_USER);
|
|
sb->st_mode &= ~S_ISUID; /* turn off suid bit */
|
|
}
|
|
|
|
if (group && rpmugGid(group, &sb->st_gid)) {
|
|
if (warn)
|
|
rpmlog(RPMLOG_WARNING,
|
|
_("group %s does not exist - using %s\n"), group, GID_0_GROUP);
|
|
sb->st_mode &= ~S_ISGID; /* turn off sgid bit */
|
|
}
|
|
|
|
rc = 0;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
struct fingerPrint *rpmfilesFps(rpmfiles fi)
|
|
{
|
|
return (fi != NULL) ? fi->fps : NULL;
|
|
}
|
|
|
|
static int iterFwd(rpmfi fi)
|
|
{
|
|
return fi->i + 1;
|
|
}
|
|
|
|
static int iterBack(rpmfi fi)
|
|
{
|
|
return fi->i - 1;
|
|
}
|
|
|
|
static int iterInterval(rpmfi fi)
|
|
{
|
|
if (fi->i == -1)
|
|
return fi->intervalStart;
|
|
else if (fi->i + 1 < fi->intervalEnd)
|
|
return fi->i + 1;
|
|
else
|
|
return RPMERR_ITER_END;
|
|
}
|
|
|
|
int rpmfiNext(rpmfi fi)
|
|
{
|
|
int next = -1;
|
|
if (fi != NULL) {
|
|
do {
|
|
next = fi->next(fi);
|
|
} while (next == RPMERR_ITER_SKIP);
|
|
|
|
if (next >= 0)
|
|
next = rpmfiSetFX(fi, next);
|
|
}
|
|
return next;
|
|
}
|
|
|
|
rpmfi rpmfiInit(rpmfi fi, int fx)
|
|
{
|
|
if (fi != NULL) {
|
|
if (fx >= 0 && fx < rpmfilesFC(fi->files)) {
|
|
fi->i = fx - 1;
|
|
fi->j = -1;
|
|
}
|
|
}
|
|
|
|
return fi;
|
|
}
|
|
|
|
rpmFileTypes rpmfiWhatis(rpm_mode_t mode)
|
|
{
|
|
if (S_ISDIR(mode)) return XDIR;
|
|
if (S_ISCHR(mode)) return CDEV;
|
|
if (S_ISBLK(mode)) return BDEV;
|
|
if (S_ISLNK(mode)) return LINK;
|
|
if (S_ISSOCK(mode)) return SOCK;
|
|
if (S_ISFIFO(mode)) return PIPE;
|
|
return REG;
|
|
}
|
|
|
|
int rpmfilesCompare(rpmfiles afi, int aix, rpmfiles bfi, int bix)
|
|
{
|
|
mode_t amode = rpmfilesFMode(afi, aix);
|
|
mode_t bmode = rpmfilesFMode(bfi, bix);
|
|
rpmFileTypes awhat = rpmfiWhatis(amode);
|
|
|
|
if ((rpmfilesFFlags(afi, aix) & RPMFILE_GHOST) ||
|
|
(rpmfilesFFlags(bfi, bix) & RPMFILE_GHOST)) return 0;
|
|
|
|
/* Mode difference is a conflict, except for symlinks */
|
|
if (!(awhat == LINK && rpmfiWhatis(bmode) == LINK) && amode != bmode)
|
|
return 1;
|
|
|
|
if (awhat == LINK || awhat == REG) {
|
|
if (rpmfilesFSize(afi, aix) != rpmfilesFSize(bfi, bix))
|
|
return 1;
|
|
}
|
|
|
|
if (!rstreq(rpmfilesFUser(afi, aix), rpmfilesFUser(bfi, bix)))
|
|
return 1;
|
|
if (!rstreq(rpmfilesFGroup(afi, aix), rpmfilesFGroup(bfi, bix)))
|
|
return 1;
|
|
|
|
if (awhat == LINK) {
|
|
const char * alink = rpmfilesFLink(afi, aix);
|
|
const char * blink = rpmfilesFLink(bfi, bix);
|
|
if (alink == blink) return 0;
|
|
if (alink == NULL) return 1;
|
|
if (blink == NULL) return -1;
|
|
return strcmp(alink, blink);
|
|
} else if (awhat == REG) {
|
|
size_t adiglen, bdiglen;
|
|
int aalgo, balgo;
|
|
const unsigned char * adigest, * bdigest;
|
|
adigest = rpmfilesFDigest(afi, aix, &aalgo, &adiglen);
|
|
bdigest = rpmfilesFDigest(bfi, bix, &balgo, &bdiglen);
|
|
if (adigest == bdigest) return 0;
|
|
if (adigest == NULL) return 1;
|
|
if (bdigest == NULL) return -1;
|
|
/* can't meaningfully compare different hash types */
|
|
if (aalgo != balgo || adiglen != bdiglen) return -1;
|
|
return memcmp(adigest, bdigest, adiglen);
|
|
} else if (awhat == CDEV || awhat == BDEV) {
|
|
if (rpmfilesFRdev(afi, aix) != rpmfilesFRdev(bfi, bix))
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int rpmfileContentsEqual(rpmfiles ofi, int oix, rpmfiles nfi, int nix)
|
|
{
|
|
char * fn = rpmfilesFN(nfi, nix);
|
|
rpmFileTypes diskWhat, newWhat, oldWhat;
|
|
struct stat sb;
|
|
int equal = 0;
|
|
|
|
if (fn == NULL || (lstat(fn, &sb))) {
|
|
goto exit; /* The file doesn't exist on the disk */
|
|
}
|
|
|
|
if (rpmfilesFSize(nfi, nix) != sb.st_size) {
|
|
goto exit;
|
|
}
|
|
|
|
diskWhat = rpmfiWhatis((rpm_mode_t)sb.st_mode);
|
|
newWhat = rpmfiWhatis(rpmfilesFMode(nfi, nix));
|
|
oldWhat = rpmfiWhatis(rpmfilesFMode(ofi, oix));
|
|
if ((diskWhat != newWhat) || (diskWhat != oldWhat)) {
|
|
goto exit;
|
|
}
|
|
|
|
if (diskWhat == REG) {
|
|
int oalgo, nalgo;
|
|
size_t odiglen, ndiglen;
|
|
const unsigned char * odigest, * ndigest;
|
|
char buffer[1024];
|
|
|
|
odigest = rpmfilesFDigest(ofi, oix, &oalgo, &odiglen);
|
|
ndigest = rpmfilesFDigest(nfi, nix, &nalgo, &ndiglen);
|
|
/* See if the file in old pkg is identical to the one in new pkg */
|
|
if ((oalgo != nalgo) || (odiglen != ndiglen) || (!ndigest) ||
|
|
(memcmp(odigest, ndigest, ndiglen) != 0)) {
|
|
goto exit;
|
|
}
|
|
|
|
if (rpmDoDigest(nalgo, fn, 0, (unsigned char *)buffer) != 0) {
|
|
goto exit; /* assume file has been removed */
|
|
}
|
|
|
|
/* See if the file on disk is identical to the one in new pkg */
|
|
if (memcmp(ndigest, buffer, ndiglen) == 0) {
|
|
equal = 1;
|
|
goto exit;
|
|
}
|
|
} else if (diskWhat == LINK) {
|
|
const char * nFLink;
|
|
char buffer[1024];
|
|
ssize_t link_len;
|
|
|
|
nFLink = rpmfilesFLink(nfi, nix);
|
|
link_len = readlink(fn, buffer, sizeof(buffer) - 1);
|
|
if (link_len == -1) {
|
|
goto exit; /* assume file has been removed */
|
|
}
|
|
buffer[link_len] = '\0';
|
|
/* See if the link on disk is identical to the one in new pkg */
|
|
if (nFLink && rstreq(nFLink, buffer)) {
|
|
equal = 1;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
exit:
|
|
free(fn);
|
|
return equal;
|
|
}
|
|
|
|
|
|
rpmFileAction rpmfilesDecideFate(rpmfiles ofi, int oix,
|
|
rpmfiles nfi, int nix,
|
|
int skipMissing)
|
|
{
|
|
char * fn = rpmfilesFN(nfi, nix);
|
|
rpmfileAttrs newFlags = rpmfilesFFlags(nfi, nix);
|
|
char buffer[1024];
|
|
rpmFileTypes dbWhat, newWhat, diskWhat;
|
|
struct stat sb;
|
|
rpmFileAction save = (newFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_SAVE;
|
|
rpmFileAction action = FA_CREATE; /* assume we can create */
|
|
|
|
/* If the new file is a ghost, leave whatever might be on disk alone. */
|
|
if (newFlags & RPMFILE_GHOST) {
|
|
action = FA_SKIP;
|
|
goto exit;
|
|
}
|
|
|
|
if (lstat(fn, &sb)) {
|
|
/*
|
|
* The file doesn't exist on the disk. Create it unless the new
|
|
* package has marked it as missingok, or allfiles is requested.
|
|
*/
|
|
if (skipMissing && (newFlags & RPMFILE_MISSINGOK)) {
|
|
rpmlog(RPMLOG_DEBUG, "%s skipped due to missingok flag\n",
|
|
fn);
|
|
action = FA_SKIP;
|
|
goto exit;
|
|
} else {
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
diskWhat = rpmfiWhatis((rpm_mode_t)sb.st_mode);
|
|
dbWhat = rpmfiWhatis(rpmfilesFMode(ofi, oix));
|
|
newWhat = rpmfiWhatis(rpmfilesFMode(nfi, nix));
|
|
|
|
/*
|
|
* This order matters - we'd prefer to TOUCH the file if at all
|
|
* possible in case something else (like the timestamp) has changed.
|
|
* Only regular files and symlinks might need a backup, everything
|
|
* else falls through here with FA_CREATE.
|
|
*/
|
|
if (dbWhat == REG) {
|
|
int oalgo, nalgo;
|
|
size_t odiglen, ndiglen;
|
|
const unsigned char * odigest, * ndigest;
|
|
|
|
/* See if the file on disk is identical to the one in new pkg */
|
|
ndigest = rpmfilesFDigest(nfi, nix, &nalgo, &ndiglen);
|
|
if (diskWhat == REG && newWhat == REG) {
|
|
if (rpmDoDigest(nalgo, fn, 0, (unsigned char *)buffer))
|
|
goto exit; /* assume file has been removed */
|
|
if (ndigest && memcmp(ndigest, buffer, ndiglen) == 0) {
|
|
action = FA_TOUCH;
|
|
goto exit; /* unmodified config file, touch it. */
|
|
}
|
|
}
|
|
|
|
/* See if the file on disk is identical to the one in old pkg */
|
|
odigest = rpmfilesFDigest(ofi, oix, &oalgo, &odiglen);
|
|
if (diskWhat == REG) {
|
|
/* hash algo changed or digest was not computed, recalculate it */
|
|
if ((oalgo != nalgo) || (newWhat != REG)) {
|
|
if (rpmDoDigest(oalgo, fn, 0, (unsigned char *)buffer))
|
|
goto exit; /* assume file has been removed */
|
|
}
|
|
if (odigest && memcmp(odigest, buffer, odiglen) == 0)
|
|
goto exit; /* unmodified config file, replace. */
|
|
}
|
|
|
|
/* if new file is no longer config, backup it and replace it */
|
|
if (!(newFlags & RPMFILE_CONFIG)) {
|
|
action = FA_SAVE;
|
|
goto exit;
|
|
}
|
|
|
|
/* If file can be determined identical in old and new pkg, let it be */
|
|
if (newWhat == REG && oalgo == nalgo && odiglen == ndiglen) {
|
|
if (odigest && ndigest && memcmp(odigest, ndigest, odiglen) == 0) {
|
|
action = FA_SKIP; /* identical file, dont bother */
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
/* ...but otherwise a backup will be needed */
|
|
action = save;
|
|
} else if (dbWhat == LINK) {
|
|
const char * oFLink, * nFLink;
|
|
|
|
if (diskWhat == LINK) {
|
|
/* Read link from the disk */
|
|
ssize_t link_len = readlink(fn, buffer, sizeof(buffer) - 1);
|
|
if (link_len == -1)
|
|
goto exit; /* assume file has been removed */
|
|
buffer[link_len] = '\0';
|
|
}
|
|
|
|
/* See if the link on disk is identical to the one in new pkg */
|
|
nFLink = rpmfilesFLink(nfi, nix);
|
|
if (diskWhat == LINK && newWhat == LINK) {
|
|
if (nFLink && rstreq(nFLink, buffer)) {
|
|
action = FA_TOUCH;
|
|
goto exit; /* unmodified config file, touch it. */
|
|
}
|
|
}
|
|
|
|
/* See if the link on disk is identical to the one in old pkg */
|
|
oFLink = rpmfilesFLink(ofi, oix);
|
|
if (diskWhat == LINK) {
|
|
if (oFLink && rstreq(oFLink, buffer))
|
|
goto exit; /* unmodified config file, replace. */
|
|
}
|
|
|
|
/* if new file is no longer config, backup it and replace it */
|
|
if (!(newFlags & RPMFILE_CONFIG)) {
|
|
action = FA_SAVE;
|
|
goto exit;
|
|
}
|
|
|
|
/* If link is identical in old and new pkg, let it be */
|
|
if (newWhat == LINK && oFLink && nFLink && rstreq(oFLink, nFLink)) {
|
|
action = FA_SKIP; /* identical file, don't bother. */
|
|
goto exit;
|
|
}
|
|
|
|
/* ...but otherwise a backup will be needed */
|
|
action = save;
|
|
}
|
|
|
|
exit:
|
|
free(fn);
|
|
return action;
|
|
}
|
|
|
|
int rpmfilesConfigConflict(rpmfiles fi, int ix)
|
|
{
|
|
char * fn = NULL;
|
|
rpmfileAttrs flags = rpmfilesFFlags(fi, ix);
|
|
char buffer[1024];
|
|
rpmFileTypes newWhat, diskWhat;
|
|
struct stat sb;
|
|
int rc = 0;
|
|
|
|
/* Non-configs are not config conflicts. */
|
|
if (!(flags & RPMFILE_CONFIG))
|
|
return 0;
|
|
|
|
/* Only links and regular files can be %config */
|
|
newWhat = rpmfiWhatis(rpmfilesFMode(fi, ix));
|
|
if (newWhat != LINK && newWhat != REG)
|
|
return 0;
|
|
|
|
/* If it's not on disk, there's nothing to be saved */
|
|
fn = rpmfilesFN(fi, ix);
|
|
if (lstat(fn, &sb))
|
|
goto exit;
|
|
|
|
/*
|
|
* Preserve legacy behavior: an existing %ghost %config is considered
|
|
* "modified" but unlike regular %config, its never removed and
|
|
* never backed up. Whether this actually makes sense is a whole
|
|
* another question, but this is very long-standing behavior that
|
|
* people might be depending on. The resulting FA_ALTNAME etc action
|
|
* is special-cased in FSM to avoid actually creating backups on ghosts.
|
|
*/
|
|
if (flags & RPMFILE_GHOST) {
|
|
rc = 1;
|
|
goto exit;
|
|
}
|
|
|
|
/* Files of different types obviously are not identical */
|
|
diskWhat = rpmfiWhatis((rpm_mode_t)sb.st_mode);
|
|
if (diskWhat != newWhat) {
|
|
rc = 1;
|
|
goto exit;
|
|
}
|
|
|
|
/* Files of different sizes obviously are not identical */
|
|
if (rpmfilesFSize(fi, ix) != sb.st_size) {
|
|
rc = 1;
|
|
goto exit;
|
|
}
|
|
|
|
memset(buffer, 0, sizeof(buffer));
|
|
if (newWhat == REG) {
|
|
int algo;
|
|
size_t diglen;
|
|
const unsigned char *ndigest = rpmfilesFDigest(fi,ix, &algo, &diglen);
|
|
if (rpmDoDigest(algo, fn, 0, (unsigned char *)buffer))
|
|
goto exit; /* assume file has been removed */
|
|
if (ndigest && memcmp(ndigest, buffer, diglen) == 0)
|
|
goto exit; /* unmodified config file */
|
|
} else /* newWhat == LINK */ {
|
|
const char * nFLink;
|
|
ssize_t link_len = readlink(fn, buffer, sizeof(buffer) - 1);
|
|
if (link_len == -1)
|
|
goto exit; /* assume file has been removed */
|
|
buffer[link_len] = '\0';
|
|
nFLink = rpmfilesFLink(fi, ix);
|
|
if (nFLink && rstreq(nFLink, buffer))
|
|
goto exit; /* unmodified config file */
|
|
}
|
|
|
|
rc = 1;
|
|
|
|
exit:
|
|
free(fn);
|
|
return rc;
|
|
}
|
|
|
|
rpmfiles rpmfilesFree(rpmfiles fi)
|
|
{
|
|
if (fi == NULL) return NULL;
|
|
|
|
if (fi->nrefs > 1)
|
|
return rpmfilesUnlink(fi);
|
|
|
|
if (rpmfilesFC(fi) > 0) {
|
|
if (fi->ofndata != &fi->fndata) {
|
|
rpmfnClear(fi->ofndata);
|
|
delete fi->ofndata;
|
|
}
|
|
rpmfnClear(&fi->fndata);
|
|
|
|
fi->flinks = _free(fi->flinks);
|
|
fi->flangs = _free(fi->flangs);
|
|
fi->digests = _free(fi->digests);
|
|
fi->signatures = _free(fi->signatures);
|
|
fi->signatureoffs = _free(fi->signatureoffs);
|
|
fi->veritysigs = _free(fi->veritysigs);
|
|
fi->fcaps = _free(fi->fcaps);
|
|
|
|
fi->cdict = _free(fi->cdict);
|
|
fi->mdict = _free(fi->mdict);
|
|
|
|
fi->fuser = _free(fi->fuser);
|
|
fi->fgroup = _free(fi->fgroup);
|
|
|
|
fi->fstates = _free(fi->fstates);
|
|
fi->fps = _free(fi->fps);
|
|
|
|
/* these point to header memory if KEEPHEADER is used, dont free */
|
|
if (!(fi->fiflags & RPMFI_KEEPHEADER) && fi->h == NULL) {
|
|
fi->fmtimes = _free(fi->fmtimes);
|
|
fi->fmodes = _free(fi->fmodes);
|
|
fi->fflags = _free(fi->fflags);
|
|
fi->vflags = _free(fi->vflags);
|
|
fi->fsizes = _free(fi->fsizes);
|
|
fi->lfsizes = _free(fi->lfsizes);
|
|
fi->frdevs = _free(fi->frdevs);
|
|
fi->finodes = _free(fi->finodes);
|
|
|
|
fi->fcolors = _free(fi->fcolors);
|
|
fi->fcdictx = _free(fi->fcdictx);
|
|
fi->fmdictx = _free(fi->fmdictx);
|
|
fi->ddict = _free(fi->ddict);
|
|
fi->fddictx = _free(fi->fddictx);
|
|
fi->fddictn = _free(fi->fddictn);
|
|
|
|
}
|
|
}
|
|
|
|
fi->replacedSizes = _free(fi->replacedSizes);
|
|
|
|
fi->h = headerFree(fi->h);
|
|
fi->pool = rpmstrPoolFree(fi->pool);
|
|
|
|
delete fi->nlinks;
|
|
|
|
(void) rpmfilesUnlink(fi);
|
|
delete fi;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
rpmfi rpmfiFree(rpmfi fi)
|
|
{
|
|
if (fi == NULL) return NULL;
|
|
|
|
if (fi->nrefs > 1)
|
|
return rpmfiUnlink(fi);
|
|
|
|
fi->files = rpmfilesFree(fi->files);
|
|
fi->fn = _free(fi->fn);
|
|
fi->ofn = _free(fi->ofn);
|
|
fi->found = _free(fi->found);
|
|
fi->archive = rpmcpioFree(fi->archive);
|
|
|
|
delete fi;
|
|
return NULL;
|
|
}
|
|
|
|
static rpmsid * tag2pool(rpmstrPool pool, Header h, rpmTag tag, rpm_count_t size)
|
|
{
|
|
rpmsid *sids = NULL;
|
|
struct rpmtd_s td;
|
|
if (headerGet(h, tag, &td, HEADERGET_MINMEM)) {
|
|
if (rpmtdCount(&td) == size) { /* ensure right size */
|
|
sids = rpmtdToPool(&td, pool);
|
|
}
|
|
rpmtdFreeData(&td);
|
|
}
|
|
return sids;
|
|
}
|
|
|
|
/* validate a indexed tag data triplet (such as file bn/dn/dx) */
|
|
static int indexSane(rpmtd xd, rpmtd yd, rpmtd zd)
|
|
{
|
|
int sane = 0;
|
|
uint32_t xc = rpmtdCount(xd);
|
|
uint32_t yc = rpmtdCount(yd);
|
|
uint32_t zc = rpmtdCount(zd);
|
|
|
|
/* check that the amount of data in each is sane */
|
|
/* normally yc <= xc but larger values are not fatal (RhBug:1001553) */
|
|
if (xc > 0 && yc > 0 && zc == xc) {
|
|
uint32_t * i, nvalid = 0;
|
|
/* ...and that the indexes are within bounds */
|
|
while ((i = rpmtdNextUint32(zd))) {
|
|
if (*i >= yc)
|
|
break;
|
|
nvalid++;
|
|
}
|
|
/* unless the loop runs to finish, the data is broken */
|
|
sane = (nvalid == zc);
|
|
}
|
|
return sane;
|
|
}
|
|
|
|
/* Get file data from header */
|
|
/* Requires totalfc to be set and label err: to goto on error */
|
|
#define _hgfi(_h, _tag, _td, _flags, _data) \
|
|
if (headerGet((_h), (_tag), (_td), (_flags))) { \
|
|
if (rpmtdCount(_td) != totalfc) { \
|
|
rpmlog(RPMLOG_ERR, _("Wrong number of entries for tag %s: %u found but %u expected.\n"), rpmTagGetName(_tag), rpmtdCount(_td), totalfc); \
|
|
goto err; \
|
|
} \
|
|
if (rpmTagGetTagType(_tag) != RPM_STRING_ARRAY_TYPE && rpmTagGetTagType(_tag) != RPM_I18NSTRING_TYPE && \
|
|
(_td)->size < totalfc * sizeof(*(_data))) { \
|
|
rpmlog(RPMLOG_ERR, _("Malformed data for tag %s: %u bytes found but %" PRIu64 " expected.\n"), rpmTagGetName(_tag), (_td)->size, (uint64_t)totalfc * sizeof(*(_data))); \
|
|
goto err; \
|
|
} \
|
|
_data = (typeof((_data)))((_td)->data); \
|
|
}
|
|
/* Get file data from header without checking number of entries */
|
|
#define _hgfinc(_h, _tag, _td, _flags, _data) \
|
|
if (headerGet((_h), (_tag), (_td), (_flags))) {\
|
|
_data = (typeof((_data)))((_td)->data); \
|
|
}
|
|
|
|
/*** Hard link handling ***/
|
|
|
|
struct fileid_s {
|
|
rpm_dev_t id_dev;
|
|
rpm_ino_t id_ino;
|
|
|
|
bool operator == (const fileid_s & other) const {
|
|
return (id_dev == other.id_dev && id_ino == other.id_ino);
|
|
}
|
|
};
|
|
|
|
struct fidHash {
|
|
size_t operator() (const fileid_s & fid) const {
|
|
return fid.id_ino + (fid.id_dev<<16) + (fid.id_dev>>16);
|
|
}
|
|
};
|
|
|
|
static void rpmfilesBuildNLink(rpmfiles fi, Header h)
|
|
{
|
|
std::unordered_map<fileid_s,std::shared_ptr<hardlinks>,fidHash> files;
|
|
rpm_dev_t * fdevs = NULL;
|
|
struct rpmtd_s td;
|
|
int totalfc = rpmfilesFC(fi);
|
|
bool havelinks = false;
|
|
|
|
if (!fi->finodes)
|
|
return;
|
|
|
|
_hgfi(h, RPMTAG_FILEDEVICES, &td, HEADERGET_ALLOC, fdevs);
|
|
if (!fdevs)
|
|
return;
|
|
|
|
/* Collect file indexes sharing the same dev:ino pair (if any) */
|
|
for (int i=0; i < totalfc; i++) {
|
|
if (!S_ISREG(rpmfilesFMode(fi, i)) ||
|
|
(rpmfilesFFlags(fi, i) & RPMFILE_GHOST) ||
|
|
fi->finodes[i] <= 0) {
|
|
continue;
|
|
}
|
|
fileid_s f_id = { fdevs[i], fi->finodes[i] };
|
|
/* Shared pointer vector, this will be reused in the below */
|
|
auto entry = files.emplace(
|
|
std::make_pair(f_id, std::make_shared<hardlinks>())).first;
|
|
entry->second->push_back(i);
|
|
if (entry->second->size() > 1)
|
|
havelinks = true;
|
|
}
|
|
|
|
/* Collect the hardlink sets to into a hash keyed by file index */
|
|
if (havelinks) {
|
|
fi->nlinks = new nlinkHash {};
|
|
for (int i=0; i < totalfc; i++) {
|
|
if (!S_ISREG(rpmfilesFMode(fi, i)) ||
|
|
(rpmfilesFFlags(fi, i) & RPMFILE_GHOST)) {
|
|
continue;
|
|
}
|
|
|
|
/* Transfer the hardlink sets from above to the new hash */
|
|
fileid_s f_id = { fdevs[i], fi->finodes[i] };
|
|
auto entry = files.find(f_id);
|
|
if (entry != files.end()) {
|
|
auto const & links = entry->second;
|
|
if (links->size() > 1) {
|
|
for (int j : (*links))
|
|
fi->nlinks->insert({j, links});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
_free(fdevs);
|
|
err:
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Convert a tag of variable len hex strings to binary presentation,
|
|
* accessed via offsets to a contiguous binary blob. Empty values
|
|
* are represented by identical consequtive offsets. The offsets array
|
|
* always has one extra element to allow calculating the size of the
|
|
* last element.
|
|
*/
|
|
static uint8_t *hex2binv(Header h, rpmTagVal tag, rpm_count_t num,
|
|
uint32_t **offsetp)
|
|
{
|
|
struct rpmtd_s td;
|
|
uint8_t *bin = NULL;
|
|
uint32_t *offs = NULL;
|
|
|
|
if (headerGet(h, tag, &td, HEADERGET_MINMEM) && rpmtdCount(&td) == num) {
|
|
const char *s;
|
|
int i = 0;
|
|
uint8_t *t = bin = (uint8_t *)xmalloc(((rpmtdSize(&td) / 2) + 1));
|
|
offs = (uint32_t *)xmalloc((num + 1) * sizeof(*offs));
|
|
|
|
while ((s = rpmtdNextString(&td))) {
|
|
uint32_t slen = strlen(s);
|
|
uint32_t len = slen / 2;
|
|
if (slen % 2) {
|
|
bin = _free(bin);
|
|
offs = _free(offs);
|
|
goto exit;
|
|
}
|
|
offs[i] = t - bin;
|
|
for (int j = 0; j < len; j++, t++, s += 2)
|
|
*t = (rnibble(s[0]) << 4) | rnibble(s[1]);
|
|
i++;
|
|
}
|
|
offs[i] = t - bin;
|
|
*offsetp = offs;
|
|
}
|
|
|
|
exit:
|
|
rpmtdFreeData(&td);
|
|
return bin;
|
|
}
|
|
|
|
/* Convert a tag of hex strings to binary presentation */
|
|
static uint8_t *hex2bin(Header h, rpmTagVal tag, rpm_count_t num, size_t len)
|
|
{
|
|
struct rpmtd_s td;
|
|
uint8_t *bin = NULL;
|
|
|
|
if (headerGet(h, tag, &td, HEADERGET_MINMEM) && rpmtdCount(&td) == num) {
|
|
uint8_t *t = bin = (uint8_t *)xmalloc(num * len);
|
|
const char *s;
|
|
|
|
while ((s = rpmtdNextString(&td))) {
|
|
if (*s == '\0') {
|
|
memset(t, 0, len);
|
|
t += len;
|
|
continue;
|
|
}
|
|
if (strlen(s) != len * 2) {
|
|
bin = _free(bin);
|
|
break;
|
|
}
|
|
for (int j = 0; j < len; j++, t++, s += 2)
|
|
*t = (rnibble(s[0]) << 4) | rnibble(s[1]);
|
|
}
|
|
}
|
|
rpmtdFreeData(&td);
|
|
|
|
return bin;
|
|
}
|
|
|
|
/*
|
|
* Convert a tag of base64 strings to binary presentation.
|
|
* This handles variable length strings by finding the longest string
|
|
* before building the output array. Dummy strings in the tag should be
|
|
* added as '\0'
|
|
*/
|
|
static uint8_t *base2bin(Header h, rpmTagVal tag, rpm_count_t num, int *len)
|
|
{
|
|
struct rpmtd_s td;
|
|
uint8_t *bin = NULL, *t = NULL;
|
|
size_t maxlen = 0;
|
|
int status, i= 0;
|
|
uint8_t **arr = (uint8_t **)xmalloc(num * sizeof(void *));
|
|
size_t *lengths = (size_t *)xcalloc(num, sizeof(size_t));
|
|
const char *s;
|
|
|
|
if (!headerGet(h, tag, &td, HEADERGET_MINMEM) || rpmtdCount(&td) != num)
|
|
goto out;
|
|
|
|
while ((s = rpmtdNextString(&td))) {
|
|
/* Insert a dummy entry for empty strings */
|
|
if (*s == '\0') {
|
|
arr[i++] = NULL;
|
|
continue;
|
|
}
|
|
status = rpmBase64Decode(s, (void**)&arr[i], &lengths[i]);
|
|
if (lengths[i] > maxlen)
|
|
maxlen = lengths[i];
|
|
if (status) {
|
|
rpmlog(RPMLOG_DEBUG, _("%s: base64 decode failed, len %zu\n"),
|
|
__func__, lengths[i]);
|
|
goto out;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
if (maxlen) {
|
|
rpmlog(RPMLOG_DEBUG, _("%s: base64 decode success, len %zu\n"),
|
|
__func__, maxlen);
|
|
|
|
t = bin = (uint8_t *)xcalloc(num, maxlen);
|
|
|
|
for (i = 0; i < num; i++) {
|
|
if (arr[i]) {
|
|
memcpy(t, arr[i], lengths[i]);
|
|
free(arr[i]);
|
|
}
|
|
t += maxlen;
|
|
}
|
|
*len = maxlen;
|
|
}
|
|
out:
|
|
free(arr);
|
|
free(lengths);
|
|
rpmtdFreeData(&td);
|
|
|
|
return bin;
|
|
}
|
|
|
|
static int rpmfilesPopulate(rpmfiles fi, Header h, rpmfiFlags flags)
|
|
{
|
|
headerGetFlags scareFlags = (flags & RPMFI_KEEPHEADER) ?
|
|
HEADERGET_MINMEM : HEADERGET_ALLOC;
|
|
headerGetFlags defFlags = HEADERGET_ALLOC;
|
|
struct rpmtd_s digalgo, td;
|
|
rpm_count_t totalfc = rpmfilesFC(fi);
|
|
|
|
/* XXX TODO: all these should be sanity checked, ugh... */
|
|
if (!(flags & RPMFI_NOFILEMODES))
|
|
_hgfi(h, RPMTAG_FILEMODES, &td, scareFlags, fi->fmodes);
|
|
if (!(flags & RPMFI_NOFILEFLAGS))
|
|
_hgfi(h, RPMTAG_FILEFLAGS, &td, scareFlags, fi->fflags);
|
|
if (!(flags & RPMFI_NOFILEVERIFYFLAGS))
|
|
_hgfi(h, RPMTAG_FILEVERIFYFLAGS, &td, scareFlags, fi->vflags);
|
|
if (!(flags & RPMFI_NOFILESIZES)) {
|
|
_hgfi(h, RPMTAG_FILESIZES, &td, scareFlags, fi->fsizes);
|
|
_hgfi(h, RPMTAG_LONGFILESIZES, &td, scareFlags, fi->lfsizes);
|
|
}
|
|
if (!(flags & RPMFI_NOFILECOLORS))
|
|
_hgfi(h, RPMTAG_FILECOLORS, &td, scareFlags, fi->fcolors);
|
|
|
|
if (!(flags & RPMFI_NOFILECLASS)) {
|
|
_hgfinc(h, RPMTAG_CLASSDICT, &td, scareFlags, fi->cdict);
|
|
fi->ncdict = rpmtdCount(&td);
|
|
_hgfi(h, RPMTAG_FILECLASS, &td, scareFlags, fi->fcdictx);
|
|
}
|
|
if (!(flags & RPMFI_NOFILEMIME)) {
|
|
_hgfinc(h, RPMTAG_MIMEDICT, &td, scareFlags, fi->mdict);
|
|
fi->nmdict = rpmtdCount(&td);
|
|
_hgfi(h, RPMTAG_FILEMIMEINDEX, &td, scareFlags, fi->fmdictx);
|
|
}
|
|
if (!(flags & RPMFI_NOFILEDEPS)) {
|
|
_hgfinc(h, RPMTAG_DEPENDSDICT, &td, scareFlags, fi->ddict);
|
|
fi->nddict = rpmtdCount(&td);
|
|
_hgfinc(h, RPMTAG_FILEDEPENDSX, &td, scareFlags, fi->fddictx);
|
|
_hgfinc(h, RPMTAG_FILEDEPENDSN, &td, scareFlags, fi->fddictn);
|
|
}
|
|
|
|
if (!(flags & RPMFI_NOFILESTATES))
|
|
_hgfi(h, RPMTAG_FILESTATES, &td, defFlags, fi->fstates);
|
|
|
|
if (!(flags & RPMFI_NOFILECAPS))
|
|
_hgfi(h, RPMTAG_FILECAPS, &td, defFlags, fi->fcaps);
|
|
|
|
if (!(flags & RPMFI_NOFILELINKTOS))
|
|
fi->flinks = tag2pool(fi->pool, h, RPMTAG_FILELINKTOS, totalfc);
|
|
/* FILELANGS are only interesting when installing */
|
|
if ((headerGetInstance(h) == 0) && !(flags & RPMFI_NOFILELANGS))
|
|
fi->flangs = tag2pool(fi->pool, h, RPMTAG_FILELANGS, totalfc);
|
|
|
|
/* See if the package has non-md5 file digests */
|
|
fi->digestalgo = RPM_HASH_MD5;
|
|
if (headerGet(h, RPMTAG_FILEDIGESTALGO, &digalgo, HEADERGET_MINMEM)) {
|
|
uint32_t *algo = rpmtdGetUint32(&digalgo);
|
|
/* Hmm, what to do with unknown digest algorithms? */
|
|
if (algo && rpmDigestLength(*algo) != 0) {
|
|
fi->digestalgo = *algo;
|
|
}
|
|
}
|
|
|
|
fi->digests = NULL;
|
|
/* grab hex digests from header and store in binary format */
|
|
if (!(flags & RPMFI_NOFILEDIGESTS)) {
|
|
size_t diglen = rpmDigestLength(fi->digestalgo);
|
|
fi->digests = hex2bin(h, RPMTAG_FILEDIGESTS, totalfc, diglen);
|
|
}
|
|
|
|
fi->signatures = NULL;
|
|
/* grab hex signatures from header and store in binary format */
|
|
if (!(flags & RPMFI_NOFILESIGNATURES)) {
|
|
fi->signatures = hex2binv(h, RPMTAG_FILESIGNATURES,
|
|
totalfc, &fi->signatureoffs);
|
|
}
|
|
|
|
fi->veritysigs = NULL;
|
|
if (!(flags & RPMFI_NOVERITYSIGNATURES)) {
|
|
fi->verityalgo = headerGetNumber(h, RPMTAG_VERITYSIGNATUREALGO);
|
|
fi->veritysigs = base2bin(h, RPMTAG_VERITYSIGNATURES,
|
|
totalfc, &fi->veritysiglength);
|
|
}
|
|
|
|
/* XXX TR_REMOVED doesn;t need fmtimes, frdevs, finodes */
|
|
if (!(flags & RPMFI_NOFILEMTIMES))
|
|
_hgfi(h, RPMTAG_FILEMTIMES, &td, scareFlags, fi->fmtimes);
|
|
if (!(flags & RPMFI_NOFILERDEVS))
|
|
_hgfi(h, RPMTAG_FILERDEVS, &td, scareFlags, fi->frdevs);
|
|
if (!(flags & RPMFI_NOFILEINODES)) {
|
|
_hgfi(h, RPMTAG_FILEINODES, &td, scareFlags, fi->finodes);
|
|
rpmfilesBuildNLink(fi, h);
|
|
}
|
|
if (!(flags & RPMFI_NOFILEUSER)) {
|
|
fi->fuser = tag2pool(fi->pool, h, RPMTAG_FILEUSERNAME, totalfc);
|
|
if (!fi->fuser) goto err;
|
|
}
|
|
if (!(flags & RPMFI_NOFILEGROUP)) {
|
|
fi->fgroup = tag2pool(fi->pool, h, RPMTAG_FILEGROUPNAME, totalfc);
|
|
if (!fi->fgroup) goto err;
|
|
}
|
|
/* TODO: validate and return a real error */
|
|
return 0;
|
|
err:
|
|
return -1;
|
|
}
|
|
|
|
rpmfiles rpmfilesNew(rpmstrPool pool, Header h, rpmTagVal tagN, rpmfiFlags flags)
|
|
{
|
|
rpmfiles fi = new rpmfiles_s {};
|
|
int fc;
|
|
|
|
fi->magic = RPMFIMAGIC;
|
|
fi->fiflags = flags;
|
|
/* private or shared pool? */
|
|
fi->pool = (pool != NULL) ? rpmstrPoolLink(pool) : rpmstrPoolCreate();
|
|
|
|
/*
|
|
* Grab and validate file triplet data. Headers with no files simply
|
|
* fall through here and an empty file set is returned.
|
|
*/
|
|
fc = rpmfnInit(&fi->fndata, RPMTAG_BASENAMES, h, fi->pool);
|
|
|
|
/* Broken data, bail out */
|
|
if (fc < 0)
|
|
goto err;
|
|
|
|
/* populate the rest of the stuff if we have files */
|
|
if (fc > 0) {
|
|
if (headerIsEntry(h, RPMTAG_ORIGBASENAMES)) {
|
|
/* For relocated packages, grab the original paths too */
|
|
int ofc;
|
|
fi->ofndata = new rpmfn_s {};
|
|
ofc = rpmfnInit(fi->ofndata, RPMTAG_ORIGBASENAMES, h, fi->pool);
|
|
|
|
if (ofc != 0 && ofc != fc)
|
|
goto err;
|
|
} else {
|
|
/* In the normal case, orig is the same as actual path data */
|
|
fi->ofndata = &fi->fndata;
|
|
}
|
|
|
|
if (rpmfilesPopulate(fi, h, flags))
|
|
goto err;
|
|
}
|
|
|
|
/* freeze the pool to save memory, but only if private pool */
|
|
if (fi->pool != pool)
|
|
rpmstrPoolFreeze(fi->pool, 0);
|
|
|
|
fi->h = (fi->fiflags & RPMFI_KEEPHEADER) ? headerLink(h) : NULL;
|
|
|
|
return rpmfilesLink(fi);
|
|
|
|
err:
|
|
rpmfilesFree(fi);
|
|
return NULL;
|
|
}
|
|
|
|
static int iterWriteArchiveNext(rpmfi fi);
|
|
static int iterReadArchiveNext(rpmfi fi);
|
|
static int iterReadArchiveNextContentFirst(rpmfi fi);
|
|
static int iterReadArchiveNextOmitHardlinks(rpmfi fi);
|
|
|
|
static int (*nextfuncs[])(rpmfi fi) = {
|
|
iterFwd,
|
|
iterBack,
|
|
iterWriteArchiveNext,
|
|
iterReadArchiveNext,
|
|
iterReadArchiveNextContentFirst,
|
|
iterReadArchiveNextOmitHardlinks,
|
|
iterInterval,
|
|
};
|
|
|
|
|
|
static rpmfi initIter(rpmfiles files, int itype, int link)
|
|
{
|
|
rpmfi fi = NULL;
|
|
|
|
if (files && itype>=0 && itype<=RPMFILEITERMAX) {
|
|
fi = new rpmfi_s {};
|
|
fi->i = -1;
|
|
fi->j = -1;
|
|
fi->files = link ? rpmfilesLink(files) : files;
|
|
fi->next = nextfuncs[itype];
|
|
if (itype == RPMFI_ITER_BACK) {
|
|
fi->i = rpmfilesFC(fi->files);
|
|
} else if (itype >=RPMFI_ITER_READ_ARCHIVE
|
|
&& itype <= RPMFI_ITER_READ_ARCHIVE_OMIT_HARDLINKS) {
|
|
|
|
fi->found = (uint8_t *)xcalloc(1, (rpmfiFC(fi)>>3) + 1);
|
|
}
|
|
rpmfiLink(fi);
|
|
}
|
|
return fi;
|
|
}
|
|
|
|
rpmfi rpmfilesIter(rpmfiles files, int itype)
|
|
{
|
|
/* standalone iterators need to bump our refcount */
|
|
return initIter(files, itype, 1);
|
|
}
|
|
|
|
rpmfi rpmfilesFindPrefix(rpmfiles fi, const char *pfx)
|
|
{
|
|
int l, u, c, comparison;
|
|
rpmfi iterator = NULL;
|
|
|
|
if (!fi || !pfx)
|
|
return NULL;
|
|
|
|
size_t plen = strlen(pfx);
|
|
l = 0;
|
|
u = rpmfilesFC(fi);
|
|
while (l < u) {
|
|
c = (l + u) / 2;
|
|
|
|
comparison = cmpPfx(fi, c, pfx, plen);
|
|
|
|
if (comparison < 0)
|
|
u = c;
|
|
else if (comparison > 0)
|
|
l = c + 1;
|
|
else {
|
|
if (cmpPfx(fi, l, pfx, plen))
|
|
l = c;
|
|
while (l > 0 && !cmpPfx(fi, l - 1, pfx, plen))
|
|
l--;
|
|
if ( u >= rpmfilesFC(fi) || cmpPfx(fi, u, pfx, plen))
|
|
u = c;
|
|
while (++u < rpmfilesFC(fi)) {
|
|
if (cmpPfx(fi, u, pfx, plen))
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if (l < u) {
|
|
iterator = initIter(fi, RPMFI_ITER_INTERVAL, 1);
|
|
iterator->intervalStart = l;
|
|
iterator->intervalEnd = u;
|
|
}
|
|
|
|
return iterator;
|
|
}
|
|
|
|
rpmfi rpmfiNewPool(rpmstrPool pool, Header h, rpmTagVal tagN, rpmfiFlags flags)
|
|
{
|
|
rpmfiles files = rpmfilesNew(pool, h, tagN, flags);
|
|
/* we already own rpmfiles, avoid extra refcount on it */
|
|
return initIter(files, RPMFI_ITER_FWD, 0);
|
|
}
|
|
|
|
rpmfi rpmfiNew(const rpmts ts, Header h, rpmTagVal tagN, rpmfiFlags flags)
|
|
{
|
|
return rpmfiNewPool(NULL, h, tagN, flags);
|
|
}
|
|
|
|
void rpmfilesSetFReplacedSize(rpmfiles fi, int ix, rpm_loff_t newsize)
|
|
{
|
|
if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
|
|
/* Switch over to 64 bit variant */
|
|
int fc = rpmfilesFC(fi);
|
|
if (fi->replacedSizes == NULL)
|
|
fi->replacedSizes = (rpm_loff_t *)xcalloc(fc, sizeof(*fi->replacedSizes));
|
|
fi->replacedSizes[ix] = newsize;
|
|
}
|
|
}
|
|
|
|
rpm_loff_t rpmfilesFReplacedSize(rpmfiles fi, int ix)
|
|
{
|
|
rpm_loff_t rsize = 0;
|
|
if (fi != NULL && ix >= 0 && ix < rpmfilesFC(fi)) {
|
|
if (fi->replacedSizes) {
|
|
rsize = fi->replacedSizes[ix];
|
|
}
|
|
}
|
|
return rsize;
|
|
}
|
|
|
|
void rpmfilesFpLookup(rpmfiles fi, fingerPrintCache fpc)
|
|
{
|
|
/* This can get called twice (eg yum), scratch former results and redo */
|
|
if (rpmfilesFC(fi) > 0) {
|
|
rpmfn fn = &fi->fndata;
|
|
if (fi->fps)
|
|
free(fi->fps);
|
|
fi->fps = fpLookupList(fpc, fi->pool,
|
|
fn->dnid, fn->bnid, fn->dil, fn->fc);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Generate iterator accessors function wrappers, these do nothing but
|
|
* call the corresponding rpmfiFooIndex(fi, fi->[ij])
|
|
*/
|
|
|
|
#define RPMFI_ITERFUNC(TYPE, NAME, IXV) \
|
|
TYPE rpmfi ## NAME(rpmfi fi) { return rpmfiles ## NAME(fi ? fi->files : NULL, fi ? fi->IXV : -1); }
|
|
|
|
RPMFI_ITERFUNC(rpmsid, BNId, i)
|
|
RPMFI_ITERFUNC(rpmsid, DNId, j)
|
|
RPMFI_ITERFUNC(const char *, BN, i)
|
|
RPMFI_ITERFUNC(const char *, DN, j)
|
|
RPMFI_ITERFUNC(const char *, OBN, i)
|
|
RPMFI_ITERFUNC(const char *, ODN, j)
|
|
RPMFI_ITERFUNC(const char *, FLink, i)
|
|
RPMFI_ITERFUNC(const char *, FUser, i)
|
|
RPMFI_ITERFUNC(const char *, FGroup, i)
|
|
RPMFI_ITERFUNC(const char *, FCaps, i)
|
|
RPMFI_ITERFUNC(const char *, FLangs, i)
|
|
RPMFI_ITERFUNC(const char *, FClass, i)
|
|
RPMFI_ITERFUNC(const char *, FMime, i)
|
|
RPMFI_ITERFUNC(rpmfileState, FState, i)
|
|
RPMFI_ITERFUNC(rpmfileAttrs, FFlags, i)
|
|
RPMFI_ITERFUNC(rpmVerifyAttrs, VFlags, i)
|
|
RPMFI_ITERFUNC(rpm_mode_t, FMode, i)
|
|
RPMFI_ITERFUNC(rpm_rdev_t, FRdev, i)
|
|
RPMFI_ITERFUNC(rpm_time_t, FMtime, i)
|
|
RPMFI_ITERFUNC(rpm_ino_t, FInode, i)
|
|
RPMFI_ITERFUNC(rpm_loff_t, FSize, i)
|
|
RPMFI_ITERFUNC(rpm_color_t, FColor, i)
|
|
RPMFI_ITERFUNC(uint32_t, FNlink, i)
|
|
|
|
const char * rpmfiFN(rpmfi fi)
|
|
{
|
|
const char *fn = ""; /* preserve behavior on errors */
|
|
if (fi != NULL) {
|
|
free(fi->fn);
|
|
fi->fn = rpmfilesFN(fi->files, fi->i);
|
|
if (fi->fn != NULL)
|
|
fn = fi->fn;
|
|
}
|
|
return fn;
|
|
}
|
|
|
|
const char * rpmfiOFN(rpmfi fi)
|
|
{
|
|
const char *fn = ""; /* preserve behavior on errors */
|
|
if (fi != NULL) {
|
|
free(fi->ofn);
|
|
fi->ofn = rpmfilesOFN(fi->files, fi->i);
|
|
if (fi->ofn != NULL)
|
|
fn = fi->ofn;
|
|
}
|
|
return fn;
|
|
}
|
|
|
|
const unsigned char * rpmfiFDigest(rpmfi fi, int *algo, size_t *len)
|
|
{
|
|
return rpmfilesFDigest(fi ? fi->files : NULL, fi ? fi->i : -1, algo, len);
|
|
}
|
|
|
|
const unsigned char * rpmfiFSignature(rpmfi fi, size_t *len)
|
|
{
|
|
return rpmfilesFSignature(fi ? fi->files : NULL, fi ? fi->i : -1, len);
|
|
}
|
|
|
|
const unsigned char * rpmfiVSignature(rpmfi fi, size_t *len, uint16_t *algo)
|
|
{
|
|
return rpmfilesVSignature(fi ? fi->files : NULL, fi ? fi->i : -1, len, algo);
|
|
}
|
|
|
|
uint32_t rpmfiFDepends(rpmfi fi, const uint32_t ** fddictp)
|
|
{
|
|
return rpmfilesFDepends(fi ? fi->files : NULL, fi ? fi->i : -1, fddictp);
|
|
}
|
|
|
|
int rpmfiStat(rpmfi fi, int flags, struct stat *sb)
|
|
{
|
|
int rc = -1;
|
|
if (fi != NULL) {
|
|
rc = rpmfilesStat(fi->files, fi->i, flags, sb);
|
|
/* In archives, hardlinked files are empty except for the last one */
|
|
if (rc == 0 && fi->archive && sb->st_nlink > 1) {
|
|
const int *links = NULL;
|
|
if (rpmfiFLinks(fi, &links) && links[sb->st_nlink-1] != fi->i)
|
|
sb->st_size = 0;
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
int rpmfiCompare(const rpmfi afi, const rpmfi bfi)
|
|
{
|
|
return rpmfilesCompare(afi ? afi->files : NULL, afi ? afi->i : -1,
|
|
bfi ? bfi->files : NULL, bfi ? bfi->i : -1);
|
|
}
|
|
|
|
rpmVerifyAttrs rpmfiVerify(rpmfi fi, rpmVerifyAttrs omitMask)
|
|
{
|
|
return rpmfilesVerify(fi->files, fi->i, omitMask);
|
|
}
|
|
|
|
rpmstrPool rpmfilesPool(rpmfiles fi)
|
|
{
|
|
return (fi != NULL) ? fi->pool : NULL;
|
|
}
|
|
|
|
rpmfiles rpmfiFiles(rpmfi fi)
|
|
{
|
|
return (fi != NULL) ? fi->files : NULL;
|
|
}
|
|
|
|
/******************************************************/
|
|
/*** Archive handling *********************************/
|
|
/******************************************************/
|
|
|
|
rpmfi rpmfiNewArchiveReader(FD_t fd, rpmfiles files, int itype)
|
|
{
|
|
rpmcpio_t archive = rpmcpioOpen(fd, O_RDONLY);
|
|
rpmfi fi = NULL;
|
|
if (archive && itype >= RPMFI_ITER_READ_ARCHIVE) {
|
|
fi = rpmfilesIter(files, itype);
|
|
}
|
|
if (fi) {
|
|
fi->archive = archive;
|
|
} else {
|
|
rpmcpioFree(archive);
|
|
}
|
|
return fi;
|
|
}
|
|
|
|
rpmfi rpmfiNewArchiveWriter(FD_t fd, rpmfiles files)
|
|
{
|
|
rpmcpio_t archive = rpmcpioOpen(fd, O_WRONLY);
|
|
rpmfi fi = NULL;
|
|
if (archive) {
|
|
fi = rpmfilesIter(files, RPMFI_ITER_WRITE_ARCHIVE);
|
|
}
|
|
if (fi) {
|
|
fi->archive = archive;
|
|
} else {
|
|
rpmcpioFree(archive);
|
|
}
|
|
return fi;
|
|
}
|
|
|
|
int rpmfiArchiveClose(rpmfi fi)
|
|
{
|
|
if (fi == NULL)
|
|
return -1;
|
|
int rc = rpmcpioClose(fi->archive);
|
|
return rc;
|
|
}
|
|
|
|
rpm_loff_t rpmfiArchiveTell(rpmfi fi)
|
|
{
|
|
if (fi == NULL || fi->archive == NULL)
|
|
return 0;
|
|
return (rpm_loff_t) rpmcpioTell(fi->archive);
|
|
}
|
|
|
|
static int rpmfiArchiveWriteHeader(rpmfi fi)
|
|
{
|
|
int rc;
|
|
struct stat st;
|
|
|
|
if (rpmfiStat(fi, 0, &st))
|
|
return -1;
|
|
|
|
rpmfiles files = fi->files;
|
|
|
|
if (files->lfsizes) {
|
|
return rpmcpioStrippedHeaderWrite(fi->archive, rpmfiFX(fi), st.st_size);
|
|
} else {
|
|
const char * dn = rpmfiDN(fi);
|
|
char * path = rstrscat(NULL, (dn[0] == '/' && !rpmExpandNumeric("%{_noPayloadPrefix}")) ? "." : "",
|
|
dn, rpmfiBN(fi), NULL);
|
|
rc = rpmcpioHeaderWrite(fi->archive, path, &st);
|
|
free(path);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int iterWriteArchiveNextFile(rpmfi fi)
|
|
{
|
|
rpmfiles files = rpmfiFiles(fi);
|
|
int fx = rpmfiFX(fi);
|
|
int fc = rpmfiFC(fi);
|
|
const int * hardlinks;
|
|
int numHardlinks = 0;
|
|
|
|
/* already processing hard linked files */
|
|
if (rpmfiFNlink(fi) > 1) {
|
|
/* search next hard linked file */
|
|
fi->i = -1;
|
|
for (int i=fx+1; i<fc; i++) {
|
|
/* no ghosts */
|
|
if (rpmfilesFFlags(files, i) & RPMFILE_GHOST)
|
|
continue;
|
|
numHardlinks = rpmfilesFLinks(files, i, &hardlinks);
|
|
if (numHardlinks > 1 && hardlinks[0] == i) {
|
|
rpmfiSetFX(fi, i);
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
fi->i = -1;
|
|
/* search next non hardlinked file */
|
|
for (int i=fx+1; i<fc; i++) {
|
|
/* no ghosts */
|
|
if (rpmfilesFFlags(files, i) & RPMFILE_GHOST)
|
|
continue;
|
|
if (rpmfilesFNlink(files, i) < 2) {
|
|
rpmfiSetFX(fi, i);
|
|
break;
|
|
}
|
|
}
|
|
if (rpmfiFX(fi) == -1) {
|
|
/* continue with first hard linked file */
|
|
for (int i=0; i<fc; i++) {
|
|
/* no ghosts */
|
|
if (rpmfilesFFlags(files, i) & RPMFILE_GHOST)
|
|
continue;
|
|
numHardlinks = rpmfilesFLinks(files, i, &hardlinks);
|
|
if (numHardlinks > 1) {
|
|
rpmfiSetFX(fi, i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (rpmfiFX(fi) == -1)
|
|
return -1;
|
|
|
|
/* write header(s) */
|
|
if (numHardlinks>1) {
|
|
for (int i=0; i<numHardlinks; i++) {
|
|
rpmfiSetFX(fi, hardlinks[i]);
|
|
int rc = rpmfiArchiveWriteHeader(fi);
|
|
if (rc) {
|
|
return rc;
|
|
}
|
|
}
|
|
rpmfiSetFX(fi, hardlinks[0]);
|
|
} else {
|
|
int rc = rpmfiArchiveWriteHeader(fi);
|
|
if (rc) {
|
|
return rc;
|
|
}
|
|
}
|
|
return rpmfiFX(fi);
|
|
}
|
|
|
|
static int iterWriteArchiveNext(rpmfi fi)
|
|
{
|
|
int fx;
|
|
/* loop over the files we can handle ourself */
|
|
do {
|
|
fx = iterWriteArchiveNextFile(fi);
|
|
if (S_ISLNK(rpmfiFMode(fi))) {
|
|
/* write symlink target */
|
|
const char *lnk = rpmfiFLink(fi);
|
|
size_t len = strlen(lnk);
|
|
if (rpmfiArchiveWrite(fi, lnk, len) != len) {
|
|
return RPMERR_WRITE_FAILED;
|
|
}
|
|
} else if (S_ISREG(rpmfiFMode(fi)) && rpmfiFSize(fi)) {
|
|
/* this file actually needs some content */
|
|
return fx;
|
|
}
|
|
/* go on for special files, directories and empty files */
|
|
} while (fx >= 0);
|
|
return fx;
|
|
}
|
|
|
|
size_t rpmfiArchiveWrite(rpmfi fi, const void * buf, size_t size)
|
|
{
|
|
if (fi == NULL || fi->archive == NULL)
|
|
return -1;
|
|
return rpmcpioWrite(fi->archive, buf, size);
|
|
}
|
|
|
|
int rpmfiArchiveWriteFile(rpmfi fi, FD_t fd)
|
|
{
|
|
rpm_loff_t left;
|
|
int rc = 0;
|
|
size_t len;
|
|
char buf[BUFSIZ*4];
|
|
|
|
if (fi == NULL || fi->archive == NULL || fd == NULL)
|
|
return -1;
|
|
|
|
left = rpmfiFSize(fi);
|
|
|
|
while (left) {
|
|
len = (left > sizeof(buf) ? sizeof(buf) : left);
|
|
if (Fread(buf, sizeof(*buf), len, fd) != len || Ferror(fd)) {
|
|
rc = RPMERR_READ_FAILED;
|
|
break;
|
|
}
|
|
|
|
if (rpmcpioWrite(fi->archive, buf, len) != len) {
|
|
rc = RPMERR_WRITE_FAILED;
|
|
break;
|
|
}
|
|
left -= len;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static void rpmfiSetFound(rpmfi fi, int ix)
|
|
{
|
|
fi->found[ix >> 3] |= (1 << (ix % 8));
|
|
}
|
|
|
|
static int rpmfiFound(rpmfi fi, int ix)
|
|
{
|
|
return fi->found[ix >> 3] & (1 << (ix % 8));
|
|
}
|
|
|
|
static int iterReadArchiveNext(rpmfi fi)
|
|
{
|
|
int rc;
|
|
int fx = -1;
|
|
int fc = rpmfilesFC(fi->files);
|
|
char * path = NULL;
|
|
|
|
if (fi->archive == NULL)
|
|
return -1;
|
|
|
|
/* Read next payload header. */
|
|
rc = rpmcpioHeaderRead(fi->archive, &path, &fx);
|
|
|
|
/* if archive ended, check if we found all files */
|
|
if (rc == RPMERR_ITER_END) {
|
|
for (int i=0; i<fc; i++) {
|
|
if (!rpmfiFound(fi, i) &&
|
|
!(rpmfilesFFlags(fi->files, i) & RPMFILE_GHOST)) {
|
|
rc = RPMERR_MISSING_FILE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (rc) {
|
|
return rc;
|
|
}
|
|
|
|
if (path) {
|
|
/* Regular cpio archive, identify mapping index. */
|
|
fx = rpmfilesFindOFN(fi->files, path);
|
|
free(path);
|
|
}
|
|
|
|
if (fx >= 0 && fx < fc) {
|
|
rpm_loff_t fsize = 0;
|
|
rpm_mode_t mode = rpmfilesFMode(fi->files, fx);
|
|
|
|
/* %ghost in payload, should not be there but rpm < 4.11 sometimes did this */
|
|
if (rpmfilesFFlags(fi->files, fx) & RPMFILE_GHOST)
|
|
return RPMERR_ITER_SKIP;
|
|
|
|
if (S_ISREG(mode)) {
|
|
const int * links;
|
|
uint32_t numlinks = rpmfilesFLinks(fi->files, fx, &links);
|
|
if (!(numlinks > 1 && links[numlinks-1] != fx))
|
|
fsize = rpmfilesFSize(fi->files, fx);
|
|
} else if (S_ISLNK(mode)) {
|
|
/* Skip over symlink target data in payload */
|
|
rpm_loff_t lsize = rpmfilesFSize(fi->files, fx);
|
|
char *buf = (char *)xmalloc(lsize + 1);
|
|
if (rpmcpioRead(fi->archive, buf, lsize) != lsize)
|
|
rc = RPMERR_READ_FAILED;
|
|
/* XXX should we validate the payload matches? */
|
|
free(buf);
|
|
}
|
|
rpmcpioSetExpectedFileSize(fi->archive, fsize);
|
|
rpmfiSetFound(fi, fx);
|
|
} else {
|
|
/* Mapping error */
|
|
rc = RPMERR_UNMAPPED_FILE;
|
|
}
|
|
return (rc != 0) ? rc : fx;
|
|
}
|
|
|
|
|
|
static int iterReadArchiveNextOmitHardlinks(rpmfi fi)
|
|
{
|
|
int fx;
|
|
const int * links;
|
|
int nlink;
|
|
do {
|
|
fx = iterReadArchiveNext(fi);
|
|
nlink = rpmfilesFLinks(fi->files, fx, &links);
|
|
} while (fx>=0 && nlink>1 && links[nlink-1]!=fx);
|
|
return fx;
|
|
}
|
|
|
|
static int iterReadArchiveNextContentFirst(rpmfi fi)
|
|
{
|
|
int fx = rpmfiFX(fi);
|
|
const int * links;
|
|
int nlink;
|
|
/* decide what to do on the basis of the last entry */
|
|
nlink = rpmfilesFLinks(fi->files, fx, &links);
|
|
if (nlink > 1) {
|
|
/* currently reading through hard links */
|
|
if (fx == links[nlink-1]) {
|
|
/* arrived back at last entry, read on */
|
|
fx = iterReadArchiveNext(fi);
|
|
} else {
|
|
/* next hard link */
|
|
/* scales poorly but shouldn't matter */
|
|
for (int i=0; i<nlink; i++) {
|
|
if (links[i] == fx) {
|
|
fx = links[i+1];
|
|
return fx;
|
|
}
|
|
}
|
|
/* should never happen */
|
|
return -1;
|
|
}
|
|
} else {
|
|
fx = iterReadArchiveNext(fi);
|
|
}
|
|
|
|
/* look at the new entry */
|
|
nlink = rpmfilesFLinks(fi->files, fx, &links);
|
|
/* arrived at new set of hardlinks? */
|
|
if (nlink > 1) {
|
|
/* read over all entries to the last one (containing the content) */
|
|
do {
|
|
fx = iterReadArchiveNext(fi);
|
|
} while (fx >=0 && fx != links[nlink-1]);
|
|
/* rewind to the first entry */
|
|
if (fx >= 0) {
|
|
fx = links[0];
|
|
}
|
|
}
|
|
return fx;
|
|
}
|
|
|
|
int rpmfiArchiveHasContent(rpmfi fi)
|
|
{
|
|
int res = 0;
|
|
if (fi && S_ISREG(rpmfiFMode(fi))) {
|
|
const int * links;
|
|
int nlink = rpmfiFLinks(fi, &links);
|
|
if (nlink > 1) {
|
|
if (fi->next == iterReadArchiveNext ||
|
|
fi->next == iterReadArchiveNextOmitHardlinks) {
|
|
res = rpmfiFX(fi) == links[nlink-1];
|
|
} else if (fi->next == iterReadArchiveNextContentFirst) {
|
|
res = rpmfiFX(fi) == links[0];
|
|
}
|
|
} else {
|
|
res = 1;
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
ssize_t rpmfiArchiveRead(rpmfi fi, void * buf, size_t size)
|
|
{
|
|
if (fi == NULL || fi->archive == NULL)
|
|
return -1;
|
|
return rpmcpioRead(fi->archive, buf, size);
|
|
}
|
|
|
|
int rpmfiArchiveReadToFilePsm(rpmfi fi, FD_t fd, int nodigest, rpmpsm psm)
|
|
{
|
|
if (fi == NULL || fi->archive == NULL || fd == NULL)
|
|
return -1;
|
|
|
|
rpm_loff_t left = rpmfiFSize(fi);
|
|
const unsigned char * fidigest = NULL;
|
|
int digestalgo = 0;
|
|
int rc = 0;
|
|
char buf[BUFSIZ*4];
|
|
|
|
if (!nodigest) {
|
|
digestalgo = rpmfiDigestAlgo(fi);
|
|
fidigest = rpmfilesFDigest(fi->files, rpmfiFX(fi), NULL, NULL);
|
|
fdInitDigest(fd, digestalgo, 0);
|
|
}
|
|
|
|
while (left) {
|
|
size_t len;
|
|
len = (left > sizeof(buf) ? sizeof(buf) : left);
|
|
if (rpmcpioRead(fi->archive, buf, len) != len) {
|
|
rc = RPMERR_READ_FAILED;
|
|
goto exit;
|
|
}
|
|
if ((Fwrite(buf, sizeof(*buf), len, fd) != len) || Ferror(fd)) {
|
|
rc = RPMERR_WRITE_FAILED;
|
|
goto exit;
|
|
}
|
|
|
|
rpmpsmNotify(psm, RPMCALLBACK_INST_PROGRESS, rpmfiArchiveTell(fi));
|
|
left -= len;
|
|
}
|
|
|
|
if (!nodigest) {
|
|
void * digest = NULL;
|
|
|
|
(void) Fflush(fd);
|
|
fdFiniDigest(fd, digestalgo, &digest, NULL, 0);
|
|
|
|
if (digest != NULL && fidigest != NULL) {
|
|
size_t diglen = rpmDigestLength(digestalgo);
|
|
if (memcmp(digest, fidigest, diglen)) {
|
|
rc = RPMERR_DIGEST_MISMATCH;
|
|
|
|
/* ...but in old packages, empty files have zeros for digest */
|
|
if (rpmfiFSize(fi) == 0 && digestalgo == RPM_HASH_MD5) {
|
|
uint8_t zeros[diglen];
|
|
memset(&zeros, 0, diglen);
|
|
if (memcmp(zeros, fidigest, diglen) == 0)
|
|
rc = 0;
|
|
}
|
|
}
|
|
} else {
|
|
rc = RPMERR_DIGEST_MISMATCH;
|
|
}
|
|
free(digest);
|
|
}
|
|
|
|
exit:
|
|
return rc;
|
|
}
|
|
|
|
int rpmfiArchiveReadToFile(rpmfi fi, FD_t fd, int nodigest)
|
|
{
|
|
return rpmfiArchiveReadToFilePsm(fi, fd, nodigest, NULL);
|
|
}
|
|
|
|
char * rpmfileStrerror(int rc)
|
|
{
|
|
char *msg = NULL;
|
|
const char *s = NULL;
|
|
const char *prefix = "cpio";
|
|
int myerrno = errno;
|
|
|
|
switch (rc) {
|
|
default:
|
|
break;
|
|
case RPMERR_BAD_MAGIC: s = _("Bad magic"); break;
|
|
case RPMERR_BAD_HEADER: s = _("Bad/unreadable header");break;
|
|
|
|
case RPMERR_OPEN_FAILED: s = "open"; break;
|
|
case RPMERR_CHMOD_FAILED: s = "chmod"; break;
|
|
case RPMERR_CHOWN_FAILED: s = "chown"; break;
|
|
case RPMERR_WRITE_FAILED: s = "write"; break;
|
|
case RPMERR_UTIME_FAILED: s = "utime"; break;
|
|
case RPMERR_UNLINK_FAILED: s = "unlink"; break;
|
|
case RPMERR_RENAME_FAILED: s = "rename"; break;
|
|
case RPMERR_SYMLINK_FAILED: s = "symlink"; break;
|
|
case RPMERR_STAT_FAILED: s = "stat"; break;
|
|
case RPMERR_LSTAT_FAILED: s = "lstat"; break;
|
|
case RPMERR_MKDIR_FAILED: s = "mkdir"; break;
|
|
case RPMERR_RMDIR_FAILED: s = "rmdir"; break;
|
|
case RPMERR_MKNOD_FAILED: s = "mknod"; break;
|
|
case RPMERR_MKFIFO_FAILED: s = "mkfifo"; break;
|
|
case RPMERR_LINK_FAILED: s = "link"; break;
|
|
case RPMERR_READLINK_FAILED: s = "readlink"; break;
|
|
case RPMERR_READ_FAILED: s = "read"; break;
|
|
case RPMERR_COPY_FAILED: s = "copy"; break;
|
|
case RPMERR_LSETFCON_FAILED: s = "lsetfilecon"; break;
|
|
case RPMERR_SETCAP_FAILED: s = "cap_set_file"; break;
|
|
|
|
case RPMERR_HDR_SIZE: s = _("Header size too big"); break;
|
|
case RPMERR_FILE_SIZE: s = _("File too large for archive"); break;
|
|
case RPMERR_UNKNOWN_FILETYPE: s = _("Unknown file type"); break;
|
|
case RPMERR_MISSING_FILE: s = _("Missing file(s)"); break;
|
|
case RPMERR_DIGEST_MISMATCH: s = _("Digest mismatch"); break;
|
|
case RPMERR_INTERNAL: s = _("Internal error"); break;
|
|
case RPMERR_UNMAPPED_FILE: s = _("Archive file not in header"); break;
|
|
case RPMERR_INVALID_SYMLINK: s = _("Unsafe symlink"); break;
|
|
case RPMERR_ENOTDIR: s = strerror(ENOTDIR); break;
|
|
case RPMERR_ENOENT: s = strerror(ENOENT); break;
|
|
case RPMERR_ENOTEMPTY: s = strerror(ENOTEMPTY); break;
|
|
case RPMERR_EXIST_AS_DIR:
|
|
s = _("File from package already exists as a directory in system");
|
|
break;
|
|
}
|
|
|
|
if (s != NULL) {
|
|
rasprintf(&msg, "%s: %s", prefix, s);
|
|
if ((rc <= RPMERR_CHECK_ERRNO) && myerrno) {
|
|
rstrscat(&msg, _(" failed - "), strerror(myerrno), NULL);
|
|
}
|
|
} else {
|
|
rasprintf(&msg, _("%s: (error 0x%x)"), prefix, rc);
|
|
}
|
|
|
|
return msg;
|
|
}
|