#include "zypp/base/String.h"
#include "zypp/base/Gettext.h"
#include "zypp/base/LocaleGuard.h"
+#include "zypp/base/DtorReset.h"
#include "zypp/Date.h"
#include "zypp/Pathname.h"
#include "zypp/PathInfo.h"
#include "zypp/PublicKey.h"
+#include "zypp/ProgressData.h"
#include "zypp/target/rpm/RpmDb.h"
#include "zypp/target/rpm/RpmCallbacks.h"
return librpmDb::stringPath( root_r, sub_r );
}
-/******************************************************************
- **
- **
- ** FUNCTION NAME : operator<<
- ** FUNCTION TYPE : std::ostream &
-*/
-std::ostream & operator<<( std::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 : RpmDb
// METHOD TYPE : Constructor
//
RpmDb::RpmDb()
- : _dbStateInfo( DbSI_NO_INIT )
-#warning Check for obsolete memebers
- , _backuppath ("/var/adm/backup")
+ : _backuppath ("/var/adm/backup")
, _packagebackups(false)
- , _warndirexists(false)
{
process = 0;
exit_code = -1;
sKeyRingReceiver.reset();
}
-Date RpmDb::timestamp() const
-{
- Date ts_rpm;
-
- Pathname db_path;
- if ( dbPath().empty() )
- db_path = "/var/lib/rpm";
- else
- db_path = dbPath();
-
- PathInfo rpmdb_info(root() + db_path + "/Packages");
-
- if ( rpmdb_info.isExist() )
- return rpmdb_info.mtime();
- else
- return Date::now();
-}
///////////////////////////////////////////////////////////////////
//
//
//
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 << "]";
+ return str << "RpmDb[" << stringPath( _root, _dbPath ) << "]";
}
///////////////////////////////////////////////////////////////////
// METHOD NAME : RpmDb::initDatabase
// METHOD TYPE : PMError
//
-void RpmDb::initDatabase( Pathname root_r, Pathname dbPath_r, bool doRebuild_r )
+void RpmDb::initDatabase( Pathname root_r, bool doRebuild_r )
{
///////////////////////////////////////////////////////////////////
// Check arguments
if ( root_r.empty() )
root_r = "/";
- if ( dbPath_r.empty() )
- dbPath_r = "/var/lib/rpm";
+ // NOTE: Former argument, but now locked to "/var/lib/rpm".
+ // A custom dbPath is not actually needed and would only work
+ // reliably if libsolv also supports it. By now no further
+ // cleanup in the code.
+ const Pathname & dbPath_r { librpmDb::defaultDbPath() };
- if ( ! (root_r.absolute() && dbPath_r.absolute()) )
+ if ( ! root_r.absolute() )
{
ERR << "Illegal root or dbPath: " << stringPath( root_r, dbPath_r ) << endl;
ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
}
- if ( dbPath_r == "/var/lib/rpm"
- && ! PathInfo( root_r/"/var/lib/rpm" ).isExist()
+ if ( ! PathInfo( root_r/"/var/lib/rpm" ).isExist()
&& PathInfo( root_r/"/usr/lib/sysimage/rpm" ).isDir() )
{
WAR << "Rpm package was deleted? Injecting missing rpmdb compat symlink." << endl;
return;
}
- DbStateInfoBits info = DbSI_NO_INIT;
try
{
- internal_initDatabase( root_r, dbPath_r, info );
+ // creates dbdir and empty rpm database if not present
+ librpmDb::dbAccess( root_r );
}
catch (const RpmException & excpt_r)
{
ZYPP_CAUGHT(excpt_r);
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 ) );
- }
ZYPP_RETHROW(excpt_r);
}
- 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 ( doRebuild_r )
- {
- if ( dbsi_has( info, DbSI_HAVE_V4 )
- && ! dbsi_has( info, DbSI_MADE_V4 ) )
- {
- rebuildDatabase();
- }
- }
+ rebuildDatabase();
MIL << "Synchronizing keys with zypp keyring" << endl;
syncTrustedKeys();
///////////////////////////////////////////////////////////////////
//
//
-// METHOD NAME : RpmDb::internal_initDatabase
-// METHOD TYPE : PMError
-//
-void 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() )
- {
- // should not happen (checked in initDatabase)
- ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
- }
- if ( ! dbInfo.usableArgs() )
- {
- ERR << "Bad database directory: " << dbInfo.dbDir() << endl;
- ZYPP_THROW(RpmInvalidRootException(root_r, dbPath_r));
- }
-
- 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
- librpmDb::dbAccess( root_r, dbPath_r );
-
- if ( ! dbInfo.hasDbV4() )
- {
- dbInfo.restat();
- if ( dbInfo.hasDbV4() )
- {
- dbsi_set( info_r, DbSI_HAVE_V4 | DbSI_MADE_V4 );
- }
- }
-
- 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
- ///////////////////////////////////////////////////////////////////
- librpmDb::constPtr 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 void convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r );
- convertV3toV4( dbInfo.dbV3().path(), dbptr );
-
- // create a backup copy
- int res = filesystem::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;
- // 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;
- }
-}
-
-///////////////////////////////////////////////////////////////////
-//
-//
-// 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() )
- {
- filesystem::unlink( pi.path() );
- }
- }
-
- pi( dbdir_r + master );
- if ( pi.isFile() )
- {
- MIL << "Removing rpm4 database " << pi << endl;
- filesystem::unlink( pi.path() );
- }
-
- if ( v3backup_r )
- {
- pi( dbdir_r + v3backup );
- if ( pi.isFile() )
- {
- MIL << "Removing converted rpm3 database backup " << pi << endl;
- filesystem::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() )
- {
- filesystem::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
- filesystem::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
- filesystem::unlink( b );
- }
- filesystem::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 );
- }
-}
-
-///////////////////////////////////////////////////////////////////
-//
-//
// METHOD NAME : RpmDb::closeDatabase
// METHOD TYPE : PMError
//
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;
}
void RpmDb::doRebuildDatabase(callback::SendReport<RebuildDBReport> & report)
{
FAILIFNOTINITIALIZED;
-
MIL << "RpmDb::rebuildDatabase" << *this << endl;
- // FIXME Timecount _t( "RpmDb::rebuildDatabase" );
- PathInfo dbMaster( root() + dbPath() + "Packages" );
- PathInfo dbMasterBackup( dbMaster.path().extend( ".y2backup" ) );
-
- // 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.
- run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
+ const Pathname mydbpath { root()/dbPath() }; // the configured path used in reports
+ {
+ // For --rebuilddb take care we're using the real db directory
+ // and not a symlink. Otherwise rpm will rename the symlink and
+ // replace it with a real directory containing the converted db.
+ DtorReset guardRoot { _root };
+ DtorReset guardDbPath{ _dbPath };
+ _root = "/";
+ _dbPath = filesystem::expandlink( mydbpath );
- // progress report: watch this file growing
- PathInfo newMaster( root()
- + dbPath().extend( str::form( "rebuilddb.%d",
- process?process->getpid():0) )
- + "Packages" );
+ // run rpm
+ RpmArgVec opts;
+ opts.push_back("--rebuilddb");
+ opts.push_back("-vv");
+ run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
+ }
- std::string line;
- std::string errmsg;
+ // generate and report progress
+ ProgressData tics;
+ {
+ ProgressData::value_type hdrTotal = 0;
+ for ( librpmDb::db_const_iterator it; *it; ++it, ++hdrTotal )
+ {;}
+ tics.range( hdrTotal );
+ }
+ tics.sendTo( [&report,&mydbpath]( const ProgressData & tics_r ) -> bool {
+ return report->progress( tics_r.reportValue(), mydbpath );
+ } );
+ tics.toMin();
+ std::string line;
+ std::string errmsg;
while ( systemReadLine( line ) )
{
- if ( newMaster() )
- { // file is removed at the end of rebuild.
- // current size should be upper limit for new db
- if ( ! report->progress( (100 * newMaster.size()) / dbMaster.size(), root() + dbPath()) )
+ static const std::string debugPrefix { "D:" };
+ static const std::string progressPrefix { "D: read h#" };
+ static const std::string ignoreSuffix { "digest: OK" };
+
+ if ( ! str::startsWith( line, debugPrefix ) )
+ {
+ if ( ! str::endsWith( line, ignoreSuffix ) )
{
- WAR << "User requested abort." << endl;
- systemKill();
- filesystem::recursive_rmdir( newMaster.path().dirname() );
+ errmsg += line;
+ errmsg += '\n';
+ WAR << line << endl;
}
}
-
- if ( line.compare( 0, 2, "D:" ) )
+ else if ( str::startsWith( line, progressPrefix ) )
{
- errmsg += line + '\n';
- // report.notify( line );
- WAR << line << endl;
+ if ( ! tics.incr() )
+ {
+ WAR << "User requested abort." << endl;
+ systemKill();
+ }
}
}
- int rpm_status = systemStatus();
-
- if ( rpm_status != 0 )
+ if ( systemStatus() != 0 )
{
//TranslatorExplanation after semicolon is error message
ZYPP_THROW(RpmSubprocessException(std::string(_("RPM failed: ")) + (errmsg.empty() ? error_message: errmsg) ) );
}
else
{
- report->progress( 100, root() + dbPath() ); // 100%
+ tics.toMax();
}
}
opts.push_back ( "--allmatches" );
opts.push_back ( "--" );
opts.push_back ( keyName.c_str() );
- // don't call modifyDatabase because it would remove the old
- // rpm3 database, if the current database is a temporary one.
run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
std::string line;
opts.push_back ( "--" );
std::string pubkeypath( pubkey_r.path().asString() );
opts.push_back ( pubkeypath.c_str() );
-
- // don't call modifyDatabase because it would remove the old
- // rpm3 database, if the current database is a temporary one.
run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
std::string line;
opts.push_back ( "-e" );
opts.push_back ( "--" );
opts.push_back ( rpm_name.c_str() );
-
- // don't call modifyDatabase because it would remove the old
- // rpm3 database, if the current database is a temporary one.
run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
std::string line;
// rpm requires additional quoting of special chars:
std::string quotedFilename( rpmQuoteFilename( workaroundRpmPwdBug( filename ) ) );
opts.push_back ( quotedFilename.c_str() );
-
- modifyDatabase(); // BEFORE run_rpm
run_rpm( opts, ExternalProgram::Stderr_To_Stdout );
std::string line;
opts.push_back("--");
opts.push_back(name_r.c_str());
-
- modifyDatabase(); // BEFORE run_rpm
run_rpm (opts, ExternalProgram::Stderr_To_Stdout);
std::string line;