libzypp 17.37.17
zckhelper.cc
Go to the documentation of this file.
1/*---------------------------------------------------------------------\
2| ____ _ __ __ ___ |
3| |__ / \ / / . \ . \ |
4| / / \ V /| _/ _/ |
5| / /__ | | | | | | |
6| /_____||_| |_| |_| |
7| |
8----------------------------------------------------------------------*/
9#include "zckhelper.h"
12#include <fcntl.h>
13#include <fstream>
14
15extern "C" {
16#include <zck.h>
17}
18
19namespace zyppng {
20
21 ZckError::ZckError(const std::string &msg_r) : Exception(msg_r)
22 { }
23
24 ZckError::ZckError(std::string &&msg_r) : Exception(std::move(msg_r))
25 { }
26
27 expected<void> ZckLoader::buildZchunkFile(const zypp::Pathname &target, const zypp::Pathname &delta, const std::optional<zypp::ByteCount> &expectedFileSize, const std::optional<zypp::ByteCount> &zcKHeaderSize)
28 {
29 if ( _state != Initial && _state != Finished ) {
30 return expected<void>::error( ZYPP_EXCPT_PTR(ZckError("Called buildZchunkFile in invalid state!")) );
31 }
32
33 _zchunkContext = zypp::AutoDispose<zckCtx *> ( zck_create(), []( auto ptr ) { if ( ptr ) zck_free( &ptr ); } );
34 if( !_zchunkContext )
35 return expected<void>::error( ZYPP_EXCPT_PTR(ZckError( zypp::str::Format("Failed to create zchunk context: %1%") % zck_get_error(NULL) )) );
36
37 _targetFd = open( target.asString().c_str(), O_RDWR );
38 if( _targetFd < 0 )
39 return expected<void>::error( ZYPP_EXCPT_PTR(ZckError( zypp::str::Format("Unable to open %1%") % target )) );
40
41 if(!zck_init_adv_read(_zchunkContext, _targetFd))
42 return expected<void>::error( ZYPP_EXCPT_PTR(ZckError( zypp::str::Format( "Unable to init read for %1%: %2%") % target % zck_get_error(_zchunkContext) )) );
43
45 _delta = delta;
46 _zcKHeaderSize = zcKHeaderSize;
47 _expectedFileSize = expectedFileSize;
48 _bytesReused = 0;
49
50 std::vector<Block> initialBlocks;
51 if ( _zcKHeaderSize ) {
52 Block b;
53 b._start = 0;
54 b._len = *_zcKHeaderSize;
55 initialBlocks.push_back (b);
57 } else {
58 Block b;
59 b._start = 0;
60 b._len = minZchunkDownloadSize();
61 initialBlocks.push_back (b);
63 }
64
65 _sigBlocksRequired.emit( initialBlocks );
67 }
68
70 {
71 const auto &emitFailed = [this]( PrepareResult::Code code, std::string message ){
74 ._code = code,
75 ._blocks = std::vector<Block>(),
76 ._bytesReused = 0,
77 ._message = std::move(message),
78 });
79
80 if ( _targetFd )
81 ftruncate ( _targetFd, 0 );
82
84 };
85
86 switch( _state ) {
87 case DownloadLead: {
88 // calling code claims to have downloaded the lead.
89 if ( !zck_read_lead( _zchunkContext ) )
90 return emitFailed( PrepareResult::Error, zypp::str::Format( "Unable to read lead from %1%: %2%") % _target % zck_get_error(_zchunkContext) );
91
92 // get actual lead length, no need to fetch it again
93 const auto start = zck_get_lead_length(_zchunkContext);
94
95 // not adding the header checksums here, zck will validate it when reading the header
96 Block b;
97 b._start = start;
98 b._len = zck_get_header_length(_zchunkContext) - start;
99
101 _sigBlocksRequired.emit( std::vector<Block>{b} );
102
103 break;
104 }
105 case DownloadHeader: {
106 // calling code claims to have downloaded the full header, lets check
107 if ( _zcKHeaderSize ) {
108 // if the zckHeaderSize was known initially the DownloadLead state was skipped. We need to read the lead here
109 if ( !zck_read_lead( _zchunkContext ) ) {
110 return emitFailed( PrepareResult::Error, zypp::str::Format( "Unable to read lead from %1%: %2%") % _target % zck_get_error(_zchunkContext) );
111 }
112 }
113
114 if(!zck_read_header(_zchunkContext)) {
115 return emitFailed( PrepareResult::Error, zypp::str::Format( "Unable to read the header from %1%: %2%") % _target % zck_get_error(_zchunkContext) );
116 }
117
118 // yay we have downloaded the header , now we know all chunks from the target file. Next step is to calculate
119 // what we can reuse and request the missing chunks
120
121 zypp::AutoFD src_fd = open( _delta.asString().c_str(), O_RDONLY);
122 if(src_fd < 0) {
123 return emitFailed ( PrepareResult::Error, zypp::str::Format("Unable to open %1%") % _delta );
124 }
125
126 zypp::AutoDispose<zckCtx *> zck_src ( zck_create(), []( auto ptr ) { if ( ptr ) zck_free( &ptr ); } );
127 if( !zck_src ) {
128 return emitFailed ( PrepareResult::Error, zypp::str::Format("%1%") % zck_get_error(NULL) );
129 }
130
131 if(!zck_init_read(zck_src, src_fd)) {
132 return emitFailed ( PrepareResult::Error, zypp::str::Format( "Unable to open %1%: %2%") % _delta % zck_get_error(zck_src) );
133 }
134
135 {
136 // Returns 0 for error, -1 for invalid checksum and 1 for valid checksum
137 switch ( zck_find_valid_chunks(_zchunkContext) ) {
138 case 0: { // Returns 0 if there was a error
139 return emitFailed ( PrepareResult::Error, zypp::str::Format( "Unable to open %1%: %2%") % _target % zck_get_error(_zchunkContext) );
140 }
141 case 1: { // getting a 1 would mean the file is already complete, basically impossible but lets handle it anyway
145 });
147 }
148 }
149
150 const auto srcHashType = zck_get_chunk_hash_type( _zchunkContext );
151 const auto targetHashType = zck_get_chunk_hash_type( _zchunkContext );
152
153 zypp::ByteCount fileSize = zck_get_length( _zchunkContext );
154 if ( _expectedFileSize && _expectedFileSize.value() != fileSize ) {
155 // check if the file size as reported by zchunk is equal to the one we expect
156 return emitFailed(
158 zypp::str::Format("Zchunk header reports a different filesize than what was expected ( Zck: %1% != Exp: %2%).") % fileSize % *_expectedFileSize
159 );
160 }
161
162 if ( ftruncate ( _targetFd, fileSize ) < 0 ) {
163 return emitFailed ( PrepareResult::Error, zypp::str::Format( "Failed to truncate file to required filesize.") );
164 }
165
166 if( srcHashType != targetHashType ) {
167 return emitFailed ( PrepareResult::Error, zypp::str::Format( "ERROR: Chunk hash types don't match. Source Hash: %1% vs Target Hash: %2%")
168 % zck_hash_name_from_type ( srcHashType )
169 % zck_hash_name_from_type ( targetHashType ) );
170 }
171
172 std::vector<Block> ranges;
173
174 if(!zck_copy_chunks( zck_src, _zchunkContext ))
175 return emitFailed ( PrepareResult::Error, zypp::str::Format( "Unable to copy chunks from deltafile.") );
176
177 // we calculate what is already downloaded by substracting the block sizes we still need to download from the full file size
178 auto bytesReused = fileSize;
179
180 auto chunk = zck_get_first_chunk( _zchunkContext );
181
182 if ( !chunk ) {
183 return emitFailed ( PrepareResult::Error, zypp::str::Format( "Unable to get first chunk: %1%.") % zck_get_error(_zchunkContext) );
184 }
185
186 do {
187 // Get validity of current chunk: 1 = valid, 0 = missing, -1 = invalid
188 if ( zck_get_chunk_valid( chunk ) == 1 )
189 continue;
190
191 zypp::AutoFREE<char> zckDigest( zck_get_chunk_digest( chunk ) );
192 UByteArray chksumVec = zypp::Digest::hexStringToUByteArray( std::string_view( zckDigest.value() ) );
193 std::string chksumName;
194 std::optional<size_t> chksumCompareLen;
195
196 switch ( targetHashType ) {
197 case ZCK_HASH_SHA1: {
198 chksumName = zypp::Digest::sha1();
199 break;
200 }
201 case ZCK_HASH_SHA256: {
202 chksumName = zypp::Digest::sha256();
203 break;
204 }
205 case ZCK_HASH_SHA512: {
206 chksumName = zypp::Digest::sha512();
207 break;
208 }
209 case ZCK_HASH_SHA512_128: {
210 // defined in zchunk as
211 // SHA-512/128 (first 128 bits of SHA-512 checksum)
212 chksumName = zypp::Digest::sha512();
213 chksumCompareLen = chksumVec.size();
214 break;
215 }
216 default: {
217 return emitFailed ( PrepareResult::Error, zypp::str::Format( "Unsupported chunk hash type: %1%.") % zck_hash_name_from_type( targetHashType ) );
218 }
219 }
220
221 const auto s = static_cast<size_t>( zck_get_chunk_start( chunk ) );
222 const auto l = static_cast<size_t>( zck_get_chunk_comp_size ( chunk ) );
223
224 MIL_MEDIA << "Downloading block " << s << " with length " << l << " checksum " << zckDigest.value() << " type " << chksumName << std::endl;
225 ranges.push_back( Block {
226 ._start = s,
227 ._len = l,
228 ._chksumtype = chksumName,
229 ._checksum = std::move( chksumVec ),
230 ._relevantDigestLen = std::move(chksumCompareLen)
231 } );
232
233 // substract the block length from the already downloaded bytes size
234 bytesReused -= l;
235
236 } while ( (chunk = zck_get_next_chunk( chunk )) );
237
238
240 _bytesReused = bytesReused;
241 _sigBlocksRequired.emit( ranges );
242 }
243 break;
244 }
245 case DownloadChunks: {
246 // all data should be there now.
247
248 /* Validate the chunk and data checksums for the current file.
249 * Returns 0 for error, -1 for invalid checksum and 1 for valid checksum */
250 const auto res = zck_validate_checksums( _zchunkContext );
251 if ( res == 0 || res == -1 ) {
252 if( zck_is_error(nullptr) ) {
253 std::string err = zck_get_error(NULL);
254 zck_clear_error(NULL);
255 return emitFailed( PrepareResult::Error, std::move(err) );
256 }
257 if( zck_is_error(_zchunkContext) )
258 return emitFailed( PrepareResult::Error, zck_get_error(_zchunkContext) );
259 return emitFailed( PrepareResult::Error, "zck_validate_checksums returned a unknown error." );
260 }
261
263 PrepareResult pres;
266 _sigFinished.emit(pres);
267 break;
268 }
269
270 case Initial:
271 case Finished: {
272 return expected<void>::error( ZYPP_EXCPT_PTR(ZckError( zypp::str::Format("Continue called in invalid state.") )) );
273 }
274 }
275 // continuation worked
277 }
278
279 void ZckLoader::setFailed(const std::string &msg)
280 {
282
283 if ( _targetFd )
284 ftruncate ( _targetFd, 0 );
285
287 ._code = PrepareResult::Error,
288 ._blocks = std::vector<Block>(),
289 ._bytesReused = 0,
290 ._message = msg,
291 });
292 }
293
294 SignalProxy<void (const std::vector<ZckLoader::Block> &)> ZckLoader::sigBlocksRequired()
295 {
296 return _sigBlocksRequired;
297 }
298
303
305 {
306 return zck_get_min_download_size();
307 }
308
310 std::ifstream dFile(file.c_str());
311 if (!dFile.is_open())
312 return false;
313
314 constexpr std::string_view magic("\0ZCK1", 5);
315
316 std::array<char, magic.size()> lead;
317 lead.fill('\0');
318 dFile.read(lead.data(), lead.size());
319 return (magic == std::string_view(lead.data(), lead.size()));
320 }
321
323 {
324 const auto &setFailed = []( PrepareResult::Code code, std::string message ){
325 return PrepareResult {
326 ._code = code,
327 ._blocks = std::vector<Block>(),
328 ._bytesReused = 0,
329 ._message = std::move(message),
330 };
331 };
332
333 zypp::AutoFD src_fd = open( delta.asString().c_str(), O_RDONLY);
334 if(src_fd < 0)
335 return setFailed ( PrepareResult::Error, zypp::str::Format("Unable to open %1%") % delta );
336
337 zypp::AutoDispose<zckCtx *> zck_src ( zck_create(), []( auto ptr ) { if ( ptr ) zck_free( &ptr ); } );
338 if( !zck_src )
339 return setFailed ( PrepareResult::Error, zypp::str::Format("%1%") % zck_get_error(NULL) );
340
341 if(!zck_init_read(zck_src, src_fd))
342 return setFailed ( PrepareResult::Error, zypp::str::Format( "Unable to open %1%: %2%") % delta % zck_get_error(zck_src) );
343
344 zypp::AutoFD target_fd = open( target.asString().c_str(), O_RDWR);
345 if(target_fd < 0)
346 return setFailed ( PrepareResult::Error, zypp::str::Format("Unable to open %1%") % target );
347
348 zypp::AutoDispose<zckCtx *> zckTarget ( zck_create(), []( auto ptr ) { if ( ptr ) zck_free( &ptr ); } );
349 if( !zckTarget )
350 return setFailed ( PrepareResult::Error, zypp::str::Format("%1%") % zck_get_error(NULL) );
351
352 if(!zck_init_read(zckTarget, target_fd))
353 return setFailed ( PrepareResult::Error, zypp::str::Format( "Unable to open %1%: %2%") % target % zck_get_error(zckTarget) );
354
355 // Returns 0 for error, -1 for invalid checksum and 1 for valid checksum
356 switch ( zck_find_valid_chunks(zckTarget) ) {
357 case 0: // Returns 0 if there was a error
358 return setFailed ( PrepareResult::Error, zypp::str::Format( "Unable to open %1%: %2%") % target % zck_get_error(zckTarget) );
359 case 1: // getting a 1 would mean the file is already complete, basically impossible but lets handle it anyway
360 return PrepareResult {
362 };
363 }
364
365 const auto srcHashType = zck_get_chunk_hash_type( zckTarget );
366 const auto targetHashType = zck_get_chunk_hash_type( zckTarget );
367
368 auto _fileSize = expectedFileSize;
369
370 const size_t fLen = zck_get_length( zckTarget );
371 if ( expectedFileSize > 0 ) {
372 // check if the file size as reported by zchunk is equal to the one we expect
373 if ( expectedFileSize != fLen ) {
374 return setFailed(
376 zypp::str::Format("Zchunk header reports a different filesize than what was expected ( Zck: %1% != Exp: %2%).") % fLen % _fileSize
377 );
378 }
379 } else {
380 _fileSize = fLen;
381 }
382
383 if( srcHashType != targetHashType )
384 return setFailed ( PrepareResult::Error, zypp::str::Format( "ERROR: Chunk hash types don't match. Source Hash: %1% vs Target Hash: %2%")
385 % zck_hash_name_from_type ( srcHashType )
386 % zck_hash_name_from_type ( targetHashType ) );
387
388 std::vector<Block> ranges;
389
390 if(!zck_copy_chunks( zck_src, zckTarget ))
391 return setFailed ( PrepareResult::Error, zypp::str::Format( "Unable to copy chunks from deltafile.") );
392
393 // we calculate what is already downloaded by substracting the block sizes we still need to download from the full file size
394 auto bytesReused = _fileSize;
395
396 auto chunk = zck_get_first_chunk( zckTarget );
397 do {
398 // Get validity of current chunk: 1 = valid, 0 = missing, -1 = invalid
399 if ( zck_get_chunk_valid( chunk ) == 1 )
400 continue;
401
402 zypp::AutoFREE<char> zckDigest( zck_get_chunk_digest( chunk ) );
403 UByteArray chksumVec = zypp::Digest::hexStringToUByteArray( std::string_view( zckDigest.value() ) );
404 std::string chksumName;
405 std::optional<size_t> chksumCompareLen;
406
407 switch ( targetHashType ) {
408 case ZCK_HASH_SHA1: {
409 chksumName = zypp::Digest::sha1();
410 break;
411 }
412 case ZCK_HASH_SHA256: {
413 chksumName = zypp::Digest::sha256();
414 break;
415 }
416 case ZCK_HASH_SHA512: {
417 chksumName = zypp::Digest::sha512();
418 break;
419 }
420 case ZCK_HASH_SHA512_128: {
421 // defined in zchunk as
422 // SHA-512/128 (first 128 bits of SHA-512 checksum)
423 chksumName = zypp::Digest::sha512();
424 chksumCompareLen = chksumVec.size();
425 break;
426 }
427 default: {
428 return setFailed ( PrepareResult::Error, zypp::str::Format( "Unsupported chunk hash type: %1%.") % zck_hash_name_from_type( targetHashType ) );
429 }
430 }
431
432 const auto s = static_cast<size_t>( zck_get_chunk_start( chunk ) );
433 const auto l = static_cast<size_t>( zck_get_chunk_comp_size ( chunk ) );
434
435 MIL_MEDIA << "Downloading block " << s << " with length " << l << " checksum " << zckDigest.value() << " type " << chksumName << std::endl;
436 ranges.push_back( Block {
437 ._start = s,
438 ._len = l,
439 ._chksumtype = chksumName,
440 ._checksum = std::move( chksumVec ),
441 ._relevantDigestLen = std::move(chksumCompareLen)
442 } );
443
444 // substract the block length from the already downloaded bytes size
445 bytesReused -= l;
446
447 } while ( (chunk = zck_get_next_chunk( chunk )) );
448
449 return PrepareResult {
450 ._code = PrepareResult::Success,
451 ._blocks = std::move(ranges),
452 ._bytesReused = std::move(bytesReused),
453 ._message = std::string()
454 };
455 }
456
457 bool ZckLoader::validateZckFile(const zypp::Pathname &file, std::string &error)
458 {
459 const auto &setFailed = [&]( std::string &&err ) {
460 error = std::move(err);
461 return false;
462 };
463
464 zypp::AutoFD target_fd = open( file.asString().c_str(), O_RDONLY );
465 if( target_fd < 0 )
466 return setFailed ( zypp::str::Format("Unable to open %1%") % file );
467
468 zypp::AutoDispose<zckCtx *> zckTarget ( zck_create(), []( auto ptr ) { if ( ptr ) zck_free( &ptr ); } );
469 if( !zckTarget )
470 return setFailed ( zypp::str::Format("%1%") % zck_get_error(nullptr) );
471
472 if(!zck_init_read(zckTarget, target_fd))
473 return setFailed ( zypp::str::Format( "Unable to open %1%: %2%") % file % zck_get_error(zckTarget) );
474
475 /* Validate the chunk and data checksums for the current file.
476 * Returns 0 for error, -1 for invalid checksum and 1 for valid checksum */
477 const auto res = zck_validate_checksums( zckTarget );
478 if ( res == 0 || res == -1 ) {
479 if( zck_is_error(nullptr) ) {
480 std::string err = zck_get_error(NULL);
481 zck_clear_error(NULL);
482 return setFailed( std::move(err) );
483 }
484 if( zck_is_error(zckTarget) )
485 return setFailed( zck_get_error(zckTarget) );
486 return setFailed( "zck_validate_checksums returned a unknown error." );
487 }
488
489 return true;
490 }
491
492} // namespace zyppng
Reference counted access to a Tp object calling a custom Dispose function when the last AutoDispose h...
Definition AutoDispose.h:95
reference value() const
Reference to the Tp object.
Store and operate with byte count.
Definition ByteCount.h:32
static const std::string & sha512()
sha512
Definition Digest.cc:57
static const std::string & sha1()
sha1
Definition Digest.cc:45
static const std::string & sha256()
sha256
Definition Digest.cc:51
Exception()
Default ctor.
Definition Exception.cc:94
const char * c_str() const
String representation.
Definition Pathname.h:112
const std::string & asString() const
String representation.
Definition Pathname.h:93
ZckError(const std::string &msg_r)
Definition zckhelper.cc:21
static PrepareResult prepareZck(const zypp::Pathname &delta, const zypp::Pathname &target, const zypp::ByteCount &expectedFileSize)
Definition zckhelper.cc:322
static bool validateZckFile(const zypp::Pathname &file, std::string &error)
Definition zckhelper.cc:457
void setFailed(const std::string &msg)
Definition zckhelper.cc:279
std::optional< zypp::ByteCount > _expectedFileSize
Definition zckhelper.h:123
expected< void > buildZchunkFile(const zypp::Pathname &target, const zypp::Pathname &delta, const std::optional< zypp::ByteCount > &expectedFileSize, const std::optional< zypp::ByteCount > &zcKHeaderSize)
Definition zckhelper.cc:27
expected< void > cont()
Definition zckhelper.cc:69
Signal< void(PrepareResult)> _sigFinished
Definition zckhelper.h:127
zypp::Pathname _delta
Definition zckhelper.h:122
zypp::ByteCount _bytesReused
Definition zckhelper.h:119
SignalProxy< void(PrepareResult)> sigFinished()
Called once the zchunk build process is finished, either with error or success.
Definition zckhelper.cc:299
zypp::AutoDispose< zckCtx * > _zchunkContext
Definition zckhelper.h:117
RangeDesc Block
Definition zckhelper.h:47
zypp::Pathname _target
Definition zckhelper.h:121
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
static zypp::ByteCount minZchunkDownloadSize()
The minimum size to download to have enough data to know the full header size.
Definition zckhelper.cc:304
Signal< void(const std::vector< Block > &)> _sigBlocksRequired
Definition zckhelper.h:126
std::optional< zypp::ByteCount > _zcKHeaderSize
Definition zckhelper.h:124
zypp::AutoFD _targetFd
Definition zckhelper.h:118
static expected success(ConsParams &&...params)
Definition expected.h:115
unsigned short b
#define MIL_MEDIA
Definition Arch.h:364
AutoDispose<int> calling close
Convenient building of std::string with boost::format.
Definition String.h:254
#define ZYPP_EXCPT_PTR(EXCPT)
Drops a logline and returns Exception as a std::exception_ptr.
Definition Exception.h:463