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