00001
00002
00003
00004
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;
00035 unsigned int freePrev;
00036 unsigned int isFree;
00037
00038
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
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 return fd ;
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
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
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
00138
00139
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
00158
00159
00160
00161
00162 if (headerIsEntry(h, RPMTAG_DIRNAMES)) {
00163 xx = hre(h, RPMTAG_OLDFILENAMES);
00164 return;
00165 }
00166
00167 void *hgePtr = NULL;
00168 if (!hge(h, RPMTAG_OLDFILENAMES, &fnt, &hgePtr, &count))
00169 return;
00170 fileNames = (char **)hgePtr;
00171 if (fileNames == NULL || count <= 0)
00172 return;
00173
00174 dirNames = (const char **)alloca(sizeof(*dirNames) * count);
00175 baseNames = (const char **)alloca(sizeof(*dirNames) * count);
00176 dirIndexes = (int_32 *)alloca(sizeof(*dirIndexes) * count);
00177
00178 if (fileNames[0][0] != '/') {
00179
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
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)
00197 continue;
00198 baseName = strrchr(fileNames[i], '/') + 1;
00199 len = baseName - fileNames[i];
00200 needle = dirNames;
00201 savechar = *baseName;
00202 *baseName = '\0';
00203
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
00214
00215 *baseName = savechar;
00216 baseNames[i] = baseName;
00217 }
00218
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
00234
00235
00236
00237
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
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
00273
00274
00275 if (!hge(h, RPMTAG_PROVIDENAME, &pnt, &hgePtr, &providesCount))
00276 goto exit;
00277 provides = (const char **)hgePtr;
00278
00279
00280
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
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
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
00338
00339
00340 void internal_convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r,
00341 callback::SendReport<ConvertDBReport> & report )
00342 {
00343
00344 MIL << "Convert rpm3 database to rpm4" << endl;
00345
00346
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
00367
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
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
00400 #warning Add CBSuggest handling if needed, also on lines below
00401
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
00409
00410 if ( lseek( Fileno( fd ), (off_t)offset, SEEK_SET ) == -1 ) {
00411 ostream * reportAs = &(ERR);
00412
00413
00414
00415
00416
00417
00418
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
00428
00429
00430
00431
00432
00433
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
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
00460 proceed = false;
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
00493
00494
00495
00496
00497 void convertV3toV4( const Pathname & v3db_r, const librpmDb::constPtr & v4db_r )
00498 {
00499
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 }
00514 }
00515 }