Imported Upstream version 17.22.0
[platform/upstream/libzypp.git] / zypp / repo / yum / Downloader.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9
10 #include <fstream>
11 #include <solv/solvversion.h>
12 #include "zypp/base/String.h"
13 #include "zypp/base/LogTools.h"
14 #include "zypp/base/Function.h"
15 #include "zypp/ZConfig.h"
16
17 #include "Downloader.h"
18 #include "zypp/repo/MediaInfoDownloader.h"
19 #include "zypp/base/UserRequestException.h"
20 #include "zypp/parser/xml/Reader.h"
21 #include "zypp/parser/yum/RepomdFileReader.h"
22
23 using namespace zypp::xml;
24 using namespace zypp::parser::yum;
25
26 namespace zypp
27 {
28 namespace repo
29 {
30 namespace yum
31 {
32   ///////////////////////////////////////////////////////////////////
33   namespace
34   {
35     inline OnMediaLocation loc_with_path_prefix( OnMediaLocation loc_r, const Pathname & prefix_r )
36     {
37       if ( ! prefix_r.empty() && prefix_r != "/" )
38         loc_r.changeFilename( prefix_r / loc_r.filename() );
39       return loc_r;
40     }
41
42     // search old repository file to run the delta algorithm on
43     Pathname search_deltafile( const Pathname & dir, const Pathname & file )
44     {
45       Pathname deltafile;
46       if ( ! PathInfo(dir).isDir() )
47         return deltafile;
48
49       // Strip the checksum preceding the file stem so we can look for an
50       // old *-primary.xml which may contain some reusable blocks.
51       std::string base { file.basename() };
52       size_t hypoff = base.find( "-" );
53       if ( hypoff != std::string::npos )
54         base.replace( 0, hypoff + 1, "" );
55
56       std::list<std::string> retlist;
57       if ( ! filesystem::readdir( retlist, dir, false ) )
58       {
59         for ( const auto & fn : retlist )
60         {
61           if ( str::endsWith( fn, base ) )
62             deltafile = fn;
63         }
64       }
65       return deltafile;
66     }
67   } // namespace
68   ///////////////////////////////////////////////////////////////////
69
70   ///////////////////////////////////////////////////////////////////
71   /// \class Downloader::Impl
72   /// \brief Helper filtering the files offered by a RepomdFileReader
73   ///
74   /// Clumsy construct; basically an Impl class for Downloader, maintained
75   /// in Downloader::download only while parsing a repomd.xml.
76   ///     File types:
77   ///         type        (plain)
78   ///         type_db     (sqlite, ignored by zypp)
79   ///         type_zck    (zchunk, preferred)
80   ///     Localized type:
81   ///         susedata.LOCALE
82   ///////////////////////////////////////////////////////////////////
83   struct Downloader::Impl
84   {
85     NON_COPYABLE( Impl );
86     NON_MOVABLE( Impl );
87
88     Impl( Downloader & downloader_r, MediaSetAccess & media_r, const Pathname & destDir_r )
89     : _downloader { downloader_r }
90     , _media { media_r }
91     , _destDir { destDir_r }
92     {
93       addWantedLocale( ZConfig::instance().textLocale() );
94       for ( const Locale & it : ZConfig::instance().repoRefreshLocales() )
95         addWantedLocale( it );
96     }
97
98     /** The callback invoked by the RepomdFileReader.
99      * It's a pity, but in the presence of separate "type" and "type_zck" entries,
100      * we have to scan the whole file before deciding what to download....
101      */
102     bool operator()( const OnMediaLocation & loc_r, const std::string & typestr_r )
103     {
104       if ( str::endsWith( typestr_r, "_db" ) )
105         return true;    // skip sqlitedb
106
107       bool zchk { str::endsWith( typestr_r, "_zck" ) };
108 #ifdef LIBSOLVEXT_FEATURE_ZSTD_COMPRESSION
109       const std::string & basetype { zchk ? typestr_r.substr( 0, typestr_r.size()-4 ) : typestr_r };
110 #else
111       if ( zchk )
112         return true;    // skip zchunk if not supported by libsolv
113       const std::string & basetype { typestr_r };
114 #endif
115
116       // filter well known resource types
117       if ( basetype == "other" || basetype == "filelists" )
118         return true;    // skip it
119
120       // filter localized susedata
121       if ( str::startsWith( basetype, "susedata." ) )
122       {
123         // susedata.LANG
124         if ( ! wantLocale( Locale(basetype.c_str()+9) ) )
125           return true;  // skip it
126       }
127
128       // may take it... (prefer zchnk)
129       if ( zchk || !_wantedFiles.count( basetype ) )
130         _wantedFiles[basetype] = loc_r;
131
132       return true;
133     }
134
135     void finalize()
136     {
137       // schedule fileS for download
138       for ( const auto & el : _wantedFiles )
139       {
140         const OnMediaLocation & loc { el.second };
141         const OnMediaLocation & loc_with_path { loc_with_path_prefix( loc, _downloader.repoInfo().path() ) };
142         _downloader.enqueueDigested( loc_with_path, FileChecker(), search_deltafile( deltaDir()/"repodata", loc.filename() ) );
143       }
144     }
145
146   private:
147     const Pathname & deltaDir() const
148     { return _downloader._deltaDir; }
149
150     bool wantLocale( const Locale & locale_r ) const
151     { return _wantedLocales.count( locale_r ); }
152
153     void addWantedLocale( Locale locale_r )
154     {
155       while ( locale_r )
156       {
157         _wantedLocales.insert( locale_r );
158         locale_r = locale_r.fallback();
159       }
160     }
161
162   private:
163     Downloader & _downloader;
164     MediaSetAccess & _media;
165     const Pathname & _destDir;
166
167     LocaleSet _wantedLocales;   ///< Locales do download
168     std::map<std::string,OnMediaLocation> _wantedFiles;
169   };
170
171   ///////////////////////////////////////////////////////////////////
172   //
173   // class Downloader
174   //
175   ///////////////////////////////////////////////////////////////////
176
177   Downloader::Downloader( const RepoInfo & info_r, const Pathname & deltaDir_r )
178   : repo::Downloader { info_r}
179   , _deltaDir { deltaDir_r }
180   {}
181
182   void Downloader::download( MediaSetAccess & media_r, const Pathname & destDir_r, const ProgressData::ReceiverFnc & progress_r )
183   {
184     downloadMediaInfo( destDir_r, media_r );
185
186     Pathname masterIndex { repoInfo().path() / "/repodata/repomd.xml" };
187     defaultDownloadMasterIndex( media_r, destDir_r, masterIndex );
188
189     // setup parser
190     Impl pimpl( *this, media_r, destDir_r );
191     RepomdFileReader( destDir_r / masterIndex, std::ref(pimpl) );
192     pimpl.finalize();
193
194     // ready, go!
195     start( destDir_r, media_r );
196   }
197
198   RepoStatus Downloader::status( MediaSetAccess & media_r )
199   {
200     RepoStatus ret { media_r.provideOptionalFile( repoInfo().path() / "/repodata/repomd.xml" ) };
201     if ( !ret.empty() ) // else: mandatory master index is missing
202       ret = ret && RepoStatus( media_r.provideOptionalFile( "/media.1/media" ) );
203     // else: mandatory master index is missing -> stay empty
204     return ret;
205   }
206 } // namespace yum
207 } // namespace repo
208 } // namespace zypp
209
210
211