libzypp 17.37.17
MediaCurl2.cc
Go to the documentation of this file.
1/*---------------------------------------------------------------------\
2| ____ _ __ __ ___ |
3| |__ / \ / / . \ . \ |
4| / / \ V /| _/ _/ |
5| / /__ | | | | | | |
6| /_____||_| |_| |_| |
7| |
8\---------------------------------------------------------------------*/
12
13#include <iostream>
14
15#include <zypp/base/Logger.h>
17#include <zypp/base/String.h>
18#include <zypp/base/Gettext.h>
19#include <zypp-core/parser/Sysconfig>
20
22
25#include <zypp-curl/ProxyInfo>
26#include <zypp-curl/CurlConfig>
28#include <zypp/Target.h>
29#include <zypp/ZYppFactory.h>
30#include <zypp/ZConfig.h>
31#include <zypp/zypp_detail/ZYppImpl.h> // for zypp_poll
32
35
36#include <cstdlib>
37#include <sys/types.h>
38#include <sys/stat.h>
39#include <sys/mount.h>
40#include <dirent.h>
41#include <unistd.h>
42#include <glib.h>
43
47
48#ifdef ENABLE_ZCHUNK_COMPRESSION
50#endif
51
52
58
59using std::endl;
60
61using namespace internal;
62using namespace zypp::base;
63
64namespace zypp {
65
66 namespace media {
67
69 const Pathname & attach_point_hint_r )
70 : MediaNetworkCommonHandler( origin_r, attach_point_hint_r,
71 "/", // urlpath at attachpoint
72 true ) // does_download
73 , _executor( std::make_shared<internal::MediaNetworkRequestExecutor>() )
74 {
75
76 MIL << "MediaCurl2::MediaCurl2(" << origin_r.authority().url() << ", " << attach_point_hint_r << ")" << endl;
77
78 if( !attachPoint().empty())
79 {
80 PathInfo ainfo(attachPoint());
81 Pathname apath(attachPoint() + "XXXXXX");
82 char *atemp = ::strdup( apath.asString().c_str());
83 char *atest = NULL;
84 if( !ainfo.isDir() || !ainfo.userMayRWX() ||
85 atemp == NULL || (atest=::mkdtemp(atemp)) == NULL)
86 {
87 WAR << "attach point " << ainfo.path()
88 << " is not useable for " << origin_r.authority().url().getScheme() << endl;
89 setAttachPoint("", true);
90 }
91 else if( atest != NULL)
92 ::rmdir(atest);
93
94 if( atemp != NULL)
95 ::free(atemp);
96 }
97 }
98
100
102 {
103 if ( !zyppng::NetworkRequestDispatcher::supportsProtocol ( url ) )
104 {
105 std::string msg("Unsupported protocol '");
106 msg += url.getScheme();
107 msg += "'";
109 }
110 }
111
113 {
114 // clear effective settings
116 }
117
119
120 void MediaCurl2::releaseFrom( const std::string & ejectDev )
121 {
122 disconnect();
123 }
124
126
127 void MediaCurl2::getFileCopy( const OnMediaLocation & srcFile , const Pathname & target ) const
128 {
129
130 const auto &filename = srcFile.filename();
131
132 // Optional files will send no report until data are actually received (we know it exists).
133 OptionalDownloadProgressReport reportfilter( srcFile.optional() );
135
136 const auto &mirrOrder = mirrorOrder (srcFile);
137 for ( unsigned mirr : mirrOrder ) {
138 MIL << "Trying to fetch file " << srcFile << " via URL: " << _origin[mirr].url() << std::endl;
139 Url fileurl(getFileUrl(mirr, filename));
140
141 try
142 {
143 const auto &myOrigin = _origin[mirr];
144 if(!myOrigin.url().isValid())
145 ZYPP_THROW(MediaBadUrlException(myOrigin.url()));
146
147 if(myOrigin.url().getHost().empty())
149
150
151 Pathname dest = target.absolutename();
152 if( assert_dir( dest.dirname() ) ) {
153 DBG << "assert_dir " << dest.dirname() << " failed" << endl;
154 ZYPP_THROW( MediaSystemException(fileurl, "System error on " + dest.dirname().asString()) );
155 }
156
157 ManagedFile destNew { target.extend( ".new.zypp.XXXXXX" ) }; {
158 AutoFREE<char> buf { ::strdup( (*destNew).c_str() ) };
159 if( ! buf ) {
160 ERR << "out of memory for temp file name" << endl;
161 ZYPP_THROW(MediaSystemException(fileurl, "out of memory for temp file name"));
162 }
163
164 AutoFD tmp_fd { ::mkostemp( buf, O_CLOEXEC ) };
165 if( tmp_fd == -1 ) {
166 ERR << "mkstemp failed for file '" << destNew << "'" << endl;
168 }
169 destNew = ManagedFile( (*buf), filesystem::unlink );
170 }
171
172 DBG << "dest: " << dest << endl;
173 DBG << "temp: " << destNew << endl;
174 #if 0
175 Not implemented here yet because NetworkRequest can not do IFMODSINCE yet
176 // set IFMODSINCE time condition (no download if not modified)
177 if( PathInfo(target).isExist() && !(options & OPTION_NO_IFMODSINCE) )
178 {
179 curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_IFMODSINCE);
180 curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, (long)PathInfo(target).mtime());
181 }
182 else
183 {
184 curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
185 curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, 0L);
186 }
187 #endif
188
189 DBG << srcFile.filename().asString() << endl;
190 DBG << "URL: " << fileurl.asString() << endl;
191
192 // Use URL without options and without username and passwd
193 // (some proxies dislike them in the URL).
194 // Curl seems to need the just scheme, hostname and a path;
195 // the rest was already passed as curl options (in attachTo).
196 Url curlUrl( clearQueryString(fileurl) );
197
198 RequestData r;
199 r._mirrorIdx = mirr;
200 r._req = std::make_shared<zyppng::NetworkRequest>( curlUrl, destNew, zyppng::NetworkRequest::WriteShared /*do not truncate*/ );
201 r._req->transferSettings() = myOrigin.getConfig<TransferSettings>( MIRR_SETTINGS_KEY.data() );
202 r._req->setExpectedFileSize ( srcFile.downloadSize () );
203
204 bool done = false;
205 #ifdef ENABLE_ZCHUNK_COMPRESSION
206 done = const_cast<MediaCurl2*>(this)->tryZchunk(r, srcFile, destNew, report);
207 #endif
208 if ( !done ) {
209 r._req->resetRequestRanges();
210 const_cast<MediaCurl2 *>(this)->executeRequest ( r, &report );
211 }
212
213 #if 0
214 Also disabled IFMODSINCE code, see above while not yet implemented here
215 #if CURLVERSION_AT_LEAST(7,19,4)
216 // bnc#692260: If the client sends a request with an If-Modified-Since header
217 // with a future date for the server, the server may respond 200 sending a
218 // zero size file.
219 // curl-7.19.4 introduces CURLINFO_CONDITION_UNMET to check this condition.
220 if ( ftell(file) == 0 && ret == 0 )
221 {
222 long httpReturnCode = 33;
223 if ( curl_easy_getinfo( _curl, CURLINFO_RESPONSE_CODE, &httpReturnCode ) == CURLE_OK && httpReturnCode == 200 )
224 {
225 long conditionUnmet = 33;
226 if ( curl_easy_getinfo( _curl, CURLINFO_CONDITION_UNMET, &conditionUnmet ) == CURLE_OK && conditionUnmet )
227 {
228 WAR << "TIMECONDITION unmet - retry without." << endl;
229 curl_easy_setopt(_curl, CURLOPT_TIMECONDITION, CURL_TIMECOND_NONE);
230 curl_easy_setopt(_curl, CURLOPT_TIMEVALUE, 0L);
231 ret = executeCurl();
232 }
233 }
234 }
235 #endif
236 #endif
237
238
239 // apply umask
240 if ( ::chmod( destNew->c_str(), filesystem::applyUmaskTo( 0644 ) ) )
241 {
242 ERR << "Failed to chmod file " << destNew << endl;
243 }
244
245 // move the temp file into dest
246 if ( rename( destNew, dest ) != 0 ) {
247 ERR << "Rename failed" << endl;
249 }
250 destNew.resetDispose(); // no more need to unlink it
251
252 DBG << "done: " << PathInfo(dest) << endl;
253
254 break; // success!
255 }
256 catch (MediaException & excpt_r)
257 {
258 // check if we can retry on the next mirror
259 if( !canTryNextMirror ( excpt_r ) || ( mirr == mirrOrder.back() ) ) {
260 // rewrite the exception to contain the correct pathname and url
261 // the executeRequest implementation just emits the full Url in the exception
262 if ( typeid(excpt_r) == typeid( MediaFileNotFoundException ) ) {
263 ZYPP_CAUGHT(excpt_r);
264 ZYPP_THROW( MediaFileNotFoundException( url(), filename ) );
265 }
266 ZYPP_RETHROW(excpt_r);
267 }
268 ZYPP_CAUGHT(excpt_r);
269 continue;
270 }
271 }
272 }
273
274 bool MediaCurl2::getDoesFileExist( const Pathname & filename ) const
275 {
276 DBG << filename.asString() << endl;
277
278 std::exception_ptr lastErr;
279 MIL << "Trying origin: " << _origin << std::endl;
280 for ( unsigned mirr : mirrorOrder( OnMediaLocation( filename ).setMirrorsAllowed(false) )) {
281
282 const auto &myEndpoint = _origin[mirr];
283
284 if( !myEndpoint.url().isValid() )
285 ZYPP_THROW(MediaBadUrlException(myEndpoint.url()));
286
287 if( _origin[mirr].url().getHost().empty() )
288 ZYPP_THROW(MediaBadUrlEmptyHostException(myEndpoint.url()));
289
290 Url url(getFileUrl(mirr, filename));
291
292 DBG << "URL: " << url.asString() << endl;
293
294 // Use URL without options and without username and passwd
295 // (some proxies dislike them in the URL).
296 // Curl seems to need the just scheme, hostname and a path;
297 // the rest was already passed as curl options (in attachTo).
298 Url curlUrl( clearQueryString(url) );
299
300 RequestData r;
301 r._mirrorIdx = mirr;
302 r._req = std::make_shared<zyppng::NetworkRequest>( curlUrl, "/dev/null" );
303 r._req->setOptions ( zyppng::NetworkRequest::HeadRequest ); // just check for existance
304 r._req->transferSettings() = myEndpoint.getConfig<TransferSettings>(MIRR_SETTINGS_KEY.data());
305
306 // as we are not having user interaction, the user can't cancel
307 // the file existence checking, a callback or timeout return code
308 // will be always a timeout.
309 try {
310 const_cast<MediaCurl2*>(this)->executeRequest ( r );
311
312 } catch ( const MediaFileNotFoundException &e ) {
313 // if the file did not exist then we can return false
314 return false;
315 } catch ( const MediaException &e ) {
316 // check if we can retry on the next mirror
317 lastErr = ZYPP_FWD_CURRENT_EXCPT();
318 if( !canTryNextMirror ( e ) ) {
319 break;
320 }
321 continue;
322 }
323 // exists
324 return ( !r._req->hasError() );
325 }
326
327 if ( lastErr ) {
328 try {
329 ZYPP_RETHROW (lastErr);
330 } catch ( const MediaFileNotFoundException &e ) {
331 // if the file did not exist then we can return false
332 return false;
333 }
334 }
335 // we probably never had mirrors, otherwise we either had an error or a success.
336 return false;
337 }
338
340 {
341#ifdef ENABLE_ZCHUNK_COMPRESSION
342
343 // HERE add zchunk logic if required
344 if ( !srcFile.deltafile().empty()
346
347 zyppng::Ref<zyppng::ZckLoader> zckHelper = std::make_shared<zyppng::ZckLoader>();
348
349 const auto &fetchChunks = [&]( const std::vector<zyppng::ZckLoader::Block> &blocks ){
350
351 reqData._req->resetRequestRanges();
352
353 for ( const auto &block : blocks ) {
354 if ( block._checksum.size() && block._chksumtype.size() ) {
355 std::optional<zypp::Digest> dig = zypp::Digest();
356 if ( !dig->create( block._chksumtype ) ) {
357 WAR_MEDIA << "Trying to create Digest with chksum type " << block._chksumtype << " failed " << std::endl;
358 zckHelper->setFailed( str::Str() << "Trying to create Digest with chksum type " << block._chksumtype << " failed " );
359 return;
360 }
361
363 DBG_MEDIA << "Starting block " << block._start << " with checksum " << zypp::Digest::digestVectorToString( block._checksum ) << "." << std::endl;
364 reqData._req->addRequestRange( block._start, block._len, std::move(dig), block._checksum, {}, block._relevantDigestLen, block._chksumPad );
365 } else {
367 DBG_MEDIA << "Starting block " << block._start << " without checksum!" << std::endl;
368 reqData._req->addRequestRange( block._start, block._len );
369 }
370 };
371
372 executeRequest ( reqData, &report );
373 zckHelper->cont().unwrap();
374 };
375
376
378 const auto &fin = [&]( zyppng::ZckLoader::PrepareResult result ){
379 res = std::move(result);
380 };
381
382 const auto &hdrSize = srcFile.headerSize();
383 const auto &dSize = srcFile.downloadSize();
384 zckHelper->connectFunc( &zyppng::ZckLoader::sigBlocksRequired, fetchChunks );
385 zckHelper->connectFunc( &zyppng::ZckLoader::sigFinished, fin );
386 zckHelper->buildZchunkFile( target, srcFile.deltafile(), dSize ? dSize : std::optional<zypp::ByteCount>{}, hdrSize ? hdrSize : std::optional<zypp::ByteCount>{} ).unwrap();
387
388 switch(res._code) {
390 ERR << "Failed to setup zchunk because of: " << res._message << std::endl;
391 return false;
392 }
394 ZYPP_THROW( MediaFileSizeExceededException( reqData._req->url(), srcFile.downloadSize(), res._message ));
397 return true; //done
398 }
399 }
400#endif
401 return false;
402 }
403
405 {
406 const auto &authCb = [&]( const zypp::Url &, TransferSettings &settings, const std::string & availAuthTypes, bool firstTry, bool &canContinue ) {
407 auto &originEndpoint = _origin[reqData._mirrorIdx];
408 auto &epSettings = originEndpoint.getConfig<TransferSettings>(MIRR_SETTINGS_KEY.data());
409 if ( authenticate( _origin[reqData._mirrorIdx].url(), epSettings, availAuthTypes, firstTry ) ) {
410 settings = epSettings;
411 canContinue = true;
412 return;
413 }
414 canContinue = false;
415 };
416
417 auto conn = _executor->sigAuthRequired().connect (authCb);
418 zypp_defer {
419 conn.disconnect ();
420 };
421
422 _executor->executeRequest ( reqData._req, report );
423 }
424 } // namespace media
425} // namespace zypp
426//
void resetDispose()
Set no dispose function.
Compute Message Digests (MD5, SHA1 etc)
Definition Digest.h:38
static std::string digestVectorToString(const UByteArray &vec)
get hex string representation of the digest vector given as parameter
Definition Digest.cc:244
Manages a data source characterized by an authoritative URL and a list of mirror URLs.
const OriginEndpoint & authority() const
Describes a resource file located on a medium.
bool optional() const
Whether this is an optional resource.
const ByteCount & downloadSize() const
The size of the resource on the server.
const Pathname & filename() const
The path to the resource on the medium.
const Pathname & deltafile() const
The existing deltafile that can be used to reduce download size ( zchunk or metalink )
const ByteCount & headerSize() const
The size of the header prepending the resource (e.g.
const zypp::Url & url() const
Url manipulation class.
Definition Url.h:93
std::string getScheme() const
Returns the scheme name of the URL.
Definition Url.cc:551
std::string asString() const
Returns a default string representation of the Url object.
Definition Url.cc:515
Wrapper class for stat/lstat.
Definition PathInfo.h:226
const Pathname & path() const
Return current Pathname.
Definition PathInfo.h:251
Pathname dirname() const
Return all but the last component od this path.
Definition Pathname.h:126
const char * c_str() const
String representation.
Definition Pathname.h:112
const std::string & asString() const
String representation.
Definition Pathname.h:93
bool empty() const
Test for an empty path.
Definition Pathname.h:116
void releaseFrom(const std::string &ejectDev) override
Call concrete handler to release the media.
void disconnectFrom() override
void getFileCopy(const OnMediaLocation &srcFile, const Pathname &targetFilename) const override
MediaCurl2(const MirroredOrigin origin_r, const Pathname &attach_point_hint_r)
Definition MediaCurl2.cc:68
bool getDoesFileExist(const Pathname &filename) const override
Repeatedly calls doGetDoesFileExist() until it successfully returns, fails unexpectedly,...
void executeRequest(RequestData &reqData, callback::SendReport< DownloadProgressReport > *report=nullptr)
internal::MediaNetworkRequestExecutorRef _executor
Definition MediaCurl2.h:102
bool tryZchunk(RequestData &reqData, const OnMediaLocation &srcFile, const Pathname &target, callback::SendReport< DownloadProgressReport > &report)
void checkProtocol(const Url &url) const override
check the url is supported by the curl library
Just inherits Exception to separate media exceptions.
MirroredOrigin _origin
Contains the authority URL and mirrors.
Url url() const
Primary Url used.
void disconnect()
Use concrete handler to isconnect media.
OriginEndpoint originEndpoint() const
Primary OriginEndpoint used.
void setAttachPoint(const Pathname &path, bool temp)
Set a new attach point.
Pathname attachPoint() const
Return the currently used attach point.
bool authenticate(const Url &url, TransferSettings &settings, const std::string &availAuthTypes, bool firstTry)
static bool canTryNextMirror(const Excpt &excpt_r)
std::vector< unsigned > mirrorOrder(const OnMediaLocation &loc) const
Url getFileUrl(int mirrorIdx, const Pathname &filename) const
concatenate the attach url and the filename to a complete download url
MediaNetworkCommonHandler(const MirroredOrigin &origin_r, const Pathname &attach_point_r, const Pathname &urlpath_below_attachpoint_r, const bool does_download_r)
static constexpr std::string_view MIRR_SETTINGS_KEY
Holds transfer setting.
SignalProxy< void(PrepareResult)> sigFinished()
Called once the zchunk build process is finished, either with error or success.
Definition zckhelper.cc:299
static bool isZchunkFile(const zypp::Pathname &file)
Definition zckhelper.cc:309
SignalProxy< void(const std::vector< Block > &)> sigBlocksRequired()
Signal to notify the caller about required blocks, once the blocks are downloaded call cont to contin...
Definition zckhelper.cc:294
#define WAR_MEDIA
#define DBG_MEDIA
Definition Arch.h:364
const long & ZYPP_MEDIA_CURL_DEBUG()
const long& for setting CURLOPT_DEBUGDATA Returns a reference to a static variable,...
Definition curlhelper.cc:36
mode_t applyUmaskTo(mode_t mode_r)
Modify mode_r according to the current umask ( mode_r & ~getUmask() ).
Definition PathInfo.h:805
int unlink(const Pathname &path)
Like 'unlink'.
Definition PathInfo.cc:705
Easy-to use interface to the ZYPP dependency resolver.
AutoDispose< const Pathname > ManagedFile
A Pathname plus associated cleanup code to be executed when path is no longer needed.
Definition ManagedFile.h:27
std::shared_ptr< T > Ref
Definition zyppglobal.h:110
Bottleneck filtering all DownloadProgressReport issued from Media[Muli]Curl.
AutoDispose<int> calling close
zyppng::NetworkRequestRef _req
Definition MediaCurl2.h:95
Convenient building of std::string via std::ostringstream Basically a std::ostringstream autoconverti...
Definition String.h:213
#define zypp_defer
#define ZYPP_RETHROW(EXCPT)
Drops a logline and rethrows, updating the CodeLocation.
Definition Exception.h:479
#define ZYPP_CAUGHT(EXCPT)
Drops a logline telling the Exception was caught (in order to handle it).
Definition Exception.h:475
#define ZYPP_FWD_CURRENT_EXCPT()
Drops a logline and returns the current Exception as a std::exception_ptr.
Definition Exception.h:471
#define ZYPP_THROW(EXCPT)
Drops a logline and throws the Exception.
Definition Exception.h:459
#define DBG
Definition Logger.h:99
#define MIL
Definition Logger.h:100
#define ERR
Definition Logger.h:102
#define WAR
Definition Logger.h:101
Interface to gettext.