1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/Fetcher.cc
17 #include "zypp/base/Easy.h"
18 #include "zypp/base/Logger.h"
19 #include "zypp/base/PtrTypes.h"
20 #include "zypp/base/DefaultIntegral.h"
21 #include "zypp/base/String.h"
22 #include "zypp/Fetcher.h"
23 #include "zypp/CheckSum.h"
24 #include "zypp/base/UserRequestException.h"
28 ///////////////////////////////////////////////////////////////////
30 { /////////////////////////////////////////////////////////////////
33 * Class to encapsulate the \ref OnMediaLocation object
34 * and the \ref FileChecker together
39 FetcherJob( const OnMediaLocation &loc )
44 //MIL << location << endl;
49 //MIL << location << " | * " << checkers.size() << endl;
52 OnMediaLocation location;
53 //CompositeFileChecker checkers;
54 list<FileChecker> checkers;
59 typedef shared_ptr<FetcherJob> FetcherJob_Ptr;
61 std::ostream & operator<<( std::ostream & str, const FetcherJob_Ptr & obj )
63 return str << obj->location;
67 ///////////////////////////////////////////////////////////////////
69 // CLASS NAME : Fetcher::Impl
71 /** Fetcher implementation. */
74 friend std::ostream & operator<<( std::ostream & str, const Fetcher::Impl & obj );
81 void enqueue( const OnMediaLocation &resource, const FileChecker &checker = FileChecker() );
82 void enqueueDir( const OnMediaLocation &resource, bool recursive, const FileChecker &checker = FileChecker() );
83 void enqueueDigested( const OnMediaLocation &resource, const FileChecker &checker = FileChecker() );
84 void addCachePath( const Pathname &cache_dir );
86 void start( const Pathname &dest_dir,
87 MediaSetAccess &media,
88 const ProgressData::ReceiverFnc & progress_receiver );
90 /** Offer default Impl. */
91 static shared_ptr<Impl> nullimpl()
93 static shared_ptr<Impl> _nullimpl( new Impl );
98 * tries to provide the file represented by job into dest_dir by
99 * looking at the cache. If success, returns true, and the desired
100 * file should be available on dest_dir
102 bool provideFromCache( const OnMediaLocation &resource, const Pathname &dest_dir );
104 * Validates the job against is checkers, by using the file instance
108 void validate( const OnMediaLocation &resource, const Pathname &dest_dir, const list<FileChecker> &checkers );
111 * scan the directory and adds the individual jobs
113 void addDirJobs( MediaSetAccess &media, const OnMediaLocation &resource,
114 const Pathname &dest_dir, bool recursive );
116 * Provide the resource to \ref dest_dir
118 void provideToDest( MediaSetAccess &media, const OnMediaLocation &resource, const Pathname &dest_dir );
121 friend Impl * rwcowClone<Impl>( const Impl * rhs );
122 /** clone for RWCOW_pointer */
124 { return new Impl( *this ); }
126 std::list<FetcherJob_Ptr> _resources;
127 std::list<Pathname> _caches;
129 ///////////////////////////////////////////////////////////////////
131 void Fetcher::Impl::enqueueDigested( const OnMediaLocation &resource, const FileChecker &checker )
134 job.reset(new FetcherJob(resource));
135 ChecksumFileChecker digest_check(resource.checksum());
136 job->checkers.push_back(digest_check);
138 job->checkers.push_back(checker);
139 _resources.push_back(job);
142 void Fetcher::Impl::enqueueDir( const OnMediaLocation &resource,
144 const FileChecker &checker )
147 job.reset(new FetcherJob(resource));
149 job->checkers.push_back(checker);
150 job->directory = true;
151 job->recursive = recursive;
152 _resources.push_back(job);
155 void Fetcher::Impl::enqueue( const OnMediaLocation &resource, const FileChecker &checker )
158 job.reset(new FetcherJob(resource));
160 job->checkers.push_back(checker);
161 _resources.push_back(job);
164 void Fetcher::Impl::reset()
169 void Fetcher::Impl::addCachePath( const Pathname &cache_dir )
171 PathInfo info(cache_dir);
172 if ( info.isExist() )
176 DBG << "Adding fetcher cache: '" << cache_dir << "'." << endl;
177 _caches.push_back(cache_dir);
181 // don't add bad cache directory, just log the error
182 ERR << "Not adding cache: '" << cache_dir << "'. Not a directory." << endl;
187 ERR << "Not adding cache '" << cache_dir << "'. Path does not exists." << endl;
192 bool Fetcher::Impl::provideFromCache( const OnMediaLocation &resource, const Pathname &dest_dir )
194 Pathname dest_full_path = dest_dir + resource.filename();
196 // first check in the destination directory
197 if ( PathInfo(dest_full_path).isExist() )
199 if ( is_checksum( dest_full_path, resource.checksum() )
200 && (! resource.checksum().empty() ) )
204 MIL << "start fetcher with " << _caches.size() << " cache directories." << endl;
205 for_ ( it_cache, _caches.begin(), _caches.end() )
207 // does the current file exists in the current cache?
208 Pathname cached_file = *it_cache + resource.filename();
209 if ( PathInfo( cached_file ).isExist() )
211 DBG << "File '" << cached_file << "' exist, testing checksum " << resource.checksum() << endl;
212 // check the checksum
213 if ( is_checksum( cached_file, resource.checksum() ) && (! resource.checksum().empty() ) )
216 MIL << "file " << resource.filename() << " found in previous cache. Using cached copy." << endl;
217 // checksum is already checked.
218 // we could later implement double failover and try to download if file copy fails.
219 // replicate the complete path in the target directory
220 if( dest_full_path != cached_file )
222 if ( assert_dir( dest_full_path.dirname() ) != 0 )
223 ZYPP_THROW( Exception("Can't create " + dest_full_path.dirname().asString()));
225 if ( filesystem::hardlink(cached_file, dest_full_path ) != 0 )
227 WAR << "Can't hardlink '" << cached_file << "' to '" << dest_dir << "'. Trying copying." << endl;
228 if ( filesystem::copy(cached_file, dest_full_path ) != 0 )
230 ERR << "Can't copy " << cached_file + " to " + dest_dir << endl;
240 } // iterate over caches
244 void Fetcher::Impl::validate( const OnMediaLocation &resource, const Pathname &dest_dir, const list<FileChecker> &checkers )
246 // no matter where did we got the file, try to validate it:
247 Pathname localfile = dest_dir + resource.filename();
248 // call the checker function
250 MIL << "Checking job [" << localfile << "] (" << checkers.size() << " checkers )" << endl;
251 for ( list<FileChecker>::const_iterator it = checkers.begin();
252 it != checkers.end();
261 ERR << "Invalid checker for '" << localfile << "'" << endl;
266 catch ( const FileCheckException &e )
270 catch ( const Exception &e )
276 ZYPP_THROW(Exception("Unknown error while validating " + resource.filename().asString()));
280 void Fetcher::Impl::addDirJobs( MediaSetAccess &media,
281 const OnMediaLocation &resource,
282 const Pathname &dest_dir, bool recursive )
284 // first get the content of the directory so we can add
285 // individual transfer jobs
286 MIL << "Adding directory " << resource.filename() << endl;
287 filesystem::DirContent content;
288 media.dirInfo( content, resource.filename(), false /* dots */, resource.medianr());
290 filesystem::DirEntry shafile, shasig, shakey;
291 shafile.name = "SHA1SUMS"; shafile.type = filesystem::FT_FILE;
292 shasig.name = "SHA1SUMS.asc"; shasig.type = filesystem::FT_FILE;
293 shakey.name = "SHA1SUMS.key"; shakey.type = filesystem::FT_FILE;
295 // create a new fetcher with a different state to transfer the
296 // file containing checksums and its signature
298 // signature checker for SHA1SUMS. We havent got the signature from
300 SignatureFileChecker sigchecker;
302 // now try to find the SHA1SUMS signature
303 if ( find(content.begin(), content.end(), shasig)
306 MIL << "found checksums signature file: " << shasig.name << endl;
307 // TODO refactor with OnMediaLocation::extend or something
308 fetcher.enqueue( OnMediaLocation(resource.filename() + shasig.name, resource.medianr()).setOptional(true) );
309 assert_dir(dest_dir + resource.filename());
310 fetcher.start( dest_dir, media );
312 // if we get the signature, update the checker
313 sigchecker = SignatureFileChecker(dest_dir + resource.filename() + shasig.name);
317 MIL << "no signature for " << shafile.name << endl;
319 // look for the SHA1SUMS.key file
320 if ( find(content.begin(), content.end(), shakey)
323 MIL << "found public key file: " << shakey.name << endl;
324 fetcher.enqueue( OnMediaLocation(resource.filename() + shakey.name, resource.medianr()).setOptional(true) );
325 assert_dir(dest_dir + resource.filename());
326 fetcher.start( dest_dir, media );
329 sigchecker.addPublicKey(dest_dir + resource.filename() + shakey.name, context);
332 // look for the SHA1SUMS public key file
333 if ( find(content.begin(), content.end(), shafile)
336 MIL << "found checksums file: " << shafile.name << endl;
337 fetcher.enqueue( OnMediaLocation(resource.filename() + shafile.name, resource.medianr()).setOptional(true) );
338 assert_dir(dest_dir + resource.filename());
339 fetcher.start( dest_dir, media );
343 // hash table to store checksums
344 map<string, CheckSum> checksums;
346 // look for the SHA1SUMS file
347 if ( find(content.begin(), content.end(), shafile) != content.end() )
349 MIL << "found checksums file: " << shafile.name << endl;
350 fetcher.enqueue( OnMediaLocation(
351 resource.filename() + shafile.name,
352 resource.medianr()).setOptional(true),
353 FileChecker(sigchecker) );
354 assert_dir(dest_dir + resource.filename());
355 fetcher.start( dest_dir + resource.filename(), media );
358 // now read the SHA1SUMS file
359 Pathname pShafile = dest_dir + resource.filename() + shafile.name;
360 std::ifstream in( pShafile.c_str() );
364 while ( getline(in, buffer) )
366 vector<string> words;
367 str::split( buffer, back_inserter(words) );
368 if ( words.size() != 2 )
369 ZYPP_THROW(Exception("Wrong format for SHA1SUMS file"));
370 //MIL << "check: '" << words[0] << "' | '" << words[1] << "'" << endl;
371 if ( ! words[1].empty() )
372 checksums[words[1]] = CheckSum::sha1(words[0]);
376 ZYPP_THROW(Exception("Can't open SHA1SUMS file: " + pShafile.asString()));
379 for ( filesystem::DirContent::const_iterator it = content.begin();
383 // skip SHA1SUMS* as they were already retrieved
384 if ( str::hasPrefix(it->name, "SHA1SUMS") )
387 Pathname filename = resource.filename() + it->name;
391 case filesystem::FT_NOT_AVAIL: // old directory.yast contains no typeinfo at all
392 case filesystem::FT_FILE:
395 if ( checksums.find(it->name) != checksums.end() )
396 checksum = checksums[it->name];
398 // create a resource from the file
399 // if checksum was not available we will have a empty
400 // checksum, which will end in a validation anyway
401 // warning the user that there is no checksum
402 enqueueDigested(OnMediaLocation()
403 .setFilename(filename)
404 .setChecksum(checksum));
408 case filesystem::FT_DIR: // newer directory.yast contain at least directory info
410 addDirJobs(media, filename, dest_dir, recursive);
413 // don't provide devices, sockets, etc.
419 void Fetcher::Impl::provideToDest( MediaSetAccess &media, const OnMediaLocation &resource, const Pathname &dest_dir )
421 bool got_from_cache = false;
423 // start look in cache
424 got_from_cache = provideFromCache(resource, dest_dir);
426 if ( ! got_from_cache )
428 MIL << "Not found in cache, downloading" << endl;
430 // try to get the file from the net
433 Pathname tmp_file = media.provideFile(resource);
434 Pathname dest_full_path = dest_dir + resource.filename();
435 if ( assert_dir( dest_full_path.dirname() ) != 0 )
436 ZYPP_THROW( Exception("Can't create " + dest_full_path.dirname().asString()));
437 if ( filesystem::copy(tmp_file, dest_full_path ) != 0 )
439 ZYPP_THROW( Exception("Can't copy " + tmp_file.asString() + " to " + dest_dir.asString()));
442 media.releaseFile(resource); //not needed anymore, only eat space
444 catch (Exception & excpt_r)
446 ZYPP_CAUGHT(excpt_r);
447 excpt_r.remember("Can't provide " + resource.filename().asString() + " : " + excpt_r.msg());
448 ZYPP_RETHROW(excpt_r);
453 // We got the file from cache
454 // continue with next file
459 void Fetcher::Impl::start( const Pathname &dest_dir,
460 MediaSetAccess &media,
461 const ProgressData::ReceiverFnc & progress_receiver )
463 ProgressData progress(_resources.size());
464 progress.sendTo(progress_receiver);
466 for ( list<FetcherJob_Ptr>::const_iterator it_res = _resources.begin(); it_res != _resources.end(); ++it_res )
469 if ( (*it_res)->directory )
471 const OnMediaLocation location((*it_res)->location);
472 addDirJobs(media, location, dest_dir, true);
476 provideToDest(media, (*it_res)->location, dest_dir);
478 // validate job, this throws if not valid
479 validate((*it_res)->location, dest_dir, (*it_res)->checkers);
481 if ( ! progress.incr() )
482 ZYPP_THROW(AbortRequestException());
486 /** \relates Fetcher::Impl Stream output */
487 inline std::ostream & operator<<( std::ostream & str, const Fetcher::Impl & obj )
489 for ( list<FetcherJob_Ptr>::const_iterator it_res = obj._resources.begin(); it_res != obj._resources.end(); ++it_res )
496 ///////////////////////////////////////////////////////////////////
498 // CLASS NAME : Fetcher
500 ///////////////////////////////////////////////////////////////////
502 ///////////////////////////////////////////////////////////////////
504 // METHOD NAME : Fetcher::Fetcher
505 // METHOD TYPE : Ctor
508 : _pimpl( new Impl() )
511 ///////////////////////////////////////////////////////////////////
513 // METHOD NAME : Fetcher::~Fetcher
514 // METHOD TYPE : Dtor
519 void Fetcher::enqueueDigested( const OnMediaLocation &resource, const FileChecker &checker )
521 _pimpl->enqueueDigested(resource, checker);
524 void Fetcher::enqueueDir( const OnMediaLocation &resource,
526 const FileChecker &checker )
528 _pimpl->enqueueDir(resource, recursive, checker);
531 void Fetcher::enqueue( const OnMediaLocation &resource, const FileChecker &checker )
533 _pimpl->enqueue(resource, checker);
536 void Fetcher::addCachePath( const Pathname &cache_dir )
538 _pimpl->addCachePath(cache_dir);
541 void Fetcher::reset()
546 void Fetcher::start( const Pathname &dest_dir,
547 MediaSetAccess &media,
548 const ProgressData::ReceiverFnc & progress_receiver )
550 _pimpl->start(dest_dir, media, progress_receiver);
554 /******************************************************************
556 ** FUNCTION NAME : operator<<
557 ** FUNCTION TYPE : std::ostream &
559 std::ostream & operator<<( std::ostream & str, const Fetcher & obj )
561 return str << *obj._pimpl;
564 /////////////////////////////////////////////////////////////////
566 ///////////////////////////////////////////////////////////////////