libzypp 17.31.0
DiskUsageCounter.cc
Go to the documentation of this file.
1/*---------------------------------------------------------------------\
2| ____ _ __ __ ___ |
3| |__ / \ / / . \ . \ |
4| / / \ V /| _/ _/ |
5| / /__ | | | | | | |
6| /_____||_| |_| |_| |
7| |
8\---------------------------------------------------------------------*/
12extern "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-core/base/DtorReset>
23#include <zypp/base/String.h>
24
26#include <zypp/ExternalProgram.h>
27#include <zypp/sat/Pool.h>
28#include <zypp/sat/detail/PoolImpl.h>
29
30using std::endl;
31
33namespace 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" ) {
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;
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
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
static const Unit K
1024 Byte
Definition: ByteCount.h:45
static MountPointSet justRootPartition()
Only one entry for "/" to collect total sizes.
MountPointSet disk_usage(const ResPool &pool) const
Compute disk usage if the current transaction woud be commited.
std::set< MountPoint > MountPointSet
static MountPointSet detectMountPoints(const std::string &rootdir="/")
Get mountpoints of system below rootdir If we happen to detect snapshotting btrfs partitions,...
Assign a vaiable a certain value when going out of scope.
Definition: dtorreset.h:50
Execute a program and give access to its io An object of this class encapsulates the execution of an ...
Global ResObject pool.
Definition: ResPool.h:61
const_iterator end() const
Definition: ResPool.h:100
const_iterator begin() const
Definition: ResPool.h:97
Libsolv (bit)Map wrapper.
Definition: Map.h:34
size_type size() const
Size of the Map.
Definition: Map.cc:61
static constexpr PoolSizeType poolSize
An object indicating the bitmap should match the current pools capacity.
Definition: Map.h:41
void set(size_type idx_r)
Set bit idx_r.
Definition: Map.cc:81
static Pool instance()
Singleton ctor.
Definition: Pool.h:55
detail::CPool * get() const
Expert backdoor.
Definition: Pool.cc:49
A Solvable object within the sat Pool.
Definition: Solvable.h:54
IdType id() const
Expert backdoor.
Definition: Solvable.h:428
String related utilities and Regular expression matching.
std::string getline(std::istream &str, const Trim trim_r)
Return stream content up to (but not returning) the next newline.
Definition: String.cc:478
unsigned split(const C_Str &line_r, TOutputIterator result_r, const C_Str &sepchars_r=" \t", const Trim trim_r=NO_TRIM)
Split line_r into words.
Definition: String.h:531
Easy-to use interface to the ZYPP dependency resolver.
Definition: CodePitfalls.doc:2
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
std::ostream & operator<<(std::ostream &str, const SerialNumber &obj)
Definition: SerialNumber.cc:52
sat::Map Bitmap
Definition: Bitmap.h:19
Mount point description If block_size is set DiskUsageCoutner will assume half a block_size is wasted...
ByteCount commitDiff() const
Size change due to installation as ByteCount for convenience.
ByteCount usedSize() const
Used size of the filesystem as ByteCount for convenience.
@ Hint_growonly
growonly partitions (e.g. snapshotting btrfs)
bool readonly
hint for readonly partitions
std::string dir
Directory name.
ByteCount blockSize() const
Block size of the filesystem as ByteCount for convenience.
bool growonly
hint for growonly partitions (e.g. snapshotting btrfs)
std::string fstype
Filesystem type (provided by detectMountPoints)
ByteCount totalSize() const
Total size of the filesystem as ByteCount for convenience.
To Solvable transform functor.
Definition: Solvable.h:562
#define for_(IT, BEG, END)
Convenient for-loops using iterator.
Definition: Easy.h:28
#define DBG
Definition: Logger.h:95
#define MIL
Definition: Logger.h:96
#define WAR
Definition: Logger.h:97