libzypp  15.28.6
DiskUsageCounter.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
12 extern "C"
13 {
14 #include <sys/statvfs.h>
15 }
16 
17 #include <iostream>
18 #include <fstream>
19 
20 #include "zypp/base/Easy.h"
21 #include "zypp/base/LogTools.h"
22 #include "zypp/base/DtorReset.h"
23 #include "zypp/base/String.h"
24 
25 #include "zypp/DiskUsageCounter.h"
26 #include "zypp/ExternalProgram.h"
27 #include "zypp/sat/Pool.h"
29 
30 using std::endl;
31 
33 namespace zypp
34 {
35 
37  namespace
38  {
39 
40  DiskUsageCounter::MountPointSet calcDiskUsage( DiskUsageCounter::MountPointSet result, const Bitmap & installedmap_r )
41  {
42  if ( result.empty() )
43  {
44  // partitioning is not set
45  return result;
46  }
47 
48  sat::Pool satpool( sat::Pool::instance() );
49 
50  // init libsolv result vector with mountpoints
51  static const ::DUChanges _initdu = { 0, 0, 0, 0 };
52  std::vector< ::DUChanges> duchanges( result.size(), _initdu );
53  {
54  unsigned idx = 0;
55  for_( it, result.begin(), result.end() )
56  {
57  duchanges[idx].path = it->dir.c_str();
58  if ( it->growonly )
59  duchanges[idx].flags |= DUCHANGES_ONLYADD;
60  ++idx;
61  }
62  }
63  // now calc...
64  ::pool_calc_duchanges( satpool.get(),
65  const_cast<Bitmap &>(installedmap_r),
66  &duchanges[0],
67  duchanges.size() );
68 
69  // and process the result
70  {
71  unsigned idx = 0;
72  for_( it, result.begin(), result.end() )
73  {
74  // Limit estimated waste (half block per file) as it does not apply to
75  // btrfs, which reports up to 64K blocksize (bsc#974275,bsc#965322)
76  static const ByteCount blockAdjust( 2, ByteCount::K ); // (files * blocksize) / 2 / 1K; result value in K!
77 
78  it->pkg_size = it->used_size // current usage
79  + duchanges[idx].kbytes // package data size
80  + ( duchanges[idx].files * ( it->fstype == "btrfs" ? 4096 : it->block_size ) / blockAdjust ); // half block per file
81  ++idx;
82  }
83  }
84 
85  return result;
86  }
87 
89  } // namespace
91 
93  {
94  Bitmap bitmap( Bitmap::poolSize );
95 
96  // build installedmap (installed != transact)
97  // stays installed or gets installed
98  for_( it, pool_r.begin(), pool_r.end() )
99  {
100  if ( it->status().isInstalled() != it->status().transacts() )
101  {
102  bitmap.set( sat::asSolvable()(*it).id() );
103  }
104  }
105  return calcDiskUsage( _mps, bitmap );
106  }
107 
109  {
110  Bitmap bitmap( Bitmap::poolSize );
111  bitmap.set( solv_r.id() );
112 
113  // temp. unset @system Repo
114  DtorReset tmp( sat::Pool::instance().get()->installed );
115  sat::Pool::instance().get()->installed = nullptr;
116 
117  return calcDiskUsage( _mps, bitmap );
118  }
119 
121  {
122  // temp. unset @system Repo
123  DtorReset tmp( sat::Pool::instance().get()->installed );
124  sat::Pool::instance().get()->installed = nullptr;
125 
126  return calcDiskUsage( _mps, bitmap_r );
127  }
128 
130  {
132 
133  typedef std::map<std::string, MountPoint> Btrfsfilter;
134  Btrfsfilter btrfsfilter; // see btrfs hack below
135 
136  std::ifstream procmounts( "/proc/mounts" );
137 
138  if ( !procmounts ) {
139  WAR << "Unable to read /proc/mounts" << std::endl;
140  } else {
141 
142  std::string prfx;
143  if ( rootdir != "/" )
144  prfx = rootdir; // rootdir not /
145 
146  while ( procmounts ) {
147  std::string l = str::getline( procmounts );
148  if ( !(procmounts.fail() || procmounts.bad()) ) {
149  // data to consume
150 
151  // rootfs / rootfs rw 0 0
152  // /dev/root / reiserfs rw 0 0
153  // proc /proc proc rw 0 0
154  // devpts /dev/pts devpts rw 0 0
155  // /dev/hda5 /boot ext2 rw 0 0
156  // shmfs /dev/shm shm rw 0 0
157  // usbdevfs /proc/bus/usb usbdevfs rw 0 0
158 
159  std::vector<std::string> words;
160  str::split( l, std::back_inserter(words) );
161 
162  if ( words.size() < 3 ) {
163  WAR << "Suspicious entry in /proc/mounts: " << l << std::endl;
164  continue;
165  }
166 
167  //
168  // Filter devices without '/' (proc,shmfs,..)
169  //
170  if ( words[0].find( '/' ) == std::string::npos ) {
171  DBG << "Discard mount point : " << l << std::endl;
172  continue;
173  }
174 
175  // remove /proc entry
176  if (words[0] == "/proc")
177  {
178  DBG << "Discard /proc filesystem: " << l << std::endl;
179  continue;
180  }
181 
182  //
183  // Filter mountpoints not at or below _rootdir
184  //
185  std::string mp = words[1];
186  if ( prfx.size() ) {
187  if ( mp.compare( 0, prfx.size(), prfx ) != 0 ) {
188  // mountpoint not below rootdir
189  DBG << "Unwanted mount point : " << l << std::endl;
190  continue;
191  }
192  // strip prfx
193  mp.erase( 0, prfx.size() );
194  if ( mp.empty() ) {
195  mp = "/";
196  } else if ( mp[0] != '/' ) {
197  // mountpoint not below rootdir
198  DBG << "Unwanted mount point : " << l << std::endl;
199  continue;
200  }
201  }
202 
203  //
204  // Filter cdrom
205  //
206  if ( words[2] == "iso9660" ) {
207  DBG << "Discard cdrom : " << l << std::endl;
208  continue;
209  }
210 
211  if ( words[2] == "vfat" || words[2] == "fat" || words[2] == "ntfs" || words[2] == "ntfs-3g")
212  {
213  MIL << words[1] << " contains ignored fs (" << words[2] << ')' << std::endl;
214  continue;
215  }
216 
217  //
218  // Filter some common unwanted mountpoints
219  //
220  const char * mpunwanted[] = {
221  "/mnt", "/media", "/mounts", "/floppy", "/cdrom",
222  "/suse", "/tmp", "/var/tmp", "/var/adm/mount", "/var/adm/YaST",
223  /*last*/0/*entry*/
224  };
225 
226  const char ** nomp = mpunwanted;
227  for ( ; *nomp; ++nomp ) {
228  std::string pre( *nomp );
229  if ( mp.compare( 0, pre.size(), pre ) == 0 // mp has prefix pre
230  && ( mp.size() == pre.size() || mp[pre.size()] == '/' ) ) {
231  break;
232  }
233  }
234  if ( *nomp ) {
235  DBG << "Filter mount point : " << l << std::endl;
236  continue;
237  }
238 
239  //
240  // Check whether mounted readonly
241  //
242  MountPoint::HintFlags hints;
243 
244  std::vector<std::string> flags;
245  str::split( words[3], std::back_inserter(flags), "," );
246 
247  for ( unsigned i = 0; i < flags.size(); ++i ) {
248  if ( flags[i] == "ro" ) {
249  hints |= MountPoint::Hint_readonly;
250  break;
251  }
252  }
253  if ( hints.testFlag( MountPoint::Hint_readonly ) ) {
254  DBG << "Filter ro mount point : " << l << std::endl;
255  continue;
256  }
257 
258  //
259  // check for snapshotting btrfs
260  //
261  bool btrfshack = false;
262  if ( words[2] == "btrfs" )
263  {
264  btrfshack = true;
265  if ( geteuid() != 0 )
266  {
267  DBG << "Assume snapshots on " << words[1] << ": non-root user can't check" << std::endl;
268  hints |= MountPoint::Hint_growonly;
269  }
270  else
271  {
272  // For now just check whether there is
273  // at least one snapshot on the volume:
274  ExternalProgram prog({"btrfs","subvolume","list","-s",words[1]});
275  std::string line( prog.receiveLine() );
276  if ( ! line.empty() )
277  {
278  DBG << "Found a snapshot on " << words[1] << ": " << line; // has trailing std::endl
279  hints |= MountPoint::Hint_growonly;
280  }
281  prog.kill();
282  }
283  }
284 
285  //
286  // statvfs (full path!) and get the data
287  //
288  struct statvfs sb;
289  if ( statvfs( words[1].c_str(), &sb ) != 0 ) {
290  WAR << "Unable to statvfs(" << words[1] << "); errno " << errno << std::endl;
291  ret.insert( DiskUsageCounter::MountPoint( mp, words[2], 0LL, 0LL, 0LL, 0LL, hints ) );
292  }
293  else
294  {
295  //
296  // Filter zero sized devices (bnc#769819)
297  //
298  if ( sb.f_blocks == 0 || sb.f_bsize == 0 )
299  {
300  DBG << "Filter zero-sized mount point : " << l << std::endl;
301  continue;
302  }
303  if ( btrfshack )
304  {
305  // HACK:
306  // Collect just the top/1st mountpoint of each btrfs volume
307  // (by device). This filters away nested subvolumes
308  // which otherwise break per package disk usage computation.
309  // FIX: Computation must learn to handle multiple mount points
310  // contributing to the same file system.
311  MountPoint & bmp( btrfsfilter[words[0]] );
312  if ( bmp.fstype.empty() ) // 1st occurance
313  {
314  bmp = DiskUsageCounter::MountPoint( mp, words[2], sb.f_bsize,
315  ((long long)sb.f_blocks)*sb.f_bsize/1024,
316  ((long long)(sb.f_blocks - sb.f_bfree))*sb.f_bsize/1024, 0LL, hints );
317  }
318  else if ( bmp.dir > mp )
319  bmp.dir = mp;
320  continue;
321  }
322  ret.insert( DiskUsageCounter::MountPoint( mp, words[2], sb.f_bsize,
323  ((long long)sb.f_blocks)*sb.f_bsize/1024,
324  ((long long)(sb.f_blocks - sb.f_bfree))*sb.f_bsize/1024, 0LL, hints ) );
325  }
326  }
327  }
328  }
329 
330  // collect filtered btrfs volumes
331  for ( auto && bmp : btrfsfilter )
332  ret.insert( std::move(bmp.second) );
333 
334  return ret;
335  }
336 
338  {
340  ret.insert( DiskUsageCounter::MountPoint() );
341  return ret;
342  }
343 
344  std::ostream & operator<<( std::ostream & str, const DiskUsageCounter::MountPoint & obj )
345  {
346  str << "dir:[" << obj.dir << "] [ bs: " << obj.blockSize()
347  << " ts: " << obj.totalSize()
348  << " us: " << obj.usedSize()
349  << " (+-: " << obj.commitDiff()
350  << ")" << (obj.readonly?"r":"") << (obj.growonly?"g":"") << " " << obj.fstype << "]";
351  return str;
352  }
353 
354  std::ostream & operator<<( std::ostream & str, const DiskUsageCounter::MountPointSet & obj )
355  { return dumpRange( str, obj.begin(), obj.end() ); }
356 
358 } // namespace zypp
ByteCount blockSize() const
Block size of the filesystem as ByteCount for convenience.
#define MIL
Definition: Logger.h:64
A Solvable object within the sat Pool.
Definition: Solvable.h:53
const_iterator begin() const
Definition: ResPool.h:97
ByteCount commitDiff() const
Size change due to installation as ByteCount for convenience.
growonly partitions (e.g. snapshotting btrfs)
size_type size() const
Size of the Map.
Definition: Map.cc:59
static MountPointSet justRootPartition()
Only one entry for "/" to collect total sizes.
bool readonly
hint for readonly partitions
void set(size_type idx_r)
Set bit idx_r.
Definition: Map.cc:79
detail::CPool * get() const
Expert backdoor.
Definition: Pool.cc:49
#define for_(IT, BEG, END)
Convenient for-loops using iterator.
Definition: Easy.h:27
const_iterator end() const
Definition: ResPool.h:100
Assign a vaiable a certain value when going out of scope.
Definition: DtorReset.h:49
std::string fstype
Filesystem type (provided by detectMountPoints)
std::set< MountPoint > MountPointSet
static Pool instance()
Singleton ctor.
Definition: Pool.h:53
std::ostream & dumpRange(std::ostream &str, TIterator begin, TIterator end, const std::string &intro="{", const std::string &pfx="\n ", const std::string &sep="\n ", const std::string &sfx="\n", const std::string &extro="}")
Print range defined by iterators (multiline style).
Definition: LogTools.h:91
unsigned split(const C_Str &line_r, TOutputIterator result_r, const C_Str &sepchars_r=" \t")
Split line_r into words.
Definition: String.h:518
std::ostream & operator<<(std::ostream &str, const Exception &obj)
Definition: Exception.cc:120
Execute a program and give access to its io An object of this class encapsulates the execution of an ...
sat::Map Bitmap
Definition: Bitmap.h:19
#define WAR
Definition: Logger.h:65
Mount point description If block_size is set DiskUsageCoutner will assume half a block_size is wasted...
std::string dir
Directory name.
std::string getline(std::istream &str, const Trim trim_r)
Return stream content up to (but not returning) the next newline.
Definition: String.cc:476
ByteCount totalSize() const
Total size of the filesystem as ByteCount for convenience.
static constexpr PoolSizeType poolSize
An object indicating the bitmap should match the current pools capacity.
Definition: Map.h:41
Global ResObject pool.
Definition: ResPool.h:60
static const Unit K
1024 Byte
Definition: ByteCount.h:45
IdType id() const
Expert backdoor.
Definition: Solvable.h:374
MountPointSet disk_usage(const ResPool &pool) const
Compute disk usage if the current transaction woud be commited.
bool growonly
hint for growonly partitions (e.g. snapshotting btrfs)
Libsolv (bit)Map wrapper.
Definition: Map.h:33
To Solvable transform functor.
Definition: Solvable.h:503
ByteCount usedSize() const
Used size of the filesystem as ByteCount for convenience.
#define DBG
Definition: Logger.h:63
static MountPointSet detectMountPoints(const std::string &rootdir="/")
Get mountpoints of system below rootdir If we happen to detect snapshotting btrfs partitions...