1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/Fetcher.cc
15 #include "zypp/base/Easy.h"
16 #include "zypp/base/Logger.h"
17 #include "zypp/base/PtrTypes.h"
18 #include "zypp/base/DefaultIntegral.h"
19 #include "zypp/Fetcher.h"
20 #include "zypp/base/UserRequestException.h"
24 ///////////////////////////////////////////////////////////////////
26 { /////////////////////////////////////////////////////////////////
29 * Class to encapsulate the \ref OnMediaLocation object
30 * and the \ref FileChecker together
34 FetcherJob( const OnMediaLocation &loc )
37 //MIL << location << endl;
42 //MIL << location << " | * " << checkers.size() << endl;
45 OnMediaLocation location;
46 //CompositeFileChecker checkers;
47 list<FileChecker> checkers;
50 typedef shared_ptr<FetcherJob> FetcherJob_Ptr;
52 ///////////////////////////////////////////////////////////////////
54 // CLASS NAME : Fetcher::Impl
56 /** Fetcher implementation. */
66 void enqueue( const OnMediaLocation &resource, const FileChecker &checker );
67 void enqueueDigested( const OnMediaLocation &resource, const FileChecker &checker );
68 void addCachePath( const Pathname &cache_dir );
70 void start( const Pathname &dest_dir,
71 MediaSetAccess &media,
72 const ProgressData::ReceiverFnc & progress_receiver );
74 /** Offer default Impl. */
75 static shared_ptr<Impl> nullimpl()
77 static shared_ptr<Impl> _nullimpl( new Impl );
82 friend Impl * rwcowClone<Impl>( const Impl * rhs );
83 /** clone for RWCOW_pointer */
85 { return new Impl( *this ); }
87 std::list<FetcherJob_Ptr> _resources;
88 std::list<Pathname> _caches;
90 ///////////////////////////////////////////////////////////////////
92 void Fetcher::Impl::enqueueDigested( const OnMediaLocation &resource, const FileChecker &checker )
95 job.reset(new FetcherJob(resource));
96 ChecksumFileChecker digest_check(resource.checksum());
97 job->checkers.push_back(digest_check);
99 job->checkers.push_back(checker);
100 _resources.push_back(job);
103 void Fetcher::Impl::enqueue( const OnMediaLocation &resource, const FileChecker &checker )
106 job.reset(new FetcherJob(resource));
108 job->checkers.push_back(checker);
109 _resources.push_back(job);
112 void Fetcher::Impl::reset()
117 void Fetcher::Impl::addCachePath( const Pathname &cache_dir )
119 PathInfo info(cache_dir);
120 if ( info.isExist() )
124 DBG << "Adding fetcher cache: '" << cache_dir << "'." << endl;
125 _caches.push_back(cache_dir);
129 // don't add bad cache directory, just log the error
130 ERR << "Not adding cache: '" << cache_dir << "'. Not a directory." << endl;
135 ERR << "Not adding cache '" << cache_dir << "'. Path does not exists." << endl;
140 void Fetcher::Impl::start( const Pathname &dest_dir,
141 MediaSetAccess &media,
142 const ProgressData::ReceiverFnc & progress_receiver )
144 ProgressData progress(_resources.size());
145 progress.sendTo(progress_receiver);
147 for ( list<FetcherJob_Ptr>::const_iterator it_res = _resources.begin(); it_res != _resources.end(); ++it_res )
149 bool got_from_cache = false;
151 MIL << "start fetcher with " << _caches.size() << " cache directories." << endl;
152 for_ ( it_cache, _caches.begin(), _caches.end() )
154 // does the current file exists in the current cache?
155 Pathname cached_file = *it_cache + (*it_res)->location.filename();
157 if ( PathInfo( cached_file ).isExist() )
159 DBG << "File '" << cached_file << "' exist, testing checksum " << (*it_res)->location.checksum() << endl;
161 // check the checksum
162 if ( is_checksum( cached_file, (*it_res)->location.checksum() ) && (! (*it_res)->location.checksum().empty() ) )
165 MIL << "file " << (*it_res)->location.filename() << " found in previous cache. Using cached copy." << endl;
166 // checksum is already checked.
167 // we could later implement double failover and try to download if file copy fails.
169 // replicate the complete path in the target directory
170 Pathname dest_full_path = dest_dir + (*it_res)->location.filename();
172 if( dest_full_path != cached_file )
174 if ( assert_dir( dest_full_path.dirname() ) != 0 )
175 ZYPP_THROW( Exception("Can't create " + dest_full_path.dirname().asString()));
177 if ( filesystem::hardlink(cached_file, dest_full_path ) != 0 )
179 WAR << "Can't hardlink '" << cached_file << "' to '" << dest_dir << "'. Trying copying." << endl;
180 if ( filesystem::copy(cached_file, dest_full_path ) != 0 )
182 ERR << "Can't copy " << cached_file + " to " + dest_dir << endl;
189 got_from_cache = true;
195 if ( ! got_from_cache )
197 MIL << "Not found in cache, downloading" << endl;
199 // try to get the file from the net
202 Pathname tmp_file = media.provideFile((*it_res)->location);
203 Pathname dest_full_path = dest_dir + (*it_res)->location.filename();
204 if ( assert_dir( dest_full_path.dirname() ) != 0 )
205 ZYPP_THROW( Exception("Can't create " + dest_full_path.dirname().asString()));
206 if ( filesystem::copy(tmp_file, dest_full_path ) != 0 )
208 ZYPP_THROW( Exception("Can't copy " + tmp_file.asString() + " to " + dest_dir.asString()));
211 media.releaseFile((*it_res)->location); //not needed anymore, only eat space
213 catch (Exception & excpt_r)
215 ZYPP_CAUGHT(excpt_r);
216 excpt_r.remember("Can't provide " + (*it_res)->location.filename().asString() + " : " + excpt_r.msg());
217 ZYPP_RETHROW(excpt_r);
222 // We got the file from cache
223 // continue with next file
227 // no matter where did we got the file, try to validate it:
228 Pathname localfile = dest_dir + (*it_res)->location.filename();
229 // call the checker function
231 MIL << "Checking job [" << localfile << "] (" << (*it_res)->checkers.size() << " checkers )" << endl;
232 for ( list<FileChecker>::const_iterator it = (*it_res)->checkers.begin();
233 it != (*it_res)->checkers.end();
242 ERR << "Invalid checker for '" << localfile << "'" << endl;
247 catch ( const FileCheckException &e )
251 catch ( const Exception &e )
257 ZYPP_THROW(Exception("Unknown error while validating " + (*it_res)->location.filename().asString()));
260 if ( ! progress.incr() )
261 ZYPP_THROW(AbortRequestException());
265 /** \relates Fetcher::Impl Stream output */
266 inline std::ostream & operator<<( std::ostream & str, const Fetcher::Impl & obj )
268 return str << "Fetcher::Impl";
271 ///////////////////////////////////////////////////////////////////
273 // CLASS NAME : Fetcher
275 ///////////////////////////////////////////////////////////////////
277 ///////////////////////////////////////////////////////////////////
279 // METHOD NAME : Fetcher::Fetcher
280 // METHOD TYPE : Ctor
283 : _pimpl( new Impl() )
286 ///////////////////////////////////////////////////////////////////
288 // METHOD NAME : Fetcher::~Fetcher
289 // METHOD TYPE : Dtor
294 void Fetcher::enqueueDigested( const OnMediaLocation &resource, const FileChecker &checker )
296 _pimpl->enqueueDigested(resource, checker);
299 void Fetcher::enqueue( const OnMediaLocation &resource, const FileChecker &checker )
301 _pimpl->enqueue(resource, checker);
304 void Fetcher::addCachePath( const Pathname &cache_dir )
306 _pimpl->addCachePath(cache_dir);
309 void Fetcher::reset()
314 void Fetcher::start( const Pathname &dest_dir,
315 MediaSetAccess &media,
316 const ProgressData::ReceiverFnc & progress_receiver )
318 _pimpl->start(dest_dir, media, progress_receiver);
322 /******************************************************************
324 ** FUNCTION NAME : operator<<
325 ** FUNCTION TYPE : std::ostream &
327 std::ostream & operator<<( std::ostream & str, const Fetcher & obj )
329 return str << *obj._pimpl;
332 /////////////////////////////////////////////////////////////////
334 ///////////////////////////////////////////////////////////////////