--- /dev/null
+/*---------------------------------------------------------------------\
+| ____ _ __ __ ___ |
+| |__ / \ / / . \ . \ |
+| / / \ V /| _/ _/ |
+| / /__ | | | | | | |
+| /_____||_| |_| |_| |
+| |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/BinHeader.cc
+ *
+*/
+#include "librpm.h"
+
+#include <iostream>
+
+#include "zypp/base/Logger.h"
+
+#include "zypp/target/rpm/BinHeader.h"
+
+using namespace std;
+
+#undef Y2LOG
+#define Y2LOG "BinHeader"
+
+namespace zypp {
+ namespace target {
+ namespace rpm {
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ // CLASS NAME : BinHeader::intList
+ //
+ ///////////////////////////////////////////////////////////////////
+
+ BinHeader::intList::intList()
+ : cnt( 0 ), val( 0 ), type( RPM_NULL_TYPE )
+ {}
+
+ unsigned BinHeader::intList::set( void * val_r, tag cnt_r, tag type_r ) {
+ val = val_r;
+ cnt = val ? cnt_r : 0;
+ type = type_r;
+ return cnt;
+ }
+
+ int BinHeader::intList::operator[]( const unsigned idx_r ) const {
+ if ( idx_r < cnt ) {
+ switch ( type ) {
+ case RPM_CHAR_TYPE:
+ return ((char*)val)[idx_r];
+ case RPM_INT8_TYPE:
+ return ((int_8*)val)[idx_r];
+ case RPM_INT16_TYPE:
+ return ((int_16*)val)[idx_r];
+ case RPM_INT32_TYPE:
+ return ((int_32*)val)[idx_r];
+ }
+ }
+ return 0;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ // CLASS NAME : BinHeader::stringList
+ //
+ ///////////////////////////////////////////////////////////////////
+
+ void BinHeader::stringList::clear() {
+ if ( val )
+ free( val );
+ val = 0;
+ cnt = 0;
+ }
+
+ BinHeader::stringList::stringList()
+ : cnt( 0 ), val( 0 )
+ {}
+
+ unsigned BinHeader::stringList::set( char ** val_r, tag cnt_r ) {
+ clear();
+ val = val_r;
+ cnt = val ? cnt_r : 0;
+ return cnt;
+ }
+
+ std::string BinHeader::stringList::operator[]( const unsigned idx_r ) const {
+ return( idx_r < cnt ? val[idx_r] : "" );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ // CLASS NAME : BinHeader
+ //
+ ///////////////////////////////////////////////////////////////////
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : BinHeader::BinHeader
+ // METHOD TYPE : Constructor
+ //
+ BinHeader::BinHeader( Header h_r )
+ : _h( h_r )
+ {
+ if ( _h ) {
+ ::headerLink( _h );
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : BinHeader::BinHeader
+ // METHOD TYPE : Constructor
+ //
+ BinHeader::BinHeader( BinHeader::Ptr & rhs )
+ {
+ INT << "INJECT from " << rhs;
+ if ( ! (rhs && rhs->_h) ) {
+ _h = 0;
+ } else {
+ _h = rhs->_h; // ::headerLink already done in rhs
+ rhs->_h = 0;
+ }
+ INT << ": " << *this << " (" << rhs << ")" << endl;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : BinHeader::~BinHeader
+ // METHOD TYPE : Destructor
+ //
+ BinHeader::~BinHeader()
+ {
+ if ( _h ) {
+ ::headerFree( _h );
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : BinHeader::assertHeader
+ // METHOD TYPE : void
+ //
+ bool BinHeader::assertHeader()
+ {
+ if ( !_h ) {
+ _h = ::headerNew();
+ if ( !_h ) {
+ INT << "OOPS: NULL HEADER created!" << endl;
+ return false;
+ }
+ }
+ return true;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : BinHeader::has_tag
+ // METHOD TYPE : bool
+ //
+ // DESCRIPTION :
+ //
+ bool BinHeader::has_tag( tag tag_r ) const
+ {
+ return( !empty() && ::headerIsEntry( _h, tag_r ) );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : BinHeader::int_list
+ // METHOD TYPE : unsigned
+ //
+ // DESCRIPTION :
+ //
+ unsigned BinHeader::int_list( tag tag_r, intList & lst_r ) const
+ {
+ if ( !empty() ) {
+ int_32 type = 0;
+ int_32 cnt = 0;
+ void * val = 0;
+ ::headerGetEntry( _h, tag_r, &type, &val, &cnt );
+
+ if ( val ) {
+ switch ( type ) {
+ case RPM_NULL_TYPE:
+ return lst_r.set( 0, 0, type );
+ case RPM_CHAR_TYPE:
+ case RPM_INT8_TYPE:
+ case RPM_INT16_TYPE:
+ case RPM_INT32_TYPE:
+ return lst_r.set( val, cnt, type );
+
+ case RPM_STRING_ARRAY_TYPE:
+ free( val );
+ // fall through
+ default:
+ INT << "RPM_TAG MISSMATCH: RPM_INT32_TYPE " << tag_r << " got type " << type << endl;
+ }
+ }
+ }
+ return lst_r.set( 0, 0, RPM_NULL_TYPE );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : BinHeader::string_list
+ // METHOD TYPE : unsigned
+ //
+ // DESCRIPTION :
+ //
+ unsigned BinHeader::string_list( tag tag_r, stringList & lst_r ) const
+ {
+ if ( !empty() ) {
+ int_32 type = 0;
+ int_32 cnt = 0;
+ void * val = 0;
+ ::headerGetEntry( _h, tag_r, &type, &val, &cnt );
+
+ if ( val ) {
+ switch ( type ) {
+ case RPM_NULL_TYPE:
+ return lst_r.set( 0, 0 );
+ case RPM_STRING_ARRAY_TYPE:
+ return lst_r.set( (char**)val, cnt );
+
+ default:
+ INT << "RPM_TAG MISSMATCH: RPM_STRING_ARRAY_TYPE " << tag_r << " got type " << type << endl;
+ }
+ }
+ }
+ return lst_r.set( 0, 0 );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : BinHeader::int_val
+ // METHOD TYPE : int
+ //
+ // DESCRIPTION :
+ //
+ int BinHeader::int_val( tag tag_r ) const
+ {
+ if ( !empty() ) {
+ int_32 type = 0;
+ int_32 cnt = 0;
+ void * val = 0;
+ ::headerGetEntry( _h, tag_r, &type, &val, &cnt );
+
+ if ( val ) {
+ switch ( type ) {
+ case RPM_NULL_TYPE:
+ return 0;
+ case RPM_CHAR_TYPE:
+ return *((char*)val);
+ case RPM_INT8_TYPE:
+ return *((int_8*)val);
+ case RPM_INT16_TYPE:
+ return *((int_16*)val);
+ case RPM_INT32_TYPE:
+ return *((int_32*)val);
+
+ case RPM_STRING_ARRAY_TYPE:
+ free( val );
+ // fall through
+ default:
+ INT << "RPM_TAG MISSMATCH: RPM_INT32_TYPE " << tag_r << " got type " << type << endl;
+ }
+ }
+ }
+ return 0;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : BinHeader::string_val
+ // METHOD TYPE : std::string
+ //
+ // DESCRIPTION :
+ //
+ std::string BinHeader::string_val( tag tag_r ) const
+ {
+ if ( !empty() ) {
+ int_32 type = 0;
+ int_32 cnt = 0;
+ void * val = 0;
+ ::headerGetEntry( _h, tag_r, &type, &val, &cnt );
+
+ if ( val ) {
+ switch ( type ) {
+ case RPM_NULL_TYPE:
+ return "";
+ case RPM_STRING_TYPE:
+ return (char*)val;
+
+ case RPM_STRING_ARRAY_TYPE:
+ free( val );
+ // fall through
+ default:
+ INT << "RPM_TAG MISSMATCH: RPM_STRING_TYPE " << tag_r << " got type " << type << endl;
+ }
+ }
+ }
+ return "";
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : BinHeader::stringList_val
+ // METHOD TYPE : std::list<std::string>
+ //
+ // DESCRIPTION :
+ //
+ std::list<std::string> BinHeader::stringList_val( tag tag_r ) const
+ {
+ std::list<std::string> ret;
+
+ if ( !empty() ) {
+ stringList lines;
+ unsigned count = string_list( tag_r, lines );
+ for ( unsigned i = 0; i < count; ++i ) {
+ ret.push_back( lines[i] );
+ }
+ }
+ return ret;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : BinHeader::dumpOn
+ // METHOD TYPE : ostream &
+ //
+ // DESCRIPTION :
+ //
+ ostream & BinHeader::dumpOn( ostream & str ) const
+ {
+ ReferenceCounted::dumpOn( str );
+ return str << '{' << (void*)_h << '}';
+ }
+
+ } // namespace rpm
+ } // namespace target
+} // namespace zypp
--- /dev/null
+/*---------------------------------------------------------------------\
+| ____ _ __ __ ___ |
+| |__ / \ / / . \ . \ |
+| / / \ V /| _/ _/ |
+| / /__ | | | | | | |
+| /_____||_| |_| |_| |
+| |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/BinHeader.h
+ *
+*/
+#ifndef ZYPP_TARGET_RPM_BINHEADER_H
+#define ZYPP_TARGET_RPM_BINHEADER_H
+
+extern "C" {
+#include <stdint.h>
+}
+
+#include <iosfwd>
+#include <string>
+#include <list>
+
+#include "zypp/base/ReferenceCounted.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/PtrTypes.h"
+#include "zypp/target/rpm/librpm.h"
+
+namespace zypp {
+ namespace target {
+ namespace rpm {
+ ///////////////////////////////////////////////////////////////////
+ //
+ // CLASS NAME : BinHeader
+ /**
+ *
+ **/
+ class BinHeader : public base::ReferenceCounted, private base::NonCopyable
+ {
+
+ public:
+
+ typedef intrusive_ptr<BinHeader> Ptr;
+
+ typedef intrusive_ptr<const BinHeader> constPtr;
+
+ typedef int32_t tag;
+
+ class intList;
+
+ class stringList;
+
+ private:
+
+ Header _h;
+
+ bool assertHeader();
+
+ public:
+
+ BinHeader( Header h_r = 0 );
+
+ /**
+ * <B>Dangerous!<\B> This one takes the header out of rhs
+ * and leaves rhs empty.
+ **/
+ BinHeader( BinHeader::Ptr & rhs );
+
+ virtual ~BinHeader();
+
+ public:
+
+ bool empty() const { return( _h == NULL ); }
+
+ bool has_tag( tag tag_r ) const;
+
+ unsigned int_list( tag tag_r, intList & lst_r ) const;
+
+ unsigned string_list( tag tag_r, stringList & lst_r ) const;
+
+ int int_val( tag tag_r ) const;
+
+ std::string string_val( tag tag_r ) const;
+
+ public:
+
+ std::list<std::string> stringList_val( tag tag_r ) const;
+
+ public:
+
+ virtual std::ostream & dumpOn( std::ostream & str ) const;
+ };
+
+ ///////////////////////////////////////////////////////////////////
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ // CLASS NAME : BinHeader::intList
+ /**
+ *
+ **/
+ class BinHeader::intList {
+ intList ( const intList & );
+ intList & operator=( const intList & );
+ private:
+ unsigned cnt;
+ void * val;
+ tag type;
+ private:
+ friend class BinHeader;
+ unsigned set( void * val_r, tag cnt_r, tag type_r );
+ public:
+ intList();
+ unsigned size() const { return cnt; }
+ int operator[]( const unsigned idx_r ) const;
+ };
+
+ ///////////////////////////////////////////////////////////////////
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ // CLASS NAME : BinHeader::stringList
+ /**
+ *
+ **/
+ class BinHeader::stringList {
+ stringList ( const stringList & );
+ stringList & operator=( const stringList & );
+ private:
+ unsigned cnt;
+ char ** val;
+ void clear();
+ private:
+ friend class BinHeader;
+ unsigned set( char ** val_r, tag cnt_r );
+ public:
+ stringList();
+ ~stringList() { clear(); }
+ unsigned size() const { return cnt; }
+ std::string operator[]( const unsigned idx_r ) const;
+ };
+
+ ///////////////////////////////////////////////////////////////////
+
+ } // namespace rpm
+ } // namespace target
+} // namespace zypp
+
+#endif // ZYPP_TARGET_RPM_BINHEADER_H
--- /dev/null
+/*---------------------------------------------------------------------\
+| ____ _ __ __ ___ |
+| |__ / \ / / . \ . \ |
+| / / \ V /| _/ _/ |
+| / /__ | | | | | | |
+| /_____||_| |_| |_| |
+| |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/BinHeaderCache.cc
+ *
+*/
+#include "librpm.h"
+
+#include <iostream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/Date.h"
+#include "zypp/PathInfo.h"
+#include "zypp/base/String.h"
+
+#include "zypp/target/rpm/BinHeader.h"
+#include "zypp/target/rpm/BinHeaderCache.h"
+
+using namespace std;
+
+namespace zypp {
+ namespace target {
+ namespace rpm {
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ // CLASS NAME : BinHeaderCache::Cache
+ /**
+ *
+ **/
+ class BinHeaderCache::Cache {
+
+ friend std::ostream & operator<<( std::ostream & str, const Cache & obj );
+
+ Cache & operator=( const Cache & );
+ Cache ( const Cache & );
+
+ private:
+
+ FD_t fd;
+ pos _fdpos; // keep track of filepos for tell()
+
+ public:
+
+ Cache() : fd( 0 ), _fdpos( ~0 ) {}
+ ~Cache() { close(); }
+
+ public:
+
+ bool open( const Pathname & file_r );
+
+ bool isOpen() const { return( fd != 0 ); }
+
+ void close();
+
+ public:
+
+ pos tell() const;
+
+ pos seek( const pos pos_r );
+
+ unsigned readData( void * buf_r, unsigned count_r );
+
+ Header readHeader( bool magicp = true );
+ };
+
+ ///////////////////////////////////////////////////////////////////
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : BinHeaderCache::Cache::open
+ // METHOD TYPE : bool
+ //
+ bool BinHeaderCache::Cache::open( const Pathname & file_r )
+ {
+ close();
+
+ switch ( zipType( file_r ) ) {
+ case filesystem::ZT_NONE:
+ {
+ fd = ::Fopen( file_r.asString().c_str(), "r.fdio" );
+ DBG << "PLAIN: open 'r.fdio' " << fd << endl;
+ }
+ break;
+ case filesystem::ZT_GZ:
+ {
+ fd = ::Fopen( file_r.asString().c_str(), "r.gzdio" );
+ DBG << "GZIP: open 'r.gzdio' " << fd << endl;
+ }
+ break;
+ case filesystem::ZT_BZ2:
+ {
+ ERR << "BZIP2 is not supported: " << file_r << endl;
+ #warning Check BZIP2 support
+ break;
+ fd = ::Fopen( file_r.asString().c_str(), "r.bzdio" );
+ DBG << "BZIP2: open 'r.bzdio' " << fd << endl;
+ }
+ break;
+ }
+
+ if ( fd == 0 || ::Ferror(fd) ) {
+ ERR << "Can't open cache for reading: " << file_r << " (" << ::Fstrerror(fd) << ")" << endl;
+ close();
+ return false;
+ }
+ _fdpos = 0;
+ return true;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : BinHeaderCache::Cache::close
+ // METHOD TYPE : void
+ //
+ void BinHeaderCache::Cache::close()
+ {
+ if ( fd ) {
+ ::Fclose( fd );
+ fd = 0;
+ _fdpos = ~0;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : BinHeaderCache::Cache::tell
+ // METHOD TYPE : pos
+ //
+
+ extern "C" {
+ typedef struct X_FDSTACK_s {
+ FDIO_t io;
+ void * fp;
+ int fdno;
+ } XFDSTACK_t;
+
+ struct X_FD_s {
+ int nrefs;
+ int flags;
+ int magic;
+ #define XFDMAGIC 0x04463138
+ int nfps;
+ XFDSTACK_t fps[8];
+ };
+ }
+
+ BinHeaderCache::pos BinHeaderCache::Cache::tell() const
+ {
+ pos rc = npos;
+
+ struct X_FD_s * xfd = (struct X_FD_s*)fd;
+
+ if ( !xfd || xfd->magic != XFDMAGIC) {
+ INT << "magic(" << XFDMAGIC << ") failed: " << xfd->magic << endl;
+ return rc;
+ }
+
+ return _fdpos;
+ #if 0
+ if ( xfd->fps[xfd->nfps].io == fpio ) {
+ FILE * fp = (FILE *)xfd->fps[xfd->nfps].fp;
+ rc = ftell(fp);
+ }
+
+ if ( rc == npos )
+ WAR << "Can't tell:" << ::Ferror(fd) << " (" << ::Fstrerror(fd) << ")" << endl;
+ return rc;
+ #endif
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : BinHeaderCache::Cache::seek
+ // METHOD TYPE : pos
+ //
+ BinHeaderCache::pos BinHeaderCache::Cache::seek( const pos pos_r )
+ {
+ pos rc = npos;
+
+ if ( pos_r != npos ) {
+ ::Fseek( fd, pos_r, SEEK_SET );
+ _fdpos = pos_r;
+ if ( tell() == pos_r )
+ rc = pos_r;
+ } else {
+ INT << "Attempt to seek to pos -1" << endl;
+ }
+
+ if ( rc == npos )
+ WAR << "Can't seek to " << pos_r << ":" << ::Ferror(fd) << " (" << ::Fstrerror(fd) << ")" << endl;
+ return rc;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : BinHeaderCache::Cache::readData
+ // METHOD TYPE : unsigned
+ //
+ unsigned BinHeaderCache::Cache::readData( void * buf_r, unsigned count_r )
+ {
+ if ( !buf_r ) {
+ INT << "Attempt to fill NULL buffer" << endl;
+ return 0;
+ }
+ if ( !count_r ) {
+ return 0;
+ }
+
+ unsigned got = ::Fread( buf_r, sizeof(char), count_r, fd );
+ _fdpos += got;
+ if ( got != count_r ) {
+ if ( got || ::Ferror(fd) ) {
+ ERR << "Error reading " << count_r << " byte (" << ::Fstrerror(fd) << ")" << endl;
+ } // else EOF?
+ return 0;
+ }
+ return count_r;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : BinHeaderCache::Cache::readHeader
+ // METHOD TYPE : Header
+ //
+
+ extern "C" {
+ #include <netinet/in.h>
+ // from rpm: lib/header.c
+ struct XXentryInfo {
+ int_32 tag;
+ int_32 type;
+ int_32 offset; /* Offset from beginning of data segment,
+ only defined on disk */
+ int_32 count;
+ };
+ }
+
+ Header BinHeaderCache::Cache::readHeader( bool magicp )
+ {
+ static const int_32 rpm_header_magic = 0x01e8ad8e;
+
+ int_32 block[4];
+ int_32 il, dl;
+ unsigned totalSize = 0;
+
+ unsigned count = (magicp ? 4 : 2) * sizeof(int_32);
+ if ( readData( block, count ) != count ) {
+ ERR << "Error reading header info (" << ::Fstrerror(fd) << ")" << endl;
+ return 0;
+ }
+
+ count = 0;
+
+ if ( magicp ) {
+ if ( block[count] != rpm_header_magic ) {
+ ERR << "Error bad header magic " << str::hexstring( block[count] )
+ << " (" << str::hexstring( rpm_header_magic ) << ")" << endl;
+ return 0;
+ }
+ count += 2;
+ }
+
+ il = ntohl( block[count++] );
+ dl = ntohl( block[count++] );
+
+ totalSize = (2*sizeof(int_32)) + (il * sizeof(struct XXentryInfo)) + dl;
+ if (totalSize > (32*1024*1024)) {
+ ERR << "Error header ecxeeds 32Mb limit (" << totalSize << ")" << endl;
+ return NULL;
+ }
+
+ char * data = new char[totalSize];
+ int_32 * p = (int_32 *)data;
+ Header h = 0;
+
+ *p++ = htonl(il);
+ *p++ = htonl(dl);
+ totalSize -= (2*sizeof(int_32));
+
+ if ( readData( p, totalSize ) != totalSize ) {
+ ERR << "Error reading header data (" << ::Fstrerror(fd) << ")" << endl;
+ } else {
+ h = ::headerCopyLoad( data );
+ if ( !h ) {
+ ERR << "Error loading header data" << endl;
+ }
+ }
+
+ delete [] data;
+
+ return h;
+ }
+
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ // CLASS NAME : BinHeaderCache
+ //
+ ///////////////////////////////////////////////////////////////////
+
+ const unsigned BinHeaderCache::BHC_MAGIC_SZE( 64 );
+
+ const BinHeaderCache::pos BinHeaderCache::npos( BinHeaderCache::pos(-1) );
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : BinHeaderCache::BinHeaderCache
+ // METHOD TYPE : Constructor
+ //
+ BinHeaderCache::BinHeaderCache( const Pathname & cache_r )
+ : _c( * new Cache )
+ , _cpath( cache_r )
+ , _cdate( 0 )
+ , _cheaderStart( npos )
+ {
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : BinHeaderCache::~BinHeaderCache
+ // METHOD TYPE : Destructor
+ //
+ BinHeaderCache::~BinHeaderCache()
+ {
+ delete &_c;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : BinHeaderCache::_cReadMagic
+ // METHOD TYPE : int
+ //
+ // [string\0][string\0][\0padded]
+ //
+ int BinHeaderCache::_cReadMagic()
+ {
+ char magic[BHC_MAGIC_SZE+1];
+ memset( magic, 0, BHC_MAGIC_SZE+1 );
+
+ if ( _c.readData( magic, BHC_MAGIC_SZE ) != BHC_MAGIC_SZE ) {
+ ERR << "Error reading magic of cache file " << _cpath << endl;
+ return -1;
+ }
+
+ _cmagic = magic;
+ if ( _cmagic.size() < BHC_MAGIC_SZE ) {
+ _cdate = strtoul( magic+_cmagic.size()+1, 0, 10 );
+ if ( _cdate ) {
+ _cheaderStart = BHC_MAGIC_SZE;
+ return 0;
+ }
+ }
+
+ ERR << "No magic in cache file " << _cpath << endl;
+ return -2;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : BinHeaderCache::open
+ // METHOD TYPE : bool
+ //
+ bool BinHeaderCache::open()
+ {
+ if ( _c.isOpen() )
+ return true;
+
+ if ( !_c.open( _cpath ) ) {
+ close();
+ return false;
+ }
+
+ if ( _cReadMagic() != 0 ) {
+ close();
+ return false;
+ }
+
+ if ( !magicOk() ) {
+ ERR << "Bad magic in cache file " << _cpath << endl;
+ close();
+ return false;
+ }
+
+ return true;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : BinHeaderCache::isOpen
+ // METHOD TYPE : bool
+ //
+ bool BinHeaderCache::isOpen() const
+ {
+ return _c.isOpen();
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : BinHeaderCache::close
+ // METHOD TYPE : void
+ //
+ void BinHeaderCache::close()
+ {
+ _cmagic = "";
+ _cdate = 0;
+ _cheaderStart = npos;
+ _c.close();
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : BinHeaderCache::tell
+ // METHOD TYPE : BinHeaderCache::pos
+ //
+ BinHeaderCache::pos BinHeaderCache::tell() const
+ {
+ return _c.tell();
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : BinHeaderCache::seek
+ // METHOD TYPE : BinHeaderCache::pos
+ //
+ BinHeaderCache::pos BinHeaderCache::seek( const pos pos_r )
+ {
+ return _c.seek( pos_r );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : BinHeaderCache::readData
+ // METHOD TYPE : unsigned
+ //
+ unsigned BinHeaderCache::readData( void * buf_r, unsigned count_r )
+ {
+ return _c.readData( buf_r, count_r );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : BinHeaderCache::readHeader
+ // METHOD TYPE : BinHeaderPtr
+ //
+ BinHeader::Ptr BinHeaderCache::readHeader( bool magicp )
+ {
+ Header h = _c.readHeader( magicp );
+ if ( !h )
+ return 0;
+ return new BinHeader( h );
+ }
+
+ /******************************************************************
+ **
+ **
+ ** FUNCTION NAME : operator<<
+ ** FUNCTION TYPE : ostream &
+ */
+ ostream & operator<<( ostream & str, const BinHeaderCache & obj )
+ {
+ str << "BinHeaderCache@" << (void *)&obj;
+ if ( obj.isOpen() ) {
+ str << '(' << obj._cmagic << '|' << Date(obj._cdate) << "|at " << obj._cheaderStart << ')';
+ } else {
+ str << "(closed)";
+ }
+ return str;
+ }
+
+ } // namespace rpm
+ } // namespace target
+} // namespace zypp
--- /dev/null
+/*---------------------------------------------------------------------\
+| ____ _ __ __ ___ |
+| |__ / \ / / . \ . \ |
+| / / \ V /| _/ _/ |
+| / /__ | | | | | | |
+| /_____||_| |_| |_| |
+| |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/BinHeaderCache.h
+ *
+*/
+#ifndef ZYPP_TARGET_BINHEADERCACHE_H
+#define ZYPP_TARGET_BINHEADERCACHE_H
+
+#include <iosfwd>
+
+#include "zypp/Pathname.h"
+
+#include "zypp/target/rpm/BinHeader.h"
+
+namespace zypp {
+ namespace target {
+ namespace rpm {
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ // CLASS NAME : BinHeaderCache
+ /**
+ *
+ **/
+ class BinHeaderCache {
+
+ friend std::ostream & operator<<( std::ostream & str, const BinHeaderCache & obj );
+
+ BinHeaderCache & operator=( const BinHeaderCache & );
+ BinHeaderCache ( const BinHeaderCache & );
+
+ public:
+
+ typedef unsigned pos;
+
+ static const pos npos;
+
+ private:
+
+ static const unsigned BHC_MAGIC_SZE;
+
+ class Cache;
+
+ Cache & _c;
+
+ private:
+
+ int _cReadMagic();
+
+ protected:
+
+ Pathname _cpath;
+
+ protected:
+
+ std::string _cmagic;
+
+ time_t _cdate;
+
+ pos _cheaderStart;
+
+ protected:
+
+ virtual bool magicOk() { return _cmagic.empty(); }
+
+ public:
+
+ BinHeaderCache( const Pathname & cache_r );
+
+ virtual ~BinHeaderCache();
+
+ public:
+
+ bool open();
+
+ void close();
+
+ bool isOpen() const;
+
+ const Pathname & cpath() const { return _cpath; }
+
+ const std::string & cmagic() const { return _cmagic; }
+
+ time_t cdate() const { return _cdate; }
+
+ pos tell() const;
+
+ pos seek( const pos pos_r );
+
+ unsigned readData( void * buf_r, unsigned count_r );
+
+ BinHeader::Ptr readHeader( bool magicp = true );
+ };
+
+ ///////////////////////////////////////////////////////////////////
+
+ } // namespace rpm
+ } // namespace target
+} // namespace zypp
+
+#endif // ZYPP_TARGET_BINHEADERCACHE_H
--- /dev/null
+## Process this file with automake to produce Makefile.in
+## ##################################################
+
+SUBDIRS =
+
+## ##################################################
+
+include_HEADERS = \
+ librpm.h \
+ BinHeader.h \
+ BinHeaderCache.h \
+ RpmHeader.h \
+ RpmHeaderCache.h \
+ librpmDb.h \
+ RpmDb.h
+
+
+noinst_LTLIBRARIES = lib@PACKAGE@_target_rpm.la
+
+## ##################################################
+
+lib@PACKAGE@_target_rpm_la_SOURCES = \
+ BinHeader.cc \
+ BinHeaderCache.cc \
+ RpmHeader.cc \
+ RpmHeaderCache.cc \
+ librpmDb.cc \
+ RpmDb.cc
+
+
+lib@PACKAGE@_target_rpm_la_LIBADD =
+
+## ##################################################
--- /dev/null
+/*---------------------------------------------------------------------\
+| |
+| __ __ ____ _____ ____ |
+| \ \ / /_ _/ ___|_ _|___ \ |
+| \ V / _` \___ \ | | __) | |
+| | | (_| |___) || | / __/ |
+| |_|\__,_|____/ |_| |_____| |
+| |
+| Package Management |
+| (C) 2002 SuSE AG |
+\----------------------------------------------------------------------/
+
+ File: RpmDb.cc
+ Purpose: Interface to installed RPM system
+ Author: Stefan Schubert <schubi@suse.de>
+ Maintainer: Ludwig Nussel <lnussel@suse.de>
+
+ Copied and adapted from agent-targetpkg
+
+/-*/
+#include "librpm.h"
+
+#include <cstdlib>
+#include <cstdio>
+#include <ctime>
+
+#include <iostream>
+#include <fstream>
+#include <list>
+#include <map>
+#include <string>
+#include <vector>
+
+#include <y2util/Date.h>
+#include <y2util/FSize.h>
+#include <y2util/Vendor.h>
+#include <y2util/Y2SLog.h>
+#include <y2util/TagParser.h>
+#include <y2util/Pathname.h>
+#include <y2util/PathInfo.h>
+#include <y2util/ExternalDataSource.h>
+#include <y2util/diff.h>
+
+#include <y2pm/RpmDb.h>
+#include <y2pm/InstTargetError.h>
+#include <y2pm/RpmDbCallbacks.h>
+
+#include <y2pm/librpmDb.h>
+#include <y2pm/PMRpmPackageDataProvider.h>
+#include <y2pm/PMPackageManager.h>
+#include <y2pm/Timecount.h>
+
+#include <Y2PM.h>
+
+#ifndef _
+#define _(X) X
+#endif
+
+using namespace std;
+using namespace RpmDbCallbacks;
+
+/******************************************************************
+**
+**
+** FUNCTION NAME : stringPath
+** FUNCTION TYPE : inline string
+*/
+inline string stringPath( const Pathname & root_r, const Pathname & sub_r )
+{
+ return librpmDb::stringPath( root_r, sub_r );
+}
+
+/******************************************************************
+**
+**
+** FUNCTION NAME : operator<<
+** FUNCTION TYPE : ostream &
+*/
+ostream & operator<<( ostream & str, const RpmDb::DbStateInfoBits & obj )
+{
+ if ( obj == RpmDb::DbSI_NO_INIT ) {
+ str << "NO_INIT";
+ } else {
+#define ENUM_OUT(B,C) str << ( obj & RpmDb::B ? C : '-' )
+ str << "V4(";
+ ENUM_OUT( DbSI_HAVE_V4, 'X' );
+ ENUM_OUT( DbSI_MADE_V4, 'c' );
+ ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
+ str << ")V3(";
+ ENUM_OUT( DbSI_HAVE_V3, 'X' );
+ ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
+ ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
+ str << ")";
+#undef ENUM_OUT
+ }
+ return str;
+}
+
+///////////////////////////////////////////////////////////////////
+// CLASS NAME : RpmDbPtr
+// CLASS NAME : RpmDbconstPtr
+///////////////////////////////////////////////////////////////////
+IMPL_BASE_POINTER(RpmDb);
+
+#define WARNINGMAILPATH "/var/log/YaST2/"
+#define FILEFORBACKUPFILES "YaSTBackupModifiedFiles"
+
+///////////////////////////////////////////////////////////////////
+//
+// CLASS NAME : RpmDb::Logfile
+/**
+ * Simple wrapper for progress log. Refcnt, filename and corresponding
+ * ofstream are static members. Logfile constructor raises, destructor
+ * lowers refcounter. On refcounter changing from 0->1, file is opened.
+ * Changing from 1->0 the file is closed. Thus Logfile objects should be
+ * local to those functions, writing the log, and must not be stored
+ * permanently;
+ *
+ * Usage:
+ * some methothd ()
+ * {
+ * Logfile progresslog;
+ * ...
+ * progresslog() << "some message" << endl;
+ * ...
+ * }
+ **/
+class RpmDb::Logfile {
+ Logfile( const Logfile & );
+ Logfile & operator=( const Logfile & );
+ private:
+ static ofstream _log;
+ static unsigned _refcnt;
+ static Pathname _fname;
+ static void openLog() {
+ if ( !_fname.empty() ) {
+ _log.clear();
+ _log.open( _fname.asString().c_str(), std::ios::out|std::ios::app );
+ if( !_log )
+ ERR << "Could not open logfile '" << _fname << "'" << endl;
+ }
+ }
+ static void closeLog() {
+ _log.clear();
+ _log.close();
+ }
+ static void refUp() {
+ if ( !_refcnt )
+ openLog();
+ ++_refcnt;
+ }
+ static void refDown() {
+ --_refcnt;
+ if ( !_refcnt )
+ closeLog();
+ }
+ public:
+ Logfile() { refUp(); }
+ ~Logfile() { refDown(); }
+ ostream & operator()( bool timestamp = false ) {
+ if ( timestamp ) {
+ _log << Date::form( "%Y-%m-%d %H:%M:%S ", Date::now() );
+ }
+ return _log;
+ }
+ static void setFname( const Pathname & fname_r ) {
+ MIL << "installation log file " << fname_r << endl;
+ if ( _refcnt )
+ closeLog();
+ _fname = fname_r;
+ if ( _refcnt )
+ openLog();
+ }
+};
+
+///////////////////////////////////////////////////////////////////
+
+Pathname RpmDb::Logfile::_fname;
+ofstream RpmDb::Logfile::_log;
+unsigned RpmDb::Logfile::_refcnt = 0;
+
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmDb::setInstallationLogfile
+// METHOD TYPE : bool
+//
+bool RpmDb::setInstallationLogfile( const Pathname & filename )
+{
+ Logfile::setFname( filename );
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+// CLASS NAME : RpmDb::Packages
+/**
+ * Helper class for RpmDb::getPackages() to build the
+ * list<PMPackagePtr> returned. We have to assert, that there
+ * is a unique entry for every PkgName.
+ *
+ * In the first step we build the _index map which helps to catch
+ * multiple occurances of a PkgName in the rpmdb. That's not desired,
+ * but possible. Usg. the last package instance installed is strored
+ * in the _index map.
+ *
+ * At the end buildList() is called to build the list<PMPackagePtr>
+ * from the _index map. _valid is set true to assign that the list
+ * is in sync with the rpmdb content. Operations changing the rpmdb
+ * content (install/remove package) should set _valid to false. The
+ * next call to RpmDb::getPackages() will then reread the the rpmdb.
+ *
+ * Note that outside RpmDb::getPackages() _list and _index are always
+ * in sync. So you may use lookup(PkgName) to retrieve a specific
+ * PMPackagePtr.
+ **/
+class RpmDb::Packages {
+ public:
+ list<PMPackagePtr> _list;
+ map<PkgName,PMPackagePtr> _index;
+ bool _valid;
+ Packages() : _valid( false ) {}
+ void clear() {
+ _list.clear();
+ _index.clear();
+ _valid = false;
+ }
+ PMPackagePtr lookup( const PkgName & name_r ) const {
+ map<PkgName,PMPackagePtr>::const_iterator got = _index.find( name_r );
+ if ( got != _index.end() )
+ return got->second;
+ return PMPackagePtr();
+ }
+ void buildList() {
+ _list.clear();
+ for ( map<PkgName,PMPackagePtr>::iterator iter = _index.begin();
+ iter != _index.end(); ++iter ) {
+ if ( iter->second )
+ _list.push_back( iter->second );
+ }
+ _valid = true;
+ }
+};
+
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+// CLASS NAME : RpmDb
+//
+///////////////////////////////////////////////////////////////////
+
+#define FAILIFNOTINITIALIZED if( ! initialized() ) { WAR << "No database access: " << Error::E_RpmDB_not_open << endl; return Error::E_RpmDB_not_open; }
+
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmDb::RpmDb
+// METHOD TYPE : Constructor
+//
+RpmDb::RpmDb()
+ : _dbStateInfo( DbSI_NO_INIT )
+ , _packages( * new Packages ) // delete in destructor
+#warning Check for obsolete memebers
+ , _backuppath ("/var/adm/backup")
+ , _packagebackups(false)
+ , _warndirexists(false)
+{
+ process = 0;
+ exit_code = -1;
+
+ // Some rpm versions are patched not to abort installation if
+ // symlink creation failed.
+ setenv( "RPM_IgnoreFailedSymlinks", "1", 1 );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmDb::~RpmDb
+// METHOD TYPE : Destructor
+//
+RpmDb::~RpmDb()
+{
+ M__ << "~RpmDb()" << endl;
+ closeDatabase();
+
+ delete process;
+ delete &_packages;
+ M__ << "~RpmDb() end" << endl;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmDb::dumpOn
+// METHOD TYPE : std::ostream &
+//
+std::ostream & RpmDb::dumpOn( std::ostream & str ) const
+{
+ str << "RpmDb[";
+
+ if ( _dbStateInfo == DbSI_NO_INIT ) {
+ str << "NO_INIT";
+ } else {
+#define ENUM_OUT(B,C) str << ( _dbStateInfo & B ? C : '-' )
+ str << "V4(";
+ ENUM_OUT( DbSI_HAVE_V4, 'X' );
+ ENUM_OUT( DbSI_MADE_V4, 'c' );
+ ENUM_OUT( DbSI_MODIFIED_V4, 'm' );
+ str << ")V3(";
+ ENUM_OUT( DbSI_HAVE_V3, 'X' );
+ ENUM_OUT( DbSI_HAVE_V3TOV4, 'B' );
+ ENUM_OUT( DbSI_MADE_V3TOV4, 'c' );
+ str << "): " << stringPath( _root, _dbPath );
+#undef ENUM_OUT
+ }
+ return str << "]";
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmDb::initDatabase
+// METHOD TYPE : PMError
+//
+PMError RpmDb::initDatabase( Pathname root_r, Pathname dbPath_r )
+{
+ ///////////////////////////////////////////////////////////////////
+ // Check arguments
+ ///////////////////////////////////////////////////////////////////
+ if ( root_r.empty() )
+ root_r = "/";
+
+ if ( dbPath_r.empty() )
+ dbPath_r = "/var/lib/rpm";
+
+ if ( ! (root_r.absolute() && dbPath_r.absolute()) ) {
+ ERR << "Illegal root or dbPath: " << stringPath( root_r, dbPath_r ) << endl;
+ return Error::E_invalid_argument;
+ }
+
+ MIL << "Calling initDatabase: " << stringPath( root_r, dbPath_r ) << endl;
+
+ ///////////////////////////////////////////////////////////////////
+ // Check whether already initialized
+ ///////////////////////////////////////////////////////////////////
+ if ( initialized() ) {
+ if ( root_r == _root && dbPath_r == _dbPath ) {
+ return Error::E_ok;
+ } else {
+ ERR << "Can't switch to " << stringPath( root_r, dbPath_r )
+ << " while accessing " << stringPath( _root, _dbPath ) << endl;
+ return Error::E_RpmDB_already_open;
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ // init database
+ ///////////////////////////////////////////////////////////////////
+ librpmDb::unblockAccess();
+ DbStateInfoBits info = DbSI_NO_INIT;
+ PMError err = internal_initDatabase( root_r, dbPath_r, info );
+
+ if ( err ) {
+ librpmDb::blockAccess();
+ ERR << "Cleanup on error: state " << info << endl;
+
+ if ( dbsi_has( info, DbSI_MADE_V4 ) ) {
+ // remove the newly created rpm4 database and
+ // any backup created on conversion.
+ removeV4( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
+ }
+
+ } else {
+ if ( dbsi_has( info, DbSI_HAVE_V3 ) ) {
+ if ( root_r == "/" || dbsi_has( info, DbSI_MODIFIED_V4 ) ) {
+ // Move obsolete rpm3 database beside.
+ MIL << "Cleanup: state " << info << endl;
+ removeV3( root_r + dbPath_r, dbsi_has( info, DbSI_MADE_V3TOV4 ) );
+ dbsi_clr( info, DbSI_HAVE_V3 );
+ } else {
+ // Performing an update: Keep the original rpm3 database
+ // and wait if the rpm4 database gets modified by installing
+ // or removing packages. Cleanup in modifyDatabase or closeDatabase.
+ MIL << "Update mode: Cleanup delayed until closeOldDatabase." << endl;
+ }
+ }
+#warning CHECK: notify root about conversion backup.
+
+ _root = root_r;
+ _dbPath = dbPath_r;
+ _dbStateInfo = info;
+
+ if ( ! ( err || Y2PM::runningFromSystem() ) ) {
+ if ( dbsi_has( info, DbSI_HAVE_V4 )
+ && ! dbsi_has( info, DbSI_MADE_V4 ) ) {
+ err = rebuildDatabase();
+ }
+ }
+
+ // Close the database in case any write acces (create/convert)
+ // happened during init. This should drop any lock acquired
+ // by librpm. On demand it will be reopened readonly and should
+ // not hold any lock.
+ librpmDb::dbRelease( true );
+ MIL << "InitDatabase: " << *this << endl;
+ }
+
+ return err;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmDb::internal_initDatabase
+// METHOD TYPE : PMError
+//
+PMError RpmDb::internal_initDatabase( const Pathname & root_r, const Pathname & dbPath_r,
+ DbStateInfoBits & info_r )
+{
+ info_r = DbSI_NO_INIT;
+
+ ///////////////////////////////////////////////////////////////////
+ // Get info about the desired database dir
+ ///////////////////////////////////////////////////////////////////
+ librpmDb::DbDirInfo dbInfo( root_r, dbPath_r );
+
+ if ( dbInfo.illegalArgs() ) {
+ return Error::E_invalid_argument; // should not happen (checked in initDatabase)
+ }
+ if ( ! dbInfo.usableArgs() ) {
+ ERR << "Bad database directory: " << dbInfo.dbDir() << endl;
+ return Error::E_invalid_argument;
+ }
+
+ if ( dbInfo.hasDbV4() ) {
+ dbsi_set( info_r, DbSI_HAVE_V4 );
+ MIL << "Found rpm4 database in " << dbInfo.dbDir() << endl;
+ } else {
+ MIL << "Creating new rpm4 database in " << dbInfo.dbDir() << endl;
+ }
+
+ if ( dbInfo.hasDbV3() ) {
+ dbsi_set( info_r, DbSI_HAVE_V3 );
+ }
+ if ( dbInfo.hasDbV3ToV4() ) {
+ dbsi_set( info_r, DbSI_HAVE_V3TOV4 );
+ }
+
+ DBG << "Initial state: " << info_r << ": " << stringPath( root_r, dbPath_r );
+ librpmDb::dumpState( DBG ) << endl;
+
+ ///////////////////////////////////////////////////////////////////
+ // Access database, create if needed
+ ///////////////////////////////////////////////////////////////////
+
+ // creates dbdir and empty rpm4 database if not present
+ PMError err = librpmDb::dbAccess( root_r, dbPath_r );
+
+ if ( ! dbInfo.hasDbV4() ) {
+ dbInfo.restat();
+ if ( dbInfo.hasDbV4() ) {
+ dbsi_set( info_r, DbSI_HAVE_V4 | DbSI_MADE_V4 );
+ }
+ }
+
+ if ( err ) {
+ ERR << "Can't access rpm4 database " << dbInfo.dbV4() << " " << err << endl;
+ return err;
+ }
+
+ DBG << "Access state: " << info_r << ": " << stringPath( root_r, dbPath_r );
+ librpmDb::dumpState( DBG ) << endl;
+
+ ///////////////////////////////////////////////////////////////////
+ // Check whether to convert something. Create backup but do
+ // not remove anything here
+ ///////////////////////////////////////////////////////////////////
+ constlibrpmDbPtr dbptr;
+ librpmDb::dbAccess( dbptr );
+ bool dbEmpty = dbptr->empty();
+ if ( dbEmpty ) {
+ MIL << "Empty rpm4 database " << dbInfo.dbV4() << endl;
+ }
+
+ if ( dbInfo.hasDbV3() ) {
+ MIL << "Found rpm3 database " << dbInfo.dbV3() << endl;
+
+ if ( dbEmpty ) {
+ extern PMError convertV3toV4( const Pathname & v3db_r, const constlibrpmDbPtr & v4db_r );
+
+ err = convertV3toV4( dbInfo.dbV3().path(), dbptr );
+ if ( err ) {
+ return err;
+ }
+
+ // create a backup copy
+ int res = PathInfo::copy( dbInfo.dbV3().path(), dbInfo.dbV3ToV4().path() );
+ if ( res ) {
+ WAR << "Backup converted rpm3 database failed: error(" << res << ")" << endl;
+ } else {
+ dbInfo.restat();
+ if ( dbInfo.hasDbV3ToV4() ) {
+ MIL << "Backup converted rpm3 database: " << dbInfo.dbV3ToV4() << endl;
+ dbsi_set( info_r, DbSI_HAVE_V3TOV4 | DbSI_MADE_V3TOV4 );
+ }
+ }
+
+ } else {
+
+ WAR << "Non empty rpm3 and rpm4 database found: using rpm4" << endl;
+#warning EXCEPTION: nonempty rpm4 and rpm3 database found.
+ //ConvertDbReport::Send report( RpmDbCallbacks::convertDbReport );
+ //report->start( dbInfo.dbV3().path() );
+ //report->stop( some error );
+
+ // set DbSI_MODIFIED_V4 as it's not a temporary which can be removed.
+ dbsi_set( info_r, DbSI_MODIFIED_V4 );
+
+ }
+
+ DBG << "Convert state: " << info_r << ": " << stringPath( root_r, dbPath_r );
+ librpmDb::dumpState( DBG ) << endl;
+ }
+
+ if ( dbInfo.hasDbV3ToV4() ) {
+ MIL << "Rpm3 database backup: " << dbInfo.dbV3ToV4() << endl;
+ }
+
+ return err;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmDb::removeV4
+// METHOD TYPE : void
+//
+void RpmDb::removeV4( const Pathname & dbdir_r, bool v3backup_r )
+{
+ const char * v3backup = "packages.rpm3";
+ const char * master = "Packages";
+ const char * index[] = {
+ "Basenames",
+ "Conflictname",
+ "Depends",
+ "Dirnames",
+ "Filemd5s",
+ "Group",
+ "Installtid",
+ "Name",
+ "Providename",
+ "Provideversion",
+ "Pubkeys",
+ "Requirename",
+ "Requireversion",
+ "Sha1header",
+ "Sigmd5",
+ "Triggername",
+ // last entry!
+ NULL
+ };
+
+ PathInfo pi( dbdir_r );
+ if ( ! pi.isDir() ) {
+ ERR << "Can't remove rpm4 database in non directory: " << dbdir_r << endl;
+ return;
+ }
+
+ for ( const char ** f = index; *f; ++f ) {
+ pi( dbdir_r + *f );
+ if ( pi.isFile() ) {
+ PathInfo::unlink( pi.path() );
+ }
+ }
+
+ pi( dbdir_r + master );
+ if ( pi.isFile() ) {
+ MIL << "Removing rpm4 database " << pi << endl;
+ PathInfo::unlink( pi.path() );
+ }
+
+ if ( v3backup_r ) {
+ pi( dbdir_r + v3backup );
+ if ( pi.isFile() ) {
+ MIL << "Removing converted rpm3 database backup " << pi << endl;
+ PathInfo::unlink( pi.path() );
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmDb::removeV3
+// METHOD TYPE : void
+//
+void RpmDb::removeV3( const Pathname & dbdir_r, bool v3backup_r )
+{
+ const char * master = "packages.rpm";
+ const char * index[] = {
+ "conflictsindex.rpm",
+ "fileindex.rpm",
+ "groupindex.rpm",
+ "nameindex.rpm",
+ "providesindex.rpm",
+ "requiredby.rpm",
+ "triggerindex.rpm",
+ // last entry!
+ NULL
+ };
+
+ PathInfo pi( dbdir_r );
+ if ( ! pi.isDir() ) {
+ ERR << "Can't remove rpm3 database in non directory: " << dbdir_r << endl;
+ return;
+ }
+
+ for ( const char ** f = index; *f; ++f ) {
+ pi( dbdir_r + *f );
+ if ( pi.isFile() ) {
+ PathInfo::unlink( pi.path() );
+ }
+ }
+
+#warning CHECK: compare vs existing v3 backup. notify root
+ pi( dbdir_r + master );
+ if ( pi.isFile() ) {
+ Pathname m( pi.path() );
+ if ( v3backup_r ) {
+ // backup was already created
+ PathInfo::unlink( m );
+ Pathname b( m.extend( "3" ) );
+ pi( b ); // stat backup
+ } else {
+ Pathname b( m.extend( ".deleted" ) );
+ pi( b );
+ if ( pi.isFile() ) {
+ // rempve existing backup
+ PathInfo::unlink( b );
+ }
+ PathInfo::rename( m, b );
+ pi( b ); // stat backup
+ }
+ MIL << "(Re)moved rpm3 database to " << pi << endl;
+ }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmDb::modifyDatabase
+// METHOD TYPE : void
+//
+void RpmDb::modifyDatabase()
+{
+ if ( ! initialized() )
+ return;
+
+ // tag database as modified
+ dbsi_set( _dbStateInfo, DbSI_MODIFIED_V4 );
+
+ // Move outdated rpm3 database beside.
+ if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) ) {
+ MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
+ removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
+ dbsi_clr( _dbStateInfo, DbSI_HAVE_V3 );
+ }
+
+ // invalidate Packages list
+ _packages._valid = false;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmDb::closeDatabase
+// METHOD TYPE : PMError
+//
+PMError RpmDb::closeDatabase()
+{
+ if ( ! initialized() ) {
+ return Error::E_ok;
+ }
+
+ MIL << "Calling closeDatabase: " << *this << endl;
+
+ ///////////////////////////////////////////////////////////////////
+ // Block further database access
+ ///////////////////////////////////////////////////////////////////
+ _packages.clear();
+ librpmDb::blockAccess();
+
+ ///////////////////////////////////////////////////////////////////
+ // Check fate if old version database still present
+ ///////////////////////////////////////////////////////////////////
+ if ( dbsi_has( _dbStateInfo, DbSI_HAVE_V3 ) ) {
+ MIL << "Update mode: Delayed cleanup: state " << _dbStateInfo << endl;
+ if ( dbsi_has( _dbStateInfo, DbSI_MODIFIED_V4 ) ) {
+ // Move outdated rpm3 database beside.
+ removeV3( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
+ } else {
+ // Remove unmodified rpm4 database
+ removeV4( _root + _dbPath, dbsi_has( _dbStateInfo, DbSI_MADE_V3TOV4 ) );
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ // Uninit
+ ///////////////////////////////////////////////////////////////////
+ _root = _dbPath = Pathname();
+ _dbStateInfo = DbSI_NO_INIT;
+
+ MIL << "closeDatabase: " << *this << endl;
+ return Error::E_ok;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmDb::rebuildDatabase
+// METHOD TYPE : PMError
+//
+PMError RpmDb::rebuildDatabase()
+{
+ FAILIFNOTINITIALIZED;
+
+ MIL << "RpmDb::rebuildDatabase" << *this << endl;
+ Timecount _t( "RpmDb::rebuildDatabase" );
+
+ PathInfo dbMaster( root() + dbPath() + "Packages" );
+ PathInfo dbMasterBackup( dbMaster.path().extend( ".y2backup" ) );
+
+ // report
+ RebuildDbReport::Send report( rebuildDbReport );
+ report->start();
+ ProgressData pd;
+ // current size should be upper limit for new db
+ report->progress( pd.init( dbMaster.size() ) );
+
+ // run rpm
+ RpmArgVec opts;
+ opts.push_back("--rebuilddb");
+ opts.push_back("-vv");
+
+ // don't call modifyDatabase because it would remove the old
+ // rpm3 database, if the current database is a temporary one.
+ // But do invalidate packages list.
+ _packages._valid = false;
+ run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
+
+ // progress report: watch this file growing
+ PathInfo newMaster( root()
+ + dbPath().extend( stringutil::form( "rebuilddb.%d",
+ process?process->getpid():0) )
+ + "Packages" );
+
+ string line;
+ string errmsg;
+
+ while ( systemReadLine( line ) ) {
+ if ( newMaster() ) { // file is removed at the end of rebuild.
+ report->progress( pd.set( newMaster.size() ) );
+ }
+
+ if ( line.compare( 0, 2, "D:" ) ) {
+ errmsg += line + '\n';
+ report->notify( line );
+ WAR << line << endl;
+ }
+ }
+
+ int rpm_status = systemStatus();
+
+ // evaluate result
+ PMError err;
+
+ if ( rpm_status != 0 ) {
+ ERR << "rpm failed, message was:" << endl << errmsg; // has trailing NL
+ err = Error::E_RpmDB_subprocess_failed;
+ err.setDetails(errmsg);
+ } else {
+ report->progress( pd.toMax() ); // 100%
+ }
+
+ report->stop( err );
+ return err;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmDb::importPubkey
+// METHOD TYPE : PMError
+//
+PMError RpmDb::importPubkey( const Pathname & pubkey_r )
+{
+ FAILIFNOTINITIALIZED;
+ PMError err;
+
+ RpmArgVec opts;
+ opts.push_back ( "--import" );
+ opts.push_back ( "--" );
+ opts.push_back ( pubkey_r.asString().c_str() );
+
+ // don't call modifyDatabase because it would remove the old
+ // rpm3 database, if the current database is a temporary one.
+ // But do invalidate packages list.
+ _packages._valid = false;
+ run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
+
+ string line;
+ while ( systemReadLine( line ) ) {
+ if ( line.substr( 0, 6 ) == "error:" ) {
+ WAR << line << endl;
+ } else {
+ DBG << line << endl;
+ }
+ }
+
+ int rpm_status = systemStatus();
+
+ if ( rpm_status != 0 ) {
+ ERR << "Failed to import public key from file " << pubkey_r << ": rpm returned " << rpm_status << endl;
+ err = Error::E_RpmDB_subprocess_failed;
+ } else {
+ MIL << "Imported public key from file " << pubkey_r << endl;
+ }
+
+ return err;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmDb::importPubkey
+// METHOD TYPE : PMError
+//
+PMError RpmDb::importPubkey( const Pathname & keyring_r, const string & keyname_r )
+{
+ FAILIFNOTINITIALIZED;
+
+ // create tempfile
+ char tmpname[] = "/tmp/y2.pubkey.XXXXXX";
+ int tmpfd = mkstemp( tmpname );
+ if ( tmpfd == -1 ) {
+ ERR << "Unable to create a unique temporary file for pubkey" << endl;
+ return Error::E_RpmDB_subprocess_failed;
+ }
+
+ // export keyname from keyring
+ RpmArgVec args;
+ args.push_back( "gpg" );
+ args.push_back( "--armor" );
+ args.push_back( "--no-default-keyring" );
+ args.push_back( "--keyring" );
+ args.push_back( keyring_r.asString().c_str() );
+ args.push_back( "--export" );
+ args.push_back( keyname_r.c_str() );
+
+ const char * argv[args.size() + 1];
+ const char ** p = argv;
+ p = copy( args.begin(), args.end(), p );
+ *p = 0;
+
+ // launch gpg
+ ExternalProgram prg( argv, ExternalProgram::Discard_Stderr, false, -1, true );
+ PMError err;
+ int res = 0;
+
+ // read key
+ for ( string line( prg.receiveLine() ); line.length(); line = prg.receiveLine() ) {
+ ssize_t written = write( tmpfd, line.c_str(), line.length() );
+ if ( written == -1 || unsigned(written) != line.length() ) {
+ ERR << "Error writing pubkey to " << tmpname << endl;
+ err = Error::E_RpmDB_subprocess_failed;
+ break;
+ }
+ res += written; // empty file indicates key not found
+ }
+ close( tmpfd );
+
+ if ( ! res ) {
+ WAR << "gpg: no key '" << keyname_r << "' found in '" << keyring_r << "'" << endl;
+ err = Error::E_RpmDB_subprocess_failed;
+ }
+
+ // check gpg returncode
+ res = prg.close();
+ if ( res ) {
+ ERR << "gpg: export '" << keyname_r << "' from '" << keyring_r << "' returned " << res << endl;
+ err = Error::E_RpmDB_subprocess_failed;
+ }
+
+ if ( ! err ) {
+ MIL << "Exported '" << keyname_r << "' from '" << keyring_r << "' to " << tmpname << endl;
+ err = importPubkey( tmpname );
+ }
+
+ // remove tempfile
+ PathInfo::unlink( tmpname );
+
+ return err;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmDb::pubkeys
+// METHOD TYPE : set<PkgEdition>
+//
+set<PkgEdition> RpmDb::pubkeys() const
+{
+ set<PkgEdition> ret;
+
+ librpmDb::db_const_iterator it;
+ for ( it.findByName( PkgName( "gpg-pubkey" ) ); *it; ++it ) {
+ ret.insert( it->tag_edition() );
+ }
+
+ return ret;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmDb::packagesValid
+// METHOD TYPE : bool
+//
+bool RpmDb::packagesValid() const
+{
+ return( _packages._valid || ! initialized() );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmDb::getPackages
+// METHOD TYPE : const std::list<PMPackagePtr> &
+//
+// DESCRIPTION :
+//
+const std::list<PMPackagePtr> & RpmDb::getPackages()
+{
+ if ( packagesValid() ) {
+ return _packages._list;
+ }
+
+ // report
+ Timecount _t( "RpmDb::getPackages" );
+ ScanDbReport::Send report( scanDbReport );
+ report->start();
+
+#warning how to detect corrupt db while reading.
+
+ _packages.clear();
+
+ ///////////////////////////////////////////////////////////////////
+ // Collect package data. A map is used to check whethere there are
+ // multiple entries for the same PkgName. If so we consider the last
+ // one installed to be the one we're interesed in.
+ ///////////////////////////////////////////////////////////////////
+ ProgressData pd;
+ librpmDb::db_const_iterator iter; // findAll
+ {
+ // quick check
+ unsigned expect = 0;
+ for ( ; *iter; ++iter ) {
+ ++expect;
+ }
+ if ( iter.dbError() ) {
+ ERR << "No database access: " << iter.dbError() << endl;
+ report->stop( iter.dbError() );
+ return _packages._list;
+ }
+ report->progress( pd.init( expect ) );
+ }
+
+ for ( iter.findAll(); *iter; ++iter, report->progress( pd.incr() ) ) {
+
+ PkgName name = iter->tag_name();
+ if ( name == PkgName( "gpg-pubkey" ) ) {
+ // pseudo package filtered, as we can't handle multiple instances
+ // of 'gpg-pubkey-VERS-REL'.
+ continue;
+ }
+ Date installtime = iter->tag_installtime();
+ PMPackagePtr & nptr = _packages._index[name]; // be shure to get a reference!
+
+ if ( nptr ) {
+ WAR << "Multiple entries for package '" << name << "' in rpmdb" << endl;
+ if ( nptr->installtime() > installtime )
+ continue;
+ // else overwrite previous entry
+ }
+
+ // create dataprovider and package
+ PMRpmPackageDataProviderPtr ndp = new PMRpmPackageDataProvider( this );
+ nptr = new PMPackage( name, iter->tag_edition(), iter->tag_arch(), ndp );
+
+ // add PMSolvable data to package, collect filerequires on the fly
+ nptr->setProvides ( iter->tag_provides ( &_filerequires ) );
+ nptr->setRequires ( iter->tag_requires ( &_filerequires ) );
+ nptr->setConflicts( iter->tag_conflicts( &_filerequires ) );
+ nptr->setObsoletes( iter->tag_obsoletes( &_filerequires ) );
+
+ // let dataprovider collect static data
+ ndp->loadStaticData( *iter );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ // Evaluate filerequires collected so far
+ ///////////////////////////////////////////////////////////////////
+ for( FileDeps::FileNames::iterator it = _filerequires.begin(); it != _filerequires.end(); ++it ) {
+
+ for ( iter.findByFile( *it ); *iter; ++iter ) {
+ PMPackagePtr pptr = _packages.lookup( iter->tag_name() );
+ if ( !pptr ) {
+ WAR << "rpmdb.findByFile returned unpknown package " << *iter << endl;
+ continue;
+ }
+ pptr->addProvides( *it );
+ }
+
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ // Build final packages list
+ ///////////////////////////////////////////////////////////////////
+ _packages.buildList();
+ DBG << "Found installed packages: " << _packages._list.size() << endl;
+ report->stop( PMError::E_ok );
+ return _packages._list;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmDb::traceFileRel
+// METHOD TYPE : void
+//
+// DESCRIPTION :
+//
+void RpmDb::traceFileRel( const PkgRelation & rel_r )
+{
+ if ( ! rel_r.isFileRel() )
+ return;
+
+ if ( ! _filerequires.insert( rel_r.name() ).second )
+ return; // already got it in _filerequires
+
+ if ( ! _packages._valid )
+ return; // collect only. Evaluated in first call to getPackages()
+
+ //
+ // packages already initialized. Must check and insert here
+ //
+ librpmDb::db_const_iterator iter;
+ if ( iter.dbError() ) {
+ ERR << "No database access: " << iter.dbError() << endl;
+ return;
+ }
+
+ for ( iter.findByFile( rel_r.name() ); *iter; ++iter ) {
+ PMPackagePtr pptr = _packages.lookup( iter->tag_name() );
+ if ( !pptr ) {
+ WAR << "rpmdb.findByFile returned unpknown package " << *iter << endl;
+ continue;
+ }
+ pptr->addProvides( rel_r.name() );
+ }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmDb::hasFile
+// METHOD TYPE : bool
+//
+// DESCRIPTION :
+//
+bool RpmDb::hasFile( const std::string & file_r ) const
+{
+ librpmDb::db_const_iterator it;
+ return it.findByFile( file_r );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmDb::hasProvides
+// METHOD TYPE : bool
+//
+// DESCRIPTION :
+//
+bool RpmDb::hasProvides( const std::string & tag_r ) const
+{
+ librpmDb::db_const_iterator it;
+ return it.findByProvides( tag_r );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmDb::hasRequiredBy
+// METHOD TYPE : bool
+//
+// DESCRIPTION :
+//
+bool RpmDb::hasRequiredBy( const std::string & tag_r ) const
+{
+ librpmDb::db_const_iterator it;
+ return it.findByRequiredBy( tag_r );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmDb::hasConflicts
+// METHOD TYPE : bool
+//
+// DESCRIPTION :
+//
+bool RpmDb::hasConflicts( const std::string & tag_r ) const
+{
+ librpmDb::db_const_iterator it;
+ return it.findByConflicts( tag_r );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmDb::hasPackage
+// METHOD TYPE : bool
+//
+// DESCRIPTION :
+//
+bool RpmDb::hasPackage( const PkgName & name_r ) const
+{
+ librpmDb::db_const_iterator it;
+ return it.findPackage( name_r );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmDb::getData
+// METHOD TYPE : PMError
+//
+// DESCRIPTION :
+//
+PMError RpmDb::getData( const PkgName & name_r,
+ constRpmHeaderPtr & result_r ) const
+{
+ librpmDb::db_const_iterator it;
+ it.findPackage( name_r );
+ result_r = *it;
+ return it.dbError();
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmDb::getData
+// METHOD TYPE : PMError
+//
+// DESCRIPTION :
+//
+PMError RpmDb::getData( const PkgName & name_r, const PkgEdition & ed_r,
+ constRpmHeaderPtr & result_r ) const
+{
+ librpmDb::db_const_iterator it;
+ it.findPackage( name_r, ed_r );
+ result_r = *it;
+ return it.dbError();
+}
+
+/*--------------------------------------------------------------*/
+/* Checking the source rpm <rpmpath> with rpm --chcksig and */
+/* the version number. */
+/*--------------------------------------------------------------*/
+unsigned
+RpmDb::checkPackage (const Pathname & packagePath, string version, string md5 )
+{
+ unsigned result = 0;
+
+ if ( ! version.empty() ) {
+ constRpmHeaderPtr h( RpmHeader::readPackage( packagePath, RpmHeader::NOSIGNATURE ) );
+ if ( ! h || PkgEdition( version ) != h->tag_edition() ) {
+ result |= CHK_INCORRECT_VERSION;
+ }
+ }
+
+ if(!md5.empty())
+ {
+#warning TBD MD5 check
+ WAR << "md5sum check not yet implemented" << endl;
+ return CHK_INCORRECT_FILEMD5;
+ }
+
+ std::string path = packagePath.asString();
+ // checking --checksig
+ const char *const argv[] = {
+ "rpm", "--checksig", "--", path.c_str(), 0
+ };
+
+ exit_code = -1;
+
+ string output = "";
+ unsigned int k;
+ for ( k = 0; k < (sizeof(argv) / sizeof(*argv)) -1; k++ )
+ {
+ output = output + " " + argv[k];
+ }
+
+ DBG << "rpm command: " << output << endl;
+
+ if ( process != NULL )
+ {
+ delete process;
+ process = NULL;
+ }
+ // Launch the program
+ process = new ExternalProgram( argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
+
+
+ if ( process == NULL )
+ {
+ result |= CHK_OTHER_FAILURE;
+ D__ << "create process failed" << endl;
+ }
+
+ string value;
+ output = process->receiveLine();
+
+ while ( output.length() > 0)
+ {
+ string::size_type ret;
+
+ // extract \n
+ ret = output.find_first_of ( "\n" );
+ if ( ret != string::npos )
+ {
+ value.assign ( output, 0, ret );
+ }
+ else
+ {
+ value = output;
+ }
+
+ D__ << "stdout: " << value << endl;
+
+ string::size_type pos;
+ if((pos = value.find (path)) != string::npos)
+ {
+ string rest = value.substr (pos + path.length() + 1);
+ if (rest.find("NOT OK") == string::npos)
+ {
+ // see what checks are ok
+ if (rest.find("md5") == string::npos)
+ {
+ result |= CHK_MD5SUM_MISSING;
+ }
+ if (rest.find("gpg") == string::npos)
+ {
+ result |= CHK_GPGSIG_MISSING;
+ }
+ }
+ else
+ {
+ // see what checks are not ok
+ if (rest.find("MD5") != string::npos)
+ {
+ result |= CHK_INCORRECT_PKGMD5;
+ }
+ else
+ {
+ result |= CHK_MD5SUM_MISSING;
+ }
+
+ if (rest.find("GPG") != string::npos)
+ {
+ result |= CHK_INCORRECT_GPGSIG;
+ }
+ else
+ {
+ result |= CHK_GPGSIG_MISSING;
+ }
+ }
+ }
+
+ output = process->receiveLine();
+ }
+
+ if ( result == 0 && systemStatus() != 0 )
+ {
+ // error
+ result |= CHK_OTHER_FAILURE;
+ }
+
+ return ( result );
+}
+
+// determine changed files of installed package
+bool
+RpmDb::queryChangedFiles(FileList & fileList, const string& packageName)
+{
+ bool ok = true;
+
+ fileList.clear();
+
+ if( ! initialized() ) return false;
+
+ RpmArgVec opts;
+
+ opts.push_back ("-V");
+ opts.push_back ("--nodeps");
+ opts.push_back ("--noscripts");
+ opts.push_back ("--nomd5");
+ opts.push_back ("--");
+ opts.push_back (packageName.c_str());
+
+ run_rpm (opts, ExternalProgram::Discard_Stderr);
+
+ if ( process == NULL )
+ return false;
+
+ /* from rpm manpage
+ 5 MD5 sum
+ S File size
+ L Symlink
+ T Mtime
+ D Device
+ U User
+ G Group
+ M Mode (includes permissions and file type)
+ */
+
+ string line;
+ while (systemReadLine(line))
+ {
+ if (line.length() > 12 &&
+ (line[0] == 'S' || line[0] == 's' ||
+ (line[0] == '.' && line[7] == 'T')))
+ {
+ // file has been changed
+ string filename;
+
+ filename.assign(line, 11, line.length() - 11);
+ fileList.insert(filename);
+ }
+ }
+
+ systemStatus();
+ // exit code ignored, rpm returns 1 no matter if package is installed or
+ // not
+
+ return ok;
+}
+
+
+
+/****************************************************************/
+/* private member-functions */
+/****************************************************************/
+
+/*--------------------------------------------------------------*/
+/* Run rpm with the specified arguments, handling stderr */
+/* as specified by disp */
+/*--------------------------------------------------------------*/
+void
+RpmDb::run_rpm (const RpmArgVec& opts,
+ ExternalProgram::Stderr_Disposition disp)
+{
+ if ( process ) {
+ delete process;
+ process = NULL;
+ }
+ exit_code = -1;
+
+ if ( ! initialized() ) {
+ ERR << "Attempt to run rpm: " << Error::E_RpmDB_not_open << endl;
+ return;
+ }
+
+ RpmArgVec args;
+
+ // always set root and dbpath
+ args.push_back("rpm");
+ args.push_back("--root");
+ args.push_back(_root.asString().c_str());
+ args.push_back("--dbpath");
+ args.push_back(_dbPath.asString().c_str());
+
+ const char* argv[args.size() + opts.size() + 1];
+
+ const char** p = argv;
+ p = copy (args.begin (), args.end (), p);
+ p = copy (opts.begin (), opts.end (), p);
+ *p = 0;
+
+ // Invalidate all outstanding database handles in case
+ // the database gets modified.
+ librpmDb::dbRelease( true );
+
+ // Launch the program with default locale
+ process = new ExternalProgram(argv, disp, false, -1, true);
+ return;
+}
+
+/*--------------------------------------------------------------*/
+/* Read a line from the rpm process */
+/*--------------------------------------------------------------*/
+bool
+RpmDb::systemReadLine(string &line)
+{
+ line.erase();
+
+ if ( process == NULL )
+ return false;
+
+ line = process->receiveLine();
+
+ if (line.length() == 0)
+ return false;
+
+ if (line[line.length() - 1] == '\n')
+ line.erase(line.length() - 1);
+
+ return true;
+}
+
+/*--------------------------------------------------------------*/
+/* Return the exit status of the rpm process, closing the */
+/* connection if not already done */
+/*--------------------------------------------------------------*/
+int
+RpmDb::systemStatus()
+{
+ if ( process == NULL )
+ return -1;
+
+ exit_code = process->close();
+ process->kill();
+ delete process;
+ process = 0;
+
+// D__ << "exit code " << exit_code << endl;
+
+ return exit_code;
+}
+
+/*--------------------------------------------------------------*/
+/* Forcably kill the rpm process */
+/*--------------------------------------------------------------*/
+void
+RpmDb::systemKill()
+{
+ if (process) process->kill();
+}
+
+
+// generate diff mails for config files
+void RpmDb::processConfigFiles(const string& line, const string& name, const char* typemsg, const char* difffailmsg, const char* diffgenmsg)
+{
+ string msg = line.substr(9);
+ string::size_type pos1 = string::npos;
+ string::size_type pos2 = string::npos;
+ string file1s, file2s;
+ Pathname file1;
+ Pathname file2;
+
+ pos1 = msg.find (typemsg);
+ for (;;)
+ {
+ if( pos1 == string::npos )
+ break;
+
+ pos2 = pos1 + strlen (typemsg);
+
+ if (pos2 >= msg.length() )
+ break;
+
+ file1 = msg.substr (0, pos1);
+ file2 = msg.substr (pos2);
+
+ file1s = file1.asString();
+ file2s = file2.asString();
+
+ if (!_root.empty() && _root != "/")
+ {
+ file1 = _root + file1;
+ file2 = _root + file2;
+ }
+
+ string out;
+ int ret = Diff::differ (file1.asString(), file2.asString(), out, 25);
+ if (ret)
+ {
+ Pathname file = _root + WARNINGMAILPATH;
+ if (PathInfo::assert_dir(file) != 0)
+ {
+ ERR << "Could not create " << file.asString() << endl;
+ break;
+ }
+ file += Date::form("config_diff_%Y_%m_%d.log", Date::now());
+ ofstream notify(file.asString().c_str(), std::ios::out|std::ios::app);
+ if(!notify)
+ {
+ ERR << "Could not open " << file << endl;
+ break;
+ }
+
+ // Translator: %s = name of an rpm package. A list of diffs follows
+ // this message.
+ notify << stringutil::form(_("Changed configuration files for %s:"), name.c_str()) << endl;
+ if(ret>1)
+ {
+ ERR << "diff failed" << endl;
+ notify << stringutil::form(difffailmsg,
+ file1s.c_str(), file2s.c_str()) << endl;
+ }
+ else
+ {
+ notify << stringutil::form(diffgenmsg,
+ file1s.c_str(), file2s.c_str()) << endl;
+
+ // remove root for the viewer's pleasure (#38240)
+ if (!_root.empty() && _root != "/")
+ {
+ if(out.substr(0,4) == "--- ")
+ {
+ out.replace(4, file1.asString().length(), file1s);
+ }
+ string::size_type pos = out.find("\n+++ ");
+ if(pos != string::npos)
+ {
+ out.replace(pos+5, file2.asString().length(), file2s);
+ }
+ }
+ notify << out << endl;
+ }
+ notify.close();
+ notify.open("/var/lib/update-messages/yast2-packagemanager.rpmdb.configfiles");
+ notify.close();
+ }
+ else
+ {
+ WAR << "rpm created " << file2 << " but it is not different from " << file2 << endl;
+ }
+ break;
+ }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmDb::installPackage
+// METHOD TYPE : PMError
+//
+PMError RpmDb::installPackage( const Pathname & filename, unsigned flags )
+{
+ FAILIFNOTINITIALIZED;
+ Logfile progresslog;
+
+ MIL << "RpmDb::installPackage(" << filename << "," << flags << ")" << endl;
+
+ // report
+ InstallPkgReport::Send report( installPkgReport );
+ report->start( filename );
+ ProgressData pd;
+
+ // backup
+ if ( _packagebackups ) {
+ report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
+ if ( ! backupPackage( filename ) ) {
+ ERR << "backup of " << filename.asString() << " failed" << endl;
+ }
+ report->progress( pd.set( 0 ) ); // allow 1% for backup creation.
+ } else {
+ report->progress( pd.init( 100 ) );
+ }
+
+ // run rpm
+ RpmArgVec opts;
+ if (flags & RPMINST_NOUPGRADE)
+ opts.push_back("-i");
+ else
+ opts.push_back("-U");
+ opts.push_back("--percent");
+
+ if (flags & RPMINST_NODIGEST)
+ opts.push_back("--nodigest");
+ if (flags & RPMINST_NOSIGNATURE)
+ opts.push_back("--nosignature");
+ if (flags & RPMINST_NODOCS)
+ opts.push_back ("--excludedocs");
+ if (flags & RPMINST_NOSCRIPTS)
+ opts.push_back ("--noscripts");
+ if (flags & RPMINST_FORCE)
+ opts.push_back ("--force");
+ if (flags & RPMINST_NODEPS)
+ opts.push_back ("--nodeps");
+ if(flags & RPMINST_IGNORESIZE)
+ opts.push_back ("--ignoresize");
+ if(flags & RPMINST_JUSTDB)
+ opts.push_back ("--justdb");
+
+ opts.push_back("--");
+ opts.push_back (filename.asString().c_str());
+
+ modifyDatabase(); // BEFORE run_rpm
+ run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
+
+ string line;
+ string rpmmsg;
+ vector<string> configwarnings;
+ vector<string> errorlines;
+
+ while (systemReadLine(line))
+ {
+ if (line.substr(0,2)=="%%")
+ {
+ int percent;
+ sscanf (line.c_str () + 2, "%d", &percent);
+ report->progress( pd.set( percent ) );
+ }
+ else
+ rpmmsg += line+'\n';
+
+ if( line.substr(0,8) == "warning:" )
+ {
+ configwarnings.push_back(line);
+ }
+ }
+ int rpm_status = systemStatus();
+
+ // evaluate result
+ for(vector<string>::iterator it = configwarnings.begin();
+ it != configwarnings.end(); ++it)
+ {
+ processConfigFiles(*it, Pathname::basename(filename), " saved as ",
+ // %s = filenames
+ _("rpm saved %s as %s but it was impossible to determine the difference"),
+ // %s = filenames
+ _("rpm saved %s as %s.\nHere are the first 25 lines of difference:\n"));
+ processConfigFiles(*it, Pathname::basename(filename), " created as ",
+ // %s = filenames
+ _("rpm created %s as %s but it was impossible to determine the difference"),
+ // %s = filenames
+ _("rpm created %s as %s.\nHere are the first 25 lines of difference:\n"));
+ }
+
+ PMError err;
+
+ if ( rpm_status != 0 ) {
+ // %s = filename of rpm package
+ progresslog(/*timestamp*/true) << stringutil::form(_("%s install failed"), Pathname::basename(filename).c_str()) << endl;
+ progresslog() << _("rpm output:") << endl << rpmmsg << endl;
+ ERR << "rpm failed, message was: " << rpmmsg << endl;
+ err = Error::E_RpmDB_subprocess_failed;
+ err.setDetails( rpmmsg );
+ } else {
+ // %s = filename of rpm package
+ progresslog(/*timestamp*/true) << stringutil::form(_("%s installed ok"), Pathname::basename(filename).c_str()) << endl;
+ if( ! rpmmsg.empty() ) {
+ progresslog() << _("Additional rpm output:") << endl << rpmmsg << endl;
+ }
+ }
+
+ report->stop( err );
+ return err;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmDb::removePackage
+// METHOD TYPE : PMError
+//
+PMError RpmDb::removePackage( constPMPackagePtr package, unsigned flags )
+{
+ return removePackage( package->name(), flags );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmDb::removePackage
+// METHOD TYPE : PMError
+//
+PMError RpmDb::removePackage( const string & name_r, unsigned flags )
+{
+ FAILIFNOTINITIALIZED;
+ Logfile progresslog;
+
+ MIL << "RpmDb::removePackage(" << name_r << "," << flags << ")" << endl;
+
+ // report
+ RemovePkgReport::Send report( removePkgReport );
+ report->start( name_r );
+ ProgressData pd;
+
+ // backup
+ if ( _packagebackups ) {
+ report->progress( pd.init( -2, 100 ) ); // allow 1% for backup creation.
+ if ( ! backupPackage( name_r ) ) {
+ ERR << "backup of " << name_r << " failed" << endl;
+ }
+ report->progress( pd.set( 0 ) );
+ } else {
+ report->progress( pd.init( 100 ) );
+ }
+
+ // run rpm
+ RpmArgVec opts;
+ opts.push_back("-e");
+ opts.push_back("--allmatches");
+
+ if (flags & RPMINST_NOSCRIPTS)
+ opts.push_back("--noscripts");
+ if (flags & RPMINST_NODEPS)
+ opts.push_back("--nodeps");
+ if (flags & RPMINST_JUSTDB)
+ opts.push_back("--justdb");
+ if (flags & RPMINST_FORCE) {
+ WAR << "IGNORE OPTION: 'rpm -e' does not support '--force'" << endl;
+ }
+
+ opts.push_back("--");
+ opts.push_back(name_r.c_str());
+
+ modifyDatabase(); // BEFORE run_rpm
+ run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
+
+ string line;
+ string rpmmsg;
+
+ // got no progress from command, so we fake it:
+ // 5 - command started
+ // 50 - command completed
+ // 100 if no error
+ report->progress( pd.set( 5 ) );
+ while (systemReadLine(line))
+ {
+ rpmmsg += line+'\n';
+ }
+ report->progress( pd.set( 50 ) );
+ int rpm_status = systemStatus();
+
+ // evaluate result
+ PMError err;
+
+ if ( rpm_status != 0 ) {
+ // %s = name of rpm package
+ progresslog(/*timestamp*/true) << stringutil::form(_("%s remove failed"), name_r.c_str()) << endl;
+ progresslog() << _("rpm output:") << endl << rpmmsg << endl;
+ ERR << "rpm failed, message was: " << rpmmsg << endl;
+ err = Error::E_RpmDB_subprocess_failed;
+ err.setDetails( rpmmsg );
+ } else {
+ progresslog(/*timestamp*/true) << stringutil::form(_("%s remove ok"), name_r.c_str()) << endl;
+ if( ! rpmmsg.empty() ) {
+ progresslog() << _("Additional rpm output:") << endl << rpmmsg << endl;
+ }
+ }
+
+ report->stop( err );
+ return err;
+}
+
+string
+RpmDb::checkPackageResult2string(unsigned code)
+{
+ string msg;
+ // begin of line characters
+ string bol = " - ";
+ // end of line characters
+ string eol = "\n";
+ if(code == 0)
+ return string(_("Ok"))+eol;
+
+ //translator: these are different kinds of how an rpm package can be broken
+ msg = _("Package is not OK for the following reasons:");
+ msg += eol;
+
+ if(code&CHK_INCORRECT_VERSION)
+ {
+ msg += bol;
+ msg+=_("Package contains different version than expected");
+ msg += eol;
+ }
+ if(code&CHK_INCORRECT_FILEMD5)
+ {
+ msg += bol;
+ msg+=_("Package file has incorrect MD5 sum");
+ msg += eol;
+ }
+ if(code&CHK_GPGSIG_MISSING)
+ {
+ msg += bol;
+ msg+=_("Package is not signed");
+ msg += eol;
+ }
+ if(code&CHK_MD5SUM_MISSING)
+ {
+ msg += bol;
+ msg+=_("Package has no MD5 sum");
+ msg += eol;
+ }
+ if(code&CHK_INCORRECT_GPGSIG)
+ {
+ msg += bol;
+ msg+=_("Package has incorrect signature");
+ msg += eol;
+ }
+ if(code&CHK_INCORRECT_PKGMD5)
+ {
+ msg += bol;
+ msg+=_("Package archive has incorrect MD5 sum");
+ msg += eol;
+ }
+ if(code&CHK_OTHER_FAILURE)
+ {
+ msg += bol;
+ msg+=_("rpm failed for unkown reason, see log file");
+ msg += eol;
+ }
+
+ return msg;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmDb::backupPackage
+// METHOD TYPE : bool
+//
+bool RpmDb::backupPackage( const Pathname & filename )
+{
+ constRpmHeaderPtr h( RpmHeader::readPackage( filename, RpmHeader::NOSIGNATURE ) );
+ if( ! h )
+ return false;
+
+ return backupPackage( h->tag_name() );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmDb::backupPackage
+// METHOD TYPE : bool
+//
+bool RpmDb::backupPackage(const string& packageName)
+{
+ Logfile progresslog;
+ bool ret = true;
+ Pathname backupFilename;
+ Pathname filestobackupfile = _root+_backuppath+FILEFORBACKUPFILES;
+
+ if (_backuppath.empty())
+ {
+ INT << "_backuppath empty" << endl;
+ return false;
+ }
+
+ FileList fileList;
+
+ if (!queryChangedFiles(fileList, packageName))
+ {
+ ERR << "Error while getting changed files for package " <<
+ packageName << endl;
+ return false;
+ }
+
+ if (fileList.size() <= 0)
+ {
+ DBG << "package " << packageName << " not changed -> no backup" << endl;
+ return true;
+ }
+
+ if (PathInfo::assert_dir(_root + _backuppath) != 0)
+ {
+ return false;
+ }
+
+ {
+ // build up archive name
+ time_t currentTime = time(0);
+ struct tm *currentLocalTime = localtime(¤tTime);
+
+ int date = (currentLocalTime->tm_year + 1900) * 10000
+ + (currentLocalTime->tm_mon + 1) * 100
+ + currentLocalTime->tm_mday;
+
+ int num = 0;
+ do
+ {
+ backupFilename = _root + _backuppath
+ + stringutil::form("%s-%d-%d.tar.gz",packageName.c_str(), date, num);
+
+ }
+ while ( PathInfo(backupFilename).isExist() && num++ < 1000);
+
+ PathInfo pi(filestobackupfile);
+ if(pi.isExist() && !pi.isFile())
+ {
+ ERR << filestobackupfile.asString() << " already exists and is no file" << endl;
+ return false;
+ }
+
+ std::ofstream fp ( filestobackupfile.asString().c_str(), std::ios::out|std::ios::trunc );
+
+ if(!fp)
+ {
+ ERR << "could not open " << filestobackupfile.asString() << endl;
+ return false;
+ }
+
+ for (FileList::const_iterator cit = fileList.begin();
+ cit != fileList.end(); ++cit)
+ {
+ string name = *cit;
+ if ( name[0] == '/' )
+ {
+ // remove slash, file must be relative to -C parameter of tar
+ name = name.substr( 1 );
+ }
+ D__ << "saving file "<< name << endl;
+ fp << name << endl;
+ }
+ fp.close();
+
+ const char* const argv[] =
+ {
+ "tar",
+ "-czhP",
+ "-C",
+ _root.asString().c_str(),
+ "--ignore-failed-read",
+ "-f",
+ backupFilename.asString().c_str(),
+ "-T",
+ filestobackupfile.asString().c_str(),
+ NULL
+ };
+
+ // execute tar in inst-sys (we dont know if there is a tar below _root !)
+ ExternalProgram tar(argv, ExternalProgram::Stderr_To_Stdout, false, -1, true);
+
+ string tarmsg;
+
+ // TODO: its probably possible to start tar with -v and watch it adding
+ // files to report progress
+ for (string output = tar.receiveLine(); output.length() ;output = tar.receiveLine())
+ {
+ tarmsg+=output;
+ }
+
+ int ret = tar.close();
+
+ if ( ret != 0)
+ {
+ ERR << "tar failed: " << tarmsg << endl;
+ ret = false;
+ }
+ else
+ {
+ MIL << "tar backup ok" << endl;
+ progresslog(/*timestamp*/true) << stringutil::form(_("created backup %s"), backupFilename.asString().c_str()) << endl;
+ }
+
+ PathInfo::unlink(filestobackupfile);
+ }
+
+ return ret;
+}
+
+void RpmDb::setBackupPath(const Pathname& path)
+{
+ _backuppath = path;
+}
--- /dev/null
+/*---------------------------------------------------------------------\
+| ____ _ __ __ ___ |
+| |__ / \ / / . \ . \ |
+| / / \ V /| _/ _/ |
+| / /__ | | | | | | |
+| /_____||_| |_| |_| |
+| |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/RpmDb.h
+ *
+*/
+
+// -*- C++ -*-
+
+#ifndef RpmDb_h
+#define RpmDb_h
+
+#include <iosfwd>
+#include <list>
+#include <vector>
+#include <string>
+
+#include "zypp/Pathname.h"
+#include "zypp/ExternalProgram.h"
+
+#include "zypp/PMPackagePtr.h"
+
+namespace zypp {
+ namespace target {
+ namespace rpm {
+
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ // CLASS NAME : RpmDb
+ /**
+ * @short Interface to the rpm program
+ **/
+ class RpmDb : public base::ReferenceCounted, private base::NonCopyable
+ {
+ REP_BODY(RpmDb);
+
+ public:
+
+ /**
+ * Default error class
+ **/
+ typedef class InstTargetError Error;
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ // INITALISATION
+ //
+ ///////////////////////////////////////////////////////////////////
+ private:
+
+ enum DbStateInfoBits {
+ DbSI_NO_INIT = 0x0000,
+ DbSI_HAVE_V4 = 0x0001,
+ DbSI_MADE_V4 = 0x0002,
+ DbSI_MODIFIED_V4 = 0x0004,
+ DbSI_HAVE_V3 = 0x0008,
+ DbSI_HAVE_V3TOV4 = 0x0010,
+ DbSI_MADE_V3TOV4 = 0x0020
+ };
+
+ friend std::ostream & operator<<( std::ostream & str, const DbStateInfoBits & obj );
+
+ void dbsi_set( DbStateInfoBits & val_r, const unsigned & bits_r ) const {
+ val_r = (DbStateInfoBits)(val_r | bits_r);
+ }
+ void dbsi_clr( DbStateInfoBits & val_r, const unsigned & bits_r ) const {
+ val_r = (DbStateInfoBits)(val_r & ~bits_r);
+ }
+ bool dbsi_has( const DbStateInfoBits & val_r, const unsigned & bits_r ) const {
+ return( (val_r & bits_r) == bits_r );
+ }
+
+ /**
+ * Internal state info
+ **/
+ DbStateInfoBits _dbStateInfo;
+
+ /**
+ * Root directory for all operations.
+ **/
+ Pathname _root;
+
+ /**
+ * Directory that contains the rpmdb.
+ **/
+ Pathname _dbPath;
+
+ /**
+ * Internal helper for @ref initDatabase.
+ **/
+ PMError internal_initDatabase( const Pathname & root_r, const Pathname & dbPath_r,
+ DbStateInfoBits & info_r );
+
+ /**
+ * Remove the rpm4 database in dbdir_r and optionally any backup created
+ * on conversion.
+ **/
+ static void removeV4( const Pathname & dbdir_r, bool v3backup_r );
+
+ /**
+ * Remove the rpm3 database in dbdir_r. Create a backup copy named
+ * packages.rpm3 if it does not already exist.
+ **/
+ static void removeV3( const Pathname & dbdir_r, bool v3backup_r );
+
+ /**
+ * Called before the database is modified by installPackage/removePackage.
+ * Invalidates Packages list and moves away any old database.
+ **/
+ void modifyDatabase();
+
+ public:
+
+ /**
+ * Constructor. There's no rpmdb access until @ref initDatabase
+ * was called.
+ **/
+ RpmDb();
+
+ /**
+ * Destructor.
+ **/
+ ~RpmDb();
+
+ /**
+ * @return Root directory for all operations (empty if not initialized).
+ **/
+ const Pathname & root() const { return _root; }
+
+ /**
+ * @return Directory that contains the rpmdb (empty if not initialized).
+ **/
+ const Pathname & dbPath() const { return _dbPath; }
+
+ /**
+ * @return Whether we are initialized.
+ **/
+ bool initialized() const { return( ! _root.empty() ); }
+
+ /**
+ * Prepare access to the rpm database. Optional arguments may denote the
+ * root directory for all operations and the directory (below root) that
+ * contains the rpmdb (usg. you won't need to set this).
+ *
+ * On empty Pathnames the default is used:
+ * <PRE>
+ * root: /
+ * dbPath: /var/lib/rpm
+ * </PRE>
+ *
+ * Calling initDatabase a second time with different arguments will return
+ * an error but leave the database in it's original state.
+ *
+ * Converting an old batabase is done if necessary. On update: The converted
+ * database will be removed by @ref closeDatabase, if it was not modified
+ * (no packages were installed or deleted). Otherwise the new database
+ * is kept, and the old one is removed.
+ **/
+ PMError initDatabase( Pathname root_r = Pathname(),
+ Pathname dbPath_r = Pathname() );
+
+ /**
+ * Block further access to the rpm database and go back to uninitialized
+ * state. On update: Decides what to do with any converted database
+ * (see @ref initDatabase).
+ **/
+ PMError closeDatabase();
+
+ /**
+ * Rebuild the rpm database (rpm --rebuilddb).
+ **/
+ PMError rebuildDatabase();
+
+ /**
+ * Import ascii armored public key in file pubkey_r.
+ **/
+ PMError importPubkey( const Pathname & pubkey_r );
+
+ /**
+ * Import ascii armored public key keyname_r exported by keyring_r.
+ **/
+ PMError importPubkey( const Pathname & keyring_r, const std::string & keyname_r );
+
+ /**
+ * Return the editions of all installed public keys.
+ **/
+ std::set<PkgEdition> pubkeys() const;
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ // Cached RPM database retrieval via librpm.
+ //
+ ///////////////////////////////////////////////////////////////////
+ private:
+
+ class Packages;
+
+ Packages & _packages;
+
+ FileDeps::FileNames _filerequires;
+
+ public:
+
+ /**
+ * @return Whether the list of installed packages is valid, or
+ * you'd better reread it. (<B>NOTE:</B> returns valid, if not
+ * initialized).
+ **/
+ bool packagesValid() const;
+
+ /**
+ * If necessary build, and return the list of all installed packages.
+ **/
+ const std::list<PMPackagePtr> & getPackages();
+
+
+ /**
+ * Hack to lookup required and conflicting file relations.
+ **/
+ void traceFileRel( const PkgRelation & rel_r );
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ // Direct RPM database retrieval via librpm.
+ //
+ ///////////////////////////////////////////////////////////////////
+ public:
+
+ /**
+ * Return true if at least one package owns a certain file.
+ **/
+ bool hasFile( const std::string & file_r ) const;
+
+ /**
+ * Return true if at least one package provides a certain tag.
+ **/
+ bool hasProvides( const std::string & tag_r ) const;
+
+ /**
+ * Return true if at least one package requires a certain tag.
+ **/
+ bool hasRequiredBy( const std::string & tag_r ) const;
+
+ /**
+ * Return true if at least one package conflicts with a certain tag.
+ **/
+ bool hasConflicts( const std::string & tag_r ) const;
+
+ /**
+ * Return true if package is installed.
+ **/
+ bool hasPackage( const PkgName & name_r ) const;
+
+ /**
+ * Get an installed packages data from rpmdb. Package is
+ * identified by name. Data returned via result are NULL,
+ * if packge is not installed (PMError is not set), or RPM database
+ * could not be read (PMError is set).
+ **/
+ PMError getData( const PkgName & name_r,
+ constRpmHeaderPtr & result_r ) const;
+
+ /**
+ * Get an installed packages data from rpmdb. Package is
+ * identified by name and edition. Data returned via result are NULL,
+ * if packge is not installed (PMError is not set), or RPM database
+ * could not be read (PMError is set).
+ **/
+ PMError getData( const PkgName & name_r, const PkgEdition & ed_r,
+ constRpmHeaderPtr & result_r ) const;
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ ///////////////////////////////////////////////////////////////////
+ private:
+
+ /**
+ * The connection to the rpm process.
+ */
+ ExternalProgram *process;
+
+ typedef std::vector<const char*> RpmArgVec;
+
+ /**
+ * Run rpm with the specified arguments and handle stderr.
+ * @param n_opts The number of arguments
+ * @param options Array of the arguments, @ref n_opts elements
+ * @param stderr_disp How to handle stderr, merged with stdout by default
+ */
+ void run_rpm( const RpmArgVec& options,
+ ExternalProgram::Stderr_Disposition stderr_disp =
+ ExternalProgram::Stderr_To_Stdout);
+
+
+ /**
+ * Read a line from the general rpm query
+ */
+ bool systemReadLine(std::string &line);
+
+ /**
+ * Return the exit status of the general rpm process,
+ * closing the connection if not already done.
+ */
+ int systemStatus();
+
+ /**
+ * Forcably kill the system process
+ */
+ void systemKill();
+
+ /**
+ * The exit code of the rpm process, or -1 if not yet known.
+ */
+ int exit_code;
+
+ /** /var/adm/backup */
+ Pathname _backuppath;
+
+ /** create package backups? */
+ bool _packagebackups;
+
+ /** whether <_root>/<WARNINGMAILPATH> was already created */
+ bool _warndirexists;
+
+ /**
+ * handle rpm messages like "/etc/testrc saved as /etc/testrc.rpmorig"
+ *
+ * @param line rpm output starting with warning:
+ * @param name name of package, appears in subject line
+ * @param typemsg " saved as " or " created as "
+ * @param difffailmsg what to put into mail if diff failed, must contain two %s for the two files
+ * @param diffgenmsg what to put into mail if diff succeeded, must contain two %s for the two files
+ * */
+ void processConfigFiles(const std::string& line,
+ const std::string& name,
+ const char* typemsg,
+ const char* difffailmsg,
+ const char* diffgenmsg);
+
+
+ public:
+
+ typedef std::set<std::string> FileList;
+
+ /**
+ * Bits representing rpm installation options, useable as or
+ * combination
+ *
+ * @see installPackage(), removePackage()
+ * */
+ enum RpmInstFlag
+ {
+ RPMINST_NONE = 0x0000,
+ RPMINST_NODOCS = 0x0001,
+ RPMINST_NOSCRIPTS = 0x0002,
+ RPMINST_FORCE = 0x0004,
+ RPMINST_NODEPS = 0x0008,
+ RPMINST_IGNORESIZE = 0x0010,
+ RPMINST_JUSTDB = 0x0020,
+ RPMINST_NODIGEST = 0x0040,
+ RPMINST_NOSIGNATURE= 0x0080,
+ RPMINST_NOUPGRADE = 0x0100
+ };
+
+ /**
+ * Bits of possible package corruptions
+ * @see checkPackage
+ * @see checkPackageResult2string
+ * */
+ enum checkPackageResult
+ {
+ CHK_OK = 0x00,
+ CHK_INCORRECT_VERSION = 0x01, // package does not contain expected version
+ CHK_INCORRECT_FILEMD5 = 0x02, // md5sum of file is wrong (outside)
+ CHK_GPGSIG_MISSING = 0x04, // package is not signeed
+ CHK_MD5SUM_MISSING = 0x08, // package is not signeed
+ CHK_INCORRECT_GPGSIG = 0x10, // signature incorrect
+ CHK_INCORRECT_PKGMD5 = 0x20, // md5sum incorrect (inside)
+ CHK_OTHER_FAILURE = 0x40 // rpm failed for some reason
+ };
+
+
+ /**
+ * Check rpm with rpm --checksig
+ *
+ * @param filename which file to check
+ * @param version check if package really contains this version, leave emtpy to skip check
+ * @param md5 md5sum for whole file, leave empty to skip check (not yet implemented)
+ *
+ * @return checkPackageResult
+ */
+ unsigned checkPackage (const Pathname& filename, std::string version = "", std::string md5 = "" );
+
+ /** install rpm package
+ *
+ * @param filename file to install
+ * @param flags which rpm options to use
+ *
+ * @return success
+ * */
+ PMError installPackage (const Pathname& filename, unsigned flags = 0 );
+
+ /** remove rpm package
+ *
+ * @param name_r Name of the rpm package to remove.
+ * @param iflags which rpm options to use
+ *
+ * @return success
+ * */
+ PMError removePackage(const std::string & name_r, unsigned flags = 0);
+ PMError removePackage(constPMPackagePtr package, unsigned flags = 0);
+
+ /**
+ * get backup dir for rpm config files
+ *
+ * */
+ Pathname getBackupPath (void) { return _backuppath; }
+
+ /**
+ * create tar.gz of all changed files in a Package
+ *
+ * @param packageName name of the Package to backup
+ *
+ * @see setBackupPath
+ * */
+ bool backupPackage(const std::string& packageName);
+
+ /**
+ * queries file for name and then calls above backupPackage
+ * function. For convenience.
+ *
+ * @param filename rpm file that is about to be installed
+ * */
+ bool backupPackage(const Pathname& filename);
+
+ /**
+ * set path where package backups are stored
+ *
+ * @see backupPackage
+ * */
+ void setBackupPath(const Pathname& path);
+
+ /**
+ * whether to create package backups during install or
+ * removal
+ *
+ * @param yes true or false
+ * */
+ void createPackageBackups(bool yes) { _packagebackups = yes; }
+
+ /**
+ * determine which files of an installed package have been
+ * modified.
+ *
+ * @param fileList (output) where to store modified files
+ * @param packageName name of package to query
+ *
+ * @return false if package couln't be queried for some
+ * reason
+ * */
+ bool queryChangedFiles(FileList & fileList, const std::string& packageName);
+
+ public: // static members
+
+ /** create error description of bits set according to
+ * checkPackageResult
+ * */
+ static std::string checkPackageResult2string(unsigned code);
+
+ public:
+
+ /**
+ * Dump debug info.
+ **/
+ virtual std::ostream & dumpOn( std::ostream & str ) const;
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ // Installation log
+ //
+ ///////////////////////////////////////////////////////////////////
+ private:
+
+ /**
+ * Progress of installation may be logged to file
+ **/
+ class Logfile;
+
+ public:
+
+ /**
+ * Set logfile for progress log. Empty filename to disable logging.
+ **/
+ static bool setInstallationLogfile( const Pathname & filename );
+
+ };
+
+ } // namespace rpm
+ } // namespace target
+} // namespace zypp
+
+#endif // RpmDb_h
--- /dev/null
+/*---------------------------------------------------------------------\
+| ____ _ __ __ ___ |
+| |__ / \ / / . \ . \ |
+| / / \ V /| _/ _/ |
+| / /__ | | | | | | |
+| /_____||_| |_| |_| |
+| |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/RpmHeader.cc
+ *
+*/
+#include "librpm.h"
+
+#include <iostream>
+#include <map>
+#include <set>
+
+#include "zypp/base/Logger.h"
+#include "zypp/PathInfo.h"
+
+#include "zypp/target/rpm/RpmHeader.h"
+#include "zypp/CapFactory.h"
+#include "zypp/Rel.h"
+#warning FIXME this include
+#if 0
+#include <y2pm/PkgDu.h>
+#endif
+
+using namespace std;
+
+namespace zypp {
+ namespace target {
+ namespace rpm {
+
+ ///////////////////////////////////////////////////////////////////
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::RpmHeader
+ // METHOD TYPE : Constructor
+ //
+ // DESCRIPTION :
+ //
+ RpmHeader::RpmHeader( Header h_r )
+ : BinHeader( h_r )
+ {
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::RpmHeader
+ // METHOD TYPE : Constructor
+ //
+ RpmHeader::RpmHeader( BinHeader::Ptr & rhs )
+ : BinHeader( rhs )
+ {
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::~RpmHeader
+ // METHOD TYPE : Destructor
+ //
+ // DESCRIPTION :
+ //
+ RpmHeader::~RpmHeader()
+ {
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::readPackage
+ // METHOD TYPE : constRpmHeaderPtr
+ //
+ RpmHeader::constPtr RpmHeader::readPackage( const Pathname & path_r,
+ VERIFICATION verification_r )
+ {
+ PathInfo file( path_r );
+ if ( ! file.isFile() ) {
+ ERR << "Not a file: " << file << endl;
+ return (RpmHeader*)0;
+ }
+
+ FD_t fd = ::Fopen( file.asString().c_str(), "r.ufdio" );
+ if ( fd == 0 || ::Ferror(fd) ) {
+ ERR << "Can't open file for reading: " << file << " (" << ::Fstrerror(fd) << ")" << endl;
+ if ( fd )
+ ::Fclose( fd );
+ return (RpmHeader*)0;
+ }
+
+ rpmts ts = ::rpmtsCreate();
+ unsigned vsflag = RPMVSF_DEFAULT;
+ if ( verification_r & NODIGEST )
+ vsflag |= _RPMVSF_NODIGESTS;
+ if ( verification_r & NOSIGNATURE )
+ vsflag |= _RPMVSF_NOSIGNATURES;
+ ::rpmtsSetVSFlags( ts, rpmVSFlags(vsflag) );
+
+ Header nh = 0;
+ int res = ::rpmReadPackageFile( ts, fd, path_r.asString().c_str(), &nh );
+
+ ts = ::rpmtsFree(ts);
+
+ ::Fclose( fd );
+
+ if ( ! nh ) {
+ WAR << "Error reading header from " << path_r << " error(" << res << ")" << endl;
+ return (RpmHeader*)0;
+ }
+
+ RpmHeader::constPtr h( new RpmHeader( nh ) );
+ headerFree( nh ); // clear the reference set in ReadPackageFile
+
+ MIL << h << " from " << path_r << endl;
+ return h;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::dumpOn
+ // METHOD TYPE : ostream &
+ //
+ // DESCRIPTION :
+ //
+ ostream & RpmHeader::dumpOn( ostream & str ) const
+ {
+ return BinHeader::dumpOn( str ) << '{' << tag_name() << "-" << tag_edition() << ( isSrc() ? ".src}" : "}");
+ }
+
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::isSrc
+ // METHOD TYPE : bool
+ //
+ bool RpmHeader::isSrc() const
+ {
+ return has_tag( RPMTAG_SOURCEPACKAGE );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::tag_name
+ // METHOD TYPE : string
+ //
+ // DESCRIPTION :
+ //
+ string RpmHeader::tag_name() const
+ {
+ return string( string_val( RPMTAG_NAME ) );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::tag_edition
+ // METHOD TYPE : Edition
+ //
+ // DESCRIPTION :
+ //
+ Edition RpmHeader::tag_edition() const
+ {
+ return Edition( string_val ( RPMTAG_EPOCH ),
+ string_val( RPMTAG_VERSION ),
+ string_val( RPMTAG_RELEASE ) );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::tag_arch
+ // METHOD TYPE : Arch
+ //
+ // DESCRIPTION :
+ //
+ Arch RpmHeader::tag_arch() const
+ {
+ return Arch( string_val( RPMTAG_ARCH ) );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::tag_installtime
+ // METHOD TYPE : Date
+ //
+ // DESCRIPTION :
+ //
+ Date RpmHeader::tag_installtime() const
+ {
+ return int_val( RPMTAG_INSTALLTIME );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::tag_buildtime
+ // METHOD TYPE : Date
+ //
+ // DESCRIPTION :
+ //
+ Date RpmHeader::tag_buildtime() const
+ {
+ return int_val( RPMTAG_BUILDTIME );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::PkgRelList_val
+ // METHOD TYPE : unsigned
+ //
+ // DESCRIPTION :
+ //
+ CapSet RpmHeader::PkgRelList_val( tag tag_r, bool pre, set<string> * freq_r ) const
+ {
+ CapSet ret;
+
+ int_32 kindFlags = 0;
+ int_32 kindVersion = 0;
+
+ switch ( tag_r ) {
+ case RPMTAG_REQUIRENAME:
+ kindFlags = RPMTAG_REQUIREFLAGS;
+ kindVersion = RPMTAG_REQUIREVERSION;
+ break;
+ case RPMTAG_PROVIDENAME:
+ kindFlags = RPMTAG_PROVIDEFLAGS;
+ kindVersion = RPMTAG_PROVIDEVERSION;
+ break;
+ case RPMTAG_OBSOLETENAME:
+ kindFlags = RPMTAG_OBSOLETEFLAGS;
+ kindVersion = RPMTAG_OBSOLETEVERSION;
+ break;
+ case RPMTAG_CONFLICTNAME:
+ kindFlags = RPMTAG_CONFLICTFLAGS;
+ kindVersion = RPMTAG_CONFLICTVERSION;
+ break;
+ default:
+ INT << "Illegal RPMTAG_dependencyNAME " << tag_r << endl;
+ return ret;
+ break;
+ }
+
+ stringList names;
+ unsigned count = string_list( tag_r, names );
+ if ( !count )
+ return ret;
+
+ intList flags;
+ int_list( kindFlags, flags );
+
+ stringList versions;
+ string_list( kindVersion, versions );
+
+ for ( unsigned i = 0; i < count; ++i ) {
+
+ string n( names[i] );
+
+ Rel op = Rel::ANY;
+ int_32 f = flags[i];
+ string v = versions[i];
+
+ if ( n[0] == '/' ) {
+ if ( freq_r ) {
+ freq_r->insert( n );
+ }
+ } else {
+ if ( v.size() ) {
+ switch ( f & RPMSENSE_SENSEMASK ) {
+ case RPMSENSE_LESS:
+ op = Rel::LT;
+ break;
+ case RPMSENSE_LESS|RPMSENSE_EQUAL:
+ op = Rel::LE;
+ break;
+ case RPMSENSE_GREATER:
+ op = Rel::GT;
+ break;
+ case RPMSENSE_GREATER|RPMSENSE_EQUAL:
+ op = Rel::GE;
+ break;
+ case RPMSENSE_EQUAL:
+ op = Rel::EQ;
+ break;
+ }
+ }
+ }
+ if ((pre && (f & RPMSENSE_PREREQ))
+ || ((! pre) && !(f & RPMSENSE_PREREQ)))
+ {
+ CapFactory _f;
+ Capability cap = _f.parse(
+ ResTraits<Package>::kind,
+ n,
+ op,
+ Edition(v)
+ );
+ ret.insert(cap);
+ }
+ }
+
+ return ret;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::tag_provides
+ // METHOD TYPE : CapSet
+ //
+ // DESCRIPTION :
+ //
+ CapSet RpmHeader::tag_provides( set<string> * freq_r ) const
+ {
+ return PkgRelList_val( RPMTAG_PROVIDENAME, false, freq_r );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::tag_requires
+ // METHOD TYPE : CapSet
+ //
+ // DESCRIPTION :
+ //
+ CapSet RpmHeader::tag_requires( set<string> * freq_r ) const
+ {
+ return PkgRelList_val( RPMTAG_REQUIRENAME, false, freq_r );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::tag_requires
+ // METHOD TYPE : CapSet
+ //
+ // DESCRIPTION :
+ //
+ CapSet RpmHeader::tag_prerequires( set<string> * freq_r ) const
+ {
+ return PkgRelList_val( RPMTAG_REQUIRENAME, true, freq_r );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::tag_conflicts
+ // METHOD TYPE : CapSet
+ //
+ // DESCRIPTION :
+ //
+ CapSet RpmHeader::tag_conflicts( set<string> * freq_r ) const
+ {
+ return PkgRelList_val( RPMTAG_CONFLICTNAME, false, freq_r );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::tag_obsoletes
+ // METHOD TYPE : CapSet
+ //
+ // DESCRIPTION :
+ //
+ CapSet RpmHeader::tag_obsoletes( set<string> * freq_r ) const
+ {
+ return PkgRelList_val( RPMTAG_OBSOLETENAME, false, freq_r );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::tag_size
+ // METHOD TYPE : ByteCount
+ //
+ // DESCRIPTION :
+ //
+ ByteCount RpmHeader::tag_size() const
+ {
+ return int_val( RPMTAG_SIZE );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::tag_archivesize
+ // METHOD TYPE : ByteCount
+ //
+ // DESCRIPTION :
+ //
+ ByteCount RpmHeader::tag_archivesize() const
+ {
+ return int_val( RPMTAG_ARCHIVESIZE );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::tag_summary
+ // METHOD TYPE : std::string
+ //
+ // DESCRIPTION :
+ //
+ std::string RpmHeader::tag_summary() const
+ {
+ return string_val( RPMTAG_SUMMARY );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::tag_description
+ // METHOD TYPE : std::string
+ //
+ // DESCRIPTION :
+ //
+ std::string RpmHeader::tag_description() const
+ {
+ return string_val( RPMTAG_DESCRIPTION );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::tag_group
+ // METHOD TYPE : std::string
+ //
+ // DESCRIPTION :
+ //
+ std::string RpmHeader::tag_group() const
+ {
+ return string_val( RPMTAG_GROUP );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::tag_vendor
+ // METHOD TYPE : std::string
+ //
+ // DESCRIPTION :
+ //
+ std::string RpmHeader::tag_vendor() const
+ {
+ return string_val( RPMTAG_VENDOR );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::tag_distribution
+ // METHOD TYPE : std::string
+ //
+ // DESCRIPTION :
+ //
+ std::string RpmHeader::tag_distribution() const
+ {
+ return string_val( RPMTAG_DISTRIBUTION );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::tag_license
+ // METHOD TYPE : std::string
+ //
+ // DESCRIPTION :
+ //
+ std::string RpmHeader::tag_license() const
+ {
+ return string_val( RPMTAG_LICENSE );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::tag_buildhost
+ // METHOD TYPE : std::string
+ //
+ // DESCRIPTION :
+ //
+ std::string RpmHeader::tag_buildhost() const
+ {
+ return string_val( RPMTAG_BUILDHOST );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::tag_packager
+ // METHOD TYPE : std::string
+ //
+ // DESCRIPTION :
+ //
+ std::string RpmHeader::tag_packager() const
+ {
+ return string_val( RPMTAG_PACKAGER );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::tag_url
+ // METHOD TYPE : std::string
+ //
+ // DESCRIPTION :
+ //
+ std::string RpmHeader::tag_url() const
+ {
+ return string_val( RPMTAG_URL );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::tag_os
+ // METHOD TYPE : std::string
+ //
+ // DESCRIPTION :
+ //
+ std::string RpmHeader::tag_os() const
+ {
+ return string_val( RPMTAG_OS );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::tag_prein
+ // METHOD TYPE : std::string
+ //
+ // DESCRIPTION :
+ //
+ std::string RpmHeader::tag_prein() const
+ {
+ return string_val( RPMTAG_PREIN );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::tag_postin
+ // METHOD TYPE : std::string
+ //
+ // DESCRIPTION :
+ //
+ std::string RpmHeader::tag_postin() const
+ {
+ return string_val( RPMTAG_POSTIN );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::tag_preun
+ // METHOD TYPE : std::string
+ //
+ // DESCRIPTION :
+ //
+ std::string RpmHeader::tag_preun() const
+ {
+ return string_val( RPMTAG_PREUN );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::tag_postun
+ // METHOD TYPE : std::string
+ //
+ // DESCRIPTION :
+ //
+ std::string RpmHeader::tag_postun() const
+ {
+ return string_val( RPMTAG_POSTUN );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::tag_sourcerpm
+ // METHOD TYPE : std::string
+ //
+ // DESCRIPTION :
+ //
+ std::string RpmHeader::tag_sourcerpm() const
+ {
+ return string_val( RPMTAG_SOURCERPM );
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::tag_filenames
+ // METHOD TYPE : std::list<std::string>
+ //
+ // DESCRIPTION :
+ //
+ std::list<std::string> RpmHeader::tag_filenames() const
+ {
+ std::list<std::string> ret;
+
+ stringList basenames;
+ if ( string_list( RPMTAG_BASENAMES, basenames ) ) {
+ stringList dirnames;
+ string_list( RPMTAG_DIRNAMES, dirnames );
+ intList dirindexes;
+ int_list( RPMTAG_DIRINDEXES, dirindexes );
+ for ( unsigned i = 0; i < basenames.size(); ++ i ) {
+ ret.push_back( dirnames[dirindexes[i]] + basenames[i] );
+ }
+ }
+
+ return ret;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::tag_changelog
+ // METHOD TYPE : Changelog
+ //
+ // DESCRIPTION :
+ //
+ Changelog RpmHeader::tag_changelog() const
+ {
+ Changelog ret;
+
+ intList times;
+ if ( int_list( RPMTAG_CHANGELOGTIME, times ) ) {
+ stringList names;
+ string_list( RPMTAG_CHANGELOGNAME, names );
+ stringList texts;
+ string_list( RPMTAG_CHANGELOGTEXT, texts );
+ for ( unsigned i = 0; i < times.size(); ++ i ) {
+ ret.push_back( ChangelogEntry( times[i], names[i], texts[i] ) );
+ }
+ }
+
+ return ret;
+ }
+
+#warning FIXME disk usage data
+#if 0
+ ///////////////////////////////////////////////////////////////////
+ //
+ //
+ // METHOD NAME : RpmHeader::tag_du
+ // METHOD TYPE : PkgDu &
+ //
+ // DESCRIPTION :
+ //
+ PkgDu & RpmHeader::tag_du( PkgDu & dudata_r ) const
+ {
+ dudata_r.clear();
+ stringList basenames;
+ if ( string_list( RPMTAG_BASENAMES, basenames ) ) {
+ stringList dirnames;
+ string_list( RPMTAG_DIRNAMES, dirnames );
+ intList dirindexes;
+ int_list( RPMTAG_DIRINDEXES, dirindexes );
+
+ intList filedevices;
+ int_list( RPMTAG_FILEDEVICES, filedevices );
+ intList fileinodes;
+ int_list( RPMTAG_FILEINODES, fileinodes );
+ intList filesizes;
+ int_list( RPMTAG_FILESIZES, filesizes );
+ intList filemodes;
+ int_list( RPMTAG_FILEMODES, filemodes );
+
+ ///////////////////////////////////////////////////////////////////
+ // Create and collect Entries by index. devino_cache is used to
+ // filter out hardliks ( different name but same device and inode ).
+ ///////////////////////////////////////////////////////////////////
+ PathInfo::devino_cache trace;
+ vector<PkgDu::Entry> entries;
+ entries.resize( dirnames.size() );
+ for ( unsigned i = 0; i < dirnames.size(); ++i ) {
+ entries[i] = dirnames[i];
+ }
+
+ for ( unsigned i = 0; i < basenames.size(); ++ i ) {
+ PathInfo::stat_mode mode( filemodes[i] );
+ if ( mode.isFile() ) {
+ if ( trace.insert( filedevices[i], fileinodes[i] ) ) {
+ // Count full 1K blocks
+ entries[dirindexes[i]]._size += ByteCount( filesizes[i] ).fillBlock();
+ ++(entries[dirindexes[i]]._files);
+ }
+ // else: hardlink; already counted this device/inode
+ }
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ // Crreate and collect by index Entries. DevInoTrace is used to
+ // filter out hardliks ( different name but same device and inode ).
+ ///////////////////////////////////////////////////////////////////
+ for ( unsigned i = 0; i < entries.size(); ++i ) {
+ if ( entries[i]._size ) {
+ dudata_r.add( entries[i] );
+ }
+ }
+ }
+ return dudata_r;
+ }
+#endif
+
+ } // namespace rpm
+ } // namespace target
+} // namespace zypp
--- /dev/null
+/*---------------------------------------------------------------------\
+| ____ _ __ __ ___ |
+| |__ / \ / / . \ . \ |
+| / / \ V /| _/ _/ |
+| / /__ | | | | | | |
+| /_____||_| |_| |_| |
+| |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/RpmHeader.h
+ *
+*/
+#ifndef ZYPP_TARGET_RPM_RPMHEADER_H
+#define ZYPP_TARGET_RPM_RPMHEADER_H
+
+#include <iosfwd>
+#include <list>
+
+#include "zypp/target/rpm/BinHeader.h"
+
+#include "zypp/Package.h"
+#include "zypp/Changelog.h"
+#include "zypp/CapSetFwd.h"
+
+namespace zypp {
+ namespace target {
+ namespace rpm {
+
+#if 0
+ class PkgDu;
+#endif
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ // CLASS NAME : RpmHeader
+ /**
+ * @short Wrapper class for rpm header struct.
+ *
+ * <code>RpmHeader</code> provides methods to query the content
+ * of a rpm header struct retrieved from the RPM database or by reading
+ * the rpm header of a package on disk.
+ *
+ * The rpm header contains all data associated with a package. So you
+ * probabely do not want to permanently store too many of them.
+ *
+ * <B>NEVER create <code>RpmHeader</code> from a NULL <code>Header</code>! </B>
+ **/
+ class RpmHeader : public BinHeader {
+ public:
+ typedef intrusive_ptr<RpmHeader> Ptr;
+ typedef intrusive_ptr<const RpmHeader> constPtr;
+
+ private:
+
+ CapSet PkgRelList_val( tag tag_r, bool pre, std::set<std::string> * freq_r = 0 ) const;
+
+ public:
+
+ /**
+ *
+ **/
+ RpmHeader( Header h_r = 0 );
+
+ /**
+ * <B>Dangerous!<\B> This one takes the header out of rhs
+ * and leaves rhs empty.
+ **/
+ RpmHeader( BinHeader::Ptr & rhs );
+
+ virtual ~RpmHeader();
+
+ bool isSrc() const;
+
+ public:
+
+ std::string tag_name() const;
+ Edition tag_edition() const;
+ Arch tag_arch() const;
+
+ Date tag_installtime() const;
+ Date tag_buildtime() const;
+
+ /**
+ * If <code>freq_r</code> is not NULL, file dependencies found are inserted.
+ **/
+ CapSet tag_provides ( std::set<std::string> * freq_r = 0 ) const;
+ /**
+ * @see #tag_provides
+ **/
+ CapSet tag_requires ( std::set<std::string> * freq_r = 0 ) const;
+ /**
+ * @see #tag_provides
+ **/
+ CapSet tag_prerequires ( std::set<std::string> * freq_r = 0 ) const;
+ /**
+ * @see #tag_provides
+ **/
+ CapSet tag_conflicts( std::set<std::string> * freq_r = 0 ) const;
+ /**
+ * @see #tag_provides
+ **/
+ CapSet tag_obsoletes( std::set<std::string> * freq_r = 0 ) const;
+
+ ByteCount tag_size() const;
+ ByteCount tag_archivesize() const;
+
+ std::string tag_summary() const;
+ std::string tag_description() const;
+ std::string tag_group() const;
+ std::string tag_vendor() const;
+ std::string tag_distribution() const;
+ std::string tag_license() const;
+ std::string tag_buildhost() const;
+ std::string tag_packager() const;
+ std::string tag_url() const;
+ std::string tag_os() const;
+ std::string tag_prein() const;
+ std::string tag_postin() const;
+ std::string tag_preun() const;
+ std::string tag_postun() const;
+ std::string tag_sourcerpm() const;
+
+ std::list<std::string> tag_filenames() const;
+
+ Changelog tag_changelog() const;
+
+#warning FIXME disk usage data
+#if 0
+ /**
+ * Returns reference to arg <code>dudata_r</code>.
+ **/
+ PkgDu & tag_du( PkgDu & dudata_r ) const;
+#endif
+
+ public:
+
+ virtual std::ostream & dumpOn( std::ostream & str ) const;
+
+ public:
+
+ /**
+ * Digest and signature verification flags
+ **/
+ enum VERIFICATION {
+ VERIFY = 0x0000,
+ NODIGEST = (1<<0),
+ NOSIGNATURE = (1<<1),
+ NOVERIFY = 0xffff
+ };
+
+ /**
+ * Get an accessible packages data from disk.
+ * Returns NULL on any error.
+ **/
+ static RpmHeader::constPtr readPackage( const Pathname & path,
+ VERIFICATION verification = VERIFY );
+ };
+
+///////////////////////////////////////////////////////////////////
+ } // namespace rpm
+ } // namespace target
+} // namespace zypp
+
+#endif // ZYPP_TARGET_RPM_RPMHEADER_H
+
--- /dev/null
+/*---------------------------------------------------------------------\
+| ____ _ __ __ ___ |
+| |__ / \ / / . \ . \ |
+| / / \ V /| _/ _/ |
+| / /__ | | | | | | |
+| /_____||_| |_| |_| |
+| |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/RpmHeaderCache.cc
+ *
+*/
+
+#include <iostream>
+
+#include "zypp/base/Logger.h"
+
+#include "zypp/target/rpm/RpmHeaderCache.h"
+#include "zypp/target/rpm/RpmHeader.h"
+#include "librpm.h"
+#include "zypp/PathInfo.h"
+
+using namespace std;
+
+namespace zypp {
+ namespace target {
+ namespace rpm {
+
+///////////////////////////////////////////////////////////////////
+
+#warning add this function if needed
+#if 0
+const PkgNameEd & RpmHeaderCache::def_magic()
+{
+ static PkgNameEd _def_magic( PkgName("YaST-PHC"), PkgEdition("1.0-0") );
+ return _def_magic;
+}
+#endif
+
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmHeaderCache::RpmHeaderCache
+// METHOD TYPE : Constructor
+//
+RpmHeaderCache::RpmHeaderCache( const Pathname & cache_r )
+ : BinHeaderCache( cache_r )
+{
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmHeaderCache::~RpmHeaderCache
+// METHOD TYPE : Destructor
+//
+RpmHeaderCache::~RpmHeaderCache()
+{
+}
+
+#warning add this function if needed
+#if 0
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmHeaderCache::magicOk
+// METHOD TYPE : bool
+//
+bool RpmHeaderCache::magicOk()
+{
+ PkgNameEd magic( PkgNameEd::fromString( _cmagic ) );
+ if ( magic != def_magic() ) {
+ ERR << "Found magic " << magic << ", expected " << def_magic() << endl;
+ return false;
+ }
+ DBG << "Found magic " << magic << endl;
+ return true;
+}
+#endif
+
+///////////////////////////////////////////////////////////////////
+#define RETURN_IF_CLOSED(R) if ( !isOpen() ) { ERR << "Cache not open: " << _cpath << endl; return R; }
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmHeaderCache::getFirst
+// METHOD TYPE : constRpmHeaderPtr
+//
+RpmHeader::constPtr RpmHeaderCache::getFirst( Pathname & citem_r, int & isSource_r, pos & at_r )
+{
+ RETURN_IF_CLOSED( (RpmHeader*)0 );
+
+ if ( seek( _cheaderStart ) == npos ) {
+ ERR << "Can't seek to first header at " << _cheaderStart << endl;
+ return (RpmHeader*)0;
+ }
+
+ return getNext( citem_r, isSource_r, at_r );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmHeaderCache::getNext
+// METHOD TYPE : constRpmHeaderPtr
+//
+RpmHeader::constPtr RpmHeaderCache::getNext( Pathname & citem_r, int & isSource_r, pos & at_r )
+{
+ RETURN_IF_CLOSED( RpmHeader::constPtr() );
+
+ static const unsigned sigsize = 8;
+
+ char sig[sigsize+1];
+ sig[sigsize] = '\0';
+
+ unsigned count = readData( sig, sigsize );
+ if ( count != sigsize ) {
+ if ( count ) {
+ ERR << "Error reading entry." << endl;
+ } // else EOF?
+ return (RpmHeader*)0;
+ }
+
+ if ( sig[0] != '@' || sig[sigsize-1] != '@' ) {
+ ERR << "Invalid entry." << endl;
+ return (RpmHeader*)0;
+ }
+
+ sig[sigsize-1] = '\0';
+ count = atoi( &sig[1] );
+
+ char citem[count+1];
+ citem[count] = '\0';
+
+ if ( readData( citem, count ) != count ) {
+ ERR << "Error reading entry data." << endl;
+ return (RpmHeader*)0;
+ }
+
+ isSource_r = ( citem[0] == 's' );
+ citem_r = &citem[1];
+ at_r = tell();
+
+ return getAt( at_r );
+}
+
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmHeaderCache::getAt
+// METHOD TYPE : constRpmHeaderPtr
+//
+RpmHeader::constPtr RpmHeaderCache::getAt( pos at_r )
+{
+ RETURN_IF_CLOSED( RpmHeader::constPtr() );
+
+ if ( seek( at_r ) == npos ) {
+ ERR << "Can't seek to header at " << at_r << endl;
+ return (RpmHeader*)0;
+ }
+
+ BinHeader::Ptr bp = readHeader();
+ if ( !bp ) {
+ ERR << "Can't read header at " << at_r << endl;
+ return (RpmHeader*)0;
+ }
+
+ return new RpmHeader( bp );
+}
+
+/******************************************************************
+**
+**
+** FUNCTION NAME : operator<<
+** FUNCTION TYPE : ostream &
+*/
+ostream & operator<<( ostream & str, const RpmHeaderCache & obj )
+{
+ return str << "RpmHeaderCache@" << static_cast<const BinHeaderCache &>(obj);
+}
+
+///////////////////////////////////////////////////////////////////
+//
+#warning buildHeaderCache needs cleanup
+//
+///////////////////////////////////////////////////////////////////
+
+static const unsigned PHC_MAGIC_SZE = 64; // dont change!
+static const string PHC_MAGIC( "YaST-PHC-1.0-0" );
+
+/******************************************************************
+**
+**
+** FUNCTION NAME : phcAddMagic
+** FUNCTION TYPE : void
+*/
+void phcAddMagic( FD_t fd )
+{
+ char magic[PHC_MAGIC_SZE];
+ memset( magic, 0, PHC_MAGIC_SZE );
+ strcpy( magic, PHC_MAGIC.c_str() );
+ strcpy( magic+PHC_MAGIC.size()+1, str::numstring( Date::now() ).c_str() );
+
+ ::Fwrite( magic, sizeof(char), PHC_MAGIC_SZE, fd );
+}
+
+/******************************************************************
+**
+**
+** FUNCTION NAME : phcAddHeader
+** FUNCTION TYPE : unsigned
+*/
+unsigned phcAddHeader( FD_t fd, Header h, const Pathname & citem_r, int isSource )
+{
+ string entry = str::form( "%c%s", (isSource?'s':'b'), citem_r.asString().c_str() );
+ entry = str::form( "@%6zu@%s", entry.size(), entry.c_str() );
+
+ ::Fwrite( entry.c_str(), sizeof(char), entry.size(), fd );
+ ::headerWrite( fd, h, HEADER_MAGIC_YES );
+
+ return 1;
+}
+
+/******************************************************************
+**
+**
+** FUNCTION NAME : phcAddFile
+** FUNCTION TYPE : unsigned
+*/
+unsigned phcAddFile( FD_t fd, const PathInfo & cpath_r, const Pathname & citem_r )
+{
+ FD_t pkg = ::Fopen( cpath_r.asString().c_str(), "r.ufdio" );
+ if ( pkg == 0 || ::Ferror(pkg) ) {
+ ERR << "Can't open file for reading: " << cpath_r << " (" << ::Fstrerror(pkg) << ")" << endl;
+ if ( pkg )
+ ::Fclose( pkg );
+ return 0;
+ }
+
+ rpmts ts = rpmtsCreate();
+ Header h = 0;
+ int res = ::rpmReadPackageFile( ts, pkg, cpath_r.path().asString().c_str(), &h );
+ ts = rpmtsFree(ts);
+ ::Fclose( pkg );
+
+ if ( ! h ) {
+ WAR << "Error reading header from " << cpath_r << " error(" << res << ")" << endl;
+ return 0;
+ }
+
+ RpmHeader::constPtr dummy( new RpmHeader( h ) ); // to handle header free
+ headerFree( h ); // clear reference set in ReadPackageFile
+ MIL << dummy << " for " << citem_r << endl;
+
+ return phcAddHeader( fd, h, citem_r, dummy->isSrc() );
+}
+
+/******************************************************************
+**
+**
+** FUNCTION NAME : phcScanDir
+** FUNCTION TYPE : unsigned
+*/
+unsigned phcScanDir( FD_t fd, const PathInfo & cpath_r, const Pathname & prfx_r,
+ const RpmHeaderCache::buildOpts & options_r )
+{
+ DBG << "SCAN " << cpath_r << " (" << prfx_r << ")" << endl;
+
+ list<string> retlist;
+ int res = filesystem::readdir( retlist, cpath_r.path(), false );
+ if ( res ) {
+ ERR << "Error reading content of " << cpath_r << " (readdir " << res << ")" << endl;
+ return 0;
+ }
+
+ unsigned count = 0;
+ list<string> downlist;
+
+ for ( list<string>::const_iterator it = retlist.begin(); it != retlist.end(); ++it ) {
+ PathInfo cpath( cpath_r.path() + *it, PathInfo::LSTAT );
+ if ( cpath.isFile() ) {
+ count += phcAddFile( fd, cpath, prfx_r + *it );
+ } else if ( options_r.recurse && cpath.isDir() ) {
+ downlist.push_back( *it );
+ }
+ }
+ retlist.clear();
+
+ for ( list<string>::const_iterator it = downlist.begin(); it != downlist.end(); ++it ) {
+ count += phcScanDir( fd, PathInfo(cpath_r.path() + *it), prfx_r + *it, options_r );
+ }
+
+ return count;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : RpmHeaderCache::buildHeaderCache
+// METHOD TYPE : int
+//
+int RpmHeaderCache::buildHeaderCache( const Pathname & cache_r,
+ const Pathname & pkgroot_r,
+ const buildOpts & options_r )
+{
+ ///////////////////////////////////////////////////////////////////
+ // Check pkgroot
+ ///////////////////////////////////////////////////////////////////
+
+ PathInfo pkgroot( pkgroot_r );
+ if ( !pkgroot.isDir() ) {
+ ERR << "Not a directory: Pkgroot " << pkgroot << endl;
+ return -1;
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ // Prepare cache file
+ ///////////////////////////////////////////////////////////////////
+ FD_t fd = ::Fopen( cache_r.asString().c_str(), "w" );
+ if ( fd == 0 || ::Ferror(fd) ) {
+ ERR << "Can't open cache for writing: " << cache_r << " (" << ::Fstrerror(fd) << ")" << endl;
+ if ( fd )
+ ::Fclose( fd );
+ return -2;
+ }
+
+ phcAddMagic( fd );
+
+ ///////////////////////////////////////////////////////////////////
+ // Scan pkgroot_r
+ ///////////////////////////////////////////////////////////////////
+ MIL << "Start scan below " << pkgroot_r
+ << " (recurse=" << (options_r.recurse?"yes":"no")
+ << ")" << endl;
+ unsigned count = phcScanDir( fd, pkgroot, "/", options_r );
+
+ if ( ::Ferror(fd) ) {
+ ERR << "Error writing cache: " << cache_r << " (" << ::Fstrerror(fd) << ")" << endl;
+ ::Fclose( fd );
+ return -3;
+ }
+
+ MIL << "Found " << count << " package(s) below " << pkgroot_r << endl;
+ ::Fclose( fd );
+ return count;
+}
+
+ } // namespace rpm
+ } // namespace target
+} // namespace zypp
--- /dev/null
+/*---------------------------------------------------------------------\
+| ____ _ __ __ ___ |
+| |__ / \ / / . \ . \ |
+| / / \ V /| _/ _/ |
+| / /__ | | | | | | |
+| /_____||_| |_| |_| |
+| |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/RpmHeaderCache.h
+ *
+*/
+#ifndef ZYPP_TARGET_RPM_RPMHEADERCACHE_H
+#define ZYPP_TARGET_RPM_RPMHEADERCACHE_H
+
+#include <iosfwd>
+
+#include "zypp/Pathname.h"
+#include "zypp/target/rpm/BinHeaderCache.h"
+#include "zypp/target/rpm/RpmHeader.h"
+
+namespace zypp {
+ namespace target {
+ namespace rpm {
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ // CLASS NAME : RpmHeaderCache
+ /**
+ *
+ **/
+ class RpmHeaderCache : public BinHeaderCache {
+
+ friend std::ostream & operator<<( std::ostream & str, const RpmHeaderCache & obj );
+
+ RpmHeaderCache & operator=( const RpmHeaderCache & );
+ RpmHeaderCache ( const RpmHeaderCache & );
+
+ private:
+#warning Add this function if it is needed
+#if 0
+ static const PkgNameEd & def_magic();
+
+ protected:
+
+ virtual bool magicOk();
+#endif
+
+ public:
+
+ RpmHeaderCache( const Pathname & cache_r );
+ virtual ~RpmHeaderCache();
+
+ RpmHeader::constPtr getFirst( Pathname & citem_r, int & isSource_r, pos & at_r );
+ RpmHeader::constPtr getNext( Pathname & citem_r, int & isSource_r, pos & at_r );
+
+ RpmHeader::constPtr getAt( pos at_r );
+
+ public:
+
+ struct buildOpts {
+ bool recurse;
+ buildOpts()
+ : recurse( false )
+ {}
+ };
+
+ static int buildHeaderCache( const Pathname & cache_r, const Pathname & pkgroot_r,
+ const buildOpts & options_r = buildOpts() );
+ };
+
+ ///////////////////////////////////////////////////////////////////
+
+ } // namespace rpm
+ } // namespace target
+} // namespace zypp
+
+#endif // ZYPP_TARGET_RPM_RPMHEADERCACHE_H
--- /dev/null
+/*---------------------------------------------------------------------\
+| |
+| __ __ ____ _____ ____ |
+| \ \ / /_ _/ ___|_ _|___ \ |
+| \ V / _` \___ \ | | __) | |
+| | | (_| |___) || | / __/ |
+| |_|\__,_|____/ |_| |_____| |
+| |
+| core system |
+| (C) SuSE GmbH |
+\----------------------------------------------------------------------/
+
+ File: librpm.h
+
+ Author: Michael Andres <ma@suse.de>
+ Maintainer: Michael Andres <ma@suse.de>
+
+ Purpose:
+
+/-*/
+#ifndef librpm_h
+#define librpm_h
+
+extern "C" {
+#include <rpm/rpmlib.h>
+#include <rpm/rpmmacro.h>
+#include <rpm/rpmdb.h>
+#include <rpm/rpmts.h>
+#include <fcntl.h>
+}
+
+#endif // librpm_h
--- /dev/null
+/*---------------------------------------------------------------------\
+| ____ _ __ __ ___ |
+| |__ / \ / / . \ . \ |
+| / / \ V /| _/ _/ |
+| / /__ | | | | | | |
+| /_____||_| |_| |_| |
+| |
+\---------------------------------------------------------------------*/
+/** \file zypp/target/rpm/BinHeader.h
+ *
+*/
+#include "librpm.h"
+
+#include <iostream>
+
+#include "zypp/base/Logger.h"
+#include "zypp/target/rpm/librpmDb.h"
+#include "zypp/target/rpm/RpmHeader.h"
+#include "zypp/base/Exception.h"
+
+using namespace std;
+
+namespace zypp {
+ namespace target {
+ namespace rpm {
+///////////////////////////////////////////////////////////////////
+//
+// CLASS NAME : librpmDb::D
+/**
+ * @short librpmDb internal database handle
+ **/
+class librpmDb::D {
+ D & operator=( const D & ); // NO ASSIGNMENT!
+ D ( const D & ); // NO COPY!
+ public:
+
+ const Pathname _root; // root directory for all operations
+ const Pathname _dbPath; // directory (below root) that contains the rpmdb
+ rpmdb _db; // database handle
+#warning fix string representation of the error
+ shared_ptr<Exception> _error; // database error
+
+ friend ostream & operator<<( ostream & str, const D & obj ) {
+ str << "{" << obj._error << "(" << obj._root << ")" << obj._dbPath << "}";
+ return str;
+ }
+
+ D( const Pathname & root_r, const Pathname & dbPath_r, bool readonly_r )
+ : _root ( root_r )
+ , _dbPath( dbPath_r )
+ , _db ( 0 )
+ {
+ _error.reset();
+ // set %_dbpath macro
+ ::addMacro( NULL, "_dbpath", NULL, _dbPath.asString().c_str(), RMIL_CMDLINE );
+ const char * root = ( _root == "/" ? NULL : _root.asString().c_str() );
+ int perms = 0644;
+
+ // check whether to create a new db
+ PathInfo master( _root + _dbPath + "Packages" );
+ if ( ! master.isFile() ) {
+ // init database
+ int res = ::rpmdbInit( root, perms );
+ if ( res ) {
+ _error = shared_ptr<Exception>(new Exception("Error::E_RpmDB_init_failed"));
+ ERR << "rpmdbInit error(" << res << "): " << *this << endl;
+ return;
+ }
+ }
+
+ // open database
+ int res = ::rpmdbOpen( root, &_db, (readonly_r ? O_RDONLY : O_RDWR ), perms );
+ if ( res || !_db ) {
+ _error = shared_ptr<Exception>(new Exception("Error::E_RpmDB_open_failed"));
+ if ( _db ) {
+ ::rpmdbClose( _db );
+ _db = 0;
+ }
+ ERR << "rpmdbOpen error(" << res << "): " << *this << endl;
+ return;
+ }
+
+ DBG << "DBACCESS " << *this << endl;
+ }
+
+ ~D() {
+ if ( _db ) {
+#if 0
+ // login here may cause a SEGV, if call is caused by
+ // static variables being deleted. Might be that PMError
+ // static strings or logstreams are already destructed.
+ int res = ::rpmdbClose( _db );
+ if ( res ) {
+ WAR << "::rpmdbClose error(" << res << ")" << endl;
+ }
+ DBG << "DBCLOSE " << *this << endl;
+#else
+ ::rpmdbClose( _db );
+#endif
+ }
+ }
+};
+
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+// CLASS NAME : librpmDb (ststic interface)
+//
+///////////////////////////////////////////////////////////////////
+
+Pathname librpmDb::_defaultRoot ( "/" );
+Pathname librpmDb::_defaultDbPath( "/var/lib/rpm" );
+librpmDb::constPtr librpmDb::_defaultDb;
+bool librpmDb::_dbBlocked ( true );
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::globalInit
+// METHOD TYPE : bool
+//
+bool librpmDb::globalInit()
+{
+ static bool initialized = false;
+
+ if ( initialized )
+ return true;
+
+ int rc = ::rpmReadConfigFiles( NULL, NULL );
+ if ( rc ) {
+ ERR << "rpmReadConfigFiles returned " << rc << endl;
+ return false;
+ }
+
+ // should speed up convertdb and rebuilddb.
+ ::addMacro( NULL, "_rpmdb_rebuild", NULL, "%{nil}", RMIL_CMDLINE );
+
+ initialized = true; // Necessary to be able to use exand().
+
+#define OUTVAL(n) << " (" #n ":" << expand( "%{" #n "}" ) << ")"
+ MIL << "librpm init done:"
+ OUTVAL(_target)
+ OUTVAL(_dbpath)
+ << endl;
+#undef OUTVAL
+ return initialized;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::expand
+// METHOD TYPE : std::string
+//
+std::string librpmDb::expand( const std::string & macro_r )
+{
+ if ( ! globalInit() )
+ return macro_r; // unexpanded
+
+ char * val = ::rpmExpand( macro_r.c_str(), NULL );
+ if ( !val )
+ return "";
+
+ string ret( val );
+ free( val );
+ return ret;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::newLibrpmDb
+// METHOD TYPE : librpmDb *
+//
+librpmDb * librpmDb::newLibrpmDb( Pathname root_r, Pathname dbPath_r, bool readonly_r )
+{
+ // check arguments
+ if ( ! (root_r.absolute() && dbPath_r.absolute()) ) {
+ ERR << "Illegal root or dbPath: " << stringPath( root_r, dbPath_r ) << endl;
+ ZYPP_THROW(Exception("Error::E_invalid_argument"));
+ }
+
+ // initialize librpm
+ if ( ! globalInit() ) {
+ ZYPP_THROW(Exception("Error::E_RpmDB_global_init_failed"));
+ }
+
+ // open rpmdb
+ librpmDb * ret = new librpmDb( root_r, dbPath_r, readonly_r );
+ shared_ptr<Exception> err_r = ret->_d._error;
+ if ( err_r ) {
+ delete ret;
+ ret = 0;
+ ZYPP_THROW(ret->_d._error);
+ }
+ return ret;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::dbAccess
+// METHOD TYPE : PMError
+//
+void librpmDb::dbAccess( const Pathname & root_r, const Pathname & dbPath_r )
+{
+ // check arguments
+ if ( ! (root_r.absolute() && dbPath_r.absolute()) ) {
+ ERR << "Illegal root or dbPath: " << stringPath( root_r, dbPath_r ) << endl;
+ ZYPP_THROW(Exception("Error::E_invalid_argument"));
+ }
+
+ if ( _defaultDb ) {
+ // already accessing a database: switching is not allowed.
+ if ( _defaultRoot == root_r && _defaultDbPath == dbPath_r )
+ return;
+ else {
+ ERR << "Can't switch to " << stringPath( root_r, dbPath_r )
+ << " while accessing " << stringPath( _defaultRoot, _defaultDbPath ) << endl;
+ ZYPP_THROW(Exception("Error::E_RpmDB_already_open"));
+ }
+ }
+
+ // got no database: we could switch to a new one (even if blocked!)
+ _defaultRoot = root_r;
+ _defaultDbPath = dbPath_r;
+ MIL << "Set new database location: " << stringPath( _defaultRoot, _defaultDbPath ) << endl;
+
+ return dbAccess();
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::dbAccess
+// METHOD TYPE : PMError
+//
+void librpmDb::dbAccess()
+{
+ if ( _dbBlocked ) {
+ WAR << "Access is blocked: " << stringPath( _defaultRoot, _defaultDbPath ) << endl;
+ ZYPP_THROW(Exception("Error::E_RpmDB_access_blocked"));
+ }
+
+ if ( !_defaultDb ) {
+ // get access
+ _defaultDb = newLibrpmDb( _defaultRoot, _defaultDbPath, /*readonly*/true );
+ }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::dbAccess
+// METHOD TYPE : PMError
+//
+void librpmDb::dbAccess( librpmDb::constPtr & ptr_r )
+{
+ try {
+ dbAccess();
+ }
+ catch (Exception & excpt_r)
+ {
+ ptr_r = 0;
+ ZYPP_RETHROW(excpt_r);
+ }
+ ptr_r = _defaultDb;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::dbRelease
+// METHOD TYPE : unsigned
+//
+unsigned librpmDb::dbRelease( bool force_r )
+{
+ if ( !_defaultDb ) {
+ return 0;
+ }
+
+ unsigned outstanding = _defaultDb->refCount() - 1; // refCount can't be 0
+
+ switch ( outstanding ) {
+ default:
+ if ( !force_r ) {
+ DBG << "dbRelease: keep access, outstanding " << outstanding << endl;
+ break;
+ }
+ // else fall through:
+ case 0:
+ DBG << "dbRelease: release" << (force_r && outstanding ? "(forced)" : "")
+ << ", outstanding " << outstanding << endl;
+
+ _defaultDb->_d._error = shared_ptr<Exception>(new Exception("Error::E_RpmDB_access_blocked")); // tag handle invalid
+ _defaultDb = 0;
+ break;
+ }
+
+ return outstanding;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::blockAccess
+// METHOD TYPE : unsigned
+//
+unsigned librpmDb::blockAccess()
+{
+ MIL << "Block access" << endl;
+ _dbBlocked = true;
+ return dbRelease( /*force*/true );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::unblockAccess
+// METHOD TYPE : void
+//
+void librpmDb::unblockAccess()
+{
+ MIL << "Unblock access" << endl;
+ _dbBlocked = false;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::dumpState
+// METHOD TYPE : ostream &
+//
+ostream & librpmDb::dumpState( ostream & str )
+{
+ if ( !_defaultDb ) {
+ return str << "[librpmDb " << (_dbBlocked?"BLOCKED":"CLOSED") << " " << stringPath( _defaultRoot, _defaultDbPath ) << "]";
+ }
+ return str << "[" << _defaultDb << "]";
+}
+
+///////////////////////////////////////////////////////////////////
+//
+// CLASS NAME : librpmDb (internal database handle interface (nonstatic))
+//
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::librpmDb
+// METHOD TYPE : Constructor
+//
+// DESCRIPTION :
+//
+librpmDb::librpmDb( const Pathname & root_r, const Pathname & dbPath_r, bool readonly_r )
+ : _d( * new D( root_r, dbPath_r, readonly_r ) )
+{
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::~librpmDb
+// METHOD TYPE : Destructor
+//
+// DESCRIPTION :
+//
+librpmDb::~librpmDb()
+{
+ delete &_d;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::unref_to
+// METHOD TYPE : void
+//
+void librpmDb::unref_to( unsigned refCount_r ) const
+{
+ if ( refCount_r == 1 ) {
+ dbRelease();
+ }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::root
+// METHOD TYPE : const Pathname &
+//
+const Pathname & librpmDb::root() const
+{
+ return _d._root;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::dbPath
+// METHOD TYPE : const Pathname &
+//
+const Pathname & librpmDb::dbPath() const
+{
+ return _d._dbPath;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::error
+// METHOD TYPE : PMError
+//
+PMError librpmDb::error() const
+{
+ return _d._error;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::empty
+// METHOD TYPE : bool
+//
+bool librpmDb::empty() const
+{
+ return( valid() && ! *db_const_iterator( this ) );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::dont_call_it
+// METHOD TYPE : void *
+//
+void * librpmDb::dont_call_it() const
+{
+ return _d._db;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::dumpOn
+// METHOD TYPE : ostream &
+//
+// DESCRIPTION :
+//
+ostream & librpmDb::dumpOn( ostream & str ) const
+{
+ ReferenceCounted::dumpOn( str ) << _d;
+ return str;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+// CLASS NAME : librpmDb::DbDirInfo
+//
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::DbDirInfo::DbDirInfo
+// METHOD TYPE : Constructor
+//
+librpmDb::DbDirInfo::DbDirInfo( const Pathname & root_r, const Pathname & dbPath_r )
+ : _root( root_r )
+ , _dbPath( dbPath_r )
+{
+ // check and adjust arguments
+ if ( ! (root_r.absolute() && dbPath_r.absolute()) ) {
+ ERR << "Relative path for root(" << _root << ") or dbPath(" << _dbPath << ")" << endl;
+ } else {
+ _dbDir ( _root + _dbPath );
+ _dbV4 ( _dbDir.path() + "Packages" );
+ _dbV3 ( _dbDir.path() + "packages.rpm" );
+ _dbV3ToV4( _dbDir.path() + "packages.rpm3" );
+ DBG << *this << endl;
+ }
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::DbDirInfo::update
+// METHOD TYPE : void
+//
+void librpmDb::DbDirInfo::restat()
+{
+ _dbDir();
+ _dbV4();
+ _dbV3();
+ _dbV3ToV4();
+ DBG << *this << endl;
+}
+
+/******************************************************************
+**
+**
+** FUNCTION NAME : operator<<
+** FUNCTION TYPE : std::ostream &
+*/
+std::ostream & operator<<( std::ostream & str, const librpmDb::DbDirInfo & obj )
+{
+ if ( obj.illegalArgs() ) {
+ str << "ILLEGAL: '(" << obj.root() << ")" << obj.dbPath() << "'";
+ } else {
+ str << "'(" << obj.root() << ")" << obj.dbPath() << "':" << endl;
+ str << " Dir: " << obj._dbDir << endl;
+ str << " V4: " << obj._dbV4 << endl;
+ str << " V3: " << obj._dbV3 << endl;
+ str << " V3ToV4: " << obj._dbV3ToV4;
+ }
+ return str;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+// CLASS NAME : librpmDb::db_const_iterator::D
+/**
+ *
+ **/
+class librpmDb::db_const_iterator::D {
+ D & operator=( const D & ); // NO ASSIGNMENT!
+ D ( const D & ); // NO COPY!
+ public:
+
+ librpmDb::constPtr _dbptr;
+#warning use better type
+ string _dberr;
+
+ RpmHeader::constPtr _hptr;
+ rpmdbMatchIterator _mi;
+
+ D( librpmDb::constPtr dbptr_r )
+ : _dbptr( dbptr_r )
+ , _mi( 0 )
+ {
+ if ( !_dbptr ) {
+ // try to get librpmDb's default db
+ _dberr = librpmDb::dbAccess( _dbptr );
+ if ( !_dbptr ) {
+ WAR << "No database access: " << _dberr << endl;
+ }
+ } else {
+ destroy(); // Checks whether _dbptr still valid
+ }
+ }
+
+ ~D() {
+ if ( _mi ) {
+ ::rpmdbFreeIterator( _mi );
+ }
+ }
+
+ /**
+ * Let iterator access a dbindex file. Call @ref advance to access the
+ * 1st element (if present).
+ **/
+ bool create( int rpmtag, const void * keyp = NULL, size_t keylen = 0 ) {
+ destroy();
+ if ( ! _dbptr )
+ return false;
+ _mi = ::rpmdbInitIterator( _dbptr->_d._db, rpmTag(rpmtag), keyp, keylen );
+ return _mi;
+ }
+
+ /**
+ * Destroy iterator. Invalidates _dbptr, if database was blocked meanwile.
+ * Always returns false.
+ **/
+ bool destroy() {
+ if ( _mi ) {
+ _mi = ::rpmdbFreeIterator( _mi );
+ _hptr = 0;
+ }
+ if ( _dbptr && _dbptr->error() ) {
+ _dberr = _dbptr->error();
+ WAR << "Lost database access: " << _dberr << endl;
+ _dbptr = 0;
+ }
+ return false;
+ }
+
+ /**
+ * Advance to the first/next header in iterator. Destroys iterator if
+ * no more headers available.
+ **/
+ bool advance() {
+ if ( !_mi )
+ return false;
+ Header h = ::rpmdbNextIterator( _mi );
+ if ( ! h ) {
+ destroy();
+ return false;
+ }
+ _hptr = new RpmHeader( h );
+ return true;
+ }
+
+ /**
+ * Access a dbindex file and advance to the 1st header.
+ **/
+ bool init( int rpmtag, const void * keyp = NULL, size_t keylen = 0 ) {
+ if ( ! create( rpmtag, keyp, keylen ) )
+ return false;
+ return advance();
+ }
+
+ /**
+ * Create an itertator that contains the database entry located at
+ * off_r, and advance to the 1st header.
+ **/
+ bool set( int off_r ) {
+ if ( ! create( RPMDBI_PACKAGES ) )
+ return false;
+#warning TESTCASE: rpmdbAppendIterator and (non)sequential access?
+ ::rpmdbAppendIterator( _mi, &off_r, 1 );
+ return advance();
+ }
+
+ unsigned offset() {
+ return( _mi ? ::rpmdbGetIteratorOffset( _mi ) : 0 );
+ }
+
+ int size() {
+ if ( !_mi )
+ return 0;
+ int ret = ::rpmdbGetIteratorCount( _mi );
+#warning TESTCASE: rpmdbGetIteratorCount returns 0 on sequential access?
+ return( ret ? ret : -1 ); // -1: sequential access
+ }
+};
+
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+// CLASS NAME : librpmDb::Ptr::db_const_iterator
+//
+///////////////////////////////////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::db_const_iterator::db_iterator
+// METHOD TYPE : Constructor
+//
+librpmDb::db_const_iterator::db_const_iterator( librpmDb::constPtr dbptr_r )
+ : _d( * new D( dbptr_r ) )
+{
+ findAll();
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::db_const_iterator::~db_const_iterator
+// METHOD TYPE : Destructor
+//
+librpmDb::db_const_iterator::~db_const_iterator()
+{
+ delete &_d;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::db_const_iterator::operator++
+// METHOD TYPE : void
+//
+void librpmDb::db_const_iterator::operator++()
+{
+ _d.advance();
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::db_const_iterator::dbHdrNum
+// METHOD TYPE : unsigned
+//
+unsigned librpmDb::db_const_iterator::dbHdrNum() const
+{
+ return _d.offset();
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::db_const_iterator::operator*
+// METHOD TYPE : const RpmHeader::constPtr &
+//
+const RpmHeader::constPtr & librpmDb::db_const_iterator::operator*() const
+{
+ return _d._hptr;
+}
+
+#warning FIXME this function
+#if 0
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::db_const_iterator::dbError
+// METHOD TYPE : PMError
+//
+PMError librpmDb::db_const_iterator::dbError() const
+{
+ if ( _d._dbptr )
+ return _d._dbptr->error();
+
+ return _d._dberr;
+}
+#endif
+
+/******************************************************************
+**
+**
+** FUNCTION NAME : operator<<
+** FUNCTION TYPE : ostream &
+*/
+ostream & operator<<( ostream & str, const librpmDb::db_const_iterator & obj )
+{
+ str << "db_const_iterator(" << obj._d._dbptr
+ << " Size:" << obj._d.size()
+ << " HdrNum:" << obj._d.offset()
+ << ")";
+ return str;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::db_const_iterator::findAll
+// METHOD TYPE : bool
+//
+bool librpmDb::db_const_iterator::findAll()
+{
+ return _d.init( RPMDBI_PACKAGES );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::db_const_iterator::findByFile
+// METHOD TYPE : bool
+//
+bool librpmDb::db_const_iterator::findByFile( const std::string & file_r )
+{
+ return _d.init( RPMTAG_BASENAMES, file_r.c_str() );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::db_const_iterator::findByProvides
+// METHOD TYPE : bool
+//
+bool librpmDb::db_const_iterator::findByProvides( const std::string & tag_r )
+{
+ return _d.init( RPMTAG_PROVIDENAME, tag_r.c_str() );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::db_const_iterator::findByRequiredBy
+// METHOD TYPE : bool
+//
+bool librpmDb::db_const_iterator::findByRequiredBy( const std::string & tag_r )
+{
+ return _d.init( RPMTAG_REQUIRENAME, tag_r.c_str() );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::db_const_iterator::findByConflicts
+// METHOD TYPE : bool
+//
+bool librpmDb::db_const_iterator::findByConflicts( const std::string & tag_r )
+{
+ return _d.init( RPMTAG_CONFLICTNAME, tag_r.c_str() );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::findByName
+// METHOD TYPE : bool
+//
+bool librpmDb::db_const_iterator::findByName( const string & name_r )
+{
+ return _d.init( RPMTAG_NAME, name_r.c_str() );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::db_const_iterator::findPackage
+// METHOD TYPE : bool
+//
+bool librpmDb::db_const_iterator::findPackage( const string & name_r )
+{
+ if ( ! _d.init( RPMTAG_NAME, name_r.c_str() ) )
+ return false;
+
+ if ( _d.size() == 1 )
+ return true;
+
+ // check installtime on multiple entries
+ int match = 0;
+ time_t itime = 0;
+ for ( ; operator*(); operator++() ) {
+ if ( operator*()->tag_installtime() > itime ) {
+ match = _d.offset();
+ itime = operator*()->tag_installtime();
+ }
+ }
+
+ return _d.set( match );
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::db_const_iterator::findPackage
+// METHOD TYPE : bool
+//
+bool librpmDb::db_const_iterator::findPackage( const std::string & name_r, const Edition & ed_r )
+{
+ if ( ! _d.init( RPMTAG_NAME, name_r.c_str() ) )
+ return false;
+
+ for ( ; operator*(); operator++() ) {
+ if ( ed_r == operator*()->tag_edition() ) {
+ int match = _d.offset();
+ return _d.set( match );
+ }
+ }
+
+ return _d.destroy();
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//
+// METHOD NAME : librpmDb::db_const_iterator::findPackage
+// METHOD TYPE : bool
+//
+bool librpmDb::db_const_iterator::findPackage( const Package::constPtr & which_r )
+{
+ if ( ! which_r )
+ return _d.destroy();
+
+ return findPackage( which_r->name(), which_r->edition() );
+}
+
+ } // namespace rpm
+ } // namespace target
+} // namespace zypp
--- /dev/null
+/*---------------------------------------------------------------------\
+| |
+| __ __ ____ _____ ____ |
+| \ \ / /_ _/ ___|_ _|___ \ |
+| \ V / _` \___ \ | | __) | |
+| | | (_| |___) || | / __/ |
+| |_|\__,_|____/ |_| |_____| |
+| |
+| core system |
+| (C) SuSE GmbH |
+\----------------------------------------------------------------------/
+
+ File: librpmDb.h
+
+ Author: Michael Andres <ma@suse.de>
+ Maintainer: Michael Andres <ma@suse.de>
+
+ Purpose: Manage access to librpm database.
+
+/-*/
+#ifndef librpmDb_h
+#define librpmDb_h
+
+#include <iosfwd>
+
+#include "zypp/base/ReferenceCounted.h"
+#include "zypp/base/NonCopyable.h"
+#include "zypp/base/PtrTypes.h"
+#include "zypp/PathInfo.h"
+#include "zypp/Package.h"
+#include "zypp/target/rpm/RpmHeader.h"
+
+namespace zypp {
+ namespace target {
+ namespace rpm {
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ // CLASS NAME : librpmDb
+ /**
+ * @short Manage access to librpm database.
+ **/
+ class librpmDb : public base::ReferenceCounted, private base::NonCopyable
+ {
+ public:
+ typedef intrusive_ptr<librpmDb> Ptr;
+ typedef intrusive_ptr<const librpmDb> constPtr;
+ private:
+ /**
+ * <B>INTENTIONALLY UNDEFINED<\B> because of bug in Ptr classes
+ * which allows implicit conversion from librpmDb::Ptr to
+ * librpmDb::constPtr. Currently we don't want to provide non const
+ * handles, as the database is opened READONLY.
+ *
+ * \throws Exception
+ *
+ **/
+ static void dbAccess( librpmDb::Ptr & ptr_r );
+
+ public:
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ // static interface
+ //
+ ///////////////////////////////////////////////////////////////////
+ private:
+
+ /**
+ * Current root directory for all operations.
+ * (initialy /)
+ **/
+ static Pathname _defaultRoot;
+
+ /**
+ * Current directory (below root) that contains the rpmdb.
+ * (initialy /var/lib/rpm)
+ **/
+ static Pathname _defaultDbPath;
+
+ /**
+ * Current rpmdb handle.
+ **/
+ static librpmDb::constPtr _defaultDb;
+
+ /**
+ * Wheter access is blocked (no _defaultDb will be available).
+ **/
+ static bool _dbBlocked;
+
+ /**
+ * For internal use. Pointer returned should immediately be
+ * wrapped into librpmDb::Ptr.
+ *
+ * \throws Exception
+ *
+ **/
+ static librpmDb * newLibrpmDb( Pathname root_r, Pathname dbPath_r, bool readonly_r );
+
+ /**
+ * Access the database at the current default location. If necessary
+ * (eg. after @ref dbRelease), the database is opened. This just creates
+ * the internal handle. Once the handle is passed to e.g. some
+ * @ref db_const_iterator, the database will be closed if the last
+ * outstanding reference goes out of scope. If no external reference is
+ * created, you'll have to explicitly call @ref dbRelease to close the
+ * database.
+ *
+ * \throws Exception
+ *
+ **/
+ static void dbAccess();
+
+ public:
+
+ /**
+ * Initialize lib librpm (read configfiles etc.). It's called
+ * on demand but you may call it anytime.
+ *
+ * @return Whether librpm is initialized.
+ **/
+ static bool globalInit();
+
+ /**
+ * @return librpm macro expansion.
+ **/
+ static std::string expand( const std::string & macro_r );
+
+ /**
+ * @return String '(root_r)sub_r' used in debug output.
+ **/
+ static std::string stringPath( const Pathname & root_r, const Pathname & sub_r ) {
+ return std::string( "'(" ) + root_r.asString() + ")" + sub_r.asString() + "'";
+ }
+
+ public:
+
+ /**
+ * @return Current root directory for all operations.
+ **/
+ static const Pathname & defaultRoot() { return _defaultRoot; }
+
+ /**
+ * @return Current directory (below root) that contains the rpmdb.
+ **/
+ static const Pathname & defaultDbPath() { return _defaultDbPath; }
+
+ /**
+ * Adjust access to the given database location, making it the new
+ * default location on success. No relative Pathnames are allowed.
+ *
+ * It's not possible to access a database while access is blocked
+ * (see @ref blockAccess), but valid Pathnames provided will be stored
+ * as new default location.
+ *
+ * It's not allowed to switch to another location while a database
+ * is accessed. Use @ref dbRelease to force releasing the database first.
+ *
+ * \throws Exception
+ *
+ **/
+ static void dbAccess( const Pathname & root_r, const Pathname & dbPath_r );
+
+ /**
+ * Same as &ref dbAccess(), but returns the database handle if
+ * avaialble, otherwise NULL. This creates an external reference, thus
+ * it should not be used longer than necessary. Be prepared that the
+ * handle might become invalid (see @ref dbRelease) later.
+ *
+ * \throws Exception
+ *
+ **/
+ static void dbAccess( librpmDb::constPtr & ptr_r );
+
+ /**
+ * If there are no outstanding references to the database (e.g. by @ref db_const_iterator),
+ * the database is closed. Subsequent calls to @ref dbAccess may however
+ * open the database again.
+ *
+ * If forced, the internal reference is dropped and it will look like
+ * the database was closed. But physically the database will be closed
+ * after all outstanding references are gone.
+ *
+ * @return The number of outstandig references to the database, 0 if
+ * if database was physically closed.
+ **/
+ static unsigned dbRelease( bool force_r = false );
+
+ /**
+ * Blocks further access to rpmdb. Basically the same as @ref dbRelease( true ),
+ * but subsequent calls to @ref dbAccess will fail returning E_RpmDB_access_blocked.
+ *
+ * @return The number of outstandig references to the database, 0 if
+ * if database was physically closed.
+ **/
+ static unsigned blockAccess();
+
+ /**
+ * Allow access to rpmdb e.g. after @ref blockAccess. Subsequent calls to
+ * @ref dbAccess will perform.
+ *
+ * <B>NOTE:</B> Initially we're in blocked mode. So you must call @ref unblockAccess
+ * unblockAccess at least once. Othwise nothing will happen.
+ *
+ * @return The number of outstandig references to the database, 0 if
+ * if database was physically closed.
+ **/
+ static void unblockAccess();
+
+ /**
+ * @return Whether database access is blocked.
+ **/
+ static bool isBlocked() { return _dbBlocked; }
+
+ /**
+ * Dump debug info.
+ **/
+ static std::ostream & dumpState( std::ostream & str );
+
+ public:
+
+ /**
+ * Collect info about what kind of rpmdb seems to be present by
+ * looking at paths and filenames.
+ **/
+ class DbDirInfo;
+
+ /**
+ * Subclass to retrieve database content.
+ **/
+ class db_const_iterator;
+
+ private:
+ ///////////////////////////////////////////////////////////////////
+ //
+ // internal database handle interface (nonstatic)
+ //
+ ///////////////////////////////////////////////////////////////////
+
+ /**
+ * Hides librpm specific data
+ **/
+ class D;
+ D & _d;
+
+ protected:
+
+ /**
+ * Private constructor! librpmDb objects are to be created via
+ * static interface only.
+ **/
+ librpmDb( const Pathname & root_r, const Pathname & dbPath_r, bool readonly_r );
+
+ /**
+ * Trigger from @ref Rep, after refCount was decreased.
+ **/
+ virtual void unref_to( unsigned refCount_r ) const;
+
+ public:
+
+ /**
+ * Destructor. Closes rpmdb.
+ **/
+ virtual ~librpmDb();
+
+ /**
+ * @return This handles root directory for all operations.
+ **/
+ const Pathname & root() const;
+
+ /**
+ * @return This handles directory that contains the rpmdb.
+ **/
+ const Pathname & dbPath() const;
+#warning uncomment
+#if 0
+
+ /**
+ * Return any database error. Usg. if the database was
+ * blocked by calling @ref dbRelease(true) or @ref blockAccess.
+ **/
+ PMError error() const;
+
+ /**
+ * @return Whether
+ **/
+ bool valid() const { return( ! error() ); }
+#endif
+
+ /**
+ * @return True if handle is valid and database is empty.
+ **/
+ bool empty() const;
+
+ public:
+
+ /**
+ * Dont call it ;) It's for development and testing only.
+ **/
+ void * dont_call_it() const;
+
+ /**
+ * Dump debug info.
+ **/
+ virtual std::ostream & dumpOn( std::ostream & str ) const;
+ };
+
+ ///////////////////////////////////////////////////////////////////
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ // CLASS NAME : librpmDb::DbDirInfo
+ /**
+ * Collect info about what kind of rpmdb seems to be present by
+ * looking at paths and filenames.
+ **/
+ class librpmDb::DbDirInfo {
+ friend std::ostream & operator<<( std::ostream & str, const DbDirInfo & obj );
+
+ private:
+
+ /**
+ * Root directory for all operations.
+ **/
+ Pathname _root;
+
+ /**
+ * Directory that contains the rpmdb.
+ **/
+ Pathname _dbPath;
+
+ /**
+ * database directory (unset on illegal constructor arguments)
+ **/
+ PathInfo _dbDir;
+
+ /**
+ * rpmV4 database (_dbDir/Packages)
+ **/
+ PathInfo _dbV4;
+
+ /**
+ * rpmV3 database (_dbDir/packages.rpm)
+ **/
+ PathInfo _dbV3;
+
+ /**
+ * rpmV3 database backup created on conversion to rpmV4 (_dbDir/packages.rpm3)
+ **/
+ PathInfo _dbV3ToV4;
+
+ public:
+
+ /**
+ * For Constructor arguments see @ref accessPath. On illegal
+ * arguments @ref _dbDir is unset.
+ **/
+ DbDirInfo( const Pathname & root_r, const Pathname & dbPath_r );
+
+ public:
+
+ /**
+ * Root directory for all operations.
+ **/
+ const Pathname & root() const { return _root; }
+
+ /**
+ * Directory that contains the rpmdb.
+ **/
+ const Pathname & dbPath() const { return _dbPath; }
+
+ /**
+ * database directory (unset on illegal constructor arguments)
+ **/
+ const PathInfo & dbDir() const { return _dbDir; }
+
+ /**
+ * rpmV4 database (_dbDir/Packages)
+ **/
+ const PathInfo & dbV4() const { return _dbV4; }
+
+ /**
+ * rpmV3 database (_dbDir/packages.rpm)
+ **/
+ const PathInfo & dbV3() const { return _dbV3; }
+
+ /**
+ * rpmV3 database backup created on conversion to rpmV4 (_dbDir/packages.rpm3)
+ **/
+ const PathInfo & dbV3ToV4() const { return _dbV3ToV4; }
+
+ public:
+
+ /**
+ * Restat all paths
+ **/
+ void restat();
+
+ public:
+
+ /**
+ * Whether constructor arguments were illegal.
+ **/
+ bool illegalArgs() const { return _dbDir.path().empty(); }
+
+ /**
+ * Whether constructor arguments were llegal and dbDir either
+ * is a directory or may be created (path does not exist).
+ **/
+ bool usableArgs() const { return _dbDir.isDir() || ! ( _dbDir.path().empty() || _dbDir.isExist() ); }
+
+ /**
+ * Whether dbDir directory exists.
+ **/
+ bool hasDbDir() const { return _dbDir.isDir(); }
+
+ /**
+ * Whether dbV4 file exists.
+ **/
+ bool hasDbV4() const { return _dbV4.isFile(); }
+
+ /**
+ * Whether dbV3 file exists.
+ **/
+ bool hasDbV3() const { return _dbV3.isFile(); }
+
+ /**
+ * Whether dbV3ToV4 file exists.
+ **/
+ bool hasDbV3ToV4() const { return _dbV3ToV4.isFile(); }
+ };
+
+ ///////////////////////////////////////////////////////////////////
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ // CLASS NAME : librpmDb::db_const_iterator
+ /**
+ * @short Subclass to retrieve database content.
+ *
+ *
+ **/
+ class librpmDb::db_const_iterator {
+ db_const_iterator & operator=( const db_const_iterator & ); // NO ASSIGNMENT!
+ db_const_iterator ( const db_const_iterator & ); // NO COPY!
+ friend std::ostream & operator<<( std::ostream & str, const db_const_iterator & obj );
+ friend class librpmDb;
+
+ private:
+
+ /**
+ * Hides librpm specific data
+ **/
+ class D;
+ D & _d;
+
+ public:
+
+ /**
+ * Constructor. Iterator is initialized to @ref findAll.
+ * The default form accesses librpmDb's default database.
+ * Explicitly providing a database handle should not be
+ * neccesary, except for testing.
+ **/
+ db_const_iterator( librpmDb::constPtr dbptr_r = 0 );
+
+ /**
+ * Destructor.
+ **/
+ ~db_const_iterator();
+
+#warning FIXME this function
+#if 0
+ /**
+ * Return any database error.
+ *
+ * <B>NOTE:</B> If the database gets blocked (see @ref dbRelease)
+ * dbError will immediately report this, but an already running
+ * iteration will proceed to its end. Then the database is dropped.
+ **/
+ PMError dbError() const;
+#endif
+
+ /**
+ * Advance to next RpmHeader::constPtr.
+ **/
+ void operator++();
+
+ /**
+ * Returns the current headers index in database,
+ * 0 if no header.
+ **/
+ unsigned dbHdrNum() const;
+
+ /**
+ * Returns the current RpmHeader::constPtr or
+ * NULL, if no more entries available.
+ **/
+ const RpmHeader::constPtr & operator*() const;
+
+ /**
+ * Forwards to the current RpmHeader::constPtr.
+ **/
+ const RpmHeader::constPtr & operator->() const {
+ return operator*();
+ }
+
+ public:
+
+ /**
+ * Reset to iterate all packages. Returns true if iterator
+ * contains at least one entry.
+ *
+ * <B>NOTE:</B> No entry (false) migt be returned due to a
+ * meanwhile blocked database (see @ref dbRelease). Use
+ * @ref dbError to check this.
+ **/
+ bool findAll();
+
+ /**
+ * Reset to iterate all packages that own a certain file.
+ **/
+ bool findByFile( const std::string & file_r );
+
+ /**
+ * Reset to iterate all packages that provide a certain tag.
+ **/
+ bool findByProvides( const std::string & tag_r );
+
+ /**
+ * Reset to iterate all packages that require a certain tag.
+ **/
+ bool findByRequiredBy( const std::string & tag_r );
+
+ /**
+ * Reset to iterate all packages that conflict with a certain tag.
+ **/
+ bool findByConflicts( const std::string & tag_r );
+
+ /**
+ * Reset to iterate all packages with a certain name.
+ *
+ * <B>NOTE:</B> Multiple entries for one package installed
+ * in different versions are possible but not desired. Usually
+ * you'll want to use @ref findPackage instead.
+ *
+ * findByName is needed to retrieve pseudo packages like
+ * 'gpg-pubkey', which in fact exist in multiple instances.
+ **/
+ bool findByName( const std::string & name_r );
+
+ public:
+
+ /**
+ * Find package by name.
+ *
+ * Multiple entries for one package installed in different versions
+ * are possible but not desired. If so, the last package installed
+ * is returned.
+ **/
+ bool findPackage( const std::string & name_r );
+
+ /**
+ * Find package by name and edition.
+ * Commonly used by PMRpmPackageDataProvider.
+ **/
+ bool findPackage( const std::string & name_r, const Edition & ed_r );
+#warning define if eneded
+#if 0
+ /**
+ * Abbr. for <code>findPackage( which_r.name, which_r.edition );</code>
+ **/
+ bool findPackage( const PkgNameEd & which_r ) {
+ return findPackage( which_r.name, which_r.edition );
+ }
+#endif
+ /**
+ * Abbr. for <code>findPackage( which_r->name(), which_r->edition() );</code>
+ **/
+ bool findPackage( const Package::constPtr & which_r );
+ };
+
+///////////////////////////////////////////////////////////////////
+ } //namespace rpm
+ } //namespace target
+} // namespace zypp
+
+#endif // librpmDb_h
+