Fix CommitPackageCache to be independent of outer package sequence repesentation.
[platform/upstream/libzypp.git] / zypp / target / CommitPackageCacheReadAhead.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/target/CommitPackageCacheReadAhead.cc
10  *
11 */
12 #include <iostream>
13
14 #include "zypp/base/Logger.h"
15 #include "zypp/base/Exception.h"
16 #include "zypp/PathInfo.h"
17 #include "zypp/RepoInfo.h"
18 #include "zypp/target/CommitPackageCacheReadAhead.h"
19
20 using std::endl;
21
22 ///////////////////////////////////////////////////////////////////
23 namespace zypp
24 { /////////////////////////////////////////////////////////////////
25   ///////////////////////////////////////////////////////////////////
26   namespace target
27   { /////////////////////////////////////////////////////////////////
28
29     ///////////////////////////////////////////////////////////////////
30     //
31     //  CLASS NAME : IMediaKey
32     //
33     ///////////////////////////////////////////////////////////////////
34
35     std::ostream & operator<<( std::ostream & str, const IMediaKey & obj )
36     {
37       return str << "[S" << obj._repo.id() << ":" << obj._mediaNr << "]"
38                  << " " << obj._repo.info().alias();
39     }
40
41     ///////////////////////////////////////////////////////////////////
42     //
43     //  CLASS NAME : CommitPackageCacheReadAhead
44     //
45     ///////////////////////////////////////////////////////////////////
46
47     ///////////////////////////////////////////////////////////////////
48     //
49     //  METHOD NAME : CommitPackageCacheReadAhead::CommitPackageCacheReadAhead
50     //  METHOD TYPE : Ctor
51     //
52     CommitPackageCacheReadAhead::CommitPackageCacheReadAhead( const Pathname &        rootDir_r,
53                                                               const PackageProvider & packageProvider_r )
54     : CommitPackageCache::Impl( packageProvider_r )
55     , _rootDir( rootDir_r )
56     {}
57
58     ///////////////////////////////////////////////////////////////////
59     //
60     //  METHOD NAME : CommitPackageCacheReadAhead::onInteractiveMedia
61     //  METHOD TYPE : bool
62     //
63     bool CommitPackageCacheReadAhead::onInteractiveMedia( const PoolItem & pi ) const
64     {
65       if ( pi->mediaNr() == 0 ) // no media access at all
66         return false;
67       if ( pi->repoInfo().baseUrlsEmpty() )
68         return false; // no Url - should actually not happen
69       std::string scheme( pi->repoInfo().baseUrlsBegin()->getScheme() );
70       return ( scheme == "dvd" || scheme == "cd" );
71     }
72
73     ///////////////////////////////////////////////////////////////////
74     //
75     //  METHOD NAME : CommitPackageCacheReadAhead::cacheLastInteractive
76     //  METHOD TYPE : void
77     //
78     void CommitPackageCacheReadAhead::cacheLastInteractive( const PoolItem & citem_r )
79     {
80       // Fill cache errors are never proagated.
81       try
82         {
83           doCacheLastInteractive( citem_r );
84         }
85       catch ( const Exception & excpt_r )
86         {
87           ZYPP_CAUGHT( excpt_r );
88           WAR << "Failed to cache " << _lastInteractive << endl;
89         }
90     }
91
92     ///////////////////////////////////////////////////////////////////
93     //
94     //  METHOD NAME : CommitPackageCacheReadAhead::doCacheLastInteractive
95     //  METHOD TYPE : void
96     //
97     void CommitPackageCacheReadAhead::doCacheLastInteractive( const PoolItem & citem_r )
98     {
99       CacheMap  addToCache;
100       ByteCount addSize;
101
102       // Collect all remaining packages to install from
103       // _lastInteractive media. (just the PoolItem data)
104       for_( it,_commitList.begin(), _commitList.end() )
105         {
106           PoolItem pi( *it );
107           if ( IMediaKey( pi ) == _lastInteractive
108                && isKind<Package>(pi.resolvable())
109                && pi.status().isToBeInstalled() )
110             {
111               if ( _cacheMap.find( pi ) == _cacheMap.end() )
112                 {
113                   addToCache[pi];
114                   addSize += pi->downloadSize();
115                 }
116             }
117         }
118
119       if ( addToCache.empty() )
120         return;
121       MIL << "could cache " << _lastInteractive << ": " << addToCache.size() << " items: " <<  addSize << endl;
122
123       // Check whether we can afford caching the items. We cache them all or
124       // nothing. It does not make sense to cache only some packages, if a
125       // CD change can't be avoided.
126       if ( ! _cacheDir )
127         {
128           _cacheDir.reset( new filesystem::TmpDir( _rootDir, "commitCache." ) );
129           PathInfo pi( _cacheDir->path() );
130           if ( ! pi.isDir() )
131             {
132               ERR << "Can not initialize cache dir " << pi << endl;
133               return;
134             }
135         }
136
137       // In case someone removes cacheDir behind our back, df will be
138       // -1, so we won't cache.
139       ByteCount df( filesystem::df( _cacheDir->path() ) );
140       MIL << "available disk space in " << _cacheDir->path() << ": " << df << endl;
141
142       if ( df / 10 < addSize )
143         {
144           WAR << "cache would require more than 10% of the available " << df << " disk space " << endl;
145           WAR << "not caching " << _lastInteractive << endl;
146           return;
147         }
148
149       // Get all files to cache from the Source and copy them to
150       // the cache.
151       // NOTE: All files copied to the cache directory are stored in addToCache,
152       // which is a local variable. If we throw on error, addToCache will be
153       // deleted and all the ManagedFiles stored so far will delete themself.
154       // THIS IS EXACTLY WHAT WE WANT.
155       for ( CacheMap::iterator it = addToCache.begin(); it != addToCache.end(); ++it )
156         {
157           // let the source provide the file
158           ManagedFile fromSource( sourceProvidePackage( it->first ) );
159
160           // copy it to the cachedir
161           std::string destName( str::form( "S%p_%u_%s",
162                                            it->first->repository().id(),
163                                            it->first->mediaNr(),
164                                            fromSource.value().basename().c_str() ) );
165
166           ManagedFile fileInCache( _cacheDir->path() / destName,
167                                    filesystem::unlink );
168
169           if ( filesystem::copy( fromSource.value(), fileInCache ) != 0 )
170             {
171               // copy to cache failed.
172               ERR << "Copy to cache failed on " << fromSource.value() << endl;
173               ZYPP_THROW( Exception("Copy to cache failed.") );
174             }
175
176           // remember the cached file.
177           it->second = fileInCache;
178         }
179
180       // Here: All files are sucessfully copied to the cache.
181       // Update the real cache map.
182       _cacheMap.insert( addToCache.begin(), addToCache.end() );
183       return;
184     }
185
186     ///////////////////////////////////////////////////////////////////
187     //
188     //  METHOD NAME : CommitPackageCacheReadAhead::get
189     //  METHOD TYPE : ManagedFile
190     //
191     ManagedFile CommitPackageCacheReadAhead::get( const PoolItem & citem_r )
192     {
193       // Non CD/DVD media provide their packages without cache.
194       if ( ! onInteractiveMedia( citem_r ) )
195         {
196           return sourceProvidePackage( citem_r );
197         }
198
199       // Check whether it's cached.
200       CacheMap::iterator it = _cacheMap.find( citem_r );
201       if ( it != _cacheMap.end() )
202         {
203           // ManagedFile delivered to the application is removed
204           // from the cache. So if the application releases the
205           // file, it actually gets deleted from disk.
206           ManagedFile cacheHit( it->second );
207           _cacheMap.erase( it );
208
209           // safety check whether the file still exists
210           PathInfo pi( cacheHit.value() );
211           if ( pi.isFile() )
212             {
213               MIL << "Cache package provide " << cacheHit << endl;
214               return cacheHit;
215             }
216
217           WAR << "Cached file vanished: " << pi << endl;
218         }
219
220       // HERE: It's not in the cache.
221       // In case we have to change the media to provide the requested
222       // file, try to cache files from the current media, that are
223       // required later.
224       IMediaKey current( citem_r );
225       if ( current != _lastInteractive )
226         {
227           if ( _lastInteractive != IMediaKey() )
228             {
229               cacheLastInteractive( citem_r );
230             }
231
232           DBG << "Interactive change [" << ++_dbgChanges << "] from " << _lastInteractive
233           << " to " << current << endl;
234           _lastInteractive = current;
235         }
236
237       // Provide and return the file from media.
238       return sourceProvidePackage( citem_r );
239     }
240
241
242     /////////////////////////////////////////////////////////////////
243   } // namespace target
244   ///////////////////////////////////////////////////////////////////
245   /////////////////////////////////////////////////////////////////
246 } // namespace zypp
247 ///////////////////////////////////////////////////////////////////