00001
00002
00003
00004
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;
00058 unsigned int freePrev;
00059 unsigned int isFree;
00060
00061
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
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 return fd ;
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
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
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
00173
00174
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
00189
00190
00191
00192
00193 if (headerIsEntry(h, RPMTAG_DIRNAMES))
00194 {
00195 xx = headerRemoveEntry(h, RPMTAG_OLDFILENAMES);
00196 return;
00197 }
00198
00199 HGEPtr_t hgePtr = NULL;
00200 if (!headerGetEntryMinMemory(h, RPMTAG_OLDFILENAMES, hTYP_t(&fnt), &hgePtr, &count))
00201 return;
00202 fileNames = (char **)hgePtr;
00203 if (fileNames == NULL || count <= 0)
00204 return;
00205
00206 dirNames = (const char **)alloca(sizeof(*dirNames) * count);
00207 baseNames = (const char **)alloca(sizeof(*dirNames) * count);
00208 dirIndexes = (int_32 *)alloca(sizeof(*dirIndexes) * count);
00209
00210 if (fileNames[0][0] != '/')
00211 {
00212
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
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)
00232 continue;
00233 baseName = strrchr(fileNames[i], '/') + 1;
00234 len = baseName - fileNames[i];
00235 needle = dirNames;
00236 savechar = *baseName;
00237 *baseName = '\0';
00238
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
00251
00252 *baseName = savechar;
00253 baseNames[i] = baseName;
00254 }
00255
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
00272
00273
00274
00275
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
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
00310
00311
00312 if (!headerGetEntryMinMemory(h, RPMTAG_PROVIDENAME, hTYP_t(&pnt), &hgePtr, &providesCount))
00313 goto exit;
00314 provides = (const char **)hgePtr;
00315
00316
00317
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
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
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
00379
00380
00381 void internal_convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r,
00382 callback::SendReport<ConvertDBReport> & report )
00383 {
00384
00385 MIL << "Convert rpm3 database to rpm4" << endl;
00386
00387
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
00411
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
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
00448 #warning Add CBSuggest handling if needed, also on lines below
00449
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
00457
00458 if ( lseek( Fileno( fd ), (off_t)offset, SEEK_SET ) == -1 )
00459 {
00460 ostream * reportAs = &(ERR);
00461
00462
00463
00464
00465
00466
00467
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
00478
00479
00480
00481
00482
00483
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
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
00516 proceed = false;
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
00554
00555
00556
00557
00558 void convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r )
00559 {
00560
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 }
00576 }
00577 }