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

Generated on Thu Jul 6 00:07:26 2006 for zypp by  doxygen 1.4.6