Imported Upstream version 15.0.0
[platform/upstream/libzypp.git] / zypp / DiskUsageCounter.cc
index 2e15214..6ce9c07 100644 (file)
@@ -18,11 +18,14 @@ extern "C"
 #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;
 
@@ -34,102 +37,93 @@ namespace zypp
   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;
 
@@ -148,12 +142,12 @@ namespace zypp
          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;
@@ -172,6 +166,13 @@ namespace zypp
              continue;
            }
 
+           // remove /proc entry
+           if (words[0] == "/proc")
+           {
+             DBG << "Discard /proc filesystem: " << l << std::endl;
+             continue;
+           }
+
            //
            // Filter mountpoints not at or below _rootdir
            //
@@ -201,6 +202,12 @@ namespace zypp
              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
            //
@@ -226,34 +233,68 @@ namespace zypp
            //
            // 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 ) );
            }
          }
        }
@@ -262,15 +303,26 @@ namespace zypp
     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
 ///////////////////////////////////////////////////////////////////