#include <fstream>
#include "zypp/base/Easy.h"
-#include "zypp/base/Logger.h"
+#include "zypp/base/LogTools.h"
+#include "zypp/base/DtorReset.h"
#include "zypp/base/String.h"
#include "zypp/DiskUsageCounter.h"
-#include "zypp/Package.h"
+#include "zypp/ExternalProgram.h"
+#include "zypp/sat/Pool.h"
+#include "zypp/sat/detail/PoolImpl.h"
using std::endl;
namespace
{ /////////////////////////////////////////////////////////////////
- inline void addDu( DiskUsageCounter::MountPointSet & result_r, DiskUsage & du_r )
+ DiskUsageCounter::MountPointSet calcDiskUsage( DiskUsageCounter::MountPointSet result, const Bitmap & installedmap_r )
{
- // traverse mountpoints in reverse order. This is done beacuse
- // DiskUsage::extract computes the mountpoint size, and then
- // removes the entry. So we must process leaves first.
- for_( mpit, result_r.rbegin(), result_r.rend() )
+ if ( result.empty() )
{
- // Extract usage for the mount point
- DiskUsage::Entry entry = du_r.extract( mpit->dir );
- // Adjust the data.
- mpit->pkg_size += entry._size;
+ // partitioning is not set
+ return result;
}
- }
- inline void delDu( DiskUsageCounter::MountPointSet & result_r, DiskUsage & du_r )
- {
- // traverse mountpoints in reverse order. This is done beacuse
- // DiskUsage::extract computes the mountpoint size, and then
- // removes the entry. So we must process leaves first.
- for_( mpit, result_r.rbegin(), result_r.rend() )
+ sat::Pool satpool( sat::Pool::instance() );
+
+ // init libsolv result vector with mountpoints
+ static const ::DUChanges _initdu = { 0, 0, 0, 0 };
+ std::vector< ::DUChanges> duchanges( result.size(), _initdu );
+ {
+ unsigned idx = 0;
+ for_( it, result.begin(), result.end() )
+ {
+ duchanges[idx].path = it->dir.c_str();
+ if ( it->growonly )
+ duchanges[idx].flags |= DUCHANGES_ONLYADD;
+ ++idx;
+ }
+ }
+ // now calc...
+ ::pool_calc_duchanges( satpool.get(),
+ const_cast<Bitmap &>(installedmap_r),
+ &duchanges[0],
+ duchanges.size() );
+
+ // and process the result
{
- // Extract usage for the mount point
- DiskUsage::Entry entry = du_r.extract( mpit->dir );
- // Adjust the data.
- mpit->pkg_size -= entry._size;
+ unsigned idx = 0;
+ for_( it, result.begin(), result.end() )
+ {
+ static const ByteCount blockAdjust( 2, ByteCount::K ); // (files * blocksize) / (2 * 1K)
+ it->pkg_size = it->used_size // current usage
+ + duchanges[idx].kbytes // package data size
+ + ( duchanges[idx].files * it->block_size / blockAdjust ); // half block per file
+ ++idx;
+ }
}
+
+ return result;
}
/////////////////////////////////////////////////////////////////
} // namespace
///////////////////////////////////////////////////////////////////
- DiskUsageCounter::MountPointSet DiskUsageCounter::disk_usage( const ResPool & pool_r )
+ DiskUsageCounter::MountPointSet DiskUsageCounter::disk_usage( const ResPool & pool_r ) const
{
- DiskUsageCounter::MountPointSet result = mps;
-
- if ( result.empty() )
- {
- // partitioning is not set
- return result;
- }
+ Bitmap bitmap( Bitmap::poolSize );
- // set used size after commit to the current used size
- for_( it, result.begin(), result.end() )
+ // build installedmap (installed != transact)
+ // stays installed or gets installed
+ for_( it, pool_r.begin(), pool_r.end() )
{
- it->pkg_size = it->used_size;
+ if ( it->status().isInstalled() != it->status().transacts() )
+ {
+ bitmap.set( sat::asSolvable()(*it).id() );
+ }
}
+ return calcDiskUsage( _mps, bitmap );
+ }
- // iterate through all items
- for_( it, pool_r.begin(), pool_r.end() )
- {
- // skip items that do not transact
- if ( ! it->status().transacts() )
- continue;
+ DiskUsageCounter::MountPointSet DiskUsageCounter::disk_usage( sat::Solvable solv_r ) const
+ {
+ Bitmap bitmap( Bitmap::poolSize );
+ bitmap.set( solv_r.id() );
- DiskUsage du( (*it)->diskusage() );
+ // temp. unset @system Repo
+ DtorReset tmp( sat::Pool::instance().get()->installed );
+ sat::Pool::instance().get()->installed = nullptr;
- // skip items without du info
- if ( du.empty() )
- continue; // or find some substitute info
+ return calcDiskUsage( _mps, bitmap );
+ }
- // Adjust the data.
- if ( it->status().isUninstalled() )
- {
- // an uninstalled item gets installed:
- addDu( result, du );
+ DiskUsageCounter::MountPointSet DiskUsageCounter::disk_usage( const Bitmap & bitmap_r ) const
+ {
+ // temp. unset @system Repo
+ DtorReset tmp( sat::Pool::instance().get()->installed );
+ sat::Pool::instance().get()->installed = nullptr;
- // While there is no valid solver result, items to update
- // are selected, but installed old versions are not yet
- // deselected. We try to compensate this:
- if ( ! (*it)->installOnly() )
- {
- // Item to update -> check the installed ones.
- for_( nit, pool_r.byIdentBegin( *it ), pool_r.byIdentEnd( *it ) )
- { // same kind and name
- if ( nit->status().staysInstalled() ) // and unselected installed
- {
- DiskUsage ndu( (*nit)->diskusage() );
- if ( ! ndu.empty() )
- {
- delDu( result, ndu );
- }
- }
- }
- }
- }
- else
- {
- // an installed item gets deleted:
- delDu( result, du );
- }
- }
- return result;
+ return calcDiskUsage( _mps, bitmap_r );
}
- DiskUsageCounter::MountPointSet DiskUsageCounter::detectMountPoints(const std::string &rootdir)
+ DiskUsageCounter::MountPointSet DiskUsageCounter::detectMountPoints( const std::string & rootdir )
{
DiskUsageCounter::MountPointSet ret;
if ( !(procmounts.fail() || procmounts.bad()) ) {
// data to consume
- // rootfs / rootfs rw 0 0
+ // rootfs / rootfs rw 0 0
// /dev/root / reiserfs rw 0 0
- // proc /proc proc rw 0 0
- // devpts /dev/pts devpts rw 0 0
+ // proc /proc proc rw 0 0
+ // devpts /dev/pts devpts rw 0 0
// /dev/hda5 /boot ext2 rw 0 0
- // shmfs /dev/shm shm rw 0 0
+ // shmfs /dev/shm shm rw 0 0
// usbdevfs /proc/bus/usb usbdevfs rw 0 0
std::vector<std::string> words;
continue;
}
+ // remove /proc entry
+ if (words[0] == "/proc")
+ {
+ DBG << "Discard /proc filesystem: " << l << std::endl;
+ continue;
+ }
+
//
// Filter mountpoints not at or below _rootdir
//
continue;
}
+ if ( words[2] == "vfat" || words[2] == "fat" || words[2] == "ntfs" || words[2] == "ntfs-3g")
+ {
+ MIL << words[1] << " contains ignored fs (" << words[2] << ')' << std::endl;
+ continue;
+ }
+
//
// Filter some common unwanted mountpoints
//
//
// Check whether mounted readonly
//
- bool ro = false;
+ MountPoint::HintFlags hints;
+
std::vector<std::string> flags;
str::split( words[3], std::back_inserter(flags), "," );
for ( unsigned i = 0; i < flags.size(); ++i ) {
if ( flags[i] == "ro" ) {
- ro = true;
+ hints |= MountPoint::Hint_readonly;
break;
}
}
- if ( ro ) {
+ if ( hints.testFlag( MountPoint::Hint_readonly ) ) {
DBG << "Filter ro mount point : " << l << std::endl;
continue;
}
//
+ // check for snapshotting btrfs
+ //
+ if ( words[2] == "btrfs" )
+ {
+ if ( geteuid() != 0 )
+ {
+ DBG << "Assume snapshots on " << words[1] << ": non-root user can't check" << std::endl;
+ hints |= MountPoint::Hint_growonly;
+ }
+ else
+ {
+ // For now just check whether there is
+ // at least one snapshot on the volume:
+ ExternalProgram prog({"btrfs","subvolume","list","-s",words[1]});
+ std::string line( prog.receiveLine() );
+ if ( ! line.empty() )
+ {
+ DBG << "Found a snapshot on " << words[1] << ": " << line; // has trailing std::endl
+ hints |= MountPoint::Hint_growonly;
+ }
+ prog.kill();
+ }
+ }
+
+ //
// statvfs (full path!) and get the data
//
struct statvfs sb;
if ( statvfs( words[1].c_str(), &sb ) != 0 ) {
WAR << "Unable to statvfs(" << words[1] << "); errno " << errno << std::endl;
- ret.insert( DiskUsageCounter::MountPoint( mp ) );
+ ret.insert( DiskUsageCounter::MountPoint( mp, words[2], 0LL, 0LL, 0LL, 0LL, hints ) );
}
else
{
- ret.insert( DiskUsageCounter::MountPoint( mp, sb.f_bsize,
+ //
+ // Filter zero sized devices (bnc#769819)
+ //
+ if ( sb.f_blocks == 0 || sb.f_bsize == 0 )
+ {
+ DBG << "Filter zero-sized mount point : " << l << std::endl;
+ continue;
+ }
+ ret.insert( DiskUsageCounter::MountPoint( mp, words[2], sb.f_bsize,
((long long)sb.f_blocks)*sb.f_bsize/1024,
- ((long long)(sb.f_blocks - sb.f_bfree))*sb.f_bsize/1024, 0LL, ro ) );
+ ((long long)(sb.f_blocks - sb.f_bfree))*sb.f_bsize/1024, 0LL, hints ) );
}
}
}
return ret;
}
+ DiskUsageCounter::MountPointSet DiskUsageCounter::justRootPartition()
+ {
+ DiskUsageCounter::MountPointSet ret;
+ ret.insert( DiskUsageCounter::MountPoint() );
+ return ret;
+ }
+
std::ostream & operator<<( std::ostream & str, const DiskUsageCounter::MountPoint & obj )
{
- str << "dir:[" << obj.dir << "] [ bs: " << obj.block_size
- << " ts: " << obj.total_size
- << " us: " << obj.used_size
- << " (+-: " << (obj.pkg_size-obj.used_size)
- << ")]" << std::endl;
+ str << "dir:[" << obj.dir << "] [ bs: " << obj.blockSize()
+ << " ts: " << obj.totalSize()
+ << " us: " << obj.usedSize()
+ << " (+-: " << obj.commitDiff()
+ << ")" << (obj.readonly?"r":"") << (obj.growonly?"g":"") << " " << obj.fstype << "]";
return str;
}
+ std::ostream & operator<<( std::ostream & str, const DiskUsageCounter::MountPointSet & obj )
+ { return dumpRange( str, obj.begin(), obj.end() ); }
+
+ /////////////////////////////////////////////////////////////////
} // namespace zypp
///////////////////////////////////////////////////////////////////