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