1660 lines
45 KiB
C
1660 lines
45 KiB
C
#include "system.h"
|
|
|
|
#include <rpmlib.h>
|
|
#include <rpmmacro.h> /* XXX for rpmExpand */
|
|
|
|
#include "depends.h"
|
|
#include "fprint.h"
|
|
#include "hash.h"
|
|
#include "install.h"
|
|
#include "lookup.h"
|
|
#include "md5.h"
|
|
#include "misc.h"
|
|
#include "rpmdb.h"
|
|
|
|
/* 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
|
|
|
|
/*@access rpmdb@*/
|
|
/*@access rpmTransactionSet@*/
|
|
/*@access rpmProblemSet@*/
|
|
/*@access rpmProblem@*/
|
|
|
|
typedef struct transactionFileInfo {
|
|
/* for all packages */
|
|
enum rpmTransactionType type;
|
|
enum fileActions * actions;
|
|
fingerPrint * fps;
|
|
uint_32 * fflags, * fsizes;
|
|
const char ** bnl; /* base names */
|
|
const char ** dnl; /* dir names */
|
|
const int * dil; /* dir index list */
|
|
char ** fmd5s;
|
|
uint_16 * fmodes;
|
|
Header h;
|
|
int fc;
|
|
char * fstates;
|
|
/* these are for TR_ADDED packages */
|
|
char ** flinks;
|
|
struct availablePackage * ap;
|
|
struct sharedFileInfo * replaced;
|
|
uint_32 * replacedSizes;
|
|
/* for TR_REMOVED packages */
|
|
unsigned int record;
|
|
} TFI_t;
|
|
|
|
struct diskspaceInfo {
|
|
dev_t dev;
|
|
signed long needed; /* in blocks */
|
|
int block;
|
|
signed long avail;
|
|
};
|
|
|
|
/* 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))))
|
|
|
|
#define XFA_SKIPPING(_a) \
|
|
((_a) == FA_SKIP || (_a) == FA_SKIPNSTATE || (_a) == FA_SKIPNETSHARED)
|
|
|
|
static void freeFi(TFI_t *fi)
|
|
{
|
|
if (fi->h) {
|
|
headerFree(fi->h); fi->h = NULL;
|
|
}
|
|
if (fi->actions) {
|
|
free(fi->actions); fi->actions = NULL;
|
|
}
|
|
if (fi->replacedSizes) {
|
|
free(fi->replacedSizes); fi->replacedSizes = NULL;
|
|
}
|
|
if (fi->replaced) {
|
|
free(fi->replaced); fi->replaced = NULL;
|
|
}
|
|
if (fi->bnl) {
|
|
free(fi->bnl); fi->bnl = NULL;
|
|
free(fi->dnl); fi->dnl = NULL;
|
|
fi->dil = NULL;
|
|
}
|
|
if (fi->flinks) {
|
|
free(fi->flinks); fi->flinks = NULL;
|
|
}
|
|
if (fi->fmd5s) {
|
|
free(fi->fmd5s); fi->fmd5s = NULL;
|
|
}
|
|
|
|
switch (fi->type) {
|
|
case TR_REMOVED:
|
|
if (fi->fsizes) {
|
|
free(fi->fsizes); fi->fsizes = NULL;
|
|
}
|
|
if (fi->fflags) {
|
|
free(fi->fflags); fi->fflags = NULL;
|
|
}
|
|
if (fi->fmodes) {
|
|
free(fi->fmodes); fi->fmodes = NULL;
|
|
}
|
|
if (fi->fstates) {
|
|
free(fi->fstates); fi->fstates = NULL;
|
|
}
|
|
break;
|
|
case TR_ADDED:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void freeFl(rpmTransactionSet ts, TFI_t *flList)
|
|
{
|
|
TFI_t *fi;
|
|
int oc;
|
|
|
|
for (oc = 0, fi = flList; oc < ts->orderCount; oc++, fi++) {
|
|
freeFi(fi);
|
|
}
|
|
}
|
|
|
|
void rpmtransSetScriptFd(rpmTransactionSet ts, FD_t fd)
|
|
{
|
|
ts->scriptFd = fd;
|
|
}
|
|
|
|
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 void * key, Header h, const char * str1,
|
|
Header altH, unsigned long ulong1)
|
|
{
|
|
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));
|
|
}
|
|
|
|
probs->probs[probs->numProblems].type = type;
|
|
probs->probs[probs->numProblems].key = key;
|
|
probs->probs[probs->numProblems].h = headerLink(h);
|
|
probs->probs[probs->numProblems].ulong1 = ulong1;
|
|
if (str1)
|
|
probs->probs[probs->numProblems].str1 = xstrdup(str1);
|
|
else
|
|
probs->probs[probs->numProblems].str1 = NULL;
|
|
|
|
if (altH) {
|
|
probs->probs[probs->numProblems].altH = headerLink(altH);
|
|
} else
|
|
probs->probs[probs->numProblems].altH = NULL;
|
|
|
|
probs->probs[probs->numProblems++].ignoreProblem = 0;
|
|
}
|
|
|
|
static void psAppendFile(rpmProblemSet probs, rpmProblemType type,
|
|
const void * key, Header h, const char * dirName,
|
|
const char * baseName, Header altH, unsigned long ulong1)
|
|
{
|
|
char * str = alloca(strlen(dirName) + strlen(baseName) + 1);
|
|
|
|
sprintf(str, "%s%s", dirName, baseName);
|
|
psAppend(probs, type, key, h, str, altH, ulong1);
|
|
}
|
|
|
|
static int archOkay(Header h)
|
|
{
|
|
int_8 * pkgArchNum;
|
|
void * pkgArch;
|
|
int type, count, archNum;
|
|
|
|
/* make sure we're trying to install this on the proper architecture */
|
|
headerGetEntry(h, RPMTAG_ARCH, &type, (void **) &pkgArch, &count);
|
|
if (type == RPM_INT8_TYPE) {
|
|
/* old arch handling */
|
|
rpmGetArchInfo(NULL, &archNum);
|
|
pkgArchNum = pkgArch;
|
|
if (archNum != *pkgArchNum) {
|
|
return 0;
|
|
}
|
|
} else {
|
|
/* 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 */
|
|
headerGetEntry(h, RPMTAG_OS, &type, (void **) &pkgOs, &count);
|
|
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 {
|
|
/* 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++) {
|
|
headerFree(probs->probs[i].h);
|
|
if (probs->probs[i].str1) free(probs->probs[i].str1);
|
|
if (probs->probs[i].altH) {
|
|
headerFree(probs->probs[i].altH);
|
|
}
|
|
}
|
|
free(probs);
|
|
}
|
|
|
|
static Header relocateFileList(struct availablePackage * alp,
|
|
rpmProblemSet probs, Header origH,
|
|
enum fileActions * actions,
|
|
int allowBadRelocate)
|
|
{
|
|
int numValid, numRelocations;
|
|
int i, j;
|
|
rpmRelocation * rawRelocations = alp->relocs;
|
|
rpmRelocation * relocations = NULL;
|
|
const char ** validRelocations;
|
|
char ** baseFileNames, ** dirNames;
|
|
int_32 * dirIndexes;
|
|
int_32 * newDirIndexes;
|
|
int_32 fileCount, dirCount;
|
|
char * skipDirList;
|
|
Header h;
|
|
int relocated = 0, len;
|
|
char * str;
|
|
int fileAlloced = 0;
|
|
char * filespec = NULL;
|
|
char * chptr;
|
|
int haveRelocatedFile = 0;
|
|
|
|
if (!headerGetEntry(origH, RPMTAG_PREFIXES, NULL,
|
|
(void **) &validRelocations, &numValid))
|
|
numValid = 0;
|
|
|
|
if (!rawRelocations && !numValid) {
|
|
Header oH = headerLink(origH);
|
|
return oH;
|
|
}
|
|
|
|
h = headerCopy(origH);
|
|
|
|
if (rawRelocations) {
|
|
for (i = 0; rawRelocations[i].newPath || rawRelocations[i].oldPath;
|
|
i++) ;
|
|
numRelocations = i;
|
|
} else {
|
|
numRelocations = 0;
|
|
}
|
|
relocations = alloca(sizeof(*relocations) * numRelocations);
|
|
|
|
/* Build sorted relocation list from raw relocations. */
|
|
for (i = 0; i < numRelocations; i++) {
|
|
/* FIXME: default relocations (oldPath == NULL) need to be handled
|
|
in the UI, not rpmlib */
|
|
|
|
/* FIXME: Trailing /'s will confuse us greatly. Internal ones will
|
|
too, but those are more trouble to fix up. :-( */
|
|
str = alloca(strlen(rawRelocations[i].oldPath) + 1);
|
|
strcpy(str, rawRelocations[i].oldPath);
|
|
stripTrailingSlashes(str);
|
|
relocations[i].oldPath = str;
|
|
|
|
/* An old path w/o a new path is valid, and indicates exclusion */
|
|
if (rawRelocations[i].newPath) {
|
|
str = alloca(strlen(rawRelocations[i].newPath) + 1);
|
|
strcpy(str, rawRelocations[i].newPath);
|
|
stripTrailingSlashes(str);
|
|
relocations[i].newPath = str;
|
|
|
|
/* Verify that the relocation's old path is in the header. */
|
|
for (j = 0; j < numValid; j++)
|
|
if (!strcmp(validRelocations[j], relocations[i].oldPath)) break;
|
|
if (j == numValid && !allowBadRelocate)
|
|
psAppend(probs, RPMPROB_BADRELOCATE, alp->key, alp->h,
|
|
relocations[i].oldPath, NULL,0 );
|
|
} 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 (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;
|
|
}
|
|
|
|
/* Add relocation values to the header */
|
|
if (numValid) {
|
|
const char ** actualRelocations;
|
|
|
|
actualRelocations = xmalloc(sizeof(*actualRelocations) * numValid);
|
|
for (i = 0; i < numValid; i++) {
|
|
for (j = 0; j < numRelocations; j++) {
|
|
if (strcmp(validRelocations[i], relocations[j].oldPath))
|
|
continue;
|
|
actualRelocations[i] = relocations[j].newPath;
|
|
break;
|
|
}
|
|
if (j == numRelocations)
|
|
actualRelocations[i] = validRelocations[i];
|
|
}
|
|
|
|
headerAddEntry(h, RPMTAG_INSTPREFIXES, RPM_STRING_ARRAY_TYPE,
|
|
(void **) actualRelocations, numValid);
|
|
|
|
xfree(actualRelocations);
|
|
xfree(validRelocations);
|
|
}
|
|
|
|
/* For all relocations, we go through sorted file and relocation lists
|
|
* backwards so that /usr/local relocations take precedence over /usr
|
|
* ones. */
|
|
|
|
headerGetEntry(h, RPMTAG_COMPFILELIST, NULL, (void **) &baseFileNames,
|
|
&fileCount);
|
|
headerGetEntry(h, RPMTAG_COMPFILEDIRS, NULL, (void **) &dirIndexes, NULL);
|
|
headerGetEntry(h, RPMTAG_COMPDIRLIST, NULL, (void **) &dirNames,
|
|
&dirCount);
|
|
skipDirList = xcalloc(sizeof(*skipDirList), dirCount);
|
|
|
|
newDirIndexes = alloca(sizeof(*newDirIndexes) * fileCount);
|
|
memcpy(newDirIndexes, dirIndexes, sizeof(*newDirIndexes) * fileCount);
|
|
dirIndexes = newDirIndexes;
|
|
|
|
/* Now relocate individual files. */
|
|
|
|
for (i = fileCount - 1; i >= 0; i--) {
|
|
/* If we're skipping the directory this file is part of, skip this
|
|
file as well. */
|
|
if (skipDirList[dirIndexes[i]]) {
|
|
actions[i] = FA_SKIPNSTATE;
|
|
rpmMessage(RPMMESS_DEBUG, _("excluding file %s%s\n"),
|
|
dirNames[dirIndexes[i]], baseFileNames[i]);
|
|
continue;
|
|
}
|
|
|
|
/* See if this file needs relocating (which will only occur if the
|
|
full file path we have exactly matches a path in the relocation
|
|
list. XXX FIXME: Would a bsearch of the (already sorted)
|
|
relocation list be a good idea? */
|
|
|
|
len = strlen(dirNames[dirIndexes[i]]) + strlen(baseFileNames[i]) + 1;
|
|
if (len >= fileAlloced) {
|
|
fileAlloced = len * 2;
|
|
filespec = xrealloc(filespec, fileAlloced);
|
|
}
|
|
strcpy(filespec, dirNames[dirIndexes[i]]);
|
|
strcat(filespec, baseFileNames[i]);
|
|
|
|
for (j = numRelocations - 1; j >= 0; j--)
|
|
if (!strcmp(relocations[j].oldPath, filespec)) break;
|
|
|
|
if (j < 0) continue;
|
|
|
|
if (actions && !relocations[j].newPath) {
|
|
/* On install, a relocate to NULL means skip the file */
|
|
skipDirList[i] = 1;
|
|
rpmMessage(RPMMESS_DEBUG, _("excluding directory %s\n"),
|
|
dirNames[i]);
|
|
continue;
|
|
}
|
|
|
|
rpmMessage(RPMMESS_DEBUG, _("relocating %s to %s\n"),
|
|
filespec, relocations[j].newPath);
|
|
relocated = 1;
|
|
|
|
len = strlen(relocations[j].newPath);
|
|
if (len >= fileAlloced) {
|
|
fileAlloced = len * 2;
|
|
filespec = xrealloc(filespec, fileAlloced);
|
|
}
|
|
strcpy(filespec, relocations[j].newPath);
|
|
chptr = strrchr(filespec, '/');
|
|
*chptr++ = '\0';
|
|
|
|
/* filespec is the new path, and chptr is the new basename */
|
|
if (strcmp(baseFileNames[i], chptr)) {
|
|
baseFileNames[i] = alloca(strlen(chptr) + 1);
|
|
strcpy(baseFileNames[i], chptr);
|
|
}
|
|
|
|
/* Does this directory already exist in the directory list? */
|
|
for (j = 0; j < dirCount; j++)
|
|
if (!strcmp(filespec, dirNames[j])) break;
|
|
|
|
if (j < dirCount) {
|
|
dirIndexes[i] = j;
|
|
continue;
|
|
}
|
|
|
|
/* Creating new paths is a pita */
|
|
if (!haveRelocatedFile) {
|
|
char ** newDirList;
|
|
int k;
|
|
|
|
haveRelocatedFile = 1;
|
|
newDirList = xmalloc(sizeof(*newDirList) * (dirCount + 1));
|
|
for (k = 0; k < dirCount; k++) {
|
|
newDirList[k] = alloca(strlen(dirNames[k]) + 1);
|
|
strcpy(newDirList[k], dirNames[k]);
|
|
}
|
|
free(dirNames);
|
|
dirNames = newDirList;
|
|
} else {
|
|
dirNames = xrealloc(dirNames,
|
|
sizeof(*dirNames) * (dirCount + 1));
|
|
}
|
|
|
|
dirNames[dirCount] = alloca(strlen(filespec) + 1);
|
|
strcpy(dirNames[dirCount], filespec);
|
|
dirIndexes[i] = dirCount;
|
|
dirCount++;
|
|
}
|
|
|
|
/* Start off by relocating directories. */
|
|
for (i = dirCount - 1; i >= 0; i--) {
|
|
for (j = numRelocations - 1; j >= 0; j--) {
|
|
int len;
|
|
|
|
len = strlen(relocations[j].oldPath);
|
|
if (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);
|
|
|
|
strcpy(t, s);
|
|
strcat(t, dirNames[i] + len);
|
|
rpmMessage(RPMMESS_DEBUG, _("relocating directory %s to %s\n"),
|
|
dirNames[i], t);
|
|
dirNames[i] = t;
|
|
relocated = 1;
|
|
} else if (actions) {
|
|
/* On install, a relocate to NULL means skip the file */
|
|
skipDirList[i] = 1;
|
|
rpmMessage(RPMMESS_DEBUG, _("excluding directory %s\n"),
|
|
dirNames[i]);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Save original filenames in header and replace (relocated) filenames. */
|
|
if (relocated) {
|
|
int c;
|
|
void * p;
|
|
int t;
|
|
|
|
headerGetEntry(h, RPMTAG_COMPFILELIST, &t, &p, &c);
|
|
headerAddEntry(h, RPMTAG_ORIGCOMPFILELIST, t, p, c);
|
|
xfree(p);
|
|
|
|
headerGetEntry(h, RPMTAG_COMPFILEDIRS, &t, &p, &c);
|
|
headerAddEntry(h, RPMTAG_ORIGCOMPFILEDIRS, t, p, c);
|
|
xfree(p);
|
|
|
|
headerGetEntry(h, RPMTAG_COMPDIRLIST, &t, &p, &c);
|
|
headerAddEntry(h, RPMTAG_ORIGCOMPDIRLIST, t, p, c);
|
|
|
|
headerModifyEntry(h, RPMTAG_COMPFILELIST, RPM_STRING_ARRAY_TYPE,
|
|
baseFileNames, fileCount);
|
|
headerModifyEntry(h, RPMTAG_COMPFILEDIRS, RPM_STRING_ARRAY_TYPE,
|
|
dirIndexes, fileCount);
|
|
headerModifyEntry(h, RPMTAG_COMPDIRLIST, RPM_STRING_ARRAY_TYPE,
|
|
dirNames, dirCount);
|
|
}
|
|
|
|
free(baseFileNames);
|
|
free(dirNames);
|
|
if (filespec) free(filespec);
|
|
free(skipDirList);
|
|
|
|
return h;
|
|
}
|
|
|
|
static int psTrim(rpmProblemSet filter, rpmProblemSet target)
|
|
{
|
|
/* 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. */
|
|
rpmProblem * f, * t;
|
|
int gotProblems = 0;
|
|
|
|
f = filter->probs;
|
|
t = target->probs;
|
|
|
|
while ((f - filter->probs) < filter->numProblems) {
|
|
if (!f->ignoreProblem) {
|
|
f++;
|
|
continue;
|
|
}
|
|
while ((t - target->probs) < target->numProblems) {
|
|
if (f->h == t->h && f->type == t->type && t->key == f->key &&
|
|
XSTRCMP(f->str1, t->str1))
|
|
break;
|
|
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 enum fileTypes whatis(short mode)
|
|
{
|
|
enum fileTypes result;
|
|
|
|
if (S_ISDIR(mode))
|
|
result = XDIR;
|
|
else if (S_ISCHR(mode))
|
|
result = CDEV;
|
|
else if (S_ISBLK(mode))
|
|
result = BDEV;
|
|
else if (S_ISLNK(mode))
|
|
result = LINK;
|
|
else if (S_ISSOCK(mode))
|
|
result = SOCK;
|
|
else if (S_ISFIFO(mode))
|
|
result = PIPE;
|
|
else
|
|
result = REG;
|
|
|
|
return result;
|
|
}
|
|
|
|
static enum fileActions decideFileFate(const char * dirName,
|
|
const char * baseName, short dbMode,
|
|
const char * dbMd5, const char * dbLink, short newMode,
|
|
const char * newMd5, const char * newLink, int newFlags,
|
|
int brokenMd5, int transFlags)
|
|
{
|
|
char buffer[1024];
|
|
const char * dbAttr, * newAttr;
|
|
enum fileTypes dbWhat, newWhat, diskWhat;
|
|
struct stat sb;
|
|
int i, rc;
|
|
int save = (newFlags & RPMFILE_NOREPLACE) ? FA_ALTNAME : FA_SAVE;
|
|
char * filespec = alloca(strlen(dirName) + strlen(baseName) + 1);
|
|
|
|
sprintf(filespec, "%s%s", dirName, baseName);
|
|
|
|
if (lstat(filespec, &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) &&
|
|
(newFlags & RPMFILE_MISSINGOK)) {
|
|
rpmMessage(RPMMESS_DEBUG, _("%s skipped due to missingok flag\n"),
|
|
filespec);
|
|
return FA_SKIP;
|
|
} else {
|
|
return FA_CREATE;
|
|
}
|
|
}
|
|
|
|
diskWhat = whatis(sb.st_mode);
|
|
dbWhat = whatis(dbMode);
|
|
newWhat = whatis(newMode);
|
|
|
|
/* 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) {
|
|
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;
|
|
}
|
|
|
|
if (dbWhat == REG) {
|
|
if (brokenMd5)
|
|
rc = mdfileBroken(filespec, buffer);
|
|
else
|
|
rc = mdfile(filespec, buffer);
|
|
|
|
if (rc) {
|
|
/* assume the file has been removed, don't freak */
|
|
return FA_CREATE;
|
|
}
|
|
dbAttr = dbMd5;
|
|
newAttr = newMd5;
|
|
} else /* dbWhat == LINK */ {
|
|
memset(buffer, 0, sizeof(buffer));
|
|
i = readlink(filespec, buffer, sizeof(buffer) - 1);
|
|
if (i == -1) {
|
|
/* assume the file has been removed, don't freak */
|
|
return FA_CREATE;
|
|
}
|
|
dbAttr = dbLink;
|
|
newAttr = newLink;
|
|
}
|
|
|
|
/* this order matters - we'd prefer to CREATE the file if at all
|
|
possible in case something else (like the timestamp) has changed */
|
|
|
|
if (!strcmp(dbAttr, buffer)) {
|
|
/* this config file has never been modified, so just replace it */
|
|
return FA_CREATE;
|
|
}
|
|
|
|
if (!strcmp(dbAttr, newAttr)) {
|
|
/* this file is the same in all versions of this package */
|
|
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...
|
|
*/
|
|
return save;
|
|
}
|
|
|
|
static int filecmp(short mode1, const char * md51, const char * link1,
|
|
short mode2, const char * md52, const char * link2)
|
|
{
|
|
enum fileTypes what1, what2;
|
|
|
|
what1 = whatis(mode1);
|
|
what2 = whatis(mode2);
|
|
|
|
if (what1 != what2) return 1;
|
|
|
|
if (what1 == LINK)
|
|
return strcmp(link1, link2);
|
|
else if (what1 == REG)
|
|
return strcmp(md51, md52);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int handleInstInstalledFiles(TFI_t * fi, rpmdb db,
|
|
struct sharedFileInfo * shared,
|
|
int sharedCount, int reportConflicts,
|
|
rpmProblemSet probs, int transFlags)
|
|
{
|
|
Header h;
|
|
int i;
|
|
const char ** otherMd5s;
|
|
const char ** otherLinks;
|
|
const char * otherStates;
|
|
uint_32 * otherFlags;
|
|
uint_32 * otherSizes;
|
|
uint_16 * otherModes;
|
|
int numReplaced = 0;
|
|
|
|
if ((h = rpmdbGetRecord(db, shared->otherPkg)) == NULL)
|
|
return 1;
|
|
|
|
headerGetEntryMinMemory(h, RPMTAG_FILEMD5S, NULL,
|
|
(void **) &otherMd5s, NULL);
|
|
headerGetEntryMinMemory(h, RPMTAG_FILELINKTOS, NULL,
|
|
(void **) &otherLinks, NULL);
|
|
headerGetEntryMinMemory(h, RPMTAG_FILESTATES, NULL,
|
|
(void **) &otherStates, NULL);
|
|
headerGetEntryMinMemory(h, RPMTAG_FILEMODES, NULL,
|
|
(void **) &otherModes, NULL);
|
|
headerGetEntryMinMemory(h, RPMTAG_FILEFLAGS, NULL,
|
|
(void **) &otherFlags, NULL);
|
|
headerGetEntryMinMemory(h, RPMTAG_FILESIZES, NULL,
|
|
(void **) &otherSizes, NULL);
|
|
|
|
fi->replaced = xmalloc(sizeof(*fi->replaced) * sharedCount);
|
|
|
|
for (i = 0; i < sharedCount; i++, shared++) {
|
|
int otherFileNum, fileNum;
|
|
otherFileNum = shared->otherFileNum;
|
|
fileNum = shared->pkgFileNum;
|
|
|
|
if (otherStates[otherFileNum] != RPMFILE_STATE_NORMAL)
|
|
continue;
|
|
|
|
if (filecmp(otherModes[otherFileNum],
|
|
otherMd5s[otherFileNum],
|
|
otherLinks[otherFileNum],
|
|
fi->fmodes[fileNum],
|
|
fi->fmd5s[fileNum],
|
|
fi->flinks[fileNum])) {
|
|
if (reportConflicts)
|
|
psAppendFile(probs, RPMPROB_FILE_CONFLICT, fi->ap->key,
|
|
fi->ap->h, fi->dnl[fi->dil[fileNum]],
|
|
fi->bnl[fileNum], h,0 );
|
|
if (!(otherFlags[otherFileNum] | fi->fflags[fileNum])
|
|
& RPMFILE_CONFIG) {
|
|
if (!shared->isRemoved)
|
|
fi->replaced[numReplaced++] = *shared;
|
|
}
|
|
}
|
|
|
|
if ((otherFlags[otherFileNum] | fi->fflags[fileNum]) & RPMFILE_CONFIG) {
|
|
fi->actions[fileNum] = decideFileFate(
|
|
fi->dnl[fi->dil[fileNum]],
|
|
fi->bnl[fileNum],
|
|
otherModes[otherFileNum],
|
|
otherMd5s[otherFileNum],
|
|
otherLinks[otherFileNum],
|
|
fi->fmodes[fileNum],
|
|
fi->fmd5s[fileNum],
|
|
fi->flinks[fileNum],
|
|
fi->fflags[fileNum],
|
|
!headerIsEntry(h, RPMTAG_RPMVERSION),
|
|
transFlags);
|
|
}
|
|
|
|
fi->replacedSizes[fileNum] = otherSizes[otherFileNum];
|
|
}
|
|
|
|
free(otherMd5s);
|
|
free(otherLinks);
|
|
headerFree(h);
|
|
|
|
fi->replaced = xrealloc(fi->replaced, /* XXX memory leak */
|
|
sizeof(*fi->replaced) * (numReplaced + 1));
|
|
fi->replaced[numReplaced].otherPkg = 0;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int handleRmvdInstalledFiles(TFI_t * fi, rpmdb db,
|
|
struct sharedFileInfo * shared,
|
|
int sharedCount)
|
|
{
|
|
Header h;
|
|
const char * otherStates;
|
|
int i;
|
|
|
|
if ((h = rpmdbGetRecord(db, shared->otherPkg)) == NULL)
|
|
return 1;
|
|
|
|
headerGetEntryMinMemory(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;
|
|
}
|
|
|
|
headerFree(h);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void handleOverlappedFiles(TFI_t * fi, hashTable ht,
|
|
rpmProblemSet probs, struct diskspaceInfo * 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);
|
|
}
|
|
|
|
sprintf(filespec, "%s%s", fi->dnl[fi->dil[i]], fi->bnl[i]);
|
|
|
|
if (dsl) {
|
|
ds = dsl;
|
|
while (ds->block && ds->dev != fi->fps[i].entry->dev) ds++;
|
|
if (!ds->block) 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.
|
|
*/
|
|
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; 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 (FP_EQUAL(fi->fps[i], recs[otherPkgNum]->fps[otherFileNum]))
|
|
break;
|
|
}
|
|
/* XXX is this test still necessary? */
|
|
if (recs[otherPkgNum]->actions[otherFileNum] != FA_UNKNOWN)
|
|
break;
|
|
}
|
|
|
|
switch (fi->type) {
|
|
struct stat sb;
|
|
case TR_ADDED:
|
|
if (otherPkgNum < 0) {
|
|
/* XXX is this test still necessary? */
|
|
if (fi->actions[i] != FA_UNKNOWN)
|
|
break;
|
|
if ((fi->fflags[i] & RPMFILE_CONFIG) &&
|
|
!lstat(filespec, &sb)) {
|
|
/* 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 nnon-identical files as a conflict. */
|
|
if (probs && filecmp(recs[otherPkgNum]->fmodes[otherFileNum],
|
|
recs[otherPkgNum]->fmd5s[otherFileNum],
|
|
recs[otherPkgNum]->flinks[otherFileNum],
|
|
fi->fmodes[i],
|
|
fi->fmd5s[i],
|
|
fi->flinks[i])) {
|
|
psAppend(probs, RPMPROB_NEW_FILE_CONFLICT, fi->ap->key,
|
|
fi->ap->h, filespec, recs[otherPkgNum]->ap->h, 0);
|
|
}
|
|
|
|
/* Try to get the disk accounting correct even if a conflict. */
|
|
fixupSize = recs[otherPkgNum]->fsizes[otherFileNum];
|
|
|
|
if ((fi->fflags[i] & RPMFILE_CONFIG) && !lstat(filespec, &sb)) {
|
|
/* 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) {
|
|
#if XXX_ERASED_PACKAGES_LAST
|
|
fi->actions[i] = FA_SKIP;
|
|
break;
|
|
#else
|
|
/* Here is an overlapped added file we don't want to nuke */
|
|
if (recs[otherPkgNum]->actions[otherFileNum] != FA_REMOVE) {
|
|
/* 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;
|
|
#endif
|
|
}
|
|
if (XFA_SKIPPING(fi->actions[i]))
|
|
break;
|
|
if (fi->fstates[i] != RPMFILE_STATE_NORMAL)
|
|
break;
|
|
if (!(S_ISREG(fi->fmodes[i]) && (fi->fflags[i] & RPMFILE_CONFIG))) {
|
|
fi->actions[i] = FA_REMOVE;
|
|
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_REMOVE;
|
|
break;
|
|
}
|
|
|
|
if (ds) {
|
|
uint_32 s = BLOCK_ROUND(fi->fsizes[i], ds->block);
|
|
|
|
switch (fi->actions[i]) {
|
|
case FA_BACKUP:
|
|
case FA_SAVE:
|
|
case FA_ALTNAME:
|
|
ds->needed += s;
|
|
break;
|
|
|
|
/* FIXME: If a two packages share a file (same md5sum), and
|
|
that file is being replaced on disk, will ds->needed get
|
|
decremented twice? Quite probably! */
|
|
case FA_CREATE:
|
|
ds->needed += s;
|
|
ds->needed -= BLOCK_ROUND(fi->replacedSizes[i], ds->block);
|
|
break;
|
|
|
|
case FA_REMOVE:
|
|
ds->needed -= s;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
ds->needed -= BLOCK_ROUND(fixupSize, ds->block);
|
|
}
|
|
}
|
|
if (filespec) free(filespec);
|
|
}
|
|
|
|
static int ensureOlder(rpmdb db, Header new, int dbOffset, rpmProblemSet probs,
|
|
const void * key)
|
|
{
|
|
Header old;
|
|
int result, rc = 0;
|
|
|
|
old = rpmdbGetRecord(db, dbOffset);
|
|
if (old == NULL) return 1;
|
|
|
|
result = rpmVersionCompare(old, new);
|
|
if (result <= 0)
|
|
rc = 0;
|
|
else if (result > 0) {
|
|
rc = 1;
|
|
psAppend(probs, RPMPROB_OLDPACKAGE, key, new, NULL, old, 0);
|
|
}
|
|
|
|
headerFree(old);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static void skipFiles(TFI_t * fi, int noDocs)
|
|
{
|
|
int i;
|
|
char ** netsharedPaths = NULL;
|
|
const char ** fileLangs;
|
|
const char ** languages;
|
|
const char *s;
|
|
|
|
if (!noDocs)
|
|
noDocs = rpmExpandNumeric("%{_excludedocs}");
|
|
|
|
{ const char *tmpPath = rpmExpand("%{_netsharedpath}", NULL);
|
|
if (tmpPath && *tmpPath != '%')
|
|
netsharedPaths = splitString(tmpPath, strlen(tmpPath), ':');
|
|
xfree(tmpPath);
|
|
}
|
|
|
|
if (!headerGetEntry(fi->h, RPMTAG_FILELANGS, NULL, (void **) &fileLangs,
|
|
NULL))
|
|
fileLangs = NULL;
|
|
|
|
s = rpmExpand("%{_install_langs}", NULL);
|
|
if (!(s && *s != '%')) {
|
|
if (s) xfree(s);
|
|
s = NULL;
|
|
}
|
|
if (s) {
|
|
languages = (const char **) splitString(s, strlen(s), ':');
|
|
xfree(s);
|
|
/* XXX LINGUAS/LANG is used by the installer so leave alone for now */
|
|
} else if ((s = getenv("LINGUAS")) || (s = getenv("LANG")) || (s = "en")) {
|
|
languages = (const char **) splitString(s, strlen(s), ':');
|
|
} else
|
|
languages = NULL;
|
|
|
|
for (i = 0; i < fi->fc; i++) {
|
|
char **nsp;
|
|
|
|
/* Don't bother with skipped files */
|
|
if (XFA_SKIPPING(fi->actions[i]))
|
|
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;
|
|
const char * dir = fi->dnl[fi->dil[i]];
|
|
|
|
len = strlen(*nsp);
|
|
if (strncmp(dir, *nsp, len))
|
|
continue;
|
|
|
|
/* Only directories or complete file paths can be net shared */
|
|
if (!(dir[len] == '/' || dir[len] == '\0'))
|
|
continue;
|
|
break;
|
|
}
|
|
|
|
if (nsp && *nsp) {
|
|
fi->actions[i] = FA_SKIPNETSHARED;
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
* Skip i18n language specific files.
|
|
*/
|
|
if (fileLangs && languages && *fileLangs[i]) {
|
|
const char **lang, *l, *le;
|
|
for (lang = languages; *lang; lang++) {
|
|
if (!strcmp(*lang, "all"))
|
|
break;
|
|
for (l = fileLangs[i]; *l; l = le) {
|
|
for (le = l; *le && *le != '|'; le++)
|
|
;
|
|
if ((le-l) > 0 && !strncmp(*lang, l, (le-l)))
|
|
break;
|
|
if (*le == '|') le++; /* skip over | */
|
|
}
|
|
if (*l) break;
|
|
}
|
|
if (*lang == NULL) {
|
|
fi->actions[i] = FA_SKIPNSTATE;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Skip documentation if requested.
|
|
*/
|
|
if (noDocs && (fi->fflags[i] & RPMFILE_DOC))
|
|
fi->actions[i] = FA_SKIPNSTATE;
|
|
}
|
|
|
|
if (netsharedPaths) freeSplitString(netsharedPaths);
|
|
if (fileLangs) free(fileLangs);
|
|
if (languages) freeSplitString((char **)languages);
|
|
}
|
|
|
|
#define NOTIFY(_x) if (notify) (void) notify _x
|
|
|
|
/* Return -1 on error, > 0 if newProbs is set, 0 if everything happened */
|
|
|
|
int rpmRunTransactions(rpmTransactionSet ts, rpmCallbackFunction notify,
|
|
void * notifyData, rpmProblemSet okProbs,
|
|
rpmProblemSet * newProbs, int transFlags, int ignoreSet)
|
|
{
|
|
int i, j;
|
|
int rc, ourrc = 0;
|
|
struct availablePackage * alp;
|
|
rpmProblemSet probs;
|
|
dbiIndexSet dbi, * matches;
|
|
Header * hdrs;
|
|
int fileCount;
|
|
int totalFileCount = 0;
|
|
hashTable ht;
|
|
TFI_t * flList, * fi;
|
|
struct sharedFileInfo * shared, * sharedList;
|
|
int numShared;
|
|
int flEntries;
|
|
int nexti;
|
|
int lastFailed;
|
|
const char * currDir;
|
|
FD_t fd;
|
|
const char ** filesystems;
|
|
int filesystemCount;
|
|
struct diskspaceInfo * di = NULL;
|
|
int oc;
|
|
fingerPrintCache fpc;
|
|
|
|
/* FIXME: what if the same package is included in ts twice? */
|
|
|
|
/* Get available space on mounted file systems */
|
|
if (!(ignoreSet & RPMPROB_FILTER_DISKSPACE) &&
|
|
!rpmGetFilesystemList(&filesystems, &filesystemCount)) {
|
|
struct stat sb;
|
|
|
|
di = alloca(sizeof(*di) * (filesystemCount + 1));
|
|
|
|
for (i = 0; (i < filesystemCount) && di; i++) {
|
|
#if STATFS_IN_SYS_STATVFS
|
|
struct statvfs sfb;
|
|
if (statvfs(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.
|
|
*/
|
|
if (statfs(filesystems[i], &sfb, sizeof(sfb), 0))
|
|
# else
|
|
if (statfs(filesystems[i], &sfb))
|
|
# endif
|
|
#endif
|
|
{
|
|
di = NULL;
|
|
} else {
|
|
di[i].block = sfb.f_bsize;
|
|
di[i].needed = 0;
|
|
#ifdef STATFS_HAS_F_BAVAIL
|
|
di[i].avail = 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.
|
|
*/
|
|
di[i].avail = sfb.f_blocks - sfb.f_bfree;
|
|
#endif
|
|
|
|
|
|
stat(filesystems[i], &sb);
|
|
di[i].dev = sb.st_dev;
|
|
}
|
|
}
|
|
|
|
if (di) di[i].block = 0;
|
|
}
|
|
|
|
probs = psCreate();
|
|
*newProbs = probs;
|
|
hdrs = alloca(sizeof(*hdrs) * ts->addedPackages.size);
|
|
|
|
/* ===============================================
|
|
* 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 */
|
|
for (alp = ts->addedPackages.list; (alp - ts->addedPackages.list) <
|
|
ts->addedPackages.size; alp++) {
|
|
if (!archOkay(alp->h) && !(ignoreSet & RPMPROB_FILTER_IGNOREARCH))
|
|
psAppend(probs, RPMPROB_BADARCH, alp->key, alp->h, NULL, NULL, 0);
|
|
|
|
if (!osOkay(alp->h) && !(ignoreSet & RPMPROB_FILTER_IGNOREOS)) {
|
|
psAppend(probs, RPMPROB_BADOS, alp->key, alp->h, NULL, NULL, 0);
|
|
}
|
|
|
|
if (!(ignoreSet & RPMPROB_FILTER_OLDPACKAGE)) {
|
|
rc = rpmdbFindPackage(ts->db, alp->name, &dbi);
|
|
if (rc == 2) {
|
|
return -1;
|
|
} else if (!rc) {
|
|
for (i = 0; i < dbiIndexSetCount(dbi); i++)
|
|
ensureOlder(ts->db, alp->h, dbiIndexRecordOffset(dbi, i),
|
|
probs, alp->key);
|
|
|
|
dbiFreeIndexRecord(dbi);
|
|
}
|
|
}
|
|
|
|
rc = findMatches(ts->db, alp->name, alp->version, alp->release, &dbi);
|
|
if (rc == 2) {
|
|
return -1;
|
|
} else if (!rc) {
|
|
if (!(ignoreSet & RPMPROB_FILTER_REPLACEPKG))
|
|
psAppend(probs, RPMPROB_PKG_INSTALLED, alp->key, alp->h, NULL,
|
|
NULL, 0);
|
|
dbiFreeIndexRecord(dbi);
|
|
}
|
|
|
|
if (headerGetEntry(alp->h, RPMTAG_COMPFILELIST, NULL, NULL,
|
|
&fileCount))
|
|
totalFileCount += fileCount;
|
|
}
|
|
|
|
/* FIXME: it seems a bit silly to read in all of these headers twice */
|
|
/* The ordering doesn't matter here */
|
|
for (i = 0; i < ts->numRemovedPackages; i++) {
|
|
Header h;
|
|
|
|
if ((h = rpmdbGetRecord(ts->db, ts->removedPackages[i]))) {
|
|
if (headerGetEntry(h, RPMTAG_COMPFILELIST, NULL, NULL,
|
|
&fileCount))
|
|
totalFileCount += fileCount;
|
|
headerFree(h);
|
|
}
|
|
}
|
|
|
|
/* ===============================================
|
|
* Initialize file list:
|
|
*/
|
|
flEntries = ts->addedPackages.size + ts->numRemovedPackages;
|
|
flList = alloca(sizeof(*flList) * (flEntries));
|
|
|
|
/* 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. */
|
|
for (fi = flList, oc = 0; oc < ts->orderCount; fi++, oc++) {
|
|
memset(fi, 0, sizeof(*fi));
|
|
|
|
switch (ts->order[oc].type) {
|
|
case TR_ADDED:
|
|
i = ts->order[oc].u.addedIndex;
|
|
alp = ts->addedPackages.list + ts->order[oc].u.addedIndex;
|
|
|
|
if (!headerGetEntryMinMemory(alp->h, RPMTAG_COMPFILELIST, NULL,
|
|
NULL, &fi->fc)) {
|
|
fi->h = headerLink(alp->h);
|
|
hdrs[i] = headerLink(fi->h);
|
|
continue;
|
|
}
|
|
|
|
/* Allocate file actions (and initialize to RPMFILE_STATE_NORMAL) */
|
|
fi->actions = xcalloc(fi->fc, sizeof(*fi->actions));
|
|
hdrs[i] = relocateFileList(alp, probs, alp->h, fi->actions,
|
|
ignoreSet & RPMPROB_FILTER_FORCERELOCATE);
|
|
fi->h = headerLink(hdrs[i]);
|
|
fi->type = TR_ADDED;
|
|
fi->ap = alp;
|
|
break;
|
|
case TR_REMOVED:
|
|
fi->record = ts->order[oc].u.removed.dboffset;
|
|
fi->h = rpmdbGetRecord(ts->db, fi->record);
|
|
if (fi->h == NULL) {
|
|
/* ACK! */
|
|
continue;
|
|
}
|
|
fi->type = TR_REMOVED;
|
|
break;
|
|
}
|
|
|
|
if (!headerGetEntry(fi->h, RPMTAG_COMPFILELIST, NULL,
|
|
(void **) &fi->bnl, &fi->fc)) {
|
|
/* This catches removed packages w/ no file lists */
|
|
fi->fc = 0;
|
|
continue;
|
|
}
|
|
|
|
headerGetEntry(fi->h, RPMTAG_COMPFILEDIRS, NULL, (void **) &fi->dil,
|
|
NULL);
|
|
headerGetEntry(fi->h, RPMTAG_COMPDIRLIST, NULL, (void **) &fi->dnl,
|
|
NULL);
|
|
|
|
/* actions is initialized earlier for added packages */
|
|
if (fi->actions == NULL)
|
|
fi->actions = xcalloc(fi->fc, sizeof(*fi->actions));
|
|
|
|
headerGetEntry(fi->h, RPMTAG_FILEMODES, NULL,
|
|
(void **) &fi->fmodes, NULL);
|
|
headerGetEntry(fi->h, RPMTAG_FILEFLAGS, NULL,
|
|
(void **) &fi->fflags, NULL);
|
|
headerGetEntry(fi->h, RPMTAG_FILESIZES, NULL,
|
|
(void **) &fi->fsizes, NULL);
|
|
headerGetEntry(fi->h, RPMTAG_FILESTATES, NULL,
|
|
(void **) &fi->fstates, NULL);
|
|
|
|
switch (ts->order[oc].type) {
|
|
case TR_REMOVED:
|
|
headerGetEntry(fi->h, RPMTAG_FILEMD5S, NULL,
|
|
(void **) &fi->fmd5s, NULL);
|
|
headerGetEntry(fi->h, RPMTAG_FILELINKTOS, NULL,
|
|
(void **) &fi->flinks, NULL);
|
|
fi->fsizes = memcpy(xmalloc(fi->fc * sizeof(*fi->fsizes)),
|
|
fi->fsizes, fi->fc * sizeof(*fi->fsizes));
|
|
fi->fflags = memcpy(xmalloc(fi->fc * sizeof(*fi->fflags)),
|
|
fi->fflags, fi->fc * sizeof(*fi->fflags));
|
|
fi->fmodes = memcpy(xmalloc(fi->fc * sizeof(*fi->fmodes)),
|
|
fi->fmodes, fi->fc * sizeof(*fi->fmodes));
|
|
fi->fstates = memcpy(xmalloc(fi->fc * sizeof(*fi->fstates)),
|
|
fi->fstates, fi->fc * sizeof(*fi->fstates));
|
|
fi->dil = memcpy(xmalloc(fi->fc * sizeof(*fi->dil)),
|
|
fi->dil, fi->fc * sizeof(*fi->dil));
|
|
headerFree(fi->h);
|
|
fi->h = NULL;
|
|
break;
|
|
case TR_ADDED:
|
|
headerGetEntryMinMemory(fi->h, RPMTAG_FILEMD5S, NULL,
|
|
(void **) &fi->fmd5s, NULL);
|
|
headerGetEntryMinMemory(fi->h, RPMTAG_FILELINKTOS, NULL,
|
|
(void **) &fi->flinks, NULL);
|
|
|
|
/* 0 makes for noops */
|
|
fi->replacedSizes = xcalloc(fi->fc, sizeof(*fi->replacedSizes));
|
|
|
|
/* Skip netshared paths, not our i18n files, and excluded docs */
|
|
skipFiles(fi, transFlags & RPMTRANS_FLAG_NODOCS);
|
|
break;
|
|
}
|
|
|
|
fi->fps = xmalloc(sizeof(*fi->fps) * fi->fc);
|
|
}
|
|
|
|
/* There are too many returns to plug this leak. Use alloca instead. */
|
|
{ const char *s = currentDirectory();
|
|
char *t = alloca(strlen(s) + 1);
|
|
strcpy(t, s);
|
|
currDir = t;
|
|
xfree(s);
|
|
}
|
|
|
|
chdir("/");
|
|
/*@-unrecog@*/ chroot(ts->root); /*@=unrecog@*/
|
|
|
|
ht = htCreate(totalFileCount * 2, 0, 0, fpHashFunction, fpEqual);
|
|
fpc = fpCacheCreate(totalFileCount);
|
|
|
|
/* ===============================================
|
|
* Add fingerprint for each file not skipped.
|
|
*/
|
|
for (fi = flList; (fi - flList) < flEntries; fi++) {
|
|
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;
|
|
htAddEntry(ht, fi->fps + i, fi);
|
|
}
|
|
}
|
|
|
|
NOTIFY((NULL, RPMCALLBACK_TRANS_START, 6, flEntries, NULL, notifyData));
|
|
|
|
/* ===============================================
|
|
* Compute file disposition for each package in transaction set.
|
|
*/
|
|
for (fi = flList; (fi - flList) < flEntries; fi++) {
|
|
int knownBad;
|
|
|
|
NOTIFY((NULL, RPMCALLBACK_TRANS_PROGRESS, (fi - flList), flEntries,
|
|
NULL, notifyData));
|
|
|
|
/* Extract file info for all files in this package from the database. */
|
|
matches = xmalloc(sizeof(*matches) * fi->fc);
|
|
if (rpmdbFindFpList(ts->db, fi->fps, matches, fi->fc)) return 1;
|
|
|
|
numShared = 0;
|
|
for (i = 0; i < fi->fc; i++)
|
|
numShared += dbiIndexSetCount(matches[i]);
|
|
|
|
/* Build sorted file info list for this package. */
|
|
shared = sharedList = xmalloc(sizeof(*sharedList) * (numShared + 1));
|
|
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++;
|
|
}
|
|
dbiFreeIndexRecord(matches[i]);
|
|
}
|
|
numShared = shared - sharedList;
|
|
shared->otherPkg = -1;
|
|
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)
|
|
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;
|
|
break;
|
|
}
|
|
|
|
/* Determine the fate of each file. */
|
|
switch (fi->type) {
|
|
case TR_ADDED:
|
|
handleInstInstalledFiles(fi, ts->db, shared, nexti - i,
|
|
!(beingRemoved || (ignoreSet & RPMPROB_FILTER_REPLACEOLDFILES)),
|
|
probs, transFlags);
|
|
break;
|
|
case TR_REMOVED:
|
|
if (!beingRemoved)
|
|
handleRmvdInstalledFiles(fi, ts->db, shared, nexti - i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
free(sharedList);
|
|
|
|
/* Update disk space needs on each partition for this package. */
|
|
handleOverlappedFiles(fi, ht,
|
|
(ignoreSet & RPMPROB_FILTER_REPLACENEWFILES) ? NULL : probs, di);
|
|
|
|
/* Check added package has sufficient space on each partition used. */
|
|
switch (fi->type) {
|
|
case TR_ADDED:
|
|
if (!(di && fi->fc))
|
|
break;
|
|
for (i = 0; i < filesystemCount; i++) {
|
|
if (adj_fs_blocks(di[i].needed) <= di[i].avail)
|
|
continue;
|
|
psAppend(probs, RPMPROB_DISKSPACE, fi->ap->key, fi->ap->h,
|
|
filesystems[i], NULL,
|
|
(adj_fs_blocks(di[i].needed) - di[i].avail) * di[i].block);
|
|
}
|
|
break;
|
|
case TR_REMOVED:
|
|
break;
|
|
}
|
|
}
|
|
|
|
NOTIFY((NULL, RPMCALLBACK_TRANS_STOP, 6, flEntries, NULL, notifyData));
|
|
|
|
chroot(".");
|
|
chdir(currDir);
|
|
|
|
/* ===============================================
|
|
* Free unused memory as soon as possible.
|
|
*/
|
|
htFree(ht);
|
|
|
|
for (oc = 0, fi = flList; oc < ts->orderCount; oc++, fi++) {
|
|
if (fi->fc == 0)
|
|
continue;
|
|
free(fi->dnl); fi->dnl = NULL;
|
|
free(fi->bnl); fi->bnl = NULL;
|
|
switch (fi->type) {
|
|
case TR_ADDED:
|
|
free(fi->fmd5s); fi->fmd5s = NULL;
|
|
free(fi->flinks); fi->flinks = NULL;
|
|
free(fi->fps); fi->fps = NULL;
|
|
break;
|
|
case TR_REMOVED:
|
|
free(fi->fps); fi->fps = NULL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
fpCacheFree(fpc);
|
|
|
|
/* ===============================================
|
|
* If unfiltered problems exist, free memory and return.
|
|
*/
|
|
if ((transFlags & RPMTRANS_FLAG_BUILD_PROBS) ||
|
|
(probs->numProblems && (!okProbs || psTrim(okProbs, probs)))) {
|
|
*newProbs = probs;
|
|
|
|
for (alp = ts->addedPackages.list, fi = flList;
|
|
(alp - ts->addedPackages.list) < ts->addedPackages.size;
|
|
alp++, fi++) {
|
|
headerFree(hdrs[alp - ts->addedPackages.list]);
|
|
}
|
|
|
|
freeFl(ts, flList);
|
|
return ts->orderCount;
|
|
}
|
|
|
|
/* ===============================================
|
|
* Install and remove packages.
|
|
*/
|
|
lastFailed = -2;
|
|
for (oc = 0, fi = flList; oc < ts->orderCount; oc++, fi++) {
|
|
switch (ts->order[oc].type) {
|
|
case TR_ADDED:
|
|
alp = ts->addedPackages.list + ts->order[oc].u.addedIndex;
|
|
i = ts->order[oc].u.addedIndex;
|
|
|
|
if ((fd = alp->fd) == 0) {
|
|
fd = notify(fi->h, RPMCALLBACK_INST_OPEN_FILE, 0, 0,
|
|
alp->key, notifyData);
|
|
if (fd) {
|
|
Header h;
|
|
|
|
headerFree(hdrs[i]);
|
|
rc = rpmReadPackageHeader(fd, &h, NULL, NULL, NULL);
|
|
if (rc) {
|
|
(void)notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE, 0, 0,
|
|
alp->key, notifyData);
|
|
ourrc++;
|
|
fd = NULL;
|
|
} else {
|
|
hdrs[i] = relocateFileList(alp, probs, h, NULL, 1);
|
|
headerFree(h);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fd) {
|
|
if (installBinaryPackage(ts->root, ts->db, fd,
|
|
hdrs[i], transFlags, notify,
|
|
notifyData, alp->key, fi->actions,
|
|
fi->fc ? fi->replaced : NULL,
|
|
ts->scriptFd)) {
|
|
ourrc++;
|
|
lastFailed = i;
|
|
}
|
|
} else {
|
|
ourrc++;
|
|
lastFailed = i;
|
|
}
|
|
|
|
headerFree(hdrs[i]);
|
|
|
|
if (alp->fd == NULL && fd)
|
|
(void)notify(fi->h, RPMCALLBACK_INST_CLOSE_FILE, 0, 0, alp->key,
|
|
notifyData);
|
|
break;
|
|
case TR_REMOVED:
|
|
if (ts->order[oc].u.removed.dependsOnIndex == lastFailed)
|
|
break;
|
|
if (removeBinaryPackage(ts->root, ts->db, fi->record,
|
|
transFlags, fi->actions, ts->scriptFd))
|
|
ourrc++;
|
|
break;
|
|
}
|
|
}
|
|
|
|
freeFl(ts, flList);
|
|
|
|
if (ourrc)
|
|
return -1;
|
|
else
|
|
return 0;
|
|
}
|