libzypp  10.5.0
CommitPackageCacheReadAhead.cc
Go to the documentation of this file.
00001 /*---------------------------------------------------------------------\
00002 |                          ____ _   __ __ ___                          |
00003 |                         |__  / \ / / . \ . \                         |
00004 |                           / / \ V /|  _/  _/                         |
00005 |                          / /__ | | | | | |                           |
00006 |                         /_____||_| |_| |_|                           |
00007 |                                                                      |
00008 \---------------------------------------------------------------------*/
00012 #include <iostream>
00013 
00014 #include "zypp/base/Logger.h"
00015 #include "zypp/base/Exception.h"
00016 #include "zypp/PathInfo.h"
00017 #include "zypp/RepoInfo.h"
00018 #include "zypp/target/CommitPackageCacheReadAhead.h"
00019 
00020 using std::endl;
00021 
00023 namespace zypp
00024 { 
00025 
00026   namespace target
00027   { 
00028 
00030     //
00031     //  CLASS NAME : IMediaKey
00032     //
00034 
00035     std::ostream & operator<<( std::ostream & str, const IMediaKey & obj )
00036     {
00037       return str << "[S" << obj._repo.id() << ":" << obj._mediaNr << "]"
00038                  << " " << obj._repo.info().alias();
00039     }
00040 
00042     //
00043     //  CLASS NAME : CommitPackageCacheReadAhead
00044     //
00046 
00048     //
00049     //  METHOD NAME : CommitPackageCacheReadAhead::CommitPackageCacheReadAhead
00050     //  METHOD TYPE : Ctor
00051     //
00052     CommitPackageCacheReadAhead::CommitPackageCacheReadAhead( const Pathname &        rootDir_r,
00053                                                               const PackageProvider & packageProvider_r )
00054     : CommitPackageCache::Impl( packageProvider_r )
00055     , _rootDir( rootDir_r )
00056     {}
00057 
00059     //
00060     //  METHOD NAME : CommitPackageCacheReadAhead::onInteractiveMedia
00061     //  METHOD TYPE : bool
00062     //
00063     bool CommitPackageCacheReadAhead::onInteractiveMedia( const PoolItem & pi ) const
00064     {
00065       if ( pi->mediaNr() == 0 ) // no media access at all
00066         return false;
00067       if ( pi->repoInfo().baseUrlsEmpty() )
00068         return false; // no Url - should actually not happen
00069       std::string scheme( pi->repoInfo().baseUrlsBegin()->getScheme() );
00070       return ( scheme == "dvd" || scheme == "cd" );
00071     }
00072 
00074     //
00075     //  METHOD NAME : CommitPackageCacheReadAhead::cacheLastInteractive
00076     //  METHOD TYPE : void
00077     //
00078     void CommitPackageCacheReadAhead::cacheLastInteractive( const PoolItem & citem_r )
00079     {
00080       // Fill cache errors are never proagated.
00081       try
00082         {
00083           doCacheLastInteractive( citem_r );
00084         }
00085       catch ( const Exception & excpt_r )
00086         {
00087           ZYPP_CAUGHT( excpt_r );
00088           WAR << "Failed to cache " << _lastInteractive << endl;
00089         }
00090     }
00091 
00093     //
00094     //  METHOD NAME : CommitPackageCacheReadAhead::doCacheLastInteractive
00095     //  METHOD TYPE : void
00096     //
00097     void CommitPackageCacheReadAhead::doCacheLastInteractive( const PoolItem & citem_r )
00098     {
00099       CacheMap  addToCache;
00100       ByteCount addSize;
00101 
00102       // Collect all remaining packages to install from
00103       // _lastInteractive media. (just the PoolItem data)
00104       for_( it,_commitList.begin(), _commitList.end() )
00105         {
00106           PoolItem pi( *it );
00107           if ( IMediaKey( pi ) == _lastInteractive
00108                && isKind<Package>(pi.resolvable())
00109                && pi.status().isToBeInstalled() )
00110             {
00111               if ( _cacheMap.find( pi ) == _cacheMap.end() )
00112                 {
00113                   addToCache[pi];
00114                   addSize += pi->downloadSize();
00115                 }
00116             }
00117         }
00118 
00119       if ( addToCache.empty() )
00120         return;
00121       MIL << "could cache " << _lastInteractive << ": " << addToCache.size() << " items: " <<  addSize << endl;
00122 
00123       // Check whether we can afford caching the items. We cache them all or
00124       // nothing. It does not make sense to cache only some packages, if a
00125       // CD change can't be avoided.
00126       if ( ! _cacheDir )
00127         {
00128           _cacheDir.reset( new filesystem::TmpDir( _rootDir, "commitCache." ) );
00129           PathInfo pi( _cacheDir->path() );
00130           if ( ! pi.isDir() )
00131             {
00132               ERR << "Can not initialize cache dir " << pi << endl;
00133               return;
00134             }
00135         }
00136 
00137       // In case someone removes cacheDir behind our back, df will be
00138       // -1, so we won't cache.
00139       ByteCount df( filesystem::df( _cacheDir->path() ) );
00140       MIL << "available disk space in " << _cacheDir->path() << ": " << df << endl;
00141 
00142       if ( df / 10 < addSize )
00143         {
00144           WAR << "cache would require more than 10% of the available " << df << " disk space " << endl;
00145           WAR << "not caching " << _lastInteractive << endl;
00146           return;
00147         }
00148 
00149       // Get all files to cache from the Source and copy them to
00150       // the cache.
00151       // NOTE: All files copied to the cache directory are stored in addToCache,
00152       // which is a local variable. If we throw on error, addToCache will be
00153       // deleted and all the ManagedFiles stored so far will delete themself.
00154       // THIS IS EXACTLY WHAT WE WANT.
00155       for ( CacheMap::iterator it = addToCache.begin(); it != addToCache.end(); ++it )
00156         {
00157           // let the source provide the file
00158           ManagedFile fromSource( sourceProvidePackage( it->first ) );
00159 
00160           // copy it to the cachedir
00161           std::string destName( str::form( "S%p_%u_%s",
00162                                            it->first->repository().id(),
00163                                            it->first->mediaNr(),
00164                                            fromSource.value().basename().c_str() ) );
00165 
00166           ManagedFile fileInCache( _cacheDir->path() / destName,
00167                                    filesystem::unlink );
00168 
00169           if ( filesystem::copy( fromSource.value(), fileInCache ) != 0 )
00170             {
00171               // copy to cache failed.
00172               ERR << "Copy to cache failed on " << fromSource.value() << endl;
00173               ZYPP_THROW( Exception("Copy to cache failed.") );
00174             }
00175 
00176           // remember the cached file.
00177           it->second = fileInCache;
00178         }
00179 
00180       // Here: All files are sucessfully copied to the cache.
00181       // Update the real cache map.
00182       _cacheMap.insert( addToCache.begin(), addToCache.end() );
00183       return;
00184     }
00185 
00187     //
00188     //  METHOD NAME : CommitPackageCacheReadAhead::get
00189     //  METHOD TYPE : ManagedFile
00190     //
00191     ManagedFile CommitPackageCacheReadAhead::get( const PoolItem & citem_r )
00192     {
00193       // Non CD/DVD media provide their packages without cache.
00194       if ( ! onInteractiveMedia( citem_r ) )
00195         {
00196           return sourceProvidePackage( citem_r );
00197         }
00198 
00199       // Check whether it's cached.
00200       CacheMap::iterator it = _cacheMap.find( citem_r );
00201       if ( it != _cacheMap.end() )
00202         {
00203           // ManagedFile delivered to the application is removed
00204           // from the cache. So if the application releases the
00205           // file, it actually gets deleted from disk.
00206           ManagedFile cacheHit( it->second );
00207           _cacheMap.erase( it );
00208 
00209           // safety check whether the file still exists
00210           PathInfo pi( cacheHit.value() );
00211           if ( pi.isFile() )
00212             {
00213               MIL << "Cache package provide " << cacheHit << endl;
00214               return cacheHit;
00215             }
00216 
00217           WAR << "Cached file vanished: " << pi << endl;
00218         }
00219 
00220       // HERE: It's not in the cache.
00221       // In case we have to change the media to provide the requested
00222       // file, try to cache files from the current media, that are
00223       // required later.
00224       IMediaKey current( citem_r );
00225       if ( current != _lastInteractive )
00226         {
00227           if ( _lastInteractive != IMediaKey() )
00228             {
00229               cacheLastInteractive( citem_r );
00230             }
00231 
00232           DBG << "Interactive change [" << ++_dbgChanges << "] from " << _lastInteractive
00233           << " to " << current << endl;
00234           _lastInteractive = current;
00235         }
00236 
00237       // Provide and return the file from media.
00238       return sourceProvidePackage( citem_r );
00239     }
00240 
00241 
00243   } // namespace target
00246 } // namespace zypp