- Adapt to satsolver-0.12 API.
[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
71       // init satsolver result vector with mountpoints
72       static const ::DUChanges _initdu = { 0, 0, 0 };
73       std::vector< ::DUChanges> duchanges( result.size(), _initdu );
74       {
75         unsigned idx = 0;
76         for_( it, result.begin(), result.end() )
77         {
78           duchanges[idx].path = it->dir.c_str();
79           ++idx;
80         }
81       }
82
83       // now calc...
84       ::pool_calc_duchanges( satpool.get(),
85                              &installedmap_r._installedmap,
86                              &duchanges[0],
87                              duchanges.size() );
88
89       // and process the result
90       {
91         unsigned idx = 0;
92         for_( it, result.begin(), result.end() )
93         {
94           static const ByteCount blockAdjust( 2, ByteCount::K ); // (files * blocksize) / (2 * 1K)
95
96           it->pkg_size = it->used_size          // current usage
97                        + duchanges[idx].kbytes  // package data size
98                        + ( duchanges[idx].files * it->block_size / blockAdjust ); // half block per file
99           ++idx;
100         }
101       }
102
103       return result;
104     }
105
106     /////////////////////////////////////////////////////////////////
107   } // namespace
108   ///////////////////////////////////////////////////////////////////
109
110   DiskUsageCounter::MountPointSet DiskUsageCounter::disk_usage( const ResPool & pool_r )
111   {
112     SatMap installedmap( sat::Pool::instance().capacity() );
113     // build installedmap (installed != transact)
114     // stays installed or gets installed
115     for_( it, pool_r.begin(), pool_r.end() )
116     {
117       if ( it->status().isInstalled() != it->status().transacts() )
118       {
119         installedmap.add( *it );
120       }
121     }
122     return calcDiskUsage( mps, installedmap );
123   }
124
125   DiskUsageCounter::MountPointSet DiskUsageCounter::disk_usage( sat::Solvable solv_r )
126   {
127     SatMap installedmap;
128     installedmap.add( solv_r );
129     return calcDiskUsage( mps, installedmap );
130   }
131
132   DiskUsageCounter::MountPointSet DiskUsageCounter::detectMountPoints(const std::string &rootdir)
133   {
134     DiskUsageCounter::MountPointSet ret;
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", "/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             bool ro = false;
243             std::vector<std::string> flags;
244             str::split( words[3], std::back_inserter(flags), "," );
245
246             for ( unsigned i = 0; i < flags.size(); ++i ) {
247               if ( flags[i] == "ro" ) {
248                 ro = true;
249                 break;
250               }
251             }
252             if ( ro ) {
253               DBG << "Filter ro mount point : " << l << std::endl;
254               continue;
255             }
256
257             //
258             // statvfs (full path!) and get the data
259             //
260             struct statvfs sb;
261             if ( statvfs( words[1].c_str(), &sb ) != 0 ) {
262               WAR << "Unable to statvfs(" << words[1] << "); errno " << errno << std::endl;
263               ret.insert( DiskUsageCounter::MountPoint( mp ) );
264             }
265             else
266             {
267               ret.insert( DiskUsageCounter::MountPoint( mp, sb.f_bsize,
268                 ((long long)sb.f_blocks)*sb.f_bsize/1024,
269                 ((long long)(sb.f_blocks - sb.f_bfree))*sb.f_bsize/1024, 0LL, ro ) );
270             }
271           }
272         }
273     }
274
275     return ret;
276   }
277
278   DiskUsageCounter::MountPointSet DiskUsageCounter::justRootPartition()
279   {
280     DiskUsageCounter::MountPointSet ret;
281     ret.insert( DiskUsageCounter::MountPoint() );
282     return ret;
283   }
284
285   std::ostream & operator<<( std::ostream & str, const DiskUsageCounter::MountPoint & obj )
286   {
287      str << "dir:[" << obj.dir << "] [ bs: " << obj.blockSize()
288         << " ts: " << obj.totalSize()
289         << " us: " << obj.usedSize()
290         << " (+-: " << obj.commitDiff()
291         << ")]";
292     return str;
293   }
294
295   /////////////////////////////////////////////////////////////////
296 } // namespace zypp
297 ///////////////////////////////////////////////////////////////////