2064 lines
55 KiB
C
2064 lines
55 KiB
C
/** \ingroup rpmtrans
|
|
* \file lib/transaction.c
|
|
*/
|
|
|
|
#include "system.h"
|
|
|
|
#include "rpmmacro.h" /* XXX for rpmExpand */
|
|
|
|
#include "psm.h"
|
|
|
|
#include "rpmdb.h"
|
|
#include "fprint.h"
|
|
#include "rpmhash.h"
|
|
#include "misc.h" /* XXX stripTrailingChar, splitString, currentDirectory */
|
|
|
|
#ifdef DYING
|
|
/*@-redecl -exportheadervar@*/
|
|
extern const char * chroot_prefix;
|
|
/*@=redecl =exportheadervar@*/
|
|
#endif
|
|
|
|
/* XXX FIXME: merge with existing (broken?) tests in system.h */
|
|
/* portability fiddles */
|
|
#if STATFS_IN_SYS_STATVFS
|
|
# include <sys/statvfs.h>
|
|
#else
|
|
# if STATFS_IN_SYS_VFS
|
|
# include <sys/vfs.h>
|
|
# else
|
|
# if STATFS_IN_SYS_MOUNT
|
|
# include <sys/mount.h>
|
|
# else
|
|
# if STATFS_IN_SYS_STATFS
|
|
# include <sys/statfs.h>
|
|
# endif
|
|
# endif
|
|
# endif
|
|
#endif
|
|
|
|
#include "debug.h"
|
|
|
|
/*@access FD_t@*/ /* XXX compared with NULL */
|
|
/*@access Header@*/ /* XXX compared with NULL */
|
|
/*@access dbiIndexSet@*/
|
|
/*@access rpmdb@*/
|
|
/*@access rpmTransactionSet@*/
|
|
/*@access TFI_t@*/
|
|
/*@access PSM_t@*/
|
|
/*@access rpmProblemSet@*/
|
|
/*@access rpmProblem@*/
|
|
|
|
struct diskspaceInfo {
|
|
dev_t dev; /*!< file system device number. */
|
|
signed long bneeded; /*!< no. of blocks needed. */
|
|
signed long ineeded; /*!< no. of inodes needed. */
|
|
int bsize; /*!< file system block size. */
|
|
signed long bavail; /*!< no. of blocks available. */
|
|
signed long iavail; /*!< no. of inodes available. */
|
|
};
|
|
|
|
/* Adjust for root only reserved space. On linux e2fs, this is 5%. */
|
|
#define adj_fs_blocks(_nb) (((_nb) * 21) / 20)
|
|
|
|
/* argon thought a shift optimization here was a waste of time... he's
|
|
probably right :-( */
|
|
#define BLOCK_ROUND(size, block) (((size) + (block) - 1) / (block))
|
|
|
|
#define XSTRCMP(a, b) ((!(a) && !(b)) || ((a) && (b) && !strcmp((a), (b))))
|
|
|
|
static /*@null@*/ void * freeFl(rpmTransactionSet ts,
|
|
/*@only@*/ /*@null@*/ TFI_t flList)
|
|
/*@*/
|
|
{
|
|
if (flList) {
|
|
TFI_t fi;
|
|
int oc;
|
|
|
|
/*@-usereleased@*/
|
|
for (oc = 0, fi = flList; oc < ts->orderCount; oc++, fi++)
|
|
freeFi(fi);
|
|
flList = _free(flList);
|
|
/*@=usereleased@*/
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
void rpmtransSetScriptFd(rpmTransactionSet ts, FD_t fd)
|
|
{
|
|
ts->scriptFd = (fd ? fdLink(fd, "rpmtransSetScriptFd") : NULL);
|
|
}
|
|
|
|
int rpmtransGetKeys(const rpmTransactionSet ts, const void *** ep, int * nep)
|
|
{
|
|
int rc = 0;
|
|
|
|
if (nep) *nep = ts->orderCount;
|
|
if (ep) {
|
|
const void ** e;
|
|
int oc;
|
|
|
|
*ep = e = xmalloc(ts->orderCount * sizeof(*e));
|
|
for (oc = 0; oc < ts->orderCount; oc++, e++) {
|
|
switch (ts->order[oc].type) {
|
|
case TR_ADDED:
|
|
if (ts->addedPackages.list) {
|
|
struct availablePackage * alp;
|
|
alp = ts->addedPackages.list + ts->order[oc].u.addedIndex;
|
|
*e = alp->key;
|
|
break;
|
|
}
|
|
/*@fallthrough@*/
|
|
default:
|
|
case TR_REMOVED:
|
|
/*@-mods@*/ /* FIX: double indirection. */
|
|
*e = NULL;
|
|
/*@=mods@*/
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static rpmProblemSet psCreate(void)
|
|
/*@*/
|
|
{
|
|
rpmProblemSet probs;
|
|
|
|
probs = xmalloc(sizeof(*probs)); /* XXX memory leak */
|
|
probs->numProblems = probs->numProblemsAlloced = 0;
|
|
probs->probs = NULL;
|
|
|
|
return probs;
|
|
}
|
|
|
|
static void psAppend(rpmProblemSet probs, rpmProblemType type,
|
|
const struct availablePackage * alp,
|
|
const char * dn, const char *bn,
|
|
Header altH, unsigned long ulong1)
|
|
/*@modifies probs, alp @*/
|
|
{
|
|
rpmProblem p;
|
|
char *t;
|
|
|
|
if (probs->numProblems == probs->numProblemsAlloced) {
|
|
if (probs->numProblemsAlloced)
|
|
probs->numProblemsAlloced *= 2;
|
|
else
|
|
probs->numProblemsAlloced = 2;
|
|
probs->probs = xrealloc(probs->probs,
|
|
probs->numProblemsAlloced * sizeof(*probs->probs));
|
|
}
|
|
|
|
p = probs->probs + probs->numProblems++;
|
|
p->type = type;
|
|
/*@-assignexpose@*/
|
|
p->key = alp->key;
|
|
/*@=assignexpose@*/
|
|
p->ulong1 = ulong1;
|
|
p->ignoreProblem = 0;
|
|
|
|
if (dn || bn) {
|
|
p->str1 =
|
|
t = xmalloc((dn ? strlen(dn) : 0) + (bn ? strlen(bn) : 0) + 1);
|
|
if (dn) t = stpcpy(t, dn);
|
|
if (bn) t = stpcpy(t, bn);
|
|
} else
|
|
p->str1 = NULL;
|
|
|
|
if (alp) {
|
|
p->h = headerLink(alp->h);
|
|
p->pkgNEVR =
|
|
t = xmalloc(strlen(alp->name) +
|
|
strlen(alp->version) +
|
|
strlen(alp->release) + sizeof("--"));
|
|
t = stpcpy(t, alp->name);
|
|
t = stpcpy(t, "-");
|
|
t = stpcpy(t, alp->version);
|
|
t = stpcpy(t, "-");
|
|
t = stpcpy(t, alp->release);
|
|
} else {
|
|
p->h = NULL;
|
|
p->pkgNEVR = NULL;
|
|
}
|
|
|
|
if (altH) {
|
|
const char * n, * v, * r;
|
|
(void) headerNVR(altH, &n, &v, &r);
|
|
p->altNEVR =
|
|
t = xmalloc(strlen(n) + strlen(v) + strlen(r) + sizeof("--"));
|
|
t = stpcpy(t, n);
|
|
t = stpcpy(t, "-");
|
|
t = stpcpy(t, v);
|
|
t = stpcpy(t, "-");
|
|
t = stpcpy(t, r);
|
|
} else
|
|
p->altNEVR = NULL;
|
|
}
|
|
|
|
static int archOkay(Header h)
|
|
/*@*/
|
|
{
|
|
void * pkgArch;
|
|
int type, count;
|
|
|
|
/* make sure we're trying to install this on the proper architecture */
|
|
(void) headerGetEntry(h, RPMTAG_ARCH, &type, (void **) &pkgArch, &count);
|
|
#ifndef DYING
|
|
if (type == RPM_INT8_TYPE) {
|
|
int_8 * pkgArchNum;
|
|
int archNum;
|
|
|
|
/* old arch handling */
|
|
rpmGetArchInfo(NULL, &archNum);
|
|
pkgArchNum = pkgArch;
|
|
if (archNum != *pkgArchNum) {
|
|
return 0;
|
|
}
|
|
} else
|
|
#endif
|
|
{
|
|
/* new arch handling */
|
|
if (!rpmMachineScore(RPM_MACHTABLE_INSTARCH, pkgArch)) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static int osOkay(Header h)
|
|
/*@*/
|
|
{
|
|
void * pkgOs;
|
|
int type, count;
|
|
|
|
/* make sure we're trying to install this on the proper os */
|
|
(void) headerGetEntry(h, RPMTAG_OS, &type, (void **) &pkgOs, &count);
|
|
#ifndef DYING
|
|
if (type == RPM_INT8_TYPE) {
|
|
/* v1 packages and v2 packages both used improper OS numbers, so just
|
|
deal with it hope things work */
|
|
return 1;
|
|
} else
|
|
#endif
|
|
{
|
|
/* new os handling */
|
|
if (!rpmMachineScore(RPM_MACHTABLE_INSTOS, pkgOs)) {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
void rpmProblemSetFree(rpmProblemSet probs)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < probs->numProblems; i++) {
|
|
rpmProblem p = probs->probs + i;
|
|
p->h = headerFree(p->h);
|
|
p->pkgNEVR = _free(p->pkgNEVR);
|
|
p->altNEVR = _free(p->altNEVR);
|
|
p->str1 = _free(p->str1);
|
|
}
|
|
free(probs);
|
|
}
|
|
|
|
static /*@observer@*/ const char * ftstring (fileTypes 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";
|
|
}
|
|
/*@notreached@*/
|
|
}
|
|
|
|
static fileTypes whatis(uint_16 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;
|
|
}
|
|
|
|
#define alloca_strdup(_s) strcpy(alloca(strlen(_s)+1), (_s))
|
|
|
|
/**
|
|
* Relocate files in header.
|
|
* @param ts transaction set
|
|
* @param fi transaction element file info
|
|
* @param alp available package
|
|
* @param origH package header
|
|
* @param actions file dispositions
|
|
* @return header with relocated files
|
|
*/
|
|
static Header relocateFileList(const rpmTransactionSet ts, TFI_t fi,
|
|
struct availablePackage * alp,
|
|
Header origH, fileAction * actions)
|
|
/*@modifies ts, fi, alp, origH, actions @*/
|
|
{
|
|
HGE_t hge = fi->hge;
|
|
HAE_t hae = fi->hae;
|
|
HME_t hme = fi->hme;
|
|
HFD_t hfd = (fi->hfd ? fi->hfd : headerFreeData);
|
|
static int _printed = 0;
|
|
rpmProblemSet probs = ts->probs;
|
|
int allowBadRelocate = (ts->ignoreSet & RPMPROB_FILTER_FORCERELOCATE);
|
|
rpmRelocation * rawRelocations = alp->relocs;
|
|
rpmRelocation * relocations = NULL;
|
|
int numRelocations;
|
|
const char ** validRelocations;
|
|
rpmTagType validType;
|
|
int numValid;
|
|
const char ** baseNames;
|
|
const char ** dirNames;
|
|
int_32 * dirIndexes;
|
|
int_32 * newDirIndexes;
|
|
int_32 fileCount;
|
|
int_32 dirCount;
|
|
uint_32 * fFlags = NULL;
|
|
uint_16 * fModes = NULL;
|
|
char * skipDirList;
|
|
Header h;
|
|
int nrelocated = 0;
|
|
int fileAlloced = 0;
|
|
char * fn = NULL;
|
|
int haveRelocatedFile = 0;
|
|
int reldel = 0;
|
|
int len;
|
|
int i, j;
|
|
|
|
if (!hge(origH, RPMTAG_PREFIXES, &validType,
|
|
(void **) &validRelocations, &numValid))
|
|
numValid = 0;
|
|
|
|
numRelocations = 0;
|
|
if (rawRelocations)
|
|
while (rawRelocations[numRelocations].newPath ||
|
|
rawRelocations[numRelocations].oldPath)
|
|
numRelocations++;
|
|
|
|
/*
|
|
* If no relocations are specified (usually the case), then return the
|
|
* original header. If there are prefixes, however, then INSTPREFIXES
|
|
* should be added, but, since relocateFileList() can be called more
|
|
* than once for the same header, don't bother if already present.
|
|
*/
|
|
if (rawRelocations == NULL || numRelocations == 0) {
|
|
if (numValid) {
|
|
if (!headerIsEntry(origH, RPMTAG_INSTPREFIXES))
|
|
(void) hae(origH, RPMTAG_INSTPREFIXES,
|
|
validType, validRelocations, numValid);
|
|
validRelocations = hfd(validRelocations, validType);
|
|
}
|
|
return headerLink(origH);
|
|
}
|
|
|
|
#ifdef DYING
|
|
h = headerCopy(origH);
|
|
#else
|
|
h = headerLink(origH);
|
|
#endif
|
|
|
|
relocations = alloca(sizeof(*relocations) * numRelocations);
|
|
|
|
/* Build sorted relocation list from raw relocations. */
|
|
for (i = 0; i < numRelocations; i++) {
|
|
char * t;
|
|
|
|
/*
|
|
* Default relocations (oldPath == NULL) are handled in the UI,
|
|
* not rpmlib.
|
|
*/
|
|
if (rawRelocations[i].oldPath == NULL) continue; /* XXX can't happen */
|
|
|
|
/* FIXME: Trailing /'s will confuse us greatly. Internal ones will
|
|
too, but those are more trouble to fix up. :-( */
|
|
t = alloca_strdup(rawRelocations[i].oldPath);
|
|
relocations[i].oldPath = (t[0] == '/' && t[1] == '\0')
|
|
? t
|
|
: stripTrailingChar(t, '/');
|
|
|
|
/* An old path w/o a new path is valid, and indicates exclusion */
|
|
if (rawRelocations[i].newPath) {
|
|
int del;
|
|
|
|
t = alloca_strdup(rawRelocations[i].newPath);
|
|
relocations[i].newPath = (t[0] == '/' && t[1] == '\0')
|
|
? t
|
|
: stripTrailingChar(t, '/');
|
|
|
|
/*@-nullpass@*/ /* FIX: relocations[i].oldPath == NULL */
|
|
/* Verify that the relocation's old path is in the header. */
|
|
for (j = 0; j < numValid; j++)
|
|
if (!strcmp(validRelocations[j], relocations[i].oldPath))
|
|
/*@innerbreak@*/ break;
|
|
/* XXX actions check prevents problem from being appended twice. */
|
|
if (j == numValid && !allowBadRelocate && actions)
|
|
psAppend(probs, RPMPROB_BADRELOCATE, alp,
|
|
relocations[i].oldPath, NULL, NULL, 0);
|
|
del =
|
|
strlen(relocations[i].newPath) - strlen(relocations[i].oldPath);
|
|
/*@=nullpass@*/
|
|
|
|
if (del > reldel)
|
|
reldel = del;
|
|
} else {
|
|
relocations[i].newPath = NULL;
|
|
}
|
|
}
|
|
|
|
/* stupid bubble sort, but it's probably faster here */
|
|
for (i = 0; i < numRelocations; i++) {
|
|
int madeSwap;
|
|
madeSwap = 0;
|
|
for (j = 1; j < numRelocations; j++) {
|
|
rpmRelocation tmpReloc;
|
|
if (relocations[j - 1].oldPath == NULL || /* XXX can't happen */
|
|
relocations[j ].oldPath == NULL || /* XXX can't happen */
|
|
strcmp(relocations[j - 1].oldPath, relocations[j].oldPath) <= 0)
|
|
continue;
|
|
tmpReloc = relocations[j - 1];
|
|
relocations[j - 1] = relocations[j];
|
|
relocations[j] = tmpReloc;
|
|
madeSwap = 1;
|
|
}
|
|
if (!madeSwap) break;
|
|
}
|
|
|
|
if (!_printed) {
|
|
_printed = 1;
|
|
rpmMessage(RPMMESS_DEBUG, _("========== relocations\n"));
|
|
for (i = 0; i < numRelocations; i++) {
|
|
if (relocations[i].oldPath == NULL) continue; /* XXX can't happen */
|
|
if (relocations[i].newPath == NULL)
|
|
rpmMessage(RPMMESS_DEBUG, _("%5d exclude %s\n"),
|
|
i, relocations[i].oldPath);
|
|
else
|
|
rpmMessage(RPMMESS_DEBUG, _("%5d relocate %s -> %s\n"),
|
|
i, relocations[i].oldPath, relocations[i].newPath);
|
|
}
|
|
}
|
|
|
|
/* Add relocation values to the header */
|
|
if (numValid) {
|
|
const char ** actualRelocations;
|
|
int numActual;
|
|
|
|
actualRelocations = xmalloc(numValid * sizeof(*actualRelocations));
|
|
numActual = 0;
|
|
for (i = 0; i < numValid; i++) {
|
|
for (j = 0; j < numRelocations; j++) {
|
|
if (relocations[j].oldPath == NULL || /* XXX can't happen */
|
|
strcmp(validRelocations[i], relocations[j].oldPath))
|
|
continue;
|
|
/* On install, a relocate to NULL means skip the path. */
|
|
if (relocations[j].newPath) {
|
|
actualRelocations[numActual] = relocations[j].newPath;
|
|
numActual++;
|
|
}
|
|
/*@innerbreak@*/ break;
|
|
}
|
|
if (j == numRelocations) {
|
|
actualRelocations[numActual] = validRelocations[i];
|
|
numActual++;
|
|
}
|
|
}
|
|
|
|
if (numActual)
|
|
(void) hae(h, RPMTAG_INSTPREFIXES, RPM_STRING_ARRAY_TYPE,
|
|
(void **) actualRelocations, numActual);
|
|
|
|
actualRelocations = _free(actualRelocations);
|
|
validRelocations = hfd(validRelocations, validType);
|
|
}
|
|
|
|
(void) hge(h, RPMTAG_BASENAMES, NULL, (void **) &baseNames, &fileCount);
|
|
(void) hge(h, RPMTAG_DIRINDEXES, NULL, (void **) &dirIndexes, NULL);
|
|
(void) hge(h, RPMTAG_DIRNAMES, NULL, (void **) &dirNames, &dirCount);
|
|
(void) hge(h, RPMTAG_FILEFLAGS, NULL, (void **) &fFlags, NULL);
|
|
(void) hge(h, RPMTAG_FILEMODES, NULL, (void **) &fModes, NULL);
|
|
|
|
skipDirList = alloca(dirCount * sizeof(*skipDirList));
|
|
memset(skipDirList, 0, dirCount * sizeof(*skipDirList));
|
|
|
|
newDirIndexes = alloca(sizeof(*newDirIndexes) * fileCount);
|
|
memcpy(newDirIndexes, dirIndexes, sizeof(*newDirIndexes) * fileCount);
|
|
dirIndexes = newDirIndexes;
|
|
|
|
/*
|
|
* 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--) {
|
|
fileTypes ft;
|
|
int fnlen;
|
|
|
|
len = reldel +
|
|
strlen(dirNames[dirIndexes[i]]) + strlen(baseNames[i]) + 1;
|
|
if (len >= fileAlloced) {
|
|
fileAlloced = len * 2;
|
|
fn = xrealloc(fn, fileAlloced);
|
|
}
|
|
*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) continue; /* XXX can't happen */
|
|
len = strcmp(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 (strncmp(relocations[j].oldPath, fn, len))
|
|
continue;
|
|
/*@innerbreak@*/ break;
|
|
}
|
|
if (j < 0) continue;
|
|
|
|
ft = whatis(fModes[i]);
|
|
|
|
/* 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 (strncmp(fn, dirNames[j], fnlen))
|
|
continue;
|
|
/*@innerbreak@*/ break;
|
|
}
|
|
if (j < dirCount)
|
|
skipDirList[j] = 1;
|
|
}
|
|
if (actions) {
|
|
actions[i] = FA_SKIPNSTATE;
|
|
rpmMessage(RPMMESS_DEBUG, _("excluding %s %s\n"),
|
|
ftstring(ft), fn);
|
|
}
|
|
continue;
|
|
}
|
|
|
|
/* Relocation on full paths only, please. */
|
|
if (fnlen != len) continue;
|
|
|
|
if (actions)
|
|
rpmMessage(RPMMESS_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);
|
|
/*@-nullpass -nullderef@*/ /* LCL: te != NULL here. */
|
|
if (strcmp(baseNames[i], te)) /* basename changed too? */
|
|
baseNames[i] = alloca_strdup(te);
|
|
*te = '\0'; /* terminate new directory name */
|
|
/*@=nullpass =nullderef@*/
|
|
}
|
|
|
|
/* Does this directory already exist in the directory list? */
|
|
for (j = 0; j < dirCount; j++) {
|
|
if (fnlen != strlen(dirNames[j]))
|
|
continue;
|
|
if (strncmp(fn, dirNames[j], fnlen))
|
|
continue;
|
|
/*@innerbreak@*/ break;
|
|
}
|
|
|
|
if (j < dirCount) {
|
|
dirIndexes[i] = j;
|
|
continue;
|
|
}
|
|
|
|
/* Creating new paths is a pita */
|
|
if (!haveRelocatedFile) {
|
|
const char ** newDirList;
|
|
|
|
haveRelocatedFile = 1;
|
|
newDirList = xmalloc((dirCount + 1) * sizeof(*newDirList));
|
|
for (j = 0; j < dirCount; j++)
|
|
newDirList[j] = alloca_strdup(dirNames[j]);
|
|
dirNames = hfd(dirNames, RPM_STRING_ARRAY_TYPE);
|
|
dirNames = newDirList;
|
|
} else {
|
|
dirNames = xrealloc(dirNames,
|
|
sizeof(*dirNames) * (dirCount + 1));
|
|
}
|
|
|
|
dirNames[dirCount] = alloca_strdup(fn);
|
|
dirIndexes[i] = dirCount;
|
|
dirCount++;
|
|
}
|
|
|
|
/* Finish off by relocating directories. */
|
|
for (i = dirCount - 1; i >= 0; i--) {
|
|
for (j = numRelocations - 1; j >= 0; j--) {
|
|
|
|
if (relocations[j].oldPath == NULL) continue; /* XXX can't happen */
|
|
len = strcmp(relocations[j].oldPath, "/")
|
|
? strlen(relocations[j].oldPath)
|
|
: 0;
|
|
|
|
if (len && strncmp(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 */
|
|
const char * s = relocations[j].newPath;
|
|
char * t = alloca(strlen(s) + strlen(dirNames[i]) - len + 1);
|
|
|
|
(void) stpcpy( stpcpy(t, s) , dirNames[i] + len);
|
|
if (actions)
|
|
rpmMessage(RPMMESS_DEBUG,
|
|
_("relocating directory %s to %s\n"), dirNames[i], t);
|
|
dirNames[i] = t;
|
|
nrelocated++;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Save original filenames in header and replace (relocated) filenames. */
|
|
if (nrelocated) {
|
|
int c;
|
|
void * p;
|
|
rpmTagType t;
|
|
|
|
p = NULL;
|
|
(void) hge(h, RPMTAG_BASENAMES, &t, &p, &c);
|
|
(void) hae(h, RPMTAG_ORIGBASENAMES, t, p, c);
|
|
p = hfd(p, t);
|
|
|
|
p = NULL;
|
|
(void) hge(h, RPMTAG_DIRNAMES, &t, &p, &c);
|
|
(void) hae(h, RPMTAG_ORIGDIRNAMES, t, p, c);
|
|
p = hfd(p, t);
|
|
|
|
p = NULL;
|
|
(void) hge(h, RPMTAG_DIRINDEXES, &t, &p, &c);
|
|
(void) hae(h, RPMTAG_ORIGDIRINDEXES, t, p, c);
|
|
p = hfd(p, t);
|
|
|
|
(void) hme(h, RPMTAG_BASENAMES, RPM_STRING_ARRAY_TYPE,
|
|
baseNames, fileCount);
|
|
fi->bnl = hfd(fi->bnl, RPM_STRING_ARRAY_TYPE);
|
|
(void) hge(h, RPMTAG_BASENAMES, NULL, (void **) &fi->bnl, &fi->fc);
|
|
|
|
(void) hme(h, RPMTAG_DIRNAMES, RPM_STRING_ARRAY_TYPE,
|
|
dirNames, dirCount);
|
|
fi->dnl = hfd(fi->dnl, RPM_STRING_ARRAY_TYPE);
|
|
(void) hge(h, RPMTAG_DIRNAMES, NULL, (void **) &fi->dnl, &fi->dc);
|
|
|
|
(void) hme(h, RPMTAG_DIRINDEXES, RPM_INT32_TYPE,
|
|
dirIndexes, fileCount);
|
|
(void) hge(h, RPMTAG_DIRINDEXES, NULL, (void **) &fi->dil, NULL);
|
|
}
|
|
|
|
baseNames = hfd(baseNames, RPM_STRING_ARRAY_TYPE);
|
|
dirNames = hfd(dirNames, RPM_STRING_ARRAY_TYPE);
|
|
fn = _free(fn);
|
|
|
|
return h;
|
|
}
|
|
|
|
/*
|
|
* As the problem sets are generated in an order solely dependent
|
|
* on the ordering of the packages in the transaction, and that
|
|
* ordering can't be changed, the problem sets must be parallel to
|
|
* one another. Additionally, the filter set must be a subset of the
|
|
* target set, given the operations available on transaction set.
|
|
* This is good, as it lets us perform this trim in linear time, rather
|
|
* then logarithmic or quadratic.
|
|
*/
|
|
static int psTrim(rpmProblemSet filter, rpmProblemSet target)
|
|
/*@modifies target @*/
|
|
{
|
|
rpmProblem f = filter->probs;
|
|
rpmProblem t = target->probs;
|
|
int gotProblems = 0;
|
|
|
|
while ((f - filter->probs) < filter->numProblems) {
|
|
if (!f->ignoreProblem) {
|
|
f++;
|
|
continue;
|
|
}
|
|
while ((t - target->probs) < target->numProblems) {
|
|
/*@-nullpass@*/ /* LCL: looks good to me */
|
|
if (f->h == t->h && f->type == t->type && t->key == f->key &&
|
|
XSTRCMP(f->str1, t->str1))
|
|
/*@innerbreak@*/ break;
|
|
/*@=nullpass@*/
|
|
t++;
|
|
gotProblems = 1;
|
|
}
|
|
|
|
if ((t - target->probs) == target->numProblems) {
|
|
/* this can't happen ;-) lets be sane if it doesn though */
|
|
break;
|
|
}
|
|
|
|
t->ignoreProblem = f->ignoreProblem;
|
|
t++, f++;
|
|
}
|
|
|
|
if ((t - target->probs) < target->numProblems)
|
|
gotProblems = 1;
|
|
|
|
return gotProblems;
|
|
}
|
|
|
|
static int sharedCmp(const void * one, const void * two)
|
|
/*@*/
|
|
{
|
|
const struct sharedFileInfo * a = one;
|
|
const struct sharedFileInfo * b = two;
|
|
|
|
if (a->otherPkg < b->otherPkg)
|
|
return -1;
|
|
else if (a->otherPkg > b->otherPkg)
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static fileAction decideConfigFate(TFI_t dbfi, const int dbix,
|
|
TFI_t newfi, const int newix, rpmtransFlags transFlags)
|
|
/*@*/
|
|
{
|
|
const char *dn = newfi->dnl[newfi->dil[newix]];
|
|
const char *bn = newfi->bnl[newix];
|
|
char *fn = alloca(strlen(dn) + strlen(bn) + 1);
|
|
(void) stpcpy( stpcpy(fn, dn), bn);
|
|
|
|
struct stat sb;
|
|
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 (!(transFlags & RPMTRANS_FLAG_ALLFILES) &&
|
|
(newfi->fflags[newix] & RPMFILE_MISSINGOK)) {
|
|
rpmMessage(RPMMESS_DEBUG, _("%s skipped due to missingok flag\n"), fn);
|
|
return FA_SKIP;
|
|
} else {
|
|
return FA_CREATE;
|
|
}
|
|
}
|
|
|
|
fileTypes diskWhat = whatis(sb.st_mode);
|
|
fileTypes dbWhat = whatis(dbfi->fmodes[dbix]);
|
|
fileTypes newWhat = whatis(newfi->fmodes[newix]);
|
|
|
|
/* 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 (dbWhat == REG) {
|
|
/* this order matters - we'd prefer to CREATE the file if at all
|
|
possible in case something else (like the timestamp) has changed */
|
|
if (diskWhat == REG) {
|
|
char mdsum[50];
|
|
if (mdfile(fn, mdsum) != 0)
|
|
return FA_CREATE; /* assume file has been removed */
|
|
if (strcmp(dbfi->fmd5s[dbix], mdsum) == 0)
|
|
return FA_CREATE; /* unmodified config file, replace. */
|
|
}
|
|
if (newWhat == REG) {
|
|
if (strcmp(dbfi->fmd5s[dbix], newfi->fmd5s[newix]) == 0)
|
|
return FA_SKIP; /* identical file, don't bother. */
|
|
}
|
|
}
|
|
if (dbWhat == LINK) {
|
|
if (diskWhat == LINK) {
|
|
char linkto[PATH_MAX+1] = "";
|
|
if (readlink(fn, linkto, sizeof(linkto) - 1) < 0)
|
|
return FA_CREATE;
|
|
if (strcmp(dbfi->flinks[dbix], linkto) == 0)
|
|
return FA_CREATE;
|
|
}
|
|
if (newWhat == LINK) {
|
|
if (strcmp(dbfi->flinks[dbix], newfi->flinks[newix]) == 0)
|
|
return FA_SKIP;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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...
|
|
*/
|
|
if (newfi->fflags[newix] & RPMFILE_NOREPLACE)
|
|
return FA_ALTNAME;
|
|
if (diskWhat != REG && diskWhat != LINK)
|
|
return FA_CREATE;
|
|
return FA_SAVE;
|
|
}
|
|
|
|
static int configConflict(TFI_t fi, const int ix)
|
|
{
|
|
if ((fi->fflags[ix] & RPMFILE_CONFIG) == 0)
|
|
return 0;
|
|
|
|
const char *bn = fi->bnl[ix];
|
|
const char *dn = fi->dnl[fi->dil[ix]];
|
|
char *fn = alloca(strlen(dn) + strlen(bn) + 1);
|
|
(void) stpcpy( stpcpy(fn, dn), bn);
|
|
|
|
struct stat sb;
|
|
if (lstat(fn, &sb) != 0)
|
|
return 0;
|
|
|
|
fileTypes diskWhat = whatis(sb.st_mode);
|
|
fileTypes newWhat = whatis(fi->fmodes[ix]);
|
|
|
|
if (diskWhat == REG && newWhat == REG) {
|
|
char mdsum[50];
|
|
if (mdfile(fn, mdsum) != 0)
|
|
return 0; /* assume file has been removed */
|
|
if (strcmp(fi->fmd5s[ix], mdsum) == 0)
|
|
return 0; /* unmodified config file */
|
|
}
|
|
if (diskWhat == LINK && newWhat == LINK) {
|
|
char linkto[PATH_MAX+1] = "";
|
|
if (readlink(fn, linkto, sizeof(linkto) - 1) < 0)
|
|
return 0;
|
|
if (strcmp(fi->flinks[ix], linkto) == 0)
|
|
return 0;
|
|
}
|
|
|
|
if (fi->fflags[ix] & RPMFILE_NOREPLACE)
|
|
return 1;
|
|
if (diskWhat != REG && diskWhat != LINK)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
static int filecmp(const TFI_t fi1, const int ix1, const TFI_t fi2, const int ix2)
|
|
/*@*/
|
|
{
|
|
uint_16 m1 = fi1->fmodes[ix1], m2 = fi2->fmodes[ix2];
|
|
uint_32 f1 = fi1->fflags[ix1], f2 = fi2->fflags[ix2];
|
|
const char *u1 = fi1->fuser[ix1], *u2 = fi2->fuser[ix2];
|
|
const char *g1 = fi1->fgroup[ix1], *g2 = fi2->fgroup[ix2];
|
|
|
|
/* both file type and permissions must match */
|
|
if (m1 != m2)
|
|
return 1;
|
|
|
|
/* ownership must also match */
|
|
if (strcmp(u1, u2) || strcmp(g1, g2))
|
|
return 1;
|
|
|
|
if ((f1 | f2) & RPMFILE_GHOST)
|
|
/* one or both %ghost files, no extra check */
|
|
;
|
|
else if (S_ISLNK(m1)) {
|
|
/* symlinks must have the same target */
|
|
const char *l1 = fi1->flinks[ix1], *l2 = fi2->flinks[ix2];
|
|
if (strcmp(l1, l2))
|
|
return 1;
|
|
}
|
|
else if (S_ISREG(m1)) {
|
|
/* regular files must have the same md5 sum */
|
|
const char *md51 = fi1->fmd5s[ix1], *md52 = fi2->fmd5s[ix2];
|
|
if (strcmp(md51, md52))
|
|
return 1;
|
|
}
|
|
|
|
/* e.g. mtime difference is immaterial */
|
|
return 0;
|
|
}
|
|
|
|
static int handleInstInstalledFiles(const TFI_t fi, /*@null@*/ rpmdb db,
|
|
struct sharedFileInfo * shared,
|
|
int sharedCount, int reportConflicts,
|
|
rpmProblemSet probs,
|
|
rpmtransFlags transFlags)
|
|
/*@modifies fi, db, probs @*/
|
|
{
|
|
int i;
|
|
int numReplaced = 0;
|
|
|
|
rpmdbMatchIterator mi;
|
|
Header h;
|
|
TFI_t otherFi;
|
|
|
|
mi = rpmdbInitIterator(db, RPMDBI_PACKAGES, &shared->otherPkg, sizeof(shared->otherPkg));
|
|
h = rpmdbNextIterator(mi);
|
|
if (h) {
|
|
otherFi = alloca(sizeof(*otherFi));
|
|
memset(otherFi, 0, sizeof(*otherFi));
|
|
fi->type = TR_ADDED; /* load fuser and fgroup */
|
|
loadFi(h, otherFi);
|
|
}
|
|
else {
|
|
mi = rpmdbFreeIterator(mi);
|
|
return 1;
|
|
}
|
|
|
|
fi->replaced = xmalloc(sharedCount * sizeof(*fi->replaced));
|
|
|
|
for (i = 0; i < sharedCount; i++, shared++) {
|
|
int otherFileNum, fileNum;
|
|
int isCfgFile;
|
|
otherFileNum = shared->otherFileNum;
|
|
fileNum = shared->pkgFileNum;
|
|
|
|
/* XXX another tedious segfault, assume file state normal. */
|
|
if (otherFi->fstates && otherFi->fstates[otherFileNum] != RPMFILE_STATE_NORMAL)
|
|
continue;
|
|
|
|
if (XFA_SKIPPING(fi->actions[fileNum]))
|
|
continue;
|
|
|
|
isCfgFile = (otherFi->fflags[otherFileNum] | fi->fflags[fileNum]) & RPMFILE_CONFIG;
|
|
|
|
if (filecmp(otherFi, otherFileNum, fi, fileNum)) {
|
|
if (reportConflicts)
|
|
psAppend(probs, RPMPROB_FILE_CONFLICT, fi->ap,
|
|
fi->dnl[fi->dil[fileNum]], fi->bnl[fileNum], h, 0);
|
|
if (!isCfgFile) {
|
|
/*@-assignexpose@*/
|
|
if (!shared->isRemoved)
|
|
fi->replaced[numReplaced++] = *shared;
|
|
/*@=assignexpose@*/
|
|
}
|
|
}
|
|
|
|
if (isCfgFile)
|
|
fi->actions[fileNum] = decideConfigFate(otherFi, otherFileNum, fi, fileNum, transFlags);
|
|
|
|
fi->replacedSizes[fileNum] = otherFi->fsizes[otherFileNum];
|
|
}
|
|
|
|
otherFi = freeFi(otherFi);
|
|
mi = rpmdbFreeIterator(mi);
|
|
|
|
fi->replaced = xrealloc(fi->replaced,
|
|
sizeof(*fi->replaced) * (numReplaced + 1));
|
|
fi->replaced[numReplaced].otherPkg = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int handleRmvdInstalledFiles(TFI_t fi, /*@null@*/ rpmdb db,
|
|
struct sharedFileInfo * shared,
|
|
int sharedCount)
|
|
/*@modifies fi, db @*/
|
|
{
|
|
HGE_t hge = fi->hge;
|
|
Header h;
|
|
const char * otherStates;
|
|
int i;
|
|
|
|
rpmdbMatchIterator mi;
|
|
|
|
mi = rpmdbInitIterator(db, RPMDBI_PACKAGES,
|
|
&shared->otherPkg, sizeof(shared->otherPkg));
|
|
h = rpmdbNextIterator(mi);
|
|
if (h == NULL) {
|
|
mi = rpmdbFreeIterator(mi);
|
|
return 1;
|
|
}
|
|
|
|
(void) hge(h, RPMTAG_FILESTATES, NULL, (void **) &otherStates, NULL);
|
|
|
|
for (i = 0; i < sharedCount; i++, shared++) {
|
|
int otherFileNum, fileNum;
|
|
otherFileNum = shared->otherFileNum;
|
|
fileNum = shared->pkgFileNum;
|
|
|
|
if (otherStates[otherFileNum] != RPMFILE_STATE_NORMAL)
|
|
continue;
|
|
|
|
fi->actions[fileNum] = FA_SKIP;
|
|
}
|
|
|
|
mi = rpmdbFreeIterator(mi);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Update disk space needs on each partition for this package.
|
|
*/
|
|
static void handleOverlappedFiles(TFI_t fi, hashTable ht,
|
|
rpmProblemSet probs, struct diskspaceInfo * dsl)
|
|
/*@modifies fi, probs, dsl @*/
|
|
{
|
|
int i, j;
|
|
struct diskspaceInfo * ds = NULL;
|
|
uint_32 fixupSize = 0;
|
|
char * filespec = NULL;
|
|
int fileSpecAlloced = 0;
|
|
|
|
for (i = 0; i < fi->fc; i++) {
|
|
int otherPkgNum, otherFileNum;
|
|
const TFI_t * recs;
|
|
int numRecs;
|
|
|
|
if (XFA_SKIPPING(fi->actions[i]))
|
|
continue;
|
|
|
|
j = strlen(fi->dnl[fi->dil[i]]) + strlen(fi->bnl[i]) + 1;
|
|
if (j > fileSpecAlloced) {
|
|
fileSpecAlloced = j * 2;
|
|
filespec = xrealloc(filespec, fileSpecAlloced);
|
|
}
|
|
|
|
(void) stpcpy( stpcpy( filespec, fi->dnl[fi->dil[i]]), fi->bnl[i]);
|
|
|
|
if (dsl) {
|
|
ds = dsl;
|
|
while (ds->bsize && ds->dev != fi->fps[i].entry->dev) ds++;
|
|
if (!ds->bsize) ds = NULL;
|
|
fixupSize = 0;
|
|
}
|
|
|
|
/*
|
|
* Retrieve all records that apply to this file. Note that the
|
|
* file info records were built in the same order as the packages
|
|
* will be installed and removed so the records for an overlapped
|
|
* files will be sorted in exactly the same order.
|
|
*/
|
|
(void) htGetEntry(ht, &fi->fps[i], (const void ***) &recs, &numRecs, NULL);
|
|
|
|
/*
|
|
* If this package is being added, look only at other packages
|
|
* being added -- removed packages dance to a different tune.
|
|
* If both this and the other package are being added, overlapped
|
|
* files must be identical (or marked as a conflict). The
|
|
* disposition of already installed config files leads to
|
|
* a small amount of extra complexity.
|
|
*
|
|
* If this package is being removed, then there are two cases that
|
|
* need to be worried about:
|
|
* If the other package is being added, then skip any overlapped files
|
|
* so that this package removal doesn't nuke the overlapped files
|
|
* that were just installed.
|
|
* If both this and the other package are being removed, then each
|
|
* file removal from preceding packages needs to be skipped so that
|
|
* the file removal occurs only on the last occurence of an overlapped
|
|
* file in the transaction set.
|
|
*
|
|
*/
|
|
|
|
/* Locate this overlapped file in the set of added/removed packages. */
|
|
for (j = 0; j < numRecs && recs[j] != fi; j++)
|
|
{};
|
|
|
|
/* Find what the previous disposition of this file was. */
|
|
otherFileNum = -1; /* keep gcc quiet */
|
|
for (otherPkgNum = j - 1; otherPkgNum >= 0; otherPkgNum--) {
|
|
/* Added packages need only look at other added packages. */
|
|
if (fi->type == TR_ADDED && recs[otherPkgNum]->type != TR_ADDED)
|
|
continue;
|
|
|
|
/* TESTME: there are more efficient searches in the world... */
|
|
for (otherFileNum = 0; otherFileNum < recs[otherPkgNum]->fc;
|
|
otherFileNum++) {
|
|
|
|
/* If the addresses are the same, so are the values. */
|
|
if ((fi->fps + i) == (recs[otherPkgNum]->fps + otherFileNum))
|
|
/*@innerbreak@*/ break;
|
|
|
|
/* Otherwise, compare fingerprints by value. */
|
|
/*@-nullpass@*/ /* LCL: looks good to me */
|
|
if (FP_EQUAL(fi->fps[i], recs[otherPkgNum]->fps[otherFileNum]))
|
|
/*@innerbreak@*/ break;
|
|
/*@=nullpass@*/
|
|
|
|
}
|
|
/* XXX is this test still necessary? */
|
|
if (recs[otherPkgNum]->actions[otherFileNum] != FA_UNKNOWN)
|
|
/*@innerbreak@*/ break;
|
|
}
|
|
|
|
switch (fi->type) {
|
|
case TR_ADDED:
|
|
if (otherPkgNum < 0) {
|
|
/* XXX is this test still necessary? */
|
|
if (fi->actions[i] != FA_UNKNOWN)
|
|
break;
|
|
if (configConflict(fi, i)) {
|
|
/* Here is a non-overlapped pre-existing config file. */
|
|
fi->actions[i] = (fi->fflags[i] & RPMFILE_NOREPLACE)
|
|
? FA_ALTNAME : FA_BACKUP;
|
|
} else {
|
|
fi->actions[i] = FA_CREATE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
/* Mark added overlapped non-identical files as a conflict. */
|
|
if (probs && filecmp(recs[otherPkgNum], otherFileNum, fi, i)) {
|
|
psAppend(probs, RPMPROB_NEW_FILE_CONFLICT, fi->ap,
|
|
filespec, NULL, recs[otherPkgNum]->ap->h, 0);
|
|
}
|
|
|
|
/* Try to get the disk accounting correct even if a conflict. */
|
|
fixupSize = recs[otherPkgNum]->fsizes[otherFileNum];
|
|
|
|
if (configConflict(fi, i)) {
|
|
/* Here is an overlapped pre-existing config file. */
|
|
fi->actions[i] = (fi->fflags[i] & RPMFILE_NOREPLACE)
|
|
? FA_ALTNAME : FA_SKIP;
|
|
} else {
|
|
fi->actions[i] = FA_CREATE;
|
|
}
|
|
break;
|
|
case TR_REMOVED:
|
|
if (otherPkgNum >= 0) {
|
|
/* Here is an overlapped added file we don't want to nuke. */
|
|
if (recs[otherPkgNum]->actions[otherFileNum] != FA_ERASE) {
|
|
/* On updates, don't remove files. */
|
|
fi->actions[i] = FA_SKIP;
|
|
break;
|
|
}
|
|
/* Here is an overlapped removed file: skip in previous. */
|
|
recs[otherPkgNum]->actions[otherFileNum] = FA_SKIP;
|
|
}
|
|
if (XFA_SKIPPING(fi->actions[i]))
|
|
break;
|
|
if (fi->fstates && fi->fstates[i] != RPMFILE_STATE_NORMAL)
|
|
break;
|
|
if (!(S_ISREG(fi->fmodes[i]) && (fi->fflags[i] & RPMFILE_CONFIG))) {
|
|
fi->actions[i] = FA_ERASE;
|
|
break;
|
|
}
|
|
|
|
/* Here is a pre-existing modified config file that needs saving. */
|
|
{ char mdsum[50];
|
|
if (!mdfile(filespec, mdsum) && strcmp(fi->fmd5s[i], mdsum)) {
|
|
fi->actions[i] = FA_BACKUP;
|
|
break;
|
|
}
|
|
}
|
|
fi->actions[i] = FA_ERASE;
|
|
break;
|
|
}
|
|
|
|
if (ds) {
|
|
uint_32 s = BLOCK_ROUND(fi->fsizes[i], ds->bsize);
|
|
|
|
switch (fi->actions[i]) {
|
|
case FA_BACKUP:
|
|
case FA_SAVE:
|
|
case FA_ALTNAME:
|
|
ds->ineeded++;
|
|
ds->bneeded += s;
|
|
break;
|
|
|
|
/*
|
|
* FIXME: If two packages share a file (same md5sum), and
|
|
* that file is being replaced on disk, will ds->bneeded get
|
|
* decremented twice? Quite probably!
|
|
*/
|
|
case FA_CREATE:
|
|
ds->bneeded += s;
|
|
ds->bneeded -= BLOCK_ROUND(fi->replacedSizes[i], ds->bsize);
|
|
break;
|
|
|
|
case FA_ERASE:
|
|
ds->ineeded--;
|
|
ds->bneeded -= s;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
ds->bneeded -= BLOCK_ROUND(fixupSize, ds->bsize);
|
|
}
|
|
}
|
|
if (filespec) free(filespec);
|
|
}
|
|
|
|
static int ensureOlder(struct availablePackage * alp, Header old,
|
|
rpmProblemSet probs)
|
|
/*@modifies alp, probs @*/
|
|
{
|
|
int result, rc = 0;
|
|
|
|
if (old == NULL) return 1;
|
|
|
|
result = rpmVersionCompare(old, alp->h);
|
|
if (result <= 0)
|
|
rc = 0;
|
|
else if (result > 0) {
|
|
rc = 1;
|
|
psAppend(probs, RPMPROB_OLDPACKAGE, alp, NULL, NULL, old, 0);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static void skipFiles(const rpmTransactionSet ts, TFI_t fi)
|
|
/*@globals rpmGlobalMacroContext @*/
|
|
/*@modifies fi, rpmGlobalMacroContext @*/
|
|
{
|
|
int noDocs = (ts->transFlags & RPMTRANS_FLAG_NODOCS);
|
|
char ** netsharedPaths = NULL;
|
|
const char ** languages;
|
|
const char * dn, * bn;
|
|
int dnlen, bnlen, ix;
|
|
const char * s;
|
|
int * drc;
|
|
char * dff;
|
|
int i, j;
|
|
|
|
if (!noDocs)
|
|
noDocs = rpmExpandNumeric("%{?_excludedocs}");
|
|
|
|
{ const char *tmpPath = rpmExpand("%{?_netsharedpath}", NULL);
|
|
if (tmpPath && *tmpPath)
|
|
netsharedPaths = splitString(tmpPath, strlen(tmpPath), ':');
|
|
tmpPath = _free(tmpPath);
|
|
}
|
|
|
|
s = rpmExpand("%{?_install_langs}", NULL);
|
|
if (!(s && *s))
|
|
s = _free(s);
|
|
if (s) {
|
|
languages = (const char **) splitString(s, strlen(s), ':');
|
|
s = _free(s);
|
|
} else
|
|
languages = NULL;
|
|
|
|
/* Compute directory refcount, skip directory if now empty. */
|
|
drc = alloca(fi->dc * sizeof(*drc));
|
|
memset(drc, 0, fi->dc * sizeof(*drc));
|
|
dff = alloca(fi->dc * sizeof(*dff));
|
|
memset(dff, 0, fi->dc * sizeof(*dff));
|
|
|
|
for (i = 0; i < fi->fc; i++) {
|
|
char **nsp;
|
|
|
|
bn = fi->bnl[i];
|
|
bnlen = strlen(bn);
|
|
ix = fi->dil[i];
|
|
dn = fi->dnl[ix];
|
|
dnlen = strlen(dn);
|
|
|
|
drc[ix]++;
|
|
|
|
/* Don't bother with skipped files */
|
|
if (XFA_SKIPPING(fi->actions[i])) {
|
|
drc[ix]--;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Skip net shared paths.
|
|
* Net shared paths are not relative to the current root (though
|
|
* they do need to take package relocations into account).
|
|
*/
|
|
for (nsp = netsharedPaths; nsp && *nsp; nsp++) {
|
|
int len;
|
|
|
|
len = strlen(*nsp);
|
|
if (dnlen >= len) {
|
|
if (strncmp(dn, *nsp, len)) continue;
|
|
/* Only directories or complete file paths can be net shared */
|
|
if (!(dn[len] == '/' || dn[len] == '\0')) continue;
|
|
} else {
|
|
if (len < (dnlen + bnlen)) continue;
|
|
if (strncmp(dn, *nsp, dnlen)) continue;
|
|
if (strncmp(bn, (*nsp) + dnlen, bnlen)) continue;
|
|
len = dnlen + bnlen;
|
|
/* Only directories or complete file paths can be net shared */
|
|
if (!((*nsp)[len] == '/' || (*nsp)[len] == '\0')) continue;
|
|
}
|
|
|
|
/*@innerbreak@*/ break;
|
|
}
|
|
|
|
if (nsp && *nsp) {
|
|
drc[ix]--; dff[ix] = 1;
|
|
fi->actions[i] = FA_SKIPNETSHARED;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Skip i18n language specific files.
|
|
*/
|
|
if (fi->flangs && languages && *fi->flangs[i]) {
|
|
const char **lang, *l, *le;
|
|
for (lang = languages; *lang != '\0'; lang++) {
|
|
if (!strcmp(*lang, "all"))
|
|
/*@innerbreak@*/ break;
|
|
for (l = fi->flangs[i]; *l != '\0'; l = le) {
|
|
for (le = l; *le != '\0' && *le != '|'; le++)
|
|
{};
|
|
if ((le-l) > 0 && !strncmp(*lang, l, (le-l)))
|
|
/*@innerbreak@*/ break;
|
|
if (*le == '|') le++; /* skip over | */
|
|
}
|
|
if (*l != '\0')
|
|
/*@innerbreak@*/ break;
|
|
}
|
|
if (*lang == NULL) {
|
|
drc[ix]--; dff[ix] = 1;
|
|
fi->actions[i] = FA_SKIPNSTATE;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Skip documentation if requested.
|
|
*/
|
|
if (noDocs && (fi->fflags[i] & RPMFILE_DOC)) {
|
|
drc[ix]--; dff[ix] = 1;
|
|
fi->actions[i] = FA_SKIPNSTATE;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/* Skip (now empty) directories that had skipped files. */
|
|
for (j = 0; j < fi->dc; j++) {
|
|
|
|
if (drc[j]) continue; /* dir still has files. */
|
|
if (!dff[j]) continue; /* dir was not emptied here. */
|
|
|
|
/* Find parent directory and basename. */
|
|
dn = fi->dnl[j]; dnlen = strlen(dn) - 1;
|
|
bn = dn + dnlen; bnlen = 0;
|
|
while (bn > dn && bn[-1] != '/') {
|
|
bnlen++;
|
|
dnlen--;
|
|
bn--;
|
|
}
|
|
|
|
/* If explicitly included in the package, skip the directory. */
|
|
for (i = 0; i < fi->fc; i++) {
|
|
const char * dir;
|
|
|
|
if (XFA_SKIPPING(fi->actions[i]))
|
|
continue;
|
|
if (whatis(fi->fmodes[i]) != XDIR)
|
|
continue;
|
|
dir = fi->dnl[fi->dil[i]];
|
|
if (strlen(dir) != dnlen)
|
|
continue;
|
|
if (strncmp(dir, dn, dnlen))
|
|
continue;
|
|
if (strlen(fi->bnl[i]) != bnlen)
|
|
continue;
|
|
if (strncmp(fi->bnl[i], bn, bnlen))
|
|
continue;
|
|
rpmMessage(RPMMESS_DEBUG, _("excluding directory %s\n"), dn);
|
|
fi->actions[i] = FA_SKIPNSTATE;
|
|
/*@innerbreak@*/ break;
|
|
}
|
|
}
|
|
|
|
if (netsharedPaths) freeSplitString(netsharedPaths);
|
|
#ifdef DYING /* XXX freeFi will deal with this later. */
|
|
fi->flangs = _free(fi->flangs);
|
|
#endif
|
|
if (languages) freeSplitString((char **)languages);
|
|
}
|
|
|
|
/**
|
|
* Iterator across transaction elements, forward on install, backward on erase.
|
|
*/
|
|
struct tsIterator_s {
|
|
/*@kept@*/ rpmTransactionSet ts; /*!< transaction set. */
|
|
int reverse; /*!< reversed traversal? */
|
|
int ocsave; /*!< last returned iterator index. */
|
|
int oc; /*!< iterator index. */
|
|
};
|
|
|
|
/**
|
|
* Return transaction element order count.
|
|
* @param a transaction element iterator
|
|
* @return element order count
|
|
*/
|
|
static int tsGetOc(void * a)
|
|
/*@*/
|
|
{
|
|
struct tsIterator_s * iter = a;
|
|
int oc = iter->ocsave;
|
|
return oc;
|
|
}
|
|
|
|
/**
|
|
* Destroy transaction element iterator.
|
|
* @param a transaction element iterator
|
|
* @return NULL always
|
|
*/
|
|
static /*@null@*/ void * tsFreeIterator(/*@only@*//*@null@*/ const void * a)
|
|
/*@modifies a @*/
|
|
{
|
|
return _free(a);
|
|
}
|
|
|
|
/**
|
|
* Create transaction element iterator.
|
|
* @param a transaction set
|
|
* @return transaction element iterator
|
|
*/
|
|
static void * tsInitIterator(/*@kept@*/ const void * a)
|
|
/*@*/
|
|
{
|
|
rpmTransactionSet ts = (void *)a;
|
|
struct tsIterator_s * iter = NULL;
|
|
|
|
iter = xcalloc(1, sizeof(*iter));
|
|
iter->ts = ts;
|
|
iter->reverse = ((ts->transFlags & RPMTRANS_FLAG_REVERSE) ? 1 : 0);
|
|
iter->oc = (iter->reverse ? (ts->orderCount - 1) : 0);
|
|
iter->ocsave = iter->oc;
|
|
return iter;
|
|
}
|
|
|
|
/**
|
|
* Return next transaction element's file info.
|
|
* @param a file info iterator
|
|
* @return next index, -1 on termination
|
|
*/
|
|
static /*@dependent@*/ TFI_t tsNextIterator(void * a)
|
|
/*@*/
|
|
{
|
|
struct tsIterator_s * iter = a;
|
|
rpmTransactionSet ts = iter->ts;
|
|
TFI_t fi = NULL;
|
|
int oc = -1;
|
|
|
|
if (iter->reverse) {
|
|
if (iter->oc >= 0) oc = iter->oc--;
|
|
} else {
|
|
if (iter->oc < ts->orderCount) oc = iter->oc++;
|
|
}
|
|
iter->ocsave = oc;
|
|
if (oc != -1)
|
|
fi = ts->flList + oc;
|
|
return fi;
|
|
}
|
|
|
|
#define NOTIFY(_ts, _al) if ((_ts)->notify) (void) (_ts)->notify _al
|
|
|
|
static int upgrade_honor_buildtime(void)
|
|
{
|
|
static int honor_buildtime = -1;
|
|
|
|
if (honor_buildtime < 0)
|
|
honor_buildtime = rpmExpandNumeric("%{?_upgrade_honor_buildtime}%{?!_upgrade_honor_buildtime:1}") ? 1 : 0;
|
|
|
|
return honor_buildtime;
|
|
}
|
|
|
|
int rpmRunTransactions( rpmTransactionSet ts,
|
|
rpmCallbackFunction notify, rpmCallbackData notifyData,
|
|
rpmProblemSet okProbs, rpmProblemSet * newProbs,
|
|
rpmtransFlags transFlags, rpmprobFilterFlags ignoreSet)
|
|
{
|
|
int i, j;
|
|
int ourrc = 0;
|
|
struct availablePackage * alp;
|
|
unsigned int totalFileCount = 0;
|
|
hashTable ht;
|
|
TFI_t fi;
|
|
struct diskspaceInfo * dip;
|
|
struct sharedFileInfo * shared, * sharedList;
|
|
int numShared;
|
|
int nexti;
|
|
int lastFailed;
|
|
fingerPrintCache fpc;
|
|
struct psm_s psmbuf;
|
|
PSM_t psm = &psmbuf;
|
|
void * tsi;
|
|
|
|
/* FIXME: what if the same package is included in ts twice? */
|
|
|
|
ts->transFlags = transFlags;
|
|
if (ts->transFlags & RPMTRANS_FLAG_NOSCRIPTS)
|
|
ts->transFlags |= (_noTransScripts | _noTransTriggers);
|
|
if (ts->transFlags & RPMTRANS_FLAG_NOTRIGGERS)
|
|
ts->transFlags |= _noTransTriggers;
|
|
|
|
if (ts->transFlags & RPMTRANS_FLAG_JUSTDB)
|
|
ts->transFlags |= (_noTransScripts | _noTransTriggers);
|
|
|
|
/* if SELinux isn't enabled or init fails, don't bother... */
|
|
if (!ts->selinuxEnabled)
|
|
ts->transFlags |= RPMTRANS_FLAG_NOCONTEXTS;
|
|
|
|
if (!(ts->transFlags & RPMTRANS_FLAG_NOCONTEXTS)) {
|
|
const char *fn = rpmGetPath("%{?_install_file_context_path}", NULL);
|
|
if (matchpathcon_init(fn) == -1)
|
|
ts->transFlags |= RPMTRANS_FLAG_NOCONTEXTS;
|
|
fn = _free(fn);
|
|
}
|
|
|
|
ts->notify = notify;
|
|
ts->notifyData = notifyData;
|
|
/*@-assignexpose@*/
|
|
ts->probs = *newProbs = psCreate();
|
|
/*@=assignexpose@*/
|
|
ts->ignoreSet = ignoreSet;
|
|
ts->currDir = _free(ts->currDir);
|
|
ts->currDir = currentDirectory();
|
|
ts->chrootDone = 0;
|
|
if (ts->rpmdb) ts->rpmdb->db_chrootDone = 0;
|
|
ts->id = (int_32) time(NULL);
|
|
|
|
memset(psm, 0, sizeof(*psm));
|
|
/*@-assignexpose@*/
|
|
psm->ts = ts;
|
|
/*@=assignexpose@*/
|
|
|
|
/* Get available space on mounted file systems. */
|
|
if (!(ts->ignoreSet & RPMPROB_FILTER_DISKSPACE) &&
|
|
!rpmGetFilesystemList(&ts->filesystems, &ts->filesystemCount)) {
|
|
struct stat sb;
|
|
|
|
ts->di = _free(ts->di);
|
|
dip = ts->di = xcalloc(sizeof(*ts->di), ts->filesystemCount + 1);
|
|
|
|
for (i = 0; (i < ts->filesystemCount) && dip; i++) {
|
|
#if STATFS_IN_SYS_STATVFS
|
|
struct statvfs sfb;
|
|
memset(&sfb, 0, sizeof(sfb));
|
|
if (!statvfs(ts->filesystems[i], &sfb))
|
|
#else
|
|
struct statfs sfb;
|
|
# if STAT_STATFS4
|
|
/* This platform has the 4-argument version of the statfs call. The last two
|
|
* should be the size of struct statfs and 0, respectively. The 0 is the
|
|
* filesystem type, and is always 0 when statfs is called on a mounted
|
|
* filesystem, as we're doing.
|
|
*/
|
|
memset(&sfb, 0, sizeof(sfb));
|
|
if (!statfs(ts->filesystems[i], &sfb, sizeof(sfb), 0))
|
|
# else
|
|
memset(&sfb, 0, sizeof(sfb));
|
|
if (!statfs(ts->filesystems[i], &sfb))
|
|
# endif
|
|
#endif
|
|
{
|
|
ts->di[i].bsize = sfb.f_bsize;
|
|
ts->di[i].bneeded = 0;
|
|
ts->di[i].ineeded = 0;
|
|
#ifdef STATFS_HAS_F_BAVAIL
|
|
ts->di[i].bavail = sfb.f_bavail;
|
|
#else
|
|
/* FIXME: the statfs struct doesn't have a member to tell how many blocks are
|
|
* available for non-superusers. f_blocks - f_bfree is probably too big, but
|
|
* it's about all we can do.
|
|
*/
|
|
ts->di[i].bavail = sfb.f_blocks - sfb.f_bfree;
|
|
#endif
|
|
/* XXX Avoid FAT and other file systems that have not inodes. */
|
|
ts->di[i].iavail = !(sfb.f_ffree == 0 && sfb.f_files == 0)
|
|
? sfb.f_ffree : -1;
|
|
|
|
}
|
|
if (!ts->di[i].bsize)
|
|
ts->di[i].bsize = 1024;
|
|
if (!stat(ts->filesystems[i], &sb))
|
|
ts->di[i].dev = sb.st_dev;
|
|
}
|
|
|
|
if (dip) ts->di[i].bsize = 0;
|
|
}
|
|
|
|
/* ===============================================
|
|
* For packages being installed:
|
|
* - verify package arch/os.
|
|
* - verify package epoch:version-release is newer.
|
|
* - count files.
|
|
* For packages being removed:
|
|
* - count files.
|
|
*/
|
|
/* The ordering doesn't matter here */
|
|
if (ts->addedPackages.list != NULL)
|
|
for (alp = ts->addedPackages.list;
|
|
(alp - ts->addedPackages.list) < ts->addedPackages.size;
|
|
alp++)
|
|
{
|
|
if (!archOkay(alp->h) && !(ts->ignoreSet & RPMPROB_FILTER_IGNOREARCH))
|
|
psAppend(ts->probs, RPMPROB_BADARCH, alp, NULL, NULL, NULL, 0);
|
|
|
|
if (!osOkay(alp->h) && !(ts->ignoreSet & RPMPROB_FILTER_IGNOREOS))
|
|
psAppend(ts->probs, RPMPROB_BADOS, alp, NULL, NULL, NULL, 0);
|
|
|
|
if (!(ts->ignoreSet & RPMPROB_FILTER_OLDPACKAGE)) {
|
|
rpmdbMatchIterator mi;
|
|
Header oldH;
|
|
mi = rpmdbInitIterator(ts->rpmdb, RPMTAG_NAME, alp->name, 0);
|
|
while ((oldH = rpmdbNextIterator(mi)) != NULL)
|
|
(void) ensureOlder(alp, oldH, ts->probs);
|
|
mi = rpmdbFreeIterator(mi);
|
|
}
|
|
|
|
if (!(ts->ignoreSet & RPMPROB_FILTER_REPLACEPKG)) {
|
|
rpmdbMatchIterator mi;
|
|
char b[80];
|
|
|
|
mi = rpmdbInitIterator(ts->rpmdb, RPMTAG_NAME, alp->name, 0);
|
|
if (alp->epoch) {
|
|
sprintf(b, "%u", *alp->epoch);
|
|
(void) rpmdbSetIteratorRE(mi, RPMTAG_EPOCH,
|
|
RPMMIRE_DEFAULT, b);
|
|
}
|
|
(void) rpmdbSetIteratorRE(mi, RPMTAG_VERSION,
|
|
RPMMIRE_DEFAULT, alp->version);
|
|
(void) rpmdbSetIteratorRE(mi, RPMTAG_RELEASE,
|
|
RPMMIRE_DEFAULT, alp->release);
|
|
if (alp->disttag) {
|
|
(void) rpmdbSetIteratorRE(mi, RPMTAG_DISTTAG,
|
|
RPMMIRE_DEFAULT, alp->disttag);
|
|
}
|
|
if (alp->buildtime && upgrade_honor_buildtime()) {
|
|
sprintf(b, "%u", *alp->buildtime);
|
|
(void) rpmdbSetIteratorRE(mi, RPMTAG_BUILDTIME,
|
|
RPMMIRE_DEFAULT, b);
|
|
}
|
|
|
|
while (rpmdbNextIterator(mi) != NULL) {
|
|
psAppend(ts->probs, RPMPROB_PKG_INSTALLED, alp,
|
|
NULL, NULL, NULL, 0);
|
|
/*@innerbreak@*/ break;
|
|
}
|
|
mi = rpmdbFreeIterator(mi);
|
|
}
|
|
|
|
totalFileCount += alp->filesCount;
|
|
|
|
}
|
|
|
|
if (ts->erasedPackages.list != NULL)
|
|
for (alp = ts->erasedPackages.list;
|
|
(alp - ts->erasedPackages.list) < ts->erasedPackages.size;
|
|
alp++)
|
|
{
|
|
totalFileCount += alp->filesCount;
|
|
}
|
|
|
|
/* ===============================================
|
|
* Initialize file list:
|
|
*/
|
|
ts->flEntries = ts->addedPackages.size + ts->numRemovedPackages;
|
|
ts->flList = xcalloc(ts->flEntries, sizeof(*ts->flList));
|
|
|
|
/*
|
|
* FIXME?: we'd be better off assembling one very large file list and
|
|
* calling fpLookupList only once. I'm not sure that the speedup is
|
|
* worth the trouble though.
|
|
*/
|
|
tsi = tsInitIterator(ts);
|
|
while ((fi = tsNextIterator(tsi)) != NULL) {
|
|
int oc = tsGetOc(tsi);
|
|
fi->magic = TFIMAGIC;
|
|
fi->type = ts->order[oc].type;
|
|
switch (fi->type) {
|
|
case TR_ADDED:
|
|
fi->ap = ts->addedPackages.list + ts->order[oc].u.addedIndex;
|
|
fi->record = 0;
|
|
loadFi(fi->ap->h, fi);
|
|
if (fi->fc == 0)
|
|
continue;
|
|
|
|
{ Header foo = relocateFileList(ts, fi, fi->ap, fi->h, fi->actions);
|
|
foo = headerFree(foo);
|
|
}
|
|
|
|
/* Skip netshared paths, not our i18n files, and excluded docs */
|
|
skipFiles(ts, fi);
|
|
break;
|
|
case TR_REMOVED:
|
|
fi->ap = ts->erasedPackages.list + ts->order[oc].u.removed.erasedIndex;
|
|
fi->record = ts->order[oc].u.removed.dboffset;
|
|
loadFi(fi->ap->h, fi);
|
|
break;
|
|
}
|
|
|
|
if (fi->fc)
|
|
fi->fps = xmalloc(fi->fc * sizeof(*fi->fps));
|
|
}
|
|
tsi = tsFreeIterator(tsi);
|
|
|
|
if (!ts->chrootDone) {
|
|
(void) chdir("/");
|
|
/*@-unrecog -superuser @*/
|
|
(void) chroot(ts->rootDir);
|
|
/*@=unrecog =superuser @*/
|
|
ts->chrootDone = 1;
|
|
if (ts->rpmdb) ts->rpmdb->db_chrootDone = 1;
|
|
#ifdef DYING
|
|
/*@-onlytrans@*/
|
|
chroot_prefix = ts->rootDir;
|
|
/*@=onlytrans@*/
|
|
#endif
|
|
}
|
|
|
|
ht = htCreate(totalFileCount, fpHashFunction, fpEqual);
|
|
/* XXX fpCache size = directories + space for rpmdbFindFpList */
|
|
fpc = fpCacheCreate(totalFileCount / 2 + 4096);
|
|
|
|
/* ===============================================
|
|
* Add fingerprint for each file not skipped.
|
|
*/
|
|
tsi = tsInitIterator(ts);
|
|
while ((fi = tsNextIterator(tsi)) != NULL) {
|
|
fpLookupList(fpc, fi->dnl, fi->bnl, fi->dil, fi->fc, fi->fps);
|
|
for (i = 0; i < fi->fc; i++) {
|
|
if (XFA_SKIPPING(fi->actions[i]))
|
|
continue;
|
|
/*@-dependenttrans@*/
|
|
htAddEntry(ht, fi->fps + i, fi);
|
|
/*@=dependenttrans@*/
|
|
}
|
|
}
|
|
tsi = tsFreeIterator(tsi);
|
|
|
|
/*@-moduncon@*/
|
|
NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_START, 6, ts->flEntries,
|
|
NULL, ts->notifyData));
|
|
/*@=moduncon@*/
|
|
|
|
/* ===============================================
|
|
* Compute file disposition for each package in transaction set.
|
|
*/
|
|
tsi = tsInitIterator(ts);
|
|
while ((fi = tsNextIterator(tsi)) != NULL) {
|
|
dbiIndexSet * matches;
|
|
int knownBad;
|
|
|
|
/*@-moduncon@*/
|
|
NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_PROGRESS, (fi - ts->flList),
|
|
ts->flEntries, NULL, ts->notifyData));
|
|
/*@=moduncon@*/
|
|
|
|
if (fi->fc == 0) continue;
|
|
|
|
/* Extract file info for all files in this package from the database. */
|
|
matches = xcalloc(sizeof(*matches), fi->fc);
|
|
if (rpmdbFindFpList(ts->rpmdb, fi->fps, matches, fi->fc, fpc))
|
|
return 1; /* XXX WTFO? */
|
|
|
|
numShared = 0;
|
|
for (i = 0; i < fi->fc; i++)
|
|
numShared += dbiIndexSetCount(matches[i]);
|
|
|
|
/* Build sorted file info list for this package. */
|
|
shared = sharedList = xmalloc((numShared + 1) * sizeof(*sharedList));
|
|
for (i = 0; i < fi->fc; i++) {
|
|
/*
|
|
* Take care not to mark files as replaced in packages that will
|
|
* have been removed before we will get here.
|
|
*/
|
|
for (j = 0; j < dbiIndexSetCount(matches[i]); j++) {
|
|
int k, ro;
|
|
ro = dbiIndexRecordOffset(matches[i], j);
|
|
knownBad = 0;
|
|
for (k = 0; ro != knownBad && k < ts->orderCount; k++) {
|
|
switch (ts->order[k].type) {
|
|
case TR_REMOVED:
|
|
if (ts->order[k].u.removed.dboffset == ro)
|
|
knownBad = ro;
|
|
break;
|
|
case TR_ADDED:
|
|
break;
|
|
}
|
|
}
|
|
|
|
shared->pkgFileNum = i;
|
|
shared->otherPkg = dbiIndexRecordOffset(matches[i], j);
|
|
shared->otherFileNum = dbiIndexRecordFileNumber(matches[i], j);
|
|
shared->isRemoved = (knownBad == ro);
|
|
shared++;
|
|
}
|
|
matches[i] = dbiFreeIndexSet(matches[i]);
|
|
}
|
|
numShared = shared - sharedList;
|
|
shared->otherPkg = -1;
|
|
matches = _free(matches);
|
|
|
|
/* Sort file info by other package index (otherPkg) */
|
|
qsort(sharedList, numShared, sizeof(*shared), sharedCmp);
|
|
|
|
/* For all files from this package that are in the database ... */
|
|
for (i = 0; i < numShared; i = nexti) {
|
|
int beingRemoved;
|
|
|
|
shared = sharedList + i;
|
|
|
|
/* Find the end of the files in the other package. */
|
|
for (nexti = i + 1; nexti < numShared; nexti++) {
|
|
if (sharedList[nexti].otherPkg != shared->otherPkg)
|
|
/*@innerbreak@*/ break;
|
|
}
|
|
|
|
/* Is this file from a package being removed? */
|
|
beingRemoved = 0;
|
|
for (j = 0; j < ts->numRemovedPackages; j++) {
|
|
if (ts->removedPackages[j] != shared->otherPkg)
|
|
continue;
|
|
beingRemoved = 1;
|
|
/*@innerbreak@*/ break;
|
|
}
|
|
|
|
/* Determine the fate of each file. */
|
|
switch (fi->type) {
|
|
case TR_ADDED:
|
|
(void) handleInstInstalledFiles(fi, ts->rpmdb, shared, nexti - i,
|
|
!(beingRemoved || (ts->ignoreSet & RPMPROB_FILTER_REPLACEOLDFILES)),
|
|
ts->probs, ts->transFlags);
|
|
break;
|
|
case TR_REMOVED:
|
|
if (!beingRemoved)
|
|
(void) handleRmvdInstalledFiles(fi, ts->rpmdb, shared, nexti - i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
free(sharedList);
|
|
|
|
/* Update disk space needs on each partition for this package. */
|
|
handleOverlappedFiles(fi, ht,
|
|
((ts->ignoreSet & RPMPROB_FILTER_REPLACENEWFILES)
|
|
? NULL : ts->probs), ts->di);
|
|
|
|
/* Check added package has sufficient space on each partition used. */
|
|
switch (fi->type) {
|
|
case TR_ADDED:
|
|
if (!(ts->di && fi->fc))
|
|
break;
|
|
for (i = 0; i < ts->filesystemCount; i++) {
|
|
|
|
dip = ts->di + i;
|
|
|
|
/* XXX Avoid FAT and other file systems that have not inodes. */
|
|
if (dip->iavail <= 0)
|
|
continue;
|
|
|
|
if (adj_fs_blocks(dip->bneeded) > dip->bavail)
|
|
psAppend(ts->probs, RPMPROB_DISKSPACE, fi->ap,
|
|
ts->filesystems[i], NULL, NULL,
|
|
(adj_fs_blocks(dip->bneeded) - dip->bavail) * dip->bsize);
|
|
|
|
if (adj_fs_blocks(dip->ineeded) > dip->iavail)
|
|
psAppend(ts->probs, RPMPROB_DISKNODES, fi->ap,
|
|
ts->filesystems[i], NULL, NULL,
|
|
(adj_fs_blocks(dip->ineeded) - dip->iavail));
|
|
}
|
|
break;
|
|
case TR_REMOVED:
|
|
break;
|
|
}
|
|
}
|
|
tsi = tsFreeIterator(tsi);
|
|
|
|
if (ts->chrootDone) {
|
|
/*@-unrecog -superuser @*/
|
|
(void) chroot(".");
|
|
/*@=unrecog =superuser @*/
|
|
ts->chrootDone = 0;
|
|
if (ts->rpmdb) ts->rpmdb->db_chrootDone = 0;
|
|
#ifdef DYING
|
|
chroot_prefix = NULL;
|
|
#endif
|
|
(void) chdir(ts->currDir);
|
|
}
|
|
|
|
/*@-moduncon@*/
|
|
NOTIFY(ts, (NULL, RPMCALLBACK_TRANS_STOP, 6, ts->flEntries,
|
|
NULL, ts->notifyData));
|
|
/*@=moduncon@*/
|
|
|
|
/* ===============================================
|
|
* Free unused memory as soon as possible.
|
|
*/
|
|
|
|
tsi = tsInitIterator(ts);
|
|
while ((fi = tsNextIterator(tsi)) != NULL) {
|
|
psm->fi = fi;
|
|
if (fi->fc == 0)
|
|
continue;
|
|
fi->fps = _free(fi->fps);
|
|
}
|
|
tsi = tsFreeIterator(tsi);
|
|
|
|
fpc = fpCacheFree(fpc);
|
|
ht = htFree(ht, NULL, NULL);
|
|
|
|
/* ===============================================
|
|
* If unfiltered problems exist, free memory and return.
|
|
*/
|
|
if ((ts->transFlags & RPMTRANS_FLAG_BUILD_PROBS) ||
|
|
(ts->probs->numProblems && (!okProbs || psTrim(okProbs, ts->probs))))
|
|
{
|
|
*newProbs = ts->probs;
|
|
|
|
ts->flList = freeFl(ts, ts->flList);
|
|
ts->flEntries = 0;
|
|
/*@-nullstate@*/
|
|
return ts->orderCount;
|
|
/*@=nullstate@*/
|
|
}
|
|
|
|
/* ===============================================
|
|
* Install and remove packages.
|
|
*/
|
|
|
|
lastFailed = -2; /* erased packages have -1 */
|
|
tsi = tsInitIterator(ts);
|
|
while ((fi = tsNextIterator(tsi)) != NULL) {
|
|
Header h;
|
|
int oc = tsGetOc(tsi);
|
|
int gotfd = 0;
|
|
psm->fi = fi;
|
|
switch (fi->type)
|
|
{
|
|
case TR_ADDED:
|
|
alp = ts->addedPackages.list + ts->order[oc].u.addedIndex;
|
|
assert(alp == fi->ap);
|
|
i = alp - ts->addedPackages.list;
|
|
|
|
h = headerLink(fi->h);
|
|
if (alp->fd == NULL) {
|
|
alp->fd = ts->notify(fi->h, RPMCALLBACK_INST_OPEN_FILE, 0, 0,
|
|
alp->key, ts->notifyData);
|
|
if (alp->fd) {
|
|
rpmRC rpmrc;
|
|
|
|
h = headerFree(h);
|
|
|
|
/*@-mustmod@*/ /* LCL: segfault */
|
|
rpmrc = rpmReadPackageHeader(alp->fd, &h, NULL, NULL, NULL);
|
|
/*@=mustmod@*/
|
|
if (!(rpmrc == RPMRC_OK || rpmrc == RPMRC_BADSIZE)) {
|
|
(void)ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE,
|
|
0, 0, alp->key, ts->notifyData);
|
|
alp->fd = NULL;
|
|
ourrc++;
|
|
} else {
|
|
Header foo = relocateFileList(ts, fi, alp, h, NULL);
|
|
h = headerFree(h);
|
|
h = headerLink(foo);
|
|
foo = headerFree(foo);
|
|
}
|
|
if (alp->fd) gotfd = 1;
|
|
}
|
|
}
|
|
|
|
if (alp->fd) {
|
|
Header hsave = NULL;
|
|
|
|
if (fi->h) {
|
|
hsave = headerLink(fi->h);
|
|
fi->h = headerFree(fi->h);
|
|
}
|
|
fi->h = headerLink(h);
|
|
|
|
assert(alp == fi->ap);
|
|
if (psmStage(psm, PSM_PKGINSTALL)) {
|
|
ourrc++;
|
|
lastFailed = i;
|
|
}
|
|
else {
|
|
psmTriggerAdded(psm);
|
|
}
|
|
fi->h = headerFree(fi->h);
|
|
if (hsave) {
|
|
fi->h = headerLink(hsave);
|
|
hsave = headerFree(hsave);
|
|
}
|
|
} else {
|
|
ourrc++;
|
|
lastFailed = i;
|
|
}
|
|
|
|
h = headerFree(h);
|
|
|
|
if (gotfd) {
|
|
(void)ts->notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE, 0, 0,
|
|
alp->key, ts->notifyData);
|
|
alp->fd = NULL;
|
|
}
|
|
break;
|
|
case TR_REMOVED:
|
|
/* If install failed, then we shouldn't erase. */
|
|
if (ts->order[oc].u.removed.dependsOnIndex == lastFailed)
|
|
break;
|
|
|
|
if (psmStage(psm, PSM_PKGERASE))
|
|
ourrc++;
|
|
else {
|
|
psmTriggerRemoved(psm);
|
|
}
|
|
|
|
break;
|
|
}
|
|
(void) rpmdbSync(ts->rpmdb);
|
|
}
|
|
|
|
if (ourrc == 0)
|
|
psmTriggerPosttrans(psm);
|
|
|
|
tsi = tsFreeIterator(tsi);
|
|
|
|
ts->flList = freeFl(ts, ts->flList);
|
|
ts->flEntries = 0;
|
|
|
|
if (!(ts->transFlags & RPMTRANS_FLAG_NOCONTEXTS))
|
|
matchpathcon_fini();
|
|
|
|
/*@-nullstate@*/
|
|
if (ourrc)
|
|
return -1;
|
|
else
|
|
return 0;
|
|
/*@=nullstate@*/
|
|
}
|