1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/DiskUsageCounter.cc
14 #include <sys/statvfs.h>
20 #include "zypp/base/Logger.h"
21 #include "zypp/base/String.h"
23 #include "zypp/DiskUsageCounter.h"
24 #include "zypp/Package.h"
27 ///////////////////////////////////////////////////////////////////
29 { /////////////////////////////////////////////////////////////////
31 DiskUsageCounter::MountPointSet DiskUsageCounter::disk_usage(const ResPool &pool)
33 MountPointSet result = mps;
37 // partitioning is not set
41 // set used size after commit to the current used size
42 for (MountPointSet::iterator mpit = result.begin();
46 mpit->pkg_size = mpit->used_size;
49 // iterate through all packages
50 for (ResPool::byKind_iterator it = pool.byKindBegin(ResTraits<Package>::kind);
51 it != pool.byKindEnd(ResTraits<Package>::kind);
54 bool inst = it->status().isToBeInstalled();
55 bool rem = it->status().isToBeUninstalled();
57 // if the package is not selected for installation or removing
58 // it can't affect disk usage
61 Package::constPtr pkg = boost::dynamic_pointer_cast<const Package>( it->resolvable() );
62 DiskUsage du = pkg->diskusage();
63 DiskUsage du_another_package;
64 Edition edition_another_package;
66 // the same package has been selected for installation
67 bool found_installed = false;
68 // the same package has been selected for uninstallation
69 bool found_to_install = false;
71 // the du is empty or the package is selected for installation (check whether the package is already installed)
72 if (du.size() == 0 || inst)
74 // disk usage is unknown for already installed packages
75 // find the same package from any installation source
76 std::string name = (*it)->name();
78 for (ResPool::byName_iterator nameit = pool.byNameBegin(name);
79 nameit != pool.byNameEnd(name);
82 // is version and architecture same?
83 if (isKind<Package>(nameit->resolvable()))
86 Package::constPtr pkg_from_source = boost::dynamic_pointer_cast<const Package>( nameit->resolvable() );
88 if (nameit->status().isToBeInstalled())
90 found_to_install = true;
94 if ((*it)->edition() == (*nameit)->edition() && (*it)->arch() == (*nameit)->arch())
98 if (nameit->status().isInstalled())
100 found_installed = true;
101 XXX << name << '-' << (*it)->edition() << ": found already installed package (" << (*nameit)->edition() << ")" << std::endl;
106 // the package will be uninstalled and du is empty, try to use du from another object
107 du = pkg_from_source->diskusage();
110 XXX << name << '-' << (*it)->edition() << ": using DiskUsage from another Package object (" << (*nameit)->edition() << ")" << std::endl;
117 if (inst && nameit->status().isInstalled())
119 // just freshen the package, don't change du statistics
120 found_installed = true;
121 XXX << name << '-' << (*it)->edition() << ": found already installed package (" << (*nameit)->edition() << ")" << std::endl;
123 else if (pkg_from_source->diskusage().size() > 0)
125 // found different version of the package, remember the disk usage
126 // it will be used the same version is not found
127 du_another_package = pkg_from_source->diskusage();
128 edition_another_package = (*nameit)->edition();
134 // don't subtract the disk usage for updated package
135 if (du.size() == 0 && du_another_package.size() > 0 && !(rem && found_to_install))
137 XXX << name << '-' << (*it)->edition() << ": using DU info from version " << edition_another_package << std::endl;
138 du = du_another_package;
142 // don't modify du if the installed package is already installed (freshening)
143 if (du.size() > 0 && !(inst && found_installed))
145 // iterate trough all mount points, add usage to each directory
146 // directory tree must be processed from leaves to the root directory
147 // so iterate in reverse order so e.g. /usr is used before /
148 for (MountPointSet::reverse_iterator mpit = result.rbegin();
149 mpit != result.rend();
152 // get usage for the mount point
153 DiskUsage::Entry entry = du.extract(mpit->dir);
155 // add or subtract it to the current value
158 mpit->pkg_size += entry._size;
160 else // the package will be uninstalled
162 mpit->pkg_size -= entry._size;
173 DiskUsageCounter::MountPointSet DiskUsageCounter::detectMountPoints(const std::string &rootdir)
175 DiskUsageCounter::MountPointSet ret;
177 std::ifstream procmounts( "/proc/mounts" );
180 WAR << "Unable to read /proc/mounts" << std::endl;
184 if ( rootdir != "/" )
185 prfx = rootdir; // rootdir not /
187 while ( procmounts ) {
188 std::string l = str::getline( procmounts );
189 if ( !(procmounts.fail() || procmounts.bad()) ) {
192 // rootfs / rootfs rw 0 0
193 // /dev/root / reiserfs rw 0 0
194 // proc /proc proc rw 0 0
195 // devpts /dev/pts devpts rw 0 0
196 // /dev/hda5 /boot ext2 rw 0 0
197 // shmfs /dev/shm shm rw 0 0
198 // usbdevfs /proc/bus/usb usbdevfs rw 0 0
200 std::vector<std::string> words;
201 str::split( l, std::back_inserter(words) );
203 if ( words.size() < 3 ) {
204 WAR << "Suspicious entry in /proc/mounts: " << l << std::endl;
209 // Filter devices without '/' (proc,shmfs,..)
211 if ( words[0].find( '/' ) == std::string::npos ) {
212 DBG << "Discard mount point : " << l << std::endl;
217 // Filter mountpoints not at or below _rootdir
219 std::string mp = words[1];
221 if ( mp.compare( 0, prfx.size(), prfx ) != 0 ) {
222 // mountpoint not below rootdir
223 DBG << "Unwanted mount point : " << l << std::endl;
227 mp.erase( 0, prfx.size() );
230 } else if ( mp[0] != '/' ) {
231 // mountpoint not below rootdir
232 DBG << "Unwanted mount point : " << l << std::endl;
240 if ( words[2] == "iso9660" ) {
241 DBG << "Discard cdrom : " << l << std::endl;
246 // Filter some common unwanted mountpoints
248 char * mpunwanted[] = {
249 "/mnt", "/media", "/mounts", "/floppy", "/cdrom",
250 "/suse", "/var/tmp", "/var/adm/mount", "/var/adm/YaST",
254 char ** nomp = mpunwanted;
255 for ( ; *nomp; ++nomp ) {
256 std::string pre( *nomp );
257 if ( mp.compare( 0, pre.size(), pre ) == 0 // mp has prefix pre
258 && ( mp.size() == pre.size() || mp[pre.size()] == '/' ) ) {
263 DBG << "Filter mount point : " << l << std::endl;
268 // Check whether mounted readonly
271 std::vector<std::string> flags;
272 str::split( words[3], std::back_inserter(flags), "," );
274 for ( unsigned i = 0; i < flags.size(); ++i ) {
275 if ( flags[i] == "ro" ) {
282 // statvfs (full path!) and get the data
285 if ( statvfs( words[1].c_str(), &sb ) != 0 ) {
286 WAR << "Unable to statvfs(" << words[1] << "); errno " << errno << std::endl;
287 ret.insert( DiskUsageCounter::MountPoint( mp ) );
291 ret.insert( DiskUsageCounter::MountPoint( mp, sb.f_bsize,
292 ((long long)sb.f_blocks)*sb.f_bsize/1024,
293 ((long long)(sb.f_blocks - sb.f_bfree))*sb.f_bsize/1024, 0LL, ro ) );
303 ///////////////////////////////////////////////////////////////////