1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/DiskUsageCounter.cc
14 #include <sys/statvfs.h>
20 #include "zypp/base/Easy.h"
21 #include "zypp/base/LogTools.h"
22 #include "zypp/base/DtorReset.h"
23 #include "zypp/base/String.h"
25 #include "zypp/DiskUsageCounter.h"
26 #include "zypp/ExternalProgram.h"
27 #include "zypp/sat/Pool.h"
28 #include "zypp/sat/detail/PoolImpl.h"
32 ///////////////////////////////////////////////////////////////////
34 { /////////////////////////////////////////////////////////////////
36 ///////////////////////////////////////////////////////////////////
38 { /////////////////////////////////////////////////////////////////
40 DiskUsageCounter::MountPointSet calcDiskUsage( DiskUsageCounter::MountPointSet result, const Bitmap & installedmap_r )
44 // partitioning is not set
48 sat::Pool satpool( sat::Pool::instance() );
50 // init libsolv result vector with mountpoints
51 static const ::DUChanges _initdu = { 0, 0, 0, 0 };
52 std::vector< ::DUChanges> duchanges( result.size(), _initdu );
55 for_( it, result.begin(), result.end() )
57 duchanges[idx].path = it->dir.c_str();
59 duchanges[idx].flags |= DUCHANGES_ONLYADD;
64 ::pool_calc_duchanges( satpool.get(),
65 const_cast<Bitmap &>(installedmap_r),
69 // and process the result
72 for_( it, result.begin(), result.end() )
74 static const ByteCount blockAdjust( 2, ByteCount::K ); // (files * blocksize) / (2 * 1K)
75 it->pkg_size = it->used_size // current usage
76 + duchanges[idx].kbytes // package data size
77 + ( duchanges[idx].files * it->block_size / blockAdjust ); // half block per file
85 /////////////////////////////////////////////////////////////////
87 ///////////////////////////////////////////////////////////////////
89 DiskUsageCounter::MountPointSet DiskUsageCounter::disk_usage( const ResPool & pool_r ) const
91 Bitmap bitmap( Bitmap::poolSize );
93 // build installedmap (installed != transact)
94 // stays installed or gets installed
95 for_( it, pool_r.begin(), pool_r.end() )
97 if ( it->status().isInstalled() != it->status().transacts() )
99 bitmap.set( sat::asSolvable()(*it).id() );
102 return calcDiskUsage( _mps, bitmap );
105 DiskUsageCounter::MountPointSet DiskUsageCounter::disk_usage( sat::Solvable solv_r ) const
107 Bitmap bitmap( Bitmap::poolSize );
108 bitmap.set( solv_r.id() );
110 // temp. unset @system Repo
111 DtorReset tmp( sat::Pool::instance().get()->installed );
112 sat::Pool::instance().get()->installed = nullptr;
114 return calcDiskUsage( _mps, bitmap );
117 DiskUsageCounter::MountPointSet DiskUsageCounter::disk_usage( const Bitmap & bitmap_r ) const
119 // temp. unset @system Repo
120 DtorReset tmp( sat::Pool::instance().get()->installed );
121 sat::Pool::instance().get()->installed = nullptr;
123 return calcDiskUsage( _mps, bitmap_r );
126 DiskUsageCounter::MountPointSet DiskUsageCounter::detectMountPoints( const std::string & rootdir )
128 DiskUsageCounter::MountPointSet ret;
130 typedef std::map<ulong, MountPoint> Btrfsfilter;
131 Btrfsfilter btrfsfilter; // see btrfs hack below
133 std::ifstream procmounts( "/proc/mounts" );
136 WAR << "Unable to read /proc/mounts" << std::endl;
140 if ( rootdir != "/" )
141 prfx = rootdir; // rootdir not /
143 while ( procmounts ) {
144 std::string l = str::getline( procmounts );
145 if ( !(procmounts.fail() || procmounts.bad()) ) {
148 // rootfs / rootfs rw 0 0
149 // /dev/root / reiserfs rw 0 0
150 // proc /proc proc rw 0 0
151 // devpts /dev/pts devpts rw 0 0
152 // /dev/hda5 /boot ext2 rw 0 0
153 // shmfs /dev/shm shm rw 0 0
154 // usbdevfs /proc/bus/usb usbdevfs rw 0 0
156 std::vector<std::string> words;
157 str::split( l, std::back_inserter(words) );
159 if ( words.size() < 3 ) {
160 WAR << "Suspicious entry in /proc/mounts: " << l << std::endl;
165 // Filter devices without '/' (proc,shmfs,..)
167 if ( words[0].find( '/' ) == std::string::npos ) {
168 DBG << "Discard mount point : " << l << std::endl;
172 // remove /proc entry
173 if (words[0] == "/proc")
175 DBG << "Discard /proc filesystem: " << l << std::endl;
180 // Filter mountpoints not at or below _rootdir
182 std::string mp = words[1];
184 if ( mp.compare( 0, prfx.size(), prfx ) != 0 ) {
185 // mountpoint not below rootdir
186 DBG << "Unwanted mount point : " << l << std::endl;
190 mp.erase( 0, prfx.size() );
193 } else if ( mp[0] != '/' ) {
194 // mountpoint not below rootdir
195 DBG << "Unwanted mount point : " << l << std::endl;
203 if ( words[2] == "iso9660" ) {
204 DBG << "Discard cdrom : " << l << std::endl;
208 if ( words[2] == "vfat" || words[2] == "fat" || words[2] == "ntfs" || words[2] == "ntfs-3g")
210 MIL << words[1] << " contains ignored fs (" << words[2] << ')' << std::endl;
215 // Filter some common unwanted mountpoints
217 const char * mpunwanted[] = {
218 "/mnt", "/media", "/mounts", "/floppy", "/cdrom",
219 "/suse", "/tmp", "/var/tmp", "/var/adm/mount", "/var/adm/YaST",
223 const char ** nomp = mpunwanted;
224 for ( ; *nomp; ++nomp ) {
225 std::string pre( *nomp );
226 if ( mp.compare( 0, pre.size(), pre ) == 0 // mp has prefix pre
227 && ( mp.size() == pre.size() || mp[pre.size()] == '/' ) ) {
232 DBG << "Filter mount point : " << l << std::endl;
237 // Check whether mounted readonly
239 MountPoint::HintFlags hints;
241 std::vector<std::string> flags;
242 str::split( words[3], std::back_inserter(flags), "," );
244 for ( unsigned i = 0; i < flags.size(); ++i ) {
245 if ( flags[i] == "ro" ) {
246 hints |= MountPoint::Hint_readonly;
250 if ( hints.testFlag( MountPoint::Hint_readonly ) ) {
251 DBG << "Filter ro mount point : " << l << std::endl;
256 // check for snapshotting btrfs
258 bool btrfshack = false;
259 if ( words[2] == "btrfs" )
262 if ( geteuid() != 0 )
264 DBG << "Assume snapshots on " << words[1] << ": non-root user can't check" << std::endl;
265 hints |= MountPoint::Hint_growonly;
269 // For now just check whether there is
270 // at least one snapshot on the volume:
271 ExternalProgram prog({"btrfs","subvolume","list","-s",words[1]});
272 std::string line( prog.receiveLine() );
273 if ( ! line.empty() )
275 DBG << "Found a snapshot on " << words[1] << ": " << line; // has trailing std::endl
276 hints |= MountPoint::Hint_growonly;
283 // statvfs (full path!) and get the data
286 if ( statvfs( words[1].c_str(), &sb ) != 0 ) {
287 WAR << "Unable to statvfs(" << words[1] << "); errno " << errno << std::endl;
288 ret.insert( DiskUsageCounter::MountPoint( mp, words[2], 0LL, 0LL, 0LL, 0LL, hints ) );
293 // Filter zero sized devices (bnc#769819)
295 if ( sb.f_blocks == 0 || sb.f_bsize == 0 )
297 DBG << "Filter zero-sized mount point : " << l << std::endl;
303 // Collect just the top/1st mountpoint of each btrfs volume
304 // (by file system ID). This filters away nested subvolumes
305 // which otherwise break per package disk usage computation.
306 // FIX: Computation must learn to handle multiple mount points
307 // contributing to the same file system.
308 MountPoint & bmp( btrfsfilter[sb.f_fsid] );
309 if ( bmp.fstype.empty() ) // 1st occurance
311 bmp = DiskUsageCounter::MountPoint( mp, words[2], sb.f_bsize,
312 ((long long)sb.f_blocks)*sb.f_bsize/1024,
313 ((long long)(sb.f_blocks - sb.f_bfree))*sb.f_bsize/1024, 0LL, hints );
315 else if ( bmp.dir > mp )
319 ret.insert( DiskUsageCounter::MountPoint( mp, words[2], sb.f_bsize,
320 ((long long)sb.f_blocks)*sb.f_bsize/1024,
321 ((long long)(sb.f_blocks - sb.f_bfree))*sb.f_bsize/1024, 0LL, hints ) );
327 // collect filtered btrfs volumes
328 for ( auto && bmp : btrfsfilter )
329 ret.insert( std::move(bmp.second) );
334 DiskUsageCounter::MountPointSet DiskUsageCounter::justRootPartition()
336 DiskUsageCounter::MountPointSet ret;
337 ret.insert( DiskUsageCounter::MountPoint() );
341 std::ostream & operator<<( std::ostream & str, const DiskUsageCounter::MountPoint & obj )
343 str << "dir:[" << obj.dir << "] [ bs: " << obj.blockSize()
344 << " ts: " << obj.totalSize()
345 << " us: " << obj.usedSize()
346 << " (+-: " << obj.commitDiff()
347 << ")" << (obj.readonly?"r":"") << (obj.growonly?"g":"") << " " << obj.fstype << "]";
351 std::ostream & operator<<( std::ostream & str, const DiskUsageCounter::MountPointSet & obj )
352 { return dumpRange( str, obj.begin(), obj.end() ); }
354 /////////////////////////////////////////////////////////////////
356 ///////////////////////////////////////////////////////////////////