- Allow computation of disk usage per solvable.
[platform/upstream/libzypp.git] / zypp / DiskUsageCounter.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/DiskUsageCounter.cc
10  *
11  */
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/String.h"
23
24 #include "zypp/DiskUsageCounter.h"
25 #include "zypp/sat/Pool.h"
26 #include "zypp/sat/detail/PoolImpl.h"
27
28 using std::endl;
29
30 ///////////////////////////////////////////////////////////////////
31 namespace zypp
32 { /////////////////////////////////////////////////////////////////
33
34   ///////////////////////////////////////////////////////////////////
35   namespace
36   { /////////////////////////////////////////////////////////////////
37
38     struct SatMap : private base::NonCopyable
39     {
40       SatMap( unsigned capacity_r = 1 )
41       {
42         ::map_init( &_installedmap, sat::Pool::instance().capacity() );
43       }
44
45       void add( sat::Solvable solv_r )
46       {
47         MAPSET( &_installedmap, solv_r.id() );
48       }
49
50       void add( const PoolItem & pi_r )
51       { add( pi_r->satSolvable() ); }
52
53       void add( const ResObject::constPtr & obj_r )
54       { add( obj_r->satSolvable() ); }
55
56       mutable ::Map _installedmap;
57     };
58
59     DiskUsageCounter::MountPointSet calcDiskUsage( const DiskUsageCounter::MountPointSet & mps_r, const SatMap & installedmap_r )
60     {
61       DiskUsageCounter::MountPointSet result = mps_r;
62
63       if ( result.empty() )
64       {
65         // partitioning is not set
66         return result;
67       }
68
69       sat::Pool satpool( sat::Pool::instance() );
70       if ( ! satpool.findSystemRepo() )
71       {
72         // take care we have at least an empty stystem repo.
73         // ::pool_calc_duchanges requires it.
74         satpool.systemRepo();
75         satpool.prepare();
76       }
77
78       // init satsolver result vector with mountpoints
79       static const ::DUChanges _initdu = { 0, 0, 0 };
80       std::vector< ::DUChanges> duchanges( result.size(), _initdu );
81       {
82         unsigned idx = 0;
83         for_( it, result.begin(), result.end() )
84         {
85           duchanges[idx].path = it->dir.c_str();
86           ++idx;
87         }
88       }
89
90       // now calc...
91       ::pool_calc_duchanges( satpool.get(),
92                              satpool.systemRepo().get(),
93                              &installedmap_r._installedmap,
94                              &duchanges[0],
95                              duchanges.size() );
96
97       // and process the result
98       {
99         unsigned idx = 0;
100         for_( it, result.begin(), result.end() )
101         {
102           static const ByteCount blockAdjust( 2, ByteCount::K ); // (files * blocksize) / (2 * 1K)
103
104           it->pkg_size = it->used_size          // current usage
105                        + duchanges[idx].kbytes  // package data size
106                        + ( duchanges[idx].files * it->block_size / blockAdjust ); // half block per file
107           ++idx;
108         }
109       }
110
111       return result;
112     }
113
114     /////////////////////////////////////////////////////////////////
115   } // namespace
116   ///////////////////////////////////////////////////////////////////
117
118   DiskUsageCounter::MountPointSet DiskUsageCounter::disk_usage( const ResPool & pool_r )
119   {
120     SatMap installedmap( sat::Pool::instance().capacity() );
121     // build installedmap (installed != transact)
122     // stays installed or gets installed
123     for_( it, pool_r.begin(), pool_r.end() )
124     {
125       if ( it->status().isInstalled() != it->status().transacts() )
126       {
127         installedmap.add( *it );
128       }
129     }
130     return calcDiskUsage( mps, installedmap );
131   }
132
133   DiskUsageCounter::MountPointSet DiskUsageCounter::disk_usage( sat::Solvable solv_r )
134   {
135     SatMap installedmap;
136     installedmap.add( solv_r );
137     return calcDiskUsage( mps, installedmap );
138   }
139
140   DiskUsageCounter::MountPointSet DiskUsageCounter::detectMountPoints(const std::string &rootdir)
141   {
142     DiskUsageCounter::MountPointSet ret;
143
144       std::ifstream procmounts( "/proc/mounts" );
145
146       if ( !procmounts ) {
147         WAR << "Unable to read /proc/mounts" << std::endl;
148       } else {
149
150         std::string prfx;
151         if ( rootdir != "/" )
152           prfx = rootdir; // rootdir not /
153
154         while ( procmounts ) {
155           std::string l = str::getline( procmounts );
156           if ( !(procmounts.fail() || procmounts.bad()) ) {
157             // data to consume
158
159             // rootfs   /               rootfs          rw 0 0
160             // /dev/root        /               reiserfs        rw 0 0
161             // proc     /proc           proc            rw 0 0
162             // devpts   /dev/pts        devpts          rw 0 0
163             // /dev/hda5        /boot           ext2            rw 0 0
164             // shmfs    /dev/shm        shm             rw 0 0
165             // usbdevfs         /proc/bus/usb   usbdevfs        rw 0 0
166
167             std::vector<std::string> words;
168             str::split( l, std::back_inserter(words) );
169
170             if ( words.size() < 3 ) {
171               WAR << "Suspicious entry in /proc/mounts: " << l << std::endl;
172               continue;
173             }
174
175             //
176             // Filter devices without '/' (proc,shmfs,..)
177             //
178             if ( words[0].find( '/' ) == std::string::npos ) {
179               DBG << "Discard mount point : " << l << std::endl;
180               continue;
181             }
182
183             //
184             // Filter mountpoints not at or below _rootdir
185             //
186             std::string mp = words[1];
187             if ( prfx.size() ) {
188               if ( mp.compare( 0, prfx.size(), prfx ) != 0 ) {
189                 // mountpoint not below rootdir
190                 DBG << "Unwanted mount point : " << l << std::endl;
191                 continue;
192               }
193               // strip prfx
194               mp.erase( 0, prfx.size() );
195               if ( mp.empty() ) {
196                 mp = "/";
197               } else if ( mp[0] != '/' ) {
198                 // mountpoint not below rootdir
199                 DBG << "Unwanted mount point : " << l << std::endl;
200                 continue;
201               }
202             }
203
204             //
205             // Filter cdrom
206             //
207             if ( words[2] == "iso9660" ) {
208               DBG << "Discard cdrom : " << l << std::endl;
209               continue;
210             }
211
212             if ( words[2] == "vfat" || words[2] == "fat" || words[2] == "ntfs" || words[2] == "ntfs-3g")
213             {
214               MIL << words[1] << " contains ignored fs (" << words[2] << ')' << std::endl;
215               continue;
216             }
217
218             //
219             // Filter some common unwanted mountpoints
220             //
221             const char * mpunwanted[] = {
222               "/mnt", "/media", "/mounts", "/floppy", "/cdrom",
223               "/suse", "/var/tmp", "/var/adm/mount", "/var/adm/YaST",
224               /*last*/0/*entry*/
225             };
226
227             const char ** nomp = mpunwanted;
228             for ( ; *nomp; ++nomp ) {
229               std::string pre( *nomp );
230               if ( mp.compare( 0, pre.size(), pre ) == 0 // mp has prefix pre
231                    && ( mp.size() == pre.size() || mp[pre.size()] == '/' ) ) {
232                 break;
233               }
234             }
235             if ( *nomp ) {
236               DBG << "Filter mount point : " << l << std::endl;
237               continue;
238             }
239
240             //
241             // Check whether mounted readonly
242             //
243             bool ro = false;
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                 ro = true;
250                 break;
251               }
252             }
253             if ( ro ) {
254               DBG << "Filter ro mount point : " << l << std::endl;
255               continue;
256             }
257
258             //
259             // statvfs (full path!) and get the data
260             //
261             struct statvfs sb;
262             if ( statvfs( words[1].c_str(), &sb ) != 0 ) {
263               WAR << "Unable to statvfs(" << words[1] << "); errno " << errno << std::endl;
264               ret.insert( DiskUsageCounter::MountPoint( mp ) );
265             }
266             else
267             {
268               ret.insert( DiskUsageCounter::MountPoint( mp, sb.f_bsize,
269                 ((long long)sb.f_blocks)*sb.f_bsize/1024,
270                 ((long long)(sb.f_blocks - sb.f_bfree))*sb.f_bsize/1024, 0LL, ro ) );
271             }
272           }
273         }
274     }
275
276     return ret;
277   }
278
279   DiskUsageCounter::MountPointSet DiskUsageCounter::justRootPartition()
280   {
281     DiskUsageCounter::MountPointSet ret;
282     ret.insert( DiskUsageCounter::MountPoint() );
283     return ret;
284   }
285
286   std::ostream & operator<<( std::ostream & str, const DiskUsageCounter::MountPoint & obj )
287   {
288      str << "dir:[" << obj.dir << "] [ bs: " << obj.blockSize()
289         << " ts: " << obj.totalSize()
290         << " us: " << obj.usedSize()
291         << " (+-: " << obj.commitDiff()
292         << ")]";
293     return str;
294   }
295
296   /////////////////////////////////////////////////////////////////
297 } // namespace zypp
298 ///////////////////////////////////////////////////////////////////