35f02298841a6501b6296e22416574944d5487ac
[platform/upstream/libzypp.git] / zypp / target / rpm / librpmDb.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/target/rpm/librpmDb.cc
10  *
11 */
12 #include "librpm.h"
13
14 #include <iostream>
15
16 #include <zypp/base/Logger.h>
17 #include <zypp/PathInfo.h>
18 #include <zypp/target/rpm/librpmDb.h>
19 #include <zypp/target/rpm/RpmHeader.h>
20 #include <zypp/target/rpm/RpmException.h>
21
22 #undef ZYPP_BASE_LOGGER_LOGGROUP
23 #define ZYPP_BASE_LOGGER_LOGGROUP "librpmDb"
24
25 using std::endl;
26
27 namespace zypp
28 {
29 namespace target
30 {
31 namespace rpm
32 {
33 ///////////////////////////////////////////////////////////////////
34 //
35 //      CLASS NAME : librpmDb::D
36 /**
37  * @short librpmDb internal database handle
38  **/
39 class librpmDb::D
40 {
41   D & operator=( const D & ); // NO ASSIGNMENT!
42   D ( const D & );            // NO COPY!
43 public:
44
45   const Pathname _root;   // root directory for all operations
46   const Pathname _dbPath; // directory (below root) that contains the rpmdb
47   rpmts _ts;              // transaction handle, includes database
48   shared_ptr<RpmException> _error;  // database error
49
50   friend std::ostream & operator<<( std::ostream & str, const D & obj )
51   {
52     str << "{" << obj._error  << "(" << obj._root << ")" << obj._dbPath << "}";
53     return str;
54   }
55
56   D( const Pathname & root_r, const Pathname & dbPath_r, bool readonly_r )
57       : _root  ( root_r )
58       , _dbPath( dbPath_r )
59       , _ts    ( 0 )
60   {
61     _error.reset();
62     // set %_dbpath macro
63     ::addMacro( NULL, "_dbpath", NULL, _dbPath.asString().c_str(), RMIL_CMDLINE );
64
65     _ts = ::rpmtsCreate();
66     ::rpmtsSetRootDir( _ts, _root.c_str() );
67
68     // open database (creates a missing one on the fly)
69     int res = ::rpmtsOpenDB( _ts, (readonly_r ? O_RDONLY : O_RDWR ));
70     if ( res )
71     {
72       ERR << "rpmdbOpen error(" << res << "): " << *this << endl;
73       _error = shared_ptr<RpmDbOpenException>(new RpmDbOpenException(_root, _dbPath));
74       rpmtsFree(_ts);
75       ZYPP_THROW(*_error);
76       return;
77     }
78
79     DBG << "DBACCESS " << *this << endl;
80   }
81
82   ~D()
83   {
84     if ( _ts )
85     {
86       ::rpmtsFree(_ts);
87     }
88   }
89 };
90
91 ///////////////////////////////////////////////////////////////////
92
93 ///////////////////////////////////////////////////////////////////
94 //
95 //      CLASS NAME : librpmDb (ststic interface)
96 //
97 ///////////////////////////////////////////////////////////////////
98
99 Pathname         librpmDb::_defaultRoot  ( "/" );
100 // NOTE: Former variable, but now locked to "/var/lib/rpm".
101 // A custom dbPath is not actually needed and would only work
102 // reliably if libsolv also supports it.
103 // The protected librpmDb ctor would allow to build a db_const_iterator
104 // to access (ro) a database at a custom location.
105 const Pathname   librpmDb::_defaultDbPath( "/var/lib/rpm" );
106 librpmDb::constPtr librpmDb::_defaultDb;
107 bool             librpmDb::_dbBlocked    ( true );
108
109 ///////////////////////////////////////////////////////////////////
110 //
111 //
112 //      METHOD NAME : librpmDb::globalInit
113 //      METHOD TYPE : bool
114 //
115 bool librpmDb::globalInit()
116 {
117   static bool initialized = false;
118
119   if ( initialized )
120     return true;
121
122   int rc = ::rpmReadConfigFiles( NULL, NULL );
123   if ( rc )
124   {
125     ERR << "rpmReadConfigFiles returned " << rc << endl;
126     return false;
127   }
128
129   initialized = true; // Necessary to be able to use exand().
130
131 #define OUTVAL(n) << " (" #n ":" << expand( "%{" #n "}" ) << ")"
132   MIL << "librpm init done:"
133   OUTVAL(_target)
134   OUTVAL(_dbpath)
135   << endl;
136 #undef OUTVAL
137   return initialized;
138 }
139
140 ///////////////////////////////////////////////////////////////////
141 //
142 //
143 //      METHOD NAME : librpmDb::expand
144 //      METHOD TYPE : std::string
145 //
146 std::string librpmDb::expand( const std::string & macro_r )
147 {
148   if ( ! globalInit() )
149     return macro_r;  // unexpanded
150
151   char * val = ::rpmExpand( macro_r.c_str(), NULL );
152   if ( !val )
153     return "";
154
155   std::string ret( val );
156   free( val );
157   return ret;
158 }
159
160 ///////////////////////////////////////////////////////////////////
161 //
162 //
163 //      METHOD NAME : librpmDb::newLibrpmDb
164 //      METHOD TYPE : librpmDb *
165 //
166 librpmDb * librpmDb::newLibrpmDb()
167 {
168   // initialize librpm
169   if ( ! globalInit() )
170   {
171     ZYPP_THROW(GlobalRpmInitException());
172   }
173
174   // open rpmdb
175   librpmDb * ret = 0;
176   try
177   {
178     ret = new librpmDb( _defaultRoot, _defaultDbPath, /*readonly*/true );
179   }
180   catch (const RpmException & excpt_r)
181   {
182     ZYPP_CAUGHT(excpt_r);
183     delete ret;
184     ret = 0;
185     ZYPP_RETHROW(excpt_r);
186   }
187   return ret;
188 }
189
190 ///////////////////////////////////////////////////////////////////
191 //
192 //
193 //      METHOD NAME : librpmDb::dbAccess
194 //      METHOD TYPE : PMError
195 //
196 void librpmDb::dbAccess( const Pathname & root_r )
197 {
198   if ( _defaultDb )
199   {
200     // already accessing a database: switching is not allowed.
201     if ( _defaultRoot == root_r )
202       return;
203     else
204       ZYPP_THROW(RpmDbAlreadyOpenException(_defaultRoot, _defaultDbPath, root_r, _defaultDbPath));
205   }
206
207   // got no database: we could switch to a new one (even if blocked!)
208
209   if ( root_r.empty() || ! root_r.absolute() )
210     ZYPP_THROW(RpmInvalidRootException(root_r, _defaultDbPath));
211
212   PathInfo pi { root_r / _defaultDbPath };
213   if ( pi.isExist() && ! pi.isDir() ) {
214     RpmInvalidRootException excpt { root_r, _defaultDbPath };
215     excpt.addHistory( str::Str() << pi );
216     ZYPP_THROW(excpt);
217   }
218
219   _defaultRoot = root_r;
220   MIL << "Set new database location: " << stringPath( _defaultRoot, _defaultDbPath ) << endl;
221
222   return dbAccess();
223 }
224
225 ///////////////////////////////////////////////////////////////////
226 //
227 //
228 //      METHOD NAME : librpmDb::dbAccess
229 //      METHOD TYPE : PMError
230 //
231 void librpmDb::dbAccess()
232 {
233   if ( _dbBlocked )
234   {
235     ZYPP_THROW(RpmAccessBlockedException(_defaultRoot, _defaultDbPath));
236   }
237
238   if ( !_defaultDb )
239   {
240     // get access
241     _defaultDb = newLibrpmDb();
242   }
243 }
244
245 ///////////////////////////////////////////////////////////////////
246 //
247 //
248 //      METHOD NAME : librpmDb::dbAccess
249 //      METHOD TYPE : PMError
250 //
251 void librpmDb::dbAccess( librpmDb::constPtr & ptr_r )
252 {
253   ptr_r = nullptr;
254   dbAccess();
255   ptr_r = _defaultDb;
256 }
257
258 ///////////////////////////////////////////////////////////////////
259 //
260 //
261 //      METHOD NAME : librpmDb::dbRelease
262 //      METHOD TYPE : unsigned
263 //
264 unsigned librpmDb::dbRelease( bool force_r )
265 {
266   if ( !_defaultDb )
267   {
268     return 0;
269   }
270
271   unsigned outstanding = _defaultDb->refCount() - 1; // refCount can't be 0
272
273   switch ( outstanding )
274   {
275   default:
276     if ( !force_r )
277     {
278       DBG << "dbRelease: keep access, outstanding " << outstanding << endl;
279       break;
280     }
281     // else fall through:
282   case 0:
283     DBG << "dbRelease: release" << (force_r && outstanding ? "(forced)" : "")
284     << ", outstanding " << outstanding << endl;
285
286     _defaultDb->_d._error = shared_ptr<RpmAccessBlockedException>(new RpmAccessBlockedException(_defaultDb->_d._root, _defaultDb->_d._dbPath));
287     // tag handle invalid
288     _defaultDb = 0;
289     break;
290   }
291
292   return outstanding;
293 }
294
295 ///////////////////////////////////////////////////////////////////
296 //
297 //
298 //      METHOD NAME : librpmDb::blockAccess
299 //      METHOD TYPE : unsigned
300 //
301 unsigned librpmDb::blockAccess()
302 {
303   MIL << "Block access" << endl;
304   _dbBlocked = true;
305   return dbRelease( /*force*/true );
306 }
307
308 ///////////////////////////////////////////////////////////////////
309 //
310 //
311 //      METHOD NAME : librpmDb::unblockAccess
312 //      METHOD TYPE : void
313 //
314 void librpmDb::unblockAccess()
315 {
316   MIL << "Unblock access" << endl;
317   _dbBlocked = false;
318 }
319
320 ///////////////////////////////////////////////////////////////////
321 //
322 //
323 //      METHOD NAME : librpmDb::dumpState
324 //      METHOD TYPE : ostream &
325 //
326 std::ostream & librpmDb::dumpState( std::ostream & str )
327 {
328   if ( !_defaultDb )
329   {
330     return str << "[librpmDb " << (_dbBlocked?"BLOCKED":"CLOSED") << " " << stringPath( _defaultRoot, _defaultDbPath ) << "]";
331   }
332   return str << "[" << _defaultDb << "]";
333 }
334
335 ///////////////////////////////////////////////////////////////////
336 //
337 //      CLASS NAME : librpmDb (internal database handle interface (nonstatic))
338 //
339 ///////////////////////////////////////////////////////////////////
340
341 ///////////////////////////////////////////////////////////////////
342 //
343 //
344 //      METHOD NAME : librpmDb::librpmDb
345 //      METHOD TYPE : Constructor
346 //
347 //      DESCRIPTION :
348 //
349 librpmDb::librpmDb( const Pathname & root_r, const Pathname & dbPath_r, bool readonly_r )
350     : _d( * new D( root_r, dbPath_r, readonly_r ) )
351 {}
352
353 ///////////////////////////////////////////////////////////////////
354 //
355 //
356 //      METHOD NAME : librpmDb::~librpmDb
357 //      METHOD TYPE : Destructor
358 //
359 //      DESCRIPTION :
360 //
361 librpmDb::~librpmDb()
362 {
363   delete &_d;
364 }
365
366 ///////////////////////////////////////////////////////////////////
367 //
368 //
369 //      METHOD NAME : librpmDb::unref_to
370 //      METHOD TYPE : void
371 //
372 void librpmDb::unref_to( unsigned refCount_r ) const
373 {
374   if ( refCount_r == 1 )
375   {
376     dbRelease();
377   }
378 }
379
380 ///////////////////////////////////////////////////////////////////
381 //
382 //
383 //      METHOD NAME : librpmDb::root
384 //      METHOD TYPE : const Pathname &
385 //
386 const Pathname & librpmDb::root() const
387 {
388   return _d._root;
389 }
390
391 ///////////////////////////////////////////////////////////////////
392 //
393 //
394 //      METHOD NAME : librpmDb::dbPath
395 //      METHOD TYPE : const Pathname &
396 //
397 const Pathname & librpmDb::dbPath() const
398 {
399   return _d._dbPath;
400 }
401
402 ///////////////////////////////////////////////////////////////////
403 //
404 //
405 //      METHOD NAME : librpmDb::error
406 //      METHOD TYPE : PMError
407 //
408 shared_ptr<RpmException> librpmDb::error() const
409 {
410   return _d._error;
411 }
412
413 ///////////////////////////////////////////////////////////////////
414 //
415 //
416 //      METHOD NAME : librpmDb::empty
417 //      METHOD TYPE : bool
418 //
419 bool librpmDb::empty() const
420 {
421   return( valid() && ! *db_const_iterator( this ) );
422 }
423
424 ///////////////////////////////////////////////////////////////////
425 //
426 //
427 //      METHOD NAME : librpmDb::size
428 //      METHOD TYPE : unsigned
429 //
430 unsigned librpmDb::size() const
431 {
432   unsigned count = 0;
433   if ( valid() )
434   {
435     db_const_iterator it( this );
436     for ( db_const_iterator it( this ); *it; ++it )
437       ++count;
438   }
439   return count;
440 }
441
442 ///////////////////////////////////////////////////////////////////
443 //
444 //
445 //      METHOD NAME : librpmDb::dont_call_it
446 //      METHOD TYPE : void *
447 //
448 void * librpmDb::dont_call_it() const
449 {
450   return rpmtsGetRdb(_d._ts);
451 }
452
453 ///////////////////////////////////////////////////////////////////
454 //
455 //
456 //      METHOD NAME : librpmDb::dumpOn
457 //      METHOD TYPE : ostream &
458 //
459 //      DESCRIPTION :
460 //
461 std::ostream & librpmDb::dumpOn( std::ostream & str ) const
462 {
463   ReferenceCounted::dumpOn( str ) << _d;
464   return str;
465 }
466
467 ///////////////////////////////////////////////////////////////////
468 //
469 //      CLASS NAME : librpmDb::db_const_iterator::D
470 /**
471  *
472  **/
473 class librpmDb::db_const_iterator::D
474 {
475   D & operator=( const D & ); // NO ASSIGNMENT!
476   D ( const D & );            // NO COPY!
477 public:
478
479   librpmDb::constPtr     _dbptr;
480   shared_ptr<RpmException> _dberr;
481
482   RpmHeader::constPtr _hptr;
483   rpmdbMatchIterator   _mi;
484
485   D( librpmDb::constPtr dbptr_r )
486       : _dbptr( dbptr_r )
487       , _mi( 0 )
488   {
489     if ( !_dbptr )
490     {
491       try
492       {
493         librpmDb::dbAccess( _dbptr );
494       }
495       catch (const RpmException & excpt_r)
496       {
497         ZYPP_CAUGHT(excpt_r);
498       }
499       if ( !_dbptr )
500       {
501         WAR << "No database access: " << _dberr << endl;
502       }
503     }
504     else
505     {
506       destroy(); // Checks whether _dbptr still valid
507     }
508   }
509
510   ~D()
511   {
512     if ( _mi )
513     {
514       ::rpmdbFreeIterator( _mi );
515     }
516   }
517
518   /**
519    * Let iterator access a dbindex file. Call @ref advance to access the
520    * 1st element (if present).
521    **/
522   bool create( int rpmtag, const void * keyp = NULL, size_t keylen = 0 )
523   {
524     destroy();
525     if ( ! _dbptr )
526       return false;
527     _mi = ::rpmtsInitIterator( _dbptr->_d._ts, rpmTag(rpmtag), keyp, keylen );
528     return _mi;
529   }
530
531   /**
532    * Destroy iterator. Invalidates _dbptr, if database was blocked meanwile.
533    * Always returns false.
534    **/
535   bool destroy()
536   {
537     if ( _mi )
538     {
539       _mi = ::rpmdbFreeIterator( _mi );
540       _hptr = 0;
541     }
542     if ( _dbptr && _dbptr->error() )
543     {
544       _dberr = _dbptr->error();
545       WAR << "Lost database access: " << _dberr << endl;
546       _dbptr = 0;
547     }
548     return false;
549   }
550
551   /**
552    * Advance to the first/next header in iterator. Destroys iterator if
553    * no more headers available.
554    **/
555   bool advance()
556   {
557     if ( !_mi )
558       return false;
559     Header h = ::rpmdbNextIterator( _mi );
560     if ( ! h )
561     {
562       destroy();
563       return false;
564     }
565     _hptr = new RpmHeader( h );
566     return true;
567   }
568
569   /**
570    * Access a dbindex file and advance to the 1st header.
571    **/
572   bool init( int rpmtag, const void * keyp = NULL, size_t keylen = 0 )
573   {
574     if ( ! create( rpmtag, keyp, keylen ) )
575       return false;
576     return advance();
577   }
578
579   /**
580    * Create an itertator that contains the database entry located at
581    * off_r, and advance to the 1st header.
582    **/
583   bool set( int off_r )
584   {
585     if ( ! create( RPMDBI_PACKAGES ) )
586       return false;
587 #warning TESTCASE: rpmdbAppendIterator and (non)sequential access?
588 #ifdef RPMFILEITERMAX   // since rpm.4.12
589     ::rpmdbAppendIterator( _mi, (const unsigned *)&off_r, 1 );
590 #else
591     ::rpmdbAppendIterator( _mi, &off_r, 1 );
592 #endif
593     return advance();
594   }
595
596   unsigned offset()
597   {
598     return( _mi ? ::rpmdbGetIteratorOffset( _mi ) : 0 );
599   }
600
601   int size()
602   {
603     if ( !_mi )
604       return 0;
605     int ret = ::rpmdbGetIteratorCount( _mi );
606 #warning TESTCASE: rpmdbGetIteratorCount returns 0 on sequential access?
607     return( ret ? ret : -1 ); // -1: sequential access
608   }
609 };
610
611 ///////////////////////////////////////////////////////////////////
612
613 ///////////////////////////////////////////////////////////////////
614 //
615 //      CLASS NAME : librpmDb::Ptr::db_const_iterator
616 //
617 ///////////////////////////////////////////////////////////////////
618
619 ///////////////////////////////////////////////////////////////////
620 //
621 //
622 //      METHOD NAME : librpmDb::db_const_iterator::db_iterator
623 //      METHOD TYPE : Constructor
624 //
625 librpmDb::db_const_iterator::db_const_iterator( librpmDb::constPtr dbptr_r )
626     : _d( * new D( dbptr_r ) )
627 {
628   findAll();
629 }
630
631 ///////////////////////////////////////////////////////////////////
632 //
633 //
634 //      METHOD NAME : librpmDb::db_const_iterator::~db_const_iterator
635 //      METHOD TYPE : Destructor
636 //
637 librpmDb::db_const_iterator::~db_const_iterator()
638 {
639   delete &_d;
640 }
641
642 ///////////////////////////////////////////////////////////////////
643 //
644 //
645 //      METHOD NAME : librpmDb::db_const_iterator::operator++
646 //      METHOD TYPE : void
647 //
648 void librpmDb::db_const_iterator::operator++()
649 {
650   _d.advance();
651 }
652
653 ///////////////////////////////////////////////////////////////////
654 //
655 //
656 //      METHOD NAME : librpmDb::db_const_iterator::dbHdrNum
657 //      METHOD TYPE : unsigned
658 //
659 unsigned librpmDb::db_const_iterator::dbHdrNum() const
660 {
661   return _d.offset();
662 }
663
664 ///////////////////////////////////////////////////////////////////
665 //
666 //
667 //      METHOD NAME : librpmDb::db_const_iterator::operator*
668 //      METHOD TYPE : const RpmHeader::constPtr &
669 //
670 const RpmHeader::constPtr & librpmDb::db_const_iterator::operator*() const
671 {
672   return _d._hptr;
673 }
674
675 ///////////////////////////////////////////////////////////////////
676 //
677 //
678 //      METHOD NAME : librpmDb::db_const_iterator::dbError
679 //      METHOD TYPE : PMError
680 //
681 shared_ptr<RpmException> librpmDb::db_const_iterator::dbError() const
682 {
683   if ( _d._dbptr )
684     return _d._dbptr->error();
685
686   return _d._dberr;
687 }
688
689 /******************************************************************
690 **
691 **
692 **      FUNCTION NAME : operator<<
693 **      FUNCTION TYPE : ostream &
694 */
695 std::ostream & operator<<( std::ostream & str, const librpmDb::db_const_iterator & obj )
696 {
697   str << "db_const_iterator(" << obj._d._dbptr
698   << " Size:" << obj._d.size()
699   << " HdrNum:" << obj._d.offset()
700   << ")";
701   return str;
702 }
703
704 ///////////////////////////////////////////////////////////////////
705 //
706 //
707 //      METHOD NAME : librpmDb::db_const_iterator::findAll
708 //      METHOD TYPE : bool
709 //
710 bool librpmDb::db_const_iterator::findAll()
711 {
712   return _d.init( RPMDBI_PACKAGES );
713 }
714
715 ///////////////////////////////////////////////////////////////////
716 //
717 //
718 //      METHOD NAME : librpmDb::db_const_iterator::findByFile
719 //      METHOD TYPE : bool
720 //
721 bool librpmDb::db_const_iterator::findByFile( const std::string & file_r )
722 {
723   return _d.init( RPMTAG_BASENAMES, file_r.c_str() );
724 }
725
726 ///////////////////////////////////////////////////////////////////
727 //
728 //
729 //      METHOD NAME : librpmDb::db_const_iterator::findByProvides
730 //      METHOD TYPE : bool
731 //
732 bool librpmDb::db_const_iterator::findByProvides( const std::string & tag_r )
733 {
734   return _d.init( RPMTAG_PROVIDENAME, tag_r.c_str() );
735 }
736
737 ///////////////////////////////////////////////////////////////////
738 //
739 //
740 //      METHOD NAME : librpmDb::db_const_iterator::findByRequiredBy
741 //      METHOD TYPE : bool
742 //
743 bool librpmDb::db_const_iterator::findByRequiredBy( const std::string & tag_r )
744 {
745   return _d.init( RPMTAG_REQUIRENAME, tag_r.c_str() );
746 }
747
748 ///////////////////////////////////////////////////////////////////
749 //
750 //
751 //      METHOD NAME : librpmDb::db_const_iterator::findByConflicts
752 //      METHOD TYPE : bool
753 //
754 bool librpmDb::db_const_iterator::findByConflicts( const std::string & tag_r )
755 {
756   return _d.init( RPMTAG_CONFLICTNAME, tag_r.c_str() );
757 }
758
759 ///////////////////////////////////////////////////////////////////
760 //
761 //
762 //      METHOD NAME : librpmDb::findByName
763 //      METHOD TYPE : bool
764 //
765 bool librpmDb::db_const_iterator::findByName( const std::string & name_r )
766 {
767   return _d.init( RPMTAG_NAME, name_r.c_str() );
768 }
769
770 ///////////////////////////////////////////////////////////////////
771 //
772 //
773 //      METHOD NAME : librpmDb::db_const_iterator::findPackage
774 //      METHOD TYPE : bool
775 //
776 bool librpmDb::db_const_iterator::findPackage( const std::string & name_r )
777 {
778   if ( ! _d.init( RPMTAG_NAME, name_r.c_str() ) )
779     return false;
780
781   if ( _d.size() == 1 )
782     return true;
783
784   // check installtime on multiple entries
785   int match    = 0;
786   time_t itime = 0;
787   for ( ; operator*(); operator++() )
788   {
789     if ( operator*()->tag_installtime() > itime )
790     {
791       match = _d.offset();
792       itime = operator*()->tag_installtime();
793     }
794   }
795
796   return _d.set( match );
797 }
798
799 ///////////////////////////////////////////////////////////////////
800 //
801 //
802 //      METHOD NAME : librpmDb::db_const_iterator::findPackage
803 //      METHOD TYPE : bool
804 //
805 bool librpmDb::db_const_iterator::findPackage( const std::string & name_r, const Edition & ed_r )
806 {
807   if ( ! _d.init( RPMTAG_NAME, name_r.c_str() ) )
808     return false;
809
810   for ( ; operator*(); operator++() )
811   {
812     if ( ed_r == operator*()->tag_edition() )
813     {
814       int match = _d.offset();
815       return _d.set( match );
816     }
817   }
818
819   return _d.destroy();
820 }
821
822 ///////////////////////////////////////////////////////////////////
823 //
824 //
825 //      METHOD NAME : librpmDb::db_const_iterator::findPackage
826 //      METHOD TYPE : bool
827 //
828 bool librpmDb::db_const_iterator::findPackage( const Package::constPtr & which_r )
829 {
830   if ( ! which_r )
831     return _d.destroy();
832
833   return findPackage( which_r->name(), which_r->edition() );
834 }
835
836 } // namespace rpm
837 } // namespace target
838 } // namespace zypp