changing ostream::operator<< breaks testsuite. reverted.
[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/Logger.h"
22 #include "zypp/base/String.h"
23
24 #include "zypp/DiskUsageCounter.h"
25 #include "zypp/Package.h"
26
27 using std::endl;
28
29 ///////////////////////////////////////////////////////////////////
30 namespace zypp
31 { /////////////////////////////////////////////////////////////////
32
33   ///////////////////////////////////////////////////////////////////
34   namespace
35   { /////////////////////////////////////////////////////////////////
36
37     inline void addDu( DiskUsageCounter::MountPointSet & result_r, DiskUsage & du_r )
38     {
39       // traverse mountpoints in reverse order. This is done beacuse
40       // DiskUsage::extract computes the mountpoint size, and then
41       // removes the entry. So we must process leaves first.
42       for_( mpit, result_r.rbegin(), result_r.rend() )
43       {
44         // Extract usage for the mount point
45         DiskUsage::Entry entry = du_r.extract( mpit->dir );
46         // Adjust the data.
47         mpit->pkg_size += entry._size;
48       }
49     }
50
51     inline void delDu( DiskUsageCounter::MountPointSet & result_r, DiskUsage & du_r )
52     {
53       // traverse mountpoints in reverse order. This is done beacuse
54       // DiskUsage::extract computes the mountpoint size, and then
55       // removes the entry. So we must process leaves first.
56       for_( mpit, result_r.rbegin(), result_r.rend() )
57       {
58         // Extract usage for the mount point
59         DiskUsage::Entry entry = du_r.extract( mpit->dir );
60         // Adjust the data.
61         mpit->pkg_size -= entry._size;
62       }
63     }
64
65     /////////////////////////////////////////////////////////////////
66   } // namespace
67   ///////////////////////////////////////////////////////////////////
68
69  DiskUsageCounter::MountPointSet DiskUsageCounter::disk_usage( const ResPool & pool_r )
70   {
71     DiskUsageCounter::MountPointSet result = mps;
72
73     if ( result.empty() )
74     {
75       // partitioning is not set
76       return result;
77     }
78
79     // set used size after commit to the current used size
80     for_( it, result.begin(), result.end() )
81     {
82       it->pkg_size = it->used_size;
83     }
84
85     // iterate through all items
86     for_( it, pool_r.begin(), pool_r.end() )
87     {
88       DiskUsage du( (*it)->diskusage() );
89
90       // skip items without du info
91       if ( du.empty() )
92         continue; // or find some substitute info
93
94       // skip items that do not transact
95       if ( ! it->status().transacts() )
96         continue;
97
98       // Adjust the data.
99       if ( it->status().isUninstalled() )
100       {
101         // an uninstalled item gets installed:
102         addDu( result, du );
103
104         // While there is no valid solver result, items to update
105         // are selected, but installed old versions are not yet
106         // deselected. We try to compensate this:
107         if ( ! (*it)->installOnly() )
108         {
109           // Item to update -> check the installed ones.
110           for_( nit, pool_r.byNameBegin((*it)->name()), pool_r.byNameEnd((*it)->name()) )
111           {                                          // same name
112             if (    (*nit)->kind() == (*it)->kind()  // same kind
113                  && nit->status().staysInstalled() ) // and unselected installed
114             {
115               DiskUsage ndu( (*nit)->diskusage() );
116               if ( ! ndu.empty() )
117               {
118                 delDu( result, ndu );
119               }
120             }
121           }
122         }
123       }
124       else
125       {
126         // an installed item gets deleted:
127         delDu( result, du );
128       }
129     }
130     return result;
131   }
132
133   DiskUsageCounter::MountPointSet DiskUsageCounter::detectMountPoints(const std::string &rootdir)
134   {
135     DiskUsageCounter::MountPointSet ret;
136
137       std::ifstream procmounts( "/proc/mounts" );
138
139       if ( !procmounts ) {
140         WAR << "Unable to read /proc/mounts" << std::endl;
141       } else {
142
143         std::string prfx;
144         if ( rootdir != "/" )
145           prfx = rootdir; // rootdir not /
146
147         while ( procmounts ) {
148           std::string l = str::getline( procmounts );
149           if ( !(procmounts.fail() || procmounts.bad()) ) {
150             // data to consume
151
152             // rootfs   /               rootfs          rw 0 0
153             // /dev/root        /               reiserfs        rw 0 0
154             // proc     /proc           proc            rw 0 0
155             // devpts   /dev/pts        devpts          rw 0 0
156             // /dev/hda5        /boot           ext2            rw 0 0
157             // shmfs    /dev/shm        shm             rw 0 0
158             // usbdevfs         /proc/bus/usb   usbdevfs        rw 0 0
159
160             std::vector<std::string> words;
161             str::split( l, std::back_inserter(words) );
162
163             if ( words.size() < 3 ) {
164               WAR << "Suspicious entry in /proc/mounts: " << l << std::endl;
165               continue;
166             }
167
168             //
169             // Filter devices without '/' (proc,shmfs,..)
170             //
171             if ( words[0].find( '/' ) == std::string::npos ) {
172               DBG << "Discard mount point : " << l << std::endl;
173               continue;
174             }
175
176             //
177             // Filter mountpoints not at or below _rootdir
178             //
179             std::string mp = words[1];
180             if ( prfx.size() ) {
181               if ( mp.compare( 0, prfx.size(), prfx ) != 0 ) {
182                 // mountpoint not below rootdir
183                 DBG << "Unwanted mount point : " << l << std::endl;
184                 continue;
185               }
186               // strip prfx
187               mp.erase( 0, prfx.size() );
188               if ( mp.empty() ) {
189                 mp = "/";
190               } else if ( mp[0] != '/' ) {
191                 // mountpoint not below rootdir
192                 DBG << "Unwanted mount point : " << l << std::endl;
193                 continue;
194               }
195             }
196
197             //
198             // Filter cdrom
199             //
200             if ( words[2] == "iso9660" ) {
201               DBG << "Discard cdrom : " << l << std::endl;
202               continue;
203             }
204
205             //
206             // Filter some common unwanted mountpoints
207             //
208             const char * mpunwanted[] = {
209               "/mnt", "/media", "/mounts", "/floppy", "/cdrom",
210               "/suse", "/var/tmp", "/var/adm/mount", "/var/adm/YaST",
211               /*last*/0/*entry*/
212             };
213
214             const char ** nomp = mpunwanted;
215             for ( ; *nomp; ++nomp ) {
216               std::string pre( *nomp );
217               if ( mp.compare( 0, pre.size(), pre ) == 0 // mp has prefix pre
218                    && ( mp.size() == pre.size() || mp[pre.size()] == '/' ) ) {
219                 break;
220               }
221             }
222             if ( *nomp ) {
223               DBG << "Filter mount point : " << l << std::endl;
224               continue;
225             }
226
227             //
228             // Check whether mounted readonly
229             //
230             bool ro = false;
231             std::vector<std::string> flags;
232             str::split( words[3], std::back_inserter(flags), "," );
233
234             for ( unsigned i = 0; i < flags.size(); ++i ) {
235               if ( flags[i] == "ro" ) {
236                 ro = true;
237                 break;
238               }
239             }
240             if ( ro ) {
241               DBG << "Filter ro mount point : " << l << std::endl;
242               continue;
243             }
244
245             //
246             // statvfs (full path!) and get the data
247             //
248             struct statvfs sb;
249             if ( statvfs( words[1].c_str(), &sb ) != 0 ) {
250               WAR << "Unable to statvfs(" << words[1] << "); errno " << errno << std::endl;
251               ret.insert( DiskUsageCounter::MountPoint( mp ) );
252             }
253             else
254             {
255               ret.insert( DiskUsageCounter::MountPoint( mp, sb.f_bsize,
256                 ((long long)sb.f_blocks)*sb.f_bsize/1024,
257                 ((long long)(sb.f_blocks - sb.f_bfree))*sb.f_bsize/1024, 0LL, ro ) );
258             }
259           }
260         }
261     }
262
263     return ret;
264   }
265
266   std::ostream & operator<<( std::ostream & str, const DiskUsageCounter::MountPoint & obj )
267   {
268     str << "dir:[" << obj.dir << "] [ bs: " << obj.block_size
269         << " ts: " << obj.total_size
270         << " us: " << obj.used_size
271         << " (+-: " << (obj.pkg_size-obj.used_size)
272         << ")]" << std::endl;
273     return str;
274   }
275
276 } // namespace zypp
277 ///////////////////////////////////////////////////////////////////