2002-03-25 23:16:26 +03:00
/** \ingroup rpmdep
* \ file lib / depends . c
*/
# include "system.h"
2002-03-26 01:02:39 +03:00
# include "rpmlib.h"
# include "rpmmacro.h" /* XXX for rpmExpand() */
2002-03-25 23:16:26 +03:00
# include "depends.h"
2009-10-02 11:47:28 +04:00
# include "al.h"
2010-07-12 10:20:27 +04:00
# include "rpmhash.h"
2002-03-25 23:16:26 +03:00
# include "debug.h"
/*@access Header@*/ /* XXX compared with NULL */
/*@access FD_t@*/ /* XXX compared with NULL */
/*@access rpmdb@*/ /* XXX compared with NULL */
/*@access rpmdbMatchIterator@*/ /* XXX compared with NULL */
/*@access rpmTransactionSet@*/
/*@access rpmDependencyConflict@*/
/*@access availableList@*/
2009-10-04 17:23:40 +04:00
/*@only@*/ char * printDepend ( const char * depend , const char * key ,
2002-03-25 23:16:26 +03:00
const char * keyEVR , int keyFlags )
/*@*/
{
char * tbuf , * t ;
size_t nb ;
nb = 0 ;
if ( depend ) nb + = strlen ( depend ) + 1 ;
if ( key ) nb + = strlen ( key ) ;
if ( keyFlags & RPMSENSE_SENSEMASK ) {
if ( nb ) nb + + ;
if ( keyFlags & RPMSENSE_LESS ) nb + + ;
if ( keyFlags & RPMSENSE_GREATER ) nb + + ;
if ( keyFlags & RPMSENSE_EQUAL ) nb + + ;
}
if ( keyEVR & & * keyEVR ) {
if ( nb ) nb + + ;
nb + = strlen ( keyEVR ) ;
}
t = tbuf = xmalloc ( nb + 1 ) ;
if ( depend ) {
while ( * depend ! = ' \0 ' ) * t + + = * depend + + ;
* t + + = ' ' ;
}
if ( key )
while ( * key ! = ' \0 ' ) * t + + = * key + + ;
if ( keyFlags & RPMSENSE_SENSEMASK ) {
if ( t ! = tbuf ) * t + + = ' ' ;
if ( keyFlags & RPMSENSE_LESS ) * t + + = ' < ' ;
if ( keyFlags & RPMSENSE_GREATER ) * t + + = ' > ' ;
if ( keyFlags & RPMSENSE_EQUAL ) * t + + = ' = ' ;
}
if ( keyEVR & & * keyEVR ) {
if ( t ! = tbuf ) * t + + = ' ' ;
while ( * keyEVR ! = ' \0 ' ) * t + + = * keyEVR + + ;
}
* t = ' \0 ' ;
return tbuf ;
}
2002-07-20 14:39:40 +04:00
/* parseEVR() moved to rpmvercmp.c */
2002-03-25 23:16:26 +03:00
const char * rpmNAME = PACKAGE ;
const char * rpmEVR = VERSION ;
int rpmFLAGS = RPMSENSE_EQUAL ;
2010-09-11 01:52:31 +04:00
# include "set.h"
2002-03-25 23:16:26 +03:00
int rpmRangesOverlap ( const char * AName , const char * AEVR , int AFlags ,
const char * BName , const char * BEVR , int BFlags )
{
2010-12-04 20:30:16 +03:00
const char * aDepend = NULL ;
const char * bDepend = NULL ;
2002-03-25 23:16:26 +03:00
char * aEVR , * bEVR ;
const char * aE , * aV , * aR , * bE , * bV , * bR ;
int result ;
int sense ;
/* Different names don't overlap. */
if ( strcmp ( AName , BName ) ) {
result = 0 ;
goto exit ;
}
2004-01-22 14:56:33 +03:00
/* Same name. If either A or B is an existence test, always overlap. */
if ( ! ( ( AFlags & RPMSENSE_SENSEMASK ) & & ( BFlags & RPMSENSE_SENSEMASK ) ) ) {
2002-03-25 23:16:26 +03:00
result = 1 ;
goto exit ;
}
2004-01-21 18:11:03 +03:00
if ( ! AEVR ) AEVR = " " ;
if ( ! BEVR ) BEVR = " " ;
2010-09-11 01:52:31 +04:00
if ( * AEVR & & * BEVR ) {
/* equal version strings => equal versions */
if ( strcmp ( AEVR , BEVR ) = = 0 ) {
sense = 0 ;
goto sense_result ;
}
}
/* something beats nothing */
else if ( * AEVR ) {
sense = 1 ;
goto sense_result ;
}
else if ( * BEVR ) {
sense = - 1 ;
goto sense_result ;
}
else {
/* both EVRs are non-existent or empty, always overlap */
2002-03-25 23:16:26 +03:00
result = 1 ;
goto exit ;
}
2010-09-11 01:52:31 +04:00
int aset = strncmp ( AEVR , " set: " , 4 ) = = 0 ;
int bset = strncmp ( BEVR , " set: " , 4 ) = = 0 ;
if ( aset & & bset ) {
sense = rpmsetcmp ( AEVR , BEVR ) ;
if ( sense < - 1 ) {
if ( sense = = - 3 )
rpmMessage ( RPMMESS_WARNING , _ ( " failed to decode %s \n " ) , AEVR ) ;
if ( sense = = - 4 )
rpmMessage ( RPMMESS_WARNING , _ ( " failed to decode %s \n " ) , BEVR ) ;
/* neither is subset of each other */
result = 0 ;
goto exit ;
}
}
else if ( aset | | bset ) {
/* no overlap between a set and non-set */
result = 0 ;
goto exit ;
}
else {
/* Both AEVR and BEVR exist. */
aEVR = xstrdup ( AEVR ) ;
parseEVR ( aEVR , & aE , & aV , & aR ) ;
bEVR = xstrdup ( BEVR ) ;
parseEVR ( bEVR , & bE , & bV , & bR ) ;
/* rpmEVRcmp() is also shared; the code moved to rpmvercmp.c */
2010-12-04 20:30:16 +03:00
if ( rpmIsDebug ( ) ) {
aDepend = printDepend ( NULL , AName , AEVR , AFlags ) ;
bDepend = printDepend ( NULL , BName , BEVR , BFlags ) ;
}
2010-09-11 01:52:31 +04:00
sense = rpmEVRcmp ( aE , aV , aR , aDepend , bE , bV , bR , bDepend ) ;
aEVR = _free ( aEVR ) ;
bEVR = _free ( bEVR ) ;
}
2002-03-25 23:16:26 +03:00
2010-09-11 01:52:31 +04:00
sense_result :
2002-03-25 23:16:26 +03:00
/* Detect overlap of {A,B} range. */
result = 0 ;
if ( sense < 0 & & ( ( AFlags & RPMSENSE_GREATER ) | | ( BFlags & RPMSENSE_LESS ) ) ) {
result = 1 ;
} else if ( sense > 0 & & ( ( AFlags & RPMSENSE_LESS ) | | ( BFlags & RPMSENSE_GREATER ) ) ) {
result = 1 ;
} else if ( sense = = 0 & &
( ( ( AFlags & RPMSENSE_EQUAL ) & & ( BFlags & RPMSENSE_EQUAL ) ) | |
( ( AFlags & RPMSENSE_LESS ) & & ( BFlags & RPMSENSE_LESS ) ) | |
( ( AFlags & RPMSENSE_GREATER ) & & ( BFlags & RPMSENSE_GREATER ) ) ) ) {
result = 1 ;
}
exit :
2010-12-04 20:30:16 +03:00
if ( rpmIsDebug ( ) ) {
if ( ! aDepend )
aDepend = printDepend ( NULL , AName , AEVR , AFlags ) ;
if ( ! bDepend )
bDepend = printDepend ( NULL , BName , BEVR , BFlags ) ;
rpmMessage ( RPMMESS_DEBUG , _ ( " %s A %s \t B %s \n " ) ,
( result ? _ ( " YES " ) : _ ( " NO " ) ) , aDepend , bDepend ) ;
aDepend = _free ( aDepend ) ;
bDepend = _free ( bDepend ) ;
}
2002-03-25 23:16:26 +03:00
return result ;
}
/*@-typeuse@*/
typedef int ( * dbrecMatch_t ) ( Header h , const char * reqName , const char * reqEVR , int reqFlags ) ;
/*@=typeuse@*/
static int rangeMatchesDepFlags ( Header h ,
const char * reqName , const char * reqEVR , int reqFlags )
/*@*/
{
HGE_t hge = ( HGE_t ) headerGetEntryMinMemory ;
HFD_t hfd = headerFreeData ;
rpmTagType pnt , pvt ;
const char * * provides ;
const char * * providesEVR ;
int_32 * provideFlags ;
int providesCount ;
int result ;
int i ;
if ( ! ( reqFlags & RPMSENSE_SENSEMASK ) | | ! reqEVR | | ! strlen ( reqEVR ) )
return 1 ;
/* Get provides information from header */
/*
* Rpm prior to 3.0 .3 does not have versioned provides .
* If no provides version info is available , match any requires .
*/
if ( ! hge ( h , RPMTAG_PROVIDEVERSION , & pvt ,
( void * * ) & providesEVR , & providesCount ) )
return 1 ;
( void ) hge ( h , RPMTAG_PROVIDEFLAGS , NULL , ( void * * ) & provideFlags , NULL ) ;
if ( ! hge ( h , RPMTAG_PROVIDENAME , & pnt , ( void * * ) & provides , & providesCount ) )
{
providesEVR = hfd ( providesEVR , pvt ) ;
return 0 ; /* XXX should never happen */
}
result = 0 ;
for ( i = 0 ; i < providesCount ; i + + ) {
/* Filter out provides that came along for the ride. */
if ( strcmp ( provides [ i ] , reqName ) )
continue ;
2004-01-22 15:43:59 +03:00
if ( ! ( provideFlags [ i ] & RPMSENSE_SENSEMASK ) )
2009-10-04 12:19:04 +04:00
provideFlags [ i ] | = RPMSENSE_EQUAL ; /* ALT21-139-g6cb9a9a */
2002-03-25 23:16:26 +03:00
result = rpmRangesOverlap ( provides [ i ] , providesEVR [ i ] , provideFlags [ i ] ,
reqName , reqEVR , reqFlags ) ;
/* If this provide matches the require, we're done. */
if ( result )
break ;
}
provides = hfd ( provides , pnt ) ;
providesEVR = hfd ( providesEVR , pvt ) ;
return result ;
}
int headerMatchesDepFlags ( Header h ,
const char * reqName , const char * reqEVR , int reqFlags )
{
HGE_t hge = ( HGE_t ) headerGetEntryMinMemory ;
const char * name , * version , * release ;
int_32 * epoch ;
const char * pkgEVR ;
char * p ;
int pkgFlags = RPMSENSE_EQUAL ;
if ( ! ( ( reqFlags & RPMSENSE_SENSEMASK ) & & reqEVR & & * reqEVR ) )
return 1 ;
/* Get package information from header */
( void ) headerNVR ( h , & name , & version , & release ) ;
pkgEVR = p = alloca ( 21 + strlen ( version ) + 1 + strlen ( release ) + 1 ) ;
* p = ' \0 ' ;
if ( hge ( h , RPMTAG_EPOCH , NULL , ( void * * ) & epoch , NULL ) ) {
sprintf ( p , " %d: " , * epoch ) ;
while ( * p ! = ' \0 ' )
p + + ;
}
( void ) stpcpy ( stpcpy ( stpcpy ( p , version ) , " - " ) , release ) ;
return rpmRangesOverlap ( name , pkgEVR , pkgFlags , reqName , reqEVR , reqFlags ) ;
}
rpmTransactionSet rpmtransCreateSet ( rpmdb rpmdb , const char * rootDir )
{
rpmTransactionSet ts ;
int rootLen ;
if ( ! rootDir ) rootDir = " " ;
ts = xcalloc ( 1 , sizeof ( * ts ) ) ;
ts - > filesystemCount = 0 ;
ts - > filesystems = NULL ;
ts - > di = NULL ;
/*@-assignexpose@*/
ts - > rpmdb = rpmdb ;
/*@=assignexpose@*/
ts - > scriptFd = NULL ;
ts - > id = 0 ;
ts - > numRemovedPackages = 0 ;
2009-10-04 09:57:34 +04:00
ts - > removedPackages = NULL ;
2002-03-25 23:16:26 +03:00
/* This canonicalizes the root */
rootLen = strlen ( rootDir ) ;
if ( ! ( rootLen & & rootDir [ rootLen - 1 ] = = ' / ' ) ) {
char * t ;
t = alloca ( rootLen + 2 ) ;
* t = ' \0 ' ;
( void ) stpcpy ( stpcpy ( t , rootDir ) , " / " ) ;
rootDir = t ;
}
ts - > rootDir = xstrdup ( rootDir ) ;
ts - > currDir = NULL ;
ts - > chrootDone = 0 ;
alCreate ( & ts - > addedPackages ) ;
2009-10-04 20:52:03 +04:00
alCreate ( & ts - > erasedPackages ) ;
2002-03-25 23:16:26 +03:00
ts - > orderCount = 0 ;
2009-10-04 09:57:34 +04:00
ts - > order = NULL ;
2002-03-25 23:16:26 +03:00
2010-03-31 19:36:41 +04:00
ts - > selinuxEnabled = is_selinux_enabled ( ) > 0 ;
2002-03-25 23:16:26 +03:00
return ts ;
}
/**
* Compare removed package instances ( qsort / bsearch ) .
* @ param a 1 st instance address
* @ param b 2 nd instance address
* @ return result of comparison
*/
static int intcmp ( const void * a , const void * b ) /*@*/
{
const int * aptr = a ;
const int * bptr = b ;
int rc = ( * aptr - * bptr ) ;
return rc ;
}
/**
* Add removed package instance to ordered transaction set .
* @ param ts transaction set
* @ param dboffset rpm database instance
* @ param depends installed package of pair ( or - 1 on erase )
* @ return 0 on success
*/
static int removePackage ( rpmTransactionSet ts , int dboffset , int depends )
/*@modifies ts @*/
{
/* Filter out duplicate erasures. */
if ( ts - > numRemovedPackages > 0 & & ts - > removedPackages ! = NULL ) {
if ( bsearch ( & dboffset , ts - > removedPackages , ts - > numRemovedPackages ,
sizeof ( int ) , intcmp ) ! = NULL )
return 0 ;
}
2009-10-04 20:52:03 +04:00
/* Fetch header. */
rpmdbMatchIterator mi = rpmdbInitIterator ( ts - > rpmdb ,
RPMDBI_PACKAGES , & dboffset , sizeof ( dboffset ) ) ;
Header h = rpmdbNextIterator ( mi ) ;
if ( h )
h = headerLink ( h ) ;
mi = rpmdbFreeIterator ( mi ) ;
if ( h = = NULL )
return 1 ;
struct availablePackage * alp =
alAddPackage ( & ts - > erasedPackages , h , NULL , NULL , NULL ) ;
int alNum = alp - ts - > erasedPackages . list ;
2009-10-04 09:57:34 +04:00
AUTO_REALLOC ( ts - > removedPackages , ts - > numRemovedPackages ) ;
ts - > removedPackages [ ts - > numRemovedPackages + + ] = dboffset ;
qsort ( ts - > removedPackages , ts - > numRemovedPackages , sizeof ( int ) , intcmp ) ;
2002-03-25 23:16:26 +03:00
2009-10-04 09:57:34 +04:00
AUTO_REALLOC ( ts - > order , ts - > orderCount ) ;
2009-10-04 20:52:03 +04:00
transactionElement te = & ts - > order [ ts - > orderCount + + ] ;
te - > type = TR_REMOVED ;
te - > u . removed . dboffset = dboffset ;
te - > u . removed . dependsOnIndex = depends ;
te - > u . removed . erasedIndex = alNum ;
2002-03-25 23:16:26 +03:00
return 0 ;
}
2005-10-17 17:23:09 +04:00
static int rpmDigestCompare ( Header first , Header second )
{
const char * one , * two ;
if ( ! headerGetEntry ( first , RPMTAG_SHA1HEADER , NULL , ( void * * ) & one , NULL ) )
one = NULL ;
if ( ! headerGetEntry ( second , RPMTAG_SHA1HEADER , NULL , ( void * * ) & two , NULL ) )
two = NULL ;
if ( one & & two )
return strcmp ( one , two ) ;
if ( one & & ! two )
return 1 ;
if ( ! one & & two )
return - 1 ;
return 0 ;
}
2002-03-25 23:16:26 +03:00
int rpmtransAddPackage ( rpmTransactionSet ts , Header h , FD_t fd ,
const void * key , int upgrade , rpmRelocation * relocs )
{
HGE_t hge = ( HGE_t ) headerGetEntryMinMemory ;
HFD_t hfd = headerFreeData ;
rpmTagType ont , ovt ;
/* this is an install followed by uninstalls */
const char * name ;
int count ;
const char * * obsoletes ;
/*
* FIXME : handling upgrades like this is * almost * okay . It doesn ' t
* check to make sure we ' re upgrading to a newer version , and it
* makes it difficult to generate a return code based on the number of
* packages which failed .
*/
2009-10-02 07:58:27 +04:00
struct availablePackage * alp =
alAddPackage ( & ts - > addedPackages , h , key , fd , relocs ) ;
int alNum = alp - ts - > addedPackages . list ;
2009-10-04 09:57:34 +04:00
AUTO_REALLOC ( ts - > order , ts - > orderCount ) ;
ts - > order [ ts - > orderCount ] . type = TR_ADDED ;
2002-03-25 23:16:26 +03:00
ts - > order [ ts - > orderCount + + ] . u . addedIndex = alNum ;
if ( ! upgrade | | ts - > rpmdb = = NULL )
return 0 ;
/* XXX binary rpms always have RPMTAG_SOURCERPM, source rpms do not */
if ( headerIsEntry ( h , RPMTAG_SOURCEPACKAGE ) )
return 0 ;
( void ) headerNVR ( h , & name , NULL , NULL ) ;
{ rpmdbMatchIterator mi ;
Header h2 ;
mi = rpmdbInitIterator ( ts - > rpmdb , RPMTAG_NAME , name , 0 ) ;
while ( ( h2 = rpmdbNextIterator ( mi ) ) ! = NULL ) {
2005-10-17 17:23:09 +04:00
if ( rpmDigestCompare ( h , h2 ) | | rpmVersionCompare ( h , h2 ) )
2002-03-25 23:16:26 +03:00
( void ) removePackage ( ts , rpmdbGetIteratorOffset ( mi ) , alNum ) ;
}
mi = rpmdbFreeIterator ( mi ) ;
}
if ( hge ( h , RPMTAG_OBSOLETENAME , & ont , ( void * * ) & obsoletes , & count ) ) {
const char * * obsoletesEVR ;
int_32 * obsoletesFlags ;
int j ;
( void ) hge ( h , RPMTAG_OBSOLETEVERSION , & ovt , ( void * * ) & obsoletesEVR ,
NULL ) ;
( void ) hge ( h , RPMTAG_OBSOLETEFLAGS , NULL , ( void * * ) & obsoletesFlags ,
NULL ) ;
for ( j = 0 ; j < count ; j + + ) {
/* XXX avoid self-obsoleting packages. */
if ( ! strcmp ( name , obsoletes [ j ] ) )
continue ;
{ rpmdbMatchIterator mi ;
Header h2 ;
mi = rpmdbInitIterator ( ts - > rpmdb , RPMTAG_NAME , obsoletes [ j ] , 0 ) ;
( void ) rpmdbPruneIterator ( mi ,
ts - > removedPackages , ts - > numRemovedPackages , 1 ) ;
while ( ( h2 = rpmdbNextIterator ( mi ) ) ! = NULL ) {
/*
* Rpm prior to 3.0 .3 does not have versioned obsoletes .
* If no obsoletes version info is available , match all names .
*/
if ( obsoletesEVR = = NULL | |
headerMatchesDepFlags ( h2 ,
obsoletes [ j ] , obsoletesEVR [ j ] , obsoletesFlags [ j ] ) )
{
( void ) removePackage ( ts , rpmdbGetIteratorOffset ( mi ) , alNum ) ;
}
}
mi = rpmdbFreeIterator ( mi ) ;
}
}
obsoletesEVR = hfd ( obsoletesEVR , ovt ) ;
obsoletes = hfd ( obsoletes , ont ) ;
}
return 0 ;
}
int rpmtransRemovePackage ( rpmTransactionSet ts , int dboffset )
{
return removePackage ( ts , dboffset , - 1 ) ;
}
rpmTransactionSet rpmtransFree ( rpmTransactionSet ts )
{
if ( ts ) {
alFree ( & ts - > addedPackages ) ;
2009-10-04 20:52:03 +04:00
alFree ( & ts - > erasedPackages ) ;
2002-03-25 23:16:26 +03:00
ts - > di = _free ( ts - > di ) ;
ts - > removedPackages = _free ( ts - > removedPackages ) ;
ts - > order = _free ( ts - > order ) ;
if ( ts - > scriptFd ! = NULL )
ts - > scriptFd =
fdFree ( ts - > scriptFd , " rpmtransSetScriptFd (rpmtransFree " ) ;
ts - > rootDir = _free ( ts - > rootDir ) ;
ts - > currDir = _free ( ts - > currDir ) ;
ts = _free ( ts ) ;
}
return NULL ;
}
rpmDependencyConflict rpmdepFreeConflicts ( rpmDependencyConflict conflicts ,
int numConflicts )
{
int i ;
if ( conflicts )
for ( i = 0 ; i < numConflicts ; i + + ) {
conflicts [ i ] . byHeader = headerFree ( conflicts [ i ] . byHeader ) ;
conflicts [ i ] . byName = _free ( conflicts [ i ] . byName ) ;
conflicts [ i ] . byVersion = _free ( conflicts [ i ] . byVersion ) ;
conflicts [ i ] . byRelease = _free ( conflicts [ i ] . byRelease ) ;
conflicts [ i ] . needsName = _free ( conflicts [ i ] . needsName ) ;
conflicts [ i ] . needsVersion = _free ( conflicts [ i ] . needsVersion ) ;
}
return ( conflicts = _free ( conflicts ) ) ;
}
2010-08-19 00:31:09 +04:00
static __thread
hashTable dbProvCache ;
2010-07-05 14:35:34 +04:00
/* Cached rpmdb provide lookup, returns 0 if satisfied, 1 otherwise */
static
int dbSatisfiesDepend ( rpmTransactionSet ts ,
const char * keyName , const char * keyEVR , int keyFlags )
{
rpmdbMatchIterator mi ;
Header h ;
int rc = 1 ;
2010-07-05 23:31:58 +04:00
/* Lookup dbProvCache by keyName. */
const void * * cacheData = NULL ;
if ( htGetEntry ( dbProvCache , keyName , & cacheData , NULL , NULL ) = = 0 ) {
if ( * cacheData = = NULL )
/* cache value is NULL (for "no"), the dependency is not satisfied */
return 1 ;
if ( ( keyFlags & RPMSENSE_SENSEMASK ) = = 0 )
/* cache value is "yes", unversioned dependency is satisfied */
return 0 ;
}
2010-07-05 14:35:34 +04:00
if ( * keyName = = ' / ' & & ( keyFlags & RPMSENSE_SENSEMASK ) = = 0 ) {
mi = rpmdbInitIterator ( ts - > rpmdb , RPMTAG_BASENAMES , keyName , 0 ) ;
rpmdbPruneIterator ( mi , ts - > removedPackages , ts - > numRemovedPackages , 1 ) ;
while ( ( h = rpmdbNextIterator ( mi ) ) ! = NULL ) {
rc = 0 ;
break ;
}
mi = rpmdbFreeIterator ( mi ) ;
}
if ( rc = = 1 ) {
mi = rpmdbInitIterator ( ts - > rpmdb , RPMTAG_PROVIDENAME , keyName , 0 ) ;
rpmdbPruneIterator ( mi , ts - > removedPackages , ts - > numRemovedPackages , 1 ) ;
while ( ( h = rpmdbNextIterator ( mi ) ) ! = NULL ) {
if ( rangeMatchesDepFlags ( h , keyName , keyEVR , keyFlags ) ) {
rc = 0 ;
break ;
}
2010-07-05 23:31:58 +04:00
else {
/* version did not match */
rc = - 1 ;
}
2010-07-05 14:35:34 +04:00
}
mi = rpmdbFreeIterator ( mi ) ;
}
2010-07-05 23:31:58 +04:00
/* Update dbProvCache.
* When versions did not match , it is still okay to say " yes " for the name . */
if ( cacheData = = NULL )
2010-08-18 11:56:59 +04:00
/* XXX keyName points to header memory, no need for strdup */
htAddEntry ( dbProvCache , keyName , rc < 1 ? " yes " : NULL ) ;
2010-07-05 23:31:58 +04:00
return rc ? 1 : 0 ;
2010-07-05 14:35:34 +04:00
}
2002-03-25 23:16:26 +03:00
/**
* Check key for an unsatisfied dependency .
* @ todo Eliminate rpmrc provides .
* @ param ts transaction set
depends.c: permit self-conflicting packages
Only for the last two weeks or so, the issues has been raised twice.
By specifying "Provides: foo, Conflicts: foo", people expect that
other packages which provide "foo" will not be installed along with
the package. What people don't anticipate is that the package will
conflict with itself, and will not be installed at all. This is where
apt and rpm differ. In apt, "conflicts may never self match". In rpm,
Requires and Conflicts are handled in exactly the same way, except that
Requires should match, and Conflicts should not match (I call this
a symmetry). Both can match against the package they come from.
So, to permit self-conflicting packages, I have to break the symmetry
and pass additional argument which indicates the type of dependency
being processed (either Requires or Conflicts). The code is then
adjusted to discard self-matching Conflicts.
Obsoletes should be handled specially, too. In tsSatisfiesDepend(),
I attempt to handle the Obsoletes case as well. It is rather
unfortunate that, in rpmdepCheck(), Obsoletes are simply not checked
just yet.
2010-08-19 01:04:06 +04:00
* @ param h header the dependency comes from
* @ param tag RPMTAG_REQUIRENAME , PROVIDENAME or OBSOLETENAME
2002-03-25 23:16:26 +03:00
* @ param keyDepend dependency string representation
* @ param keyName dependency name string
* @ param keyEVR dependency [ epoch : ] version [ - release ] string
* @ param keyFlags dependency logical range qualifiers
* @ return 0 if satisfied , 1 if not satisfied , 2 if error
*/
2010-07-05 14:35:34 +04:00
static int tsSatisfiesDepend ( rpmTransactionSet ts ,
depends.c: permit self-conflicting packages
Only for the last two weeks or so, the issues has been raised twice.
By specifying "Provides: foo, Conflicts: foo", people expect that
other packages which provide "foo" will not be installed along with
the package. What people don't anticipate is that the package will
conflict with itself, and will not be installed at all. This is where
apt and rpm differ. In apt, "conflicts may never self match". In rpm,
Requires and Conflicts are handled in exactly the same way, except that
Requires should match, and Conflicts should not match (I call this
a symmetry). Both can match against the package they come from.
So, to permit self-conflicting packages, I have to break the symmetry
and pass additional argument which indicates the type of dependency
being processed (either Requires or Conflicts). The code is then
adjusted to discard self-matching Conflicts.
Obsoletes should be handled specially, too. In tsSatisfiesDepend(),
I attempt to handle the Obsoletes case as well. It is rather
unfortunate that, in rpmdepCheck(), Obsoletes are simply not checked
just yet.
2010-08-19 01:04:06 +04:00
Header h , rpmTag tag , const char * keyDepend ,
2009-09-30 16:11:11 +04:00
const char * keyName , const char * keyEVR , int keyFlags )
/*@modifies ts @*/
2002-03-25 23:16:26 +03:00
{
depends.c: permit self-conflicting packages
Only for the last two weeks or so, the issues has been raised twice.
By specifying "Provides: foo, Conflicts: foo", people expect that
other packages which provide "foo" will not be installed along with
the package. What people don't anticipate is that the package will
conflict with itself, and will not be installed at all. This is where
apt and rpm differ. In apt, "conflicts may never self match". In rpm,
Requires and Conflicts are handled in exactly the same way, except that
Requires should match, and Conflicts should not match (I call this
a symmetry). Both can match against the package they come from.
So, to permit self-conflicting packages, I have to break the symmetry
and pass additional argument which indicates the type of dependency
being processed (either Requires or Conflicts). The code is then
adjusted to discard self-matching Conflicts.
Obsoletes should be handled specially, too. In tsSatisfiesDepend(),
I attempt to handle the Obsoletes case as well. It is rather
unfortunate that, in rpmdepCheck(), Obsoletes are simply not checked
just yet.
2010-08-19 01:04:06 +04:00
const char * keyType ;
switch ( tag ) {
case RPMTAG_REQUIRENAME :
keyType = " Requires " ;
break ;
case RPMTAG_CONFLICTNAME :
keyType = " Conflicts " ;
break ;
default :
assert ( tag = = RPMTAG_OBSOLETENAME ) ;
keyType = " Obsoletes " ;
break ;
}
2002-03-25 23:16:26 +03:00
/*
* New features in rpm packaging implicitly add versioned dependencies
* on rpmlib provides . The dependencies look like " rpmlib(YaddaYadda) " .
* Check those dependencies now .
*/
if ( ! strncmp ( keyName , " rpmlib( " , sizeof ( " rpmlib( " ) - 1 ) ) {
if ( rpmCheckRpmlibProvides ( keyName , keyEVR , keyFlags ) ) {
rpmMessage ( RPMMESS_DEBUG , _ ( " %s: %-45s YES (rpmlib provides) \n " ) ,
keyType , keyDepend + 2 ) ;
2010-07-05 14:35:34 +04:00
return 0 ;
2002-03-25 23:16:26 +03:00
}
goto unsatisfied ;
}
depends.c: permit self-conflicting packages
Only for the last two weeks or so, the issues has been raised twice.
By specifying "Provides: foo, Conflicts: foo", people expect that
other packages which provide "foo" will not be installed along with
the package. What people don't anticipate is that the package will
conflict with itself, and will not be installed at all. This is where
apt and rpm differ. In apt, "conflicts may never self match". In rpm,
Requires and Conflicts are handled in exactly the same way, except that
Requires should match, and Conflicts should not match (I call this
a symmetry). Both can match against the package they come from.
So, to permit self-conflicting packages, I have to break the symmetry
and pass additional argument which indicates the type of dependency
being processed (either Requires or Conflicts). The code is then
adjusted to discard self-matching Conflicts.
Obsoletes should be handled specially, too. In tsSatisfiesDepend(),
I attempt to handle the Obsoletes case as well. It is rather
unfortunate that, in rpmdepCheck(), Obsoletes are simply not checked
just yet.
2010-08-19 01:04:06 +04:00
struct availablePackage * * all =
alAllSatisfiesDepend ( & ts - > addedPackages , keyName , keyEVR , keyFlags ) ;
if ( all ) {
int ret = 1 ;
if ( tag = = RPMTAG_REQUIRENAME )
ret = 0 ;
else {
struct availablePackage * * alpp ;
for ( alpp = all ; * alpp ; alpp + + ) {
// Conflicts are Obsoletes do not self match.
if ( ( * alpp ) - > h = = h )
continue ;
// Obsoletes match only against packags names, not Provides.
if ( tag = = RPMTAG_OBSOLETENAME & & strcmp ( ( * alpp ) - > name , keyName ) )
continue ;
ret = 0 ;
break ;
}
}
all = _free ( all ) ;
if ( ret = = 0 ) {
rpmMessage ( RPMMESS_DEBUG , _ ( " %s: %-45s YES (added provides) \n " ) ,
keyType , keyDepend + 2 ) ;
return 0 ;
}
2002-03-25 23:16:26 +03:00
}
2010-08-19 00:31:09 +04:00
if ( dbSatisfiesDepend ( ts , keyName , keyEVR , keyFlags ) = = 0 ) {
2010-07-05 14:35:34 +04:00
rpmMessage ( RPMMESS_DEBUG , _ ( " %s: %-45s YES (rpmdb provides) \n " ) ,
2002-03-25 23:16:26 +03:00
keyType , keyDepend + 2 ) ;
2010-07-05 14:35:34 +04:00
return 0 ;
2002-03-25 23:16:26 +03:00
}
unsatisfied :
rpmMessage ( RPMMESS_DEBUG , _ ( " %s: %-45s NO \n " ) , keyType , keyDepend + 2 ) ;
2010-07-05 14:35:34 +04:00
return 1 ;
2002-03-25 23:16:26 +03:00
}
/**
* Check header requires / conflicts against against installed + added packages .
* @ param ts transaction set
* @ param psp dependency problems
* @ param h header to check
* @ param keyName dependency name
* @ return 0 no problems found
*/
static int checkPackageDeps ( rpmTransactionSet ts , problemsSet psp ,
2009-03-10 14:27:38 +03:00
Header h , const char * keyName )
2002-03-25 23:16:26 +03:00
/*@modifies ts, h, psp */
{
HGE_t hge = ( HGE_t ) headerGetEntryMinMemory ;
HFD_t hfd = headerFreeData ;
rpmTagType rnt , rvt ;
rpmTagType cnt , cvt ;
const char * name , * version , * release ;
const char * * requires ;
const char * * requiresEVR = NULL ;
int_32 * requireFlags = NULL ;
int requiresCount = 0 ;
const char * * conflicts ;
const char * * conflictsEVR = NULL ;
int_32 * conflictFlags = NULL ;
int conflictsCount = 0 ;
rpmTagType type ;
int i , rc ;
int ourrc = 0 ;
( void ) headerNVR ( h , & name , & version , & release ) ;
if ( ! hge ( h , RPMTAG_REQUIRENAME , & rnt , ( void * * ) & requires , & requiresCount ) )
{
requiresCount = 0 ;
rvt = RPM_STRING_ARRAY_TYPE ;
} else {
( void ) hge ( h , RPMTAG_REQUIREFLAGS , NULL , ( void * * ) & requireFlags , NULL ) ;
( void ) hge ( h , RPMTAG_REQUIREVERSION , & rvt , ( void * * ) & requiresEVR , NULL ) ;
}
for ( i = 0 ; i < requiresCount & & ! ourrc ; i + + ) {
const char * keyDepend ;
/* Filter out requires that came along for the ride. */
if ( keyName & & strcmp ( keyName , requires [ i ] ) )
continue ;
keyDepend = printDepend ( " R " ,
requires [ i ] , requiresEVR [ i ] , requireFlags [ i ] ) ;
depends.c: permit self-conflicting packages
Only for the last two weeks or so, the issues has been raised twice.
By specifying "Provides: foo, Conflicts: foo", people expect that
other packages which provide "foo" will not be installed along with
the package. What people don't anticipate is that the package will
conflict with itself, and will not be installed at all. This is where
apt and rpm differ. In apt, "conflicts may never self match". In rpm,
Requires and Conflicts are handled in exactly the same way, except that
Requires should match, and Conflicts should not match (I call this
a symmetry). Both can match against the package they come from.
So, to permit self-conflicting packages, I have to break the symmetry
and pass additional argument which indicates the type of dependency
being processed (either Requires or Conflicts). The code is then
adjusted to discard self-matching Conflicts.
Obsoletes should be handled specially, too. In tsSatisfiesDepend(),
I attempt to handle the Obsoletes case as well. It is rather
unfortunate that, in rpmdepCheck(), Obsoletes are simply not checked
just yet.
2010-08-19 01:04:06 +04:00
rc = tsSatisfiesDepend ( ts , h , RPMTAG_REQUIRENAME , keyDepend ,
2010-08-18 14:24:07 +04:00
keyName ? : requires [ i ] , // points to added/erased header memory
requiresEVR [ i ] , requireFlags [ i ] ) ;
2002-03-25 23:16:26 +03:00
switch ( rc ) {
case 0 : /* requirements are satisfied. */
break ;
case 1 : /* requirements are not satisfied. */
rpmMessage ( RPMMESS_DEBUG , _ ( " package %s-%s-%s require not satisfied: %s \n " ) ,
name , version , release , keyDepend + 2 ) ;
2009-10-04 09:57:34 +04:00
AUTO_REALLOC ( psp - > problems , psp - > num ) ;
2002-03-25 23:16:26 +03:00
{ rpmDependencyConflict pp = psp - > problems + psp - > num ;
pp - > byHeader = headerLink ( h ) ;
pp - > byName = xstrdup ( name ) ;
pp - > byVersion = xstrdup ( version ) ;
pp - > byRelease = xstrdup ( release ) ;
pp - > needsName = xstrdup ( requires [ i ] ) ;
pp - > needsVersion = xstrdup ( requiresEVR [ i ] ) ;
pp - > needsFlags = requireFlags [ i ] ;
pp - > sense = RPMDEP_SENSE_REQUIRES ;
}
psp - > num + + ;
break ;
case 2 : /* something went wrong! */
default :
ourrc = 1 ;
break ;
}
keyDepend = _free ( keyDepend ) ;
}
if ( requiresCount ) {
requiresEVR = hfd ( requiresEVR , rvt ) ;
requires = hfd ( requires , rnt ) ;
}
if ( ! hge ( h , RPMTAG_CONFLICTNAME , & cnt , ( void * * ) & conflicts , & conflictsCount ) )
{
conflictsCount = 0 ;
cvt = RPM_STRING_ARRAY_TYPE ;
} else {
( void ) hge ( h , RPMTAG_CONFLICTFLAGS , & type ,
( void * * ) & conflictFlags , & conflictsCount ) ;
( void ) hge ( h , RPMTAG_CONFLICTVERSION , & cvt ,
( void * * ) & conflictsEVR , & conflictsCount ) ;
}
for ( i = 0 ; i < conflictsCount & & ! ourrc ; i + + ) {
const char * keyDepend ;
/* Filter out conflicts that came along for the ride. */
if ( keyName & & strcmp ( keyName , conflicts [ i ] ) )
continue ;
keyDepend = printDepend ( " C " , conflicts [ i ] , conflictsEVR [ i ] , conflictFlags [ i ] ) ;
depends.c: permit self-conflicting packages
Only for the last two weeks or so, the issues has been raised twice.
By specifying "Provides: foo, Conflicts: foo", people expect that
other packages which provide "foo" will not be installed along with
the package. What people don't anticipate is that the package will
conflict with itself, and will not be installed at all. This is where
apt and rpm differ. In apt, "conflicts may never self match". In rpm,
Requires and Conflicts are handled in exactly the same way, except that
Requires should match, and Conflicts should not match (I call this
a symmetry). Both can match against the package they come from.
So, to permit self-conflicting packages, I have to break the symmetry
and pass additional argument which indicates the type of dependency
being processed (either Requires or Conflicts). The code is then
adjusted to discard self-matching Conflicts.
Obsoletes should be handled specially, too. In tsSatisfiesDepend(),
I attempt to handle the Obsoletes case as well. It is rather
unfortunate that, in rpmdepCheck(), Obsoletes are simply not checked
just yet.
2010-08-19 01:04:06 +04:00
rc = tsSatisfiesDepend ( ts , h , RPMTAG_CONFLICTNAME , keyDepend ,
2010-08-18 14:24:07 +04:00
keyName ? : conflicts [ i ] , // points to added/erased header memory
conflictsEVR [ i ] , conflictFlags [ i ] ) ;
2002-03-25 23:16:26 +03:00
/* 1 == unsatisfied, 0 == satsisfied */
switch ( rc ) {
case 0 : /* conflicts exist. */
rpmMessage ( RPMMESS_DEBUG , _ ( " package %s conflicts: %s \n " ) ,
name , keyDepend + 2 ) ;
2009-10-04 09:57:34 +04:00
AUTO_REALLOC ( psp - > problems , psp - > num ) ;
2002-03-25 23:16:26 +03:00
{ rpmDependencyConflict pp = psp - > problems + psp - > num ;
pp - > byHeader = headerLink ( h ) ;
pp - > byName = xstrdup ( name ) ;
pp - > byVersion = xstrdup ( version ) ;
pp - > byRelease = xstrdup ( release ) ;
pp - > needsName = xstrdup ( conflicts [ i ] ) ;
pp - > needsVersion = xstrdup ( conflictsEVR [ i ] ) ;
pp - > needsFlags = conflictFlags [ i ] ;
pp - > sense = RPMDEP_SENSE_CONFLICTS ;
}
psp - > num + + ;
break ;
case 1 : /* conflicts don't exist. */
break ;
case 2 : /* something went wrong! */
default :
ourrc = 1 ;
break ;
}
keyDepend = _free ( keyDepend ) ;
}
if ( conflictsCount ) {
conflictsEVR = hfd ( conflictsEVR , cvt ) ;
conflicts = hfd ( conflicts , cnt ) ;
}
return ourrc ;
}
/**
2010-07-05 16:34:16 +04:00
* Erasing : check provides key against tag ( requires or conflicts ) matches .
2002-03-25 23:16:26 +03:00
* @ param ts transaction set
* @ param psp dependency problems
2010-07-05 16:34:16 +04:00
* @ param tag RPMTAG_REQUIRENAME or RPMTAG_CONFLICTNAME
* @ param key requires name
2002-03-25 23:16:26 +03:00
* @ return 0 no problems found
*/
2010-07-05 16:34:16 +04:00
static int checkDependent ( rpmTransactionSet ts , problemsSet psp ,
rpmTag tag , const char * key )
/*@modifies ts, psp @*/
2002-03-25 23:16:26 +03:00
{
2010-07-05 16:34:16 +04:00
rpmdbMatchIterator mi = rpmdbInitIterator ( ts - > rpmdb , tag , key , 0 ) ;
rpmdbPruneIterator ( mi , ts - > removedPackages , ts - > numRemovedPackages , 1 ) ;
2002-03-25 23:16:26 +03:00
Header h ;
int rc = 0 ;
while ( ( h = rpmdbNextIterator ( mi ) ) ! = NULL ) {
2010-08-19 00:31:09 +04:00
if ( checkPackageDeps ( ts , psp , h , key ) ) {
2002-03-25 23:16:26 +03:00
rc = 1 ;
break ;
}
}
mi = rpmdbFreeIterator ( mi ) ;
return rc ;
}
int rpmdepCheck ( rpmTransactionSet ts ,
rpmDependencyConflict * conflicts , int * numConflicts )
{
HGE_t hge = ( HGE_t ) headerGetEntryMinMemory ;
HFD_t hfd = headerFreeData ;
struct availablePackage * p ;
problemsSet ps ;
int i , j ;
2010-08-19 03:00:44 +04:00
int rc = 0 ;
2002-03-25 23:16:26 +03:00
ps = xcalloc ( 1 , sizeof ( * ps ) ) ;
ps - > num = 0 ;
2009-10-04 09:57:34 +04:00
ps - > problems = NULL ;
2002-03-25 23:16:26 +03:00
* conflicts = NULL ;
* numConflicts = 0 ;
2010-07-12 10:20:27 +04:00
/* XXX figure some kind of heuristic for the cache size */
2010-08-19 00:31:09 +04:00
dbProvCache = htCreate ( 1024 , hashFunctionString , hashEqualityString ) ;
2010-07-12 10:20:27 +04:00
2002-03-25 23:16:26 +03:00
/*
* Look at all of the added packages and make sure their dependencies
* are satisfied .
*/
if ( ( p = ts - > addedPackages . list ) ! = NULL )
2010-07-05 21:27:23 +04:00
for ( i = 0 ; i < ts - > addedPackages . size ; i + + , p + + )
2002-03-25 23:16:26 +03:00
{
rpmMessage ( RPMMESS_DEBUG , " ========== +++ %s-%s-%s \n " ,
p - > name , p - > version , p - > release ) ;
2010-08-19 00:31:09 +04:00
rc = checkPackageDeps ( ts , ps , p - > h , NULL ) ;
2002-03-25 23:16:26 +03:00
if ( rc )
goto exit ;
for ( j = 0 ; j < p - > providesCount ; j + + ) {
/* Adding: check provides key against conflicts matches. */
2010-08-19 00:31:09 +04:00
if ( ! checkDependent ( ts , ps , RPMTAG_CONFLICTNAME , p - > provides [ j ] ) )
2002-03-25 23:16:26 +03:00
continue ;
rc = 1 ;
/*@innerbreak@*/ break ;
}
if ( rc )
goto exit ;
}
/*
* Look at the removed packages and make sure they aren ' t critical .
*/
2010-07-05 21:27:23 +04:00
if ( ( p = ts - > erasedPackages . list ) ! = NULL )
for ( i = 0 ; i < ts - > erasedPackages . size ; i + + , p + + )
{
2010-08-19 03:00:44 +04:00
rpmMessage ( RPMMESS_DEBUG , " ========== --- %s-%s-%s \n " ,
p - > name , p - > version , p - > release ) ;
for ( j = 0 ; j < p - > providesCount ; j + + ) {
/* Erasing: check provides against requiredby matches. */
if ( ! checkDependent ( ts , ps , RPMTAG_REQUIRENAME , p - > provides [ j ] ) )
continue ;
rc = 1 ;
/*@innerbreak@*/ break ;
2002-03-25 23:16:26 +03:00
}
{ const char * * baseNames , * * dirNames ;
int_32 * dirIndexes ;
rpmTagType dnt , bnt ;
int fileCount ;
char * fileName = NULL ;
int fileAlloced = 0 ;
int len ;
2010-08-19 03:00:44 +04:00
Header h = p - > h ;
2002-03-25 23:16:26 +03:00
if ( hge ( h , RPMTAG_BASENAMES , & bnt , ( void * * ) & baseNames , & fileCount ) )
{
( void ) hge ( h , RPMTAG_DIRNAMES , & dnt , ( void * * ) & dirNames , NULL ) ;
( void ) hge ( h , RPMTAG_DIRINDEXES , NULL , ( void * * ) & dirIndexes ,
NULL ) ;
for ( j = 0 ; j < fileCount ; j + + ) {
len = strlen ( baseNames [ j ] ) + 1 +
strlen ( dirNames [ dirIndexes [ j ] ] ) ;
if ( len > fileAlloced ) {
fileAlloced = len * 2 ;
fileName = xrealloc ( fileName , fileAlloced ) ;
}
* fileName = ' \0 ' ;
( void ) stpcpy ( stpcpy ( fileName , dirNames [ dirIndexes [ j ] ] ) , baseNames [ j ] ) ;
/* Erasing: check filename against requiredby matches. */
2010-08-19 00:31:09 +04:00
if ( ! checkDependent ( ts , ps , RPMTAG_REQUIRENAME , fileName ) )
2002-03-25 23:16:26 +03:00
continue ;
rc = 1 ;
/*@innerbreak@*/ break ;
}
fileName = _free ( fileName ) ;
baseNames = hfd ( baseNames , bnt ) ;
dirNames = hfd ( dirNames , dnt ) ;
if ( rc )
goto exit ;
}
}
}
if ( ps - > num ) {
* conflicts = ps - > problems ;
ps - > problems = NULL ;
* numConflicts = ps - > num ;
}
rc = 0 ;
exit :
ps - > problems = _free ( ps - > problems ) ;
ps = _free ( ps ) ;
2010-08-18 11:56:59 +04:00
dbProvCache = htFree ( dbProvCache , NULL , NULL ) ;
2002-03-25 23:16:26 +03:00
return rc ;
}