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