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
35 FetcherJob( const OnMediaLocation &loc )
40 //MIL << location << endl;
45 //MIL << location << " | * " << checkers.size() << endl;
48 OnMediaLocation location;
49 //CompositeFileChecker checkers;
50 list<FileChecker> checkers;
55 typedef shared_ptr<FetcherJob> FetcherJob_Ptr;
57 std::ostream & operator<<( std::ostream & str, const FetcherJob_Ptr & obj )
59 return str << obj->location;
63 ///////////////////////////////////////////////////////////////////
65 // CLASS NAME : Fetcher::Impl
67 /** Fetcher implementation. */
70 friend std::ostream & operator<<( std::ostream & str, const Fetcher::Impl & obj );
77 void enqueue( const OnMediaLocation &resource, const FileChecker &checker );
78 void enqueueDir( const OnMediaLocation &resource, bool recursive, const FileChecker &checker );
79 void enqueueDigested( const OnMediaLocation &resource, const FileChecker &checker );
80 void addCachePath( const Pathname &cache_dir );
82 void start( const Pathname &dest_dir,
83 MediaSetAccess &media,
84 const ProgressData::ReceiverFnc & progress_receiver );
86 /** Offer default Impl. */
87 static shared_ptr<Impl> nullimpl()
89 static shared_ptr<Impl> _nullimpl( new Impl );
94 * tries to provide the file represented by job into dest_dir by
95 * looking at the cache. If success, returns true, and the desired
96 * file should be available on dest_dir
98 bool provideFromCache( const OnMediaLocation &resource, const Pathname &dest_dir );
100 * Validates the job against is checkers, by using the file instance
104 void validate( const OnMediaLocation &resource, const Pathname &dest_dir, const list<FileChecker> &checkers );
107 * scan the directory and adds the individual jobs
109 void addDirJobs( MediaSetAccess &media, const OnMediaLocation &resource,
110 const Pathname &dest_dir, bool recursive );
112 * Provide the resource to \ref dest_dir
114 void provideToDest( MediaSetAccess &media, const OnMediaLocation &resource, const Pathname &dest_dir );
117 friend Impl * rwcowClone<Impl>( const Impl * rhs );
118 /** clone for RWCOW_pointer */
120 { return new Impl( *this ); }
122 std::list<FetcherJob_Ptr> _resources;
123 std::list<Pathname> _caches;
125 ///////////////////////////////////////////////////////////////////
127 void Fetcher::Impl::enqueueDigested( const OnMediaLocation &resource, const FileChecker &checker )
130 job.reset(new FetcherJob(resource));
131 ChecksumFileChecker digest_check(resource.checksum());
132 job->checkers.push_back(digest_check);
134 job->checkers.push_back(checker);
135 _resources.push_back(job);
138 void Fetcher::Impl::enqueueDir( const OnMediaLocation &resource,
140 const FileChecker &checker )
143 job.reset(new FetcherJob(resource));
145 job->checkers.push_back(checker);
146 job->directory = true;
147 job->recursive = recursive;
148 _resources.push_back(job);
151 void Fetcher::Impl::enqueue( const OnMediaLocation &resource, const FileChecker &checker )
154 job.reset(new FetcherJob(resource));
156 job->checkers.push_back(checker);
157 _resources.push_back(job);
160 void Fetcher::Impl::reset()
165 void Fetcher::Impl::addCachePath( const Pathname &cache_dir )
167 PathInfo info(cache_dir);
168 if ( info.isExist() )
172 DBG << "Adding fetcher cache: '" << cache_dir << "'." << endl;
173 _caches.push_back(cache_dir);
177 // don't add bad cache directory, just log the error
178 ERR << "Not adding cache: '" << cache_dir << "'. Not a directory." << endl;
183 ERR << "Not adding cache '" << cache_dir << "'. Path does not exists." << endl;
188 bool Fetcher::Impl::provideFromCache( const OnMediaLocation &resource, const Pathname &dest_dir )
190 Pathname dest_full_path = dest_dir + resource.filename();
192 // first check in the destination directory
193 if ( PathInfo(dest_full_path).isExist() )
195 if ( is_checksum( dest_full_path, resource.checksum() )
196 && (! resource.checksum().empty() ) )
200 MIL << "start fetcher with " << _caches.size() << " cache directories." << endl;
201 for_ ( it_cache, _caches.begin(), _caches.end() )
203 // does the current file exists in the current cache?
204 Pathname cached_file = *it_cache + resource.filename();
205 if ( PathInfo( cached_file ).isExist() )
207 DBG << "File '" << cached_file << "' exist, testing checksum " << resource.checksum() << endl;
208 // check the checksum
209 if ( is_checksum( cached_file, resource.checksum() ) && (! resource.checksum().empty() ) )
212 MIL << "file " << resource.filename() << " found in previous cache. Using cached copy." << endl;
213 // checksum is already checked.
214 // we could later implement double failover and try to download if file copy fails.
215 // replicate the complete path in the target directory
216 if( dest_full_path != cached_file )
218 if ( assert_dir( dest_full_path.dirname() ) != 0 )
219 ZYPP_THROW( Exception("Can't create " + dest_full_path.dirname().asString()));
221 if ( filesystem::hardlink(cached_file, dest_full_path ) != 0 )
223 WAR << "Can't hardlink '" << cached_file << "' to '" << dest_dir << "'. Trying copying." << endl;
224 if ( filesystem::copy(cached_file, dest_full_path ) != 0 )
226 ERR << "Can't copy " << cached_file + " to " + dest_dir << endl;
236 } // iterate over caches
240 void Fetcher::Impl::validate( const OnMediaLocation &resource, const Pathname &dest_dir, const list<FileChecker> &checkers )
242 // no matter where did we got the file, try to validate it:
243 Pathname localfile = dest_dir + resource.filename();
244 // call the checker function
246 MIL << "Checking job [" << localfile << "] (" << checkers.size() << " checkers )" << endl;
247 for ( list<FileChecker>::const_iterator it = checkers.begin();
248 it != checkers.end();
257 ERR << "Invalid checker for '" << localfile << "'" << endl;
262 catch ( const FileCheckException &e )
266 catch ( const Exception &e )
272 ZYPP_THROW(Exception("Unknown error while validating " + resource.filename().asString()));
276 void Fetcher::Impl::addDirJobs( MediaSetAccess &media,
277 const OnMediaLocation &resource,
278 const Pathname &dest_dir, bool recursive )
280 filesystem::DirContent content;
282 media.dirInfo( content, resource.filename(), false /* dots */, resource.medianr());
284 for ( filesystem::DirContent::const_iterator it = content.begin(); it != content.end(); ++it )
286 MIL << (*it).name << endl;
289 filesystem::DirEntry shafile, shasig;
290 shafile.name = "SHA1SUMS";
291 shafile.name = "SHA1SUMS.asc";
292 shasig.type = filesystem::FT_FILE;
293 shasig.type = filesystem::FT_FILE;
295 // look for the SHA1SUMS file
296 if ( find(content.begin(), content.end(), shafile) != content.end() )
298 MIL << "found checksums file: " << shafile.name << endl;
300 provideToDest(media, resource.filename() + shafile.name, dest_dir);
301 // look for the SHA1SUMS.asc signature
302 if ( find(content.begin(), content.end(), shasig) != content.end() )
304 provideToDest(media, resource.filename() + shasig.name, dest_dir);
309 for ( filesystem::DirContent::const_iterator it = content.begin(); it != content.end(); ++it )
311 Pathname filename = resource.filename() + it->name;
315 case filesystem::FT_NOT_AVAIL: // old directory.yast contains no typeinfo at all
316 case filesystem::FT_FILE:
317 enqueue(OnMediaLocation().setFilename(filename), FileChecker());
319 MIL << filename << endl;
321 case filesystem::FT_DIR: // newer directory.yast contain at least directory info
324 addDirJobs(media, filename, dest_dir, recursive);
331 // don't provide devices, sockets, etc.
337 void Fetcher::Impl::provideToDest( MediaSetAccess &media, const OnMediaLocation &resource, const Pathname &dest_dir )
339 bool got_from_cache = false;
341 // start look in cache
342 got_from_cache = provideFromCache(resource, dest_dir);
344 if ( ! got_from_cache )
346 MIL << "Not found in cache, downloading" << endl;
348 // try to get the file from the net
351 Pathname tmp_file = media.provideFile(resource);
352 Pathname dest_full_path = dest_dir + resource.filename();
353 if ( assert_dir( dest_full_path.dirname() ) != 0 )
354 ZYPP_THROW( Exception("Can't create " + dest_full_path.dirname().asString()));
355 if ( filesystem::copy(tmp_file, dest_full_path ) != 0 )
357 ZYPP_THROW( Exception("Can't copy " + tmp_file.asString() + " to " + dest_dir.asString()));
360 media.releaseFile(resource); //not needed anymore, only eat space
362 catch (Exception & excpt_r)
364 ZYPP_CAUGHT(excpt_r);
365 excpt_r.remember("Can't provide " + resource.filename().asString() + " : " + excpt_r.msg());
366 ZYPP_RETHROW(excpt_r);
371 // We got the file from cache
372 // continue with next file
377 void Fetcher::Impl::start( const Pathname &dest_dir,
378 MediaSetAccess &media,
379 const ProgressData::ReceiverFnc & progress_receiver )
381 ProgressData progress(_resources.size());
382 progress.sendTo(progress_receiver);
384 for ( list<FetcherJob_Ptr>::const_iterator it_res = _resources.begin(); it_res != _resources.end(); ++it_res )
387 if ( (*it_res)->directory )
389 const OnMediaLocation location((*it_res)->location);
390 addDirJobs(media, location, dest_dir, true);
394 provideToDest(media, (*it_res)->location, dest_dir);
396 // validate job, this throws if not valid
397 validate((*it_res)->location, dest_dir, (*it_res)->checkers);
399 if ( ! progress.incr() )
400 ZYPP_THROW(AbortRequestException());
404 /** \relates Fetcher::Impl Stream output */
405 inline std::ostream & operator<<( std::ostream & str, const Fetcher::Impl & obj )
407 for ( list<FetcherJob_Ptr>::const_iterator it_res = obj._resources.begin(); it_res != obj._resources.end(); ++it_res )
414 ///////////////////////////////////////////////////////////////////
416 // CLASS NAME : Fetcher
418 ///////////////////////////////////////////////////////////////////
420 ///////////////////////////////////////////////////////////////////
422 // METHOD NAME : Fetcher::Fetcher
423 // METHOD TYPE : Ctor
426 : _pimpl( new Impl() )
429 ///////////////////////////////////////////////////////////////////
431 // METHOD NAME : Fetcher::~Fetcher
432 // METHOD TYPE : Dtor
437 void Fetcher::enqueueDigested( const OnMediaLocation &resource, const FileChecker &checker )
439 _pimpl->enqueueDigested(resource, checker);
442 void Fetcher::enqueueDir( const OnMediaLocation &resource,
444 const FileChecker &checker )
446 _pimpl->enqueueDir(resource, recursive, checker);
449 void Fetcher::enqueue( const OnMediaLocation &resource, const FileChecker &checker )
451 _pimpl->enqueue(resource, checker);
454 void Fetcher::addCachePath( const Pathname &cache_dir )
456 _pimpl->addCachePath(cache_dir);
459 void Fetcher::reset()
464 void Fetcher::start( const Pathname &dest_dir,
465 MediaSetAccess &media,
466 const ProgressData::ReceiverFnc & progress_receiver )
468 _pimpl->start(dest_dir, media, progress_receiver);
472 /******************************************************************
474 ** FUNCTION NAME : operator<<
475 ** FUNCTION TYPE : std::ostream &
477 std::ostream & operator<<( std::ostream & str, const Fetcher & obj )
479 return str << *obj._pimpl;
482 /////////////////////////////////////////////////////////////////
484 ///////////////////////////////////////////////////////////////////