librpmDb.cv3.cc

Go to the documentation of this file.
00001 /*---------------------------------------------------------------------\
00002 |                          ____ _   __ __ ___                          |
00003 |                         |__  / \ / / . \ . \                         |
00004 |                           / / \ V /|  _/  _/                         |
00005 |                          / /__ | | | | | |                           |
00006 |                         /_____||_| |_| |_|                           |
00007 |                                                                      |
00008 \---------------------------------------------------------------------*/
00012 #include "librpm.h"
00013 
00014 #include <iostream>
00015 
00016 #include "zypp/base/Logger.h"
00017 
00018 #include "zypp/target/rpm/librpmDb.h"
00019 #include "zypp/target/rpm/RpmCallbacks.h"
00020 #include "zypp/ZYppCallbacks.h"
00021 
00022 extern "C"
00023 {
00024 #include <string.h>
00025 
00026 #define FA_MAGIC      0x02050920
00027 
00028   struct faFileHeader
00029   {
00030     unsigned int magic;
00031     unsigned int firstFree;
00032   };
00033 
00034   struct faHeader
00035   {
00036     unsigned int size;
00037     unsigned int freeNext; /* offset of the next free block, 0 if none */
00038     unsigned int freePrev;
00039     unsigned int isFree;
00040 
00041     /* note that the u16's appear last for alignment/space reasons */
00042   };
00043 }
00044 
00045 namespace zypp
00046 {
00047 namespace target
00048 {
00049 namespace rpm
00050 {
00051 
00052 static int fadFileSize;
00053 
00054 static ssize_t Pread(FD_t fd, void * buf, size_t count, off_t offset)
00055 {
00056   if (Fseek(fd, offset, SEEK_SET) < 0)
00057     return -1;
00058   return Fread(buf, sizeof(char), count, fd);
00059 }
00060 
00061 static FD_t fadOpen(const char * path)
00062 {
00063   struct faFileHeader newHdr;
00064   FD_t fd;
00065   struct stat stb;
00066 
00067   fd = Fopen(path, "r.fdio");
00068   if (!fd || Ferror(fd))
00069     return NULL;
00070 
00071   if (fstat(Fileno(fd), &stb))
00072   {
00073     Fclose(fd);
00074     return NULL;
00075   }
00076   fadFileSize = stb.st_size;
00077 
00078   /* is this file brand new? */
00079   if (fadFileSize == 0)
00080   {
00081     Fclose(fd);
00082     return NULL;
00083   }
00084   if (Pread(fd, &newHdr, sizeof(newHdr), 0) != sizeof(newHdr))
00085   {
00086     Fclose(fd);
00087     return NULL;
00088   }
00089   if (newHdr.magic != FA_MAGIC)
00090   {
00091     Fclose(fd);
00092     return NULL;
00093   }
00094   /*@-refcounttrans@*/ return fd /*@=refcounttrans@*/ ;
00095 }
00096 
00097 static int fadNextOffset(FD_t fd, unsigned int lastOffset)
00098 {
00099   struct faHeader header;
00100   int offset;
00101 
00102   offset = (lastOffset)
00103            ? (lastOffset - sizeof(header))
00104            : sizeof(struct faFileHeader);
00105 
00106   if (offset >= fadFileSize)
00107     return 0;
00108 
00109   if (Pread(fd, &header, sizeof(header), offset) != sizeof(header))
00110     return 0;
00111 
00112   if (!lastOffset && !header.isFree)
00113     return (offset + sizeof(header));
00114 
00115   do
00116   {
00117     offset += header.size;
00118 
00119     if (Pread(fd, &header, sizeof(header), offset) != sizeof(header))
00120       return 0;
00121 
00122     if (!header.isFree) break;
00123   }
00124   while (offset < fadFileSize && header.isFree);
00125 
00126   if (offset < fadFileSize)
00127   {
00128     /* Sanity check this to make sure we're not going in loops */
00129     offset += sizeof(header);
00130 
00131     if (offset < 0 || (unsigned)offset <= lastOffset) return -1;
00132 
00133     return offset;
00134   }
00135   else
00136     return 0;
00137 }
00138 
00139 static int fadFirstOffset(FD_t fd)
00140 {
00141   return fadNextOffset(fd, 0);
00142 }
00143 
00144 /*@-boundsread@*/
00145 static int dncmp(const void * a, const void * b)
00146 /*@*/
00147 {
00148   const char *const * first = (const char *const *)a;
00149   const char *const * second = (const char *const *)b;
00150   return strcmp(*first, *second);
00151 }
00152 /*@=boundsread@*/
00153 
00154 /*@-bounds@*/
00155 static void compressFilelist(Header h)
00156 /*@*/
00157 {
00158   HGE_t hge = (HGE_t)headerGetEntryMinMemory;
00159   HAE_t hae = (HAE_t)headerAddEntry;
00160   HRE_t hre = (HRE_t)headerRemoveEntry;
00161   HFD_t hfd = headerFreeData;
00162   char ** fileNames;
00163   const char ** dirNames;
00164   const char ** baseNames;
00165   int_32 * dirIndexes;
00166   rpmTagType fnt;
00167   int count;
00168   int i, xx;
00169   int dirIndex = -1;
00170 
00171   /*
00172    * This assumes the file list is already sorted, and begins with a
00173    * single '/'. That assumption isn't critical, but it makes things go
00174    * a bit faster.
00175    */
00176 
00177   if (headerIsEntry(h, RPMTAG_DIRNAMES))
00178   {
00179     xx = hre(h, RPMTAG_OLDFILENAMES);
00180     return;             /* Already converted. */
00181   }
00182 
00183   void *hgePtr = NULL;
00184   if (!hge(h, RPMTAG_OLDFILENAMES, &fnt, &hgePtr, &count))
00185     return;             /* no file list */
00186   fileNames = (char **)hgePtr;
00187   if (fileNames == NULL || count <= 0)
00188     return;
00189 
00190   dirNames = (const char **)alloca(sizeof(*dirNames) * count);  /* worst case */
00191   baseNames = (const char **)alloca(sizeof(*dirNames) * count);
00192   dirIndexes = (int_32 *)alloca(sizeof(*dirIndexes) * count);
00193 
00194   if (fileNames[0][0] != '/')
00195   {
00196     /* HACK. Source RPM, so just do things differently */
00197     dirIndex = 0;
00198     dirNames[dirIndex] = "";
00199     for (i = 0; i < count; i++)
00200     {
00201       dirIndexes[i] = dirIndex;
00202       baseNames[i] = fileNames[i];
00203     }
00204     goto exit;
00205   }
00206 
00207   /*@-branchstate@*/
00208   for (i = 0; i < count; i++)
00209   {
00210     const char ** needle;
00211     char savechar;
00212     char * baseName;
00213     int len;
00214 
00215     if (fileNames[i] == NULL)   /* XXX can't happen */
00216       continue;
00217     baseName = strrchr(fileNames[i], '/') + 1;
00218     len = baseName - fileNames[i];
00219     needle = dirNames;
00220     savechar = *baseName;
00221     *baseName = '\0';
00222     /*@-compdef@*/
00223     if (dirIndex < 0 ||
00224         (needle = (const char **)bsearch(&fileNames[i], dirNames, dirIndex + 1, sizeof(dirNames[0]), dncmp)) == NULL)
00225     {
00226       char *s = (char *)alloca(len + 1);
00227       memcpy(s, fileNames[i], len + 1);
00228       s[len] = '\0';
00229       dirIndexes[i] = ++dirIndex;
00230       dirNames[dirIndex] = s;
00231     }
00232     else
00233       dirIndexes[i] = needle - dirNames;
00234     /*@=compdef@*/
00235 
00236     *baseName = savechar;
00237     baseNames[i] = baseName;
00238   }
00239   /*@=branchstate@*/
00240 
00241 exit:
00242   if (count > 0)
00243   {
00244     xx = hae(h, RPMTAG_DIRINDEXES, RPM_INT32_TYPE, dirIndexes, count);
00245     xx = hae(h, RPMTAG_BASENAMES, RPM_STRING_ARRAY_TYPE,
00246              baseNames, count);
00247     xx = hae(h, RPMTAG_DIRNAMES, RPM_STRING_ARRAY_TYPE,
00248              dirNames, dirIndex + 1);
00249   }
00250 
00251   fileNames = (char**)hfd(fileNames, fnt);
00252 
00253   xx = hre(h, RPMTAG_OLDFILENAMES);
00254 }
00255 /*@=bounds@*/
00256 
00257 /*
00258  * Up to rpm 3.0.4, packages implicitly provided their own name-version-release.
00259  * Retrofit an explicit "Provides: name = epoch:version-release".
00260  */
00261 void providePackageNVR(Header h)
00262 {
00263   HGE_t hge = (HGE_t)headerGetEntryMinMemory;
00264   HFD_t hfd = headerFreeData;
00265   const char *name, *version, *release;
00266   void *hgePtr = NULL;
00267   int_32 * epoch;
00268   const char *pEVR;
00269   char *p;
00270   int_32 pFlags = RPMSENSE_EQUAL;
00271   const char ** provides = NULL;
00272   const char ** providesEVR = NULL;
00273   rpmTagType pnt, pvt;
00274   int_32 * provideFlags = NULL;
00275   int providesCount;
00276   int i, xx;
00277   int bingo = 1;
00278 
00279   /* Generate provides for this package name-version-release. */
00280   xx = headerNVR(h, &name, &version, &release);
00281   if (!(name && version && release))
00282     return;
00283   pEVR = p = (char *)alloca(21 + strlen(version) + 1 + strlen(release) + 1);
00284   *p = '\0';
00285   if (hge(h, RPMTAG_EPOCH, NULL, &hgePtr, NULL))
00286   {
00287     epoch = (int_32 *)hgePtr;
00288     sprintf(p, "%d:", *epoch);
00289     while (*p != '\0')
00290       p++;
00291   }
00292   (void) stpcpy( stpcpy( stpcpy(p, version) , "-") , release);
00293 
00294   /*
00295    * Rpm prior to 3.0.3 does not have versioned provides.
00296    * If no provides at all are available, we can just add.
00297    */
00298   if (!hge(h, RPMTAG_PROVIDENAME, &pnt, &hgePtr, &providesCount))
00299     goto exit;
00300   provides = (const char **)hgePtr;
00301 
00302   /*
00303    * Otherwise, fill in entries on legacy packages.
00304    */
00305   if (!hge(h, RPMTAG_PROVIDEVERSION, &pvt, &hgePtr, NULL))
00306   {
00307     providesEVR = (const char **)hgePtr;
00308     for (i = 0; i < providesCount; i++)
00309     {
00310       const char * vdummy = "";
00311       int_32 fdummy = RPMSENSE_ANY;
00312       xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEVERSION, RPM_STRING_ARRAY_TYPE,
00313                                   &vdummy, 1);
00314       xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEFLAGS, RPM_INT32_TYPE,
00315                                   &fdummy, 1);
00316     }
00317     goto exit;
00318   }
00319 
00320   xx = hge(h, RPMTAG_PROVIDEFLAGS, NULL, &hgePtr, NULL);
00321   provideFlags = (int_32 *)hgePtr;
00322 
00323   /*@-nullderef@*/    /* LCL: providesEVR is not NULL */
00324   if (provides && providesEVR && provideFlags)
00325     for (i = 0; i < providesCount; i++)
00326     {
00327       if (!(provides[i] && providesEVR[i]))
00328         continue;
00329       if (!(provideFlags[i] == RPMSENSE_EQUAL &&
00330             !strcmp(name, provides[i]) && !strcmp(pEVR, providesEVR[i])))
00331         continue;
00332       bingo = 0;
00333       break;
00334     }
00335   /*@=nullderef@*/
00336 
00337 exit:
00338   provides = (const char **)hfd(provides, pnt);
00339   providesEVR = (const char **)hfd(providesEVR, pvt);
00340 
00341   if (bingo)
00342   {
00343     xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDENAME, RPM_STRING_ARRAY_TYPE,
00344                                 &name, 1);
00345     xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEFLAGS, RPM_INT32_TYPE,
00346                                 &pFlags, 1);
00347     xx = headerAddOrAppendEntry(h, RPMTAG_PROVIDEVERSION, RPM_STRING_ARRAY_TYPE,
00348                                 &pEVR, 1);
00349   }
00350 }
00351 
00355 
00356 using namespace std;
00357 
00358 #undef Y2LOG
00359 #define Y2LOG "librpmDb"
00360 
00361 /******************************************************************
00362 **
00363 **
00364 **      FUNCTION NAME : internal_convertV3toV4
00365 **      FUNCTION TYPE : int
00366 */
00367 void internal_convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r,
00368                              callback::SendReport<ConvertDBReport> & report )
00369 {
00370 //  Timecount _t( "convert V3 to V4" );
00371   MIL << "Convert rpm3 database to rpm4" << endl;
00372 
00373   // Check arguments
00374   FD_t fd = fadOpen( v3db_r.asString().c_str() );
00375   if ( fd == 0 )
00376   {
00377     Fclose( fd );
00378     ZYPP_THROW(RpmDbOpenException(Pathname("/"), v3db_r));
00379   }
00380 
00381   if ( ! v4db_r )
00382   {
00383     Fclose( fd );
00384     INT << "NULL rpmV4 database passed as argument!" << endl;
00385     ZYPP_THROW(RpmNullDatabaseException());
00386   }
00387 
00388   shared_ptr<RpmException> err = v4db_r->error();
00389   if ( err )
00390   {
00391     Fclose( fd );
00392     INT << "Can't access rpmV4 database " << v4db_r << endl;
00393     ZYPP_THROW(*err);
00394   }
00395 
00396   // open rpmV4 database for writing. v4db_r is ok so librpm should
00397   // be properly initialized.
00398   rpmdb db = 0;
00399   string rootstr( v4db_r->root().asString() );
00400   const char * root = ( rootstr == "/" ? NULL : rootstr.c_str() );
00401 
00402   int res = ::rpmdbOpen( root, &db, O_RDWR, 0644 );
00403   if ( res || ! db )
00404   {
00405     if ( db )
00406     {
00407       ::rpmdbClose( db );
00408     }
00409     Fclose( fd );
00410     ZYPP_THROW(RpmDbOpenException(root, Pathname(string(db->db_root))));
00411   }
00412 
00413   // Check ammount of packages to process.
00414   int max = 0;
00415   for ( int offset = fadFirstOffset(fd); offset; offset = fadNextOffset(fd, offset) )
00416   {
00417     ++max;
00418   }
00419   MIL << "Packages in rpmV3 database " << v3db_r << ": " << max << endl;
00420 
00421   unsigned failed      = 0;
00422   unsigned ignored     = 0;
00423   unsigned alreadyInV4 = 0;
00424   report->progress( (100 * (failed + ignored + alreadyInV4) / max), v3db_r );
00425 
00426   if ( !max )
00427   {
00428     Fclose( fd );
00429     ::rpmdbClose( db );
00430     return;
00431   }
00432 
00433   // Start conversion.
00434 #warning Add CBSuggest handling if needed, also on lines below
00435 //  CBSuggest proceed;
00436   bool proceed = true;
00437   for ( int offset = fadFirstOffset(fd); offset && proceed ;
00438         offset = fadNextOffset(fd, offset),
00439         report->progress( (100 * (failed + ignored + alreadyInV4) / max), v3db_r ) )
00440   {
00441 
00442     // have to use lseek instead of Fseek because headerRead
00443     // uses low level IO
00444     if ( lseek( Fileno( fd ), (off_t)offset, SEEK_SET ) == -1 )
00445     {
00446       ostream * reportAs = &(ERR);
00447       /*      proceed = report->dbReadError( offset );
00448             if ( proceed == CBSuggest::SKIP ) {
00449         // ignore this error
00450         ++ignored;
00451         reportAs = &(WAR << "IGNORED: ");
00452             } else {*/
00453       // PROCEED will fail after conversion; CANCEL immediately stop loop
00454       ++failed;
00455 //      }
00456       (*reportAs) << "rpmV3 database entry: Can't seek to offset " << offset << " (errno " << errno << ")" << endl;
00457       continue;
00458     }
00459     Header h = headerRead(fd, HEADER_MAGIC_NO);
00460     if ( ! h )
00461     {
00462       ostream * reportAs = &(ERR);
00463       /*      proceed = report->dbReadError( offset );
00464             if ( proceed == CBSuggest::SKIP ) {
00465         // ignore this error
00466         ++ignored;
00467         reportAs = &(WAR << "IGNORED: ");
00468             } else {*/
00469       // PROCEED will fail after conversion; CANCEL immediately stop loop
00470       ++failed;
00471 //      }
00472       (*reportAs) << "rpmV3 database entry: No header at offset " << offset << endl;
00473       continue;
00474     }
00475     compressFilelist(h);
00476     providePackageNVR(h);
00477     const char *name = 0;
00478     const char *version = 0;
00479     const char *release = 0;
00480     headerNVR(h, &name, &version, &release);
00481     string nrv( string(name) + "-" +  version + "-" + release );
00482     rpmdbMatchIterator mi = rpmdbInitIterator(db, RPMTAG_NAME, name, 0);
00483     rpmdbSetIteratorRE(mi, RPMTAG_VERSION, RPMMIRE_DEFAULT, version);
00484     rpmdbSetIteratorRE(mi, RPMTAG_RELEASE, RPMMIRE_DEFAULT, release);
00485     if (rpmdbNextIterator(mi))
00486     {
00487 //      report.dbInV4( nrv );
00488       WAR << "SKIP: rpmV3 database entry: " << nrv << " is already in rpmV4 database" << endl;
00489       rpmdbFreeIterator(mi);
00490       headerFree(h);
00491       ++alreadyInV4;
00492       continue;
00493     }
00494     rpmdbFreeIterator(mi);
00495     if (rpmdbAdd(db, -1, h, 0, 0))
00496     {
00497 //      report.dbWriteError( nrv );
00498       proceed = false;//CBSuggest::CANCEL; // immediately stop loop
00499       ++failed;
00500       ERR << "rpmV4 database error: could not add " << nrv << " to rpmV4 database" << endl;
00501       headerFree(h);
00502       continue;
00503     }
00504     headerFree(h);
00505   }
00506 
00507   Fclose(fd);
00508   ::rpmdbClose(db);
00509 
00510   if ( failed )
00511   {
00512     ERR << "Convert rpm3 database to rpm4: Aborted after "
00513     << alreadyInV4 << " package(s) and " << (failed+ignored) << " error(s)."
00514     << endl;
00515     ZYPP_THROW(RpmDbConvertException());
00516   }
00517   else
00518   {
00519     MIL << "Convert rpm3 database to rpm4: " << max << " package(s) processed";
00520     if ( alreadyInV4 )
00521     {
00522       MIL << "; " << alreadyInV4 << " already present in rpmV4 database";
00523     }
00524     if ( ignored )
00525     {
00526       MIL << "; IGNORED: " << ignored << " unconverted due to error";
00527     }
00528     MIL << endl;
00529   }
00530 }
00531 
00532 /******************************************************************
00533 *
00534 *
00535 *       FUNCTION NAME : convertV3toV4
00536 *
00537 * \throws RpmException
00538 *
00539 */
00540 void convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r )
00541 {
00542   // report
00543   callback::SendReport<ConvertDBReport> report;
00544   report->start(v3db_r);
00545   try
00546   {
00547     internal_convertV3toV4( v3db_r, v4db_r, report );
00548   }
00549   catch (RpmException & excpt_r)
00550   {
00551     report->finish(v3db_r, ConvertDBReport::FAILED,excpt_r.asUserString());
00552     ZYPP_RETHROW(excpt_r);
00553   }
00554   report->finish(v3db_r, ConvertDBReport::NO_ERROR, "");
00555 }
00556 
00557 } // namespace rpm
00558 } // namespace target
00559 } // namespace zypp

Generated on Tue Sep 25 19:23:09 2007 for libzypp by  doxygen 1.5.3