#include <iostream>
#include <fstream>
-#include "zypp/base/Logger.h"
+#include "zypp/base/Easy.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 zypp
{ /////////////////////////////////////////////////////////////////
- DiskUsageCounter::MountPointSet DiskUsageCounter::disk_usage(const ResPool &pool)
- {
- MountPointSet result = mps;
+ ///////////////////////////////////////////////////////////////////
+ namespace
+ { /////////////////////////////////////////////////////////////////
- if (mps.empty())
+ DiskUsageCounter::MountPointSet calcDiskUsage( DiskUsageCounter::MountPointSet result, const Bitmap & installedmap_r )
{
- // partitioning is not set
- return result;
- }
+ if ( result.empty() )
+ {
+ // partitioning is not set
+ return result;
+ }
- // set used size after commit to the current used size
- for (MountPointSet::iterator mpit = result.begin();
- mpit != result.end();
- mpit++)
- {
- mpit->pkg_size = mpit->used_size;
- }
+ sat::Pool satpool( sat::Pool::instance() );
- // iterate through all packages
- for (ResPool::byKind_iterator it = pool.byKindBegin(ResTraits<Package>::kind);
- it != pool.byKindEnd(ResTraits<Package>::kind);
- ++it)
- {
- bool inst = it->status().isToBeInstalled();
- bool rem = it->status().isToBeUninstalled();
+ // 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() );
- // if the package is not selected for installation or removing
- // it can't affect disk usage
- if (inst || rem)
+ // and process the result
{
- Package::constPtr pkg = asKind<Package>( it->resolvable() );
- DiskUsage du = pkg->diskusage();
- DiskUsage du_another_package;
- Edition edition_another_package;
-
- // the same package has been selected for installation
- bool found_installed = false;
- // the same package has been selected for uninstallation
- bool found_to_install = false;
-
- // the du is empty or the package is selected for installation (check whether the package is already installed)
- if (du.size() == 0 || inst)
- {
- // disk usage is unknown for already installed packages
- // find the same package from any installation source
- std::string name = (*it)->name();
-
- for (ResPool::byName_iterator nameit = pool.byNameBegin(name);
- nameit != pool.byNameEnd(name);
- ++nameit)
- {
- // is version and architecture same?
- if (isKind<Package>(nameit->resolvable()))
- {
- // found a package
- Package::constPtr pkg_from_source = asKind<Package>( nameit->resolvable() );
-
- if (nameit->status().isToBeInstalled())
- {
- found_to_install = true;
- }
-
- // check the version
- if ((*it)->edition() == (*nameit)->edition() && (*it)->arch() == (*nameit)->arch())
- {
- if (inst)
- {
- if (nameit->status().isInstalled() && !nameit->status().isToBeUninstalled())
- {
- found_installed = true;
- XXX << name << '-' << (*it)->edition() << ": found already installed package (" << (*nameit)->edition() << ")" << std::endl;
- }
- }
- else
- {
- // the package will be uninstalled and du is empty, try to use du from another object
- du = pkg_from_source->diskusage();
- if (du.size() > 0)
- {
- XXX << name << '-' << (*it)->edition() << ": using DiskUsage from another Package object (" << (*nameit)->edition() << ")" << std::endl;
- break;
- }
- }
- }
- else
- {
- if (inst && nameit->status().isInstalled() && !nameit->status().isToBeUninstalled())
- {
- // just freshen the package, don't change du statistics
- found_installed = true;
- XXX << name << '-' << (*it)->edition() << ": found already installed package (" << (*nameit)->edition() << ")" << std::endl;
- }
- else if (pkg_from_source->diskusage().size() > 0)
- {
- // found different version of the package, remember the disk usage
- // it will be used the same version is not found
- du_another_package = pkg_from_source->diskusage();
- edition_another_package = (*nameit)->edition();
- }
- }
- }
- }
+ 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;
+ }
+ }
- // don't subtract the disk usage for updated package
- if (du.size() == 0 && du_another_package.size() > 0 && !(rem && found_to_install))
- {
- XXX << name << '-' << (*it)->edition() << ": using DU info from version " << edition_another_package << std::endl;
- du = du_another_package;
- }
- }
+ return result;
+ }
- // don't modify du if the installed package is already installed (freshening)
- if (du.size() > 0 && !(inst && found_installed))
- {
- // iterate trough all mount points, add usage to each directory
- // directory tree must be processed from leaves to the root directory
- // so iterate in reverse order so e.g. /usr is used before /
- for (MountPointSet::reverse_iterator mpit = result.rbegin();
- mpit != result.rend();
- mpit++)
- {
- // get usage for the mount point
- DiskUsage::Entry entry = du.extract(mpit->dir);
-
- // add or subtract it to the current value
- if (inst)
- {
- mpit->pkg_size += entry._size;
- }
- else // the package will be uninstalled
- {
- mpit->pkg_size -= entry._size;
- }
- }
- }
+ /////////////////////////////////////////////////////////////////
+ } // namespace
+ ///////////////////////////////////////////////////////////////////
+
+ DiskUsageCounter::MountPointSet DiskUsageCounter::disk_usage( const ResPool & pool_r ) const
+ {
+ Bitmap bitmap( Bitmap::poolSize );
+
+ // build installedmap (installed != transact)
+ // stays installed or gets installed
+ for_( it, pool_r.begin(), pool_r.end() )
+ {
+ if ( it->status().isInstalled() != it->status().transacts() )
+ {
+ bitmap.set( sat::asSolvable()(*it).id() );
}
}
+ return calcDiskUsage( _mps, bitmap );
+ }
- return result;
+ DiskUsageCounter::MountPointSet DiskUsageCounter::disk_usage( sat::Solvable solv_r ) const
+ {
+ Bitmap bitmap( Bitmap::poolSize );
+ bitmap.set( solv_r.id() );
+
+ // temp. unset @system Repo
+ DtorReset tmp( sat::Pool::instance().get()->installed );
+ sat::Pool::instance().get()->installed = nullptr;
+
+ return calcDiskUsage( _mps, bitmap );
}
+ 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;
+
+ 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
- << "]" << 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
///////////////////////////////////////////////////////////////////