libzypp  17.37.18
zckhelper.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 ----------------------------------------------------------------------*/
9 #include "zckhelper.h"
10 #include <zypp-core/AutoDispose.h>
12 #include <fcntl.h>
13 #include <fstream>
14 
15 extern "C" {
16 #include <zck.h>
17 }
18 
19 namespace 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 
44  _target = target;
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 );
66  return expected<void>::success();
67  }
68 
70  {
71  const auto &emitFailed = [this]( PrepareResult::Code code, std::string message ){
72  _state = Finished;
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 
83  return expected<void>::success();
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
142  _state = Finished;
145  });
146  return expected<void>::success();
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 
262  _state = Finished;
263  PrepareResult pres;
265  pres._bytesReused = _bytesReused;
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
276  return expected<void>::success();
277  }
278 
279  void ZckLoader::setFailed(const std::string &msg)
280  {
281  _state = Finished;
282 
283  if ( _targetFd )
284  ftruncate ( _targetFd, 0 );
285 
288  ._blocks = std::vector<Block>(),
289  ._bytesReused = 0,
290  ._message = msg,
291  });
292  }
293 
295  {
296  return _sigBlocksRequired;
297  }
298 
300  {
301  return _sigFinished;
302  }
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 
322  ZckLoader::PrepareResult ZckLoader::prepareZck( const zypp::Pathname &delta, const zypp::Pathname &target, const zypp::ByteCount &expectedFileSize )
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 {
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
static const std::string & sha256()
sha256
Definition: Digest.cc:51
static const std::string & sha1()
sha1
Definition: Digest.cc:45
unsigned short b
Store and operate with byte count.
Definition: ByteCount.h:31
static bool isZchunkFile(const zypp::Pathname &file)
Definition: zckhelper.cc:309
std::optional< zypp::ByteCount > _zcKHeaderSize
Definition: zckhelper.h:124
static bool validateZckFile(const zypp::Pathname &file, std::string &error)
Definition: zckhelper.cc:457
zypp::AutoDispose< zckCtx * > _zchunkContext
Definition: zckhelper.h:117
const char * c_str() const
String representation.
Definition: Pathname.h:112
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
static zypp::ByteCount minZchunkDownloadSize()
The minimum size to download to have enough data to know the full header size.
Definition: zckhelper.cc:304
Definition: ansi.h:854
Convenient building of std::string with boost::format.
Definition: String.h:253
std::optional< zypp::ByteCount > _expectedFileSize
Definition: zckhelper.h:123
AutoDispose<int> calling ::close
Definition: AutoDispose.h:309
#define ZYPP_EXCPT_PTR(EXCPT)
Drops a logline and returns Exception as a std::exception_ptr.
Definition: Exception.h:463
SignalProxy< void(PrepareResult)> sigFinished()
Called once the zchunk build process is finished, either with error or success.
Definition: zckhelper.cc:299
ZckError(const std::string &msg_r)
Definition: zckhelper.cc:21
zypp::Pathname _delta
Definition: zckhelper.h:122
Signal< void(const std::vector< Block > &)> _sigBlocksRequired
Definition: zckhelper.h:126
static const std::string & sha512()
sha512
Definition: Digest.cc:57
void setFailed(const std::string &msg)
Definition: zckhelper.cc:279
zypp::ByteCount _bytesReused
Definition: zckhelper.h:119
const std::string & asString() const
String representation.
Definition: Pathname.h:93
zypp::Pathname _target
Definition: zckhelper.h:121
static expected success(ConsParams &&...params)
Definition: expected.h:115
#define MIL_MEDIA
Definition: mediadebug_p.h:29
Base class for Exception.
Definition: Exception.h:152
reference value() const
Reference to the Tp object.
Definition: AutoDispose.h:138
zypp::ByteCount _bytesReused
Definition: zckhelper.h:59
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 PrepareResult prepareZck(const zypp::Pathname &delta, const zypp::Pathname &target, const zypp::ByteCount &expectedFileSize)
Definition: zckhelper.cc:322
Reference counted access to a Tp object calling a custom Dispose function when the last AutoDispose h...
Definition: AutoDispose.h:94
Signal< void(PrepareResult)> _sigFinished
Definition: zckhelper.h:127
zypp::AutoFD _targetFd
Definition: zckhelper.h:118
expected< void > cont()
Definition: zckhelper.cc:69